GCC Code Coverage Report


Directory: ./
File: src/SubDevice.cpp
Date: 2026-09-23 16:22:15
Exec Total Coverage
Lines: 119 251 47.4%
Branches: 134 1200 11.2%

Line Branch Exec Source
1 /*****************************************************************************
2 *
3 * This file is part of the ecapp library (EtherCAT application devices).
4 *
5 * Copyright (C) 2026 Florian Pose <fp@igh.de>
6 *
7 * The ecapp library is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation, version 3 of the
10 * License.
11 *
12 * The ecapp library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with the ecapp library. If not, see
19 * <https://www.gnu.org/licenses/>.
20 *
21 ****************************************************************************/
22
23 #include "ecapp/SubDevice.h"
24
25 #include "ecapp/Domain.h"
26 #include "ecapp/Exceptions.h"
27
28 #include <map>
29 #include <memory>
30 #include <pdserv.h>
31 #include <sstream>
32 #include <stdexcept>
33 #include <string>
34 #include <type_traits>
35 #include <utility>
36 #include <variant>
37
38 using std::size_t;
39 using std::string;
40 using std::stringstream;
41
42 /****************************************************************************/
43
44 namespace {
45
46 /** The pdserv type identifier (pd_*_T) matching a ChannelValue's
47 * currently active alternative. */
48 ✗ int pdservTypeOf(const EcApp::ChannelValue &v)
49 {
50 ✗ return std::visit(
51 ✗ [](auto &&value) -> int {
52 using T = std::decay_t<decltype(value)>;
53 if constexpr (std::is_same_v<T, bool>) {
54 ✗ return pd_boolean_T;
55 }
56 else if constexpr (std::is_same_v<T, uint8_t>) {
57 ✗ return pd_uint8_T;
58 }
59 else if constexpr (std::is_same_v<T, int8_t>) {
60 ✗ return pd_sint8_T;
61 }
62 else if constexpr (std::is_same_v<T, uint16_t>) {
63 ✗ return pd_uint16_T;
64 }
65 else if constexpr (std::is_same_v<T, int16_t>) {
66 ✗ return pd_sint16_T;
67 }
68 else if constexpr (std::is_same_v<T, uint32_t>) {
69 ✗ return pd_uint32_T;
70 }
71 else if constexpr (std::is_same_v<T, int32_t>) {
72 ✗ return pd_sint32_T;
73 }
74 else if constexpr (std::is_same_v<T, float>) {
75 ✗ return pd_single_T;
76 }
77 else { // double
78 ✗ return pd_double_T;
79 }
80 },
81 ✗ v);
82 }
83
84 /** `base`, offset by `index` elements of whatever type `tag` is
85 * currently tagging -- `base` must actually point at an array of that
86 * type (guaranteed by registerInputChannel()/registerOutputChannel()/
87 * registerParameter(), the only places that ever populate the tables
88 * this is read back from). */
89 ✗ void *offsetOf(void *base, const EcApp::ChannelValue &tag, std::size_t index)
90 {
91 ✗ return std::visit(
92 ✗ [&](auto &&value) -> void * {
93 using T = std::decay_t<decltype(value)>;
94 ✗ return static_cast<T *>(base) + index;
95 },
96 ✗ tag);
97 }
98
99 /** Address of the alternative currently active in `v`. Only valid
100 * while `v` keeps that same alternative (a differently-indexed
101 * assignment could reallocate/retag it) -- callers of the
102 * ChannelValue overloads of registerInputChannel()/
103 * registerOutputChannel()/registerParameter() are documented to
104 * uphold that. */
105 9 void *addressOf(EcApp::ChannelValue &v)
106 {
107
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
18 return std::visit([](auto &value) -> void * { return &value; }, v);
108 }
109
110 } // namespace
111
112 /****************************************************************************/
113
114 namespace EcApp {
115
116 /** Everything SubDevice needs to store privately -- see the doc
117 * comment on SubDevice::impl in SubDevice.h for why this is a Pimpl
118 * rather than direct members. */
119 20 struct SubDevice::Impl
120 {
121 20 Impl(Domain *domainOut,
122 Domain *domainIn,
123 pdserv *pdServ,
124 pdtask *task,
125 20 std::string prefix) :
126 domainOut(domainOut),
127 domainIn(domainIn),
128 pdServ(pdServ),
129 task(task),
130 20 prefix(std::move(prefix))
131 20 {}
132
133 Domain *domainOut;
134 Domain *domainIn;
135 pdserv *pdServ;
136 pdtask *task;
137 std::string prefix;
138
139 std::map<std::string, std::vector<std::string>> inputAliases;
140 std::map<std::string, std::vector<std::string>> outputAliases;
141
142 // Address + type tag of a kind registered through
143 // registerInputChannel()/registerOutputChannel(), for setAlias()'s
144 // use; absent for a kind that hasn't been migrated to it yet.
145 std::map<std::string, std::pair<void *, ChannelValue>> inputChannelData;
146 std::map<std::string, std::pair<void *, ChannelValue>> outputChannelData;
147
148 57 struct ParamEntry
149 {
150 std::string name;
151 void *data;
152 ChannelValue type;
153 unsigned int mode;
154 };
155 // Keyed by the associated channel kind's name (see
156 // registerParameter()); one kind may have several parameters
157 // registered against it (e.g. "Gain" and "Offset" both against
158 // "Input"), hence multimap.
159 std::multimap<std::string, ParamEntry> paramsByKind;
160 };
161
162 /****************************************************************************/
163
164 20 SubDevice::SubDevice(
165 Domain *domainOut,
166 Domain *domainIn,
167 pdserv *pdServ,
168 pdtask *task,
169 20 std::string prefix) :
170 impl(std::make_unique<Impl>(
171 domainOut,
172 domainIn,
173 pdServ,
174 task,
175 20 std::move(prefix)))
176 {
177
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 6 times.
20 if (impl->domainOut) {
178
1/2
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 impl->domainOut->addOutputDevice(this);
179 }
180
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 6 times.
20 if (impl->domainIn) {
181
1/2
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 impl->domainIn->addInputDevice(this);
182 }
183 20 }
184
185 /****************************************************************************/
186
187 40 SubDevice::~SubDevice()
188 {
189
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 6 times.
20 if (impl->domainOut) {
190 14 impl->domainOut->removeOutputDevice(this);
191 }
192
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 6 times.
20 if (impl->domainIn) {
193 14 impl->domainIn->removeInputDevice(this);
194 }
195 20 }
196
197 /****************************************************************************/
198
199 22 size_t SubDevice::inputIndex(const string &kind, unsigned int index) const
200 {
201 22 size_t position = 0;
202
3/4
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 80 times.
✓ Branch 7 taken 2 times.
82 for (const auto &ck : inputKinds()) {
203
2/2
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 60 times.
80 if (ck.name == kind) {
204
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (index < ck.count) {
205 40 return position + index;
206 }
207 ✗ break;
208 }
209 60 position += ck.count;
210 }
211
212
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 stringstream err;
213
5/10
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
2 err << "Unknown input channel \"" << kind << "\" index " << index << ".";
214
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
2 throw UnknownChannel(err.str());
215 }
216
217 /****************************************************************************/
218
219 4 size_t SubDevice::inputIndex(unsigned int index) const
220 {
221
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
4 return inputIndex(soleInputKind(), index);
222 }
223
224 /****************************************************************************/
225
226 24 size_t SubDevice::outputIndex(const string &kind, unsigned int index) const
227 {
228 24 size_t position = 0;
229
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 47 times.
✗ Branch 7 not taken.
47 for (const auto &ck : outputKinds()) {
230
2/2
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 23 times.
47 if (ck.name == kind) {
231
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 if (index < ck.count) {
232 48 return position + index;
233 }
234 ✗ break;
235 }
236 23 position += ck.count;
237 }
238
239 ✗ stringstream err;
240 ✗ err << "Unknown output channel \"" << kind << "\" index " << index << ".";
241 ✗ throw UnknownChannel(err.str());
242 }
243
244 /****************************************************************************/
245
246 5 size_t SubDevice::outputIndex(unsigned int index) const
247 {
248
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 return outputIndex(soleOutputKind(), index);
249 }
250
251 /****************************************************************************/
252
253 ✗ size_t SubDevice::inputIndex(const string &kind, const string &alias) const
254 {
255 ✗ auto it = impl->inputAliases.find(kind);
256 ✗ if (it != impl->inputAliases.end()) {
257 ✗ for (size_t i = 0; i < it->second.size(); i++) {
258 ✗ if (it->second[i] == alias) {
259 ✗ return inputIndex(kind, static_cast<unsigned int>(i));
260 }
261 }
262 }
263
264 ✗ stringstream err;
265 err << "Unknown alias \"" << alias << "\" for input channel \"" << kind
266 ✗ << "\".";
267 ✗ throw UnknownChannel(err.str());
268 }
269
270 /****************************************************************************/
271
272 ✗ size_t SubDevice::outputIndex(const string &kind, const string &alias) const
273 {
274 ✗ auto it = impl->outputAliases.find(kind);
275 ✗ if (it != impl->outputAliases.end()) {
276 ✗ for (size_t i = 0; i < it->second.size(); i++) {
277 ✗ if (it->second[i] == alias) {
278 ✗ return outputIndex(kind, static_cast<unsigned int>(i));
279 }
280 }
281 }
282
283 ✗ stringstream err;
284 err << "Unknown alias \"" << alias << "\" for output channel \"" << kind
285 ✗ << "\".";
286 ✗ throw UnknownChannel(err.str());
287 }
288
289 /****************************************************************************/
290
291 ✗ void SubDevice::setAlias(
292 const string &kind,
293 const std::vector<string> &aliases)
294 {
295 ✗ const ChannelKind *k = nullptr;
296 ✗ bool isInput = true;
297
298 ✗ for (const auto &ck : inputKinds()) {
299 ✗ if (ck.name == kind) {
300 ✗ k = &ck;
301 ✗ isInput = true;
302 ✗ break;
303 }
304 }
305 ✗ if (!k) {
306 ✗ for (const auto &ck : outputKinds()) {
307 ✗ if (ck.name == kind) {
308 ✗ k = &ck;
309 ✗ isInput = false;
310 ✗ break;
311 }
312 }
313 }
314 ✗ if (!k) {
315 ✗ stringstream err;
316 ✗ err << "setAlias(): unknown channel kind \"" << kind << "\".";
317 ✗ throw UnknownChannel(err.str());
318 }
319
320 ✗ if (aliases.size() != k->count) {
321 ✗ stringstream err;
322 ✗ err << "setAlias(\"" << kind << "\", ...): " << aliases.size()
323 ✗ << " alias(es) given, but this kind has " << k->count
324 ✗ << " channel(s).";
325 ✗ throw AliasCountMismatch(err.str());
326 }
327
328 ✗ auto &aliasTable = isInput ? impl->inputAliases : impl->outputAliases;
329 auto &channelDataTable =
330 ✗ isInput ? impl->inputChannelData : impl->outputChannelData;
331
332 ✗ if (aliasTable.count(kind)) {
333 throw std::logic_error(
334 ✗ "setAlias() already called for channel \"" + kind + "\".");
335 }
336 ✗ aliasTable[kind] = aliases;
337
338 ✗ if (!impl->pdServ || !impl->task) {
339 ✗ return;
340 }
341
342 ✗ auto dit = channelDataTable.find(kind);
343 ✗ if (dit != channelDataTable.end()) {
344 ✗ void *base = dit->second.first;
345 ✗ const ChannelValue &tag = dit->second.second;
346 ✗ for (size_t i = 0; i < aliases.size(); i++) {
347 ✗ if (aliases[i].empty()) {
348 ✗ continue;
349 }
350 ✗ pdserv_signal(
351 ✗ impl->task, 1,
352 ✗ (impl->prefix + "/" + kind + "/" + aliases[i]).c_str(),
353 ✗ pdservTypeOf(tag), offsetOf(base, tag, i), 1, NULL);
354 }
355 }
356
357 ✗ auto range = impl->paramsByKind.equal_range(kind);
358 ✗ for (auto pit = range.first; pit != range.second; ++pit) {
359 ✗ const Impl::ParamEntry &param = pit->second;
360 ✗ for (size_t i = 0; i < aliases.size(); i++) {
361 ✗ if (aliases[i].empty()) {
362 ✗ continue;
363 }
364 ✗ pdserv_parameter(
365 ✗ impl->pdServ,
366 ✗ (impl->prefix + "/" + param.name + "/" + aliases[i])
367 .c_str(),
368 ✗ param.mode, pdservTypeOf(param.type),
369 ✗ offsetOf(param.data, param.type, i), 1, NULL, NULL, NULL);
370 }
371 }
372 }
373
374 /****************************************************************************/
375
376 template <typename T>
377 8 void SubDevice::registerInputChannel(const string &kind, std::vector<T> &data)
378 {
379
6/48
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 2 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✓ Branch 23 taken 2 times.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 57 not taken.
✓ Branch 58 taken 4 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✓ Branch 63 taken 4 times.
8 if (impl->pdServ && impl->task) {
380 ✗ pdserv_signal(
381 ✗ impl->task, 1, (impl->prefix + "/" + kind).c_str(),
382 pdservTypeOf(ChannelValue(T {})), data.data(), data.size(),
383 NULL);
384 }
385
3/16
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 21 taken 2 times.
✗ Branch 22 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✓ Branch 61 taken 4 times.
✗ Branch 62 not taken.
8 impl->inputChannelData[kind] = {data.data(), ChannelValue(T {})};
386 8 }
387
388 15 void SubDevice::registerInputChannel(
389 const string &kind,
390 std::vector<uint8_t> &data,
391 AsBoolean)
392 {
393
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 15 times.
15 if (impl->pdServ && impl->task) {
394 ✗ pdserv_signal(
395 ✗ impl->task, 1, (impl->prefix + "/" + kind).c_str(),
396 ✗ pd_boolean_T, data.data(), data.size(), NULL);
397 }
398
1/2
✓ Branch 5 taken 15 times.
✗ Branch 6 not taken.
15 impl->inputChannelData[kind] = {data.data(), ChannelValue(uint8_t {})};
399 15 }
400
401 template <typename T>
402 8 void SubDevice::registerOutputChannel(
403 const string &kind,
404 std::vector<T> &data)
405 {
406
6/48
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 4 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✓ Branch 23 taken 4 times.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 33 not taken.
✓ Branch 34 taken 2 times.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✓ Branch 39 taken 2 times.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
8 if (impl->pdServ && impl->task) {
407 ✗ pdserv_signal(
408 ✗ impl->task, 1, (impl->prefix + "/" + kind).c_str(),
409 pdservTypeOf(ChannelValue(T {})), data.data(), data.size(),
410 NULL);
411 }
412
3/16
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 21 taken 4 times.
✗ Branch 22 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✓ Branch 37 taken 2 times.
✗ Branch 38 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
8 impl->outputChannelData[kind] = {data.data(), ChannelValue(T {})};
413 8 }
414
415 12 void SubDevice::registerOutputChannel(
416 const string &kind,
417 std::vector<uint8_t> &data,
418 AsBoolean)
419 {
420
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 (impl->pdServ && impl->task) {
421 ✗ pdserv_signal(
422 ✗ impl->task, 1, (impl->prefix + "/" + kind).c_str(),
423 ✗ pd_boolean_T, data.data(), data.size(), NULL);
424 }
425
1/2
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
12 impl->outputChannelData[kind] = {data.data(), ChannelValue(uint8_t {})};
426 12 }
427
428 template <typename T>
429 8 void SubDevice::registerParameter(
430 const string &name,
431 const string &kind,
432 std::vector<T> &data,
433 unsigned int mode)
434 {
435
4/48
✗ 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.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✓ Branch 26 taken 2 times.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✓ Branch 31 taken 2 times.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
8 if (impl->pdServ && impl->task) {
436 ✗ pdserv_parameter(
437 ✗ impl->pdServ, (impl->prefix + "/" + name).c_str(), mode,
438 pdservTypeOf(ChannelValue(T {})), data.data(), data.size(),
439 NULL, NULL, NULL);
440 }
441
2/16
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✓ Branch 28 taken 2 times.
✗ Branch 29 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
8 impl->paramsByKind.emplace(
442 kind,
443 Impl::ParamEntry {name, data.data(), ChannelValue(T {}), mode});
444 8 }
445
446 5 void SubDevice::registerParameter(
447 const string &name,
448 const string &kind,
449 std::vector<uint8_t> &data,
450 AsBoolean,
451 unsigned int mode)
452 {
453
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
5 if (impl->pdServ && impl->task) {
454 ✗ pdserv_parameter(
455 ✗ impl->pdServ, (impl->prefix + "/" + name).c_str(), mode,
456 ✗ pd_boolean_T, data.data(), data.size(), NULL, NULL, NULL);
457 }
458
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
10 impl->paramsByKind.emplace(
459 kind,
460 15 Impl::ParamEntry {
461 10 name, data.data(), ChannelValue(uint8_t {}), mode});
462 5 }
463
464 template <typename T>
465 19 void SubDevice::registerInputChannel(const string &kind, T &data)
466 {
467
12/54
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 2 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 2 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✓ Branch 23 taken 2 times.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✓ Branch 42 taken 1 times.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✓ Branch 47 taken 1 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 3 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✓ Branch 55 taken 3 times.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 65 not taken.
✓ Branch 66 taken 3 times.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✓ Branch 71 taken 3 times.
19 if (impl->pdServ && impl->task) {
468 ✗ pdserv_signal(
469 ✗ impl->task, 1, (impl->prefix + "/" + kind).c_str(),
470 pdservTypeOf(ChannelValue(T {})), &data, 1, NULL);
471 }
472
6/18
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
✓ Branch 18 taken 2 times.
✗ Branch 19 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✓ Branch 39 taken 1 times.
✗ Branch 40 not taken.
✓ Branch 46 taken 3 times.
✗ Branch 47 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✓ Branch 60 taken 3 times.
✗ Branch 61 not taken.
19 impl->inputChannelData[kind] = {&data, ChannelValue(T {})};
473 19 }
474
475 1 void SubDevice::registerInputChannel(
476 const string &kind,
477 uint8_t &data,
478 AsBoolean)
479 {
480
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
1 if (impl->pdServ && impl->task) {
481 ✗ pdserv_signal(
482 ✗ impl->task, 1, (impl->prefix + "/" + kind).c_str(),
483 pd_boolean_T, &data, 1, NULL);
484 }
485
1/2
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 impl->inputChannelData[kind] = {&data, ChannelValue(uint8_t {})};
486 1 }
487
488 template <typename T>
489 2 void SubDevice::registerOutputChannel(const string &kind, T &data)
490 {
491
4/54
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
2 if (impl->pdServ && impl->task) {
492 ✗ pdserv_signal(
493 ✗ impl->task, 1, (impl->prefix + "/" + kind).c_str(),
494 pdservTypeOf(ChannelValue(T {})), &data, 1, NULL);
495 }
496
2/18
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
2 impl->outputChannelData[kind] = {&data, ChannelValue(T {})};
497 2 }
498
499 ✗ void SubDevice::registerOutputChannel(
500 const string &kind,
501 uint8_t &data,
502 AsBoolean)
503 {
504 ✗ if (impl->pdServ && impl->task) {
505 ✗ pdserv_signal(
506 ✗ impl->task, 1, (impl->prefix + "/" + kind).c_str(),
507 pd_boolean_T, &data, 1, NULL);
508 }
509 ✗ impl->outputChannelData[kind] = {&data, ChannelValue(uint8_t {})};
510 }
511
512 template <typename T>
513 4 void SubDevice::registerParameter(
514 const string &name,
515 const string &kind,
516 T &data,
517 unsigned int mode)
518 {
519
6/54
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 2 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✓ Branch 50 taken 1 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✓ Branch 55 taken 1 times.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
4 if (impl->pdServ && impl->task) {
520 ✗ pdserv_parameter(
521 ✗ impl->pdServ, (impl->prefix + "/" + name).c_str(), mode,
522 pdservTypeOf(ChannelValue(T {})), &data, 1, NULL, NULL, NULL);
523 }
524
3/18
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✓ Branch 45 taken 1 times.
✗ Branch 46 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
4 impl->paramsByKind.emplace(
525 kind, Impl::ParamEntry {name, &data, ChannelValue(T {}), mode});
526 4 }
527
528 1 void SubDevice::registerParameter(
529 const string &name,
530 const string &kind,
531 uint8_t &data,
532 AsBoolean,
533 unsigned int mode)
534 {
535
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
1 if (impl->pdServ && impl->task) {
536 ✗ pdserv_parameter(
537 ✗ impl->pdServ, (impl->prefix + "/" + name).c_str(), mode,
538 pd_boolean_T, &data, 1, NULL, NULL, NULL);
539 }
540
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 impl->paramsByKind.emplace(
541 kind,
542 2 Impl::ParamEntry {name, &data, ChannelValue(uint8_t {}), mode});
543 1 }
544
545 3 void SubDevice::registerInputChannel(const string &kind, ChannelValue &value)
546 {
547
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
3 if (impl->pdServ && impl->task) {
548 ✗ pdserv_signal(
549 ✗ impl->task, 1, (impl->prefix + "/" + kind).c_str(),
550 ✗ pdservTypeOf(value), addressOf(value), 1, NULL);
551 }
552
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
3 impl->inputChannelData[kind] = {addressOf(value), value};
553 3 }
554
555 5 void SubDevice::registerOutputChannel(const string &kind, ChannelValue &value)
556 {
557
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
5 if (impl->pdServ && impl->task) {
558 ✗ pdserv_signal(
559 ✗ impl->task, 1, (impl->prefix + "/" + kind).c_str(),
560 ✗ pdservTypeOf(value), addressOf(value), 1, NULL);
561 }
562
2/4
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
5 impl->outputChannelData[kind] = {addressOf(value), value};
563 5 }
564
565 1 void SubDevice::registerParameter(
566 const string &name,
567 const string &kind,
568 ChannelValue &value,
569 unsigned int mode)
570 {
571
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
1 if (impl->pdServ && impl->task) {
572 ✗ pdserv_parameter(
573 ✗ impl->pdServ, (impl->prefix + "/" + name).c_str(), mode,
574 pdservTypeOf(value), addressOf(value), 1, NULL, NULL, NULL);
575 }
576
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 impl->paramsByKind.emplace(
577 2 kind, Impl::ParamEntry {name, addressOf(value), value, mode});
578 1 }
579
580 // Explicitly instantiated for every ChannelValue alternative except
581 // bool for the vector overloads (a std::vector<bool> has no contiguous
582 // storage to publish -- use the AsBoolean overload of
583 // std::vector<uint8_t> instead); bool included for the scalar
584 // overloads, which have no such problem.
585 #define ECAPP_INSTANTIATE_VECTOR_REGISTRATION(T) \
586 template void SubDevice::registerInputChannel<T>( \
587 const string &, std::vector<T> &); \
588 template void SubDevice::registerOutputChannel<T>( \
589 const string &, std::vector<T> &); \
590 template void SubDevice::registerParameter<T>( \
591 const string &, const string &, std::vector<T> &, unsigned int)
592
593 #define ECAPP_INSTANTIATE_SCALAR_REGISTRATION(T) \
594 template void SubDevice::registerInputChannel<T>(const string &, T &); \
595 template void SubDevice::registerOutputChannel<T>(const string &, T &); \
596 template void SubDevice::registerParameter<T>( \
597 const string &, const string &, T &, unsigned int)
598
599 #define ECAPP_INSTANTIATE_CHANNEL_REGISTRATION(T) \
600 ECAPP_INSTANTIATE_VECTOR_REGISTRATION(T); \
601 ECAPP_INSTANTIATE_SCALAR_REGISTRATION(T)
602
603 ECAPP_INSTANTIATE_CHANNEL_REGISTRATION(uint8_t);
604 ECAPP_INSTANTIATE_CHANNEL_REGISTRATION(int8_t);
605 ECAPP_INSTANTIATE_CHANNEL_REGISTRATION(uint16_t);
606 ECAPP_INSTANTIATE_CHANNEL_REGISTRATION(int16_t);
607 ECAPP_INSTANTIATE_CHANNEL_REGISTRATION(uint32_t);
608 ECAPP_INSTANTIATE_CHANNEL_REGISTRATION(int32_t);
609 ECAPP_INSTANTIATE_CHANNEL_REGISTRATION(float);
610 ECAPP_INSTANTIATE_CHANNEL_REGISTRATION(double);
611 ECAPP_INSTANTIATE_SCALAR_REGISTRATION(bool);
612
613 #undef ECAPP_INSTANTIATE_CHANNEL_REGISTRATION
614 #undef ECAPP_INSTANTIATE_SCALAR_REGISTRATION
615 #undef ECAPP_INSTANTIATE_VECTOR_REGISTRATION
616
617 /****************************************************************************/
618
619 8 string SubDevice::soleInputKind() const
620 {
621 8 const auto &kinds = inputKinds();
622
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 3 times.
8 if (kinds.size() != 1) {
623
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
10 stringstream err;
624 err << "Ambiguous input channel: "
625 5 << (kinds.empty() ? "no kind is registered"
626 : "more than one kind is registered")
627
5/8
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 2 times.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 5 times.
✗ Branch 10 not taken.
10 << " (name one explicitly).";
628
2/4
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
5 throw UnknownChannel(err.str());
629 }
630 3 return kinds.front().name;
631 }
632
633 /****************************************************************************/
634
635 14 string SubDevice::soleOutputKind() const
636 {
637 14 const auto &kinds = outputKinds();
638
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 12 times.
14 if (kinds.size() != 1) {
639
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 stringstream err;
640 err << "Ambiguous output channel: "
641 2 << (kinds.empty() ? "no kind is registered"
642 : "more than one kind is registered")
643
4/8
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
4 << " (name one explicitly).";
644
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
2 throw UnknownChannel(err.str());
645 }
646 12 return kinds.front().name;
647 }
648
649 } // namespace EcApp
650
651 /****************************************************************************/
652