diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3ed6b9948e87a42dd6439f11687b67ae2bf1ee46 --- /dev/null +++ b/main.cpp @@ -0,0 +1,264 @@ +#include <ros/ros.h> +#include <ros/console.h> +#include <stdio.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +typedef unsigned char byte; +// function declarations -------------------------------------------------------- + +// gets count bytes from socket file descriptor +std::unique_ptr<byte[]> get_bytes_socket(int fd, int count); + +// log bytes sent/received +void byte_info(const byte *msg, int msg_length, bool send=true); + +// wrapping get_bytes_socket for converting to integer +int get_int_socket(int fd); + +// retrieves error message and prints it, then throws +void get_error_msg(int fd, int errorcode); // needs fix see implementation + +// converts num to network order and adds it to msg starting from index +void add_int32_in_network_order(int num, byte *msg, int index); + +// wraps write() for printing and throwing on errors +void send_bytes(int fd, const byte* bytes, int length); + +void set_time_unit_and_timeout(int fd, unsigned long int microseconds, int timeout); + +// attaches UPPAAL variable called var to channel +void add_var_to_channel(int fd, std::string channel, bool is_input, std::string var); + +// reports to var_count variables to channel named chan_name +void report_now(int fd, std::string chan_name, unsigned short var_count, int *vars); + +// throws if not answered with byte 0 +void request_start(int fd); +// ------------------------------------------------------------------------------- + +const double SECONDS_BEFORE_TIMEOUT = 30; +struct Channel { + int identifier; + bool is_input; + std::vector<std::string> vars; // associated variables in Uppaal + Channel(int id, bool is_input) : identifier(id), is_input(is_input), vars(){}; + Channel() = default; // default constructor needed for std::map +}; +std::map<std::string, Channel> channels; + +std::unique_ptr<byte[]> get_bytes_socket(int fd, int count){ + std::unique_ptr<byte[]> arr = std::make_unique<byte[]>(count); + int already_read = 0; + ros::Time start_time = ros::Time::now(); + int success = 0; + while (already_read < count) { + success = recv(fd, &arr[already_read], count-already_read, MSG_DONTWAIT); + if (success == 0) throw "connection was closed by TRON"; + if (success == -1){ + if (ros::Time::now().toSec() - start_time.toSec() >= SECONDS_BEFORE_TIMEOUT) + throw "timeout while reading bytes from socket file descriptor"; + continue; + } + already_read += success; // read returns number of bytes read + } + byte_info(arr.get(), already_read, false); + return arr; // no explicit move needed since return value is rvalue +}; + +int get_int_socket(int fd) { + auto ack = get_bytes_socket(fd, 4); + unsigned int h = ntohl(*reinterpret_cast<unsigned int*>(ack.get())); + return *reinterpret_cast<int*>(&h); // reinterpret unsigned int as int +} + +void add_int32_in_network_order(int num, byte *msg, int index){ + unsigned int n = htonl(*reinterpret_cast<unsigned int*>(&num)); + byte* bytes = reinterpret_cast<byte*>(&n); + msg[index] = bytes[0]; + msg[++index] = bytes[1]; + msg[++index] = bytes[2]; + msg[++index] = bytes[3]; +} + +void byte_info(const byte* msg, int msg_length, bool send){ + std::stringstream strstr; + strstr << (send ? "sending" : "received") << " bytes:"; + for (int i = 0; i < msg_length; i++) strstr << " " << (int)msg[i]; + ROS_INFO(strstr.str().c_str()); +} + +void send_bytes(int fd, const byte* bytes, int length){ + byte_info(bytes, length); + int ret = write(fd, (void*) bytes, length); + if (ret < 0) throw "writing failed"; +} + +void get_error_msg(int fd, int errorcode) { + ROS_WARN("got error, trying to get corresponding message"); + std::unique_ptr<byte[]> get_err_msg_msg = std::make_unique<byte[]>(5); + get_err_msg_msg[0] = 127; // get error msg code + add_int32_in_network_order(errorcode, get_err_msg_msg.get(), 1); + send_bytes(fd, get_err_msg_msg.get(), 5); // connection is closed after this write? + byte err_msg_length = get_bytes_socket(fd, 1)[0]; + auto err_msg = get_bytes_socket(fd, err_msg_length); + std::string msg_str = std::string(reinterpret_cast<char*>(err_msg.get()), (size_t) err_msg_length); + ROS_FATAL("TRON sent error message: %s", msg_str.c_str()); + throw "got error from TRON"; +} + +void add_var_to_channel(int fd, std::string channel, bool is_input, std::string var) { + std::unique_ptr<byte[]> msg = std::make_unique<byte[]>(6 + var.length()); + if (channels.find(channel) == channels.end()) throw "channel not declared"; + Channel chan = channels.at(channel); + msg[0] = is_input ? 3 : 4; + add_int32_in_network_order(chan.identifier, msg.get(), 1); + msg[5] = (byte) var.length(); + for (int i = 0; i < var.length(); i++) msg[6+i] = var.at(i); + ROS_INFO("attaching variable %s to channel %s", var.c_str(), channel.c_str()); + send_bytes(fd, msg.get(), 6 + var.length()); + int ack = get_int_socket(fd); + if (ack < 0) get_error_msg(fd, ack); + ROS_INFO("success: attached variable"); + chan.vars.push_back(var); +} + +void send_channel_decl_msg(int fd, bool is_input, std::string name) { + // prepare packet + size_t msg_length = 2 + name.length(); + std::unique_ptr<byte[]> msg = std::make_unique<byte[]>(msg_length); + msg[0] = is_input ? 1 : 2; + msg[1] = name.length(); + for (int i = 2, c = 0; i < msg_length; i++, c++) msg[i] = name[c]; + + // send packet + ROS_INFO("declaring channel %s as %s", name.c_str(), (is_input ? "input" : "output")); + byte_info(msg.get(), msg_length); + write(fd, (void*) msg.get(), msg_length); + + // get answer from TRON + int channel_identifier = get_int_socket(fd); + if (channel_identifier < 0) { // error handling + get_error_msg(fd, channel_identifier); + } + if (channel_identifier == 0) throw "did not get channel identifier"; + // assigned channel ID successfully + ROS_INFO("success: identifier for channel %s is %i", name.c_str(), channel_identifier); + channels[name] = Channel(channel_identifier, is_input); +} + +void set_time_unit_and_timeout(int fd, unsigned long int microseconds, int timeout){ + std::unique_ptr<byte[]> msg = std::make_unique<byte[]>(9); + msg[0] = 5; + byte *microseconds_bytes = reinterpret_cast<byte*>(µseconds); + + // htonl does not exist for long int + const bool IS_BIG_ENDIAN = htonl(47) == 47; + if (IS_BIG_ENDIAN) { + for (int i = 0; i < 8; i++) msg[i+1] = microseconds_bytes[i]; + } else { + for (int i = 0; i < 8; i++) msg[i+1] = microseconds_bytes[7-i]; + } + ROS_INFO("setting time unit: %i microseconds", microseconds); + send_bytes(fd, msg.get(), 9); + int ack = get_int_socket(fd); + if (ack != 0) get_error_msg(fd, ack); + ROS_INFO("success: set time unit"); + + msg.reset(new byte[5]); + msg[0] = 6; + add_int32_in_network_order(timeout, msg.get(), 1); + ROS_INFO("setting timeout to %i units", timeout); + send_bytes(fd, msg.get(), 5); + ack = get_int_socket(fd); + if (ack != 0) get_error_msg(fd, ack); + ROS_INFO("success: set timeout"); +} + +void request_start(int fd) { + ROS_INFO("requesting start"); + byte start = 64; + send_bytes(fd, &start, 1); + byte answer = get_bytes_socket(fd, 1)[0]; + if (answer != 0) throw "starting failed"; + ROS_INFO("success: starting test phase"); +} + +// nach input verarbeitung weiter machen +void report_now(int fd, std::string chan_name, unsigned short var_count, int *vars){ + std::unique_ptr<byte[]> msg = std::make_unique<byte[]>(6 + 4 * var_count); + if (channels.find(chan_name) == channels.end()) throw "channel not declared"; + Channel chan = channels.at(chan_name); + + add_int32_in_network_order(chan.identifier, msg.get(), 0); + + unsigned short var_count_network_order = htons(var_count); + byte *var_count_bytes = reinterpret_cast<byte*>(&var_count_network_order); + msg[4] = var_count_bytes[0]; + msg[5] = var_count_bytes[1]; + + for (unsigned short i = 0; i < var_count; i++) + add_int32_in_network_order(vars[i], msg.get(), 6 + i * 4); + + send_bytes(fd, msg.get(), 6 + 4 * var_count); +} + +int main(int argc, char**argv){ + ros::init(argc, argv, "TRON dapter"); + ros::NodeHandle nh; + + const std::string IP = "127.0.0.1"; + const short PORT = 8080; + + int socketfd; + if ((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + ROS_FATAL("socket could not be created"); + ros::shutdown(); + } + ROS_INFO("socket created successfully"); + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(PORT); + { + int x = inet_pton(AF_INET, IP.c_str(), &addr.sin_addr); + if (x == 1) { + ROS_INFO("valid internet address"); + } else { + ROS_FATAL("internet address could not be converted"); + ros::shutdown(); + } + } + if (connect(socketfd, (struct sockaddr*)&addr, sizeof(sockaddr_in)) < 0) { + ROS_FATAL("connection failed"); + ros::shutdown(); + } + ROS_INFO("successfully connected to TRON"); + try { + send_channel_decl_msg(socketfd, true, "ausloesen"); + //add_var_to_channel(socketfd, "ausloesen", true, "global"); + send_channel_decl_msg(socketfd, false, "position"); + add_var_to_channel(socketfd, "position", false, "zahl"); + // nicht in dokumentation: lokale variablen nicht möglich + //add_var_to_channel(socketfd, "ausloesen", "lokal"); + + unsigned long int microseconds = 1000000; // one second + // Dokumentation arbeitet mit 2 unsigned ints, äußerst mystisch + set_time_unit_and_timeout(socketfd, microseconds, 10); + + // FALSCH IN DOKUMENTATION: Start und error msg byte codes vertauscht + // 64 ist start + // 127 ist gerErrorMessage + request_start(socketfd); // communication not synchronous anymore after start + + // erst nach ausloesen input wäre akzeptierbar + int zahl = 4; + //report_now(socketfd, "position", 1, &zahl); + } catch (const char* err){ + ROS_FATAL("shutting down: %s", err); + ros::shutdown(); + } + + ros::Duration(100).sleep(); +} \ No newline at end of file