Lely core libraries  2.3.4
device.cpp
Go to the documentation of this file.
1 
24 #include "coapp.hpp"
25 #include <lely/co/csdo.h>
26 #if !LELY_NO_CO_DCF
27 #include <lely/co/dcf.h>
28 #endif
29 #include <lely/co/dev.h>
30 #include <lely/co/obj.h>
31 #include <lely/co/pdo.h>
32 #include <lely/co/val.h>
33 #include <lely/coapp/device.hpp>
34 #if !LELY_NO_CO_RPDO && !LELY_NO_CO_MPDO
35 #include <lely/util/bits.h>
36 #endif
37 #include <lely/util/error.hpp>
38 
39 #if !LELY_NO_CO_RPDO && !LELY_NO_CO_MPDO
40 #include <algorithm>
41 #endif
42 #if !LELY_NO_CO_RPDO || !LELY_NO_CO_TPDO
43 #include <map>
44 #endif
45 #include <memory>
46 #include <string>
47 #include <tuple>
48 #include <utility>
49 #include <vector>
50 
51 namespace lely {
52 
53 namespace canopen {
54 
57  struct DeviceDeleter {
58  void
59  operator()(co_dev_t* dev) const noexcept {
60  co_dev_destroy(dev);
61  }
62  };
63 
64 #if !LELY_NO_CO_DCF
65  Impl_(Device* self, const ::std::string& dcf_txt,
66  const ::std::string& dcf_bin, uint8_t id, util::BasicLockable* mutex);
67 #endif
68  virtual ~Impl_() = default;
69 
70  void
71  lock() override {
72  if (mutex) mutex->lock();
73  }
74 
75  void
76  unlock() override {
77  if (mutex) mutex->unlock();
78  }
79 
80  uint8_t
81  netid() const noexcept {
82  return co_dev_get_netid(dev.get());
83  }
84 
85  uint8_t
86  id() const noexcept {
87  return co_dev_get_id(dev.get());
88  }
89 
90  void OnWrite(uint16_t idx, uint8_t subidx);
91 
92  ::std::tuple<uint16_t, uint8_t>
93  RpdoMapping(uint8_t id, uint16_t idx, uint8_t subidx,
94  ::std::error_code& ec) const noexcept {
95  (void)id;
96 #if !LELY_NO_CO_RPDO
97  auto it = rpdo_mapping.find((static_cast<uint32_t>(id) << 24) |
98  (static_cast<uint32_t>(idx) << 8) | subidx);
99  if (it != rpdo_mapping.end()) {
100  idx = (it->second >> 8) & 0xffff;
101  subidx = it->second & 0xff;
102  ec.clear();
103  } else {
104 #endif
105  idx = 0;
106  subidx = 0;
107  ec = SdoErrc::NO_PDO;
108 #if !LELY_NO_CO_RPDO
109  }
110 #endif
111  return ::std::make_tuple(idx, subidx);
112  }
113 
114  ::std::tuple<uint8_t, uint16_t, uint8_t>
115  RpdoMapping(uint16_t idx, uint8_t subidx,
116  ::std::error_code& ec) const noexcept {
117  uint8_t id = 0;
118 #if !LELY_NO_CO_RPDO
119  auto it = rpdo_mapping.find((static_cast<uint32_t>(idx) << 8) | subidx);
120  if (it != rpdo_mapping.end()) {
121  id = (it->second >> 24) & 0xff;
122  idx = (it->second >> 8) & 0xffff;
123  subidx = it->second & 0xff;
124  ec.clear();
125  } else {
126 #endif
127  idx = 0;
128  subidx = 0;
129  ec = SdoErrc::NO_PDO;
130 #if !LELY_NO_CO_RPDO
131  }
132 #endif
133  return ::std::make_tuple(id, idx, subidx);
134  }
135 
136  ::std::tuple<uint16_t, uint8_t>
137  TpdoMapping(uint8_t id, uint16_t idx, uint8_t subidx,
138  ::std::error_code& ec) const noexcept {
139  (void)id;
140 #if !LELY_NO_CO_TPDO
141  auto it = tpdo_mapping.find((static_cast<uint32_t>(id) << 24) |
142  (static_cast<uint32_t>(idx) << 8) | subidx);
143  if (it != tpdo_mapping.end()) {
144  idx = (it->second >> 8) & 0xffff;
145  subidx = it->second & 0xff;
146  ec.clear();
147  } else {
148 #endif
149  idx = 0;
150  subidx = 0;
151  ec = SdoErrc::NO_PDO;
152 #if !LELY_NO_CO_TPDO
153  }
154 #endif
155  return ::std::make_tuple(idx, subidx);
156  }
157 
158  Device* self;
159 
160  BasicLockable* mutex{nullptr};
161 
162  ::std::unique_ptr<co_dev_t, DeviceDeleter> dev;
163 
164 #if !LELY_NO_CO_RPDO
165  ::std::map<uint32_t, uint32_t> rpdo_mapping;
166 #endif
167 #if !LELY_NO_CO_TPDO
168  ::std::map<uint32_t, uint32_t> tpdo_mapping;
169 #endif
170 
171  ::std::function<void(uint16_t, uint8_t)> on_write;
172 #if !LELY_NO_CO_LSS
173  ::std::function<void(uint8_t, uint16_t, uint8_t)> on_rpdo_write;
174 #endif
175 };
176 
177 #if !LELY_NO_CO_DCF
178 Device::Device(const ::std::string& dcf_txt, const ::std::string& dcf_bin,
179  uint8_t id, util::BasicLockable* mutex)
180  : impl_(new Impl_(this, dcf_txt, dcf_bin, id, mutex)) {}
181 #endif
182 
183 Device::~Device() = default;
184 
185 uint8_t
186 Device::netid() const noexcept {
187  ::std::lock_guard<Impl_> lock(*impl_);
188 
189  return impl_->netid();
190 }
191 
192 uint8_t
193 Device::id() const noexcept {
194  ::std::lock_guard<Impl_> lock(*impl_);
195 
196  return impl_->id();
197 }
198 
199 namespace {
200 
201 void
202 OnDnCon(co_csdo_t*, uint16_t, uint8_t, uint32_t ac, void* data) noexcept {
203  *static_cast<uint32_t*>(data) = ac;
204 }
205 
206 template <class T>
207 void
208 OnUpCon(co_csdo_t*, uint16_t, uint8_t, uint32_t ac, const void* ptr, size_t n,
209  void* data) noexcept {
210  using traits = canopen_traits<T>;
211  using c_type = typename traits::c_type;
212 
213  auto t = static_cast<::std::tuple<uint32_t&, T&>*>(data);
214 
215  auto val = c_type();
216  if (!ac) {
217  ::std::error_code ec;
218  val = traits::construct(ptr, n, ec);
219  if (ec) ac = static_cast<uint32_t>(sdo_errc(ec));
220  }
221 
222  *t = ::std::forward_as_tuple(ac, traits::from_c_type(val));
223 
224  traits::destroy(val);
225 }
226 
227 } // namespace
228 
229 template <class T>
230 typename ::std::enable_if<is_canopen<T>::value, T>::type
231 Device::Read(uint16_t idx, uint8_t subidx) const {
232  ::std::error_code ec;
233  T value(Read<T>(idx, subidx, ec));
234  if (ec) throw_sdo_error(id(), idx, subidx, ec, "Read");
235  return value;
236 }
237 
238 template <class T>
239 typename ::std::enable_if<is_canopen<T>::value, T>::type
240 Device::Read(uint16_t idx, uint8_t subidx, ::std::error_code& ec) const {
241  uint32_t ac = 0;
242  T value = T();
243  auto t = ::std::tie(ac, value);
244 
245  ::std::lock_guard<Impl_> lock(*impl_);
246  int errsv = get_errc();
247  set_errc(0);
248  if (!co_dev_up_req(dev(), idx, subidx, &OnUpCon<T>, &t)) {
249  if (ac)
250  ec = static_cast<SdoErrc>(ac);
251  else
252  ec.clear();
253  } else {
254  ec = util::make_error_code();
255  }
256  set_errc(errsv);
257  return value;
258 }
259 
260 template <class T>
261 typename ::std::enable_if<is_canopen<T>::value>::type
262 Device::Write(uint16_t idx, uint8_t subidx, const T& value) {
263  ::std::error_code ec;
264  Write(idx, subidx, value, ec);
265  if (ec) throw_sdo_error(id(), idx, subidx, ec, "Write");
266 }
267 
268 template <class T>
269 typename ::std::enable_if<is_canopen<T>::value>::type
270 Device::Write(uint16_t idx, uint8_t subidx, const T& value,
271  ::std::error_code& ec) {
272  using traits = canopen_traits<T>;
273 
274  auto val = traits::to_c_type(value, ec);
275  if (ec) return;
276  uint32_t ac = 0;
277 
278  {
279  ::std::lock_guard<Impl_> lock(*impl_);
280  int errsv = get_errc();
281  set_errc(0);
282  if (!co_dev_dn_val_req(dev(), idx, subidx, traits::index, &val, &OnDnCon,
283  &ac)) {
284  if (ac)
285  ec = static_cast<SdoErrc>(ac);
286  else
287  ec.clear();
288  } else {
289  ec = util::make_error_code();
290  }
291  set_errc(errsv);
292  }
293 
294  traits::destroy(val);
295 }
296 
297 template <>
298 void
299 Device::Write(uint16_t idx, uint8_t subidx, const ::std::string& value,
300  ::std::error_code& ec) {
301  Write(idx, subidx, value.c_str(), ec);
302 }
303 
304 template <>
305 void
306 Device::Write(uint16_t idx, uint8_t subidx, const ::std::vector<uint8_t>& value,
307  ::std::error_code& ec) {
308  Write(idx, subidx, value.data(), value.size(), ec);
309 }
310 
311 #ifndef DOXYGEN_SHOULD_SKIP_THIS
312 
313 // BOOLEAN
314 template bool Device::Read<bool>(uint16_t, uint8_t) const;
315 template bool Device::Read<bool>(uint16_t, uint8_t, ::std::error_code&) const;
316 template void Device::Write<bool>(uint16_t, uint8_t, const bool&);
317 template void Device::Write<bool>(uint16_t, uint8_t, const bool&,
318  ::std::error_code&);
319 
320 // INTEGER8
321 template int8_t Device::Read<int8_t>(uint16_t, uint8_t) const;
322 template int8_t Device::Read<int8_t>(uint16_t, uint8_t,
323  ::std::error_code&) const;
324 template void Device::Write<int8_t>(uint16_t, uint8_t, const int8_t&);
325 template void Device::Write<int8_t>(uint16_t, uint8_t, const int8_t&,
326  ::std::error_code&);
327 
328 // INTEGER16
329 template int16_t Device::Read<int16_t>(uint16_t, uint8_t) const;
330 template int16_t Device::Read<int16_t>(uint16_t, uint8_t,
331  ::std::error_code&) const;
332 template void Device::Write<int16_t>(uint16_t, uint8_t, const int16_t&);
333 template void Device::Write<int16_t>(uint16_t, uint8_t, const int16_t&,
334  ::std::error_code&);
335 
336 // INTEGER32
337 template int32_t Device::Read<int32_t>(uint16_t, uint8_t) const;
338 template int32_t Device::Read<int32_t>(uint16_t, uint8_t,
339  ::std::error_code&) const;
340 template void Device::Write<int32_t>(uint16_t, uint8_t, const int32_t&);
341 template void Device::Write<int32_t>(uint16_t, uint8_t, const int32_t&,
342  ::std::error_code&);
343 
344 // UNSIGNED8
345 template uint8_t Device::Read<uint8_t>(uint16_t, uint8_t) const;
346 template uint8_t Device::Read<uint8_t>(uint16_t, uint8_t,
347  ::std::error_code&) const;
348 template void Device::Write<uint8_t>(uint16_t, uint8_t, const uint8_t&);
349 template void Device::Write<uint8_t>(uint16_t, uint8_t, const uint8_t&,
350  ::std::error_code&);
351 
352 // UNSIGNED16
353 template uint16_t Device::Read<uint16_t>(uint16_t, uint8_t) const;
354 template uint16_t Device::Read<uint16_t>(uint16_t, uint8_t,
355  ::std::error_code&) const;
356 template void Device::Write<uint16_t>(uint16_t, uint8_t, const uint16_t&);
357 template void Device::Write<uint16_t>(uint16_t, uint8_t, const uint16_t&,
358  ::std::error_code&);
359 
360 // UNSIGNED32
361 template uint32_t Device::Read<uint32_t>(uint16_t, uint8_t) const;
362 template uint32_t Device::Read<uint32_t>(uint16_t, uint8_t,
363  ::std::error_code&) const;
364 template void Device::Write<uint32_t>(uint16_t, uint8_t, const uint32_t&);
365 template void Device::Write<uint32_t>(uint16_t, uint8_t, const uint32_t&,
366  ::std::error_code&);
367 
368 // REAL32
369 template float Device::Read<float>(uint16_t, uint8_t) const;
370 template float Device::Read<float>(uint16_t, uint8_t, ::std::error_code&) const;
371 template void Device::Write<float>(uint16_t, uint8_t, const float&);
372 template void Device::Write<float>(uint16_t, uint8_t, const float&,
373  ::std::error_code&);
374 
375 // VISIBLE_STRING
376 template ::std::string Device::Read<::std::string>(uint16_t, uint8_t) const;
377 template ::std::string Device::Read<::std::string>(uint16_t, uint8_t,
378  ::std::error_code&) const;
379 template void Device::Write<::std::string>(uint16_t, uint8_t,
380  const ::std::string&);
381 // template void Device::Write<::std::string>(uint16_t, uint8_t,
382 // const ::std::string&,
383 // ::std::error_code&);
384 
385 // OCTET_STRING
386 template ::std::vector<uint8_t> Device::Read<::std::vector<uint8_t>>(
387  uint16_t, uint8_t) const;
388 template ::std::vector<uint8_t> Device::Read<::std::vector<uint8_t>>(
389  uint16_t, uint8_t, ::std::error_code&) const;
390 template void Device::Write<::std::vector<uint8_t>>(
391  uint16_t, uint8_t, const ::std::vector<uint8_t>&);
392 // template void Device::Write<::std::vector<uint8_t>>(
393 // uint16_t, uint8_t, const ::std::vector<uint8_t>&, ::std::error_code&);
394 
395 // UNICODE_STRING
396 template ::std::basic_string<char16_t>
397  Device::Read<::std::basic_string<char16_t>>(uint16_t, uint8_t) const;
398 template ::std::basic_string<char16_t>
399 Device::Read<::std::basic_string<char16_t>>(uint16_t, uint8_t,
400  ::std::error_code&) const;
401 template void Device::Write<::std::basic_string<char16_t>>(
402  uint16_t, uint8_t, const ::std::basic_string<char16_t>&);
403 template void Device::Write<::std::basic_string<char16_t>>(
404  uint16_t, uint8_t, const ::std::basic_string<char16_t>&,
405  ::std::error_code&);
406 
407 // TIME_OF_DAY
408 // TIME_DIFFERENCE
409 // DOMAIN
410 // INTEGER24
411 
412 // REAL64
413 template double Device::Read<double>(uint16_t, uint8_t) const;
414 template double Device::Read<double>(uint16_t, uint8_t,
415  ::std::error_code&) const;
416 template void Device::Write<double>(uint16_t, uint8_t, const double&);
417 template void Device::Write<double>(uint16_t, uint8_t, const double&,
418  ::std::error_code&);
419 
420 // INTEGER40
421 // INTEGER48
422 // INTEGER56
423 
424 // INTEGER64
425 template int64_t Device::Read<int64_t>(uint16_t, uint8_t) const;
426 template int64_t Device::Read<int64_t>(uint16_t, uint8_t,
427  ::std::error_code&) const;
428 template void Device::Write<int64_t>(uint16_t, uint8_t, const int64_t&);
429 template void Device::Write<int64_t>(uint16_t, uint8_t, const int64_t&,
430  ::std::error_code&);
431 
432 // UNSIGNED24
433 // UNSIGNED40
434 // UNSIGNED48
435 // UNSIGNED56
436 
437 // UNSIGNED64
438 template uint64_t Device::Read<uint64_t>(uint16_t, uint8_t) const;
439 template uint64_t Device::Read<uint64_t>(uint16_t, uint8_t,
440  ::std::error_code&) const;
441 template void Device::Write<uint64_t>(uint16_t, uint8_t, const uint64_t&);
442 template void Device::Write<uint64_t>(uint16_t, uint8_t, const uint64_t&,
443  ::std::error_code&);
444 
445 #endif // DOXYGEN_SHOULD_SKIP_THIS
446 
447 void
448 Device::Write(uint16_t idx, uint8_t subidx, const char* value) {
449  ::std::error_code ec;
450  Write(idx, subidx, value, ec);
451  if (ec) throw_sdo_error(id(), idx, subidx, ec, "Write");
452 }
453 
454 void
455 Device::Write(uint16_t idx, uint8_t subidx, const char* value,
456  ::std::error_code& ec) {
457  Write(idx, subidx, value, ::std::char_traits<char>::length(value), ec);
458 }
459 
460 void
461 Device::Write(uint16_t idx, uint8_t subidx, const char16_t* value) {
462  ::std::error_code ec;
463  Write(idx, subidx, value, ec);
464  if (ec) throw_sdo_error(id(), idx, subidx, ec, "Write");
465 }
466 
467 void
468 Device::Write(uint16_t idx, uint8_t subidx, const char16_t* value,
469  ::std::error_code& ec) {
471 
472  auto val = traits::to_c_type(value, ec);
473  if (ec) return;
474  uint32_t ac = 0;
475 
476  {
477  ::std::lock_guard<Impl_> lock(*impl_);
478  int errsv = get_errc();
479  set_errc(0);
480  if (!co_dev_dn_val_req(dev(), idx, subidx, traits::index, &val, &OnDnCon,
481  &ac)) {
482  if (ac)
483  ec = static_cast<SdoErrc>(ac);
484  else
485  ec.clear();
486  } else {
487  ec = util::make_error_code();
488  }
489  set_errc(errsv);
490  }
491 
492  traits::destroy(val);
493 }
494 
495 void
496 Device::Write(uint16_t idx, uint8_t subidx, const void* p, ::std::size_t n) {
497  ::std::error_code ec;
498  Write(idx, subidx, p, n, ec);
499  if (ec) throw_sdo_error(id(), idx, subidx, ec, "Write");
500 }
501 
502 void
503 Device::Write(uint16_t idx, uint8_t subidx, const void* p, ::std::size_t n,
504  ::std::error_code& ec) {
505  uint32_t ac = 0;
506 
507  ::std::lock_guard<Impl_> lock(*impl_);
508  int errsv = get_errc();
509  set_errc(0);
510  if (!co_dev_dn_req(dev(), idx, subidx, p, n, &OnDnCon, &ac)) {
511  if (ac)
512  ec = static_cast<SdoErrc>(ac);
513  else
514  ec.clear();
515  } else {
516  ec = util::make_error_code();
517  }
518  set_errc(errsv);
519 }
520 
521 void
522 Device::WriteDcf(const uint8_t* begin, const uint8_t* end) {
523  ::std::error_code ec;
524  WriteDcf(begin, end, ec);
525  if (ec) throw_sdo_error(id(), 0, 0, ec, "WriteDcf");
526 }
527 
528 void
529 Device::WriteDcf(const uint8_t* begin, const uint8_t* end,
530  ::std::error_code& ec) {
531  uint32_t ac = 0;
532 
533  ::std::lock_guard<Impl_> lock(*impl_);
534  int errsv = get_errc();
535  set_errc(0);
536  if (!co_dev_dn_dcf_req(dev(), begin, end, &OnDnCon, &ac)) {
537  if (ac)
538  ec = static_cast<SdoErrc>(ac);
539  else
540  ec.clear();
541  } else {
542  ec = util::make_error_code();
543  }
544  set_errc(errsv);
545 }
546 
547 void
548 Device::WriteDcf(const char* path) {
549  ::std::error_code ec;
550  WriteDcf(path, ec);
551  if (ec) throw_sdo_error(id(), 0, 0, ec, "WriteDcf");
552 }
553 
554 #if !LELY_NO_STDIO
555 void
556 Device::WriteDcf(const char* path, ::std::error_code& ec) {
557  int errsv = get_errc();
558  set_errc(0);
559 
560  void* dom = nullptr;
561  if (co_val_read_file(CO_DEFTYPE_DOMAIN, &dom, path)) {
562  auto begin =
563  static_cast<const uint8_t*>(co_val_addressof(CO_DEFTYPE_DOMAIN, &dom));
564  auto end = begin + co_val_sizeof(CO_DEFTYPE_DOMAIN, &dom);
565  WriteDcf(begin, end, ec);
566  } else {
567  ec = util::make_error_code();
568  }
569  if (dom) co_val_fini(CO_DEFTYPE_DOMAIN, &dom);
570  set_errc(errsv);
571 }
572 #endif
573 
574 void
575 Device::WriteEvent(uint16_t idx, uint8_t subidx) {
576  ::std::error_code ec;
577  WriteEvent(idx, subidx, ec);
578  if (ec) throw_sdo_error(id(), idx, subidx, ec, "WriteEvent");
579 }
580 
581 void
582 Device::WriteEvent(uint16_t idx, uint8_t subidx,
583  ::std::error_code& ec) noexcept {
584  ::std::lock_guard<Impl_> lock(*impl_);
585 
586  SetEvent(idx, subidx, ec);
587 }
588 
589 template <class T>
590 typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
591 Device::RpdoRead(uint8_t id, uint16_t idx, uint8_t subidx) const {
592  ::std::error_code ec;
593  T value(RpdoRead<T>(id, idx, subidx, ec));
594  if (ec) throw_sdo_error(id, idx, subidx, ec, "RpdoRead");
595  return value;
596 }
597 
598 template <class T>
599 typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
600 Device::RpdoRead(uint8_t id, uint16_t idx, uint8_t subidx,
601  ::std::error_code& ec) const {
602  ec.clear();
603  {
604  ::std::lock_guard<Impl_> lock(*impl_);
605  ::std::tie(idx, subidx) = impl_->RpdoMapping(id, idx, subidx, ec);
606  }
607  if (ec) return T();
608  return Read<T>(idx, subidx, ec);
609 }
610 
611 template <class T>
612 typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
613 Device::TpdoRead(uint8_t id, uint16_t idx, uint8_t subidx) const {
614  ::std::error_code ec;
615  T value(TpdoRead<T>(id, idx, subidx, ec));
616  if (ec) throw_sdo_error(id, idx, subidx, ec, "TpdoRead");
617  return value;
618 }
619 
620 template <class T>
621 typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
622 Device::TpdoRead(uint8_t id, uint16_t idx, uint8_t subidx,
623  ::std::error_code& ec) const {
624  ec.clear();
625  {
626  ::std::lock_guard<Impl_> lock(*impl_);
627  ::std::tie(idx, subidx) = impl_->TpdoMapping(id, idx, subidx, ec);
628  }
629  if (ec) return T();
630  return Read<T>(idx, subidx, ec);
631 }
632 
633 template <class T>
634 typename ::std::enable_if<is_canopen_basic<T>::value>::type
635 Device::TpdoWrite(uint8_t id, uint16_t idx, uint8_t subidx, T value) {
636  ::std::error_code ec;
637  TpdoWrite(id, idx, subidx, value, ec);
638  if (ec) throw_sdo_error(id, idx, subidx, ec, "TpdoWrite");
639 }
640 
641 template <class T>
642 typename ::std::enable_if<is_canopen_basic<T>::value>::type
643 Device::TpdoWrite(uint8_t id, uint16_t idx, uint8_t subidx, T value,
644  ::std::error_code& ec) {
645  ec.clear();
646  {
647  ::std::lock_guard<Impl_> lock(*impl_);
648  ::std::tie(idx, subidx) = impl_->TpdoMapping(id, idx, subidx, ec);
649  }
650  if (!ec) Write<T>(idx, subidx, value, ec);
651 }
652 
653 #ifndef DOXYGEN_SHOULD_SKIP_THIS
654 
655 // BOOLEAN
656 template bool Device::RpdoRead<bool>(uint8_t, uint16_t, uint8_t) const;
657 template bool Device::RpdoRead<bool>(uint8_t, uint16_t, uint8_t,
658  ::std::error_code&) const;
659 template bool Device::TpdoRead<bool>(uint8_t, uint16_t, uint8_t) const;
660 template bool Device::TpdoRead<bool>(uint8_t, uint16_t, uint8_t,
661  ::std::error_code&) const;
662 template void Device::TpdoWrite<bool>(uint8_t, uint16_t, uint8_t, bool);
663 template void Device::TpdoWrite<bool>(uint8_t, uint16_t, uint8_t, bool,
664  ::std::error_code&);
665 
666 // INTEGER8
667 template int8_t Device::RpdoRead<int8_t>(uint8_t, uint16_t, uint8_t) const;
668 template int8_t Device::RpdoRead<int8_t>(uint8_t, uint16_t, uint8_t,
669  ::std::error_code&) const;
670 template int8_t Device::TpdoRead<int8_t>(uint8_t, uint16_t, uint8_t) const;
671 template int8_t Device::TpdoRead<int8_t>(uint8_t, uint16_t, uint8_t,
672  ::std::error_code&) const;
673 template void Device::TpdoWrite<int8_t>(uint8_t, uint16_t, uint8_t, int8_t);
674 template void Device::TpdoWrite<int8_t>(uint8_t, uint16_t, uint8_t, int8_t,
675  ::std::error_code&);
676 
677 // INTEGER16
678 template int16_t Device::RpdoRead<int16_t>(uint8_t, uint16_t, uint8_t) const;
679 template int16_t Device::RpdoRead<int16_t>(uint8_t, uint16_t, uint8_t,
680  ::std::error_code&) const;
681 template int16_t Device::TpdoRead<int16_t>(uint8_t, uint16_t, uint8_t) const;
682 template int16_t Device::TpdoRead<int16_t>(uint8_t, uint16_t, uint8_t,
683  ::std::error_code&) const;
684 template void Device::TpdoWrite<int16_t>(uint8_t, uint16_t, uint8_t, int16_t);
685 template void Device::TpdoWrite<int16_t>(uint8_t, uint16_t, uint8_t, int16_t,
686  ::std::error_code&);
687 
688 // INTEGER32
689 template int32_t Device::RpdoRead<int32_t>(uint8_t, uint16_t, uint8_t) const;
690 template int32_t Device::RpdoRead<int32_t>(uint8_t, uint16_t, uint8_t,
691  ::std::error_code&) const;
692 template int32_t Device::TpdoRead<int32_t>(uint8_t, uint16_t, uint8_t) const;
693 template int32_t Device::TpdoRead<int32_t>(uint8_t, uint16_t, uint8_t,
694  ::std::error_code&) const;
695 template void Device::TpdoWrite<int32_t>(uint8_t, uint16_t, uint8_t, int32_t);
696 template void Device::TpdoWrite<int32_t>(uint8_t, uint16_t, uint8_t, int32_t,
697  ::std::error_code&);
698 
699 // UNSIGNED8
700 template uint8_t Device::RpdoRead<uint8_t>(uint8_t, uint16_t, uint8_t) const;
701 template uint8_t Device::RpdoRead<uint8_t>(uint8_t, uint16_t, uint8_t,
702  ::std::error_code&) const;
703 template uint8_t Device::TpdoRead<uint8_t>(uint8_t, uint16_t, uint8_t) const;
704 template uint8_t Device::TpdoRead<uint8_t>(uint8_t, uint16_t, uint8_t,
705  ::std::error_code&) const;
706 template void Device::TpdoWrite<uint8_t>(uint8_t, uint16_t, uint8_t, uint8_t);
707 template void Device::TpdoWrite<uint8_t>(uint8_t, uint16_t, uint8_t, uint8_t,
708  ::std::error_code&);
709 
710 // UNSIGNED16
711 template uint16_t Device::RpdoRead<uint16_t>(uint8_t, uint16_t, uint8_t) const;
712 template uint16_t Device::RpdoRead<uint16_t>(uint8_t, uint16_t, uint8_t,
713  ::std::error_code&) const;
714 template uint16_t Device::TpdoRead<uint16_t>(uint8_t, uint16_t, uint8_t) const;
715 template uint16_t Device::TpdoRead<uint16_t>(uint8_t, uint16_t, uint8_t,
716  ::std::error_code&) const;
717 template void Device::TpdoWrite<uint16_t>(uint8_t, uint16_t, uint8_t, uint16_t);
718 template void Device::TpdoWrite<uint16_t>(uint8_t, uint16_t, uint8_t, uint16_t,
719  ::std::error_code&);
720 
721 // UNSIGNED32
722 template uint32_t Device::RpdoRead<uint32_t>(uint8_t, uint16_t, uint8_t) const;
723 template uint32_t Device::RpdoRead<uint32_t>(uint8_t, uint16_t, uint8_t,
724  ::std::error_code&) const;
725 template uint32_t Device::TpdoRead<uint32_t>(uint8_t, uint16_t, uint8_t) const;
726 template uint32_t Device::TpdoRead<uint32_t>(uint8_t, uint16_t, uint8_t,
727  ::std::error_code&) const;
728 template void Device::TpdoWrite<uint32_t>(uint8_t, uint16_t, uint8_t, uint32_t);
729 template void Device::TpdoWrite<uint32_t>(uint8_t, uint16_t, uint8_t, uint32_t,
730  ::std::error_code&);
731 
732 // REAL32
733 template float Device::RpdoRead<float>(uint8_t, uint16_t, uint8_t) const;
734 template float Device::RpdoRead<float>(uint8_t, uint16_t, uint8_t,
735  ::std::error_code&) const;
736 template float Device::TpdoRead<float>(uint8_t, uint16_t, uint8_t) const;
737 template float Device::TpdoRead<float>(uint8_t, uint16_t, uint8_t,
738  ::std::error_code&) const;
739 template void Device::TpdoWrite<float>(uint8_t, uint16_t, uint8_t, float);
740 template void Device::TpdoWrite<float>(uint8_t, uint16_t, uint8_t, float,
741  ::std::error_code&);
742 
743 // VISIBLE_STRING
744 // OCTET_STRING
745 // UNICODE_STRING
746 // TIME_OF_DAY
747 // TIME_DIFFERENCE
748 // DOMAIN
749 // INTEGER24
750 
751 // REAL64
752 template double Device::RpdoRead<double>(uint8_t, uint16_t, uint8_t) const;
753 template double Device::RpdoRead<double>(uint8_t, uint16_t, uint8_t,
754  ::std::error_code&) const;
755 template double Device::TpdoRead<double>(uint8_t, uint16_t, uint8_t) const;
756 template double Device::TpdoRead<double>(uint8_t, uint16_t, uint8_t,
757  ::std::error_code&) const;
758 template void Device::TpdoWrite<double>(uint8_t, uint16_t, uint8_t, double);
759 template void Device::TpdoWrite<double>(uint8_t, uint16_t, uint8_t, double,
760  ::std::error_code&);
761 
762 // INTEGER40
763 // INTEGER48
764 // INTEGER56
765 
766 // INTEGER64
767 template int64_t Device::RpdoRead<int64_t>(uint8_t, uint16_t, uint8_t) const;
768 template int64_t Device::RpdoRead<int64_t>(uint8_t, uint16_t, uint8_t,
769  ::std::error_code&) const;
770 template int64_t Device::TpdoRead<int64_t>(uint8_t, uint16_t, uint8_t) const;
771 template int64_t Device::TpdoRead<int64_t>(uint8_t, uint16_t, uint8_t,
772  ::std::error_code&) const;
773 template void Device::TpdoWrite<int64_t>(uint8_t, uint16_t, uint8_t, int64_t);
774 template void Device::TpdoWrite<int64_t>(uint8_t, uint16_t, uint8_t, int64_t,
775  ::std::error_code&);
776 
777 // UNSIGNED24
778 // UNSIGNED40
779 // UNSIGNED48
780 // UNSIGNED56
781 
782 // UNSIGNED64
783 template uint64_t Device::RpdoRead<uint64_t>(uint8_t, uint16_t, uint8_t) const;
784 template uint64_t Device::RpdoRead<uint64_t>(uint8_t, uint16_t, uint8_t,
785  ::std::error_code&) const;
786 template uint64_t Device::TpdoRead<uint64_t>(uint8_t, uint16_t, uint8_t) const;
787 template uint64_t Device::TpdoRead<uint64_t>(uint8_t, uint16_t, uint8_t,
788  ::std::error_code&) const;
789 template void Device::TpdoWrite<uint64_t>(uint8_t, uint16_t, uint8_t, uint64_t);
790 template void Device::TpdoWrite<uint64_t>(uint8_t, uint16_t, uint8_t, uint64_t,
791  ::std::error_code&);
792 
793 #endif // DOXYGEN_SHOULD_SKIP_THIS
794 
795 void
796 Device::TpdoWriteEvent(uint8_t id, uint16_t idx, uint8_t subidx) {
797  ::std::error_code ec;
798  TpdoWriteEvent(id, idx, subidx, ec);
799  if (ec) throw_sdo_error(id, idx, subidx, ec, "TpdoWriteEvent");
800 }
801 
802 void
803 Device::TpdoWriteEvent(uint8_t id, uint16_t idx, uint8_t subidx,
804  ::std::error_code& ec) noexcept {
805  ::std::lock_guard<Impl_> lock(*impl_);
806 
807  TpdoSetEvent(id, idx, subidx, ec);
808 }
809 
810 void
811 Device::OnWrite(::std::function<void(uint16_t, uint8_t)> on_write) {
812  ::std::lock_guard<Impl_> lock(*impl_);
813  impl_->on_write = on_write;
814 }
815 
816 void
817 Device::OnRpdoWrite(
818  ::std::function<void(uint8_t, uint16_t, uint8_t)> on_rpdo_write) {
819 #if LELY_NO_CO_RPDO
820  (void)on_rpdo_write;
821 #else
822  ::std::lock_guard<Impl_> lock(*impl_);
823  impl_->on_rpdo_write = on_rpdo_write;
824 #endif
825 }
826 
827 co_dev_t*
828 Device::dev() const noexcept {
829  return impl_->dev.get();
830 }
831 
832 const ::std::type_info&
833 Device::Type(uint16_t idx, uint8_t subidx) const {
834  ::std::error_code ec;
835  auto& ti = Type(idx, subidx, ec);
836  if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "Type");
837  return ti;
838 }
839 
840 const ::std::type_info&
841 Device::Type(uint16_t idx, uint8_t subidx,
842  ::std::error_code& ec) const noexcept {
843  auto obj = co_dev_find_obj(dev(), idx);
844  if (!obj) {
845  ec = SdoErrc::NO_OBJ;
846  return typeid(void);
847  }
848 
849  auto sub = co_obj_find_sub(obj, subidx);
850  if (!sub) {
851  ec = SdoErrc::NO_SUB;
852  return typeid(void);
853  }
854 
855  ec.clear();
856  switch (co_sub_get_type(sub)) {
857  case CO_DEFTYPE_BOOLEAN:
858  return typeid(bool);
859  case CO_DEFTYPE_INTEGER8:
860  return typeid(int8_t);
862  return typeid(int16_t);
864  return typeid(int32_t);
866  return typeid(uint8_t);
868  return typeid(uint16_t);
870  return typeid(uint32_t);
871  case CO_DEFTYPE_REAL32:
872  return typeid(float);
874  return typeid(::std::string);
876  return typeid(::std::vector<uint8_t>);
878  return typeid(::std::basic_string<char16_t>);
879  // case CO_DEFTYPE_TIME_OF_DAY: ...
880  // case CO_DEFTYPE_TIME_DIFF: ...
881  case CO_DEFTYPE_DOMAIN:
882  return typeid(::std::vector<uint8_t>);
883  // case CO_DEFTYPE_INTEGER24: ...
884  case CO_DEFTYPE_REAL64:
885  return typeid(double);
886  // case CO_DEFTYPE_INTEGER40: ...
887  // case CO_DEFTYPE_INTEGER48: ...
888  // case CO_DEFTYPE_INTEGER56: ...
890  return typeid(int64_t);
891  // case CO_DEFTYPE_UNSIGNED24: ...
892  // case CO_DEFTYPE_UNSIGNED40: ...
893  // case CO_DEFTYPE_UNSIGNED48: ...
894  // case CO_DEFTYPE_UNSIGNED56: ...
896  return typeid(uint64_t);
897  default:
898  return typeid(void);
899  }
900 }
901 
902 template <class T>
903 typename ::std::enable_if<is_canopen<T>::value, T>::type
904 Device::Get(uint16_t idx, uint8_t subidx) const {
905  ::std::error_code ec;
906  auto value = Get<T>(idx, subidx, ec);
907  if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "Get");
908  return value;
909 }
910 
911 template <class T>
912 typename ::std::enable_if<is_canopen<T>::value, T>::type
913 Device::Get(uint16_t idx, uint8_t subidx,
914  ::std::error_code& ec) const noexcept {
915  using traits = canopen_traits<T>;
916  using c_type = typename traits::c_type;
917 
918  auto obj = co_dev_find_obj(dev(), idx);
919  if (!obj) {
920  ec = SdoErrc::NO_OBJ;
921  return T();
922  }
923 
924  auto sub = co_obj_find_sub(obj, subidx);
925  if (!sub) {
926  ec = SdoErrc::NO_SUB;
927  return T();
928  }
929 
930  if (!is_canopen_same(traits::index, co_sub_get_type(sub))) {
931  ec = SdoErrc::TYPE_LEN;
932  return T();
933  }
934 
935  ec.clear();
936 
937  auto pval = static_cast<const c_type*>(co_sub_get_val(sub));
938  return traits::from_c_type(*pval);
939 }
940 
941 template <class T>
942 typename ::std::enable_if<is_canopen<T>::value>::type
943 Device::Set(uint16_t idx, uint8_t subidx, const T& value) {
944  ::std::error_code ec;
945  Set(idx, subidx, value, ec);
946  if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "Set");
947 }
948 
949 template <class T>
950 typename ::std::enable_if<is_canopen<T>::value>::type
951 Device::Set(uint16_t idx, uint8_t subidx, const T& value,
952  ::std::error_code& ec) noexcept {
953  using traits = canopen_traits<T>;
954 
955  auto obj = co_dev_find_obj(dev(), idx);
956  if (!obj) {
957  ec = SdoErrc::NO_OBJ;
958  return;
959  }
960 
961  auto sub = co_obj_find_sub(obj, subidx);
962  if (!sub) {
963  ec = SdoErrc::NO_SUB;
964  return;
965  }
966 
967  if (!is_canopen_same(traits::index, co_sub_get_type(sub))) {
968  ec = SdoErrc::TYPE_LEN;
969  return;
970  }
971 
972  auto val = traits::to_c_type(value, ec);
973  if (ec) return;
974  auto p = traits::address(val);
975  auto n = traits::size(val);
976 
977  int errsv = get_errc();
978  set_errc(0);
979  if (co_sub_set_val(sub, p, n) == n)
980  ec.clear();
981  else
982  ec = util::make_error_code();
983  set_errc(errsv);
984 
985  traits::destroy(val);
986 }
987 
988 template <>
989 void
990 Device::Set(uint16_t idx, uint8_t subidx, const ::std::string& value,
991  ::std::error_code& ec) noexcept {
992  Set(idx, subidx, value.c_str(), ec);
993 }
994 
995 template <>
996 void
997 Device::Set(uint16_t idx, uint8_t subidx, const ::std::vector<uint8_t>& value,
998  ::std::error_code& ec) noexcept {
999  Set(idx, subidx, value.data(), value.size(), ec);
1000 }
1001 
1002 #ifndef DOXYGEN_SHOULD_SKIP_THIS
1003 
1004 // BOOLEAN
1005 template bool Device::Get<bool>(uint16_t, uint8_t) const;
1006 template bool Device::Get<bool>(uint16_t, uint8_t,
1007  ::std::error_code&) const noexcept;
1008 template void Device::Set<bool>(uint16_t, uint8_t, const bool&);
1009 template void Device::Set<bool>(uint16_t, uint8_t, const bool&,
1010  ::std::error_code&) noexcept;
1011 
1012 // INTEGER8
1013 template int8_t Device::Get<int8_t>(uint16_t, uint8_t) const;
1014 template int8_t Device::Get<int8_t>(uint16_t, uint8_t,
1015  ::std::error_code&) const noexcept;
1016 template void Device::Set<int8_t>(uint16_t, uint8_t, const int8_t&);
1017 template void Device::Set<int8_t>(uint16_t, uint8_t, const int8_t&,
1018  ::std::error_code&) noexcept;
1019 
1020 // INTEGER16
1021 template int16_t Device::Get<int16_t>(uint16_t, uint8_t) const;
1022 template int16_t Device::Get<int16_t>(uint16_t, uint8_t,
1023  ::std::error_code&) const noexcept;
1024 template void Device::Set<int16_t>(uint16_t, uint8_t, const int16_t&);
1025 template void Device::Set<int16_t>(uint16_t, uint8_t, const int16_t&,
1026  ::std::error_code&) noexcept;
1027 
1028 // INTEGER32
1029 template int32_t Device::Get<int32_t>(uint16_t, uint8_t) const;
1030 template int32_t Device::Get<int32_t>(uint16_t, uint8_t,
1031  ::std::error_code&) const noexcept;
1032 template void Device::Set<int32_t>(uint16_t, uint8_t, const int32_t&);
1033 template void Device::Set<int32_t>(uint16_t, uint8_t, const int32_t&,
1034  ::std::error_code&) noexcept;
1035 
1036 // UNSIGNED8
1037 template uint8_t Device::Get<uint8_t>(uint16_t, uint8_t) const;
1038 template uint8_t Device::Get<uint8_t>(uint16_t, uint8_t,
1039  ::std::error_code&) const noexcept;
1040 template void Device::Set<uint8_t>(uint16_t, uint8_t, const uint8_t&);
1041 template void Device::Set<uint8_t>(uint16_t, uint8_t, const uint8_t&,
1042  ::std::error_code&) noexcept;
1043 
1044 // UNSIGNED16
1045 template uint16_t Device::Get<uint16_t>(uint16_t, uint8_t) const;
1046 template uint16_t Device::Get<uint16_t>(uint16_t, uint8_t,
1047  ::std::error_code&) const noexcept;
1048 template void Device::Set<uint16_t>(uint16_t, uint8_t, const uint16_t&);
1049 template void Device::Set<uint16_t>(uint16_t, uint8_t, const uint16_t&,
1050  ::std::error_code&) noexcept;
1051 
1052 // UNSIGNED32
1053 template uint32_t Device::Get<uint32_t>(uint16_t, uint8_t) const;
1054 template uint32_t Device::Get<uint32_t>(uint16_t, uint8_t,
1055  ::std::error_code&) const noexcept;
1056 template void Device::Set<uint32_t>(uint16_t, uint8_t, const uint32_t&);
1057 template void Device::Set<uint32_t>(uint16_t, uint8_t, const uint32_t&,
1058  ::std::error_code&) noexcept;
1059 
1060 // REAL32
1061 template float Device::Get<float>(uint16_t, uint8_t) const;
1062 template float Device::Get<float>(uint16_t, uint8_t,
1063  ::std::error_code&) const noexcept;
1064 template void Device::Set<float>(uint16_t, uint8_t, const float&);
1065 template void Device::Set<float>(uint16_t, uint8_t, const float&,
1066  ::std::error_code&) noexcept;
1067 
1068 // VISIBLE_STRING
1069 template ::std::string Device::Get<::std::string>(uint16_t, uint8_t) const;
1070 template ::std::string Device::Get<::std::string>(
1071  uint16_t, uint8_t, ::std::error_code&) const noexcept;
1072 template void Device::Set<::std::string>(uint16_t, uint8_t,
1073  const ::std::string&);
1074 // template void Device::Set<::std::string>(uint16_t, uint8_t,
1075 // const ::std::string&,
1076 // ::std::error_code&) noexcept;
1077 
1078 // OCTET_STRING
1079 template ::std::vector<uint8_t> Device::Get<::std::vector<uint8_t>>(
1080  uint16_t, uint8_t) const;
1081 template ::std::vector<uint8_t> Device::Get<::std::vector<uint8_t>>(
1082  uint16_t, uint8_t, ::std::error_code&) const noexcept;
1083 template void Device::Set<::std::vector<uint8_t>>(
1084  uint16_t, uint8_t, const ::std::vector<uint8_t>&);
1085 // template void Device::Set<::std::vector<uint8_t>>(uint16_t, uint8_t,
1086 // const
1087 // ::std::vector<uint8_t>&,
1088 // ::std::error_code&)
1089 // noexcept;
1090 
1091 // UNICODE_STRING
1092 template ::std::basic_string<char16_t>
1093  Device::Get<::std::basic_string<char16_t>>(uint16_t, uint8_t) const;
1094 template ::std::basic_string<char16_t>
1095 Device::Get<::std::basic_string<char16_t>>(uint16_t, uint8_t,
1096  ::std::error_code&) const noexcept;
1097 template void Device::Set<::std::basic_string<char16_t>>(
1098  uint16_t, uint8_t, const ::std::basic_string<char16_t>&);
1099 template void Device::Set<::std::basic_string<char16_t>>(
1100  uint16_t, uint8_t, const ::std::basic_string<char16_t>&,
1101  ::std::error_code&) noexcept;
1102 
1103 // TIME_OF_DAY
1104 // TIME_DIFFERENCE
1105 // DOMAIN
1106 // INTEGER24
1107 
1108 // REAL64
1109 template double Device::Get<double>(uint16_t, uint8_t) const;
1110 template double Device::Get<double>(uint16_t, uint8_t,
1111  ::std::error_code&) const noexcept;
1112 template void Device::Set<double>(uint16_t, uint8_t, const double&);
1113 template void Device::Set<double>(uint16_t, uint8_t, const double&,
1114  ::std::error_code&) noexcept;
1115 
1116 // INTEGER40
1117 // INTEGER48
1118 // INTEGER56
1119 
1120 // INTEGER64
1121 template int64_t Device::Get<int64_t>(uint16_t, uint8_t) const;
1122 template int64_t Device::Get<int64_t>(uint16_t, uint8_t,
1123  ::std::error_code&) const noexcept;
1124 template void Device::Set<int64_t>(uint16_t, uint8_t, const int64_t&);
1125 template void Device::Set<int64_t>(uint16_t, uint8_t, const int64_t&,
1126  ::std::error_code&) noexcept;
1127 
1128 // UNSIGNED24
1129 // UNSIGNED40
1130 // UNSIGNED48
1131 // UNSIGNED56
1132 
1133 // UNSIGNED64
1134 template uint64_t Device::Get<uint64_t>(uint16_t, uint8_t) const;
1135 template uint64_t Device::Get<uint64_t>(uint16_t, uint8_t,
1136  ::std::error_code&) const noexcept;
1137 template void Device::Set<uint64_t>(uint16_t, uint8_t, const uint64_t&);
1138 template void Device::Set<uint64_t>(uint16_t, uint8_t, const uint64_t&,
1139  ::std::error_code&) noexcept;
1140 
1141 #endif // DOXYGEN_SHOULD_SKIP_THIS
1142 
1143 void
1144 Device::Set(uint16_t idx, uint8_t subidx, const char* value) {
1145  ::std::error_code ec;
1146  Set(idx, subidx, value, ec);
1147  if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "Set");
1148 }
1149 
1150 void
1151 Device::Set(uint16_t idx, uint8_t subidx, const char* value,
1152  ::std::error_code& ec) noexcept {
1153  Set(idx, subidx, value, ::std::char_traits<char>::length(value), ec);
1154 }
1155 
1156 void
1157 Device::Set(uint16_t idx, uint8_t subidx, const char16_t* value) {
1158  ::std::error_code ec;
1159  Set(idx, subidx, value, ec);
1160  if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "Set");
1161 }
1162 
1163 void
1164 Device::Set(uint16_t idx, uint8_t subidx, const char16_t* value,
1165  ::std::error_code& ec) noexcept {
1167 
1168  auto obj = co_dev_find_obj(dev(), idx);
1169  if (!obj) {
1170  ec = SdoErrc::NO_OBJ;
1171  return;
1172  }
1173 
1174  auto sub = co_obj_find_sub(obj, subidx);
1175  if (!sub) {
1176  ec = SdoErrc::NO_SUB;
1177  return;
1178  }
1179 
1180  if (!is_canopen_same(traits::index, co_sub_get_type(sub))) {
1181  ec = SdoErrc::TYPE_LEN;
1182  return;
1183  }
1184 
1185  auto val = traits::to_c_type(value, ec);
1186  if (ec) return;
1187  auto p = traits::address(val);
1188  auto n = traits::size(val);
1189 
1190  int errsv = get_errc();
1191  set_errc(0);
1192  if (co_sub_set_val(sub, p, n) == n)
1193  ec.clear();
1194  else
1195  ec = util::make_error_code();
1196  set_errc(errsv);
1197 
1198  traits::destroy(val);
1199 }
1200 
1201 void
1202 Device::Set(uint16_t idx, uint8_t subidx, const void* p, ::std::size_t n) {
1203  ::std::error_code ec;
1204  Set(idx, subidx, p, n, ec);
1205  if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "Set");
1206 }
1207 
1208 void
1209 Device::Set(uint16_t idx, uint8_t subidx, const void* p, ::std::size_t n,
1210  ::std::error_code& ec) noexcept {
1211  auto obj = co_dev_find_obj(dev(), idx);
1212  if (!obj) {
1213  ec = SdoErrc::NO_OBJ;
1214  return;
1215  }
1216 
1217  auto sub = co_obj_find_sub(obj, subidx);
1218  if (!sub) {
1219  ec = SdoErrc::NO_SUB;
1220  return;
1221  }
1222 
1223  int errsv = get_errc();
1224  set_errc(0);
1225  if (co_sub_set_val(sub, p, n) == n)
1226  ec.clear();
1227  else
1228  ec = util::make_error_code();
1229  set_errc(errsv);
1230 }
1231 
1232 #if !LELY_NO_CO_OBJ_FILE
1233 
1234 const char*
1235 Device::GetUploadFile(uint16_t idx, uint8_t subidx) const {
1236  ::std::error_code ec;
1237  auto filename = GetUploadFile(idx, subidx, ec);
1238  if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "GetUploadFile");
1239  return filename;
1240 }
1241 
1242 const char*
1243 Device::GetUploadFile(uint16_t idx, uint8_t subidx,
1244  ::std::error_code& ec) const noexcept {
1245  auto obj = co_dev_find_obj(dev(), idx);
1246  if (!obj) {
1247  ec = SdoErrc::NO_OBJ;
1248  return nullptr;
1249  }
1250 
1251  auto sub = co_obj_find_sub(obj, subidx);
1252  if (!sub) {
1253  ec = SdoErrc::NO_SUB;
1254  return nullptr;
1255  }
1256 
1257  ec.clear();
1258  return co_sub_get_upload_file(sub);
1259 }
1260 
1261 void
1262 Device::SetUploadFile(uint16_t idx, uint8_t subidx, const char* filename) {
1263  ::std::error_code ec;
1264  SetUploadFile(idx, subidx, filename, ec);
1265  if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "SetUploadFile");
1266 }
1267 
1268 void
1269 Device::SetUploadFile(uint16_t idx, uint8_t subidx, const char* filename,
1270  ::std::error_code& ec) noexcept {
1271  auto obj = co_dev_find_obj(dev(), idx);
1272  if (!obj) {
1273  ec = SdoErrc::NO_OBJ;
1274  return;
1275  }
1276 
1277  auto sub = co_obj_find_sub(obj, subidx);
1278  if (!sub) {
1279  ec = SdoErrc::NO_SUB;
1280  return;
1281  }
1282 
1283  int errsv = get_errc();
1284  set_errc(0);
1285  if (!co_sub_set_upload_file(sub, filename))
1286  ec.clear();
1287  else
1288  ec = util::make_error_code();
1289  set_errc(errsv);
1290 }
1291 
1292 const char*
1293 Device::GetDownloadFile(uint16_t idx, uint8_t subidx) const {
1294  ::std::error_code ec;
1295  auto filename = GetDownloadFile(idx, subidx, ec);
1296  if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "GetDownloadFile");
1297  return filename;
1298 }
1299 
1300 const char*
1301 Device::GetDownloadFile(uint16_t idx, uint8_t subidx,
1302  ::std::error_code& ec) const noexcept {
1303  auto obj = co_dev_find_obj(dev(), idx);
1304  if (!obj) {
1305  ec = SdoErrc::NO_OBJ;
1306  return nullptr;
1307  }
1308 
1309  auto sub = co_obj_find_sub(obj, subidx);
1310  if (!sub) {
1311  ec = SdoErrc::NO_SUB;
1312  return nullptr;
1313  }
1314 
1315  ec.clear();
1316  return co_sub_get_download_file(sub);
1317 }
1318 
1319 void
1320 Device::SetDownloadFile(uint16_t idx, uint8_t subidx, const char* filename) {
1321  ::std::error_code ec;
1322  SetDownloadFile(idx, subidx, filename, ec);
1323  if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "SetDownloadFile");
1324 }
1325 
1326 void
1327 Device::SetDownloadFile(uint16_t idx, uint8_t subidx, const char* filename,
1328  ::std::error_code& ec) noexcept {
1329  auto obj = co_dev_find_obj(dev(), idx);
1330  if (!obj) {
1331  ec = SdoErrc::NO_OBJ;
1332  return;
1333  }
1334 
1335  auto sub = co_obj_find_sub(obj, subidx);
1336  if (!sub) {
1337  ec = SdoErrc::NO_SUB;
1338  return;
1339  }
1340 
1341  int errsv = get_errc();
1342  set_errc(0);
1343  if (!co_sub_set_download_file(sub, filename))
1344  ec.clear();
1345  else
1346  ec = util::make_error_code();
1347  set_errc(errsv);
1348 }
1349 
1350 #endif // !LELY_NO_CO_OBJ_FILE
1351 
1352 void
1353 Device::SetEvent(uint16_t idx, uint8_t subidx) {
1354  ::std::error_code ec;
1355  SetEvent(idx, subidx, ec);
1356  if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "SetEvent");
1357 }
1358 
1359 void
1360 Device::SetEvent(uint16_t idx, uint8_t subidx, ::std::error_code& ec) noexcept {
1361  auto obj = co_dev_find_obj(dev(), idx);
1362  if (!obj) {
1363  ec = SdoErrc::NO_OBJ;
1364  return;
1365  }
1366 
1367  auto sub = co_obj_find_sub(obj, subidx);
1368  if (!sub) {
1369  ec = SdoErrc::NO_SUB;
1370  return;
1371  }
1372 
1373 #if !LELY_NO_CO_TPDO
1374  co_dev_tpdo_event(dev(), idx, subidx);
1375 #if !LELYY_NO_CO_MPDO
1376  co_dev_sam_mpdo_event(dev(), idx, subidx);
1377 #endif
1378 #endif // !LELY_NO_CO_TPDO
1379 }
1380 
1381 template <class T>
1382 typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
1383 Device::RpdoGet(uint8_t id, uint16_t idx, uint8_t subidx) const {
1384  ::std::error_code ec;
1385  auto value = RpdoGet<T>(id, idx, subidx, ec);
1386  if (ec) throw_sdo_error(id, idx, subidx, ec, "RpdoGet");
1387  return value;
1388 }
1389 
1390 template <class T>
1391 typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
1392 Device::RpdoGet(uint8_t id, uint16_t idx, uint8_t subidx,
1393  ::std::error_code& ec) const noexcept {
1394  ec.clear();
1395  ::std::tie(idx, subidx) = impl_->RpdoMapping(id, idx, subidx, ec);
1396  if (ec) return T();
1397  return Get<T>(idx, subidx, ec);
1398 }
1399 
1400 template <class T>
1401 typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
1402 Device::TpdoGet(uint8_t id, uint16_t idx, uint8_t subidx) const {
1403  ::std::error_code ec;
1404  auto value = TpdoGet<T>(id, idx, subidx, ec);
1405  if (ec) throw_sdo_error(id, idx, subidx, ec, "TpdoGet");
1406  return value;
1407 }
1408 
1409 template <class T>
1410 typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
1411 Device::TpdoGet(uint8_t id, uint16_t idx, uint8_t subidx,
1412  ::std::error_code& ec) const noexcept {
1413  ec.clear();
1414  ::std::tie(idx, subidx) = impl_->TpdoMapping(id, idx, subidx, ec);
1415  if (ec) return T();
1416  return Get<T>(idx, subidx, ec);
1417 }
1418 
1419 template <class T>
1420 typename ::std::enable_if<is_canopen_basic<T>::value>::type
1421 Device::TpdoSet(uint8_t id, uint16_t idx, uint8_t subidx, T value) {
1422  ::std::error_code ec;
1423  TpdoSet(id, idx, subidx, value, ec);
1424  if (ec) throw_sdo_error(id, idx, subidx, ec, "TpdoSet");
1425 }
1426 
1427 template <class T>
1428 typename ::std::enable_if<is_canopen_basic<T>::value>::type
1429 Device::TpdoSet(uint8_t id, uint16_t idx, uint8_t subidx, T value,
1430  ::std::error_code& ec) noexcept {
1431  ec.clear();
1432  ::std::tie(idx, subidx) = impl_->TpdoMapping(id, idx, subidx, ec);
1433  if (!ec) Set<T>(idx, subidx, value, ec);
1434 }
1435 
1436 #ifndef DOXYGEN_SHOULD_SKIP_THIS
1437 
1438 // BOOLEAN
1439 template bool Device::RpdoGet<bool>(uint8_t, uint16_t, uint8_t) const;
1440 template bool Device::RpdoGet<bool>(uint8_t, uint16_t, uint8_t,
1441  ::std::error_code&) const noexcept;
1442 template bool Device::TpdoGet<bool>(uint8_t, uint16_t, uint8_t) const;
1443 template bool Device::TpdoGet<bool>(uint8_t, uint16_t, uint8_t,
1444  ::std::error_code&) const noexcept;
1445 template void Device::TpdoSet<bool>(uint8_t, uint16_t, uint8_t, bool);
1446 template void Device::TpdoSet<bool>(uint8_t, uint16_t, uint8_t, bool,
1447  ::std::error_code&) noexcept;
1448 
1449 // INTEGER8
1450 template int8_t Device::RpdoGet<int8_t>(uint8_t, uint16_t, uint8_t) const;
1451 template int8_t Device::RpdoGet<int8_t>(uint8_t, uint16_t, uint8_t,
1452  ::std::error_code&) const noexcept;
1453 template int8_t Device::TpdoGet<int8_t>(uint8_t, uint16_t, uint8_t) const;
1454 template int8_t Device::TpdoGet<int8_t>(uint8_t, uint16_t, uint8_t,
1455  ::std::error_code&) const noexcept;
1456 template void Device::TpdoSet<int8_t>(uint8_t, uint16_t, uint8_t, int8_t);
1457 template void Device::TpdoSet<int8_t>(uint8_t, uint16_t, uint8_t, int8_t,
1458  ::std::error_code&) noexcept;
1459 
1460 // INTEGER16
1461 template int16_t Device::RpdoGet<int16_t>(uint8_t, uint16_t, uint8_t) const;
1462 template int16_t Device::RpdoGet<int16_t>(uint8_t, uint16_t, uint8_t,
1463  ::std::error_code&) const noexcept;
1464 template int16_t Device::TpdoGet<int16_t>(uint8_t, uint16_t, uint8_t) const;
1465 template int16_t Device::TpdoGet<int16_t>(uint8_t, uint16_t, uint8_t,
1466  ::std::error_code&) const noexcept;
1467 template void Device::TpdoSet<int16_t>(uint8_t, uint16_t, uint8_t, int16_t);
1468 template void Device::TpdoSet<int16_t>(uint8_t, uint16_t, uint8_t, int16_t,
1469  ::std::error_code&) noexcept;
1470 
1471 // INTEGER32
1472 template int32_t Device::RpdoGet<int32_t>(uint8_t, uint16_t, uint8_t) const;
1473 template int32_t Device::RpdoGet<int32_t>(uint8_t, uint16_t, uint8_t,
1474  ::std::error_code&) const noexcept;
1475 template int32_t Device::TpdoGet<int32_t>(uint8_t, uint16_t, uint8_t) const;
1476 template int32_t Device::TpdoGet<int32_t>(uint8_t, uint16_t, uint8_t,
1477  ::std::error_code&) const noexcept;
1478 template void Device::TpdoSet<int32_t>(uint8_t, uint16_t, uint8_t, int32_t);
1479 template void Device::TpdoSet<int32_t>(uint8_t, uint16_t, uint8_t, int32_t,
1480  ::std::error_code&) noexcept;
1481 
1482 // UNSIGNED8
1483 template uint8_t Device::RpdoGet<uint8_t>(uint8_t, uint16_t, uint8_t) const;
1484 template uint8_t Device::RpdoGet<uint8_t>(uint8_t, uint16_t, uint8_t,
1485  ::std::error_code&) const noexcept;
1486 template uint8_t Device::TpdoGet<uint8_t>(uint8_t, uint16_t, uint8_t) const;
1487 template uint8_t Device::TpdoGet<uint8_t>(uint8_t, uint16_t, uint8_t,
1488  ::std::error_code&) const noexcept;
1489 template void Device::TpdoSet<uint8_t>(uint8_t, uint16_t, uint8_t, uint8_t);
1490 template void Device::TpdoSet<uint8_t>(uint8_t, uint16_t, uint8_t, uint8_t,
1491  ::std::error_code&) noexcept;
1492 
1493 // UNSIGNED16
1494 template uint16_t Device::RpdoGet<uint16_t>(uint8_t, uint16_t, uint8_t) const;
1495 template uint16_t Device::RpdoGet<uint16_t>(uint8_t, uint16_t, uint8_t,
1496  ::std::error_code&) const noexcept;
1497 template uint16_t Device::TpdoGet<uint16_t>(uint8_t, uint16_t, uint8_t) const;
1498 template uint16_t Device::TpdoGet<uint16_t>(uint8_t, uint16_t, uint8_t,
1499  ::std::error_code&) const noexcept;
1500 template void Device::TpdoSet<uint16_t>(uint8_t, uint16_t, uint8_t, uint16_t);
1501 template void Device::TpdoSet<uint16_t>(uint8_t, uint16_t, uint8_t, uint16_t,
1502  ::std::error_code&) noexcept;
1503 
1504 // UNSIGNED32
1505 template uint32_t Device::RpdoGet<uint32_t>(uint8_t, uint16_t, uint8_t) const;
1506 template uint32_t Device::RpdoGet<uint32_t>(uint8_t, uint16_t, uint8_t,
1507  ::std::error_code&) const noexcept;
1508 template uint32_t Device::TpdoGet<uint32_t>(uint8_t, uint16_t, uint8_t) const;
1509 template uint32_t Device::TpdoGet<uint32_t>(uint8_t, uint16_t, uint8_t,
1510  ::std::error_code&) const noexcept;
1511 template void Device::TpdoSet<uint32_t>(uint8_t, uint16_t, uint8_t, uint32_t);
1512 template void Device::TpdoSet<uint32_t>(uint8_t, uint16_t, uint8_t, uint32_t,
1513  ::std::error_code&) noexcept;
1514 
1515 // REAL32
1516 template float Device::RpdoGet<float>(uint8_t, uint16_t, uint8_t) const;
1517 template float Device::RpdoGet<float>(uint8_t, uint16_t, uint8_t,
1518  ::std::error_code&) const noexcept;
1519 template float Device::TpdoGet<float>(uint8_t, uint16_t, uint8_t) const;
1520 template float Device::TpdoGet<float>(uint8_t, uint16_t, uint8_t,
1521  ::std::error_code&) const noexcept;
1522 template void Device::TpdoSet<float>(uint8_t, uint16_t, uint8_t, float);
1523 template void Device::TpdoSet<float>(uint8_t, uint16_t, uint8_t, float,
1524  ::std::error_code&) noexcept;
1525 
1526 // VISIBLE_STRING
1527 // OCTET_STRING
1528 // UNICODE_STRING
1529 // TIME_OF_DAY
1530 // TIME_DIFFERENCE
1531 // DOMAIN
1532 // INTEGER24
1533 
1534 // REAL64
1535 template double Device::RpdoGet<double>(uint8_t, uint16_t, uint8_t) const;
1536 template double Device::RpdoGet<double>(uint8_t, uint16_t, uint8_t,
1537  ::std::error_code&) const noexcept;
1538 template double Device::TpdoGet<double>(uint8_t, uint16_t, uint8_t) const;
1539 template double Device::TpdoGet<double>(uint8_t, uint16_t, uint8_t,
1540  ::std::error_code&) const noexcept;
1541 template void Device::TpdoSet<double>(uint8_t, uint16_t, uint8_t, double);
1542 template void Device::TpdoSet<double>(uint8_t, uint16_t, uint8_t, double,
1543  ::std::error_code&) noexcept;
1544 
1545 // INTEGER40
1546 // INTEGER48
1547 // INTEGER56
1548 
1549 // INTEGER64
1550 template int64_t Device::RpdoGet<int64_t>(uint8_t, uint16_t, uint8_t) const;
1551 template int64_t Device::RpdoGet<int64_t>(uint8_t, uint16_t, uint8_t,
1552  ::std::error_code&) const noexcept;
1553 template int64_t Device::TpdoGet<int64_t>(uint8_t, uint16_t, uint8_t) const;
1554 template int64_t Device::TpdoGet<int64_t>(uint8_t, uint16_t, uint8_t,
1555  ::std::error_code&) const noexcept;
1556 template void Device::TpdoSet<int64_t>(uint8_t, uint16_t, uint8_t, int64_t);
1557 template void Device::TpdoSet<int64_t>(uint8_t, uint16_t, uint8_t, int64_t,
1558  ::std::error_code&) noexcept;
1559 
1560 // UNSIGNED24
1561 // UNSIGNED40
1562 // UNSIGNED48
1563 // UNSIGNED56
1564 
1565 // UNSIGNED64
1566 template uint64_t Device::RpdoGet<uint64_t>(uint8_t, uint16_t, uint8_t) const;
1567 template uint64_t Device::RpdoGet<uint64_t>(uint8_t, uint16_t, uint8_t,
1568  ::std::error_code&) const noexcept;
1569 template uint64_t Device::TpdoGet<uint64_t>(uint8_t, uint16_t, uint8_t) const;
1570 template uint64_t Device::TpdoGet<uint64_t>(uint8_t, uint16_t, uint8_t,
1571  ::std::error_code&) const noexcept;
1572 template void Device::TpdoSet<uint64_t>(uint8_t, uint16_t, uint8_t, uint64_t);
1573 template void Device::TpdoSet<uint64_t>(uint8_t, uint16_t, uint8_t, uint64_t,
1574  ::std::error_code&) noexcept;
1575 
1576 #endif // DOXYGEN_SHOULD_SKIP_THIS
1577 
1578 void
1579 Device::TpdoSetEvent(uint8_t id, uint16_t idx, uint8_t subidx) {
1580  ::std::error_code ec;
1581  TpdoSetEvent(id, idx, subidx, ec);
1582  if (ec) throw_sdo_error(id, idx, subidx, ec, "TpdoSetEvent");
1583 }
1584 
1585 void
1586 Device::TpdoSetEvent(uint8_t id, uint16_t idx, uint8_t subidx,
1587  ::std::error_code& ec) noexcept {
1588  ec.clear();
1589  ::std::tie(idx, subidx) = impl_->TpdoMapping(id, idx, subidx, ec);
1590  if (!ec) SetEvent(idx, subidx, ec);
1591 }
1592 
1593 void
1595 #if !LELY_NO_CO_RPDO
1596  impl_->rpdo_mapping.clear();
1597 
1598  // Loop over all RPDOs.
1599  co_obj_t* obj_1400 = nullptr;
1600  for (int i = 0; !obj_1400 && i < 512; i++)
1601  obj_1400 = co_dev_find_obj(dev(), 0x1400 + i);
1602  for (; obj_1400; obj_1400 = co_obj_next(obj_1400)) {
1603  int i = co_obj_get_idx(obj_1400) - 0x1400;
1604  if (i >= 512) break;
1605  // Skip invalid PDOs.
1606  auto cobid = co_obj_get_val_u32(obj_1400, 1);
1607  if (cobid & CO_PDO_COBID_VALID) continue;
1608  // Obtain the remote node-ID.
1609  uint8_t id = 0;
1610  auto obj_5800 = co_dev_find_obj(dev(), 0x5800 + i);
1611  if (obj_5800) {
1612  id = co_obj_get_val_u32(obj_5800, 0) & 0xff;
1613  } else {
1614  // Obtain the node-ID from the predefined connection, if possible.
1615  if (cobid & CO_PDO_COBID_FRAME) continue;
1616  switch (cobid & 0x780) {
1617  case 0x180:
1618  case 0x280:
1619  case 0x380:
1620  case 0x480:
1621  id = cobid & 0x7f;
1622  break;
1623  default:
1624  continue;
1625  }
1626  }
1627  // Skip invalid node-IDs.
1628  if (!id || id > CO_NUM_NODES) continue;
1629  // Obtain the local RPDO mapping.
1630  auto obj_1600 = co_dev_find_obj(dev(), 0x1600 + i);
1631  if (!obj_1600) continue;
1632  // Obtain the remote TPDO mapping.
1633  auto obj_5a00 = co_dev_find_obj(dev(), 0x5a00 + i);
1634  if (!obj_5a00) continue;
1635  // Check if the number of mapped objects is the same.
1636  auto n = co_obj_get_val_u8(obj_1600, 0);
1637  if (!n || n > CO_PDO_NUM_MAPS) continue;
1638  if (n != co_obj_get_val_u8(obj_5a00, 0)) continue;
1639  for (int j = 1; j <= n; j++) {
1640  auto rmap = co_obj_get_val_u32(obj_1600, j);
1641  auto tmap = co_obj_get_val_u32(obj_5a00, j);
1642  // Ignore empty mapping entries.
1643  if (!rmap && !tmap) continue;
1644  // Check if the mapped objects have the same length.
1645  if ((rmap & 0xff) != (tmap & 0xff)) break;
1646  rmap >>= 8;
1647  tmap >>= 8;
1648  // Skip dummy-mapped objects.
1649  if (co_type_is_basic((rmap >> 8) & 0xffff)) continue;
1650  tmap |= static_cast<uint32_t>(id) << 24;
1651  impl_->rpdo_mapping[tmap] = rmap;
1652  // Store the reverse mapping for OnRpdoWrite().
1653  impl_->rpdo_mapping[rmap] = tmap;
1654  }
1655  }
1656 
1657 #if !LELY_NO_CO_MPDO
1658  // Check if at least one RPDO is a SAM-MPDO consumer.
1659  bool has_sam_mpdo = false;
1660  // Loop over all RPDOs.
1661  obj_1400 = nullptr;
1662  for (int i = 0; !obj_1400 && i < CO_NUM_PDOS; i++)
1663  obj_1400 = co_dev_find_obj(dev(), 0x1400 + i);
1664  for (; !has_sam_mpdo && obj_1400; obj_1400 = co_obj_next(obj_1400)) {
1665  int i = co_obj_get_idx(obj_1400) - 0x1400;
1666  if (i >= CO_NUM_PDOS) break;
1667  // Skip invalid PDOs.
1668  if (co_obj_get_val_u32(obj_1400, 1) & CO_PDO_COBID_VALID) continue;
1669  // SAM-MPDOs MUST be event-driven.
1670  if (co_obj_get_val_u8(obj_1400, 2) < 0xfe) continue;
1671  // Check if the PDO is a SAM-MPDO.
1672  if (co_dev_get_val_u8(dev(), 0x1600 + i, 0) != CO_PDO_MAP_SAM_MPDO)
1673  continue;
1674  has_sam_mpdo = true;
1675  }
1676 
1677  if (has_sam_mpdo) {
1678  // Loop over the object dispatching list (object 1FD0..1FFF).
1679  co_obj_t* obj_1fd0 = nullptr;
1680  for (int i = 0; !obj_1fd0 && i < 48; i++)
1681  obj_1fd0 = co_dev_find_obj(dev(), 0x1fd0 + i);
1682  for (; obj_1fd0; obj_1fd0 = co_obj_next(obj_1fd0)) {
1683  if (co_obj_get_idx(obj_1fd0) > 0x1fff) break;
1684  auto n = co_obj_get_val_u8(obj_1fd0, 0);
1685  if (!n) continue;
1686  auto sub = co_sub_next(co_obj_first_sub(obj_1fd0));
1687  for (; sub && co_sub_get_subidx(sub) <= n; sub = co_sub_next(sub)) {
1688  auto val = co_sub_get_val_u64(sub);
1689  if (!val) continue;
1690  // Extract the mapping.
1691  uint32_t rmap = (val << 8) >> 40;
1692  uint32_t tmap = ror32(val, 8);
1693  // Skip dummy-mapped objects.
1694  if (co_type_is_basic((rmap >> 8) & 0xffff)) continue;
1695  // Extract the block of sub-indices.
1696  uint8_t min = (val >> 8) & 0xff;
1697  uint8_t max = min;
1698  uint8_t blk = (val >> 56) & 0xff;
1699  if (blk) max += ::std::min(blk - 1, 0xff - min);
1700  // Store the (reverse) mapping for each of the sub-indices.
1701  for (int j = 0; j <= max - min; j++) {
1702  // Ignore out-of-range sub-indices.
1703  if (static_cast<uint8_t>(j) > 0xff - (rmap & 0xff)) break;
1704  impl_->rpdo_mapping[tmap + j] = rmap + j;
1705  }
1706  }
1707  }
1708  }
1709 #endif // !LELY_NO_CO_MPDO
1710 #endif // !LELY_NO_CO_RPDO
1711 }
1712 
1713 void
1715 #if !LELY_NO_CO_TPDO
1716  impl_->tpdo_mapping.clear();
1717 
1718  // Loop over all TPDOs.
1719  co_obj_t* obj_1800 = nullptr;
1720  for (int i = 0; !obj_1800 && i < 512; i++)
1721  obj_1800 = co_dev_find_obj(dev(), 0x1800 + i);
1722  for (; obj_1800; obj_1800 = co_obj_next(obj_1800)) {
1723  int i = co_obj_get_idx(obj_1800) - 0x1800;
1724  if (i >= 512) break;
1725  // Skip invalid PDOs.
1726  auto cobid = co_obj_get_val_u32(obj_1800, 1);
1727  if (cobid & CO_PDO_COBID_VALID) continue;
1728  // Obtain the remote node-ID.
1729  uint8_t id = 0;
1730  auto obj_5c00 = co_dev_find_obj(dev(), 0x5c00 + i);
1731  if (obj_5c00) {
1732  id = co_obj_get_val_u32(obj_5c00, 0) & 0xff;
1733  } else {
1734  // Obtain the node-ID from the predefined connection, if possible.
1735  if (cobid & CO_PDO_COBID_FRAME) continue;
1736  switch (cobid & 0x780) {
1737  case 0x200:
1738  case 0x300:
1739  case 0x400:
1740  case 0x500:
1741  id = cobid & 0x7f;
1742  break;
1743  default:
1744  continue;
1745  }
1746  }
1747  // Skip invalid node-IDs.
1748  if (!id || id > CO_NUM_NODES) continue;
1749  // Obtain the local TPDO mapping.
1750  auto obj_1a00 = co_dev_find_obj(dev(), 0x1a00 + i);
1751  if (!obj_1a00) continue;
1752  // Obtain the remote RPDO mapping.
1753  auto obj_5e00 = co_dev_find_obj(dev(), 0x5e00 + i);
1754  if (!obj_5e00) continue;
1755  // Check if the number of mapped objects is the same.
1756  auto n = co_obj_get_val_u8(obj_1a00, 0);
1757  if (!n || n > CO_PDO_NUM_MAPS) continue;
1758  if (n != co_obj_get_val_u8(obj_5e00, 0)) continue;
1759  for (int j = 1; j <= n; j++) {
1760  auto tmap = co_obj_get_val_u32(obj_1a00, j);
1761  auto rmap = co_obj_get_val_u32(obj_5e00, j);
1762  // Ignore empty mapping entries.
1763  if (!rmap && !tmap) continue;
1764  // Check if the mapped objects have the same length.
1765  if ((tmap & 0xff) != (rmap & 0xff)) break;
1766  tmap >>= 8;
1767  rmap >>= 8;
1768  rmap |= static_cast<uint32_t>(id) << 24;
1769  impl_->tpdo_mapping[rmap] = tmap;
1770  }
1771  }
1772 #endif // !LELY_NO_CO_TPDO
1773 }
1774 
1775 void
1776 Device::RpdoWrite(uint8_t id, uint16_t idx, uint8_t subidx) {
1777 #if LELY_NO_CO_RPDO
1778  (void)id;
1779  (void)idx;
1780  (void)subidx;
1781 #else
1782  OnRpdoWrite(id, idx, subidx);
1783 
1784  if (impl_->on_rpdo_write) {
1785  auto f = impl_->on_rpdo_write;
1786  util::UnlockGuard<Impl_> unlock(*impl_);
1787  f(id, idx, subidx);
1788  }
1789 #endif
1790 }
1791 
1792 #if !LELY_NO_CO_DCF
1793 Device::Impl_::Impl_(Device* self_, const ::std::string& dcf_txt,
1794  const ::std::string& dcf_bin, uint8_t id,
1795  util::BasicLockable* mutex_)
1796  : self(self_),
1797  mutex(mutex_),
1798  dev(co_dev_create_from_dcf_file(dcf_txt.c_str())) {
1799  if (!dcf_bin.empty() &&
1800  co_dev_read_dcf_file(dev.get(), nullptr, nullptr, dcf_bin.c_str()) == -1)
1801  util::throw_errc("Device");
1802 
1803  if (id != 0xff && co_dev_set_id(dev.get(), id) == -1)
1804  util::throw_errc("Device");
1805 
1806  // Register a notification function for all objects in the object dictionary
1807  // in case of write (SDO upload) access.
1808  for (auto obj = co_dev_first_obj(dev.get()); obj; obj = co_obj_next(obj)) {
1809  // Skip data types and the communication profile area.
1810  if (co_obj_get_idx(obj) < 0x2000) continue;
1811  // Skip reserved objects.
1812  if (co_obj_get_idx(obj) >= 0xC000) break;
1814  obj,
1815  [](co_sub_t* sub, co_sdo_req* req, void* data) -> uint32_t {
1816  // Implement the default behavior, but do not issue a notification for
1817  // incomplete or failed writes.
1818  uint32_t ac = 0;
1819  if (co_sub_on_dn(sub, req, &ac) == -1 || ac) return ac;
1820  auto impl_ = static_cast<Impl_*>(data);
1821  impl_->OnWrite(co_obj_get_idx(co_sub_get_obj(sub)),
1822  co_sub_get_subidx(sub));
1823  return 0;
1824  },
1825  static_cast<void*>(this));
1826  }
1827 }
1828 #endif // !LELY_NO_CO_DCF
1829 
1830 void
1831 Device::Impl_::OnWrite(uint16_t idx, uint8_t subidx) {
1832  self->OnWrite(idx, subidx);
1833 
1834  if (on_write) {
1835  auto f = on_write;
1836  util::UnlockGuard<Impl_> unlock(*this);
1837  f(idx, subidx);
1838  }
1839 
1840 #if !LELY_NO_CO_RPDO
1841  uint8_t id = 0;
1842  ::std::error_code ec;
1843  ::std::tie(id, idx, subidx) = RpdoMapping(idx, subidx, ec);
1844  if (!ec) self->RpdoWrite(id, idx, subidx);
1845 #endif
1846 }
1847 
1848 } // namespace canopen
1849 
1850 } // namespace lely
This header file is part of the utilities library; it contains the bit function definitions.
uint_least32_t ror32(uint_least32_t x, unsigned int n)
Rotates the 32-bit unsigned integer x right by n bits.
Definition: bits.h:742
The CANopen device description.
Definition: device.hpp:45
void RpdoWrite(uint8_t id, uint16_t idx, uint8_t subidx)
Invokes OnRpdoWrite() as if a value was written to an RPDO-mapped object in the local object dictiona...
Definition: device.cpp:1776
uint8_t netid() const noexcept
Returns the network-ID.
Definition: device.cpp:186
void SetUploadFile(uint16_t idx, uint8_t subidx, const char *filename)
Sets the value of the UploadFile attribute of a sub-object, if present.
Definition: device.cpp:1262
typename ::std::enable_if< is_canopen_basic< T >::value, T >::type RpdoGet(uint8_t id, uint16_t idx, uint8_t subidx) const
Reads the value of a sub-object in a remote object dictionary by reading the corresponding PDO-mapped...
void TpdoWriteEvent(uint8_t id, uint16_t idx, uint8_t subidx)
Triggers the transmission of every event-driven, asynchronous Transmit-PDO which is mapped into the s...
Definition: device.cpp:796
typename ::std::enable_if< is_canopen_basic< T >::value, T >::type TpdoRead(uint8_t id, uint16_t idx, uint8_t subidx) const
Submits an SDO upload request to a TPDO-mapped sub-object in the local object dictionary,...
void WriteDcf(const uint8_t *begin, const uint8_t *end)
Submits a series of SDO download requests to the local object dictionary.
Definition: device.cpp:522
void UpdateTpdoMapping()
Updates the mapping from remote RPDO-mapped sub-objects to local TPDO-mapped sub-objects.
Definition: device.cpp:1714
typename ::std::enable_if< is_canopen< T >::value, T >::type Read(uint16_t idx, uint8_t subidx) const
Submits an SDO upload request to the local object dictionary.
typename ::std::enable_if< is_canopen_basic< T >::value >::type TpdoWrite(uint8_t id, uint16_t idx, uint8_t subidx, T value)
Writes a value to a sub-object in a remote object dictionary by submitting an SDO download request to...
typename ::std::enable_if< is_canopen_basic< T >::value, T >::type TpdoGet(uint8_t id, uint16_t idx, uint8_t subidx) const
Reads the value of a TPDO-mapped sub-object in the local object dictionary that will be written to an...
void SetDownloadFile(uint16_t idx, uint8_t subidx, const char *filename)
Sets the value of the DownloadFile attribute of a sub-object, if present.
Definition: device.cpp:1320
const ::std::type_info & Type(uint16_t idx, uint8_t subidx) const
Returns the type of a sub-object.
Definition: device.cpp:833
void UpdateRpdoMapping()
Updates the mapping from remote TPDO-mapped sub-objects to local RPDO-mapped sub-objects.
Definition: device.cpp:1594
typename ::std::enable_if< is_canopen< T >::value, T >::type Get(uint16_t idx, uint8_t subidx) const
Reads the value of a sub-object.
typename ::std::enable_if< is_canopen< T >::value >::type Set(uint16_t idx, uint8_t subidx, const T &value)
Writes a CANopen value to a sub-object.
typename ::std::enable_if< is_canopen_basic< T >::value >::type TpdoSet(uint8_t id, uint16_t idx, uint8_t subidx, T value)
Writes a value to a sub-object in a remote object dictionary by writing to the corresponding PDO-mapp...
void SetEvent(uint16_t idx, uint8_t subidx)
Checks if the specified sub-object in the local object dictionary can be mapped into a PDO and,...
Definition: device.cpp:1353
void WriteEvent(uint16_t idx, uint8_t subidx)
Checks if the specified sub-object in the local object dictionary can be mapped into a PDO and,...
Definition: device.cpp:575
typename ::std::enable_if< is_canopen< T >::value >::type Write(uint16_t idx, uint8_t subidx, const T &value)
Submits an SDO download request to the local object dictionary.
Device(const ::std::string &dcf_txt, const ::std::string &dcf_bin="", uint8_t id=0xff, util::BasicLockable *mutex=nullptr)
Creates a new CANopen device description.
Definition: device.cpp:178
const char * GetDownloadFile(uint16_t idx, uint8_t subidx) const
Returns the value of the DownloadFile attribute of a sub-object, if present.
Definition: device.cpp:1293
void TpdoSetEvent(uint8_t id, uint16_t idx, uint8_t subidx)
Triggers the transmission of every event-driven, asynchronous Transmit-PDO which is mapped into the s...
Definition: device.cpp:1579
__co_dev * dev() const noexcept
Returns a pointer to the internal CANopen device from <lely/co/dev.hpp>.
Definition: device.cpp:828
uint8_t id() const noexcept
Returns the node-ID.
Definition: device.cpp:193
const char * GetUploadFile(uint16_t idx, uint8_t subidx) const
Returns the value of the UploadFile attribute of a sub-object, if present.
Definition: device.cpp:1235
typename ::std::enable_if< is_canopen_basic< T >::value, T >::type RpdoRead(uint8_t id, uint16_t idx, uint8_t subidx) const
Reads the value of a sub-object in a remote object dictionary by submitting an SDO upload request to ...
An abstract interface conforming to the BasicLockable concept.
Definition: mutex.hpp:34
A mutex wrapper that provides a convenient RAII-style mechanism for releasing a mutex for the duratio...
Definition: mutex.hpp:57
This header file is part of the CANopen library; it contains the device description declarations.
void co_dev_sam_mpdo_event(co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx)
Checks if the specified sub-object in the object dictionary of a CANopen device can be mapped into a ...
Definition: dev.c:939
co_unsigned8_t co_dev_get_id(const co_dev_t *dev)
Returns the node-ID of a CANopen device.
Definition: dev.c:197
int co_dev_set_id(co_dev_t *dev, co_unsigned8_t id)
Sets the node-ID of a CANopen device.
Definition: dev.c:205
#define CO_NUM_NODES
The maximum number of nodes in a CANopen network.
Definition: dev.h:56
co_obj_t * co_dev_first_obj(const co_dev_t *dev)
Finds the first object (with the lowest index) in the object dictionary of a CANopen device.
Definition: dev.c:297
co_unsigned8_t co_dev_get_netid(const co_dev_t *dev)
Returns the network-ID of a CANopen device.
Definition: dev.c:174
void co_dev_destroy(co_dev_t *dev)
Destroys a CANopen device, including all objects in its object dictionary.
Definition: dev.c:163
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
void co_dev_tpdo_event(co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx)
Checks if the specified sub-object in the object dictionary of a CANopen device can be mapped into a ...
Definition: dev.c:867
int co_dev_read_dcf_file(co_dev_t *dev, co_unsigned16_t *pmin, co_unsigned16_t *pmax, const char *filename)
Reads the values of a range of objects from a file, in the concise DCF format, and stores them in the...
Definition: dev.c:750
This is the internal header file of the C++ CANopen application library.
This header file is part of the CANopen library; it contains the Client-SDO declarations.
int co_dev_up_req(const co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx, co_csdo_up_con_t *con, void *data)
Submits an upload request to a local device.
Definition: csdo.c:812
int co_dev_dn_val_req(co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx, co_unsigned16_t type, const void *val, co_csdo_dn_con_t *con, void *data)
Submits a download request to a local device.
Definition: csdo.c:701
int co_dev_dn_req(co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx, const void *ptr, size_t n, co_csdo_dn_con_t *con, void *data)
Submits a download request to a local device.
Definition: csdo.c:664
int co_dev_dn_dcf_req(co_dev_t *dev, const uint_least8_t *begin, const uint_least8_t *end, co_csdo_dn_con_t *con, void *data)
Submits a series of download requests to a local device.
Definition: csdo.c:739
This header file is part of the CANopen library; it contains the Electronic Data Sheet (EDS) and Devi...
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
This header file is part of the C++ CANopen application library; it contains the CANopen device descr...
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:932
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:944
This header file is part of the utilities library; it contains C++ convenience functions for creating...
constexpr bool is_canopen_same(uint16_t t1, uint16_t t2) noexcept
Returns true if the CANopen data types t1 and t2 map to the same C++ type, and false if not.
SdoErrc
The SDO abort codes.
Definition: sdo_error.hpp:42
@ NO_SUB
Sub-index does not exist.
@ TYPE_LEN
Data type does not match, length of service parameter does not match.
@ NO_OBJ
Object does not exist in the object dictionary.
@ NO_PDO
Object cannot be mapped to the PDO.
SdoErrc sdo_errc(::std::error_code ec)
Returns the SDO abort code corresponding to an error code.
Definition: sdo_error.cpp:180
void throw_sdo_error(uint8_t id, uint16_t idx, uint8_t subidx, ::std::error_code ec)
Throws a lely::canopen::SdoError with the specified attributes if ec is an SDO error (ec....
Definition: sdo_error.hpp:219
::std::error_code make_error_code(SdoErrc e) noexcept
Creates an error code corresponding to an SDO abort code.
Definition: sdo_error.cpp:170
This header file is part of the CANopen library; it contains the object dictionary declarations.
co_unsigned8_t co_sub_get_subidx(const co_sub_t *sub)
Returns the sub-index of a CANopen sub-object.
Definition: obj.c:559
const void * co_sub_get_val(const co_sub_t *sub)
Returns a pointer to the current value of a CANopen sub-object.
Definition: obj.c:712
int co_sub_set_download_file(co_sub_t *sub, const char *filename)
Sets the value of the DownloadFile attribute of a CANopen sub-object.
Definition: obj.c:875
co_sub_t * co_sub_next(const co_sub_t *sub)
Finds the next sub-object in a CANopen object.
Definition: obj.c:542
co_unsigned16_t co_obj_get_idx(const co_obj_t *obj)
Returns the index of a CANopen object.
Definition: obj.c:164
int co_sub_set_upload_file(co_sub_t *sub, const char *filename)
Sets the value of the UploadFile attribute of a CANopen sub-object.
Definition: obj.c:848
size_t co_sub_set_val(co_sub_t *sub, const void *ptr, size_t n)
Sets the current value of a CANopen sub-object.
Definition: obj.c:718
co_obj_t * co_obj_next(const co_obj_t *obj)
Finds the next object in the object dictionary of a CANopen device.
Definition: obj.c:147
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:240
int co_sub_on_dn(co_sub_t *sub, struct co_sdo_req *req, co_unsigned32_t *pac)
Implements the default behavior when a download indication is received by a CANopen sub-object.
Definition: obj.c:912
co_sub_t * co_obj_first_sub(const co_obj_t *obj)
Finds the first sub-object (with the lowest sub-index) in a CANopen object.
Definition: obj.c:249
void co_obj_set_dn_ind(co_obj_t *obj, co_sub_dn_ind_t *ind, void *data)
Sets the download indication function for a CANopen object.
Definition: obj.c:389
const char * co_sub_get_download_file(const co_sub_t *sub)
Returns a pointer to the value of the DownloadFile attribute of a CANopen sub-object,...
Definition: obj.c:863
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:551
const char * co_sub_get_upload_file(const co_sub_t *sub)
Returns a pointer to the value of the UploadFile attribute of a CANopen sub-object,...
Definition: obj.c:836
co_unsigned16_t co_sub_get_type(const co_sub_t *sub)
Returns the data type of a CANopen sub-object.
Definition: obj.c:603
This header file is part of the CANopen library; it contains the Process Data Object (PDO) declaratio...
#define CO_PDO_NUM_MAPS
The maximum number of mapped application objects in a single PDO.
Definition: pdo.h:34
#define CO_PDO_COBID_FRAME
The bit in the PDO COB-ID specifying whether to use an 11-bit (0) or 29-bit (1) CAN-ID.
Definition: pdo.h:46
#define CO_PDO_COBID_VALID
The bit in the PDO COB-ID specifying whether the PDO exists and is valid.
Definition: pdo.h:37
#define CO_NUM_PDOS
The maximum number of Receive/Transmit-PDOs.
Definition: pdo.h:28
#define CO_PDO_MAP_SAM_MPDO
The value of sub-index 0 of the PDO mapping parameter record indicating a a source address mode multi...
Definition: pdo.h:52
A CANopen Client-SDO.
Definition: csdo.c:71
A CANopen device.
Definition: dev.h:30
A CANopen object.
Definition: obj.h:31
A CANopen sub-object.
Definition: obj.h:53
A CANopen SDO upload/download request.
Definition: sdo.h:181
The internal implementation of the CANopen device description.
Definition: device.cpp:56
void lock() override
Blocks until a lock can be obtained for the current execution agent (thread, process,...
Definition: device.cpp:71
void unlock() override
Releases the lock held by the execution agent. Throws no exceptions.
Definition: device.cpp:76
A class template mapping CANopen types to C and C++ types.
Definition: type_traits.hpp:98
#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_VISIBLE_STRING
The data type (and object index) of an array of visible characters.
Definition: type.h:56
#define CO_DEFTYPE_UNSIGNED64
The data type (and object index) of a 64-bit unsigned integer.
Definition: type.h:110
#define CO_DEFTYPE_INTEGER8
The data type (and object index) of an 8-bit signed integer.
Definition: type.h:35
#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_REAL64
The data type (and object index) of a 64-bit IEEE-754 floating-point number.
Definition: type.h:83
#define CO_DEFTYPE_BOOLEAN
The data type (and object index) of a boolean truth value.
Definition: type.h:32
#define CO_DEFTYPE_INTEGER32
The data type (and object index) of a 32-bit signed integer.
Definition: type.h:41
#define CO_DEFTYPE_INTEGER64
The data type (and object index) of a 64-bit signed integer.
Definition: type.h:95
#define CO_DEFTYPE_UNSIGNED32
The data type (and object index) of a 32-bit unsigned integer.
Definition: type.h:50
#define CO_DEFTYPE_INTEGER16
The data type (and object index) of a 16-bit signed integer.
Definition: type.h:38
#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
#define CO_DEFTYPE_REAL32
The data type (and object index) of a 32-bit IEEE-754 floating-point number.
Definition: type.h:53
This header file is part of the CANopen library; it contains the CANopen value declarations.
const void * co_val_addressof(co_unsigned16_t type, const void *val)
Returns the address of the first byte in a value of the specified data type.
Definition: val.c:260
size_t co_val_sizeof(co_unsigned16_t type, const void *val)
Returns the size (in bytes) of a value of the specified data type.
Definition: val.c:269
size_t co_val_read_file(co_unsigned16_t type, void *val, const char *filename)
Reads a value of the specified data type from a file.
Definition: val.c:671
void co_val_fini(co_unsigned16_t type, void *val)
Finalizes a value of the specified data type.
Definition: val.c:249