7#ifndef QGC_NO_SERIAL_LINK
19 , _commLostCheckTimer(new QTimer(this))
24 (void) connect(_commLostCheckTimer, &QTimer::timeout,
this, &VehicleLinkManager::_commLostCheck);
26 _commLostCheckTimer->setSingleShot(
false);
27 _commLostCheckTimer->setInterval(
QGC::runningUnitTests() ? kTestCommLostCheckTimeoutMs : _commLostCheckTimeoutMSecs);
38 if (message.msgid == MAVLINK_MSG_ID_RADIO_STATUS) {
42 const int linkIndex = _containsLinkIndex(link);
43 if (linkIndex == -1) {
48 LinkInfo_t &linkInfo = _rgLinkInfo[linkIndex];
49 linkInfo.heartbeatElapsedTimer.restart();
50 if (_rgLinkInfo[linkIndex].commLost) {
51 _commRegainedOnLink(link);
55void VehicleLinkManager::_commRegainedOnLink(
LinkInterface *link)
58 const int linkIndex = _containsLinkIndex(link);
59 if (linkIndex == -1) {
63 _rgLinkInfo[linkIndex].commLost =
false;
66 QString commRegainedMessage;
67 const bool isPrimaryLink = link == _primaryLink.lock().get();
68 if (_rgLinkInfo.count() > 1) {
69 commRegainedMessage = tr(
"%1Communication regained on %2 link").arg(_vehicle->_vehicleIdSpeech()).arg(isPrimaryLink ? tr(
"primary") : tr(
"secondary"));
71 commRegainedMessage = tr(
"%1Communication regained").arg(_vehicle->_vehicleIdSpeech());
75 QString primarySwitchMessage;
76 if (_updatePrimaryLink()) {
77 primarySwitchMessage = tr(
"%1Switching communication to new primary link").arg(_vehicle->_vehicleIdSpeech());
80 if (!commRegainedMessage.isEmpty()) {
84 if (!primarySwitchMessage.isEmpty()) {
92 if (_communicationLost &&
93 std::any_of(_rgLinkInfo.cbegin(), _rgLinkInfo.cend(),
94 [](
const LinkInfo_t &info) { return !info.commLost; })) {
95 _communicationLost =
false;
100void VehicleLinkManager::_commLostCheck()
102 if (!_communicationLostEnabled) {
109 bool linkStatusChange =
false;
110 for (LinkInfo_t &linkInfo: _rgLinkInfo) {
111 if (!linkInfo.commLost && !linkInfo.link->linkConfiguration()->isHighLatency() && (linkInfo.heartbeatElapsedTimer.elapsed() > heartbeatTimeout)) {
112 linkInfo.commLost =
true;
113 linkStatusChange =
true;
116 const bool isPrimaryLink = linkInfo.link.get() == _primaryLink.lock().get();
117 if (_rgLinkInfo.count() > 1) {
118 const QString msg = tr(
"%1Communication lost on %2 link.").arg(_vehicle->_vehicleIdSpeech()).arg(isPrimaryLink ? tr(
"primary") : tr(
"secondary"));
124 if (linkStatusChange) {
128 if (_updatePrimaryLink()) {
129 QString msg = tr(
"%1Switching communication to secondary link.").arg(_vehicle->_vehicleIdSpeech());
134 if (_communicationLost) {
138 bool totalCommunicationLoss =
true;
139 for (
const LinkInfo_t &linkInfo: _rgLinkInfo) {
140 if (!linkInfo.commLost) {
141 totalCommunicationLoss =
false;
146 if (totalCommunicationLoss) {
147 if (_autoDisconnect) {
155 _communicationLost =
true;
160int VehicleLinkManager::_containsLinkIndex(
const LinkInterface *link)
162 for (
int i = 0; i < _rgLinkInfo.count(); i++) {
163 if (_rgLinkInfo[i].link.get() == link) {
173 if (_containsLinkIndex(link) != -1) {
174 qCWarning(VehicleLinkManagerLog) <<
"_addLink call with link which is already in the list";
180 qCDebug(VehicleLinkManagerLog) <<
"_addLink stale link" << (
void*)link;
184 qCDebug(VehicleLinkManagerLog) <<
"_addLink:" << link->
linkConfiguration()->name() << QString(
"%1").arg((qulonglong)link, 0, 16);
186 link->addVehicleReference();
189 linkInfo.link = sharedLink;
190 if (!link->linkConfiguration()->isHighLatency()) {
191 linkInfo.heartbeatElapsedTimer.start();
193 _rgLinkInfo.append(linkInfo);
195 _updatePrimaryLink();
201 if (_rgLinkInfo.count() == 1) {
202 _commLostCheckTimer->start();
208 const int linkIndex = _containsLinkIndex(link);
209 if (linkIndex == -1) {
210 qCWarning(VehicleLinkManagerLog) <<
"_removeLink call with link which is already in the list";
214 qCDebug(VehicleLinkManagerLog) <<
"_removeLink:" << QString(
"%1").arg((qulonglong)link, 0, 16);
216 if (link == _primaryLink.lock().get()) {
217 _primaryLink.reset();
222 link->removeVehicleReference();
224 _rgLinkInfo.removeAt(linkIndex);
226 if (_rgLinkInfo.isEmpty()) {
227 _commLostCheckTimer->stop();
231void VehicleLinkManager::_linkDisconnected()
233 qCDebug(VehicleLinkManagerLog) << Q_FUNC_INFO <<
"linkCount" << _rgLinkInfo.count();
235 LinkInterface *link = qobject_cast<LinkInterface*>(sender());
241 _updatePrimaryLink();
242 if (_rgLinkInfo.isEmpty() && !_allLinksRemovedSignalledByCloseVehicle) {
243 qCDebug(VehicleLinkManagerLog) <<
"signalling allLinksRemoved";
246 _vehicle->_stopCommandProcessing();
253#ifndef QGC_NO_SERIAL_LINK
255 for (
const LinkInfo_t &linkInfo: _rgLinkInfo) {
256 if (linkInfo.commLost) {
261 auto linkInterface = candidateLink.get();
263 return candidateLink;
269 for (
const LinkInfo_t &linkInfo: _rgLinkInfo) {
270 if (linkInfo.commLost) {
277 return candidateLink;
289 for (
const LinkInfo_t &linkInfo: _rgLinkInfo) {
290 if (linkInfo.commLost) {
297 return candidateLink;
304bool VehicleLinkManager::_updatePrimaryLink()
307 const int linkIndex = _containsLinkIndex(
primaryLink.get());
309 if ((linkIndex != -1) && !_rgLinkInfo[linkIndex].commLost && !
primaryLink->linkConfiguration()->isHighLatency()) {
315 if ((linkIndex != -1) && !bestActivePrimaryLink) {
326 MAV_COMP_ID_AUTOPILOT1,
327 MAV_CMD_CONTROL_HIGH_LATENCY,
333 _primaryLink = bestActivePrimaryLink;
336 if (bestActivePrimaryLink && bestActivePrimaryLink->linkConfiguration()->isHighLatency()) {
338 MAV_CMD_CONTROL_HIGH_LATENCY,
350 const QList<LinkInfo_t> rgLinkInfoCopy = _rgLinkInfo;
351 for (
const LinkInfo_t &linkInfo: rgLinkInfoCopy) {
352 _removeLink(linkInfo.link.get());
357 _allLinksRemovedSignalledByCloseVehicle =
true;
360 _vehicle->_stopCommandProcessing();
374 return (_containsLinkIndex(link) != -1);
379 if (!_primaryLink.expired()) {
380 return _primaryLink.lock()->linkConfiguration()->name();
388 for (
const LinkInfo_t& linkInfo: _rgLinkInfo) {
389 if (linkInfo.link->linkConfiguration()->name() == name) {
390 _primaryLink = linkInfo.link;
400 for (
const LinkInfo_t &linkInfo: _rgLinkInfo) {
401 rgNames.append(linkInfo.link->linkConfiguration()->name());
409 QStringList rgStatuses;
411 for (
const LinkInfo_t &linkInfo: _rgLinkInfo) {
412 rgStatuses.append(linkInfo.commLost ? tr(
"Comm Lost") :
"");
std::shared_ptr< LinkConfiguration > SharedLinkConfigurationPtr
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void say(const QString &text, TextMods textMods=TextMod::None)
static AudioOutput * instance()
The link interface defines the interface for all links used to communicate with the ground station ap...
SharedLinkConfigurationPtr linkConfiguration()
SharedLinkInterfacePtr sharedLinkInterfacePointerForLink(const LinkInterface *link)
static bool isLinkUSBDirect(const LinkInterface *link)
static LinkManager * instance()
void communicationLostEnabledChanged(bool communicationLostEnabled)
void primaryLinkChanged()
QStringList linkNames() const
WeakLinkInterfacePtr primaryLink() const
void allLinksRemoved(Vehicle *vehicle)
QStringList linkStatuses() const
void setPrimaryLinkByName(const QString &name)
void mavlinkMessageReceived(LinkInterface *link, const mavlink_message_t &message)
void setCommunicationLostEnabled(bool communicationLostEnabled)
bool containsLink(LinkInterface *link)
void communicationLostChanged(bool communicationLost)
static constexpr int kTestHeartbeatTimeoutMs
void linkStatusesChanged()
bool communicationLostEnabled() const
QString primaryLinkName() const
void sendMavCommand(int compId, MAV_CMD command, bool showError, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f, float param6=0.0f, float param7=0.0f)
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.