---
name: bmcli
description: Operate BUSMUST CAN/CAN FD/LIN analyzer hardware and BMCLI-owned protocol services such as Modbus. Use only when the user explicitly names BMCLI or BUSMUST analyzer hardware, asks to run or discover BMCLI commands, or is building a test/tool/dashboard that explicitly uses BMCLI as its hardware backend or protocol backend. Do not trigger for general coding, code review, ECU/industrial development, specification reading, database analysis, dashboard design, or test methodology unless BMCLI-backed execution is an explicit requirement.
---

# BMCLI

Use `bmcli` as the application-level interface to BUSMUST hardware. Prefer it
over writing new BMAPI bindings when the requested operation is already exposed
by the CLI.

## Confirm activation before loading references

Use this Skill only after confirming at least one of these facts from the
request or project:

- the user explicitly asks to use BMCLI or a BUSMUST analyzer;
- an existing script/application already invokes BMCLI; or
- the requested deliverable must execute through BMCLI Web, Gateway, CLI,
  Modbus service, or BUSMUST analyzer hardware.

CAN, LIN, DBC, LDF, UDS, XCP, Modbus, ECU, industrial device, firmware,
Dashboard, testing, debugging, Python, LabVIEW, REST, or development are only
keywords. Keywords alone are not sufficient.
For ordinary coding, review, document reading, specification analysis, generic
test design, or UI design with no explicit BMCLI dependency, do not use this
Skill, do not run bench discovery, and do not load any BMCLI methodology.

After activation, load only the reference required by the confirmed scenario.
Do not preload every reference and do not reproduce unrelated methodologies.

## Establish the bench context

Every activated BMCLI workflow that will access hardware must begin with `bmcli
bench show --format=json`. A planning-only task may inspect supplied project
configuration without touching hardware. Use the CLI output as the effective
configuration contract; do not independently merge `.bmcli/target.json` and
`bench.local.json`.
Follow it with `bmcli bench validate --hardware --format=json` before hardware
access, or without `--hardware` when deliberately planning offline.

1. Read explicit values from the current request first: analyzer serial number,
   port, bus type, bitrate, CAN termination/mode or LIN role/pull-up/supply,
   UDS request/response IDs, addressing mode, padding, database paths, and target
   variant.
2. Run `bmcli bench show --format=json` to resolve `.bmcli/target.json` and
   `.bmcli/bench.local.json` through BMCLI. See
   [environment.md](references/environment.md) for ownership and schema.
3. Treat explicit current-request values as authoritative, followed by project
   target/network/node configuration, local bench bindings, then discovery.
4. Validate cached physical values before a hardware action. Run `bmcli doctor`
   and `bmcli bench validate --hardware --format=json`. Never assume enumeration
   index 0 still means the same analyzer.
5. Classify the state as `READY`, `DEGRADED`, `NEEDS_INPUT`,
   `DISCOVERY_REQUIRED`, or `OFFLINE`, then follow the state rules in
   [environment.md](references/environment.md).

Do not silently guess a port, bus type, bitrate, UDS request/response ID, XCP
master/slave ID, addressing byte, database, module, or target variant. These
values are bench-specific; a plausible-looking default is not evidence. If
required values remain missing immediately before execution, ask for all of
them once as a structured multi-question prompt when the host supports it, or
as one concise checklist otherwise. Continue the authorized workflow
automatically after the answers. When discovery has more than one plausible
answer, present the evidence and ask the user to select.

## Discover only with permission

- Enumerating USB analyzers is read-only and may be done automatically.
- If exactly one suitable analyzer exists, propose its stable `SN/port` values;
  do not infer which port reaches the ECU without evidence.
- AutoSet requires exclusive hardware access. If the daemon is running, ask
  before stopping it. Start with passive detection, which needs existing CAN
  traffic but does not transmit:

  `bmcli channel autoset --channel=<SN/port> --mode=passive --timeout=10000 --format=json`

- If passive detection fails because the bus is silent, offer active detection.
  Explain that it transmits probe frames and may affect a live network; run it
  only after explicit authorization, preferably on an isolated bench:

  `bmcli channel autoset --channel=<SN/port> --mode=active --timeout=10000 --format=json`

- Treat passive success as RX traffic evidence and active success as repeated
  TX-ACK evidence at the detected timing, not proof of a specific ECU identity.
- Functional-address UDS discovery can transmit onto the bus and may wake
  multiple ECUs. Use it only after explicit authorization and only on an
  isolated bench. Dry-run first, then run `bmcli uds discover --channel=<SN/port>
  --allow-functional --format=json`. If TesterPresent gets no response, retry
  only with a documented harmless read request such as `--data="22 F1 90"`.
  Accept only IDs marked `correlated`, meaning they answered every repeated
  probe. If `selection_required=true`, list all response IDs and ask the user
  which ECU is the target. Never auto-select or persist one of multiple IDs.
