Skip to content

BMCLI gateway

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

For the complete normalized .bmgw fields, defaults, built-in Transformation capabilities, and capacity limits, see the project format guide.

gateway is a table-driven signal gateway: source services decode data and update SignalState, Mappings connect inputs to outputs, Transformations calculate physical values, and destination tasks encode and submit data according to their own transmission policies. remote-bmapi is the original GWR UDP forwarding entry point; the two serve different purposes.

A .bmgw file contains normalized JSON with format="bmgw" and schema_version=1. The project stores logical Endpoints, Signals, Codecs, Messages/PDUs, Modbus Resources, Bindings, Mappings, TxTasks, E2E definitions, and plugin declarations; it no longer depends on the imported DBC at runtime. gateway save saves configuration, excluding current signal values, receive times, E2E Counters, and in-flight requests; it does not bundle DLL/SO files. Copy relative plugin directories together with the project.

The local Bench supplies physical device serial numbers, COM/tty ports, and network addresses. A CAN Endpoint’s network refers to a logical Bench network; a Modbus Endpoint explicitly specifies role=client|server, network, and node. Client read Bindings use the existing Modbus polling service, while write Bindings are submitted by the shared destination task. On a Server, serve is a read-only mapping and external is an input source that permits complete remote writes. Do not replace an atomic 64-bit value write with writes to several narrower resources.

A Boolean bit in one register uses the Resource’s bit_offset (0..15, with 0 being the least significant bit of the wire word) and a 1-bit Codec. Multiple output bits in the same word belong to one TxTask. They are combined into one local read-modify-write transaction on the shared physical session, preserving other bits; a failed read prevents the write. A Server supports bit mirrors at the same address and batches of external bit setpoints. See Gateway project format for complete layouts, read-only bits, and whole-word ownership across instances.

Terminal window
bmcli gateway validate --file=vehicle.bmgw --format=json
bmcli daemon start
bmcli gateway load --file=vehicle.bmgw
# Open CAN channels according to the Bench and acquire the required control first
bmcli gateway start --instance=vehicle
bmcli gateway signals --instance=vehicle --offset=0 --limit=100 --format=json
bmcli gateway status --instance=vehicle --format=json
bmcli gateway stop --instance=vehicle
# Unload after confirming state=stopped
bmcli gateway unload --instance=vehicle

validate and import run locally without starting a daemon or bus I/O. load builds and validates a normalized-table instance; startup loads the native plugins explicitly specified in the configuration and prepares protocol resources. The load name defaults to the project ID and can be overridden with --instance; loading an existing name is rejected. Startup checks output ownership: separate Gateway/Simulation instances cannot drive the same ID/IDE message on the same physical CAN channel, and Classic CAN versus CAN FD does not distinguish owners. Modbus Clients cannot write overlapping addresses in the same shared physical session, Unit ID, and table. Read-only polling and non-overlapping outputs may share a connection. A conflict returns output_conflict (REST 409) and rejects the entire start operation; a replacement can take over only after stopping and releasing all transport resources. Legacy Simulation retains its semantics for multiple tasks transmitting the same message within one resource. gateway import exports normalized tables through the existing DBC parser; VAL_ retains label metadata only and does not infer invalid values from English labels. For the complete parameter contract, use bmcli help gateway <action> --format=json.

SignalState retains the exact type bool/int64/uint64/float64/enum, physical value, validity, and original observation time. Configuration values for 64-bit integers and enums use decimal strings. In query results, value is always a string, or null when no value exists, to avoid JavaScript precision loss. Raw values belong to each Binding’s Codec; they are not the shared physical value exchanged between buses.

See the project format for complete numeric and frame boundaries. Integer arithmetic, integer-valued mathematical/coordinate results, and float32/float64 wire encoding are checked for exactness; an erroneous destination uses invalid_raw or its last value and clears the validity bit. Typed integer scaling does not reconstruct values through double; ordinary floating-point quantization preserves previous behavior. k/b/threshold, Codec parameters, and non-step waveform parameters remain Float64; integer parameters that cannot be represented exactly are rejected. Gateway sources reject RTR frames, short frames, and TX completions. Classic DLC 9..15 means 8 bytes; ESI/BRS does not invalidate data, and the current plugin frame ABI does not expose ESI.

When Linear/basic arithmetic produces an integer result from integer inputs and then converts it to float64, the result must also be exactly representable; otherwise, the output reports transform_error and retains the last value. Fractions and ordinary floating-point calculations still use floating-point rounding. Built-in transformation chains depending only on constants inherit the constants’ skew/TTL exemptions; chains with dynamic inputs or reads still determine freshness from actual source observations.

