Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Included ModelChanged Eventhandling. Fixed update of nodes after reconnection. #368

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
370df35
Initial commit.
mdornaus Aug 30, 2022
16ada09
Decoupled eventhandling from event processing due to threading issues.
mdornaus Aug 31, 2022
3545052
Changed deletion of Jobs.
mdornaus Sep 10, 2022
68c9d0f
Removed some debug logging. Increased Queue timing (10ms) lookup.
mdornaus Sep 10, 2022
d52d8b2
Added Logging.
mdornaus Sep 12, 2022
5fe6d56
Increased Timing for update loop.
mdornaus Sep 12, 2022
a77b113
Added mutex on Adding Dataset.
mdornaus Sep 12, 2022
84747cd
Changed back timings removed lock guard.
mdornaus Sep 13, 2022
397d86e
Improved Node Cache update on remove. Hotfix cleared Subscriptioncache.
mdornaus Sep 13, 2022
6e05866
Refactored ModelChangedEventhandling. Preparation for Dev merge.
mdornaus Feb 28, 2023
4d13a60
Restored configuration.json.example file.
mdornaus Mar 7, 2023
51f8c90
Removed unused Pointer.
mdornaus Mar 13, 2023
46cd1b0
Refactored Comments.
mdornaus Mar 15, 2023
2e3217a
Changed pseudo constant initialization of static array to dynamic all…
mdornaus Mar 15, 2023
1ac7267
Changed back the updateInterval.
mdornaus Apr 6, 2023
713e2e9
Changed numerical Id of UA_NS0ID_Server with constant.
mdornaus Apr 6, 2023
ed6b945
Several minor refactorings in OpcUaInterface.hpp.
mdornaus Apr 6, 2023
112ccf8
Corrected Indentation.
mdornaus Apr 6, 2023
0d92ec5
Removed unused locale in handler_events. Used Conversions Constructor…
mdornaus Apr 6, 2023
af8d443
Removed unused structure and queue for MachineObserver. Corrected err…
mdornaus Apr 6, 2023
cc7822e
Inlined methods for EventSubscription to allow multiple includes.
mdornaus Apr 6, 2023
c4518b5
Fixed Threading issue by implementing Eventqueue.
mdornaus Apr 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 215 additions & 3 deletions DashboardClient/DashboardClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,213 @@ namespace Umati
std::shared_ptr<OpcUaTypeReader> pTypeReader)
: m_pDashboardDataClient(pDashboardDataClient), m_pPublisher(pPublisher), m_pTypeReader(pTypeReader)
{
this->startEventThread();
}

DashboardClient::~DashboardClient() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best practice is following the rule of tree.

https://en.cppreference.com/w/cpp/language/rule_of_three

this->stopEventThread();
}

void DashboardClient::startEventThread() {
if (m_eventThreadRunning)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m_eventThreadRunning is

  • a missleading name, more like m_keepEventThreadRunning
  • is m_eventThreadRunning synchronized and wrapped by a mutex?

{
LOG(INFO) << "EventThread Running";
return;
}

auto func = [this]() {
int cnt = 0;
while (this->m_eventThreadRunning) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not protected by mutex

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced by atomic.

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<const ModelOpcUa::SimpleNode> simpleNode = search->second;
this->deleteAndUnsubscribeNode(*simpleNode);
}
}
this -> Publish();
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation looks suscious.
A continue inside should allow to preoces more than 10 Events per seconds (in case of busy OPC UA Servers)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced by Threadsafe queue!

