Figure 13 shows the structure of a RequestSPDU which originates at the SafetyConsumer and contains a SafetyConsumerID, a MonitoringNumber (MNR), and one byte of (non-safety-related) flags (Flags).

image016.png

Figure 13 – RequestSPDU

NOTE: The RequestSPDU does not contain a CRC-checksum.

Figure 14 shows the structure of a ResponseSPDU which originates at the SafetyProvider and contains the safety data (1 – 1500 Byte) and additional 25 Byte safety code (STrailer) as described in the subsequent sections, and the non-safety related data.

image017.png

Figure 14 – ResponseSPDU

NOTE: In order to avoid spurious trips, the ResponseSPDU is transmitted in an atomic (consistent) way from the OPC UA platform interface of the SafetyProvider to the OPC UA platform interface of the SafetyConsumer. This is the task of the respective OPC UA mapper, see Figure 2.

Identifier of the SafetyConsumer instance, for diagnostic purposes, see Clause 11.1.2.

The SafetyConsumer uses the MNR to detect mis-timed SPDUs, e.g. such SPDUs which are continuously repeated by an erroneous network storing element. A different MNR is used in every RequestSPDU of a given SafetyConsumer, and a ResponseSPDU will only be accepted, if its MNR is identical to its matching RequestSPDU.

The checking for correctness of the MNR is performed by the SafetyConsumer, only.

[RQ8.1] The flags of the Safety Consumer (RequestSPDU.Flags) shall be used as shown in Table 18.

Table 18 – Structure of RequestSPDU.Flags

Bit nr.

Identifier

Description

LSB =

Bit 0

CommunicationError

0: No error

1: An error was detected in the previous ResponseSPDU.

Bit 1

OperatorAckRequested

Used to inform the SafetyProvider that operator acknowledgment is requested.

Bit 2

FSV_Activated

Is used for conformance test of SafetyConsumer.SAPI.FSV_Activated

Bit 3......7

Reserved for future use

Always set to zero, not evaluated.

NOTE: CommunicationError can be used as a trigger, e.g. for a communication analysis tool.

Flags reserved for future use shall be set to zero by the SafetyConsumer and shall not be evaluated by the SafetyProvider.

[RQ8.2] SafetyData shall contain the safety-related application data transmitted from the SafetyProvider to the SafetyConsumer. It may comprise multiple basic OPC UA variables (see Clause 6.4). For the sake of reducing distinctions of cases, SafetyData shall always be a structure, even if it contains a single basic OPC UA variable, only.

For the calculation of the CRC Signature, the order in which this data is processed by the calculation is important. SafetyProvider and SafetyConsumer must agree upon the number, type and order of application data transmitted in SafetyData. The sequence of SafetyData is fixed.

NOTE SafetyData may contain qualifier bits for a fine-grained activation of fail-safe substitute values. For a valid process value, the respective qualifier is set to 1 (good), whereas the value 0 (bad) is used for invalid values. Invalid process values are replaced by a fail-safe substitute value in the consumer’s safety application. See Clause 5.3.

[RQ8.3] The flags of the SafetyProvider (ResponseSPDU.Flags) shall be used as shown in Table 19.

Table 19 – Structure of ResponseSPDU.Flags 

Bit nr.

Name

Description

LSB =

Bit 0

OperatorAckProvider

Operator acknowledgment at the provider, hereby forwarded to the SafetyConsumer, see OperatorAckProvider in the SAPI of the SafetyProvider, Clause 7.3.1.

Bit 1

ActivateFSV

Activation of fail-safe values by the safety application at the SafetyProvider, hereby forwarded to the SafetyConsumer, see ActivateFSV in the SAPI of the SafetyProvider, Clause 7.3.1.

Bit 2

TestModeActivated

Enabling and disabling of test mode in the SafetyProvider, hereby forwarded to the SafetyConsumer, see EnableTestMode in the SAPI of the SafetyProvider, Clause 7.3.1.

Bit 3 ...... 7

Reserved for future use

Always set to zero, not evaluated.

[RQ8.4] Flags reserved for future use shall be set to zero by the SafetyProvider and shall not be evaluated by the SafetyConsumer.

This field is used by the SafetyConsumer to check whether the ResponseSPDU is coming from the correct SafetyProvider. For details, see Clause 8.1.3.1.

