The two SCL-services “SafetyProvider” and “SafetyConsumer” are specified using state diagrams.

Figure 15 shows the sequence of request and response with SafetyData and the timeouts for OPC UA Safety.

image018.png

NOTE: Transmission errors are handled within the OPC UA stack (e.g. when using client/server over TCP) and do not have to be corrected or re-transmitted by OPC UA Safety.

Figure 15 – Sequence diagram for OPC UA Safety

The SafetyConsumerTimeout is the watchdog time checked in the SafetyConsumer. The watchdog is restarted whenever a new RequestSPDU is generated (transitions T14 and T26 of the SafetyConsumer). If an appropriate ResponseSPDU is received in time, and the checks for data integrity, authenticity, and timeliness are all valid, the timer will not expire before it is restarted.

Otherwise, the watchdog timer expires, and the SafetyConsumer triggers a safe reaction. To duly check its timer, the SafetyConsumer is executed cyclically, with period ConsumerCycleTime. ConsumerCycleTime is expected to be smaller than SafetyConsumerTimeout.

The ConsumerCycleTime is the maximum time for the cyclic update of the SafetyConsumer. It is the timeframe from one call of the SafetyConsumer to the next call of the SafetyConsumer. The implementation and error reaction of ConsumerCycleTime is not part of OPC UA Safety; it is vendor specific.

[RQ8.10] Figure 16 shows a simplified representation of the state diagram of the SafetyProvider. The exact behavior is described in Table 21, Table 22, and Table 23. The SafetyProvider shall implement that behavior. It is not required to literally follow the entries given in the tables, if the behavior does not change.

image019.png

Figure 16 – Simplified representation of the state diagram for the SafetyProvider

Graphical representation

Type

Description

image020.png

Activity State

Within these interruptible "activity" states the SafetyProvider waits for new inputs.

image021.png

Action State

Within these non-interruptible "action" states events like new request is deferred until the next "activity" state is reached, see [1].

The transitions are fired in case of an event, for example receiving a SPDU. In case of several possible transitions, so-called guard conditions (refer to […] in UML diagrams) define which transition to fire

The diagram consists of activity and action states. Activity states are surrounded by bold lines, action states are surrounded by thin lines. While activity states may be interruptible by new events, action states are not. External events occurring while the state machine is in an action state, are deferred until the next activity state is reached.

Table 20 – Symbols used for state machines.

Table 21 – SafetyProvider instance internal items

INTERNAL ITEMS

TYPE

DEFINITION

RequestSPDU_i

Variable

Local Memory for RequestSPDU (required to react on changes).

<Get RequestSPDU>

Macro

Instruction to take the whole RequestSPDU from the OPC UA Mapper.

<Set ResponseSPDU>

Macro

Instruction to transfer the whole ResponseSPDU to the OPC UA Mapper

<build ResponseSPDU>

Macro

Take the MNR and the SafetyConsumerID of the received RequestSPDU. Add the SPDU_ID_1, SPDU_ID_2, SPDU_ID_3, Flags, and SafetyData, as well as the calculated CRC.

See Clause 8.1.3.1

Table 22 – States of SafetyProvider instance

STATE NAME

STATE DESCRIPTION

Initialization

// Initial state

SAPI.SafetyData:= 0SAPI.MonitoringNumber:= 0SAPI.SafetyConsumerID:= 0

RequestSPDU_i:= 0

S1_WaitForRequest

// waiting on next RequestSPDU from SafetyConsumer

<Get RequestSPDU>

S2_PrepareSPDU

ResponseSPDU.Flags.ActivateFSV := SAPI.ActivateFSVResponseSPDU.Flags.OperatorAckProvider := SAPI.OperatorAckProvider Response.Flags.TestModeActivated := SAPI.EnableTestMode

<build ResponseSPDU> // see Clause 8.1.3.1

Table 23 – SafetyProvider driver transitions

TRAN-SITION

SOURCE STATE

TARGET STATE

GUARD CONDITION

ACTIVITY

T1

Init

1

-

T2

1

2

// RequestSPDU received

<Get RequestSPDU>

When: [RequestSPDU_i<> RequestSPDU]

// Process Request

