view OSTC4Operations.cpp @ 14:e47e0f59101d default tip

Enable OSTC 4/5 Icon option The button for uploading the icon is now activated based on the first FW version supporting this function
author Ideenmodellierer
date Mon, 12 Jan 2026 18:47:00 +0100
parents ac837fe1d590
children
line wrap: on
line source

//////////////////////////////////////////////////////////////////////////////
/// \file   OSTC4Operations.h
/// \brief  Implementing various operations for H&W OSTC4 dive computer
/// \author JD Gascuel.
/// \sa     OSTC3Operations, HardwareOperations.h
///
/// \copyright (c) 2016 JD Gascuel. All rights reserved.
/// $Id$
//////////////////////////////////////////////////////////////////////////////
//
// BSD 2-Clause License:
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#include "OSTC4Operations.h"
#include <QRegularExpression>
#include <QSettings>

#include "Utils/Exception.h"
#include "Utils/Log.h"
#include "Utils/ProgressEvent.h"

extern QSettings *settings;

#define FIRMWARE_BLOCK 0x40       // 64 bytes
#define FIRMWARE_BLOCK_DELAY 15   // 15 msec.
#define FIRMWARE_BLOCK_FAST 0x300 // 4096 bytes

#define MIN_FW_ICON_MAJOR  1
#define MIN_FW_ICON_MINOR  7
#define MIN_FW_ICON_STEP  5

//////////////////////////////////////////////////////////////////////////////

QSize OSTC4Operations::nameSize() const
{
    return QSize(12, 4);
}

//////////////////////////////////////////////////////////////////////////////

QString OSTC4Operations::model() const
{
    return "OSTC 4/5";
}

HardwareOperations::CompanionFeatures OSTC4Operations::supported() const
{
    // No DUMPSCREEN

    return m_supportedFeatures;
}

QString OSTC4Operations::firmwareTemplate() const
{
    return "ostc4*.bin";
}

QStringList OSTC4Operations::listPorts() const
{
    return listBluetoothPorts();
}
#if 0
QRegExp OSTC4Operations::portTemplate() const
{
#if defined(Q_OS_MAC)
    return QRegExp("tty[.]OSTC4-.*", Qt::CaseInsensitive);
#elif defined(Q_OS_LINUX)
    // Seems ok for debian, ubuntu, redhat, CentOS and SUSE (google dixit)
    return QRegExp("ttyUSB.*", Qt::CaseSensitive);
#elif defined(Q_OS_WIN)
    return QRegExp("COM.*", Qt::CaseSensitive);
#endif
}
#endif

QRegularExpression OSTC4Operations::portTemplate() const
{
#if defined(Q_OS_MAC)
    return QRegularExpression("tty[.]OSTC4-.*", QRegularExpression::CaseInsensitiveOption);
#elif defined(Q_OS_LINUX)
    // Debian, Ubuntu, RedHat, CentOS, SUSE
    return QRegularExpression("^(rfcomm.*|ttyUSB.*)$");
#elif defined(Q_OS_WIN)
    return QRegularExpression("COM.*"); // default: case-sensitive
#endif
}
//////////////////////////////////////////////////////////////////////////////

