Lely core libraries  2.3.4
master.cpp
Go to the documentation of this file.
1 
24 #include "coapp.hpp"
25 
26 #if !LELY_NO_COAPP_MASTER
27 
28 #include <lely/co/dev.h>
29 #include <lely/co/nmt.h>
30 #include <lely/coapp/driver.hpp>
31 
32 #include <algorithm>
33 #include <array>
34 #include <map>
35 #include <string>
36 
37 #include <cassert>
38 
39 namespace lely {
40 
41 namespace canopen {
42 
45  Impl_(BasicMaster* self, co_nmt_t* nmt);
46 
47  ev::Future<void> AsyncDeconfig(DriverBase* driver);
48 
49 #if !LELY_NO_CO_NG
50  void OnNgInd(co_nmt_t* nmt, uint8_t id, int state, int reason) noexcept;
51 #endif
52 #if !LELY_NO_CO_NMT_BOOT
53  void OnBootInd(co_nmt_t* nmt, uint8_t id, uint8_t st, char es) noexcept;
54 #endif
55 #if !LELY_NO_CO_NMT_CFG
56  void OnCfgInd(co_nmt_t* nmt, uint8_t id, co_csdo_t* sdo) noexcept;
57 #endif
58 
59  BasicMaster* self;
60  ::std::function<void(uint8_t, bool)> on_node_guarding;
61  ::std::function<void(uint8_t, NmtState, char, const ::std::string&)> on_boot;
62  ::std::array<bool, CO_NUM_NODES> ready{{false}};
63 #if !LELY_NO_CO_NMT_CFG
64  ::std::array<bool, CO_NUM_NODES> config{{false}};
65 #endif
66  ::std::map<uint8_t, Sdo> sdos;
67 };
68 
69 void
71  ::std::lock_guard<util::BasicLockable> lock(*node);
73 }
74 
75 void
77  ::std::lock_guard<util::BasicLockable> lock(*node);
79 }
80 
82  io::CanChannelBase& chan, const ::std::string& dcf_txt,
83  const ::std::string& dcf_bin, uint8_t id)
84  : Node(exec, timer, chan, dcf_txt, dcf_bin, id),
85  tpdo_event_mutex(*this),
86  impl_(new Impl_(this, Node::nmt())) {}
87 
88 BasicMaster::~BasicMaster() = default;
89 
90 bool
91 BasicMaster::Boot(uint8_t id) {
92  if (!id || id > CO_NUM_NODES) return false;
93 
94 #if LELY_NO_CO_NMT_BOOT
95  return false;
96 #else
97  ::std::lock_guard<util::BasicLockable> lock(*this);
98 
99  if (co_nmt_is_booting(nmt(), id)) return false;
100 
101  // Abort any ongoing or pending SDO requests for the slave, since the master
102  // MAY need the Client-SDO service for the NMT 'boot slave' process.
103  CancelSdo(id);
104 
105  auto ready = impl_->ready[id - 1];
106  impl_->ready[id - 1] = false;
107  if (co_nmt_boot_req(nmt(), id, co_nmt_get_timeout(nmt())) == -1) {
108  impl_->ready[id - 1] = ready;
109  util::throw_errc("Boot");
110  }
111 
112  return true;
113 #endif
114 }
115 
116 bool
117 BasicMaster::IsReady(uint8_t id) const {
118  if (!id || id > CO_NUM_NODES) return false;
119 
120  ::std::lock_guard<util::BasicLockable> lock(const_cast<BasicMaster&>(*this));
121  return impl_->ready[id - 1];
122 }
123 
126  {
127  ::std::lock_guard<util::BasicLockable> lock(*this);
128  auto it = find(id);
129  if (it != end()) return impl_->AsyncDeconfig(it->second);
130  }
131  return ev::make_empty_future();
132 }
133 
136  ::std::array<ev::Future<void>, CO_NUM_NODES> futures;
137  ::std::size_t n = 0;
138  {
139  ::std::lock_guard<util::BasicLockable> lock(*this);
140  for (const auto& it : *this) futures[n++] = impl_->AsyncDeconfig(it.second);
141  }
142  // Create a temporary array of pointers, since ev::Future is not guaranteed to
143  // be the same size as ev_future_t*.
144  ::std::array<ev_future_t*, CO_NUM_NODES> tmp;
145  ::std::copy_n(futures.begin(), n, tmp.begin());
146  return ev::when_all(GetExecutor(), n, tmp.data());
147 }
148 
149 void
150 BasicMaster::Error(uint8_t id) {
151  ::std::lock_guard<util::BasicLockable> lock(*this);
152 
153  if (co_nmt_node_err_ind(nmt(), id) == -1) util::throw_errc("Error");
154 }
155 
156 void
157 BasicMaster::Error(uint16_t eec, uint8_t er, const uint8_t msef[5]) noexcept {
158  ::std::lock_guard<util::BasicLockable> lock(*this);
159 
160  Node::Error(eec, er, msef);
161 }
162 
163 void
165  ::std::lock_guard<util::BasicLockable> lock(*this);
166 
167  if (co_nmt_cs_req(nmt(), static_cast<uint8_t>(cs), id) == -1)
168  util::throw_errc("Command");
169 }
170 
171 void
172 BasicMaster::RpdoRtr(int num) noexcept {
173  ::std::lock_guard<util::BasicLockable> lock(*this);
174 
175  Node::RpdoRtr(num);
176 }
177 
178 void
179 BasicMaster::TpdoEvent(int num) noexcept {
180  ::std::lock_guard<util::BasicLockable> lock(*this);
181 
182  Node::TpdoEvent(num);
183 }
184 
185 ::std::chrono::milliseconds
187  ::std::lock_guard<util::BasicLockable> lock(const_cast<BasicMaster&>(*this));
188 
190 }
191 
192 void
193 BasicMaster::SetTimeout(const ::std::chrono::milliseconds& timeout) {
194  ::std::lock_guard<util::BasicLockable> lock(*this);
195 
197 }
198 
199 void
201  ::std::error_code ec;
202  SubmitWriteDcf(id, req, ec);
203  if (ec) throw SdoError(id, req.idx, req.subidx, ec, "SubmitWriteDcf");
204 }
205 
206 void
208  ::std::error_code& ec) {
209  ::std::lock_guard<BasicLockable> lock(*this);
210 
211  ec.clear();
212  auto sdo = GetSdo(id);
213  if (sdo) {
214  SetTime();
215  sdo->SubmitDownloadDcf(req);
216  } else {
217  ec = SdoErrc::NO_SDO;
218  }
219 }
220 
222 BasicMaster::AsyncWriteDcf(ev_exec_t* exec, uint8_t id, const uint8_t* begin,
223  const uint8_t* end,
224  const ::std::chrono::milliseconds& timeout) {
225  if (!exec) exec = GetExecutor();
226 
227  ::std::lock_guard<BasicLockable> lock(*this);
228 
229  auto sdo = GetSdo(id);
230  if (sdo) {
231  SetTime();
232  return sdo->AsyncDownloadDcf(exec, begin, end, timeout);
233  } else {
234  return make_error_sdo_future<void>(id, 0, 0, SdoErrc::NO_SDO,
235  "AsyncWriteDcf");
236  }
237 }
238 
240 BasicMaster::AsyncWriteDcf(ev_exec_t* exec, uint8_t id, const char* path,
241  const ::std::chrono::milliseconds& timeout) {
242  if (!exec) exec = GetExecutor();
243 
244  ::std::lock_guard<BasicLockable> lock(*this);
245 
246  auto sdo = GetSdo(id);
247  if (sdo) {
248  SetTime();
249  return sdo->AsyncDownloadDcf(exec, path, timeout);
250  } else {
251  return make_error_sdo_future<void>(id, 0, 0, SdoErrc::NO_SDO,
252  "AsyncWriteDcf");
253  }
254 }
255 
256 void
258  ::std::lock_guard<util::BasicLockable> lock(*this);
259 
260  if (!driver.id() || driver.id() > 0x7f)
261  throw ::std::out_of_range("invalid node-ID: " +
262  ::std::to_string(driver.id()));
263  if (driver.id() == co_dev_get_id(co_nmt_get_dev(nmt())))
264  throw ::std::out_of_range("cannot register node-ID of master: " +
265  ::std::to_string(driver.id()));
266  if (find(driver.id()) != end())
267  throw ::std::out_of_range("node-ID " + ::std::to_string(driver.id()) +
268  " already registered");
269 
270  MapType::operator[](driver.id()) = &driver;
271 }
272 
273 void
275  ::std::lock_guard<util::BasicLockable> lock(*this);
276 
277  auto id = driver.id();
278  auto it = find(id);
279  if (it != end() && it->second == &driver) {
280  CancelSdo(id);
281  erase(it);
282  }
283 }
284 
285 void
287  ::std::function<void(uint8_t, bool)> on_node_guarding) {
288  ::std::lock_guard<util::BasicLockable> lock(*this);
289  impl_->on_node_guarding = on_node_guarding;
290 }
291 
292 void
294  ::std::function<void(uint8_t, NmtState, char, const ::std::string&)>
295  on_boot) {
296  ::std::lock_guard<util::BasicLockable> lock(*this);
297  impl_->on_boot = on_boot;
298 }
299 
300 void
302  io::CanState old_state) noexcept {
303  for (const auto& it : *this) {
305  it.second->OnCanState(new_state, old_state);
306  }
307 }
308 
309 void
311  for (const auto& it : *this) {
313  it.second->OnCanError(error);
314  }
315 }
316 
317 void
318 BasicMaster::OnRpdoWrite(uint8_t id, uint16_t idx, uint8_t subidx) noexcept {
319  auto it = find(id);
320  if (it != end()) {
322  it->second->OnRpdoWrite(idx, subidx);
323  }
324 }
325 
326 void
328  // Abort all ongoing and pending SDO requests unless the master is in the
329  // pre-operational or operational state.
330  if (cs != NmtCommand::ENTER_PREOP && cs != NmtCommand::START) CancelSdo();
331  for (const auto& it : *this) {
333  it.second->OnCommand(cs);
334  }
335 }
336 
337 void
338 BasicMaster::OnHeartbeat(uint8_t id, bool occurred) noexcept {
339  auto it = find(id);
340  if (it != end()) {
342  it->second->OnHeartbeat(occurred);
343  }
344 }
345 
346 void
347 BasicMaster::OnState(uint8_t id, NmtState st) noexcept {
348  if (st == NmtState::BOOTUP && !IsConfig(id)) {
349  IsReady(id, false);
350  // Abort any ongoing or pending SDO requests for the slave, since the master
351  // MAY need the Client-SDO service for the NMT 'boot slave' process.
352  CancelSdo(id);
353  }
354  auto it = find(id);
355  if (it != end()) {
357  it->second->OnState(st);
358  }
359 }
360 
361 void
362 BasicMaster::OnSync(uint8_t cnt, const time_point& t) noexcept {
363  for (const auto& it : *this) {
365  it.second->OnSync(cnt, t);
366  }
367 }
368 
369 void
370 BasicMaster::OnSyncError(uint16_t eec, uint8_t er) noexcept {
371  for (const auto& it : *this) {
373  it.second->OnSyncError(eec, er);
374  }
375 }
376 
377 void
379  const ::std::chrono::system_clock::time_point& abs_time) noexcept {
380  for (const auto& it : *this) {
382  it.second->OnTime(abs_time);
383  }
384 }
385 
386 void
387 BasicMaster::OnEmcy(uint8_t id, uint16_t eec, uint8_t er,
388  uint8_t msef[5]) noexcept {
389  auto it = find(id);
390  if (it != end()) {
392  it->second->OnEmcy(eec, er, msef);
393  }
394 }
395 
396 void
397 BasicMaster::OnNodeGuarding(uint8_t id, bool occurred) noexcept {
398  auto it = find(id);
399  if (it != end()) {
401  it->second->OnNodeGuarding(occurred);
402  }
403 }
404 
405 void
406 BasicMaster::OnBoot(uint8_t id, NmtState st, char es,
407  const ::std::string& what) noexcept {
408  auto it = find(id);
409  if (it != end()) {
411  it->second->OnBoot(st, es, what);
412  }
413 }
414 
415 void
416 BasicMaster::IsReady(uint8_t id, bool ready) noexcept {
417  if (id && id <= CO_NUM_NODES) impl_->ready[id - 1] = ready;
418 }
419 
420 void
421 BasicMaster::OnConfig(uint8_t id) noexcept {
422 #if LELY_NO_CO_NMT_CFG
423  ConfigResult(id, ::std::error_code());
424 #else
425  auto it = find(id);
426  // If no remote interface is registered for this node, the 'update
427  // configuration' process is considered complete.
428  if (it == end()) {
429  ConfigResult(id, ::std::error_code());
430  return;
431  }
432  // Let the driver perform the configuration update.
434  it->second->OnConfig([this, id](::std::error_code ec) {
435  ::std::lock_guard<util::BasicLockable> lock(*this);
436  // Report the result of the 'update configuration' process.
437  ConfigResult(id, ec);
438  });
439 #endif
440 }
441 
442 void
443 BasicMaster::ConfigResult(uint8_t id, ::std::error_code ec) noexcept {
444  assert(id && id <= CO_NUM_NODES);
445 
446 #if LELY_NO_CO_NMT_CFG
447  (void)id;
448  (void)ec;
449 #else
450  impl_->config[id - 1] = false;
451 #if !LELY_NO_CO_NMT_BOOT
452  if (co_nmt_is_booting(nmt(), id))
453  // Destroy the Client-SDO, since it will be taken over by the master.
454  impl_->sdos.erase(id);
455 #endif
456  // Ignore any errors, since we cannot handle them here.
457  co_nmt_cfg_res(nmt(), id, static_cast<uint32_t>(sdo_errc(ec)));
458 #endif
459 }
460 
461 bool
462 BasicMaster::IsConfig(uint8_t id) const {
463  if (!id || id > CO_NUM_NODES) return false;
464 
465 #if LELY_NO_CO_NMT_CFG
466  return false;
467 #else
468  return impl_->config[id - 1];
469 #endif
470 }
471 
472 Sdo*
473 BasicMaster::GetSdo(uint8_t id) {
474  if (!id || id > 0x7f)
475  throw ::std::out_of_range("invalid node-ID: " + ::std::to_string(id));
476  // The Client-SDO service only exists in the pre-operational and operational
477  // state.
478  auto st = co_nmt_get_st(nmt());
479  if (st != CO_NMT_ST_PREOP && st != CO_NMT_ST_START) return nullptr;
480  // During the 'update configuration' step of the NMT 'boot slave' process, a
481  // Client-SDO queue may be available.
482  auto it = impl_->sdos.find(id);
483  if (it != impl_->sdos.end()) return &it->second;
484 #if !LELY_NO_CO_NMT_BOOT
485  // The master needs the Client-SDO service during the NMT 'boot slave'
486  // process.
487  if (co_nmt_is_booting(nmt(), id)) return nullptr;
488 #endif
489  // Return a Client-SDO queue for the default SDO.
490  return &(impl_->sdos[id] = Sdo(co_nmt_get_net(nmt()), id));
491 }
492 
493 void
495  if (id)
496  impl_->sdos.erase(id);
497  else
498  impl_->sdos.clear();
499 }
500 
501 void
503  io::CanState old_state) noexcept {
504  for (const auto& it : *this) {
505  DriverBase* driver = it.second;
506  driver->GetExecutor().post(
507  [=]() { driver->OnCanState(new_state, old_state); });
508  }
509 }
510 
511 void
513  for (const auto& it : *this) {
514  DriverBase* driver = it.second;
515  driver->GetExecutor().post([=]() { driver->OnCanError(error); });
516  }
517 }
518 
519 void
520 AsyncMaster::OnRpdoWrite(uint8_t id, uint16_t idx, uint8_t subidx) noexcept {
521  auto it = find(id);
522  if (it != end()) {
523  DriverBase* driver = it->second;
524  driver->GetExecutor().post([=]() { driver->OnRpdoWrite(idx, subidx); });
525  }
526 }
527 
528 void
530  // Abort all ongoing and pending SDO requests unless the master is in the
531  // pre-operational or operational state.
532  if (cs != NmtCommand::ENTER_PREOP && cs != NmtCommand::START) CancelSdo();
533  for (const auto& it : *this) {
534  DriverBase* driver = it.second;
535  driver->GetExecutor().post([=]() { driver->OnCommand(cs); });
536  }
537 }
538 
539 void
540 AsyncMaster::OnHeartbeat(uint8_t id, bool occurred) noexcept {
541  auto it = find(id);
542  if (it != end()) {
543  DriverBase* driver = it->second;
544  driver->GetExecutor().post([=]() { driver->OnHeartbeat(occurred); });
545  }
546 }
547 
548 void
549 AsyncMaster::OnState(uint8_t id, NmtState st) noexcept {
550  if (st == NmtState::BOOTUP && !IsConfig(id)) {
551  IsReady(id, false);
552  // Abort any ongoing or pending SDO requests for the slave, since the master
553  // MAY need the Client-SDO service for the NMT 'boot slave' process.
554  CancelSdo(id);
555  }
556  auto it = find(id);
557  if (it != end()) {
558  DriverBase* driver = it->second;
559  driver->GetExecutor().post([=]() { driver->OnState(st); });
560  }
561 }
562 
563 void
564 AsyncMaster::OnSync(uint8_t cnt, const time_point& t) noexcept {
565  for (const auto& it : *this) {
566  DriverBase* driver = it.second;
567  driver->GetExecutor().post([=]() { driver->OnSync(cnt, t); });
568  }
569 }
570 
571 void
572 AsyncMaster::OnSyncError(uint16_t eec, uint8_t er) noexcept {
573  for (const auto& it : *this) {
574  DriverBase* driver = it.second;
575  driver->GetExecutor().post([=]() { driver->OnSyncError(eec, er); });
576  }
577 }
578 
579 void
581  const ::std::chrono::system_clock::time_point& abs_time) noexcept {
582  for (const auto& it : *this) {
583  DriverBase* driver = it.second;
584  driver->GetExecutor().post([=]() { driver->OnTime(abs_time); });
585  }
586 }
587 
588 void
589 AsyncMaster::OnEmcy(uint8_t id, uint16_t eec, uint8_t er,
590  uint8_t msef[5]) noexcept {
591  ::std::array<uint8_t, 5> value = {0};
592  ::std::copy_n(msef, value.size(), value.begin());
593  auto it = find(id);
594  if (it != end()) {
595  DriverBase* driver = it->second;
596  driver->GetExecutor().post(
597  [=]() mutable { driver->OnEmcy(eec, er, value.data()); });
598  }
599 }
600 
601 void
602 AsyncMaster::OnNodeGuarding(uint8_t id, bool occurred) noexcept {
603  auto it = find(id);
604  if (it != end()) {
605  DriverBase* driver = it->second;
606  driver->GetExecutor().post([=]() { driver->OnNodeGuarding(occurred); });
607  }
608 }
609 
610 void
611 AsyncMaster::OnBoot(uint8_t id, NmtState st, char es,
612  const ::std::string& what) noexcept {
613  auto it = find(id);
614  if (it != end()) {
615  DriverBase* driver = it->second;
616  driver->GetExecutor().post([=]() { driver->OnBoot(st, es, what); });
617  }
618 }
619 
620 void
621 AsyncMaster::OnConfig(uint8_t id) noexcept {
622  auto it = find(id);
623  // If no remote interface is registered for this node, the 'update
624  // configuration' process is considered complete.
625  if (it == end()) {
626  ConfigResult(id, ::std::error_code());
627  return;
628  }
629 
630  // Let the driver perform the configuration update.
631  DriverBase* driver = it->second;
632  driver->GetExecutor().post([this, id, driver]() {
633  driver->OnConfig([this, id](::std::error_code ec) {
634  ::std::lock_guard<util::BasicLockable> lock(*this);
635  ConfigResult(id, ec);
636  });
637  });
638 }
639 
640 BasicMaster::Impl_::Impl_(BasicMaster* self_, co_nmt_t* nmt) : self(self_) {
641 #if !LELY_NO_CO_NG
643  nmt,
644  [](co_nmt_t* nmt, uint8_t id, int state, int reason,
645  void* data) noexcept {
646  static_cast<Impl_*>(data)->OnNgInd(nmt, id, state, reason);
647  },
648  this);
649 #endif
650 
651 #if !LELY_NO_CO_NMT_BOOT
653  nmt,
654  [](co_nmt_t* nmt, uint8_t id, uint8_t st, char es, void* data) noexcept {
655  static_cast<Impl_*>(data)->OnBootInd(nmt, id, st, es);
656  },
657  this);
658 #endif
659 
660 #if !LELY_NO_CO_NMT_CFG
662  nmt,
663  [](co_nmt_t* nmt, uint8_t id, co_csdo_t* sdo, void* data) noexcept {
664  static_cast<Impl_*>(data)->OnCfgInd(nmt, id, sdo);
665  },
666  this);
667 #endif
668 }
669 
670 ev::Future<void>
671 BasicMaster::Impl_::AsyncDeconfig(DriverBase* driver) {
672  self->IsReady(driver->id(), false);
673  ev::Promise<void> p;
674  driver->GetExecutor().post([=]() mutable {
675  driver->OnDeconfig([p](::std::error_code ec) mutable { p.set(ec); });
676  });
677  return p.get_future();
678 }
679 
680 #if !LELY_NO_CO_NG
681 void
682 BasicMaster::Impl_::OnNgInd(co_nmt_t* nmt, uint8_t id, int state,
683  int reason) noexcept {
684  // Invoke the default behavior before notifying the implementation.
685  co_nmt_on_ng(nmt, id, state, reason);
686  // Only handle node guarding timeout events. State changes are handled by
687  // OnSt().
688  if (reason != CO_NMT_EC_TIMEOUT) return;
689  // Notify the implementation.
690  bool occurred = state == CO_NMT_EC_OCCURRED;
691  self->OnNodeGuarding(id, occurred);
692  if (on_node_guarding) {
693  auto f = on_node_guarding;
694  util::UnlockGuard<util::BasicLockable> unlock(*self);
695  f(id, occurred);
696  }
697 }
698 #endif
699 
700 #if !LELY_NO_CO_NMT_BOOT
701 void
702 BasicMaster::Impl_::OnBootInd(co_nmt_t*, uint8_t id, uint8_t st,
703  char es) noexcept {
704  if (id && id <= CO_NUM_NODES && (!es || es == 'L')) ready[id - 1] = true;
705  ::std::string what = es ? co_nmt_es2str(es) : "";
706  self->OnBoot(id, static_cast<NmtState>(st), es, what);
707  if (on_boot) {
708  auto f = on_boot;
709  util::UnlockGuard<util::BasicLockable> unlock(*self);
710  f(id, static_cast<NmtState>(st), es, what);
711  }
712 }
713 #endif
714 
715 #if !LELY_NO_CO_NMT_CFG
716 void
717 BasicMaster::Impl_::OnCfgInd(co_nmt_t*, uint8_t id, co_csdo_t* sdo) noexcept {
718  // Create a Client-SDO for the 'update configuration' process.
719  try {
720  sdos[id] = Sdo(sdo);
721  } catch (...) {
722  self->ConfigResult(id, SdoErrc::ERROR);
723  return;
724  }
725  config[id - 1] = true;
726  self->OnConfig(id);
727 }
728 #endif
729 
730 } // namespace canopen
731 
732 } // namespace lely
733 
734 #endif // !LELY_NO_COAPP_MASTER
void OnCanState(::std::function< void(io::CanState, io::CanState)> on_can_state)
Definition: master.hpp:1963
void OnTime(::std::function< void(const ::std::chrono::system_clock::time_point &)> on_time)
Definition: master.hpp:2011
void OnCanError(::std::function< void(io::CanError)> on_can_error)
Definition: master.hpp:1969
void OnNodeGuarding(::std::function< void(uint8_t, bool)> on_node_guarding)
Definition: master.hpp:2025
void OnEmcy(::std::function< void(uint8_t, uint16_t, uint8_t, uint8_t[5])> on_emcy)
Definition: master.hpp:2018
void OnConfig(uint8_t id) noexcept override
The default implementation queues a notification for the driver registered for node id.
Definition: master.cpp:621
void OnBoot(::std::function< void(uint8_t, NmtState, char, const ::std::string &)> on_boot)
Definition: master.hpp:2031
void OnState(::std::function< void(uint8_t, NmtState)> on_state)
Definition: master.hpp:1993
void OnSyncError(::std::function< void(uint16_t, uint8_t)> on_sync_error)
Definition: master.hpp:2005
void OnRpdoWrite(::std::function< void(uint8_t, uint16_t, uint8_t)> on_rpdo_write)
Definition: master.hpp:1975
void OnCommand(::std::function< void(NmtCommand)> on_command)
Definition: master.hpp:1981
void OnSync(::std::function< void(uint8_t, const time_point &)> on_sync)
Definition: master.hpp:1999
void OnHeartbeat(::std::function< void(uint8_t, bool)> on_heartbeat)
Definition: master.hpp:1987
void lock() override
Blocks until a lock can be obtained for the current execution agent (thread, process,...
Definition: master.cpp:70
void unlock() override
Releases the lock held by the execution agent. Throws no exceptions.
Definition: master.cpp:76
The CANopen master.
Definition: master.hpp:50
void OnState(::std::function< void(uint8_t, NmtState)> on_state)
Definition: master.hpp:1691
bool IsConfig(uint8_t id) const
Returns true if the remote node is configuring (i.e., the 'update configuration' step of the NMT 'boo...
Definition: master.cpp:462
void TpdoEvent(int num=0) noexcept
Definition: master.cpp:179
void SubmitWriteDcf(uint8_t id, SdoDownloadDcfRequest &req)
Equivalent to SubmitWriteDcf(uint8_t id, SdoDownloadDcfRequest& req, ::std::error_code& ec),...
Definition: master.cpp:200
ev::Future<::std::size_t, void > AsyncDeconfig()
Queues the DriverBase::OnDeconfig() method for all registered drivers and creates a future which beco...
Definition: master.cpp:135
virtual void OnConfig(uint8_t id) noexcept
The function invoked when the 'update configuration' step is reached during the NMT 'boot slave' proc...
Definition: master.cpp:421
void OnTime(::std::function< void(const ::std::chrono::system_clock::time_point &)> on_time)
Definition: master.hpp:1709
void RpdoRtr(int num=0) noexcept
Definition: master.cpp:172
void OnHeartbeat(::std::function< void(uint8_t, bool)> on_heartbeat)
Definition: master.hpp:1685
void Command(NmtCommand cs, uint8_t id=0)
Issues an NMT command to a slave.
Definition: master.cpp:164
BasicMaster(ev_exec_t *exec, io::TimerBase &timer, io::CanChannelBase &chan, const ::std::string &dcf_txt, const ::std::string &dcf_bin="", uint8_t id=0xff)
Creates a new CANopen master.
Definition: master.cpp:81
void OnCanState(::std::function< void(io::CanState, io::CanState)> on_can_state)
Definition: master.hpp:1661
void Erase(DriverBase &driver)
Unregisters a driver for a remote CANopen node.
Definition: master.cpp:274
void OnBoot(::std::function< void(uint8_t, NmtState, char, const ::std::string &)> on_boot)
Registers the function invoked when the NMT 'boot slave' process completes.
Definition: master.cpp:293
void Insert(DriverBase &driver)
Registers a driver for a remote CANopen node.
Definition: master.cpp:257
void Error(uint8_t id)
Indicates the occurrence of an error event on a remote node and triggers the error handling process (...
Definition: master.cpp:150
bool Boot(uint8_t id)
Requests the NMT 'boot slave' process for the specified node.
Definition: master.cpp:91
void OnNodeGuarding(::std::function< void(uint8_t, bool)> on_node_guarding)
Registers the function invoked when a node guarding timeout event occurs or is resolved.
Definition: master.cpp:286
SdoFuture< void > AsyncWriteDcf(ev_exec_t *exec, uint8_t id, const uint8_t *begin, const uint8_t *end)
Equivalent to AsyncWriteDcf(ev_exec_t* exec, uint8_t id, const uint8_t* begin, const uint8_t* end,...
Definition: master.hpp:1586
bool IsReady(uint8_t id) const
Returns true if the remote node is ready (i.e., the NMT 'boot slave' process has successfully complet...
Definition: master.cpp:117
::std::chrono::milliseconds GetTimeout() const
Returns the SDO timeout used during the NMT 'boot slave' and 'check configuration' processes.
Definition: master.cpp:186
void OnEmcy(::std::function< void(uint8_t, uint16_t, uint8_t, uint8_t[5])> on_emcy)
Definition: master.hpp:1716
void SetTimeout(const ::std::chrono::milliseconds &timeout)
Sets the SDO timeout used during the NMT 'boot slave' and 'check configuration' processes.
Definition: master.cpp:193
void OnCanError(::std::function< void(io::CanError)> on_can_error)
Definition: master.hpp:1667
TpdoEventMutex tpdo_event_mutex
Definition: master.hpp:1740
void ConfigResult(uint8_t id, ::std::error_code ec) noexcept
Reports the result of the 'update configuration' step to the NMT service.
Definition: master.cpp:443
void CancelSdo(uint8_t id=0)
Aborts any ongoing or pending SDO requests for the specified slave.
Definition: master.cpp:494
void OnSync(::std::function< void(uint8_t, const time_point &)> on_sync)
Definition: master.hpp:1697
void OnRpdoWrite(::std::function< void(uint8_t, uint16_t, uint8_t)> on_rpdo_write)
Definition: master.hpp:1673
void OnCommand(::std::function< void(NmtCommand)> on_command)
Definition: master.hpp:1679
Sdo * GetSdo(uint8_t id)
Returns a pointer to the default client-SDO service for the given node.
Definition: master.cpp:473
void OnSyncError(::std::function< void(uint16_t, uint8_t)> on_sync_error)
Definition: master.hpp:1703
uint8_t id() const noexcept
Returns the node-ID.
Definition: device.cpp:193
The abstract driver interface for a remote CANopen node.
Definition: driver.hpp:36
virtual void OnCommand(NmtCommand cs) noexcept=0
The function invoked when an NMT state change occurs on the master.
virtual void OnSync(uint8_t cnt, const time_point &t) noexcept=0
The function invoked when a SYNC message is sent/received by the master.
virtual void OnNodeGuarding(bool occurred) noexcept=0
The function invoked when a node guarding timeout event occurs or is resolved for the remote node.
virtual void OnHeartbeat(bool occurred) noexcept=0
The function invoked when a heartbeat timeout event occurs or is resolved for the remote node.
virtual void OnConfig(::std::function< void(::std::error_code ec)> res) noexcept=0
The function invoked when the 'update configuration' step is reached during the NMT 'boot slave' proc...
virtual void OnCanError(io::CanError error) noexcept=0
The function invoked when an error is detected on the CAN bus.
virtual ev::Executor GetExecutor() const noexcept=0
Returns the executor used to execute event handlers for this driver, including SDO confirmation funct...
virtual void OnTime(const ::std::chrono::system_clock::time_point &abs_time) noexcept=0
The function invoked when a TIME message is received by the master.
virtual void OnSyncError(uint16_t eec, uint8_t er) noexcept=0
The function invoked when the data length of a received SYNC message does not match.
virtual void OnState(NmtState st) noexcept=0
The function invoked when an NMT state change or boot-up event is detected for the remote node by the...
virtual uint8_t id() const noexcept=0
Returns the node-ID.
virtual void OnCanState(io::CanState new_state, io::CanState old_state) noexcept=0
The function invoked when a CAN bus state change is detected.
virtual void OnEmcy(uint16_t eec, uint8_t er, uint8_t msef[5]) noexcept=0
The function invoked when an EMCY message is received from the remote node.
virtual void OnBoot(NmtState st, char es, const ::std::string &what) noexcept=0
The function invoked when the NMT 'boot slave' process completes for the remote node.
void lock() override
Blocks until a lock can be obtained for the current execution agent (thread, process,...
Definition: node.cpp:396
void unlock() override
Releases the lock held by the execution agent. Throws no exceptions.
Definition: node.cpp:403
The base class for CANopen nodes.
Definition: node.hpp:116
void SetTime()
Updates the CAN network time.
Definition: node.cpp:415
__co_nmt * nmt() const noexcept
Returns a pointer to the internal CANopen NMT master/slave service from <lely/co/nmt....
Definition: node.cpp:434
void TpdoEvent(int num=0) noexcept
Triggers the transmission of an acyclic or event-driven PDO.
Definition: node.cpp:457
ev::Executor GetExecutor() const noexcept
Returns the executor used to process I/O and CANopen events.
Definition: node.cpp:159
void RpdoRtr(int num=0) noexcept
Requests the transmission of a PDO by sending a CAN frame with the RTR (Remote Transmission Request) ...
Definition: node.cpp:444
A series of SDO download (i.e., write) requests corresponding to the entries in a concise DCF.
Definition: sdo.hpp:318
The type of exception thrown when an SDO abort code is received.
Definition: sdo_error.hpp:121
A Client-SDO queue.
Definition: sdo.hpp:588
uint16_t idx
The object index.
Definition: sdo.hpp:193
uint8_t subidx
The object sub-index.
Definition: sdo.hpp:195
The type of objects thrown as exceptions to report a system error with an associated error code.
Definition: exception.hpp:54
void post(ev_task &task) noexcept
Definition: exec.hpp:75
A future.
Definition: future.hpp:384
A reference to an abstract CAN channel.
Definition: can.hpp:430
void unlock() final
Releases the lock held by the execution agent. Throws no exceptions.
Definition: can_net.hpp:109
void lock() final
Blocks until a lock can be obtained for the current execution agent (thread, process,...
Definition: can_net.hpp:104
A reference to an abstract timer.
Definition: timer.hpp:130
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.
co_unsigned8_t co_dev_get_id(const co_dev_t *dev)
Returns the node-ID of a CANopen device.
Definition: dev.c:197
#define CO_NUM_NODES
The maximum number of nodes in a CANopen network.
Definition: dev.h:56
This is the internal header file of the C++ CANopen application library.
This header file is part of the C++ CANopen application library; it contains the remote node driver i...
CanError
The error flags of a CAN bus, which are not mutually exclusive.
Definition: err.hpp:47
CanState
The states of a CAN node, depending on the TX/RX error count.
Definition: err.hpp:33
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
Definition: ev.h:29
inline ::std::chrono::milliseconds from_sdo_timeout(int timeout)
Converts an SDO timeout to a duration.
Definition: sdo.hpp:140
int to_sdo_timeout(const ::std::chrono::duration< Rep, Period > &d)
Converts a duration to an SDO timeout.
Definition: sdo.hpp:148
NmtState
The NMT states.
Definition: node.hpp:56
@ ERROR
General error.
@ NO_SDO
Resource not available: SDO connection.
SdoErrc sdo_errc(::std::error_code ec)
Returns the SDO abort code corresponding to an error code.
Definition: sdo_error.cpp:180
NmtCommand
The NMT command specifiers.
Definition: node.hpp:42
@ ENTER_PREOP
Enter pre-operational.
This header file is part of the CANopen library; it contains the network management (NMT) declaration...
can_net_t * co_nmt_get_net(const co_nmt_t *nmt)
Returns a pointer to the CAN network of an NMT master/slave service.
Definition: nmt.c:1267
void co_nmt_set_cfg_ind(co_nmt_t *nmt, co_nmt_cfg_ind_t *ind, void *data)
Sets the indication function invoked when a CANopen NMT 'configuration request' process is received.
Definition: nmt.c:1537
#define CO_NMT_ST_PREOP
The NMT state 'pre-operational'.
Definition: nmt.h:70
co_dev_t * co_nmt_get_dev(const co_nmt_t *nmt)
Returns a pointer to the CANopen device of an NMT master/slave service.
Definition: nmt.c:1275
int co_nmt_get_timeout(const co_nmt_t *nmt)
Returns the default SDO timeout used during the NMT 'boot slave' and 'check configuration' processes.
Definition: nmt.c:1790
void co_nmt_set_boot_ind(co_nmt_t *nmt, co_nmt_boot_ind_t *ind, void *data)
Sets the indication function invoked when a CANopen NMT 'boot slave' process completes.
Definition: nmt.c:1513
int co_nmt_is_booting(const co_nmt_t *nmt, co_unsigned8_t id)
Returns 1 if the NMT 'boot slave' process is currently running for the specified node,...
Definition: nmt.c:1929
int co_nmt_boot_req(co_nmt_t *nmt, co_unsigned8_t id, int timeout)
Requests the NMT 'boot slave' process for the specified node.
Definition: nmt.c:1871
@ CO_NMT_EC_TIMEOUT
An NMT error control timeout event.
Definition: nmt.h:87
#define CO_NMT_ST_START
The NMT state 'operational'.
Definition: nmt.h:61
co_unsigned8_t co_nmt_get_st(const co_nmt_t *nmt)
Returns the current state of a CANopen NMT service (one of CO_NMT_ST_BOOTUP, CO_NMT_ST_STOP,...
Definition: nmt.c:1766
int co_nmt_cfg_res(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac)
Indicates the result of the 'update configuration' step of an NMT 'request configuration' request for...
Definition: nmt.c:2040
void co_nmt_set_timeout(co_nmt_t *nmt, int timeout)
Sets the default SDO timeout used during the NMT 'boot slave' and 'check configuration' processes.
Definition: nmt.c:1798
int co_nmt_node_err_ind(co_nmt_t *nmt, co_unsigned8_t id)
Indicates the occurrence of an error event and triggers the error handling process (see Fig.
Definition: nmt.c:2132
void co_nmt_set_ng_ind(co_nmt_t *nmt, co_nmt_ng_ind_t *ind, void *data)
Sets the indication function invoked when a node guarding event occurs.
Definition: nmt.c:1318
int co_nmt_cs_req(co_nmt_t *nmt, co_unsigned8_t cs, co_unsigned8_t id)
Submits an NMT request to a slave.
Definition: nmt.c:1806
void co_nmt_on_ng(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason)
Implements the default behavior when a node guarding event occurs (see sections 7....
Definition: nmt.c:1327
const char * co_nmt_es2str(char es)
Returns a pointer to a string describing an NMT boot error status.
Definition: nmt.c:786
@ CO_NMT_EC_OCCURRED
An NMT error control event occurred.
Definition: nmt.h:80
A CANopen Client-SDO.
Definition: csdo.c:71
A CANopen NMT master/slave service.
Definition: nmt.c:148
The internal implementation of the CANopen master.
Definition: master.cpp:44