using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Opc.Ua;
using Opc.Ua.Client;
namespace OpcUaHelper
{
///
/// Defines numerous re-useable utility functions.
///
public partial class ClientUtils
{
///
/// Handles an exception.
///
public static void HandleException( string caption, Exception e )
{
ExceptionDlg.Show( caption, e );
}
///
/// Returns the application icon.
///
public static System.Drawing.Icon GetAppIcon()
{
try
{
return new Icon( "App.ico" );
}
catch (Exception)
{
return null;
}
}
#region DisplayText Lookup
///
/// Gets the display text for the access level attribute.
///
/// The access level.
/// The access level formatted as a string.
public static string GetAccessLevelDisplayText( byte accessLevel )
{
StringBuilder buffer = new StringBuilder( );
if (accessLevel == AccessLevels.None)
{
buffer.Append( "None" );
}
if ((accessLevel & AccessLevels.CurrentRead) == AccessLevels.CurrentRead)
{
buffer.Append( "Read" );
}
if ((accessLevel & AccessLevels.CurrentWrite) == AccessLevels.CurrentWrite)
{
if (buffer.Length > 0)
{
buffer.Append( " | " );
}
buffer.Append( "Write" );
}
if ((accessLevel & AccessLevels.HistoryRead) == AccessLevels.HistoryRead)
{
if (buffer.Length > 0)
{
buffer.Append( " | " );
}
buffer.Append( "HistoryRead" );
}
if ((accessLevel & AccessLevels.HistoryWrite) == AccessLevels.HistoryWrite)
{
if (buffer.Length > 0)
{
buffer.Append( " | " );
}
buffer.Append( "HistoryWrite" );
}
if ((accessLevel & AccessLevels.SemanticChange) == AccessLevels.SemanticChange)
{
if (buffer.Length > 0)
{
buffer.Append( " | " );
}
buffer.Append( "SemanticChange" );
}
return buffer.ToString( );
}
///
/// Gets the display text for the event notifier attribute.
///
/// The event notifier.
/// The event notifier formatted as a string.
public static string GetEventNotifierDisplayText( byte eventNotifier )
{
StringBuilder buffer = new StringBuilder( );
if (eventNotifier == EventNotifiers.None)
{
buffer.Append( "None" );
}
if ((eventNotifier & EventNotifiers.SubscribeToEvents) == EventNotifiers.SubscribeToEvents)
{
buffer.Append( "Subscribe" );
}
if ((eventNotifier & EventNotifiers.HistoryRead) == EventNotifiers.HistoryRead)
{
if (buffer.Length > 0)
{
buffer.Append( " | " );
}
buffer.Append( "HistoryRead" );
}
if ((eventNotifier & EventNotifiers.HistoryWrite) == EventNotifiers.HistoryWrite)
{
if (buffer.Length > 0)
{
buffer.Append( " | " );
}
buffer.Append( "HistoryWrite" );
}
return buffer.ToString( );
}
///
/// Gets the display text for the value rank attribute.
///
/// The value rank.
/// The value rank formatted as a string.
public static string GetValueRankDisplayText( int valueRank )
{
switch (valueRank)
{
case ValueRanks.Any: return "Any";
case ValueRanks.Scalar: return "Scalar";
case ValueRanks.ScalarOrOneDimension: return "ScalarOrOneDimension";
case ValueRanks.OneOrMoreDimensions: return "OneOrMoreDimensions";
case ValueRanks.OneDimension: return "OneDimension";
case ValueRanks.TwoDimensions: return "TwoDimensions";
}
return valueRank.ToString( );
}
///
/// Gets the display text for the specified attribute.
///
/// The currently active session.
/// The id of the attribute.
/// The value of the attribute.
/// The attribute formatted as a string.
public static string GetAttributeDisplayText( Session session, uint attributeId, Variant value )
{
if (value == Variant.Null)
{
return String.Empty;
}
switch (attributeId)
{
case Attributes.AccessLevel:
case Attributes.UserAccessLevel:
{
byte? field = value.Value as byte?;
if (field != null)
{
return GetAccessLevelDisplayText( field.Value );
}
break;
}
case Attributes.EventNotifier:
{
byte? field = value.Value as byte?;
if (field != null)
{
return GetEventNotifierDisplayText( field.Value );
}
break;
}
case Attributes.DataType:
{
return session.NodeCache.GetDisplayText( value.Value as NodeId );
}
case Attributes.ValueRank:
{
int? field = value.Value as int?;
if (field != null)
{
return GetValueRankDisplayText( field.Value );
}
break;
}
case Attributes.NodeClass:
{
int? field = value.Value as int?;
if (field != null)
{
return ((NodeClass)field.Value).ToString( );
}
break;
}
case Attributes.NodeId:
{
NodeId field = value.Value as NodeId;
if (!NodeId.IsNull( field ))
{
return field.ToString( );
}
return "Null";
}
}
// check for byte strings.
if (value.Value is byte[])
{
return Utils.ToHexString( value.Value as byte[] );
}
// use default format.
return value.ToString( );
}
#endregion
#region Browse
///
/// Browses the address space and returns the references found.
///
/// The session.
/// The set of browse operations to perform.
/// if set to true a exception will be thrown on an error.
///
/// The references found. Null if an error occurred.
///
public static ReferenceDescriptionCollection Browse( Session session, BrowseDescriptionCollection nodesToBrowse, bool throwOnError )
{
return Browse( session, null, nodesToBrowse, throwOnError );
}
///
/// Browses the address space and returns the references found.
///
public static ReferenceDescriptionCollection Browse( Session session, ViewDescription view, BrowseDescriptionCollection nodesToBrowse, bool throwOnError )
{
try
{
ReferenceDescriptionCollection references = new ReferenceDescriptionCollection( );
while (nodesToBrowse.Count > 0)
{
// start the browse operation.
BrowseResultCollection results = null;
DiagnosticInfoCollection diagnosticInfos = null;
session.Browse(
null,
view,
0,
nodesToBrowse,
out results,
out diagnosticInfos );
ClientBase.ValidateResponse( results, nodesToBrowse );
ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToBrowse );
ByteStringCollection continuationPoints = new ByteStringCollection( );
BrowseDescriptionCollection unprocessedOperations = new BrowseDescriptionCollection( );
for (int ii = 0; ii < nodesToBrowse.Count; ii++)
{
// check for error.
if (StatusCode.IsBad( results[ii].StatusCode ))
{
// this error indicates that the server does not have enough simultaneously active
// continuation points. This request will need to be resent after the other operations
// have been completed and their continuation points released.
if (results[ii].StatusCode == StatusCodes.BadNoContinuationPoints)
{
unprocessedOperations.Add( nodesToBrowse[ii] );
}
continue;
}
// check if all references have been fetched.
if (results[ii].References.Count == 0)
{
continue;
}
// save results.
references.AddRange( results[ii].References );
// check for continuation point.
if (results[ii].ContinuationPoint != null)
{
continuationPoints.Add( results[ii].ContinuationPoint );
}
}
// process continuation points.
while (continuationPoints.Count > 0)
{
// continue browse operation.
session.BrowseNext(
null,
false,
continuationPoints,
out results,
out diagnosticInfos );
ClientBase.ValidateResponse( results, continuationPoints );
ClientBase.ValidateDiagnosticInfos( diagnosticInfos, continuationPoints );
ByteStringCollection revisedContinuationPoints = new ByteStringCollection( );
for (int ii = 0; ii < continuationPoints.Count; ii++)
{
// check for error.
if (StatusCode.IsBad( results[ii].StatusCode ))
{
continue;
}
// check if all references have been fetched.
if (results[ii].References.Count == 0)
{
continue;
}
// save results.
references.AddRange( results[ii].References );
// check for continuation point.
if (results[ii].ContinuationPoint != null)
{
revisedContinuationPoints.Add( results[ii].ContinuationPoint );
}
}
// check if browsing must continue;
continuationPoints = revisedContinuationPoints;
}
// check if unprocessed results exist.
nodesToBrowse = unprocessedOperations;
}
// return complete list.
return references;
}
catch (Exception exception)
{
if (throwOnError)
{
throw new ServiceResultException( exception, StatusCodes.BadUnexpectedError );
}
return null;
}
}
///
/// Browses the address space and returns the references found.
///
/// The session.
/// The NodeId for the starting node.
/// if set to true a exception will be thrown on an error.
///
/// The references found. Null if an error occurred.
///
public static ReferenceDescriptionCollection Browse( Session session, BrowseDescription nodeToBrowse, bool throwOnError )
{
return Browse( session, null, nodeToBrowse, throwOnError );
}
///
/// Browses the address space and returns the references found.
///
public static ReferenceDescriptionCollection Browse( Session session, ViewDescription view, BrowseDescription nodeToBrowse, bool throwOnError )
{
try
{
ReferenceDescriptionCollection references = new ReferenceDescriptionCollection( );
// construct browse request.
BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection( );
nodesToBrowse.Add( nodeToBrowse );
// start the browse operation.
BrowseResultCollection results = null;
DiagnosticInfoCollection diagnosticInfos = null;
session.Browse(
null,
view,
0,
nodesToBrowse,
out results,
out diagnosticInfos );
ClientBase.ValidateResponse( results, nodesToBrowse );
ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToBrowse );
do
{
// check for error.
if (StatusCode.IsBad( results[0].StatusCode ))
{
throw new ServiceResultException( results[0].StatusCode );
}
// process results.
for (int ii = 0; ii < results[0].References.Count; ii++)
{
references.Add( results[0].References[ii] );
}
// check if all references have been fetched.
if (results[0].References.Count == 0 || results[0].ContinuationPoint == null)
{
break;
}
// continue browse operation.
ByteStringCollection continuationPoints = new ByteStringCollection( );
continuationPoints.Add( results[0].ContinuationPoint );
session.BrowseNext(
null,
false,
continuationPoints,
out results,
out diagnosticInfos );
ClientBase.ValidateResponse( results, continuationPoints );
ClientBase.ValidateDiagnosticInfos( diagnosticInfos, continuationPoints );
}
while (true);
//return complete list.
return references;
}
catch (Exception exception)
{
if (throwOnError)
{
throw new ServiceResultException( exception, StatusCodes.BadUnexpectedError );
}
return null;
}
}
///
/// Browses the address space and returns all of the supertypes of the specified type node.
///
/// The session.
/// The NodeId for a type node in the address space.
/// if set to true a exception will be thrown on an error.
///
/// The references found. Null if an error occurred.
///
public static ReferenceDescriptionCollection BrowseSuperTypes( Session session, NodeId typeId, bool throwOnError )
{
ReferenceDescriptionCollection supertypes = new ReferenceDescriptionCollection( );
try
{
// find all of the children of the field.
BrowseDescription nodeToBrowse = new BrowseDescription( );
nodeToBrowse.NodeId = typeId;
nodeToBrowse.BrowseDirection = BrowseDirection.Inverse;
nodeToBrowse.ReferenceTypeId = ReferenceTypeIds.HasSubtype;
nodeToBrowse.IncludeSubtypes = false; // more efficient to use IncludeSubtypes=False when possible.
nodeToBrowse.NodeClassMask = 0; // the HasSubtype reference already restricts the targets to Types.
nodeToBrowse.ResultMask = (uint)BrowseResultMask.All;
ReferenceDescriptionCollection references = Browse( session, nodeToBrowse, throwOnError );
while (references != null && references.Count > 0)
{
// should never be more than one supertype.
supertypes.Add( references[0] );
// only follow references within this server.
if (references[0].NodeId.IsAbsolute)
{
break;
}
// get the references for the next level up.
nodeToBrowse.NodeId = (NodeId)references[0].NodeId;
references = Browse( session, nodeToBrowse, throwOnError );
}
// return complete list.
return supertypes;
}
catch (Exception exception)
{
if (throwOnError)
{
throw new ServiceResultException( exception, StatusCodes.BadUnexpectedError );
}
return null;
}
}
///
/// Returns the node ids for a set of relative paths.
///
/// An open session with the server to use.
/// The starting node for the relative paths.
/// The namespace URIs referenced by the relative paths.
/// The relative paths.
/// A collection of local nodes.
public static List TranslateBrowsePaths(
Session session,
NodeId startNodeId,
NamespaceTable namespacesUris,
params string[] relativePaths )
{
// build the list of browse paths to follow by parsing the relative paths.
BrowsePathCollection browsePaths = new BrowsePathCollection( );
if (relativePaths != null)
{
for (int ii = 0; ii < relativePaths.Length; ii++)
{
BrowsePath browsePath = new BrowsePath( );
// The relative paths used indexes in the namespacesUris table. These must be
// converted to indexes used by the server. An error occurs if the relative path
// refers to a namespaceUri that the server does not recognize.
// The relative paths may refer to ReferenceType by their BrowseName. The TypeTree object
// allows the parser to look up the server's NodeId for the ReferenceType.
browsePath.RelativePath = RelativePath.Parse(
relativePaths[ii],
session.TypeTree,
namespacesUris,
session.NamespaceUris );
browsePath.StartingNode = startNodeId;
browsePaths.Add( browsePath );
}
}
// make the call to the server.
BrowsePathResultCollection results;
DiagnosticInfoCollection diagnosticInfos;
ResponseHeader responseHeader = session.TranslateBrowsePathsToNodeIds(
null,
browsePaths,
out results,
out diagnosticInfos );
// ensure that the server returned valid results.
Session.ValidateResponse( results, browsePaths );
Session.ValidateDiagnosticInfos( diagnosticInfos, browsePaths );
// collect the list of node ids found.
List nodes = new List( );
for (int ii = 0; ii < results.Count; ii++)
{
// check if the start node actually exists.
if (StatusCode.IsBad( results[ii].StatusCode ))
{
nodes.Add( null );
continue;
}
// an empty list is returned if no node was found.
if (results[ii].Targets.Count == 0)
{
nodes.Add( null );
continue;
}
// Multiple matches are possible, however, the node that matches the type model is the
// one we are interested in here. The rest can be ignored.
BrowsePathTarget target = results[ii].Targets[0];
if (target.RemainingPathIndex != UInt32.MaxValue)
{
nodes.Add( null );
continue;
}
// The targetId is an ExpandedNodeId because it could be node in another server.
// The ToNodeId function is used to convert a local NodeId stored in a ExpandedNodeId to a NodeId.
nodes.Add( ExpandedNodeId.ToNodeId( target.TargetId, session.NamespaceUris ) );
}
// return whatever was found.
return nodes;
}
#endregion
#region Events
///
/// Finds the type of the event for the notification.
///
/// The monitored item.
/// The notification.
/// The NodeId of the EventType.
public static NodeId FindEventType( MonitoredItem monitoredItem, EventFieldList notification )
{
EventFilter filter = monitoredItem.Status.Filter as EventFilter;
if (filter != null)
{
for (int ii = 0; ii < filter.SelectClauses.Count; ii++)
{
SimpleAttributeOperand clause = filter.SelectClauses[ii];
if (clause.BrowsePath.Count == 1 && clause.BrowsePath[0] == BrowseNames.EventType)
{
return notification.EventFields[ii].Value as NodeId;
}
}
}
return null;
}
///
/// Constructs an event object from a notification.
///
/// The session.
/// The monitored item that produced the notification.
/// The notification.
/// The known event types.
/// Mapping between event types and known event types.
///
/// The event object. Null if the notification is not a valid event type.
///
public static BaseEventState ConstructEvent(
Session session,
MonitoredItem monitoredItem,
EventFieldList notification,
Dictionary knownEventTypes,
Dictionary eventTypeMappings )
{
// find the event type.
NodeId eventTypeId = FindEventType( monitoredItem, notification );
if (eventTypeId == null)
{
return null;
}
// look up the known event type.
Type knownType = null;
NodeId knownTypeId = null;
if (eventTypeMappings.TryGetValue( eventTypeId, out knownTypeId ))
{
knownType = knownEventTypes[knownTypeId];
}
// try again.
if (knownType == null)
{
if (knownEventTypes.TryGetValue( eventTypeId, out knownType ))
{
knownTypeId = eventTypeId;
eventTypeMappings.Add( eventTypeId, eventTypeId );
}
}
// try mapping it to a known type.
if (knownType == null)
{
// browse for the supertypes of the event type.
ReferenceDescriptionCollection supertypes = ClientUtils.BrowseSuperTypes( session, eventTypeId, false );
// can't do anything with unknown types.
if (supertypes == null)
{
return null;
}
// find the first supertype that matches a known event type.
for (int ii = 0; ii < supertypes.Count; ii++)
{
NodeId superTypeId = (NodeId)supertypes[ii].NodeId;
if (knownEventTypes.TryGetValue( superTypeId, out knownType ))
{
knownTypeId = superTypeId;
eventTypeMappings.Add( eventTypeId, superTypeId );
}
if (knownTypeId != null)
{
break;
}
}
// can't do anything with unknown types.
if (knownTypeId == null)
{
return null;
}
}
// construct the event based on the known event type.
BaseEventState e = (BaseEventState)Activator.CreateInstance( knownType, new object[] { (NodeState)null } );
// get the filter which defines the contents of the notification.
EventFilter filter = monitoredItem.Status.Filter as EventFilter;
// initialize the event with the values in the notification.
e.Update( session.SystemContext, filter.SelectClauses, notification );
// save the orginal notification.
e.Handle = notification;
return e;
}
#endregion
#region Type Model Browsing
///
/// Collects the instance declarations for a type.
///
public static List CollectInstanceDeclarationsForType( Session session, NodeId typeId )
{
return CollectInstanceDeclarationsForType( session, typeId, true );
}
///
/// Collects the instance declarations for a type.
///
public static List CollectInstanceDeclarationsForType( Session session, NodeId typeId, bool includeSupertypes )
{
// process the types starting from the top of the tree.
List instances = new List( );
Dictionary map = new Dictionary( );
// get the supertypes.
if (includeSupertypes)
{
ReferenceDescriptionCollection supertypes = ClientUtils.BrowseSuperTypes( session, typeId, false );
if (supertypes != null)
{
for (int ii = supertypes.Count - 1; ii >= 0; ii--)
{
CollectInstanceDeclarations( session, (NodeId)supertypes[ii].NodeId, null, instances, map );
}
}
}
// collect the fields for the selected type.
CollectInstanceDeclarations( session, typeId, null, instances, map );
// return the complete list.
return instances;
}
///
/// Collects the fields for the instance node.
///
private static void CollectInstanceDeclarations(
Session session,
NodeId typeId,
InstanceDeclaration parent,
List instances,
IDictionary map )
{
// find the children.
BrowseDescription nodeToBrowse = new BrowseDescription( );
if (parent == null)
{
nodeToBrowse.NodeId = typeId;
}
else
{
nodeToBrowse.NodeId = parent.NodeId;
}
nodeToBrowse.BrowseDirection = BrowseDirection.Forward;
nodeToBrowse.ReferenceTypeId = ReferenceTypeIds.HasChild;
nodeToBrowse.IncludeSubtypes = true;
nodeToBrowse.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable | NodeClass.Method);
nodeToBrowse.ResultMask = (uint)BrowseResultMask.All;
// ignore any browsing errors.
ReferenceDescriptionCollection references = ClientUtils.Browse( session, nodeToBrowse, false );
if (references == null)
{
return;
}
// process the children.
List nodeIds = new List( );
List children = new List( );
for (int ii = 0; ii < references.Count; ii++)
{
ReferenceDescription reference = references[ii];
if (reference.NodeId.IsAbsolute)
{
continue;
}
// create a new declaration.
InstanceDeclaration child = new InstanceDeclaration( );
child.RootTypeId = typeId;
child.NodeId = (NodeId)reference.NodeId;
child.BrowseName = reference.BrowseName;
child.NodeClass = reference.NodeClass;
if (!LocalizedText.IsNullOrEmpty( reference.DisplayName ))
{
child.DisplayName = reference.DisplayName.Text;
}
else
{
child.DisplayName = reference.BrowseName.Name;
}
if (parent != null)
{
child.BrowsePath = new QualifiedNameCollection( parent.BrowsePath );
child.BrowsePathDisplayText = Utils.Format( "{0}/{1}", parent.BrowsePathDisplayText, reference.BrowseName );
child.DisplayPath = Utils.Format( "{0}/{1}", parent.DisplayPath, reference.DisplayName );
}
else
{
child.BrowsePath = new QualifiedNameCollection( );
child.BrowsePathDisplayText = Utils.Format( "{0}", reference.BrowseName );
child.DisplayPath = Utils.Format( "{0}", reference.DisplayName );
}
child.BrowsePath.Add( reference.BrowseName );
// check if reading an overridden declaration.
InstanceDeclaration overriden = null;
if (map.TryGetValue( child.BrowsePathDisplayText, out overriden ))
{
child.OverriddenDeclaration = overriden;
}
map[child.BrowsePathDisplayText] = child;
// add to list.
children.Add( child );
nodeIds.Add( child.NodeId );
}
// check if nothing more to do.
if (children.Count == 0)
{
return;
}
// find the modelling rules.
List modellingRules = FindTargetOfReference( session, nodeIds, Opc.Ua.ReferenceTypeIds.HasModellingRule, false );
if (modellingRules != null)
{
for (int ii = 0; ii < nodeIds.Count; ii++)
{
children[ii].ModellingRule = modellingRules[ii];
// if the modelling rule is null then the instance is not part of the type declaration.
if (NodeId.IsNull( modellingRules[ii] ))
{
map.Remove( children[ii].BrowsePathDisplayText );
}
}
}
// update the descriptions.
UpdateInstanceDescriptions( session, children, false );
// recusively collect instance declarations for the tree below.
for (int ii = 0; ii < children.Count; ii++)
{
if (!NodeId.IsNull( children[ii].ModellingRule ))
{
instances.Add( children[ii] );
CollectInstanceDeclarations( session, typeId, children[ii], instances, map );
}
}
}
///
/// Finds the targets for the specified reference.
///
private static List FindTargetOfReference( Session session, List nodeIds, NodeId referenceTypeId, bool throwOnError )
{
try
{
// construct browse request.
BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection( );
for (int ii = 0; ii < nodeIds.Count; ii++)
{
BrowseDescription nodeToBrowse = new BrowseDescription( );
nodeToBrowse.NodeId = nodeIds[ii];
nodeToBrowse.BrowseDirection = BrowseDirection.Forward;
nodeToBrowse.ReferenceTypeId = referenceTypeId;
nodeToBrowse.IncludeSubtypes = false;
nodeToBrowse.NodeClassMask = 0;
nodeToBrowse.ResultMask = (uint)BrowseResultMask.None;
nodesToBrowse.Add( nodeToBrowse );
}
// start the browse operation.
BrowseResultCollection results = null;
DiagnosticInfoCollection diagnosticInfos = null;
session.Browse(
null,
null,
1,
nodesToBrowse,
out results,
out diagnosticInfos );
ClientBase.ValidateResponse( results, nodesToBrowse );
ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToBrowse );
List targetIds = new List( );
ByteStringCollection continuationPoints = new ByteStringCollection( );
for (int ii = 0; ii < nodeIds.Count; ii++)
{
targetIds.Add( null );
// check for error.
if (StatusCode.IsBad( results[ii].StatusCode ))
{
continue;
}
// check for continuation point.
if (results[ii].ContinuationPoint != null && results[ii].ContinuationPoint.Length > 0)
{
continuationPoints.Add( results[ii].ContinuationPoint );
}
// get the node id.
if (results[ii].References.Count > 0)
{
if (NodeId.IsNull( results[ii].References[0].NodeId ) || results[ii].References[0].NodeId.IsAbsolute)
{
continue;
}
targetIds[ii] = (NodeId)results[ii].References[0].NodeId;
}
}
// release continuation points.
if (continuationPoints.Count > 0)
{
session.BrowseNext(
null,
true,
continuationPoints,
out results,
out diagnosticInfos );
ClientBase.ValidateResponse( results, nodesToBrowse );
ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToBrowse );
}
//return complete list.
return targetIds;
}
catch (Exception exception)
{
if (throwOnError)
{
throw new ServiceResultException( exception, StatusCodes.BadUnexpectedError );
}
return null;
}
}
///
/// Finds the targets for the specified reference.
///
private static void UpdateInstanceDescriptions( Session session, List instances, bool throwOnError )
{
try
{
ReadValueIdCollection nodesToRead = new ReadValueIdCollection( );
for (int ii = 0; ii < instances.Count; ii++)
{
ReadValueId nodeToRead = new ReadValueId( );
nodeToRead.NodeId = instances[ii].NodeId;
nodeToRead.AttributeId = Attributes.Description;
nodesToRead.Add( nodeToRead );
nodeToRead = new ReadValueId( );
nodeToRead.NodeId = instances[ii].NodeId;
nodeToRead.AttributeId = Attributes.DataType;
nodesToRead.Add( nodeToRead );
nodeToRead = new ReadValueId( );
nodeToRead.NodeId = instances[ii].NodeId;
nodeToRead.AttributeId = Attributes.ValueRank;
nodesToRead.Add( nodeToRead );
}
// start the browse operation.
DataValueCollection results = null;
DiagnosticInfoCollection diagnosticInfos = null;
session.Read(
null,
0,
TimestampsToReturn.Neither,
nodesToRead,
out results,
out diagnosticInfos );
ClientBase.ValidateResponse( results, nodesToRead );
ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToRead );
// update the instances.
for (int ii = 0; ii < nodesToRead.Count; ii += 3)
{
InstanceDeclaration instance = instances[ii / 3];
instance.Description = results[ii].GetValue( LocalizedText.Null ).Text;
instance.DataType = results[ii + 1].GetValue( NodeId.Null );
instance.ValueRank = results[ii + 2].GetValue( ValueRanks.Any );
if (!NodeId.IsNull( instance.DataType ))
{
instance.BuiltInType = DataTypes.GetBuiltInType( instance.DataType, session.TypeTree );
instance.DataTypeDisplayText = session.NodeCache.GetDisplayText( instance.DataType );
if (instance.ValueRank >= 0)
{
instance.DataTypeDisplayText += "[]";
}
}
}
}
catch (Exception exception)
{
if (throwOnError)
{
throw new ServiceResultException( exception, StatusCodes.BadUnexpectedError );
}
}
}
#endregion
#region Private Methods
///
/// Collects the fields for the type.
///
/// The session.
/// The type id.
/// The fields.
/// The node id for the declaration of the field.
private static void CollectFieldsForType( Session session, NodeId typeId, SimpleAttributeOperandCollection fields, List fieldNodeIds )
{
// get the supertypes.
ReferenceDescriptionCollection supertypes = ClientUtils.BrowseSuperTypes( session, typeId, false );
if (supertypes == null)
{
return;
}
// process the types starting from the top of the tree.
Dictionary foundNodes = new Dictionary( );
QualifiedNameCollection parentPath = new QualifiedNameCollection( );
for (int ii = supertypes.Count - 1; ii >= 0; ii--)
{
CollectFields( session, (NodeId)supertypes[ii].NodeId, parentPath, fields, fieldNodeIds, foundNodes );
}
// collect the fields for the selected type.
CollectFields( session, typeId, parentPath, fields, fieldNodeIds, foundNodes );
}
///
/// Collects the fields for the instance.
///
/// The session.
/// The instance id.
/// The fields.
/// The node id for the declaration of the field.
private static void CollectFieldsForInstance( Session session, NodeId instanceId, SimpleAttributeOperandCollection fields, List fieldNodeIds )
{
Dictionary foundNodes = new Dictionary( );
QualifiedNameCollection parentPath = new QualifiedNameCollection( );
CollectFields( session, instanceId, parentPath, fields, fieldNodeIds, foundNodes );
}
///
/// Collects the fields for the instance node.
///
/// The session.
/// The node id.
/// The parent path.
/// The event fields.
/// The node id for the declaration of the field.
/// The table of found nodes.
private static void CollectFields(
Session session,
NodeId nodeId,
QualifiedNameCollection parentPath,
SimpleAttributeOperandCollection fields,
List fieldNodeIds,
Dictionary foundNodes )
{
// find all of the children of the field.
BrowseDescription nodeToBrowse = new BrowseDescription( );
nodeToBrowse.NodeId = nodeId;
nodeToBrowse.BrowseDirection = BrowseDirection.Forward;
nodeToBrowse.ReferenceTypeId = ReferenceTypeIds.Aggregates;
nodeToBrowse.IncludeSubtypes = true;
nodeToBrowse.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable);
nodeToBrowse.ResultMask = (uint)BrowseResultMask.All;
ReferenceDescriptionCollection children = ClientUtils.Browse( session, nodeToBrowse, false );
if (children == null)
{
return;
}
// process the children.
for (int ii = 0; ii < children.Count; ii++)
{
ReferenceDescription child = children[ii];
if (child.NodeId.IsAbsolute)
{
continue;
}
// construct browse path.
QualifiedNameCollection browsePath = new QualifiedNameCollection( parentPath );
browsePath.Add( child.BrowseName );
// check if the browse path is already in the list.
int index = ContainsPath( fields, browsePath );
if (index < 0)
{
SimpleAttributeOperand field = new SimpleAttributeOperand( );
field.TypeDefinitionId = ObjectTypeIds.BaseEventType;
field.BrowsePath = browsePath;
field.AttributeId = (child.NodeClass == NodeClass.Variable) ? Attributes.Value : Attributes.NodeId;
fields.Add( field );
fieldNodeIds.Add( (NodeId)child.NodeId );
}
// recusively find all of the children.
NodeId targetId = (NodeId)child.NodeId;
// need to guard against loops.
if (!foundNodes.ContainsKey( targetId ))
{
foundNodes.Add( targetId, browsePath );
CollectFields( session, (NodeId)child.NodeId, browsePath, fields, fieldNodeIds, foundNodes );
}
}
}
///
/// Determines whether the specified select clause contains the browse path.
///
/// The select clause.
/// The browse path.
///
/// true if the specified select clause contains path; otherwise, false.
///
private static int ContainsPath( SimpleAttributeOperandCollection selectClause, QualifiedNameCollection browsePath )
{
for (int ii = 0; ii < selectClause.Count; ii++)
{
SimpleAttributeOperand field = selectClause[ii];
if (field.BrowsePath.Count != browsePath.Count)
{
continue;
}
bool match = true;
for (int jj = 0; jj < field.BrowsePath.Count; jj++)
{
if (field.BrowsePath[jj] != browsePath[jj])
{
match = false;
break;
}
}
if (match)
{
return ii;
}
}
return -1;
}
#endregion
}
}