SymbolLoaderFactory.Create Method

Creates the specified connection.

Namespace:  TwinCAT.Ads.TypeSystem
Assembly:  TwinCAT.Ads (in TwinCAT.Ads.dll) Version: 6.0.116+a71ced3

Syntax

C#

public static ISymbolLoader Create(
    IConnection connection,
    ISymbolLoaderSettings settings
)

Parameters

connection

Type: TwinCAT.IConnection
The connection.

settings

Type: TwinCAT.ISymbolLoaderSettings
The settings.

Return Value

Type: ISymbolLoader
ISymbolLoader.

Examples

The following sample shows how to create a dynamic version of the SymbolLoader V2. The dynamic symbol loader makes use of the Dynamic Language Runtime (DLR) of the .NET Framework. That means Structures, Arrays and Enumeration types and instances are generated 'on-the-fly' during symbol Browsing. These created dynamic objects are a one to one representation of the Symbol Server target objects (e.g the IEC61131 types on the PLC). Dynamic language features are only available from .NET4 upwards.

Dynamic Tree Mode

namespace Sample
{
    using System;
    using System.Diagnostics;
    using System.Threading;
    using TwinCAT;
    using TwinCAT.Ads;
    using TwinCAT.Ads.TypeSystem;
    using TwinCAT.Ads.ValueAccess;
    using TwinCAT.TypeSystem;
    using TwinCAT.TypeSystem.Generic;
    using TwinCAT.ValueAccess;

    class SymbolBrowserProgramV2DynamicTree
    {

    #region CODE_SAMPLE_SIMPLEDYNAMIC
    /// <summary>
    /// Defines the entry point of the application.
    /// </summary>
    /// <param name="args">The arguments.</param>
    static async void Main(string[] args)
    {
        // Get the AdsAddress from command-line arguments
        AmsAddress address = ArgParser.Parse(args);

        CancellationTokenSource cancelSource = new CancellationTokenSource();
        CancellationToken cancel = cancelSource.Token;

        using (AdsClient client = new AdsClient())
        {
        // Connect to the target device
        client.Connect(address);

        // Usage of "dynamic" Type and Symbols (>= .NET4 only)
        SymbolLoaderSettings settings = new SymbolLoaderSettings(SymbolsLoadMode.DynamicTree);
        IAdsSymbolLoader dynLoader = (IAdsSymbolLoader)SymbolLoaderFactory.Create(client, settings);

        #endregion

        // Set the Default setting for Notifications
        dynLoader.DefaultNotificationSettings = new NotificationSettings(AdsTransMode.OnChange, 200, 2000);

        // Get the Symbols (Dynamic Symbols)
        var resultSymbols = await ((IDynamicSymbolLoader)dynLoader).GetDynamicSymbolsAsync(cancel);

        dynamic dynamicSymbols = resultSymbols.Symbols;
        dynamic adsPort = dynamicSymbols.TwinCAT_SystemInfoVarList._AppInfo.AdsPort;

        #region CODE_SAMPLE_SIMPLEDYNAMIC

        // Access Main Symbol with Dynamic Language Runtime support (DLR)
        // Dynamically created property "Main"
        //dynamic symMain = dynamicSymbols.Main;

        // Main is an 'VirtualSymbol' / Organizational unit that doesn't have a value
        // Calling ReadValue is not allowed
        //bool test = symMain.HasValue;
        //dynamic invalid = symMain.ReadValue();

        //Reading TaskInfo Value
        //With calling ReadValueAsync() a 'snapshot' of the Symbols Instance is taken (reading async)
        ResultReadValueAccess resultRead = await dynamicSymbols.TwinCAT_SystemInfoVarList._TaskInfo.ReadValueAsync(cancel);
        dynamic vTaskInfoArray = resultRead.Value;

        // Getting the Snapshot time in UTC format
        DateTimeOffset timeStamp1 = vTaskInfoArray.TimeStamp;

        // Getting TaskInfo Symbol for Task 1
        dynamic symTaskInfo1 = dynamicSymbols.TwinCAT_SystemInfoVarList._TaskInfo[1];

        // Getting CycleCount Symbol
        dynamic symCycleCount = symTaskInfo1.CycleCount;


        // Take Snapshot value of the ApplicationInfo struct
        resultRead = await dynamicSymbols.TwinCAT_SystemInfoVarList._AppInfo.ReadValueAsync(cancel);
        dynamic vAppInfo = resultRead.Value;

        // Get the UTC Timestamp of the snapshot
        DateTimeOffset timeStamp2 = vAppInfo.TimeStamp;

        // Access the ProjectName of the ApplicationInfo Snapshot (type-safe!)
        string projectNameValue = vAppInfo.ProjectName;

        // Reading the CycleCount Value
        resultRead = await symTaskInfo1.CycleCount.ReadValueAsync(cancel);     // Taking a Value Snapshot
        int cycleCountValue = (int)resultRead.Value;
        #endregion

        // Registering for dynamic "ValueChanged" events for the Values
        // Using Default Notification settings           
        symCycleCount.ValueChanged += new EventHandler<ValueChangedEventArgs>(cycleCount_ValueChanged);

        // Override default notification settings
        symTaskInfo1.NotificationSettings = new NotificationSettings(AdsTransMode.Cyclic, 500, 0);

        // Register for ValueChanged event.
        symTaskInfo1.ValueChanged += new EventHandler<ValueChangedEventArgs>(taskInfo1Value_ValueChanged); // Struct Type

        Thread.Sleep(10000); // Sleep main thread for 10 Seconds
        }
        Console.WriteLine("CycleCount Changed events received: {0}",_cycleCountEvents);
        Console.WriteLine("taskInfo1 Changed events received: {0}", _taskInfo1Events);

        Console.WriteLine("");
        Console.WriteLine("Press [Enter] for leave:");
        Console.ReadLine();
    }

