diff Serial.cpp @ 10:9a3c1a6f9833

TODO: FIX HW flow control definitions in case of problem during communication. Background: Flags are not defined in terminos header
author Ideenmodellierer
date Mon, 12 Jan 2026 13:55:38 +0000
parents 177f640940f2
children
line wrap: on
line diff
--- a/Serial.cpp	Mon Jan 12 13:52:28 2026 +0000
+++ b/Serial.cpp	Mon Jan 12 13:55:38 2026 +0000
@@ -36,58 +36,62 @@
 
 #include "Serial.h"
 
+#include "Utils/Exception.h"
 #include "Utils/Log.h"
-#include "Utils/Exception.h"
 
 #include <QString>
 
+#ifdef Q_OS_LINUX
+#include <termios.h>
+#endif
+
 #ifdef WIN32
-#   define  S_WRITE(p, b, li, lo)   WriteFile(p, b, li, &lo, NULL)
-#   define  S_READ(p, b, li, lo)    ReadFile(p, b, li, &lo, NULL)
-#   define  S_LEN                   DWORD
-#   define  S_FLUSH(p)              FlushFileBuffers(p)
-#   define  S_PURGE(p)              PurgeComm(p, PURGE_RXCLEAR | PURGE_TXCLEAR)
-#   define  S_CLOSE(p)              CloseHandle(p)
+#define S_WRITE(p, b, li, lo) WriteFile(p, b, li, &lo, NULL)
+#define S_READ(p, b, li, lo) ReadFile(p, b, li, &lo, NULL)
+#define S_LEN DWORD
+#define S_FLUSH(p) FlushFileBuffers(p)
+#define S_PURGE(p) PurgeComm(p, PURGE_RXCLEAR | PURGE_TXCLEAR)
+#define S_CLOSE(p) CloseHandle(p)
 #else
-#   include <fcntl.h>
-#   include <termios.h>
-#   include <sys/ioctl.h>
-#   include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
 
-#   define  INVALID_HANDLE_VALUE    (-1)
-#   define  S_WRITE(p, b, li, lo)   (lo = write(p, b, li))
-#   define  S_READ(p, b, li, lo)    (lo = read (p, b, li))
-#   define  S_LEN                   ssize_t
-#   define  S_FLUSH(p)              tcdrain(p)
-#   define  S_PURGE(p)              tcflush(p,TCIOFLUSH)
-#   define  S_CLOSE(p)              ::close(p)
+#define INVALID_HANDLE_VALUE (-1)
+#define S_WRITE(p, b, li, lo) (lo = write(p, b, li))
+#define S_READ(p, b, li, lo) (lo = read(p, b, li))
+#define S_LEN ssize_t
+#define S_FLUSH(p) tcdrain(p)
+#define S_PURGE(p) tcflush(p, TCIOFLUSH)
+#define S_CLOSE(p) ::close(p)
 #endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
