From 370df35a54c361e4fe751e6a32865240d6e8d2e7 Mon Sep 17 00:00:00 2001 From: Matthias Dornaus Date: Tue, 30 Aug 2022 15:45:46 +0200 Subject: [PATCH 01/22] Initial commit. --- DashboardClient/DashboardClient.cpp | 121 ++++++++++++ DashboardClient/DashboardClient.hpp | 7 + DashboardClient/IDashboardDataClient.hpp | 4 + DashboardClient/OpcUaTypeReader.hpp | 1 + DashboardOpcUaClient.hpp | 4 + MachineObserver/DashboardMachineObserver.cpp | 192 +++++++++++++++++++ MachineObserver/DashboardMachineObserver.hpp | 4 + ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp | 8 + ModelOpcUa/src/ModelOpcUa/ModelInstance.hpp | 2 +- OpcUaClient/OpcUaClient.cpp | 15 +- OpcUaClient/OpcUaClient.hpp | 10 +- configuration.json.example | 12 +- 12 files changed, 365 insertions(+), 15 deletions(-) diff --git a/DashboardClient/DashboardClient.cpp b/DashboardClient/DashboardClient.cpp index 345d6d4e..5baab936 100644 --- a/DashboardClient/DashboardClient.cpp +++ b/DashboardClient/DashboardClient.cpp @@ -27,6 +27,126 @@ namespace Umati : m_pDashboardDataClient(pDashboardDataClient), m_pPublisher(pPublisher), m_pTypeReader(pTypeReader) { } + void DashboardClient::updateDeleteDataSet(ModelOpcUa::NodeId_t refreshNodeId) { + auto search = browsedSimpleNodes.find(refreshNodeId); + if( search != browsedSimpleNodes.end()) { + std::shared_ptr simpleNode = search->second; + auto childNodes = simpleNode->ChildNodes; + if(!childNodes.empty()) { + auto child = childNodes.front(); + std::shared_ptr placeholderNode = std::dynamic_pointer_cast(child); + if(placeholderNode == nullptr) { + LOG(INFO) << "Not a Placeholder"; + } else { + auto browseResults = m_pDashboardDataClient->Browse(refreshNodeId, child->ReferenceType, child->SpecifiedTypeNodeId); + //Check if Instances are still in browsresult + std::list instances = placeholderNode->getInstances(); + std::list missingElements; + for(std::list::iterator it = instances.begin(); it != instances.end(); it++) { + bool found = false; + ModelOpcUa::NodeId_t nodeId = it->pNode->NodeId; + for(auto &browseResult : browseResults) { + if(browseResult.NodeId == nodeId) { + found = true; + break; + } + } + if(!found) { + LOG(ERROR) << "Deleted Node: " << it -> pNode ->NodeId; + missingElements.push_back(*it); + } + } + //Remove and Unsubscribe the elements + for(std::list::iterator it = missingElements.begin(); it != missingElements.end();it++) { + std::shared_ptr placeholderNodeUnconst = std::const_pointer_cast(placeholderNode); + placeholderNodeUnconst->removeInstance(*it); + //deleteAndUnsubscribeNode(*it); + } + } + } + } + } + void DashboardClient::deleteAndUnsubscribeNode(std::shared_ptr node) { + /*const std::list> childNodes = node->ChildNodes; + for(std::list>::iterator it = childNodes.begin(); it != childNodes.end(); it++) { + deleteAndUnsubscribeNode(*it); + } + std::vector monItemIds; + std::vector clientHandles; + for (auto values : m_subscribedValues){ + auto value = values.get(); + if(value){ + monItemIds.push_back(value->getMonitoredItemId()); + clientHandles.push_back(value->getClientHandle()); + } + } + m_subscribedValues.clear(); + + m_pDashboardDataClient->Unsubscribe(monItemIds, clientHandles); + + std::lock_guard l(m_dataSetMutex); + m_dataSets.clear();*/ + } + void DashboardClient::updateAddDataSet(ModelOpcUa::NodeId_t refreshNodeId) { + if(!m_dataSets.empty()) { + std::shared_ptr dataSet = m_dataSets.front(); + auto search = browsedSimpleNodes.find(refreshNodeId); + if( search != browsedSimpleNodes.end()) { + std::shared_ptr simpleNode = search->second; + auto childNodes = simpleNode->ChildNodes; + if(!childNodes.empty()) { + auto child = childNodes.front(); + std::shared_ptr placeholderNode = std::dynamic_pointer_cast(child); + if(placeholderNode == nullptr) { + LOG(INFO) << "Not a Placeholder"; + } else { + LOG(INFO) << "Placeholder"; + auto browseResults = m_pDashboardDataClient->Browse(refreshNodeId, child->ReferenceType, + child->SpecifiedTypeNodeId); + LOG(INFO) << "browsed"; + for (auto &browseResult : browseResults) { + if (browseResult.TypeDefinition.Id == NodeId_BaseObjectType.Id) { + auto ifs = m_pDashboardDataClient->Browse(browseResult.NodeId, + Dashboard::IDashboardDataClient::BrowseContext_t::HasInterface()); + browseResult.TypeDefinition = ifs.front().NodeId; + LOG(INFO) << "Updated TypeDefinition of " << browseResult.BrowseName.Name << " to " << browseResult.TypeDefinition + << " because the node implements an interface"; + } + + auto possibleType = m_pTypeReader->m_typeMap->find(browseResult.TypeDefinition); // use subtype + if (possibleType != m_pTypeReader->m_typeMap->end()) + { + // LOG(INFO) << "Found type for " << typeName; + if(browsedNodes.find(browseResult.NodeId) == browsedNodes.end()) { + LOG(ERROR) << "Added" << browseResult.NodeId; + auto sharedPossibleType = possibleType->second; + ModelOpcUa::PlaceholderElement plElement; + plElement.BrowseName = browseResult.BrowseName; + plElement.pNode = TransformToNodeIds(browseResult.NodeId, sharedPossibleType); + plElement.TypeDefinition = browseResult.TypeDefinition; + //Const cast + std::shared_ptr placeholderNodeUnconst = std::const_pointer_cast(placeholderNode); + placeholderNodeUnconst->addInstance(plElement); + subscribeValues(plElement.pNode, dataSet->values, dataSet->values_mutex); + std::lock_guard l(m_dataSetMutex); + } + else { + LOG(INFO) << "Allready found"; + } + } + } + } + } + } + } + } + bool DashboardClient::containsNodeId(ModelOpcUa::NodeId_t nodeId) { + if(browsedNodes.find(nodeId) != browsedNodes.end()) { + return true; + } else { + return false; + } + } /** @@ -232,6 +352,7 @@ namespace Umati foundChildNodes); pNode->ofBaseDataVariableType = pTypeDefinition->ofBaseDataVariableType; + browsedSimpleNodes.insert({startNode, pNode}); return pNode; } diff --git a/DashboardClient/DashboardClient.hpp b/DashboardClient/DashboardClient.hpp index c64efb4d..e6f85328 100644 --- a/DashboardClient/DashboardClient.hpp +++ b/DashboardClient/DashboardClient.hpp @@ -52,6 +52,10 @@ namespace Umati { void Unsubscribe(ModelOpcUa::NodeId_t nodeId); + bool containsNodeId(ModelOpcUa::NodeId_t nodeId); + void updateAddDataSet(ModelOpcUa::NodeId_t nodeId); + void updateDeleteDataSet(ModelOpcUa::NodeId_t nodeId); + protected: @@ -92,6 +96,7 @@ namespace Umati { std::shared_ptr m_pTypeReader; std::set browsedNodes; + std::map> browsedSimpleNodes; std::recursive_mutex m_dataSetMutex; std::list> m_dataSets; std::map m_latestMessages; @@ -134,6 +139,8 @@ namespace Umati { void TransformToNodeIdNodeNotFoundLog(const ModelOpcUa::NodeId_t &startNode, const std::shared_ptr &pChild) const; + + void deleteAndUnsubscribeNode(std::shared_ptr node); }; } } diff --git a/DashboardClient/IDashboardDataClient.hpp b/DashboardClient/IDashboardDataClient.hpp index 336904f4..7d5c0685 100644 --- a/DashboardClient/IDashboardDataClient.hpp +++ b/DashboardClient/IDashboardDataClient.hpp @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -249,6 +250,9 @@ namespace Umati /// Verify that the connection and session are ok virtual bool VerifyConnection() = 0; + + virtual std::shared_ptr getUaClient() = 0; + }; } // namespace Dashboard } // namespace Umati diff --git a/DashboardClient/OpcUaTypeReader.hpp b/DashboardClient/OpcUaTypeReader.hpp index 24eacc04..1ea8e94a 100644 --- a/DashboardClient/OpcUaTypeReader.hpp +++ b/DashboardClient/OpcUaTypeReader.hpp @@ -48,6 +48,7 @@ namespace Umati std::shared_ptr typeDefinitionToStructureNode(const ModelOpcUa::NodeId_t &typeDefinition) const; std::shared_ptr getIdentificationTypeStructureNode(const ModelOpcUa::NodeId_t &typeDefinition) const; ModelOpcUa::NodeId_t getIdentificationTypeNodeId(const ModelOpcUa::NodeId_t &typeDefinition) const; + protected: /// Map of typedef std::shared_ptr m_namespaces; + protected: std::function m_issueReset; std::shared_ptr m_opcUaWrapper; diff --git a/MachineObserver/DashboardMachineObserver.cpp b/MachineObserver/DashboardMachineObserver.cpp index 63efa879..bf046a37 100644 --- a/MachineObserver/DashboardMachineObserver.cpp +++ b/MachineObserver/DashboardMachineObserver.cpp @@ -18,10 +18,155 @@ #include #include "PublishMachinesList.hpp" + namespace Umati { namespace MachineObserver { + static DashboardMachineObserver* dbmo; + + static void handler_events(UA_Client *client, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext, size_t nEventFields, UA_Variant *eventFields) { + UA_NodeId* sourceNodeId; + UA_NodeId affectedNode; + UA_NodeId affectedType; + UA_Byte verb; + for(size_t i = 0; i < nEventFields; ++i) { + UA_Variant eventField = eventFields[i]; + if(UA_Variant_hasScalarType(&eventField, &UA_TYPES[UA_TYPES_NODEID])) { + UA_NodeId* nodeId = (UA_NodeId *)eventField.data; + if(i == 1) { + sourceNodeId = nodeId; + } + } else { + UA_ExtensionObject* extensionObjects = (UA_ExtensionObject*)eventField.data; + UA_ModelChangeStructureDataType modelChangeStructureDataTypes[eventField.arrayLength]; + for(int j = 0; j < eventField.arrayLength; j ++) { + UA_ExtensionObject extensionObject = extensionObjects[j]; + UA_ModelChangeStructureDataType* modelChangeStructureDataType =(UA_ModelChangeStructureDataType*)extensionObject.content.decoded.data; + modelChangeStructureDataTypes[j] = *modelChangeStructureDataType; + } + + dbmo -> updateAfterModelChangeEvent(modelChangeStructureDataTypes, eventField.arrayLength); + } + } + + + } + + void DashboardMachineObserver::AddSubscription() + { + dbmo = this; + std::shared_ptr client = m_pDataClient->getUaClient(); + UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default(); + UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client.get(), request,NULL, NULL, NULL); + if(response.responseHeader.serviceResult == UA_STATUSCODE_GOOD) + { + LOG(INFO) << "###################################### Created Subscription #####################################"; + UA_UInt32 subId = response.subscriptionId; + UA_MonitoredItemCreateRequest item; + UA_MonitoredItemCreateRequest_init(&item); + item.itemToMonitor.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER; + item.monitoringMode = UA_MONITORINGMODE_REPORTING; + //Allways Subscribe to the Serer object + item.itemToMonitor.nodeId = UA_NODEID_NUMERIC(0, 2253); // Root->Objects->Server + + + + UA_EventFilter filter; + UA_EventFilter_init(&filter); + //Setup selection Clauses for Filter + int nSelectClauses = 3; + UA_SimpleAttributeOperand *selectClauses = (UA_SimpleAttributeOperand*) + UA_Array_new(nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]); + + + + for(size_t i =0; i pDataClient, @@ -31,6 +176,7 @@ namespace Umati :MachineObserver(std::move(pDataClient), std::move(pOpcUaTypeReader), std::move(machinesFilter)), m_pPublisher(std::move(pPublisher)) { + AddSubscription(); startUpdateMachineThread(); } @@ -39,6 +185,52 @@ namespace Umati stopMachineUpdateThread(); } + void DashboardMachineObserver::updateAfterModelChangeEvent(UA_ModelChangeStructureDataType* modelChangeStructureDataTypes, size_t nModelChangeStructureDataTypes) { + bool nodeAdded = false; + bool nodeDeleted = false; + bool referenceAdded = false; + bool referenceDeleted = false; + bool dataTypeChanged = false; + std::list> changedDbcs = std::list>(); + for(int i = 0; i < nModelChangeStructureDataTypes; i++) { + UA_ModelChangeStructureDataType modelChangeStructureDataType = modelChangeStructureDataTypes[i]; + UA_NodeId affectedNode = modelChangeStructureDataType.affected; + UA_NodeId affectedType = modelChangeStructureDataType.affectedType; + UA_Byte verb = modelChangeStructureDataType.verb; + + if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED) {nodeAdded = true;} + if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED) {nodeDeleted = true;} + if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED) {referenceAdded = true;} + if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED) {referenceDeleted = true;} + if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED) == UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED) {dataTypeChanged = true;} + + /* Find the Dashboardclient for the event. Check uris with namespaces.*/ + ModelOpcUa::NodeId_t typeId = ModelOpcUa::NodeId_t(); + typeId.Id = "i=" + std::to_string(affectedType.identifier.numeric); + affectedType.namespaceIndex; + std::vector namespaces = m_pDataClient->Namespaces(); + if(affectedType.namespaceIndex < namespaces.size()) { + typeId.Uri = namespaces[affectedType.namespaceIndex]; + ModelOpcUa::NodeId_t nodeId = ModelOpcUa::NodeId_t(); + nodeId.Id = "i=" + std::to_string(affectedNode.identifier.numeric); + for(auto it = m_dashboardClients.begin(); it != m_dashboardClients.end(); it++) { + std::shared_ptr dbc = it -> second; + nodeId.Uri = (it -> first).Uri; + if(dbc->containsNodeId(nodeId)) { + changedDbcs.push_back(dbc); + if(nodeAdded || referenceAdded) {dbc->updateAddDataSet(nodeId);} + if(nodeDeleted || referenceDeleted) {dbc->updateDeleteDataSet(nodeId);} + } + } + } + } + //Publish DataboardClients that have changes + for(std::shared_ptr dbc : changedDbcs) { + std::unique_lock ul(m_dashboardClients_mutex); + dbc->Publish(); + } + } + void DashboardMachineObserver::PublishAll() { { diff --git a/MachineObserver/DashboardMachineObserver.hpp b/MachineObserver/DashboardMachineObserver.hpp index 531f1553..078453dc 100644 --- a/MachineObserver/DashboardMachineObserver.hpp +++ b/MachineObserver/DashboardMachineObserver.hpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace Umati { @@ -35,12 +36,15 @@ namespace Umati ~DashboardMachineObserver() override; void PublishAll(); + void updateAfterModelChangeEvent(UA_ModelChangeStructureDataType* modelChangeStructureDataTypes, size_t nModelChangeStructureDataTypes); protected: void startUpdateMachineThread(); void stopMachineUpdateThread(); + void AddSubscription(); + void publishMachinesList(); // Inherit from MachineObserver diff --git a/ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp b/ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp index 15a18763..2fb1368a 100644 --- a/ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp +++ b/ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp @@ -21,6 +21,14 @@ namespace ModelOpcUa { std::list PlaceholderNode::getInstances() const { return this->Instances; } + void PlaceholderNode::removeInstance(const PlaceholderElement &instance) { + for(std::list::iterator it = this->Instances.begin(); it != this->Instances.end(); it++ ) { + if(it->pNode->TypeNodeId == instance.pNode->TypeNodeId) { + this->Instances.erase(it); + break; + } + } + } Node::Node(const NodeDefinition &nodeDefinition, std::list> childNodes) : NodeDefinition(nodeDefinition), ChildNodes(std::move(childNodes)) { diff --git a/ModelOpcUa/src/ModelOpcUa/ModelInstance.hpp b/ModelOpcUa/src/ModelOpcUa/ModelInstance.hpp index 3371619a..a85709fd 100644 --- a/ModelOpcUa/src/ModelOpcUa/ModelInstance.hpp +++ b/ModelOpcUa/src/ModelOpcUa/ModelInstance.hpp @@ -65,7 +65,7 @@ namespace ModelOpcUa { using Node::Node; void addInstance(const PlaceholderElement &instance); - + void removeInstance(const PlaceholderElement &instance); std::list getInstances() const; protected: diff --git a/OpcUaClient/OpcUaClient.cpp b/OpcUaClient/OpcUaClient.cpp index 89b14ac8..00a360ce 100644 --- a/OpcUaClient/OpcUaClient.cpp +++ b/OpcUaClient/OpcUaClient.cpp @@ -460,8 +460,11 @@ std::string OpcUaClient::readNodeBrowseName(const ModelOpcUa::NodeId_t &_nodeId) return _nodeId.Uri + ";" + resName; } -UA_NodeClass OpcUaClient::readNodeClass(const open62541Cpp::UA_NodeId &nodeId) { - checkConnection(); + UA_NodeClass OpcUaClient::readNodeClass(const open62541Cpp::UA_NodeId &nodeId) + { + LOG(INFO) << "NodeId: " << nodeId.NodeId->identifier.numeric; + LOG(INFO) << "Uri" << nodeId.NodeId->namespaceIndex; + checkConnection(); UA_NodeClass returnClass; UA_NodeClass_init(&returnClass); @@ -1045,8 +1048,12 @@ bool OpcUaClient::VerifyConnection() { return false; } - return true; -} + return true; + } + std::shared_ptr OpcUaClient::getUaClient() + { + return m_pClient; + } } // namespace OpcUa } // namespace Umati diff --git a/OpcUaClient/OpcUaClient.hpp b/OpcUaClient/OpcUaClient.hpp index 5ac54536..907659e1 100644 --- a/OpcUaClient/OpcUaClient.hpp +++ b/OpcUaClient/OpcUaClient.hpp @@ -74,16 +74,18 @@ class OpcUaClient : public Dashboard::IDashboardDataClient { bool VerifyConnection() override; - bool isSameOrSubtype(const ModelOpcUa::NodeId_t &expectedType, const ModelOpcUa::NodeId_t &checkType, size_t maxDepth) override; - + bool isSameOrSubtype(const ModelOpcUa::NodeId_t &expectedType, const ModelOpcUa::NodeId_t &checkType, + size_t maxDepth) override; + void buildCustomDataTypes() override; void readTypeDictionaries() override; void updateCustomTypes() override; - protected: - void connectionStatusChanged(UA_Int32 clientConnectionId, UA_ServerState serverStatus); + std::shared_ptr getUaClient() override; + protected: + void connectionStatusChanged(UA_Int32 clientConnectionId, UA_ServerState serverStatus); bool connect(); diff --git a/configuration.json.example b/configuration.json.example index 9a1a480b..70f0b8ca 100644 --- a/configuration.json.example +++ b/configuration.json.example @@ -18,9 +18,9 @@ }, "MachinesFilter": [ { - "Uri": "http://example.com/BasicMachineTool/", - "Id": "i=66382", - "$comment": "BasicMachineTool" + "Uri": "de.uni-stuttgart.isw.glas.sampleserver", + "Id": "i=58192", + "$comment": "GlassMachineType" } ], "ObjectTypeNamespaces": [ @@ -124,9 +124,9 @@ "Namespace": "http://opcfoundation.org/UA/MachineTool/", "Types": [ { - "Uri": "http://opcfoundation.org/UA/MachineTool/", - "Id": "i=13", - "$comment": "MachineToolType" + "Uri": "http://opcfoundation.org/UA/Glass/Flat/", + "Id": "i=1015", + "$comment": "GlassMachineType" } ], "IdentificationType": { From 16ada09d2dcb91ccc0519f89a0f9c04eb6397c78 Mon Sep 17 00:00:00 2001 From: Matthias Dornaus Date: Wed, 31 Aug 2022 10:51:08 +0200 Subject: [PATCH 02/22] Decoupled eventhandling from event processing due to threading issues. --- MachineObserver/DashboardMachineObserver.cpp | 39 +++++++++++++++++--- MachineObserver/DashboardMachineObserver.hpp | 16 +++++++- configuration.json.example | 2 +- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/MachineObserver/DashboardMachineObserver.cpp b/MachineObserver/DashboardMachineObserver.cpp index bf046a37..f2c85eca 100644 --- a/MachineObserver/DashboardMachineObserver.cpp +++ b/MachineObserver/DashboardMachineObserver.cpp @@ -45,7 +45,6 @@ namespace Umati UA_ModelChangeStructureDataType* modelChangeStructureDataType =(UA_ModelChangeStructureDataType*)extensionObject.content.decoded.data; modelChangeStructureDataTypes[j] = *modelChangeStructureDataType; } - dbmo -> updateAfterModelChangeEvent(modelChangeStructureDataTypes, eventField.arrayLength); } } @@ -176,6 +175,7 @@ namespace Umati :MachineObserver(std::move(pDataClient), std::move(pOpcUaTypeReader), std::move(machinesFilter)), m_pPublisher(std::move(pPublisher)) { + startEventThread(); AddSubscription(); startUpdateMachineThread(); } @@ -218,17 +218,24 @@ namespace Umati nodeId.Uri = (it -> first).Uri; if(dbc->containsNodeId(nodeId)) { changedDbcs.push_back(dbc); - if(nodeAdded || referenceAdded) {dbc->updateAddDataSet(nodeId);} - if(nodeDeleted || referenceDeleted) {dbc->updateDeleteDataSet(nodeId);} + StructureChangeEvent stc; + stc.dbc = dbc; + stc.refreshNode = nodeId; + stc.nodeAdded = nodeAdded; + stc.nodeDeleted = nodeDeleted; + stc.referenceAdded = referenceAdded; + stc.referenceDeleted = referenceDeleted; + stc.dataTypeChanged = dataTypeChanged; + this->modelStructureChangeEvents.push(stc); } } } } //Publish DataboardClients that have changes - for(std::shared_ptr dbc : changedDbcs) { + /*for(std::shared_ptr dbc : changedDbcs) { std::unique_lock ul(m_dashboardClients_mutex); dbc->Publish(); - } + }*/ } void DashboardMachineObserver::PublishAll() @@ -274,6 +281,28 @@ namespace Umati m_running = true; m_updateMachineThread = std::thread(func); } + void DashboardMachineObserver::startEventThread() + { + std::thread func1 ([this]() { + while (true) + { + if(!this->modelStructureChangeEvents.empty()) { + StructureChangeEvent sce = this->modelStructureChangeEvents.front(); + if(sce.nodeAdded || sce.referenceAdded) { + sce.dbc->updateAddDataSet(sce.refreshNode); + sce.dbc->Publish(); + } + if(sce.nodeDeleted || sce.referenceDeleted) { + sce.dbc->updateDeleteDataSet(sce.refreshNode); + sce.dbc->Publish(); + } + this->modelStructureChangeEvents.pop(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }); + func1.detach(); + } void DashboardMachineObserver::stopMachineUpdateThread() { diff --git a/MachineObserver/DashboardMachineObserver.hpp b/MachineObserver/DashboardMachineObserver.hpp index 078453dc..fb21ea5b 100644 --- a/MachineObserver/DashboardMachineObserver.hpp +++ b/MachineObserver/DashboardMachineObserver.hpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace Umati { @@ -37,6 +38,17 @@ namespace Umati void PublishAll(); void updateAfterModelChangeEvent(UA_ModelChangeStructureDataType* modelChangeStructureDataTypes, size_t nModelChangeStructureDataTypes); + struct StructureChangeEvent{ + std::shared_ptr dbc; + ModelOpcUa::NodeId_t refreshNode; + bool nodeAdded = false; + bool nodeDeleted = false; + bool referenceAdded = false; + bool referenceDeleted = false; + bool dataTypeChanged = false; + }; + std::queue modelStructureChangeEvents; + protected: void startUpdateMachineThread(); @@ -66,9 +78,11 @@ namespace Umati ModelOpcUa::NodeId_t TypeDefinition; ModelOpcUa::NodeId_t Parent; }; + int m_publishMachinesOnline = 0; - + + void startEventThread(); std::atomic_bool m_running = {false}; std::thread m_updateMachineThread; diff --git a/configuration.json.example b/configuration.json.example index 70f0b8ca..b6a945f8 100644 --- a/configuration.json.example +++ b/configuration.json.example @@ -19,7 +19,7 @@ "MachinesFilter": [ { "Uri": "de.uni-stuttgart.isw.glas.sampleserver", - "Id": "i=58192", + "Id": "i=55091", "$comment": "GlassMachineType" } ], From 35450528b438e9a7ad5b7abc569dde0652158249 Mon Sep 17 00:00:00 2001 From: Matthias Dornaus Date: Sat, 10 Sep 2022 17:24:31 +0200 Subject: [PATCH 03/22] Changed deletion of Jobs. --- DashboardClient/DashboardClient.cpp | 46 +++++++++++--------- DashboardClient/DashboardClient.hpp | 3 +- MachineObserver/DashboardMachineObserver.cpp | 7 +-- ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp | 2 +- OpcUaClient/OpcUaClient.cpp | 4 +- Util/ConfigureLogger.cpp | 2 + configuration.json.example | 2 +- 7 files changed, 38 insertions(+), 28 deletions(-) diff --git a/DashboardClient/DashboardClient.cpp b/DashboardClient/DashboardClient.cpp index 5baab936..ddaf7fea 100644 --- a/DashboardClient/DashboardClient.cpp +++ b/DashboardClient/DashboardClient.cpp @@ -52,7 +52,7 @@ namespace Umati } } if(!found) { - LOG(ERROR) << "Deleted Node: " << it -> pNode ->NodeId; + LOG(INFO) << "Deleted Node: " << it -> pNode ->NodeId; missingElements.push_back(*it); } } @@ -60,32 +60,38 @@ namespace Umati for(std::list::iterator it = missingElements.begin(); it != missingElements.end();it++) { std::shared_ptr placeholderNodeUnconst = std::const_pointer_cast(placeholderNode); placeholderNodeUnconst->removeInstance(*it); - //deleteAndUnsubscribeNode(*it); + deleteAndUnsubscribeNode(*it); } } } } } - void DashboardClient::deleteAndUnsubscribeNode(std::shared_ptr node) { - /*const std::list> childNodes = node->ChildNodes; - for(std::list>::iterator it = childNodes.begin(); it != childNodes.end(); it++) { - deleteAndUnsubscribeNode(*it); - } - std::vector monItemIds; - std::vector clientHandles; - for (auto values : m_subscribedValues){ - auto value = values.get(); - if(value){ - monItemIds.push_back(value->getMonitoredItemId()); - clientHandles.push_back(value->getClientHandle()); + void DashboardClient::deleteAndUnsubscribeNode(ModelOpcUa::PlaceholderElement placeHolderElement) { + std::shared_ptr element = placeHolderElement.pNode; + const std::list> children = element->ChildNodes; + for(auto it = children.begin(); it != children.end();it++) { + std::shared_ptr node = *it; + std::shared_ptr simpleNode = std::dynamic_pointer_cast(node); + if(simpleNode != nullptr) { + const ModelOpcUa::SimpleNode simpleN = *simpleNode; + deleteAndUnsubscribeNode(simpleN.NodeId); + } else { + std::shared_ptr placeHolderNode = std::dynamic_pointer_cast(node); + if(placeHolderNode != nullptr) { + std::list instances = placeHolderNode->getInstances(); + for(std::list::iterator it = instances.begin(); it != instances.end(); it++) { + deleteAndUnsubscribeNode(*it); + } + } } } - m_subscribedValues.clear(); + browsedNodes.erase(element->NodeId); + browsedSimpleNodes.erase(element->NodeId); - m_pDashboardDataClient->Unsubscribe(monItemIds, clientHandles); - - std::lock_guard l(m_dataSetMutex); - m_dataSets.clear();*/ + } + void DashboardClient::deleteAndUnsubscribeNode(const ModelOpcUa::NodeId_t nodeId) { + browsedNodes.erase(nodeId); + browsedSimpleNodes.erase(nodeId); } void DashboardClient::updateAddDataSet(ModelOpcUa::NodeId_t refreshNodeId) { if(!m_dataSets.empty()) { @@ -118,7 +124,7 @@ namespace Umati { // LOG(INFO) << "Found type for " << typeName; if(browsedNodes.find(browseResult.NodeId) == browsedNodes.end()) { - LOG(ERROR) << "Added" << browseResult.NodeId; + LOG(INFO) << "Added" << browseResult.NodeId; auto sharedPossibleType = possibleType->second; ModelOpcUa::PlaceholderElement plElement; plElement.BrowseName = browseResult.BrowseName; diff --git a/DashboardClient/DashboardClient.hpp b/DashboardClient/DashboardClient.hpp index e6f85328..2c396734 100644 --- a/DashboardClient/DashboardClient.hpp +++ b/DashboardClient/DashboardClient.hpp @@ -140,7 +140,8 @@ namespace Umati { void TransformToNodeIdNodeNotFoundLog(const ModelOpcUa::NodeId_t &startNode, const std::shared_ptr &pChild) const; - void deleteAndUnsubscribeNode(std::shared_ptr node); + void deleteAndUnsubscribeNode(ModelOpcUa::PlaceholderElement placeHolderElement); + void deleteAndUnsubscribeNode(const ModelOpcUa::NodeId_t nodeId); }; } } diff --git a/MachineObserver/DashboardMachineObserver.cpp b/MachineObserver/DashboardMachineObserver.cpp index f2c85eca..6f3ec1ca 100644 --- a/MachineObserver/DashboardMachineObserver.cpp +++ b/MachineObserver/DashboardMachineObserver.cpp @@ -156,9 +156,9 @@ namespace Umati UA_MonitoredItemCreateResult result = UA_Client_MonitoredItems_createEvent(client.get(), subId, UA_TIMESTAMPSTORETURN_BOTH, item, &monId, handler_events, NULL); if(result.statusCode == UA_STATUSCODE_GOOD) { - LOG(INFO) << "###################################### Created MonitoredItem #####################################"; + LOG(INFO) << "Created MonitoredItem"; } else { - LOG(INFO) << "###################################### Error Error Error #####################################"; + LOG(ERROR) << "Unable to create MonitoredItem"; } monId = result.monitoredItemId; } else { @@ -298,7 +298,8 @@ namespace Umati } this->modelStructureChangeEvents.pop(); } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + //LOG(INFO) << "Thread Loop"; + std::this_thread::sleep_for(std::chrono::milliseconds(250)); } }); func1.detach(); diff --git a/ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp b/ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp index 2fb1368a..03658f04 100644 --- a/ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp +++ b/ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp @@ -23,7 +23,7 @@ namespace ModelOpcUa { } void PlaceholderNode::removeInstance(const PlaceholderElement &instance) { for(std::list::iterator it = this->Instances.begin(); it != this->Instances.end(); it++ ) { - if(it->pNode->TypeNodeId == instance.pNode->TypeNodeId) { + if(it->pNode->NodeId == instance.pNode->NodeId) { this->Instances.erase(it); break; } diff --git a/OpcUaClient/OpcUaClient.cpp b/OpcUaClient/OpcUaClient.cpp index 00a360ce..06a4318a 100644 --- a/OpcUaClient/OpcUaClient.cpp +++ b/OpcUaClient/OpcUaClient.cpp @@ -462,8 +462,8 @@ std::string OpcUaClient::readNodeBrowseName(const ModelOpcUa::NodeId_t &_nodeId) UA_NodeClass OpcUaClient::readNodeClass(const open62541Cpp::UA_NodeId &nodeId) { - LOG(INFO) << "NodeId: " << nodeId.NodeId->identifier.numeric; - LOG(INFO) << "Uri" << nodeId.NodeId->namespaceIndex; + //LOG(INFO) << "NodeId: " << nodeId.NodeId->identifier.numeric; + //LOG(INFO) << "Uri" << nodeId.NodeId->namespaceIndex; checkConnection(); UA_NodeClass returnClass; diff --git a/Util/ConfigureLogger.cpp b/Util/ConfigureLogger.cpp index 5b359e96..500920cd 100644 --- a/Util/ConfigureLogger.cpp +++ b/Util/ConfigureLogger.cpp @@ -37,6 +37,8 @@ namespace Umati { TO_STANDARD_OUTPUT = false * VERBOSE: TO_STANDARD_OUTPUT = false +* INFO: + TO_STANDARD_OUTPUT = true )LOG_CONFIG"); conf.setGlobally(el::ConfigurationType::Filename, name + "-%datetime{%Y%M%d}.log"); diff --git a/configuration.json.example b/configuration.json.example index b6a945f8..70f0b8ca 100644 --- a/configuration.json.example +++ b/configuration.json.example @@ -19,7 +19,7 @@ "MachinesFilter": [ { "Uri": "de.uni-stuttgart.isw.glas.sampleserver", - "Id": "i=55091", + "Id": "i=58192", "$comment": "GlassMachineType" } ], From 68c9d0f1e70f0b4871850412c8e216f345ad2012 Mon Sep 17 00:00:00 2001 From: Matthias Dornaus Date: Sat, 10 Sep 2022 18:04:05 +0200 Subject: [PATCH 04/22] Removed some debug logging. Increased Queue timing (10ms) lookup. --- DashboardClient/DashboardClient.cpp | 6 +----- MachineObserver/DashboardMachineObserver.cpp | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/DashboardClient/DashboardClient.cpp b/DashboardClient/DashboardClient.cpp index ddaf7fea..532c74ad 100644 --- a/DashboardClient/DashboardClient.cpp +++ b/DashboardClient/DashboardClient.cpp @@ -36,7 +36,6 @@ namespace Umati auto child = childNodes.front(); std::shared_ptr placeholderNode = std::dynamic_pointer_cast(child); if(placeholderNode == nullptr) { - LOG(INFO) << "Not a Placeholder"; } else { auto browseResults = m_pDashboardDataClient->Browse(refreshNodeId, child->ReferenceType, child->SpecifiedTypeNodeId); //Check if Instances are still in browsresult @@ -104,12 +103,9 @@ namespace Umati auto child = childNodes.front(); std::shared_ptr placeholderNode = std::dynamic_pointer_cast(child); if(placeholderNode == nullptr) { - LOG(INFO) << "Not a Placeholder"; } else { - LOG(INFO) << "Placeholder"; auto browseResults = m_pDashboardDataClient->Browse(refreshNodeId, child->ReferenceType, child->SpecifiedTypeNodeId); - LOG(INFO) << "browsed"; for (auto &browseResult : browseResults) { if (browseResult.TypeDefinition.Id == NodeId_BaseObjectType.Id) { auto ifs = m_pDashboardDataClient->Browse(browseResult.NodeId, @@ -137,7 +133,7 @@ namespace Umati std::lock_guard l(m_dataSetMutex); } else { - LOG(INFO) << "Allready found"; + //LOG(INFO) << "Allready found"; } } } diff --git a/MachineObserver/DashboardMachineObserver.cpp b/MachineObserver/DashboardMachineObserver.cpp index 6f3ec1ca..bf2bf712 100644 --- a/MachineObserver/DashboardMachineObserver.cpp +++ b/MachineObserver/DashboardMachineObserver.cpp @@ -299,7 +299,7 @@ namespace Umati this->modelStructureChangeEvents.pop(); } //LOG(INFO) << "Thread Loop"; - std::this_thread::sleep_for(std::chrono::milliseconds(250)); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } }); func1.detach(); From d52d8b2b42ce4cff17850a757e21fd1d79de8acc Mon Sep 17 00:00:00 2001 From: Matthias Dornaus Date: Mon, 12 Sep 2022 09:24:25 +0200 Subject: [PATCH 05/22] Added Logging. --- DashboardClient/DashboardClient.cpp | 10 +++++++--- MachineObserver/DashboardMachineObserver.cpp | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/DashboardClient/DashboardClient.cpp b/DashboardClient/DashboardClient.cpp index 532c74ad..690cd289 100644 --- a/DashboardClient/DashboardClient.cpp +++ b/DashboardClient/DashboardClient.cpp @@ -93,6 +93,7 @@ namespace Umati browsedSimpleNodes.erase(nodeId); } void DashboardClient::updateAddDataSet(ModelOpcUa::NodeId_t refreshNodeId) { + LOG(INFO) << "Update Add DataSet"; if(!m_dataSets.empty()) { std::shared_ptr dataSet = m_dataSets.front(); auto search = browsedSimpleNodes.find(refreshNodeId); @@ -104,8 +105,10 @@ namespace Umati std::shared_ptr placeholderNode = std::dynamic_pointer_cast(child); if(placeholderNode == nullptr) { } else { + LOG(INFO) << "Start Browsing Jobs"; auto browseResults = m_pDashboardDataClient->Browse(refreshNodeId, child->ReferenceType, child->SpecifiedTypeNodeId); + LOG(INFO) << "End Browsing Jobs"; for (auto &browseResult : browseResults) { if (browseResult.TypeDefinition.Id == NodeId_BaseObjectType.Id) { auto ifs = m_pDashboardDataClient->Browse(browseResult.NodeId, @@ -118,9 +121,8 @@ namespace Umati auto possibleType = m_pTypeReader->m_typeMap->find(browseResult.TypeDefinition); // use subtype if (possibleType != m_pTypeReader->m_typeMap->end()) { - // LOG(INFO) << "Found type for " << typeName; if(browsedNodes.find(browseResult.NodeId) == browsedNodes.end()) { - LOG(INFO) << "Added" << browseResult.NodeId; + LOG(INFO) << "Added Job:" << browseResult.NodeId; auto sharedPossibleType = possibleType->second; ModelOpcUa::PlaceholderElement plElement; plElement.BrowseName = browseResult.BrowseName; @@ -129,11 +131,13 @@ namespace Umati //Const cast std::shared_ptr placeholderNodeUnconst = std::const_pointer_cast(placeholderNode); placeholderNodeUnconst->addInstance(plElement); + LOG(INFO) << "Start Subscription"; subscribeValues(plElement.pNode, dataSet->values, dataSet->values_mutex); std::lock_guard l(m_dataSetMutex); + LOG(INFO) << "End Subcription"; } else { - //LOG(INFO) << "Allready found"; + LOG(INFO) << "Allready found Job"; } } } diff --git a/MachineObserver/DashboardMachineObserver.cpp b/MachineObserver/DashboardMachineObserver.cpp index bf2bf712..9b539b2e 100644 --- a/MachineObserver/DashboardMachineObserver.cpp +++ b/MachineObserver/DashboardMachineObserver.cpp @@ -217,6 +217,7 @@ namespace Umati std::shared_ptr dbc = it -> second; nodeId.Uri = (it -> first).Uri; if(dbc->containsNodeId(nodeId)) { + LOG(INFO) << "ModelChangeEvent"; changedDbcs.push_back(dbc); StructureChangeEvent stc; stc.dbc = dbc; @@ -240,6 +241,7 @@ namespace Umati void DashboardMachineObserver::PublishAll() { + LOG(INFO) << "Publish"; { std::unique_lock ul(m_dashboardClients_mutex); for (const auto &pDashClient : m_dashboardClients) @@ -287,6 +289,7 @@ namespace Umati while (true) { if(!this->modelStructureChangeEvents.empty()) { + LOG(INFO) << "Process ModelChnageEvent"; StructureChangeEvent sce = this->modelStructureChangeEvents.front(); if(sce.nodeAdded || sce.referenceAdded) { sce.dbc->updateAddDataSet(sce.refreshNode); From 5fe6d56b54600d90150e7f3b10a8db62d859c7cf Mon Sep 17 00:00:00 2001 From: Matthias Dornaus Date: Mon, 12 Sep 2022 11:16:43 +0200 Subject: [PATCH 06/22] Increased Timing for update loop. --- DashboardClient/DashboardClient.cpp | 2 +- MachineObserver/DashboardMachineObserver.cpp | 2 +- OpcUaClient/OpcUaClient.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DashboardClient/DashboardClient.cpp b/DashboardClient/DashboardClient.cpp index 690cd289..26b1042b 100644 --- a/DashboardClient/DashboardClient.cpp +++ b/DashboardClient/DashboardClient.cpp @@ -133,7 +133,7 @@ namespace Umati placeholderNodeUnconst->addInstance(plElement); LOG(INFO) << "Start Subscription"; subscribeValues(plElement.pNode, dataSet->values, dataSet->values_mutex); - std::lock_guard l(m_dataSetMutex); + //std::lock_guard l(m_dataSetMutex); LOG(INFO) << "End Subcription"; } else { diff --git a/MachineObserver/DashboardMachineObserver.cpp b/MachineObserver/DashboardMachineObserver.cpp index 9b539b2e..532d99e9 100644 --- a/MachineObserver/DashboardMachineObserver.cpp +++ b/MachineObserver/DashboardMachineObserver.cpp @@ -289,7 +289,7 @@ namespace Umati while (true) { if(!this->modelStructureChangeEvents.empty()) { - LOG(INFO) << "Process ModelChnageEvent"; + LOG(INFO) << "Process ModelChangeEvent"; StructureChangeEvent sce = this->modelStructureChangeEvents.front(); if(sce.nodeAdded || sce.referenceAdded) { sce.dbc->updateAddDataSet(sce.refreshNode); diff --git a/OpcUaClient/OpcUaClient.cpp b/OpcUaClient/OpcUaClient.cpp index 06a4318a..00a360ce 100644 --- a/OpcUaClient/OpcUaClient.cpp +++ b/OpcUaClient/OpcUaClient.cpp @@ -462,8 +462,8 @@ std::string OpcUaClient::readNodeBrowseName(const ModelOpcUa::NodeId_t &_nodeId) UA_NodeClass OpcUaClient::readNodeClass(const open62541Cpp::UA_NodeId &nodeId) { - //LOG(INFO) << "NodeId: " << nodeId.NodeId->identifier.numeric; - //LOG(INFO) << "Uri" << nodeId.NodeId->namespaceIndex; + LOG(INFO) << "NodeId: " << nodeId.NodeId->identifier.numeric; + LOG(INFO) << "Uri" << nodeId.NodeId->namespaceIndex; checkConnection(); UA_NodeClass returnClass; From a77b1132a581359abd1754a9506bf1f5e495e22a Mon Sep 17 00:00:00 2001 From: Matthias Dornaus Date: Mon, 12 Sep 2022 13:31:59 +0200 Subject: [PATCH 07/22] Added mutex on Adding Dataset. --- DashboardClient/DashboardClient.cpp | 1 + DashboardClient/IDashboardDataClient.hpp | 3 +++ OpcUaClient/OpcUaClient.cpp | 4 ++++ OpcUaClient/OpcUaClient.hpp | 12 ++++++------ 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/DashboardClient/DashboardClient.cpp b/DashboardClient/DashboardClient.cpp index 26b1042b..4971c51e 100644 --- a/DashboardClient/DashboardClient.cpp +++ b/DashboardClient/DashboardClient.cpp @@ -123,6 +123,7 @@ namespace Umati { if(browsedNodes.find(browseResult.NodeId) == browsedNodes.end()) { LOG(INFO) << "Added Job:" << browseResult.NodeId; + std::lock_guard l(*(m_pDashboardDataClient->getClientMutex())); auto sharedPossibleType = possibleType->second; ModelOpcUa::PlaceholderElement plElement; plElement.BrowseName = browseResult.BrowseName; diff --git a/DashboardClient/IDashboardDataClient.hpp b/DashboardClient/IDashboardDataClient.hpp index 7d5c0685..2af1246c 100644 --- a/DashboardClient/IDashboardDataClient.hpp +++ b/DashboardClient/IDashboardDataClient.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "NodeIdsWellKnown.hpp" namespace Umati @@ -253,6 +254,8 @@ namespace Umati virtual std::shared_ptr getUaClient() = 0; + virtual std::recursive_mutex* getClientMutex() = 0; + }; } // namespace Dashboard } // namespace Umati diff --git a/OpcUaClient/OpcUaClient.cpp b/OpcUaClient/OpcUaClient.cpp index 00a360ce..43d723d2 100644 --- a/OpcUaClient/OpcUaClient.cpp +++ b/OpcUaClient/OpcUaClient.cpp @@ -1054,6 +1054,10 @@ bool OpcUaClient::VerifyConnection() { { return m_pClient; } + std::recursive_mutex* OpcUaClient::getClientMutex() { + + return &m_clientMutex; + } } // namespace OpcUa } // namespace Umati diff --git a/OpcUaClient/OpcUaClient.hpp b/OpcUaClient/OpcUaClient.hpp index 907659e1..2d0b2b73 100644 --- a/OpcUaClient/OpcUaClient.hpp +++ b/OpcUaClient/OpcUaClient.hpp @@ -170,12 +170,12 @@ class OpcUaClient : public Dashboard::IDashboardDataClient { /// Map for chaching super types. Key = Type, Value = Supertype std::map m_superTypes; - public: - std::shared_ptr m_pClient; // Zugriff aus dem ConnectThread, dem PublisherThread - std::recursive_mutex m_clientMutex; - - private: - void on_connected(); + public: + std::shared_ptr m_pClient; // Zugriff aus dem ConnectThread, dem PublisherThread + std::recursive_mutex* getClientMutex(); + std::recursive_mutex m_clientMutex; + private: + void on_connected(); std::vector readValues2(const std::list &modelNodeIds); From 84747cda2e1e26bf74a4dfaef0573d73491bc629 Mon Sep 17 00:00:00 2001 From: Matthias Dornaus Date: Tue, 13 Sep 2022 09:12:25 +0200 Subject: [PATCH 08/22] Changed back timings removed lock guard. --- DashboardClient/DashboardClient.cpp | 2 +- MachineObserver/DashboardMachineObserver.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DashboardClient/DashboardClient.cpp b/DashboardClient/DashboardClient.cpp index 4971c51e..36337e27 100644 --- a/DashboardClient/DashboardClient.cpp +++ b/DashboardClient/DashboardClient.cpp @@ -123,7 +123,7 @@ namespace Umati { if(browsedNodes.find(browseResult.NodeId) == browsedNodes.end()) { LOG(INFO) << "Added Job:" << browseResult.NodeId; - std::lock_guard l(*(m_pDashboardDataClient->getClientMutex())); + //std::lock_guard l(*(m_pDashboardDataClient->getClientMutex())); auto sharedPossibleType = possibleType->second; ModelOpcUa::PlaceholderElement plElement; plElement.BrowseName = browseResult.BrowseName; diff --git a/MachineObserver/DashboardMachineObserver.cpp b/MachineObserver/DashboardMachineObserver.cpp index 532d99e9..8a67970f 100644 --- a/MachineObserver/DashboardMachineObserver.cpp +++ b/MachineObserver/DashboardMachineObserver.cpp @@ -271,7 +271,7 @@ namespace Umati int cnt = 0; while (this->m_running) { - if ((cnt % 10) == 0) + if ((cnt % 100) == 0 || cnt == 0) { this->UpdateMachines(); } From 397d86ef97e56537a5ffa372bed84de6cc90e405 Mon Sep 17 00:00:00 2001 From: Matthias Dornaus Date: Tue, 13 Sep 2022 15:14:35 +0200 Subject: [PATCH 09/22] Improved Node Cache update on remove. Hotfix cleared Subscriptioncache. --- DashboardClient/DashboardClient.cpp | 39 ++++++++++++++++++++++++----- DashboardClient/DashboardClient.hpp | 2 +- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/DashboardClient/DashboardClient.cpp b/DashboardClient/DashboardClient.cpp index 36337e27..bf0bb992 100644 --- a/DashboardClient/DashboardClient.cpp +++ b/DashboardClient/DashboardClient.cpp @@ -64,6 +64,8 @@ namespace Umati } } } + //Hotfix clear subscription cache. + m_subscribedValues.clear(); } void DashboardClient::deleteAndUnsubscribeNode(ModelOpcUa::PlaceholderElement placeHolderElement) { std::shared_ptr element = placeHolderElement.pNode; @@ -73,7 +75,7 @@ namespace Umati std::shared_ptr simpleNode = std::dynamic_pointer_cast(node); if(simpleNode != nullptr) { const ModelOpcUa::SimpleNode simpleN = *simpleNode; - deleteAndUnsubscribeNode(simpleN.NodeId); + deleteAndUnsubscribeNode(simpleN); } else { std::shared_ptr placeHolderNode = std::dynamic_pointer_cast(node); if(placeHolderNode != nullptr) { @@ -81,16 +83,37 @@ namespace Umati for(std::list::iterator it = instances.begin(); it != instances.end(); it++) { deleteAndUnsubscribeNode(*it); } + } } } + LOG(INFO) << "Erase NodeId :" << element->NodeId; browsedNodes.erase(element->NodeId); browsedSimpleNodes.erase(element->NodeId); } - void DashboardClient::deleteAndUnsubscribeNode(const ModelOpcUa::NodeId_t nodeId) { - browsedNodes.erase(nodeId); - browsedSimpleNodes.erase(nodeId); + void DashboardClient::deleteAndUnsubscribeNode(const ModelOpcUa::SimpleNode simpleNode) { + const std::list> children = simpleNode.ChildNodes; + for(auto it = children.begin(); it != children.end();it++) { + std::shared_ptr node = *it; + std::shared_ptr simpleNode = std::dynamic_pointer_cast(node); + if(simpleNode != nullptr) { + const ModelOpcUa::SimpleNode simpleN = *simpleNode; + deleteAndUnsubscribeNode(simpleN); + } else { + std::shared_ptr placeHolderNode = std::dynamic_pointer_cast(node); + if(placeHolderNode != nullptr) { + std::list instances = placeHolderNode->getInstances(); + for(std::list::iterator it = instances.begin(); it != instances.end(); it++) { + deleteAndUnsubscribeNode(*it); + } + + } + } + } + LOG(INFO) << "Erase NodeId :" << simpleNode.NodeId; + browsedNodes.erase(simpleNode.NodeId); + browsedSimpleNodes.erase(simpleNode.NodeId); } void DashboardClient::updateAddDataSet(ModelOpcUa::NodeId_t refreshNodeId) { LOG(INFO) << "Update Add DataSet"; @@ -351,6 +374,8 @@ namespace Umati break; } } + } else { + LOG(INFO) << "Not Inserted Node: " << startNode; } auto pNode = std::make_shared( startNode, @@ -634,8 +659,10 @@ namespace Umati try { for(auto value : m_subscribedValues){ - if(value && value.get()->getNodeId() == pNode.get()->NodeId) - return; + if(value && value.get()->getNodeId() == pNode.get()->NodeId) { + LOG(INFO) << "Allready Subscribed" << value.get()->getNodeId(); + return; + } } auto subscribedValue = m_pDashboardDataClient->Subscribe(pNode->NodeId, callback); m_subscribedValues.push_back(subscribedValue); diff --git a/DashboardClient/DashboardClient.hpp b/DashboardClient/DashboardClient.hpp index 2c396734..416fd179 100644 --- a/DashboardClient/DashboardClient.hpp +++ b/DashboardClient/DashboardClient.hpp @@ -141,7 +141,7 @@ namespace Umati { const std::shared_ptr &pChild) const; void deleteAndUnsubscribeNode(ModelOpcUa::PlaceholderElement placeHolderElement); - void deleteAndUnsubscribeNode(const ModelOpcUa::NodeId_t nodeId); + void deleteAndUnsubscribeNode(const ModelOpcUa::SimpleNode nodeId); }; } } From 6e05866e7aebd290c22848c26e067f2e022a0914 Mon Sep 17 00:00:00 2001 From: Matthias Dornaus Date: Tue, 28 Feb 2023 08:58:24 +0100 Subject: [PATCH 10/22] Refactored ModelChangedEventhandling. Preparation for Dev merge. --- DashboardClient/DashboardClient.cpp | 17 ++ DashboardClient/DashboardClient.hpp | 1 + DashboardClient/IDashboardDataClient.hpp | 28 +- MachineObserver/DashboardMachineObserver.cpp | 232 +---------------- OpcUaClient/OpcUaClient.cpp | 16 +- OpcUaClient/OpcUaClient.hpp | 5 +- OpcUaClient/OpcUaInterface.hpp | 253 ++++++++++++++++--- 7 files changed, 276 insertions(+), 276 deletions(-) diff --git a/DashboardClient/DashboardClient.cpp b/DashboardClient/DashboardClient.cpp index bf0bb992..1f878839 100644 --- a/DashboardClient/DashboardClient.cpp +++ b/DashboardClient/DashboardClient.cpp @@ -115,6 +115,23 @@ namespace Umati browsedNodes.erase(simpleNode.NodeId); browsedSimpleNodes.erase(simpleNode.NodeId); } + void DashboardClient::subscribeEvents() { + auto ecbf = [](IDashboardDataClient::StructureChangeEvent sce, void* context) { + DashboardClient* pDashboardClient = static_cast (context); + if(pDashboardClient != nullptr) { + LOG(INFO) << "Event Callback" << pDashboardClient; + if(sce.nodeAdded || sce.referenceAdded) { + pDashboardClient->updateAddDataSet(sce.refreshNode); + pDashboardClient->Publish(); + } + if(sce.nodeDeleted || sce.referenceDeleted) { + pDashboardClient->updateDeleteDataSet(sce.refreshNode); + pDashboardClient->Publish(); + } + } + }; + m_pDashboardDataClient->SubscribeEvent(ecbf, this); + } void DashboardClient::updateAddDataSet(ModelOpcUa::NodeId_t refreshNodeId) { LOG(INFO) << "Update Add DataSet"; if(!m_dataSets.empty()) { diff --git a/DashboardClient/DashboardClient.hpp b/DashboardClient/DashboardClient.hpp index 416fd179..f5ec0212 100644 --- a/DashboardClient/DashboardClient.hpp +++ b/DashboardClient/DashboardClient.hpp @@ -51,6 +51,7 @@ namespace Umati { void Publish(); void Unsubscribe(ModelOpcUa::NodeId_t nodeId); + void subscribeEvents(); bool containsNodeId(ModelOpcUa::NodeId_t nodeId); void updateAddDataSet(ModelOpcUa::NodeId_t nodeId); diff --git a/DashboardClient/IDashboardDataClient.hpp b/DashboardClient/IDashboardDataClient.hpp index 2af1246c..b1134fdb 100644 --- a/DashboardClient/IDashboardDataClient.hpp +++ b/DashboardClient/IDashboardDataClient.hpp @@ -27,7 +27,22 @@ namespace Umati class IDashboardDataClient { public: + /// Struct for handling Events + struct StructureChangeEvent{ + ModelOpcUa::NodeId_t refreshNode; + bool nodeAdded = false; + bool nodeDeleted = false; + bool referenceAdded = false; + bool referenceDeleted = false; + bool dataTypeChanged = false; + }; + + /*! \brief Callback function for new Value Data Callback. + * + * This callback is triggered if a the data of an item changes. + */ typedef std::function newValueCallbackFunction_t; + typedef std::function eventCallbackFunction_t; virtual ~IDashboardDataClient() = default; @@ -235,16 +250,23 @@ namespace Umati virtual void buildCustomDataTypes() = 0; + class EventSubscriptionHandle { + public: + EventSubscriptionHandle(){}; + ~EventSubscriptionHandle(){}; + }; + virtual std::string readNodeBrowseName(const ModelOpcUa::NodeId_t &nodeId) = 0; /// \todo Extract from interface! virtual std::string getTypeName(const ModelOpcUa::NodeId_t &nodeId) = 0; - virtual std::shared_ptr - Subscribe(ModelOpcUa::NodeId_t nodeId, newValueCallbackFunction_t callback) = 0; - + virtual std::shared_ptr Subscribe(ModelOpcUa::NodeId_t nodeId, newValueCallbackFunction_t callback) = 0; virtual void Unsubscribe(std::vector monItemIds, std::vector clientHandles) = 0; + virtual std::shared_ptr SubscribeEvent(eventCallbackFunction_t ecbf, void* context) = 0; + virtual void UnsubscribeEvent() = 0; + virtual std::vector ReadeNodeValues(std::list nodeIds) = 0; virtual std::vector Namespaces() = 0; diff --git a/MachineObserver/DashboardMachineObserver.cpp b/MachineObserver/DashboardMachineObserver.cpp index 8a67970f..93e1f70f 100644 --- a/MachineObserver/DashboardMachineObserver.cpp +++ b/MachineObserver/DashboardMachineObserver.cpp @@ -23,150 +23,6 @@ namespace Umati { namespace MachineObserver { - static DashboardMachineObserver* dbmo; - - static void handler_events(UA_Client *client, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext, size_t nEventFields, UA_Variant *eventFields) { - UA_NodeId* sourceNodeId; - UA_NodeId affectedNode; - UA_NodeId affectedType; - UA_Byte verb; - for(size_t i = 0; i < nEventFields; ++i) { - UA_Variant eventField = eventFields[i]; - if(UA_Variant_hasScalarType(&eventField, &UA_TYPES[UA_TYPES_NODEID])) { - UA_NodeId* nodeId = (UA_NodeId *)eventField.data; - if(i == 1) { - sourceNodeId = nodeId; - } - } else { - UA_ExtensionObject* extensionObjects = (UA_ExtensionObject*)eventField.data; - UA_ModelChangeStructureDataType modelChangeStructureDataTypes[eventField.arrayLength]; - for(int j = 0; j < eventField.arrayLength; j ++) { - UA_ExtensionObject extensionObject = extensionObjects[j]; - UA_ModelChangeStructureDataType* modelChangeStructureDataType =(UA_ModelChangeStructureDataType*)extensionObject.content.decoded.data; - modelChangeStructureDataTypes[j] = *modelChangeStructureDataType; - } - dbmo -> updateAfterModelChangeEvent(modelChangeStructureDataTypes, eventField.arrayLength); - } - } - - - } - - void DashboardMachineObserver::AddSubscription() - { - dbmo = this; - std::shared_ptr client = m_pDataClient->getUaClient(); - UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default(); - UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client.get(), request,NULL, NULL, NULL); - if(response.responseHeader.serviceResult == UA_STATUSCODE_GOOD) - { - LOG(INFO) << "###################################### Created Subscription #####################################"; - UA_UInt32 subId = response.subscriptionId; - UA_MonitoredItemCreateRequest item; - UA_MonitoredItemCreateRequest_init(&item); - item.itemToMonitor.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER; - item.monitoringMode = UA_MONITORINGMODE_REPORTING; - //Allways Subscribe to the Serer object - item.itemToMonitor.nodeId = UA_NODEID_NUMERIC(0, 2253); // Root->Objects->Server - - - - UA_EventFilter filter; - UA_EventFilter_init(&filter); - //Setup selection Clauses for Filter - int nSelectClauses = 3; - UA_SimpleAttributeOperand *selectClauses = (UA_SimpleAttributeOperand*) - UA_Array_new(nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]); - - - - for(size_t i =0; i pDataClient, std::shared_ptr pPublisher, @@ -175,8 +31,6 @@ namespace Umati :MachineObserver(std::move(pDataClient), std::move(pOpcUaTypeReader), std::move(machinesFilter)), m_pPublisher(std::move(pPublisher)) { - startEventThread(); - AddSubscription(); startUpdateMachineThread(); } @@ -185,60 +39,6 @@ namespace Umati stopMachineUpdateThread(); } - void DashboardMachineObserver::updateAfterModelChangeEvent(UA_ModelChangeStructureDataType* modelChangeStructureDataTypes, size_t nModelChangeStructureDataTypes) { - bool nodeAdded = false; - bool nodeDeleted = false; - bool referenceAdded = false; - bool referenceDeleted = false; - bool dataTypeChanged = false; - std::list> changedDbcs = std::list>(); - for(int i = 0; i < nModelChangeStructureDataTypes; i++) { - UA_ModelChangeStructureDataType modelChangeStructureDataType = modelChangeStructureDataTypes[i]; - UA_NodeId affectedNode = modelChangeStructureDataType.affected; - UA_NodeId affectedType = modelChangeStructureDataType.affectedType; - UA_Byte verb = modelChangeStructureDataType.verb; - - if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED) {nodeAdded = true;} - if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED) {nodeDeleted = true;} - if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED) {referenceAdded = true;} - if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED) {referenceDeleted = true;} - if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED) == UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED) {dataTypeChanged = true;} - - /* Find the Dashboardclient for the event. Check uris with namespaces.*/ - ModelOpcUa::NodeId_t typeId = ModelOpcUa::NodeId_t(); - typeId.Id = "i=" + std::to_string(affectedType.identifier.numeric); - affectedType.namespaceIndex; - std::vector namespaces = m_pDataClient->Namespaces(); - if(affectedType.namespaceIndex < namespaces.size()) { - typeId.Uri = namespaces[affectedType.namespaceIndex]; - ModelOpcUa::NodeId_t nodeId = ModelOpcUa::NodeId_t(); - nodeId.Id = "i=" + std::to_string(affectedNode.identifier.numeric); - for(auto it = m_dashboardClients.begin(); it != m_dashboardClients.end(); it++) { - std::shared_ptr dbc = it -> second; - nodeId.Uri = (it -> first).Uri; - if(dbc->containsNodeId(nodeId)) { - LOG(INFO) << "ModelChangeEvent"; - changedDbcs.push_back(dbc); - StructureChangeEvent stc; - stc.dbc = dbc; - stc.refreshNode = nodeId; - stc.nodeAdded = nodeAdded; - stc.nodeDeleted = nodeDeleted; - stc.referenceAdded = referenceAdded; - stc.referenceDeleted = referenceDeleted; - stc.dataTypeChanged = dataTypeChanged; - this->modelStructureChangeEvents.push(stc); - } - } - } - } - //Publish DataboardClients that have changes - /*for(std::shared_ptr dbc : changedDbcs) { - std::unique_lock ul(m_dashboardClients_mutex); - dbc->Publish(); - }*/ - } - void DashboardMachineObserver::PublishAll() { LOG(INFO) << "Publish"; @@ -283,30 +83,6 @@ namespace Umati m_running = true; m_updateMachineThread = std::thread(func); } - void DashboardMachineObserver::startEventThread() - { - std::thread func1 ([this]() { - while (true) - { - if(!this->modelStructureChangeEvents.empty()) { - LOG(INFO) << "Process ModelChangeEvent"; - StructureChangeEvent sce = this->modelStructureChangeEvents.front(); - if(sce.nodeAdded || sce.referenceAdded) { - sce.dbc->updateAddDataSet(sce.refreshNode); - sce.dbc->Publish(); - } - if(sce.nodeDeleted || sce.referenceDeleted) { - sce.dbc->updateDeleteDataSet(sce.refreshNode); - sce.dbc->Publish(); - } - this->modelStructureChangeEvents.pop(); - } - //LOG(INFO) << "Thread Loop"; - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - }); - func1.detach(); - } void DashboardMachineObserver::stopMachineUpdateThread() { @@ -358,12 +134,6 @@ namespace Umati { try { - if(machine.NodeId == ModelOpcUa::NodeId_t{"10.80.0.113_4840_http://samplemanufacturer.com/umati_OPC40077_sample_instance/","i=5002"} || - machine.NodeId == ModelOpcUa::NodeId_t{"10.80.0.113_4840_http://samplemanufacturer.com/umati_OPC40077_sample_instance","i=6013"}) - { - LOG(INFO) << "Ignoring Machine: " << machine.BrowseName.Name; - return; - } LOG(INFO) << "New Machine: " << machine.BrowseName.Name << " NodeId:" << static_cast(machine.NodeId); @@ -399,6 +169,8 @@ namespace Umati Topics::Machine(p_type, static_cast(machine.NodeId)), Topics::OnlineStatus(static_cast(machine.NodeId))); + pDashClient->subscribeEvents(); + LOG(INFO) << "Read model finished"; { diff --git a/OpcUaClient/OpcUaClient.cpp b/OpcUaClient/OpcUaClient.cpp index 43d723d2..4d4410f3 100644 --- a/OpcUaClient/OpcUaClient.cpp +++ b/OpcUaClient/OpcUaClient.cpp @@ -972,10 +972,18 @@ std::shared_ptr OpcUaC return nullptr; } -void OpcUaClient::Unsubscribe(std::vector monItemIds, std::vector clientHandles) { - std::lock_guard l(m_clientMutex); - m_opcUaWrapper->SubscriptionUnsubscribe(m_pClient.get(), monItemIds, clientHandles); -} + void OpcUaClient::Unsubscribe(std::vector monItemIds, std::vector clientHandles){ + + std::lock_guard l(m_clientMutex); + m_opcUaWrapper->SubscriptionUnsubscribe(m_pClient.get(), monItemIds, clientHandles); + } + std::shared_ptr OpcUaClient::SubscribeEvent(IDashboardDataClient::eventCallbackFunction_t ecbf, void* context) { + return m_opcUaWrapper->EventSubscribe(m_pClient.get(), ecbf, context); + } + + void OpcUaClient::UnsubscribeEvent() { + m_opcUaWrapper->EventUnsubscribe(m_pClient.get()); + } std::vector OpcUaClient::ReadeNodeValues(std::list modelNodeIds) { return readValues2(modelNodeIds); } diff --git a/OpcUaClient/OpcUaClient.hpp b/OpcUaClient/OpcUaClient.hpp index 2d0b2b73..c0785bc2 100644 --- a/OpcUaClient/OpcUaClient.hpp +++ b/OpcUaClient/OpcUaClient.hpp @@ -62,7 +62,10 @@ class OpcUaClient : public Dashboard::IDashboardDataClient { std::shared_ptr Subscribe(ModelOpcUa::NodeId_t nodeId, newValueCallbackFunction_t callback) override; - void Unsubscribe(std::vector monItemIds, std::vector clientHandle) override; + void Unsubscribe(std::vectormonItemIds, std::vector clientHandle) override; + + std::shared_ptr SubscribeEvent(IDashboardDataClient::eventCallbackFunction_t eccbf, void* context) override; + void UnsubscribeEvent() override; std::vector ReadeNodeValues(std::list modelNodeIds) override; diff --git a/OpcUaClient/OpcUaInterface.hpp b/OpcUaClient/OpcUaInterface.hpp index 94f3f7ad..cf1e7623 100644 --- a/OpcUaClient/OpcUaInterface.hpp +++ b/OpcUaClient/OpcUaInterface.hpp @@ -16,11 +16,12 @@ #include "Subscription.hpp" namespace Umati { -namespace OpcUa { -class OpcUaInterface { - public: - virtual UA_StatusCode DiscoveryGetEndpoints( - UA_Client *client, const open62541Cpp::UA_String *sDiscoveryURL, size_t *endpointDescriptionsSize, UA_EndpointDescription **endpointDescriptions) = 0; + namespace OpcUa { + class OpcUaInterface { + + public: + + static Umati::Dashboard::IDashboardDataClient::eventCallbackFunction_t s_eventcallback; virtual UA_StatusCode DiscoveryFindServers( UA_Client *client, const open62541Cpp::UA_String &sDiscoveryURL, size_t *registerdServerSize, UA_ApplicationDescription **applicationDescriptions) = 0; @@ -66,30 +67,41 @@ class OpcUaInterface { virtual void SubscriptionUnsubscribe(UA_Client *client, std::vector monItemIds, std::vector clientHandles) = 0; - protected: - std::vector namespaceArray; - Subscription *p_subscr; -}; - -class OpcUaWrapper : public OpcUaInterface { - public: - UA_StatusCode DiscoveryGetEndpoints( - UA_Client *client, const open62541Cpp::UA_String *sDiscoveryURL, size_t *endpointDescriptionsSize, UA_EndpointDescription **endpointDescriptions) override { - return UA_Client_getEndpoints(client, static_cast(*sDiscoveryURL).c_str(), endpointDescriptionsSize, endpointDescriptions); - } - - UA_StatusCode DiscoveryFindServers( - UA_Client *client, - const open62541Cpp::UA_String &sDiscoveryURL, - size_t *registerdServerSize, - UA_ApplicationDescription **applicationDescriptions) override { - return UA_Client_findServers( - client, (char *)sDiscoveryURL.String->data, sDiscoveryURL.String->length, nullptr, 0, nullptr, registerdServerSize, applicationDescriptions); - } - - UA_StatusCode SessionConnect(UA_Client *client, const open62541Cpp::UA_String &sURL) override { - return UA_Client_connect(client, static_cast(sURL).c_str()); - } + virtual std::shared_ptr EventSubscribe(UA_Client* client, + Dashboard::IDashboardDataClient::eventCallbackFunction_t eventcallback, void* context) = 0; + virtual void EventUnsubscribe(UA_Client* client)= 0; + + protected: + std::vector namespaceArray; + Subscription *p_subscr; + Dashboard::IDashboardDataClient::eventCallbackFunction_t eventcallback; + void* context; + }; + + class OpcUaWrapper : public OpcUaInterface { + + + public: + + UA_StatusCode DiscoveryGetEndpoints(UA_Client *client, const open62541Cpp::UA_String *sDiscoveryURL, + size_t *endpointDescriptionsSize, + UA_EndpointDescription **endpointDescriptions) override { + return UA_Client_getEndpoints(client, static_cast(*sDiscoveryURL).c_str() , endpointDescriptionsSize, endpointDescriptions); + }; + + UA_StatusCode DiscoveryFindServers(UA_Client *client, const open62541Cpp::UA_String &sDiscoveryURL, + size_t *registerdServerSize, + UA_ApplicationDescription **applicationDescriptions) override { + + return UA_Client_findServers(client,(char *)sDiscoveryURL.String->data,sDiscoveryURL.String->length, nullptr, 0, nullptr, registerdServerSize, + applicationDescriptions); + }; + + UA_StatusCode SessionConnect( + UA_Client *client, + const open62541Cpp::UA_String &sURL) override { + return UA_Client_connect(client, static_cast(sURL).c_str()); + } UA_StatusCode SessionConnectUsername(UA_Client *client, const open62541Cpp::UA_String &sURL, std::string username, std::string password) override { return UA_Client_connectUsername(client, static_cast(sURL).c_str(), username.c_str(), password.c_str()); @@ -218,14 +230,179 @@ class OpcUaWrapper : public OpcUaInterface { } } - void SubscriptionUnsubscribe(UA_Client *client, std::vector monItemIds, std::vector clientHandles) { - p_subscr->Unsubscribe(client, monItemIds, clientHandles); - } - - UA_SessionState m_pSessionState; - UA_SecureChannelState m_pChannelState; -}; -} // namespace OpcUa -} // namespace Umati + void SubscriptionUnsubscribe(UA_Client *client, std::vector monItemIds, std::vector clientHandles){ + p_subscr->Unsubscribe(client, monItemIds, clientHandles); + } + + static void handler_events(UA_Client *client, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext, size_t nEventFields, UA_Variant *eventFields) { + UA_NodeId* sourceNodeId; + UA_NodeId affectedNode; + UA_NodeId affectedType; + UA_Byte verb; + for(size_t i = 0; i < nEventFields; ++i) { + UA_Variant eventField = eventFields[i]; + if(UA_Variant_hasScalarType(&eventField, &UA_TYPES[UA_TYPES_NODEID])) { + UA_NodeId* nodeId = (UA_NodeId *)eventField.data; + if(i == 1) { + sourceNodeId = nodeId; + } + } else { + UA_ExtensionObject* extensionObjects = (UA_ExtensionObject*)eventField.data; + UA_ModelChangeStructureDataType modelChangeStructureDataTypes[eventField.arrayLength]; + for(int j = 0; j < eventField.arrayLength; j ++) { + UA_ExtensionObject extensionObject = extensionObjects[j]; + UA_ModelChangeStructureDataType* modelChangeStructureDataType =(UA_ModelChangeStructureDataType*)extensionObject.content.decoded.data; + modelChangeStructureDataTypes[j] = *modelChangeStructureDataType; + } + OpcUaWrapper* pWrapper = static_cast (monContext); + if(pWrapper != nullptr && pWrapper->eventcallback != nullptr) { + bool nodeAdded = false; + bool nodeDeleted = false; + bool referenceAdded = false; + bool referenceDeleted = false; + bool dataTypeChanged = false; + for(int i = 0; i < eventField.arrayLength; i++) { + UA_ModelChangeStructureDataType modelChangeStructureDataType = modelChangeStructureDataTypes[i]; + UA_NodeId affectedNode = modelChangeStructureDataType.affected; + UA_NodeId affectedType = modelChangeStructureDataType.affectedType; + UA_Byte verb = modelChangeStructureDataType.verb; + if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED) {nodeAdded = true;} + if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED) {nodeDeleted = true;} + if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED) {referenceAdded = true;} + if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED) {referenceDeleted = true;} + if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED) == UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED) {dataTypeChanged = true;} + Umati::Dashboard::IDashboardDataClient::StructureChangeEvent stc; + ModelOpcUa::NodeId_t nodeId = ModelOpcUa::NodeId_t(); + nodeId.Id = "i=" + std::to_string(affectedNode.identifier.numeric); + stc.refreshNode = nodeId; + stc.nodeAdded = nodeAdded; + stc.nodeDeleted = nodeDeleted; + stc.referenceAdded = referenceAdded; + stc.referenceDeleted = referenceDeleted; + stc.dataTypeChanged = dataTypeChanged; + pWrapper->eventcallback(stc, pWrapper->context); + } + } else { + LOG(ERROR) << "Unable to propagate Event callback!"; + } + } + } + } + std::shared_ptr EventSubscribe(UA_Client* client, + Dashboard::IDashboardDataClient::eventCallbackFunction_t eventcallback, void* context) { + this->eventcallback = eventcallback; + this->context = context; + UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default(); + UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL); + if(response.responseHeader.serviceResult == UA_STATUSCODE_GOOD) { + LOG(INFO) << "###################################### Created Event Subscription #####################################"; + UA_UInt32 subId = response.subscriptionId; + UA_MonitoredItemCreateRequest item; + UA_MonitoredItemCreateRequest_init(&item); + item.itemToMonitor.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER; + item.monitoringMode = UA_MONITORINGMODE_REPORTING; + item.itemToMonitor.nodeId = UA_NODEID_NUMERIC(0, 2253); // Root->Objects->Server + + UA_EventFilter filter; + UA_EventFilter_init(&filter); + + //Setup selection Clauses for Filter + int nSelectClauses = 3; + UA_SimpleAttributeOperand *selectClauses = (UA_SimpleAttributeOperand*) + UA_Array_new(nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]); + + for(size_t i =0; i Date: Tue, 7 Mar 2023 08:31:16 +0100 Subject: [PATCH 11/22] Restored configuration.json.example file. --- configuration.json.example | 223 ++++++++++++++++++++++++++++++++++++- 1 file changed, 219 insertions(+), 4 deletions(-) diff --git a/configuration.json.example b/configuration.json.example index 70f0b8ca..aa5b2cbb 100644 --- a/configuration.json.example +++ b/configuration.json.example @@ -18,9 +18,9 @@ }, "MachinesFilter": [ { - "Uri": "de.uni-stuttgart.isw.glas.sampleserver", - "Id": "i=58192", - "$comment": "GlassMachineType" + "Uri": "http://example.com/BasicMachineTool/", + "Id": "i=66382", + "$comment": "BasicMachineTool" } ], "ObjectTypeNamespaces": [ @@ -337,13 +337,228 @@ }, { "Namespace": "http://opcfoundation.org/UA/Glass/Flat/", - "Types": [ + "Types": [ { "Uri": "http://opcfoundation.org/UA/Glass/Flat/", "Id": "i=1015", "$comment": "GlassMachineType" } ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/MachineTool/", + "Id": "i=11", + "$comment": "MachineToolIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/Woodworking/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/Woodworking/", + "Id": "i=2", + "$comment": "WwMachineType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1009", + "$comment": "SurfaceTechnologCoatingSystemType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1006", + "$comment": "SurfaceTecnologyObjectType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1010", + "$comment": "SurfaceTechnologyDosingSystemType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1011", + "$comment": "SurfaceTechnologyFilterSystemType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1005", + "$comment": "SurfaceTechnologyPipeType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1008", + "$comment": "SurfaceTechnologyPumpType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1004", + "$comment": "SurfaceTechnologyValveType" + }, + { + "Uri": "http://opcfoundation.org/UA/SurfaceTechnology/", + "Id": "i=1003", + "$comment": "SurfaceTechnologyVesselType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/GMS/", + "Types": [ + { + "Uri": "http://opcfoundation.org/GMS/", + "Id": "i=1002", + "$comment": "GMSType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/GMS/", + "Id": "i=1011", + "$comment": "GMSIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/umati/generic/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/umati/generic/", + "Id": "i=1002", + "$comment": "UmatiPlasticsRubberGenericType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/IMM2MES/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/IMM2MES/", + "Id": "i=1007", + "$comment": "IMM_MES_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Extruder/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Extruder/", + "Id": "i=1015", + "$comment": "Extruder_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Corrugator/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/Extrusion_v2/Corrugator/", + "Id": "i=1003", + "$comment": "Corrugator_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/LDS/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/LDS/", + "Id": "i=1007", + "$comment": "LDS_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/HotRunner/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/HotRunner/", + "Id": "i=1010", + "$comment": "HRD_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/TCD/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/TCD/", + "Id": "i=1012", + "$comment": "TCD_InterfaceType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/PlasticsRubber/umati/OPC40079ForUmati/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/PlasticsRubber/umati/OPC40079ForUmati/", + "Id": "i=1003", + "$comment": "ImmRobotCellType" + } + ], + "IdentificationType": { + "Uri": "http://opcfoundation.org/UA/Machinery/", + "Id": "i=1012", + "$comment": "MachineIdentificationType" + } + }, + { + "Namespace": "http://opcfoundation.org/UA/Glass/Flat/", + "Types": [ + { + "Uri": "http://opcfoundation.org/UA/MachineTool/", + "Id": "i=13", + "$comment": "MachineToolType" + } + ], "IdentificationType": { "Uri": "http://opcfoundation.org/UA/Glass/Flat/", "Id": "i=1020", From 51f8c90bdd43150094b5f7a3e53e4d31deaf4965 Mon Sep 17 00:00:00 2001 From: mdornaus Date: Mon, 13 Mar 2023 13:31:06 +0100 Subject: [PATCH 12/22] Removed unused Pointer. --- OpcUaClient/OpcUaClient.cpp | 4 ---- OpcUaClient/OpcUaClient.hpp | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/OpcUaClient/OpcUaClient.cpp b/OpcUaClient/OpcUaClient.cpp index 4d4410f3..e3e2d640 100644 --- a/OpcUaClient/OpcUaClient.cpp +++ b/OpcUaClient/OpcUaClient.cpp @@ -1058,10 +1058,6 @@ bool OpcUaClient::VerifyConnection() { return true; } - std::shared_ptr OpcUaClient::getUaClient() - { - return m_pClient; - } std::recursive_mutex* OpcUaClient::getClientMutex() { return &m_clientMutex; diff --git a/OpcUaClient/OpcUaClient.hpp b/OpcUaClient/OpcUaClient.hpp index c0785bc2..cdd9606a 100644 --- a/OpcUaClient/OpcUaClient.hpp +++ b/OpcUaClient/OpcUaClient.hpp @@ -86,7 +86,7 @@ class OpcUaClient : public Dashboard::IDashboardDataClient { void updateCustomTypes() override; - std::shared_ptr getUaClient() override; + protected: void connectionStatusChanged(UA_Int32 clientConnectionId, UA_ServerState serverStatus); From 46cd1b0e863151c3394f3397baeabf494827e59d Mon Sep 17 00:00:00 2001 From: mdornaus Date: Wed, 15 Mar 2023 13:36:35 +0100 Subject: [PATCH 13/22] Refactored Comments. --- DashboardClient/DashboardClient.cpp | 191 +++++++++---------- DashboardClient/DashboardClient.hpp | 2 +- DashboardClient/IDashboardDataClient.hpp | 23 ++- DashboardOpcUaClient.hpp | 1 - MachineObserver/DashboardMachineObserver.cpp | 4 +- ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp | 9 +- OpcUaClient/OpcUaClient.cpp | 15 +- OpcUaClient/OpcUaClient.hpp | 4 +- OpcUaClient/OpcUaInterface.hpp | 73 ++++--- 9 files changed, 158 insertions(+), 164 deletions(-) diff --git a/DashboardClient/DashboardClient.cpp b/DashboardClient/DashboardClient.cpp index 1f878839..be00f087 100644 --- a/DashboardClient/DashboardClient.cpp +++ b/DashboardClient/DashboardClient.cpp @@ -35,33 +35,28 @@ namespace Umati if(!childNodes.empty()) { auto child = childNodes.front(); std::shared_ptr placeholderNode = std::dynamic_pointer_cast(child); - if(placeholderNode == nullptr) { - } else { - auto browseResults = m_pDashboardDataClient->Browse(refreshNodeId, child->ReferenceType, child->SpecifiedTypeNodeId); - //Check if Instances are still in browsresult - std::list instances = placeholderNode->getInstances(); - std::list missingElements; - for(std::list::iterator it = instances.begin(); it != instances.end(); it++) { - bool found = false; - ModelOpcUa::NodeId_t nodeId = it->pNode->NodeId; - for(auto &browseResult : browseResults) { - if(browseResult.NodeId == nodeId) { - found = true; - break; - } - } - if(!found) { - LOG(INFO) << "Deleted Node: " << it -> pNode ->NodeId; - missingElements.push_back(*it); - } + if(placeholderNode == nullptr) { return; } + auto browseResults = m_pDashboardDataClient->Browse(refreshNodeId, child->ReferenceType, child->SpecifiedTypeNodeId); + //Check if Instances are still in browsresult + std::list instances = placeholderNode->getInstances(); + std::list missingElements; + for(auto& el : instances) { + bool found = false; + ModelOpcUa::NodeId_t nodeId = el.pNode->NodeId; + if(std::any_of(browseResults.begin(), browseResults.end(),[nodeId](ModelOpcUa::BrowseResult_t br){ return br.NodeId == nodeId;})) { + found = true; + break; } - //Remove and Unsubscribe the elements - for(std::list::iterator it = missingElements.begin(); it != missingElements.end();it++) { - std::shared_ptr placeholderNodeUnconst = std::const_pointer_cast(placeholderNode); - placeholderNodeUnconst->removeInstance(*it); - deleteAndUnsubscribeNode(*it); + if(!found) { + missingElements.push_back(el); } } + //Remove and Unsubscribe the elements + for(auto& el : missingElements) { + std::shared_ptr placeholderNodeUnconst = std::const_pointer_cast(placeholderNode); + placeholderNodeUnconst->removeInstance(el); + deleteAndUnsubscribeNode(el); + } } } //Hotfix clear subscription cache. @@ -70,32 +65,28 @@ namespace Umati void DashboardClient::deleteAndUnsubscribeNode(ModelOpcUa::PlaceholderElement placeHolderElement) { std::shared_ptr element = placeHolderElement.pNode; const std::list> children = element->ChildNodes; - for(auto it = children.begin(); it != children.end();it++) { - std::shared_ptr node = *it; + for(auto& el : children) { + std::shared_ptr node = el; std::shared_ptr simpleNode = std::dynamic_pointer_cast(node); if(simpleNode != nullptr) { const ModelOpcUa::SimpleNode simpleN = *simpleNode; deleteAndUnsubscribeNode(simpleN); } else { std::shared_ptr placeHolderNode = std::dynamic_pointer_cast(node); - if(placeHolderNode != nullptr) { - std::list instances = placeHolderNode->getInstances(); - for(std::list::iterator it = instances.begin(); it != instances.end(); it++) { - deleteAndUnsubscribeNode(*it); - } - + if(placeHolderNode == nullptr) { continue;} + std::list instances = placeHolderNode->getInstances(); + for(auto& el : instances) { + deleteAndUnsubscribeNode(el); } } } - LOG(INFO) << "Erase NodeId :" << element->NodeId; browsedNodes.erase(element->NodeId); browsedSimpleNodes.erase(element->NodeId); } void DashboardClient::deleteAndUnsubscribeNode(const ModelOpcUa::SimpleNode simpleNode) { const std::list> children = simpleNode.ChildNodes; - for(auto it = children.begin(); it != children.end();it++) { - std::shared_ptr node = *it; + for(auto node : children) { std::shared_ptr simpleNode = std::dynamic_pointer_cast(node); if(simpleNode != nullptr) { const ModelOpcUa::SimpleNode simpleN = *simpleNode; @@ -111,76 +102,76 @@ namespace Umati } } } - LOG(INFO) << "Erase NodeId :" << simpleNode.NodeId; browsedNodes.erase(simpleNode.NodeId); browsedSimpleNodes.erase(simpleNode.NodeId); } void DashboardClient::subscribeEvents() { - auto ecbf = [](IDashboardDataClient::StructureChangeEvent sce, void* context) { - DashboardClient* pDashboardClient = static_cast (context); - if(pDashboardClient != nullptr) { - LOG(INFO) << "Event Callback" << pDashboardClient; - if(sce.nodeAdded || sce.referenceAdded) { - pDashboardClient->updateAddDataSet(sce.refreshNode); - pDashboardClient->Publish(); - } - if(sce.nodeDeleted || sce.referenceDeleted) { - pDashboardClient->updateDeleteDataSet(sce.refreshNode); - pDashboardClient->Publish(); + auto ecbf = [this](IDashboardDataClient::StructureChangeEvent sce) { + if(sce.nodeAdded || sce.referenceAdded) { + std::thread t([this, sce](){ + this->updateAddDataSet(sce.refreshNode); + this->Publish(); + }); + t.detach(); + } + if(sce.nodeDeleted || sce.referenceDeleted) { + std::thread t([this, sce](){ + this -> updateDeleteDataSet(sce.refreshNode); + if(sce.nodeDeleted == true) { + auto search = browsedSimpleNodes.find(sce.refreshNode); + if(search != browsedSimpleNodes.end()) { + std::shared_ptr simpleNode = search->second; + this->deleteAndUnsubscribeNode(*simpleNode); + } } + this -> Publish(); + }); + t.detach(); } }; - m_pDashboardDataClient->SubscribeEvent(ecbf, this); + m_pDashboardDataClient->SubscribeEvent(ecbf); } void DashboardClient::updateAddDataSet(ModelOpcUa::NodeId_t refreshNodeId) { LOG(INFO) << "Update Add DataSet"; - if(!m_dataSets.empty()) { - std::shared_ptr dataSet = m_dataSets.front(); - auto search = browsedSimpleNodes.find(refreshNodeId); - if( search != browsedSimpleNodes.end()) { - std::shared_ptr simpleNode = search->second; - auto childNodes = simpleNode->ChildNodes; - if(!childNodes.empty()) { - auto child = childNodes.front(); - std::shared_ptr placeholderNode = std::dynamic_pointer_cast(child); - if(placeholderNode == nullptr) { - } else { - LOG(INFO) << "Start Browsing Jobs"; - auto browseResults = m_pDashboardDataClient->Browse(refreshNodeId, child->ReferenceType, - child->SpecifiedTypeNodeId); - LOG(INFO) << "End Browsing Jobs"; - for (auto &browseResult : browseResults) { - if (browseResult.TypeDefinition.Id == NodeId_BaseObjectType.Id) { - auto ifs = m_pDashboardDataClient->Browse(browseResult.NodeId, - Dashboard::IDashboardDataClient::BrowseContext_t::HasInterface()); - browseResult.TypeDefinition = ifs.front().NodeId; - LOG(INFO) << "Updated TypeDefinition of " << browseResult.BrowseName.Name << " to " << browseResult.TypeDefinition - << " because the node implements an interface"; - } - - auto possibleType = m_pTypeReader->m_typeMap->find(browseResult.TypeDefinition); // use subtype - if (possibleType != m_pTypeReader->m_typeMap->end()) - { - if(browsedNodes.find(browseResult.NodeId) == browsedNodes.end()) { - LOG(INFO) << "Added Job:" << browseResult.NodeId; - //std::lock_guard l(*(m_pDashboardDataClient->getClientMutex())); - auto sharedPossibleType = possibleType->second; - ModelOpcUa::PlaceholderElement plElement; - plElement.BrowseName = browseResult.BrowseName; - plElement.pNode = TransformToNodeIds(browseResult.NodeId, sharedPossibleType); - plElement.TypeDefinition = browseResult.TypeDefinition; - //Const cast - std::shared_ptr placeholderNodeUnconst = std::const_pointer_cast(placeholderNode); - placeholderNodeUnconst->addInstance(plElement); - LOG(INFO) << "Start Subscription"; - subscribeValues(plElement.pNode, dataSet->values, dataSet->values_mutex); - //std::lock_guard l(m_dataSetMutex); - LOG(INFO) << "End Subcription"; - } - else { - LOG(INFO) << "Allready found Job"; - } - } + if(m_dataSets.empty()) { + return; + } + std::shared_ptr dataSet = m_dataSets.front(); + auto search = browsedSimpleNodes.find(refreshNodeId); + if(search == browsedSimpleNodes.end()) { + return; + } + std::shared_ptr simpleNode = search->second; + auto childNodes = simpleNode->ChildNodes; + if(!childNodes.empty()) { + auto child = childNodes.front(); + std::shared_ptr placeholderNode = std::dynamic_pointer_cast(child); + if(placeholderNode == nullptr) { + } else { + auto browseResults = m_pDashboardDataClient->Browse(refreshNodeId, child->ReferenceType, + child->SpecifiedTypeNodeId); + for (auto &browseResult : browseResults) { + if (browseResult.TypeDefinition.Id == NodeId_BaseObjectType.Id) { + auto ifs = m_pDashboardDataClient->Browse(browseResult.NodeId, + Dashboard::IDashboardDataClient::BrowseContext_t::HasInterface()); + browseResult.TypeDefinition = ifs.front().NodeId; + LOG(INFO) << "Updated TypeDefinition of " << browseResult.BrowseName.Name << " to " << browseResult.TypeDefinition + << " because the node implements an interface"; + } + + auto possibleType = m_pTypeReader->m_typeMap->find(browseResult.TypeDefinition); // use subtype + if (possibleType != m_pTypeReader->m_typeMap->end()) + { + if(browsedNodes.find(browseResult.NodeId) == browsedNodes.end()) { + auto sharedPossibleType = possibleType->second; + ModelOpcUa::PlaceholderElement plElement; + plElement.BrowseName = browseResult.BrowseName; + plElement.pNode = TransformToNodeIds(browseResult.NodeId, sharedPossibleType); + plElement.TypeDefinition = browseResult.TypeDefinition; + //Const cast + std::shared_ptr placeholderNodeUnconst = std::const_pointer_cast(placeholderNode); + placeholderNodeUnconst->addInstance(plElement); + subscribeValues(plElement.pNode, dataSet->values, dataSet->values_mutex); } } } @@ -188,11 +179,7 @@ namespace Umati } } bool DashboardClient::containsNodeId(ModelOpcUa::NodeId_t nodeId) { - if(browsedNodes.find(nodeId) != browsedNodes.end()) { - return true; - } else { - return false; - } + return (browsedNodes.find(nodeId) != browsedNodes.end()); } @@ -575,7 +562,7 @@ namespace Umati std::map, nlohmann::json> &valueMap, std::mutex &valueMap_mutex) { - // LOG(INFO) << "subscribeValues " << pNode->NodeId.Uri << ";" << pNode->NodeId.Id; + //LOG(INFO) << "subscribeValues " << pNode->NodeId.Uri << ";" << pNode->NodeId.Id; // Only Mandatory/Optional variables if (isMandatoryOrOptionalVariable(pNode)) @@ -677,7 +664,7 @@ namespace Umati { for(auto value : m_subscribedValues){ if(value && value.get()->getNodeId() == pNode.get()->NodeId) { - LOG(INFO) << "Allready Subscribed" << value.get()->getNodeId(); + //LOG(INFO) << "Allready Subscribed" << value.get()->getNodeId(); return; } } diff --git a/DashboardClient/DashboardClient.hpp b/DashboardClient/DashboardClient.hpp index f5ec0212..e524264d 100644 --- a/DashboardClient/DashboardClient.hpp +++ b/DashboardClient/DashboardClient.hpp @@ -56,6 +56,7 @@ namespace Umati { bool containsNodeId(ModelOpcUa::NodeId_t nodeId); void updateAddDataSet(ModelOpcUa::NodeId_t nodeId); void updateDeleteDataSet(ModelOpcUa::NodeId_t nodeId); + void deleteAndUnsubscribeNode(const ModelOpcUa::SimpleNode nodeId); protected: @@ -142,7 +143,6 @@ namespace Umati { const std::shared_ptr &pChild) const; void deleteAndUnsubscribeNode(ModelOpcUa::PlaceholderElement placeHolderElement); - void deleteAndUnsubscribeNode(const ModelOpcUa::SimpleNode nodeId); }; } } diff --git a/DashboardClient/IDashboardDataClient.hpp b/DashboardClient/IDashboardDataClient.hpp index b1134fdb..6208061d 100644 --- a/DashboardClient/IDashboardDataClient.hpp +++ b/DashboardClient/IDashboardDataClient.hpp @@ -8,11 +8,9 @@ #pragma once -#include #include #include #include -#include #include "NodeIdsWellKnown.hpp" namespace Umati @@ -42,7 +40,7 @@ namespace Umati * This callback is triggered if a the data of an item changes. */ typedef std::function newValueCallbackFunction_t; - typedef std::function eventCallbackFunction_t; + typedef std::function eventCallbackFunction_t; virtual ~IDashboardDataClient() = default; @@ -252,8 +250,17 @@ namespace Umati class EventSubscriptionHandle { public: - EventSubscriptionHandle(){}; + EventSubscriptionHandle(int32_t clientHandle, int32_t subscriptionId) : m_clientHandle(clientHandle), m_subscriptionId(subscriptionId){}; ~EventSubscriptionHandle(){}; + void unsubscribe() { m_unsubscribed = true; } + bool isUnsubscribed() {return m_unsubscribed;} + int32_t getClientHandle() { return m_clientHandle;} + int32_t getSubscriptionId() { return m_subscriptionId;} + + private: + bool m_unsubscribed = false; + int32_t m_clientHandle; + int32_t m_subscriptionId; }; virtual std::string readNodeBrowseName(const ModelOpcUa::NodeId_t &nodeId) = 0; @@ -264,8 +271,8 @@ namespace Umati virtual std::shared_ptr Subscribe(ModelOpcUa::NodeId_t nodeId, newValueCallbackFunction_t callback) = 0; virtual void Unsubscribe(std::vector monItemIds, std::vector clientHandles) = 0; - virtual std::shared_ptr SubscribeEvent(eventCallbackFunction_t ecbf, void* context) = 0; - virtual void UnsubscribeEvent() = 0; + virtual std::shared_ptr SubscribeEvent(eventCallbackFunction_t ecbf) = 0; + virtual void UnsubscribeEvent(std::shared_ptr eventSubscriptionHandle) = 0; virtual std::vector ReadeNodeValues(std::list nodeIds) = 0; @@ -274,10 +281,6 @@ namespace Umati /// Verify that the connection and session are ok virtual bool VerifyConnection() = 0; - virtual std::shared_ptr getUaClient() = 0; - - virtual std::recursive_mutex* getClientMutex() = 0; - }; } // namespace Dashboard } // namespace Umati diff --git a/DashboardOpcUaClient.hpp b/DashboardOpcUaClient.hpp index 88020ecb..edbf4c35 100644 --- a/DashboardOpcUaClient.hpp +++ b/DashboardOpcUaClient.hpp @@ -28,7 +28,6 @@ class DashboardOpcUaClient { void addSubscriptionToModelChangeEvent(); void StartMachineObserver(); void Iterate(); - std::map m_namespaces; protected: std::function m_issueReset; diff --git a/MachineObserver/DashboardMachineObserver.cpp b/MachineObserver/DashboardMachineObserver.cpp index 93e1f70f..a6aba733 100644 --- a/MachineObserver/DashboardMachineObserver.cpp +++ b/MachineObserver/DashboardMachineObserver.cpp @@ -41,7 +41,7 @@ namespace Umati void DashboardMachineObserver::PublishAll() { - LOG(INFO) << "Publish"; + LOG(TRACE) << "Publish"; { std::unique_lock ul(m_dashboardClients_mutex); for (const auto &pDashClient : m_dashboardClients) @@ -71,7 +71,7 @@ namespace Umati int cnt = 0; while (this->m_running) { - if ((cnt % 100) == 0 || cnt == 0) + if ((cnt % 100) == 0) { this->UpdateMachines(); } diff --git a/ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp b/ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp index 03658f04..ffaced6c 100644 --- a/ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp +++ b/ModelOpcUa/src/ModelOpcUa/ModelInstance.cpp @@ -22,12 +22,9 @@ namespace ModelOpcUa { return this->Instances; } void PlaceholderNode::removeInstance(const PlaceholderElement &instance) { - for(std::list::iterator it = this->Instances.begin(); it != this->Instances.end(); it++ ) { - if(it->pNode->NodeId == instance.pNode->NodeId) { - this->Instances.erase(it); - break; - } - } + Instances.remove_if([instance](PlaceholderElement const placeholderElement) { + return placeholderElement.pNode->NodeId == instance.pNode->NodeId; + }); } Node::Node(const NodeDefinition &nodeDefinition, std::list> childNodes) diff --git a/OpcUaClient/OpcUaClient.cpp b/OpcUaClient/OpcUaClient.cpp index e3e2d640..e5989966 100644 --- a/OpcUaClient/OpcUaClient.cpp +++ b/OpcUaClient/OpcUaClient.cpp @@ -462,8 +462,6 @@ std::string OpcUaClient::readNodeBrowseName(const ModelOpcUa::NodeId_t &_nodeId) UA_NodeClass OpcUaClient::readNodeClass(const open62541Cpp::UA_NodeId &nodeId) { - LOG(INFO) << "NodeId: " << nodeId.NodeId->identifier.numeric; - LOG(INFO) << "Uri" << nodeId.NodeId->namespaceIndex; checkConnection(); UA_NodeClass returnClass; @@ -977,12 +975,12 @@ std::shared_ptr OpcUaC std::lock_guard l(m_clientMutex); m_opcUaWrapper->SubscriptionUnsubscribe(m_pClient.get(), monItemIds, clientHandles); } - std::shared_ptr OpcUaClient::SubscribeEvent(IDashboardDataClient::eventCallbackFunction_t ecbf, void* context) { - return m_opcUaWrapper->EventSubscribe(m_pClient.get(), ecbf, context); + std::shared_ptr OpcUaClient::SubscribeEvent(IDashboardDataClient::eventCallbackFunction_t ecbf) { + return m_opcUaWrapper->EventSubscribe(m_pClient.get(), ecbf); } - void OpcUaClient::UnsubscribeEvent() { - m_opcUaWrapper->EventUnsubscribe(m_pClient.get()); + void OpcUaClient::UnsubscribeEvent(std::shared_ptr eventSubscriptionHandle) { + m_opcUaWrapper->EventUnsubscribe(m_pClient.get(), eventSubscriptionHandle); } std::vector OpcUaClient::ReadeNodeValues(std::list modelNodeIds) { return readValues2(modelNodeIds); } @@ -1058,10 +1056,5 @@ bool OpcUaClient::VerifyConnection() { return true; } - std::recursive_mutex* OpcUaClient::getClientMutex() { - - return &m_clientMutex; - } - } // namespace OpcUa } // namespace Umati diff --git a/OpcUaClient/OpcUaClient.hpp b/OpcUaClient/OpcUaClient.hpp index cdd9606a..2a0812d9 100644 --- a/OpcUaClient/OpcUaClient.hpp +++ b/OpcUaClient/OpcUaClient.hpp @@ -64,8 +64,8 @@ class OpcUaClient : public Dashboard::IDashboardDataClient { void Unsubscribe(std::vectormonItemIds, std::vector clientHandle) override; - std::shared_ptr SubscribeEvent(IDashboardDataClient::eventCallbackFunction_t eccbf, void* context) override; - void UnsubscribeEvent() override; + std::shared_ptr SubscribeEvent(IDashboardDataClient::eventCallbackFunction_t eccbf) override; + void UnsubscribeEvent(std::shared_ptr eventSubscriptionHandle) override; std::vector ReadeNodeValues(std::list modelNodeIds) override; diff --git a/OpcUaClient/OpcUaInterface.hpp b/OpcUaClient/OpcUaInterface.hpp index cf1e7623..f7116edf 100644 --- a/OpcUaClient/OpcUaInterface.hpp +++ b/OpcUaClient/OpcUaInterface.hpp @@ -23,6 +23,9 @@ namespace Umati { static Umati::Dashboard::IDashboardDataClient::eventCallbackFunction_t s_eventcallback; + virtual UA_StatusCode DiscoveryGetEndpoints( + UA_Client *client, const open62541Cpp::UA_String *sDiscoveryURL, size_t *endpointDescriptionsSize, UA_EndpointDescription **endpointDescriptions) = 0; + virtual UA_StatusCode DiscoveryFindServers( UA_Client *client, const open62541Cpp::UA_String &sDiscoveryURL, size_t *registerdServerSize, UA_ApplicationDescription **applicationDescriptions) = 0; @@ -68,14 +71,13 @@ namespace Umati { virtual void SubscriptionUnsubscribe(UA_Client *client, std::vector monItemIds, std::vector clientHandles) = 0; virtual std::shared_ptr EventSubscribe(UA_Client* client, - Dashboard::IDashboardDataClient::eventCallbackFunction_t eventcallback, void* context) = 0; - virtual void EventUnsubscribe(UA_Client* client)= 0; + Dashboard::IDashboardDataClient::eventCallbackFunction_t eventcallback) = 0; + virtual void EventUnsubscribe(UA_Client* client, std::shared_ptr eventSubscriptionhandle)= 0; protected: std::vector namespaceArray; Subscription *p_subscr; Dashboard::IDashboardDataClient::eventCallbackFunction_t eventcallback; - void* context; }; class OpcUaWrapper : public OpcUaInterface { @@ -256,31 +258,29 @@ namespace Umati { } OpcUaWrapper* pWrapper = static_cast (monContext); if(pWrapper != nullptr && pWrapper->eventcallback != nullptr) { - bool nodeAdded = false; - bool nodeDeleted = false; - bool referenceAdded = false; - bool referenceDeleted = false; - bool dataTypeChanged = false; for(int i = 0; i < eventField.arrayLength; i++) { UA_ModelChangeStructureDataType modelChangeStructureDataType = modelChangeStructureDataTypes[i]; UA_NodeId affectedNode = modelChangeStructureDataType.affected; UA_NodeId affectedType = modelChangeStructureDataType.affectedType; UA_Byte verb = modelChangeStructureDataType.verb; - if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED) {nodeAdded = true;} - if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED) {nodeDeleted = true;} - if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED) {referenceAdded = true;} - if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED) {referenceDeleted = true;} - if((verb & UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED) == UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED) {dataTypeChanged = true;} Umati::Dashboard::IDashboardDataClient::StructureChangeEvent stc; ModelOpcUa::NodeId_t nodeId = ModelOpcUa::NodeId_t(); - nodeId.Id = "i=" + std::to_string(affectedNode.identifier.numeric); + nodeId.Uri = pWrapper->namespaceArray.at(affectedNode.namespaceIndex); + std::string nodeIdPrefix = "i="; + switch(affectedNode.identifierType){ + case UA_NODEIDTYPE_NUMERIC: nodeIdPrefix = "i="; break; + case UA_NODEIDTYPE_STRING: nodeIdPrefix = "s="; break; + case UA_NODEIDTYPE_BYTESTRING: nodeIdPrefix = "b="; break; + case UA_NODEIDTYPE_GUID: nodeIdPrefix = "g="; break; + } + nodeId.Id = nodeIdPrefix + std::to_string(affectedNode.identifier.numeric); stc.refreshNode = nodeId; - stc.nodeAdded = nodeAdded; - stc.nodeDeleted = nodeDeleted; - stc.referenceAdded = referenceAdded; - stc.referenceDeleted = referenceDeleted; - stc.dataTypeChanged = dataTypeChanged; - pWrapper->eventcallback(stc, pWrapper->context); + stc.nodeAdded = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED; + stc.nodeDeleted = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED; + stc.referenceAdded = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED; + stc.referenceDeleted = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED; + stc.dataTypeChanged = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED) == UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED; + pWrapper->eventcallback(stc); } } else { LOG(ERROR) << "Unable to propagate Event callback!"; @@ -289,13 +289,11 @@ namespace Umati { } } std::shared_ptr EventSubscribe(UA_Client* client, - Dashboard::IDashboardDataClient::eventCallbackFunction_t eventcallback, void* context) { + Dashboard::IDashboardDataClient::eventCallbackFunction_t eventcallback) { this->eventcallback = eventcallback; - this->context = context; UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default(); UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL); if(response.responseHeader.serviceResult == UA_STATUSCODE_GOOD) { - LOG(INFO) << "###################################### Created Event Subscription #####################################"; UA_UInt32 subId = response.subscriptionId; UA_MonitoredItemCreateRequest item; UA_MonitoredItemCreateRequest_init(&item); @@ -368,6 +366,9 @@ namespace Umati { UA_ExtensionObject extensionObject; UA_ExtensionObject_setValue(&extensionObject, &literalOperand, &UA_TYPES[UA_TYPES_LITERALOPERAND]); contentFilterElement.filterOperands[0] = extensionObject; + if(!contentFilterElement.filterOperands) { + UA_ContentFilterElement_delete(&contentFilterElement); + } contentFilter.elementsSize = 1; contentFilter.elements = (UA_ContentFilterElement* ) UA_Array_new(contentFilter.elementsSize, &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENT]); @@ -379,23 +380,37 @@ namespace Umati { item.requestedParameters.filter.content.decoded.data = &filter; item.requestedParameters.filter.content.decoded.type = &UA_TYPES[UA_TYPES_EVENTFILTER]; - //UA_UInt32 monId = 0; - UA_MonitoredItemCreateResult result = UA_Client_MonitoredItems_createEvent(client, subId, UA_TIMESTAMPSTORETURN_BOTH, item, this, handler_events, NULL); + if(result.statusCode == UA_STATUSCODE_GOOD) { LOG(INFO) << "Created MonitoredItem"; + return std::make_shared(result.monitoredItemId, item.requestedParameters.clientHandle); } else { LOG(ERROR) << "Unable to create MonitoredItem"; - } - //monId = result.monitoredItemId; + return NULL; + } + } else { return NULL; } return NULL; } - void EventUnsubscribe(UA_Client* client) { - LOG(ERROR) << "Unsubscribe EventCallback"; + void EventUnsubscribe(UA_Client* client, std::shared_ptr eventSubscriptionHandle) { + LOG(INFO) << "Unsubscribe EventCallback"; + UA_DeleteSubscriptionsRequest deleteRequest; + UA_DeleteSubscriptionsRequest_init(&deleteRequest); + deleteRequest.subscriptionIdsSize = 1; + UA_UInt32* pSubscriptionId = (UA_UInt32 *) UA_Array_new(1, &UA_TYPES[UA_TYPES_UINT32]); + pSubscriptionId[0] = (UA_UInt32)eventSubscriptionHandle->getSubscriptionId(); + deleteRequest.subscriptionIds = pSubscriptionId; + UA_DeleteSubscriptionsResponse response = UA_Client_Subscriptions_delete(client, deleteRequest); + if(UA_STATUSCODE_GOOD == response.results[0]) { + eventSubscriptionHandle->unsubscribe(); + } + UA_DeleteSubscriptionsRequest_clear(&deleteRequest); + UA_DeleteSubscriptionsResponse_clear(&response); + UA_Array_delete(pSubscriptionId, 1, &UA_TYPES[UA_TYPES_UINT32]); }; UA_SessionState m_pSessionState; From 2e3217a2a8a16c10f83b96b168e283d58bbddb51 Mon Sep 17 00:00:00 2001 From: mdornaus Date: Wed, 15 Mar 2023 15:25:11 +0100 Subject: [PATCH 14/22] Changed pseudo constant initialization of static array to dynamic allocation. --- OpcUaClient/OpcUaInterface.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpcUaClient/OpcUaInterface.hpp b/OpcUaClient/OpcUaInterface.hpp index f7116edf..0a1684af 100644 --- a/OpcUaClient/OpcUaInterface.hpp +++ b/OpcUaClient/OpcUaInterface.hpp @@ -250,7 +250,7 @@ namespace Umati { } } else { UA_ExtensionObject* extensionObjects = (UA_ExtensionObject*)eventField.data; - UA_ModelChangeStructureDataType modelChangeStructureDataTypes[eventField.arrayLength]; + UA_ModelChangeStructureDataType* modelChangeStructureDataTypes = (UA_ModelChangeStructureDataType*)UA_Array_new(eventField.arrayLength, &UA_TYPES[UA_TYPES_MODELCHANGESTRUCTUREDATATYPE]); for(int j = 0; j < eventField.arrayLength; j ++) { UA_ExtensionObject extensionObject = extensionObjects[j]; UA_ModelChangeStructureDataType* modelChangeStructureDataType =(UA_ModelChangeStructureDataType*)extensionObject.content.decoded.data; @@ -285,6 +285,7 @@ namespace Umati { } else { LOG(ERROR) << "Unable to propagate Event callback!"; } + UA_Array_delete(modelChangeStructureDataTypes, eventField.arrayLength, &UA_TYPES[UA_TYPES_MODELCHANGESTRUCTUREDATATYPE]); } } } From 1ac7267aba6d70958404ef3104483f6e71c1287a Mon Sep 17 00:00:00 2001 From: mdornaus Date: Thu, 6 Apr 2023 11:04:26 +0200 Subject: [PATCH 15/22] Changed back the updateInterval. --- MachineObserver/DashboardMachineObserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MachineObserver/DashboardMachineObserver.cpp b/MachineObserver/DashboardMachineObserver.cpp index a6aba733..8e01a84f 100644 --- a/MachineObserver/DashboardMachineObserver.cpp +++ b/MachineObserver/DashboardMachineObserver.cpp @@ -71,7 +71,7 @@ namespace Umati int cnt = 0; while (this->m_running) { - if ((cnt % 100) == 0) + if ((cnt % 10) == 0) { this->UpdateMachines(); } From 713e2e950676de38ed62ec24fcad509411f8473e Mon Sep 17 00:00:00 2001 From: mdornaus Date: Thu, 6 Apr 2023 11:10:59 +0200 Subject: [PATCH 16/22] Changed numerical Id of UA_NS0ID_Server with constant. --- OpcUaClient/OpcUaInterface.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpcUaClient/OpcUaInterface.hpp b/OpcUaClient/OpcUaInterface.hpp index 0a1684af..dc960408 100644 --- a/OpcUaClient/OpcUaInterface.hpp +++ b/OpcUaClient/OpcUaInterface.hpp @@ -300,7 +300,7 @@ namespace Umati { UA_MonitoredItemCreateRequest_init(&item); item.itemToMonitor.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER; item.monitoringMode = UA_MONITORINGMODE_REPORTING; - item.itemToMonitor.nodeId = UA_NODEID_NUMERIC(0, 2253); // Root->Objects->Server + item.itemToMonitor.nodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_SERVER); // Root->Objects->Server UA_EventFilter filter; UA_EventFilter_init(&filter); From ed6b94590a1637c8a8dd6ba9df09aec48f9d84c8 Mon Sep 17 00:00:00 2001 From: mdornaus Date: Thu, 6 Apr 2023 13:35:32 +0200 Subject: [PATCH 17/22] Several minor refactorings in OpcUaInterface.hpp. --- OpcUaClient/OpcUaInterface.hpp | 357 ++++++++++++++++----------------- 1 file changed, 178 insertions(+), 179 deletions(-) diff --git a/OpcUaClient/OpcUaInterface.hpp b/OpcUaClient/OpcUaInterface.hpp index dc960408..b2233989 100644 --- a/OpcUaClient/OpcUaInterface.hpp +++ b/OpcUaClient/OpcUaInterface.hpp @@ -14,6 +14,7 @@ #include #include #include "Subscription.hpp" +#include namespace Umati { namespace OpcUa { @@ -232,191 +233,189 @@ namespace Umati { } } - void SubscriptionUnsubscribe(UA_Client *client, std::vector monItemIds, std::vector clientHandles){ - p_subscr->Unsubscribe(client, monItemIds, clientHandles); - } + void SubscriptionUnsubscribe(UA_Client *client, std::vector monItemIds, std::vector clientHandles){ + p_subscr->Unsubscribe(client, monItemIds, clientHandles); + } - static void handler_events(UA_Client *client, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext, size_t nEventFields, UA_Variant *eventFields) { - UA_NodeId* sourceNodeId; - UA_NodeId affectedNode; - UA_NodeId affectedType; - UA_Byte verb; - for(size_t i = 0; i < nEventFields; ++i) { - UA_Variant eventField = eventFields[i]; - if(UA_Variant_hasScalarType(&eventField, &UA_TYPES[UA_TYPES_NODEID])) { - UA_NodeId* nodeId = (UA_NodeId *)eventField.data; - if(i == 1) { - sourceNodeId = nodeId; - } - } else { - UA_ExtensionObject* extensionObjects = (UA_ExtensionObject*)eventField.data; - UA_ModelChangeStructureDataType* modelChangeStructureDataTypes = (UA_ModelChangeStructureDataType*)UA_Array_new(eventField.arrayLength, &UA_TYPES[UA_TYPES_MODELCHANGESTRUCTUREDATATYPE]); - for(int j = 0; j < eventField.arrayLength; j ++) { - UA_ExtensionObject extensionObject = extensionObjects[j]; - UA_ModelChangeStructureDataType* modelChangeStructureDataType =(UA_ModelChangeStructureDataType*)extensionObject.content.decoded.data; - modelChangeStructureDataTypes[j] = *modelChangeStructureDataType; - } - OpcUaWrapper* pWrapper = static_cast (monContext); - if(pWrapper != nullptr && pWrapper->eventcallback != nullptr) { - for(int i = 0; i < eventField.arrayLength; i++) { - UA_ModelChangeStructureDataType modelChangeStructureDataType = modelChangeStructureDataTypes[i]; - UA_NodeId affectedNode = modelChangeStructureDataType.affected; - UA_NodeId affectedType = modelChangeStructureDataType.affectedType; - UA_Byte verb = modelChangeStructureDataType.verb; - Umati::Dashboard::IDashboardDataClient::StructureChangeEvent stc; - ModelOpcUa::NodeId_t nodeId = ModelOpcUa::NodeId_t(); - nodeId.Uri = pWrapper->namespaceArray.at(affectedNode.namespaceIndex); - std::string nodeIdPrefix = "i="; - switch(affectedNode.identifierType){ - case UA_NODEIDTYPE_NUMERIC: nodeIdPrefix = "i="; break; - case UA_NODEIDTYPE_STRING: nodeIdPrefix = "s="; break; - case UA_NODEIDTYPE_BYTESTRING: nodeIdPrefix = "b="; break; - case UA_NODEIDTYPE_GUID: nodeIdPrefix = "g="; break; - } - nodeId.Id = nodeIdPrefix + std::to_string(affectedNode.identifier.numeric); - stc.refreshNode = nodeId; - stc.nodeAdded = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED; - stc.nodeDeleted = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED; - stc.referenceAdded = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED; - stc.referenceDeleted = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED; - stc.dataTypeChanged = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED) == UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED; - pWrapper->eventcallback(stc); - } - } else { - LOG(ERROR) << "Unable to propagate Event callback!"; - } - UA_Array_delete(modelChangeStructureDataTypes, eventField.arrayLength, &UA_TYPES[UA_TYPES_MODELCHANGESTRUCTUREDATATYPE]); + static void handler_events(UA_Client *client, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext, size_t nEventFields, UA_Variant *eventFields) { + UA_NodeId* sourceNodeId; + UA_NodeId affectedNode; + UA_NodeId affectedType; + UA_Byte verb; + for(size_t i = 0; i < nEventFields; ++i) { + UA_Variant eventField = eventFields[i]; + if(UA_Variant_hasScalarType(&eventField, &UA_TYPES[UA_TYPES_NODEID])) { + UA_NodeId* nodeId = (UA_NodeId *)eventField.data; + if(i == 1) { + sourceNodeId = nodeId; + } + } else { + UA_ExtensionObject* extensionObjects = (UA_ExtensionObject*)eventField.data; + UA_ModelChangeStructureDataType* modelChangeStructureDataTypes = (UA_ModelChangeStructureDataType*)UA_Array_new(eventField.arrayLength, &UA_TYPES[UA_TYPES_MODELCHANGESTRUCTUREDATATYPE]); + for(int j = 0; j < eventField.arrayLength; j ++) { + UA_ExtensionObject extensionObject = extensionObjects[j]; + UA_ModelChangeStructureDataType* modelChangeStructureDataType =(UA_ModelChangeStructureDataType*)extensionObject.content.decoded.data; + modelChangeStructureDataTypes[j] = *modelChangeStructureDataType; } + OpcUaWrapper* pWrapper = static_cast (monContext); + if(pWrapper != nullptr && pWrapper->eventcallback != nullptr) { + for(int i = 0; i < eventField.arrayLength; i++) { + UA_ModelChangeStructureDataType modelChangeStructureDataType = modelChangeStructureDataTypes[i]; + UA_NodeId affectedNode = modelChangeStructureDataType.affected; + UA_NodeId affectedType = modelChangeStructureDataType.affectedType; + UA_Byte verb = modelChangeStructureDataType.verb; + Umati::Dashboard::IDashboardDataClient::StructureChangeEvent stc; + std::map id2Uri{{affectedNode.namespaceIndex, pWrapper->namespaceArray.at(affectedNode.namespaceIndex)}}; + open62541Cpp::UA_NodeId affectedNodeCpp; + switch(affectedNode.identifierType) { + case UA_NODEIDTYPE_NUMERIC: affectedNodeCpp = open62541Cpp::UA_NodeId(affectedNode.namespaceIndex, affectedNode.identifier.numeric); break; + case UA_NODEIDTYPE_STRING: affectedNodeCpp = open62541Cpp::UA_NodeId(affectedNode.namespaceIndex, std::string(*affectedNode.identifier.string.data, affectedNode.identifier.string.length)); break; + case UA_NODEIDTYPE_GUID: affectedNodeCpp = open62541Cpp::UA_NodeId(affectedNode.namespaceIndex, affectedNode.identifier.guid); break; + case UA_NODEIDTYPE_BYTESTRING: affectedNodeCpp = open62541Cpp::UA_NodeId(affectedNode.namespaceIndex, std::string(*affectedNode.identifier.byteString.data, affectedNode.identifier.byteString.length)); break; + } + ModelOpcUa::NodeId_t nodeId = Converter::UaNodeIdToModelNodeId(affectedNodeCpp, id2Uri).getNodeId(); + stc.nodeAdded = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED; + stc.nodeDeleted = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED; + stc.referenceAdded = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED; + stc.referenceDeleted = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED; + stc.dataTypeChanged = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED) == UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED; + pWrapper->eventcallback(stc); + } + } else { + LOG(ERROR) << "Unable to propagate Event callback!"; + } + UA_Array_delete(modelChangeStructureDataTypes, eventField.arrayLength, &UA_TYPES[UA_TYPES_MODELCHANGESTRUCTUREDATATYPE]); } } - std::shared_ptr EventSubscribe(UA_Client* client, - Dashboard::IDashboardDataClient::eventCallbackFunction_t eventcallback) { - this->eventcallback = eventcallback; - UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default(); - UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL); - if(response.responseHeader.serviceResult == UA_STATUSCODE_GOOD) { - UA_UInt32 subId = response.subscriptionId; - UA_MonitoredItemCreateRequest item; - UA_MonitoredItemCreateRequest_init(&item); - item.itemToMonitor.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER; - item.monitoringMode = UA_MONITORINGMODE_REPORTING; - item.itemToMonitor.nodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_SERVER); // Root->Objects->Server - - UA_EventFilter filter; - UA_EventFilter_init(&filter); - - //Setup selection Clauses for Filter - int nSelectClauses = 3; - UA_SimpleAttributeOperand *selectClauses = (UA_SimpleAttributeOperand*) - UA_Array_new(nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]); - - for(size_t i =0; i(result.monitoredItemId, item.requestedParameters.clientHandle); - } else { - LOG(ERROR) << "Unable to create MonitoredItem"; - return NULL; - } - - } else { - return NULL; - } - return NULL; - } + } - void EventUnsubscribe(UA_Client* client, std::shared_ptr eventSubscriptionHandle) { - LOG(INFO) << "Unsubscribe EventCallback"; - UA_DeleteSubscriptionsRequest deleteRequest; - UA_DeleteSubscriptionsRequest_init(&deleteRequest); - deleteRequest.subscriptionIdsSize = 1; - UA_UInt32* pSubscriptionId = (UA_UInt32 *) UA_Array_new(1, &UA_TYPES[UA_TYPES_UINT32]); - pSubscriptionId[0] = (UA_UInt32)eventSubscriptionHandle->getSubscriptionId(); - deleteRequest.subscriptionIds = pSubscriptionId; - UA_DeleteSubscriptionsResponse response = UA_Client_Subscriptions_delete(client, deleteRequest); - if(UA_STATUSCODE_GOOD == response.results[0]) { - eventSubscriptionHandle->unsubscribe(); - } - UA_DeleteSubscriptionsRequest_clear(&deleteRequest); - UA_DeleteSubscriptionsResponse_clear(&response); - UA_Array_delete(pSubscriptionId, 1, &UA_TYPES[UA_TYPES_UINT32]); - }; + std::shared_ptr EventSubscribe(UA_Client* client, + Dashboard::IDashboardDataClient::eventCallbackFunction_t eventcallback) { + this->eventcallback = eventcallback; + UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default(); + UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL); + if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { + return nullptr; + } + UA_UInt32 subId = response.subscriptionId; + UA_MonitoredItemCreateRequest item; + UA_MonitoredItemCreateRequest_init(&item); + item.itemToMonitor.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER; + item.monitoringMode = UA_MONITORINGMODE_REPORTING; + item.itemToMonitor.nodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_SERVER); // Root->Objects->Server + + UA_EventFilter filter; + UA_EventFilter_init(&filter); + + //Setup selection Clauses for Filter + int nSelectClauses = 3; + UA_SimpleAttributeOperand *selectClauses = (UA_SimpleAttributeOperand*) + UA_Array_new(nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]); + + for(size_t i =0; i(result.monitoredItemId, item.requestedParameters.clientHandle); + } else { + LOG(ERROR) << "Unable to create MonitoredItem"; + return nullptr; + } + } + + void EventUnsubscribe(UA_Client* client, std::shared_ptr eventSubscriptionHandle) { + LOG(INFO) << "Unsubscribe EventCallback"; + UA_DeleteSubscriptionsRequest deleteRequest; + UA_DeleteSubscriptionsRequest_init(&deleteRequest); + deleteRequest.subscriptionIdsSize = 1; + UA_UInt32* pSubscriptionId = (UA_UInt32 *) UA_Array_new(1, &UA_TYPES[UA_TYPES_UINT32]); + pSubscriptionId[0] = (UA_UInt32)eventSubscriptionHandle->getSubscriptionId(); + deleteRequest.subscriptionIds = pSubscriptionId; + UA_DeleteSubscriptionsResponse response = UA_Client_Subscriptions_delete(client, deleteRequest); + if(UA_STATUSCODE_GOOD == response.results[0]) { + eventSubscriptionHandle->unsubscribe(); + } + UA_DeleteSubscriptionsRequest_clear(&deleteRequest); + UA_DeleteSubscriptionsResponse_clear(&response); + UA_Array_delete(pSubscriptionId, 1, &UA_TYPES[UA_TYPES_UINT32]); + }; + + UA_SessionState m_pSessionState; + UA_SecureChannelState m_pChannelState; + }; } } From 112ccf829728e27a5e8697e88b7403aefe8bd538 Mon Sep 17 00:00:00 2001 From: mdornaus Date: Thu, 6 Apr 2023 13:53:43 +0200 Subject: [PATCH 18/22] Corrected Indentation. --- OpcUaClient/OpcUaClient.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/OpcUaClient/OpcUaClient.cpp b/OpcUaClient/OpcUaClient.cpp index e5989966..02790d3f 100644 --- a/OpcUaClient/OpcUaClient.cpp +++ b/OpcUaClient/OpcUaClient.cpp @@ -1053,8 +1053,7 @@ bool OpcUaClient::VerifyConnection() { LOG(WARNING) << "Getting NodeClass failed. Got NodeClass: " << (status); return false; } - - return true; - } + return true; +} } // namespace OpcUa } // namespace Umati From 0d92ec5baf922a1275f1a2084704e135c3954df2 Mon Sep 17 00:00:00 2001 From: mdornaus Date: Thu, 6 Apr 2023 14:10:43 +0200 Subject: [PATCH 19/22] Removed unused locale in handler_events. Used Conversions Constructor to create open62541cpp::UA_NodeId. --- OpcUaClient/OpcUaInterface.hpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/OpcUaClient/OpcUaInterface.hpp b/OpcUaClient/OpcUaInterface.hpp index b2233989..b93ba723 100644 --- a/OpcUaClient/OpcUaInterface.hpp +++ b/OpcUaClient/OpcUaInterface.hpp @@ -238,7 +238,6 @@ namespace Umati { } static void handler_events(UA_Client *client, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext, size_t nEventFields, UA_Variant *eventFields) { - UA_NodeId* sourceNodeId; UA_NodeId affectedNode; UA_NodeId affectedType; UA_Byte verb; @@ -266,13 +265,7 @@ namespace Umati { UA_Byte verb = modelChangeStructureDataType.verb; Umati::Dashboard::IDashboardDataClient::StructureChangeEvent stc; std::map id2Uri{{affectedNode.namespaceIndex, pWrapper->namespaceArray.at(affectedNode.namespaceIndex)}}; - open62541Cpp::UA_NodeId affectedNodeCpp; - switch(affectedNode.identifierType) { - case UA_NODEIDTYPE_NUMERIC: affectedNodeCpp = open62541Cpp::UA_NodeId(affectedNode.namespaceIndex, affectedNode.identifier.numeric); break; - case UA_NODEIDTYPE_STRING: affectedNodeCpp = open62541Cpp::UA_NodeId(affectedNode.namespaceIndex, std::string(*affectedNode.identifier.string.data, affectedNode.identifier.string.length)); break; - case UA_NODEIDTYPE_GUID: affectedNodeCpp = open62541Cpp::UA_NodeId(affectedNode.namespaceIndex, affectedNode.identifier.guid); break; - case UA_NODEIDTYPE_BYTESTRING: affectedNodeCpp = open62541Cpp::UA_NodeId(affectedNode.namespaceIndex, std::string(*affectedNode.identifier.byteString.data, affectedNode.identifier.byteString.length)); break; - } + open62541Cpp::UA_NodeId affectedNodeCpp = open62541Cpp::UA_NodeId(affectedNode); ModelOpcUa::NodeId_t nodeId = Converter::UaNodeIdToModelNodeId(affectedNodeCpp, id2Uri).getNodeId(); stc.nodeAdded = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEADDED; stc.nodeDeleted = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_NODEDELETED; From af8d4435f21a2028d67ff2e81bab71ab0a5deef6 Mon Sep 17 00:00:00 2001 From: mdornaus Date: Thu, 6 Apr 2023 14:25:14 +0200 Subject: [PATCH 20/22] Removed unused structure and queue for MachineObserver. Corrected error for Node. --- MachineObserver/DashboardMachineObserver.hpp | 13 +------------ OpcUaClient/OpcUaInterface.hpp | 7 +------ 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/MachineObserver/DashboardMachineObserver.hpp b/MachineObserver/DashboardMachineObserver.hpp index fb21ea5b..03243839 100644 --- a/MachineObserver/DashboardMachineObserver.hpp +++ b/MachineObserver/DashboardMachineObserver.hpp @@ -37,18 +37,7 @@ namespace Umati ~DashboardMachineObserver() override; void PublishAll(); - void updateAfterModelChangeEvent(UA_ModelChangeStructureDataType* modelChangeStructureDataTypes, size_t nModelChangeStructureDataTypes); - struct StructureChangeEvent{ - std::shared_ptr dbc; - ModelOpcUa::NodeId_t refreshNode; - bool nodeAdded = false; - bool nodeDeleted = false; - bool referenceAdded = false; - bool referenceDeleted = false; - bool dataTypeChanged = false; - }; - std::queue modelStructureChangeEvents; - + void updateAfterModelChangeEvent(UA_ModelChangeStructureDataType* modelChangeStructureDataTypes, size_t nModelChangeStructureDataTypes); protected: void startUpdateMachineThread(); diff --git a/OpcUaClient/OpcUaInterface.hpp b/OpcUaClient/OpcUaInterface.hpp index b93ba723..e100a328 100644 --- a/OpcUaClient/OpcUaInterface.hpp +++ b/OpcUaClient/OpcUaInterface.hpp @@ -243,12 +243,7 @@ namespace Umati { UA_Byte verb; for(size_t i = 0; i < nEventFields; ++i) { UA_Variant eventField = eventFields[i]; - if(UA_Variant_hasScalarType(&eventField, &UA_TYPES[UA_TYPES_NODEID])) { - UA_NodeId* nodeId = (UA_NodeId *)eventField.data; - if(i == 1) { - sourceNodeId = nodeId; - } - } else { + if(!UA_Variant_hasScalarType(&eventField, &UA_TYPES[UA_TYPES_NODEID])) { UA_ExtensionObject* extensionObjects = (UA_ExtensionObject*)eventField.data; UA_ModelChangeStructureDataType* modelChangeStructureDataTypes = (UA_ModelChangeStructureDataType*)UA_Array_new(eventField.arrayLength, &UA_TYPES[UA_TYPES_MODELCHANGESTRUCTUREDATATYPE]); for(int j = 0; j < eventField.arrayLength; j ++) { From cc7822e999213f366023f544b3a14d0d2cfd9e7e Mon Sep 17 00:00:00 2001 From: mdornaus Date: Thu, 6 Apr 2023 14:46:36 +0200 Subject: [PATCH 21/22] Inlined methods for EventSubscription to allow multiple includes. --- DashboardClient/IDashboardDataClient.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/DashboardClient/IDashboardDataClient.hpp b/DashboardClient/IDashboardDataClient.hpp index 6208061d..923129a7 100644 --- a/DashboardClient/IDashboardDataClient.hpp +++ b/DashboardClient/IDashboardDataClient.hpp @@ -250,12 +250,12 @@ namespace Umati class EventSubscriptionHandle { public: - EventSubscriptionHandle(int32_t clientHandle, int32_t subscriptionId) : m_clientHandle(clientHandle), m_subscriptionId(subscriptionId){}; - ~EventSubscriptionHandle(){}; - void unsubscribe() { m_unsubscribed = true; } - bool isUnsubscribed() {return m_unsubscribed;} - int32_t getClientHandle() { return m_clientHandle;} - int32_t getSubscriptionId() { return m_subscriptionId;} + inline EventSubscriptionHandle(int32_t clientHandle, int32_t subscriptionId) : m_clientHandle(clientHandle), m_subscriptionId(subscriptionId){}; + inline ~EventSubscriptionHandle(){}; + inline void unsubscribe() { m_unsubscribed = true; } + inline bool isUnsubscribed() {return m_unsubscribed;} + inline int32_t getClientHandle() { return m_clientHandle;} + inline int32_t getSubscriptionId() { return m_subscriptionId;} private: bool m_unsubscribed = false; From c4518b5ea4bc3f98bb1b65625c34060ce00f6322 Mon Sep 17 00:00:00 2001 From: Matthias Dornaus Date: Tue, 25 Apr 2023 13:34:36 +0200 Subject: [PATCH 22/22] Fixed Threading issue by implementing Eventqueue. --- DashboardClient/DashboardClient.cpp | 105 +++++++++++++++++++++------- DashboardClient/DashboardClient.hpp | 14 ++++ OpcUaClient/OpcUaInterface.hpp | 1 + 3 files changed, 94 insertions(+), 26 deletions(-) diff --git a/DashboardClient/DashboardClient.cpp b/DashboardClient/DashboardClient.cpp index be00f087..05a97ff9 100644 --- a/DashboardClient/DashboardClient.cpp +++ b/DashboardClient/DashboardClient.cpp @@ -26,7 +26,80 @@ namespace Umati std::shared_ptr pTypeReader) : m_pDashboardDataClient(pDashboardDataClient), m_pPublisher(pPublisher), m_pTypeReader(pTypeReader) { + this->startEventThread(); } + + DashboardClient::~DashboardClient() { + this->stopEventThread(); + } + + void DashboardClient::startEventThread() { + if (m_eventThreadRunning) + { + LOG(INFO) << "EventThread Running"; + return; + } + + auto func = [this]() { + int cnt = 0; + while (this->m_eventThreadRunning) { + if(!m_eventqueue.empty()) { + IDashboardDataClient::StructureChangeEvent sce = m_eventqueue.front(); + m_eventqueue.pop(); + this->m_dynamicNodes.push_back(sce.refreshNode); + if(sce.nodeAdded || sce.referenceAdded) { + this->updateAddDataSet(sce.refreshNode); + this->Publish(); + } + if(sce.nodeDeleted || sce.referenceDeleted) { + this -> updateDeleteDataSet(sce.refreshNode); + if(sce.nodeDeleted == true) { + auto search = browsedSimpleNodes.find(sce.refreshNode); + if(search != browsedSimpleNodes.end()) { + std::shared_ptr simpleNode = search->second; + this->deleteAndUnsubscribeNode(*simpleNode); + } + } + this -> Publish(); + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }; + m_eventThreadRunning = true; + m_eventThread = std::thread(func); + } + void DashboardClient::stopEventThread() { + m_eventThreadRunning = false; + if (m_eventThread.joinable()) + { + m_eventThread.join(); + } + } + void DashboardClient::reloadDataSet(ModelOpcUa::NodeId_t nodeId) { + auto search = browsedSimpleNodes.find(nodeId); + if(search == browsedSimpleNodes.end()){ + return; + } + std::shared_ptr simpleNode = search->second; + auto childNodes = simpleNode->ChildNodes; + if(childNodes.empty()) { + return; + } + auto child = childNodes.front(); + std::shared_ptr placeholderNode = std::dynamic_pointer_cast(child); + if(placeholderNode == nullptr) { + return; + } + std::shared_ptr placeholderNodeUnconst = std::const_pointer_cast(placeholderNode); + for(auto instance : placeholderNode->getInstances()) { + placeholderNodeUnconst->removeInstance(instance); + deleteAndUnsubscribeNode(instance); + } + auto browseResults = m_pDashboardDataClient->Browse(nodeId, child->ReferenceType, child->SpecifiedTypeNodeId); + this->updateAddDataSet(nodeId); + } + void DashboardClient::updateDeleteDataSet(ModelOpcUa::NodeId_t refreshNodeId) { auto search = browsedSimpleNodes.find(refreshNodeId); if( search != browsedSimpleNodes.end()) { @@ -43,9 +116,11 @@ namespace Umati for(auto& el : instances) { bool found = false; ModelOpcUa::NodeId_t nodeId = el.pNode->NodeId; - if(std::any_of(browseResults.begin(), browseResults.end(),[nodeId](ModelOpcUa::BrowseResult_t br){ return br.NodeId == nodeId;})) { - found = true; - break; + for(ModelOpcUa::BrowseResult_t browseResult : browseResults) { + if(browseResult.NodeId == nodeId) { + found = true; + break; + } } if(!found) { missingElements.push_back(el); @@ -59,8 +134,6 @@ namespace Umati } } } - //Hotfix clear subscription cache. - m_subscribedValues.clear(); } void DashboardClient::deleteAndUnsubscribeNode(ModelOpcUa::PlaceholderElement placeHolderElement) { std::shared_ptr element = placeHolderElement.pNode; @@ -107,27 +180,7 @@ namespace Umati } void DashboardClient::subscribeEvents() { auto ecbf = [this](IDashboardDataClient::StructureChangeEvent sce) { - if(sce.nodeAdded || sce.referenceAdded) { - std::thread t([this, sce](){ - this->updateAddDataSet(sce.refreshNode); - this->Publish(); - }); - t.detach(); - } - if(sce.nodeDeleted || sce.referenceDeleted) { - std::thread t([this, sce](){ - this -> updateDeleteDataSet(sce.refreshNode); - if(sce.nodeDeleted == true) { - auto search = browsedSimpleNodes.find(sce.refreshNode); - if(search != browsedSimpleNodes.end()) { - std::shared_ptr simpleNode = search->second; - this->deleteAndUnsubscribeNode(*simpleNode); - } - } - this -> Publish(); - }); - t.detach(); - } + this->m_eventqueue.push(sce); }; m_pDashboardDataClient->SubscribeEvent(ecbf); } diff --git a/DashboardClient/DashboardClient.hpp b/DashboardClient/DashboardClient.hpp index e524264d..211d8145 100644 --- a/DashboardClient/DashboardClient.hpp +++ b/DashboardClient/DashboardClient.hpp @@ -16,6 +16,9 @@ #include #include #include +#include +#include + namespace Umati { namespace Dashboard { @@ -42,6 +45,8 @@ namespace Umati { std::shared_ptr pPublisher, std::shared_ptr pTypeReader); + ~DashboardClient(); + void addDataSet( const ModelOpcUa::NodeId_t &startNodeId, const std::shared_ptr &pTypeDefinition, @@ -57,6 +62,7 @@ namespace Umati { void updateAddDataSet(ModelOpcUa::NodeId_t nodeId); void updateDeleteDataSet(ModelOpcUa::NodeId_t nodeId); void deleteAndUnsubscribeNode(const ModelOpcUa::SimpleNode nodeId); + void reloadDataSet(ModelOpcUa::NodeId_t nodeId); protected: @@ -103,6 +109,14 @@ namespace Umati { std::list> m_dataSets; std::map m_latestMessages; + bool m_eventThreadRunning; + std::thread m_eventThread; + std::queue m_eventqueue; + std::list m_dynamicNodes; + + void startEventThread(); + void stopEventThread(); + bool isMandatoryOrOptionalVariable(const std::shared_ptr &pNode); void handleSubscribeChildNodes(const std::shared_ptr &pNode, diff --git a/OpcUaClient/OpcUaInterface.hpp b/OpcUaClient/OpcUaInterface.hpp index e100a328..60222cdc 100644 --- a/OpcUaClient/OpcUaInterface.hpp +++ b/OpcUaClient/OpcUaInterface.hpp @@ -267,6 +267,7 @@ namespace Umati { stc.referenceAdded = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEADDED; stc.referenceDeleted = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED) == UA_MODELCHANGESTRUCTUREVERBMASK_REFERENCEDELETED; stc.dataTypeChanged = (verb & UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED) == UA_MODELCHANGESTRUCTUREVERBMASK_DATATYPECHANGED; + stc.refreshNode = nodeId; pWrapper->eventcallback(stc); } } else {