| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022 |
- using Opc.Ua;
- using Opc.Ua.Client;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace OpcUaHelper
- {
- /// <summary>
- /// 辅助类
- /// </summary>
- public class FormUtils
- {
- /// <summary>
- /// Gets the display text for the access level attribute.
- /// </summary>
- /// <param name="accessLevel">The access level.</param>
- /// <returns>The access level formatted as a string.</returns>
- private 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( );
- }
- /// <summary>
- /// Gets the display text for the event notifier attribute.
- /// </summary>
- /// <param name="eventNotifier">The event notifier.</param>
- /// <returns>The event notifier formatted as a string.</returns>
- private 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( );
- }
- /// <summary>
- /// Gets the display text for the value rank attribute.
- /// </summary>
- /// <param name="valueRank">The value rank.</param>
- /// <returns>The value rank formatted as a string.</returns>
- private 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( );
- }
- /// <summary>
- /// Gets the display text for the specified attribute.
- /// </summary>
- /// <param name="session">The currently active session.</param>
- /// <param name="attributeId">The id of the attribute.</param>
- /// <param name="value">The value of the attribute.</param>
- /// <returns>The attribute formatted as a string.</returns>
- 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( );
- }
- /// <summary>
- /// Discovers the servers on the local machine.
- /// </summary>
- /// <param name="configuration">The configuration.</param>
- /// <returns>A list of server urls.</returns>
- public static IList<string> DiscoverServers( ApplicationConfiguration configuration )
- {
- List<string> serverUrls = new List<string>( );
- // set a short timeout because this is happening in the drop down event.
- EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create( configuration );
- endpointConfiguration.OperationTimeout = 5000;
- // Connect to the local discovery server and find the available servers.
- using (DiscoveryClient client = DiscoveryClient.Create( new Uri( "opc.tcp://localhost:4840" ), endpointConfiguration ))
- {
- ApplicationDescriptionCollection servers = client.FindServers( null );
- // populate the drop down list with the discovery URLs for the available servers.
- for (int ii = 0; ii < servers.Count; ii++)
- {
- if (servers[ii].ApplicationType == ApplicationType.DiscoveryServer)
- {
- continue;
- }
- for (int jj = 0; jj < servers[ii].DiscoveryUrls.Count; jj++)
- {
- string discoveryUrl = servers[ii].DiscoveryUrls[jj];
- // Many servers will use the '/discovery' suffix for the discovery endpoint.
- // The URL without this prefix should be the base URL for the server.
- if (discoveryUrl.EndsWith( "/discovery" ))
- {
- discoveryUrl = discoveryUrl.Substring( 0, discoveryUrl.Length - "/discovery".Length );
- }
- // ensure duplicates do not get added.
- if (!serverUrls.Contains( discoveryUrl ))
- {
- serverUrls.Add( discoveryUrl );
- }
- }
- }
- }
- return serverUrls;
- }
- /// <summary>
- /// Finds the endpoint that best matches the current settings.
- /// </summary>
- /// <param name="discoveryUrl">The discovery URL.</param>
- /// <param name="useSecurity">if set to <c>true</c> select an endpoint that uses security.</param>
- /// <returns>The best available endpoint.</returns>
- public static EndpointDescription SelectEndpoint( string discoveryUrl, bool useSecurity )
- {
- // needs to add the '/discovery' back onto non-UA TCP URLs.
- if (!discoveryUrl.StartsWith( Utils.UriSchemeOpcTcp ))
- {
- if (!discoveryUrl.EndsWith( "/discovery" ))
- {
- discoveryUrl += "/discovery";
- }
- }
- // parse the selected URL.
- Uri uri = new Uri( discoveryUrl );
- // set a short timeout because this is happening in the drop down event.
- EndpointConfiguration configuration = EndpointConfiguration.Create( );
- configuration.OperationTimeout = 5000;
- EndpointDescription selectedEndpoint = null;
- // Connect to the server's discovery endpoint and find the available configuration.
- using (DiscoveryClient client = DiscoveryClient.Create( uri, configuration ))
- {
- EndpointDescriptionCollection endpoints = client.GetEndpoints( null );
- // select the best endpoint to use based on the selected URL and the UseSecurity checkbox.
- for (int ii = 0; ii < endpoints.Count; ii++)
- {
- EndpointDescription endpoint = endpoints[ii];
- // check for a match on the URL scheme.
- if (endpoint.EndpointUrl.StartsWith( uri.Scheme ))
- {
- // check if security was requested.
- if (useSecurity)
- {
- if (endpoint.SecurityMode == MessageSecurityMode.None)
- {
- continue;
- }
- }
- else
- {
- if (endpoint.SecurityMode != MessageSecurityMode.None)
- {
- continue;
- }
- }
- // pick the first available endpoint by default.
- if (selectedEndpoint == null)
- {
- selectedEndpoint = endpoint;
- }
- // The security level is a relative measure assigned by the server to the
- // endpoints that it returns. Clients should always pick the highest level
- // unless they have a reason not too.
- if (endpoint.SecurityLevel > selectedEndpoint.SecurityLevel)
- {
- selectedEndpoint = endpoint;
- }
- }
- }
- // pick the first available endpoint by default.
- if (selectedEndpoint == null && endpoints.Count > 0)
- {
- selectedEndpoint = endpoints[0];
- }
- }
- // if a server is behind a firewall it may return URLs that are not accessible to the client.
- // This problem can be avoided by assuming that the domain in the URL used to call
- // GetEndpoints can be used to access any of the endpoints. This code makes that conversion.
- // Note that the conversion only makes sense if discovery uses the same protocol as the endpoint.
- Uri endpointUrl = Utils.ParseUri( selectedEndpoint.EndpointUrl );
- if (endpointUrl != null && endpointUrl.Scheme == uri.Scheme)
- {
- UriBuilder builder = new UriBuilder( endpointUrl );
- builder.Host = uri.DnsSafeHost;
- builder.Port = uri.Port;
- selectedEndpoint.EndpointUrl = builder.ToString( );
- }
- // return the selected endpoint.
- return selectedEndpoint;
- }
- /// <summary>
- /// Browses the address space and returns the references found.
- /// </summary>
- /// <param name="session">The session.</param>
- /// <param name="nodesToBrowse">The set of browse operations to perform.</param>
- /// <param name="throwOnError">if set to <c>true</c> a exception will be thrown on an error.</param>
- /// <returns>
- /// The references found. Null if an error occurred.
- /// </returns>
- public static ReferenceDescriptionCollection Browse( ISession session, BrowseDescriptionCollection nodesToBrowse, bool throwOnError )
- {
- try
- {
- ReferenceDescriptionCollection references = new ReferenceDescriptionCollection( );
- BrowseDescriptionCollection unprocessedOperations = new BrowseDescriptionCollection( );
- while (nodesToBrowse.Count > 0)
- {
- // start the browse operation.
- BrowseResultCollection results = null;
- DiagnosticInfoCollection diagnosticInfos = null;
- session.Browse(
- null,
- null,
- 0,
- nodesToBrowse,
- out results,
- out diagnosticInfos );
- ClientBase.ValidateResponse( results, nodesToBrowse );
- ClientBase.ValidateDiagnosticInfos( diagnosticInfos, nodesToBrowse );
- ByteStringCollection continuationPoints = new ByteStringCollection( );
- 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.
- ByteStringCollection revisedContiuationPoints = new ByteStringCollection( );
- while (continuationPoints.Count > 0)
- {
- // continue browse operation.
- session.BrowseNext(
- null,
- true,
- continuationPoints,
- out results,
- out diagnosticInfos );
- ClientBase.ValidateResponse( results, continuationPoints );
- ClientBase.ValidateDiagnosticInfos( diagnosticInfos, continuationPoints );
- 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)
- {
- revisedContiuationPoints.Add( results[ii].ContinuationPoint );
- }
- }
- // check if browsing must continue;
- revisedContiuationPoints = continuationPoints;
- }
- // 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;
- }
- }
- /// <summary>
- /// Finds the type of the event for the notification.
- /// </summary>
- /// <param name="monitoredItem">The monitored item.</param>
- /// <param name="notification">The notification.</param>
- /// <returns>The NodeId of the EventType.</returns>
- 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;
- }
- /// <summary>
- /// Browses the address space and returns the references found.
- /// </summary>
- /// <param name="session">The session.</param>
- /// <param name="nodeToBrowse">The NodeId for the starting node.</param>
- /// <param name="throwOnError">if set to <c>true</c> a exception will be thrown on an error.</param>
- /// <returns>
- /// The references found. Null if an error occurred.
- /// </returns>
- public static ReferenceDescriptionCollection Browse( Session session, 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,
- null,
- 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;
- }
- }
- /// <summary>
- /// Browses the address space and returns all of the supertypes of the specified type node.
- /// </summary>
- /// <param name="session">The session.</param>
- /// <param name="typeId">The NodeId for a type node in the address space.</param>
- /// <param name="throwOnError">if set to <c>true</c> a exception will be thrown on an error.</param>
- /// <returns>
- /// The references found. Null if an error occurred.
- /// </returns>
- 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;
- }
- }
- /// <summary>
- /// Constructs an event object from a notification.
- /// </summary>
- /// <param name="session">The session.</param>
- /// <param name="monitoredItem">The monitored item that produced the notification.</param>
- /// <param name="notification">The notification.</param>
- /// <param name="knownEventTypes">The known event types.</param>
- /// <param name="eventTypeMappings">Mapping between event types and known event types.</param>
- /// <returns>
- /// The event object. Null if the notification is not a valid event type.
- /// </returns>
- public static BaseEventState ConstructEvent(
- Session session,
- MonitoredItem monitoredItem,
- EventFieldList notification,
- Dictionary<NodeId, Type> knownEventTypes,
- Dictionary<NodeId, NodeId> 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 = FormUtils.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;
- }
- /// <summary>
- /// Returns the node ids for a set of relative paths.
- /// </summary>
- /// <param name="session">An open session with the server to use.</param>
- /// <param name="startNodeId">The starting node for the relative paths.</param>
- /// <param name="namespacesUris">The namespace URIs referenced by the relative paths.</param>
- /// <param name="relativePaths">The relative paths.</param>
- /// <returns>A collection of local nodes.</returns>
- public static List<NodeId> 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<NodeId> nodes = new List<NodeId>( );
- 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;
- }
- /// <summary>
- /// Collects the fields for the type.
- /// </summary>
- /// <param name="session">The session.</param>
- /// <param name="fields">The fields.</param>
- /// <param name="fieldNodeIds">The node id for the declaration of the field.</param>
- public static void CollectFieldsForType( Session session, NodeId typeId, SimpleAttributeOperandCollection fields, List<NodeId> fieldNodeIds )
- {
- // get the supertypes.
- ReferenceDescriptionCollection supertypes = FormUtils.BrowseSuperTypes( session, typeId, false );
- if (supertypes == null)
- {
- return;
- }
- // process the types starting from the top of the tree.
- Dictionary<NodeId, QualifiedNameCollection> foundNodes = new Dictionary<NodeId, QualifiedNameCollection>( );
- 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 );
- }
- /// <summary>
- /// Collects the fields for the instance.
- /// </summary>
- /// <param name="session">The session.</param>
- /// <param name="fields">The fields.</param>
- /// <param name="fieldNodeIds">字段</param>
- /// <param name="instanceId">The node id for the declaration of the field.</param>
- public static void CollectFieldsForInstance( Session session, NodeId instanceId, SimpleAttributeOperandCollection fields, List<NodeId> fieldNodeIds )
- {
- Dictionary<NodeId, QualifiedNameCollection> foundNodes = new Dictionary<NodeId, QualifiedNameCollection>( );
- QualifiedNameCollection parentPath = new QualifiedNameCollection( );
- CollectFields( session, instanceId, parentPath, fields, fieldNodeIds, foundNodes );
- }
- /// <summary>
- /// Collects the fields for the instance node.
- /// </summary>
- /// <param name="session">The session.</param>
- /// <param name="nodeId">The node id.</param>
- /// <param name="parentPath">The parent path.</param>
- /// <param name="fields">The event fields.</param>
- /// <param name="fieldNodeIds">The node id for the declaration of the field.</param>
- /// <param name="foundNodes">The table of found nodes.</param>
- private static void CollectFields(
- Session session,
- NodeId nodeId,
- QualifiedNameCollection parentPath,
- SimpleAttributeOperandCollection fields,
- List<NodeId> fieldNodeIds,
- Dictionary<NodeId, QualifiedNameCollection> 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 = FormUtils.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 );
- }
- }
- }
- /// <summary>
- /// Determines whether the specified select clause contains the browse path.
- /// </summary>
- /// <param name="selectClause">The select clause.</param>
- /// <param name="browsePath">The browse path.</param>
- /// <returns>
- /// <c>true</c> if the specified select clause contains path; otherwise, <c>false</c>.
- /// </returns>
- 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;
- }
- }
- }
|