Viscon.Communication.Ads 0.2.0

dotnet add package Viscon.Communication.Ads --version 0.2.0
NuGet\Install-Package Viscon.Communication.Ads -Version 0.2.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Viscon.Communication.Ads" Version="0.2.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Viscon.Communication.Ads --version 0.2.0
#r "nuget: Viscon.Communication.Ads, 0.2.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Viscon.Communication.Ads as a Cake Addin
#addin nuget:?package=Viscon.Communication.Ads&version=0.2.0

// Install Viscon.Communication.Ads as a Cake Tool
#tool nuget:?package=Viscon.Communication.Ads&version=0.2.0

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

<a id='snippet-connect'></a>

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();

<sup><a href='/samples/Samples/Samples.cs#L13-L18' title='Snippet source file'>snippet source</a> | <a href='#snippet-connect' title='Start of snippet'>anchor</a></sup>

Read device info

<a id='snippet-readdeviceinfoasync'></a>

AdsDeviceInfo deviceInfo = await client.ReadDeviceInfoAsync();
Console.WriteLine(deviceInfo.ToString());

<sup><a href='/samples/Samples/Samples.cs#L20-L23' title='Snippet source file'>snippet source</a> | <a href='#snippet-readdeviceinfoasync' title='Start of snippet'>anchor</a></sup>

Read/Write a variable by name

<a id='snippet-readwritevariablebyname'></a>

var varHandle = await client.GetSymhandleByNameAsync(".TestVar");
await client.WriteAsync<byte>(varHandle, 0);
var value = await client.ReadAsync<byte>(varHandle);
await client.ReleaseSymhandleAsync(varHandle);

<sup><a href='/samples/Samples/Samples.cs#L25-L30' title='Snippet source file'>snippet source</a> | <a href='#snippet-readwritevariablebyname' title='Start of snippet'>anchor</a></sup>

You can also use the AdsCommands directly if you need to write directly with IndexGroup/IndexOffset

Working with notifications

<a id='snippet-workingwithnotifications'></a>

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);

<sup><a href='/samples/Samples/Samples.cs#L32-L38' title='Snippet source file'>snippet source</a> | <a href='#snippet-workingwithnotifications' title='Start of snippet'>anchor</a></sup>

Simple example with most basic functions

Here is a sample which shows usage of most basic functions.

<a id='snippet-program'></a>

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();
    }
}

<sup><a href='/samples/Samples/Program.cs#L1-L71' title='Snippet source file'>snippet source</a> | <a href='#snippet-program' title='Start of snippet'>anchor</a></sup>

Using commands directly

<a id='snippet-usingcommands'></a>

var stateCmd = new AdsReadStateCommand();
var state = (await stateCmd.RunAsync(client.Ams, CancellationToken.None)).AdsState.ToString();
Console.WriteLine($"State: {state}");

<sup><a href='/samples/Samples/Samples.cs#L40-L44' title='Snippet source file'>snippet source</a> | <a href='#snippet-usingcommands' title='Start of snippet'>anchor</a></sup>

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.

<a id='snippet-testclass'></a>

[AdsSerializable]
public class TestClass
{
    [Ads]
    public ushort Var1 { get; set; }

    [Ads]
    public byte Var2 { get; set; }
}

<sup><a href='/samples/Samples/Samples.cs#L58-L68' title='Snippet source file'>snippet source</a> | <a href='#snippet-testclass' title='Start of snippet'>anchor</a></sup>

<a id='snippet-readtestclass'></a>

var handle = await client.GetSymhandleByNameAsync(".Test");
var testInstance = await client.ReadAsync<TestClass>(handle);
await client.WriteAsync(handle, testInstance);

<sup><a href='/samples/Samples/Samples.cs#L46-L50' title='Snippet source file'>snippet source</a> | <a href='#snippet-readtestclass' title='Start of snippet'>anchor</a></sup>

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

<a id='snippet-gettargetdesc'></a>

var xml = await client.Special.GetTargetDescAsync();
xml = XDocument.Parse(xml).ToString();

<sup><a href='/samples/Samples/Samples.cs#L52-L55' title='Snippet source file'>snippet source</a> | <a href='#snippet-gettargetdesc' title='Start of snippet'>anchor</a></sup>

Credits, sources and inspiration

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 is compatible.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.2.0 225 6/15/2023
0.1.1 136 5/16/2023
0.1.0 127 5/15/2023