Пользуйтесь реальностью процессов
Одно из фундаментальных отличий программирования технологических процессов от классического программирования — это обычно значительно меньший уровень абстракции. Алгоритмы определяются технологией процесса, который в свою очередь часто опирается на здравый смысл и простую логику. Об этом часто забывают увлекаясь программированием ради программирования.
Не путайте причины и следствия
Например, пусть при падении давления нужно выключать насос, а при выключенном насосе какой-то клапан должен быть закрыт; предположим, никаких иных условий выключения и закрытия — нет. Ошибочно при этом (даже якобы в целях оптимизации кода) закрывать клапан при падении давления, т.к. алгоритм закрытия клапана строго привязан именно к состоянию насоса. При удлинении или изменении следственных цепочек это может привести к серьезным ошибкам в процессе или сложностям в модификации кода.
Настройка среды CODESYS 2.3
Некоторые настройки «по умолчанию» в CODESYS удивляют. Например, эта среда — единственная из виденных мной, в которой шрифт не моноширинный, т.е. символы в нем имеют разную ширину. Сразу же после установки среды заходите в Проект > Опции > Редактор > Шрифт и выбирайте Consolas (код на всех скриншотах) или хотя бы Courier New.
После этого отмените автообъявление (штука, появляющаяся когда вы вводите незнакомый Кодесису набор символов), этот адский источник всплывающих окон, которыми так любит радовать эта среда.
В настройках «Рабочего стола» уберите лишние галочки и уменьшите таймауты связи до 1000 мс — это сильно упростит вам жизнь, поверьте.
Чтобы перепроверить правильность кода — после каждого изменения жмите клавишу F11 для компиляции кода. После нажатия в нижней части экрана высветятся сообщения о текущих ошибках или их отсутствии. Чтобы скрыть лишнюю плашку с этими сообщениями — нажмите SHIFT+ESC.
Не стремитесь использовать чужой код
Вторичное использование чужого кода в классическом программировании — норма и необходимость, обусловленная множеством причин. В АСУ ТП же, по моему мнению, почти нет необходимости использовать сторонний код.
Я не считаю себя профессионалом и не писал особо сложных программ, но для тех что писал мне вполне хватало стандартных библиотек Util.lib и Standart.lib. Те же библиотеки Oscat шикарны и всеохватывающи, но в них нет ничего сверхестественного и, мне кажется, лучше самому писать аналогичные функции. А на тот код, который обычно выкладывают на форумах, лучше вообще не смотреть. Даже с тем, что я выкладываю в блоге лучше просто ознакамливаться, а не использовать. Пишите свои алгоритмы и как можно больше)
Примеры правильного кода внутри библиотек
Чтобы понять как должен выглядеть грамотный код и узнать некоторые программистские приёмчки не нужно далеко ходить. Открывайте Кодесисом библиотеки Util.lib и Oscat.lib как обычные проекты и впитывайте мудрость строк.
Пользуйтесь интегрированной справкой CODESYS
Банально, но там есть ответы на большинство вопросов начинающих. Справка небольшая поэтому лучше пробежаться по ней всей чтобы познакомится с возможностями языков и функций.
Вызов справки по «F1» работает по большинству служебных слов и элементам библиотек Util.lib и Standart.lib. Иногда в CODESYS 2.3 вызов по «F1» не срабатывает, например для SEL и ARRAY, поэтому вручную открывайте справку и ищите нужный идентификатор во вкладке «Поиск»
Не повторяйте код
Это самый распространенный косяк у неопытных программистов независимо от платформы и языка программирования. Видите дублированный код — выносите его в отдельную функцию или функциональный блок, и/или же переводите переменные в элементы массивов и прогоняйте в цикле. Предыдущее предложение вам может быть непонятно, но подробности можно загуглить.
На скриншоте код, обрабатывающий данные с частотных преобразователей Danfoss. Переменные типа CWS1_FREQ созданы в Конфигурации ПЛК в разделе ModBus (Master). Слева представлена первая версия кода где каждый ПЧ представлен отдельным куском из восьми строк; код в каждом куске различается только данными, а логика одинакова.
Справа оптимизированная версия — логика вынесена в отдельную функцию GET_PUMP (с адресацией и указателями, о которых расскажу ниже), а код для конкретного ПЧ теперь занимает одну строчку. Если бы в Конфигурации можно было задавать переменные как элементы массива (типа FREQ_CWS[1]), то код обработки любого количества ПЧ свелся к одной строке, вложенных в два цикла (см. совет ниже).
Плюсы языка ST — массивы и циклы
Язык ST я использую из-за того, что моё знакомство с программированием началось с C/C++. В некоторых случаях удобно использовать CFC или что похуже, но в целом ST более ёмок, лаконичен и чёток. Но, чем действительно он отличается от других языков так это возможностью нормального использования массивов и циклов, которые мне сильно облегчают жизнь и увеличивают читабельность кода.
Не злоупотребляйте циклами
При том, что циклы FOR бывают крайне удобны, всегда помните, что код в ПЛК выполняется в суперцикле и часто можно обойтись простым инкрементированием с обнулением (см. скриншот ниже). Также большие циклы FOR и тем более WHILE могут сильно «затормозить» суперцикл ПЛК вплоть до срабатывания сторожевого таймера (об этом в следующем совете). Иногда, конечно, нужно сделать перебор в текущем суперцикле и тут уж лучше использовать FOR.
i станет равным 25000 за 25000 циклов
j станет равным 25000 за 1 цикл, но
возможно сработает watchdog
Сторожевой таймер и сброс ПЛК
Если суперцикл выполняется слишком долго, то ПЛК производит аппаратный сброс на уровне микроконтроллера. Такое возникает при переходе в бесконечный цикл и других зависаниях. Отвечает за анализ времени выполнения цикла сторожевой таймер, или Watchdog.
Сброс по watchdog’у отличается от любого сброса через среду CODESYS. Этим можно пользоваться. В моей практике сторожевой таймер применяется в контроллерах ОВЕН. В этих ПЛК иногда происходит неприятный момент — созданный в Конфигурации модуль ModBus (Master) перестает вести опрос Слейвов (обычно это возникает после перезаливки программы). Поначалу это решали сбросом питания, но тут возникали трудности при удаленной закачке через интернет.
Позже я обнаружил, что проблему решает искусственное заведение программы в бесконечный цикл. Я вывел сброс на кнопку в визуализации. Или же можно анализировать изменение переменной «Опрашиваемый адрес» в ModBus (Master) — если Slave-устройств больше одного, то она должна меняться каждый цикл.
Расширенное использование функций
Помимо вывода одного-единственного значения любого стандартного типа функция способна возвращать значения пользовательского типа, например структуры, которые могут состояться из любого количества переменных разного типа.
Но что интереснее, имеется возможность изменения переменных, которые являются аргументами функции, внутри самой функции. Это возможно благодаря использованию указателей и адресации на эти переменные. Понять что к чему можно по скриншоту ниже, на котором приведен код функции, инициализирующий переменную если та равна нулю. Параметр внутри функции определяется как указатель на нужный тип, а при вызове функции в параметре используется не сама переменная, а её адрес (функция ADR).
Иногда SEL удобнее IF
В случае когда при каком-то условии переменная должна изменить своё значение, а в противном — иметь другое значение, применение IF может быть не лучшем решением. Если условие применимо только к этой единственной переменной — используйте функцию-селектор SEL. Она работает с переменными любого типа.
Буль не буль
Очень часто в чужом коде встречаю странное использование операций с булевыми величинами или, наоборот, игнорирование булевой логики.
Во-первых, операции типа сравнения или проверки равенства возвращают булевы значения.
Во-вторых нет необходимости явно сравнивать булеву переменную со значениями TRUE или FALSE.
Наглядная работа с булями
Мне очень часто для сокращения кода приходится использовать явные преобразования BOOL в целое число, например, в формулах. Для этого приходится использовать функцию приведения типов в её стандартном неуклюжем виде BOOL_TO_BYTE(). Но как-то раз наткнулся на блог Егора Петрова, где он придумал более простое и изящное решение — по сути переименовать эту функцию в более читаемую — WHEN(). Теперь пользуюсь, пользуйтесь и вы.
Источник: https://www.asutp-volgograd.com/blog/codesys-advices.html
🔗 Советы по программированию ПЛК с примерами в среде CODESYS