open62541 (R 1.1.2)中文文档 (译文)第三篇 (7 - 10)

本文深入探讨了OPC-UA协议中的服务体系结构,包括发现、安全通道、会话等服务集,以及信息建模的概念与节点、引用的构成。强调了服务调用在通信中的核心作用,并介绍了客户端配置与生命周期管理,为理解和应用OPC-UA提供了详尽指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

open62541(R 1.1.2) 文档

*注:原文PDF文档 是从官网下载的 Linux64bit的发布版本中自带的文档,原PDF中的源代码用PDF浏览器查看,有残缺。需要结合源文件中的示例代码进行相应的修改。或参考其它版本的文档。原文代码中的注释并没有译文,这个在使用时再补充进去。
第一篇 章节 一 至 五

第二篇 章节 六

第三篇 章节 七 至 十

第四篇 章节 十一 至 十三

第三篇目录

7、服务

在OPC-UA中,所有通信都基于服务调用,每个呼叫都由请求和响应消息组成。这些消息定义为具有二进制编码的数据结构,并在 Generated Data Type Definitions 中列出。由于所有服务都是在标准中预先定义的,因此用户不能修改它们。但是,您可以 Call 服务调用服务器上的用户定义方法。

以下服务签名是内部的,用户不可见。不过,我们在这里介绍它们,以概述OPC UA的功能。请参阅 ClientServer API,其中服务暴露给最终用户。有关服务及其行为的权威定义,请参阅OPC UA标准第4部分。
大多数服务都将作为服务器、当前会话和请求和响应结构的指针。返回可能的错误代码作为响应的一部分。

typedef void (*UA_Service)(UA_Server*, UA_Session*,
	const void* request, void* response);
typedef void (*UA_ChannelService)(UA_Server*, UA_SecureChannel*,
	const void* request, void* response);

7.1 发现服务集

此服务集定义用于发现服务器实现的端点并读取这些端点的安全配置的服务。

7.1.1 FindServer

返回服务器或发现服务器已知的服务器。客户端可以通过指定筛选条件来减少返回的结果数

void Service_FindServers(UA_Server *server, UA_Session *session,
					const UA_FindServersRequest *request,
					UA_FindServersResponse *response);

7.1.2 GetEndpoints

返回服务器支持的端点以及建立SecureChannel和会话所需的所有配置信息。

void Service_GetEndpoints(UA_Server *server, UA_Session *session,
	const UA_GetEndpointsRequest *request,
	UA_GetEndpointsResponse *response);
#ifdef UA_ENABLE_DISCOVERY
#ifdef UA_ENABLE_DISCOVERY_MULTICAST

7.1.3 FindServersOnNetwork

返回发现服务器已知的服务器。与FindServer不同,此服务仅由发现服务器实现。它还返回可能通过多播检测到的服务器。

void Service_FindServersOnNetwork(UA_Server *server, UA_Session *session,
const UA_FindServersOnNetworkRequest *request,
UA_FindServersOnNetworkResponse *response);
#endif /* UA_ENABLE_DISCOVERY_MULTICAST */

7.1.4 RegisterServer

在本地发现服务中注册远程服务器。

void Service_RegisterServer(UA_Server *server, UA_Session *session,
const UA_RegisterServerRequest *request,
UA_RegisterServerResponse *response);

7.1.5 RegisterServer2

此服务允许服务器向发现服务器注册其发现URL和功能。它扩展了来自RegisterServer的注册信息以及FindServersNetwork所需的信息。

void Service_RegisterServer2(UA_Server *server, UA_Session *session,
const UA_RegisterServer2Request *request,
UA_RegisterServer2Response *response);
#endif /* UA_ENABLE_DISCOVERY */

7.2 安全通道服务集

此服务集定义用于打开通信通道的服务,该通道确保与服务器交换的所有消息的机密性和完整性。

7.2.1 OpenSecureChannel

打开或续订可用于确保会话期间消息交换的机密性和完整性的SecureChannel。

void Service_OpenSecureChannel(UA_Server *server, UA_SecureChannel* channel,
const UA_OpenSecureChannelRequest *request,
UA_OpenSecureChannelResponse *response);

7.2.2 CloseSecureChannel

用于终止SecureChannel。

void Service_CloseSecureChannel(UA_Server *server, UA_SecureChannel *channel);

7.3 会话服务集

此服务集定义会话上下文中应用层连接建立的服务。

7.3.1 CreateSession

OPC UA客户端用于创建会话,服务器返回两个唯一标识会话的值。第一个值是sessionId,用于在审核日志和服务器的地址空间中标识会话。第二个是authenticationToken,用于将传入请求与会话相关联。

void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
const UA_CreateSessionRequest *request,
UA_CreateSessionResponse *response);

7.3.2 ActivateSession

客户端用于将其软件证书提交给服务器进行验证,并指定与会话关联的用户的身份。此服务请求应由客户端在CreateSession之后发出任何其他服务请求之前发出。否则将导致服务器关闭会话。

void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
const UA_ActivateSessionRequest *request,
UA_ActivateSessionResponse *response);

7.3.3 CloseSession

用于终止会话。

void Service_CloseSession(UA_Server *server, UA_SecureChannel *channel,
const UA_CloseSessionRequest *request,
UA_CloseSessionResponse *response);

7.3.4 Cancel

用于取消未完成的服务请求。成功取消的服务请求应以 Bad_RequestCancelledByClient 响应。

/* Not Implemented */

7.4 节点管理服务集

此服务集定义服务以添加和删除地址空间节点及其之间的引用。所有添加的节点继续存在于地址空间中,即使创建它们的客户端与服务器断开连接。

7.4.1 AddNodes

用于将一个或多个节点添加到地址空间层次结构中。

void Service_AddNodes(UA_Server *server, UA_Session *session,
const UA_AddNodesRequest *request,
UA_AddNodesResponse *response);

7.4.2 AddReferences

用于向一个或多个节点添加一个或多个引用。

void Service_AddReferences(UA_Server *server, UA_Session *session,
const UA_AddReferencesRequest *request,
UA_AddReferencesResponse *response);

7.4.3 DeleteNodes

用于从地址空间中删除一个或多个节点。

void Service_DeleteNodes(UA_Server *server, UA_Session *session,
const UA_DeleteNodesRequest *request,
UA_DeleteNodesResponse *response);

7.4.4 DeleteReferences

用于删除节点的一个或多个引用。

void Service_DeleteReferences(UA_Server *server, UA_Session *session,
const UA_DeleteReferencesRequest *request,
UA_DeleteReferencesResponse *response);

7.5 视图服务集

客户机使用视图服务集的浏览服务在地址空间或作为地址空间子集的视图中导航。

7.5.1 Browse

用于发现指定节点的引用。使用视图可以进一步限制浏览。此浏览服务还支持原始筛选功能。

void Service_Browse(UA_Server *server, UA_Session *session,
const UA_BrowseRequest *request,
UA_BrowseResponse *response);

7.5.3 TranslateBrowsePathsToNodeIds

用于将文本节点路径转换为各自的ID。

void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
const UA_TranslateBrowsePathsToNodeIdsRequest *request,
UA_TranslateBrowsePathsToNodeIdsResponse *response);

7.5.4 RegisterNodes

由客户端用来注册它们知道要重复访问的节点(例如写入、调用)。它允许服务器设置所需的任何内容,以便访问操作更高效。

void Service_RegisterNodes(UA_Server *server, UA_Session *session,
const UA_RegisterNodesRequest *request,
UA_RegisterNodesResponse *response);

7.5.5 UnregisterNodes

此服务用于注销通过RegisterNodes服务获得的nodeid。

void Service_UnregisterNodes(UA_Server *server, UA_Session *session,
const UA_UnregisterNodesRequest *request,
UA_UnregisterNodesResponse *response);

7.6 查询服务集

此服务集用于向服务器发出查询。OPC-UA查询是通用的,它提供了一种底层存储机制独立的查询功能,可用于访问各种OPC-UA数据存储和信息管理系统。OPC-UA查询允许客户端访问由服务器维护的数据,而不必知道用于数据内部存储的逻辑模式。了解地址空间就足够了。

7.6.1 QueryFirst

此服务用于向服务器发出查询请求

/* Not Implemented */

7.6.2 QueryNext

此服务用于请求下一组QueryFirst或QueryNext响应信息,这些信息太大,无法在单个响应中发送。

/* Not Implemented */

7.7 属性服务集

此服务集提供访问属于节点一部分的属性的服务。

7.7.1 Read

用于读取节点的属性。对于其元素被索引的构造属性值(如数组),此服务允许客户端将整个索引值集作为一个组合读取、读取单个元素或读取组合元素的范围。

void Service_Read(UA_Server *server, UA_Session *session,
const UA_ReadRequest *request,
UA_ReadResponse *response);

7.7.2 Write

用于写入节点的属性。对于其元素被索引的构造属性值(如数组),此服务允许客户端将整个索引值集作为一个组合写入、写入单个元素或写入组合元素的范围。

void Service_Write(UA_Server *server, UA_Session *session,
const UA_WriteRequest *request,
UA_WriteResponse *response);

7.7.3 HistoryRead

用于读取一个或多个节点的历史值或事件。服务器可以让使用此服务的客户机使用历史值,尽管历史值本身在地址空间中不可见。

#ifdef UA_ENABLE_HISTORIZING
void Service_HistoryRead(UA_Server *server, UA_Session *session,
						const UA_HistoryReadRequest *request,
						UA_HistoryReadResponse *response);

7.7.4 HistoryUpdate

用于更新一个或多个节点的历史值或事件。几个请求参数指示服务器如何更新历史值或事件。有效的操作是插入、替换或删除。

void Service_HistoryUpdate(UA_Server *server, UA_Session *session,
						const UA_HistoryUpdateRequest *request,
						UA_HistoryUpdateResponse *response);
#endif

7.8 方法服务集

方法服务集定义调用方法的方法。方法应该是对象的一个组成部分。有关详细信息,请参阅MethodNodes部分。

7.8.1 Call

用于调用(调用)方法。每个方法调用都是在现有会话的上下文中调用的。如果会话终止,则无法将方法的执行结果返回给客户端并将其丢弃。

#ifdef UA_ENABLE_METHODCALLS
void Service_Call(UA_Server* server, UA_Session* session,
	const UA_CallRequest* request,
	UA_CallResponse* response);
#if UA_MULTITHREADING >= 100
void Service_CallAsync(UA_Server* server, UA_Session* session, UA_UInt32 requestId,
	const UA_CallRequest* request, UA_CallResponse* response,
	UA_Boolean* finished);
#endif 
#endif
#ifdef UA_ENABLE_SUBSCRIPTIONS

7.9 监视项服务集

客户机定义MonitoredItems以订阅数据和事件。每个MonitoredItem标识要监视的项以及用于发送通知的订阅。要监视的项可以是任何节点属性。

7.9.1 CreateMonitoredItems

用于创建一个或多个MonitoredItem并将其添加到订阅。删除订阅时,服务器会自动删除MonitoredItem。删除MonitoredItem会导致删除其整个已触发项链接集,但对被触发项引用的MonitoredItem没有影响。

void Service_CreateMonitoredItems(UA_Server *server, UA_Session *session,
								const UA_CreateMonitoredItemsRequest *request,
								UA_CreateMonitoredItemsResponse *response);

7.9.2 DeleteMonitoredItems

用于删除订阅的一个或多个MonitoredItem。删除MonitoredItem时,其触发的项链接也将被删除。

void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
								const UA_DeleteMonitoredItemsRequest *request,
								UA_DeleteMonitoredItemsResponse *response);

7.9.3 ModifyMonitoredItems

用于修改订阅的MonitoredItems。服务器应立即应用对MonitoredItem设置的更改。它们将尽快生效,但不迟于新修订的采样间隔的两倍。可以修改的参数的非法请求值不会生成错误。相反,服务器将选择默认值并在相应的修改后的参数中指明它们。

void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
								const UA_ModifyMonitoredItemsRequest *request,
								UA_ModifyMonitoredItemsResponse *response);

7.9.4 SetMonitoringMode

用于设置订阅的一个或多个MonitoredItem的监视模式

void Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
							const UA_SetMonitoringModeRequest *request,
							UA_SetMonitoringModeResponse *response);

7.9.5 SetTriggering

用于创建和删除触发项的触发链接。

/* Not Implemented */

7.10 订阅服务集

订阅用于向客户端报告通知。

7.10.1 CreateSubscription

用于创建订阅。订阅监视一组监视通知的MonitoredItem,并将它们返回给客户端以响应发布请求。

void Service_CreateSubscription(UA_Server *server, UA_Session *session,
							const UA_CreateSubscriptionRequest *request,
							UA_CreateSubscriptionResponse *response);

7.10.2 ModifySubscription

用于修改订阅。

void Service_ModifySubscription(UA_Server *server, UA_Session *session,
						const UA_ModifySubscriptionRequest *request,
						UA_ModifySubscriptionResponse *response);

7.10.3 SetPublishingMode

用于启用对一个或多个订阅发送通知

void Service_SetPublishingMode(UA_Server *server, UA_Session *session,
							const UA_SetPublishingModeRequest *request,
							UA_SetPublishingModeResponse *response);

7.10.4 Publish

用于两个目的。首先,它用于确认收到一个或多个订阅的通知消息。其次,它用于请求服务器返回NotificationMessage或keep-alive消息。

请注意,服务签名是一个异常,不包含指向PublishResponse的指针。这是因为该服务在内部对发布请求进行排队,并根据超时异步发送响应。

void Service_Publish(UA_Server *server, UA_Session *session,
					const UA_PublishRequest *request, UA_UInt32 requestId);

7.10.5 Republish

请求订阅从其重新传输队列重新发布NotificationMessage。

void Service_Republish(UA_Server *server, UA_Session *session,
						const UA_RepublishRequest *request,
						UA_RepublishResponse *response);

7.10.6 DeleteSubscriptions

调用以删除属于客户端会话的一个或多个订阅

void Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
											const UA_DeleteSubscriptionsRequest *request,
											UA_DeleteSubscriptionsResponse *response);

7.10.7 TransferSubscription

用于将订阅及其监视数据项从一个会话传输到另一个会话。例如,客户机可能需要重新打开会话,然后将其订阅转移到该会话。它还可以由一个客户端通过将订阅传输到其会话来从另一个客户端接管订阅。

/* Not Implemented */
#endif /* UA_ENABLE_SUBSCRIPTIONS */

8 信息建模

OPC-UA中的信息建模结合了面向对象和语义建模的概念。OPC UA信息模型的核心是由

  • 节点:有八种可能的节点类型(变量、对象、方法等)
  • 引用:两个节点之间的类型化和定向关系
    每个节点都由一个唯一的(在服务器内)NodeId标识。Reference是形式的三元组(source nodeid,referencetype nodeid,target nodeid)。节点之间的引用示例是 Variable 与其VariableType之间的 hasTypeDefinition 引用。某些 ReferenceTypes 是层次结构的,不能形成定向循环。有关可能的引用及其语义的详细信息,请参阅ReferenceTypes部分。
    警告!!本节中定义的结构仅与自定义节点存储的开发人员相关。只有通过OPC UA Services 才能与信息模型进行交互。因此,下面的部分纯粹是信息性的,这样用户就可以对底层表示有一个清晰的心理模型。

8.1 Base Node Attributes

节点根据其节点类型包含属性。基本节点属性对所有节点类型都是通用的。在OPC UA Services 服务中,属性通过包含节点的 NodeId 和整数的 Attribute Id 来引用。在内部,open62541在不知道确切节点类型或不重要的地方使用UA_Node。nodeClass 属性用于确保从 UA_Node 强制转换到特定节点类型的正确性。

/* Ordered tree structure for fast member check */
typedef struct UA_ReferenceTarget {
	ZIP_ENTRY(UA_ReferenceTarget) idTreeFields;
	ZIP_ENTRY(UA_ReferenceTarget) nameTreeFields;
	UA_UInt32 targetIdHash; /* Hash of the target’s NodeId */
	UA_UInt32 targetNameHash; /* Hash of the target’s BrowseName */
	UA_ExpandedNodeId targetId;
} UA_ReferenceTarget;
ZIP_HEAD(UA_ReferenceTargetIdTree, UA_ReferenceTarget);
typedef struct UA_ReferenceTargetIdTree UA_ReferenceTargetIdTree;
ZIP_PROTTYPE(UA_ReferenceTargetIdTree, UA_ReferenceTarget, UA_ReferenceTarget)
ZIP_HEAD(UA_ReferenceTargetNameTree, UA_ReferenceTarget);
typedef struct UA_ReferenceTargetNameTree UA_ReferenceTargetNameTree;
ZIP_PROTTYPE(UA_ReferenceTargetNameTree, UA_ReferenceTarget, UA_UInt32)
/* List of reference targets with the same reference type and direction */
typedef struct {
	UA_NodeId referenceTypeId;
	UA_Boolean isInverse;
	size_t refTargetsSize;
	UA_ReferenceTarget* refTargets;
	UA_ReferenceTargetIdTree refTargetsIdTree;
	UA_ReferenceTargetNameTree refTargetsNameTree;
} UA_NodeReferenceKind;
#define UA_NODE_BASEATTRIBUTES \
UA_NodeId nodeId; \
UA_NodeClass nodeClass; \
UA_QualifiedName browseName; \
UA_LocalizedText displayName; \
UA_LocalizedText description; \
UA_UInt32 writeMask; \
size_t referencesSize; \
UA_NodeReferenceKind *references; \
\
/* Members specific to open62541 */ \
void *context; \
UA_Boolean constructed; /* Constructors were called */
typedef struct {
	UA_NODE_BASEATTRIBUTES
} UA_Node;

8.2 VariableNode

变量将值与元数据一起存储在DataValue中,以便进行内部检查。最值得注意的是,属性数据类型、值秩和数组维度约束变量可以接受的可能值。
变量有两种类型:属性和数据变量。属性与具有hasProperty引用的父节点相关,并且可能本身没有子节点。Datavariables可以包含属性(hasProperty)也可以包含Datavariables(hasComponents)。
所有变量都是某个VariableTypeNode的实例,返回时约束可能的数据类型、值秩和数组维度属性

8.2.1 Data Type

