OPC UA认知笔记之三
作者:互联网
读写变量节点,callback和datasource两种方式真的有区别吗?
这个问题是其实是很多初接触open62541应用普遍会遇到的问题,按照网上比较一致的解释是:一个设置了读写回调的节点,客户端对其读写操作是对变量节点所包含的变量进行操作;而一个设置了可变数据源的变量节点,客户端对其读写操作同样是对变量节点所包含的变量进行操作,但是由于该变量节点包含的变量已经同一个内存进行了映射,相当于客户端穿过了该变量节点直接读写了该内存中的数据。
不过,以上解释似乎不能让人心服口服,请看一段程序:
#include "open62541.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
static UA_Int32 externValue = 32; // global value for callback and datasource
static volatile UA_Boolean running = true;
static void stopHandler(int sig) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c");
running = false;
}
static void beforeReadData(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeid, void *nodeContext,
const UA_NumericRange *range, const UA_DataValue *data) {
UA_Variant_setScalarCopy(&dataValue->value, &externValue, &UA_TYPES[UA_TYPES_INT32]);
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "read callback");
}
static UA_StatusCode readCurrentData(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeId, void *nodeContext,
UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
UA_DataValue *dataValue) {
UA_Variant_setScalarCopy(&dataValue->value, &externValue, &UA_TYPES[UA_TYPES_INT32]);
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "read data source");
return UA_STATUSCODE_GOOD;
}
static void addDataSourceToDataVariable(UA_Server *server) {
UA_NodeId currentNodeId = UA_NODEID_STRING(1, "v-dataSource");
UA_DataSource ds;
ds.read = readCurrentData;
ds.write = NULL;
UA_Server_setVariableNode_dataSource(server, currentNodeId, ds);
}
static void addCallbackToDataVariable(UA_Server *server) {
UA_NodeId currentNodeId = UA_NODEID_STRING(1, "v-callBack");
UA_ValueCallback callback;
callback.onRead = beforeReadData;
callback.onWrite = NULL;
UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
}
static void addVariable(UA_Server *server, char *name) {
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.description = UA_LOCALIZEDTEXT("en-US", name);
attr.displayName = UA_LOCALIZEDTEXT("en-US", name);
attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
UA_NodeId oldNodeId = UA_NODEID_STRING(1, name);
UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, name);
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_NodeId typeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
UA_Server_addVariableNode(server, oldNodeId, parentNodeId, parentReferenceNodeId,
myIntegerName, typeNodeId, attr, NULL, NULL);
}
int main() {
signal(SIGINT, stopHandler);
signal(SIGTERM, stopHandler);
UA_Server *server = UA_Server_new();
UA_ServerConfig* config = UA_Server_getConfig(server);
config->verifyRequestTimestamp = UA_RULEHANDLING_ACCEPT;
UA_ServerConfig_setMinimal(config, 5566, NULL);
addVariable(server, "v-callBack");
addVariable(server, "v-dataSource");
addDataSourceToDataVariable(server);
addCallbackToDataVariable(server);
addchangeDataMethod(server);
UA_StatusCode retval = UA_Server_run(server, &running);
UA_Server_delete(server);
return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}
在通过server程序的编译时,callback有一个可以忽略的warning,如下所示。
server.c: In function ‘beforeReadData’:
server.c:48:30: warning: passing argument 1 of ‘UA_Variant_setScalarCopy’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
UA_Variant_setScalarCopy(&data->value, &externValue, &UA_TYPES[UA_TYPES_INT32]);
其实这个warning时可以忽略的,server应用程序可以正常运行,server应用程序创建了两个变量节点“v-callBack”和“v-dataSource”,而且使分别用beforeReadData和readCurrentData两个函数来让UaExpert客户端读取变量节点,通过比较可以发现两种方式的读操作效果几乎没有差别,callBack的方式似乎也可以直接和“老板谈”(直接读取变量)。看来这段代码似乎不能分辨两种操作方式的区别,权且记录一下,以备今后再解释。
标签:const,之三,NodeId,server,OPC,Server,UA,void 来源: https://www.cnblogs.com/ChenMichael/p/16397455.html