[RQ7.15] The ResponseSPDU shall be built by the SafetyProvider by copying RequestSPDU.MonitoringNumber and RequestSPDU.SafetyConsumerID into the ResponseSPDU. In addition, SPDU_ID, Flags, the SafetyData and the NonSafetyData shall be updated. Finally, ResponseSPDU.CRC shall be calculated and appended.
Figure 20 gives an overview over the task of building the ResponseSPDU.
Figure 20 – Overview of task for SafetyProvider
For the ResponseSPDU.Flags, see 7.2.1.6. For the calculation of the SPDU_ID, see 7.2.3.2. For the calculation of the CRC, see 7.2.3.6.
[RQ7.16] SPDU_ID_1, SPDU_ID_2 and SPDU_ID_3 shall be calculated according to Figure 21 and Table 36.
Figure 21 – Calculation of the SPDU_ID
Table 36 – Presentation of the SPDU_ID
SPDU_ID_1:= SafetyBaseID (octets 0…3) XOR SafetyProviderLevel_ID (octets 0…3) |
SPDU_ID_2:= SafetyBaseID (octets 4…7) XOR SafetyStructureSignature (octets 0…3) |
SPDU_ID_3:= SafetyBaseID (octets 8…11) XOR SafetyBaseID (octets 12…15) XOR SafetyProviderID (octets 0…3) |
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_ID_1, SPDU_ID_2, and SPDU_ID_3 differ, there probably is a mismatching SafetyBaseID.
- If SPDU_ID_3 differs, but SPDU_ID_1 and SPDU_ID_2 do not, there is a mismatching SafetyProviderID.
- If SPDU_ID_2 differs, but SPDU_ID_1 and SPDU_ID_3 do not, the structure or identifier of the SafetyData do not match.
- If SPDU_ID_1 differs, but SPDU_ID_2 and SPDU_ID_3 do not, the SafetyProviderLevel does not match.
By 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.
Figure 22 shows a concrete example of the calculation of SPDU_ID_1, SPDU_ID_2 and SPDU_ID_3. The following input values were chosen for the calculation: SafetyBaseID is the GUID “72962B91-FA75-4AE6-8D28-B404DC7DAF63”, SafetyProviderID has the value 0xE0EA6B40, SafetyStructureSignature has the value 0xDE7329FD and the SafetyProviderLevel_ID is chosen as 0xDEAA9DEE (representing SIL 3).
See OPC 10000-6, 5.2.2.6 for details on the encoding of Guids. The octets from the resulting octet stream are used according to Figure 21, i.e. in “reverse order”.
The resulting SPDU_IDs are as follows: SPDU_ID_1 has the value of 0xAC3CB67F, SPDU_ID_2 has the value of 0x9495D388 and SPDU_ID_3 has the value of 0x87F13E11.
Figure 22 (informative) – Example for the calculation of SPDU_ID_1, SPDU_ID_2 and SPDU_ID_3
The SafetyProviderLevel is the SIL the SafetyProvider implementation (hardware and software) is capable of.
Table 37 – Coding for the SafetyProviderLevel_ID
SafetyProviderLevel |
Value of SafetyProviderLevel_ID |
0x119128810x647C46540xDEAA9DEE0xAB47F33B |
[RQ7.17] Exactly one of the values provided in Table 37 shall be used as constant code value for SafetyProviderLevel_ID. The values were chosen in such a way that the hamming distance between them becomes maximal (hamming distance of 21).
[RQ7.18] 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 SIL 1 to SIL 3 should not be able to accidently use the value 0xAB47F33B used for SIL 4. 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 3.1.2.12.
SafetyStructureSignature is used to check the number, DataTypes, 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, the identifier of the Structure DataType (SafetyStructureIdentifier) is also taken into account when calculating SafetyStructureSignature. This ensures that the SafetyProvider and the SafetyConsumer are using the same identifier for the Structure DataType, 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 DataType “vec3D_in” where the vector components are given in inches, or even at a SafetyConsumer expecting a Structure of DataType “orientation”, containing three Floats to define an orientation using Euler angles.
[RQ7.19] SafetyStructureSignature shall be calculated as a 32 bit CRC signature (polynomial: 0x F4ACFB13, see Clause B.1) over SafetyStructureIdentifier (encoding: UTF-8), SafetyStructureSignatureVersion and the sequence of the DataTypeEncodingIDs. After each DataTypeEncodingID, a 16-bit zero-value (0x0000) shall be inserted. All integers shall be encoded using little endian octet ordering. Data shall be processed in reverse order, see Clause B.1. The value “0” shall not be used as a signature. Instead, the value “1” shall be used in this case.
The terminating zero of SafetyStructureIdentifier shall not be considered when calculating the CRC.
[RQ7.20] SafetyStructureIdentifier may be visible in the OPC UA Information Model for diagnostic purposes but shall not be evaluated by the SafetyConsumer during runtime.
[RQ7.21] For all releases up to Release 2.0 of the specification, the value for SafetyStructureSignatureVersion shall be 0x0001.
Example:
SafetyStructureIdentifier,e.g. “Motörhead” = 0x4d 0x6f 0x74 0xc3 0xb6 0x72 0x68 0x65 0x61 0x64
SafetyStructureSignatureVersion:= 0x0001
1. DataType Int16: (DataTypeEncodingId = 0x0004), // see 6.2.5
2. DataType Boolean: (DataTypeEncodingId = 0x0001),
3. DataType Float: (DataTypeEncodingId =0x000a)
= CRC32_Backward(0x4d, 0x6f, 0x74, 0xc3, 0xb6, 0x72, 0x68, 0x65, 0x61, 0x64,
0x01,0x00,
0x04,0x00, 0x00,0x00,
0x01,0x00, 0x00,0x00,
0x0A, 0x00, 0x00, 0x00)
= CRC32_Forward(
0x00, 0x00, 0x00, 0x0A,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x04,
0x00,0x01,
0x64, 0x61, 0x65, 0x68, 0x72, 0xb6, 0xc3, 0x74, 0x6f, 0x4d)
= 0xe2e86173
NOTE 1 The insertion of 0x0000 values after each DataTypeEncodingID allows for introducing arrays in later versions of this document.
NOTE 2 SafetyStructureSignatureVersion is the version of the procedure used to calculate the signature, as defined in requirement RQ7.19. If future releases of this document define an alternative procedure, they will indicate this by using a different version number.
OPC 10000-3, 5.8.2 defines different categories of DataTypes. Regarding the DataTypeEncodingID which is to be used within the SafetyStructureSignature, the following holds:
- For Built-in DataTypes, the ID from Table 1 of OPC 10000-6 is used as DataTypeEncodingID.
- For Simple DataTypes, the DataTypeEncodingID of the Built-in DataType from which they are derived is used.
- As of now, Structured DataTypes (including OptionSets) shall not be used within SafetyData. Arrays are not supported. Instead, multiple variables of the same type are used.
- Enumeration DataTypes are encoded on the wire as Int32 and therefore shall use the DataTypeEncodingID of the Int32 Built-in DataType.
The SafetyProvider calculates the CRC signature (ResponseSPDU.CRC) and sends it to the SafetyConsumer as part of the ResponseSPDU. This enables the SafetyConsumer to check the correctness of the ResponseSPDU including the SafetyData, flags, MNR, SafetyConsumerID and SPDU_ID by recalculating the CRC signature (CRC_calc).
[RQ7.22] The generator polynomial 0x F4ACFB13 shall be used for the 32-Bit CRC signature.
[RQ7.23] If SafetyData is longer than one octet (e.g. if it is of DataType UInt16, Int16 or Float), it shall be decoded and encoded using little endian order in which the least significant octet appears first in the incremental memory address stream.
[RQ7.24] The calculation sequence shall begin with the highest memory address (n) of the STrailer counting back to the lowest memory address (0) and then include also the SafetyData beginning with the highest memory address.
Figure 23 shows the calculation sequence of a ResponseSPDU CRC on a little-endian machine, using an example SafetyData with the following fields:
Int32var1
UInt32var2
UInt16var3
Int16var4
Booleanvar5
For the example given above, the STrailer (without CRC) and SafetyData have a combined length of 34 octets (16 octets STrailer without CRC, 12 octets of SafetyData). The calculation of ResponseSPDU.CRC (SafetyProvider) or CRC_calc (SafetyConsumer) is done in reverse order, from bottom to top. In the example shown in Figure 23, CRC calculation starts at octet index 33 (most significant octet of the MNR) and ends at octet index 0.
NOTE The reverse order ensures that the effectiveness of the CRC mechanism remains independent of any CRCs used within the underlying OPC UA channel, even if it would coincidentally use the same CRC polynomial.
Figure 23 – Calculation of the CRC (on little-endian machines, CRC32_Backward)
An alternative way to calculate the CRC (particularly useful on big-endian machines) is shown in Figure 24. Here, the individual elements of the ResponseSPDU are already arranged in memory in reversed order, and CRC calculation is executed from octet 0 to octet 33.
Figure 24 – Calculation of the CRC (on big-endian machines, CRC32_Forward)
[RQ7.25] On the SafetyConsumer, CRC_calc shall be calculated using data received in the ResponseSPDU, and not from expected values.