变量的(标量)数据类型被约束为类型层次结构中的特定类型或其子类型之一。数据类型被指定为指向类型层次结构中的 DataTypeNode 的 NodeId。参见章节
DataTypeNode 以获取更多详细信息。
如果数据类型属性指向UInt32,则 value 属性必须是该类型,因为UInt32在类型层次结构中没有子级。如果数据类型属性指向 Number,那么value属性的类型仍然可以是UInt32,但也可以是Float或Byte。
确保变量中的数据类型属性与其VariableTypeNode之间的一致性。

8.2.2 Value Rank

此属性指示变量的 value 属性是否为数组以及数组的维数。它可能具有以下值:

  • n >= 1:该值是具有指定维数的数组
  • n = 0:该值是具有一个或多个维度的数组
  • n = -1:值是标量
  • n = -2:该值可以是标量或具有任意维数的数组
  • n = -3:该值可以是标量或一维数组

确保变量中的value rank属性与其VariableTypeNode之间的一致性。

8.2.3 Array Dimensions

如果Value Rank允许值是一个(多维)数组,则每个维度中的确切长度可以用此属性进一步约束。

  1. 对于正长度,保证变量值在该尺寸中具有相同的长度。
  2. 尺寸长度0是通配符,实际值在此尺寸中可以有任何长度。
    确保变量中的数组维度属性与其 VariableTypeNode 之间的一致性。
 /* Indicates whether a variable contains data inline or whether it points to an
 3. external data source */
typedef enum {
	UA_VALUESOURCE_DATA,
	UA_VALUESOURCE_DATASOURCE
} UA_ValueSource;
#define UA_NODE_VARIABLEATTRIBUTES \
/* Constraints on possible values */ \
UA_NodeId dataType; \
UA_Int32 valueRank; \
size_t arrayDimensionsSize; \
UA_UInt32 *arrayDimensions; \
\
/* The current value */ \
UA_ValueSource valueSource; \
union { \
struct { \
UA_DataValue value; \
UA_ValueCallback callback; \
} data; \
UA_DataSource dataSource; \
} value;
typedef struct {
	UA_NODE_BASEATTRIBUTES
		UA_NODE_VARIABLEATTRIBUTES
		UA_Byte accessLevel;
	UA_Double minimumSamplingInterval;
	UA_Boolean historizing;
} UA_VariableNode;

8.3 VariableTypeNode

VariableTypes用于为变量提供类型定义。VariableTypes约束变量实例的数据类型、value rank 和 array dimensions 属性。此外,从特定变量类型实例化可以提供语义信息。
例如,MotorTemperatureVariableType 的实例比从 BaseDataVariable 实例化的浮点变量更有意义。

typedef struct {
	UA_NODE_BASEATTRIBUTES
		UA_NODE_VARIABLEATTRIBUTES
		UA_Boolean isAbstract;
	/* Members specific to open62541 */
	UA_NodeTypeLifecycle lifecycle;
} UA_VariableTypeNode;

8.4 MethodNode

方法定义可调用函数,并使用 Call service 进行调用。MethodNode 可能具有 QualifiedName(0,“InputArguments”)和(0,“OutputArguments”)的特殊属性(带有hasProperty引用的变量childen)。输入和输出参数都是通过 UA_Argument 数组来描述的。当Call service 使用一个通用的 Variant 数组作为输入和输出时,将检查实际参数值以匹配 MethodNode 的签名。
请注意,同一 MethodNode 可以从多个对象(和对象类型)引用。为此,方法和提供上下文的对象的 NodeId 是调用请求消息的一部分。

typedef struct {
	UA_NODE_BASEATTRIBUTES
		UA_Boolean executable;
	/* Members specific to open62541 */
	UA_MethodCallback method;
#if UA_MULTITHREADING >= 100
	UA_Boolean async; /* Indicates an async method call */
#endif
} UA_MethodNode;

8.5 ObjectNode

对象用于表示系统、系统组件、真实世界对象和软件对象。对象是对象类型的实例,可以包含变量、方法和其他对象。

typedef struct {
	UA_NODE_BASEATTRIBUTES
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
		struct UA_MonitoredItem* monitoredItemQueue;
#endif
	UA_Byte eventNotifier;
} UA_ObjectNode;

8.6 ObjectTypeNode

对象类型提供对象的定义。无法实例化抽象对象。有关构造函数和析构函数回调的使用,请参见节点生命周期:构造函数、析构函数和节点上下文。

typedef struct {
	UA_NODE_BASEATTRIBUTES
	UA_Boolean isAbstract;
	/* Members specific to open62541 */
	UA_NodeTypeLifecycle lifecycle;
} UA_ObjectTypeNode;

8.7 ReferenceTypeNode

两个节点之间的每个引用都是用一个ReferenceType类型来定义关系的。OPC UA标准将一组参考类型定义为OPC UA信息模型的强制性部分。
4. 抽象引用类型不能在实际引用中使用,只能用于构建ReferenceTypes层次结构
5. 从源节点和目标节点的角度来看,对称引用具有相同的含义
下图显示了标准引用类型的层次结构(箭头表示hasSubType关系)。参考OPC UA规范的第3部分,了解每个引用类型的完整语义。
在这里插入图片描述
ReferenceType层次结构可以用用户定义的ReferenceType进行扩展。OPC-UA的许多配套规范定义了在其感兴趣的领域中使用的新的参考类型。
对于下面的自定义引用类型的示例,我们尝试为技术系统的结构建模。为此,我们引入两个定制的referencetype。首先,层次包含引用类型表示系统(由OPC UA对象表示)包含组件(或子系统)。这就产生了一个包含关系的三层结构。例如,马达(对象)包含在汽车中,曲轴包含在马达中。其次,symmetric connectedTo ReferenceType表示两个组件是连接的。例如,电动机的曲轴与齿轮箱相连。连接独立于包含层次结构,可以归纳出一般的图结构。connectedTo的其他子类型可用于区分物理连接、电气连接和信息相关连接。客户可以学习
基于对两个自定义参考类型的共同理解,在OPC-UA信息模型中表示的(物理)系统的布局

typedef struct {
	UA_NODE_BASEATTRIBUTES
	UA_Boolean isAbstract;
	UA_Boolean symmetric;
	UA_LocalizedText inverseName;
} UA_ReferenceTypeNode;

8.8 DataTypeNode

数据类型表示简单和结构化的数据类型。数据类型可以包含数组。但它们总是描述单个实例的结构。在open62541中,信息模型层次结构中的数据类型节点通过节点标识与UA_数据类型类型描述进行匹配,以进行泛型类型处理。
抽象数据类型(例如数字)不能是实际值的类型。它们用于将值约束到可能的子数据类型(例如UInt32)。

typedef struct {
	UA_NODE_BASEATTRIBUTES
	UA_Boolean isAbstract;
} UA_DataTypeNode;

8.9 ViewNode

每个视图定义地址空间中节点的子集。在浏览信息模型时,可以使用视图来只关注节点和引用的子集。可以创建视图节点并与之交互。但是open62541目前不支持在浏览服务中使用它们。

typedef struct {
	UA_NODE_BASEATTRIBUTES
	UA_Byte eventNotifier;
	UA_Boolean containsNoLoops;
} UA_ViewNode;

8.10 Nodestore Plugin API

以下定义用于实现自定义节点存储后端。大多数用户都希望使用默认的nodestore,而不需要使用nodestoreapi。
在自定义nodestore实现之外,用户不应手动编辑节点。请使用OPC UA服务。否则,将忽略所有一致性检查。这最终会使应用程序崩溃.

typedef void (*UA_NodestoreVisitor)(void* visitorCtx, const UA_Node* node);
typedef struct {
	/* Nodestore context and lifecycle */
	void* context;
	void (*clear)(void* nsCtx);
	/* The following definitions are used to create empty nodes of the different
	* node types. The memory is managed by the nodestore. Therefore, the node
	* has to be removed via a special deleteNode function. (If the new node is
	* not added to the nodestore.) */
	UA_Node* (*newNode)(void* nsCtx, UA_NodeClass nodeClass);
	void (*deleteNode)(void* nsCtx, UA_Node* node);
	/* ‘‘Get‘‘ returns a pointer to an immutable node. ‘‘Release‘‘ indicates
	* that the pointer is no longer accessed afterwards. */
	const UA_Node* (*getNode)(void* nsCtx, const UA_NodeId* nodeId);
	void (*releaseNode)(void* nsCtx, const UA_Node* node);
	/* Returns an editable copy of a node (needs to be deleted with the
	* deleteNode function or inserted / replaced into the nodestore). */
	UA_StatusCode(*getNodeCopy)(void* nsCtx, const UA_NodeId* nodeId,
		UA_Node** outNode);
	/* Inserts a new node into the nodestore. If the NodeId is zero, then a
	* fresh numeric NodeId is assigned. If insertion fails, the node is
	* deleted. */
	UA_StatusCode(*insertNode)(void* nsCtx, UA_Node* node,
		UA_NodeId* addedNodeId);
		/* To replace a node, get an editable copy of the node, edit and replace
		* with this function. If the node was already replaced since the copy was
		* made, UA_STATUSCODE_BADINTERNALERROR is returned. If the NodeId is not
		* found, UA_STATUSCODE_BADNODEIDUNKNOWN is returned. In both error cases,
		* the editable node is deleted. */
		UA_StatusCode(*replaceNode)(void* nsCtx, UA_Node * node);
	/* Removes a node from the nodestore. */
	UA_StatusCode(*removeNode)(void* nsCtx, const UA_NodeId* nodeId);
	/* Execute a callback for every node in the nodestore. */
	void (*iterate)(void* nsCtx, UA_NodestoreVisitor visitor,
		void* visitorCtx);
} UA_Nodestore;
/* Attributes must be of a matching type (VariableAttributes, ObjectAttributes,
 6. and so on). The attributes are copied. Note that the attributes structs do
 7. not contain NodeId, NodeClass and BrowseName. The NodeClass of the node needs
 8. to be correctly set before calling this method. UA_Node_clear is called on
 9. the node when an error occurs internally. */
UA_StatusCode UA_Node_setAttributes(UA_Node* node, const void* attributes,
	const UA_DataType* attributeType);
/* Reset the destination node and copy the content of the source */
UA_StatusCode UA_Node_copy(const UA_Node* src, UA_Node* dst);
/* Allocate new node and copy the values from src */
UA_Node* UA_Node_copy_alloc(const UA_Node* src);
/* Add a single reference to the node */
UA_StatusCode UA_Node_addReference(UA_Node* node, const UA_AddReferencesItem* item,
	UA_UInt32 targetBrowseNameHash);
/* Delete a single reference from the node */
UA_StatusCode UA_Node_deleteReference(UA_Node* node, const UA_DeleteReferencesItem* item);
/* Delete all references of the node */
void UA_Node_deleteReferences(UA_Node* node);
/* Remove all malloc’ed members of the node and reset */
void UA_Node_clear(UA_Node* node);

9 服务器

9.1 服务器配置

配置结构在初始化期间传递给服务器。服务器期望在运行时不修改配置。目前,一次只能有一台服务器使用配置。在关闭期间,服务器将清理在运行时通过提供的API修改的配置部分。
/plugins文件夹中提供了配置示例。常用用法如下:
1.以默认设置为起点创建服务器配置
2.修改配置,例如通过添加服务器证书
3.用它实例化服务器
4.关闭服务器后,清理配置(可用内存)
第四章教程提供了一个很好的起点。

typedef struct {
	UA_UInt32 min;
	UA_UInt32 max;
} UA_UInt32Range;
typedef struct {
	UA_Duration min;
	UA_Duration max;
} UA_DurationRange;
#ifdef UA_ENABLE_DISCOVERY
typedef struct {
	/* Timeout in seconds when to automatically remove a registered server from
	* the list, if it doesn’t re-register within the given time frame. A value
	* of 0 disables automatic removal. Default is 60 Minutes (60*60). Must be
	* bigger than 10 seconds, because cleanup is only triggered approximately
	* every 10 seconds. The server will still be removed depending on the
	* state of the semaphore file. */
	UA_UInt32 cleanupTimeout;
	/* Enable mDNS announce and response to queries */
	bool mdnsEnable;
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
	UA_MdnsDiscoveryConfiguration mdns;
	UA_String mdnsInterfaceIP;
# if !defined(UA_HAS_GETIFADDR)
	uint32_t* ipAddressList;
	size_t ipAddressListSize;
	133open62541 Documentation, Release 1.1.2
# endif
#endif
} UA_ServerConfig_Discovery;
#endif
typedef void
(*UA_Server_AsyncOperationNotifyCallback)(UA_Server* server);
struct UA_ServerConfig {
	UA_UInt16 nThreads; /* only if multithreading is enabled */
	UA_Logger logger;
	/* Server Description:
	* The description must be internally consistent.
	* - The ApplicationUri set in the ApplicationDescription must match the
	* URI set in the server certificate */
	UA_BuildInfo buildInfo;
	UA_ApplicationDescription applicationDescription;
	UA_ByteString serverCertificate;
	UA_Double shutdownDelay; /* Delay in ms from the shutdown signal (ctrl-c)
	until the actual shutdown. Clients need to be
	able to get a notification ahead of time. */
	/* Rule Handling */
	UA_RuleHandling verifyRequestTimestamp; /* Verify that the server sends a
	* timestamp in the request header */
	/* Custom DataTypes. Attention! Custom datatypes are not cleaned up together
	* with the configuration. So it is possible to allocate them on ROM. */
	const UA_DataTypeArray* customDataTypes;
	/* 注意:请参阅(6.2 Generic Type Handling)部分。/Examples/custom/datatype/中提供了使用自定义数据类型的示例。*/
/* Networking */
	size_t networkLayersSize;
	UA_ServerNetworkLayer* networkLayers;
	UA_String customHostname;
#ifdef UA_ENABLE_PUBSUB
	/*PubSub network layer */
	size_t pubsubTransportLayersSize;
	UA_PubSubTransportLayer* pubsubTransportLayers;
#endif
	/* Available security policies */
	size_t securityPoliciesSize;
	UA_SecurityPolicy* securityPolicies;
	/* Available endpoints */
	size_t endpointsSize;
	UA_EndpointDescription* endpoints;
	/* Only allow the following discovery services to be executed on a
	* SecureChannel with SecurityPolicyNone: GetEndpointsRequest,
	* FindServersRequest and FindServersOnNetworkRequest.
	* *
	Only enable this option if there is no endpoint with SecurityPolicy#None
	* in the endpoints list. The SecurityPolicy#None must be present in the
	134 Chapter 9. Serveropen62541 Documentation, Release 1.1.2
	* securityPolicies list. */
	UA_Boolean securityPolicyNoneDiscoveryOnly;
	/* Node Lifecycle callbacks */
	UA_GlobalNodeLifecycle nodeLifecycle;
	/*Note: See the section for node lifecycle handling.*/
/* Access Control */
	UA_AccessControl accessControl;
	/*Note: See the section for access - control handling.*/
/* Async Operations */
#if UA_MULTITHREADING >= 100
	UA_Double asyncOperationTimeout; /* in ms, 0 => unlimited */
	size_t maxAsyncOperationQueueSize; /* 0 => unlimited */
	UA_DEPRECATED UA_Double asyncCallRequestTimeout; /* in ms, 0 => unlimited */
	/* Notify workers when an async operation was enqueued */
	UA_Server_AsyncOperationNotifyCallback asyncOperationNotifyCallback;
#endif
	/* 注意:有关异步操作,请参阅部分。*/
	/* Nodestore */
	UA_Nodestore nodestore;
	/* Certificate Verification */
	UA_CertificateVerification certificateVerification;
	/* Relax constraints for the InformationModel */
	UA_Boolean relaxEmptyValueConstraint; /* Nominally, only variables with data
	* type BaseDataType can have an empty
	* value. */
	/* Limits for SecureChannels */
	UA_UInt16 maxSecureChannels;
	UA_UInt32 maxSecurityTokenLifetime; /* in ms */
	/* Limits for Sessions */
	UA_UInt16 maxSessions;
	UA_Double maxSessionTimeout; /* in ms */
	/* Operation limits */
	UA_UInt32 maxNodesPerRead;
	UA_UInt32 maxNodesPerWrite;
	UA_UInt32 maxNodesPerMethodCall;
	UA_UInt32 maxNodesPerBrowse;
	UA_UInt32 maxNodesPerRegisterNodes;
	UA_UInt32 maxNodesPerTranslateBrowsePathsToNodeIds;
	UA_UInt32 maxNodesPerNodeManagement;
	UA_UInt32 maxMonitoredItemsPerCall;
	/* Limits for Requests */
	UA_UInt32 maxReferencesPerNode;
	/* Limits for Subscriptions */
	UA_UInt32 maxSubscriptions;
	UA_UInt32 maxSubscriptionsPerSession;
	UA_DurationRange publishingIntervalLimits; /* in ms (must not be less than 5) */
	UA_UInt32Range lifeTimeCountLimits;
	UA_UInt32Range keepAliveCountLimits;
	UA_UInt32 maxNotificationsPerPublish;
	UA_Boolean enableRetransmissionQueue;
	UA_UInt32 maxRetransmissionQueueSize; /* 0 -> unlimited size */
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
	UA_UInt32 maxEventsPerNode; /* 0 -> unlimited size */
#endif
/* Limits for MonitoredItems */
	UA_UInt32 maxMonitoredItems;
	UA_UInt32 maxMonitoredItemsPerSubscription;
	UA_DurationRange samplingIntervalLimits; /* in ms (must not be less than 5) */
	UA_UInt32Range queueSizeLimits; /* Negotiated with the client */
	/* Limits for PublishRequests */
	UA_UInt32 maxPublishReqPerSession;
	/* Discovery */
#ifdef UA_ENABLE_DISCOVERY
	UA_ServerConfig_Discovery discovery;
#endif
#ifdef UA_ENABLE_SUBSCRIPTIONS
	/* Register MonitoredItem in Userland
	* *
	@param server Allows the access to the server object
	* @param sessionId The session id, represented as an node id
	* @param sessionContext An optional pointer to user-defined data for the specific data source
	* @param nodeid Id of the node in question
	* @param nodeidContext An optional pointer to user-defined data, associated
	* with the node in the nodestore. Note that, if the node has already been removed,
	* this value contains a NULL pointer.
	* @param attributeId Identifies which attribute (value, data type etc.) is monitored
	* @param removed Determines if the MonitoredItem was removed or created. */
	void (*monitoredItemRegisterCallback)(UA_Server* server,
		const UA_NodeId* sessionId, void* sessionContext,
		const UA_NodeId* nodeId, void* nodeContext,
		UA_UInt32 attibuteId, UA_Boolean removed);
#endif
	/* Historical Access */
#ifdef UA_ENABLE_HISTORIZING
	UA_HistoryDatabase historyDatabase;
	UA_Boolean accessHistoryDataCapability;
	UA_UInt32 maxReturnDataValues; /* 0 -> unlimited size */
	UA_Boolean accessHistoryEventsCapability;
	UA_UInt32 maxReturnEventValues; /* 0 -> unlimited size */
	UA_Boolean insertDataCapability;
	UA_Boolean insertEventCapability;
	UA_Boolean insertAnnotationsCapability;
	UA_Boolean replaceDataCapability;
	UA_Boolean replaceEventCapability;
	UA_Boolean updateDataCapability;
	UA_Boolean updateEventCapability;
	UA_Boolean deleteRawCapability;
	UA_Boolean deleteEventCapability;
	UA_Boolean deleteAtTimeDataCapability;
#endif
};
void
UA_ServerConfig_clean(UA_ServerConfig* config);
/* Set a custom hostname in server configuration */
void
UA_ServerConfig_setCustomHostname(UA_ServerConfig* config,
	const UA_String customHostname);

