QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QGCSerialPortInfo.cc
Go to the documentation of this file.
1#include "QGCSerialPortInfo.h"
2
3#include "JsonParsing.h"
5
6#include <QtCore/QFile>
7#include <QtCore/QJsonArray>
8#include <QtCore/QJsonDocument>
9#include <QtCore/QJsonObject>
10#include <QtCore/QStringList>
11
12QGC_LOGGING_CATEGORY(QGCSerialPortInfoLog, "Comms.QGCSerialPortInfo")
13
14bool QGCSerialPortInfo::_jsonLoaded = false;
15bool QGCSerialPortInfo::_jsonDataValid = false;
16QList<QGCSerialPortInfo::BoardInfo_t> QGCSerialPortInfo::_boardInfoList;
17QList<QGCSerialPortInfo::BoardRegExpFallback_t> QGCSerialPortInfo::_boardDescriptionFallbackList;
18QList<QGCSerialPortInfo::BoardRegExpFallback_t> QGCSerialPortInfo::_boardManufacturerFallbackList;
19
22{
23 qCDebug(QGCSerialPortInfoLog) << this;
24}
25
27 : QSerialPortInfo(port)
28{
29 qCDebug(QGCSerialPortInfoLog) << this;
30}
31
33{
34 qCDebug(QGCSerialPortInfoLog) << this;
35}
36
37bool QGCSerialPortInfo::_loadJsonData()
38{
39 if (_jsonLoaded) {
40 return _jsonDataValid;
41 }
42
43 _jsonLoaded = true;
44
45 QString errorString;
46 int version;
47 const QJsonObject json = JsonParsing::openInternalQGCJsonFile(QStringLiteral(":/json/USBBoardInfo.json"), QString(_jsonFileTypeValue), 1, 1, version, errorString);
48 if (!errorString.isEmpty()) {
49 qCWarning(QGCSerialPortInfoLog) << "Internal Error:" << errorString;
50 return false;
51 }
52
53 static const QList<JsonParsing::KeyValidateInfo> rootKeyInfoList = {
54 { JsonParsing::jsonFileTypeKey, QJsonValue::String, true }, // Standard header, validated by openInternalQGCJsonFile
55 { JsonParsing::jsonVersionKey, QJsonValue::Double, true }, // Standard header, validated by openInternalQGCJsonFile
56 { "comment", QJsonValue::String, false }, // Developer notes only, never parsed
57 { _jsonBoardInfoKey, QJsonValue::Array, true },
58 { _jsonBoardDescriptionFallbackKey, QJsonValue::Array, true },
59 { _jsonBoardManufacturerFallbackKey, QJsonValue::Array, true },
60 };
61 if (!JsonParsing::validateKeysStrict(json, rootKeyInfoList, errorString)) {
62 qCWarning(QGCSerialPortInfoLog) << errorString;
63 return false;
64 }
65
66 static const QList<JsonParsing::KeyValidateInfo> boardKeyInfoList = {
67 { "comment", QJsonValue::String, false }, // Developer notes only, never parsed
68 { _jsonVendorIDKey, QJsonValue::Double, true },
69 { _jsonProductIDKey, QJsonValue::Double, true },
70 { _jsonBoardClassKey, QJsonValue::String, true },
71 { _jsonNameKey, QJsonValue::String, true },
72 };
73 const QJsonArray rgBoardInfo = json[_jsonBoardInfoKey].toArray();
74 for (const QJsonValue &jsonValue : rgBoardInfo) {
75 if (!jsonValue.isObject()) {
76 qCWarning(QGCSerialPortInfoLog) << "Entry in boardInfo array is not object";
77 return false;
78 }
79
80 const QJsonObject boardObject = jsonValue.toObject();
81 if (!JsonParsing::validateKeysStrict(boardObject, boardKeyInfoList, errorString)) {
82 qCWarning(QGCSerialPortInfoLog) << errorString;
83 return false;
84 }
85
86 const BoardInfo_t boardInfo = {
87 boardObject[_jsonVendorIDKey].toInt(),
88 boardObject[_jsonProductIDKey].toInt(),
89 _boardClassStringToType(boardObject[_jsonBoardClassKey].toString()),
90 boardObject[_jsonNameKey].toString()
91 };
92 if (boardInfo.boardType == BoardTypeUnknown) {
93 qCWarning(QGCSerialPortInfoLog) << "Bad board class" << boardObject[_jsonBoardClassKey].toString();
94 return false;
95 }
96
97 _boardInfoList.append(boardInfo);
98 }
99
100 static const QList<JsonParsing::KeyValidateInfo> fallbackKeyInfoList = {
101 { "comment", QJsonValue::String, false }, // Developer notes only, never parsed
102 { _jsonRegExpKey, QJsonValue::String, true },
103 { _jsonBoardClassKey, QJsonValue::String, true },
104 { _jsonAndroidOnlyKey, QJsonValue::Bool, false },
105 };
106 const QJsonArray rgBoardDescriptionFallback = json[_jsonBoardDescriptionFallbackKey].toArray();
107 for (const QJsonValue &jsonValue : rgBoardDescriptionFallback) {
108 if (!jsonValue.isObject()) {
109 qCWarning(QGCSerialPortInfoLog) << "Entry in boardFallback array is not object";
110 return false;
111 }
112
113 const QJsonObject fallbackObject = jsonValue.toObject();
114 if (!JsonParsing::validateKeysStrict(fallbackObject, fallbackKeyInfoList, errorString)) {
115 qCWarning(QGCSerialPortInfoLog) << errorString;
116 return false;
117 }
118
119 const QRegularExpression regExp(fallbackObject[_jsonRegExpKey].toString(), QRegularExpression::CaseInsensitiveOption);
120 if (!regExp.isValid()) {
121 qCWarning(QGCSerialPortInfoLog) << "Invalid regular expression in board description fallback:"
122 << regExp.errorString()
123 << "pattern:" << fallbackObject[_jsonRegExpKey].toString();
124 return false;
125 }
126 const BoardRegExpFallback_t boardFallback = {
127 regExp,
128 _boardClassStringToType(fallbackObject[_jsonBoardClassKey].toString()),
129 fallbackObject[_jsonAndroidOnlyKey].toBool(false)
130 };
131 if (boardFallback.boardType == BoardTypeUnknown) {
132 qCWarning(QGCSerialPortInfoLog) << "Bad board class" << fallbackObject[_jsonBoardClassKey].toString();
133 return false;
134 }
135
136 _boardDescriptionFallbackList.append(boardFallback);
137 }
138
139 const QJsonArray rgBoardManufacturerFallback = json[_jsonBoardManufacturerFallbackKey].toArray();
140 for (const QJsonValue &jsonValue : rgBoardManufacturerFallback) {
141 if (!jsonValue.isObject()) {
142 qCWarning(QGCSerialPortInfoLog) << "Entry in boardFallback array is not object";
143 return false;
144 }
145
146 const QJsonObject fallbackObject = jsonValue.toObject();
147 if (!JsonParsing::validateKeysStrict(fallbackObject, fallbackKeyInfoList, errorString)) {
148 qCWarning(QGCSerialPortInfoLog) << errorString;
149 return false;
150 }
151
152 const QRegularExpression regExp(fallbackObject[_jsonRegExpKey].toString(), QRegularExpression::CaseInsensitiveOption);
153 if (!regExp.isValid()) {
154 qCWarning(QGCSerialPortInfoLog) << "Invalid regular expression in board manufacturer fallback:"
155 << regExp.errorString()
156 << "pattern:" << fallbackObject[_jsonRegExpKey].toString();
157 return false;
158 }
159 const BoardRegExpFallback_t boardFallback = {
160 regExp,
161 _boardClassStringToType(fallbackObject[_jsonBoardClassKey].toString()),
162 fallbackObject[_jsonAndroidOnlyKey].toBool(false)
163 };
164 if (boardFallback.boardType == BoardTypeUnknown) {
165 qCWarning(QGCSerialPortInfoLog) << "Bad board class" << fallbackObject[_jsonBoardClassKey].toString();
166 return false;
167 }
168
169 _boardManufacturerFallbackList.append(boardFallback);
170 }
171
172 _jsonDataValid = true;
173
174 return true;
175}
176
177QGCSerialPortInfo::BoardType_t QGCSerialPortInfo::_boardClassStringToType(const QString &boardClass)
178{
179 static const BoardClassString2BoardType_t rgBoardClass2BoardType[BoardTypeUnknown] = {
180 { _boardTypeToString(BoardTypePixhawk), BoardTypePixhawk },
181 { _boardTypeToString(BoardTypeRTKGPS), BoardTypeRTKGPS },
182 { _boardTypeToString(BoardTypeSiKRadio), BoardTypeSiKRadio },
183 { _boardTypeToString(BoardTypeOpenPilot), BoardTypeOpenPilot },
184 };
185
186 for (const BoardClassString2BoardType_t &board : rgBoardClass2BoardType) {
187 if (boardClass == board.classString) {
188 return board.boardType;
189 }
190 }
191
192 return BoardTypeUnknown;
193}
194
196{
197 boardType = BoardTypeUnknown;
198
199 if (!_loadJsonData()) {
200 return false;
201 }
202
203 if (isNull()) {
204 return false;
205 }
206
207 for (const BoardInfo_t &boardInfo : _boardInfoList) {
208 if ((vendorIdentifier() == boardInfo.vendorId) && ((productIdentifier() == boardInfo.productId) || (boardInfo.productId == 0))) {
209 boardType = boardInfo.boardType;
210 name = boardInfo.name;
211 return true;
212 }
213 }
214
215 for (const BoardRegExpFallback_t &boardFallback : _boardDescriptionFallbackList) {
216 if (description().contains(boardFallback.regExp)) {
217#ifndef Q_OS_ANDROID
218 if (boardFallback.androidOnly) {
219 continue;
220 }
221#endif
222 boardType = boardFallback.boardType;
223 name = _boardTypeToString(boardType);
224 return true;
225 }
226 }
227
228 for (const BoardRegExpFallback_t &boardFallback : _boardManufacturerFallbackList) {
229 if (manufacturer().contains(boardFallback.regExp)) {
230#ifndef Q_OS_ANDROID
231 if (boardFallback.androidOnly) {
232 continue;
233 }
234#endif
235 boardType = boardFallback.boardType;
236 name = _boardTypeToString(boardType);
237 return true;
238 }
239 }
240
241 return false;
242}
243
244QString QGCSerialPortInfo::_boardTypeToString(BoardType_t boardType)
245{
246 switch (boardType) {
247 case BoardTypePixhawk:
248 return QStringLiteral("Pixhawk");
250 return QStringLiteral("SiK Radio");
252 return QStringLiteral("OpenPilot");
253 case BoardTypeRTKGPS:
254 return QStringLiteral("RTK GPS");
255 case BoardTypeUnknown:
256 default:
257 return QStringLiteral("Unknown");
258 }
259}
260
261QList<QGCSerialPortInfo> QGCSerialPortInfo::availablePorts()
262{
263 QList<QGCSerialPortInfo> list;
264
265 const QList<QSerialPortInfo> availablePorts = QSerialPortInfo::availablePorts();
266 for (const QSerialPortInfo &portInfo : availablePorts) {
267 if (isSystemPort(portInfo)) {
268 continue;
269 }
270
271 const QGCSerialPortInfo *const qgcPortInfo = reinterpret_cast<const QGCSerialPortInfo*>(&portInfo);
272 list << *qgcPortInfo;
273 }
274
275 return list;
276}
277
279{
280 BoardType_t boardType;
281 QString name;
282 if (!getBoardInfo(boardType, name)) {
283 return false;
284 }
285
286 return ((boardType == BoardTypePixhawk) && description().contains(QStringLiteral("BL")));
287}
288
290{
291 return description().contains(QStringLiteral("CubeBlack"));
292}
293
295{
296#ifdef Q_OS_MACOS
297 static const QStringList systemPortLocations = {
298 QStringLiteral("tty.MALS"),
299 QStringLiteral("tty.SOC"),
300 QStringLiteral("tty.Bluetooth-Incoming-Port"),
301 QStringLiteral("tty.usbserial"),
302 QStringLiteral("tty.usbmodem")
303 };
304 for (const QString &systemPortLocation : systemPortLocations) {
305 if (port.systemLocation().contains(systemPortLocation)) {
306 return true;
307 }
308 }
309#else
310 Q_UNUSED(port);
311#endif
312
313 // TODO: Add Linux (LTE modems, etc) and Windows as needed
314
315 return false;
316}
317
319{
320 BoardType_t boardType;
321 QString name;
322 if (!getBoardInfo(boardType, name)) {
323 return false;
324 }
325
326 static const QList<BoardType_t> flashable = {
329 };
330
331 return flashable.contains(boardType);
332}
QString errorString
#define QGC_LOGGING_CATEGORY(name, categoryStr)
QGC's version of Qt QSerialPortInfo. It provides additional information about board types that QGC ca...
static bool isSystemPort(const QSerialPortInfo &port)
bool getBoardInfo(BoardType_t &boardType, QString &name) const
static QList< QGCSerialPortInfo > availablePorts()
Override of QSerialPortInfo::availablePorts.
Provides information about existing serial ports.
quint16 productIdentifier() const
Returns the 16-bit product number for the serial port, if available; otherwise returns zero.
QString manufacturer() const
Returns the manufacturer string of the serial port, if available; otherwise returns an empty string.
static QList< QSerialPortInfo > availablePorts()
Returns a list of available serial ports on the system.
bool isNull() const
Returns whether this QSerialPortInfo object holds a serial port definition.
QString description() const
Returns the description string of the serial port, if available; otherwise returns an empty string.
quint16 vendorIdentifier() const
Returns the 16-bit vendor number for the serial port, if available; otherwise returns zero.
Provides functions to access serial ports.
Definition qserialport.h:17
constexpr const char * jsonFileTypeKey
Definition JsonParsing.h:15
QJsonObject openInternalQGCJsonFile(const QString &jsonFilename, const QString &expectedFileType, int minSupportedVersion, int maxSupportedVersion, int &version, QString &errorString, const QStringList &defaultTranslateKeys, const QStringList &defaultArrayIDKeys)
bool validateKeysStrict(const QJsonObject &jsonObject, const QList< KeyValidateInfo > &keyInfo, QString &errorString)
Validates keys like validateKeys but also rejects any keys not listed in keyInfo.
constexpr const char * jsonVersionKey
Definition JsonParsing.h:14