diff --git a/devOpcuaSup/UaSdk/ItemUaSdk.cpp b/devOpcuaSup/UaSdk/ItemUaSdk.cpp index a029fcb4..bebdbd36 100644 --- a/devOpcuaSup/UaSdk/ItemUaSdk.cpp +++ b/devOpcuaSup/UaSdk/ItemUaSdk.cpp @@ -104,6 +104,7 @@ ItemUaSdk::show (int level) const << " dataDirty=" << (dataTreeDirty ? "y" : "n") << " context=" << linkinfo.subscription << "@" << session->getName() << " sampling=" << revisedSamplingInterval << "(" << linkinfo.samplingInterval << ")" + << " deadband=" << linkinfo.deadband << " qsize=" << revisedQueueSize << "(" << linkinfo.queueSize << ")" << " cqsize=" << linkinfo.clientQueueSize << " discard=" << (linkinfo.discardOldest ? "old" : "new") diff --git a/devOpcuaSup/UaSdk/SubscriptionUaSdk.cpp b/devOpcuaSup/UaSdk/SubscriptionUaSdk.cpp index c83ab8be..04bdb458 100644 --- a/devOpcuaSup/UaSdk/SubscriptionUaSdk.cpp +++ b/devOpcuaSup/UaSdk/SubscriptionUaSdk.cpp @@ -178,6 +178,19 @@ SubscriptionUaSdk::addMonitoredItems () monitoredItemCreateRequests[i].RequestedParameters.SamplingInterval = it->linkinfo.samplingInterval; monitoredItemCreateRequests[i].RequestedParameters.QueueSize = it->linkinfo.queueSize; monitoredItemCreateRequests[i].RequestedParameters.DiscardOldest = it->linkinfo.discardOldest; + if (it->linkinfo.deadband > 0.0) { + OpcUa_DataChangeFilter* pDataChangeFilter = NULL; + OpcUa_EncodeableObject_CreateExtension( + &OpcUa_DataChangeFilter_EncodeableType, + &monitoredItemCreateRequests[i].RequestedParameters.Filter, + (OpcUa_Void**)&pDataChangeFilter); + if ( pDataChangeFilter ) + { + pDataChangeFilter->DeadbandType = OpcUa_DeadbandType_Absolute; + pDataChangeFilter->DeadbandValue = it->linkinfo.deadband; + pDataChangeFilter->Trigger = OpcUa_DataChangeTrigger_StatusValue; + } + } i++; } @@ -199,20 +212,19 @@ SubscriptionUaSdk::addMonitoredItems () std::cout << "Subscription " << name << "@" << psessionuasdk->getName() << ": created " << items.size() << " monitored items (" << status.toString().toUtf8() << ")" << std::endl; - if (debug >= 5) { - for (i = 0; i < items.size(); i++) { - UaNodeId node(monitoredItemCreateRequests[i].ItemToMonitor.NodeId); - if (OpcUa_IsGood(monitoredItemCreateResults[i].StatusCode)) - std::cout << "** Monitored item " << node.toXmlString().toUtf8() + for (i = 0; i < items.size(); i++) { + if (OpcUa_IsGood(monitoredItemCreateResults[i].StatusCode)) { + if (debug >= 5) + std::cout << "** Monitored item " << UaNodeId(monitoredItemCreateRequests[i].ItemToMonitor.NodeId).toXmlString().toUtf8() << " succeeded with id " << monitoredItemCreateResults[i].MonitoredItemId << " revised sampling interval " << monitoredItemCreateResults[i].RevisedSamplingInterval << " revised queue size " << monitoredItemCreateResults[i].RevisedQueueSize << std::endl; - else - std::cout << "** Monitored item " << node.toXmlString().toUtf8() - << " failed with error " - << UaStatus(monitoredItemCreateResults[i].StatusCode).toString().toUtf8() - << std::endl; + } else { + errlogPrintf("OPC UA record %s monitored item %s failed with error %s\n", + items[i]->recConnector->getRecordName(), + UaNodeId(monitoredItemCreateRequests[i].ItemToMonitor.NodeId).toXmlString().toUtf8(), + UaStatus(monitoredItemCreateResults[i].StatusCode).toString().toUtf8()); } } } diff --git a/devOpcuaSup/devOpcua.h b/devOpcuaSup/devOpcua.h index 7c1901fa..3b8453a2 100644 --- a/devOpcuaSup/devOpcua.h +++ b/devOpcuaSup/devOpcua.h @@ -101,6 +101,7 @@ typedef struct linkInfo { epicsUInt32 queueSize; epicsUInt32 clientQueueSize; bool discardOldest = true; + double deadband = 0; std::string element; std::list elementPath; diff --git a/devOpcuaSup/linkParser.cpp b/devOpcuaSup/linkParser.cpp index 88706f90..4ce86249 100644 --- a/devOpcuaSup/linkParser.cpp +++ b/devOpcuaSup/linkParser.cpp @@ -275,6 +275,9 @@ parseLink (dbCommon *prec, const DBEntry &ent) } else if (pinfo->linkedToItem && optname == "sampling") { if (epicsParseDouble(optval.c_str(), &pinfo->samplingInterval, nullptr)) throw std::runtime_error(SB() << "error converting '" << optval << "' to Double"); + } else if (pinfo->linkedToItem && optname == "deadband") { + if (epicsParseDouble(optval.c_str(), &pinfo->deadband, nullptr)) + throw std::runtime_error(SB() << "error converting '" << optval << "' to Double"); } else if (pinfo->linkedToItem && optname == "qsize") { if (epicsParseUInt32(optval.c_str(), &pinfo->queueSize, 0, nullptr)) throw std::runtime_error(SB() << "error converting '" << optval << "' to UInt32"); @@ -352,6 +355,7 @@ parseLink (dbCommon *prec, const DBEntry &ent) else std::cout << " id(s)=" << pinfo->identifierString; std::cout << " sampling=" << pinfo->samplingInterval + << " deadband=" << pinfo->deadband << " qsize=" << pinfo->queueSize << " cqsize=" << pinfo->clientQueueSize << " discard=" << (pinfo->discardOldest ? "old" : "new") diff --git a/devOpcuaSup/open62541/ItemOpen62541.cpp b/devOpcuaSup/open62541/ItemOpen62541.cpp index 930e5809..6e9ae9ea 100644 --- a/devOpcuaSup/open62541/ItemOpen62541.cpp +++ b/devOpcuaSup/open62541/ItemOpen62541.cpp @@ -104,6 +104,7 @@ ItemOpen62541::show (int level) const << " dataDirty=" << (dataTreeDirty ? "y" : "n") << " context=" << linkinfo.subscription << "@" << session->getName() << " sampling=" << revisedSamplingInterval << "(" << linkinfo.samplingInterval << ")" + << " deadband=" << linkinfo.deadband << " qsize=" << revisedQueueSize << "(" << linkinfo.queueSize << ")" << " cqsize=" << linkinfo.clientQueueSize << " discard=" << (linkinfo.discardOldest ? "old" : "new") diff --git a/devOpcuaSup/open62541/SubscriptionOpen62541.cpp b/devOpcuaSup/open62541/SubscriptionOpen62541.cpp index 624cb76a..11648ac6 100644 --- a/devOpcuaSup/open62541/SubscriptionOpen62541.cpp +++ b/devOpcuaSup/open62541/SubscriptionOpen62541.cpp @@ -148,6 +148,7 @@ SubscriptionOpen62541::addMonitoredItems () UA_UInt32 i; UA_MonitoredItemCreateRequest monitoredItemCreateRequest; UA_MonitoredItemCreateResult monitoredItemCreateResult; + UA_DataChangeFilter dataChangeFilter; if (items.size()) { monitoredItemCreateResult.statusCode = UA_STATUSCODE_GOOD; // suppress compiler warning @@ -161,29 +162,38 @@ SubscriptionOpen62541::addMonitoredItems () monitoredItemCreateRequest.requestedParameters.samplingInterval = it->linkinfo.samplingInterval; monitoredItemCreateRequest.requestedParameters.queueSize = it->linkinfo.queueSize; monitoredItemCreateRequest.requestedParameters.discardOldest = it->linkinfo.discardOldest; + if (it->linkinfo.deadband > 0.0) { + UA_DataChangeFilter_init(&dataChangeFilter); + dataChangeFilter.deadbandType = UA_DEADBANDTYPE_ABSOLUTE; + dataChangeFilter.deadbandValue = it->linkinfo.deadband; + dataChangeFilter.trigger = UA_DATACHANGETRIGGER_STATUSVALUE; + UA_ExtensionObject *filter = &monitoredItemCreateRequest.requestedParameters.filter; + filter->content.decoded.data = &dataChangeFilter; + filter->content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGEFILTER]; + filter->encoding = UA_EXTENSIONOBJECT_DECODED; + } monitoredItemCreateResult = UA_Client_MonitoredItems_createDataChange( session.client, subscriptionSettings.subscriptionId, UA_TIMESTAMPSTORETURN_BOTH, - monitoredItemCreateRequest, items[i], [] (UA_Client *client, UA_UInt32 subId, void *subContext, + monitoredItemCreateRequest, it, [] (UA_Client *client, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext, UA_DataValue *value) { static_cast(subContext)-> dataChange(monId, *static_cast(monContext), value); }, nullptr /* deleteCallback */); if (monitoredItemCreateResult.statusCode == UA_STATUSCODE_GOOD) { - items[i]->setRevisedSamplingInterval(monitoredItemCreateResult.revisedSamplingInterval); - items[i]->setRevisedQueueSize(monitoredItemCreateResult.revisedQueueSize); - } - if (debug >= 5) { - if (monitoredItemCreateResult.statusCode == UA_STATUSCODE_GOOD) + it->setRevisedSamplingInterval(monitoredItemCreateResult.revisedSamplingInterval); + it->setRevisedQueueSize(monitoredItemCreateResult.revisedQueueSize); + if (debug >= 5) { std::cout << "** Monitored item " << monitoredItemCreateRequest.itemToMonitor.nodeId << " succeeded with id " << monitoredItemCreateResult.monitoredItemId << " revised sampling interval " << monitoredItemCreateResult.revisedSamplingInterval << " revised queue size " << monitoredItemCreateResult.revisedQueueSize << std::endl; - else - std::cout << "** Monitored item " << monitoredItemCreateRequest.itemToMonitor.nodeId - << " failed with error " - << UA_StatusCode_name(monitoredItemCreateResult.statusCode) - << std::endl; + } + } else { + std::cerr << "OPC UA record " << it->recConnector->getRecordName() + << " monitored item " << monitoredItemCreateRequest.itemToMonitor.nodeId + << " failed with error " << UA_StatusCode_name(monitoredItemCreateResult.statusCode) + << std::endl; } i++; }