The Codec special table identifies special raw values before applying physical = factor * raw + offset. invalid_raw specifies the destination’s invalid encoding, and a Binding may specify a validity bit. An input timeout or disconnect invalidates the Signal: if an invalid code/validity bit exists, the corresponding state is encoded; otherwise, the last encodable value is retained, using the configured initial value until a valid observation exists. Destination E2E generation continues at its normal cadence and is recorded separately from source-signal invalidity.

Each CAN Message supports at most one E2E configuration per direction. Multiple Checks or multiple Protects (including separate PDUs) return unsupported_gateway_feature, a message containing currently unsupported, and the common error fields details={} and retryable=false. The CLI exit code is 1; REST returns 422. One Check and one Protect remain independent of each other.

A complete external Modbus Server write is validated against the built-in Codec’s range/type constraints before the mirror and input queue are updated. Failure returns protocol exception 03 without a partial write. Explicitly configured invalid_raw/invalid special values can still be written as an invalid state.

Mapping defaults to identity. Common calculations use linear, threshold, fixed mathematical functions, or coordinate transforms. Complex logic explicitly selects a native Transformation, Codec, or Gateway Processor at configuration time. Plugins can access only declared inputs/context and stage outputs as a complete group; there is no fallback path that switches algorithms at runtime. Native plugins execute in the BMCLI process, and the host cannot interrupt a function that hangs.

Destinations default to periodic, using the task or Message period. Explicit on_change compares business values, actual encoding quality, and MUX activation state; it does not compare receive time, version, Counter, or CRC. Threshold is a Transformation, not a Modbus write policy. Latest values are coalesced while the destination has not yet been submitted; accepted requests remain frozen. This mode does not guarantee preservation of every short pulse or user-click event.

A Processor can also stage complete payloads for CAN tasks with mode=request through declared send_tasks. Limits are 16 items per callback and 64 per instance (including in-flight items). Queue exhaustion, unauthorized access, or callback failure rejects the entire group without partial transmission. In request mode, the Message fixes the ID/flags/port, E2E bits belong to the normal writer, and ordinary TX Bindings cannot encode the same message concurrently. State includes queued_requests, cancelled_requests, and processor_rejected_requests, separate from actual acceptance/completion counts. For the complete C ABI, port-state semantics, build instructions, and examples, see Gateway plugin examples.

State satisfies accepted = sent + failed + pending; sent means an actual protocol completion result was observed. unconfirmed is the subset of pending that remains unconfirmed after stopping and releasing the transport; it is neither success nor failure. stop may initially return stopping; unload is allowed only after stopped. Stopping/restarting the same instance preserves the E2E context of accepted transmissions; unloading and loading again restarts from the configured initial values.

Only Signals declared with producer=software can be changed through gateway set; external inputs, waveforms, and derived outputs retain their respective write ownership. The CLI defaults to readable text. JSON/JSONL returns one complete object; CSV returns two columns, path,value, retaining nested field paths and exact values. Signal pagination allows at most 1000 items per page.

Client Poll/Write and the existing Modbus CLI/Poll share physical connections and a FIFO. TCP Unit IDs are 0..255; RTU/ASCII addressed IDs are 1..247. TCP responses must match the transaction ID and Unit ID. The shared connection allocates transaction IDs consecutively; protocol errors/timeouts close the connection before recovery. A successful response does not establish the actual feedback state of relay contacts.

Binding resolves a hostname to a fixed address (preferring IPv4, otherwise IPv6); the same address/port shares one connection. Runtime reconnects retain that address, and resolution occurs again only on the next binding. COM names, Win32 device-path aliases, and Linux tty symlinks are normalized using OS physical-device identity. Serial baud rate, data bits, parity, and stop bits must agree. Unresolvable endpoints fail before ownership is acquired. A Server configures one Unit ID per physical listening endpoint/serial port; a Client can poll multiple Unit IDs in one physical session.

REST uses the same daemon instance service and does not execute CLI subprocesses. Stopping Web does not stop Gateway. Web files and plugin dependencies must be inside the daemon’s working directory at startup; start the daemon from the project directory to use that directory’s Bench. CLI --project selects the caller’s Bench and does not change the file root of an already-running Web service.

Method / path Service operation
GET /api/v1/gateways[?instance=<name>] List instances or read one instance’s state
POST /api/v1/gateways Load { "file": "vehicle.bmgw", "instance": "vehicle" }
POST /api/v1/gateways/validate Validate file
POST /api/v1/gateways/import Import file, specifying output/instance/network/direction; supports merge/missing_cycle/default_period_ms/stale_after_ms
POST /api/v1/gateways/{instance}/start, /stop Start/stop
POST /api/v1/gateways/{instance}/save Save to file
DELETE /api/v1/gateways/{instance} Unload a stopped instance
GET /api/v1/gateways/{instance}/signals?offset=0&limit=100 Consistent paginated value snapshot
PUT /api/v1/gateways/{instance}/signals { "signal": "command", "value": "9007199254740993" }

REST rejects unknown/duplicate fields, embedded NULs, non-string assigned values, and out-of-range pagination parameters. Errors use the common status/error envelope. Missing instances return 404; duplicate loads/unloading an instance still running return 409; files outside the project return 403. OpenAPI and capabilities are checked for consistency against actual routes. The old /api/v1/gateway alias is no longer provided; remote BMAPI uses /api/v1/remote-bmapi.

See examples/gateway/can-temperature.bmgw. It maps temperature in 0x100 on the chassis network to 0x200 on adas, with a 20 ms destination period, source invalidation after 100 ms, and destination validity at bit 16. The example itself does not bind or open any physical port.


Shared project entry points for Gateway and Simulation

Section titled “Shared project entry points for Gateway and Simulation”

Normalized .bmgw projects can be operated through either entry point. simulation with --instance calls the same typed service as gateway; loading .bmgw, import, save, signals, and set also select this model. Instance names are unique throughout the daemon. Switching entry points does not duplicate the project, state, scheduled tasks, E2E, or fault rules. Original JSON/BMSIM simulation manifests and their positional arguments and task/source/fault commands continue to use the compatibility adapter.

Terminal window
bmcli simulation load --file=vehicle.bmgw --instance=vehicle
bmcli simulation start --instance=vehicle
bmcli gateway signals --instance=vehicle --format=json
bmcli simulation stop --instance=vehicle

The two entry points together allow at most 16 loaded instances, including unfinished load reservations. The conservative host-memory budget is 256 MiB total and 128 MiB per .bmgw. Checks cover normalized tables, the expanded signal graph, destination state, queues, and protocol adapters; additional allocations made by plugins are outside the host budget’s control. Excess capacity and duplicate names are rejected before runtime state is expanded, and failed loads release their reservations. memory_budget_bytes / daemon_memory_budget_bytes in gateway status are budget figures, not the OS’s actual resident-memory usage.

simulation list and legacy status commands without --instance continue to show legacy manifest resources. Use gateway status to list normalized projects, or simulation status --instance=<name> to inspect one project.

The gateway status instance list returns summaries only, with empty tasks. With --instance, transmission tasks are paginated by --offset / --limit: 32 by default and 100 maximum; the response byte budget may further reduce the current page. Iterate with task_total, task_offset, and task_next_offset until task_next_offset == task_total. State responses for load, start, stop, and save also contain only the first task page. The Simulation entry point for normalized projects follows the same rules; legacy Simulation manifests retain their original pagination limits. REST uses GET /api/v1/gateways?instance=vehicle&offset=0&limit=32; pagination parameters require an instance to be specified.

Task coalesced counts distinct destination snapshots received by the scheduler and replaced by newer snapshots before acceptance. Coalescing graph updates within one source-processing round is not counted, nor is this a loss counter for a reliable event queue. late, last_lateness_us, and max_lateness_us describe how far periodic opportunities lag the most recent due time; skipped complete periods are counted separately as missed. Instance poll_coalesced counts Modbus observations replaced by newer ones before reaching the graph, and poll_missed counts skipped Poll periods. Statistics persist across stop/start of the same loaded instance and restart on a cold load.

Each signal in gateway signals / simulation signals and corresponding REST snapshots includes bindings: the signal’s output or Server-mirror Binding id, actual raw (a decimal string), encoded valid, and error.code/message. On overflow, quantization into a reserved code, or native encoding failure, the configured invalid code or last encodable value is used on the wire, while the Binding retains the specific failure reason. A later successful encoding clears the error. The source signal’s own value and quality are not rewritten, and transport accepted/sent/failed counts remain independent. Input signals without such Bindings return an empty array.

Each task also returns five groups of microsecond timings. Each group’s count and max cover the instance lifetime; window contains up to the latest 64 measurements. p50 / p95 / p99 are exact nearest-rank percentiles of that window; all are 0 when no measurements exist.

Field Measurement boundary
source_age_at_submit_us At submission, the age from the earliest observation among currently valid and active output dependencies to destination submission. A single-input mapping can reflect that source’s wait; multiple inputs, constants, or held values must not be interpreted as one RX routing latency. No sample is taken without a valid observation
tick_to_submit_us From entry into the current Runtime tick to acceptance of the destination by the transport service, including this round’s graph processing, encoding, E2E, and earlier tasks; excludes waiting for the next tick
completion_wait_us From destination submission to actual completion observation: CAN uses the TX completion time recorded by the host RxBroker, and Modbus uses request-group return time; this does not mean the remote ECU has consumed the data
queue_wait_us Modbus only: waiting from acceptance until the Client worker starts processing the request group
protocol_wait_us Modbus only: from worker processing start to request-group return, including shared physical-session queuing, transport, peer responses, and sequential execution of multiple Resources

