头图

OPC (OLE for Process Control) 是一种工业通讯协议的标准,用于实现不同制造商的设备和系统之间的数据交换。它主要用于工业自动化系统中。OPC标准有几个不同的规范,包括OPC DA (Data Access)、OPC UA (Unified Architecture)、OPC HDA (Historical Data Access) 等。

本文主要介绍 OPC UA over TCP、OPC UA Secure Conversation的报文格式。

1. OPC Classic (包括OPC DA, OPC HDA等)

OPC Classic是基于Microsoft的COM/DCOM(组件对象模型/分布式组件对象模型)技术,因此它并没有一个类似于TCP/IP的统一、标准化的“报文格式”。
在OPC Classic中,数据交换和通信是通过COM/DCOM机制实现的,这意味着数据是以COM对象的形式进行传输的,而不是通过某种特定的、固定格式的报文。

2.OPC UA (Unified Architecture)

OPC UA是一种更现代的协议,设计用来取代OPC Classic,提供更加安全、跨平台的数据交换机制。OPC UA定义了一套详细的服务和信息模型,使得它可以用于不同的传输层,比如TCP、HTTP等。
OPC UA通信模型中,客户端和服务器之间的交互是基于一系列的服务请求和响应。每个服务请求和响应都遵循OPC UA定义的编码规则,可以序列化为二进制流或者XML。
常见的OPC UA报文主要分为两类 OPC UA over TCP、OPC UA Secure Conversation

2.1 OPC UA over TCP报文结构

OPC UA over TCP报文包括消息头和消息体,主要结构如下:

用途消息头消息体
长度8byte不定
描述控制和描述报文实际要传输的数据,其内容和结构取决于具体的OPC UA服务请求或响应

2.1.1消息头

其中,消息头部分报文结构如下:

用途消息类型保留段消息大小
长度3byte1byte4byte
描述用于标识报文类型如果消息类型是OPC UA链接协议支持的值之一,则设置为“F”的ACSII码整个消息头+消息体的长度,单位为字节

消息类型部分共分四类:

  • HEL:表示消息体为Hello报文
  • ACK:表示消息体为Acknowledge报文
  • ERR:表示消息体为Error报文
  • RHE:表示消息体为ReverseHello报文

2.1.2 消息体

2.1.2.1 Hello报文

当消息类型为HEL时,代表消息体部分为一个Hello报文,具体格式如下:

用途长度描述
协议版本号4byte这个字段指示发送方使用的OPC UA规范的版本。接收方可以用这个信息来判断是否能够理解接收到的报文。
接收缓冲区大小4byte指定了接收方准备为此连接分配的最大消息大小。它用于流控制和避免接收方被过大的消息所淹没。单位为字节,该值必须大于8192。
发送缓冲区大小4byte发送缓冲区大小。这个字段指定了发送方准备为此连接使用的最大消息大小。这也是流控制的一部分,确保双方都能处理交换的数据。单位为字节,该值必须大于8192。
最大消息大小4byte这个字段指定了双方允许的最大消息体的大小。它用于防止因处理过大的单个消息而导致的性能问题。0表示客户端不限制。
最大分块数量4byte这个字段指定了应答报文可以被分割成的最大块数。这有助于管理大量数据的传输,确保即使是大消息也可以在双方之间有效地传输。0表示客户端不限制。
终端URL最大4096byte客户端希望连接到的终端的URL。 如果长度超过4096 或无法识别URL所标识的资源,服务器应返回Bad_TcpEndpointUrlInvalid错误消息并关闭连接。

Hello报文是OPC UA TCP协议握手过程的一个重要部分,通过它,客户端和服务器可以交换基本的通信参数,为后续的更复杂交互建立基础。

2.1.2.2Acknowledge报文
用途长度描述
协议版本号4byte服务端支持的OPC UA协议的版本
接收缓冲区大小4byte指定了接收方准备为此连接分配的最大消息大小。它用于流控制和避免接收方被过大的消息所淹没。单位为字节,该值必须大于8192。
发送缓冲区大小4byte发送缓冲区大小。这个字段指定了发送方准备为此连接使用的最大消息大小。这也是流控制的一部分,确保双方都能处理交换的数据。单位为字节,该值必须大于8192。
最大消息大小4byte这个字段指定了双方允许的最大消息体的大小。它用于防止因处理过大的单个消息而导致的性能问题。0表示客户端不限制。
最大分块数量4byte这个字段指定了应答报文可以被分割成的最大块数。这有助于管理大量数据的传输,确保即使是大消息也可以在双方之间有效地传输。0表示客户端不限制。