RequestSPDU_i:= RequestSPDU

SAPI.MonitoringNumber:= RequestSPDU.MonitoringNumber

SAPI.SafetyConsumerID := RequestSPDU.SafetyConsumerID

T3

2

1

// SPDU is prepared

-

<Set ResponseSPDU>

[RQ8.11] Figure 17 shows a simplified representation of the state diagram of the SafetyConsumer. The exact behavior is described in Table 24, Table 25, and Table 26. The SafetyConsumer shall implement that behavior. It is not required to literally follow the entries given in the tables, if the behavior does not change.

image022.png

Figure 17 – Principle state diagram for SafetyConsumer

Table 24 – SafetyConsumer driver internal items

INTERNAL ITEMS

TYPE

DEFINITION

Constants

MNR_min := 0x100

UInt32

// 0x100 is the start value for MNR, also used after wrap-around.

// The values 0…0xFF are reserved for future use.

Variables

FaultReqOA_i

Boolean

Local memory for errors which request operator acknowledgment.

MNR_i

UInt32

Local Monitoring Number (MNR).

prevMNR_i

UInt32

Local memory for previous MNR

SafetyProviderID_i

UInt32

Local memory for SafetyProviderID in use.

CRCCheck_i

Boolean

Local variable used to store the result of the CRC-check.

SPDUCheck_i

Boolean

Local variable used to store the result of the additional SPDU-checks.

SPDU_ID_1_i

UInt32

Local variable to store the expected SPDU_ID_1

SPDU_ID_2_i

UInt32

Local variable to store the expected SPDU_ID_2

SPDU_ID_3_i

UInt32

Local variable to store the expected SPDU_ID_3

Timers

ConsumerTimer

Timer

This timer is used to check whether the next valid ResponseSPDU has arrived on time. It is initialized using the parameter SPI.SafetyConsumerTimeOut.

ErrorIntervalTimer

Timer

This timer is initialized using the parameter SPI.SafetyErrorIntervalLimit.

See Table 17, Clause 7.4.2, and Clause 11.4 for more information.

Macros <...><...>

<risingEdge x>

Macro

// detection of a rising edge:

If x==true && tmp==false Then result:= trueElse result := falseEndif

tmp := x

<Get ResponseSPDU>

Macro

Instruction to take the whole ResponseSPDU from the OPC UA Mapper.

<Use FSV>

Macro

SafetyData is set to binary 0

SAPI.FSV_Activated := 1

RequestSPDU.Flags.FSV_Activated := 1

NOTE: If a safety application prefers different fail-safe values than binary 0, this can be implemented in the safety application by querying SAPI.FSV_Activated.

<Use SafetyData>

Macro

SAPI.SafetyData is set to ResponseSPDU. SafetyData

SAPI.FSV_Activated := 0

RequestSPDU.Flags.FSV_Activated := 0

RequestSPDU.Flags.CommunicationError:=0

<Set RequestSPDU>

Macro

Instruction to transfer the whole RequestSPDU to the OPC UA Mapper

<(Re)Start ConsumerTimer>

Macro

Restarts the consumer timer.

<(Re)Start ErrorIntervalTimer>

Macro

Restarts the error interval timer.

<ConsumerTimer expired?>

Macro

Yields “true” if the timer is running longer than SPI.SafetyConsumerTimeOut since last restart, “false” otherwise.

<ErrorIntervalTimer expired?>

Macro

Yields “true” if the timer is running longer than SPI.SafetyErrorIntervalLimit since last restart, “false” otherwise.

<Build RequestSPDU>

Macro

RequestSPDU.SafetyConsumerID := SPI.SafetyConsumerID

RequestSPDU.MonitoringNumber := MNR_i

<Calc SPDU_ID_i>

Macro

uint128 BaseID

uint32 ProviderID

const uint32 SafetyProviderLevel_ID := … // see Clause 8.1.3.3

If(SAPI.SafetyBaseID == 0) thenBaseID := SPI.SafetyBaseIDElse BaseID := SAPI.SafetyBaseID

Endif If(SAPI.SafetyProviderID == 0) thenProviderID := SPI.SafetyProviderIDElse ProviderID := SAPI.SafetyProviderID

