(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

How to register and publish variables

Description: How to register and publish variables

Keywords: pal_statistics, logging

Tutorial Level: INTERMEDIATE

Next Tutorial: pal_statistics/Tutorials/Aggregate, store and visualise statistics

Registration C++

The Code

The following example shows how to register and publish a temperature reading.

   1   #include <pal_statistics/pal_statistics_macros.h>
   2   void do_monitor_temperature()
   3   {
   4     double temperature;
   5     pal_statistics::RegistrationsRAII registrations;
   6     REGISTER_VARIABLE("/statistics_topic", "motor temperature", &temperature, &registrations);
   7     while (ros::ok())
   8     {
   9       temperature = read_temperature();
  10       ros::Duration(0.1).sleep();
  11       PUBLISH_STATISTICS("/statistics_topic");
  12     }
  13   //Unregister variable
  14   UNREGISTER_VARIABLE("/statistics_topic", "motor temperature");
  15   }

The Code Explained

Now, let's break down the code piece by piece.

   5     pal_statistics::RegistrationsRAII registrations;

To log a variable, it must be registered with a name to a topic, the address of the variable must be provided.

When asked to publish all registered variables, their address will be accessed and and their values updated.

   6     REGISTER_VARIABLE("/statistics_topic", "motor temperature", &temperature, &registrations);
   7     while (ros::ok())
   8     {
   9       temperature = read_temperature();
  10       ros::Duration(0.1).sleep();
  11       PUBLISH_STATISTICS("/statistics_topic");

This part of the code modifies the variable we have registered, and publishes them periodically. If the topic name is changed, it will attempt to publish variables registered to the new topic name, which will not include "motor temperature"

  12     }

Unregister the variable, this is critical. If a variable is destroyed, but it has not been unregistered, it may be attempted to read, which in the worst case scenario will cause a segmentation fault.

Automatic Unregister C++

The Code

Following on the previous example, we'll see how to have automatic unregistration when going out of scope.

This is the recommended way of using pal_statistics, in the same way smart pointers are preferred over old C-style pointers.

   1   #include <pal_statistics/pal_statistics_macros.h>
   2   void do_monitor_temperature()
   3   {
   4     double temperature;
   5     RegistrationsRAII registrations;
   6     REGISTER_VARIABLE("/statistics_topic", "motor temperature", &temperature, &registrations);
   7     while (ros::ok())
   8     {
   9       temperature = read_temperature();
  10       ros::Duration(0.1).sleep();
  11       PUBLISH_STATISTICS("/statistics_topic");
  12     }
  13   //Unregister is automatic when registrations goes out of scope
  14   }

The Code Explained

Now, let's break down the code piece by piece.

   5     RegistrationsRAII registrations;
   6     REGISTER_VARIABLE("/statistics_topic", "motor temperature", &temperature, &registrations);

We declare an object of type RegistrationsRAII after the declaration of our variables.

This object when provided to the REGISTER_VARIABLE macro, will keep track of the registration done. When it is destroyed, it will unregister all variables associated to it.

By declaring it after our variables, we ensure that it will be destroyed before the variables.

Advanced Registration C++

Non double variables

Any variable that can be cast to a double is suitable for registration.

   1     unsigned int uint_variable = 0;
   2     bool bool_variable = false;
   3     RegistrationsRAII registrations;
   4     REGISTER_VARIABLE("/statistics_topic", "uint_variable", &uint_variable, &registrations);
   5     REGISTER_VARIABLE("/statistics_topic", "bool_variable", &bool_variable, &registrations);

Registering functions

You can also register a function to be called:

   1 std::vector<int> container;
   2 REGISTER_VARIABLE("/statistics_topic", "container_size",
   3                     boost::function<size_t()>(boost::bind(&std::vector<int>::size, &container)));

As well as a lambda expression:

   1 REGISTER_VARIABLE("/statistics_topic", "lambda_magic",
   2                     boost::function<double()>([]() { return lambda_magic(); }));

Real Time Usage

This framework has been designed for realtime operation.

The following actions are RT safe (no memory allocs, waits, or other blocking or undeterministic behavior).

  • Publishing (using PUBLISH_ASYNC_STATISTICS)

  • Enable a variable
  • Disable a variable

Everything else is not RT safe, but for completeness:

  • Register a variable
  • Unregister a variable
  • Start publish thread
  • Publish without using PUBLISH_ASYNC_STATISTICS

Real Time usage example

   1   #include <pal_statistics/pal_statistics_macros.h>
   2   void do_monitor_temperature()
   3   {
   4     // Initialization. Non RT Safe code
   5     double temperature;
   6     RegistrationsRAII registrations;
   7     REGISTER_VARIABLE("/statistics_topic", "motor temperature", &temperature, &registrations);
   8     START_PUBLISH_THREAD("/statistics_topic");
   9 
  10     // Main loop. RT Safe code only
  11     while (ros::ok())
  12     {
  13       temperature = read_temperature();
  14       PUBLISH_ASYNC_STATISTICS("/statistics_topic");
  15     }
  16   }

Each call to PUBLISH_ASYNC_STATISTICS reads the value of the registered variables and passes it to the publish thread without blocking, the publish thread which will then wake up and publish all the data that hasn't been published yet.

Enable/Disable variable

This is an advanced feature, allowing a soft unregistration in a RT-safe way. Look at the code documentation for more information.

Python API

The Python API is more limited due to language constraints, and since it doesn't have to deal with Real Time issues.

Registering variables

   1 from pal_statistics import StatisticsRegistry
   2 
   3 # Create Registry for a topic
   4 registry = StatisticsRegistry("/statistics_topic")
   5 var1 = 123
   6 registry.registerFunction("var1", (lambda: var1))

Notice that in Python, there's no registerVariable method, because python will copy the value of variables of simple types such as numbers. Therefore we have to use a lambda to read the value of the address at a later time.

It can still be used to register full functions that return some value:

   1 registerFunction("my_function", self.my_function)

Publishing registered variables

   1         registry.publish()

Unregistering registered variables

        registry.unregister("var1")

Also, when a registry is destroyed, the variables are automatically unregistered.

Wiki: pal_statistics/Tutorials/Registering and publishing variables (last edited 2020-04-20 21:20:17 by JohnStechschulte)