Tcopen: Соглашения

Созданный на 30 окт. 2020  ·  31Комментарии  ·  Источник: TcOpenGroup/TcOpen

Конвенционный документ здесь

Пожалуйста, внесите свой вклад в обсуждение ниже

  • Давайте сохраним обсуждения здесь для отслеживания.
  • Быстрые чаты здесь:TcOpen Slack

  • [ ] Соглашения об именах переменных (VAR, VAR_INPUT, VAR_OUTPUT, VAR_IN_OUT, VAR_INST, TEMP)

  • [ ] Соглашение об именах методов
  • [ ] Соглашение об именах свойств
  • [ ] Соглашение об именах для блоков (FB, FC, PRG и т. д.)
discussion

Самый полезный комментарий

У меня есть предложение по поводу массивов. Существует формальное требование к компилятору inxton, который транслирует только массивы, основанные на 0. Причина в том, чтобы предотвратить путаницу при использовании в C#.

_array : МАССИВ [0..10] BOOL; // транс-сваи
пока
_array : МАССИВ[1..10] BOOL; // не перегружает

Любой комментарий по этому поводу?

Все 31 Комментарий

@mark-lazarides @Roald87 @philippleidig @jozefchmelar давайте продолжим обсуждение соглашений здесь... слабину для быстрого разговора; обсуждения здесь, чтобы отслеживать активность

  • На мой взгляд, свойства должны быть определены следующим образом «IsEnabled». В самом названии уже должно быть указано, какой это тип.

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

  • Наследование каждого базового класса "fbComponent" необходимо для использования Inxton или tc.prober?

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

    Именование типа


| Тип блока | Обозначение | Префикс | Пример |
| :------------- | :--------- | :------------ | :------------------------------------------------- -- |
| Имя ФБ/КЛАССА | PascalCase | Нет | Cyclinder |
| Имя типа ENUM | PascalCase | Нет | MachineState.Start |
| имя ИНТЕРФЕЙСА | ПаскальКейс | I | ICyclinder |
| ФУНКЦИЯ имя | PascalCase | Нет | Add() |
| СТРУКТУРА имя | ПаскальКейс | Нет | Data |
| СОЮЗ имя | ПаскальКейс | Нет | Control |

@philippleidig

  • На мой взгляд, свойства должны быть определены следующим образом «IsEnabled». В самом названии уже должно быть указано, какой это тип.

Полностью согласен.

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

Мы используем его, как описано, с нашими компонентами. Это полезно при управлении состоянием последовательности. В подавляющем большинстве случаев bool достаточно. Иногда было бы неплохо получить больше информации о состоянии метода... но для этого потребуется более широкое обсуждение (возможно, что-то вроде беглого синтаксиса)

  • Наследование каждого базового класса "fbComponent" необходимо для использования Inxton или tc.prober?

Нет, для этого нет особых требований ни к Inxton, ни к tc.prober. Мы используем его таким образом. ComponentBase — это абстрактный класс с некоторым публичным контрактом (ручной метод и т. д.), но он может реализовывать некоторые общие функции для компонентов. Я не большой поклонник наследования (я предпочитаю композицию), но в этом случае я хотел бы иметь какой-то вариант на будущее.

В inxton, если вы хотите собрать все компоненты в коллекцию, вы можете сделать это, когда something is copmonent для этого есть механизм.

Есть и другая причина тому. В настоящее время мы работаем над открытием исходного кода нашей базовой библиотеки, к которой предъявляются некоторые требования. Надеюсь, на следующей неделе что-нибудь придумаю. Чтобы дать вам больше деталей.

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

Именование типа

Тип блока Обозначение Префикс Пример
Имя FB/CLASS PascalCase No Cyclinder
Имя типа ENUM PascalCase Нет MachineState.Start
Имя ИНТЕРФЕЙСА PascalCase I ICyclinder
Имя ФУНКЦИИ PascalCase Нет Add()
Имя STRUCT PascalCase Нет Data
Имя UNION PascalCase Нет Control

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

В большинстве случаев я не вижу смысла в использовании префиксов. Я согласен с предложением @philippleidig .

Я бы сказал, что указатель и ссылка здесь являются исключением.

Я предложил свои условности в PR #5

Именование элементов и именование типов

Я не вижу преимущества в использовании префикса. Мне это никак не помогает.

Переменные-члены

Переменные-члены класса (FB) должны быть скрыты и начинаться с маленького имени.
~ ПаскальВАР{атрибут 'скрыть'}триггер: BOOL;{атрибут 'скрыть'}счетчик: INT;{атрибут 'скрыть'}аналогСтатус : АналогСтатус;END_VAR~

@jozefchmelar

