简体   繁体   English

OPC UA-.net Standard C# 简单控制台客户端

[英]OPC UA-.net Standard C# simple console client

I am trying out the SampleApplication NetCoreConsoleClient from OPC UA Foundation GitHub page OPC-UA.net Standard and I came to several problems along the way.我正在尝试来自 OPC UA Foundation GitHub 页面OPC-UA.net Standard的 SampleApplication NetCoreConsoleClient,并且在此过程中遇到了几个问题。

I wanted to use this library to simply read the data sent by the server (i am using Prosys OPC UA Server) and write it out in the console.我想使用这个库来简单地读取服务器发送的数据(我使用的是 Prosys OPC UA 服务器)并将其写在控制台中。 I have struggled to get to the actual data variables that I am sending with the server.我一直在努力获取我与服务器一起发送的实际数据变量。 I manage to connect to it and subscribe but I can't get the desired MonitoredItem values to be written out with the onNotification method.我设法连接到它并订阅,但我无法使用onNotification方法写出所需的 MonitoredItem 值。

Console.WriteLine("6 - Add a list of items (server current time and status) to the subscription.");
exitCode = ExitCode.ErrorMonitoredItem;
var list = new List<MonitoredItem> 
{
    new MonitoredItem(subscription.DefaultItem)
    {
        DisplayName = "ServerStatusCurrentTime", StartNodeId = "i="+Variables.Server_ServerStatus_CurrentTime.ToString()
    }
};
list.ForEach(i => i.Notification += OnNotification);
subscription.AddItems(list);

here the sample adds a new MonitoredItem to the list.此处示例将一个新的 MonitoredItem 添加到列表中。 When I tried adding my own item I never got any response from it even though the Server is all the time sending changed values and so it should trigger the onNotification method.当我尝试添加自己的项目时,我从未收到任何响应,即使服务器一直在发送更改的值,因此它应该触发onNotification方法。

I get the DisplayName and StartNodeId of desired value from this part:我从这部分获得所需值的 DisplayName 和 StartNodeId:

foreach (var rd in references)
{
    Console.WriteLine(" {0}, {1}, {2}", rd.DisplayName, rd.BrowseName, rd.NodeClass);
    ReferenceDescriptionCollection nextRefs;
    byte[] nextCp;
    session.Browse(
    null,
    null,
    ExpandedNodeId.ToNodeId(rd.NodeId, session.NamespaceUris),
                0u,
                BrowseDirection.Forward,
                ReferenceTypeIds.HierarchicalReferences,
                true,
                (uint)NodeClass.Variable | (uint)NodeClass.Object | (uint)NodeClass.Method,
                out nextCp,
                out nextRefs);

    foreach (var nextRd in nextRefs)
    {
        Console.WriteLine("   + {0}, {1}, {2}", nextRd.DisplayName, nextRd.BrowseName, nextRd.NodeClass);
    }
}

and so:所以:

var list = new List<MonitoredItem>
{
    new MonitoredItem(subscription.DefaultItem)
    {
        DisplayName = "Simulation", StartNodeId = "ns=2;s=85\:Simulation"
    } 
};

I never get any value returned.我从来没有得到任何回报。 I am somehow confused with the OPC UA standard and its way of packaging the data.我在某种程度上对 OPC UA 标准及其打包数据的方式感到困惑。

I struggled with very similar problems with the fact that the examples available online for OPCFoundation are not very easy to follow.由于 OPCFoundation 在线提供的示例不是很容易理解,因此我遇到了非常相似的问题。 The solution below uses OPCFOundation's library to read from a standard OPC UA server.下面的解决方案使用 OPCFoundation 的库从标准 OPC UA 服务器读取。

I tried the solution below with kepware ServerEX OPC UA Server and works completely stable but I believe same would probably work for Prosys or any standard OPC UA Server with some tweaking.我在 kepware ServerEX OPC UA 服务器上尝试了下面的解决方案并且工作完全稳定,但我相信同样适用于 Prosys 或任何标准的 OPC UA 服务器,并进行一些调整。

