Пример #1
0
        public static LogEntry For(BrowseNextRequest request)
        {
            LogEntry entry = new LogEntry("BrowseNextRequest");

            entry.Add("RequestHeader", For(request.RequestHeader));
            entry.Add("ReleaseContinuationPoints", For(request.ReleaseContinuationPoints));
            return(entry);
        }
Пример #2
0
        public async Task BrowseNextAsync()
        {
            var response = new BrowseNextResponse();
            var request  = new BrowseNextRequest();
            var channel  = new TestRequestChannel(response);

            var ret = await channel.BrowseNextAsync(request);

            ret
            .Should().BeSameAs(response);

            channel.Request
            .Should().BeSameAs(request);
        }
Пример #3
0
        public async Task BrowseObjects()
        {
            var channel = new UaTcpSessionChannel(
                localDescription,
                certificateStore,
                new AnonymousIdentity(),
                EndpointUrl,
                SecurityPolicyUris.None,
                loggerFactory: loggerFactory);

            await channel.OpenAsync();

            var rds           = new List <ReferenceDescription>();
            var browseRequest = new BrowseRequest {
                NodesToBrowse = new[] { new BrowseDescription {
                                            NodeId = ExpandedNodeId.ToNodeId(ExpandedNodeId.Parse(ObjectIds.ObjectsFolder), channel.NamespaceUris), ReferenceTypeId = NodeId.Parse(ReferenceTypeIds.HierarchicalReferences), ResultMask = (uint)BrowseResultMask.TargetInfo, NodeClassMask = (uint)NodeClass.Unspecified, BrowseDirection = BrowseDirection.Forward, IncludeSubtypes = true
                                        } }, RequestedMaxReferencesPerNode = 1000
            };
            var browseResponse = await channel.BrowseAsync(browseRequest).ConfigureAwait(false);

            rds.AddRange(browseResponse.Results.Where(result => result.References != null).SelectMany(result => result.References));
            var continuationPoints = browseResponse.Results.Select(br => br.ContinuationPoint).Where(cp => cp != null).ToArray();

            while (continuationPoints.Length > 0)
            {
                var browseNextRequest = new BrowseNextRequest {
                    ContinuationPoints = continuationPoints, ReleaseContinuationPoints = false
                };
                var browseNextResponse = await channel.BrowseNextAsync(browseNextRequest);

                rds.AddRange(browseNextResponse.Results.Where(result => result.References != null).SelectMany(result => result.References));
                continuationPoints = browseNextResponse.Results.Select(br => br.ContinuationPoint).Where(cp => cp != null).ToArray();
            }

            rds
            .Should().NotBeEmpty();

            logger.LogInformation("+ Objects, 0:Objects, Object");
            foreach (var rd in rds)
            {
                logger.LogInformation("   + {0}, {1}, {2}", rd.DisplayName, rd.BrowseName, rd.NodeClass);
            }

            logger.LogInformation($"Closing session '{channel.SessionId}'.");
            await channel.CloseAsync();
        }
Пример #4
0
        private async Task LoadChildren(ReferenceDescriptionViewModel viewModel)
        {
            if (_session == null || _session.Disposed)
            {
                return;
            }
            try
            {
                if (!_session.Connected)
                {
                    await _session.ConnectAsync();
                }
                var browseRequest = new BrowseRequest {
                    NodesToBrowse = { new BrowseDescription {
                                          NodeId = ExpandedNodeId.ToNodeId(viewModel.NodeId, _session.NamespaceUris), ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences, ResultMask = (uint)BrowseResultMask.All, NodeClassMask = (uint)NodeClass.Variable | (uint)NodeClass.Object | (uint)NodeClass.Method, BrowseDirection = BrowseDirection.Forward
                                      } }
                };
                var browseResponse = await _session.BrowseAsync(browseRequest);

                foreach (var description in browseResponse.Results.SelectMany(result => result.References))
                {
                    viewModel.Children.Add(new ReferenceDescriptionViewModel(description, viewModel, LoadChildren));
                    await Task.Delay(50);
                }
                var continuationPoints = new ByteStringCollection(browseResponse.Results.Select(br => br.ContinuationPoint).Where(cp => null != cp));
                while (continuationPoints.Count > 0)
                {
                    var browseNextRequest = new BrowseNextRequest {
                        ContinuationPoints = continuationPoints, ReleaseContinuationPoints = false
                    };
                    var browseNextResponse = await _session.BrowseNextAsync(browseNextRequest);

                    foreach (var description in browseNextResponse.Results.SelectMany(result => result.References))
                    {
                        viewModel.Children.Add(new ReferenceDescriptionViewModel(description, viewModel, LoadChildren));
                        await Task.Delay(50);
                    }
                    continuationPoints = new ByteStringCollection(browseNextResponse.Results.Select(br => br.ContinuationPoint).Where(cp => null != cp));
                }
            }
            catch (OperationCanceledException)
            {
            }
        }