-Serial::~Serial()
-{}
+Serial::~Serial() {}
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void Serial::open(const QString& port, const QString &type)
+void Serial::open(const QString &port, const QString &type)
 {
-    if( _isOpen ) return;
+    if (_isOpen)
+        return;
     LOG_TRACE("Open " << port << " ...");
 
     _hSerial = INVALID_HANDLE_VALUE;
 
     //------------------------------------------------------------------------
     // Sanity checks.
-    if( port.isEmpty() )
-        LOG_THROW( "Port is not defined." );
-    if( type.isEmpty() )
-        LOG_THROW( "Port type is not defined." );
+    if (port.isEmpty())
+        LOG_THROW("Port is not defined.");
+    if (type.isEmpty())
+        LOG_THROW("Port type is not defined.");
 
-    bool usbMode = type.contains("ostc2c",    Qt::CaseInsensitive)
-                || (   type.contains("ostc3",    Qt::CaseInsensitive)
-                   && !type.contains("ostc3p",   Qt::CaseInsensitive))
-                || type.contains("ostc_cr",  Qt::CaseInsensitive);
+    bool usbMode = type.contains("ostc2c", Qt::CaseInsensitive)
+                   || (type.contains("ostc3", Qt::CaseInsensitive)
+                       && !type.contains("ostc3p", Qt::CaseInsensitive))
+                   || type.contains("ostc_cr", Qt::CaseInsensitive);
     LOG_TRACE((usbMode ? "Fast USB" : "Slow Bluetooth") << " connection mode.");
 
     //------------------------------------------------------------------------
@@ -96,104 +100,105 @@
     //         hence we have to prepend DEVICE NAMESPACE...
     // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
     QByteArray com = port.toLatin1() + "\0";
-    if( !com.startsWith("\\\\.\\") )
+    if (!com.startsWith("\\\\.\\"))
         com = "\\\\.\\" + com;
 
     _hSerial = CreateFileA(com.data(),
-                         GENERIC_READ | GENERIC_WRITE,
-                         0,             // Exclusive access.
-                         NULL,          // No security
-                         OPEN_EXISTING,
-                         FILE_ATTRIBUTE_DEVICE | FILE_FLAG_NO_BUFFERING,
-                         0);
-    if(_hSerial==INVALID_HANDLE_VALUE)
-    {
-        if(GetLastError()==ERROR_FILE_NOT_FOUND)
-            LOG_THROW( "Unknown port" );
-        LOG_THROW( "Unable to open port" );
+                           GENERIC_READ | GENERIC_WRITE,
+                           0,    // Exclusive access.
+                           NULL, // No security
+                           OPEN_EXISTING,
+                           FILE_ATTRIBUTE_DEVICE | FILE_FLAG_NO_BUFFERING,
+                           0);
+    if (_hSerial == INVALID_HANDLE_VALUE) {
+        if (GetLastError() == ERROR_FILE_NOT_FOUND)
+            LOG_THROW("Unknown port");
+        LOG_THROW("Unable to open port");
     }
     S_PURGE(_hSerial);
 
     DCB dcbSerial = {sizeof(dcbSerial), 0};
-    if( !GetCommState(_hSerial, &dcbSerial) )
-        LOG_THROW( "Unable to get COM port config" );
+    if (!GetCommState(_hSerial, &dcbSerial))
+        LOG_THROW("Unable to get COM port config");
 
-    dcbSerial.BaudRate     = CBR_115200;
-    dcbSerial.ByteSize     = 8;
-    dcbSerial.Parity       = NOPARITY;
-    dcbSerial.StopBits     = ONESTOPBIT;
-    dcbSerial.fOutxCtsFlow = DTR_CONTROL_ENABLE;                 // NO HARDWARE FLOW CONTROL
-    dcbSerial.fRtsControl  = RTS_CONTROL_ENABLE; //RTS_CONTROL_DISABLE;   // NO HARDWARE FLOW CONTROL
+    dcbSerial.BaudRate = CBR_115200;
+    dcbSerial.ByteSize = 8;
+    dcbSerial.Parity = NOPARITY;
+    dcbSerial.StopBits = ONESTOPBIT;
+    dcbSerial.fOutxCtsFlow = DTR_CONTROL_ENABLE; // NO HARDWARE FLOW CONTROL
+    dcbSerial.fRtsControl = RTS_CONTROL_ENABLE; //RTS_CONTROL_DISABLE;   // NO HARDWARE FLOW CONTROL
 
-    if( !SetCommState(_hSerial, &dcbSerial) )
-        LOG_THROW( "Unable to set COM port config" );
+    if (!SetCommState(_hSerial, &dcbSerial))
+        LOG_THROW("Unable to set COM port config");
 
-    COMMTIMEOUTS timeouts={0};
-    if( usbMode ) {
-        timeouts.ReadTotalTimeoutConstant  =  500;  // 0.5 sec
-        timeouts.WriteTotalTimeoutConstant = 1000;  // 1.0 sec
+    COMMTIMEOUTS timeouts = {0};
+    if (usbMode) {
+        timeouts.ReadTotalTimeoutConstant = 500;   // 0.5 sec
+        timeouts.WriteTotalTimeoutConstant = 1000; // 1.0 sec
     } else {
-        timeouts.ReadTotalTimeoutConstant  = 2000;  // 2.0 sec timeout.
+        timeouts.ReadTotalTimeoutConstant = 2000; // 2.0 sec timeout.
     }
-    if( !SetCommTimeouts(_hSerial, &timeouts) )
-        LOG_THROW( "Unable to configure port" );
+    if (!SetCommTimeouts(_hSerial, &timeouts))
+        LOG_THROW("Unable to configure port");
 
     LOG_TRACE("Connection:");
     LOG_TRACE("    " << dcbSerial.BaudRate << " bauds.");
-    LOG_TRACE("    " << (int)dcbSerial.ByteSize << " bits, "
-                     << (dcbSerial.Parity ? "+parity, " : "no parity, ")
-                     << (dcbSerial.StopBits ? QString(" +%1").arg(dcbSerial.StopBits) : QString("no")) << " stops bits.");
+    LOG_TRACE(
+        "    " << (int) dcbSerial.ByteSize << " bits, "
+               << (dcbSerial.Parity ? "+parity, " : "no parity, ")
+               << (dcbSerial.StopBits ? QString(" +%1").arg(dcbSerial.StopBits) : QString("no"))
+               << " stops bits.");
     LOG_TRACE("    CTS is " << (dcbSerial.fOutxCtsFlow ? "ON." : "OFF."));
     LOG_TRACE("    RTS is " << ((dcbSerial.fRtsControl == RTS_CONTROL_HANDSHAKE) ? "ON."
-                              : (dcbSerial.fRtsControl == RTS_CONTROL_ENABLE)    ? "FORCED."
-                              :                                                    "OFF."));
-    LOG_TRACE("    Read  timeout " << timeouts.ReadTotalTimeoutConstant  << " msec.");
+                                : (dcbSerial.fRtsControl == RTS_CONTROL_ENABLE)  ? "FORCED."
+                                                                                 : "OFF."));
+    LOG_TRACE("    Read  timeout " << timeouts.ReadTotalTimeoutConstant << " msec.");
     LOG_TRACE("    Write timeout " << timeouts.WriteTotalTimeoutConstant << " msec.");
 #endif
 
     //------------------------------------------------------------------------
 #if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
     QByteArray p = port.toLatin1();
