Skip to content

feat: support >3 work maps via active-slot swap#32

Open
rvbcrs wants to merge 13 commits into
masterfrom
feat/multi-map-swap
Open

feat: support >3 work maps via active-slot swap#32
rvbcrs wants to merge 13 commits into
masterfrom
feat/multi-map-swap

Conversation

@rvbcrs
Copy link
Copy Markdown
Owner

@rvbcrs rvbcrs commented May 4, 2026

Summary

  • New `swap_active_map` extended-command handler on the mower copies `mapN.{yaml,pgm,png}` over the active slot via two-phase atomic copy and reloads `map_server` via `ros2_run` (consistent ROS env).
  • New server endpoint `POST /api/dashboard/maps/:sn/active-slot` orchestrates the swap with idempotency caching (`deviceCache.active_map_slot`) and proper error code translation (mower result:0/1/2/3/4 → HTTP 200/500/400/409/500).
  • App + dashboard + scheduleRunner pre-swap the active slot before every `start_navigation` and use `area: 0` post-swap, so any work map (canonical `mapN`) can be mowed regardless of the legacy 1/10/200 area enum.

Spec: `docs/superpowers/specs/2026-05-04-multi-map-swap-active-slot.md`
Plan: `docs/superpowers/plans/2026-05-04-multi-map-swap-active-slot.md`

What's NOT in this PR

  • Live deploy to the test mower — TODO: SCP `research/extended_commands.py` to `/root/novabot/scripts/` + restart daemon, then verify swap → curl confirmation → real mow on slot 1.
  • App's BLE mapping screen still caps at 3 maps in the UI (`_getMapName()` cap). Lifting that requires a separate change. This PR only enables MOWING on slots 3+ that already exist on the mower.

Test plan

  • `cd server && npx vitest run` — 297/297 passing (10 new active-slot cases)
  • `cd app && npx tsc --noEmit` — clean
  • `cd dashboard && npx tsc --noEmit` — clean
  • Acceptance test on LFIN1231000211: SCP extended_commands.py → restart → `curl POST /maps//active-slot {slot:1}` → `cmp /userdata/lfi/maps/home0/map.yaml /userdata/lfi/maps/home0/map1.yaml` returns 0
  • Real mow on slot 1 from the OpenNova app drives the right polygon

Firmware scan note

robot_decision binary (`mappingSaveMapDeal` at 0x7e000) shows a `cmp w0, #0x9` against what's likely the map count, with a fall-through path that logs + likely rejects. Hypothesis: firmware caps internally at 9 maps. Not empirically confirmed; the swap flow itself works for any N where mapN.{yaml,pgm} files exist on disk.

rvbcrs added 13 commits May 4, 2026 02:04
After a successful slot swap the loaded map.yaml is already the requested
map, so start_navigation must use area=0 instead of area=1. Adds optional
areaOverride param to runStartMowing; legacy (no-swap) path unchanged.

Also converts the dynamic mapSync import to a static top-level import,
removing the unused publishToDevice import that was the only previous
static import from that module.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant