Lely core libraries 1.9.2
dcf.c
Go to the documentation of this file.
1
25#include "co.h"
26
27#ifndef LELY_NO_CO_DCF
28
29#include "obj.h"
30#include <lely/co/dcf.h>
31#include <lely/co/pdo.h>
32#include <lely/libc/strings.h>
33#include <lely/libc/stdio.h>
34#include <lely/util/config.h>
35#include <lely/util/diag.h>
36#include <lely/util/lex.h>
37
38#include <assert.h>
39#include <stdlib.h>
40#include <string.h>
41
42static struct __co_dev *__co_dev_init_from_dcf_cfg(
43 struct __co_dev *dev, const config_t *cfg);
44
45static int co_dev_parse_cfg(co_dev_t *dev, const config_t *cfg);
46
47static int co_obj_parse_cfg(
48 co_obj_t *obj, const config_t *cfg, const char *section);
49#ifndef LELY_NO_CO_OBJ_NAME
50static int co_obj_parse_names(co_obj_t *obj, const config_t *cfg);
51#endif
52static int co_obj_parse_values(co_obj_t *obj, const config_t *cfg);
53static co_obj_t *co_obj_build(co_dev_t *dev, co_unsigned16_t idx);
54
55static int co_sub_parse_cfg(
56 co_sub_t *sub, const config_t *cfg, const char *section);
57static co_sub_t *co_sub_build(co_obj_t *obj, co_unsigned8_t subidx,
58 co_unsigned16_t type, const char *name);
59
60static int co_rpdo_build(co_dev_t *dev, co_unsigned16_t num, int mask);
61static int co_tpdo_build(co_dev_t *dev, co_unsigned16_t num, int mask);
62
63static void co_val_set_id(co_unsigned16_t type, void *val, co_unsigned8_t id);
64
65static co_unsigned16_t config_get_idx(const config_t *cfg, const char *section,
66 co_unsigned16_t maxidx, co_unsigned16_t *idx);
67
68struct __co_dev *
69__co_dev_init_from_dcf_file(struct __co_dev *dev, const char *filename)
70{
72 if (__unlikely(!cfg)) {
74 "unable to create configuration struct");
75 goto error_create_cfg;
76 }
77
78 if (__unlikely(!config_parse_ini_file(cfg, filename)))
79 goto error_parse_ini_file;
80
81 if (__unlikely(!__co_dev_init_from_dcf_cfg(dev, cfg)))
82 goto error_init_dev;
83
84 config_destroy(cfg);
85
86 return dev;
87
88error_init_dev:
89error_parse_ini_file:
90 config_destroy(cfg);
91error_create_cfg:
92 return NULL;
93}
94
96co_dev_create_from_dcf_file(const char *filename)
97{
98 int errc = 0;
99
100 co_dev_t *dev = __co_dev_alloc();
101 if (__unlikely(!dev)) {
102 errc = get_errc();
103 goto error_alloc_dev;
104 }
105
106 if (__unlikely(!__co_dev_init_from_dcf_file(dev, filename))) {
107 errc = get_errc();
108 goto error_init_dev;
109 }
110
111 return dev;
112
113error_init_dev:
114 __co_dev_free(dev);
115error_alloc_dev:
116 set_errc(errc);
117 return NULL;
118}
119
120struct __co_dev *
121__co_dev_init_from_dcf_text(struct __co_dev *dev, const char *begin,
122 const char *end, struct floc *at)
123{
125 if (__unlikely(!cfg)) {
127 "unable to create configuration struct");
128 goto error_create_cfg;
129 }
130
131 if (__unlikely(!config_parse_ini_text(cfg, begin, end, at)))
132 goto error_parse_ini_text;
133
134 if (__unlikely(!__co_dev_init_from_dcf_cfg(dev, cfg)))
135 goto error_init_dev;
136
137 config_destroy(cfg);
138
139 return dev;
140
141error_init_dev:
142error_parse_ini_text:
143 config_destroy(cfg);
144error_create_cfg:
145 return NULL;
146}
147
148co_dev_t *
149co_dev_create_from_dcf_text(const char *begin, const char *end, struct floc *at)
150{
151 int errc = 0;
152
153 co_dev_t *dev = __co_dev_alloc();
154 if (__unlikely(!dev)) {
155 errc = get_errc();
156 goto error_alloc_dev;
157 }
158
159 if (__unlikely(!__co_dev_init_from_dcf_text(dev, begin, end, at))) {
160 errc = get_errc();
161 goto error_init_dev;
162 }
163
164 return dev;
165
166error_init_dev:
167 __co_dev_free(dev);
168error_alloc_dev:
169 set_errc(errc);
170 return NULL;
171}
172
173static struct __co_dev *
174__co_dev_init_from_dcf_cfg(struct __co_dev *dev, const config_t *cfg)
175{
176 assert(dev);
177 assert(cfg);
178
179 if (__unlikely(!__co_dev_init(dev, 0xff))) {
181 "unable to initialize device description");
182 goto error_init_dev;
183 }
184
185 if (__unlikely(co_dev_parse_cfg(dev, cfg) == -1))
186 goto error_parse_cfg;
187
188 return dev;
189
190error_parse_cfg:
191 __co_dev_fini(dev);
192error_init_dev:
193 return NULL;
194}
195
196static int
197co_dev_parse_cfg(co_dev_t *dev, const config_t *cfg)
198{
199 assert(dev);
200 assert(cfg);
201
202 const char *val;
203
204 // clang-format off
206 config_get(cfg, "DeviceInfo", "VendorName")) == -1)) {
207 // clang-format on
208 diag(DIAG_ERROR, get_errc(), "unable to set vendor name");
209 goto error_parse_dev;
210 }
211
212 val = config_get(cfg, "DeviceInfo", "VendorNumber");
213 if (val && *val)
214 co_dev_set_vendor_id(dev, strtoul(val, NULL, 0));
215
216 // clang-format off
218 config_get(cfg, "DeviceInfo", "ProductName")) == -1)) {
219 // clang-format on
220 diag(DIAG_ERROR, get_errc(), "unable to set product name");
221 goto error_parse_dev;
222 }
223
224 val = config_get(cfg, "DeviceInfo", "ProductNumber");
225 if (val && *val)
226 co_dev_set_product_code(dev, strtoul(val, NULL, 0));
227
228 val = config_get(cfg, "DeviceInfo", "RevisionNumber");
229 if (val && *val)
230 co_dev_set_revision(dev, strtoul(val, NULL, 0));
231
232 // clang-format off
234 config_get(cfg, "DeviceInfo", "OrderCode")) == -1)) {
235 diag(DIAG_ERROR, get_errc(), "unable to set order code");
236 goto error_parse_dev;
237 // clang-format on
238 }
239
240 unsigned int baud = 0;
241 val = config_get(cfg, "DeviceInfo", "BaudRate_10");
242 if (val && *val && strtoul(val, NULL, 0))
243 baud |= CO_BAUD_10;
244 val = config_get(cfg, "DeviceInfo", "BaudRate_20");
245 if (val && *val && strtoul(val, NULL, 0))
246 baud |= CO_BAUD_20;
247 val = config_get(cfg, "DeviceInfo", "BaudRate_50");
248 if (val && *val && strtoul(val, NULL, 0))
249 baud |= CO_BAUD_50;
250 val = config_get(cfg, "DeviceInfo", "BaudRate_125");
251 if (val && *val && strtoul(val, NULL, 0))
252 baud |= CO_BAUD_125;
253 val = config_get(cfg, "DeviceInfo", "BaudRate_250");
254 if (val && *val && strtoul(val, NULL, 0))
255 baud |= CO_BAUD_250;
256 val = config_get(cfg, "DeviceInfo", "BaudRate_500");
257 if (val && *val && strtoul(val, NULL, 0))
258 baud |= CO_BAUD_500;
259 val = config_get(cfg, "DeviceInfo", "BaudRate_800");
260 if (val && *val && strtoul(val, NULL, 0))
261 baud |= CO_BAUD_800;
262 val = config_get(cfg, "DeviceInfo", "BaudRate_1000");
263 if (val && *val && strtoul(val, NULL, 0))
265 co_dev_set_baud(dev, baud);
266
267 val = config_get(cfg, "DeviceInfo", "LSS_Supported");
268 if (val && *val)
269 co_dev_set_lss(dev, strtoul(val, NULL, 0));
270
271 // For each of the basic data types, check whether it is supported for
272 // mapping dummy entries in PDOs.
273 co_unsigned32_t dummy = 0;
274 for (int i = 0; i < 0x20; i++) {
275 // Create the key name.
276 char key[10];
277 sprintf(key, "Dummy%04X", (co_unsigned16_t)i);
278
279 val = config_get(cfg, "DummyUsage", key);
280 if (val && *val && strtoul(val, NULL, 0))
281 dummy |= 1u << i;
282 }
284
285 // Count the total number of objects.
286 co_unsigned16_t n = 0;
287 n += config_get_idx(cfg, "MandatoryObjects", 0, NULL);
288 n += config_get_idx(cfg, "OptionalObjects", 0, NULL);
289 n += config_get_idx(cfg, "ManufacturerObjects", 0, NULL);
290
291 // Parse the object indices.
292 co_unsigned16_t *idx = malloc(n * sizeof(co_unsigned16_t));
293 if (__unlikely(!idx)) {
294 diag(DIAG_ERROR, errno2c(errno),
295 "unable to create object list");
296 goto error_parse_idx;
297 }
298 co_unsigned16_t i = 0;
299 i += config_get_idx(cfg, "MandatoryObjects", n - i, idx + i);
300 i += config_get_idx(cfg, "OptionalObjects", n - i, idx + i);
301 config_get_idx(cfg, "ManufacturerObjects", n - i, idx + i);
302
303 for (i = 0; i < n; i++) {
304 if (__unlikely(!idx[i])) {
305 diag(DIAG_ERROR, 0, "entry (%d) missing in object list",
306 i);
307 goto error_parse_obj;
308 }
309
310 // Create the section name for the object.
311 char section[5];
312 sprintf(section, "%X", idx[i]);
313
314 // Create the object and add it to the dictionary.
315 co_obj_t *obj = co_obj_build(dev, idx[i]);
316 if (__unlikely(!obj))
317 goto error_parse_obj;
318
319 // Parse the configuration section for the object.
320 if (__unlikely(co_obj_parse_cfg(obj, cfg, section) == -1))
321 goto error_parse_obj;
322 }
323
324 if (!co_dev_find_obj(dev, 0x1000))
325 diag(DIAG_WARNING, 0, "mandatory object 0x1000 missing");
326 if (!co_dev_find_obj(dev, 0x1001))
327 diag(DIAG_WARNING, 0, "mandatory object 0x1001 missing");
328 if (!co_dev_find_obj(dev, 0x1018))
329 diag(DIAG_WARNING, 0, "mandatory object 0x1018 missing");
330
331 // Parse compact PDO definitions after the explicit object definitions
332 // to prevent overwriting PDOs.
333 val = config_get(cfg, "DeviceInfo", "CompactPDO");
334 if (val && *val) {
335 unsigned int mask = strtoul(val, NULL, 0);
336
337 co_unsigned16_t nrpdo = 0;
338 val = config_get(cfg, "DeviceInfo", "NrOfRxPDO");
339 if (val && *val)
340 nrpdo = (co_unsigned16_t)strtoul(val, NULL, 0);
341 for (co_unsigned16_t num = 1; num <= nrpdo; num++) {
342 if (__unlikely(co_rpdo_build(dev, num, mask) == -1))
343 goto error_parse_pdo;
344 }
345
346 co_unsigned16_t ntpdo = 0;
347 val = config_get(cfg, "DeviceInfo", "NrOfTxPDO");
348 if (val && *val)
349 ntpdo = (co_unsigned16_t)strtoul(val, NULL, 0);
350 for (co_unsigned16_t num = 1; num <= ntpdo; num++) {
351 if (__unlikely(co_tpdo_build(dev, num, mask) == -1))
352 goto error_parse_pdo;
353 }
354 }
355
356 val = config_get(cfg, "DeviceComissioning", "NodeID");
357 // clang-format off
358 if (val && *val && co_dev_set_id(dev,
359 (co_unsigned8_t)strtoul(val, NULL, 0)) == -1) {
360 // clang-format on
361 diag(DIAG_ERROR, get_errc(), "invalid node-ID (%s) specified",
362 val);
363 goto error_parse_dcf;
364 }
365
366 val = config_get(cfg, "DeviceComissioning", "NetNumber");
367 // clang-format off
368 if (val && *val && co_dev_set_netid(dev,
369 (co_unsigned32_t)strtoul(val, NULL, 0)) == -1) {
370 // clang-format on
372 "invalid network-ID (%s) specified", val);
373 goto error_parse_dcf;
374 }
375
376 // clang-format off
378 config_get(cfg, "DeviceComissioning", "NodeName"))
379 == -1)) {
380 // clang-format on
381 diag(DIAG_ERROR, get_errc(), "unable to set node name");
382 goto error_parse_dcf;
383 }
384
385 val = config_get(cfg, "DeviceComissioning", "Baudrate");
386 if (val && *val)
387 co_dev_set_rate(dev, (co_unsigned16_t)strtoul(val, NULL, 0));
388
389 val = config_get(cfg, "DeviceComissioning", "LSS_SerialNumber");
390 // clang-format off
391 if (val && *val && !co_dev_set_val_u32(dev, 0x1018, 0x04,
392 strtoul(val, NULL, 0))) {
393 // clang-format on
394 diag(DIAG_ERROR, get_errc(), "unable to set serial number");
395 goto error_parse_dcf;
396 }
397
398 free(idx);
399
400 return 0;
401
402error_parse_pdo:
403error_parse_dcf:
404error_parse_obj:
405 free(idx);
406error_parse_idx:
407error_parse_dev:
408 return -1;
409}
410
411static int
412co_obj_parse_cfg(co_obj_t *obj, const config_t *cfg, const char *section)
413{
414 assert(obj);
415 assert(cfg);
416
417 const char *val;
418 struct floc at = { section, 0, 0 };
419
420 co_unsigned16_t idx = co_obj_get_idx(obj);
421
422 const char *name = config_get(cfg, section, "ParameterName");
423 if (__unlikely(!name || !*name)) {
424 diag(DIAG_ERROR, 0,
425 "ParameterName not specified for object 0x%04X",
426 idx);
427 return -1;
428 }
429#ifndef LELY_NO_CO_OBJ_NAME
430 val = config_get(cfg, section, "Denotation");
431 if (val && *val)
432 name = val;
433 if (__unlikely(co_obj_set_name(obj, name) == -1)) {
435 "unable to set name of object 0x%04X", idx);
436 return -1;
437 }
438#endif
439
440 co_unsigned8_t code = co_obj_get_code(obj);
441 val = config_get(cfg, section, "ObjectType");
442 if (val && *val) {
443 code = (co_unsigned8_t)strtoul(val, NULL, 0);
444 if (__unlikely(co_obj_set_code(obj, code) == -1)) {
445 diag(DIAG_ERROR, 0,
446 "ObjectType = 0x%x for object 0x%04X",
447 code, idx);
448 return -1;
449 }
450 }
451
452 if (code == CO_OBJECT_DEFSTRUCT || code == CO_OBJECT_ARRAY
453 || code == CO_OBJECT_RECORD) {
454 co_unsigned8_t subnum = 0;
455 val = config_get(cfg, section, "SubNumber");
456 if (val && *val)
457 subnum = (co_unsigned8_t)strtoul(val, NULL, 0);
458 co_unsigned8_t subobj = 0;
459 val = config_get(cfg, section, "CompactSubObj");
460 if (val && *val)
461 subobj = (co_unsigned8_t)strtoul(val, NULL, 0);
462 if (__unlikely(!subnum && !subobj)) {
463 diag(DIAG_ERROR, 0,
464 "neither SubNumber nor CompactSubObj specified for object 0x%04X",
465 idx);
466 return -1;
467 }
468 if (__unlikely(subnum && subobj)) {
469 diag(DIAG_ERROR, 0,
470 "both SubNumber and CompactSubObj specified for object 0x%04X",
471 idx);
472 return -1;
473 }
474
475 // Parse the sub-objects specified by SubNumber.
476 for (size_t subidx = 0; subnum && subidx < 0xff; subidx++) {
477 // Create section name for the sub-object.
478 char section[10];
479 sprintf(section, "%Xsub%X", (co_unsigned16_t)idx,
480 (co_unsigned8_t)subidx);
481
482 // Check whether the sub-index exists by checking the
483 // presence of the mandatory ParameterName keyword.
484 const char *name = config_get(
485 cfg, section, "ParameterName");
486 if (!name || !*name)
487 continue;
488 subnum--;
489
490 // The Denonation entry, if it exists, overrides
491 // ParameterName.
492 val = config_get(cfg, section, "Denotation");
493 if (val && *val)
494 name = val;
495
496 // Obtain the data type of the sub-object.
497 val = config_get(cfg, section, "DataType");
498 if (!val || !*val) {
499 diag_at(DIAG_ERROR, 0, &at,
500 "DataType not specified");
501 return -1;
502 }
503 co_unsigned16_t type =
504 (co_unsigned16_t)strtoul(val, NULL, 0);
505
506 // Create and insert the sub-object.
507 co_sub_t *sub = co_sub_build(obj,
508 (co_unsigned8_t)subidx, type, name);
509 if (__unlikely(!sub))
510 return -1;
511
512 // Parse the configuration section for the sub-object.
513 // clang-format off
514 if (__unlikely(co_sub_parse_cfg(sub, cfg, section)
515 == -1))
516 // clang-format on
517 return -1;
518 }
519
520 // Create an array based on CompactSubObj.
521 if (subobj) {
522 co_sub_t *sub = co_sub_build(obj, 0,
523 CO_DEFTYPE_UNSIGNED8, "NrOfObjects");
524 if (__unlikely(!sub))
525 return -1;
526 co_val_make(sub->type, &sub->def, &subobj,
527 sizeof(subobj));
528 co_val_copy(sub->type, sub->val, &sub->def);
530
531 name = config_get(cfg, section, "ParameterName");
532
533 // Obtain the data type of the sub-object.
534 val = config_get(cfg, section, "DataType");
535 if (!val || !*val) {
536 diag_at(DIAG_ERROR, 0, &at,
537 "DataType not specified");
538 return -1;
539 }
540 co_unsigned16_t type =
541 (co_unsigned16_t)strtoul(val, NULL, 0);
542
543 // Create the sub-objects.
544 for (size_t subidx = 1; subidx <= subobj; subidx++) {
545 // Create name of the sub-object.
546 char *subname = NULL;
547 // clang-format off
548 if (__unlikely(asprintf(&subname, "%s%u", name,
549 (co_unsigned8_t)subidx) < 0))
550 // clang-format on
551 return -1;
552
553 // Create and insert the sub-object.
554 sub = co_sub_build(obj, (co_unsigned8_t)subidx,
555 type, subname);
556 free(subname);
557 if (__unlikely(!sub))
558 return -1;
559
560 // Parse the configuration section for the
561 // sub-object.
562 // clang-format off
563 if (__unlikely(co_sub_parse_cfg(sub, cfg,
564 section) == -1))
565 // clang-format on
566 return -1;
567 }
568
569#ifndef LELY_NO_CO_OBJ_NAME
570 // Parse the names of the sub-objects.
571 if (__unlikely(co_obj_parse_names(obj, cfg) == -1))
572 return -1;
573#endif
574
575 // Parse the values of the sub-objects.
576 if (__unlikely(co_obj_parse_values(obj, cfg) == -1))
577 return -1;
578 }
579
580 co_sub_t *sub = co_obj_find_sub(obj, 0x00);
581 // clang-format off
582 if (__unlikely(!sub || co_sub_get_type(sub)
584 // clang-format on
585 diag(DIAG_ERROR, 0,
586 "object 0x%04X does not provide the highest sub-index implemented",
587 idx);
588 return -1;
589 }
590 } else {
591 // Obtain the data type of the object (optional for DOMAIN
592 // objects).
593 co_unsigned16_t type = code == CO_OBJECT_DOMAIN
595 : 0;
596 val = config_get(cfg, section, "DataType");
597 if (val && *val)
598 type = (co_unsigned16_t)strtoul(val, NULL, 0);
599 if (__unlikely(!type)) {
600 diag_at(DIAG_ERROR, 0, &at, "DataType not specified");
601 return -1;
602 }
603
604 // Create and insert the sub-object.
605 co_sub_t *sub = co_sub_build(obj, 0, type, name);
606 if (__unlikely(!sub))
607 return -1;
608
609 // Parse the configuration section for the sub-object.
610 if (__unlikely(co_sub_parse_cfg(sub, cfg, section) == -1))
611 return -1;
612 }
613
614 return 0;
615}
616
617#ifndef LELY_NO_CO_OBJ_NAME
618static int
619co_obj_parse_names(co_obj_t *obj, const config_t *cfg)
620{
621 assert(obj);
622 assert(cfg);
623
624 co_unsigned16_t idx = co_obj_get_idx(obj);
625
626 // Create the section name for the explicit names of the sub-objects.
627 char section[9];
628 sprintf(section, "%XName", idx);
629
630 const char *val = config_get(cfg, section, "NrOfEntries");
631 if (!val || !*val)
632 return 0;
633
634 co_unsigned8_t n = (co_unsigned8_t)strtoul(val, NULL, 0);
635 for (size_t subidx = 1; n && subidx < 0xff; subidx++) {
636 char key[4];
637 sprintf(key, "%u", (co_unsigned8_t)subidx);
638
639 val = config_get(cfg, section, key);
640 if (val && *val) {
641 n--;
643 obj, (co_unsigned8_t)subidx);
644 // clang-format off
645 if (sub && __unlikely(co_sub_set_name(sub, val)
646 == -1)) {
647 // clang-format on
649 "unable to set name of sub-object %Xsub%X",
650 (co_unsigned16_t)idx,
651 (co_unsigned8_t)subidx);
652 return -1;
653 }
654 }
655 }
656
657 return 0;
658}
659#endif // LELY_NO_CO_OBJ_NAME
660
661static int
662co_obj_parse_values(co_obj_t *obj, const config_t *cfg)
663{
664 assert(obj);
665 assert(cfg);
666
667 co_unsigned8_t id = co_dev_get_id(co_obj_get_dev(obj));
668 co_unsigned16_t idx = co_obj_get_idx(obj);
669
670 // Create the section name for the explicit values of the sub-objects.
671 char section[10];
672 sprintf(section, "%XValue", (co_unsigned16_t)idx);
673 struct floc at = { section, 0, 0 };
674
675 const char *val = config_get(cfg, section, "NrOfEntries");
676 if (!val || !*val)
677 return 0;
678
679 co_unsigned8_t n = (co_unsigned8_t)strtoul(val, NULL, 0);
680 for (size_t subidx = 1; n && subidx < 0xff; subidx++) {
681 char key[4];
682 sprintf(key, "%u", (co_unsigned8_t)subidx);
683
684 val = config_get(cfg, section, key);
685 if (val && *val) {
686 n--;
688 obj, (co_unsigned8_t)subidx);
689 co_unsigned16_t type = co_sub_get_type(sub);
690 co_val_fini(type, sub->val);
691 if (!strncmp(val, "$NODEID", 7)) {
692 val += 7;
694 }
695 // clang-format off
697 type, sub->val, val, NULL, &at))) {
698 // clang-format on
700 "unable to set value of sub-object %Xsub%X",
701 (co_unsigned16_t)idx,
702 (co_unsigned8_t)subidx);
703 return -1;
704 }
706 co_val_set_id(type, sub->val, id);
707 }
708 }
709
710 return 0;
711}
712
713static co_obj_t *
714co_obj_build(co_dev_t *dev, co_unsigned16_t idx)
715{
716 assert(dev);
717
718 co_obj_t *obj = co_obj_create(idx);
719 if (__unlikely(!obj)) {
720 diag(DIAG_ERROR, get_errc(), "unable to create object 0x%04X",
721 idx);
722 return NULL;
723 }
724
725 if (__unlikely(co_dev_insert_obj(dev, obj) == -1)) {
726 diag(DIAG_ERROR, 0,
727 "unable to insert object 0x%04X into the object dictionary",
728 idx);
729 co_obj_destroy(obj);
730 return NULL;
731 }
732
733 return obj;
734}
735
736static int
737co_sub_parse_cfg(co_sub_t *sub, const config_t *cfg, const char *section)
738{
739 assert(sub);
740 assert(cfg);
741
742 const char *val;
743 struct floc at = { section, 0, 0 };
744
745 co_unsigned8_t id = co_dev_get_id(co_obj_get_dev(co_sub_get_obj(sub)));
746 co_unsigned16_t type = co_sub_get_type(sub);
747
748#ifndef LELY_NO_CO_OBJ_LIMITS
749 val = config_get(cfg, section, "LowLimit");
750 if (val && *val) {
751 if (!strncmp(val, "$NODEID", 7)) {
752 val += 7;
754 }
755 if (__unlikely(!co_val_lex(type, &sub->min, val, NULL, &at))) {
757 "unable to parse LowLimit");
758 return -1;
759 }
761 co_val_set_id(type, &sub->min, id);
762 }
763
764 val = config_get(cfg, section, "HighLimit");
765 if (val && *val) {
766 if (!strncmp(val, "$NODEID", 7)) {
767 val += 7;
769 }
770 if (__unlikely(!co_val_lex(type, &sub->max, val, NULL, &at))) {
772 "unable to parse HighLimit");
773 return -1;
774 }
776 co_val_set_id(type, &sub->max, id);
777 }
778#endif // LELY_NO_CO_OBJ_LIMITS
779
780 unsigned int access = co_sub_get_access(sub);
781 val = config_get(cfg, section, "AccessType");
782 if (val && *val) {
783 if (!strcasecmp(val, "ro")) {
784 access = CO_ACCESS_RO;
785 } else if (!strcasecmp(val, "wo")) {
786 access = CO_ACCESS_WO;
787 } else if (!strcasecmp(val, "rw")) {
788 access = CO_ACCESS_RW;
789 } else if (!strcasecmp(val, "rwr")) {
790 access = CO_ACCESS_RWR;
791 } else if (!strcasecmp(val, "rww")) {
792 access = CO_ACCESS_RWW;
793 } else if (!strcasecmp(val, "const")) {
794 access = CO_ACCESS_CONST;
795 } else {
796 diag_at(DIAG_ERROR, 0, &at, "AccessType = %s", val);
797 return -1;
798 }
799 co_sub_set_access(sub, access);
800 } else if (type != CO_DEFTYPE_DOMAIN) {
801 diag_at(DIAG_ERROR, 0, &at, "AccessType not specified");
802 return -1;
803 }
804
805 val = config_get(cfg, section, "DefaultValue");
806 if (val && *val) {
807 if (!strncmp(val, "$NODEID", 7)) {
808 val += 7;
810 }
811 if (__unlikely(!co_val_lex(type, &sub->def, val, NULL, &at))) {
813 "unable to parse DefaultValue");
814 return -1;
815 }
817 co_val_set_id(type, &sub->def, id);
818 }
819
820 val = config_get(cfg, section, "PDOMapping");
821 if (val && *val)
822 co_sub_set_pdo_mapping(sub, strtoul(val, NULL, 0));
823
824 val = config_get(cfg, section, "ObjFlags");
825 if (val && *val)
826 sub->flags |= strtoul(val, NULL, 0);
827
828 val = config_get(cfg, section, "ParameterValue");
829 if (val && *val) {
830 if (!strncmp(val, "$NODEID", 7)) {
831 val += 7;
833 }
834 if (__unlikely(!co_val_lex(type, sub->val, val, NULL, &at))) {
836 "unable to parse ParameterValue");
837 return -1;
838 }
840 co_val_set_id(type, sub->val, id);
841#ifndef LELY_NO_CO_OBJ_FILE
842 } else if (type == CO_DEFTYPE_DOMAIN
843 && (val = config_get(cfg, section, "UploadFile"))
844 != NULL) {
845 if (!(access & CO_ACCESS_READ) || (access & CO_ACCESS_WRITE)) {
846 diag_at(DIAG_WARNING, 0, &at,
847 "AccessType must be 'ro' or 'const' when using UploadFile");
848 access |= CO_ACCESS_READ;
849 access &= ~CO_ACCESS_WRITE;
850 co_sub_set_access(sub, access);
851 }
852
854 // Store the filename instead of its contents in the object
855 // dictionary.
856 // clang-format off
857 if (__unlikely(co_val_init_dom(sub->val, val, strlen(val) + 1)
858 == -1)) {
859 // clang-format on
861 "unable to parse UploadFile");
862 return -1;
863 }
864 } else if (type == CO_DEFTYPE_DOMAIN
865 && (val = config_get(cfg, section, "DownloadFile"))
866 != NULL) {
867 if ((access & CO_ACCESS_READ) || !(access & CO_ACCESS_WRITE)) {
868 diag_at(DIAG_WARNING, 0, &at,
869 "AccessType must be 'wo' when using DownloadFile");
870 access &= ~CO_ACCESS_READ;
871 access |= CO_ACCESS_WRITE;
872 co_sub_set_access(sub, access);
873 }
875 // Store the filename instead of its contents in the object
876 // dictionary.
877 // clang-format off
878 if (__unlikely(co_val_init_dom(sub->val, val, strlen(val) + 1)
879 == -1)) {
880 // clang-format on
882 "unable to parse DownloadFile");
883 return -1;
884 }
885#endif
886 } else {
889 co_val_copy(type, sub->val, &sub->def);
890 }
891
892#ifndef LELY_NO_CO_OBJ_LIMITS
893 if (co_type_is_basic(type)) {
894 const void *min = co_sub_addressof_min(sub);
895 const void *max = co_sub_addressof_max(sub);
896 const void *def = co_sub_addressof_def(sub);
897 const void *val = co_sub_addressof_val(sub);
898 if (co_val_cmp(type, min, max) > 0)
899 diag_at(DIAG_WARNING, 0, &at,
900 "LowLimit exceeds HighLimit");
901 if (co_val_cmp(type, def, min) < 0)
902 diag_at(DIAG_WARNING, 0, &at, "DefaultValue underflow");
903 if (co_val_cmp(type, def, max) > 0)
904 diag_at(DIAG_WARNING, 0, &at, "DefaultValue overflow");
905 if (co_val_cmp(type, val, def)
906 && co_val_cmp(type, val, min) < 0)
907 diag_at(DIAG_WARNING, 0, &at,
908 "ParameterValue underflow");
909 if (co_val_cmp(type, val, def)
910 && co_val_cmp(type, val, max) > 0)
911 diag_at(DIAG_WARNING, 0, &at,
912 "ParameterValue overflow");
913 }
914#endif
915
916 return 0;
917}
918
919static co_sub_t *
920co_sub_build(co_obj_t *obj, co_unsigned8_t subidx, co_unsigned16_t type,
921 const char *name)
922{
923 assert(obj);
924
925 co_unsigned16_t idx = co_obj_get_idx(obj);
926
927 co_sub_t *sub = co_sub_create(subidx, type);
928 if (__unlikely(!sub)) {
930 "unable to create sub-object %Xsub%X", idx,
931 subidx);
932 goto error;
933 }
934
935 if (__unlikely(co_obj_insert_sub(obj, sub) == -1)) {
936 diag(DIAG_ERROR, 0,
937 "unable to insert sub-object %Xsub%X into the object dictionary",
938 idx, subidx);
939 goto error;
940 }
941
942#ifndef LELY_NO_CO_OBJ_NAME
943 if (__unlikely(co_sub_set_name(sub, name) == -1)) {
945 "unable to set name of sub-object %Xsub%X", idx,
946 subidx);
947 goto error;
948 }
949#else
950 (void)name;
951#endif
952
953 return sub;
954
955error:
956 co_sub_destroy(sub);
957 return NULL;
958}
959
960static int
961co_rpdo_build(co_dev_t *dev, co_unsigned16_t num, int mask)
962{
963 assert(dev);
964 assert(num && num <= 512);
965
966 // Find the highest sub-index supported.
967 mask &= 0x3f;
968 co_unsigned8_t n = 0;
969 for (int i = 0; i < 6; i++) {
970 if (mask & (1 << i))
971 n = i + 1;
972 }
973
974 // Create the RPDO communication parameter if it does not exist.
975 if (!co_dev_find_obj(dev, 0x1400 + num - 1)) {
976 co_obj_t *obj = co_obj_build(dev, 0x1400 + num - 1);
977 if (__unlikely(!obj))
978 return -1;
979#ifndef LELY_NO_CO_OBJ_NAME
980 // clang-format off
982 "RPDO communication parameter") == -1)) {
983 // clang-format on
984 diag(DIAG_ERROR, get_errc(), "unable configure RPDO %u",
985 num);
986 return -1;
987 }
988#endif
990
991 co_sub_t *sub = co_sub_build(obj, 0, CO_DEFTYPE_UNSIGNED8,
992 "Highest sub-index supported");
993 if (__unlikely(!sub))
994 return -1;
995 co_val_make(sub->type, &sub->def, &n, sizeof(n));
996 co_val_copy(sub->type, sub->val, &sub->def);
998
999 if (mask & 0x01) {
1000 sub = co_sub_build(obj, 1, CO_DEFTYPE_UNSIGNED32,
1001 "COB-ID used by RPDO");
1002 if (__unlikely(!sub))
1003 return -1;
1004 co_unsigned32_t cobid = CO_PDO_COBID_VALID;
1005 if (num <= 4) {
1006 cobid = num * 0x100 + 0x100 + 0xff;
1009 }
1010 co_val_make(sub->type, &sub->def, &cobid,
1011 sizeof(cobid));
1012 co_val_copy(sub->type, sub->val, &sub->def);
1014 }
1015
1016 if (mask & 0x02) {
1017 sub = co_sub_build(obj, 2, CO_DEFTYPE_UNSIGNED8,
1018 "Transmission type");
1019 if (__unlikely(!sub))
1020 return -1;
1022 }
1023
1024 if (mask & 0x04) {
1025 sub = co_sub_build(obj, 3, CO_DEFTYPE_UNSIGNED16,
1026 "Inhibit time");
1027 if (__unlikely(!sub))
1028 return -1;
1030 }
1031
1032 if (mask & 0x08) {
1033 sub = co_sub_build(obj, 4, CO_DEFTYPE_UNSIGNED8,
1034 "Compatibility entry");
1035 if (__unlikely(!sub))
1036 return -1;
1038 }
1039
1040 if (mask & 0x10) {
1041 sub = co_sub_build(obj, 5, CO_DEFTYPE_UNSIGNED16,
1042 "Event timer");
1043 if (__unlikely(!sub))
1044 return -1;
1046 }
1047
1048 if (mask & 0x20) {
1049 sub = co_sub_build(obj, 6, CO_DEFTYPE_UNSIGNED8,
1050 "SYNC start value");
1051 if (__unlikely(!sub))
1052 return -1;
1054 }
1055 }
1056
1057 // Create the RPDO mapping parameter if it does not exist.
1058 if (!co_dev_find_obj(dev, 0x1600 + num - 1)) {
1059 co_obj_t *obj = co_obj_build(dev, 0x1600 + num - 1);
1060 if (__unlikely(!obj))
1061 return -1;
1062#ifndef LELY_NO_CO_OBJ_NAME
1063 // clang-format off
1064 if (__unlikely(co_obj_set_name(obj, "RPDO mapping parameter")
1065 == -1)) {
1066 // clang-format on
1067 diag(DIAG_ERROR, get_errc(), "unable configure RPDO %u",
1068 num);
1069 return -1;
1070 }
1071#endif
1073
1074 co_sub_t *sub = co_sub_build(obj, 0, CO_DEFTYPE_UNSIGNED8,
1075 "Highest sub-index supported");
1076 if (__unlikely(!sub))
1077 return -1;
1079
1080 for (co_unsigned8_t i = 1; i <= 0x40; i++) {
1081 char name[22];
1082 sprintf(name, "Application object %u", i);
1083
1084 co_sub_t *sub = co_sub_build(
1085 obj, i, CO_DEFTYPE_UNSIGNED32, name);
1086 if (__unlikely(!sub))
1087 return -1;
1089 }
1090 }
1091
1092 return 0;
1093}
1094
1095static int
1096co_tpdo_build(co_dev_t *dev, co_unsigned16_t num, int mask)
1097{
1098 assert(dev);
1099 assert(num && num <= 512);
1100
1101 // Find the highest sub-index supported.
1102 mask &= 0x3f;
1103 co_unsigned8_t n = 0;
1104 for (int i = 0; i < 6; i++) {
1105 if (mask & (1 << i))
1106 n = i + 1;
1107 }
1108
1109 // Create the TPDO communication parameter if it does not exist.
1110 if (!co_dev_find_obj(dev, 0x1800 + num - 1)) {
1111 co_obj_t *obj = co_obj_build(dev, 0x1800 + num - 1);
1112 if (__unlikely(!obj))
1113 return -1;
1114#ifndef LELY_NO_CO_OBJ_NAME
1115 // clang-format off
1117 "TPDO communication parameter") == -1)) {
1118 // clang-format on
1119 diag(DIAG_ERROR, get_errc(), "unable configure TPDO %u",
1120 num);
1121 return -1;
1122 }
1123#endif
1125
1126 co_sub_t *sub = co_sub_build(obj, 0, CO_DEFTYPE_UNSIGNED8,
1127 "Highest sub-index supported");
1128 if (__unlikely(!sub))
1129 return -1;
1130 co_val_make(sub->type, &sub->def, &n, sizeof(n));
1131 co_val_copy(sub->type, sub->val, &sub->def);
1133
1134 if (mask & 0x01) {
1135 sub = co_sub_build(obj, 1, CO_DEFTYPE_UNSIGNED32,
1136 "COB-ID used by TPDO");
1137 if (__unlikely(!sub))
1138 return -1;
1139 co_unsigned32_t cobid = CO_PDO_COBID_VALID;
1140 if (num <= 4) {
1141 cobid = num * 0x100 + 0x80 + 0xff;
1144 }
1145 co_val_make(sub->type, &sub->def, &cobid,
1146 sizeof(cobid));
1147 co_val_copy(sub->type, sub->val, &sub->def);
1149 }
1150
1151 if (mask & 0x02) {
1152 sub = co_sub_build(obj, 2, CO_DEFTYPE_UNSIGNED8,
1153 "Transmission type");
1154 if (__unlikely(!sub))
1155 return -1;
1157 }
1158
1159 if (mask & 0x04) {
1160 sub = co_sub_build(obj, 3, CO_DEFTYPE_UNSIGNED16,
1161 "Inhibit time");
1162 if (__unlikely(!sub))
1163 return -1;
1165 }
1166
1167 if (mask & 0x08) {
1168 sub = co_sub_build(obj, 4, CO_DEFTYPE_UNSIGNED8,
1169 "Reserved");
1170 if (__unlikely(!sub))
1171 return -1;
1173 }
1174
1175 if (mask & 0x10) {
1176 sub = co_sub_build(obj, 5, CO_DEFTYPE_UNSIGNED16,
1177 "Event timer");
1178 if (__unlikely(!sub))
1179 return -1;
1181 }
1182
1183 if (mask & 0x20) {
1184 sub = co_sub_build(obj, 6, CO_DEFTYPE_UNSIGNED8,
1185 "SYNC start value");
1186 if (__unlikely(!sub))
1187 return -1;
1189 }
1190 }
1191
1192 // Create the TPDO mapping parameter if it does not exist.
1193 if (!co_dev_find_obj(dev, 0x1a00 + num - 1)) {
1194 co_obj_t *obj = co_obj_build(dev, 0x1a00 + num - 1);
1195 if (__unlikely(!obj))
1196 return -1;
1197#ifndef LELY_NO_CO_OBJ_NAME
1198 // clang-format off
1199 if (__unlikely(co_obj_set_name(obj, "TPDO mapping parameter")
1200 == -1)) {
1201 // clang-format on
1202 diag(DIAG_ERROR, get_errc(), "unable configure TPDO %u",
1203 num);
1204 return -1;
1205 }
1206#endif
1208
1209 co_sub_t *sub = co_sub_build(obj, 0, CO_DEFTYPE_UNSIGNED8,
1210 "Highest sub-index supported");
1211 if (__unlikely(!sub))
1212 return -1;
1214
1215 for (co_unsigned8_t i = 1; i <= 0x40; i++) {
1216 char name[22];
1217 sprintf(name, "Application object %u", i);
1218
1219 co_sub_t *sub = co_sub_build(
1220 obj, i, CO_DEFTYPE_UNSIGNED32, name);
1221 if (__unlikely(!sub))
1222 return -1;
1224 }
1225 }
1226
1227 return 0;
1228}
1229
1230static void
1231co_val_set_id(co_unsigned16_t type, void *val, co_unsigned8_t id)
1232{
1233 assert(val);
1234
1235 union co_val *u = val;
1236 switch (type) {
1237#define LELY_CO_DEFINE_TYPE(a, b, c, d) \
1238 case CO_DEFTYPE_##a: \
1239 u->c += id; \
1240 break;
1241#include <lely/co/def/basic.def>
1242#undef LELY_CO_DEFINE_TYPE
1243 }
1244}
1245
1246static co_unsigned16_t
1247config_get_idx(const config_t *cfg, const char *section, co_unsigned16_t maxidx,
1248 co_unsigned16_t *idx)
1249{
1250 assert(cfg);
1251
1252 if (!idx)
1253 maxidx = 0;
1254
1255 const char *val = config_get(cfg, section, "SupportedObjects");
1256 if (__unlikely(!val || !*val))
1257 return 0;
1258
1259 co_unsigned16_t n = (co_unsigned16_t)strtoul(val, NULL, 0);
1260 for (size_t i = 0; i < (size_t)MIN(n, maxidx); i++) {
1261 char key[6];
1262 sprintf(key, "%u", (co_unsigned16_t)(i + 1));
1263
1264 val = config_get(cfg, section, key);
1265 // clang-format off
1266 idx[i] = val && *val
1267 ? (co_unsigned16_t)strtoul(val, NULL, 0) : 0;
1268 // clang-format on
1269 }
1270
1271 return n;
1272}
1273
1274#endif // !LELY_NO_CO_DCF
This header file is part of the utilities library; it contains the configuration functions.
size_t config_parse_ini_file(config_t *config, const char *filename)
Parses an INI file and adds the keys to a configuration struct.
Definition: config_ini.c:47
@ CONFIG_CASE
Section and key names are case-insensitive.
Definition: config.h:37
size_t config_parse_ini_text(config_t *config, const char *begin, const char *end, struct floc *at)
Parses a string in INI-format and adds the keys to a configuration struct.
Definition: config_ini.c:75
config_t * config_create(int flags)
Creates a new configuration struct with an unnamed empty root section.
Definition: config.c:110
const char * config_get(const config_t *config, const char *section, const char *key)
Retrieves a key from a configuration struct.
Definition: config.c:188
void config_destroy(config_t *config)
Destroys a configuration struct.
Definition: config.c:135
co_dev_t * co_dev_create_from_dcf_text(const char *begin, const char *end, struct floc *at)
Creates a CANopen device from an EDS or DCF text string.
Definition: dcf.c:149
co_dev_t * co_dev_create_from_dcf_file(const char *filename)
Creates a CANopen device from an EDS or DCF file.
Definition: dcf.c:96
This header file is part of the CANopen library; it contains the Electronic Data Sheet (EDS) and Devi...
co_obj_t * co_dev_find_obj(const co_dev_t *dev, co_unsigned16_t idx)
Finds an object in the object dictionary of a CANopen device.
Definition: dev.c:279
co_unsigned8_t co_dev_get_id(const co_dev_t *dev)
Returns the node-ID of a CANopen device.
Definition: dev.c:198
int co_dev_set_vendor_name(co_dev_t *dev, const char *vendor_name)
Sets the vendor name of a CANopen device.
Definition: dev.c:335
void co_dev_set_product_code(co_dev_t *dev, co_unsigned32_t product_code)
Sets the product code of a CANopen device.
Definition: dev.c:411
#define CO_BAUD_20
A bit rate of 20 kbit/s.
Definition: dev.h:77
void co_dev_set_dummy(co_dev_t *dev, co_unsigned32_t dummy)
Sets the data types supported by a CANopen device for mapping dummy entries in PDOs.
Definition: dev.c:521
int co_dev_insert_obj(co_dev_t *dev, co_obj_t *obj)
Inserts an object into the object dictionary of a CANopen device.
Definition: dev.c:243
void co_dev_set_vendor_id(co_dev_t *dev, co_unsigned32_t vendor_id)
Sets the vendor ID of a CANopen device.
Definition: dev.c:365
int co_dev_set_id(co_dev_t *dev, co_unsigned8_t id)
Sets the node-ID of a CANopen device.
Definition: dev.c:206
int co_dev_set_netid(co_dev_t *dev, co_unsigned8_t id)
Sets the network-ID of a CANopen device.
Definition: dev.c:183
void co_dev_set_revision(co_dev_t *dev, co_unsigned32_t revision)
Sets the revision number of a CANopen device.
Definition: dev.c:427
#define CO_BAUD_125
A bit rate of 125 kbit/s.
Definition: dev.h:71
#define CO_BAUD_10
A bit rate of 10 kbit/s.
Definition: dev.h:80
#define CO_BAUD_250
A bit rate of 250 kbit/s.
Definition: dev.h:68
void co_dev_set_lss(co_dev_t *dev, int lss)
Sets the LSS support flag.
Definition: dev.c:505
int co_dev_set_order_code(co_dev_t *dev, const char *order_code)
Sets the order code of a CANopen device.
Definition: dev.c:443
void co_dev_set_rate(co_dev_t *dev, co_unsigned16_t rate)
Sets the (pending) baudrate of a CANopen device.
Definition: dev.c:489
#define CO_BAUD_1000
A bit rate of 1 Mbit/s.
Definition: dev.h:59
#define CO_BAUD_500
A bit rate of 500 kbit/s.
Definition: dev.h:65
#define CO_BAUD_800
A bit rate of 800 kbit/s.
Definition: dev.h:62
int co_dev_set_product_name(co_dev_t *dev, const char *product_name)
Sets the product name of a CANopen device.
Definition: dev.c:381
void co_dev_set_baud(co_dev_t *dev, unsigned int baud)
Sets the supported bit rates of a CANopen device.
Definition: dev.c:473
#define CO_BAUD_50
A bit rate of 50 kbit/s.
Definition: dev.h:74
int co_dev_set_name(co_dev_t *dev, const char *name)
Sets the name of a CANopen device.
Definition: dev.c:305
This header file is part of the utilities library; it contains the diagnostic declarations.
@ DIAG_WARNING
A warning.
Definition: diag.h:47
@ DIAG_ERROR
An error.
Definition: diag.h:49
void diag_at(enum diag_severity severity, int errc, const struct floc *at, const char *format,...)
Emits a diagnostic message occurring at a location in a text file.
Definition: diag.c:172
void diag(enum diag_severity severity, int errc, const char *format,...)
Emits a diagnostic message.
Definition: diag.c:156
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:947
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:957
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:43
#define __unlikely(x)
Indicates to the compiler that the expression is most-likely false.
Definition: features.h:286
int co_sub_set_access(co_sub_t *sub, unsigned int access)
Sets the access type of a CANopen sub-object.
Definition: obj.c:690
int co_sub_set_name(co_sub_t *sub, const char *name)
Sets the name of a CANopen sub-object.
Definition: obj.c:484
#define CO_ACCESS_READ
The object can be read.
Definition: obj.h:57
void co_sub_destroy(co_sub_t *sub)
Destroys a CANopen sub-object.
Definition: obj.c:449
#define CO_ACCESS_WO
Write-only access.
Definition: obj.h:72
#define CO_ACCESS_RO
Read-only access.
Definition: obj.h:69
#define CO_OBJ_FLAGS_MAX_NODEID
The upper limit of the object value is of the form $NODEID { "+" number }.
Definition: obj.h:108
co_unsigned16_t co_obj_get_idx(const co_obj_t *obj)
Returns the index of a CANopen object.
Definition: obj.c:136
#define CO_OBJECT_DEFSTRUCT
A record type definition.
Definition: obj.h:39
int co_obj_set_code(co_obj_t *obj, co_unsigned8_t code)
Sets the code (type) of a CANopen object.
Definition: obj.c:258
co_sub_t * co_obj_find_sub(const co_obj_t *obj, co_unsigned8_t subidx)
Finds a sub-object in a CANopen object.
Definition: obj.c:207
const void * co_sub_addressof_def(const co_sub_t *sub)
Returns the address of the default value of a CANopen sub-object.
Definition: obj.c:574
const void * co_sub_addressof_max(const co_sub_t *sub)
Returns the address of the upper limit of the value of a CANopen sub-object.
Definition: obj.c:545
#define CO_OBJ_FLAGS_DEF_NODEID
The default object value is of the form $NODEID { "+" number }.
Definition: obj.h:111
co_unsigned8_t co_obj_get_code(const co_obj_t *obj)
Returns the object code of a CANopen object.
Definition: obj.c:250
const void * co_sub_addressof_val(const co_sub_t *sub)
Returns the address of the current value of a CANopen sub-object.
Definition: obj.c:601
#define CO_OBJECT_ARRAY
A multiple data field object where each data field is a simple variable of the same basic data type.
Definition: obj.h:48
#define CO_OBJECT_RECORD
A multiple data field object where the data fields may be any combination of simple variables.
Definition: obj.h:54
int co_obj_set_name(co_obj_t *obj, const char *name)
Sets the name of a CANopen object.
Definition: obj.c:226
#define CO_OBJ_FLAGS_MIN_NODEID
The lower limit of the object value is of the form $NODEID { "+" number }.
Definition: obj.h:105
#define CO_OBJ_FLAGS_VAL_NODEID
The current object value is of the form $NODEID { "+" number }.
Definition: obj.h:114
void co_sub_set_pdo_mapping(co_sub_t *sub, int pdo_mapping)
Enables or disables PDO mapping a CANopen sub-object.
Definition: obj.c:714
#define CO_OBJECT_DOMAIN
A large variable amount of data.
Definition: obj.h:33
void co_obj_destroy(co_obj_t *obj)
Destroys a CANopen object, including its sub-objects.
Definition: obj.c:118
#define CO_ACCESS_RW
Read or write access.
Definition: obj.h:75
#define CO_ACCESS_RWR
Read or write on process input.
Definition: obj.h:78
#define CO_OBJ_FLAGS_DOWNLOAD_FILE
If a write access is performed for the object, the data is stored in a file.
Definition: obj.h:102
co_dev_t * co_obj_get_dev(const co_obj_t *obj)
Returns a pointer to the CANopen device containing the specified object.
Definition: obj.c:128
int co_obj_insert_sub(co_obj_t *obj, co_sub_t *sub)
Inserts a sub-object into a CANopen object.
Definition: obj.c:164
co_obj_t * co_obj_create(co_unsigned16_t idx)
Creates a CANopen object.
Definition: obj.c:106
co_obj_t * co_sub_get_obj(const co_sub_t *sub)
Returns the a pointer to the CANopen object containing the specified sub-object.
Definition: obj.c:458
#define CO_OBJ_FLAGS_UPLOAD_FILE
If a read access is performed for the object, the data is stored in a file.
Definition: obj.h:96
#define CO_ACCESS_WRITE
The object can be written.
Definition: obj.h:60
unsigned int co_sub_get_access(const co_sub_t *sub)
Returns the access type of a CANopen sub-object.
Definition: obj.c:682
const void * co_sub_addressof_min(const co_sub_t *sub)
Returns the address of the lower limit of the value of a CANopen sub-object.
Definition: obj.c:518
#define CO_ACCESS_RWW
Read or write on process output.
Definition: obj.h:81
co_sub_t * co_sub_create(co_unsigned8_t subidx, co_unsigned16_t type)
Creates a CANopen sub-object.
Definition: obj.c:424
co_unsigned16_t co_sub_get_type(const co_sub_t *sub)
Returns the data type of a CANopen sub-object.
Definition: obj.c:508
#define CO_ACCESS_CONST
Constant value.
Definition: obj.h:84
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
This header file is part of the utilities library; it contains the lexer function declarations.
This header file is part of the CANopen library; it contains the Process Data Object (PDO) declaratio...
#define CO_PDO_COBID_VALID
The bit in the PDO COB-ID specifying whether the PDO exists and is valid.
Definition: pdo.h:28
This is the internal header file of the CANopen library.
This is the internal header file of the object dictionary.
This header file is part of the C11 and POSIX compatibility library; it includes <stdio....
int asprintf(char **strp, const char *fmt,...)
Equivalent to sprintf(), except that it allocates a string large enough to hold the output,...
Definition: stdio.c:103
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
This header file is part of the C11 and POSIX compatibility library; it includes <string....
This header file is part of the C11 and POSIX compatibility library; it includes <strings....
int strcasecmp(const char *s1, const char *s2)
Compares the string at s1 to the string at s2, ignoring differences in case.
Definition: strings.c:71
A CANopen device.
Definition: dev.c:38
unsigned baud
The supported bit rates.
Definition: dev.c:60
co_unsigned32_t dummy
The data types supported for mapping dummy entries in PDOs.
Definition: dev.c:66
char * name
A pointer to the name of the device.
Definition: dev.c:46
A CANopen object.
Definition: obj.h:32
A CANopen sub-object.
Definition: obj.h:54
unsigned flags
The object flags.
Definition: obj.h:82
union co_val def
The default value.
Definition: obj.h:74
union co_val min
The lower limit of the object value.
Definition: obj.h:69
union co_val max
The upper limit of the object value.
Definition: obj.h:71
co_unsigned16_t type
The data type.
Definition: obj.h:62
void * val
A pointer to the sub-object value.
Definition: obj.h:76
A configuration struct.
Definition: config.c:35
A location in a text file.
Definition: diag.h:31
#define CO_DEFTYPE_UNSIGNED16
The data type (and object index) of a 16-bit unsigned integer.
Definition: type.h:47
#define CO_DEFTYPE_DOMAIN
The data type (and object index) of an arbitrary large block of data.
Definition: type.h:77
#define CO_DEFTYPE_UNSIGNED8
The data type (and object index) of an 8-bit unsigned integer.
Definition: type.h:44
#define CO_DEFTYPE_UNSIGNED32
The data type (and object index) of a 32-bit unsigned integer.
Definition: type.h:50
int co_type_is_basic(co_unsigned16_t type)
Returns 1 if the specified (static) data type is a basic type, and 0 if not.
Definition: type.c:28
A union of the CANopen static data types.
Definition: val.h:163
int co_val_init_dom(void **val, const void *dom, size_t n)
Initializes an arbitrary large block of data (CO_DEFTYPE_DOMAIN).
Definition: val.c:254
size_t co_val_copy(co_unsigned16_t type, void *dst, const void *src)
Copies one value to another.
Definition: val.c:333
int co_val_cmp(co_unsigned16_t type, const void *v1, const void *v2)
Compares two values of the specified data type.
Definition: val.c:386
size_t co_val_make(co_unsigned16_t type, void *val, const void *ptr, size_t n)
Constructs a value of the specified data type.
Definition: val.c:306
void co_val_fini(co_unsigned16_t type, void *val)
Finalizes a value of the specified data type.
Definition: val.c:273
size_t co_val_lex(co_unsigned16_t type, void *val, const char *begin, const char *end, struct floc *at)
Lexes a value of the specified data type from a memory buffer.
Definition: val.c:869