Чтение свойств из функциональных блоков TwinCAT с помощью c#

#c# #ads #twincat #twincat-ads-.net

#c# #объявления #twincat #twincat-ads-.net

Вопрос:

Мы используем приложение на C # для чтения переменных из ПЛК Beckhoff через TwinCAT ADS v.3. Если мы попытаемся использовать тот же код для чтения свойств, код завершится ошибкой с исключением.

     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);
  

При попытке создать дескриптор переменной, используя приведенный выше код, мы получаем TwinCAT.Ads.AdsErrorException: 'Ads-Error 0x710 : Symbol could not be found.' .

Поскольку мы знали, что METHOD это должно быть помечено {attribute 'TcRpcEnable'} символом so, его можно вызвать с помощью этого кода:

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

Мы также попытались использовать этот атрибут {attribute 'TcRpcEnable'} для свойства. Используя TcAdsClient.CreateSymbolLoader и перебирая все доступные символы, мы обнаружили, что получатель / установщик свойства затем были помечены как rpc-методы.

 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;
  

Но, как бы мы ни старались, мы не можем вызвать метод rpc:

 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'
  

Есть предложения о том, как мы можем читать / записывать переменные, определенные как свойства в функциональных блоках?

Ответ №1:

Когда вы определяете новое свойство, вы автоматически создаете get и set для этого свойства.

Обычно свойства используются для чтения или записи переменных, которые находятся в разделе VAR функционального блока.

Все переменные, находящиеся в разделе VAR, являются частными, поэтому для доступа к этим переменным из-за пределов функционального блока требуются свойства.

Теоретически свойства не должны выполнять никаких сложных вычислений или запускать какую-либо логику, в отличие от методов.

Я хочу подчеркнуть, что вам не нужно и не следует вызывать свойства через ОБЪЯВЛЕНИЯ. В любом случае у вас есть доступ ко всем частным переменным через ADS, поэтому в первую очередь нет необходимости вызывать свойства через ADS.

@Редактировать

Я по-прежнему придерживаюсь мнения, что свойства не должны содержать никакой логики, и поэтому нет необходимости вызывать их через ОБЪЯВЛЕНИЯ.

Тем не менее, всегда есть исключения.

Имейте в виду, что в соответствии с документацией Бекхоффа будут работать только простые типы данных и указатели, а не структуры. Кроме того, «Мониторинг функций невозможен в системе compact runtime».

Вот мой рабочий пример после экспериментов с атрибутом {attribute ‘monitoring’ := ‘call’}

В Twincat:

 {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();
        }
    }
  

Комментарии:

1. Мы надеялись упростить разработку нашего функционального блока с помощью интерфейсов. Таким образом, мы могли бы определять «переменные» в интерфейсе, используя свойства, которые необходимо будет реализовать в функциональном блоке. Но поскольку наше приложение на c # использует ADS для прямого доступа к внутренним переменным, и мы не можем использовать одно и то же имя для свойства и переменной, нам придется вернуться к конвейерам именования.

2. вы все равно можете реализовать методы получения и установки в качестве методов и вызывать их с помощью удаленного вызова процедуры, если хотите упростить разработку с помощью интерфейсов.

Ответ №2:

Согласно Стефану Хеннекену в его блоге, свойство должно быть украшено прагмой, чтобы включить это:

 {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);
}
  

Комментарии:

1. Кстати, какую библиотеку ADS вы используете в этом примере? У AdsClient нет метода writeValue или readValue . Я протестировал его с помощью хорошо известного метода чтения / записи, и он не работает

2. Привет @FilippoBoido, кажется, вы правы. У меня не было возможности протестировать этот код, и я просто повторил то, что было прокомментировано в связанном блоге. У меня тоже такие же проблемы. Я повторно приму ваш ответ и посмотрю, смогу ли я продолжить это.