26 #if !LELY_NO_COAPP_MASTER 50 void OnNgInd(
CONMT* nmt, uint8_t
id,
int state,
int reason) noexcept;
51 void OnBootInd(
CONMT* nmt, uint8_t
id, uint8_t st,
char es) noexcept;
52 void OnCfgInd(
CONMT* nmt, uint8_t
id,
COCSDO* sdo) noexcept;
55 ::std::function<void(uint8_t, bool)> on_node_guarding;
56 ::std::function<void(uint8_t, NmtState, char, const ::std::string&)> on_boot;
57 ::std::array<bool, CO_NUM_NODES> ready{{
false}};
58 ::std::array<bool, CO_NUM_NODES> config{{
false}};
59 ::std::map<uint8_t, Sdo> sdos;
64 ::std::lock_guard<util::BasicLockable>
lock(*node);
70 ::std::lock_guard<util::BasicLockable>
lock(*node);
76 const ::std::string& dcf_bin, uint8_t
id)
77 :
Node(exec, timer, chan, dcf_txt, dcf_bin, id),
81 BasicMaster::~BasicMaster() =
default;
87 ::std::lock_guard<util::BasicLockable>
lock(*
this);
95 auto ready = impl_->ready[
id - 1];
96 impl_->ready[
id - 1] =
false;
98 impl_->ready[
id - 1] = ready;
99 util::throw_errc(
"Boot");
109 ::std::lock_guard<util::BasicLockable>
lock(const_cast<BasicMaster&>(*
this));
110 return impl_->ready[
id - 1];
116 ::std::lock_guard<util::BasicLockable>
lock(*
this);
118 if (it != end())
return impl_->AsyncDeconfig(it->second);
120 return ev::make_empty_future();
128 ::std::lock_guard<util::BasicLockable>
lock(*
this);
129 for (
const auto& it : *
this) futures[n++] = impl_->AsyncDeconfig(it.second);
133 ::std::array<ev_future_t*, CO_NUM_NODES> tmp;
134 ::std::copy_n(futures.begin(), n, tmp.begin());
140 ::std::lock_guard<util::BasicLockable>
lock(*
this);
142 if (
nmt()->nodeErrInd(
id) == -1) util::throw_errc(
"Error");
147 ::std::lock_guard<util::BasicLockable>
lock(*
this);
149 Node::Error(eec, er, msef);
154 ::std::lock_guard<util::BasicLockable>
lock(*
this);
156 if (
nmt()->csReq(static_cast<uint8_t>(cs),
id) == -1)
157 util::throw_errc(
"Command");
162 ::std::lock_guard<util::BasicLockable>
lock(*
this);
169 ::std::lock_guard<util::BasicLockable>
lock(*
this);
174 ::std::chrono::milliseconds
176 ::std::lock_guard<util::BasicLockable>
lock(const_cast<BasicMaster&>(*
this));
183 ::std::lock_guard<util::BasicLockable>
lock(*
this);
190 ::std::lock_guard<util::BasicLockable>
lock(*
this);
192 if (!driver.
id() || driver.
id() > 0x7f)
193 throw ::std::out_of_range(
"invalid node-ID: " +
194 ::std::to_string(driver.
id()));
195 if (driver.
id() ==
nmt()->getDev()->getId())
196 throw ::std::out_of_range(
"cannot register node-ID of master: " +
197 ::std::to_string(driver.
id()));
198 if (find(driver.
id()) != end())
199 throw ::std::out_of_range(
"node-ID " + ::std::to_string(driver.
id()) +
200 " already registered");
202 MapType::operator[](driver.
id()) = &driver;
207 ::std::lock_guard<util::BasicLockable>
lock(*
this);
209 auto id = driver.
id();
211 if (it != end() && it->second == &driver) {
219 ::std::function<
void(uint8_t,
bool)> on_node_guarding) {
220 ::std::lock_guard<util::BasicLockable>
lock(*
this);
221 impl_->on_node_guarding = on_node_guarding;
226 ::std::function<
void(uint8_t,
NmtState,
char, const ::std::string&)>
228 ::std::lock_guard<util::BasicLockable>
lock(*
this);
229 impl_->on_boot = on_boot;
234 if (
id &&
id <=
CO_NUM_NODES) impl_->ready[
id - 1] = ready;
239 for (
const auto& it : *
this) {
241 it.second->OnCanError(
error);
248 for (
const auto& it : *
this) {
250 it.second->OnCanState(new_state, old_state);
259 it->second->OnRpdoWrite(idx, subidx);
268 for (
const auto& it : *
this) {
270 it.second->OnCommand(cs);
279 it->second->OnNodeGuarding(occurred);
288 it->second->OnHeartbeat(occurred);
303 it->second->OnState(st);
309 const ::std::string& what) noexcept {
313 it->second->OnBoot(st, es, what);
328 it->second->OnConfig([
this,
id](::std::error_code ec) {
329 ::std::lock_guard<util::BasicLockable>
lock(*
this);
338 impl_->config[
id - 1] =
false;
339 if (
nmt()->isBooting(
id))
341 impl_->sdos.erase(
id);
343 nmt()->cfgRes(
id, static_cast<uint32_t>(
sdo_errc(ec)));
348 for (
const auto& it : *
this) {
350 it.second->OnSync(cnt, t);
356 for (
const auto& it : *
this) {
358 it.second->OnSyncError(eec, er);
364 const ::std::chrono::system_clock::time_point& abs_time) noexcept {
365 for (
const auto& it : *
this) {
367 it.second->OnTime(abs_time);
373 uint8_t msef[5]) noexcept {
377 it->second->OnEmcy(eec, er, msef);
385 return impl_->config[
id - 1];
390 if (!
id ||
id > 0x7f)
391 throw ::std::out_of_range(
"invalid node-ID: " + ::std::to_string(
id));
394 auto st =
nmt()->getSt();
398 auto it = impl_->sdos.find(
id);
399 if (it != impl_->sdos.end())
return &it->second;
402 if (
nmt()->isBooting(
id))
return nullptr;
404 return &(impl_->sdos[
id] =
Sdo(
nmt()->getNet(),
id));
410 impl_->sdos.erase(
id);
417 for (
const auto& it : *
this) {
426 for (
const auto& it : *
this) {
429 [=]() { driver->
OnCanState(new_state, old_state); });
438 driver->
GetExecutor().
post([=]() { driver->OnRpdoWrite(idx, subidx); });
447 for (
const auto& it : *
this) {
488 const ::std::string& what) noexcept {
509 driver->
OnConfig([
this,
id](::std::error_code ec) {
510 ::std::lock_guard<util::BasicLockable>
lock(*
this);
518 for (
const auto& it : *
this) {
526 for (
const auto& it : *
this) {
534 const ::std::chrono::system_clock::time_point& abs_time) noexcept {
535 for (
const auto& it : *
this) {
543 uint8_t msef[5]) noexcept {
544 ::std::array<uint8_t, 5> value = {0};
545 ::std::copy_n(msef, value.size(), value.begin());
550 [=]()
mutable { driver->
OnEmcy(eec, er, value.data()); });
555 nmt->setNgInd<
Impl_, &Impl_::OnNgInd>(
this);
556 nmt->setBootInd<
Impl_, &Impl_::OnBootInd>(
this);
557 nmt->setCfgInd<
Impl_, &Impl_::OnCfgInd>(
this);
561 BasicMaster::Impl_::AsyncDeconfig(
DriverBase* driver) {
562 self->IsReady(driver->
id(),
false);
565 driver->
OnDeconfig([p](::std::error_code ec)
mutable { p.
set(ec); });
571 BasicMaster::Impl_::OnNgInd(
CONMT* nmt, uint8_t
id,
int state,
572 int reason) noexcept {
574 nmt->onNg(
id, state, reason);
580 self->OnNodeGuarding(
id, occurred);
581 if (on_node_guarding) {
582 auto f = on_node_guarding;
589 BasicMaster::Impl_::OnBootInd(
CONMT*, uint8_t
id, uint8_t st,
591 if (
id &&
id <=
CO_NUM_NODES && (!es || es ==
'L')) ready[
id - 1] =
true;
593 self->OnBoot(
id, static_cast<NmtState>(st), es, what);
597 f(
id, static_cast<NmtState>(st), es, what);
602 BasicMaster::Impl_::OnCfgInd(
CONMT*, uint8_t
id,
COCSDO* sdo) noexcept {
610 config[
id - 1] =
true;
618 #endif // !LELY_NO_COAPP_MASTER An NMT error control timeout event.
A reference to an abstract timer.
#define CO_NUM_NODES
The maximum number of nodes in a CANopen network.
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 OnConfig(uint8_t id) noexcept
The function invoked when the 'update configuration' step is reached during the NMT 'boot slave' proc...
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.
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
void lock() final
Blocks until a lock can be obtained for the current execution agent (thread, process, task).
void OnSyncError(uint16_t eec, uint8_t er) noexcept override
The default implementation notifies all registered drivers.
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.
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.
#define CO_NMT_ST_START
The NMT state 'operational'.
void CancelSdo(uint8_t id=0)
Aborts any ongoing or pending SDO requests for the specified slave.
void OnTime(const ::std::chrono::system_clock::time_point &abs_time) noexcept override
The default implementation queues a notification for all registered drivers.
void OnState(uint8_t id, NmtState st) noexcept override
The default implementation notifies the driver registered for 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.
This is the internal header file of the C++ CANopen application library.
void TpdoEvent(int num=0) noexcept
void post(ev_task &task) noexcept
SdoErrc sdo_errc(::std::error_code ec)
Returns the SDO abort code corresponding to an error code.
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, and 0 if not.
NmtCommand
The NMT command specifiers.
virtual void OnNodeGuarding(bool occurred) noexcept=0
The function invoked when a node guarding timeout event occurs or is resolved for the remote node...
void unlock() final
Releases the lock held by the execution agent. Throws no exceptions.
void unlock() override
Releases the lock held by the execution agent. Throws no exceptions.
const char * co_nmt_es2str(char es)
Returns a pointer to a string describing an NMT boot error status.
virtual uint8_t id() const noexcept=0
Returns the node-ID.
CONMT * nmt() const noexcept
Returns a pointer to the internal CANopen NMT master/slave service from <lely/co/nmt.hpp>.
The abstract driver interface for a remote CANopen node.
void SetTimeout(const ::std::chrono::milliseconds &timeout)
Sets the SDO timeout used during the NMT 'boot slave' and 'check configuration' processes.
An NMT error control event occurred.
An opaque CANopen Client-SDO service type.
CanState
The states of a CAN node, depending on the TX/RX error count.
ev::Executor GetExecutor() const noexcept
Returns the executor used to process I/O and CANopen events.
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...
void OnSync(uint8_t cnt, const time_point &t) noexcept override
The default implementation notifies all registered drivers.
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.
void Error(uint8_t id)
Indicates the occurrence of an error event on a remote node and triggers the error handling process (...
virtual void OnCommand(NmtCommand cs) noexcept=0
The function invoked when an NMT state change occurs on the master.
void OnHeartbeat(uint8_t id, bool occurred) noexcept override
The default implementation queues a notification for the driver registered for node id...
void RpdoRtr(int num=0) noexcept
void OnEmcy(uint8_t id, uint16_t eec, uint8_t er, uint8_t msef[5]) noexcept override
The default implementation notifies the driver registered for node id.
void OnRpdoWrite(uint8_t id, uint16_t idx, uint8_t subidx) noexcept override
The default implementation notifies the driver registered for node id.
This header file is part of the CANopen library; it contains the C++ interface of the network managem...
bool IsConfig(uint8_t id) const
Returns true if the remote node is configuring (i.e., the 'update configuration' step of the NMT 'boo...
void OnEmcy(uint8_t id, uint16_t eec, uint8_t er, uint8_t msef[5]) noexcept override
The default implementation queues a notification for the driver registered for node id...
void OnSync(uint8_t cnt, const time_point &t) noexcept override
The default implementation queues a notification for all registered drivers.
CanError
The error flags of a CAN bus, which are not mutually exclusive.
Future< T, E > get_future() const noexcept
Returns a lely::ev::Future with (a reference to) the same shared state as *this.
bool Boot(uint8_t id)
Requests the NMT 'boot slave' process for the specified node.
void Command(NmtCommand cs, uint8_t id=0)
Issues an NMT command to a slave.
The internal implementation of the CANopen master.
A mutex wrapper that provides a convenient RAII-style mechanism for releasing a mutex for the duratio...
void OnNodeGuarding(uint8_t id, bool occurred) noexcept override
The default implementation queues a notification for all registered drivers.
#define CO_NMT_ST_PREOP
The NMT state 'pre-operational'.
void OnCommand(NmtCommand cs) noexcept override
The default implementation notifies all registered drivers.
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...
inline ::std::chrono::milliseconds from_sdo_timeout(int timeout)
Converts an SDO timeout to a duration.
The type of objects thrown as exceptions to report a system error with an associated error code...
This header file is part of the C++ CANopen application library; it contains the remote node driver i...
void OnBoot(uint8_t id, NmtState st, char es, const ::std::string &what) noexcept override
The default implementation queues a notification for the driver registered for node id...
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 OnCanError(io::CanError error) noexcept override
The default implementation queues a notification for all registered drivers.
void OnState(uint8_t id, NmtState st) noexcept override
The default implementation queues a notification for the driver registered for node id...
TpdoEventMutex tpdo_event_mutex
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...
virtual void OnHeartbeat(bool occurred) noexcept=0
The function invoked when a heartbeat timeout event occurs or is resolved for the remote node...
void OnCommand(NmtCommand cs) noexcept override
The default implementation queues a notification for all registered drivers.
void Insert(DriverBase &driver)
Registers a driver for a remote CANopen node.
void OnSyncError(uint16_t eec, uint8_t er) noexcept override
The default implementation queues a notification for all registered drivers.
void OnCanState(io::CanState new_state, io::CanState old_state) noexcept override
The default implementation invokes lely::canopen::Node::OnCanState() and notifies each registered dri...
void RpdoRtr(int num=0) noexcept
Requests the transmission of a PDO by sending a CAN frame with the RTR (Remote Transmission Request) ...
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.
ev::Future<::std::size_t, void > AsyncDeconfig()
Queues the DriverBase::OnDeconfig() method for all registered drivers and creates a future which beco...
void OnConfig(uint8_t id) noexcept override
The default implementation queues a notification for the driver registered for node id...
void OnHeartbeat(uint8_t id, bool occurred) noexcept override
The default implementation notifies the driver registered for node id.
virtual ev::Executor GetExecutor() const noexcept=0
Returns the executor used to execute event handlers for this driver, including SDO confirmation funct...
::std::chrono::milliseconds GetTimeout() const
Returns the SDO timeout used during the NMT 'boot slave' and 'check configuration' processes...
void TpdoEvent(int num=0) noexcept
Triggers the transmission of an acyclic or event-driven PDO.
The internal implementation of the CANopen device description.
An opaque CANopen NMT master/slave service type.
void lock() override
Blocks until a lock can be obtained for the current execution agent (thread, process, task).
void unlock() override
Releases the lock held by the execution agent. Throws no exceptions.
virtual void OnCanError(io::CanError error) noexcept=0
The function invoked when an error is detected on the CAN bus.
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.
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.
uint8_t id() const noexcept
Returns the node-ID.
void OnTime(const ::std::chrono::system_clock::time_point &abs_time) noexcept override
The default implementation notifies all registered drivers.
void ConfigResult(uint8_t id, ::std::error_code ec) noexcept
Reports the result of the 'update configuration' step to the NMT service.
Sdo * GetSdo(uint8_t id)
Returns a pointer to the default client-SDO service for the given node.
The base class for CANopen nodes.
virtual void OnDeconfig(::std::function< void(::std::error_code ec)> res) noexcept=0
The function invoked by BasicMaster::AsyncDeconfig() to start the deconfiguration process...
void OnCanState(io::CanState new_state, io::CanState old_state) noexcept override
The default implementation invokes lely::canopen::Node::OnCanState() and queues a notification for ea...
bool set(U &&u)
Satisfies a promise, if it was not aready satisfied, and stores the specified value as the result in ...
bool IsReady(uint8_t id) const
Returns true if the remote node is ready (i.e., the NMT 'boot slave' process has successfully complet...
void OnCanError(io::CanError error) noexcept override
The default implementation notifies all registered drivers.
This header file is part of the CANopen library; it contains the C++ interface of the device descript...
int to_sdo_timeout(const ::std::chrono::duration< Rep, Period > &d)
Converts a duration to an SDO timeout.
void OnRpdoWrite(uint8_t id, uint16_t idx, uint8_t subidx) noexcept override
The default implementation queues a notification for the driver registered for node id...
A reference to an abstract CAN channel.
void Erase(DriverBase &driver)
Unregisters a driver for a remote CANopen node.
void lock() override
Blocks until a lock can be obtained for the current execution agent (thread, process, task).