These measurements use the same host’s monotonic clock; they are not directly subtracted from uncalibrated hardware or remote-machine timestamps. Transmission still uses the latest state; statistics do not change triggering or failure-handling rules.

The installation package includes share/bmcli/include/bmcli_gateway_plugin.h and Gateway C plugin examples: declarative Context reads for Transformations, bidirectional Codec encoding, Processor frame callbacks and private Context, and portable native-context.bmgw. Source and installation packages use the same relative example-directory layout; place compiled DLL/SO files in the project’s plugins subdirectory.

Gateway and Simulation .bmgw projects use the same rule set, with enabled=false by default. Supported kind values are drop, repeat, bypass, suppress, xor_byte, set_byte, freeze_counter, counter_jump, and corrupt_checksum. start/every/count triggers on actual generation opportunities, without turning skipped periods into a burst of faults. count=0 means unlimited; stopping/starting the same instance rearms opportunity rules.

drop discards the frame after normal Protect and advances Context; repeat uses the previous normally accepted, valid protected frame; bypass uses current business values and the protection-bit initial values in the Message configuration; suppress neither generates nor sends a frame. The latter three do not advance E2E. If multiple flow rules, or a flow rule and an override rule, trigger on the same opportunity, fault_conflict is reported and the conflicting candidate is not sent.

Override rules reuse Simulation’s post-Protect implementation. Byte rules use byte_offset/mask; freeze_counter defaults to counter_initial and can specify raw with the signed integer string value; counter_jump uses value as a positive or negative increment, defaulting to mask; corrupt_checksum uses a nonzero, in-range XOR value/mask. Native E2E does not declare a generic field layout to the host; use byte overrides or flow rules. generated/dropped/injected/repeated/unprotected/suppressed are counted separately from transport accepted/sent/failed/pending.

Merging multiple DBC imports and timing policies

Section titled “Merging multiple DBC imports and timing policies”

The imported file contains complete normalized tables; original DBCs are retained only as provenance. Import multiple networks one at a time, or merge DBCs containing different messages into one logical network. --merge reads an existing project and preserves its configuration and names. Identical CAN Endpoints are merged; duplicate Signal/Message/Binding/Task IDs or resource conflicts are rejected. The result is saved atomically after all validation completes. merge and output may refer to the same file; failure leaves the original unchanged. Existing relative plugin paths are retained; migrate the project with its plugin directory.

Terminal window
bmcli gateway import --file=chassis.dbc --output=vehicle.bmgw --instance=vehicle --network=chassis --direction=rx --stale-after-ms=100
bmcli gateway import --file=adas.dbc --merge=vehicle.bmgw --output=vehicle.bmgw --instance=vehicle --network=adas --direction=tx --missing-cycle=periodic --default-period-ms=20

TX uses the DBC period when present; a missing period returns missing_cycle by default. Explicit --missing-cycle=on_change creates an on-change task; periodic also requires a nonzero --default-period-ms, which fills only missing periods and does not replace original DBC periods. RX defaults to a timeout of three times each Message’s DBC period, recording the source in provenance. If the DBC period is absent, --stale-after-ms must be explicit. 0 explicitly disables timed invalidation for that source; choose a value based on its actual update pattern. Effective periods and timeouts are stored in the normalized tables, and runtime no longer reads the DBC.

REST import path and policy fields are strings. default_period_ms/stale_after_ms are JSON integers in 0..4294967295; other scalar types and out-of-range values are rejected. CLI uses the corresponding hyphenated options. REST merge input and output obey the daemon project-directory file boundary. Imported files and loaded instances have separate lifecycles; after changing a file, explicitly stop, unload, and reload the instance.

An ordinary CAN message send --count=N --cycle=... allows other daemon commands and recording to run during its long loop. Success accepted is BMAPI submission count. If a channel lifecycle request interrupts the batch, the sender fails with operation_cancelled, cancelled: true, accepted and failed_index; those accepted frames are not retracted. Recording should be started and confirmed ready before a test’s stimulus when full coverage is required.

Gateway status retains the instance phase and individual task errors, and adds health: "ok"|"degraded" plus failed_tasks across all task pages. An instance can remain running while a target task has failed; inspect health and task errors instead of treating the instance phase as delivery success.


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