    static object _notificationSynchronizer = new object();
    static int _cycleCountEvents = 0;

    /// <summary>
    /// Handler function for the CycleCount ValueChanged event.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The e.</param>
    static void cycleCount_ValueChanged(object sender, ValueChangedEventArgs e)
    {
        lock(_notificationSynchronizer)
        {
        Interlocked.Increment(ref _cycleCountEvents);
        // val is a type safe value of int!
        dynamic val = e.Value;
        uint intVal = val;

        DateTimeOffset changedTime = e.DateTime.ToLocalTime(); // Convert UTC to local time
        Console.WriteLine("CycleCount changed to: {0}, TimeStamp: {1}", intVal, changedTime.ToString("HH:mm:ss:fff"));
        }
    }

    static int _taskInfo1Events = 0;

    /// <summary>
    /// Handler function for the TaskInfo ValueChanged event.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The e.</param>
    static void taskInfo1Value_ValueChanged(object sender, ValueChangedEventArgs e)
    {
        lock (_notificationSynchronizer)
        {
        Interlocked.Increment(ref _taskInfo1Events);
        dynamic val = e.Value;
        DateTimeOffset changedTime = e.DateTime.ToLocalTime(); // Convert to local time

        // Val is a during Runtime created struct type and contains
        // the same Properties as related PLC object.
        int cycleTime = val.CycleTime;
        Console.WriteLine("TaskInfo1Value changed TimeStamp: {0}", changedTime.ToString("HH:mm:ss:fff"));
        }
    }
    }
}

The following sample shows how to create a static (non dynamic) version of the SymbolLoader V2. The static symbol loader in version 2 is a nearly code compatible version of the Dynamic Loader, only the dynamic creation of objects is not available. The reason for supporting this mode is that .NET Framework Versions lower than Version 4.0 (CLR2) doesn't support the Dynamic Language Runtime (DLR). The SymbolLoader V2 static object is supported from .NET 2.0 on.

