scanner.cpp
Go to the documentation of this file.
1 // Copyright (c) 2019-2020 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 #include <algorithm>
17 #include <iostream>
18 
19 #include "psen_scan/scanner.h"
20 #include "psen_scan/scanner_data.h"
28 
29 namespace psen_scan
30 {
31 constexpr std::chrono::seconds MIN_FETCH_FRAME_TIMEOUT{ std::chrono::seconds(1) };
32 
33 bool isValidIpAddress(const char* ipAddress)
34 {
35  struct sockaddr_in sa
36  {
37  };
38  int result = inet_pton(AF_INET, ipAddress, &(sa.sin_addr));
39  return result == 1;
40 }
41 
53 Scanner::Scanner(const std::string& scanner_ip,
54  const uint32_t& host_ip,
55  const uint32_t& host_udp_port,
56  const std::string& password,
57  const PSENscanInternalAngle& angle_start,
58  const PSENscanInternalAngle& angle_end,
59  std::unique_ptr<ScannerCommunicationInterface> communication_interface)
60  : scanner_ip_(scanner_ip)
61  , start_monitoring_frame_(password, host_ip, host_udp_port)
62  , stop_monitoring_frame_(password)
63  , angle_start_(angle_start)
64  , angle_end_(angle_end)
65  , previous_monitoring_frame_({})
66  , communication_interface_(std::move(communication_interface))
67 {
68  if (!isValidIpAddress(scanner_ip_.c_str()))
69  {
70  throw PSENScanFatalException("Scanner IP is invalid!");
71  }
72 
73  if (host_udp_port > 65535) // MAX_UINT16
74  {
75  throw PSENScanFatalException("Host UDP Port is too big!");
76  }
77 
78  if (host_udp_port < 1024)
79  {
80  std::cout << "Attention: UDP Port is in IANA Standard Port range (below 1024)! "
81  << "Please consider using a port number above 1024." << std::endl;
82  }
83 
84  if (angle_start >= angle_end)
85  {
86  throw PSENScanFatalException("Attention: Start angle has to be smaller than end angle!");
87  }
88 
89  if (angle_end > MAX_SCAN_ANGLE)
90  {
91  throw PSENScanFatalException("Attention: End angle has to be smaller than the physical Maximum!");
92  }
93 
95  {
96  throw PSENScanFatalException("Nullpointer isn't a valid argument!");
97  }
98 
100 }
101 
107 {
108  communication_interface_->write(boost::asio::buffer(&start_monitoring_frame_, sizeof(StartMonitoringFrame)));
109 }
110 
116 {
117  communication_interface_->write(boost::asio::buffer(&stop_monitoring_frame_, sizeof(StopMonitoringFrame)));
118 }
119 
127 MonitoringFrame Scanner::fetchMonitoringFrame(std::chrono::steady_clock::duration timeout)
128 {
129  MonitoringFrame monitoring_frame;
130  std::size_t bytes_received;
131  auto buf = boost::asio::buffer(&monitoring_frame, sizeof(MonitoringFrame));
132  try
133  {
134  bytes_received = communication_interface_->read(buf, timeout);
135  if (bytes_received != sizeof(MonitoringFrame))
136  {
137  throw FetchMonitoringFrameException("Received Frame length doesn't match MonitoringFrame length!");
138  }
139  }
140  catch (const ScannerReadTimeout& e)
141  {
142  stop();
143  sleep(1);
144  start();
145  throw FetchMonitoringFrameException(e.what() + static_cast<std::string>(" Restarting Scanner!"));
146  }
147  return monitoring_frame;
148 }
149 
159 bool Scanner::parseFields(const MonitoringFrame& monitoring_frame)
160 {
161  if (monitoring_frame.opcode_ != MONITORING_FRAME_OPCODE)
162  {
163  previous_monitoring_frame_ = monitoring_frame;
164  throw ParseMonitoringFrameException("MonitoringFrame's Opcode doesn't match expected value!");
165  }
166 
167  if (monitoring_frame.scanner_id_ != 0)
168  {
169  previous_monitoring_frame_ = monitoring_frame;
170  std::string err = "MonitoringFrame's ScannerID doesn't belong to master! \n";
171  err.append("Please contact the maintainer if you need master+slave functionality!");
173  }
174 
175  if (monitoring_frame.number_of_samples_ > MAX_NUMBER_OF_SAMPLES)
176  {
177  previous_monitoring_frame_ = monitoring_frame;
178  throw ParseMonitoringFrameException("MonitoringFrame's number of samples exceeds the maximum allowed amount!");
179  }
180 
181  return true;
182 }
183 
194 {
195  if (diag_info.ossd1_short_circuit_)
196  {
197  throw DiagnosticInformationException("OSSD1 Overcurrent/Short Circuit!");
198  }
199 
200  if (diag_info.short_circuit_at_least_two_ossd_)
201  {
202  throw DiagnosticInformationException("Short Circuit at least between two OSSDs!");
203  }
204 
206  {
207  throw DiagnosticInformationException("Integrity check problem on any OSSD!");
208  }
209 
210  if (diag_info.internal_error_1_ || diag_info.internal_error_2_ || diag_info.internal_error_3_ ||
211  diag_info.internal_error_4_ || diag_info.internal_error_5_)
212  {
213  throw DiagnosticInformationException("Internal Error!");
214  }
215 
216  if (diag_info.window_cleaning_alarm_)
217  {
218  throw DiagnosticInformationException("Window cleaning alarm!");
219  }
220 
221  if (diag_info.power_supply_problem_)
222  {
223  throw DiagnosticInformationException("Power supply problem!");
224  }
225 
226  if (diag_info.network_problem_)
227  {
228  throw DiagnosticInformationException("Network problem!");
229  }
230 
231  if (diag_info.dust_circuit_failure_)
232  {
233  throw DiagnosticInformationException("Dust circuit failure!");
234  }
235 
236  if (diag_info.measure_problem_)
237  {
238  throw DiagnosticInformationException("Measure problem!");
239  }
240 
241  if (diag_info.incoherence_data_)
242  {
243  throw DiagnosticInformationException("Incoherence data!");
244  }
245 
247  {
248  throw DiagnosticInformationException("Zone: Invalid Input transition or integrity!");
249  }
250 
252  {
253  throw DiagnosticInformationException("Zone: Invalid Input configuration/connection!");
254  }
255 
256  if (diag_info.window_cleaning_warning_)
257  {
258  throw DiagnosticInformationException("Window cleaning warning!");
259  }
260 
261  if (diag_info.internal_communication_problem_)
262  {
263  throw DiagnosticInformationException("Internal communication problem!");
264  }
265 
266  if (diag_info.generic_error_)
267  {
268  throw DiagnosticInformationException("Generic error!");
269  }
270 
271  if (diag_info.display_communication_problem_)
272  {
273  throw DiagnosticInformationException("Display commuication problem!");
274  }
275 
276  if (diag_info.temperature_measurement_problem_)
277  {
278  throw DiagnosticInformationException("Temperature measurement problem!");
279  }
280 
281  if (diag_info.configuration_error_)
282  {
283  throw DiagnosticInformationException("Configuration error!");
284  }
285 
286  if (diag_info.out_of_range_error_)
287  {
288  throw DiagnosticInformationException("Out of range error!");
289  }
290 
291  if (diag_info.temperature_range_error_)
292  {
293  throw DiagnosticInformationException("Temperature range error!");
294  }
295 
296  return true;
297 }
298 
307 {
309 
310  MonitoringFrame monitoring_frame;
311  bool firstrun = true;
312  do
313  {
314  std::chrono::steady_clock::duration fetch_frame_timeout{ MIN_FETCH_FRAME_TIMEOUT };
315  bool exception_occured = false;
316  do
317  {
318  exception_occured = false;
319  try
320  {
321  monitoring_frame = fetchMonitoringFrame(fetch_frame_timeout);
322  parseFields(monitoring_frame);
324  }
325  catch (const FetchMonitoringFrameException& e)
326  {
327  std::cerr << e.what() << '\n';
328  exception_occured = true;
329  fetch_frame_timeout = psen_scan_utils::adjustTimeout(fetch_frame_timeout);
330  }
331  } while (exception_occured);
332 
333  if (firstrun && MIN_SCAN_ANGLE != PSENscanInternalAngle(monitoring_frame.from_theta_))
334  {
335  previous_monitoring_frame_ = monitoring_frame;
336  throw CoherentMonitoringFramesException("First Monitoring frame missing!");
337  }
338 
339  if (!firstrun && monitoring_frame.from_theta_ < previous_monitoring_frame_.from_theta_)
340  {
341  previous_monitoring_frame_ = monitoring_frame;
342  throw CoherentMonitoringFramesException("New Cycle has begun! Disregard old values!");
343  }
344 
345  if (PSENscanInternalAngle(monitoring_frame.from_theta_) != MIN_SCAN_ANGLE &&
347  {
348  previous_monitoring_frame_ = monitoring_frame;
349  throw CoherentMonitoringFramesException("Resolution of new MonitoringFrame doesn't match previous "
350  "resolution(s)!");
351  }
352 
353  if (!firstrun && ((monitoring_frame.to_theta() != MAX_SCAN_ANGLE &&
355  (monitoring_frame.to_theta() == MAX_SCAN_ANGLE &&
356  previous_monitoring_frame_.scan_counter_ + 1 != monitoring_frame.scan_counter_)))
357  {
358  previous_monitoring_frame_ = monitoring_frame;
359  throw CoherentMonitoringFramesException("ScanCounter of new MonitoringFrame doesn't match previous ScanCounter!");
360  }
361 
362  if (MIN_SCAN_ANGLE != PSENscanInternalAngle(monitoring_frame.from_theta_) &&
364  {
365  previous_monitoring_frame_ = monitoring_frame;
366  throw CoherentMonitoringFramesException("Start angle of new MonitoringFrame doesn't match angle of previous "
367  "MonitoringFrame!");
368  }
369 
370  uint16_t length = std::min(monitoring_frame.number_of_samples_,
371  MAX_NUMBER_OF_SAMPLES); // TODO(gsansone): Is Exception. Remove check?
372  scan.measures_.insert(
373  scan.measures_.end(), monitoring_frame.measures_.begin(), monitoring_frame.measures_.begin() + length);
374 
375  previous_monitoring_frame_ = monitoring_frame;
376  firstrun = false; // next run is the second run or higher
377  } while (monitoring_frame.to_theta() != MAX_SCAN_ANGLE);
378  scan.resolution_ = PSENscanInternalAngle(monitoring_frame.resolution_);
379 
380  auto begin_position = static_cast<int>(angle_start_) / static_cast<int>(scan.resolution_);
381  if (begin_position > 0)
382  {
383  scan.measures_.erase(scan.measures_.begin(), scan.measures_.begin() + begin_position);
384  }
385 
386  PSENscanInternalAngle temp_max_scan_angle = MAX_SCAN_ANGLE;
387  auto end_position = static_cast<int>(temp_max_scan_angle - angle_end_) / static_cast<int>(scan.resolution_);
388  if (end_position > 0)
389  {
390  scan.measures_.erase(scan.measures_.end() - end_position, scan.measures_.end());
391  }
392 
393  return scan;
394 }
395 
396 } // namespace psen_scan
DiagnosticInformation diagnostic_information_
Frame containing all necessary fields for a Start Monitoring Command.
std::string scanner_ip_
Definition: scanner.h:58
std::array< uint16_t, 550 > measures_
DiagnosticInformation Bitfield for DiagnosticArea.
MonitoringFrame as coming from Laserscanner.
StopMonitoringFrame stop_monitoring_frame_
Definition: scanner.h:60
LaserScan getCompleteScan()
Reads from UDP Interface until complete laserscan object can be formed.
Definition: scanner.cpp:306
void start()
Send start signal to Laserscanner.
Definition: scanner.cpp:106
uint16_t const MAX_NUMBER_OF_SAMPLES
Definition: scanner_data.h:51
PSENscanInternalAngle const MAX_SCAN_ANGLE(2750)
MonitoringFrame fetchMonitoringFrame(std::chrono::steady_clock::duration timeout)
Gets one MonitoringFrame from Laserscanner.
Definition: scanner.cpp:127
Class to hold the data for one laserscan without depencies to ROS.
Definition: laserscan.h:29
bool isDiagnosticInformationOk(const DiagnosticInformation &diag_info)
Checks if DiagnosticInformation Bitfield contains no errors.
Definition: scanner.cpp:193
uint32_t const MONITORING_FRAME_OPCODE
Definition: scanner_data.h:50
MonitoringFrame previous_monitoring_frame_
Definition: scanner.h:63
bool isValidIpAddress(const char *ipAddress)
Definition: scanner.cpp:33
static std::chrono::steady_clock::duration adjustTimeout(const std::chrono::steady_clock::duration &timeout, const std::chrono::steady_clock::duration timeout_increase=DEFAULT_TIMEOUT_INCREASE, const std::chrono::steady_clock::duration max_timeout=DEFAULT_MAX_TIMEOUT)
Increases the given timeout by the specified timeout increase. If the new timeout exceeds the max tim...
DiagnosticArea diagnostic_area_
constexpr std::chrono::seconds MIN_FETCH_FRAME_TIMEOUT
Definition: scanner.cpp:31
std::vector< uint16_t > measures_
Definition: laserscan.h:36
bool parseFields(const MonitoringFrame &monitoring_frame)
Parses a MonitoringFrame to check whether all fields are as expected.
Definition: scanner.cpp:159
Frame containing all necessary fields for a Stop Monitoring Command.
PSENscanInternalAngle const MIN_SCAN_ANGLE(0)
PSENscanInternalAngle angle_end_
Definition: scanner.h:62
PSENscanInternalAngle resolution_
Definition: laserscan.h:37
PSENscanInternalAngle to_theta() const
Class to model angles in PSENscan internal format (tenth of degrees)
TFSIMD_FORCE_INLINE tfScalar length(const Quaternion &q)
PSENscanInternalAngle angle_start_
Definition: scanner.h:61
StartMonitoringFrame start_monitoring_frame_
Definition: scanner.h:59
Scanner(const std::string &scanner_ip, const uint32_t &host_ip, const uint32_t &host_udp_port, const std::string &password, const PSENscanInternalAngle &angle_start, const PSENscanInternalAngle &angle_end, std::unique_ptr< ScannerCommunicationInterface > communication_interface)
Construct a new Scanner:: Scanner object.
Definition: scanner.cpp:53
void stop()
Send stop signal to Laserscanner.
Definition: scanner.cpp:115
std::unique_ptr< ScannerCommunicationInterface > communication_interface_
Definition: scanner.h:64


psen_scan
Author(s):
autogenerated on Mon Feb 28 2022 23:16:20