Lely core libraries  2.2.5
driver.cpp
Go to the documentation of this file.
1 
24 #include "coapp.hpp"
25 
26 #if !LELY_NO_COAPP_MASTER
27 
28 #include <lely/coapp/driver.hpp>
29 
30 #include <algorithm>
31 #include <array>
32 #include <string>
33 
34 #include <cassert>
35 
36 namespace lely {
37 
38 namespace canopen {
39 
40 BasicDriver::BasicDriver(ev_exec_t* exec, BasicMaster& master_, uint8_t id)
41  : master(master_),
42  rpdo_mapped(master.RpdoMapped(id)),
43  tpdo_mapped(master.TpdoMapped(id)),
44  tpdo_event_mutex(master.tpdo_event_mutex),
45  exec_(exec ? exec : static_cast<ev_exec_t*>(master.GetExecutor())),
46  id_(id) {
47  master.Insert(*this);
48 }
49 
50 BasicDriver::~BasicDriver() { master.Erase(*this); }
51 
52 void
54  if (driver.Number() < 1 || driver.Number() > 8)
55  throw ::std::out_of_range("invalid logical device number: " +
56  ::std::to_string(driver.Number()));
57  if (find(driver.id()) != end())
58  throw ::std::out_of_range("logical device number " +
59  ::std::to_string(driver.Number()) +
60  " already registered");
61 
62  MapType::operator[](driver.Number()) = &driver;
63 }
64 
65 void
67  auto it = find(driver.Number());
68  if (it != end() && it->second == &driver) erase(it);
69 }
70 
73  if (num) {
74  auto it = find(num);
75  if (it != end()) return it->second->AsyncConfig();
76  } else if (size() == 1) {
77  // Shortcut in case of a single logical device.
78  return begin()->second->AsyncConfig();
79  } else if (!empty()) {
80  ::std::array<SdoFuture<void>, 8> futures;
81  ::std::size_t n = 0;
82  // Post an OnConfig() task for each logical device driver.
83  for (const auto& it : *this) futures[n++] = it.second->AsyncConfig();
84  // Create a temporary array of pointers, since SdoFuture is not guaranteed
85  // to be the same size as ev_future_t*.
86  ::std::array<ev_future_t*, 8> tmp;
87  ::std::copy(futures.begin(), futures.end(), tmp.begin());
88  // Create a future which becomes ready when all OnConfig() tasks have
89  // finished.
90  return ev::when_all(GetExecutor(), n, tmp.data())
91  // Check the results of the tasks.
92  .then(GetExecutor(), [futures](ev::Future<::std::size_t, void>) {
93  for (const auto& it : futures) {
94  // Throw an exception in an error occurred.
95  if (it) it.get().value();
96  }
97  });
98  }
99  return make_empty_sdo_future();
100 }
101 
104  if (num) {
105  auto it = find(num);
106  if (it != end()) return it->second->AsyncDeconfig();
107  } else if (size() == 1) {
108  // Shortcut in case of a single logical device.
109  return begin()->second->AsyncDeconfig();
110  } else if (!empty()) {
111  ::std::array<SdoFuture<void>, 8> futures;
112  ::std::size_t n = 0;
113  // Post an OnConfig() task for each logical device driver.
114  for (const auto& it : *this) futures[n++] = it.second->AsyncDeconfig();
115  assert(n <= 8);
116  // Create a temporary array of pointers, since SdoFuture is not guaranteed
117  // to be the same size as ev_future_t*.
118  ::std::array<ev_future_t*, 8> tmp;
119  ::std::copy(futures.begin(), futures.end(), tmp.begin());
120  // Create a future which becomes ready when all OnDeconfig() tasks have
121  // finished.
122  return ev::when_all(GetExecutor(), n, tmp.data())
123  // Check the results of the tasks.
124  .then(GetExecutor(), [futures](ev::Future<::std::size_t, void>) {
125  for (const auto& it : futures) {
126  // Throw an exception in an error occurred.
127  if (it) it.get().value();
128  }
129  });
130  }
131  return make_empty_sdo_future();
132 }
133 
134 void
136  io::CanState old_state) noexcept {
137  for (const auto& it : *this) it.second->OnCanState(new_state, old_state);
138 }
139 
140 void
142  for (const auto& it : *this) it.second->OnCanError(error);
143 }
144 
145 void
146 BasicDriver::OnRpdoWrite(uint16_t idx, uint8_t subidx) noexcept {
147  if (idx >= 0x6000 && idx <= 0x9fff) {
148  int num = (idx - 0x6000) / 0x800 + 1;
149  auto it = find(num);
150  if (it != end()) it->second->OnRpdoWrite(idx - (num - 1) * 0x800, subidx);
151  } else {
152  for (const auto& it : *this) it.second->OnRpdoWrite(idx, subidx);
153  }
154 }
155 
156 void
158  for (const auto& it : *this) it.second->OnCommand(cs);
159 }
160 
161 void
162 BasicDriver::OnNodeGuarding(bool occurred) noexcept {
163  for (const auto& it : *this) it.second->OnNodeGuarding(occurred);
164 }
165 
166 void
167 BasicDriver::OnHeartbeat(bool occurred) noexcept {
168  for (const auto& it : *this) it.second->OnHeartbeat(occurred);
169 }
170 
171 void
173  for (const auto& it : *this) it.second->OnState(st);
174 }
175 
176 void
177 BasicDriver::OnBoot(NmtState st, char es, const ::std::string& what) noexcept {
178  for (const auto& it : *this) it.second->OnBoot(st, es, what);
179 }
180 
181 void
183  ::std::function<void(::std::error_code ec)> res) noexcept {
184  if (empty()) {
185  // Shortcut if no logical device drivers have been registered.
186  res(::std::error_code{});
187  } else {
188  try {
189  auto f = AsyncConfig();
190  // Invoke res() when AsyncConfig() completes.
191  f.submit(GetExecutor(), [res, f] {
192  // Extract the error code from the exception pointer, if any.
193  ::std::error_code ec;
194  auto& result = f.get();
195  if (result.has_error()) {
196  try {
197  ::std::rethrow_exception(result.error());
198  } catch (const ::std::system_error& e) {
199  ec = e.code();
200  } catch (...) {
201  // Ignore exceptions we cannot handle.
202  }
203  }
204  res(ec);
205  });
206  } catch (::std::system_error& e) {
207  res(e.code());
208  }
209  }
210 }
211 
212 void
213 BasicDriver::OnDeconfig(
214  ::std::function<void(::std::error_code ec)> res) noexcept {
215  if (empty()) {
216  // Shortcut if no logical device drivers have been registered.
217  res(::std::error_code{});
218  } else {
219  try {
220  auto f = AsyncDeconfig();
221  // Invoke res() when AsyncConfig() completes.
222  f.submit(GetExecutor(), [res, f] {
223  // Extract the error code from the exception pointer, if any.
224  ::std::error_code ec;
225  auto& result = f.get();
226  if (result.has_error()) {
227  try {
228  ::std::rethrow_exception(result.error());
229  } catch (const ::std::system_error& e) {
230  ec = e.code();
231  } catch (...) {
232  // Ignore exceptions we cannot handle.
233  }
234  }
235  res(ec);
236  });
237  } catch (::std::system_error& e) {
238  res(e.code());
239  }
240  }
241 }
242 
243 void
244 BasicDriver::OnSync(uint8_t cnt, const time_point& t) noexcept {
245  for (const auto& it : *this) it.second->OnSync(cnt, t);
246 }
247 
248 void
249 BasicDriver::OnSyncError(uint16_t eec, uint8_t er) noexcept {
250  for (const auto& it : *this) it.second->OnSyncError(eec, er);
251 }
252 
253 void
254 BasicDriver::OnTime(
255  const ::std::chrono::system_clock::time_point& abs_time) noexcept {
256  for (const auto& it : *this) it.second->OnTime(abs_time);
257 }
258 
259 void
260 BasicDriver::OnEmcy(uint16_t eec, uint8_t er, uint8_t msef[5]) noexcept {
261  for (const auto& it : *this) it.second->OnEmcy(eec, er, msef);
262 }
263 
264 } // namespace canopen
265 
266 } // namespace lely
267 
268 #endif // !LELY_NO_COAPP_MASTER
void OnCommand(NmtCommand cs) noexcept override
The default implementation notifies all registered logical device drivers.
Definition: driver.cpp:157
void OnCanState(io::CanState new_state, io::CanState old_state) noexcept override
The default implementation notifies all registered logical device drivers.
Definition: driver.cpp:135
SdoFuture< void > AsyncDeconfig(int num=0)
Invokes LogicalDriverBase::AsyncDeconfig() for the specified logical device driver.
Definition: driver.cpp:103
void Insert(LogicalDriverBase &driver)
Registers a logical device driver for the remote node.
Definition: driver.cpp:53
SdoFuture< void > AsyncConfig(int num=0)
Invokes LogicalDriverBase::AsyncConfig() for the specified logical device driver.
Definition: driver.cpp:72
void OnState(NmtState st) noexcept override
The default implementation notifies all registered logical device drivers.
Definition: driver.cpp:172
void Erase(LogicalDriverBase &driver)
Unregisters a logical device driver for the remote node.
Definition: driver.cpp:66
ev::Executor GetExecutor() const noexcept final
Returns the executor used to execute event handlers for this driver, including SDO confirmation funct...
Definition: driver.hpp:311
void OnHeartbeat(bool occurred) noexcept override
The default implementation notifies all registered logical device drivers.
Definition: driver.cpp:167
void OnNodeGuarding(bool occurred) noexcept override
The default implementation notifies all registered logical device drivers.
Definition: driver.cpp:162
BasicDriver(ev_exec_t *exec, BasicMaster &master, uint8_t id)
Creates a new driver for a remote CANopen node and registers it with the master.
Definition: driver.cpp:40
void OnConfig(::std::function< void(::std::error_code ec)> res) noexcept override
The default implementation invokes AsyncConfig() to start the configuration process for all registere...
Definition: driver.cpp:182
void OnRpdoWrite(uint16_t idx, uint8_t subidx) noexcept override
The default implementation notifies all registered logical device drivers, unless the object index is...
Definition: driver.cpp:146
void OnBoot(NmtState st, char es, const ::std::string &what) noexcept override
The default implementation notifies all registered logical device drivers.
Definition: driver.cpp:177
void OnCanError(io::CanError error) noexcept override
The default implementation notifies all registered logical device drivers.
Definition: driver.cpp:141
BasicMaster & master
A reference to the master with which this driver is registered.
Definition: driver.hpp:671
The CANopen master.
Definition: master.hpp:50
void Erase(DriverBase &driver)
Unregisters a driver for a remote CANopen node.
Definition: master.cpp:206
void Insert(DriverBase &driver)
Registers a driver for a remote CANopen node.
Definition: master.cpp:189
virtual uint8_t id() const noexcept=0
Returns the node-ID.
The abstract driver interface for a logical device on a remote CANopen node.
Definition: driver.hpp:258
virtual int Number() const noexcept=0
Returns the number of the logical device on the remote node.
The type of objects thrown as exceptions to report a system error with an associated error code.
Definition: exception.hpp:54
A future.
Definition: future.hpp:384
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
NmtState
The NMT states.
Definition: node.hpp:56
SdoFuture< void > make_empty_sdo_future()
Returns an SDO future with a shared state that is immediately ready, containing a successful result o...
Definition: sdo.hpp:73
NmtCommand
The NMT command specifiers.
Definition: node.hpp:42