Virtual Tree Mode

using System;
using System.Threading;
using System.Diagnostics;
using TwinCAT;
using TwinCAT.Ads;
using TwinCAT.TypeSystem;
using TwinCAT.TypeSystem.Generic;
using TwinCAT.Ads.ValueAccess;
using TwinCAT.Ads.TypeSystem;

namespace Sample
{
    class SymbolBrowserProgramV2VirtualTree
    {
    /// <summary>
    /// Defines the entry point of the application.
    /// </summary>
    /// <param name="args">The arguments.</param>
    static void Main(string[] args)
    {
        ConsoleLogger logger = new ConsoleLogger();

        Console.WriteLine("");
        Console.WriteLine("Press [Enter] for start:");
        Console.ReadLine();

        //logger.Active = false;

        Stopwatch stopper = new Stopwatch();

        // Parse the command-line arguments
        AmsAddress address = ArgParser.Parse(args);

        stopper.Start();

        using (AdsClient client = new AdsClient())
        {
        //client.Synchronize = false;

        // Connect the AdsClient to the device target.
        client.Connect(address);

        // Creates the Symbol Objects as hierarchical tree        
        SymbolLoaderSettings settings = new SymbolLoaderSettings(SymbolsLoadMode.VirtualTree, ValueAccessMode.IndexGroupOffsetPreferred);
        ISymbolLoader symbolLoader = SymbolLoaderFactory.Create(client, settings);

        // Dump Datatypes from Target Device
        Console.WriteLine(string.Format("Dumping '{0}' DataTypes:", symbolLoader.DataTypes.Count));
        foreach (IDataType type in symbolLoader.DataTypes)
        {
            logger.DumpType(type);
        }
        Console.WriteLine("");

        // Dump Symbols from target device
        Console.WriteLine("Dumping '{0}' Symbols:", symbolLoader.Symbols.Count);
        foreach (ISymbol symbol in symbolLoader.Symbols)
        {
            logger.DumpSymbol(symbol,0);
        }
        }
        stopper.Stop();
        TimeSpan elapsed = stopper.Elapsed;

        Console.WriteLine("");
        Console.WriteLine("Browsing complete tree: {0},({1} DataTypes, {2} Symbols)",elapsed,logger.DataTypesCount,logger.DataTypesCount);
        Console.WriteLine("Press [Enter] for leave:");
        Console.ReadLine();
    }

Examples

The SymbolLoader V2 static object is supported from .NET 2.0 on.

Flat Mode

using System;
using System.Diagnostics;
using System.Threading;
using TwinCAT;
using TwinCAT.Ads;
using TwinCAT.Ads.TypeSystem;
using TwinCAT.Ads.ValueAccess;
using TwinCAT.TypeSystem;
using TwinCAT.TypeSystem.Generic;

namespace Sample
{
    class SymbolBrowserProgramV2Flat
    {
    /// <summary>
    /// Defines the entry point of the application.
    /// </summary>
    /// <param name="args">The arguments.</param>
    static void Main(string[] args)
    {
        ConsoleLogger logger = new ConsoleLogger();

        Console.WriteLine("");
        Console.WriteLine("Press [Enter] for start:");
        Console.ReadLine();

        //logger.Active = false;

        Stopwatch stopper = new Stopwatch();

        // Parse the command line arguments
        AmsAddress address = ArgParser.Parse(args);

        stopper.Start();

        // Create the ADS Client
        using (AdsClient client = new AdsClient())
        {
        //client.Synchronize = false;

        // Connect to Address
        client.Timeout = 30000;
        client.Connect(address);

        // Creates the Symbol Objects in Flat Mode (Flat list)
        SymbolLoaderSettings settings = new SymbolLoaderSettings(SymbolsLoadMode.Flat, ValueAccessMode.IndexGroupOffsetPreferred);
        ISymbolLoader symbolLoader = SymbolLoaderFactory.Create(client, settings);

        // Dump Datatypes from Target Device
        Console.WriteLine(string.Format("Dumping '{0}' DataTypes:",symbolLoader.DataTypes.Count));
        foreach (IDataType type in symbolLoader.DataTypes)
        {
            logger.DumpType(type);
        }

        Console.WriteLine("");

        // Dump Symbols from target device
        Console.WriteLine("Dumping '{0}' Symbols:",symbolLoader.Symbols.Count);
        foreach (ISymbol symbol in symbolLoader.Symbols)
        {
            logger.DumpSymbol(symbol,0);
        }
        }
        stopper.Stop();
        TimeSpan elapsed = stopper.Elapsed;

        Console.WriteLine("");
        Console.WriteLine("Browsing complete tree: {0},({1} DataTypes, {2} Symbols)", elapsed, logger.DataTypesCount, logger.DataTypesCount);
        Console.WriteLine("Press [Enter] for leave:");
        Console.ReadLine();
    }

Examples

Argument Parser

public static class ArgParser
{
    /// <summary>
    /// Parses the arguments.
    /// </summary>
    /// <param name="args">The arguments.</param>
    /// <returns>AmsAddress.</returns>
    public static AmsAddress Parse(string[] args)
    {
    AmsNetId netId = AmsNetId.Local;
    int port = 851;

    if (args != null)
    {
        if (args.Length > 0 && args[0] != null)
        netId = AmsNetId.Parse(args[0]);

        if (args.Length > 1 && args[1] != null)
        port = int.Parse(args[1]);
    }
    return new AmsAddress(netId, port);
    }
}

Dumping Symbols

/// <summary>
/// Console logger
/// </summary>
public class ConsoleLogger
{
    public ConsoleLogger()
    {
    }
    bool _active = true;

