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=jsonbmcli daemon startbmcli channel open --channel=10356/0 --nbitrate=500k --dbitrate=2M --mode=normalbmcli channel open --channel=10357/0 --nbitrate=500k --dbitrate=2M --mode=normalbmcli channel claim --channel=10356/0,10357/0bmcli simulation load --file=examples/simulation/vehicle.jsonbmcli simulation source clear Speed --simulation=powertrain --task=vehiclebmcli simulation start powertrain --channel=10356/0bmcli message recv --channel=10357/0 --id=0x100 --count=10 --duration=2 --format=jsonbmcli simulation status powertrain --format=jsonThe 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=Nbmcli simulation source set Speed --simulation=powertrain --task=vehicle --type=step --point=0:20 --point=1000:80bmcli simulation source set Speed --simulation=powertrain --task=vehicle --type=sine --offset=60 --amplitude=20 --period=2000bmcli simulation source clear Speed --simulation=powertrain --task=vehiclesource 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=CA22bmcli logging start --channel=10357/0 --path=simulation.ascbmcli simulation load --file=examples/simulation/vehicle.jsonbmcli simulation start powertrain --channel=10356/0Start reception in terminal B and leave the command waiting:
bmcli message recv --channel=10357/0 --count=0 --duration=30 --format=jsonOnce 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=1234bmcli logging status --format=jsonThe 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 powertrainbmcli simulation unload powertrainbmcli logging stopbmcli txtask list --channel=10356/0bmcli channel release --channel=10356/0,10357/0bmcli channel close --channel=10356/0bmcli channel close --channel=10357/0The 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.