Install the following Nuget Package: OPCFoundation.NetStandard.Opc.Ua安装以下 Nuget 包:OPCFoundation.NetStandard.Opc.Ua

References: The answer is an adaptation of multiple answers and some good work by people on stackoverflow.参考资料:答案是对多个答案的改编以及人们在 stackoverflow 上的一些优秀作品。


using System;
using System.Collections.Generic;
using System.Windows.Forms;

using Opc.Ua;   // Install-Package OPCFoundation.NetStandard.Opc.Ua
using Opc.Ua.Client;
using Opc.Ua.Configuration;

using System.Threading;

namespace Test_OPC_UA
{
    public partial class Form1 : Form
    {
        //creating a object that encapsulates the netire OPC UA Server related work
        OPCUAClass myOPCUAServer;

        //creating a dictionary of Tags that would be captured from the OPC UA Server
        Dictionary<String, Form1.OPCUAClass.TagClass> TagList = new Dictionary<String, Form1.OPCUAClass.TagClass>();


        public Form1()
        {
            InitializeComponent();


            //Add tags to the Tag List, For each tag, you have to define the name of the tag and its address
            //the address can typically be found by browsing the OPC UA Server's tree. In the example below
            // The OPC Server had the following hierarchy: M0401 -> CPU945 -> IBatchOutput
            //i used TBC0401 as a name of the tag, you can use any name
            //add as many tags as you want to capture
            TagList.Add("TBC0401", new Form1.OPCUAClass.TagClass("TBC0401", "M0401.CPU945.iBatchOutput"));

            //to initialize the OPC UA Server, provide the IP Address, Port Number, the list of tags you want to capture
            //in some OPC UA servers and kepware aswell the session can be closed by the OPC UA Server, so its better to 
            //allow the class to reinitiate session periodically, before renewing current sessions are closed
            myOPCUAServer = new OPCUAClass("127.0.0.1", "49320", TagList, true, 1, "2");


            //once the OPC Server has been initialized, you can easily read Tag values and even see when they were
            // updated last time
            //as an example i could read the TBC0401 tag by:

            var tagCurrentValue = TagList["TBC0401"].CurrentValue;
            var tagLastGoodValue = TagList["TBC0401"].LastGoodValue;
            var lastTimeTagupdated = TagList["TBC0401"].LastUpdatedTime;

        }



        public class OPCUAClass
        {
            public string ServerAddress { get; set; }
            public string ServerPortNumber { get; set; }
            public bool SecurityEnabled { get; set; }
            public string MyApplicationName { get; set; }
            public Session OPCSession { get; set; }
            public string OPCNameSpace { get; set; }
            public Dictionary<string, TagClass> TagList { get; set; }

            public bool SessionRenewalRequired { get; set; }
            public double SessionRenewalPeriodMins { get; set; }
            public DateTime LastTimeSessionRenewed { get; set; }
            public DateTime LastTimeOPCServerFoundAlive { get; set; }
            public bool ClassDisposing { get; set; }
            public bool InitialisationCompleted { get; set; }
            private Thread RenewerTHread { get; set; }
            public OPCUAClass(string serverAddres, string serverport, Dictionary<string, TagClass> taglist, bool sessionrenewalRequired, double sessionRenewalMinutes, string nameSpace)
            {
                ServerAddress = serverAddres;
                ServerPortNumber = serverport;
                MyApplicationName = "MyApplication";
                TagList = taglist;
                SessionRenewalRequired = sessionrenewalRequired;
                SessionRenewalPeriodMins = sessionRenewalMinutes;
                OPCNameSpace = nameSpace;
                LastTimeOPCServerFoundAlive = DateTime.Now;
                InitializeOPCUAClient();

                if (SessionRenewalRequired)
                {
                    LastTimeSessionRenewed = DateTime.Now;
                    RenewerTHread = new Thread(renewSessionThread);
                    RenewerTHread.Start();
                }
            }