9.2 服务器生命周期

/* The method UA_Server_new is defined in server_config_default.h. So default
* plugins outside of the core library (for logging, etc) are already available
* during the initialization.
* *
UA_Server * UA_Server_new(void);
*/
/* Creates a new server. Moves the config into the server with a shallow copy.
* The config content is cleared together with the server. */
UA_Server* UA_Server_newWithConfig(const UA_ServerConfig* config);
void UA_Server_delete(UA_Server* server);
UA_ServerConfig* UA_Server_getConfig(UA_Server* server);
/* Runs the main loop of the server. In each iteration, this calls into the
* networklayers to see if messages have arrived.
* *
@param server The server object.
* @param running The loop is run as long as *running is true.
* Otherwise, the server shuts down.
* @return Returns the statuscode of the UA_Server_run_shutdown method */
UA_StatusCode UA_Server_run(UA_Server* server, const volatile UA_Boolean* running);
/* The prologue part of UA_Server_run (no need to use if you call
* UA_Server_run) */
UA_StatusCode UA_Server_run_startup(UA_Server* server);
/* Executes a single iteration of the server’s main loop.
* *
@param server The server object.
* @param waitInternal Should we wait for messages in the networklayer?
* Otherwise, the timouts for the networklayers are set to zero.
* The default max wait time is 50millisec.
* @return Returns how long we can wait until the next scheduled
* callback (in ms) */
UA_UInt16 UA_Server_run_iterate(UA_Server* server, UA_Boolean waitInternal);
/* The epilogue part of UA_Server_run (no need to use if you call
* UA_Server_run) */
UA_StatusCode 9.2.Server Lifecycle 137open62541 Documentation, Release 1.1.2
UA_Server_run_shutdown(UA_Server * server);

9.3 定时回调

typedef void (*UA_ServerCallback)(UA_Server* server, void* data);
/* Add a callback for execution at a specified time. If the indicated time lies
* in the past, then the callback is executed at the next iteration of the
* server’s main loop.
* *
@param server The server object.
* @param callback The callback that shall be added.
* @param data Data that is forwarded to the callback.
* @param date The timestamp for the execution time.
* @param callbackId Set to the identifier of the repeated callback . This can
* be used to cancel the callback later on. If the pointer is null, the
* identifier is not set.
* @return Upon success, UA_STATUSCODE_GOOD is returned. An error code
* otherwise. */
UA_StatusCode UA_THREADSAFE UA_Server_addTimedCallback(UA_Server* server, UA_ServerCallback callback,
	void* data, UA_DateTime date, UA_UInt64* callbackId);
/* Add a callback for cyclic repetition to the server.
* *
@param server The server object.
* @param callback The callback that shall be added.
* @param data Data that is forwarded to the callback.
* @param interval_ms The callback shall be repeatedly executed with the given
* interval (in ms). The interval must be positive. The first execution
* occurs at now() + interval at the latest.
* @param callbackId Set to the identifier of the repeated callback . This can
* be used to cancel the callback later on. If the pointer is null, the
* identifier is not set.
* @return Upon success, UA_STATUSCODE_GOOD is returned. An error code
* otherwise. */
UA_StatusCode UA_THREADSAFE UA_Server_addRepeatedCallback(UA_Server* server, UA_ServerCallback callback,
	void* data, UA_Double interval_ms, UA_UInt64* callbackId);
UA_StatusCode UA_THREADSAFE UA_Server_changeRepeatedCallbackInterval(UA_Server* server, UA_UInt64 callbackId,
	UA_Double interval_ms);
/* Remove a repeated callback. Does nothing if the callback is not found.
* *
@param server The server object.
* @param callbackId The id of the callback */
void UA_THREADSAFE UA_Server_removeCallback(UA_Server* server, UA_UInt64 callbackId);
#define UA_Server_removeRepeatedCallback(server, callbackId) \
UA_Server_removeCallback(server, callbackId);

9.4 读取和写入节点属性

用于读取和写入节点属性的函数在后台调用常规的读写服务,这些服务也在网络上使用。
无法读取以下属性,因为本地“admin”用户始终具有完全权限。(估计是需要管理员才能读取这些属性,未验证)

  • UserWriteMask
  • UserAccessLevel
  • UserExecutable
/* Read an attribute of a node. The specialized functions below provide a more
* concise syntax.
* *
@param server The server object.
* @param item ReadValueIds contain the NodeId of the target node, the id of the
* attribute to read and (optionally) an index range to read parts
* of an array only. See the section on NumericRange for the format
* used for array ranges.
* @param timestamps Which timestamps to return for the attribute.
* @return Returns a DataValue that contains either an error code, or a variant
* with the attribute value and the timestamps. */
UA_DataValue UA_THREADSAFE
UA_Server_read(UA_Server* server, const UA_ReadValueId* item,
	UA_TimestampsToReturn timestamps);
/* Don’t use this function. There are typed versions for every supported  attribute. */
UA_StatusCode UA_THREADSAFE
__UA_Server_read(UA_Server* server, const UA_NodeId* nodeId,
	UA_AttributeId attributeId, void* v);
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readNodeId(UA_Server* server, const UA_NodeId nodeId,
	UA_NodeId* outNodeId) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_NODEID, outNodeId);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readNodeClass(UA_Server* server, const UA_NodeId nodeId,
	UA_NodeClass* outNodeClass) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_NODECLASS,
		outNodeClass);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readBrowseName(UA_Server* server, const UA_NodeId nodeId,
	UA_QualifiedName* outBrowseName) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_BROWSENAME,
		outBrowseName);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readDisplayName(UA_Server* server, const UA_NodeId nodeId,
	UA_LocalizedText* outDisplayName) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
		outDisplayName);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readDescription(UA_Server* server, const UA_NodeId nodeId,
	UA_LocalizedText* outDescription) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_DESCRIPTION,
		outDescription);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readWriteMask(UA_Server * server, const UA_NodeId nodeId,
	UA_UInt32 * outWriteMask) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_WRITEMASK,
		outWriteMask);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readIsAbstract(UA_Server* server, const UA_NodeId nodeId,
	UA_Boolean* outIsAbstract) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ISABSTRACT,
		outIsAbstract);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readSymmetric(UA_Server* server, const UA_NodeId nodeId,
	UA_Boolean* outSymmetric) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_SYMMETRIC,
		outSymmetric);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readInverseName(UA_Server* server, const UA_NodeId nodeId,
	UA_LocalizedText* outInverseName) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_INVERSENAME,
		outInverseName);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readContainsNoLoops(UA_Server* server, const UA_NodeId nodeId,
	UA_Boolean* outContainsNoLoops) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_CONTAINSNOLOOPS,
		outContainsNoLoops);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readEventNotifier(UA_Server* server, const UA_NodeId nodeId,
	UA_Byte* outEventNotifier) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER,
		outEventNotifier);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readValue(UA_Server* server, const UA_NodeId nodeId,
	UA_Variant* outValue) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_VALUE, outValue);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readDataType(UA_Server* server, const UA_NodeId nodeId,
	UA_NodeId* outDataType) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_DATATYPE,
		outDataType);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readValueRank(UA_Server* server, const UA_NodeId nodeId,
	UA_Int32* outValueRank) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_VALUERANK,
		outValueRank);
}
/*Returns a variant with an int32 array */
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readArrayDimensions(UA_Server * server, const UA_NodeId nodeId,
	UA_Variant * outArrayDimensions) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ARRAYDIMENSIONS,
		outArrayDimensions);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readAccessLevel(UA_Server* server, const UA_NodeId nodeId,
	UA_Byte* outAccessLevel) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
		outAccessLevel);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readMinimumSamplingInterval(UA_Server* server, const UA_NodeId nodeId,
	UA_Double* outMinimumSamplingInterval) {
	return __UA_Server_read(server, &nodeId,
		UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
		outMinimumSamplingInterval);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readHistorizing(UA_Server* server, const UA_NodeId nodeId,
	UA_Boolean* outHistorizing) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_HISTORIZING,
		outHistorizing);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_readExecutable(UA_Server* server, const UA_NodeId nodeId,
	UA_Boolean* outExecutable) {
	return __UA_Server_read(server, &nodeId, UA_ATTRIBUTEID_EXECUTABLE,
		outExecutable);
}

创建节点后,无法更改以下节点属性:

  • NodeClass
  • NodeId
  • Symmetric
  • ContainsNoLoops
    无法从服务器写入以下属性,因为它们特定于不同的用户并由访问控制回调设置:
  • UserWriteMask
  • UserAccessLevel
  • UserExecutable
/* Overwrite an attribute of a node. The specialized functions below provide a
* more concise syntax.
* *
@param server The server object.
* @param value WriteValues contain the NodeId of the target node, the id of the
* attribute to overwritten, the actual value and (optionally) an
* index range to replace parts of an array only. of an array only.
* See the section on NumericRange for the format used for array
* ranges.
* @return Returns a status code. */
UA_StatusCode UA_THREADSAFE
UA_Server_write(UA_Server* server, const UA_WriteValue* value);
/* Don’t use this function. There are typed versions with no additional
* overhead. */
UA_StatusCode UA_THREADSAFE
__UA_Server_write(UA_Server * server, const UA_NodeId * nodeId,
	const UA_AttributeId attributeId,
	const UA_DataType * attr_type, const void* attr);
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeBrowseName(UA_Server* server, const UA_NodeId nodeId,
	const UA_QualifiedName browseName) {
	return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_BROWSENAME,
		&UA_TYPES[UA_TYPES_QUALIFIEDNAME], &browseName);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeDisplayName(UA_Server* server, const UA_NodeId nodeId,
	const UA_LocalizedText displayName) {
	return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
		&UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &displayName);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeDescription(UA_Server* server, const UA_NodeId nodeId,
	const UA_LocalizedText description) {
	return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_DESCRIPTION,
		&UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &description);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeWriteMask(UA_Server* server, const UA_NodeId nodeId,
	const UA_UInt32 writeMask) {
	return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_WRITEMASK,
		&UA_TYPES[UA_TYPES_UINT32], &writeMask);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeIsAbstract(UA_Server* server, const UA_NodeId nodeId,
	const UA_Boolean isAbstract) {
	return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_ISABSTRACT,
		&UA_TYPES[UA_TYPES_BOOLEAN], &isAbstract);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeInverseName(UA_Server* server, const UA_NodeId nodeId,
	const UA_LocalizedText inverseName) {
	return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_INVERSENAME,
		&UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &inverseName);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeEventNotifier(UA_Server* server, const UA_NodeId nodeId,
	const UA_Byte eventNotifier) {
	return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER,
		&UA_TYPES[UA_TYPES_BYTE], &eventNotifier);
}

将 UA_Variant 写入variable/variableType节点。StatusCode 设置为 UA_STATUSCODE_GOOD,sourceTimestamp和serverTimestamp设置为UA_DateTime_now()

static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeValue(UA_Server* server, const UA_NodeId nodeId,
	const UA_Variant value) {
		return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_VALUE,
			&UA_TYPES[UA_TYPES_VARIANT], &value);
}

将UA_DataValue写入variable/variableType节点。与UA_Server_writeValue不同,此函数还可以写入sourceTimestamp、serverTimestamp和statusCode。