std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
};
m_eventThreadRunning = true;
m_eventThread = std::thread(func);
}
void DashboardClient::stopEventThread() {
m_eventThreadRunning = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m_eventThreadRunning is not protected. Might lead to memory corruption

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced by atomic bool.

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<const ModelOpcUa::SimpleNode> simpleNode = search->second;
auto childNodes = simpleNode->ChildNodes;
if(childNodes.empty()) {
return;
}
auto child = childNodes.front();
std::shared_ptr<const ModelOpcUa::PlaceholderNode> placeholderNode = std::dynamic_pointer_cast<const ModelOpcUa::PlaceholderNode>(child);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make data model non-const?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This involves changeing some classes in the model and i the codebase. I want to do this, but maybe it is better to do this in a separate task.

if(placeholderNode == nullptr) {
return;
}
std::shared_ptr<ModelOpcUa::PlaceholderNode> placeholderNodeUnconst = std::const_pointer_cast<ModelOpcUa::PlaceholderNode>(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()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invert + Return

std::shared_ptr<const ModelOpcUa::SimpleNode> simpleNode = search->second;
auto childNodes = simpleNode->ChildNodes;
if(!childNodes.empty()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invert + Return

auto child = childNodes.front();
std::shared_ptr<const ModelOpcUa::PlaceholderNode> placeholderNode = std::dynamic_pointer_cast<const ModelOpcUa::PlaceholderNode>(child);
if(placeholderNode == nullptr) { return; }
auto browseResults = m_pDashboardDataClient->Browse(refreshNodeId, child->ReferenceType, child->SpecifiedTypeNodeId);
//Check if Instances are still in browsresult
std::list<ModelOpcUa::PlaceholderElement> instances = placeholderNode->getInstances();
std::list<ModelOpcUa::PlaceholderElement> missingElements;
for(auto& el : instances) {
bool found = false;
ModelOpcUa::NodeId_t nodeId = el.pNode->NodeId;
for(ModelOpcUa::BrowseResult_t browseResult : browseResults) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::find_if

if(browseResult.NodeId == nodeId) {
found = true;
break;
}
}
if(!found) {
missingElements.push_back(el);
}
}
//Remove and Unsubscribe the elements
for(auto& el : missingElements) {
std::shared_ptr<ModelOpcUa::PlaceholderNode> placeholderNodeUnconst = std::const_pointer_cast<ModelOpcUa::PlaceholderNode>(placeholderNode);
placeholderNodeUnconst->removeInstance(el);
deleteAndUnsubscribeNode(el);
}
}
}
}
void DashboardClient::deleteAndUnsubscribeNode(ModelOpcUa::PlaceholderElement placeHolderElement) {
std::shared_ptr<const ModelOpcUa::SimpleNode> element = placeHolderElement.pNode;
const std::list<std::shared_ptr<const ModelOpcUa::Node>> children = element->ChildNodes;
for(auto& el : children) {
std::shared_ptr<const ModelOpcUa::Node> node = el;
std::shared_ptr<const ModelOpcUa::SimpleNode> simpleNode = std::dynamic_pointer_cast<const ModelOpcUa::SimpleNode>(node);
if(simpleNode != nullptr) {
const ModelOpcUa::SimpleNode simpleN = *simpleNode;
deleteAndUnsubscribeNode(simpleN);
} else {
std::shared_ptr<const ModelOpcUa::PlaceholderNode> placeHolderNode = std::dynamic_pointer_cast<const ModelOpcUa::PlaceholderNode>(node);
if(placeHolderNode == nullptr) { continue;}
std::list<ModelOpcUa::PlaceholderElement> instances = placeHolderNode->getInstances();
for(auto& el : instances) {
deleteAndUnsubscribeNode(el);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::foreach

}
}
}
browsedNodes.erase(element->NodeId);
browsedSimpleNodes.erase(element->NodeId);

}
void DashboardClient::deleteAndUnsubscribeNode(const ModelOpcUa::SimpleNode simpleNode) {
const std::list<std::shared_ptr<const ModelOpcUa::Node>> children = simpleNode.ChildNodes;
for(auto node : children) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks identical to the method above? Use a single function for this?

std::shared_ptr<const ModelOpcUa::SimpleNode> simpleNode = std::dynamic_pointer_cast<const ModelOpcUa::SimpleNode>(node);
if(simpleNode != nullptr) {
const ModelOpcUa::SimpleNode simpleN = *simpleNode;
deleteAndUnsubscribeNode(simpleN);
} else {
std::shared_ptr<const ModelOpcUa::PlaceholderNode> placeHolderNode = std::dynamic_pointer_cast<const ModelOpcUa::PlaceholderNode>(node);
if(placeHolderNode != nullptr) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invert the condition and continue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

std::list<ModelOpcUa::PlaceholderElement> instances = placeHolderNode->getInstances();
for(std::list<ModelOpcUa::PlaceholderElement>::iterator it = instances.begin(); it != instances.end(); it++) {
deleteAndUnsubscribeNode(*it);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::foreach

}

}
}
}
browsedNodes.erase(simpleNode.NodeId);
browsedSimpleNodes.erase(simpleNode.NodeId);
}
void DashboardClient::subscribeEvents() {
auto ecbf = [this](IDashboardDataClient::StructureChangeEvent sce) {
this->m_eventqueue.push(sce);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::queue is not thread safe, mutex required.

};
m_pDashboardDataClient->SubscribeEvent(ecbf);
}
void DashboardClient::updateAddDataSet(ModelOpcUa::NodeId_t refreshNodeId) {
LOG(INFO) << "Update Add DataSet";
if(m_dataSets.empty()) {
return;
}
std::shared_ptr<Umati::Dashboard::DashboardClient::DataSetStorage_t> dataSet = m_dataSets.front();
auto search = browsedSimpleNodes.find(refreshNodeId);
if(search == browsedSimpleNodes.end()) {
return;
}
std::shared_ptr<const ModelOpcUa::SimpleNode> simpleNode = search->second;
auto childNodes = simpleNode->ChildNodes;
if(!childNodes.empty()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invert + return?

auto child = childNodes.front();
std::shared_ptr<const ModelOpcUa::PlaceholderNode> placeholderNode = std::dynamic_pointer_cast<const ModelOpcUa::PlaceholderNode>(child);
if(placeholderNode == nullptr) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return instead?

} 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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation

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())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invert + return

