open62541 client批量监测

添加监测变量

  • 接口:UA_Client_MonitoredItems_createDataChanges
  • 使用:
    服务器添加两个UA_UInt32类型变量节点,the.answer和myself
#include "open62541.h"

UA_Boolean running = true;

UA_NodeId addTheAnswerVariable(UA_Server *server)
{
	/* Define the attribute of the myInteger variable node */
	UA_VariableAttributes attr = UA_VariableAttributes_default;
	UA_Int32 myInteger = 1;
	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 theAnswerNodeId = 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, theAnswerNodeId, parentNodeId,
		parentReferenceNodeId, myIntegerName,
		UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);

	return theAnswerNodeId;
}

UA_NodeId addmyselfVariable(UA_Server *server)
{
	/* Define the attribute of the myInteger variable node */
	UA_VariableAttributes attr = UA_VariableAttributes_default;
	UA_Int32 myInteger = 1;
	UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
	attr.description = UA_LOCALIZEDTEXT("en-US", "myself");
	attr.displayName = UA_LOCALIZEDTEXT("en-US", "myself");
	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 myselfNodeId = UA_NODEID_STRING(1, "myself");
	UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "myself");
	UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
	UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
	UA_Server_addVariableNode(server, myselfNodeId, parentNodeId,
		parentReferenceNodeId, myIntegerName,
		UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);

	return myselfNodeId;
}

int main(void)
{
	UA_Server *server = UA_Server_new();
	//Set a user-defined port
	UA_ServerConfig_setMinimal(UA_Server_getConfig(server), 4999, NULL);

	UA_NodeId targetNodeId = addTheAnswerVariable(server);
	UA_NodeId myselfNodeId = addmyselfVariable(server);

	UA_StatusCode retval = UA_Server_run(server, &running);

	UA_Server_delete(server);

	return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}

客户端代码如下:

#include "open62541.h"

static void handler_DataChanged(UA_Client *client, UA_UInt32 subId,
	void *subContext, UA_UInt32 monId,
	void *monContext, UA_DataValue *value)
{
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "Received Notification");

	UA_NodeId *curNodeId = (UA_NodeId *)monContext;
	printf("nodeid: namespaceIndex = %d, identifierType = %d, identifier.string = %.*s\n",
		curNodeId->namespaceIndex, curNodeId->identifierType,
		curNodeId->identifier.string.length, curNodeId->identifier.string.data);

	UA_Int32 currentValue = *(UA_Int32*)(value->value.data);
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "SubId:%u, MonId:%u, Current Value: %d\n",
		subId, monId, currentValue);
}

static void handler_DeleteDataChanged(UA_Client *client, UA_UInt32 subId, void *subContext,
	UA_UInt32 monId, void *monContext) 
{
	UA_NodeId *curNodeId = (UA_NodeId *)monContext;
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT,
		"Delete item Nodeid message:namespaceIndex = %d, identifierType = %d, identifier.string = %.*s\n",
		curNodeId->namespaceIndex, curNodeId->identifierType,
		curNodeId->identifier.string.length, curNodeId->identifier.string.data);
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "Delete item SubId:%u, MonId:%u\n", subId, monId);
}

