3#include <QtCore/QApplicationStatic>
4#include <QtCore/QCoreApplication>
5#include <QtCore/QtMath>
26 return _ntripManagerInstance();
47constexpr TransitionRow kTransitions[] = {
49 {CS::Disconnected, Ev::StartRequested, CS::Connecting},
52 {CS::Connecting, Ev::TransportConnected, CS::Connected},
53 {CS::Connecting, Ev::RTCMBeforeConnected, CS::Connected},
54 {CS::Connecting, Ev::TransportError, CS::Reconnecting},
55 {CS::Connecting, Ev::TransportFatalError, CS::Error},
56 {CS::Connecting, Ev::ConfigInvalid, CS::Error},
57 {CS::Connecting, Ev::StopRequested, CS::Disconnected},
60 {CS::Connected, Ev::TransportError, CS::Reconnecting},
61 {CS::Connected, Ev::TransportFatalError, CS::Error},
62 {CS::Connected, Ev::StopRequested, CS::Disconnected},
63 {CS::Connected, Ev::HotReconfigure, CS::Connecting},
68 {CS::Connecting, Ev::HotReconfigure, CS::Connecting},
72 {CS::Reconnecting, Ev::ReconnectDue, CS::Connecting},
73 {CS::Reconnecting, Ev::ReconnectGaveUp, CS::Error},
74 {CS::Reconnecting, Ev::StopRequested, CS::Disconnected},
75 {CS::Reconnecting, Ev::StartRequested, CS::Connecting},
78 {CS::Error, Ev::StartRequested, CS::Connecting},
79 {CS::Error, Ev::StopRequested, CS::Disconnected},
85 case NTRIPError::AuthFailed:
86 case NTRIPError::InvalidConfig:
101 qCDebug(NTRIPManagerLog) <<
"NTRIPManager created";
103 _settingsDebounceTimer.setSingleShot(
true);
104 _settingsDebounceTimer.setInterval(kSettingsDebounceMs);
105 connect(&_settingsDebounceTimer, &QChronoTimer::timeout,
this, &NTRIPManager::_onSettingChanged);
110 [
this](
const QString& mountpoint) {
111 if (_settings && _settings->ntripMountpoint()) {
112 _settings->ntripMountpoint()->setRawValue(mountpoint);
116 _reconnectTimer.setSingleShot(
true);
125 qCDebug(NTRIPManagerLog) <<
"NTRIPManager destroyed";
136 _rtcmMavlink = mavlink;
142 qCWarning(NTRIPManagerLog) <<
"NTRIPManager::init() called more than once";
149 qCCritical(NTRIPManagerLog) <<
"init: NTRIPSettings unavailable — SettingsManager not ready?";
151 const Fact* facts[] = {
152 _settings->ntripServerConnectEnabled(),
153 _settings->ntripServerHostAddress(),
154 _settings->ntripServerPort(),
155 _settings->ntripUsername(),
156 _settings->ntripPassword(),
157 _settings->ntripMountpoint(),
158 _settings->ntripWhitelist(),
159 _settings->ntripUseTls(),
160 _settings->ntripAllowSelfSignedCerts(),
161 _settings->ntripUdpForwardEnabled(),
162 _settings->ntripUdpTargetAddress(),
163 _settings->ntripUdpTargetPort(),
165 for (
const auto* fact : facts) {
172 _ggaProvider.
init(_settings);
177 QObject* parentObj =
qgcApp() ?
static_cast<QObject*
>(
qgcApp()) :
static_cast<QObject*
>(
this);
179 _rtcmMavlink->setObjectName(QStringLiteral(
"RTCMMavlink"));
182 _setupRtcmUdpInput();
189void NTRIPManager::_setupRtcmUdpInput()
191 if (!_settings || !_settings->rtcmUdpInputPort()) {
195 const quint16 port =
static_cast<quint16
>(_settings->rtcmUdpInputPort()->rawValue().toUInt());
199 auto applyUdpInputSettings = [
this]() {
200 const quint16 inPort =
static_cast<quint16
>(_settings->rtcmUdpInputPort()->rawValue().toUInt());
201 _rtcmUdpInput->
setPort(inPort);
202 _rtcmUdpInput->
setValidation(_settings->rtcmUdpValidate()->rawValue().toBool());
203 if (_settings->rtcmUdpInputEnabled()->rawValue().toBool()) {
204 _rtcmUdpInput->
start();
206 _rtcmUdpInput->
stop();
212 applyUdpInputSettings();
234 QGeoCoordinate sortCoord;
236 sortCoord = mvm->activeVehicle()->coordinate();
245bool NTRIPManager::_dispatch(Event ev,
const QString& detail)
247 for (
const auto& row : kTransitions) {
248 if (row.from == _connectionStatus && row.event == ev) {
249 _enterState(row.to, detail);
253 qCDebug(NTRIPManagerLog) <<
"NTRIP event" <<
static_cast<int>(ev) <<
"ignored in state"
254 <<
static_cast<int>(_connectionStatus);
258void NTRIPManager::_enterState(ConnectionStatus to,
const QString& detail)
261 const bool stateChanged = (from != to);
262 const QString msg = detail.isEmpty() ? _defaultMessageFor(to) : detail;
267 _connectionStatus = to;
268 const bool msgChanged = (_statusMessage != msg);
270 _statusMessage = msg;
274 qCDebug(NTRIPManagerLog) <<
"NTRIP state" <<
static_cast<int>(from) <<
"→" <<
static_cast<int>(to) << msg;
283 _onEnterState(from, to);
286QString NTRIPManager::_defaultMessageFor(ConnectionStatus state)
290 return tr(
"Disconnected");
292 return tr(
"Connecting...");
294 return tr(
"Connected");
296 return tr(
"Reconnecting...");
303void NTRIPManager::_onEnterState(ConnectionStatus , ConnectionStatus to)
310 _teardownTransport();
313 _udpForwarder.
stop();
314 _setSecurityWarning({});
320 _setSecurityWarning({});
321 _teardownTransport();
326 _resetReconnectAttempts();
329 _ggaProvider.
start(_transport);
334 _teardownTransport();
337 _scheduleReconnect();
342 _teardownTransport();
345 _udpForwarder.
stop();
346 _setSecurityWarning({});
356void NTRIPManager::_teardownTransport()
361 _transport->disconnect(
this);
363 _transport->deleteLater();
364 _transport =
nullptr;
367int NTRIPManager::_reconnectBackoffMs()
const
369 return qMin(kMinReconnectMs * (1 << qMin(_reconnectAttempts, 5)), kMaxReconnectMs);
372void NTRIPManager::_scheduleReconnect()
376 const auto backoff = std::chrono::milliseconds{_reconnectBackoffMs()};
377 ++_reconnectAttempts;
378 if (_reconnectExhausted()) {
382 _reconnectTimer.setInterval(backoff);
383 _reconnectTimer.start();
386void NTRIPManager::_startTransport()
395 _applyUdpForwarderConfig(
config);
397 if (
const QString err =
config.validationError(); !err.isEmpty()) {
398 qCWarning(NTRIPManagerLog) <<
"NTRIP config invalid:" << err <<
"host=" <<
config.host
399 <<
" port=" <<
config.port;
404 qCDebug(NTRIPManagerLog) <<
"startTransport: host=" <<
config.host <<
" port=" <<
config.port
405 <<
" mount=" <<
config.mountpoint;
408 const QString msg = tr(
"Connecting to %1:%2...").arg(
config.host).arg(
config.port);
409 if (_statusMessage != msg) {
410 _statusMessage = msg;
417 if (_injectedTransport) {
418 _transport = _injectedTransport;
419 _injectedTransport =
nullptr;
426 connect(_transport, &
NTRIPTransport::error,
this, &NTRIPManager::_onTransportError, Qt::QueuedConnection);
438 qCDebug(NTRIPManagerLog) <<
"NTRIP transport started";
445void NTRIPManager::_onTransportError(
NTRIPError code,
const QString& detail)
447 qCWarning(NTRIPManagerLog) <<
"NTRIP error:" <<
static_cast<int>(code) << detail;
451 if (_casterStatus != caster) {
452 _casterStatus = caster;
456 if (_isEnabled() && isRetryable(code)) {
457 const int backoffMs = _reconnectBackoffMs();
458 qCDebug(NTRIPManagerLog) <<
"NTRIP reconnecting in" << backoffMs <<
"ms (attempt" << (_reconnectAttempts + 1)
466void NTRIPManager::_onPlaintextCredentialsWarning()
468 qCWarning(NTRIPManagerLog) <<
"Credentials sent without TLS encryption — enable TLS in NTRIP settings";
469 _setSecurityWarning(tr(
"Credentials are being sent without TLS encryption."));
472void NTRIPManager::_setSecurityWarning(
const QString& warning)
474 if (_securityWarning == warning) {
477 _securityWarning = warning;
481void NTRIPManager::_rtcmDataReceived(
const QByteArray& data,
int messageId)
485 qCDebug(NTRIPManagerLog) <<
"NTRIP forwarding RTCM:" << data.size() <<
"bytes";
498 qCWarning(NTRIPManagerLog) <<
"RTCMMavlink not ready; dropping" << data.size() <<
"bytes";
504bool NTRIPManager::_isEnabled()
const
506 return _settings && _settings->ntripServerConnectEnabled() &&
507 _settings->ntripServerConnectEnabled()->rawValue().toBool();
510void NTRIPManager::_onSettingChanged()
520 _resetReconnectAttempts();
526 const bool isActive =
543 qCDebug(NTRIPManagerLog) <<
"NTRIP transport-affecting setting changed, reconnecting";
549 qCDebug(NTRIPManagerLog) <<
"NTRIP UDP forward settings changed, reconfiguring in place";
550 _applyUdpForwarderConfig(newConfig);
554 qCDebug(NTRIPManagerLog) <<
"NTRIP RTCM whitelist changed, applying to live parser";
558 _runningConfig = newConfig;
563 if (!
config.udpForwardEnabled) {
564 _udpForwarder.
stop();
568 qCWarning(NTRIPManagerLog) <<
"UDP forward config invalid:" <<
config.udpTargetAddress <<
config.udpTargetPort;
Q_APPLICATION_STATIC(NTRIPManager, _ntripManagerInstance)
#define QGC_LOGGING_CATEGORY(name, categoryStr)
A Fact is used to hold a single value within the system.
void rawValueChanged(const QVariant &value)
static MultiVehicleManager * instance()
Vehicle * activeVehicle() const
void recordMessage(int bytes, int messageId=0)
void sourceChanged(const QString &source)
void init(NTRIPSettings *settings)
void start(NTRIPTransport *transport)
void statusMessageChanged()
void setRtcmMavlink(RTCMMavlink *mavlink)
void casterStatusChanged(CasterStatus status)
ConnectionStatus
Public connection status. Numeric values are stable — QML binds against them.
Q_INVOKABLE void fetchMountpoints()
RTCMMavlink * rtcmMavlink() const
void securityWarningChanged()
NTRIPManager(QObject *parent=nullptr)
@ StartRequested
startNTRIP() called or settings enable went true.
@ ReconnectGaveUp
NTRIPReconnectPolicy fired gaveUp().
@ HotReconfigure
Transport-affecting setting changed while connected; reconnect in place.
@ TransportFatalError
NTRIPTransport emitted a non-retryable error.
@ RTCMBeforeConnected
RTCM data arrived before the connected() signal was processed.
@ ConfigInvalid
NTRIPTransportConfig::isValid() returned false.
@ TransportError
NTRIPTransport emitted a retryable error.
@ TransportConnected
NTRIPTransport emitted connected().
@ StopRequested
stopNTRIP() called or settings enable went false.
@ ReconnectDue
NTRIPReconnectPolicy fired reconnectRequested().
void connectionStatusChanged()
void fetch(const NTRIPTransportConfig &config, const QGeoCoordinate &sortCoord={})
void mountpointSelected(const QString &mountpoint)
void plaintextCredentialsWarning()
void RTCMDataUpdate(const QByteArray &message, int messageId)
void error(NTRIPError code, const QString &detail)
void RTCMDataUpdate(QByteArrayView data)
static SettingsManager * instance()
NTRIPSettings * ntripSettings() const
bool configure(const QString &address, quint16 port)
void forward(const QByteArray &data)
bool whitelistDiffers(const NTRIPTransportConfig &other) const
bool transportDiffers(const NTRIPTransportConfig &other) const
static NTRIPTransportConfig fromSettings(NTRIPSettings &settings)
bool udpForwardDiffers(const NTRIPTransportConfig &other) const
static QVector< int > parseWhitelist(const QString &csv)