############################# 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 :doc:`reference_mdefs`. .. _reference/message_definitions/syntax: 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: .. code-block:: text TYPE FIELD_NAME Static arrays append a count in square brackets: .. code-block:: text TYPE FIELD_NAME[COUNT] Imported sub-types use ``define``: .. code-block:: text define LOCAL_TYPE_NAME as "other/message_definition/name" Service and event definitions add section markers: .. code-block:: text service request ... response ... .. code-block:: text 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. 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. Regular message definitions =========================== A regular message definition is just a sequence of field declarations and optional ``define`` statements. Example: .. code-block:: text 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 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: .. code-block:: text 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: .. code-block:: text frame[r * 4 + c] Use the same row-major convention consistently in publishers, subscribers, service providers, and any generated or handwritten bindings. Imported sub-types with ``define`` ================================== Sub-types are imported with: .. code-block:: text 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 :doc:`reference_mdefs`. * 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: .. code-block:: text define value_t as "value" value_t* values Services ======== A service definition starts with ``service``. It can then contain two sections: * ``request`` for request fields * ``response`` for response fields Example: .. code-block:: text 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. 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: .. code-block:: text 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. Dynamic fields ============== Dynamic fields are written by appending ``*`` to the type name: .. code-block:: text 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: .. code-block:: text request uint8_t* data .. code-block:: text 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. Field-name restrictions ======================= Field names must be plain identifiers without punctuation. The parser rejects field names containing any of these characters: .. code-block:: text ; . , + - * / { } ( ) # $ ä ö ü ? ' ` " \ 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. 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 :doc:`reference_mdefs`.