首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C# OPC应用程序代码相同,但工作方式不同

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

Stack Overflow用户
提问于 2015-05-15 12:00:56
回答 2查看 1.6K关注 0票数 9

我正在开发一个C#自定义OPC客户端,我开始在一个控制台应用程序中编写快速性,一切都像我所希望的那样完美。

然后,我决定为视觉体验制作一个windows表单应用程序。

windows窗体应用程序只是在大约一分钟后停止工作,停止从OPC服务器读取数据。当控制台应用程序不停地阅读和阅读的时候。

在调试模式下,我也找不到任何明显的东西。

我绝对是抓住了这里的吸管,希望有人能给我一些启示。

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

这里是控制台应用程序

代码语言:javascript
复制
 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应用程序

代码语言:javascript
复制
 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类

代码语言:javascript
复制
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; }


        }

任何问题请问!

EN

回答 2

Stack Overflow用户

发布于 2015-05-15 12:38:20

正如所怀疑的,这是一个交叉线程问题。问题是,除了UI线程之外,您不能从任何其他线程更新UI。事务完成的事件实际上在一个单独的线程上被调用,因此它更新UI。

它工作了一段时间,因为它对错误的容忍度相对较高,但是,您可能会达到这样的程度,即您正处于死锁状态,或者抛出了一个没有被捕获(或报告)的异常。

不过,修复方法很简单。

在这种方法中:

代码语言:javascript
复制
public void update(string message)
{
    this.richTextBox1.Text = message;
}

改为:

代码语言:javascript
复制
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诊断页面,您将看到您的所有订阅都在那里)。

票数 5
EN

Stack Overflow用户

发布于 2015-09-23 15:20:13

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

服务器很可能是由垃圾收集器收集的。

代码语言:javascript
复制
 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);**
    ....
    }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30259029

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档