Skip to content
Snippets Groups Projects
Commit 9fdff7ab authored by Christoph Schröter's avatar Christoph Schröter
Browse files

No commit message

No commit message
parent e08abb24
No related branches found
No related tags found
No related merge requests found
......@@ -125,7 +125,7 @@ catkin_package(
## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
include
${catkin_INCLUDE_DIRS}
)
......@@ -145,7 +145,7 @@ include_directories(
# add_executable(${PROJECT_NAME}_node src/actionlib_example_node.cpp)
add_executable(${PROJECT_NAME}_garage_server src/garage_server.cpp)
add_executable(${PROJECT_NAME}_garage_client src/garage_client.cpp)
add_executable(${PROJECT_NAME}_tron_adapter src/tron_adapter.cpp)
add_executable(${PROJECT_NAME}_garage_adapter src/garage_adapter.cpp)
## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
......@@ -157,6 +157,8 @@ add_executable(${PROJECT_NAME}_tron_adapter src/tron_adapter.cpp)
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_library(tron_adapter src/tron_adapter.cpp)
## Specify libraries to link a library or executable target against
target_link_libraries(
${PROJECT_NAME}_garage_server ${catkin_LIBRARIES}
......@@ -165,7 +167,7 @@ target_link_libraries(
${PROJECT_NAME}_garage_client ${catkin_LIBRARIES}
)
target_link_libraries(
${PROJECT_NAME}_tron_adapter ${catkin_LIBRARIES}
${PROJECT_NAME}_garage_adapter ${catkin_LIBRARIES} tron_adapter
)
#############
......
#ifndef TRON_ADAPTER
#define TRON_ADAPTER
#include <ros/ros.h>
#include <arpa/inet.h>
typedef uint8_t byte;
// some networking helper functions ---------------------------------------------------
/**
* logs bytes
* @param buf start of byte array
* @param buf_length how many bytes to log
* @param send true if message gets send, false if received
*/
inline void byte_info(const byte* buf, int buf_length, bool send=true);
/**
* reads bytes from file descriptor
* @param fd file descriptor
* @param count how many bytes to read
* @return byte array read
*/
std::unique_ptr<byte[]> get_bytes_socket(int fd, int count);
/**
* converts bytes to host order and casts to int32
* @param buf start of 4 bytes to convert
*/
inline int32_t network_bytes_to_int_32(byte *buf);
/**
* converts bytes to host order and casts to uint16
* @param buf start of 2 bytes to convert
*/
inline uint16_t network_bytes_to_uint_16(byte *buf);
/**
* reads 4 bytes from socket and returns them as an int (converted to host order before)
* @param fd file descriptor
*/
int32_t get_int_socket(int fd);
/**
* converts int32 to network order and sets it converted to network order into byte array
* @param num number to convert
* @param buf start adress of array to add to
* @param index wanted starting index of bytes in array
*/
void add_int32_in_network_order(int32_t num, byte *buf, int index);
/**
* wraps systems byte-sending for automated logging
* @param fd file descriptor
* @param buf start adress of byte array to send
* @param length how many bytes to send
*/
inline void send_bytes(int fd, const byte *buf, int length);
/**
* reads 4 bytes instantly, throws if not successfull
* @param fd file descriptor
* @param buf start adress to read into
*/
inline bool read_4_bytes_nonblock(int fd, byte *buf);
/**
* constructs socket connected to given parameters
* @param IP IPv4 adress to connect to
* @param port port to connect to
*/
int create_connected_socket(std::string IP, uint16_t port);
// ----------------------------------------------------------------------------------------
struct Channel {
/**
* represents a channel
* @param name name of channel in Uppaal
* @param identifier identifier for TRON
* @param is_input channel used as input or output
* @param vars names of variables appended to this channel
*/
std::string name;
int32_t identifier;
bool is_input;
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){};
};
struct Mapping {
/**
* represents a Mapping of one topic and one channel
* @param topic name of topic in ROS
* @param channel associated channel
* @param input_callback callback used if channel is input
* @param byte_offset offsets of bytes to map (from end of last field)
* @param converters_to_TRON converters if variable is not int32, nullptr if not used
* @param converters_to_topics converters if variable is not int32, nullptr if not used
*/
std::string topic; // ROS topic name
Channel channel = Channel("", 0, true); // TRON channel
// Callback used if TRON sends some message
// output callbacks must be specified when subscribing to topics
void(*input_callback)(Mapping& map, int32_t*) = nullptr;
// note: since ROS messages can be complex, using byte positions might be cumbersome to use
// it is easy to implement custom callbacks using ROS message types
// (with publish_to_topic for inputs and report_now for outputs)
// offset in byte for next value (from last value)
std::vector<int> byte_offset;
// custom conversion function used if data is not int32 and therefore
// need to be converted to an int32 first
// needs to make sure count of bytes used gets added to *int (parameter)
// to indicate beginning of next field
std::vector<int32_t (*)(byte*, int*)> converters_to_TRON;
// similarly the conversion to a topic field takes the value from TRON,
// a pointer to the buffer at which to add the bytes and
// a pointer to an integer to increment by the amount of bytes added
std::vector<void (*)(int32_t, byte*, int*)> converters_to_topics;
/* 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 = 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
and mapped to the first variable of the corresponding channel.
The conversion function also adds 8 to the current position,
thus the next field starts 8 bytes + 2 bytes for offset after the last
known position, which was 4.
Because the second converter points to nullptr, it is considered an int32,
which is mapped to the second variable of the channel.
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.
*/
};
// sockets file descriptor, mappings, publishers and subscribers kept in struct
struct TRON_Adapter {
// TRON message codes
static const byte GET_ERROR_MSG = 127;
static const byte DECL_CHAN_INPUT = 1;
static const byte DECL_CHAN_OUTPUT = 2;
static const byte ADD_VAR_TO_INPUT = 3;
static const byte ADD_VAR_TO_OUTPUT = 4;
static const byte SET_TIME_UNIT = 5;
static const byte SET_TIMEOUT = 6;
static const byte REQUEST_START = 64;
static const byte ANSWER_START = 0;
static const int32_t ACK_SINGLE = 1 << 31; // 32 Bit int with most significant bit set to 1
int socket_fd = -1;
std::list<Mapping> mappings;
std::vector<ros::Publisher> input_publishers;
std::vector<ros::Subscriber> output_subscribers;
// keep track of acknowledgements that are missing
/* note: since communication is asynchronous this value can only be
compared reliably with 0 after testing is terminated */
/* note: TRON only acknowledges if virtual clock is used, which it is not */
int acks_missing = 0;
/**
* creates connected instance of TRON_Adapter
* @param IP IPv4 address to connect to
* @param PORT port to connect to
*/
TRON_Adapter(std::string IP, uint16_t PORT);
/**
* used only to be able to declare adapters as global variables without initialising
*/
TRON_Adapter() = default;
/**
* declares used channel to TRON and returns Mapping to add variables;
* when done with adding variables, add the Mapping instance to mappings list
* @param topic name of the topic
* @param channel name of channel in Uppaal model
* @param channelIsInput is channel used as input or output
*/
Mapping createMapping(std::string topic, std::string channel, bool channelIsInput);
/**
* declares variable to mapping calling add_var_to_channel and adds given values to corresponding lists
* @param map mapping to append variable to
* @param name_tron name of variable as in Uppaal model
* @param byte_offset offset from ending of last field
* @param conv_to_TRON converter from byte* to int32_t in order to send to TRON, needs to increase given int* by count of bytes used
* @param conv_to_topic conv_to_TRON reversed for messaging topic
*/
void add_var_to_mapping(Mapping& map, std::string name_tron, int byte_offset = 0,
int32_t (*conv_to_TRON)(byte*, int*) = nullptr,
void (*conv_to_topic)(int32_t, byte*, int*) = nullptr);
/**
* sends time variables to TRON
* @param microseconds how many microseconds represent one time unit
* @param timeout how many time units pass until timeout
*/
void set_time_unit_and_timeout(uint64_t microseconds, int32_t timeout);
/**
* sends REQUEST_START to TRON ans waits until receiving ANSWER_START (with timeout)
*/
void request_start();
/**
* report to TRON
* @param chan channel to activate
* @param var_count how many variables to attach
* @param vars pointer to int32 array with variables to attach
*/
void report_now(Channel& chan, uint16_t var_count = 0, int32_t *vars = nullptr);
void report_now(std::string chan, uint16_t var_count = 0, int32_t *vars = nullptr);
/**
* starts callback for every message received from TRON
*/
void process_TRONs_msgs();
/**
* gets and logs error message from TRON, then throws since proceeding would lead to more errors
* @param errorcode code received from TRON
*/
void get_error_msg(int32_t errorcode);
/**
* declares channel to TRON and constructs corresponding instance
* @param is_input is channel used as input or output
* @param name name of channel in Uppaal model
*/
std::unique_ptr<Channel> send_channel_decl_msg(bool is_input, std::string name);
/**
* declares variable being attached to channel for TRON
* @param chan channel to associated with variable
* @param is_input is channel used as input or output
* @param var name of int variable in Uppaal model
*/
void add_var_to_channel(Channel& chan, bool is_input, std::string var);
/**
* searches publisher list for given topic and publishes to it
* @param topic name of the topic to publish to
* @param ptr pointer to message
*/
template <class T>
void publish_to_topic(std::string topic, boost::shared_ptr<T> ptr) {
for (ros::Publisher& pub : input_publishers)
if (pub.getTopic() == topic) {
pub.publish(ptr);
return;
}
throw "did not find publisher for topic";
}
/**
* callback reports to topic like defined in given byte-mapping
* note: needs to be specified for each mapping
* @param map mapping associated with this callback
* @param vars variables received from TRON
*/
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 (htonl(47) == 47){
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);
}
}
publish_to_topic<T>(map.topic, shared_ptr);
}
/**
* callback reports to TRON like defined in byte-mappings
* note: this callback reports to all channels associated with calling topic
* @param event message event received from topic
*/
template<class T>
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) {
if (mapping.topic.c_str() == topic && !mapping.channel.is_input) {
int var_count = mapping.byte_offset.size();
int32_t vars[var_count * 4];
int next_pos = 0;
for (int i = 0; i < var_count; i++) {
next_pos += mapping.byte_offset[i];
if (mapping.converters_to_TRON[i] == nullptr) {
if (htonl(47) == 47)
vars[i] = network_bytes_to_int_32(&bytes[next_pos]);
else vars[i] = *reinterpret_cast<int32_t*>(&bytes[next_pos]);
next_pos += 4;
continue;
} else {
vars[i] = mapping.converters_to_TRON[i](&bytes[next_pos], &next_pos);
}
}
report_now(mapping.channel, var_count, vars);
}
}
}
};
#endif //TRON_ADAPTER
<launch>
<node name="tron_adapter" pkg="actionlib_example" type="actionlib_example_tron_adapter" respawn="false" output="screen"/>
<node name="tron_adapter" pkg="actionlib_example" type="actionlib_example_garage_adapter" respawn="false" output="screen"/>
<node name="actionlib_server" pkg="actionlib_example" type="actionlib_example_garage_server" respawn="false" output="screen"/>
</launch>
#include <ros/ros.h>
#include <actionlib_example/TriggerAction.h>
#include "tron_adapter.h"
TRON_Adapter adapter;
void send_goal (Mapping& map, int32_t* val, bool open){
auto shared_ptr = boost::make_shared<actionlib_example::TriggerActionGoal>();
shared_ptr->goal.open = open;
adapter.publish_to_topic<actionlib_example::TriggerActionGoal>(map.topic, shared_ptr);
}
void send_goal_open(Mapping& map, int32_t* val) {send_goal(map, val, true);}
void send_goal_close(Mapping& map, int32_t* val) {send_goal(map, val, false);}
void feedback_callback(const boost::shared_ptr<actionlib_example::TriggerActionFeedback> ptr){
for (Mapping& map : adapter.mappings) {
if (map.channel.name == "position" && map.channel.is_input == false)
adapter.report_now(map.channel, 1, &ptr->feedback.position);
}
}
void result_callback(const boost::shared_ptr<actionlib_example::TriggerActionResult> ptr){
for (Mapping& map : adapter.mappings) {
if (map.channel.name == "fertig" && map.channel.is_input == false)
adapter.report_now(map.channel, 1, &ptr->result.position);
}
}
void configuration_phase(ros::NodeHandle& nh){
/* note: for configuration phase maximum message length is 256 Bytes,
therefore heap allocation can be avoided most of the time in called functions */
// note: since we are not an actual client (or server)
// we need to use the high level packet::*Action* messages.
// custom callbacks are implemented in order to not worry about header size
Mapping map = adapter.createMapping("/garage_server/goal", "oeffnen", true);
map.input_callback = send_goal_open;
adapter.mappings.push_back(map);
map = adapter.createMapping("/garage_server/goal", "schliessen", true);
map.input_callback = send_goal_close;
adapter.mappings.push_back(map);
adapter.input_publishers.push_back(nh.advertise<actionlib_example::TriggerActionGoal>("/garage_server/goal", 1));
map = adapter.createMapping("/garage_server/result", "fertig", false);
adapter.add_var_to_mapping(map, "akt_position");
adapter.mappings.push_back(map);
adapter.output_subscribers.push_back(nh.subscribe("/garage_server/result", 100, result_callback));
map = adapter.createMapping("/garage_server/feedback", "position", false);
adapter.add_var_to_mapping(map, "akt_position");
adapter.mappings.push_back(map);
adapter.output_subscribers.push_back(nh.subscribe("/garage_server/feedback", 100,
feedback_callback));
// not obvious in documentation: local variables are not supported
uint64_t microseconds = 1000000; // one second
// documentation states 2 signed integers are used for some reason
adapter.set_time_unit_and_timeout(microseconds, 300);
// wait till subscribers initialized
for (ros::Publisher& pub : adapter.input_publishers) {
while (pub.getNumSubscribers() == 0) { ros::Duration(1).sleep(); };
}
}
int main(int argc, char**argv){
ros::init(argc, argv, "TRON dapter");
ros::NodeHandle nh;
try {
const std::string IP = "127.0.0.1";
const uint16_t PORT = 8080;
adapter = TRON_Adapter(IP, PORT);
configuration_phase(nh);
adapter.request_start();
// testing phase loop
ros::Rate test_phase_freq(60);
while (ros::ok()) {
ros::spinOnce();
adapter.process_TRONs_msgs();
test_phase_freq.sleep();
}
} catch (const char* err){
ROS_FATAL("shutting down: %s", err);
ros::shutdown();
} catch (...){
ROS_FATAL("shutting down");
ros::shutdown();
}
}
......@@ -3,22 +3,18 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <std_msgs/Int32.h>
#include <boost/make_shared.hpp>
#include <memory>
#include <actionlib_example/TriggerActionGoal.h>
#include <actionlib_example/TriggerActionFeedback.h>
#include <actionlib_example/TriggerAction.h>
const std::string IP = "127.0.0.1";
const uint16_t PORT = 8080;
#include <algorithm> // for std::find
#include <fcntl.h>
#include "tron_adapter.h"
typedef uint8_t byte;
// some helper functions -----------------------------------------------
// log bytes sent/received
inline void byte_info(const byte* buf, int buf_length, bool send=true){
// logs bytes
inline void byte_info(const byte* buf, int buf_length, bool send){
std::stringstream strstr;
strstr << (send ? "sending" : "received") << " bytes:";
for (int i = 0; i < buf_length; i++) strstr << " " << (int)buf[i];
......@@ -75,7 +71,7 @@ void add_int32_in_network_order(int32_t num, byte *buf, int index){
inline void send_bytes(int fd, const byte *buf, int length){
byte_info(buf, length);
int ret = write(fd, (void*) buf, length);
if (ret < 0) throw "writing failed";
if (ret < 0) throw "sending failed";
}
// returns false if nothing more to read and true if 4 bytes are read successfully
......@@ -113,144 +109,36 @@ int create_connected_socket(std::string IP, uint16_t port){
return socketfd;
}
// consts for TRON ----------------------------------------------------------
const byte GET_ERROR_MSG = 127;
const byte DECL_CHAN_INPUT = 1;
const byte DECL_CHAN_OUTPUT = 2;
const byte ADD_VAR_TO_INPUT = 3;
const byte ADD_VAR_TO_OUTPUT = 4;
const byte SET_TIME_UNIT = 5;
const byte SET_TIMEOUT = 6;
const byte REQUEST_START = 64;
const byte ANSWER_START = 0;
const int32_t ACK_SINGLE = 1 << 31; // 32 Bit int with most significant bit set to 1
// global vars -------------------------------------------------------------
// ROS uses little endian for its messages
const bool SYS_IS_BIG_ENDIAN = htonl(47) == 47;
int socket_fd;
struct Channel {
std::string name;
int32_t identifier;
bool is_input;
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){};
};
// 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
// offset in byte for next value (from last value)
std::vector<int> byte_offset;
// custom conversion function used if data is not int32 and therefore
// need to be converted to an int32 first
// nullptr if not used
// needs to make sure count of bytes used gets added to *int (parameter)
// to indicate beginning of next field
std::vector<int32_t (*)(byte*, int*)> converters_to_TRON;
// similarly the conversion to a topic field takes the value from TRON,
// a pointer to the buffer at which to add the bytes and
// a pointer to an integer to increment by the amount of bytes added
std::vector<void (*)(int32_t, byte*, int*)> converters_to_topics;
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 = 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
and mapped to the first variable of the corresponding channel.
The conversion function also adds 8 to the current position,
thus the next field starts 8 bytes + 2 bytes for offset after the last
known position, which was 4.
Because the second converter points to nullptr, it is considered an int32,
which is mapped to the second variable of the channel.
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.
*/
// 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;
template <class T>
void publish_to_topic(std::string topic, boost::shared_ptr<T> ptr) {
for (ros::Publisher& pub : input_publishers)
if (pub.getTopic() == topic) {
pub.publish(ptr);
return;
}
throw "did not find publisher for topic";
}
// 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);
}
}
publish_to_topic<T>(map.topic, shared_ptr);
TRON_Adapter::TRON_Adapter(std::string IP, uint16_t PORT) {
socket_fd = create_connected_socket(IP, PORT);
}
// subscribers already have callbacks
std::vector<ros::Subscriber> output_subscribers;
Mapping TRON_Adapter::createMapping(std::string topic, std::string channel, bool channelIsInput){
Mapping map;
map.topic = topic;
map.channel = *send_channel_decl_msg(channelIsInput, channel).get();
return map;
}
// keep track of acknowledgements that are missing
/* note: since communication is asynchronous this value can only be
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;
void TRON_Adapter::add_var_to_mapping(Mapping& map, std::string name_tron, int byte_offset,
int32_t (*conv_to_TRON)(byte*, int*),
void (*conv_to_topic)(int32_t, byte*, int*)){
add_var_to_channel(map.channel, map.channel.is_input, name_tron);
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);
}
void get_error_msg(int32_t errorcode) {
void TRON_Adapter::get_error_msg(int32_t errorcode) {
ROS_WARN("got error, trying to get corresponding message");
byte get_err_msg_msg[5];
get_err_msg_msg[0] = GET_ERROR_MSG; // get error msg code
get_err_msg_msg[0] = GET_ERROR_MSG;
add_int32_in_network_order(errorcode, get_err_msg_msg, 1);
send_bytes(socket_fd, get_err_msg_msg, 5); // connection is closed after this write?
send_bytes(socket_fd, get_err_msg_msg, 5);
byte err_msg_length = get_bytes_socket(socket_fd, 1)[0];
auto err_msg = get_bytes_socket(socket_fd, err_msg_length);
std::string msg_str = std::string(reinterpret_cast<char*>(err_msg.get()), (size_t) err_msg_length);
......@@ -258,7 +146,16 @@ void get_error_msg(int32_t errorcode) {
throw "got error from TRON";
}
void add_var_to_channel(Channel& chan, bool is_input, std::string var) {
void TRON_Adapter::add_var_to_channel(Channel& chan, bool is_input, std::string var) {
bool var_already_declared = false;
for (Mapping& map : mappings)
if (chan.name == map.channel.name)
if (std::find(map.channel.vars.begin(), map.channel.vars.end(), var) != map.channel.vars.end()) var_already_declared = true;
if (var_already_declared) {
ROS_INFO("variable %s was already declared to channel %s", var.c_str(), chan.name.c_str());
chan.vars.push_back(var);
return;
}
byte msg[6 + var.length()];
msg[0] = is_input ? ADD_VAR_TO_INPUT : ADD_VAR_TO_OUTPUT;
add_int32_in_network_order(chan.identifier, msg, 1);
......@@ -272,7 +169,7 @@ void add_var_to_channel(Channel& chan, bool is_input, std::string var) {
chan.vars.push_back(var);
}
std::unique_ptr<Channel> send_channel_decl_msg(bool is_input, std::string name) {
std::unique_ptr<Channel> TRON_Adapter::send_channel_decl_msg(bool is_input, std::string name) {
// prepare packet
size_t msg_length = 2 + name.length();
byte msg[msg_length];
......@@ -282,8 +179,7 @@ std::unique_ptr<Channel> send_channel_decl_msg(bool is_input, std::string name)
// send packet
ROS_INFO("declaring channel %s as %s", name.c_str(), (is_input ? "input" : "output"));
byte_info(msg, msg_length);
write(socket_fd, (void*) msg, msg_length);
send_bytes(socket_fd, msg, msg_length);
// get answer from TRON
int32_t channel_identifier = get_int_socket(socket_fd);
......@@ -296,7 +192,7 @@ std::unique_ptr<Channel> send_channel_decl_msg(bool is_input, std::string name)
return std::make_unique<Channel>(name, channel_identifier, is_input);
}
void set_time_unit_and_timeout(uint64_t microseconds, int32_t timeout){
void TRON_Adapter::set_time_unit_and_timeout(uint64_t microseconds, int32_t timeout){
byte msg[9];
msg[0] = SET_TIME_UNIT;
byte *microseconds_bytes = reinterpret_cast<byte*>(&microseconds);
......@@ -322,7 +218,7 @@ void set_time_unit_and_timeout(uint64_t microseconds, int32_t timeout){
ROS_INFO("success: set timeout");
}
void request_start() {
void TRON_Adapter::request_start() {
/* documentation confuses codes for start and getErrorMessage, actually used:
64 is start
127 is gerErrorMessage */
......@@ -334,7 +230,7 @@ void request_start() {
ROS_INFO("success: starting test phase");
}
void report_now(Channel& chan, uint16_t var_count, int32_t *vars){
void TRON_Adapter::report_now(Channel& chan, uint16_t var_count, int32_t *vars){
std::unique_ptr<byte[]> msg = std::make_unique<byte[]>(6 + 4 * var_count);
add_int32_in_network_order(chan.identifier, msg.get(), 0);
......@@ -355,93 +251,16 @@ void report_now(Channel& chan, uint16_t var_count, int32_t *vars){
acks_missing++;
}
// callback reports to TRON like defined in mappings
template<class T>
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) {
if (mapping.topic.c_str() == topic && !mapping.channel.is_input) {
int var_count = mapping.byte_offset.size();
int32_t vars[var_count * 4];
int next_pos = 0;
for (int i = 0; i < var_count; i++) {
next_pos += mapping.byte_offset[i];
if (mapping.converters_to_TRON[i] == nullptr) {
if (SYS_IS_BIG_ENDIAN)
vars[i] = network_bytes_to_int_32(&bytes[next_pos]);
else vars[i] = *reinterpret_cast<int32_t*>(&bytes[next_pos]);
next_pos += 4;
continue;
} else {
vars[i] = mapping.converters_to_TRON[i](&bytes[next_pos], &next_pos);
}
}
report_now(mapping.channel, var_count, vars);
}
}
}
void send_goal (Mapping& map, int32_t* val, bool open){
auto shared_ptr = boost::make_shared<actionlib_example::TriggerActionGoal>();
shared_ptr->goal.open = open;
publish_to_topic<actionlib_example::TriggerActionGoal>(map.topic, shared_ptr);
}
void send_goal_open(Mapping& map, int32_t* val) {send_goal(map, val, true);}
void send_goal_close(Mapping& map, int32_t* val) {send_goal(map, val, false);}
void feedback_callback(const boost::shared_ptr<actionlib_example::TriggerActionFeedback> ptr){
for (Mapping& map : mappings) {
if (map.channel.name == "position" && map.channel.is_input == false)
report_now(map.channel, 1, &ptr->feedback.position);
}
}
void result_callback(const boost::shared_ptr<actionlib_example::TriggerActionResult> ptr){
for (Mapping& map : mappings) {
if (map.channel.name == "fertig" && map.channel.is_input == false)
report_now(map.channel, 1, &ptr->result.position);
}
}
void configuration_phase(ros::NodeHandle& nh){
/* note: for configuration phase maximum message length is 256 Bytes,
therefore heap allocation can be avoided most of the time in called functions */
// note: since we are not an actual client (or server)
// we need to use the high level packet::*Action* messages.
// custom callbacks are implemented in order to not worry about header size
Mapping map = Mapping("/garage_server/goal", "oeffnen", true);
map.input_callback = send_goal_open;
mappings.push_back(map);
map = Mapping("/garage_server/goal", "schliessen", true);
map.input_callback = send_goal_close;
mappings.push_back(map);
input_publishers.push_back(nh.advertise<actionlib_example::TriggerActionGoal>("/garage_server/goal", 100));
map = Mapping("/garage_server/result", "fertig", false);
map.add_var_to_mapping("akt_position", 0);
mappings.push_back(map);
output_subscribers.push_back(nh.subscribe("/garage_server/result", 100, result_callback));
map = Mapping("/garage_server/feedback", "position", false);
map.add_var_to_mapping("akt_position", 0);
mappings.push_back(map);
output_subscribers.push_back(nh.subscribe("/garage_server/feedback", 100,
feedback_callback));
// not obvious in documentation: local variables are not supported
// add_var_to_channel(socketfd, "ausloesen", "lokal");
uint64_t microseconds = 1000000; // one second
// documentation states 2 signed integers are used for some reason
set_time_unit_and_timeout(microseconds, 300);
// wait till subscribers initialized
for (ros::Publisher& pub : input_publishers) {
while (pub.getNumSubscribers() == 0) { ros::Duration(1).sleep(); };
void TRON_Adapter::report_now(std::string chan, uint16_t var_count, int32_t *vars){
for (Mapping& map : mappings)
if (map.channel.name == chan && !map.channel.is_input) {
report_now(map.channel, var_count, vars);
return;
}
throw "could not report to channel";
}
void process_TRONs_msgs(){
void TRON_Adapter::process_TRONs_msgs(){
/* note: TRONs communication after start is not guaranteed to be synchronous,
thus incoming messages must be checked for their content */
while (true){
......@@ -488,16 +307,13 @@ void process_TRONs_msgs(){
}
for (Mapping& map : mappings)
if (map.channel.name == chan->name && map.channel.is_input)
for (ros::Publisher& pub : input_publishers){
if (pub.getTopic() == map.topic) {
if (map.channel.name == chan->name && map.channel.is_input) {
if (map.input_callback != nullptr)
map.input_callback(map, vals);
else throw "no callback declared";
break;
}
}
// send acknowledgement
add_int32_in_network_order(ACK_SINGLE, bytes, 0);
ROS_INFO("sending acknowledgement");
......@@ -505,28 +321,4 @@ void process_TRONs_msgs(){
}
}
int main(int argc, char**argv){
ros::init(argc, argv, "TRON dapter");
ros::NodeHandle nh;
try {
socket_fd = create_connected_socket(IP, PORT);
configuration_phase(nh);
request_start();
// testing phase loop
ros::Rate test_phase_freq(1);
ros::AsyncSpinner spinner(4);
spinner.start();
while (ros::ok()) {
ros::spinOnce();
process_TRONs_msgs();
test_phase_freq.sleep();
}
} catch (const char* err){
ROS_FATAL("shutting down: %s", err);
ros::shutdown();
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment