diff options
| author | Your Name <you@example.com> | 2026-05-19 03:10:04 +0530 |
|---|---|---|
| committer | Your Name <you@example.com> | 2026-05-19 03:10:04 +0530 |
| commit | 1232a43f17cd4c1d994b2d652060d4fc9e1053ea (patch) | |
| tree | ef552a7cd0ca92dcf2ec0d063f00089f315c3ee0 | |
| parent | d21fc93c4b7d025d99ecc7c15b3998dbdebf076c (diff) | |
fix: add per-board mutex locks to flash targets
flash-a and flash-b now require lock-a/lock-b to be held before
flashing. Prevents cross-session hardware conflicts when multiple
LLM agents share the same ESP32 boards.
Lock infrastructure matches main repo and price-discovery worktrees:
- HARDWARE_LOCK_DIR = physical-router-test-automation/locks
- require_lock_a/require_lock_b macros
- lock-a/lock-b/unlock-a/unlock-b/force-unlock-a/force-unlock-b/lock-status targets
| -rw-r--r-- | Makefile | 112 |
1 files changed, 108 insertions, 4 deletions
| @@ -13,6 +13,52 @@ PORT ?= $(PORT_A) | |||
| 13 | BAUD ?= 460800 | 13 | BAUD ?= 460800 |
| 14 | TARGET ?= esp32s3 | 14 | TARGET ?= esp32s3 |
| 15 | 15 | ||
| 16 | HARDWARE_LOCK_DIR := /home/c03rad0r/physical-router-test-automation/locks | ||
| 17 | |||
| 18 | RED := \033[31m | ||
| 19 | GREEN := \033[32m | ||
| 20 | YELLOW := \033[33m | ||
| 21 | BOLD := \033[1m | ||
| 22 | RESET := \033[0m | ||
| 23 | |||
| 24 | define require_lock_a | ||
| 25 | @if [ ! -f "$(HARDWARE_LOCK_DIR)/board-a.lock" ]; then \ | ||
| 26 | echo "$(RED)$(BOLD)Board A not locked — run 'make lock-a PHASE=\"description\"' first$(RESET)"; \ | ||
| 27 | echo "$(YELLOW)Another LLM session may be using Board A.$(RESET)"; \ | ||
| 28 | exit 1; \ | ||
| 29 | fi | ||
| 30 | endef | ||
| 31 | |||
| 32 | define require_lock_b | ||
| 33 | @if [ ! -f "$(HARDWARE_LOCK_DIR)/board-b.lock" ]; then \ | ||
| 34 | echo "$(RED)$(BOLD)Board B not locked — run 'make lock-b PHASE=\"description\"' first$(RESET)"; \ | ||
| 35 | echo "$(YELLOW)Another LLM session may be using Board B.$(RESET)"; \ | ||
| 36 | exit 1; \ | ||
| 37 | fi | ||
| 38 | endef | ||
| 39 | |||
| 40 | define _acquire_lock | ||
| 41 | @if [ -f "$(HARDWARE_LOCK_DIR)/$(1).lock" ]; then \ | ||
| 42 | echo "$(RED)$(BOLD)Cannot acquire lock — $(1) already locked:$(RESET)"; \ | ||
| 43 | echo ""; \ | ||
| 44 | cat $(HARDWARE_LOCK_DIR)/$(1).lock | sed 's/^/ /'; \ | ||
| 45 | echo ""; \ | ||
| 46 | echo "$(YELLOW)Use 'make force-unlock-$(1)' to override.$(RESET)"; \ | ||
| 47 | exit 1; \ | ||
| 48 | fi; \ | ||
| 49 | branch=$$(git branch --show-current 2>/dev/null || echo "unknown"); \ | ||
| 50 | worktree=$$(pwd); \ | ||
| 51 | echo "locked: true" > $(HARDWARE_LOCK_DIR)/$(1).lock; \ | ||
| 52 | echo "board: $(1)" >> $(HARDWARE_LOCK_DIR)/$(1).lock; \ | ||
| 53 | echo "branch: $$branch" >> $(HARDWARE_LOCK_DIR)/$(1).lock; \ | ||
| 54 | echo "worktree: $$worktree" >> $(HARDWARE_LOCK_DIR)/$(1).lock; \ | ||
| 55 | echo "session: $$USER@$$HOSTNAME" >> $(HARDWARE_LOCK_DIR)/$(1).lock; \ | ||
| 56 | echo "timestamp: $$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >> $(HARDWARE_LOCK_DIR)/$(1).lock; \ | ||
| 57 | echo "phase: $(PHASE)" >> $(HARDWARE_LOCK_DIR)/$(1).lock; \ | ||
| 58 | echo "$(GREEN)$(BOLD)$(1) lock acquired$(RESET)"; \ | ||
| 59 | cat $(HARDWARE_LOCK_DIR)/$(1).lock | ||
| 60 | endef | ||
| 61 | |||
| 16 | NODE ?= node | 62 | NODE ?= node |
| 17 | NPM ?= npm | 63 | NPM ?= npm |
| 18 | PYTHON ?= python3 | 64 | PYTHON ?= python3 |
| @@ -27,6 +73,7 @@ TOLLGATE_IP ?= 10.192.45.1 | |||
| 27 | .PHONY: tokens wallet-setup wallet-info wallet-balance mint-token send-token | 73 | .PHONY: tokens wallet-setup wallet-info wallet-balance mint-token send-token |
| 28 | .PHONY: clean erase-nvs reset serial-log bootstrap-config | 74 | .PHONY: clean erase-nvs reset serial-log bootstrap-config |
| 29 | .PHONY: cvm-pubkey cvm-test-tool cvm-announce | 75 | .PHONY: cvm-pubkey cvm-test-tool cvm-announce |
| 76 | .PHONY: lock-a lock-b unlock-a unlock-b force-unlock-a force-unlock-b lock-status | ||
| 30 | 77 | ||
| 31 | help: | 78 | help: |
| 32 | @echo "TollGate ESP32 — Makefile" | 79 | @echo "TollGate ESP32 — Makefile" |
| @@ -131,11 +178,15 @@ flash: build | |||
| 131 | @echo "=== Flashing to $(PORT) ===" | 178 | @echo "=== Flashing to $(PORT) ===" |
| 132 | . $(IDF_PATH)/export.sh && idf.py -p $(PORT) -b $(BAUD) flash | 179 | . $(IDF_PATH)/export.sh && idf.py -p $(PORT) -b $(BAUD) flash |
| 133 | 180 | ||
| 134 | flash-a: PORT=$(PORT_A) | 181 | flash-a: build |
| 135 | flash-a: flash | 182 | $(call require_lock_a) |
| 183 | @echo "=== Flashing to $(PORT_A) (Board A) ===" | ||
| 184 | . $(IDF_PATH)/export.sh && idf.py -p $(PORT_A) -b $(BAUD) flash | ||
| 136 | 185 | ||
| 137 | flash-b: PORT=$(PORT_B) | 186 | flash-b: build |
| 138 | flash-b: flash | 187 | $(call require_lock_b) |
| 188 | @echo "=== Flashing to $(PORT_B) (Board B) ===" | ||
| 189 | . $(IDF_PATH)/export.sh && idf.py -p $(PORT_B) -b $(BAUD) flash | ||
| 139 | 190 | ||
| 140 | build: | 191 | build: |
| 141 | @echo "=== Building $(TARGET) ===" | 192 | @echo "=== Building $(TARGET) ===" |
| @@ -293,3 +344,56 @@ bootstrap-config: | |||
| 293 | @echo "=== Bootstrapping config.json ===" | 344 | @echo "=== Bootstrapping config.json ===" |
| 294 | @echo '{"wifi_networks":[{"ssid":"$(WIFI_SSID)","password":"$(WIFI_PASSWORD)"}],"ap_ssid":"$(AP_SSID)","ap_password":"$(AP_PASSWORD)","mint_url":"$(MINT_URL)","lnurl_url":"$(LNURL_URL)","price_per_step":$(PRICE_PER_STEP),"step_size_ms":$(STEP_SIZE)}' > main/config.json | 345 | @echo '{"wifi_networks":[{"ssid":"$(WIFI_SSID)","password":"$(WIFI_PASSWORD)"}],"ap_ssid":"$(AP_SSID)","ap_password":"$(AP_PASSWORD)","mint_url":"$(MINT_URL)","lnurl_url":"$(LNURL_URL)","price_per_step":$(PRICE_PER_STEP),"step_size_ms":$(STEP_SIZE)}' > main/config.json |
| 295 | @echo "Config written to main/config.json" | 346 | @echo "Config written to main/config.json" |
| 347 | |||
| 348 | # ────────────────────────────────────────────── | ||
| 349 | # Board Lock Management | ||
| 350 | # ────────────────────────────────────────────── | ||
| 351 | |||
| 352 | lock-a: | ||
| 353 | @$(call _acquire_lock,board-a) | ||
| 354 | |||
| 355 | lock-b: | ||
| 356 | @$(call _acquire_lock,board-b) | ||
| 357 | |||
| 358 | unlock-a: | ||
| 359 | @if [ -f "$(HARDWARE_LOCK_DIR)/board-a.lock" ]; then \ | ||
| 360 | rm $(HARDWARE_LOCK_DIR)/board-a.lock; \ | ||
| 361 | echo "$(GREEN)Board A lock released.$(RESET)"; \ | ||
| 362 | else \ | ||
| 363 | echo "$(YELLOW)Board A not locked.$(RESET)"; \ | ||
| 364 | fi | ||
| 365 | |||
| 366 | unlock-b: | ||
| 367 | @if [ -f "$(HARDWARE_LOCK_DIR)/board-b.lock" ]; then \ | ||
| 368 | rm $(HARDWARE_LOCK_DIR)/board-b.lock; \ | ||
| 369 | echo "$(GREEN)Board B lock released.$(RESET)"; \ | ||
| 370 | else \ | ||
| 371 | echo "$(YELLOW)Board B not locked.$(RESET)"; \ | ||
| 372 | fi | ||
| 373 | |||
| 374 | force-unlock-a: | ||
| 375 | @echo "$(RED)$(BOLD)WARNING: Force-releasing Board A lock!$(RESET)" | ||
| 376 | @cat $(HARDWARE_LOCK_DIR)/board-a.lock 2>/dev/null | sed 's/^/ /' || true | ||
| 377 | @rm -f $(HARDWARE_LOCK_DIR)/board-a.lock | ||
| 378 | @echo "$(GREEN)Board A lock force-released.$(RESET)" | ||
| 379 | |||
| 380 | force-unlock-b: | ||
| 381 | @echo "$(RED)$(BOLD)WARNING: Force-releasing Board B lock!$(RESET)" | ||
| 382 | @cat $(HARDWARE_LOCK_DIR)/board-b.lock 2>/dev/null | sed 's/^/ /' || true | ||
| 383 | @rm -f $(HARDWARE_LOCK_DIR)/board-b.lock | ||
| 384 | @echo "$(GREEN)Board B lock force-released.$(RESET)" | ||
| 385 | |||
| 386 | lock-status: | ||
| 387 | @echo "$(BOLD)Board Lock Status$(RESET)" | ||
| 388 | @if [ -f "$(HARDWARE_LOCK_DIR)/board-a.lock" ]; then \ | ||
| 389 | echo "Board a: $(YELLOW)LOCKED$(RESET)"; \ | ||
| 390 | cat $(HARDWARE_LOCK_DIR)/board-a.lock | sed 's/^/ /'; \ | ||
| 391 | else \ | ||
| 392 | echo "Board a: $(GREEN)available$(RESET)"; \ | ||
| 393 | fi | ||
| 394 | @if [ -f "$(HARDWARE_LOCK_DIR)/board-b.lock" ]; then \ | ||
| 395 | echo "Board b: $(YELLOW)LOCKED$(RESET)"; \ | ||
| 396 | cat $(HARDWARE_LOCK_DIR)/board-b.lock | sed 's/^/ /'; \ | ||
| 397 | else \ | ||
| 398 | echo "Board b: $(GREEN)available$(RESET)"; \ | ||
| 399 | fi | ||