            //class destructor
            ~OPCUAClass()
            {

                ClassDisposing = true;
                try
                {

                    OPCSession.Close();
                    OPCSession.Dispose();
                    OPCSession = null;
                    RenewerTHread.Abort();
                }
                catch { }

            }

            private void renewSessionThread()
            {
                while (!ClassDisposing)
                {
                    if ((DateTime.Now - LastTimeSessionRenewed).TotalMinutes > SessionRenewalPeriodMins
                        || (DateTime.Now - LastTimeOPCServerFoundAlive).TotalSeconds > 60)
                    {
                        Console.WriteLine("Renewing Session");
                        try
                        {
                            OPCSession.Close();
                            OPCSession.Dispose();
                        }
                        catch { }
                        InitializeOPCUAClient();
                        LastTimeSessionRenewed = DateTime.Now;

                    }
                    Thread.Sleep(2000);

                }

            }



            public void InitializeOPCUAClient()
            {
                //Console.WriteLine("Step 1 - Create application configuration and certificate.");
                var config = new ApplicationConfiguration()
                {
                    ApplicationName = MyApplicationName,
                    ApplicationUri = Utils.Format(@"urn:{0}:" + MyApplicationName + "", ServerAddress),
                    ApplicationType = ApplicationType.Client,
                    SecurityConfiguration = new SecurityConfiguration
                    {
                        ApplicationCertificate = new CertificateIdentifier { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\MachineDefault", SubjectName = Utils.Format(@"CN={0}, DC={1}", MyApplicationName, ServerAddress) },
                        TrustedIssuerCertificates = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\UA Certificate Authorities" },
                        TrustedPeerCertificates = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\UA Applications" },
                        RejectedCertificateStore = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\RejectedCertificates" },
                        AutoAcceptUntrustedCertificates = true,
                        AddAppCertToTrustedStore = true
                    },
                    TransportConfigurations = new TransportConfigurationCollection(),
                    TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
                    ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 },
                    TraceConfiguration = new TraceConfiguration()
                };
                config.Validate(ApplicationType.Client).GetAwaiter().GetResult();
                if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
                {
                    config.CertificateValidator.CertificateValidation += (s, e) => { e.Accept = (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted); };
                }

                var application = new ApplicationInstance
                {
                    ApplicationName = MyApplicationName,
                    ApplicationType = ApplicationType.Client,
                    ApplicationConfiguration = config
                };
                application.CheckApplicationInstanceCertificate(false, 2048).GetAwaiter().GetResult();


                //string serverAddress = Dns.GetHostName();
                string serverAddress = ServerAddress; ;
                var selectedEndpoint = CoreClientUtils.SelectEndpoint("opc.tcp://" + serverAddress + ":" + ServerPortNumber + "", useSecurity: SecurityEnabled, operationTimeout: 15000);

                // Console.WriteLine($"Step 2 - Create a session with your server: {selectedEndpoint.EndpointUrl} ");
                OPCSession = Session.Create(config, new ConfiguredEndpoint(null, selectedEndpoint, EndpointConfiguration.Create(config)), false, "", 60000, null, null).GetAwaiter().GetResult();
                {


                    //Console.WriteLine("Step 4 - Create a subscription. Set a faster publishing interval if you wish.");
                    var subscription = new Subscription(OPCSession.DefaultSubscription) { PublishingInterval = 1000 };

                    //Console.WriteLine("Step 5 - Add a list of items you wish to monitor to the subscription.");
                    var list = new List<MonitoredItem> { };
                    //list.Add(new MonitoredItem(subscription.DefaultItem) { DisplayName = "M0404.CPU945.iBatchOutput", StartNodeId = "ns=2;s=M0404.CPU945.iBatchOutput" });

                    list.Add(new MonitoredItem(subscription.DefaultItem) { DisplayName = "ServerStatusCurrentTime", StartNodeId = "i=2258" });

                    foreach (KeyValuePair<string, TagClass> td in TagList)
                    {
                        list.Add(new MonitoredItem(subscription.DefaultItem) { DisplayName = td.Value.DisplayName, StartNodeId = "ns=" + OPCNameSpace + ";s=" + td.Value.NodeID + "" });

                    }


                    list.ForEach(i => i.Notification += OnTagValueChange);
                    subscription.AddItems(list);

                    //Console.WriteLine("Step 6 - Add the subscription to the session.");
                    OPCSession.AddSubscription(subscription);
                    subscription.Create();



                }




            }


