From ec7b85de88d291a3924546cd209be7cf7ba9f843 Mon Sep 17 00:00:00 2001 From: Patrick Menschel Date: Mon, 16 Dec 2024 19:35:56 +0100 Subject: [PATCH] Revert "Comms: Update Serial Threading" This reverts commit 587887d11b5d580dd3c6a453b97ec7e6964c0079. --- .../qserialport_android.cpp | 85 ++- .../libs/qtandroidserialport/qserialport_p.h | 7 +- .../qgroundcontrol/QGCUsbSerialManager.java | 4 +- .../APM/APMAutoPilotPlugin.cc | 2 +- src/Comms/LinkManager.cc | 2 +- src/Comms/LinkManager.h | 3 + src/Comms/QGCSerialPortInfo.cc | 30 +- src/Comms/QGCSerialPortInfo.h | 19 +- src/Comms/SerialLink.cc | 580 +++++++++--------- src/Comms/SerialLink.h | 216 +++---- 10 files changed, 452 insertions(+), 496 deletions(-) diff --git a/android/libs/qtandroidserialport/qserialport_android.cpp b/android/libs/qtandroidserialport/qserialport_android.cpp index c04633c5508..4ca4e53be7d 100644 --- a/android/libs/qtandroidserialport/qserialport_android.cpp +++ b/android/libs/qtandroidserialport/qserialport_android.cpp @@ -3,7 +3,6 @@ #include #include -#include #include @@ -65,12 +64,6 @@ void QSerialPortPrivate::close() { qCDebug(AndroidSerialPortLog) << "Closing" << systemLocation.toLatin1().constData(); - if (_readTimer) { - _readTimer->stop(); - _readTimer->deleteLater(); - _readTimer = nullptr; - } - if (_deviceId != INVALID_DEVICE_ID) { if (!AndroidSerial::close(_deviceId)) { qCWarning(AndroidSerialPortLog) << "Failed to close device with ID" << _deviceId; @@ -100,39 +93,19 @@ bool QSerialPortPrivate::startAsyncRead() setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to start async read"))); } - Q_Q(QSerialPort); - if (!_readTimer) { - _readTimer = new QTimer(q); - _readTimer->setInterval(EMIT_INTERVAL_MS); - (void) QObject::connect(_readTimer, &QTimer::timeout, q, [this]() { - Q_Q(QSerialPort); - QMutexLocker locker(&_readMutex); - if (!buffer.isEmpty()) { - emit q->readyRead(); - } - }); - _readTimer->start(); - } - return result; } bool QSerialPortPrivate::_stopAsyncRead() { - bool result = true; - - if (AndroidSerial::readThreadRunning(_deviceId)) { - result = AndroidSerial::stopReadThread(_deviceId); - if (!result) { - qCWarning(AndroidSerialPortLog) << "Failed to stop async read thread for device ID" << _deviceId; - setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to stop async read"))); - } + if (!AndroidSerial::readThreadRunning(_deviceId)) { + return true; } - if (_readTimer) { - _readTimer->stop(); - _readTimer->deleteLater(); - _readTimer = nullptr; + const bool result = AndroidSerial::stopReadThread(_deviceId); + if (!result) { + qCWarning(AndroidSerialPortLog) << "Failed to stop async read thread for device ID" << _deviceId; + setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to stop async read"))); } return result; @@ -142,7 +115,7 @@ void QSerialPortPrivate::newDataArrived(const char *bytes, int length) { Q_Q(QSerialPort); - // qCDebug(AndroidSerialPortLog) << "newDataArrived" << length; + qCDebug(AndroidSerialPortLog) << "newDataArrived" << length; QMutexLocker locker(&_readMutex); int bytesToRead = length; @@ -153,12 +126,18 @@ void QSerialPortPrivate::newDataArrived(const char *bytes, int length) if (!_stopAsyncRead()) { qCWarning(AndroidSerialPortLog) << "Failed to stop async read."; } + return; } } - // qCDebug(AndroidSerialPortLog) << "nextDataBlockSize" << buffer.nextDataBlockSize(); - (void) buffer.append(bytes, bytesToRead); + // const bool shouldSignal = buffer.isEmpty(); + + buffer.append(bytes, bytesToRead); + + // TODO: Limit signals as one read can handle multiple signals + // if (shouldSignal) + emit q->readyRead(); _readWaitCondition.wakeAll(); } @@ -169,17 +148,19 @@ bool QSerialPortPrivate::waitForReadyRead(int msecs) return true; } - QDeadlineTimer deadline(msecs); + QElapsedTimer timer; + timer.start(); while (buffer.isEmpty()) { - if (!_readWaitCondition.wait(&_readMutex, deadline)) { - break; + if (_readWaitCondition.wait(&_readMutex, msecs - static_cast(timer.elapsed()))) { + if (!buffer.isEmpty()) { + return true; + } } - if (!buffer.isEmpty()) { - return true; + if (timer.elapsed() >= msecs) { + break; } } - locker.unlock(); qCWarning(AndroidSerialPortLog) << "Timeout while waiting for ready read on device ID" << _deviceId; setError(QSerialPortErrorInfo(QSerialPort::TimeoutError, QSerialPort::tr("Timeout while waiting for ready read"))); @@ -200,9 +181,7 @@ bool QSerialPortPrivate::waitForBytesWritten(int msecs) bool QSerialPortPrivate::_writeDataOneShot(int msecs) { - if (writeBuffer.isEmpty()) { - return true; - } + Q_Q(QSerialPort); qint64 pendingBytesWritten = 0; @@ -211,9 +190,10 @@ bool QSerialPortPrivate::_writeDataOneShot(int msecs) const qint64 dataSize = writeBuffer.nextDataBlockSize(); const qint64 written = _writeToPort(dataPtr, dataSize, msecs); + if (written < 0) { qCWarning(AndroidSerialPortLog) << "Failed to write data one shot on device ID" << _deviceId; - // setError(QSerialPortErrorInfo(QSerialPort::WriteError, QSerialPort::tr("Failed to write data one shot"))); + setError(QSerialPortErrorInfo(QSerialPort::WriteError, QSerialPort::tr("Failed to write data one shot"))); return false; } @@ -223,7 +203,6 @@ bool QSerialPortPrivate::_writeDataOneShot(int msecs) const bool result = (pendingBytesWritten > 0); if (result) { - Q_Q(QSerialPort); emit q->bytesWritten(pendingBytesWritten); } @@ -235,7 +214,7 @@ qint64 QSerialPortPrivate::_writeToPort(const char *data, qint64 maxSize, int ti const qint64 result = AndroidSerial::write(_deviceId, data, maxSize, timeout, async); if (result < 0) { qCWarning(AndroidSerialPortLog) << "Failed to write to port ID" << _deviceId; - // setError(QSerialPortErrorInfo(QSerialPort::WriteError, QSerialPort::tr("Failed to write to port"))); + setError(QSerialPortErrorInfo(QSerialPort::WriteError, QSerialPort::tr("Failed to write to port"))); } return result; @@ -259,7 +238,7 @@ qint64 QSerialPortPrivate::writeData(const char *data, qint64 maxSize) bool QSerialPortPrivate::flush() { - const bool result = _writeDataOneShot(); + const bool result = _writeDataOneShot(0); if (!result) { qCWarning(AndroidSerialPortLog) << "Flush operation failed for device ID" << _deviceId; setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to flush"))); @@ -291,6 +270,14 @@ bool QSerialPortPrivate::clear(QSerialPort::Directions directions) // setError(QSerialPortErrorInfo(QSerialPort::UnknownError, QSerialPort::tr("Failed to purge buffers"))); } + /*if (input) { + buffer.clear(); + } + + if (output) { + writeBuffer.clear(); + }*/ + return result; } diff --git a/android/libs/qtandroidserialport/qserialport_p.h b/android/libs/qtandroidserialport/qserialport_p.h index c2a31d8b71c..3136318d190 100644 --- a/android/libs/qtandroidserialport/qserialport_p.h +++ b/android/libs/qtandroidserialport/qserialport_p.h @@ -33,15 +33,11 @@ constexpr qint64 DEFAULT_READ_BUFFER_SIZE = MAX_READ_SIZE; constexpr qint64 DEFAULT_WRITE_BUFFER_SIZE = 16 * 1024; constexpr int DEFAULT_WRITE_TIMEOUT = 0; constexpr int DEFAULT_READ_TIMEOUT = 0; -constexpr int EMIT_THRESHOLD = 64; -constexpr int EMIT_INTERVAL_MS = 10; #ifndef QSERIALPORT_BUFFERSIZE #define QSERIALPORT_BUFFERSIZE DEFAULT_WRITE_BUFFER_SIZE #endif -class QTimer; - Q_DECLARE_LOGGING_CATEGORY(AndroidSerialPortLog) QT_BEGIN_NAMESPACE @@ -136,7 +132,7 @@ class QSerialPortPrivate : public QIODevicePrivate qint64 _writeToPort(const char *data, qint64 maxSize, int timeout = DEFAULT_WRITE_TIMEOUT, bool async = false); bool _stopAsyncRead(); bool _setParameters(qint32 baudRate, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, QSerialPort::Parity parity); - bool _writeDataOneShot(int msecs = DEFAULT_WRITE_TIMEOUT); + bool _writeDataOneShot(int msecs); static qint32 _settingFromBaudRate(qint32 baudRate); static int _stopBitsToAndroidStopBits(QSerialPort::StopBits stopBits); @@ -147,7 +143,6 @@ class QSerialPortPrivate : public QIODevicePrivate int _deviceId = INVALID_DEVICE_ID; // QString _serialNumber; - QTimer *_readTimer = nullptr; QMutex _readMutex; QWaitCondition _readWaitCondition; }; diff --git a/android/src/org/mavlink/qgroundcontrol/QGCUsbSerialManager.java b/android/src/org/mavlink/qgroundcontrol/QGCUsbSerialManager.java index 76e041fd43c..b864cda234f 100644 --- a/android/src/org/mavlink/qgroundcontrol/QGCUsbSerialManager.java +++ b/android/src/org/mavlink/qgroundcontrol/QGCUsbSerialManager.java @@ -144,8 +144,6 @@ public void onReceive(Context context, Intent intent) { break; } - updateCurrentDrivers(); - try { nativeUpdateAvailableJoysticks(); } catch (final Exception ex) { @@ -411,7 +409,7 @@ private static UsbSerialPort findPortByDeviceId(final int deviceId) { * @return An array of device information strings or null if no devices are available. */ public static String[] availableDevicesInfo() { - // updateCurrentDrivers(); + updateCurrentDrivers(); if (usbManager.getDeviceList().size() < 1) { return null; diff --git a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc index 589eb3fce27..434f54fc15a 100644 --- a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc @@ -204,7 +204,7 @@ void APMAutoPilotPlugin::_checkForBadCubeBlack(void) // FIXME: Put back for (const QVariant& varLink: _vehicle->links()) { SerialLink* serialLink = varLink.value(); - if (serialLink && QSerialPortInfo(*serialLink->port()).description().contains(QStringLiteral("CubeBlack"))) { + if (serialLink && QSerialPortInfo(*serialLink->_hackAccessToPort()).description().contains(QStringLiteral("CubeBlack"))) { cubeBlackFound = true; } diff --git a/src/Comms/LinkManager.cc b/src/Comms/LinkManager.cc index 3a2cdfebd81..9c0981c89ef 100644 --- a/src/Comms/LinkManager.cc +++ b/src/Comms/LinkManager.cc @@ -945,7 +945,7 @@ void LinkManager::_updateSerialPorts() for (const QGCSerialPortInfo &info: portList) { const QString port = info.systemLocation().trimmed(); _commPortList += port; - _commPortDisplayList += SerialConfiguration::cleanPortDisplayName(port); + _commPortDisplayList += SerialConfiguration::cleanPortDisplayname(port); } } diff --git a/src/Comms/LinkManager.h b/src/Comms/LinkManager.h index 568b8bce7b6..7889d9339fb 100644 --- a/src/Comms/LinkManager.h +++ b/src/Comms/LinkManager.h @@ -76,6 +76,9 @@ class LinkManager : public QObject void loadLinkConfigurationList(); void saveLinkConfigurationList(); + /// Suspend automatic confguration updates (during link maintenance for instance) + void suspendConfigurationUpdates(bool suspend) { _configUpdateSuspended = suspend; } + /// Sets the flag to suspend the all new connections /// @param reason User visible reason to suspend connections void setConnectionsSuspended(const QString &reason) { _connectionsSuspended = true; _connectionsSuspendedReason = reason; } diff --git a/src/Comms/QGCSerialPortInfo.cc b/src/Comms/QGCSerialPortInfo.cc index beef48b45de..c9283a847f1 100644 --- a/src/Comms/QGCSerialPortInfo.cc +++ b/src/Comms/QGCSerialPortInfo.cc @@ -52,7 +52,7 @@ bool QGCSerialPortInfo::_loadJsonData() QString errorString; int version; - const QJsonObject json = JsonHelper::openInternalQGCJsonFile(QStringLiteral(":/json/USBBoardInfo.json"), QString(_jsonFileTypeValue), 1, 1, version, errorString); + const QJsonObject json = JsonHelper::openInternalQGCJsonFile(":/json/USBBoardInfo.json", _jsonFileTypeValue, 1, 1, version, errorString); if (!errorString.isEmpty()) { qCWarning(QGCSerialPortInfoLog) << "Internal Error:" << errorString; return false; @@ -165,14 +165,7 @@ bool QGCSerialPortInfo::_loadJsonData() QGCSerialPortInfo::BoardType_t QGCSerialPortInfo::_boardClassStringToType(const QString &boardClass) { - static const BoardClassString2BoardType_t rgBoardClass2BoardType[BoardTypeUnknown] = { - { _boardTypeToString(BoardTypePixhawk), BoardTypePixhawk }, - { _boardTypeToString(BoardTypeRTKGPS), BoardTypeRTKGPS }, - { _boardTypeToString(BoardTypeSiKRadio), BoardTypeSiKRadio }, - { _boardTypeToString(BoardTypeOpenPilot), BoardTypeOpenPilot }, - }; - - for (const BoardClassString2BoardType_t &board : rgBoardClass2BoardType) { + for (const BoardClassString2BoardType_t &board : _rgBoardClass2BoardType) { if (boardClass == board.classString) { return board.boardType; } @@ -252,14 +245,12 @@ QString QGCSerialPortInfo::_boardTypeToString(BoardType_t boardType) QList QGCSerialPortInfo::availablePorts() { QList list; - - const QList availablePorts = QSerialPortInfo::availablePorts(); - for (const QSerialPortInfo &portInfo : availablePorts) { + for (const QSerialPortInfo &portInfo : QSerialPortInfo::availablePorts()) { if (isSystemPort(portInfo)) { continue; } - const QGCSerialPortInfo *const qgcPortInfo = reinterpret_cast(&portInfo); + const QGCSerialPortInfo* const qgcPortInfo = reinterpret_cast(&portInfo); list << *qgcPortInfo; } @@ -307,10 +298,11 @@ bool QGCSerialPortInfo::canFlash() const return false; } - static const QList flashable = { - BoardTypePixhawk, - BoardTypeSiKRadio - }; - - return flashable.contains(boardType); + switch(boardType) { + case QGCSerialPortInfo::BoardTypePixhawk: + case QGCSerialPortInfo::BoardTypeSiKRadio: + return true; + default: + return false; + } } diff --git a/src/Comms/QGCSerialPortInfo.h b/src/Comms/QGCSerialPortInfo.h index 3888fcc05e2..ce83f4713d1 100644 --- a/src/Comms/QGCSerialPortInfo.h +++ b/src/Comms/QGCSerialPortInfo.h @@ -33,13 +33,12 @@ class QGCSerialPortInfo : public QSerialPortInfo ~QGCSerialPortInfo(); enum BoardType_t { - BoardTypePixhawk = 0, + BoardTypePixhawk, BoardTypeSiKRadio, BoardTypeOpenPilot, BoardTypeRTKGPS, BoardTypeUnknown }; - bool getBoardInfo(BoardType_t &boardType, QString &name) const; /// @return true: we can flash this board type @@ -56,11 +55,6 @@ class QGCSerialPortInfo : public QSerialPortInfo static QList availablePorts(); private: - struct BoardClassString2BoardType_t { - const QString classString; - const BoardType_t boardType = BoardTypeUnknown; - }; - static bool _loadJsonData(); static BoardType_t _boardClassStringToType(const QString &boardClass); static QString _boardTypeToString(BoardType_t boardType); @@ -68,6 +62,17 @@ class QGCSerialPortInfo : public QSerialPortInfo static bool _jsonLoaded; static bool _jsonDataValid; + struct BoardClassString2BoardType_t { + const char *classString; + BoardType_t boardType; + }; + static constexpr const BoardClassString2BoardType_t _rgBoardClass2BoardType[BoardTypeUnknown] = { + { "Pixhawk", QGCSerialPortInfo::BoardTypePixhawk }, + { "RTK GPS", QGCSerialPortInfo::BoardTypeRTKGPS }, + { "SiK Radio", QGCSerialPortInfo::BoardTypeSiKRadio }, + { "OpenPilot", QGCSerialPortInfo::BoardTypeOpenPilot }, + }; + struct BoardInfo_t { int vendorId; int productId; diff --git a/src/Comms/SerialLink.cc b/src/Comms/SerialLink.cc index de16c3eab31..18de72d747e 100644 --- a/src/Comms/SerialLink.cc +++ b/src/Comms/SerialLink.cc @@ -8,402 +8,410 @@ ****************************************************************************/ #include "SerialLink.h" +#include "QGC.h" #include "QGCLoggingCategory.h" -#include "QGCSerialPortInfo.h" #ifdef Q_OS_ANDROID +#include "QGCApplication.h" +#include "LinkManager.h" #include "qserialportinfo.h" #else #include #endif +#include #include -#include -QGC_LOGGING_CATEGORY(SerialLinkLog, "qgc.comms.seriallink") +QGC_LOGGING_CATEGORY(SerialLinkLog, "SerialLinkLog") -namespace { - constexpr int CONNECT_TIMEOUT_MS = 1000; - constexpr int READ_TIMEOUT_MS = 100; -} - -/*===========================================================================*/ - -SerialConfiguration::SerialConfiguration(const QString &name, QObject *parent) - : LinkConfiguration(name, parent) +SerialLink::SerialLink(SharedLinkConfigurationPtr& config) + : LinkInterface(config) + , _serialConfig(qobject_cast(config.get())) { - // qCDebug(SerialLinkLog) << Q_FUNC_INFO << this; + qRegisterMetaType(); + qCDebug(SerialLinkLog) << "Create SerialLink portName:baud:flowControl:parity:dataButs:stopBits" << _serialConfig->portName() << _serialConfig->baud() << _serialConfig->flowControl() + << _serialConfig->parity() << _serialConfig->dataBits() << _serialConfig->stopBits(); } -SerialConfiguration::SerialConfiguration(const SerialConfiguration *source, QObject *parent) - : LinkConfiguration(source, parent) +SerialLink::~SerialLink() { - // qCDebug(SerialLinkLog) << Q_FUNC_INFO << this; - - SerialConfiguration::copyFrom(source); + disconnect(); } -SerialConfiguration::~SerialConfiguration() +bool SerialLink::_isBootloader() { - // qCDebug(SerialLinkLog) << Q_FUNC_INFO << this; + QList portList = QSerialPortInfo::availablePorts(); + if( portList.count() == 0){ + return false; + } + for (const QSerialPortInfo &info: portList) + { + qCDebug(SerialLinkLog) << "PortName : " << info.portName() << "Description : " << info.description(); + qCDebug(SerialLinkLog) << "Manufacturer: " << info.manufacturer(); + if (info.portName().trimmed() == _serialConfig->portName() && + (info.description().toLower().contains("bootloader") || + info.description().toLower().contains("px4 bl") || + info.description().toLower().contains("px4 fmu v1.6"))) { + qCDebug(SerialLinkLog) << "BOOTLOADER FOUND"; + return true; + } + } + // Not found + return false; } -void SerialConfiguration::setPortName(const QString &name) +void SerialLink::_writeBytes(const QByteArray &data) { - const QString portName = name.trimmed(); - if (portName.isEmpty()) { - return; - } - - if (portName != _portName) { - _portName = portName; - emit portNameChanged(); + if(_port && _port->isOpen()) { + emit bytesSent(this, data); + _port->write(data); + } else { + // Error occurred + qWarning() << "Serial port not writeable"; + _emitLinkError(tr("Could not send data - link %1 is disconnected!").arg(_config->name())); } - - const QString portDisplayName = cleanPortDisplayName(portName); - setPortDisplayName(portDisplayName); } -void SerialConfiguration::copyFrom(const LinkConfiguration *source) +void SerialLink::disconnect(void) { - Q_ASSERT(source); - LinkConfiguration::copyFrom(source); + if (_port) { + // This prevents stale signals from calling the link after it has been deleted + QObject::disconnect(_port, &QIODevice::readyRead, this, &SerialLink::_readBytes); + _port->close(); + _port->deleteLater(); + _port = nullptr; + emit disconnected(); + } - const SerialConfiguration* const serialSource = qobject_cast(source); - Q_ASSERT(serialSource); - - setBaud(serialSource->baud()); - setDataBits(serialSource->dataBits()); - setFlowControl(serialSource->flowControl()); - setStopBits(serialSource->stopBits()); - setParity(serialSource->parity()); - setPortName(serialSource->portName()); - setPortDisplayName(serialSource->portDisplayName()); - setUsbDirect(serialSource->usbDirect()); +#ifdef Q_OS_ANDROID + LinkManager::instance()->suspendConfigurationUpdates(false); +#endif } -void SerialConfiguration::loadSettings(QSettings &settings, const QString &root) +bool SerialLink::_connect(void) { - settings.beginGroup(root); + qCDebug(SerialLinkLog) << "CONNECT CALLED"; - setBaud(settings.value("baud", _baud).toInt()); - setDataBits(static_cast(settings.value("dataBits", _dataBits).toInt())); - setFlowControl(static_cast(settings.value("flowControl", _flowControl).toInt())); - setStopBits(static_cast(settings.value("stopBits", _stopBits).toInt())); - setParity(static_cast(settings.value("parity", _parity).toInt())); - setPortName(settings.value("portName", _portName).toString()); - setPortDisplayName(settings.value("portDisplayName", _portDisplayName).toString()); - - settings.endGroup(); -} + if (_port) { + qCWarning(SerialLinkLog) << "connect called while already connected"; + return true; + } -void SerialConfiguration::saveSettings(QSettings &settings, const QString &root) -{ - settings.beginGroup(root); +#ifdef Q_OS_ANDROID + LinkManager::instance()->suspendConfigurationUpdates(true); +#endif - settings.setValue("baud", _baud); - settings.setValue("dataBits", _dataBits); - settings.setValue("flowControl", _flowControl); - settings.setValue("stopBits", _stopBits); - settings.setValue("parity", _parity); - settings.setValue("portName", _portName); - settings.setValue("portDisplayName", _portDisplayName); + QSerialPort::SerialPortError error; + QString errorString; + + // Initialize the connection + if (!_hardwareConnect(error, errorString)) { + if (_config->isAutoConnect()) { + // Be careful with spitting out open error related to trying to open a busy port using autoconnect + if (error == QSerialPort::PermissionError) { + // Device already open, ignore and fail connect + return false; + } + } - settings.endGroup(); + _emitLinkError(tr("Error connecting: Could not create port. %1").arg(errorString)); + return false; + } + return true; } -QStringList SerialConfiguration::supportedBaudRates() +/// Performs the actual hardware port connection. +/// @param[out] error if failed +/// @param[out] error string if failed +/// @return success/fail +bool SerialLink::_hardwareConnect(QSerialPort::SerialPortError& error, QString& errorString) { - QStringList supportBaudRateStrings; + if (_port) { + qCDebug(SerialLinkLog) << "SerialLink:" << QString::number((qulonglong)this, 16) << "closing port"; + _port->close(); - const QList rates = QSerialPortInfo::standardBaudRates(); - for (qint32 rate : rates) { - supportBaudRateStrings.append(QString::number(rate)); + // Wait 50 ms while continuing to run the event queue + for (unsigned i = 0; i < 10; i++) { + QGC::SLEEP::usleep(5000); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + } + delete _port; + _port = nullptr; } - return supportBaudRateStrings; -} - -QString SerialConfiguration::cleanPortDisplayName(const QString &name) -{ - const QList availablePorts = QSerialPortInfo::availablePorts(); - for (const QSerialPortInfo &portInfo : availablePorts) { - if (portInfo.systemLocation() == name) { - return portInfo.portName(); + qCDebug(SerialLinkLog) << "SerialLink: hardwareConnect to " << _serialConfig->portName(); + + // If we are in the Pixhawk bootloader code wait for it to timeout + if (_isBootloader()) { + qCDebug(SerialLinkLog) << "Not connecting to a bootloader, waiting for 2nd chance"; + const unsigned retry_limit = 12; + unsigned retries; + + for (retries = 0; retries < retry_limit; retries++) { + if (!_isBootloader()) { + // Wait 500 ms while continuing to run the event loop + for (unsigned i = 0; i < 100; i++) { + QGC::SLEEP::msleep(5); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + } + break; + } + + // Wait 500 ms while continuing to run the event loop + for (unsigned i = 0; i < 100; i++) { + QGC::SLEEP::msleep(5); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + } + } + // Check limit + if (retries == retry_limit) { + // bail out + qWarning() << "Timeout waiting for something other than booloader"; + return false; } } - return QString(); -} + _port = new QSerialPort(_serialConfig->portName(), this); -/*===========================================================================*/ -SerialWorker::SerialWorker(const SerialConfiguration *config, QObject *parent) - : QObject(parent) - , _config(config) - , _port(new QSerialPort(this)) - , _timer(new QTimer(this)) -{ - // qCDebug(SerialLinkLog) << Q_FUNC_INFO << this; + // After the bootloader times out, it still can take a second or so for the Pixhawk USB driver to come up and make + // the port available for open. So we retry a few times to wait for it. +#ifdef Q_OS_ANDROID + _port->open(QIODevice::ReadWrite); +#else - (void) qRegisterMetaType("QSerialPort::SerialPortError"); -} + // Try to open the port three times + for (int openRetries = 0; openRetries < 3; openRetries++) { + if (!_port->open(QIODevice::ReadWrite)) { + qCDebug(SerialLinkLog) << "Port open failed, retrying"; + // Wait 250 ms while continuing to run the event loop + for (unsigned i = 0; i < 50; i++) { + QGC::SLEEP::msleep(5); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + } + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + } else { + break; + } + } +#endif + if (!_port->isOpen() ) { + qDebug() << "open failed" << _port->errorString() << _port->error() << _config->name() << "autconnect:" << _config->isAutoConnect(); + error = _port->error(); + errorString = _port->errorString(); + _port->close(); + delete _port; + _port = nullptr; + return false; // couldn't open serial port + } -SerialWorker::~SerialWorker() -{ - disconnectFromPort(); + _port->setDataTerminalReady(true); - // qCDebug(SerialLinkLog) << Q_FUNC_INFO << this; -} + qCDebug(SerialLinkLog) << "Configuring port"; + _port->setBaudRate (_serialConfig->baud()); + _port->setDataBits (static_cast (_serialConfig->dataBits())); + _port->setFlowControl (static_cast (_serialConfig->flowControl())); + _port->setStopBits (static_cast (_serialConfig->stopBits())); + _port->setParity (static_cast (_serialConfig->parity())); -bool SerialWorker::isConnected() const -{ - return _port->isOpen(); -} + emit connected(); -void SerialWorker::setupPort() -{ - (void) connect(_port, &QSerialPort::aboutToClose, this, &SerialWorker::_onPortDisconnected); - (void) connect(_port, &QSerialPort::readyRead, this, &SerialWorker::_onPortReadyRead); - // (void) connect(_port, &QSerialPort::bytesWritten, this, &SerialWorker::_onPortBytesWritten); - (void) connect(_port, &QSerialPort::errorOccurred, this, &SerialWorker::_onPortErrorOccurred); + QObject::connect(_port, &QSerialPort::errorOccurred, this, &SerialLink::linkError); + QObject::connect(_port, &QIODevice::readyRead, this, &SerialLink::_readBytes); - (void) connect(_timer, &QTimer::timeout, this, &SerialWorker::_checkPortAvailability); - _timer->start(CONNECT_TIMEOUT_MS); + qCDebug(SerialLinkLog) << "Connection SeriaLink: " << "with settings" << _serialConfig->portName() + << _serialConfig->baud() << _serialConfig->dataBits() << _serialConfig->parity() << _serialConfig->stopBits(); + + return true; // successful connection } -void SerialWorker::connectToPort() +void SerialLink::_readBytes(void) { - if (isConnected()) { - qCWarning(SerialLinkLog) << "Already connected to" << _port->portName(); - return; - } - - _port->setPortName(_config->portName()); - - const QGCSerialPortInfo portInfo(*_port); - if (portInfo.isBootloader()) { - qCWarning(SerialLinkLog) << "Not connecting to bootloader" << _port->portName(); - emit errorOccurred(tr("Not connecting to a bootloader")); - _onPortDisconnected(); - return; - } - - _errorEmitted = false; - - qCDebug(SerialLinkLog) << "Attempting to open port" << _port->portName(); - if (!_port->open(QIODevice::ReadWrite)) { - qCWarning(SerialLinkLog) << "Opening port" << _port->portName() << "failed:" << _port->errorString(); - - if (!_errorEmitted) { - emit errorOccurred(tr("Could not open port: %1").arg(_port->errorString())); - _errorEmitted = true; + if (_port && _port->isOpen()) { + qint64 byteCount = _port->bytesAvailable(); + if (byteCount) { + QByteArray buffer; + buffer.resize(byteCount); + _port->read(buffer.data(), buffer.size()); + emit bytesReceived(this, buffer); } - - _onPortDisconnected(); - return; + } else { + // Error occurred + qWarning() << "Serial port not readable"; + _emitLinkError(tr("Could not read data - link %1 is disconnected!").arg(_config->name())); } - - _onPortConnected(); } -void SerialWorker::disconnectFromPort() -{ - if (!isConnected()) { - qCDebug(SerialLinkLog) << "Already disconnected from port:" << _port->portName(); - return; +void SerialLink::linkError(QSerialPort::SerialPortError error) +{ + switch (error) { + case QSerialPort::NoError: + break; + case QSerialPort::ResourceError: + // This indicates the hardware was pulled from the computer. For example usb cable unplugged. + _connectionRemoved(); + break; + default: + // You can use the following qDebug output as needed during development. Make sure to comment it back out + // when you are done. The reason for this is that this signal is very noisy. For example if you try to + // connect to a PixHawk before it is ready to accept the connection it will output a continuous stream + // of errors until the Pixhawk responds. + // qCDebug(SerialLinkLog) << "SerialLink::linkError" << error; + break; } - - qCDebug(SerialLinkLog) << "Attempting to close port:" << _port->portName(); - _port->close(); } -void SerialWorker::writeData(const QByteArray &data) +bool SerialLink::isConnected() const { - if (data.isEmpty()) { - emit errorOccurred(tr("Data to Send is Empty")); - return; - } - - if (!isConnected()) { - emit errorOccurred(tr("Port is not Connected")); - return; - } - - if (!_port->isWritable()) { - emit errorOccurred(tr("Port is not Writable")); - return; - } + bool isConnected = false; - qint64 totalBytesWritten = 0; - while (totalBytesWritten < data.size()) { - const qint64 bytesWritten = _port->write(data.constData() + totalBytesWritten, data.size() - totalBytesWritten); - if (bytesWritten == -1) { - emit errorOccurred(tr("Could Not Send Data - Write Failed: %1").arg(_port->errorString())); - return; - } else if (bytesWritten == 0) { - emit errorOccurred(tr("Could Not Send Data - Write Returned 0 Bytes")); - return; - } - totalBytesWritten += bytesWritten; + if (_port) { + isConnected = _port->isOpen(); } - const QByteArray sent = data.first(totalBytesWritten); - emit dataSent(sent); + return isConnected; } -void SerialWorker::_onPortConnected() +void SerialLink::_emitLinkError(const QString& errorMsg) { - qCDebug(SerialLinkLog) << "Port connected:" << _port->portName(); - - _port->setDataTerminalReady(true); - _port->setBaudRate(_config->baud()); - _port->setDataBits(static_cast(_config->dataBits())); - _port->setFlowControl(static_cast(_config->flowControl())); - _port->setStopBits(static_cast(_config->stopBits())); - _port->setParity(static_cast(_config->parity())); - - _errorEmitted = false; - emit connected(); + QString msg("Error on link %1. %2"); + qDebug() << errorMsg; + emit communicationError(tr("Link Error"), msg.arg(_config->name()).arg(errorMsg)); } -void SerialWorker::_onPortDisconnected() +bool SerialLink::isSecureConnection() { - qCDebug(SerialLinkLog) << "Port disconnected:" << _port->portName(); - _errorEmitted = false; - emit disconnected(); + return _serialConfig && _serialConfig->usbDirect(); } -void SerialWorker::_onPortReadyRead() -{ - const QByteArray data = _port->readAll(); - if (!data.isEmpty()) { - // qCDebug(SerialLinkLog) << "_onPortReadyRead:" << data.size(); - emit dataReceived(data); - } -} +//-------------------------------------------------------------------------- +//-- SerialConfiguration -void SerialWorker::_onPortBytesWritten(qint64 bytes) const +SerialConfiguration::SerialConfiguration(const QString& name) : LinkConfiguration(name) { - qCDebug(SerialLinkLog) << _port->portName() << "Wrote" << bytes << "bytes"; + _baud = 57600; + _flowControl= QSerialPort::NoFlowControl; + _parity = QSerialPort::NoParity; + _dataBits = 8; + _stopBits = 1; + _usbDirect = false; } -void SerialWorker::_onPortErrorOccurred(QSerialPort::SerialPortError portError) +SerialConfiguration::SerialConfiguration(const SerialConfiguration* copy) : LinkConfiguration(copy) { - if (portError == QSerialPort::NoError) { - qCDebug(SerialLinkLog) << "About to open port" << _port->portName(); - return; - } /* else if (error == QSerialPort::ResourceError) { - serialPort->close(); - }*/ - - const QString errorString = _port->errorString(); - - if (!_errorEmitted) { - qCWarning(SerialLinkLog) << "Port error:" << portError << errorString; - emit errorOccurred(errorString); - _errorEmitted = true; - } + _baud = copy->baud(); + _flowControl = copy->flowControl(); + _parity = copy->parity(); + _dataBits = copy->dataBits(); + _stopBits = copy->stopBits(); + _portName = copy->portName(); + _portDisplayName = copy->portDisplayName(); + _usbDirect = copy->_usbDirect; } -void SerialWorker::_checkPortAvailability() +void SerialConfiguration::copyFrom(const LinkConfiguration *source) { - if (!isConnected()) { - return; - } - - bool portExists = false; - const auto availablePorts = QSerialPortInfo::availablePorts(); - for (const QSerialPortInfo &info : availablePorts) { - if (info.portName() == _config->portDisplayName()) { - portExists = true; - break; - } - } - - if (!portExists) { - _port->close(); + LinkConfiguration::copyFrom(source); + const SerialConfiguration* ssource = qobject_cast(source); + if (ssource) { + _baud = ssource->baud(); + _flowControl = ssource->flowControl(); + _parity = ssource->parity(); + _dataBits = ssource->dataBits(); + _stopBits = ssource->stopBits(); + _portName = ssource->portName(); + _portDisplayName = ssource->portDisplayName(); + _usbDirect = ssource->_usbDirect; + } else { + qWarning() << "Internal error"; } } -/*===========================================================================*/ - -SerialLink::SerialLink(SharedLinkConfigurationPtr &config, QObject *parent) - : LinkInterface(config, parent) - , _serialConfig(qobject_cast(config.get())) - , _worker(new SerialWorker(_serialConfig)) - , _workerThread(new QThread(this)) +void SerialConfiguration::setBaud(int baud) { - // qCDebug(SerialLinkLog) << Q_FUNC_INFO << this; - - _workerThread->setObjectName(QStringLiteral("Serial_%1").arg(_serialConfig->name())); - - _worker->moveToThread(_workerThread); - - (void) connect(_workerThread, &QThread::started, _worker, &SerialWorker::setupPort); - (void) connect(_workerThread, &QThread::finished, _worker, &QObject::deleteLater); - - (void) connect(_worker, &SerialWorker::connected, this, &SerialLink::_onConnected, Qt::QueuedConnection); - (void) connect(_worker, &SerialWorker::disconnected, this, &SerialLink::_onDisconnected, Qt::QueuedConnection); - (void) connect(_worker, &SerialWorker::dataReceived, this, &SerialLink::_onDataReceived, Qt::QueuedConnection); - (void) connect(_worker, &SerialWorker::dataSent, this, &SerialLink::_onDataSent, Qt::QueuedConnection); - (void) connect(_worker, &SerialWorker::errorOccurred, this, &SerialLink::_onErrorOccurred, Qt::QueuedConnection); - - _workerThread->start(); + _baud = baud; } -SerialLink::~SerialLink() +void SerialConfiguration::setDataBits(int databits) { - SerialLink::disconnect(); - - _workerThread->quit(); - if (!_workerThread->wait()) { - qCWarning(SerialLinkLog) << "Failed to wait for Serial Thread to close"; - } - - // qCDebug(SerialLinkLog) << Q_FUNC_INFO << this; + _dataBits = databits; } -bool SerialLink::isConnected() const +void SerialConfiguration::setFlowControl(int flowControl) { - return _worker->isConnected(); + _flowControl = flowControl; } -bool SerialLink::_connect() +void SerialConfiguration::setStopBits(int stopBits) { - return QMetaObject::invokeMethod(_worker, "connectToPort", Qt::QueuedConnection); + _stopBits = stopBits; } -void SerialLink::disconnect() +void SerialConfiguration::setParity(int parity) { - (void) QMetaObject::invokeMethod(_worker, "disconnectFromPort", Qt::QueuedConnection); + _parity = parity; } -void SerialLink::_onConnected() +void SerialConfiguration::setPortName(const QString& portName) { - emit connected(); + // No effect on a running connection + QString pname = portName.trimmed(); + if (!pname.isEmpty() && pname != _portName) { + _portName = pname; + _portDisplayName = cleanPortDisplayname(pname); + } } -void SerialLink::_onDisconnected() +QString SerialConfiguration::cleanPortDisplayname(const QString name) { - emit disconnected(); + QString pname = name.trimmed(); +#ifdef Q_OS_WIN + pname.replace("\\\\.\\", ""); +#else + pname.replace("/dev/cu.", ""); + pname.replace("/dev/", ""); +#endif + return pname; } -void SerialLink::_onErrorOccurred(const QString &errorString) +void SerialConfiguration::saveSettings(QSettings& settings, const QString& root) { - qCWarning(SerialLinkLog) << "Communication error:" << errorString; - emit communicationError(tr("Serial Link Error"), tr("Link %1: (Port: %2) %3").arg(_serialConfig->name(), _serialConfig->portName(), errorString)); + settings.beginGroup(root); + settings.setValue("baud", _baud); + settings.setValue("dataBits", _dataBits); + settings.setValue("flowControl", _flowControl); + settings.setValue("stopBits", _stopBits); + settings.setValue("parity", _parity); + settings.setValue("portName", _portName); + settings.setValue("portDisplayName",_portDisplayName); + settings.endGroup(); } -void SerialLink::_onDataReceived(const QByteArray &data) +void SerialConfiguration::loadSettings(QSettings& settings, const QString& root) { - emit bytesReceived(this, data); + settings.beginGroup(root); + if(settings.contains("baud")) _baud = settings.value("baud").toInt(); + if(settings.contains("dataBits")) _dataBits = settings.value("dataBits").toInt(); + if(settings.contains("flowControl")) _flowControl = settings.value("flowControl").toInt(); + if(settings.contains("stopBits")) _stopBits = settings.value("stopBits").toInt(); + if(settings.contains("parity")) _parity = settings.value("parity").toInt(); + if(settings.contains("portName")) _portName = settings.value("portName").toString(); + if(settings.contains("portDisplayName"))_portDisplayName= settings.value("portDisplayName").toString(); + settings.endGroup(); } -void SerialLink::_onDataSent(const QByteArray &data) +QStringList SerialConfiguration::supportedBaudRates() { - emit bytesSent(this, data); + QStringList supportBaudRateStrings; + for (int rate : QSerialPortInfo::standardBaudRates()) { + (void) supportBaudRateStrings.append(QString::number(rate)); + } + + return supportBaudRateStrings; } -void SerialLink::_writeBytes(const QByteArray &data) +void SerialConfiguration::setUsbDirect(bool usbDirect) { - (void) QMetaObject::invokeMethod(_worker, "writeData", Qt::QueuedConnection, Q_ARG(QByteArray, data)); + if (_usbDirect != usbDirect) { + _usbDirect = usbDirect; + emit usbDirectChanged(_usbDirect); + } } diff --git a/src/Comms/SerialLink.h b/src/Comms/SerialLink.h index 5fb05ef65cc..3f4e4ef066e 100644 --- a/src/Comms/SerialLink.h +++ b/src/Comms/SerialLink.h @@ -9,168 +9,136 @@ #pragma once -#include -#include +#include "LinkConfiguration.h" +#include "LinkInterface.h" +#include +#include +#include #ifdef Q_OS_ANDROID #include "qserialport.h" #else #include #endif - -#include "LinkConfiguration.h" -#include "LinkInterface.h" - -class QThread; -class QTimer; +#include Q_DECLARE_LOGGING_CATEGORY(SerialLinkLog) -/*===========================================================================*/ +// We use QSerialPort::SerialPortError in a signal so we must declare it as a meta type +Q_DECLARE_METATYPE(QSerialPort::SerialPortError) + +class LinkManager; +/// SerialLink configuration class SerialConfiguration : public LinkConfiguration { Q_OBJECT - Q_PROPERTY(qint32 baud READ baud WRITE setBaud NOTIFY baudChanged) - Q_PROPERTY(QSerialPort::DataBits dataBits READ dataBits WRITE setDataBits NOTIFY dataBitsChanged) - Q_PROPERTY(QSerialPort::FlowControl flowControl READ flowControl WRITE setFlowControl NOTIFY flowControlChanged) - Q_PROPERTY(QSerialPort::StopBits stopBits READ stopBits WRITE setStopBits NOTIFY stopBitsChanged) - Q_PROPERTY(QSerialPort::Parity parity READ parity WRITE setParity NOTIFY parityChanged) - Q_PROPERTY(QString portName READ portName WRITE setPortName NOTIFY portNameChanged) - Q_PROPERTY(QString portDisplayName READ portDisplayName NOTIFY portDisplayNameChanged) - Q_PROPERTY(bool usbDirect READ usbDirect WRITE setUsbDirect NOTIFY usbDirectChanged) public: - explicit SerialConfiguration(const QString &name, QObject *parent = nullptr); - explicit SerialConfiguration(const SerialConfiguration *copy, QObject *parent = nullptr); - virtual ~SerialConfiguration(); - LinkType type() const override { return LinkConfiguration::TypeSerial; } - void copyFrom(const LinkConfiguration *source) override; - void loadSettings(QSettings &settings, const QString &root) override; - void saveSettings(QSettings &settings, const QString &root) override; - QString settingsURL() override { return QStringLiteral("SerialSettings.qml"); } - QString settingsTitle() override { return tr("Serial Link Settings"); } - - qint32 baud() const { return _baud; } - void setBaud(qint32 baud) { if (baud != _baud) { _baud = baud; emit baudChanged(); } } - - QSerialPort::DataBits dataBits() const { return _dataBits; } - void setDataBits(QSerialPort::DataBits databits) { if (databits != _dataBits) { _dataBits = databits; emit dataBitsChanged(); } } - - QSerialPort::FlowControl flowControl() const { return _flowControl; } - void setFlowControl(QSerialPort::FlowControl flowControl) { if (flowControl != _flowControl) { _flowControl = flowControl; emit flowControlChanged(); } } - - QSerialPort::StopBits stopBits() const { return _stopBits; } - void setStopBits(QSerialPort::StopBits stopBits) { if (stopBits != _stopBits) { _stopBits = stopBits; emit stopBitsChanged(); } } - - QSerialPort::Parity parity() const { return _parity; } - void setParity(QSerialPort::Parity parity) { if (parity != _parity) { _parity = parity; emit parityChanged(); } } - - QString portName() const { return _portName; } - void setPortName(const QString &name); - - QString portDisplayName() const { return _portDisplayName; } - void setPortDisplayName(const QString &portDisplayName) { if (portDisplayName != _portDisplayName) { _portDisplayName = portDisplayName; emit portDisplayNameChanged(); } } - - bool usbDirect() const { return _usbDirect; } - void setUsbDirect(bool usbDirect) { if (usbDirect != _usbDirect) { _usbDirect = usbDirect; emit usbDirectChanged(); } } + SerialConfiguration(const QString& name); + SerialConfiguration(const SerialConfiguration* copy); + + Q_PROPERTY(int baud READ baud WRITE setBaud NOTIFY baudChanged) + Q_PROPERTY(int dataBits READ dataBits WRITE setDataBits NOTIFY dataBitsChanged) + Q_PROPERTY(int flowControl READ flowControl WRITE setFlowControl NOTIFY flowControlChanged) + Q_PROPERTY(int stopBits READ stopBits WRITE setStopBits NOTIFY stopBitsChanged) + Q_PROPERTY(int parity READ parity WRITE setParity NOTIFY parityChanged) + Q_PROPERTY(QString portName READ portName WRITE setPortName NOTIFY portNameChanged) + Q_PROPERTY(QString portDisplayName READ portDisplayName NOTIFY portDisplayNameChanged) + Q_PROPERTY(bool usbDirect READ usbDirect WRITE setUsbDirect NOTIFY usbDirectChanged) ///< true: direct usb connection to board + + int baud() const { return _baud; } + int dataBits() const { return _dataBits; } + int flowControl() const { return _flowControl; } ///< QSerialPort Enums + int stopBits() const { return _stopBits; } + int parity() const { return _parity; } ///< QSerialPort Enums + bool usbDirect() const { return _usbDirect; } + + const QString portName () const { return _portName; } + const QString portDisplayName () const { return _portDisplayName; } + + void setBaud (int baud); + void setDataBits (int databits); + void setFlowControl (int flowControl); ///< QSerialPort Enums + void setStopBits (int stopBits); + void setParity (int parity); ///< QSerialPort Enums + void setPortName (const QString& portName); + void setUsbDirect (bool usbDirect); static QStringList supportedBaudRates(); - static QString cleanPortDisplayName(const QString &name); + static QString cleanPortDisplayname(const QString name); + + /// From LinkConfiguration + LinkType type () const override { return LinkConfiguration::TypeSerial; } + void copyFrom (const LinkConfiguration* source) override; + void loadSettings (QSettings& settings, const QString& root) override; + void saveSettings (QSettings& settings, const QString& root) override; + void updateSettings (); + QString settingsURL () override { return "SerialSettings.qml"; } + QString settingsTitle () override { return tr("Serial Link Settings"); } signals: - void baudChanged(); - void dataBitsChanged(); - void flowControlChanged(); - void stopBitsChanged(); - void parityChanged(); - void portNameChanged(); - void portDisplayNameChanged(); - void usbDirectChanged(); + void baudChanged (); + void dataBitsChanged (); + void flowControlChanged (); + void stopBitsChanged (); + void parityChanged (); + void portNameChanged (); + void portDisplayNameChanged (); + void usbDirectChanged (bool usbDirect); private: - qint32 _baud = QSerialPort::Baud57600; - QSerialPort::DataBits _dataBits = QSerialPort::Data8; - QSerialPort::FlowControl _flowControl = QSerialPort::NoFlowControl; - QSerialPort::StopBits _stopBits = QSerialPort::OneStop; - QSerialPort::Parity _parity = QSerialPort::NoParity; + int _baud; + int _dataBits; + int _flowControl; + int _stopBits; + int _parity; QString _portName; QString _portDisplayName; - bool _usbDirect = false; -}; - -/*===========================================================================*/ - -class SerialWorker : public QObject -{ - Q_OBJECT - -public: - explicit SerialWorker(const SerialConfiguration *config, QObject *parent = nullptr); - ~SerialWorker(); - - bool isConnected() const; - const QSerialPort *port() const { return _port; } - -signals: - void connected(); - void disconnected(); - void dataReceived(const QByteArray &data); - void dataSent(const QByteArray &data); - void errorOccurred(const QString &errorString); - -public slots: - void setupPort(); - void connectToPort(); - void disconnectFromPort(); - void writeData(const QByteArray &data); - -private slots: - void _onPortConnected(); - void _onPortDisconnected(); - void _onPortReadyRead(); - void _onPortBytesWritten(qint64 bytes) const; - void _onPortErrorOccurred(QSerialPort::SerialPortError portError); - void _checkPortAvailability(); - -private: - const SerialConfiguration *_config = nullptr; - QSerialPort *_port = nullptr; - QTimer *_timer = nullptr; - bool _errorEmitted = false; + bool _usbDirect; }; -/*===========================================================================*/ - class SerialLink : public LinkInterface { Q_OBJECT public: - explicit SerialLink(SharedLinkConfigurationPtr &config, QObject *parent = nullptr); + SerialLink(SharedLinkConfigurationPtr& config); virtual ~SerialLink(); - bool isConnected() const override; - bool isSecureConnection() override { return _serialConfig->usbDirect(); } + // LinkInterface overrides + bool isConnected (void) const override; + void disconnect (void) override; + bool isSecureConnection (void) override; - const QSerialPort *port() const { return _worker->port(); } + /// Don't even think of calling this method! + QSerialPort* _hackAccessToPort(void) { return _port; } + +private slots: + void _writeBytes(const QByteArray &data) override; public slots: - void disconnect() override; + void linkError(QSerialPort::SerialPortError error); private slots: - void _onConnected(); - void _onDisconnected(); - void _onDataReceived(const QByteArray &data); - void _onDataSent(const QByteArray &data); - void _onErrorOccurred(const QString &errorString); + void _readBytes (void); private: - bool _connect() override; - void _writeBytes(const QByteArray &data) override; - - const SerialConfiguration *_serialConfig = nullptr; - SerialWorker *_worker = nullptr; - QThread *_workerThread = nullptr; + // LinkInterface overrides + bool _connect(void) override; + + void _emitLinkError (const QString& errorMsg); + bool _hardwareConnect (QSerialPort::SerialPortError& error, QString& errorString); + bool _isBootloader (void); + + QSerialPort* _port = nullptr; + quint64 _bytesRead = 0; + int _timeout; + QMutex _dataMutex; ///< Mutex for reading data from _port + QMutex _writeMutex; ///< Mutex for accessing the _transmitBuffer. + volatile bool _stopp = false; + QMutex _stoppMutex; ///< Mutex for accessing _stopp + QByteArray _transmitBuffer; ///< An internal buffer for receiving data from member functions and actually transmitting them via the serial port. + const SerialConfiguration* _serialConfig = nullptr; };