-    if( ! p.startsWith("/dev/") )
+    if (!p.startsWith("/dev/"))
         p = "/dev/" + p;
-    _hSerial = ::open( p.constData(), O_RDWR | O_NOCTTY | O_NONBLOCK, 0);
-    if( _hSerial<0 )
-        LOG_THROW( "Unable to open port " << p);
+    _hSerial = ::open(p.constData(), O_RDWR | O_NOCTTY | O_NONBLOCK, 0);
+    if (_hSerial < 0)
+        LOG_THROW("Unable to open port " << p);
 
-    if( ioctl(_hSerial, TIOCEXCL) < 0 )
-        LOG_THROW( "Port in use" );
+    if (ioctl(_hSerial, TIOCEXCL) < 0)
+        LOG_THROW("Port in use");
 
     // Once opened, clearing the O_NONBLOCK flag.
-    if( fcntl(_hSerial, F_SETFL, 0) < 0 )
-        LOG_THROW( "Can't reset non-blocking I/O" );
+    if (fcntl(_hSerial, F_SETFL, 0) < 0)
+        LOG_THROW("Can't reset non-blocking I/O");
 
     struct termios termios;
     tcgetattr(_hSerial, &termios);
 
     cfmakeraw(&termios);
-    if( cfsetspeed(&termios, B115200) < 0 )
-        LOG_THROW( "Bad port speed" );
+    if (cfsetspeed(&termios, B115200) < 0)
+        LOG_THROW("Bad port speed");
 
