GCC Code Coverage Report


Directory: ./
File: src/devices/Ex3xxx.cpp
Date: 2026-09-03 16:05:57
Exec Total Coverage
Lines: 100 204 49.0%
Branches: 54 462 11.7%

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/Ex3xxx.h"
24
25 #include "SlaveContext.h"
26 #include "ecapp/AnalogInputType.h"
27 #include "ecapp/ConfigurableScale.h"
28 #include "ecapp/Domain.h"
29 #include "ecapp/Exceptions.h"
30 #include "ecapp/Factory.h"
31 #include "ecapp/Master.h"
32 #include "ecapp/SwappedSync.h"
33
34 #include <memory>
35 #include <sstream>
36 #include <stdexcept>
37
38 /****************************************************************************/
39
40 using std::logic_error;
41 using std::make_unique;
42 using std::runtime_error;
43 using std::size_t;
44 using std::stringstream;
45 using std::unique_ptr;
46
47 namespace EcApp {
48
49 /* Master 0, Slave 22, "EL3054"
50 * Vendor ID: 0x00000002
51 * Product code: 0x0bee3052
52 * Revision number: 0x00140000
53 */
54
55 ec_pdo_entry_info_t el3054_pdo_entries[] = {
56 {0x6000, 0x01, 1}, {0x6000, 0x02, 1}, {0x6000, 0x03, 2},
57 {0x6000, 0x05, 2}, {0x6000, 0x07, 1}, {0x0000, 0x00, 1}, /* Gap */
58 {0x0000, 0x00, 6}, /* Gap */
59 {0x6000, 0x0f, 1}, {0x6000, 0x10, 1}, {0x6000, 0x11, 16},
60 {0x6010, 0x01, 1}, {0x6010, 0x02, 1}, {0x6010, 0x03, 2},
61 {0x6010, 0x05, 2}, {0x6010, 0x07, 1}, {0x0000, 0x00, 1}, /* Gap */
62 {0x0000, 0x00, 6}, /* Gap */
63 {0x6010, 0x0f, 1}, {0x6010, 0x10, 1}, {0x6010, 0x11, 16},
64 {0x6020, 0x01, 1}, {0x6020, 0x02, 1}, {0x6020, 0x03, 2},
65 {0x6020, 0x05, 2}, {0x6020, 0x07, 1}, {0x0000, 0x00, 1}, /* Gap */
66 {0x0000, 0x00, 6}, /* Gap */
67 {0x6020, 0x0f, 1}, {0x6020, 0x10, 1}, {0x6020, 0x11, 16},
68 {0x6030, 0x01, 1}, {0x6030, 0x02, 1}, {0x6030, 0x03, 2},
69 {0x6030, 0x05, 2}, {0x6030, 0x07, 1}, {0x0000, 0x00, 1}, /* Gap */
70 {0x0000, 0x00, 6}, /* Gap */
71 {0x6030, 0x0f, 1}, {0x6030, 0x10, 1}, {0x6030, 0x11, 16},
72 };
73
74 ec_pdo_info_t el3054_pdos[] = {
75 {0x1a00, 10, el3054_pdo_entries + 0},
76 {0x1a02, 10, el3054_pdo_entries + 10},
77 {0x1a04, 10, el3054_pdo_entries + 20},
78 {0x1a06, 10, el3054_pdo_entries + 30},
79 };
80
81 ec_sync_info_t el3054_syncs[] = {
82 {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
83 {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
84 {2, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
85 {3, EC_DIR_INPUT, 4, el3054_pdos + 0, EC_WD_DISABLE},
86 {0xff}};
87
88 /* Master 0, Slave 13, "EL3102"
89 * Vendor ID: 0x00000002
90 * Product code: 0x0c1e3052
91 * Revision number: 0x00140000
92 */
93
94 ec_pdo_entry_info_t el31x2_pdo_entries[] = {
95 {0x3101, 0x01, 8}, /* Status */
96 {0x3101, 0x02, 16}, /* Value */
97 {0x3102, 0x01, 8}, /* Status */
98 {0x3102, 0x02, 16}, /* Value */
99 };
100
101 ec_pdo_info_t el31x2_pdos[] = {
102 {0x1a00, 2, el31x2_pdo_entries + 0}, /* TxPDO-Map Channel 1 */
103 {0x1a01, 2, el31x2_pdo_entries + 2}, /* TxPDO-Map Channel 2 */
104 };
105
106 ec_sync_info_t el31x2_syncs[] = {
107 {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
108 {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
109 {2, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
110 {3, EC_DIR_INPUT, 2, el31x2_pdos + 0, EC_WD_DISABLE},
111 {0xff}};
112
113 /* Master 0, Slave 12, "EL3062-0030"
114 * Vendor ID: 0x00000002
115 * Product code: 0x0bf63052
116 * Revision number: 0x0015001e
117 */
118
119 ec_pdo_entry_info_t el3062_pdo_entries[] = {
120 {0x6000, 0x01, 1}, /* Underrange */
121 {0x6000, 0x02, 1}, /* Overrange */
122 {0x6000, 0x03, 2}, /* Limit 1 */
123 {0x6000, 0x05, 2}, /* Limit 2 */
124 {0x6000, 0x07, 1}, /* Error */
125 {0x0000, 0x00, 1}, /* Gap */
126 {0x0000, 0x00, 6}, /* Gap */
127 {0x6000, 0x0f, 1}, /* TxPDO State */
128 {0x6000, 0x10, 1}, /* TxPDO Toggle */
129 {0x6000, 0x11, 16}, /* Value */
130 {0x6010, 0x01, 1}, /* Underrange */
131 {0x6010, 0x02, 1}, /* Overrange */
132 {0x6010, 0x03, 2}, /* Limit 1 */
133 {0x6010, 0x05, 2}, /* Limit 2 */
134 {0x6010, 0x07, 1}, /* Error */
135 {0x0000, 0x00, 1}, /* Gap */
136 {0x0000, 0x00, 6}, /* Gap */
137 {0x6010, 0x0f, 1}, /* TxPDO State */
138 {0x6010, 0x10, 1}, /* TxPDO Toggle */
139 {0x6010, 0x11, 16}, /* Value */
140 };
141
142 ec_pdo_info_t el3062_pdos[] = {
143 {0x1a00, 10, el3062_pdo_entries + 0}, /* AI TxPDO-Map Standard Ch.1 */
144 {0x1a02, 10,
145 el3062_pdo_entries + 10}, /* AI TxPDO-Map Standard Ch.2 */
146 };
147
148 ec_sync_info_t el3062_syncs[] = {
149 {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
150 {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
151 {2, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
152 {3, EC_DIR_INPUT, 2, el3062_pdos + 0, EC_WD_DISABLE},
153 {0xff}};
154
155 /*
156 * EL3024,
157 * EL3104,
158 * EPP3184-0002
159 */
160 ec_pdo_entry_info_t ex3xx4_pdo_entries[] = {
161 {0x6000, 0x01, 1}, /* Underrange */
162 {0x6000, 0x02, 1}, /* Overrange */
163 {0x6000, 0x03, 2}, /* Limit 1 */
164 {0x6000, 0x05, 2}, /* Limit 2 */
165 {0x6000, 0x07, 1}, /* Error */
166 {0x0000, 0x00, 1}, /* Gap */
167 {0x0000, 0x00, 5}, /* Gap */
168 {0x6000, 0x0e, 1}, /* Sync error */
169 {0x6000, 0x0f, 1}, /* TxPDO State */
170 {0x6000, 0x10, 1}, /* TxPDO Toggle */
171 {0x6000, 0x11, 16}, /* Value */
172 {0x6010, 0x01, 1}, /* Underrange */
173 {0x6010, 0x02, 1}, /* Overrange */
174 {0x6010, 0x03, 2}, /* Limit 1 */
175 {0x6010, 0x05, 2}, /* Limit 2 */
176 {0x6010, 0x07, 1}, /* Error */
177 {0x0000, 0x00, 1}, /* Gap */
178 {0x0000, 0x00, 5}, /* Gap */
179 {0x6010, 0x0e, 1}, /* Sync error */
180 {0x6010, 0x0f, 1}, /* TxPDO State */
181 {0x6010, 0x10, 1}, /* TxPDO Toggle */
182 {0x6010, 0x11, 16}, /* Value */
183 {0x6020, 0x01, 1}, /* Underrange */
184 {0x6020, 0x02, 1}, /* Overrange */
185 {0x6020, 0x03, 2}, /* Limit 1 */
186 {0x6020, 0x05, 2}, /* Limit 2 */
187 {0x6020, 0x07, 1}, /* Error */
188 {0x0000, 0x00, 1}, /* Gap */
189 {0x0000, 0x00, 5}, /* Gap */
190 {0x6020, 0x0e, 1}, /* Sync error */
191 {0x6020, 0x0f, 1}, /* TxPDO State */
192 {0x6020, 0x10, 1}, /* TxPDO Toggle */
193 {0x6020, 0x11, 16}, /* Value */
194 {0x6030, 0x01, 1}, /* Underrange */
195 {0x6030, 0x02, 1}, /* Overrange */
196 {0x6030, 0x03, 2}, /* Limit 1 */
197 {0x6030, 0x05, 2}, /* Limit 2 */
198 {0x6030, 0x07, 1}, /* Error */
199 {0x0000, 0x00, 1}, /* Gap */
200 {0x0000, 0x00, 5}, /* Gap */
201 {0x6030, 0x0e, 1}, /* Sync error */
202 {0x6030, 0x0f, 1}, /* TxPDO State */
203 {0x6030, 0x10, 1}, /* TxPDO Toggle */
204 {0x6030, 0x11, 16}, /* Value */
205 };
206
207 ec_pdo_info_t ex3xx4_pdos[] = {
208 {0x1a00, 11, ex3xx4_pdo_entries + 0}, /* AI TxPDO-Map Standard Ch.1 */
209 {0x1a02, 11,
210 ex3xx4_pdo_entries + 11}, /* AI TxPDO-Map Standard Ch.2 */
211 {0x1a04, 11,
212 ex3xx4_pdo_entries + 22}, /* AI TxPDO-Map Standard Ch.3 */
213 {0x1a06, 11,
214 ex3xx4_pdo_entries + 33}, /* AI TxPDO-Map Standard Ch.4 */
215 };
216
217 ec_sync_info_t ex3xx4_syncs[] = {
218 {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
219 {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
220 {2, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
221 {3, EC_DIR_INPUT, 4, ex3xx4_pdos + 0, EC_WD_DISABLE},
222 {0xff}};
223
224 /* Master 0, Slave 5, "EL3164"
225 * Vendor ID: 0x00000002
226 * Product code: 0x0bf83052
227 * Revision number: 0x00140000
228 */
229
230 ec_pdo_entry_info_t el3164_pdo_entries[] = {
231 {0x6000, 0x01, 1}, /* Underrange */
232 {0x6000, 0x02, 1}, /* Overrange */
233 {0x6000, 0x03, 2}, /* Limit 1 */
234 {0x6000, 0x05, 2}, /* Limit 2 */
235 {0x6000, 0x07, 1}, /* Error */
236 {0x0000, 0x00, 1}, /* Gap */
237 {0x0000, 0x00, 6}, /* Gap */
238 {0x6000, 0x0f, 1}, /* TxPDO State */
239 {0x6000, 0x10, 1}, /* TxPDO Toggle */
240 {0x6000, 0x11, 16}, /* Value */
241 {0x6010, 0x01, 1}, /* Underrange */
242 {0x6010, 0x02, 1}, /* Overrange */
243 {0x6010, 0x03, 2}, /* Limit 1 */
244 {0x6010, 0x05, 2}, /* Limit 2 */
245 {0x6010, 0x07, 1}, /* Error */
246 {0x0000, 0x00, 1}, /* Gap */
247 {0x0000, 0x00, 6}, /* Gap */
248 {0x6010, 0x0f, 1}, /* TxPDO State */
249 {0x6010, 0x10, 1}, /* TxPDO Toggle */
250 {0x6010, 0x11, 16}, /* Value */
251 {0x6020, 0x01, 1}, /* Underrange */
252 {0x6020, 0x02, 1}, /* Overrange */
253 {0x6020, 0x03, 2}, /* Limit 1 */
254 {0x6020, 0x05, 2}, /* Limit 2 */
255 {0x6020, 0x07, 1}, /* Error */
256 {0x0000, 0x00, 1}, /* Gap */
257 {0x0000, 0x00, 6}, /* Gap */
258 {0x6020, 0x0f, 1}, /* TxPDO State */
259 {0x6020, 0x10, 1}, /* TxPDO Toggle */
260 {0x6020, 0x11, 16}, /* Value */
261 {0x6030, 0x01, 1}, /* Underrange */
262 {0x6030, 0x02, 1}, /* Overrange */
263 {0x6030, 0x03, 2}, /* Limit 1 */
264 {0x6030, 0x05, 2}, /* Limit 2 */
265 {0x6030, 0x07, 1}, /* Error */
266 {0x0000, 0x00, 1}, /* Gap */
267 {0x0000, 0x00, 6}, /* Gap */
268 {0x6030, 0x0f, 1}, /* TxPDO State */
269 {0x6030, 0x10, 1}, /* TxPDO Toggle */
270 {0x6030, 0x11, 16}, /* Value */
271 };
272
273 ec_pdo_info_t el3164_pdos[] = {
274 {0x1a00, 10, el3164_pdo_entries + 0}, /* AI TxPDO-Map Standard Ch.1 */
275 {0x1a02, 10,
276 el3164_pdo_entries + 10}, /* AI TxPDO-Map Standard Ch.2 */
277 {0x1a04, 10,
278 el3164_pdo_entries + 20}, /* AI TxPDO-Map Standard Ch.3 */
279 {0x1a06, 10,
280 el3164_pdo_entries + 30}, /* AI TxPDO-Map Standard Ch.4 */
281 };
282
283 ec_sync_info_t el3164_syncs[] = {
284 {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
285 {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
286 {2, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
287 {3, EC_DIR_INPUT, 4, el3164_pdos + 0, EC_WD_DISABLE},
288 {0xff}};
289
290 /* Master 0, Slave 1, "EL3182"
291 * Vendor ID: 0x00000002
292 * Product code: 0x0c6e3052
293 * Revision number: 0x00120000
294 */
295
296 ec_pdo_entry_info_t el3182_pdo_entries[] = {
297 {0x6000, 0x01, 1}, /* Underrange */
298 {0x6000, 0x02, 1}, /* Overrange */
299 {0x6000, 0x03, 2}, /* Limit 1 */
300 {0x6000, 0x05, 2}, /* Limit 2 */
301 {0x6000, 0x07, 1}, /* Error */
302 {0x0000, 0x00, 1}, /* Gap */
303 {0x0000, 0x00, 5}, /* Gap */
304 {0x6000, 0x0e, 1}, /* Sync error */
305 {0x6000, 0x0f, 1}, /* TxPDO State */
306 {0x6000, 0x10, 1}, /* TxPDO Toggle */
307 {0x6000, 0x11, 16}, /* Value */
308 {0x6010, 0x01, 1}, /* Underrange */
309 {0x6010, 0x02, 1}, /* Overrange */
310 {0x6010, 0x03, 2}, /* Limit 1 */
311 {0x6010, 0x05, 2}, /* Limit 2 */
312 {0x6010, 0x07, 1}, /* Error */
313 {0x0000, 0x00, 1}, /* Gap */
314 {0x0000, 0x00, 5}, /* Gap */
315 {0x6010, 0x0e, 1}, /* Sync error */
316 {0x6010, 0x0f, 1}, /* TxPDO State */
317 {0x6010, 0x10, 1}, /* TxPDO Toggle */
318 {0x6010, 0x11, 16}, /* Value */
319 };
320
321 ec_pdo_info_t el3182_pdos[] = {
322 {0x1a00, 11, el3182_pdo_entries + 0}, /* AI TxPDO-Map Standard Ch.1 */
323 {0x1a02, 11,
324 el3182_pdo_entries + 11}, /* AI TxPDO-Map Standard Ch.2 */
325 };
326
327 ec_sync_info_t el3182_syncs[] = {
328 {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
329 {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
330 {2, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
331 {3, EC_DIR_INPUT, 2, el3182_pdos + 0, EC_WD_DISABLE},
332 {0xff}};
333
334 /* Master 0, Slave 2, "EL3008"
335 * Vendor ID: 0x00000002
336 * Product code: 0x0bc03052
337 * Revision number: 0x00150000
338 */
339
340 ec_pdo_entry_info_t el30x8_pdo_entries[] = {
341 {0x6000, 0x01, 1}, /* Underrange */
342 {0x6000, 0x02, 1}, /* Overrange */
343 {0x6000, 0x03, 2}, /* Limit 1 */
344 {0x6000, 0x05, 2}, /* Limit 2 */
345 {0x6000, 0x07, 1}, /* Error */
346 {0x0000, 0x00, 1}, /* Gap */
347 {0x0000, 0x00, 6}, /* Gap */
348 {0x6000, 0x0f, 1}, /* TxPDO State */
349 {0x6000, 0x10, 1}, /* TxPDO Toggle */
350 {0x6000, 0x11, 16}, /* Value */
351 {0x6010, 0x01, 1}, /* Underrange */
352 {0x6010, 0x02, 1}, /* Overrange */
353 {0x6010, 0x03, 2}, /* Limit 1 */
354 {0x6010, 0x05, 2}, /* Limit 2 */
355 {0x6010, 0x07, 1}, /* Error */
356 {0x0000, 0x00, 1}, /* Gap */
357 {0x0000, 0x00, 6}, /* Gap */
358 {0x6010, 0x0f, 1}, /* TxPDO State */
359 {0x6010, 0x10, 1}, /* TxPDO Toggle */
360 {0x6010, 0x11, 16}, /* Value */
361 {0x6020, 0x01, 1}, /* Underrange */
362 {0x6020, 0x02, 1}, /* Overrange */
363 {0x6020, 0x03, 2}, /* Limit 1 */
364 {0x6020, 0x05, 2}, /* Limit 2 */
365 {0x6020, 0x07, 1}, /* Error */
366 {0x0000, 0x00, 1}, /* Gap */
367 {0x0000, 0x00, 6}, /* Gap */
368 {0x6020, 0x0f, 1}, /* TxPDO State */
369 {0x6020, 0x10, 1}, /* TxPDO Toggle */
370 {0x6020, 0x11, 16}, /* Value */
371 {0x6030, 0x01, 1}, /* Underrange */
372 {0x6030, 0x02, 1}, /* Overrange */
373 {0x6030, 0x03, 2}, /* Limit 1 */
374 {0x6030, 0x05, 2}, /* Limit 2 */
375 {0x6030, 0x07, 1}, /* Error */
376 {0x0000, 0x00, 1}, /* Gap */
377 {0x0000, 0x00, 6}, /* Gap */
378 {0x6030, 0x0f, 1}, /* TxPDO State */
379 {0x6030, 0x10, 1}, /* TxPDO Toggle */
380 {0x6030, 0x11, 16}, /* Value */
381 {0x6040, 0x01, 1}, /* Underrange */
382 {0x6040, 0x02, 1}, /* Overrange */
383 {0x6040, 0x03, 2}, /* Limit 1 */
384 {0x6040, 0x05, 2}, /* Limit 2 */
385 {0x6040, 0x07, 1}, /* Error */
386 {0x0000, 0x00, 1}, /* Gap */
387 {0x0000, 0x00, 6}, /* Gap */
388 {0x6040, 0x0f, 1}, /* TxPDO State */
389 {0x6040, 0x10, 1}, /* TxPDO Toggle */
390 {0x6040, 0x11, 16}, /* Value */
391 {0x6050, 0x01, 1}, /* Underrange */
392 {0x6050, 0x02, 1}, /* Overrange */
393 {0x6050, 0x03, 2}, /* Limit 1 */
394 {0x6050, 0x05, 2}, /* Limit 2 */
395 {0x6050, 0x07, 1}, /* Error */
396 {0x0000, 0x00, 1}, /* Gap */
397 {0x0000, 0x00, 6}, /* Gap */
398 {0x6050, 0x0f, 1}, /* TxPDO State */
399 {0x6050, 0x10, 1}, /* TxPDO Toggle */
400 {0x6050, 0x11, 16}, /* Value */
401 {0x6060, 0x01, 1}, /* Underrange */
402 {0x6060, 0x02, 1}, /* Overrange */
403 {0x6060, 0x03, 2}, /* Limit 1 */
404 {0x6060, 0x05, 2}, /* Limit 2 */
405 {0x6060, 0x07, 1}, /* Error */
406 {0x0000, 0x00, 1}, /* Gap */
407 {0x0000, 0x00, 6}, /* Gap */
408 {0x6060, 0x0f, 1}, /* TxPDO State */
409 {0x6060, 0x10, 1}, /* TxPDO Toggle */
410 {0x6060, 0x11, 16}, /* Value */
411 {0x6070, 0x01, 1}, /* Underrange */
412 {0x6070, 0x02, 1}, /* Overrange */
413 {0x6070, 0x03, 2}, /* Limit 1 */
414 {0x6070, 0x05, 2}, /* Limit 2 */
415 {0x6070, 0x07, 1}, /* Error */
416 {0x0000, 0x00, 1}, /* Gap */
417 {0x0000, 0x00, 6}, /* Gap */
418 {0x6070, 0x0f, 1}, /* TxPDO State */
419 {0x6070, 0x10, 1}, /* TxPDO Toggle */
420 {0x6070, 0x11, 16}, /* Value */
421 };
422
423 ec_pdo_info_t el30x8_pdos[] = {
424 {0x1a00, 10, el30x8_pdo_entries + 0}, /* AI TxPDO-Map Standard Ch.1 */
425 {0x1a02, 10,
426 el30x8_pdo_entries + 10}, /* AI TxPDO-Map Standard Ch.2 */
427 {0x1a04, 10,
428 el30x8_pdo_entries + 20}, /* AI TxPDO-Map Standard Ch.3 */
429 {0x1a06, 10,
430 el30x8_pdo_entries + 30}, /* AI TxPDO-Map Standard Ch.4 */
431 {0x1a08, 10,
432 el30x8_pdo_entries + 40}, /* AI TxPDO-Map Standard Ch.5 */
433 {0x1a0a, 10,
434 el30x8_pdo_entries + 50}, /* AI TxPDO-Map Standard Ch.6 */
435 {0x1a0c, 10,
436 el30x8_pdo_entries + 60}, /* AI TxPDO-Map Standard Ch.7 */
437 {0x1a0e, 10,
438 el30x8_pdo_entries + 70}, /* AI TxPDO-Map Standard Ch.8 */
439 };
440
441 ec_sync_info_t el30x8_syncs[] = {
442 {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
443 {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
444 {2, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
445 {3, EC_DIR_INPUT, 8, el30x8_pdos + 0, EC_WD_DISABLE},
446 {0xff}};
447
448 /* Master 0, Slave 5, "EL3311"
449 * Vendor ID: 0x00000002
450 * Product code: 0x0cef3052
451 * Revision number: 0x00170000
452 */
453
454 ec_pdo_entry_info_t el3311_entries[] = {
455 {0x6000, 0x01, 1}, /* Underrange */
456 {0x6000, 0x02, 1}, /* Overrange */
457 {0x6000, 0x03, 2}, /* Limit 1 */
458 {0x6000, 0x05, 2}, /* Limit 2 */
459 {0x6000, 0x07, 1}, /* Error */
460 {0x0000, 0x00, 7}, /* Gap */
461 {0x6000, 0x0f, 1}, /* TxPDO State */
462 {0x1800, 0x09, 1}, {0x6000, 0x11, 16}, /* Value */
463 };
464
465 ec_pdo_info_t el3311_pdos[] = {
466 {0x1a00, 9, el3311_entries + 0}, /* TC TxPDO-MapCh.1 */
467 };
468
469 ec_sync_info_t el3311_syncs[] = {
470 {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
471 {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
472 {2, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
473 {3, EC_DIR_INPUT, 1, el3311_pdos + 0, EC_WD_DISABLE},
474 {0xff}};
475
476 /* Master 0, Slave 0, "EL3318"
477 * Vendor ID: 0x00000002
478 * Product code: 0x0cf63052
479 * Revision number: 0x00140000
480 */
481
482 ec_pdo_entry_info_t el3318_pdo_entries[] = {
483 {0x6000, 0x01, 1}, /* Underrange */
484 {0x6000, 0x02, 1}, /* Overrange */
485 {0x0000, 0x00, 4}, /* Gap */
486 {0x6000, 0x07, 1}, /* Error */
487 {0x0000, 0x00, 7}, /* Gap */
488 {0x6000, 0x0f, 1}, /* TxPDO State */
489 {0x1800, 0x09, 1}, {0x6000, 0x11, 16}, /* Value */
490 {0x6010, 0x01, 1}, /* Underrange */
491 {0x6010, 0x02, 1}, /* Overrange */
492 {0x0000, 0x00, 4}, /* Gap */
493 {0x6010, 0x07, 1}, /* Error */
494 {0x0000, 0x00, 7}, /* Gap */
495 {0x6010, 0x0f, 1}, /* TxPDO State */
496 {0x1801, 0x09, 1}, {0x6010, 0x11, 16}, /* Value */
497 {0x6020, 0x01, 1}, /* Underrange */
498 {0x6020, 0x02, 1}, /* Overrange */
499 {0x0000, 0x00, 4}, /* Gap */
500 {0x6020, 0x07, 1}, /* Error */
501 {0x0000, 0x00, 7}, /* Gap */
502 {0x6020, 0x0f, 1}, /* TxPDO State */
503 {0x1802, 0x09, 1}, {0x6020, 0x11, 16}, /* Value */
504 {0x6030, 0x01, 1}, /* Underrange */
505 {0x6030, 0x02, 1}, /* Overrange */
506 {0x0000, 0x00, 4}, /* Gap */
507 {0x6030, 0x07, 1}, /* Error */
508 {0x0000, 0x00, 7}, /* Gap */
509 {0x6030, 0x0f, 1}, /* TxPDO State */
510 {0x1803, 0x09, 1}, {0x6030, 0x11, 16}, /* Value */
511 {0x6040, 0x01, 1}, /* Underrange */
512 {0x6040, 0x02, 1}, /* Overrange */
513 {0x0000, 0x00, 4}, /* Gap */
514 {0x6040, 0x07, 1}, /* Error */
515 {0x0000, 0x00, 7}, /* Gap */
516 {0x6040, 0x0f, 1}, /* TxPDO State */
517 {0x1804, 0x09, 1}, {0x6040, 0x11, 16}, /* Value */
518 {0x6050, 0x01, 1}, /* Underrange */
519 {0x6050, 0x02, 1}, /* Overrange */
520 {0x0000, 0x00, 4}, /* Gap */
521 {0x6050, 0x07, 1}, /* Error */
522 {0x0000, 0x00, 7}, /* Gap */
523 {0x6050, 0x0f, 1}, /* TxPDO State */
524 {0x1805, 0x09, 1}, {0x6050, 0x11, 16}, /* Value */
525 {0x6060, 0x01, 1}, /* Underrange */
526 {0x6060, 0x02, 1}, /* Overrange */
527 {0x0000, 0x00, 4}, /* Gap */
528 {0x6060, 0x07, 1}, /* Error */
529 {0x0000, 0x00, 7}, /* Gap */
530 {0x6060, 0x0f, 1}, /* TxPDO State */
531 {0x1806, 0x09, 1}, {0x6060, 0x11, 16}, /* Value */
532 {0x6070, 0x01, 1}, /* Underrange */
533 {0x6070, 0x02, 1}, /* Overrange */
534 {0x0000, 0x00, 4}, /* Gap */
535 {0x6070, 0x07, 1}, /* Error */
536 {0x0000, 0x00, 7}, /* Gap */
537 {0x6070, 0x0f, 1}, /* TxPDO State */
538 {0x1807, 0x09, 1}, {0x6070, 0x11, 16}, /* Value */
539 };
540
541 ec_pdo_info_t el3318_pdos[] = {
542 {0x1a00, 8, el3318_pdo_entries + 0}, /* TC TxPDO-MapCh.1 */
543 {0x1a01, 8, el3318_pdo_entries + 8}, /* TC TxPDO-MapCh.2 */
544 {0x1a02, 8, el3318_pdo_entries + 16}, /* TC TxPDO-MapCh.3 */
545 {0x1a03, 8, el3318_pdo_entries + 24}, /* TC TxPDO-MapCh.4 */
546 {0x1a04, 8, el3318_pdo_entries + 32}, /* TC TxPDO-MapCh.5 */
547 {0x1a05, 8, el3318_pdo_entries + 40}, /* TC TxPDO-MapCh.6 */
548 {0x1a06, 8, el3318_pdo_entries + 48}, /* TC TxPDO-MapCh.7 */
549 {0x1a07, 8, el3318_pdo_entries + 56}, /* TC TxPDO-MapCh.8 */
550 };
551
552 ec_sync_info_t el3318_syncs[] = {
553 {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
554 {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
555 {2, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
556 {3, EC_DIR_INPUT, 8, el3318_pdos + 0, EC_WD_DISABLE},
557 {0xff}};
558
559 /* Master 0, Slave 6, "EM3702"
560 * Vendor ID: 0x00000002
561 * Product code: 0x0e763452
562 * Revision number: 0x00100000
563 */
564
565 ec_pdo_entry_info_t em3702_pdo_entries[] = {
566 {0x6000, 0x01, 1}, /* Underrange */
567 {0x6000, 0x02, 1}, /* Overrange */
568 {0x6000, 0x03, 2}, /* Limit 1 */
569 {0x6000, 0x05, 2}, /* Limit 2 */
570 {0x6000, 0x07, 1}, /* Error */
571 {0x0000, 0x00, 7}, /* Gap */
572 {0x6000, 0x0f, 1}, /* TxPDO State */
573 {0x0000, 0x00, 1}, /* Gap */
574 {0x6000, 0x11, 16}, /* Value */
575 {0x6010, 0x01, 1}, /* Underrange */
576 {0x6010, 0x02, 1}, /* Overrange */
577 {0x6010, 0x03, 2}, /* Limit 1 */
578 {0x6010, 0x05, 2}, /* Limit 2 */
579 {0x6010, 0x07, 1}, /* Error */
580 {0x0000, 0x00, 7}, /* Gap */
581 {0x6010, 0x0f, 1}, /* TxPDO State */
582 {0x0000, 0x00, 1}, /* Gap */
583 {0x6010, 0x11, 16}, /* Value */
584 };
585
586 ec_pdo_info_t em3702_pdos[] = {
587 {0x1a00, 9, em3702_pdo_entries + 0}, /* AI TxPDO-Map InputsCh.1 */
588 {0x1a01, 9, em3702_pdo_entries + 9}, /* AI TxPDO-Map InputsCh.2 */
589 };
590
591 ec_sync_info_t em3702_syncs[] = {
592 {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
593 {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
594 {2, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
595 {3, EC_DIR_INPUT, 2, em3702_pdos + 0, EC_WD_DISABLE},
596 {0xff}};
597
598 /* Master 0, Slave 2, "EL3602-0010"
599 * Vendor ID: 0x00000002
600 * Product code: 0x0e123052
601 * Revision number: 0x0012000a
602 */
603
604 ec_pdo_entry_info_t el3602_pdo_entries[] = {
605 {0x6000, 0x01, 1}, /* Underrange */
606 {0x6000, 0x02, 1}, /* Overrange */
607 {0x6000, 0x03, 2}, /* Limit 1 */
608 {0x6000, 0x05, 2}, /* Limit 2 */
609 {0x6000, 0x07, 1}, /* Error */
610 {0x0000, 0x00, 7}, /* Gap */
611 {0x1800, 0x07, 1}, {0x1800, 0x09, 1}, {0x6000, 0x11, 32}, /* Value */
612 {0x6010, 0x01, 1}, /* Underrange */
613 {0x6010, 0x02, 1}, /* Overrange */
614 {0x6010, 0x03, 2}, /* Limit 1 */
615 {0x6010, 0x05, 2}, /* Limit 2 */
616 {0x6010, 0x07, 1}, /* Error */
617 {0x0000, 0x00, 7}, /* Gap */
618 {0x1801, 0x07, 1}, {0x1801, 0x09, 1}, {0x6010, 0x11, 32}, /* Value */
619 };
620
621 ec_pdo_info_t el3602_pdos[] = {
622 {0x1a00, 9, el3602_pdo_entries + 0}, /* AI TxPDO-Map Inputs Ch.1 */
623 {0x1a01, 9, el3602_pdo_entries + 9}, /* AI TxPDO-Map Inputs Ch.2 */
624 };
625
626 ec_sync_info_t el3602_syncs[] = {
627 {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
628 {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
629 {2, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
630 {3, EC_DIR_INPUT, 2, el3602_pdos + 0, EC_WD_DISABLE},
631 {0xff}};
632
633 /****************************************************************************/
634
635 enum Width { W16 = 16, W32 = 32 };
636
637 enum InputTypeStyle { NoInputType, InputTypeEL3074, InputTypeEPP3184 };
638
639 struct Ex3xxxType
640 {
641 const char *name;
642 uint32_t product_code;
643 unsigned int num_channels;
644 unsigned int pdo_entry_index;
645 unsigned int pdo_entry_inc;
646 unsigned int value_pdo_entry_subindex;
647 Width width;
648 double value_scale;
649 double full_range;
650 InputTypeStyle inputTypeStyle;
651 ec_sync_info_t *sync;
652 };
653
654 const static Ex3xxxType ex3xxxType[Ex3xxx<Mode::Control>::NumTypes] = {
655
656 // EL3008
657 {"EL3008", 0x0bc03052, 8, 0x6000, 0x10, 0x11, W16, 1.0, 32768.0,
658 NoInputType, el30x8_syncs},
659
660 // EL3024
661 {"EL3024", 0x0bd03052, 4, 0x6000, 0x10, 0x11, W16, 1.0, 32768.0,
662 NoInputType, ex3xx4_syncs},
663
664 // EL3054
665 {"EL3054", 0x0bee3052, 4, 0x6000, 0x10, 0x11, W16, 1.0, 32768.0,
666 NoInputType, el3054_syncs},
667
668 // EL3062
669 {"EL3062", 0x0bf63052, 2, 0x6000, 0x10, 0x11, W16, 1.0, 32768.0,
670 NoInputType, el3062_syncs},
671
672 // EL3062-0030 (30 V)
673 {"EL3062-0030", 0x0bf63052, 2, 0x6000, 0x10, 0x11, W16, 30.0, 32768.0,
674 NoInputType, el3062_syncs},
675
676 // EL3068
677 {"EL3068", 0x0bfc3052, 8, 0x6000, 0x10, 0x11, W16, 1.0, 32768.0,
678 NoInputType, el30x8_syncs},
679
680 // EL3074 (extended range!)
681 {"EL3074", 0x0c023052, 4, 0x6000, 0x10, 0x11, W16, 1.0, 30518.0,
682 InputTypeEL3074, ex3xx4_syncs},
683
684 // EL3102
685 {"EL3102", 0x0c1e3052, 2, 0x3101, 0x01, 0x02, W16, 1.0, 32768.0,
686 NoInputType, el31x2_syncs},
687
688 // EL3104
689 {"EL3104", 0x0c203052, 4, 0x6000, 0x10, 0x11, W16, 1.0, 32768.0,
690 NoInputType, ex3xx4_syncs},
691
692 // EL3122
693 {"EL3122", 0x0c323052, 2, 0x3101, 0x01, 0x02, W16, 1.0, 32768.0,
694 NoInputType, el31x2_syncs},
695
696 // EL3164
697 {"EL3164", 0x0bf83052, 4, 0x6000, 0x10, 0x11, W16, 1.0, 32768.0,
698 NoInputType, el3164_syncs},
699
700 // EL3182 (extended range!)
701 {"EL3182", 0x0c6e3052, 2, 0x6000, 0x10, 0x11, W16, 1.0, 30518.0,
702 NoInputType, el3182_syncs},
703
704 // EL3311
705 {"EL3311", 0x0cef3052, 1, 0x6000, 0x00, 0x11, W16, 0.1 * 32768.0,
706 32768.0, NoInputType, el3311_syncs},
707
708 // EL3318
709 {"EL3318", 0x0cf63052, 8, 0x6000, 0x10, 0x11, W16, 0.1 * 32768.0,
710 32768.0, NoInputType, el3318_syncs},
711
712 // EL3602-0010
713 {"EL3602-0010", 0x0e123052, 2, 0x6000, 0x10, 0x11, W32, 75e-3,
714 2147483647.0, NoInputType, el3602_syncs},
715
716 // EM3702
717 {"EM3702", 0x0e763452, 2, 0x6000, 0x10, 0x11, W16, 7.5e5, 32768.0,
718 NoInputType, em3702_syncs},
719
720 // EPP3184
721 {"EPP3184", 0x64768d09, 4, 0x6000, 0x10, 0x11, W16, 1.0, 32768.0,
722 InputTypeEPP3184, ex3xx4_syncs},
723 };
724
725 /****************************************************************************/
726
727 template <Mode mode>
728 2 Ex3xxx<mode>::Ex3xxx(
729 pdserv *pdserv,
730 pdtask *task,
731 const std::string &prefix,
732 Master *master,
733 uint16_t alias,
734 uint16_t position,
735 Domain *domainIn,
736 Type typeIdx) :
737 2 type(ex3xxxType[typeIdx]),
738 sc(NULL),
739 domainIn(domainIn),
740 2 inputType(type.num_channels, InputPlusMinus10V),
741 2 inputTypeSet(type.num_channels, false),
742 2 off_sts(type.num_channels, 0),
743 2 off_in(type.num_channels, 0),
744 2 status(type.num_channels, 0),
745 4 scale(type.num_channels, type.value_scale),
746 2 offset(type.num_channels, 0.0),
747
8/32
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
✓ Branch 17 taken 2 times.
✗ Branch 18 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✓ Branch 27 taken 2 times.
✗ Branch 28 not taken.
✓ Branch 32 taken 2 times.
✗ Branch 33 not taken.
✓ Branch 37 taken 2 times.
✗ Branch 38 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
✗ Branch 89 not taken.
✗ Branch 90 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
18 input(type.num_channels, 0.0)
748 {
749
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
2 if (pdserv && task) {
750 pdserv_parameter(
751 pdserv, (prefix + "/Gain").c_str(), 0666, pd_double_T,
752 scale.data(), scale.size(), NULL, NULL, NULL);
753 pdserv_parameter(
754 pdserv, (prefix + "/Offset").c_str(), 0666, pd_double_T,
755 offset.data(), offset.size(), NULL, NULL, NULL);
756 pdserv_signal(
757 task, 1, (prefix + "/Input").c_str(), pd_double_T,
758 input.data(), input.size(), NULL);
759 pdserv_signal(
760 task, 1, (prefix + "/Status").c_str(), pd_uint8_T,
761 status.data(), status.size(), NULL);
762 }
763
764
1/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if (!master) {
765 2 return;
766 }
767
768 sc = ecrt_master_slave_config(
769 master->getMaster(), alias, position, 0x00000002,
770 type.product_code);
771 if (!sc) {
772 stringstream err;
773 err << "Failed to get slave configuration.";
774 throw runtime_error(err.str());
775 }
776
777 if (ecrt_slave_config_pdos(
778 sc, EC_END, SwappedSync<mode>(type.sync).getSync())) {
779 stringstream err;
780 err << "Failed to configure PDOs.";
781 throw runtime_error(err.str());
782 }
783
784 for (unsigned int c = 0; c < type.num_channels; c++) {
785 unsigned int entry_index =
786 type.pdo_entry_index + c * type.pdo_entry_inc;
787
788 off_sts[c] = ecrt_slave_config_reg_pdo_entry(
789 sc, entry_index, 0x01, domainIn->getDomain(), NULL);
790 if (off_sts[c] < 0) {
791 stringstream err;
792 err << "Failed to register Ex3xxx status PDO entry.";
793 throw runtime_error(err.str());
794 }
795
796 off_in[c] = ecrt_slave_config_reg_pdo_entry(
797 sc, entry_index, type.value_pdo_entry_subindex,
798 domainIn->getDomain(), NULL);
799 if (off_in[c] < 0) {
800 stringstream err;
801 err << "Failed to register Ex3xxx value PDO entry.";
802 throw runtime_error(err.str());
803 }
804 }
805 }
806
807 /****************************************************************************/
808
809 template <Mode mode>
810 2 void Ex3xxx<mode>::setInputType(unsigned int channel, InputType it)
811 {
812
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if (channel >= type.num_channels) {
813 return;
814 }
815
2/4
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
2 if (inputTypeSet[channel]) {
816
1/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2 stringstream err;
817
2/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
2 err << "Ex3xxx::setInputType() was already called for channel "
818
1/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 << channel
819 << "; the EtherCAT master queues every SDO download rather "
820 "than replacing an earlier one for the same object, so "
821 "a second call would not overwrite the first, only "
822 "waste an extra download at activation time.";
823
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 throw logic_error(err.str());
824 }
825 1 inputTypeSet[channel] = true;
826 1 inputType[channel] = it;
827
828
1/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (!sc) {
829 1 return;
830 }
831
832 switch (type.inputTypeStyle) {
833 case InputTypeEL3074: {
834 /* Messungs Modus, zulässige Werte:
835 2: V ±10V
836 14: V 0-10V
837 17: I ±20mA
838 18: I 0-20mA
839 19: I 4-20mA
840 20: I 4-20mA NAMUR
841 */
842 int sdoValue;
843 switch (it) {
844 case InputPlusMinus10V:
845 sdoValue = 2;
846 break;
847 case Input0To20mA:
848 sdoValue = 18;
849 break;
850 case Input4To20mA:
851 sdoValue = 19;
852 break;
853 case InputPlusMinus20mA:
854 sdoValue = 17;
855 break;
856 case Input0To10V:
857 sdoValue = 14;
858 break;
859 default:
860 throw runtime_error("Invalid input type");
861 }
862 int ret = ecrt_slave_config_sdo16(
863 sc, 0x800D + 0x0010 * channel, 0x11, sdoValue);
864 if (ret) {
865 stringstream err;
866 err << "Failed to configure SDO.";
867 throw runtime_error(err.str());
868 }
869 } break;
870
871 case InputTypeEPP3184: {
872 int ret = ecrt_slave_config_sdo16(sc, 0xF800, 0x01 + channel, it);
873 if (ret) {
874 stringstream err;
875 err << "Failed to configure SDO.";
876 throw runtime_error(err.str());
877 }
878 } break;
879
880 default:
881 break;
882 }
883 }
884
885 /****************************************************************************/
886
887 template <Mode mode>
888 1 void Ex3xxx<mode>::update()
889 {
890
2/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
9 for (unsigned int c = 0; c < type.num_channels; c++) {
891 8 status[c] = getStatus(c);
892 8 input[c] = getInput(c);
893 }
894 1 }
895
896 /****************************************************************************/
897
898 template <Mode mode>
899 1 void Ex3xxx<mode>::setScale(unsigned int channel, double newScale)
900 {
901
1/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (channel < type.num_channels) {
902 1 scale[channel] = newScale;
903 }
904 1 }
905
906 /****************************************************************************/
907
908 template <Mode mode>
909 1 void Ex3xxx<mode>::setOffset(unsigned int channel, double newOffset)
910 {
911
1/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (channel < type.num_channels) {
912 1 offset[channel] = newOffset;
913 }
914 1 }
915
916 /****************************************************************************/
917
918 template <Mode mode>
919 9 uint8_t Ex3xxx<mode>::getStatus(unsigned int channel) const
920 {
921
2/12
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 9 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
9 if (domainIn->getData() && channel < type.num_channels) {
922 return EC_READ_U8(domainIn->getData() + off_sts[channel]);
923 }
924 else {
925 9 return 0;
926 }
927 }
928
929 /****************************************************************************/
930
931 template <Mode mode>
932 9 double Ex3xxx<mode>::getInput(unsigned int channel) const
933 {
934
2/12
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 9 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
9 if (domainIn->getData() && channel < type.num_channels) {
935 if (type.width == W16) {
936 int16_t raw = EC_READ_S16(domainIn->getData() + off_in[channel]);
937 return raw / type.full_range * scale[channel] + offset[channel];
938 }
939 else { // W32
940 int32_t raw = EC_READ_S32(domainIn->getData() + off_in[channel]);
941 return raw / type.full_range * scale[channel] + offset[channel];
942 }
943 }
944 else {
945 9 return 0.0;
946 }
947 }
948
949 /****************************************************************************/
950
951 template <Mode mode>
952 void Ex3xxx<mode>::setStatus(unsigned int channel, uint8_t newStatus) const
953 {
954 if (domainIn->getData() && channel < type.num_channels) {
955 EC_WRITE_U8(domainIn->getData() + off_sts[channel], newStatus);
956 }
957 }
958
959 /****************************************************************************/
960
961 template <Mode mode>
962 void Ex3xxx<mode>::setInput(unsigned int channel, double value) const
963 {
964 if (domainIn->getData() && channel < type.num_channels
965 && scale[channel]) {
966 if (type.width == W16) {
967 int16_t raw(
968 (value - offset[channel]) * type.full_range
969 / scale[channel]);
970 EC_WRITE_S16(domainIn->getData() + off_in[channel], raw);
971 }
972 else { // W32
973 int32_t raw(
974 (value - offset[channel]) * type.full_range
975 / scale[channel]);
976 EC_WRITE_S32(domainIn->getData() + off_in[channel], raw);
977 }
978 }
979 }
980
981 /****************************************************************************/
982
983 template <Mode mode>
984 6 unsigned int Ex3xxx<mode>::numChannels() const
985 {
986 6 return type.num_channels;
987 }
988
989 /****************************************************************************/
990
991 template <Mode mode>
992 3 bool Ex3xxx<mode>::supportsInputType() const
993 {
994 3 return type.inputTypeStyle != NoInputType;
995 }
996
997 /****************************************************************************/
998
999 template class Ex3xxx<Mode::Control>;
1000 template class Ex3xxx<Mode::Simulation>;
1001
1002 /****************************************************************************/
1003 // Generic SubDevice adapter: pure analog input, no outputs. Also
1004 // implements SwitchableInputType so createSubDevice("EL3074") lets callers
1005 // do EcApp::capability<SwitchableInputType>(*device).setInputType(...),
1006 // while createSubDevice("EL3008") still dynamic_casts to
1007 // SwitchableInputType successfully (all Ex3xxx types share this adapter
1008 // class) but throws UnsupportedCapability from setInputType() itself,
1009 // since EL3008 has no switchable range.
1010 /****************************************************************************/
1011
1012 template <Mode mode>
1013 4 class Ex3xxxAdapter :
1014 public SubDevice,
1015 public SwitchableInputType,
1016 public ConfigurableScale
1017 {
1018 public:
1019 2 Ex3xxxAdapter(const SlaveContext &ctx, typename Ex3xxx<mode>::Type t) :
1020 2 device(ctx.pdServ,
1021 2 ctx.task,
1022 ctx.prefix,
1023 2 ctx.master,
1024 2 ctx.alias,
1025 2 ctx.position,
1026 2 ctx.domainIn,
1027 t),
1028 inputKinds_ {
1029 {"Input", device.numChannels(), double(0)},
1030
8/32
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 2 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 2 times.
✗ Branch 19 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 4 times.
✓ Branch 26 taken 2 times.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 66 not taken.
✗ Branch 67 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
10 {"Status", device.numChannels(), uint16_t(0)}}
1031 2 {}
1032
1033 1 void updateInputs() override { device.update(); }
1034 void updateOutputs() override {}
1035
1036 10 const std::vector<ChannelKind> &inputKinds() const override
1037 {
1038 10 return inputKinds_;
1039 }
1040 1 const std::vector<ChannelKind> &outputKinds() const override
1041 {
1042 1 return outputKinds_;
1043 }
1044
1045 3 void setInputType(unsigned int channel, AnalogInputType t) override
1046 {
1047
2/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (!device.supportsInputType()) {
1048
1/4
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
2 stringstream err;
1049
1/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 err << "This device does not support switchable input "
1050 "ranges.";
1051
2/8
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
1 throw UnsupportedCapability(err.str());
1052 }
1053 2 device.setInputType(channel, map(t));
1054 1 }
1055
1056 1 void setScale(unsigned int channel, double scale) override
1057 {
1058 1 device.setScale(channel, scale);
1059 1 }
1060
1061 1 void setOffset(unsigned int channel, double offset) override
1062 {
1063 1 device.setOffset(channel, offset);
1064 1 }
1065
1066 protected:
1067 2 ChannelValue getInputAt(size_t i) const override
1068 {
1069 // Kind blocks concatenated in inputKinds() order: all "Input"
1070 // channels first, then all "Status" channels.
1071 2 unsigned int numChannels = device.numChannels();
1072
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 if (i < numChannels) {
1073 1 return device.getInput(static_cast<unsigned int>(i));
1074 }
1075 else {
1076 2 return uint16_t(device.getStatus(
1077 1 static_cast<unsigned int>(i) - numChannels));
1078 }
1079 }
1080 void setOutputAt(size_t, const ChannelValue &) override
1081 {
1082 throw UnknownChannel("Ex3xxx has no output channels.");
1083 }
1084
1085 private:
1086 2 static typename Ex3xxx<mode>::InputType map(AnalogInputType t)
1087 {
1088
1/10
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 2 times.
2 switch (t) {
1089 case AnalogInputType::PlusMinus10V:
1090 return Ex3xxx<mode>::InputPlusMinus10V;
1091 case AnalogInputType::PlusMinus20mA:
1092 return Ex3xxx<mode>::InputPlusMinus20mA;
1093 case AnalogInputType::ZeroTo10V:
1094 return Ex3xxx<mode>::Input0To10V;
1095 case AnalogInputType::ZeroTo20mA:
1096 return Ex3xxx<mode>::Input0To20mA;
1097 2 case AnalogInputType::FourTo20mA:
1098 default:
1099 2 return Ex3xxx<mode>::Input4To20mA;
1100 }
1101 }
1102
1103 Ex3xxx<mode> device;
1104 std::vector<ChannelKind> inputKinds_;
1105 std::vector<ChannelKind> outputKinds_;
1106 };
1107
1108 /****************************************************************************/
1109
1110 namespace {
1111
1112 136 DeviceFactoryFn makeEx3xxxFactory(unsigned int typeIdx)
1113 {
1114 4 return [typeIdx](const SlaveContext &ctx) -> unique_ptr<SubDevice> {
1115
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ctx.mode == Mode::Control) {
1116 return make_unique<Ex3xxxAdapter<Mode::Control>>(
1117 ctx, static_cast<Ex3xxx<Mode::Control>::Type>(typeIdx));
1118 }
1119 else {
1120
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 return make_unique<Ex3xxxAdapter<Mode::Simulation>>(
1121 ctx,
1122 6 static_cast<Ex3xxx<Mode::Simulation>::Type>(typeIdx));
1123 }
1124 136 };
1125 }
1126
1127 struct Ex3xxxRegistrations
1128 {
1129 8 Ex3xxxRegistrations()
1130 {
1131
2/2
✓ Branch 0 taken 136 times.
✓ Branch 1 taken 8 times.
144 for (unsigned int i = 0; i < Ex3xxx<Mode::Control>::NumTypes; i++) {
1132 136 const auto &t = ex3xxxType[i];
1133
4/8
✓ Branch 3 taken 136 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 136 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 136 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 136 times.
✗ Branch 14 not taken.
680 Registry::instance().add(
1134 272 {t.name, __FILE__, 0x00000002, t.product_code,
1135
1/2
✓ Branch 1 taken 136 times.
✗ Branch 2 not taken.
272 AnyRevision, wrapFactory(makeEx3xxxFactory(i))});
1136 }
1137 8 }
1138 8 } ex3xxxRegistrations;
1139
1140 } // namespace
1141
1142 24 } // namespace EcApp
1143
1144 /****************************************************************************/
1145