# Twincat ADS-Notifications im Thread



## nekron (29 November 2008)

Hi ...

hat jemand schon mal mit ADS in einem separaten Thread gearbeitet ? Ich starte einen Backgroundworker (oder Thread, kommt aufs gleiche raus) und geb ihm ein paar Events (AmsRouterStateChanged, AdsStateChanged, ADSIGRP_SYM_VERSION)...

Diese Events werden aber NIE gestartet, egal was mein Ams-Router macht ... wenn ich das ganze ganz normal in einem mainform_load aufrufe funktioniert es Problemlos.

Hat das jemand schon mal gemacht ? Ach ja C#, bevor ichs vergess...

Die ganze Zeit hab ich in einem Timer gearbeitet, bringt natuerlich leichte Nachteile mit sich ...

danke
 michael


----------



## Neals (29 November 2008)

Events werden dort abgearbeitet, wo sie aufgerufen werden!

Also werden die Events immer in dem Thread gefeuert und die angehängten Methoden in dem Thread abgearbeitet, in dem der ADS Client erzeugt wurde.

Ich würd das spontan so machen, dass ich für jeden Event eine Methode habe, in der vom ThreadPool dann ein Thread genommen wird um darauf zu reagieren:


```
protected virtual void OnNotification (AdsNotificationEventArgs e)
{
    ThreadPool.QuequeUserWorkItem(NotificationCallback, e);
}

public void NotificationCallback (object state)
{
    AdsNotificationEventArgs args = (AdsNotificationEventArgs)state;
    
    // Do something...
}
```

Keine Garantie auf Richtigkeit, aber sollte schon grob passen und erklären was ich meine. ;-)


----------



## nekron (30 November 2008)

Moin moin Neals,

also irgendwie bin ich anscheinend doch zu unbewandert in dem ganzen um das zu verstehen ...

Kannst du mir eventuell mit einem konkreteren Beispiel dienen ?

danke
 michael


----------



## Neals (30 November 2008)

Moin hört sich gut an, bin auch Norddeutscher ;-)

Hab das Beispiel 8 aus dem InfoSys mal erweitert...

Hier das Beispiel: http://infosys.beckhoff.com/content/1031/tcsample_net/Samples/Sample08/TwinCATADS_Sample08.exe

Hier meine Erweiterung:


```
void AmsRouterNotificationCallback(object sender, AmsRouterNotificationEventArgs e)
        {
            // Thread aus dem ThreadPool beauftragen
            ThreadPool.QueueUserWorkItem(RouterNotificationCallback, e);
        }

        // Diese Funktion wird vom Thread ausgeführt
        private void RouterNotificationCallback(object e)
        {
            // Daten umwandeln
            AmsRouterNotificationEventArgs args = (AmsRouterNotificationEventArgs) e;

            if(args.State == AmsRouterState.Stop || args.State == AmsRouterState.Removed)
            {
                // Verbindung abbrechen und versuchen neu aufzubauen oder so...
            }

            // Aus einem Thread auf Form zugreifen
            BeginInvoke((MethodInvoker) delegate
                {
                     _routerLabelValue.Text = args.State.ToString();
                });
        }
```


----------



## nekron (1 Dezember 2008)

Moin moin Neals,

bin zwar kein Norddeutscher, verbringe aber recht viel Zeit dort oben 

Ups - da haben wir mächtig aneinander vorbeigeredet ....

Mein Problem ist ein ganz anderes ... Ich starte einen Thread, der eine Verbindung zum Twincat öffnet, dann ein paar Events um einen Verbindungsabbruch zu erkennen... Diese werden aber im Gegensatz zum Starten aus dem Haupt-Thread ( -> Applikation) nie aufgerufen ...


```
[SIZE=2]
[/SIZE][SIZE=2][COLOR=#0000ff]public[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#0000ff]void[/COLOR][/SIZE][SIZE=2] _worker()
{
[/SIZE][SIZE=2][COLOR=#008080]TcAdsClient[/COLOR][/SIZE][SIZE=2] _tcClient = [/SIZE][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#008080]TcAdsClient[/COLOR][/SIZE][SIZE=2]();
[/SIZE][SIZE=2][COLOR=#008080]AdsStream[/COLOR][/SIZE][SIZE=2] dataStream = [/SIZE][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#008080]AdsStream[/COLOR][/SIZE][SIZE=2](30);
[/SIZE][SIZE=2][COLOR=#0000ff]while[/COLOR][/SIZE][SIZE=2] ([/SIZE][SIZE=2][COLOR=#0000ff]true[/COLOR][/SIZE][SIZE=2])
{
[/SIZE][SIZE=2][COLOR=#0000ff]try
[/COLOR][/SIZE][SIZE=2]{
[/SIZE][SIZE=2][COLOR=#0000ff]if[/COLOR][/SIZE][SIZE=2] (!_tcClient.IsConnected)
{
_tcClient.Connect(801);
_tcClient.AddDeviceNotification(0xF008, 0x0, dataStream, [/SIZE][SIZE=2][COLOR=#008080]AdsTransMode[/COLOR][/SIZE][SIZE=2].OnChange, 500, 500, 0);
_tcClient.AdsNotification += [/SIZE][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#008080]AdsNotificationEventHandler[/COLOR][/SIZE][SIZE=2](_tcClient_AdsNotification);
_tcClient.AdsStateChanged += [/SIZE][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#008080]AdsStateChangedEventHandler[/COLOR][/SIZE][SIZE=2](_tcClient_AdsStateChanged);
_tcClient.AmsRouterNotification += [/SIZE][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE][SIZE=2] [/SIZE][SIZE=2][COLOR=#008080]AmsRouterNotificationEventHandler[/COLOR][/SIZE][SIZE=2](_tcClient_AmsRouterNotification);
}
System.Threading.[/SIZE][SIZE=2][COLOR=#008080]Thread[/COLOR][/SIZE][SIZE=2].Sleep(100);
}
[/SIZE][SIZE=2][COLOR=#0000ff]catch[/COLOR][/SIZE][SIZE=2] ([/SIZE][SIZE=2][COLOR=#008080]AdsErrorException[/COLOR][/SIZE][SIZE=2] ex)
{
// ...
}
}
}
[/SIZE]
```
 
Das ganze jetzt ohne Retry-Time usw ... auf jeden Fall werden die Notifications nicht aufgerufen ...

Hast Du dazu eine Idee ??

danke
 michael


----------



## Neals (1 Dezember 2008)

Wenn deine Funktion worker in einem Thread laufen würde, währe sie ja durchgehend dabei, neu zu connecten und den Events immer wieder die gleiche Funktion zuzuweisen. Das heißt die währe dann 10 - 20 mal oder so auf dem Event und wenn das Event gefeuert wird, wird die Funktion 10 mal ausgeführt.

Ich würde sagen die Funktionen in der Schleiße sind fehl am Platze...

Habs mal so gemacht:


```
public partial class Form1 : Form
    {
        private TcAdsClient tcClient;
        private AdsStream adsStream;
        private BinaryReader binRead;
        private int notificationHandle;
        Thread adsThread;


        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            
            try
            {
                adsThread = new Thread(AdsThread);
                adsThread.Start();
            }
            catch (AdsErrorException ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void AdsThread()
        {
            tcClient = new TcAdsClient();

            /* connect the client to the local PLC */
            tcClient.Connect(801);

            adsStream = new AdsStream(2);                /* stream storing the ADS state of the PLC */
            binRead = new BinaryReader(adsStream);    /* reader to read the state data */

            /* register callback to react on state changes of the local AMS router */
            tcClient.AmsRouterNotification +=
                                    AmsRouterNotificationCallback;


            notificationHandle = tcClient.AddDeviceNotification(
                                        (int)AdsReservedIndexGroups.DeviceData,    /* index group of the device state*/
                                        (int)AdsReservedIndexOffsets.DeviceDataAdsState, /*index offsset of the device state */
                                        adsStream,    /* stream to store the state */
                                        AdsTransMode.OnChange,    /* transfer mode: transmit ste on change */
                                        0,    /* transmit changes immediately */
                                        0,
                                        null);

            /* register callback to react on state changes of the local PLC */
            tcClient.AdsNotification += OnAdsNotification;

            while (tcClient.IsConnected)
            {
                Application.DoEvents();
                Thread.Sleep(50);
            }
        }

        /* callback function called on state changes of the PLC */
        void OnAdsNotification(object sender, AdsNotificationEventArgs e)
        {
            if (e.NotificationHandle == notificationHandle)
            {
                AdsState plcState = (AdsState)binRead.ReadInt16(); /* state was written to the stream */
                BeginInvoke((MethodInvoker) delegate
                                                {
                                                    _plcLabelValue.Text = plcState.ToString();
                                                });
            }
        }

        /* callback function called on state changes of the local AMS router */
        void AmsRouterNotificationCallback(object sender, AmsRouterNotificationEventArgs e)
        {
            BeginInvoke((MethodInvoker) delegate
                                            {
                                                _routerLabelValue.Text = e.State.ToString();
                                            });
        }

        private void _exitButton_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                adsThread.Abort();
                tcClient.DeleteDeviceNotification(notificationHandle);
                tcClient.Dispose();
            }
            catch (AdsErrorException ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
```


----------



## Raf (9 März 2010)

No German writing here sorry,

I had the same problem with the onnotification. The problem resides in the property TcAdsClient.Synchronize. This one should be set to false for the notification to triggered correctly.

Although the last sample program works great, it doesn't answer completely to the originally posted question.


----------



## Neals (10 März 2010)

My posted code was "my" best practice.

If you set the property Synchronize to true, the client tries to fire all events in the main-thread.

Which question is not answered yet?


----------



## cpalm (28 Januar 2011)

Nochmal hallo in die kompetente Runde,

vielleicht kann ich diesen älteren Thread mit meinem Multi-Threading Problem nochmal etwas aufwärmen...
Ich habe genau das gleiche Problem wie oben geschildert und habe alle Vorschläge durchprobiert.
Mein Hauptunterschied ist allerdings das ich ein WPF XAML Frontend  entwickle und da sieht das alles mit den  Nachrichtenbearbeitungsschleifen etwas anders aus.

Als funktionierende Alternative "polle" ich derzeit im Bulk-Verfahren.  Aber die Round-Trip Zeiten sind gähnend langsam. Gefühlte 300 ms (für  nur 7 bool Variablen - die wie gesagt alle in einem Rutsch übertragen  werden).

Wer hat hier einen Tipp für mich?


----------

