Reactive Read/Write with Reactive Extensions
Observation of Notifications
Notifications (address specified by InstancePath) will be received cyclically as defined in Default and put into the Observer pipeline for further processing. This example takes 20 Notification samples before returning.
Observe for Notifications
// 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));
// Reactive Notification Handler
var valueObserver = Observer.Create<ushort>(val =>
{
Console.WriteLine(string.Format("Value: {0}", val.ToString()));
}
);
// Turning ADS Notifications into sequences of Value Objects (Taking 20 Values)
// and subscribe to them.
IDisposable subscription = client.WhenNotification<ushort>("TwinCAT_SystemInfoVarList._TaskInfo.CycleCount", NotificationSettings.Default).Take(20).Subscribe(valueObserver);
Console.ReadKey(); // Wait for Key press
subscription.Dispose(); // Dispose the Subscription
}
Observation of Symbolic Notifications
This example determines a symbol via Symbolloader and samples its values by Notifications with customized NotificationSettings. Again 20 samples are taken before the Observation finishes.
Observe for Symbol Notifications
// 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
}
Observation of dynamic Symbol Notifications
Here, the symbol is determined via Symbolloader again, but now the Notifications will be processed as 'dynamic' values.
Observer for dynamic Symbol Notifications
// 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 = (IDynamicSymbolLoader)SymbolLoaderFactory.Create(client, SymbolLoaderSettings.DefaultDynamic);
dynamic symbols = symbolLoader.SymbolsDynamic;
dynamic cycleCount = symbols.TwinCAT_SystemInfoVarList._TaskInfo[1].CycleCount;
// Reactive Notification Handler
var valueObserver = Observer.Create<object>(val =>
{
// Value objects can be dynamically (on the fly) created objects here (e.g. structs)
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.
// We have to give the 'hint' about IValueSymbol here, that the CLR finds the Extension Method 'WhenValueChanged' during runtime.
IDisposable subscription = ((IValueSymbol)cycleCount).WhenValueChanged().Take(20).Subscribe(valueObserver);
Console.ReadKey(); // Wait for Key press
subscription.Dispose(); // Dispose the Subscription
}
Polling observer
A polling observer doesn't use ADS notifications, but instead the value read is triggered by a time interval (polling) or a customized trigger function (on request).
Polling observer
// 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.CycleCount"];
// Reactive Notification Handler
var valueObserver = Observer.Create<object>(val =>
{
Console.WriteLine(string.Format("Instance: {0}, Value: {1}", cycleCount.InstancePath, val.ToString()));
}
);
// Take 20 Values in an Interval of 500ms
IDisposable subscription = cycleCount.PollValues(TimeSpan.FromMilliseconds(500)).Take(20).Subscribe(valueObserver);
Console.ReadKey(); // Wait for Key press
subscription.Dispose(); // Dispose the Subscription
}
Writing values with observable subject
In this example, a symbolic value is written in a static time interval (1 second). The writing stops after 10 values. The WriteValues extension method can be used to seamlessly bind value writing into a reactive application.
Writing values with observable subject
using (TcAdsClient client = new TcAdsClient())
{
// Connect to target
client.Connect(new AmsAddress(AmsNetId.Local, 851));
// Create Symbol information (Symbol 'i : INT' in PLC Global Variables list.
var symbolLoader = SymbolLoaderFactory.Create(client, SymbolLoaderSettings.Default);
IValueSymbol gvlIntSymbol = (IValueSymbol)symbolLoader.Symbols["GVL.i"];
// Produces object (short) Values 0,1,2,3 ... in seconds period
IObservable<object> timerObservable = Observable.Interval(TimeSpan.FromSeconds(1.0)).Select(i => (object)(short)i);
// Take 10 Values (0..9) and write them to GVL.i
IDisposable dispose = gvlIntSymbol.WriteValues(timerObservable.Take(10));
Console.ReadKey(); // Wait for Key press
dispose.Dispose(); // Dispose the Subscription
}