pilz_modbus_client_node.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018 Pilz GmbH & Co. KG
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU Lesser General Public License for more details.
13 
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <algorithm>
19 #include <numeric>
20 #include <stdlib.h>
21 
22 #include <ros/ros.h>
23 
24 #include <pilz_utils/get_param.h>
31 
32 static constexpr int32_t MODBUS_CONNECTION_RETRIES_DEFAULT {10};
33 static constexpr double MODBUS_CONNECTION_RETRY_TIMEOUT_S_DEFAULT {1.0};
34 static constexpr int MODBUS_RESPONSE_TIMEOUT_MS {20};
35 
36 using namespace prbt_hardware_support;
37 
41 int main(int argc, char **argv)
42 {
43  ros::init(argc, argv, "modbus_client_node");
44 
45  ros::NodeHandle pnh{"~"};
46  ros::NodeHandle nh;
47 
48  std::vector<unsigned short> registers_to_read;
49  bool has_register_range_parameters = pnh.hasParam(PARAM_NUM_REGISTERS_TO_READ_STR) &&
51 
52  if (!has_register_range_parameters)
53  {
54  ROS_INFO_STREAM("Parameters for register range are not set. Will try to determine range from api spec...");
55 
56  try
57  {
58  ModbusApiSpec api_spec(nh);
59  api_spec.getAllDefinedRegisters(registers_to_read);
60  ROS_DEBUG("registers_to_read.size() %d", registers_to_read.size());
61  }
62  // LCOV_EXCL_START Can be ignored here, exceptions of ModbusApiSpec are tested in unittest_modbus_api_spec
63  catch (const ModbusApiSpecException &ex)
64  {
65  ROS_ERROR_STREAM(ex.what());
66  return EXIT_FAILURE;
67  }
68  // LCOV_EXCL_STOP
69  }
70 
71  // LCOV_EXCL_START Simple parameter reading not analyzed
72 
73  std::string ip;
74  int port;
75 
76  try
77  {
78  ip = pilz_utils::getParam<std::string>(pnh, PARAM_MODBUS_SERVER_IP_STR);
79  port = pilz_utils::getParam<int>(pnh, PARAM_MODBUS_SERVER_PORT_STR);
80  if (has_register_range_parameters)
81  {
82  int num_registers_to_read = pilz_utils::getParam<int>(pnh, PARAM_NUM_REGISTERS_TO_READ_STR);
83  int index_of_first_register = pilz_utils::getParam<int>(pnh, PARAM_INDEX_OF_FIRST_REGISTER_TO_READ_STR);
84  registers_to_read = std::vector<unsigned short>(num_registers_to_read);
85  std::iota(registers_to_read.begin(), registers_to_read.end(), index_of_first_register);
86  }
87  }
88  catch (const std::runtime_error &ex)
89  {
90  ROS_ERROR_STREAM(ex.what());
91  return EXIT_FAILURE;
92  }
93 
94  int32_t modbus_connection_retries{MODBUS_CONNECTION_RETRIES_DEFAULT};
95  pnh.param<int32_t>(PARAM_MODBUS_CONNECTION_RETRIES, modbus_connection_retries, MODBUS_CONNECTION_RETRIES_DEFAULT);
96 
97  double modbus_connection_retry_timeout_s{MODBUS_CONNECTION_RETRY_TIMEOUT_S_DEFAULT};
98  pnh.param<double>(PARAM_MODBUS_CONNECTION_RETRY_TIMEOUT, modbus_connection_retry_timeout_s,
100 
101  int response_timeout_ms;
102  pnh.param<int>(PARAM_MODBUS_RESPONSE_TIMEOUT_STR, response_timeout_ms,
104 
105  std::string modbus_read_topic_name;
106  nh.param<std::string>(PARAM_MODBUS_READ_TOPIC_NAME_STR, modbus_read_topic_name,
108 
109  std::string modbus_write_service_name;
110  nh.param<std::string>(PARAM_MODBUS_WRITE_SERVICE_NAME_STR, modbus_write_service_name,
112 
113 
114  // LCOV_EXCL_STOP
115 
116  prbt_hardware_support::PilzModbusClient modbus_client(pnh,
117  registers_to_read,
118  std::unique_ptr<LibModbusClient>(new LibModbusClient()),
119  static_cast<unsigned int>(response_timeout_ms),
120  modbus_read_topic_name, modbus_write_service_name);
121 
122  ROS_DEBUG_STREAM("Modbus client IP: " << ip << " | Port: " << port);
123  std::ostringstream oss;
124  if (!registers_to_read.empty())
125  {
126  std::copy(registers_to_read.begin(), registers_to_read.end()-1,
127  std::ostream_iterator<unsigned short>(oss, ","));
128  oss << registers_to_read.back();
129  }
130  ROS_DEBUG_STREAM("Registers to read: " << oss.str());
131  ROS_DEBUG_STREAM("Modbus response timeout: " << response_timeout_ms);
132  ROS_DEBUG_STREAM("Modbus read topic: \"" << modbus_read_topic_name << "\"");
133  ROS_DEBUG_STREAM("Modbus write service: \"" << modbus_write_service_name << "\"");
134 
135  bool res = modbus_client.init(ip.c_str(), static_cast<unsigned int>(port),
136  static_cast<unsigned int>(modbus_connection_retries),
137  ros::Duration(modbus_connection_retry_timeout_s));
138 
139  ROS_DEBUG_STREAM("Connection with modbus server " << ip << ":" << port << " established");
140 
141  // LCOV_EXCL_START inside this main ignored, tested multiple times in the unittest
142  if (!res)
143  {
144  ROS_ERROR_STREAM("Connection to modbus server " << ip << ":" << port << " could not be established");
145  return EXIT_FAILURE;
146  }
147 
148  try
149  {
150  modbus_client.run();
151  }
152  catch(PilzModbusClientException& e)
153  {
154  ROS_ERROR_STREAM(e.what());
155  return EXIT_FAILURE;
156  }
157  // LCOV_EXCL_STOP
158 
159  // If the client stop we don't want to kill this node since
160  // with required=true this would kill all nodes and no STOP1 would be performed in case of a disconnect.
161  ros::spin();
162 
163  return EXIT_SUCCESS;
164 }
Expection thrown by prbt_hardware_support::ModbusApiSpec.
static const std::string PARAM_MODBUS_SERVER_PORT_STR
Definition: param_names.h:27
void getAllDefinedRegisters(std::vector< unsigned short > &registers)
Expection thrown by prbt_hardware_support::PilzModbusClient.
static const std::string PARAM_MODBUS_RESPONSE_TIMEOUT_STR
Definition: param_names.h:28
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
static const std::string SERVICE_MODBUS_WRITE
static const std::string PARAM_MODBUS_WRITE_SERVICE_NAME_STR
Definition: param_names.h:30
static constexpr double MODBUS_CONNECTION_RETRY_TIMEOUT_S_DEFAULT
static const std::string TOPIC_MODBUS_READ
ROSCPP_DECL void spin(Spinner &spinner)
static constexpr int32_t MODBUS_CONNECTION_RETRIES_DEFAULT
bool param(const std::string &param_name, T &param_val, const T &default_val) const
static const std::string PARAM_MODBUS_READ_TOPIC_NAME_STR
Definition: param_names.h:29
Connects to a modbus server and publishes the received data into ROS.
static const std::string PARAM_INDEX_OF_FIRST_REGISTER_TO_READ_STR
Definition: param_names.h:31
static constexpr int MODBUS_RESPONSE_TIMEOUT_MS
#define ROS_DEBUG_STREAM(args)
int main(int argc, char **argv)
Read requested parameters, start and initialize the prbt_hardware_support::PilzModbusClient.
#define ROS_INFO_STREAM(args)
static const std::string PARAM_MODBUS_CONNECTION_RETRIES
Definition: param_names.h:33
static const std::string PARAM_MODBUS_CONNECTION_RETRY_TIMEOUT
Definition: param_names.h:34
#define ROS_ERROR_STREAM(args)
Wrapper around libmodbus, see https://libmodbus.org/.
Specifies the meaning of the holding registers.
static const std::string PARAM_MODBUS_SERVER_IP_STR
Definition: param_names.h:26
#define ROS_DEBUG(...)
static const std::string PARAM_NUM_REGISTERS_TO_READ_STR
Definition: param_names.h:32


prbt_hardware_support
Author(s):
autogenerated on Tue Feb 2 2021 03:50:17