    /// <summary>
    /// Gets or sets a value indicating whether this ConsoleLogger is active.
    /// </summary>
    /// <value><c>true</c> if active; otherwise, <c>false</c>.</value>
    public bool Active
    {
    get { return _active; }
    set
    {
        _active = value;
    }
    }

    int _dataTypes = 0;

    /// <summary>
    /// Gets the number of dumped dataTypes.
    /// </summary>
    /// <value>The data types count.</value>
    public int DataTypesCount
    {
    get { return _dataTypes; }
    }

    int _symbols = 0;

    /// <summary>
    /// Gets the number of dumped symbols
    /// </summary>
    /// <value>The symbols count.</value>
    public int SymbolsCount
    {
    get { return _symbols; }
    }


    /// <summary>
    /// Dumps the data type.
    /// </summary>
    /// <param name="dataType">Data Type.</param>
    public void DumpType(IDataType dataType)
    {
    WriteLine(string.Format("DataType: {0}, Category: {1}, Size: {2}", dataType.Name, dataType.Category, dataType.Size));

    switch (dataType.Category)
    {
        case DataTypeCategory.Alias:
        IAliasType alias = (IAliasType)dataType;
        WriteLine(GetPrefix(1) + string.Format("Alias BaseType: {0}", alias.BaseTypeName));
        break;

        case DataTypeCategory.Enum:

        //IEnumType<ushort> enumType = (IEnumType<ushort>)dataType;
        IEnumType enumType = (IEnumType)dataType;
        WriteLine(GetPrefix(1) + string.Format("Enum BaseType: {0}", enumType.BaseTypeName));

        foreach (IEnumValue enumValue in enumType.EnumValues)
        {
            WriteLine(GetPrefix(2) + string.Format("Name: {0}, Value: {1}", enumValue.Name, enumValue.Primitive));
        }
        break;
        case DataTypeCategory.Array:

        IArrayType arrayType = (IArrayType)dataType;
        int i = 0;

        foreach (IDimension dim in arrayType.Dimensions)
        {
            WriteLine(GetPrefix(2) + string.Format("{0}: LowerBound: {1}, Elements: {2}", i++, dim.LowerBound, dim.ElementCount));
        }
        break;
        case DataTypeCategory.Struct:
        IStructType structType = (IStructType)dataType;

        foreach (IMember member in structType.Members)
        {
            WriteLine(GetPrefix(2) + string.Format("Offset {0}: Name: {1}, Type: {2}", member.Offset, member.InstanceName, member.TypeName));
        }
        break;
        default:
        break;
    }

    foreach (ITypeAttribute attribute in dataType.Attributes)
    {
        WriteLine(GetPrefix(1) + string.Format("{{ {0} : {1} }}", attribute.Name, attribute.Value));
    }
    if (!string.IsNullOrEmpty(dataType.Comment))
    {
        WriteLine(GetPrefix(1) + string.Format("Comment: {0}", dataType.Comment));
    }

    IRpcCallableType rpcCallable = dataType as IRpcCallableType;

    if (rpcCallable != null)
    {
        foreach (IRpcMethod rpcMethod in rpcCallable.RpcMethods)
        {
        if (string.IsNullOrEmpty(rpcMethod.Comment))
            WriteLine(GetPrefix(1) + string.Format("Method: {0}", rpcMethod));
        else
            WriteLine(GetPrefix(1) + string.Format("Method: {0}, Comment: {1}", rpcMethod, rpcMethod.Comment));
        }
    }
    _dataTypes++;
    }

    ///// <summary>
    ///// Dumps the Datatype to Console
    ///// </summary>
    ///// <param name="dataType">DataType.</param>
    //public void DumpType(ITcAdsDataType dataType)
    //{
    //    // Dump the Attributes (PLC Metadata)
    //    foreach (ITypeAttribute attribute in dataType.Attributes)
    //    {
    //    WriteLine(GetPrefix(1) + string.Format("{{ {0} : {1} }}", attribute.Name, attribute.Value));
    //    }

    //    WriteLine(string.Format("DataType: {0}, Category: {1}, Size: {2}", dataType.Name, dataType.Category, dataType.Size));

    //    if (dataType.BaseType != null)
    //    {
    //    WriteLine(GetPrefix(1) + string.Format("BaseType: {0}", dataType.BaseType));
    //    }

    //    switch (dataType.Category)
    //    {
    //    case DataTypeCategory.Enum:
    //        foreach (IEnumValue enumValue in dataType.EnumValues)
    //        {
    //        WriteLine(GetPrefix(2) + string.Format("Name: {0}, Value: {1}", enumValue.Name, enumValue.Primitive));
    //        }
    //        break;
    //    case DataTypeCategory.Array:
    //        int i = 0;
    //        foreach (IDimension dim in dataType.Dimensions)
    //        {
    //        WriteLine(GetPrefix(2) + string.Format("{0}: LowerBound: {1}, Elements: {2}", i++, dim.LowerBound, dim.ElementCount));
    //        }
    //        break;
    //    case DataTypeCategory.Struct:
    //        foreach (ITcAdsSubItem subItem in dataType.SubItems)
    //        {
    //        WriteLine(GetPrefix(2) + string.Format("Offset {0}: Name: {1}, Type: {2}", subItem.Offset, subItem.SubItemName, subItem.Name));
    //        }
    //        break;
    //    default:
    //        break;
    //    }
    //    _dataTypes++;
    //}

