GCC Code Coverage Report


Directory: ./
File: src/devices/Ex20xx.cpp
Date: 2026-09-23 16:22:15
Exec Total Coverage
Lines: 81 125 64.8%
Branches: 59 378 15.6%

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 "devices/Ex20xx.h"
24
25 #include "SlaveContext.h"
26 #include "ecapp/Domain.h"
27 #include "ecapp/Factory.h"
28 #include "ecapp/Master.h"
29 #include "ecapp/SwappedSync.h"
30
31 #include <memory>
32 #include <sstream>
33 #include <stdexcept>
34
35 /****************************************************************************/
36
37 using std::make_unique;
38 using std::runtime_error;
39 using std::size_t;
40 using std::stringstream;
41 using std::unique_ptr;
42
43 namespace EcApp {
44
45 /* Master 0, Slave 4, "EL2004"
46 * Vendor ID: 0x00000002
47 * Product code: 0x07d43052
48 * Revision number: 0x00000000
49 */
50
51 ec_pdo_entry_info_t el2004_pdo_entries[] = {
52 {0x3001, 0x01, 1}, /* Output */
53 {0x3001, 0x02, 1}, /* Output */
54 {0x3001, 0x03, 1}, /* Output */
55 {0x3001, 0x04, 1}, /* Output */
56 };
57
58 ec_pdo_info_t el2004_pdos[] = {
59 {0x1600, 1, el2004_pdo_entries + 0}, /* Channel 1 */
60 {0x1601, 1, el2004_pdo_entries + 1}, /* Channel 2 */
61 {0x1602, 1, el2004_pdo_entries + 2}, /* Channel 3 */
62 {0x1603, 1, el2004_pdo_entries + 3}, /* Channel 4 */
63 };
64
65 ec_sync_info_t el2004_syncs[] = {
66 {0, EC_DIR_OUTPUT, 4, el2004_pdos + 0, EC_WD_ENABLE},
67 {0xff}};
68
69 /* Master 0, Slave 17, "EL2024"
70 * Vendor ID: 0x00000002
71 * Product code: 0x07e83052
72 * Revision number: 0x00130000
73 */
74
75 ec_pdo_entry_info_t el2024_pdo_entries[] = {
76 {0x7000, 0x01, 1}, /* Output */
77 {0x7010, 0x01, 1}, /* Output */
78 {0x7020, 0x01, 1}, /* Output */
79 {0x7030, 0x01, 1}, /* Output */
80 };
81
82 ec_pdo_info_t el2024_pdos[] = {
83 {0x1600, 1, el2024_pdo_entries + 0}, /* Channel 1 */
84 {0x1601, 1, el2024_pdo_entries + 1}, /* Channel 2 */
85 {0x1602, 1, el2024_pdo_entries + 2}, /* Channel 3 */
86 {0x1603, 1, el2024_pdo_entries + 3}, /* Channel 4 */
87 };
88
89 ec_sync_info_t el2024_syncs[] = {
90 {0, EC_DIR_OUTPUT, 4, el2024_pdos + 0, EC_WD_ENABLE},
91 {0xff}};
92
93 /* Master 0, Slave 7, "EL2008"
94 * Vendor ID: 0x00000002
95 * Product code: 0x07d83052
96 * Revision number: 0x00120000
97 */
98
99 ec_pdo_entry_info_t el2008_pdo_entries[] = {
100 {0x7000, 0x01, 1}, /* Output */
101 {0x7010, 0x01, 1}, /* Output */
102 {0x7020, 0x01, 1}, /* Output */
103 {0x7030, 0x01, 1}, /* Output */
104 {0x7040, 0x01, 1}, /* Output */
105 {0x7050, 0x01, 1}, /* Output */
106 {0x7060, 0x01, 1}, /* Output */
107 {0x7070, 0x01, 1}, /* Output */
108 };
109
110 ec_pdo_info_t el2008_pdos[] = {
111 {0x1600, 1, el2008_pdo_entries + 0}, /* Channel 1 */
112 {0x1601, 1, el2008_pdo_entries + 1}, /* Channel 2 */
113 {0x1602, 1, el2008_pdo_entries + 2}, /* Channel 3 */
114 {0x1603, 1, el2008_pdo_entries + 3}, /* Channel 4 */
115 {0x1604, 1, el2008_pdo_entries + 4}, /* Channel 5 */
116 {0x1605, 1, el2008_pdo_entries + 5}, /* Channel 6 */
117 {0x1606, 1, el2008_pdo_entries + 6}, /* Channel 7 */
118 {0x1607, 1, el2008_pdo_entries + 7}, /* Channel 8 */
119 };
120
121 ec_sync_info_t el2008_syncs[] = {
122 {0, EC_DIR_OUTPUT, 8, el2008_pdos + 0, EC_WD_ENABLE},
123 {0xff}};
124
125 /* Master 0, Slave 8, "EL2034"
126 * Vendor ID: 0x00000002
127 * Product code: 0x07f23052
128 * Revision number: 0x00110000
129 */
130
131 ec_pdo_entry_info_t el2034_pdo_entries[] = {
132 {0x7000, 0x01, 1}, /* Output */
133 {0x7010, 0x01, 1}, /* Output */
134 {0x7020, 0x01, 1}, /* Output */
135 {0x7030, 0x01, 1}, /* Output */
136 {0x6000, 0x01, 1}, /* Diag */
137 {0x6010, 0x01, 1}, /* Diag */
138 {0x6020, 0x01, 1}, /* Diag */
139 {0x6030, 0x01, 1}, /* Diag */
140 };
141
142 ec_pdo_info_t el2034_pdos[] = {
143 {0x1600, 1, el2034_pdo_entries + 0}, /* Channel 1 */
144 {0x1601, 1, el2034_pdo_entries + 1}, /* Channel 2 */
145 {0x1602, 1, el2034_pdo_entries + 2}, /* Channel 3 */
146 {0x1603, 1, el2034_pdo_entries + 3}, /* Channel 4 */
147 {0x1a00, 1, el2034_pdo_entries + 4}, /* Diag 1 */
148 {0x1a01, 1, el2034_pdo_entries + 5}, /* Diag 2 */
149 {0x1a02, 1, el2034_pdo_entries + 6}, /* Diag 3 */
150 {0x1a03, 1, el2034_pdo_entries + 7}, /* Diag 4 */
151 };
152
153 ec_sync_info_t el2034_syncs[] = {
154 {0, EC_DIR_OUTPUT, 4, el2034_pdos + 0, EC_WD_ENABLE},
155 {1, EC_DIR_INPUT, 4, el2034_pdos + 4, EC_WD_DISABLE},
156 {0xff}};
157
158 /* Master 0, Slave 19, "EP2028-0032"
159 * Vendor ID: 0x00000002
160 * Product code: 0x07ec4052
161 * Revision number: 0x00120020
162 */
163
164 ec_pdo_entry_info_t ep2028_0032_pdo_entries[] = {
165 {0x7000, 0x01, 1}, /* Output */
166 {0x7010, 0x01, 1}, /* Output */
167 {0x7020, 0x01, 1}, /* Output */
168 {0x7030, 0x01, 1}, /* Output */
169 {0x7040, 0x01, 1}, /* Output */
170 {0x7050, 0x01, 1}, /* Output */
171 {0x7060, 0x01, 1}, /* Output */
172 {0x7070, 0x01, 1}, /* Output */
173 };
174
175 ec_pdo_info_t ep2028_0032_pdos[] = {
176 {0x1600, 1, ep2028_0032_pdo_entries + 0}, /* Channel 1 */
177 {0x1601, 1, ep2028_0032_pdo_entries + 1}, /* Channel 2 */
178 {0x1602, 1, ep2028_0032_pdo_entries + 2}, /* Channel 3 */
179 {0x1603, 1, ep2028_0032_pdo_entries + 3}, /* Channel 4 */
180 {0x1604, 1, ep2028_0032_pdo_entries + 4}, /* Channel 5 */
181 {0x1605, 1, ep2028_0032_pdo_entries + 5}, /* Channel 6 */
182 {0x1606, 1, ep2028_0032_pdo_entries + 6}, /* Channel 7 */
183 {0x1607, 1, ep2028_0032_pdo_entries + 7}, /* Channel 8 */
184 };
185
186 ec_sync_info_t ep2028_0032_syncs[] = {
187 {0, EC_DIR_OUTPUT, 8, ep2028_0032_pdos + 0, EC_WD_ENABLE},
188 {0xff}};
189
190 /* Master 0, Slave 1, "EL2622"
191 * Vendor ID: 0x00000002
192 * Product code: 0x0a3e3052
193 * Revision number: 0x00120000
194 */
195
196 ec_pdo_entry_info_t el2622_pdo_entries[] = {
197 {0x7000, 0x01, 1}, /* Output */
198 {0x7010, 0x01, 1}, /* Output */
199 };
200
201 ec_pdo_info_t el2622_pdos[] = {
202 {0x1600, 1, el2622_pdo_entries + 0}, /* Channel 1 */
203 {0x1601, 1, el2622_pdo_entries + 1}, /* Channel 2 */
204 };
205
206 ec_sync_info_t el2622_syncs[] = {
207 {0, EC_DIR_OUTPUT, 2, el2622_pdos + 0, EC_WD_ENABLE},
208 {0xff}};
209
210 /****************************************************************************/
211
212 struct Ex20xxType
213 {
214 const char *name;
215 uint32_t product_code;
216 unsigned int num_channels;
217 uint16_t entry_index;
218 uint16_t entry_index_inc;
219 uint16_t entry_subindex_inc;
220 bool has_diagnosis;
221 ec_sync_info_t *sync;
222 };
223
224 const static Ex20xxType el20xxType[Ex20xx<Mode::Control>::NumTypes] = {
225 {"EL2004", 0x07d43052, 4, 0x3001, 0x0000, 0x01, false, el2004_syncs},
226 {"EL2008", 0x07d83052, 8, 0x7000, 0x0010, 0x00, false, el2008_syncs},
227 {"EL2024", 0x07e83052, 4, 0x7000, 0x0010, 0x00, false, el2024_syncs},
228 {"EL2034", 0x07f23052, 4, 0x7000, 0x0010, 0x00, true, el2034_syncs},
229 {"EP2028-0032", 0x07ec4052, 8, 0x7000, 0x0010, 0x00, false,
230 ep2028_0032_syncs},
231 {"EL2622", 0x0a3e3052, 2, 0x7000, 0x0010, 0x00, false, el2622_syncs},
232 {"EL2808", 0x0af83052, 8, 0x7000, 0x0010, 0x00, false, el2008_syncs},
233 };
234
235 /****************************************************************************/
236
237 template <Mode mode>
238 1 Ex20xx<mode>::Ex20xx(
239 Master *master,
240 uint16_t alias,
241 uint16_t position,
242 Domain *domainOut,
243 Domain *domainIn,
244 Type typeIdx) :
245 1 type(el20xxType[typeIdx]),
246 sc(NULL),
247 domainOut(domainOut),
248 domainIn(domainIn),
249 1 off_out(type.num_channels, -1),
250 1 off_out_bit(type.num_channels, 0),
251 off_in(0),
252 1 input(type.num_channels, 0),
253 1 prevInput(type.num_channels, 0),
254 1 toggle(type.num_channels, 0),
255 1 prevToggle(type.num_channels, 0),
256 1 autoValue(type.num_channels, 0),
257 1 enableForcing(type.num_channels, 0),
258 1 forcedValue(type.num_channels, 0),
259 1 output(type.num_channels, 0),
260
11/44
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 27 taken 1 times.
✗ Branch 28 not taken.
✓ Branch 32 taken 1 times.
✗ Branch 33 not taken.
✓ Branch 37 taken 1 times.
✗ Branch 38 not taken.
✓ Branch 42 taken 1 times.
✗ Branch 43 not taken.
✓ Branch 47 taken 1 times.
✗ Branch 48 not taken.
✓ Branch 52 taken 1 times.
✗ Branch 53 not taken.
✗ Branch 80 not taken.
✗ Branch 81 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 90 not taken.
✗ Branch 91 not taken.
✗ Branch 95 not taken.
✗ Branch 96 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✗ Branch 105 not taken.
✗ Branch 106 not taken.
✗ Branch 110 not taken.
✗ Branch 111 not taken.
✗ Branch 115 not taken.
✗ Branch 116 not taken.
✗ Branch 120 not taken.
✗ Branch 121 not taken.
✗ Branch 125 not taken.
✗ Branch 126 not taken.
✗ Branch 130 not taken.
✗ Branch 131 not taken.
11 diagnosis(type.num_channels, 0)
261 {
262 // pdserv publishing happens in Ex20xxAdapter's constructor below,
263 // via SubDevice::registerOutputChannel()/registerInputChannel()/
264 // registerParameter() -- that is also what makes "Output"/
265 // "Diagnosis" alias-able through SubDevice::setAlias().
266
267
1/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (!master) {
268 1 return;
269 }
270
271 ✗ sc = ecrt_master_slave_config(
272 master->getMaster(), alias, position, 0x00000002,
273 ✗ type.product_code);
274 ✗ if (!sc) {
275 ✗ stringstream err;
276 ✗ err << "Failed to get slave configuration.";
277 ✗ throw runtime_error(err.str());
278 }
279
280 ✗ if (ecrt_slave_config_pdos(
281 ✗ sc, EC_END, SwappedSync<mode>(type.sync).getSync())) {
282 ✗ stringstream err;
283 ✗ err << "Failed to configure PDOs.";
284 ✗ throw runtime_error(err.str());
285 }
286
287 ✗ for (unsigned int i = 0; i < type.num_channels; i++) {
288 ✗ uint16_t entry_index = type.entry_index + type.entry_index_inc * i;
289 ✗ uint8_t entry_subindex = 0x01 + type.entry_subindex_inc * i;
290 ✗ off_out[i] = ecrt_slave_config_reg_pdo_entry(
291 sc, entry_index, entry_subindex, domainOut->getDomain(),
292 ✗ &off_out_bit[i]);
293 ✗ if (off_out[i] < 0) {
294 ✗ stringstream err;
295 ✗ err << "Failed to register Ex2xxx value PDO entry.";
296 ✗ throw runtime_error(err.str());
297 }
298 }
299
300 ✗ if (type.has_diagnosis) {
301 ✗ off_in = ecrt_slave_config_reg_pdo_entry(
302 sc, 0x6000, 0x01, domainIn->getDomain(), NULL);
303 ✗ if (off_in < 0) {
304 ✗ stringstream err;
305 ✗ err << "Failed to register Ex2xxx diagnosis PDO entry.";
306 ✗ throw runtime_error(err.str());
307 }
308 }
309 }
310
311 /****************************************************************************/
312
313 template <Mode mode>
314 3 void Ex20xx<mode>::setOutput(unsigned int channel, bool value)
315 {
316
1/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 if (channel < type.num_channels) {
317 3 input[channel] = value;
318 }
319 3 }
320
321 /****************************************************************************/
322
323 template <Mode mode>
324 ✗ bool Ex20xx<mode>::getOutput(unsigned int channel) const
325 {
326 ✗ if (channel < type.num_channels and domainOut->getData()) {
327 ✗ return EC_READ_BIT(
328 domainOut->getData() + off_out[channel],
329 off_out_bit[channel]);
330 }
331 else {
332 ✗ return false;
333 }
334 }
335
336 /****************************************************************************/
337
338 template <Mode mode>
339 1 unsigned int Ex20xx<mode>::numChannels() const
340 {
341 1 return type.num_channels;
342 }
343
344 /****************************************************************************/
345
346 template <Mode mode>
347 1 bool Ex20xx<mode>::hasDiagnosis() const
348 {
349 1 return type.has_diagnosis;
350 }
351
352 /****************************************************************************/
353
354 template <Mode mode>
355 ✗ bool Ex20xx<mode>::getDiagnosis(unsigned int channel) const
356 {
357 ✗ return channel < diagnosis.size() ? diagnosis[channel] : false;
358 }
359
360 /****************************************************************************/
361
362 template <Mode mode>
363 1 void Ex20xx<mode>::updateInputs()
364 {
365
2/12
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
1 if (type.has_diagnosis && domainIn->getData()) {
366 ✗ for (unsigned int i = 0; i < type.num_channels; i++) {
367 ✗ diagnosis[i] = EC_READ_BIT(domainIn->getData() + off_in, i);
368 }
369 }
370 1 }
371
372 /****************************************************************************/
373
374 template <Mode mode>
375 1 void Ex20xx<mode>::updateOutputs()
376 {
377
2/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
9 for (unsigned int i = 0; i < type.num_channels; i++) {
378 // toggle logic
379
3/8
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
8 const bool inputRising = input[i] && !prevInput[i];
380 8 const bool inputUnchanged = input[i] == prevInput[i];
381 8 prevInput[i] = input[i];
382
383 8 const bool toggleChanged = toggle[i] != prevToggle[i];
384 8 prevToggle[i] = toggle[i];
385
386 8 const bool toggleXorState = toggleChanged != autoValue[i];
387
3/8
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
8 const bool toggledState = inputUnchanged && toggleXorState;
388
3/8
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
8 autoValue[i] = inputRising || toggledState;
389
390
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
8 if (enableForcing[i]) {
391 ✗ output[i] = forcedValue[i];
392 }
393 else {
394 8 output[i] = autoValue[i];
395 }
396
397
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
8 if (domainOut->getData()) {
398 ✗ EC_WRITE_BIT(
399 domainOut->getData() + off_out[i], off_out_bit[i],
400 output[i]);
401 }
402 }
403 1 }
404
405 /****************************************************************************/
406
407 template class Ex20xx<Mode::Control>;
408 template class Ex20xx<Mode::Simulation>;
409
410 /****************************************************************************/
411 // Generic SubDevice adapter: translates the family's typed, bit-indexed
412 // setOutput()/getOutput() into ChannelValue, and registers each type
413 // with the factory so createSubDevice("EL2008") never has to know it
414 // resolves to Ex20xx.
415 /****************************************************************************/
416
417 template <Mode mode>
418 2 class Ex20xxAdapter : public SubDevice
419 {
420 public:
421 1 Ex20xxAdapter(const SlaveContext &ctx, typename Ex20xx<mode>::Type t) :
422 SubDevice(
423 1 ctx.domainOut,
424 1 ctx.domainIn,
425 1 ctx.pdServ,
426 1 ctx.task,
427 ctx.prefix),
428 1 device(ctx.master,
429 1 ctx.alias,
430 1 ctx.position,
431 1 ctx.domainOut,
432 1 ctx.domainIn,
433 t),
434
7/28
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✓ Branch 18 taken 1 times.
✗ Branch 19 not taken.
✓ Branch 21 taken 1 times.
✓ Branch 22 taken 1 times.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
5 outputKinds_ {{"Output", device.numChannels(), false}}
435 {
436 // device.input (not device.output, the post-forcing effective
437 // value) is what the generic (kind, index) API's "Output"
438 // channel resolves to (see setOutputAt() below), hence
439 // registered under that same name; device.output keeps its
440 // own pdserv-only name since it has no matching ChannelKind --
441 // see the identical reasoning in Ex4xxxAdapter.
442
2/8
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
1 registerOutputChannel("Output", device.input, AsBoolean {});
443
2/8
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
1 registerOutputChannel("EffectiveOutput", device.output, AsBoolean {});
444
3/12
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
1 registerParameter(
445 "EnableForcing", "Output", device.enableForcing,
446 AsBoolean {});
447
3/12
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
1 registerParameter(
448 "ForcedValue", "Output", device.forcedValue, AsBoolean {});
449
3/12
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
1 registerParameter("Toggle", "Output", device.toggle);
450
451
2/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
1 if (device.hasDiagnosis()) {
452 ✗ inputKinds_.push_back({"Diagnosis", device.numChannels(), false});
453 ✗ registerInputChannel("Diagnosis", device.diagnosis, AsBoolean {});
454 }
455 1 }
456
457 1 void updateInputs() override { device.updateInputs(); }
458 1 void updateOutputs() override { device.updateOutputs(); }
459
460 ✗ const std::vector<ChannelKind> &inputKinds() const override
461 {
462 ✗ return inputKinds_;
463 }
464 13 const std::vector<ChannelKind> &outputKinds() const override
465 {
466 13 return outputKinds_;
467 }
468
469 protected:
470 ✗ ChannelValue getInputAt(size_t i) const override
471 {
472 ✗ return device.getDiagnosis(static_cast<unsigned int>(i));
473 }
474 4 void setOutputAt(size_t i, const ChannelValue &value) override
475 {
476 4 device.setOutput(static_cast<unsigned int>(i), std::get<bool>(value));
477 3 }
478
479 private:
480 Ex20xx<mode> device;
481 std::vector<ChannelKind> inputKinds_;
482 std::vector<ChannelKind> outputKinds_;
483 };
484
485 /****************************************************************************/
486
487 namespace {
488
489 84 DeviceFactoryFn makeEx20xxFactory(unsigned int typeIdx)
490 {
491 2 return [typeIdx](const SlaveContext &ctx) -> unique_ptr<SubDevice> {
492
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ctx.mode == Mode::Control) {
493 ✗ return make_unique<Ex20xxAdapter<Mode::Control>>(
494 ✗ ctx, static_cast<Ex20xx<Mode::Control>::Type>(typeIdx));
495 }
496 else {
497
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 return make_unique<Ex20xxAdapter<Mode::Simulation>>(
498 ctx,
499 3 static_cast<Ex20xx<Mode::Simulation>::Type>(typeIdx));
500 }
501 84 };
502 }
503
504 struct Ex20xxRegistrations
505 {
506 12 Ex20xxRegistrations()
507 {
508
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 12 times.
96 for (unsigned int i = 0; i < Ex20xx<Mode::Control>::NumTypes; i++) {
509 84 const auto &t = el20xxType[i];
510
4/8
✓ Branch 3 taken 84 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 84 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 84 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 84 times.
✗ Branch 14 not taken.
420 Registry::instance().add(
511 168 {t.name, __FILE__, 0x00000002, t.product_code,
512
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
168 AnyRevision, wrapFactory(makeEx20xxFactory(i))});
513 }
514 12 }
515 12 } ex20xxRegistrations;
516
517 } // namespace
518
519 36 } // namespace EcApp
520
521 /****************************************************************************/
522