Skip to content

Commit 8af9505

Browse files
author
Nilay Vishwakarma
committed
update readme
1 parent ee92356 commit 8af9505

File tree

1 file changed

+155
-141
lines changed

1 file changed

+155
-141
lines changed

README.md

Lines changed: 155 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,8 @@
22

33
A flexible finite state machine (FSM) library for .NET, supporting .NET Standard 2.0+, .NET Framework 4.6.1+, and .NET 5/6/7/8/9. FsmNet is a C# library designed to create and manage Finite State Machines (FSMs) in a simple and efficient way. It allows developers to define states, transitions, and events, enabling the modeling of complex behaviors in a structured manner.
44

5-
## Features
6-
- **State Management**: Easily define and manage states in your FSM. Strongly-typed FSMs using enums for states
7-
- **Transition Handling**: Transition conditions and side effects.
8-
- **Serialization**: Serialization to/from JSON and YAML.
9-
- **Builder Pattern**: Builder pattern for easy FSM construction.
10-
- **Extensible**: Easily extend the library to fit specific needs or integrate with other systems.
11-
- **Cross-Platform**: Multi-targeted for broad .NET compatibility
5+
## Quick Start
126

13-
## Installation
147
Install FsmNet via NuGet Package Manager or dotnet.
158

169
```bash
@@ -21,9 +14,125 @@ Install-Package FsmNet
2114
dotnet add package FsmNet
2215
```
2316

