=========== Stow Schema =========== Overview ======== The Stow schema is defined by a ``stow.yaml`` file inside your application's root directory. The schema includes user-defined enums, structs, item categories, and the Stow items themselves. This ``stow.yaml`` can be composed with additional YAML fragments and board-specific overrides, see :ref:`Fragments & Overrides `. The Stow is automatically generated by the build system based on the ``stow.yaml``. The generated code can be found within the build folder under ``gantry_generated``. Schema File =========== The ``stow.yaml`` file contains the following sections: - ``enums``: Enum types and their possible values - ``structs``: Custom struct types and their fields - ``roles``: Possible external device roles - ``categories``: Categories for organizing items - ``items``: The items in the Stow The ``description`` fields do not impact the firmware, they just serve as comments in the schema file and within the generated source code. Enums ----- The ``enums`` sections lists custom enumerations and their named values. Enum items reference one of these defined enums. **Example:** .. code-block:: yaml enums: Toggle: description: "A toggle to enable or disable" values: - name: DISABLED value: 0 - name: ENABLED value: 1 SpiChipSelect: description: "A SPI chip select index" values: - name: SPI_CS0 value: 0 - name: SPI_CS1 value: 1 The enum values can be accessed by including ``generated_stow_enums.h``. For example, ``enum Toggle`` will have the value ``Toggle_ENABLED``. Structs ------- Structs are composite types with named fields. Fields can be any Stow type, including nested structs. The constraints for each field are defined within the struct definition. **Example:** .. code-block:: yaml structs: - name: SpiBuffer description: "A SPI buffer containing a chip select and data" fields: - name: CS type: ENUM constraints: - enum: SpiChipSelect - name: Buffer type: BUFFER constraints: - min_len: 0 - max_len: 512 The structs can be accessed by including the relevant ``generated_struct_*.h`` (e.g. ``generated_struct_SpiBuffer.h``). In this example, the struct is accessible in C code a ``SpiBuffer_t``. Roles ----- Items in the Stow have role-based access permissions. The roles section defines possible external client roles. There can be up to **16** roles defined. **Example:** .. code-block:: yaml roles: - User - Clinician - Developer The roles are converted into a bitfield (included via ``generated_stow_items.h``). In this example, an external client may have the roles ``STOW_ROLE_CLINICIAN | STOW_ROLE_DEVELOPER`` indicating that the client is both a clinician and a developer. Categories ---------- Categories are strings used to group related items. Each item may belong to multiple categories. Categories do not serve a functional purpose within the firmware. Instead, external devices reading the Stow will see the categories for each item. This information can be used to organize items (e.g. in a user interface). .. code-block:: yaml categories: - System - BLE - SPI Items ----- Items are the individual data entires in the Stow. Each item has: - ``name``: The name of the data item. - ``categories``: List of categories that the item belongs to. - ``type``: The datatype of the item. - ``storage``: Storage mode for the item. - ``permissions``: ``read`` and ``write`` access controls. Each is a list of roles able to access the item. The ``ANY`` keyword can be used to allow access to all roles. The ``INTERNAL`` keyword can be used to restrict access from all roles; only the firmware can access these items. - ``default``: The default value of the item. - ``constraints``: The item's value constraints. - ``custom_validate`` *(optional)*: Name of a C function providing additional validation beyond type-level constraints. - ``custom_get`` *(optional)*: Name of a C function to override the default get behavior. - ``custom_set`` *(optional)*: Name of a C function to override the default set behavior. After generation, items are accessed via the ``STOW_ID_`` enum (e.g. ``STOW_ID_DEVICE_NAME``). All IDs are defined in ``generated_stow_items.h``. Built-in Items ************** The first two items in the Stow built-in and included by default, they are not defined in the ``stow.yaml``. The first is the ``StowHash``, a SHA256 hash string of the current Stow description. The Stow hash allows clients cache the Stow description. The second is the ``FirmwareVersion``, a string derived from the Zephyr ``VERSION`` file. Custom Interface **************** The ``custom_get``, ``custom_set``, and ``custom_validate`` fields each accept a C identifier naming a function that the application provides. For when and how these functions are called, see the :doc:`API docs `. Each name must be a valid C identifier. Any field that is omitted defaults to the standard interface behavior. .. code-block:: yaml - name: SensorReading description: "Live sensor reading (computed on demand)" categories: [Sensor] type: INT storage: EPHEMERAL permissions: - read: ANY - write: INTERNAL default: 0 constraints: - min: -1000 - max: 1000 custom_get: sensor_reading_get - name: TargetTemperature description: "Target temperature with application-specific validation" categories: [System] type: FLOAT storage: PERSISTENT permissions: - read: ANY - write: [User] default: 20.0 constraints: - min: 10.0 - max: 40.0 custom_validate: target_temp_validate custom_set: target_temp_set Constraints by Type ******************* **Integer / Float** Integer and float constraints define a range of valid values. .. code-block:: yaml constraints: - min: -100 - max: 100 **String / Byte Array / Buffer** String, Byte Array, and Buffer constraints define the minimum and maximum lengths for the buffer. .. code-block:: yaml constraints: - min_len: 0 - max_len: 64 **Enum** Enum constraints reference one of the defined ``enums``. The referenced enum definition contains a list of possible values. .. code-block:: yaml constraints: - enum: MyEnumName **Struct** Struct constraints reference one of the defined ``structs``. The referenced struct contains the constraints for each field in its definition. .. code-block:: yaml constraints: - struct: MyStructName Example: ******** .. code-block:: yaml items: - name: DeviceName description: "The name of the device" categories: [System] type: STRING storage: PERSISTENT permissions: - read: ANY - write: [User] default: "Gantry" constraints: - min_len: 3 - max_len: 25 - name: SerialNumber description: "The device serial number" categories: [System] type: STRING storage: TOFU permissions: - read: ANY - write: [Developer] default: "0000000000000000" constraints: - min_len: 16 - max_len: 16 - name: BleConnectionState description: "The current BLE connection state" categories: [System, BLE] type: ENUM storage: EPHEMERAL permissions: - read: ANY - write: INTERNAL default: DISCONNECTED constraints: - enum: BleConnectionState - name: TestInt description: "Test int value" categories: [System, Test] type: INT storage: EPHEMERAL permissions: - read: [User, Clinician] - write: [Clinician] default: 0 constraints: - min: -100 - max: 100 custom_validate: test_custom_validator - name: TestFloat description: "Test float value" categories: [System, Test] type: FLOAT storage: EPHEMERAL permissions: - read: ANY - write: ANY default: 21.5 constraints: - min: 15.0 - max: 30.0 - name: TestBytes description: "Test bytes" categories: [Test] type: BYTE_ARRAY storage: EPHEMERAL permissions: - read: ANY - write: ANY default: [0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01] constraints: - min_len: 12 - max_len: 12 - name: TestBuffer description: "Test buffer" categories: [Test] type: BUFFER storage: EPHEMERAL permissions: - read: ANY - write: ANY default: [] constraints: - min_len: 0 - max_len: 20 - name: SpiTxBuffer description: "SPI TX buffer" categories: [SPI] type: STRUCT storage: EPHEMERAL permissions: - read: ANY - write: ANY default: - field: CS value: SPI_CS0 - field: Buffer value: [] constraints: - struct: SpiBuffer .. _fragments-overrides: Fragments & Overrides ===================== A Stow definition can be assembled from multiple YAML fragments instead of a single ``stow.yaml``. This allows a module's items live in their own file, be reused across apps, and be overridden per app or per board. By convention, fragment files are named ``*.stow.yaml``. The fully composed definition is written to ``merged.stow.yaml``. .. note:: ``CONFIG_GANTRY_STOW`` requires **CMake >= 3.29** to defer fragment composition such that an app (or other modules) can define additional stow fragments. Registering a fragment ----------------------- Call ``gantry_stow_add_fragment()`` from your app's ``CMakeLists.txt``: .. code-block:: cmake gantry_stow_add_fragment(${CMAKE_CURRENT_SOURCE_DIR}/ble.stow.yaml) A fragment has the same shape as ``stow.yaml`` (``enums``, ``structs``, ``roles``, ``categories``, ``items``), but need not be complete. Merge order ----------- Fragments are composed lowest to highest precedence: 1. Fragments registered via ``gantry_stow_add_fragment()``, in registration order. 2. The app's own ``stow.yaml`` content. This is applied on top of all registered fragments, so it's the natural place to override values for a specific app. 3. An auto-discovered board fragment, ``boards/.stow.yaml``. Always highest precedence, if present. Overrides --------- Enums, structs, and items are matched by ``name`` across fragments: - Duplicate definitions are merged. - If a later fragment redefines something **differently**, only the fields it specifies are patched onto the earlier definition and unspecified fields are inherited. This means a fragment can define an item in full, and a later layer can override just what it needs to change: .. code-block:: yaml # ble.stow.yaml items: - name: DeviceName description: "The name of the device" categories: [General] type: STRING storage: PERSISTENT permissions: - read: ANY - write: [Session] default: "BleDevice" constraints: - min_len: 3 - max_len: 25 .. code-block:: yaml # stow.yaml items: - name: DeviceName default: "Gantry" ``roles`` and ``categories`` are merged as an ordered union of unique strings.