The query mini-language¶
canair read — and the capture/decode tools — select ECUs and PIDs with a
small, consistent syntax.
Selectors¶
A selector is ECU[:PIDLIST]. (The BMS/IGPM/VCU names below are from
the bundled Ioniq profile, used as examples — the ECU and PID names available are
whatever the active profile defines; see canair ecu.)
| Selector | Meaning |
|---|---|
BMS |
all known PIDs for BMS |
BMS:2101 |
BMS PID 2101 only |
IGPM:BC03,BC06 |
two IGPM DIDs (comma-separated) |
VCU:2101 BMS:2101 |
cross-ECU — a space separates independent selectors |
A PID like 2101 vs 22B002 differs by its service-byte prefix — see
ECU protocols & PID prefixes for what the 21/22 means and
why canair can resolve a bare identifier.
Bind each PID to its ECU with a colon, never a space
IGPM 22BC07 means "all of IGPM plus a bogus ECU 22BC07" — write
IGPM:22BC07. A bare PID in the ECU slot is rejected with a hint. In a query
step, a space separates independent ECU selectors.
Groups (@name)¶
A group is a named, reusable set of selectors saved per-profile in
groups.yaml — a saved query. Recall one on the command line with the @
sigil; it expands to its member selectors before the query is parsed, so it
composes with other groups and with ad-hoc selectors:
canair monitor @charging # e.g. BMS:2101 BMS:2105 OBC VCU MCU
canair read @driving # a whole group
canair monitor @driving CLU:220B # a group plus an extra selector
canair monitor "@charging @powertrain" # two groups in one step
Groups work anywhere canair read/canair monitor take steps. Members are
plain selectors (ECU or ECU:PID) — never other groups, never full pipeline
steps. A group reference carries no PID suffix (@charging, not
@charging:2101).
List and edit the vocabulary with canair groups
(add/rm/rename/set-description/set-members) — never hand-edit
groups.yaml; the editor re-validates on write. canair validate groups checks
every member's ECU exists.
Overlapping selectors are coalesced¶
Groups overlap, and a group often already contains an ECU you also named by hand.
canair read/canair monitor therefore coalesce every overlapping selector down
to one per ECU before polling, however many steps or groups they came from:
If @driving already contains IGPM and AAF:2180 AAF:2181, that names IGPM
twice and AAF three times. Only one block per ECU is polled and rendered, and the
overlap is reported up front so the collapse is never silent:
Merged overlapping selectors (each ECU is polled once):
IGPM ← IGPM ×2
AAF ← AAF, AAF:2180, AAF:2181
The rules:
- A bare ECU supersedes its
ECU:PIDselectors.AAFalready means every AAF PID, soAAF+AAF:2180is justAAF. - Different PIDs on one ECU union.
BMS:2101+BMS:2105becomesBMS:2101,2105— a legitimate combination, so it merges without a note. - Aliases resolve to the canonical ECU.
LDCandOBCare one ECU, socanair read LDC OBC:2101polls it once. - Position is the first mention's. Coalescing never reorders the display.
- A non-
querystep ends the run. A deliberate re-read across a pipeline (canair read "query BMS:2101" "sleep 5" "query BMS:2101") keeps both reads.
Why this matters in canair monitor
A duplicated ECU used to be polled twice per cycle — halving the refresh rate for no new data — and its rows shared a selection key, so the TUI cursor and viewport would snap back to the first copy. Coalescing removes both.
Pipelines¶
canair read also accepts a pipeline of steps (each a quoted string), run
in order over one session. A bare selector is shorthand for a query step.
Step verbs include: query, session <ECU> [--wake], skm-wake [acc|ign1|ign2],
raw <TX:PID>, scan, iocontrol, sleep, repl. This lets you,
for example, wake an ECU, open a session, and read several PIDs in one command
over one connection.
Sessions and keepalive¶
Some ECUs only answer certain requests inside an extended diagnostic session.
Opening one (a session <ECU> step, or any command's --session) does the right
thing automatically:
Keeping a session alive is automatic
There is no tester-present command or flag. Once a session is open, canair
keeps it alive by sending TesterPresent (3E00) whenever the session goes
idle past the timeout; real request traffic resets that timer, so a busy
polling loop injects no redundant keepalives. TesterPresent is shared by UDS
and KWP2000, so it's sent identically regardless of the ECU's protocol.
To send one by hand, use a query step (canair read BMS:3E00).
After a session¶
Using the WebSocket terminal overrides the WiCAN's AutoPID mode. Pass --reboot
to any live command to restore AutoPID after your session ends.