void addMonitoredItemsToVariable(UA_Client *client)
{
	/* Create a subscription */
	UA_CreateSubscriptionRequest subRequest = UA_CreateSubscriptionRequest_default();
	UA_CreateSubscriptionResponse subResponse = UA_Client_Subscriptions_create(client, subRequest, NULL, NULL, NULL);

	UA_UInt32 subId = subResponse.subscriptionId;
	if (subResponse.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
	{
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "Create subscription succeeded, id %u\n", subId);
	}

	UA_MonitoredItemCreateRequest items[2];
	UA_UInt32 newMonitoredItemIds[2];
	UA_Client_DataChangeNotificationCallback callbacks[2];
	UA_Client_DeleteMonitoredItemCallback deleteCallbacks[2];

	UA_NodeId *contexts[2];

	items[0] = UA_MonitoredItemCreateRequest_default(UA_NODEID_STRING(1, "the.answer"));
	items[1] = UA_MonitoredItemCreateRequest_default(UA_NODEID_STRING(1, "myself"));

	callbacks[0] = handler_DataChanged;
	callbacks[1] = handler_DataChanged;
	deleteCallbacks[0] = handler_DeleteDataChanged;
	deleteCallbacks[1] = handler_DeleteDataChanged;

	contexts[0] = UA_NodeId_new();
	contexts[1] = UA_NodeId_new();
	*(contexts[0]) = UA_NODEID_STRING(1, "the.answer");
	*(contexts[1]) = UA_NODEID_STRING(1, "myself");

	/* Create monitored items */
	UA_CreateMonitoredItemsRequest monRequest;
	UA_CreateMonitoredItemsRequest_init(&monRequest);
	monRequest.subscriptionId = subId;
	monRequest.timestampsToReturn = UA_TIMESTAMPSTORETURN_BOTH;
	monRequest.itemsToCreate = (UA_MonitoredItemCreateRequest*)items;
	monRequest.itemsToCreateSize = 2;
	UA_CreateMonitoredItemsResponse monResponse =
		UA_Client_MonitoredItems_createDataChanges(client, monRequest, (void **)contexts, callbacks, deleteCallbacks);

	UA_MonitoredItemCreateResult result;
	UA_MonitoredItemCreateResult_init(&result);
	if (monResponse.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
		result.statusCode = monResponse.responseHeader.serviceResult;

	if (result.statusCode == UA_STATUSCODE_GOOD && monResponse.resultsSize != 2)
		result.statusCode = UA_STATUSCODE_BADINTERNALERROR;

	if (result.statusCode == UA_STATUSCODE_GOOD)
		UA_MonitoredItemCreateResult_copy(&monResponse.results[0], &result);

	printf("add monitor item success , id1 = %d, id2 = %d\n", monResponse.results[0].monitoredItemId, monResponse.results[1].monitoredItemId);

	UA_CreateMonitoredItemsResponse_clear(&monResponse);
}

运行客户端,可以看到成功连接到了服务器,同时多了两个监控项id 1和2
在这里插入图片描述
这里我们使用UaExpert去连接服务端然后将对应值进行修改,再次看Client端输出的信息
在这里插入图片描述
可以看到the.answer和myself两个节点的值改变后通过我们的回调接口将传入的NodeId和值都对应打印出来。

这里碰到的问题大致说一下:
UA_Client_MonitoredItems_createDataChanges接口中UA_Client_DeleteMonitoredItemCallback *参数不需要时也不能直接传入NULL,不然会造成崩溃,查看源码可以发现内部没有对这个形参为空的处理,而是直接进行copy。
在这里插入图片描述
从而导致崩溃问题的发生。

移除监测变量

  • 接口:UA_Client_MonitoredItems_delete
    代码如下:
void deleteMonitoredItems(UA_Client *client)
{
	UA_DeleteMonitoredItemsRequest deleteRequest;
	UA_DeleteMonitoredItemsRequest_init(&deleteRequest);

	deleteRequest.subscriptionId = subId;
	deleteRequest.monitoredItemIds = monItemsId;
	deleteRequest.monitoredItemIdsSize = 2;

	UA_DeleteMonitoredItemsResponse deleteResponse =
		UA_Client_MonitoredItems_delete(client, deleteRequest);

	if (deleteResponse.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
	{
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "delete monitoredItems success!\n");
	}
	else
	{
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "delete monitoredItems failed! ret = %d\n", deleteResponse.responseHeader.serviceResult);
	}
	//delete subscrioptionId
	UA_Client_Subscriptions_deleteSingle(client, subId);
}

这里删除监控项后会去调用对应的deleteCallback接口,我们其中有对应的打印信息。
在这里插入图片描述
这里用了UA_Client_addRepeatedCallback定时器,5000ms后就执行删除监测项的操作。这个时候再次通过UaExpert去修改变量值我们这里将不会再有对应的变化。

Client完整代码

#include "open62541.h"

static UA_UInt32 subId;
static UA_UInt32 monItemsId[2];
static UA_UInt64 timeId;

static void handler_DataChanged(UA_Client *client, UA_UInt32 subId,
	void *subContext, UA_UInt32 monId,
	void *monContext, UA_DataValue *value)
{
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "Received Notification");

	UA_NodeId *curNodeId = (UA_NodeId *)monContext;
	printf("nodeid: namespaceIndex = %d, identifierType = %d, identifier.string = %.*s\n",
		curNodeId->namespaceIndex, curNodeId->identifierType,
		curNodeId->identifier.string.length, curNodeId->identifier.string.data);

	UA_Int32 currentValue = *(UA_Int32*)(value->value.data);
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "SubId:%u, MonId:%u, Current Value: %d\n",
		subId, monId, currentValue);
}

