10.2. Syntax of Message Definitions

This page documents the accepted syntax of LN message-definition files as implemented by python/links_and_nodes_base/message_definitions.py. It describes the file format itself. For details on how message-definition files are found on disk, see Message Definitions.

10.2.1. General format

A message definition is a plain text file. Empty lines are ignored. Lines starting with # are comments. Inline comments are also allowed: everything after # on a line is ignored.

A regular field line has this shape:

TYPE FIELD_NAME

Static arrays append a count in square brackets:

TYPE FIELD_NAME[COUNT]

Imported sub-types use define:

define LOCAL_TYPE_NAME as "other/message_definition/name"

Service and event definitions add section markers:

service
request
...
response
...
event
connect
...
call
...

The first non-empty, non-comment line may be one of:

  • nothing special: a regular message definition

  • service: a service definition with request and response sections

  • event: an event definition with connect and call sections

If the first non-empty line is service or event, the remaining lines must follow the corresponding section syntax.

10.2.2. Primary types

The built-in scalar element types accepted by the parser are:

  • float or float32_t

  • double or float64_t

  • char

  • int8_t

  • uint8_t

  • int16_t or short

  • uint16_t

  • int32_t or int

  • uint32_t

  • int64_t

  • uint64_t

Each field is stored as one or more values of the chosen element type. There is no separate boolean type; use an integer type such as uint8_t when needed.

10.2.3. Regular message definitions

A regular message definition is just a sequence of field declarations and optional define statements.

Example:

define pose_t as "my_robot/pose"
uint64_t seq
pose_t pose
double covariance[6*6]

Imported sub-types become usable like built-in types. They can be used as:

  • a single field, for example pose_t pose

  • a static array, for example pose_t poses[4]

  • a dynamic field, for example pose_t* poses in service/event or other dynamic message definitions

10.2.4. Static arrays and multi-dimensional data

Static arrays are written as FIELD_NAME[COUNT]. COUNT is evaluated as a Python integer expression, so forms such as [12] and [3*4] are accepted.

LN message definitions do not provide dedicated multi-dimensional syntax. Instead, N-D arrays are represented as a flat 1-D array with the product of the dimensions as the count.

Example:

double frame[3*4]

This is the convention used in share/message_definitions/ln/frame34. For such flattened N-D arrays, LN uses the standard C row-major convention. In the 3*4 example, the first index is the row and the second index is the column, and consecutive elements of one row are adjacent in memory.

For example, a logical matrix element frame[r][c] is stored at:

frame[r * 4 + c]

Use the same row-major convention consistently in publishers, subscribers, service providers, and any generated or handwritten bindings.

10.2.5. Imported sub-types with define

Sub-types are imported with:

define TYPE_NAME as "relative/or/global/message_definition_name"

Rules:

  • TYPE_NAME is the local type name used later in the file.

  • The imported path must be written in double quotes.

  • TYPE_NAME must not contain a double quote.

  • The referenced file is looked up first relative to the current message definition file and then through the normal message-definition search path; see Message Definitions.

  • After a define, the imported type can be used wherever a primary type can be used, subject to the same static or dynamic rules.

Example:

define value_t as "value"
value_t* values

10.2.6. Services

A service definition starts with service. It can then contain two sections:

  • request for request fields

  • response for response fields

Example:

service
request
char* message
response
char* error_message
char* result

Fields declared after request belong to the request payload. Fields declared after response belong to the response payload.

define statements may appear inside service files and can be used by either section.

10.2.7. Events

An event definition starts with event. It uses two section markers:

  • connect for the subscription/filter part

  • call for the emitted event payload

Example:

event
connect
char* event_pattern
char* name_pattern
call
char* event
char* name

Internally, LN parses connect and call like the request and response parts of a service definition.

10.2.8. Dynamic fields

Dynamic fields are written by appending * to the type name:

char* string
uint8_t* data
pyobject* values

Accepted properties:

  • * can be used with both primary types and imported sub-types.

  • Arrays of pointer types are not supported. A declaration such as char* names[4] is rejected.

  • Dynamic fields make the message definition dynamic.

The parser also enforces a length-field convention. A dynamic field named data uses a companion field named data_len of type uint32_t.

If data_len is not written explicitly, LN inserts it automatically immediately before data in the parsed field layout. If it already exists but is not directly before the dynamic field, LN moves it there in the parsed layout.

That means these two request sections are equivalent:

request
uint8_t* data
request
uint32_t data_len
uint8_t* data

Dynamic fields are the accepted representation for variable-length payloads. They are used heavily in service and event definitions, and the parser also accepts them in other dynamic message definitions. By contrast, the regular fixed-size topic layout is based on non-pointer fields and static arrays.

10.2.9. Field-name restrictions

Field names must be plain identifiers without punctuation. The parser rejects field names containing any of these characters:

; . , + - * / { } ( ) # $ ä ö ü ? ' ` " \

In practice, use simple ASCII names such as count, frame, error_message, or obj_req_single.

Within one request or regular message, a field name may only be used once. Within one response section, a response field name may also only be used once. Request and response sections are checked independently.

10.2.10. Notes and examples

Examples from the source tree:

  • share/message_definitions/ln/frame34 demonstrates flattened N-D array storage via double frame[3*4].

  • share/message_definitions/ln/string shows a dynamic string payload: char* string.

  • share/message_definitions/ln/string_request shows a service definition.

  • share/message_definitions/ln/resource_event shows an event definition.

  • share/message_definitions/tests/time_service shows primary types, imported sub-types, static arrays, and dynamic fields in the same file.

If you need the search-path and lookup rules for these files, continue with Message Definitions.