78 lines
3.1 KiB
GDScript
78 lines
3.1 KiB
GDScript
extends Node
|
|
signal action_activated(cabinet_id: StringName, action_id: StringName, context: Dictionary)
|
|
signal action_deactivated(cabinet_id: StringName, action_id: StringName, context: Dictionary)
|
|
signal status_changed(message: String)
|
|
var active_cabinet: CabinetProfile
|
|
var real_gpio := false
|
|
var status := "GPIO simulation (use --gpio=real on the Pi)"
|
|
var client: Node
|
|
var generations: Dictionary = {}
|
|
|
|
func _ready() -> void:
|
|
real_gpio = "--gpio=real" in OS.get_cmdline_user_args()
|
|
if real_gpio:
|
|
client = preload("res://core/runtime/pi_gpio_client.gd").new()
|
|
client.status_changed.connect(_status)
|
|
add_child(client)
|
|
_status("GPIO real mode — local Pi bridge required")
|
|
if "--gpio-test" in OS.get_cmdline_user_args():
|
|
test_led.call_deferred()
|
|
|
|
func _status(message: String) -> void:
|
|
status = message
|
|
print("[GPIO] ", message)
|
|
status_changed.emit(message)
|
|
|
|
func configure(cabinet: CabinetProfile) -> void:
|
|
active_cabinet = cabinet
|
|
|
|
func trigger(action_id: StringName, context: Dictionary = {}) -> void:
|
|
if active_cabinet == null:
|
|
_status("No active cabinet for GPIO action")
|
|
return
|
|
trigger_for_cabinet(active_cabinet, action_id, context)
|
|
|
|
func trigger_for_cabinet(cabinet: CabinetProfile, action_id: StringName, context: Dictionary = {}) -> void:
|
|
if cabinet == null or cabinet.hardware_config == null:
|
|
_status("Missing cabinet hardware configuration")
|
|
return
|
|
var action := cabinet.hardware_config.get_action(action_id)
|
|
if action == null:
|
|
_status("Unknown hardware action: %s" % action_id)
|
|
return
|
|
_send_action(cabinet, action, context)
|
|
|
|
func _send_action(cabinet: CabinetProfile, action: HardwareAction, context: Dictionary) -> void:
|
|
var key := "%s:%s" % [cabinet.hardware_config.controller_id, action.gpio_pin]
|
|
var generation := int(generations.get(key, 0)) + 1
|
|
generations[key] = generation
|
|
var pulse := action.action_type == HardwareAction.ActionType.PULSE_OUTPUT
|
|
var active := true if pulse else bool(context.get("active", true))
|
|
if real_gpio:
|
|
var ok: bool = await client.send({
|
|
"controller": str(cabinet.hardware_config.controller_id),
|
|
"pin": action.gpio_pin,
|
|
"active_high": action.active_high,
|
|
"mode": "pulse" if pulse else "set",
|
|
"duration_ms": action.pulse_duration_ms,
|
|
"active": active,
|
|
})
|
|
if not ok: return
|
|
else:
|
|
print("[MOCK GPIO] %s cabinet=%s action=%s BCM=%d" % ["PULSE" if pulse else "SET", cabinet.cabinet_id, action.action_id, action.gpio_pin])
|
|
if active:
|
|
action_activated.emit(cabinet.cabinet_id, action.action_id, context)
|
|
else:
|
|
action_deactivated.emit(cabinet.cabinet_id, action.action_id, context)
|
|
if pulse:
|
|
await get_tree().create_timer(action.pulse_duration_ms / 1000.0).timeout
|
|
if int(generations.get(key, 0)) == generation:
|
|
# Physical timing belongs to the bridge, even if the game exits mid-pulse.
|
|
action_deactivated.emit(cabinet.cabinet_id, action.action_id, context)
|
|
|
|
func test_led() -> void:
|
|
var cabinet = load("res://data/cabinets/cabinet_01.tres").duplicate(true)
|
|
var action: HardwareAction = cabinet.hardware_config.get_action(&"coin_release")
|
|
action.pulse_duration_ms = 2000
|
|
_send_action(cabinet, action, {"source": "gpio_led_test"})
|