fix broken pseudo-states (they work more like regular transitions now)
This commit is contained in:
parent
3e192f8e26
commit
43e3b2117c
15 changed files with 1038 additions and 346 deletions
|
|
@ -1,16 +1,21 @@
|
|||
import { computeArena2, computePath, ConcreteState, getDescendants, HistoryState, isOverlapping, OrState, StableState, Statechart, stateDescription, Transition, transitionDescription } from "./abstract_syntax";
|
||||
import { AbstractState, computeArena, computePath, ConcreteState, getDescendants, HistoryState, isOverlapping, OrState, StableState, Statechart, stateDescription, Transition, transitionDescription, TransitionSrcTgt } from "./abstract_syntax";
|
||||
import { evalExpr } from "./actionlang_interpreter";
|
||||
import { Environment, FlatEnvironment, ScopedEnvironment } from "./environment";
|
||||
import { Action, EventTrigger, TransitionLabel } from "./label_ast";
|
||||
import { BigStepOutput, Environment, initialRaised, Mode, RaisedEvents, RT_Event, RT_History, RT_Statechart, TimerElapseEvent, Timers } from "./runtime_types";
|
||||
import { BigStepOutput, initialRaised, Mode, RaisedEvents, RT_Event, RT_History, RT_Statechart, TimerElapseEvent, Timers, InputEvent } from "./runtime_types";
|
||||
|
||||
const initialEnv = new Map<string, any>([
|
||||
["_timers", []],
|
||||
["_log", (str: string) => console.log(str)],
|
||||
]);
|
||||
const initialScopedEnvironment = new ScopedEnvironment({env: initialEnv, children: {}});
|
||||
// const intiialFlatEnvironment = new FlatEnvironment(initialEnv);
|
||||
|
||||
export function initialize(ast: Statechart): BigStepOutput {
|
||||
let history = new Map();
|
||||
let enteredStates, environment, rest;
|
||||
({enteredStates, environment, history, ...rest} = enterDefault(0, ast.root, {
|
||||
environment: new Environment([new Map<string, any>([
|
||||
["_timers", []],
|
||||
["_log", (str: string) => console.log(str)],
|
||||
])]),
|
||||
environment: initialScopedEnvironment,
|
||||
history,
|
||||
...initialRaised,
|
||||
}));
|
||||
|
|
@ -66,42 +71,49 @@ export function execAction(action: Action, rt: ActionScope): ActionScope {
|
|||
throw new Error("should never reach here");
|
||||
}
|
||||
|
||||
export function entryActions(simtime: number, state: ConcreteState, actionScope: ActionScope): ActionScope {
|
||||
// console.log('enter', stateDescription(state), '...');
|
||||
export function entryActions(simtime: number, state: TransitionSrcTgt, actionScope: ActionScope): ActionScope {
|
||||
console.log('enter', stateDescription(state), '...');
|
||||
|
||||
let {environment, ...rest} = actionScope;
|
||||
// environment = environment.pushScope();
|
||||
|
||||
environment = environment.enterScope(state.uid);
|
||||
|
||||
for (const action of state.entryActions) {
|
||||
({environment, ...rest} = execAction(action, {environment, ...rest}));
|
||||
}
|
||||
// schedule timers
|
||||
// we store timers in the environment (dirty!)
|
||||
environment = environment.transform<Timers>("_timers", oldTimers => {
|
||||
if (state.kind !== "pseudo") {
|
||||
// we store timers in the environment (dirty!)
|
||||
const timers: Timers = environment.get("_timers") || [];
|
||||
const newTimers = [
|
||||
...oldTimers,
|
||||
...timers,
|
||||
...state.timers.map(timeOffset => {
|
||||
const futureSimTime = simtime + timeOffset;
|
||||
return [futureSimTime, {kind: "timer", state: state.uid, timeDurMs: timeOffset}] as [number, TimerElapseEvent];
|
||||
}),
|
||||
];
|
||||
newTimers.sort((a,b) => a[0] - b[0]);
|
||||
return newTimers;
|
||||
}, []);
|
||||
// new nested scope
|
||||
newTimers.sort((a,b) => a[0] - b[0]); // earliest timers come first
|
||||
environment = environment.set("_timers", newTimers);
|
||||
}
|
||||
return {environment, ...rest};
|
||||
}
|
||||
|
||||
export function exitActions(simtime: number, state: ConcreteState, {enteredStates, ...actionScope}: EnteredScope): ActionScope {
|
||||
// console.log('exit', stateDescription(state), '...');
|
||||
export function exitActions(simtime: number, state: TransitionSrcTgt, {enteredStates, ...actionScope}: EnteredScope): ActionScope {
|
||||
console.log('exit', stateDescription(state), '...');
|
||||
|
||||
for (const action of state.exitActions) {
|
||||
(actionScope = execAction(action, actionScope));
|
||||
}
|
||||
let environment = actionScope.environment;
|
||||
// cancel timers
|
||||
environment = environment.transform<Timers>("_timers", oldTimers => {
|
||||
// remove all timers of 'state':
|
||||
return oldTimers.filter(([_, {state: s}]) => s !== state.uid);
|
||||
}, []);
|
||||
// environment = environment.popScope();
|
||||
if (state.kind !== "pseudo") {
|
||||
const timers: Timers = environment.get("_timers") || [];
|
||||
const newTimers = timers.filter(([_, {state: s}]) => s !== state.uid);
|
||||
environment = environment.set("_timers", newTimers);
|
||||
}
|
||||
|
||||
environment = environment.exitScope();
|
||||
|
||||
return {...actionScope, environment};
|
||||
}
|
||||
|
||||
|
|
@ -182,32 +194,37 @@ export function enterStates(simtime: number, state: ConcreteState, toEnter: Set<
|
|||
|
||||
// exit the given state and all its active descendants
|
||||
export function exitCurrent(simtime: number, state: ConcreteState, rt: EnteredScope): ActionScope {
|
||||
console.log('exitCurrent', stateDescription(state));
|
||||
let {enteredStates, history, ...actionScope} = rt;
|
||||
|
||||
if (enteredStates.has(state.uid)) {
|
||||
// exit all active children...
|
||||
for (const child of state.children) {
|
||||
({history, ...actionScope} = exitCurrent(simtime, child, {enteredStates, history, ...actionScope}));
|
||||
if (state.children) {
|
||||
for (const child of state.children) {
|
||||
({history, ...actionScope} = exitCurrent(simtime, child, {enteredStates, history, ...actionScope}));
|
||||
}
|
||||
}
|
||||
|
||||
// execute exit actions
|
||||
({history, ...actionScope} = exitActions(simtime, state, {enteredStates, history, ...actionScope}));
|
||||
|
||||
// record history
|
||||
history = new Map(history); // defensive copy
|
||||
for (const h of state.history) {
|
||||
if (h.kind === "shallow") {
|
||||
history.set(h.uid, new Set(state.children
|
||||
.filter(child => enteredStates.has(child.uid))
|
||||
.map(child => child.uid)));
|
||||
}
|
||||
else if (h.kind === "deep") {
|
||||
// horribly inefficient (i don't care)
|
||||
history.set(h.uid,
|
||||
getDescendants(state)
|
||||
.difference(new Set([state.uid]))
|
||||
.intersection(enteredStates)
|
||||
);
|
||||
if (state.history) {
|
||||
history = new Map(history); // defensive copy
|
||||
for (const h of state.history) {
|
||||
if (h.kind === "shallow") {
|
||||
history.set(h.uid, new Set(state.children
|
||||
.filter(child => enteredStates.has(child.uid))
|
||||
.map(child => child.uid)));
|
||||
}
|
||||
else if (h.kind === "deep") {
|
||||
// horribly inefficient (i don't care)
|
||||
history.set(h.uid,
|
||||
getDescendants(state)
|
||||
.difference(new Set([state.uid]))
|
||||
.intersection(enteredStates)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -215,82 +232,106 @@ export function exitCurrent(simtime: number, state: ConcreteState, rt: EnteredSc
|
|||
return {history, ...actionScope};
|
||||
}
|
||||
|
||||
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>();
|
||||
function allowedToFire(arena: OrState, alreadyFiredArenas: OrState[]) {
|
||||
for (const alreadyFired of alreadyFiredArenas) {
|
||||
if (isOverlapping(arena, alreadyFired))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function attemptSrcState(simtime: number, sourceState: AbstractState, event: RT_Event|undefined, statechart: Statechart, {environment, mode, arenasFired, ...rest}: RT_Statechart & RaisedEvents): (RT_Statechart & RaisedEvents) | undefined {
|
||||
console.log('attemptSrcState', stateDescription(sourceState), arenasFired);
|
||||
const outgoing = statechart.transitions.get(sourceState.uid) || [];
|
||||
const labels = outgoing.flatMap(t =>
|
||||
t.label
|
||||
.filter(l => l.kind === "transitionLabel")
|
||||
.map(l => [t,l] as [Transition, TransitionLabel]));
|
||||
let triggered;
|
||||
if (event !== undefined) {
|
||||
if (event.kind === "input") {
|
||||
// get transitions triggered by event
|
||||
triggered = labels.filter(([_t,l]) =>
|
||||
l.trigger.kind === "event" && l.trigger.event === event.name);
|
||||
}
|
||||
else {
|
||||
// get transitions triggered by timeout
|
||||
triggered = labels.filter(([_t,l]) =>
|
||||
l.trigger.kind === "after" && l.trigger.durationMs === event.timeDurMs);
|
||||
}
|
||||
}
|
||||
else {
|
||||
triggered = labels.filter(([_t,l]) => l.trigger.kind === "triggerless");
|
||||
}
|
||||
// eval guard
|
||||
const inState = (stateLabel: string) => {
|
||||
for (const [uid, state] of statechart.uid2State.entries()) {
|
||||
if (stateDescription(state) === stateLabel) {
|
||||
return (mode.has(uid));
|
||||
}
|
||||
}
|
||||
};
|
||||
const guardEnvironment = environment.set("inState", inState);
|
||||
const enabled = triggered.filter(([t,l]) => evalExpr(l.guard, guardEnvironment));
|
||||
if (enabled.length > 0) {
|
||||
if (enabled.length > 1) {
|
||||
throw new NonDeterminismError(`Non-determinism: state '${stateDescription(sourceState)}' has multiple (${enabled.length}) enabled outgoing transitions: ${enabled.map(([t]) => transitionDescription(t)).join(', ')}`, [...enabled.map(([t]) => t.uid), sourceState.uid]);
|
||||
}
|
||||
const [toFire, label] = enabled[0];
|
||||
const arena = computeArena(toFire.src, toFire.tgt);
|
||||
if (allowedToFire(arena, arenasFired)) {
|
||||
environment = environment.enterScope("<transition>");
|
||||
// if there's an event parameter, add it to environment
|
||||
if (event && event.kind === "input" && event.param !== undefined) {
|
||||
const varName = (label.trigger as EventTrigger).paramName as string;
|
||||
environment = environment.set(varName, event.param);
|
||||
}
|
||||
({mode, environment, ...rest} = fire(simtime, toFire, statechart.transitions, label, arena, {mode, environment, ...rest}));
|
||||
rest = {...rest, firedTransitions: [...rest.firedTransitions, toFire.uid]}
|
||||
environment.exitScope();
|
||||
arenasFired = [...arenasFired, arena];
|
||||
|
||||
// if there is any pseudo-state in the modal configuration, immediately fire any enabled outgoing transitions of that state:
|
||||
for (const activeState of mode) {
|
||||
const s = statechart.uid2State.get(activeState);
|
||||
if (s?.kind === "pseudo") {
|
||||
console.log('fire pseudo-state...');
|
||||
const newConfig = attemptSrcState(simtime, s, undefined, statechart, {environment, mode, arenasFired: [], ...rest});
|
||||
if (newConfig === undefined) {
|
||||
throw new RuntimeError("Stuck in choice-state.", [activeState]);
|
||||
}
|
||||
arenasFired = [...arenasFired, ...newConfig.arenasFired];
|
||||
return {...newConfig, arenasFired};
|
||||
}
|
||||
}
|
||||
return {mode, environment, arenasFired, ...rest};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A fair step is a response to one (input|internal) event, where possibly multiple transitions are made as long as their arenas do not overlap. A reasonably accurate and more intuitive explanation is that every orthogonal region is allowed to fire at most one transition.
|
||||
export function fairStep(simtime: number, event: RT_Event, statechart: Statechart, activeParent: StableState, {arenasFired, ...config}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
||||
console.log('fairStep', arenasFired);
|
||||
for (const state of activeParent.children) {
|
||||
if (mode.has(state.uid)) {
|
||||
const outgoing = statechart.transitions.get(state.uid) || [];
|
||||
const labels = outgoing.flatMap(t =>
|
||||
t.label
|
||||
.filter(l => l.kind === "transitionLabel")
|
||||
.map(l => [t,l] as [Transition, TransitionLabel]));
|
||||
let triggered;
|
||||
if (event.kind === "input") {
|
||||
// get transitions triggered by event
|
||||
triggered = labels.filter(([_t,l]) =>
|
||||
l.trigger.kind === "event" && l.trigger.event === event.name);
|
||||
}
|
||||
else {
|
||||
// get transitions triggered by timeout
|
||||
triggered = labels.filter(([_t,l]) =>
|
||||
l.trigger.kind === "after" && l.trigger.durationMs === event.timeDurMs);
|
||||
}
|
||||
// eval guard
|
||||
const guardEnvironment = environment.set("inState", (stateLabel: string) => {
|
||||
for (const [uid, state] of statechart.uid2State.entries()) {
|
||||
if (stateDescription(state) === stateLabel) {
|
||||
return (mode.has(uid));
|
||||
}
|
||||
}
|
||||
});
|
||||
const enabled = triggered.filter(([t,l]) =>
|
||||
evalExpr(l.guard, guardEnvironment));
|
||||
if (enabled.length > 0) {
|
||||
if (enabled.length > 1) {
|
||||
throw new NonDeterminismError(`Non-determinism: state '${stateDescription(state)}' has multiple (${enabled.length}) enabled outgoing transitions: ${enabled.map(([t]) => transitionDescription(t)).join(', ')}`, [...enabled.map(([t]) => t.uid), state.uid]);
|
||||
}
|
||||
const [t,l] = enabled[0]; // just pick one transition
|
||||
const arena = computeArena2(t, statechart.transitions);
|
||||
let overlapping = false;
|
||||
for (const alreadyFired of arenasFired) {
|
||||
if (isOverlapping(arena, alreadyFired)) {
|
||||
overlapping = true;
|
||||
}
|
||||
}
|
||||
if (!overlapping) {
|
||||
if (event.kind === "input" && event.param !== undefined) {
|
||||
// input events may have a parameter
|
||||
// add event parameter to environment in new scope
|
||||
environment = environment.pushScope();
|
||||
environment = environment.newVar(
|
||||
(l.trigger as EventTrigger).paramName as string,
|
||||
event.param,
|
||||
);
|
||||
}
|
||||
({mode, environment, ...rest} = fireTransition(simtime, t, statechart.transitions, l, arena, {mode, environment, ...rest}));
|
||||
if (event.kind === "input" && event.param !== undefined) {
|
||||
environment = environment.popScope();
|
||||
}
|
||||
arenasFired.add(arena);
|
||||
}
|
||||
else {
|
||||
// console.log('skip (overlapping arenas)');
|
||||
}
|
||||
if (config.mode.has(state.uid)) {
|
||||
const didFire = attemptSrcState(simtime, state, event, statechart, {...config, arenasFired});
|
||||
if (didFire) {
|
||||
({arenasFired, ...config} = didFire);
|
||||
}
|
||||
else {
|
||||
// no enabled outgoing transitions, try the children:
|
||||
({environment, mode, ...rest} = handleEvent(simtime, event, statechart, state,
|
||||
{environment, mode, ...rest}));
|
||||
console.log('attempt children');
|
||||
({arenasFired, ...config} = fairStep(simtime, event, statechart, state, {...config, arenasFired}));
|
||||
}
|
||||
}
|
||||
}
|
||||
return {environment, mode, ...rest};
|
||||
return {arenasFired, ...config};
|
||||
}
|
||||
|
||||
export function handleInputEvent(simtime: number, event: RT_Event, statechart: Statechart, {mode, environment, history}: {mode: Mode, environment: Environment, history: RT_History}): BigStepOutput {
|
||||
let raised = initialRaised;
|
||||
|
||||
({mode, environment, ...raised} = handleEvent(simtime, event, statechart, statechart.root, {mode, environment, history, ...raised}));
|
||||
({mode, environment, ...raised} = fairStep(simtime, event, statechart, statechart.root, {mode, environment, history, arenasFired: [], ...raised}));
|
||||
|
||||
return handleInternalEvents(simtime, statechart, {mode, environment, history, ...raised});
|
||||
}
|
||||
|
|
@ -298,77 +339,127 @@ export function handleInputEvent(simtime: number, event: RT_Event, statechart: S
|
|||
export function handleInternalEvents(simtime: number, statechart: Statechart, {internalEvents, ...rest}: RT_Statechart & RaisedEvents): BigStepOutput {
|
||||
while (internalEvents.length > 0) {
|
||||
const [nextEvent, ...remainingEvents] = internalEvents;
|
||||
({internalEvents, ...rest} = handleEvent(simtime,
|
||||
({internalEvents, ...rest} = fairStep(simtime,
|
||||
{kind: "input", ...nextEvent}, // internal event becomes input event
|
||||
statechart, statechart.root, {internalEvents: remainingEvents, ...rest}));
|
||||
statechart, statechart.root, { arenasFired: [], internalEvents: remainingEvents, ...rest}));
|
||||
}
|
||||
return rest;
|
||||
}
|
||||
|
||||
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));
|
||||
function resolveHistory(tgt: AbstractState, history: RT_History): Set<string> {
|
||||
if (tgt.kind === "shallow" || tgt.kind === "deep") {
|
||||
const toEnter = history.get(tgt.uid) || new Set();
|
||||
return toEnter;
|
||||
}
|
||||
else {
|
||||
const toEnter = new Set([tgt.uid]);
|
||||
return toEnter;
|
||||
}
|
||||
}
|
||||
|
||||
const srcPath = computePath({ancestor: arena, descendant: t.src as ConcreteState}).reverse() as ConcreteState[];
|
||||
export function fire(simtime: number, t: Transition, ts: Map<string, Transition[]>, label: TransitionLabel, arena: OrState, {mode, environment, history, ...rest}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
||||
console.log('will now fire', transitionDescription(t), 'arena', arena);
|
||||
|
||||
const srcPath = computePath({ancestor: arena, descendant: t.src as ConcreteState}) as ConcreteState[];
|
||||
|
||||
console.log(srcPath);
|
||||
// console.log('arena:', arena, 'srcPath:', srcPath);
|
||||
|
||||
// exit src and other states up to arena
|
||||
({environment, history, ...rest} = exitCurrent(simtime, srcPath[0], {environment, enteredStates: mode, history, ...rest}))
|
||||
({environment, history, ...rest} = exitCurrent(simtime, srcPath[0], {environment, enteredStates: mode, history, ...rest}));
|
||||
const toExit = getDescendants(arena);
|
||||
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
|
||||
|
||||
// console.log({exitedMode});
|
||||
console.log('toExit', toExit);
|
||||
console.log('exitedMode', exitedMode);
|
||||
|
||||
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
|
||||
// IF however, the target is a pseudo-state, DON'T enter it (pseudo-states are NOT states), instead fire the first pseudo-outgoing transition.
|
||||
export function fireSecondHalfOfTransition(simtime: number, t: Transition, ts: Map<string, Transition[]>, label: TransitionLabel, arena: OrState, {mode, environment, history, firedTransitions, ...raised}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
||||
// exec transition actions
|
||||
// transition actions
|
||||
for (const action of label.actions) {
|
||||
({environment, history, firedTransitions, ...raised} = execAction(action, {environment, history, firedTransitions, ...raised}));
|
||||
({environment, history, ...rest} = execAction(action, {environment, history, ...rest}));
|
||||
}
|
||||
|
||||
firedTransitions = [...firedTransitions, t.uid];
|
||||
const tgtPath = computePath({ancestor: arena, descendant: t.tgt});
|
||||
const state = tgtPath[0] as ConcreteState; // first state to enter
|
||||
const toEnter = resolveHistory(t.tgt, history)
|
||||
.union(new Set(tgtPath.map(s=>s.uid)));
|
||||
|
||||
if (t.tgt.kind === "pseudo") {
|
||||
const outgoing = ts.get(t.tgt.uid) || [];
|
||||
for (const nextT of outgoing) {
|
||||
for (const nextLabel of nextT.label) {
|
||||
if (nextLabel.kind === "transitionLabel") {
|
||||
if (evalExpr(nextLabel.guard, environment)) {
|
||||
console.log('fire', transitionDescription(nextT));
|
||||
// found ourselves an enabled transition
|
||||
return fireSecondHalfOfTransition(simtime, nextT, ts, nextLabel, arena, {mode, environment, history, firedTransitions, ...raised});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error("stuck in pseudo-state!!");
|
||||
}
|
||||
else {
|
||||
const tgtPath = computePath({ancestor: arena, descendant: t.tgt});
|
||||
const state = tgtPath[0] as ConcreteState;
|
||||
let toEnter;
|
||||
if (t.tgt.kind === "deep" || t.tgt.kind === "shallow") {
|
||||
toEnter = new Set([
|
||||
...tgtPath.slice(0,-1).map(s => s.uid),
|
||||
...history.get(t.tgt.uid)!
|
||||
]) as Set<string>;
|
||||
}
|
||||
else {
|
||||
toEnter = new Set(tgtPath.map(s=>s.uid));
|
||||
}
|
||||
|
||||
// enter tgt
|
||||
let enteredStates;
|
||||
({enteredStates, environment, history, firedTransitions, ...raised} = enterStates(simtime, state, toEnter, {environment, history, firedTransitions, ...raised}));
|
||||
const enteredMode = mode.union(enteredStates);
|
||||
let enteredStates;
|
||||
({enteredStates, environment, history, ...rest} = enterStates(simtime, state, toEnter, {environment, history, ...rest}));
|
||||
const enteredMode = exitedMode.union(enteredStates);
|
||||
|
||||
// console.log({enteredMode});
|
||||
console.log('new mode', enteredMode);
|
||||
|
||||
return {mode: enteredMode, environment, history, firedTransitions, ...raised};
|
||||
}
|
||||
console.log('done firing', transitionDescription(t));
|
||||
|
||||
return {mode: enteredMode, environment, history, ...rest};
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
// const srcPath = computePath({ancestor: arena, descendant: t.src as ConcreteState}).reverse() as ConcreteState[];
|
||||
|
||||
// // console.log('arena:', arena, 'srcPath:', srcPath);
|
||||
|
||||
// // exit src and other states up to arena
|
||||
// ({environment, history, ...rest} = exitCurrent(simtime, srcPath[0], {environment, enteredStates: mode, history, ...rest}))
|
||||
// const toExit = getDescendants(arena);
|
||||
// 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
|
||||
|
||||
// // console.log({exitedMode});
|
||||
|
||||
// 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
|
||||
// // IF however, the target is a pseudo-state, DON'T enter it (pseudo-states are NOT states), instead fire the first pseudo-outgoing transition.
|
||||
// export function fireSecondHalfOfTransition(simtime: number, t: Transition, ts: Map<string, Transition[]>, label: TransitionLabel, arena: OrState, {mode, environment, history, firedTransitions, ...raised}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
||||
// console.log('fire (2nd half)', transitionDescription(t));
|
||||
// // exec transition actions
|
||||
// for (const action of label.actions) {
|
||||
// ({environment, history, firedTransitions, ...raised} = execAction(action, {environment, history, firedTransitions, ...raised}));
|
||||
// }
|
||||
|
||||
// firedTransitions = [...firedTransitions, t.uid];
|
||||
|
||||
// if (t.tgt.kind === "pseudo") {
|
||||
// const outgoing = ts.get(t.tgt.uid) || [];
|
||||
// for (const nextT of outgoing) {
|
||||
// for (const nextLabel of nextT.label) {
|
||||
// if (nextLabel.kind === "transitionLabel") {
|
||||
// if (evalExpr(nextLabel.guard, environment)) {
|
||||
// console.log('fire', transitionDescription(nextT));
|
||||
// // found ourselves an enabled transition
|
||||
// return fireSecondHalfOfTransition(simtime, nextT, ts, nextLabel, arena, {mode, environment, history, firedTransitions, ...raised});
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// throw new Error("stuck in pseudo-state!!");
|
||||
// }
|
||||
// else {
|
||||
// const tgtPath = computePath({ancestor: arena, descendant: t.tgt});
|
||||
// const state = tgtPath[0] as ConcreteState;
|
||||
// let toEnter;
|
||||
// if (t.tgt.kind === "deep" || t.tgt.kind === "shallow") {
|
||||
// toEnter = new Set([
|
||||
// ...tgtPath.slice(0,-1).map(s => s.uid),
|
||||
// ...history.get(t.tgt.uid)!
|
||||
// ]) as Set<string>;
|
||||
// }
|
||||
// else {
|
||||
// toEnter = new Set(tgtPath.map(s=>s.uid));
|
||||
// }
|
||||
|
||||
// // enter tgt
|
||||
// let enteredStates;
|
||||
// ({enteredStates, environment, history, firedTransitions, ...raised} = enterStates(simtime, state, toEnter, {environment, history, firedTransitions, ...raised}));
|
||||
// const enteredMode = mode.union(enteredStates);
|
||||
|
||||
// // console.log({enteredMode});
|
||||
|
||||
// return {mode: enteredMode, environment, history, firedTransitions, ...raised};
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue