gpt4 book ai didi

sockets - Opc-UA 通信协议(protocol),客户端如何理解可用的服务器节点?

转载 作者:行者123 更新时间:2023-12-03 12:03:38 25 4
gpt4 key购买 nike

当我开始使用 opc ua 时,我只是想知道在 opc ua 通信层的底层发生了什么。

让我们举一个非常简单的服务器实现的例子,它在地址空间中有 3 个节点。这些节点提供可由 opc-UA 客户端写入和读取的数据。

通过阅读 open62541 附带的部分代码,我了解到通信是通过 TCP 发生的。这意味着服务器启动一个客户端可以连接的套接字并使客户端能够
在节点上执行各种操作。

我的问题是,客户端如何知道可用的服务器节点?我知道它浏览地址空间,但它究竟在哪里浏览以查找可用节点?
opc-UA 使用什么暴露机制向客户端呈现可用节点?服务器是否在某个 xml 文件或其他任何地方写入可用信息和节点,因此当客户端连接时,它会尝试读取文件的内容以了解 addressSpace 结构?

open62541 的示例服务器实现

#include <stdio.h>
#include <open62541.h>
#include <signal.h>

static void
addVariable(UA_Server *server) {
/* Define the attribute of the myInteger variable node */
UA_VariableAttributes attr = UA_VariableAttributes_default;
UA_Int32 myInteger = 43;
UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
attr.description = UA_LOCALIZEDTEXT("en-US", "the answer");
attr.displayName = UA_LOCALIZEDTEXT("en-US", "the answer");
attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;

/* Add the variable node to the information model */
UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
parentReferenceNodeId, myIntegerName,
UA_NODEID_NULL, attr, NULL, NULL);
}

static void
addThirdVariable(UA_Server *server) {
/* Define the attribute of the myInteger variable node */
UA_VariableAttributes attr = UA_VariableAttributes_default;
UA_String myInteger = UA_STRING("My name is variable 3"); // variable name
UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_STRING]);
attr.description = UA_LOCALIZEDTEXT("en-US", "the answer");
attr.displayName = UA_LOCALIZEDTEXT("en-US", "the answer");
attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;

/* Add the variable node to the information model */
UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "third.variable");
UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "third varaible");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
parentReferenceNodeId, myIntegerName,
UA_NODEID_NULL, attr, NULL, NULL);
}

void addSecondVariable(UA_Server * server) {
//variable attributes
UA_VariableAttributes attr = UA_VariableAttributes_default;
UA_String machine_name = UA_STRING("My name is a machine"); // variable name
UA_Variant_setScalar(&attr.value, &machine_name, &UA_TYPES[UA_TYPES_STRING]);

attr.description = UA_LOCALIZEDTEXT("en-US", "machine name");
attr.displayName = UA_LOCALIZEDTEXT("en-US", "machine name");
attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
//setting access level not important

//add the variable to the information model
UA_NodeId myStringNodeID = UA_NODEID_STRING(1, "the.machine");
UA_QualifiedName myStringName = UA_QUALIFIEDNAME(1, "the machine");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);

UA_Server_addVariableNode(server, myStringNodeID, parentNodeId,
parentReferenceNodeId, myStringName,
UA_NODEID_NULL, attr, NULL, NULL);



}

UA_Boolean running = true;
static void stopHandler(int sign) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
running = false;
}

int main(void) {
signal(SIGINT, stopHandler);
signal(SIGTERM, stopHandler);

UA_ServerConfig *config = UA_ServerConfig_new_default();
UA_Server *server = UA_Server_new(config);
addVariable(server);
addSecondVariable(server);
addThirdVariable(server);
UA_StatusCode retval = UA_Server_run(server, &running);


UA_Server_delete(server);
UA_ServerConfig_delete(config);
return (int)retval;
}

最佳答案

有几种方法可以发现节点。

首先,您应该知道 AddressSpace 不是树,而是图。图的节点是 OPC UA Node s,图的边是 OPC UA Reference s。

一个Node的名字是 NodeId这是一个限定名称。名称可以是整数 (i=)、字符串 (s=) 或不透明对象 (o=)。限定符指定 Namespace在服务器的命名空间表中。

关于命名空间,有两个保留的命名空间索引:

  • 0 ,它指定 OPC UA 命名空间
  • 1 ,它指定了服务器本身(恕我直言,每台服务器应该有一个不同的)

  • OPC UA 基金会是 OPC UA 命名空间的主人,并在 0 中定义了一大堆标准节点。命名空间。特别是节点 Server , ObjectsTypes0 中定义命名空间,具有众所周知的整数名称。我不会谈论 AttributeNode , 但考虑到 Node 的概念, Reference , NamespaceAttribute ,OPC UA 标准通过 Bootstrap 自行拉动。 0 namespace 定义了服务器节点的基本结构,所有定义的节点都有众所周知的 NodeId s。我说“ Bootstrap ”,因为特别是在 Server 下节点位于 NamespaceTable它将命名空间索引与相应的命名空间 URN 相关联。 (包括标准化索引 01 )表格的元素可以是 Read就像任何其他节点一样。

    要开始回答您的问题,简而言之,访问服务器中特定节点的最直接方法是了解其 NodeId。 .

    现在,如果您没有列表,您如何知道服务器上存在哪些节点?好吧,该操作称为 Browsing ,并且有两种风格:跟随 Reference s,或通过 BrowsePath 传送s。

    关于以下引用,记得我说过 AddressSpace是一个图表。好吧,一个 Node指向其他 Node直通 Reference s。给定一个特定的 NodeId (例如众所周知的 NodeId ,或者根,在命名空间 NodeId 中也有众所周知的 0 ),您可以查询 Node 的引用这将指定其他 Node s,然后从 Node 跟随您感兴趣的线索至 Node , 直到你找到你要找的东西。这意味着客户端和服务器之间的大量交换,老实说很少值得麻烦。

    关于通过 BrowsePath传送s,有一个服务叫 TranslateBrowsePath由给定开始的服务器实现 NodeId和浏览路径,服务器会为您提供与查询匹配的节点列表。 (即从指定起点 Node通过与浏览路径匹配的 Reference的路径可达) 浏览路径语言非常丰富,你可以用它进行相当复杂的查询。

    关于sockets - Opc-UA 通信协议(protocol),客户端如何理解可用的服务器节点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47889301/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com