GCC Code Coverage Report


Directory: ./
File: src/ScalarSubscriber.cpp
Date: 2026-09-08 09:09:37
Exec Total Coverage
Lines: 58 132 43.9%
Branches: 24 156 15.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 62 class QtPdCom::ScalarSubscriber::Impl: public QObject
36 {
37 Q_OBJECT
38
39 public:
40 31 Impl(ScalarSubscriber *parent):
41 parent {parent},
42 filterConstant {0.0},
43 31 timer {nullptr}
44 31 {}
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 12 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 << "trans" << transmission.toString();
84 #endif
85 }
86
87 12 ScalarSubscription(
88 ScalarSubscriber::Impl *parent,
89 PdCom::Process *process,
90 const std::string &path,
91 const PdCom::Selector &selector,
92 12 const Transmission &transmission):
93 24 Subscriber {transmission.toPdCom()},
94 Subscription {*this, *process, path, selector},
95 parent {parent},
96
1/2
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
36 selector {selector}
97 {
98 #ifdef DEBUG_PD_SCALARSUBSCRIBER
99 qDebug() << this << __func__ << "path" << path.c_str() << "trans"
100 << transmission.toString();
101 #endif
102 12 }
103
104 ✗ const PdCom::Selector &getSelector() const { return selector; }
105
106 private:
107 ScalarSubscriber::Impl *parent;
108 const PdCom::Selector selector;
109
110 5 void stateChanged(const PdCom::Subscription &) override
111 {
112 #ifdef DEBUG_PD_SCALARSUBSCRIBER
113 QString path;
114 if (not getVariable().empty()) {
115 path = getVariable().getPath().data();
116 }
117 qDebug() << this << __func__ << (int) getState() << path;
118 #endif
119
120
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 if (getState() == PdCom::Subscription::State::Active) {
121 // changed to active. If event mode, poll once.
122
2/4
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
15 if (getTransmission() == PdCom::event_mode
123
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 5 times.
15 or getTransmission() == PdCom::poll_mode) {
124 5 poll(); // poll once to get initial value
125 }
126 }
127
128
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if (getState() != PdCom::Subscription::State::Active) {
129 #ifdef DEBUG_PD_SCALARSUBSCRIBER
130 qDebug() << this << "inactive";
131 #endif
132 ✗ parent->stopTimer();
133 }
134
135 5 parent->parent->stateChange(getState());
136 5 }
137
138 8 void newValues(std::chrono::nanoseconds ts) override
139 {
140 8 parent->parent->newValues(ts);
141
142
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
8 if (getTransmission() == PdCom::poll_mode) {
143 ✗ parent->startTimer();
144 }
145 8 }
146 };
147
148 /*****************************************************************************
149 * Implementation class.
150 ****************************************************************************/
151
152 ✗ void QtPdCom::ScalarSubscriber::Impl::createTimer(double interval)
153 {
154 #ifdef DEBUG_PD_SCALARSUBSCRIBER
155 qDebug() << this << __func__ << interval;
156 #endif
157
158 ✗ if (timer) {
159 ✗ return;
160 }
161
162 ✗ timer = new QTimer(this);
163 ✗ timer->setSingleShot(true);
164 ✗ timer->setInterval(interval * 1000.0);
165 ✗ connect(timer, SIGNAL(timeout()), this, SLOT(timeout()));
166 }
167
168 /****************************************************************************/
169
170 43 void QtPdCom::ScalarSubscriber::Impl::deleteTimer()
171 {
172 #ifdef DEBUG_PD_SCALARSUBSCRIBER
173 qDebug() << this << __func__;
174 #endif
175
176
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 if (not timer) {
177 43 return;
178 }
179
180 ✗ delete timer;
181 ✗ timer = nullptr;
182 }
183
184 /****************************************************************************/
185
186 ✗ void QtPdCom::ScalarSubscriber::Impl::startTimer()
187 {
188 #ifdef DEBUG_PD_SCALARSUBSCRIBER
189 qDebug() << this << __func__;
190 #endif
191
192 ✗ if (not timer or timer->interval() == 0) {
193 // qWarning() << "No timer to start";
194 ✗ return;
195 }
196
197 ✗ timer->start();
198 }
199
200 /****************************************************************************/
201
202 ✗ void QtPdCom::ScalarSubscriber::Impl::stopTimer()
203 {
204 #ifdef DEBUG_PD_SCALARSUBSCRIBER
205 qDebug() << this << __func__;
206 #endif
207
208 ✗ if (not timer) {
209 // qWarning() << "No timer to stop";
210 ✗ return;
211 }
212
213 ✗ timer->stop();
214 }
215
216 /****************************************************************************/
217
218 ✗ void QtPdCom::ScalarSubscriber::Impl::timeout()
219 {
220 #ifdef DEBUG_PD_SCALARSUBSCRIBER
221 qDebug() << this << __func__;
222 #endif
223
224 try {
225 ✗ subscription->poll();
226 }
227 ✗ catch (std::exception &e) {
228 ✗ qWarning() << "Failed to poll:" << e.what();
229 }
230 }
231
232 /*****************************************************************************
233 * Public class.
234 ****************************************************************************/
235
236 /** Constructor.
237 */
238 31 ScalarSubscriber::ScalarSubscriber():
239 scale {1.0},
240 offset {0.0},
241
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 impl {std::unique_ptr<Impl> {new Impl {this}}}
242 31 {}
243
244 /****************************************************************************/
245
246 /** Destructor.
247 */
248 62 ScalarSubscriber::~ScalarSubscriber()
249 {
250 31 clearVariable();
251 31 }
252
253 /****************************************************************************/
254
255 /** Subscribes to a ProcessVariable.
256 */
257 ✗ void ScalarSubscriber::setVariable(
258 PdCom::Variable pv,
259 const PdCom::Selector &selector,
260 const Transmission &transmission,
261 double scale,
262 double offset,
263 double tau)
264 {
265 ✗ clearVariable();
266
267 ✗ if (pv.empty()) {
268 ✗ return;
269 }
270
271 ✗ this->scale = scale;
272 ✗ this->offset = offset;
273
274 ✗ if (tau > 0.0
275 ✗ and (transmission.isContinuous() or transmission.isPoll())) {
276 ✗ impl->filterConstant = transmission.getInterval() / tau;
277 }
278 else {
279 ✗ impl->filterConstant = 0.0;
280 }
281
282 try {
283 ✗ impl->subscription = std::unique_ptr<Impl::ScalarSubscription>(
284 new Impl::ScalarSubscription(
285 ✗ impl.get(),
286 pv,
287 selector,
288 ✗ transmission));
289 }
290 ✗ catch (PdCom::Exception &e) {
291 ✗ qCritical() << QString("Failed to subscribe to variable"
292 " \"%1\" with transmission %2: %3")
293 ✗ .arg(QString(pv.getPath().c_str()))
294 ✗ .arg(transmission.toString())
295 ✗ .arg(e.what());
296 ✗ return;
297 }
298
299 ✗ if (transmission.isPoll()) {
300 ✗ impl->createTimer(transmission.getInterval());
301 }
302 ✗ pv.getProcess()->callPendingCallbacks();
303 }
304
305 /****************************************************************************/
306
307 /** Subscribes to a ProcessVariable.
308 */
309 12 void ScalarSubscriber::setVariable(
310 PdCom::Process *process,
311 const QString &path,
312 const PdCom::Selector &selector,
313 const Transmission &transmission,
314 double scale,
315 double offset,
316 double tau)
317 {
318 12 clearVariable();
319
320
3/6
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
12 if (path.isEmpty() or not process) {
321 ✗ return;
322 }
323
324 12 this->scale = scale;
325 12 this->offset = offset;
326
327
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (tau > 0.0
328
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
12 and (transmission.isContinuous() or transmission.isPoll())) {
329 ✗ impl->filterConstant = transmission.getInterval() / tau;
330 }
331 else {
332 12 impl->filterConstant = 0.0;
333 }
334
335 try {
336 60 impl->subscription = std::unique_ptr<Impl::ScalarSubscription>(
337 new Impl::ScalarSubscription(
338 12 impl.get(),
339 process,
340
1/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
24 path.toStdString(),
341 selector,
342
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
24 transmission));
343 }
344 ✗ catch (PdCom::Exception &e) {
345 ✗ qCritical() << QString("Failed to subscribe to variable"
346 " \"%1\" with sample time %2: %3")
347 ✗ .arg(path)
348 ✗ .arg(transmission.toString())
349 ✗ .arg(e.what());
350 ✗ return;
351 }
352
353
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 12 times.
12 if (transmission.isPoll() and not impl->timer) {
354 ✗ impl->createTimer(transmission.getInterval());
355 }
356 }
357
358 /****************************************************************************/
359
360 /** Unsubscribe from a Variable.
361 */
362 43 void ScalarSubscriber::clearVariable()
363 {
364 #ifdef DEBUG_PD_SCALARSUBSCRIBER
365 qDebug() << impl.get() << __func__;
366 #endif
367
368
2/2
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 31 times.
43 if (impl->subscription) {
369 12 impl->subscription.reset();
370 12 stateChange(PdCom::Subscription::State::Invalid);
371 }
372
373 43 impl->deleteTimer();
374 43 }
375
376 /****************************************************************************/
377
378 ✗ bool ScalarSubscriber::hasVariable() const
379 {
380 ✗ return impl->subscription
381 ✗ and not impl->subscription->getVariable().empty();
382 }
383
384 /****************************************************************************/
385
386 17 void ScalarSubscriber::stateChange(PdCom::Subscription::State)
387 17 {}
388
389 /****************************************************************************/
390
391 ✗ double ScalarSubscriber::getFilterConstant() const
392 {
393 ✗ return impl->filterConstant;
394 }
395
396 /****************************************************************************/
397
398 8 PdCom::Variable ScalarSubscriber::getVariable() const
399 {
400
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 if (impl->subscription) {
401 8 return impl->subscription->getVariable();
402 }
403 else {
404 ✗ return PdCom::Variable();
405 }
406 }
407
408 /****************************************************************************/
409
410 8 const void *ScalarSubscriber::getData() const
411 {
412
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 if (impl->subscription) {
413 8 return impl->subscription->getData();
414 }
415 else {
416 ✗ return nullptr;
417 }
418 }
419
420 /****************************************************************************/
421
422 ✗ bool QtPdCom::ScalarSubscriber::poll()
423 {
424 ✗ if (!impl->subscription
425 ✗ || impl->subscription->getState()
426 != PdCom::Subscription::State::Active) {
427 ✗ return false;
428 }
429 ✗ impl->subscription->poll();
430 ✗ return true;
431 }
432
433 /****************************************************************************/
434
435 ✗ const PdCom::Selector &ScalarSubscriber::getSelector() const
436 {
437 ✗ return impl->subscription->getSelector();
438 }
439
440 /****************************************************************************/
441
442 // Tell qmake, that there are subclasses of QObject defined here and MOC must
443 // be run on this file.
444 #include "ScalarSubscriber.moc"
445
446 /****************************************************************************/
447