{
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<ModelOpcUa::PlaceholderNode> placeholderNodeUnconst = std::const_pointer_cast<ModelOpcUa::PlaceholderNode>(placeholderNode);
placeholderNodeUnconst->addInstance(plElement);
subscribeValues(plElement.pNode, dataSet->values, dataSet->values_mutex);
}
}
}
}
}
}
bool DashboardClient::containsNodeId(ModelOpcUa::NodeId_t nodeId) {
return (browsedNodes.find(nodeId) != browsedNodes.end());
}


Expand Down Expand Up @@ -224,6 +431,8 @@ namespace Umati
break;
}
}
} else {
LOG(INFO) << "Not Inserted Node: " << startNode;
}
auto pNode = std::make_shared<ModelOpcUa::SimpleNode>(
startNode,
Expand All @@ -232,6 +441,7 @@ namespace Umati
foundChildNodes);

pNode->ofBaseDataVariableType = pTypeDefinition->ofBaseDataVariableType;
browsedSimpleNodes.insert({startNode, pNode});
return pNode;
}

Expand Down Expand Up @@ -405,7 +615,7 @@ namespace Umati
std::map<std::shared_ptr<const ModelOpcUa::Node>, 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))
Expand Down Expand Up @@ -506,8 +716,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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you remove the comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll Remove the comment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you?

return;
}
}
auto subscribedValue = m_pDashboardDataClient->Subscribe(pNode->NodeId, callback);
m_subscribedValues.push_back(subscribedValue);
Expand Down
23 changes: 23 additions & 0 deletions DashboardClient/DashboardClient.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#include <map>
#include <set>
#include <mutex>
#include <thread>
#include <queue>

