/// <summary> /// Creates Profile Items in the backend store based for all OPC nodes in the nodeset model /// </summary> /// <param name="nodesetModel">nodeset model to import</param> /// <param name="updateExisting">Indicates if existing profile items should be updated/overwritten or kept unchanged.</param> /// <returns></returns> private static Dictionary <string, ProfileTypeDefinitionModel> ImportProfileItems(NodeSetModel nodesetModel, DALContext dalContext) { nodesetModel.UpdateIndices(); foreach (var dataType in nodesetModel.DataTypes) { dataType.ImportProfileItem(dalContext); } foreach (var customType in nodesetModel.VariableTypes) { customType.ImportProfileItem(dalContext); } foreach (var uaInterface in nodesetModel.Interfaces) { uaInterface.ImportProfileItem(dalContext); } foreach (var objectType in nodesetModel.ObjectTypes) { objectType.ImportProfileItem(dalContext); } foreach (var uaObject in nodesetModel.Objects) { uaObject.ImportProfileItem(dalContext); } foreach (var uaVariable in nodesetModel.DataVariables) { if (uaVariable.Parent.Namespace != uaVariable.Namespace) { dalContext.Logger.LogWarning($"UAVariable {uaVariable} ignored because it's parent {uaVariable.Parent} is in a different namespace {uaVariable.Parent.Namespace}."); continue; } if (uaVariable.Parent is DataVariableModel) { if (dalContext.profileItems.TryGetValue(uaVariable.Parent.NodeId, out var parent)) { var nodeIdParts = uaVariable.NodeId.Split(';'); if (parent.Attributes.FirstOrDefault(a => a.OpcNodeId == nodeIdParts[1] && nodeIdParts[0].EndsWith(a.Namespace)) != null) { continue; } } else { if (uaVariable.Parent is DataVariableModel dvParentModel && dalContext.profileItems.TryGetValue(dvParentModel.Parent.NodeId, out var dvGrandParent)) { var nodeIdParts = uaVariable.Parent.NodeId.Split(';'); var attribute = dvGrandParent.Attributes.FirstOrDefault(a => a.OpcNodeId == nodeIdParts[1] && nodeIdParts[0].EndsWith(a.Namespace)); if (attribute != null && !string.IsNullOrEmpty(attribute.DataVariableNodeIds)) { var map = DataVariableNodeIdMap.GetMap(attribute.DataVariableNodeIds); if (map.DataVariableNodeIdsByBrowseName.ContainsKey(uaVariable.BrowseName)) { continue; } } } } // These are usually OK as they are generated based on the variable type //continue; } if (uaVariable.Parent is ObjectTypeModel || uaVariable.Parent is ObjectModel || uaVariable.Parent is VariableTypeModel) { if (dalContext.profileItems.TryGetValue(uaVariable.Parent.NodeId, out var parent)) { var nodeIdParts = uaVariable.NodeId.Split(';'); if (parent.Attributes.FirstOrDefault(a => a.OpcNodeId == nodeIdParts[1] && nodeIdParts[0].EndsWith(a.Namespace)) != null) { continue; } } } dalContext.Logger.LogWarning($"UAVariable {uaVariable} ignored."); } return(dalContext.profileItems); }
static UANodeSet ExportNodeSet(UANodeSet nodeSet, NodeSetModel nodesetModel, Dictionary <string, NodeSetModel> nodesetModels, Dictionary <string, string> aliases) { nodesetModel.UpdateIndices(); var namespaceUris = nodesetModel.AllNodesByNodeId.Values.Select(v => v.Namespace).Distinct().ToList(); var requiredModels = new List <ModelTableEntry>(); NamespaceTable namespaces; // Ensure OPC UA model is the first one if (nodeSet.NamespaceUris?.Any() == true) { namespaces = new NamespaceTable(nodeSet.NamespaceUris); } else { // Ensure OPC UA model is the first one namespaces = new NamespaceTable(new[] { strOpcNamespaceUri }); } foreach (var nsUri in namespaceUris) { namespaces.GetIndexOrAppend(nsUri); } HashSet <string> nodeIdsUsed = new HashSet <string>(); var items = ExportAllNodes(nodesetModel, aliases, namespaces, nodeIdsUsed); // remove unused aliases var usedAliases = aliases.Where(pk => nodeIdsUsed.Contains(pk.Key)).ToDictionary(kv => kv.Key, kv => kv.Value); // Add aliases for all nodeids from other namespaces var currentNodeSetNamespaceIndex = namespaces.GetIndex(nodesetModel.ModelUri); bool bAliasesAdded = false; foreach (var nodeId in nodeIdsUsed) { var parsedNodeId = NodeId.Parse(nodeId); if (parsedNodeId.NamespaceIndex != currentNodeSetNamespaceIndex) { if (!usedAliases.ContainsKey(nodeId)) { var namespaceUri = namespaces.GetString(parsedNodeId.NamespaceIndex); var nodeIdWithUri = new ExpandedNodeId(parsedNodeId, namespaceUri).ToString(); var nodeModel = nodesetModels.Select(nm => nm.Value.AllNodesByNodeId.TryGetValue(nodeIdWithUri, out var model) ? model : null).Where(n => n != null).FirstOrDefault(); var displayName = nodeModel.DisplayName?.FirstOrDefault()?.Text; if (displayName != null && !usedAliases.ContainsValue(displayName)) { usedAliases.Add(nodeId, displayName); aliases.Add(nodeId, displayName); bAliasesAdded = true; } } } } var aliasList = usedAliases .Select(alias => new NodeIdAlias { Alias = alias.Value, Value = alias.Key }) .OrderBy(kv => kv.Value) .ToList(); nodeSet.Aliases = aliasList.ToArray(); if (bAliasesAdded) { // Re-export with new aliases items = ExportAllNodes(nodesetModel, aliases, namespaces, null); } var allNamespaces = namespaces.ToArray(); nodeSet.NamespaceUris = allNamespaces.Where(ns => ns != strOpcNamespaceUri).ToArray(); foreach (var uaNamespace in allNamespaces.Except(namespaceUris)) { if (!requiredModels.Any(m => m.ModelUri == uaNamespace)) { if (nodesetModels.TryGetValue(uaNamespace, out var requiredNodeSetModel)) { var requiredModel = new ModelTableEntry { ModelUri = uaNamespace, Version = requiredNodeSetModel.Version, PublicationDate = requiredNodeSetModel.PublicationDate != null?DateTime.SpecifyKind(requiredNodeSetModel.PublicationDate.Value, DateTimeKind.Utc) : default,
public async System.Threading.Tasks.Task <Dictionary <string, ProfileTypeDefinitionModel> > ImportNodeSetModelAsync(NodeSetModel nodeSetModel, UserToken userToken) { #if NODESETDBTEST { var sw2 = Stopwatch.StartNew(); Logger.LogTrace($"Saving NodeSetModel"); foreach (var nodeSet in NodesetModels.Where(ns => ns.Key != nodeSetModel.ModelUri)) { nsDBContext.NodeSets.Attach(nodeSet.Value); } nsDBContext.NodeSets.Add(nodeSetModel); nsDBContext.SaveChanges(); Logger.LogTrace($"Saved NodeSetModel after {sw2.Elapsed}"); var savedModel = nsDBContext.NodeSets .Where(m => m.ModelUri == nodeSetModel.ModelUri && m.PublicationDate == nodeSetModel.PublicationDate) .FirstOrDefault(); //.ToList(); //var savedModel2 = nsDBContext.NodeSets.Find(nodeSetModel.ModelUri, nodeSetModel.PublicationDate); } #endif ProfileModel profile = (ProfileModel)nodeSetModel.CustomState; var authorToken = userToken; if (_coreNodeSetUris.Contains(profile.Namespace)) { userToken = UserToken.GetGlobalUser(userToken); authorToken = null; } _dal.StartTransaction(); foreach (var nsFile in profile.NodeSetFiles) { await _nsFileDal.Upsert(nsFile, userToken, true); } var result = await _nsDal.Upsert(profile, userToken, true); var dalContext = new DALContext(this, userToken, authorToken, false); var profileItems = ImportProfileItems(nodeSetModel, dalContext); var sw = Stopwatch.StartNew(); Logger.LogTrace($"Commiting transaction"); await _dal.CommitTransactionAsync(); Logger.LogTrace($"Committed transaction after {sw.Elapsed}"); // TODO figure out why the InstanceParent property doesn't get written propertly: this fixup only works for some cases and is very slow //foreach (var item in dalContext.profileItems.Values.Where(pi => pi.InstanceParent != null)) //{ // var existingItem = await _dal.GetExistingAsync(item, userId); // if (existingItem.InstanceParent == null) // { // existingItem.InstanceParent = item.InstanceParent; // try // { // await dalContext.UpsertAsync(existingItem, true); // } // catch (Exception ex) // { // Logger.LogError(ex.InnerException != null ? ex.InnerException : ex , $"Error updating instance parent for {existingItem} to {item.InstanceParent}"); // } // } //} if ((profile.ID ?? 0) == 0) { // Ensure that the Profile has an ID, as it is referenced by the imported NodeModels. var writtenProfile = await _nsDal.GetExistingAsync(profile, userToken); profile.ID = writtenProfile?.ID; } return(profileItems); }