GCC Code Coverage Report


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