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

Posted

技术标签:

【中文标题】C# OPC 应用程序代码相同,但工作方式不同【英文标题】:C# OPC Applications Identical Code, but work differently 【发布时间】:2015-07-27 08:24:16 【问题描述】:

我正在开发一个 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("-------------------<");
        

这是 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);

            

        

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; 


        

有什么问题欢迎提问!

【问题讨论】:

UIUpdater 是什么? 只是一个更新UserInterface Thread的类 你能贴出它的代码吗?这看起来像一个跨线程问题。我自己使用 OPC 开发了很多应用程序,RSLinx 非常强大,看起来您正在连接到 Micrologix 或 SLC,或者 PLC5 时代的东西...... 添加了 UI 类,以及一些额外的 windows 窗体代码 使readplcOnTransactionCompleted 非静态。那么你应该让servergroup 成为Form1 类的成员,而不是只在方法的范围内。 【参考方案1】:

正如怀疑的那样,这是一个跨线程问题。问题是您不能从 UI 线程以外的任何其他线程更新 UI。事务完成的事件实际上是在一个单独的线程上调用的,因此它会更新 UI。

它工作了一段时间,因为它相对容忍错误,但是您可能已经达到了死锁或抛出未被捕获(或报告)的异常的地步。

虽然修复很简单。

在这个方法中:

public void update(string message)

    this.richTextBox1.Text = message;

改成:

public void update(string message)

    richTextBox1.Invoke(
      (MethodInvoker) delegate 
       
          richTextBox1.Text = message; 
      );

它的作用是告诉richTextBox1“调用”或在其拥有的线程(也称为 UI 线程)上运行以下委托(函数)。

你真的应该尽量避免在这段代码中使用static 方法和引用。我看不出你的代码不应该是实例方法而不是静态方法的任何理由。

顺便说一句,我编写的 OPC 程序每秒处理数以千计的标签和数百次 UI 更新。您正在做的事情适用于小型演示程序,但不会很好地扩展。当架构增长时,您需要开始批处理 UI 更新,这样您就不会忙于在更新中重复调用 UI 线程。

编辑

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

【讨论】:

刚把代码改成Invoke,还是没效果,这么久还是crash【参考方案2】:

将您的服务器放在 readplc() 方法的外部作为表单级对象。 只要您的表单被实例化(未关闭) - 您的服务器对象将处于活动状态,您的订阅事件也应该如此。

垃圾收集器很可能正在收集服务器。

 Opc.Da.Server server = null;

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();
            **this.server = new Opc.Da.Server(fact, null);**
    ....
    

【讨论】:

以上是关于C# OPC 应用程序代码相同,但工作方式不同的主要内容,如果未能解决你的问题,请参考以下文章

尽管代码相同,为啥这两个 html/css 文件的工作方式不同? [关闭]

为啥我的 C# 应用程序在此 REST 请求上失败,但通过浏览器可以正常工作?

Paypal REST api 调用适用于 cURL,但不适用于 C# 代码

php函数file_exits,copy,shell_exec不在一台机器上工作,但相同的代码在不同的机器上工作

SQL Server 中的 NEXT VALUE FOR @Sequence 的工作方式是不是与 C# 中的 Interlocked.Increment() 相同? [复制]

Beautiful Soup 4 CSS 选择器的工作方式与教程显示的方式不同