QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GPSProtocolRuntime.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <thread>
5#include <utility>
6
8
9QGC_LOGGING_CATEGORY(GPSProtocolRuntimeLog, "GPS.Protocols.Runtime")
10
11using namespace std::chrono_literals;
12
14 : GPSProtocolRuntime(family, family.create ? family.create() : nullptr, std::move(io), std::move(observer))
15{}
16
17GPSProtocolRuntime::GPSProtocolRuntime(const GPSReceiverFamily& family, std::unique_ptr<GPSFamilyProtocol> protocol,
19 : _family(family)
20 , _io(std::move(io))
21 , _observer(std::move(observer))
22 , _demux(family.stream)
23 , _protocol(std::move(protocol))
24 , _channel(*this)
25 , _context(_channel)
26{
27 if (!_io.read) {
28 _io.read = [](std::span<uint8_t>, GPSDeadline) { return GPSReadResult{GPSReadStatus::Error}; };
29 }
30 if (!_io.write) {
31 _io.write = [](std::span<const uint8_t>, GPSDeadline) { return GPSWriteResult{}; };
32 }
33 if (!_io.setBaudrate) {
34 _io.setBaudrate = [](unsigned) { return GPSBaudStatus::Unsupported; };
35 }
36 if (!_io.clock.wait) {
37 _io.clock.wait = [token = _io.cancelToken](std::chrono::microseconds duration) {
38 const auto stopped = [&token] { return token.isCancelled(); };
39 const auto until = std::chrono::steady_clock::now() + duration;
40 while (!stopped() && std::chrono::steady_clock::now() < until) {
41 std::this_thread::sleep_for(
42 std::min<std::chrono::steady_clock::duration>(until - std::chrono::steady_clock::now(), 20ms));
43 }
44 return !stopped();
45 };
46 }
47 if (!_protocol) {
48 qCWarning(logCategory()) << "No protocol for receiver family" << _family.type;
49 }
50}
51
53
55{
56 return _family.logCategory ? _family.logCategory : &GPSProtocolRuntimeLog;
57}
58
60{
61 _channel._resetError();
62 if (!_protocol) {
63 return false;
64 }
65 _channel._detectedBaud = config.detectedBaud;
66 const bool configured = _protocol->configure(_channel, config, baud);
68 if (configured) {
69 (void) consume({});
70 } else {
71 // Every failed configuration reports a failure, keeping the detail the family left.
72 _channel._describeFailure();
73 _channel.failControl(_channel.errorDetail());
74 }
75 return configured;
76}
77
78bool GPSProtocolRuntime::probe(std::chrono::milliseconds budget)
79{
80 _channel._resetError();
81 if (!_protocol) {
82 return false;
83 }
84 bool answered = false;
85 {
86 const auto scope = _channel.deadlineScope(budget);
87 answered = _protocol->probe(_channel);
88 }
90 return answered;
91}
92
93GPSReceiveUpdates GPSProtocolRuntime::receive(std::chrono::milliseconds timeout)
94{
95 if (!_protocol) {
96 return {};
97 }
98 return _protocol->receive(_channel, timeout);
99}
100
102{
103 if (!_protocol) {
104 return false;
105 }
106 return _protocol->armDecodeOnly(config, _context);
107}
108
110{
111 return _protocol && _protocol->armNavigationDecode(decode, _context);
112}
113
114GPSEventBatch GPSProtocolRuntime::decode(std::span<const uint8_t> bytes)
115{
116 if (!_protocol) {
117 return {};
118 }
119 _protocol->flush(_context);
120 for (const uint8_t byte : bytes) {
121 _demux.push(byte, *this);
122 }
123 _channel._offerRaw(bytes);
124 return _context._takeBatch();
125}
126
127GPSReceiveUpdates GPSProtocolRuntime::consume(std::span<const uint8_t> bytes)
128{
129 return _deliver(decode(bytes));
130}
131
132GPSReceiveUpdates GPSProtocolRuntime::consumeFrame(const GPSFrame& frame)
133{
134 if (!_protocol) {
135 return {};
136 }
137 GPSProtocolRuntime::frame(frame);
138 return _deliver(_context._takeBatch());
139}
140
141GPSReceiveUpdates GPSProtocolRuntime::_deliver(GPSEventBatch batch)
142{
143 if (_observer.decoded) {
144 _observer.decoded(batch);
145 }
146 // A nested consume from the observer may already have refilled the context's storage.
147 _context._recycle(std::move(batch.events));
148 return batch.updates;
149}
150
152{
153 return _protocol && !_context.failed() && _protocol->receiverReady();
154}
155
157{
158 return _protocol ? _protocol->identity().trimmed() : QString();
159}
160
161void GPSProtocolRuntime::frame(const GPSFrame& frame)
162{
163 _context.markUpdates(_protocol->onFrame(frame, _context));
164 _channel._offerFrame(frame);
165}
166
167void GPSProtocolRuntime::_commandFinished(const GPSConfigurationEvidence& result)
168{
169 if (_observer.commandFinished) {
170 _observer.commandFinished(result);
171 }
172}
173
175{
176 return _channel.nowUs();
177}
178
180{
181 return _channel.stream();
182}
183
185{
186 return _channel._replyPending();
187}
188
190{
191 _channel._resolveReply(outcome);
192}
193
194void GPSDecodeContext::offerReply(std::string_view reply)
195{
196 _channel._offerText(reply);
197}
198
200{
201 return _channel.failed();
202}
203
204void GPSDecodeContext::failControl(const QString& detail)
205{
206 _channel.failControl(detail);
207}
208
209GPSReceiveUpdates GPSFamilyProtocol::receive(GPSCommandChannel& channel, std::chrono::milliseconds timeout)
210{
211 const GPSReceiveUpdates updates = channel.receiveCycle(timeout);
212 channel.serviceControls();
213 return updates;
214}
215
static std::atomic< jobject > _context
GPSCommandOutcome
Definition GPSCommand.h:33
const QLoggingCategory &(*)() GPSLogCategory
A family's logging category function, as declared by Q_DECLARE_LOGGING_CATEGORY; usable as qCWarning(...
Config config
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void serviceControls()
Runs the family's streaming services once under SERVICE_TIMEOUT, then flushes. Nested calls do nothin...
uint64_t nowUs() const
GPSReceiveUpdates receiveCycle(std::chrono::milliseconds timeout)
void failControl(GPSProtocolError failure=GPSProtocolError::Protocol)
GPSDeadlineScope deadlineScope(std::chrono::milliseconds timeout)
const QString & errorDetail() const
GPSStreamDemux & stream()
void finishCommand(GPSCommandOutcome outcome)
void markUpdates(GPSReceiveUpdates updates)
Adds updates a decoder reports for a frame without publishing an event, such as protocol activity.
void resolveReply(GPSCommandOutcome outcome)
Resolves the outstanding reply; Pending and later resolutions are ignored.
void offerReply(std::string_view reply)
Passes one decoded text reply to the outstanding text matcher, if any.
void failControl(const QString &detail)
Records a protocol failure described by detail unless a failure is already recorded,...
uint64_t nowUs() const
GPSStreamDemux & stream()
bool replyPending() const
True while a transact() reply is outstanding and unresolved.
virtual void serviceStreaming(GPSCommandChannel &channel)
virtual GPSReceiveUpdates receive(GPSCommandChannel &channel, std::chrono::milliseconds timeout)
One streaming receive. The default runs GPSCommandChannel::receiveCycle() and then the streaming serv...
GPSEventBatch decode(std::span< const uint8_t > bytes)
Decodes bytes without I/O or observer callbacks.
bool armDecodeOnly(const GPSConfig &config)
GPSReceiveUpdates consume(std::span< const uint8_t > bytes)
Decodes bytes and delivers their batch to the observer. An empty span flushes.
GPSReceiveUpdates receive(std::chrono::milliseconds timeout)
bool armNavigationDecode(GPSNavigationDecode decode={})
GPSReceiveUpdates consumeFrame(const GPSFrame &frame)
Decodes one frame another stream framed and delivers its batch to the observer, without flushing.
QString identity() const
Model and firmware reported during configuration, trimmed; empty when unknown.
bool probe(std::chrono::milliseconds budget)
GPSLogCategory logCategory() const
The family's logging category, or the runtime's when the family has none.
GPSProtocolRuntime(const GPSReceiverFamily &family, GPSRuntimeIO io, GPSRuntimeObserver observer={})
Creates the family's protocol with its factory.
bool configure(const GPSConfig &config, unsigned &baud)
void push(uint8_t byte, Handler &handler)
std::function< bool(std::chrono::microseconds)> wait
Waits duration on this clock; false when the wait was cancelled. Empty means a real wait.
Definition GPSClock.h:14
The receiver configuration requested for one configure() attempt.
Evidence from one configuration command, not a claim of physical receiver operation.
Definition GPSCommand.h:46
Events decoded from one span of received bytes, in publication order.
std::vector< GPSProtocolEvent > events
GPSReceiveUpdates updates
What a decode-only runtime decodes from a receiver configured elsewhere (GPSFamilyProtocol::armNaviga...
GPSLogCategory logCategory
std::function< GPSReadResult(std::span< uint8_t >, GPSDeadline)> read
GPSCancelToken cancelToken
Checked before each write and baud change; reads observe it in the transport.
std::function< GPSWriteResult(std::span< const uint8_t >, GPSDeadline)> write
GPSClock clock
Without a wait, the runtime sleeps in short slices until the duration elapses or cancelToken is cance...
std::function< GPSBaudStatus(unsigned)> setBaudrate
Observers of runtime output, invoked synchronously on the runtime thread.
std::function< void(const GPSConfigurationEvidence &)> commandFinished
Every completed command, during configuration and streaming alike.
std::function< void(const GPSEventBatch &)> decoded
Borrowed for the callback; it may decode nested input through GPSProtocolRuntime::consume().