Automatic dynamic marshalling of values

The 'Dynamic Symbol Loader' of the .NET ADS Communication API makes use of the .NET dynamic language runtime (DLR).

The dynamic language runtime is a runtime environment that adds a set of services for dynamic languages to the common language runtime (CLR). The DLR makes it easier to develop dynamic languages to run on the .NET Framework and to add dynamic features to statically typed languages.

Dynamic languages can identify the type of an object at run time, whereas in statically typed languages (without using the 'dynamic' keyword, specify object types at design time.

The advantage here is - from the moment on the symbolic (and dataType) information is available from the ADS Device - Symbol/Variable values can be marshalled 'on-the-fly' during runtime in a type-safe manner.

This works not only with primitive types but also with complex types. This reduces the complexity of the written application code to access the values, because neither the type of the data must be known, nor how the value data must be marshalled from/to the process image.

The price to be paid is simply that the full symbolic information and data types must be downloaded from the ADS Device by the symbol loader.

Example

Automatic marshalling values with 'Dynamic Values'

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

    client.Connect(AmsNetId.Local, 851);

    // Load all Symbols + DataTypes
    // Primitive Parts will be automatically resolved to .NET Primitive types.
    IDynamicSymbolLoader loader = (IDynamicSymbolLoader)SymbolLoaderFactory.Create(client, SymbolLoaderSettings.DefaultDynamic);

    dynamic symbols = loader.SymbolsDynamic;
    dynamic main = symbols.Main;

    // Use typed object to use InfoTips
    DynamicSymbol nCounter = main.nCounter; // UDINT

    // or to be fullDynamic 
    //dynamic nCounter = main.nCounter;

    // Works for ALL sorts of types (not restricted to ANY_TYPE basing primitive types)
    valueToRead = (uint)nCounter.ReadValue();
    // or
    var varValue = nCounter.ReadValue();
    // or
    dynamic dynValue = nCounter.ReadValue();

    // Same for writing
    nCounter.WriteValue(valueToWrite);

    // Or Notifications / Events
    nCounter.ValueChanged += NCounter_ValueChanged;

    //Reading complexTypes e.g. Struct

    DynamicSymbol myStructSymbol = main.plcStruct; // Dynamically created
    dynamic myStructVal = myStructSymbol.ReadValue(); // Takes an ADS Snapshot of the value

    dynamic int1Val = myStructVal.int1; // Value to an INT (short)
    dynamic valueNestedStruct = myStructVal.nestedStruct; //value to another complex type (here a nested Struct)

    myStructSymbol.ValueChanged += MyStructSymbol_ValueChanged;
}

Automatic marshalling values with 'Dynamic Values'

private void NCounter_ValueChanged(object sender, ValueChangedArgs e)
{
    var uintVal = e.Value;
}

private void MyStructSymbol_ValueChanged(object sender, ValueChangedArgs e)
{
    dynamic structValue = e.Value; // Snapshot of the whole Struct and all its contents
}

Calling 'ReadValue' or the 'ValueChanged' notification takes a full snapshot (with snapshot time) of the value. That means, when for example subelements of a struct value will be accessed, all subvalues will represent the value of that snapshot time consistently. Starting point is always the 'DynamicSymbol' object that called 'ReadValue'.

An update of the value can be done directly on the value with 'UpdateValue', or with reading a new Value on the 'DynamicSymbol' ('ReadValue').

The 'ValueChanged' event on the 'DynamicSymbol' assigns a Notification for just this symbol. The 'ValueChanged' Handler will contain the value completely marshalled as dynamic object.