    /// <summary>
    /// Dump Symbol
    /// </summary>
    /// <param name="symbol">The symbol.</param>
    /// <param name="level">Output indentation level</param>
    public void DumpSymbol(ISymbol symbol, int level)
    {
    IDataType type = symbol.DataType as IDataType;

    foreach (ITypeAttribute attribute in symbol.Attributes)
    {
        WriteLine(GetPrefix(level) + string.Format("{{ {0} : {1} }}", attribute.Name, attribute.Value));
    }

    WriteLine(GetPrefix(level) + string.Format("{0} : {1} (IG: 0x{2} IO: 0x{3} size:{4})", symbol.InstanceName, symbol.TypeName, ((IAdsSymbol)symbol).IndexGroup.ToString("x"), ((IAdsSymbol)symbol).IndexOffset.ToString("x"), symbol.Size));

    if (symbol.Category == DataTypeCategory.Array)
    {
        IArrayInstance arrInstance = (IArrayInstance)symbol;
        IArrayType arrType = (IArrayType)symbol.DataType;

        int count = 0;
        level++;

        foreach (ISymbol arrayElement in arrInstance.Elements)
        {
        DumpSymbol(arrayElement, level);
        count++;

        if (count > 20) // Write only the first 20 to limit output
            break;
        }
    }
    else if (symbol.Category == DataTypeCategory.Struct)
    {
        IStructInstance structInstance = (IStructInstance)symbol;
        IStructType structType = (IStructType)symbol.DataType;

        level++;

        foreach (ISymbol member in structInstance.MemberInstances)
        {
        DumpSymbol(member, level);
        }
    }
    _symbols++;
    }

    ///// <summary>
    ///// Dumps the specified Symbol to the Console
    ///// </summary>
    ///// <param name="symbol">The symbol.</param>
    ///// <param name="level">The level.</param>
    //public void DumpSymbol(IAdsSymbol2 symbol, int level)
    //{
    //    // Dump Attributes of the Symbol
    //    foreach (ITypeAttribute attribute in symbol.Attributes)
    //    {
    //    WriteLine(GetPrefix(level) + string.Format("{{ {0} : {1} }}", attribute.Name, attribute.Value));
    //    }

    //    ITcAdsSymbolBrowser subSymbolProvider = (ITcAdsSymbolBrowser)symbol;

    //    // Dump The Symbol
    //    WriteLine(GetPrefix(level) + string.Format("{0} : {1} ({2}, IG: 0x{3} IO: 0x{4} size:{6} subCount:{5})", symbol.Name, symbol.TypeName, symbol.DataTypeId, symbol.IndexGroup.ToString("x"), symbol.IndexOffset.ToString("x"), subSymbolProvider.SubSymbols.Count, symbol.Size));
    //    level++;

    //    // Dump all SubSymbols with indentation
    //    foreach (IAdsSymbol2 subSymbol in ((ITcAdsSymbolBrowser)symbol).SubSymbols)
    //    {
    //    DumpSymbol(subSymbol, level);
    //    }
    //    _symbols++;
    //}

    /// <summary>
    /// Dump namespace.
    /// </summary>
    /// <param name="ns">The namespace.</param>
    public void DumpNamespace(INamespace<IDataType> ns)
    {
    WriteLine("Namespace: {0}, DataTypes: {1}", ns.Name, ns.DataTypes.Count);

    foreach (IDataType type in ns.DataTypes)
    {
        DumpType(type);
    }
    }

    /// <summary>
    /// Get the indentation prefix
    /// </summary>
    /// <param name="level">The level.</param>
    /// <returns>System.String.</returns>
    public string GetPrefix(int level)
    {
    return "".PadLeft(level * 3);
    }

    /// <summary>
    /// Writes a line to the Console
    /// </summary>
    /// <param name="message">The message.</param>
    public void WriteLine(string message)
    {
    if (Active)
    {
        Console.WriteLine(message);
    }
    }

    /// <summary>
    /// Writes a line to the console
    /// </summary>
    /// <param name="format">The format.</param>
    /// <param name="args">The arguments.</param>
    public void WriteLine(string format, params object[] args)
    {
    if (Active)
    {
        Console.WriteLine(format, args);
    }
    }
}

Reference

SymbolLoaderFactory Class

TwinCAT.Ads.TypeSystem Namespace