OPC UA 与物理过程的连接

一旦在opc ua 的服务器端建立了一个信息模型之后,就在客户端读取某个变量的值,如何将这些存储在变量节点中的数据和物理过程中的参数联系起来呢?比如 读取传感器的值,传送到 temperature 变量中去,或者将speed 的值赋值给电机驱动器?这就是本文将要讨论的问题。

模型的建立

     在《OPC UA 的本质》一文中已经描述了opc ua信息模型的表达方式。如果使用手工编写XML 的信息模型文档,还是比较麻烦的事情。为此有许多模型的设计工具可供使用。

unified-automation公司的uaModeler 是一个比较好的软件,它可以交互式的方式设计opc ua的信息模型,并且可以直接输出XML模型文档,或者直接输出C++和H 文件。不过这个软件不是免费开源程序。另外它输出的C++程序是针对unified-automation公司的C++ SDK的。

      我采用的是一个开源设计模型设计工具 opcua-modeler。github 地址:

https://github.com/FreeOpcUa/opcua-modeler

      使用该工具可以产生 XML 模型文档。

      使用open62541 提供的nodeset_compiler 工具,可以将XML 文档转换成与open62541 兼容的C 和h 文件。

python ./nodeset_compiler.py --types-array=UA_TYPES --existing ../../deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml --xml demo.xml demo

    然后可以将产生的C 和h 文件结合到opcua 的程序中去。

开发工具是VS2017  C++。

调试使用uaExpert Client 程序。

模型数据的读写

     

在open62541 的网站上,介绍了几种读写模型数据的方式。

简单写入

写入变量节点 i=8003 s=analogValue

static void
updateAnalogValue (UA_Server *server) {
	UA_Float analog = 5188; 
	UA_Variant value;
	UA_Variant_setScalar(&value, &analog, &UA_TYPES[UA_TYPES_FLOAT]);
// UA_NodeId currentNodeId =UA_NODEID_STRING(1, (char*)"analogValue");
	UA_NodeId currentNodeId = UA_NODEID_NUMERIC(0, 8003);
	UA_Server_writeValue(server, currentNodeId, value);
}

在这里我遇到了麻烦,open62541 官网以及CSDN 网上的大多数例子中都是使用

UA_NodeId currentNodeId =UA_NODEID_STRING(1, (char*)"analogValue");

获取NodeId 的。但是在我的程序中却发现数据不变化。后来才发现,NodeId 有两种方式。

一种是string 方式,另一种是numeric 方式。在程序中加入的节点都是string 模式的,而open62541 的nodeset_compiler  产生的NodeId 是numeric 方式,要采用:

UA_NodeId currentNodeId = UA_NODEID_NUMERIC(0, 8003);

 才行。

   在主程序中:

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

值得注意的是,UA_Server_run(server, &running)是一个阻塞性语句。所以updateAnalogValue(server); 不能放置 它的后面。

回调方式

在这种方式下,当有客户端读写变量的时候,就调用一个回调函数,更新数据

static void
beforeReadAnalogValue(UA_Server *server,
	const UA_NodeId *sessionId, void *sessionContext,
	const UA_NodeId *nodeid, void *nodeContext,
	const UA_NumericRange *range, const UA_DataValue *data) {
	UA_Float  analog =3.33455;
	UA_Variant value;
	UA_Variant_setScalar(&value, &analog, &UA_TYPES[UA_TYPES_FLOAT]);
	UA_NodeId currentNodeId = UA_NODEID_NUMERIC(0, 8003);
	UA_Server_writeValue(server, currentNodeId, value);
	
}
static void
afterWriteAnalogValue(UA_Server *server,
	const UA_NodeId *sessionId, void *sessionContext,
	const UA_NodeId *nodeId, void *nodeContext,
	const UA_NumericRange *range, const UA_DataValue *data) {
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
		"The variable was updated");
}
static void
addValueCallbackToAnalogValueVariable(UA_Server *server) {
//  UA_NodeId currentNodeId =UA_NODEID_STRING(0,   (char *) "analogInput");
	UA_NodeId currentNodeId = UA_NODEID_NUMERIC(0, 8003);
	UA_ValueCallback callback;
	printf("currentNodeId=%d\n", currentNodeId.namespaceIndex);
	callback.onRead = beforeReadAnalogValue;
	callback.onWrite = afterWriteAnalogValue;
	UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
}

数据源

 open62541 官网上还介绍了一种数据源的方式,不过它好像是建立一具有数据源的变量

UA_Server_addDataSourceVariableNode(server, currentNodeId, parentNodeId,

                  parentReferenceNodeId, currentName,

                  variableTypeNodeId, attr,

                  timeDataSource, NULL, NULL);

我不清楚如何在已经建立的变量节点上实现DataSource的方式。所以就不在这里介绍了。感觉使用回调方式已经够了。

猜你喜欢

转载自blog.csdn.net/yaojiawan/article/details/89041216