- Persist confirmed values with `bmcli bench init`; do not hand-edit files when
  the CLI can represent the field. Keep `bench.local.json` out of version
  control and portable target/network/node requirements in `target.json`.
- One `.bmcli` directory is one target and one bench. Do not invent profile
  activation. A bench may contain several named networks and channels. Prefer a
  logical network in `--channel=<network-name>` for maintainable project scripts;
  use explicit `SN/port` when bypassing project configuration intentionally.

## Plan before acting

Read [command-discovery.md](references/command-discovery.md) before composing
commands. Query the installed binary's JSON help; never treat remembered syntax
or a copied example as the command contract.

Use these exact discovery forms before naming a callable command:

```text
bmcli help --format=json
bmcli help <category> --format=json
bmcli help <category> <action> --format=json
```

Do not substitute remembered spellings such as `stream`, `filter`, `stats`, or
`channel listen`. If runtime help does not list a capability, describe it as an
application-layer workflow or a future requirement, not as a BMCLI command.

In the current command model, read a DID with `bmcli uds request 22 F1 90
--channel=<SN/port> --request-id=<CAN-ID> --response-id=<CAN-ID> --format=json` and inspect
DTCs with `bmcli uds dtc active --format=json` after resolving the endpoint from
bench context. There is no `uds read`, `read-did`, or `uds dtc read` convenience
command unless a future runtime help explicitly adds one.

For a UDS service with a subfunction, use the presence-only
`--suppress-positive-response` option only when the specification permits it.
BMCLI sets bit 7 and treats a successful transmission without a positive reply
as success. Do not apply this option to DID/address services; BMCLI rejects
those requests to prevent payload corruption. During a long diagnostic
sequence, query `bmcli help uds present start --format=json`, start one
daemon-owned TesterPresent task, inspect `uds present status` counters, and stop
the task in cleanup. Without suppression, each `3E 00` must receive `7E 00`;
with suppression, BMCLI sends `3E 80` and does not wait for that reply.

For raw capture, the current command is `bmcli message recv
--channel=<SN/port> --duration=60 --format=jsonl`; for a file-backed recording,
query `bmcli help logging start --format=json` and use `logging start`. There is
no `message listen`, `channel listen`, or `stream` command. `duration` is in
seconds; `timeout`, `cycle`, `interval`, and `delay` are in milliseconds.

For XCP-backed observation or calibration, first discover the installed `a2l`
and `xcp` command families from JSON Help. Treat the A2L as a read-only
symbol/address contract: BMCLI intentionally parses only fields required for
measurement, characteristic, layout, conversion, and address access. Use a
synchronous measurement read for a bounded check and dynamic DAQ for a
time-series observation. XCP and UDS can run concurrently when they use
distinct CAN IDs; never reuse the same response route. The current XCP endpoint
uses its configured slave ID for both synchronous responses and DAQ DTOs; do
not invent a separate DAQ CAN ID. `xcp daq receive` is bounded and returns one
JSON document with a `samples` array, so use `--format=json`, never JSONL.

When planning offline and unable to execute runtime help, include only executable
BMCLI snippets that appear verbatim in this skill. Describe every other operation
as a capability whose exact command must be resolved from JSON help at execution
time. Never fill a syntax gap from general CAN/LIN/UDS tool experience.

## Work with LIN

Select LIN by enumerated capability, not product name or numeric index. Query
current help for `channel open`, `lin config`, `lin response`, `lin schedule`,
`message send`, and `message recv` before use. A bounded master write can run
standalone:

```text
bmcli message send --channel=<SN/port> --id=0x12 --type=master-write --data="01 02 03 04" --bitrate=19200 --format=json
```

Use the daemon for persistent master/slave interaction. Open every endpoint
before arming the slave response table:

```text
bmcli daemon start
bmcli channel open --channel=<slave-SN/port> --mode=slave --bitrate=19200 --pullup=off --power=off
bmcli channel open --channel=<master-SN/port> --mode=master --bitrate=19200 --pullup=1k --power=on
bmcli lin response set --channel=<slave-SN/port> --id=0x12 --data="01 02 03 04" --checksum=enhanced
bmcli message send --channel=<master-SN/port> --id=0x12 --type=master-read --dlc=4 --timeout=1000 --format=json
```

