Access Data via Symbol Loader

Some ADS Devices (e.g. the TwinCAT PLC) provide symbolic information for download. That means all visible Symbols and DataTypes can be retrieved from the target system. While this need an extra effort to upload and hold the data, this feature helps to remove the dependency of the code/configuration running on the target device.

E.g. because the symbolic information can now be browsed and determined during runtime, the application can be written without knowing what's running on the target system. Even more having the Symbol information cached, the access of the process image data will be easier because the datasize and access (instance path) is stored in the symbol.

Dependent how it is parametrized, the symbol loader can work with 'ANY_TYPES' (marshallable Primitive types, Value marshalling with ANYTYPE concept) or full dynamic symbols (Automatic dynamic marshalling of values).

Example

Accessing symbolic data by preloaded Symbolic information (asynchronous)

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

Accessing symbolic data by preloaded Symbolic information (synchronous)

using (AdsClient client = new AdsClient())
{
    uint valueToRead = 0;
    uint valueToWrite = 42;

    client.Connect(AmsNetId.Local, 851);

    // Load all Symbols + DataTypes
    ISymbolLoader loader = SymbolLoaderFactory.Create(client, SymbolLoaderSettings.Default);
    Symbol symbol = (Symbol)loader.Symbols["MAIN.nCounter"];

    // Works for ALL Primitive 'ANY TYPES' Symbols
    symbol.WriteValue(valueToWrite); 
    valueToRead = (uint)symbol.ReadValue();

    // 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: loader.Symbols, recurse: true, selector: filter);

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