static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeDataValue(UA_Server* server, const UA_NodeId nodeId,
	const UA_DataValue value) {
	return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_VALUE,
		&UA_TYPES[UA_TYPES_DATAVALUE], &value);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeDataType(UA_Server* server, const UA_NodeId nodeId,
	const UA_NodeId dataType) {
	return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_DATATYPE,
		&UA_TYPES[UA_TYPES_NODEID], &dataType);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeValueRank(UA_Server* server, const UA_NodeId nodeId,
	const UA_Int32 valueRank) {
	return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_VALUERANK,
		&UA_TYPES[UA_TYPES_INT32], &valueRank);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeArrayDimensions(UA_Server* server, const UA_NodeId nodeId,
	const UA_Variant arrayDimensions) {
	return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_ARRAYDIMENSIONS,
		&UA_TYPES[UA_TYPES_VARIANT], &arrayDimensions);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeAccessLevel(UA_Server* server, const UA_NodeId nodeId,
	const UA_Byte accessLevel) {
	return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
		&UA_TYPES[UA_TYPES_BYTE], &accessLevel);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeMinimumSamplingInterval(UA_Server* server, const UA_NodeId nodeId,
	const UA_Double miniumSamplingInterval) {
	return __UA_Server_write(server, &nodeId,
		UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
		&UA_TYPES[UA_TYPES_DOUBLE],
		&miniumSamplingInterval);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeHistorizing(UA_Server* server, const UA_NodeId nodeId,
	const UA_Boolean historizing) {
	return __UA_Server_write(server, &nodeId,
		UA_ATTRIBUTEID_HISTORIZING,
		&UA_TYPES[UA_TYPES_BOOLEAN],
		&historizing);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_writeExecutable(UA_Server* server, const UA_NodeId nodeId,
	const UA_Boolean executable) {
		return __UA_Server_write(server, &nodeId, UA_ATTRIBUTEID_EXECUTABLE,
			&UA_TYPES[UA_TYPES_BOOLEAN], &executable);
}

9.5浏览

/* Browse the references of a particular node. See the definition of
* BrowseDescription structure for details. */
UA_BrowseResult UA_THREADSAFE
UA_Server_browse(UA_Server* server, UA_UInt32 maxReferences,
	const UA_BrowseDescription* bd);
UA_BrowseResult UA_THREADSAFE
UA_Server_browseNext(UA_Server* server, UA_Boolean releaseContinuationPoint,
	const UA_ByteString* continuationPoint);
/* Nonstandard version of the browse service that recurses into child nodes.
* Possible loops (that can occur for non-hierarchical references) are handled
* by adding every target node at most once to the results array. */
UA_StatusCode UA_THREADSAFE
UA_Server_browseRecursive(UA_Server* server, const UA_BrowseDescription* bd,
	size_t* resultsSize, UA_ExpandedNodeId** results);
UA_BrowsePathResult UA_THREADSAFE
UA_Server_translateBrowsePathToNodeIds(UA_Server* server,
	const UA_BrowsePath* browsePath);
/* A simplified TranslateBrowsePathsToNodeIds based on the
* SimpleAttributeOperand type (Part 4, 7.4.4.5).
* *
This specifies a relative path using a list of BrowseNames instead of the
* RelativePath structure. The list of BrowseNames is equivalent to a
* RelativePath that specifies forward references which are subtypes of the
* HierarchicalReferences ReferenceType. All Nodes followed by the browsePath
* shall be of the NodeClass Object or Variable. */
UA_BrowsePathResult UA_THREADSAFE
UA_Server_browseSimplifiedBrowsePath(UA_Server* server, const UA_NodeId origin,
	size_t browsePathSize,
	const UA_QualifiedName* browsePath);
#ifndef HAVE_NODEITER_CALLBACK
#define HAVE_NODEITER_CALLBACK
/* Iterate over all nodes referenced by parentNodeId by calling the callback
* function for each child node (in ifdef because GCC/CLANG handle include order
* differently) */
typedef UA_StatusCode
(*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean isInverse,
	UA_NodeId referenceTypeId, void* handle);
#endif
UA_StatusCode UA_THREADSAFE
UA_Server_forEachChildNodeCall(UA_Server* server, UA_NodeId parentNodeId,
	UA_NodeIteratorCallback callback, void* handle);
#ifdef UA_ENABLE_DISCOVERY

9.6发现

/* Register the given server instance at the discovery server.
* This should be called periodically.
* The semaphoreFilePath is optional. If the given file is deleted,
* the server will automatically be unregistered. This could be
* for example a pid file which is deleted if the server crashes.
* *
When the server shuts down you need to call unregister.
* *
@param server
* @param client the client which is used to call the RegisterServer. It must
* already be connected to the correct endpoint
* @param semaphoreFilePath optional parameter pointing to semaphore file. */
UA_StatusCode UA_THREADSAFE
UA_Server_register_discovery(UA_Server* server, struct UA_Client* client,
	const char* semaphoreFilePath);
/* Unregister the given server instance from the discovery server.
* This should only be called when the server is shutting down.
* @param server
* @param client the client which is used to call the RegisterServer. It must
* already be connected to the correct endpoint */
UA_StatusCode UA_THREADSAFE
UA_Server_unregister_discovery(UA_Server* server, struct UA_Client* client);
/* Adds a periodic callback to register the server with the LDS (local discovery server)
* periodically. The interval between each register call is given as second parameter.
* It should be 10 minutes by default (= 10*60*1000).
* *
The delayFirstRegisterMs parameter indicates the delay for the first register call.
* If it is 0, the first register call will be after intervalMs milliseconds,
* otherwise the server’s first register will be after delayFirstRegisterMs.
* *
When you manually unregister the server, you also need to cancel the
* periodic callback, otherwise it will be automatically be registered again.
* *
If you call this method multiple times for the same discoveryServerUrl, the older
* periodic callback will be removed.
* *
@param server
* @param client the client which is used to call the RegisterServer.
* It must not yet be connected and will be connected for every register call
* to the given discoveryServerUrl.
* @param discoveryServerUrl where this server should register itself.
* The string will be copied internally. Therefore you can free it after calling this meth
* @param intervalMs
* @param delayFirstRegisterMs
* @param periodicCallbackId */
UA_StatusCode UA_THREADSAFE
UA_Server_addPeriodicServerRegisterCallback(UA_Server* server, struct UA_Client* client,
	const char* discoveryServerUrl,
	UA_Double intervalMs,
	UA_Double delayFirstRegisterMs,
	UA_UInt64* periodicCallbackId);
/* Callback for RegisterServer. Data is passed from the register call */
typedef void (*UA_Server_registerServerCallback)(const UA_RegisteredServer* registeredServer,
	void* data);
/* Set the callback which is called if another server registeres or unregisters
* with this instance. This callback is called every time the server gets a register
* call. This especially means that for every periodic server register the callback will
* be called.
* *
@param server
* @param cb the callback
* @param data data passed to the callback
* @return UA_STATUSCODE_SUCCESS on success */
void UA_THREADSAFE
UA_Server_setRegisterServerCallback(UA_Server* server, UA_Server_registerServerCallback cb,
	void* data);
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
/* Callback for server detected through mDNS. Data is passed from the register
* call
* *
@param isServerAnnounce indicates if the server has just been detected. If
* set to false, this means the server is shutting down.
* @param isTxtReceived indicates if we already received the corresponding TXT
* record with the path and caps data */
typedef void (*UA_Server_serverOnNetworkCallback)(const UA_ServerOnNetwork* serverOnNetwork,
	UA_Boolean isServerAnnounce,
	UA_Boolean isTxtReceived, void* data);
/* Set the callback which is called if another server is found through mDNS or
* deleted. It will be called for any mDNS message from the remote server, thus
* it may be called multiple times for the same instance. Also the SRV and TXT
* records may arrive later, therefore for the first call the server
* capabilities may not be set yet. If called multiple times, previous data will
* be overwritten.
* *
@param server
* @param cb the callback
* @param data data passed to the callback
* @return UA_STATUSCODE_SUCCESS on success */
void UA_THREADSAFE
UA_Server_setServerOnNetworkCallback(UA_Server* server,
	UA_Server_serverOnNetworkCallback cb,
	void* data);
#endif /* UA_ENABLE_DISCOVERY_MULTICAST */
#endif /* UA_ENABLE_DISCOVERY */

9.7信息模型回调

从信息模型到用户定义代码的回调可以发生在三个地方。

  • 自定义节点构造函数和析构函数
  • 将变量节点与外部数据源链接
  • MethodNode回调

9.7.1节点生命周期:构造函数、析构函数和节点上下文

为了完成节点的实例化,执行(用户定义)构造函数回调。可以为所有节点提供全局构造函数,也可以有特定于新节点类型定义的节点类型构造函数(附加到ObjectTypeNode或VariableTypeNode)。
在ObjectTypes和VariableTypes的层次结构中,只执行为新节点定义的(最低)类型的构造函数。请注意,每个对象和变量只能有一个istypeofreference。但从技术上讲,类型节点可以有多个hasSubType引用来实现多重继承。构造函数中的(多重)继承问题需要用户解决。
当节点被销毁时,在全局析构函数之前调用节点类型析构函数。因此,整个节点生命周期如下:
1.全局构造函数(在服务器配置中设置)
2.节点类型构造函数(用于VariableType或ObjectTypes)
3.(节点使用期限)
4.节点类型析构函数
5.全局析构函数
构造函数和析构函数回调可以设置为NULL,在这种情况下不使用。如果节点类型构造函数失败,则在移除节点之前将调用全局析构函数。析构函数被认为永远不会失败。
每个节点都携带一个用户上下文和一个构造函数上下文指针。用户上下文用于将自定义数据附加到节点。但是(用户定义的)构造函数和析构函数可以替换用户上下文指针,如果他们愿意的话。构造函数的初始值为NULL。在网络上使用AddNodes服务时,新节点的用户上下文指针最初也被设置为NULL。

/* To be set in the server config. */
typedef struct {
	/* Can be NULL. May replace the nodeContext */
	UA_StatusCode(*constructor)(UA_Server* server,
		const UA_NodeId* sessionId, void* sessionContext,
		const UA_NodeId* nodeId, void** nodeContext);
	/* Can be NULL. The context cannot be replaced since the node is destroyed
	* immediately afterwards anyway. */
	void (*destructor)(UA_Server* server,
		const UA_NodeId* sessionId, void* sessionContext,
		const UA_NodeId* nodeId, void* nodeContext);
	/* Can be NULL. Called during recursive node instantiation. While mandatory
	* child nodes are automatically created if not already present, optional child
	* nodes are not. This callback can be used to define whether an optional child
	* node should be created.
	* *
	@param server The server executing the callback
	* @param sessionId The identifier of the session
	* @param sessionContext Additional data attached to the session in the
	* access control layer
	* @param sourceNodeId Source node from the type definition. If the new node
	* shall be created, it will be a copy of this node.
	* @param targetParentNodeId Parent of the potential new child node
	* @param referenceTypeId Identifies the reference type which that the parent
	* node has to the new node.
	* @return Return UA_TRUE if the child node shall be instantiatet,
	* UA_FALSE otherwise. */
	UA_Boolean(*createOptionalChild)(UA_Server* server,
		const UA_NodeId* sessionId,
		void* sessionContext,
		const UA_NodeId* sourceNodeId,
		const UA_NodeId* targetParentNodeId,
		const UA_NodeId* referenceTypeId);
	/* Can be NULL. Called when a node is to be copied during recursive
	* node instantiation. Allows definition of the NodeId for the new node.
	* If the callback is set to NULL or the resulting NodeId is UA_NODEID_NULL,
	* then a random NodeId will be generated.
	* *
	@param server The server executing the callback
	* @param sessionId The identifier of the session
	* @param sessionContext Additional data attached to the session in the
	* access control layer
	* @param sourceNodeId Source node of the copy operation
	* @param targetParentNodeId Parent node of the new node
	* @param referenceTypeId Identifies the reference type which that the parent
	* node has to the new node. */
	UA_StatusCode(*generateChildNodeId)(UA_Server* server,
		const UA_NodeId* sessionId, void* sessionContext,
		const UA_NodeId* sourceNodeId,
		const UA_NodeId* targetParentNodeId,
		const UA_NodeId* referenceTypeId,
		UA_NodeId* targetNodeId);
} UA_GlobalNodeLifecycle;
typedef struct {
	/* Can be NULL. May replace the nodeContext */
	UA_StatusCode(*constructor)(UA_Server* server,
		const UA_NodeId* sessionId, void* sessionContext,
		const UA_NodeId* typeNodeId, void* typeNodeContext,
		const UA_NodeId* nodeId, void** nodeContext);
	/* Can be NULL. May replace the nodeContext. */
	void (*destructor)(UA_Server* server,
		const UA_NodeId* sessionId, void* sessionContext,
		const UA_NodeId* typeNodeId, void* typeNodeContext,
		const UA_NodeId* nodeId, void** nodeContext);
} UA_NodeTypeLifecycle;
UA_StatusCode UA_THREADSAFE
UA_Server_setNodeTypeLifecycle(UA_Server* server, UA_NodeId nodeId,
	UA_NodeTypeLifecycle lifecycle);
UA_StatusCode UA_THREADSAFE
UA_Server_getNodeContext(UA_Server* server, UA_NodeId nodeId,
	void** nodeContext);
/* Careful! The user has to ensure that the destructor callbacks still work. */
UA_StatusCode UA_THREADSAFE
UA_Server_setNodeContext(UA_Server* server, UA_NodeId nodeId,
	void* nodeContext);

9.7.2数据源回调

服务器有一种处理变量内容的独特方式。与存储附加到变量节点的变量不同,节点可以指向具有本地数据提供程序的函数。每当读取value属性时,都会调用该函数,并要求该函数提供包含值内容和附加时间戳的UA_DataValue返回值。
应该实现read回调。可以将write回调设置为空指针。

typedef struct {
	/* Copies the data from the source into the provided value.
	* *
	!! ZERO-COPY OPERATIONS POSSIBLE !!
	* It is not required to return a copy of the actual content data. You can
	* return a pointer to memory owned by the user. Memory can be reused
	* between read callbacks of a DataSource, as the result is already encoded
	* on the network buffer between each read operation.
	* *
	To use zero-copy reads, set the value of the ‘value->value‘ Variant
	* without copying, e.g. with ‘UA_Variant_setScalar‘. Then, also set
	* ‘value->value.storageType‘ to ‘UA_VARIANT_DATA_NODELETE‘ to prevent the
	* memory being cleaned up. Don’t forget to also set ‘value->hasValue‘ to
	* true to indicate the presence of a value.
	* *
	@param server The server executing the callback
	* @param sessionId The identifier of the session
	* @param sessionContext Additional data attached to the session in the
	* access control layer
	* @param nodeId The identifier of the node being read from
	* @param nodeContext Additional data attached to the node by the user
	* @param includeSourceTimeStamp If true, then the datasource is expected to
	* set the source timestamp in the returned value
	* @param range If not null, then the datasource shall return only a
	* selection of the (nonscalar) data. Set
	* UA_STATUSCODE_BADINDEXRANGEINVALID in the value if this does not
	* apply
	* @param value The (non-null) DataValue that is returned to the client. The
	* data source sets the read data, the result status and optionally a
	* sourcetimestamp.
	* @return Returns a status code for logging. Error codes intended for the
	* original caller are set in the value. If an error is returned,
	* then no releasing of the value is done
	*/
	UA_StatusCode(*read)(UA_Server* server, const UA_NodeId* sessionId,
		void* sessionContext, const UA_NodeId* nodeId,
		void* nodeContext, UA_Boolean includeSourceTimeStamp,
		const UA_NumericRange* range, UA_DataValue* value);
	/* Write into a data source. This method pointer can be NULL if the
	* operation is unsupported.
	* *
	@param server The server executing the callback
	* @param sessionId The identifier of the session
	* @param sessionContext Additional data attached to the session in the
	* access control layer
	* @param nodeId The identifier of the node being written to
	* @param nodeContext Additional data attached to the node by the user
	* @param range If not NULL, then the datasource shall return only a
	* selection of the (nonscalar) data. Set
	* UA_STATUSCODE_BADINDEXRANGEINVALID in the value if this does not
	* apply
	* @param value The (non-NULL) DataValue that has been written by the client.
	* The data source contains the written data, the result status and
	* optionally a sourcetimestamp
	* @return Returns a status code for logging. Error codes intended for the
	* original caller are set in the value. If an error is returned,
	* then no releasing of the value is done
	*/
	UA_StatusCode(*write)(UA_Server* server, const UA_NodeId* sessionId,
		void* sessionContext, const UA_NodeId* nodeId,
		void* nodeContext, const UA_NumericRange* range,
		const UA_DataValue* value);
} UA_DataSource;
UA_StatusCode UA_THREADSAFE
UA_Server_setVariableNode_dataSource(UA_Server* server, const UA_NodeId nodeId,
	const UA_DataSource dataSource);

9.7.3值回调

值回调可以附加到变量和变量类型节点。如果不为空,则分别在读前和写后调用它们。

typedef struct {
	/* Called before the value attribute is read. It is possible to write into the
	* value attribute during onRead (using the write service). The node is
	* re-opened afterwards so that changes are considered in the following read
	* operation.
	* *
	@param handle Points to user-provided data for the callback.
	* @param nodeid The identifier of the node.
	* @param data Points to the current node value.
	* @param range Points to the numeric range the client wants to read from
	* (or NULL). */
	void (*onRead)(UA_Server* server, const UA_NodeId* sessionId,
		void* sessionContext, const UA_NodeId* nodeid,
		void* nodeContext, const UA_NumericRange* range,
		const UA_DataValue* value);
	/* Called after writing the value attribute. The node is re-opened after
	* writing so that the new value is visible in the callback.
	* *
	@param server The server executing the callback
	* @sessionId The identifier of the session
	* @sessionContext Additional data attached to the session
	* in the access control layer
	* @param nodeid The identifier of the node.
	* @param nodeUserContext Additional data attached to the node by
	* the user.
	* @param nodeConstructorContext Additional data attached to the node
	* by the type constructor(s).
	* @param range Points to the numeric range the client wants to write to (or
	* NULL). */
	void (*onWrite)(UA_Server* server, const UA_NodeId* sessionId,
		void* sessionContext, const UA_NodeId* nodeId,
		void* nodeContext, const UA_NumericRange* range,
		const UA_DataValue* data);
} UA_ValueCallback;
UA_StatusCode UA_THREADSAFE
UA_Server_setVariableNode_valueCallback(UA_Server* server,
	const UA_NodeId nodeId,
	const UA_ValueCallback callback);

9.7.4本地监测数据

MonitoredItems 与OPC-UA的订阅机制一起用于传输数据更改和事件的通知。MonitoredItems 也可以在本地注册。然后通知被转发到用户定义的回调,而不是远程客户端。

#ifdef UA_ENABLE_SUBSCRIPTIONS
typedef void (*UA_Server_DataChangeNotificationCallback)
(UA_Server* server, UA_UInt32 monitoredItemId, void* monitoredItemContext,
	const UA_NodeId* nodeId, void* nodeContext, UA_UInt32 attributeId,
	const UA_DataValue* value);
typedef void (*UA_Server_EventNotificationCallback)
(UA_Server* server, UA_UInt32 monId, void* monContext,
	size_t nEventFields, const UA_Variant* eventFields);
/* Create a local MonitoredItem with a sampling interval that detects data
* changes.
* *
@param server The server executing the MonitoredItem
* @timestampsToReturn Shall timestamps be added to the value for the callback?
* @item The parameters of the new MonitoredItem. Note that the attribute of the
* ReadValueId (the node that is monitored) can not be
* ‘‘UA_ATTRIBUTEID_EVENTNOTIFIER‘‘. A different callback type needs to be
* registered for event notifications.
* @monitoredItemContext A pointer that is forwarded with the callback
* @callback The callback that is executed on detected data changes
* *
@return Returns a description of the created MonitoredItem. The structure
* also contains a StatusCode (in case of an error) and the identifier of the
* new MonitoredItem. */
UA_MonitoredItemCreateResult UA_THREADSAFE
UA_Server_createDataChangeMonitoredItem(UA_Server * server,
	UA_TimestampsToReturn timestampsToReturn,
	const UA_MonitoredItemCreateRequest item,
	void* monitoredItemContext,
	UA_Server_DataChangeNotificationCallback callback);
/* UA_MonitoredItemCreateResult */
/* UA_Server_createEventMonitoredItem(UA_Server *server, */
/* UA_TimestampsToReturn timestampsToReturn, */
/* const UA_MonitoredItemCreateRequest item, void *context, */
/* UA_Server_EventNotificationCallback callback); */
UA_StatusCode UA_THREADSAFE
UA_Server_deleteMonitoredItem(UA_Server* server, UA_UInt32 monitoredItemId);
#endif

9.7.5方法回调

当通过网络添加方法节点时,方法回调设置为NULL(不可执行)。理论上,当确实需要通过网络添加方法时,可以通过全局构造函数中的 UA_Server_setMethodNode_callback 添加回调。有关在对象上调用方法的信息,请参阅与对象交互一节。

typedef UA_StatusCode
(*UA_MethodCallback)(UA_Server *server, const UA_NodeId *sessionId,
			void *sessionContext, const UA_NodeId *methodId,
			void *methodContext, const UA_NodeId *objectId,
			void *objectContext, size_t inputSize,
			const UA_Variant *input, size_t outputSize,
			UA_Variant *output);
#ifdef UA_ENABLE_METHODCALLS
UA_StatusCode UA_THREADSAFE
UA_Server_setMethodNode_callback(UA_Server *server,
								const UA_NodeId methodNodeId,
								UA_MethodCallback methodCallback);
#endif

9.8与对象交互

信息模型中的对象表示为对象节点。提供了一些方便的函数来简化与对象的交互。

/* Write an object property. The property is represented as a VariableNode with
* a ‘‘HasProperty‘‘ reference from the ObjectNode. The VariableNode is
* identified by its BrowseName. Writing the property sets the value attribute
* of the VariableNode.
* *
@param server The server object
* @param objectId The identifier of the object (node)
* @param propertyName The name of the property
* @param value The value to be set for the event attribute
* @return The StatusCode for setting the event attribute */
UA_StatusCode UA_THREADSAFE
UA_Server_writeObjectProperty(UA_Server* server, const UA_NodeId objectId,
	const UA_QualifiedName propertyName,
	const UA_Variant value);
/* Directly point to the scalar value instead of a variant */
UA_StatusCode UA_THREADSAFE
UA_Server_writeObjectProperty_scalar(UA_Server* server, const UA_NodeId objectId,
	const UA_QualifiedName propertyName,
	const void* value, const UA_DataType* type);
/* Read an object property.
* *
@param server The server object
* @param objectId The identifier of the object (node)
* @param propertyName The name of the property
* @param value Contains the property value after reading. Must not be NULL.
* @return The StatusCode for setting the event attribute */
UA_StatusCode UA_THREADSAFE
UA_Server_readObjectProperty(UA_Server* server, const UA_NodeId objectId,
	const UA_QualifiedName propertyName,
	UA_Variant* value);
#ifdef UA_ENABLE_METHODCALLS
UA_CallMethodResult UA_THREADSAFE
UA_Server_call(UA_Server* server, const UA_CallMethodRequest* request);
#endif

9.9节点新增删除

在运行时创建动态节点实例时,您可能不关心新节点的特定NodeId,只要您以后可以引用它。当传递带有数字标识符0的数字NodeId时,堆栈会将其计算为“在该命名空间中选择一个随机未分配的数字NodeId”。要找出实际分配给新节点的NodeId,可以传递一个指针outNewNodeId,它将(在成功插入节点之后)包含新节点的NodeId。如果不需要此结果,也可以传递空指针。
请参阅节点生命周期:构造函数、析构函数和节点上下文一节,关于构造函数和将用户定义的数据附加到节点。
节点添加和删除的方法主要采用未修改的常量参数。创建节点时,会创建节点标识符、节点属性等的深层副本。因此,例如,可以使用指向堆栈上内存位置的值属性(变量)调用UA_Server_addVariablenode。如果需要对变量值进行更改以显示在特定内存位置,请使用数据源回调或值回调。

/* Protect against redundant definitions for server/client */
#ifndef UA_DEFAULT_ATTRIBUTES_DEFINED
#define UA_DEFAULT_ATTRIBUTES_DEFINED
/* The default for variables is "BaseDataType" for the datatype, -2 for the
* valuerank and a read-accesslevel. */
extern const UA_VariableAttributes UA_VariableAttributes_default;
extern const UA_VariableTypeAttributes UA_VariableTypeAttributes_default;
/* Methods are executable by default */
extern const UA_MethodAttributes UA_MethodAttributes_default;
/* The remaining attribute definitions are currently all zeroed out */
extern const UA_ObjectAttributes UA_ObjectAttributes_default;
extern const UA_ObjectTypeAttributes UA_ObjectTypeAttributes_default;
extern const UA_ReferenceTypeAttributes UA_ReferenceTypeAttributes_default;
extern const UA_DataTypeAttributes UA_DataTypeAttributes_default;
extern const UA_ViewAttributes UA_ViewAttributes_default;
#endif
/* Don’t use this function. There are typed versions as inline functions. */
UA_StatusCode UA_THREADSAFE
__UA_Server_addNode(UA_Server* server, const UA_NodeClass nodeClass,
	const UA_NodeId* requestedNewNodeId,
	const UA_NodeId* parentNodeId,
	const UA_NodeId* referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_NodeId* typeDefinition,
	const UA_NodeAttributes* attr,
	const UA_DataType* attributeType,
	void* nodeContext, UA_NodeId* outNewNodeId);
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_addVariableNode(UA_Server* server, const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_NodeId typeDefinition,
	const UA_VariableAttributes attr,
	void* nodeContext, UA_NodeId* outNewNodeId) {
	return __UA_Server_addNode(server, UA_NODECLASS_VARIABLE, &requestedNewNodeId,
		&parentNodeId, &referenceTypeId, browseName,
		&typeDefinition, (const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],
		nodeContext, outNewNodeId);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_addVariableTypeNode(UA_Server* server,
	const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_NodeId typeDefinition,
	const UA_VariableTypeAttributes attr,
	void* nodeContext, UA_NodeId* outNewNodeId) {
	return __UA_Server_addNode(server, UA_NODECLASS_VARIABLETYPE,
		&requestedNewNodeId, &parentNodeId, &referenceTypeId,
		browseName, &typeDefinition,
		(const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],
		nodeContext, outNewNodeId);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_addObjectNode(UA_Server* server, const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_NodeId typeDefinition,
	const UA_ObjectAttributes attr,
	void* nodeContext, UA_NodeId* outNewNodeId) {
	return __UA_Server_addNode(server, UA_NODECLASS_OBJECT, &requestedNewNodeId,
		&parentNodeId, &referenceTypeId, browseName,
		&typeDefinition, (const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
		nodeContext, outNewNodeId);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_addObjectTypeNode(UA_Server* server, const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_ObjectTypeAttributes attr,
	void* nodeContext, UA_NodeId* outNewNodeId) {
	return __UA_Server_addNode(server, UA_NODECLASS_OBJECTTYPE, &requestedNewNodeId,
		&parentNodeId, &referenceTypeId, browseName,
		&UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],
		nodeContext, outNewNodeId);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_addViewNode(UA_Server* server, const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_ViewAttributes attr,
	void* nodeContext, UA_NodeId* outNewNodeId) {
	return __UA_Server_addNode(server, UA_NODECLASS_VIEW, &requestedNewNodeId,
		&parentNodeId, &referenceTypeId, browseName,
		&UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_VIEWATTRIBUTES],
		nodeContext, outNewNodeId);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_addReferenceTypeNode(UA_Server* server,
	const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_ReferenceTypeAttributes attr,
	void* nodeContext, UA_NodeId* outNewNodeId) {
	return __UA_Server_addNode(server, UA_NODECLASS_REFERENCETYPE,
		&requestedNewNodeId, &parentNodeId, &referenceTypeId,
		browseName, &UA_NODEID_NULL,
		(const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],
		nodeContext, outNewNodeId);
}
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_addDataTypeNode(UA_Server* server,
	const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_DataTypeAttributes attr,
	void* nodeContext, UA_NodeId* outNewNodeId) {
	return __UA_Server_addNode(server, UA_NODECLASS_DATATYPE, &requestedNewNodeId,
		&parentNodeId, &referenceTypeId, browseName,
		&UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],
		154 Chapter 9. Serveropen62541 Documentation, Release 1.1.2
		nodeContext, outNewNodeId);
}
UA_StatusCode UA_THREADSAFE
UA_Server_addDataSourceVariableNode(UA_Server* server,
	const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_NodeId typeDefinition,
	const UA_VariableAttributes attr,
	const UA_DataSource dataSource,
	void* nodeContext, UA_NodeId* outNewNodeId);
#ifdef UA_ENABLE_METHODCALLS
UA_StatusCode UA_THREADSAFE
UA_Server_addMethodNodeEx(UA_Server* server, const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_MethodAttributes attr, UA_MethodCallback method,
	size_t inputArgumentsSize, const UA_Argument* inputArguments,
	const UA_NodeId inputArgumentsRequestedNewNodeId,
	UA_NodeId* inputArgumentsOutNewNodeId,
	size_t outputArgumentsSize, const UA_Argument* outputArguments,
	const UA_NodeId outputArgumentsRequestedNewNodeId,
	UA_NodeId* outputArgumentsOutNewNodeId,
	void* nodeContext, UA_NodeId* outNewNodeId);
static UA_INLINE UA_THREADSAFE UA_StatusCode
UA_Server_addMethodNode(UA_Server* server, const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName, const UA_MethodAttributes attr,
	UA_MethodCallback method,
	size_t inputArgumentsSize, const UA_Argument* inputArguments,
	size_t outputArgumentsSize, const UA_Argument* outputArguments,
	void* nodeContext, UA_NodeId* outNewNodeId) {
	return UA_Server_addMethodNodeEx(server, requestedNewNodeId, parentNodeId,
		referenceTypeId, browseName, attr, method,
		inputArgumentsSize, inputArguments, UA_NODEID_NULL, NULL,
		outputArgumentsSize, outputArguments, UA_NODEID_NULL, NULL,
		nodeContext, outNewNodeId);
}
#endif

方法对UA_Server_addNode_begin和_finish将AddNodes服务分成两部分。如果在完成实例化之前需要修改节点,这是很有用的。例如,添加具有特定 NodeIds 的子节点。否则,将使用伪随机唯一NodeIds添加强制子节点(例如,对象类型的子节点)。通过匹配的BrowseName在完成部分检测到现有子级。
_begin方法:

  • 准备节点并将其添加到nodestore
  • 从TypeDefinition节点内部复制一些未分配的属性
  • 添加对父对象的引用(以及类型定义,如果适用)
  • 执行变量类型检查。
    如果将parentNodeId和referenceTypeId设置为UA_node_ID_NULL,则可以添加没有父节点的对象节点。然后,在调用 _finish方法之前,您需要自己添加父引用和hasTypeDef引用。并不是说这只允许用于对象节点。
    _finish方法:
  • 强制复制子集
  • 最后调用节点构造函数
  • 如果遇到错误,可能会移除节点。
    特殊的UA_Server_addMethodNode_finish方法需要用于方法节点,因为在那里您需要显式指定在完成步骤中添加的输入和输出参数(如果还没有),变量的VariableAttributes,对象的ObjectAttributes,等等。如果适用,将从TypeDefinition节点获取缺少的属性。
UA_StatusCode UA_THREADSAFE
UA_Server_addNode_begin(UA_Server* server, const UA_NodeClass nodeClass,
	const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_NodeId typeDefinition,
	const void* attr, const UA_DataType* attributeType,
	void* nodeContext, UA_NodeId* outNewNodeId);
UA_StatusCode UA_THREADSAFE
UA_Server_addNode_finish(UA_Server* server, const UA_NodeId nodeId);
#ifdef UA_ENABLE_METHODCALLS
UA_StatusCode UA_THREADSAFE
UA_Server_addMethodNode_finish(UA_Server* server, const UA_NodeId nodeId,
	UA_MethodCallback method,
	size_t inputArgumentsSize, const UA_Argument* inputArguments,
	size_t outputArgumentsSize, const UA_Argument* outputArguments);
#endif
/* Deletes a node and optionally all references leading to the node. */
UA_StatusCode UA_THREADSAFE
UA_Server_deleteNode(UA_Server* server, const UA_NodeId nodeId,
	UA_Boolean deleteReferences);

9.10 引用管理

UA_StatusCode UA_THREADSAFE
UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
		const UA_NodeId refTypeId,
		const UA_ExpandedNodeId targetId, UA_Boolean isForward);
UA_StatusCode UA_THREADSAFE
UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId,
		const UA_NodeId referenceTypeId, UA_Boolean isForward,
		const UA_ExpandedNodeId targetNodeId,
		UA_Boolean deleteBidirectional);

9.11事件(下面的文档可能不全,参照其它文档再补充)

UA_Server_createEvent方法创建一个事件并将其表示为节点。节点接收一个唯一的EventId,它会自动添加到节点中。该方法向对象节点返回一个NodeId,该节点通过outNodeId表示事件。NodeId可用于设置事件的属性。生成的NodeId始终是数字。outNodeId不能为NULL。
注意:要在UAExpert中查看事件,必须给字段Time赋值!
方法UA_Server_triggeredevent通过将事件添加到指定的源节点及其所有父节点的所有监视项来“触发”事件。由监视项指定的任何筛选器都将自动执行应用。使用此方法删除UA_Server_createEvent生成的节点。新事件的EventId将自动生成,并通过outEventId返回。如果不需要EventId,则可以传递NULL。deleteEventNode指定在调用方法后是否应删除事件的节点表示形式。如果频繁触发具有相似属性的事件,则此功能非常有用。UA_TRUE将导致节点被删除。

#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
/* The EventQueueOverflowEventType is defined as abstract, therefore we can not
* create an instance of that type directly, but need to create a subtype. The
* following is an arbitrary number which shall refer to our internal overflow
* type. This is already posted on the OPC Foundation bug tracker under the
* following link for clarification:
* https://opcfoundation-onlineapplications.org/mantis/view.php?id=4206 */
# define UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE 4035
/* Creates a node representation of an event
* *
@param server The server object
* @param eventType The type of the event for which a node should be created
* @param outNodeId The NodeId of the newly created node for the event
* @return The StatusCode of the UA_Server_createEvent method */
UA_StatusCode UA_THREADSAFE
UA_Server_createEvent(UA_Server* server, const UA_NodeId eventType,
	UA_NodeId* outNodeId);
/* Triggers a node representation of an event by applying EventFilters and
adding the event to the appropriate queues.
* @param server The server object
* @param eventNodeId The NodeId of the node representation of the event which should be triggered
* @param outEvent the EventId of the new event
* @param deleteEventNode Specifies whether the node representation of the event should be deleted
* @return The StatusCode of the UA_Server_triggerEvent method */
UA_StatusCode UA_THREADSAFE
UA_Server_triggerEvent(UA_Server* server, const UA_NodeId eventNodeId, const UA_NodeId originId,
	UA_ByteString* outEventId, const UA_Boolean deleteEventNode);
#endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */
#ifdef UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS
typedef enum UA_TwoStateVariableCallbackType {
	UA_ENTERING_ENABLEDSTATE,
	UA_ENTERING_ACKEDSTATE,
	UA_ENTERING_CONFIRMEDSTATE,
	UA_ENTERING_ACTIVESTATE
} UA_TwoStateVariableCallbackType;

用于设置用户特定回调的回调原型

typedef UA_StatusCode
(*UA_TwoStateVariableChangeCallback)(UA_Server *server, const UA_NodeId *condition);

创建条件实例。函数首先检查传递的conditionType是否为conditionType的子类型。然后检查条件源是否具有对其父级的HasEventSource引用。否则,将在条件源和服务器对象之间创建HasEventSource引用。若要在地址空间中公开条件,应传递层次引用类型以创建对条件源的引用。否则,应传递UA_NODEID_NULL以使条件不暴露。

@param server服务器对象
@param conditionId请求的条件对象的NodeId。对于NS Idx=0的随机标识,应为UA_NODEID_NULL。
@param conditionType 节点表示形式为NodeId
@param conditionName要创建的条件的名称
@param conditionSource要创建的条件源的NodeId(条件的父项)
@param hierarchicalreferencetype 条件与其源之间的层次引用类型的NodeId
@param outConditionId 已创建条件的NodeId
@return 方法的执行结果

UA_StatusCode
UA_Server_createCondition(UA_Server *server,
		const UA_NodeId conditionId, const UA_NodeId conditionType,
		UA_QualifiedName conditionName, const UA_NodeId conditionSource,
		const UA_NodeId hierarchialReferenceType, UA_NodeId *outConditionId);

设置条件字段的值。
@param server The server object
@param condition The NodeId of the node representation of the Condition Instance
@param value Variant Value to be written to the Field
@param fieldName Name of the Field in which the value should be written
@return The StatusCode of the UA_Server_setConditionField method*/
set the value of property of condition field.
@param server The server object
@param condition The NodeId of the node representation of the Condition Instance
@param value Variant Value to be written to the Field
@param variableFieldName Name of the Field which has a property
@param variablePropertyName Name of the Field Property in which the value should be written
@return The StatusCode of the UA_Server_setConditionVariableFieldProperty*/
triggers an event only for an enabled condition. The condition list is updated then with the last generated EventId.
@param server The server object
@param condition The NodeId of the node representation of the Condition Instance
@param conditionSource The NodeId of the node representation of the Condition Source
@param outEventId last generated EventId
@return The StatusCode of the UA_Server_triggerConditionEvent method*/
add an optional condition field using its name. (TODO Adding optional methods is not implemented yet)
@param server The server object
@param condition The NodeId of the node representation of the Condition Instance
@param conditionType The NodeId of the node representation of the Condition Type from which the optional field comes
@param fieldName Name of the optional field
@param outOptionalVariable The NodeId of the created field (Variable Node)
@return The StatusCode of the UA_Server_addConditionOptionalField method*/
Function used to set a user specific callback to TwoStateVariable Fields of a condition. The callbacks will be called before triggering the events when transition to true State of EnabledState/Id, AckedState/Id, ConfirmedState/Id and ActiveState/Id occurs.
@param server The server object
@param condition The NodeId of the node representation of the Condition Instance
@param conditionSource The NodeId of the node representation of the Condition Source
@param removeBranch (Not Implemented yet)
@param callback User specific callback function
@param callbackType Callback function type, indicates where it should be called
@return The StatusCode of the UA_Server_setConditionTwoStateVariableCallback method*/

9.12 工具函数

/* Add a new namespace to the server. Returns the index of the new namespace */
UA_UInt16 UA_THREADSAFE UA_Server_addNamespace(UA_Server* server, const char* name);
/* Get namespace by name from the server. */
UA_StatusCode UA_THREADSAFE
UA_Server_getNamespaceByName(UA_Server* server, const UA_String namespaceUri,
	size_t* foundIndex);
#ifdef UA_ENABLE_HISTORIZING
UA_Boolean UA_THREADSAFE
UA_Server_AccessControl_allowHistoryUpdateUpdateData(UA_Server* server,
	const UA_NodeId* sessionId, void* sessionCont
	const UA_NodeId* nodeId,
	UA_PerformUpdateType performInsertReplace,
	const UA_DataValue* value);
158 Chapter 9. Serveropen62541 Documentation, Release 1.1.2
UA_Boolean UA_THREADSAFE
UA_Server_AccessControl_allowHistoryUpdateDeleteRawModified(UA_Server * server,
	const UA_NodeId * sessionId, void* sess
	const UA_NodeId * nodeId,
	UA_DateTime startTimestamp,
	UA_DateTime endTimestamp,
	bool isDeleteModified);
#endif // UA_ENABLE_HISTORIZING

9.13异步操作

有些操作(例如读取需要预热的传感器)可能需要相当长的时间。为了在这种操作期间不阻塞服务器,可以将其“外包”给工作线程。
以 CallRequest 为例。它被分成单独的方法调用操作。如果方法被标记为async,那么操作将被放入队列中,在队列中由worker检索它。工作线程在准备好时返回结果。有关用法,请参阅/examples/tutorial_server_method_async.c中的示例。
请注意,当工作线程检索到操作时,该操作也可能超时(请参阅服务器配置中的asyncOperationTimeout设置)。

#if UA_MULTITHREADING >= 100
/* Set the async flag in a method node */
UA_StatusCode
UA_Server_setMethodNodeAsync(UA_Server* server, const UA_NodeId id,
	UA_Boolean isAsync);
typedef enum {
	UA_ASYNCOPERATIONTYPE_INVALID, /* 0, the default */
	UA_ASYNCOPERATIONTYPE_CALL
	/* UA_ASYNCOPERATIONTYPE_READ, */
	/* UA_ASYNCOPERATIONTYPE_WRITE, */
} UA_AsyncOperationType;
typedef union {
	UA_CallMethodRequest callMethodRequest;
	/* UA_ReadValueId readValueId; */
	/* UA_WriteValue writeValue; */
} UA_AsyncOperationRequest;
typedef union {
	UA_CallMethodResult callMethodResult;
	/* UA_DataValue readResult; */
	/* UA_StatusCode writeResult; */
} UA_AsyncOperationResponse;
/* Get the next async operation without blocking
* *
@param server The server object
* @param type The type of the async operation
* @param request Receives pointer to the operation
* @param context Receives the pointer to the operation context
* @param timeout The timestamp when the operation times out and can
* no longer be returned to the client. The response has to
* be set in UA_Server_setAsyncOperationResult in any case.
* @return false if queue is empty, true else */
UA_Boolean
UA_Server_getAsyncOperationNonBlocking(UA_Server* server, UA_AsyncOperationType* type,
	const UA_AsyncOperationRequest** request,
	void** context, UA_DateTime* timeout);
/* UA_Boolean */
/* UA_Server_getAsyncOperationBlocking(UA_Server *server, UA_AsyncOperationType *type, */
/* const UA_AsyncOperationRequest **request, */
/* void **context, UA_DateTime *timeout); */
/* Submit an async operation result
* *
@param server The server object
* @param response Pointer to the operation result
* @param context Pointer to the operation context */
void
UA_Server_setAsyncOperationResult(UA_Server* server,
	const UA_AsyncOperationResponse* response,
	void* context);
/* Get the next async operation. Attention! This method is deprecated and has
* been replaced by UA_Server_getAsyncOperationNonBlocking! */
UA_DEPRECATED UA_Boolean
UA_Server_getAsyncOperation(UA_Server* server, UA_AsyncOperationType* type,
	const UA_AsyncOperationRequest** request,
	void** context);
#endif /* !UA_MULTITHREADING >= 100 */

9.14统计

跟踪堆栈当前状态的统计计数器。计数器按OPC UA通信层结构。

typedef struct {
	UA_NetworkStatistics ns;
	UA_SecureChannelStatistics scs;
	UA_SessionStatistics ss;
} UA_ServerStatistics;
UA_ServerStatistics
UA_Server_getStatistics(UA_Server *server);

10 客户端

客户端实现允许远程访问所有OPC UA服务。为了方便起见,一些功能已经被封装在高级抽象中

10.1客户端配置

客户端配置用于设置连接参数和客户端使用的其他设置。在将配置传递给客户端后,不应修改该配置。目前,一次只能有一个客户机使用配置。
/plugins文件夹中提供了配置示例。常用用法如下:
1.创建以默认设置为起点的客户端配置
2.修改配置,例如修改超时
3.用它实例化一个客户机
4.关闭客户端后,清理配置(可用内存)
教程<4.Tutorials>提供了一个很好的起点。

typedef struct {
	/* Basic client configuration */
	void* clientContext; /* User-defined data attached to the client */
	UA_Logger logger; /* Logger used by the client */
	UA_UInt32 timeout; /* Response timeout in ms */
	/* The description must be internally consistent.
	* - The ApplicationUri set in the ApplicationDescription must match the
	* URI set in the server certificate */
	UA_ApplicationDescription clientDescription;
	/* Basic connection configuration */
	UA_ExtensionObject userIdentityToken; /* Configured User-Identity Token */
	UA_MessageSecurityMode securityMode; /* None, Sign, SignAndEncrypt. The
	* default is invalid. This indicates
	* the client to select any matching
	* endpoint. */
	UA_String securityPolicyUri; /* SecurityPolicy for the SecureChannel. An
	* empty string indicates the client to select
	* any matching SecurityPolicy. */
	/* Advanced connection configuration
	* *
	If either endpoint or userTokenPolicy has been set (at least one non-zero
	* byte in either structure), then the selected Endpoint and UserTokenPolicy
	* overwrite the settings in the basic connection configuration. The
	* userTokenPolicy array in the EndpointDescription is ignored. The selected
	* userTokenPolicy is set in the dedicated configuration field.
	* *
	If the advanced configuration is not set, the client will write to it the
	* selected Endpoint and UserTokenPolicy during GetEndpoints.
	* *
	The information in the advanced configuration is used during reconnect
	* when the SecureChannel was broken. */
	UA_EndpointDescription endpoint;
	UA_UserTokenPolicy userTokenPolicy;
	/* Advanced client configuration */
	UA_UInt32 secureChannelLifeTime; /* Lifetime in ms (then the channel needs
	to be renewed) */
	UA_UInt32 requestedSessionTimeout; /* Session timeout in ms */
	UA_ConnectionConfig localConnectionConfig;
	UA_UInt32 connectivityCheckInterval; /* Connectivity check interval in ms.
	* 0 = background task disabled */
	const UA_DataTypeArray* customDataTypes; /* Custom DataTypes. Attention!
	* Custom datatypes are not cleaned
	* up together with the
	* configuration. So it is possible
	* to allocate them on ROM. */
	/* Available SecurityPolicies */
	size_t securityPoliciesSize;
	UA_SecurityPolicy* securityPolicies;
	/* Certificate Verification Plugin */
	UA_CertificateVerification certificateVerification;
	/* Callbacks for async connection handshakes */
	UA_ConnectClientConnection initConnectionFunc;
	UA_StatusCode(*pollConnectionFunc)(UA_Client* client, void* context,
		UA_UInt32 timeout);
	/* Callback for state changes. The client state is differentated into the
	* SecureChannel state and the Session state. The connectStatus is set if
	* the client connection (including reconnects) has failed and the client
	* has to "give up". If the connectStatus is not set, the client still has
	* hope to connect or recover. */
	void (*stateCallback)(UA_Client* client,
		UA_SecureChannelState channelState,
		UA_SessionState sessionState,
		UA_StatusCode connectStatus);
	/* When connectivityCheckInterval is greater than 0, every
	* connectivityCheckInterval (in ms), a async read request is performed on
	* the server. inactivityCallback is called when the client receive no
	* response for this read request The connection can be closed, this in an
	* attempt to recreate a healthy connection. */
	void (*inactivityCallback)(UA_Client* client);
#ifdef UA_ENABLE_SUBSCRIPTIONS
	/* Number of PublishResponse queued up in the server */
	UA_UInt16 outStandingPublishRequests;
	/* If the client does not receive a PublishResponse after the defined delay
	* of ‘‘(sub->publishingInterval * sub->maxKeepAliveCount) +
	* client->config.timeout)‘‘, then subscriptionInactivityCallback is called
	* for the subscription.. */
	void (*subscriptionInactivityCallback)(UA_Client* client,
		UA_UInt32 subscriptionId,
		void* subContext);
#endif
} UA_ClientConfig;

10.2客户端生命周期

* plugins outside of the core library (for logging, etc) are already available
* during the initialization.
* *
UA_Client * UA_Client_new(void);
*/
/* Creates a new client. Moves the config into the client with a shallow copy.
* The config content is cleared together with the client. */
UA_Client*
UA_Client_newWithConfig(const UA_ClientConfig* config);
/* Returns the current state. All arguments except ‘‘client‘‘ can be NULL. */
void
UA_Client_getState(UA_Client* client,
	UA_SecureChannelState* channelState,
	UA_SessionState* sessionState,
	UA_StatusCode* connectStatus);
/* Get the client configuration */
UA_ClientConfig*
UA_Client_getConfig(UA_Client* client);
/* Get the client context */
static UA_INLINE void*
UA_Client_getContext(UA_Client* client) {
	return UA_Client_getConfig(client)->clientContext; /* Cannot fail */
} /
*(Disconnectand) delete the client * /
void
UA_Client_delete(UA_Client * client);

10.3连接到服务器

一旦客户端连接到endpointUrl,就不可能切换到其他服务器。必须为此创建一个新的客户端。
一旦建立了连接,客户端将保持连接打开,并在必要时重新连接。
如果连接无法恢复(state->connectStatus设置为错误),则客户端不再可用。如果需要,创建一个新客户机。

/* Connect to the server. First a SecureChannel is opened, then a Session. The
* client configuration restricts the SecureChannel selection and contains the
* UserIdentityToken for the Session.
* *
@param client to use
* @param endpointURL to connect (for example "opc.tcp://localhost:4840")
* @return Indicates whether the operation succeeded or returns an error code */
UA_StatusCode
UA_Client_connect(UA_Client* client, const char* endpointUrl);
/* Connect async (non-blocking) to the server. After initiating the connection,
* call UA_Client_run_iterate repeatedly until the connection is fully
* established. You can set a callback to client->config.stateCallback to be
* notified when the connection status changes. Or use UA_Client_getState to get
* the state manually. */
UA_StatusCode
UA_Client_connectAsync(UA_Client* client, const char* endpointUrl);
/* Connect to the server without creating a session
* *
@param client to use
* @param endpointURL to connect (for example "opc.tcp://localhost:4840")
* @return Indicates whether the operation succeeded or returns an error code */
UA_StatusCode
UA_Client_connectSecureChannel(UA_Client* client, const char* endpointUrl);
/* Connect async (non-blocking) only the SecureChannel */
UA_StatusCode
UA_Client_connectSecureChannelAsync(UA_Client* client, const char* endpointUrl);
/* Connect to the server and create+activate a Session with the given username
* and password. This first set the UserIdentityToken in the client config and
* then calls the regular connect method. */
static UA_INLINE UA_StatusCode
UA_Client_connectUsername(UA_Client* client, const char* endpointUrl,
	const char* username, const char* password) {
	UA_UserNameIdentityToken* identityToken = UA_UserNameIdentityToken_new();
	if (!identityToken)
		return UA_STATUSCODE_BADOUTOFMEMORY;
	identityToken->userName = UA_STRING_ALLOC(username);
	identityToken->password = UA_STRING_ALLOC(password);
	UA_ClientConfig* cc = UA_Client_getConfig(client);
	UA_ExtensionObject_clear(&cc->userIdentityToken);
	cc->userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
	cc->userIdentityToken.content.decoded.type = &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN];
	cc->userIdentityToken.content.decoded.data = identityToken;
	return UA_Client_connect(client, endpointUrl);
}
/*Disconnect and close a connection to the selected server.Disconnection is
* always performed async(without blocking). */
UA_StatusCode
UA_Client_disconnect(UA_Client * client);
/* Disconnect async. Run UA_Client_run_iterate until the callback notifies that
* all connections are closed. */
UA_StatusCode
UA_Client_disconnectAsync(UA_Client* client);
/* Disconnect the SecureChannel but keep the Session intact (if it exists).
* This is always an async (non-blocking) operation. */
UA_StatusCode
UA_Client_disconnectSecureChannel(UA_Client* client);
/* Deprecated methods */
UA_DEPRECATED static UA_INLINE UA_StatusCode
UA_Client_connect_async(UA_Client* client, const char* endpointUrl) {
		return UA_Client_connectAsync(client, endpointUrl);
}
UA_DEPRECATED static UA_INLINE UA_StatusCode
UA_Client_connect_noSession(UA_Client* client, const char* endpointUrl) {
	return UA_Client_connectSecureChannel(client, endpointUrl);
}
UA_DEPRECATED static UA_INLINE UA_StatusCode
UA_Client_connect_username(UA_Client* client, const char* endpointUrl,
	const char* username, const char* password) {
	return UA_Client_connectUsername(client, endpointUrl,
		username, password);
}
UA_DEPRECATED static UA_INLINE UA_StatusCode
UA_Client_disconnect_async(UA_Client* client, UA_UInt32* requestId) {
	return UA_Client_disconnect(client);
}
UA_DEPRECATED static UA_INLINE UA_StatusCode
UA_Client_close(UA_Client* client) {
	return UA_Client_disconnect(client);
}

10.4发现

/* Gets a list of endpoints of a server
* *
@param client to use. Must be connected to the same endpoint given in
* serverUrl or otherwise in disconnected state.
* @param serverUrl url to connect (for example "opc.tcp://localhost:4840")
* @param endpointDescriptionsSize size of the array of endpoint descriptions
* @param endpointDescriptions array of endpoint descriptions that is allocated
* by the function (you need to free manually)
* @return Indicates whether the operation succeeded or returns an error code */
UA_StatusCode
UA_Client_getEndpoints(UA_Client* client, const char* serverUrl,
	size_t* endpointDescriptionsSize,
	UA_EndpointDescription** endpointDescriptions);
/* Gets a list of all registered servers at the given server.
* *
You can pass an optional filter for serverUris. If the given server is not registered,
* an empty array will be returned. If the server is registered, only that application
* description will be returned.
* *
Additionally you can optionally indicate which locale you want for the server name
* in the returned application description. The array indicates the order of preference.
* A server may have localized names.
* *
@param client to use. Must be connected to the same endpoint given in
* serverUrl or otherwise in disconnected state.
* @param serverUrl url to connect (for example "opc.tcp://localhost:4840")
* @param serverUrisSize Optional filter for specific server uris
* @param serverUris Optional filter for specific server uris
* @param localeIdsSize Optional indication which locale you prefer
* @param localeIds Optional indication which locale you prefer
* @param registeredServersSize size of returned array, i.e., number of found/registered servers
* @param registeredServers array containing found/registered servers
* @return Indicates whether the operation succeeded or returns an error code */
UA_StatusCode
UA_Client_findServers(UA_Client* client, const char* serverUrl,
	size_t serverUrisSize, UA_String* serverUris,
	size_t localeIdsSize, UA_String* localeIds,
	size_t* registeredServersSize,
	UA_ApplicationDescription** registeredServers);
#ifdef UA_ENABLE_DISCOVERY
/* Get a list of all known server in the network. Only supported by LDS servers.
* *
@param client to use. Must be connected to the same endpoint given in
* serverUrl or otherwise in disconnected state.
* @param serverUrl url to connect (for example "opc.tcp://localhost:4840")
* @param startingRecordId optional. Only return the records with an ID higher
* or equal the given. Can be used for pagination to only get a subset of
* the full list
* @param maxRecordsToReturn optional. Only return this number of records
* @param serverCapabilityFilterSize optional. Filter the returned list to only
* get servers with given capabilities, e.g. "LDS"
* @param serverCapabilityFilter optional. Filter the returned list to only get
* servers with given capabilities, e.g. "LDS"
* @param serverOnNetworkSize size of returned array, i.e., number of
* known/registered servers
* @param serverOnNetwork array containing known/registered servers
* @return Indicates whether the operation succeeded or returns an error code */
UA_StatusCode
UA_Client_findServersOnNetwork(UA_Client* client, const char* serverUrl,
	UA_UInt32 startingRecordId, UA_UInt32 maxRecordsToReturn,
	size_t serverCapabilityFilterSize, UA_String* serverCapabilityFilte
	size_t* serverOnNetworkSize, UA_ServerOnNetwork** serverOnNetwork);
#endif

10.5服务

原始OPC UA服务向客户端公开。但大多数情况下,最好使用ua_client_highlevel.h中包装原始服务的便利函数。

/* Don’t use this function. Use the type versions below instead. */
void
__UA_Client_Service(UA_Client* client, const void* request,
	const UA_DataType* requestType, void* response,
	const UA_DataType* responseType);
/*
* Attribute Service Set
* ^^^^^^^^^^^^^^^^^^^^^ */
static UA_INLINE UA_ReadResponse
UA_Client_Service_read(UA_Client* client, const UA_ReadRequest request) {
	UA_ReadResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_READREQUEST],
		&response, &UA_TYPES[UA_TYPES_READRESPONSE]);
	return response;
}
static UA_INLINE UA_WriteResponse
UA_Client_Service_write(UA_Client* client, const UA_WriteRequest request) {
	UA_WriteResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_WRITEREQUEST],
		&response, &UA_TYPES[UA_TYPES_WRITERESPONSE]);
	166 Chapter 10. Clientopen62541 Documentation, Release 1.1.2
		return response;
}
/*
*Historical Access Service Set
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
#ifdef UA_ENABLE_HISTORIZING
static UA_INLINE UA_HistoryReadResponse
UA_Client_Service_historyRead(UA_Client * client, const UA_HistoryReadRequest request) {
	UA_HistoryReadResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_HISTORYREADREQUEST],
		&response, &UA_TYPES[UA_TYPES_HISTORYREADRESPONSE]);
	return response;
}
static UA_INLINE UA_HistoryUpdateResponse
UA_Client_Service_historyUpdate(UA_Client* client, const UA_HistoryUpdateRequest request) {
	UA_HistoryUpdateResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_HISTORYUPDATEREQUEST],
		&response, &UA_TYPES[UA_TYPES_HISTORYUPDATERESPONSE]);
	return response;
}
#endif
/*
* Method Service Set
* ^^^^^^^^^^^^^^^^^^ */
#ifdef UA_ENABLE_METHODCALLS
static UA_INLINE UA_CallResponse
UA_Client_Service_call(UA_Client * client, const UA_CallRequest request) {
	UA_CallResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CALLREQUEST],
		&response, &UA_TYPES[UA_TYPES_CALLRESPONSE]);
	return response;
}
#endif
/*
* NodeManagement Service Set
* ^^^^^^^^^^^^^^^^^^^^^^^^^^ */
static UA_INLINE UA_AddNodesResponse
UA_Client_Service_addNodes(UA_Client * client, const UA_AddNodesRequest request) {
	UA_AddNodesResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ADDNODESREQUEST],
		&response, &UA_TYPES[UA_TYPES_ADDNODESRESPONSE]);
	return response;
}
static UA_INLINE UA_AddReferencesResponse
UA_Client_Service_addReferences(UA_Client * client,
	const UA_AddReferencesRequest request) {
	UA_AddReferencesResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST],
		&response, &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE]);
	return response;
}
static UA_INLINE UA_DeleteNodesResponse
UA_Client_Service_deleteNodes(UA_Client * client,
	const UA_DeleteNodesRequest request) {
	UA_DeleteNodesResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETENODESREQUEST],
		&response, &UA_TYPES[UA_TYPES_DELETENODESRESPONSE]);
	10.5.Services 167open62541 Documentation, Release 1.1.2
		return response;
}
static UA_INLINE UA_DeleteReferencesResponse
UA_Client_Service_deleteReferences(UA_Client * client,
	const UA_DeleteReferencesRequest request) {
	UA_DeleteReferencesResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST],
		&response, &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE]);
	return response;
}
/**
View Service Set
* ^^^^^^^^^^^^^^^^*/
static UA_INLINE UA_BrowseResponse
UA_Client_Service_browse(UA_Client * client, const UA_BrowseRequest request) {
	UA_BrowseResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_BROWSEREQUEST],
		&response, &UA_TYPES[UA_TYPES_BROWSERESPONSE]);
	return response;
}
static UA_INLINE UA_BrowseNextResponse
UA_Client_Service_browseNext(UA_Client * client,
	const UA_BrowseNextRequest request) {
	UA_BrowseNextResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST],
		&response, &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE]);
	return response;
}
static UA_INLINE UA_TranslateBrowsePathsToNodeIdsResponse
UA_Client_Service_translateBrowsePathsToNodeIds(UA_Client * client,
	const UA_TranslateBrowsePathsToNodeIdsRequest request) {
	UA_TranslateBrowsePathsToNodeIdsResponse response;
	__UA_Client_Service(client, &request,
		&UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST],
		&response,
		&UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE]);
	return response;
}
static UA_INLINE UA_RegisterNodesResponse
UA_Client_Service_registerNodes(UA_Client * client,
	const UA_RegisterNodesRequest request) {
	UA_RegisterNodesResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST],
		&response, &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE]);
	return response;
}
static UA_INLINE UA_UnregisterNodesResponse
UA_Client_Service_unregisterNodes(UA_Client * client,
	const UA_UnregisterNodesRequest request) {
	UA_UnregisterNodesResponse response;
	__UA_Client_Service(client, &request,
		&UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST],
		&response, &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE]);
	return response;
}
/*
* Query Service Set
* ^^^^^^^^^^^^^^^^^*/
#ifdef UA_ENABLE_QUERY
static UA_INLINE UA_QueryFirstResponse
UA_Client_Service_queryFirst(UA_Client * client,
	const UA_QueryFirstRequest request) {
	UA_QueryFirstResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_QUERYFIRSTREQUEST],
		&response, &UA_TYPES[UA_TYPES_QUERYFIRSTRESPONSE]);
	return response;
}
static UA_INLINE UA_QueryNextResponse
UA_Client_Service_queryNext(UA_Client * client,
	const UA_QueryNextRequest request) {
	UA_QueryNextResponse response;
	__UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_QUERYFIRSTREQUEST],
		&response, &UA_TYPES[UA_TYPES_QUERYFIRSTRESPONSE]);
	return response;
}
#endif

