fix: plant steps were wrongly showing statechart output events

This commit is contained in:
Joeri Exelmans 2025-11-06 21:26:17 +01:00
parent 8ed9267cb7
commit f5c427d61b
11 changed files with 104 additions and 33 deletions

View file

@ -33,6 +33,9 @@ details:has(+ details) {
background-color: rgba(0,0,255,0.2); background-color: rgba(0,0,255,0.2);
border: solid blue 1px; border: solid blue 1px;
} }
.runtimeState.plantStep:not(.active) {
background-color: #f7f7f7;
}
.runtimeState.plantStep * { .runtimeState.plantStep * {
color: grey; color: grey;
} }

View file

@ -5,10 +5,10 @@ import { Dispatch, ReactElement, SetStateAction, useCallback, useEffect, useMemo
import AddIcon from '@mui/icons-material/Add'; import AddIcon from '@mui/icons-material/Add';
import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome'; import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
import VisibilityIcon from '@mui/icons-material/Visibility';
import SaveOutlinedIcon from '@mui/icons-material/SaveOutlined';
import CachedOutlinedIcon from '@mui/icons-material/CachedOutlined'; import CachedOutlinedIcon from '@mui/icons-material/CachedOutlined';
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
import SaveOutlinedIcon from '@mui/icons-material/SaveOutlined';
import VisibilityIcon from '@mui/icons-material/Visibility';
import { Statechart } from "@/statecharts/abstract_syntax"; import { Statechart } from "@/statecharts/abstract_syntax";
import { detectConnections } from "@/statecharts/detect_connections"; import { detectConnections } from "@/statecharts/detect_connections";
@ -18,7 +18,7 @@ import { parseStatechart } from "../statecharts/parser";
import { BigStep, RaisedEvent } from "../statecharts/runtime_types"; import { BigStep, RaisedEvent } from "../statecharts/runtime_types";
import { getSimTime, getWallClkDelay, TimeMode } from "../statecharts/time"; import { getSimTime, getWallClkDelay, TimeMode } from "../statecharts/time";
import { BottomPanel } from "./BottomPanel"; import { BottomPanel } from "./BottomPanel";
import { PersistentDetails } from "./PersistentDetails"; import { PersistentDetails, PersistentDetailsLocalStorage } from "./PersistentDetails";
import { digitalWatchPlant } from "./Plant/DigitalWatch/DigitalWatch"; import { digitalWatchPlant } from "./Plant/DigitalWatch/DigitalWatch";
import { dummyPlant } from "./Plant/Dummy/Dummy"; import { dummyPlant } from "./Plant/Dummy/Dummy";
import { microwavePlant } from "./Plant/Microwave/Microwave"; import { microwavePlant } from "./Plant/Microwave/Microwave";
@ -26,14 +26,11 @@ import { Plant } from "./Plant/Plant";
import { trafficLightPlant } from "./Plant/TrafficLight/TrafficLight"; import { trafficLightPlant } from "./Plant/TrafficLight/TrafficLight";
import { RTHistory } from "./RTHistory"; import { RTHistory } from "./RTHistory";
import { ShowAST, ShowInputEvents, ShowInternalEvents, ShowOutputEvents } from "./ShowAST"; import { ShowAST, ShowInputEvents, ShowInternalEvents, ShowOutputEvents } from "./ShowAST";
import { InsertMode } from "./TopPanel/InsertModes";
import { TopPanel } from "./TopPanel/TopPanel"; import { TopPanel } from "./TopPanel/TopPanel";
import { VisualEditor, VisualEditorState } from "./VisualEditor/VisualEditor"; import { VisualEditor, VisualEditorState } from "./VisualEditor/VisualEditor";
import { checkProperty, PropertyCheckResult } from "./check_property"; import { checkProperty, PropertyCheckResult } from "./check_property";
import { usePersistentState } from "./persistent_state";
import { useEditor } from "./useEditor"; import { useEditor } from "./useEditor";
import { useUrlHashState } from "./useUrlHashState"; import { useUrlHashState } from "./useUrlHashState";
import { formatTime } from "@/util/util";
export type EditHistory = { export type EditHistory = {
current: VisualEditorState, current: VisualEditorState,
@ -112,6 +109,10 @@ export function App() {
setInsertMode, setInsertMode,
plantName, plantName,
setPlantName, setPlantName,
showConnections,
setShowConnections,
showProperties,
setShowProperties,
showExecutionTrace, showExecutionTrace,
setShowExecutionTrace, setShowExecutionTrace,
showPlantTrace, showPlantTrace,
@ -417,33 +418,33 @@ export function App() {
style={{flex: '0 0 content', backgroundColor: ''}} style={{flex: '0 0 content', backgroundColor: ''}}
> >
{/* State tree */} {/* State tree */}
<PersistentDetails localStorageKey="showStateTree" initiallyOpen={true}> <PersistentDetailsLocalStorage localStorageKey="showStateTree" initiallyOpen={true}>
<summary>state tree</summary> <summary>state tree</summary>
<ul> <ul>
{ast && <ShowAST {...{...ast, trace, highlightActive}}/>} {ast && <ShowAST {...{...ast, trace, highlightActive}}/>}
</ul> </ul>
</PersistentDetails> </PersistentDetailsLocalStorage>
{/* Input events */} {/* Input events */}
<PersistentDetails localStorageKey="showInputEvents" initiallyOpen={true}> <PersistentDetailsLocalStorage localStorageKey="showInputEvents" initiallyOpen={true}>
<summary>input events</summary> <summary>input events</summary>
{ast && <ShowInputEvents {ast && <ShowInputEvents
inputEvents={ast.inputEvents} inputEvents={ast.inputEvents}
onRaise={(e,p) => onRaise("debug."+e,p)} onRaise={(e,p) => onRaise("debug."+e,p)}
disabled={trace===null || trace.trace[trace.idx].kind === "error"} disabled={trace===null || trace.trace[trace.idx].kind === "error"}
showKeys={showKeys}/>} showKeys={showKeys}/>}
</PersistentDetails> </PersistentDetailsLocalStorage>
{/* Internal events */} {/* Internal events */}
<PersistentDetails localStorageKey="showInternalEvents" initiallyOpen={true}> <PersistentDetailsLocalStorage localStorageKey="showInternalEvents" initiallyOpen={true}>
<summary>internal events</summary> <summary>internal events</summary>
{ast && <ShowInternalEvents internalEvents={ast.internalEvents}/>} {ast && <ShowInternalEvents internalEvents={ast.internalEvents}/>}
</PersistentDetails> </PersistentDetailsLocalStorage>
{/* Output events */} {/* Output events */}
<PersistentDetails localStorageKey="showOutputEvents" initiallyOpen={true}> <PersistentDetailsLocalStorage localStorageKey="showOutputEvents" initiallyOpen={true}>
<summary>output events</summary> <summary>output events</summary>
{ast && <ShowOutputEvents outputEvents={ast.outputEvents}/>} {ast && <ShowOutputEvents outputEvents={ast.outputEvents}/>}
</PersistentDetails> </PersistentDetailsLocalStorage>
{/* Plant */} {/* Plant */}
<PersistentDetails localStorageKey="showPlant" initiallyOpen={true}> <PersistentDetailsLocalStorage localStorageKey="showPlant" initiallyOpen={true}>
<summary>plant</summary> <summary>plant</summary>
<select <select
disabled={trace!==null} disabled={trace!==null}
@ -458,9 +459,9 @@ export function App() {
{<plant.render state={plant.cleanupState(plantState)} speed={speed} {<plant.render state={plant.cleanupState(plantState)} speed={speed}
raiseUIEvent={e => onRaise("plant.ui."+e.name, e.param)} raiseUIEvent={e => onRaise("plant.ui."+e.name, e.param)}
/>} />}
</PersistentDetails> </PersistentDetailsLocalStorage>
{/* Connections */} {/* Connections */}
<PersistentDetails localStorageKey="showConnEditor" initiallyOpen={false}> <PersistentDetails state={showConnections} setState={setShowConnections}>
<summary>connections</summary> <summary>connections</summary>
<button title="auto-connect (name-based)" className={autoConnect?"active":""} <button title="auto-connect (name-based)" className={autoConnect?"active":""}
onClick={() => setAutoConnect(c => !c)}> onClick={() => setAutoConnect(c => !c)}>
@ -469,8 +470,13 @@ export function App() {
{ast && ConnEditor(ast, plant, plantConns, setPlantConns)} {ast && ConnEditor(ast, plant, plantConns, setPlantConns)}
</PersistentDetails> </PersistentDetails>
{/* Properties */} {/* Properties */}
<PersistentDetails localStorageKey="showProperty" initiallyOpen={false}> <details open={showProperties} onToggle={e => setShowProperties(e.newState === "open")}>
<summary>properties</summary> <summary>properties</summary>
{plant && <div>
available signals:
&nbsp;
{plant.signals.join(', ')}
</div>}
{properties.map((property, i) => { {properties.map((property, i) => {
const result = propertyResults && propertyResults[i]; const result = propertyResults && propertyResults[i];
let violated = null, propertyError = null; let violated = null, propertyError = null;
@ -495,7 +501,7 @@ export function App() {
<AddIcon fontSize="small"/> add property <AddIcon fontSize="small"/> add property
</button> </button>
</div> </div>
</PersistentDetails> </details>
{/* Traces */} {/* Traces */}
<details open={showExecutionTrace} onToggle={e => setShowExecutionTrace(e.newState === "open")}><summary>execution trace</summary> <details open={showExecutionTrace} onToggle={e => setShowExecutionTrace(e.newState === "open")}><summary>execution trace</summary>
<div> <div>
@ -517,9 +523,9 @@ export function App() {
</div> </div>
<div className="toolbar"> <div className="toolbar">
<input id="checkbox-show-plant-items" type="checkbox" checked={showPlantTrace} onChange={e => setShowPlantTrace(e.target.checked)}/> <input id="checkbox-show-plant-items" type="checkbox" checked={showPlantTrace} onChange={e => setShowPlantTrace(e.target.checked)}/>
<label htmlFor="checkbox-show-plant-items">show plant steps</label> <label title="plant steps are steps where only the state of the plant changed" htmlFor="checkbox-show-plant-items">show plant steps</label>
<input id="checkbox-autoscroll" type="checkbox" checked={autoScroll} onChange={e => setAutoScroll(e.target.checked)}/> <input id="checkbox-autoscroll" type="checkbox" checked={autoScroll} onChange={e => setAutoScroll(e.target.checked)}/>
<label htmlFor="checkbox-autoscroll">auto-scroll</label> <label title="automatically scroll down event trace when new events occur" htmlFor="checkbox-autoscroll">auto-scroll</label>
&emsp; &emsp;
<button title="save current trace" disabled={trace === null} onClick={() => onSaveTrace()}> <button title="save current trace" disabled={trace === null} onClick={() => onSaveTrace()}>
<SaveOutlinedIcon fontSize="small"/> save trace <SaveOutlinedIcon fontSize="small"/> save trace
@ -617,9 +623,9 @@ function ConnEditor(ast: Statechart, plant: Plant<any, any>, plantConns: Conns,
</select> </select>
</div>)]} </div>)]}
{/* Plant UI events can go to SC or to Plant */} {/* Plant UI events typically go to the Plant */}
{plant.uiEvents.map(e => <div style={{width:'100%', textAlign:'right'}}> {plant.uiEvents.map(e => <div style={{width:'100%', textAlign:'right'}}>
<label htmlFor={`select-dst-plant-ui-${e.event}`} style={{width:'50%'}}>ui.{e.event}&nbsp;&nbsp;</label> <label htmlFor={`select-dst-plant-ui-${e.event}`} style={{width:'50%', color: 'grey'}}>ui.{e.event}&nbsp;&nbsp;</label>
<select id={`select-dst-plant-ui-${e.event}`} <select id={`select-dst-plant-ui-${e.event}`}
style={{width:'50%'}} style={{width:'50%'}}
value={plantConns['plant.ui.'+e.event]?.join('.')} value={plantConns['plant.ui.'+e.event]?.join('.')}

View file

@ -4,7 +4,7 @@ import { TraceableError } from "../statecharts/parser";
import "./BottomPanel.css"; import "./BottomPanel.css";
import logo from "../../artwork/logo-playful.svg"; import logo from "../../artwork/logo-playful.svg";
import { PersistentDetails } from "./PersistentDetails"; import { PersistentDetailsLocalStorage } from "./PersistentDetails";
export function BottomPanel(props: {errors: TraceableError[]}) { export function BottomPanel(props: {errors: TraceableError[]}) {
const [greeting, setGreeting] = useState( const [greeting, setGreeting] = useState(
@ -24,7 +24,7 @@ export function BottomPanel(props: {errors: TraceableError[]}) {
{greeting} {greeting}
{props.errors.length > 0 && {props.errors.length > 0 &&
<div className="errorStatus"> <div className="errorStatus">
<PersistentDetails initiallyOpen={false} localStorageKey="errorsExpanded"> <PersistentDetailsLocalStorage initiallyOpen={false} localStorageKey="errorsExpanded">
<summary>{props.errors.length} errors</summary> <summary>{props.errors.length} errors</summary>
<div style={{maxHeight: '25vh', overflow: 'auto'}}> <div style={{maxHeight: '25vh', overflow: 'auto'}}>
{props.errors.map(({message, shapeUid})=> {props.errors.map(({message, shapeUid})=>
@ -32,7 +32,7 @@ export function BottomPanel(props: {errors: TraceableError[]}) {
{shapeUid}: {message} {shapeUid}: {message}
</div>)} </div>)}
</div> </div>
</PersistentDetails> </PersistentDetailsLocalStorage>
</div> </div>
} }
</div>; </div>;

View file

@ -1,5 +1,5 @@
import { usePersistentState } from "@/App/persistent_state" import { usePersistentState } from "@/App/persistent_state"
import { DetailsHTMLAttributes, PropsWithChildren } from "react"; import { DetailsHTMLAttributes, Dispatch, PropsWithChildren, SetStateAction } from "react";
type Props = { type Props = {
localStorageKey: string, localStorageKey: string,
@ -7,9 +7,15 @@ type Props = {
} & DetailsHTMLAttributes<HTMLDetailsElement>; } & DetailsHTMLAttributes<HTMLDetailsElement>;
// A <details> node that remembers whether it was open or closed by storing that state in localStorage. // A <details> node that remembers whether it was open or closed by storing that state in localStorage.
export function PersistentDetails({localStorageKey, initiallyOpen, children, ...rest}: PropsWithChildren<Props>) { export function PersistentDetailsLocalStorage({localStorageKey, initiallyOpen, children, ...rest}: PropsWithChildren<Props>) {
const [open, setOpen] = usePersistentState(localStorageKey, initiallyOpen); const [open, setOpen] = usePersistentState(localStorageKey, initiallyOpen);
return <details open={open} onToggle={e => setOpen(e.newState === "open")} {...rest}> return <details open={open} onToggle={e => setOpen(e.newState === "open")} {...rest}>
{children} {children}
</details>; </details>;
} }
export function PersistentDetails({state, setState, children, ...rest}: PropsWithChildren<{state: boolean, setState: Dispatch<SetStateAction<boolean>>}>) {
return <details open={state} onToggle={e => setState(e.newState === "open")} {...rest}>
{children}
</details>;
}

View file

@ -156,4 +156,21 @@ export const digitalWatchPlant = makeStatechartPlant({
{ kind: "event", event: "bottomRightMouseUp" }, { kind: "event", event: "bottomRightMouseUp" },
{ kind: "event", event: "bottomLeftMouseUp" }, { kind: "event", event: "bottomLeftMouseUp" },
], ],
signals: [
"lightOn",
"beep",
"alarmOn",
"displayingTime",
"displayingAlarm",
"displayingChrono",
"hideH",
"hideM",
"hideS",
// these properties are true for as long as the mouse button is down:
"topLeftPressed",
"topRightPressed",
"bottomRightPressed",
"bottomLeftPressed",
],
}); });

View file

@ -15,4 +15,5 @@ export const dummyPlant: Plant<{}, {}> = {
execution: dummyExecution, execution: dummyExecution,
cleanupState: ({}) => ({}), cleanupState: ({}) => ({}),
render: ({}) => <></>, render: ({}) => <></>,
signals: [],
}; };

View file

@ -143,6 +143,16 @@ const microwavePlantSpec: StatechartPlantSpec<MicrowaveState> = {
{kind: "event", event: "incTimeMouseDown"}, {kind: "event", event: "incTimeMouseDown"},
{kind: "event", event: "incTimeMouseUp"}, {kind: "event", event: "incTimeMouseUp"},
], ],
signals: [
"bellRinging",
"magnetronRunning",
"doorOpen",
// these booleans are true for as long as the respective button is pressed (i.e., mouse button is down)
"startPressed",
"stopPressed",
"incTimePressed",
]
} }
export const microwavePlant = makeStatechartPlant(microwavePlantSpec); export const microwavePlant = makeStatechartPlant(microwavePlantSpec);

View file

@ -17,6 +17,8 @@ export type Plant<StateType, CleanStateType> = {
inputEvents: EventTrigger[]; inputEvents: EventTrigger[];
outputEvents: EventTrigger[]; outputEvents: EventTrigger[];
signals: string[]; // signal names. all signals are booleans.
execution: TimedReactive<StateType>; execution: TimedReactive<StateType>;
cleanupState: (state: StateType) => CleanStateType; cleanupState: (state: StateType) => CleanStateType;
render: (props: PlantRenderProps<CleanStateType>) => ReactNode; render: (props: PlantRenderProps<CleanStateType>) => ReactNode;
@ -59,9 +61,10 @@ export type StatechartPlantSpec<CleanStateType> = {
ast: Statechart, ast: Statechart,
cleanupState: (rtConfig: RT_Statechart) => CleanStateType, cleanupState: (rtConfig: RT_Statechart) => CleanStateType,
render: (props: PlantRenderProps<CleanStateType>) => ReactNode, render: (props: PlantRenderProps<CleanStateType>) => ReactNode,
signals: string[],
} }
export function makeStatechartPlant<CleanStateType>({uiEvents, ast, cleanupState, render}: StatechartPlantSpec<CleanStateType>): Plant<BigStep, CleanStateType> { export function makeStatechartPlant<CleanStateType>({uiEvents, ast, cleanupState, render, signals}: StatechartPlantSpec<CleanStateType>): Plant<BigStep, CleanStateType> {
return { return {
uiEvents, uiEvents,
inputEvents: ast.inputEvents, inputEvents: ast.inputEvents,
@ -69,6 +72,7 @@ export function makeStatechartPlant<CleanStateType>({uiEvents, ast, cleanupState
execution: statechartExecution(ast), execution: statechartExecution(ast),
cleanupState, cleanupState,
render, render,
signals,
} }
} }

View file

@ -109,6 +109,13 @@ const trafficLightPlantSpec: StatechartPlantSpec<TrafficLightState> = {
uiEvents: [ uiEvents: [
{kind: "event", event: "policeInterrupt"}, {kind: "event", event: "policeInterrupt"},
], ],
signals: [
"redOn",
"yellowOn",
"greenOn",
"timerGreen",
"timerValue",
],
} }
export const trafficLightPlant = makeStatechartPlant(trafficLightPlantSpec); export const trafficLightPlant = makeStatechartPlant(trafficLightPlantSpec);

View file

@ -90,6 +90,7 @@ function RTEventParam(props: {param?: any}) {
export const RTHistoryItem = memo(function RTHistoryItem({ast, idx, item, prevItem, isPlantStep, active, onMouseDown, propertyStatus}: {idx: number, ast: Statechart, item: TraceItem, prevItem?: TraceItem, isPlantStep: boolean, active: boolean, onMouseDown: (idx: number, timestamp: number) => void, propertyStatus: PropertyStatus}) { export const RTHistoryItem = memo(function RTHistoryItem({ast, idx, item, prevItem, isPlantStep, active, onMouseDown, propertyStatus}: {idx: number, ast: Statechart, item: TraceItem, prevItem?: TraceItem, isPlantStep: boolean, active: boolean, onMouseDown: (idx: number, timestamp: number) => void, propertyStatus: PropertyStatus}) {
if (item.kind === "bigstep") { if (item.kind === "bigstep") {
const outputEvents = isPlantStep ? item.state.plant.outputEvents : item.state.sc.outputEvents;
// @ts-ignore // @ts-ignore
const newStates = item.state.sc.mode.difference(prevItem?.state.sc.mode || new Set()); const newStates = item.state.sc.mode.difference(prevItem?.state.sc.mode || new Set());
return <div return <div
@ -104,8 +105,8 @@ export const RTHistoryItem = memo(function RTHistoryItem({ast, idx, item, prevIt
</div> </div>
<ShowMode mode={newStates} statechart={ast}/> <ShowMode mode={newStates} statechart={ast}/>
<ShowEnvironment environment={item.state.sc.environment}/> <ShowEnvironment environment={item.state.sc.environment}/>
{item.state.sc.outputEvents.length>0 && <>^ {outputEvents.length>0 && <>^
{item.state.sc.outputEvents.map((e:RaisedEvent) => <span className="outputEvent">{e.name}<RTEventParam param={e.param}/></span>)} {outputEvents.map((e:RaisedEvent) => <span className="outputEvent">{e.name}<RTEventParam param={e.param}/></span>)}
</>} </>}
</div>; </div>;
} }

View file

@ -8,7 +8,7 @@ import { Conns } from "@/statecharts/timed_reactive";
export function useUrlHashState(editorState: VisualEditorState | null, setEditHistory: Dispatch<SetStateAction<EditHistory|null>>) { export function useUrlHashState(editorState: VisualEditorState | null, setEditHistory: Dispatch<SetStateAction<EditHistory|null>>) {
// i should probably put all these things into a single object, the 'app state'... // i should probably put all these things into a single object, the 'app state'...
const [autoScroll, setAutoScroll] = useState(true); const [autoScroll, setAutoScroll] = useState(false);
const [autoConnect, setAutoConnect] = useState(true); const [autoConnect, setAutoConnect] = useState(true);
const [plantConns, setPlantConns] = useState<Conns>({}); const [plantConns, setPlantConns] = useState<Conns>({});
const [showKeys, setShowKeys] = useState(true); const [showKeys, setShowKeys] = useState(true);
@ -16,6 +16,8 @@ export function useUrlHashState(editorState: VisualEditorState | null, setEditHi
const [insertMode, setInsertMode] = useState<InsertMode>("and"); const [insertMode, setInsertMode] = useState<InsertMode>("and");
const [plantName, setPlantName] = useState("dummy"); const [plantName, setPlantName] = useState("dummy");
const [showConnections, setShowConnections] = useState(false);
const [showProperties, setShowProperties] = useState(false);
const [showExecutionTrace, setShowExecutionTrace] = useState(true); const [showExecutionTrace, setShowExecutionTrace] = useState(true);
const [showPlantTrace, setShowPlantTrace] = useState(false); const [showPlantTrace, setShowPlantTrace] = useState(false);
const [properties, setProperties] = useState<string[]>([]); const [properties, setProperties] = useState<string[]>([]);
@ -82,6 +84,12 @@ export function useUrlHashState(editorState: VisualEditorState | null, setEditHi
if (recoveredState.insertMode !== undefined) { if (recoveredState.insertMode !== undefined) {
setInsertMode(recoveredState.insertMode); setInsertMode(recoveredState.insertMode);
} }
if (recoveredState.showConnections !== undefined) {
setShowConnections(recoveredState.showConnections);
}
if (recoveredState.showProperties !== undefined) {
setShowProperties(recoveredState.showProperties);
}
if (recoveredState.showExecutionTrace !== undefined) { if (recoveredState.showExecutionTrace !== undefined) {
setShowExecutionTrace(recoveredState.showExecutionTrace); setShowExecutionTrace(recoveredState.showExecutionTrace);
} }
@ -123,6 +131,8 @@ export function useUrlHashState(editorState: VisualEditorState | null, setEditHi
insertMode, insertMode,
plantName, plantName,
editorState, editorState,
showConnections,
showProperties,
showExecutionTrace, showExecutionTrace,
showPlantTrace, showPlantTrace,
properties, properties,
@ -149,6 +159,8 @@ export function useUrlHashState(editorState: VisualEditorState | null, setEditHi
zoom, zoom,
insertMode, insertMode,
plantName, plantName,
showConnections,
showProperties,
showExecutionTrace, showExecutionTrace,
showPlantTrace, showPlantTrace,
properties, properties,
@ -171,6 +183,10 @@ export function useUrlHashState(editorState: VisualEditorState | null, setEditHi
setInsertMode, setInsertMode,
plantName, plantName,
setPlantName, setPlantName,
showConnections,
setShowConnections,
showProperties,
setShowProperties,
showExecutionTrace, showExecutionTrace,
setShowExecutionTrace, setShowExecutionTrace,
showPlantTrace, showPlantTrace,