9c31de3c38
config into dataclass and enums new Gui that includes settings deleted GlobalVariables small fixes (import, names...)
184 lines
6.9 KiB
Python
184 lines
6.9 KiB
Python
"""
|
|
MainWindow.py - Master Dashboard with Dynamic Input Selection
|
|
"""
|
|
import random
|
|
import time
|
|
import dearpygui.dearpygui as dpg
|
|
from config import cfg, BackendType
|
|
|
|
# Input State Tracking
|
|
input_config = {
|
|
"source": "Gamepad", # Options: "Gamepad", "GUI Sliders", "Random Walk"
|
|
"manual_vx": 0.0,
|
|
"manual_vy": 0.0,
|
|
"manual_omega": 0.0,
|
|
"random_interval": 2.0, # Change random vector every N seconds
|
|
"last_random_time": 0.0,
|
|
"random_vx": 0.0,
|
|
"random_vy": 0.0,
|
|
"random_omega": 0.0,
|
|
}
|
|
|
|
|
|
def create_parameter_gui(on_start_callback=None, on_stop_callback=None):
|
|
dpg.create_context()
|
|
dpg.create_viewport(title="JackBot Master Control Deck", width=460, height=800)
|
|
|
|
is_running = {"value": False}
|
|
|
|
def toggle_start(sender, app_data, user_data):
|
|
is_running["value"] = not is_running["value"]
|
|
if is_running["value"]:
|
|
dpg.configure_item("start_btn", label="STOP ROBOT")
|
|
dpg.configure_item("status_text", default_value="Status: RUNNING", color=(0, 255, 0))
|
|
dpg.configure_item("backend_combo", enabled=False)
|
|
if on_start_callback:
|
|
on_start_callback()
|
|
else:
|
|
dpg.configure_item("start_btn", label="START ROBOT")
|
|
dpg.configure_item("status_text", default_value="Status: STOPPED", color=(255, 100, 100))
|
|
dpg.configure_item("backend_combo", enabled=True)
|
|
if on_stop_callback:
|
|
on_stop_callback()
|
|
|
|
with dpg.window(label="Robot Control & Telemetry Deck", width=440, height=770, pos=(5, 5)):
|
|
|
|
# --- Execution Control ---
|
|
dpg.add_text("Control State", color=(0, 200, 255))
|
|
dpg.add_text("Status: STOPPED", tag="status_text", color=(255, 100, 100))
|
|
dpg.add_button(
|
|
label="START ROBOT",
|
|
tag="start_btn",
|
|
width=-1,
|
|
height=40,
|
|
callback=toggle_start
|
|
)
|
|
dpg.add_separator()
|
|
|
|
# --- Input Source Switcher ---
|
|
dpg.add_text("Input Controller Source", color=(0, 200, 255))
|
|
dpg.add_combo(
|
|
tag="input_source_combo",
|
|
label="Input Source",
|
|
items=["Gamepad", "GUI Sliders", "Random Walk"],
|
|
default_value=input_config["source"],
|
|
callback=lambda s, d: input_config.update({"source": d})
|
|
)
|
|
|
|
# Manual Sliders (Active when GUI Sliders selected)
|
|
dpg.add_slider_float(
|
|
label="Vx (Fwd/Bwd)",
|
|
default_value=0.0, min_value=-1.0, max_value=1.0,
|
|
callback=lambda s, d: input_config.update({"manual_vx": d})
|
|
)
|
|
dpg.add_slider_float(
|
|
label="Vy (Strafe)",
|
|
default_value=0.0, min_value=-1.0, max_value=1.0,
|
|
callback=lambda s, d: input_config.update({"manual_vy": d})
|
|
)
|
|
dpg.add_slider_float(
|
|
label="Omega (Turn)",
|
|
default_value=0.0, min_value=-1.0, max_value=1.0,
|
|
callback=lambda s, d: input_config.update({"manual_omega": d})
|
|
)
|
|
dpg.add_separator()
|
|
|
|
# --- Active Command Readouts ---
|
|
dpg.add_text("Active Command Output", color=(0, 200, 255))
|
|
dpg.add_text("Joysticks Connected: 0", tag="txt_joy_count")
|
|
dpg.add_text("Motion State: IDLE", tag="txt_motion_state", color=(200, 200, 200))
|
|
dpg.add_text("Active Vx : 0.00", tag="txt_vx")
|
|
dpg.add_text("Active Vy : 0.00", tag="txt_vy")
|
|
dpg.add_text("Active Omega: 0.00", tag="txt_omega")
|
|
dpg.add_separator()
|
|
|
|
# --- Backend Selection ---
|
|
dpg.add_text("Backend Selection", color=(0, 200, 255))
|
|
dpg.add_combo(
|
|
tag="backend_combo",
|
|
label="Target Backend",
|
|
items=[e.value for e in BackendType],
|
|
default_value=cfg.backend.value,
|
|
callback=lambda sender, data: setattr(cfg, 'backend', BackendType(data))
|
|
)
|
|
dpg.add_separator()
|
|
|
|
# --- Kinematics Adjustments ---
|
|
dpg.add_text("Gait & Kinematics", color=(0, 200, 255))
|
|
dpg.add_slider_float(
|
|
label="Step Height (m)",
|
|
default_value=cfg.step_height,
|
|
min_value=0.01, max_value=0.10, format="%.3f m",
|
|
callback=lambda sender, data: setattr(cfg, 'step_height', data)
|
|
)
|
|
dpg.add_slider_float(
|
|
label="Step Length (m)",
|
|
default_value=cfg.step_length,
|
|
min_value=0.01, max_value=0.10, format="%.3f m",
|
|
callback=lambda sender, data: setattr(cfg, 'step_length', data)
|
|
)
|
|
dpg.add_separator()
|
|
|
|
# --- Hardware Comms ---
|
|
dpg.add_text("Hardware Communications", color=(0, 200, 255))
|
|
dpg.add_input_text(label="ESP32 IP", default_value=cfg.esp32_ip, callback=lambda s, d: setattr(cfg, 'esp32_ip', d))
|
|
dpg.add_input_text(label="Serial Port", default_value=cfg.port, callback=lambda s, d: setattr(cfg, 'port', d))
|
|
|
|
dpg.setup_dearpygui()
|
|
dpg.show_viewport()
|
|
|
|
|
|
def resolve_active_command(gamepad_cmd: dict | None) -> dict:
|
|
"""Selects motion vector based on current active source in GUI."""
|
|
source = input_config["source"]
|
|
now = time.time()
|
|
|
|
if source == "GUI Sliders":
|
|
vx = input_config["manual_vx"]
|
|
vy = input_config["manual_vy"]
|
|
omega = input_config["manual_omega"]
|
|
joy_count = gamepad_cmd.get("joysticks_count", 0) if gamepad_cmd else 0
|
|
|
|
elif source == "Random Walk":
|
|
# Generate new random direction vector periodically
|
|
if now - input_config["last_random_time"] > input_config["random_interval"]:
|
|
input_config["random_vx"] = round(random.uniform(-1.0, 1.0), 2)
|
|
input_config["random_vy"] = round(random.uniform(-1.0, 1.0), 2)
|
|
input_config["random_omega"] = round(random.uniform(-1.0, 1.0), 2)
|
|
input_config["last_random_time"] = now
|
|
|
|
vx = input_config["random_vx"]
|
|
vy = input_config["random_vy"]
|
|
omega = input_config["random_omega"]
|
|
joy_count = gamepad_cmd.get("joysticks_count", 0) if gamepad_cmd else 0
|
|
|
|
else: # "Gamepad"
|
|
if gamepad_cmd:
|
|
vx, vy, omega = gamepad_cmd.get("dirmov", [0.0, 0.0, 0.0])
|
|
joy_count = gamepad_cmd.get("joysticks_count", 0)
|
|
else:
|
|
vx, vy, omega, joy_count = 0.0, 0.0, 0.0, 0
|
|
|
|
state = "walking" if (vx != 0.0 or vy != 0.0 or omega != 0.0) else "idle"
|
|
|
|
# Update GUI Labels
|
|
if dpg.is_dearpygui_running():
|
|
dpg.set_value("txt_joy_count", f"Joysticks Connected: {joy_count}")
|
|
dpg.set_value("txt_motion_state", f"Motion State: {state.upper()} ({source})")
|
|
dpg.set_value("txt_vx", f"Active Vx : {vx:>6.2f}")
|
|
dpg.set_value("txt_vy", f"Active Vy : {vy:>6.2f}")
|
|
dpg.set_value("txt_omega", f"Active Omega: {omega:>6.2f}")
|
|
|
|
return {
|
|
"state": state,
|
|
"dirmov": [vx, vy, omega]
|
|
}
|
|
|
|
|
|
def render_gui_frame():
|
|
if dpg.is_dearpygui_running():
|
|
dpg.render_dearpygui_frame()
|
|
|
|
|
|
def stop_gui():
|
|
dpg.destroy_context() |