[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>