docs(specs): honest hardware-coupled Blockly DSL — design (5 phases)
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
# DSL Blockly honnête & couplé hardware — Design
|
||||
|
||||
**Date** : 2026-06-14
|
||||
**Statut** : design approuvé (hybride : honnête + roadmap ; priorités binding carte / média SD / transitions réelles)
|
||||
**Périmètre** : `frontend-v3/packages/shared/blockly`, `frontend-v3/apps/atelier`, `tools/zacus-gateway`, `tools/scenario`
|
||||
|
||||
## 1. Problème
|
||||
|
||||
Le DSL Blockly actuel (64 blocs, source unique `frontend-v3/packages/shared/blockly/zacus_blocks.mjs`) est ~70 % **fantôme** : la plupart des blocs sont validés par le gateway puis **ignorés par le firmware**. Le firmware Freenove master sait réellement faire :
|
||||
- **Énigmes** : `qr` + `sound` uniquement (`puzzle_binding.h` enum `PB_NONE/PB_QR/PB_SOUND`) ;
|
||||
- **Écran** : step-extra `scene` (title≤47 / subtitle≤63 / symbol≤15 / effect∈pulse|glitch|gyro|none) ;
|
||||
- **Média** : WAV + MP3 par **chemin** (`media_manager`, `POST /game/media/play {path}`), résolu sur `/sdcard/...` ou `/littlefs/...` ;
|
||||
- **Relais ESP-NOW** : IR chunké (`/game/scenario/relay {peers,ir}`) + canal texte libre ≤200 o (sans consommateur structuré) ;
|
||||
- **Voix** : déléguée au voice-bridge :8200 ;
|
||||
- **PAS de moteur de transitions on-device** : le firmware est piloté pas-à-pas par `POST /game/step {step_id}` ; la logique vit côté gateway/web.
|
||||
|
||||
Sans support firmware : servo, relais, buzzer, NFC, morse/radio/LED/keypad, cartes M5/BOX-3/PLIP, couche GM. Non exposées en blocs alors que réelles : **binding à une carte précise**, **chemin média SD réel**, **transitions `button/serial/audio_done/unlock/espnow`**, **branchement sur l'état des énigmes** (`puzzle_state`).
|
||||
|
||||
## 2. Intention
|
||||
|
||||
Rendre le DSL **honnête** (refléter ce que la carte fait) et **réellement couplé au hardware**, en gardant les blocs non encore supportés comme **roadmap balisée** (hybride). Priorités : binding carte, média SD réel, transitions réelles.
|
||||
|
||||
## 3. Architecture cible
|
||||
|
||||
### 3.1 Deux voies de blocs
|
||||
- **✅ RÉELLE** (exécutée on-device) : `sceneStart/End/Goto/Branch`, `scene`, `puzzleQR`, `puzzleSound`, média réel, relais ESP-NOW, `npcSay`/`npcIntentMatch`, transitions réelles.
|
||||
- **🔬 HARDWARE FUTUR** (validée, non exécutée) : servo, relais, buzzer, NFC, `puzzleLED/Radio/Morse/NFC/Coffre`, M5/BOX-3/PLIP, GM. Catégorie(s) Blockly distincte(s), couleur/badge « non câblé », et un avertissement non bloquant à la compilation (warning, pas erreur).
|
||||
|
||||
La distinction vit dans `zacus_blocks.mjs` : un set `ZACUS_REAL_KINDS` (firmware-backed) ; `ZACUS_TOOLBOX_CATEGORIES` réorganisé pour grouper réel vs futur. La toolbox de l'atelier (`apps/atelier/src/components/toolbox.ts`) est générée depuis ces catégories — donc se met à jour automatiquement.
|
||||
|
||||
### 3.2 Binding carte
|
||||
- Cartes connues : `master`, `box3`, `plip`, `p7` (cf. `tools/zacus-gateway/boards.yaml`).
|
||||
- **Champ `board`** (défaut `master`) sur les blocs énigme (`puzzleQR`, `puzzleSound`) et un bloc contexte **`hwOnBoard`** (« ▸ sur la carte X { … } », c-block) qui cible les actions/cues qu'il contient.
|
||||
- Compilation : le `board` est porté sur le step (`step.board`) et/ou l'action (`action.board`). Le gateway route :
|
||||
- énigme/scénario d'une carte ≠ master → `POST /game/scenario/relay {peers:[board], ir}` ;
|
||||
- cue média → `POST /game/media/play` **sur l'IP/relais de la bonne carte**.
|
||||
- `board` validé contre `boards.yaml` (gateway) ; carte inconnue → erreur de compile claire.
|
||||
|
||||
### 3.3 Média réel (SD)
|
||||
- Bloc **`mediaPlay`** : `path` (chemin SD réel, ex. `zacus/<board>/<file>.wav`, piochable dans le pool stagé P4) + `board` (hérité de `hwOnBoard` si présent) + `loop?`.
|
||||
- Remplace l'usage opaque de `hwSoundPlay`(`asset`). Compile → action `sound_play{path,board}`.
|
||||
- Le gateway expose le routage : action `sound_play` d'un step joué → `POST http://<ip(board)>/game/media/play {path}` (résolution IP/relais via `boards.yaml`). Cohérent avec le staging P4 (`/sdcard/zacus/{board}/`).
|
||||
|
||||
### 3.4 Transitions réelles
|
||||
- Nouveaux blocs « **Quand …** » (hat/stack attachés à un step) produisant une `RuntimeTransition` avec le vrai `event_type` :
|
||||
- `onButton(button)` → `event_type: button`, `event_name: <button>` ;
|
||||
- `onAudioDone()` → `audio_done` ;
|
||||
- `onUnlock(source)` → `unlock` ;
|
||||
- `onSerial(token)` → `serial` ;
|
||||
- `onEspnow(command)` → `espnow` ;
|
||||
- `onTimer(seconds)` → `timer` (`after_ms`) — existe déjà via `logicTimer`, à aligner ;
|
||||
- `onPuzzleSolved(puzzleId)` → branchement sur `puzzle_state` (résolu) ; `onCodeAssembled(code)` → idem code assemblé.
|
||||
- Chaque bloc « Quand » porte une cible `target` (step id). Compilation dans `blocks_to_runtime3.py` : émettre la transition avec `event_type`/`event_name`/`target_step_id` corrects (aujourd'hui seuls `action`/`timer` sortent). `runtime3_common.py::EVENT_TYPES` déjà = `{button,serial,timer,audio_done,unlock,espnow,action}` → pas d'extension du schéma, juste de l'émission.
|
||||
|
||||
### 3.5 Contrat executor annexe (roadmap, doc seulement)
|
||||
- Spécifier un **schéma CMD ESP-NOW structuré** (JSON compact ≤200 o) remplaçant le texte libre : `{op, args}` (ex. `{"op":"play","args":{"path":"…"}}`, `{"op":"led","args":{"pattern":"…"}}`). Documente le contrat (`docs/` ou spec firmware) pour que les cartes annexes (M5/BOX-3/PLIP) le décodent **plus tard** ; aucun firmware annexe écrit ici.
|
||||
|
||||
## 4. Composants & fichiers
|
||||
|
||||
| Fichier | Changement |
|
||||
|---|---|
|
||||
| `frontend-v3/packages/shared/blockly/zacus_blocks.mjs` | `ZACUS_REAL_KINDS` ; champ `board` sur puzzles ; blocs `hwOnBoard`, `mediaPlay`, `onButton/onAudioDone/onUnlock/onSerial/onEspnow/onPuzzleSolved/onCodeAssembled` ; réorg `ZACUS_TOOLBOX_CATEGORIES` (réel vs futur) |
|
||||
| `frontend-v3/apps/atelier/src/components/toolbox.ts` | (auto depuis catégories) — vérifier le rendu 2 voies |
|
||||
| `frontend-v3/apps/atelier/src/lib/yaml-export.ts` | sérialiser les nouveaux params (`board`, `path`, event targets) |
|
||||
| `tools/zacus-gateway/blocks_to_runtime3.py` | compiler `board`, `mediaPlay`→`sound_play{path,board}`, blocs `onX`→transitions `event_type` réelles |
|
||||
| `tools/scenario/runtime3_common.py` | validation `board`∈boards, `sound_play.path`, warning « bloc hardware-futur » |
|
||||
| `tools/zacus-gateway/main.py` | routage : `sound_play`/relais vers la bonne carte (IP/relais via `boards.yaml`) ; endpoint de « run d'un step » si nécessaire |
|
||||
| `docs/…/esp-now-cmd-contract.md` | contrat CMD structuré (roadmap) |
|
||||
|
||||
## 5. Découpage d'implémentation (phases)
|
||||
- **D1** — Réorg honnête : `ZACUS_REAL_KINDS` + catégories toolbox 2 voies + badge/warning compile « hardware futur ». Web + compile, risque nul (aucune sémantique cassée). Régénérer le catalogue hub (`tools/dev/sync_blockly_catalog.sh` si présent).
|
||||
- **D2** — Binding carte : champ `board` + bloc `hwOnBoard` + compile (`step.board`/`action.board`) + validation `boards.yaml` + routage gateway relais.
|
||||
- **D3** — Média réel SD : bloc `mediaPlay(path,board,loop)` + compile `sound_play{path,board}` + routage `/game/media/play` sur la bonne carte.
|
||||
- **D4** — Transitions réelles : blocs `onX` → `RuntimeTransition` avec `event_type` réels + branchement `puzzle_state`.
|
||||
- **D5** — Doc contrat executor annexe ESP-NOW.
|
||||
|
||||
## 6. Tests & risques
|
||||
- **Pipeline** : `make content-checks` / `make runtime3-*`, `python3 -m unittest discover -s tests`, et un round-trip blocs→IR validé (compile + validators) à chaque phase.
|
||||
- **Frontend** : `tsc --noEmit` + `vite build` verts (toolbox).
|
||||
- **Parité TS↔PY** : `yaml-export.ts` (serialize) et `blocks_to_runtime3.py` (compile) doivent rester en miroir — tout nouveau param sérialisé côté TS doit être lu côté PY.
|
||||
- **E2E hardware** : routage gateway→carte (relais, media/play) testable seulement avec les cartes — hardware-gated ; sinon stub/contrat.
|
||||
- **Risque principal** : la double implémentation TS (sérialisation/preview) + Python (compile/validation) — un champ ajouté d'un seul côté = silencieusement perdu. Chaque phase ajoute le champ des DEUX côtés + un test de parité.
|
||||
|
||||
## 7. Hors périmètre
|
||||
- Profils de groupe (TECH/NON_TECH/MIXED/BOTH) — écartés pour cette itération.
|
||||
- Écriture de firmware annexe (M5/BOX-3/PLIP) — seulement le contrat documenté (D5).
|
||||
- Implémentation firmware des énigmes manquantes (morse/NFC/…) — restent « hardware futur ».
|
||||
- Le graphe spatial FlowGraph (déjà fait, branche séparée).
|
||||
Reference in New Issue
Block a user