3

We are using a C# application to read variables from a Beckhoff PLC through TwinCAT ADS v.3. If we attempt to utilize the same code to read properties the code fails with an exception.

    FUNCTION_BLOCK FB_Sample
    VAR
       SomeVariable : INT;
    END_VAR
    PROPERTY SomeProp : INT // declared in a separate file
    // Code used to read variable (symbol)
    var handle = client.CreateVariableHandle("sampleProgram.Source.SomeVariable");
    var result = client.ReadAny(handle, typeof(int));
    client.DeleteVariableHandle(handle);
    // Adapted code used to read property (not a symbol)
    var handle = client.CreateVariableHandle("sampleProgram.Source.SomeProp"); // This fails
    var result = client.ReadAny(handle, typeof(int));
    client.DeleteVariableHandle(handle);

When trying to create a variable handle using the above code, we receive TwinCAT.Ads.AdsErrorException: 'Ads-Error 0x710 : Symbol could not be found.'.

Since we knew that METHOD must be marked with {attribute 'TcRpcEnable'} so it can be called with this code:

client.InvokeRpcMethod("{symbolPath}", "{methodName}", {parameters} });

We attempted to use that attribute {attribute 'TcRpcEnable'} on the property as well. Using TcAdsClient.CreateSymbolLoader and looping over all available symbols we discovered that the getter/setter of the property were then marked as rpc-methods.

Console.WriteLine($"Name: {rpcMethod.Name}; Parameters.Count: {rpcMethod.Parameters.Count}; ReturnType: {rpcMethod.ReturnType};");
RpcMethods: 2
Name: __setSomeProp; Parameters.Count: 1; ReturnType: ;
Name: __getSomeProp; Parameters.Count: 0; ReturnType: INT;

But try as we might, we cannot invoke the rpc method:

var propertyResult = client.InvokeRpcMethod("sampleProgram.Source", "__getSomeProp", Array.Empty<object>());
// Throws: TwinCAT.Ads.AdsErrorException: 'Ads-Error 0x710 : Symbol could not be found.'

var propertyResult = client.InvokeRpcMethod("sampleProgram.Source", "__get{SomeProp}", Array.Empty<object>());
// Throws: TwinCAT.Ads.RpcMethodNotSupportedException: 'The RPC method '__get{SomeProp}' is not supported on symbol 'sampleProgram.Source!'

var propertyResult = client.InvokeRpcMethod("sampleProgram.Source", "get{SomeProp}", Array.Empty<object>());
// Throws: TwinCAT.Ads.RpcMethodNotSupportedException: 'The RPC method 'get{SomeProp}' is not supported on symbol 'sampleProgram.Source!'

var propertyResult = client.InvokeRpcMethod("sampleProgram.Source.SomeProp", "get", Array.Empty<object>());
// Throws: System.ArgumentNullException: 'Value cannot be null.
//         Parameter name: symbol'

Any suggestion on how we can read/write variables defined as properties on function blocks?

froeschli
  • 2,692
  • 2
  • 28
  • 55

2 Answers2

2

When you define a new property you automatically create a get and a set for that property.

You normally use properties to read or write variables that are in the VAR section of the function block.

All variables that are in the VAR section are private thus the need of properties to access those VARs from outside the Function Block.

Properties in theory should not do any complex calculations or run any logic unlike Methods.

The point I want to make is that you do not need and should not call properties via ADS. You have access to all private VARs via ADS anyway so there is no need to call properties through ADS in the first place.

@Edit

I'm still of the opinion that properties should not contain any logic and therefor there is no need to call them via ADS.

Nevertheless there are always exceptions.

Be aware that according to the Beckhoff documentation, only simple data types and pointers will work, not structures. Moreover "Function monitoring is not possible in the compact runtime system".

Here my working example after experimenting with the {attribute 'monitoring' := 'call'} attribute

In Twincat:

{attribute 'monitoring' := 'call'}
PROPERTY RemoteCall : INT

GET:
RemoteCall := buffer;
SET:
buffer := buffer + RemoteCall;

In C#

    class Program
    {
        static TcAdsClient tcClient;
        static void Main(string[] args)
        {
            tcClient = new TcAdsClient();
            tcClient.Connect(851);
            
            AdsStream dataStream = new AdsStream(2);
            int iHandle = tcClient.CreateVariableHandle("MAIN.fbTest.RemoteCall");
            tcClient.Read(iHandle, dataStream);
            Console.WriteLine("Remote Var before property call: " + BitConverter.ToInt16(dataStream.ToArray(), 0));
            tcClient.WriteAny(iHandle,Convert.ToInt16(2));
            tcClient.Read(iHandle, dataStream);
            Console.WriteLine("Remote Var after property call: " + BitConverter.ToInt16(dataStream.ToArray(), 0));
            Console.WriteLine();
            Console.ReadLine();
        }
    }
Filippo Boido
  • 1,136
  • 7
  • 11
  • 1
    We were hoping to streamline our function block development using interfaces. That way, we could define "variables" on an interface using properties, which will need to be implemented on the function block. But since our c# application uses ADS to access the internal variables directly and we cannot use the same name for a property and a variable, we will have to revert to naming convetions. – froeschli Sep 29 '20 at 06:11
  • 1
    you can still implement getters and setters as Methods and call those via remote procedure call if you want to streamline your development using interfaces. – Filippo Boido Sep 29 '20 at 15:22
0

According to Stefan Hennecken on his Blog the property has to be decorated with a pragma to enable this:

{attribute ‘monitoring’ := ‘call’}
PROPERTY PUBLIC nProp : BYTE

Then it can be read/written with this code sample:

using (AdsClient client = new AdsClient())
{
    byte valuePlc;
    client.Connect(AmsNetId.Local, 851);
    valuePlc = (byte)client.ReadValue(“MAIN.fbFoo.nProp”, typeof(byte));
    client.WriteValue(“MAIN.fbFoo.nProp”, ++valuePlc);
}
froeschli
  • 2,692
  • 2
  • 28
  • 55
  • By the way what kind of ADS lib are you using in this example? AdsClient does not have a Method WriteValue or a ReadValue. I tested it with the well known Read/Write Method and it doesm't work – Filippo Boido Oct 19 '20 at 11:33
  • Hi @FilippoBoido, it seems you are correct. I did not have a chance to test this code and simply repeated, what was commented on the linked blog. I too have the same issues. I will re-accept your answer and see, if I can get a follow up on this. – froeschli Oct 20 '20 at 14:18