GCC Code Coverage Report


Directory: ./
File: qtpdcom/src/ScalarSubscriber.cpp
Date: 2025-06-08 04:08:54
Exec Total Coverage
Lines: 97 134 72.4%
Branches: 46 150 30.7%

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 4 class QtPdCom::ScalarSubscriber::Impl: public QObject
36 {
37 Q_OBJECT
38
39 public:
40 2 Impl(ScalarSubscriber *parent):
41 parent {parent},
42 filterConstant {0.0},
43 2 timer {nullptr}
44 2 {}
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 2 struct QtPdCom::ScalarSubscriber::Impl::ScalarSubscription:
67 public PdCom::Subscriber,
68 public PdCom::Subscription
69 {
70 public:
71 1 ScalarSubscription(
72 ScalarSubscriber::Impl *parent,
73 PdCom::Variable pv,
74 const PdCom::Selector &selector,
75 1 const Transmission &transmission):
76 2 Subscriber {transmission.toPdCom()},
77 Subscription {*this, pv, selector},
78 parent {parent},
79 3 selector {selector}
80 {
81 #ifdef DEBUG_PD_SCALARSUBSCRIBER
82 qDebug() << this << __func__ << "var" << pv.getPath().c_str()
83 << "trans" << transmission.toString();
84 #endif
85 1 }
86
87 1 ScalarSubscription(
88 ScalarSubscriber::Impl *parent,
89 PdCom::Process *process,
90 const std::string &path,
91 const PdCom::Selector &selector,
92 1 const Transmission &transmission):
93 2 Subscriber {transmission.toPdCom()},
94 Subscription {*this, *process, path, selector},
95 parent {parent},
96 3 selector {selector}
97 {
98 #ifdef DEBUG_PD_SCALARSUBSCRIBER
99 qDebug() << this << __func__ << "path" << path.c_str() << "trans"
100 << transmission.toString();
101 #endif
102 1 }
103
104 const PdCom::Selector &getSelector() const { return selector; }
105
106 private:
107 ScalarSubscriber::Impl *parent;
108 const PdCom::Selector selector;
109
110 4 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
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
4 if (getState() == PdCom::Subscription::State::Active) {
121 // changed to active. If event mode, poll once.
122
3/4
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
4 if (getTransmission() == PdCom::event_mode
123
4/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
4 or getTransmission() == PdCom::poll_mode) {
124 1 poll(); // poll once to get initial value
125 }
126 }
127
128
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
4 if (getState() != PdCom::Subscription::State::Active) {
129 #ifdef DEBUG_PD_SCALARSUBSCRIBER
130 qDebug() << this << "inactive";
131 #endif
132 2 parent->stopTimer();
133 }
134
135 4 parent->parent->stateChange(getState());
136 4 }
137
138 12 void newValues(std::chrono::nanoseconds ts) override
139 {
140 12 parent->parent->newValues(ts);
141
142
2/2
✓ Branch 10 taken 4 times.
✓ Branch 11 taken 8 times.
12 if (getTransmission() == PdCom::poll_mode) {
143 4 parent->startTimer();
144 }
145 12 }
146 };
147
148 /*****************************************************************************
149 * Implementation class.
150 ****************************************************************************/
151
152 1 void QtPdCom::ScalarSubscriber::Impl::createTimer(double interval)
153 {
154 #ifdef DEBUG_PD_SCALARSUBSCRIBER
155 qDebug() << this << __func__ << interval;
156 #endif
157
158
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 if (timer) {
159 return;
160 }
161
162
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 timer = new QTimer(this);
163 1 timer->setSingleShot(true);
164 1 timer->setInterval(interval * 1000.0);
165 1 connect(timer, SIGNAL(timeout()), this, SLOT(timeout()));
166 }
167
168 /****************************************************************************/
169
170 4 void QtPdCom::ScalarSubscriber::Impl::deleteTimer()
171 {
172 #ifdef DEBUG_PD_SCALARSUBSCRIBER
173 qDebug() << this << __func__;
174 #endif
175
176
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 times.
4 if (not timer) {
177 3 return;
178 }
179
180
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 delete timer;
181 1 timer = nullptr;
182 }
183
184 /****************************************************************************/
185
186 4 void QtPdCom::ScalarSubscriber::Impl::startTimer()
187 {
188 #ifdef DEBUG_PD_SCALARSUBSCRIBER
189 qDebug() << this << __func__;
190 #endif
191
192
3/6
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 4 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 4 times.
4 if (not timer or timer->interval() == 0) {
193 // qWarning() << "No timer to start";
194 return;
195 }
196
197 4 timer->start();
198 }
199
200 /****************************************************************************/
201
202 2 void QtPdCom::ScalarSubscriber::Impl::stopTimer()
203 {
204 #ifdef DEBUG_PD_SCALARSUBSCRIBER
205 qDebug() << this << __func__;
206 #endif
207
208
2/2
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
2 if (not timer) {
209 // qWarning() << "No timer to stop";
210 1 return;
211 }
212
213 1 timer->stop();
214 }
215
216 /****************************************************************************/
217
218 3 void QtPdCom::ScalarSubscriber::Impl::timeout()
219 {
220 #ifdef DEBUG_PD_SCALARSUBSCRIBER
221 qDebug() << this << __func__;
222 #endif
223
224 try {
225
1/2
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 subscription->poll();
226 }
227 catch (std::exception &e) {
228 qWarning() << "Failed to poll:" << e.what();
229 }
230 3 }
231
232 /*****************************************************************************
233 * Public class.
234 ****************************************************************************/
235
236 /** Constructor.
237 */
238 2 ScalarSubscriber::ScalarSubscriber():
239 scale {1.0},
240 offset {0.0},
241
1/2
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
2 impl {std::unique_ptr<Impl> {new Impl {this}}}
242 2 {}
243
244 /****************************************************************************/
245
246 /** Destructor.
247 */
248 4 ScalarSubscriber::~ScalarSubscriber()
249 {
250 2 clearVariable();
251 2 }
252
253 /****************************************************************************/
254
255 /** Subscribes to a ProcessVariable.
256 */
257 1 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 1 clearVariable();
266
267
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (pv.empty()) {
268 return;
269 }
270
271 1 this->scale = scale;
272 1 this->offset = offset;
273
274
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (tau > 0.0
275
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
1 and (transmission.isContinuous() or transmission.isPoll())) {
276 impl->filterConstant = transmission.getInterval() / tau;
277 }
278 else {
279 1 impl->filterConstant = 0.0;
280 }
281
282 try {
283
0/2
✗ Branch 12 not taken.
✗ Branch 13 not taken.
4 impl->subscription = std::unique_ptr<Impl::ScalarSubscription>(
284 new Impl::ScalarSubscription(
285 1 impl.get(),
286 pv,
287 selector,
288
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
4 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
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (transmission.isPoll()) {
300 impl->createTimer(transmission.getInterval());
301 }
302 1 pv.getProcess()->callPendingCallbacks();
303 }
304
305 /****************************************************************************/
306
307 /** Subscribes to a ProcessVariable.
308 */
309 1 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 1 clearVariable();
319
320
3/6
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
1 if (path.isEmpty() or not process) {
321 return;
322 }
323
324 1 this->scale = scale;
325 1 this->offset = offset;
326
327
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (tau > 0.0
328
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
1 and (transmission.isContinuous() or transmission.isPoll())) {
329 impl->filterConstant = transmission.getInterval() / tau;
330 }
331 else {
332 1 impl->filterConstant = 0.0;
333 }
334
335 try {
336 5 impl->subscription = std::unique_ptr<Impl::ScalarSubscription>(
337 new Impl::ScalarSubscription(
338 1 impl.get(),
339 process,
340
1/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
2 path.toStdString(),
341 selector,
342
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
4 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
3/6
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
1 if (transmission.isPoll() and not impl->timer) {
354 1 impl->createTimer(transmission.getInterval());
355 }
356 }
357
358 /****************************************************************************/
359
360 /** Unsubscribe from a Variable.
361 */
362 4 void ScalarSubscriber::clearVariable()
363 {
364 #ifdef DEBUG_PD_SCALARSUBSCRIBER
365 qDebug() << impl.get() << __func__;
366 #endif
367
368
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
4 if (impl->subscription) {
369 2 impl->subscription.reset();
370 2 stateChange(PdCom::Subscription::State::Invalid);
371 }
372
373 4 impl->deleteTimer();
374 4 }
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 6 void ScalarSubscriber::stateChange(PdCom::Subscription::State)
387 6 {}
388
389 /****************************************************************************/
390
391 double ScalarSubscriber::getFilterConstant() const
392 {
393 return impl->filterConstant;
394 }
395
396 /****************************************************************************/
397
398 12 PdCom::Variable ScalarSubscriber::getVariable() const
399 {
400
1/2
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
12 if (impl->subscription) {
401 12 return impl->subscription->getVariable();
402 }
403 else {
404 return PdCom::Variable();
405 }
406 }
407
408 /****************************************************************************/
409
410 12 const void *ScalarSubscriber::getData() const
411 {
412
1/2
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
12 if (impl->subscription) {
413 12 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