Acknowledge报文提供了客户端和服务器之间通信所需的基本参数,确保双方能够有效地交换后续的OPC UA消息。客户端在收到Acknowledge报文后,会根据提供的参数调整自己的通信设置,随后双方可以开始正式的数据交换。

2.1.2.3 Error报文
用途长度描述
错误码4byte错误的数字代码。
原因最大4096byte错误的详细描述。

错误码会随着版本更新而更新,这里提供一份当前版本(UA-1.05.03-2023-12-15)的错误码列表,具体可以参看官方github

错误名错误码
Good0x00000000
Uncertain0x40000000
Bad0x80000000
BadUnexpectedError0x80010000
BadInternalError0x80020000
BadOutOfMemory0x80030000
BadResourceUnavailable0x80040000
BadCommunicationError0x80050000
BadEncodingError0x80060000
BadDecodingError0x80070000
BadEncodingLimitsExceeded0x80080000
BadRequestTooLarge0x80B80000
BadResponseTooLarge0x80B90000
BadUnknownResponse0x80090000
BadTimeout0x800A0000
BadServiceUnsupported0x800B0000
BadShutdown0x800C0000
BadServerNotConnected0x800D0000
BadServerHalted0x800E0000
BadNothingToDo0x800F0000
BadTooManyOperations0x80100000
BadTooManyMonitoredItems0x80DB0000
BadDataTypeIdUnknown0x80110000
BadCertificateInvalid0x80120000
BadSecurityChecksFailed0x80130000
BadCertificatePolicyCheckFailed0x81140000
BadCertificateTimeInvalid0x80140000
BadCertificateIssuerTimeInvalid0x80150000
BadCertificateHostNameInvalid0x80160000
BadCertificateUriInvalid0x80170000
BadCertificateUseNotAllowed0x80180000
BadCertificateIssuerUseNotAllowed0x80190000
BadCertificateUntrusted0x801A0000
BadCertificateRevocationUnknown0x801B0000
BadCertificateIssuerRevocationUnknown0x801C0000
BadCertificateRevoked0x801D0000
BadCertificateIssuerRevoked0x801E0000
BadCertificateChainIncomplete0x810D0000
BadUserAccessDenied0x801F0000
BadIdentityTokenInvalid0x80200000
BadIdentityTokenRejected0x80210000
BadSecureChannelIdInvalid0x80220000
BadInvalidTimestamp0x80230000
BadNonceInvalid0x80240000
BadSessionIdInvalid0x80250000
BadSessionClosed0x80260000
BadSessionNotActivated0x80270000
BadSubscriptionIdInvalid0x80280000
BadRequestHeaderInvalid0x802A0000
BadTimestampsToReturnInvalid0x802B0000
BadRequestCancelledByClient0x802C0000
BadTooManyArguments0x80E50000
BadLicenseExpired0x810E0000
BadLicenseLimitsExceeded0x810F0000
BadLicenseNotAvailable0x81100000
BadServerTooBusy0x80EE0000
GoodPasswordChangeRequired0x00EF0000
GoodSubscriptionTransferred0x002D0000
GoodCompletesAsynchronously0x002E0000
GoodOverload0x002F0000
GoodClamped0x00300000
BadNoCommunication0x80310000
BadWaitingForInitialData0x80320000
BadNodeIdInvalid0x80330000
BadNodeIdUnknown0x80340000
BadAttributeIdInvalid0x80350000
BadIndexRangeInvalid0x80360000
BadIndexRangeNoData0x80370000
BadIndexRangeDataMismatch0x80EA0000
BadDataEncodingInvalid0x80380000
BadDataEncodingUnsupported0x80390000
BadNotReadable0x803A0000
BadNotWritable0x803B0000
BadOutOfRange0x803C0000
BadNotSupported0x803D0000
BadNotFound0x803E0000
BadObjectDeleted0x803F0000
BadNotImplemented0x80400000
BadMonitoringModeInvalid0x80410000
BadMonitoredItemIdInvalid0x80420000
BadMonitoredItemFilterInvalid0x80430000
BadMonitoredItemFilterUnsupported0x80440000
BadFilterNotAllowed0x80450000
BadStructureMissing0x80460000
BadEventFilterInvalid0x80470000
BadContentFilterInvalid0x80480000
BadFilterOperatorInvalid0x80C10000
BadFilterOperatorUnsupported0x80C20000
BadFilterOperandCountMismatch0x80C30000
BadFilterOperandInvalid0x80490000
BadFilterElementInvalid0x80C40000
BadFilterLiteralInvalid0x80C50000
BadContinuationPointInvalid0x804A0000
BadNoContinuationPoints0x804B0000
BadReferenceTypeIdInvalid0x804C0000
BadBrowseDirectionInvalid0x804D0000
BadNodeNotInView0x804E0000
BadNumericOverflow0x81120000
BadLocaleNotSupported0x80ED0000
BadNoValue0x80F00000
BadServerUriInvalid0x804F0000
BadServerNameMissing0x80500000
BadDiscoveryUrlMissing0x80510000
BadSempahoreFileMissing0x80520000
BadRequestTypeInvalid0x80530000
BadSecurityModeRejected0x80540000
BadSecurityPolicyRejected0x80550000
BadTooManySessions0x80560000
BadUserSignatureInvalid0x80570000
BadApplicationSignatureInvalid0x80580000
BadNoValidCertificates0x80590000
BadIdentityChangeNotSupported0x80C60000
BadRequestCancelledByRequest0x805A0000
BadParentNodeIdInvalid0x805B0000
BadReferenceNotAllowed0x805C0000
BadNodeIdRejected0x805D0000
BadNodeIdExists0x805E0000
BadNodeClassInvalid0x805F0000
BadBrowseNameInvalid0x80600000
BadBrowseNameDuplicated0x80610000
BadNodeAttributesInvalid0x80620000
BadTypeDefinitionInvalid0x80630000
BadSourceNodeIdInvalid0x80640000
BadTargetNodeIdInvalid0x80650000
BadDuplicateReferenceNotAllowed0x80660000
BadInvalidSelfReference0x80670000
BadReferenceLocalOnly0x80680000
BadNoDeleteRights0x80690000
UncertainReferenceNotDeleted0x40BC0000
BadServerIndexInvalid0x806A0000
BadViewIdUnknown0x806B0000
BadViewTimestampInvalid0x80C90000
BadViewParameterMismatch0x80CA0000
BadViewVersionInvalid0x80CB0000
UncertainNotAllNodesAvailable0x40C00000
GoodResultsMayBeIncomplete0x00BA0000
BadNotTypeDefinition0x80C80000
UncertainReferenceOutOfServer0x406C0000
BadTooManyMatches0x806D0000
BadQueryTooComplex0x806E0000
BadNoMatch0x806F0000
BadMaxAgeInvalid0x80700000
BadSecurityModeInsufficient0x80E60000
BadHistoryOperationInvalid0x80710000
BadHistoryOperationUnsupported0x80720000
BadInvalidTimestampArgument0x80BD0000
BadWriteNotSupported0x80730000
BadTypeMismatch0x80740000
BadMethodInvalid0x80750000
BadArgumentsMissing0x80760000
BadNotExecutable0x81110000
BadTooManySubscriptions0x80770000
BadTooManyPublishRequests0x80780000
BadNoSubscription0x80790000
BadSequenceNumberUnknown0x807A0000
GoodRetransmissionQueueNotSupported0x00DF0000
BadMessageNotAvailable0x807B0000
BadInsufficientClientProfile0x807C0000
BadStateNotActive0x80BF0000
BadAlreadyExists0x81150000
BadTcpServerTooBusy0x807D0000
BadTcpMessageTypeInvalid0x807E0000
BadTcpSecureChannelUnknown0x807F0000
BadTcpMessageTooLarge0x80800000
BadTcpNotEnoughResources0x80810000
BadTcpInternalError0x80820000
BadTcpEndpointUrlInvalid0x80830000
BadRequestInterrupted0x80840000
BadRequestTimeout0x80850000
BadSecureChannelClosed0x80860000
BadSecureChannelTokenUnknown0x80870000
BadSequenceNumberInvalid0x80880000
BadProtocolVersionUnsupported0x80BE0000
BadConfigurationError0x80890000
BadNotConnected0x808A0000
BadDeviceFailure0x808B0000
BadSensorFailure0x808C0000
BadOutOfService0x808D0000
BadDeadbandFilterInvalid0x808E0000
UncertainNoCommunicationLastUsableValue0x408F0000
UncertainLastUsableValue0x40900000
UncertainSubstituteValue0x40910000
UncertainInitialValue0x40920000
UncertainSensorNotAccurate0x40930000
UncertainEngineeringUnitsExceeded0x40940000
UncertainSubNormal0x40950000
GoodLocalOverride0x00960000
GoodSubNormal0x00EB0000
BadRefreshInProgress0x80970000
BadConditionAlreadyDisabled0x80980000
BadConditionAlreadyEnabled0x80CC0000
BadConditionDisabled0x80990000
BadEventIdUnknown0x809A0000
BadEventNotAcknowledgeable0x80BB0000
BadDialogNotActive0x80CD0000
BadDialogResponseInvalid0x80CE0000
BadConditionBranchAlreadyAcked0x80CF0000
BadConditionBranchAlreadyConfirmed0x80D00000
BadConditionAlreadyShelved0x80D10000
BadConditionNotShelved0x80D20000
BadShelvingTimeOutOfRange0x80D30000
BadNoData0x809B0000
BadBoundNotFound0x80D70000
BadBoundNotSupported0x80D80000
BadDataLost0x809D0000
BadDataUnavailable0x809E0000
BadEntryExists0x809F0000
BadNoEntryExists0x80A00000
BadTimestampNotSupported0x80A10000
GoodEntryInserted0x00A20000
GoodEntryReplaced0x00A30000
UncertainDataSubNormal0x40A40000
GoodNoData0x00A50000
GoodMoreData0x00A60000
BadAggregateListMismatch0x80D40000
BadAggregateNotSupported0x80D50000
BadAggregateInvalidInputs0x80D60000
BadAggregateConfigurationRejected0x80DA0000
GoodDataIgnored0x00D90000
BadRequestNotAllowed0x80E40000
BadRequestNotComplete0x81130000
BadTransactionPending0x80E80000
BadTicketRequired0x811F0000
BadTicketInvalid0x81200000
BadLocked0x80E90000
BadRequiresLock0x80EC0000
GoodEdited0x00DC0000
GoodPostActionFailed0x00DD0000
UncertainDominantValueChanged0x40DE0000
GoodDependentValueChanged0x00E00000
BadDominantValueChanged0x80E10000
UncertainDependentValueChanged0x40E20000
BadDependentValueChanged0x80E30000
GoodEdited_DependentValueChanged0x01160000
GoodEdited_DominantValueChanged0x01170000
GoodEdited_DominantValueChanged_DependentValueChanged0x01180000
BadEdited_OutOfRange0x81190000
BadInitialValue_OutOfRange0x811A0000
BadOutOfRange_DominantValueChanged0x811B0000
BadEdited_OutOfRange_DominantValueChanged0x811C0000
BadOutOfRange_DominantValueChanged_DependentValueChanged0x811D0000
BadEdited_OutOfRange_DominantValueChanged_DependentValueChanged0x811E0000
GoodCommunicationEvent0x00A70000
GoodShutdownEvent0x00A80000
GoodCallAgain0x00A90000
GoodNonCriticalTimeout0x00AA0000
BadInvalidArgument0x80AB0000
BadConnectionRejected0x80AC0000
BadDisconnect0x80AD0000
BadConnectionClosed0x80AE0000
BadInvalidState0x80AF0000
BadEndOfStream0x80B00000
BadNoDataAvailable0x80B10000
BadWaitingForResponse0x80B20000
BadOperationAbandoned0x80B30000
BadExpectedStreamToBlock0x80B40000
BadWouldBlock0x80B50000
BadSyntaxError0x80B60000
BadMaxConnectionsReached0x80B70000
UncertainTransducerInManual0x42080000
UncertainSimulatedValue0x42090000
UncertainSensorCalibration0x420A0000
UncertainConfigurationError0x420F0000
GoodCascadeInitializationAcknowledged0x04010000
GoodCascadeInitializationRequest0x04020000
GoodCascadeNotInvited0x04030000
GoodCascadeNotSelected0x04040000
GoodFaultStateActive0x04070000
GoodInitiateFaultState0x04080000
GoodCascade0x04090000
BadDataSetIdInvalid0x80E70000
2.1.2.4ReverseHello报文
用途长度描述
服务器URI最大4096byte发送消息的服务器的ApplicationUri。
终端URL最大4096byte客户端在建立SecureChannel时使用的端点的URL

