Lely core libraries  2.2.5
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/stdio.h>
33 #include <lely/libc/strings.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 
42 static struct __co_dev *__co_dev_init_from_dcf_cfg(
43  struct __co_dev *dev, const config_t *cfg);
44 
45 static int co_dev_parse_cfg(co_dev_t *dev, const config_t *cfg);
46 
47 static int co_obj_parse_cfg(
48  co_obj_t *obj, const config_t *cfg, const char *section);
49 #ifndef LELY_NO_CO_OBJ_NAME
50 static int co_obj_parse_names(co_obj_t *obj, const config_t *cfg);
51 #endif
52 static int co_obj_parse_values(co_obj_t *obj, const config_t *cfg);
53 static co_obj_t *co_obj_build(co_dev_t *dev, co_unsigned16_t idx);
54 
55 static int co_sub_parse_cfg(
56  co_sub_t *sub, const config_t *cfg, const char *section);
57 static co_sub_t *co_sub_build(co_obj_t *obj, co_unsigned8_t subidx,
58  co_unsigned16_t type, const char *name);
59 
60 static int co_rpdo_build(co_dev_t *dev, co_unsigned16_t num, int mask);
61 static int co_tpdo_build(co_dev_t *dev, co_unsigned16_t num, int mask);
62 
63 size_t co_val_lex_dcf(co_unsigned16_t type, void *val, const char *begin,
64  const char *end, struct floc *at);
65 static size_t co_val_lex_id(
66  const char *begin, const char *end, struct floc *at);
67 static void co_val_set_id(co_unsigned16_t type, void *val, co_unsigned8_t id);
68 
69 static co_unsigned16_t config_get_idx(const config_t *cfg, const char *section,
70  co_unsigned16_t maxidx, co_unsigned16_t *idx);
71 
72 struct __co_dev *
73 __co_dev_init_from_dcf_file(struct __co_dev *dev, const char *filename)
74 {
76  if (!cfg) {
78  "unable to create configuration struct");
79  goto error_create_cfg;
80  }
81 
82  if (!config_parse_ini_file(cfg, filename))
83  goto error_parse_ini_file;
84 
85  if (!__co_dev_init_from_dcf_cfg(dev, cfg))
86  goto error_init_dev;
87 
88  config_destroy(cfg);
89 
90  return dev;
91 
92 error_init_dev:
93 error_parse_ini_file:
94  config_destroy(cfg);
95 error_create_cfg:
96  return NULL;
97 }
98 
99 co_dev_t *
100 co_dev_create_from_dcf_file(const char *filename)
101 {
102  int errc = 0;
103 
104  co_dev_t *dev = __co_dev_alloc();
105  if (!dev) {
106  errc = get_errc();
107  goto error_alloc_dev;
108  }
109 
110  if (!__co_dev_init_from_dcf_file(dev, filename)) {
111  errc = get_errc();
112  goto error_init_dev;
113  }
114 
115  return dev;
116 
117 error_init_dev:
118  __co_dev_free(dev);
119 error_alloc_dev:
120  set_errc(errc);
121  return NULL;
122 }
123 
124 struct __co_dev *
125 __co_dev_init_from_dcf_text(struct __co_dev *dev, const char *begin,
126  const char *end, struct floc *at)
127 {
129  if (!cfg) {
131  "unable to create configuration struct");
132  goto error_create_cfg;
133  }
134 
135  if (!config_parse_ini_text(cfg, begin, end, at))
136  goto error_parse_ini_text;
137 
138  if (!__co_dev_init_from_dcf_cfg(dev, cfg))
139  goto error_init_dev;
140 
141  config_destroy(cfg);
142 
143  return dev;
144 
145 error_init_dev:
146 error_parse_ini_text:
147  config_destroy(cfg);
148 error_create_cfg:
149  return NULL;
150 }
151 
152 co_dev_t *
153 co_dev_create_from_dcf_text(const char *begin, const char *end, struct floc *at)
154 {
155  int errc = 0;
156 
157  co_dev_t *dev = __co_dev_alloc();
158  if (!dev) {
159  errc = get_errc();
160  goto error_alloc_dev;
161  }
162 
163  if (!__co_dev_init_from_dcf_text(dev, begin, end, at)) {
164  errc = get_errc();
165  goto error_init_dev;
166  }
167 
168  return dev;
169 
170 error_init_dev:
171  __co_dev_free(dev);
172 error_alloc_dev:
173  set_errc(errc);
174  return NULL;
175 }
176 
177 static struct __co_dev *
178 __co_dev_init_from_dcf_cfg(struct __co_dev *dev, const config_t *cfg)
179 {
180  assert(dev);
181  assert(cfg);
182 
183  if (!__co_dev_init(dev, 0xff)) {
185  "unable to initialize device description");
186  goto error_init_dev;
187  }
188 
189  if (co_dev_parse_cfg(dev, cfg) == -1)
190  goto error_parse_cfg;
191 
192  return dev;
193 
194 error_parse_cfg:
195  __co_dev_fini(dev);
196 error_init_dev:
197  return NULL;
198 }
199 
200 static int
201 co_dev_parse_cfg(co_dev_t *dev, const config_t *cfg)
202 {
203  assert(dev);
204  assert(cfg);
205 
206  const char *val;
207 
208  // clang-format off
209  if (co_dev_set_vendor_name(dev,
210  config_get(cfg, "DeviceInfo", "VendorName")) == -1) {
211  // clang-format on
212  diag(DIAG_ERROR, get_errc(), "unable to set vendor name");
213  goto error_parse_dev;
214  }
215 
216  val = config_get(cfg, "DeviceInfo", "VendorNumber");
217  if (val && *val)
218  co_dev_set_vendor_id(dev, strtoul(val, NULL, 0));
219 
220  // clang-format off
221  if (co_dev_set_product_name(dev,
222  config_get(cfg, "DeviceInfo", "ProductName")) == -1) {
223  // clang-format on
224  diag(DIAG_ERROR, get_errc(), "unable to set product name");
225  goto error_parse_dev;
226  }
227 
228  val = config_get(cfg, "DeviceInfo", "ProductNumber");
229  if (val && *val)
230  co_dev_set_product_code(dev, strtoul(val, NULL, 0));
231 
232  val = config_get(cfg, "DeviceInfo", "RevisionNumber");
233  if (val && *val)
234  co_dev_set_revision(dev, strtoul(val, NULL, 0));
235 
236  // clang-format off
237  if (co_dev_set_order_code(dev,
238  config_get(cfg, "DeviceInfo", "OrderCode")) == -1) {
239  diag(DIAG_ERROR, get_errc(), "unable to set order code");
240  goto error_parse_dev;
241  // clang-format on
242  }
243 
244  unsigned int baud = 0;
245  val = config_get(cfg, "DeviceInfo", "BaudRate_10");
246  if (val && *val && strtoul(val, NULL, 0))
247  baud |= CO_BAUD_10;
248  val = config_get(cfg, "DeviceInfo", "BaudRate_20");
249  if (val && *val && strtoul(val, NULL, 0))
250  baud |= CO_BAUD_20;
251  val = config_get(cfg, "DeviceInfo", "BaudRate_50");
252  if (val && *val && strtoul(val, NULL, 0))
253  baud |= CO_BAUD_50;
254  val = config_get(cfg, "DeviceInfo", "BaudRate_125");
255  if (val && *val && strtoul(val, NULL, 0))
256  baud |= CO_BAUD_125;
257  val = config_get(cfg, "DeviceInfo", "BaudRate_250");
258  if (val && *val && strtoul(val, NULL, 0))
259  baud |= CO_BAUD_250;
260  val = config_get(cfg, "DeviceInfo", "BaudRate_500");
261  if (val && *val && strtoul(val, NULL, 0))
262  baud |= CO_BAUD_500;
263  val = config_get(cfg, "DeviceInfo", "BaudRate_800");
264  if (val && *val && strtoul(val, NULL, 0))
265  baud |= CO_BAUD_800;
266  val = config_get(cfg, "DeviceInfo", "BaudRate_1000");
267  if (val && *val && strtoul(val, NULL, 0))
268  baud |= CO_BAUD_1000;
269  co_dev_set_baud(dev, baud);
270 
271  val = config_get(cfg, "DeviceInfo", "LSS_Supported");
272  if (val && *val)
273  co_dev_set_lss(dev, strtoul(val, NULL, 0));
274 
275  // For each of the basic data types, check whether it is supported for
276  // mapping dummy entries in PDOs.
277  co_unsigned32_t dummy = 0;
278  for (int i = 0; i < 0x20; i++) {
279  // Create the key name.
280  char key[10];
281  snprintf(key, sizeof(key), "Dummy%04X", (co_unsigned16_t)i);
282 
283  val = config_get(cfg, "DummyUsage", key);
284  if (val && *val && strtoul(val, NULL, 0))
285  dummy |= 1u << i;
286  }
287  co_dev_set_dummy(dev, dummy);
288 
289  // Count the total number of objects.
290  co_unsigned16_t n = 0;
291  n += config_get_idx(cfg, "MandatoryObjects", 0, NULL);
292  n += config_get_idx(cfg, "OptionalObjects", 0, NULL);
293  n += config_get_idx(cfg, "ManufacturerObjects", 0, NULL);
294 
295  // Parse the object indices.
296  co_unsigned16_t *idx = malloc(n * sizeof(co_unsigned16_t));
297  if (!idx) {
298  diag(DIAG_ERROR, errno2c(errno),
299  "unable to create object list");
300  goto error_parse_idx;
301  }
302  co_unsigned16_t i = 0;
303  i += config_get_idx(cfg, "MandatoryObjects", n - i, idx + i);
304  i += config_get_idx(cfg, "OptionalObjects", n - i, idx + i);
305  config_get_idx(cfg, "ManufacturerObjects", n - i, idx + i);
306 
307  for (i = 0; i < n; i++) {
308  if (!idx[i]) {
309  diag(DIAG_ERROR, 0, "entry (%d) missing in object list",
310  i);
311  goto error_parse_obj;
312  }
313 
314  // Create the section name for the object.
315  char section[5];
316  snprintf(section, sizeof(section), "%X", idx[i]);
317 
318  // Create the object and add it to the dictionary.
319  co_obj_t *obj = co_obj_build(dev, idx[i]);
320  if (!obj)
321  goto error_parse_obj;
322 
323  // Parse the configuration section for the object.
324  if (co_obj_parse_cfg(obj, cfg, section) == -1)
325  goto error_parse_obj;
326  }
327 
328  if (!co_dev_find_obj(dev, 0x1000))
329  diag(DIAG_WARNING, 0, "mandatory object 0x1000 missing");
330  if (!co_dev_find_obj(dev, 0x1001))
331  diag(DIAG_WARNING, 0, "mandatory object 0x1001 missing");
332  if (!co_dev_find_obj(dev, 0x1018))
333  diag(DIAG_WARNING, 0, "mandatory object 0x1018 missing");
334 
335  // Parse compact PDO definitions after the explicit object definitions
336  // to prevent overwriting PDOs.
337  val = config_get(cfg, "DeviceInfo", "CompactPDO");
338  unsigned int mask = val && *val ? strtoul(val, NULL, 0) : 0;
339  if (mask) {
340  co_unsigned16_t nrpdo = 0;
341  val = config_get(cfg, "DeviceInfo", "NrOfRxPDO");
342  if (val && *val)
343  nrpdo = (co_unsigned16_t)strtoul(val, NULL, 0);
344  // Count the number of implicit RPDOs.
345  for (co_unsigned16_t i = 0; i < 512 && nrpdo; i++) {
346  if (co_dev_find_obj(dev, 0x1400 + i)
347  || co_dev_find_obj(dev, 0x1600 + i))
348  nrpdo--;
349  }
350  for (co_unsigned16_t i = 0; i < 512; i++) {
351  if (!co_dev_find_obj(dev, 0x1400 + i)
352  && !co_dev_find_obj(dev, 0x1600 + i)) {
353  if (!nrpdo)
354  continue;
355  nrpdo--;
356  }
357  // Add missing communication and/or mapping objects.
358  if (co_rpdo_build(dev, i + 1, mask) == -1)
359  goto error_parse_pdo;
360  }
361 
362  co_unsigned16_t ntpdo = 0;
363  val = config_get(cfg, "DeviceInfo", "NrOfTxPDO");
364  if (val && *val)
365  ntpdo = (co_unsigned16_t)strtoul(val, NULL, 0);
366  // Count the number of implicit TPDOs.
367  for (co_unsigned16_t i = 0; i < 512 && ntpdo; i++) {
368  if (co_dev_find_obj(dev, 0x1800 + i)
369  || co_dev_find_obj(dev, 0x1a00 + i))
370  ntpdo--;
371  }
372  for (co_unsigned16_t i = 0; i < 512; i++) {
373  if (!co_dev_find_obj(dev, 0x1800 + i)
374  && !co_dev_find_obj(dev, 0x1a00 + i)) {
375  if (!ntpdo)
376  continue;
377  ntpdo--;
378  }
379  // Add missing communication and/or mapping objects.
380  if (co_tpdo_build(dev, i + 1, mask) == -1)
381  goto error_parse_pdo;
382  }
383  }
384 
385  val = config_get(cfg, "DeviceComissioning", "NodeID");
386  // clang-format off
387  if (val && *val && co_dev_set_id(dev,
388  (co_unsigned8_t)strtoul(val, NULL, 0)) == -1) {
389  // clang-format on
390  diag(DIAG_ERROR, get_errc(), "invalid node-ID (%s) specified",
391  val);
392  goto error_parse_dcf;
393  }
394 
395  val = config_get(cfg, "DeviceComissioning", "NetNumber");
396  // clang-format off
397  if (val && *val && co_dev_set_netid(dev,
398  (co_unsigned32_t)strtoul(val, NULL, 0)) == -1) {
399  // clang-format on
401  "invalid network-ID (%s) specified", val);
402  goto error_parse_dcf;
403  }
404 
405  // clang-format off
406  if (co_dev_set_name(dev,
407  config_get(cfg, "DeviceComissioning", "NodeName"))
408  == -1) {
409  // clang-format on
410  diag(DIAG_ERROR, get_errc(), "unable to set node name");
411  goto error_parse_dcf;
412  }
413 
414  val = config_get(cfg, "DeviceComissioning", "Baudrate");
415  if (val && *val)
416  co_dev_set_rate(dev, (co_unsigned16_t)strtoul(val, NULL, 0));
417 
418  val = config_get(cfg, "DeviceComissioning", "LSS_SerialNumber");
419  // clang-format off
420  if (val && *val && !co_dev_set_val_u32(dev, 0x1018, 0x04,
421  strtoul(val, NULL, 0))) {
422  // clang-format on
423  diag(DIAG_ERROR, get_errc(), "unable to set serial number");
424  goto error_parse_dcf;
425  }
426 
427  free(idx);
428 
429  return 0;
430 
431 error_parse_pdo:
432 error_parse_dcf:
433 error_parse_obj:
434  free(idx);
435 error_parse_idx:
436 error_parse_dev:
437  return -1;
438 }
439 
440 static int
441 co_obj_parse_cfg(co_obj_t *obj, const config_t *cfg, const char *section)
442 {
443  assert(obj);
444  assert(cfg);
445 
446  const char *val;
447  struct floc at = { section, 0, 0 };
448 
449  co_unsigned16_t idx = co_obj_get_idx(obj);
450 
451  const char *name = config_get(cfg, section, "ParameterName");
452  if (!name) {
453  diag(DIAG_ERROR, 0,
454  "ParameterName not specified for object 0x%04X",
455  idx);
456  return -1;
457  }
458 #ifndef LELY_NO_CO_OBJ_NAME
459  val = config_get(cfg, section, "Denotation");
460  if (val && *val)
461  name = val;
462  if (co_obj_set_name(obj, name) == -1) {
464  "unable to set name of object 0x%04X", idx);
465  return -1;
466  }
467 #endif
468 
469  co_unsigned8_t code = co_obj_get_code(obj);
470  val = config_get(cfg, section, "ObjectType");
471  if (val && *val) {
472  code = (co_unsigned8_t)strtoul(val, NULL, 0);
473  if (co_obj_set_code(obj, code) == -1) {
474  diag(DIAG_ERROR, 0,
475  "ObjectType = 0x%x for object 0x%04X",
476  code, idx);
477  return -1;
478  }
479  }
480 
481  if (code == CO_OBJECT_DEFSTRUCT || code == CO_OBJECT_ARRAY
482  || code == CO_OBJECT_RECORD) {
483  co_unsigned8_t subnum = 0;
484  val = config_get(cfg, section, "SubNumber");
485  if (val && *val)
486  subnum = (co_unsigned8_t)strtoul(val, NULL, 0);
487  co_unsigned8_t subobj = 0;
488  val = config_get(cfg, section, "CompactSubObj");
489  if (val && *val)
490  subobj = (co_unsigned8_t)strtoul(val, NULL, 0);
491  if (!subnum && !subobj) {
492  diag(DIAG_ERROR, 0,
493  "neither SubNumber nor CompactSubObj specified for object 0x%04X",
494  idx);
495  return -1;
496  }
497  if (subnum && subobj) {
498  diag(DIAG_ERROR, 0,
499  "both SubNumber and CompactSubObj specified for object 0x%04X",
500  idx);
501  return -1;
502  }
503 
504  // Parse the sub-objects specified by SubNumber.
505  for (size_t subidx = 0; subnum && subidx < 0xff; subidx++) {
506  // Create section name for the sub-object.
507  char section[10];
508  snprintf(section, sizeof(section), "%Xsub%X",
509  (co_unsigned16_t)idx,
510  (co_unsigned8_t)subidx);
511 
512  // Check whether the sub-index exists by checking the
513  // presence of the mandatory ParameterName keyword.
514  const char *name = config_get(
515  cfg, section, "ParameterName");
516  if (!name)
517  continue;
518  subnum--;
519 
520  // The Denonation entry, if it exists, overrides
521  // ParameterName.
522  val = config_get(cfg, section, "Denotation");
523  if (val && *val)
524  name = val;
525 
526  // Obtain the data type of the sub-object.
527  val = config_get(cfg, section, "DataType");
528  if (!val || !*val) {
529  diag_at(DIAG_ERROR, 0, &at,
530  "DataType not specified");
531  return -1;
532  }
533  co_unsigned16_t type =
534  (co_unsigned16_t)strtoul(val, NULL, 0);
535 
536  // Create and insert the sub-object.
537  co_sub_t *sub = co_sub_build(obj,
538  (co_unsigned8_t)subidx, type, name);
539  if (!sub)
540  return -1;
541 
542  // Parse the configuration section for the sub-object.
543  if (co_sub_parse_cfg(sub, cfg, section) == -1)
544  return -1;
545  }
546 
547  // Create an array based on CompactSubObj.
548  if (subobj) {
549  co_sub_t *sub = co_sub_build(obj, 0,
550  CO_DEFTYPE_UNSIGNED8, "NrOfObjects");
551  if (!sub)
552  return -1;
553  co_val_make(sub->type, sub->val, &subobj,
554  sizeof(subobj));
555 #ifndef LELY_NO_CO_OBJ_DEFAULT
556  co_val_copy(sub->type, &sub->def, sub->val);
557 #endif
559 
560  name = config_get(cfg, section, "ParameterName");
561 
562  // Obtain the data type of the sub-object.
563  val = config_get(cfg, section, "DataType");
564  if (!val || !*val) {
565  diag_at(DIAG_ERROR, 0, &at,
566  "DataType not specified");
567  return -1;
568  }
569  co_unsigned16_t type =
570  (co_unsigned16_t)strtoul(val, NULL, 0);
571 
572  // Create the sub-objects.
573  for (size_t subidx = 1; subidx <= subobj; subidx++) {
574  // Create name of the sub-object.
575  char *subname = NULL;
576  // clang-format off
577  if (asprintf(&subname, "%s%u", name,
578  (co_unsigned8_t)subidx) < 0)
579  // clang-format on
580  return -1;
581 
582  // Create and insert the sub-object.
583  sub = co_sub_build(obj, (co_unsigned8_t)subidx,
584  type, subname);
585  free(subname);
586  if (!sub)
587  return -1;
588 
589  // Parse the configuration section for the
590  // sub-object.
591  if (co_sub_parse_cfg(sub, cfg, section) == -1)
592  return -1;
593  }
594 
595 #ifndef LELY_NO_CO_OBJ_NAME
596  // Parse the names of the sub-objects.
597  if (co_obj_parse_names(obj, cfg) == -1)
598  return -1;
599 #endif
600 
601  // Parse the values of the sub-objects.
602  if (co_obj_parse_values(obj, cfg) == -1)
603  return -1;
604  }
605 
606  co_sub_t *sub = co_obj_find_sub(obj, 0x00);
607  if (!sub || co_sub_get_type(sub) != CO_DEFTYPE_UNSIGNED8) {
608  diag(DIAG_ERROR, 0,
609  "object 0x%04X does not provide the highest sub-index implemented",
610  idx);
611  return -1;
612  }
613  } else {
614  // Obtain the data type of the object (optional for DOMAIN
615  // objects).
616  co_unsigned16_t type = code == CO_OBJECT_DOMAIN
618  : 0;
619  val = config_get(cfg, section, "DataType");
620  if (val && *val)
621  type = (co_unsigned16_t)strtoul(val, NULL, 0);
622  if (!type) {
623  diag_at(DIAG_ERROR, 0, &at, "DataType not specified");
624  return -1;
625  }
626 
627  // Create and insert the sub-object.
628  co_sub_t *sub = co_sub_build(obj, 0, type, name);
629  if (!sub)
630  return -1;
631 
632  // Parse the configuration section for the sub-object.
633  if (co_sub_parse_cfg(sub, cfg, section) == -1)
634  return -1;
635  }
636 
637  return 0;
638 }
639 
640 #ifndef LELY_NO_CO_OBJ_NAME
641 static int
642 co_obj_parse_names(co_obj_t *obj, const config_t *cfg)
643 {
644  assert(obj);
645  assert(cfg);
646 
647  co_unsigned16_t idx = co_obj_get_idx(obj);
648 
649  // Create the section name for the explicit names of the sub-objects.
650  char section[9];
651  snprintf(section, sizeof(section), "%XName", idx);
652 
653  const char *val = config_get(cfg, section, "NrOfEntries");
654  if (!val || !*val)
655  return 0;
656 
657  co_unsigned8_t n = (co_unsigned8_t)strtoul(val, NULL, 0);
658  for (size_t subidx = 1; n && subidx < 0xff; subidx++) {
659  char key[4];
660  snprintf(key, sizeof(key), "%u", (co_unsigned8_t)subidx);
661 
662  val = config_get(cfg, section, key);
663  if (val && *val) {
664  n--;
665  co_sub_t *sub = co_obj_find_sub(
666  obj, (co_unsigned8_t)subidx);
667  if (sub && co_sub_set_name(sub, val) == -1) {
669  "unable to set name of sub-object %Xsub%X",
670  (co_unsigned16_t)idx,
671  (co_unsigned8_t)subidx);
672  return -1;
673  }
674  }
675  }
676 
677  return 0;
678 }
679 #endif // LELY_NO_CO_OBJ_NAME
680 
681 static int
682 co_obj_parse_values(co_obj_t *obj, const config_t *cfg)
683 {
684  assert(obj);
685  assert(cfg);
686 
687  co_unsigned8_t id = co_dev_get_id(co_obj_get_dev(obj));
688  co_unsigned16_t idx = co_obj_get_idx(obj);
689 
690  // Create the section name for the explicit values of the sub-objects.
691  char section[10];
692  snprintf(section, sizeof(section), "%XValue", (co_unsigned16_t)idx);
693  struct floc at = { section, 0, 0 };
694 
695  const char *val = config_get(cfg, section, "NrOfEntries");
696  if (!val || !*val)
697  return 0;
698 
699  co_unsigned8_t n = (co_unsigned8_t)strtoul(val, NULL, 0);
700  for (size_t subidx = 1; n && subidx < 0xff; subidx++) {
701  char key[4];
702  snprintf(key, sizeof(key), "%u", (co_unsigned8_t)subidx);
703 
704  val = config_get(cfg, section, key);
705  if (val && *val) {
706  n--;
707  co_sub_t *sub = co_obj_find_sub(
708  obj, (co_unsigned8_t)subidx);
709  co_unsigned16_t type = co_sub_get_type(sub);
710  co_val_fini(type, sub->val);
711  size_t chars = co_val_lex_id(val, NULL, &at);
712  if (chars) {
713  val += chars;
715  }
716  if (!co_val_lex_dcf(type, sub->val, val, NULL, &at)) {
718  "unable to set value of sub-object %Xsub%X",
719  (co_unsigned16_t)idx,
720  (co_unsigned8_t)subidx);
721  return -1;
722  }
723  if (sub->flags & CO_OBJ_FLAGS_VAL_NODEID)
724  co_val_set_id(type, sub->val, id);
725  }
726  }
727 
728  return 0;
729 }
730 
731 static co_obj_t *
732 co_obj_build(co_dev_t *dev, co_unsigned16_t idx)
733 {
734  assert(dev);
735 
736  co_obj_t *obj = co_obj_create(idx);
737  if (!obj) {
738  diag(DIAG_ERROR, get_errc(), "unable to create object 0x%04X",
739  idx);
740  return NULL;
741  }
742 
743  if (co_dev_insert_obj(dev, obj) == -1) {
744  diag(DIAG_ERROR, 0,
745  "unable to insert object 0x%04X into the object dictionary",
746  idx);
747  co_obj_destroy(obj);
748  return NULL;
749  }
750 
751  return obj;
752 }
753 
754 static int
755 co_sub_parse_cfg(co_sub_t *sub, const config_t *cfg, const char *section)
756 {
757  assert(sub);
758  assert(cfg);
759 
760  int result = -1;
761 
762  const char *val;
763  struct floc at = { section, 0, 0 };
764 
765  co_unsigned8_t id = co_dev_get_id(co_obj_get_dev(co_sub_get_obj(sub)));
766  co_unsigned16_t type = co_sub_get_type(sub);
767 
768 #ifdef LELY_NO_CO_OBJ_DEFAULT
769  union co_val def;
770  co_val_init(type, &def);
771 #endif
772 
773 #ifndef LELY_NO_CO_OBJ_LIMITS
774  val = config_get(cfg, section, "LowLimit");
775  if (val && *val) {
776  size_t chars = co_val_lex_id(val, NULL, &at);
777  if (chars) {
778  val += chars;
780  }
781  if (!co_val_lex_dcf(type, &sub->min, val, NULL, &at)) {
782  diag_at(DIAG_ERROR, get_errc(), &at,
783  "unable to parse LowLimit");
784  goto error;
785  }
786  if (sub->flags & CO_OBJ_FLAGS_MIN_NODEID)
787  co_val_set_id(type, &sub->min, id);
788  }
789 
790  val = config_get(cfg, section, "HighLimit");
791  if (val && *val) {
792  size_t chars = co_val_lex_id(val, NULL, &at);
793  if (chars) {
794  val += chars;
796  }
797  if (!co_val_lex_dcf(type, &sub->max, val, NULL, &at)) {
798  diag_at(DIAG_ERROR, get_errc(), &at,
799  "unable to parse HighLimit");
800  goto error;
801  }
802  if (sub->flags & CO_OBJ_FLAGS_MAX_NODEID)
803  co_val_set_id(type, &sub->max, id);
804  }
805 #endif // LELY_NO_CO_OBJ_LIMITS
806 
807  unsigned int access = co_sub_get_access(sub);
808  val = config_get(cfg, section, "AccessType");
809  if (val && *val) {
810  if (!strcasecmp(val, "ro")) {
811  access = CO_ACCESS_RO;
812  } else if (!strcasecmp(val, "wo")) {
813  access = CO_ACCESS_WO;
814  } else if (!strcasecmp(val, "rw")) {
815  access = CO_ACCESS_RW;
816  } else if (!strcasecmp(val, "rwr")) {
817  access = CO_ACCESS_RWR;
818  } else if (!strcasecmp(val, "rww")) {
819  access = CO_ACCESS_RWW;
820  } else if (!strcasecmp(val, "const")) {
821  access = CO_ACCESS_CONST;
822  } else {
823  diag_at(DIAG_ERROR, 0, &at, "AccessType = %s", val);
824  goto error;
825  }
826  co_sub_set_access(sub, access);
827  } else if (type != CO_DEFTYPE_DOMAIN) {
828  diag_at(DIAG_ERROR, 0, &at, "AccessType not specified");
829  goto error;
830  }
831 
832  val = config_get(cfg, section, "DefaultValue");
833  if (val && *val) {
834  size_t chars = co_val_lex_id(val, NULL, &at);
835  if (chars) {
836  val += chars;
838  }
839 #ifdef LELY_NO_CO_OBJ_DEFAULT
840  if (!co_val_lex_dcf(type, &def, val, NULL, &at)) {
841 #else
842  if (!co_val_lex_dcf(type, &sub->def, val, NULL, &at)) {
843 #endif
844  diag_at(DIAG_ERROR, get_errc(), &at,
845  "unable to parse DefaultValue");
846  goto error;
847  }
848  if (sub->flags & CO_OBJ_FLAGS_DEF_NODEID)
849 #ifdef LELY_NO_CO_OBJ_DEFAULT
850  co_val_set_id(type, &def, id);
851 #else
852  co_val_set_id(type, &sub->def, id);
853 #endif
854  }
855 
856  val = config_get(cfg, section, "PDOMapping");
857  if (val && *val)
858  co_sub_set_pdo_mapping(sub, strtoul(val, NULL, 0));
859 
860  val = config_get(cfg, section, "ObjFlags");
861  if (val && *val)
862  sub->flags |= strtoul(val, NULL, 0);
863 
864  val = config_get(cfg, section, "ParameterValue");
865  if (val && *val) {
866  size_t chars = co_val_lex_id(val, NULL, &at);
867  if (chars) {
868  val += chars;
870  }
871  if (!co_val_lex_dcf(type, sub->val, val, NULL, &at)) {
872  diag_at(DIAG_ERROR, get_errc(), &at,
873  "unable to parse ParameterValue");
874  goto error;
875  }
876  if (sub->flags & CO_OBJ_FLAGS_VAL_NODEID)
877  co_val_set_id(type, sub->val, id);
878 #ifndef LELY_NO_CO_OBJ_FILE
879  } else if (type == CO_DEFTYPE_DOMAIN
880  && (val = config_get(cfg, section, "UploadFile"))
881  != NULL) {
882  if (!(access & CO_ACCESS_READ) || (access & CO_ACCESS_WRITE)) {
883  diag_at(DIAG_WARNING, 0, &at,
884  "AccessType must be 'ro' or 'const' when using UploadFile");
885  access |= CO_ACCESS_READ;
886  access &= ~CO_ACCESS_WRITE;
887  co_sub_set_access(sub, access);
888  }
889 
891  // Store the filename instead of its contents in the object
892  // dictionary.
893  if (co_val_init_dom(sub->val, val, strlen(val) + 1) == -1) {
894  diag_at(DIAG_ERROR, get_errc(), &at,
895  "unable to parse UploadFile");
896  goto error;
897  }
898  } else if (type == CO_DEFTYPE_DOMAIN
899  && (val = config_get(cfg, section, "DownloadFile"))
900  != NULL) {
901  if ((access & CO_ACCESS_READ) || !(access & CO_ACCESS_WRITE)) {
902  diag_at(DIAG_WARNING, 0, &at,
903  "AccessType must be 'wo' when using DownloadFile");
904  access &= ~CO_ACCESS_READ;
905  access |= CO_ACCESS_WRITE;
906  co_sub_set_access(sub, access);
907  }
909  // Store the filename instead of its contents in the object
910  // dictionary.
911  if (co_val_init_dom(sub->val, val, strlen(val) + 1) == -1) {
912  diag_at(DIAG_ERROR, get_errc(), &at,
913  "unable to parse DownloadFile");
914  goto error;
915  }
916 #endif
917  } else {
918  if (sub->flags & CO_OBJ_FLAGS_DEF_NODEID)
920 #ifdef LELY_NO_CO_OBJ_DEFAULT
921  co_val_copy(type, sub->val, &def);
922 #else
923  co_val_copy(type, sub->val, &sub->def);
924 #endif
925  }
926 
927 #ifndef LELY_NO_CO_OBJ_LIMITS
928  if (co_type_is_basic(type)) {
929  const void *min = co_sub_addressof_min(sub);
930  const void *max = co_sub_addressof_max(sub);
931 #ifndef LELY_NO_CO_OBJ_DEFAULT
932  const void *def = co_sub_addressof_def(sub);
933 #endif
934  const void *val = co_sub_addressof_val(sub);
935  if (co_val_cmp(type, min, max) > 0)
936  diag_at(DIAG_WARNING, 0, &at,
937  "LowLimit exceeds HighLimit");
938 #ifndef LELY_NO_CO_OBJ_DEFAULT
939  if (co_val_cmp(type, def, min) < 0)
940  diag_at(DIAG_WARNING, 0, &at, "DefaultValue underflow");
941  if (co_val_cmp(type, def, max) > 0)
942  diag_at(DIAG_WARNING, 0, &at, "DefaultValue overflow");
943 #endif
944  if (co_val_cmp(type, val, min) < 0)
945  diag_at(DIAG_WARNING, 0, &at,
946  "ParameterValue underflow");
947  if (co_val_cmp(type, val, max) > 0)
948  diag_at(DIAG_WARNING, 0, &at,
949  "ParameterValue overflow");
950  }
951 #endif
952 
953  result = 0;
954 
955 error:
956 #ifdef LELY_NO_CO_OBJ_DEFAULT
957  co_val_fini(type, &def);
958 #endif
959  return result;
960 }
961 
962 static co_sub_t *
963 co_sub_build(co_obj_t *obj, co_unsigned8_t subidx, co_unsigned16_t type,
964  const char *name)
965 {
966  assert(obj);
967 
968  co_unsigned16_t idx = co_obj_get_idx(obj);
969 
970  co_sub_t *sub = co_sub_create(subidx, type);
971  if (!sub) {
973  "unable to create sub-object %Xsub%X", idx,
974  subidx);
975  goto error;
976  }
977 
978  if (co_obj_insert_sub(obj, sub) == -1) {
979  diag(DIAG_ERROR, 0,
980  "unable to insert sub-object %Xsub%X into the object dictionary",
981  idx, subidx);
982  goto error;
983  }
984 
985 #ifndef LELY_NO_CO_OBJ_NAME
986  if (co_sub_set_name(sub, name) == -1) {
988  "unable to set name of sub-object %Xsub%X", idx,
989  subidx);
990  goto error;
991  }
992 #else
993  (void)name;
994 #endif
995 
996  return sub;
997 
998 error:
999  co_sub_destroy(sub);
1000  return NULL;
1001 }
1002 
1003 static int
1004 co_rpdo_build(co_dev_t *dev, co_unsigned16_t num, int mask)
1005 {
1006  assert(dev);
1007  assert(num && num <= 512);
1008 
1009  // Find the highest sub-index supported.
1010  mask &= 0x3f;
1011  co_unsigned8_t n = 0;
1012  for (int i = 0; i < 6; i++) {
1013  if (mask & (1 << i))
1014  n = i + 1;
1015  }
1016 
1017  // Create the RPDO communication parameter if it does not exist.
1018  if (!co_dev_find_obj(dev, 0x1400 + num - 1)) {
1019  co_obj_t *obj = co_obj_build(dev, 0x1400 + num - 1);
1020  if (!obj)
1021  return -1;
1022 #ifndef LELY_NO_CO_OBJ_NAME
1023  if (co_obj_set_name(obj, "RPDO communication parameter")
1024  == -1) {
1025  diag(DIAG_ERROR, get_errc(), "unable configure RPDO %u",
1026  num);
1027  return -1;
1028  }
1029 #endif
1031 
1032  co_sub_t *sub = co_sub_build(obj, 0, CO_DEFTYPE_UNSIGNED8,
1033  "Highest sub-index supported");
1034  if (!sub)
1035  return -1;
1036  co_val_make(sub->type, sub->val, &n, sizeof(n));
1037 #ifndef LELY_NO_CO_OBJ_DEFAULT
1038  co_val_copy(sub->type, &sub->def, sub->val);
1039 #endif
1041 
1042  if (mask & 0x01) {
1043  sub = co_sub_build(obj, 1, CO_DEFTYPE_UNSIGNED32,
1044  "COB-ID used by RPDO");
1045  if (!sub)
1046  return -1;
1047  co_unsigned32_t cobid = CO_PDO_COBID_VALID;
1048  if (num <= 4) {
1049  cobid = num * 0x100 + 0x100 + 0xff;
1052  }
1053  co_val_make(sub->type, sub->val, &cobid, sizeof(cobid));
1054 #ifndef LELY_NO_CO_OBJ_DEFAULT
1055  co_val_copy(sub->type, &sub->def, sub->val);
1056 #endif
1058  }
1059 
1060  if (mask & 0x02) {
1061  sub = co_sub_build(obj, 2, CO_DEFTYPE_UNSIGNED8,
1062  "Transmission type");
1063  if (!sub)
1064  return -1;
1066  }
1067 
1068  if (mask & 0x04) {
1069  sub = co_sub_build(obj, 3, CO_DEFTYPE_UNSIGNED16,
1070  "Inhibit time");
1071  if (!sub)
1072  return -1;
1074  }
1075 
1076  if (mask & 0x08) {
1077  sub = co_sub_build(obj, 4, CO_DEFTYPE_UNSIGNED8,
1078  "Compatibility entry");
1079  if (!sub)
1080  return -1;
1082  }
1083 
1084  if (mask & 0x10) {
1085  sub = co_sub_build(obj, 5, CO_DEFTYPE_UNSIGNED16,
1086  "Event timer");
1087  if (!sub)
1088  return -1;
1090  }
1091 
1092  if (mask & 0x20) {
1093  sub = co_sub_build(obj, 6, CO_DEFTYPE_UNSIGNED8,
1094  "SYNC start value");
1095  if (!sub)
1096  return -1;
1098  }
1099  }
1100 
1101  // Create the RPDO mapping parameter if it does not exist.
1102  if (!co_dev_find_obj(dev, 0x1600 + num - 1)) {
1103  co_obj_t *obj = co_obj_build(dev, 0x1600 + num - 1);
1104  if (!obj)
1105  return -1;
1106 #ifndef LELY_NO_CO_OBJ_NAME
1107  if (co_obj_set_name(obj, "RPDO mapping parameter") == -1) {
1108  diag(DIAG_ERROR, get_errc(), "unable configure RPDO %u",
1109  num);
1110  return -1;
1111  }
1112 #endif
1114 
1115  co_sub_t *sub = co_sub_build(obj, 0, CO_DEFTYPE_UNSIGNED8,
1116  "Highest sub-index supported");
1117  if (!sub)
1118  return -1;
1120 
1121  for (co_unsigned8_t i = 1; i <= 0x40; i++) {
1122  char name[22];
1123  snprintf(name, sizeof(name), "Application object %u",
1124  i);
1125 
1126  co_sub_t *sub = co_sub_build(
1127  obj, i, CO_DEFTYPE_UNSIGNED32, name);
1128  if (!sub)
1129  return -1;
1131  }
1132  }
1133 
1134  return 0;
1135 }
1136 
1137 static int
1138 co_tpdo_build(co_dev_t *dev, co_unsigned16_t num, int mask)
1139 {
1140  assert(dev);
1141  assert(num && num <= 512);
1142 
1143  // Find the highest sub-index supported.
1144  mask &= 0x3f;
1145  co_unsigned8_t n = 0;
1146  for (int i = 0; i < 6; i++) {
1147  if (mask & (1 << i))
1148  n = i + 1;
1149  }
1150 
1151  // Create the TPDO communication parameter if it does not exist.
1152  if (!co_dev_find_obj(dev, 0x1800 + num - 1)) {
1153  co_obj_t *obj = co_obj_build(dev, 0x1800 + num - 1);
1154  if (!obj)
1155  return -1;
1156 #ifndef LELY_NO_CO_OBJ_NAME
1157  if (co_obj_set_name(obj, "TPDO communication parameter")
1158  == -1) {
1159  diag(DIAG_ERROR, get_errc(), "unable configure TPDO %u",
1160  num);
1161  return -1;
1162  }
1163 #endif
1165 
1166  co_sub_t *sub = co_sub_build(obj, 0, CO_DEFTYPE_UNSIGNED8,
1167  "Highest sub-index supported");
1168  if (!sub)
1169  return -1;
1170  co_val_make(sub->type, sub->val, &n, sizeof(n));
1171 #ifndef LELY_NO_CO_OBJ_DEFAULT
1172  co_val_copy(sub->type, &sub->def, sub->val);
1173 #endif
1175 
1176  if (mask & 0x01) {
1177  sub = co_sub_build(obj, 1, CO_DEFTYPE_UNSIGNED32,
1178  "COB-ID used by TPDO");
1179  if (!sub)
1180  return -1;
1181  co_unsigned32_t cobid = CO_PDO_COBID_VALID;
1182  if (num <= 4) {
1183  cobid = num * 0x100 + 0x80 + 0xff;
1186  }
1187  co_val_make(sub->type, sub->val, &cobid, sizeof(cobid));
1188 #ifndef LELY_NO_CO_OBJ_DEFAULT
1189  co_val_copy(sub->type, &sub->def, sub->val);
1190 #endif
1192  }
1193 
1194  if (mask & 0x02) {
1195  sub = co_sub_build(obj, 2, CO_DEFTYPE_UNSIGNED8,
1196  "Transmission type");
1197  if (!sub)
1198  return -1;
1200  }
1201 
1202  if (mask & 0x04) {
1203  sub = co_sub_build(obj, 3, CO_DEFTYPE_UNSIGNED16,
1204  "Inhibit time");
1205  if (!sub)
1206  return -1;
1208  }
1209 
1210  if (mask & 0x08) {
1211  sub = co_sub_build(obj, 4, CO_DEFTYPE_UNSIGNED8,
1212  "Reserved");
1213  if (!sub)
1214  return -1;
1216  }
1217 
1218  if (mask & 0x10) {
1219  sub = co_sub_build(obj, 5, CO_DEFTYPE_UNSIGNED16,
1220  "Event timer");
1221  if (!sub)
1222  return -1;
1224  }
1225 
1226  if (mask & 0x20) {
1227  sub = co_sub_build(obj, 6, CO_DEFTYPE_UNSIGNED8,
1228  "SYNC start value");
1229  if (!sub)
1230  return -1;
1232  }
1233  }
1234 
1235  // Create the TPDO mapping parameter if it does not exist.
1236  if (!co_dev_find_obj(dev, 0x1a00 + num - 1)) {
1237  co_obj_t *obj = co_obj_build(dev, 0x1a00 + num - 1);
1238  if (!obj)
1239  return -1;
1240 #ifndef LELY_NO_CO_OBJ_NAME
1241  if (co_obj_set_name(obj, "TPDO mapping parameter") == -1) {
1242  diag(DIAG_ERROR, get_errc(), "unable configure TPDO %u",
1243  num);
1244  return -1;
1245  }
1246 #endif
1248 
1249  co_sub_t *sub = co_sub_build(obj, 0, CO_DEFTYPE_UNSIGNED8,
1250  "Highest sub-index supported");
1251  if (!sub)
1252  return -1;
1254 
1255  for (co_unsigned8_t i = 1; i <= 0x40; i++) {
1256  char name[22];
1257  snprintf(name, sizeof(name), "Application object %u",
1258  i);
1259 
1260  co_sub_t *sub = co_sub_build(
1261  obj, i, CO_DEFTYPE_UNSIGNED32, name);
1262  if (!sub)
1263  return -1;
1265  }
1266  }
1267 
1268  return 0;
1269 }
1270 
1271 size_t
1272 co_val_lex_dcf(co_unsigned16_t type, void *val, const char *begin,
1273  const char *end, struct floc *at)
1274 {
1275  assert(begin);
1276  assert(!end || end >= begin);
1277 
1278  switch (type) {
1279  case CO_DEFTYPE_OCTET_STRING: {
1280  size_t n = 0;
1281  size_t chars = lex_hex(begin, end, NULL, NULL, &n);
1282  if (val) {
1283  if (co_val_init_os(val, NULL, n) == -1) {
1284  diag_if(DIAG_ERROR, get_errc(), at,
1285  "unable to create value of type OCTET_STRING");
1286  return 0;
1287  }
1288  uint_least8_t *os = *(void **)val;
1289  assert(!n || os);
1290  lex_hex(begin, end, NULL, os, &n);
1291  }
1292  return floc_lex(at, begin, begin + chars);
1293  }
1295  const char *cp;
1296  // Count the number of 16-bit code units.
1297  size_t n = 0;
1298  for (cp = begin; (!end || cp < end) && *cp;) {
1299  char32_t c32 = 0;
1300  cp += lex_utf8(cp, end, NULL, &c32);
1301  assert(c32 < 0xd800 || c32 > 0xdfff);
1302  n += c32 <= 0xffff ? 1 : 2;
1303  }
1304  if (val) {
1305  if (co_val_init_us_n(val, NULL, n) == -1) {
1306  diag_if(DIAG_ERROR, get_errc(), at,
1307  "unable to create value of type UNICODE_STRING");
1308  return 0;
1309  }
1310  // Parse the UTF-8 characters.
1311  char16_t *us = *(void **)val;
1312  assert(us);
1313  for (cp = begin; (!end || cp < end) && *cp;) {
1314  char32_t c32 = 0;
1315  cp += lex_utf8(cp, end, NULL, &c32);
1316  assert(c32 < 0xd800 || c32 > 0xdfff);
1317  // Store the character as UTF-16LE.
1318  if (c32 <= 0xffff) {
1319  *us++ = c32;
1320  } else {
1321  c32 -= 0x10000ul;
1322  *us++ = 0xd800 + ((c32 >> 10) & 0x3ff);
1323  *us++ = 0xdc00 + (c32 & 0x3ff);
1324  }
1325  }
1326  }
1327  return floc_lex(at, begin, cp);
1328  }
1329  case CO_DEFTYPE_DOMAIN: {
1330  size_t n = 0;
1331  size_t chars = lex_hex(begin, end, NULL, NULL, &n);
1332  if (val) {
1333  if (co_val_init_dom(val, NULL, n) == -1) {
1334  diag_if(DIAG_ERROR, get_errc(), at,
1335  "unable to create value of type DOMAIN");
1336  return 0;
1337  }
1338  void *dom = *(void **)val;
1339  assert(!n || dom);
1340  lex_hex(begin, end, NULL, dom, &n);
1341  }
1342  return floc_lex(at, begin, begin + chars);
1343  }
1344  default: return co_val_lex(type, val, begin, end, at);
1345  }
1346 }
1347 
1348 static size_t
1349 co_val_lex_id(const char *begin, const char *end, struct floc *at)
1350 {
1351  assert(begin);
1352 
1353  const char *cp = begin;
1354 
1355  if ((end && cp - end < 7) || strncasecmp(begin, "$NODEID", 7))
1356  return 0;
1357  cp += 7;
1358  if (at)
1359  at->column += 7;
1360 
1361  cp += lex_ctype(isspace, cp, end, at);
1362  cp += lex_char('+', cp, end, at);
1363  cp += lex_ctype(isspace, cp, end, at);
1364 
1365  return cp - begin;
1366 }
1367 
1368 static void
1369 co_val_set_id(co_unsigned16_t type, void *val, co_unsigned8_t id)
1370 {
1371  assert(val);
1372 
1373  union co_val *u = val;
1374  switch (type) {
1375 #define LELY_CO_DEFINE_TYPE(a, b, c, d) \
1376  case CO_DEFTYPE_##a: \
1377  u->c += id; \
1378  break;
1379 #include <lely/co/def/basic.def>
1380 #undef LELY_CO_DEFINE_TYPE
1381  }
1382 }
1383 
1384 static co_unsigned16_t
1385 config_get_idx(const config_t *cfg, const char *section, co_unsigned16_t maxidx,
1386  co_unsigned16_t *idx)
1387 {
1388  assert(cfg);
1389 
1390  if (!idx)
1391  maxidx = 0;
1392 
1393  const char *val = config_get(cfg, section, "SupportedObjects");
1394  if (!val || !*val)
1395  return 0;
1396 
1397  co_unsigned16_t n = (co_unsigned16_t)strtoul(val, NULL, 0);
1398  for (size_t i = 0; i < (size_t)MIN(n, maxidx); i++) {
1399  char key[6];
1400  snprintf(key, sizeof(key), "%u", (co_unsigned16_t)(i + 1));
1401 
1402  val = config_get(cfg, section, key);
1403  // clang-format off
1404  idx[i] = val && *val
1405  ? (co_unsigned16_t)strtoul(val, NULL, 0) : 0;
1406  // clang-format on
1407  }
1408 
1409  return n;
1410 }
1411 
1412 #endif // !LELY_NO_CO_DCF
co_unsigned8_t co_dev_get_id(const co_dev_t *dev)
Returns the node-ID of a CANopen device.
Definition: dev.c:207
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:362
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:438
#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:548
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:252
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:392
int co_dev_set_id(co_dev_t *dev, co_unsigned8_t id)
Sets the node-ID of a CANopen device.
Definition: dev.c:215
int co_dev_set_netid(co_dev_t *dev, co_unsigned8_t id)
Sets the network-ID of a CANopen device.
Definition: dev.c:192
void co_dev_set_revision(co_dev_t *dev, co_unsigned32_t revision)
Sets the revision number of a CANopen device.
Definition: dev.c:454
#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:532
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:470
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:288
void co_dev_set_rate(co_dev_t *dev, co_unsigned16_t rate)
Sets the (pending) baudrate of a CANopen device.
Definition: dev.c:516
#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:408
void co_dev_set_baud(co_dev_t *dev, unsigned int baud)
Sets the supported bit rates of a CANopen device.
Definition: dev.c:500
#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:332
This header file is part of the utilities library; it contains the configuration functions.
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
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
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_CASE
Section and key names are case-insensitive.
Definition: config.h:37
config_t * config_create(int flags)
Creates a new configuration struct with an unnamed empty root section.
Definition: config.c:110
void config_destroy(config_t *config)
Destroys a configuration struct.
Definition: config.c:135
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:100
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:153
This header file is part of the CANopen library; it contains the Electronic Data Sheet (EDS) and Devi...
This header file is part of the utilities library; it contains the diagnostic declarations.
size_t floc_lex(struct floc *at, const char *begin, const char *end)
Increments a file location by reading characters from a memory buffer.
Definition: diag.c:54
@ DIAG_WARNING
A warning.
Definition: diag.h:47
@ DIAG_ERROR
An error.
Definition: diag.h:49
void diag_if(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:190
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
int co_sub_set_access(co_sub_t *sub, unsigned int access)
Sets the access type of a CANopen sub-object.
Definition: obj.c:753
int co_sub_set_name(co_sub_t *sub, const char *name)
Sets the name of a CANopen sub-object.
Definition: obj.c:546
#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:493
#define CO_ACCESS_WO
Write-only access.
Definition: obj.h:72
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:146
#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
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:607
co_sub_t * co_sub_create(co_unsigned8_t subidx, co_unsigned16_t type)
Creates a CANopen sub-object.
Definition: obj.c:468
co_unsigned16_t co_obj_get_idx(const co_obj_t *obj)
Returns the index of a CANopen object.
Definition: obj.c:154
#define CO_OBJECT_DEFSTRUCT
A record type definition.
Definition: obj.h:39
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:638
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:580
int co_obj_set_code(co_obj_t *obj, co_unsigned8_t code)
Sets the code (type) of a CANopen object.
Definition: obj.c:294
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:667
#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:286
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:225
#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:262
#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:777
#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
int co_obj_insert_sub(co_obj_t *obj, co_sub_t *sub)
Inserts a sub-object into a CANopen object.
Definition: obj.c:182
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:520
co_obj_t * co_obj_create(co_unsigned16_t idx)
Creates a CANopen object.
Definition: obj.c:106
#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:745
#define CO_ACCESS_RWW
Read or write on process output.
Definition: obj.h:81
co_unsigned16_t co_sub_get_type(const co_sub_t *sub)
Returns the data type of a CANopen sub-object.
Definition: obj.c:570
#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.
size_t lex_char(int c, const char *begin, const char *end, struct floc *at)
Lexes the specified character from a memory buffer.
Definition: lex.c:38
size_t lex_ctype(int(*ctype)(int), const char *begin, const char *end, struct floc *at)
Greedily lexes a sequence of characters of the specified class from a memory buffer.
Definition: lex.c:51
size_t lex_utf8(const char *begin, const char *end, struct floc *at, char32_t *pc32)
Lexes a UTF-8 encoded Unicode character from a memory buffer.
Definition: lex.c:83
size_t lex_hex(const char *begin, const char *end, struct floc *at, void *ptr, size_t *pn)
Lexes and decodes the hexadecimal representation of binary data from a memory buffer.
Definition: lex.c:652
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....
A CANopen device.
Definition: dev.c:41
unsigned baud
The supported bit rates.
Definition: dev.c:63
co_unsigned32_t dummy
The data types supported for mapping dummy entries in PDOs.
Definition: dev.c:69
char * name
A pointer to the name of the device.
Definition: dev.c:49
A CANopen object.
Definition: obj.h:32
A CANopen sub-object.
Definition: obj.h:54
unsigned long flags
The object flags.
Definition: obj.h:84
union co_val def
The default value.
Definition: obj.h:75
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:78
A configuration struct.
Definition: config.c:35
A location in a text file.
Definition: diag.h:31
int column
The column number (starting from 1).
Definition: diag.h:37
#define CO_DEFTYPE_UNICODE_STRING
The data type (and object index) of an array of (16-bit) Unicode characters.
Definition: type.h:62
#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
#define CO_DEFTYPE_OCTET_STRING
The data type (and object index) of an array of octets.
Definition: type.h:59
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(co_unsigned16_t type, void *val)
Initializes a value of the specified data type to zero.
Definition: val.c:122
int co_val_init_os(uint_least8_t **val, const uint_least8_t *os, size_t n)
Initializes an array of octets (CO_DEFTYPE_OCTET_STRING).
Definition: val.c:205
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:256
size_t co_val_copy(co_unsigned16_t type, void *dst, const void *src)
Copies one value to another.
Definition: val.c:344
int co_val_init_us_n(char16_t **val, const char16_t *us, size_t n)
Initializes an array of (16-bit) Unicode characters (CO_DEFTYPE_UNICODE_STRING).
Definition: val.c:237
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:397
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:308
void co_val_fini(co_unsigned16_t type, void *val)
Finalizes a value of the specified data type.
Definition: val.c:275
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:871