Mercurial > public > ostc_companion
view OSTCFrogOperations.cpp @ 2:177f640940f2
Update exception class and cleanup redifinitions
During firmware download and exception caused the application to stop.
Rootcause was the defference between QT5 and QT6 exception and string
handling which is updated now. In addition some old definitions were
removed to avoid compiler warnings.
| author | Ideenmodellierer |
|---|---|
| date | Fri, 28 Nov 2025 19:57:35 +0100 |
| parents | 0b3630a29ad8 |
| children |
line wrap: on
line source
////////////////////////////////////////////////////////////////////////////// /// \file OSTCFrogOperations.cpp /// \brief Implementing various operations for H&W Frog dive computer /// \author JD Gascuel. /// /// \copyright (c) 2011-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 "OSTCFrogOperations.h" #include "Utils/Log.h" #include "Utils/Exception.h" #include "HexFile.h" #include "SettingsDialog.h" #include <QApplication> #include <QDateTime> #include <QProgressBar> #include <QStringList> // Byte extration, compatible littleendian or bigendian. #define LOW(x) ((unsigned char)((x) % 256)) #define HIGH(x) ((unsigned char)((x / (1<<8)) % 256)) #define UPPER(x) ((unsigned char)((x / (1<<16)) % 256)) #define UP32(x) ((unsigned char)((x / (1<<24)) % 256)) #define IMAGE_ROUNDING 1024 #define FIRMWARE_AREA 0x3E0000 #define FIRMWARE_SIZE 0x01D000 extern QProgressBar* progress; ////////////////////////////////////////////////////////////////////////////// OSTCFrogOperations::OSTCFrogOperations() : _firmware(0), _serialNumber(0), _isOpen(false), _commandMode(false) { } OSTCFrogOperations::~OSTCFrogOperations() { if( _isOpen ) disconnect(true); } ////////////////////////////////////////////////////////////////////////////// /// /// /// PORT management /// /// /// ////////////////////////////////////////////////////////////////////////////// //QRegExp OSTCFrogOperations::portTemplate() const QRegularExpression OSTCFrogOperations::portTemplate() const { #if defined(Q_OS_MAC) return QRegExp("tty[.]frog.*", Qt::CaseInsensitive); #elif defined(Q_OS_LINUX) // Seems ok for debian, ubuntu, and SUSE (google dixit). // Obviously, needs the rfcomm package. "hcitool scan" or lsusb to have // a list of connected stuff... return QRegExp("rfcomm.*", Qt::CaseInsensitive); #elif defined(Q_OS_WIN) // return QRegExp("COM.*", Qt::CaseSensitive); return QRegularExpression( "COM([0-9]+)", QRegularExpression::CaseInsensitiveOption ); #endif } QStringList OSTCFrogOperations::listPorts() const { return listBluetoothPorts(); } QString OSTCFrogOperations::firmwareTemplate() const { return "*frog.firmware.hex"; } QString OSTCFrogOperations::model() const { return "Frog"; } QString OSTCFrogOperations::description() { return _description; } QImage OSTCFrogOperations::dumpScreen() const { LOG_THROW("Not implemented..."); return QImage(); } HardwareOperations::CompanionFeatures OSTCFrogOperations::supported() const { // No ICON, no PARAMETER, no FIRMWARE, no DUMPSCREEN yet... return CompanionFeatures(NAME|DATE); } bool OSTCFrogOperations::connect() { try { //---- Open the serial port------------------------------------------- LOG_TRACE( QString("Open port %1...").arg(Settings::port) ); _isOpen = false; //---- Execute the start protocol ------------------------------------ static char animation[] = "/-\\|"; int reply = 0; for(int retry=0; retry < 30; ++retry) { _serial.open( Settings::port, "Frog"); _serial.sleep(100); _serial.purge(); _serial.writeByte(0xBB); try { reply = _serial.readByte(); if( reply == 0x4D ) break; } catch(...) {} LOG_TRACE( QString("Starting... %1").arg(animation[retry % sizeof animation])); } if( reply != 0x4D ) LOG_THROW("Not started"); //---- Enquire about Frog id ----------------------------------------- getIdentity(); //---- Everything is ok ---------------------------------------------- _isOpen = true; return true; } catch(const Exception& e) { _serial.close(); LOG_THROW("Cannot connect " << Settings::port << ": " << e.what()); } _isOpen = false; return false; } void OSTCFrogOperations::connectServiceMode() { connect(); } bool OSTCFrogOperations::disconnect(bool /*closing*/) { if( !_isOpen ) return false; _serial.purge(); _serial.writeByte(0xFF); _serial.sleep(100); _serial.purge(); _serial.close(); _description.clear(); // cleanup for interface updateStatus() _isOpen = false; return true; } ////////////////////////////////////////////////////////////////////////////// /// /// /// LOW Level commands /// /// /// ////////////////////////////////////////////////////////////////////////////// void OSTCFrogOperations::beginCommands() { Q_ASSERT( !_commandMode ); static char animation[] = "/-\\|"; for(int i=0;; ++i) { if( i == 100 ) // 20.0 sec loop ? LOG_THROW("Bad reply to open command"); _serial.sleep(100); _serial.purge(); _serial.writeByte(0xAA); // Start byte int reply = 0; try { reply = _serial.readByte(); } catch(...) {} if( reply == 0x4B ) goto Started; LOG_TRACE(QString("Connecting %1") .arg(animation[i%4])); _serial.sleep(200); continue; Started: unsigned char buffer[] = "\xAA\xAB\xAC"; _serial.writeBlock(buffer, 3); try { unsigned char reply = _serial.readByte(); if( reply == 0x4C ) { _commandMode = true; return; } } catch(...) {} _serial.sleep(200); } } ////////////////////////////////////////////////////////////////////////////// void OSTCFrogOperations::endCommands() { Q_ASSERT( _commandMode ); _serial.sleep(100); _serial.purge(); _serial.writeByte(0xFF); // Exit service mode _serial.sleep(10); unsigned char buffer = _serial.readByte(); if( buffer != 0xFF ) LOG_THROW("End failed"); _commandMode = false; disconnect(); } ////////////////////////////////////////////////////////////////////////////// void OSTCFrogOperations::eraseRange(unsigned int addr, unsigned int size) { Q_ASSERT( _commandMode ); // Convert size to number of pages, rounded up. size = ((size + 4095) / 4096); if( size < 256 || addr != 0x300000 ) { unsigned char buffer[4]; // Erase just the needed pages. buffer[0] = UPPER(addr); buffer[1] = HIGH(addr); buffer[2] = LOW(addr); buffer[3] = LOW(size); _serial.writeByte(0x42); // Command _serial.sleep(10); _serial.writeBlock(buffer, 4); // Wait (120/4)ms by block of 4K, plus 3% VAT to be sure. _serial.sleep(40 + size * 31); } else { // Erase the whole 512KB of icon memory... _serial.writeByte(0x41); _serial.sleep(3000); } try { (void)_serial.readByte(); } catch(...) {} } ////////////////////////////////////////////////////////////////////////////// void OSTCFrogOperations::startWrite(unsigned int addr) { Q_ASSERT( _commandMode ); unsigned char buffer[3]; buffer[0] = UPPER(addr); buffer[1] = HIGH(addr); buffer[2] = LOW(addr); _serial.writeByte(0x30); _serial.sleep(10); _serial.writeBlock(buffer, 3); _serial.sleep(10); } ////////////////////////////////////////////////////////////////////////////// void OSTCFrogOperations::stopWrite() { Q_ASSERT( _commandMode ); _serial.flush(); _serial.sleep(200); // Should be > 100ms. unsigned char reply = _serial.readByte(); if( reply != 0x4C ) LOG_THROW("stopWrite"); } ////////////////////////////////////////////////////////////////////////////// void OSTCFrogOperations::readBytes(unsigned int addr, unsigned char* ptr, unsigned int size) { Q_ASSERT( _commandMode ); unsigned char buffer[6]; buffer[0] = UPPER(addr); buffer[1] = HIGH(addr); buffer[2] = LOW(addr); buffer[3] = UPPER(size); buffer[4] = HIGH(size); buffer[5] = LOW(size); _serial.writeByte(0x20); _serial.sleep(10); _serial.writeBlock(buffer, 6); _serial.sleep(10); unsigned int len = _serial.readBlock(ptr, size); if( len < size ) LOG_THROW("readBytes too short"); unsigned char reply = _serial.readByte(); if( reply != 0x4C ) LOG_THROW("readBytes"); } ////////////////////////////////////////////////////////////////////////////// /// /// /// HIGH Level commands /// /// /// ////////////////////////////////////////////////////////////////////////////// void OSTCFrogOperations::getIdentity() { //---- get model HardwareDescriptor hw = hardwareDescriptor(); if( hw != HW_UNKNOWN_OSTC && hw != HW_Frog ) LOG_THROW("Not a Frog."); //---- get identity _serial.sleep(100); // Make sure last command is finished. _serial.purge(); _serial.writeByte('i'); // 0x63 unsigned char buffer[1+2+2+13+1] = {0}; unsigned len = _serial.readBlock(buffer, sizeof buffer); if( len != sizeof buffer || buffer[0] != 'i' || buffer[18] != 0x4D ) LOG_THROW("get identity data"); _serialNumber = buffer[1] + buffer[2]*256; _firmware = buffer[3]*256 + buffer[4]; _description = QString("%1 #%2, v%3.%4, %5") .arg(model()) .arg(_serialNumber, 4, 10, QChar('0')) .arg(_firmware / 256).arg(_firmware % 256) .arg( QString::fromLatin1((char*)buffer+5, 13) .replace(QChar('\0'), "") .trimmed() ); LOG_TRACE("Found " << _description); } ////////////////////////////////////////////////////////////////////////////// void OSTCFrogOperations::writeText(const QString& _msg) { // Pad to 15 chars: QByteArray ascii = (_msg + QString(15, QChar(' '))).left(15).toLatin1(); _serial.sleep(100); // Make sure last command is finished. _serial.purge(); _serial.writeByte('n'); // 0x6E unsigned char reply = _serial.readByte(); if( reply != 'n' ) LOG_THROW("message start"); _serial.writeBlock((unsigned char *)ascii.constData(), 15); reply = _serial.readByte(); if( reply != 0x4D ) LOG_THROW("message end"); } ////////////////////////////////////////////////////////////////////////////// void OSTCFrogOperations::setDate(const QDateTime &date) { unsigned char buffer[6]; buffer[0] = date.time().hour(); buffer[1] = date.time().minute(); buffer[2] = date.time().second(); buffer[3] = date.date().month(); buffer[4] = date.date().day(); buffer[5] = date.date().year() % 100; _serial.sleep(100); // Make sure last command is finished. _serial.purge(); _serial.writeByte('b'); // 0x62 unsigned char reply = _serial.readByte(); if( reply != 'b' ) LOG_THROW("sync time"); _serial. writeBlock( buffer, sizeof buffer); reply = _serial.readByte(); if( reply != 0x4D ) LOG_THROW("sync time end"); writeText("Set " + date.toString("MM/dd hh:mm")); } ////////////////////////////////////////////////////////////////////////////// QSize OSTCFrogOperations::nameSize() const { return QSize(13, 1); } ////////////////////////////////////////////////////////////////////////////// void OSTCFrogOperations::setName(const QString &newName) { QByteArray padded = (newName+QString(13, QChar(' '))).left(13).toLatin1(); _serial.sleep(100); // Make sure last command is finished. _serial.purge(); _serial.writeByte('c'); // 0x63 unsigned char reply = _serial.readByte(); if( reply != 'c' ) LOG_THROW("set custom text"); _serial.writeBlock((unsigned char*)padded.constData(), 13); reply = _serial.readByte(); if( reply != 0x4D ) LOG_THROW("custom text end"); // Re-read new name: getIdentity(); writeText(customText()); } ////////////////////////////////////////////////////////////////////////////// void OSTCFrogOperations::setIcons(const QString &/*fileName*/) { // beginCommands(); // eraseRange(0x000000, 0x00000); // startWrite(0x000000); // stopWrite(); // endCommands(); LOG_THROW( "Set icons: Not yet implemented." ); } int OSTCFrogOperations::firmware() const { return _firmware; } int OSTCFrogOperations::serialNumber() const { return _serialNumber; } QString OSTCFrogOperations::customText() const { return _description.section(',', 2).trimmed(); } /////////////////////////////////////////////////////////////////////////////// static unsigned char frogSecretKey[16] = { 111, 85, 190, 69, 108,254, 242, 19, 231, 49, 248,255, 233, 48, 176,241 }; void OSTCFrogOperations::loadFirmware(HexFile &hex, const QString &fileName) const { hex.allocate(FIRMWARE_SIZE); hex.loadEncrypted(fileName, frogSecretKey); } /////////////////////////////////////////////////////////////////////////////// void OSTCFrogOperations::upgradeFW(const QString &fileName) { try { //---- Load and check firmware --------------------------------------- LOG_TRACE("Loading firmware..."); HexFile hex; loadFirmware(hex, fileName); unsigned int checksum = hex.checksum(); beginCommands(); writeText("Frog Companion"); getIdentity(); unsigned char buffer[5]; buffer[0] = LOW(checksum); buffer[1] = HIGH(checksum); buffer[2] = UPPER(checksum); buffer[3] = UP32(checksum); // Compute magic checksum's checksum. buffer[4] = 0x55; buffer[4] ^= buffer[0]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); buffer[4] ^= buffer[1]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); buffer[4] ^= buffer[2]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); buffer[4] ^= buffer[3]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); _serial.sleep(100); // Make sure last command is finished. _serial.purge(); _serial.writeByte('P'); // 0x50 unsigned char reply = _serial.readByte(); if( reply != 'P' ) LOG_THROW( "Programming start" ); _serial.writeBlock(buffer, sizeof buffer); _serial.sleep(4000); // NOTE: the device never return, because it always to a reset, // with ot without reprogramming... _serial.close(); _isOpen = false; } catch(const Exception& e) { LOG_TRACE(QString("Cannot upgrade: <font color='red'>%1</font>") .arg(e.what())); // Unknown state: so make a hard cleanup: _commandMode = false; _isOpen = false; _serial.close(); } }