Endif

SPDU_ID_1_i := BaseID (bytes 0…3) XOR SafetyProviderLevel_ID

SPDU_ID_2_i := BaseID (bytes 4…7) XOR SPI.SafetyStructureSignature

SPDU_ID_3_i := BaseID (bytes 8…11) XOR BaseID (bytes 12…15) XOR ProviderID

// see Clause 8.1.3.2 for clarification

<Set Diag(ID, Boolean permanent)>

Macro

// ID is the identifier for the type of diagnostic output, see Table 29// permanent is used to indicate a permanent error. // Only one diagnostic message is created for multiple permanent // errors in sequence

If(RequestSPDU.Flags.CommunicationError == 0) Then <do vendor-specific function for diagnostic output using ID>Else //do nothingEndif

RequestSPDU.Flags.CommunicationError:= permanent

// Note: See for possible values for “ID” and their codes.

External Event

Restart Cycle

Event

The external call of SafetyConsumer can be interpreted as event “Restart Cycle”

Note: A macro is a shorthand representation for operations described in the according definition.

Table 25 – SafetyConsumer driver states

STATE NAME

STATE DESCRIPTION

Initialization

// Initial state of the SafetyConsumer driver instance.

<Use FSV> SAPI.OperatorAckRequested := 0RequestSPDU.Flags.OperatorAckRequested :=0SAPI.OperatorAckProvider := 0FaultReqOA_i :=0SAPI.TestModeActivated := 0RequestSPDU.Flags.CommunicationError:= 0

S11_Wait for (Re)Start

// Safety Layer is waiting (Re)Start

S12_initialize MNR

// Use previous MNR if known// or random MNR within the allowed range (e.g. after cold start), see Clause 11.2.

MNR_i := (previous MNR_i if known) or (random MNR)

MNR_i := max(MNR_i, MNR_min)

S13_PrepareRequest

// Build RequestSPDU and send (done in T16)

S14_WaitForChangedSPDU

// Safety Layer is waiting on next ResponseSPDU from SafetyProvider

S15_CRCCheckSPDU

// Check CRC

uint32 CRC_calcCRCCheck_i := (CRC_calc == ResponseSPDU.CRC)

// see Clause 8.1.3.5 on how to calculate CRC_calc

S16_CheckResponseSPDU

// Check SafetyConsumerID and SPDU_ID and MNR (see T22, T23, T24)

SPDUCheck_i := ResponseSPDU.SPDU_ID_1== SPDU_ID_1_i && ResponseSPDU.SPDU_ID_2== SPDU_ID_2_i && ResponseSPDU.SPDU_ID_3== SPDU_ID_3_i && ResponseSPDU.SafetyConsumerID== SPI.SafetyConsumerID && ResponseSPDU.MNR==MNR_i

S17_Error

SAPI.TestModeActivated := 0

S18_ProvideSafetyData

// Provide SafetyData to the application program

Table 26 – SafetyConsumer driver transitions

TRANSITION

SOURCE STATE

TARGET STATE

GUARD CONDITION

ACTIVITY

T12

Init

S11

-

T13

S11

S12

//Start

[SAPI.Enable==1]

<(Re)Start ErrorIntervalTimer><calc SPDU_ID>// see Clause 8.1.3.2 for clarification

T14

S12

S13

// MNR initialized

<(Re)Start ConsumerTimer>

T15

S18

S11

// Termination

[SAPI.Enable==0]

<Use FSV>

T16

S13

S14

// Build Request SPDU and send

prevMNR_i := MNR_i,

If MNR_i== 0xFFFFFFFFFThen MNR_i := MNR_min,Else

MNR_i := MNR_i + 1Endif

<Build RequestSPDU>

<Set RequestSPDU>

T17

S14

S15

// New ResponseSPDU received

<Get ResponseSPDU>

[ResponseSPDU.MNR<>prevMNR_i]

-

T18

S14

S17

// WDTimeout

[<ConsumerTimer expired?>]

<Set Diag(CommErrTO,1)><use FSV>

If SPI.SafetyOperatorAckNecessary == 1Then FaultReqOA_i := 1Else // do nothingEndif

T19

S15

S13

