Mercurial > public > ostc_companion
view OSTC4Operations.cpp @ 4:e30f00f760d3 default tip
Cleanup OSTC label and removed url
The computer type will now show OSTC 4/5 instead of only 4. The url has
been removed because it is no longer maintained. The ui header have been
deleted because they are generated files shich should not be under
version controll. Delete locally if you want to force an update of the
dialog layout.
| author | Ideenmodellierer |
|---|---|
| date | Sun, 30 Nov 2025 18:37:32 +0100 |
| parents | 4ace58a7c03c |
| 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 <QSettings> #include <QRegularExpression> #include "OSTC4Operations.h" #include "Utils/Log.h" #include "Utils/Exception.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 ////////////////////////////////////////////////////////////////////////////// QSize OSTC4Operations::nameSize() const { return QSize(12, 4); } ////////////////////////////////////////////////////////////////////////////// QString OSTC4Operations::model() const { return "OSTC 4/5"; } HardwareOperations::CompanionFeatures OSTC4Operations::supported() const { // No ICON, no DUMPSCREEN return CompanionFeatures(PARAMETERS|DATE|NAME|FIRMWARE |HELIUM_DIVE|CCR_DIVE|BLUETOOTH|VPM_MODEL|SIGNAL_CHECK); } 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("ttyUSB.*"); // default: case-sensitive #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'); // 0x69 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" : ""); } //---- RTE firmware ------------------------------------------------------ QString rteFW; { unsigned char echo = retryCommand(_serial, 'k'); // 0x69 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'); // 0x69 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"; } ////////////////////////////////////////////////////////////////////////////// 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..."); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// /// FIRMWARE UPGRADE /// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// #define SWAP4BYTES(x) \ uint( (((x) & 0x000000FF) << 24) \ | (((x) & 0x0000FF00) << 8) \ | (((x) & 0x00FF0000) >> 8) \ | (((x) & 0xFF000000) >> 24)) ////////////////////////////////////////////////////////////////////////////// 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)"); } //////////////////////////////////////////////////////////////////////////////
