OPC UA访问

说明

随着现在WEB、移动互联网的快速发展,OPC UA方式用的越来越广泛。我最近项目需要,自己整理了一下,这里和大家分享。

开始

我平常用KepServer 比较多,所以我就拿KepServer服务端自带UA服务端做说明演示。

一:KepServer UA服务端配置:

1.打开KepServer Configuration

OPC UA访问_第1张图片

2.选中【项目】右键-选择属性,选择OPC UA ,打开OPC UA Server功能。

OPC UA访问_第2张图片

3.再有电脑右下角托盘处,找到KepServer图标,右键-OPC UA配置

OPC UA访问_第3张图片

4.选择【添加】

OPC UA访问_第4张图片

这里可以配置网卡、端口号、安全策略。设置后好保存。

5.重启,测试。

二:OPC UA 

OPC UA有好多开源封装好的类库。我用的OPC基金会官方开源类库,这里写一写分享这个库的用法。

OPC UA连接以Session为抽象通讯单位,几乎所有操作都是在这基础上完成的。下面分享一些经常用的方法:

1.初始化配置

private void SetDefaultConfiguration()
        {
            var certificateValidator = new CertificateValidator();
            certificateValidator.CertificateValidation += (sender, eventArgs) =>
            {
                if (ServiceResult.IsGood(eventArgs.Error))
                    eventArgs.Accept = true;
                else if ((eventArgs.Error.StatusCode.Code == StatusCodes.BadCertificateUntrusted) && true)
                    eventArgs.Accept = true;
                else
                    throw new Exception(string.Format("Failed to validate certificate with error code {0}: {1}", eventArgs.Error.Code, eventArgs.Error.AdditionalInfo));
            };

            m_configuration = new ApplicationConfiguration()
            {
                ApplicationUri = "",
                ApplicationName = "Client",
                ApplicationType = ApplicationType.Client,
                CertificateValidator = certificateValidator,
                ServerConfiguration = new ServerConfiguration
                {
                    MaxSubscriptionCount = 10000,
                    MaxMessageQueueSize = 10000,
                    MaxNotificationQueueSize = 10000,
                    MaxPublishRequestCount = 10000
                },
                SecurityConfiguration = new SecurityConfiguration
                {
                    AutoAcceptUntrustedCertificates = true,
                },
                TransportQuotas = new TransportQuotas
                {
                    OperationTimeout = 600000,
                    MaxStringLength = 1048576,
                    MaxByteStringLength = 1048576,
                    MaxArrayLength = 65535,
                    MaxMessageSize = 4194304,
                    MaxBufferSize = 65535,
                    ChannelLifetime = 600000,
                    SecurityTokenLifetime = 3600000
                },
                ClientConfiguration = new ClientConfiguration
                {
                    DefaultSessionTimeout = 60000,
                    MinSubscriptionLifetime = 10000
                },
                DisableHiResClock = true
            };
        }

2.连接UA 服务器

   public async Task Connect(string serverUrl, bool useSecurity)
        {
            // disconnect from existing session.
            Disconnect();
            if (m_configuration == null)
            {
                throw new ArgumentNullException("m_configuration");
            }

            // select the best endpoint.
            EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint(serverUrl, useSecurity, m_discoverTimeout);
            EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(m_configuration);
            ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);

            m_session = await Session.Create(
                m_configuration,
                endpoint,
                false,
                !DisableDomainCheck,
                (String.IsNullOrEmpty(SessionName)) ? m_configuration.ApplicationName : SessionName,
                60000,
                UserIdentity,
                PreferredLocales);

            // set up keep alive callback.
            m_session.KeepAlive += new KeepAliveEventHandler(Session_KeepAlive);

            // raise an event.
            DoConnectComplete(null);

            // return the new session.
            return m_session;
        }

3.浏览节点

 public ReferenceDescriptionCollection BrowseNodes(NodeId sourceId)
        {

            m_session = this.MySession;

            if (m_session == null)
            {
                return null;
            }

            // set a suitable initial state.
            //if (m_session != null && !m_connectedOnce)
            //{
            //    m_connectedOnce = true;
            //}

            // fetch references from the server.
            // find all of the components of the node.
            BrowseDescription nodeToBrowse1 = new BrowseDescription();

            nodeToBrowse1.NodeId = sourceId;
            nodeToBrowse1.BrowseDirection = BrowseDirection.Forward;
            nodeToBrowse1.ReferenceTypeId = ReferenceTypeIds.Aggregates;
            nodeToBrowse1.IncludeSubtypes = true;
            nodeToBrowse1.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable);
            nodeToBrowse1.ResultMask = (uint)BrowseResultMask.All;

            Browser bro = new Browser(m_session);
            ReferenceDescriptionCollection references = bro.Browse(sourceId);           
            return references;
        }

4.读取节点下所有子节点

        public  ReferenceDescriptionCollection Browse( 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;

                    m_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.
                        m_session.BrowseNext(
                            null,
                            false,
                            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;
            }
        }

5.同步写

 public bool WriteValue(NodeId nodeId,DataValue dataValue,string oValue)
        {
            bool result =true;
            WriteValueCollection writeValues = new WriteValueCollection();
            WriteValue writeValue = new WriteValue();

            writeValue.NodeId =nodeId;
            writeValue.AttributeId = Attributes.Value;
            writeValue.Value.Value = ChangeType(dataValue, oValue);
            writeValue.Value.StatusCode = StatusCodes.Good;
            writeValue.Value.ServerTimestamp = DateTime.MinValue;
            writeValue.Value.SourceTimestamp = DateTime.MinValue;
            writeValues.Add(writeValue);

            StatusCodeCollection results;
            DiagnosticInfoCollection diagnosticinfos;
            MySession.Write(null, writeValues, out results, out diagnosticinfos);

            ClientBase.ValidateResponse(results, writeValues);
            ClientBase.ValidateDiagnosticInfos(diagnosticinfos, writeValues);

            if (StatusCode.IsBad(results[0]))
            {
                throw new ServiceResultException(results[0]);
            }

            return result;
        }

