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
<PackageReference Include="Viscon.Communication.Ads" Version="0.2.0" />
paket add Viscon.Communication.Ads --version 0.2.0
#r "nuget: Viscon.Communication.Ads, 0.2.0"
// 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
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 | Versions 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. |
-
.NETFramework 4.6.2
- System.Text.Encoding.CodePages (>= 7.0.0)
- System.Threading.Channels (>= 7.0.0)
-
.NETStandard 2.0
- System.Text.Encoding.CodePages (>= 7.0.0)
- System.Threading.Channels (>= 7.0.0)
-
.NETStandard 2.1
- System.Text.Encoding.CodePages (>= 7.0.0)
- System.Threading.Channels (>= 7.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.