[RQ8.5] The SafetyConsumerID in the ResponseSPDU shall be a copy of the SafetyConsumerID received in the corresponding RequestSPDU. See Clause 8.1.3.1.

[RQ8.6] The MonitoringNumber in the ResponseSPDU shall be a copy of the MonitoringNumber received in the corresponding RequestSPDU. See Clause 8.1.3.1.

The SafetyConsumer uses the ResponseSPDU.MonitoringNumber to detect mis-timed SPDUs, e.g. such SPDUs which are continuously repeated by an erroneous network storing element. A different MonitoringNumber is used in every RequestSPDU of a given SafetyConsumer, and a ResponseSPDU will only be accepted, if its MonitoringNumber is identical to its matching RequestSPDU.

[RQ8.7] This CRC-checksum shall be used to detect data corruption. See Clause 8.1.3.5 on how it is calculated in the SafetyProvider and how it is checked in the SafetyConsumer.

[RQ8.8] This structure shall be used to transmit non-safety data values (e.g. diagnostic information) together with safe data consistently. Non-safety data is not CRC-protected and may stem from an unsafe source. [RQ8.9] When presented to the safety application (e.g. at an output of the SafetyConsumer), non-safety values shall clearly be indicated as “non-safety”, by an appropriate vendor-specific mechanism (e.g. by using a different color).

To avoid possible problems with empty structures, the dummy structure NonSafetyDataPlaceholder shall be used when no non-safety data is used.

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.

[RQ8.12] ResponseSPDU shall be built by the SafetyProvider by copying RequestSPDU.MonitoringNumber and the RequestSPDU.SafetyConsumerID into the ResponseSPDU. After this, SPDU_ID, Flags, and the SafetyData shall be updated. Finally, ResponseSPDU.CRC shall be calculated and appended.

image024.png

Figure 19 – Overview of task for SafetyProvider

For the ResponseSPDU.Flags, see Clause 8.1.1.5. For the calculation of the SPDU_ID, see Clause 8.1.3.2. For the calculation of CRC, see Clause 8.1.3.5.

[RQ8.13] The SPDU_ID_1-3 shall be calculated according to Figure 20 and Table 27.

image025.png

Figure 20 – Calculation of the SPDU_ID

Table 27 – Presentation of the SPDU_ID

SPDU_ID_1 := SafetyBaseID (bytes 0…3) XOR SafetyProviderLevel_ID

SPDU_ID_2 := SafetyBaseID (bytes 4…7) XOR SafetyStructureSignature

SPDU_ID_3 := SafetyBaseID (bytes 8…11) XOR SafetyBaseID (bytes 12…15) XOR SafetyProviderID

NOTE: In case of a mismatch between expected SPDU_ID and actual SPDU_ID, the following rules can be used for diagnostic purposes:

  • If all of SPDU_ID1, SPDU_ID2, and SPDU_ID3 differ, there is a mismatching SafetyBaseID.
  • If SPDU_ID3 differs, but SPDU_ID1 and SPDU_ID2 do not, there is a mismatching SafetyProviderID.
  • If SPDU_ID2 differs, but SPDU_ID1 and SPDU_ID3 do not, the structure or identifier of the safety data do not match.
  • If SPDU_ID3 differs, but SPDU_ID1 and SPDU_ID2 do not, the SafetyProviderLevel does not match.

By using these rules, there is a very low probability (<10-9) that a mismatching SafetyBaseID will be misinterpreted. From a practical view, this probability can be ignored.

Table 28 – Coding for the SafetyProviderLevel_ID

SafetyProviderLevel

Value of SafetyProviderLevel_ID

Up to SIL1Up to SIL2Up to SIL3Up to SIL4

0x119128810x647C46540xDEAA9DEE0xAB47F33B

[RQ8.14] Exactly one of the values provided in Table 28 shall be used as constant code value for SafetyProviderLevel_ID. They were chosen in such a way that the hamming distance becomes maximal (hamming distance of 21).

[RQ8.15] Measures shall be taken to avoid that a SafetyProvider is erroneously using a code-value belonging to a SIL that is higher than the SIL it is capable of. For instance, a SafetyProvider capable of SIL1-3 should not be able to accidently use the value 0xAB47F33B used for SIL4. One way to achieve this is to avoid that this constant appears in the source code of the SafetyProvider at all.

The SafetyProviderLevel is independent to the SIL capability of the provided SafetyData, see Clause 7.3.3.