10.6异步服务

所有OPC UA服务本质上都是异步的。因此,无需先等待响应即可拨打多个服务电话。回应的顺序可能不同。

/* Use the type versions of this method. See below. However, the general
* mechanism of async service calls is explained here.
* *
We say that an async service call has been dispatched once this method
* returns UA_STATUSCODE_GOOD. If there is an error after an async service has
* been dispatched, the callback is called with an "empty" response where the
* statusCode has been set accordingly. This is also done if the client is
* shutting down and the list of dispatched async services is emptied.
* *
The statusCode received when the client is shutting down is
* UA_STATUSCODE_BADSHUTDOWN.
* *
The statusCode received when the client don’t receive response
* after specified config->timeout (in ms) is
* UA_STATUSCODE_BADTIMEOUT.
* *
Instead, you can use __UA_Client_AsyncServiceEx to specify
* a custom timeout
* *
The userdata and requestId arguments can be NULL. */
typedef void (*UA_ClientAsyncServiceCallback)(UA_Client* client, void* userdata,
	UA_UInt32 requestId, void* response);
UA_StatusCode
__UA_Client_AsyncService(UA_Client* client, const void* request,
	const UA_DataType* requestType,
	UA_ClientAsyncServiceCallback callback,
	const UA_DataType* responseType,
	void* userdata, UA_UInt32* requestId);