6.异步写

        public bool BeginWrite(NodeId nodeid, DataValue dataValue, string oValue,AsyncCallback callback)
        {
            bool result = true;
            WriteValueCollection writeValues = new WriteValueCollection();
            WriteValue writeValue = new WriteValue();

            writeValue.NodeId = nodeid;
            writeValue.AttributeId = Attributes.Value;
            writeValue.Value.Value = ChangeType(dataValue, oValue);
            writeValue.Value.StatusCode = StatusCodes.Good;
            writeValue.Value.ServerTimestamp = DateTime.MinValue;
            writeValue.Value.SourceTimestamp = DateTime.MinValue;
            writeValues.Add(writeValue);
            MySession.BeginWrite(null, writeValues, callback, writeValues);
            return result;
        }

7.同步读

  DataValue readValue = ua.MySession.ReadValue((NodeId)CurrentSelectedItem.Reference.NodeId);
                    CurrentSelectedItem.Value = readValue.Value.ToString();

8.异步读

   ReadValueIdCollection tmpReads = new ReadValueIdCollection();
                    ReadValueId tmpReadId = new ReadValueId();
                    tmpReadId.NodeId = (NodeId)CurrentSelectedItem.Reference.NodeId;
                    tmpReadId.AttributeId = Attributes.Value;
                    tmpReads.Add(tmpReadId);
                    ua.MySession.BeginRead(null,0,TimestampsToReturn.Neither,tmpReads,callback, tmpReads);

9.添加订阅

  public MonitoredItem CreateMonitoredItem(NodeId nodeId, string displayName)
        {
            if (m_subscription == null)
            {
                m_subscription = new Subscription(m_session.DefaultSubscription);

                m_subscription.PublishingEnabled = true;
                m_subscription.PublishingInterval = 1000;
                m_subscription.KeepAliveCount = 10;
                m_subscription.LifetimeCount = 10;
                m_subscription.MaxNotificationsPerPublish = 1000;
                m_subscription.Priority = 100;

                m_session.AddSubscription(m_subscription);

                m_subscription.Create();

                
            }

            // add the new monitored item
.
            MonitoredItem monitoredItem = new MonitoredItem(m_subscription.DefaultItem);

            monitoredItem.StartNodeId = nodeId;
            monitoredItem.AttributeId = Attributes.Value;
            monitoredItem.DisplayName = displayName;
            monitoredItem.MonitoringMode = MonitoringMode.Reporting;
            monitoredItem.SamplingInterval = 1000;
            monitoredItem.QueueSize = 0;
            monitoredItem.DiscardOldest = true;
            monitoredItem.Handle = nodeId;
            monitoredItem.Notification += m_MonitoredItem_Notification;

            m_subscription.AddItem(monitoredItem);
            m_subscription.ApplyChanges();

            if (ServiceResult.IsBad(monitoredItem.Status.Error))
            {
                string tmpStr = monitoredItem.Status.Error.StatusCode.ToString();
            }

            return monitoredItem;
        }


//订阅回调
  protected virtual void m_MonitoredItem_Notification(MonitoredItem monitoreditem, MonitoredItemNotificationEventArgs e)
        {
            this.SubscriptionDataChangeEvent?.Invoke(monitoreditem, e);
        }

三、自己学习时测试源码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;

namespace UAClient
{
    public class UAHelper 
    {

        #region Private Fields
        //private ApplicationInstance application;
        private ApplicationConfiguration m_configuration;
        private Session m_session;
        private int m_reconnectPeriod = 10;
        private int m_discoverTimeout = 5000;
        private SessionReconnectHandler m_reconnectHandler;
        private CertificateValidationEventHandler m_CertificateValidation;
        private EventHandler m_ReconnectComplete;
        private EventHandler m_ReconnectStarting;
        private EventHandler m_KeepAliveComplete;
        private EventHandler m_ConnectComplete;
        public event MonitoredItemNotificationEventHandler SubscriptionDataChangeEvent = null;
        private Subscription m_subscription;
        //private StatusStrip m_StatusStrip;
        //private ToolStripItem m_ServerStatusLB;
        //private ToolStripItem m_StatusUpateTimeLB;
        #endregion

        #region Public Members

        //public string OpcUaName { get; set; }
        /// 
        /// The name of the session to create.
        /// 
        public string SessionName { get; set; }

        /// 
        /// Gets or sets a flag indicating that the domain checks should be ignored when connecting.
        /// 
        public bool DisableDomainCheck { get; set; }


        /// 
        /// The locales to use when creating the session.
        /// 
        public string[] PreferredLocales { get; set; }

        /// 
        /// The user identity to use when creating the session.
        /// 
        public IUserIdentity UserIdentity { get; set; }

        /// 
        /// The client application configuration.
        /// 
        public ApplicationConfiguration Configuration
        {
            get { return m_configuration; }

            set
            {
                if (!Object.ReferenceEquals(m_configuration, value))
                {
                    if (m_configuration != null)
                    {
                        m_configuration.CertificateValidator.CertificateValidation -= m_CertificateValidation;
                    }

                    m_configuration = value;

                    if (m_configuration != null)
                    {
                        m_configuration.CertificateValidator.CertificateValidation += m_CertificateValidation;
                    }
                }
            }
        }

        /// 
        /// The currently active session. 
        /// 
        public Session MySession
        {
            get { return m_session; }
        }

        /// 
        /// The number of seconds between reconnect attempts (0 means reconnect is disabled).
        /// 
        [DefaultValue(10)]
        public int ReconnectPeriod
        {
            get { return m_reconnectPeriod; }
            set { m_reconnectPeriod = value; }
        }

        /// 
        /// Raised when a good keep alive from the server arrives.
        /// 
        public event EventHandler KeepAliveComplete
        {
            add { m_KeepAliveComplete += value; }
            remove { m_KeepAliveComplete -= value; }
        }

        /// 
        /// Raised when a reconnect operation starts.
        /// 
        public event EventHandler ReconnectStarting
        {
            add { m_ReconnectStarting += value; }
            remove { m_ReconnectStarting -= value; }
        }

