Add test execution dashboard page

This commit is contained in:
2025-10-17 11:13:32 +00:00
parent a30ebcab96
commit 98af58d25c

View File

@@ -14,9 +14,12 @@ from dataclasses import dataclass, field
import streamlit as st
from py_dvt_ate.data.repository import SQLiteRepository
from py_dvt_ate.framework.runner import TestRunner
from py_dvt_ate.instruments.factory import InstrumentConfig, InstrumentFactory, InstrumentSet
from py_dvt_ate.simulation.physics.engine import PhysicsEngine
from py_dvt_ate.simulation.server import ServerConfig, SimulationServer
from py_dvt_ate.tests.thermal.tempco import TempCoTest
# History buffer size for charts
HISTORY_SIZE = 500
@@ -105,12 +108,23 @@ def init_session_state() -> None:
)
st.session_state.instruments = InstrumentFactory.create(config)
if "repository" not in st.session_state:
# Create test repository (in-memory for dashboard demo)
st.session_state.repository = SQLiteRepository(":memory:")
if "test_runner" not in st.session_state:
st.session_state.test_runner = TestRunner(st.session_state.repository)
if "history" not in st.session_state:
st.session_state.history = SimulationHistory()
if "running" not in st.session_state:
st.session_state.running = False
if "last_update" not in st.session_state:
st.session_state.last_update = time.time()
if "test_running" not in st.session_state:
st.session_state.test_running = False
if "test_run_id" not in st.session_state:
st.session_state.test_run_id = None
# Note: time_multiplier, temp_setpoint, input_voltage, output_enabled,
# load_current are managed by their respective widgets via keys
@@ -457,6 +471,189 @@ self-heating effects!
)
def test_execution_page() -> None:
"""Test execution page for running DVT tests."""
st.header("Test Execution")
st.markdown("Run DVT characterisation tests using the virtual lab bench.")
# Test selection
st.subheader("Select Test")
test_options = {
"TempCo (Temperature Coefficient)": TempCoTest(),
}
selected_test_name = st.selectbox(
"Available Tests",
options=list(test_options.keys()),
disabled=st.session_state.test_running,
)
selected_test = test_options[selected_test_name]
# Display test description
st.info(f"**{selected_test.name}**: {selected_test.description}")
# Test configuration
st.subheader("Test Configuration")
col1, col2 = st.columns(2)
with col1:
if selected_test.name == "tempco":
input_voltage = st.number_input(
"Input Voltage (V)",
min_value=0.0,
max_value=12.0,
value=5.0,
step=0.1,
disabled=st.session_state.test_running,
)
load_current = st.number_input(
"Load Current (mA)",
min_value=0.0,
max_value=500.0,
value=100.0,
step=10.0,
disabled=st.session_state.test_running,
)
settle_time = st.number_input(
"Settle Time (s)",
min_value=0.0,
max_value=60.0,
value=5.0,
step=1.0,
disabled=st.session_state.test_running,
)
with col2:
if selected_test.name == "tempco":
temp_min = st.number_input(
"Min Temperature (C)",
min_value=-40.0,
max_value=125.0,
value=-40.0,
step=5.0,
disabled=st.session_state.test_running,
)
temp_max = st.number_input(
"Max Temperature (C)",
min_value=-40.0,
max_value=125.0,
value=85.0,
step=5.0,
disabled=st.session_state.test_running,
)
temp_step = st.number_input(
"Temperature Step (C)",
min_value=1.0,
max_value=50.0,
value=25.0,
step=5.0,
disabled=st.session_state.test_running,
)
# Generate temperature points
if selected_test.name == "tempco":
import numpy as np
temperatures = list(np.arange(temp_min, temp_max + temp_step / 2, temp_step))
st.caption(f"Temperature points: {temperatures}")
test_config = {
"temperatures": temperatures,
"input_voltage": input_voltage,
"load_current": load_current / 1000.0, # Convert mA to A
"settle_time": settle_time,
}
else:
test_config = {}
# Run test button
st.subheader("Execution")
col1, col2 = st.columns([1, 3])
with col1:
if st.button(
"Run Test",
type="primary",
use_container_width=True,
disabled=st.session_state.test_running,
):
st.session_state.test_running = True
st.session_state.test_run_id = None
st.rerun()
with col2:
if st.session_state.test_running:
st.info("🔄 Test is running...")
elif st.session_state.test_run_id:
st.success("✅ Test completed!")
else:
st.caption("Click 'Run Test' to start")
# Execute test if running
if st.session_state.test_running:
with st.spinner("Running test..."):
try:
runner: TestRunner = st.session_state.test_runner
instruments: InstrumentSet = st.session_state.instruments
# Run the test
run_id = runner.run_test(
test=selected_test,
instruments=instruments,
config=test_config,
operator="dashboard_user",
description=f"Dashboard execution: {selected_test_name}",
)
st.session_state.test_run_id = run_id
st.session_state.test_running = False
st.rerun()
except Exception as e:
st.error(f"Test execution failed: {e}")
st.session_state.test_running = False
# Display results if available
if st.session_state.test_run_id:
st.subheader("Test Results")
repository: SQLiteRepository = st.session_state.repository
run = repository.get_run(st.session_state.test_run_id)
col1, col2, col3 = st.columns(3)
with col1:
status_color = {"PASSED": "🟢", "FAILED": "🔴", "ERROR": "🟡"}.get(
run.status.value, ""
)
st.metric("Status", f"{status_color} {run.status.value}")
with col2:
if run.completed_at and run.started_at:
duration = (run.completed_at - run.started_at).total_seconds()
st.metric("Duration", f"{duration:.1f} s")
else:
st.metric("Duration", "N/A")
with col3:
results = repository.get_results(st.session_state.test_run_id)
st.metric("Results", len(results))
# Show detailed results
if results:
st.markdown("#### Detailed Results")
results_data = []
for result in results:
results_data.append({
"Parameter": result.parameter,
"Value": f"{result.value:.6f}",
"Unit": result.unit or "",
"Lower Limit": f"{result.lower_limit:.6f}" if result.lower_limit is not None else "N/A",
"Upper Limit": f"{result.upper_limit:.6f}" if result.upper_limit is not None else "N/A",
"Status": "✅ PASS" if result.passed else "❌ FAIL",
})
st.table(results_data)
def main() -> None:
"""Main entry point for the Streamlit dashboard."""
st.set_page_config(
@@ -480,9 +677,20 @@ def main() -> None:
# Sidebar controls (static - doesn't need fragment)
display_controls()
# Create tabs for different views
tab1, tab2, tab3 = st.tabs(["🔬 Lab Bench", "🧪 Test Execution", "📊 Results Viewer"])
with tab1:
# Dynamic simulation display (uses fragment for smooth updates)
simulation_display()
with tab2:
test_execution_page()
with tab3:
st.header("Results Viewer")
st.info("Results viewer coming soon in Task 17.3!")
if __name__ == "__main__":
main()