Program Listing for File node_canopen_proxy_driver_impl.hpp

Return to documentation for file (/tmp/ws/src/ros2_canopen/canopen_proxy_driver/include/canopen_proxy_driver/node_interfaces/node_canopen_proxy_driver_impl.hpp)

//    Copyright 2022 Christoph Hellmann Santos
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

#ifndef NODE_CANOPEN_PROXY_DRIVER_IMPL_HPP_
#define NODE_CANOPEN_PROXY_DRIVER_IMPL_HPP_

#include "canopen_core/driver_error.hpp"
#include "canopen_proxy_driver/node_interfaces/node_canopen_proxy_driver.hpp"

using namespace ros2_canopen::node_interfaces;

template <class NODETYPE>
NodeCanopenProxyDriver<NODETYPE>::NodeCanopenProxyDriver(NODETYPE * node)
: ros2_canopen::node_interfaces::NodeCanopenBaseDriver<NODETYPE>(node)
{
}

template <class NODETYPE>
void NodeCanopenProxyDriver<NODETYPE>::init(bool called_from_base)
{
  RCLCPP_ERROR(this->node_->get_logger(), "Not init implemented.");
}

template <>
void NodeCanopenProxyDriver<rclcpp::Node>::init(bool called_from_base)
{
  nmt_state_publisher = this->node_->create_publisher<std_msgs::msg::String>(
    std::string(this->node_->get_name()).append("/nmt_state").c_str(), 10);
  tpdo_subscriber = this->node_->create_subscription<canopen_interfaces::msg::COData>(
    std::string(this->node_->get_name()).append("/tpdo").c_str(), 10,
    std::bind(&NodeCanopenProxyDriver<rclcpp::Node>::on_tpdo, this, std::placeholders::_1));

  rpdo_publisher = this->node_->create_publisher<canopen_interfaces::msg::COData>(
    std::string(this->node_->get_name()).append("/rpdo").c_str(), 10);

  nmt_state_reset_service = this->node_->create_service<std_srvs::srv::Trigger>(
    std::string(this->node_->get_name()).append("/nmt_reset_node").c_str(),
    std::bind(
      &NodeCanopenProxyDriver<rclcpp::Node>::on_nmt_state_reset, this, std::placeholders::_1,
      std::placeholders::_2));

  nmt_state_start_service = this->node_->create_service<std_srvs::srv::Trigger>(
    std::string(this->node_->get_name()).append("/nmt_start_node").c_str(),
    std::bind(
      &NodeCanopenProxyDriver<rclcpp::Node>::on_nmt_state_start, this, std::placeholders::_1,
      std::placeholders::_2));

  sdo_read_service = this->node_->create_service<canopen_interfaces::srv::CORead>(
    std::string(this->node_->get_name()).append("/sdo_read").c_str(),
    std::bind(
      &NodeCanopenProxyDriver<rclcpp::Node>::on_sdo_read, this, std::placeholders::_1,
      std::placeholders::_2));

  sdo_write_service = this->node_->create_service<canopen_interfaces::srv::COWrite>(
    std::string(this->node_->get_name()).append("/sdo_write").c_str(),
    std::bind(
      &NodeCanopenProxyDriver<rclcpp::Node>::on_sdo_write, this, std::placeholders::_1,
      std::placeholders::_2));
}

template <>
void NodeCanopenProxyDriver<rclcpp_lifecycle::LifecycleNode>::init(bool called_from_base)
{
  nmt_state_publisher = this->node_->create_publisher<std_msgs::msg::String>(
    std::string(this->node_->get_name()).append("/nmt_state").c_str(), 10);
  tpdo_subscriber = this->node_->create_subscription<canopen_interfaces::msg::COData>(
    std::string(this->node_->get_name()).append("/tpdo").c_str(), 10,
    std::bind(
      &NodeCanopenProxyDriver<rclcpp_lifecycle::LifecycleNode>::on_tpdo, this,
      std::placeholders::_1));

  rpdo_publisher = this->node_->create_publisher<canopen_interfaces::msg::COData>(
    std::string(this->node_->get_name()).append("/rpdo").c_str(), 10);

  nmt_state_reset_service = this->node_->create_service<std_srvs::srv::Trigger>(
    std::string(this->node_->get_name()).append("/nmt_reset_node").c_str(),
    std::bind(
      &NodeCanopenProxyDriver<rclcpp_lifecycle::LifecycleNode>::on_nmt_state_reset, this,
      std::placeholders::_1, std::placeholders::_2));

  nmt_state_start_service = this->node_->create_service<std_srvs::srv::Trigger>(
    std::string(this->node_->get_name()).append("/nmt_start_node").c_str(),
    std::bind(
      &NodeCanopenProxyDriver<rclcpp_lifecycle::LifecycleNode>::on_nmt_state_start, this,
      std::placeholders::_1, std::placeholders::_2));

  sdo_read_service = this->node_->create_service<canopen_interfaces::srv::CORead>(
    std::string(this->node_->get_name()).append("/sdo_read").c_str(),
    std::bind(
      &NodeCanopenProxyDriver<rclcpp_lifecycle::LifecycleNode>::on_sdo_read, this,
      std::placeholders::_1, std::placeholders::_2));

  sdo_write_service = this->node_->create_service<canopen_interfaces::srv::COWrite>(
    std::string(this->node_->get_name()).append("/sdo_write").c_str(),
    std::bind(
      &NodeCanopenProxyDriver<rclcpp_lifecycle::LifecycleNode>::on_sdo_write, this,
      std::placeholders::_1, std::placeholders::_2));
}

