diff --git a/owlps-positioning/Makefile b/owlps-positioning/Makefile index 5c17b54..f720f45 100644 --- a/owlps-positioning/Makefile +++ b/owlps-positioning/Makefile @@ -91,6 +91,7 @@ OBJ_LIST = \ outputterminal.o \ outputcsv.o \ outputudpsocket.o \ + outputtcpsocketevaal.o \ positioning.o \ input.o \ inputcsv.o \ @@ -246,6 +247,9 @@ $(OBJ_DIR)/outputcsv.o: \ $(OBJ_DIR)/outputudpsocket.o: \ $(SRC_DIR)/outputmedium.hh \ $(OBJ_DIR)/result.o +$(OBJ_DIR)/outputtcpsocketevaal.o: \ + $(SRC_DIR)/outputmedium.hh \ + $(OBJ_DIR)/result.o $(OBJ_DIR)/outputcsv.o: \ $(SRC_DIR)/outputmedium.hh \ $(OBJ_DIR)/result.o @@ -253,6 +257,7 @@ $(OBJ_DIR)/output.o: \ $(OBJ_DIR)/outputterminal.o \ $(OBJ_DIR)/outputcsv.o \ $(OBJ_DIR)/outputudpsocket.o \ + $(OBJ_DIR)/outputtcpsocketevaal.o \ $(OBJ_DIR)/configuration.o \ $(OBJ_DIR)/posexcept.o $(OBJ_DIR)/multilaterationalgorithm.o: \ diff --git a/owlps-positioning/src/output.cc b/owlps-positioning/src/output.cc index 0496953..43cbee3 100644 --- a/owlps-positioning/src/output.cc +++ b/owlps-positioning/src/output.cc @@ -5,6 +5,7 @@ #include "outputterminal.hh" #include "outputcsv.hh" #include "outputudpsocket.hh" +#include "outputtcpsocketevaal.hh" #include @@ -57,6 +58,9 @@ void Output::initialise_output_media() else if (*i == "UDP") initialise_output_udp_socket() ; + else if (*i == "TCPEvAAL") + initialise_output_tcpevaal_socket() ; + else throw bad_configuration( "The specified output medium « "+ *i +" » is unknown!") ; @@ -70,18 +74,6 @@ void Output::initialise_output_terminal() } -void Output::initialise_output_udp_socket() -{ - if (! Configuration::is_configured("output.udp-host")) - throw missing_configuration( - "No remote UDP host specified in the configuration!") ; - - output_media.push_back( - new OutputUDPSocket(Configuration::string_value("output.udp-host"), - Configuration::int_value("output.udp-port"))) ; -} - - void Output::initialise_output_csv() { if (! Configuration::is_configured("output.csv-file")) @@ -94,6 +86,24 @@ void Output::initialise_output_csv() } +void Output::initialise_output_udp_socket() +{ + if (! Configuration::is_configured("output.udp-host")) + throw missing_configuration( + "No remote UDP host specified in the configuration!") ; + + output_media.push_back( + new OutputUDPSocket(Configuration::string_value("output.udp-host"), + Configuration::int_value("output.udp-port"))) ; +} + + +void Output::initialise_output_tcpevaal_socket() +{ + output_media.push_back(new OutputTCPSocketEvAAL) ; +} + + void Output::write(const ResultList &results) const { for (vector::const_iterator i = output_media.begin() ; diff --git a/owlps-positioning/src/output.hh b/owlps-positioning/src/output.hh index 29d4d15..1a5b26a 100644 --- a/owlps-positioning/src/output.hh +++ b/owlps-positioning/src/output.hh @@ -19,6 +19,7 @@ protected: void initialise_output_terminal(void) ; void initialise_output_csv(void) ; void initialise_output_udp_socket(void) ; + void initialise_output_tcpevaal_socket(void) ; //@} public: diff --git a/owlps-positioning/src/outputtcpsocketevaal.cc b/owlps-positioning/src/outputtcpsocketevaal.cc new file mode 100644 index 0000000..fcb3851 --- /dev/null +++ b/owlps-positioning/src/outputtcpsocketevaal.cc @@ -0,0 +1,157 @@ +#include "outputtcpsocketevaal.hh" +#include "request.hh" +#include "resultlist.hh" +#include "posexcept.hh" + +#include +#include // For perror() + +using namespace std ; + + + +/* *** Constructors *** */ + + +OutputTCPSocketEvAAL::OutputTCPSocketEvAAL( + const string &_remote_host, + const uint_fast16_t _remote_port): + remote_host(_remote_host), remote_port(_remote_port) +{ + if (! init_socket()) + throw error_opening_output_file("TCP socket") ; +} + + +OutputTCPSocketEvAAL::~OutputTCPSocketEvAAL() +{ + close_socket() ; +} + + + +/* *** Operations *** */ + + +/** + * @return \em true if the socket were successfully opened. + * @return \em false in case of error. + */ +bool OutputTCPSocketEvAAL::init_socket() +{ + sockfd = socket(AF_INET, SOCK_STREAM, 0) ; + if (sockfd < 0) + { + perror("TCP socket creation failed") ; + return false ; + } + + struct sockaddr_in server_description ; + memset(&server_description, 0, sizeof(server_description)) ; + server_description.sin_family = AF_INET ; + // Server IP address: + server_description.sin_addr.s_addr = inet_addr(remote_host.c_str()) ; + // Listening port on the server: + server_description.sin_port = htons(remote_port) ; + + int ret = connect(sockfd, (struct sockaddr *)&server_description, + sizeof(server_description)) ; + if (ret < 0) + { + perror("Cannot bind the TCP socket") ; + return false ; + } + + return true ; +} + + +/** + * Normally, the socket is closed automatically by the destructor. Use + * this if you want to close the socket prematurely. + * #sockfd is set to -1, even in case of error. + * @return \em true if the socket were successfully closed or were not + * opened. + * @return \em false in case of error. + */ +bool OutputTCPSocketEvAAL::close_socket() +{ + if (sockfd >= 0) + { + if (close(sockfd)) + { + perror("Cannot close TCP socket") ; + return false ; + } + + sockfd = -1 ; + } + + return true ; +} + + +void OutputTCPSocketEvAAL::write(const Result &result) +{ + const Point3D &position = result.get_position() ; + long timestamp = 1234567l ; // FIXME + int area_of_interest = 0 ; // FIXME + + ostringstream str ; + str + << "OwlPS " + << position.get_x() << ' ' + << position.get_y() << ' ' + << timestamp << ' ' + << area_of_interest << '\n' ; + + if (send_data(str.str())) + acknowledge() ; +} + + +void OutputTCPSocketEvAAL::write(const ResultList &results) +{ + assert(results.get_results().size() == 1) ; + write(results.get_results()[0]) ; +} + + +/** + * Sends the text buffer 'data'. + */ +bool OutputTCPSocketEvAAL::send_data(const string &data) const +{ + ssize_t nsent = send(sockfd, data.c_str(), data.size(), 0) ; + if (nsent != static_cast(data.size())) + { + perror("Error sending result data") ; + return false ; + } + return true ; +} + + +bool OutputTCPSocketEvAAL::acknowledge() const +{ + char ack_buf[10] ; + ssize_t nrecv = recv(sockfd, ack_buf, 10, 0) ; + if (nrecv < 0) + perror("Error receiving acknowledgement") ; + + ack_buf[9] = '\0' ; + string ack(ack_buf) ; + if (ack == "Published") + return true ; + + if (ack == "NotParsed") + { + cerr << "TCP server error: string not parsed.\n" ; + return false ; + } + + cerr + << "TCP server error: unknown acknoledgment string « " + << ack << " ».\n" ; + return false ; +} diff --git a/owlps-positioning/src/outputtcpsocketevaal.hh b/owlps-positioning/src/outputtcpsocketevaal.hh new file mode 100644 index 0000000..b355b59 --- /dev/null +++ b/owlps-positioning/src/outputtcpsocketevaal.hh @@ -0,0 +1,51 @@ +#ifndef _OWLPS_POSITIONING_OUTPUTTCPSOCKETEVAAL_HH_ +#define _OWLPS_POSITIONING_OUTPUTTCPSOCKETEVAAL_HH_ + +#include "outputmedium.hh" + +#include +#include // is not C++ 98 compliant +#include + +/// Sends results to a remote host by TCP (EvAAL competition format) +/** + * The results are sent through an TCP socket as a string value, + * conforming to the EvAAL competition format. + */ +class OutputTCPSocketEvAAL: public OutputMedium +{ +protected: + int sockfd ; + std::string remote_host ; + uint_fast16_t remote_port ; + + struct sockaddr_in server_info ; + struct sockaddr_in client_info ; + + /** @name Operations */ + //@{ + /// Initialises the socket + bool init_socket(void) ; + /// Sends a string through the socket + bool send_data(const std::string &data) const ; + /// Receives and parse the acknowledgement from the server + bool acknowledge(void) const ; + //@} + +public: + OutputTCPSocketEvAAL(const std::string &_remote_host = "127.0.0.1", + const uint_fast16_t _remote_port = 4444) ; + ~OutputTCPSocketEvAAL(void) ; + + /** @name Operations */ + //@{ + void write(const Result &result) ; + void write(const ResultList &results) ; + /// Closes the socket + bool close_socket(void) ; + //@} +} ; + + + +#endif // _OWLPS_POSITIONING_OUTPUTTCPSOCKETEVAAL_HH_ diff --git a/owlps-positioning/src/userinterface.cc b/owlps-positioning/src/userinterface.cc index 375a150..be4acf4 100644 --- a/owlps-positioning/src/userinterface.cc +++ b/owlps-positioning/src/userinterface.cc @@ -241,8 +241,9 @@ void UserInterface::fill_output_options() options.add_options() ("output.medium,O", po::value< vector >()->composing(), "Medium to which the results will be wrote. You can specify" - " this option more than once. Allowed: Terminal, CSV, UDP." - " If this option is absent, results will be printed on the terminal.") + " this option more than once." + " Allowed: Terminal, CSV, UDP, TCPEvAAL." + " If this option is absent, the results are printed on the terminal.") ("output.csv-file", po::value(), "CSV file to use for output (when output.medium = CSV).") ("output.udp-host", po::value(),