            public class TagClass
            {

                public TagClass(string displayName, string nodeID)
                {
                    DisplayName = displayName;
                    NodeID = nodeID;

                }

                public DateTime LastUpdatedTime { get; set; }

                public DateTime LastSourceTimeStamp { get; set; }


                public string StatusCode { get; set; }

                public string LastGoodValue { get; set; }
                public string CurrentValue { get; set; }
                public string NodeID { get; set; }

                public string DisplayName { get; set; }


            }


            public void OnTagValueChange(MonitoredItem item, MonitoredItemNotificationEventArgs e)
            {

                foreach (var value in item.DequeueValues())
                {

                    if (item.DisplayName == "ServerStatusCurrentTime")
                    {
                        LastTimeOPCServerFoundAlive = value.SourceTimestamp.ToLocalTime();

                    }
                    else
                    {
                        if (value.Value != null)
                            Console.WriteLine("{0}: {1}, {2}, {3}", item.DisplayName, value.Value.ToString(), value.SourceTimestamp.ToLocalTime(), value.StatusCode);
                        else
                            Console.WriteLine("{0}: {1}, {2}, {3}", item.DisplayName, "Null Value", value.SourceTimestamp, value.StatusCode);

                        if (TagList.ContainsKey(item.DisplayName))
                        {
                            if (value.Value != null)
                            {
                                TagList[item.DisplayName].LastGoodValue = value.Value.ToString();
                                TagList[item.DisplayName].CurrentValue = value.Value.ToString();
                                TagList[item.DisplayName].LastUpdatedTime = DateTime.Now;
                                TagList[item.DisplayName].LastSourceTimeStamp = value.SourceTimestamp.ToLocalTime();
                                TagList[item.DisplayName].StatusCode = value.StatusCode.ToString();

                            }
                            else
                            {
                                TagList[item.DisplayName].StatusCode = value.StatusCode.ToString();
                                TagList[item.DisplayName].CurrentValue = null;

                            }

                        }

                    }

                }
                InitialisationCompleted = true;
            }

        }

    }
}




I have struggled with reading nodes from the Prosys OPC UA simulation server as well.我也一直在努力从 Prosys OPC UA 模拟服务器读取节点。 There are not many SO questions on C# and OPC UA, I hope this provides useful if anybody stumbles upon this old thread.关于 C# 和 OPC UA 的 SO 问题并不多,如果有人偶然发现这个旧线程,我希望这会有所帮助。

I have found the ConsoleReferenceClient much more helpful than the examples provided in the samples repo.我发现ConsoleReferenceClient比示例存储库中提供的示例更有帮助。

The code is much cleaner, nicely documented and easy to follow.代码更简洁,文档齐全且易于遵循。 It has a ReadNodes method which reads the current value of the node.它有一个ReadNodes方法,用于读取节点的当前值。

Find it here: https://github.com/OPCFoundation/UA-.NETStandard/blob/master/Applications/ConsoleReferenceClient/UAClient.cs在这里找到: https : //github.com/OPCFoundation/UA-.NETStandard/blob/master/Applications/ConsoleReferenceClient/UAClient.cs

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM