cola_transmitter.cpp
Go to the documentation of this file.
1 /*
2  * @brief sim_loc_cola_transmitter connects to the localization controller, sends cola command requests,
3  * and waits for the response with timeout.
4  *
5  * Copyright (C) 2019 Ing.-Buero Dr. Michael Lehning, Hildesheim
6  * Copyright (C) 2019 SICK AG, Waldkirch
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  * All rights reserved.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions are met:
24  *
25  * * Redistributions of source code must retain the above copyright
26  * notice, this list of conditions and the following disclaimer.
27  * * Redistributions in binary form must reproduce the above copyright
28  * notice, this list of conditions and the following disclaimer in the
29  * documentation and/or other materials provided with the distribution.
30  * * Neither the name of SICK AG nor the names of its
31  * contributors may be used to endorse or promote products derived from
32  * this software without specific prior written permission
33  * * Neither the name of Ing.-Buero Dr. Michael Lehning nor the names of its
34  * contributors may be used to endorse or promote products derived from
35  * this software without specific prior written permission
36  *
37  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
38  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
41  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
43  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
44  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
45  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
46  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
47  * POSSIBILITY OF SUCH DAMAGE.
48  *
49  * Authors:
50  * Michael Lehning <michael.lehning@lehning.de>
51  *
52  * Copyright 2019 SICK AG
53  * Copyright 2019 Ing.-Buero Dr. Michael Lehning
54  *
55  */
56 
57 #include <thread>
58 #include <mutex>
59 
60 #include "sick_scan/ros_wrapper.h"
61 
62 #include "sick_scan/cola_parser.h"
64 #include "sick_scan/utils.h"
65 
66 static std::mutex s_ColaTransmitterSendMutex; // mutex to lock ColaTransmitter::send
67 
74 sick_scan_xd::ColaTransmitter::ColaTransmitter(const std::string & server_address, int tcp_port, double default_receive_timeout)
75 : m_server_address(server_address), m_tcp_port(tcp_port),
76  m_receive_timeout(default_receive_timeout), m_receiver_thread_running(false), m_receiver_thread(0)
77 {
78 
79 }
80 
85 {
86  m_receiver_thread_running = false;
87  close();
88  stopReceiverThread();
89 }
90 
96 {
97  return m_tcp_socket.connect(m_server_address, m_tcp_port);
98 }
99 
105 {
106  try
107  {
108  m_tcp_socket.close();
109  return true;
110  }
111  catch(std::exception & exc)
112  {
113  ROS_WARN_STREAM("ColaTransmitter::closeTcpConnections(): exception " << exc.what() << " on closing connection.");
114  }
115  return false;
116 }
117 
124 bool sick_scan_xd::ColaTransmitter::send(const std::vector<uint8_t> & data, ROS::Time & send_timestamp)
125 {
126  return send(m_tcp_socket.socket(), data, send_timestamp);
127 }
128 
136 bool sick_scan_xd::ColaTransmitter::send(socket_t & socket, const std::vector<uint8_t> & data, ROS::Time & send_timestamp)
137 {
138  std::lock_guard<std::mutex> send_lock_guard(s_ColaTransmitterSendMutex);
139  try
140  {
141  if (ROS::ok() && socket != INVALID_SOCKET)
142  {
144  size_t bytes_written = ::send(socket, (char*)data.data(), data.size(), 0);
145  if (bytes_written == data.size())
146  return true;
147  ROS_WARN_STREAM("## ERROR ColaTransmitter::send: tcp socket write error, " << bytes_written << " of " << data.size() << " bytes written");
148  }
149  }
150  catch(std::exception & exc)
151  {
152  ROS_WARN_STREAM("## ERROR ColaTransmitter::send(): exception " << exc.what());
153  }
154  return false;
155 }
156 
164 bool sick_scan_xd::ColaTransmitter::receive(std::vector<uint8_t> & telegram, double timeout, ROS::Time & receive_timestamp)
165 {
166  return receive(m_tcp_socket.socket(), telegram, timeout, receive_timestamp);
167 }
168 
177 bool sick_scan_xd::ColaTransmitter::receive(socket_t & socket, std::vector<uint8_t> & telegram, double timeout, ROS::Time & receive_timestamp)
178 {
179  telegram.clear();
180  telegram.reserve(1024);
181  try
182  {
183  std::vector<uint8_t> binETX = sick_scan_xd::ColaParser::binaryETX();
184  ROS::Time start_time = ROS::now();
185  while (ROS::ok() && socket != INVALID_SOCKET)
186  {
187  // Read 1 byte
188  uint8_t byte_received = 0;
189  // set socket to nonblocking mode
190  int recv_flags = 0;
191 # ifdef _MSC_VER
192  u_long recv_mode = 1; // FIONBIO enables or disables the blocking mode for the socket. If iMode = 0, blocking is enabled, if iMode != 0, non-blocking mode is enabled.
193  ioctlsocket(socket, FIONBIO, &recv_mode);
194 # else
195  recv_flags |= MSG_DONTWAIT;
196 # endif
197 
198  // Possibly better than receiving byte for byte: use boost::asio::read_until to read until <ETX> received
199  if (::recv(socket, (char*)&byte_received, 1, recv_flags) == 1)
200  {
201  if (telegram.empty())
202  receive_timestamp = ROS::now(); // timestamp after first byte received
203  telegram.push_back(byte_received);
204  // Check for "<ETX>" (message completed) and return if received data ends with "<ETX>"
205  bool is_binary_cola = sick_scan_xd::ColaAsciiBinaryConverter::IsColaBinary(telegram);
206  if(is_binary_cola)
207  {
208  // Cola-Binary: Check telegram length and return if all bytes received
209  uint32_t telegram_length = sick_scan_xd::ColaAsciiBinaryConverter::ColaBinaryTelegramLength(telegram);
210  if(telegram_length > 0 && telegram.size() >= telegram_length)
211  return true; // all bytes received, telegram completed
212  }
213  else
214  {
215  // Cola-ASCII: Check for "<ETX>" (message completed) and return if received data ends with "<ETX>"
216  if (dataEndWithETX(telegram, binETX))
217  return true; // <ETX> received, telegram completed
218  }
219  }
220  else
221  ROS::sleep(0.0001);
222  // Check for timeout
223  if (ROS::seconds(ROS::now() - start_time) >= timeout)
224  {
225  // ROS_DEBUG_STREAM("ColaTransmitter::receive(): timeout, " << telegram.size() << " byte received: " << sick_scan_xd::Utils::toHexString(telegram));
226  break;
227  }
228  }
229  }
230  catch(std::exception & exc)
231  {
232  ROS_WARN_STREAM("## ERROR ColaTransmitter::receive(): exception " << exc.what());
233  }
234  return false; // no tcp connection or timeout
235 }
236 
243 bool sick_scan_xd::ColaTransmitter::dataEndWithETX(const std::vector<uint8_t> & data, const std::vector<uint8_t> & etx)
244 {
245  if(data.size() < etx.size())
246  return false;
247  for(size_t n = data.size() - etx.size(), m = 0; m < etx.size(); n++, m++)
248  {
249  if(data[n] != etx[m])
250  return false;
251  }
252  return true;
253 }
254 
261 {
262  stopReceiverThread();
263  m_receiver_thread_running = true;
264  m_receiver_thread = new std::thread(&sick_scan_xd::ColaTransmitter::runReceiverThreadCb, this);
265  return true;
266 }
267 
273 {
274  m_receiver_thread_running = false;
275  if(m_receiver_thread)
276  {
277  m_receiver_thread->join();
278  delete(m_receiver_thread);
279  m_receiver_thread = 0;
280  }
281  return true;
282 }
283 
294 bool sick_scan_xd::ColaTransmitter::waitPopResponse(std::vector<uint8_t> & telegram, double timeout, ROS::Time & receive_timestamp)
295 {
296  ROS::Time start_time = ROS::now();
297  while(ROS::ok() && m_receiver_thread_running && m_response_fifo.empty())
298  {
299  ROS::sleep(0.0001);
300  m_response_fifo.waitOnceForElement();
301  if(ROS::seconds(ROS::now() - start_time) >= timeout)
302  break;
303  }
304  if(!m_response_fifo.empty())
305  {
306  ColaResponseContainer response = m_response_fifo.pop();
307  telegram = response.telegram_data;
308  receive_timestamp = response.receive_timestamp;
309  return true;
310  }
311  return false; // timeout
312 }
313 
318 {
319  while(ROS::ok() && m_receiver_thread_running)
320  {
322  if(receive(response.telegram_data, m_receive_timeout, response.receive_timestamp))
323  m_response_fifo.push(response);
324  else
325  m_response_fifo.notify();
326  }
327 }
response
const std::string response
sick_scan_xd::ColaParser::binaryETX
static std::vector< uint8_t > binaryETX(void)
Returns the binary "end of text" tag in a Cola-Binary command, i.e. {0x03}.
Definition: cola_parser.h:177
ROS::sleep
void sleep(double seconds)
Definition: ros_wrapper.cpp:110
socket_t
int32_t socket_t
Definition: server_socket.h:70
utils.h
sick_scan_xd::ColaTransmitter::runReceiverThreadCb
void runReceiverThreadCb(void)
Definition: cola_transmitter.cpp:317
data
data
sick_scan_xd::ColaAsciiBinaryConverter::IsColaBinary
static bool IsColaBinary(const std::vector< uint8_t > &cola_telegram)
Returns true for Cola-Binary, if a given telegram is Cola-Binary encoded and starts with 4 Bytes { 0x...
Definition: cola_converter.cpp:342
ROS::now
ROS::Time now(void)
Definition: ros_wrapper.cpp:116
ROS_WARN_STREAM
#define ROS_WARN_STREAM(args)
Definition: sick_scan_logging.h:123
sick_scan_xd::ColaTransmitter::connect
virtual bool connect(void)
Definition: cola_transmitter.cpp:95
sick_scan_xd::ColaTransmitter::ColaTransmitter
ColaTransmitter(const std::string &server_address="192.168.0.1", int tcp_port=2111, double default_receive_timeout=1)
Definition: cola_transmitter.cpp:74
sick_scan_xd::ColaTransmitter::dataEndWithETX
static bool dataEndWithETX(const std::vector< uint8_t > &data, const std::vector< uint8_t > &etx)
Definition: cola_transmitter.cpp:243
test_server.tcp_port
int tcp_port
Definition: test_server.py:204
multiscan_pcap_player.send_timestamp
int send_timestamp
Definition: multiscan_pcap_player.py:201
std_msgs::Time
::std_msgs::Time_< std::allocator< void > > Time
Definition: Time.h:48
sick_scan_xd::ColaTransmitter::stopReceiverThread
virtual bool stopReceiverThread(void)
Definition: cola_transmitter.cpp:272
sick_scan_xd::ColaTransmitter::send
virtual bool send(const std::vector< uint8_t > &data, ROS::Time &send_timestamp)
Definition: cola_transmitter.cpp:124
sick_scan_xd::ColaTransmitter::~ColaTransmitter
virtual ~ColaTransmitter()
Definition: cola_transmitter.cpp:84
sick_scan_xd::ColaTransmitter::waitPopResponse
virtual bool waitPopResponse(std::vector< uint8_t > &telegram, double timeout, ROS::Time &receive_timestamp)
Definition: cola_transmitter.cpp:294
ROS::seconds
double seconds(ROS::Duration duration)
Definition: ros_wrapper.cpp:180
sick_generic_device_finder.timeout
timeout
Definition: sick_generic_device_finder.py:113
sick_scan_xd::ColaAsciiBinaryConverter::ColaBinaryTelegramLength
static uint32_t ColaBinaryTelegramLength(const std::vector< uint8_t > &cola_telegram)
Decodes the header and returns the length of a Cola-Binary telegram.
Definition: cola_converter.cpp:354
cola_transmitter.h
s_ColaTransmitterSendMutex
static std::mutex s_ColaTransmitterSendMutex
Definition: cola_transmitter.cpp:66
sick_scan_xd::ColaTransmitter::receive
virtual bool receive(std::vector< uint8_t > &telegram, double timeout, ROS::Time &receive_timestamp)
Definition: cola_transmitter.cpp:164
receive
static bool receive(sick_scan_xd::ServerSocket &tcp_client_socket, size_t nr_bytes, bool little_endian, std::vector< uint8_t > &value, bool read_blocking=true)
Definition: test_server_cola_msg.cpp:167
roswrap::ok
ROSCPP_DECL bool ok()
Check whether it's time to exit.
Definition: rossimu.cpp:273
ros_wrapper.h
sick_scan_xd::ColaTransmitter::startReceiverThread
virtual bool startReceiverThread(void)
Definition: cola_transmitter.cpp:260
cola_parser.h
sick_scan_xd::ColaTransmitter::ColaResponseContainer
Definition: cola_transmitter.h:166
sick_scan_xd::ColaTransmitter::close
virtual bool close(void)
Definition: cola_transmitter.cpp:104
INVALID_SOCKET
#define INVALID_SOCKET
Definition: udp_sockets.h:83


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