В большинстве случаев я не вижу смысла в использовании префиксов. Я согласен с предложением @philippleidig .

Я бы сказал, что указатель и ссылка здесь являются исключением.
👍
Я предложил свои условности в PR #5

Именование элементов и именование типов

Я не вижу преимущества в использовании префикса. Мне это никак не помогает.
👍👍

Переменные-члены

Переменные-члены класса (FB) должны быть скрыты и начинаться с маленького имени.

    VAR
        {attribute 'hide'}
        trigger : BOOL;
        {attribute 'hide'}
        counter : INT;
        {attribute 'hide'}
        analogStatus : AnalogStatus;
    END_VAR
  • скрытые переменные не будут видны поверх рекламы, поэтому, если они не нужны в HMI, их можно скрыть. Но если нам нужно увидеть их в HMI, мы не должны использовать атрибут «скрыть».
  • Tc3 нечувствителен к регистру, поэтому trigger (имя переменной) и Trigger (имя свойства) будут конфликтовать. Нам нужно будет добавить к нему префикс _ , как было предложено, я думаю

Полностью согласен 👍

Также есть атрибут "conditionalshow". Но это можно использовать только в сочетании с скомпилированной библиотекой.
https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/8095402123.html &id=7685156034373049758
Поскольку я предполагаю, что мы предоставим открытую библиотеку, это имеет лишь ограниченный смысл.

_ в качестве префикса для переменных-членов необходим, как сказал @PTKu .

Обычно мне нравится придерживаться соглашений об именах и выборе имен C#.

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

@philippleidig , что вы имеете в виду под возвращаемыми значениями? В этом случае они используются в качестве проверки ошибок? Обычно возвращаемое значение зависит от метода. CalculcateArea вернет REAL `LREAL`.

Полностью согласен с предложенным названием переменной!

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

@philippleidig , что вы имеете в виду под возвращаемыми значениями? В этом случае они используются в качестве проверки ошибок? Обычно возвращаемое значение зависит от метода. CalculcateArea вернет REAL``LREAL .

@Roald87 Идея заключалась бы в том, что метод компонента, выполняющего действие, возвращал бы значение «истина», когда действие завершено (MoveToHome() при достижении датчика/положения исходного положения). Это не препятствует другим типам возврата, когда это необходимо.

Полностью согласен с предложенным названием переменной!

У меня есть предложение по поводу массивов. Существует формальное требование к компилятору inxton, который транслирует только массивы, основанные на 0. Причина в том, чтобы предотвратить путаницу при использовании в C#.

_array : МАССИВ [0..10] BOOL; // транс-сваи
пока
_array : МАССИВ[1..10] BOOL; // не перегружает

Любой комментарий по этому поводу?

То же самое для TwinCAT HMI (TE2000)

То же самое для TwinCAT HMI (TE2000)

Было бы очень удобно синхронизировать массивы с HMI!

@philippleidig || @ Roald87 , не мог бы кто-нибудь из вас PR-соглашения о массивах, тогда, пожалуйста ... Мне просто нравится видеть больше участников в репо :).

У меня есть предложение по поводу массивов. Существует формальное требование к компилятору inxton, который транслирует только массивы, основанные на 0. Причина в том, чтобы предотвратить путаницу при использовании в C#.

_array : МАССИВ [0..10] BOOL; // транс-сваи
пока
_array : МАССИВ[1..10] BOOL; // не перегружает

Любой комментарий по этому поводу?

Из-за того, как работает цикл структурированного текста, я предпочитаю сохранять массивы ПЛК с размерами 1..X. В результате код легче читать в любом месте ПЛК. Я думаю, что мы должны всегда писать привлекательный и удобный для сопровождения код на ПЛК. Если нам нужны прокладки, чтобы лучше работать со сторонними фрагментами кода, мы можем управлять ими отдельно.

// Declaration
NUMBER_OF_DRIVES : INT := 10;
drives  : ARRAY[1..NUMBER_OF_DRIVES] OF I_Drive;

// now in the code
FOR i := 1 to NUMBER_OF_DRIVES DO
   drives[i].SomethingCool();
END_FOR

// Compared to

// Declaration
drives : ARRAY[0..(NUMBER_OF_DRIVES -1) ] OF I_Drive;
// Code
FOR i := 0 to (NUMBER_OF_DRIVES -1) DO
   drives[i].SomethingCool();
END_FOR

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

Я думаю, что методы должны возвращать то, что разумно для метода. Название метода должно помочь вам понять это.

то есть

IF NOT piece.PassesValidation() THEN
 LogError('Piece does not pass validation');
END_IF

// OR

IF sequence.Finished THEN
  axis.Disable(); // No return type necessary.
  state := WaitForAxisDisabled;
