Files
room_glitch/editor/workspace/simulation_timeline.gd
T

678 lines
7.8 KiB
GDScript

@tool
class_name SimulationTimeline
extends VBoxContainer
signal pause_requested
signal resume_requested
signal step_requested(
seconds: float
)
signal reset_requested
signal sequence_selected(
index: int
)
signal run_sequence_requested
signal stop_sequence_requested
signal replay_sequence_requested
var sequence_select: OptionButton
var run_button: Button
var stop_button: Button
var replay_button: Button
var tree: Tree
var clear_button: Button
var count_label: Label
var _first_trace_time: float = -1.0
var _entry_count: int = 0
var pause_button: Button
var step_button: Button
var reset_button: Button
var _paused: bool = false
func _ready() -> void:
if tree != null:
return
_build_ui()
func _build_ui() -> void:
var toolbar := HBoxContainer.new()
add_child(
toolbar
)
var title := Label.new()
title.text = "Simulation Timeline"
title.size_flags_horizontal = (
Control.SIZE_EXPAND_FILL
)
toolbar.add_child(
title
)
count_label = Label.new()
count_label.text = "0 events"
toolbar.add_child(
count_label
)
sequence_select = OptionButton.new()
sequence_select.custom_minimum_size.x = 180.0
sequence_select.item_selected.connect(
func(index: int):
sequence_selected.emit(
index
)
)
toolbar.add_child(
sequence_select
)
run_button = Button.new()
run_button.text = "Run"
run_button.pressed.connect(
func():
run_sequence_requested.emit()
)
toolbar.add_child(
run_button
)
stop_button = Button.new()
stop_button.text = "Stop"
stop_button.disabled = true
stop_button.pressed.connect(
func():
stop_sequence_requested.emit()
)
toolbar.add_child(
stop_button
)
replay_button = Button.new()
replay_button.text = "Replay"
replay_button.pressed.connect(
func():
replay_sequence_requested.emit()
)
toolbar.add_child(
replay_button
)
pause_button = Button.new()
pause_button.text = "Pause"
pause_button.pressed.connect(
_on_pause_button_pressed
)
toolbar.add_child(
pause_button
)
step_button = Button.new()
step_button.text = "Step 0.1s"
step_button.pressed.connect(
func():
step_requested.emit(
0.1
)
)
toolbar.add_child(
step_button
)
reset_button = Button.new()
reset_button.text = "Reset"
reset_button.pressed.connect(
func():
reset_requested.emit()
)
toolbar.add_child(
reset_button
)
clear_button = Button.new()
clear_button.text = "Clear"
clear_button.pressed.connect(
clear
)
toolbar.add_child(
clear_button
)
tree = Tree.new()
tree.size_flags_horizontal = (
Control.SIZE_EXPAND_FILL
)
tree.size_flags_vertical = (
Control.SIZE_EXPAND_FILL
)
tree.custom_minimum_size.y = 180.0
tree.columns = 4
tree.set_column_title(
0,
"Time"
)
tree.set_column_title(
1,
"Type"
)
tree.set_column_title(
2,
"Source / Target"
)
tree.set_column_title(
3,
"Detail"
)
tree.column_titles_visible = true
tree.set_column_expand(
0,
false
)
tree.set_column_custom_minimum_width(
0,
80
)
tree.set_column_expand(
1,
false
)
tree.set_column_custom_minimum_width(
1,
90
)
tree.set_column_expand(
2,
true
)
tree.set_column_expand(
3,
true
)
add_child(
tree
)
tree.create_item()
set_paused(
false
)
func clear() -> void:
_first_trace_time = -1.0
_entry_count = 0
if count_label != null:
count_label.text = "0 events"
if tree == null:
return
tree.clear()
tree.create_item()
func add_trace_entry(
entry: Dictionary
) -> void:
if tree == null:
_build_ui()
var absolute_time := float(
entry.get(
"time",
0.0
)
)
if _first_trace_time < 0.0:
_first_trace_time = absolute_time
var relative_time := (
absolute_time
- _first_trace_time
)
var root := tree.get_root()
if root == null:
root = tree.create_item()
var item := tree.create_item(
root
)
item.set_text(
0,
"%.3f" % relative_time
)
var entry_type := StringName(
entry.get(
"type",
&"unknown"
)
)
item.set_text(
1,
String(entry_type).to_upper()
)
item.set_text(
2,
_get_subject_text(
entry
)
)
item.set_text(
3,
_get_detail_text(
entry
)
)
_entry_count += 1
if count_label != null:
count_label.text = (
"%d events"
% _entry_count
)
tree.scroll_to_item(
item
)
func _get_subject_text(
entry: Dictionary
) -> String:
var entry_type := StringName(
entry.get(
"type",
&""
)
)
match entry_type:
&"event":
return String(
entry.get(
"source_id",
&""
)
)
&"rule":
return String(
entry.get(
"rule_id",
&""
)
)
&"action":
return String(
entry.get(
"target_id",
&""
)
)
&"gpio":
return String(
entry.get(
"cabinet_id",
&""
)
)
&"device":
return String(
entry.get(
"device_id",
&""
)
)
&"input":
return String(
entry.get(
"source_id",
&""
)
)
&"sequence":
return String(
entry.get(
"display_name",
entry.get(
"sequence_id",
&""
)
)
)
_:
return ""
func _get_detail_text(
entry: Dictionary
) -> String:
var entry_type := StringName(
entry.get(
"type",
&""
)
)
match entry_type:
&"event":
return String(
entry.get(
"event_id",
&""
)
)
&"rule":
var target_id := String(
entry.get(
"target_id",
&""
)
)
var action_id := String(
entry.get(
"action_id",
&""
)
)
var delay := float(
entry.get(
"delay_seconds",
0.0
)
)
if delay > 0.0:
return (
"→ %s.%s after %.3fs"
% [
target_id,
action_id,
delay
]
)
return (
"→ %s.%s"
% [
target_id,
action_id
]
)
&"action":
return String(
entry.get(
"action_id",
&""
)
)
&"gpio":
var pin := int(
entry.get(
"gpio_pin",
-1
)
)
var electrical_state := bool(
entry.get(
"electrical_state",
false
)
)
var action_id := String(
entry.get(
"action_id",
&""
)
)
var level := "LOW"
if electrical_state:
level = "HIGH"
return (
"GPIO %d %s (%s)"
% [
pin,
level,
action_id
]
)
&"device":
var active := bool(
entry.get(
"active",
false
)
)
if active:
return "ACTIVE"
return "INACTIVE"
&"input":
var event_id := String(
entry.get(
"event_id",
&""
)
)
var note := String(
entry.get(
"note",
""
)
)
if not note.is_empty():
return (
"%s — %s"
% [
event_id,
note
]
)
return event_id
&"sequence":
var state := String(
entry.get(
"state",
&""
)
)
return state.to_upper()
_:
return str(
entry
)
func _on_pause_button_pressed() -> void:
if _paused:
resume_requested.emit()
else:
pause_requested.emit()
func set_paused(
value: bool
) -> void:
_paused = value
if pause_button != null:
if _paused:
pause_button.text = "Resume"
else:
pause_button.text = "Pause"
if step_button != null:
step_button.disabled = not _paused
func set_sequences(
sequences: Array[TestSequence]
) -> void:
if sequence_select == null:
return
sequence_select.clear()
for sequence in sequences:
if sequence == null:
continue
var label := (
sequence.display_name
)
if label.is_empty():
label = String(
sequence.sequence_id
)
sequence_select.add_item(
label
)
if sequence_select.item_count > 0:
sequence_select.select(
0
)
func set_sequence_running(
running: bool
) -> void:
if run_button != null:
run_button.disabled = running
if stop_button != null:
stop_button.disabled = not running
if replay_button != null:
replay_button.disabled = running
if sequence_select != null:
sequence_select.disabled = running