template <class NODETYPE>
void NodeCanopenProxyDriver<NODETYPE>::on_nmt(canopen::NmtState nmt_state)
{
  if (this->activated_.load())
  {
    auto message = std_msgs::msg::String();

    switch (nmt_state)
    {
      case canopen::NmtState::BOOTUP:
        message.data = "BOOTUP";
        this->diagnostic_collector_->updateAll(
          diagnostic_msgs::msg::DiagnosticStatus::OK, "NMT bootup", "NMT", "BOOTUP");
        break;
      case canopen::NmtState::PREOP:
        message.data = "PREOP";
        this->diagnostic_collector_->updateAll(
          diagnostic_msgs::msg::DiagnosticStatus::OK, "NMT preop", "NMT", "PREOP");
        break;
      case canopen::NmtState::RESET_COMM:
        message.data = "RESET_COMM";
        this->diagnostic_collector_->updateAll(
          diagnostic_msgs::msg::DiagnosticStatus::WARN, "NMT reset comm", "NMT", "RESET_COMM");
        break;
      case canopen::NmtState::RESET_NODE:
        message.data = "RESET_NODE";
        this->diagnostic_collector_->updateAll(
          diagnostic_msgs::msg::DiagnosticStatus::WARN, "NMT reset node", "NMT", "RESET_NODE");
        break;
      case canopen::NmtState::START:
        message.data = "START";
        this->diagnostic_collector_->updateAll(
          diagnostic_msgs::msg::DiagnosticStatus::OK, "NMT start", "NMT", "START");
        break;
      case canopen::NmtState::STOP:
        message.data = "STOP";
        this->diagnostic_collector_->updateAll(
          diagnostic_msgs::msg::DiagnosticStatus::OK, "NMT stop", "NMT", "STOP");
        break;
      case canopen::NmtState::TOGGLE:
        message.data = "TOGGLE";
        this->diagnostic_collector_->updateAll(
          diagnostic_msgs::msg::DiagnosticStatus::OK, "NMT toggle", "NMT", "TOGGLE");
        break;
      default:
        RCLCPP_ERROR(this->node_->get_logger(), "Unknown NMT State.");
        message.data = "ERROR";
        this->diagnostic_collector_->updateAll(
          diagnostic_msgs::msg::DiagnosticStatus::ERROR, "NMT unknown state", "NMT", "ERROR");
        break;
    }
    RCLCPP_INFO(
      this->node_->get_logger(), "Slave 0x%X: Switched NMT state to %s",
      this->lely_driver_->get_id(), message.data.c_str());

    nmt_state_publisher->publish(message);
  }
}

template <class NODETYPE>
void NodeCanopenProxyDriver<NODETYPE>::on_tpdo(const canopen_interfaces::msg::COData::SharedPtr msg)
{
  ros2_canopen::COData data = {msg->index, msg->subindex, msg->data};
  if (!tpdo_transmit(data))
  {
    RCLCPP_ERROR(this->node_->get_logger(), "Could transmit PDO because driver not activated.");
  }
}

template <class NODETYPE>
bool NodeCanopenProxyDriver<NODETYPE>::tpdo_transmit(ros2_canopen::COData & data)
{
  if (this->activated_.load())
  {
    RCLCPP_INFO(
      this->node_->get_logger(), "Node ID 0x%X: Transmit PDO index %x, subindex %hhu, data %d",
      this->lely_driver_->get_id(), data.index_, data.subindex_,
      data.data_);  // ToDo: Remove or make debug
    this->lely_driver_->tpdo_transmit(data);
    return true;
  }
  return false;
}

template <class NODETYPE>
void NodeCanopenProxyDriver<NODETYPE>::on_rpdo(ros2_canopen::COData d)
{
  if (this->activated_.load())
  {
    // RCLCPP_INFO(
    //   this->node_->get_logger(), "Node ID 0x%X: Received PDO index %#04x, subindex %hhu, data
    //   %x", this->lely_driver_->get_id(), d.index_, d.subindex_, d.data_);
    auto message = canopen_interfaces::msg::COData();
    message.index = d.index_;
    message.subindex = d.subindex_;
    message.data = d.data_;
    rpdo_publisher->publish(message);
  }
}

