C# Backgroundworker 和 Twincat,如何在工作线程内触发通知事件
我对混合事件和线程知之甚少。场景是 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;
}
}
}
提前致谢。 阿比拉什。
I had little knowledge about mixing events and threads. The scenario is that there is a C# program running on a PC and Twincat running on a PLC. We need to access PLC variables inside the C# program ( Already done without a background worker thread and its working fine.) , Now we need to move these processing to a thread ( preferably Background Worker ). Here is the code which is not working.( The form contains a START button , which will start BGworker, a STOP button which will cancel BGWorker, and an UPDATE button which will update the values from PLC to a textbox.), But now tcClient_OnNotification is not getting called! Please point out where i am missing, any help will be most appreciated.
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;
}
}
}
thanks in advance.
Abhilash.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
检查
TcAdsClient.Synchronize
TwinCAT ADS.NET 帮助 说:
Check
TcAdsClient.Synchronize
TwinCAT ADS.NET Help says:
您绝对应该检查以下操作是否适合您:
在初始化 TcAdsClient 实例后立即执行此操作。将 Synchronize 设置为 true 在严重依赖主线程的基于 GUI 的应用程序中最有意义。
在我当前的项目中,我围绕 TcAdsClient 创建了一个包装器类,以便能够在启动和停止 TwinCat 环境的 Windows 服务中使用它,但包装器设备类在单独的线程上托管 AdsClient(在无限的 Run() 循环中) )。
对于 TwinCat 变量的更改通知,我的包装类提供了一个自己的事件,Windows 服务连接到该事件;每当底层 TwinCat 客户端的 AdsNotificationExEventHandler 在设备包装类内被触发时,就会触发它。当我在 WindowsForms 应用程序中测试此设置时,一切正常。但不在控制台应用程序中,也不在我的 Windows 服务中 - AdsNotificationExEventHandler 从未触发。关键是 TcAdsClient 的线程同步功能 - 默认设置是尝试将所有通知同步到主线程,这对于我的设置来说不是正确的选择。看来你也是如此。
You should definitely check if the following works for you:
Do this right after initializing your TcAdsClient instance. Setting Synchronize to true makes most sense in GUI based applications that heavily rely on a main thread.
In my current project I created a wrapper class around TcAdsClient to be able to use it inside a Windows service that starts and stops the TwinCat environment, but the wrapper device class is hosting the AdsClient on a separate thread (in an endless Run() loop).
For change notification in terms of TwinCat variables my wrapper class offers an own event which the Windows service is hooked up to; it is triggered whenever the AdsNotificationExEventHandler of the underlying TwinCat client gets fired inside the device wrapper class. When I tested this setup in a WindowsForms application, everything works fine. But not in a Console application and also not within my Windows service - the AdsNotificationExEventHandler never fired. The key is the thread synchronozation feature of the TcAdsClient - the default setting is that is tries to synchronize all notifications onto the Main thread, which was not the right choice for my setup. It seems that the same is true for you, as well.
我猜测
TcAdsClient
本身正在线程上收到通知,请尝试使用标准消息循环在创建它的线程上调用您的事件。问题是您在 ThreadPool 线程上创建了它,并且您没有在那里池化任何消息,因此您的方法永远不会被调用。
您的
BackgroundWorker
似乎完全没用,因为它不做任何工作。只需将其删除即可。I'll guess that
TcAdsClient
is himself being notified on a thread, try to invoke your event on the Thread that created it using the standard message loop.The problem is that you created it on a
ThreadPool
thread and you don't pool any message there so your method is never called.Your
BackgroundWorker
seem completely useless as anyway it doesn't do any work. Just remove it .