Пример #5
0
        private async Task <IList <ReferenceDescription> > BrowseTree(NodeId tree)
        {
            if (connection == null)
            {
                return(new ReferenceDescription[0]);
            }

            var browseRequest = new BrowseRequest {
                NodesToBrowse = new[] {
                    new BrowseDescription {
                        NodeId          = tree,
                        ReferenceTypeId = NodeId.Parse(ReferenceTypeIds.HierarchicalReferences),
                        ResultMask      = (uint)BrowseResultMask.TargetInfo,
                        NodeClassMask   = (uint)NodeClass.Object | (int)NodeClass.Variable,
                        BrowseDirection = BrowseDirection.Forward,
                        IncludeSubtypes = true
                    }
                },
                RequestedMaxReferencesPerNode = 1000
            };

            var            rds            = new List <ReferenceDescription>();
            BrowseResponse browseResponse = await connection.BrowseAsync(browseRequest);

            Workstation.ServiceModel.Ua.BrowseResult[] results = CleanNulls(browseResponse.Results).ToArray();
            rds.AddRange(results.SelectMany(result => CleanNulls(result.References)));

            var continuationPoints = results.Select(br => br.ContinuationPoint).Where(cp => cp != null).ToArray();

            while (continuationPoints.Length > 0 && connection != null)
            {
                var browseNextRequest = new BrowseNextRequest {
                    ContinuationPoints = continuationPoints, ReleaseContinuationPoints = false
                };
                var browseNextResponse = await connection.BrowseNextAsync(browseNextRequest);

                Workstation.ServiceModel.Ua.BrowseResult[] nextResults = CleanNulls(browseNextResponse.Results).ToArray();
                rds.AddRange(nextResults.SelectMany(result => CleanNulls(result.References)));
                continuationPoints = nextResults.Select(br => br.ContinuationPoint).Where(cp => cp != null).ToArray();
            }

            return(rds);
        }
Пример #6
0
        public async Task BrowseRoot()
        {
            var channel = new UaTcpSessionChannel(
                this.localDescription,
                this.certificateStore,
                new AnonymousIdentity(),
                EndpointUrl,
                SecurityPolicyUris.None,
                loggerFactory: this.loggerFactory);

            await channel.OpenAsync();

            var rds           = new List <ReferenceDescription>();
            var browseRequest = new BrowseRequest {
                NodesToBrowse = new[] { new BrowseDescription {
                                            NodeId = ExpandedNodeId.ToNodeId(ExpandedNodeId.Parse(ObjectIds.RootFolder), channel.NamespaceUris), ReferenceTypeId = NodeId.Parse(ReferenceTypeIds.HierarchicalReferences), ResultMask = (uint)BrowseResultMask.TargetInfo, NodeClassMask = (uint)NodeClass.Unspecified, BrowseDirection = BrowseDirection.Forward, IncludeSubtypes = true
                                        } }, RequestedMaxReferencesPerNode = 1000
            };
            var browseResponse = await channel.BrowseAsync(browseRequest).ConfigureAwait(false);

            rds.AddRange(browseResponse.Results.Where(result => result.References != null).SelectMany(result => result.References));
            var continuationPoints = browseResponse.Results.Select(br => br.ContinuationPoint).Where(cp => cp != null).ToArray();

            while (continuationPoints.Length > 0)
            {
                var browseNextRequest = new BrowseNextRequest {
                    ContinuationPoints = continuationPoints, ReleaseContinuationPoints = false
                };
                var browseNextResponse = await channel.BrowseNextAsync(browseNextRequest);

                rds.AddRange(browseNextResponse.Results.Where(result => result.References != null).SelectMany(result => result.References));
                continuationPoints = browseNextResponse.Results.Select(br => br.ContinuationPoint).Where(cp => cp != null).ToArray();
            }

            Assert.IsTrue(rds.Count == 3);

            Console.WriteLine($"Closing session '{channel.SessionId}'.");
            await channel.CloseAsync();
        }
