Async programming (async, await)

Since .Version 4.0, the .NET API suppprts concurrent operation in form of the compiler support of the async/await statements. This is a special code generation supported flavor of concurrency that uses so called 'futures'. A 'future' (or promise) is a type that represents an operation that will be completed in the future and is represented by the .NET type Task or Task.TRESULT. type. This ensures that the called asynchronous method is started on call and delivers its result later on, without blocking the calling thread. As consequence the calling thread is able to process other work in the meanwhile. The deep support level in the .NET framework and the underlying code generation makes asynchronous programming almost as easy as synchronous programming.

From version 5.0.0 on, the TwinCAT.Ads API also supports the async programming model.

The main advantages are:

More about asynchronous programming can be read here: Asynchronous programming

and here: The Task asynchronous programming model in C#

Example

Read/Write AnyType by IndexGroup/IndexOffset (asynchronously)

using (AdsClient client = new AdsClient())
{
    CancellationToken cancel = CancellationToken.None;

    uint valueToRead = 0;
    uint valueToWrite = 42;

    client.Connect(AmsNetId.Local, 851);
    ResultWrite resultWrite = await client.WriteAnyAsync(0x4020, 0x0, valueToWrite,cancel);
    bool succeeded = resultWrite.Succeeded;

    ResultValue<uint> resultRead = await client.ReadAnyAsync<uint>(0x4020, 0x0, cancel);

    if(resultRead.Succeeded)
    {
        valueToRead = (uint)resultRead.Value;
    }
}

Read/Write AnyType by variable handle (asynchronously)

using (AdsClient client = new AdsClient())
{
    CancellationToken cancel = CancellationToken.None;
    uint varHandle = 0;
    client.Connect(AmsNetId.Local, 851);

    uint valueToRead = 0;
    uint valueToWrite = 42;

    ResultHandle resultHandle = await client.CreateVariableHandleAsync("MAIN.nCounter", cancel);
    varHandle = resultHandle.Handle;

    if (resultHandle.Succeeded)
    {
        try
        {
            ResultWrite resultWrite = await client.WriteAnyAsync(varHandle, valueToWrite, cancel);
            ResultValue<uint> resultRead = await client.ReadAnyAsync<uint>(varHandle, cancel);

            if (resultRead.Succeeded)
                valueToRead = resultRead.Value;
        }
        finally
        {
            // Unregister VarHandle after Use
            ResultAds result = await client.DeleteVariableHandleAsync(varHandle,cancel);
        }
    }
}

Read/Write AnyType by SymbolBrowser (asynchronously)

using (AdsClient client = new AdsClient())
{
    CancellationToken cancel = CancellationToken.None;

    uint valueToRead = 0;
    uint valueToWrite = 42;

    client.Connect(AmsNetId.Local, 851);

    // Load all Symbols + DataTypes
    ISymbolLoader loader = SymbolLoaderFactory.Create(client, SymbolLoaderSettings.Default);

    ResultSymbols resultSymbols  = await loader.GetSymbolsAsync(cancel);

    if (resultSymbols.Succeeded)
    {
        Symbol symbol = (Symbol)resultSymbols.Symbols["MAIN.nCounter"];

        // Works for ALL Primitive 'ANY TYPES' Symbols
        ResultWriteAccess resultWrite = await symbol.WriteValueAsync(valueToWrite, cancel);
        ResultReadValueAccess resultRead = await symbol.ReadValueAsync(cancel);

        if (resultRead.Succeeded)
            valueToRead = (uint)resultRead.Value;

        // Simple filtering of Symbols
        Regex filterExpression = new Regex(pattern: @"^MAIN.*"); // Everything that starts with "MAIN"

        // FilterFunction that filters for the InstancePath
        Func<ISymbol, bool> filter = s => filterExpression.IsMatch(s.InstancePath);
        SymbolIterator iterator = new SymbolIterator(symbols: resultSymbols.Symbols, recurse: true, selector: filter);

        foreach (ISymbol filteredSymbol in iterator)
        {
            Console.WriteLine(filteredSymbol.InstancePath);
        }
    }
}