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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Velentr.FiniteStateMachine" Version="3.1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Velentr.FiniteStateMachine" Version="3.1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Velentr.FiniteStateMachine" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Velentr.FiniteStateMachine --version 3.1.0.2
                    
#r "nuget: Velentr.FiniteStateMachine, 3.1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=Velentr.FiniteStateMachine&version=3.1.0.2
                    
Install Velentr.FiniteStateMachine as a Cake Addin
#tool nuget:?package=Velentr.FiniteStateMachine&version=3.1.0.2
                    
Install Velentr.FiniteStateMachine as a Cake Tool

Velentr.FiniteStateMachine

Logo

A simple library containing code for a Finite State Machine definition

Installation

Nuget

NuGet version (Velentr.FiniteStateMachine)

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 a boolean 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

  1. Clone or fork the repo
  2. Create a new branch
  3. Code!
  4. Push your changes and open a PR
  5. Once approved, they'll be merged in
  6. Profit!

Future Plans

See list of issues under the Milestones: https://github.com/vonderborch/Velentr.FiniteStateMachine/milestones

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
3.1.0.2 133 5/19/2025
3.0.0.16 92 5/18/2025
1.0.0 388 9/29/2021