QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
ADSBVehicleManager.cc
Go to the documentation of this file.
2#include "MAVLinkLib.h"
3#include "AppMessages.h"
4#include "SettingsManager.h"
6#include "ADSBTCPLink.h"
7#include "ADSBVehicle.h"
10
11#include <QtCore/QApplicationStatic>
12#include <QtCore/QThread>
13#include <QtCore/QTimer>
14
15QGC_LOGGING_CATEGORY(ADSBVehicleManagerLog, "ADSB.ADSBVehicleManager")
16
17Q_APPLICATION_STATIC(ADSBVehicleManager, _adsbVehicleManager, SettingsManager::instance()->adsbVehicleManagerSettings());
18
20 : QObject(parent)
21 , _adsbSettings(settings)
22 , _adsbVehicleCleanupTimer(new QTimer(this))
23 , _adsbVehicles(new QmlObjectListModel(this))
24{
25 // qCDebug(ADSBVehicleManagerLog) << Q_FUNC_INFO << this;
26
27 (void) qRegisterMetaType<ADSB::VehicleInfo_t>("ADSB::VehicleInfo_t");
28
29 _adsbVehicleCleanupTimer->setSingleShot(false);
30 _adsbVehicleCleanupTimer->setInterval(1000);
31 (void) connect(_adsbVehicleCleanupTimer, &QTimer::timeout, this, &ADSBVehicleManager::_cleanupStaleVehicles);
32 // Always run: vehicles can also arrive via mavlink messages, not just the tcp link
33 _adsbVehicleCleanupTimer->start();
34
35 Fact* const adsbEnabled = _adsbSettings->adsbServerConnectEnabled();
36 Fact* const hostAddress = _adsbSettings->adsbServerHostAddress();
37 Fact* const port = _adsbSettings->adsbServerPort();
38
39 (void) connect(adsbEnabled, &Fact::rawValueChanged, this, [this, hostAddress, port](QVariant value) {
40 if (value.toBool()) {
41 _start(hostAddress->rawValue().toString(), port->rawValue().toUInt());
42 } else {
43 _stop();
44 }
45 });
46
47 if (adsbEnabled->rawValue().toBool()) {
48 _start(hostAddress->rawValue().toString(), port->rawValue().toUInt());
49 }
50}
51
53{
54 _stop();
55
56 // qCDebug(ADSBVehicleManagerLog) << Q_FUNC_INFO << this;
57}
58
60{
61 return _adsbVehicleManager();
62}
63
65{
66 if (message.msgid != MAVLINK_MSG_ID_ADSB_VEHICLE) {
67 return;
68 }
69
70 _handleADSBVehicle(message);
71}
72
73void ADSBVehicleManager::_handleADSBVehicle(const mavlink_message_t &message)
74{
75 mavlink_adsb_vehicle_t adsbVehicleMsg{};
76 mavlink_msg_adsb_vehicle_decode(&message, &adsbVehicleMsg);
77
78 if (adsbVehicleMsg.tslc > kMaxTimeSinceLastSeen) {
79 return;
80 }
81
82 ADSB::VehicleInfo_t vehicleInfo{};
83
84 vehicleInfo.availableFlags = ADSB::AvailableInfoTypes::fromInt(0);
85
86 vehicleInfo.icaoAddress = adsbVehicleMsg.ICAO_address;
87 vehicleInfo.lastContact = adsbVehicleMsg.tslc;
88
89 if (adsbVehicleMsg.flags & ADSB_FLAGS_VALID_COORDS) {
90 vehicleInfo.availableFlags |= ADSB::LocationAvailable;
91 vehicleInfo.location.setLatitude(adsbVehicleMsg.lat / 1e7);
92 vehicleInfo.location.setLongitude(adsbVehicleMsg.lon / 1e7);
93 }
94
95 if (adsbVehicleMsg.flags & ADSB_FLAGS_VALID_ALTITUDE) {
96 vehicleInfo.availableFlags |= ADSB::AltitudeAvailable;
97 vehicleInfo.location.setAltitude(adsbVehicleMsg.altitude / 1e3);
98 }
99
100 if (adsbVehicleMsg.flags & ADSB_FLAGS_VALID_HEADING) {
101 vehicleInfo.availableFlags |= ADSB::HeadingAvailable;
102 vehicleInfo.heading = adsbVehicleMsg.heading / 1e2;
103 }
104
105 if (adsbVehicleMsg.flags & ADSB_FLAGS_VALID_VELOCITY) {
106 vehicleInfo.availableFlags |= ADSB::VelocityAvailable;
107 vehicleInfo.velocity = adsbVehicleMsg.hor_velocity / 1e2;
108 }
109
110 if (adsbVehicleMsg.flags & ADSB_FLAGS_VALID_CALLSIGN) {
111 vehicleInfo.availableFlags |= ADSB::CallsignAvailable;
112 vehicleInfo.callsign = QString::fromLatin1(adsbVehicleMsg.callsign, sizeof(adsbVehicleMsg.callsign));
113 }
114
115 if (adsbVehicleMsg.flags & ADSB_FLAGS_VALID_SQUAWK) {
116 vehicleInfo.availableFlags |= ADSB::SquawkAvailable;
117 vehicleInfo.squawk = adsbVehicleMsg.squawk;
118 }
119
120 if (adsbVehicleMsg.flags & ADSB_FLAGS_SIMULATED) {
121 vehicleInfo.simulated = true;
122 }
123
124 if (adsbVehicleMsg.flags & ADSB_FLAGS_VERTICAL_VELOCITY_VALID) {
125 vehicleInfo.availableFlags |= ADSB::VerticalVelAvailable;
126 vehicleInfo.verticalVel = adsbVehicleMsg.ver_velocity;
127 }
128
129 if (adsbVehicleMsg.flags & ADSB_FLAGS_BARO_VALID) {
130 vehicleInfo.baro = true;
131 }
132
133 if (adsbVehicleMsg.flags & ADSB_FLAGS_SOURCE_UAT) {
134
135 }
136
137 adsbVehicleMsg.altitude_type = adsbVehicleMsg.altitude_type;
138 adsbVehicleMsg.emitter_type = adsbVehicleMsg.emitter_type;
139
140 adsbVehicleUpdate(vehicleInfo);
141}
142
144{
145 const uint32_t icaoAddress = vehicleInfo.icaoAddress;
146 if (_adsbICAOMap.contains(icaoAddress)) {
147 _adsbICAOMap[icaoAddress]->update(vehicleInfo);
148 return;
149 }
150
151 if (vehicleInfo.availableFlags & ADSB::LocationAvailable) {
152 ADSBVehicle* const adsbVehicle = new ADSBVehicle(vehicleInfo, this);
153 _adsbICAOMap[icaoAddress] = adsbVehicle;
154 _adsbVehicles->append(adsbVehicle);
155 qCDebug(ADSBVehicleManagerLog) << "Added" << QString::number(adsbVehicle->icaoAddress());
156 }
157}
158
159void ADSBVehicleManager::_start(const QString &hostAddress, quint16 port)
160{
161 if (_adsbTcpLink) {
162 qCWarning(ADSBVehicleManagerLog) << "TCP Link already started";
163 return;
164 }
165
166 if (hostAddress.isEmpty()) {
167 qCWarning(ADSBVehicleManagerLog) << "No ADSB server host address set";
168 return;
169 }
170
171 // Run the link on a worker thread so socket reads and SBS-1 parsing stay off the ui thread.
172 // The thread is unparented so QObject parent-child destruction can never delete it while it
173 // is still running (which is fatal); _stop() manages its lifetime explicitly.
174 _adsbTcpLinkThread = new QThread();
175 _adsbTcpLinkThread->setObjectName(QStringLiteral("ADSBTCPLink"));
176
177 _adsbTcpLink = new ADSBTCPLink(hostAddress, port); // unparented, deleted when the thread finishes
178 _adsbTcpLink->moveToThread(_adsbTcpLinkThread);
179
180 (void) connect(_adsbTcpLinkThread, &QThread::started, _adsbTcpLink, &ADSBTCPLink::init);
181 (void) connect(_adsbTcpLinkThread, &QThread::finished, _adsbTcpLink, &QObject::deleteLater);
182 (void) connect(_adsbTcpLink, &ADSBTCPLink::adsbVehicleUpdate, this, &ADSBVehicleManager::adsbVehicleUpdate, Qt::QueuedConnection);
183 (void) connect(_adsbTcpLink, &ADSBTCPLink::errorOccurred, this, &ADSBVehicleManager::_linkError, Qt::QueuedConnection);
184
185 _adsbTcpLinkThread->start();
186}
187
188void ADSBVehicleManager::_stop()
189{
190 if (!_adsbTcpLinkThread) {
191 return;
192 }
193
194 _adsbTcpLinkThread->quit();
195 if (!_adsbTcpLinkThread->wait(kThreadStopTimeoutMs)) {
196 // Bail out without deleting or resetting state: destroying a still-running QThread is
197 // fatal, and clearing the pointers would allow a second link to be started alongside it.
198 qCWarning(ADSBVehicleManagerLog) << "ADSBTCPLink thread failed to stop";
199 return;
200 }
201 // wait() succeeded so the thread has finished: delete it directly rather than with
202 // deleteLater(), which would leak during shutdown once the event loop stops running.
203 delete _adsbTcpLinkThread;
204 _adsbTcpLinkThread = nullptr;
205 _adsbTcpLink = nullptr; // deleted via the thread finished connection
206
207 _adsbVehicles->clearAndDeleteContents();
208 _adsbICAOMap.clear();
209}
210
211void ADSBVehicleManager::_cleanupStaleVehicles()
212{
213 for (qsizetype i = _adsbVehicles->count() - 1; i >= 0; i--) {
214 ADSBVehicle* const adsbVehicle = _adsbVehicles->value<ADSBVehicle*>(i);
215 if (adsbVehicle->expired()) {
216 qCDebug(ADSBVehicleManagerLog) << "Expired" << QString::number(adsbVehicle->icaoAddress());
217 (void) _adsbVehicles->removeAt(i);
218 (void) _adsbICAOMap.remove(adsbVehicle->icaoAddress());
219 adsbVehicle->deleteLater();
220 }
221 }
222}
223
224void ADSBVehicleManager::_linkError(const QString &errorMsg, bool stopped)
225{
226 qCDebug(ADSBVehicleManagerLog) << errorMsg;
227
228 QString msg = QStringLiteral("ADSB Server Error: %1").arg(errorMsg);
229
230 if (stopped) {
231 (void) msg.append("\nADSB has been disabled");
232 _adsbSettings->adsbServerConnectEnabled()->setRawValue(false);
233 }
234
236}
Q_APPLICATION_STATIC(ADSBVehicleManager, _adsbVehicleManager, SettingsManager::instance() ->adsbVehicleManagerSettings())
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void adsbVehicleUpdate(const ADSB::VehicleInfo_t &vehicleInfo)
void mavlinkMessageReceived(const mavlink_message_t &message)
static ADSBVehicleManager * instance()
bool expired() const
Definition ADSBVehicle.h:41
uint32_t icaoAddress() const
Definition ADSBVehicle.h:32
A Fact is used to hold a single value within the system.
Definition Fact.h:17
void rawValueChanged(const QVariant &value)
QVariant rawValue() const
Definition Fact.h:90
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
T value(int index) const
QObject * removeAt(int index)
int count() const override final
void clearAndDeleteContents() override final
Clears the list and calls deleteLater on each entry.
Provides access to all app settings.
@ SquawkAvailable
Definition ADSB.h:39
@ VelocityAvailable
Definition ADSB.h:37
@ CallsignAvailable
Definition ADSB.h:38
@ AltitudeAvailable
Definition ADSB.h:35
@ LocationAvailable
Definition ADSB.h:34
@ VerticalVelAvailable
Definition ADSB.h:40
@ HeadingAvailable
Definition ADSB.h:36
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.
Definition AppMessages.cc:9
uint32_t icaoAddress
Definition ADSB.h:49
AvailableInfoTypes availableFlags
Definition ADSB.h:48