From 2d50cdb454af9c821c55a1ed6330b276d278b437 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 4 Jul 2026 01:21:59 +0200 Subject: [PATCH] feat: add guided demo view --- README.md | 21 ++ docs/DEMO_SCRIPT.md | 81 +++----- docs/JURY_QA.md | 16 ++ docs/SUBMISSION_CHECKLIST.md | 7 + src/App.tsx | 29 ++- src/components/CurrentProofCard.tsx | 98 +++++++++ src/components/GuidedDemoView.tsx | 182 +++++++++++++++++ src/components/Header.tsx | 3 +- src/domain/types.ts | 2 +- src/styles.css | 300 ++++++++++++++++++++++++++++ 10 files changed, 685 insertions(+), 54 deletions(-) create mode 100644 src/components/CurrentProofCard.tsx create mode 100644 src/components/GuidedDemoView.tsx diff --git a/README.md b/README.md index b456609..cb52e3f 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,27 @@ https://www.arhipovdan.ru/ http://127.0.0.1:3100/ ``` +### Guided Demo + +Рекомендуемый путь для жюри — использовать **Guided Demo** view. +В Header переключите режим на `Guided Demo`. +Это чистое демо-представление для защиты (1 экран без скролла). + +Отличия Guided Demo от Engineering Dashboard: +- Guided Demo показывает только ключевую информацию для защиты (Proof Card, Narration, Snapshot), скрывая длинные логи и графики. +- Engineering Mode дает полный доступ ко всем метрикам, логам, PID-графику и дереву классификации. + +Рекомендуемый путь: +- Start Guided Demo +- Next +- Oversized +- Round +- Low confidence +- Jam +- Emergency stop + +В Guided Demo закрываются критерии: classification correctness, routing, timing, fault handling. + ## Presentation Mode В Header есть переключатель: diff --git a/docs/DEMO_SCRIPT.md b/docs/DEMO_SCRIPT.md index 5f54139..22c16eb 100644 --- a/docs/DEMO_SCRIPT.md +++ b/docs/DEMO_SCRIPT.md @@ -1,56 +1,28 @@ # Demo Script -Open https://arhipovdan.ru/ and switch Header to `Presentation Mode`. +Open https://arhipovdan.ru/ and switch Header to `Guided Demo`. -## 3-Minute Defense +## 30-Second Pitch -### 0:00-0:25 — System Overview +"We built an engineering simulation of the OZON sorting cell. It models the full loop: detection -> classification -> command -> routing. We cover happy paths to B/C/D, fault handling, and timing. The Guided Demo view shows the live proof." -Demo step: `System overview`. +## 3-Minute Guided Demo -Say: the system models the full loop: detection -> classification -> actuator command -> physical route. +1. Open **Guided Demo**. +2. Click **Start Guided Demo**. +3. Point to the **Current Proof Card** and the **Scene**. + - "Watch how detection leads to classification and routing." +4. Click **Next** to show `Oversized item to C`. + - "Dimensions fail first, priority C." +5. Click **Next** to show `Round object to D`. + - "Dimensions pass, but roundness triggers D." +6. Click **Next** through `Boundary`, `Low confidence`, `Jam`, `Emergency stop`. + - On faults: click "Apply fault scenario" to confirm. + - "The line stops safely on emergency." -Watch: engineering SVG scene, sensors, stop-gate, pushers and B/C/D routes. +## 5-Minute Guided Demo -### 0:25-0:55 — Normal Item To B - -Demo step: `Normal item to B`. - -Apply scenario, use `Run suggested action` or `Step state`. - -Say: dimensions pass, roundness is below threshold, category B opens the gate and sends the item straight. - -### 0:55-1:25 — Oversized To C - -Demo step: `Oversized item to C`. - -Say: dimensions fail first, so C has priority even before shape routing. - -Watch: decision tree FAIL, `ROUTE_TO_C`, lower roll-cage. - -### 1:25-1:55 — Round Object To D - -Demo step: `Round object to D`. - -Say: dimensions pass, but roundness >= 0.8, so the item goes to D for repack/shape issue. - -### 1:55-2:25 — Fault And Safety - -Demo step: `Jam / fault handling` or `Emergency stop`. - -With Safe Demo ON, confirm the fault scenario intentionally. - -Say: abnormal states stop conveyor motion and require Reset. - -### 2:25-3:00 — Criteria Coverage - -Demo step: `Performance and synchronization`, then focus criteria panel. - -Say: this checklist maps every OZON criterion to scenario, component and doc evidence. - -## 5-Minute Defense - -Use all 10 demo steps: +Use all 10 demo steps in Guided Demo View: 1. System overview. 2. Normal item to B. @@ -65,11 +37,20 @@ Use all 10 demo steps: For each step: -1. Read `What to watch`. -2. Apply recommended scenario. -3. Use `Step state` or `Run suggested action`. -4. Point to the highlighted focus area. -5. Close with the presenter phrase. +1. Read the **Demo Narration Card**. +2. Use **Next** or the **Primary Action Button**. +3. Point to the highlighted values in the **Proof Card**. +4. Read the **Presenter Phrase** from the screen. + +## What to say on each step + +Use the `presenterPhrase` text shown in the **Demo Narration Card**. It is specifically written to be short and clear for the jury. + +## What to do if demo gets stuck + +- Click **Reset Demo**. +- If still stuck, switch back to **Engineering Mode** and use manual scenario selection. +- Proceed manually. ## Fallback If Domain Does Not Open diff --git a/docs/JURY_QA.md b/docs/JURY_QA.md index aa8d304..67cb368 100644 --- a/docs/JURY_QA.md +++ b/docs/JURY_QA.md @@ -59,3 +59,19 @@ The stop-gate fixes the item. B opens the gate, C extends pusher C, D extends pu ## 15. What Happens On Jam? The system enters FAULT, conveyor target speed becomes 0, actual speed decays toward 0, and Reset is required. + +## 16. Why is the interface divided into Guided and Engineering? + +Guided Demo is focused solely on the defense presentation, showing only the Proof Card, Scene, and Narrative, ensuring clarity in a 3-minute pitch. Engineering Mode provides full access to logs, PID, and internal state. + +## 17. How does Guided Demo prove engineering realism? + +Even in Guided Demo, the scene and proof card are driven by the real underlying state machine, classifier, and metric engine—not a pre-rendered video. + +## 18. Where to see the full event log and PID? + +In the Engineering Dashboard. + +## 19. Why aren't all panels shown in Guided Demo? + +To avoid cognitive overload during the pitch. The jury needs to understand the decision and see the route clearly without distraction from raw JSON logs or PID graphs. diff --git a/docs/SUBMISSION_CHECKLIST.md b/docs/SUBMISSION_CHECKLIST.md index 8fe232c..251a535 100644 --- a/docs/SUBMISSION_CHECKLIST.md +++ b/docs/SUBMISSION_CHECKLIST.md @@ -11,6 +11,13 @@ Before final submission: - [ ] Presentation Mode opens. - [ ] Demo steps checked. - [ ] Safe Demo confirmation checked for jam/emergency. +- [ ] Guided Demo verified on 1920x1080 (no scroll). +- [ ] Guided Demo verified on 1440x900. +- [ ] Start Guided Demo button works. +- [ ] Next/Previous buttons work. +- [ ] Reset Demo button works. +- [ ] Fault confirmation works in Guided Demo. +- [ ] Engineering Mode is not broken. - [ ] OZON Criteria Coverage Panel checked. - [ ] Engineering Mode still works. - [ ] Scenarios checked: normal_flow, oversized_item, round_object, boundary_dimensions, close_items, low_confidence, jam, emergency_stop. diff --git a/src/App.tsx b/src/App.tsx index 4f9d6d7..3fd87e7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,6 +5,8 @@ import { SCENARIOS } from './data/scenarios'; import { createSimulation, setRunning, stepSimulation, stepSimulationToNextState } from './domain/simulation'; import type { DemoStep, PresentationMode, ScenarioId, SimulationState } from './domain/types'; +import GuidedDemoView from './components/GuidedDemoView'; + const TICK_MS = 250; function isFaultDemoStep(step: DemoStep): boolean { @@ -13,7 +15,7 @@ function isFaultDemoStep(step: DemoStep): boolean { export default function App() { const [activeScenarioId, setActiveScenarioId] = useState('normal_flow'); - const [presentationMode, setPresentationMode] = useState('engineering'); + const [presentationMode, setPresentationMode] = useState('guided'); const [safeDemoEnabled, setSafeDemoEnabled] = useState(true); const [demoStepIndex, setDemoStepIndex] = useState(0); const [pendingFaultStepId, setPendingFaultStepId] = useState(); @@ -87,7 +89,30 @@ export default function App() { setActiveScenarioId(DEMO_STEPS[0].scenarioId); }; - return ( + return presentationMode === 'guided' ? ( + + ) : ( +
+