        /// 
        /// Raised when a reconnect operation completes.
        /// 
        public event EventHandler ReconnectComplete
        {
            add { m_ReconnectComplete += value; }
            remove { m_ReconnectComplete -= value; }
        }

        /// 
        /// Raised after successfully connecting to or disconnecing from a server.
        /// 
        public event EventHandler ConnectComplete
        {
            add { m_ConnectComplete += value; }
            remove { m_ConnectComplete -= value; }
        }

        public UAHelper(string OpcUaName):this(OpcUaName,string.Empty)
        {
        }

        public UAHelper(string sessionName, ApplicationConfiguration cfg)
        {
            this.SessionName = sessionName;
            if (cfg == null)
            {
                SetDefaultConfiguration();
            }
            else
            {
                this.m_configuration = cfg;
            }
        }
        public  UAHelper(string sessionName, string cfgPath)
        {
            this.SessionName = sessionName;
            if (string.IsNullOrEmpty(cfgPath))
            {
                SetDefaultConfiguration();
            }
            else
            {
                LoadConfiguration(cfgPath);
                m_configuration.CertificateValidator = new CertificateValidator();
            }
        }

        private async void LoadConfiguration(string cfgPath)
        {
            m_configuration = await ApplicationConfiguration.Load("Client", ApplicationType.Client);
        }

        private void SetDefaultConfiguration()
        {
            var certificateValidator = new CertificateValidator();
            certificateValidator.CertificateValidation += (sender, eventArgs) =>
            {
                if (ServiceResult.IsGood(eventArgs.Error))
                    eventArgs.Accept = true;
                else if ((eventArgs.Error.StatusCode.Code == StatusCodes.BadCertificateUntrusted) && true)
                    eventArgs.Accept = true;
                else
                    throw new Exception(string.Format("Failed to validate certificate with error code {0}: {1}", eventArgs.Error.Code, eventArgs.Error.AdditionalInfo));
            };

            m_configuration = new ApplicationConfiguration()
            {
                ApplicationUri = "",
                ApplicationName = "Client",
                ApplicationType = ApplicationType.Client,
                CertificateValidator = certificateValidator,
                ServerConfiguration = new ServerConfiguration
                {
                    MaxSubscriptionCount = 10000,
                    MaxMessageQueueSize = 10000,
                    MaxNotificationQueueSize = 10000,
                    MaxPublishRequestCount = 10000
                },
                SecurityConfiguration = new SecurityConfiguration
                {
                    AutoAcceptUntrustedCertificates = true,
                },
                TransportQuotas = new TransportQuotas
                {
                    OperationTimeout = 600000,
                    MaxStringLength = 1048576,
                    MaxByteStringLength = 1048576,
                    MaxArrayLength = 65535,
                    MaxMessageSize = 4194304,
                    MaxBufferSize = 65535,
                    ChannelLifetime = 600000,
                    SecurityTokenLifetime = 3600000
                },
                ClientConfiguration = new ClientConfiguration
                {
                    DefaultSessionTimeout = 60000,
                    MinSubscriptionLifetime = 10000
                },
                DisableHiResClock = true
            };
        }

        /// 
        /// Sets the URLs shown in the control.
        /// 
        public void SetAvailableUrls(IList urls)
        {
            //UrlCB.Items.Clear();

            if (urls != null)
            {
                foreach (string url in urls)
                {
                    int index = url.LastIndexOf("/discovery", StringComparison.InvariantCultureIgnoreCase);

                    if (index != -1)
                    {
                        //UrlCB.Items.Add(url.Substring(0, index));
                        continue;
                    }

                    //UrlCB.Items.Add(url);
                }

                //if (UrlCB.Items.Count > 0)
                //{
                //    UrlCB.SelectedIndex = 0;
                //}
            }
        }

        /// 
        /// Creates a new session.
        /// 
        /// The new session object.
        public async Task Connect(string serverUrl)
        {
            Session session = await Connect(serverUrl,false);
            return session;
        }

        /// 
        /// Creates a new session.
        /// 
        /// The URL of a server endpoint.
        /// Whether to use security.
        /// The new session object.
        public async Task Connect(string serverUrl, bool useSecurity)
        {
            // disconnect from existing session.
            Disconnect();
            if (m_configuration == null)
            {
                throw new ArgumentNullException("m_configuration");
            }

            // select the best endpoint.
            EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint(serverUrl, useSecurity, m_discoverTimeout);
            EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(m_configuration);
            ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);

            m_session = await Session.Create(
                m_configuration,
                endpoint,
                false,
                !DisableDomainCheck,
                (String.IsNullOrEmpty(SessionName)) ? m_configuration.ApplicationName : SessionName,
                60000,
                UserIdentity,
                PreferredLocales);

            // set up keep alive callback.
            m_session.KeepAlive += new KeepAliveEventHandler(Session_KeepAlive);

            // raise an event.
            DoConnectComplete(null);

            // return the new session.
            return m_session;
        }

        /// 
        /// Disconnects from the server.
        /// 
        public void Disconnect()
        {
            UpdateStatus(false, DateTime.UtcNow, "Disconnected");

            // stop any reconnect operation.
            if (m_reconnectHandler != null)
            {
                m_reconnectHandler.Dispose();
                m_reconnectHandler = null;
            }

            // disconnect any existing session.
            if (m_session != null)
            {
                m_session.Close(10000);
                m_session = null;
            }

            // raise an event.
            //DoConnectComplete(null);  2018.08.15 注释,没有明白断开连接时为什么要触发连接成功
        }

        ///// 
        ///// Prompts the user to choose a server on another host.
        ///// 
        //public void Discover(string hostName)
        //{
        //    string endpointUrl = new DiscoverServerDlg().ShowDialog(m_configuration, hostName);

        //    if (endpointUrl != null)
        //    {
        //        ServerUrl = endpointUrl;
        //    }
        //}
        #endregion

        #region Private Methods
        /// 
        /// Raises the connect complete event on the main GUI thread.
        /// 
        private void DoConnectComplete(object state)
        {
            if (m_ConnectComplete != null)
            {
                m_ConnectComplete(this, null);
            }
        }

