添加监测变量
- 接口: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的效果。
可以看到服务器和客户端都有对应移除监控项的信息输出。