24-
## Quick Start
17+
```csharp
18+
public enum SwitchState { Off, On }
19+
public class SwitchContext { }
20+
var builder = FiniteStateMachineBuilder<SwitchState, SwitchContext>.Create("Switch")
21+
.WithInitialState(SwitchState.Off)
22+
.AddTransition(SwitchState.Off, SwitchState.On).Done()
23+
.AddTransition(SwitchState.On, SwitchState.Off).Done();
24+
var fsm = new FiniteStateMachine<SwitchState, SwitchContext>(builder.Build());
25+
var context = new SwitchContext();
26+
Console.WriteLine(fsm.Current); // Off
27+
fsm.TryTransitionTo(SwitchState.On, context);
28+
Console.WriteLine(fsm.Current); // On
29+
```
30+
31+
## Features
32+
- **State Management**: Easily define and manage states in your FSM. Strongly-typed FSMs using enums for states
33+
- **Transition Handling**: Transition conditions and side effects.
34+
- **Serialization**: Serialization to/from JSON and YAML.
35+
- **Builder Pattern**: Builder pattern for easy FSM construction.
36+
- **Extensible**: Easily extend the library to fit specific needs or integrate with other systems.
37+
- **Cross-Platform**: Multi-targeted for broad .NET compatibility
38+
39+
## Concepts
40+
41+
### State
42+
A state represents a distinct condition or situation in the lifecycle of an object or process. In FsmNet, states are typically defined using an enum (e.g., Open, Closed, InProgress).
43+
44+
### Transition
45+
A transition is a rule that defines how the FSM moves from one state to another.
46+
Each transition specifies:
47+
- The source state (where the transition starts)
48+
- The target state (where the transition ends)
49+
- An optional condition (a function that must return true for the transition to occur)
50+
- An optional side effect (an action to execute when the transition happens)
51+
52+
### Condition
53+
A condition is a predicate (function) that determines if a transition is allowed, based on the current context.
54+
Example: `ctx => ctx.IsAgentAssigned`
55+
56+
### Side Effect
57+
A side effect is an action that is executed when a transition occurs.
58+
Example: `ctx => Console.WriteLine("Customer notified")`
59+
60+
### Context
61+
The context is an object that holds data relevant to the FSM’s operation and is passed to conditions and side effects.
62+
Example: a `TicketContext` with properties like `IsAgentAssigned`.
63+
64+
### State Machine Definition
65+
A state machine definition describes all possible states, transitions, the initial state, and associated logic for an FSM.
66+
67+
## Advantage of using FSM
68+
69+
Using a finite state machine (FSM) for managing state offers several advantages over ad-hoc approaches (like scattered conditionals, flags, or event-driven code) and even over some object-oriented state patterns. Here are the key benefits:
70+
71+
1. Clarity and Explicitness
72+
- All possible states and transitions are explicitly defined.
73+
- The system’s behavior is easy to visualize, reason about, and document.
74+
- Reduces ambiguity and hidden state changes.
75+
2. Predictability and Robustness
76+
- Transitions are controlled and validated.
77+
- Only allowed transitions can occur, preventing invalid or unexpected state changes.
78+
- Makes it easier to handle edge cases and errors.
79+
3. Maintainability
80+
- Adding or modifying states and transitions is straightforward.
81+
- Changes are localized to the FSM definition, not scattered across the codebase.
82+
- Reduces the risk of introducing bugs when requirements change.
83+
4. Testability
84+
- FSMs are easy to test.
85+
- You can systematically test all states and transitions.
86+
- Makes it easier to write unit tests for state-dependent logic.
87+
5. Separation of Concerns
88+
- State logic is separated from business logic.
89+
- Conditions and side effects are encapsulated, making the codebase cleaner and more modular.
90+
6. Scalability
91+
- FSMs scale well as complexity grows.
92+
- Adding new states or transitions does not exponentially increase code complexity, unlike nested if/else or switch statements.
93+
7. Visualization and Documentation
94+
- FSMs can be visualized as state diagrams.
95+
- This aids in communication with stakeholders and helps onboard new developers.
96+
97+
| Approach | Pros of FSM over this approach |
98+
|------------------|------------------------------------------------------|
99+
| If/else, switch | Avoids spaghetti code, centralizes state logic |
100+
| Flags/booleans | Prevents invalid state combinations |
101+
| Event-driven | Makes allowed transitions explicit and predictable |
102+
| State pattern | FSM is more declarative and easier to visualize |
103+
104+
## Use Cases
105+
Here are some common use cases for implementing a finite state machine (FSM) in software development:
106+
107+
1. Workflow and Process Management
108+
- Example: Ticketing systems, order processing, approval workflows.
109+
- Why: Each item moves through a series of well-defined states (e.g., Open → In Progress → Resolved).
110+
2. User Interface (UI) Navigation
111+
- Example: Wizard dialogs, multi-step forms, menu navigation.
112+
- Why: UI components often have distinct states and transitions based on user actions.
113+
3. Game Development
114+
- Example: Character states (Idle, Walking, Jumping, Attacking), enemy AI behaviors.
115+
- Why: Game entities often have clear, rule-based state transitions.
116+
4. Protocol and Communication Handling
117+
- Example: Network protocol implementations (TCP handshake, HTTP request/response), parsers.
118+
- Why: Protocols are defined by sequences of states and transitions based on received data.
119+
5. Device and Hardware Control
120+
- Example: Embedded systems, robotics, IoT devices (e.g., a washing machine: Idle → Washing → Rinsing → Spinning → Done).
121+
- Why: Devices operate in modes with strict rules for moving between them.
122+
6. Authentication and Authorization Flows
123+
- Example: Login processes, multi-factor authentication, session management.
124+
- Why: Security flows require strict control over allowed transitions.
125+
7. Error Handling and Recovery
126+
- Example: Retry logic, circuit breakers, transaction management.
127+
- Why: Systems need to move between normal, error, and recovery states in a controlled way.
128+
8. Text Parsing and Lexical Analysis
129+
- Example: Tokenizers, interpreters, compilers.
130+
- Why: Parsing often involves moving through states based on input characters or tokens.
131+
132+
133+
## Example
25134

26-
Let's define a state machine first
135+
Let's define a state machine for order management.
27136

28137
```mermaid
29138
stateDiagram-v2
@@ -153,99 +262,49 @@ context.ReturnRequested = true;
153262
fsm.TryTransitionTo(OrderState.Returned, context); // Moves to Returned, notifies return
154263
```
155264

156-
## Concepts
157-
158-
### State
159-
A state represents a distinct condition or situation in the lifecycle of an object or process. In FsmNet, states are typically defined using an enum (e.g., Open, Closed, InProgress).
160-
161-
### Transition
162-
A transition is a rule that defines how the FSM moves from one state to another.
163-
Each transition specifies:
164-
- The source state (where the transition starts)
165-
- The target state (where the transition ends)
166-
- An optional condition (a function that must return true for the transition to occur)
167-
- An optional side effect (an action to execute when the transition happens)
168-
169-
### Condition
170-
A condition is a predicate (function) that determines if a transition is allowed, based on the current context.
171-
Example: `ctx => ctx.IsAgentAssigned`
172-
173-
### Side Effect
174-
A side effect is an action that is executed when a transition occurs.
175-
Example: `ctx => Console.WriteLine("Customer notified")`
176-
177-
### Context
178-
The context is an object that holds data relevant to the FSM’s operation and is passed to conditions and side effects.
179-
Example: a `TicketContext` with properties like `IsAgentAssigned`.
180-
181-
### State Machine Definition
182-
A state machine definition describes all possible states, transitions, the initial state, and associated logic for an FSM.
183265