Treat role, bitrate, power direction, pull-up, protocol version, checksum model,
ID, DLC, and payload as explicit test inputs. Validate response ID, exact bytes,
checksum, error flags, `completed`, and response count. Master-read and
sync-break emit the same header bytes on the wire but have opposite success
criteria: master-read requires payload+checksum; sync-break requires no slave
response and must fail on an unexpected payload. Never substitute one for the
other. Use `lin response` for per-ID slave mappings and the reserved `system`
schedule for repeated master slots. The first system entry auto-activates; use
`lin schedule deactivate/activate` for deliberate switching and `clear` to stop
all slots.

For an LDF, load it as a typed resource, inspect the named schedule before any
hardware action, then activate only after the selected table, master channel,
power state, and connected slave nodes have been confirmed:

```text
bmcli ldf load --file=<network.ldf> --name=<network>
bmcli ldf info --database=<network> --format=json
bmcli ldf schedule show <table> --database=<network> --format=json
bmcli ldf schedule activate <table> --database=<network> --channel=<master-SN/port> --format=json
```

BMCLI parses LDF files read-only and never edits them. It can materialize
unconditional and diagnostic frame slots into the daemon `system` table.
Event-triggered, sporadic, and configuration commands remain queryable but are
not automatically executed; when activation rejects one, explain the boundary
and ask for the intended runtime policy. Without an LDF, continue using
explicit `message`, `lin response`, and `lin schedule` primitives. Persist LIN
timing and local pull-up/power/channel bindings only through fields reported by
current `bench init --help`; do not invent unsupported schema members.

Load exactly the task methodology needed, only after the activation gate above:

- Read [development.md](references/development.md) only when the requested
  application or upper computer explicitly uses BMCLI as its hardware backend.
  Web Dashboard, Python orchestration and LabVIEW integration are variants
  inside this application-development scenario; do not treat their keywords as
  separate activation reasons.
- Read both [development.md](references/development.md) and
  [web-automation.md](references/web-automation.md) only for a BMCLI Web-backed
  browser dashboard or explicit BMCLI REST/SSE/WebSocket client.
- Read [modbus.md](references/modbus.md) only after this Skill is activated and
  the task explicitly uses BMCLI to access a Modbus device, EdgeX Modbus
  profile, Modbus polling, or a BMCLI-backed industrial Dashboard. The word
  “Modbus” by itself is not an activation reason.
- Read [verification-validation.md](references/verification-validation.md) when
  the user is turning a DBC/LDF, communication matrix, diagnostic survey, CDD,
  bootloader flow, or protocol specification into tests that will execute
  through BMCLI. Merely reading or reviewing those files does not qualify.
- Read [debugging.md](references/debugging.md) only when BMCLI/analyzer captures,
  diagnostics, or controlled traffic are part of the requested ECU fault
  isolation. Generic source debugging does not qualify.
- For a direct command, syntax, installation, or capability question, do not
  load development, Web, V&V, or debugging methodology unless it becomes
  necessary.
- If the activated task does not match application development, V&V, or ECU
  fault isolation, treat it as ad-hoc BMCLI use and load no scenario methodology.
  Runtime Help plus the relevant safety boundary is sufficient.

When relevant to the requested operation:

1. State the target channel and bus configuration.
2. Use `--format=json` for bounded commands and `--format=jsonl` for streams.
3. Use stable `SN/port`, never a legacy enumeration index in generated assets.
4. Dry-run UDS request construction before first transmission when supported.
5. Capture the exact command, exit code, structured output, timestamps, and
   relevant artifacts in generated test evidence.
6. Separate transport failure, negative UDS response, assertion failure, and
   infrastructure failure in test results.
7. Clean up daemon, cyclic tasks, routes, logging, replay, TesterPresent, and
   XCP activity that the workflow started. For an owned XCP workflow, stop DAQ,
   delete its dynamic DAQ configuration, disconnect, and unload only the
   database resource that workflow loaded. Do not stop or unload pre-existing
   user-owned activity without approval.

For engineering databases, query `bmcli help database --format=json` and the
typed category Help before constructing commands. Use `database
load/list/info/validate/unload` only for persistent resource lifecycle. Use
`dbc message/signal/decode/encode` for DBC semantics. `dbc load` and `database
load` of the same DBC share one resource; they are not category-wide aliases.
DBC, LDF, and A2L are peer typed resources and all unload through `bmcli
database unload <resource>`; do not invent `dbc unload`, `ldf unload`, or `a2l
unload`.
When more than one DBC is registered, always select the intended variant with
`--database=<resource>`. A direct `--dbc=<file>` is appropriate for a one-off
operation that must not change persistent registry state.

## Use the least risky interface

- Use direct BMCLI commands for inspection and one-off operations.
- Use shell scripts for fixed, serial command sequences with simple pass/fail.
- Use Python `subprocess` for loops, matrices, retries, JSON/JSONL parsing,
  artifact generation, richer assertions, and long-term test maintenance.
