Add Pac-Man power-on flow and document Odin integration
- Add a four-second arcade-style boot sequence followed by the Character / Nickname introduction. - Separate cabinet power-on from RFID credit and player Start. - Add operator Power on control and cabinet power-state reporting. - Prevent repeated power commands from restarting boot or gameplay. - Ignore RFID scans received before the cabinet is ready. - Add power-state, RFID, controller, and GPIO feedback regression tests. - Expand the master walkthrough with export, packaging, file transfer, fresh-Pi setup, RFID compilation, and end-to-end testing instructions. - Correct tester hardware documentation: BCM23/J20 drives an LED; physical buttons connect through a separate I2C1 input board. - Document operator-triggered power-on as the current test workflow and retain the temporary 2.4 configuration compatibility notes. - Record successful physical testing of power-on, RFID credit, and player Start. - Add an Odin integration README describing the proposed Rust/NATS bridge, shared messages, Flutter GM interface, autopilot sequencing, hardware ownership, and Starfish SSH-management role. - Define migration milestones and information needed from the framework owner; link the integration guide from the root README. Validation: 69 controller tests, 10 GPIO tests, Pac-Man power/RFID checks, managed-cabinet lifecycle checks, and visual screen inspection.
This commit is contained in:
1 parent
c1b2195834
commit
41ef12f954
27 files changed
+577
-48
No files matched your search
@@ -157,6 +157,11 @@ class Room:
|
||||
self.state.update(session=str(uuid.uuid4()), phase="preparing" if action == "prepare" else "resetting",
|
||||
reward="not requested", commands=[], seen=[])
|
||||
self.command(action)
|
||||
elif action == "power_on":
|
||||
if self.state["game"] != "pacman" or "pacman_power_v1" not in self.features or self.state["phase"] != "powered_off":
|
||||
raise ValueError("Power on requires a prepared, powered-off Pac-Man cabinet (2.4+)")
|
||||
self.state["phase"] = "booting"
|
||||
self.command("power_on")
|
||||
elif action == "start":
|
||||
if self.state.get("game_config", {}).get("rfid_credit_device"):
|
||||
raise ValueError("Scan a card and press Start on the cabinet for this RFID scenario")
|
||||
@@ -214,9 +219,13 @@ class Room:
|
||||
self.state["reward"] = "GPIO accepted (physical delivery unconfirmed)"
|
||||
else:
|
||||
self.state["reward"] = "requested"
|
||||
elif command["type"] == "power_on":
|
||||
if not success: self.state["phase"] = "needs_prepare"
|
||||
elif success:
|
||||
self.state["phase"] = {"prepare": "ready", "reset": "waiting" if self.state.get("gate") else "idle", "start": "playing"}[command["type"]]
|
||||
if command["type"] == "prepare" and self.state.pop("auto_start", False):
|
||||
if command["type"] == "prepare" and self.state["game"] == "pacman" and "pacman_power_v1" in self.features:
|
||||
self.state["phase"] = "powered_off" if self.state.get("game_config", {}).get("power_on_device") else "booting"
|
||||
if command["type"] == "prepare" and self.state["phase"] == "ready" and self.state.pop("auto_start", False):
|
||||
self.state["phase"] = "starting"
|
||||
self.command("start")
|
||||
if command["type"] == "prepare":
|
||||
@@ -232,7 +241,14 @@ class Room:
|
||||
self.state["seen"].append(event_id)
|
||||
self.state["seen"] = self.state["seen"][-500:]
|
||||
name = report.get("event")
|
||||
if name == "started" and self.state["phase"] in ("ready", "starting"):
|
||||
if name == "power_booting" and self.state["game"] == "pacman" and self.state["phase"] == "powered_off":
|
||||
self.state["phase"] = "booting"
|
||||
elif name == "power_ready" and self.state["game"] == "pacman" and self.state["phase"] == "booting":
|
||||
self.state["phase"] = "ready"
|
||||
if self.state.pop("auto_start", False):
|
||||
self.state["phase"] = "starting"
|
||||
self.command("start")
|
||||
elif name == "started" and self.state["phase"] in ("ready", "starting"):
|
||||
self.state["phase"] = "playing"
|
||||
elif name == "won" and self.state["phase"] == "playing":
|
||||
self.state["phase"] = "won"
|
||||
@@ -282,6 +298,18 @@ class Fleet:
|
||||
limits.update({"galaga": {"score_to_win": (0, 1000000), "stages_to_win": (0, 100)},
|
||||
"frogger": {"homes_to_win": (0, 5), "boards_to_win": (0, 99)},
|
||||
"pacman": {"boards_to_win": (0, 100), "score_to_win": (0, 1000000)}}.get(game, {}))
|
||||
power_device = settings.get("power_on_device")
|
||||
if power_device is not None:
|
||||
if game != "pacman" or not isinstance(power_device, str) or not re.fullmatch(r"[A-Za-z0-9_-]{1,64}", power_device):
|
||||
raise ValueError("Invalid power-on input device")
|
||||
if not any(d.get("id") == power_device and d.get("kind") == "input" for d in self.catalog["cabinets"][cabinet].get("devices", [])):
|
||||
raise ValueError("Power-on device is missing from the cabinet PCB input inventory")
|
||||
if "pacman_power_v1" not in self.rooms[cabinet].features:
|
||||
raise ValueError("Update the game export: power-on inputs require arcade-cabinet-2.4")
|
||||
if self.rooms[cabinet].gpio != "real":
|
||||
raise ValueError("Power-on inputs require real GPIO feedback")
|
||||
if item.get("activate_after_win"):
|
||||
raise ValueError("Choose button power-on or automatic dependency activation")
|
||||
credit_device = settings.get("rfid_credit_device")
|
||||
if credit_device is not None:
|
||||
if game != "pacman" or not isinstance(credit_device, str) or not re.fullmatch(r"[A-Za-z0-9_-]{1,64}", credit_device):
|
||||
@@ -299,7 +327,7 @@ class Fleet:
|
||||
if credit_device is None or not isinstance(uids, list) or len(uids) > 100 or any(not isinstance(uid, str) or not re.fullmatch(r"[0-9A-F]{8}", uid) for uid in uids):
|
||||
raise ValueError("RFID allowed UIDs must be a list of uppercase 8-digit hexadecimal IDs")
|
||||
for key, value in settings.items():
|
||||
if key in ("rfid_credit_device", "rfid_allowed_uids"): continue
|
||||
if key in ("rfid_credit_device", "rfid_allowed_uids", "power_on_device"): continue
|
||||
if key not in limits or type(value) not in (int, float) or not math.isfinite(value):
|
||||
raise ValueError("Unsupported game objective: " + str(key))
|
||||
low, high = limits[key]
|
||||
|
||||
Reference in new issue
Block a user