END_IF

@Roald87 Идея заключалась бы в том, что метод компонента, выполняющего действие, возвращал бы значение «истина», когда действие завершено (MoveToHome() при достижении датчика/положения исходного положения). Это не препятствует другим типам возврата, когда это необходимо.

Мне действительно не нравится подход многократного вызова общедоступного метода, когда этот метод многократно выполняет функциональность, пока не вернет правильное значение. Есть единственный случай, который я считаю приемлемым (если в интерфейсе есть метод типа «Выполнить», но я полагаю, что есть и лучшие способы для этого).

Проблемы с ним;

  • Вы можете/создаете пути выполнения в классе, которые можно вызывать одновременно. то есть
atEnd :=  axis.GoToEnd();
atBeginning := axis.GoToBeginning();
  • Классы должны управлять своим состоянием внутри. Методы можно использовать для выполнения запросов и модификаций этого состояния, а свойства также можно использовать для доступа к состоянию или установки простого состояния.
  • Как назвать метод, чтобы было понятно, как и почему он используется таким образом? ось.GoToEndTrueWhenComplete()?
  • Что, если базовое состояние класса изменится таким образом, что это изменит способ выполнения этого вызова?
  • Условия гонки могут быть более легко сгенерированы. Если мы неоднократно вызываем .Enable() для чего-то, но получаем ошибку при одном сканировании, то .Enable() вполне может включить Enable в любом случае, используя подход «продолжать вызов, пока не будет выполнено». Вместо этого это должно быть .RequestEnable : BOOL, которое указывает, верны ли базовые условия в точке запроса (позволяя вызывающему коду изящно вернуться в эту точку). Если запрос может быть выполнен, вызывающий код может затем отслеживать .IsEnabled и .InError для завершения.

@philippleidig не знаком с TwinCAT HMI. Как там обрабатываются массивы, не основанные на 0?

@марк-лазаридес

Я думаю, что методы должны возвращать то, что когда-либо разумно для метода. Название метода должно помочь вам понять это.
👍

Параллелизм и условия гонки — все это разумные опасения. ИМХО Эти проблемы должны решаться максимально на уровне компонентов, но что более важно на уровне согласования при потреблении компонентов. Методы компонента должны вызываться из правильно реализованных примитивов, подобных контроллеру состояния (будь то простой CASE, IF, ELSIF или более сложный секвенсор/селектор/итератор), которые предотвратят одновременные вызовы конфликтующих методов одного и того же экземпляра компонента. .

Нечто подобное должно быть предотвращено в потребительском коде компонента.
~в конце := ось.GoToEnd();в начале := ось.Перейти к началу();~

Когда executing methods возвращает true , это позволяет чистое декларативное использование.

Я имею в виду что-то вроде этого:

~~~
ВАР
_состояние: INT;

END_VAR

CASE _state OF
0:
ЕСЛИ(ось.MoveAbsolute(Позиция: 100.0)) ТО
_состояние := 1;
END_IF;
1:
ЕСЛИ(ось.MoveRelative(Позиция: 100.0)) ТО
_состояние := 2;
END_IF;
2:
ЕСЛИ(ось.MoveAbsolute(Позиция: 300.0)) ТО
_состояние := 3;
END_IF;
3:
_состояние := 0;
END_CASE
~~~

Это может быть сокращено до

~~~
ВАР
_состояние: INT;

END_VAR

CASE _state OF
0:
Ожидание (ось.MoveAbsolute (Позиция: 100.0), 1);
1:
Ожидание(ось.MoveRelative(Позиция: 100.0),2);
2:
Ожидание (ось.MoveAbsolute (Позиция: 300.0), 3);
3:
Ждать(правда,0);

END_CASE

====================================

МЕТОД Подождите
VAR_INPUT
сделано : BOOL
следующее состояние: INT;
END_VAR
ЕСЛИ (сделано) ТО
_state := следующее состояние;

END_IF;

~~~
редактировать: я предполагаю, что компонент используется в одной задаче ПЛК

Это накладывает ограничения на потребление кода. Наши компоненты не должны быть подвержены ошибкам, если потребитель использует их в неправильном порядке. Они должны хорошо реагировать на все взаимодействия. Они не обязательно будут "работать" (т. е. вызов res := axis.MoveTo(Position:=100); до axis.Enable() не сработает), но коду-потребителю должно быть предоставлено достаточно информации во всех точках, чтобы понять, в чем проблема.

Это все еще не очень хорошо читается для меня также. Вы не можете прочитать axis.MoveAbsolute(syx) и понять, что его нужно вызывать циклически. Вы бы поняли это, только если бы знали, что наш идиоматический стиль требовал этого от вас, и в этот момент я думаю, что нам не удалось создать что-то очень полезное.

