diff --git a/main.cpp b/main.cpp index 15f96998be161279e3d73dead2f437b937f23d91..6f91855e7aa89c42c0ec731b9821b9ca41b47976 100644 --- a/main.cpp +++ b/main.cpp @@ -135,8 +135,10 @@ struct Channel { std::vector<std::string> vars; // associated variables in Uppaal Channel(std::string name, int32_t id, bool is_input) : name(name), identifier(id), is_input(is_input){}; }; -std::vector<Channel> channels; +// used in struct Mapping for convenience +std::unique_ptr<Channel> send_channel_decl_msg(bool is_input, std::string name); +void add_var_to_channel(Channel& chan, bool is_input, std::string var); struct Mapping { std::string topic; // ROS topic name @@ -154,13 +156,13 @@ struct Mapping { // a pointer to an integer to increment by the amount of bytes added std::vector<void (*)(int32_t, byte*, int*)> converters_to_topics; - int channel_index; // TRON channel + Channel channel = Channel("", 0, true); // TRON channel /* note: all vectors need to have the same size example: topic = "example_topic" byte_offset = [4, 2] converters = *some pointer converting 8 bytes to int32, nullptr - channel = pointer to struct Channel "example channel" + channel = struct Channel "example channel" This example maps any message from topic "example_topic" to "example_channel". 8 Bytes starting from index 4 are converted via the given pointer @@ -173,14 +175,58 @@ struct Mapping { The next position is incremented by 4 if there is no conversion function given. Note that the first 4 Bytes of the topics message are ignored in this example. */ -}; -std::vector<Mapping> mappings; + // Callback used if TRON sends some message + void(*input_callback)(Mapping& map, int32_t*) = nullptr; + + Mapping(std::string topic, std::string channel, bool channelIsInput){ + this->topic = topic; + this->channel = *send_channel_decl_msg(channelIsInput, channel).get(); + } + + void add_var_to_mapping(std::string name_tron, int byte_offset, + int32_t (*conv_to_TRON)(byte*, int*) = nullptr, + void (*conv_to_topic)(int32_t, byte*, int*) = nullptr){ + add_var_to_channel(this->channel, this->channel.is_input, name_tron); + this->byte_offset.push_back(byte_offset); + this->converters_to_TRON.push_back(conv_to_TRON); + this->converters_to_topics.push_back(conv_to_topic); + } +}; +std::list<Mapping> mappings; std::vector<ros::Publisher> input_publishers; -// custom callbacks for TRONs messages taking channel name, pointer to values and publisher to publish to -// key is pair of topic name and channel name in this order -// not using custom fallbacks leads to mapping_callback_to_topics being used -std::map<std::pair<std::string, std::string>, void(*)(std::string, int32_t*, ros::Publisher&)> input_callbacks; +// callback publishing to topics like defined in mapping, used for input_callback +template<class T> +void mapping_callback_to_topic(Mapping& map, int32_t* vars){ + boost::shared_ptr<T> shared_ptr = boost::make_shared<T>(); + byte* ptr = reinterpret_cast<byte*>(shared_ptr.get()); + int next_pos = 0; + for (int i = 0; i < map.channel.vars.size(); i++){ + int32_t val = vars[i]; + next_pos += map.byte_offset[i]; + if (map.converters_to_topics[i] == nullptr) { + byte* val_bytes; + if (SYS_IS_BIG_ENDIAN){ + uint32_t switched_byte_order = htonl(*reinterpret_cast<uint32_t*>(&val)); + val = *reinterpret_cast<int32_t*>(&switched_byte_order); + } + val_bytes = reinterpret_cast<byte*>(&val); + ptr[next_pos++] = val_bytes[0]; + ptr[next_pos++] = val_bytes[1]; + ptr[next_pos++] = val_bytes[2]; + ptr[next_pos++] = val_bytes[3]; + } else { + map.converters_to_topics[i](val, &ptr[next_pos], &next_pos); + } + } + for (ros::Publisher& pub : input_publishers) + if (pub.getTopic() == map.topic) { + pub.publish(shared_ptr); + return; + } + throw "did not find publisher for topic"; +} + // subscribers already have callbacks std::vector<ros::Subscriber> output_subscribers; @@ -190,17 +236,6 @@ compared reliably with 0 after testing is terminated */ /* note: TRON only acknowledges if virtual clock is used, which it is not (yet) */ int acks_missing = 0; -// specific functions ----------------------------------------------------- - -int get_channel_index(std::string chan_name) { - int i = 0; - for (Channel& cur_chan : channels) { - if (cur_chan.name == chan_name) return i; - i++; - } - throw "channel not declared"; -} - void get_error_msg(int32_t errorcode) { ROS_WARN("got error, trying to get corresponding message"); byte get_err_msg_msg[5]; @@ -214,14 +249,13 @@ void get_error_msg(int32_t errorcode) { throw "got error from TRON"; } -void add_var_to_channel(std::string channel, bool is_input, std::string var) { +void add_var_to_channel(Channel& chan, bool is_input, std::string var) { byte msg[6 + var.length()]; - Channel& chan = channels[get_channel_index(channel)]; msg[0] = is_input ? ADD_VAR_TO_INPUT : ADD_VAR_TO_OUTPUT; add_int32_in_network_order(chan.identifier, msg, 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()); + ROS_INFO("attaching variable %s to channel %s", var.c_str(), chan.name.c_str()); send_bytes(socket_fd, msg, 6 + var.length()); int32_t ack = get_int_socket(socket_fd); if (ack < 0) get_error_msg(ack); @@ -229,7 +263,7 @@ void add_var_to_channel(std::string channel, bool is_input, std::string var) { chan.vars.push_back(var); } -void send_channel_decl_msg(bool is_input, std::string name) { +std::unique_ptr<Channel> send_channel_decl_msg(bool is_input, std::string name) { // prepare packet size_t msg_length = 2 + name.length(); byte msg[msg_length]; @@ -250,7 +284,7 @@ void send_channel_decl_msg(bool is_input, std::string name) { 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.push_back(Channel(name, channel_identifier, is_input)); + return std::make_unique<Channel>(name, channel_identifier, is_input); } void set_time_unit_and_timeout(uint64_t microseconds, int32_t timeout){ @@ -291,13 +325,10 @@ void request_start() { ROS_INFO("success: starting test phase"); } -void report_now(std::string chan_name, uint16_t var_count, int32_t *vars){ +void report_now(Channel& chan, uint16_t var_count, int32_t *vars){ std::unique_ptr<byte[]> msg = std::make_unique<byte[]>(6 + 4 * var_count); - Channel *chan = nullptr; - for (Channel& cur_chan : channels) if (cur_chan.name == chan_name) chan = &cur_chan; - if (chan == nullptr) throw "channel not declared"; - add_int32_in_network_order(chan->identifier, msg.get(), 0); + 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); @@ -307,41 +338,17 @@ void report_now(std::string chan_name, uint16_t var_count, int32_t *vars){ for (unsigned short i = 0; i < var_count; i++) add_int32_in_network_order(vars[i], msg.get(), 6 + i * 4); - ROS_INFO("sending to output channel %s", chan_name.c_str()); + ROS_INFO("sending to output channel %s", chan.name.c_str()); if (var_count == 0) ROS_INFO("no variables attached"); for (unsigned short i = 0; i < var_count; i++) - ROS_INFO("attached value %i to variable %s", vars[i], chan->vars[i].c_str()); + ROS_INFO("attached value %i to variable %s", vars[i], chan.vars[i].c_str()); send_bytes(socket_fd, msg.get(), 6 + 4 * var_count); acks_missing++; } -void init_topic_channel_mapping(const std::string topic, const std::string channel){ - Mapping map; - map.topic = topic; - map.channel_index = get_channel_index(channel); - mappings.push_back(map); -} - -void add_var_to_mapping(std::string topic, std::string channel, int byte_offset, - int32_t (*conv_to_TRON)(byte*, int*) = nullptr, - void (*conv_to_topic)(int32_t, byte*, int*) = nullptr) { - int index = -1; - for (int i = 0; i < mappings.size(); i++) { - Mapping& map = mappings[i]; - if (channels[map.channel_index].name == channel && map.topic == topic) index = i; - } - if (index < 0) throw "did not find mapping"; - - Mapping& map = mappings[index]; - map.byte_offset.push_back(byte_offset); - map.converters_to_TRON.push_back(conv_to_TRON); - map.converters_to_topics.push_back(conv_to_topic); -} - - // callback reports to TRON like defined in mappings template<class T> -void mapping_callback_to_TRON(const ros::MessageEvent<T>& event){ +void mappings_callback_to_TRON(const ros::MessageEvent<T>& event){ std::string topic = event.getConnectionHeader().at("topic"); byte *bytes = reinterpret_cast<byte*>(event.getMessage().get()); for (Mapping& mapping : mappings) { @@ -361,38 +368,7 @@ void mapping_callback_to_TRON(const ros::MessageEvent<T>& event){ vars[i] = mapping.converters_to_TRON[i](&bytes[next_pos], &next_pos); } } - report_now(channels[mapping.channel_index].name, var_count, vars); - } - } -} - -// callback publishing to topics like defined in mappings -template<class T> -void mapping_callback_to_topics(std::string channel, int32_t* vars, ros::Publisher& pub){ - for (Mapping& mapping : mappings) { - if (channels[mapping.channel_index].name == channel) { - boost::shared_ptr<T> shared_ptr = boost::make_shared<T>(); - byte* ptr = reinterpret_cast<byte*>(shared_ptr.get()); - int next_pos = 0; - for (int i = 0; i < channels[mapping.channel_index].vars.size(); i++){ - int32_t val = vars[i]; - next_pos += mapping.byte_offset[i]; - if (mapping.converters_to_topics[i] == nullptr) { - byte* val_bytes; - if (SYS_IS_BIG_ENDIAN){ - uint32_t switched_byte_order = htonl(*reinterpret_cast<uint32_t*>(&val)); - val = *reinterpret_cast<int32_t*>(&switched_byte_order); - } - val_bytes = reinterpret_cast<byte*>(&val); - ptr[next_pos++] = val_bytes[0]; - ptr[next_pos++] = val_bytes[1]; - ptr[next_pos++] = val_bytes[2]; - ptr[next_pos++] = val_bytes[3]; - } else { - mapping.converters_to_topics[i](val, &ptr[next_pos], &next_pos); - } - } - pub.publish(shared_ptr); + report_now(mapping.channel, var_count, vars); } } } @@ -406,21 +382,18 @@ void configuration_phase(ros::NodeHandle& nh){ // add callback for mapping (key is pair of topic and channel name) // for output: add subscribers to output_subscribers - send_channel_decl_msg(true, "ausloesen"); - init_topic_channel_mapping("/command", "ausloesen"); - add_var_to_channel("ausloesen", true, "zahl"); - add_var_to_mapping("/command", "ausloesen", 0); + Mapping map = Mapping("/command", "ausloesen", true); + map.add_var_to_mapping("zahl", 0); + map.input_callback = mapping_callback_to_topic<std_msgs::Int32>; + mappings.push_back(map); input_publishers.push_back(nh.advertise<std_msgs::Int32>("/command", 1)); - input_callbacks[std::pair<std::string, std::string>("/command", "ausloesen")] - = mapping_callback_to_topics<std_msgs::Int32>; // output channels: declare, init mapping, add vars to TRON channel and mapping // add subscriber to list - send_channel_decl_msg(false, "position"); - init_topic_channel_mapping("/position", "position"); - add_var_to_channel("position", false, "zahl"); - add_var_to_mapping("/position", "position", 0); + map = Mapping("/position", "position", false); + map.add_var_to_mapping("zahl", 0); + mappings.push_back(map); output_subscribers.push_back(nh.subscribe("/position", 1, - mapping_callback_to_TRON<std_msgs::Int32>)); + mappings_callback_to_TRON<std_msgs::Int32>)); // not obvious in documentation: local variables are not supported // add_var_to_channel(socketfd, "ausloesen", "lokal"); uint64_t microseconds = 1000000; // one second @@ -453,7 +426,7 @@ void process_TRONs_msgs(){ // bytes are channel identifier // find corresponding channel const Channel *chan = nullptr; - for (Channel& cur_chan : channels) if (cur_chan.identifier == next) chan = &cur_chan; + for (Mapping& map : mappings) if (map.channel.identifier == next) chan = &map.channel; /* note: this only happens if message number 12 in TRON User Manual is received, which should not be the case according to the documentation */ if (chan == nullptr) @@ -480,15 +453,12 @@ void process_TRONs_msgs(){ } for (Mapping& map : mappings) - if (channels[map.channel_index].name == chan->name) + if (map.channel.name == chan->name) for (ros::Publisher& pub : input_publishers){ if (pub.getTopic() == map.topic) { - if (input_callbacks.find( - std::pair<std::string, std::string>(map.topic, channels[map.channel_index].name)) - != input_callbacks.end()) - input_callbacks.at(std::pair<std::string, std::string>(map.topic, channels[map.channel_index].name)) - (chan->name, vals, pub); - else throw "did not find callback"; + if (map.input_callback != nullptr) + map.input_callback(map, vals); + else throw "no callback declared"; break; } }