        /// 
        /// Finds the endpoint that best matches the current settings.
        /// 
        private EndpointDescription SelectEndpoint(string discoveryUrl,bool useSecurity)
        {
            Cursor cuisour = Cursors.Wait;
            try
            {               
                // return the selected endpoint.
                return CoreClientUtils.SelectEndpoint(discoveryUrl, useSecurity, m_discoverTimeout);
            }
            finally
            {
                cuisour = Cursors.Arrow;
            }
        }
        #endregion

        #region Event Handlers
        /// 
        /// Updates the status control.
        /// 
        /// Whether the status represents an error.
        /// The time associated with the status.
        /// The status message.
        /// Arguments used to format the status message.
        private void UpdateStatus(bool error, DateTime time, string status, params object[] args)
        {
            //if (m_ServerStatusLB != null)
            //{
            //    m_ServerStatusLB.Text = String.Format(status, args);
            //    m_ServerStatusLB.ForeColor = (error) ? Color.Red : Color.Empty;
            //}

            //if (m_StatusUpateTimeLB != null)
            //{
            //    m_StatusUpateTimeLB.Text = time.ToLocalTime().ToString("hh:mm:ss");
            //    m_StatusUpateTimeLB.ForeColor = (error) ? Color.Red : Color.Empty;
            //}
        }

        /// 
        /// Handles a keep alive event from a session.
        /// 
        private void Session_KeepAlive(Session session, KeepAliveEventArgs e)
        {
            //if (this.InvokeRequired)
            //{
            //    this.BeginInvoke(new KeepAliveEventHandler(Session_KeepAlive), session, e);
            //    return;
            //}

            try
            {
                // check for events from discarded sessions.
                if (!Object.ReferenceEquals(session, m_session))
                {
                    return;
                }

                // start reconnect sequence on communication error.
                if (ServiceResult.IsBad(e.Status))
                {
                    if (m_reconnectPeriod <= 0)
                    {
                        UpdateStatus(true, e.CurrentTime, "Communication Error ({0})", e.Status);
                        return;
                    }

                    UpdateStatus(true, e.CurrentTime, "Reconnecting in {0}s", m_reconnectPeriod);

                    if (m_reconnectHandler == null)
                    {
                        if (m_ReconnectStarting != null)
                        {
                            m_ReconnectStarting(this, e);
                        }

                        m_reconnectHandler = new SessionReconnectHandler();
                        m_reconnectHandler.BeginReconnect(m_session, m_reconnectPeriod * 1000, Server_ReconnectComplete);
                    }

                    return;
                }

                // update status.
                UpdateStatus(false, e.CurrentTime, "Connected [{0}]", session.Endpoint.EndpointUrl);

                // raise any additional notifications.
                if (m_KeepAliveComplete != null)
                {
                    m_KeepAliveComplete(this, e);
                }
            }
            catch (Exception exception)
            {
                //ClientUtils.HandleException("Error", exception);
            }
        }

        ///// 
        ///// Handles a click on the connect button.
        ///// 
        //private async void Server_ConnectMI_Click(object sender, EventArgs e)
        //{
        //    try
        //    {
        //        await Connect();
        //    }
        //    catch (Exception exception)
        //    {
        //        ClientUtils.HandleException(this.Text, exception);
        //    }
        //}

        /// 
        /// Handles a reconnect event complete from the reconnect handler.
        /// 
        private void Server_ReconnectComplete(object sender, EventArgs e)
        {
            //if (this.InvokeRequired)
            //{
            //    this.BeginInvoke(new EventHandler(Server_ReconnectComplete), sender, e);
            //    return;
            //}

            try
            {
                // ignore callbacks from discarded objects.
                if (!Object.ReferenceEquals(sender, m_reconnectHandler))
                {
                    return;
                }

                m_session = m_reconnectHandler.Session;
                m_reconnectHandler.Dispose();
                m_reconnectHandler = null;

                // raise any additional notifications.
                if (m_ReconnectComplete != null)
                {
                    m_ReconnectComplete(this, e);
                }
            }
            catch (Exception exception)
            {
                //ClientUtils.HandleException("Error", exception);
            }
        }

        /// 
        /// Handles a certificate validation error.
        /// 
        private void CertificateValidator_CertificateValidation(CertificateValidator sender, CertificateValidationEventArgs e)
        {
            //if (this.InvokeRequired)
            //{
            //    this.Invoke(new CertificateValidationEventHandler(CertificateValidator_CertificateValidation), sender, e);
            //    return;
            //}

            try
            {
                e.Accept = m_configuration.SecurityConfiguration.AutoAcceptUntrustedCertificates;

                if (!m_configuration.SecurityConfiguration.AutoAcceptUntrustedCertificates)
                {
                     MessageBoxResult result = MessageBox.Show(
                        e.Certificate.Subject,
                        "Untrusted Certificate",
                        MessageBoxButton.YesNo,
                        MessageBoxImage.Error
                       );

                     e.Accept = (result == MessageBoxResult.Yes);
                }
            }
            catch (Exception exception)
            {
                //ClientUtils.HandleException("Error", exception);
            }
        }
        #endregion

        public ReferenceDescriptionCollection BrowseNodes(NodeId sourceId)
        {

            m_session = this.MySession;

            if (m_session == null)
            {
                return null;
            }

            // set a suitable initial state.
            //if (m_session != null && !m_connectedOnce)
            //{
            //    m_connectedOnce = true;
            //}

            // fetch references from the server.
            // find all of the components of the node.
            BrowseDescription nodeToBrowse1 = new BrowseDescription();

            nodeToBrowse1.NodeId = sourceId;
            nodeToBrowse1.BrowseDirection = BrowseDirection.Forward;
            nodeToBrowse1.ReferenceTypeId = ReferenceTypeIds.Aggregates;
            nodeToBrowse1.IncludeSubtypes = true;
            nodeToBrowse1.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable);
            nodeToBrowse1.ResultMask = (uint)BrowseResultMask.All;

