# State machines

> How-to guide: Define finite state machines in the server with enumerations and attributes, and change their states at runtime.

> This content was machine translated and may contain errors. [Read in German](https://www.traeger.de/docs/development/opcua/server/state-machines)

**Sample applications:**

- Server.StateMachine ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Scenarios/Server.StateMachine), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Scenarios/Server.StateMachine)): This use case demonstrates how to implement a finite state machine node with custom states and how the server changes its state.
- Client.StateMachine ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Scenarios/Client.StateMachine), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Scenarios/Client.StateMachine)): This use case demonstrates how a client monitors the current state of a state machine provided by the server.

Types used: `OpcFiniteStateMachineNode`, `OpcStateMachineStatesAttribute`, `OpcStateMachineTransitionAttribute`, `OpcStateMachineTransitionsAttribute`, `OpcStateMachineInitialStateAttribute`

Many information models describe the state of a machine or a process with a finite state machine (FiniteStateMachineType as defined in OPC UA Part 16). Such a state machine has a fixed set of states and the allowed transitions between them. It provides its current state through the child nodes CurrentState and LastTransition, which clients can read and subscribe to. In the server, you define such a state machine with the `OpcFiniteStateMachineNode<T>` class.

## Define the states

You define the states with an enumeration. The value of an entry is the number of the state, and its name is the name of the state:

```csharp
public enum MachineState : uint
{
    Stopped = 0,
    Starting = 1,
    Running = 2,
    Stopping = 3
}
```

```vbnet
Public Enum MachineState As UInteger
    Stopped = 0
    Starting = 1
    Running = 2
    Stopping = 3
End Enum

```

## Define the state machine class

You define the state machine itself as a subclass of the `OpcFiniteStateMachineNode<T>` class and describe it with attributes:

- `OpcStateMachineStates` defines the enumeration of the states.
- `OpcStateMachineTransition` defines one allowed transition from one state to another.
- `OpcStateMachineInitialState` defines the state that the state machine takes when it is created in the address space. Without this attribute, it takes the first of its states.

```csharp
[OpcStateMachineStates(typeof(MachineState))]
[OpcStateMachineTransition(MachineState.Stopped, MachineState.Starting)]
[OpcStateMachineTransition(MachineState.Starting, MachineState.Running)]
[OpcStateMachineTransition(MachineState.Running, MachineState.Stopping)]
[OpcStateMachineTransition(MachineState.Stopping, MachineState.Stopped)]
[OpcStateMachineInitialState(MachineState.Stopped)]
public class MachineStateMachineNode : OpcFiniteStateMachineNode<MachineState>
{
    public MachineStateMachineNode(IOpcNode parent, OpcName name)
        : base(parent, name)
    {
    }
}
```

```vbnet
<OpcStateMachineStates(GetType(MachineState))>
<OpcStateMachineTransition(MachineState.Stopped, MachineState.Starting)>
<OpcStateMachineTransition(MachineState.Starting, MachineState.Running)>
<OpcStateMachineTransition(MachineState.Running, MachineState.Stopping)>
<OpcStateMachineTransition(MachineState.Stopping, MachineState.Stopped)>
<OpcStateMachineInitialState(MachineState.Stopped)>
Public Class MachineStateMachineNode
    Inherits OpcFiniteStateMachineNode(Of MachineState)

    Public Sub New(ByVal parent As IOpcNode, ByVal name As OpcName)
        MyBase.New(parent, name)
    End Sub
End Class
```

If the states follow each other in the order of the enumeration, the `OpcStateMachineTransitions` attribute is enough instead of the individual transitions. It defines the transitions from each state to the next one and, with `IsLoop`, also the transition from the last state to the first one:

```csharp
// Transitions from each state to the next one and from the last state to the first one.
[OpcStateMachineStates(typeof(MachineState))]
[OpcStateMachineTransitions(typeof(MachineState), IsLoop = true)]
public class MachineCycleNode : OpcFiniteStateMachineNode<MachineState>
{
    public MachineCycleNode(IOpcNode parent, OpcName name)
        : base(parent, name)
    {
    }
}
```

```vbnet
'Transitions from each state to the next one and from the last state to the first one.
<OpcStateMachineStates(GetType(MachineState))>
<OpcStateMachineTransitions(GetType(MachineState), IsLoop:=True)>
Public Class MachineCycleNode
    Inherits OpcFiniteStateMachineNode(Of MachineState)

    Public Sub New(ByVal parent As IOpcNode, ByVal name As OpcName)
        MyBase.New(parent, name)
    End Sub
End Class
```

## Create the state machine in the address space

You create the state machine in the node manager like any other node, for example as a child node of the machine whose state it describes:

```csharp
// MyNodeManager.CreateNodes(...)
var machineNode = new OpcObjectNode(new OpcName("Machine", this.DefaultNamespaceIndex));
var machineStateNode = new MachineStateMachineNode(machineNode, "State");
```

```vbnet
'MyNodeManager.CreateNodes(...)
Dim machineNode = New OpcObjectNode(New OpcName("Machine", Me.DefaultNamespaceIndex))
Dim machineStateNode = New MachineStateMachineNode(machineNode, "State")
```

## Change the state

You change the state with the `ChangeStateTo` method. It applies the change and notifies all clients that subscribe to the state. If there is no transition from the current state to the requested state, the method throws an `InvalidOperationException`. The `CanChangeStateTo` method determines beforehand whether a change is allowed. If the state machine is already in the requested state, the call has no effect:

```csharp
if (machineStateNode.CanChangeStateTo(server.SystemContext, MachineState.Starting))
    machineStateNode.ChangeStateTo(server.SystemContext, MachineState.Starting);
 
// Throws an InvalidOperationException, because there is no transition
// from the state Starting to the state Stopped.
machineStateNode.ChangeStateTo(server.SystemContext, MachineState.Stopped);

```

```vbnet
If machineStateNode.CanChangeStateTo(server.SystemContext, MachineState.Starting) Then
    machineStateNode.ChangeStateTo(server.SystemContext, MachineState.Starting)
End If

'Throws an InvalidOperationException, because there is no transition
'from the state Starting to the state Stopped.
machineStateNode.ChangeStateTo(server.SystemContext, MachineState.Stopped)
```

The `CurrentState` property returns the current state, and the `LastTransition` property returns the most recent transition. If a state machine from an information model does not support the CurrentState child node, a subclass can indicate this with the `SupportsCurrentState` property. State changes then have no effect.