对于基于连接的协议,如TCP,ReverseHello消息允许防火墙后面的服务器没有打开端口连接到客户端,并请求客户端使用服务器创建的套接字建立SecureChannel。

对于基于消息的协议,ReverseHello消息允许服务器向客户端通告它们的存在。在这种情况下,终端URL指定服务器的特定地址和访问它所需的任何令牌。

2.2 OPC UA Secure Conversation报文结构

OPC UA Secure Conversation(OPC UA 安全会话)的报文格式设计用于在客户端与服务器之间建立和维护一个加密和签名的通信通道。

用途消息头安全头序列头载荷安全脚
长度12byte不定8byte不定不定
描述控制和描述报文包含安全相关的信息。根据对称、不对称安全算法有不同长度包括序列号和请求ID这是实际的应用数据部分,根据安全头中定义的安全策略,它可能被加密和/或签名。(可选)如果报文被签名,这部分包含签名。不是所有的安全策略都需要签名。

2.2.1 消息头

用途消息类型是否终结消息大小安全通道ID
长度3byte1byte4byte4byte
描述用于标识报文类型一个字节的ASCII代码,指示是否是消息中的最后一个块。从消息头开始的长度,单位为字节服务器分配的SecureChannel的唯一标识符
2.2.1.1 消息类型

