Skip to content

Add sub state machines and the materialized Part 16 model to the StateMachines sample - #860

Merged
romanett merged 1 commit into
masterfrom
romanett/ua-netstandard-829-e614b9
Sep 4, 2026
Merged

romanett merged 1 commit into
masterfrom
romanett/ua-netstandard-829-e614b9

Conversation

@romanett

@romanett romanett commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Proposed changes

The StateMachines sample was written against 2.0.0-preview.2, whose fluent builder materialized no state or transition nodes and could not usefully attach a sub state machine to a state. 2.0.0-preview.4 does both, so the two parts issue #829 deferred can now be built.

Server (Workshop/StateMachines/Server/StateMachinesNodeManager.cs)

  • The Operation machine gains a Production sub state machine below its Running state, in the sample's own namespace: Loading (initial) → Processing → Unloading → Loading, with StartBatch as a cause of the child and the rest of the batch on the child's own timed transitions. The framework activates the child and resets it to Loading when the parent enters Running and suspends it when the parent leaves — declared with preserveOnReentry: false, and the remarks say what true would do instead.
  • Everything else is what the builder now materializes on its own, so the server code did not grow for it: a StateType node per state carrying StateNumber (the initial one an InitialStateType), a TransitionType node per transition carrying TransitionNumber and the §4.4.11 FromState / ToState / HasEffect references, the HasCause reference each WithCause adds, and the AvailableStates / AvailableTransitions properties. CurrentState/Id now names a node a client can browse to.

Client (Workshop/StateMachines/Client/MainForm.cs)

  • A model list, one row per state and per transition with its number and NodeId, read through GetAvailableStatesAsync / GetAvailableTransitionsAsync rather than browsed by hand.
  • GetSubStateMachineAsync on each state node is how the client finds Production — off the state node per §4.4.16, not by browse name.
  • The Operation machine is watched through ObserveEffectiveStateAsync, so a transition of the parent or of the child is reported as one combined snapshot and a row reads Running / Processing. The Program machine keeps ObserveFiniteTransitionsAsync, which is the contrast worth showing.
  • A Production state field shows the child, including the Bad_StateNotActive it reports while suspended, and a StartBatch button is offered only while the child's cause is executable.

Related Issues

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Enhancement (non-breaking change which adds functionality)
  • Test enhancement (non-breaking change to increase test coverage)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected, requires version increase of Nuget packages)
  • Documentation Update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc.
  • I have signed the CLA.
  • I ran tests locally with my changes, all passed.
  • I fixed all failing tests in the CI pipelines.
  • I fixed all introduced issues with CodeQL and LGTM.
  • I have added tests that prove my fix is effective or that my feature works and increased code coverage.
  • I have added necessary documentation (if appropriate).
  • Any dependent changes have been merged and published in downstream modules.

Further comments

Tests. The existing browse shape assertion covers the new children of the machine. Five new cases in StateMachinesNodeManagerTests hold the sample to AvailableStates / AvailableTransitions listing exactly the nodes below the machine in declaration order with the declared numbers, to the FromState / ToState / HasCause / HasEffect references of a transition, to HasSubStateMachine hanging off the state node and not off the machine root, to Bad_StateNotActive and Bad_NotExecutable while the child is suspended, and to a batch running to completion on the child's timers and starting over when the parent re-enters Running. The tier 2 case in WorkshopClientSubscriptionTests now also checks the model list, the child's cause becoming executable once the machine is started, and the sub state the window shows.

Locally: StateMachinesNodeManagerTests 15/15, WorkshopClientSubscriptionTests 7/7, whole solution builds without new warnings.

Two things a reviewer may want to know about the child machine. Its cause method has to be added after reading .StateMachine inside configure — that read is what freezes the child and creates the node the method hangs on. And a child is activated through SetState, which runs no dispatcher synchronization, so no timed transition is armed out of the child's initial state; the child's first move is therefore a cause (StartBatch) and only the later transitions are timed. A timer left armed when the parent leaves is harmless: DoTransition is refused with BadStateNotActive, and after the reset the from-state no longer matches.

🤖 Generated with Claude Code

…eMachines sample (#829)

The sample was written against 2.0.0-preview.2, whose fluent builder
materialized no state or transition nodes and could not attach a sub state
machine to a state. preview.4 does both, so the two parts issue #829 deferred
can be built.

Server: the Operation machine now carries a Production sub state machine below
its Running state - Loading (initial), Processing and Unloading, with StartBatch
as a cause of the child and the rest of the batch on the child's own timers. The
framework activates and resets it when the parent enters Running and suspends it
when the parent leaves. The states, transitions, their numbers and the Part 16
references are materialized by the builder, so the server code only declares the
machine.

Client: the window lists the model of the machine - a row per state and per
transition with its number and NodeId - read through GetAvailableStatesAsync and
GetAvailableTransitionsAsync, finds the sub state machine with
GetSubStateMachineAsync on the state node it hangs off, and watches the Operation
machine through ObserveEffectiveStateAsync so a transition of the parent or of
the child is reported as one combined snapshot. The cause of the child is offered
only while the child is active, which is what its Executable attribute says.

Tests: the browse shape assertion covers the new children; new cases hold the
machine to AvailableStates / AvailableTransitions in declaration order with the
declared numbers, to the FromState / ToState / HasCause / HasEffect references of
a transition, to HasSubStateMachine hanging off the state node rather than the
machine root, to Bad_StateNotActive and BadNotExecutable while the child is
suspended, and to a batch running to completion and starting over on re-entry.
The tier 2 client case checks the model list, the cause becoming executable and
the displayed sub state.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@romanett
romanett merged commit 011a3ee into master Sep 4, 2026
8 checks passed
romanett added a commit that referenced this pull request Sep 4, 2026
Master extended four of the clients this branch had already split, so their new
features are ported into the models rather than back into the windows:

- RoleManagement (#864): the identity criteria (UserName/Thumbprint/X509Subject) and
  the criteria strings this client can be matched by, the CustomConfiguration flag,
  the AccessRestrictions and Endpoints columns, and the audit trail stream all live in
  RoleManagementClientModel; the window renders them.
- StateMachines (#860): the materialized Part 16 model (available states and
  transitions), the sub state machine below Running with its StartBatch cause, and the
  effective state stream live in StateMachinesClientModel.
- NodeManagement: the hand written model change pump is replaced by the stack's
  ModelChangeTracker inside the model.
- UserAuthentication: the Kerberos and SAML stubs are gone from the model and the
  window, as on master.

The model fixtures gained cases for the new surface; the testing guide keeps the
model fixture column with master's updated StateMachines row.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

State Machines sample: add sub state machines and materialized state/transition nodes

1 participant