20#ifndef QGC_NO_SERIAL_LINK
34#include <QtCore/QApplicationStatic>
35#include <QtCore/QTimer>
44 , _portListTimer(new QTimer(this))
48 qCDebug(LinkManagerLog) <<
this;
50 (void) qRegisterMetaType<QAbstractSocket::SocketError>(
"QAbstractSocket::SocketError");
51 (void) qRegisterMetaType<LinkInterface*>(
"LinkInterface*");
52#ifndef QGC_NO_SERIAL_LINK
53 (void) qRegisterMetaType<QGCSerialPortInfo>(
"QGCSerialPortInfo");
59 qCDebug(LinkManagerLog) <<
this;
64 return _linkManagerInstance();
71#if defined(Q_OS_ANDROID) && !defined(QGC_NO_SERIAL_LINK)
78 (void) connect(_portListTimer, &QTimer::timeout,
this, &LinkManager::_updateAutoConnectLinks);
79 _portListTimer->start(_autoconnectUpdateTimerMSecs);
85 QMutexLocker locker(&_linksMutex);
91 return _qmlConfigurations;
97 if (sharedConfig.get() ==
config) {
98 sharedConfig->setAutoConnectStarted(
true);
99 sharedConfig->resetReconnectBackoff();
113 config->setSuppressAutoReconnect(
true);
125 config->setSuppressAutoReconnect(
true);
134 config->setSuppressAutoReconnect(
false);
139#ifndef QGC_NO_SERIAL_LINK
141 link = std::make_shared<SerialLink>(
config);
145 link = std::make_shared<UDPLink>(
config);
148 link = std::make_shared<TCPLink>(
config);
151 link = std::make_shared<BluetoothLink>(
config);
154 link = std::make_shared<LogReplayLink>(
config);
157 case LinkConfiguration::TypeMock:
158 link = std::make_shared<MockLink>(
config);
170 if (!link->_allocateMavlinkChannel()) {
171 qCWarning(LinkManagerLog) <<
"Link failed to setup mavlink channels";
185 if (!link->_connect()) {
190 link->_freeMavlinkChannel();
196 QMutexLocker locker(&_linksMutex);
197 _rgLinks.append(link);
204void LinkManager::_linkConnected()
206 const LinkInterface *
const link = qobject_cast<LinkInterface*>(sender());
213void LinkManager::_communicationError(
const QString &title,
const QString &
error)
215 const LinkInterface *
const link = qobject_cast<LinkInterface*>(sender());
220 qCDebug(LinkManagerLog) <<
"Auto-connect link error (will retry):" << title <<
error;
229 QMutexLocker locker(&_linksMutex);
243 QMutexLocker locker(&_linksMutex);
247 if (linkConfig && (linkConfig->type() ==
LinkConfiguration::TypeUdp) && (linkConfig->name() == _mavlinkForwardingSupportLinkName)) {
257 QList<SharedLinkInterfacePtr>
links;
259 QMutexLocker locker(&_linksMutex);
264 sharedLink->disconnect();
268void LinkManager::_linkDisconnected()
270 LinkInterface*
const link = qobject_cast<LinkInterface*>(sender());
279 QMutexLocker locker(&_linksMutex);
281 for (
auto it = _rgLinks.begin(); it != _rgLinks.end(); ++it) {
282 if (it->get() == link) {
283 config = it->get()->linkConfiguration();
284 const QString linkName =
config ?
config->name() : QStringLiteral(
"<null config>");
285 qCDebug(LinkManagerLog) << linkName <<
"use_count:" << it->use_count();
287 (void) _rgLinks.erase(it);
293 if (!linkToCleanup) {
294 qCDebug(LinkManagerLog) <<
"link already removed";
299 config->noteDisconnected();
314 QMutexLocker locker(&_linksMutex);
317 if (sharedLink.get() == link) {
324 qCDebug(LinkManagerLog) <<
"link not in list (likely disconnected)";
328bool LinkManager::_connectionsSuspendedMsg()
const
330 if (_connectionsSuspended) {
344 for (
int i = 0; i < _rgLinkConfigs.count(); i++) {
347 qCWarning(LinkManagerLog) <<
"Internal error for link configuration in LinkManager";
351 if (linkConfig->isDynamic()) {
356 settings.setValue(root +
"/name", linkConfig->name());
357 settings.setValue(root +
"/type", linkConfig->type());
358 settings.setValue(root +
"/auto", linkConfig->isAutoConnect());
359 settings.setValue(root +
"/high_latency", linkConfig->isHighLatency());
360 linkConfig->saveSettings(settings, root);
364 settings.setValue(root +
"/count", trueCount);
374 for (
int i = 0; i < count; i++) {
376 if (!settings.contains(root +
"/type")) {
377 qCWarning(LinkManagerLog) <<
"Link Configuration" << root <<
"has no type.";
383 qCWarning(LinkManagerLog) <<
"Link Configuration" << root <<
"an invalid type:" << type;
387 if (!settings.contains(root +
"/name")) {
388 qCWarning(LinkManagerLog) <<
"Link Configuration" << root <<
"has no name.";
392 const QString name = settings.value(root +
"/name").toString();
393 if (name.isEmpty()) {
394 qCWarning(LinkManagerLog) <<
"Link Configuration" << root <<
"has an empty name.";
400#ifndef QGC_NO_SERIAL_LINK
418 case LinkConfiguration::TypeMock:
428 const bool autoConnect = settings.value(root +
"/auto").toBool();
430 const bool highLatency = settings.value(root +
"/high_latency").toBool();
439 _configurationsLoaded =
true;
442void LinkManager::_addUDPAutoConnectLink()
444 if (!_autoConnectSettings->autoConnectUDP()->rawValue().toBool()) {
449 QMutexLocker locker(&_linksMutex);
458 qCDebug(LinkManagerLog) <<
"New auto-connect UDP port added";
466void LinkManager::_addMAVLinkForwardingLink()
473 QMutexLocker locker(&_linksMutex);
484 _createDynamicForwardLink(_mavlinkForwardingLinkName, hostName);
487void LinkManager::_reconnectAutoConnectLinks()
496 if (
config->link() ||
config->suppressAutoReconnect() || !
config->autoConnectStarted()) {
501 if (!
config->reconnectReady()) {
505 qCDebug(LinkManagerLog) <<
"Reconnecting auto-connect link" <<
config->name();
506 config->noteReconnectAttempt();
511void LinkManager::_updateAutoConnectLinks()
513 if (_connectionsSuspended) {
517 _addUDPAutoConnectLink();
518 _addMAVLinkForwardingLink();
519 _reconnectAutoConnectLinks();
521 const int nmeaSource = _autoConnectSettings->nmeaSource()->rawValue().toInt();
523 if ((_nmeaSocket->localPort() != _autoConnectSettings->nmeaUdpPort()->rawValue().toUInt()) || (_nmeaSocket->state() != UdpIODevice::BoundState)) {
524 qCDebug(LinkManagerLog) <<
"Changing port for UDP NMEA stream";
525 _nmeaSocket->close();
526 _nmeaSocket->bind(QHostAddress::AnyIPv4, _autoConnectSettings->nmeaUdpPort()->rawValue().toUInt());
530 _nmeaSocket->close();
539#ifndef QGC_NO_SERIAL_LINK
545 _nmeaDeviceName =
"";
548 _addSerialAutoConnectLink();
559 QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
566 static QStringList list;
567 if (!list.isEmpty()) {
571#ifndef QGC_NO_SERIAL_LINK
572 list += tr(
"Serial");
576 list += tr(
"Bluetooth");
578 list += tr(
"Mock Link");
580 list += tr(
"Log Replay");
583 qCWarning(LinkManagerLog) <<
"Internal error";
591 if (!
config || !editedConfig) {
592 qCWarning(LinkManagerLog) <<
"Internal error";
596 config->copyFrom(editedConfig);
606 qCWarning(LinkManagerLog) <<
"Internal error";
616#ifndef QGC_NO_SERIAL_LINK
618 _updateSerialPorts();
628 qCWarning(LinkManagerLog) <<
"Internal error";
632#ifndef QGC_NO_SERIAL_LINK
634 _updateSerialPorts();
644 qCWarning(LinkManagerLog) <<
"Internal error";
653 _removeConfiguration(
config);
660 _createDynamicForwardLink(_mavlinkForwardingSupportLinkName, hostName);
661 _mavlinkSupportForwardingEnabled =
true;
669 for (
auto it = _rgLinkConfigs.begin(); it != _rgLinkConfigs.end(); ++it) {
670 if (it->get() ==
config) {
671 (void) _rgLinkConfigs.erase(it);
676 qCWarning(LinkManagerLog) <<
"called with unknown config";
686 QMutexLocker locker(&_linksMutex);
689 if (sharedLink.get() == link) {
702 return _rgLinkConfigs.last();
708 if (sharedConfig->isAutoConnect()) {
709 sharedConfig->setAutoConnectStarted(
true);
718 if (_mavlinkChannelsUsedBitMask & (1 << mavlinkChannel)) {
722 mavlink_reset_channel_status(mavlinkChannel);
724 mavlinkStatus->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
725 _mavlinkChannelsUsedBitMask |= (1 << mavlinkChannel);
726 qCDebug(LinkManagerLog) <<
"allocateMavlinkChannel" << mavlinkChannel;
727 return mavlinkChannel;
730 qCWarning(LinkManagerLog) <<
"allocateMavlinkChannel: all channels reserved!";
736 qCDebug(LinkManagerLog) <<
"freeMavlinkChannel" << channel;
742 _mavlinkChannelsUsedBitMask &= ~(1 << channel);
753 return qobject_cast<LogReplayLink*>(sharedConfig->link());
759void LinkManager::_createDynamicForwardLink(
const char *linkName,
const QString &hostName)
770 qCDebug(LinkManagerLog) <<
"New dynamic MAVLink forwarding port added:" << linkName <<
" hostname:" << hostName;
775#ifndef QGC_NO_SERIAL_LINK
776 const SerialLink*
const serialLink = qobject_cast<const SerialLink*>(link);
787 if (serialConfig && serialConfig->
usbDirect()) {
795#ifndef QGC_NO_SERIAL_LINK
797void LinkManager::_filterCompositePorts(QList<QGCSerialPortInfo> &portList)
799 typedef QPair<quint16, quint16> VidPidPair_t;
801 QMap<VidPidPair_t, QStringList> seenSerialNumbers;
803 for (
auto it = portList.begin(); it != portList.end();) {
807 if (seenSerialNumbers.contains(vidPid) && seenSerialNumbers[vidPid].contains(portInfo.
serialNumber())) {
812 it = portList.erase(it);
816 seenSerialNumbers[vidPid].append(portInfo.
serialNumber());
822void LinkManager::_addSerialAutoConnectLink()
824 QList<QGCSerialPortInfo> portList;
837 _filterCompositePorts(portList);
839 QStringList currentPorts;
841 qCDebug(LinkManagerVerboseLog) <<
"-----------------------------------------------------";
842 qCDebug(LinkManagerVerboseLog) <<
"portName: " << portInfo.
portName();
843 qCDebug(LinkManagerVerboseLog) <<
"systemLocation: " << portInfo.
systemLocation();
844 qCDebug(LinkManagerVerboseLog) <<
"description: " << portInfo.
description();
845 qCDebug(LinkManagerVerboseLog) <<
"manufacturer: " << portInfo.
manufacturer();
846 qCDebug(LinkManagerVerboseLog) <<
"serialNumber: " << portInfo.
serialNumber();
847 qCDebug(LinkManagerVerboseLog) <<
"vendorIdentifier: " << portInfo.
vendorIdentifier();
848 qCDebug(LinkManagerVerboseLog) <<
"productIdentifier: " << portInfo.
productIdentifier();
857 (portInfo.
systemLocation().trimmed() == _autoConnectSettings->autoConnectNmeaPort()->cookedValueString())) {
860 qCDebug(LinkManagerLog) <<
"Configuring nmea port" << _nmeaDeviceName;
862 _nmeaBaud = _autoConnectSettings->autoConnectNmeaBaud()->cookedValue().toUInt();
863 newPort->
setBaudRate(
static_cast<qint32
>(_nmeaBaud));
864 qCDebug(LinkManagerLog) <<
"Configuring nmea baudrate" << _nmeaBaud;
871 }
else if (_autoConnectSettings->autoConnectNmeaBaud()->cookedValue().toUInt() != _nmeaBaud) {
872 _nmeaBaud = _autoConnectSettings->autoConnectNmeaBaud()->cookedValue().toUInt();
873 _nmeaPort->
setBaudRate(
static_cast<qint32
>(_nmeaBaud));
874 qCDebug(LinkManagerLog) <<
"Configuring nmea baudrate" << _nmeaBaud;
876 }
else if (portInfo.
getBoardInfo(boardType, boardName)) {
878 if (!_allowAutoConnectToBoard(boardType)) {
884 qCDebug(LinkManagerLog) <<
"Waiting for bootloader to finish" << portInfo.
systemLocation();
888 qCDebug(LinkManagerVerboseLog) <<
"Skipping existing autoconnect" << portInfo.
systemLocation();
889 }
else if (!_autoconnectPortWaitList.contains(portInfo.
systemLocation())) {
893 qCDebug(LinkManagerLog) <<
"Waiting for next autoconnect pass" << portInfo.
systemLocation() << boardName;
895 }
else if ((++_autoconnectPortWaitList[portInfo.
systemLocation()] * _autoconnectUpdateTimerMSecs) > _autoconnectConnectDelayMSecs) {
910 qCDebug(LinkManagerLog) <<
"RTK GPS auto-connected" << portInfo.
portName().trimmed();
915 qCWarning(LinkManagerLog) <<
"Internal error: Unknown board type" << boardType;
920 qCDebug(LinkManagerLog) <<
"New auto-connect port added: " << pSerialConfig->
name() << portInfo.
systemLocation();
934 if (!_autoConnectRTKPort.isEmpty() && !currentPorts.contains(_autoConnectRTKPort)) {
935 qCDebug(LinkManagerLog) <<
"RTK GPS disconnected" << _autoConnectRTKPort;
937 _autoConnectRTKPort.clear();
945 if (_autoConnectSettings->autoConnectPixhawk()->rawValue().toBool()) {
950 if (_autoConnectSettings->autoConnectSiKRadio()->rawValue().toBool()) {
955 if (_autoConnectSettings->autoConnectLibrePilot()->rawValue().toBool()) {
965 qCWarning(LinkManagerLog) <<
"Internal error: Unknown board type" << boardType;
972bool LinkManager::_portAlreadyConnected(
const QString &portName)
974 QMutexLocker locker(&_linksMutex);
976 const QString searchPort = portName.trimmed();
979 const SerialConfiguration*
const serialConfig = qobject_cast<const SerialConfiguration*>(linkConfig.get());
980 if (serialConfig && (serialConfig->
portName() == searchPort)) {
988void LinkManager::_updateSerialPorts()
990 _commPortList.clear();
991 _commPortDisplayList.clear();
994 const QString port = info.systemLocation().trimmed();
995 _commPortList += port;
1002 if (_commPortDisplayList.isEmpty()) {
1003 _updateSerialPorts();
1006 return _commPortDisplayList;
1011 if (_commPortList.isEmpty()) {
1012 _updateSerialPorts();
1015 return _commPortList;
1023bool LinkManager::_isSerialPortConnected()
1025 QMutexLocker locker(&_linksMutex);
1028 if (qobject_cast<const SerialLink*>(link.get())) {
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)
static GPSManager * instance()
void connectGPS(const QString &device, QStringView gps_type)
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)
virtual 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...
Q_INVOKABLE void createConnectedLink(const LinkConfiguration *config)
This should only be used by Qml code.
SharedLinkInterfacePtr sharedLinkInterfacePointerForLink(const LinkInterface *link)
QStringList serialPorts()
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 resetNmeaSourceDevice()
static QGCPositionManager * instance()
void setNmeaSourceDevice(QIODevice *device)
QGC's version of Qt QSerialPortInfo. It provides additional information about board types that QGC ca...
bool isBootloader() const
bool getBoardInfo(BoardType_t &boardType, QString &name) const
static QList< QGCSerialPortInfo > availablePorts()
Override of QSerialPortInfo::availablePorts.
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.
QString portName() const
Returns the name of the serial port.
bool hasVendorIdentifier() const
Returns true if there is a valid 16-bit vendor number present; otherwise returns false.
QString serialNumber() const
QString systemLocation() const
Returns the system location of the serial port.
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.
bool hasProductIdentifier() const
Returns true if there is a valid 16-bit product number present; otherwise returns false.
Provides functions to access serial ports.
void close() override
\reimp
bool setBaudRate(qint32 baudRate, Directions directions=AllDirections)
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
QObject * removeOne(const QObject *object) override final
void setBaud(qint32 baud)
static QStringList supportedBaudRates()
void setUsbDirect(bool usbDirect)
static QString cleanPortDisplayName(const QString &name)
void setPortName(const QString &name)
AutoConnectSettings * autoConnectSettings() const
static SettingsManager * instance()
MavlinkSettings * mavlinkSettings() const
void setAutoConnect(bool autoc=true) override
Set if this is this an Auto Connect configuration.
Q_INVOKABLE void addHost(const QString &host)
UdpIODevice provides a QIODevice interface over a QUdpSocket in server mode.
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.