消息类型主要有三种:

  • MSG 使用与通道有关的密钥加密的消息
  • OPN 打开安全通道消息
  • CLO 关闭安全通道消息
2.2.1.2 是否终结

一个字节的ASCII代码,指示MessageChunk是否是消息中的最后一个块。

定义为以下值:

  • C 中间块。
  • F 最后一个块。
  • A 最后一个块(当发生错误并且消息被中止时使用)。

此字段仅对消息类型是MSG有意义,对于其他消息类型,此字段始终为“F”。

2.2.1.3 消息大小

从消息头开始的长度,单位为字节

2.2.1.4 安全通道ID

服务器分配的SecureChannel的唯一标识符,如果服务器接收到无法识别的安全用户ID,则应返回相应的传输层错误。

2.2.2 安全头

定义了消息应用了哪些加密操作,有两个版本:非对称算法安全头和对称算法安全头。

2.2.2.1 非对称算法安全头
用途长度描述
安全策略URI长度4byte安全策略URI的长度。如果未指定URI,则此值可能为0或-1。其他负值无效。单位为字节。
安全策略URI不定用于保护消息的安全策略的URI。此字段编码为不带空结束符的UTF-8字符串。
发送方证书长度4byte发送方证书的长度。如果未指定证书,则此值可能为0或-1。其他负值无效。单位为字节。
发送方证书不定发送方证书
接收方证书指纹长度4byte接收方证书指纹的长度,如果已加密,则此字段的值为20。如果未加密,则值可以是0或-1。单位为字节。
接收方证书指纹不定接收方证书指纹。如果消息未加密,则此字段应为空。
2.2.2.2对称算法安全头
用途长度描述
令牌ID4byte用于保护消息的安全通道安全令牌的唯一标识符。如果服务器接收到它无法识别的令牌ID,它将返回相应的传输层错误。

2.2.3 序列头

用途长度描述
序列号4byte由发送方分配的单调递增序列号。
请求ID4byte由客户端分配给OPC UA请求消息的标识符。请求和相关响应的所有消息都使用相同的标识符。

2.2.4 载荷

这是报文的主体部分,包含了实际的操作请求或响应数据。载荷的大小是可变的,取决于实际传输的数据量。

2.2.5 安全脚

这部分是可选的,仅在使用某些特定的安全策略时才存在。它包含了额外的安全信息,比如填充数据和签名。安全脚大小同样是可变的,取决于使用的安全策略和数据。


杜衡老师
1 声望2 粉丝

« 上一篇
Modbus报文详解
下一篇 »
S7Comm报文详解

引用和评论

0 条评论