Is there a way to have a precedence between two ROS nodes?
Hello, I'm asking you if there is a way to have a precedence between two ROS nodes. In particular, i have a ROS node that makes an output that is a text file with 60 data in it, and it recreates it every time because the data are changing. Then i have a node that has to analyze that text file. Basically, what i need is to make some changes to have a mechanism that stops the analyzer node when the writer node is running, then it has to send a signal to the analyzer node to make it able to run and analyze the text file. And then the writer node has to return let's say "in charge" to be able to re-write the text file again. So, in simple words, is a loop. Someone told me that a possible solution can be something like a "semaphore" topic in which the writer node writes, for example, a boolean value of 1 when is doing the opening, the writing and the closing of the text file, so the analyzer node knows that cannot do its elaboration, since the file is not ready yet. And, when the writer has finished and closed the text file, it has to be published a value 0 that permits the analysis by the analyzer node. I searched for the publishing of boolean values and i found a code that can be something like this:
ros::Publisher pub = n.advertise<std_msgs::Bool>("semaphore", 1000);
std_msgs::Bool state;
state.data = 1;
I don't know if i have only to use the publisher in the writer node and the subscriber in the analyzer node. Maybe i have to use both of them in the two nodes, something like: writer put a 1 in the topic semaphore so the analyzer knows that cannot access the text file, makes the text file and then put a 0 in the topic and subscribe to the topic waiting again a 1; the analyzer does something similar but in reverse. I put the two codes below, because i don't have any idea where to put the publisher and the the subscriber and how to make them working well. NOTE: a new text file is created almost every 10 seconds, since in the text file are written data coming from another ROS topic and the code in the writer has a mechanism to do this kind of elaboration. Thank you in advance!!! EDIT: I edited the codes with a topic based solution mentioned in my last comment.
Writer code:
#include "ros/ros.h"
#include "std_msgs/String.h"
#include "std_msgs/Bool.h"
#include "../include/heart_rate_monitor/wfdb.h"
#include <stdio.h>
#include <sstream>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <deque>
#include "heart_rate_monitor/analyse_heart_rate.h"
using namespace std;
static std::deque<std::string> queue_buffer;
static int entries_added_since_last_write = 0;
ros::Publisher pub;
void write_data_to_file()
{
// open file;
std::ofstream data_file("/home/marco/catkin_ws/src/heart_rate_monitor/my_data_file.txt");
if (data_file.is_open())
{
for (int i = 0; i < queue_buffer.size(); ++i)
{
data_file << queue_buffer[i] << std::endl;
}
}
else
{
std::cout << "Error - Cannot open file." << std::endl;
exit(1);
}
data_file.close();
std_msgs::Bool state;
state.data = 0;
pub.publish(state);
}
void process_message(const std_msgs::String::ConstPtr& string_msg)
{
std_msgs::Bool state;
state.data = 1;
pub.publish(state);
// if buffer has already 60 entries, throw away the oldest one
if (queue_buffer.size() == 60)
{
queue_buffer.pop_front();
}
// add the new data at the end
queue_buffer.push_back(string_msg->data);
// check if 10 elements have been added and write to file if so
entries_added_since_last_write++;
if (entries_added_since_last_write >= 10
&& queue_buffer.size() == 60)
{
// write data to file and reset counter
write_data_to_file();
entries_added_since_last_write = 0;
}
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "writer");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("/HeartRateInterval", 1000, process_message);
pub = n.advertise<std_msgs::Bool>("/semaphore", 1000);
ros::spin();
return 0;
}
Analyzer code:
#include "ros/ros.h"
#include "std_msgs/String.h"
#include "std_msgs/Bool.h"
#include "../include/heart_rate_monitor/wfdb.h"
#include <stdio.h>
#include <sstream>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <deque>
#include "heart_rate_monitor/analyse_heart_rate.h"
void orderCallback(const std_msgs::Bool::ConstPtr& msg)
{
if (msg->data == 0)
{
chdir("/home/marco/catkin_ws/src/heart_rate_monitor");
system("get_hrv -R my_data_file.txt >doc.txt");
}
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "analyzer");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("/semaphore", 1000, orderCallback);
ros::spin();
return 0;
}
Asked by Marcofon on 2016-11-25 04:52:13 UTC
Answers
Your approach is a bit strange. Are you aware that you can send a vectors in a ROS Message? Just send all of your values in a single message and do not take the detour over the filesystem.
Asked by NEngelhard on 2016-11-25 05:28:07 UTC
Comments
@NEngelhard thank you for your reply! I'm sorry but I don't understand what you are saying. If you are talking about the data written in the text file, I must send them in batch of 60 data at once.
Asked by Marcofon on 2016-11-25 05:53:00 UTC
Why don't you pack them in a single ROS-Message?
Asked by NEngelhard on 2016-11-25 07:07:45 UTC
@NEngelhard because i need to do step by step elaborations to 60 data at a time.
Asked by Marcofon on 2016-11-25 09:07:19 UTC
What do you mean by 'elaborations'? You want to process all 60 data points at a time, so just send all of them in a single message.
Asked by NEngelhard on 2016-11-25 11:10:01 UTC
@NEngelhard they are coming from a live stream of data, so I have to store 60 of them in an array and then they are written on the text file. The elaborations are some statistics methods used by the script launched by the analyzer node.
Asked by Marcofon on 2016-11-25 11:29:58 UTC
But why do you write them into a file?
Asked by NEngelhard on 2016-11-25 11:36:11 UTC
@Marcofon: what @NEngelhard is trying to say, is that you could make your first node store the 60 data points in an internal array (just a variable in the node), then construct a ROS msg with an array as a member, populate the array from the variable, then send the msg in one go to the other node.
Asked by gvdhoorn on 2016-11-25 11:47:23 UTC
That would seem to achieve what you want to do, without using any intermediate temporary files.
See the Array handling section on the wiki/msg page.
Asked by gvdhoorn on 2016-11-25 11:48:36 UTC
@gvdhoorn @NEngelhard thank you for your replies, but I need this structure and that text file because the script reads only text file. This structure is working. I need help to set a sort of precedence as I asked in my question. Thank you very much!
Asked by Marcofon on 2016-11-25 12:14:49 UTC
Then make your "processing node" receive the ROS msg (with the array), write it out to a file and start the script with the file.
Now you have two nodes (one collecting, one processing), and the second node only works when the first node hands it the data. A file is still used.
Asked by gvdhoorn on 2016-11-25 12:20:24 UTC
You cannot sequence node behaviour in time in an asynchronous pub-sub system, unless you use the dataflow between nodes to trigger things (ie: an additional topic). But if you're doing that, you might as well send the data along with the trigger ..
Asked by gvdhoorn on 2016-11-25 12:21:15 UTC
In fact the idea was using a topic, if you read my question this is exactly what I tried to do. Thank you.
Asked by Marcofon on 2016-11-25 12:39:18 UTC
@gvdhoorn can you help me to develop the solution using a topic? Because it's better for me, for other reasons of my project, to keep the codes of the two nodes as close as possible to those at this moment. So, if i can use the topic solution, it's way better! Thank you in advance!
Asked by Marcofon on 2016-11-28 03:51:43 UTC
Do you actually need two nodes? If not: one node, one callback that handles incoming samples, stores them in a vector. A timer calls another callback: as soon as len(vector) > 60
, take first 60 out of vector, write to file, call script, gather script output.
Asked by gvdhoorn on 2016-11-28 05:26:16 UTC
Or two node approach: first node: gather 60 samples (store in vector), as soon as len(vector) > 60
, take first 60, call service of second node (passing 60 samples along with request). Second node implements service that writes request to text file, calls script, gathers results, returns results.
Asked by gvdhoorn on 2016-11-28 05:27:29 UTC
Yes, @gvdhoorn, i need two nodes because it was imposed to me by my chief and it is very recommended for me to keep them separated. And, if possible, i ask you if you can help me achieving it with the topic solution, since it's difficult for me to modify those codes as you said. Thank you!!
Asked by Marcofon on 2016-11-28 06:02:56 UTC
Ok, two nodes:
- N1: subscribes to
semaphore
topic. Callback stores samples instd::vector
. Iflen(vector) > 60
, take first 60 samples, put in msg with array, publish msg (tosemaphore_batch
fi)
...
Asked by gvdhoorn on 2016-11-28 06:11:01 UTC
...
- N2: subscribes to
semaphore_batch
, callback writes data to file, invokes script
The service version is the same, but uses a ServiceServer
in N2, and a ServiceClient
in N1.
Asked by gvdhoorn on 2016-11-28 06:11:44 UTC
Ok @gvdhoorn. Thank you for your hints! Now i will try to work on this! I'll report here what i'll achieve! Thank you again!
Asked by Marcofon on 2016-11-28 06:28:16 UTC
@gvdhoorn @NEngelhard i will edit my codes in the question with a topic based solution. Thank you very much to both of you, you greatly helped me with my problem and to better understand how ROS works! Cheers!!!
Asked by Marcofon on 2016-12-01 08:47:10 UTC
Comments