            Browser bro = new Browser(m_session);
            ReferenceDescriptionCollection references = bro.Browse(sourceId);           
            return references;
        }

        /// 
        /// 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  ReferenceDescriptionCollection Browse( 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;

                    m_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.
                        m_session.BrowseNext(
                            null,
                            false,
                            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;
            }
        }

        /// 
        /// Changes the value in the text box to the data type required for the write operation.
        /// 
        /// A value with the correct type.
        public object ChangeType(DataValue m_value,string v)
        {
            object value = (m_value != null) ? m_value.Value : null;

            switch (m_value.WrappedValue.TypeInfo.BuiltInType)
            {
                case BuiltInType.Boolean:
                    {
                        value = Convert.ToBoolean(v);
                        break;
                    }

                case BuiltInType.SByte:
                    {
                        value = Convert.ToSByte(v);
                        break;
                    }

                case BuiltInType.Byte:
                    {
                        value = Convert.ToByte(v);
                        break;
                    }

                case BuiltInType.Int16:
                    {
                        value = Convert.ToInt16(v);
                        break;
                    }

                case BuiltInType.UInt16:
                    {
                        value = Convert.ToUInt16(v);
                        break;
                    }

                case BuiltInType.Int32:
                    {
                        value = Convert.ToInt32(v);
                        break;
                    }

                case BuiltInType.UInt32:
                    {
                        value = Convert.ToUInt32(v);
                        break;
                    }

                case BuiltInType.Int64:
                    {
                        value = Convert.ToInt64(v);
                        break;
                    }

                case BuiltInType.UInt64:
                    {
                        value = Convert.ToUInt64(v);
                        break;
                    }

                case BuiltInType.Float:
                    {
                        value = Convert.ToSingle(v);
                        break;
                    }

                case BuiltInType.Double:
                    {
                        value = Convert.ToDouble(v);
                        break;
                    }

                default:
                    {
                        value = v;
                        break;
                    }
            }

            return value;
        }

        /// 
        /// Creates the monitored item.
        /// 
        public MonitoredItem CreateMonitoredItem(NodeId nodeId, string displayName)
        {
            if (m_subscription == null)
            {
                m_subscription = new Subscription(m_session.DefaultSubscription);

                m_subscription.PublishingEnabled = true;
                m_subscription.PublishingInterval = 1000;
                m_subscription.KeepAliveCount = 10;
                m_subscription.LifetimeCount = 10;
                m_subscription.MaxNotificationsPerPublish = 1000;
                m_subscription.Priority = 100;

                m_session.AddSubscription(m_subscription);

                m_subscription.Create();

                
            }

            // add the new monitored item.
            MonitoredItem monitoredItem = new MonitoredItem(m_subscription.DefaultItem);

            monitoredItem.StartNodeId = nodeId;
            monitoredItem.AttributeId = Attributes.Value;
            monitoredItem.DisplayName = displayName;
            monitoredItem.MonitoringMode = MonitoringMode.Reporting;
            monitoredItem.SamplingInterval = 1000;
            monitoredItem.QueueSize = 0;
            monitoredItem.DiscardOldest = true;
            monitoredItem.Handle = nodeId;
            monitoredItem.Notification += m_MonitoredItem_Notification;

            m_subscription.AddItem(monitoredItem);
            m_subscription.ApplyChanges();

            if (ServiceResult.IsBad(monitoredItem.Status.Error))
            {
                string tmpStr = monitoredItem.Status.Error.StatusCode.ToString();
            }

            return monitoredItem;
        }

        public bool DeleteMonitoredItem(List monitoredItems)
        {
            bool result = true;
            for (int i = 0; i < monitoredItems.Count; i++)
            {
                monitoredItems[i].Notification -= m_MonitoredItem_Notification;
                
            }
            if (m_subscription != null)
            {
                m_subscription.RemoveItems(monitoredItems);
                m_subscription.ApplyChanges();
            }
            return result;
        }

        public bool WriteValue(NodeId nodeId,DataValue dataValue,string oValue)
        {
            bool result =true;
            WriteValueCollection writeValues = new WriteValueCollection();
            WriteValue writeValue = new WriteValue();

            writeValue.NodeId =nodeId;
            writeValue.AttributeId = Attributes.Value;
            writeValue.Value.Value = ChangeType(dataValue, oValue);
            writeValue.Value.StatusCode = StatusCodes.Good;
            writeValue.Value.ServerTimestamp = DateTime.MinValue;
            writeValue.Value.SourceTimestamp = DateTime.MinValue;
            writeValues.Add(writeValue);

            StatusCodeCollection results;
            DiagnosticInfoCollection diagnosticinfos;
            MySession.Write(null, writeValues, out results, out diagnosticinfos);

            ClientBase.ValidateResponse(results, writeValues);
            ClientBase.ValidateDiagnosticInfos(diagnosticinfos, writeValues);

            if (StatusCode.IsBad(results[0]))
            {
                throw new ServiceResultException(results[0]);
            }

            return result;
        }

        public bool BeginWrite(NodeId nodeid, DataValue dataValue, string oValue,AsyncCallback callback)
        {
            bool result = true;
            WriteValueCollection writeValues = new WriteValueCollection();
            WriteValue writeValue = new WriteValue();

            writeValue.NodeId = nodeid;
            writeValue.AttributeId = Attributes.Value;
            writeValue.Value.Value = ChangeType(dataValue, oValue);
            writeValue.Value.StatusCode = StatusCodes.Good;
            writeValue.Value.ServerTimestamp = DateTime.MinValue;
            writeValue.Value.SourceTimestamp = DateTime.MinValue;
            writeValues.Add(writeValue);
            MySession.BeginWrite(null, writeValues, callback, writeValues);
            return result;
        }

        protected virtual void m_MonitoredItem_Notification(MonitoredItem monitoreditem, MonitoredItemNotificationEventArgs e)
        {
            this.SubscriptionDataChangeEvent?.Invoke(monitoreditem, e);
        }

       
    }

}



using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Net.Mime;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Opc.Ua;
using Opc.Ua.Client;
//using Opc.Ua.Client.Controls;
using Opc.Ua.Configuration;
using UAClient.Annotations;

