Showroom

One namespace of plausible vendor-specific datatypes for an aerial drone system, and everything dsdlc does with it -- generated into every language and profile the compiler supports, and then built by thirteen real build systems.

It exists so you can answer three questions in order, without inventing a schema of your own:

  1. What would I write? lanyard is a fictional vendor's namespace, the sort of thing a drone programme adds alongside the standard uavcan types. Nothing here is regulated and UDRAL is deliberately unused -- the point is what a vendor writes from scratch, leaning on the standard types where standard types exist.
  2. What does the compiler make of it? Every definition below pairs its authored DSDL with its wire-layout facts and a declaration excerpt in each language.
  3. How do I get that into my build? The same namespace, wired into CMake, Make, Ninja Multi-Config, Bazel, cargo, go modules, npm, pnpm, and five Python build backends. See Build Recipes.

The two halves make opposite bets on purpose. Browsing generates and stops -- it must never fail for a reason unrelated to what it is showing. The recipes compile and run, because a recipe that did not build would prove nothing.

Generate it

cmake --build <build-dir> --target showroom

Output lands in <build-dir>/showroom/<variant>/:

Variant Command
c --target-language c
cpp-std, cpp-pmr, cpp-autosar --target-language cpp --cpp-profile <profile>
rust-std, rust-no-std-alloc --target-language rust --rust-profile <profile>
go --target-language go
ts --target-language ts
python --target-language python
mlir --target-language mlir (the intermediate form, one file)

Nothing here is compiled -- correctness of the generated code is what test/lit and the integration suites are for, and compiling would make browsing depend on six language toolchains being present. Building the output is what the recipes do, deliberately as a separate half.

Individual variants build on their own: cmake --build <build-dir> --target showroom-rust-std.

There is no submodule dependency -- lanyard refers only to uavcan types, which dsdlc carries in its embedded catalog.

Contents

Twenty-four definitions across six sub-namespaces, chosen so that between them they exercise the language and the three LEAST SUPPORTED TRANSPORTs a real vehicle spans.

Type Port Least supported transport Kind
lanyard.flight.ControlSurfaces.1.0 6212 CAN FD message
lanyard.flight.FixedWingSurfaces.1.0 -- unspecified message
lanyard.flight.FlightMode.1.0 -- unspecified message
lanyard.flight.MultirotorMix.1.0 -- unspecified message
lanyard.flight.VehicleState.1.0 6210 CAN FD message
lanyard.flight.VehicleState.1.1 6210 CAN FD message
lanyard.flight.VehicleState.2.0 6211 CAN FD message
lanyard.health.BatteryStatus.2.0 6251 CAN FD message
lanyard.health.LegacyBatteryPoll.1.0 258 Classic CAN service
lanyard.health.SubsystemReport.1.0 -- unspecified message
lanyard.health.SystemHealth.1.0 6250 CAN FD message
lanyard.link.RcInput.1.0 6240 CAN FD message
lanyard.link.TelemetryLinkStats.1.0 6241 CAN FD message
lanyard.nav.GlobalPosition.1.0 6220 CAN FD message
lanyard.nav.MissionPlan.1.0 6221 Cyphal/UDP ONLY message
lanyard.nav.UploadMission.1.0 256 CAN FD and Cyphal/UDP service
lanyard.nav.Waypoint.1.0 -- unspecified message
lanyard.payload.CameraFrameMetadata.1.0 6231 Cyphal/UDP ONLY message
lanyard.payload.CapturePhoto.1.0 257 CAN FD and Cyphal/UDP service
lanyard.payload.GimbalStatus.1.0 6230 CAN FD message
lanyard.propulsion.EscStatus.1.0 6200 Classic CAN (CAN 2.0B) message
lanyard.propulsion.EscStatus.2.0 6201 CAN FD message
lanyard.propulsion.ThrottleCommand.0.1 6202 CAN FD message
lanyard.propulsion.ThrottleCommand.1.0 6203 CAN FD message

Least Supported Transport

