Search code examples
c++arraystextmessageros

I want to store in an array messages from a ROS topic for further elaboration


I'm sorry if this question is too simple for you, but i don't have good programming skills and ROS knowledge. I have a ROS topic in which are published some numbers that are heart beat intervals in seconds. I need to subscribe to that topic and do this kind of elaboration: The idea is to have a little array of ten numbers in which i can store continuously ten heart beat. Then i have a bigger array of 60 numbers that must go up by ten position in order to have at the bottom the newest ten values of the small array and it has to "throw away" the ten oldest values ( i did a bit of research and maybe i have to use a vector instead of an array because in C++ array are fixed as far as i read ). Then i have to print every time these 60 values in a text file (i mean in a loop, so the the text file will be continuously overwritten). Moreover, i see that ROS outputs the data from a topic in this manner: data: 0.987 with every data divided from the others by --- in a column. What i really want, because i need it for a script that reads text file in this manner, is a text file in which the values are in one column without spaces and other signs or words, like this:

0.404
0.952
0.956
0.940
0.960

I provide below the code for my node, in which, for now, i did only the subscribing part, since i have no idea on how to do the things that i have to do later. Thank you in advance for your help!!!

Code:

#include "ros/ros.h"
#include "std_msgs/String.h"
#include "../include/heart_rate_monitor/wfdb.h"
#include <stdio.h>
#include <sstream>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <vector>

int main(int argc, char **argv)
{

ros::init(argc, argv, "writer");


ros::NodeHandle n;


ros::Subscriber sub = n.subscribe("/HeartRateInterval", 1000);


ros::spin();

return 0;
}

NOTE: I didn't include the Float32/64 header because i publish the heart beats as a string. I don't know if this is of interest.

EDIT: I will include below the code of the publisher node which publish on the ROS topic the data.

#include "ros/ros.h"
#include "std_msgs/String.h"
#include "../include/heart_rate_monitor/wfdb.h"
#include <stdio.h>
#include <sstream>
#include <iostream>
#include <fstream>
#include <iomanip>

using namespace std;


int main(int argc, char **argv)
{

ros::init(argc, argv, "heart_rate_monitor");

ros::NodeHandle n;

ros::Publisher pub = n.advertise<std_msgs::String>("/HeartRateInterval", 1000);

ros::Rate loop_rate(1);

while (ros::ok())

{ 

ifstream inputFile("/home/marco/Scrivania/marks.txt");

string line;

while (getline(inputFile, line)) {


istringstream ss(line);

string heart;

ss >> heart;

std_msgs::String msg;

msg.data = ss.str();

pub.publish(msg);

ros::spinOnce();

loop_rate.sleep();

}

}

return 0;

}

Since what is published is the "variable" msg, i tried to replace in the code given as an answer the variable string_msg with msg, but nothing has changed. Thank you!


Solution

  • I'm not sure I understood exactly what you want but here is a brief example which might do what you need.

    I'm using here an std::deque to have a circular buffer of 60 values. What you are missing in your code is a callback function process_message which is called for the subscriber every time a new message arrives.

    I did not compile this code, so it may not compile right away but the basics are there.

    #include <ros/ros.h>
    #include <std_msgs/String.h>
    #include "../include/heart_rate_monitor/wfdb.h"
    #include <stdio.h>
    #include <sstream>
    #include <iostream>
    #include <fstream>
    #include <iomanip>
    #include <algorithm>
    #include <deque>
    
    
    static std::deque<std::string> queue_buffer;
    static int entries_added_since_last_write = 0;
    
    void write_data_to_file()
    {
      // open file
      std::ofstream data_file("my_data_file.txt");
      if (data_file.is_open())
      {
        for (int i = 0; i < queue_buffer.size(); ++i)
        {
          data_file << queue_buffer[i] << std::end;
        }
      }
      else
      {
        std::cout << "Error - Cannot open file." << std::endl;
        exit(1);
      }
      data_file.close();
    }
    
    void process_message(const std_msgs::String::ConstPtr& string_msg)
    {
      // 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);
      ros::spin();
      return 0;
    }