Legacy Software - Seriell durch boost::asio tcp/ip ersetzen
-
ich versuche gerade eine alte Software mit seriellen Geräte wieder zum laufen zu bekommen - die Geräte fehlen mir - aber ich habe eine TCP/IP basierte Simulation welche exakt die Protokolle unterstützt
Es gibt RS232 auf TCP/IP Mapper - aber die freien Varianten sind zu stark begrenzt - ich brauch >15 Ports
Konkret habe ich 3 Funktionen in der alten Software die ich per #define auf
asio-TCP/IP-Varianten umbiege (damit ich nicht die ganze Protokoll-Verarbeitung anfassen muss - möglichst wenig Änderungen an der alten Software ist mein Ziel)1. serial_port::read(buffer, buffer_size, read_size)
Blockiert (sehr sehr sehr kurz) und liefert einfach nur was auf der seriellen Schnittstelle verfuegbar ist
(asio/socket -> async_receive?)2. serial_port::read_timeout(buffer, read_size, timeout)
Blockiert (maximal mit timeout) und wartet auf die size an Daten
(asio/socket -> async_read_some?)3. serial_port::write_timeout(buffer, write_size)
Blockiert (maximal mit timeout) bis bytes geschrieben
(asio/socket -> async_write_some?)wie koennte ich die am besten mit asio als Client nachbauen - hat jemand Tipps?
ich orientiere mich dabei an dem https://github.com/chriskohlhoff/asio/blob/master/asio/src/examples/cpp03/timeouts/blocking_tcp_client.cpp Beispiel für die Timeouts
-
ist fuer VS2010/C++03, Boost 1.61.0 und es ist nur wichtig das die Timesouts auch anschlagen - ob die Kommunikation schneller oder langsamer abläuft ist nicht relevant
-
Ich baue jetzt erstmal ein verwertbares Beispiel und Frage dann noch mal später
-
VS2010, C++03, Boost 1.61.0
basiert auf: https://github.com/chriskohlhoff/asio/blob/master/asio/src/examples/cpp03/timeouts/blocking_tcp_client.cppdie Routinen
1. write_n_timeout
2. read_available
3. read_n_with_timeoutVerhalten sich mal so wie ich das gerne hätte - aber:
-die Fehlerfälle werde noch nicht "sauber" mit return-Code (weil die alten C/C++-Schnittstellen so sind) behandelt (wie komme ich da richtig drann?)
-ich bin mir mit den boost::lambdas oder handlern unsicher - ist das richtig so oder geht das mit weniger Code/sauberer
-ich bin bei den async_write/read/receive(mit den cancel) nicht 100% sicher ob das die richtigen Funktionen fuer mich sindFragen sind mit "Frage:" markiert
zum testen benutzte ich Hercules: http://www.hw-group.com/products/hercules/index_de.html
#include <boost/asio/connect.hpp> #include <boost/asio/deadline_timer.hpp> #include <boost/asio/io_service.hpp> #include <boost/asio/ip/tcp.hpp> #include <boost/asio/streambuf.hpp> #include <boost/system/system_error.hpp> #include <boost/asio/write.hpp> #include <boost/asio/read.hpp> #include <boost/bind/bind.hpp> #include <boost/asio/placeholders.hpp> #include <cstdlib> #include <iostream> #include <string> #include <boost/lambda/bind.hpp> #include <boost/lambda/lambda.hpp> using boost::asio::deadline_timer; using boost::asio::ip::tcp; using boost::lambda::bind; using boost::lambda::var; using boost::lambda::_1; class client { public: client(): socket_(io_service_), deadline_(io_service_) { deadline_.expires_at(boost::posix_time::pos_infin); check_deadline(); } void connect(const std::string& host, const std::string& service, boost::posix_time::time_duration timeout) { tcp::resolver::query query(host, service); tcp::resolver::iterator iter = tcp::resolver(io_service_).resolve(query); deadline_.expires_from_now(timeout); boost::system::error_code ec = boost::asio::error::would_block; boost::asio::async_connect(socket_, iter, var(ec) = _1); // Block until the asynchronous operation has completed. do { io_service_.run_one(); } while (ec == boost::asio::error::would_block); if (ec || !socket_.is_open()) { throw boost::system::system_error(ec ? ec : boost::asio::error::operation_aborted); } } //warte auf n bytes bis zum timeout int read_n_with_timeout(void* p_data, size_t p_size, boost::posix_time::time_duration timeout) { deadline_.expires_from_now(timeout); boost::system::error_code ec = boost::asio::error::would_block; boost::asio::async_read(socket_, boost::asio::buffer(p_data, p_size), var(ec) = _1); // async - fuer den timeout // Block until the asynchronous operation has completed. do { io_service_.run_one(); } while (ec == boost::asio::error::would_block); if (ec) { //throw boost::system::system_error(ec); return -1; } //FRAGE: im Timeoutfall kommt "Die Netzwerkverbindung wurde durch das lokale System getrennt" als Exception meldung - kann ich damit sauber einen Timeout erkennen? return 0; // 0 = OK, <0 = Fehler, -1 = TIMEOUT } void read_available_handler(const boost::system::error_code& error, std::size_t bytes_transferred) { if(!error) { printf("read_available_handler: %u\n", bytes_transferred); int brk = 1; } else if(error == boost::asio::error::operation_aborted) { //wenn keine Daten + cancel() printf("read_handler error: boost::asio::error::operation_aborted\n"); assert(bytes_transferred == 0); } else { printf("read_available_handler error: %s\n", error.message().c_str()); assert(false); } } //lese was da ist - 0-n bytes ohne timeout da nicht blockierend int read_available(void* p_data, size_t p_size) { boost::system::error_code ec;// = boost::asio::error::would_block; //FRAGE: wie kann man das mit boost::lambda - oder eben C++03 konform schreiben ohne den read_available_handler? socket_.async_receive(boost::asio::buffer(p_data, p_size), boost::bind(&client::read_available_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); // async - fuer den timeout //hier will auch jemand 0-n bytes lesen - eben was da ist //http://stackoverflow.com/questions/31939843/boostasioasync-receive-and-0-bytes-in-socket socket_.cancel(); // abbrechen scheint hier die loesung zu sein um die gleiche semantic zu erreichen - es funktioniert auch, sieht aber komisch aus //FRAGE: ist das so richtig? //do //{ io_service_.run_one(); //} //while (ec == boost::asio::error::would_block); //FRAGE: wie komme ich hier an die transfered bytes? if (ec) { //throw boost::system::system_error(ec); return -1; } return 0; // 0-n byte gelesen, <0 = FEHLER } //schreibe n bytes (mit timeout) int write_n_timeout(void* p_data, size_t p_size, boost::posix_time::time_duration timeout) { deadline_.expires_from_now(timeout); boost::system::error_code ec = boost::asio::error::would_block; boost::asio::async_write(socket_, boost::asio::buffer(p_data, p_size), var(ec) = _1); // Block until the asynchronous operation has completed. do { io_service_.run_one(); } while (ec == boost::asio::error::would_block); if (ec) { //throw boost::system::system_error(ec); return -1; } //Frage: wie den Timeout sauber erkennen? return 0; // 0 = OK, <0 = Fehler, -1 = TIMEOUT } private: void check_deadline() { if (deadline_.expires_at() <= deadline_timer::traits_type::now()) { boost::system::error_code ignored_ec; socket_.close(ignored_ec); deadline_.expires_at(boost::posix_time::pos_infin); } // Put the actor back to sleep. deadline_.async_wait(bind(&client::check_deadline, this)); } boost::asio::io_service io_service_; tcp::socket socket_; deadline_timer deadline_; boost::asio::streambuf input_buffer_; }; //---------------------------------------------------------------------- int main(int argc, char* argv[]) { try { std::string host = "localhost"; std::string service = "544"; client c; c.connect(host, service, boost::posix_time::seconds(10)); char buffer[10]={0}; printf("write_n_timeout (schreibe n bytes (mit timeout))\n"); c.write_n_timeout(buffer, sizeof(buffer), boost::posix_time::seconds(10)); printf("read_available (lese was da ist - 0-n bytes ohne timeout da nicht blockierend)\n"); // bei dieser implementierung bin ich mir total unsicher c.read_available(buffer, sizeof(buffer)); // hol was da ist - nicht blockierend und ohne timeout printf("read_n_with_timeout (warte auf n bytes bis zum timeout)\n"); boost::posix_time::ptime time_sent = boost::posix_time::microsec_clock::universal_time(); c.read_n_with_timeout(buffer, sizeof(buffer), boost::posix_time::seconds(10)); boost::posix_time::ptime time_received = boost::posix_time::microsec_clock::universal_time(); std::cout << "Round trip time: "; std::cout << (time_received - time_sent).total_microseconds(); std::cout << " microseconds\n"; } catch (std::exception& e) { std::cerr << "Exception: " << e.what() << "\n"; } return 0; }