Lite.StateMachine
2.0.0-alpha2
dotnet add package Lite.StateMachine --version 2.0.0-alpha2
NuGet\Install-Package Lite.StateMachine -Version 2.0.0-alpha2
<PackageReference Include="Lite.StateMachine" Version="2.0.0-alpha2" />
<PackageVersion Include="Lite.StateMachine" Version="2.0.0-alpha2" />
<PackageReference Include="Lite.StateMachine" />
paket add Lite.StateMachine --version 2.0.0-alpha2
#r "nuget: Lite.StateMachine, 2.0.0-alpha2"
#:package Lite.StateMachine@2.0.0-alpha2
#addin nuget:?package=Lite.StateMachine&version=2.0.0-alpha2&prerelease
#tool nuget:?package=Lite.StateMachine&version=2.0.0-alpha2&prerelease
Lite State Machine for .NET
Flexible lightweight finite state machine (FSM) for .NET, supporting shared context for passing parameters, composite (sub) states, command states, lazy-loading and thread safe. The library is AOT friendly, cross-platform and optimized for speed for use in enterprise, robotic/industrial systems, and even tiny (mobile) applications.
The Lite State Machine is designed for vertical scaling. Meaning, it can be used for the most basic (tiny) system and beyond medical-grade robotics systems.
Download Lite.StateMachine @ NuGet.org today!
Copyright 2022-2025 Xeno Innovations, Inc. (dba, Suess Labs)<br /> Created by: Damian Suess<br /> Date: 2022-06-07<br />
Usage
Create a state machine by defining the states, transitions, and shared context.
You can define the state machine using either the fluent design pattern or standard line-by-line. Each state is represented by a enum StateId in the following example.
Basic State
// That's it! Just create the state machine, register states, and run it.
var machine = await new StateMachine<StateId>()
.RegisterState<BasicState1>(StateId.State1, StateId.State2)
.RegisterState<BasicState2>(StateId.State2, StateId.State3)
.RegisterState<BasicState3>(StateId.State3)
.RunAsync(StateId.State1);
// To avoid hung states, you can pass in a timeout value (in milliseconds)
// Useful for robotic systems; fail fast and recover!
var machine = new StateMachine<BasicStateId>();
machine.DefaultStateTimeoutMs = 3000;
Define States:
// Optional Wrapper
public class BaseState : IState<StateId>
{
public virtual Task OnEntering(Context<StateId> context) => Task.CompletedTask;
public virtual Task OnEnter(Context<StateId> context) => Task.CompletedTask;
public virtual Task OnExit(Context<StateId> context) => Task.CompletedTask;
}
public class BasicState1() : IState<StateId>
{
public async Task OnEnter(Context<BasicStateId> context)
{
await Task.Yield(); // Some async work here...
context.NextState(Result.Ok);
}
}
public class BasicState2() : IState<StateId>
{
public Task OnEnter(Context<StateId> context)
{
context.NextState(Result.Ok);
return Task.CompletedTask; // Notice, we did not async/await this method
}
}
public class BasicState3() : IState<StateId>
{
public Task OnEnter(Context<StateId> context)
{
context.NextState(Result.Ok);
return Task.CompletedTask;
}
}
Composite States
using Lite.StateMachine;
var ctxProperties = new PropertyBag() { { "CounterKey", 0 } };
// Note the use of generics '<TStateClass>' to strongly-type the state machine
var machine = new StateMachine<CompositeL1StateId>()
.RegisterState<Composite_State1>(CompositeL1StateId.State1, CompositeL1StateId.State2)
.RegisterComposite<Composite_State2>(
stateId: CompositeL1StateId.State2,
initialChildStateId: CompositeL1StateId.State2_Sub1,
onSuccess: CompositeL1StateId.State3)
.RegisterSubState<Composite_State2_Sub1>(
stateId: CompositeL1StateId.State2_Sub1,
parentStateId: CompositeL1StateId.State2,
onSuccess: CompositeL1StateId.State2_Sub2)
.RegisterSubState<Composite_State2_Sub2>(
stateId: CompositeL1StateId.State2_Sub2,
parentStateId: CompositeL1StateId.State2,
onSuccess: null) // NULL denotes returning to parent state on success
.RegisterState<Composite_State3>(CompositeL1StateId.State3);
// Optional, pass in starting Context Property
await machine.RunAsync(CompositeL1StateId.State1, ctxProperties);
States are represented by classes that implement the IState interface.
public class Composite_State1() : BaseState
{
public override Task OnEnter(Context<CompositeL1StateId> context)
{
context.NextState(Result.Ok);
return Task.CompletedTask;
}
}
public class Composite_State2() : BaseState
{
public override Task OnEnter(Context<CompositeL1StateId> context)
{
// Signify we're ready to go to sub-states
context.NextState(Result.Ok);
return Task.CompletedTask;
}
public override Task OnExit(Context<CompositeL1StateId> context)
{
// Signify we're ready to go to next state after composite
context.NextState(Result.Ok);
return Task.CompletedTask;
}
}
public class Composite_State2_Sub1() : BaseState
{
public override Task OnEnter(Context<CompositeL1StateId> context)
{
context.Parameters.Add("ParameterSubStateEntered", SUCCESS);
context.NextState(Result.Ok);
return Task.CompletedTask;
}
}
public class Composite_State2_Sub2() : BaseState
{
public override Task OnEnter(Context<CompositeL1StateId> context)
{
context.NextState(Result.Ok);
return Task.CompletedTask;
}
}
public class Composite_State3() : BaseState
{
public override Task OnEnter(Context<CompositeL1StateId> context)
{
context.NextState(Result.Ok);
return Task.CompletedTask;
}
}
Generate DOT Graph (GraphViz)
var uml = machine.ExportUml(includeSubmachines: true);
Features
- AOT Friendly - No Reflection, no Linq, etc.
- Passing parameters between state transitions via
Context - Types of States
- Basic Linear State (
BaseState) - Composite States (
CompositeState)- Hieratical / Nested Sub-states
- Similar to Actor/Director model
- Command States with optional Timeout (
CommandState)- Uses internal Event Aggregator for sending/receiving messages
- Allows users to hook to external messaging services (TCP/IP, RabbitMQ, DBus, etc.)
- Basic Linear State (
- State Transition Triggers
- Transitions are triggered by setting the context's next state result:
- On Success:
context.NextState(Result.Ok); - On Error:
context.NextState(Result.Error); - On Failure: :
context.NextState(Result.Failure);
- State Handlers
OnEntering- Initial entry of the stateOnEnter- Resting (idle) place for state.OnExit- (Optional) Thrown during transitioning. Used for housekeeping or exiting activity.OnMessage(Optional)- Must ensure that code has exited
OnMessagebefore going to the next state.
- Must ensure that code has exited
OnTimeout- (Optional) Thrown when the state is auto-transitioning due to timeout exceeded
- Transition has knowledge of the
PreviousStateandNextState
References
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.0-alpha2 | 0 | 12/30/2025 |
| 2.0.0-alpha1 | 37 | 12/28/2025 |
Overhauled framework to include enhanced state definitions for deep sub-state hierarchy, async/await friendly state classes, RegisterState overloads with friendly naming conventions, uses new namespace "Lite.StateMachine".