Use of ADS Notifications

If values from a PLC or NC are to be displayed continuously on a user interface, then it is very inefficient to use asynchronous read access, since this function must be called cyclically (polled triggered by a timer). Instead of using a pulling (read) model, ADS Notifications are implementing a push model. That means ADS Notifications are triggered by the sender and form a single or series of ADS messages/events. Together with these ADS Notifications, values can be transmitted. A distinction is drawn between whether the TwinCAT server is to transmit the values cyclically, or only when the values change.

In principle (raw mode) a notification is begun with the registration of the notification AddDeviceNotification. After this, events are automatically fired by TwinCAT. DeleteDeviceNotification is used to halt the notification again. Since the number of notifications is limited, you should ensure the notifications no longer required by your program are unregistered/deleted.

There exist several 'modes' for different type of ADS Notification triggers. For a complete list please consult AdsTransMode.

All the following examples demonstrate how to receive ADS Notifications. The .NET ADS API supports different information layers which different levels of ADS Notification support. All are using a PLC variable in the PLC and each time the value of the PLC variable changes, an ADS Notification message is sent and the registered callback method is invoked with event arguments that contain all the necessary information (value, time stamp, ...).

Hint: Don't use time intensive executions or ADS commands inside of your callback (not more than approx. 500). Remind to sync your callback in your main thread (typically the UI thread) if necessary, because the ADS Notifications appear on a background thread.

Using ADS Notifications with Symbolic information

C#

private void SymbolValueChanged()
{
    using (TcAdsClient client = new TcAdsClient())
    {
    // Connect to target
    client.Connect("1.2.3.4.5.6", 851);
    Symbol symbol = null;

    try
    {

        ISymbolLoader loader = SymbolLoaderFactory.Create(client, SymbolLoaderSettings.Default);
        // DINT Type (UINT32)
        symbol = (Symbol)loader.Symbols["MAIN.nCounter"];

        // Set the Notification Settings of the Symbol if NotificationSettings.Default is not appropriate
        // Check for change every 500 ms
        symbol.NotificationSettings = new NotificationSettings(AdsTransMode.OnChange, 500, 0);

        symbol.ValueChanged += Symbol_ValueChanged; // Registers the notification
        Thread.Sleep(5000); // Sleep the main thread to get some (asynchronous Notifications)
    }
    finally
    {
        // Unregister the Event and the underlying Handle
        symbol.ValueChanged -= Symbol_ValueChanged; // Unregisters the notification
    }
    }
}

private void Symbol_ValueChanged(object sender, ValueChangedArgs e)
{
    Symbol symbol = (Symbol)e.Symbol;

    // Object Value can be cast to int automatically, because it is an Primitive Value (DINT --> Int32).
    // The Symbol information is used internally to cast the value to its appropriate .NET Type.
    int iVal = (int)e.Value;
}

Using ADS Notifications in 'ANYTYPE' style

C#

//AdsStream readStream = new AdsStream(sizeof(UInt32));

private void ReceiveNotifications()
{
    using (TcAdsClient client = new TcAdsClient())
    {
    // Add the Notification event 'Ex' handler
    client.AdsNotificationEx += Client_AdsNotification;

    // Connect to target
    client.Connect(AmsNetId.Local, 851);
    int notificationHandle = 0;

    try
    {
        // Notification to a ZDINT Type (UINT32)
        // Check for change every 200 ms
        notificationHandle = client.AddDeviceNotificationEx("MAIN.nCounter",AdsTransMode.OnChange, 200, 0, null,typeof(uint));
        Thread.Sleep(5000); // Sleep the main thread to get some (asynchronous Notifications)
    }
    finally
    {
        // Unregister the Event / Handle
        client.DeleteDeviceNotification(notificationHandle);
        client.AdsNotificationEx -= Client_AdsNotification;
    }
    }
}

private void Client_AdsNotification(object sender, AdsNotificationExEventArgs e)
{
    // Or here we know about UDINT type --> can be marshalled as UINT32
    uint nCounter = (uint)e.Value;
}

Using Raw ADS Notifications

C#

AdsStream readStream = new AdsStream(sizeof(UInt32));

private void ReceiveNotifications()
{
    using (TcAdsClient client = new TcAdsClient())
    {
    // Add the Notification event handler
    client.AdsNotification += Client_AdsNotification;

    // Connect to target
    client.Connect("1.2.3.4.5.6", 851);
    int notificationHandle = 0;

    try
    {
        // Notification to a DINT Type (UINT32)
        // Check for change every 200 ms
        notificationHandle = client.AddDeviceNotification("MAIN.nCounter", readStream, AdsTransMode.OnChange, 200, 0, null);
        Thread.Sleep(5000); // Sleep the main thread to get some (asynchronous Notifications)
    }
    finally
    {
        // Unregister the Event / Handle
        client.DeleteDeviceNotification(notificationHandle);
        client.AdsNotification -= Client_AdsNotification;
    }
    }
}

private void Client_AdsNotification(object sender, AdsNotificationEventArgs e)
{
    int offset = (int)e.DataStream.Position;
    int length = (int)e.DataStream.Length;

    e.DataStream.Position = offset;
    AdsBinaryReader reader = new AdsBinaryReader(e.DataStream);

    // Read the Unmarshalled data
    //byte[] data = reader.ReadBytes(length);

    // Or here we know about UDINT type --> can be marshalled as UINT32
    uint nCounter = reader.ReadUInt32();
}

Using reactive ADS Notifications

C#

// To Test the Observer run a project on the local PLC System (Port 851)
using (TcAdsClient client = new TcAdsClient())
{
    // Connect to target
    client.Connect(new AmsAddress(AmsNetId.Local, 851));

    // Create Symbol information
    var symbolLoader = SymbolLoaderFactory.Create(client, SymbolLoaderSettings.Default);
    IValueSymbol cycleCount = (IValueSymbol)symbolLoader.Symbols["TwinCAT_SystemInfoVarList._TaskInfo[1].CycleCount"];

    // Reactive Notification Handler
    var valueObserver = Observer.Create<object>(val =>
    {
    Console.WriteLine(string.Format("Instance: {0}, Value: {1}", cycleCount.InstancePath, val.ToString()));
    }
    );

    cycleCount.NotificationSettings = new NotificationSettings(AdsTransMode.OnChange, 500, 5000); // optional: Change NotificationSettings on Symbol

    // Turning ADS Notifications into sequences of Value Objects (Taking 20 Values)
    // and subscribe to them.
    IDisposable subscription = cycleCount.WhenValueChanged().Take(20).Subscribe(valueObserver);

    Console.ReadKey(); // Wait for Key press
    subscription.Dispose(); // Dispose the Subscription
}