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