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;
20 qCDebug(SerialLinkLog) <<
this;
26 qCDebug(SerialLinkLog) <<
this;
33 qCDebug(SerialLinkLog) <<
this;
75 settings.beginGroup(root);
77 setBaud(settings.value(
"baud", _baud).toInt());
85 setPortName(settings.value(
"portName", _portName).toString());
86 setdtrForceLow(settings.value(
"dtrForceLow", _dtrForceLow).toBool());
93 settings.beginGroup(root);
95 settings.setValue(
"baud", _baud);
96 settings.setValue(
"dataBits", _dataBits);
97 settings.setValue(
"flowControl", _flowControl);
98 settings.setValue(
"stopBits", _stopBits);
99 settings.setValue(
"parity", _parity);
100 settings.setValue(
"portName", _portName);
101 settings.setValue(
"portDisplayName", _portDisplayName);
102 settings.setValue(
"dtrForceLow", _dtrForceLow);
109 static const QSet<qint32> kDefaultSupportedBaudRates = {
156 QSet<qint32> mergedBaudRateSet(kDefaultSupportedBaudRates.constBegin(), kDefaultSupportedBaudRates.constEnd());
157 (void) mergedBaudRateSet.unite(QSet<qint32>(activeSupportedBaudRates.constBegin(), activeSupportedBaudRates.constEnd()));
159 QList<qint32> mergedBaudRateList = mergedBaudRateSet.values();
160 std::sort(mergedBaudRateList.begin(), mergedBaudRateList.end());
162 QStringList supportBaudRateStrings{};
163 supportBaudRateStrings.reserve(mergedBaudRateList.size());
164 for (
const qint32 rate : std::as_const(mergedBaudRateList)) {
165 supportBaudRateStrings.append(QString::number(rate));
168 return supportBaudRateStrings;
175 if (portInfo.systemLocation() ==
name) {
180 if (!portInfo.description().isEmpty()) {
181 displayName = portInfo.description();
182 }
else if (!portInfo.manufacturer().isEmpty()) {
183 displayName = portInfo.manufacturer();
185 if (!displayName.isEmpty()) {
186 return QStringLiteral(
"%1 (%2)").arg(displayName, portInfo.portName());
189 return portInfo.portName();
202 qCDebug(SerialLinkLog) <<
this;
204 (void) qRegisterMetaType<QSerialPort::SerialPortError>(
"QSerialPort::SerialPortError");
211 qCDebug(SerialLinkLog) <<
this;
216 return (_port && _port->isOpen());
226 _timer =
new QTimer(
this);
229 (void) connect(_port, &QSerialPort::aboutToClose,
this, &SerialWorker::_onPortDisconnected);
230 (void) connect(_port, &QSerialPort::readyRead,
this, &SerialWorker::_onPortReadyRead);
237 (void) connect(_timer, &QTimer::timeout,
this, &SerialWorker::_checkPortAvailability);
243 qCWarning(SerialLinkLog) <<
"Already connected to" << _port->
portName();
251 qCWarning(SerialLinkLog) <<
"Not connecting to bootloader" << _port->
portName();
253 _onPortDisconnected();
257 _errorEmitted =
false;
259 qCDebug(SerialLinkLog) <<
"Attempting to open port" << _port->
portName();
260 if (!_port->
open(QIODevice::ReadWrite)) {
261 qCWarning(SerialLinkLog) <<
"Opening port" << _port->
portName() <<
"failed:" << _port->errorString();
265 emit
errorOccurred(tr(
"Could not open port: %1").arg(_port->errorString()));
266 _errorEmitted =
true;
269 _onPortDisconnected();
280 qCDebug(SerialLinkLog) <<
"Already disconnected from port:" << _port->
portName();
284 qCDebug(SerialLinkLog) <<
"Attempting to close port:" << _port->
portName();
291 if (data.isEmpty()) {
301 if (!_port->isWritable()) {
306 qint64 totalBytesWritten = 0;
307 while (totalBytesWritten < data.size()) {
308 const qint64 bytesWritten = _port->write(data.constData() + totalBytesWritten, data.size() - totalBytesWritten);
309 if (bytesWritten == -1) {
310 emit
errorOccurred(tr(
"Could Not Send Data - Write Failed: %1").arg(_port->errorString()));
312 }
else if (bytesWritten == 0) {
313 emit
errorOccurred(tr(
"Could Not Send Data - Write Returned 0 Bytes"));
316 totalBytesWritten += bytesWritten;
319 const QByteArray sent = data.first(totalBytesWritten);
323void SerialWorker::_onPortConnected()
325 qCDebug(SerialLinkLog) <<
"Port connected:" << _port->
portName();
335 _timer->start(CONNECT_TIMEOUT_MS);
338 _errorEmitted =
false;
342void SerialWorker::_onPortDisconnected()
344 qCDebug(SerialLinkLog) <<
"Port disconnected:" << _port->
portName();
350 _errorEmitted =
false;
354void SerialWorker::_onPortReadyRead()
356 const QByteArray data = _port->readAll();
357 if (!data.isEmpty()) {
363void SerialWorker::_onPortBytesWritten(qint64 bytes)
const
365 qCDebug(SerialLinkLog) << _port->
portName() <<
"Wrote" << bytes <<
"bytes";
372 qCDebug(SerialLinkLog) <<
"About to open port" << _port->
portName();
376 qCDebug(SerialLinkLog) <<
"Resource error (likely USB disconnect):" << _port->errorString();
389 qCWarning(SerialLinkLog) <<
"Port error:" << portError <<
errorString;
391 if (!_errorEmitted) {
393 _errorEmitted =
true;
397void SerialWorker::_checkPortAvailability()
403 bool portExists =
false;
404 const QString configuredPort = _serialConfig->
portName();
409 if ((info.systemLocation() == configuredPort) || (info.portName() == configuredPort)) {
426 , _workerThread(new QThread(this))
428 qCDebug(SerialLinkLog) <<
this;
430 _workerThread->setObjectName(QStringLiteral(
"Serial_%1").arg(_serialConfig->
name()));
432 (void) _worker->moveToThread(_workerThread);
435 (void) connect(_workerThread, &QThread::finished, _worker, &QObject::deleteLater);
443 _workerThread->start();
449 (void) QMetaObject::invokeMethod(_worker,
"disconnectFromPort", Qt::BlockingQueuedConnection);
453 _workerThread->quit();
454 if (!_workerThread->wait(DISCONNECT_TIMEOUT_MS)) {
455 qCWarning(SerialLinkLog) <<
"Failed to wait for Serial Thread to close";
458 qCDebug(SerialLinkLog) <<
this;
466bool SerialLink::_connect()
468 return QMetaObject::invokeMethod(_worker,
"connectToPort", Qt::QueuedConnection);
474 (void) QMetaObject::invokeMethod(_worker,
"disconnectFromPort", Qt::QueuedConnection);
478void SerialLink::_onConnected()
480 _disconnectedEmitted =
false;
484void SerialLink::_onDisconnected()
486 if (!_disconnectedEmitted.exchange(
true)) {
491void SerialLink::_onErrorOccurred(
const QString &
errorString)
493 qCWarning(SerialLinkLog) <<
"Communication error:" <<
errorString;
497void SerialLink::_onDataReceived(
const QByteArray &data)
502void SerialLink::_onDataSent(
const QByteArray &data)
507void SerialLink::_writeBytes(
const QByteArray &data)
509 (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.
virtual void copyFrom(const LinkConfiguration *source)
bool isAutoConnect() const
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)
QGC's version of Qt QSerialPortInfo. It provides additional information about board types that QGC ca...
bool isBootloader() const
Provides information about existing serial ports.
static QList< QSerialPortInfo > availablePorts()
Returns a list of available serial ports on the system.
static QList< qint32 > standardBaudRates()
Returns a list of available standard baud rates supported by the target platform.
Provides functions to access serial ports.
bool setDataTerminalReady(bool set)
bool open(OpenMode mode) override
\reimp
void close() override
\reimp
bool setStopBits(StopBits stopBits)
void setPortName(const QString &name)
Sets the name of the serial port.
void errorOccurred(QSerialPort::SerialPortError error)
DataBits
This enum describes the number of data bits used.
Parity
This enum describes the parity scheme used.
SerialPortError error() const
the error status of the serial port
SerialPortError
This enum describes the errors that may be contained by the QSerialPort::error property.
QString portName() const
Returns the name set by setPort() or passed to the QSerialPort constructor.
bool setBaudRate(qint32 baudRate, Directions directions=AllDirections)
StopBits
This enum describes the number of stop bits used.
bool setDataBits(DataBits dataBits)
FlowControl
This enum describes the flow control used.
bool setParity(Parity parity)
bool setFlowControl(FlowControl flowControl)
void setParity(QSerialPort::Parity parity)
QSerialPort::FlowControl flowControl() const
void loadSettings(QSettings &settings, const QString &root) override
QSerialPort::StopBits stopBits() const
void saveSettings(QSettings &settings, const QString &root) const override
void setStopBits(QSerialPort::StopBits stopBits)
void setPortDisplayName(const QString &portDisplayName)
void setBaud(qint32 baud)
SerialConfiguration(const QString &name, QObject *parent=nullptr)
void setDataBits(QSerialPort::DataBits databits)
void setdtrForceLow(bool dtrForceLow)
static QStringList supportedBaudRates()
QString portDisplayName() const
QSerialPort::Parity parity() const
void setUsbDirect(bool usbDirect)
QSerialPort::DataBits dataBits() const
void copyFrom(const LinkConfiguration *source) override
static QString cleanPortDisplayName(const QString &name)
void setFlowControl(QSerialPort::FlowControl flowControl)
void setPortName(const QString &name)
virtual ~SerialConfiguration()
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)