ExST: расширенный ST — АСУ ТП: сообщество инженеров

ExST: расширенный ST

Коротко о статье

Статья описывает расширения языка ST (Extended ST), включая операторы GOTO, CONTINUE, S=, R=, типизированные литералы, CASE ELSE, OR_ELSE и AND_THEN. Из неё можно узнать особенности работы этих конструкций и примеры их применения.

Главное
  • Оператор GOTO в ST работает как JMP с метками, но его следует избегать из-за риска бесконечных циклов.
  • CONTINUE относится к расширениям языка и пропускает текущую итерацию цикла, в отличие от EXIT, который прерывает цикл.
  • Операторы S= и R= устанавливают или сбрасывают переменную в TRUE/FALSE соответственно и не могут изменить её обратно.
  • Присваивание как выражение позволяет использовать присваивание внутри условий, но рекомендуется разделять такие конструкции для читаемости.
  • Типизированные литералы, например BYTE#127, задают точный тип константы и могут вызывать предупреждения при потере данных.
  • OR_ELSE и AND_THEN вычисляют правую часть только при необходимости, что оптимизирует производительность и предотвращает ошибки с указателями.
Термины и технологии
  • ST
  • Extended ST
  • GOTO
  • CONTINUE
  • EXIT
  • S=
  • R=
  • OR_ELSE
  • AND_THEN
  • TwinCAT
  • CoDeSys

Кому полезно: Инженеры АСУ ТП, программирующие на ST в TwinCAT или CoDeSys.

По теме на сайте

Первоисточник: gotwincat.blogspot.com

Выжимка составлена редакцией сайта с помощью ИИ по тексту статьи.

Язык ST (Structured Text) получил много полезных расширений, что в последствии назвали Extended ST. И это не только модное ООП. Я свалил все в кучу: и расширения, и просто малоизвестные вещи, к тому же, всё это подходит не только для TwinCAT, но и для CoDeSys-мира вообще. Начнем с мальчика для битья — оператора GOTO…

Язык ST все еще поддерживает оператор GOTO. Не прямо так гоу-ту, а как в ассемблере — JMP куда-то там на метку в коде. Выглядит это так:

label_infinitum:

(* полезный код *)

JMP myLabel_4321;

(* бесполезный кот *)

myLabel_4321: // это метка для перехода

(* еще полезный код *)

JMP label_infinitum;

Избегайте бесконечно-вечных циклов.

CONTINUE

CONTINUE относится к расширению языка, а EXIT нет. Если вы не знали, то EXIT немедленно прекращает выполнение текущего цикла и выходит из него (текущая итерации FOR, WHILE или REPEAT не будет выполнена до конца). CONTINUE же прекращает текущую итерацию и немедленно переходит к началу цикла, выполнять следующую итерацию:

FOR i := 0 TO 100 BY 2 DO

IF i MOD 3 = 0 THEN

CONTINUE; // пропускаем инкремент переменной a и переходим к следующей итерации

END_IF

a := a + 1;

END_FOR

Осторожнее с вложенными циклами: EXIT выходит только из одного вложения. Для выхода из нескольких вложенных циклов, помогает команда JMP.

FOR j := 0 TO 100 DO

FOR i := 0 TO 100 DO

IF ??? THEN

EXIT; // завершит цикл FOR i …

ELSE

JMP finita_incantata;

END_IF

END_FOR

// EXIT приведет сюда

END_FOR

finita_incantata:

Оператор MOD находит остаток от деления, как оператор % в C++.

Оператор установки S=

Запись A S= X; аналогична:

IF X THEN

A := TRUE;

END_IF

Как только A станет равным TRUE, то никакое значение X уже не изменит A. Оператор S= никогда не сможет сбросить A в FALSE.

A X A

FALSE S= FALSE → FALSE

FALSE S= TRUE → TRUE

TRUE S= FALSE → TRUE

TRUE S= TRUE → TRUE

Оператор сброса R=

Запись F R= X; аналогична:

IF X THEN

F := FALSE;

END_IF

Как только F станет равным FALSE, то никакое значение X уже не изменит F. Оператор R= никогда не сможет установить F в TRUE.

F X F