Awaiting cycle

+

Ready to demonstrate sorting cycle

+

The demo will show detection, classification, control command and routing to B/C/D.

+
+ Detection + → + Classification + → + Command + → + Actuator + → + Route + → + Log +
+

Click Start Guided Demo or Next to begin.

+
+ + ); + } + + return ( +
+
+

Current Item Proof

+

{item?.name} (ID: {item?.id})

+
+
+
+ Dimensions + + {dimensions?.width} x {dimensions?.depth} x {dimensions?.height} mm + +
+
+ Roundness + + {item?.roundness.toFixed(2)} + +
+
+ Confidence + + {(item?.confidence ?? 0 * 100).toFixed(0)}% + +
+
+ Category + {result?.category} +
+
+ Command + {simulation.machineState.startsWith('ROUTE_TO_') ? simulation.machineState : 'WAITING'} +
+
+ Target zone + {result?.category ? `Zone ${result.category}` : '-'} +
+
+
+ Decision reason: {result?.reason} + {lowConfidence &&

Low CV confidence — rule-based fallback used

} + {simulation.machineState === 'FAULT' &&

FAULT: Conveyor stopped, reset required

} + {simulation.machineState === 'EMERGENCY_STOP' &&

EMERGENCY_STOP: All actuators stopped

} + {simulation.metrics.queueLength > 1 &&

Spacing warning: sequential processing enforced

} +
+
+ Detection + → + Classification + → + Command + → + Actuator + → + Route + → + Log +
+
+ ); +} \ No newline at end of file diff --git a/src/components/GuidedDemoView.tsx b/src/components/GuidedDemoView.tsx new file mode 100644 index 0000000..2f34f91 --- /dev/null +++ b/src/components/GuidedDemoView.tsx @@ -0,0 +1,182 @@ +import { useEffect } from 'react'; +import SorterScene from './SorterScene'; +import CurrentProofCard from './CurrentProofCard'; +import { OZON_CRITERIA } from '../data/criteria'; +import type { DemoStep, Scenario, ScenarioId, SimulationState, PresentationMode } from '../domain/types'; + +interface GuidedDemoViewProps { + simulation: SimulationState; + scenarios: Scenario[]; + activeScenarioId: ScenarioId; + presentationMode: PresentationMode; + safeDemoEnabled: boolean; + demoSteps: DemoStep[]; + demoStepIndex: number; + currentDemoStep: DemoStep; + pendingFaultStepId?: string; + onStart: () => void; + onPause: () => void; + onReset: () => void; + onStep: () => void; + onScenarioChange: (scenarioId: ScenarioId) => void; + onPresentationModeChange: (mode: any) => void; + onSafeDemoChange: (enabled: boolean) => void; + onDemoStepChange: (index: number) => void; + onApplyDemoStepScenario: (step: DemoStep) => void; + onRunPreferredAction: (step: DemoStep) => void; + onRestartDemo: () => void; +} + +export default function GuidedDemoView({ + simulation, + scenarios, + activeScenarioId, + demoSteps, + demoStepIndex, + currentDemoStep, + pendingFaultStepId, + safeDemoEnabled, + onStart, + onPause, + onReset, + onStep, + onPresentationModeChange, + onDemoStepChange, + onApplyDemoStepScenario, + onRunPreferredAction, + onRestartDemo, +}: GuidedDemoViewProps) { + const isFaultStep = currentDemoStep.scenarioId === 'jam' || currentDemoStep.scenarioId === 'emergency_stop'; + const pendingFaultConfirmation = pendingFaultStepId === currentDemoStep.id; + + const handleStartDemo = () => { + if (!simulation.currentItem && demoStepIndex === 0 && !simulation.running) { + onRunPreferredAction(currentDemoStep); + } else if (demoStepIndex === 0 && simulation.running) { + onRestartDemo(); + } else { + onRunPreferredAction(currentDemoStep); + } + }; + + const handleNext = () => { + onDemoStepChange(demoStepIndex + 1); + }; + + const handlePrevious = () => { + onDemoStepChange(demoStepIndex - 1); + }; + + const relatedCriteriaList = OZON_CRITERIA.filter(c => currentDemoStep.relatedCriteria.includes(c.id)); + + return ( +
+ {/* 1. Top Demo Header */} +
+
+

Guided Sorting Demo

+

Step {demoStepIndex + 1} / {demoSteps.length}: {currentDemoStep.title}

+
+
+
+ + {simulation.systemStatus} +
+
Scenario: {simulation.scenario.name}
+
+
+ +
+ + +
+ + +
+
+ + {/* Main Grid */} +
+ {/* 2. Large Scene Area */} +
+
+ +
+
+ Cycle Time + {simulation.metrics.avgCycleTimeMs} ms +
+
+ CV Latency + {simulation.metrics.cvLatencyMs} ms +
+
+ Actuator Latency + {simulation.metrics.actuatorLatencyMs} ms +
+
+ Throughput + {simulation.metrics.throughputItemsPerMin} items/min +
+
+
+
+ + {/* Right Column */} + +
+
+ ); +} \ No newline at end of file diff --git a/src/components/Header.tsx b/src/components/Header.tsx index f62af76..65a7828 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -34,7 +34,8 @@ export default function Header({
- + +
{presentationMode === 'presentation' ? (