// When CRC err and SafetyErrorIntervalTimer expired[(crcCheck_i == 0

) && <ErrorIntervalTimer expired?>]

<(Re)Start ErrorIntervalTimer><Set Diag(CRCerrIgn,0)>

T20

S15

S17

// When CRC err and SafetyErrorIntervalTimer not expired

[(crcCheck_i == 0

) && not <ErrorIntervalTimer expired?>]

<(Re)Start ErrorIntervalTimer><Set Diag(CRCerrOA,1)><use FSV>

FaultReqOA_i:= 1

T21

S15

S16

// When CRCCheckOK

[crcCheck_i == 1

]

-

T22

S16

S18

// SPDU OK

[SPDUCheck_i==1]

// For clarification, refer to Figure 18

// indicate OA from provider

SAPI.OperatorAckProvider := ResponseSPDU.Flags.OperatorAckProvider

// OA requested due to edge at ActivateFSV?

If (<risingEdge ResponseSPDU.Flags.ActivateFSV>&& SPI.SafetyOperatorAckNecessary == 1)Then FaultReqOA_i:=1;<Set Diag(FSV_Requested,1)> Else // do nothing

Endif

// Set Flags if OA requested:

If FaultReqOA_i==1 Then SAPI.OperatorAckRequested:= 1, RequestSPDU.Flags.OperatorAckRequested:=1, FaultReqOA_i:= 0Else //do nothingEndif

// Reset flags after OA:

If (<risingEdge SAPI.OperatorAckConsumer >) Then SAPI.OperatorAckRequested:=0, RequestSPDU.Flags.OperatorAckRequested:=0Else // do nothingEndif If SAPI.OperatorAckRequested==1 || ResponseSPDU.ActivateFSV==1Then <use FSV> Else <use SafetyData>Endif

// Notify safety application that SafetyProvider is in test mode:

SAPI.TestModeActivated:= ResponseSPDU.Flags.TestModeActivated

T23

S16

S13

// SPDU NOK and SafetyErrorIntervalTimer expired[SPDUCheck_i == 0 && <ErrorIntervalTimer expired?>]

<(Re)Start ErrorIntervalTimer>,// Send diagnostic message according the// detected error:

If ResponseSPDU.SafetyConsumerID<> SPI.SafetyConsumerIDThen <Set Diag(CoIDerrIgn,0)>ElseIf ResponseSPDU.MNR<>MNR_iThen <Set Diag(MNRerrIgn,0)> Else //do nothing EndIf If ResponseSPDU.SPDU_ID_1<> SPDU_ID_1_i || ResponseSPDU.SPDU_ID_2<> SPDU_ID_2_i || ResponseSPDU.SPDU_ID_3<> SPDU_ID_3_iThen <Set Diag(SD_IDerrIgn,0)> Else // do nothing Endif Endif

T24

S16

S17

// SPDU NOK and SafetyErrorIntervalTimer not expired

[SPDUCheck_i == 0 && not <ErrorIntervalTimer expired?>]

<(Re)Start ErrorIntervalTimer>// Send diagnostic message according the // detected error:

If ResponseSPDU.SafetyConsumerID<> SPI.SafetyConsumerIDThen <Set Diag(CoIDerrIgn,1)>Else If ResponseSPDU.MNR<>MNR_iThen <Set Diag(MNRerrIgn,1)> Else //do nothing Endif If ResponseSPDU.SPDU_ID_1<> SPDU_ID_1_i || ResponseSPDU.SPDU_ID_2<> SPDU_ID_2_i || ResponseSPDU.SPDU_ID_3<> SPDU_ID_3_iThen <Set Diag(SD_IDerrIgn,1)> <use FSV> Else //do nothing Endif Endif

FaultReqOA_i:= 1

T25

S17

S18

// SPDU NOK

-

T26

S18

S13

// Restart Cycle

[SAPI.Enable==1]

<(Re)Start ConsumerTimer>

Figure 18 shows the sequence after a second ResponseSPDU error was detected before the timer SafetyErrorIntervalTimer stops.

image023.png

Figure 18 – Sequence diagram for OA

After the error is gone the sequence follows the logic of T22 in Table 26.