http_command_interface.cpp
Go to the documentation of this file.
1 // Copyright (c) 2014, Pepperl+Fuchs GmbH, Mannheim
2 // Copyright (c) 2014, Denis Dillenberger
3 // All rights reserved.
4 //
5 // Use, modification, and distribution is subject to the
6 // 3-clause BSD license ("Revised BSD License",
7 // "New BSD License", or "Modified BSD License")
8 // You should have received a copy of this license
9 // in a file named COPYING or LICENSE.
10 
12 #include <iostream>
13 #include <boost/asio.hpp>
14 #include <boost/property_tree/json_parser.hpp>
15 
16 namespace pepperl_fuchs {
17 
18 //-----------------------------------------------------------------------------
19 HttpCommandInterface::HttpCommandInterface(const std::string &http_host, int http_port)
20 {
21  http_host_ = http_host;
22  http_port_ = http_port;
24 }
25 
26 //-----------------------------------------------------------------------------
27 int HttpCommandInterface::httpGet(const std::string request_path, std::string &header, std::string &content)
28 {
29  header = "";
30  content = "";
31  using boost::asio::ip::tcp;
32  try
33  {
34  boost::asio::io_service io_service;
35 
36  // Lookup endpoint
37  tcp::resolver resolver(io_service);
38  tcp::resolver::query query(http_host_, std::to_string(http_port_));
39  tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
40  tcp::resolver::iterator end;
41 
42  // Create socket
43  tcp::socket socket(io_service);
44  boost::system::error_code error = boost::asio::error::host_not_found;
45 
46  // Iterate over endpoints and etablish connection
47  while (error && endpoint_iterator != end)
48  {
49  socket.close();
50  socket.connect(*endpoint_iterator++, error);
51  }
52  if (error)
53  throw boost::system::system_error(error);
54 
55  // Prepare request
56  boost::asio::streambuf request;
57  std::ostream request_stream(&request);
58  request_stream << "GET " << request_path << " HTTP/1.0\r\n\r\n";
59 
60  boost::asio::write(socket, request);
61 
62  // Read the response status line. The response streambuf will automatically
63  // grow to accommodate the entire line. The growth may be limited by passing
64  // a maximum size to the streambuf constructor.
65  boost::asio::streambuf response;
66  boost::asio::read_until(socket, response, "\r\n");
67 
68  // Check that response is OK.
69  std::istream response_stream(&response);
70  std::string http_version;
71  response_stream >> http_version;
72  unsigned int status_code;
73  response_stream >> status_code;
74  std::string status_message;
75  std::getline(response_stream, status_message);
76  if (!response_stream || http_version.substr(0, 5) != "HTTP/")
77  {
78  std::cout << "Invalid response\n";
79  return 0;
80  }
81 
82  // Read the response headers, which are terminated by a blank line.
83  boost::asio::read_until(socket, response, "\r\n\r\n");
84 
85  // Process the response headers.
86  std::string tmp;
87  while (std::getline(response_stream, tmp) && tmp != "\r")
88  header += tmp+"\n";
89 
90  // Write whatever content we already have to output.
91  while (std::getline(response_stream, tmp))
92  content += tmp;
93 
94  // Read until EOF, writing data to output as we go.
95  while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error))
96  {
97  response_stream.clear();
98  while (std::getline(response_stream, tmp))
99  content += tmp;
100  }
101 
102  if (error != boost::asio::error::eof)
103  throw boost::system::system_error(error);
104 
105  // Substitute CRs by a space
106  for( std::size_t i=0; i<header.size(); i++ )
107  if( header[i] == '\r' )
108  header[i] = ' ';
109 
110  for( std::size_t i=0; i<content.size(); i++ )
111  if( content[i] == '\r' )
112  content[i] = ' ';
113 
114  return status_code;
115  }
116  catch (std::exception& e)
117  {
118  std::cerr << "Exception: " << e.what() << std::endl;
119  return 0;
120  }
121 }
122 
123 //-----------------------------------------------------------------------------
124 bool HttpCommandInterface::sendHttpCommand(const std::string cmd, const std::map<std::string, std::string> param_values)
125 {
126  // Build request string
127  std::string request_str = "/cmd/" + cmd + "?";
128  for( auto& kv : param_values )
129  request_str += kv.first + "=" + kv.second + "&";
130  if(request_str.back() == '&' )
131  request_str = request_str.substr(0,request_str.size()-1);
132 
133  // Do HTTP request
134  std::string header, content;
135  http_status_code_ = httpGet(request_str,header,content);
136 
137  // Try to parse JSON response
138  try
139  {
140  std::stringstream ss(content);
141  boost::property_tree::json_parser::read_json(ss,pt_);
142  }
143  catch (std::exception& e)
144  {
145  std::cerr << "ERROR: Exception: " << e.what() << std::endl;
146  return false;
147  }
148 
149  // Check HTTP-status code
150  if( http_status_code_ != 200 )
151  return false;
152  else
153  return true;
154 }
155 
156 //-----------------------------------------------------------------------------
157 bool HttpCommandInterface::sendHttpCommand(const std::string cmd, const std::string param, const std::string value)
158 {
159  std::map<std::string, std::string> param_values;
160  if( param != "" )
161  param_values[param] = value;
162  return sendHttpCommand(cmd,param_values);
163 }
164 
165 //-----------------------------------------------------------------------------
166 bool HttpCommandInterface::setParameter(const std::string name, const std::string value)
167 {
168  return sendHttpCommand("set_parameter",name,value) && checkErrorCode();
169 }
170 
171 //-----------------------------------------------------------------------------
172 boost::optional< std::string > HttpCommandInterface::getParameter(const std::string name)
173 {
174  if( !sendHttpCommand("get_parameter","list",name) || ! checkErrorCode() )
175  return boost::optional<std::string>();
176  return pt_.get_optional<std::string>(name);
177 }
178 
179 //-----------------------------------------------------------------------------
180 std::map< std::string, std::string > HttpCommandInterface::getParameters(const std::vector<std::string> &names)
181 {
182  // Build request string
183  std::map< std::string, std::string > key_values;
184  std::string namelist;
185  for( const auto& s: names )
186  namelist += (s + ";");
187  namelist.substr(0,namelist.size()-1);
188 
189  // Read parameter values via HTTP/JSON request/response
190  if( !sendHttpCommand("get_parameter","list",namelist) || ! checkErrorCode() )
191  return key_values;
192 
193  // Extract values from JSON property_tree
194  for( const auto& s: names )
195  {
196  auto ov = pt_.get_optional<std::string>(s);
197  if( ov )
198  key_values[s] = *ov;
199  else
200  key_values[s] = "--COULD NOT RETRIEVE VALUE--";
201  }
202 
203  return key_values;
204 }
205 
206 //-----------------------------------------------------------------------------
208 {
209  // Check the JSON response if error_code == 0 && error_text == success
210  boost::optional<int> error_code = pt_.get_optional<int>("error_code");
211  boost::optional<std::string> error_text = pt_.get_optional<std::string>("error_text");
212  if( !error_code || (*error_code) != 0 || !error_text || (*error_text) != "success" )
213  {
214  if( error_text )
215  std::cerr << "ERROR: scanner replied: " << *error_text << std::endl;
216  return false;
217  }
218  return true;
219 }
220 
221 //-----------------------------------------------------------------------------
222 boost::optional<ProtocolInfo> HttpCommandInterface::getProtocolInfo()
223 {
224  // Read protocol info via HTTP/JSON request/response
225  if( !sendHttpCommand("get_protocol_info") || !checkErrorCode() )
226  return boost::optional<ProtocolInfo>();
227 
228  // Read and set protocol info
229  boost::optional<std::string> protocol_name = pt_.get_optional<std::string>("protocol_name");
230  boost::optional<int> version_major = pt_.get_optional<int>("version_major");
231  boost::optional<int> version_minor = pt_.get_optional<int>("version_minor");
232  auto ocommands = pt_.get_child_optional("commands");
233  if( !protocol_name || !version_major || !version_minor || !ocommands )
234  return boost::optional<ProtocolInfo>();
235 
236  ProtocolInfo pi;
237  pi.protocol_name = *protocol_name;
238  pi.version_major = *version_major;
239  pi.version_minor = *version_minor;
240 
241  // Read available commands of the protocol
242  boost::property_tree::ptree commands = *ocommands;
243  for( auto i= commands.begin(); i!=commands.end(); i++ )
244  {
245  std::string cmd = i->second.get<std::string>("");
246  pi.commands.push_back(cmd);
247  }
248 
249  return pi;
250 }
251 
252 //-----------------------------------------------------------------------------
253 std::vector< std::string > HttpCommandInterface::getParameterList()
254 {
255  // Read available parameters via HTTP/JSON request/response
256  std::vector< std::string > parameter_list;
257  if( !sendHttpCommand("list_parameters") || !checkErrorCode() )
258  return parameter_list;
259 
260  // Check if JSON contains the key "parameters"
261  auto oparameters = pt_.get_child_optional("parameters");
262  if( !oparameters )
263  return parameter_list;
264 
265  // Extract parameter names from JSON
266  boost::property_tree::ptree parameters = *oparameters;
267  for( auto i= parameters.begin(); i!=parameters.end(); i++ )
268  {
269  std::string param = i->second.get<std::string>("");
270  parameter_list.push_back(param);
271  }
272 
273  return parameter_list;
274 
275 }
276 
277 //-----------------------------------------------------------------------------
278 boost::optional<HandleInfo> HttpCommandInterface::requestHandleTCP(int start_angle)
279 {
280  // Prepare HTTP request
281  std::map< std::string, std::string > params;
282  params["packet_type"] = "C";
283  params["start_angle"] = std::to_string(start_angle);
284 
285  // Request handle via HTTP/JSON request/response
286  if( !sendHttpCommand("request_handle_tcp", params) || !checkErrorCode() )
287  return boost::optional<HandleInfo>();
288 
289  // Extract handle info from JSON response
290  boost::optional<int> port = pt_.get_optional<int>("port");
291  boost::optional<std::string> handle = pt_.get_optional<std::string>("handle");
292  if(!port || !handle)
293  return boost::optional<HandleInfo>();
294 
295  // Prepare return value
296  HandleInfo hi;
298  hi.handle = *handle;
299  hi.hostname = http_host_;
300  hi.port = *port;
301  hi.packet_type = 'C';
302  hi.start_angle = start_angle;
303  hi.watchdog_enabled = true;
304  hi.watchdog_timeout = 60000;
305  return hi;
306 }
307 
308 //-----------------------------------------------------------------------------
309 boost::optional<HandleInfo> HttpCommandInterface::requestHandleUDP(int port, std::string hostname, int start_angle)
310 {
311  // Prepare HTTP request
312  if( hostname == "" )
313  hostname = discoverLocalIP();
314  std::map< std::string, std::string > params;
315  params["packet_type"] = "C";
316  params["start_angle"] = std::to_string(start_angle);
317  params["port"] = std::to_string(port);
318  params["address"] = hostname;
319 
320  // Request handle via HTTP/JSON request/response
321  if( !sendHttpCommand("request_handle_udp", params) || !checkErrorCode() )
322  return boost::optional<HandleInfo>();
323 
324  // Extract handle info from JSON response
325  boost::optional<std::string> handle = pt_.get_optional<std::string>("handle");
326  if(!handle)
327  return boost::optional<HandleInfo>();
328 
329  // Prepare return value
330  HandleInfo hi;
332  hi.handle = *handle;
333  hi.hostname = hostname;
334  hi.port = port;
335  hi.packet_type = 'C';
336  hi.start_angle = start_angle;
337  hi.watchdog_enabled = true;
338  hi.watchdog_timeout = 60000;
339  return hi;
340 }
341 
342 //-----------------------------------------------------------------------------
343 bool HttpCommandInterface::releaseHandle(const std::string& handle)
344 {
345  if( !sendHttpCommand("release_handle", "handle", handle) || !checkErrorCode() )
346  return false;
347  return true;
348 }
349 
350 //-----------------------------------------------------------------------------
351 bool HttpCommandInterface::startScanOutput(const std::string& handle)
352 {
353  if( !sendHttpCommand("start_scanoutput", "handle", handle) || !checkErrorCode() )
354  return false;
355  return true;
356 }
357 
358 //-----------------------------------------------------------------------------
359 bool HttpCommandInterface::stopScanOutput(const std::string& handle)
360 {
361  if( !sendHttpCommand("stop_scanoutput", "handle", handle) || !checkErrorCode() )
362  return false;
363  return true;
364 }
365 //-----------------------------------------------------------------------------
366 bool HttpCommandInterface::feedWatchdog(const std::string &handle)
367 {
368  if( !sendHttpCommand("feed_watchdog", "handle", handle) || !checkErrorCode() )
369  return false;
370  return true;
371 }
372 
373 //-----------------------------------------------------------------------------
375 {
376  if( !sendHttpCommand("reboot_device") || !checkErrorCode() )
377  return false;
378  return true;
379 }
380 
381 //-----------------------------------------------------------------------------
382 bool HttpCommandInterface::resetParameters(const std::vector<std::string> &names)
383 {
384  // Prepare HTTP request
385  std::string namelist;
386  for( const auto& s: names )
387  namelist += (s + ";");
388  namelist.substr(0,namelist.size()-1);
389 
390  if( !sendHttpCommand("reset_parameter","list",namelist) || ! checkErrorCode() )
391  return false;
392 
393  return true;
394 }
395 
396 //-----------------------------------------------------------------------------
398 {
399  std::string local_ip;
400  try
401  {
402  using boost::asio::ip::udp;
403  boost::asio::io_service netService;
404  udp::resolver resolver(netService);
405  udp::resolver::query query(udp::v4(), http_host_ , "");
406  udp::resolver::iterator endpoints = resolver.resolve(query);
407  udp::endpoint ep = *endpoints;
408  udp::socket socket(netService);
409  socket.connect(ep);
410  boost::asio::ip::address addr = socket.local_endpoint().address();
411  local_ip = addr.to_string();
412  }
413  catch (std::exception& e)
414  {
415  std::cerr << "Could not deal with socket-exception: " << e.what() << std::endl;
416  }
417 
418  return local_ip;
419 }
420 
421 //-----------------------------------------------------------------------------
422 }
static const int HANDLE_TYPE_UDP
Definition: protocol_info.h:39
bool param(const std::string &param_name, T &param_val, const T &default_val)
bool setParameter(const std::string name, const std::string value)
int httpGet(const std::string request_path, std::string &header, std::string &content)
bool startScanOutput(const std::string &handle)
Initiate output of scan data.
bool sendHttpCommand(const std::string cmd, const std::map< std::string, std::string > param_values)
Information about the HTTP/JSON protocol.
Definition: protocol_info.h:19
bool resetParameters(const std::vector< std::string > &names)
boost::optional< HandleInfo > requestHandleTCP(int start_angle=-1800000)
HttpCommandInterface(const std::string &http_host, int http_port=80)
XmlRpcServer s
boost::optional< std::string > getParameter(const std::string name)
std_msgs::Header * header(M &m)
bool releaseHandle(const std::string &handle)
Release handle.
int http_status_code_
HTTP-Status code of last request.
std::map< std::string, std::string > getParameters(const std::vector< std::string > &names)
boost::optional< HandleInfo > requestHandleUDP(int port, std::string hostname=std::string(""), int start_angle=-1800000)
Encapsulates data about an etablished data connection.
Definition: protocol_info.h:36
boost::optional< ProtocolInfo > getProtocolInfo()
int http_port_
Port of HTTP-Interface.
std::string protocol_name
protocol name, defaults to "pfsdp"
Definition: protocol_info.h:22
bool stopScanOutput(const std::string &handle)
Terminate output of scan data.
static const int HANDLE_TYPE_TCP
Definition: protocol_info.h:38
boost::property_tree::ptree pt_
Returned JSON as property_tree.
std::vector< std::string > getParameterList()
bool feedWatchdog(const std::string &handle)
Feed the watchdog to keep the handle alive.


pepperl_fuchs_r2000
Author(s): Denis Dillenberger
autogenerated on Mon Jun 10 2019 14:12:48