QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
LinkManager.cc
Go to the documentation of this file.
1#include "LinkManager.h"
2#include "LogReplayLink.h"
3#include "QGCNetworkHelper.h"
4#include "MAVLinkProtocol.h"
6#include "AppMessages.h"
9#include "SettingsManager.h"
10#include "MavlinkSettings.h"
11#include "AutoConnectSettings.h"
12#include "TCPLink.h"
13#include "UDPLink.h"
14#include "BluetoothLink.h"
15
16#ifndef QGC_NO_SERIAL_LINK
17#include "SerialAutoConnect.h"
18#include "SerialLink.h"
19#include "SerialPortManager.h"
20#ifdef Q_OS_ANDROID
21#include "AndroidSerial.h"
22#include "AppSettings.h"
23#endif
24#endif
25
26#ifdef QT_DEBUG
27#include "MockLink.h"
28#endif
29
30#include <algorithm>
31#include <utility>
32
33#include <QtCore/QApplicationStatic>
34#include <QtCore/QTimer>
35
36QGC_LOGGING_CATEGORY(LinkManagerLog, "Comms.LinkManager")
37QGC_LOGGING_CATEGORY(LinkManagerVerboseLog, "Comms.LinkManager:verbose")
38
39Q_APPLICATION_STATIC(LinkManager, _linkManagerInstance);
40
41LinkManager::LinkManager(QObject *parent)
42 : QObject(parent)
43 , _portListTimer(new QTimer(this))
44 , _qmlConfigurations(new QmlObjectListModel(this))
45{
46 qCDebug(LinkManagerLog) << this;
47
48 (void) qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
49 (void) qRegisterMetaType<LinkInterface*>("LinkInterface*");
50#ifndef QGC_NO_SERIAL_LINK
51 (void) qRegisterMetaType<QGCSerialPortInfo>("QGCSerialPortInfo");
53 &LinkManager::_updateSerialPorts);
54#endif
55}
56
58{
59 qCDebug(LinkManagerLog) << this;
60}
61
63{
64 return _linkManagerInstance();
65}
66
68{
69 _autoConnectSettings = SettingsManager::instance()->autoConnectSettings();
70
71#ifndef QGC_NO_SERIAL_LINK
72#ifdef Q_OS_ANDROID
73 // The serial backend is fixed at startup. Changing the setting requires an app restart.
75 SettingsManager::instance()->appSettings()->androidUsePosixSerial()->rawValue().toBool());
77#endif
78 if (!_serialAutoConnect) {
79 _serialAutoConnect = std::make_unique<SerialAutoConnect>(
82 }
83#endif
84
85 if (!QGC::runningUnitTests()) {
86 (void) connect(_portListTimer, &QTimer::timeout, this, &LinkManager::_updateAutoConnectLinks);
87 _portListTimer->start(_autoconnectUpdateTimerMSecs); // timeout must be long enough to get past bootloader on second pass
88 }
89}
90
91QList<SharedLinkInterfacePtr> LinkManager::links()
92{
93 QMutexLocker locker(&_linksMutex);
94 return _rgLinks;
95}
96
97QmlObjectListModel *LinkManager::_qmlLinkConfigurations()
98{
99 return _qmlConfigurations;
100}
101
103{
104 for (SharedLinkConfigurationPtr &sharedConfig : _rgLinkConfigs) {
105 if (sharedConfig.get() == config) {
106 sharedConfig->setAutoConnectStarted(true);
107 sharedConfig->resetReconnectBackoff();
108 createConnectedLink(sharedConfig);
109 }
110 }
111}
112
114{
115 if (!link) {
116 return;
117 }
118
120 if (config) {
121 config->setSuppressAutoReconnect(true);
122 }
123
124 link->disconnect();
125}
126
128{
129 if (!config) {
130 return;
131 }
132
133 config->setSuppressAutoReconnect(true);
134
135 if (LinkInterface *const link = config->link()) {
136 link->disconnect();
137 }
138}
139
141{
142 config->setSuppressAutoReconnect(false);
143
144 SharedLinkInterfacePtr link = nullptr;
145
146 switch(config->type()) {
147#ifndef QGC_NO_SERIAL_LINK
149 const auto* serialConfig = qobject_cast<const SerialConfiguration*>(config.get());
150 if (!serialConfig) {
151 return false;
152 }
153 auto reservation = SerialPortManager::instance()->reservePort(serialConfig->portName());
154 if (!reservation) {
155 qCDebug(LinkManagerLog) << "Serial port is already reserved:" << serialConfig->portName();
156 return false;
157 }
158 link = std::make_shared<SerialLink>(config, std::move(reservation));
159 break;
160 }
161#endif
163 link = std::make_shared<UDPLink>(config);
164 break;
166 link = std::make_shared<TCPLink>(config);
167 break;
169 link = std::make_shared<BluetoothLink>(config);
170 break;
172 link = std::make_shared<LogReplayLink>(config);
173 break;
174#ifdef QT_DEBUG
175 case LinkConfiguration::TypeMock:
176 link = std::make_shared<MockLink>(config);
177 break;
178#endif
180 default:
181 break;
182 }
183
184 if (!link) {
185 return false;
186 }
187
188 if (!link->_allocateMavlinkChannel()) {
189 qCWarning(LinkManagerLog) << "Link failed to setup mavlink channels";
190 return false;
191 }
192
193 // Set up signal connections before adding to list, so link is fully initialized
194 (void) connect(link.get(), &LinkInterface::communicationError, this, &LinkManager::_communicationError);
197 (void) connect(link.get(), &LinkInterface::connected, this, &LinkManager::_linkConnected);
198 (void) connect(link.get(), &LinkInterface::disconnected, this, &LinkManager::_linkDisconnected);
199
201
202 // Try to connect before adding to active links list
203 if (!link->_connect()) {
204 (void) disconnect(link.get(), &LinkInterface::communicationError, this, &LinkManager::_communicationError);
207 (void) disconnect(link.get(), &LinkInterface::disconnected, this, &LinkManager::_linkDisconnected);
208 link->_freeMavlinkChannel();
209 config->setLink(nullptr);
210 return false;
211 }
212
213 {
214 QMutexLocker locker(&_linksMutex);
215 _rgLinks.append(link);
216 }
217 config->setLink(link);
218
219 return true;
220}
221
222void LinkManager::_linkConnected()
223{
224 const LinkInterface *const link = qobject_cast<LinkInterface*>(sender());
225 const SharedLinkConfigurationPtr config = link ? link->linkConfiguration() : nullptr;
226 if (config) {
227 config->noteConnected();
228 }
229}
230
231void LinkManager::_communicationError(const QString &title, const QString &error)
232{
233 const LinkInterface *const link = qobject_cast<LinkInterface*>(sender());
234 const SharedLinkConfigurationPtr config = link ? link->linkConfiguration() : nullptr;
235
236 // Auto-connect links retry on a timer; a popup per failed attempt is just noise. Log only.
237 if (config && config->isAutoConnect() && !config->suppressAutoReconnect()) {
238 qCDebug(LinkManagerLog) << "Auto-connect link error (will retry):" << title << error;
239 return;
240 }
241
242 // Dynamic links recreated on a timer: only the first attempt of a failure streak pops up
243 if (config && (config->reconnectAttempts() > 1)) {
244 qCDebug(LinkManagerLog) << "Link error on retry:" << title << error;
245 return;
246 }
247
249}
250
252{
253 QMutexLocker locker(&_linksMutex);
254
255 for (const SharedLinkInterfacePtr &link : _rgLinks) {
256 const SharedLinkConfigurationPtr linkConfig = link->linkConfiguration();
257 if (linkConfig && (linkConfig->type() == LinkConfiguration::TypeUdp) && (linkConfig->name() == _mavlinkForwardingLinkName)) {
258 return link;
259 }
260 }
261
262 return nullptr;
263}
264
266{
267 QMutexLocker locker(&_linksMutex);
268
269 for (const SharedLinkInterfacePtr &link : _rgLinks) {
270 const SharedLinkConfigurationPtr linkConfig = link->linkConfiguration();
271 if (linkConfig && (linkConfig->type() == LinkConfiguration::TypeUdp) && (linkConfig->name() == _mavlinkForwardingSupportLinkName)) {
272 return link;
273 }
274 }
275
276 return nullptr;
277}
278
280{
281 QList<SharedLinkInterfacePtr> links;
282 {
283 QMutexLocker locker(&_linksMutex);
284 links = _rgLinks;
285 }
286
287 for (const SharedLinkInterfacePtr &sharedLink: links) {
288 sharedLink->disconnect();
289 }
290}
291
292void LinkManager::_linkDisconnected()
293{
294 LinkInterface* const link = qobject_cast<LinkInterface*>(sender());
295
296 if (!link) {
297 return;
298 }
299
300 SharedLinkInterfacePtr linkToCleanup;
302 {
303 QMutexLocker locker(&_linksMutex);
304
305 for (auto it = _rgLinks.begin(); it != _rgLinks.end(); ++it) {
306 if (it->get() == link) {
307 config = it->get()->linkConfiguration();
308 const QString linkName = config ? config->name() : QStringLiteral("<null config>");
309 qCDebug(LinkManagerLog) << linkName << "use_count:" << it->use_count();
310 linkToCleanup = *it;
311 (void) _rgLinks.erase(it);
312 break;
313 }
314 }
315 }
316
317 if (!linkToCleanup) {
318 qCDebug(LinkManagerLog) << "link already removed";
319 return;
320 }
321
322 if (config) {
323 config->noteDisconnected();
324 config->setLink(nullptr);
325
326 if (config->isDynamic() && (config->name() == _mavlinkForwardingSupportLinkName) &&
327 _mavlinkSupportForwardingEnabled) {
328 _mavlinkSupportForwardingEnabled = false;
330 }
331 }
332
333 (void) disconnect(link, &LinkInterface::communicationError, this, &LinkManager::_communicationError);
336 (void) disconnect(link, &LinkInterface::connected, this, &LinkManager::_linkConnected);
337 (void) disconnect(link, &LinkInterface::disconnected, this, &LinkManager::_linkDisconnected);
338
339 link->_freeMavlinkChannel();
340}
341
343{
344 QMutexLocker locker(&_linksMutex);
345
346 for (const SharedLinkInterfacePtr &sharedLink: _rgLinks) {
347 if (sharedLink.get() == link) {
348 return sharedLink;
349 }
350 }
351
352 // Link not found - this is normal during disconnect when queued signals are still processing.
353 // Callers should check for nullptr return value.
354 qCDebug(LinkManagerLog) << "link not in list (likely disconnected)";
355 return SharedLinkInterfacePtr(nullptr);
356}
357
358bool LinkManager::_connectionsSuspendedMsg() const
359{
360 if (_connectionsSuspended) {
361 QGC::showAppMessage(tr("Connect not allowed: %1").arg(_connectionsSuspendedReason));
362 return true;
363 }
364
365 return false;
366}
367
369{
370 QSettings settings;
371 settings.remove(LinkConfiguration::settingsRoot());
372
373 int trueCount = 0;
374 for (int i = 0; i < _rgLinkConfigs.count(); i++) {
375 SharedLinkConfigurationPtr linkConfig = _rgLinkConfigs[i];
376 if (!linkConfig) {
377 qCWarning(LinkManagerLog) << "Internal error for link configuration in LinkManager";
378 continue;
379 }
380
381 if (linkConfig->isDynamic()) {
382 continue;
383 }
384
385 const QString root = LinkConfiguration::settingsRoot() + QStringLiteral("/Link%1").arg(trueCount++);
386 settings.setValue(root + "/name", linkConfig->name());
387 settings.setValue(root + "/type", linkConfig->type());
388 settings.setValue(root + "/auto", linkConfig->isAutoConnect());
389 settings.setValue(root + "/high_latency", linkConfig->isHighLatency());
390 linkConfig->saveSettings(settings, root);
391 }
392
393 const QString root = QString(LinkConfiguration::settingsRoot());
394 settings.setValue(root + "/count", trueCount);
395}
396
398{
399 QSettings settings;
400 // Is the group even there?
401 if (settings.contains(LinkConfiguration::settingsRoot() + "/count")) {
402 // Find out how many configurations we have
403 const int count = settings.value(LinkConfiguration::settingsRoot() + "/count").toInt();
404 for (int i = 0; i < count; i++) {
405 const QString root = LinkConfiguration::settingsRoot() + QStringLiteral("/Link%1").arg(i);
406 if (!settings.contains(root + "/type")) {
407 qCWarning(LinkManagerLog) << "Link Configuration" << root << "has no type.";
408 continue;
409 }
410
411 LinkConfiguration::LinkType type = static_cast<LinkConfiguration::LinkType>(settings.value(root + "/type").toInt());
412 if (type >= LinkConfiguration::TypeLast) {
413 qCWarning(LinkManagerLog) << "Link Configuration" << root << "an invalid type:" << type;
414 continue;
415 }
416
417 if (!settings.contains(root + "/name")) {
418 qCWarning(LinkManagerLog) << "Link Configuration" << root << "has no name.";
419 continue;
420 }
421
422 const QString name = settings.value(root + "/name").toString();
423 if (name.isEmpty()) {
424 qCWarning(LinkManagerLog) << "Link Configuration" << root << "has an empty name.";
425 continue;
426 }
427
428 LinkConfiguration* link = nullptr;
429 switch(type) {
430#ifndef QGC_NO_SERIAL_LINK
432 link = new SerialConfiguration(name);
433 break;
434#endif
436 link = new UDPConfiguration(name);
437 break;
439 link = new TCPConfiguration(name);
440 break;
442 link = new BluetoothConfiguration(name);
443 break;
445 link = new LogReplayConfiguration(name);
446 break;
447#ifdef QT_DEBUG
448 case LinkConfiguration::TypeMock:
449 link = new MockConfiguration(name);
450 break;
451#endif
453 default:
454 break;
455 }
456
457 if (link) {
458 const bool autoConnect = settings.value(root + "/auto").toBool();
459 link->setAutoConnect(autoConnect);
460 const bool highLatency = settings.value(root + "/high_latency").toBool();
461 link->setHighLatency(highLatency);
462 link->loadSettings(settings, root);
463 addConfiguration(link);
464 }
465 }
466 }
467
468 // Enable automatic Serial PX4/3DR Radio hunting
469 _configurationsLoaded = true;
470}
471
472void LinkManager::_addUDPAutoConnectLink()
473{
474 if (!_autoConnectSettings->autoConnectUDP()->rawValue().toBool()) {
475 return;
476 }
477
478 _retryDynamicUdpLink(_defaultUDPLinkName, [this](UDPConfiguration& udpConfig) {
479 udpConfig.setAutoConnect(true);
480 // Listening is this link's main job; an unresolvable target must not stop it
481 udpConfig.setRequireResolvedHosts(false);
482 udpConfig.setLocalPort(_autoConnectSettings->udpListenPort()->rawValue().toUInt());
483 const QString targetHostIP = _autoConnectSettings->udpTargetHostIP()->rawValue().toString();
484 if (!targetHostIP.isEmpty()) {
485 udpConfig.addHost(targetHostIP, _autoConnectSettings->udpTargetHostPort()->rawValue().toUInt());
486 }
487 });
488}
489
490void LinkManager::_addMAVLinkForwardingLink()
491{
492 if (!SettingsManager::instance()->mavlinkSettings()->forwardMavlink()->rawValue().toBool()) {
493 return;
494 }
495
496 _retryDynamicUdpLink(_mavlinkForwardingLinkName, [](UDPConfiguration& udpConfig) {
497 udpConfig.setForwarding(true);
498 udpConfig.addHost(
499 SettingsManager::instance()->mavlinkSettings()->forwardMavlinkHostName()->rawValue().toString());
500 });
501}
502
503void LinkManager::_retryDynamicUdpLink(const QString& name, const std::function<void(UDPConfiguration&)>& configure)
504{
505 SharedLinkConfigurationPtr config = _findDynamicUdpConfiguration(name);
506 if (config && (config->link() || !config->reconnectReady())) {
507 return;
508 }
509
510 UDPConfiguration udpConfig(name);
511 udpConfig.setDynamic(true);
512 configure(udpConfig);
513
514 // Reuse the config on retry so its backoff applies and configs don't pile up
515 if (config) {
516 config->copyFrom(&udpConfig);
517 qCDebug(LinkManagerLog) << "Retrying dynamic UDP link:" << name;
518 } else {
519 config = addConfiguration(new UDPConfiguration(&udpConfig));
520 qCDebug(LinkManagerLog) << "New dynamic UDP link added:" << name << udpConfig.hostList();
521 }
522
523 config->noteReconnectAttempt();
525}
526
527SharedLinkConfigurationPtr LinkManager::_findDynamicUdpConfiguration(const QString& name) const
528{
529 // User-created links may use the same name; those must never be reused
530 for (const SharedLinkConfigurationPtr& config : _rgLinkConfigs) {
531 if (config && config->isDynamic() && (config->type() == LinkConfiguration::TypeUdp) &&
532 (config->name() == name)) {
533 return config;
534 }
535 }
536
537 return nullptr;
538}
539
540void LinkManager::_reconnectAutoConnectLinks()
541{
542 for (SharedLinkConfigurationPtr &config : _rgLinkConfigs) {
543 if (!config || config->isDynamic() || !config->isAutoConnect()) {
544 continue;
545 }
546
547 // Only re-establish links started this session (boot or manual connect); a freshly
548 // added auto-connect config waits for next app start rather than connecting now.
549 if (config->link() || config->suppressAutoReconnect() || !config->autoConnectStarted()) {
550 continue;
551 }
552
553 // Exponential backoff between attempts so a dead host isn't hammered every tick.
554 if (!config->reconnectReady()) {
555 continue;
556 }
557
558 qCDebug(LinkManagerLog) << "Reconnecting auto-connect link" << config->name();
559 config->noteReconnectAttempt();
561 }
562}
563
564void LinkManager::_updateAutoConnectLinks()
565{
566#ifndef QGC_NO_SERIAL_LINK
567 // Existing links still need fresh unplug checks while new connections are suspended.
568 const auto ports = SerialPortManager::instance()->availablePorts();
569#endif
570 if (_connectionsSuspended) {
571 return;
572 }
573
574 _addUDPAutoConnectLink();
575 _addMAVLinkForwardingLink();
576 _reconnectAutoConnectLinks();
577
578#ifndef QGC_NO_SERIAL_LINK
579 if (_serialAutoConnect) {
580 _serialAutoConnect->update(ports, {_autoConnectSettings->autoConnectPixhawk()->rawValue().toBool(),
581 _autoConnectSettings->autoConnectSiKRadio()->rawValue().toBool(),
582 _autoConnectSettings->autoConnectLibrePilot()->rawValue().toBool()});
583 }
584#endif
585}
586
588{
589 setConnectionsSuspended(tr("Shutdown"));
591
592 // Wait for all the vehicles to go away to ensure an orderly shutdown and deletion of all objects
593 while (MultiVehicleManager::instance()->vehicles()->count()) {
594 QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
595 }
596}
597
599{
600 //-- Must follow same order as enum LinkType in LinkConfiguration.h
601 static QStringList list;
602 if (!list.isEmpty()) {
603 return list;
604 }
605
606#ifndef QGC_NO_SERIAL_LINK
607 list += tr("Serial");
608#endif
609 list += tr("UDP");
610 list += tr("TCP");
611 list += tr("Bluetooth");
612#ifdef QT_DEBUG
613 list += tr("Mock Link");
614#endif
615 list += tr("Log Replay");
616
617 if (list.size() != static_cast<int>(LinkConfiguration::TypeLast)) {
618 qCWarning(LinkManagerLog) << "Internal error";
619 }
620
621 return list;
622}
623
625{
626 if (!config || !editedConfig) {
627 qCWarning(LinkManagerLog) << "Internal error";
628 return;
629 }
630
631 config->copyFrom(editedConfig);
633 emit config->nameChanged(config->name());
634 // Discard temporary duplicate
635 delete editedConfig;
636}
637
639{
640 if (!config) {
641 qCWarning(LinkManagerLog) << "Internal error";
642 return;
643 }
644
647}
648
650{
651#ifndef QGC_NO_SERIAL_LINK
653 _updateSerialPorts();
654 }
655#endif
656
657 return LinkConfiguration::createSettings(type, name);
658}
659
661{
662 if (!config) {
663 qCWarning(LinkManagerLog) << "Internal error";
664 return nullptr;
665 }
666
667#ifndef QGC_NO_SERIAL_LINK
668 if (config->type() == LinkConfiguration::TypeSerial) {
669 _updateSerialPorts();
670 }
671#endif
672
674}
675
677{
678 if (!config) {
679 qCWarning(LinkManagerLog) << "Internal error";
680 return;
681 }
682
683 LinkInterface* const link = config->link();
684 if (link) {
685 link->disconnect();
686 }
687
688 _removeConfiguration(config);
690}
691
693{
694 SharedLinkConfigurationPtr config = _findDynamicUdpConfiguration(_mavlinkForwardingSupportLinkName);
695 if (config && config->link()) {
696 return;
697 }
698
699 UDPConfiguration udpConfig(_mavlinkForwardingSupportLinkName);
700 udpConfig.setDynamic(true);
701 udpConfig.setForwarding(true);
702 udpConfig.addHost(
703 SettingsManager::instance()->mavlinkSettings()->forwardMavlinkAPMSupportHostName()->rawValue().toString());
704
705 if (config) {
706 config->copyFrom(&udpConfig);
707 } else {
708 config = addConfiguration(new UDPConfiguration(&udpConfig));
709 }
710
712 return;
713 }
714
715 // Cleared again in _linkDisconnected if the connect fails
716 _mavlinkSupportForwardingEnabled = true;
718}
719
720void LinkManager::_removeConfiguration(const LinkConfiguration *config)
721{
722 (void) _qmlConfigurations->removeOne(config);
723
724 for (auto it = _rgLinkConfigs.begin(); it != _rgLinkConfigs.end(); ++it) {
725 if (it->get() == config) {
726 (void) _rgLinkConfigs.erase(it);
727 return;
728 }
729 }
730
731 qCWarning(LinkManagerLog) << "called with unknown config";
732}
733
738
740{
741 QMutexLocker locker(&_linksMutex);
742
743 for (const SharedLinkInterfacePtr &sharedLink : _rgLinks) {
744 if (sharedLink.get() == link) {
745 return true;
746 }
747 }
748
749 return false;
750}
751
753{
754 (void) _qmlConfigurations->append(config);
755 (void) _rgLinkConfigs.append(SharedLinkConfigurationPtr(config));
756
757 return _rgLinkConfigs.last();
758}
759
760bool LinkManager::containsConfiguration(const QString& name) const
761{
762 return std::ranges::any_of(_rgLinkConfigs, [&name](const SharedLinkConfigurationPtr& config) {
763 return config && (config->name() == name);
764 });
765}
766
768{
769 for (SharedLinkConfigurationPtr &sharedConfig : _rgLinkConfigs) {
770 if (sharedConfig->isAutoConnect()) {
771 sharedConfig->setAutoConnectStarted(true);
772 createConnectedLink(sharedConfig);
773 }
774 }
775}
776
778{
779 for (uint8_t mavlinkChannel = 0; mavlinkChannel < MAVLINK_COMM_NUM_BUFFERS; mavlinkChannel++) {
780 if (_mavlinkChannelsUsedBitMask & (1 << mavlinkChannel)) {
781 continue;
782 }
783
784 mavlink_reset_channel_status(mavlinkChannel);
785 mavlink_status_t* const mavlinkStatus = mavlink_get_channel_status(mavlinkChannel);
786 mavlinkStatus->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
787 _mavlinkChannelsUsedBitMask |= (1 << mavlinkChannel);
788 qCDebug(LinkManagerLog) << "allocateMavlinkChannel" << mavlinkChannel;
789 return mavlinkChannel;
790 }
791
792 qCWarning(LinkManagerLog) << "allocateMavlinkChannel: all channels reserved!";
793 return invalidMavlinkChannel();
794}
795
797{
798 qCDebug(LinkManagerLog) << "freeMavlinkChannel" << channel;
799
800 if (invalidMavlinkChannel() == channel) {
801 return;
802 }
803
804 _mavlinkChannelsUsedBitMask &= ~(1 << channel);
805}
806
808{
809 LogReplayConfiguration* const linkConfig = new LogReplayConfiguration(tr("Log Replay"));
810 linkConfig->setLogFilename(logFile);
811 linkConfig->setName(linkConfig->logFilenameShort());
812
813 SharedLinkConfigurationPtr sharedConfig = addConfiguration(linkConfig);
814 if (createConnectedLink(sharedConfig)) {
815 return qobject_cast<LogReplayLink*>(sharedConfig->link());
816 }
817
818 return nullptr;
819}
820
821bool LinkManager::isLinkUSBDirect([[maybe_unused]] const LinkInterface *link)
822{
823#ifndef QGC_NO_SERIAL_LINK
824 const SerialLink* const serialLink = qobject_cast<const SerialLink*>(link);
825 if (!serialLink) {
826 return false;
827 }
828
830 if (!config) {
831 return false;
832 }
833
834 const SerialConfiguration* const serialConfig = qobject_cast<const SerialConfiguration*>(config.get());
835 if (serialConfig && serialConfig->usbDirect()) {
836 return link;
837 }
838#endif
839
840 return false;
841}
842
843#ifndef QGC_NO_SERIAL_LINK // Serial Only Functions
844
845void LinkManager::_updateSerialPorts()
846{
847 QStringList portList;
848 QStringList displayList;
849 const auto inventory = SerialPortManager::instance()->availablePorts();
850 for (const SerialPortManager::Port& info : inventory) {
851 const QString port = info.systemLocation;
852 portList += port;
853 displayList += info.displayName.isEmpty() ? info.portName : info.displayName;
854 }
855 const bool portsChanged = _commPortList != portList;
856 const bool displayChanged = _commPortDisplayList != displayList;
857 _commPortList = std::move(portList);
858 _commPortDisplayList = std::move(displayList);
859 if (portsChanged) {
860 emit commPortsChanged();
861 }
862 if (displayChanged) {
864 }
865}
866
868{
869 if (_commPortDisplayList.isEmpty()) {
870 _updateSerialPorts();
871 }
872
873 return _commPortDisplayList;
874}
875
877{
878 if (_commPortList.isEmpty()) {
879 _updateSerialPorts();
880 }
881
882 return _commPortList;
883}
884
889
890#endif // QGC_NO_SERIAL_LINK
Config config
std::shared_ptr< LinkConfiguration > SharedLinkConfigurationPtr
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
Q_APPLICATION_STATIC(LinkManager, _linkManagerInstance)
mavlink_status_t * mavlink_get_channel_status(uint8_t chan)
Definition QGCMAVLink.cc:53
#define MAVLINK_COMM_NUM_BUFFERS
Error error
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Interface holding link specific settings.
@ TypeSerial
Serial Link.
@ TypeBluetooth
Bluetooth Link.
virtual void loadSettings(QSettings &settings, const QString &root)=0
static LinkConfiguration * duplicateSettings(const LinkConfiguration *source)
void setDynamic(bool dynamic=true)
Set if this is this a dynamic configuration. (decided at runtime)
void setHighLatency(bool hl=false)
Set if this is this an High Latency configuration.
static LinkConfiguration * createSettings(int type, const QString &name)
void setForwarding(bool forwarding=true)
Set if this is this a forwarding link configuration. (decided at runtime)
static QString settingsRoot()
void setName(const QString &name)
void setAutoConnect(bool autoc=true)
Set if this is this an Auto Connect configuration.
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 disconnected()
virtual void _freeMavlinkChannel()
virtual Q_INVOKABLE void disconnect()=0
void communicationError(const QString &title, const QString &error)
void bytesSent(LinkInterface *link, const QByteArray &data)
void connected()
SharedLinkConfigurationPtr linkConfiguration()
Manage communication links The Link Manager organizes the physical Links. It can manage arbitrary lin...
Definition LinkManager.h:30
void commPortStringsChanged()
Q_INVOKABLE void createConnectedLink(const LinkConfiguration *config)
This should only be used by Qml code.
SharedLinkInterfacePtr sharedLinkInterfacePointerForLink(const LinkInterface *link)
QStringList serialPorts()
bool containsConfiguration(const QString &name) const
Q_INVOKABLE void endConfigurationEditing(LinkConfiguration *config, LinkConfiguration *editedConfig)
SharedLinkConfigurationPtr addConfiguration(LinkConfiguration *config)
QStringList linkTypeStrings() const
void loadLinkConfigurationList()
static bool isLinkUSBDirect(const LinkInterface *link)
uint8_t allocateMavlinkChannel()
Q_INVOKABLE void createMavlinkForwardingSupportLink()
Q_INVOKABLE LinkConfiguration * createConfiguration(int type, const QString &name)
Create/Edit Link Configuration.
void startAutoConnectedLinks()
Q_INVOKABLE void endCreateConfiguration(LinkConfiguration *config)
Q_INVOKABLE void shutdown()
Called to signal app shutdown. Disconnects all links while turning off auto-connect.
static LinkManager * instance()
void freeMavlinkChannel(uint8_t channel)
Q_INVOKABLE void removeConfiguration(LinkConfiguration *config)
QList< SharedLinkInterfacePtr > links()
static constexpr uint8_t invalidMavlinkChannel()
void setConnectionsSuspended(const QString &reason)
Definition LinkManager.h:77
void disconnectAll()
Q_INVOKABLE LogReplayLink * startLogReplay(const QString &logFile)
void commPortsChanged()
QStringList serialPortStrings()
Q_INVOKABLE void disconnectLink(LinkInterface *link)
static QStringList serialBaudRates()
SharedLinkInterfacePtr mavlinkForwardingSupportLink()
Returns pointer to the mavlink support forwarding link, or nullptr if it does not exist.
Q_INVOKABLE void disconnectLinkConfiguration(LinkConfiguration *config)
Stop a link and suppress auto-reconnect, working whether or not a live link currently exists.
Q_INVOKABLE LinkConfiguration * startConfigurationEditing(LinkConfiguration *config)
bool containsLink(const LinkInterface *link)
SharedLinkInterfacePtr mavlinkForwardingLink()
Returns pointer to the mavlink forwarding link, or nullptr if it does not exist.
static bool isBluetoothAvailable()
void saveLinkConfigurationList()
void mavlinkSupportForwardingEnabledChanged()
QString logFilenameShort() const
void setLogFilename(const QString &logFilename)
void receiveBytes(LinkInterface *link, const QByteArray &data)
void logSentBytes(const LinkInterface *link, const QByteArray &data)
static MAVLinkProtocol * instance()
void resetMetadataForLink(LinkInterface *link)
static MultiVehicleManager * instance()
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
QObject * removeOne(const QObject *object) override final
bool usbDirect() const
Definition SerialLink.h:80
static QStringList supportedBaudRates()
static SerialPortManager * instance()
QList< Port > availablePorts()
void portsEnumerated(const QStringList &availablePorts)
Emitted only after a fresh scan, never for a retained Android snapshot. Includes native names and pat...
ReservationPtr reservePort(const QString &systemLocation)
void setSinglePortOnly(bool enabled)
AutoConnectSettings * autoConnectSettings() const
static SettingsManager * instance()
QStringList hostList() const
Definition UDPLink.h:83
void setLocalPort(quint16 port)
Definition UDPLink.h:95
Q_INVOKABLE void addHost(const QString &host)
Definition UDPLink.cc:129
void setRequireResolvedHosts(bool require)
Definition UDPLink.h:92
bool usePosixSerial()
void setUsePosixSerial(bool use)
bool isBluetoothAvailable()
Check if Bluetooth is available on this device.
bool runningUnitTests()
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.
Definition AppMessages.cc:9