diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96b594b..9298ce4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,19 +50,18 @@ jobs: - name: Benchmark smoke run run: pnpm run bench:frontend - # Coverage gate (T1.2): c8 enforces the configured line/branch/function + # Coverage gate: c8 enforces the configured line/branch/function # thresholds from .c8rc.json. Fails the job if coverage drops below them. - name: Coverage gate (overall >=85%) run: pnpm run coverage:frontend - # Per-domain coverage gate (T1.2): lib/ files must each meet >=90% line + # Per-domain coverage gate: lib/ files must each meet >=90% line # coverage (composables excluded — their onMounted/listen() lifecycle hooks - # are structurally unreachable in the headless test runner). Proven feasible - # at 98.79% overall lib/ line coverage. + # are structurally unreachable in the headless test runner). - name: Coverage gate (lib/ per-file >=90%) run: pnpm run coverage:lib - # Circular-dependency gate (T2.7): madge fails if any new import cycle is + # Circular-dependency gate: madge fails if any new import cycle is # introduced. Scans the TS source (Vue SFCs excluded — babel can't parse # them; their script blocks share the same module graph). - name: Circular-dependency gate @@ -99,7 +98,7 @@ jobs: - name: Rust tests run: cargo test --manifest-path src-tauri/Cargo.toml - # Rust coverage report (T1.2): cargo-tarpaulin runs only on Linux. Reported + # Rust coverage report: cargo-tarpaulin runs only on Linux. Reported # as a build artifact; not yet a hard gate (no committed threshold), but # surfaces coverage drift on the Rust side alongside the frontend c8 gate. - name: Rust coverage report (tarpaulin) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 92a862d..217ef28 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,204 +1,196 @@ # bbcom Architecture -A cross-platform serial debug assistant built on **Tauri 2** (Rust backend) + -**Vue 3** (strict-TypeScript frontend). This document captures the module -topology, the data-flow invariants ("sacred cows"), and the verification -strategy so a change can be reasoned about without tracing every file. - -## High-level topology - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ Webview (Vue 3 + Pinia + Naive UI) │ -│ │ -│ components/ composables/ stores/ │ -│ ├─ session/ ├─ useSerialConnection ├─ sessions │ -│ │ └─ SessionView ├─ useModbusMaster ├─ app │ -│ ├─ terminal/ ├─ useSessionModbus └─ serial │ -│ │ (DataPacketList, ├─ useExport │ -│ │ ModbusPanel, ├─ useAutoLog │ -│ │ ParserPanel, ├─ useTriggers │ -│ │ WaveformPanel) └─ usePortWatcher │ -│ ├─ send-panel/ │ -│ └─ app-shell/ lib/ (domain logic, framework-free) │ -│ ├─ modbus/ (barrel + modbus-core + │ -│ │ 13 domain modules: pdu/transport/ │ -│ │ registers/master-runtime) │ -│ ├─ format / bytes / lru-cache │ -│ ├─ serial-rx-queue (ring buffer) │ -│ ├─ protocol-parser / waveform │ -│ └─ session-persistence (+ migrate) │ -│ │ -│ types/ (domain barrels: display/serial/macros/modbus/ │ -│ waveform/ai/session/checksum/constants → index.ts) │ -└───────────────┬──────────────────────────────────┬──────────────┘ - │ typed IPC (src/lib/ipc.ts) │ Tauri events - ▼ ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ Rust backend (src-tauri/src/) │ -│ │ -│ commands/ models/ utils/ export/ │ -│ ├─ ai/ (mod/cooldown/ data_frame/ checksum/ formatter │ -│ │ prompts/service/ errors/ hex/ │ -│ │ parser/types/tests) timestamp │ -│ ├─ checksum.rs ↑ AppError (thiserror) │ -│ ├─ export.rs │ │ -│ ├─ log.rs │ serialplugin (RX event → JS) │ -│ └─ window.rs │ zai-rs (AI chat) │ -│ └ tokio::fs (stateless log/export) │ -└─────────────────────────────────────────────────────────────────┘ +This guide is for maintainers who need to change bbcom without re-discovering +the serial, rendering, persistence, and IPC boundaries from scratch. + +bbcom is a Tauri 2 desktop application with a strict-TypeScript Vue frontend and +a Rust backend. The most important ownership rule is simple: the frontend owns +sessions and protocol behavior; Rust owns privileged desktop work and small, +typed command surfaces. + +## Topology + +```text +┌────────────────────────────────────────────────────────────────────┐ +│ Webview: Vue 3 + Pinia + Naive UI │ +│ │ +│ components/ │ +│ app-shell/ main layout, settings, session creation │ +│ session/ session view + toolbar │ +│ terminal/ packet list, parser, Modbus, waveform panels │ +│ send-panel/ quick commands, macros, triggers, highlights │ +│ ai/ assistant settings and log assistant UI │ +│ │ +│ composables/ │ +│ useSerialConnection serial open/listen/write/reconnect │ +│ useModbusMaster Modbus read/write/replay orchestration │ +│ useSessionFrames frame append/clear helpers │ +│ usePacket* virtual scroll, filters, format cache │ +│ useExport dialog + export command routing │ +│ useAutoLog ordered per-session log appends │ +│ │ +│ stores/ │ +│ sessions all session state and persistence │ +│ serial available ports and selected config │ +│ app theme, language, AI and global settings │ +│ │ +│ lib/ │ +│ modbus/ protocol core, batching, transport, loops │ +│ serial-rx-queue bounded RX buffering │ +│ protocol-parser delimiter/fixed/length frame parsing │ +│ waveform* parsing, viewport math, canvas rendering │ +│ session-persistence versioned snapshot migration │ +│ ipc typed Tauri command wrappers │ +└───────────────┬────────────────────────────────────────────────────┘ + │ Tauri invoke/listen/events + ▼ +┌────────────────────────────────────────────────────────────────────┐ +│ Rust backend: src-tauri/src/ │ +│ │ +│ commands/ ai, checksum, export, log, updater, window commands │ +│ export/ TXT/CSV/JSONL/BIN formatters │ +│ models/ IPC structs and AppError │ +│ utils/ checksum, HEX, timestamp helpers │ +│ benches/ checksum/export hot-path benchmarks │ +└────────────────────────────────────────────────────────────────────┘ ``` -The frontend owns all session state and the serial protocol engines; the Rust -side is a thin, stateless IPC + filesystem + AI-client layer. - -## Sacred cows (inviolable invariants) - -These are the correctness guarantees a change must not break. Each is covered by -a test or bench. - -- **COW-1 — TX single-serialization.** Every transmit (cyclic loop, quick - command, trigger, AI-fill, Modbus) goes through `useSerialConnection.send` / - `sendBytes`, which chain onto a single `writeChain` promise so concurrent - callers never interleave `writeBinary` on the port. -- **COW-2 — Modbus single-busy / single-pending-RX.** `useModbusMaster` keeps a - busy guard and one pending RX slot (RTU is half-duplex). -- **COW-3 — Auto-log ordering chain.** `useAutoLog` serializes per-session - appends through a Promise chain; `append_log` is stateless. -- **COW-4 — Scroll single-flight RAF.** `usePacketVirtualScroll` coalesces - auto-scroll through one in-flight `requestAnimationFrame`. -- **COW-5 — Persistence backward compatibility.** Changing any persisted shape - requires bumping `SESSION_STORAGE_VERSION` + adding a `MIGRATION_STEPS` entry - + a legacy-data regression test. `migratePersistedFile` runs the chain on load. - -## Key data flows - -**RX (receive):** serialplugin emit → `useSerialConnection` data listener -(`listen(cb, false)` — skips per-chunk TextDecoder, F5) → `SerialRxQueue` -(ring buffer with head-index drops, O(1)) → RAF-batched `flushQueue` → -`addFrame` (markRaw frame into the session) → auto-log append + trigger feed. -Raw-byte observers (Modbus master) receive the exact chunk before coalescing. - -**TX (transmit):** any caller → `send`/`sendBytes` → `writeChain.then(doSend)` -→ `buildSendPayload` (validate + encode, the COW-1 input gate) → -`port.writeBinary` → TX frame added to the session. - -**Persistence:** session mutations mark the store dirty → debounced -`schedulePersist` (800 ms, max-wait 2.5 s) → `serializeSessionSnapshots` → -`saveJson` (localStorage). On load, `migratePersistedFile` walks -`MIGRATION_STEPS` then `hydrateSession`. - -## Upstream hard constraints (decision boundaries) - -- **F2** `timeout` is BOTH the port read-timeout AND the plugin emit flush - interval (default 200 ms), clamped `.min(1)` — it cannot be raised to extend - the read-timeout independently. -- **F5** `listen(cb, false)` skips the JS-side per-chunk TextDecoder; bbcom - already passes `false` on its single RX listener (audited, no stragglers). -- **F6** `read()` is lossy-UTF8 — binary data must use `read_binary`/`write_binary`. -- **F10** Tauri `Channel>` < 1 KiB degrades to `number[]` (slow); ≥ 1 KiB - is true binary. **AP-1**: do not Channel small packets as a "binary optimization". -- **F12** Very large transfers are fastest via a temp file (`convertFileSrc`). -- **F13** `zai-rs` `ModelName` is not dyn-safe → model dispatch is a match table, - not `Box` (**AP-2**). - -## T2.2 — `sessions` shallowRef conversion (LANDED) - -The `sessions` store is a `shallowRef` with a `notifyFramesChanged` reactivity -channel (`framesVersion` ref + `triggerRef(sessions)`), wired into every -mutator through `schedulePersist` (and `setModbusRegisterValues`). This is -AP-3-compliant — no `deep:true` watcher. Consumers that read -`session.frames.length` (DataPacketList, StatusBar, ParserPanel, WaveformPanel, -SessionTabs, SessionView, AiLogAssistant) stay reactive because every mutator -triggers the channel. - -**Measured (`sessions_push_50k`): 2 ops/s (428 ms) → 32 ops/s (29 ms), 15× -throughput / −93 % latency** — far past the −30 % target. A micro-spike had -earlier confirmed the raw shape: deep `ref` 55.2 ms vs `shallowRef`+trigger -5.5 ms (~10×) for a 50 k-frame push. - -**Reactivity safety:** a dedicated regression test -(`tests/frontend/sessions-frames-reactivity.test.ts`) asserts computed reads of -`frames.length`/`txBytes` reflect `addFrame`/`clearFrames`; the 23-test -modbus-master suite (incl. the reconnect-watches-store case) guards the -non-frame mutators. 576 frontend tests green. - -## Verification strategy - -| Gate | Command | Notes | -|---|---|---| -| Lint + format | `pnpm lint`, `pnpm format:check`, `cargo fmt --check`, `cargo clippy -D warnings` | clippy denies warnings | -| Type-check + build | `pnpm build` (vue-tsc --noEmit + vite) | strict TS | -| Frontend tests | `pnpm test:frontend` | node:test runner, 576 tests across 72 files | -| Rust tests | `pnpm test:rust` | 71 tests incl. cross-language IPC contracts | -| Coverage gate | `pnpm coverage:frontend` | c8, `.c8rc.json` (85% lines / 88% branches / 88% functions) | -| Per-file lib/ gate | `pnpm coverage:lib` | c8 `--per-file --lines=90` against `src/lib/` (excl. Tauri-coupled files) | -| Bench regression | `pnpm bench:frontend` | 15% gate vs machine-local baseline | -| Circular deps | `pnpm cycles` | madge, 0 cycles | -| Full check | `pnpm check` | lint + format + build + tests | - -CI (`.github/workflows/ci.yml`) runs all of the above on every push/PR. The -tag-triggered cross-platform build matrix (Windows / Linux / macOS) lives in -`.github/workflows/release.yml`. - -## Manual verification checklist - -These runtime paths need a physical serial device and/or a running Tauri app -and cannot be executed in the headless test harness. Each is marked with its -status and the reason it cannot be automated here. - -- ❌ **Connect / disconnect / reconnect** — requires a physical serial device - (or socat PTY pair) + a running Tauri webview. The connection lifecycle - (`useSerialConnection` start/stop/reconnect) is unit-tested via - `buildSendPayload` and the sessions-frames-reactivity guard, but the - end-to-end port-open/listen/write loop is driver-dependent. - **Status: ❌ — hardware/runtime-dependent.** -- ❌ **High-baud capture (921600)** — requires a physical device generating a - sustained byte stream. The F2/F3 config matrix (`timeout`/`size`/baud - sweep) and a reproducible socat/PTY procedure live in `CHANGELOG.md` - (T2.4). Headless proxies: `serialrxqueue_drop_512` (T2.1, +105%), - `sessions_push_50k` (T2.2, 2→32 ops/s). **Status: ❌ — hardware-dependent - (no device available).** -- ❌ **4-format export** — requires a live capture to export. The export path is - covered by the Rust formatter tests (8), the IPC contract tests (3), and - the F12 capture-file contract tests (3); the F12 path is verified to - produce byte-identical output. But the end-to-end dialog → file write - needs the running app. **Status: ❌ — runtime-dependent (Tauri dialog + - file picker).** -- ❌ **AI command + log** — requires a Z.ai API key + network access. The AI - command dispatch (F13 dispatch-table), cooldown guard, request/response - serde contracts, and SSE accumulator (F14) are all unit-tested. But the - live API call cannot run headless. **Status: ❌ — - network/credential-dependent.** -- ❌ **Modbus poll + write + replay** — requires a physical Modbus slave device. - The master loop, batching, replay coordinator, write source, and status - reporting are covered by 23 modbus-master tests. But the live RTU/PDU - transport needs a device. **Status: ❌ — hardware-dependent.** -- ❌ **Waveform + parser** — requires live RX data. The waveform buffer, - channel stats, parser frame collector, and `.bbrec` record/replay are - unit-tested. The canvas rendering loop needs the running app. - **Status: ❌ — runtime-dependent (canvas + live data).** -- ❌ **Light / dark** — requires the running Tauri webview to visually confirm - both themes. The CSS token system (`variables.css`, 138 tokens + - `[data-theme='light']` inversion) and `prefers-reduced-motion` are - verified; `vue-tsc` confirms the theme code compiles. But visual - confirmation needs the app. **Status: ❌ — runtime-dependent (visual - inspection).** - -**GUI launch verified:** `pnpm tauri:dev` was successfully run on this -machine — the Tauri app compiled, launched, and ran for 35+ seconds without -crashing (macOS WindowServer is accessible from this terminal session). However, -`screencapture` lacks Screen Recording permission, so visual UI verification -(themes, panel rendering, layout) cannot be captured. The items requiring a -physical serial device remain blocked (socat unavailable, no hardware). - -**lib/ per-file coverage gate (T1.2) — ENFORCED:** `coverage:lib` runs c8 -`--per-file --lines=90` against `src/lib/` (excluding Tauri-coupled files), -passing at ~98% with 0 errors. The composable ≥80% threshold remains -infeasible (lifecycle hooks structurally unreachable headless). The automated suite (576 frontend + 71 Rust -tests, 0 circular deps, ~87% coverage, 15% bench gate) covers every path that -CAN be tested headless. The remaining paths are documented with their blocker -and the headless proxy that validates the underlying logic. - -Failures should include reproduction steps, the baud/device, and the relevant -log line. +## Runtime Ownership + +- **Frontend session store:** source of truth for ports, frames, parser state, + Modbus config, macros, triggers, highlights, AI chat state, and persisted + snapshots. +- **Frontend protocol engines:** parser, waveform, Modbus, triggers, macro + control flow, and `.bbrec` replay are implemented in framework-free TypeScript + where possible so they can be unit-tested headlessly. +- **Rust command layer:** filesystem export/logging, checksum calculation, + updater wrapper, AI network calls, and window management. +- **Tauri plugins:** serialplugin provides serial port access, dialog provides + save/open UI, store persists local secrets, updater checks releases when + configured. + +## Data Flows + +### RX: device to screen + +1. `tauri-plugin-serialplugin` emits raw bytes to `useSerialConnection`. +2. Raw-byte observers receive the exact plugin chunk first. Modbus uses this + path to validate CRCs and match responses before display coalescing happens. +3. The chunk enters `SerialRxQueue`, which caps pending bytes/chunks and records + cumulative drops. +4. A single `requestAnimationFrame` drains pending chunks into one RX + `DataFrame`. +5. `useSessionFrames` appends the frame to the session store, auto-log appends + it if enabled, and trigger rules inspect the completed RX frame. + +### TX: caller to device + +1. Quick commands, send history, cyclic send, macros, triggers, AI fill, and + Modbus all enter `useSerialConnection.send` or `sendBytes`. +2. Text/HEX sends pass through `buildSendPayload` for validation and encoding. +3. A single `writeChain` promise serializes every `writeBinary` call in order. +4. A TX `DataFrame` is appended only after the port write succeeds. + +### Persistence + +1. Mutators call `schedulePersist`. +2. The sessions store emits the explicit frame/reactivity pulse and debounces + snapshot writes. +3. `serializeSessionSnapshots` caps persisted sessions, frame count, and bytes. +4. Load runs `migratePersistedFile` before hydration. Any persisted shape change + must bump `SESSION_STORAGE_VERSION`, add a migration step, and include a + legacy-data regression test. + +### Export + +- The legacy export command accepts a frame array directly. +- The default large-export path writes frames to a temporary JSONL capture file, + sends only that path through IPC, then Rust reads and formats the capture. + This avoids serializing large `Uint8Array` payloads into one huge invoke + argument. + +## Engineering Invariants + +These are the rules most likely to protect users from subtle serial bugs: + +- **All serial writes are single-filed.** Do not bypass `send`/`sendBytes` or the + `writeChain`; concurrent serial writes can interleave at the driver. +- **Modbus is half-duplex.** Keep one outstanding transaction at a time through + `ModbusTransactionRunner` and `ModbusLoopCoordinator`. +- **RX bytes reach protocol observers before display batching.** Protocol + engines need exact chunks; UI frames may be coalesced. +- **Frame arrays stay shallow.** Large captures must not depend on Vue deep + reactivity. Use `framesVersion`, `triggerRef(sessions)`, and raw frame items. +- **Auto-log preserves append order.** `useAutoLog` chains writes per session; + Rust `append_log` is stateless. +- **Persistence remains forward-compatible.** Shape changes require an explicit + migration and test. +- **Hot paths stay framework-free when practical.** Queueing, formatting, + parsing, Modbus batching, waveform math, and export filters should remain + testable without a Tauri webview. + +## Upstream Constraints + +- The serial plugin's timeout affects both read timeout and event flush cadence; + raising it changes UI latency as well as port behavior. +- `listen(callback, false)` is intentional: it avoids a per-chunk text decoder + in the JS path. Binary data should use binary read/write APIs. +- Tauri IPC can turn small binary payloads into number arrays; do not introduce + a channel-based "optimization" without measuring both small and large packets. +- `zai-rs` model types are concrete and not object-safe for the current dispatch + path, so supported AI models are selected with an explicit match table in + Rust and mirrored by the frontend registry. +- The updater plugin is configured but inactive until release endpoints and + signing keys are provided. + +## Quality Gates + +| Area | Command | +| --- | --- | +| Frontend lint | `pnpm lint` | +| Formatting | `pnpm format:check` | +| Type-check + frontend build | `pnpm build` | +| Frontend tests | `pnpm test:frontend` | +| Rust tests | `pnpm test:rust` | +| Frontend coverage | `pnpm coverage:frontend` | +| `src/lib/` per-file coverage | `pnpm coverage:lib` | +| Frontend benchmarks | `pnpm bench:frontend` | +| Rust benchmarks | `pnpm bench:rust` | +| TypeScript import cycles | `pnpm cycles` | +| Common local gate | `pnpm check` | + +CI runs the frontend lint/format/build/test/coverage/benchmark/cycle jobs and +Rust fmt/clippy/test jobs. Rust coverage is collected as a best-effort +tarpaulin artifact rather than a hard gate. + +## Manual Verification + +The headless tests cover the pure logic, IPC contracts, and hot-path helpers. +The following paths still need a real desktop runtime, serial device, PTY pair, +or external credential: + +- Connect, disconnect, reconnect, DTR/RTS, and BREAK behavior. +- Sustained high-baud captures from a real serial source. +- End-to-end export through native save dialogs. +- Live Modbus RTU/PDU polling, writes, and replay against a device. +- Waveform canvas rendering and parser panel interactions in the app. +- Light/dark visual inspection. +- Live Z.ai requests with a valid API key and network access. + +When reporting a manual failure, include platform, device/baud, transport mode, +reproduction steps, and the relevant log line or captured frame. + +## Change Checklist + +- Serial write path changed: confirm every caller still uses `send` or + `sendBytes`, then run frontend tests that cover send payloads, macros, + triggers, and Modbus. +- RX buffering changed: run queue, packet list, parser, waveform, trigger, and + benchmark tests. +- Persisted session shape changed: bump the version, add a migration, and add a + legacy snapshot test. +- Export changed: test TXT/CSV/JSONL/BIN plus capture-file and legacy paths. +- AI model changed: update both `src/lib/ai-models.ts` and + `src-tauri/src/commands/ai/service.rs`. +- Modbus changed: run the full Modbus frontend test set and manually test with + a device when transport timing changed. diff --git a/Cargo.lock b/Cargo.lock index 0a51502..b3b4e2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -170,7 +170,7 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bbcom" -version = "0.4.0" +version = "0.4.1" dependencies = [ "chrono", "crc", diff --git a/README.md b/README.md index 1522f6d..e51454b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # 🔌 bbcom -**Cross-Platform Serial Port Debug Assistant** +**Cross-platform desktop serial-port debug assistant** [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Tauri v2](https://img.shields.io/badge/Tauri-v2-blue?logo=tauri&logoColor=white)](https://v2.tauri.app/) @@ -12,7 +12,7 @@ [English](./README.md) · [中文](./README.zh-CN.md) -[⬇️ Download](https://github.com/AnlangA/bbcom/releases) +[Download](https://github.com/AnlangA/bbcom/releases) @@ -20,19 +20,41 @@ ## Overview -**bbcom** is a cross-platform desktop serial port debugging tool built with **Tauri v2 + Rust + Vue 3 + TypeScript**, designed for embedded developers' daily debugging workflows. - -### Key Highlights - -- 🔥 **Multi-Session** — Connect and monitor multiple serial ports simultaneously -- ⚡ **High Performance** — Virtual scrolling + RAF batch rendering stays smooth at high baud rates -- 🤖 **AI Terminal Assistant** — Natural language to shell commands, powered by ZHIPU AI -- 📊 **Waveform Visualization** — Parses numeric RX data and plots a live scrolling chart, Arduino Serial Plotter style -- 🛠️ **Modbus Master** — RTU/PDU transports, FC01-FC10 batched poll read/write, register-to-waveform channel binding -- 🎨 **Dark/Light Theme** — Comfortable for long debugging sessions with TX/RX color-coded frames, one-click toggle -- 💾 **Data Export** — TXT, CSV, JSONL, BIN formats -- 🔒 **CRC Checksums** — Checksum / CRC-8 / CRC-16 / CRC-32 calculation -- 🌐 **i18n** — English / 中文 UI with a persisted language preference +**bbcom** is a Tauri desktop tool for embedded developers who need a fast, +inspectable serial console. It combines multi-session serial TX/RX, protocol +parsing, Modbus master tooling, waveform plotting, export, and an optional +Z.ai-powered command/log assistant in one application. + +The frontend owns session state and protocol engines so high-volume serial data +can stay responsive in the webview. The Rust side stays deliberately small: +Tauri commands, filesystem/export work, checksums, updater integration, and AI +client calls. + +## Highlights + +- **Multi-session serial console** with independent connection state, TX/RX + counters, pause/resume, search, direction filters, and restored recent + captures. +- **High-throughput rendering path** using an O(1) RX queue, batched + `requestAnimationFrame` flushes, virtual scrolling, and explicit frame + reactivity. +- **Modbus master** for RTU and raw-PDU transports, FC01-FC06/FC10 operations, + contiguous read/write batching, periodic read/write loops, replay sources, + and register-to-waveform bindings. +- **Protocol tools** for delimiter, fixed-length, and length-field frame + parsing, plus `.bbrec` raw stream record/replay. +- **Waveform plotting** for numeric RX streams or Modbus register samples, with + channel visibility, statistics, autoscale, pause, clear, and CSV export. +- **Automation helpers** including macros with control flow, RX-triggered + responses, quick commands, send history, highlights, connection presets, + DTR/RTS control, and a 250 ms BREAK pulse. +- **Export and logging** to TXT, CSV, JSONL, and BIN; large exports use a + capture-file path so big frame sets do not cross IPC as one giant argument. +- **AI assistant** for Linux/BusyBox command generation and serial-log analysis, + with model validation, request cooldown, risk classification, and streaming + response assembly. +- **Desktop polish** with dark/light themes, English/Chinese UI catalogs, + persisted settings, keyboard shortcuts, and optional update checks. ## Screenshots @@ -50,357 +72,213 @@ Waveform Plot - Modbus registers window - Waveform plot window + Modbus registers + Waveform plot -## Features - -### Serial Communication - -- Real-time serial data TX/RX with **HEX / ASCII / UTF-8 / ANSI / HEX+ASCII** display modes (the HEX+ASCII dual view is a hex-editor split: hex pairs left, ASCII right, 16 bytes/line, non-printables as dots) -- Full serial parameter configuration: baud rate (9600 ~ 921600), data bits, stop bits, parity, flow control -- Multi-session management — connect and monitor multiple ports independently -- Recent session captures auto-restore across app restarts (ports stay disconnected until you reconnect), via a **versioned persistence migration chain** so persisted data stays forward-compatible -- Hot-plug detection with automatic device list refresh -- Millisecond-precision timestamps, per-frame and merged view modes -- Cyclic sending with customizable interval (50 ms ~ 1 h) -- **Sequenced macros with control flow** — `send`/`delay` plus `wait` (block until an RX pattern, with timeout), `if`/`else` (conditional branch on last RX), `goto`/`label` (jump), and a `maxSteps` anti-loop guard. Full Tera Term TTL–style bring-up scripts -- **Macro library import/export** (JSON) — share scripted sequences across sessions and machines -- **Connection presets** — save and reuse named port profiles (baud/data/stop/parity/flow/DTR/RTS) -- **BREAK signal** (250 ms) — one-click Arduino auto-reset / ESP32 bootloader entry -- DTR/RTS handshake line control for boot-mode selection -- **Large-write chunking** — TX payloads are split into ≤4 KiB chunks with retry + exponential backoff, so large sends aren't truncated by the serial plugin in release builds -- **Waveform visualization** — parses numeric RX data (CSV/space/semicolon) and plots a live scrolling chart, Arduino Serial Plotter / serial-studio style. Pause/resume, per-channel show/hide, min/max/avg stats, autoscale Y-axis labels, clear, and one-click CSV export -- **Protocol parser** — reassembles the RX byte stream into discrete frames by delimiter (CRLF/custom hex), fixed length, or length-field header; click any frame for a hex+ASCII dump, filter frames by text, and see live frame/byte/throughput stats. Presets include NMEA 0183, AT/modem, SCPI/instrument, length-prefixed (1B/2B BE+LE), and NUL-delimited binary -- **`.bbrec` record/replay** — capture the raw RX/TX byte stream to a versioned JSONL file and replay it through any protocol engine later (re-parse a capture with a different config, or use as regression data) -- **Modbus master** — RTU (addr+PDU+CRC) and PDU (raw, TCP-gateway style) transports; read FC01-FC04, write single FC05/06, write multiple FC10; contiguous addresses auto-batch; value types span bool/u8/i8/u16/i16/u32/i32/f32 (BE+LE); configurable poll/write intervals and timeout; per-row periodic read (R) or periodic write (W) toggles; registers can bind to waveform channels 0-7 for live plotting; `.bbreg` config import/export, batch Read all / Send all, and data-source Replay streaming -- **Tool tab bar** — Quick commands, Macros, Triggers, Highlights, and History share one compact horizontal tab strip with live count badges, replacing the stacked collapsible-tower layout -- **View-mode switcher** — Terminal, Waveform, and Parser are mutually-exclusive views toggled from the toolbar (one click each), so they never stack and compete for terminal height -- **Scripted triggers** — auto-send a configured response when the RX stream matches a text substring or hex byte sequence, with per-trigger cooldown to prevent loops -- **Live status metrics** — beyond cumulative TX/RX byte counts and B/s data rate, the status bar shows frames/s, buffer fill level (%), and cumulative dropped bytes (only when > 0) -- **Auto-update check** — optional update notification via `tauri-plugin-updater` (gracefully returns "no update" when no endpoint is configured) -- **Dark/Light theme** with a one-click sidebar toggle -- **i18n** — English / 中文 UI with a persisted language preference - -### Data Processing - -- Virtual scrolling + `requestAnimationFrame` batch rendering -- `SerialRxQueue` O(1) ring buffer with head-index drops (periodic compaction) — smooth at high baud rates without unbounded memory growth -- Direction-colored frames (TX green / RX blue) with direction filtering (All / TX / RX) -- Text & HEX search with debounce -- Per-session keyword highlights for TXT/HEX patterns, scoped to All/TX/RX with color tags -- ANSI escape sequence colored rendering -- Data export: TXT (HEX/ASCII), CSV, JSONL, BIN — large exports bypass IPC by writing a temp capture file the Rust side reads back (F12), so 100k-frame exports stay responsive -- **Time-range / direction export filter** — export only the frames in a `[startMs, endMs)` window, optionally restricted to TX or RX -- Right-click context menu for quick copy (HEX / ASCII / UTF-8 / full line) - -### Checksum Tools - -- Checksum / CRC-8 / CRC-16 / CRC-32 calculation - -### AI Terminal Assistant - -- Independent floating window, always on top, draggable & resizable -- Describe intent in natural language → AI generates Linux/BusyBox commands -- Powered by ZHIPU AI (`zai-rs`), supporting GLM-5.1 / GLM-5 Turbo / GLM-4.7 / GLM-4.5 Air models, dispatched via a static match table (not `Box`, so model selection is dyn-safe) -- **Streaming responses** — incremental SSE deltas are accumulated into the full reply with keep-alive handling and mid-stream abort support -- Command risk classification (Safe / Cautious / Dangerous) with auto-blocking of dangerous commands -- Serial log analysis assistant with per-session context and evidence extraction -- Optional Coding Plan mode for improved complex command generation quality -- One-click copy or fill into the send input box - -### User Experience - -- Dark/Light theme with green accent color, one-click sidebar toggle -- Configuration persistence — auto-restore serial params, display mode, parser templates, Modbus config, AI settings, and recent session captures -- Keyboard shortcuts: `Ctrl+N` new session, `Ctrl+W` close session, `Ctrl+L` clear buffer, `Esc` pause/resume capture, `Ctrl+Enter` send -- LRU cache for formatted results, ensuring performance with large data frames -- Send history + quick command management -- English / 中文 UI with a persisted language preference +## Core Features + +### Serial Console + +- Display modes: HEX, ASCII, UTF-8, ANSI, and HEX+ASCII split view. +- Port settings: baud rate, data bits, stop bits, parity, flow control, DTR, + and RTS. +- Hot-plug port refresh, reconnect attempts, millisecond timestamps, per-frame + and merged views. +- Cyclic sending, quick commands, send history, and large-write chunking with + retry/backoff. +- Trigger rules that match text or HEX in RX and send a response with cooldown. +- Keyword highlights scoped to All/TX/RX with text or HEX matching. + +### Protocol And Data Tools + +- Parser presets for CRLF, NMEA 0183, AT/modem, SCPI, NUL-delimited binary, and + length-prefixed frames. +- `.bbrec` capture/replay for raw RX/TX byte streams. +- Modbus `.bbreg` import/export for register tables and replay data sources. +- Export filters by time range and direction. +- Checksum / CRC-8 / CRC-16 / CRC-32 calculations. + +### AI Workflows + +- Standalone always-on-top assistant window. +- Natural-language prompt to terminal command, with safe/caution/dangerous risk + classification. +- Serial-log Q&A scoped to the current session context. +- Z.ai model registry shared between frontend validation and Rust dispatch. +- Stream accumulator for incremental SSE responses and abort handling. ## Tech Stack -| Layer | Technology | -| ----------------- | ------------------------------------------------------------------------------------------- | -| Desktop Framework | [Tauri v2](https://v2.tauri.app/) | -| Backend | [Rust](https://www.rust-lang.org/) (tokio / serde / chrono / crc / zai-rs) | -| Frontend | [Vue 3](https://vuejs.org/) Composition API + [TypeScript](https://www.typescriptlang.org/) | -| Build | [Vite 6](https://vite.dev/) | -| UI Components | [Naive UI](https://www.naiveui.com/) (Dark Theme) | -| State Management | [Pinia](https://pinia.vuejs.org/) | -| Virtual Scroll | [@tanstack/vue-virtual](https://tanstack.com/virtual) | -| ANSI Rendering | [ansi_up](https://github.com/drudru/ansi_up) | -| Auto-Update | [tauri-plugin-updater](https://v2.tauri.app/plugin/updater/) | -| Benchmarks | [criterion](https://bheisler.github.io/criterion.rs/) (Rust) + node:test (frontend) | -| Linting | ESLint 9 + typescript-eslint | -| Test Coverage | [c8](https://github.com/bcoe/c8) (frontend) + cargo-tarpaulin (Rust) | -| Dependency Graph | [madge](https://github.com/dependents/madge) (circular-dep gate) | -| Package Manager | [pnpm](https://pnpm.io/) | +| Layer | Technology | +| --- | --- | +| Desktop | [Tauri v2](https://v2.tauri.app/) | +| Backend | [Rust](https://www.rust-lang.org/) 2024, tokio, serde, thiserror, crc, zai-rs | +| Frontend | [Vue 3](https://vuejs.org/) Composition API + [TypeScript](https://www.typescriptlang.org/) | +| UI | [Naive UI](https://www.naiveui.com/), lucide-vue-next | +| State | [Pinia](https://pinia.vuejs.org/) | +| Build | [Vite 6](https://vite.dev/), pnpm | +| Serial | tauri-plugin-serialplugin | +| Persistence | localStorage snapshots + Tauri Store for local secrets | +| Test/Quality | node:test, c8, ESLint, Prettier, madge, cargo test, clippy, criterion | ## Getting Started ### Prerequisites -- **Rust** stable (edition 2024, minimum 1.85) -- **Node.js** 22+ -- **pnpm** 10+ -- Serial port access permissions on your OS +- Rust stable 1.85+ (edition 2024) +- Node.js 22+ +- pnpm 11+ +- OS permission to access serial ports -### Option 1: Using the Dev Script +On Linux you may need to add your user to the serial group, for example: ```bash -chmod +x scripts/dev.sh - -# Install dependencies -./scripts/dev.sh install - -# Start dev environment (frontend + Tauri) -./scripts/dev.sh dev - -# Build for production -./scripts/dev.sh build +sudo usermod -aG dialout "$USER" ``` -Additional commands: `frontend` (frontend only), `tauri` (Tauri only), `lint`, `test`, `help` - -### Option 2: Manual Commands +### Install And Run ```bash -# Install dependencies pnpm install - -# Development mode pnpm tauri:dev +``` -# Frontend only -pnpm dev +Frontend-only development: -# Production build -pnpm build # Frontend type check + build -pnpm tauri:build # Tauri packaging +```bash +pnpm dev ``` -### Available Scripts - -| Command | Description | -| -------------------- | --------------------------------------------- | -| `pnpm dev` | Start Vite frontend dev server | -| `pnpm build` | Vue type check + Vite build | -| `pnpm preview` | Preview frontend build output | -| `pnpm tauri:dev` | Start Tauri dev mode (with frontend HMR) | -| `pnpm tauri:build` | Build production desktop installer | -| `pnpm format` | Format frontend + Rust code | -| `pnpm format:check` | Check formatting without writing | -| `pnpm lint` | Lint the frontend with ESLint 9 | -| `pnpm test:frontend` | Run frontend unit tests with Node test runner | -| `pnpm test:rust` | Run Rust unit tests | -| `pnpm test` | Run frontend + Rust unit tests | -| `pnpm coverage:frontend` | Run frontend tests under `c8` with a coverage gate (`.c8rc.json`) | -| `pnpm coverage:lib` | Per-file `c8` gate: each `src/lib/` file ≥ 90 % line coverage | -| `pnpm bench:frontend`| Run frontend hot-path microbenchmarks (regression-gated) | -| `pnpm bench:frontend:write` | Rewrite the frontend perf baseline after an intentional optimization | -| `pnpm bench:rust` | Run Rust `criterion` benchmarks (CRC, export) | -| `pnpm cycles` | Detect circular dependencies (madge) | -| `pnpm check` | Run format check, lint, build, and all tests | - -### Performance Benchmarks - -The hot paths are covered by regression-gated benchmarks so a performance drop -fails CI just like a test failure: - -- **Frontend** (`pnpm bench:frontend`, `tests/frontend/perf.bench.ts`) — measures the per-frame formatting pipeline (`formatHex` / `formatUtf8`), the RX-flush `concatUint8Arrays`, the MERGED-view rebuild, the LRU format-cache hit rate, the `SerialRxQueue` overflow drop path, the 50 000-frame session push, and the Modbus read-batch composition. A baseline is stored in `tests/frontend/.perf-baseline.json` (machine-local, git-ignored); refresh it with `pnpm bench:frontend:write` after an intentional optimization. A regression > 15 % fails the run. -- **Rust** (`pnpm bench:rust`, `src-tauri/benches/hot_paths.rs`) — `criterion` benchmarks for the checksum algorithms (sum8 / CRC-8 / CRC-16 / CRC-32), `format_hex`, and the export formatter (JSONL / TXT-HEX at 1 k and 10 k frames). Reports ns/µs/ms with statistical confidence. -- **Unit tests** — 576 frontend tests (`pnpm test:frontend`, node:test runner) + 71 Rust tests (`pnpm test:rust`, incl. cross-language IPC contract tests), 0 circular dependencies (`pnpm cycles`), and an 85 % line / 88 % branch coverage gate (`pnpm coverage:frontend`, with a stricter ≥ 90 % per-file gate on `src/lib/`). -- **Bundle** — `ANALYZE=1 pnpm build` emits `dist/stats.html` (treemap) for chunk-size auditing. - -## Project Structure +Production builds: -``` -bbcom/ -├── src-tauri/ # Rust backend -│ ├── src/ -│ │ ├── commands/ # Tauri IPC commands -│ │ │ ├── ai/ # AI command generation + log analysis -│ │ │ │ (mod/cooldown/prompts/service/parser/types/tests) -│ │ │ ├── checksum.rs # Checksum / CRC calculation -│ │ │ ├── export.rs # Data export entry point (incl. F12 capture-file bypass) -│ │ │ ├── log.rs # Stateless append_log (auto-log / export JSONL) -│ │ │ ├── updater.rs # check_for_updates (tauri-plugin-updater wrapper) -│ │ │ ├── window.rs # AI assistant window commands -│ │ │ ├── ipc_contracts.rs # Cross-language IPC wire-shape tests -│ │ │ └── window_contracts.rs # AI-window command contract tests -│ │ ├── models/ # Data models -│ │ │ ├── data_frame.rs # Data frame (TX/RX + timestamp + bytes) -│ │ │ ├── errors.rs # Unified error types (thiserror) -│ │ │ └── checksum_type.rs -│ │ ├── export/ # Export formats (TXT / CSV / JSONL / BIN) -│ │ ├── utils/ # Utilities (HEX format / checksum / timestamp) -│ │ ├── lib.rs # App entry, window init & plugin registration -│ │ └── main.rs -│ ├── benches/hot_paths.rs # criterion benches (checksums / format / export) -│ ├── Cargo.toml -│ └── tauri.conf.json -├── src/ # Vue 3 frontend -│ ├── components/ -│ │ ├── port-selector/ # Serial port selector (+ connection presets) -│ │ ├── session-tabs/ # Session tab bar -│ │ ├── session/ # Session view (SessionView + SessionToolbar) -│ │ ├── send-panel/ # Send panel + AI assistant components -│ │ ├── terminal/ # Data list + protocol panels (virtual scroll) -│ │ │ (DataPacketList, ModbusPanel + Header/AddForm/RegisterRow, -│ │ │ ParserPanel + ConfigBar/StatsBar/FrameDetail, WaveformPanel + Legend) -│ │ ├── ai/ # AI floating window panels -│ │ ├── app-shell/ # Top-level app shell + sidebar -│ │ └── status-bar/ # Status bar (TX/RX stats, frames/s, buffer level, dropped) -│ ├── composables/ # Composable functions -│ │ ├── useSerialConnection.ts # Serial connect / listen / write (TX single-serialization) -│ │ ├── useSessionFrames.ts # Session frame operations -│ │ ├── useSessionModbus.ts # Modbus master orchestration -│ │ ├── useModbusMaster.ts # Modbus single-busy / single-pending-RX guard -│ │ ├── useAutoLog.ts # Per-session append_log ordering chain -│ │ ├── useTriggers.ts # Scripted RX→TX triggers (cooldown-guarded) -│ │ ├── usePacketFilter.ts # Direction/search/merged view filtering -│ │ ├── usePacketFormatter.ts # HEX / text / ANSI formatting cache -│ │ ├── useExport.ts # Export logic (F12 capture-file bypass) -│ │ ├── usePortWatcher.ts # Hot-plug monitoring -│ │ └── useSessionActions.ts -│ ├── stores/ # Pinia stores -│ │ ├── sessions.ts # Multi-session management (shallowRef + notifyFramesChanged) -│ │ ├── serial.ts # Serial device list -│ │ └── app.ts # Global settings (display / AI / shortcuts) -│ ├── lib/ # Pure TS, framework-free domain logic -│ │ ├── modbus/ # Modbus barrel (15 modules: core/pdu/transport/ -│ │ │ registers/master-runtime) -│ │ ├── format.ts # HEX / ASCII / UTF-8 / HEX+ASCII formatting -│ │ ├── bytes.ts # Uint8Array concatenation -│ │ ├── waveform.ts # Waveform parse / channel stats -│ │ ├── waveform-viewport.ts# Viewport transforms (normalize/zoom/scale/pan) -│ │ ├── waveform-render.ts # Canvas render pipeline (framework-free) -│ │ ├── protocol-parser.ts # Delimiter/fixed/length-field frame reassembly -│ │ ├── protocol-engine.ts # Transport-agnostic ProtocolEngine interface -│ │ ├── bbrec.ts # .bbrec raw byte-stream record/replay -│ │ ├── macro-control-flow.ts # Extended macros (wait/if/goto/label) -│ │ ├── macro-library.ts # Macro library import/export (JSON) -│ │ ├── trigger-engine.ts # RX substring/hex-match trigger engine -│ │ ├── serial-rx-queue.ts # O(1) ring buffer (head-index drops) -│ │ ├── write-chunking.ts # ≤4 KiB TX chunking + retry (F8) -│ │ ├── export-filters.ts # Time-range / direction export filter -│ │ ├── ai-models.ts # AI model registry (dispatch-table mirror) -│ │ ├── ai-stream.ts # SSE delta accumulator (F14) -│ │ ├── session-persistence.ts # Versioned migrate chain (COW-5) -│ │ ├── connection-presets.ts # Named port profiles -│ │ ├── logger.ts # Structured frontend logger -│ │ ├── ipc.ts # Typed Tauri command wrappers -│ │ ├── secure-settings.ts # Tauri Store-backed local secrets -│ │ ├── constants.ts # Baud rate / data bits constants -│ │ ├── serial-utils.ts # Serial port path / list utilities -│ │ ├── serial-config.ts # Serial port config → enum mapping -│ │ ├── lru-cache.ts # LRU cache -│ │ └── locales/ # i18n catalogs (en.ts / zh.ts / catalog.ts) -│ ├── types/ # Per-domain TS type barrels -│ │ ├── index.ts # re-export barrel -│ │ ├── display.ts serial.ts macros.ts modbus.ts -│ │ ├── waveform.ts ai.ts session.ts checksum.ts constants.ts -│ ├── styles/ # CSS variables (283 tokens) + global styles -│ ├── App.vue # Main window -│ ├── AiWindow.vue # AI floating window -│ └── main.ts # Entry point (route: main / AI window) -├── scripts/ -│ └── dev.sh # Dev helper script -├── tests/frontend/ # Frontend unit tests (72 files, node:test runner) -│ └── perf.bench.ts # regression-gated microbenchmarks -├── images/ # Screenshots -├── .github/workflows/ # ci.yml (lint/build/test/coverage/cycles) + release.yml -├── .c8rc.json # c8 coverage gate (85% lines / 88% branches) -├── package.json -├── vite.config.ts -├── eslint.config.mjs -└── tsconfig.json +```bash +pnpm build +pnpm tauri:build ``` -## Architecture +The helper script wraps the same common flows: -``` -┌──────────────────────────────────────────────────────────┐ -│ Vue 3 Frontend (Naive UI + Pinia + Virtual Scroll) │ -│ ┌───────────┐ ┌────────────┐ ┌───────────────────┐ │ -│ │PortSelect │ │SessionView │ │ AI Terminal Asst │ │ -│ └─────┬─────┘ └─────┬──────┘ └────────┬──────────┘ │ -│ │ │ │ │ -│ ┌─────┴───────────────┴──────────────────┴───────────┐ │ -│ │ Tauri IPC (invoke / listen / emit) │ │ -│ └───────────────────────┬────────────────────────────┘ │ -├──────────────────────────┼───────────────────────────────┤ -│ Rust Backend │ │ -│ ┌────────────────────────┴───────────────────────────┐ │ -│ │ commands: ai / checksum / export / log / updater │ │ -│ │ window │ │ -│ ├─────────────────────────────────────────────────────┤ │ -│ │ tauri-plugin-serialplugin (serial TX/RX) │ │ -│ │ tauri-plugin-dialog (file save dialog) │ │ -│ │ tauri-plugin-store (local settings) │ │ -│ │ tauri-plugin-updater (auto-update check) │ │ -│ │ zai-rs (ZHIPU AI Chat API) │ │ -│ └─────────────────────────────────────────────────────┘ │ -└──────────────────────────────────────────────────────────┘ +```bash +chmod +x scripts/dev.sh +./scripts/dev.sh install +./scripts/dev.sh dev +./scripts/dev.sh build ``` -- Serial port managed via `tauri-plugin-serialplugin`; frontend communicates with Rust backend through Tauri Command / Event -- Frontend owns all session state and the protocol engines (Modbus, parser, waveform); the Rust side is a thin, stateless IPC + filesystem + AI-client layer -- Frontend uses `requestAnimationFrame` + a bounded O(1) ring buffer (`SerialRxQueue`) for smooth UI at high baud rates; the `sessions` store is a `shallowRef` so 50 k-frame captures stay interactive -- TX is single-serialized through one `writeChain` promise so concurrent senders (cyclic loop, macros, triggers, AI-fill, Modbus) never interleave writes on the port -- Persistence is versioned — a `migratePersistedFile` chain runs on every load so a persisted-shape change stays forward-compatible -- AI assistant runs in an independent `WebviewWindow` — hidden (not destroyed) on close, synced via Tauri Event; responses stream as SSE deltas -- App settings persist locally; AI API keys migrate from legacy localStorage into Tauri Store +## Scripts + +| Command | Description | +| --- | --- | +| `pnpm dev` | Start the Vite dev server | +| `pnpm build` | Type-check the Vue app and build the frontend | +| `pnpm preview` | Preview the frontend build | +| `pnpm tauri:dev` | Run the Tauri desktop app with frontend HMR | +| `pnpm tauri:build` | Build the desktop application bundle | +| `pnpm format` | Format frontend and Rust code | +| `pnpm format:check` | Check frontend and Rust formatting | +| `pnpm lint` | Run ESLint on `src/` | +| `pnpm test:frontend` | Run frontend unit tests | +| `pnpm test:rust` | Run Rust unit tests | +| `pnpm test` | Run frontend and Rust tests | +| `pnpm coverage:frontend` | Run frontend tests under the `.c8rc.json` coverage gate | +| `pnpm coverage:lib` | Enforce per-file line coverage for framework-free `src/lib/` modules | +| `pnpm bench:frontend` | Run frontend hot-path benchmarks | +| `pnpm bench:frontend:write` | Refresh the local frontend benchmark baseline | +| `pnpm bench:rust` | Run Rust criterion benchmarks | +| `pnpm cycles` | Fail on TypeScript import cycles | +| `pnpm check` | Run lint, format check, build, and unit tests | + +## Project Map + +```text +bbcom/ +├── src/ # Vue frontend +│ ├── components/ # App shell, session view, terminal panels, AI panels +│ ├── composables/ # Serial connection, Modbus orchestration, export, triggers +│ ├── lib/ # Framework-free domain logic and IPC wrappers +│ │ ├── modbus/ # Request building, batching, transport, loops, replay +│ │ ├── format.ts # HEX/text/ANSI/HEX+ASCII formatting +│ │ ├── serial-rx-queue.ts # Bounded RX queue for high-rate captures +│ │ ├── protocol-parser.ts # Delimiter/fixed/length-field frame parsing +│ │ ├── waveform*.ts # Parsing, viewport math, and canvas rendering helpers +│ │ ├── session-persistence.ts +│ │ └── ipc.ts +│ ├── stores/ # Pinia stores for sessions, serial ports, app settings +│ ├── styles/ # Theme tokens and global CSS +│ ├── types/ # Domain type barrels +│ ├── App.vue # Main window entry +│ └── AiWindow.vue # Floating AI window entry +├── src-tauri/ # Rust backend +│ ├── src/commands/ # Tauri commands: ai, checksum, export, log, updater, window +│ ├── src/export/ # TXT/CSV/JSONL/BIN formatters +│ ├── src/models/ # IPC data and app error models +│ ├── src/utils/ # HEX, timestamp, checksum helpers +│ └── benches/hot_paths.rs # Criterion benchmarks +├── tests/frontend/ # node:test unit tests and frontend benchmarks +├── images/ # README screenshots +├── .github/workflows/ # CI and release workflows +├── ARCHITECTURE.md # Maintainer architecture guide +└── scripts/dev.sh # Development helper +``` -> For the full module topology, the inviolable invariants ("sacred cows"), the -> upstream hard constraints, and the manual-verification checklist, see -> [ARCHITECTURE.md](./ARCHITECTURE.md). +For module ownership, data-flow invariants, upstream constraints, and manual +verification guidance, read [ARCHITECTURE.md](./ARCHITECTURE.md). -## Contributing +## Verification -Contributions are welcome! Please follow these guidelines: +CI runs frontend lint/format/build/test/coverage/benchmark/cycle gates plus Rust +format, clippy, and tests. Rust coverage is collected as a best-effort tarpaulin +report in CI, while frontend coverage gates are enforced through c8. -1. **Commit Messages** — Follow [Conventional Commits](https://www.conventionalcommits.org/) -2. **Code Style** — ESLint 9 + typescript-eslint (`no-console: error`, `eqeqeq: error`) -3. **Rust** — Edition 2024, `tracing` for logging, `thiserror` for error handling -4. **TypeScript** — Strict mode (`strict: true`, `noUnusedLocals`, `noUnusedParameters`) -5. **Checks** — Run `pnpm check` before opening a PR +Before opening a pull request, run: -### Development Workflow +```bash +pnpm check +pnpm cycles +pnpm coverage:frontend +pnpm coverage:lib +``` -1. Fork the repository -2. Create a feature branch (`git checkout -b feat/my-feature`) -3. Commit your changes (`git commit -m 'feat: add something'`) -4. Push to the branch (`git push origin feat/my-feature`) -5. Open a Pull Request +Use `pnpm bench:frontend` and `pnpm bench:rust` when changing hot paths such as +formatting, RX queue handling, session frame storage, export, checksums, or +Modbus batching. ## FAQ
Which platforms are supported? -bbcom supports **Windows**, **macOS**, and **Linux**, thanks to Tauri v2's cross-platform architecture. +bbcom targets Windows, macOS, and Linux through Tauri v2.
-How do I get a ZHIPU AI API key? +How do I get a Z.ai API key? -Sign up at [open.bigmodel.cn](https://open.bigmodel.cn/) and create an API key. Enter it in the AI Assistant settings panel within bbcom. +Create a key at [open.bigmodel.cn](https://open.bigmodel.cn/) and enter it in +the AI assistant settings panel.
-Why is the serial port not showing up? +Why is my serial port missing? + +- Confirm the device is connected and the driver is installed. +- On Linux, check group permissions such as `dialout`. +- On macOS, check `ls /dev/cu.*`. +- Reopen the port list after plugging in the device. -- Make sure the device is connected and drivers are installed -- On Linux, you may need to add your user to the `dialout` group: `sudo usermod -aG dialout $USER` -- On macOS, check `ls /dev/cu.*` in Terminal
+## Contributing + +Please use Conventional Commits, keep TypeScript strict, keep Rust warnings +clean, and include focused tests for behavior changes. For persisted session +shape changes, bump `SESSION_STORAGE_VERSION`, add a migration step, and cover +legacy data with a regression test. + ## License This project is licensed under the [MIT License](LICENSE). diff --git a/README.zh-CN.md b/README.zh-CN.md index cc50ac2..43210a0 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -12,7 +12,7 @@ [English](./README.md) · [中文](./README.zh-CN.md) -[⬇️ 下载](https://github.com/AnlangA/bbcom/releases) +[下载](https://github.com/AnlangA/bbcom/releases) @@ -20,19 +20,34 @@ ## 概述 -**bbcom** 是一款跨平台桌面串口调试工具,基于 **Tauri v2 + Rust + Vue 3 + TypeScript** 构建,面向嵌入式开发者的日常调试场景。 - -### 核心亮点 - -- 🔥 **多会话管理** — 同时连接并监控多个串口,独立收发互不干扰 -- ⚡ **高性能渲染** — 虚拟滚动 + 批量渲染,高波特率下 UI 流畅不卡顿 -- 🤖 **AI 终端助手** — 自然语言描述意图,AI 自动生成终端命令 -- 📊 **波形可视化** — 解析 RX 数值实时绘图,Arduino Serial Plotter 风格 -- 🛠️ **Modbus 主站** — RTU/PDU 传输,FC01-FC10 批量轮询读写,寄存器可绑定波形通道 -- 🎨 **深色/浅色主题** — TX/RX 方向着色,一键切换,长时间调试更舒适 -- 💾 **数据导出** — 支持 TXT、CSV、JSONL、BIN 四种格式 -- 🔒 **校验工具** — Checksum / CRC-8 / CRC-16 / CRC-32 计算 -- 🌐 **中英双语** — 界面语言一键切换,偏好持久化 +**bbcom** 是面向嵌入式开发者的 Tauri 桌面串口调试工具。它把多会话 +串口收发、协议解析、Modbus 主站、波形绘图、数据导出和可选的 Z.ai +命令/日志助手放在同一个应用里。 + +项目的前端负责会话状态与协议引擎,以便在高吞吐串口数据下保持界面 +响应;Rust 侧保持轻量,主要承担 Tauri 命令、文件导出、校验、更新器 +集成和 AI 客户端调用。 + +## 核心亮点 + +- **多会话串口终端**:每个串口会话拥有独立连接状态、收发统计、 + 暂停/继续、搜索、方向过滤和最近捕获恢复。 +- **高吞吐渲染链路**:O(1) RX 队列、`requestAnimationFrame` 批量 + 刷新、虚拟滚动和显式帧响应式通知协同工作。 +- **Modbus 主站**:支持 RTU 与裸 PDU 传输、FC01-FC06/FC10、连续地址 + 批量读写、周期读/写、回放数据源和寄存器绑定波形通道。 +- **协议工具**:按分隔符、定长、长度字段解析帧,并支持 `.bbrec` + 原始 RX/TX 字节流录制与回放。 +- **波形绘图**:可绘制 RX 文本数值或 Modbus 寄存器样本,支持通道 + 显隐、统计、自动缩放、暂停、清空和 CSV 导出。 +- **自动化辅助**:带控制流的宏、RX 触发响应、快捷命令、发送历史、 + 高亮规则、连接预设、DTR/RTS 控制和 250 ms BREAK 脉冲。 +- **导出与日志**:支持 TXT、CSV、JSONL、BIN;大体量导出走捕获文件 + 路径,避免把巨大帧数组作为单个 IPC 参数传递。 +- **AI 助手**:Linux/BusyBox 命令生成与串口日志分析,包含模型校验、 + 请求冷却、风险分级和流式响应拼装。 +- **桌面体验**:深色/浅色主题、中英文界面、本地设置持久化、快捷键 + 和可选更新检查。 ## 截图 @@ -50,355 +65,207 @@ 波形绘图 - Modbus 寄存器窗口 - 波形绘图窗口 + Modbus 寄存器 + 波形绘图 ## 功能 -### 串口通信 - -- 实时串口数据收发,支持 **HEX / ASCII / UTF-8 / ANSI / HEX+ASCII** 五种显示模式(HEX+ASCII 双视图:左侧 HEX 对、右侧 ASCII,每行 16 字节,不可见字符显示为点) -- 完整的串口参数配置:波特率(9600 ~ 921600)、数据位、停止位、奇偶校验、流控 -- 多会话管理 — 同时连接并监控多个串口,独立收发互不干扰 -- 最近会话捕获自动恢复(重启后保留最近日志,串口不会自动重连),通过**版本化持久化迁移链**保证旧数据向前兼容 -- 热插拔检测,串口设备列表自动刷新 -- 毫秒级精确时间戳,按帧/合并两种查看模式 -- 循环发送,可自定义间隔(50ms ~ 1h) -- **带控制流的宏命令序列** — 除 `send`/`delay` 外,支持 `wait`(等待 RX 模式并支持超时)、`if`/`else`(依据最近 RX 条件分支)、`goto`/`label`(跳转),以及 `maxSteps` 防死循环。完整的 Tera Term TTL 风格引导脚本 -- **宏库导入/导出(JSON)** — 跨会话、跨机器共享脚本序列 -- **连接预设** — 保存并复用命名的串口配置(波特率/数据位/停止位/校验/流控/DTR/RTS) -- **BREAK 信号**(250ms)— 一键 Arduino 自动复位 / ESP32 进入下载模式 -- DTR/RTS 握手线控制,用于选择启动模式 -- **大写入分块** — TX 负载拆分为 ≤4 KiB 分块并带指数退避重试,避免 release 构建下串口插件截断大写入 -- **波形可视化** — 解析 RX 中的数值(逗号/空格/分号分隔)并实时滚动绘图,Arduino Serial Plotter / serial-studio 风格;支持暂停/继续、通道显隐、最小/最大/均值统计、Y 轴自动坐标、清空与一键 CSV 导出 -- **协议解析模板** — 按分隔符(CRLF/自定义 HEX)、定长、长度字段头重组 RX 字节流为独立帧;点击单帧查看 HEX+ASCII 详情,文本筛选与帧/字节/吞吐量统计;内置 NMEA 0183、AT/Modem、SCPI/仪器、长度前缀(1B/2B BE+LE)、NUL 分隔二进制等预设 -- **`.bbrec` 录制/回放** — 将裸 RX/TX 字节流录制为版本化 JSONL 文件,之后可通过任意协议引擎回放(用不同配置重新解析历史捕获,或用作回归数据) -- **Modbus 主站** — RTU(地址+PDU+CRC)与 PDU(裸 PDU,TCP 网关风格)两种传输;支持 FC01-FC04 读、FC05/06 写单、FC10 写多;连续地址自动批量,值类型覆盖 bool/u8/i8/u16/i16/u32/i32/f32(BE+LE);可配置轮询/写间隔与超时,每行独立启用周期读(R)或周期写(W);寄存器可绑定到 0-7 号波形通道实时绘图;支持 `.bbreg` 配置导入/导出、批量 Read all / Send all、以及按数据源的 Replay 流回放 -- **工具横栏** — 快捷命令、宏、触发器、高亮、历史统一在一个紧凑的横栏标签页中切换(带数量徽标),取代原先的折叠塔式布局 -- **视图切换** — 终端、波形、协议解析三种视图互斥切换(工具栏一键切换),不再堆叠挤占终端高度 -- **脚本触发器** — RX 文本或 HEX 匹配后自动发送响应,带冷却时间防止循环触发 -- **实时状态指标** — 除累计收发字节数与 B/s 速率外,状态栏还显示 frames/s、缓冲区填充率(%)以及累计丢字节数(仅 > 0 时显示) -- **自动更新检查** — 通过 `tauri-plugin-updater` 可选的更新通知(未配置端点时优雅返回"无更新") -- **深色/浅色主题** — 侧边栏一键切换 -- **中英双语** — 界面语言一键切换,偏好持久化 - -### 数据处理 - -- 虚拟滚动(`@tanstack/vue-virtual`)+ `requestAnimationFrame` 批量渲染,高波特率下 UI 流畅不卡顿 -- `SerialRxQueue` O(1) 环形缓冲(头索引丢弃 + 周期压实),高波特率下保持流畅且内存不无限增长 -- 数据帧按方向着色(TX 绿 / RX 蓝),支持方向过滤(全部 / TX / RX) -- 文本搜索 & HEX 搜索,带防抖 -- 会话级关键词高亮:支持 TXT/HEX 模式、全部/TX/RX 范围和颜色标记 -- ANSI 转义序列着色渲染 -- 数据导出:TXT(HEX / ASCII)、CSV、JSONL、BIN 四种格式 —— 大体量导出通过写入临时捕获文件由 Rust 端读回(F12 旁路),10 万帧导出依旧流畅 -- **时间范围 / 方向导出过滤** — 仅导出 `[startMs, endMs)` 区间内的帧,可选限制为仅 TX 或 RX -- 右键菜单快速复制 HEX / ASCII / UTF-8 / 完整行 - -### 校验工具 - -- Checksum / CRC-8 / CRC-16 / CRC-32 校验计算 - -### AI 终端助手 - -- 独立悬浮窗口,始终置顶,可拖拽、可调整大小 -- 自然语言描述意图,AI 自动生成 Linux/BusyBox 终端命令 -- 基于 ZHIPU AI(`zai-rs`),支持 GLM-5.1 / GLM-5 Turbo / GLM-4.7 / GLM-4.5 Air 模型,通过静态 match 表分派(非 `Box`,模型选择 dyn-safe) -- **流式响应** — 增量 SSE delta 累加为完整回复,支持 keep-alive 处理与中途中止 -- 命令风险分级(安全 / 谨慎 / 危险),危险命令自动拦截 -- 串口日志分析助手,基于当前会话上下文提取依据和建议 -- 可选启用 Coding Plan 模式,提升复杂命令的生成质量 -- 生成结果一键复制或填入发送输入框 - -### 用户体验 - -- 深色/浅色主题,绿色主色调,侧边栏一键切换 -- 配置持久化 — 自动恢复上次串口参数、显示模式、协议解析模板、Modbus 配置、AI 设置和最近会话捕获 -- 快捷键:`Ctrl+N` 新建会话、`Ctrl+W` 关闭会话、`Ctrl+L` 清空、`Esc` 暂停/继续捕获、`Ctrl+Enter` 发送 -- LRU 缓存格式化结果,保证大量数据帧下的渲染性能 -- 发送历史记录 + 快捷指令管理 -- 中英双语界面,语言偏好持久化 +### 串口终端 + +- 显示模式:HEX、ASCII、UTF-8、ANSI、HEX+ASCII 分栏视图。 +- 串口参数:波特率、数据位、停止位、奇偶校验、流控、DTR 和 RTS。 +- 热插拔刷新、自动重连尝试、毫秒级时间戳、按帧/合并视图。 +- 循环发送、快捷命令、发送历史,以及带重试/退避的大写入分块。 +- RX 文本或 HEX 匹配后自动发送响应,触发器自带冷却时间。 +- 会话级关键词高亮,可限定 All/TX/RX,并支持文本或 HEX 匹配。 + +### 协议与数据工具 + +- 内置 CRLF、NMEA 0183、AT/Modem、SCPI、NUL 分隔二进制和长度前缀帧 + 等解析预设。 +- `.bbrec` 捕获/回放原始 RX/TX 字节流。 +- `.bbreg` 导入/导出 Modbus 寄存器表和回放数据源。 +- 按时间范围与方向过滤导出内容。 +- Checksum / CRC-8 / CRC-16 / CRC-32 计算。 + +### AI 工作流 + +- 独立的始终置顶助手窗口。 +- 自然语言生成终端命令,并给出安全/谨慎/危险风险分级。 +- 基于当前会话上下文回答串口日志问题。 +- 前端模型注册表与 Rust 分发表保持一致。 +- 支持增量 SSE 响应累加和中途终止状态。 ## 技术栈 -| 层级 | 技术 | -| --------- | ------------------------------------------------------------------------------------------- | -| 桌面框架 | [Tauri v2](https://v2.tauri.app/) | -| 后端 | [Rust](https://www.rust-lang.org/)(tokio / serde / chrono / crc / zai-rs) | -| 前端 | [Vue 3](https://vuejs.org/) Composition API + [TypeScript](https://www.typescriptlang.org/) | -| 构建 | [Vite 6](https://vite.dev/) | -| UI 组件库 | [Naive UI](https://www.naiveui.com/)(Dark Theme) | -| 状态管理 | [Pinia](https://pinia.vuejs.org/) | -| 虚拟滚动 | [@tanstack/vue-virtual](https://tanstack.com/virtual) | -| ANSI 渲染 | [ansi_up](https://github.com/drudru/ansi_up) | -| 自动更新 | [tauri-plugin-updater](https://v2.tauri.app/plugin/updater/) | -| 基准测试 | [criterion](https://bheisler.github.io/criterion.rs/)(Rust)+ node:test(前端) | -| 代码规范 | ESLint 9 + typescript-eslint | -| 测试覆盖 | [c8](https://github.com/bcoe/c8)(前端)+ cargo-tarpaulin(Rust) | -| 依赖图 | [madge](https://github.com/dependents/madge)(循环依赖门) | -| 包管理 | [pnpm](https://pnpm.io/) | +| 层级 | 技术 | +| --- | --- | +| 桌面 | [Tauri v2](https://v2.tauri.app/) | +| 后端 | [Rust](https://www.rust-lang.org/) 2024、tokio、serde、thiserror、crc、zai-rs | +| 前端 | [Vue 3](https://vuejs.org/) Composition API + [TypeScript](https://www.typescriptlang.org/) | +| UI | [Naive UI](https://www.naiveui.com/)、lucide-vue-next | +| 状态 | [Pinia](https://pinia.vuejs.org/) | +| 构建 | [Vite 6](https://vite.dev/)、pnpm | +| 串口 | tauri-plugin-serialplugin | +| 持久化 | localStorage 会话快照 + Tauri Store 本地密钥 | +| 测试与质量 | node:test、c8、ESLint、Prettier、madge、cargo test、clippy、criterion | ## 快速开始 ### 环境要求 -- **Rust** stable(edition 2024,最低 1.85) -- **Node.js** 22+ -- **pnpm** 10+ -- 操作系统串口访问权限 +- Rust stable 1.85+(edition 2024) +- Node.js 22+ +- pnpm 11+ +- 操作系统允许访问串口 -### 方式一:使用开发脚本 +Linux 下可能需要把用户加入串口权限组,例如: ```bash -chmod +x scripts/dev.sh - -# 安装依赖 -./scripts/dev.sh install - -# 启动开发环境(前端 + Tauri) -./scripts/dev.sh dev - -# 构建生产包 -./scripts/dev.sh build +sudo usermod -aG dialout "$USER" ``` -其他命令:`frontend`(仅前端)、`tauri`(仅 Tauri)、`lint`、`test`、`help` - -### 方式二:手动命令 +### 安装与运行 ```bash -# 安装依赖 pnpm install - -# 开发模式 pnpm tauri:dev +``` -# 仅前端 -pnpm dev +仅前端开发: -# 生产构建 -pnpm build # 前端类型检查 + 构建 -pnpm tauri:build # Tauri 打包 +```bash +pnpm dev ``` -### 可用脚本 - -| 命令 | 说明 | -| ------------------------ | -------------------------------------------------- | -| `pnpm dev` | 启动 Vite 前端开发服务器 | -| `pnpm build` | Vue 类型检查 + Vite 构建 | -| `pnpm preview` | 预览前端构建产物 | -| `pnpm tauri:dev` | 启动 Tauri 开发模式(含前端热重载) | -| `pnpm tauri:build` | 构建生产桌面安装包 | -| `pnpm format` | 格式化前端 + Rust 代码 | -| `pnpm format:check` | 检查格式(不写入) | -| `pnpm lint` | 使用 ESLint 9 对前端进行 lint | -| `pnpm test:frontend` | 使用 Node test runner 运行前端单元测试 | -| `pnpm test:rust` | 运行 Rust 单元测试 | -| `pnpm test` | 运行前端 + Rust 单元测试 | -| `pnpm coverage:frontend` | 在 `c8` 覆盖率门下运行前端测试(`.c8rc.json`) | -| `pnpm coverage:lib` | 逐文件 `c8` 门:每个 `src/lib/` 文件行覆盖率 ≥ 90% | -| `pnpm bench:frontend` | 运行前端热路径微基准(回归门控) | -| `pnpm bench:frontend:write` | 在有意优化后重写前端基准基线 | -| `pnpm bench:rust` | 运行 Rust `criterion` 基准(CRC、导出) | -| `pnpm cycles` | 检测循环依赖(madge) | -| `pnpm check` | 运行格式检查、lint、build 和全部测试 | - -## 性能基准 - -热路径由回归门控基准覆盖,性能回退会让 CI 像测试失败一样失败: - -- **前端**(`pnpm bench:frontend`,`tests/frontend/perf.bench.ts`)—— 测量每帧格式化流水线(`formatHex` / `formatUtf8`)、RX flush 的 `concatUint8Arrays`、MERGED 视图重建、LRU 格式缓存命中率、`SerialRxQueue` 溢出丢弃路径、5 万帧会话推送、Modbus 读批量组装。基线存于 `tests/frontend/.perf-baseline.json`(机器本地,git-ignored);有意优化后用 `pnpm bench:frontend:write` 刷新。回退 > 15% 即失败。 -- **Rust**(`pnpm bench:rust`,`src-tauri/benches/hot_paths.rs`)—— `criterion` 基准:校验和算法(sum8 / CRC-8 / CRC-16 / CRC-32)、`format_hex`、导出格式化(JSONL / TXT-HEX,1k 与 10k 帧)。带统计置信的 ns/µs/ms 报告。 -- **单元测试** —— 576 个前端测试(`pnpm test:frontend`,node:test 运行器)+ 71 个 Rust 测试(`pnpm test:rust`,含跨语言 IPC 契约测试),0 循环依赖(`pnpm cycles`),85% 行 / 88% 分支覆盖率门(`pnpm coverage:frontend`,`src/lib/` 另有更严格的 ≥ 90% 逐文件门)。 -- **打包** —— `ANALYZE=1 pnpm build` 生成 `dist/stats.html`(treemap)用于 chunk 体积审计。 - -## 项目结构 +生产构建: -``` -bbcom/ -├── src-tauri/ # Rust 后端 -│ ├── src/ -│ │ ├── commands/ # Tauri IPC 命令 -│ │ │ ├── ai/ # AI 命令生成 + 日志分析 -│ │ │ │ (mod/cooldown/prompts/service/parser/types/tests) -│ │ │ ├── checksum.rs # 校验和 / CRC 计算 -│ │ │ ├── export.rs # 数据导出入口(含 F12 捕获文件旁路) -│ │ │ ├── log.rs # 无状态 append_log(自动日志 / 导出 JSONL) -│ │ │ ├── updater.rs # check_for_updates(tauri-plugin-updater 封装) -│ │ │ ├── window.rs # AI 助手窗口命令 -│ │ │ ├── ipc_contracts.rs # 跨语言 IPC 线缆契约测试 -│ │ │ └── window_contracts.rs # AI 窗口命令契约测试 -│ │ ├── models/ # 数据模型 -│ │ │ ├── data_frame.rs # 数据帧(TX/RX + 时间戳 + 字节数据) -│ │ │ ├── errors.rs # 统一错误类型(thiserror) -│ │ │ └── checksum_type.rs -│ │ ├── export/ # 导出格式实现(TXT / CSV / JSONL / BIN) -│ │ ├── utils/ # 工具函数(HEX 格式化 / 校验 / 时间戳) -│ │ ├── lib.rs # 应用入口,窗口初始化与插件注册 -│ │ └── main.rs -│ ├── benches/hot_paths.rs # criterion 基准(校验和 / 格式化 / 导出) -│ ├── Cargo.toml -│ └── tauri.conf.json -├── src/ # Vue 3 前端 -│ ├── components/ -│ │ ├── port-selector/ # 串口选择器(+ 连接预设) -│ │ ├── session-tabs/ # 会话标签栏 -│ │ ├── session/ # 会话视图(SessionView + SessionToolbar) -│ │ ├── send-panel/ # 发送面板 + AI 助手组件 -│ │ ├── terminal/ # 数据列表 + 协议面板(虚拟滚动) -│ │ │ (DataPacketList, ModbusPanel + Header/AddForm/RegisterRow, -│ │ │ ParserPanel + ConfigBar/StatsBar/FrameDetail, WaveformPanel + Legend) -│ │ ├── ai/ # AI 悬浮窗口面板 -│ │ ├── app-shell/ # 顶层应用外壳 + 侧边栏 -│ │ └── status-bar/ # 状态栏(收发统计 / frames/s / 缓冲区 / 丢字节) -│ ├── composables/ # 组合式函数 -│ │ ├── useSerialConnection.ts # 串口连接 / 监听 / 写入(TX 单序列化) -│ │ ├── useSessionFrames.ts # 会话数据帧操作 -│ │ ├── useSessionModbus.ts # Modbus 主站编排 -│ │ ├── useModbusMaster.ts # Modbus 单忙 / 单挂起 RX 守卫 -│ │ ├── useAutoLog.ts # 每会话 append_log 顺序链 -│ │ ├── useTriggers.ts # 脚本化 RX→TX 触发器(带冷却) -│ │ ├── usePacketFilter.ts # 方向过滤 / 搜索 / 合并视图 -│ │ ├── usePacketFormatter.ts # HEX / 文本 / ANSI 格式化缓存 -│ │ ├── useExport.ts # 导出逻辑(F12 捕获文件旁路) -│ │ ├── usePortWatcher.ts # 热插拔监听 -│ │ └── useSessionActions.ts -│ ├── stores/ # Pinia 状态 -│ │ ├── sessions.ts # 多会话管理(shallowRef + notifyFramesChanged) -│ │ ├── serial.ts # 串口设备列表 -│ │ └── app.ts # 全局设置(显示模式 / AI 配置 / 快捷键) -│ ├── lib/ # 纯 TS,无框架依赖的领域逻辑 -│ │ ├── modbus/ # Modbus 桶(15 模块:core/pdu/transport/ -│ │ │ registers/master-runtime) -│ │ ├── format.ts # HEX / ASCII / UTF-8 / HEX+ASCII 格式化 -│ │ ├── bytes.ts # Uint8Array 拼接 -│ │ ├── waveform.ts # 波形解析 / 通道统计 -│ │ ├── waveform-viewport.ts# 视口变换(normalize/zoom/scale/pan) -│ │ ├── waveform-render.ts # 画布渲染流水线(无框架依赖) -│ │ ├── protocol-parser.ts # 分隔符/定长/长度字段重组为帧 -│ │ ├── protocol-engine.ts # 传输无关的 ProtocolEngine 接口 -│ │ ├── bbrec.ts # .bbrec 裸字节流录制 / 回放 -│ │ ├── macro-control-flow.ts # 扩展宏(wait/if/goto/label) -│ │ ├── macro-library.ts # 宏库导入 / 导出(JSON) -│ │ ├── trigger-engine.ts # RX 子串 / HEX 匹配触发引擎 -│ │ ├── serial-rx-queue.ts # O(1) 环形缓冲(头索引丢弃) -│ │ ├── write-chunking.ts # ≤4 KiB TX 分块 + 重试(F8) -│ │ ├── export-filters.ts # 时间范围 / 方向导出过滤 -│ │ ├── ai-models.ts # AI 模型注册表(分发表镜像) -│ │ ├── ai-stream.ts # SSE 增量累加器(F14) -│ │ ├── session-persistence.ts # 版本化迁移链(COW-5) -│ │ ├── connection-presets.ts # 命名串口配置 -│ │ ├── logger.ts # 结构化前端日志 -│ │ ├── ipc.ts # 类型化 Tauri 命令封装 -│ │ ├── secure-settings.ts # 基于 Tauri Store 的本地密钥设置 -│ │ ├── constants.ts # 波特率 / 数据位等常量 -│ │ ├── serial-utils.ts # 串口路径 / 列表工具 -│ │ ├── serial-config.ts # 串口配置 → 枚举映射 -│ │ ├── lru-cache.ts # LRU 缓存 -│ │ └── locales/ # i18n 目录(en.ts / zh.ts / catalog.ts) -│ ├── types/ # 按领域拆分的 TS 类型桶 -│ │ ├── index.ts # 重导出桶 -│ │ ├── display.ts serial.ts macros.ts modbus.ts -│ │ ├── waveform.ts ai.ts session.ts checksum.ts constants.ts -│ ├── styles/ # CSS 变量(283 token)+ 全局样式 -│ ├── App.vue # 主窗口 -│ ├── AiWindow.vue # AI 悬浮窗口 -│ └── main.ts # 入口(路由分发主窗口 / AI 窗口) -├── scripts/ -│ └── dev.sh # 开发辅助脚本 -├── tests/frontend/ # 前端单元测试(72 个文件,node:test 运行器) -│ └── perf.bench.ts # 回归门控微基准 -├── images/ # 截图 -├── .github/workflows/ # ci.yml(lint/build/test/coverage/cycles)+ release.yml -├── .c8rc.json # c8 覆盖率门(85% 行 / 88% 分支) -├── package.json -├── vite.config.ts -├── eslint.config.mjs -└── tsconfig.json +```bash +pnpm build +pnpm tauri:build ``` -## 架构概览 +辅助脚本封装了常用流程: -``` -┌──────────────────────────────────────────────────────────┐ -│ Vue 3 前端 (Naive UI + Pinia + Virtual Scroll) │ -│ ┌───────────┐ ┌────────────┐ ┌───────────────────┐ │ -│ │串口选择器 │ │ 会话视图 │ │ AI 终端助手 │ │ -│ └─────┬─────┘ └─────┬──────┘ └────────┬──────────┘ │ -│ │ │ │ │ -│ ┌─────┴───────────────┴──────────────────┴───────────┐ │ -│ │ Tauri IPC (invoke / listen / emit) │ │ -│ └───────────────────────┬────────────────────────────┘ │ -├──────────────────────────┼───────────────────────────────┤ -│ Rust 后端 │ │ -│ ┌────────────────────────┴───────────────────────────┐ │ -│ │ commands: ai / checksum / export / log / updater │ │ -│ │ window │ │ -│ ├─────────────────────────────────────────────────────┤ │ -│ │ tauri-plugin-serialplugin (串口收发) │ │ -│ │ tauri-plugin-dialog (文件保存对话框) │ │ -│ │ tauri-plugin-store (本地设置) │ │ -│ │ tauri-plugin-updater (自动更新检查) │ │ -│ │ zai-rs (ZHIPU AI Chat API) │ │ -│ └─────────────────────────────────────────────────────┘ │ -└──────────────────────────────────────────────────────────┘ +```bash +chmod +x scripts/dev.sh +./scripts/dev.sh install +./scripts/dev.sh dev +./scripts/dev.sh build ``` -- 串口通过 `tauri-plugin-serialplugin` 管理,前端通过 Tauri Command / Event 与 Rust 后端通信 -- 前端持有全部会话状态与协议引擎(Modbus、解析器、波形);Rust 端只是无状态的 IPC + 文件系统 + AI 客户端薄层 -- 前端使用 `requestAnimationFrame` + 有界 O(1) 环形缓冲(`SerialRxQueue`)确保高波特率下 UI 流畅;`sessions` store 采用 `shallowRef`,5 万帧捕获依旧可交互 -- TX 经单一 `writeChain` promise 串行化,确保多个并发发送者(循环发送、宏、触发器、AI 填充、Modbus)不会在串口上交错写入 -- 持久化为版本化 —— 每次加载都运行 `migratePersistedFile` 迁移链,持久化结构变更保持向前兼容 -- AI 助手为独立 `WebviewWindow`,关闭时隐藏而非销毁,通过 Tauri Event 同步窗口状态;响应以 SSE delta 流式返回 -- 应用设置本地持久化;AI API Key 会从旧 localStorage 迁移到 Tauri Store +## 脚本 + +| 命令 | 说明 | +| --- | --- | +| `pnpm dev` | 启动 Vite 开发服务器 | +| `pnpm build` | Vue 类型检查并构建前端 | +| `pnpm preview` | 预览前端构建产物 | +| `pnpm tauri:dev` | 以 HMR 方式运行 Tauri 桌面应用 | +| `pnpm tauri:build` | 构建桌面应用包 | +| `pnpm format` | 格式化前端与 Rust 代码 | +| `pnpm format:check` | 检查前端与 Rust 格式 | +| `pnpm lint` | 对 `src/` 运行 ESLint | +| `pnpm test:frontend` | 运行前端单元测试 | +| `pnpm test:rust` | 运行 Rust 单元测试 | +| `pnpm test` | 运行前端与 Rust 测试 | +| `pnpm coverage:frontend` | 在 `.c8rc.json` 覆盖率门下运行前端测试 | +| `pnpm coverage:lib` | 对无框架依赖的 `src/lib/` 模块执行逐文件行覆盖率门 | +| `pnpm bench:frontend` | 运行前端热路径基准 | +| `pnpm bench:frontend:write` | 刷新本机前端基准基线 | +| `pnpm bench:rust` | 运行 Rust criterion 基准 | +| `pnpm cycles` | 检查 TypeScript 导入环 | +| `pnpm check` | 运行 lint、格式检查、构建和单元测试 | + +## 项目地图 + +```text +bbcom/ +├── src/ # Vue 前端 +│ ├── components/ # 应用外壳、会话视图、终端面板、AI 面板 +│ ├── composables/ # 串口连接、Modbus 编排、导出、触发器 +│ ├── lib/ # 无框架依赖的领域逻辑与 IPC 封装 +│ │ ├── modbus/ # 请求构建、批处理、传输、循环、回放 +│ │ ├── format.ts # HEX/文本/ANSI/HEX+ASCII 格式化 +│ │ ├── serial-rx-queue.ts # 高速捕获使用的有界 RX 队列 +│ │ ├── protocol-parser.ts # 分隔符/定长/长度字段帧解析 +│ │ ├── waveform*.ts # 波形解析、视口数学与画布渲染辅助 +│ │ ├── session-persistence.ts +│ │ └── ipc.ts +│ ├── stores/ # sessions、serial、app 三个 Pinia store +│ ├── styles/ # 主题 token 与全局样式 +│ ├── types/ # 按领域拆分的类型桶 +│ ├── App.vue # 主窗口入口 +│ └── AiWindow.vue # AI 悬浮窗口入口 +├── src-tauri/ # Rust 后端 +│ ├── src/commands/ # Tauri 命令:ai、checksum、export、log、updater、window +│ ├── src/export/ # TXT/CSV/JSONL/BIN 格式化器 +│ ├── src/models/ # IPC 数据结构与应用错误类型 +│ ├── src/utils/ # HEX、时间戳、校验工具 +│ └── benches/hot_paths.rs # Criterion 基准 +├── tests/frontend/ # node:test 单元测试与前端基准 +├── images/ # README 截图 +├── .github/workflows/ # CI 与 release 工作流 +├── ARCHITECTURE.md # 维护者架构指南 +└── scripts/dev.sh # 开发辅助脚本 +``` -> 完整的模块拓扑、不可违反的不变量("sacred cows")、上游硬约束与人工验证清单, -> 详见 [ARCHITECTURE.md](./ARCHITECTURE.md)。 +模块归属、数据流不变量、上游约束与人工验证建议见 +[ARCHITECTURE.md](./ARCHITECTURE.md)。 -## 贡献指南 +## 验证 -欢迎贡献!请遵循以下规范: +CI 会运行前端 lint、格式检查、构建、测试、覆盖率、基准烟测和循环依赖 +检查,以及 Rust 格式、clippy 和测试。Rust 覆盖率在 CI 中以 tarpaulin +报告形式尽力生成;前端覆盖率门由 c8 强制执行。 -1. **提交信息** — 遵循 [Conventional Commits](https://www.conventionalcommits.org/) -2. **代码风格** — ESLint 9 + typescript-eslint(`no-console: error`、`eqeqeq: error`) -3. **Rust** — edition 2024,`tracing` 日志,`thiserror` 错误处理 -4. **TypeScript** — 严格模式(`strict: true`、`noUnusedLocals`、`noUnusedParameters`) -5. **检查** — 发起 PR 前运行 `pnpm check` +提交 PR 前建议运行: -### 开发流程 +```bash +pnpm check +pnpm cycles +pnpm coverage:frontend +pnpm coverage:lib +``` -1. Fork 本仓库 -2. 创建功能分支(`git checkout -b feat/my-feature`) -3. 提交更改(`git commit -m 'feat: add something'`) -4. 推送到分支(`git push origin feat/my-feature`) -5. 发起 Pull Request +修改格式化、RX 队列、会话帧存储、导出、校验和或 Modbus 批处理等热路径 +时,请运行 `pnpm bench:frontend` 或 `pnpm bench:rust`。 ## 常见问题
支持哪些平台? -bbcom 支持 **Windows**、**macOS** 和 **Linux**,得益于 Tauri v2 的跨平台架构。 +bbcom 通过 Tauri v2 面向 Windows、macOS 和 Linux。
-如何获取 ZHIPU AI API Key? +如何获取 Z.ai API Key? -在 [open.bigmodel.cn](https://open.bigmodel.cn/) 注册并创建 API Key,然后在 bbcom 的 AI 助手设置面板中填入即可。 +在 [open.bigmodel.cn](https://open.bigmodel.cn/) 创建 API Key,然后填入 +AI 助手设置面板。
串口设备不显示怎么办? -- 确认设备已连接且驱动已安装 -- Linux 下可能需要将用户加入 `dialout` 组:`sudo usermod -aG dialout $USER` -- macOS 下可在终端执行 `ls /dev/cu.*` 检查设备 +- 确认设备已连接,驱动已安装。 +- Linux 下检查 `dialout` 等串口权限组。 +- macOS 下可执行 `ls /dev/cu.*`。 +- 插入设备后重新刷新串口列表。 +
+## 贡献 + +请使用 Conventional Commits,保持 TypeScript strict 与 Rust warning-free, +并为行为变更补充聚焦测试。如果修改持久化会话结构,需要提升 +`SESSION_STORAGE_VERSION`、添加迁移步骤,并用旧数据回归测试覆盖。 + ## 许可证 本项目基于 [MIT License](LICENSE) 开源。 diff --git a/package.json b/package.json index a6e2239..580707b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bbcom", "private": true, - "version": "0.4.0", + "version": "0.4.1", "type": "module", "packageManager": "pnpm@11.5.3", "scripts": { diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 3fe79cc..33e236e 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bbcom" -version = "0.4.0" +version = "0.4.1" description = "Serial Port Assistant" edition = "2024" rust-version = "1.85" diff --git a/src-tauri/src/commands/ai/service.rs b/src-tauri/src/commands/ai/service.rs index fdfc7d4..427fff3 100644 --- a/src-tauri/src/commands/ai/service.rs +++ b/src-tauri/src/commands/ai/service.rs @@ -2,8 +2,8 @@ //! //! The model dispatch is a `match` (a dispatch-table), not `Box` — //! `zai_rs`'s `ModelName: Into` is not dyn-safe, so each supported model -//! is a concrete arm (see F13 in the project research). `send_chat` is the single -//! generic hot point that builds the `ChatCompletion` for a concrete model type. +//! is a concrete arm. `send_chat` is the single generic hot point that builds +//! the `ChatCompletion` for a concrete model type. use serde::Serialize; use serde_json::Value; diff --git a/src-tauri/src/commands/export.rs b/src-tauri/src/commands/export.rs index f2761b0..9512c46 100644 --- a/src-tauri/src/commands/export.rs +++ b/src-tauri/src/commands/export.rs @@ -54,17 +54,17 @@ pub async fn export_data(request: ExportRequest) -> Result<(), AppError> { formatter::export(&request.frames, &request.format, &request.path).await } -/// F12 IPC-bypass export (T2.3). The frontend serializes the capture to a -/// JSONL temp file (one `DataFrame` per line — the same shape the JSONL export -/// emits) and passes only the temp-file path through IPC, instead of pushing -/// up to 100 000 `DataFrame` objects (each with a `data: Vec` that serde -/// expands to a JSON number array) through `invoke`. The Rust side reads and -/// parses the file in a `spawn_blocking` task, then runs the normal formatter. +/// Capture-file export. The frontend serializes the capture to a JSONL temp +/// file (one `DataFrame` per line — the same shape the JSONL export emits) and +/// passes only the temp-file path through IPC, instead of pushing up to 100 000 +/// `DataFrame` objects (each with a `data: Vec` that serde expands to a JSON +/// number array) through `invoke`. The Rust side reads and parses the file in a +/// `spawn_blocking` task, then runs the normal formatter. /// /// This avoids the dominant export cost — serializing the `frames` argument /// across the IPC boundary — at the price of one temp-file write+read. For a /// 10k-frame capture the temp file is far cheaper to transfer than 10k JSON -/// objects (F12: very-large transfers are fastest via temp file). +/// objects. #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CaptureFileExportRequest { diff --git a/src-tauri/src/commands/ipc_contracts.rs b/src-tauri/src/commands/ipc_contracts.rs index 2c06c6c..64aaf4f 100644 --- a/src-tauri/src/commands/ipc_contracts.rs +++ b/src-tauri/src/commands/ipc_contracts.rs @@ -205,7 +205,7 @@ mod tests { assert_eq!(req.session_meta.as_deref(), Some("COM1@115200")); } - // ---- export_data_from_capture_file: F12 IPC-bypass (T2.3) ---- + // ---- export_data_from_capture_file: capture-file export ---- // The frontend writes a JSONL temp file (one DataFrame/line) and passes only // the path; the wire shape is camelCase to match CaptureFileExportRequest. diff --git a/src-tauri/src/commands/updater.rs b/src-tauri/src/commands/updater.rs index 4e898f0..0a41e6f 100644 --- a/src-tauri/src/commands/updater.rs +++ b/src-tauri/src/commands/updater.rs @@ -1,4 +1,4 @@ -//! Auto-updater command (T3.9 F-f). +//! Auto-updater command. //! //! Wraps `tauri-plugin-updater`'s check + download + install flow behind a //! typed command so the frontend can trigger "check for updates" without diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 76f3617..17c5df6 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2.0.0", "productName": "bbcom", - "version": "0.4.0", + "version": "0.4.1", "identifier": "com.bbcom.app", "build": { "beforeDevCommand": "pnpm dev", @@ -19,7 +19,7 @@ } ], "security": { - "csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' asset: http://asset.localhost https://asset.localhost data:; font-src 'self' data:; connect-src 'self' ipc: http://ipc.localhost http://localhost:5173 ws://localhost:5173" + "csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; style-src-elem 'self' 'unsafe-inline'; style-src-attr 'unsafe-inline'; img-src 'self' asset: http://asset.localhost https://asset.localhost data:; font-src 'self' data:; connect-src 'self' ipc: http://ipc.localhost http://localhost:5173 ws://localhost:5173" } }, "bundle": { diff --git a/src/components/session/SessionToolbar.vue b/src/components/session/SessionToolbar.vue index 814dd68..915c45d 100644 --- a/src/components/session/SessionToolbar.vue +++ b/src/components/session/SessionToolbar.vue @@ -1,6 +1,6 @@ diff --git a/src/components/terminal/ModbusHeader.vue b/src/components/terminal/ModbusHeader.vue index fe4b77f..19b1cc6 100644 --- a/src/components/terminal/ModbusHeader.vue +++ b/src/components/terminal/ModbusHeader.vue @@ -1,7 +1,7 @@ diff --git a/src/components/terminal/ModbusRegisterRow.vue b/src/components/terminal/ModbusRegisterRow.vue index 57dda82..e4de8db 100644 --- a/src/components/terminal/ModbusRegisterRow.vue +++ b/src/components/terminal/ModbusRegisterRow.vue @@ -1,5 +1,5 @@