UA_StatusCode
UA_Client_sendAsyncRequest(UA_Client * client, const void* request,
	const UA_DataType * requestType, UA_ClientAsyncServiceCallback callback,
	const UA_DataType * responseType, void* userdata, UA_UInt32 * requestId);
/* Listen on the network and process arriving asynchronous responses in the
* background. Internal housekeeping, renewal of SecureChannels and subscription
* management is done as well. */
UA_StatusCode
UA_Client_run_iterate(UA_Client* client, UA_UInt32 timeout);
UA_DEPRECATED static UA_INLINE UA_StatusCode
UA_Client_runAsync(UA_Client* client, UA_UInt32 timeout) {
	return UA_Client_run_iterate(client, timeout);
}
UA_DEPRECATED static UA_INLINE UA_StatusCode
UA_Client_manuallyRenewSecureChannel(UA_Client* client) {
	return UA_Client_run_iterate(client, 0);
}
/*Use the type versions of this method.See below.However, the general
* mechanism of async service calls is explained here.
**
We say that an async service call has been dispatched once this method
* returns UA_STATUSCODE_GOOD.If there is an error after an async service has
* been dispatched, the callback is called with an "empty" response where the
* statusCode has been set accordingly.This is also done if the client is
* shutting downand the list of dispatched async services is emptied.
**
The statusCode received when the client is shutting down is
* UA_STATUSCODE_BADSHUTDOWN.
**
The statusCode received when the client don’t receive response
* after specified timeout(in ms) is
* UA_STATUSCODE_BADTIMEOUT.
**
The timeout can be disabled by setting timeout to 0
* *
The userdata and requestId arguments can be NULL. */
UA_StatusCode
__UA_Client_AsyncServiceEx(UA_Client * client, const void* request,
	const UA_DataType * requestType,
	UA_ClientAsyncServiceCallback callback,
	const UA_DataType * responseType,
	void* userdata, UA_UInt32 * requestId,
	UA_UInt32 timeout);

