Statechart
A hierarchical statechart attached to an AgentLike owner. Behavior consists of named states which may nest: a state declared inside another is a substate of it. Once started the chart occupies a chain of states — one leaf plus each of its ancestors — not a single state. activeStateNames exposes that chain, and it degenerates to one element for a chart with no nesting.
Transitions resolve through the least common ancestor of the source and target chains: states are exited from the leaf upward to (but not including) the LCA, then entered from below the LCA downward to the new leaf. So a transition between two substates of the same parent leaves that parent — and any pending triggers it owns — untouched. Where a substate and an ancestor both have a trigger enabled by the same event, the innermost wins and the ancestor's action does not run.
Calling transitionTo from an exit action
transitionTo records a target and schedules a zero-delay event rather than transitioning on the spot, so where it is called from matters:
From a trigger (timeout, condition, signal, message) or an entry action, the request is folded into the transition being processed. A second call in the same action body is ignored — the first target wins.
From an exit action, the transition in flight is already past the point of redirection, so the request becomes a second transition performed once the first completes. The in-flight target is therefore entered on the way through, and its entry actions run, before the chart moves on to the state the exit action asked for.
The second case is deterministic but easy to be surprised by, so it is reported: a WARN is emitted to the AgentModel.logger category (ksl.modeling.agent.AgentModel) naming the agent, the statechart, the exiting state, the requested target and the in-flight target. Enable that category at WARN or lower to see it. To move directly to a state, trigger the transition from the source state rather than from its exit action.
Implemented as an inner class of AgentModel rather than a separate ModelElement: the inner-class outer-instance reference (this@AgentModel) gives this class direct access to the outer's protected schedule(...) and executive. No ModelElement registration is needed, so a Statechart can be constructed at any time — including during a replication, as part of a transient Agent's construction.
Lifecycle:
start enters the initial state and installs triggers.
stop tears down pending timeout/condition/transition events and the arrival listener.
For setup-time agents, AgentModel.initialize / AgentModel.afterReplication (for transient
Agent) or the owner's own lifecycle (forPermanentAgent/AgentResource) drives start/stop on each replication.For runtime-created transient
Agents,statechart { }auto-starts the chart immediately since the simulation is already running.
Hierarchical states, history, and onSignal are not in this version (planned as Phase 1b.1).
Properties
All currently-active state names, root composite first, leaf last. For a flat statechart this is always a single-element list. Useful for diagnostics and for checking whether a particular composite is active.
Name of the currently active leaf state, or the last leaf held before the statechart was stopped at end-of-replication. null only before the first start.
Number of currently-attached observers (for diagnostics).
All state names declared in this statechart (including nested substates), in undefined order. Used by AgentPerformance to pre-allocate per-state statistics responses.
Functions
Register an observer to receive state-entry, state-exit, and transition events. Events fire for every level of the active chain (composite + leaf), in entry / exit order.
Remove a previously-attached observer. No-op if not attached.
Enter the initial state and install triggers. Idempotent — a second call before stop is a no-op. If the declared initial state is composite, descends through initial substates to reach the leaf. Registers with the enclosing AgentModel so it is cleaned up deterministically at end-of-replication even if the owning agent is never explicitly stopped.
Tear down all pending triggers across every active level and deregister from the AgentModel active-statechart registry. Leaves currentStateName reporting the last leaf for post-simulation inspection. Idempotent. Safe to call while the model is running (graceful stop) or between runs.