Skip to content

BMCLI Simulation practice

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

This example simulates vehicle speed and gear on an isolated 500k/2M CAN bench using the bundled examples/simulation/vehicle.dbc and vehicle.json, without modifying the DBC. Its Counter+SUM scheme is a teaching protocol, not an AUTOSAR profile. First follow the BMCLI skill to check bench identity, wiring, termination, and occupancy. Replace 10356/0 (transmit) and 10357/0 (receive) below with verified SN/port identifiers for your bench. Do not perform these injections on a running vehicle.

1. Select the Interface and Establish a Normal Node

Section titled “1. Select the Interface and Establish a Normal Node”

Discover commands for the installed version with bmcli help simulation --format=json. Use simulation for DBC signal waveforms, runtime value changes, and per-frame E2E; use txtask for hardware periodic transmission of fixed bytes, message send for a single frame, and replay for existing logs. You do not need an application loop to update the counter or a wrapper that turns simulation into a hardware TX Task.

bmcli doctor --format=json
bmcli daemon start
bmcli channel open --channel=10356/0 --nbitrate=500k --dbitrate=2M --mode=normal
bmcli channel open --channel=10357/0 --nbitrate=500k --dbitrate=2M --mode=normal
bmcli channel claim --channel=10356/0,10357/0
bmcli simulation load --file=examples/simulation/vehicle.json
bmcli simulation source clear Speed --simulation=powertrain --task=vehicle
bmcli simulation start powertrain --channel=10356/0
bmcli message recv --channel=10357/0 --id=0x100 --count=10 --duration=2 --format=json
bmcli simulation status powertrain --format=json

The receiver should see a period of about 50 ms and leading bytes D0 07 03 (Speed=20, Gear=D). The low four bits of byte6 increment each frame and wrap modulo 16; byte7 is the low eight bits of the sum of the first seven bytes. Check actual RX, not only a successful start response or the accepted count.

Change values atomically at runtime, attach step/sine sources, and restore constants:

bmcli simulation signal set --simulation=powertrain --task=vehicle --signal=Speed=80 --signal=Gear=N
bmcli simulation source set Speed --simulation=powertrain --task=vehicle --type=step --point=0:20 --point=1000:80
bmcli simulation source set Speed --simulation=powertrain --task=vehicle --type=sine --offset=60 --amplitude=20 --period=2000
bmcli simulation source clear Speed --simulation=powertrain --task=vehicle

source clear restores the configured constant (80 here), rather than freezing the latest sample. Waveforms use the epoch from the whole-resource start; setting step points in the past immediately selects the value appropriate for the current time. E2E continues to generate protection per frame. Ordinary signal set cannot change Counter/Checksum.

2. Enable One Fault at a Time and Check Triggering and Recovery

Section titled “2. Enable One Fault at a Time and Check Triggering and Recovery”

All four bundled rules fire once at sequence=1 (the second actual candidate opportunity in this task run) and are disabled by default. Before each experiment, stop, unload, and load to clear previous context, caches, and cumulative statistics; clear the Speed waveform. Enable first, then start, and establish a receive cursor before start. Do not start and wait for manual intervention before enabling: the 50 ms window may have passed, leaving fired=0 even with enabled=true.

The Python snippet below only orchestrates existing CLI commands. It assumes the channels above are open/claimed and powertrain is currently stopped. Run it from the source bmcli directory or the installed share/bmcli directory, with bmcli on PATH. Each iteration outputs actual frames and full state; the resource is unloaded at the end.

