sick_scan_messages.cpp
Go to the documentation of this file.
1 /*
2  * @brief Implementation of ROS messages for sick_scan
3  *
4  * Copyright (C) 2021, Ing.-Buero Dr. Michael Lehning, Hildesheim
5  * Copyright (C) 2021, SICK AG, Waldkirch
6  * All rights reserved.
7  *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 *
21 * All rights reserved.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions are met:
25 *
26 * * Redistributions of source code must retain the above copyright
27 * notice, this list of conditions and the following disclaimer.
28 * * Redistributions in binary form must reproduce the above copyright
29 * notice, this list of conditions and the following disclaimer in the
30 * documentation and/or other materials provided with the distribution.
31 * * Neither the name of Osnabrueck University nor the names of its
32 * contributors may be used to endorse or promote products derived from
33 * this software without specific prior written permission.
34 * * Neither the name of SICK AG nor the names of its
35 * contributors may be used to endorse or promote products derived from
36 * this software without specific prior written permission
37 * * Neither the name of Ing.-Buero Dr. Michael Lehning nor the names of its
38 * contributors may be used to endorse or promote products derived from
39 * this software without specific prior written permission
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
42 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
45 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
46 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
48 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
49 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
51 * POSSIBILITY OF SUCH DAMAGE.
52  *
53  * Created on: 12.01.2021
54  *
55  * Authors:
56  * Michael Lehning <michael.lehning@lehning.de>
57  *
58  * Based on the TiM communication example by SICK AG.
59  *
60  */
61 #include <iomanip>
63 
65 {
66 }
67 
69 {
70 }
71 
72 static char* readNextAsciiHexValue(char* msg_ptr, uint32_t* value)
73 {
74  if (msg_ptr)
75  {
76  if(1 != sscanf(msg_ptr, "%x", value))
77  return 0; // read error
78  msg_ptr = strchr(msg_ptr, ' ');
79  while (msg_ptr && isspace(*msg_ptr))
80  msg_ptr++;
81  }
82  return msg_ptr;
83 }
84 
85 /*
86  * @brief parses and converts a lidar LIDoutputstate message to a ros LIDoutputstate message
87  *
88  * Example LIDoutputstate message from 20210111_sick_tim781s_mon_elephant.pcapng.json:
89  * "tcp.description": ".......4sSN LIDoutputstate .................................E",
90  * "tcp.payload": "02:02:02:02:00:00:00:34:73:53:4e:20:4c:49:44:6f:75:74:70:75:74:73:74:61:74:65:20:00:00:00:00:00:00:01:00:00:01:26:00:00:00:01:22:00:00:00:00:00:01:00:00:00:04:02:00:00:00:00:00:00:45"
91  * 33 byte LIDoutputstate payload after "sSN LIDoutputstate ": "00:00:00:00:00:00:01:00:00:01:26:00:00:00:01:22:00:00:00:00:00:01:00:00:00:04:02:00:00:00:00:00:00"
92  *
93  * @param[in] timeStamp timestamp on receiving the lidar message
94  * @param[in] receiveBuffer byte array of lidar message
95  * @param[in] receiveLength size of lidar message in byte
96  * @param[in] useBinaryProtocol binary lidar message (true, Cola-B) or ascii lidar message (false, Cola-A)
97  * @param[in] frame_id frame id of output message
98  * @param[out] output_msg converted output message
99  *
100  * @return true on success, false on error
101  */
102 bool sick_scan_xd::SickScanMessages::parseLIDoutputstateMsg(const rosTime& timeStamp, uint8_t* receiveBuffer, int receiveLength, bool useBinaryProtocol, const std::string& frame_id, sick_scan_msg::LIDoutputstateMsg& output_msg)
103 {
104  if(useBinaryProtocol) // Cola-A support in release 3.6
105  {
106  // parse and convert LIDoutputstate message
107  int dbg_telegram_length = receiveLength;
108  std::string dbg_telegram_hex_copy = DataDumper::binDataToAsciiString(receiveBuffer, receiveLength);
109  int msg_start_idx = 27;
110  int msg_parameter_length = receiveLength - msg_start_idx - 1; // Cola-B: payload + 1 byte CRC // Cola-A: payload + 1 byte ETX
111  if(msg_parameter_length < 8) // at least 6 byte (version+system) + 2 byte (timestamp status)
112  {
113  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLIDoutputstateMsg(): received " << receiveLength << " byte, expected at least 8 byte (" << __FILE__ << ":" << __LINE__ << ")");
114  return false;
115  }
116  // The LIDoutputstate parameter may have 11 byte timestamp or not, i.e. the parameter format is either:
117  // a) msg_parameter_length := (6 byte version+system) + number_of_output_states * (5 byte state+counter per output_state) + (2 byte 0x0000 no timestamp), or
118  // b) msg_parameter_length := (6 byte version+system) + number_of_output_states * (5 byte state+counter per output_state) + (2 byte 0x0001 timestamp available) + (11 byte timestamp)
119  bool parameter_format_ok = false;
120  // Check parameter format a (default, TiM781S: no timestamp)
121  uint16_t timestamp_status = (receiveBuffer[receiveLength - 3] << 8) | (receiveBuffer[receiveLength - 2]); // 2 byte before receiveBuffer[receiveLength-1] = CRC
122  int number_of_output_states = (msg_parameter_length - 6 - 2) / 5; // each output state has 1 byte state enum + 4 byte counter = 5 byte
123  if(msg_parameter_length == 6 + number_of_output_states * 5 + 2 && timestamp_status == 0)
124  {
125  parameter_format_ok = true;
126  ROS_DEBUG_STREAM("SickScanMessages::parseLIDoutputstateMsg(): " << number_of_output_states << " output states, no timestamp" );
127  }
128  else // Check parameter format b with timestamp
129  {
130  timestamp_status = (receiveBuffer[receiveLength - 14] << 8) | (receiveBuffer[receiveLength - 13]); // 2 byte before receiveBuffer[receiveLength-12] = (11 byte timestamp) + (1 byte CRC)
131  number_of_output_states = (msg_parameter_length - 6 - 2 - 11) / 5; // each output state has 1 byte state enum + 4 byte counter = 5 byte
132  if(msg_parameter_length == 6 + number_of_output_states * 5 + 2 + 11 && timestamp_status > 0)
133  {
134  parameter_format_ok = true;
135  ROS_DEBUG_STREAM("SickScanMessages::parseLIDoutputstateMsg(): " << number_of_output_states << " output states and timestamp" );
136  }
137  }
138  if(!parameter_format_ok)
139  {
140  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLIDoutputstateMsg(): received " << receiveLength << " byte with invalid format (" << __FILE__ << ":" << __LINE__ << "): "
141  << DataDumper::binDataToAsciiString(receiveBuffer, receiveLength));
142  return false;
143  }
144  // Read 2 byte version + 4 byte system counter
145  receiveBuffer += msg_start_idx;
146  receiveLength -= msg_start_idx;
147  if( !readBinaryBuffer(receiveBuffer, receiveLength, output_msg.version_number)
148  || !readBinaryBuffer(receiveBuffer, receiveLength, output_msg.system_counter))
149  {
150  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLIDoutputstateMsg(): error parsing version_number and system_counter (" << __FILE__ << ":" << __LINE__ << ")");
151  return false;
152  }
153  // Read N output states
154  output_msg.output_state.reserve(number_of_output_states);
155  output_msg.output_count.reserve(number_of_output_states);
156  for(int state_cnt = 0; state_cnt < number_of_output_states; state_cnt++)
157  {
158  uint8_t output_state;
159  uint32_t output_count;
160  if( !readBinaryBuffer(receiveBuffer, receiveLength, output_state)
161  || !readBinaryBuffer(receiveBuffer, receiveLength, output_count))
162  {
163  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLIDoutputstateMsg(): error parsing version_number and system_counter (" << __FILE__ << ":" << __LINE__ << ")");
164  return false;
165  }
166  if(output_state == 0 || output_state == 1) // 0: not active, 1: active, 2: not used
167  {
168  output_msg.output_state.push_back(output_state);
169  output_msg.output_count.push_back(output_count);
170  }
171  }
172  // Read timestamp state
173  if( !readBinaryBuffer(receiveBuffer, receiveLength, output_msg.time_state))
174  {
175  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLIDoutputstateMsg(): error parsing time_state (" << __FILE__ << ":" << __LINE__ << ")");
176  return false;
177  }
178  if(output_msg.time_state != timestamp_status) // time_state and previously parsed timestamp_status must be identical
179  {
180  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLIDoutputstateMsg(): time_state mismatch, received " << (int)output_msg.time_state << ", expected " << (int)timestamp_status << " (" << __FILE__ << ":" << __LINE__ << ")");
181  return false;
182 
183  }
184  // Read optional timestamp
185  if(timestamp_status > 0)
186  {
187  if(!readBinaryBuffer(receiveBuffer, receiveLength, output_msg.year)
188  || !readBinaryBuffer(receiveBuffer, receiveLength, output_msg.month)
189  || !readBinaryBuffer(receiveBuffer, receiveLength, output_msg.day)
190  || !readBinaryBuffer(receiveBuffer, receiveLength, output_msg.hour)
191  || !readBinaryBuffer(receiveBuffer, receiveLength, output_msg.minute)
192  || !readBinaryBuffer(receiveBuffer, receiveLength, output_msg.second)
193  || !readBinaryBuffer(receiveBuffer, receiveLength, output_msg.microsecond))
194  {
195  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLIDoutputstateMsg(): error parsing timestamp (" << __FILE__ << ":" << __LINE__ << ")");
196  return false;
197  }
198  }
199  output_msg.header.stamp = timeStamp;
200  ROS_HEADER_SEQ(output_msg.header, 0);
201  output_msg.header.frame_id = frame_id;
202 
203  // Debug messages
204  std::stringstream state_str;
205  assert(output_msg.output_state.size() == output_msg.output_state.size());
206  state_str << number_of_output_states << " output states received" << (timestamp_status?" with":", no") << " timestamp\n";
207  for(int state_cnt = 0; state_cnt < output_msg.output_count.size(); state_cnt++)
208  state_str << "state[" << state_cnt << "]: " << (uint32_t)output_msg.output_state[state_cnt] << ", count=" << (uint32_t)output_msg.output_count[state_cnt] << "\n";
209  ROS_DEBUG_STREAM("SickScanMessages::parseLIDoutputstateMsg():\n"
210  << dbg_telegram_length << " byte telegram: " << dbg_telegram_hex_copy << "\n"
211  << "version_number: " << (uint32_t)output_msg.version_number << ", system_counter: " << (uint32_t)output_msg.system_counter << "\n"
212  << state_str.str()
213  << "time state: " << (uint32_t)output_msg.time_state
214  << ", date: " << std::setfill('0') << std::setw(4) << (uint32_t)output_msg.year << "-" << std::setfill('0') << std::setw(2) << (uint32_t)output_msg.month << "-" << std::setfill('0') << std::setw(2) << (uint32_t)output_msg.day
215  << ", time: " << std::setfill('0') << std::setw(2) << (uint32_t)output_msg.hour << ":" << std::setfill('0') << std::setw(2) << (uint32_t)output_msg.minute << ":" << std::setfill('0') << std::setw(2) << (uint32_t)output_msg.second << "." << std::setfill('0') << std::setw(6) << (uint32_t)output_msg.microsecond);
216 
217  return true;
218  }
219  else // i.e. RMS-2xxx
220  {
221  // ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLIDoutputstateMsg not implemented for Cola-A messages(" << __FILE__ << ":" << __LINE__ << ")");
222  int msg_offset = 20; // Cola-A: 1 byte STX + 19 byte "sSN LIDoutputstate " = 20 byte
223  char* msg_ptr = (char*)(msg_offset < receiveLength ? (receiveBuffer + msg_offset) : 0);
224  // Parse version_number and system_counter
225  uint32_t version_number = 0, system_counter = 0;
226  msg_ptr = readNextAsciiHexValue(msg_ptr, &version_number);
227  msg_ptr = readNextAsciiHexValue(msg_ptr, &system_counter);
228  output_msg.version_number = (uint16_t)(version_number & 0xFFFF);
229  output_msg.system_counter = system_counter;
230  for(int state_cnt = 0; state_cnt < 7 && msg_ptr != 0; state_cnt++) // Read state and count of Out1 to Out7
231  {
232  uint32_t output_state = 2, output_count = 0; // output_state 2: not used
233  msg_ptr = readNextAsciiHexValue(msg_ptr, &output_state);
234  msg_ptr = readNextAsciiHexValue(msg_ptr, &output_count);
235  if (msg_ptr && (output_state == 0 || output_state == 1)) // 0: not active, 1: active, 2: not used
236  {
237  output_msg.output_state.push_back((uint8_t)(output_state & 0xFF));
238  output_msg.output_count.push_back(output_count);
239  }
240  }
241  for(int state_cnt = 0; state_cnt < 8; state_cnt++) // Read state and count of Ext.Out1 to Ext.Out8 (not supported?)
242  {
243  }
244  // Read optional date and time
245  uint32_t time_state = 0, year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, microsecond = 0;
246  msg_ptr = readNextAsciiHexValue(msg_ptr, &time_state);
247  if (msg_ptr && time_state > 0)
248  {
249  msg_ptr = readNextAsciiHexValue(msg_ptr, &year);
250  msg_ptr = readNextAsciiHexValue(msg_ptr, &month);
251  msg_ptr = readNextAsciiHexValue(msg_ptr, &day);
252  msg_ptr = readNextAsciiHexValue(msg_ptr, &hour);
253  msg_ptr = readNextAsciiHexValue(msg_ptr, &minute);
254  msg_ptr = readNextAsciiHexValue(msg_ptr, &second);
255  msg_ptr = readNextAsciiHexValue(msg_ptr, &microsecond);
256  }
257  output_msg.time_state = (uint16_t)time_state;
258  output_msg.year = (uint16_t)year;
259  output_msg.month = (uint8_t)month;
260  output_msg.day = (uint8_t)day;
261  output_msg.hour = (uint8_t)hour;
262  output_msg.minute = (uint8_t)minute;
263  output_msg.second = (uint8_t)second;
264  output_msg.microsecond = (uint32_t)microsecond;
265  output_msg.header.stamp = timeStamp;
266  ROS_HEADER_SEQ(output_msg.header, 0);
267  output_msg.header.frame_id = frame_id;
268  // Debug messages
269  std::stringstream state_str;
270  for(int state_cnt = 0; state_cnt < output_msg.output_count.size(); state_cnt++)
271  state_str << "state[" << state_cnt << "]: " << (uint32_t)output_msg.output_state[state_cnt] << ", count=" << (uint32_t)output_msg.output_count[state_cnt] << "\n";
272  ROS_DEBUG_STREAM("SickScanMessages::parseLIDoutputstateMsg():\n"
273  << receiveLength << " byte telegram: " << std::string((char*)receiveBuffer + 1, receiveLength - 2) << "\n"
274  << "version_number: " << (uint32_t)output_msg.version_number << ", system_counter: " << (uint32_t)output_msg.system_counter << "\n"
275  << state_str.str()
276  << "time state: " << (uint32_t)output_msg.time_state
277  << ", date: " << std::setfill('0') << std::setw(4) << (uint32_t)output_msg.year << "-" << std::setfill('0') << std::setw(2) << (uint32_t)output_msg.month << "-" << std::setfill('0') << std::setw(2) << (uint32_t)output_msg.day
278  << ", time: " << std::setfill('0') << std::setw(2) << (uint32_t)output_msg.hour << ":" << std::setfill('0') << std::setw(2) << (uint32_t)output_msg.minute << ":" << std::setfill('0') << std::setw(2) << (uint32_t)output_msg.second << "." << std::setfill('0') << std::setw(6) << (uint32_t)output_msg.microsecond);
279  return true;
280  }
281  return false;
282 }
283 
284 /*
285  * @brief parses and converts a lidar LFErec message to a ros LFErec message
286  *
287  * Example LIDoutputstate message from 20210111_sick_tim781s_lferec_elephant.pcapng.json:
288  * "tcp.description": "........sSN LFErec ........................0..............3...........................0..............4....P......................0..............35.....",
289  * "tcp.payload": "02:02:02:02:00:00:00:8e:73:53:4e:20:4c:46:45:72:65:63:20:00:03:00:01:01:00:00:00:00:3f:80:00:00:00:00:00:00:00:00:0d:05:ff:f9:22:30:00:00:00:00:00:00:00:00:01:07:b2:01:01:08:33:2e:00:09:ac:90:00:01:02:00:00:00:00:3f:80:00:00:00:00:00:00:00:00:0d:05:ff:f9:22:30:01:00:00:00:00:00:00:00:01:07:b2:01:01:08:34:02:00:06:9f:50:00:01:03:00:00:00:00:3f:80:00:00:00:00:00:00:00:00:0d:05:ff:f9:22:30:01:00:00:00:00:00:00:00:01:07:b2:01:01:08:33:35:00:09:ac:90:ac"
290  * 131 byte LFErec payload after "sSN LFErec ": "00:03:00:01:01:00:00:00:00:3f:80:00:00:00:00:00:00:00:00:0d:05:ff:f9:22:30:00:00:00:00:00:00:00:00:01:07:b2:01:01:08:33:2e:00:09:ac:90:00:01:02:00:00:00:00:3f:80:00:00:00:00:00:00:00:00:0d:05:ff:f9:22:30:01:00:00:00:00:00:00:00:01:07:b2:01:01:08:34:02:00:06:9f:50:00:01:03:00:00:00:00:3f:80:00:00:00:00:00:00:00:00:0d:05:ff:f9:22:30:01:00:00:00:00:00:00:00:01:07:b2:01:01:08:33:35:00:09:ac:90"
291  * LFErec payload starts with 16 byte fields_number "00:03" -> 3 fields, each field has (131-2)/3 = 43 byte
292  * Each field contains a field_index (1,2,3) and field_result (0: invalid/incorrect, 1: free/clear, 2: infringed)
293  * Note: field indices are sorted in reverse order, i.e. with 2 fields configured, we have typically
294  * output_msg.fields[0].field_index = 1, output_msg.fields[0].field_result_mrs = 0 (invalid)
295  * output_msg.fields[1].field_index = 2, output_msg.fields[1].field_result_mrs = 1 or 2 (clear=1 or infringed=2)
296  * output_msg.fields[2].field_index = 3, output_msg.fields[2].field_result_mrs = 1 or 2 (clear=1 or infringed=2)
297  *
298  * @param[in] timeStamp timestamp on receiving the lidar message
299  * @param[in] receiveBuffer byte array of lidar message
300  * @param[in] receiveLength size of lidar message in byte
301  * @param[in] useBinaryProtocol binary lidar message (true, Cola-B) or ascii lidar message (false, Cola-A)
302  * @param[in] eval_field_logic USE_EVAL_FIELD_LMS5XX_LOGIC or USE_EVAL_FIELD_TIM7XX_LOGIC
303  * @param[in] frame_id frame id of output message
304  * @param[out] output_msg converted output message
305  *
306  * @return true on success, false on error
307  */
308 bool sick_scan_xd::SickScanMessages::parseLFErecMsg(const rosTime& timeStamp, uint8_t* receiveBuffer, int receiveLength, bool useBinaryProtocol, EVAL_FIELD_SUPPORT eval_field_logic, const std::string& frame_id, sick_scan_msg::LFErecMsg& output_msg)
309 {
310  if(useBinaryProtocol)
311  {
312  uint8_t* msg_receiveBuffer = receiveBuffer;
313  int msg_receiveLength = receiveLength;
314 
315  // parse and convert LFErec messages, see https://github.com/SICKAG/libsick_ldmrs/blob/master/src/sopas/LdmrsSopasLayer.cpp lines 1414 ff.
316  int msg_start_idx = 19; // 4 byte STX + 4 byte payload_length + 11 byte "sSN LFErec " = 19 byte
317  receiveBuffer += msg_start_idx;
318  receiveLength -= msg_start_idx;
319 
320  output_msg.fields_number = 0;
321  if(!readBinaryBuffer(receiveBuffer, receiveLength, output_msg.fields_number) || output_msg.fields_number <= 0)
322  {
323  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): parse error, fields number = " << output_msg.fields_number<< " should be greater than 0 (" << __FILE__ << ":" << __LINE__ << ")");
324  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): " << msg_receiveLength << " byte received: " << DataDumper::binDataToAsciiString(msg_receiveBuffer, msg_receiveLength));
325  return false;
326  }
327  output_msg.fields.reserve(output_msg.fields_number);
328  for(int field_idx = 0; field_idx < output_msg.fields_number; field_idx++)
329  {
331  if( !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.version_number)
332  || !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.field_index)
333  || !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.sys_count)
334  || !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.dist_scale_factor)
335  || !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.dist_scale_offset)
336  || !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.angle_scale_factor)
337  || !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.angle_scale_offset)
338  || !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.field_result_mrs))
339  {
340  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): parse error field " << field_idx << " (" << __FILE__ << ":" << __LINE__ << ")");
341  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): " << msg_receiveLength << " byte received: " << DataDumper::binDataToAsciiString(msg_receiveBuffer, msg_receiveLength));
342  return false;
343  }
344  uint16_t dummy_byte[3] = {0};
345  if(!readBinaryBuffer(receiveBuffer, receiveLength, dummy_byte[0])
346  || !readBinaryBuffer(receiveBuffer, receiveLength, dummy_byte[1])
347  || !readBinaryBuffer(receiveBuffer, receiveLength, dummy_byte[2]))
348  {
349  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): parse error field " << field_idx << " (" << __FILE__ << ":" << __LINE__ << ")");
350  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): " << msg_receiveLength << " byte received: " << DataDumper::binDataToAsciiString(msg_receiveBuffer, msg_receiveLength));
351  return false;
352  }
353  int additional_dummy_byte_length = 0;
354  // if(eval_field_logic == USE_EVAL_FIELD_LMS5XX_LOGIC && field_msg.field_result_mrs == 2)
355  // additional_dummy_byte_length = (21 - 6);
356  if(eval_field_logic == USE_EVAL_FIELD_LMS5XX_LOGIC && (dummy_byte[0] != 0 || dummy_byte[1] != 0 || dummy_byte[2] != 0))
357  additional_dummy_byte_length = (21 - 6);
358  receiveBuffer += additional_dummy_byte_length;
359  receiveLength -= additional_dummy_byte_length;
360  if(receiveLength < 0)
361  {
362  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): parse error field " << field_idx << " (" << __FILE__ << ":" << __LINE__ << ")");
363  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): " << msg_receiveLength << " byte received: " << DataDumper::binDataToAsciiString(msg_receiveBuffer, msg_receiveLength));
364  return false;
365  }
366  if(!readBinaryBuffer(receiveBuffer, receiveLength, field_msg.time_state))
367  {
368  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): parse error field " << field_idx << " (" << __FILE__ << ":" << __LINE__ << ")");
369  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): " << msg_receiveLength << " byte received: " << DataDumper::binDataToAsciiString(msg_receiveBuffer, msg_receiveLength));
370  return false;
371  }
372  if(field_msg.time_state)
373  {
374  if( !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.year)
375  || !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.month)
376  || !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.day)
377  || !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.hour)
378  || !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.minute)
379  || !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.second)
380  || !readBinaryBuffer(receiveBuffer, receiveLength, field_msg.microsecond))
381  {
382  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): parse error field " << field_idx << " (" << __FILE__ << ":" << __LINE__ << ")");
383  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): " << msg_receiveLength << " byte received: " << DataDumper::binDataToAsciiString(msg_receiveBuffer, msg_receiveLength));
384  return false;
385  }
386  }
387  else
388  {
389  field_msg.year = 0;
390  field_msg.month = 0;
391  field_msg.day = 0;
392  field_msg.hour = 0;
393  field_msg.minute = 0;
394  field_msg.second = 0;
395  field_msg.microsecond = 0;
396  }
397  output_msg.fields.push_back(field_msg);
398  }
399  if(output_msg.fields.size() != output_msg.fields_number)
400  {
401  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): parse error, fields number = " << output_msg.fields_number << ", but " << output_msg.fields.size() << " fields parsed (" << __FILE__ << ":" << __LINE__ << ")");
402  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): " << msg_receiveLength << " byte received: " << DataDumper::binDataToAsciiString(msg_receiveBuffer, msg_receiveLength));
403  return false;
404  }
405  if(receiveLength != 1) // 1 byte CRC still in receiveBuffer
406  {
407  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): parse error, " << receiveLength << " bytes in buffer after decoding all fields, should be 0 (" << __FILE__ << ":" << __LINE__ << ")");
408  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg(): " << msg_receiveLength << " byte received: " << DataDumper::binDataToAsciiString(msg_receiveBuffer, msg_receiveLength));
409  return false;
410  }
411 
412  output_msg.header.stamp = timeStamp;
413  ROS_HEADER_SEQ(output_msg.header, 0);
414  output_msg.header.frame_id = frame_id;
415 
416  std::stringstream fields_str;
417  for(int field_idx = 0; field_idx < output_msg.fields.size(); field_idx++)
418  {
419  sick_scan_msg::LFErecFieldMsg& field_msg = output_msg.fields[field_idx];
420  if(field_idx > 0)
421  fields_str << "\n";
422  fields_str << "field[" << field_idx << "]: idx=" << (uint32_t)field_msg.field_index
423  << ", dist_scale_factor=" << field_msg.dist_scale_factor
424  << ", dist_scale_offset=" << field_msg.dist_scale_offset
425  << ", angle_scale_factor=" << field_msg.angle_scale_factor
426  << ", angle_scale_offset=" << field_msg.angle_scale_offset
427  << ", field_result_mrs=" << (uint32_t)field_msg.field_result_mrs
428  << ", time state: " << (uint32_t)field_msg.time_state
429  << ", date: " << std::setfill('0') << std::setw(4) << (uint32_t)field_msg.year << "-" << std::setfill('0') << std::setw(2) << (uint32_t)field_msg.month << "-" << std::setfill('0') << std::setw(2) << (uint32_t)field_msg.day
430  << ", time: " << std::setfill('0') << std::setw(2) << (uint32_t)field_msg.hour << ":" << std::setfill('0') << std::setw(2) << (uint32_t)field_msg.minute << ":" << std::setfill('0') << std::setw(2) << (uint32_t)field_msg.second
431  << "." << std::setfill('0') << std::setw(6) << (uint32_t)field_msg.microsecond;
432  }
433  ROS_DEBUG_STREAM("SickScanMessages::parseLFErecMsg(): LFErec with " << output_msg.fields.size() << " fields:\n" << fields_str.str());
434 
435  return true;
436  }
437  else
438  {
439  ROS_ERROR_STREAM("## ERROR SickScanMessages::parseLFErecMsg not implemented for Cola-A messages (" << __FILE__ << ":" << __LINE__ << ")");
440  }
441  return false;
442 }
443 
444 /*
445  * @brief returns the sopas command keyword, e.g.: getSopasCmdKeyword("\x02\x02\x02\x02\x00\x00\x00\x8esSN LFErec \x3", 20) returns "LFErec".
446  */
447 std::string sick_scan_xd::SickScanMessages::getSopasCmdKeyword(const uint8_t* sopasRequest, int requestLength)
448 {
449  const uint32_t binary_stx = 0x02020202;
450  bool requestIsBinary = (requestLength >= sizeof(binary_stx) && memcmp(sopasRequest, &binary_stx, sizeof(binary_stx)) == 0); // Cola-B always starts with 0x02020202
451 
452  int keyword_start = 0, keyword_end = 0;
453  if(requestIsBinary && requestLength > 12) // 0x02020202 + { 4 byte payload length } + { 4 byte command id incl. space }
454  keyword_start = 12;
455  else if(!requestIsBinary && requestLength > 5)
456  keyword_start = 5; // 0x02 + { 4 byte command id incl. space }
457  else
458  return ""; // no keyword found
459  keyword_end = keyword_start;
460  while(keyword_end < requestLength-1 && sopasRequest[keyword_end] != 0x03 && !isspace(sopasRequest[keyword_end])) // count until <ETX> or " "
461  keyword_end++;
462  std::string keyword((const char*)&sopasRequest[keyword_start], keyword_end - keyword_start);
463  // ROS_DEBUG_STREAM("SickScanMessages::getSopasCmdKeyword(): keyword=\"" << DataDumper::binDataToAsciiString(&sopasRequest[keyword_start], keyword_end - keyword_start) << "\"=\"" << keyword << "\"");
464  return keyword;
465 }
sick_scan_messages.h
sick_scan_xd::SickScanMessages::SickScanMessages
SickScanMessages(rosNodePtr nh=0)
Definition: sick_scan_messages.cpp:64
DataDumper::binDataToAsciiString
static std::string binDataToAsciiString(const uint8_t *binary_data, int length)
Definition: dataDumper.cpp:108
sick_scan_xd::LFErecMsg
::sick_scan_xd::LFErecMsg_< std::allocator< void > > LFErecMsg
Definition: LFErecMsg.h:61
sick_scan_xd::readBinaryBuffer
bool readBinaryBuffer(uint8_ptr &buffer, int &bufferlen, T &value)
Definition: sick_scan_messages.h:73
ROS_HEADER_SEQ
#define ROS_HEADER_SEQ(msgheader, value)
Definition: sick_ros_wrapper.h:162
sick_scan_xd::SickScanMessages::getSopasCmdKeyword
static std::string getSopasCmdKeyword(const uint8_t *sopasRequest, int requestLength)
Definition: sick_scan_messages.cpp:447
sick_scan_xd::EVAL_FIELD_SUPPORT
EVAL_FIELD_SUPPORT
Definition: sick_generic_parser.h:93
sick_scan_xd::LIDoutputstateMsg
::sick_scan_xd::LIDoutputstateMsg_< std::allocator< void > > LIDoutputstateMsg
Definition: LIDoutputstateMsg.h:110
ROS_DEBUG_STREAM
#define ROS_DEBUG_STREAM(args)
Definition: sick_scan_logging.h:113
ros::NodeHandle
sick_scan_xd::LFErecFieldMsg
::sick_scan_xd::LFErecFieldMsg_< std::allocator< void > > LFErecFieldMsg
Definition: LFErecFieldMsg.h:124
sick_scan_xd::USE_EVAL_FIELD_LMS5XX_LOGIC
@ USE_EVAL_FIELD_LMS5XX_LOGIC
Definition: sick_generic_parser.h:97
readNextAsciiHexValue
static char * readNextAsciiHexValue(char *msg_ptr, uint32_t *value)
Definition: sick_scan_messages.cpp:72
sick_scan_xd::SickScanMessages::parseLFErecMsg
static bool parseLFErecMsg(const rosTime &timeStamp, uint8_t *receiveBuffer, int receiveLength, bool useBinaryProtocol, EVAL_FIELD_SUPPORT eval_field_logic, const std::string &frame_id, sick_scan_msg::LFErecMsg &output_msg)
Definition: sick_scan_messages.cpp:308
ros::Time
sick_scan_xd::SickScanMessages::~SickScanMessages
virtual ~SickScanMessages()
Definition: sick_scan_messages.cpp:68
ROS_ERROR_STREAM
#define ROS_ERROR_STREAM(...)
Definition: sick_scan_ros2_example.cpp:72
sick_scan_timing_dump_analyser.keyword
keyword
Definition: sick_scan_timing_dump_analyser.py:18
roswrap::message_traits::timeStamp
ros::Time * timeStamp(M &m)
returns TimeStamp<M>::pointer(m);
Definition: message_traits.h:317
sick_scan_xd::SickScanMessages::parseLIDoutputstateMsg
static bool parseLIDoutputstateMsg(const rosTime &timeStamp, uint8_t *receiveBuffer, int receiveLength, bool useBinaryProtocol, const std::string &frame_id, sick_scan_msg::LIDoutputstateMsg &output_msg)
Definition: sick_scan_messages.cpp:102


sick_scan_xd
Author(s): Michael Lehning , Jochen Sprickerhof , Martin Günther
autogenerated on Fri Oct 25 2024 02:47:11