Пример #7
0
        public async Task <BrowseNextResponse> BrowseNextAsync(BrowseNextRequest browseNextRequest)
        {
            UpdateRequestHeader(browseNextRequest, true, "BrowseNext");
            BrowseNextResponse browseNextResponse = null;

            try
            {
                if (UseTransportChannel)
                {
                    var serviceResponse = await Task <IServiceResponse> .Factory.FromAsync(TransportChannel.BeginSendRequest, TransportChannel.EndSendRequest, browseNextRequest, null).ConfigureAwait(false);

                    if (serviceResponse == null)
                    {
                        throw new ServiceResultException(StatusCodes.BadUnknownResponse);
                    }
                    ValidateResponse(serviceResponse.ResponseHeader);
                    browseNextResponse = (BrowseNextResponse)serviceResponse;
                }
                else
                {
                    var browseNextResponseMessage = await Task <BrowseNextResponseMessage> .Factory.FromAsync(InnerChannel.BeginBrowseNext, InnerChannel.EndBrowseNext, new BrowseNextMessage(browseNextRequest), null).ConfigureAwait(false);

                    if (browseNextResponseMessage == null || browseNextResponseMessage.BrowseNextResponse == null)
                    {
                        throw new ServiceResultException(StatusCodes.BadUnknownResponse);
                    }
                    browseNextResponse = browseNextResponseMessage.BrowseNextResponse;
                    ValidateResponse(browseNextResponse.ResponseHeader);
                }
            }
            finally
            {
                RequestCompleted(browseNextRequest, browseNextResponse, "BrowseNext");
            }
            return(browseNextResponse);
        }
Пример #8
0
        /// <summary>
        /// Requests the next set of Browse responses, when the information is too large to be sent in a single response.
        /// </summary>
        /// <param name="channel">A instance of <see cref="IRequestChannel"/>.</param>
        /// <param name="request">A <see cref="BrowseNextRequest"/>.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation that returns a <see cref="BrowseNextResponse"/>.</returns>
        public static async Task <BrowseNextResponse> BrowseNextAsync(this IRequestChannel channel, BrowseNextRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            return((BrowseNextResponse)await channel.RequestAsync(request).ConfigureAwait(false));
        }
Пример #9
0
        /// <summary>
        /// Begins an asynchronous invocation of the BrowseNext service.
        /// </summary>
        public IAsyncResult BeginBrowseNext(
            RequestHeader        requestHeader,
            bool                 releaseContinuationPoints,
            ByteStringCollection continuationPoints,
            AsyncCallback        callback,
            object               asyncState)
        {
            BrowseNextRequest request = new BrowseNextRequest();

            request.RequestHeader             = requestHeader;
            request.ReleaseContinuationPoints = releaseContinuationPoints;
            request.ContinuationPoints        = continuationPoints;

            UpdateRequestHeader(request, requestHeader == null, "BrowseNext");

            if (UseTransportChannel)
            {
                return TransportChannel.BeginSendRequest(request, callback, asyncState);
            }

            return InnerChannel.BeginBrowseNext(new BrowseNextMessage(request), callback, asyncState);
        }
