Zum Inhalt springen

Anleitung

Zustandsautomaten

Endliche Zustandsautomaten im Server über Enumerationen und Attribute definieren und ihre Zustände zur Laufzeit wechseln.

Anleitung

Zustandsautomaten ​

Beteiligte TypenOpcFiniteStateMachineNodeOpcStateMachineStatesAttributeOpcStateMachineTransitionAttributeOpcStateMachineTransitionsAttributeOpcStateMachineInitialStateAttribute

Viele Informationsmodelle beschreiben den Zustand einer Maschine oder eines Ablaufs über einen endlichen Zustandsautomaten (FiniteStateMachineType gemäß OPC UA Teil 16). Ein solcher Automat kennt eine feste Menge von Zuständen (engl. states) und die erlaubten Übergänge (engl. transitions) zwischen ihnen. Seinen aktuellen Zustand stellt er über die Kind-Nodes CurrentState und LastTransition bereit, die Clients lesen und abonnieren können. Im Server definieren Sie einen solchen Automaten über die Klasse OpcFiniteStateMachineNode<T>.

Zustände definieren ​

Die Zustände legen Sie über eine Enumeration fest. Der Wert eines Eintrags ist die Nummer des Zustands, sein Name der Name des Zustands:

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

Zustandsautomat-Klasse definieren ​

Den Zustandsautomaten selbst definieren Sie als Unterklasse der Klasse OpcFiniteStateMachineNode<T> und beschreiben ihn über Attribute:

  • OpcStateMachineStates legt die Enumeration der Zustände fest.
  • OpcStateMachineTransition legt je einen erlaubten Übergang von einem Zustand in einen anderen fest.
  • OpcStateMachineInitialState legt den Zustand fest, den der Automat beim Anlegen im Adressraum einnimmt. Ohne dieses Attribut nimmt er den ersten seiner Zustände ein.
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

Wenn die Zustände in der Reihenfolge der Enumeration aufeinander folgen, genügt statt der einzelnen Übergänge das Attribut OpcStateMachineTransitions. Es legt die Übergänge von jedem Zustand zum nächsten fest, mit IsLoop zudem den Übergang vom letzten zum ersten Zustand:

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

Zustandsautomat im Adressraum anlegen ​

Den Zustandsautomaten legen Sie wie jeden anderen Node im Node-Manager an, zum Beispiel als Kind-Node der Maschine, deren Zustand er beschreibt:

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")

Zustand wechseln ​

Den Zustand wechseln Sie über die Methode ChangeStateTo. Sie übernimmt die Änderung und informiert alle Clients, die den Zustand abonniert haben. Wenn es keinen Übergang vom aktuellen in den gewünschten Zustand gibt, löst die Methode eine InvalidOperationException aus. Ob ein Wechsel erlaubt ist, ermittelt vorab die Methode CanChangeStateTo. Wenn der Automat bereits im gewünschten Zustand ist, bleibt der Aufruf ohne Wirkung:

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)

Den aktuellen Zustand liefert die Eigenschaft CurrentState, den zuletzt ausgeführten Übergang die Eigenschaft LastTransition. Wenn ein Automat aus einem Informationsmodell den Kind-Node CurrentState nicht unterstützt, kann eine Unterklasse das über die Eigenschaft SupportsCurrentState angeben. Zustandswechsel bleiben dann ohne Wirkung.