QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
SerialGPSTransport.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <utility>
5
6#include <QtCore/QCoreApplication>
7#include <QtCore/QDeadlineTimer>
8#include <QtCore/QFileInfo>
9#ifdef Q_OS_ANDROID
10#include "qserialport.h"
11#else
12#include <QtSerialPort/QSerialPort>
13#endif
14
15#include "QGCLoggingCategory.h"
16
17QGC_LOGGING_CATEGORY(SerialGPSTransportLog, "GPS.Transport.SerialGPSTransport")
18
19namespace {
20
22QString openFailure(const QSerialPort& serial, const QString& device)
23{
24 switch (serial.error()) {
26 //: %1 is a serial device, such as /dev/ttyACM0 or COM3
27 return QCoreApplication::translate("SerialGPSTransport", "%1 was not found").arg(device);
29#ifdef Q_OS_UNIX
30 // A device held by another program reports a permission error too.
31 if (const QFileInfo file(device); file.exists() && !(file.isReadable() && file.isWritable())) {
32 //: %1 is a serial device, such as /dev/ttyACM0
33 return QCoreApplication::translate("SerialGPSTransport", "permission denied for %1").arg(device);
34 }
35#endif
36 //: %1 is a serial device, such as /dev/ttyACM0 or COM3
37 return QCoreApplication::translate("SerialGPSTransport", "%1 is in use by another program").arg(device);
38 default:
39 return serial.errorString();
40 }
41}
42
43} // namespace
44
46 std::chrono::milliseconds openTimeout)
47 : GPSDeviceTransport(std::move(cancelToken))
48 , _device(std::move(device))
49 , _openTimeout(openTimeout)
50{}
51
53
55{
56 if (isCancelled()) {
58 }
59 _serial = std::make_unique<QSerialPort>();
60 _inputOverflow = false;
61 _serial->setReadBufferSize(READ_BUFFER_BYTES);
62 QObject::connect(
63 _serial.get(), &QSerialPort::readyRead, _serial.get(),
64 [this]() {
65 // With no serial flow control, exhausting our budget makes stream continuity unverifiable.
66 // Retire the attempt rather than decode bytes across a possible overrun.
67 _inputOverflow = _inputOverflow || _serial->bytesAvailable() >= READ_BUFFER_BYTES;
68 },
69 Qt::DirectConnection);
70 _acceptedTotal = 0;
71 _writtenTotal = 0;
72 QObject::connect(
73 _serial.get(), &QSerialPort::bytesWritten, _serial.get(), [this](qint64 count) { _writtenTotal += count; },
74 Qt::DirectConnection);
75 _serial->setPortName(_device);
76 const QDeadlineTimer openDeadline(_openTimeout);
77 while (!_serial->open(QIODevice::ReadWrite)) {
78 if (isCancelled()) {
80 }
81 // Device can take 10-20s to become accessible after startup.
82 if (_serial->error() != QSerialPort::PermissionError || openDeadline.hasExpired()) {
83 qCWarning(SerialGPSTransportLog)
84 << "GPS: Failed to open Serial Device" << _device << _serial->errorString();
85 return {openDeadline.hasExpired() ? GPSOpenStatus::TimedOut : GPSOpenStatus::Error,
86 openFailure(*_serial, _device)};
87 }
88 qCDebug(SerialGPSTransportLog) << "Cannot open device... retrying";
89 const QDeadlineTimer retryDeadline(
90 (std::min) (std::chrono::milliseconds(openDeadline.remainingTime()), OPEN_RETRY));
91 (void) waitUntil([]() { return false; }, retryDeadline);
92 if (isCancelled()) {
94 }
95 }
96 _serial->clearError();
97
98 const bool configured = _serial->setBaudRate(QSerialPort::Baud9600) && _serial->setDataBits(QSerialPort::Data8) &&
99 _serial->setParity(QSerialPort::NoParity) && _serial->setStopBits(QSerialPort::OneStop) &&
100 _serial->setFlowControl(QSerialPort::NoFlowControl);
101 if (!configured || isCancelled()) {
103 _serial->errorString()};
104 _serial->close();
105 return result;
106 }
107 return {GPSOpenStatus::Opened};
108}
109
111{
112 return _inputBudgetExhausted() || !_serial || !_serial->isOpen() ||
113 ((_serial->error() != QSerialPort::NoError) && (_serial->error() != QSerialPort::TimeoutError));
114}
115
117{
118 return _serial.get();
119}
120
122{
123 return fatalError();
124}
125
126bool SerialGPSTransport::waitReadable(QDeadlineTimer deadline)
127{
128 // Unlike a socket, a serial port delivers no input once a stop or an overrun was seen.
129 return GPSDeviceTransport::waitReadable(deadline) && !isCancelled() && !fatalError();
130}
131
133{
134 return {_inputBudgetExhausted() ? GPSReadStatus::Overflow : GPSReadStatus::Error, 0, _errorDetail()};
135}
136
137bool SerialGPSTransport::_inputBudgetExhausted() const
138{
139 return _inputOverflow || (_serial && _serial->bytesAvailable() >= READ_BUFFER_BYTES);
140}
141
142QString SerialGPSTransport::_errorDetail() const
143{
144 return _inputBudgetExhausted()
145 ? QStringLiteral("Serial GPS input buffer exhausted; reconnect required to restore stream continuity")
146 : _serial ? _serial->errorString()
147 : QStringLiteral("Serial GPS connection is closed");
148}
149
150void SerialGPSTransport::waitWritten(QDeadlineTimer& deadline)
151{
152 _serial->waitForBytesWritten(static_cast<int>(
153 (deadline.isForever() ? CANCELLATION_POLL
154 : (std::min) (std::chrono::milliseconds(deadline.remainingTime()), CANCELLATION_POLL))
155 .count()));
156}
157
158qint64 SerialGPSTransport::confirmedWritten(int accepted, qint64 drained)
159{
160 Q_UNUSED(drained)
161 const qint64 previousAccepted = _acceptedTotal;
162 _acceptedTotal += accepted;
163 // Late bytesWritten signals belong to earlier connection-wide submissions.
164 const qint64 confirmed =
165 !fatalError() ? (std::max) (_writtenTotal, _acceptedTotal - _serial->bytesToWrite()) : _writtenTotal;
166 return confirmed - previousAccepted;
167}
168
169bool SerialGPSTransport::setBaudrate(unsigned baudrate)
170{
171 if (isCancelled() || fatalError()) {
172 return false;
173 }
174 if (_serial->setBaudRate(static_cast<qint32>(baudrate))) {
175 return true;
176 }
177 // A refused rate leaves the port open at its previous rate; only a lost device ends the link.
178 if (_serial->isOpen() && _serial->error() != QSerialPort::ResourceError) {
179 qCDebug(SerialGPSTransportLog) << "Link refused" << baudrate << "baud:" << _serial->errorString();
180 _serial->clearError();
181 }
182 return false;
183}
QString errorString
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool waitUntil(const std::function< bool()> &done, QDeadlineTimer deadline)
virtual bool waitReadable(QDeadlineTimer deadline)
Waits until input is buffered. Input reported buffered is delivered even after the link fails.
static constexpr qint64 READ_BUFFER_BYTES
bool isCancelled() const
Provides functions to access serial ports.
Definition qserialport.h:17
SerialPortError error() const
the error status of the serial port
bool setBaudrate(unsigned baudrate) override
void waitWritten(QDeadlineTimer &deadline) override
Waits for queued bytes to drain. May expire deadline to end the write.
bool fatalError() const override
True once the port hits an error the receive loop should stop retrying past.
GPSOpenResult open() override
SerialGPSTransport(QString device, GPSCancelToken cancelToken, std::chrono::milliseconds openTimeout=OPEN_TIMEOUT)
GPSReadResult readFailure() const override
Describes a failed link; by default, a closed connection with the device's error.
qint64 confirmedWritten(int accepted, qint64 drained) override
~SerialGPSTransport() override
bool waitReadable(QDeadlineTimer deadline) override
Waits until input is buffered. Input reported buffered is delivered even after the link fails.
bool readUnavailable() const override
Whether read() fails without waiting; by default, when there is no device.
QIODevice * device() const override
The open device, or null before open().