diff --git a/backend/app/main.py b/backend/app/main.py index 936fdfc..62a1e78 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -331,19 +331,23 @@ def run_simulator_scenario(payload: SimulatorScenarioCreate, db: Session = Depen elif payload.scenario == "cycle_signal_edge": current_value = machine.cycle_signal_edge != "falling" previous_value = not current_value + last_cycle_at = None + if machine.last_cycle_at is not None: + last_cycle_at = machine.last_cycle_at.replace(tzinfo=UTC) if machine.last_cycle_at.tzinfo is None else machine.last_cycle_at.astimezone(UTC) + current_event_time = last_cycle_at + timedelta(seconds=cycle_time_sec) if last_cycle_at is not None else now events.extend( [ SimulatorEventCreate( machine_id=machine.code, event_type="digital_input_changed", - timestamp=now - timedelta(milliseconds=max(machine.debounce_ms + 100, 500)), + timestamp=current_event_time - timedelta(milliseconds=max(machine.debounce_ms + 100, 500)), input_name="cycle_signal", input_value=previous_value, ), SimulatorEventCreate( machine_id=machine.code, event_type="digital_input_changed", - timestamp=now, + timestamp=current_event_time, input_name="cycle_signal", input_value=current_value, cycle_time_sec=cycle_time_sec, diff --git a/backend/tests/test_events.py b/backend/tests/test_events.py index 0b9120b..6749373 100644 --- a/backend/tests/test_events.py +++ b/backend/tests/test_events.py @@ -195,3 +195,23 @@ def test_simulator_debounce_noise_is_ignored(db_session: Session) -> None: assert result["scenario"] == "debounce_noise" assert len(result["events"]) == 2 assert cycles == [] + + +def test_simulator_passive_edge_uses_requested_real_cycle_time(db_session: Session) -> None: + machine = seed_running_machine(db_session) + machine.last_cycle_at = datetime(2026, 6, 14, 8, 0, tzinfo=timezone.utc) + db_session.commit() + + result = run_simulator_scenario( + SimulatorScenarioCreate( + machine_id=machine.code, + scenario="cycle_signal_edge", + cycle_time_sec=12, + ), + db_session, + ) + + cycle = db_session.execute(select(Cycle).where(Cycle.machine_id == machine.id)).scalar_one() + assert result["scenario"] == "cycle_signal_edge" + assert len(result["events"]) == 2 + assert cycle.cycle_time_sec == 12 diff --git a/frontend/public/KSS.png b/frontend/public/KSS.png new file mode 100644 index 0000000..0919c35 Binary files /dev/null and b/frontend/public/KSS.png differ diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6d8b97d..8ccc363 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -39,6 +39,7 @@ function FieldLabel({ label, error, children }: { label: string; error?: ReactNo } export default function App() { + const [showSplash, setShowSplash] = useState(true); const [dashboard, setDashboard] = useState(null); const [orders, setOrders] = useState([]); const [unqualifiedDowntimes, setUnqualifiedDowntimes] = useState([]); @@ -77,6 +78,11 @@ export default function App() { return language === "fr" ? fr : en; } + useEffect(() => { + const timer = window.setTimeout(() => setShowSplash(false), 900); + return () => window.clearTimeout(timer); + }, []); + const moduleTabs: Array<{ key: ModuleKey; label: string; description: string }> = [ { key: "atelier", label: t("Workshop", "Atelier"), description: t("Machines view", "Vue machines") }, { key: "machine", label: "Machine", description: t("Cycles and events", "Cycles et evenements") }, @@ -1118,13 +1124,34 @@ export default function App() { ); } + if (showSplash) { + return ( +
+ KSS +
+ ); + } + return (
+ +
-
-

Plast Track MVP

-

{t("Workshop supervision", "Supervision atelier")}

+
+ KSS +
+

Plast Track MVP

+

{t("Workshop supervision", "Supervision atelier")}

+