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;
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);
323 ConfigResult(
id, ::std::error_code());
328 it->second->OnConfig([
this,
id](::std::error_code ec) {
329 ::std::lock_guard<util::BasicLockable> lock(*
this);
331 ConfigResult(
id, ec);
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 {
502 ConfigResult(
id, ::std::error_code());
509 driver->
OnConfig([
this,
id](::std::error_code ec) {
510 ::std::lock_guard<util::BasicLockable> lock(*
this);
511 ConfigResult(
id, ec);
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);
564 driver->GetExecutor().post([=]()
mutable {
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;
An opaque CANopen Client-SDO service type.
An opaque CANopen NMT master/slave service type.
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 OnConfig(uint8_t id) noexcept override
The default implementation queues a notification for the driver registered for node id.
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.
void OnNodeGuarding(uint8_t id, bool occurred) noexcept override
The default implementation queues a notification for all registered drivers.
void OnSync(uint8_t cnt, const time_point &t) 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 queues a notification for ea...
void OnHeartbeat(uint8_t id, bool occurred) noexcept override
The default implementation queues a notification for the driver registered for node id.
void OnSyncError(uint16_t eec, uint8_t er) noexcept override
The default implementation queues a notification for all registered drivers.
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.
void OnCommand(NmtCommand cs) 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.
void OnTime(const ::std::chrono::system_clock::time_point &abs_time) noexcept override
The default implementation queues a notification for all registered drivers.
void OnCanError(io::CanError error) noexcept override
The default implementation queues a notification for all registered drivers.
void lock() override
Blocks until a lock can be obtained for the current execution agent (thread, process,...
void unlock() override
Releases the lock held by the execution agent. Throws no exceptions.
void OnSyncError(uint16_t eec, uint8_t er) noexcept override
The default implementation notifies all registered drivers.
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 TpdoEvent(int num=0) noexcept
ev::Future<::std::size_t, void > AsyncDeconfig()
Queues the DriverBase::OnDeconfig() method for all registered drivers and creates a future which beco...
virtual void OnConfig(uint8_t id) noexcept
The function invoked when the 'update configuration' step is reached during the NMT 'boot slave' proc...
void OnTime(const ::std::chrono::system_clock::time_point &abs_time) noexcept override
The default implementation notifies all registered drivers.
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 OnHeartbeat(uint8_t id, bool occurred) noexcept override
The default implementation notifies the driver registered for node id.
void RpdoRtr(int num=0) noexcept
void Command(NmtCommand cs, uint8_t id=0)
Issues an NMT command to a slave.
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.
void Erase(DriverBase &driver)
Unregisters a driver for a remote CANopen node.
void OnCanError(io::CanError error) noexcept override
The default implementation notifies all registered drivers.
void OnRpdoWrite(uint8_t id, uint16_t idx, uint8_t subidx) noexcept override
The default implementation notifies the driver registered for node id.
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 OnSync(uint8_t cnt, const time_point &t) noexcept override
The default implementation notifies all registered drivers.
void Insert(DriverBase &driver)
Registers a driver for a remote CANopen node.
void Error(uint8_t id)
Indicates the occurrence of an error event on a remote node and triggers the error handling process (...
bool Boot(uint8_t id)
Requests the NMT 'boot slave' process for the specified node.
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.
bool IsReady(uint8_t id) const
Returns true if the remote node is ready (i.e., the NMT 'boot slave' process has successfully complet...
::std::chrono::milliseconds GetTimeout() const
Returns the SDO timeout used during the NMT 'boot slave' and 'check configuration' processes.
void OnState(uint8_t id, NmtState st) noexcept override
The default implementation notifies the driver registered for node id.
void OnCommand(NmtCommand cs) noexcept override
The default implementation notifies all registered drivers.
void SetTimeout(const ::std::chrono::milliseconds &timeout)
Sets the SDO timeout used during the NMT 'boot slave' and 'check configuration' processes.
TpdoEventMutex tpdo_event_mutex
void ConfigResult(uint8_t id, ::std::error_code ec) noexcept
Reports the result of the 'update configuration' step to the NMT service.
void CancelSdo(uint8_t id=0)
Aborts any ongoing or pending SDO requests for the specified slave.
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...
Sdo * GetSdo(uint8_t id)
Returns a pointer to the default client-SDO service for the given node.
uint8_t id() const noexcept
Returns the node-ID.
The abstract driver interface for a remote CANopen node.
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,...
void unlock() override
Releases the lock held by the execution agent. Throws no exceptions.
The base class for CANopen nodes.
CONMT * nmt() const noexcept
Returns a pointer to the internal CANopen NMT master/slave service from <lely/co/nmt....
void TpdoEvent(int num=0) noexcept
Triggers the transmission of an acyclic or event-driven PDO.
ev::Executor GetExecutor() const noexcept
Returns the executor used to process I/O and CANopen events.
void RpdoRtr(int num=0) noexcept
Requests the transmission of a PDO by sending a CAN frame with the RTR (Remote Transmission Request) ...
The type of objects thrown as exceptions to report a system error with an associated error code.
void post(ev_task &task) noexcept
Future< T, E > get_future() const noexcept
Returns a lely::ev::Future with (a reference to) the same shared state as *this.
bool set(U &&u)
Satisfies a promise, if it was not aready satisfied, and stores the specified value as the result in ...
A reference to an abstract CAN channel.
void unlock() final
Releases the lock held by the execution agent. Throws no exceptions.
void lock() final
Blocks until a lock can be obtained for the current execution agent (thread, process,...
A reference to an abstract timer.
A mutex wrapper that provides a convenient RAII-style mechanism for releasing a mutex for the duratio...
#define CO_NUM_NODES
The maximum number of nodes in a CANopen network.
This header file is part of the CANopen library; it contains the C++ interface of the device descript...
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.
CanState
The states of a CAN node, depending on the TX/RX error count.
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
inline ::std::chrono::milliseconds from_sdo_timeout(int timeout)
Converts an SDO timeout to a duration.
int to_sdo_timeout(const ::std::chrono::duration< Rep, Period > &d)
Converts a duration to an SDO timeout.
SdoErrc sdo_errc(::std::error_code ec)
Returns the SDO abort code corresponding to an error code.
NmtCommand
The NMT command specifiers.
@ ENTER_PREOP
Enter pre-operational.
#define CO_NMT_ST_PREOP
The NMT state 'pre-operational'.
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.
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,...
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.
@ CO_NMT_EC_TIMEOUT
An NMT error control timeout event.
#define CO_NMT_ST_START
The NMT state 'operational'.
const char * co_nmt_es2str(char es)
Returns a pointer to a string describing an NMT boot error status.
@ CO_NMT_EC_OCCURRED
An NMT error control event occurred.
This header file is part of the CANopen library; it contains the C++ interface of the network managem...
The internal implementation of the CANopen master.