Defining messages
CONTENT

jrosclient keeps all standard ROS message definitions in id.jrosmessages Java package. It provides definition for some basic ROS messages from std_msgs and others.

In case some of standard ROS messages are missing or you want to use your own messages, jrosclient allows you to define them manually as described in this document.

Additionally there is msgmonster application which automates this process to some extend.

Built-in type mapping

jrosclient supports following mapping of ROS built-in types to Java types:

Message class requirements

Every message class should:

Example of defining new message

As an example lets define message class for geometry_msgs/PolygonStamped (it is already part of jrosclient).

Its ROS definition can be found in ROS_INSTALL_DIR/share/geometry_msgs/msg/PolygonStamped.msg and it looks like:

# This represents a Polygon with reference coordinate frame and timestamp
Header header
Polygon polygon

As we see it consist from two other messages:

Because Polygon type is not available before proceeding with PolygonStamped we need to define it first.

Polygon definition

Polygon definition can be found in ROS_INSTALL_DIR/geometry_msgs/msg/Polygon.msg and it consist from:

#A specification of a polygon where the first and last points are assumed to be connected
Point32[] points

Point32 type is available in jrosclient so we can define Polygon message type right away.

Before writing a message class make sure you have:

To calculate md5 sum for geometry_msgs/Polygon we can use following ROS command:

rosmsg md5 geometry_msgs/Polygon
cd60a26494a087f577976f0329fa120e

Now since we have all the information let's define a Java message class for geometry_msgs/Polygon:

/**
 * Example of custom message definition
 */
@MessageMetadata(
    type = PolygonMessage.NAME,
    md5sum = "cd60a26494a087f577976f0329fa120e")
class PolygonMessage implements Message {

    static final String NAME = "geometry_msgs/Polygon";

    @Streamed
    public Point32Message[] points = new Point32Message[0];

    public PolygonMessage withPoints(Point32Message[] points) {
        this.points = points;
        return this;
    }

}

PolygonStamped definition

As we saw before geometry_msgs/PolygonStamped message consist from:

Because now we have all these types available we are ready to define geometry_msgs/PolygonStamped itself.

As always first step is to calculate the md5 sum or the ROS message definition:

rosmsg md5 geometry_msgs/PolygonStamped
c6be8f7dc3bee7fe9e8d296070f53340

Now Java message class definition for geometry_msgs/PolygonStamped:

/**
 * Example of custom message definition
 */
@MessageMetadata(
    type = PolygonStampedMessage.NAME,
    md5sum = "c6be8f7dc3bee7fe9e8d296070f53340")
class PolygonStampedMessage implements Message {

    static final String NAME = "geometry_msgs/PolygonStamped";

    @Streamed
    public HeaderMessage header = new HeaderMessage();
    
    @Streamed
    public PolygonMessage polygon = new PolygonMessage();

    public PolygonStampedMessage withPolygon(PolygonMessage polygon) {
        this.polygon = polygon;
        return this;
    }

    public PolygonStampedMessage withHeader(HeaderMessage header) {
        this.header = header;
        return this;
    }

}

Using new message

Since we just defined geometry_msgs/PolygonStamped message lets create a topic for it and start to publish its objects from Java using jrosclient.

Application code:

import id.jrosclient.JRosClient;
import id.jrosclient.TopicSubmissionPublisher;
import id.jrosmessages.Message;
import id.jrosmessages.MessageMetadata;
import id.jrosmessages.geometry_msgs.Point32Message;
import id.jrosmessages.primitives.Time;
import id.jrosmessages.std_msgs.HeaderMessage;
import id.kineticstreamer.annotations.Streamed;
import id.xfunction.cli.CommandLineInterface;

/**
 * Demonstrates how to define custom messages using PolygonStampedMessage
 * as an example.
 */
public class PolygonApp {

    public static void main(String[] args) throws Exception {
        var cli = new CommandLineInterface();
        String topic = "/PolygonExample";
        try (var client = new JRosClient("http://localhost:11311/")) {
            var publisher = new TopicSubmissionPublisher<>(PolygonStampedMessage.class, topic);
            client.publish(publisher);
            cli.print("Press any key to stop publishing...");
            while (!cli.wasKeyPressed()) {
                PolygonStampedMessage polygon = new PolygonStampedMessage()
                        .withHeader(new HeaderMessage()
                                .withFrameId("/map")
                                .withStamp(Time.now()))
                        .withPolygon(new PolygonMessage()
                                .withPoints(new Point32Message[]{
                                        new Point32Message(2F, 2F, 0F),
                                        new Point32Message(1F, 2F, 3F),
                                        new Point32Message(0F, 0F, 0F)}));
                publisher.submit(polygon);
                cli.print("Published");
                Thread.sleep(1000);
            }
        }
    }
}


/**
 * Example of custom message definition
 */
@MessageMetadata(
    type = PolygonMessage.NAME,
    md5sum = "cd60a26494a087f577976f0329fa120e")
class PolygonMessage implements Message {

    static final String NAME = "geometry_msgs/Polygon";

    @Streamed
    public Point32Message[] points = new Point32Message[0];

    public PolygonMessage withPoints(Point32Message[] points) {
        this.points = points;
        return this;
    }

}

/**
 * Example of custom message definition
 */
@MessageMetadata(
    type = PolygonStampedMessage.NAME,
    md5sum = "c6be8f7dc3bee7fe9e8d296070f53340")
class PolygonStampedMessage implements Message {

    static final String NAME = "geometry_msgs/PolygonStamped";

    @Streamed
    public HeaderMessage header = new HeaderMessage();
    
    @Streamed
    public PolygonMessage polygon = new PolygonMessage();

    public PolygonStampedMessage withPolygon(PolygonMessage polygon) {
        this.polygon = polygon;
        return this;
    }

    public PolygonStampedMessage withHeader(HeaderMessage header) {
        this.header = header;
        return this;
    }

}

This sample as well as how to run it can be found here.

Once you start this application you should see its output like:

Published
Published
Published
Published
Published
Published
Published
Published
Published
...

Now start RViz and go to Displays -> Add -> By Topic -> /BasicShapesExample. This should subscribe you for the topic and you should see the polygon: