4

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.

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
abhilash
  • 245
  • 4
  • 10

3 Answers3

7

Check TcAdsClient.Synchronize

TwinCAT ADS.NET Help says:

If Synchronize is set to true, the notifications are synchronized onto the Main thread. This is necessary for Windows Forms projects. In Console Application this property should be set to false.

PhilMY
  • 2,621
  • 21
  • 29
  • too bad the op never marked an answer, because this definitely *is* the answer as I found out today – stijn Aug 16 '14 at 13:46
5

You should definitely check if the following works for you:

myTcAdsClient.Synchronize = false 

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.

Schulle0815
  • 61
  • 1
  • 4
0

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 .

Julien Roncaglia
  • 17,397
  • 4
  • 57
  • 75
  • Actually BackgroundWorker will do all the database job, which i was not included here. So i can't remove that. I didn't understand your second point, could you please elaborate a little – abhilash Nov 04 '11 at 09:53