Skip to content

A .Net implementation for the Twincat ADS protocol

License

Notifications You must be signed in to change notification settings

VisconFactoryIntelligence/AdsClient

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

license .NET NuGet

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

Credits, sources and inspiration