http_command_interface.cpp
Go to the documentation of this file.
00001 // Copyright (c) 2014, Pepperl+Fuchs GmbH, Mannheim
00002 // Copyright (c) 2014, Denis Dillenberger
00003 // All rights reserved.
00004 //
00005 // Use, modification, and distribution is subject to the
00006 // 3-clause BSD license ("Revised BSD License",
00007 // "New BSD License", or "Modified BSD License")
00008 // You should have received a copy of this license
00009 // in a file named COPYING or LICENSE.
00010 
00011 #include <pepperl_fuchs_r2000/http_command_interface.h>
00012 #include <iostream>
00013 #include <boost/asio.hpp>
00014 #include <boost/property_tree/json_parser.hpp>
00015 
00016 namespace pepperl_fuchs {
00017 
00018 //-----------------------------------------------------------------------------
00019 HttpCommandInterface::HttpCommandInterface(const std::string &http_host, int http_port)
00020 {
00021     http_host_ = http_host;
00022     http_port_ = http_port;
00023     http_status_code_ = 0;
00024 }
00025 
00026 //-----------------------------------------------------------------------------
00027 int HttpCommandInterface::httpGet(const std::string request_path, std::string &header, std::string &content)
00028 {
00029     header = "";
00030     content = "";
00031     using boost::asio::ip::tcp;
00032     try
00033     {
00034         boost::asio::io_service io_service;
00035 
00036         // Lookup endpoint
00037         tcp::resolver resolver(io_service);
00038         tcp::resolver::query query(http_host_, std::to_string(http_port_));
00039         tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
00040         tcp::resolver::iterator end;
00041 
00042         // Create socket
00043         tcp::socket socket(io_service);
00044         boost::system::error_code error = boost::asio::error::host_not_found;
00045 
00046         // Iterate over endpoints and etablish connection
00047         while (error && endpoint_iterator != end)
00048         {
00049             socket.close();
00050             socket.connect(*endpoint_iterator++, error);
00051         }
00052         if (error)
00053             throw boost::system::system_error(error);
00054 
00055         // Prepare request
00056         boost::asio::streambuf request;
00057         std::ostream request_stream(&request);
00058         request_stream << "GET " << request_path << " HTTP/1.0\r\n\r\n";
00059 
00060         boost::asio::write(socket, request);
00061 
00062         // Read the response status line. The response streambuf will automatically
00063         // grow to accommodate the entire line. The growth may be limited by passing
00064         // a maximum size to the streambuf constructor.
00065         boost::asio::streambuf response;
00066         boost::asio::read_until(socket, response, "\r\n");
00067 
00068         // Check that response is OK.
00069         std::istream response_stream(&response);
00070         std::string http_version;
00071         response_stream >> http_version;
00072         unsigned int status_code;
00073         response_stream >> status_code;
00074         std::string status_message;
00075         std::getline(response_stream, status_message);
00076         if (!response_stream || http_version.substr(0, 5) != "HTTP/")
00077         {
00078             std::cout << "Invalid response\n";
00079             return 0;
00080         }
00081 
00082         // Read the response headers, which are terminated by a blank line.
00083         boost::asio::read_until(socket, response, "\r\n\r\n");
00084 
00085         // Process the response headers.
00086         std::string tmp;
00087         while (std::getline(response_stream, tmp) && tmp != "\r")
00088             header += tmp+"\n";
00089 
00090         // Write whatever content we already have to output.
00091         while (std::getline(response_stream, tmp))
00092             content += tmp;
00093 
00094         // Read until EOF, writing data to output as we go.
00095         while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error))
00096         {
00097             response_stream.clear();
00098             while (std::getline(response_stream, tmp))
00099                 content += tmp;
00100         }
00101 
00102         if (error != boost::asio::error::eof)
00103             throw boost::system::system_error(error);
00104 
00105         // Substitute CRs by a space
00106         for( std::size_t i=0; i<header.size(); i++ )
00107             if( header[i] == '\r' )
00108                 header[i] = ' ';
00109 
00110         for( std::size_t i=0; i<content.size(); i++ )
00111             if( content[i] == '\r' )
00112                 content[i] = ' ';
00113 
00114         return status_code;
00115     }
00116     catch (std::exception& e)
00117     {
00118         std::cerr << "Exception: " <<  e.what() << std::endl;
00119         return 0;
00120     }
00121 }
00122 
00123 //-----------------------------------------------------------------------------
00124 bool HttpCommandInterface::sendHttpCommand(const std::string cmd, const std::map<std::string, std::string> param_values)
00125 {
00126     // Build request string
00127     std::string request_str = "/cmd/" + cmd + "?";
00128     for( auto& kv : param_values )
00129         request_str += kv.first + "=" + kv.second + "&";
00130     if(request_str.back() == '&' )
00131         request_str = request_str.substr(0,request_str.size()-1);
00132 
00133     // Do HTTP request
00134     std::string header, content;
00135     http_status_code_ = httpGet(request_str,header,content);
00136 
00137     // Try to parse JSON response
00138     try
00139     {
00140         std::stringstream ss(content);
00141         boost::property_tree::json_parser::read_json(ss,pt_);
00142     }
00143     catch (std::exception& e)
00144     {
00145         std::cerr << "ERROR: Exception: " <<  e.what() << std::endl;
00146         return false;
00147     }
00148 
00149     // Check HTTP-status code
00150     if( http_status_code_ != 200 )
00151         return false;
00152     else
00153         return true;
00154 }
00155 
00156 //-----------------------------------------------------------------------------
00157 bool HttpCommandInterface::sendHttpCommand(const std::string cmd, const std::string param, const std::string value)
00158 {
00159     std::map<std::string, std::string> param_values;
00160     if( param != "" )
00161         param_values[param] = value;
00162     return sendHttpCommand(cmd,param_values);
00163 }
00164 
00165 //-----------------------------------------------------------------------------
00166 bool HttpCommandInterface::setParameter(const std::string name, const std::string value)
00167 {
00168     return sendHttpCommand("set_parameter",name,value) && checkErrorCode();
00169 }
00170 
00171 //-----------------------------------------------------------------------------
00172 boost::optional< std::string > HttpCommandInterface::getParameter(const std::string name)
00173 {
00174     if( !sendHttpCommand("get_parameter","list",name) || ! checkErrorCode()  )
00175         return boost::optional<std::string>();
00176     return pt_.get_optional<std::string>(name);
00177 }
00178 
00179 //-----------------------------------------------------------------------------
00180 std::map< std::string, std::string > HttpCommandInterface::getParameters(const std::vector<std::string> &names)
00181 {
00182     // Build request string
00183     std::map< std::string, std::string > key_values;
00184     std::string namelist;
00185     for( const auto& s: names )
00186         namelist += (s + ";");
00187     namelist.substr(0,namelist.size()-1);
00188 
00189     // Read parameter values via HTTP/JSON request/response
00190     if( !sendHttpCommand("get_parameter","list",namelist) || ! checkErrorCode()  )
00191         return key_values;
00192 
00193     // Extract values from JSON property_tree
00194     for( const auto& s: names )
00195     {
00196         auto ov = pt_.get_optional<std::string>(s);
00197         if( ov )
00198             key_values[s] = *ov;
00199         else
00200             key_values[s] = "--COULD NOT RETRIEVE VALUE--";
00201     }
00202 
00203     return key_values;
00204 }
00205 
00206 //-----------------------------------------------------------------------------
00207 bool HttpCommandInterface::checkErrorCode()
00208 {
00209     // Check the JSON response if error_code == 0 && error_text == success
00210     boost::optional<int> error_code = pt_.get_optional<int>("error_code");
00211     boost::optional<std::string> error_text = pt_.get_optional<std::string>("error_text");
00212     if( !error_code || (*error_code) != 0 || !error_text || (*error_text) != "success" )
00213     {
00214         if( error_text )
00215             std::cerr << "ERROR: scanner replied: " << *error_text << std::endl;
00216         return false;
00217     }
00218     return true;
00219 }
00220 
00221 //-----------------------------------------------------------------------------
00222 boost::optional<ProtocolInfo> HttpCommandInterface::getProtocolInfo()
00223 {
00224     // Read protocol info via HTTP/JSON request/response
00225     if( !sendHttpCommand("get_protocol_info") || !checkErrorCode() )
00226         return boost::optional<ProtocolInfo>();
00227 
00228     // Read and set protocol info
00229     boost::optional<std::string> protocol_name = pt_.get_optional<std::string>("protocol_name");
00230     boost::optional<int> version_major = pt_.get_optional<int>("version_major");
00231     boost::optional<int> version_minor = pt_.get_optional<int>("version_minor");
00232     auto ocommands = pt_.get_child_optional("commands");
00233     if( !protocol_name || !version_major || !version_minor || !ocommands )
00234         return boost::optional<ProtocolInfo>();
00235 
00236     ProtocolInfo pi;
00237     pi.protocol_name = *protocol_name;
00238     pi.version_major = *version_major;
00239     pi.version_minor = *version_minor;
00240 
00241     // Read available commands of the protocol
00242     boost::property_tree::ptree commands = *ocommands;
00243     for( auto i= commands.begin(); i!=commands.end(); i++ )
00244     {
00245         std::string cmd = i->second.get<std::string>("");
00246         pi.commands.push_back(cmd);
00247     }
00248 
00249     return pi;
00250 }
00251 
00252 //-----------------------------------------------------------------------------
00253 std::vector< std::string > HttpCommandInterface::getParameterList()
00254 {
00255     // Read available parameters via HTTP/JSON request/response
00256     std::vector< std::string > parameter_list;
00257     if( !sendHttpCommand("list_parameters") || !checkErrorCode() )
00258         return parameter_list;
00259 
00260     // Check if JSON contains the key "parameters"
00261     auto oparameters = pt_.get_child_optional("parameters");
00262     if( !oparameters )
00263         return parameter_list;
00264 
00265     // Extract parameter names from JSON
00266     boost::property_tree::ptree parameters = *oparameters;
00267     for( auto i= parameters.begin(); i!=parameters.end(); i++ )
00268     {
00269         std::string param = i->second.get<std::string>("");
00270         parameter_list.push_back(param);
00271     }
00272 
00273     return parameter_list;
00274 
00275 }
00276 
00277 //-----------------------------------------------------------------------------
00278 boost::optional<HandleInfo> HttpCommandInterface::requestHandleTCP(int start_angle)
00279 {
00280     // Prepare HTTP request
00281     std::map< std::string, std::string > params;
00282     params["packet_type"] = "C";
00283     params["start_angle"] = std::to_string(start_angle);
00284 
00285     // Request handle via HTTP/JSON request/response
00286     if( !sendHttpCommand("request_handle_tcp", params) || !checkErrorCode() )
00287         return boost::optional<HandleInfo>();
00288 
00289     // Extract handle info from JSON response
00290     boost::optional<int> port = pt_.get_optional<int>("port");
00291     boost::optional<std::string> handle = pt_.get_optional<std::string>("handle");
00292     if(!port || !handle)
00293         return boost::optional<HandleInfo>();
00294 
00295     // Prepare return value
00296     HandleInfo hi;
00297     hi.handle_type = HandleInfo::HANDLE_TYPE_TCP;
00298     hi.handle = *handle;
00299     hi.hostname = http_host_;
00300     hi.port = *port;
00301     hi.packet_type = 'C';
00302     hi.start_angle = start_angle;
00303     hi.watchdog_enabled = true;
00304     hi.watchdog_timeout = 60000;
00305     return hi;
00306 }
00307 
00308 //-----------------------------------------------------------------------------
00309 boost::optional<HandleInfo> HttpCommandInterface::requestHandleUDP(int port, std::string hostname, int start_angle)
00310 {
00311     // Prepare HTTP request
00312     if( hostname == "" )
00313         hostname = discoverLocalIP();
00314     std::map< std::string, std::string > params;
00315     params["packet_type"] = "C";
00316     params["start_angle"] = std::to_string(start_angle);
00317     params["port"] = std::to_string(port);
00318     params["address"] = hostname;
00319 
00320     // Request handle via HTTP/JSON request/response
00321     if( !sendHttpCommand("request_handle_udp", params) || !checkErrorCode() )
00322         return boost::optional<HandleInfo>();
00323 
00324     // Extract handle info from JSON response
00325     boost::optional<std::string> handle = pt_.get_optional<std::string>("handle");
00326     if(!handle)
00327         return boost::optional<HandleInfo>();
00328 
00329     // Prepare return value
00330     HandleInfo hi;
00331     hi.handle_type = HandleInfo::HANDLE_TYPE_UDP;
00332     hi.handle = *handle;
00333     hi.hostname = hostname;
00334     hi.port = port;
00335     hi.packet_type = 'C';
00336     hi.start_angle = start_angle;
00337     hi.watchdog_enabled = true;
00338     hi.watchdog_timeout = 60000;
00339     return hi;
00340 }
00341 
00342 //-----------------------------------------------------------------------------
00343 bool HttpCommandInterface::releaseHandle(const std::string& handle)
00344 {
00345     if( !sendHttpCommand("release_handle", "handle", handle) || !checkErrorCode() )
00346         return false;
00347     return true;
00348 }
00349 
00350 //-----------------------------------------------------------------------------
00351 bool HttpCommandInterface::startScanOutput(const std::string& handle)
00352 {
00353     if( !sendHttpCommand("start_scanoutput", "handle", handle) || !checkErrorCode() )
00354         return false;
00355     return true;
00356 }
00357 
00358 //-----------------------------------------------------------------------------
00359 bool HttpCommandInterface::stopScanOutput(const std::string& handle)
00360 {
00361     if( !sendHttpCommand("stop_scanoutput", "handle", handle) || !checkErrorCode() )
00362         return false;
00363     return true;
00364 }
00365 //-----------------------------------------------------------------------------
00366 bool HttpCommandInterface::feedWatchdog(const std::string &handle)
00367 {
00368     if( !sendHttpCommand("feed_watchdog", "handle", handle) || !checkErrorCode() )
00369         return false;
00370     return true;
00371 }
00372 
00373 //-----------------------------------------------------------------------------
00374 bool HttpCommandInterface::rebootDevice()
00375 {
00376     if( !sendHttpCommand("reboot_device") || !checkErrorCode() )
00377         return false;
00378     return true;
00379 }
00380 
00381 //-----------------------------------------------------------------------------
00382 bool HttpCommandInterface::resetParameters(const std::vector<std::string> &names)
00383 {
00384     // Prepare HTTP request
00385     std::string namelist;
00386     for( const auto& s: names )
00387         namelist += (s + ";");
00388     namelist.substr(0,namelist.size()-1);
00389 
00390     if( !sendHttpCommand("reset_parameter","list",namelist) || ! checkErrorCode()  )
00391         return false;
00392 
00393     return true;
00394 }
00395 
00396 //-----------------------------------------------------------------------------
00397 std::string HttpCommandInterface::discoverLocalIP()
00398 {
00399     std::string local_ip;
00400     try
00401     {
00402         using boost::asio::ip::udp;
00403         boost::asio::io_service netService;
00404         udp::resolver resolver(netService);
00405         udp::resolver::query query(udp::v4(), http_host_ , "");
00406         udp::resolver::iterator endpoints = resolver.resolve(query);
00407         udp::endpoint ep = *endpoints;
00408         udp::socket socket(netService);
00409         socket.connect(ep);
00410         boost::asio::ip::address addr = socket.local_endpoint().address();
00411         local_ip = addr.to_string();
00412     }
00413     catch (std::exception& e)
00414     {
00415         std::cerr << "Could not deal with socket-exception: " << e.what() << std::endl;
00416     }
00417 
00418     return local_ip;
00419 }
00420 
00421 //-----------------------------------------------------------------------------
00422 }


pepperl_fuchs_r2000
Author(s): Denis Dillenberger
autogenerated on Wed Aug 26 2015 15:22:22