fix: sometimes entering default state would not highlight initial state marker
This commit is contained in:
parent
22fbe70a60
commit
373e26dc1b
5 changed files with 44 additions and 28 deletions
|
|
@ -1,8 +1,9 @@
|
||||||
details.active {
|
details.active {
|
||||||
/* background-color: rgba(128, 72, 0, 0.855);
|
/* background-color: rgba(128, 72, 0, 0.855);
|
||||||
color: white; */
|
color: white; */
|
||||||
|
border: rgb(192, 125, 0);
|
||||||
box-shadow: rgba(128, 72, 0, 0.856) 0 0 8;
|
background-color:rgb(255, 251, 244);
|
||||||
|
filter: drop-shadow( 0px 0px 3px rgba(192, 125, 0, 0.856));
|
||||||
}
|
}
|
||||||
|
|
||||||
details {
|
details {
|
||||||
|
|
@ -11,6 +12,8 @@ details {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
padding-right: 2px;
|
padding-right: 2px;
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
color: black;
|
color: black;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|
|
||||||
|
|
@ -105,10 +105,12 @@ export function App() {
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const highlightActive = (rtIdx !== undefined) && new Set([...rt[rtIdx].mode].filter(uid => {
|
// const highlightActive = (rtIdx !== undefined) && new Set([...rt[rtIdx].mode].filter(uid => {
|
||||||
const state = ast.uid2State.get(uid);
|
// const state = ast.uid2State.get(uid);
|
||||||
return state && state.parent?.kind !== "and";
|
// return state && state.parent?.kind !== "and";
|
||||||
})) || new Set();
|
// })) || new Set();
|
||||||
|
|
||||||
|
const highlightActive = (rtIdx === undefined) ? new Set() : rt[rtIdx].mode;
|
||||||
|
|
||||||
const highlightTransitions = (rtIdx === undefined) ? [] : rt[rtIdx].firedTransitions;
|
const highlightTransitions = (rtIdx === undefined) ? [] : rt[rtIdx].firedTransitions;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { getSimTime, setPaused, setRealtime, TimeMode } from "../statecharts/tim
|
||||||
import { Statechart } from "../statecharts/abstract_syntax";
|
import { Statechart } from "../statecharts/abstract_syntax";
|
||||||
|
|
||||||
import CachedIcon from '@mui/icons-material/Cached';
|
import CachedIcon from '@mui/icons-material/Cached';
|
||||||
import ClearIcon from '@mui/icons-material/Clear';
|
|
||||||
import PauseIcon from '@mui/icons-material/Pause';
|
import PauseIcon from '@mui/icons-material/Pause';
|
||||||
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||||
import BoltIcon from '@mui/icons-material/Bolt';
|
import BoltIcon from '@mui/icons-material/Bolt';
|
||||||
|
|
@ -63,6 +62,19 @@ export function TopPanel({rt, time, setTime, onInit, onClear, onRaise, ast, mode
|
||||||
const [displayTime, setDisplayTime] = useState("0.000");
|
const [displayTime, setDisplayTime] = useState("0.000");
|
||||||
const [timescale, setTimescale] = useState(1);
|
const [timescale, setTimescale] = useState(1);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === " ") {
|
||||||
|
e.preventDefault();
|
||||||
|
onChangePaused(time.kind !== "paused", performance.now());
|
||||||
|
};
|
||||||
|
};
|
||||||
|
window.addEventListener("keydown", onKeyDown);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("keydown", onKeyDown);
|
||||||
|
};
|
||||||
|
}, [time]);
|
||||||
|
|
||||||
function updateDisplayedTime() {
|
function updateDisplayedTime() {
|
||||||
const now = performance.now();
|
const now = performance.now();
|
||||||
const timeMs = getSimTime(time, now);
|
const timeMs = getSimTime(time, now);
|
||||||
|
|
@ -81,7 +93,7 @@ export function TopPanel({rt, time, setTime, onInit, onClear, onRaise, ast, mode
|
||||||
function onChangePaused(paused: boolean, wallclktime: number) {
|
function onChangePaused(paused: boolean, wallclktime: number) {
|
||||||
setTime(time => {
|
setTime(time => {
|
||||||
if (paused) {
|
if (paused) {
|
||||||
return setPaused(time, performance.now());
|
return setPaused(time, wallclktime);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return setRealtime(time, timescale, wallclktime);
|
return setRealtime(time, timescale, wallclktime);
|
||||||
|
|
|
||||||
|
|
@ -786,7 +786,7 @@ export function VisualEditor({ast, setAST, rt, errors, setErrors, mode, highligh
|
||||||
highlight={arrowsToHighlight.hasOwnProperty(arrow.uid)}
|
highlight={arrowsToHighlight.hasOwnProperty(arrow.uid)}
|
||||||
fired={highlightTransitions.includes(arrow.uid)}
|
fired={highlightTransitions.includes(arrow.uid)}
|
||||||
arc={arc}
|
arc={arc}
|
||||||
initialMarker={initialMarker}
|
initialMarker={Boolean(initialMarker)}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@ import { BigStepOutput, Environment, initialRaised, Mode, RaisedEvents, RT_Event
|
||||||
|
|
||||||
export function initialize(ast: Statechart): BigStepOutput {
|
export function initialize(ast: Statechart): BigStepOutput {
|
||||||
let history = new Map();
|
let history = new Map();
|
||||||
let enteredStates, environment, raised;
|
let enteredStates, environment, rest;
|
||||||
({enteredStates, environment, history, ...raised} = enterDefault(0, ast.root, {
|
({enteredStates, environment, history, ...rest} = enterDefault(0, ast.root, {
|
||||||
environment: new Environment([new Map([["_timers", []]])]),
|
environment: new Environment([new Map([["_timers", []]])]),
|
||||||
history,
|
history,
|
||||||
...initialRaised,
|
...initialRaised,
|
||||||
}));
|
}));
|
||||||
return handleInternalEvents(0, ast, {mode: enteredStates, environment, history, ...raised});
|
return handleInternalEvents(0, ast, {mode: enteredStates, environment, history, ...rest});
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActionScope = {
|
type ActionScope = {
|
||||||
|
|
@ -121,7 +121,9 @@ export function enterDefault(simtime: number, state: ConcreteState, rt: ActionSc
|
||||||
({enteredStates: enteredChildren, firedTransitions, ...actionScope} = enterDefault(simtime, toEnter, {firedTransitions, ...actionScope}));
|
({enteredStates: enteredChildren, firedTransitions, ...actionScope} = enterDefault(simtime, toEnter, {firedTransitions, ...actionScope}));
|
||||||
enteredStates = enteredStates.union(enteredChildren);
|
enteredStates = enteredStates.union(enteredChildren);
|
||||||
}
|
}
|
||||||
// console.warn(state.uid + ': no initial state');
|
else {
|
||||||
|
console.warn(state.uid + ': no initial state');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {enteredStates, firedTransitions, ...actionScope};
|
return {enteredStates, firedTransitions, ...actionScope};
|
||||||
|
|
@ -155,12 +157,7 @@ export function enterStates(simtime: number, state: ConcreteState, toEnter: Set<
|
||||||
}
|
}
|
||||||
else if (childToEnter.length === 0) {
|
else if (childToEnter.length === 0) {
|
||||||
// also good, enter default child
|
// also good, enter default child
|
||||||
for (const [_, defaultChild] of state.initial) {
|
return enterDefault(simtime, state, {...actionScope});
|
||||||
let enteredChildren;
|
|
||||||
({enteredStates: enteredChildren, ...actionScope} = enterDefault(simtime, defaultChild, actionScope));
|
|
||||||
enteredStates = enteredStates.union(enteredChildren);
|
|
||||||
break; // one is enough
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new Error("can only enter one child of an OR-state, stupid!");
|
throw new Error("can only enter one child of an OR-state, stupid!");
|
||||||
|
|
@ -184,6 +181,7 @@ export function exitCurrent(simtime: number, state: ConcreteState, rt: EnteredSc
|
||||||
({history, ...actionScope} = exitActions(simtime, state, {enteredStates, history, ...actionScope}));
|
({history, ...actionScope} = exitActions(simtime, state, {enteredStates, history, ...actionScope}));
|
||||||
|
|
||||||
// record history
|
// record history
|
||||||
|
history = new Map(history); // defensive copy
|
||||||
for (const h of state.history) {
|
for (const h of state.history) {
|
||||||
if (h.kind === "shallow") {
|
if (h.kind === "shallow") {
|
||||||
history.set(h.uid, new Set(state.children
|
history.set(h.uid, new Set(state.children
|
||||||
|
|
@ -204,7 +202,7 @@ export function exitCurrent(simtime: number, state: ConcreteState, rt: EnteredSc
|
||||||
return {history, ...actionScope};
|
return {history, ...actionScope};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleEvent(simtime: number, event: RT_Event, statechart: Statechart, activeParent: StableState, {environment, mode, history, ...raised}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
export function handleEvent(simtime: number, event: RT_Event, statechart: Statechart, activeParent: StableState, {environment, mode, ...rest}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
||||||
const arenasFired = new Set<OrState>();
|
const arenasFired = new Set<OrState>();
|
||||||
for (const state of activeParent.children) {
|
for (const state of activeParent.children) {
|
||||||
if (mode.has(state.uid)) {
|
if (mode.has(state.uid)) {
|
||||||
|
|
@ -256,7 +254,7 @@ export function handleEvent(simtime: number, event: RT_Event, statechart: Statec
|
||||||
event.param,
|
event.param,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
({mode, environment, history, ...raised} = fireTransition(simtime, t, statechart.transitions, l, arena, {mode, environment, history, ...raised}));
|
({mode, environment, ...rest} = fireTransition(simtime, t, statechart.transitions, l, arena, {mode, environment, ...rest}));
|
||||||
if (event.kind === "input" && event.param !== undefined) {
|
if (event.kind === "input" && event.param !== undefined) {
|
||||||
environment = environment.popScope();
|
environment = environment.popScope();
|
||||||
}
|
}
|
||||||
|
|
@ -268,11 +266,12 @@ export function handleEvent(simtime: number, event: RT_Event, statechart: Statec
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// no enabled outgoing transitions, try the children:
|
// no enabled outgoing transitions, try the children:
|
||||||
({environment, mode, history, ...raised} = handleEvent(simtime, event, statechart, state, {environment, mode, history, ...raised}));
|
({environment, mode, ...rest} = handleEvent(simtime, event, statechart, state,
|
||||||
|
{environment, mode, ...rest}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {environment, mode, history, ...raised};
|
return {environment, mode, ...rest};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleInputEvent(simtime: number, event: RT_Event, statechart: Statechart, {mode, environment, history}: {mode: Mode, environment: Environment, history: RT_History}): BigStepOutput {
|
export function handleInputEvent(simtime: number, event: RT_Event, statechart: Statechart, {mode, environment, history}: {mode: Mode, environment: Environment, history: RT_History}): BigStepOutput {
|
||||||
|
|
@ -293,20 +292,20 @@ export function handleInternalEvents(simtime: number, statechart: Statechart, {i
|
||||||
return rest;
|
return rest;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fireTransition(simtime: number, t: Transition, ts: Map<string, Transition[]>, label: TransitionLabel, arena: OrState, {mode, environment, history, ...raised}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
export function fireTransition(simtime: number, t: Transition, ts: Map<string, Transition[]>, label: TransitionLabel, arena: OrState, {mode, environment, history, ...rest}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
||||||
console.log('fire', transitionDescription(t));
|
console.log('fire', transitionDescription(t));
|
||||||
|
|
||||||
const srcPath = computePath({ancestor: arena, descendant: t.src as ConcreteState}).reverse() as ConcreteState[];
|
const srcPath = computePath({ancestor: arena, descendant: t.src as ConcreteState}).reverse() as ConcreteState[];
|
||||||
|
|
||||||
// exit src and other states up to arena
|
// exit src and other states up to arena
|
||||||
({environment, history, ...raised} = exitCurrent(simtime, srcPath[0], {environment, enteredStates: mode, history, ...raised}))
|
({environment, history, ...rest} = exitCurrent(simtime, srcPath[0], {environment, enteredStates: mode, history, ...rest}))
|
||||||
const toExit = getDescendants(arena);
|
const toExit = getDescendants(arena);
|
||||||
toExit.delete(arena.uid); // do not exit the arena itself
|
toExit.delete(arena.uid); // do not exit the arena itself
|
||||||
const exitedMode = mode.difference(toExit); // active states after exiting the states we need to exit
|
const exitedMode = mode.difference(toExit); // active states after exiting the states we need to exit
|
||||||
|
|
||||||
// console.log({exitedMode});
|
// console.log({exitedMode});
|
||||||
|
|
||||||
return fireSecondHalfOfTransition(simtime, t, ts, label, arena, {mode: exitedMode, history, environment, ...raised});
|
return fireSecondHalfOfTransition(simtime, t, ts, label, arena, {mode: exitedMode, history, environment, ...rest});
|
||||||
}
|
}
|
||||||
|
|
||||||
// assuming we've already exited the source state of the transition, now enter the target state
|
// assuming we've already exited the source state of the transition, now enter the target state
|
||||||
|
|
@ -350,7 +349,7 @@ export function fireSecondHalfOfTransition(simtime: number, t: Transition, ts: M
|
||||||
|
|
||||||
// enter tgt
|
// enter tgt
|
||||||
let enteredStates;
|
let enteredStates;
|
||||||
({enteredStates, environment, history, ...raised} = enterStates(simtime, state, toEnter, {environment, history, firedTransitions, ...raised}));
|
({enteredStates, environment, history, firedTransitions, ...raised} = enterStates(simtime, state, toEnter, {environment, history, firedTransitions, ...raised}));
|
||||||
const enteredMode = mode.union(enteredStates);
|
const enteredMode = mode.union(enteredStates);
|
||||||
|
|
||||||
// console.log({enteredMode});
|
// console.log({enteredMode});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue