diff HardwareOperations.cpp @ 1:0b3630a29ad8

Initial version based on previous repository. Project was ported to QT6 and in now cmake based.
author Ideenmodellierer <tiefenrauscher@web.de>
date Thu, 27 Nov 2025 18:40:28 +0100
parents
children e30f00f760d3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HardwareOperations.cpp	Thu Nov 27 18:40:28 2025 +0100
@@ -0,0 +1,377 @@
+/////////////////////////////////////////////////////////////////////////////
+/// \file   HardwareOperations.cpp
+/// \brief  Abstract operations for HW dive computers.
+/// \author JD Gascuel.
+/// \sa OSTC3Operations.cpp
+///
+/// \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 "HardwareOperations.h"
+#include "MainWindow.h"
+#include "Utils/Log.h"
+#include <QSerialPortInfo>
+
+#ifdef WIN32
+#   include <winreg.h>
+#endif
+
+#include <QDir>
+
+//////////////////////////////////////////////////////////////////////////////
+
+unsigned char HardwareOperations::retryCommand(Serial& serial,
+                                               unsigned char cmd,
+                                               int retries)
+{
+    for(int retry=0; retry<retries; ++retry)
+    {
+        serial.writeByte( cmd );    // Send command
+        serial.sleep(25);           // Allow 25msec lag.
+
+        try {
+            unsigned char echo = serial.readByte();
+            if( echo == cmd || echo == 'M' || echo == 'L' )
+                return echo;            // Got it, or unknown command...
+        }
+        catch(const ReadTimeout&) {
+            continue;
+        }
+
+        serial.sleep(100);         // Cleanup any pending stuff,
+        serial.purge();            // and retry...
+    }
+
+    return 0xFF;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+HardwareOperations::HardwareDescriptor
+HardwareOperations::hardwareDescriptor()
+{
+    unsigned char echo  = 0;
+    unsigned int  hardFeatures = 0xFF; // timeout response...
+    unsigned int  softFeatures = 0;
+    unsigned int  model        = 0;
+    unsigned char ok    = 0;
+
+    try {
+        //---- First: try the new extended hardware query --------------------
+        echo = retryCommand(_serial, 0x60, 1); // BACKQUOTE char
+        if( echo == 0x60 ) {
+            uchar extendedDescriptor[6];
+            _serial.readBlock(extendedDescriptor, sizeof extendedDescriptor);
+
+
+            hardFeatures = extendedDescriptor[0] * 256 + extendedDescriptor[1];
+            softFeatures = extendedDescriptor[2] * 256 + extendedDescriptor[3];
+            model        = extendedDescriptor[4];
+            ok           = extendedDescriptor[5];
+        }
+        else
+        {
+            // Did we have a timeout ?
+            // In that case, some hwOS versions fails and need a reset of
+            // the connection mode...
+            if( echo == 0xFF )
+            {
+                echo = retryCommand(_serial, 0xBB);     // Try to reconnect
+                if( echo == 0xBB )
+                    echo = _serial.readByte();          // Eat 4d prompt
+            }
+
+            // Then try the OLD hardware descriptor command...
+            echo = retryCommand(_serial, 'j');  // 0x6A
+            if( echo == 'j' ) {
+                hardFeatures = _serial.readByte();
+                ok    = _serial.readByte();
+            }
+        }
+    }
+    catch(const ReadTimeout&) {}
+
+    if( (echo != 0x60 && echo != 'j')
+     || (ok != 'M' && ok != 'L')
+    ) {
+        LOG_TRACE("Old OSTC not responding...");
+        return HW_UNKNOWN_OSTC;
+    }
+
+    switch(hardFeatures) {
+    case HW_Frog        : LOG_TRACE("Frog found"); break;
+    case HW_OSTCSport_a : LOG_TRACE("OSTC Sport found"); break;
+    case HW_OSTC2c      : LOG_TRACE("OSTC 2c found"); break;
+    case HW_OSTC2_a:
+    case HW_OSTC2_c     : LOG_TRACE("OSTC 2 found"); break;
+    case HW_OSTCcR_a:
+    case HW_OSTCcR_b    : LOG_TRACE("OSTC cR found"); break;
+    case HW_OSTC3       : LOG_TRACE("OSTC 3 found"); break;
+    case HW_OSTC3p_a    : LOG_TRACE("OSTC 3+ found"); break;
+    case HW_OSTC4       : LOG_TRACE("OSTC 4 found"); break;
+
+    case HW_OSTCSport_b : LOG_TRACE("OSTC Sport, OSTC 2 or OSTC 3 found."); break;
+
+    case 0xFF: case 0x4C: case 0x4D:
+        LOG_TRACE("old OSTC not responding...");
+        return HW_UNKNOWN_OSTC;
+
+    default:
+     //   LOG_TRACE("Unknown hardware feature =" << QString().sprintf("0x%04x", hardFeatures));
+        LOG_TRACE("Unknown hardware feature =" << QString::asprintf("0x%04x", hardFeatures));
+        break;
+    }
+
+    if( echo == 0x60 ) {
+        LOG_TRACE("    software feature = "  <<  QString::asprintf("0x%04x", softFeatures));
+        LOG_TRACE("    model = "  <<  QString::asprintf("0x%02x", model));
+    }
+
+    return (HardwareDescriptor)hardFeatures;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QStringList HardwareOperations::listBluetoothPorts() const
+{
+    assert(supported() & BLUETOOTH);
+    QStringList list;
+    QString PortDesc;
+    const auto serialPortInfos = QSerialPortInfo::availablePorts();
+
+#if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
+    // TODO: Linux USB search...
+    QRegExp pTemplate = portTemplate();
+    QDir dev("/dev");
+    QStringList all = dev.entryList(QStringList() << "tty.*",
+                                    QDir::NoDotAndDotDot|QDir::System|QDir::Writable,
+                                    QDir::Name);
+
+    for(int i=0; i<(int)all.count(); ++i) {
+        if( pTemplate.indexIn(all[i]) >= 0 ) {
+            LOG_TRACE("Port " << all[i]);
+            list += all[i];
+        }
+        else
+            LOG_DEBUG("...  " << all[i]);
+    }
+#else
+    /* Check the descriptors of the available COMs for Bluetooth tag */
+    for (const QSerialPortInfo &serialPortInfo : serialPortInfos) {
+         PortDesc = serialPortInfo.description();
+         if( PortDesc.contains("Bluetooth"))
+             list += serialPortInfo.portName();
+    }
+
+    if( list.isEmpty() ) /* no port identified => fallback to old detection function */
+    {
+        for(int i=1; i<300; ++i)
+        {
+            QString port = QString("COM%1").arg(i);
+
+            // First: try to read default configuration...
+            COMMCONFIG config = {0};
+            config.dwSize   = sizeof config;
+            config.wVersion = 1;
+            DWORD len = sizeof config;
+
+            QByteArray fixed = "\\\\.\\" + port.toLocal8Bit();
+            if( GetDefaultCommConfigA(fixed.constData(), &config, &len) ) {
+                if( config.dwProviderSubType == PST_RS232 )
+                    list += port;
+            }
+        }
+
+        //---- Second chance
+        //     overide usual MS bug, by looking into the registry for more
+        //     BLUETOOTH ports...
+        {
+            HKEY key;
+            const char registryPath[] = "HARDWARE\\DEVICEMAP\\SERIALCOMM";
+            if( RegOpenKeyExA(HKEY_LOCAL_MACHINE,           // PWD
+                             registryPath,                  // "SOFTWARE\Intel\PSIB"
+                             0,                             // Options
+                             KEY_READ,                      // Desired SAM: See 32bits view.
+                             &key) == ERROR_SUCCESS
+            ) {
+                for(DWORD i = 0; ++i;) {
+                    char nameBuffer[128] = {0};
+                    DWORD nameLen = sizeof nameBuffer;
+                    unsigned char dataBuffer[128] = {0};
+                    DWORD dataLen = sizeof dataBuffer;
+                    long rc = RegEnumValueA(key, i,
+                                  nameBuffer, &nameLen,
+                                  nullptr,
+                                  nullptr,
+                                  dataBuffer, &dataLen);
+                    if( rc != ERROR_SUCCESS )
+                        break;
+
+                    QString name = QString(nameBuffer);
+                    QString port = QString((char*)dataBuffer);
+                    LOG_TRACE("Resource " << i << ": " << name << ", " << port);
+                    if( name.contains("\\BtModem") || name.contains("\\BthModem") || name.contains("\\BtPort") ) {
+                        list += port + " (Bluetooth)";
+                        LOG_TRACE("Port " << name);
+                    }
+                    else
+                        LOG_DEBUG("...  " << name);
+                }
+                RegCloseKey(key);
+            }
+        }
+    }
+#endif
+
+    return list;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QStringList HardwareOperations::listUSBPorts() const
+{
+    assert( !(supported() & BLUETOOTH) );
+    QStringList list;
+
+#if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
+    // TODO: Linux USB search...
+    QDir dev("/dev");
+    QRegExp pTemplate = portTemplate();
+    QStringList all = dev.entryList(QStringList() << "tty.*",
+                                    QDir::System|QDir::Writable|QDir::NoDotAndDotDot,
+                                    QDir::Name);
+    for(int i=0; i<(int)all.count(); ++i) {
+        if( pTemplate.indexIn(all[i]) >= 0 ) {
+            LOG_TRACE("Port " << all[i]);
+            list += all[i];
+        }
+        else
+            LOG_TRACE("... " << all[i]);
+    }
+#else
+    //---- First chance: Try the normal port list:
+    for(int i=1; i<300; ++i)
+    {
+        QString port = QString("COM%1").arg(i);
+
+        // First: try to read default configuration...
+        COMMCONFIG config;
+        memset(&config, 0, sizeof config);
+        config.dwSize   = sizeof config;
+        config.wVersion = 1;
+        DWORD len = sizeof config;
+
+        QByteArray fixed = "\\\\.\\" + port.toLocal8Bit();
+        if( GetDefaultCommConfigA(fixed.constData(), &config, &len) ) {
+            LOG_TRACE("Port " << port << " subtype=" << int(config.dwProviderSubType) );
+            if( config.dwProviderSubType == PST_RS232 )
+                list += port;
+        } else if( len != sizeof config )
+            LOG_THROW("Required " << len << " bytes.");
+        else if( HRESULT rc = GetLastError() )
+            if( rc != 87 )
+                LOG_TRACE("Port " << port << "  error=" << rc );
+
+    }
+    //---- Second chance
+    //     overide usual MS bug, by looking into the registry for more
+    //     USB serial ports...
+    {
+        HKEY key;
+        const char registryPath[] = "HARDWARE\\DEVICEMAP\\SERIALCOMM";
+        if( RegOpenKeyExA(HKEY_LOCAL_MACHINE,           // PWD
+                         registryPath,                  // path
+                         0,                             // Options
+                         KEY_READ,                      // Desired SAM: See 32bits view.
+                         &key) == ERROR_SUCCESS
+        ) {
+            for(DWORD i = 0;; ++i) {
+                char nameBuffer[128] = {0};
+                DWORD nameLen = sizeof nameBuffer;
+                unsigned char dataBuffer[128] = {0};
+                DWORD dataLen = sizeof dataBuffer;
+                long rc = RegEnumValueA(key, i,
+                              nameBuffer, &nameLen,
+                              nullptr,
+                              nullptr,
+                              dataBuffer, &dataLen);
+
+                if( rc == ERROR_NO_MORE_ITEMS )
+                    break;
+
+                if( rc != ERROR_SUCCESS )
+                    LOG_THROW( "Enumeration error" );
+
+                QString name = QString(nameBuffer);
+                QString port = QString((char*)dataBuffer);
+                LOG_TRACE("Resource " << i << ": " << name << ", " << port);
+
+                if( name.contains("\\VCP") )
+                    list += port + " (USB)";
+            }
+            RegCloseKey(key);
+        }
+    }
+#endif
+
+    return list;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+//QString HardwareOperations::scanNewPort()
+//{
+//    static QStringList oldPorts;
+
+//    //---- Get current ports, type-less (ie. strip (USB) or (Bluetooth) ------
+//    QStringList newPorts;
+//    foreach(QString port, listPorts())
+//        newPorts += port.section(" ", 0, 0);
+
+//    //---- Simplify all ports not in the list anymore -----------------------
+//    for(int p=0; p<oldPorts.count(); ++p) {
+//        QString port = oldPorts[p];
+//        if( ! newPorts.contains(port) )
+//            oldPorts.removeAll(port);
+//    }
+
+//    //---- Check for new port in the list -----------------------------------
+//    for(int p=0; p<newPorts.count(); ++p) {
+//        QString port = newPorts[p];
+//        // Did we find a new port ?
+//        if( ! oldPorts.contains(port) ) {
+//            oldPorts += port;
+//            return port;
+//        }
+//    }
+
+//    return QString::null;
+//}