Polish dashboard UX and update README

- Wrap simulation controls in form to prevent page reruns on change
- Fix TempCo test configs to use 2+ temperature points
- Add Installation, Quick Start, and usage examples to README
This commit is contained in:
2025-11-15 13:18:38 +00:00
parent 9b8aeb22f4
commit 4ca0496200
4 changed files with 162 additions and 59 deletions

View File

@@ -233,63 +233,72 @@ def display_controls() -> None:
st.sidebar.divider()
# Time multiplier
st.sidebar.subheader("Simulation Speed")
st.sidebar.select_slider(
"Time Multiplier",
options=[1, 2, 5, 10, 20, 50, 100],
value=10,
format_func=lambda x: f"{x}x",
key="time_multiplier",
)
st.sidebar.caption(
f"1 real second = {st.session_state.get('time_multiplier', 10)} simulation seconds"
)
# Parameter Controls - wrapped in form to prevent reruns on every change
with st.sidebar.form("parameter_controls"):
st.subheader("Simulation Parameters")
st.caption("💡 Change parameters below, then click Apply to update the simulation")
st.sidebar.divider()
# Time multiplier
time_multiplier = st.select_slider(
"Time Multiplier",
options=[1, 2, 5, 10, 20, 50, 100],
value=st.session_state.get("time_multiplier", 10),
format_func=lambda x: f"{x}x",
)
st.caption(f"1 real second = {time_multiplier} simulation seconds")
# Temperature setpoint
st.sidebar.subheader("Thermal Chamber")
st.sidebar.slider(
"Temperature Setpoint (C)",
min_value=-40.0,
max_value=125.0,
value=25.0,
step=5.0,
key="temp_setpoint",
)
st.divider()
st.sidebar.divider()
# Temperature setpoint
st.markdown("**Thermal Chamber**")
temp_setpoint = st.slider(
"Temperature Setpoint (C)",
min_value=-40.0,
max_value=125.0,
value=st.session_state.get("temp_setpoint", 25.0),
step=5.0,
)
# Power supply controls
st.sidebar.subheader("Power Supply")
st.sidebar.slider(
"Input Voltage (V)",
min_value=0.0,
max_value=12.0,
value=5.0,
step=0.1,
key="input_voltage",
)
st.divider()
st.sidebar.toggle(
"Output Enabled",
value=False,
key="output_enabled",
)
# Power supply controls
st.markdown("**Power Supply**")
input_voltage = st.slider(
"Input Voltage (V)",
min_value=0.0,
max_value=12.0,
value=st.session_state.get("input_voltage", 5.0),
step=0.1,
)
st.sidebar.divider()
output_enabled = st.toggle(
"Output Enabled",
value=st.session_state.get("output_enabled", False),
)
# Load controls
st.sidebar.subheader("Electronic Load")
st.sidebar.slider(
"Load Current (mA)",
min_value=0.0,
max_value=500.0,
value=100.0,
step=10.0,
key="load_current",
)
st.divider()
# Load controls
st.markdown("**Electronic Load**")
load_current = st.slider(
"Load Current (mA)",
min_value=0.0,
max_value=500.0,
value=st.session_state.get("load_current", 100.0),
step=10.0,
)
# Apply button
submitted = st.form_submit_button("✅ Apply Changes", type="primary", use_container_width=True)
if submitted:
# Update session state with new values
st.session_state.time_multiplier = time_multiplier
st.session_state.temp_setpoint = temp_setpoint
st.session_state.input_voltage = input_voltage
st.session_state.output_enabled = output_enabled
st.session_state.load_current = load_current
st.rerun()
@st.fragment(run_every=0.1)
@@ -613,13 +622,13 @@ def test_execution_page() -> None:
runner: TestRunner = st.session_state.test_runner
instruments: InstrumentSet = st.session_state.instruments
# Connect instruments before running test
# Instruments should already be connected from dashboard startup
# Just verify they're connected
try:
instruments.chamber.transport.connect() # type: ignore[attr-defined]
instruments.psu.transport.connect() # type: ignore[attr-defined]
instruments.dmm.transport.connect() # type: ignore[attr-defined]
# Quick connectivity check
_ = instruments.chamber.get_temperature()
except Exception as e:
st.error(f"Failed to connect to instruments: {e}")
st.error(f"Instruments not available: {e}")
st.session_state.test_running = False
st.stop()

View File

@@ -74,6 +74,9 @@ class ITestRepository(ABC):
def get_all_runs(self) -> list[TestRun]:
"""Retrieve all test runs, ordered by started_at descending."""
def close(self) -> None:
"""Close repository and release resources. Optional to implement."""
class SQLiteRepository(ITestRepository):
"""SQLite-based repository for test data.
@@ -400,3 +403,13 @@ class SQLiteRepository(ITestRepository):
)
for row in rows
]
def close(self) -> None:
"""Close repository and release resources.
SQLite connections are managed via context managers and auto-close.
This method performs explicit cleanup for Windows file handle issues.
"""
# Force garbage collection to release any lingering connections
import gc
gc.collect()