import json, subprocess, time
def run(*args):
p = subprocess.run(["bmcli", *args, "--format=json"], capture_output=True,
text=True, timeout=10, check=True)
return json.loads(p.stdout)
for fault in (None, "drop-one", "repeat-one", "bypass-one", "bad-checksum"):
run("simulation", "unload", "powertrain")
run("simulation", "load", "--file=examples/simulation/vehicle.json")
run("simulation", "source", "clear", "Speed", "--simulation=powertrain", "--task=vehicle")
if fault:
run("simulation", "fault", "enable", fault, "--simulation=powertrain")
receiver = subprocess.Popen(["bmcli", "message", "recv", "--channel=10357/0",
"--id=0x100", "--count=8", "--duration=2", "--format=json"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
try:
time.sleep(0.2) # Establish an independent RX cursor before fault timing begins
run("simulation", "start", "powertrain", "--channel=10356/0")
stdout, stderr = receiver.communicate(timeout=5)
assert receiver.returncode == 0, stderr
print(fault or "normal", json.loads(stdout))
if fault:
rule = run("simulation", "fault", "show", fault, "--simulation=powertrain")["data"][0]
assert rule["fired"] == 1, rule
run("simulation", "fault", "disable", fault, "--simulation=powertrain")
print(run("simulation", "status", "powertrain"))
finally:
run("simulation", "stop", "powertrain")
if receiver.poll() is None:
receiver.kill()
receiver.communicate()
run("simulation", "unload", "powertrain")
Experiment First counters / arrival pattern Independent acceptance criteria
Normal 0, 1, 2 Valid SUM on every frame; about 50 ms
drop-one 0, 2, 3 First interval about 100 ms; both SUMs valid; dropped=1; logical generated exceeds accepted by 1
repeat-one 0, 0, 1 All bytes of the first two frames match and SUM is valid; repeated=1; context does not advance twice
bypass-one 0, 0, 1 Second frame uses zero Counter/Checksum from default_data, retaining application values 20/D; unprotected=1
bad-checksum 0, 1, 2 Only the second frame has an invalid SUM; corrupted=1; subsequent frames recover valid checksums

The normal path resumes automatically after the fault window. disable prevents later windows without rolling context back. suppress-send differs from drop: it generates no protected frame, so the receive sequence is 0, 1 with an interval of about 100 ms. counter-freeze/jump modifies bits after protection and may invalidate the checksum; use frame-repeat for a valid repeated frame. Do not activate two flow rules, or a flow rule and an overlay, at the same opportunity; conflicts fail closed. For further boundaries, fields, and statistics, see the Reference.

3. Switch to an OEM Plugin and Reuse the Experiment

Section titled “3. Switch to an OEM Plugin and Reuse the Experiment”

Follow the OEM example to build a trusted library with matching bitness/architecture, then fill in the library path and actual SHA-256 in oem.json.template. After load, use the resource name oem-demo. Commands and criteria for drop-one, repeat-one, and bypass-one stay the same; the host need not know the vendor’s counter algorithm. The sample bad-checksum uses an explicit XOR of byte7. Configure overlays for a real vendor according to its private layout. Plugin errors/timeouts do not automatically send unprotected frames; bypass must be explicitly enabled for the experiment.

4. Coexist with Existing Transmission, Reception, Logging, and Diagnostics

Section titled “4. Coexist with Existing Transmission, Reception, Logging, and Diagnostics”

Configure hardware txtask before starting simulation (hardware configuration changes may cancel existing simulation through channel lifecycle handling). Check simulation 0x100, hardware 0x651, and single frame 0x652 together. First establish transmission resources and recording in terminal A:

bmcli txtask add --channel=10356/0 --message=0x651 --cycle=50 --data=CA22
bmcli logging start --channel=10357/0 --path=simulation.asc
bmcli simulation load --file=examples/simulation/vehicle.json
bmcli simulation start powertrain --channel=10356/0

Start reception in terminal B and leave the command waiting:

bmcli message recv --channel=10357/0 --count=0 --duration=30 --format=json

Once reception has started, return to terminal A and send the one-shot frame during this 30-second receive window. If the window has ended, restart reception in terminal B before transmitting. Do not wait for the receive command to return before sending:

bmcli message send --channel=10356/0 --id=0x652 --data=1234
bmcli logging status --format=json

The frames returned in terminal B should include 0x100, 0x651, and 0x652 (payload 12 34).

If a verified independent ECU/test fixture is available, use its actual endpoints for one UDS TesterPresent and XCP CONNECT/memory read. Consult help uds request, help xcp config set, and help xcp memory read first. Do not send sample addresses to an unknown ECU. Verify the positive UDS response, actual XCP data, and corresponding response frames in the log, and check that simulation frames continue throughout, with valid counter/checksum and no log gap/drop/error. Plugin startup/handshaking must not block these unrelated channel operations. For cancellation, check that the pending start fails and releases its binding. UDS/XCP configuration queries, endpoint configuration, and XCP disconnect should not stop independent simulation. Physical channel close/reset must cancel simulation on that channel without affecting other channels.

Clean up the simulation, logging, and hardware txtask created here, release the claim, and close channels you opened. Stop the daemon only if this example created it. Do not globally clear other users’ tasks. For an isolated dedicated bench, use this cleanup order (check actual txtask IDs with list before remove):

bmcli simulation stop powertrain
bmcli simulation unload powertrain
bmcli logging stop
bmcli txtask list --channel=10356/0
bmcli channel release --channel=10356/0,10357/0
bmcli channel close --channel=10356/0
bmcli channel close --channel=10357/0

The repository’s bounded regression entry is test/test_simulation_cli.py --tx=<TX> --rx=<RX>, also registered as SIM01 in the canonical HW suite. It uses the global 4502 lock and validates actual RX; do not run it alongside other daemon tests.


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