namespace UAClient
{
    /// 
    /// MainWindow.xaml 的交互逻辑
    /// 
    public partial class MainWindow : Window
    {
        private UAHelper ua = null;
        private ItemInfo CurrentSelectedItem = null;
        private List SubSelectedItems = new List();
        private ObservableCollection ItemsConlection = new ObservableCollection();
        private ObservableCollection SubcriptionItemsConlection = new ObservableCollection();
        public MainWindow()
        {
            InitializeComponent();
            ua = new UAHelper("Test");
            ua.SubscriptionDataChangeEvent += Ua_SubscriptionDataChange;
            ua.ConnectComplete += ua_ConnectComplete;
            ua.KeepAliveComplete += ua_KeepAliveComplete;
            ua.ReconnectComplete += ua_ReconnectComplete;
        }

        private void Ua_SubscriptionDataChange(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
        {
            MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;

            if (notification == null)
            {
                return;
            }

            ItemInfo tmpItemInfo = GetItemInfo((NodeId)monitoredItem.Handle);

            tmpItemInfo.Value = Utils.Format("{0}", notification.Value.WrappedValue);
            tmpItemInfo.StatusCode = Utils.Format("{0}", notification.Value.StatusCode);
            tmpItemInfo.TimeStamp = Utils.Format("{0:HH:mm:ss.fff}", notification.Value.SourceTimestamp.ToLocalTime());
        }

        private ItemInfo GetItemInfo(NodeId nodeid)
        {
            ItemInfo tmpItemInfo = null;
            lock (SubcriptionItemsConlection)
            {
                foreach (var i in SubcriptionItemsConlection)
                {
                    if (i.Reference.NodeId.ToString().Equals(nodeid.ToString(),StringComparison.CurrentCultureIgnoreCase))
                    {
                        tmpItemInfo = i;
                        break;
                    }
                }
            }

            return tmpItemInfo;
        }

        private void ua_ReconnectComplete(object sender, EventArgs e)
        {
            //throw new NotImplementedException();
        }

        private void ua_KeepAliveComplete(object sender, EventArgs e)
        {
            //MessageBox.Show("连接成功!");
            ReferenceDescriptionCollection referenceDescriptionCollection = ua.BrowseNodes(ObjectIds.ObjectsFolder);
        }

        private void ua_ConnectComplete(object sender, EventArgs e)
        {
            //MessageBox.Show("连接成功!");
            btnConnect.IsEnabled = false;
            ReferenceDescriptionCollection referenceDescriptionCollection = ua.BrowseNodes(ObjectIds.ObjectsFolder);
            FillTree(referenceDescriptionCollection);
        }

        private async  void btnConnect_Click(object sender, RoutedEventArgs e)
        {
            string tmpUrl = tbUrl.Text.Trim();
            if (string.IsNullOrEmpty(tmpUrl))
            {
                MessageBox.Show("地址不能为空", "提示", MessageBoxButton.OK);
                return;
            }

            try
            {
                 await ua.Connect(tmpUrl);
            }
            catch (Exception ex)
            {
                ShowMessage("error",ex);
            }

        }

        private void ShowMessage(string caption, Exception e)
        {
            MessageBox.Show(e.Message,caption,MessageBoxButton.OK);
        }

        private void btnBrowse_Click(object sender, RoutedEventArgs e)
        {
            ReferenceDescriptionCollection collection = ua.BrowseNodes(ObjectIds.ObjectsFolder);
            //throw new NotImplementedException();
        }

        private void FillTree(List ls)
        {
            tvNodes.Items.Clear();
            for (int i = 0; i < ls.Count; i++)
            {
                TreeViewItem item = new TreeViewItem();
                item.Header = ls[i].DisplayName;
                item.Tag = ls[i];
                item.Items.Add("*");//占位符
                item.Expanded += Item_Expanded;
                item.Selected += SubItem_Selected;
                tvNodes.Items.Add(item);
            }
        }

        private void Item_Expanded(object sender, RoutedEventArgs e)
        {
            TreeViewItem item = e.OriginalSource as TreeViewItem;
            item.Items.Clear();
            ReferenceDescription refDes = item.Tag as ReferenceDescription;

            if (refDes == null || refDes.NodeId.IsAbsolute)
            {
                return;
            }

            ReferenceDescriptionCollection nodes = ua.BrowseNodes((NodeId)refDes.NodeId);

            for (int i = 0; i < nodes.Count; i++)
            {
                TreeViewItem subItem = new TreeViewItem();
                subItem.Header = nodes[i].DisplayName;
                subItem.Tag = nodes[i];
                subItem.Items.Add("*");
                subItem.Expanded += Item_Expanded;
                subItem.Selected += SubItem_Selected;
                item.Items.Add(subItem);
            }
        }

        private void SubItem_Selected(object sender, RoutedEventArgs e)
        {
            ItemsConlection.Clear();
            TreeViewItem item = e.OriginalSource as TreeViewItem;
            ReferenceDescription reference = item.Tag as ReferenceDescription;
            DisplayAttributes((NodeId)reference.NodeId);
            //throw new NotImplementedException();
        }

        /// 
        /// Displays the attributes and properties in the attributes view.
        /// 
        /// The NodeId of the Node to browse.
        private void DisplayAttributes(NodeId sourceId)
        {
            //try
            //{
            ReadValueIdCollection nodesToRead = new ReadValueIdCollection();

            // attempt to read all possible attributes.
            for (uint ii = Attributes.NodeClass; ii <= Attributes.UserExecutable; ii++)
            {
                ReadValueId nodeToRead = new ReadValueId();
                nodeToRead.NodeId = sourceId;
                nodeToRead.AttributeId = ii;
                nodesToRead.Add(nodeToRead);
            }

            int startOfProperties = nodesToRead.Count;

            //// find all of the pror of the node.
            //BrowseDescription nodeToBrowse1 = new BrowseDescription();

            //nodeToBrowse1.NodeId = sourceId;
            //nodeToBrowse1.BrowseDirection = BrowseDirection.Forward;
            //nodeToBrowse1.ReferenceTypeId = ReferenceTypeIds.HasProperty;
            //nodeToBrowse1.IncludeSubtypes = true;
            //nodeToBrowse1.NodeClassMask = 0;
            //nodeToBrowse1.ResultMask = (uint)BrowseResultMask.All;

            //BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection();
            //nodesToBrowse.Add(nodeToBrowse1);

            //// fetch property references from the server.
            //ReferenceDescriptionCollection references = ua.Browse(nodesToBrowse, true);
            ReferenceDescriptionCollection references = ua.BrowseNodes(sourceId);

            if (references == null)
            {
                return;
            }

            for (int ii = 0; ii < references.Count; ii++)
            {
                // ignore external references.
                if (references[ii].NodeId.IsAbsolute)
                {
                    continue;
                }

                ReadValueId nodeToRead = new ReadValueId();
                nodeToRead.NodeId = (NodeId)references[ii].NodeId;
                nodeToRead.AttributeId = Attributes.Value;
                nodesToRead.Add(nodeToRead);
            }

            // read all values.
            DataValueCollection results = null;
            DiagnosticInfoCollection diagnosticInfos = null;

            ua.MySession.Read(
                null,
                0,
                TimestampsToReturn.Neither,
                nodesToRead,
                out results,
                out diagnosticInfos);

            ClientBase.ValidateResponse(results, nodesToRead);
            ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);

            // process results.
            for (int ii = 0; ii < results.Count; ii++)
            {
                string name = null;
                string datatype = null;
                string value = null;
                ReferenceDescription tmpReferencedes = null;
                DataValue tmpDataValue = results[ii];

                // process attribute value.
                if (ii < startOfProperties)
                {
                    // ignore attributes which are invalid for the node.
                    if (results[ii].StatusCode == StatusCodes.BadAttributeIdInvalid)
                    {
                        continue;
                    }

                    // get the name of the attribute.
                    name = Attributes.GetBrowseName(nodesToRead[ii].AttributeId);

                    // display any unexpected error.
                    if (StatusCode.IsBad(results[ii].StatusCode))
                    {
                        datatype = Utils.Format("{0}", Attributes.GetDataTypeId(nodesToRead[ii].AttributeId));
                        value = Utils.Format("{0}", results[ii].StatusCode);
                    }

                    // display the value.
                    else
                    {
                        TypeInfo typeInfo = TypeInfo.Construct(results[ii].Value);

                        datatype = typeInfo.BuiltInType.ToString();

                        if (typeInfo.ValueRank >= ValueRanks.OneOrMoreDimensions)
                        {
                            datatype += "[]";
                        }

                        value = Utils.Format("{0}", results[ii].Value);
                    }
                }

                // process property value.
                else
                {
                    // ignore properties which are invalid for the node.
                    if (results[ii].StatusCode == StatusCodes.BadNodeIdUnknown)
                    {
                        continue;
                    }

                    // get the name of the property.
                    name = Utils.Format("{0}", references[ii - startOfProperties]);

                    tmpReferencedes = references[ii - startOfProperties];

                    // display any unexpected error.
                    if (StatusCode.IsBad(results[ii].StatusCode))
                    {
                        datatype = String.Empty;
                        value = Utils.Format("{0}", results[ii].StatusCode);
                    }

                    // display the value.
                    else
                    {
                        TypeInfo typeInfo = TypeInfo.Construct(results[ii].Value);

                        datatype = typeInfo.BuiltInType.ToString();

                        if (typeInfo.ValueRank >= ValueRanks.OneOrMoreDimensions)
                        {
                            datatype += "[]";
                        }

                        value = Utils.Format("{0}", results[ii].Value);

                    }
                }

                // add the attribute name/value to the list view.
                ItemInfo tmpModel = new ItemInfo()
                {
                    Name = name,
                    DataType = datatype,
                    Value = value,
                    Reference = tmpReferencedes,
                    ODataValue = tmpDataValue
                };
                ItemsConlection.Add(tmpModel);
            }
            //}
            //catch (Exception e)
            //{
            //    Utils.Trace(e, "Unexpected error in '{0}'.", "error"); //待修改
            //    return;
            //}

        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            dgDetial.ItemsSource = ItemsConlection;
            dgSub.ItemsSource = SubcriptionItemsConlection;
            //dgDetial.DataContext = ItemsConlection;
        }

