canair captures¶
Alias: cap
usage: canair captures [-h] <kind> ...
[UDS+CAN] Query captured data. Choose a kind:
uds diagnostic UDS payloads (captures/*.json) — the QUERY/diff/step/
summary/sessions/latest/recover surface (domain A)
can imported raw broadcast-CAN frame logs (captures/can/index.yaml,
domain B)
A bare `canair captures BMS 2102` (or any of the --summary/--sessions/… flags) is shorthand for `canair captures uds …`.
positional arguments:
<kind>
uds Query captured diagnostic UDS payloads across all capture
files
can List imported raw broadcast-CAN frame logs
(captures/can/index.yaml)
migrate Convert legacy captures/*.yaml to JSON (captures/*.json)
migrate-rx Rename the legacy capture `ecu` field to `rx`
(captures/*.json)
merge-driver Git merge driver: auto-union capture-file sessions (or
--install it)
options:
-h, --help show this help message and exit
canair captures uds¶
usage: canair captures uds [-h] [--diff | --step]
[--summary | --sessions | --latest | --recover | --delete | --backfill-states | --backfill-state-spans | --set-state STATES]
[--discard] [--overwrite] [--cycle-tol SECONDS]
[--dry-run] [--yes] [--all] [--limit N] [--rulers]
[--notation NAME]
[--view {auto,stacked,signals,changed,interleaved}]
[--join-tol SECONDS] [--json] [--since WHEN]
[--until WHEN] [--date YYYY-MM-DD] [--today]
[--last-sessions [N]] [--last-session]
[--state STATE] [--label SUBSTR] [--dir DIR]
[QUERY ...]
Query captured UDS payloads.
positional arguments:
QUERY ECU/PID selection: 'BMS 2102', 'BMS:2102,2103', 'BMS'
(all PIDs), or a quoted cross-ECU query 'VCU:2101
BMS:2101'
options:
-h, --help show this help message and exit
--diff, -d Monitor-style view (decoded params + colored byte-
diff), one block per ECU+PID
--step, -S Interactively step through matching captures (arrow
keys; a several-PID QUERY stacks them time-joined for
cross-comparison; e=note, d=delete, ?=help)
--summary, -s Overview statistics
--sessions, -n List sessions with their metadata
(date/state/label/notes/ECUs) — a searchable table of
contents; no payloads. Honors the scope filters.
--latest, -l Latest payload per PID (ECU/PID taken from the QUERY,
e.g. `BMS --latest`)
--recover Reconcile orphaned capture journals (from a
killed/crashed session) into capture files. Add
--discard to delete them without saving.
--delete Delete the captures matching QUERY (and any scope
filters). Previews with --dry-run; confirms before
deleting unless --yes.
--backfill-states Infer each session's vehicle_states from its decoded
captures and fill sessions that have none. Reports
conflicts (never writes them unless --overwrite).
Previews with --dry-run; confirms unless --yes. Honors
the scope filters.
--backfill-state-spans
Reconstruct WHEN each state held during a session (a
state_spans timeline) so analysis resolves a capture's
state at its own timestamp instead of the session-wide
union. Only narrows states the evidence can place in
time. Previews with --dry-run; confirms unless --yes.
--set-state STATES Manually set vehicle_states (comma-separated) on the
scope-selected sessions — for a state known from the
label but not inferable from the data (e.g. --set-
state ACC --label 'ACC only'). Requires a scope filter
(--label/--date/--since/…); previews with --dry-run,
confirms unless --yes.
--discard With --recover: delete orphaned journals without
saving them
--overwrite With --backfill-states: also rewrite sessions whose
recorded states conflict with / differ from the
inferred states (default: fill empty only). With
--backfill-state-spans: also replace live-observed
timelines
--cycle-tol SECONDS With --backfill-states/--backfill-state-spans: max
timestamp gap grouping captures into one pseudo-cycle
for cross-ECU predicates (default 10s)
--dry-run With --delete/--backfill-states/--backfill-state-
spans/--set-state: preview the changes, write nothing
--yes, -y With --delete/--backfill-states/--backfill-state-
spans/--set-state: skip the confirmation prompt
(scripting)
--all, -a For --diff/--step: use every payload instead of
unique-only
--limit N, -L N Default list view: show only the most recent N
captures (default 50; 0 = no cap). A loud footer
reports any hidden history. Also caps the frames
rendered by a piped/--json --step.
--rulers, -r For --diff/--step: show the byte-index ruler above the
hex (in the notation from --notation /
display.byte_notation)
--notation NAME byte-index notation for output labels: wican
(default), isotp, torque, bix. Overrides the
display.byte_notation config key.
--view {auto,stacked,signals,changed,interleaved}
For --step: how a frame is rendered — stacked (one
block per PID), signals (params only), changed (only
params that moved), interleaved (one capture per
frame). Default auto: stacked for up to 6 PIDs, else
interleaved. Cycle it live with V.
--join-tol SECONDS For --step: max timestamp difference when joining
captures of different PIDs into one stacked frame
(default 10s, sized for a full round-robin monitor
cycle; adjustable live with t / < / >)
--json Machine-readable JSON output
(summary/sessions/latest/diff/step and the default
QUERY list)
--dir DIR Captures directory (default: active profile)
scoping:
Restrict to captures within a date/time range (inclusive) and/or by session state (token-matched) or label substring. --since/--until accept a date (YYYY-MM-DD) or a timestamp (YYYY-MM-DD HH:MM[:SS[.ffffff]])
--since WHEN Only captures on or after this date/time (YYYY-MM-DD[
HH:MM:SS])
--until WHEN Only captures on or before this date/time (YYYY-MM-DD[
HH:MM:SS])
--date YYYY-MM-DD Only captures on this exact date (shorthand for
--since X --until X)
--today Only captures recorded today (shorthand for --date
<today>)
--last-sessions [N] Only the most recent N recorded sessions in scope (N
defaults to 1)
--last-session Only the most recent recorded session in scope (alias
for --last-sessions 1)
--state STATE Only captures recorded in STATE, matched by token and
widened by the profile's implies: hierarchy (--state
ready also matches DRIVING). Comma-separate
alternatives (--state ready,driving); repeat the flag
to require several at once (--state charging --state
parked)
--label SUBSTR Only captures whose session/capture label contains
SUBSTR (case-insensitive)
Query captured UDS payloads across all capture files.
QUERY selects the ECU(s) and PID(s) to show (see the mini-language below).
By default the matching captures are listed (most recent --limit, default 50);
add --diff or --step to change how they are rendered. --summary and --sessions
are aggregate modes that take no QUERY.
QUERY List matching captures (default view; latest --limit)
QUERY --diff Monitor-style view (decoded params + colored byte-diff),
one block per ECU+PID (unique payloads only; --all = all)
QUERY --step Interactive: step through captures with arrow keys,
decoded params + byte-diff vs the previous capture of the
same PID; e adds/edits a note, d deletes a capture.
A QUERY selecting SEVERAL PIDs stacks them underneath
each other in one time-joined frame (--join-tol), so
they can be cross-compared; PIDs, tolerance and view are
all editable inside the TUI (a/t/V), s jumps between
sessions and noted captures, ? for help
QUERY --latest Most recent payload per PID for the QUERY selection
--latest Most recent payload per PID (all ECUs; no QUERY)
QUERY --delete Delete the captures matching QUERY (and scope filters);
--dry-run previews, confirms before deleting unless --yes
--summary Overview: captures per ECU, per date, total payloads
--sessions Session table of contents: date/time-span/state/label/
notes/ECUs per session (no payloads); --json for machine
output. Honors the scope filters.
Step views (--view; default auto — stacked for up to 6 PIDs, else interleaved):
stacked One block per PID per frame: params + byte-diff hex
signals Params only (no hex) — fits more PIDs on one screen
changed Only params whose decoded value moved, per block
interleaved One capture per frame, chronologically across the PIDs
Output size (default list view):
--limit N Show only the most recent N captures (default 50; 0 =
no cap). A loud footer reports any hidden history — use
--limit 0 or a tighter scope (--since/--last-session) to
see the rest.
QUERY mini-language (see canlib/query.py):
ECU PID one PID (bare ECU + PID) e.g. BMS 2102
ECU all PIDs for an ECU e.g. VCU
ECU:PID one PID e.g. VCU:2101
ECU:PID,PID several PIDs e.g. VCU:2101,22BC03
"ECU:PID ECU:PID" cross-ECU (quote the space) e.g. "VCU:2101 BMS:2101"
ECU:22 prefix PID match (22xxxx) e.g. BCM:22
ECU:BC03 suffix PID match (->22BC03) e.g. IGPM:BC03
Date scoping (inclusive, YYYY-MM-DD; combines with any mode):
--since DATE captures on or after DATE
--until DATE captures on or before DATE
--date DATE captures on DATE only (--since DATE --until DATE)
State/label scoping (state by token, label by substring; combines with any mode):
--state SUBSTR only sessions whose vehicle_states contain SUBSTR (e.g. driving)
--label SUBSTR only sessions/captures whose label contains SUBSTR
Examples (a bare `canair captures …` is shorthand for `canair captures uds …`):
canair captures uds BMS 2102 # ECU + PID (most useful)
canair captures uds BMS # All BMS captures
canair captures uds "BMS:2102,2103" # Several PIDs
canair captures uds IGPM 22BC03 --diff # Byte-diff for one ECU+PID
canair captures uds "BMS:2102,2103" --diff # Byte-diff, one block per PID
canair captures uds BMS 2102 --step # Step through one PID
canair captures uds "BMS:2102,2103" --step # Stack two PIDs, time-joined
canair captures uds "HVAC:220100,2201A0,2201A2" --step # Cross-compare three PIDs
canair captures uds "VCU:2101 BMS:2101" --step # Cross-ECU compare
canair captures uds "VCU:2101 BMS:2101" --step --join-tol 1.0 # Tighter join
canair captures uds "HVAC:220100,2201A0" --step --view signals # Params only
canair captures uds BMS --step --view interleaved # Browse every BMS PID
canair captures uds "BMS:2102,2103" --step --json --limit 5 # Frames as data
canair captures uds --diff VCU:2101 --all # One PID, every payload
canair captures uds --summary # Overview stats
canair captures uds --sessions # Session table of contents
canair captures uds --sessions --state driving # Index of every drive
canair captures uds --sessions --json # Machine-readable TOC
canair captures uds BMS --latest # Latest payload per BMS PID
canair captures uds --latest # Latest payload per PID (all ECUs)
canair captures uds OBC 2101 --delete --dry-run # Preview a delete
canair captures uds OBC 2101 --delete --yes # Delete (non-interactive)
canair captures uds BMS 2102 --limit 200 # Widen the default 50-row cap
canair captures uds BMS 2102 --limit 0 # Every matching capture (no cap)
canair captures uds --summary --since 2026-04-19 # Stats since a date
canair captures uds BMS 2101 --diff --date 2026-04-19 # One day only
canair captures uds VCU --since 2026-04-14 --until 2026-04-21 # Range
canair captures can # List imported raw broadcast-CAN frame logs
canair captures can¶
usage: canair captures can [-h] [--json]
List imported raw broadcast-CAN frame logs (domain B) — file/format/frames/IDs per log. Import them with `canair import can`.
options:
-h, --help show this help message and exit
--json Machine-readable JSON output
canair captures migrate¶
usage: canair captures migrate [-h] [--dry-run] [--json] [--dir DIR]
Convert the active profile's legacy per-day capture files (captures/YYYY-MM-DD.yaml) to JSON (captures/YYYY-MM-DD.json).
Capture data is stored as JSON (parses ~60x faster than YAML); this is the supported one-time migration for a profile created before the cutover. Each file is round-trip verified before the YAML is replaced. Performs the migration by default; pass --dry-run to preview.
options:
-h, --help show this help message and exit
--dry-run Preview conversions without writing
--json Machine-readable JSON output
--dir DIR Captures directory (default: active profile)
canair captures migrate-rx¶
usage: canair captures migrate-rx [-h] [--dry-run] [--json] [--dir DIR]
Rename the persisted capture field `ecu` → `rx` in the active profile's capture files.
The field holds the ECU CAN *response* address (RX = request TX + 8), not an ECU name, so it was renamed to `rx` to stop it being confused with the resolved short name. Renames at the capture level and inside scan_results.responding[]; idempotent (a file already on `rx` is left untouched). Readers tolerate the legacy `ecu` key, so this migration is safe to defer. Performs the rename by default; pass --dry-run to preview.
options:
-h, --help show this help message and exit
--dry-run Preview renames without writing
--json Machine-readable JSON output
--dir DIR Captures directory (default: active profile)
canair captures merge-driver¶
usage: canair captures merge-driver [-h] [--install] [--json]
[base] [ours] [theirs] [path]
Git merge driver for append-only capture files (captures/*.json).
Invoked by git during a merge as `merge-driver %O %A %B %P`; unions the
session lists so two machines' same-day appends merge cleanly instead of
conflicting. Falls back to normal conflict markers on a genuine divergent
edit.
Run `canair captures merge-driver --install` once per clone to register it
in .git/config (git never loads a driver command from a tracked file, so
this local step is required; until then merges just fall back to markers).
positional arguments:
base git %O — common ancestor version
ours git %A — our version (also the output file)
theirs git %B — their version
path git %P — merged file's pathname (for messages)
options:
-h, --help show this help message and exit
--install Register the driver in this repo's .git/config (one-time, per
clone)
--json Machine-readable JSON output