CANopen device-driver codegen — architecture (arc42)¶
Architecture documentation for the CANopen device-driver codegen toolchain (see CANopen device-driver codegen), structured per the arc42 template and encoded with sphinx-needs using the useblocks “x-as-code” arc42 directive types. Mirrors the structure of Device-driver codegen — architecture (arc42) so reviewers can read both umbrellas 1:1.
Each architectural element :refines: or :implements: a parent
requirement so the trace is preserved end-to-end.
This chapter is split across pages (see the toctree): the framing sections §1–§3 (goals, constraints, context and scope — including the crate-dependency-graph mermaid) live here on the index; the building blocks (§4) live in Building block view; the solution strategy and its ADRs (§5) live in Solution strategy; the risks (§6) live in Risks; and the cross-cutting traceability (§7) lives in Cross-cutting traceability.
1. Introduction and goals¶
The toolchain’s reason-to-exist is build-time monomorphisation of
CANopen device drivers: vendor-supplied EDS files describe each
node’s PDOs, OD, and bring-up SDO sequence; we want strongly-typed
on_rpdo / drain_tpdos code per device, with zero INI parsing
at runtime and no hand-written boilerplate per node.
Quality goals capture the qualities the architecture is optimised for.
The same set of EDS inputs (modulo file ordering) shall produce a byte-identical generated module across machines, toolchain versions, and clock walls. Generation order, hash-map iteration order, and timestamp inclusion are explicitly excluded as sources of nondeterminism. Required so the generated file is reviewable in diffs and cachable in CI. |
Each crate in the toolchain shall depend only on crates to its
left in the OD-core → parse → codegen → tooling chain (see
Toolchain layering (crate d... (ARCH_0070)). Crossover dependencies — e.g. the parser
reaching for |
A consumer of the generated modules shall pay no runtime cost
for the codegen layer’s existence: no INI parse, no allocation
for OD tables when |
The |
2. Constraints¶
The build helper shall live within cargo’s |
The EDS schema is published by CAN in Automation (CiA 306) and the underlying OD semantics by CiA 301. The parser shall track the published schema; schema drift across vendors shall be handled as captured in Unknown sections captured a... (REQ_0724) (unknown-section policy) and Liberal parsing — warn and ... (REQ_0725) (liberal-quirks policy) rather than by hard-failing the parse. |
|
|
3. Context and scope¶
Five layers, strict left-to-right dependency. Each crate has one
job and depends only on crates to its left. The follow-on
graph LR
subgraph OD["1. Shared OD core"]
ODC["fieldbus-od-core<br/>Identity, DictEntry,<br/>PdoEntry, PdoMap"]
end
subgraph Parse["2. Parse layer"]
EE["ethercat-esi<br/>(re-exports + ESI specifics)"]
P["canopen-eds<br/>INI → typed IR"]
end
subgraph Gen["3. Codegen layer"]
G["canopen-eds-codegen<br/>IR → TokenStream"]
B["canopen-eds-codegen-taktora<br/>concrete backend"]
end
subgraph RT["4. Runtime trait"]
RTC["canopen-eds-rt<br/>CanOpenDevice / CanOpenConfigurable"]
end
subgraph Tool["5. Tooling layer"]
BR["canopen-eds-build<br/>build.rs glue"]
CLI["canopen-eds-cli<br/>cargo eds expand / list"]
VER["canopen-eds-verify<br/>EDS ↔ SDO-dump diff"]
end
subgraph Cons["Consumers (follow-on)"]
USER["any CAN consumer<br/>(includes generated code)"]
SCC["taktora-connector-can<br/>thin CanOpenDevice adapter"]
end
ODC --> EE
ODC --> P
P --> G
G --> B
B --> RTC
B --> BR
B --> CLI
P --> VER
RTC --> BR
BR --> USER
USER --> SCC
|
The toolchain runs entirely at build time. Runtime consumers
only see the generated module and link against
|