adsclient
adsclient copied to clipboard
A .Net implementation for the Twincat ADS protocol
This is the client implementation of the Twincat Ads protocol from Beckhoff.
The implementation is in C# and targets .NET Framework 4.6.2, .NET Standard 2.0 and .NET Standard 2.1.
All communication methods are async.
Contributors
Getting started
Ads Route
First you have to give your device/machine the permission to communicate with the Twincat Ads server by adding a route.
There are different ways of doing this depending on the device. You can use the Twincat Remote Manager for example. On a CX9001 device you can connect with cerhost.exe and add a route with \Hard Disk\System\TcAmsRemoteMgr.exe (You may not to reboot after this!)
If the library is not working, an incorrect/missing route may be the problem!.
Installation
You only need this library. Twincat is not needed. It will not work if you have programs like system manager or PLC control running.
The package is available from NuGet.
Examples
Connect to the PLC
using var client = new AdsClient(amsNetIdSource: "10.0.0.120.1.1", ipTarget: "10.0.0.2",
amsNetIdTarget: "10.0.0.2.1.1");
await client.Ams.ConnectAsync();
snippet source | anchor
Read device info
AdsDeviceInfo deviceInfo = await client.ReadDeviceInfoAsync();
Console.WriteLine(deviceInfo.ToString());
snippet source | anchor
Read/Write a variable by name
var varHandle = await client.GetSymhandleByNameAsync(".TestVar");
await client.WriteAsync<byte>(varHandle, 0);
var value = await client.ReadAsync<byte>(varHandle);
await client.ReleaseSymhandleAsync(varHandle);
snippet source | anchor
You can also use the AdsCommands directly if you need to write directly with IndexGroup/IndexOffset
Working with notifications
client.OnNotification += (sender, e) => { Console.WriteLine(e.Notification.ToString()); };
var varHandle1 = await client.GetSymhandleByNameAsync(".VarTest1");
var varHandle2 = await client.GetSymhandleByNameAsync(".VarTest2");
var notificationHandle1 = await client.AddNotificationAsync<byte>(varHandle1, AdsTransmissionMode.Cyclic, 2000, null);
var notificationHandle2 = await client.AddNotificationAsync<byte>(varHandle2, AdsTransmissionMode.OnChange, 10, null);
snippet source | anchor
Simple example with most basic functions
Here is a sample which shows usage of most basic functions.
using Viscon.Communication.Ads;
using Viscon.Communication.Ads.Common;
namespace Samples;
public static class Program
{
static async Task Main()
{
var timeout = Task.Delay(10000);
var task = await Task.WhenAny(RunTestAsync(), timeout);
if (task == timeout)
{
Console.Error.WriteLine("Operation timed out!");
}
else
{
Console.WriteLine("Done!");
}
}
private static async Task RunTestAsync()
{
using var client = new AdsClient(
amsNetIdSource:"192.168.5.6.1.1",
ipTarget:"192.168.3.4",
amsNetIdTarget:"192.168.3.4.1.1");
await client.Ams.ConnectAsync();
var deviceInfo = await client.ReadDeviceInfoAsync();
Console.WriteLine($"Device info: {deviceInfo}");
var state = await client.ReadStateAsync();
Console.WriteLine($"State: {state}");
client.OnNotification += (sender,e) => {
Console.WriteLine(e.Notification.ToString());
};
var varHandle1 = await client.GetSymhandleByNameAsync(".VariableName1");
Console.WriteLine($"Variable1 handle: {varHandle1}");
var varHandle2 = await client.GetSymhandleByNameAsync(".VariableName2");
Console.WriteLine($"Variable2 handle: {varHandle2}");
var notification1Handle = await client.AddNotificationAsync<byte>(
varHandle1, AdsTransmissionMode.Cyclic, 5000, null);
var notification2Handle = await client.AddNotificationAsync<byte>(
varHandle2, AdsTransmissionMode.OnChange, 10, null);
var value = await client.ReadAsync<byte>(varHandle1);
Console.WriteLine($"Value before write: {value}");
await client.WriteAsync<byte>(varHandle1, 1);
Console.WriteLine("I turned something on");
value = await client.ReadAsync<byte>(varHandle1);
Console.WriteLine($"Value after write: {value}");
await Task.Delay(5000);
await client.WriteAsync<byte>(varHandle1, 0);
Console.WriteLine("I turned something off");
Console.WriteLine("Deleting active notifications...");
await client.DeleteActiveNotificationsAsync();
}
}
snippet source | anchor
Using commands directly
var stateCmd = new AdsReadStateCommand();
var state = (await stateCmd.RunAsync(client.Ams, CancellationToken.None)).AdsState.ToString();
Console.WriteLine($"State: {state}");
snippet source | anchor
Serialize to class
It's possible to read directly to a class or write from a class.
You need to set the AdsSerializable attribute on the class and the Ads attribute on the fields/properties you need.
The fields without the Ads attribute are ignored.
[AdsSerializable]
public class TestClass
{
[Ads]
public ushort Var1 { get; set; }
[Ads]
public byte Var2 { get; set; }
}
snippet source | anchor
var handle = await client.GetSymhandleByNameAsync(".Test");
var testInstance = await client.ReadAsync<TestClass>(handle);
await client.WriteAsync(handle, testInstance);
snippet source | anchor
This is an example struct in Twincat:
TYPE TestStruct :
STRUCT
Var1 : INT;
Var2 : BYTE;
END_STRUCT
END_TYPE
Special functions
These functions aren't documented by Beckhoff:
Get target description
var xml = await client.Special.GetTargetDescAsync();
xml = XDocument.Parse(xml).ToString();
snippet source | anchor