15#ifndef NODE_CANOPEN_BASE_DRIVER_IMPL
16#define NODE_CANOPEN_BASE_DRIVER_IMPL
22template <
class NODETYPE>
25 diagnostic_enabled_(false),
30template <
class NODETYPE>
40 this->non_transmit_timeout_ =
41 std::chrono::milliseconds(this->config_[
"non_transmit_timeout"].as<int>());
47 this->node_->get_logger(),
48 "Non transmit timeout" <<
static_cast<int>(this->non_transmit_timeout_.count()) <<
"ms");
52 polling_ = this->config_[
"polling"].as<
bool>();
56 RCLCPP_WARN(this->node_->get_logger(),
"Could not polling from config, setting to true.");
63 period_ms_ = this->config_[
"period"].as<std::uint32_t>();
67 RCLCPP_WARN(this->node_->get_logger(),
"Could not read period from config, setting to 10ms");
75 diagnostic_enabled_ = this->config_[
"diagnostics"][
"enable"].as<
bool>();
80 this->node_->get_logger(),
81 "Could not read enable diagnostics from config, setting to false.");
82 diagnostic_enabled_ =
false;
84 if (diagnostic_enabled_.load())
88 diagnostic_period_ms_ = this->config_[
"diagnostics"][
"period"].as<std::uint32_t>();
93 this->node_->get_logger(),
94 "Could not read diagnostics period from config, setting to 1000ms");
95 diagnostic_period_ms_ = 1000;
98 diagnostic_updater_ = std::make_shared<diagnostic_updater::Updater>(this->node_);
99 diagnostic_updater_->setHardwareID(std::to_string(this->node_id_));
102 std::optional<int> sdo_timeout_ms;
105 sdo_timeout_ms = std::optional(this->config_[
"sdo_timeout_ms"].as<int>());
110 sdo_timeout_ms_ = sdo_timeout_ms.value_or(20);
112 std::optional<int> boot_timeout_ms;
115 boot_timeout_ms = std::optional(this->config_[
"boot_timeout_ms"].as<int>());
120 boot_timeout_ms_ = boot_timeout_ms.value_or(20);
127 this->non_transmit_timeout_ =
128 std::chrono::milliseconds(this->config_[
"non_transmit_timeout"].as<int>());
134 this->node_->get_logger(),
135 "Non transmit timeout" <<
static_cast<int>(this->non_transmit_timeout_.count()) <<
"ms");
139 polling_ = this->config_[
"polling"].as<
bool>();
143 RCLCPP_WARN(this->node_->get_logger(),
"Could not polling from config, setting to true.");
150 period_ms_ = this->config_[
"period"].as<std::uint32_t>();
154 RCLCPP_WARN(this->node_->get_logger(),
"Could not read period from config, setting to 10ms");
162 diagnostic_enabled_ = this->config_[
"diagnostics"][
"enable"].as<
bool>();
167 this->node_->get_logger(),
168 "Could not read enable diagnostics from config, setting to false.");
169 diagnostic_enabled_ =
false;
171 if (diagnostic_enabled_.load())
175 diagnostic_period_ms_ = this->config_[
"diagnostics"][
"period"].as<std::uint32_t>();
180 this->node_->get_logger(),
181 "Could not read diagnostics period from config, setting to 1000ms");
182 diagnostic_period_ms_ = 1000;
185 diagnostic_updater_ = std::make_shared<diagnostic_updater::Updater>(this->node_);
186 diagnostic_updater_->setHardwareID(std::to_string(this->node_id_));
189 std::optional<int> sdo_timeout_ms;
192 sdo_timeout_ms = std::optional(this->config_[
"sdo_timeout_ms"].as<int>());
197 sdo_timeout_ms_ = sdo_timeout_ms.value_or(20);
199 std::optional<int> boot_timeout_ms;
202 boot_timeout_ms = std::optional(this->config_[
"boot_timeout_ms"].as<int>());
207 boot_timeout_ms_ = boot_timeout_ms.value_or(20);
210template <
class NODETYPE>
213 nmt_state_publisher_thread_ =
215 emcy_queue_ = this->lely_driver_->get_emcy_queue();
216 rpdo_queue_ = this->lely_driver_->get_rpdo_queue();
219 RCLCPP_INFO(this->node_->get_logger(),
"Starting with polling mode.");
220 poll_timer_ = this->node_->create_wall_timer(
221 std::chrono::milliseconds(period_ms_),
226 RCLCPP_INFO(this->node_->get_logger(),
"Starting with event mode.");
227 this->lely_driver_->set_sync_function(
231 if (diagnostic_enabled_.load())
233 RCLCPP_INFO(this->node_->get_logger(),
"Starting with diagnostics enabled.");
234 diagnostic_updater_->add(
239template <
class NODETYPE>
242 nmt_state_publisher_thread_.join();
243 poll_timer_->cancel();
246 if (diagnostic_enabled_.load())
248 diagnostic_updater_->removeByName(
"diagnostic updater");
252template <
class NODETYPE>
259template <
class NODETYPE>
264template <
class NODETYPE>
267 RCLCPP_INFO(this->node_->get_logger(),
"eds file %s", this->eds_.c_str());
268 RCLCPP_INFO(this->node_->get_logger(),
"bin file %s", this->bin_.c_str());
269 std::shared_ptr<std::promise<std::shared_ptr<ros2_canopen::LelyDriverBridge>>> prom;
270 prom = std::make_shared<std::promise<std::shared_ptr<ros2_canopen::LelyDriverBridge>>>();
271 std::future<std::shared_ptr<ros2_canopen::LelyDriverBridge>> f = prom->get_future();
275 std::scoped_lock<std::mutex> lock(this->driver_mutex_);
276 this->lely_driver_ = std::make_shared<ros2_canopen::LelyDriverBridge>(
277 *(this->exec_), *(this->master_), this->node_id_, this->node_->get_name(), this->eds_,
278 this->bin_, std::chrono::milliseconds(this->sdo_timeout_ms_),
279 std::chrono::milliseconds(this->boot_timeout_ms_));
280 this->driver_ = std::static_pointer_cast<lely::canopen::BasicDriver>(this->lely_driver_);
281 prom->set_value(lely_driver_);
284 auto future_status = f.wait_for(this->non_transmit_timeout_);
285 if (future_status != std::future_status::ready)
287 RCLCPP_ERROR(this->node_->get_logger(),
"Adding timed out.");
290 this->lely_driver_ = f.get();
291 this->driver_ = std::static_pointer_cast<lely::canopen::BasicDriver>(this->lely_driver_);
292 if (!this->lely_driver_->IsReady())
294 bool boot_success =
false;
295 int boot_attempts = 0;
296 const int max_boot_attempts = 3;
297 RCLCPP_WARN(this->node_->get_logger(),
"Wait for device to boot...");
298 while (!boot_success && boot_attempts < max_boot_attempts)
303 this->lely_driver_->wait_for_boot();
305 RCLCPP_INFO(this->node_->get_logger(),
"Driver booted and ready.");
307 catch (
const std::exception & e)
310 this->node_->get_logger(),
"Boot attempt %d failed: %s", boot_attempts, e.what());
312 if (boot_attempts < max_boot_attempts)
314 RCLCPP_INFO(this->node_->get_logger(),
"Sending NMT reset before retrying boot");
315 this->lely_driver_->nmt_command(canopen::NmtCommand::RESET_NODE);
316 this->lely_driver_->Boot();
317 RCLCPP_WARN(this->node_->get_logger(),
"Retrying boot configuration...");
321 RCLCPP_ERROR(this->node_->get_logger(),
"Boot failed after %d attempts", boot_attempts);
328 std::string(
"Boot failed after ") + std::to_string(boot_attempts) +
" attempts");
332 if (diagnostic_enabled_.load())
334 diagnostic_collector_->updateAll(
335 diagnostic_msgs::msg::DiagnosticStatus::OK,
"Device ready",
"DEVICE",
"Added to master.");
339template <
class NODETYPE>
342 std::shared_ptr<std::promise<void>> prom = std::make_shared<std::promise<void>>();
343 auto f = prom->get_future();
347 this->driver_.reset();
348 this->lely_driver_.reset();
352 auto future_status = f.wait_for(this->non_transmit_timeout_);
353 if (future_status != std::future_status::ready)
357 if (diagnostic_enabled_.load())
359 diagnostic_collector_->updateAll(
360 diagnostic_msgs::msg::DiagnosticStatus::ERROR,
"Device removed",
"DEVICE",
361 "Removed from master.");
364template <
class NODETYPE>
369 std::future<lely::canopen::NmtState> f;
371 std::scoped_lock<std::mutex> lock(this->driver_mutex_);
372 f = this->lely_driver_->async_request_nmt();
374 while (f.wait_for(this->non_transmit_timeout_) != std::future_status::ready)
376 if (!this->activated_.load())
return;
380 auto state = f.get();
383 nmt_state_cb_(state, this->lely_driver_->get_id());
387 catch (
const std::future_error & e)
393template <
class NODETYPE>
398template <
class NODETYPE>
403template <
class NODETYPE>
406 diagnostic_collector_->summary(
407 diagnostic_msgs::msg::DiagnosticStatus::ERROR,
"Emergency message received");
408 std::string emcy_msg =
"Emergency message: ";
409 emcy_msg.append(
"eec: ");
410 emcy_msg.append(std::to_string(emcy.
eec));
411 emcy_msg.append(
" er: ");
412 emcy_msg.append(std::to_string(emcy.
er));
413 emcy_msg.append(
" msef: ");
414 for (
auto & msef : emcy.
msef)
416 emcy_msg.append(std::to_string(msef));
417 emcy_msg.append(
" ");
419 diagnostic_collector_->add(
"EMCY", emcy_msg);
422template <
class NODETYPE>
425 RCLCPP_INFO(this->node_->get_logger(),
"Starting RPDO Listener");
426 auto q = lely_driver_->get_rpdo_queue();
430 while (!q->wait_and_pop_for(this->non_transmit_timeout_, rpdo))
432 if (!this->activated_.load())
return;
438 rpdo_cb_(rpdo, this->lely_driver_->get_id());
442 catch (
const std::exception & e)
444 RCLCPP_ERROR_STREAM(this->node_->get_logger(),
"RPDO Listener error: " << e.what());
449template <
class NODETYPE>
452 for (
int i = 0; i < 10; i++)
454 auto opt = emcy_queue_->try_pop();
455 if (!opt.has_value())
463 emcy_cb_(opt.value(), this->lely_driver_->get_id());
465 on_emcy(opt.value());
467 catch (
const std::exception & e)
469 RCLCPP_ERROR_STREAM(this->node_->get_logger(),
"EMCY poll error: " << e.what());
473 for (
int i = 0; i < 10; i++)
475 auto opt = rpdo_queue_->try_pop();
476 if (!opt.has_value())
484 rpdo_cb_(opt.value(), this->lely_driver_->get_id());
486 on_rpdo(opt.value());
488 catch (
const std::exception & e)
490 RCLCPP_ERROR_STREAM(this->node_->get_logger(),
"RPDO Poll error: " << e.what());
496template <
class NODETYPE>
499 RCLCPP_INFO(this->node_->get_logger(),
"Starting EMCY Listener");
500 auto q = lely_driver_->get_emcy_queue();
504 while (!q->wait_and_pop_for(this->non_transmit_timeout_, emcy))
506 if (!this->activated_.load())
return;
512 emcy_cb_(emcy, this->lely_driver_->get_id());
516 catch (
const std::exception & e)
518 RCLCPP_ERROR_STREAM(this->node_->get_logger(),
"EMCY Listener error: " << e.what());
524template <
class NODETYPE>
526 diagnostic_updater::DiagnosticStatusWrapper & stat)
A class to collect diagnostic information.
Definition diagnostic_collector.hpp:34
Driver Exception.
Definition driver_error.hpp:31
Definition node_canopen_base_driver.hpp:34
void rdpo_listener()
Definition node_canopen_base_driver_impl.hpp:423
virtual void add_to_master()
Add the driver to master.
Definition node_canopen_base_driver_impl.hpp:265
void nmt_listener()
Definition node_canopen_base_driver_impl.hpp:365
virtual void on_rpdo(COData data)
Definition node_canopen_base_driver_impl.hpp:399
virtual void poll_timer_callback()
Definition node_canopen_base_driver_impl.hpp:450
NodeCanopenBaseDriver(NODETYPE *node)
Definition node_canopen_base_driver_impl.hpp:23
virtual void diagnostic_callback(diagnostic_updater::DiagnosticStatusWrapper &stat)
Definition node_canopen_base_driver_impl.hpp:525
virtual void remove_from_master()
Remove the driver from master.
Definition node_canopen_base_driver_impl.hpp:340
void emcy_listener()
Definition node_canopen_base_driver_impl.hpp:497
virtual void on_nmt(canopen::NmtState nmt_state)
Definition node_canopen_base_driver_impl.hpp:394
virtual void on_emcy(COEmcy emcy)
Definition node_canopen_base_driver_impl.hpp:404
Node Canopen Driver.
Definition node_canopen_driver.hpp:60
void shutdown()
Shutdown the driver.
Definition node_canopen_driver.hpp:344
void deactivate()
Deactivate the driver.
Definition node_canopen_driver.hpp:265
void cleanup()
Cleanup the driver.
Definition node_canopen_driver.hpp:305
void activate()
Activate the driver.
Definition node_canopen_driver.hpp:223
void init()
Initialise the driver.
Definition node_canopen_driver.hpp:127
void configure()
Configure the driver.
Definition node_canopen_driver.hpp:175
Definition node_canopen_driver.hpp:46
Definition configuration_manager.hpp:28
Definition exchange.hpp:26
Definition exchange.hpp:34
uint16_t eec
Definition exchange.hpp:36
uint8_t er
Definition exchange.hpp:37
uint8_t msef[5]
Definition exchange.hpp:38