AdsConnection.CreateSymbolLoader Method

Creates a new instance of the Symbol loader with the specified mode.

Namespace:  TwinCAT.Ads
Assembly:  TwinCAT.Ads (in TwinCAT.Ads.dll) Version: 4.3.0.0

Syntax

C#

public IAdsSymbolLoader CreateSymbolLoader(
    ISession session,
    SymbolLoaderSettings settings
)

VB

Public Function CreateSymbolLoader ( 
    session As ISession,
    settings As SymbolLoaderSettings
) As IAdsSymbolLoader

Parameters

session

Type: TwinCAT.ISession
The session (for session orientated loades / symbols). Can be NULL if not present.

settings

Type: TwinCAT.Ads.SymbolLoaderSettings
The settings.

Return Value

Type: IAdsSymbolLoader
The IAdsSymbolLoader interface of the Symbol loader.

Exceptions

Exception

Condition

ObjectDisposedException

ObjectDisposedException

Remarks

The Symbol Loader (V2) supports the following modes. FlatThe flat mode organizes the Symbols in a flat list. At the beginning this List caches only the root symbol objects, which can be enumerated. To access the sub elements like structure fields or array elements use the SubSymbols collection. The property get accessor generates the subsymbols lazy on the fly (performance optimized) and stores them internally as weak reference (memory optimized). This mode is available in all .NET versions.VirtualTreeOn top of the behaviour of the Flat, the virtual tree mode organizes the Symbols hierarchically with parent-child relationships. That eases the access to the hierarchical structure but needs slightly more preprocessing of the data. This mode is available in all .NET Versions. DynamicTreeThe Dynamic tree mode organizes the Symbols hierarchically and (dynamically) creates struct members, array elements and enum fields on the fly. 'Dynamically' means here not only lazy creation like in Flat, but furthermore real creation of type safe .NET complex types/instances as represetantives of the TwinCAT Symbol objects/types. This feature is only available on platforms that support the Dynamic Language Runtime (DLR); actually all .NET Framework Version larger than 4.0. Virtual instances means, that all Symbols are ordered within a tree structure. For that symbol nodes that are not located on a fixed address, a Virtual Symbol will be created. Setting the virtualInstance parameter to 'false' means, that the located symbols will be returned in a flattened list.

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;

    class SymbolBrowserProgramV2DynamicTree
    {

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

        using (TcAdsClient client = new TcAdsClient())
        {
        // 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)
        dynamic dynamicSymbols = ((IDynamicSymbolLoader)dynLoader).SymbolsDynamic;

        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 ReadValue() a 'snapshot' of the Symbols Instance is taken
        dynamic vTaskInfoArray = dynamicSymbols.TwinCAT_SystemInfoVarList._TaskInfo.ReadValue(); 

        // Getting the Snapshot time in UTC format
        DateTime timeStamp1 = vTaskInfoArray.UtcTimeStamp;

        // 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
        dynamic vAppInfo = dynamicSymbols.TwinCAT_SystemInfoVarList._AppInfo.ReadValue();
        // Get the UTC Timestamp of the snapshot

        DateTime timeStamp2 = vAppInfo.UtcTimeStamp;

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

        // Reading the CycleCount Value
        uint cycleCountValue = symTaskInfo1.CycleCount.ReadValue();     // Taking a Value Snapshot
        #endregion

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

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

        // Register for ValueChanged event.
        symTaskInfo1.ValueChanged += new EventHandler<ValueChangedArgs>(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, ValueChangedArgs e)
    {
        lock(_notificationSynchronizer)
        {
        Interlocked.Increment(ref _cycleCountEvents);
        // val is a type safe value of int!
        dynamic val = e.Value;
        uint intVal = val;

        DateTime changedTime = e.UtcRtime.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, ValueChangedArgs e)
    {
        lock (_notificationSynchronizer)
        {
        Interlocked.Increment(ref _taskInfo1Events);
        dynamic val = e.Value;
        DateTime changedTime = e.UtcRtime.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 (TcAdsClient client = new TcAdsClient())
        {
        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 (TcAdsClient client = new TcAdsClient())
        {
        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(ITcAdsSymbol5 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 (ITcAdsSymbol5 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);
    }
    }
}

Examples

The following sample shows how to call (Remote Procedures / Methods) with Virtual Symbols

RPC Call in Virtual Mode

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

    using (TcAdsClient client = new TcAdsClient())
    {
        client.Synchronize = false;

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

        SymbolLoaderSettings settings = new SymbolLoaderSettings(SymbolsLoadMode.VirtualTree);
        ISymbolLoader loader = SymbolLoaderFactory.Create(client, settings);

        // Get the Symbols (Dynamic Symbols)

        IRpcStructInstance main = (IRpcStructInstance)loader.Symbols["MAIN"]; // Gets the MAIN Instance of the PLC Program

        // Call a Method that has the following signature (within MAIN Program)
        /*  {attribute 'TcRpcEnable'}
        METHOD PUBLIC M_Add : INT
        VAR_INPUT
            i1 : INT := 0;
            i2 : INT := 0;
        END_VAR 
        */

        short result = (short)main.InvokeRpcMethod("M_Add", new object[] {(short) 3, (short) 4});

        // Call a Method that has no parameter and returns VOID
        main.InvokeRpcMethod("M_Method1", new object[] {});

        //Browsing RpcMethods
        foreach(IRpcMethod method in main.RpcMethods)
        {
        string methodName = method.Name;

        foreach(IRpcMethodParameter parameter in method.Parameters)
        {
            string parameterName = parameter.Name;
            string parameterType = parameter.TypeName;
        }
        }
    }
    }
}

Examples

The following sample shows how to call (Remote Procedures / Methods) with Dynamic Symbols.

RPC Call in Dynamic 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;

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

        using (TcAdsClient client = new TcAdsClient())
        {
        client.Synchronize = false;

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

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

        // Get the Symbols (Dynamic Symbols)
        dynamic symbols = ((IDynamicSymbolLoader)dynLoader).SymbolsDynamic;
        dynamic main = symbols.Main; // Gets the MAIN Instance of the PLC Program

         // Call a Method that has the following signature (within MAIN Program)

        /*  {attribute 'TcRpcEnable'}
            METHOD PUBLIC M_Add : INT
            VAR_INPUT
            i1 : INT := 0;
            i2 : INT := 0;
            END_VAR 
        */

        short result = main.M_Add(3,4);

        // Call a Method that has no parameter and returns VOID
        main.M_Method1();

        //Browsing Rpc Methods
        foreach (IRpcMethod method in main.RpcMethods)
        {
            string methodName = method.Name;

            foreach (IRpcMethodParameter parameter in method.Parameters)
            {
            string parameterName = parameter.Name;
            string parameterType = parameter.TypeName;
            }
        }
        }
    }
    }
}

Reference

AdsConnection Class

TwinCAT.Ads Namespace

TwinCAT.Ads.TypeSystem.SymbolLoaderFactory