C#OPC应用程序相同的代码,但工作方式不同

SK2*_*017 10 c# plc console-application opc winforms

我正在开发一个C#自定义OPC客户端,我开始在控制台应用程序中快速写入,一切都按照我的要求完美运行.

然后我决定制作一个Windows窗体应用程序以获得视觉体验.

Windows窗体应用程序只是停止工作,大约一分钟后停止从OPC服务器读取数据.控制台应用程序在哪里继续阅读和阅读.

在调试模式中我也找不到任何明显的东西.

我绝对抓着稻草在这里,希望有人可以解决一些问题.

每个应用程序都使用OPCFoundation提供的.dll文件.

这是控制台应用程序

 static void Main(string[] args)
        {

            Opc.URL url = new Opc.URL("opcda://localhost/RSLinx OPC Server");
            Opc.Da.Server server = null;
            OpcCom.Factory fact = new OpcCom.Factory();
            server = new Opc.Da.Server(fact, null);
            server.Connect(url, new Opc.ConnectData(new System.Net.NetworkCredential()));
            // Create a group
            Opc.Da.Subscription group;
            Opc.Da.SubscriptionState groupState = new Opc.Da.SubscriptionState();
            groupState.Name = "Group";
            groupState.Active = true;
            group = (Opc.Da.Subscription)server.CreateSubscription(groupState);
            // add items to the group.
            Opc.Da.Item[] items = new Opc.Da.Item[6];
            items[0] = new Opc.Da.Item();
            items[0].ItemName = "[UX1]F20:9";
            items[1] = new Opc.Da.Item();
            items[1].ItemName = "[UX1]F22:30";
            items[2] = new Opc.Da.Item();
            items[2].ItemName = "[UX1]F22:6";
            items[3] = new Opc.Da.Item();
            items[3].ItemName = "[UX1]F18:8";
            items[4] = new Opc.Da.Item();
            items[4].ItemName = "[UX1]F22:32";
            items[5] = new Opc.Da.Item();
            items[5].ItemName = "[UX1]F22:5";
            items = group.AddItems(items);

                group.DataChanged += new Opc.Da.DataChangedEventHandler(OnTransactionCompleted);

        }





        static void OnTransactionCompleted(object group, object hReq, Opc.Da.ItemValueResult[] items)
        {

            Console.WriteLine("------------------->");
            Console.WriteLine("DataChanged ...");
            for (int i = 0; i < items.GetLength(0); i++)
            {

                    Console.WriteLine("Item DataChange - ItemId: {0}", items[i].ItemName);
                    Console.WriteLine(" Value: {0,-20}", items[i].Value);
                    Console.WriteLine(" TimeStamp: {0:00}:{1:00}:{2:00}.{3:000}",
                    items[i].Timestamp.Hour,
                    items[i].Timestamp.Minute,
                    items[i].Timestamp.Second,
                    items[i].Timestamp.Millisecond);

            }
            Console.WriteLine("-------------------<");
        }
Run Code Online (Sandbox Code Playgroud)

这是WinForm应用程序

 public Form1()

    {
        InitializeComponent();
        _Form1 = this;
    }

    public static Form1 _Form1;

    public void update(string message)

    {
        this.richTextBox1.Text = message;
    }

    private void Form1_Load(object sender, EventArgs e)

    {

        readplc();

    }


