Velentr.FiniteStateMachine
3.1.0.2
dotnet add package Velentr.FiniteStateMachine --version 3.1.0.2
NuGet\Install-Package Velentr.FiniteStateMachine -Version 3.1.0.2
<PackageReference Include="Velentr.FiniteStateMachine" Version="3.1.0.2" />
<PackageVersion Include="Velentr.FiniteStateMachine" Version="3.1.0.2" />
<PackageReference Include="Velentr.FiniteStateMachine" />
paket add Velentr.FiniteStateMachine --version 3.1.0.2
#r "nuget: Velentr.FiniteStateMachine, 3.1.0.2"
#addin nuget:?package=Velentr.FiniteStateMachine&version=3.1.0.2
#tool nuget:?package=Velentr.FiniteStateMachine&version=3.1.0.2
Velentr.FiniteStateMachine
A simple library containing code for a Finite State Machine definition
Installation
Nuget
The recommended installation approach is to use the available nuget package: Velentr.FiniteStateMachine
Clone
Alternatively, you can clone this repo and reference the Velentr.FiniteStateMachine project in your project.
Features
- This is a Finite State Machine. It can do things FSM's can do.
- Multiple transitions can be defined for each state.
- Multiple states can be defined.
- Triggers can be defined as either simple triggers (direct value) or functional triggers (lambda expressions, requires the finite state machine be called with .Update() to check if the trigger is valid).
- Events for a state being entered, exited, or continuing to be the current state can be defined.
Basic Usage
NOTE: States can also be defined with events that are called when entering, exiting, or when an update call is performed against the FSM and the state is the current state. This is not shown in this example, but can be seen in the unit tests .
Step 1: Define States and Triggers
Define some possible States (the different distinct states that the Finite State Machine can be in) and some Triggers (the possible actions that can occur on each state). Enums are easy to use for this, but the requirements for each are:
- States: must be a non-nullable type
- Triggers: must be an enum or a class that implements
IEquatable<T>
private enum DoorStates { Opened, Closed }
private enum DoorTriggers { Open, Close }
Step 2: Define a Blackboard
The Blackboard is an object that must be passed to the FSM when calling the Update() method, and can be passed to the FSM when calling the Trigger method. This allows for storing of data that can be used in either the lambda-based triggers or with events that are generated by the FSM. The blackboard must be a class.
private class Blackboard(int value)
{
int Value { get; set; } = value;
}
Step 3: Create a FSM
The FiniteStateMachine class requires a type for the state (DoorStates
in this example), a type for the triggers
(DoorTriggers
in this example), a type for the blackboard (Blackboard
in this example), and a initial starting
state (DoorStates.Closed
in this example).
var fsm = new FiniteStateMachine<DoorStates, DoorTriggers, Blackboard>(DoorStates.Closed);
Step 4: Define some Transitions
Transitions are what defines what happens when a trigger is done for a particular state). In this package there are two types of transitions supported:
- Simple (trigger-based): these require a specific trigger value to be provided when we check if we need to trigger a change.
- Functional (lambda expressions): these require a lambda expression accepting a
Blackboard
parameter and outputting aboolean
as to whether the trigger passes or not.
Performance-wise, the simple trigger-based transitions are faster (we only need to check if we have a transition defined for the exact value), but the functional ones are more flexible.
// AddTransition(FromState, ToState, Trigger)
fsm.AddTransition(DoorStates.Closed, DoorStates.Opened, DoorTriggers.Open);
fsm.AddTransition(DoorStates.Opened, DoorStates.Closed, DoorTriggers.Close);
// AddTransition(FromState, ToState, TriggerLambda/Method)
fsm.AddTransition(DoorStates.Opened, DoorStates.Closed, blackboard => blackboard.Value > 2);
Step 5: Profit!
Simple Triggers
Console.WriteLine(fsm.CurrentStateValue.ToString()); // The FSM is Closed!
fsm.Trigger(DoorTriggers.Open);
Console.WriteLine(fsm.CurrentStateValue.ToString()); // The FSM is Opened!
fsm.Trigger(DoorTriggers.Close);
Console.WriteLine(fsm.CurrentStateValue.ToString()); // The FSM is Closed!
Functional Triggers
Console.WriteLine(fsm.CurrentStateValue.ToString()); // The FSM is Closed!
fsm.Trigger(DoorTriggers.Open);
Console.WriteLine(fsm.CurrentStateValue.ToString()); // The FSM is Opened!
for (int i = 0; i < 4; i++)
{
fsm.Update(new Blackboard(i));
Console.WriteLine(fsm.CurrentStateValue.ToString());
// i = 0 -> The FSM is Opened!
// i = 1 -> The FSM is Opened!
// i = 2 -> The FSM is Opened!
// i = 3 -> The FSM is Closed!
}
Console.WriteLine(fsm.CurrentStateValue.ToString()); // The FSM is Closed!
Full Example
private enum DoorStates { Opened, Closed }
private enum DoorTriggers { Open, Close }
private class Blackboard(int value)
{
int Value { get; set; } = value;
}
public class MyClass()
{
FiniteStateMachine<DoorStates> fsm;
public MyClass()
{
fsm = new FiniteStateMachine<DoorStates, DoorTriggers, Blackboard>(DoorStates.Closed)
.AddState(DoorStates.Closed)
.AddState(DoorStates.Opened)
.AddTransition(DoorStates.Closed, DoorStates.Opened, DoorTriggers.Open)
.AddTransition(DoorStates.Opened, DoorStates.Closed, DoorTriggers.Close)
.AddTransition(DoorStates.Opened, DoorStates.Closed, blackboard => blackboard.Value > 2);
}
public void Test()
{
Console.WriteLine(fsm.CurrentStateValue.ToString()); // The FSM is Closed!
fsm.Trigger(DoorTriggers.Open);
Console.WriteLine(fsm.CurrentStateValue.ToString()); // The FSM is Opened!
fsm.Trigger(DoorTriggers.Close);
Console.WriteLine(fsm.CurrentStateValue.ToString()); // The FSM is Closed!
fsm.Reset();
Console.WriteLine(fsm.CurrentStateValue.ToString()); // The FSM is Closed!
fsm.Trigger(DoorTriggers.Open);
Console.WriteLine(fsm.CurrentStateValue.ToString()); // The FSM is Opened!
for (int i = 0; i < 4; i++)
{
fsm.Update(new Blackboard(i));
Console.WriteLine(fsm.CurrentStateValue.ToString());
// i = 0 -> The FSM is Opened!
// i = 1 -> The FSM is Opened!
// i = 2 -> The FSM is Opened!
// i = 3 -> The FSM is Closed!
}
Console.WriteLine(fsm.CurrentStateValue.ToString()); // The FSM is Closed!
}
}
Development
- Clone or fork the repo
- Create a new branch
- Code!
- Push your changes and open a PR
- Once approved, they'll be merged in
- Profit!
Future Plans
See list of issues under the Milestones: https://github.com/vonderborch/Velentr.FiniteStateMachine/milestones
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net9.0
- Serialize.Linq (>= 4.0.167)
- Velentr.Collections (>= 3.1.0.7)
- Velentr.Core (>= 3.2.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.