        private void DisConnect_Click(object sender, RoutedEventArgs e)
        {
            ua.Disconnect();
            btnConnect.IsEnabled = true;
            tvNodes.Items.Clear();
            ItemsConlection.Clear();
        }

        private void dgDetial_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            DataGrid tmpDg = sender as DataGrid;
            int tmpSelectedIndex = tmpDg.SelectedIndex;
            if (tmpSelectedIndex == -1) return;
            CurrentSelectedItem = ItemsConlection[tmpSelectedIndex]; ;
        }

        private void mi_Click(object sender, RoutedEventArgs e)
        {
            if (CurrentSelectedItem == null)
            {
                MessageBox.Show("请选择要操作的条目!");
                return;
            }

            MenuItem tmpMi = sender as MenuItem;
            switch (tmpMi.Tag)
            {
                case "0": //同步读
                    DataValue readValue = ua.MySession.ReadValue((NodeId)CurrentSelectedItem.Reference.NodeId);
                    CurrentSelectedItem.Value = readValue.Value.ToString();
                    break;
                case "1": //异步读
                    ReadValueIdCollection tmpReads = new ReadValueIdCollection();
                    ReadValueId tmpReadId = new ReadValueId();
                    tmpReadId.NodeId = (NodeId)CurrentSelectedItem.Reference.NodeId;
                    tmpReadId.AttributeId = Attributes.Value;
                    tmpReads.Add(tmpReadId);
                    ua.MySession.BeginRead(null,0,TimestampsToReturn.Neither,tmpReads,callback, tmpReads);
                    break;
                case "2": //同步写
                    WriteValueWin f = new WriteValueWin(CurrentSelectedItem,ua);
                    f.ShowDialog();
                    break;
                case "3": //异步写
                    break;
                case "4": //订阅
                    lock (SubcriptionItemsConlection)
                    {
                        if (SubcriptionItemsConlection.Contains(CurrentSelectedItem))
                        {
                            MessageBox.Show("订阅列表中已经存在着项!");
                            return;
                        }
                        MonitoredItem tmpMonitoredItem = ua.CreateMonitoredItem((NodeId) CurrentSelectedItem.Reference.NodeId,
                            CurrentSelectedItem.Name);
                        CurrentSelectedItem.MonitoredItem = tmpMonitoredItem;
                        SubcriptionItemsConlection.Add(CurrentSelectedItem); 
                    }
                    break;
                case "5": //取消订阅
                    break;
                default:
                    CurrentSelectedItem = null;
                    break;
            }
        }

