概述
本文主要讲述如何去获取服务器中方法节点中的输入输出参数的个数和类型。
服务端
这里我们使用uaexpert软件中自带的示例服务器UaCPPServer,我们将其运行起来如下所示。
我们使用uaexpert去连接它找到定义的方法节点那里,
这里我们以Multiply这个方法节点为例。
读取节点信息
这里代码相对简单,如下
/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
* See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
#include "open62541.h"
UA_StatusCode printMethodParameter(UA_Client *client, UA_NodeId nodeId)
{
UA_StatusCode statuscode = UA_STATUSCODE_GOOD;
UA_NodeClass nodeClass;
statuscode = UA_Client_readNodeClassAttribute(client, nodeId, &nodeClass);
if (statuscode != UA_STATUSCODE_GOOD)
return statuscode;
if (nodeClass != UA_NODECLASS_METHOD){ // 不是方法节点
printf("Not method node\n");
return UA_STATUSCODE_BAD;
}
UA_NodeId inputNode, outputNode;
// 浏览输入的节点下的节点
printf("Browsing nodes in objects folder:\n");
UA_BrowseRequest bReq;
UA_BrowseRequest_init(&bReq);
bReq.requestedMaxReferencesPerNode = 0;//限制查到的最大节点数,0 不限制
bReq.nodesToBrowse = UA_BrowseDescription_new();
bReq.nodesToBrowseSize = 1;
bReq.nodesToBrowse[0].browseDirection = UA_BROWSEDIRECTION_FORWARD;
bReq.nodesToBrowse[0].includeSubtypes = UA_TRUE;//是否包含subtypes
bReq.nodesToBrowse[0].nodeId = nodeId;//设置起始浏览节点
bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL;
UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
// 浏览方法节点下的输入输出node
for (size_t i = 0; i < bResp.resultsSize; ++i)
{
for (size_t j = 0; j < bResp.results[i].referencesSize; ++j)
{
UA_ReferenceDescription* ref = &(bResp.results[i].references[j]);
UA_QualifiedName nodeName;
UA_Client_readBrowseNameAttribute(client, ref->nodeId.nodeId, &nodeName);
if (!strncmp("InputArguments", (char*)nodeName.name.data, nodeName.name.length)) { // 输入参数node
inputNode = ref->nodeId.nodeId;
}
else { // 输出参数node
outputNode = ref->nodeId.nodeId;
}
/* TODO: distinguish further types */
}
}
UA_BrowseRequest_clear(&bReq);
UA_BrowseResponse_clear(&bResp);
UA_Variant variant;
UA_Variant_init(&variant);
// 获取输入参数类型
printf("InputArguments : ");
statuscode = UA_Client_readValueAttribute(client, inputNode, &variant);
if (statuscode == UA_STATUSCODE_GOOD)
{
if (UA_Variant_hasArrayType(&variant, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]))
{
UA_ExtensionObject* object = (UA_ExtensionObject*)variant.data;
for (size_t ii = 0; ii < variant.arrayLength; ii++)
{
UA_Argument* argument = (UA_Argument * )object[ii].content.decoded.data;
UA_NodeId intNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_INT32);
UA_NodeId doubleNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_DOUBLE);
if (UA_NodeId_equal(&argument->dataType, &intNodeId))
{
printf(" %d:Int32", ii);
}
else if(UA_NodeId_equal(&argument->dataType, &doubleNodeId))
{
printf(" %d:Double", ii);
}
// .....其他类型判断
}
}
}
else {
return statuscode;
}
printf("\n");
// 获取输出参数类型
printf("OutputArguments : ");
statuscode = UA_Client_readValueAttribute(client, outputNode, &variant);
if (statuscode == UA_STATUSCODE_GOOD)
{
if (UA_Variant_hasArrayType(&variant, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]))
{
UA_ExtensionObject* object = (UA_ExtensionObject*)variant.data;
for (size_t ii = 0; ii < variant.arrayLength; ii++)
{
UA_Argument* argument = (UA_Argument*)object[ii].content.decoded.data;
UA_NodeId intNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_INT32);
UA_NodeId doubleNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_DOUBLE);
if (UA_NodeId_equal(&argument->dataType, &intNodeId))
{
printf(" %d:Int32", ii);
}
else if (UA_NodeId_equal(&argument->dataType, &doubleNodeId))
{
printf(" %d:double", ii);
}
// .....其他类型判断
}
}
}
else {
return statuscode;
}
printf("\n");
}
int main(int argc, char *argv[]) {
UA_Client *client = UA_Client_new();
UA_ClientConfig_setDefault(UA_Client_getConfig(client));
UA_StatusCode retval;
/* Connect to a server */
retval = UA_Client_connect(client, "opc.tcp://localhost:48010");
if(retval != UA_STATUSCODE_GOOD) {
UA_Client_delete(client);
return EXIT_FAILURE;
}
printMethodParameter(client, UA_NODEID_STRING_ALLOC(2, "Demo.Method.Multiply"));
UA_Client_disconnect(client);
UA_Client_delete(client);
system("pause");
return EXIT_SUCCESS;
}
主要是通过浏览方法节点下的InputArguments和OuputArguments节点去获取对应的输入输出参数信息。运行改代码结果如下
这样我们就可以通过这些输入输出参数去调用server端方法了。