-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathOpcUaTypeReader.cpp
322 lines (291 loc) · 15.6 KB
/
OpcUaTypeReader.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2020-2021 (c) Christian von Arnim, ISW University of Stuttgart (for umati and VDW e.V.)
* Copyright 2021 (c) Moritz Walker, ISW University of Stuttgart (for umati and VDW e.V.)
* Copyright 2021 (c) Marius Dege, basysKom GmbH
*/
#include "OpcUaTypeReader.hpp"
#include <easylogging++.h>
#include <regex>
#include <Exceptions/OpcUaException.hpp>
namespace Umati {
namespace Dashboard {
OpcUaTypeReader::OpcUaTypeReader(
std::shared_ptr<IDashboardDataClient> pIClient,
std::vector<std::string> expectedObjectTypeNamespaces,
std::vector<Umati::Util::NamespaceInformation> namespaceInformations)
: m_expectedObjectTypeNamespaces(std::move(expectedObjectTypeNamespaces)), m_pClient(pIClient) {
for (auto const &el : namespaceInformations) {
m_availableObjectTypeNamespaces[el.Namespace] = el;
for (auto const &e : el.Types) {
m_identificationTypeOfTypeDefinition.insert(std::make_pair(e, el.IdentificationType));
m_knownMachineTypeDefinitions.emplace_back(e);
m_subTypeDefinitionToKnownMachineTypeDefinition.insert(std::make_pair(e, e));
}
}
}
OpcUaTypeReader::~OpcUaTypeReader() {
for (auto &entry : *m_typeMap.get()) {
entry.second.get()->SpecifiedChildNodes.get()->clear();
}
}
void OpcUaTypeReader::readTypeDictionaries() {
m_pClient->readTypeDictionaries();
m_pClient->buildCustomDataTypes();
m_pClient->updateCustomTypes();
}
void OpcUaTypeReader::readTypes() {
std::vector<std::string> notFoundObjectTypeNamespaces;
auto bidirectionalTypeMap = std::make_shared<std::map<ModelOpcUa::NodeId_t, std::shared_ptr<ModelOpcUa::StructureBiNode>>>();
initialize(notFoundObjectTypeNamespaces);
LOG(INFO) << "Browsing variable types.";
browseObjectOrVariableTypeAndFillBidirectionalTypeMap(ModelOpcUa::NodeId_t({"", "i=63"}), bidirectionalTypeMap, true);
LOG(INFO) << "Browsing variable types finished, continuing browsing object types";
browseObjectOrVariableTypeAndFillBidirectionalTypeMap(NodeId_BaseObjectType, bidirectionalTypeMap, false);
LOG(INFO) << "Browsing object types finished";
auto namespaces = m_pClient->Namespaces();
for (std::size_t iNamespace = 0; iNamespace < namespaces.size(); ++iNamespace) {
auto namespaceURI = namespaces[iNamespace];
auto it = find(notFoundObjectTypeNamespaces.begin(), notFoundObjectTypeNamespaces.end(), namespaceURI);
if (it == notFoundObjectTypeNamespaces.end()) {
continue;
} else {
notFoundObjectTypeNamespaces.erase(it);
}
findObjectTypeNamespacesAndCreateTypeMap(namespaceURI, bidirectionalTypeMap);
}
for (auto ¬FoundObjectTypeNamespace : notFoundObjectTypeNamespaces) {
LOG(WARNING) << "Unable to find namespace " << notFoundObjectTypeNamespace;
}
// printTypeMapYaml();
updateTypeMap();
updateObjectTypeNames();
}
void OpcUaTypeReader::updateObjectTypeNames() {
m_expectedObjectTypeNames.clear();
for (const auto &el : m_identificationTypeOfTypeDefinition) {
auto typeNodeId = el.first;
std::shared_ptr<ModelOpcUa::StructureNode> node;
try {
node = typeDefinitionToStructureNode(typeNodeId);
m_expectedObjectTypeNames.push_back(node->SpecifiedBrowseName.Name);
} catch (const std::exception &e) {
LOG(WARNING) << e.what() << ". Probably because the server does not have the NodeId, specified in the config (" << static_cast<std::string>(typeNodeId)
<< ").";
}
}
}
void OpcUaTypeReader::initialize(std::vector<std::string> ¬FoundObjectTypeNamespaces) {
for (auto &m_expectedObjectTypeNamespace : m_expectedObjectTypeNamespaces) {
notFoundObjectTypeNamespaces.push_back(m_expectedObjectTypeNamespace);
}
}
void OpcUaTypeReader::printTypeMapYaml() {
for (auto mapIterator = m_typeMap->begin(); mapIterator != m_typeMap->end(); mapIterator++) {
std::cout << std::endl;
ModelOpcUa::StructureNode::printYamlIntern(mapIterator->second, static_cast<std::string>(mapIterator->first), 1, std::cout);
}
}
void OpcUaTypeReader::updateTypeMap() {
for (auto mapIterator = m_typeMap->begin(); mapIterator != m_typeMap->end(); mapIterator++) {
for (auto childIterator = mapIterator->second->SpecifiedChildNodes->begin(); childIterator != mapIterator->second->SpecifiedChildNodes->end();
childIterator++) {
try {
auto childTypeNodeId = childIterator->get()->SpecifiedTypeNodeId;
if (childTypeNodeId == Dashboard::NodeId_Folder) {
for (auto childOfChildIterator = childIterator->get()->SpecifiedChildNodes->begin();
childOfChildIterator != childIterator->get()->SpecifiedChildNodes->end();
childOfChildIterator++) {
auto childOfChild = childOfChildIterator->get()->SpecifiedTypeNodeId;
auto childType = m_typeMap->find(childOfChild);
if (childType != m_typeMap->end()) {
childOfChildIterator->get()->SpecifiedChildNodes = childType->second->SpecifiedChildNodes;
childOfChildIterator->get()->ofBaseDataVariableType = childType->second->ofBaseDataVariableType;
}
}
continue;
}
auto childType = m_typeMap->find(childTypeNodeId);
if (childType != m_typeMap->end()) {
childIterator->get()->SpecifiedChildNodes = childType->second->SpecifiedChildNodes;
childIterator->get()->ofBaseDataVariableType = childType->second->ofBaseDataVariableType;
}
} catch (std::exception &ex) {
LOG(WARNING) << "Unable to update type due to " << ex.what();
}
}
}
}
void OpcUaTypeReader::browseObjectOrVariableTypeAndFillBidirectionalTypeMap(
const ModelOpcUa::NodeId_t &startNodeId, OpcUaTypeReader::BiDirTypeMap_t bidirectionalTypeMap, bool ofBaseDataVariableType) {
// startBrowseTypeResult is needed to create a startType
const ModelOpcUa::BrowseResult_t startBrowseTypeResult{
ModelOpcUa::NodeClass_t::VariableType, startNodeId, m_emptyId, m_emptyId, ModelOpcUa::QualifiedName_t{startNodeId.Uri, ""} // BrowseName
};
std::weak_ptr<ModelOpcUa::StructureBiNode> emptyParent;
std::weak_ptr<ModelOpcUa::StructureBiNode> startType =
handleBrowseTypeResult(bidirectionalTypeMap, startBrowseTypeResult, emptyParent, ModelOpcUa::ModellingRule_t::Mandatory, ofBaseDataVariableType);
browseTypes(bidirectionalTypeMap, startNodeId, startType.lock(), ofBaseDataVariableType);
}
void OpcUaTypeReader::findObjectTypeNamespacesAndCreateTypeMap(const std::string &namespaceURI, OpcUaTypeReader::BiDirTypeMap_t bidirectionalTypeMap) {
setupTypeMap(bidirectionalTypeMap, namespaceURI);
}
void OpcUaTypeReader::setupTypeMap(OpcUaTypeReader::BiDirTypeMap_t &bidirectionalTypeMap, std::string namespaceUri) {
for (auto &typeIterator : *bidirectionalTypeMap) {
if (typeIterator.second->namespaceUri != namespaceUri) {
continue;
}
// go to highest parent and then down the ladder to add / update attributes;
// create a list of pointers till parent is null
// go backwards and add / update child nodes till the end
std::shared_ptr<std::list<std::shared_ptr<ModelOpcUa::StructureBiNode>>> bloodline =
std::make_shared<std::list<std::shared_ptr<ModelOpcUa::StructureBiNode>>>();
std::shared_ptr<ModelOpcUa::StructureBiNode> currentGeneration = typeIterator.second;
while (nullptr != currentGeneration) {
bloodline->emplace_back(currentGeneration);
currentGeneration = currentGeneration->parent.lock();
}
std::string typeName = bloodline->front()->structureNode->SpecifiedBrowseName.Uri + ";" + bloodline->front()->structureNode->SpecifiedBrowseName.Name;
ModelOpcUa::StructureNode node = bloodline->front()->structureNode.operator*();
node.ofBaseDataVariableType = bloodline->front()->ofBaseDataVariableType;
std::stringstream bloodlineStringStream;
for (auto bloodlineIterator = bloodline->rbegin(); bloodlineIterator != bloodline->rend(); ++bloodlineIterator) {
auto ancestor = *bloodlineIterator;
bloodlineStringStream << "->" << static_cast<std::string>(ancestor->structureNode->SpecifiedBrowseName);
// Ancestor is a SuperType of the analysed node (var node)
// Iterate over the children of the ancestor (var ancestor)
// (var currentChild) is a child of the ancestor
// Example: (var node) = ProductionProgramStateMachineType (ns=MachineTool;i=15)
// (var ancestor.1) = ProductionStateMachineType (ns=MachineTool;i=24)
// (var ancestor.2) = FiniteStateMachineType (ns=0;i=2771)
for (auto ¤tChild : *ancestor->SpecifiedBiChildNodes) {
if (currentChild->isType) {
continue;
}
auto structureNode = currentChild->toStructureNode();
// Lookup if ancestor's child currentChild is child of the node.
auto findIterator = std::find_if(node.SpecifiedChildNodes->begin(), node.SpecifiedChildNodes->end(), [&](const auto &el) {
return el == structureNode || el->SpecifiedBrowseName == structureNode->SpecifiedBrowseName;
});
/// \todo Check if a merge is required here!
// Ancestor's child currentChild is child of the node.
// (var findIterator) contains the child of the node.
if (findIterator != node.SpecifiedChildNodes->end()) {
// (var structureNodeChildren) contains children of the child of the ancestor, which is also contained in the node.
auto structureNodeChildren = structureNode->SpecifiedChildNodes;
// Iterate over children of the child of the ancestor.
// If there are children which are contained in ancestor->child and not in node->child, add them to node->child
// Example: Ancestor FiniteStateMachineType is an ancestor of ProductionStateMachineType, both contain a node CurrentState
// ProductionStateMachineType->CurrentState does not contain the node "Number", but FiniteStateMachineType->CurrentState->Number does exist
// Thus, we add node "Number" to SpecifiedChildNodes of ProductionStateMachineType->CurrentState.
for (auto &childOfChild : *structureNodeChildren) {
auto findIt = std::find_if(findIterator->get()->SpecifiedChildNodes->begin(), findIterator->get()->SpecifiedChildNodes->end(), [&](const auto &el) {
return el == childOfChild || childOfChild->SpecifiedBrowseName == el->SpecifiedBrowseName;
});
if (findIt == findIterator->get()->SpecifiedChildNodes->end()) {
findIterator->get()->SpecifiedChildNodes->emplace_back(childOfChild);
}
}
// Check if original child is optional, if so, override
if (
(*findIterator)->ModellingRule == ModelOpcUa::ModellingRule_t::Optional ||
(*findIterator)->ModellingRule == ModelOpcUa::ModellingRule_t::OptionalPlaceholder) {
node.SpecifiedChildNodes->erase(findIterator++);
node.SpecifiedChildNodes->emplace_back(structureNode);
}
}
// Node's ancestor's child currentChild is not child of the node.
else {
// Thus, we add it.
node.SpecifiedChildNodes->emplace_back(structureNode);
}
}
}
auto shared = std::make_shared<ModelOpcUa::StructureNode>(node);
std::pair<ModelOpcUa::NodeId_t, std::shared_ptr<ModelOpcUa::StructureNode>> newType(typeIterator.first, shared);
m_typeMap->insert(newType);
}
}
std::shared_ptr<ModelOpcUa::StructureBiNode> OpcUaTypeReader::handleBrowseTypeResult(
OpcUaTypeReader::BiDirTypeMap_t &bidirectionalTypeMap,
const ModelOpcUa::BrowseResult_t &entry,
const std::weak_ptr<ModelOpcUa::StructureBiNode> &parent,
ModelOpcUa::ModellingRule_t modellingRule,
bool ofBaseDataVariableType) {
bool isObjectType = ModelOpcUa::ObjectType == entry.NodeClass;
bool isVariableType = ModelOpcUa::VariableType == entry.NodeClass;
ModelOpcUa::StructureBiNode node(
entry, ofBaseDataVariableType, std::make_shared<std::list<std::shared_ptr<ModelOpcUa::StructureNode>>>(), parent.lock(), entry.NodeId.Uri, modellingRule);
auto current = std::make_shared<ModelOpcUa::StructureBiNode>(node);
if (isObjectType || isVariableType) {
std::string typeName = node.structureNode->SpecifiedBrowseName.Uri + ";" + node.structureNode->SpecifiedBrowseName.Name;
if (bidirectionalTypeMap->count(entry.NodeId) == 0) {
current->isType = true;
current->ofBaseDataVariableType = ofBaseDataVariableType;
std::pair<ModelOpcUa::NodeId_t, std::shared_ptr<ModelOpcUa::StructureBiNode>> newType(entry.NodeId, current);
bidirectionalTypeMap->insert(newType);
std::pair<std::string, ModelOpcUa::NodeId_t> newNameMapping(typeName, entry.NodeId);
m_nameToId->insert(newNameMapping);
LOG_EVERY_N(50, INFO) << "Current size BiDirectionalTypeMap: " << bidirectionalTypeMap->size();
} else {
LOG(INFO) << "Found Type " << typeName << " again";
}
}
if (parent.lock() != nullptr) {
parent.lock()->SpecifiedBiChildNodes->emplace_back(current);
}
return current;
}
void OpcUaTypeReader::browseTypes(
OpcUaTypeReader::BiDirTypeMap_t bidirectionalTypeMap,
const ModelOpcUa::NodeId_t &startNodeId,
const std::weak_ptr<ModelOpcUa::StructureBiNode> &parent,
bool ofBaseDataVariableType) {
auto browseTypeContext = IDashboardDataClient::BrowseContext_t::ObjectAndVariableWithTypes();
auto browseResults = m_pClient->Browse(startNodeId, browseTypeContext);
for (auto &browseResult : browseResults) {
ModelOpcUa::ModellingRule_t modellingRule = ModelOpcUa::ModellingRule_t::None;
try {
modellingRule = m_pClient->BrowseModellingRule(browseResult.NodeId);
} catch (Exceptions::UmatiException &e) {
LOG(ERROR) << "Error browsing modelling rule of " << browseResult.NodeId << ": " << e.what();
}
std::weak_ptr<ModelOpcUa::StructureBiNode> current =
handleBrowseTypeResult(bidirectionalTypeMap, browseResult, parent, modellingRule, ofBaseDataVariableType);
browseTypes(bidirectionalTypeMap, browseResult.NodeId, current, ofBaseDataVariableType);
}
}
std::shared_ptr<ModelOpcUa::StructureNode> OpcUaTypeReader::typeDefinitionToStructureNode(const ModelOpcUa::NodeId_t &typeDefinition) const {
auto typePair = m_typeMap->find(typeDefinition);
if (typePair == m_typeMap->end()) {
LOG(ERROR) << "Unable to find " << static_cast<std::string>(typeDefinition) + " in typeMap";
throw Umati::MachineObserver::Exceptions::MachineInvalidException("Type not found");
}
return typePair->second;
}
std::string OpcUaTypeReader::CSNameFromUri(std::string nsUri) {
const std::regex regex(R"(\/([\w\-]+)\/?$)");
std::smatch match;
if (std::regex_search(nsUri, match, regex)) {
return match[1].str();
} else {
return nsUri;
}
}
std::shared_ptr<ModelOpcUa::StructureNode> OpcUaTypeReader::getIdentificationTypeStructureNode(const ModelOpcUa::NodeId_t &typeDefinition) const {
auto identificationTypeNodeId = getIdentificationTypeNodeId(typeDefinition);
return typeDefinitionToStructureNode(identificationTypeNodeId);
}
ModelOpcUa::NodeId_t OpcUaTypeReader::getIdentificationTypeNodeId(const ModelOpcUa::NodeId_t &typeDefinition) const {
auto pair = m_identificationTypeOfTypeDefinition.find(typeDefinition);
if (pair == m_identificationTypeOfTypeDefinition.end()) {
throw Umati::MachineObserver::Exceptions::MachineInvalidException(
"IdentificationType not found, probably because namespace " + static_cast<std::string>(typeDefinition) + " is not in the config, or this type does " +
"not have an identification type.");
}
return pair->second;
}
} // namespace Dashboard
} // namespace Umati