        private void callback(IAsyncResult ar)
        {
            if (ar.IsCompleted)
            {
                DataValueCollection results;
                DiagnosticInfoCollection diagnosticinfos;
                ReadValueIdCollection tmpReadIds  = ar.AsyncState as ReadValueIdCollection;
                ResponseHeader responseHeader = ua.MySession.EndRead(ar, out results, out diagnosticinfos);
                ClientBase.ValidateResponse(results, tmpReadIds);
                ClientBase.ValidateDiagnosticInfos(diagnosticinfos,tmpReadIds);
                CurrentSelectedItem.Value = results[0].Value.ToString();
            }

            //throw new NotImplementedException();
        }

        private bool SubCollectionIsPro = false;
        private void BtnDeleteSub_OnClick(object sender, RoutedEventArgs e)
        {
            SubCollectionIsPro = true;
            if (SubSelectedItems.Count < 1)
            {
                MessageBox.Show("请选择取消订阅的条目!");
                return;
            }

            List tmpMonitors = new List();
            for (int i=0;i< SubSelectedItems.Count;i++)
            {
                tmpMonitors.Add(SubSelectedItems[i].MonitoredItem);
                lock (SubcriptionItemsConlection)
                {
                    SubcriptionItemsConlection.Remove(SubSelectedItems[i]); 
                }
            }

            ua.DeleteMonitoredItem(tmpMonitors);

            SubSelectedItems.Clear();
            SubCollectionIsPro = false;
        }

        private void dgSub_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (SubCollectionIsPro) return;
            SubSelectedItems.Clear();
            var eOriginalSource = (DataGrid)e.OriginalSource;
            foreach (var item in eOriginalSource.SelectedItems)
            {
                ItemInfo tmpRow = item as ItemInfo;
                if (!SubSelectedItems.Contains(tmpRow))
                {
                    SubSelectedItems.Add(tmpRow);
                }
            }

        }
    }

    public class ItemInfo : INotifyPropertyChanged
    {
        private string _name;
        private string _dataType;
        private string _value;

        public string Name
        {
            get => _name;
            set
            {
                if (value == _name) return;
                _name = value;
                OnPropertyChanged();
            }
        }

        public string DataType
        {
            get => _dataType;
            set
            {
                if (value == _dataType) return;
                _dataType = value;
                OnPropertyChanged();
            }
        }

        public string Value
        {
            get => _value;
            set
            {
                if (value == _value) return;
                _value = value;
                OnPropertyChanged();
            }
        }

        private string statueCode;

        public string StatusCode
        {       
            get { return statueCode; }
            set
            {
                if (value == statueCode) return;
                statueCode = value;
                OnPropertyChanged();
            }
        }

        private string timeStamp;

        public string TimeStamp
        {
            get { return timeStamp; }
            set
            {
                if (value == timeStamp) return;
                timeStamp = value;
                OnPropertyChanged();
            }
        }

        public MonitoredItem MonitoredItem { get; set; }
        public ReferenceDescription Reference { get; set; }
        public DataValue ODataValue { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}




    
        
            
            
            
        
    




    
        
    
    
        
            
                
                
            
            
                opc.tcp://127.0.0.1:49320
                
                
            
            
                
                    
                    
                    
                
                
                    
                    
                    
                
                
                
                
                    
                        
                        
                        
                    
                    
                        
                            
                                
                                
                                
                                
                                
                                
                            
                        
                    
                
                 
                
                    
                        
                            
                            
                            
                            
                            
                        
                        
                            
                                
                                    
                                
                            
                        
                    
                
            
        
    




using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Opc.Ua;

namespace UAClient
{
    /// 
    /// WriteValueWin.xaml 的交互逻辑
    /// 
    public partial class WriteValueWin : Window
    {
        private ItemInfo Item = null;
        private UAHelper Ua = null;
        public WriteValueWin(ItemInfo item,UAHelper ua)
        {
            InitializeComponent();
            this.Item = item;
            this.Ua = ua;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
             string tmpValue = tbValue.Text.Trim();
            Button tmpBtn = sender as Button;
            if (tmpBtn.Tag.Equals("0"))
            {
                Ua.WriteValue((NodeId)Item.Reference.NodeId, Item.ODataValue, tmpValue);
            }
            else
            {
                //异步写
                Ua.BeginWrite((NodeId)Item.Reference.NodeId,Item.ODataValue, tmpValue,calback);              
            }
        }

        private void calback(IAsyncResult ar)
        {
            StatusCodeCollection results = null;
            DiagnosticInfoCollection diagnosticinfos  =null;
            if (ar.IsCompleted)
            {   
                WriteValueCollection writeValues = ar.AsyncState as WriteValueCollection; 
                Ua.MySession.EndWrite(ar, out results, out diagnosticinfos);
                ClientBase.ValidateResponse(results, writeValues);
                ClientBase.ValidateDiagnosticInfos(diagnosticinfos, writeValues);
            }
            if (StatusCode.IsBad(results[0]))
            {
                throw new ServiceResultException(results[0]);
            }
            //throw new NotImplementedException();
        }
    }
}

四:做了简单的封装,为了测试用,大家可以直接安装nuget ,搜UAClient.zlw

或者Package Manager安装  Install-Package UAClient.zlw -Version 1.0.0

NET CLI   dotnet add package UAClient.zlw --version 1.0.0   

注:仅供大家一起学习。

你可能感兴趣的:(KepServer,C#)