static void handler_DeleteDataChanged(UA_Client *client, UA_UInt32 subId, void *subContext,
	UA_UInt32 monId, void *monContext) 
{
	UA_NodeId *curNodeId = (UA_NodeId *)monContext;
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT,
		"Delete item Nodeid message:namespaceIndex = %d, identifierType = %d, identifier.string = %.*s\n",
		curNodeId->namespaceIndex, curNodeId->identifierType,
		curNodeId->identifier.string.length, curNodeId->identifier.string.data);
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "Delete item SubId:%u, MonId:%u\n", subId, monId);
}

void addMonitoredItemToVariable(UA_Client *client, UA_NodeId *target)
{
	/* Create a subscription */
	UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
	UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request,
		NULL, NULL, NULL);

	subId = response.subscriptionId;
	if (response.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
	{
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "Create subscription succeeded, id %u\n", subId);
	}

	UA_MonitoredItemCreateRequest monRequest =
		UA_MonitoredItemCreateRequest_default(*target);

	UA_MonitoredItemCreateResult monResponse =
		UA_Client_MonitoredItems_createDataChange(client, response.subscriptionId,
		UA_TIMESTAMPSTORETURN_BOTH,
		monRequest, NULL,
		handler_DataChanged, NULL);
	if (monResponse.statusCode == UA_STATUSCODE_GOOD)
	{
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "Monitoring 'the.answer', id %u\n",
			monResponse.monitoredItemId);
	}
}

void addMonitoredItemsToVariable(UA_Client *client)
{
	/* Create a subscription */
	UA_CreateSubscriptionRequest subRequest = UA_CreateSubscriptionRequest_default();
	UA_CreateSubscriptionResponse subResponse = UA_Client_Subscriptions_create(client, subRequest, NULL, NULL, NULL);

	subId = subResponse.subscriptionId;
	if (subResponse.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
	{
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "Create subscription succeeded, id %u\n", subId);
	}

	UA_MonitoredItemCreateRequest items[2];
	UA_Client_DataChangeNotificationCallback callbacks[2];
	UA_Client_DeleteMonitoredItemCallback deleteCallbacks[2];
	UA_NodeId *contexts[2];

	items[0] = UA_MonitoredItemCreateRequest_default(UA_NODEID_STRING(1, "the.answer"));
	items[1] = UA_MonitoredItemCreateRequest_default(UA_NODEID_STRING(1, "myself"));

	callbacks[0] = handler_DataChanged;
	callbacks[1] = handler_DataChanged;
	deleteCallbacks[0] = handler_DeleteDataChanged;
	deleteCallbacks[1] = handler_DeleteDataChanged;

	contexts[0] = UA_NodeId_new();
	contexts[1] = UA_NodeId_new();
	*(contexts[0]) = UA_NODEID_STRING(1, "the.answer");
	*(contexts[1]) = UA_NODEID_STRING(1, "myself");

	/* Create monitored items */
	UA_CreateMonitoredItemsRequest monRequest;
	UA_CreateMonitoredItemsRequest_init(&monRequest);
	monRequest.subscriptionId = subId;
	monRequest.timestampsToReturn = UA_TIMESTAMPSTORETURN_BOTH;
	monRequest.itemsToCreate = (UA_MonitoredItemCreateRequest*)items;
	monRequest.itemsToCreateSize = 2;
	UA_CreateMonitoredItemsResponse monResponse =
		UA_Client_MonitoredItems_createDataChanges(client, monRequest, (void **)contexts, callbacks, deleteCallbacks);

	UA_MonitoredItemCreateResult result;
	UA_MonitoredItemCreateResult_init(&result);
	if (monResponse.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
		result.statusCode = monResponse.responseHeader.serviceResult;

	if (result.statusCode == UA_STATUSCODE_GOOD && monResponse.resultsSize != 2)
		result.statusCode = UA_STATUSCODE_BADINTERNALERROR;

	if (result.statusCode == UA_STATUSCODE_GOOD)
		UA_MonitoredItemCreateResult_copy(&monResponse.results[0], &result);

	monItemsId[0] = monResponse.results[0].monitoredItemId;
	monItemsId[1] = monResponse.results[1].monitoredItemId;
	printf("add monitor item success , id1 = %d, id2 = %d\n", monResponse.results[0].monitoredItemId, monResponse.results[1].monitoredItemId);

	UA_CreateMonitoredItemsResponse_clear(&monResponse);
}

void deleteMonitoredItems(UA_Client *client)
{
	UA_DeleteMonitoredItemsRequest deleteRequest;
	UA_DeleteMonitoredItemsRequest_init(&deleteRequest);

	deleteRequest.subscriptionId = subId;
	deleteRequest.monitoredItemIds = monItemsId;
	deleteRequest.monitoredItemIdsSize = 2;

	UA_DeleteMonitoredItemsResponse deleteResponse =
		UA_Client_MonitoredItems_delete(client, deleteRequest);

	if (deleteResponse.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
	{
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "delete monitoredItems success!\n");
	}
	else
	{
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "delete monitoredItems failed! ret = %d\n", deleteResponse.responseHeader.serviceResult);
	}
	//delete subscrioptionId
	UA_Client_Subscriptions_deleteSingle(client, subId);
}

