16#ifndef QGC_NO_SERIAL_LINK
33#include <QtCore/QApplicationStatic>
34#include <QtCore/QTimer>
43 , _portListTimer(new QTimer(this))
46 qCDebug(LinkManagerLog) <<
this;
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);
59 qCDebug(LinkManagerLog) <<
this;
64 return _linkManagerInstance();
71#ifndef QGC_NO_SERIAL_LINK
78 if (!_serialAutoConnect) {
79 _serialAutoConnect = std::make_unique<SerialAutoConnect>(
86 (void) connect(_portListTimer, &QTimer::timeout,
this, &LinkManager::_updateAutoConnectLinks);
87 _portListTimer->start(_autoconnectUpdateTimerMSecs);
93 QMutexLocker locker(&_linksMutex);
99 return _qmlConfigurations;
105 if (sharedConfig.get() ==
config) {
106 sharedConfig->setAutoConnectStarted(
true);
107 sharedConfig->resetReconnectBackoff();
121 config->setSuppressAutoReconnect(
true);
133 config->setSuppressAutoReconnect(
true);
142 config->setSuppressAutoReconnect(
false);
147#ifndef QGC_NO_SERIAL_LINK
149 const auto* serialConfig = qobject_cast<const SerialConfiguration*>(
config.get());
155 qCDebug(LinkManagerLog) <<
"Serial port is already reserved:" << serialConfig->portName();
158 link = std::make_shared<SerialLink>(
config, std::move(reservation));
163 link = std::make_shared<UDPLink>(
config);
166 link = std::make_shared<TCPLink>(
config);
169 link = std::make_shared<BluetoothLink>(
config);
172 link = std::make_shared<LogReplayLink>(
config);
175 case LinkConfiguration::TypeMock:
176 link = std::make_shared<MockLink>(
config);
188 if (!link->_allocateMavlinkChannel()) {
189 qCWarning(LinkManagerLog) <<
"Link failed to setup mavlink channels";
203 if (!link->_connect()) {
208 link->_freeMavlinkChannel();
214 QMutexLocker locker(&_linksMutex);
215 _rgLinks.append(link);
222void LinkManager::_linkConnected()
224 const LinkInterface *
const link = qobject_cast<LinkInterface*>(sender());
231void LinkManager::_communicationError(
const QString &title,
const QString &
error)
233 const LinkInterface *
const link = qobject_cast<LinkInterface*>(sender());
238 qCDebug(LinkManagerLog) <<
"Auto-connect link error (will retry):" << title <<
error;
244 qCDebug(LinkManagerLog) <<
"Link error on retry:" << title <<
error;
253 QMutexLocker locker(&_linksMutex);
267 QMutexLocker locker(&_linksMutex);
271 if (linkConfig && (linkConfig->type() ==
LinkConfiguration::TypeUdp) && (linkConfig->name() == _mavlinkForwardingSupportLinkName)) {
281 QList<SharedLinkInterfacePtr>
links;
283 QMutexLocker locker(&_linksMutex);
288 sharedLink->disconnect();
292void LinkManager::_linkDisconnected()
294 LinkInterface*
const link = qobject_cast<LinkInterface*>(sender());
303 QMutexLocker locker(&_linksMutex);
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();
311 (void) _rgLinks.erase(it);
317 if (!linkToCleanup) {
318 qCDebug(LinkManagerLog) <<
"link already removed";
323 config->noteDisconnected();
326 if (
config->isDynamic() && (
config->name() == _mavlinkForwardingSupportLinkName) &&
327 _mavlinkSupportForwardingEnabled) {
328 _mavlinkSupportForwardingEnabled =
false;
344 QMutexLocker locker(&_linksMutex);
347 if (sharedLink.get() == link) {
354 qCDebug(LinkManagerLog) <<
"link not in list (likely disconnected)";
358bool LinkManager::_connectionsSuspendedMsg()
const
360 if (_connectionsSuspended) {
374 for (
int i = 0; i < _rgLinkConfigs.count(); i++) {
377 qCWarning(LinkManagerLog) <<
"Internal error for link configuration in LinkManager";
381 if (linkConfig->isDynamic()) {
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);
394 settings.setValue(root +
"/count", trueCount);
404 for (
int i = 0; i < count; i++) {
406 if (!settings.contains(root +
"/type")) {
407 qCWarning(LinkManagerLog) <<
"Link Configuration" << root <<
"has no type.";
413 qCWarning(LinkManagerLog) <<
"Link Configuration" << root <<
"an invalid type:" << type;
417 if (!settings.contains(root +
"/name")) {
418 qCWarning(LinkManagerLog) <<
"Link Configuration" << root <<
"has no name.";
422 const QString name = settings.value(root +
"/name").toString();
423 if (name.isEmpty()) {
424 qCWarning(LinkManagerLog) <<
"Link Configuration" << root <<
"has an empty name.";
430#ifndef QGC_NO_SERIAL_LINK
448 case LinkConfiguration::TypeMock:
458 const bool autoConnect = settings.value(root +
"/auto").toBool();
460 const bool highLatency = settings.value(root +
"/high_latency").toBool();
469 _configurationsLoaded =
true;
472void LinkManager::_addUDPAutoConnectLink()
474 if (!_autoConnectSettings->autoConnectUDP()->rawValue().toBool()) {
478 _retryDynamicUdpLink(_defaultUDPLinkName, [
this](
UDPConfiguration& udpConfig) {
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());
490void LinkManager::_addMAVLinkForwardingLink()
496 _retryDynamicUdpLink(_mavlinkForwardingLinkName, [](
UDPConfiguration& udpConfig) {
503void LinkManager::_retryDynamicUdpLink(
const QString& name,
const std::function<
void(
UDPConfiguration&)>& configure)
512 configure(udpConfig);
516 config->copyFrom(&udpConfig);
517 qCDebug(LinkManagerLog) <<
"Retrying dynamic UDP link:" << name;
520 qCDebug(LinkManagerLog) <<
"New dynamic UDP link added:" << name << udpConfig.
hostList();
523 config->noteReconnectAttempt();
532 (
config->name() == name)) {
540void LinkManager::_reconnectAutoConnectLinks()
549 if (
config->link() ||
config->suppressAutoReconnect() || !
config->autoConnectStarted()) {
554 if (!
config->reconnectReady()) {
558 qCDebug(LinkManagerLog) <<
"Reconnecting auto-connect link" <<
config->name();
559 config->noteReconnectAttempt();
564void LinkManager::_updateAutoConnectLinks()
566#ifndef QGC_NO_SERIAL_LINK
570 if (_connectionsSuspended) {
574 _addUDPAutoConnectLink();
575 _addMAVLinkForwardingLink();
576 _reconnectAutoConnectLinks();
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()});
594 QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
601 static QStringList list;
602 if (!list.isEmpty()) {
606#ifndef QGC_NO_SERIAL_LINK
607 list += tr(
"Serial");
611 list += tr(
"Bluetooth");
613 list += tr(
"Mock Link");
615 list += tr(
"Log Replay");
618 qCWarning(LinkManagerLog) <<
"Internal error";
626 if (!
config || !editedConfig) {
627 qCWarning(LinkManagerLog) <<
"Internal error";
631 config->copyFrom(editedConfig);
641 qCWarning(LinkManagerLog) <<
"Internal error";
651#ifndef QGC_NO_SERIAL_LINK
653 _updateSerialPorts();
663 qCWarning(LinkManagerLog) <<
"Internal error";
667#ifndef QGC_NO_SERIAL_LINK
669 _updateSerialPorts();
679 qCWarning(LinkManagerLog) <<
"Internal error";
688 _removeConfiguration(
config);
706 config->copyFrom(&udpConfig);
716 _mavlinkSupportForwardingEnabled =
true;
724 for (
auto it = _rgLinkConfigs.begin(); it != _rgLinkConfigs.end(); ++it) {
725 if (it->get() ==
config) {
726 (void) _rgLinkConfigs.erase(it);
731 qCWarning(LinkManagerLog) <<
"called with unknown config";
741 QMutexLocker locker(&_linksMutex);
744 if (sharedLink.get() == link) {
757 return _rgLinkConfigs.last();
770 if (sharedConfig->isAutoConnect()) {
771 sharedConfig->setAutoConnectStarted(
true);
780 if (_mavlinkChannelsUsedBitMask & (1 << mavlinkChannel)) {
784 mavlink_reset_channel_status(mavlinkChannel);
786 mavlinkStatus->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
787 _mavlinkChannelsUsedBitMask |= (1 << mavlinkChannel);
788 qCDebug(LinkManagerLog) <<
"allocateMavlinkChannel" << mavlinkChannel;
789 return mavlinkChannel;
792 qCWarning(LinkManagerLog) <<
"allocateMavlinkChannel: all channels reserved!";
798 qCDebug(LinkManagerLog) <<
"freeMavlinkChannel" << channel;
804 _mavlinkChannelsUsedBitMask &= ~(1 << channel);
815 return qobject_cast<LogReplayLink*>(sharedConfig->link());
823#ifndef QGC_NO_SERIAL_LINK
824 const SerialLink*
const serialLink = qobject_cast<const SerialLink*>(link);
835 if (serialConfig && serialConfig->
usbDirect()) {
843#ifndef QGC_NO_SERIAL_LINK
845void LinkManager::_updateSerialPorts()
847 QStringList portList;
848 QStringList displayList;
851 const QString port = info.systemLocation;
853 displayList += info.displayName.isEmpty() ? info.portName : info.displayName;
855 const bool portsChanged = _commPortList != portList;
856 const bool displayChanged = _commPortDisplayList != displayList;
857 _commPortList = std::move(portList);
858 _commPortDisplayList = std::move(displayList);
862 if (displayChanged) {
869 if (_commPortDisplayList.isEmpty()) {
870 _updateSerialPorts();
873 return _commPortDisplayList;
878 if (_commPortList.isEmpty()) {
879 _updateSerialPorts();
882 return _commPortList;
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)
#define MAVLINK_COMM_NUM_BUFFERS
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Interface holding link specific settings.
@ 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)
virtual void _freeMavlinkChannel()
virtual Q_INVOKABLE void disconnect()=0
void communicationError(const QString &title, const QString &error)
void bytesSent(LinkInterface *link, const QByteArray &data)
SharedLinkConfigurationPtr linkConfiguration()
Manage communication links The Link Manager organizes the physical Links. It can manage arbitrary lin...
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)
Q_INVOKABLE LogReplayLink * startLogReplay(const QString &logFile)
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
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
void setLocalPort(quint16 port)
Q_INVOKABLE void addHost(const QString &host)
void setRequireResolvedHosts(bool require)
void setUsePosixSerial(bool use)
bool isBluetoothAvailable()
Check if Bluetooth is available on this device.
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.