Viscon.Communication.Ads 0.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Viscon.Communication.Ads --version 0.1.0
NuGet\Install-Package Viscon.Communication.Ads -Version 0.1.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.1.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.1.0
#r "nuget: Viscon.Communication.Ads, 0.1.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.1.0

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

.NET

This is the client implementation of the Twincat Ads protocol from Beckhoff.
(I'm not affiliated with Beckhoff)

The implementation is in C# and can be used in .Net >= 4.5, Mono >= 3.2.8, Windows 8.1 WinRT/Phone.
Because of mono you can use it on unix/linux systems and even on Android/iPhone if you have xamarin.

This library contains sync and async methods. You can't combine them.

AdsClient is the Portable Class Library.
AdsClient.WinSock can be used in normal .Net programs and Mono/Xamarin.
AdsClient.WinRT can be use in Widows 8.1 WinRT/Phone.

Contributors

MrCircuit mycroes

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.

Here is the NuGet package: https://nuget.org/packages?q=AdsClient

Mono

You need mono >= 3.2.8

Xamarin Android: Remember to set internet permissions in the manifest. You must also configure a route for your android device.

External documentation

Specification for the ADS/AMS protocol

Index-Group/Offset specification

Examples

Simple hello machine

using (AdsClient client = new AdsClient(
        amsNetIdSource: "10.0.0.120.1.1",
        ipTarget: "10.0.0.2",
        amsNetIdTarget: "5.1.204.160.1.1"))
{
    AdsDeviceInfo deviceInfo = client.ReadDeviceInfo();
    Console.WriteLine(deviceInfo.ToString());
}

Async version:

using (AdsClient client = new AdsClient(
        amsNetIdSource: "10.0.0.120.1.1",
        ipTarget: "10.0.0.2",
        amsNetIdTarget: "5.1.204.160.1.1"))
{
    AdsDeviceInfo deviceInfo = await client.ReadDeviceInfoAsync();
    Console.WriteLine(deviceInfo.ToString());
}

Disposing AdsClient may give you first chance exceptions in the output window. This happens because I'm closing the socket while it's listening for ads packets. These exceptions are handled in the library and don't cause any problems. (If someone knows a better way, please let me know)

Read/Write a variable by name

using (AdsClient client = new AdsClient(
        amsNetIdSource: "10.0.0.120.1.1",
        ipTarget: "10.0.0.2",
        amsNetIdTarget: "5.1.204.160.1.1"))
{
    uint varHandle = client.GetSymhandleByName(".TestVar");
    client.Write<byte>(varHandle, 0);
    byte value = client.Read<byte>(varHandle);
    client.ReleaseSymhandle(varHandle);
}

Async version:

using (AdsClient client = new AdsClient(
        amsNetIdSource: "10.0.0.120.1.1",
        ipTarget: "10.0.0.2",
        amsNetIdTarget: "5.1.204.160.1.1"))
{
    uint varHandle = await client.GetSymhandleByNameAsync(".TestVar");
    await client.WriteAsync<byte>(varHandle, 0);
    byte value = await client.ReadAsync<byte>(varHandle);
    await client.ReleaseSymhandleAsync(varHandle);
}

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

Working with notifications

using (AdsClient client = new AdsClient(
        amsNetIdSource: "10.0.0.120.1.1",
        ipTarget: "10.0.0.2",
        amsNetIdTarget: "5.1.204.160.1.1"))
{
  client.OnNotification += (sender, e) => { Console.WriteLine(e.Notification.ToString()); };
  uint hVar1 = client.GetSymhandleByName(".VarTest1");
  uint hVar2 = client.GetSymhandleByName(".VarTest2");
  uint hNoti1 = client.AddNotification<byte>(hVar1, 
                                    AdsTransmissionMode.Cyclic, 2000, null);
  uint hNoti2 = client.AddNotification<byte>(hVar2, 
                                    AdsTransmissionMode.OnChange, 10, null);
  Thread.Sleep(5000);
}

Simple async example with most basic functions

Here is an async example. The non async functions work the same. (functions without async at the end) Just remove all the await/async words

using System;
using System.Threading;
using System.Threading.Tasks;
using Ads.Client;
using Ads.Client.Common;

namespace AdsTest
{
  class Program
  {
    static void Main(string[] args)
    {
      if (!RunTestAsync().Wait(10000)) 
        Console.WriteLine("Timeout!");
      else
        Console.WriteLine("done");
      Console.ReadKey();
    }

    private static async Task RunTest()
    {
      using (AdsClient client = new AdsClient(
                        amsNetIdSource:"192.168.5.6.1.1",  
                        ipTarget:"192.168.3.4",       
                        amsNetIdTarget:"192.168.3.4.1.1"))  
      {
          AdsDeviceInfo deviceInfo = await client.ReadDeviceInfoAsync();
          Console.WriteLine("Device info: " + deviceInfo.ToString());

          AdsState state = await client.ReadStateAsync();
          Console.WriteLine("State: " + state.ToString());

          client.OnNotification += (sender,e) => { 
                Console.WriteLine(e.Notification.ToString()); 
          };

          uint varHandle1 = await client.GetSymhandleByNameAsync(".VariableName1");
          Console.WriteLine("Variable1 handle: " + varHandle1.ToString());

          uint varHandle2 = await client.GetSymhandleByNameAsync(".VariableName2");
          Console.WriteLine("Variable2 handle: " + varHandle2.ToString());

          uint notification1Handle = await client.AddNotificationAsync<byte>(
                varHandle1, AdsTransmissionMode.Cyclic, 5000, null);
          uint notification2Handle = await client.AddNotificationAsync<byte>(
                varHandle2, AdsTransmissionMode.OnChange, 10, null);

          byte value = await client.ReadAsync<byte>(varHandle1);
          Console.WriteLine("Value before write: " + value.ToString());

          await client.WriteAsync<byte>(varHandle1, 1);
          Console.WriteLine("I turned something on");

          value = await client.ReadAsync<byte>(varHandle1);
          Console.WriteLine("Value after write: " + value.ToString());

          await Task.Delay(5000);  

          await client.WriteAsync<byte>(varHandle1, 0);
          Console.WriteLine("I turned something off");

          Console.WriteLine("Deleting active notifications...");
          await client.DeleteActiveNotificationsAsync();
      }
    }
  }
}

Using commands directly

AdsReadStateCommand stateCmd = new AdsReadStateCommand();
string state = stateCmd.Run(client.Ams).AdsState.ToString();
Console.WriteLine("State: " + state);

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 UInt16 Var1 { get; set; }

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

var testobject = client.Read<TestClass>(handle);
client.Write<TestClass>(handle, testobject);

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

using (AdsClient client = new AdsClient(
        amsNetIdSource: "10.0.0.120.1.1",
        ipTarget: "10.0.0.2",
        amsNetIdTarget: "5.1.204.130.1.1"))
{
  string xml = adsClient.Special.GetTargetDesc();
  xml = XDocument.Parse(xml).ToString();
}

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 247 6/15/2023
0.1.1 141 5/16/2023
0.1.0 129 5/15/2023