GCC Code Coverage Report


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