10.7定时回调

可以将重复回调附加到客户端,并将在定义的时间间隔内执行。

typedef void (*UA_ClientCallback)(UA_Client* client, void* data);
/* Add a callback for execution at a specified time. If the indicated time lies
* in the past, then the callback is executed at the next iteration of the
* server’s main loop.
* *
@param client The client object.
* @param callback The callback that shall be added.
* @param data Data that is forwarded to the callback.
* @param date The timestamp for the execution time.
* @param callbackId Set to the identifier of the repeated callback . This can
* be used to cancel the callback later on. If the pointer is null, the
* identifier is not set.
* @return Upon success, UA_STATUSCODE_GOOD is returned. An error code
* otherwise. */
UA_StatusCode
UA_Client_addTimedCallback(UA_Client* client, UA_ClientCallback callback,
	void* data, UA_DateTime date, UA_UInt64* callbackId);
/* Add a callback for cyclic repetition to the client.
* *
@param client The client object.
* @param callback The callback that shall be added.
* @param data Data that is forwarded to the callback.
* @param interval_ms The callback shall be repeatedly executed with the given
* interval (in ms). The interval must be positive. The first execution
* occurs at now() + interval at the latest.
* @param callbackId Set to the identifier of the repeated callback . This can
* be used to cancel the callback later on. If the pointer is null, the
* identifier is not set.
* @return Upon success, UA_STATUSCODE_GOOD is returned. An error code
* otherwise. */
UA_StatusCode
UA_Client_addRepeatedCallback(UA_Client* client, UA_ClientCallback callback,
	void* data, UA_Double interval_ms,
	UA_UInt64* callbackId);
UA_StatusCode
UA_Client_changeRepeatedCallbackInterval(UA_Client* client,
	UA_UInt64 callbackId,
	UA_Double interval_ms);
void
UA_Client_removeCallback(UA_Client* client, UA_UInt64 callbackId);
UA_DEPRECATED static UA_INLINE void
UA_Client_removeRepeatedCallback(UA_Client* client, UA_UInt64 callbackId) {
	UA_Client_removeCallback(client, callbackId);
}

10.7.1高级客户端功能

以下定义是在后台使用标准OPC UA服务的便利功能。这是一种不太灵活的堆栈处理方式,因为在许多地方都假定有合理的默认值;同时,使用这些函数是实现OPC-UA应用程序的最简单方法,因为您不必考虑OPC-UA服务中的所有细节。如果需要更大的灵活性,则始终可以使用原始OPC UA服务实现相同的功能。

读取属性

以下函数可用于检索单个节点属性。使用常规服务一次读取多个属性。

/* Don’t call this function, use the typed versions */
UA_StatusCode
__UA_Client_readAttribute(UA_Client* client, const UA_NodeId* nodeId,
	UA_AttributeId attributeId, void* out,
	const UA_DataType* outDataType);
static UA_INLINE UA_StatusCode
UA_Client_readNodeIdAttribute(UA_Client * client, const UA_NodeId nodeId,
	UA_NodeId * outNodeId) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_NODEID,
		outNodeId, &UA_TYPES[UA_TYPES_NODEID]);
}
static UA_INLINE UA_StatusCode
UA_Client_readNodeClassAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_NodeClass* outNodeClass) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_NODECLASS,
		outNodeClass, &UA_TYPES[UA_TYPES_NODECLASS]);
}
static UA_INLINE UA_StatusCode
UA_Client_readBrowseNameAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_QualifiedName* outBrowseName) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_BROWSENAME,
		outBrowseName,
		&UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
}
static UA_INLINE UA_StatusCode
UA_Client_readDisplayNameAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_LocalizedText* outDisplayName) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
		outDisplayName,
		&UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
}
static UA_INLINE UA_StatusCode
UA_Client_readDescriptionAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_LocalizedText* outDescription) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_DESCRIPTION,
		outDescription,
		&UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
}
static UA_INLINE UA_StatusCode
UA_Client_readWriteMaskAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_UInt32* outWriteMask) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_WRITEMASK,
		outWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
}
static UA_INLINE UA_StatusCode
UA_Client_readUserWriteMaskAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_UInt32* outUserWriteMask) {
	return __UA_Client_readAttribute(client, &nodeId,
		UA_ATTRIBUTEID_USERWRITEMASK,
		outUserWriteMask,
		&UA_TYPES[UA_TYPES_UINT32]);
}
static UA_INLINE UA_StatusCode
UA_Client_readIsAbstractAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_Boolean* outIsAbstract) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_ISABSTRACT,
		outIsAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]);
}
static UA_INLINE UA_StatusCode
UA_Client_readSymmetricAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_Boolean * outSymmetric) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_SYMMETRIC,
		outSymmetric, &UA_TYPES[UA_TYPES_BOOLEAN]);
}
static UA_INLINE UA_StatusCode
UA_Client_readInverseNameAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_LocalizedText* outInverseName) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_INVERSENAME,
		outInverseName,
		&UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
}
static UA_INLINE UA_StatusCode
UA_Client_readContainsNoLoopsAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_Boolean* outContainsNoLoops) {
	return __UA_Client_readAttribute(client, &nodeId,
		UA_ATTRIBUTEID_CONTAINSNOLOOPS,
		outContainsNoLoops,
		&UA_TYPES[UA_TYPES_BOOLEAN]);
}
static UA_INLINE UA_StatusCode
UA_Client_readEventNotifierAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_Byte* outEventNotifier) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER,
		outEventNotifier, &UA_TYPES[UA_TYPES_BYTE]);
}
static UA_INLINE UA_StatusCode
UA_Client_readValueAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_Variant* outValue) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_VALUE,
		outValue, &UA_TYPES[UA_TYPES_VARIANT]);
}
static UA_INLINE UA_StatusCode
UA_Client_readDataTypeAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_NodeId* outDataType) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_DATATYPE,
		outDataType, &UA_TYPES[UA_TYPES_NODEID]);
}
static UA_INLINE UA_StatusCode
UA_Client_readValueRankAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_Int32* outValueRank) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_VALUERANK,
		outValueRank, &UA_TYPES[UA_TYPES_INT32]);
}
UA_StatusCode
UA_Client_readArrayDimensionsAttribute(UA_Client* client, const UA_NodeId nodeId,
	size_t* outArrayDimensionsSize,
	UA_UInt32** outArrayDimensions);
static UA_INLINE UA_StatusCode
UA_Client_readAccessLevelAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_Byte* outAccessLevel) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
		outAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
}
static UA_INLINE UA_StatusCode
UA_Client_readUserAccessLevelAttribute(UA_Client * client, const UA_NodeId nodeId,
	UA_Byte * outUserAccessLevel) {
	return __UA_Client_readAttribute(client, &nodeId,
		UA_ATTRIBUTEID_USERACCESSLEVEL,
		outUserAccessLevel,
		&UA_TYPES[UA_TYPES_BYTE]);
}
static UA_INLINE UA_StatusCode
UA_Client_readMinimumSamplingIntervalAttribute(UA_Client* client,
	const UA_NodeId nodeId,
	UA_Double* outMinSamplingInterval) {
	return __UA_Client_readAttribute(client, &nodeId,
		UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
		outMinSamplingInterval,
		&UA_TYPES[UA_TYPES_DOUBLE]);
}
static UA_INLINE UA_StatusCode
UA_Client_readHistorizingAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_Boolean* outHistorizing) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_HISTORIZING,
		outHistorizing, &UA_TYPES[UA_TYPES_BOOLEAN]);
}
static UA_INLINE UA_StatusCode
UA_Client_readExecutableAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_Boolean* outExecutable) {
	return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_EXECUTABLE,
		outExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
}
static UA_INLINE UA_StatusCode
UA_Client_readUserExecutableAttribute(UA_Client* client, const UA_NodeId nodeId,
	UA_Boolean* outUserExecutable) {
	return __UA_Client_readAttribute(client, &nodeId,
		UA_ATTRIBUTEID_USEREXECUTABLE,
		outUserExecutable,
		&UA_TYPES[UA_TYPES_BOOLEAN]);
}

历史访问

以下函数可用于历史读取单个节点。一次使用多个常规节点进行读取。

#ifdef UA_ENABLE_HISTORIZING
typedef UA_Boolean
(*UA_HistoricalIteratorCallback)(UA_Client* client,
	const UA_NodeId* nodeId,
	UA_Boolean moreDataAvailable,
	const UA_ExtensionObject* data, void* callbackContext);
#ifdef UA_ENABLE_EXPERIMENTAL_HISTORIZING
UA_StatusCode
UA_Client_HistoryRead_events(UA_Client* client, const UA_NodeId* nodeId,
	const UA_HistoricalIteratorCallback callback,
	UA_DateTime startTime, UA_DateTime endTime,
	UA_String indexRange, const UA_EventFilter filter, UA_UInt32 numVa
	UA_TimestampsToReturn timestampsToReturn, void* callbackContext);
#endif // UA_ENABLE_EXPERIMENTAL_HISTORIZING
UA_StatusCode
UA_Client_HistoryRead_raw(UA_Client * client, const UA_NodeId * nodeId,
	const UA_HistoricalIteratorCallback callback,
	UA_DateTime startTime, UA_DateTime endTime,
	UA_String indexRange, UA_Boolean returnBounds, UA_UInt32 numValuesPer
	UA_TimestampsToReturn timestampsToReturn, void* callbackContext);
#ifdef UA_ENABLE_EXPERIMENTAL_HISTORIZING
UA_StatusCode
UA_Client_HistoryRead_modified(UA_Client* client, const UA_NodeId* nodeId,
	const UA_HistoricalIteratorCallback callback,
	UA_DateTime startTime, UA_DateTime endTime,
	UA_String indexRange, UA_Boolean returnBounds, UA_UInt32 numValu
	UA_TimestampsToReturn timestampsToReturn, void* callbackContext)
#endif // UA_ENABLE_EXPERIMENTAL_HISTORIZING
	UA_StatusCode
	UA_Client_HistoryUpdate_insert(UA_Client* client,
		const UA_NodeId* nodeId,
		UA_DataValue* value);
UA_StatusCode
UA_Client_HistoryUpdate_replace(UA_Client* client,
	const UA_NodeId* nodeId,
	UA_DataValue* value);
UA_StatusCode
UA_Client_HistoryUpdate_update(UA_Client* client,
	const UA_NodeId* nodeId,
	UA_DataValue* value);
UA_StatusCode
UA_Client_HistoryUpdate_deleteRaw(UA_Client* client,
	const UA_NodeId* nodeId,
	UA_DateTime startTimestamp,
	UA_DateTime endTimestamp);
#endif // UA_ENABLE_HISTORIZING

写入属性
以下函数可用于一次写入一个节点属性。使用常规写入服务一次写入多个属性。

/* Don’t call this function, use the typed versions */
UA_StatusCode
__UA_Client_writeAttribute(UA_Client* client, const UA_NodeId* nodeId,
	UA_AttributeId attributeId, const void* in,
	const UA_DataType* inDataType);
static UA_INLINE UA_StatusCode
UA_Client_writeNodeIdAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_NodeId* newNodeId) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_NODEID,
		newNodeId, &UA_TYPES[UA_TYPES_NODEID]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeNodeClassAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_NodeClass* newNodeClass) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_NODECLASS,
		newNodeClass, &UA_TYPES[UA_TYPES_NODECLASS]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeBrowseNameAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_QualifiedName* newBrowseName) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_BROWSENAME,
		newBrowseName,
		&UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeDisplayNameAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_LocalizedText* newDisplayName) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
		newDisplayName,
		&UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeDescriptionAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_LocalizedText* newDescription) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_DESCRIPTION,
		newDescription,
		&UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeWriteMaskAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_UInt32* newWriteMask) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_WRITEMASK,
		newWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeUserWriteMaskAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_UInt32* newUserWriteMask) {
	return __UA_Client_writeAttribute(client, &nodeId,
		UA_ATTRIBUTEID_USERWRITEMASK,
		newUserWriteMask,
		&UA_TYPES[UA_TYPES_UINT32]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeIsAbstractAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_Boolean* newIsAbstract) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_ISABSTRACT,
		newIsAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeSymmetricAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_Boolean* newSymmetric) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_SYMMETRIC,
		newSymmetric, &UA_TYPES[UA_TYPES_BOOLEAN]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeInverseNameAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_LocalizedText* newInverseName) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_INVERSENAME,
		newInverseName,
		&UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeContainsNoLoopsAttribute(UA_Client * client, const UA_NodeId nodeId,
	const UA_Boolean * newContainsNoLoops) {
	return __UA_Client_writeAttribute(client, &nodeId,
		UA_ATTRIBUTEID_CONTAINSNOLOOPS,
		newContainsNoLoops,
		&UA_TYPES[UA_TYPES_BOOLEAN]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeEventNotifierAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_Byte* newEventNotifier) {
	return __UA_Client_writeAttribute(client, &nodeId,
		UA_ATTRIBUTEID_EVENTNOTIFIER,
		newEventNotifier,
		&UA_TYPES[UA_TYPES_BYTE]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeValueAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_Variant* newValue) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_VALUE,
		newValue, &UA_TYPES[UA_TYPES_VARIANT]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeDataTypeAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_NodeId* newDataType) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_DATATYPE,
		newDataType, &UA_TYPES[UA_TYPES_NODEID]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeValueRankAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_Int32* newValueRank) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_VALUERANK,
		newValueRank, &UA_TYPES[UA_TYPES_INT32]);
}
UA_StatusCode
UA_Client_writeArrayDimensionsAttribute(UA_Client* client, const UA_NodeId nodeId,
	size_t newArrayDimensionsSize,
	const UA_UInt32* newArrayDimensions);
