2

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?

4

2 回答 2

1

根据 Stefan Hennecken 在他的博客上的说法,该属性必须用 pragma 进行装饰以启用此功能:

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

然后可以使用此代码示例读取/写入它:

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);
}
于 2020-10-19T08:02:00.960 回答
1

当您定义一个新属性时,您会自动为该属性创建一个get和一个set 。

您通常使用属性来读取或写入功能块的 VAR 部分中的变量。

VAR 部分中的所有变量都是私有的,因此需要属性才能从功能块外部访问这些 VAR。

与方法不同,理论上的属性不应进行任何复杂的计算或运行任何逻辑。

我想说的是,您不需要也不应该通过 ADS 调用属性。无论如何,您都可以通过 ADS 访问所有私有 VAR,因此首先无需通过 ADS 调用属性。

@编辑

我仍然认为属性不应包含任何逻辑,因此无需通过 ADS 调用它们。

然而,总是有例外。

请注意,根据Beckhoff 文档,只有简单的数据类型和指针可以工作,结构不能工作。此外,“在紧凑的运行系统中无法进行功能监控”

这是我尝试 {attribute 'monitoring' := 'call'} 属性后的工作示例

在双猫中:

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

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

在 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();
        }
    }
于 2020-09-25T21:56:30.500 回答