184-
## Advantage of using FSM
266+
## Serialization/Deserialization Example
185267

186-
Using a finite state machine (FSM) for managing state offers several advantages over ad-hoc approaches (like scattered conditionals, flags, or event-driven code) and even over some object-oriented state patterns. Here are the key benefits:
268+
1. JSON
269+
```csharp
270+
using System.Text.Json;
187271

188-
1. Clarity and Explicitness
189-
- All possible states and transitions are explicitly defined.
190-
- The system’s behavior is easy to visualize, reason about, and document.
191-
- Reduces ambiguity and hidden state changes.
192-
2. Predictability and Robustness
193-
- Transitions are controlled and validated.
194-
- Only allowed transitions can occur, preventing invalid or unexpected state changes.
195-
- Makes it easier to handle edge cases and errors.
196-
3. Maintainability
197-
- Adding or modifying states and transitions is straightforward.
198-
- Changes are localized to the FSM definition, not scattered across the codebase.
199-
- Reduces the risk of introducing bugs when requirements change.
200-
4. Testability
201-
- FSMs are easy to test.
202-
- You can systematically test all states and transitions.
203-
- Makes it easier to write unit tests for state-dependent logic.
204-
5. Separation of Concerns
205-
- State logic is separated from business logic.
206-
- Conditions and side effects are encapsulated, making the codebase cleaner and more modular.
207-
6. Scalability
208-
- FSMs scale well as complexity grows.
209-
- Adding new states or transitions does not exponentially increase code complexity, unlike nested if/else or switch statements.
210-
7. Visualization and Documentation
211-
- FSMs can be visualized as state diagrams.
212-
- This aids in communication with stakeholders and helps onboard new developers.
272+
// Serialization
273+
var definition = builder.Build(); // Ensure build is called before calling ToSerializable
274+
var serializedObject = builder.ToSerializable(); // returns SerializableStateMachine object
275+
string json = JsonSerializer.Serialize(serializedObject);
213276

214-
| Approach | Pros of FSM over this approach |
215-
|------------------|------------------------------------------------------|
216-
| If/else, switch | Avoids spaghetti code, centralizes state logic |
217-
| Flags/booleans | Prevents invalid state combinations |
218-
| Event-driven | Makes allowed transitions explicit and predictable |
219-
| State pattern | FSM is more declarative and easier to visualize |
277+
// Deserialization
278+
var jsonDto = JsonSerializer.Deserialize<SerializableStateMachine>(json);
279+
var loadedBuilder = FiniteStateMachineBuilder<TicketState, TicketContext>.Create(jsonDto.EntityType)
280+
.LoadFrom(jsonDto, registry);
281+
var loadedFsm = loadedBuilder.Build();
282+
var sm = new FiniteStateMachine<TicketState, TicketContext>(loadedFsm);
283+
```
220284

