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
51namespace lely {
52
53namespace canopen {
54
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
81 netid() const noexcept {
82 return co_dev_get_netid(dev.get());
83 }
84
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
178Device::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
183Device::~Device() = default;
184
187 ::std::lock_guard<Impl_> lock(*impl_);
188
189 return impl_->netid();
190}
191
194 ::std::lock_guard<Impl_> lock(*impl_);
195
196 return impl_->id();
197}
198
199namespace {
200
201void
202OnDnCon(co_csdo_t*, uint16_t, uint8_t, uint32_t ac, void* data) noexcept {
203 *static_cast<uint32_t*>(data) = ac;
204}
205
206template <class T>
207void
208OnUpCon(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
229template <class T>
230typename ::std::enable_if<is_canopen<T>::value, T>::type
231Device::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
238template <class T>
239typename ::std::enable_if<is_canopen<T>::value, T>::type
240Device::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
260template <class T>
261typename ::std::enable_if<is_canopen<T>::value>::type
262Device::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
268template <class T>
269typename ::std::enable_if<is_canopen<T>::value>::type
270Device::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
297template <>
298void
299Device::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
304template <>
305void
306Device::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
314template bool Device::Read<bool>(uint16_t, uint8_t) const;
315template bool Device::Read<bool>(uint16_t, uint8_t, ::std::error_code&) const;
316template void Device::Write<bool>(uint16_t, uint8_t, const bool&);
317template void Device::Write<bool>(uint16_t, uint8_t, const bool&,
318 ::std::error_code&);
319
320// INTEGER8
321template int8_t Device::Read<int8_t>(uint16_t, uint8_t) const;
322template int8_t Device::Read<int8_t>(uint16_t, uint8_t,
323 ::std::error_code&) const;
324template void Device::Write<int8_t>(uint16_t, uint8_t, const int8_t&);
325template void Device::Write<int8_t>(uint16_t, uint8_t, const int8_t&,
326 ::std::error_code&);
327
328// INTEGER16
329template int16_t Device::Read<int16_t>(uint16_t, uint8_t) const;
330template int16_t Device::Read<int16_t>(uint16_t, uint8_t,
331 ::std::error_code&) const;
332template void Device::Write<int16_t>(uint16_t, uint8_t, const int16_t&);
333template void Device::Write<int16_t>(uint16_t, uint8_t, const int16_t&,
334 ::std::error_code&);
335
336// INTEGER32
337template int32_t Device::Read<int32_t>(uint16_t, uint8_t) const;
338template int32_t Device::Read<int32_t>(uint16_t, uint8_t,
339 ::std::error_code&) const;
340template void Device::Write<int32_t>(uint16_t, uint8_t, const int32_t&);
341template void Device::Write<int32_t>(uint16_t, uint8_t, const int32_t&,
342 ::std::error_code&);
343
344// UNSIGNED8
345template uint8_t Device::Read<uint8_t>(uint16_t, uint8_t) const;
346template uint8_t Device::Read<uint8_t>(uint16_t, uint8_t,
347 ::std::error_code&) const;
348template void Device::Write<uint8_t>(uint16_t, uint8_t, const uint8_t&);
349template void Device::Write<uint8_t>(uint16_t, uint8_t, const uint8_t&,
350 ::std::error_code&);
351
352// UNSIGNED16
353template uint16_t Device::Read<uint16_t>(uint16_t, uint8_t) const;
354template uint16_t Device::Read<uint16_t>(uint16_t, uint8_t,
355 ::std::error_code&) const;
356template void Device::Write<uint16_t>(uint16_t, uint8_t, const uint16_t&);
357template void Device::Write<uint16_t>(uint16_t, uint8_t, const uint16_t&,
358 ::std::error_code&);
359
360// UNSIGNED32
361template uint32_t Device::Read<uint32_t>(uint16_t, uint8_t) const;
362template uint32_t Device::Read<uint32_t>(uint16_t, uint8_t,
363 ::std::error_code&) const;
364template void Device::Write<uint32_t>(uint16_t, uint8_t, const uint32_t&);
365template void Device::Write<uint32_t>(uint16_t, uint8_t, const uint32_t&,
366 ::std::error_code&);
367
368// REAL32
369template float Device::Read<float>(uint16_t, uint8_t) const;
370template float Device::Read<float>(uint16_t, uint8_t, ::std::error_code&) const;
371template void Device::Write<float>(uint16_t, uint8_t, const float&);
372template void Device::Write<float>(uint16_t, uint8_t, const float&,
373 ::std::error_code&);
374
375// VISIBLE_STRING
376template ::std::string Device::Read<::std::string>(uint16_t, uint8_t) const;
377template ::std::string Device::Read<::std::string>(uint16_t, uint8_t,
378 ::std::error_code&) const;
379template 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
386template ::std::vector<uint8_t> Device::Read<::std::vector<uint8_t>>(
387 uint16_t, uint8_t) const;
388template ::std::vector<uint8_t> Device::Read<::std::vector<uint8_t>>(
389 uint16_t, uint8_t, ::std::error_code&) const;
390template 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
396template ::std::basic_string<char16_t>
397 Device::Read<::std::basic_string<char16_t>>(uint16_t, uint8_t) const;
398template ::std::basic_string<char16_t>
399Device::Read<::std::basic_string<char16_t>>(uint16_t, uint8_t,
400 ::std::error_code&) const;
401template void Device::Write<::std::basic_string<char16_t>>(
402 uint16_t, uint8_t, const ::std::basic_string<char16_t>&);
403template 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
413template double Device::Read<double>(uint16_t, uint8_t) const;
414template double Device::Read<double>(uint16_t, uint8_t,
415 ::std::error_code&) const;
416template void Device::Write<double>(uint16_t, uint8_t, const double&);
417template void Device::Write<double>(uint16_t, uint8_t, const double&,
418 ::std::error_code&);
419
420// INTEGER40
421// INTEGER48
422// INTEGER56
423
424// INTEGER64
425template int64_t Device::Read<int64_t>(uint16_t, uint8_t) const;
426template int64_t Device::Read<int64_t>(uint16_t, uint8_t,
427 ::std::error_code&) const;
428template void Device::Write<int64_t>(uint16_t, uint8_t, const int64_t&);
429template 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
438template uint64_t Device::Read<uint64_t>(uint16_t, uint8_t) const;
439template uint64_t Device::Read<uint64_t>(uint16_t, uint8_t,
440 ::std::error_code&) const;
441template void Device::Write<uint64_t>(uint16_t, uint8_t, const uint64_t&);
442template void Device::Write<uint64_t>(uint16_t, uint8_t, const uint64_t&,
443 ::std::error_code&);
444
445#endif // DOXYGEN_SHOULD_SKIP_THIS
446
447void
448Device::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
454void
455Device::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
460void
461Device::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
467void
468Device::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
495void
496Device::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
502void
503Device::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
521void
522Device::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
528void
529Device::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
547void
548Device::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
555void
556Device::WriteDcf(const char* path, ::std::error_code& ec) {
557 int errsv = get_errc();
558 set_errc(0);
559
560 void* dom = nullptr;
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 }
570 set_errc(errsv);
571}
572#endif
573
574void
576 ::std::error_code ec;
577 WriteEvent(idx, subidx, ec);
578 if (ec) throw_sdo_error(id(), idx, subidx, ec, "WriteEvent");
579}
580
581void
583 ::std::error_code& ec) noexcept {
584 ::std::lock_guard<Impl_> lock(*impl_);
585
586 SetEvent(idx, subidx, ec);
587}
588
589template <class T>
590typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
591Device::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
598template <class T>
599typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
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
611template <class T>
612typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
613Device::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
620template <class T>
621typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
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
633template <class T>
634typename ::std::enable_if<is_canopen_basic<T>::value>::type
635Device::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
641template <class T>
642typename ::std::enable_if<is_canopen_basic<T>::value>::type
643Device::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
656template bool Device::RpdoRead<bool>(uint8_t, uint16_t, uint8_t) const;
657template bool Device::RpdoRead<bool>(uint8_t, uint16_t, uint8_t,
658 ::std::error_code&) const;
659template bool Device::TpdoRead<bool>(uint8_t, uint16_t, uint8_t) const;
660template bool Device::TpdoRead<bool>(uint8_t, uint16_t, uint8_t,
661 ::std::error_code&) const;
662template void Device::TpdoWrite<bool>(uint8_t, uint16_t, uint8_t, bool);
663template void Device::TpdoWrite<bool>(uint8_t, uint16_t, uint8_t, bool,
664 ::std::error_code&);
665
666// INTEGER8
667template int8_t Device::RpdoRead<int8_t>(uint8_t, uint16_t, uint8_t) const;
668template int8_t Device::RpdoRead<int8_t>(uint8_t, uint16_t, uint8_t,
669 ::std::error_code&) const;
670template int8_t Device::TpdoRead<int8_t>(uint8_t, uint16_t, uint8_t) const;
671template int8_t Device::TpdoRead<int8_t>(uint8_t, uint16_t, uint8_t,
672 ::std::error_code&) const;
673template void Device::TpdoWrite<int8_t>(uint8_t, uint16_t, uint8_t, int8_t);
674template void Device::TpdoWrite<int8_t>(uint8_t, uint16_t, uint8_t, int8_t,
675 ::std::error_code&);
676
677// INTEGER16
678template int16_t Device::RpdoRead<int16_t>(uint8_t, uint16_t, uint8_t) const;
679template int16_t Device::RpdoRead<int16_t>(uint8_t, uint16_t, uint8_t,
680 ::std::error_code&) const;
681template int16_t Device::TpdoRead<int16_t>(uint8_t, uint16_t, uint8_t) const;
682template int16_t Device::TpdoRead<int16_t>(uint8_t, uint16_t, uint8_t,
683 ::std::error_code&) const;
684template void Device::TpdoWrite<int16_t>(uint8_t, uint16_t, uint8_t, int16_t);
685template void Device::TpdoWrite<int16_t>(uint8_t, uint16_t, uint8_t, int16_t,
686 ::std::error_code&);
687
688// INTEGER32
689template int32_t Device::RpdoRead<int32_t>(uint8_t, uint16_t, uint8_t) const;
690template int32_t Device::RpdoRead<int32_t>(uint8_t, uint16_t, uint8_t,
691 ::std::error_code&) const;
692template int32_t Device::TpdoRead<int32_t>(uint8_t, uint16_t, uint8_t) const;
693template int32_t Device::TpdoRead<int32_t>(uint8_t, uint16_t, uint8_t,
694 ::std::error_code&) const;
695template void Device::TpdoWrite<int32_t>(uint8_t, uint16_t, uint8_t, int32_t);
696template void Device::TpdoWrite<int32_t>(uint8_t, uint16_t, uint8_t, int32_t,
697 ::std::error_code&);
698
699// UNSIGNED8
700template uint8_t Device::RpdoRead<uint8_t>(uint8_t, uint16_t, uint8_t) const;
701template uint8_t Device::RpdoRead<uint8_t>(uint8_t, uint16_t, uint8_t,
702 ::std::error_code&) const;
703template uint8_t Device::TpdoRead<uint8_t>(uint8_t, uint16_t, uint8_t) const;
704template uint8_t Device::TpdoRead<uint8_t>(uint8_t, uint16_t, uint8_t,
705 ::std::error_code&) const;
706template void Device::TpdoWrite<uint8_t>(uint8_t, uint16_t, uint8_t, uint8_t);
707template void Device::TpdoWrite<uint8_t>(uint8_t, uint16_t, uint8_t, uint8_t,
708 ::std::error_code&);
709
710// UNSIGNED16
711template uint16_t Device::RpdoRead<uint16_t>(uint8_t, uint16_t, uint8_t) const;
712template uint16_t Device::RpdoRead<uint16_t>(uint8_t, uint16_t, uint8_t,
713 ::std::error_code&) const;
714template uint16_t Device::TpdoRead<uint16_t>(uint8_t, uint16_t, uint8_t) const;
715template uint16_t Device::TpdoRead<uint16_t>(uint8_t, uint16_t, uint8_t,
716 ::std::error_code&) const;
717template void Device::TpdoWrite<uint16_t>(uint8_t, uint16_t, uint8_t, uint16_t);
718template void Device::TpdoWrite<uint16_t>(uint8_t, uint16_t, uint8_t, uint16_t,
719 ::std::error_code&);
720
721// UNSIGNED32
722template uint32_t Device::RpdoRead<uint32_t>(uint8_t, uint16_t, uint8_t) const;
723template uint32_t Device::RpdoRead<uint32_t>(uint8_t, uint16_t, uint8_t,
724 ::std::error_code&) const;
725template uint32_t Device::TpdoRead<uint32_t>(uint8_t, uint16_t, uint8_t) const;
726template uint32_t Device::TpdoRead<uint32_t>(uint8_t, uint16_t, uint8_t,
727 ::std::error_code&) const;
728template void Device::TpdoWrite<uint32_t>(uint8_t, uint16_t, uint8_t, uint32_t);
729template void Device::TpdoWrite<uint32_t>(uint8_t, uint16_t, uint8_t, uint32_t,
730 ::std::error_code&);
731
732// REAL32
733template float Device::RpdoRead<float>(uint8_t, uint16_t, uint8_t) const;
734template float Device::RpdoRead<float>(uint8_t, uint16_t, uint8_t,
735 ::std::error_code&) const;
736template float Device::TpdoRead<float>(uint8_t, uint16_t, uint8_t) const;
737template float Device::TpdoRead<float>(uint8_t, uint16_t, uint8_t,
738 ::std::error_code&) const;
739template void Device::TpdoWrite<float>(uint8_t, uint16_t, uint8_t, float);
740template 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
752template double Device::RpdoRead<double>(uint8_t, uint16_t, uint8_t) const;
753template double Device::RpdoRead<double>(uint8_t, uint16_t, uint8_t,
754 ::std::error_code&) const;
755template double Device::TpdoRead<double>(uint8_t, uint16_t, uint8_t) const;
756template double Device::TpdoRead<double>(uint8_t, uint16_t, uint8_t,
757 ::std::error_code&) const;
758template void Device::TpdoWrite<double>(uint8_t, uint16_t, uint8_t, double);
759template 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
767template int64_t Device::RpdoRead<int64_t>(uint8_t, uint16_t, uint8_t) const;
768template int64_t Device::RpdoRead<int64_t>(uint8_t, uint16_t, uint8_t,
769 ::std::error_code&) const;
770template int64_t Device::TpdoRead<int64_t>(uint8_t, uint16_t, uint8_t) const;
771template int64_t Device::TpdoRead<int64_t>(uint8_t, uint16_t, uint8_t,
772 ::std::error_code&) const;
773template void Device::TpdoWrite<int64_t>(uint8_t, uint16_t, uint8_t, int64_t);
774template 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
783template uint64_t Device::RpdoRead<uint64_t>(uint8_t, uint16_t, uint8_t) const;
784template uint64_t Device::RpdoRead<uint64_t>(uint8_t, uint16_t, uint8_t,
785 ::std::error_code&) const;
786template uint64_t Device::TpdoRead<uint64_t>(uint8_t, uint16_t, uint8_t) const;
787template uint64_t Device::TpdoRead<uint64_t>(uint8_t, uint16_t, uint8_t,
788 ::std::error_code&) const;
789template void Device::TpdoWrite<uint64_t>(uint8_t, uint16_t, uint8_t, uint64_t);
790template 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
795void
797 ::std::error_code ec;
798 TpdoWriteEvent(id, idx, subidx, ec);
799 if (ec) throw_sdo_error(id, idx, subidx, ec, "TpdoWriteEvent");
800}
801
802void
804 ::std::error_code& ec) noexcept {
805 ::std::lock_guard<Impl_> lock(*impl_);
806
807 TpdoSetEvent(id, idx, subidx, ec);
808}
809
810void
811Device::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
816void
817Device::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
829 return impl_->dev.get();
830}
831
832const ::std::type_info&
833Device::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
840const ::std::type_info&
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)) {
858 return typeid(bool);
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);
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: ...
882 return typeid(::std::vector<uint8_t>);
883 // case CO_DEFTYPE_INTEGER24: ...
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
902template <class T>
903typename ::std::enable_if<is_canopen<T>::value, T>::type
904Device::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
911template <class T>
912typename ::std::enable_if<is_canopen<T>::value, T>::type
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))) {
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
941template <class T>
942typename ::std::enable_if<is_canopen<T>::value>::type
943Device::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
949template <class T>
950typename ::std::enable_if<is_canopen<T>::value>::type
951Device::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))) {
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
988template <>
989void
990Device::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
995template <>
996void
997Device::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
1005template bool Device::Get<bool>(uint16_t, uint8_t) const;
1006template bool Device::Get<bool>(uint16_t, uint8_t,
1007 ::std::error_code&) const noexcept;
1008template void Device::Set<bool>(uint16_t, uint8_t, const bool&);
1009template void Device::Set<bool>(uint16_t, uint8_t, const bool&,
1010 ::std::error_code&) noexcept;
1011
1012// INTEGER8
1013template int8_t Device::Get<int8_t>(uint16_t, uint8_t) const;
1014template int8_t Device::Get<int8_t>(uint16_t, uint8_t,
1015 ::std::error_code&) const noexcept;
1016template void Device::Set<int8_t>(uint16_t, uint8_t, const int8_t&);
1017template void Device::Set<int8_t>(uint16_t, uint8_t, const int8_t&,
1018 ::std::error_code&) noexcept;
1019
1020// INTEGER16
1021template int16_t Device::Get<int16_t>(uint16_t, uint8_t) const;
1022template int16_t Device::Get<int16_t>(uint16_t, uint8_t,
1023 ::std::error_code&) const noexcept;
1024template void Device::Set<int16_t>(uint16_t, uint8_t, const int16_t&);
1025template void Device::Set<int16_t>(uint16_t, uint8_t, const int16_t&,
1026 ::std::error_code&) noexcept;
1027
1028// INTEGER32
1029template int32_t Device::Get<int32_t>(uint16_t, uint8_t) const;
1030template int32_t Device::Get<int32_t>(uint16_t, uint8_t,
1031 ::std::error_code&) const noexcept;
1032template void Device::Set<int32_t>(uint16_t, uint8_t, const int32_t&);
1033template void Device::Set<int32_t>(uint16_t, uint8_t, const int32_t&,
1034 ::std::error_code&) noexcept;
1035
1036// UNSIGNED8
1037template uint8_t Device::Get<uint8_t>(uint16_t, uint8_t) const;
1038template uint8_t Device::Get<uint8_t>(uint16_t, uint8_t,
1039 ::std::error_code&) const noexcept;
1040template void Device::Set<uint8_t>(uint16_t, uint8_t, const uint8_t&);
1041template void Device::Set<uint8_t>(uint16_t, uint8_t, const uint8_t&,
1042 ::std::error_code&) noexcept;
1043
1044// UNSIGNED16
1045template uint16_t Device::Get<uint16_t>(uint16_t, uint8_t) const;
1046template uint16_t Device::Get<uint16_t>(uint16_t, uint8_t,
1047 ::std::error_code&) const noexcept;
1048template void Device::Set<uint16_t>(uint16_t, uint8_t, const uint16_t&);
1049template void Device::Set<uint16_t>(uint16_t, uint8_t, const uint16_t&,
1050 ::std::error_code&) noexcept;
1051
1052// UNSIGNED32
1053template uint32_t Device::Get<uint32_t>(uint16_t, uint8_t) const;
1054template uint32_t Device::Get<uint32_t>(uint16_t, uint8_t,
1055 ::std::error_code&) const noexcept;
1056template void Device::Set<uint32_t>(uint16_t, uint8_t, const uint32_t&);
1057template void Device::Set<uint32_t>(uint16_t, uint8_t, const uint32_t&,
1058 ::std::error_code&) noexcept;
1059
1060// REAL32
1061template float Device::Get<float>(uint16_t, uint8_t) const;
1062template float Device::Get<float>(uint16_t, uint8_t,
1063 ::std::error_code&) const noexcept;
1064template void Device::Set<float>(uint16_t, uint8_t, const float&);
1065template void Device::Set<float>(uint16_t, uint8_t, const float&,
1066 ::std::error_code&) noexcept;
1067
1068// VISIBLE_STRING
1069template ::std::string Device::Get<::std::string>(uint16_t, uint8_t) const;
1070template ::std::string Device::Get<::std::string>(
1071 uint16_t, uint8_t, ::std::error_code&) const noexcept;
1072template 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
1079template ::std::vector<uint8_t> Device::Get<::std::vector<uint8_t>>(
1080 uint16_t, uint8_t) const;
1081template ::std::vector<uint8_t> Device::Get<::std::vector<uint8_t>>(
1082 uint16_t, uint8_t, ::std::error_code&) const noexcept;
1083template 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
1092template ::std::basic_string<char16_t>
1093 Device::Get<::std::basic_string<char16_t>>(uint16_t, uint8_t) const;
1094template ::std::basic_string<char16_t>
1095Device::Get<::std::basic_string<char16_t>>(uint16_t, uint8_t,
1096 ::std::error_code&) const noexcept;
1097template void Device::Set<::std::basic_string<char16_t>>(
1098 uint16_t, uint8_t, const ::std::basic_string<char16_t>&);
1099template 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
1109template double Device::Get<double>(uint16_t, uint8_t) const;
1110template double Device::Get<double>(uint16_t, uint8_t,
1111 ::std::error_code&) const noexcept;
1112template void Device::Set<double>(uint16_t, uint8_t, const double&);
1113template 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
1121template int64_t Device::Get<int64_t>(uint16_t, uint8_t) const;
1122template int64_t Device::Get<int64_t>(uint16_t, uint8_t,
1123 ::std::error_code&) const noexcept;
1124template void Device::Set<int64_t>(uint16_t, uint8_t, const int64_t&);
1125template 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
1134template uint64_t Device::Get<uint64_t>(uint16_t, uint8_t) const;
1135template uint64_t Device::Get<uint64_t>(uint16_t, uint8_t,
1136 ::std::error_code&) const noexcept;
1137template void Device::Set<uint64_t>(uint16_t, uint8_t, const uint64_t&);
1138template 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
1143void
1144Device::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
1150void
1151Device::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
1156void
1157Device::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
1163void
1164Device::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
1201void
1202Device::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
1208void
1209Device::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
1234const char*
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
1242const char*
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();
1259}
1260
1261void
1262Device::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
1268void
1269Device::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
1292const char*
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
1300const char*
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();
1317}
1318
1319void
1320Device::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
1326void
1327Device::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
1352void
1354 ::std::error_code ec;
1355 SetEvent(idx, subidx, ec);
1356 if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "SetEvent");
1357}
1358
1359void
1360Device::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
1381template <class T>
1382typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
1383Device::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
1390template <class T>
1391typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
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
1400template <class T>
1401typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
1402Device::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
1409template <class T>
1410typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
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
1419template <class T>
1420typename ::std::enable_if<is_canopen_basic<T>::value>::type
1421Device::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
1427template <class T>
1428typename ::std::enable_if<is_canopen_basic<T>::value>::type
1429Device::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
1439template bool Device::RpdoGet<bool>(uint8_t, uint16_t, uint8_t) const;
1440template bool Device::RpdoGet<bool>(uint8_t, uint16_t, uint8_t,
1441 ::std::error_code&) const noexcept;
1442template bool Device::TpdoGet<bool>(uint8_t, uint16_t, uint8_t) const;
1443template bool Device::TpdoGet<bool>(uint8_t, uint16_t, uint8_t,
1444 ::std::error_code&) const noexcept;
1445template void Device::TpdoSet<bool>(uint8_t, uint16_t, uint8_t, bool);
1446template void Device::TpdoSet<bool>(uint8_t, uint16_t, uint8_t, bool,
1447 ::std::error_code&) noexcept;
1448
1449// INTEGER8
1450template int8_t Device::RpdoGet<int8_t>(uint8_t, uint16_t, uint8_t) const;
1451template int8_t Device::RpdoGet<int8_t>(uint8_t, uint16_t, uint8_t,
1452 ::std::error_code&) const noexcept;
1453template int8_t Device::TpdoGet<int8_t>(uint8_t, uint16_t, uint8_t) const;
1454template int8_t Device::TpdoGet<int8_t>(uint8_t, uint16_t, uint8_t,
1455 ::std::error_code&) const noexcept;
1456template void Device::TpdoSet<int8_t>(uint8_t, uint16_t, uint8_t, int8_t);
1457template void Device::TpdoSet<int8_t>(uint8_t, uint16_t, uint8_t, int8_t,
1458 ::std::error_code&) noexcept;
1459
1460// INTEGER16
1461template int16_t Device::RpdoGet<int16_t>(uint8_t, uint16_t, uint8_t) const;
1462template int16_t Device::RpdoGet<int16_t>(uint8_t, uint16_t, uint8_t,
1463 ::std::error_code&) const noexcept;
1464template int16_t Device::TpdoGet<int16_t>(uint8_t, uint16_t, uint8_t) const;
1465template int16_t Device::TpdoGet<int16_t>(uint8_t, uint16_t, uint8_t,
1466 ::std::error_code&) const noexcept;
1467template void Device::TpdoSet<int16_t>(uint8_t, uint16_t, uint8_t, int16_t);
1468template void Device::TpdoSet<int16_t>(uint8_t, uint16_t, uint8_t, int16_t,
1469 ::std::error_code&) noexcept;
1470
1471// INTEGER32
1472template int32_t Device::RpdoGet<int32_t>(uint8_t, uint16_t, uint8_t) const;
1473template int32_t Device::RpdoGet<int32_t>(uint8_t, uint16_t, uint8_t,
1474 ::std::error_code&) const noexcept;
1475template int32_t Device::TpdoGet<int32_t>(uint8_t, uint16_t, uint8_t) const;
1476template int32_t Device::TpdoGet<int32_t>(uint8_t, uint16_t, uint8_t,
1477 ::std::error_code&) const noexcept;
1478template void Device::TpdoSet<int32_t>(uint8_t, uint16_t, uint8_t, int32_t);
1479template void Device::TpdoSet<int32_t>(uint8_t, uint16_t, uint8_t, int32_t,
1480 ::std::error_code&) noexcept;
1481
1482// UNSIGNED8
1483template uint8_t Device::RpdoGet<uint8_t>(uint8_t, uint16_t, uint8_t) const;
1484template uint8_t Device::RpdoGet<uint8_t>(uint8_t, uint16_t, uint8_t,
1485 ::std::error_code&) const noexcept;
1486template uint8_t Device::TpdoGet<uint8_t>(uint8_t, uint16_t, uint8_t) const;
1487template uint8_t Device::TpdoGet<uint8_t>(uint8_t, uint16_t, uint8_t,
1488 ::std::error_code&) const noexcept;
1489template void Device::TpdoSet<uint8_t>(uint8_t, uint16_t, uint8_t, uint8_t);
1490template void Device::TpdoSet<uint8_t>(uint8_t, uint16_t, uint8_t, uint8_t,
1491 ::std::error_code&) noexcept;
1492
1493// UNSIGNED16
1494template uint16_t Device::RpdoGet<uint16_t>(uint8_t, uint16_t, uint8_t) const;
1495template uint16_t Device::RpdoGet<uint16_t>(uint8_t, uint16_t, uint8_t,
1496 ::std::error_code&) const noexcept;
1497template uint16_t Device::TpdoGet<uint16_t>(uint8_t, uint16_t, uint8_t) const;
1498template uint16_t Device::TpdoGet<uint16_t>(uint8_t, uint16_t, uint8_t,
1499 ::std::error_code&) const noexcept;
1500template void Device::TpdoSet<uint16_t>(uint8_t, uint16_t, uint8_t, uint16_t);
1501template void Device::TpdoSet<uint16_t>(uint8_t, uint16_t, uint8_t, uint16_t,
1502 ::std::error_code&) noexcept;
1503
1504// UNSIGNED32
1505template uint32_t Device::RpdoGet<uint32_t>(uint8_t, uint16_t, uint8_t) const;
1506template uint32_t Device::RpdoGet<uint32_t>(uint8_t, uint16_t, uint8_t,
1507 ::std::error_code&) const noexcept;
1508template uint32_t Device::TpdoGet<uint32_t>(uint8_t, uint16_t, uint8_t) const;
1509template uint32_t Device::TpdoGet<uint32_t>(uint8_t, uint16_t, uint8_t,
1510 ::std::error_code&) const noexcept;
1511template void Device::TpdoSet<uint32_t>(uint8_t, uint16_t, uint8_t, uint32_t);
1512template void Device::TpdoSet<uint32_t>(uint8_t, uint16_t, uint8_t, uint32_t,
1513 ::std::error_code&) noexcept;
1514
1515// REAL32
1516template float Device::RpdoGet<float>(uint8_t, uint16_t, uint8_t) const;
1517template float Device::RpdoGet<float>(uint8_t, uint16_t, uint8_t,
1518 ::std::error_code&) const noexcept;
1519template float Device::TpdoGet<float>(uint8_t, uint16_t, uint8_t) const;
1520template float Device::TpdoGet<float>(uint8_t, uint16_t, uint8_t,
1521 ::std::error_code&) const noexcept;
1522template void Device::TpdoSet<float>(uint8_t, uint16_t, uint8_t, float);
1523template 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
1535template double Device::RpdoGet<double>(uint8_t, uint16_t, uint8_t) const;
1536template double Device::RpdoGet<double>(uint8_t, uint16_t, uint8_t,
1537 ::std::error_code&) const noexcept;
1538template double Device::TpdoGet<double>(uint8_t, uint16_t, uint8_t) const;
1539template double Device::TpdoGet<double>(uint8_t, uint16_t, uint8_t,
1540 ::std::error_code&) const noexcept;
1541template void Device::TpdoSet<double>(uint8_t, uint16_t, uint8_t, double);
1542template 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
1550template int64_t Device::RpdoGet<int64_t>(uint8_t, uint16_t, uint8_t) const;
1551template int64_t Device::RpdoGet<int64_t>(uint8_t, uint16_t, uint8_t,
1552 ::std::error_code&) const noexcept;
1553template int64_t Device::TpdoGet<int64_t>(uint8_t, uint16_t, uint8_t) const;
1554template int64_t Device::TpdoGet<int64_t>(uint8_t, uint16_t, uint8_t,
1555 ::std::error_code&) const noexcept;
1556template void Device::TpdoSet<int64_t>(uint8_t, uint16_t, uint8_t, int64_t);
1557template 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
1566template uint64_t Device::RpdoGet<uint64_t>(uint8_t, uint16_t, uint8_t) const;
1567template uint64_t Device::RpdoGet<uint64_t>(uint8_t, uint16_t, uint8_t,
1568 ::std::error_code&) const noexcept;
1569template uint64_t Device::TpdoGet<uint64_t>(uint8_t, uint16_t, uint8_t) const;
1570template uint64_t Device::TpdoGet<uint64_t>(uint8_t, uint16_t, uint8_t,
1571 ::std::error_code&) const noexcept;
1572template void Device::TpdoSet<uint64_t>(uint8_t, uint16_t, uint8_t, uint64_t);
1573template 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
1578void
1580 ::std::error_code ec;
1581 TpdoSetEvent(id, idx, subidx, ec);
1582 if (ec) throw_sdo_error(id, idx, subidx, ec, "TpdoSetEvent");
1583}
1584
1585void
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
1593void
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);
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++) {
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);
1665 int i = co_obj_get_idx(obj_1400) - 0x1400;
1666 if (i >= CO_NUM_PDOS) break;
1667 // Skip invalid PDOs.
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);
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;
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
1713void
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);
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++) {
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
1775void
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
1793Device::Impl_::Impl_(Device* self_, const ::std::string& dcf_txt,
1794 const ::std::string& dcf_bin, uint8_t id,
1796 : self(self_),
1797 mutex(mutex_),
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)),
1823 return 0;
1824 },
1825 static_cast<void*>(this));
1826 }
1827}
1828#endif // !LELY_NO_CO_DCF
1829
1830void
1831Device::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
A CANopen value.
Definition val.hpp:42
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...
Definition device.cpp:1383
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
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_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...
Definition device.cpp:1402
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.
Definition device.cpp:231
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
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...
Definition device.cpp:635
const ::std::type_info & Type(uint16_t idx, uint8_t subidx) const
Returns the type of a sub-object.
Definition device.cpp:833
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.
Definition device.cpp:943
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.
Definition device.cpp:904
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_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...
Definition device.cpp:1421
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
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.
Definition device.cpp:262
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
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,...
Definition device.cpp:613
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 ...
Definition device.cpp:591
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
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_obj_t * co_dev_find_obj(const co_dev_t *dev, co_unsigned16_t idx)
Finds an object in the object dictionary of a CANopen device.
Definition dev.c:279
co_unsigned8_t co_dev_get_id(const co_dev_t *dev)
Returns the node-ID of a CANopen device.
Definition dev.c: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_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
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
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
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.
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....
This header file is part of the CANopen library; it contains the object dictionary declarations.
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
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
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_unsigned16_t co_obj_get_idx(const co_obj_t *obj)
Returns the index of a CANopen object.
Definition obj.c:164
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
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
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
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
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
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_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
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
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
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_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
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
#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