Lely core libraries 2.3.2
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
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
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
185uint8_t
186Device::netid() const noexcept {
187 ::std::lock_guard<Impl_> lock(*impl_);
188
189 return impl_->netid();
190}
191
192uint8_t
193Device::id() const noexcept {
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;
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
574void
575Device::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
581void
582Device::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
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
600Device::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
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
622Device::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
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
796Device::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
802void
803Device::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
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
828Device::dev() const noexcept {
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&
841Device::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)) {
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
913Device::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))) {
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
1232const char*
1233Device::GetUploadFile(uint16_t idx, uint8_t subidx) const {
1234 ::std::error_code ec;
1235 auto filename = GetUploadFile(idx, subidx, ec);
1236 if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "GetUploadFile");
1237 return filename;
1238}
1239
1240const char*
1241Device::GetUploadFile(uint16_t idx, uint8_t subidx,
1242 ::std::error_code& ec) const noexcept {
1243 auto obj = co_dev_find_obj(dev(), idx);
1244 if (!obj) {
1245 ec = SdoErrc::NO_OBJ;
1246 return nullptr;
1247 }
1248
1249 auto sub = co_obj_find_sub(obj, subidx);
1250 if (!sub) {
1251 ec = SdoErrc::NO_SUB;
1252 return nullptr;
1253 }
1254
1255 ec.clear();
1256 return co_sub_get_upload_file(sub);
1257}
1258
1259void
1260Device::SetUploadFile(uint16_t idx, uint8_t subidx, const char* filename) {
1261 ::std::error_code ec;
1262 SetUploadFile(idx, subidx, filename, ec);
1263 if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "SetUploadFile");
1264}
1265
1266void
1267Device::SetUploadFile(uint16_t idx, uint8_t subidx, const char* filename,
1268 ::std::error_code& ec) noexcept {
1269 auto obj = co_dev_find_obj(dev(), idx);
1270 if (!obj) {
1271 ec = SdoErrc::NO_OBJ;
1272 return;
1273 }
1274
1275 auto sub = co_obj_find_sub(obj, subidx);
1276 if (!sub) {
1277 ec = SdoErrc::NO_SUB;
1278 return;
1279 }
1280
1281 int errsv = get_errc();
1282 set_errc(0);
1283 if (!co_sub_set_upload_file(sub, filename))
1284 ec.clear();
1285 else
1286 ec = util::make_error_code();
1287 set_errc(errsv);
1288}
1289
1290const char*
1291Device::GetDownloadFile(uint16_t idx, uint8_t subidx) const {
1292 ::std::error_code ec;
1293 auto filename = GetDownloadFile(idx, subidx, ec);
1294 if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "GetDownloadFile");
1295 return filename;
1296}
1297
1298const char*
1299Device::GetDownloadFile(uint16_t idx, uint8_t subidx,
1300 ::std::error_code& ec) const noexcept {
1301 auto obj = co_dev_find_obj(dev(), idx);
1302 if (!obj) {
1303 ec = SdoErrc::NO_OBJ;
1304 return nullptr;
1305 }
1306
1307 auto sub = co_obj_find_sub(obj, subidx);
1308 if (!sub) {
1309 ec = SdoErrc::NO_SUB;
1310 return nullptr;
1311 }
1312
1313 ec.clear();
1314 return co_sub_get_download_file(sub);
1315}
1316
1317void
1318Device::SetDownloadFile(uint16_t idx, uint8_t subidx, const char* filename) {
1319 ::std::error_code ec;
1320 SetDownloadFile(idx, subidx, filename, ec);
1321 if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "SetDownloadFile");
1322}
1323
1324void
1325Device::SetDownloadFile(uint16_t idx, uint8_t subidx, const char* filename,
1326 ::std::error_code& ec) noexcept {
1327 auto obj = co_dev_find_obj(dev(), idx);
1328 if (!obj) {
1329 ec = SdoErrc::NO_OBJ;
1330 return;
1331 }
1332
1333 auto sub = co_obj_find_sub(obj, subidx);
1334 if (!sub) {
1335 ec = SdoErrc::NO_SUB;
1336 return;
1337 }
1338
1339 int errsv = get_errc();
1340 set_errc(0);
1341 if (!co_sub_set_download_file(sub, filename))
1342 ec.clear();
1343 else
1344 ec = util::make_error_code();
1345 set_errc(errsv);
1346}
1347
1348void
1349Device::SetEvent(uint16_t idx, uint8_t subidx) {
1350 ::std::error_code ec;
1351 SetEvent(idx, subidx, ec);
1352 if (ec) throw_sdo_error(impl_->id(), idx, subidx, ec, "SetEvent");
1353}
1354
1355void
1356Device::SetEvent(uint16_t idx, uint8_t subidx, ::std::error_code& ec) noexcept {
1357 auto obj = co_dev_find_obj(dev(), idx);
1358 if (!obj) {
1359 ec = SdoErrc::NO_OBJ;
1360 return;
1361 }
1362
1363 auto sub = co_obj_find_sub(obj, subidx);
1364 if (!sub) {
1365 ec = SdoErrc::NO_SUB;
1366 return;
1367 }
1368
1369#if !LELY_NO_CO_TPDO
1370 co_dev_tpdo_event(dev(), idx, subidx);
1371#if !LELYY_NO_CO_MPDO
1372 co_dev_sam_mpdo_event(dev(), idx, subidx);
1373#endif
1374#endif // !LELY_NO_CO_TPDO
1375}
1376
1377template <class T>
1378typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
1379Device::RpdoGet(uint8_t id, uint16_t idx, uint8_t subidx) const {
1380 ::std::error_code ec;
1381 auto value = RpdoGet<T>(id, idx, subidx, ec);
1382 if (ec) throw_sdo_error(id, idx, subidx, ec, "RpdoGet");
1383 return value;
1384}
1385
1386template <class T>
1387typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
1388Device::RpdoGet(uint8_t id, uint16_t idx, uint8_t subidx,
1389 ::std::error_code& ec) const noexcept {
1390 ec.clear();
1391 ::std::tie(idx, subidx) = impl_->RpdoMapping(id, idx, subidx, ec);
1392 if (ec) return T();
1393 return Get<T>(idx, subidx, ec);
1394}
1395
1396template <class T>
1397typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
1398Device::TpdoGet(uint8_t id, uint16_t idx, uint8_t subidx) const {
1399 ::std::error_code ec;
1400 auto value = TpdoGet<T>(id, idx, subidx, ec);
1401 if (ec) throw_sdo_error(id, idx, subidx, ec, "TpdoGet");
1402 return value;
1403}
1404
1405template <class T>
1406typename ::std::enable_if<is_canopen_basic<T>::value, T>::type
1407Device::TpdoGet(uint8_t id, uint16_t idx, uint8_t subidx,
1408 ::std::error_code& ec) const noexcept {
1409 ec.clear();
1410 ::std::tie(idx, subidx) = impl_->TpdoMapping(id, idx, subidx, ec);
1411 if (ec) return T();
1412 return Get<T>(idx, subidx, ec);
1413}
1414
1415template <class T>
1416typename ::std::enable_if<is_canopen_basic<T>::value>::type
1417Device::TpdoSet(uint8_t id, uint16_t idx, uint8_t subidx, T value) {
1418 ::std::error_code ec;
1419 TpdoSet(id, idx, subidx, value, ec);
1420 if (ec) throw_sdo_error(id, idx, subidx, ec, "TpdoSet");
1421}
1422
1423template <class T>
1424typename ::std::enable_if<is_canopen_basic<T>::value>::type
1425Device::TpdoSet(uint8_t id, uint16_t idx, uint8_t subidx, T value,
1426 ::std::error_code& ec) noexcept {
1427 ec.clear();
1428 ::std::tie(idx, subidx) = impl_->TpdoMapping(id, idx, subidx, ec);
1429 if (!ec) Set<T>(idx, subidx, value, ec);
1430}
1431
1432#ifndef DOXYGEN_SHOULD_SKIP_THIS
1433
1434// BOOLEAN
1435template bool Device::RpdoGet<bool>(uint8_t, uint16_t, uint8_t) const;
1436template bool Device::RpdoGet<bool>(uint8_t, uint16_t, uint8_t,
1437 ::std::error_code&) const noexcept;
1438template bool Device::TpdoGet<bool>(uint8_t, uint16_t, uint8_t) const;
1439template bool Device::TpdoGet<bool>(uint8_t, uint16_t, uint8_t,
1440 ::std::error_code&) const noexcept;
1441template void Device::TpdoSet<bool>(uint8_t, uint16_t, uint8_t, bool);
1442template void Device::TpdoSet<bool>(uint8_t, uint16_t, uint8_t, bool,
1443 ::std::error_code&) noexcept;
1444
1445// INTEGER8
1446template int8_t Device::RpdoGet<int8_t>(uint8_t, uint16_t, uint8_t) const;
1447template int8_t Device::RpdoGet<int8_t>(uint8_t, uint16_t, uint8_t,
1448 ::std::error_code&) const noexcept;
1449template int8_t Device::TpdoGet<int8_t>(uint8_t, uint16_t, uint8_t) const;
1450template int8_t Device::TpdoGet<int8_t>(uint8_t, uint16_t, uint8_t,
1451 ::std::error_code&) const noexcept;
1452template void Device::TpdoSet<int8_t>(uint8_t, uint16_t, uint8_t, int8_t);
1453template void Device::TpdoSet<int8_t>(uint8_t, uint16_t, uint8_t, int8_t,
1454 ::std::error_code&) noexcept;
1455
1456// INTEGER16
1457template int16_t Device::RpdoGet<int16_t>(uint8_t, uint16_t, uint8_t) const;
1458template int16_t Device::RpdoGet<int16_t>(uint8_t, uint16_t, uint8_t,
1459 ::std::error_code&) const noexcept;
1460template int16_t Device::TpdoGet<int16_t>(uint8_t, uint16_t, uint8_t) const;
1461template int16_t Device::TpdoGet<int16_t>(uint8_t, uint16_t, uint8_t,
1462 ::std::error_code&) const noexcept;
1463template void Device::TpdoSet<int16_t>(uint8_t, uint16_t, uint8_t, int16_t);
1464template void Device::TpdoSet<int16_t>(uint8_t, uint16_t, uint8_t, int16_t,
1465 ::std::error_code&) noexcept;
1466
1467// INTEGER32
1468template int32_t Device::RpdoGet<int32_t>(uint8_t, uint16_t, uint8_t) const;
1469template int32_t Device::RpdoGet<int32_t>(uint8_t, uint16_t, uint8_t,
1470 ::std::error_code&) const noexcept;
1471template int32_t Device::TpdoGet<int32_t>(uint8_t, uint16_t, uint8_t) const;
1472template int32_t Device::TpdoGet<int32_t>(uint8_t, uint16_t, uint8_t,
1473 ::std::error_code&) const noexcept;
1474template void Device::TpdoSet<int32_t>(uint8_t, uint16_t, uint8_t, int32_t);
1475template void Device::TpdoSet<int32_t>(uint8_t, uint16_t, uint8_t, int32_t,
1476 ::std::error_code&) noexcept;
1477
1478// UNSIGNED8
1479template uint8_t Device::RpdoGet<uint8_t>(uint8_t, uint16_t, uint8_t) const;
1480template uint8_t Device::RpdoGet<uint8_t>(uint8_t, uint16_t, uint8_t,
1481 ::std::error_code&) const noexcept;
1482template uint8_t Device::TpdoGet<uint8_t>(uint8_t, uint16_t, uint8_t) const;
1483template uint8_t Device::TpdoGet<uint8_t>(uint8_t, uint16_t, uint8_t,
1484 ::std::error_code&) const noexcept;
1485template void Device::TpdoSet<uint8_t>(uint8_t, uint16_t, uint8_t, uint8_t);
1486template void Device::TpdoSet<uint8_t>(uint8_t, uint16_t, uint8_t, uint8_t,
1487 ::std::error_code&) noexcept;
1488
1489// UNSIGNED16
1490template uint16_t Device::RpdoGet<uint16_t>(uint8_t, uint16_t, uint8_t) const;
1491template uint16_t Device::RpdoGet<uint16_t>(uint8_t, uint16_t, uint8_t,
1492 ::std::error_code&) const noexcept;
1493template uint16_t Device::TpdoGet<uint16_t>(uint8_t, uint16_t, uint8_t) const;
1494template uint16_t Device::TpdoGet<uint16_t>(uint8_t, uint16_t, uint8_t,
1495 ::std::error_code&) const noexcept;
1496template void Device::TpdoSet<uint16_t>(uint8_t, uint16_t, uint8_t, uint16_t);
1497template void Device::TpdoSet<uint16_t>(uint8_t, uint16_t, uint8_t, uint16_t,
1498 ::std::error_code&) noexcept;
1499
1500// UNSIGNED32
1501template uint32_t Device::RpdoGet<uint32_t>(uint8_t, uint16_t, uint8_t) const;
1502template uint32_t Device::RpdoGet<uint32_t>(uint8_t, uint16_t, uint8_t,
1503 ::std::error_code&) const noexcept;
1504template uint32_t Device::TpdoGet<uint32_t>(uint8_t, uint16_t, uint8_t) const;
1505template uint32_t Device::TpdoGet<uint32_t>(uint8_t, uint16_t, uint8_t,
1506 ::std::error_code&) const noexcept;
1507template void Device::TpdoSet<uint32_t>(uint8_t, uint16_t, uint8_t, uint32_t);
1508template void Device::TpdoSet<uint32_t>(uint8_t, uint16_t, uint8_t, uint32_t,
1509 ::std::error_code&) noexcept;
1510
1511// REAL32
1512template float Device::RpdoGet<float>(uint8_t, uint16_t, uint8_t) const;
1513template float Device::RpdoGet<float>(uint8_t, uint16_t, uint8_t,
1514 ::std::error_code&) const noexcept;
1515template float Device::TpdoGet<float>(uint8_t, uint16_t, uint8_t) const;
1516template float Device::TpdoGet<float>(uint8_t, uint16_t, uint8_t,
1517 ::std::error_code&) const noexcept;
1518template void Device::TpdoSet<float>(uint8_t, uint16_t, uint8_t, float);
1519template void Device::TpdoSet<float>(uint8_t, uint16_t, uint8_t, float,
1520 ::std::error_code&) noexcept;
1521
1522// VISIBLE_STRING
1523// OCTET_STRING
1524// UNICODE_STRING
1525// TIME_OF_DAY
1526// TIME_DIFFERENCE
1527// DOMAIN
1528// INTEGER24
1529
1530// REAL64
1531template double Device::RpdoGet<double>(uint8_t, uint16_t, uint8_t) const;
1532template double Device::RpdoGet<double>(uint8_t, uint16_t, uint8_t,
1533 ::std::error_code&) const noexcept;
1534template double Device::TpdoGet<double>(uint8_t, uint16_t, uint8_t) const;
1535template double Device::TpdoGet<double>(uint8_t, uint16_t, uint8_t,
1536 ::std::error_code&) const noexcept;
1537template void Device::TpdoSet<double>(uint8_t, uint16_t, uint8_t, double);
1538template void Device::TpdoSet<double>(uint8_t, uint16_t, uint8_t, double,
1539 ::std::error_code&) noexcept;
1540
1541// INTEGER40
1542// INTEGER48
1543// INTEGER56
1544
1545// INTEGER64
1546template int64_t Device::RpdoGet<int64_t>(uint8_t, uint16_t, uint8_t) const;
1547template int64_t Device::RpdoGet<int64_t>(uint8_t, uint16_t, uint8_t,
1548 ::std::error_code&) const noexcept;
1549template int64_t Device::TpdoGet<int64_t>(uint8_t, uint16_t, uint8_t) const;
1550template int64_t Device::TpdoGet<int64_t>(uint8_t, uint16_t, uint8_t,
1551 ::std::error_code&) const noexcept;
1552template void Device::TpdoSet<int64_t>(uint8_t, uint16_t, uint8_t, int64_t);
1553template void Device::TpdoSet<int64_t>(uint8_t, uint16_t, uint8_t, int64_t,
1554 ::std::error_code&) noexcept;
1555
1556// UNSIGNED24
1557// UNSIGNED40
1558// UNSIGNED48
1559// UNSIGNED56
1560
1561// UNSIGNED64
1562template uint64_t Device::RpdoGet<uint64_t>(uint8_t, uint16_t, uint8_t) const;
1563template uint64_t Device::RpdoGet<uint64_t>(uint8_t, uint16_t, uint8_t,
1564 ::std::error_code&) const noexcept;
1565template uint64_t Device::TpdoGet<uint64_t>(uint8_t, uint16_t, uint8_t) const;
1566template uint64_t Device::TpdoGet<uint64_t>(uint8_t, uint16_t, uint8_t,
1567 ::std::error_code&) const noexcept;
1568template void Device::TpdoSet<uint64_t>(uint8_t, uint16_t, uint8_t, uint64_t);
1569template void Device::TpdoSet<uint64_t>(uint8_t, uint16_t, uint8_t, uint64_t,
1570 ::std::error_code&) noexcept;
1571
1572#endif // DOXYGEN_SHOULD_SKIP_THIS
1573
1574void
1575Device::TpdoSetEvent(uint8_t id, uint16_t idx, uint8_t subidx) {
1576 ::std::error_code ec;
1577 TpdoSetEvent(id, idx, subidx, ec);
1578 if (ec) throw_sdo_error(id, idx, subidx, ec, "TpdoSetEvent");
1579}
1580
1581void
1582Device::TpdoSetEvent(uint8_t id, uint16_t idx, uint8_t subidx,
1583 ::std::error_code& ec) noexcept {
1584 ec.clear();
1585 ::std::tie(idx, subidx) = impl_->TpdoMapping(id, idx, subidx, ec);
1586 if (!ec) SetEvent(idx, subidx, ec);
1587}
1588
1589void
1591#if !LELY_NO_CO_RPDO
1592 impl_->rpdo_mapping.clear();
1593
1594 // Loop over all RPDOs.
1595 co_obj_t* obj_1400 = nullptr;
1596 for (int i = 0; !obj_1400 && i < 512; i++)
1597 obj_1400 = co_dev_find_obj(dev(), 0x1400 + i);
1598 for (; obj_1400; obj_1400 = co_obj_next(obj_1400)) {
1599 int i = co_obj_get_idx(obj_1400) - 0x1400;
1600 if (i >= 512) break;
1601 // Skip invalid PDOs.
1602 auto cobid = co_obj_get_val_u32(obj_1400, 1);
1603 if (cobid & CO_PDO_COBID_VALID) continue;
1604 // Obtain the remote node-ID.
1605 uint8_t id = 0;
1606 auto obj_5800 = co_dev_find_obj(dev(), 0x5800 + i);
1607 if (obj_5800) {
1608 id = co_obj_get_val_u32(obj_5800, 0) & 0xff;
1609 } else {
1610 // Obtain the node-ID from the predefined connection, if possible.
1611 if (cobid & CO_PDO_COBID_FRAME) continue;
1612 switch (cobid & 0x780) {
1613 case 0x180:
1614 case 0x280:
1615 case 0x380:
1616 case 0x480:
1617 id = cobid & 0x7f;
1618 break;
1619 default:
1620 continue;
1621 }
1622 }
1623 // Skip invalid node-IDs.
1624 if (!id || id > CO_NUM_NODES) continue;
1625 // Obtain the local RPDO mapping.
1626 auto obj_1600 = co_dev_find_obj(dev(), 0x1600 + i);
1627 if (!obj_1600) continue;
1628 // Obtain the remote TPDO mapping.
1629 auto obj_5a00 = co_dev_find_obj(dev(), 0x5a00 + i);
1630 if (!obj_5a00) continue;
1631 // Check if the number of mapped objects is the same.
1632 auto n = co_obj_get_val_u8(obj_1600, 0);
1633 if (!n || n > CO_PDO_NUM_MAPS) continue;
1634 if (n != co_obj_get_val_u8(obj_5a00, 0)) continue;
1635 for (int j = 1; j <= n; j++) {
1636 auto rmap = co_obj_get_val_u32(obj_1600, j);
1637 auto tmap = co_obj_get_val_u32(obj_5a00, j);
1638 // Ignore empty mapping entries.
1639 if (!rmap && !tmap) continue;
1640 // Check if the mapped objects have the same length.
1641 if ((rmap & 0xff) != (tmap & 0xff)) break;
1642 rmap >>= 8;
1643 tmap >>= 8;
1644 // Skip dummy-mapped objects.
1645 if (co_type_is_basic((rmap >> 8) & 0xffff)) continue;
1646 tmap |= static_cast<uint32_t>(id) << 24;
1647 impl_->rpdo_mapping[tmap] = rmap;
1648 // Store the reverse mapping for OnRpdoWrite().
1649 impl_->rpdo_mapping[rmap] = tmap;
1650 }
1651 }
1652
1653#if !LELY_NO_CO_MPDO
1654 // Check if at least one RPDO is a SAM-MPDO consumer.
1655 bool has_sam_mpdo = false;
1656 // Loop over all RPDOs.
1657 obj_1400 = nullptr;
1658 for (int i = 0; !obj_1400 && i < CO_NUM_PDOS; i++)
1659 obj_1400 = co_dev_find_obj(dev(), 0x1400 + i);
1660 for (; !has_sam_mpdo && obj_1400; obj_1400 = co_obj_next(obj_1400)) {
1661 int i = co_obj_get_idx(obj_1400) - 0x1400;
1662 if (i >= CO_NUM_PDOS) break;
1663 // Skip invalid PDOs.
1664 if (co_obj_get_val_u32(obj_1400, 1) & CO_PDO_COBID_VALID) continue;
1665 // SAM-MPDOs MUST be event-driven.
1666 if (co_obj_get_val_u8(obj_1400, 2) < 0xfe) continue;
1667 // Check if the PDO is a SAM-MPDO.
1668 if (co_dev_get_val_u8(dev(), 0x1600 + i, 0) != CO_PDO_MAP_SAM_MPDO)
1669 continue;
1670 has_sam_mpdo = true;
1671 }
1672
1673 if (has_sam_mpdo) {
1674 // Loop over the object dispatching list (object 1FD0..1FFF).
1675 co_obj_t* obj_1fd0 = nullptr;
1676 for (int i = 0; !obj_1fd0 && i < 48; i++)
1677 obj_1fd0 = co_dev_find_obj(dev(), 0x1fd0 + i);
1678 for (; obj_1fd0; obj_1fd0 = co_obj_next(obj_1fd0)) {
1679 if (co_obj_get_idx(obj_1fd0) > 0x1fff) break;
1680 auto n = co_obj_get_val_u8(obj_1fd0, 0);
1681 if (!n) continue;
1682 auto sub = co_sub_next(co_obj_first_sub(obj_1fd0));
1683 for (; sub && co_sub_get_subidx(sub) <= n; sub = co_sub_next(sub)) {
1684 auto val = co_sub_get_val_u64(sub);
1685 if (!val) continue;
1686 // Extract the mapping.
1687 uint32_t rmap = (val << 8) >> 40;
1688 uint32_t tmap = ror32(val, 8);
1689 // Skip dummy-mapped objects.
1690 if (co_type_is_basic((rmap >> 8) & 0xffff)) continue;
1691 // Extract the block of sub-indices.
1692 uint8_t min = (val >> 8) & 0xff;
1693 uint8_t max = min;
1694 uint8_t blk = (val >> 56) & 0xff;
1695 if (blk) max += ::std::min(blk - 1, 0xff - min);
1696 // Store the (reverse) mapping for each of the sub-indices.
1697 for (int j = 0; j <= max - min; j++) {
1698 // Ignore out-of-range sub-indices.
1699 if (static_cast<uint8_t>(j) > 0xff - (rmap & 0xff)) break;
1700 impl_->rpdo_mapping[tmap + j] = rmap + j;
1701 }
1702 }
1703 }
1704 }
1705#endif // !LELY_NO_CO_MPDO
1706#endif // !LELY_NO_CO_RPDO
1707}
1708
1709void
1711#if !LELY_NO_CO_TPDO
1712 impl_->tpdo_mapping.clear();
1713
1714 // Loop over all TPDOs.
1715 co_obj_t* obj_1800 = nullptr;
1716 for (int i = 0; !obj_1800 && i < 512; i++)
1717 obj_1800 = co_dev_find_obj(dev(), 0x1800 + i);
1718 for (; obj_1800; obj_1800 = co_obj_next(obj_1800)) {
1719 int i = co_obj_get_idx(obj_1800) - 0x1800;
1720 if (i >= 512) break;
1721 // Skip invalid PDOs.
1722 auto cobid = co_obj_get_val_u32(obj_1800, 1);
1723 if (cobid & CO_PDO_COBID_VALID) continue;
1724 // Obtain the remote node-ID.
1725 uint8_t id = 0;
1726 auto obj_5c00 = co_dev_find_obj(dev(), 0x5c00 + i);
1727 if (obj_5c00) {
1728 id = co_obj_get_val_u32(obj_5c00, 0) & 0xff;
1729 } else {
1730 // Obtain the node-ID from the predefined connection, if possible.
1731 if (cobid & CO_PDO_COBID_FRAME) continue;
1732 switch (cobid & 0x780) {
1733 case 0x200:
1734 case 0x300:
1735 case 0x400:
1736 case 0x500:
1737 id = cobid & 0x7f;
1738 break;
1739 default:
1740 continue;
1741 }
1742 }
1743 // Skip invalid node-IDs.
1744 if (!id || id > CO_NUM_NODES) continue;
1745 // Obtain the local TPDO mapping.
1746 auto obj_1a00 = co_dev_find_obj(dev(), 0x1a00 + i);
1747 if (!obj_1a00) continue;
1748 // Obtain the remote RPDO mapping.
1749 auto obj_5e00 = co_dev_find_obj(dev(), 0x5e00 + i);
1750 if (!obj_5e00) continue;
1751 // Check if the number of mapped objects is the same.
1752 auto n = co_obj_get_val_u8(obj_1a00, 0);
1753 if (!n || n > CO_PDO_NUM_MAPS) continue;
1754 if (n != co_obj_get_val_u8(obj_5e00, 0)) continue;
1755 for (int j = 1; j <= n; j++) {
1756 auto tmap = co_obj_get_val_u32(obj_1a00, j);
1757 auto rmap = co_obj_get_val_u32(obj_5e00, j);
1758 // Ignore empty mapping entries.
1759 if (!rmap && !tmap) continue;
1760 // Check if the mapped objects have the same length.
1761 if ((tmap & 0xff) != (rmap & 0xff)) break;
1762 tmap >>= 8;
1763 rmap >>= 8;
1764 rmap |= static_cast<uint32_t>(id) << 24;
1765 impl_->tpdo_mapping[rmap] = tmap;
1766 }
1767 }
1768#endif // !LELY_NO_CO_TPDO
1769}
1770
1771void
1772Device::RpdoWrite(uint8_t id, uint16_t idx, uint8_t subidx) {
1773#if LELY_NO_CO_RPDO
1774 (void)id;
1775 (void)idx;
1776 (void)subidx;
1777#else
1778 OnRpdoWrite(id, idx, subidx);
1779
1780 if (impl_->on_rpdo_write) {
1781 auto f = impl_->on_rpdo_write;
1782 util::UnlockGuard<Impl_> unlock(*impl_);
1783 f(id, idx, subidx);
1784 }
1785#endif
1786}
1787
1788#if !LELY_NO_CO_DCF
1789Device::Impl_::Impl_(Device* self_, const ::std::string& dcf_txt,
1790 const ::std::string& dcf_bin, uint8_t id,
1791 util::BasicLockable* mutex_)
1792 : self(self_),
1793 mutex(mutex_),
1794 dev(co_dev_create_from_dcf_file(dcf_txt.c_str())) {
1795 if (!dcf_bin.empty() &&
1796 co_dev_read_dcf_file(dev.get(), nullptr, nullptr, dcf_bin.c_str()) == -1)
1797 util::throw_errc("Device");
1798
1799 if (id != 0xff && co_dev_set_id(dev.get(), id) == -1)
1800 util::throw_errc("Device");
1801
1802 // Register a notification function for all objects in the object dictionary
1803 // in case of write (SDO upload) access.
1804 for (auto obj = co_dev_first_obj(dev.get()); obj; obj = co_obj_next(obj)) {
1805 // Skip data types and the communication profile area.
1806 if (co_obj_get_idx(obj) < 0x2000) continue;
1807 // Skip reserved objects.
1808 if (co_obj_get_idx(obj) >= 0xC000) break;
1810 obj,
1811 [](co_sub_t* sub, co_sdo_req* req, void* data) -> uint32_t {
1812 // Implement the default behavior, but do not issue a notification for
1813 // incomplete or failed writes.
1814 uint32_t ac = 0;
1815 if (co_sub_on_dn(sub, req, &ac) == -1 || ac) return ac;
1816 auto impl_ = static_cast<Impl_*>(data);
1817 impl_->OnWrite(co_obj_get_idx(co_sub_get_obj(sub)),
1818 co_sub_get_subidx(sub));
1819 return 0;
1820 },
1821 static_cast<void*>(this));
1822 }
1823}
1824#endif // !LELY_NO_CO_DCF
1825
1826void
1827Device::Impl_::OnWrite(uint16_t idx, uint8_t subidx) {
1828 self->OnWrite(idx, subidx);
1829
1830 if (on_write) {
1831 auto f = on_write;
1832 util::UnlockGuard<Impl_> unlock(*this);
1833 f(idx, subidx);
1834 }
1835
1836#if !LELY_NO_CO_RPDO
1837 uint8_t id = 0;
1838 ::std::error_code ec;
1839 ::std::tie(id, idx, subidx) = RpdoMapping(idx, subidx, ec);
1840 if (!ec) self->RpdoWrite(id, idx, subidx);
1841#endif
1842}
1843
1844} // namespace canopen
1845
1846} // 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:1772
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:1260
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:1379
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:1710
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:1398
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:1318
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:1590
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:1349
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:1417
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:1291
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:1575
__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:1233
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.
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
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
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