FALSE R= FALSE → FALSE

FALSE R= TRUE → FALSE

TRUE R= FALSE → TRUE

TRUE R= TRUE → FALSE

Присваивание

Присваивание как выражение (assignment as expression) можно делать так:

IF bVar := (i = 2) THEN

i := i + 1;

END_IF

bVar — булева переменная, получает значение от выражения (i = 2), то есть будет равна TRUE когда (i = 2). Затем bVar оценивается в IF. Лучше не мудрить и не пихать в одну строку несколько команд сразу. Пишите так, чтобы было проще читать впоследствии. Компилятору все-равно как вы написали, работать это будет одинаково, а вероятность ошибиться намного выше в сложносочиненной записи:

bVar := (i = 2);

IF bVar THEN

i := i + 1;

END_IF

Можно присваивать нескольких переменных одновременно:

a:= b:= c:= k + 12;

Но не дайте обмануть себя видом и формой записи — здесь нет присваивания, передающегося справа-налево по цепочке! Аналогичная, но более длинная запись, выглядит так:

a := k + 12;

b := k + 12;

c := k + 12;

Выражение (k + 12) будет вычисляться три раза, поэтому выгоднее записать:

c := k + 12;

a:= b:= c;

Теперь вспомним операторы установки/сброса. Как будет работать следующая строка? Ответ в конце поста.

A S= F R= X;

Типизированные литералы

Звучит жутко умно, но это всего-лишь способ задать точный тип числа.

A := 123; — не говорит нам какого типа число 123 (литерал). Это точно не BOOL и скорее всего не REAL / LREAL (нет десятичной запятой). Возможны SINT, USINT, BYTE, INT, UINT, WORD, DINT, UDINT, DWORD. Так кто же из них?

Обычно тип литерала будет зависеть от типа переменной A. Поэтому можно просто уточнить запись литерала: A := 123.0; и тогда мы получим REAL. Но что если хочется BYTE, а переменная типа UINT?

Для этого тип можно обозначить точно: A := BYTE#127;

Такую запись можно использовать везде, где допустимо использование констант. Если TwinCAT обнаружит потенциальную потерю данных, то выдаст сообщение об ошибке или просто предупреждение, если всё не так страшно.

CASE … ELSE

Если конкретный шаг CASE для какого-то значения переменной не существует, то CASE игнорируется целиком, за исключением если… Блок ELSE в CASE позволяет задать действие по умолчанию, если номер шага в CASE не задан:

CASE state OF

0:

DoSomething();

100:

DoSomething2();

…

2000:

DoSomethingAgainNAgain();

ELSE

DontKnowWhatHappenedLastTime();

END_CASE

Если вы знакомы с C++, то это аналогично: switch case … default.

OR_ELSE

Работает аналогично OR, но если левая часть OR_ELSE уже равна TRUE, то правая часть не проверяется и не выполняется:

VAR

a, z, b : BOOL;

i : INT;

END_VAR

z := a OR_ELSE (b := (i = 2));

Если А равно TRUE, то правая часть OR_ELSE не будет выполняться и B никогда не сможет стать TRUE. Можно оптимизировать производительность, исключив лишний вызов функций. И теперь мы знаем, что для «обычного» OR, независимо от значения операндов, вызываются и проверяются обе части: и левая, и правая.

AND_THEN

Как AND, но правая часть AND_THEN обрабатывается только при условии, что левая часть равна TRUE. Например, это удобно для проверок указателей:

IF (ptr <> 0 AND_THEN ptr ^= 99) THEN …

Когда указатель не равен нулю и_тогда выполнить сравнение адреса указателя. Это позволяет избежать проблем с нулевым указателем. В обычном AND всегда обрабатываются обе части независимо от их значения.

Ответ

A S= F R= X;

// работает как

A S= X;

F R= X;

Источник: https://gotwincat.blogspot.com/2018/02/extended-structured-text-codesys-and-twincat.html

Источник: статья в сообществе ВКонтакте

Оставьте комментарий

Войти

Зарегистрироваться

Сбросить пароль

Пожалуйста, введите ваше имя пользователя или эл. адрес, вы получите письмо со ссылкой для сброса пароля.

Прокрутить вверх