Пример #10
0
        /// <summary>
        /// Invokes the BrowseNext service.
        /// </summary>
        public virtual ResponseHeader BrowseNext(
            RequestHeader                requestHeader,
            bool                         releaseContinuationPoints,
            ByteStringCollection         continuationPoints,
            out BrowseResultCollection   results,
            out DiagnosticInfoCollection diagnosticInfos)
        {
            BrowseNextRequest request = new BrowseNextRequest();
            BrowseNextResponse response = null;

            request.RequestHeader             = requestHeader;
            request.ReleaseContinuationPoints = releaseContinuationPoints;
            request.ContinuationPoints        = continuationPoints;

            UpdateRequestHeader(request, requestHeader == null, "BrowseNext");

            try
            {
                if (UseTransportChannel)
                {
                    IServiceResponse genericResponse = TransportChannel.SendRequest(request);

                    if (genericResponse == null)
                    {
                        throw new ServiceResultException(StatusCodes.BadUnknownResponse);
                    }

                    ValidateResponse(genericResponse.ResponseHeader);
                    response = (BrowseNextResponse)genericResponse;
                }
                else
                {
                    BrowseNextResponseMessage responseMessage = InnerChannel.BrowseNext(new BrowseNextMessage(request));

                    if (responseMessage == null || responseMessage.BrowseNextResponse == null)
                    {
                        throw new ServiceResultException(StatusCodes.BadUnknownResponse);
                    }

                    response = responseMessage.BrowseNextResponse;
                    ValidateResponse(response.ResponseHeader);
                }

                results         = response.Results;
                diagnosticInfos = response.DiagnosticInfos;
            }
            finally
            {
                RequestCompleted(request, response, "BrowseNext");
            }

            return response.ResponseHeader;
        }
        private async Task LoadChildrenAsync(ReferenceDescriptionViewModel parent)
        {
            try
            {
                var token = this.cts.Token;
                await [email protected](token);

                this.NotifyPropertyChanged("IsLoading");
                try
                {
                    parent.Children.Clear();
                    do
                    {
                        try
                        {
                            if (this.channel == null || this.channel.State != CommunicationState.Opened)
                            {
                                var getEndpointsRequest = new GetEndpointsRequest
                                {
                                    EndpointUrl = this.endpointUrl,
                                    ProfileUris = new[] { TransportProfileUris.UaTcpTransport }
                                };
                                var getEndpointsResponse = await UaTcpDiscoveryClient.GetEndpointsAsync(getEndpointsRequest);

                                token.ThrowIfCancellationRequested();
                                var selectedEndpoint = getEndpointsResponse.Endpoints.OrderBy(e => e.SecurityLevel).First();
                                if (selectedEndpoint.UserIdentityTokens.Any(p => p.TokenType == UserTokenType.Anonymous))
                                {
                                    this.HideLoginPanel();
                                    this.userIdentity = new AnonymousIdentity();
                                }
                                else if (selectedEndpoint.UserIdentityTokens.Any(p => p.TokenType == UserTokenType.UserName))
                                {
                                    if (!this.showingLoginPanel)
                                    {
                                        this.ShowLoginPanel();
                                        return;
                                    }
                                    else if (!this.ValidateLoginCredentials())
                                    {
                                        return;
                                    }
                                    else
                                    {
                                        this.userIdentity = new UserNameIdentity(this.userName, this.password);
                                    }
                                }
                                else
                                {
                                    throw new NotImplementedException("Browser supports only UserName and Anonymous identity, for now.");
                                }
                                dataTypeCache = new Dictionary <ExpandedNodeId, Type>();
                                this.channel  = new UaTcpSessionChannel(
                                    this.localDescription,
                                    this.CertificateStore,
                                    this.userIdentity,
                                    selectedEndpoint);
                                await this.channel.OpenAsync();
                            }

                            token.ThrowIfCancellationRequested();
                            var rds           = new List <ReferenceDescription>();
                            var browseRequest = new BrowseRequest {
                                NodesToBrowse = new[] { new BrowseDescription {
                                                            NodeId = ExpandedNodeId.ToNodeId(parent.NodeId, this.channel.NamespaceUris), ReferenceTypeId = NodeId.Parse(ReferenceTypeIds.HierarchicalReferences), ResultMask = (uint)BrowseResultMask.TargetInfo, NodeClassMask = (uint)NodeClass.Variable | (uint)NodeClass.Object | (uint)NodeClass.Method, BrowseDirection = BrowseDirection.Forward, IncludeSubtypes = true
                                                        } }
                            };
                            var browseResponse = await this.channel.BrowseAsync(browseRequest);

                            rds.AddRange(browseResponse.Results.Where(result => result.References != null).SelectMany(result => result.References));
                            var continuationPoints = browseResponse.Results.Select(br => br.ContinuationPoint).Where(cp => cp != null).ToArray();
                            while (continuationPoints.Length > 0)
                            {
                                token.ThrowIfCancellationRequested();
                                var browseNextRequest = new BrowseNextRequest {
                                    ContinuationPoints = continuationPoints, ReleaseContinuationPoints = false
                                };
                                var browseNextResponse = await this.channel.BrowseNextAsync(browseNextRequest);

                                rds.AddRange(browseResponse.Results.Where(result => result.References != null).SelectMany(result => result.References));
                            }

                            if (rds.Count == 0)
                            {
                                return;
                            }

                            foreach (var rd in rds)
                            {
                                token.ThrowIfCancellationRequested();

                                var  n        = ExpandedNodeId.ToNodeId(rd.NodeId, this.channel.NamespaceUris);
                                Type dataType = null;
                                EventNotifierFlags notifier    = EventNotifierFlags.None;
                                AccessLevelFlags   accessLevel = AccessLevelFlags.None;


                                if (rd.NodeClass == NodeClass.Variable)
                                {
                                    var readRequest = new ReadRequest
                                    {
                                        NodesToRead = new ReadValueId[]
                                        {
                                            new ReadValueId {
                                                NodeId = n, AttributeId = AttributeIds.DataType
                                            },
                                            new ReadValueId {
                                                NodeId = n, AttributeId = AttributeIds.ValueRank
                                            },
                                            new ReadValueId {
                                                NodeId = n, AttributeId = AttributeIds.UserAccessLevel
                                            }
                                        }
                                    };
                                    var readResponse = await this.channel.ReadAsync(readRequest);

                                    ExpandedNodeId       dataTypeId, origDataTypeId;
                                    ReferenceDescription dataTypeRef;
                                    var dataTypeNode = readResponse.Results[0].GetValueOrDefault(NodeId.Null);
                                    if (dataTypeNode != NodeId.Null)
                                    {
                                        dataTypeId = origDataTypeId = NodeId.ToExpandedNodeId(dataTypeNode, this.channel.NamespaceUris);

                                        if (!this.dataTypeCache.TryGetValue(dataTypeId, out dataType))
                                        {
                                            if (!UaTcpSecureChannel.DataTypeIdToTypeDictionary.TryGetValue(dataTypeId, out dataType))
                                            {
                                                do
                                                {
                                                    dataTypeNode = ExpandedNodeId.ToNodeId(dataTypeId, this.channel.NamespaceUris);
                                                    var browseRequest2 = new BrowseRequest {
                                                        NodesToBrowse = new[] { new BrowseDescription {
                                                                                    NodeId = dataTypeNode, ReferenceTypeId = NodeId.Parse(ReferenceTypeIds.HasSubtype), ResultMask = (uint)BrowseResultMask.None, NodeClassMask = (uint)NodeClass.DataType, BrowseDirection = BrowseDirection.Inverse, IncludeSubtypes = false
                                                                                } }
                                                    };
                                                    var browseResponse2 = await this.channel.BrowseAsync(browseRequest2);

                                                    dataTypeRef = browseResponse2.Results[0].References?.FirstOrDefault();
                                                    dataTypeId  = dataTypeRef?.NodeId;
                                                }while (dataTypeId != null && !UaTcpSecureChannel.DataTypeIdToTypeDictionary.TryGetValue(dataTypeId, out dataType));

                                                if (dataTypeId == null)
                                                {
                                                    dataType = typeof(object);
                                                }
                                            }

                                            this.dataTypeCache.Add(origDataTypeId, dataType);
                                        }

                                        var valueRank = readResponse.Results[1].GetValueOrDefault(-1);
                                        if (valueRank == 1)
                                        {
                                            dataType = dataType.MakeArrayType();
                                        }

                                        if (valueRank > 1)
                                        {
                                            dataType = dataType.MakeArrayType(valueRank);
                                        }
                                    }
                                    else
                                    {
                                        dataType = typeof(object);
                                    }

                                    accessLevel = (AccessLevelFlags)Enum.ToObject(typeof(AccessLevelFlags), readResponse.Results[2].GetValueOrDefault <byte>());
                                }
                                else if (rd.NodeClass == NodeClass.Object)
                                {
                                    var readRequest = new ReadRequest
                                    {
                                        NodesToRead = new ReadValueId[]
                                        {
                                            new ReadValueId {
                                                NodeId = n, AttributeId = AttributeIds.EventNotifier
                                            },
                                        }
                                    };
                                    var readResponse = await this.channel.ReadAsync(readRequest);

                                    notifier = (EventNotifierFlags)Enum.ToObject(typeof(EventNotifierFlags), readResponse.Results[0].GetValueOrDefault <byte>());
                                }

                                parent.Children.Add(new ReferenceDescriptionViewModel(rd, dataType, accessLevel, notifier, parent, this.LoadChildrenAsync));
                                await Task.Yield();
                            }

                            break; // exit while;
                        }
                        catch (OperationCanceledException ex)
                        {
                            // exit while;
                        }
                        catch (ServiceResultException ex)
                        {
                            Console.WriteLine("ServiceResultException: {0}", ex);
                            if (this.channel != null)
                            {
                                await this.channel.AbortAsync(token);

                                this.channel = null;
                            }

                            if (ex.HResult == unchecked ((int)StatusCodes.BadSessionIdInvalid))
                            {
                                continue;
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("Exception {0}", ex);
                            if (this.channel != null)
                            {
                                await this.channel.AbortAsync(token);

                                this.channel = null;
                            }
                        }
                        try
                        {
                            await Task.Delay(5000, token);
                        }
                        catch (OperationCanceledException)
                        {
                        }
                    }while (!token.IsCancellationRequested);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Exception {0}", ex);
                }
                finally
                {
                    [email protected]();
                    this.NotifyPropertyChanged("IsLoading");
                }
            }
            catch (OperationCanceledException)
            {
                // only get here if cancelled while waiting for lock
            }
        }
Пример #12
0
 /// <summary>
 /// Initializes the message with the body.
 /// </summary>
 public BrowseNextMessage(BrowseNextRequest BrowseNextRequest)
 {
     this.BrowseNextRequest = BrowseNextRequest;
 }
        public async Task BrowseObjectFolder()
        {
            // describe this client application.
            var clientDescription = new ApplicationDescription
            {
                ApplicationName = "Workstation.UaClient.FeatureTests",
                ApplicationUri  = $"urn:{System.Net.Dns.GetHostName()}:Workstation.UaClient.FeatureTests",
                ApplicationType = ApplicationType.Client
            };

            // place to store certificates
            var certificateStore = new DirectoryStore("./pki");

            // create a 'UaTcpSessionChannel', a client-side channel that opens a 'session' with the server.
            var channel = new UaTcpSessionChannel(
                clientDescription,
                certificateStore,
                new AnonymousIdentity(),
                "opc.tcp://localhost:48010"); // the endpoint of Unified Automation's UaCPPServer.

            try
            {
                // try opening a session and reading a few nodes.
                await channel.OpenAsync();

                Console.WriteLine($"Opened session with endpoint '{channel.RemoteEndpoint.EndpointUrl}'.");
                Console.WriteLine($"SecurityPolicy: '{channel.RemoteEndpoint.SecurityPolicyUri}'.");
                Console.WriteLine($"SecurityMode: '{channel.RemoteEndpoint.SecurityMode}'.");
                Console.WriteLine($"UserIdentityToken: '{channel.UserIdentity}'.");

                // build a BrowseRequest. See 'OPC UA Spec Part 4' section 5.8.2
                var browseRequest = new BrowseRequest
                {
                    NodesToBrowse = new[] {
                        new BrowseDescription {
                            // gather references of this nodeid.
                            NodeId = NodeId.Parse(ObjectIds.ObjectsFolder),
                            // include just 'Forward' references
                            BrowseDirection = BrowseDirection.Forward,
                            // include 'HierarchicalReferences'
                            ReferenceTypeId = NodeId.Parse(ReferenceTypeIds.HierarchicalReferences),
                            // include 'HierarchicalReferences' and all subtypes of 'HierarchicalReferences'
                            IncludeSubtypes = true,
                            // include all classes of node
                            NodeClassMask = (uint)NodeClass.Unspecified,
                            // return reference descriptions with all the fields filled out
                            ResultMask = (uint)BrowseResultMask.All,
                        }
                    },
                    RequestedMaxReferencesPerNode = 1000
                };

                // send the request to the server
                var browseResponse = await channel.BrowseAsync(browseRequest).ConfigureAwait(false);

                Console.WriteLine("\n+ Objects, 0:Objects, Object, i=85");

                Assert.IsNotNull(browseResponse.Results[0].References);
                foreach (var rd in browseResponse.Results[0].References)
                {
                    Console.WriteLine("   + {0}, {1}, {2}, {3}", rd.DisplayName, rd.BrowseName, rd.NodeClass, rd.NodeId);
                }

                // it is good practice to be prepared to receive a continuationPoint.
                // ContinuationPoints are returned when the server has more information
                // than can be delivered in current response.
                // To test this code, you can reduce the above RequestedMaxReferencesPerNode
                // to 1.
                var cp = browseResponse.Results[0].ContinuationPoint;
                while (cp != null)
                {
                    var browseNextRequest = new BrowseNextRequest {
                        ContinuationPoints = new[] { cp }, ReleaseContinuationPoints = false
                    };
                    var browseNextResponse = await channel.BrowseNextAsync(browseNextRequest);

                    Assert.IsNotNull(browseNextResponse.Results[0].References);
                    foreach (var rd in browseNextResponse.Results[0].References)
                    {
                        Console.WriteLine("   + {0}, {1}, {2}", rd.DisplayName, rd.BrowseName, rd.NodeClass);
                    }
                    cp = browseNextResponse.Results[0].ContinuationPoint;
                }

                Console.WriteLine($"\nClosing session '{channel.SessionId}'.");
                await channel.CloseAsync();
            }
            catch (Exception ex)
            {
                await channel.AbortAsync();

                Console.WriteLine(ex.Message);
            }
        }