Newer
Older
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#include <QDebug>
#include "SettingsManager.h"
QGC_LOGGING_CATEGORY(LinkManagerLog, "LinkManagerLog")
QGC_LOGGING_CATEGORY(LinkManagerVerboseLog, "LinkManagerVerboseLog")
const char* LinkManager::_defaultUDPLinkName = "UDP Link (AutoConnect)";
const char* LinkManager::_mavlinkForwardingLinkName = "MAVLink Forwarding Link";
dogmaphobic
committed
const int LinkManager::_autoconnectUpdateTimerMSecs = 1000;
#ifdef Q_OS_WIN
// Have to manually let the bootloader go by on Windows to get a working connect
const int LinkManager::_autoconnectConnectDelayMSecs = 6000;
#else
const int LinkManager::_autoconnectConnectDelayMSecs = 1000;
#endif
LinkManager::LinkManager(QGCApplication* app, QGCToolbox* toolbox)
: QGCTool(app, toolbox)
dogmaphobic
committed
, _configUpdateSuspended(false)
, _configurationsLoaded(false)
, _connectionsSuspended(false)
, _mavlinkChannelsUsedBitMask(1) // We never use channel 0 to avoid sequence numbering problems
, _autoConnectSettings(nullptr)
, _mavlinkProtocol(nullptr)
#ifndef NO_SERIAL_LINK
qmlRegisterUncreatableType<LinkManager> ("QGroundControl", 1, 0, "LinkManager", "Reference only");
qmlRegisterUncreatableType<LinkConfiguration> ("QGroundControl", 1, 0, "LinkConfiguration", "Reference only");
qmlRegisterUncreatableType<LinkInterface> ("QGroundControl", 1, 0, "LinkInterface", "Reference only");
_activeLinkCheckTimer.setInterval(_activeLinkCheckTimeoutMSecs);
_activeLinkCheckTimer.setSingleShot(false);
connect(&_activeLinkCheckTimer, &QTimer::timeout, this, &LinkManager::_activeLinkCheck);
#ifndef NO_SERIAL_LINK
void LinkManager::setToolbox(QGCToolbox *toolbox)
{
_autoConnectSettings = toolbox->settingsManager()->autoConnectSettings();
_mavlinkProtocol = _toolbox->mavlinkProtocol();
acfloria
committed
connect(_mavlinkProtocol, &MAVLinkProtocol::messageReceived, this, &LinkManager::_mavlinkMessageReceived);
acfloria
committed
connect(&_portListTimer, &QTimer::timeout, this, &LinkManager::_updateAutoConnectLinks);
_portListTimer.start(_autoconnectUpdateTimerMSecs); // timeout must be long enough to get past bootloader on second pass
// This should only be used by Qml code
void LinkManager::createConnectedLink(LinkConfiguration* config)
{
for(int i = 0; i < _sharedConfigurations.count(); i++) {
SharedLinkConfigurationPointer& sharedConf = _sharedConfigurations[i];
if (sharedConf->name() == config->name())
createConnectedLink(sharedConf);
}
}
LinkInterface* LinkManager::createConnectedLink(SharedLinkConfigurationPointer& config, bool isPX4Flow)
dogmaphobic
committed
{
qWarning() << "LinkManager::createConnectedLink called with nullptr config";
return nullptr;
dogmaphobic
committed
switch(config->type()) {
auto* serialConfig = qobject_cast<SerialConfiguration*>(config.data());
pLink = new SerialLink(config, isPX4Flow);
_activeLinkCheckList.append(qobject_cast<SerialLink*>(pLink));
if (!_activeLinkCheckTimer.isActive()) {
_activeLinkCheckTimer.start();
case LinkConfiguration::TypeUdp:
pLink = new UDPLink(config);
break;
case LinkConfiguration::TypeTcp:
pLink = new TCPLink(config);
break;
case LinkConfiguration::TypeBluetooth:
pLink = new BluetoothLink(config);
break;
case LinkConfiguration::TypeLogReplay:
pLink = new LogReplayLink(config);
break;
case LinkConfiguration::TypeMock:
pLink = new MockLink(config);
break;
dogmaphobic
committed
}
_addLink(pLink);
connectLink(pLink);
dogmaphobic
committed
}
return pLink;
}
LinkInterface* LinkManager::createConnectedLink(const QString& name)
dogmaphobic
committed
{
if (name.isEmpty()) {
qWarning() << "Internal error";
} else {
for(int i = 0; i < _sharedConfigurations.count(); i++) {
SharedLinkConfigurationPointer& conf = _sharedConfigurations[i];
if (conf->name() == name) {
return createConnectedLink(conf);
}
}
dogmaphobic
committed
}
dogmaphobic
committed
}
SharedLinkInterfacePointer LinkManager::mavlinkForwardingLink()
{
for (int i = 0; i < _sharedLinks.count(); i++) {
LinkConfiguration* linkConfig = _sharedLinks[i]->getLinkConfiguration();
if (linkConfig->type() == LinkConfiguration::TypeUdp && linkConfig->name() == _mavlinkForwardingLinkName) {
SharedLinkInterfacePointer& link = _sharedLinks[i];
return link;
}
}
return nullptr;
}
void LinkManager::_addLink(LinkInterface* link)
qWarning() << "_addLink called from incorrect thread";
dogmaphobic
committed
dogmaphobic
committed
if (!containsLink(link)) {
int mavlinkChannel = _reserveMavlinkChannel();
if (mavlinkChannel != 0) {
link->_setMavlinkChannel(mavlinkChannel);
} else {
qWarning() << "Ran out of mavlink channels";
_sharedLinks.append(SharedLinkInterfacePointer(link));
emit newLink(link);
}
dogmaphobic
committed
connect(link, &LinkInterface::communicationError, _app, &QGCApplication::criticalMessageBoxOnMainThread);
connect(link, &LinkInterface::bytesReceived, _mavlinkProtocol, &MAVLinkProtocol::receiveBytes);
connect(link, &LinkInterface::bytesSent, _mavlinkProtocol, &MAVLinkProtocol::logSentBytes);
dogmaphobic
committed
_mavlinkProtocol->resetMetadataForLink(link);
Lorenz Meier
committed
_mavlinkProtocol->setVersion(_mavlinkProtocol->getCurrentVersion());
dogmaphobic
committed
connect(link, &LinkInterface::connected, this, &LinkManager::_linkConnected);
connect(link, &LinkInterface::disconnected, this, &LinkManager::_linkDisconnected);
// This connection is queued since it will cloe the link. So we want the link emitter to return otherwise we would
// close the link our from under itself.
connect(link, &LinkInterface::connectionRemoved, this, &LinkManager::_linkConnectionRemoved, Qt::QueuedConnection);
}
// Walk list in reverse order to preserve indices during delete
for (int i=_sharedLinks.count()-1; i>=0; i--) {
disconnectLink(_sharedLinks[i].data());
}
bool LinkManager::connectLink(LinkInterface* link)
{
if (link) {
if (_connectionsSuspendedMsg()) {
return false;
}
return link->_connect();
} else {
qWarning() << "Internal error";
void LinkManager::disconnectLink(LinkInterface* link)
if (!link || !containsLink(link)) {
LinkConfiguration* config = link->getLinkConfiguration();
for (int i=0; i<_sharedAutoconnectConfigurations.count(); i++) {
if (_sharedAutoconnectConfigurations[i].data() == config) {
qCDebug(LinkManagerLog) << "Removing disconnected autoconnect config" << config->name();
_sharedAutoconnectConfigurations.removeAt(i);
break;
void LinkManager::_deleteLink(LinkInterface* link)
if (thread() != QThread::currentThread()) {
qWarning() << "_deleteLink called from incorrect thread";
return;
}
if (!link) {
return;
}
dogmaphobic
committed
// Free up the mavlink channel associated with this link
dogmaphobic
committed
for (int i=0; i<_sharedLinks.count(); i++) {
if (_sharedLinks[i].data() == link) {
_sharedLinks.removeAt(i);
break;
}
}
dogmaphobic
committed
SharedLinkInterfacePointer LinkManager::sharedLinkInterfacePointerForLink(LinkInterface* link)
{
for (int i=0; i<_sharedLinks.count(); i++) {
if (_sharedLinks[i].data() == link) {
return _sharedLinks[i];
}
}
qWarning() << "LinkManager::sharedLinkInterfaceForLink returning nullptr";
return SharedLinkInterfacePointer(nullptr);
/// @brief If all new connections should be suspended a message is displayed to the user and true
/// is returned;
bool LinkManager::_connectionsSuspendedMsg(void)
{
if (_connectionsSuspended) {
qgcApp()->showAppMessage(tr("Connect not allowed: %1").arg(_connectionsSuspendedReason));
return true;
} else {
return false;
}
}
void LinkManager::setConnectionsSuspended(QString reason)
{
_connectionsSuspended = true;
_connectionsSuspendedReason = reason;
}
void LinkManager::_linkConnected(void)
{
emit linkConnected((LinkInterface*)sender());
}
void LinkManager::_linkDisconnected(void)
{
emit linkDisconnected((LinkInterface*)sender());
}
dogmaphobic
committed
void LinkManager::_linkConnectionRemoved(LinkInterface* link)
{
// Link has been removed from system, disconnect it automatically
disconnectLink(link);
}
dogmaphobic
committed
void LinkManager::suspendConfigurationUpdates(bool suspend)
{
_configUpdateSuspended = suspend;
}
void LinkManager::saveLinkConfigurationList()
{
QSettings settings;
settings.remove(LinkConfiguration::settingsRoot());
for (int i = 0; i < _sharedConfigurations.count(); i++) {
SharedLinkConfigurationPointer linkConfig = _sharedConfigurations[i];
if (!linkConfig->isDynamic()) {
root += QString("/Link%1").arg(trueCount++);
settings.setValue(root + "/name", linkConfig->name());
settings.setValue(root + "/type", linkConfig->type());
settings.setValue(root + "/auto", linkConfig->isAutoConnect());
settings.setValue(root + "/high_latency", linkConfig->isHighLatency());
// Have the instance save its own values
linkConfig->saveSettings(settings, root);
}
} else {
qWarning() << "Internal error for link configuration in LinkManager";
dogmaphobic
committed
}
settings.setValue(root + "/count", trueCount);
emit linkConfigurationsChanged();
dogmaphobic
committed
}
void LinkManager::loadLinkConfigurationList()
{
dogmaphobic
committed
QSettings settings;
// Is the group even there?
if(settings.contains(LinkConfiguration::settingsRoot() + "/count")) {
// Find out how many configurations we have
int count = settings.value(LinkConfiguration::settingsRoot() + "/count").toInt();
for(int i = 0; i < count; i++) {
QString root(LinkConfiguration::settingsRoot());
root += QString("/Link%1").arg(i);
if(settings.contains(root + "/type")) {
LinkConfiguration::LinkType type = static_cast<LinkConfiguration::LinkType>(settings.value(root + "/type").toInt());
if(type < LinkConfiguration::TypeLast) {
dogmaphobic
committed
if(settings.contains(root + "/name")) {
QString name = settings.value(root + "/name").toString();
if(!name.isEmpty()) {
bool autoConnect = settings.value(root + "/auto").toBool();
bool highLatency = settings.value(root + "/high_latency").toBool();
pLink = new SerialConfiguration(name);
pLink = new UDPConfiguration(name);
pLink = new TCPConfiguration(name);
pLink = new BluetoothConfiguration(name);
pLink = new LogReplayLinkConfiguration(name);
pLink = new MockConfiguration(name);
dogmaphobic
committed
}
if(pLink) {
//-- Have the instance load its own values
pLink->setAutoConnect(autoConnect);
pLink->setHighLatency(highLatency);
dogmaphobic
committed
pLink->loadSettings(settings, root);
addConfiguration(pLink);
dogmaphobic
committed
}
} else {
qWarning() << "Link Configuration" << root << "has an empty name." ;
dogmaphobic
committed
}
} else {
qWarning() << "Link Configuration" << root << "has no name." ;
dogmaphobic
committed
}
} else {
qWarning() << "Link Configuration" << root << "an invalid type: " << type;
dogmaphobic
committed
}
} else {
qWarning() << "Link Configuration" << root << "has no type." ;
dogmaphobic
committed
}
}
}
}
// Enable automatic Serial PX4/3DR Radio hunting
dogmaphobic
committed
_configurationsLoaded = true;
}
SerialConfiguration* LinkManager::_autoconnectConfigurationsContainsPort(const QString& portName)
dogmaphobic
committed
{
QString searchPort = portName.trimmed();
for (int i=0; i<_sharedAutoconnectConfigurations.count(); i++) {
SerialConfiguration* serialConfig = qobject_cast<SerialConfiguration*>(_sharedAutoconnectConfigurations[i].data());
if (serialConfig) {
if (serialConfig->portName() == searchPort) {
return serialConfig;
dogmaphobic
committed
}
dogmaphobic
committed
}
}
dogmaphobic
committed
}
dogmaphobic
committed
dogmaphobic
committed
{
if (_connectionsSuspended || qgcApp()->runningUnitTests()) {
dogmaphobic
committed
return;
}
// Re-add UDP if we need to
bool foundUDP = false;
for (int i = 0; i < _sharedLinks.count(); i++) {
LinkConfiguration* linkConfig = _sharedLinks[i]->getLinkConfiguration();
if (linkConfig->type() == LinkConfiguration::TypeUdp && linkConfig->name() == _defaultUDPLinkName) {
if (!foundUDP && _autoConnectSettings->autoConnectUDP()->rawValue().toBool()) {
//-- Default UDPConfiguration is set up for autoconnect
UDPConfiguration* udpConfig = new UDPConfiguration(_defaultUDPLinkName);
SharedLinkConfigurationPointer config = addConfiguration(udpConfig);
createConnectedLink(config);
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
// Connect MAVLink forwarding if it is enabled
bool foundMAVLinkForwardingLink = false;
for (int i = 0; i < _sharedLinks.count(); i++) {
LinkConfiguration* linkConfig = _sharedLinks[i]->getLinkConfiguration();
if (linkConfig->type() == LinkConfiguration::TypeUdp && linkConfig->name() == _mavlinkForwardingLinkName) {
foundMAVLinkForwardingLink = true;
// TODO: should we check if the host/port matches the mavlinkForwardHostName setting and update if it does not match?
break;
}
}
// Create the link if necessary
bool forwardingEnabled = _toolbox->settingsManager()->appSettings()->forwardMavlink()->rawValue().toBool();
if (!foundMAVLinkForwardingLink && forwardingEnabled) {
qCDebug(LinkManagerLog) << "New MAVLink forwarding port added";
UDPConfiguration* udpConfig = new UDPConfiguration(_mavlinkForwardingLinkName);
udpConfig->setDynamic(true);
QString hostName = _toolbox->settingsManager()->appSettings()->forwardMavlinkHostName()->rawValue().toString();
udpConfig->addHost(hostName);
SharedLinkConfigurationPointer config = addConfiguration(udpConfig);
createConnectedLink(config);
emit linkConfigurationsChanged();
}
#ifndef __mobile__
// check to see if nmea gps is configured for UDP input, if so, set it up to connect
if (_autoConnectSettings->autoConnectNmeaPort()->cookedValueString() == "UDP Port") {
if (_nmeaSocket.localPort() != _autoConnectSettings->nmeaUdpPort()->rawValue().toUInt()
|| _nmeaSocket.state() != UdpIODevice::BoundState) {
qCDebug(LinkManagerLog) << "Changing port for UDP NMEA stream";
_nmeaSocket.close();
_nmeaSocket.bind(QHostAddress::AnyIPv4, _autoConnectSettings->nmeaUdpPort()->rawValue().toUInt());
_toolbox->qgcPositionManager()->setNmeaSourceDevice(&_nmeaSocket);
}
//close serial port
if (_nmeaPort) {
_nmeaPort->close();
delete _nmeaPort;
_nmeaPort = nullptr;
_nmeaDeviceName = "";
}
} else {
_nmeaSocket.close();
}
#endif
QList<QGCSerialPortInfo> portList;
#ifdef __android__
// Android builds only support a single serial connection. Repeatedly calling availablePorts after that one serial
// port is connected leaks file handles due to a bug somewhere in android serial code. In order to work around that
// bug after we connect the first serial port we stop probing for additional ports.
if (!_sharedAutoconnectConfigurations.count()) {
portList = QGCSerialPortInfo::availablePorts();
}
#else
portList = QGCSerialPortInfo::availablePorts();
dogmaphobic
committed
// Iterate Comm Ports
for (const QGCSerialPortInfo& portInfo: portList) {
qCDebug(LinkManagerVerboseLog) << "-----------------------------------------------------";
qCDebug(LinkManagerVerboseLog) << "portName: " << portInfo.portName();
qCDebug(LinkManagerVerboseLog) << "systemLocation: " << portInfo.systemLocation();
qCDebug(LinkManagerVerboseLog) << "description: " << portInfo.description();
qCDebug(LinkManagerVerboseLog) << "manufacturer: " << portInfo.manufacturer();
qCDebug(LinkManagerVerboseLog) << "serialNumber: " << portInfo.serialNumber();
qCDebug(LinkManagerVerboseLog) << "vendorIdentifier: " << portInfo.vendorIdentifier();
qCDebug(LinkManagerVerboseLog) << "productIdentifier: " << portInfo.productIdentifier();
// Save port name
currentPorts << portInfo.systemLocation();
QGCSerialPortInfo::BoardType_t boardType;
QString boardName;
#ifndef NO_SERIAL_LINK
// check to see if nmea gps is configured for current Serial port, if so, set it up to connect
if (portInfo.systemLocation().trimmed() == _autoConnectSettings->autoConnectNmeaPort()->cookedValueString()) {
if (portInfo.systemLocation().trimmed() != _nmeaDeviceName) {
_nmeaDeviceName = portInfo.systemLocation().trimmed();
qCDebug(LinkManagerLog) << "Configuring nmea port" << _nmeaDeviceName;
QSerialPort* newPort = new QSerialPort(portInfo);
_nmeaBaud = _autoConnectSettings->autoConnectNmeaBaud()->cookedValue().toUInt();
newPort->setBaudRate(static_cast<qint32>(_nmeaBaud));
qCDebug(LinkManagerLog) << "Configuring nmea baudrate" << _nmeaBaud;
// This will stop polling old device if previously set
_toolbox->qgcPositionManager()->setNmeaSourceDevice(newPort);
if (_nmeaPort) {
delete _nmeaPort;
}
_nmeaPort = newPort;
} else if (_autoConnectSettings->autoConnectNmeaBaud()->cookedValue().toUInt() != _nmeaBaud) {
_nmeaBaud = _autoConnectSettings->autoConnectNmeaBaud()->cookedValue().toUInt();
_nmeaPort->setBaudRate(static_cast<qint32>(_nmeaBaud));
qCDebug(LinkManagerLog) << "Configuring nmea baudrate" << _nmeaBaud;
}
#endif
if (portInfo.getBoardInfo(boardType, boardName)) {
if (portInfo.isBootloader()) {
// Don't connect to bootloader
qCDebug(LinkManagerLog) << "Waiting for bootloader to finish" << portInfo.systemLocation();
if (_autoconnectConfigurationsContainsPort(portInfo.systemLocation()) || _autoConnectRTKPort == portInfo.systemLocation()) {
qCDebug(LinkManagerVerboseLog) << "Skipping existing autoconnect" << portInfo.systemLocation();
} else if (!_autoconnectWaitList.contains(portInfo.systemLocation())) {
// We don't connect to the port the first time we see it. The ability to correctly detect whether we
// are in the bootloader is flaky from a cross-platform standpoint. So by putting it on a wait list
// and only connect on the second pass we leave enough time for the board to boot up.
qCDebug(LinkManagerLog) << "Waiting for next autoconnect pass" << portInfo.systemLocation();
_autoconnectWaitList[portInfo.systemLocation()] = 1;
} else if (++_autoconnectWaitList[portInfo.systemLocation()] * _autoconnectUpdateTimerMSecs > _autoconnectConnectDelayMSecs) {
SerialConfiguration* pSerialConfig = nullptr;
_autoconnectWaitList.remove(portInfo.systemLocation());
case QGCSerialPortInfo::BoardTypePixhawk:
if (_autoConnectSettings->autoConnectPixhawk()->rawValue().toBool()) {
pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName).arg(portInfo.portName().trimmed()));
pSerialConfig->setUsbDirect(true);
}
break;
if (_autoConnectSettings->autoConnectPX4Flow()->rawValue().toBool()) {
pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName).arg(portInfo.portName().trimmed()));
case QGCSerialPortInfo::BoardTypeSiKRadio:
if (_autoConnectSettings->autoConnectSiKRadio()->rawValue().toBool()) {
pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName).arg(portInfo.portName().trimmed()));
case QGCSerialPortInfo::BoardTypeOpenPilot:
if (_autoConnectSettings->autoConnectLibrePilot()->rawValue().toBool()) {
pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName).arg(portInfo.portName().trimmed()));
}
break;
#ifndef __mobile__
case QGCSerialPortInfo::BoardTypeRTKGPS:
if (_autoConnectSettings->autoConnectRTKGPS()->rawValue().toBool() && !_toolbox->gpsManager()->connected()) {
qCDebug(LinkManagerLog) << "RTK GPS auto-connected" << portInfo.portName().trimmed();
_autoConnectRTKPort = portInfo.systemLocation();
_toolbox->gpsManager()->connectGPS(portInfo.systemLocation(), boardName);
if (pSerialConfig) {
qCDebug(LinkManagerLog) << "New auto-connect port added: " << pSerialConfig->name() << portInfo.systemLocation();
pSerialConfig->setBaud(boardType == QGCSerialPortInfo::BoardTypeSiKRadio ? 57600 : 115200);
pSerialConfig->setDynamic(true);
pSerialConfig->setPortName(portInfo.systemLocation());
_sharedAutoconnectConfigurations.append(SharedLinkConfigurationPointer(pSerialConfig));
createConnectedLink(_sharedAutoconnectConfigurations.last(), boardType == QGCSerialPortInfo::BoardTypePX4Flow);
#ifndef __android__
// Android builds only support a single serial connection. Repeatedly calling availablePorts after that one serial
// port is connected leaks file handles due to a bug somewhere in android serial code. In order to work around that
// bug after we connect the first serial port we stop probing for additional ports. That means we must rely on
// the port disconnecting itself when the radio is pulled to signal communication list as opposed to automatically
// closing the Link.
// Now we go through the current configuration list and make sure any dynamic config has gone away
QList<LinkConfiguration*> _confToDelete;
for (int i=0; i<_sharedAutoconnectConfigurations.count(); i++) {
SerialConfiguration* serialConfig = qobject_cast<SerialConfiguration*>(_sharedAutoconnectConfigurations[i].data());
if (serialConfig) {
if (!currentPorts.contains(serialConfig->portName())) {
if (serialConfig->link()) {
if (serialConfig->link()->isConnected()) {
if (serialConfig->link()->active()) {
// We don't remove links which are still connected which have been active with a vehicle on them
// even though at this point the cable may have been pulled. Instead we wait for the user to
// Disconnect. Once the user disconnects, the link will be removed.
continue;
}
}
_confToDelete.append(serialConfig);
for (LinkConfiguration* pDeleteConfig: _confToDelete) {
qCDebug(LinkManagerLog) << "Removing unused autoconnect config" << pDeleteConfig->name();
if (pDeleteConfig->link()) {
disconnectLink(pDeleteConfig->link());
}
for (int i=0; i<_sharedAutoconnectConfigurations.count(); i++) {
if (_sharedAutoconnectConfigurations[i].data() == pDeleteConfig) {
_sharedAutoconnectConfigurations.removeAt(i);
break;
}
}
dogmaphobic
committed
}
// Check for RTK GPS connection gone
#if !defined(__mobile__)
if (!_autoConnectRTKPort.isEmpty() && !currentPorts.contains(_autoConnectRTKPort)) {
qCDebug(LinkManagerLog) << "RTK GPS disconnected" << _autoConnectRTKPort;
_toolbox->gpsManager()->disconnectGPS();
_autoConnectRTKPort.clear();
}
dogmaphobic
committed
}
QStringList LinkManager::linkTypeStrings(void) const
{
//-- Must follow same order as enum LinkType in LinkConfiguration.h
static QStringList list;
if(!list.size())
{
list += tr("Serial");
#endif
list += tr("UDP");
list += tr("TCP");
if (list.size() != static_cast<int>(LinkConfiguration::TypeLast)) {
void LinkManager::_updateSerialPorts()
_commPortList.clear();
_commPortDisplayList.clear();
QList<QSerialPortInfo> portList = QSerialPortInfo::availablePorts();
for (const QSerialPortInfo &info: portList)
QString port = info.systemLocation().trimmed();
_commPortList += port;
_commPortDisplayList += SerialConfiguration::cleanPortDisplayname(port);
}
QStringList LinkManager::serialPortStrings(void)
{
if(!_commPortDisplayList.size())
{
_updateSerialPorts();
}
return _commPortDisplayList;
}
QStringList LinkManager::serialPorts(void)
{
if(!_commPortList.size())
{
_updateSerialPorts();
}
return _commPortList;
}
QStringList LinkManager::serialBaudRates(void)
{
QStringList foo;
return foo;
#else
return SerialConfiguration::supportedBaudRates();
#endif
}
bool LinkManager::endConfigurationEditing(LinkConfiguration* config, LinkConfiguration* editedConfig)
{
if (config && editedConfig) {
_fixUnnamed(editedConfig);
config->copyFrom(editedConfig);
saveLinkConfigurationList();
// Tell link about changes (if any)
config->updateSettings();
emit config->nameChanged(config->name());
// Discard temporary duplicate
delete editedConfig;
} else {
qWarning() << "Internal error";
}
return true;
}
bool LinkManager::endCreateConfiguration(LinkConfiguration* config)
{
if (config) {
_fixUnnamed(config);
addConfiguration(config);
saveLinkConfigurationList();
} else {
qWarning() << "Internal error";
}
return true;
}
LinkConfiguration* LinkManager::createConfiguration(int type, const QString& name)
{
if(static_cast<LinkConfiguration::LinkType>(type) == LinkConfiguration::TypeSerial)
return LinkConfiguration::createSettings(type, name);
}
LinkConfiguration* LinkManager::startConfigurationEditing(LinkConfiguration* config)
{
if(config->type() == LinkConfiguration::TypeSerial)
_updateSerialPorts();
return LinkConfiguration::duplicateSettings(config);
} else {
qWarning() << "Internal error";
}
void LinkManager::_fixUnnamed(LinkConfiguration* config)
{
if (config) {
//-- Check for "Unnamed"
if (config->name() == "Unnamed") {
switch(config->type()) {
case LinkConfiguration::TypeSerial: {
QString tname = dynamic_cast<SerialConfiguration*>(config)->portName();
tname.replace("\\\\.\\", "");
#else
tname.replace("/dev/cu.", "");
tname.replace("/dev/", "");
#endif
config->setName(QString("Serial Device on %1").arg(tname));
break;
#endif
case LinkConfiguration::TypeUdp:
config->setName(
QString("UDP Link on Port %1").arg(dynamic_cast<UDPConfiguration*>(config)->localPort()));
break;
case LinkConfiguration::TypeTcp: {
TCPConfiguration* tconfig = dynamic_cast<TCPConfiguration*>(config);
if(tconfig) {
config->setName(
QString("TCP Link %1:%2").arg(tconfig->address().toString()).arg(static_cast<int>(tconfig->port())));
BluetoothConfiguration* tconfig = dynamic_cast<BluetoothConfiguration*>(config);
if(tconfig) {
config->setName(QString("%1 (Bluetooth Device)").arg(tconfig->device().name));
case LinkConfiguration::TypeLogReplay: {
LogReplayLinkConfiguration* tconfig = dynamic_cast<LogReplayLinkConfiguration*>(config);
if(tconfig) {
config->setName(QString("Log Replay %1").arg(tconfig->logFilenameShort()));
break;
#ifdef QT_DEBUG
case LinkConfiguration::TypeMock:
break;
#endif
case LinkConfiguration::TypeLast:
break;
}
}
void LinkManager::removeConfiguration(LinkConfiguration* config)
{
if (config) {
LinkInterface* iface = config->link();
if(iface) {
disconnectLink(iface);
}
_removeConfiguration(config);
saveLinkConfigurationList();
} else {
qWarning() << "Internal error";
}
bool LinkManager::isAutoconnectLink(LinkInterface* link)
{
for (int i=0; i<_sharedAutoconnectConfigurations.count(); i++) {
if (_sharedAutoconnectConfigurations[i].data() == link->getLinkConfiguration()) {
return true;
}
}
return false;
bool LinkManager::isBluetoothAvailable(void)
{
return qgcApp()->isBluetoothAvailable();
}
bool found = false;
if (_activeLinkCheckList.count() != 0) {
link = _activeLinkCheckList.takeFirst();
if (containsLink(link) && link->isConnected()) {
// Make sure there is a vehicle on the link
QmlObjectListModel* vehicles = _toolbox->multiVehicleManager()->vehicles();
for (int i=0; i<vehicles->count(); i++) {
Vehicle* vehicle = qobject_cast<Vehicle*>(vehicles->get(i));
if (vehicle->containsLink(link)) {
found = true;
break;
}
}
}
}
if (_activeLinkCheckList.count() == 0) {
_activeLinkCheckTimer.stop();
}
if (!found && link) {
// See if we can get an NSH prompt on this link
bool foundNSHPrompt = false;
QSignalSpy spy(link, SIGNAL(bytesReceived(LinkInterface*, QByteArray)));
if (spy.wait(100)) {
QList<QVariant> arguments = spy.takeFirst();
if (arguments[1].toByteArray().contains("nsh>")) {
foundNSHPrompt ?
tr("Please check to make sure you have an SD Card inserted in your Vehicle and try again.") :
tr("Vehicle on port %1 is not responding. If this continues, shutdown %2, restart the Vehicle letting it boot completely, then start %2.").arg(link->getSerialConfig()->portName()).arg(qgcApp()->applicationName()));