static UA_INLINE UA_StatusCode
UA_Client_writeAccessLevelAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_Byte* newAccessLevel) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
		newAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeUserAccessLevelAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_Byte* newUserAccessLevel) {
	return __UA_Client_writeAttribute(client, &nodeId,
		UA_ATTRIBUTEID_USERACCESSLEVEL,
		newUserAccessLevel,
		&UA_TYPES[UA_TYPES_BYTE]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeMinimumSamplingIntervalAttribute(UA_Client* client,
	const UA_NodeId nodeId,
	const UA_Double* newMinInterval) {
	return __UA_Client_writeAttribute(client, &nodeId,
		UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
		newMinInterval, &UA_TYPES[UA_TYPES_DOUBLE]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeHistorizingAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_Boolean* newHistorizing) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_HISTORIZING,
		newHistorizing, &UA_TYPES[UA_TYPES_BOOLEAN]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeExecutableAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_Boolean* newExecutable) {
	return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_EXECUTABLE,
		newExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
}
static UA_INLINE UA_StatusCode
UA_Client_writeUserExecutableAttribute(UA_Client* client, const UA_NodeId nodeId,
	const UA_Boolean* newUserExecutable) {
	return __UA_Client_writeAttribute(client, &nodeId,
		UA_ATTRIBUTEID_USEREXECUTABLE,
		newUserExecutable,
		&UA_TYPES[UA_TYPES_BOOLEAN]);
}

方法调用

#ifdef UA_ENABLE_METHODCALLS
UA_StatusCode
UA_Client_call(UA_Client *client, const UA_NodeId objectId,
		const UA_NodeId methodId, size_t inputSize, const UA_Variant *input,
		size_t *outputSize, UA_Variant **output);
#endif

节点管理

请参阅服务器端节点管理部分。

UA_StatusCode
UA_Client_addReference(UA_Client* client, const UA_NodeId sourceNodeId,
	const UA_NodeId referenceTypeId, UA_Boolean isForward,
	const UA_String targetServerUri,
	const UA_ExpandedNodeId targetNodeId,
	UA_NodeClass targetNodeClass);
UA_StatusCode
UA_Client_deleteReference(UA_Client* client, const UA_NodeId sourceNodeId,
	const UA_NodeId referenceTypeId, UA_Boolean isForward,
	const UA_ExpandedNodeId targetNodeId,
	UA_Boolean deleteBidirectional);
UA_StatusCode
UA_Client_deleteNode(UA_Client* client, const UA_NodeId nodeId,
	UA_Boolean deleteTargetReferences);
/* Protect against redundant definitions for server/client */
#ifndef UA_DEFAULT_ATTRIBUTES_DEFINED
#define UA_DEFAULT_ATTRIBUTES_DEFINED
/* The default for variables is "BaseDataType" for the datatype, -2 for the
* valuerank and a read-accesslevel. */
extern const UA_VariableAttributes UA_VariableAttributes_default;
extern const UA_VariableTypeAttributes UA_VariableTypeAttributes_default;
/* Methods are executable by default */
extern const UA_MethodAttributes UA_MethodAttributes_default;
/* The remaining attribute definitions are currently all zeroed out */
extern const UA_ObjectAttributes UA_ObjectAttributes_default;
extern const UA_ObjectTypeAttributes UA_ObjectTypeAttributes_default;
extern const UA_ReferenceTypeAttributes UA_ReferenceTypeAttributes_default;
extern const UA_DataTypeAttributes UA_DataTypeAttributes_default;
extern const UA_ViewAttributes UA_ViewAttributes_default;
#endif
/* Don’t call this function, use the typed versions */
UA_StatusCode
__UA_Client_addNode(UA_Client* client, const UA_NodeClass nodeClass,
	const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_NodeId typeDefinition, const UA_NodeAttributes* attr,
	const UA_DataType* attributeType, UA_NodeId* outNewNodeId);
static UA_INLINE UA_StatusCode
UA_Client_addVariableNode(UA_Client* client, const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_NodeId typeDefinition,
	const UA_VariableAttributes attr,
	UA_NodeId* outNewNodeId) {
	return __UA_Client_addNode(client, UA_NODECLASS_VARIABLE, requestedNewNodeId,
		parentNodeId, referenceTypeId, browseName,
		typeDefinition, (const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],
		outNewNodeId);
}
static UA_INLINE UA_StatusCode
UA_Client_addVariableTypeNode(UA_Client* client,
	const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_VariableTypeAttributes attr,
	UA_NodeId* outNewNodeId) {
	return __UA_Client_addNode(client, UA_NODECLASS_VARIABLETYPE,
		requestedNewNodeId,
		parentNodeId, referenceTypeId, browseName,
		UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],
		outNewNodeId);
}
static UA_INLINE UA_StatusCode
UA_Client_addObjectNode(UA_Client* client, const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_NodeId typeDefinition,
	const UA_ObjectAttributes attr, UA_NodeId* outNewNodeId) {
	return __UA_Client_addNode(client, UA_NODECLASS_OBJECT, requestedNewNodeId,
		parentNodeId, referenceTypeId, browseName,
		typeDefinition, (const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], outNewNodeId);
}
static UA_INLINE UA_StatusCode
UA_Client_addObjectTypeNode(UA_Client* client, const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_ObjectTypeAttributes attr,
	UA_NodeId* outNewNodeId) {
	return __UA_Client_addNode(client, UA_NODECLASS_OBJECTTYPE, requestedNewNodeId,
		parentNodeId, referenceTypeId, browseName,
		UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],
		outNewNodeId);
}
static UA_INLINE UA_StatusCode
UA_Client_addViewNode(UA_Client* client, const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_ViewAttributes attr,
	UA_NodeId* outNewNodeId) {
	return __UA_Client_addNode(client, UA_NODECLASS_VIEW, requestedNewNodeId,
		parentNodeId, referenceTypeId, browseName,
		UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_VIEWATTRIBUTES], outNewNodeId);
}
static UA_INLINE UA_StatusCode
UA_Client_addReferenceTypeNode(UA_Client* client,
	const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_ReferenceTypeAttributes attr,
	UA_NodeId* outNewNodeId) {
	return __UA_Client_addNode(client, UA_NODECLASS_REFERENCETYPE,
		requestedNewNodeId,
		parentNodeId, referenceTypeId, browseName,
		UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],
		outNewNodeId);
}
static UA_INLINE UA_StatusCode
UA_Client_addDataTypeNode(UA_Client* client, const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_DataTypeAttributes attr,
	UA_NodeId* outNewNodeId) {
	return __UA_Client_addNode(client, UA_NODECLASS_DATATYPE, requestedNewNodeId,
		parentNodeId, referenceTypeId, browseName,
		UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],
		outNewNodeId);
}
static UA_INLINE UA_StatusCode
UA_Client_addMethodNode(UA_Client* client, const UA_NodeId requestedNewNodeId,
	const UA_NodeId parentNodeId,
	const UA_NodeId referenceTypeId,
	const UA_QualifiedName browseName,
	const UA_MethodAttributes attr,
	UA_NodeId* outNewNodeId) {
	return __UA_Client_addNode(client, UA_NODECLASS_METHOD, requestedNewNodeId,
		parentNodeId, referenceTypeId, browseName,
		UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
		&UA_TYPES[UA_TYPES_METHODATTRIBUTES], outNewNodeId);
}

其他高级功能

/* Get the namespace-index of a namespace-URI
* *
@param client The UA_Client struct for this connection
* @param namespaceUri The interested namespace URI
* @param namespaceIndex The namespace index of the URI. The value is unchanged
* in case of an error
* @return Indicates whether the operation succeeded or returns an error code */
UA_StatusCode
UA_Client_NamespaceGetIndex(UA_Client* client, UA_String* namespaceUri,
	UA_UInt16* namespaceIndex);
#ifndef HAVE_NODEITER_CALLBACK
#define HAVE_NODEITER_CALLBACK
/* Iterate over all nodes referenced by parentNodeId by calling the callback
function for each child node */
typedef UA_StatusCode(*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean isInverse,
	UA_NodeId referenceTypeId, void* handle);
#endif
UA_StatusCode
UA_Client_forEachChildNodeCall(UA_Client* client, UA_NodeId parentNodeId,
	UA_NodeIteratorCallback callback, void* handle);

10.7.2订阅

OPC UA中订阅是异步的。也就是说,客户机向服务器发送多个发布请求。服务器返回带有通知的PublishResponses。但只有在生成通知时。客户端不等待响应并继续正常操作。
请注意订阅和监视数据项之间的区别。订阅用于返回报表。监视数据项用于生成通知。每个监视数据项只附加到一个订阅。订阅可以包含许多监视数据项。
客户端在后台自动处理PublishResponses(使用回调),并在传输过程中保留足够的PublishResponses。发布的响应可以在同步服务调用期间或在UA_Client_runAsync中接收。

/* Callbacks defined for Subscriptions */
typedef void (*UA_Client_DeleteSubscriptionCallback)
(UA_Client* client, UA_UInt32 subId, void* subContext);
typedef void (*UA_Client_StatusChangeNotificationCallback)
(UA_Client* client, UA_UInt32 subId, void* subContext,
	10.7.Timed Callbacks 181open62541 Documentation, Release 1.1.2
	UA_StatusChangeNotification * notification);
/* Provides default values for a new subscription.
* *
RequestedPublishingInterval: 500.0 [ms]
* RequestedLifetimeCount: 10000
* RequestedMaxKeepAliveCount: 10
* MaxNotificationsPerPublish: 0 (unlimited)
* PublishingEnabled: true
* Priority: 0 */
static UA_INLINE UA_CreateSubscriptionRequest
UA_CreateSubscriptionRequest_default(void) {
	UA_CreateSubscriptionRequest request;
	UA_CreateSubscriptionRequest_init(&request);
	request.requestedPublishingInterval = 500.0;
	request.requestedLifetimeCount = 10000;
	request.requestedMaxKeepAliveCount = 10;
	request.maxNotificationsPerPublish = 0;
	request.publishingEnabled = true;
	request.priority = 0;
	return request;
}
UA_CreateSubscriptionResponse
UA_Client_Subscriptions_create(UA_Client* client,
	const UA_CreateSubscriptionRequest request,
	void* subscriptionContext,
	UA_Client_StatusChangeNotificationCallback statusChangeCallback,
	UA_Client_DeleteSubscriptionCallback deleteCallback);
UA_StatusCode
UA_Client_Subscriptions_create_async(UA_Client* client,
	const UA_CreateSubscriptionRequest request,
	void* subscriptionContext,
	UA_Client_StatusChangeNotificationCallback statusChangeCallback,
	UA_Client_DeleteSubscriptionCallback deleteCallback,
	UA_ClientAsyncServiceCallback callback,
	void* userdata, UA_UInt32* requestId);
UA_ModifySubscriptionResponse
UA_Client_Subscriptions_modify(UA_Client* client,
	const UA_ModifySubscriptionRequest request);
UA_StatusCode
UA_Client_Subscriptions_modify_async(UA_Client* client,
	const UA_ModifySubscriptionRequest request,
	UA_ClientAsyncServiceCallback callback,
	void* userdata, UA_UInt32* requestId);
UA_DeleteSubscriptionsResponse
UA_Client_Subscriptions_delete(UA_Client* client,
	const UA_DeleteSubscriptionsRequest request);
UA_StatusCode
UA_Client_Subscriptions_delete_async(UA_Client* client,
	const UA_DeleteSubscriptionsRequest request,
	UA_ClientAsyncServiceCallback callback,
	void* userdata, UA_UInt32* requestId);
/* Delete a single subscription */
UA_StatusCode
UA_Client_Subscriptions_deleteSingle(UA_Client* client, UA_UInt32 subscriptionId);
static UA_INLINE UA_SetPublishingModeResponse
UA_Client_Subscriptions_setPublishingMode(UA_Client * client,
	const UA_SetPublishingModeRequest request) {
	UA_SetPublishingModeResponse response;
	__UA_Client_Service(client,
		&request, &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST],
		&response, &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE]);
	return response;
}

10.7.3 监视数据项

事件的监视数据项(MonitoredItems)指示EventNotifier属性。这表示服务器不监视属性的更改,而是从该节点转发事件通知。
在创建MonitoredItem期间,服务器可能会返回已更改的调整参数。检查返回的UA_CreateMonitoredItemsResponse 以获取当前参数。

/* Provides default values for a new monitored item. */
static UA_INLINE UA_MonitoredItemCreateRequest
UA_MonitoredItemCreateRequest_default(UA_NodeId nodeId) {
	UA_MonitoredItemCreateRequest request;
	UA_MonitoredItemCreateRequest_init(&request);
	request.itemToMonitor.nodeId = nodeId;
	request.itemToMonitor.attributeId = UA_ATTRIBUTEID_VALUE;
	request.monitoringMode = UA_MONITORINGMODE_REPORTING;
	request.requestedParameters.samplingInterval = 250;
	request.requestedParameters.discardOldest = true;
	request.requestedParameters.queueSize = 1;
	return request;
}

用户无法设置clientHandle参数,在向服务器发送请求之前,任何值都将被客户端替换。

/* Callback for the deletion of a MonitoredItem */
typedef void (*UA_Client_DeleteMonitoredItemCallback)
(UA_Client* client, UA_UInt32 subId, void* subContext,
	UA_UInt32 monId, void* monContext);
/* Callback for DataChange notifications */
typedef void (*UA_Client_DataChangeNotificationCallback)
(UA_Client* client, UA_UInt32 subId, void* subContext,
	UA_UInt32 monId, void* monContext,
	UA_DataValue* value);
/* Callback for Event notifications */
typedef void (*UA_Client_EventNotificationCallback)
(UA_Client* client, UA_UInt32 subId, void* subContext,
	UA_UInt32 monId, void* monContext,
	size_t nEventFields, UA_Variant* eventFields);
/* Don’t use to monitor the EventNotifier attribute */
UA_CreateMonitoredItemsResponse
UA_Client_MonitoredItems_createDataChanges(UA_Client* client,
	const UA_CreateMonitoredItemsRequest request, void** contexts,
	UA_Client_DataChangeNotificationCallback* callbacks,
	UA_Client_DeleteMonitoredItemCallback* deleteCallbacks);
UA_StatusCode
UA_Client_MonitoredItems_createDataChanges_async(UA_Client* client,
	const UA_CreateMonitoredItemsRequest request, void** contexts,
	UA_Client_DataChangeNotificationCallback* callbacks,
	UA_Client_DeleteMonitoredItemCallback* deleteCallbacks,
	UA_ClientAsyncServiceCallback createCallback,
	void* userdata, UA_UInt32* requestId);
UA_MonitoredItemCreateResult
UA_Client_MonitoredItems_createDataChange(UA_Client* client,
	UA_UInt32 subscriptionId,
	UA_TimestampsToReturn timestampsToReturn,
	const UA_MonitoredItemCreateRequest item,
	void* context, UA_Client_DataChangeNotificationCallback callback,
	UA_Client_DeleteMonitoredItemCallback deleteCallback);
/* Monitor the EventNotifier attribute only */
UA_CreateMonitoredItemsResponse
UA_Client_MonitoredItems_createEvents(UA_Client* client,
	const UA_CreateMonitoredItemsRequest request, void** contexts,
	UA_Client_EventNotificationCallback* callback,
	UA_Client_DeleteMonitoredItemCallback* deleteCallback);
/* Monitor the EventNotifier attribute only */
UA_StatusCode
UA_Client_MonitoredItems_createEvents_async(UA_Client* client,
	const UA_CreateMonitoredItemsRequest request, void** contexts,
	UA_Client_EventNotificationCallback* callbacks,
	UA_Client_DeleteMonitoredItemCallback* deleteCallbacks,
	UA_ClientAsyncServiceCallback createCallback,
	void* userdata, UA_UInt32* requestId);
UA_MonitoredItemCreateResult
UA_Client_MonitoredItems_createEvent(UA_Client* client,
	UA_UInt32 subscriptionId,
	UA_TimestampsToReturn timestampsToReturn,
	const UA_MonitoredItemCreateRequest item,
	void* context, UA_Client_EventNotificationCallback callback,
	UA_Client_DeleteMonitoredItemCallback deleteCallback);
UA_DeleteMonitoredItemsResponse
UA_Client_MonitoredItems_delete(UA_Client* client,
	const UA_DeleteMonitoredItemsRequest);
UA_StatusCode
UA_Client_MonitoredItems_delete_async(UA_Client* client,
	const UA_DeleteMonitoredItemsRequest request,
	UA_ClientAsyncServiceCallback callback,
	void* userdata, UA_UInt32* requestId);
UA_StatusCode
UA_Client_MonitoredItems_deleteSingle(UA_Client* client,
	UA_UInt32 subscriptionId, UA_UInt32 monitoredItemId);
/* The clientHandle parameter will be filled automatically */
UA_ModifyMonitoredItemsResponse
UA_Client_MonitoredItems_modify(UA_Client* client,
	const UA_ModifyMonitoredItemsRequest request);

以下服务调用直接转到服务器。MonitoredItem设置未存储在客户端中。

static UA_INLINE UA_SetMonitoringModeResponse
UA_Client_MonitoredItems_setMonitoringMode(UA_Client* client,
	const UA_SetMonitoringModeRequest request) {
	UA_SetMonitoringModeResponse response;
	184 Chapter 10. Clientopen62541 Documentation, Release 1.1.2
		__UA_Client_Service(client,
			&request, &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST],
			&response, &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE]);
	return response;
}
static UA_INLINE UA_SetTriggeringResponse
UA_Client_MonitoredItems_setTriggering(UA_Client* client,
	const UA_SetTriggeringRequest request) {
	UA_SetTriggeringResponse response;
	__UA_Client_Service(client,
		&request, &UA_TYPES[UA_TYPES_SETTRIGGERINGREQUEST],
		&response, &UA_TYPES[UA_TYPES_SETTRIGGERINGRESPONSE]);
	return response;
}
static UA_INLINE UA_StatusCode
UA_Client_MonitoredItems_modify_async(UA_Client* client,
	const UA_ModifyMonitoredItemsRequest request,
	UA_ClientAsyncServiceCallback callback,
	void* userdata, UA_UInt32* requestId) {
	return __UA_Client_AsyncService(client, &request,
		&UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST], callback,
		&UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE],
		userdata, requestId);
}
static UA_INLINE UA_StatusCode
UA_Client_MonitoredItems_setMonitoringMode_async(UA_Client* client,
	const UA_SetMonitoringModeRequest request,
	UA_ClientAsyncServiceCallback callback,
	void* userdata, UA_UInt32* requestId) {
	return __UA_Client_AsyncService(client, &request,
		&UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST], callback,
		&UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE],
		userdata, requestId);
}
static UA_INLINE UA_StatusCode
UA_Client_MonitoredItems_setTriggering_async(UA_Client* client,
	const UA_SetTriggeringRequest request,
	UA_ClientAsyncServiceCallback callback,
	void* userdata, UA_UInt32* requestId) {
	return __UA_Client_AsyncService(client, &request,
		&UA_TYPES[UA_TYPES_SETTRIGGERINGREQUEST], callback,
		&UA_TYPES[UA_TYPES_SETTRIGGERINGRESPONSE],
		userdata, requestId);
}
#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值