Metadata-Version: 2.4
Name: can-api-generator
Version: 2.0.0
Summary: Program to generate a 'signal' style CAN API in C
Project-URL: Documentation, https://sr.ht/~laplace/can-api-generator#readme
Project-URL: Issues, https://sr.ht/~laplace/can-api-generator/issues
Project-URL: Source, https://sr.ht/~laplace/can-api-generator
Author-email: Alexande Krishna-Becker <nabla.becker@mailbox.org>
License-Expression: LGPL-3.0-only
License-File: LICENSE.txt
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.8
Requires-Dist: can-db-model>=0.1
Requires-Dist: cantools
Requires-Dist: click
Requires-Dist: jinja2
Requires-Dist: rich
Description-Content-Type: text/markdown

# can-api-generator

[![PyPI - Version](https://img.shields.io/pypi/v/can-api-generator.svg)](https://pypi.org/project/can-api-generator)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/can-api-generator.svg)](https://pypi.org/project/can-api-generator)

-----

## Table of Contents

- [Installation](#installation)
- [License](#license)
- [Overview](#overview)
- [Two ways to decode a bus](#two-ways-to-decode-a-bus)
- [Features of the library](#features-of-the-library)
- [Additional features](#additional-features)
  - [Global RX](#global-rx)
  - [Periodic TX](#periodic-tx)
  - [Encode All Signals](#encode-all-signals)
  - [TI C2000 compatibility](#ti-c2000-compatibility)
  - [Double-buffered signal access](#double-buffered-signal-access)
  - [RX/TX callback context](#rxtx-callback-context)
  - [Message signals as RX callback argument](#message-signals-as-rx-callback-argument)
- [Example Use](#example-use)
- [Dynamic decoding](#dynamic-decoding)
  - [Preparing a database (`cbdb`)](#preparing-a-database-cbdb)
  - [The dynamic-decoder module (`dyndec`)](#the-dynamic-decoder-module-dyndec)
  - [cbdb binary format](#cbdb-binary-format)
- [Command-line reference](#command-line-reference)
  - [`api`](#api)
  - [`cbdb`](#cbdb)
  - [`dyndec`](#dyndec)

## Installation

```console
pip install can-api-generator
```

## License

`can-api-generator` is distributed under the terms of the [LGPL-3.0-only](https://spdx.org/licenses/LGPL-3.0-only.html) license.

## Overview
The `can-api-generator` is a program to generate a C implementation for a 'signal' style CAN API from a database describing the signals and their arrangement
in to messages sent over the can bus. The database may be a [KCD](http://kayak.2codeornot2code.org/) (XML) file or a [`can-db-model`](https://pypi.org/project/can-db-model/)
YAML file; the two are interchangeable everywhere a `DB_FILE` is accepted.
A signal represents a value that is shared among all nodes of the bus. Due to the fact that CAN is based on packets, which are referred
to as frames, multiple signals may to be grouped into a common message which is then transmitted as a can frame. Different messages are distinguished via the arbitration id
that is part of the can frame.
Thus the arbitration ID acts as a content address (think address of memory shared over the can bus), and not a source or destination address.

A Frames are sent out by the node that modifies a value that is contained in the Frame.
A KCD file may encompass multiple nodes that have messages sent between them. To minimize the code generated, a Node needs to be specified so that only the messages that
are transmitted or received by this node. Flags allow generating the additional code if needed.

The API is only concerned with frames that are defined by the KCD file from which the API was generated. Other frames may be transmitted over the bus and are
outside the scope of this API. The code simply decodes what it can identify and only produces frames that are defined as 'tx' from the nodes

### Practical uses
In practice frames are often sent periodically, even if no change has occured, to provide redundancy and fault tolerance to the system.
Even though any node in the network may send any frame, in practice only a single node on a bus will update certain signals, thus avoiding the problems that
arise when multiple nodes modify the same signal.

Can frames generated by this library are compatible to the DBC and KCD definitions and can thus be decoded by industry standard tools like busmaster/canoe/PeakCan etc.

## Two ways to decode a bus

The tool is a group of subcommands (run `can-api-gen --help` to list them). They cover two different decoding strategies:

* **Static, per-node API** (`can-api-gen api`) — bakes one node's message set into C at generation time. Every message becomes a struct with `encode`/`decode` functions,
  and the message set is fixed at compile time. This is the smallest and fastest option and is what most firmware wants; it is the subject of the [Features](#features-of-the-library)
  and [Example Use](#example-use) sections below.
* **Dynamic, database-driven decoder** (`can-api-gen cbdb` + `can-api-gen dyndec`) — generates a *fixed* C module once per firmware that decodes frames at runtime against a
  compiled descriptor blob (a `.cbdb`). Because the message set lives in the blob rather than in the code, a device can be re-provisioned for a different database without
  recompiling. One primary use case for this is bus observers (loggers, gateways, etc.). See [Dynamic decoding](#dynamic-decoding).

# Features of the library
---
* One struct per message: The struct contains the signals that are explicitly defined in the can frame (including multiplexes). This allows a single message struct
  to fully define a frame (the ID and length are declared as defines). (see the example for explicit code)
* Decodeing/Encoding functions: For each Message a decode and encode functions is generated that allow to translate the struct into a 'CanFrame' and decode a 'CanFrame' struct in to the
  message struct that contains the values encoded as native data types. The encoding function also sets the length and the arbitration id of the can frame.

## Additional features
---

This generator may generate additional code that was found useful in many common cases:
* a **global rx** function (enabled with `-r`/`--global-rx`): takes a received can frame, matches it against the message definitions in the API, and decodes it into
  a 'global' api struct containing the signals for every message this node consumes.
* a **periodic tx** function (enabled with `-p`/`--periodic-tx`): schedules messages to be sent automatically. Auxiliary information is stored, similar to the
  'global rx', in a TxContext struct. It allows for changing the period of the transmission on the fly and also allows a tx callback to be called upon
  transmission of any particular message. Transmission may also be disabled per message.
* an **encode-every-signal** helper per message (enabled with `-a`/`--encode-all-signals-func`): fills a caller-provided buffer with the minimum number of frames
  needed to cover every signal in a multiplexed message at least once. See [Encode All Signals](#encode-all-signals).
* an **immediate-send override** for the periodic tx path (enabled with `--send-now`): adds a per-message `send_now` flag that fires the next `periodic_tx` call
  regardless of schedule or enable state. Useful for publishing critical signal changes without waiting out the rest of the period.
* **TI C2000 compatible** codegen (enabled with `--ti-compatible`): hoists local variable declarations to the top of encode/decode functions so the generated C
  compiles under the TI C2000 compiler's strict C90 rules. See [TI C2000 compatibility](#ti-c2000-compatibility).
* **double-buffered signal access** (enabled with `--double-buffered`): emits two storage slots and front/back pointers per message so that an code inside an ISR or
  second thread is never exposed to a partially-decoded struct. A periodic tx encoder also never reads a struct that user code is mid-update. See
  [Double-buffered signal access](#double-buffered-signal-access).

### Global RX
Global RX generates the code needed to take any received can frame and decode it in to the API if the message matches a message defined in the API. This allows the application to
call a single function once per received frame and have access to all signals shared via the CAN bus system by simply reading the signals in the RX struct.

The global rx funciton needs to be called with the 'current time'. which for MCUs is most often represented by a count of the milliseconds passed since power up.

The global RX struct contains a `MsgRxContext` struct per message. This struct stores configure parameters and read back metadata about the message.
The `MsgRxContext` has the following form:
```c
struct MsgRxContext {
    uint16_t ignore;
    uint16_t valid;
    uint64_t last_rx;
    void (*rx_callback)(void);
};
```
* When the `ignore` value is set to 1, the received message is not decoded even if it's arbitration id and length matched with the message definition.
* The `valid` field is set to 1 every time the corresponging message is received. This value may be written to 0 by the application and allows the application
  to simply check if the information in the message has been written to. This is useful when a critical section is to be entered that requires other programs
  to have set configuration parameters that need to be valid before the critical section is entered.
* `last_rx` field stores the value of the 'clock' the last time this message was received. this may be used for diagnostics and statistics
* `rx_callback` is a function that is called after the corresponding message has been successfully decoded. This function optionally takes a `void *context` that allows
  to parametrize the function with data or point back to a struct containing the global rx struct'. The optional context is set by ``--rx-callback-context`

By default (with `-r` alone) global RX is **callback-only**: `RxSignals` holds only a `MsgRxContext` per message (no `_signals` storage), and decoded values are
delivered solely through the per-message RX callback, which receives a `void *` to a temporary holding the just-decoded signal struct. That temporary is zero-initialized
before decoding, so a multiplexed message's inactive-group fields read as 0 rather than stack garbage, and the bytes the callback sees match what a store-based API would
hold for the same frame. A matched frame with no registered callback updates `last_rx` and returns success without decoding. Pass `--rx-signal-store` to generate the
permanent per-message store instead (with the `valid` field and dot/pointer signal access).

In callback-only mode `MsgRxContext` omits `valid` (there is no store to validate). Pass `--with-rx-valid` to re-add and set `valid` on each matched frame, so
application code can share one `struct MsgRxContext` layout across a store-based API and a callback-only API.

### Periodic TX
The periodic TX is the counterpart to the 'global RX'. It takes care to schedule the transmission of periodic frames at the proper time. Periodic frames are pretty
common on CAN bus systems and provide robust communication of critical data without the need for explicit synchronization mechanisms. Many controllers, often found in
automotive applications send the same message periodically. This allows for 'quasi-analog' values to be transmitted via the CAN bus. When enabling the `-p`/`--periodic-tx` flag
the can api generator generates a `<api>_periodic_tx` function that properly schedules periodic messages, an `<api>_periodic_tx_init` function that seeds the per-message
contexts from the `cycle_time` values in the KCD, and an `<api>_get_msg_tx_context_by_id` helper for looking up a message context by arbitration id. To configure the
periodic transmission the `TxSignals` struct, that is generated along with the periodic tx function, provisions one `MsgTxContext` struct for each message. The
`MsgTxContext` is defined as following:
```c
struct MsgTxContext {
    uint32_t arb_id;
    uint16_t enable;
    uint64_t last_tx;
    uint32_t period;
    uint32_t offset;
    uint32_t offset_seed;
    void *(*tx_callback)(void); // returns the struct to encode in callback-only mode; +void* context with --tx-callback-context
    uint16_t send_now;          // only present when compiled with --send-now
};
```
* `arb_id` is the arbitration id of the message the context controls. It is seeded by `<api>_periodic_tx_init` so the application can look up a context by id via
  `<api>_get_msg_tx_context_by_id`.
* `enable` needs to be set to 1 for the message to be sent periodically. This allows the application to reduce bus load by silencing messages depending on the situation.
  Init sets this to 1 for messages that declare a `cycle_time` in the KCD and 0 for the rest.
* `last_tx` is updated by the periodic tx function every time a frame fires. Combined with `period`, it gates retransmission so a frame cannot fire more often than once
  per period.
* `period` configures the time between two messages (in MCU clock units). Most MCUs use a 1ms tick so this value often ends up being the number of milliseconds between
  two periodic transmissions. The application may change this at runtime via `<api>_update_tx_period(ctx, new_period)`, which also recomputes `offset` so the phase stays
  consistent.
* `offset` is a value that adds an offset to the transmission time. Multiple messages will often have the same period (say 100ms). Without the offset the CAN bus becomes
  quite 'bursty' as every 100ms many messages are encoded at once and require transmission. This may lead to dropped messages and bus load problems even at low average
  load. This offset spreads out the transmission of messages through the entire transmission period, reducing pileup of messages.
* `offset_seed` is a per-message constant seeded at generation time so that different messages with the same period end up at different phases inside that period without
  the application having to pick offsets by hand.
* `tx_callback` is called for a scheduled message **before** the frame is encoded, and its return type is `void *`. In callback-only mode (the default) the pointer it
  returns is the signal struct to encode: return a `struct <Message> *` and that struct is encoded into the outgoing frame. In store mode (`--tx-signal-store`) the
  return value is ignored and the callback instead does a last-minute in-place update of the store before it is encoded.
* `send_now` is only emitted when the generator is invoked with `--send-now`. Setting this flag to 1 makes the next `<api>_periodic_tx` call emit the corresponding frame
  immediately, regardless of the periodic schedule or the `enable` bit. The function auto-clears the flag after firing, so each set fires exactly once. Useful when a
  critical signal has just changed and you want it on the bus on the very next tick instead of waiting out the remainder of the period.

By default (with `-p` alone) periodic TX is **callback-only**: `TxSignals` holds only a `MsgTxContext` per message (no `_signals` storage), and each scheduled message's
`tx_callback` produces the frame. When a message's schedule fires, `<api>_periodic_tx` clears `send_now` and updates `last_tx` (so the slot is consumed and does not
busy-retry), then calls the callback only if it is registered; it encodes and reports the frame only if the callback returns a non-`NULL` struct pointer. A missing
callback or a `NULL` return sends nothing for that message and falls through, so another scheduled message whose callback returns data can still be sent on the same call.
Multiplex variant scheduling is the application's responsibility in this mode (the callback returns whichever variant it wants to send).

In plain callback-only mode (no `--msg-as-tx-callback-arg`) the callback returns a pointer to the signal struct to be encoded, so **the application must provide the
storage that pointer refers to**, and it must outlive the callback return so `<api>_periodic_tx` can encode from it — a `static`/global struct, not a stack local that goes
out of scope. A single buffer shared by all TX callbacks, cast to the appropriate `struct <Message>` per message, works well and keeps the footprint small, since encode
calls are serialized (one `<api>_periodic_tx` runs at a time).

Passing `--msg-as-tx-callback-arg` removes that burden in callback-only mode: `<api>_periodic_tx` stack-allocates the message struct itself and passes a `void *` to it as
the callback's first argument (context second, if `--tx-callback-context`). The callback fills that struct and returns the same pointer (or `NULL` to skip). Because the
generator owns the storage, no application-provided buffer is needed.

Pass `--tx-signal-store` to generate the permanent per-message store instead — the pre-callback-only behavior, where the callback edits the store in place (its return
value ignored), the generator handles multiplex rotation, and (with `--double-buffered`) the per-message `<api>_<msg>_commit` helper is emitted. With the store,
`--msg-as-tx-callback-arg` passes a `void *` to that message's store struct as the callback's first argument (context second, if `--tx-callback-context`), so the callback
need not look the struct up before editing it in place.

### Encode All Signals
Some messages use multiplexes combining many signals in to a single message. This may be particularly true for configuration messages that have many related parameters and use
multiplexes to fit all parameters in to a single message using many mux groups. If the entire config should be read out and the read back happens periodically, the supervisory controller
must wait a long time until the multiplex is cycled throug all it's mux groups. To reduce the time it takes to read back this kind of message, the `encode all messages` function is used.
This function generates a group of CanFrames and stores them in to a buffer provided by the caller. This buffer can then be flushed by the application. The size of the buffer required for
a given message is declared as a preprocessor define in the library header file. To receive all frames generated by the function the caller needs to provide a buffer of at least that size.

the 'encode all signals' function is generated when the tool is passed the `-a` option.

### TI C2000 compatibility
Some target compilers — notably the TI C2000 toolchain — refuse C99-style "declaration after statement" code, which requires all local variables to be declared at
the top of a block before any statements. The default code generator emits variable declarations inline inside `switch`/`case` bodies for multiplexed messages, which
the TI compiler rejects. Passing `--ti-compatible` changes the codegen so every per-signal temporary is hoisted to the top of the enclosing encode/decode function, all
mux variant assignments become pure stores instead of declarations, and the loop counter in the `encode_every_signal` helper is predeclared before its initializer.
The resulting source compiles cleanly under both `-std=c99` and `-std=c90 -Wdeclaration-after-statement -Werror=declaration-after-statement`, matching what the TI
C2000 compiler will accept.

### Double-buffered signal access
Double buffering applies to the **stored** signal interfaces, so it takes effect on the RX side together with `--rx-signal-store` and on the TX side together with
`--tx-signal-store`; in the callback-only default there is no store to double-buffer and `--double-buffered` has no effect on that side.

When an RX frame is decoded from an ISR while the main loop reads signals (or the converse for TX, where the user updates the struct while `periodic_tx` encodes
it), a reader can observe a half-written struct and act on inconsistent data. Passing `--double-buffered` (with the relevant store flag) changes the generated global RX
and periodic TX structs so every message carries two storage slots and two pointers, and the dispatcher / commit helper publishes new values via an atomic pointer swap
instead of an in-place update:

```c
struct DUTRxSignals {
    struct MsgRxContext message_1_context;
    struct Message1 message_1_signals_storage[2];
    struct Message1 * volatile message_1_signals;       /* reader view */
    struct Message1 * message_1_signals_back;            /* dispatcher decodes here */
    /* ... */
};

struct DUTTxSignals {
    struct MsgTxContext message_1_context;
    struct Message1 message_1_signals_storage[2];
    struct Message1 * message_1_signals;                 /* staging — user writes here */
    struct Message1 * volatile message_1_signals_committed; /* periodic_tx reads this */
    /* ... */
};
```

Semantics:

* Access to the signal sub-struct becomes pointer-style instead of dot-style. Read the latest decoded RX value as
  `rx_signals.message_1_signals->field`, and write a pending TX value as `tx_signals.message_1_signals->field`.
* `<api>_global_rx_init(&rx_signals)` wires up the front/back pointers and zeroes both storage slots. Call it once before `<api>_process_received_frame`.
* `<api>_periodic_tx_init(&tx_signals, now)` wires up the staging/committed pointers in addition to the usual context init. Call it once before
  `<api>_periodic_tx`.
* For each TX message the generator emits a per-message commit helper, `<api>_<message_name>_commit(&tx_signals)`. It swaps the staging and committed
  pointers — after the call, the values the user just wrote into staging are live on the `committed` side, and the new staging slot contains the previous
  `committed` snapshot. The swap is cheap (two pointer writes) and allocation-free.
* The RX dispatcher decodes into `message_<n>_signals_back` and, on success, swaps `message_<n>_signals` ↔ `message_<n>_signals_back` inside a short inner
  block. A decode that aborts mid-way never publishes a partial struct — the front pointer keeps pointing at the previously-good decode.
* Because commits are pointer-swap rather than memcpy, the new staging slot holds the previous committed snapshot. Code that updates a subset of fields per
  commit therefore still publishes coherent messages, but code that relies on staging being zero after commit must zero it explicitly.

Ordering guarantee: the front/committed pointers are declared `volatile` so the compiler will not reorder the pointer publish across reads in the same
translation unit. This is sufficient for a single-core MCU where the dispatcher runs in an ISR and readers run in the main loop, which is the target use
case. On weakly-ordered multicore hosts, user code is responsible for inserting the appropriate memory barrier between the decode writes and the pointer
publish, and between reading the pointer and dereferencing it — the generator does not emit any barriers itself.

### RX/TX callback context

Both the [Global RX](#global-rx) and [Periodic TX](#periodic-tx) paths let you register a per-message callback. Without a context flag those callbacks take no arguments
(`void (*rx_callback)(void)` / `void *(*tx_callback)(void)`), which means a callback that needs to reach application state has to find it through a global.

Passing `--rx-callback-context` (and/or `--tx-callback-context`) adds a `void *` context parameter:

* `--rx-callback-context` adds a `void *rx_callback_context` field to each `MsgRxContext` and appends a `void *` context parameter to the callback. After a message
  is decoded, its callback is invoked with `rx_callback_context`.
* `--tx-callback-context` adds a `void *tx_callback_context` field to each `MsgTxContext` and appends a `void *` context parameter to the callback. Before a message's
  frame is encoded, its callback is invoked with `tx_callback_context`.

Set the context pointer once (typically to a struct the callback needs, or back to the RX/TX signals struct itself) and each firing threads it through without a global.
The flags are independent: enable only the side you need. Without the corresponding flag the callback keeps its zero-argument signature.

### Message signals as RX callback argument

By default the RX callback receives no reference to the frame that fired it — a callback wanting the decoded values reads them out of the global RX struct by name.
Passing `--msg-as-rx-callback-arg` instead hands the callback a `void *` pointing at that message's just-decoded signal struct:

* `--msg-as-rx-callback-arg` widens the RX callback to `void (*rx_callback)(void *)` and, after a message is decoded, invokes it with a pointer to the message's signal
  struct. The user casts it back, e.g. `struct Message1 *m = arg;`.
* It composes with `--rx-callback-context`: with both flags the callback becomes `void (*rx_callback)(void *, void *)`, called as
  `rx_callback(<signals>, rx_callback_context)` — signals first, context second.
* Under `--double-buffered` the pointer is the live front buffer after the pointer swap, so the callback always sees a fully-decoded struct.

`struct MsgRxContext` remains a single shared type, which is why the signal struct is passed as `void *` rather than a typed pointer.

## Example Use

As an example the following KCD definition of all messages on a CAN bus is converted:
```xml
<?xml version="1.0" ?>
<NetworkDefinition xmlns="http://kayak.2codeornot2code.org/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="KCD_Definition.xsd">
  <Document name="can-api-generator-test-1" version="1" author="Alexander Krishna-Becker" company="radiation.systems" date="2025-11-01"/>
  <Node id="0" name="DUT"/>
  <Node id="1" name="other"/>
  <Bus name="ControlBus" baudrate="500000">
    <Message id="0x0" length="1" name="Message 1" interval="100" format="extended">
      <Notes>Test Note 1</Notes>
      <Producer> <NodeRef id="0"/> </Producer>
      <Signal name="Signal 1" offset="0" length="1">
        <Notes>Signal 1 of Message 1 of the DUT</Notes>
        <Consumer> <NodeRef id="1"/> </Consumer>
        <Value type="unsigned"/>
      </Signal>
      <Signal name="Signal 2" offset="1" length="7">
        <Notes>Signal 1 of Message 1 of the DUT</Notes>
        <Consumer> <NodeRef id="1"/> </Consumer>
        <Value type="signed"/>
      </Signal>
    </Message>

    <Message id="0x1" length="2" name="Message 2" interval="100" format="extended">
      <Notes>Test Note 1</Notes>
      <Producer> <NodeRef id="0"/> </Producer>
      <Signal name="Signal 3" offset="0" length="16">
        <Notes>Signal 1 of Message 1 of the DUT</Notes>
        <Consumer> <NodeRef id="1"/> </Consumer>
        <Value type="unsigned"/>
      </Signal>
    </Message>
  </Bus>
</NetworkDefinition>
```
The result of the conversion are the decode/encode functions along with the structs to hold the application accessible signals.

To convert the KCD file into C code the following command is used:

```bash
can-api-gen api example.kcd my_api DUT -a
```
`api` selects the static per-node generator (see [Two ways to decode a bus](#two-ways-to-decode-a-bus)). The first argument is the path to the database file (`.kcd` or `.yaml`).
The second argument is the name of the API. This name determins the name of the C and H file headers as well as parts of the name of the struct and functions.
This is done so that multiple APIs may be used within the same project without interfering with each other. The last entry is the name of the Device for which the can api is to be generated.
This is needed as multiple devices may be described on a single can Bus. The KCD file then needs to describe what signal is sent by a device, as well as which signal a device is listening for.
The interface generator uses this info to only generate messages that are relevant to the particular device. The following is the resulting header:

```c
#ifndef CAN_API_MY_API
#define CAN_API_MY_API
#include "canframe.h"

// If all signals in a message need to be encoded then it will need
// a buffer of at least the size defined here to hold the frames to set each signal
// contained in the message at least once
#define MESSAGE_1_MIN_FRAMES_FOR_ALL_SIGNALS 1
#define MESSAGE_2_MIN_FRAMES_FOR_ALL_SIGNALS 1



struct Message1 {
    uint16_t signal_1;
    int16_t signal_2;
};
struct Message2 {
    uint16_t signal_3;
};

void my_api_message_1_decode(struct Message1 *msg, CanFrame *input_frame);
void my_api_message_1_encode(struct Message1 *msg, CanFrame *output_frame);
uint16_t my_api_message_1_encode_every_signal(struct Message1* msg, CanFrame *output_frame_buf, uint32_t buf_size);
void my_api_message_2_decode(struct Message2 *msg, CanFrame *input_frame);
void my_api_message_2_encode(struct Message2 *msg, CanFrame *output_frame);
uint16_t my_api_message_2_encode_every_signal(struct Message2* msg, CanFrame *output_frame_buf, uint32_t buf_size);
#endif
```

As can be seen it provides `encode` and `decode` functions for each message that interacts with the DUT. It also emits an `encode_every_signal` helper per message
(because the example was generated with `-a`), the minimum buffer-size defines the caller needs for that helper, and the message structs themselves. The companion
`canframe.h` file is written next to the library header; it declares the small `CanFrame` struct the API uses as an I/O boundary with the application's can driver.

To enable the additional features, pass the corresponding flags at generation time:

```bash
# Generate a full-featured API with global rx, periodic tx, encode-every-signal,
# send-now support, and TI C2000 compatible codegen.
can-api-gen api example.kcd my_api DUT -r -p -a --send-now --ti-compatible
```

## Dynamic decoding

The [static `api`](#example-use) generator bakes one node's messages into C at build time. That is ideal for a node deployed into a 'fixed' environment like a car or industrial
system. Development and Diagnostic Tools however need to be adaptable to many environments on the fly. Dynamic decode allows for the firmware to be permanently on device while the
a runtime loadable database allows for adaptation of the tool to the environment it is supposed to be used in. As the tool is likely used in conjuntion with a HMI or other equipment
that equipment can then provision the right database on the device. The database generation step also generates a sidecar json file that can be consumed by upstream tools to enrich the
decoded data. Adaptability however, trades runtime overhead for this flexibility. Dynamic decoding currently simply decodes everything it receives over the bus. Modern MCUs are plenty fast
enough for this. In the case of a logger/debugger/monitor it is also more of a feature.

There are two subcommands, used together:

1. **`cbdb`** — the *database preparation* step. Compiles a `.kcd`/`.yaml` database into a compact binary descriptor table (a `.cbdb`) plus a JSON sidecar for host-side clients.
2. **`dyndec`** — the *runtime module*. Generates a fixed, database-independent C module that decodes frames against a `.cbdb` at runtime. Generated once per firmware;
   a new database is just a new `.cbdb`, no recompile.

### Preparing a database (`cbdb`)

```bash
can-api-gen cbdb example.kcd my_bus
```

This reads the database and writes two files:

```console
Wrote my_bus.cbdb (124 bytes, 3 signals) and my_bus_ids.json
```

* **`my_bus.cbdb`** — the binary descriptor table (see [cbdb binary format](#cbdb-binary-format)) that the on-device `dyndec` runtime consumes. This is the artifact you
  ship to the device: flash it, store it on a filesystem, or push it over the air. Provisioning a device for a new bus is nothing more than replacing this file.
* **`my_bus_ids.json`** — a sidecar for host-side / client code that never parses the binary blob. It maps each resolved `message.signal` name to the numeric `signal_id`
  the firmware reports, and additionally records per-message metadata (arbitration id, extended-id flag, DLC, signal membership) and any presentation info the database
  defines (unit, physical min/max, enum value labels). A dashboard app or logger uses it to label raw bus traffic symbolically without embedding the database itself.

```json
{
  "format_version": 1,
  "kcd_sha256": "f5e78b31d5a666ed68256cd43f341fbd42ba34d8518bea11f289111535be458a",
  "signals": {
    "message_1.signal_1": 3003935338,
    "message_1.signal_2": 2987157719,
    "message_2.signal_3": 1415019715
  },
  "messages": {
    "message_1": { "arb_id": 0, "extended": true, "dlc": 1,
                   "signals": ["message_1.signal_1", "message_1.signal_2"] },
    "message_2": { "arb_id": 1, "extended": true, "dlc": 2,
                   "signals": ["message_2.signal_3"] }
  },
  "signal_info": {}
}
```

A `signal_id` is the 32-bit FNV-1a hash of the resolved `"<message>.<signal>"` name (both names converted to C identifiers). The `cbdb` step aborts if two signals in a
database hash to the same id. The `kcd_sha256` field is the SHA-256 of the source database file; the same hash is embedded in the `.cbdb` header and is retrievable on
the device via `dyndec_kcd_hash()`, so a client can confirm the device's blob matches the sidecar it is holding.

### The dynamic-decoder module (`dyndec`)

```bash
can-api-gen dyndec dyndec
```

Writes `dyndec.c`, `dyndec.h`, and a sibling `canframe.h`. The module contains no database — it is generated once and reused for every `.cbdb`. As with the static API, the
`-im`/`-lm`/`-pm` flags rename the `CanFrame` members so the module drops into an existing project's frame struct.

The runtime is driven through a `DyndecContext` and a handful of functions:

```c
int      dyndec_init(DyndecContext *ctx, const uint8_t *cbdb, uint32_t cbdb_size,
                     DyndecMsgRef *msg_index, uint16_t max_msgs,
                     float *values, uint16_t max_signals);
int      dyndec_process_frame(DyndecContext *ctx, const CanFrame *frame);
int32_t  dyndec_lookup(const DyndecContext *ctx, uint32_t signal_id);
float    dyndec_get_value(const DyndecContext *ctx, int32_t value_idx);
uint16_t dyndec_signal_count(const DyndecContext *ctx);
uint32_t dyndec_signal_id_at(const DyndecContext *ctx, uint16_t value_idx);
const uint8_t *dyndec_kcd_hash(const DyndecContext *ctx);
```

Usage:

* **Provide storage and initialise.** The decoder does not allocate. The caller supplies a `DyndecMsgRef` array (one entry per message) and a `float` value store (one
  slot per signal); `dyndec_init` validates the blob's magic, format version, and size, then builds the message index and zeroes the value store. It fails with a negative
  error code (e.g. `DYNDEC_ERR_MAGIC`, `DYNDEC_ERR_VERSION`, `DYNDEC_ERR_CAPACITY`) if the blob is malformed or your arrays are too small for its message/signal counts.
  The `.cbdb` memory must stay valid and unchanged for the lifetime of the context.
* **Feed frames.** Call `dyndec_process_frame(&ctx, &frame)` once per received frame. It binary-searches the message table by arbitration id and returns the matched
  message's index, or `-1` if the id is not in the database. Every non-muxed signal of the matched message is stored as its **physical (scaled) value** — a `float` after
  applying scale and offset. A muxed signal is updated only when the frame's multiplexer selector matches that signal's mux group.
* **Read signals.** Resolve a `signal_id` (from the sidecar) to a store index with `dyndec_lookup`, then read the latest physical value with `dyndec_get_value`. `-1` from
  `dyndec_lookup` means the id is not in this database. To enumerate everything the blob carries, iterate `0 .. dyndec_signal_count()` and pair `dyndec_signal_id_at(i)`
  with `dyndec_get_value(&ctx, i)`.
* **Verify provisioning.** `dyndec_kcd_hash` returns the 32-byte SHA-256 stored in the blob header, matching the sidecar's `kcd_sha256`.

Because both `dyndec_lookup` and the value store are indexed by the signal's position in the cbdb, decoding is a bounded, allocation-free pass suitable for an ISR or a
tight bridge loop.

### cbdb binary format

The `.cbdb` is a flat, little-endian table. Messages are sorted ascending by arbitration id so the decoder can binary-search; within a message, mux selector signals come
first, then plain signals, then muxed signals, so a single decode pass has every selector's raw value in hand before it evaluates mux membership. The current format
version is `1` (`DYNDEC_CBDB_FORMAT_VERSION`).

| Section | Size | Fields |
| --- | --- | --- |
| Header | 48 B | `magic` `4s` = `"CDB1"`; `format_version` `u16` = 1; `msg_count` `u16`; `signal_count` `u16`; reserved `u16` = 0; `cbdb_size` `u32` (total bytes incl. header); `kcd_sha256` `32s` |
| Per message | 8 B | `arb_id` `u32` (bit 31 = extended-id flag); `dlc` `u8`; `n_signals` `u8`; reserved `u16` = 0 |
| Per signal | 20 B | `signal_id` `u32` (FNV-1a of `"<msg>.<sig>"`); `start_bit` `u8`; `bit_length` `u8`; `flags` `u8`; `mux_sel_idx` `u8` (index into this message's signal list, `0xFF` = not muxed); `mux_value` `u32`; `scale` `f32`; `offset` `f32` |

`flags` is a bitfield: bit 0 (`FLAG_SIGNED`) the raw value is signed, bit 1 (`FLAG_WIRE_FLOAT`) the signal is a float on the wire, bit 2 (`FLAG_MUXED`) the signal belongs
to a mux group. The message list is followed immediately by that message's signals, so message *n*'s signals precede message *n+1*'s record.

## Command-line reference

All generation is done through subcommands of `can-api-gen`. Run `can-api-gen COMMAND --help` for the authoritative option list.

### `api`

```
can-api-gen api [OPTIONS] DB_FILE LIBRARY_NAME NODE_NAME
```
Generate the static, per-node C API (see [Example Use](#example-use)).

Positional arguments:
* `DB_FILE` — path to the bus description, either a KCD (XML) or a `can-db-model` YAML file.
* `LIBRARY_NAME` — stem of the generated library. The output files are `<LIBRARY_NAME>.c`, `<LIBRARY_NAME>.h`, and a sibling `canframe.h`. The stem is also used as
  the prefix for every generated function so multiple APIs can coexist in the same project.
* `NODE_NAME` — name of the node the API is being generated for. Only messages that this node produces or consumes are emitted, so the binary size stays small for
  embedded targets.

Options:
* `-f`, `--force` — overwrite existing output files. Without this, the generator refuses to clobber an existing `.c`/`.h`.
* `-a`, `--encode-all-signals-func` — for each message, emit an `<api>_<msg>_encode_every_signal` helper that fills a caller-provided buffer with enough
  frames to cover every signal at least once (primarily useful for multiplexed config messages).
* `-r`, `--global-rx` — emit the api-wide `RxSignals` struct plus a `<api>_process_received_frame` dispatcher that takes a raw `CanFrame`, figures out which
  message it corresponds to, and decodes it into the right per-message struct. See the [Global RX](#global-rx) section for details.
* `-p`, `--periodic-tx` — emit the api-wide `TxSignals` struct, the `<api>_periodic_tx` function, and `<api>_periodic_tx_init` / `<api>_update_tx_period` /
  `<api>_get_msg_tx_context_by_id` helpers. See the [Periodic TX](#periodic-tx) section.
* `--send-now` — add a `send_now` field to each `MsgTxContext` and a gate in the periodic tx function that fires a frame immediately when the flag is set,
  bypassing both `enable` and the periodic schedule. Only meaningful together with `-p`.
* `--ti-compatible` — hoist all per-signal temporaries to the top of every encode/decode function so the generated C compiles under strict C90
  ("declarations before statements"), which is what the TI C2000 compiler requires. See [TI C2000 compatibility](#ti-c2000-compatibility).
* `--double-buffered` — emit two signal-struct storage slots plus front/back pointers per message, an `<api>_global_rx_init` helper that wires the RX
  pointers, and a per-message `<api>_<msg>_commit` helper that swaps the TX staging and committed pointers. Applies to the stored interfaces only: the RX side needs
  `--rx-signal-store` and the TX side needs `--tx-signal-store` (in the callback-only default there is no store to double-buffer). See
  [Double-buffered signal access](#double-buffered-signal-access).
* `--rx-callback-context` — add a `void *rx_callback_context` field to each `MsgRxContext` and widen the RX callback to `void (*rx_callback)(void *)`, passing the
  context pointer on each firing. See [RX/TX callback context](#rxtx-callback-context). Only meaningful together with `-r`.
* `--tx-callback-context` — add a `void *tx_callback_context` field to each `MsgTxContext` and append a `void *` context parameter to the TX callback, passing the
  context pointer on each firing (before encode). See [RX/TX callback context](#rxtx-callback-context). Only meaningful together with `-p`.
* `--msg-as-rx-callback-arg` — pass a `void *` to the message's just-decoded signal struct as the first argument to the RX callback. Composes with
  `--rx-callback-context` (signals first, then the context pointer). See [Message signals as RX callback argument](#message-signals-as-rx-callback-argument). Only
  meaningful together with `-r`.
* `--tx-signal-store` — generate the permanent per-message TX signal store. Without it, periodic TX is callback-only: each scheduled message's `tx_callback` returns the
  struct to encode, and a `NULL` return or missing callback sends nothing. See [Periodic TX](#periodic-tx). Only meaningful together with `-p`.
* `--msg-as-tx-callback-arg` — pass a `void *` message-struct pointer as the first argument to the TX callback (context second, with `--tx-callback-context`). With
  `--tx-signal-store` the pointer is the message's store struct, which the callback edits in place before encode; in callback-only mode `<api>_periodic_tx` stack-allocates
  the struct, and the callback fills it and returns it (or `NULL` to skip), so no application-provided storage is needed. See [Periodic TX](#periodic-tx). Only meaningful
  together with `-p`.
* `--rx-signal-store` — generate the permanent per-message RX signal store (the pre-2.0 default). Without it, global RX is callback-only: no store is
  generated and decoded signals reach the application only through the RX callback. See [Global RX](#global-rx). Only meaningful together with `-r`.
* `--with-rx-valid` — in callback-only mode, add a `valid` field to `MsgRxContext` and set it on each matched frame, for struct-layout compatibility with
  store-based APIs. No effect with `--rx-signal-store`. Only meaningful together with `-r`.
* `-im`, `--id-member-name TEXT` — override the name of the `CanFrame` member that carries the arbitration id (default `arbitration_id`). Used to adapt the API
  to an existing project's frame struct naming.
* `-lm`, `--length-member-name TEXT` — override the name of the `CanFrame` length member (default `length`).
* `-pm`, `--payload-member-name TEXT` — override the name of the `CanFrame` payload member (default `payload`).
* `-v`, `--verbose` — dump the processed message dicts that get fed into the Jinja templates. Useful when debugging template or generator changes.
* `--help` — show the CLI help and exit.

### `cbdb`

```
can-api-gen cbdb [OPTIONS] DB_FILE OUTPUT_BASE
```
Compile a database into the binary descriptor blob consumed by the dynamic decoder, plus a JSON sidecar. See [Preparing a database](#preparing-a-database-cbdb).

Positional arguments:
* `DB_FILE` — path to the bus description (KCD or YAML). Unlike `api`, there is no node argument: the decoder is a bus observer, so every signal of every message is
  included and no direction filtering is applied.
* `OUTPUT_BASE` — stem for the outputs. Writes `<OUTPUT_BASE>.cbdb` (the binary descriptor) and `<OUTPUT_BASE>_ids.json` (the sidecar).

Options:
* `-f`, `--force` — overwrite existing output files. Without this, the generator refuses to clobber an existing `.cbdb`/`_ids.json`.
* `--help` — show the CLI help and exit.

### `dyndec`

```
can-api-gen dyndec [OPTIONS] LIBRARY_NAME
```
Generate the KCD-independent dynamic-decoder C module. See [The dynamic-decoder module](#the-dynamic-decoder-module-dyndec).

Positional arguments:
* `LIBRARY_NAME` — stem of the generated module. Writes `<LIBRARY_NAME>.c`, `<LIBRARY_NAME>.h`, and a sibling `canframe.h`. The module is database-independent, so this
  is generated once per firmware and reused for every `.cbdb`.

Options:
* `-f`, `--force` — overwrite existing output files.
* `-im`, `--id-member-name TEXT` — override the name of the `CanFrame` arbitration-id member (default `arbitration_id`).
* `-lm`, `--length-member-name TEXT` — override the name of the `CanFrame` length member (default `length`).
* `-pm`, `--payload-member-name TEXT` — override the name of the `CanFrame` payload member (default `payload`).
* `--help` — show the CLI help and exit.
