ros_scanner_node.h
Go to the documentation of this file.
1 // Copyright (c) 2020-2022 Pilz GmbH & Co. KG
2 //
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU Lesser General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public License
14 // along with this program. If not, see <https://www.gnu.org/licenses/>.
15 
16 #ifndef PSEN_SCAN_V2_ROS_SCANNER_NODE_H
17 #define PSEN_SCAN_V2_ROS_SCANNER_NODE_H
18 
19 #include <stdexcept>
20 #include <string>
21 #include <atomic>
22 #include <chrono>
23 #include <future>
24 #include <algorithm>
25 
26 #include <fmt/format.h>
27 
28 #include <gtest/gtest_prod.h>
29 
30 #include <ros/ros.h>
31 #include <std_msgs/UInt8.h>
32 #include <sensor_msgs/LaserScan.h>
33 
35 
41 
45 namespace psen_scan_v2
46 {
47 using namespace psen_scan_v2_standalone;
48 using namespace std::chrono_literals;
49 
54 template <typename S = ScannerV2>
56 {
57 public:
68  const std::string& topic,
69  const std::string& tf_prefix,
70  const double& x_axis_rotation,
71  const ScannerConfiguration& scanner_config);
72 
77  void run();
79  void terminate();
80 
81 private:
82  void laserScanCallback(const LaserScan& scan);
83  void publishChangedIOStates(const std::vector<psen_scan_v2_standalone::IOState>& io_states);
84 
85 private:
88  std::unordered_map<ScannerId, ros::Publisher> pubs_scan_;
91  std::unordered_map<ScannerId, std::string> tf_prefixes_;
94  std::atomic_bool terminate_{ false };
95 
97 
98  friend class RosScannerNodeTests;
99  FRIEND_TEST(RosScannerNodeTests, shouldStartAndStopSuccessfullyIfScannerRespondsToRequests);
100  FRIEND_TEST(RosScannerNodeTests, shouldPublishScansWhenLaserScanCallbackIsInvoked);
101  FRIEND_TEST(RosScannerNodeTests, shouldPublishActiveZonesetWhenLaserScanCallbackIsInvoked);
102  FRIEND_TEST(RosScannerNodeTests, shouldWaitWhenStopRequestResponseIsMissing);
103  FRIEND_TEST(RosScannerNodeTests, shouldProvideScanTopic);
104  FRIEND_TEST(RosScannerNodeTests, shouldProvideActiveZonesetTopic);
105  FRIEND_TEST(RosScannerNodeTests, shouldPublishScanEqualToConversionOfSuppliedLaserScan);
106  FRIEND_TEST(RosScannerNodeTests, shouldThrowExceptionSetInScannerStartFuture);
107  FRIEND_TEST(RosScannerNodeTests, shouldThrowExceptionSetInScannerStopFuture);
108  FRIEND_TEST(RosScannerNodeTests, shouldPublishChangedIOStatesEqualToConversionOfSuppliedStandaloneIOStates);
109  FRIEND_TEST(RosScannerNodeTests, shouldPublishLatchedOnIOStatesTopic);
110  FRIEND_TEST(RosScannerNodeTests, shouldLogChangedIOStates);
111 };
112 
114 
115 template <typename S>
117  const std::string& topic,
118  const std::string& tf_prefix,
119  const double& x_axis_rotation,
120  const ScannerConfiguration& scanner_config)
121  : nh_(nh)
122  , x_axis_rotation_(x_axis_rotation)
123  , scanner_(scanner_config, std::bind(&ROSScannerNodeT<S>::laserScanCallback, this, std::placeholders::_1))
124 {
125  pubs_scan_.insert(std::make_pair(ScannerId::master, nh_.advertise<sensor_msgs::LaserScan>(topic, 1)));
126  tf_prefixes_.insert(std::make_pair(ScannerId::master, tf_prefix));
127  for (int i = 0; i < scanner_config.nrSubscribers(); i++)
128  {
130  std::string topic_subscriber = topic + "_" + psen_scan_v2_standalone::configuration::SCANNER_ID_TO_STRING.at(id);
131  pubs_scan_.insert(std::make_pair(id, nh_.advertise<sensor_msgs::LaserScan>(topic_subscriber, 1)));
132  std::string tf_prefix_subscriber =
134  tf_prefixes_.insert(std::make_pair(id, tf_prefix_subscriber));
135  }
136  pub_zone_ = nh_.advertise<std_msgs::UInt8>("active_zoneset", 1);
137  pub_io_ = nh_.advertise<psen_scan_v2::IOState>("io_state", 6, true /* latched */);
138 }
139 
140 template <typename S>
142 {
143  try
144  {
145  std::string tf_prefix_with_subscriber = tf_prefixes_.at(scan.scannerId());
146  const auto laser_scan_msg = toLaserScanMsg(scan, tf_prefix_with_subscriber, x_axis_rotation_);
148  "ScannerNode",
149  "Publishing laser scan with angle_min={:.1f} angle_max={:.1f} angle_increment={:.1f} degrees. {} angle values.",
150  data_conversion_layer::radianToDegree(laser_scan_msg.angle_min),
151  data_conversion_layer::radianToDegree(laser_scan_msg.angle_max),
152  data_conversion_layer::radianToDegree(laser_scan_msg.angle_increment),
153  laser_scan_msg.ranges.size());
154  pubs_scan_.at(scan.scannerId()).publish(laser_scan_msg);
155 
156  std_msgs::UInt8 active_zoneset;
157  active_zoneset.data = scan.activeZoneset();
158  pub_zone_.publish(active_zoneset);
159 
160  publishChangedIOStates(scan.ioStates());
161  }
162  // LCOV_EXCL_START
163  catch (const std::invalid_argument& e)
164  {
165  ROS_ERROR_STREAM(e.what());
166  }
167  // LCOV_EXCL_STOP
168 }
169 
170 template <typename S>
171 void ROSScannerNodeT<S>::publishChangedIOStates(const std::vector<psen_scan_v2_standalone::IOState>& io_states)
172 {
173  std::string tf_prefix_with_subscriber = tf_prefixes_.at(ScannerId::master); // assuming all IO is at the master
174  for (const auto& io : io_states)
175  {
176  if (last_io_state_ != io)
177  {
178  pub_io_.publish(toIOStateMsg(io, tf_prefix_with_subscriber));
179 
180  PSENSCAN_INFO("RosScannerNode",
181  "IOs changed, new input: {}, new output: {}",
182  formatPinStates(io.changedInputStates(last_io_state_)),
183  formatPinStates(io.changedOutputStates(last_io_state_)));
184  last_io_state_ = io;
185  }
186  }
187 }
188 
189 template <typename S>
191 {
192  terminate_ = true;
193 }
194 
195 template <typename S>
197 {
198  ros::Rate r(10);
199  auto start_future = scanner_.start();
200  const auto start_status = start_future.wait_for(3s);
201  if (start_status == std::future_status::ready)
202  {
203  start_future.get(); // Throws std::runtime_error if start not successful
204  }
205 
206  while (ros::ok() && !terminate_)
207  {
208  r.sleep(); // LCOV_EXCL_LINE can not be reached deterministically
209  }
210  auto stop_future = scanner_.stop();
211 
212  const auto stop_status = stop_future.wait_for(3s);
213  if (stop_status == std::future_status::ready)
214  {
215  stop_future.get(); // Throws std::runtime_error if stop not successful
216  }
217 }
218 
219 } // namespace psen_scan_v2
220 
221 #endif // PSEN_SCAN_V2_ROS_SCANNER_NODE_H
psen_scan_v2::ROSScannerNodeT::ROSScannerNodeT
ROSScannerNodeT(ros::NodeHandle &nh, const std::string &topic, const std::string &tf_prefix, const double &x_axis_rotation, const ScannerConfiguration &scanner_config)
Constructor.
Definition: ros_scanner_node.h:116
psen_scan_v2::ROSScannerNodeT::pub_io_
ros::Publisher pub_io_
Definition: ros_scanner_node.h:90
psen_scan_v2_standalone::configuration::ScannerId
ScannerId
Definition: scanner_ids.h:27
psen_scan_v2_standalone::formatPinStates
std::string formatPinStates(const std::vector< PinState > &pins)
Formats a vector of PinStates.
Definition: io_state.h:59
psen_scan_v2::ROSScannerNodeT::nh_
ros::NodeHandle nh_
Definition: ros_scanner_node.h:86
psen_scan_v2::ROSScannerNodeT::pubs_scan_
std::unordered_map< ScannerId, ros::Publisher > pubs_scan_
Definition: ros_scanner_node.h:88
laserScanCallback
void laserScanCallback(const LaserScan &scan)
Definition: main.cpp:36
psen_scan_v2_standalone::configuration::SCANNER_ID_TO_STRING
static const std::map< ScannerId, std::string > SCANNER_ID_TO_STRING
Definition: scanner_ids.h:47
ros::Publisher
run
void run(class_loader::ClassLoader *loader)
laserscan_ros_conversions.h
ROS_ERROR_STREAM
#define ROS_ERROR_STREAM(args)
psen_scan_v2::ROSScannerNodeT::publishChangedIOStates
void publishChangedIOStates(const std::vector< psen_scan_v2_standalone::IOState > &io_states)
Definition: ros_scanner_node.h:171
psen_scan_v2_standalone::LaserScan::scannerId
configuration::ScannerId scannerId() const
Definition: laserscan.cpp:136
psen_scan_v2::ROSScannerNodeT::terminate
void terminate()
Terminates the fetching and publishing of scanner data.
Definition: ros_scanner_node.h:190
psen_scan_v2::ROSScannerNodeT
ROS Node that continuously publishes scan data of a single PSENscan laser scanner.
Definition: ros_scanner_node.h:55
format_range.h
s
XmlRpcServer s
ros.h
psen_scan_v2::ROSScannerNodeT::pub_zone_
ros::Publisher pub_zone_
Definition: ros_scanner_node.h:89
psen_scan_v2_standalone::LaserScan::ioStates
const IOData & ioStates() const
Definition: laserscan.cpp:198
psen_scan_v2::toIOStateMsg
psen_scan_v2::IOState toIOStateMsg(const psen_scan_v2_standalone::IOState &io_state, const std::string &frame_id)
Definition: io_state_ros_conversion.h:39
psen_scan_v2::ROSScannerNodeT::tf_prefixes_
std::unordered_map< ScannerId, std::string > tf_prefixes_
Definition: ros_scanner_node.h:91
scanner_v2.h
ros::NodeHandle::advertise
Publisher advertise(AdvertiseOptions &ops)
ros::ok
ROSCPP_DECL bool ok()
psen_scan_v2_standalone::data_conversion_layer::radianToDegree
static constexpr double radianToDegree(const double &angle_in_rad)
Definition: angle_conversions.h:28
psen_scan_v2_standalone::configuration::subscriber_number_to_scanner_id
static ScannerId subscriber_number_to_scanner_id(uint8_t nr)
Definition: scanner_ids.h:42
psen_scan_v2
Root namespace for the ROS part.
Definition: active_zoneset_node.h:29
psen_scan_v2::ROSScannerNode
ROSScannerNodeT ROSScannerNode
Definition: ros_scanner_node.h:113
psen_scan_v2::toLaserScanMsg
sensor_msgs::LaserScan toLaserScanMsg(const LaserScan &laserscan, const std::string &frame_id, const double x_axis_rotation)
Definition: laserscan_ros_conversions.h:28
PSENSCAN_INFO
#define PSENSCAN_INFO(name,...)
Definition: logging.h:61
psen_scan_v2::ROSScannerNodeT::x_axis_rotation_
double x_axis_rotation_
Definition: ros_scanner_node.h:92
psen_scan_v2::ROSScannerNodeT::scanner_
S scanner_
Definition: ros_scanner_node.h:93
psen_scan_v2_standalone::ScannerConfiguration::nrSubscribers
uint8_t nrSubscribers() const
Definition: scanner_configuration.h:156
ros::Rate::sleep
bool sleep()
psen_scan_v2_standalone::IOState
Represents the set of all I/Os of the scanner and their states.
Definition: io_state.h:76
psen_scan_v2::ROSScannerNodeT::laserScanCallback
void laserScanCallback(const LaserScan &scan)
Definition: ros_scanner_node.h:141
io_state_ros_conversion.h
scanner_ids.h
psen_scan_v2::ROSScannerNodeT::run
void run()
Continuously fetches data from the scanner and publishes the data into the ROS network.
Definition: ros_scanner_node.h:196
std
psen_scan_v2_standalone::LaserScan
This class represents a single laser scan in the <tf_prefix> target frame.
Definition: laserscan.h:47
psen_scan_v2_standalone
Root namespace in which the software components to communicate with the scanner (firmware-version: 2)...
Definition: udp_client.h:41
PSENSCAN_INFO_ONCE
#define PSENSCAN_INFO_ONCE(name,...)
Definition: logging.h:85
ros::Rate
psen_scan_v2_standalone::LaserScan::activeZoneset
uint8_t activeZoneset() const
Definition: laserscan.cpp:151
angle_conversions.h
psen_scan_v2_standalone::protocol_layer::scanner_events
Contains the events needed to define and implement the scanner protocol.
Definition: scanner_events.h:30
ros::NodeHandle
psen_scan_v2_standalone::ScannerConfiguration
Higher level data type storing the configuration details of the scanner like scanner IP,...
Definition: scanner_configuration.h:34


psen_scan_v2
Author(s): Pilz GmbH + Co. KG
autogenerated on Sat Jun 22 2024 02:46:11