void timeCallback(UA_Client *client, void *data)
{
	deleteMonitoredItems(client);
	//关闭定时器
	UA_Client_removeCallback(client, timeId);
}

int main(void)
{
	UA_StatusCode statuscode;
	UA_Client *client = UA_Client_new();
	UA_ClientConfig_setDefault(UA_Client_getConfig(client));
	statuscode = UA_Client_connect(client, "opc.tcp://127.0.0.1:4999");
	if (statuscode != UA_STATUSCODE_GOOD)
	{
		printf("connect server failed!\n");
		return -1;
	}
	//添加items
	addMonitoredItemsToVariable(client);
	//5000ms后执行delete items操作
	UA_Client_addRepeatedCallback(client, timeCallback, NULL, 5000, &timeId);
	while (true)
	{
		UA_Client_run_iterate(client, 100);
	}

	UA_Client_delete(client);
	return 0;
}

除上述使用UA_Client_MonitoredItems_delete去删除监控节点外,发现使用UA_Client_Subscriptions_deleteSingle去删除对应的订阅id(subId)时,会同时将subId所对应的所有监控项进行删除。open62541中相关代码如下:

static void
UA_Client_Subscription_deleteInternal(UA_Client *client,
                                      UA_Client_Subscription *sub) {
    /* Remove the MonitoredItems */
    UA_Client_MonitoredItem *mon;
    UA_Client_MonitoredItem *mon_tmp;
    LIST_FOREACH_SAFE(mon, &sub->monitoredItems, listEntry, mon_tmp)
        MonitoredItem_delete(client, sub, mon);

    /* Call the delete callback */
    if(sub->deleteCallback)
        sub->deleteCallback(client, sub->subscriptionId, sub->context);

    /* Remove */
    LIST_REMOVE(sub, listEntry);
    UA_free(sub);
}

其它更细节的操作可以自行查看源码,我们将client端的接口修改一下如下:

void deleteMonitoredItems(UA_Client *client)
{
	/*UA_DeleteMonitoredItemsRequest deleteRequest;
	UA_DeleteMonitoredItemsRequest_init(&deleteRequest);

	deleteRequest.subscriptionId = subId;
	deleteRequest.monitoredItemIds = monItemsId;
	deleteRequest.monitoredItemIdsSize = 2;

	UA_DeleteMonitoredItemsResponse deleteResponse =
		UA_Client_MonitoredItems_delete(client, deleteRequest);

	if (deleteResponse.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
	{
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "delete monitoredItems success!\n");
	}
	else
	{
		UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_CLIENT, "delete monitoredItems failed! ret = %d\n", deleteResponse.responseHeader.serviceResult);
	}*/
	//delete subscrioptionId
	UA_Client_Subscriptions_deleteSingle(client, subId);
}

这里我们将UA_Client_MonitoredItems_delete部分进行了注释,直接看删除subId的效果。
在这里插入图片描述

可以看到服务器和客户端都有对应移除监控项的信息输出。

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

复杂的世界311

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值