Added Glitch Room Source code to Gitty
This commit is contained in:
commit
1ac9eb5644
505 files changed
+28504
No files matched your search
@@ -0,0 +1 @@
|
||||
__pycache__/
|
||||
@@ -0,0 +1,25 @@
|
||||
extends SceneTree
|
||||
var activated := false
|
||||
func _initialize() -> void:
|
||||
call_deferred("run")
|
||||
func run() -> void:
|
||||
var hardware = root.get_node("HardwareManager")
|
||||
hardware.action_activated.connect(func(_cabinet, _action, _context): activated = true)
|
||||
var launcher = load("res://games/launcher/ArcadeLauncher.tscn").instantiate()
|
||||
root.add_child(launcher)
|
||||
await process_frame
|
||||
await process_frame
|
||||
if is_instance_valid(launcher.setup):
|
||||
launcher.setup.close()
|
||||
await process_frame
|
||||
launcher._select(0)
|
||||
launcher.launch()
|
||||
launcher.game.start_game()
|
||||
launcher.game.win_game()
|
||||
await create_timer(1.0).timeout
|
||||
if not activated:
|
||||
push_error("Pac-Man win did not reach GPIO bridge: " + hardware.status)
|
||||
quit(1)
|
||||
else:
|
||||
print("Pac-Man win reached GPIO bridge")
|
||||
quit()
|
||||
@@ -0,0 +1 @@
|
||||
uid://cohmnr1oklto5
|
||||
@@ -0,0 +1,28 @@
|
||||
"""Run with the Godot console executable as the first argument. No physical GPIO."""
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from http.server import HTTPServer
|
||||
from test_bridge import FakePin, bridge
|
||||
|
||||
outputs = bridge.Outputs(FakePin, "cabinet_01_gpio", [17])
|
||||
server = HTTPServer(("127.0.0.1", 8765), bridge.handler_for(outputs))
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
try:
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
result = subprocess.run([
|
||||
sys.argv[1], "--headless", "--path", str(root),
|
||||
"--log-file", str(root / ".godot/gpio_integration.log"),
|
||||
"--script", "tests/gpio/check_gpio.gd", "--", "--gpio=real",
|
||||
], timeout=15)
|
||||
assert result.returncode == 0, "Godot integration failed"
|
||||
assert 17 in outputs.pins, "Bridge never received BCM17"
|
||||
assert outputs.pins[17]["polarity"] is True, "Expected active-high output"
|
||||
print("Godot -> HTTP -> GPIO backend integration passed (fake pin)")
|
||||
finally:
|
||||
server.shutdown()
|
||||
thread.join()
|
||||
outputs.close()
|
||||
server.server_close()
|
||||
@@ -0,0 +1,105 @@
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import unittest
|
||||
import tempfile
|
||||
|
||||
source = Path(__file__).resolve().parents[2] / "tools/pi_gpio/pi_gpio_bridge.py"
|
||||
spec = importlib.util.spec_from_file_location("bridge", source)
|
||||
bridge = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(bridge)
|
||||
|
||||
|
||||
class FakePin:
|
||||
def __init__(self, pin, active_high, initial_value):
|
||||
self.pin, self.active_high, self.active = pin, active_high, initial_value
|
||||
self.closed = False
|
||||
self.activations = 0
|
||||
|
||||
def on(self):
|
||||
self.active = True
|
||||
self.activations += 1
|
||||
def off(self): self.active = False
|
||||
def close(self): self.closed = True
|
||||
|
||||
|
||||
class BridgeTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.now = 0
|
||||
self.outputs = bridge.Outputs(FakePin, "cabinet_01_gpio", [17], lambda: self.now)
|
||||
self.request = dict(controller="cabinet_01_gpio", pin=17, mode="pulse", active_high=True, duration_ms=600)
|
||||
|
||||
def test_pulse_and_retrigger(self):
|
||||
self.outputs.command(self.request)
|
||||
pin = self.outputs.pins[17]["device"]
|
||||
self.assertTrue(pin.active)
|
||||
self.now = .4
|
||||
self.outputs.command(self.request)
|
||||
self.now = .7
|
||||
self.outputs.tick()
|
||||
self.assertTrue(pin.active)
|
||||
self.now = 1.01
|
||||
self.outputs.tick()
|
||||
self.assertFalse(pin.active)
|
||||
|
||||
def test_active_low_and_shutdown(self):
|
||||
self.outputs.command(dict(self.request, active_high=False))
|
||||
pin = self.outputs.pins[17]["device"]
|
||||
self.assertFalse(pin.active_high)
|
||||
self.assertTrue(pin.active)
|
||||
self.outputs.close()
|
||||
self.assertFalse(pin.active)
|
||||
self.assertTrue(pin.closed)
|
||||
|
||||
def test_digital_off_and_watchdog(self):
|
||||
self.outputs.command(dict(self.request, mode="set"))
|
||||
self.now = 2
|
||||
self.outputs.heartbeat()
|
||||
self.now = 4
|
||||
self.outputs.tick()
|
||||
self.assertTrue(self.outputs.pins[17]["device"].active)
|
||||
self.now = 5.1
|
||||
self.outputs.tick()
|
||||
self.assertFalse(self.outputs.pins[17]["device"].active)
|
||||
self.outputs.command(dict(self.request, mode="set"))
|
||||
self.outputs.command(dict(self.request, mode="set", active=False))
|
||||
self.assertFalse(self.outputs.pins[17]["device"].active)
|
||||
|
||||
def test_reject_wrong_controller_pin_and_duration(self):
|
||||
for changes in [dict(controller="cabinet_02_gpio"), dict(pin=18), dict(duration_ms=0), dict(duration_ms=10001), dict(active_high="false")]:
|
||||
with self.assertRaises(ValueError):
|
||||
self.outputs.command(dict(self.request, **changes))
|
||||
self.assertFalse(self.outputs.pins)
|
||||
|
||||
def test_polarity_cannot_change_while_owned(self):
|
||||
self.outputs.command(self.request)
|
||||
with self.assertRaises(ValueError):
|
||||
self.outputs.command(dict(self.request, active_high=False))
|
||||
|
||||
def test_reward_dedup_survives_restart(self):
|
||||
with tempfile.TemporaryDirectory() as temp:
|
||||
path = Path(temp) / "requests.sqlite3"
|
||||
outputs = bridge.Outputs(FakePin, "cabinet_01_gpio", [17], ledger=path)
|
||||
request = dict(self.request, request_id="reward-1")
|
||||
self.assertTrue(outputs.command(request)["ok"])
|
||||
self.assertTrue(outputs.command(request)["ok"])
|
||||
self.assertEqual(outputs.pins[17]["device"].activations, 1)
|
||||
outputs.close()
|
||||
outputs = bridge.Outputs(FakePin, "cabinet_01_gpio", [17], ledger=path)
|
||||
self.assertTrue(outputs.command(request)["ok"])
|
||||
self.assertFalse(outputs.pins, "Duplicate must not even reopen output")
|
||||
with self.assertRaises(ValueError):
|
||||
outputs.command(dict(request, duration_ms=900))
|
||||
outputs.close()
|
||||
|
||||
def test_incomplete_reward_is_not_replayed(self):
|
||||
request = dict(self.request, request_id="uncertain")
|
||||
import hashlib, json
|
||||
digest = hashlib.sha256(json.dumps(request, sort_keys=True).encode()).hexdigest()
|
||||
with self.outputs.ledger:
|
||||
self.outputs.ledger.execute("INSERT INTO requests VALUES (?, ?, NULL)", ("uncertain", digest))
|
||||
self.assertFalse(self.outputs.command(request)["ok"])
|
||||
self.assertFalse(self.outputs.pins)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user