Every definition states a minimally capable transport it was sized for, and most of them assert that budget with @assert _offset_.max <= ... so that a layout change breaks the build rather than quietly spilling into a multi-frame transfer. Cyphal does not limit DSDL by transport but type authors often make different design choices based on the limitations of certain transports. In some cases these design choices are not clear unless these limitations are called out in type comments or by using DSDL assert statements.

Transport Budget Examples
Classic CAN 7 payload bytes, one frame propulsion.EscStatus.1.0, hand-packed to exactly 56 bits
CAN FD 63 payload bytes, one frame nav.GlobalPosition.1.0, link.RcInput.1.0, payload.GimbalStatus.1.0
Cyphal/UDP a datagram, kilobytes nav.MissionPlan.1.0, payload.CameraFrameMetadata.1.0

EscStatus.1.0 and MissionPlan.1.0 are the two ends of that range on purpose: same compiler, same language, one sized for a 7-byte frame and the other for a 24 KiB datagram.

Versioning

Five migrations, each answering a different question about when a change forces a major bump.

Transition Breaking What it shows
ThrottleCommand 0.1 → 1.0 n/a A 0.x definition promises nothing; promotion to 1.0 is where the promise begins
VehicleState 1.0 → 1.1 no A field appended inside an unchanged @extent; both share port 6210
VehicleState 1.1 → 2.0 yes A field retyped, a field replaced, the extent grown; new port 6211
EscStatus 1.0 → 2.0 yes @sealed is a one-way door: extensibility costs a major version
LegacyBatteryPoll 1.0 → BatteryStatus 2.0 yes @deprecated marking a superseded service, both halves kept

Language features

Feature Where
@sealed EscStatus.1.0, RcInput.1.0, SubsystemReport.1.0
@extent VehicleState.1.0, MissionPlan.1.0, Waypoint.1.0
@union ControlSurfaces.1.0
@assert EscStatus.1.0, GlobalPosition.1.0, CapturePhoto.1.0
@print GlobalPosition.1.0
@deprecated LegacyBatteryPoll.1.0
Service request/response sections UploadMission.1.0, CapturePhoto.1.0
Non-byte-aligned scalars EscStatus.1.0 (uint14, int12, int9, uint4)
void padding EscStatus.1.0, GlobalPosition.1.0, SubsystemReport.1.0
Fixed-size arrays GlobalPosition.1.0 (float16[9]), RcInput.1.0 (uint11[16])
Variable-length arrays ThrottleCommand.1.0, MissionPlan.1.0, BatteryStatus.2.0
Arrays of composites MissionPlan.1.0 (delimited elements), SystemHealth.1.0 (sealed elements)
Cast modes CameraFrameMetadata.1.0 (truncated against saturated)
Constants as enumerations FlightMode.1.0, Waypoint.1.0, GlobalPosition.1.0
Reuse of standard uavcan types throughout; SubsystemReport.1.0 is the clearest case

Documentation

DSDL comment placement follows the OpenCyphal convention used by the regulated namespace: the block goes after the attribute it documents, followed by a blank line. A block placed before a field attaches to whatever precedes it instead, or is absorbed into the type's own documentation if the field is the first one.

@deprecated rides along with it. Every backend appends a Deprecated: … notice to the type's documentation and emits an IS_DEPRECATED constant. Go treats the notice as a real deprecation, and TypeScript additionally gets a /** @deprecated */ JSDoc block.

C, C++, and Rust get compile-time enforcement on top of that, by default: __attribute__((deprecated)), [[deprecated]], and #[deprecated]. Only code that names a deprecated type is diagnosed — each generated file suppresses the diagnostic across its own body, so including the headers stays clean under -Werror. dsdlc --no-deprecation-attributes drops the attributes. See health/258.LegacyBatteryPoll.1.0.dsdl.

Port identifiers

lanyard is not a standard root namespace, so its fixed port identifiers come from the unregulated ranges: 6144–7167 for messages and 256–383 for services. Staying inside them is what lets the showroom build without --allow-unregulated-fixed-port-id.

A fixed port identifier belongs to one major version. Minor versions share it (VehicleState.1.0 and 1.1 are both on 6210); a major bump takes a new one (VehicleState.2.0 moves to 6211).