fleetlessfleetlessdocs
Reference/fleetless.yaml/Messages & Parameters

✉️ Messages & Parameters

Actions, services and publishers all send a message to the robot, and all three describe it the same way: as a template with holes.

yaml
    message:
      linear:
        x: ${linear_speed}
        y: 0.0
        z: 0.0
      angular:
        z: ${angular_speed}
    parameters:
      linear_speed:
        type: float64
        min_value: -0.5
        max_value: 0.5
        description: Forward speed in metres per second.
      angular_speed:
        type: float64
        min_value: -0.25
        max_value: 0.25
field type bounds absent means
type one of fifteen ROS primitives — required
default number, text or boolean must match type and satisfy every constraint below the parameter is required
min_value number numeric types only, not above max_value no lower bound
max_value number numeric types only no upper bound
enum list of numbers or text at least one entry, integer and string types only any value the other constraints allow
regex text string types only, at least one character no pattern
description text 1 to 500 characters the bounds are all a caller has to go on

message is the complete message as it will be sent. Fixed values stand as literals, variable ones as ${name}. So the document shows both what reaches the robot and what a caller cannot change — a field written 0.0 here is 0.0, and no client can set it.

${name} is the only spelling of a placeholder. Anything without the braces is a literal, even when it looks exactly like a parameter name. Resolving by “is this name declared?” would be a trap, because a parameter added later would silently reinterpret a literal somewhere else.

Careful with flow style. In { x: ${linear_speed} } the placeholder's closing brace ends the YAML mapping. Inside braces a placeholder has to be quoted — { x: "${linear_speed}" } — while the indented form above needs no such care, which is why it is the recommended one.

parameters is keyed by parameter name, not by a field path. Same decoupling as a slug: linear_speed stays linear_speed even if the field moves in the message tree, and a caller sends a name that means something.

The type decides what you can constrain

type is required, one of:

bool · byte · char · int8 · uint8 · int16 · uint16 · int32 · uint32 · int64 · uint64 · float32 · float64 · string · wstring

That is ROS’s own spelling, not C++'s — introspection reports float64, not double. The type is never inferred, even where introspection knows it, because the robot that has never connected is exactly where the editor has to help most.

type allowed refused
integers (int8 … uint64, byte, char) min_value, max_value, enum regex
float32, float64 min_value, max_value enum, regex
bool — all four
string, wstring enum, regex min_value, max_value

A constraint on the wrong type is refused as constraint_not_allowed_for_type, never silently ignored. Floats have no enumeration deliberately: equality on floating point is unreliable, and a list of allowed float64 values is a trap that only shows up in operation.

What the constraints actually do

min_value and max_value are enforced, in the cloud, before anything reaches the robot. This is where a speed limit actually holds, rather than in the app that is supposed to respect it.

regex is compiled as a JavaScript regular expression and is not anchored. So [a-z]+ accepts any value that merely contains a lowercase run, and a pattern meant to cover the whole value writes its own ^ and $.

Without a default the parameter is required — the message cannot be built without it. There is no separate required field, which would be a second spelling of the same fact. A default that its own constraints reject is refused here rather than becoming the one value that reaches the robot unchecked.

A declared parameter that appears in no message is an error, and so is a placeholder with no declaration.

Shared messages

A message used more than once goes at the top of the file and is inserted by name.

yaml
messages:
  stop_twist:
    linear: { x: 0.0, y: 0.0, z: 0.0 }
    angular: { x: 0.0, y: 0.0, z: 0.0 }
yaml
    message: ${stop_twist}

Position decides what a ${name} means. Directly after message: it is a message name; anywhere inside a message body it is a parameter.

A shared message may hold placeholders, and whoever inserts it declares the parameters. Limits therefore stay where they belong, and two publishers can send the same message under different bounds.

Nesting is not allowed. A shared message may not insert another, which rules out cycles and lets every check look at exactly one body. At most 200 shared messages live in a file.