SafetyStructureSignature is used to check the number, data types and order of application data transmitted in SafetyData. If the SafetyConsumer is expecting anything different than what the SafetyProvider actually provides, SafetyStructureSignature will differ, allowing the SafetyConsumer to enable fail-safe substitute values.

In addition, also the identifier of the structure type (StructureIdentifier) is taken into account when calculating SafetyStructureSignature. This ensures that the SafetyProvider and SafetyConsumer are using the same identifier for the structure type, effectively avoiding any confusion.

For instance, if a SafetyProvider defines a structure with identifier “vec3D_m” comprising three floats containing a three-dimensional vector in the metric system, this structure could not be used by a SafetyConsumer expecting a structure of type “vec3D_in” where the vector components are given in inch, or even at a SafetyConsumer expecting a structure of type “orientation”, containing three floats to define an orientation using Euler angles.

[RQ8.16] StructureSignature shall be calculated as CRC32-signature (polynomial: 0x F4ACFB13, see Annex B.1) over StructureIdentifier (encoding: UTF-8), StructureSignatureVersion and the sequence of the DataType IDs. After each datatype ID, a 16-bit zero-value (0x0000) shall be inserted.

The terminating zero of StructureIdentifier shall not be considered when calculating the CRC.

[RQ8.17] StructureIdentifier shall be visible in the OPC UA information model for diagnostic purposes, but shall not be evaluated by the SafetyConsumer during runtime.

[RQ8.18] For version V1.0 of the specification, the value for StructureSignatureVersion shall be 0x0001.

Example:

StructureIdentifier, e.g. “foo” = 0x66,0x6f,0x6f

StructureSignatureVersion:= 0x0001

1. DataType Int16: (Id = 0x0004), // see Clause 6.4

2. DataType Boolean: (Id = 0x0001),

3. DataType Float32: (Id =0x000A)

StructureSignature := CRC32(0x66,0x6f,0x6f, 0x00,0x01,0x00,0x00, 0x00,0x04, 0x00,0x00, 0x00,0x01, 0x00,0x00, 0x00,0x0A)

NOTE: The insertion of 0x0000 values before the DataType ID, allows for introducing arrays in later version of OPC UA Safety.

The DataType ID can be found at the DataType or at the derived DataType.

The OPC UA Information model supports not only built-in DataTypes, but also allows for DataTypes derived from built-in DataTypes. In case of derived DataTypes, the Data Structure CRC uses the ID of a built-in DataType (which is found at the end of the tree).

Example: the base type "enumeration" is derived from the DataType Int32 (ID=6); therefore, an ID of 6 is used whenever the DataType “enumeration” is used in SafetyData.

In this version of the specification, arrays are not supported. Instead, multiple variables of the same type are used.

The SafetyProvider calculates the CRC signature (ResponseSPDU.CRC) and sends it to the SafetyConsumer as part of SPDU. This enables the SafetyConsumer to check the correctness of the SPDU including the SafetyData, Flags, MNR, SafetyConsumerID and SPDU_ID by recalculating the CRC signature (CRC_calc).

[RQ8.19] The generator polynomial 0x F4ACFB13 shall be used for the 32-Bit CRC signature.

[RQ8.20] If SafetyData is longer than one byte (e.g. UInt16, Int16, Float32), it shall be decoded and encoded using big-endian order in which the least significant byte appears last in the incremental memory address stream.

[RQ8.21] The calculation sequence shall begin with the highest memory address (n) of the SafetyData counting back to the lowest memory address (0) and then include also the STrailer beginning with the highest memory address.

Figure 21 shows the calculation sequence of the CRC_SPDU using an example SafetyData with the following fields:

Boolean var1

UInt16var2

Int16 var3

UInt32var4

Int32 var5

The STrailer and SafetyData have a total length of 34 bytes. The calculation of ResponseSPDU.CRC (SafetyProvider) or CRC_calc (SafetyConsumer) is done in reverse order, i.e. starts at byte 33 and ends at byte 0.

image026.png

Figure 21 – Calculation of the CRCr

For devices where the SafetyData remains at the same value for a longer period of time, it is a viable optimization to store the calculated CRC over the SafetyData and take – in case the SafetyData hasn’t changed, this stored CRC as start value for the CRC calculation of the STrailer.

Note: On the SafetyConsumer, CRC_calc is calculated using data received in the ResponseSPDU, and not from expected values.