4#include <QtCore/QSettings>
5#include <QtCore/QThread>
6#include <QtCore/QTimer>
11 constexpr int CONNECT_TIMEOUT_MS = 1000;
12 constexpr int DISCONNECT_TIMEOUT_MS = 3000;
17SerialConfiguration::SerialConfiguration(
const QString &name, QObject *parent)
20 qCDebug(SerialLinkLog) <<
this;
26 qCDebug(SerialLinkLog) <<
this;
28 SerialConfiguration::copyFrom(source);
31SerialConfiguration::~SerialConfiguration()
33 qCDebug(SerialLinkLog) <<
this;
36void SerialConfiguration::setPortName(
const QString &name)
38 const QString portName = name.trimmed();
39 if (portName.isEmpty()) {
43 if (portName != _portName) {
48 const QString portDisplayName = cleanPortDisplayName(portName);
49 setPortDisplayName(portDisplayName);
54 LinkConfiguration::copyFrom(source);
58 setBaud(serialSource->baud());
59 setDataBits(serialSource->dataBits());
60 setFlowControl(serialSource->flowControl());
61 setStopBits(serialSource->stopBits());
62 setParity(serialSource->parity());
63 setPortName(serialSource->portName());
64 setPortDisplayName(serialSource->portDisplayName());
65 setUsbDirect(serialSource->usbDirect());
66 setdtrForceLow(serialSource->dtrForceLow());
69void SerialConfiguration::loadSettings(QSettings &settings,
const QString &root)
71 settings.beginGroup(root);
73 setBaud(settings.value(
"baud", _baud).toInt());
78 setPortName(settings.value(
"portName", _portName).toString());
79 setPortDisplayName(settings.value(
"portDisplayName", _portDisplayName).toString());
80 setdtrForceLow(settings.value(
"dtrForceLow", _dtrForceLow).toBool());
85void SerialConfiguration::saveSettings(QSettings &settings,
const QString &root)
const
87 settings.beginGroup(root);
89 settings.setValue(
"baud", _baud);
90 settings.setValue(
"dataBits", _dataBits);
91 settings.setValue(
"flowControl", _flowControl);
92 settings.setValue(
"stopBits", _stopBits);
93 settings.setValue(
"parity", _parity);
94 settings.setValue(
"portName", _portName);
95 settings.setValue(
"portDisplayName", _portDisplayName);
96 settings.setValue(
"dtrForceLow", _dtrForceLow);
101QStringList SerialConfiguration::supportedBaudRates()
103 static const QSet<qint32> kDefaultSupportedBaudRates = {
150 QSet<qint32> mergedBaudRateSet(kDefaultSupportedBaudRates.constBegin(), kDefaultSupportedBaudRates.constEnd());
151 (void) mergedBaudRateSet.unite(QSet<qint32>(activeSupportedBaudRates.constBegin(), activeSupportedBaudRates.constEnd()));
153 QList<qint32> mergedBaudRateList = mergedBaudRateSet.values();
154 std::sort(mergedBaudRateList.begin(), mergedBaudRateList.end());
156 QStringList supportBaudRateStrings{};
157 supportBaudRateStrings.reserve(mergedBaudRateList.size());
158 for (
const qint32 rate : std::as_const(mergedBaudRateList)) {
159 supportBaudRateStrings.append(QString::number(rate));
162 return supportBaudRateStrings;
165QString SerialConfiguration::cleanPortDisplayName(
const QString &name)
169 if (portInfo.systemLocation() == name) {
170 return portInfo.portName();
181 , _serialConfig(config)
183 qCDebug(SerialLinkLog) <<
this;
185 (void) qRegisterMetaType<QSerialPort::SerialPortError>(
"QSerialPort::SerialPortError");
192 qCDebug(SerialLinkLog) <<
this;
197 return (_port && _port->isOpen());
207 _timer =
new QTimer(
this);
210 (void) connect(_port, &QSerialPort::aboutToClose,
this, &SerialWorker::_onPortDisconnected);
211 (void) connect(_port, &QSerialPort::readyRead,
this, &SerialWorker::_onPortReadyRead);
218 (void) connect(_timer, &QTimer::timeout,
this, &SerialWorker::_checkPortAvailability);
224 qCWarning(SerialLinkLog) <<
"Already connected to" << _port->
portName();
232 qCWarning(SerialLinkLog) <<
"Not connecting to bootloader" << _port->
portName();
234 _onPortDisconnected();
238 _errorEmitted =
false;
240 qCDebug(SerialLinkLog) <<
"Attempting to open port" << _port->
portName();
241 if (!_port->
open(QIODevice::ReadWrite)) {
242 qCWarning(SerialLinkLog) <<
"Opening port" << _port->
portName() <<
"failed:" << _port->errorString();
246 emit
errorOccurred(tr(
"Could not open port: %1").arg(_port->errorString()));
247 _errorEmitted =
true;
250 _onPortDisconnected();
261 qCDebug(SerialLinkLog) <<
"Already disconnected from port:" << _port->
portName();
265 qCDebug(SerialLinkLog) <<
"Attempting to close port:" << _port->
portName();
272 if (data.isEmpty()) {
282 if (!_port->isWritable()) {
287 qint64 totalBytesWritten = 0;
288 while (totalBytesWritten < data.size()) {
289 const qint64 bytesWritten = _port->write(data.constData() + totalBytesWritten, data.size() - totalBytesWritten);
290 if (bytesWritten == -1) {
291 emit
errorOccurred(tr(
"Could Not Send Data - Write Failed: %1").arg(_port->errorString()));
293 }
else if (bytesWritten == 0) {
294 emit
errorOccurred(tr(
"Could Not Send Data - Write Returned 0 Bytes"));
297 totalBytesWritten += bytesWritten;
300 const QByteArray sent = data.first(totalBytesWritten);
304void SerialWorker::_onPortConnected()
306 qCDebug(SerialLinkLog) <<
"Port connected:" << _port->
portName();
316 _timer->start(CONNECT_TIMEOUT_MS);
319 _errorEmitted =
false;
323void SerialWorker::_onPortDisconnected()
325 qCDebug(SerialLinkLog) <<
"Port disconnected:" << _port->
portName();
331 _errorEmitted =
false;
335void SerialWorker::_onPortReadyRead()
337 const QByteArray data = _port->readAll();
338 if (!data.isEmpty()) {
344void SerialWorker::_onPortBytesWritten(qint64 bytes)
const
346 qCDebug(SerialLinkLog) << _port->
portName() <<
"Wrote" << bytes <<
"bytes";
353 qCDebug(SerialLinkLog) <<
"About to open port" << _port->
portName();
357 qCDebug(SerialLinkLog) <<
"Resource error (likely USB disconnect):" << _port->errorString();
361 if (_serialConfig->isAutoConnect()) {
370 qCWarning(SerialLinkLog) <<
"Port error:" << portError <<
errorString;
372 if (!_errorEmitted) {
374 _errorEmitted =
true;
378void SerialWorker::_checkPortAvailability()
384 bool portExists =
false;
387 if (info.portName() == _serialConfig->portDisplayName()) {
404 , _workerThread(new QThread(this))
406 qCDebug(SerialLinkLog) <<
this;
408 _workerThread->setObjectName(QStringLiteral(
"Serial_%1").arg(_serialConfig->name()));
410 (void) _worker->moveToThread(_workerThread);
413 (void) connect(_workerThread, &QThread::finished, _worker, &QObject::deleteLater);
421 _workerThread->start();
427 (void) QMetaObject::invokeMethod(_worker,
"disconnectFromPort", Qt::BlockingQueuedConnection);
431 _workerThread->quit();
432 if (!_workerThread->wait(DISCONNECT_TIMEOUT_MS)) {
433 qCWarning(SerialLinkLog) <<
"Failed to wait for Serial Thread to close";
436 qCDebug(SerialLinkLog) <<
this;
444bool SerialLink::_connect()
446 return QMetaObject::invokeMethod(_worker,
"connectToPort", Qt::QueuedConnection);
452 (void) QMetaObject::invokeMethod(_worker,
"disconnectFromPort", Qt::QueuedConnection);
456void SerialLink::_onConnected()
458 _disconnectedEmitted =
false;
462void SerialLink::_onDisconnected()
464 if (!_disconnectedEmitted.exchange(
true)) {
469void SerialLink::_onErrorOccurred(
const QString &
errorString)
471 qCWarning(SerialLinkLog) <<
"Communication error:" <<
errorString;
472 emit
communicationError(tr(
"Serial Link Error"), tr(
"Link %1: (Port: %2) %3").arg(_serialConfig->name(), _serialConfig->portName(),
errorString));
475void SerialLink::_onDataReceived(
const QByteArray &data)
480void SerialLink::_onDataSent(
const QByteArray &data)
485void SerialLink::_writeBytes(
const QByteArray &data)
487 (void) QMetaObject::invokeMethod(_worker,
"writeData", Qt::QueuedConnection, Q_ARG(QByteArray, data));
std::shared_ptr< LinkConfiguration > SharedLinkConfigurationPtr
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Interface holding link specific settings.
The link interface defines the interface for all links used to communicate with the ground station ap...
void bytesReceived(LinkInterface *link, const QByteArray &data)
void communicationError(const QString &title, const QString &error)
void bytesSent(LinkInterface *link, const QByteArray &data)
bool isBootloader() const
Provides information about existing serial ports.
static QList< QSerialPortInfo > availablePorts()
static QList< qint32 > standardBaudRates()
Provides functions to access serial ports.
bool setDataTerminalReady(bool set)
bool open(OpenMode mode) override
bool setStopBits(StopBits stopBits)
void setPortName(const QString &name)
void errorOccurred(QSerialPort::SerialPortError error)
SerialPortError error() const
the error status of the serial port
bool setBaudRate(qint32 baudRate, Directions directions=AllDirections)
bool setDataBits(DataBits dataBits)
bool setParity(Parity parity)
bool setFlowControl(FlowControl flowControl)
SerialLink(SharedLinkConfigurationPtr &config, QObject *parent=nullptr)
void disconnect() override
bool isConnected() const override
void dataReceived(const QByteArray &data)
void disconnectFromPort()
SerialWorker(const SerialConfiguration *config, QObject *parent=nullptr)
void dataSent(const QByteArray &data)
void writeData(const QByteArray &data)
void errorOccurred(const QString &errorString)