void OSTC4Operations::getIdentity()
{
    descriptionString.clear();

    LOG_TRACE("Getting model...");
    HardwareDescriptor hw = hardwareDescriptor();
    if (hw != HW_OSTC4)
        LOG_THROW("Not an OSTC4/5.");

    LOG_TRACE("Getting identity...");
    getCommonIdentity();

    //---- Main firmware -----------------------------------------------------
    QString mainFW;
    {
        unsigned char echo = retryCommand(_serial, 'k'); // 0x6B OSTC4 FW Details.
        if (echo != 'k')
            LOG_THROW("Bad firmware details.");

        unsigned char reply[4 + 1];
        _serial.writeByte(0xFF); // Main firmware
        _serial.readBlock(reply, sizeof reply);
        if (reply[4] != 0x4D)
            LOG_THROW("Bad main firmware reply.");

        mainFW = QString::asprintf("%d.%02d.%02d%s",
                                   reply[0],
                                   reply[1],
                                   reply[2],
                                   reply[3] ? "beta" : "");
        if((reply[0] > MIN_FW_ICON_MAJOR)
            || ((reply[0] == MIN_FW_ICON_MAJOR) && (reply[1] > MIN_FW_ICON_MINOR))
            || ((reply[0] == MIN_FW_ICON_MAJOR) && (reply[1] = MIN_FW_ICON_MINOR) && (reply[2] >= MIN_FW_ICON_STEP)))
        {
            m_supportedFeatures |= ICON;
        }
    }

    //---- RTE firmware ------------------------------------------------------
    QString rteFW;
    {
        unsigned char echo = retryCommand(_serial, 'k'); // 0x6B OSTC4 FW Details.
        if (echo != 'k')
            LOG_THROW("Bad firmware details.");

        unsigned char reply[4 + 1];
        _serial.writeByte(0xFE); // RTE firmware
        _serial.readBlock(reply, sizeof reply);
        if (reply[4] != 0x4D)
            LOG_THROW("Bad RTE firmware reply.");

        rteFW = QString::asprintf("%d.%02d.%02d%s",
                                  reply[0],
                                  reply[1],
                                  reply[2],
                                  reply[3] ? "beta" : "");
    }

    //---- Font Package ------------------------------------------------------
    QString fontPKG;
    {
        unsigned char echo = retryCommand(_serial, 'k'); // 0x6B OSTC4 FW Details.
        if (echo != 'k')
            LOG_THROW("Bad firmware details.");

        unsigned char reply[4 + 1];
        _serial.writeByte(0x10); // Font Package
        _serial.readBlock(reply, sizeof reply);
        if (reply[4] != 0x4D)
            LOG_THROW("Bad Font package reply.");

        fontPKG = QString::asprintf("%d.%02d.%02d%s",
                                    reply[0],
                                    reply[1],
                                    reply[2],
                                    reply[3] ? "beta" : "");
    }

    //---- OSTC4 specific description ----------------------------------------
    descriptionString = QString("%1 #%2, fw %3 (main %4/RTE %5/Font %6), %7")
                            .arg(model())
                            .arg(serialNumber(), 4, 10, QChar('0'))
                            .arg(QString::asprintf("%d.%02d", firmware() / 100, firmware() % 100))
                            .arg(mainFW)
                            .arg(rteFW)
                            .arg(fontPKG)
                            .arg(QString::fromLatin1((char *) (computerText), 60)
                                     .replace(QChar('\0'), "")
                                     .trimmed());

    LOG_INFO("Found " << descriptionString);
}

//////////////////////////////////////////////////////////////////////////////

OSTC4Operations::OSTC4Operations()
    : fileChecksum(0, 0)
{
    emulatorName = "OSTC4/5";
    m_crcWrapper = new CrcWrapper(nullptr);

    m_supportedFeatures |= PARAMETERS;
    m_supportedFeatures |= DATE;
    m_supportedFeatures |= NAME;
    m_supportedFeatures |= FIRMWARE;
    m_supportedFeatures |= HELIUM_DIVE;
    m_supportedFeatures |= CCR_DIVE;
    m_supportedFeatures |= BLUETOOTH;
    m_supportedFeatures |= VPM_MODEL;
    m_supportedFeatures |= SIGNAL_CHECK;
    // m_supportedFeatures |= ICON; will be set based on FW version
}

//////////////////////////////////////////////////////////////////////////////

void OSTC4Operations::upgradeFW(const QString &fileName)
{
    LOG_INFO("Starting OSTC4/5 firmware upgrade...");

    openFirmware(fileName, true);
    openFirmware(fileName, false);

    LOG_INFO("Please wait for OSTC4/5 to complete installation...");
}

#define SWAP4BYTES(x) \
    uint((((x) & 0x000000FF) << 24) | (((x) & 0x0000FF00) << 8) | (((x) & 0x00FF0000) >> 8) \
         | (((x) & 0xFF000000) >> 24))

