Directory: | ./ |
---|---|
File: | src/ScalarSubscriber.cpp |
Date: | 2024-11-19 17:00:39 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 58 | 126 | 46.0% |
Branches: | 24 | 146 | 16.4% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /***************************************************************************** | ||
2 | * | ||
3 | * Copyright (C) 2012-2022 Florian Pose <fp@igh.de> | ||
4 | * | ||
5 | * This file is part of the QtPdCom library. | ||
6 | * | ||
7 | * The QtPdCom library is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU Lesser General Public License as published by | ||
9 | * the Free Software Foundation, either version 3 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | * The QtPdCom library is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public | ||
15 | * License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public License | ||
18 | * along with the QtPdCom Library. If not, see <http://www.gnu.org/licenses/>. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "ScalarSubscriber.h" | ||
23 | using QtPdCom::ScalarSubscriber; | ||
24 | |||
25 | #include <pdcom5/Process.h> | ||
26 | #include <pdcom5/Exception.h> | ||
27 | #include <pdcom5/Subscriber.h> | ||
28 | #include <pdcom5/Subscription.h> | ||
29 | |||
30 | #include <QStringList> | ||
31 | #include <QTimer> | ||
32 | |||
33 | /****************************************************************************/ | ||
34 | |||
35 | 38 | class QtPdCom::ScalarSubscriber::Impl: public QObject | |
36 | { | ||
37 | Q_OBJECT | ||
38 | |||
39 | public: | ||
40 | 19 | Impl(ScalarSubscriber *parent): | |
41 | parent {parent}, | ||
42 | filterConstant {0.0}, | ||
43 | 19 | timer {nullptr} | |
44 | 19 | {} | |
45 | |||
46 | ScalarSubscriber *const parent; | ||
47 | |||
48 | struct ScalarSubscription; | ||
49 | std::unique_ptr<ScalarSubscription> subscription; | ||
50 | |||
51 | double filterConstant; /**< PT1 filter constant. */ | ||
52 | |||
53 | QTimer *timer; /**< Timer for poll mode. */ | ||
54 | |||
55 | void createTimer(double); | ||
56 | void deleteTimer(); | ||
57 | void startTimer(); | ||
58 | void stopTimer(); | ||
59 | |||
60 | private slots: | ||
61 | void timeout(); | ||
62 | }; | ||
63 | |||
64 | /****************************************************************************/ | ||
65 | |||
66 | 6 | struct QtPdCom::ScalarSubscriber::Impl::ScalarSubscription: | |
67 | public PdCom::Subscriber, | ||
68 | public PdCom::Subscription | ||
69 | { | ||
70 | public: | ||
71 | ✗ | ScalarSubscription( | |
72 | ScalarSubscriber::Impl *parent, | ||
73 | PdCom::Variable pv, | ||
74 | const PdCom::Selector &selector, | ||
75 | ✗ | const Transmission &transmission): | |
76 | ✗ | Subscriber {transmission.toPdCom()}, | |
77 | Subscription {*this, pv, selector}, | ||
78 | parent {parent}, | ||
79 | ✗ | selector {selector} | |
80 | { | ||
81 | #ifdef DEBUG_PD_SCALARSUBSCRIBER | ||
82 | qDebug() << this << __func__ << "var" << pv.getPath().c_str(); | ||
83 | #endif | ||
84 | } | ||
85 | |||
86 | 6 | ScalarSubscription( | |
87 | ScalarSubscriber::Impl *parent, | ||
88 | PdCom::Process *process, | ||
89 | const std::string &path, | ||
90 | const PdCom::Selector &selector, | ||
91 | 6 | const Transmission &transmission): | |
92 | 12 | Subscriber {transmission.toPdCom()}, | |
93 | Subscription {*this, *process, path, selector}, | ||
94 | parent {parent}, | ||
95 |
1/2✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
18 | selector {selector} |
96 | { | ||
97 | #ifdef DEBUG_PD_SCALARSUBSCRIBER | ||
98 | qDebug() << this << __func__ << "path" << path.c_str(); | ||
99 | #endif | ||
100 | 6 | } | |
101 | |||
102 | ✗ | const PdCom::Selector &getSelector() const { return selector; } | |
103 | |||
104 | private: | ||
105 | ScalarSubscriber::Impl *parent; | ||
106 | const PdCom::Selector selector; | ||
107 | |||
108 | 4 | void stateChanged(const PdCom::Subscription &) override | |
109 | { | ||
110 | #ifdef DEBUG_PD_SCALARSUBSCRIBER | ||
111 | QString path; | ||
112 | if (not getVariable().empty()) { | ||
113 | path = getVariable().getPath().data(); | ||
114 | } | ||
115 | qDebug() << this << __func__ << (int) getState() << path; | ||
116 | #endif | ||
117 | |||
118 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | if (getState() == PdCom::Subscription::State::Active) { |
119 | // changed to active. If event mode, poll once. | ||
120 |
2/4✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
|
12 | if (getTransmission() == PdCom::event_mode |
121 |
2/6✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 4 times.
|
12 | or getTransmission() == PdCom::poll_mode) { |
122 | 4 | poll(); // poll once to get initial value | |
123 | } | ||
124 | } | ||
125 | |||
126 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
|
4 | if (getState() != PdCom::Subscription::State::Active) { |
127 | #ifdef DEBUG_PD_SCALARSUBSCRIBER | ||
128 | qDebug() << this << "inactive"; | ||
129 | #endif | ||
130 | ✗ | parent->stopTimer(); | |
131 | } | ||
132 | |||
133 | 4 | parent->parent->stateChange(getState()); | |
134 | 4 | } | |
135 | |||
136 | 7 | void newValues(std::chrono::nanoseconds ts) override | |
137 | { | ||
138 | 7 | parent->parent->newValues(ts); | |
139 | |||
140 |
1/2✗ Branch 5 not taken.
✓ Branch 6 taken 7 times.
|
7 | if (getTransmission() == PdCom::poll_mode) { |
141 | ✗ | parent->startTimer(); | |
142 | } | ||
143 | 7 | } | |
144 | }; | ||
145 | |||
146 | /***************************************************************************** | ||
147 | * Implementation class. | ||
148 | ****************************************************************************/ | ||
149 | |||
150 | ✗ | void QtPdCom::ScalarSubscriber::Impl::createTimer(double interval) | |
151 | { | ||
152 | #ifdef DEBUG_PD_SCALARSUBSCRIBER | ||
153 | qDebug() << this << __func__ << interval; | ||
154 | #endif | ||
155 | |||
156 | ✗ | if (timer) { | |
157 | ✗ | return; | |
158 | } | ||
159 | |||
160 | ✗ | timer = new QTimer(this); | |
161 | ✗ | timer->setSingleShot(true); | |
162 | ✗ | timer->setInterval(interval * 1000.0); | |
163 | ✗ | connect(timer, SIGNAL(timeout()), this, SLOT(timeout())); | |
164 | } | ||
165 | |||
166 | /****************************************************************************/ | ||
167 | |||
168 | 25 | void QtPdCom::ScalarSubscriber::Impl::deleteTimer() | |
169 | { | ||
170 | #ifdef DEBUG_PD_SCALARSUBSCRIBER | ||
171 | qDebug() << this << __func__; | ||
172 | #endif | ||
173 | |||
174 |
1/2✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
|
25 | if (not timer) { |
175 | 25 | return; | |
176 | } | ||
177 | |||
178 | ✗ | delete timer; | |
179 | ✗ | timer = nullptr; | |
180 | } | ||
181 | |||
182 | /****************************************************************************/ | ||
183 | |||
184 | ✗ | void QtPdCom::ScalarSubscriber::Impl::startTimer() | |
185 | { | ||
186 | #ifdef DEBUG_PD_SCALARSUBSCRIBER | ||
187 | qDebug() << this << __func__; | ||
188 | #endif | ||
189 | |||
190 | ✗ | if (not timer) { | |
191 | // qWarning() << "No timer to start"; | ||
192 | ✗ | return; | |
193 | } | ||
194 | |||
195 | ✗ | timer->start(); | |
196 | } | ||
197 | |||
198 | /****************************************************************************/ | ||
199 | |||
200 | ✗ | void QtPdCom::ScalarSubscriber::Impl::stopTimer() | |
201 | { | ||
202 | #ifdef DEBUG_PD_SCALARSUBSCRIBER | ||
203 | qDebug() << this << __func__; | ||
204 | #endif | ||
205 | |||
206 | ✗ | if (not timer) { | |
207 | // qWarning() << "No timer to stop"; | ||
208 | ✗ | return; | |
209 | } | ||
210 | |||
211 | ✗ | timer->stop(); | |
212 | } | ||
213 | |||
214 | /****************************************************************************/ | ||
215 | |||
216 | ✗ | void QtPdCom::ScalarSubscriber::Impl::timeout() | |
217 | { | ||
218 | #ifdef DEBUG_PD_SCALARSUBSCRIBER | ||
219 | qDebug() << this << __func__; | ||
220 | #endif | ||
221 | |||
222 | try { | ||
223 | ✗ | subscription->poll(); | |
224 | } | ||
225 | ✗ | catch (std::exception &e) { | |
226 | ✗ | qWarning() << "Failed to poll:" << e.what(); | |
227 | } | ||
228 | } | ||
229 | |||
230 | /***************************************************************************** | ||
231 | * Public class. | ||
232 | ****************************************************************************/ | ||
233 | |||
234 | /** Constructor. | ||
235 | */ | ||
236 | 19 | ScalarSubscriber::ScalarSubscriber(): | |
237 | scale {1.0}, | ||
238 | offset {0.0}, | ||
239 |
1/2✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
|
19 | impl {std::unique_ptr<Impl> {new Impl {this}}} |
240 | 19 | {} | |
241 | |||
242 | /****************************************************************************/ | ||
243 | |||
244 | /** Destructor. | ||
245 | */ | ||
246 | 38 | ScalarSubscriber::~ScalarSubscriber() | |
247 | { | ||
248 | 19 | clearVariable(); | |
249 | 19 | } | |
250 | |||
251 | /****************************************************************************/ | ||
252 | |||
253 | /** Subscribes to a ProcessVariable. | ||
254 | */ | ||
255 | ✗ | void ScalarSubscriber::setVariable( | |
256 | PdCom::Variable pv, | ||
257 | const PdCom::Selector &selector, | ||
258 | const Transmission &transmission, | ||
259 | double scale, | ||
260 | double offset, | ||
261 | double tau) | ||
262 | { | ||
263 | ✗ | clearVariable(); | |
264 | |||
265 | ✗ | if (pv.empty()) { | |
266 | ✗ | return; | |
267 | } | ||
268 | |||
269 | ✗ | this->scale = scale; | |
270 | ✗ | this->offset = offset; | |
271 | |||
272 | ✗ | if (tau > 0.0 | |
273 | ✗ | and (transmission.isContinuous() or transmission.isPoll())) { | |
274 | ✗ | impl->filterConstant = transmission.getInterval() / tau; | |
275 | } | ||
276 | else { | ||
277 | ✗ | impl->filterConstant = 0.0; | |
278 | } | ||
279 | |||
280 | try { | ||
281 | ✗ | impl->subscription = std::unique_ptr<Impl::ScalarSubscription>( | |
282 | new Impl::ScalarSubscription( | ||
283 | ✗ | impl.get(), | |
284 | pv, | ||
285 | selector, | ||
286 | ✗ | transmission)); | |
287 | } | ||
288 | ✗ | catch (PdCom::Exception &e) { | |
289 | ✗ | qCritical() << QString("Failed to subscribe to variable" | |
290 | " \"%1\" with transmission %2: %3") | ||
291 | ✗ | .arg(QString(pv.getPath().c_str())) | |
292 | ✗ | .arg(transmission.toString()) | |
293 | ✗ | .arg(e.what()); | |
294 | ✗ | return; | |
295 | } | ||
296 | |||
297 | ✗ | if (transmission.isPoll()) { | |
298 | ✗ | impl->createTimer(transmission.getInterval()); | |
299 | } | ||
300 | ✗ | pv.getProcess()->callPendingCallbacks(); | |
301 | } | ||
302 | |||
303 | /****************************************************************************/ | ||
304 | |||
305 | /** Subscribes to a ProcessVariable. | ||
306 | */ | ||
307 | 6 | void ScalarSubscriber::setVariable( | |
308 | PdCom::Process *process, | ||
309 | const QString &path, | ||
310 | const PdCom::Selector &selector, | ||
311 | const Transmission &transmission, | ||
312 | double scale, | ||
313 | double offset, | ||
314 | double tau) | ||
315 | { | ||
316 | 6 | clearVariable(); | |
317 | |||
318 |
3/6✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
|
6 | if (path.isEmpty() or not process) { |
319 | ✗ | return; | |
320 | } | ||
321 | |||
322 | 6 | this->scale = scale; | |
323 | 6 | this->offset = offset; | |
324 | |||
325 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (tau > 0.0 |
326 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
6 | and (transmission.isContinuous() or transmission.isPoll())) { |
327 | ✗ | impl->filterConstant = transmission.getInterval() / tau; | |
328 | } | ||
329 | else { | ||
330 | 6 | impl->filterConstant = 0.0; | |
331 | } | ||
332 | |||
333 | try { | ||
334 | 30 | impl->subscription = std::unique_ptr<Impl::ScalarSubscription>( | |
335 | new Impl::ScalarSubscription( | ||
336 | 6 | impl.get(), | |
337 | process, | ||
338 |
1/4✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
12 | path.toStdString(), |
339 | selector, | ||
340 |
2/4✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
|
12 | transmission)); |
341 | } | ||
342 | ✗ | catch (PdCom::Exception &e) { | |
343 | ✗ | qCritical() << QString("Failed to subscribe to variable" | |
344 | " \"%1\" with sample time %2: %3") | ||
345 | ✗ | .arg(path) | |
346 | ✗ | .arg(transmission.toString()) | |
347 | ✗ | .arg(e.what()); | |
348 | ✗ | return; | |
349 | } | ||
350 | |||
351 |
2/6✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
|
6 | if (transmission.isPoll() and not impl->timer) { |
352 | ✗ | impl->createTimer(transmission.getInterval()); | |
353 | } | ||
354 | } | ||
355 | |||
356 | /****************************************************************************/ | ||
357 | |||
358 | /** Unsubscribe from a Variable. | ||
359 | */ | ||
360 | 25 | void ScalarSubscriber::clearVariable() | |
361 | { | ||
362 | #ifdef DEBUG_PD_SCALARSUBSCRIBER | ||
363 | qDebug() << impl.get() << __func__; | ||
364 | #endif | ||
365 | |||
366 |
2/2✓ Branch 2 taken 6 times.
✓ Branch 3 taken 19 times.
|
25 | if (impl->subscription) { |
367 | 6 | impl->subscription.reset(); | |
368 | 6 | stateChange(PdCom::Subscription::State::Invalid); | |
369 | } | ||
370 | |||
371 | 25 | impl->deleteTimer(); | |
372 | 25 | } | |
373 | |||
374 | /****************************************************************************/ | ||
375 | |||
376 | ✗ | bool ScalarSubscriber::hasVariable() const | |
377 | { | ||
378 | ✗ | return impl->subscription | |
379 | ✗ | and not impl->subscription->getVariable().empty(); | |
380 | } | ||
381 | |||
382 | /****************************************************************************/ | ||
383 | |||
384 | 10 | void ScalarSubscriber::stateChange(PdCom::Subscription::State) | |
385 | 10 | {} | |
386 | |||
387 | /****************************************************************************/ | ||
388 | |||
389 | ✗ | double ScalarSubscriber::getFilterConstant() const | |
390 | { | ||
391 | ✗ | return impl->filterConstant; | |
392 | } | ||
393 | |||
394 | /****************************************************************************/ | ||
395 | |||
396 | 7 | PdCom::Variable ScalarSubscriber::getVariable() const | |
397 | { | ||
398 |
1/2✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
7 | if (impl->subscription) { |
399 | 7 | return impl->subscription->getVariable(); | |
400 | } | ||
401 | else { | ||
402 | ✗ | return PdCom::Variable(); | |
403 | } | ||
404 | } | ||
405 | |||
406 | /****************************************************************************/ | ||
407 | |||
408 | 7 | const void *ScalarSubscriber::getData() const | |
409 | { | ||
410 |
1/2✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
7 | if (impl->subscription) { |
411 | 7 | return impl->subscription->getData(); | |
412 | } | ||
413 | else { | ||
414 | ✗ | return nullptr; | |
415 | } | ||
416 | } | ||
417 | |||
418 | /****************************************************************************/ | ||
419 | |||
420 | ✗ | const PdCom::Selector &ScalarSubscriber::getSelector() const | |
421 | { | ||
422 | ✗ | return impl->subscription->getSelector(); | |
423 | } | ||
424 | |||
425 | /****************************************************************************/ | ||
426 | |||
427 | // Tell qmake, that there are subclasses of QObject defined here and MOC must | ||
428 | // be run on this file. | ||
429 | #include "ScalarSubscriber.moc" | ||
430 | |||
431 | /****************************************************************************/ | ||
432 |