When consuming large pools of Cyphal messages new problems arise that were not foreseen. Specifically, pydsdl and Nunavut currently collect and generate all types found under a given root namespace. This quickly becomes untenable as the number of messages grows. This post proposes a format that allows software projects to declare a set of messages or patterns that match a set of messages within a given set of namespaces as being “of interest” to them. Various tools like pydsdl and Nunavut can then use this format to apply rules like “check for semantic compatibility errors between Corn_2_1 and Corn_2_2” or “only c headers for messages selected and for the types these messages use”. The post goes further to call for proposals for a minified message description format to allow build-time (and possibly runtime) interoperability checks between software projects. As such we propose a new format; “DSDL Manifests”
DSDL Manifests
DSDL Manifests describe the DSDL definitions that are available to a system. A rules engine processes this manifest when discovering types within a root namespace and uses it to select specific DSDL versions and to ignore uninteresting types. The manifest may also contain additional rules that describe the compatibility of the selected definitions with previously encountered definitions.
Message Selection
The manifest may contain rules that describe which DSDL definitions are of interest to a system. These rules are used to include or exclude definitions as discovery takes place within a namespace. For example, given the following root namespace farm
and the following directory structure:
farm/
├── livestock/
│ ├── Cow_1_0.dsdl
│ ├── Pig_1_0.dsdl
│ ├── Pig_1_1.dsdl
├── crop/
│ ├── Corn_2_1.dsdl
│ ├── Corn_3_0.dsdl
│ ├── Wheat_0_1.dsdl
A manifest may describes rules like this:
"farm.livestock.Pig": "1.0"
"farm.crop.Corn" : "^2.0"
"farm.crop.Wheat" : "*"
Where the rules are:
- Pig:
1.0
selects the exact version 1.0 soPig_1_0.dsdl
is selected - Corn:
^2.0
selects the newest version that is semantically compatible soCorn_2_1.dsdl
is selected - Wheat:
*
selects the newest version without any compatibility checking.Wheat_0_1.dsdl
is selected but
warnings should be issued if Wheat 1.0 or greater is selected and --warnings-are-errors should be
an available option to manifest processors. - Cow: is not selected because it is not in the manifest.
Message Verification
Manifests may (optional) contain a (TBD) minimal description of the DSDL definitions previously encountered by a subscriber or utilized by a publisher. This allows additional processing of selected definitions to find transcription errors or illegal changes to versioned types. This compressed representation of the DSDL would not contain constants, comments, whitespace, or other etherial information making it suitable as an input to automated compatibility checkers.
Format
The manifest is a JSON Lines<https://jsonlines.org/>
_ format file with each line being one of three types:
- Header – The first line of the file and only one header is allowed. It contains metadata about the manifest like the version of the manifest format and the default action for unselected types.
- Exclude - Any type not selected by a rule is ignored.
- IncludeGreedy - Any type not selected by a rule has all versions of the type selected.
- IncludeLatest - Any type not selected by a rule has the latest version of the type selected.
- IncludeReleased - Same as InclusiveLatest but ignores unreleased types (i.e. 0.x versioned types).
- Signature - Describes a compressed representation of a DSDL definition that has been previously encountered. This part of the proposal is not fully developed but a place for it is reserved and the spirit of this functionality should be included in the discussion of the first version of the manifest specification.
- Selector - Each describes a single rule for including or excluding types found under a root namespace.
- Exclude - All versions type selected by a rule are ignored.
- Include - The rule selects the best, single version of the type that matches.
- IncludeGreedy - The rule selects all type versions that match.
(IncludeLatest and IncludeReleased are redundant for selectors since they are a subset of version matching rules)
The header must be the first line and must specify how many signatures and selectors are in the manifest.
If there are any signatures they must follow the header and be grouped together. If there are any selectors
they must follow the signatures and be grouped together. The order of signatures and selectors is not important. Tools should detect and report contradicting rules when processing the manifest and treat these as fatal errors.
Fixed port IDs
The manifest ignores fixed port identifiers as they are not part of the type signature.
Example
{"type":"header", "version":"1.0", "default-action": "Exclude", "selectors": 6}
{"type": "selector", action="Include", "parts": ["uavcan", "node", "ExecuteCommand"], "version":"^1.0"}
{"type": "selector", action="Include", "parts": ["uavcan", "node", "Heartbeat"], "version":"^1.0"}
{"type": "selector", action="Include", "parts": ["uavcan", "primitive", "String"], "version":"^1.0"}
{"type": "selector", action="Exclude", "parts": ["farm", "livestock", "Cow"], "version":"*", comments="No red meat please"}
{"type": "selector", action="IncludeGreedy", "parts": ["farm", "livestock", "Pig"], "version":"^1.0", comments="The other white meat"}
{"type": "selector", action="IncludeGreedy", "parts": ["farm", "crop", "Corn"], "version":">=2.0"}
The types are expressed in parts to allow search trees to be built and encouraging performant processing of the selectors. We specifically avoid regular expressions for this reason.
While not a Python-specific format, the version matching rules are borrowed from Python (todo: PEP???)
Comments are optional but should be emitted as console output in CLIs or in log files when encountered.