void OSTC4Operations::setIcons(const QString &fileName)
{
    int size;
    int blockSize;
    int transferDelay;
    unsigned char echo;

    FirmwareOSTC4 header;

    unsigned char buf3offset[4];
    unsigned char bufVersion[4];
    uint32_t checksum = 0;
    uint32_t width = 0;
    uint32_t height = 0;

    blockSize = FIRMWARE_BLOCK;
    transferDelay = FIRMWARE_BLOCK_DELAY;

    try {
        bmp = std::make_unique<BmpToArray>(fileName);
    } catch (const std::exception &e) {
        std::cerr << "Fehler beim Laden: " << e.what() << '\n';
    }

    QByteArray bin = bmp->getTransferBytes();
    bmp->getImageXY(&width, &height);

    if (bin.size() < 0x20000 - 12) /* max bin size - header */
    {
        buf3offset[0] = 0x20;
        buf3offset[1] = 0x00;
        buf3offset[2] = 0x00;
        buf3offset[3] = 0x00;
        bufVersion[0] = 1;
        bufVersion[1] = 0;
        bufVersion[2] = 0;
        bufVersion[3] = 0;

        header.type = 0x20;
        /* original header shallnot be modifieded => split image size information into 5 bytes */
        header.dummy5 = width;                       /* low byte */
        header.dummy6 = height;                      /* low byte */
        header.dummy7 = (width >> 8) & 0x0000000F;   /* shifted high nibble */
        header.dummy7 |= (height >> 4) & 0x000000F0; /* shifted high nibble */

        header.length = SWAP4BYTES(bin.size());
        header.checksum = bin.size() + (256 * 256 * 256 * header.type) + (256 * 256 * header.dummy5)
                          + (256 * header.dummy6) + header.dummy7;

        checksum = SWAP4BYTES(
            m_crcWrapper->CRC_CalcBlockCRC(reinterpret_cast<uint32_t *>(bin.data()),
                                           bin.size() / 4));

        //---- Upload icon -----------------------------------------------
        const int steps = 3 + (bin.size() + blockSize - 1) / blockSize;
        int step = 0;
        PROGRESS(step++, steps);

        LOG_INFO(QString("    uploading icon size %1 bytes").arg(bin.size()));
        writeText(QString("Upload Icon"));

        //---- Command
        PROGRESS(step++, steps);
        echo = retryCommand(_serial, 'o'); // 0x6F upload icon
        if (echo != 'o')
            LOG_THROW("Bad OSTC4/5 icon upload.");

        //---- Header
        PROGRESS(step++, steps);
        _serial.writeBlock((uchar *) &header, sizeof header);
        _serial.sleep(500);
        //---- Data
        for (int len = 0x00000; len < bin.size(); len += blockSize) {
            if (transferDelay) {
                _serial.sleep(transferDelay); // 15 msec between 64B blocks...
            }
            PROGRESS(step++, steps);

            size = qMin(blockSize, bin.size() - len);

            LOG_TRACE("Fill " << size);
            _serial.writeBlock((uchar *) bin.data() + len, (unsigned int) size);
        }
        _serial.writeBlock((uchar *) &checksum, 4);
        PROGRESS(steps, steps); // 100%

        //---- Wait acknowledge ----------------------------------------------
        PROGRESS_THROTTLE();

        for (int wait = 0; wait < 2 * 60; ++wait) // Up to 2 min...
        {
            try {
                unsigned char reply = _serial.readByte();
                if (reply == 0x4D)
                    break;
            } catch (const ReadTimeout &) {
            }
        }

        LOG_INFO("    icon uploaded.");
    } else {
        LOG_THROW("Image to large. Must be < 128k.");
    }
    PROGRESS_RESET();
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
///     FIRMWARE UPGRADE                                                   ///
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

void OSTC4Operations::openFirmware(const QString &fileName, bool dryRun)
{
    LOG_TRACE("Opening OSTC4/5 firmware '" << fileName << "'...");

    bool forceFirmwareUpdate;
    bool forceRTEUpdate;
    bool forceFontlibUpdate;
    bool useFastMode;
    bool firmwareSupportFastMode = false;
    int size;
    int blockSize;
    int transferDelay;

    // Previous trial failed ?
    if (_file.isOpen())
        _file.close();

    _file.setFileName(fileName);
    if (!_file.open(QIODevice::ReadOnly))
        LOG_THROW("Cannot open BIN file " << fileName);

    //  Get settings to overwrite version check
    forceFirmwareUpdate = settings->value("OSTC/forceFirmwareUpdate", false).toBool();
    forceRTEUpdate = settings->value("OSTC/forceRTEUpdate", false).toBool();
    forceFontlibUpdate = settings->value("OSTC/forceFontlibUpdate", false).toBool();

    useFastMode = settings->value("OSTC/useFastMode", false).toBool();

    //---- Check consistency -------------------------------------------------
    fileChecksum.reset(0, 0);
    for (int part = 1; part < 4; ++part) {
        // Less than 3 parts ?
        if ((_file.size() - _file.pos()) < 16)
            break;

        if (dryRun)
            LOG_TRACE("Checking part " << part << "...");

        FirmwareOSTC4 header;
        QByteArray bin;
        loadFirmwarePart(header, bin, part, dryRun);

        QString name = QString::asprintf("%s v%d.%02d.%02d%s",
                                         header.type == 0x10   ? "Font"
                                         : header.type == 0xFF ? "FW  "
                                         : header.type == 0xFE ? "RTE "
                                                               : "??? ",
                                         header.version.x,
                                         header.version.y,
                                         header.version.z,
                                         header.version.beta ? " beta" : "");

        //---- On first pass, just check file structure ----------------------
        if (dryRun)
            continue;

        //---- On second pass, do upload new stuff ---------------------------
        if (_connectMode != SERVICE_MODE)
            connectServiceMode();
        if (_connectMode != SERVICE_MODE)
            LOG_THROW("Cannot connect OSTC 4/5 service mode...");

        //---- Check if needed ? ---------------------------------------------
        LOG_TRACE("Check part " << part << " is needed ?");
        unsigned char echo = retryCommand(_serial, 'k'); // 0x6B OSTC4 FW Details.
        if (echo != 'k')
            LOG_THROW("Bad firmware details.");

        unsigned char reply[4 + 1];
        _serial.writeByte(header.type); // type of firmware part
        _serial.readBlock(reply, sizeof reply);
        if (reply[4] != 0x4C) // SERVICE MODE only.
            LOG_THROW("Bad firmware reply.");

        if (((reply[0] * 100 + reply[1] * 10 + reply[2]) > 151)
            && (header.type == 0xFF)) /* suitable firmware version? First supported in 1.5.2 */
        {
            firmwareSupportFastMode = true;
        }

        if ((header.version.x == reply[0] && header.version.y == reply[1]
             && header.version.z == reply[2])
            && (!(forceFirmwareUpdate && header.type == 0xFF))
            && (!(forceRTEUpdate && header.type == 0xFE))
            && (!(forceFontlibUpdate && header.type == 0x10))) {
            LOG_INFO("    part " << part << ": " << name << " already up-to-date.");
            continue;
        }
        if ((useFastMode) && (firmwareSupportFastMode)) /* is fast mode supported ? */
        {
            blockSize = FIRMWARE_BLOCK_FAST;
            transferDelay = 1;
        } else {
            blockSize = FIRMWARE_BLOCK;
            transferDelay = FIRMWARE_BLOCK_DELAY;
        }
        //---- Upload firwmare -----------------------------------------------
        const int steps = 3 + (bin.size() + blockSize - 1) / blockSize;
        int step = 0;
        PROGRESS(step++, steps);

        LOG_INFO("    uploading part " << part << ": " << name << "...");
        writeText(QString("Upload Part %1").arg(part));

        //---- Command
        PROGRESS(step++, steps);
        echo = retryCommand(_serial, 's'); // 0x73 OSTC4 FW Upgrade
        if (echo != 's')
            LOG_THROW("Bad OSTC4/5 FW upgrade commande.");

        //---- Header
        PROGRESS(step++, steps);
        _serial.writeBlock((uchar *) &header, sizeof header);
        _serial.sleep(500);
        //---- Data
        for (int len = 0x00000; len < bin.size(); len += blockSize) {
            if (transferDelay) {
                _serial.sleep(transferDelay); // 15 msec between 64B blocks...
            }
            PROGRESS(step++, steps);

            size = qMin(blockSize, bin.size() - len);

            LOG_TRACE("Fill " << size);
            _serial.writeBlock((uchar *) bin.data() + len, (unsigned int) size);
        }

        PROGRESS(steps, steps); // 100%

        //---- Wait acknowledge ----------------------------------------------
        // RTE seems to miss the acknowledge byte ...
        if (header.type != 0xFE) {
            PROGRESS_THROTTLE();

            for (int wait = 0; wait < 2 * 60; ++wait) // Up to 2 min...
            {
                try {
                    unsigned char reply = _serial.readByte();
                    if (reply == 0x4C)
                        break;
                } catch (const ReadTimeout &) {
                }
            }
        }

        LOG_INFO("    part " << part << ": " << name << " uploaded.");
    }

    //---- Done --------------------------------------------------------------
    // Low-level close, to avoid trying to send a 0xFF byte...
    if (!dryRun) {
        disconnect(true);
    }

    PROGRESS_RESET();

    //---- Check FILE checksum on first pass ---------------------------------
    if (dryRun) {
        uint expected;
        if (4 != _file.read((char *) &expected, sizeof expected))
            LOG_THROW("Missing file checksum.");

        uint checksum = fileChecksum.a() | (fileChecksum.b() << 16);

        if (checksum != expected)
            LOG_ERROR(QString::asprintf("File checksum ERROR: expected = %04X, actual = %04X",
                                        expected,
                                        checksum));
        else if (dryRun)
            LOG_TRACE(QString::asprintf("File checksum = %04X (OK)", checksum));

        if (!_file.atEnd())
            LOG_THROW("Extra data after last block.");
    }

    _file.close();
}

//////////////////////////////////////////////////////////////////////////////

void OSTC4Operations::loadFirmwarePart(FirmwareOSTC4 &header, QByteArray &bin, int part, bool dryRun)
{
    memset(&header, 0, sizeof header);

    //---- Read part header and check consistency ----------------------------
    if (sizeof header != _file.read((char *) &header, sizeof header))
        LOG_THROW("EOF in header.");
    fileChecksum.add(&header, sizeof header);

    uint checksum = SWAP4BYTES(header.length) + SWAP4BYTES(*(uint *) &header.type);
    if (checksum != header.checksum)
        LOG_THROW(
            QString::asprintf("Inconsistent header (%04X != %04X).", checksum, header.checksum));

    if (SWAP4BYTES(header.length) > (1024 * 1024) || (SWAP4BYTES(header.length) % 4))
        LOG_THROW("Inconsistent header (part=" << part << ", size=" << SWAP4BYTES(header.length)
                                               << ").");

    if (dryRun)
        switch (header.type) {
        case 0x10:
            LOG_TRACE("... font");
            break;
        case 0xFF:
            LOG_TRACE("... firmware");
            break;
        case 0xFE:
            LOG_TRACE("... RTE");
            break;
        default:
            //        LOG_THROW("Inconsistent header (part=" << part << ", type=0x" << QString::asprintf(int(header.type), 16) << ").");
            LOG_THROW("Inconsistent header (part=" << part << ", type=0x"
                                                   << QString::asprintf("Value = %d", header.type)
                                                   << ").");
            break;
        }

    if (dryRun)
        LOG_TRACE("... version " << QString::asprintf("%d.%02d.%02d.%s",
                                                      header.version.x,
                                                      header.version.y,
                                                      header.version.z,
                                                      header.version.beta ? " beta" : ""));

    //---- Read Binary Data --------------------------------------------------
    if (dryRun)
        LOG_TRACE("... size " << SWAP4BYTES(header.length) << " bytes.");
    bin.resize(SWAP4BYTES(header.length) + 4);
    assert((uint) bin.size() == SWAP4BYTES(header.length) + 4);

    if (bin.size() != _file.read(bin.data(), bin.size()))
        LOG_THROW("EOF in data.");

    if (dryRun)
        LOG_TRACE("... checksum " << QString::asprintf("%08x",
                                                       *(uint *) (bin.data()
                                                                  + SWAP4BYTES(header.length))));
    fileChecksum.add(bin);
}

void OSTC4Operations::getSignal()
{
    char buffer[60];
    memset(buffer, 0, sizeof buffer);
    unsigned char echo = retryCommand(_serial, 'l'); // 0x6c request bluetooth signal
    if (echo != 0x6C)
        LOG_THROW("Bad text reply (1)" << echo);

    _serial.sleep(1000);

    unsigned char ok = _serial.readByte();
    if (ok != 0x4D) // DOWNLOAD mode only.
        LOG_THROW("Bad clock reply (2)");
}

void OSTC4Operations::getAllHeader(unsigned char *pBuffer)
{
    unsigned char index = 0;
    char buffer[60];
    memset(buffer, 0, sizeof buffer);

    if (_connectMode != SERVICE_MODE)
        connectServiceMode();
    if (_connectMode != SERVICE_MODE)
        LOG_THROW("Cannot connect OSTC4 service mode...");

    unsigned char echo = retryCommand(_serial, 0x85); // 0x85 request all headers (dump)
    if (echo != 0x85)
        LOG_THROW("Bad text reply (1)" << echo);

    _serial.sleep(1000);

    for (index = 0; index < 8; index++) // Expect 8 blocks a 0x8000
    {
        _serial.readBlock(pBuffer + (index * 0x8000), 0x8000);
        LOG_INFO(("."));
    }
    *(pBuffer + (index * 0x8000)) = _serial.readByte(); // get lastdiveID
    LOG_INFO(QString::asprintf("%d", *(pBuffer + (index * 0x8000))));
    unsigned char ok = _serial.readByte();
    if (ok != 0x4C) //Service mode only.
        LOG_THROW("Bad clock reply (2)");
}

void OSTC4Operations::writeAllHeader(unsigned char *pBuffer)
{
    unsigned char index = 0;
    char buffer[60];
    memset(buffer, 0, sizeof buffer);

    if (_connectMode != SERVICE_MODE)
        connectServiceMode();
    if (_connectMode != SERVICE_MODE)
        LOG_THROW("Cannot connect OSTC4 service mode...");

    unsigned char echo = retryCommand(_serial, 0x86); // 0x86 request all headers (dump)
    if (echo != 0x86)
        LOG_THROW("Bad text reply (1)" << echo);

    _serial.sleep(1000);

    for (index = 0; index < 8; index++) // Expect 8 blocks a 0x8000
    {
        _serial.writeBlock(pBuffer + (index * 0x8000), 0x8000);
        LOG_INFO(("."));
    }
    _serial.writeByte(*(pBuffer + (index * 0x8000))); // write lastdiveID

    unsigned char ok = _serial.readByte();
    if (ok != 0x4C) //Service mode only.
        LOG_THROW("Bad clock reply (2)");
}

void OSTC4Operations::getAllSamples(unsigned char *pBuffer)
{
    unsigned short index = 0;
    char buffer[60];
    memset(buffer, 0, sizeof buffer);

    if (_connectMode != SERVICE_MODE)
        connectServiceMode();
    if (_connectMode != SERVICE_MODE)
        LOG_THROW("Cannot connect OSTC4 service mode...");

    unsigned char echo = retryCommand(_serial, 0x88); // 0x88 request all samples (dump)
    if (echo != 0x88)
        LOG_THROW("Bad text reply (1)" << echo);

    _serial.sleep(500);

    for (index = 0; index < 384; index++) // Expect 8 blocks a 0x8000
    {
        _serial.readBlock(pBuffer + (index * 0x8000), 0x8000);
        LOG_INFO(QString::asprintf("%d", index));
    }
    _serial.readBlock(pBuffer + (index * 0x8000), 4); // get lastdiveID
    unsigned char ok = _serial.readByte();
    if (ok != 0x4C) //Service mode only.
        LOG_THROW("Bad clock reply (2)");
}

void OSTC4Operations::writeAllSamples(unsigned char *pBuffer)
{
    unsigned short index = 0;
    char buffer[60];
    memset(buffer, 0, sizeof buffer);

    if (_connectMode != SERVICE_MODE)
        connectServiceMode();
    if (_connectMode != SERVICE_MODE)
        LOG_THROW("Cannot connect OSTC4 service mode...");

    unsigned char echo = retryCommand(_serial, 0x89); // 0x88 request all samples (dump)
    if (echo != 0x89)
        LOG_THROW("Bad text reply (1)" << echo);

    _serial.sleep(500);

    for (index = 0; index < 384; index++) // Expect 8 blocks a 0x8000 384
    {
        _serial.writeBlock((const unsigned char *) (pBuffer + (index * 0x8000)), 0x8000);
        LOG_INFO(QString::asprintf("%d", index));
    }
    _serial.writeBlock(pBuffer + (index * 0x8000), 4); // set lastdiveID
    unsigned char ok = _serial.readByte();
    if (ok != 0x4C) //Service mode only.
        LOG_THROW("Bad clock reply (2)");
}

//////////////////////////////////////////////////////////////////////////////