Я бы также сказал, что независимо от того, можно ли исключить ошибки, они все же более вероятны. При подходе с циклическим вызовом метода вы можете создать метод, который проверяет, готово ли состояние объекта к вызову, затем вызывает циклический метод, а затем отслеживает другое состояние объекта, чтобы убедиться, что за это время ничего не произошло. Или вы делаете запрос, который говорит вам, успешен он или нет, а затем следите за статусом завершения. Вы можете зарегистрировать обратный вызов для этого как часть запроса, если хотите, что является достойным подходом OO к этому и уменьшает количество циклических вызовов/опросов.

@mark-lazarides правильно! Я execute methods (назовем их так) не должен реализовывать циклическую логику. Я предположил, что мы обсуждали ранее, что мы гарантируем, что то, что должно выполняться циклическим образом, будет помещено либо в тело FB, либо в метод Cyclic ; это должно быть вызвано в каком-то подходящем месте в программе потребителя.

OK. Так почему же мы вызываем метод циклически? Я все еще думаю, что исходные аргументы остаются в силе. Метод должен выполнять одну работу. Либо начните что-то (и, следовательно, сообщите об успехе этого), либо получите что-то (и верните это).

OK. Так почему же мы вызываем метод циклически? Я все еще думаю, что исходные аргументы остаются в силе. Метод должен выполнять одну работу. Либо начните что-то (и, следовательно, сообщите об успехе этого), либо получите что-то (и верните это).

Никаких возражений. Марк, нам не нужно циклически вызывать методы execute, но это не должно быть проблемой, если мы это сделаем.

Я не понимаю, почему метод не может вернуть результат операции.

Я думаю, что мы должны придумать более сложные компоненты и прототипировать эти идеи (пневматический поршень недостаточно сложен для этого обсуждения). Я думаю, мы могли бы начать с привода/оси.

Согласен, Питер.

Из-за названия темы я думал, что мы стремимся к этому как к соглашению! Извиняюсь, если я неправильно понял.

На данный момент у Криса есть базовый блок осей в качестве PR - я прокомментировал его, но ему нужно больше внимания.

@mark-lazarides, не нужно извинений, Марк, мы здесь, чтобы обсудить свободно, завтра я посмотрю на PR...

@philippleidig @Roald87 @dhullett08 Обсуждение дизайна компонентов также здесь

Пара случайных предложений, взятых из документов PLCopen.

plcoopen_coding_guidelines_version_1.0.pdf

Константы
Должны быть написаны ЗАГЛАВНЫМИ БУКВАМИ, чтобы их было легко идентифицировать

Допустимая длина имени
Минимум 4 символа максимум 24 символа?

Пара случайных предложений, взятых из документов PLCopen.

plcoopen_coding_guidelines_version_1.0.pdf

Спасибо за ссылку

Константы
Должны быть написаны ЗАГЛАВНЫМИ БУКВАМИ, чтобы их было легко идентифицировать

👍

Допустимая длина имени
Минимум 4 символа максимум 24 символа?

Более длинные имена не должны быть проблемой, пока они не выражают намерения, 24 символов должно быть достаточно, однако я бы не стал ограничивать макс. персонажи. Слишком короткие имена действительно вызывают подозрение, они должны состоять более чем из 4 символов.

@Seversonic ваши замечания добавлены в документ...

обсуждение прод. здесь №11

Мне очень не нравятся ваши конвенции, но я думаю, что ваш проект довольно интересен!
лично я предпочитаю более классический способ
FB_ Функциональный блок fb
Метод M_Add()
P_Parameter Prop

Привет, @PeterZerlauth и спасибо. Немного утомительно согласовать соглашения, поскольку мы находимся на полпути между ПЛК и классической разработкой программного обеспечения.

Вот опрос с самого начала обсуждения:

TcOpen.Survey.Result.pdf

В дополнение к этому, было обсуждение здесь и в Slack Channel, также может быть что-то в репозитории @dhullett08 TcOpen.

Было общее ощущение (или, по крайней мере, я интерпретирую это так), что мы должны отказаться от префиксов, если они не предоставляют полезной информации или современная IDE предоставляет информацию, которую мы передавали с помощью префиксов в прошлом.

Я понимаю, что речь идет о личных предпочтениях, и на самом деле нет правильного или неправильного способа сделать это. Просто мы должны договориться о чем-то.

Закрытие здесь, обсуждение продолжается здесь: https://github.com/TcOpenGroup/TcOpen/discussions/11

Была ли эта страница полезной?
0 / 5 - 0 рейтинги