template <class NODETYPE>
void NodeCanopenProxyDriver<NODETYPE>::on_nmt_state_reset(
  const std_srvs::srv::Trigger::Request::SharedPtr request,
  std_srvs::srv::Trigger::Response::SharedPtr response)
{
  response->success = reset_node_nmt_command();
}

template <class NODETYPE>
bool NodeCanopenProxyDriver<NODETYPE>::reset_node_nmt_command()
{
  if (this->activated_.load())
  {
    this->lely_driver_->nmt_command(canopen::NmtCommand::RESET_NODE);
    return true;
  }
  RCLCPP_ERROR(
    this->node_->get_logger(), "Could not reset device via NMT because driver not activated.");
  return false;
}

template <class NODETYPE>
void NodeCanopenProxyDriver<NODETYPE>::on_nmt_state_start(
  const std_srvs::srv::Trigger::Request::SharedPtr request,
  std_srvs::srv::Trigger::Response::SharedPtr response)
{
  response->success = start_node_nmt_command();
}

template <class NODETYPE>
bool NodeCanopenProxyDriver<NODETYPE>::start_node_nmt_command()
{
  if (this->activated_.load())
  {
    this->lely_driver_->nmt_command(canopen::NmtCommand::START);
    return true;
  }
  RCLCPP_ERROR(
    this->node_->get_logger(), "Could not start device via NMT because driver not activated.");
  return false;
}

template <class NODETYPE>
void NodeCanopenProxyDriver<NODETYPE>::on_sdo_read(
  const canopen_interfaces::srv::CORead::Request::SharedPtr request,
  canopen_interfaces::srv::CORead::Response::SharedPtr response)
{
  ros2_canopen::COData data = {request->index, request->subindex, 0U};
  response->success = sdo_read(data);
  response->data = data.data_;
}

template <class NODETYPE>
bool NodeCanopenProxyDriver<NODETYPE>::sdo_read(ros2_canopen::COData & data)
{
  if (this->activated_.load())
  {
    RCLCPP_INFO(
      this->node_->get_logger(), "Slave 0x%X: SDO Read Call index=0x%X subindex=%hhu",
      this->lely_driver_->get_id(), data.index_, data.subindex_);

    // Only allow one SDO request concurrently
    std::scoped_lock<std::mutex> lk(sdo_mtex);
    // Send read request
    auto f = this->lely_driver_->async_sdo_read(data);
    // Wait for response
    f.wait();
    // Process response
    try
    {
      data.data_ = f.get().data_;
    }
    catch (std::exception & e)
    {
      RCLCPP_ERROR(this->node_->get_logger(), e.what());
      return false;
    }
    return true;
  }
  RCLCPP_ERROR(this->node_->get_logger(), "Could not read from SDO because driver not activated.");
  return false;
}

template <class NODETYPE>
void NodeCanopenProxyDriver<NODETYPE>::on_sdo_write(
  const canopen_interfaces::srv::COWrite::Request::SharedPtr request,
  canopen_interfaces::srv::COWrite::Response::SharedPtr response)
{
  ros2_canopen::COData data = {request->index, request->subindex, request->data};
  response->success = sdo_write(data);
}

template <class NODETYPE>
bool NodeCanopenProxyDriver<NODETYPE>::sdo_write(ros2_canopen::COData & data)
{
  if (this->activated_.load())
  {
    RCLCPP_INFO(
      this->node_->get_logger(), "Slave 0x%X: SDO Write Call index=0x%X subindex=%hhu data=%u",
      this->lely_driver_->get_id(), data.index_, data.subindex_, data.data_);

    // Only allow one SDO request concurrently
    std::scoped_lock<std::mutex> lk(sdo_mtex);

    // Send write request
    auto f = this->lely_driver_->async_sdo_write(data);
    // Wait for request to complete
    f.wait();

    // Process response
    try
    {
      return f.get();
    }
    catch (std::exception & e)
    {
      RCLCPP_ERROR(this->node_->get_logger(), e.what());
      return false;
    }
    return true;
  }
  RCLCPP_ERROR(this->node_->get_logger(), "Could not write to SDO because driver not activated.");
  return false;
}

template <class NODETYPE>
void NodeCanopenProxyDriver<NODETYPE>::diagnostic_callback(
  diagnostic_updater::DiagnosticStatusWrapper & stat)
{
  stat.summary(this->diagnostic_collector_->getLevel(), this->diagnostic_collector_->getMessage());
  stat.add("device_state", this->diagnostic_collector_->getValue("DEVICE"));
  stat.add("nmt_state", this->diagnostic_collector_->getValue("NMT"));
  stat.add("emcy_state", this->diagnostic_collector_->getValue("EMCY"));
}

#endif