7#include <QtNetwork/QHostAddress>
23 case NTRIPError::AuthFailed:
24 case NTRIPError::InvalidConfig:
25 case NTRIPError::RequestRejected:
27 case NTRIPError::SslError:
36 QUrl endpoint = connection.
url();
37 endpoint.setScheme(connection.
useTls ? QStringLiteral(
"ntrips") : QStringLiteral(
"ntrip"));
38 return endpoint.toString(QUrl::FullyEncoded);
46 for (
const auto& token :
whitelist.split(QLatin1Char(
','), Qt::SkipEmptyParts)) {
48 const int id = token.trimmed().toInt(&ok);
63 , _settingsDebounceTask(_scheduler, this)
64 , _reconnectTask(_scheduler, this)
65 , _ggaReporter(this, _scheduler)
68 , _correctionManager(dependencies.corrections)
69 , _networkMonitor(dependencies.network)
70 , _sourceTableController(this, _scheduler)
72 qCDebug(NTRIPManagerLog) <<
this;
77 &NTRIPManager::_setPinnedCertificate);
79 if (_networkMonitor) {
84 _waitingForNetwork =
false;
93 qCDebug(NTRIPManagerLog) <<
this;
107 switch (_connectionStatus) {
109 return tr(
"Disconnected");
111 return tr(
"Connecting");
113 return tr(
"Connected");
115 return tr(
"Reconnecting");
124 if (_initialized || _shutdown) {
125 qCWarning(NTRIPManagerLog) <<
"Already initialized or shut down";
130 _applyConfiguration();
149 if (_initialized && ggaChanged) {
152 if (_initialized && streamChanged) {
153 _settingsDebounceTask.
schedule(SETTINGS_DEBOUNCE, [
this]() { _applyConfiguration(); });
163 if (_initialized || _transport) {
164 qCWarning(NTRIPManagerLog) <<
"Inject GGA position providers before initializing NTRIP";
190 _sourceTableController.
cancel();
199 _sourceTableController.
fetch(_configuration.
connection, sortCoordinate);
206void NTRIPManager::_start()
212 _settingsDebounceTask.
cancel();
214 _reconnectBackoff.
reset();
219void NTRIPManager::_stop()
221 _settingsDebounceTask.
cancel();
227void NTRIPManager::_publish()
230 if (std::exchange(_notified.status, _connectionStatus) != _connectionStatus) {
233 if (std::exchange(_notified.message, _statusMessage) != _statusMessage) {
236 if (
const QString warning =
securityWarning(); std::exchange(_notified.securityWarning, warning) != warning) {
241bool NTRIPManager::_dispatch(Event ev,
const QString& detail, std::chrono::milliseconds retryAfter)
254 static constexpr Transition kTransitions[] = {
286 for (
const auto& row : kTransitions) {
287 if (row.from == _connectionStatus && row.event == ev) {
288 _enterState(row.to, detail, retryAfter);
292 qCDebug(NTRIPManagerLog) <<
"NTRIP event" << ev <<
"ignored in state" << _connectionStatus;
296void NTRIPManager::_enterState(ConnectionStatus to,
const QString& detail, std::chrono::milliseconds retryAfter)
299 const QString msg = detail.isEmpty() ? _defaultMessageFor(to) : detail;
301 _waitingForNetwork =
false;
302 qCDebug(NTRIPManagerLog) <<
"NTRIP state" << from <<
"→" << to << msg;
308 _connectionStatus = to;
309 _statusMessage = msg;
313 _onEnterState(to, retryAfter);
316QString NTRIPManager::_defaultMessageFor(ConnectionStatus state)
320 return tr(
"Disconnected");
322 return tr(
"Connecting...");
324 return tr(
"Connected");
326 return tr(
"Reconnecting...");
333void NTRIPManager::_onEnterState(ConnectionStatus to, std::chrono::milliseconds retryAfter)
342 _correctionSource.reset();
352 _ggaReporter.
start(_transport);
358 _scheduleReconnect(retryAfter);
367void NTRIPManager::_stopStreaming()
369 const QPointer<NTRIPTransport> transport = std::exchange(_transport,
nullptr);
371 transport->disconnect(
this);
373 transport->deleteLater();
379void NTRIPManager::_scheduleReconnect(std::chrono::milliseconds retryAfter)
381 if (_shouldWaitForNetwork()) {
386 if (_reconnectExhausted()) {
388 tr(
"Gave up after %1 reconnect attempts: %2").arg(MAX_RECONNECT_ATTEMPTS).arg(_lastFailureDetail));
392 const auto backoff = _reconnectBackoff.
next(retryAfter);
393 _reconnectTask.
schedule(backoff, [
this, beforeAttempt]() {
394 if (_shouldWaitForNetwork()) {
396 _reconnectBackoff = beforeAttempt;
405void NTRIPManager::_cancelReconnect()
408 _waitingForNetwork =
false;
411bool NTRIPManager::_shouldWaitForNetwork()
const
413 return _networkMonitor && !_networkMonitor->available() && !_casterIsLoopback();
416bool NTRIPManager::_casterIsLoopback()
const
419 if (host.compare(QStringLiteral(
"localhost"), Qt::CaseInsensitive) == 0) {
423 QHostAddress address;
424 return address.setAddress(host) && address.isLoopback();
427void NTRIPManager::_waitForNetwork()
429 _waitingForNetwork =
true;
430 _statusMessage = tr(
"Waiting for network");
433void NTRIPManager::_openSession()
437 const Configuration
config = _configuration;
438 const auto& connection =
config.connection;
440 _correctionSource.reset();
441 if (_correctionManager) {
447 qCWarning(NTRIPManagerLog) <<
"NTRIP config invalid:" << err <<
"host=" << connection.
host
448 <<
" port=" << connection.
port;
453 qCDebug(NTRIPManagerLog) <<
"Starting NTRIP transport: host=" << connection.
host <<
" port=" << connection.
port
457 _statusMessage = tr(
"Connecting to %1:%2...").arg(connection.
host).arg(connection.
port);
462 NTRIPTransport* transport = _transportFactory ? _transportFactory(_configuration,
this) : nullptr;
466 _transport = transport;
469 const auto isCurrent = [
this, guard = QPointer<NTRIPTransport>(transport)]() {
470 return guard && guard == _transport;
479 _onTransportError(failure);
482 Qt::QueuedConnection);
494 _onCorrectionFrame(frame);
497 Qt::QueuedConnection);
502 qCDebug(NTRIPManagerLog) <<
"NTRIP transport started";
509void NTRIPManager::_onTransportConnected()
515void NTRIPManager::_onTransportError(
const NTRIPFailure& failure)
517 const auto code = failure.
code;
518 const auto& detail = failure.
detail;
522 qCWarning(NTRIPManagerLog) <<
"NTRIP error:" << code << detail;
523 _lastFailureDetail = detail;
525 if (_isEnabled() && isRetryable(code)) {
526 const auto backoffMs = _reconnectBackoff.
peek(failure.
retryAfter).count();
527 qCDebug(NTRIPManagerLog) <<
"NTRIP reconnecting in" << backoffMs <<
"ms (attempt"
528 << (_reconnectBackoff.
attempts() + 1) <<
")";
537void NTRIPManager::_onCertificatePinned(
const QString& pin)
541 _setPinnedCertificate(pin);
544void NTRIPManager::_setPinnedCertificate(
const QString& pin)
556 if (_correctionSource) {
557 _correctionSource->submit(
frame.data,
frame.receivedAtMs);
562 _reconnectBackoff.
reset();
565void NTRIPManager::_applyConfiguration()
570 const Configuration& newConfig = _configuration;
571 const bool isActive =
575 _reconnectBackoff.
reset();
577 }
else if (!isActive) {
581 }
else if (newConfig.connection != _running.
connection) {
582 qCDebug(NTRIPManagerLog) <<
"NTRIP transport-affecting setting changed, reconnecting";
585 if (newConfig.filter != _running.
filter && _transport) {
586 qCDebug(NTRIPManagerLog) <<
"NTRIP RTCM whitelist changed, applying to live parser";
587 _transport->setRtcmWhitelist(newConfig.filter.messageIds());
589 _running = newConfig;
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Retry delays that start at an initial value and grow by a factor per attempt, up to a cap.
constexpr int attempts() const
Attempts counted since construction or the last reset().
constexpr Delay peek(Delay retryAfter=Delay::zero()) const
constexpr Delay next(Delay retryAfter=Delay::zero())
Returns peek(retryAfter) and counts the attempt.
void recordMessage(int bytes, int messageId, qint64 receivedAtMs)
Count every message; health uses the newest valid receipt on the scheduler's clock.
std::function< std::optional< GPSObservation >()> PositionProvider
void start(NTRIPTransport *transport)
void sourceChanged(const QString &source)
void setPositionProvider(PositionSource source, PositionProvider provider)
void configure(const Configuration &configuration)
NTRIPManager(QObject *parent=nullptr, RuntimeScheduler *scheduler=nullptr, const Dependencies &dependencies={})
void statusMessageChanged()
void setConfiguration(const Configuration &configuration)
void certificatePinChanged(const QString &pin)
The trusted self-signed caster certificate changed, or was forgotten (empty); the settings should sto...
ConnectionStatus
Public connection status. Numeric values are stable — QML binds against them.
QString securityWarning() const
Set while a connected stream sends its credentials without TLS.
void setGgaPositionProvider(NTRIPGgaReporter::PositionSource source, NTRIPGgaReporter::PositionProvider provider)
void init()
Applies the configuration; call once the position providers are installed.
void enableRequested()
Retrying after an error turns the saved connection on.
QString connectionStatusText() const
Q_INVOKABLE void retryNTRIP()
Retry a user-visible error, preserving the enabled setting for subsequent automatic retries.
void securityWarningChanged()
@ StartRequested
Settings enable went true, or the user retried an error.
@ ReconnectGaveUp
The reconnect attempt ceiling was reached.
@ HotReconfigure
Transport-affecting setting changed while connected; reconnect in place.
@ TransportFatalError
NTRIPTransport emitted a non-retryable error.
@ ConfigInvalid
Connection configuration failed validation.
@ TransportError
NTRIPTransport emitted a retryable error.
@ TransportConnected
NTRIPTransport emitted connected().
@ StopRequested
Settings enable went false, or the manager shut down.
@ ReconnectDue
The reconnect delay elapsed or the network returned.
void shutdown()
Permanently retire this manager, including deferred settings work.
void connectionStatusChanged()
Q_INVOKABLE void fetchMountpoints(const QGeoCoordinate &sortCoordinate=QGeoCoordinate())
const Configuration & configuration() const
void fetch(const NTRIPConnectionConfig &config, const QGeoCoordinate &sortCoord={})
void certificatePinned(const QString &pin)
A self-signed caster certificate was trusted on first use; the owner persists the pin.
void correctionFrameReceived(const RTCMDecodedFrame &frame)
A valid frame of a whitelisted message.
void certificatePinned(const QString &pin)
A self-signed caster certificate was trusted on first use; the owner persists the pin.
void error(const NTRIPFailure &failure)
void availableChanged(bool available)
bool schedule(std::chrono::microseconds delay, RuntimeScheduler::Callback callback)
std::optional< Frame > frame(std::string_view text)
QString streamValidationError() const
bool allowSelfSignedCerts
QString credentialsInClearWarning() const
The user-visible warning when credentials go to the caster without TLS; empty otherwise.
QString pinnedCertificate
"host:port|sha256-hex" of the self-signed certificate trusted on first use; other endpoints ignore it...
QUrl url(bool withMountpoint=true) const
The caster's HTTP(S) URL, with the mountpoint as its path when withMountpoint is set,...
std::chrono::milliseconds retryAfter
Services an NTRIPManager uses, which must outlive it.
The NTRIP settings as values; the application's settings binding supplies them.
NTRIPRTCMFilterConfig filter
NTRIPGgaReporter::Configuration gga
NTRIPConnectionConfig connection
QVector< int > messageIds() const
The positive message IDs of the whitelist; entries that are not one are skipped.