SiLA2.StateMachine
10.2.3
See the version list below for details.
dotnet add package SiLA2.StateMachine --version 10.2.3
NuGet\Install-Package SiLA2.StateMachine -Version 10.2.3
<PackageReference Include="SiLA2.StateMachine" Version="10.2.3" />
<PackageVersion Include="SiLA2.StateMachine" Version="10.2.3" />
<PackageReference Include="SiLA2.StateMachine" />
paket add SiLA2.StateMachine --version 10.2.3
#r "nuget: SiLA2.StateMachine, 10.2.3"
#:package SiLA2.StateMachine@10.2.3
#addin nuget:?package=SiLA2.StateMachine&version=10.2.3
#tool nuget:?package=SiLA2.StateMachine&version=10.2.3
SiLA2.StateMachine
A state machine module for orchestrating multi-step SiLA2 lab automation workflows. Built on the Stateless library, it provides a fluent API for defining workflows with automatic retry policies, OpenTelemetry tracing, and dependency injection support.
| NuGet Package | SiLA2.StateMachine |
| Repository | https://gitlab.com/SiLA2/sila_csharp |
| SiLA Standard | https://sila-standard.com |
| License | MIT |
Features
- Fluent Builder API: Define states, transitions, and actions with method chaining
- Async Entry/Exit Actions: Execute SiLA2 service calls on state transitions
- Workflow Context: Thread-safe data sharing between workflow steps
- Retry Policies: Configurable retry with constant or exponential backoff
- Auto-Advance: Automatic state progression via completion triggers
- Progress Events: Subscribe to workflow state changes and errors
- OpenTelemetry: Built-in tracing via
ActivitySource - DOT Graph Export: Visualize state machines with Graphviz
Installation
dotnet add package SiLA2.StateMachine
Platform Compatibility
| Target Framework | Supported |
|---|---|
| .NET 10.0+ | Yes (full feature support) |
| .NET Standard 2.0 | Yes |
| .NET Framework 4.6.1+ | Yes (via netstandard2.0) |
| .NET Core 2.0+ | Yes (via netstandard2.0) |
This package multi-targets net10.0 and netstandard2.0, allowing it to be used from both modern .NET and legacy .NET Framework projects.
Quick Start
using SiLA2.StateMachine;
using SiLA2.StateMachine.Models;
// Define states and triggers
enum State { Idle, Processing, Done, Error }
enum Trigger { Start, Complete, Fault }
// Build workflow
var workflow = new SilaWorkflowBuilder<State, Trigger>()
.StartIn(State.Idle)
.Permit(State.Idle, Trigger.Start, State.Processing)
.Permit(State.Processing, Trigger.Complete, State.Done)
.Permit(State.Processing, Trigger.Fault, State.Error)
.InState(State.Processing)
.OnEntry(async (ctx, sp, ct) =>
{
// Call SiLA2 services here
ctx.Set("result", 42);
})
.OnCompleted(Trigger.Complete)
.OnError(Trigger.Fault, maxRetries: 3, retryDelayMs: 1000)
.Build();
// Run workflow
await workflow.FireAsync(Trigger.Start);
Console.WriteLine($"State: {workflow.CurrentState}"); // Done
Console.WriteLine($"Result: {workflow.Context.Get<int>("result")}"); // 42
Core Concepts
States and Triggers
States and triggers are defined as enums (or structs). The workflow transitions between states when triggers are fired.
enum AssayState { Idle, Aspirating, Dispensing, Incubating, Measuring, Done, Error }
enum AssayTrigger { Start, AspirateComplete, DispenseComplete, IncubateComplete, MeasureComplete, Fault }
Workflow Builder
Use the fluent builder to configure states, transitions, and actions:
var workflow = new SilaWorkflowBuilder<AssayState, AssayTrigger>()
.StartIn(AssayState.Idle)
// Define transitions
.Permit(AssayState.Idle, AssayTrigger.Start, AssayState.Aspirating)
.Permit(AssayState.Aspirating, AssayTrigger.AspirateComplete, AssayState.Dispensing)
// Configure state behavior
.InState(AssayState.Aspirating)
.WithDescription("Aspirate sample from source plate")
.OnEntry(async (ctx, sp, ct) =>
{
var liquidHandler = sp.GetRequiredService<ILiquidHandlerClient>();
await liquidHandler.AspirateAsync(100.0, ct);
ctx.Set("aspiratedVolume", 100.0);
})
.OnCompleted(AssayTrigger.AspirateComplete)
.OnError(AssayTrigger.Fault, maxRetries: 2, retryDelayMs: 1000)
.Build(services, logger);
Workflow Context
The IWorkflowContext is a thread-safe data bag for passing values between states:
// In state A
ctx.Set("temperature", 37.5);
ctx.Set("sampleId", "SAMPLE-001");
// In state B
var temp = ctx.Get<double>("temperature");
if (ctx.TryGet<string>("sampleId", out var id))
{
Console.WriteLine($"Processing {id}");
}
Error Policies
Configure retry behavior for transient failures:
.InState(State.Measuring)
.OnEntry(async (ctx, sp, ct) => { /* may fail */ })
.OnError(Trigger.Fault, WorkflowErrorPolicy.ExponentialBackoff(
maxRetries: 5,
initialDelay: TimeSpan.FromSeconds(1),
multiplier: 2.0))
| Policy | Description |
|---|---|
NoRetry |
Fail immediately (default) |
SimpleRetry(n, delay) |
Retry n times with constant delay |
ExponentialBackoff(n, delay, mult) |
Retry with increasing delays |
Progress Events
Subscribe to workflow progress:
workflow.ProgressChanged += (sender, progress) =>
{
Console.WriteLine($"{progress.FromState} → {progress.ToState}");
if (progress.Exception != null)
Console.WriteLine($"Error: {progress.Exception.Message}");
};
ASP.NET Core Integration
// Program.cs
using SiLA2.StateMachine.Extensions;
builder.Services.AddSilaStateMachine();
// Or register a pre-configured workflow
builder.Services.AddSilaWorkflow<AssayState, AssayTrigger>(builder =>
{
builder
.StartIn(AssayState.Idle)
.Permit(AssayState.Idle, AssayTrigger.Start, AssayState.Processing)
// ... configure workflow
});
// In a controller or service
public class AssayController
{
private readonly SilaWorkflowBuilder<AssayState, AssayTrigger> _builder;
private readonly IServiceProvider _services;
private readonly ILogger<AssayController> _logger;
public AssayController(
SilaWorkflowBuilder<AssayState, AssayTrigger> builder,
IServiceProvider services,
ILogger<AssayController> logger)
{
_builder = builder;
_services = services;
_logger = logger;
}
public async Task<IActionResult> RunAssay()
{
var workflow = _builder.Build(_services, _logger);
await workflow.FireAsync(AssayTrigger.Start);
return Ok(new { Status = workflow.Status, State = workflow.CurrentState });
}
}
OpenTelemetry Integration
The library emits traces via System.Diagnostics.ActivitySource:
// Subscribe to workflow traces
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing.AddSource("SiLA2.StateMachine"); // Activity source name
});
Traced activities:
Workflow.Run- Full workflow executionWorkflow.Fire- Individual trigger firingWorkflow.StateEntry- State entry action execution (with retry attempts)
Advanced Patterns
Hierarchical States
.SubstateOf(State.Processing, State.Running)
Conditional Transitions
.PermitIf(State.Idle, Trigger.Start, State.FastPath,
() => _config.UseFastPath, "Fast path enabled")
.PermitIf(State.Idle, Trigger.Start, State.SlowPath,
() => !_config.UseFastPath, "Slow path enabled")
DOT Graph Export
Generate Graphviz diagrams:
var dotGraph = workflow.ToDotGraph();
File.WriteAllText("workflow.dot", dotGraph);
// Render with: dot -Tpng workflow.dot -o workflow.png
Builder Methods
| Method | Description |
|---|---|
StartIn(state) |
Set initial state (required) |
InState(state) |
Begin configuring a state |
WithDescription(text) |
Add description to current state |
OnEntry(action) |
Async action when entering state |
OnExit(action) |
Async action when leaving state |
OnCompleted(trigger) |
Auto-fire trigger after successful entry |
OnError(trigger, policy) |
Error trigger and retry policy |
Permit(from, trigger, to) |
Define transition |
PermitIf(from, trigger, to, guard) |
Conditional transition |
PermitReentry(state, trigger) |
Self-transition |
Ignore(state, trigger) |
Ignore trigger in state |
SubstateOf(sub, super) |
Hierarchical state |
Build(services, logger, context) |
Create workflow instance |
Workflow Status
| Status | Description |
|---|---|
Created |
Workflow built but not started |
Running |
Workflow is executing |
Paused |
Workflow paused (reserved) |
Completed |
All states finished successfully |
Aborted |
Workflow aborted via AbortAsync() |
Faulted |
Unrecoverable error occurred |
Working Example
See the complete plate assay workflow example:
Path: src/Examples/WorkflowOrchestration/
dotnet run --project src/Examples/WorkflowOrchestration/SiLA2.StateMachine.Example.csproj
This example demonstrates:
- Multi-step lab workflow (Discover → Aspirate → Dispense → Incubate → Measure → Report)
- Service discovery simulation
- Context data passing between states
- Retry policies for transient failures
- Progress event handling
- Serilog logging integration
Troubleshooting
| Error | Cause | Solution |
|---|---|---|
Initial state not set |
Missing StartIn() |
Call StartIn(state) before Build() |
No state selected |
Missing InState() |
Call InState(state) before OnEntry() |
Trigger not permitted |
Invalid transition | Check CanFire() or add Permit() |
Max auto-advance depth |
Infinite loop | Ensure workflow reaches terminal state |
Related Packages
- SiLA2.Client - Server discovery and gRPC channel management
- SiLA2.Core - Core server implementation
- SiLA2.AspNetCore - ASP.NET Core integration
License
MIT License - See LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. 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 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.4)
- Stateless (>= 5.20.1)
-
net10.0
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.4)
- Stateless (>= 5.20.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.