跳转到内容

BMCLI Gateway plugin examples

Reference version: BMCLI 1.6.3. Command names, parameters and machine-readable fields retain their source spelling.

For complete table fields, defaults, and built-in algorithms, see the .bmgw Project Format.

can-temperature.bmgw demonstrates the built-in CAN signal gateway: temperature from ID 0x100 on logical chassis passes through Identity to ID 0x200 on logical adas, with a 20 ms period. Raw 0xFFFF and a source not observed for over 100 ms both mean invalid. The target retains the last encodable value and updates validity bits. The project includes signals, encodings, messages, and mappings, so the DBC need not be reloaded.

Bind physical devices, bitrates, and wiring through a local Bench. Open and claim the affected CAN channels before starting. The two logical networks must bind to different physical channels, with independent gateway input and output buses. Consult bmcli help and the Reference for specific Bench, channel, and Gateway operations.

bmcli gateway validate --file=can-temperature.bmgw
bmcli gateway load --file=can-temperature.bmgw
bmcli gateway start --instance=vehicle
bmcli gateway status --instance=vehicle --format=json
bmcli gateway signals --instance=vehicle --format=json
bmcli gateway stop --instance=vehicle
bmcli gateway unload --instance=vehicle

native-context.bmgw uses three compilable examples. They demonstrate API integration; the built-in model can express these simple calculations itself.

File Purpose
demo_gain.c A Transformation reads its declared input and obtains bias from shared Signal State through reads, producing 2*x+bias. Changes to bias also trigger recalculation
demo_codec.c A Codec decodes 16-bit raw values in physical increments of 0.25, with 65535 meaning invalid. Encoding rounds to the nearest value and rejects out-of-range values, non-finite numbers, and reserved codes
demo_processor.c A Gateway Processor receives a payload in a frame callback and decodes bytes 2/3 of ID 0x100 into shared signals. Other IDs/lengths produce invalid state. The example also demonstrates create/destroy for private plugin context

The public header is bmcli_gateway_plugin.h; the source tree and installed package use the same relative layout. Put DLL/SO files in the project’s plugins subdirectory and copy that directory when moving the project. Official Windows BMCLI is 32-bit, so the DLL must match; a Linux SO must match the processor architecture of BMCLI.

Build in this directory with Windows MinGW:

Terminal window
New-Item -ItemType Directory -Force plugins | Out-Null
foreach ($sourceName in @('gain', 'codec', 'processor', 'forward')) {
gcc -m32 -std=c99 -O2 -shared -I../../include "demo_$sourceName.c" -lm -o "plugins/demo_$sourceName.dll"
if ($LASTEXITCODE -ne 0) { throw "Plugin compilation failed: $sourceName" }
}

Build natively on Linux:

Terminal window
mkdir -p plugins
for source_name in gain codec processor forward; do
cc -std=c99 -O2 -shared -fPIC -I../../include "demo_$source_name.c" -lm -o "plugins/demo_$source_name.so" || exit 1
done

Load native-context.bmgw and start native-demo. After injecting ID 0x100 with eight bytes 28 00 34 12 00 00 00 00 on chassis, ID 0x200 on adas should contain 54 00 34 12 03 00 00 00. The conversion is raw 40 → physical 10 → 2*10+1=21 → raw 84; both validity bits in byte 4 are 1.

bmcli gateway set --instance=native-demo --signal=bias --value=3

While the source remains valid, changing bias changes the first two target bytes to 5C 00. Source FF FF invalidates temperature, retaining the last raw value and clearing its validity bit; after the source stops, both application values become invalid according to their freshness settings. Codec/Transformation/Processor implementations are fixed at configuration time, with no automatic runtime switch to a backup plugin.

The exported entry must be the C symbol BMCLI_Gateway_GetPluginApi. The host checks ABI version, structure size, plugin kind, and function pointers; C++ implementations must preserve extern "C" and the calling convention. Do not pass STL containers or C++ exceptions through the interface.

create(config_json, &context) / destroy(context) manage plugin-owned memory; invoke(context, call) receives a read-only view of the current call. read can access declared inputs and reads; resolve can resolve only declared read/write sets. Declare additional signals in .bmgw reads so the static graph can check dependencies and cycles. The host exposes no raw internal object pointers, bus-opening interface, or direct hardware writes.

A Transformation, Decoder, or Processor must write every declared output exactly once in a successful call; the host stages the results before committing the group. An Encoder only sets call->raw_value and does not write outputs. Even if the plugin ignores a read/write/resolve error, the host rejects that transaction. Value types must match output signals; express validity through valid / reason. The host manages observation time, epoch, version, and derived freshness.

call, payload, inputs/outputs/reads arrays, strings, and host function objects remain valid only for this call; do not retain them for a background thread. Configuration does not save private context, current signal values, or E2E counters. Plugin calls are serialized in-process, without crash isolation or forced timeout interruption; return promptly. Use a Processor for special frames, a Codec for special bit encoding, and a Transformation for signal relationships. E2E plugins retain the separate ABI in the Simulation examples.

read_endpoint reads ports belonging to declared endpoints or send_tasks: active means that this runtime instance is enabled; observed/good/reason/observed_us describes the latest received frame, poll, server resource access, or target completion; epoch identifies this start. Before any observation, observed=0; it is not a device-connection probe. A frame-triggered Processor can also receive frames whose length/FD configuration does not match an ordinary Message; the corresponding normally decoded signals are marked invalid. State/periodic callbacks have no payload, message_id, or frame flags, and endpoint is UINT32_MAX. frame_flags uses the public BMCLI_GW_CAN_IDE/FDF/BRS bits to identify source frame format. Read signal freshness and E2E validity from the corresponding Signal State.

native-forward.bmgw with demo_forward.c demonstrates private frame processing beyond the model: select eight data bytes from standard Classic ID 0x100 on chassis, use them as the application payload for ID 0x200 on adas, and request transmission through the normal writer. Project E2E generates the target counter and checksum. For example, input 0A 00 00 00 00 00 0F FF yields a first target frame of 0A 00 00 00 00 00 00 0A. This custom processing needs no placeholder application signals.

The Processor’s configured send_tasks list references CAN TxTasks with mode=request; these tasks do not transmit periodically on their own. A zero return from stage_message(host, call->send_tasks[i], payload, length) means only that staging succeeded for this callback. If the callback succeeds and all declared outputs are complete, the signal group and transmission requests commit together. Callback failure, ignored host-interface errors, unauthorized access, or insufficient capacity discards all requests and outputs from that callback. Private plugin context is not automatically rolled back.

Each callback allows at most 16 requests; each instance allows at most 64 queued and in-flight requests. A full queue returns nonzero and increments processor_rejected_requests. Requests are sent FIFO within each task, with no total order guaranteed across tasks. Payload length must equal Message length. ID/flags/target port come from static tables and cannot be chosen in the callback; E2E-owned bits must retain Message defaults. A request task has one Processor producer; complete payloads and normal TX Bindings cannot occupy the same Message.

The normal writer checks resource ownership, backpressure, E2E, and actual completion. Queued requests remain frozen until acceptance and are not merged with newer values. Stop cancels requests not yet accepted (cancelled_requests); accepted requests continue to be counted as completed/unconfirmed. A target timeout stops the task; stop and restart the instance to recover. This bounded interface provides no persistence, unlimited retries, or lossless-event guarantee.


Source of truth: BMCLI 1.6.3 repository documentation. Run bmcli help <category> <action> --format=json for the exact contract of the installed version.