221-
## Use Cases
222-
Here are some common use cases for implementing a finite state machine (FSM) in software development:
285+
2. YAML
286+
```csharp
287+
using YamlDotNet.Serialization;
288+
using YamlDotNet.Serialization.NamingConventions;
223289

224-
1. Workflow and Process Management
225-
- Example: Ticketing systems, order processing, approval workflows.
226-
- Why: Each item moves through a series of well-defined states (e.g., Open → In Progress → Resolved).
227-
2. User Interface (UI) Navigation
228-
- Example: Wizard dialogs, multi-step forms, menu navigation.
229-
- Why: UI components often have distinct states and transitions based on user actions.
230-
3. Game Development
231-
- Example: Character states (Idle, Walking, Jumping, Attacking), enemy AI behaviors.
232-
- Why: Game entities often have clear, rule-based state transitions.
233-
4. Protocol and Communication Handling
234-
- Example: Network protocol implementations (TCP handshake, HTTP request/response), parsers.
235-
- Why: Protocols are defined by sequences of states and transitions based on received data.
236-
5. Device and Hardware Control
237-
- Example: Embedded systems, robotics, IoT devices (e.g., a washing machine: Idle → Washing → Rinsing → Spinning → Done).
238-
- Why: Devices operate in modes with strict rules for moving between them.
239-
6. Authentication and Authorization Flows
240-
- Example: Login processes, multi-factor authentication, session management.
241-
- Why: Security flows require strict control over allowed transitions.
242-
7. Error Handling and Recovery
243-
- Example: Retry logic, circuit breakers, transaction management.
244-
- Why: Systems need to move between normal, error, and recovery states in a controlled way.
245-
8. Text Parsing and Lexical Analysis
246-
- Example: Tokenizers, interpreters, compilers.
247-
- Why: Parsing often involves moving through states based on input characters or tokens.
290+
// Serialization
291+
var definition = builder.Build(); // Ensure build is called before calling ToSerializable
292+
var serializedObject = builder.ToSerializable(); // returns SerializableStateMachine object
293+
var yamlSerializer = new SerializerBuilder()
294+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
295+
.Build();
296+
var yaml = yamlSerializer.Serialize(serializedObject);
248297

298+
// Deserialization
299+
var yamlDeserializer = new DeserializerBuilder()
300+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
301+
.IgnoreUnmatchedProperties()
302+
.Build();
303+
var deserializedDto = yamlDeserializer.Deserialize<SerializableStateMachine>(new StringReader(yaml));
304+
var loadedBuilder = FiniteStateMachineBuilder<TicketState, TicketContext>.Create("Ticket")
305+
.LoadFrom(deserializedDto!, registry);
306+
var sm = new FiniteStateMachine<TicketState, TicketContext>(loadedBuilder.Build());
307+
```
249308

250309
## API Overview
251310

@@ -328,48 +387,3 @@ Here are some common use cases for implementing a finite state machine (FSM) in
328387
- `string To`: The state this transition is to.
329388
- `string ConditionName`: The name of the condition that triggers this transition.
330389
- `string SideEffectName`: The name of the side effect that occurs when this transition is taken.
331-
332-
333-
## Serialization/Deserialization Example
334-
335-
1. JSON
336-
```csharp
337-
using System.Text.Json;
338-
339-
// Serialization
340-
var definition = builder.Build(); // Ensure build is called before calling ToSerializable
341-
var serializedObject = builder.ToSerializable(); // returns SerializableStateMachine object
342-
string json = JsonSerializer.Serialize(serializedObject);
343-
344-
// Deserialization
345-
var jsonDto = JsonSerializer.Deserialize<SerializableStateMachine>(json);
346-
var loadedBuilder = FiniteStateMachineBuilder<TicketState, TicketContext>.Create(jsonDto.EntityType)
347-
.LoadFrom(jsonDto, registry);
348-
var loadedFsm = loadedBuilder.Build();
349-
var sm = new FiniteStateMachine<TicketState, TicketContext>(loadedFsm);
350-
```
351-
352-
2. YAML
353-
```csharp
354-
using YamlDotNet.Serialization;
355-
using YamlDotNet.Serialization.NamingConventions;
356-
357-
// Serialization
358-
var definition = builder.Build(); // Ensure build is called before calling ToSerializable
359-
var serializedObject = builder.ToSerializable(); // returns SerializableStateMachine object
360-
var yamlSerializer = new SerializerBuilder()
361-
.WithNamingConvention(CamelCaseNamingConvention.Instance)
362-
.Build();
363-
var yaml = yamlSerializer.Serialize(serializedObject);
364-
365-
// Deserialization
366-
var yamlDeserializer = new DeserializerBuilder()
367-
.WithNamingConvention(CamelCaseNamingConvention.Instance)
368-
.IgnoreUnmatchedProperties()
369-
.Build();
370-
var deserializedDto = yamlDeserializer.Deserialize<SerializableStateMachine>(new StringReader(yaml));
371-
var deserializedDto = yamlDeserializer.Deserialize<SerializableStateMachine>(new StringReader(yaml));
372-
var loadedBuilder = FiniteStateMachineBuilder<TicketState, TicketContext>.Create("Ticket")
373-
.LoadFrom(deserializedDto!, registry);
374-
var sm = new FiniteStateMachine<TicketState, TicketContext>(loadedBuilder.Build());
375-
```

0 commit comments

Comments
 (0)