4

我对混合事件和线程知之甚少。场景是在 PC 上运行 C# 程序,在 PLC 上运行 Twincat。我们需要访问 C# 程序中的 PLC 变量(已经在没有后台工作线程的情况下完成并且工作正常。),现在我们需要将这些处理移动到一个线程(最好是后台工作线程)。这是不工作的代码。(表单包含一个 START 按钮,它将启动 BGWorker,一个 STOP 按钮将取消 BGWorker,以及一个 UPDATE 按钮,它将值从 PLC 更新到文本框。),但现在 tcClient_OnNotification没有被调用!请指出我遗漏的地方,任何帮助将不胜感激。

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;


    using System.Threading;         // not added by default
    using System.IO;                // not added by default
    using TwinCAT.Ads;              // not added by default

    namespace BGworker
    {
        public partial class Form1 : Form
        {
            private BackgroundWorker bw = new BackgroundWorker();
            private TcAdsClient tcClient;   // C# program is the client.
            private AdsStream dataStream;   // Data transfered through System IOStream
            private BinaryReader binReader; // We are now reading value from PLC
            private int Hintval;            // Handle for integer value

            public static bool looping = true;
            public static string receivedtext = "";

            public Form1()
            {
                InitializeComponent();

                bw.WorkerReportsProgress = true;
                bw.WorkerSupportsCancellation = true;
                bw.DoWork += new DoWorkEventHandler(bw_DoWork);
                bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
                bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
            }

            private void Form1_Load(object sender, EventArgs e)
            {

            }


            private void Startbutton_Click(object sender, EventArgs e)
            {
                if (bw.IsBusy != true)
                {
                    bw.RunWorkerAsync();
                }

            }




            private void Stopbutton_Click(object sender, EventArgs e)
            {
                if (bw.WorkerSupportsCancellation == true)
                {
                    bw.CancelAsync();
                }
            }


            public void bw_DoWork(object sender, DoWorkEventArgs e)
            {
                BackgroundWorker worker = sender as BackgroundWorker;
                dataStream = new AdsStream(1 * 2); // Single value will be read
                binReader = new BinaryReader(dataStream, Encoding.ASCII);
                tcClient = new TcAdsClient();
                tcClient.Connect(801);
                //Hintval = tcClient.CreateVariableHandle(".GOUTINT");
                Hintval = tcClient.AddDeviceNotification(".GOUTINT", dataStream, 0, 2, AdsTransMode.OnChange, 100, 0, null);
                tcClient.AdsNotification += new AdsNotificationEventHandler(tcClient_OnNotification);

                while (true)
                {
                    if ((worker.CancellationPending == true))
                    {
                        e.Cancel = true;
                        break;
                    }
                    else
                    {
                        System.Threading.Thread.Sleep(100);
                        //worker.ReportProgress((5* 10));

                    }
                }

                tcClient.Dispose();
            }


            public void tcClient_OnNotification(object sender, AdsNotificationEventArgs e)
            {
                try
                {
                    // Setting the position of e.DataStream to the position of the current required value
                    e.DataStream.Position = e.Offset;

                    // Determining which variable has changed
                    if (e.NotificationHandle == Hintval)
                    {
                        receivedtext = binReader.ReadInt16().ToString();
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }

            private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                if ((e.Cancelled == true))
                {
                    this.tbProgress.Text = "Canceled!";
                }

                else if (!(e.Error == null))
                {
                    this.tbProgress.Text = ("Error: " + e.Error.Message);
                }

                else
                {
                    this.tbProgress.Text = "Done!";
                }
            }
            private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
            }

            private void buttonUpdate_Click(object sender, EventArgs e)
            {
                this.tbProgress.Text = receivedtext;
            }

        }
    }

提前致谢。阿比拉什。

4

3 回答 3

7

查看TcAdsClient.Synchronize

TwinCAT ADS.NET 帮助说:

如果 Synchronize 设置为 true,则通知将同步到主线程。这对于 Windows 窗体项目是必需的。在控制台应用程序中,此属性应设置为 false。

于 2012-04-19T14:34:38.263 回答
5

您绝对应该检查以下内容是否适合您:

myTcAdsClient.Synchronize = false 

在初始化 TcAdsClient 实例后立即执行此操作。在严重依赖主线程的基于 GUI 的应用程序中,将 Synchronize 设置为 true 最有意义。

在我当前的项目中,我围绕 TcAdsClient 创建了一个包装器类,以便能够在启动和停止 TwinCat 环境的 Windows 服务中使用它,但是包装器设备类将 AdsClient 托管在一个单独的线程上(在一个无限的 Run() 循环中)。

对于 TwinCat 变量的更改通知,我的包装类提供了一个自己的事件,Windows 服务连接到该事件;只要底层 TwinCat 客户端的 AdsNotificationExEventHandler 在设备包装类中被触发,就会触发它。当我在 WindowsForms 应用程序中测试此设置时,一切正常。但不在控制台应用程序中,也不在我的 Windows 服务中 - AdsNotificationExEventHandler 从未触发。关键是 TcAdsClient 的线程同步功能 - 默认设置是尝试将所有通知同步到主线程,这不是我设置的正确选择。对你来说似乎也是如此。

于 2014-03-04T07:32:17.067 回答
0

我猜这TcAdsClient是他自己在线程上收到通知,尝试在使用标准消息循环创建它的线程上调用您的事件。

问题是您在ThreadPool线程上创建了它并且您没有在那里汇集任何消息,因此您的方法永远不会被调用。

BackgroundWorker似乎完全没用,因为无论如何它没有做任何工作。只需删除它。

于 2011-11-04T08:32:51.117 回答