From 446829c8ec2a2eda269b4c3a68cd2dcc6dab5bdc Mon Sep 17 00:00:00 2001 From: Joeri Exelmans Date: Fri, 10 Oct 2025 14:37:35 +0200 Subject: [PATCH] timestamps visible in execution history --- src/App/App.tsx | 58 ++++++++++++++++++------------- src/VisualEditor/interpreter.ts | 8 ++--- src/VisualEditor/runtime_types.ts | 12 +++++-- src/VisualEditor/time.ts | 8 ++++- 4 files changed, 55 insertions(+), 31 deletions(-) diff --git a/src/App/App.tsx b/src/App/App.tsx index f37d420..76f0c6f 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import { ConcreteState, emptyStatechart, isAncestorOf, Statechart, stateDescription, Transition } from "../VisualEditor/ast"; import { VisualEditor } from "../VisualEditor/VisualEditor"; -import { Environment, Mode, RT_Statechart } from "../VisualEditor/runtime_types"; +import { BigStepOutput, Environment, Mode, RT_Statechart } from "../VisualEditor/runtime_types"; import { initialize, handleEvent, handleInputEvent } from "../VisualEditor/interpreter"; import { Action, Expression } from "../VisualEditor/label_ast"; @@ -67,11 +67,18 @@ export function AST(props: {root: ConcreteState, transitions: Map(emptyStatechart); const [errors, setErrors] = useState<[string,string][]>([]); - - const [rt, setRT] = useState([]); + + const [rt, setRT] = useState([]); const [rtIdx, setRTIdx] = useState(null); const [time, setTime] = useState({kind: "paused", simtime: 0}); @@ -80,23 +87,24 @@ export function App() { function restart() { - const rt = initialize(ast); + const config = initialize(ast); console.log('runtime: ', rt); - setRT([rt]); + setRT([{inputEvent: null, simtime: 0, ...config}]); setRTIdx(0); - setTime({kind: "paused", simtime: 0}); } function stop() { setRT([]); setRTIdx(null); + setTime({kind: "paused", simtime: 0}); } - function raise(event: string) { + function raise(inputEvent: string) { console.log(rtIdx); - if (rt.length>0 && rtIdx!==null && ast.inputEvents.has(event)) { - const nextConfig = handleInputEvent(event, ast, rt[rtIdx]!); - setRT([...rt.slice(0, rtIdx+1), nextConfig]); + if (rt.length>0 && rtIdx!==null && ast.inputEvents.has(inputEvent)) { + const simtime = getSimTime(time, performance.now()); + const nextConfig = handleInputEvent(inputEvent, ast, rt[rtIdx]!); + setRT([...rt.slice(0, rtIdx+1), {inputEvent, simtime, ...nextConfig}]); setRTIdx(rtIdx+1); } } @@ -104,10 +112,7 @@ export function App() { function updateDisplayedTime() { const now = performance.now(); const timeMs = getSimTime(time, now); - const leadingZeros = "00" + timeMs % 1000; - const formatted = `${Math.floor(timeMs / 1000)}.${(leadingZeros).substring(leadingZeros.length-3)}`; - // console.log(now, timeMs, formatted); - setDisplayTime(formatted); + setDisplayTime(formatTime(timeMs)); } useEffect(() => { @@ -148,6 +153,11 @@ export function App() { } }) } + + function gotoRt(idx: number, timestamp: number) { + setRTIdx(idx); + setTime({kind: "paused", simtime: timestamp}); + } return
@@ -155,7 +165,7 @@ export function App() {
- +   raise  {[...ast.inputEvents].map(event => )} @@ -168,12 +178,8 @@ export function App() {   onTimeScaleChange(e.target.value, performance.now())}/>   - {rtIdx !== null && - <> -   - - - } +   +
@@ -181,9 +187,13 @@ export function App() {
@@ -198,7 +208,7 @@ function ShowEnvironment(props: {environment: Environment}) { function ShowMode(props: {mode: Mode, statechart: Statechart}) { const activeLeafs = getActiveLeafs(props.mode, props.statechart); - return
{[...activeLeafs].map(uid => + return
mode: {[...activeLeafs].map(uid => stateDescription(props.statechart.uid2State.get(uid)!)).join(", ")}
; } diff --git a/src/VisualEditor/interpreter.ts b/src/VisualEditor/interpreter.ts index 878bdbb..3f35f19 100644 --- a/src/VisualEditor/interpreter.ts +++ b/src/VisualEditor/interpreter.ts @@ -1,9 +1,9 @@ import { evalExpr } from "./actionlang_interpreter"; import { computeArena, ConcreteState, getDescendants, isAncestorOf, isOverlapping, OrState, Statechart, stateDescription, Transition } from "./ast"; import { Action } from "./label_ast"; -import { Environment, RaisedEvents, Mode, RT_Statechart, initialRaised, BigStep } from "./runtime_types"; +import { Environment, RaisedEvents, Mode, RT_Statechart, initialRaised, BigStepOutput } from "./runtime_types"; -export function initialize(ast: Statechart): BigStep { +export function initialize(ast: Statechart): BigStepOutput { let {enteredStates, environment, ...raised} = enterDefault(ast.root, { environment: new Map(), ...initialRaised, @@ -204,7 +204,7 @@ export function handleEvent(event: string, statechart: Statechart, activeParent: return {environment, mode, ...raised}; } -export function handleInputEvent(event: string, statechart: Statechart, {mode, environment}: {mode: Mode, environment: Environment}): BigStep { +export function handleInputEvent(event: string, statechart: Statechart, {mode, environment}: {mode: Mode, environment: Environment}): BigStepOutput { let raised = initialRaised; ({mode, environment, ...raised} = handleEvent(event, statechart, statechart.root, {mode, environment, ...raised})); @@ -212,7 +212,7 @@ export function handleInputEvent(event: string, statechart: Statechart, {mode, e return handleInternalEvents(statechart, {mode, environment, ...raised}); } -export function handleInternalEvents(statechart: Statechart, {mode, environment, ...raised}: RT_Statechart & RaisedEvents): BigStep { +export function handleInternalEvents(statechart: Statechart, {mode, environment, ...raised}: RT_Statechart & RaisedEvents): BigStepOutput { while (raised.internalEvents.length > 0) { const [internalEvent, ...rest] = raised.internalEvents; ({mode, environment, ...raised} = handleEvent(internalEvent, statechart, statechart.root, {mode, environment, internalEvents: rest, outputEvents: raised.outputEvents})); diff --git a/src/VisualEditor/runtime_types.ts b/src/VisualEditor/runtime_types.ts index 20a6907..057d973 100644 --- a/src/VisualEditor/runtime_types.ts +++ b/src/VisualEditor/runtime_types.ts @@ -12,7 +12,15 @@ export type RT_Statechart = { // history: // TODO } -export type BigStep = RT_Statechart & {outputEvents: string[]}; +export type BigStepOutput = RT_Statechart & { + outputEvents: string[], +}; + +export type BigStep = { + inputEvent: string | null, // null if initialization + simtime: number, +} & BigStepOutput; + export type RaisedEvents = { internalEvents: string[]; @@ -24,4 +32,4 @@ export type Timers = Map; // transition uid -> timestamp export const initialRaised: RaisedEvents = { internalEvents: [], outputEvents: [], -} +}; diff --git a/src/VisualEditor/time.ts b/src/VisualEditor/time.ts index 76a3c5f..46b1ace 100644 --- a/src/VisualEditor/time.ts +++ b/src/VisualEditor/time.ts @@ -1,18 +1,24 @@ export type TimeMode = TimePaused | TimeRealTime; +// When the simulation is paused, we only need to know the current simulated time. export type TimePaused = { kind: "paused", simtime: number, // the current simulated time } +// When the simulation is running in real time, we need to know the time when the simulation was set to real time (both in simulated and wall-clock time), and the time scale. This allows us to compute the simulated time of every future event. +// Such a 'future event' may be: +// - raising an input event +// - changing of the time scale parameter +// - pausing the simulation export type TimeRealTime = { kind: "realtime", - scale: number, // time scale relative to wall-clock time since: { simtime: number, // the simulated time at which the time was set to realtime wallclktime: number, // the wall-clock time at which the time was set to realtime } + scale: number, // time scale relative to wall-clock time } export function getSimTime(currentMode: TimeMode, wallclktime: number): number {