- Use a DBC for matching CAN variants and reviewed LDF/matrix data for LIN.
  Without a database, degrade to explicit IDs and payloads rather than blocking
  the whole workflow.
- When sending by DBC message name, preserve its extended-ID, CAN FD, and BRS
  attributes. Supply `--flags` only when the user or test intentionally overrides
  the DBC metadata.
- Keep OEM security algorithms external. `uds unlock` requires the approved
  DLL/shared library; never invent seed-key logic.
- Keep XCP seed/key algorithms external for the same reason. Do not infer a key
  from an A2L, a captured exchange, or a simulator.

## Apply safety gates

Read/listen operations may proceed after context validation. Ask for explicit
confirmation before transmitting unspecified traffic, changing ECU state,
clearing DTCs, invoking routines, unlocking security, writing identifiers,
starting download, flashing firmware, or changing analyzer persistent state.
LIN master power output, role changes, headers, and master writes are active bus
operations and require the same authorization discipline.

Before flashing, verify the exact ECU identity, firmware artifact and checksum,
address/segment map, programming preconditions, power stability, diagnostic
session, security level, rollback/recovery path, and user authorization. The
built-in `uds download` implements the transfer phase, not every OEM bootloader
precondition or post-programming validation step.

Analyzer firmware is a separate physical-device workflow. First query `bmcli
channel enumerate --format=json` and retain the intended analyzer's `sn` and
`uid`. Use `bmcli device update <serial-or-uid> --check --format=json` to inspect
the CID changelist; this is read-only. A selector may include `/port` to identify
the parent physical device, but the update affects every channel on that device.
Never substitute another enumerated device when an explicit selector is absent,
stale, or not found. Run `bmcli device update <serial-or-uid>` only after the user
explicitly authorizes analyzer downtime and all channels on that device are idle.
The current implementation supports generation-3 HPM devices only. For an
approved local image, confirm the exact device UID, obtain the exact file from
an approved source, independently verify its SHA-256, and invoke
`bmcli device update <serial-or-uid> --file=<path> --force` explicitly. Never
infer the model or version from the filename. BMCLI validates the HPM TinyUF2
image before entering HPMBOOT/BMBOOT and must report the same UID plus the
version embedded in the image after re-enumeration. CID-server updates retain
their normal download-before-boot flow. Never turn an available-update check
into an automatic flash during routine diagnosis or release testing.

Analyzer reset is also a physical-device operation, not a channel recovery or an
ECU diagnostic reset. Query `bmcli help device reset --format=json`, stop the
daemon only with authorization, and use a stable serial or UID selector. Run it
only when the user requests an analyzer reboot or lower-risk channel recovery has
failed. Accept success only when the bounded JSON result has exit code 0,
`action=device_reset`, `disconnect_observed=true`, `re_enumerated=true`, and the
same requested UID. The nested `api_status` may describe an interrupted USB
control transfer; never override a failed top-level result or substitute another
connected analyzer.

Starting `gateway` transmits and remotely exposes analyzer control, so require
authorization for the exact physical `SN/port` set, intended client IPv4, bus
timing/role, and trusted-network boundary. Query `bmcli help gateway start
--format=json`; do not invent a UDP protocol or bypass remote BMAPI. The current
GWR-compatible transport has no authentication or encryption and must not be
placed on an untrusted network. Gateway startup does not itself lock exported
channels. Ordinary send and passive receive/logging/decoding remain shared.
Before a compound local control workflow, claim every affected channel with
`bmcli channel claim`, then release it in cleanup. A conflicting remote owner
must produce busy; never work around it by stopping or resetting hardware
without authorization. Accept success only after checking structured owner,
session, generation, counters, and cleanup state.

The upper computer must use the normal public remote-BMAPI lifecycle. BMCLI
internally owns the GWR-compatible UDP server and physical-channel translation;
it does not use `BM_OpenRemote` as a reverse forwarding API.

The current canonical start shape is `bmcli gateway start
--channel=<SN/port>[,<SN/port>...] --target=<client-IPv4>
[--advertise=<gateway-IPv4>] [--name=<display-name>]`. UDP control port 3502 and
IO port 2502 are fixed protocol ports, not `--bind` or `--port` options. An
new upper computer uses `BM_Init -> BM_EnumerateRemote ->
BM_QueryRemoteSession -> BM_ClaimRemoteSession -> BM_OpenEx -> BM_Read/BM_Write
-> BM_Close -> BM_ReleaseRemoteSession -> BM_UnInit`. An old client may retain
the shorter unlocked lifecycle for compatibility, but do not present it as
deterministic multi-endpoint control.
