GCC Code Coverage Report


Directory: ./
File: src/devices/Ex3xxx.cpp
Date: 2026-09-23 16:22:15
Exec Total Coverage
Lines: 98 194 50.5%
Branches: 64 466 13.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 Master *master,
730 uint16_t alias,
731 uint16_t position,
732 Domain *domainIn,
733 Type typeIdx) :
734 2 type(ex3xxxType[typeIdx]),
735 sc(NULL),
736 domainIn(domainIn),
737 2 inputType(type.num_channels, InputPlusMinus10V),
738 2 inputTypeSet(type.num_channels, false),
739 2 off_sts(type.num_channels, 0),
740 2 off_in(type.num_channels, 0),
741 2 status(type.num_channels, 0),
742 4 scale(type.num_channels, type.value_scale),
743 2 offset(type.num_channels, 0.0),
744
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)
745 {
746 // pdserv publishing (Input/Status/Gain/Offset) happens in
747 // Ex3xxxAdapter's constructor below, via SubDevice::
748 // registerInputChannel()/registerParameter() -- that is also what
749 // makes those channels alias-able through SubDevice::setAlias().
750
751
1/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if (!master) {
752 2 return;
753 }
754
755 ✗ sc = ecrt_master_slave_config(
756 master->getMaster(), alias, position, 0x00000002,
757 ✗ type.product_code);
758 ✗ if (!sc) {
759 ✗ stringstream err;
760 ✗ err << "Failed to get slave configuration.";
761 ✗ throw runtime_error(err.str());
762 }
763
764 ✗ if (ecrt_slave_config_pdos(
765 ✗ sc, EC_END, SwappedSync<mode>(type.sync).getSync())) {
766 ✗ stringstream err;
767 ✗ err << "Failed to configure PDOs.";
768 ✗ throw runtime_error(err.str());
769 }
770
771 ✗ for (unsigned int c = 0; c < type.num_channels; c++) {
772 ✗ unsigned int entry_index =
773 ✗ type.pdo_entry_index + c * type.pdo_entry_inc;
774
775 ✗ off_sts[c] = ecrt_slave_config_reg_pdo_entry(
776 sc, entry_index, 0x01, domainIn->getDomain(), NULL);
777 ✗ if (off_sts[c] < 0) {
778 ✗ stringstream err;
779 ✗ err << "Failed to register Ex3xxx status PDO entry.";
780 ✗ throw runtime_error(err.str());
781 }
782
783 ✗ off_in[c] = ecrt_slave_config_reg_pdo_entry(
784 ✗ sc, entry_index, type.value_pdo_entry_subindex,
785 domainIn->getDomain(), NULL);
786 ✗ if (off_in[c] < 0) {
787 ✗ stringstream err;
788 ✗ err << "Failed to register Ex3xxx value PDO entry.";
789 ✗ throw runtime_error(err.str());
790 }
791 }
792 }
793
794 /****************************************************************************/
795
796 template <Mode mode>
797 2 void Ex3xxx<mode>::setInputType(unsigned int channel, InputType it)
798 {
799
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) {
800 ✗ return;
801 }
802
2/4
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
2 if (inputTypeSet[channel]) {
803
1/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2 stringstream err;
804
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 "
805
1/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 << channel
806 << "; the EtherCAT master queues every SDO download rather "
807 "than replacing an earlier one for the same object, so "
808 "a second call would not overwrite the first, only "
809 "waste an extra download at activation time.";
810
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());
811 }
812 1 inputTypeSet[channel] = true;
813 1 inputType[channel] = it;
814
815
1/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (!sc) {
816 1 return;
817 }
818
819 ✗ switch (type.inputTypeStyle) {
820 ✗ case InputTypeEL3074: {
821 /* Messungs Modus, zulässige Werte:
822 2: V ±10V
823 14: V 0-10V
824 17: I ±20mA
825 18: I 0-20mA
826 19: I 4-20mA
827 20: I 4-20mA NAMUR
828 */
829 int sdoValue;
830 ✗ switch (it) {
831 ✗ case InputPlusMinus10V:
832 ✗ sdoValue = 2;
833 ✗ break;
834 ✗ case Input0To20mA:
835 ✗ sdoValue = 18;
836 ✗ break;
837 ✗ case Input4To20mA:
838 ✗ sdoValue = 19;
839 ✗ break;
840 ✗ case InputPlusMinus20mA:
841 ✗ sdoValue = 17;
842 ✗ break;
843 ✗ case Input0To10V:
844 ✗ sdoValue = 14;
845 ✗ break;
846 ✗ default:
847 ✗ throw runtime_error("Invalid input type");
848 }
849 ✗ int ret = ecrt_slave_config_sdo16(
850 ✗ sc, 0x800D + 0x0010 * channel, 0x11, sdoValue);
851 ✗ if (ret) {
852 ✗ stringstream err;
853 ✗ err << "Failed to configure SDO.";
854 ✗ throw runtime_error(err.str());
855 }
856 ✗ } break;
857
858 ✗ case InputTypeEPP3184: {
859 ✗ int ret = ecrt_slave_config_sdo16(sc, 0xF800, 0x01 + channel, it);
860 ✗ if (ret) {
861 ✗ stringstream err;
862 ✗ err << "Failed to configure SDO.";
863 ✗ throw runtime_error(err.str());
864 }
865 ✗ } break;
866
867 ✗ default:
868 ✗ break;
869 }
870 }
871
872 /****************************************************************************/
873
874 template <Mode mode>
875 1 void Ex3xxx<mode>::update()
876 {
877
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++) {
878 8 status[c] = getStatus(c);
879 8 input[c] = getInput(c);
880 }
881 1 }
882
883 /****************************************************************************/
884
885 template <Mode mode>
886 1 void Ex3xxx<mode>::setScale(unsigned int channel, double newScale)
887 {
888
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) {
889 1 scale[channel] = newScale;
890 }
891 1 }
892
893 /****************************************************************************/
894
895 template <Mode mode>
896 1 void Ex3xxx<mode>::setOffset(unsigned int channel, double newOffset)
897 {
898
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) {
899 1 offset[channel] = newOffset;
900 }
901 1 }
902
903 /****************************************************************************/
904
905 template <Mode mode>
906 9 uint8_t Ex3xxx<mode>::getStatus(unsigned int channel) const
907 {
908
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) {
909 ✗ return EC_READ_U8(domainIn->getData() + off_sts[channel]);
910 }
911 else {
912 9 return 0;
913 }
914 }
915
916 /****************************************************************************/
917
918 template <Mode mode>
919 9 double Ex3xxx<mode>::getInput(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 ✗ if (type.width == W16) {
923 ✗ int16_t raw = EC_READ_S16(domainIn->getData() + off_in[channel]);
924 ✗ return raw / type.full_range * scale[channel] + offset[channel];
925 }
926 else { // W32
927 ✗ int32_t raw = EC_READ_S32(domainIn->getData() + off_in[channel]);
928 ✗ return raw / type.full_range * scale[channel] + offset[channel];
929 }
930 }
931 else {
932 9 return 0.0;
933 }
934 }
935
936 /****************************************************************************/
937
938 template <Mode mode>
939 ✗ void Ex3xxx<mode>::setStatus(unsigned int channel, uint8_t newStatus) const
940 {
941 ✗ if (domainIn->getData() && channel < type.num_channels) {
942 ✗ EC_WRITE_U8(domainIn->getData() + off_sts[channel], newStatus);
943 }
944 }
945
946 /****************************************************************************/
947
948 template <Mode mode>
949 ✗ void Ex3xxx<mode>::setInput(unsigned int channel, double value) const
950 {
951 ✗ if (domainIn->getData() && channel < type.num_channels
952 ✗ && scale[channel]) {
953 ✗ if (type.width == W16) {
954 ✗ int16_t raw(
955 ✗ (value - offset[channel]) * type.full_range
956 ✗ / scale[channel]);
957 ✗ EC_WRITE_S16(domainIn->getData() + off_in[channel], raw);
958 }
959 else { // W32
960 ✗ int32_t raw(
961 ✗ (value - offset[channel]) * type.full_range
962 ✗ / scale[channel]);
963 ✗ EC_WRITE_S32(domainIn->getData() + off_in[channel], raw);
964 }
965 }
966 }
967
968 /****************************************************************************/
969
970 template <Mode mode>
971 6 unsigned int Ex3xxx<mode>::numChannels() const
972 {
973 6 return type.num_channels;
974 }
975
976 /****************************************************************************/
977
978 template <Mode mode>
979 3 bool Ex3xxx<mode>::supportsInputType() const
980 {
981 3 return type.inputTypeStyle != NoInputType;
982 }
983
984 /****************************************************************************/
985
986 template class Ex3xxx<Mode::Control>;
987 template class Ex3xxx<Mode::Simulation>;
988
989 /****************************************************************************/
990 // Generic SubDevice adapter: pure analog input, no outputs. Also
991 // implements SwitchableInputType so createSubDevice("EL3074") lets callers
992 // do EcApp::capability<SwitchableInputType>(*device).setInputType(...),
993 // while createSubDevice("EL3008") still dynamic_casts to
994 // SwitchableInputType successfully (all Ex3xxx types share this adapter
995 // class) but throws UnsupportedCapability from setInputType() itself,
996 // since EL3008 has no switchable range.
997 /****************************************************************************/
998
999 template <Mode mode>
1000 4 class Ex3xxxAdapter :
1001 public SubDevice,
1002 public SwitchableInputType,
1003 public ConfigurableScale
1004 {
1005 public:
1006 2 Ex3xxxAdapter(const SlaveContext &ctx, typename Ex3xxx<mode>::Type t) :
1007 2 SubDevice(nullptr, ctx.domainIn, ctx.pdServ, ctx.task, ctx.prefix),
1008 6 device(ctx.master, ctx.alias, ctx.position, ctx.domainIn, t),
1009 inputKinds_ {
1010 {"Input", device.numChannels(), double(0)},
1011
9/36
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 2 times.
✗ Branch 16 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✓ Branch 26 taken 2 times.
✗ Branch 27 not taken.
✓ Branch 29 taken 4 times.
✓ Branch 30 taken 2 times.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✗ Branch 87 not taken.
✗ Branch 88 not taken.
10 {"Status", device.numChannels(), uint8_t(0)}}
1012 {
1013
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerInputChannel("Input", device.input);
1014
2/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 registerInputChannel("Status", device.status);
1015
3/12
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 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.
2 registerParameter("Gain", "Input", device.scale);
1016
3/12
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 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.
2 registerParameter("Offset", "Input", device.offset);
1017 2 }
1018
1019 1 void updateInputs() override { device.update(); }
1020 ✗ void updateOutputs() override {}
1021
1022 10 const std::vector<ChannelKind> &inputKinds() const override
1023 {
1024 10 return inputKinds_;
1025 }
1026 1 const std::vector<ChannelKind> &outputKinds() const override
1027 {
1028 1 return outputKinds_;
1029 }
1030
1031 3 void setInputType(unsigned int channel, AnalogInputType t) override
1032 {
1033
2/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (!device.supportsInputType()) {
1034
1/4
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
2 stringstream err;
1035
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 "
1036 "ranges.";
1037
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());
1038 }
1039 2 device.setInputType(channel, map(t));
1040 1 }
1041
1042 1 void setScale(unsigned int channel, double scale) override
1043 {
1044 1 device.setScale(channel, scale);
1045 1 }
1046
1047 1 void setOffset(unsigned int channel, double offset) override
1048 {
1049 1 device.setOffset(channel, offset);
1050 1 }
1051
1052 protected:
1053 2 ChannelValue getInputAt(size_t i) const override
1054 {
1055 // Kind blocks concatenated in inputKinds() order: all "Input"
1056 // channels first, then all "Status" channels.
1057 2 unsigned int numChannels = device.numChannels();
1058
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 if (i < numChannels) {
1059 1 return device.getInput(static_cast<unsigned int>(i));
1060 }
1061 else {
1062 return device.getStatus(
1063 1 static_cast<unsigned int>(i) - numChannels);
1064 }
1065 }
1066 ✗ void setOutputAt(size_t, const ChannelValue &) override
1067 {
1068 ✗ throw UnknownChannel("Ex3xxx has no output channels.");
1069 }
1070
1071 private:
1072 2 static typename Ex3xxx<mode>::InputType map(AnalogInputType t)
1073 {
1074
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) {
1075 ✗ case AnalogInputType::PlusMinus10V:
1076 ✗ return Ex3xxx<mode>::InputPlusMinus10V;
1077 ✗ case AnalogInputType::PlusMinus20mA:
1078 ✗ return Ex3xxx<mode>::InputPlusMinus20mA;
1079 ✗ case AnalogInputType::ZeroTo10V:
1080 ✗ return Ex3xxx<mode>::Input0To10V;
1081 ✗ case AnalogInputType::ZeroTo20mA:
1082 ✗ return Ex3xxx<mode>::Input0To20mA;
1083 2 case AnalogInputType::FourTo20mA:
1084 default:
1085 2 return Ex3xxx<mode>::Input4To20mA;
1086 }
1087 }
1088
1089 Ex3xxx<mode> device;
1090 std::vector<ChannelKind> inputKinds_;
1091 std::vector<ChannelKind> outputKinds_;
1092 };
1093
1094 /****************************************************************************/
1095
1096 namespace {
1097
1098 204 DeviceFactoryFn makeEx3xxxFactory(unsigned int typeIdx)
1099 {
1100 4 return [typeIdx](const SlaveContext &ctx) -> unique_ptr<SubDevice> {
1101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ctx.mode == Mode::Control) {
1102 ✗ return make_unique<Ex3xxxAdapter<Mode::Control>>(
1103 ✗ ctx, static_cast<Ex3xxx<Mode::Control>::Type>(typeIdx));
1104 }
1105 else {
1106
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 return make_unique<Ex3xxxAdapter<Mode::Simulation>>(
1107 ctx,
1108 6 static_cast<Ex3xxx<Mode::Simulation>::Type>(typeIdx));
1109 }
1110 204 };
1111 }
1112
1113 struct Ex3xxxRegistrations
1114 {
1115 12 Ex3xxxRegistrations()
1116 {
1117
2/2
✓ Branch 0 taken 204 times.
✓ Branch 1 taken 12 times.
216 for (unsigned int i = 0; i < Ex3xxx<Mode::Control>::NumTypes; i++) {
1118 204 const auto &t = ex3xxxType[i];
1119
4/8
✓ Branch 3 taken 204 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 204 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 204 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 204 times.
✗ Branch 14 not taken.
1020 Registry::instance().add(
1120 408 {t.name, __FILE__, 0x00000002, t.product_code,
1121
1/2
✓ Branch 1 taken 204 times.
✗ Branch 2 not taken.
408 AnyRevision, wrapFactory(makeEx3xxxFactory(i))});
1122 }
1123 12 }
1124 12 } ex3xxxRegistrations;
1125
1126 } // namespace
1127
1128 36 } // namespace EcApp
1129
1130 /****************************************************************************/
1131