namespace Umati {

namespace Dashboard {
Expand All @@ -42,6 +45,8 @@ namespace Umati {
std::shared_ptr<IPublisher> pPublisher,
std::shared_ptr<OpcUaTypeReader> pTypeReader);

~DashboardClient();

void addDataSet(
const ModelOpcUa::NodeId_t &startNodeId,
const std::shared_ptr<ModelOpcUa::StructureNode> &pTypeDefinition,
Expand All @@ -51,6 +56,13 @@ namespace Umati {
void Publish();

void Unsubscribe(ModelOpcUa::NodeId_t nodeId);
void subscribeEvents();

bool containsNodeId(ModelOpcUa::NodeId_t nodeId);
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:
Expand Down Expand Up @@ -92,10 +104,19 @@ namespace Umati {
std::shared_ptr<OpcUaTypeReader> m_pTypeReader;

std::set<ModelOpcUa::NodeId_t> browsedNodes;
std::map<const ModelOpcUa::NodeId_t, std::shared_ptr<const ModelOpcUa::SimpleNode>> browsedSimpleNodes;
std::recursive_mutex m_dataSetMutex;
std::list<std::shared_ptr<DataSetStorage_t>> m_dataSets;
std::map<std::string, LastMessage_t> m_latestMessages;

bool m_eventThreadRunning;
std::thread m_eventThread;
std::queue<IDashboardDataClient::StructureChangeEvent> m_eventqueue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::queue is not threadsafe! Add mutex/lockfree queue or sth

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced by a threadsafe queue.

std::list<ModelOpcUa::NodeId_t> m_dynamicNodes;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never read? Could be removed?


void startEventThread();
void stopEventThread();

bool isMandatoryOrOptionalVariable(const std::shared_ptr<const ModelOpcUa::SimpleNode> &pNode);

void handleSubscribeChildNodes(const std::shared_ptr<const ModelOpcUa::SimpleNode> &pNode,
Expand Down Expand Up @@ -134,6 +155,8 @@ namespace Umati {

void TransformToNodeIdNodeNotFoundLog(const ModelOpcUa::NodeId_t &startNode,
const std::shared_ptr<ModelOpcUa::StructureNode> &pChild) const;

void deleteAndUnsubscribeNode(ModelOpcUa::PlaceholderElement placeHolderElement);
};
}
}
38 changes: 35 additions & 3 deletions DashboardClient/IDashboardDataClient.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,22 @@ namespace Umati
class IDashboardDataClient
{
public:
/// Struct for handling Events
struct StructureChangeEvent{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can there be more than one true, otherwise use ENUM?

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<void(nlohmann::json value)> newValueCallbackFunction_t;
typedef std::function<void(StructureChangeEvent sce)> eventCallbackFunction_t;

virtual ~IDashboardDataClient() = default;

Expand Down Expand Up @@ -233,22 +248,39 @@ namespace Umati

virtual void buildCustomDataTypes() = 0;

class EventSubscriptionHandle {
mdornaus marked this conversation as resolved.
Show resolved Hide resolved
public:
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;
int32_t m_clientHandle;
int32_t m_subscriptionId;
};

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<ValueSubscriptionHandle>
Subscribe(ModelOpcUa::NodeId_t nodeId, newValueCallbackFunction_t callback) = 0;

virtual std::shared_ptr<ValueSubscriptionHandle> Subscribe(ModelOpcUa::NodeId_t nodeId, newValueCallbackFunction_t callback) = 0;
virtual void Unsubscribe(std::vector<int32_t> monItemIds, std::vector<int32_t> clientHandles) = 0;

virtual std::shared_ptr<EventSubscriptionHandle> SubscribeEvent(eventCallbackFunction_t ecbf) = 0;
virtual void UnsubscribeEvent(std::shared_ptr<Dashboard::IDashboardDataClient::EventSubscriptionHandle> eventSubscriptionHandle) = 0;

virtual std::vector<nlohmann::json> ReadeNodeValues(std::list<ModelOpcUa::NodeId_t> nodeIds) = 0;

virtual std::vector<std::string> Namespaces() = 0;

/// Verify that the connection and session are ok
virtual bool VerifyConnection() = 0;

};
} // namespace Dashboard
} // namespace Umati
1 change: 1 addition & 0 deletions DashboardClient/OpcUaTypeReader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ namespace Umati
std::shared_ptr<ModelOpcUa::StructureNode> typeDefinitionToStructureNode(const ModelOpcUa::NodeId_t &typeDefinition) const;
std::shared_ptr<ModelOpcUa::StructureNode> getIdentificationTypeStructureNode(const ModelOpcUa::NodeId_t &typeDefinition) const;
ModelOpcUa::NodeId_t getIdentificationTypeNodeId(const ModelOpcUa::NodeId_t &typeDefinition) const;

protected:
/// Map of <TypeName, StructureBiNode>
typedef std::shared_ptr<std::map<ModelOpcUa::NodeId_t,
Expand Down
Loading
Loading