DaMiao Studio: passive monitor + unified control/monitor UI - #6
Merged
Conversation
…ener - damiao_motor/monitor/decode.py: reverse MIT/POS_VEL/VEL/FORCE_POS commands and feedback frames; cmd-vs-feedback disambiguation via p16 offset + structural check; extended motor-type presets (incl. DM4310V/FLOW_WHEEL +/-pi variants). - store.py: auto-learned signal registry, per-signal ring buffers, cmd<->fb pairing, per-motor aggregate views. - listener.py: listen-only PassiveCanListener; best-effort CAN_RAW_LISTEN_ONLY; never calls bus.send (guaranteed structurally + by test). - tests/test_monitor.py: 10 round-trip + never-transmit tests (all green). - motor.py: fd defaults to False (non-breaking). - .gitignore: ship the built monitor SPA despite the build/ and dist/ rules. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- monitor/service.py: owns listener+store, raw-frame ring log, thread-safe snapshots, per-motor type override, optional demo source. - monitor/server.py: Flask REST (status/signals/snapshot/motor-types) + WS /stream (subscription-scoped, ~30Hz, per-signal decimation cap); serves built SPA with client-routing fallback + dev placeholder. - monitor/demo.py: synthetic cmd+feedback traffic (no CAN) for dev/screenshots/demo. - pyproject: add flask-sock dependency. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dashboard at damiao_motor/gui/webapp: - dockview (VS Code-style) panels: drag to dock/split/merge into tabs; layout persisted. - uPlot cmd-vs-actual plots: drag a signal chip onto a plot to add it; drop cmd onto fb to overlay (dashed=command, solid=feedback); per-plot time window; rAF render loop. - views: multi-motor table, per-motor cards/gauges (+motor-type override), virtualized raw decoded CAN log; toolbar adds more panels (user-expandable). - dnd-kit signal DnD kept disjoint from dockview tab DnD; Zustand registry + out-of-React ring buffers; WS client with auto-reconnect, subscription-scoped streaming, snapshot backfill on drop. - prebuilt bundle committed to dist/ (end users need no node toolchain). Fixes during bring-up: include series in uPlot options; hydrate plotConfigs synchronously (avoid TDZ on PLOT_KEY + effect-ordering race that wiped plot signals). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- cli: new 'damiao monitor' subcommand (passive dashboard) mirroring 'gui', with --channel/--bustype/--bitrate/--feedback-offset/--motor-type/--demo; cmd_monitor handler. - packaging: switch to setuptools packages.find (was packages=['damiao_motor'], which silently omitted core/cli/gui subpackages from the wheel — latent bug); ship gui/webapp/dist via package-data. - CI: build the SPA (npm ci && npm run build) before the wheel and assert the bundle + monitor package are present in the wheel. Verified: wheel contains monitor/*, gui/webapp/dist/*, and the previously-missing core/cli/gui subpackages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- panels/registry.tsx: single source of truth for panel types (kind/title/icon/ description/render). Dock component map, toolbar add-buttons, panel titling, and default layout all derive from it — a new view is now one entry. - raw CAN log: sticky header inside the scroll container sharing the exact grid + padding (columns now align); single-line cells with ellipsis; compact decoded fields; clock timestamp instead of raw epoch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Real linearbot arm joints 4-7 report an undocumented status nibble (3), which made the feedback detector fall through and misclassify their feedback as MIT commands (phantom m20-23 signals). The arb-range + can-id-nibble match already disambiguates feedback (ids offset+1..offset+15) from MIT commands (ids 1..15), so the status gate was both unnecessary and wrong. Status still surfaces (e.g. UNKNOWN(3)) for display. Validated live on can_arm_l and can_arm_r: all 7 joints decode (cmd+feedback), pairs link, values plausible, 0 decode errors, monitor stays listen-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the rigid dockview tiling with a Grafana-style free-form canvas: every panel is a widget you drag (by header) and resize (any edge) anywhere on a 12-col grid; add from the toolbar, remove via the header ×, layout persists. Refreshed to a softer, more modern dark theme (rounded surfaces, gentler palette, more breathing room). - lib/widgets.ts: widget store (geometry + persistence, default layout, reset). - components/Canvas.tsx: GridStack init + React content via portals (no DOM fighting); geometry synced back to the store on drag/resize. - panels/registry.tsx: drop dockview component map; panels render unchanged. - remove components/Dock.tsx, lib/dock.ts, dockview dep (bundle -25%). - Toolbar adds widgets via the store; dnd-kit signal->plot drag still works. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- lib/theme.ts: persisted theme (default light), applied via data-theme on <html> before first paint. - index.css: split palette into :root (light) + [data-theme=dark]; add --hover; replace hardcoded dark colors (toolbar, buttons, widget header, table hover) with variables. - Toolbar: ☾/☀ toggle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… weight/opacity cue Dense data turned the dashed command trace into visual noise. Now: actual = bold solid, command = a fainter (45% alpha), thinner 'ghost' line in the same hue. Legend swatches match. Cleaner cmd-vs-actual read at any point density. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- monitor/control.py: ControlService wraps DaMiaoController; connect/scan/enable/disable/
command(4 modes)/set-zero/clear-error/store-params/registers(GET+PUT w/ ms + ID changes)/
motor-type. Pushes cmd+feedback into the shared SignalStore so the same plots/table/cards
visualize active control (offset-agnostic). Transmits only in control mode.
- monitor/server.py: unified Studio server. Two modes — monitor (passive listener) and
control (ControlService) feeding ONE shared store. /api/mode switch, /api/connect, gated
/api/control/*, shared /api/monitor/{signals,snapshot,stream}. Control endpoints 409 in
monitor mode (passivity preserved structurally).
Verified: demo monitor (33 signals/3 motors), control gating, mode switch, graceful
connect failure, register table — all green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Toolbar: Control/Monitor mode switch (+ active·TX / listen-only badge); 'DaMiao Studio'. - New widgets (registry): Connection (bus/scan/motor select), Motor Control (MIT/POS_VEL/ VEL/FORCE_POS, enable/disable, single+continuous send, set-zero/clear-error/store, motor- type), Registers (read/write incl. ms timeout, hex IDs, mode/baud dropdowns). - lib/control.ts REST client; store gains mode/currentMotor/controlMotors/registerTable. - Default canvas is now a full Studio layout; control widgets show an inert hint in monitor mode. Commands feed the same live plots/table/cards via the shared store. - CLI: 'damiao gui' now launches the unified Studio in control mode (legacy web_gui kept). - types: ServerStatus gains mode/connected. Builds clean; backend + tests green. UI pending visual check (local browser wedged). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ControlService passed UI/default names like 'DM4310' straight to DaMiaoController, whose core preset table only knows '4310' -> scan silently added no motors. Map extended->core (_core_motor_type) in scan + set_motor_type. Hardware-validated on linearbot can_arm_l motor 7 through the full ControlService path: connect -> scan (motors 1-7) -> clear_error -> enable (ENABLED) -> MIT command MOVES the motor (-1.2255 -> -1.154 toward target) -> return -> registers read -> disable; cmd+fb signals land in the shared store. (Tip surfaced: latched status 0x3 needs clear-error before enable.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove now-unused web_gui import from cli/commands.py and ruff-format the new monitor/ modules + edited cli/tests files so the Ruff workflow (ruff check . / ruff format --check .) passes. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a passive listen-only monitor and unifies it with active control into one app — DaMiao Studio — plus a fix to the recent CAN-FD change.
1. Passive monitor (listen-only)
damiao_motor/monitor/package: decodes both another controller's command frames and the motors' feedback off the bus, never transmitting (guaranteed structurally; asserted by test). Best-effortCAN_RAW_LISTEN_ONLYon socketcan.flask-sock), subscription-scoped & server-decimated;--demosynthetic source for hardware-free use.2. Unified Studio UI (control + monitor)
DaMiaoController; every command + feedback feeds the same live plots).damiao gui→ Studio in control mode;damiao monitor→ monitor mode. Legacyweb_gui.pyretained (unused).3. Professional front-end (
gui/webapp, React + TS + Vite)cmdoverfbto overlay), virtualized table + raw CAN log, per-motor cards.pip installneeds no Node.4. fd fix
DaMiaoMotor(fd=...)defaults toFalseagain (the recent change made it a required positional arg — breaking).Validation
can_arm_lmotor 7 through the full Studio path: connect → scan → clear-error → enable → MIT command physically moves the motor → return → register read → disable, with cmd+fb landing in the shared store.core/cli/guisubpackages were excluded from the wheel;release.ymlbuilds the SPA before the wheel; repo is ruff-clean.Notes / known limitations (beta)
0x3needs clear-error before enable (undocumented DaMiao state; surfaced honestly).DM4310Vis ±π).ControlService/the unified server (decode/store/listener are covered).Intended to ship as prerelease
v1.0.7b2(CI auto-marksb/rcas prerelease).🤖 Generated with Claude Code