Skip to content

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