-    termios.c_cflag |= CLOCAL| CS8 | CREAD;     // No DTR/DSR/DCD, 8bit.
-    termios.c_cflag &= ~( PARENB | CSTOPB       // No parity, one stop bit.
-                    | CCTS_OFLOW | CRTS_IFLOW); // No hardware flow control.
+    termios.c_cflag |= CLOCAL | CS8 | CREAD;         // No DTR/DSR/DCD, 8bit.
+    termios.c_cflag &= ~(PARENB | CSTOPB  );           // No parity, one stop bit.
+                        // | CCTS_OFLOW | CRTS_IFLOW); // No hardware flow control.
 
-    if( usbMode ) {
+    if (usbMode) {
         // FAST USB: Fix timeout to 0.5 seconde:
-        termios.c_cc[VMIN]  =  0;   // Pure timout mode (no char needed).
-        termios.c_cc[VTIME] =  5;   // 0.5s timeout after last received byte.
+        termios.c_cc[VMIN] = 0;  // Pure timout mode (no char needed).
+        termios.c_cc[VTIME] = 5; // 0.5s timeout after last received byte.
     } else {
         // SLOW BLUETOOTH: Fix timeout to 2.0 sec:
-        termios.c_cc[VMIN]  =  0;   // Pure timout mode (no char needed).
-        termios.c_cc[VTIME] = 20;   // 2.0sec timeout after last received byte.
+        termios.c_cc[VMIN] = 0;   // Pure timout mode (no char needed).
+        termios.c_cc[VTIME] = 20; // 2.0sec timeout after last received byte.
     }
 
-    if( tcsetattr(_hSerial, TCSANOW, &termios) < 0 )
-        LOG_THROW( "Unable to configure port" );
+    if (tcsetattr(_hSerial, TCSANOW, &termios) < 0)
+        LOG_THROW("Unable to configure port");
 #endif
 
-    if( _hSerial == INVALID_HANDLE_VALUE )
-        LOG_THROW( "Port not open" );
+    if (_hSerial == INVALID_HANDLE_VALUE)
+        LOG_THROW("Port not open");
 
     _isOpen = true;
     LOG_TRACE("Device open successfull.");
@@ -203,20 +208,21 @@
 
 void Serial::close()
 {
-    if( !_isOpen ) return;
+    if (!_isOpen)
+        return;
     LOG_TRACE("Device close ...");
 
 #ifdef Q_OS_MAC
-    if( ioctl(_hSerial, TIOCNXCL) < 0 )
-        LOG_THROW( "Port in use" );
+    if (ioctl(_hSerial, TIOCNXCL) < 0)
+        LOG_THROW("Port in use");
 #endif
 
-    if( _hSerial == INVALID_HANDLE_VALUE )
-        LOG_THROW( "Port not open" );
+    if (_hSerial == INVALID_HANDLE_VALUE)
+        LOG_THROW("Port not open");
     S_CLOSE(_hSerial);
 
     _hSerial = INVALID_HANDLE_VALUE;
-    _isOpen  = false;
+    _isOpen = false;
     LOG_TRACE("Device close successfull.");
 }
 
@@ -224,38 +230,38 @@
 
 unsigned char Serial::readByte() const
 {
-    if( !_isOpen )
-        LOG_THROW( "Port not open" );
+    if (!_isOpen)
+        LOG_THROW("Port not open");
 
     unsigned char byte = 0;
     S_LEN len = 0;
     S_READ(_hSerial, &byte, 1, len);
-    if( len != 1 )
-         LOG_THROW_E(ReadTimeout, "< timeout" );
+    if (len != 1)
+        LOG_THROW_E(ReadTimeout, "< timeout");
 
-    if( isprint(byte) )
+    if (isprint(byte))
         LOG_DEBUG("< " << QString::asprintf("%02x '%c'", byte, byte));
     else
-        LOG_DEBUG("< " << QString::asprintf("%02x", byte) );
+        LOG_DEBUG("< " << QString::asprintf("%02x", byte));
 
     return byte;
 }
 
 void Serial::writeByte(unsigned char byte) const
 {
-    if( !_isOpen )
-        LOG_THROW( "Port not open" );
+    if (!_isOpen)
+        LOG_THROW("Port not open");
 
-    if( isprint(byte) )
-        LOG_DEBUG("> " << QString::asprintf("%02x '%c'", byte, byte) );
+    if (isprint(byte))
+        LOG_DEBUG("> " << QString::asprintf("%02x '%c'", byte, byte));
     else
-        LOG_DEBUG("> " << QString::asprintf("%02x", byte) );
+        LOG_DEBUG("> " << QString::asprintf("%02x", byte));
 
     S_LEN len = 0;
     S_WRITE(_hSerial, &byte, 1, len);
-    if( len != 1 )
-   //     LOG_THROW_E(WriteTimeout, "> timeout");
-         LOG_THROW("> timeout");
+    if (len != 1)
+        //     LOG_THROW_E(WriteTimeout, "> timeout");
+        LOG_THROW("> timeout");
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -271,7 +277,8 @@
 {
     unsigned char lo = word & 0xFF;
     unsigned char hi = word >> 8;
-    writeByte(lo); writeByte(hi);
+    writeByte(lo);
+    writeByte(hi);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -279,9 +286,11 @@
 void Serial::writeInt24(unsigned int int24) const
 {
     unsigned char lo = int24 & 0xFF;
-    unsigned char hi = (int24 >>  8) & 0xFF;
+    unsigned char hi = (int24 >> 8) & 0xFF;
     unsigned char up = (int24 >> 16) & 0xFF;
-    writeByte(lo); writeByte(hi); writeByte(up);
+    writeByte(lo);
+    writeByte(hi);
+    writeByte(up);
 }
 
 unsigned int Serial::readInt24() const
@@ -294,51 +303,46 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-unsigned int Serial::readBlock(unsigned char* ptr, unsigned int size) const
+unsigned int Serial::readBlock(unsigned char *ptr, unsigned int size) const
 {
-    if( !_isOpen )
-        LOG_THROW( "Port not open" );
+    if (!_isOpen)
+        LOG_THROW("Port not open");
 
     unsigned int bytes = 0;
 
     bool timeout = false;
-    while( size > 0 )
-    {
+    while (size > 0) {
         // Allow up to 1.0sec for each 4K block.
         S_LEN todo = (size > 4096) ? 4096 : size;
         S_LEN done = 0;
-        S_READ(_hSerial, ptr+bytes, todo, done);
+        S_READ(_hSerial, ptr + bytes, todo, done);
 
-        if( done == 0 ) {
+        if (done == 0) {
             timeout = true;
             break;
         }
 
         size -= done;
-        bytes += (unsigned int)done;
+        bytes += (unsigned int) done;
     }
 
-    if( Log::minLevel <= Log::LEVEL_DEBUG )
-    {
+    if (Log::minLevel <= Log::LEVEL_DEBUG) {
         const int DUMP_LINE = 16;
-        const int DUMP_MAX  = 3*DUMP_LINE;
+        const int DUMP_MAX = 3 * DUMP_LINE;
         const unsigned int length = (bytes < DUMP_MAX) ? bytes : DUMP_MAX;
-        for(unsigned int i=0; i<length; i += DUMP_LINE)
-        {
-            LogAction logger(Log::LEVEL_DEBUG, __FILE__ ,__LINE__, "readBlock()");
+        for (unsigned int i = 0; i < length; i += DUMP_LINE) {
+            LogAction logger(Log::LEVEL_DEBUG, __FILE__, __LINE__, "readBlock()");
             logger << "< ";
-            for(unsigned int j=i; j<bytes && j<(i+DUMP_LINE); ++j)
+            for (unsigned int j = i; j < bytes && j < (i + DUMP_LINE); ++j)
                 logger << QString::asprintf("%02x ", ptr[j]);
-            for(unsigned int j=i; j<bytes && j<(i+DUMP_LINE); ++j)
-                logger << (isprint( ptr[j] )
-                          ? ptr[j]
-                          : (unsigned char)'.');
+            for (unsigned int j = i; j < bytes && j < (i + DUMP_LINE); ++j)
+                logger << (isprint(ptr[j]) ? ptr[j] : (unsigned char) '.');
         }
-        if( length < bytes )
-            LOG_DEBUG("< ... " << (bytes-length) << " more bytes.");
+        if (length < bytes)
+            LOG_DEBUG("< ... " << (bytes - length) << " more bytes.");
     }
 
-    if( timeout )
+    if (timeout)
         LOG_THROW_E(ReadTimeout, "< block timeout (missing " << size << " bytes)");
 
     return bytes;
@@ -346,43 +350,38 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void Serial::writeBlock(const unsigned char* ptr, unsigned int size) const
+void Serial::writeBlock(const unsigned char *ptr, unsigned int size) const
 {
-    if( !_isOpen )
-        LOG_THROW( "Port not open" );
+    if (!_isOpen)
+        LOG_THROW("Port not open");
 
-    if( Log::minLevel <= Log::LEVEL_DEBUG )
-    {
+    if (Log::minLevel <= Log::LEVEL_DEBUG) {
         const int DUMP_LINE = 16;
-        const int DUMP_MAX  = 3*DUMP_LINE;
+        const int DUMP_MAX = 3 * DUMP_LINE;
         const unsigned int length = (size < DUMP_MAX) ? size : DUMP_MAX;
-        for(unsigned int i=0; i<length; i += DUMP_LINE)
-        {
-            LogAction logger(Log::LEVEL_DEBUG, __FILE__ ,__LINE__, "writeBlock()");
+        for (unsigned int i = 0; i < length; i += DUMP_LINE) {
+            LogAction logger(Log::LEVEL_DEBUG, __FILE__, __LINE__, "writeBlock()");
             logger << "> ";
-            for(unsigned int j=i; j<size && j<(i+DUMP_LINE); ++j)
+            for (unsigned int j = i; j < size && j < (i + DUMP_LINE); ++j)
                 logger << QString::asprintf("%02x ", ptr[j]);
-            for(unsigned int j=i; j<size && j<(i+DUMP_LINE); ++j)
-                logger << (isprint( ptr[j] )
-                          ? ptr[j]
-                          : (unsigned char)'.');
+            for (unsigned int j = i; j < size && j < (i + DUMP_LINE); ++j)
+                logger << (isprint(ptr[j]) ? ptr[j] : (unsigned char) '.');
         }
-        if( length < size )
-            LOG_DEBUG("> ... " << (size-length) << " more bytes.");
+        if (length < size)
+            LOG_DEBUG("> ... " << (size - length) << " more bytes.");
     }
 
-    while( size > 0 )
-    {
+    while (size > 0) {
         // Allow up to 1.0sec for each 4K block.
         S_LEN chunck = (size > 4096) ? 4096 : size;
         S_LEN len = 0;
         S_WRITE(_hSerial, ptr, chunck, len);
 
-        if( len == 0  )
+        if (len == 0)
             LOG_THROW_E(WriteTimeout, "> block timeout");
 
         size -= len;
-        ptr  += len;
+        ptr += len;
     }
 
     // Auto-fluh on each write.
@@ -393,8 +392,8 @@
 
 void Serial::purge()
 {
-    if( !_isOpen )
-        LOG_THROW( "Port not open" );
+    if (!_isOpen)
+        LOG_THROW("Port not open");
 
     // Empty incomming buffer
     S_PURGE(_hSerial);
@@ -406,8 +405,8 @@
 
 void Serial::flush() const
 {
-    if( !_isOpen )
-        LOG_THROW( "Port not open" );
+    if (!_isOpen)
+        LOG_THROW("Port not open");
 
     S_FLUSH(_hSerial);
 }
@@ -419,6 +418,6 @@
 #ifdef WIN32
     Sleep(msec);
 #else
-    usleep(msec*1000);
+    usleep(msec * 1000);
 #endif
 }