static void readplc()
        {
                Opc.URL url = new Opc.URL("opcda://localhost/RSLinx OPC Server");
            Opc.Da.Server server = null;
            OpcCom.Factory fact = new OpcCom.Factory();
            server = new Opc.Da.Server(fact, null);
            server.Connect(url, new Opc.ConnectData(new System.Net.NetworkCredential()));
            // Create a group
            Opc.Da.Subscription group;
            Opc.Da.SubscriptionState groupState = new Opc.Da.SubscriptionState();
            groupState.Name = "Group";
            groupState.Active = true;
            group = (Opc.Da.Subscription)server.CreateSubscription(groupState);
            // add items to the group.
            Opc.Da.Item[] items = new Opc.Da.Item[6];
            items[0] = new Opc.Da.Item();
            items[0].ItemName = "[UX1]F20:9";
            items[1] = new Opc.Da.Item();
            items[1].ItemName = "[UX1]F22:30";
            items[2] = new Opc.Da.Item();
            items[2].ItemName = "[UX1]F22:6";
            items[3] = new Opc.Da.Item();
            items[3].ItemName = "[UX1]F18:8";
            items[4] = new Opc.Da.Item();
            items[4].ItemName = "[UX1]F22:32";
            items[5] = new Opc.Da.Item();
            items[5].ItemName = "[UX1]F22:5";
            items = group.AddItems(items);



                group.DataChanged += new Opc.Da.DataChangedEventHandler(OnTransactionCompleted);


        }




      static void OnTransactionCompleted(object group, object hReq, Opc.Da.ItemValueResult[] items)
        {

            for (int i = 0; i < items.GetLength(0); i++)
            {

                UIUpdater TEXT = new UIUpdater();
                    TEXT.UpdateText(items.GetLength(0).ToString() + " t " + i.ToString() + "Item DataChange - ItemId:" + items[i].ItemName +
                       "Value: " + items[i].Value + " TimeStamp: " + items[i].Timestamp.Hour + ":" +
                      items[i].Timestamp.Minute + ":" + items[i].Timestamp.Second + ":" + items[i].Timestamp.Millisecond);

            }

        }
Run Code Online (Sandbox Code Playgroud)

UIUpdate类

class UIUpdater

    {

       public void UpdateText(string DATA)

        {
            Form1._Form1.update(DATA);
        }  

        public class UpdateUI

        {



            public int updatedRows { get; set; }

            public string Custom1 { get; set; }

            public string Custom2 { get; set; }

            public string Custom3 { get; set; }

            public string exception { get; set; }

            public plcTextStatus PLCStatus { get; set; }


        }
Run Code Online (Sandbox Code Playgroud)

如有任何问题请咨询!

Ron*_*yer 5

怀疑,这是一个跨线程问题.问题是您无法从UI线程以外的任何其他线程更新UI.事务完成的事件实际上是在一个单独的线程上调用的,因此它更新了UI.

它可以工作一段时间,因为它相对容忍错误,但是你可能已达到一个点,即你是死锁或抛出一个未被捕获(或报告)的异常.

虽然修复很简单.

在这个方法中:

public void update(string message)
{
    this.richTextBox1.Text = message;
}
Run Code Online (Sandbox Code Playgroud)

将其更改为:

public void update(string message)
{
    richTextBox1.Invoke(
      (MethodInvoker) delegate 
      { 
          richTextBox1.Text = message; 
      });
}
Run Code Online (Sandbox Code Playgroud)

这样做是告诉它richTextBox1"调用"或在其拥有的线程(也就是UI线程)上运行以下委托(函数).

您应该尽量避免static在此代码中使用方法和引用.我认为你所拥有的代码不应该是实例方法而不是静态方法.

正如旁注,我编写的OPC程序每秒处理数千个标签和数百个UI更新.你正在做什么适用于小型演示程序,但不会很好地扩展.当架构增长时,您需要开始批量处理UI更新,这样您就不会在更新内部重复调用UI线程.

编辑

您遇到的另一个问题是使用本地引用(例如OPC服务器和订阅)并演示内存泄漏和僵尸对象.发生的事情是该readplc方法超出范围,并且您已创建对内部对象的引用.由于您无法取消订阅该事件,因此该事件会继续触发.这些变量应在readplc方法范围之外声明,以便您可以正确取消订阅事件并关闭OPC服务器.否则你将离开zombie订阅(查看RSLinx OPC Diagnostics页面,你会看到你所有的订阅都在那里).