06/27/2022
I'm writing a simple OPC Client tool that downloads all OPC Tag / Variables with Qualified name, Data Type, Address, Array dimensions, Descriptions, Client Access Level. Below is my code snippet, it takes a while to load all tags, wondering we have any optimized way to query all tags at once.
public async Task<List> GetAllVariableDefs()
{
List tags = new List();string[] attr = new string[]
{
"_Address",
"_Description",
"_ScalingType",
"_RawDataType",
"_ClientAccess",
};var ret = BrowseAllVariables();
foreach (var item in ret)
{
try
{
NodeId localId = ExpandedNodeId.ToNodeId(item.NodeId, Session.NamespaceUris);
OPCRawTag rawTag = new OPCRawTag(localId.Identifier.ToString());
var ret2 = BrowseAllVariables(localId);
foreach (var item2 in ret2)
{
var vf = ((VariableNode)item2);
if (attr.ToList().Contains(item2.DisplayName.Text))
{
localId = ExpandedNodeId.ToNodeId(item2.NodeId, Session.NamespaceUris);
var n3 = Session.ReadValue(localId);
rawTag.Values.Add(item2.DisplayName.Text, n3.Value?.ToString());
}
}tags.Add(new OPCTag(rawTag));
}
catch(Exception ex)
{}
}
return tags;
}
private IList BrowseAllVariables(NodeId nodeId=null)
{
var result = new List();
var nodesToBrowse = new ExpandedNodeIdCollection {
nodeId == null ? ObjectIds.ObjectsFolder : nodeId
};while (nodesToBrowse.Count > 0)
{
var nextNodesToBrowse = new ExpandedNodeIdCollection();
foreach (var node in nodesToBrowse)
{
try
{
var organizers = Session.NodeCache.FindReferences(
node,
ReferenceTypeIds.Organizes,
false,
true);
var components = Session.NodeCache.FindReferences(
node,
ReferenceTypeIds.HasComponent,
false,
true);
var properties = Session.NodeCache.FindReferences(
node,
ReferenceTypeIds.HasProperty,
false,
true);
nextNodesToBrowse.AddRange(organizers
.Where(n => n is ObjectNode)
.Select(n => n.NodeId).ToList());
nextNodesToBrowse.AddRange(components
.Where(n => n is ObjectNode)
.Select(n => n.NodeId).ToList());
result.AddRange(organizers.Where(n => n is VariableNode));
result.AddRange(components.Where(n => n is VariableNode));
result.AddRange(properties.Where(n => n is VariableNode));
}
catch (ServiceResultException sre)
{
if (sre.StatusCode == StatusCodes.BadUserAccessDenied)
{
Console.WriteLine($"Access denied: Skip node {node}.");
}
}
}
nodesToBrowse = nextNodesToBrowse;
}
return result;
}
public class OPCTag
{
public string TagName { get; set; }
public string Address { get; set; }
public string DataType { get; set; }
public string ClientAccess { get; set; }
public string Description { get; set; }
public string ScalingType { get; set; }public OPCTag()
{}
public OPCTag(string name)
{
TagName = name;
}
public OPCTag(OPCRawTag rawTag)
{
TagName = rawTag.Name;
Address = rawTag.Values.ContainsKey("_Address") ? rawTag.Values["_Address"] : string.Empty;
Description = rawTag.Values.ContainsKey("_Description") ? rawTag.Values["_Description"] : string.Empty;
ScalingType = rawTag.Values.ContainsKey("_ScalingType") ? rawTag.Values["_ScalingType"] : string.Empty;
ClientAccess = rawTag.Values.ContainsKey("_ClientAccess") ? rawTag.Values["_ClientAccess"] : string.Empty;
DataType = rawTag.Values.ContainsKey("_RawDataType") ? rawTag.Values["_RawDataType"] : string.Empty;
}
}public class OPCRawTag
{
public string Name { get; set; }
public Dictionary<string,string> Values { get; set; }
public OPCRawTag(string name="")
{
Name = name;
Values = new Dictionary<string, string>();
}
}
06/27/2022
we have an application that our end user need to query / search all tag variables that would be better viewed on one single data grid, the other client tools we have all has tree list structure, but we want everything on one single data grid solution without any drill down and expand / collapse. my end user would select any row to copy the tag name to enter in other windows of our applications as well. the tagnames shown on the data grid would be shown as full qualified name, e.g. Channel1.Device1.Tag1. In our use case we would have maximum 100 - 300 data tag variables maximum.
05/30/2017
Almost every UA server has more than 300 "tags" so you will have problems with this design.
What you need to do is base your screen on ObjectType templates.
i.e. you don't show everything and instead limit your view to an instance of object that your view is configured to use.
For example, a server may have a boiler instance Boiler1 described by a BoilerType.
You would build a list of "tags" to look for by recursively browsing for instance declarations in the BoilerType.
You would then use TranslateBrowsePathsToNodeIds to get the matching Nodes in the Boiler1 instance.
The user could then switch instances to Boiler2 and you would not need browser BoilerType - you would just call TranslateBrowsePathsToNodeIds again to get the matching Nodes in the Boiler2 instance.
A sample server with a Boiler model to experiment with:
https://github.com/OPCFoundati.....hop/Boiler
Reference on how to get the "tag" names from the ObjectType:
1 Guest(s)