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 
40 
44 namespace psen_scan_v2
45 {
46 using namespace psen_scan_v2_standalone;
47 using namespace std::chrono_literals;
48 
53 template <typename S = ScannerV2>
55 {
56 public:
67  const std::string& topic,
68  const std::string& tf_prefix,
69  const double& x_axis_rotation,
70  const ScannerConfiguration& scanner_config);
71 
76  void run();
78  void terminate();
79 
80 private:
81  void laserScanCallback(const LaserScan& scan);
82  void publishChangedIOStates(const std::vector<psen_scan_v2_standalone::IOState>& io_states);
83 
84 private:
89  std::string tf_prefix_;
92  std::atomic_bool terminate_{ false };
93 
95 
96  friend class RosScannerNodeTests;
97  FRIEND_TEST(RosScannerNodeTests, shouldStartAndStopSuccessfullyIfScannerRespondsToRequests);
98  FRIEND_TEST(RosScannerNodeTests, shouldPublishScansWhenLaserScanCallbackIsInvoked);
99  FRIEND_TEST(RosScannerNodeTests, shouldPublishActiveZonesetWhenLaserScanCallbackIsInvoked);
100  FRIEND_TEST(RosScannerNodeTests, shouldWaitWhenStopRequestResponseIsMissing);
101  FRIEND_TEST(RosScannerNodeTests, shouldProvideScanTopic);
102  FRIEND_TEST(RosScannerNodeTests, shouldProvideActiveZonesetTopic);
103  FRIEND_TEST(RosScannerNodeTests, shouldPublishScanEqualToConversionOfSuppliedLaserScan);
104  FRIEND_TEST(RosScannerNodeTests, shouldThrowExceptionSetInScannerStartFuture);
105  FRIEND_TEST(RosScannerNodeTests, shouldThrowExceptionSetInScannerStopFuture);
106  FRIEND_TEST(RosScannerNodeTests, shouldPublishChangedIOStatesEqualToConversionOfSuppliedStandaloneIOStates);
107  FRIEND_TEST(RosScannerNodeTests, shouldPublishLatchedOnIOStatesTopic);
108  FRIEND_TEST(RosScannerNodeTests, shouldLogChangedIOStates);
109 };
110 
112 
113 template <typename S>
115  const std::string& topic,
116  const std::string& tf_prefix,
117  const double& x_axis_rotation,
118  const ScannerConfiguration& scanner_config)
119  : nh_(nh)
120  , tf_prefix_(tf_prefix)
121  , x_axis_rotation_(x_axis_rotation)
122  , scanner_(scanner_config, std::bind(&ROSScannerNodeT<S>::laserScanCallback, this, std::placeholders::_1))
123 {
124  pub_scan_ = nh_.advertise<sensor_msgs::LaserScan>(topic, 1);
125  pub_zone_ = nh_.advertise<std_msgs::UInt8>("active_zoneset", 1);
126  pub_io_ = nh_.advertise<psen_scan_v2::IOState>("io_state", 6, true /* latched */);
127 }
128 
129 template <typename S>
131 {
132  try
133  {
134  const auto laser_scan_msg = toLaserScanMsg(scan, tf_prefix_, x_axis_rotation_);
136  "ScannerNode",
137  "Publishing laser scan with angle_min={:.1f} angle_max={:.1f} angle_increment={:.1f} degrees. {} angle values.",
138  data_conversion_layer::radianToDegree(laser_scan_msg.angle_min),
139  data_conversion_layer::radianToDegree(laser_scan_msg.angle_max),
140  data_conversion_layer::radianToDegree(laser_scan_msg.angle_increment),
141  laser_scan_msg.ranges.size());
142  pub_scan_.publish(laser_scan_msg);
143 
144  std_msgs::UInt8 active_zoneset;
145  active_zoneset.data = scan.activeZoneset();
146  pub_zone_.publish(active_zoneset);
147 
148  publishChangedIOStates(scan.ioStates());
149  }
150  // LCOV_EXCL_START
151  catch (const std::invalid_argument& e)
152  {
153  ROS_ERROR_STREAM(e.what());
154  }
155  // LCOV_EXCL_STOP
156 }
157 
158 template <typename S>
159 void ROSScannerNodeT<S>::publishChangedIOStates(const std::vector<psen_scan_v2_standalone::IOState>& io_states)
160 {
161  for (const auto& io : io_states)
162  {
163  if (last_io_state_ != io)
164  {
165  pub_io_.publish(toIOStateMsg(io, tf_prefix_));
166 
167  PSENSCAN_INFO("RosScannerNode",
168  "IOs changed, new input: {}, new output: {}",
169  formatPinStates(io.changedInputStates(last_io_state_)),
170  formatPinStates(io.changedOutputStates(last_io_state_)));
171  last_io_state_ = io;
172  }
173  }
174 }
175 
176 template <typename S>
178 {
179  terminate_ = true;
180 }
181 
182 template <typename S>
184 {
185  ros::Rate r(10);
186  auto start_future = scanner_.start();
187  const auto start_status = start_future.wait_for(3s);
188  if (start_status == std::future_status::ready)
189  {
190  start_future.get(); // Throws std::runtime_error if start not successful
191  }
192 
193  while (ros::ok() && !terminate_)
194  {
195  r.sleep(); // LCOV_EXCL_LINE can not be reached deterministically
196  }
197  auto stop_future = scanner_.stop();
198 
199  const auto stop_status = stop_future.wait_for(3s);
200  if (stop_status == std::future_status::ready)
201  {
202  stop_future.get(); // Throws std::runtime_error if stop not successful
203  }
204 }
205 
206 } // namespace psen_scan_v2
207 
208 #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:114
psen_scan_v2::ROSScannerNodeT::pub_io_
ros::Publisher pub_io_
Definition: ros_scanner_node.h:88
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:85
laserScanCallback
void laserScanCallback(const LaserScan &scan)
Definition: main.cpp:36
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:159
psen_scan_v2::ROSScannerNodeT::terminate
void terminate()
Terminates the fetching and publishing of scanner data.
Definition: ros_scanner_node.h:177
psen_scan_v2::ROSScannerNodeT
ROS Node that continuously publishes scan data of a single PSENscan laser scanner.
Definition: ros_scanner_node.h:54
format_range.h
s
XmlRpcServer s
ros.h
psen_scan_v2::ROSScannerNodeT::pub_zone_
ros::Publisher pub_zone_
Definition: ros_scanner_node.h:87
psen_scan_v2_standalone::LaserScan::ioStates
const IOData & ioStates() const
Definition: laserscan.cpp:190
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
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
Root namespace for the ROS part.
Definition: active_zoneset_node.h:29
psen_scan_v2::ROSScannerNode
ROSScannerNodeT ROSScannerNode
Definition: ros_scanner_node.h:111
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:90
psen_scan_v2::ROSScannerNodeT::scanner_
S scanner_
Definition: ros_scanner_node.h:91
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:130
io_state_ros_conversion.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:183
std
psen_scan_v2::ROSScannerNodeT::pub_scan_
ros::Publisher pub_scan_
Definition: ros_scanner_node.h:86
psen_scan_v2_standalone::LaserScan
This class represents a single laser scan in the <tf_prefix> target frame.
Definition: laserscan.h:46
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
r
r
psen_scan_v2_standalone::LaserScan::activeZoneset
uint8_t activeZoneset() const
Definition: laserscan.cpp:143
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::ROSScannerNodeT::tf_prefix_
std::string tf_prefix_
Definition: ros_scanner_node.h:89
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 Nov 25 2023 03:46:26