moon-dbc

    MoonDBC: CAN / CAN FD DBC parsing, validation, multiplexed frame codec and CLI

    can
    can-fd
    dbc
    automotive
    codec
    multiplex
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    10 hours ago
    Downloads
    3

    Dependencies

    #MoonDBC

    面向 CAN / CAN FD 的 DBC 数据库解析、校验与报文编解码工具库

    宋永振(SongYZZZ)的原创 MoonBit 开源项目。仓库固定为 SongYZZZ/moon-dbc,Apache-2.0。 English

    以下输出来自实际运行的原生 CLI:

    > .\dist\moon-dbc.exe inspect tests/fixtures/basic.dbc DBC database Version: 1.0 Nodes: 2 Messages: 1 Signals: 2 Standard CAN messages: 1 Extended CAN messages: 0 Classic-sized messages: 1 CAN FD-sized messages: 0 Multiplexed messages: 0 Value descriptions: 2 Diagnostics: 0 errors, 0 warnings > .\dist\moon-dbc.exe decode tests/fixtures/basic.dbc 0x123 E02E030000000000 Message: EngineData (DBC ID 291) EngineSpeed: raw=12000 value=1500 rpm Gear: raw=3 value=3 (Drive) > .\dist\moon-dbc.exe diff tests/fixtures/basic.dbc tests/fixtures/revised.dbc Semantic changes: 1 EngineData.EngineSpeed.factor: 0.125 -> 0.25

    #用途

    DBC 描述帧标识符、信号位布局、比例、偏移、单位和枚举。MoonDBC 将这些文本定义转换为结构化模型,供汽车电子、机器人与嵌入式测试程序解释采集的帧、构造测试向量、检查布局冲突或比较数据库修订。

    它处理数据库和内存中的帧。CAN 驱动负责实际总线收发;USB-CAN、SocketCAN、硬件时序、BRS、CRC、ISO-TP、UDS 和 J1939 协议层不属于本库。

    #已实现能力

    • Database、Node、Message、Signal、FrameId、CanFrame 和结构化 Diagnostic。
    • 带行列位置的词法与语法解析、输入限制、strict / permissive 模式。
    • Intel 与 DBC Motorola 位编号,1~64 位 signed / unsigned 精确 raw 值。
    • factor / offset、明确的舍入策略和 Reject / Ignore / Warn / Clamp 范围策略。
    • VAL_ 枚举及 label 编码;基本 M、mN 多路复用。
    • mux 分支感知的重叠检查、引用、重复定义、属性类型、范围、默认值校验。
    • 标准/扩展 CAN ID,保留原始 DBC ID;0~64 字节载荷。
    • 确定性 DBC writer、语义 diff、统计、真实 bit layout。
    • 文件 CLI:inspect、validate、decode、encode、diff、layout;附加 normalize。
    • 缓存并快照布局的 CompiledMessage,适合连续处理多帧。

    #格式范围

    结构0.1.0 状态
    VERSION、BU_、BO_、SG_支持文档化的核心语法
    NS_支持命名空间声明;以 BS_ 结束
    BS_仅空段;不支持位时序字段
    CM_database / node / message / signal
    VAL_支持;raw 标签键为 Int64
    BA_DEF_、BA_DEF_DEF_、BA_INT、HEX、FLOAT、STRING、ENUM;四种 scope
    VAL_TABLE_、BO_TX_BU_、SIG_GROUP_、EV_不作结构化解释;strict 拒绝,permissive 保留文本并警告
    SIG_VALTYPE_、SG_MUL_VAL_、SIG_TYPE_REF_编解码关键扩展,两个模式均拒绝

    DBC 存在厂商扩展和历史实现差异。本库不声称完整 DBC 标准兼容。详见 FORMAT_SUPPORT、PARSER_MODES。

    #安装与构建

    使用官方 MoonBit 工具链。本次验证版本为 moonc v0.10.9(2026-08-19)。 项目配置由当前 moon new 生成,使用 moon.mod 和 moon.pkg。 从仓库根目录运行:

    moon version --all moon update moon check moon test moon fmt --check moon build moon info pwsh -NoProfile -File scripts/build-cli.ps1

    构建脚本生成 dist/moon-dbc.exe。核心库不依赖硬件,也不依赖 Python。 官方 moonbitlang/x 0.5.1 仅用于 CLI 的文件读写与进程退出。 原生 stderr 的少量 C 代码只承担进程输出,DBC 核心全部使用 MoonBit。

    Mooncakes 模块名:SongYZZZ/moon-dbc,版本 0.1.0。模块名与元数据已经 通过真实 moon package 验证;实际发布状态见 Completion Report。

    #Library API

    在 moon.pkg 中 import SongYZZZ/moon-dbc 并取别名 dbc。 完整可执行代码位于 examples/basic:

    let db = @dbc.parse_dbc(source)
    let message = db.message_by_name("Engine").unwrap()
    let encoded = @dbc.encode_message(message, [
    { name: "Speed", value: Physical(1500) },
    ])
    let decoded = @dbc.decode_frame(db, encoded.frame)

    公共 API 使用 raise DbcError,调用者应捕获 Failure(Diagnostic)。 unwrap 仅用于已知存在的示例对象;未知查询返回 Option。 查询还包括 message_by_id、node_by_name、signal_by_name 和 find_signals。 连续处理时先 compile_message(message),再调用 .encode / .decode。 导出接口见 pkg.generated.mbti,设计见 DESIGN。

    moon run examples/basic moon run examples/multiplex

    #CLI

    .\dist\moon-dbc.exe inspect tests/fixtures/basic.dbc --message 0x123 .\dist\moon-dbc.exe validate tests/fixtures/basic.dbc .\dist\moon-dbc.exe decode tests/fixtures/basic.dbc 0x123 E02E030000000000 .\dist\moon-dbc.exe decode tests/fixtures/motorola.dbc 292 0540FFF000000000 --json .\dist\moon-dbc.exe encode tests/fixtures/basic.dbc 0x123 EngineSpeed=1500 Gear=3 .\dist\moon-dbc.exe encode tests/fixtures/basic.dbc 0x123 --raw EngineSpeed=12000 Gear=3 .\dist\moon-dbc.exe layout tests/fixtures/multiplex.dbc 293 .\dist\moon-dbc.exe diff tests/fixtures/basic.dbc tests/fixtures/revised.dbc .\dist\moon-dbc.exe normalize tests/fixtures/attributes.dbc

    枚举 label 输入写成 Gear=label:Drive。所有文件命令接受 --permissive。 CLI ID 是原始 DBC ID,可用十进制或 0x;扩展帧必须包含 bit 31 标志。 解码 JSON 中 raw 是十进制字符串,避免 JavaScript 消费者丢失 64 位整数精度。 exit code:成功 0;validate 有错误或 diff 有差异 1;参数、IO、parse、codec 错误 2。

    也可以直接从根目录运行:

    moon run cmd/main -- inspect tests/fixtures/basic.dbc moon run --target native cmd/main -- decode tests/fixtures/basic.dbc 0x123 E02E030000000000 moon run --target js cmd/main -- decode tests/fixtures/basic.dbc 0x123 E02E030000000000 --json

    #编解码约定

    Intel 的 start bit 指向 LSB;Motorola 的 start bit 指向 MSB,跨字节使用锯齿编号。 见带人工位图的 BIT_NUMBERING。

    physical = raw * factor + offset。物理量编码采用最近整数、半值远离零; 默认 Reject 范围越界,绝不自动 Clamp。量化后也检查范围。 [0|0] 表示声明范围仅为零,本项目不把它自动解释为未知范围。 可显式选择 Ignore,或用精确 raw API。Warn 在整帧编码结果中记录 warning。 超过 Double 精确整数范围的物理输入被拒绝,完整 64 位值用 RawValue 输入。

    mux 根据 selector 的 raw 值选择分支。无已知分支时解码返回 selector 和 always-active 信号,并产生 warning;编码拒绝 inactive、unknown、重复和缺失的 active 输入。 帧 payload 长度必须精确等于 Message.length;不截断、不补齐。

    0~64 字节是数据库载荷范围。大于 8 字节只称为 CAN FD-sized, 不声称实现 CAN FD DLC 映射、BRS、CRC 或收发总线的传输语义。

    #诊断与测试

    实际错误示例:error [signal.overlap] line 5:2 Collision.B: overlap with A at bit 4。 解析错误与严重布局错误会抛出;validate_database 收集可检查的语义问题。

    203 个非重复 MoonBit 测试已通过默认 wasm-gc、native 和 JS。 固定种子测试另外执行 1536 个 raw round-trip,不把这些循环伪装成独立 test 数。 41 个 cantools 44.1.0 交叉验证向量通过;15 个实际 CLI smoke 用例通过。

    moon check --deny-warn moon test --deny-warn moon test --target native --deny-warn moon test --target js --deny-warn pwsh -NoProfile -File scripts/smoke.ps1 pwsh -NoProfile -File scripts/source-audit.ps1 moon bench --target native --release moon package

    TESTING 说明验证方法,COMPATIBILITY 提供真实 oracle 记录,BENCHMARKS 给出实测环境和数值。 未声称 coverage 百分比。GitHub Actions 配置在 .github/workflows/ci.yml; 远程 CI 是否通过,以 GitHub 实际 run 状态为准。

    #工程与维护

    根包按 model、lexer / parser、validation、raw / frame codec、writer、analysis、diff 和 pure CLI command 分文件;cmd/main 只负责参数、文件、打印和退出。 tests/fixtures 全部为原创最小数据库,许可证与仓库一致;examples 可运行。 源码规模通过审计脚本统计,排除生成接口、构建产物、依赖缓存和 fixture。

    已知限制:extended multiplex、浮点信号、环境变量语义、信号组、多发送节点、 值表尚未结构化支持;writer 是语义序列化,不保持原始排版。 permissive 保存的扩展不进入 codec;多行未知扩展按文本行保存,不能推断其语义。 CLI 官方 IO 包先读取完整文件;解析预算限制进入模型的内容,调用者仍应限制 文件来源与读取大小。Wasm CLI 的错误输出走 stdout;native / JS 走 stderr。

    Roadmap:0.2 扩展 mux 的树与范围;更多独立 oracle / 厂商兼容样本; 有边界的流式文件读取;可选 transport DLC 工具。 贡献指南见 CONTRIBUTING,漏洞反馈见 SECURITY。 参考与原创性声明见 REFERENCES,生态查重见 ECOSYSTEM_REVIEW。License:Apache-2.0。

    DbcError

    pub suberror DbcError {
    Failure(Diagnostic)
    } derive(
    Debug
    )

    Parsing and codec failures carry the same structured diagnostic format.

    Attribute

    pub(all) struct Attribute {
    name : String
    target : ObjectRef?
    value : AttributeValue
    location : SourceLocation
    } derive(Eq,
    Debug
    )

    Attribute assignment/default (None denotes a default).

    AttributeDefinition

    pub(all) struct AttributeDefinition {
    name : String
    scope : Scope
    kind : AttributeType
    location : SourceLocation
    } derive(Eq,
    Debug
    )

    Attribute schema and scope.

    AttributeType

    pub(all) enum AttributeType {
    IntType(Int64, Int64)
    HexType(Int64, Int64)
    FloatType(Double, Double)
    StringType
    EnumType(Array[String])
    } derive(Eq,
    Debug
    )

    Typed attribute grammar; bounds are physical numeric bounds.

    AttributeValue

    pub(all) enum AttributeValue {
    Integer(Int64)
    Real(Double)
    Text(String)
    } derive(Eq,
    Debug
    )

    Attribute data; enum assignments are zero-based integer indices.

    BitCell

    pub(all) struct BitCell {
    position : Int
    owners : Array[String]
    } derive(Eq,
    Debug
    )

    Occupancy of one payload bit; multiple exclusive branch owners are retained.

    ByteOrder

    pub(all) enum ByteOrder {
    Intel
    Motorola
    } derive(Eq,
    Debug
    )

    Intel's start is the LSB; Motorola's start is the MSB (DBC sawtooth).

    CanFrame

    pub(all) struct CanFrame {
    frame_id : FrameId
    payload : Array[Byte]
    } derive(Eq,
    Debug
    )

    In-memory frame; hardware IO is deliberately outside the library.

    Change

    pub(all) struct Change {
    kind : ChangeKind
    message_id : UInt?
    message_name : String?
    signal_name : String?
    field : String
    before : String
    after : String
    breaking : Bool
    } derive(Eq,
    Debug
    )

    One changed semantic field; breaking marks frame interpretation changes.

    ChangeKind

    pub(all) enum ChangeKind {
    Added
    Removed
    Changed
    } derive(Eq,
    Debug
    )

    Kind of semantic revision; no source line comparison is involved.

    CliOutput

    pub(all) struct CliOutput {
    text : String
    exit_code : Int
    } derive(Eq,
    Debug
    )

    Text output and conventional status: 0 success, 1 validation/diff, 2 usage/error.

    Command

    pub(all) struct Command {
    kind : CommandKind
    path : String
    second_path : String?
    message_id : UInt?
    hex : String
    assignments : Array[String]
    raw : Bool
    permissive : Bool
    json : Bool
    } derive(Eq,
    Debug
    )

    Checked CLI arguments, including explicit raw/physical mode.

    CommandKind

    pub(all) enum CommandKind {
    Help
    VersionCommand
    Inspect
    Validate
    Decode
    Encode
    Diff
    Layout
    Normalize
    } derive(Eq,
    Debug
    )

    File CLI commands; parsing is pure and the process boundary owns filesystem IO.

    Comment

    pub(all) struct Comment {
    target : ObjectRef
    text : String
    location : SourceLocation
    } derive(Eq,
    Debug
    )

    Database, node, message or signal comment, preserving UTF-8 text.

    CompiledMessage

    pub struct CompiledMessage {
    message : Message
    positions : Array[Array[Int]]
    selector : Int?
    }

    Validated snapshot with cached bit coordinates for repeated frame processing.

    CompiledMessage::decode

    fn CompiledMessage::decode(self : CompiledMessage, frame : CanFrame) -> DecodedMessage raise DbcError

    Decode using cached layout. Payload length and frame kind must match exactly.

    CompiledMessage::encode

    fn CompiledMessage::encode(self : CompiledMessage, assignments : Array[SignalAssignment], range_policy? : RangePolicy) -> EncodedMessage raise DbcError

    Encode all active signals; inactive, duplicate, missing and unknown inputs fail. Unoccupied payload bits are zero. Raw inputs bypass physical range policy.

    Database

    pub(all) struct Database {
    version : String
    nodes : Array[Node]
    messages : Array[Message]
    comments : Array[Comment]
    attribute_definitions : Array[AttributeDefinition]
    attributes : Array[Attribute]
    unsupported : Array[UnsupportedStatement]
    diagnostics : Array[Diagnostic]
    }

    Semantic database; parsing implementation details are private.

    Database::attribute_value

    fn Database::attribute_value(self : Database, name : String, target : ObjectRef) -> AttributeValue?

    Resolve an explicit assignment or fall back to the attribute's default.

    Database::find_signals

    fn Database::find_signals(self : Database, name : String) -> Array[(Message, Signal)]

    Search signals across messages, returning their containing message too.

    Database::message_by_id

    fn Database::message_by_id(self : Database, id : FrameId) -> Message?

    Query an ID with frame kind (standard and extended IDs may coexist).

    Database::message_by_name

    fn Database::message_by_name(self : Database, name : String) -> Message?

    Query a message name.

    Database::new

    fn Database::new() -> Database

    Create an empty semantic database.

    Database::node_by_name

    fn Database::node_by_name(self : Database, name : String) -> Node?

    Query a declared node.

    DatabaseStats

    pub(all) struct DatabaseStats {
    nodes : Int
    messages : Int
    signals : Int
    standard_messages : Int
    extended_messages : Int
    classic_sized_messages : Int
    fd_sized_messages : Int
    multiplexed_messages : Int
    value_descriptions : Int
    errors : Int
    warnings : Int
    } derive(Eq,
    Debug
    )

    Database inventory, with message byte length separated from transport semantics.

    DecodedMessage

    pub(all) struct DecodedMessage {
    name : String
    frame_id : FrameId
    signals : Array[DecodedSignal]
    diagnostics : Array[Diagnostic]
    } derive(Eq,
    Debug
    )

    A selected message and only its currently active signals.

    DecodedSignal

    pub(all) struct DecodedSignal {
    name : String
    raw : RawValue
    physical : Double
    unit : String
    label : String?
    out_of_range : Bool
    } derive(Eq,
    Debug
    )

    Decoded data preserves exact raw values and reports physical range violations.

    Diagnostic

    pub(all) struct Diagnostic {
    severity : Severity
    code : String
    message : String
    location : SourceLocation
    message_name : String?
    signal_name : String?
    } derive(Eq,
    Debug
    )

    Stable code, readable explanation and source/object context.

    EncodedMessage

    pub(all) struct EncodedMessage {
    frame : CanFrame
    diagnostics : Array[Diagnostic]
    } derive(Eq,
    Debug
    )

    Encoded frame and warnings (range policy and unknown mux).

    FrameId

    pub(all) struct FrameId {
    id : UInt
    extended : Bool
    } derive(Eq,
    Debug
    )

    Normalized arbitration ID, with frame kind participating in identity.

    FrameId::new

    fn FrameId::new(id : UInt, extended? : Bool) -> FrameId raise DbcError

    Create a checked standard (11-bit) or extended (29-bit) identifier.

    FrameId::to_dbc

    fn FrameId::to_dbc(self : FrameId) -> UInt

    Convert a normalized ID to its conventional DBC representation.

    Message

    pub(all) struct Message {
    raw_dbc_id : UInt
    frame_id : FrameId
    name : String
    length : Int
    transmitter : String
    signals : Array[Signal]
    location : SourceLocation
    } derive(Eq,
    Debug
    )

    DBC payload length is bytes, not a transport DLC code.

    Message::new

    fn Message::new(frame_id : FrameId, name : String, length : Int, transmitter? : String, signals? : Array[Signal]) -> Message

    Construct an in-memory message retaining its conventional DBC ID.

    Message::signal_by_name

    fn Message::signal_by_name(self : Message, name : String) -> Signal?

    Query a signal within a message.

    MultiplexInfo

    pub(all) enum MultiplexInfo {
    Always
    Multiplexer
    Branch(Int64)
    } derive(Eq,
    Debug
    )

    Basic multiplexing: one unsigned selector and equality-selected branches.

    Node

    pub(all) struct Node {
    name : String
    location : SourceLocation
    } derive(Eq,
    Debug
    )

    A participant in the database's network topology.

    ObjectRef

    pub(all) enum ObjectRef {
    DatabaseRef
    NodeRef(String)
    MessageRef(UInt)
    SignalRef(UInt, String)
    } derive(Eq, Hash,
    Debug
    )

    Object addressed by comments or attributes.

    ParseLimits

    pub(all) struct ParseLimits {
    max_file_size : Int
    max_line_length : Int
    max_token_length : Int
    max_name_length : Int
    max_messages : Int
    max_signals_per_message : Int
    max_total_signals : Int
    max_attribute_size : Int
    max_nodes : Int
    max_tokens : Int
    max_metadata_records : Int
    } derive(Eq,
    Debug
    )

    Parsed input resource budgets. All lengths use UTF-16 code units.

    ParseLimits::default

    fn ParseLimits::default() -> ParseLimits

    Conservative defaults for untrusted input, including 10,000 signals.

    ParseMode

    pub(all) enum ParseMode {
    Strict
    Permissive
    } derive(Eq,
    Debug
    )

    Strict rejects unknown statements; permissive preserves noncritical ones.

    ParseOptions

    pub(all) struct ParseOptions {
    mode : ParseMode
    limits : ParseLimits
    } derive(Eq,
    Debug
    )

    Mode and explicit resource policy.

    ParseOptions::default

    fn ParseOptions::default() -> ParseOptions

    Strict parsing with bounded inputs.

    RangePolicy

    pub(all) enum RangePolicy {
    Ignore
    Warn
    Reject
    Clamp
    } derive(Eq,
    Debug
    )

    Encoding range behavior. Reject is the default; Clamp is always explicit.

    RawValue

    pub(all) enum RawValue {
    Unsigned(UInt64)
    Signed(Int64)
    } derive(Eq,
    Debug
    )

    Exact raw values retain all 64 bits; doubles are reserved for physical data.

    Scope

    pub(all) enum Scope {
    DatabaseScope
    NodeScope
    MessageScope
    SignalScope
    } derive(Eq,
    Debug
    )

    Scope of attributes; object references use original DBC IDs.

    Severity

    pub(all) enum Severity {
    Error
    Warning
    } derive(Eq,
    Debug
    )

    Severity of a diagnostic; warnings do not invalidate a database.

    Signal

    pub(all) struct Signal {
    name : String
    start_bit : Int
    bit_length : Int
    byte_order : ByteOrder
    signed : Bool
    factor : Double
    offset : Double
    minimum : Double
    maximum : Double
    unit : String
    receivers : Array[String]
    multiplex : MultiplexInfo
    values : Array[ValueDescription]
    location : SourceLocation
    } derive(Eq,
    Debug
    )

    Integer DBC signal with complete core layout/scaling metadata.

    Signal::new

    fn Signal::new(name : String, start_bit : Int, bit_length : Int, byte_order? : ByteOrder, signed? : Bool, factor? : Double, offset? : Double, minimum? : Double, maximum? : Double, unit? : String, receivers? : Array[String], multiplex? : MultiplexInfo) -> Signal

    Define a signal; semantic validation remains available for programmatic models.

    SignalAssignment

    pub(all) struct SignalAssignment {
    name : String
    value : SignalValue
    } derive(Eq,
    Debug
    )

    One named input assignment.

    SignalValue

    pub(all) enum SignalValue {
    Physical(Double)
    Raw(RawValue)
    Label(String)
    } derive(Eq,
    Debug
    )

    Explicit physical/raw/label assignment; never infer meaning from a number.

    SourceLocation

    pub(all) struct SourceLocation {
    line : Int
    column : Int
    offset : Int
    } derive(Eq,
    Debug
    )

    One-based line/column and zero-based UTF-16 code-unit offset.

    UnsupportedStatement

    pub(all) struct UnsupportedStatement {
    keyword : String
    text : String
    location : SourceLocation
    } derive(Eq,
    Debug
    )

    Preserved permissive-mode extension with an explicit warning.

    ValueDescription

    pub(all) struct ValueDescription {
    raw : Int64
    label : String
    } derive(Eq,
    Debug
    )

    Named raw value, independent of factor/offset.

    cli_help

    fn cli_help() -> String

    Usage text shared by executable and CLI tests.

    compile_message

    fn compile_message(message : Message) -> CompiledMessage raise DbcError

    Validate codec semantics once and snapshot mutable arrays before reuse.

    decode_frame

    fn decode_frame(db : Database, frame : CanFrame) -> DecodedMessage raise DbcError

    Look up the frame identity in a database and decode its active signals.

    decode_signal_raw

    fn decode_signal_raw(signal : Signal, payload : Array[Byte]) -> RawValue raise DbcError

    Decode an exact signed or unsigned integer from a checked DBC layout.

    diff_database

    fn diff_database(old : Database, new : Database) -> Array[Change]

    Compare message identity, signal semantics and global metadata, ignoring source spans.

    encode_message

    fn encode_message(message : Message, assignments : Array[SignalAssignment], range_policy? : RangePolicy) -> EncodedMessage raise DbcError

    Encode a semantic message with explicit signal values and range policy.

    encode_signal_raw

    fn encode_signal_raw(signal : Signal, payload : Array[Byte], raw : RawValue) -> Unit raise DbcError

    Write only the signal's occupied bits. Validate all inputs before mutating.

    execute_cli

    fn execute_cli(command : Command, source : String, second_source? : String) -> CliOutput raise DbcError

    Execute a parsed CLI command on caller-supplied file contents; no IO in core.

    frame_hex

    fn frame_hex(payload : Array[Byte]) -> String

    Render payload bytes as uppercase hex without separators.

    frame_id_from_dbc

    fn frame_id_from_dbc(raw : UInt) -> FrameId raise DbcError

    Interpret bit 31 as DBC's extended-ID flag; bits 29/30 are rejected.

    inspect_database

    fn inspect_database(db : Database) -> DatabaseStats

    Count semantic objects and actual validation findings.

    message_layout

    fn message_layout(message : Message) -> Array[BitCell] raise DbcError

    Compute a real bit layout for 0..64 bytes without hardcoded demo coordinates.

    parse_cli_arguments

    fn parse_cli_arguments(args : Array[String]) -> Command raise DbcError

    Parse user arguments without reading files. IDs use decimal or 0x notation.

    parse_dbc

    fn parse_dbc(source : String) -> Database raise DbcError

    Parse the supported DBC subset using strict syntax and default budgets.

    parse_dbc_with_options

    fn parse_dbc_with_options(source : String, options : ParseOptions) -> Database raise DbcError

    Bounded parsing. Semantic diagnostics remain inspectable via validation.

    parse_frame_hex

    fn parse_frame_hex(text : String) -> Array[Byte] raise DbcError

    Parse hexadecimal payload bytes, rejecting odd lengths and invalid characters.

    physical_to_raw

    fn physical_to_raw(signal : Signal, physical : Double, range_policy? : RangePolicy) -> RawValue raise DbcError

    Physical input quantizes to nearest raw integer, ties away from zero. Values beyond Double's exact integer range require the raw API.

    raw_to_physical

    fn raw_to_physical(signal : Signal, raw : RawValue) -> Double raise DbcError

    Checked linear conversion; exact raw values remain available separately.

    render_diagnostic

    fn render_diagnostic(d : Diagnostic) -> String

    Render stable diagnostic context for terminals/logs.

    render_layout

    fn render_layout(message : Message) -> String raise DbcError

    Display each byte from bit 7 through bit 0; '.' denotes unoccupied bits.

    signal_bits

    fn signal_bits(signal : Signal, payload_length : Int) -> Array[Int] raise DbcError

    Ordered payload positions, LSB first for Intel and MSB first for Motorola. Rejects invalid widths/start positions before allocating or shifting.

    signal_overlap

    fn signal_overlap(a : Signal, b : Signal, payload_length : Int) -> Array[Int] raise DbcError

    Return the exact intersecting payload bits, respecting mutually exclusive branches.

    validate_database

    fn validate_database(db : Database) -> Array[Diagnostic]

    Collect all semantic errors without throwing, including multiplex-aware overlaps.

    write_dbc

    fn write_dbc(db : Database) -> String raise DbcError

    Deterministic semantic serialization. Reject invalid models rather than losing data. Permissively preserved extensions are written verbatim after structured content.