Mercurial > public > ostc_companion
view Serial.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 Serial.cpp /// \brief RS232 serial i/o. /// \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 "Serial.h" #include "Utils/Log.h" #include "Utils/Exception.h" #include <QString> #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) #else # include <fcntl.h> # include <termios.h> # include <sys/ioctl.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) #endif /////////////////////////////////////////////////////////////////////////////// Serial::~Serial() {} /////////////////////////////////////////////////////////////////////////////// void Serial::open(const QString& port, const QString &type) { 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." ); 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."); //------------------------------------------------------------------------ #ifdef Q_OS_WIN // BUGFIX: COM ports above COM9 are not automatically recognized, // 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("\\\\.\\") ) 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" ); } S_PURGE(_hSerial); DCB dcbSerial = {sizeof(dcbSerial), 0}; 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 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 } else { timeouts.ReadTotalTimeoutConstant = 2000; // 2.0 sec timeout. } 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(" 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."); 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/") ) p = "/dev/" + 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" ); // Once opened, clearing the O_NONBLOCK flag. 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" ); 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 ) { // 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. } 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. } if( tcsetattr(_hSerial, TCSANOW, &termios) < 0 ) LOG_THROW( "Unable to configure port" ); #endif if( _hSerial == INVALID_HANDLE_VALUE ) LOG_THROW( "Port not open" ); _isOpen = true; LOG_TRACE("Device open successfull."); } /////////////////////////////////////////////////////////////////////////////// void Serial::close() { if( !_isOpen ) return; LOG_TRACE("Device close ..."); #ifdef Q_OS_MAC if( ioctl(_hSerial, TIOCNXCL) < 0 ) LOG_THROW( "Port in use" ); #endif if( _hSerial == INVALID_HANDLE_VALUE ) LOG_THROW( "Port not open" ); S_CLOSE(_hSerial); _hSerial = INVALID_HANDLE_VALUE; _isOpen = false; LOG_TRACE("Device close successfull."); } /////////////////////////////////////////////////////////////////////////////// unsigned char Serial::readByte() const { 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( isprint(byte) ) LOG_DEBUG("< " << QString::asprintf("%02x '%c'", byte, byte)); else LOG_DEBUG("< " << QString::asprintf("%02x", byte) ); return byte; } void Serial::writeByte(unsigned char byte) const { if( !_isOpen ) LOG_THROW( "Port not open" ); if( isprint(byte) ) LOG_DEBUG("> " << QString::asprintf("%02x '%c'", byte, byte) ); else 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"); } /////////////////////////////////////////////////////////////////////////////// unsigned short Serial::readShort() const { unsigned char lo = readByte(); unsigned char hi = readByte(); return hi << 8 | lo; } void Serial::writeShort(unsigned short word) const { unsigned char lo = word & 0xFF; unsigned char hi = word >> 8; writeByte(lo); writeByte(hi); } /////////////////////////////////////////////////////////////////////////////// void Serial::writeInt24(unsigned int int24) const { unsigned char lo = int24 & 0xFF; unsigned char hi = (int24 >> 8) & 0xFF; unsigned char up = (int24 >> 16) & 0xFF; writeByte(lo); writeByte(hi); writeByte(up); } unsigned int Serial::readInt24() const { unsigned char lo = readByte(); unsigned char hi = readByte(); unsigned char up = readByte(); return up << 16 | hi << 8 | lo; } /////////////////////////////////////////////////////////////////////////////// unsigned int Serial::readBlock(unsigned char* ptr, unsigned int size) const { if( !_isOpen ) LOG_THROW( "Port not open" ); unsigned int bytes = 0; bool timeout = false; 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); if( done == 0 ) { timeout = true; break; } size -= done; bytes += (unsigned int)done; } if( Log::minLevel <= Log::LEVEL_DEBUG ) { const int DUMP_LINE = 16; 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()"); logger << "< "; 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)'.'); } if( length < bytes ) LOG_DEBUG("< ... " << (bytes-length) << " more bytes."); } if( timeout ) LOG_THROW_E(ReadTimeout, "< block timeout (missing " << size << " bytes)"); return bytes; } /////////////////////////////////////////////////////////////////////////////// void Serial::writeBlock(const unsigned char* ptr, unsigned int size) const { if( !_isOpen ) LOG_THROW( "Port not open" ); if( Log::minLevel <= Log::LEVEL_DEBUG ) { const int DUMP_LINE = 16; 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()"); logger << "> "; 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)'.'); } if( length < size ) LOG_DEBUG("> ... " << (size-length) << " more bytes."); } 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 ) LOG_THROW_E(WriteTimeout, "> block timeout"); size -= len; ptr += len; } // Auto-fluh on each write. flush(); } /////////////////////////////////////////////////////////////////////////////// void Serial::purge() { if( !_isOpen ) LOG_THROW( "Port not open" ); // Empty incomming buffer S_PURGE(_hSerial); LOG_TRACE("Device purged."); } /////////////////////////////////////////////////////////////////////////////// void Serial::flush() const { if( !_isOpen ) LOG_THROW( "Port not open" ); S_FLUSH(_hSerial); } /////////////////////////////////////////////////////////////////////////////// void Serial::sleep(int msec) const { #ifdef WIN32 Sleep(msec); #else usleep(msec*1000); #endif }
