1. Обязательно представиться на русском языке кириллицей (заполнить поле "Имя").
  2. Фиктивные имена мы не приветствуем. Ивановых и Пупкиных здесь уже достаточно.
  3. Не надо писать свой вопрос в первую попавшуюся тему - всегда лучше создать новую тему.
  4. За поиск, предложение и обсуждение пиратского ПО и средств взлома - бан без предупреждения. Непонятно? - Читать здесь.
  5. Рекламу и частные объявления "куплю/продам/есть халтура" мы не размещаем ни на каких условиях.
  6. Перед тем как что-то написать - читать здесь, а затем здесь и здесь.
  7. Не надо писать в ЛС администраторам свои технические вопросы. Администраторы форума отлично знают как работает форум, а не все-все контроллеры, о которых тут пишут.

Монограф

Модератор: специалисты по PLC

Ответить

Автор темы
leon78
эксперт
эксперт
Сообщения: 1146
Зарегистрирован: 25 июл 2008, 10:06
Имя: Леонид
Страна: РФ
Благодарил (а): 49 раз
Поблагодарили: 134 раза

Монограф

Сообщение leon78 »

Михайло писал(а):
Какой граф является каноническим? Это граф, у которого:
1. В условиях переходов записаны исключительно входные сигналы, коими у ПЛК являются входные сигналы I.
2. В вершинах выполняются действия исключительно по включению/отключению выходных сигналов, коими у ПЛК являются выходные сигналы Q.
3. Промежуточные маркеры M используются только для хранения номера текущего состояния графа.
Эти три правила позволят из неканонической формы получить каноническую форму графа. Если Вы составляли ранее граф, то можете посмотреть, что же представляет собой канонический граф, проделав несложную работу по совершенствованию Вашего собственного графа.

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

Напоследок еще одно примечание.
ПРИМЕЧАНИЕ: Таймеры и счетчики, используемые в программе, могут находиться как в условиях перехода (опрос состояния таймера или счетчика), так и в действиях (включение/отключение таймера или счетчика).

<Продолжение следует.>
Хард - это то, что можно швырнуть об стенку, а софт - это то, что можно лишь обматерить.

Автор темы
leon78
эксперт
эксперт
Сообщения: 1146
Зарегистрирован: 25 июл 2008, 10:06
Имя: Леонид
Страна: РФ
Благодарил (а): 49 раз
Поблагодарили: 134 раза

Монограф

Сообщение leon78 »

Теперь о самом монографе. Монограф - это граф в канонической форме, в котором допускается распараллеливание процессов, при этом граф остается единственным в программе (поэтому и назван монографом).

Еще немного терминологии:
1. Дивергенция - это когда из одного состояния возможен переход в несколько состояний. В состоянии с дивергенцией определяется дальнейшее течение выполнения программы в зависимости от условий в переходах. Ничего сложного в этом термине нет.
2. Конвергенция - это когда из разных состояний возможны переходы в одно и то же состояние. Тоже термин несложный.
Термины "дивергенция" и "конвергенция" взяты, насколько мне помнится, из справки к программе Graph от Сименса.

А теперь возможно для кого-то немного новенького:
1. Бифуркация - это дивергенция, при которой из одного активного состояния могут быть получены два и более состояний одновременно. Это происходит в случае, если условия двух и более бифурцирующих переходов могут быть активными.
2. Монофуркация - это конвергенция, в которой специально подобранным условием исходящего перехода намеренно добиваются соединения двух и более активных состояний в одно активное. О монофуркации позже.
Термины авторские, предлагайте свои идеи.

Самым интересным новым термином является бифуркация. Не всякий граф и не всякая теория графов допускает бифуркацию. Достигается запрет несколькими способами:
1. Написание таких условий переходов, которые ни в коем случае не могут быть активны одновременно два перехода. Например, в случае двух переходов: Transition1:=SQ1; Transition2:=not(SQ1)
2. Задание приоритетов каждому исходящему переходу из одной вершины. Переключение состояний осуществляется по наиболее приоритетному переходу.

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

<Продолжение следует.>
Хард - это то, что можно швырнуть об стенку, а софт - это то, что можно лишь обматерить.

Автор темы
leon78
эксперт
эксперт
Сообщения: 1146
Зарегистрирован: 25 июл 2008, 10:06
Имя: Леонид
Страна: РФ
Благодарил (а): 49 раз
Поблагодарили: 134 раза

Re: Монограф

Сообщение leon78 »

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

1. Любые раздвоившиеся в точке бифуркации состояния должны слиться в точке монофуркации. Это означает, что первое состояние, добравшееся до точки монофуркации, должно дождаться другого состояния бегущего по параллельной ветви, и в момент прихода в точку монофуркации последнего "параллельного" состояния все состояния сливаются в одно состояние, которое уже может продолжать дальнейшее путешествие по ветвям графа. Данный процесс я называю синхронизацией. Можно представить примеры из практики: совершают одновременно ход два гидроцилиндра. Тот цилиндр, который дошел до конечной точки быстрее, ожидает окончания движения второго цилиндра (синхронизация) и потом они дружно совершают, например, обратный ход. Так как может быть неизвестно заранее, который из цилиндров приедет в конечную точку раньше, то синхронизация в данном случае удобна.
2. В параллельных ветвях должны выполнять только разные действия. Например, если в одной ветви включается пускатель KM1, то в параллельной ветви этот пускатель не должен включаться или выключаться.

Итак, о возможностях монографа:
1. Монограф легко читается
2. Монограф может описывать параллельные процессы
3. Монограф может заменять вложенные графы
4. Для любого алгоритма автоматизации может быть составлен монограф

Примеры применения:
1. С помощью монографа автоматизирован автоматический цикл стенда обкатки узла трактора. Граф состоит из 50 состояний.
2. Для станка производства сторонней организации был предложен монограф, который понятнее отражает работу станка, чем имевшиеся 7 графов, работающих параллельно.

<Продолжение следует.>
Хард - это то, что можно швырнуть об стенку, а софт - это то, что можно лишь обматерить.

Автор темы
leon78
эксперт
эксперт
Сообщения: 1146
Зарегистрирован: 25 июл 2008, 10:06
Имя: Леонид
Страна: РФ
Благодарил (а): 49 раз
Поблагодарили: 134 раза

Монограф

Сообщение leon78 »

Итак, поясню: данный граф соответствует подпрограмме SBR_Cycle в программе v0.10.mwp
управления 0.10.gif
В состояниях S0.5, S3.0, S8.3, S9.4 происходит бифуркация. Проследите, каким способом обеспечивается монофуркация!

В состоянии S4.1 бифуркация искусственно запрещена. Также посмотрите, как это делается!

Также можете проанализировать, как данные процессы протекают в программе (работа графа, бифуркация, монофуркация - синхронизация).
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Хард - это то, что можно швырнуть об стенку, а софт - это то, что можно лишь обматерить.
Аватара пользователя

CHANt
эксперт
эксперт
Сообщения: 1467
Зарегистрирован: 25 июл 2008, 10:25
Имя: Эдуард Владимирович
Страна: СССР
город/регион: Оренбург
Благодарил (а): 46 раз
Поблагодарили: 105 раз

Примеры подхода "Монограф"

Сообщение CHANt »

Почитал серию "Монограф" от Leon78 и Михайло! :) . Очень интересно! Есть над чем подумать...
Сам пользуюсь модифицированным методом, базирующийся на Switch, и практического применения его на S7-200/300 Вавиловым Константином Валерьевичем. Со статьями Вавилова можно ознакомиться на сайте Шалыто А.А. - http://is.ifmo.ru/progeny/
Правда, некоторые приемы, изложенные у К.В. Вавилова, меня не очень устраивали. К примеру: вынос таймеров за тело функции, использование двух логических переменных там, где должна быть одна выходная... К середине 2004 года получилось составить шаблон для STL (Step7), огромную помощь оказала инструкция распределенного перехода "JL". Чем и пользуюсь постоянно. Есть пока и минусы, бифуркацию не рассматривал (не было необходимости).
Пример:
Технологический алгоритм управления жаротрубным котлом с вспомогательным оборудованием (насосная группа и трехходовой клапан рециркуляции, базовые алгоритмы этих устройств остались "за кадром")
Структура программы:
Изображение
Порядок разработки алгоритма управления котлом:
На первом этапе определяю необходимые входные и выходные данные...
Изображение
Данные, необходимые для работы функционального блока, поступают от автоматики безопасности котла,и функциональных блоков управления вспомогательным оборудованием, т.е. с соответствующих алгоритмов.
Далее разработка алгоритма:
Изображение
Код в STL данного алгоритма:

Код: Выделить всё

FUNCTION_BLOCK "AUTOMAT_BOIL"
TITLE =
AUTHOR : CHANt
FAMILY : IES
NAME : AU_BOIL
VERSION : 0.1


VAR_INPUT
  A_RUN_BOIL : BOOL ;	//Включить котел
  COOL_RESET : BOOL ;	//Сбросить режим расхолаживания котла
  ALARM_SHARED : BOOL ;	//Общий аварийный сигнал выключения котельной
  ALARM_BOIL : BOOL ;	//Общий аварийный сигнал по котлу
  ALARM_BURNER : BOOL ;	//Авария горелки
  ALARM_C_BOILS : BOOL ;	//Общий аварийный сигнал по котловому контуру
  NOT_RUN_NK : BOOL ;	//Группа насосов рециркуляции неисправна
  NORMA_BURNER : BOOL ;	//Горелка в работе
  POWER : BOOL ;	//Есть электропитание котла
  TIM_NUM_BOIL : TIMER ;	//Номер таймера задержки выключения котла
  TIM_BASE_BOIL : S5TIME ;	//База времени для таймера
END_VAR
VAR_OUTPUT
  A_RUN_NK : BOOL ;	//Запустить группу рецирку
  RUN_PID : BOOL ;	//Включить регулятор рециркуляции
  OPEN_PID : BOOL ;	//Открыть регулятор при остывании котла
  CLOSE_PID : BOOL ;	//Закрыть регулятор при полной остановке котла (по умолчанию закрыт)
  RUN_BURNER : BOOL ;	//Включить горелку
  WORK_BOIL : BOOL ;	//Котел в работе
  COOL_BOIL : BOOL ;	//Расхолаживание котла
  ALARM_BOIL_1 : BOOL ;	//Авария котла
END_VAR
VAR_IN_OUT
  STATE_WORD : INT ;	//Слово с текущим номером состояния алгоритма
END_VAR
VAR
  TIM_BOIL : BOOL ;	//Истек таймер задержки выключения котла
END_VAR
VAR_TEMP
  A_RUN_NK_S : BOOL ;	//Признак запуска котловых насосов
  A_RUN_NK_R : BOOL ;	//Сбросить признак запуска котловых насосов
  RUN_PID_S : BOOL ;	//Признак запуска регулятора рециркуляции
  RUN_PID_R : BOOL ;	//Сбросить признак запуска регулятора рециркуляции
  OPEN_PID_S : BOOL ;	//Установить признак открыть регулятор
  OPEN_PID_R : BOOL ;	//Сбросить признак открыть регулятор
  CLOSE_PID_S : BOOL ;	//Признак запуска закрыть регулятор
  CLOSE_PID_R : BOOL ;	//Сбросить признак закрыть регулятор
  RUN_BURNER_S : BOOL ;	//Признак запуска горелки
  RUN_BURNER_R : BOOL ;	//Сбросить признак запуска горелки
  WORK_BOIL_S : BOOL ;	//Признак котел в работе
  WORK_BOIL_R : BOOL ;	//Сбросить признак котел в работе
  COOL_BOIL_S : BOOL ;	//Признак запуска котел расхолаживается
  COOL_BOIL_R : BOOL ;	//Сбросить признак запуска котел расхолаживается
  ALARM_BOIL_S : BOOL ;	//Признак аварии котла
  ALARM_BOIL_R : BOOL ;	//Сбросить признак аварии котла
  TIM_BOIL_S : BOOL ;	//Включить таймер задержки выключения котла
  TIM_BOIL_R : BOOL ;	//Выключить таймер задержки выключения котла
END_VAR
BEGIN
NETWORK 
TITLE =Сброс промежуточных переменных и обработка слова состояния

      CLR   ; 
      =     #A_RUN_NK_S; 
      =     #A_RUN_NK_R; 
      =     #RUN_PID_S; 
      =     #RUN_PID_R; 
      =     #OPEN_PID_S; 
      =     #OPEN_PID_R; 
      =     #CLOSE_PID_S; 
      =     #CLOSE_PID_R; 
      =     #RUN_BURNER_S; 
      =     #RUN_BURNER_R; 
      =     #WORK_BOIL_S; 
      =     #WORK_BOIL_R; 
      =     #COOL_BOIL_S; 
      =     #COOL_BOIL_R; 
      =     #ALARM_BOIL_S; 
      =     #ALARM_BOIL_R; 
      =     #TIM_BOIL_S; 
      =     #TIM_BOIL_R; 
      L     #STATE_WORD; 
      JL    GT10; 
      JU    ST_0; 
      JU    ST_1; 
      JU    ST_2; 
      JU    ST_3; 
      JU    ST_4; 
      JU    ST_5; 
      JU    ST_6; 
      JU    ST_7; 
      JU    ST_8; 
      JU    ST_9; 
GT10: JU    Err; 
NETWORK 
TITLE =Обработка начального состояния
//1.A_RUN_BOIL&!ALARM_SHARED&!ALARM_BOIL&!ALARM_BURNER&!ALARM_C_BOILS&!NOT_RUN_NK&
//POWER\z:OPEN_PID_S;CLOSE_PID_R
//2.!CLOSE_PID\z:CLOSE_PID_S
//3.ALARM_BURNER||NOT_RUN_NK\z:ALARM_BOIL_S
ST_0: A     #A_RUN_BOIL; 
      AN    #ALARM_SHARED; 
      AN    #ALARM_BOIL; 
      AN    #ALARM_BURNER; 
      AN    #ALARM_C_BOILS; 
      AN    #NOT_RUN_NK; 
      A     #POWER; 
      JCN   Tr01; 
      =     #OPEN_PID_S; 
      =     #CLOSE_PID_R; 
      L     1; 
      T     #STATE_WORD; 
      JU    End; 
Tr01: AN    #CLOSE_PID; 
      JCN   Tr02; 
      =     #CLOSE_PID_S; 
      L     7; 
      T     #STATE_WORD; 
      JU    End; 
Tr02: O     #ALARM_BURNER; 
      O     #NOT_RUN_NK; 
      JCN   End; 
      =     #ALARM_BOIL_S; 
      L     9; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 1
//1.ALARM_BOIL||!POWER||NOT_RUN_NK\z:ALARM_BOIL_S
//2.!A_RUN_BOIL||ALARM_SHARED||ALARM_C_BOILS\z:OPEN_PID_R;CLOSE_PID_S;
//3.A_RUN_BOIL&!ALARM_SHARED&!ALARM_BOIL&!ALARM_BURNER&!ALARM_C_BOILS&!NOT_RUN_NK&
//POWER\z:A_RUN_NK_S
ST_1: O     #ALARM_BOIL; 
      ON    #POWER; 
      O     #NOT_RUN_NK; 
      JCN   Tr11; 
      =     #ALARM_BOIL_S; 
      L     8; 
      T     #STATE_WORD; 
      JU    End; 
Tr11: ON    #A_RUN_BOIL; 
      O     #ALARM_SHARED; 
      O     #ALARM_C_BOILS; 
      JCN   Tr12; 
      =     #OPEN_PID_R; 
      =     #CLOSE_PID_S; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
Tr12: A     #A_RUN_BOIL; 
      AN    #ALARM_SHARED; 
      AN    #ALARM_BOIL; 
      AN    #ALARM_BURNER; 
      AN    #ALARM_C_BOILS; 
      AN    #NOT_RUN_NK; 
      A     #POWER; 
      JCN   End; 
      =     #A_RUN_NK_S; 
      L     2; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 2
//1.NOT_RUN_NK||ALARM_BOIL||!POWER\z:A_RUN_NK_R;ALARM_BOIL_S
//2.!A_RUN_BOIL||ALARM_SHARED||ALARM_C_BOILS\z:OPEN_PID_R;CLOSE_PID_S;
//A_RUN_NK_R
//3.A_RUN_BOIL&!ALARM_SHARED&!ALARM_BOIL&!ALARM_BURNER&!ALARM_C_BOILS&!NOT_RUN_NK&
//POWER\z:RUN_BURNER_S
ST_2: O     #NOT_RUN_NK; 
      O     #ALARM_BOIL; 
      ON    #POWER; 
      JCN   Tr21; 
      =     #ALARM_BOIL_S; 
      =     #A_RUN_NK_R; 
      L     8; 
      T     #STATE_WORD; 
      JU    End; 
Tr21: ON    #A_RUN_BOIL; 
      O     #ALARM_SHARED; 
      O     #ALARM_C_BOILS; 
      JCN   Tr22; 
      =     #OPEN_PID_R; 
      =     #CLOSE_PID_S; 
      =     #A_RUN_NK_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
Tr22: A     #A_RUN_BOIL; 
      AN    #ALARM_SHARED; 
      AN    #ALARM_BOIL; 
      AN    #ALARM_BURNER; 
      AN    #ALARM_C_BOILS; 
      AN    #NOT_RUN_NK; 
      A     #POWER; 
      JCN   End; 
      =     #RUN_BURNER_S; 
      L     3; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 3
//1.ALARM_BURNER||NOT_RUN_NK||ALARM_BOIL||!POWER\z:A_RUN_NK_R;RUN_BURNER_R;
//ALARM_BOIL_S
//2.!A_RUN_BOIL||ALARM_SHARED||ALARM_C_BOILS\z:OPEN_PID_R;CLOSE_PID_S;
//A_RUN_NK_R;RUN_BURNER_R
//3.NORMA_BURNER&A_RUN_BOIL&!ALARM_SHARED&!ALARM_BOIL&!ALARM_BURNER&!ALARM_C_BOILS
//&!NOT_RUN_NK&POWER\z:OPEN_PID_R;WORK_BOIL_S;RUN_PID_S
ST_3: O     #ALARM_BURNER; 
      O     #NOT_RUN_NK; 
      O     #ALARM_BOIL; 
      ON    #POWER; 
      JCN   Tr31; 
      =     #A_RUN_NK_R; 
      =     #RUN_BURNER_R; 
      =     #ALARM_BOIL_S; 
      L     8; 
      T     #STATE_WORD; 
      JU    End; 
Tr31: ON    #A_RUN_BOIL; 
      O     #ALARM_SHARED; 
      O     #ALARM_C_BOILS; 
      JCN   Tr32; 
      =     #OPEN_PID_R; 
      =     #CLOSE_PID_S; 
      =     #A_RUN_NK_R; 
      =     #RUN_BURNER_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
Tr32: A     #NORMA_BURNER; 
      A     #A_RUN_BOIL; 
      AN    #ALARM_SHARED; 
      AN    #ALARM_BOIL; 
      AN    #ALARM_BURNER; 
      AN    #ALARM_C_BOILS; 
      AN    #NOT_RUN_NK; 
      A     #POWER; 
      JCN   End; 
      =     #OPEN_PID_R; 
      =     #WORK_BOIL_S; 
      =     #RUN_PID_S; 
      L     4; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 4
//1.ALARM_BOIL||ALARM_BURNER||NOT_RUN_NK||!POWER\z:RUN_BURNER_R;
//WORK_BOIL_R;RUN_PID_R;ALARM_BOIL_S
//2.ALARM_SHARED||ALARM_C_BOILS\z:CLOSE_PID_S;A_RUN_NK_R;RUN_BURNER_R;WORK_BOIL_R;
//RUN_PID_R
//3.!NORMA_BURNER&A_RUN_BOIL&!ALARM_BURNER\z:RUN_PID_R;OPEN_PID_S;WORK_BOIL_R;
//RUN_BURNER_R;COOL_BOIL_S;TIM_BOIL_S
//4.!A_RUN_BOIL&!ALARM_BURNER\z:RUN_PID_R;OPEN_PID_S;WORK_BOIL_R;
//RUN_BURNER_R;COOL_BOIL_S;TIM_BOIL_S
ST_4: O     #ALARM_BOIL; 
      O     #ALARM_BURNER; 
      O     #NOT_RUN_NK; 
      ON    #POWER; 
      JCN   Tr41; 
      =     #RUN_BURNER_R; 
      =     #WORK_BOIL_R; 
      =     #RUN_PID_R; 
      =     #ALARM_BOIL_S; 
      L     8; 
      T     #STATE_WORD; 
      JU    End; 
Tr41: O     #ALARM_SHARED; 
      O     #ALARM_C_BOILS; 
      JCN   Tr42; 
      =     #CLOSE_PID_S; 
      =     #A_RUN_NK_R; 
      =     #RUN_BURNER_R; 
      =     #WORK_BOIL_R; 
      =     #RUN_PID_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
Tr42: AN    #NORMA_BURNER; 
      A     #A_RUN_BOIL; 
      AN    #ALARM_BURNER; 
      JCN   Tr43; 
      =     #RUN_PID_R; 
      =     #OPEN_PID_S; 
      =     #WORK_BOIL_R; 
      =     #RUN_BURNER_R; 
      =     #COOL_BOIL_S; 
      =     #TIM_BOIL_S; 
      L     5; 
      T     #STATE_WORD; 
      JU    End; 
Tr43: AN    #A_RUN_BOIL; 
      AN    #ALARM_BURNER; 
      JCN   End; 
      =     #RUN_PID_R; 
      =     #OPEN_PID_S; 
      =     #WORK_BOIL_R; 
      =     #RUN_BURNER_R; 
      =     #COOL_BOIL_S; 
      =     #TIM_BOIL_S; 
      L     5; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 5
//1.!TIM_BOIL&!POWER\z:A_RUN_NK_R;COOL_BOIL_R;TIM_BOIL_R;ALARM_BOIL_S
//2.TIM_BOIL\z:A_RUN_NK_R;COOL_BOIL_R
//3.COOL_RESET&!ALARM_BOIL&!ALARM_BURNER&!NOT_RUN_NK&POWER\z:OPEN_PID_R;CLOSE_PID_
//S;TIM_BOIL_R;A_RUN_NK_R;COOL_BOIL_R
ST_5: AN    #TIM_BOIL; 
      AN    #POWER; 
      JCN   Tr51; 
      =     #A_RUN_NK_R; 
      =     #COOL_BOIL_R; 
      =     #TIM_BOIL_R; 
      =     #ALARM_BOIL_S; 
      L     8; 
      T     #STATE_WORD; 
      JU    End; 
Tr51: A     #TIM_BOIL; 
      JCN   Tr52; 
      =     #A_RUN_NK_R; 
      =     #COOL_BOIL_R; 
      L     6; 
      T     #STATE_WORD; 
      JU    End; 
Tr52: A     #COOL_RESET; 
      AN    #ALARM_BOIL; 
      AN    #ALARM_BURNER; 
      AN    #NOT_RUN_NK; 
      A     #POWER; 
      JCN   End; 
      =     #OPEN_PID_R; 
      =     #CLOSE_PID_S; 
      =     #TIM_BOIL_R; 
      =     #A_RUN_NK_R; 
      =     #COOL_BOIL_R; 
      =     #ALARM_BOIL_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 6
//TIM_BOIL\z:OPEN_PID_R;CLOSE_PID_S;TIM_BOIL_R
ST_6: A     #TIM_BOIL; 
      JCN   End; 
      =     #OPEN_PID_R; 
      =     #CLOSE_PID_S; 
      =     #TIM_BOIL_R; 
      =     #ALARM_BOIL_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 7
//CLOSE_PID
ST_7: A     #CLOSE_PID; 
      JCN   End; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 8
//1.!POWER\z:ALARM_BOIL_R;OPEN_PID_R
//2.(ALARM_BOIL||ALARM_BURNER||NOT_RUN_NK)||(!ALARM_BOIL&!ALARM_BURNER&
//!NOT_RUN_NK)&POWER\z:OPEN_PID_S;COOL_BOIL_S;
//TIM_BOIL_S
ST_8: AN    #POWER; 
      JCN   Tr81; 
      =     #ALARM_BOIL_R; 
      =     #OPEN_PID_R; 
      =     #A_RUN_NK_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
Tr81: A(    ; 
      O     #ALARM_BOIL; 
      O     #ALARM_BURNER; 
      O     #NOT_RUN_NK; 
      )     ; 
      O(    ; 
      AN    #ALARM_BOIL; 
      AN    #ALARM_BURNER; 
      AN    #NOT_RUN_NK; 
      )     ; 
      A     #POWER; 
      JCN   End; 
      =     #OPEN_PID_S; 
      =     #COOL_BOIL_S; 
      =     #TIM_BOIL_S; 
      L     5; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 9
//1.!ALARM_BURNER&!NOT_RUN_NK&POWER\z:ALARM_BOIL_R
ST_9: AN    #ALARM_BURNER; 
      AN    #NOT_RUN_NK; 
      A     #POWER; 
      JCN   End; 
      =     #ALARM_BOIL_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка ошибки распределенного перехода

Err:  CLR   ; 
      =     #A_RUN_NK; 
      =     #RUN_PID; 
      =     #OPEN_PID; 
      =     #CLOSE_PID; 
      =     #RUN_BURNER; 
      =     #WORK_BOIL; 
      =     #COOL_BOIL; 
      =     #ALARM_BOIL_1; 
      L     0; //и загрузить число 0
      T     #STATE_WORD; //в слово состояния
      JU    End; //выйти на конец блока
End:  NOP   0; // конец блока  
NETWORK
TITLE =Обработка выходных сигналов

      A     #A_RUN_NK_S; 
      S     #A_RUN_NK; 
      A     #A_RUN_NK_R; 
      R     #A_RUN_NK; 
      NOP   0; 
      A     #RUN_PID_S; 
      S     #RUN_PID; 
      A     #RUN_PID_R; 
      R     #RUN_PID; 
      NOP   0; 
      A     #OPEN_PID_S; 
      S     #OPEN_PID; 
      A     #OPEN_PID_R; 
      R     #OPEN_PID; 
      NOP   0; 
      A     #CLOSE_PID_S; 
      S     #CLOSE_PID; 
      A     #CLOSE_PID_R; 
      R     #CLOSE_PID; 
      NOP   0; 
      A     #RUN_BURNER_S; 
      S     #RUN_BURNER; 
      A     #RUN_BURNER_R; 
      R     #RUN_BURNER; 
      NOP   0; 
      A     #WORK_BOIL_S; 
      S     #WORK_BOIL; 
      A     #WORK_BOIL_R; 
      R     #WORK_BOIL; 
      NOP   0; 
      A     #COOL_BOIL_S; 
      S     #COOL_BOIL; 
      A     #COOL_BOIL_R; 
      R     #COOL_BOIL; 
      NOP   0; 
      A     #ALARM_BOIL_S; 
      S     #ALARM_BOIL_1; 
      A     #ALARM_BOIL_R; 
      R     #ALARM_BOIL_1; 
      NOP   0; 
NETWORK
TITLE =Обработка таймера

      A     #TIM_BOIL_S; 
      L     #TIM_BASE_BOIL; 
      SS    #TIM_NUM_BOIL; 
      A     #TIM_BOIL_R; 
      R     #TIM_NUM_BOIL; 
      A     #TIM_NUM_BOIL; 
      =     #TIM_BOIL; 

END_FUNCTION_BLOCK
В общем виде, тело программы состоит из:
1) обнуление (сброс) команд, признаков и т.п., используемых в автомате, но не имеющих обнуления по условию;
2) определение текущего состояния;
3) проверка условий и действий в состоянии 0;
4) проверка условий и действий в состоянии N;
5) обработка ошибки распределенного перехода;
6) установка/сброс выходных переменных;
7) обработка вложенных таймеров.
Шаблон STL общего вида:

Код: Выделить всё

NETWORK
TITLE =Сброс промежуточных переменных и анализ слова состояния
      CLR   ; //сбросить в 0
      =     # Временная переменная 1; 
      =     # Временная переменная i; 
      L     #STATE_WORD; //загрузка слова состояния для перехода
      JL    GT_3; // метка перехода, которая указывает на конец списка
      JU    ST_0; //переход на начального состояния графа переходов
      JU    ST_1; // переход на 1 состояние графа перехода
      JU    ST_2; // переход на i состояние графа перехода
GT_3: JU    Err; // переход на метку обработок ошибок инструкции JL
NETWORK
TITLE =Обработка начального состояния
ST_0: A     #Входной параметр 1; // номер состояния автомата и условие 1
           A     #Входной параметр 2; //условие 2
           JCN   End; //если условие не выполнено уход на метку
           =     #Временная переменная 1; //Действие 1
           =     # Временная переменная 2; //Действие 2
           =     # Временная переменная i; //Действие i
           L     1; //переход по дуге графа в требуемое состояние
           T     #STATE_WORD; //загрузка номера следующего состояния
           JU    End; //Безусловный переход на конец блока
NETWORK
TITLE =Обработка состояния 1
ST_1:  A     # Входной параметр 1; // номер состояния автомата и условие 1
            AN    # Входной параметр 1; //условие 2
           JCN   Tr01; //если условие не выполнено уход на метку
           =     #Временная переменная 1; //Действие 1
           =     # Временная переменная 2; //Действие 2
           =     # Временная переменная i; //Действие i
            L     3; //переход по дуге графа в требуемое состояние
            T     #STATE_WORD; //загрузка номера следующего состояния
            JU    End; //Безусловный переход на конец блока
Tr01: A     # Входной параметр 1; // номер перехода (дуги) автомата и условие 1
          AN    # Входной параметр 1; //условие 2
          JCN   End; //если условие не выполнено уход на метку
          L     0; //переход по дуге графа в начальное состояние
          T     #STATE_WORD; //загрузка номера следующего состояния
          JU    End; //Безусловный переход на конец блока
И т.д.
NETWORK
TITLE =Обработка ошибки распределенного перехода
Err:  CLR
       =      Выходной параметр 1; 
        =      Выходной параметр 2; 
        =      Выходной параметр i; 
      L     0; //и загрузить число 0 начальное состояние
      T     #STATE_WORD; //в слово состояния
      JU    End; //выйти на конец блока
End:  NOP   0; // конец блока  
NETWORK
TITLE =Обработка выходных сигналов
      A     # Временная переменная 1; 
      S     # Выходной параметр 1; 
      A     # сброс Временная переменная 1; 
      R     # Выходной параметр 1; 
      NOP   0; 
NETWORK
TITLE =Обработка таймера

      A     #если есть команда на включение таймера, загрузить:; 
      L     #временную базу; 
      SS    #номер таймера; 
      A     #если есть команда на сброс таймера; 
      R     #сбросить таймер; 
      A     #если таймер отработал; 
      =     #установить внутреннюю перменную;
И для устранения "зависаний" выходных переменных, при некорректном выключении контроллера (бывает и такое), в ОВ100 (однократно, при запуске контроллера). в слово состояния загружаю большое число, больше чем кол-во состояний. При этом, после анализа слова управления, осуществляется переход на обработку ошибок распределенного перехода (JL) со сбросом выходных переменных и приведение алгоритма к начальному состоянию.
P.S. На STL размер кода значительно меньше чем в Graph.
--------------------------------------------------------------------------------------------

Михайло
эксперт
эксперт
Сообщения: 3643
Зарегистрирован: 10 ноя 2009, 04:58
Имя: Толмачев Михаил Алексеевич
город/регион: г. Чехов, МО
Благодарил (а): 8 раз
Поблагодарили: 286 раз

Re: Примеры подхода "Монограф"

Сообщение Михайло »

Я свой код пишу в LAD, с таймерами обращаюсь гораздо проще. Вот программа для S7-200, граф которой приведен в статейке "Монограф":
Станок Михайло v0.10.zip
Код легко переводится на язык любого ПЛК от любого производителя.

Примечания:
1. Чтобы увидеть код графа, смотрите подпрограмму SBR_Cycle. Остальные подпрограммы написаны на классическом LAD без использования теории графов.
2. В этой подпрограмме Network1...Network22 - действия (actions).
3. Network23...Network54 управляют отображением на графической панели оператора, но в графе данные действия не изображены, поэтому рекомендую для простоты удалить эти цепочки.
4. Network55 - также в графе отсутствует - этой цепочкой производится аварийный останов автоматического цикла.
5. Network56...Network59 - отсутствует в графе. Переменные T_Mode1, T_Mode2, T_Mode3, T_Mode4 используются для управления 4-мя прогрессбарами на панели оператора.
6. Network60...Network79 - включение таймеров.
7. Network80...Network127 - скелет (остов) графа.
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Аватара пользователя

CHANt
эксперт
эксперт
Сообщения: 1467
Зарегистрирован: 25 июл 2008, 10:25
Имя: Эдуард Владимирович
Страна: СССР
город/регион: Оренбург
Благодарил (а): 46 раз
Поблагодарили: 105 раз

Re: Примеры подхода "Монограф"

Сообщение CHANt »

Михайло писал(а):Код легко переводится на язык любого ПЛК от любого производителя.
Не у всех производителей среда программирования соответствует IEC 61131-3.
Михайло писал(а):2. В этой подпрограмме Network1...Network22 - действия (actions).
3. Network23...Network54 управляют отображением на графической панели оператора, но в графе данные действия не изображены, поэтому рекомендую для простоты удалить эти цепочки.
4. Network55 - также в графе отсутствует - этой цепочкой производится аварийный останов автоматического цикла.
5. Network56...Network59 - отсутствует в графе. Переменные T_Mode1, T_Mode2, T_Mode3, T_Mode4 используются для управления 4-мя прогрессбарами на панели оператора.
6. Network60...Network79 - включение таймеров.
7. Network80...Network127 - скелет (остов) графа.
Структура кода мне не совсем понятна. Вы сначала действия выполняете, а затем условия обрабатываете? По идее, надо сначала получить данные от процесса, затем действия выполнить и потом уже формировать команды на управление. Зачем ждать следующего скана программы для действий?

Затем, я в теле функции (функционального блока) не обращаюсь к областям памяти контроллера. Использую внутренние переменные самой функции, а на входах/выходах функции уже использую обращение к входам/выходам контроллера, RAM. Это обеспечивает переносимость самой функции, как библиотечного элемента в другой проект. Но, тут от задачи, вполне вероятно, что Вам это и не нужно, так как установка в единственном числе.
Ну и подходы немного разные. Я "зацепился" за единицу оборудования - шаровый кран, пытался просмотреть - как для него сформирован алгоритм (просто, рассмотреть сразу все, сходу, сложно :oops: ) ничего толком и не нашел. Получается он бесконтрольный - дали команду на закрытие/открытие и ждем ответа концевика, ну, еще автомат QF контролируете... Я не так поступаю, единица оборудования есть базовый алгоритм. В случае с краном - команда, таймер контроля, не пришел концевик - авария и соответствующие выводы в технологическом алгоритме установки. Т.е. косвенным образом контролирую оперативную цепь управления и сигнализации. Как-то так.
Я, в предыдущем посте, приводил структуру управления котлом с вспомогательным оборудованием - там так и есть, как считаю необходимым декомпозировать задачу - котел (это чайник :D и в нем ничего интересно кроме труб нет) есть технологическая установка, состоящая из единиц оборудования. Котел дал команду включить насос - технологический алгоритм группы насосов осуществляет выбор (по наработке и отсутствии аварийных сигналов) конкретного насоса, вступает в работу базовый алгоритм насоса, затем команды транслируются в функцию обмена по Profibus с ПЧ. Вот вся цепочка. Для заказчика, эта цепочка функций, отображается в FBD, а уже в теле блоков - STL.
Будет время, просмотрите работу Вавилова - http://is.ifmo.ru/progeny/_metod065.pdf Он там подробно описывал принципы формирования базовых и технологических алгоритмов.
--------------------------------------------------------------------------------------------

Михайло
эксперт
эксперт
Сообщения: 3643
Зарегистрирован: 10 ноя 2009, 04:58
Имя: Толмачев Михаил Алексеевич
город/регион: г. Чехов, МО
Благодарил (а): 8 раз
Поблагодарили: 286 раз

Re: Примеры подхода "Монограф"

Сообщение Михайло »

CHANt писал(а):Не у всех производителей среда программирования соответствует IEC 61131-3.
Тут нужны только инструкции OR, AND, NOT, SET и RESET. Больше ничего лишнего.
CHANt писал(а):Структура кода мне не совсем понятна. Вы сначала действия выполняете, а затем условия обрабатываете? По идее, надо сначала получить данные от процесса, затем действия выполнить и потом уже формировать команды на управление. Зачем ждать следующего скана программы для действий?
На самом деле цепочки выполняются псевдопараллельно, поэтому без разницы - можно и потом действия выполнить. Просто мое чутье показывает, что такое расположение облегчает первоначальное восприятие кода. Увидел наладчик действия (выходные маркеры Q) - начал соображать, если увидел состояния (промежуточные маркеры M) - соображалка не включается, т.к. он не знает, что это за биты.
CHANt писал(а):Получается он бесконтрольный - дали команду на закрытие/открытие и ждем ответа концевика, ну, еще автомат QF контролируете... Я не так поступаю, единица оборудования есть базовый алгоритм. В случае с краном - команда, таймер контроля, не пришел концевик - авария и соответствующие выводы в технологическом алгоритме установки. Т.е. косвенным образом контролирую оперативную цепь управления и сигнализации. Как-то так.
Сторожевой таймер предлагаете? У меня на панели отображается текущее действие, при зависании алгоритма причина может быть выяснена достаточно быстро. Таймер, мне кажется, излишен. Установка проектируется для себя, а не для заказчика, поэтому не заморачиваемся.
Автомат QF программа не контролирует, хотя к ПЛК его вспомогательный контакт подключен. Схему рисовал не я, я бы не стал так делать.
CHANt писал(а):Я, в предыдущем посте, приводил структуру управления котлом с вспомогательным оборудованием - там так и есть, как считаю необходимым декомпозировать задачу - котел (это чайник :D и в нем ничего интересно кроме труб нет) есть технологическая установка, состоящая из единиц оборудования. Котел дал команду включить насос - технологический алгоритм группы насосов осуществляет выбор (по наработке и отсутствии аварийных сигналов) конкретного насоса, вступает в работу базовый алгоритм насоса, затем команды транслируются в функцию обмена по Profibus с ПЧ. Вот вся цепочка. Для заказчика, эта цепочка функций, отображается в FBD, а уже в теле блоков - STL.
Деление на единицы оборудования усложняет общее восприятие программы. Концепция монографа, предлагаемая мною, противоположна вашей. Мой подход предполагает, что всей установкой управляет один граф - монограф. Если работа нескольких единиц оборудования взаимосвязана, то монограф великолепно показывает эти взаимосвязи. Согласно вашей концепции, взаимосвязи между алгоритмами отдельных единиц оборудования должны быть скрыты от взора (насколько я понял). Вы предлагаете рассматривать отдельные единицы как независимые модули, хотя на самом деле это не так. Эти взаимосвязи очень важны и поэтому восприятие программы значительно усложняется.
Аватара пользователя

CHANt
эксперт
эксперт
Сообщения: 1467
Зарегистрирован: 25 июл 2008, 10:25
Имя: Эдуард Владимирович
Страна: СССР
город/регион: Оренбург
Благодарил (а): 46 раз
Поблагодарили: 105 раз

Re: Примеры подхода "Монограф"

Сообщение CHANt »

Михайло писал(а):Деление на единицы оборудования усложняет общее восприятие программы. Концепция монографа, предлагаемая мною, противоположна вашей. Мой подход предполагает, что всей установкой управляет один граф - монограф. Если работа нескольких единиц оборудования взаимосвязана, то монограф великолепно показывает эти взаимосвязи. Согласно вашей концепции, взаимосвязи между алгоритмами отдельных единиц оборудования должны быть скрыты от взора (насколько я понял). Вы предлагаете рассматривать отдельные единицы как независимые модули, хотя на самом деле это не так. Эти взаимосвязи очень важны и поэтому восприятие программы значительно усложняется.
Не получится у меня всего одним графом...Ну никак...Объект состоит хоть и из связанных контуров, но, различается режимами работы. Т.е. один контур работает только зимой, другой контур работает всегда. Выход в безопасное состояние (действия при аварийных ситуациях) одного контура, совершенно не означает останов всего объекта. Иногда требуется привести в другое состояние единицу оборудования другого технологического контура, но не весь контур. И если единиц оборудования много, то и каждый раз писать одни и те же действия и усложнять кол-вом ветвей (нетворков) тело программы нет необходимости. Делать программу из 2-3 тыс. веток - :crazy0to: кисло.
Что касается взаимосвязи, в моем случае есть два пути:
1. связывать структуру программы в нотации CFC - структурных схем. Есть ряд производителей, которые других средств для своих контроллеров и не предлагают...
2. делать вызов в вершинах графов вложенных функций.
И никаких скрытых действий :)
Путей решения логических задач очень много, и утверждать что моя псевдонотация единственно верная, глупо :D
--------------------------------------------------------------------------------------------

Михайло
эксперт
эксперт
Сообщения: 3643
Зарегистрирован: 10 ноя 2009, 04:58
Имя: Толмачев Михаил Алексеевич
город/регион: г. Чехов, МО
Благодарил (а): 8 раз
Поблагодарили: 286 раз

Re: Примеры подхода "Монограф"

Сообщение Михайло »

Остается один мощный инструмент, который поддерживается монографом, это бифуркация... :roll:

Михайло
эксперт
эксперт
Сообщения: 3643
Зарегистрирован: 10 ноя 2009, 04:58
Имя: Толмачев Михаил Алексеевич
город/регион: г. Чехов, МО
Благодарил (а): 8 раз
Поблагодарили: 286 раз

Re: Примеры подхода "Монограф"

Сообщение Михайло »

CHANt писал(а): Далее разработка алгоритма:
Изображение
Код в STL данного алгоритма:

Код: Выделить всё

FUNCTION_BLOCK "AUTOMAT_BOIL"
TITLE =
AUTHOR : CHANt
FAMILY : IES
NAME : AU_BOIL
VERSION : 0.1


VAR_INPUT
  A_RUN_BOIL : BOOL ;	//Включить котел
  COOL_RESET : BOOL ;	//Сбросить режим расхолаживания котла
  ALARM_SHARED : BOOL ;	//Общий аварийный сигнал выключения котельной
  ALARM_BOIL : BOOL ;	//Общий аварийный сигнал по котлу
  ALARM_BURNER : BOOL ;	//Авария горелки
  ALARM_C_BOILS : BOOL ;	//Общий аварийный сигнал по котловому контуру
  NOT_RUN_NK : BOOL ;	//Группа насосов рециркуляции неисправна
  NORMA_BURNER : BOOL ;	//Горелка в работе
  POWER : BOOL ;	//Есть электропитание котла
  TIM_NUM_BOIL : TIMER ;	//Номер таймера задержки выключения котла
  TIM_BASE_BOIL : S5TIME ;	//База времени для таймера
END_VAR
VAR_OUTPUT
  A_RUN_NK : BOOL ;	//Запустить группу рецирку
  RUN_PID : BOOL ;	//Включить регулятор рециркуляции
  OPEN_PID : BOOL ;	//Открыть регулятор при остывании котла
  CLOSE_PID : BOOL ;	//Закрыть регулятор при полной остановке котла (по умолчанию закрыт)
  RUN_BURNER : BOOL ;	//Включить горелку
  WORK_BOIL : BOOL ;	//Котел в работе
  COOL_BOIL : BOOL ;	//Расхолаживание котла
  ALARM_BOIL_1 : BOOL ;	//Авария котла
END_VAR
VAR_IN_OUT
  STATE_WORD : INT ;	//Слово с текущим номером состояния алгоритма
END_VAR
VAR
  TIM_BOIL : BOOL ;	//Истек таймер задержки выключения котла
END_VAR
VAR_TEMP
  A_RUN_NK_S : BOOL ;	//Признак запуска котловых насосов
  A_RUN_NK_R : BOOL ;	//Сбросить признак запуска котловых насосов
  RUN_PID_S : BOOL ;	//Признак запуска регулятора рециркуляции
  RUN_PID_R : BOOL ;	//Сбросить признак запуска регулятора рециркуляции
  OPEN_PID_S : BOOL ;	//Установить признак открыть регулятор
  OPEN_PID_R : BOOL ;	//Сбросить признак открыть регулятор
  CLOSE_PID_S : BOOL ;	//Признак запуска закрыть регулятор
  CLOSE_PID_R : BOOL ;	//Сбросить признак закрыть регулятор
  RUN_BURNER_S : BOOL ;	//Признак запуска горелки
  RUN_BURNER_R : BOOL ;	//Сбросить признак запуска горелки
  WORK_BOIL_S : BOOL ;	//Признак котел в работе
  WORK_BOIL_R : BOOL ;	//Сбросить признак котел в работе
  COOL_BOIL_S : BOOL ;	//Признак запуска котел расхолаживается
  COOL_BOIL_R : BOOL ;	//Сбросить признак запуска котел расхолаживается
  ALARM_BOIL_S : BOOL ;	//Признак аварии котла
  ALARM_BOIL_R : BOOL ;	//Сбросить признак аварии котла
  TIM_BOIL_S : BOOL ;	//Включить таймер задержки выключения котла
  TIM_BOIL_R : BOOL ;	//Выключить таймер задержки выключения котла
END_VAR
BEGIN
NETWORK 
TITLE =Сброс промежуточных переменных и обработка слова состояния

      CLR   ; 
      =     #A_RUN_NK_S; 
      =     #A_RUN_NK_R; 
      =     #RUN_PID_S; 
      =     #RUN_PID_R; 
      =     #OPEN_PID_S; 
      =     #OPEN_PID_R; 
      =     #CLOSE_PID_S; 
      =     #CLOSE_PID_R; 
      =     #RUN_BURNER_S; 
      =     #RUN_BURNER_R; 
      =     #WORK_BOIL_S; 
      =     #WORK_BOIL_R; 
      =     #COOL_BOIL_S; 
      =     #COOL_BOIL_R; 
      =     #ALARM_BOIL_S; 
      =     #ALARM_BOIL_R; 
      =     #TIM_BOIL_S; 
      =     #TIM_BOIL_R; 
      L     #STATE_WORD; 
      JL    GT10; 
      JU    ST_0; 
      JU    ST_1; 
      JU    ST_2; 
      JU    ST_3; 
      JU    ST_4; 
      JU    ST_5; 
      JU    ST_6; 
      JU    ST_7; 
      JU    ST_8; 
      JU    ST_9; 
GT10: JU    Err; 
NETWORK 
TITLE =Обработка начального состояния
//1.A_RUN_BOIL&!ALARM_SHARED&!ALARM_BOIL&!ALARM_BURNER&!ALARM_C_BOILS&!NOT_RUN_NK&
//POWER\z:OPEN_PID_S;CLOSE_PID_R
//2.!CLOSE_PID\z:CLOSE_PID_S
//3.ALARM_BURNER||NOT_RUN_NK\z:ALARM_BOIL_S
ST_0: A     #A_RUN_BOIL; 
      AN    #ALARM_SHARED; 
      AN    #ALARM_BOIL; 
      AN    #ALARM_BURNER; 
      AN    #ALARM_C_BOILS; 
      AN    #NOT_RUN_NK; 
      A     #POWER; 
      JCN   Tr01; 
      =     #OPEN_PID_S; 
      =     #CLOSE_PID_R; 
      L     1; 
      T     #STATE_WORD; 
      JU    End; 
Tr01: AN    #CLOSE_PID; 
      JCN   Tr02; 
      =     #CLOSE_PID_S; 
      L     7; 
      T     #STATE_WORD; 
      JU    End; 
Tr02: O     #ALARM_BURNER; 
      O     #NOT_RUN_NK; 
      JCN   End; 
      =     #ALARM_BOIL_S; 
      L     9; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 1
//1.ALARM_BOIL||!POWER||NOT_RUN_NK\z:ALARM_BOIL_S
//2.!A_RUN_BOIL||ALARM_SHARED||ALARM_C_BOILS\z:OPEN_PID_R;CLOSE_PID_S;
//3.A_RUN_BOIL&!ALARM_SHARED&!ALARM_BOIL&!ALARM_BURNER&!ALARM_C_BOILS&!NOT_RUN_NK&
//POWER\z:A_RUN_NK_S
ST_1: O     #ALARM_BOIL; 
      ON    #POWER; 
      O     #NOT_RUN_NK; 
      JCN   Tr11; 
      =     #ALARM_BOIL_S; 
      L     8; 
      T     #STATE_WORD; 
      JU    End; 
Tr11: ON    #A_RUN_BOIL; 
      O     #ALARM_SHARED; 
      O     #ALARM_C_BOILS; 
      JCN   Tr12; 
      =     #OPEN_PID_R; 
      =     #CLOSE_PID_S; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
Tr12: A     #A_RUN_BOIL; 
      AN    #ALARM_SHARED; 
      AN    #ALARM_BOIL; 
      AN    #ALARM_BURNER; 
      AN    #ALARM_C_BOILS; 
      AN    #NOT_RUN_NK; 
      A     #POWER; 
      JCN   End; 
      =     #A_RUN_NK_S; 
      L     2; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 2
//1.NOT_RUN_NK||ALARM_BOIL||!POWER\z:A_RUN_NK_R;ALARM_BOIL_S
//2.!A_RUN_BOIL||ALARM_SHARED||ALARM_C_BOILS\z:OPEN_PID_R;CLOSE_PID_S;
//A_RUN_NK_R
//3.A_RUN_BOIL&!ALARM_SHARED&!ALARM_BOIL&!ALARM_BURNER&!ALARM_C_BOILS&!NOT_RUN_NK&
//POWER\z:RUN_BURNER_S
ST_2: O     #NOT_RUN_NK; 
      O     #ALARM_BOIL; 
      ON    #POWER; 
      JCN   Tr21; 
      =     #ALARM_BOIL_S; 
      =     #A_RUN_NK_R; 
      L     8; 
      T     #STATE_WORD; 
      JU    End; 
Tr21: ON    #A_RUN_BOIL; 
      O     #ALARM_SHARED; 
      O     #ALARM_C_BOILS; 
      JCN   Tr22; 
      =     #OPEN_PID_R; 
      =     #CLOSE_PID_S; 
      =     #A_RUN_NK_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
Tr22: A     #A_RUN_BOIL; 
      AN    #ALARM_SHARED; 
      AN    #ALARM_BOIL; 
      AN    #ALARM_BURNER; 
      AN    #ALARM_C_BOILS; 
      AN    #NOT_RUN_NK; 
      A     #POWER; 
      JCN   End; 
      =     #RUN_BURNER_S; 
      L     3; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 3
//1.ALARM_BURNER||NOT_RUN_NK||ALARM_BOIL||!POWER\z:A_RUN_NK_R;RUN_BURNER_R;
//ALARM_BOIL_S
//2.!A_RUN_BOIL||ALARM_SHARED||ALARM_C_BOILS\z:OPEN_PID_R;CLOSE_PID_S;
//A_RUN_NK_R;RUN_BURNER_R
//3.NORMA_BURNER&A_RUN_BOIL&!ALARM_SHARED&!ALARM_BOIL&!ALARM_BURNER&!ALARM_C_BOILS
//&!NOT_RUN_NK&POWER\z:OPEN_PID_R;WORK_BOIL_S;RUN_PID_S
ST_3: O     #ALARM_BURNER; 
      O     #NOT_RUN_NK; 
      O     #ALARM_BOIL; 
      ON    #POWER; 
      JCN   Tr31; 
      =     #A_RUN_NK_R; 
      =     #RUN_BURNER_R; 
      =     #ALARM_BOIL_S; 
      L     8; 
      T     #STATE_WORD; 
      JU    End; 
Tr31: ON    #A_RUN_BOIL; 
      O     #ALARM_SHARED; 
      O     #ALARM_C_BOILS; 
      JCN   Tr32; 
      =     #OPEN_PID_R; 
      =     #CLOSE_PID_S; 
      =     #A_RUN_NK_R; 
      =     #RUN_BURNER_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
Tr32: A     #NORMA_BURNER; 
      A     #A_RUN_BOIL; 
      AN    #ALARM_SHARED; 
      AN    #ALARM_BOIL; 
      AN    #ALARM_BURNER; 
      AN    #ALARM_C_BOILS; 
      AN    #NOT_RUN_NK; 
      A     #POWER; 
      JCN   End; 
      =     #OPEN_PID_R; 
      =     #WORK_BOIL_S; 
      =     #RUN_PID_S; 
      L     4; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 4
//1.ALARM_BOIL||ALARM_BURNER||NOT_RUN_NK||!POWER\z:RUN_BURNER_R;
//WORK_BOIL_R;RUN_PID_R;ALARM_BOIL_S
//2.ALARM_SHARED||ALARM_C_BOILS\z:CLOSE_PID_S;A_RUN_NK_R;RUN_BURNER_R;WORK_BOIL_R;
//RUN_PID_R
//3.!NORMA_BURNER&A_RUN_BOIL&!ALARM_BURNER\z:RUN_PID_R;OPEN_PID_S;WORK_BOIL_R;
//RUN_BURNER_R;COOL_BOIL_S;TIM_BOIL_S
//4.!A_RUN_BOIL&!ALARM_BURNER\z:RUN_PID_R;OPEN_PID_S;WORK_BOIL_R;
//RUN_BURNER_R;COOL_BOIL_S;TIM_BOIL_S
ST_4: O     #ALARM_BOIL; 
      O     #ALARM_BURNER; 
      O     #NOT_RUN_NK; 
      ON    #POWER; 
      JCN   Tr41; 
      =     #RUN_BURNER_R; 
      =     #WORK_BOIL_R; 
      =     #RUN_PID_R; 
      =     #ALARM_BOIL_S; 
      L     8; 
      T     #STATE_WORD; 
      JU    End; 
Tr41: O     #ALARM_SHARED; 
      O     #ALARM_C_BOILS; 
      JCN   Tr42; 
      =     #CLOSE_PID_S; 
      =     #A_RUN_NK_R; 
      =     #RUN_BURNER_R; 
      =     #WORK_BOIL_R; 
      =     #RUN_PID_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
Tr42: AN    #NORMA_BURNER; 
      A     #A_RUN_BOIL; 
      AN    #ALARM_BURNER; 
      JCN   Tr43; 
      =     #RUN_PID_R; 
      =     #OPEN_PID_S; 
      =     #WORK_BOIL_R; 
      =     #RUN_BURNER_R; 
      =     #COOL_BOIL_S; 
      =     #TIM_BOIL_S; 
      L     5; 
      T     #STATE_WORD; 
      JU    End; 
Tr43: AN    #A_RUN_BOIL; 
      AN    #ALARM_BURNER; 
      JCN   End; 
      =     #RUN_PID_R; 
      =     #OPEN_PID_S; 
      =     #WORK_BOIL_R; 
      =     #RUN_BURNER_R; 
      =     #COOL_BOIL_S; 
      =     #TIM_BOIL_S; 
      L     5; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 5
//1.!TIM_BOIL&!POWER\z:A_RUN_NK_R;COOL_BOIL_R;TIM_BOIL_R;ALARM_BOIL_S
//2.TIM_BOIL\z:A_RUN_NK_R;COOL_BOIL_R
//3.COOL_RESET&!ALARM_BOIL&!ALARM_BURNER&!NOT_RUN_NK&POWER\z:OPEN_PID_R;CLOSE_PID_
//S;TIM_BOIL_R;A_RUN_NK_R;COOL_BOIL_R
ST_5: AN    #TIM_BOIL; 
      AN    #POWER; 
      JCN   Tr51; 
      =     #A_RUN_NK_R; 
      =     #COOL_BOIL_R; 
      =     #TIM_BOIL_R; 
      =     #ALARM_BOIL_S; 
      L     8; 
      T     #STATE_WORD; 
      JU    End; 
Tr51: A     #TIM_BOIL; 
      JCN   Tr52; 
      =     #A_RUN_NK_R; 
      =     #COOL_BOIL_R; 
      L     6; 
      T     #STATE_WORD; 
      JU    End; 
Tr52: A     #COOL_RESET; 
      AN    #ALARM_BOIL; 
      AN    #ALARM_BURNER; 
      AN    #NOT_RUN_NK; 
      A     #POWER; 
      JCN   End; 
      =     #OPEN_PID_R; 
      =     #CLOSE_PID_S; 
      =     #TIM_BOIL_R; 
      =     #A_RUN_NK_R; 
      =     #COOL_BOIL_R; 
      =     #ALARM_BOIL_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 6
//TIM_BOIL\z:OPEN_PID_R;CLOSE_PID_S;TIM_BOIL_R
ST_6: A     #TIM_BOIL; 
      JCN   End; 
      =     #OPEN_PID_R; 
      =     #CLOSE_PID_S; 
      =     #TIM_BOIL_R; 
      =     #ALARM_BOIL_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 7
//CLOSE_PID
ST_7: A     #CLOSE_PID; 
      JCN   End; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 8
//1.!POWER\z:ALARM_BOIL_R;OPEN_PID_R
//2.(ALARM_BOIL||ALARM_BURNER||NOT_RUN_NK)||(!ALARM_BOIL&!ALARM_BURNER&
//!NOT_RUN_NK)&POWER\z:OPEN_PID_S;COOL_BOIL_S;
//TIM_BOIL_S
ST_8: AN    #POWER; 
      JCN   Tr81; 
      =     #ALARM_BOIL_R; 
      =     #OPEN_PID_R; 
      =     #A_RUN_NK_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
Tr81: A(    ; 
      O     #ALARM_BOIL; 
      O     #ALARM_BURNER; 
      O     #NOT_RUN_NK; 
      )     ; 
      O(    ; 
      AN    #ALARM_BOIL; 
      AN    #ALARM_BURNER; 
      AN    #NOT_RUN_NK; 
      )     ; 
      A     #POWER; 
      JCN   End; 
      =     #OPEN_PID_S; 
      =     #COOL_BOIL_S; 
      =     #TIM_BOIL_S; 
      L     5; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка состояния 9
//1.!ALARM_BURNER&!NOT_RUN_NK&POWER\z:ALARM_BOIL_R
ST_9: AN    #ALARM_BURNER; 
      AN    #NOT_RUN_NK; 
      A     #POWER; 
      JCN   End; 
      =     #ALARM_BOIL_R; 
      L     0; 
      T     #STATE_WORD; 
      JU    End; 
NETWORK
TITLE =Обработка ошибки распределенного перехода

Err:  CLR   ; 
      =     #A_RUN_NK; 
      =     #RUN_PID; 
      =     #OPEN_PID; 
      =     #CLOSE_PID; 
      =     #RUN_BURNER; 
      =     #WORK_BOIL; 
      =     #COOL_BOIL; 
      =     #ALARM_BOIL_1; 
      L     0; //и загрузить число 0
      T     #STATE_WORD; //в слово состояния
      JU    End; //выйти на конец блока
End:  NOP   0; // конец блока  
NETWORK
TITLE =Обработка выходных сигналов

      A     #A_RUN_NK_S; 
      S     #A_RUN_NK; 
      A     #A_RUN_NK_R; 
      R     #A_RUN_NK; 
      NOP   0; 
      A     #RUN_PID_S; 
      S     #RUN_PID; 
      A     #RUN_PID_R; 
      R     #RUN_PID; 
      NOP   0; 
      A     #OPEN_PID_S; 
      S     #OPEN_PID; 
      A     #OPEN_PID_R; 
      R     #OPEN_PID; 
      NOP   0; 
      A     #CLOSE_PID_S; 
      S     #CLOSE_PID; 
      A     #CLOSE_PID_R; 
      R     #CLOSE_PID; 
      NOP   0; 
      A     #RUN_BURNER_S; 
      S     #RUN_BURNER; 
      A     #RUN_BURNER_R; 
      R     #RUN_BURNER; 
      NOP   0; 
      A     #WORK_BOIL_S; 
      S     #WORK_BOIL; 
      A     #WORK_BOIL_R; 
      R     #WORK_BOIL; 
      NOP   0; 
      A     #COOL_BOIL_S; 
      S     #COOL_BOIL; 
      A     #COOL_BOIL_R; 
      R     #COOL_BOIL; 
      NOP   0; 
      A     #ALARM_BOIL_S; 
      S     #ALARM_BOIL_1; 
      A     #ALARM_BOIL_R; 
      R     #ALARM_BOIL_1; 
      NOP   0; 
NETWORK
TITLE =Обработка таймера

      A     #TIM_BOIL_S; 
      L     #TIM_BASE_BOIL; 
      SS    #TIM_NUM_BOIL; 
      A     #TIM_BOIL_R; 
      R     #TIM_NUM_BOIL; 
      A     #TIM_NUM_BOIL; 
      =     #TIM_BOIL; 

END_FUNCTION_BLOCK
1. Ваша нотация понятна. Очень неудобно то, что действия у Вас привязаны не к состояниям, а к переходам. Если Вы измените свой подход на классический, то увидите множество преимуществ. Действия (поведение системы) обычно привязываются к состоянию системы.

2. Допустим это мелочь... А теперь я напишу программу на STL, которая легко конвертится в LAD и которая будет делать то же самое, что и Ваша. Для этого в таблице символов необходимо создать 10 булевых переменных ST_0, ..., ST_9, которые будут соответствовать состояниям системы.

Код: Выделить всё

//Вводную часть кода я опустил, там просто надо покоцать лишнее...

NETWORK 
TITLE =Автоинициализация (возможны другие варианты инициализации)
AN  ST_1        // Если ни одно из состояний не активно, то
AN  ST_2
AN  ST_3
AN  ST_4
AN  ST_5
AN  ST_6
AN  ST_7
AN  ST_8
AN  ST_9
= ST_0           // инициализируем систему (устанавливаем начальное состояние)

NETWORK 
TITLE =Переход 0-1
      A     ST_0                          // Если система находится в состоянии 0
      A     #A_RUN_BOIL;            // и соблюдается условие перехода 0-1, то
      AN    #ALARM_SHARED; 
      AN    #ALARM_BOIL; 
      AN    #ALARM_BURNER; 
      AN    #ALARM_C_BOILS; 
      AN    #NOT_RUN_NK; 
      A     #POWER;  
      S     #OPEN_PID;                // произвести действия
      R     #CLOSE_PID;
      R    ST_0                           // и осуществить
      S    ST_1                           // переход 0-1

NETWORK 
TITLE =Переход 0-7
      A     ST_0                          // Если состояние начальное
      AN    #CLOSE_PID;             // и ПИД не закрыт,
      S     #CLOSE_PID;              // то закрыть ПИД
      R    ST_0                          // и произвести смену состояния с начального
      S    ST_7                          // на седьмое

NETWORK 
TITLE =Переход 0-9
      A     ST_0
      A     #ALARM_BURNER; 
      O     #NOT_RUN_NK; 
      S     #ALARM_BOIL; 
      R    ST_0
      S    ST_9


// <....>

// И таким образом все описываем переходы. Сколько переходов в системе, столько и нетворков получится в итоге.

3. В графе не расставлены приоритеты переходов. Получается, что переход 0-7 может иметь преимущество перед переходом 0-9, хотя я бы прежде обрабатывал аварийные ситуации... Для этого необходимо в условия переходов добавить дополнительные условия.

4. Использование коротких имен сильно облегчит написание и чтение кода. Многие сокращения можно позаимствовать из схем автоматики и т.п.
Аватара пользователя

CHANt
эксперт
эксперт
Сообщения: 1467
Зарегистрирован: 25 июл 2008, 10:25
Имя: Эдуард Владимирович
Страна: СССР
город/регион: Оренбург
Благодарил (а): 46 раз
Поблагодарили: 105 раз

Re: Примеры подхода "Монограф"

Сообщение CHANt »

4. Имена переменных даны такими по требованию заказчика... Это работающий в реальном проекте алгоритм. А так, пробовали и систему кодирования для электростанций Kraftwerk Kennzeichen System (KKS), и другие, но, больше понравился норвежский стандарт Norsok I-005 "System control diagram"/

3. Как раз наоборот, на рисунке графа все дуги пронумерованы (верхняя часть дроби содержит номер дуги), именно в этом порядке они и кодируются в теле программы. И естественно расставлены приоритеты, в первую очередь обрабатывать аварийные ситуации. Многое зависит от технологии и принципов реализации вторичных цепей.

2. Вы видимо никогда не сталкивались с некорректной работой графических языков в Step7...
Изображение
на рисунке выше я привел пример кода, переведенного с FBD на STL. Редактор автоматически подсовывает локальные переменные там где считает нужным и после чего все компилируется...Естественно, в переменной L 10.0 может содержатся любое значение, и хотя в данной части кода этой переменной сначала присваивается значение и только потом считывается, при сильно большом количестве сложных "нетворков", сталкивался с откровенным "ложным срабатыванием". К ложному срабатыванию, также, приводит, например, несанкционированное отключение питания контроллера, в результате чего в DB функционального блока переменные не очищаются, только RAM контроллера, иногда возникает ситуация когда выходная переменная остается установленной в 1, что лишнее... Именно это и подтолкнуло привести нотацию Switch к одному из текстовых языков Step7. Наверное, кажется все столь запутанным, на самом деле все строго и каждый участок кода соответствует каждой линии, каждой "буковке" имеющейся на рисунке графа. И именно поэтому, все внутренние переменные очищаются перед каждым вызовом функции. И обязательно, в ОВ100 (отрабатывающей при запуске контроллера) помещаю загрузку большого числа (больше чем количество состояний), в слово состояния алгоритма (#STATE_WORD) чтобы отработал кусок по метке Err и привел алгоритм к начальному состоянию, с сбросом выходных переменных. Также, не будем забывать, что код в STL занимает меньший размер, чем в графическом языке, что то же важно, когда ресурсы ограничены.

1. Насчет неудобства, не согласен. Осуществить действия в состояниях не трудно, часть необходимого кода (или вложенную функцию) помещается ДО проверок условий переходов. Кстати, по факту, действия выполняются в состоянии до перехода (на выходе из состояния), сам из себя переход (инструкция JL) это просто загрузка указателя номера состояния и переход на номер метки следующего состояния.

P.S. Задачи встречаются разные, и Ваш подход я обязательно применю (но, не в LAD :D ) для соответствующих задач/установок. Хотя, если честно, мне не сколько нужна полемика, сколько хотелось бы увидеть еще варианты автоматного подхода к решению логических задач. Интересно. Но, что-то коллеги проходят мимо темы :( Неужели никто не систематизирует свои решения к какой - либо форме ?
--------------------------------------------------------------------------------------------

Михайло
эксперт
эксперт
Сообщения: 3643
Зарегистрирован: 10 ноя 2009, 04:58
Имя: Толмачев Михаил Алексеевич
город/регион: г. Чехов, МО
Благодарил (а): 8 раз
Поблагодарили: 286 раз

Re: Примеры подхода "Монограф"

Сообщение Михайло »

CHANt писал(а):2. Вы видимо никогда не сталкивались с некорректной работой графических языков в Step7...
Не сталкивался. Я не делаю такие нагромождения. Только если чужая программа когда-нибудь будет...
CHANt писал(а):Наверное, кажется все столь запутанным, на самом деле все строго и каждый участок кода соответствует каждой линии, каждой "буковке" имеющейся на рисунке графа.
Не запутанный. Я достаточно быстро разобрался в Вашей программе. Так же быстро Вы разберетесь в моем коде.
CHANt писал(а):Также, не будем забывать, что код в STL занимает меньший размер, чем в графическом языке, что то же важно, когда ресурсы ограничены.
Достоинство вашего кода в STL перед моим в LAD не в объеме кода, а в быстродействии. Дело в том, что в моем варианте не используются метки и переходы, вместо этого процессор пробегает по всем строчкам подпрограммы циклически. А вот по размеру кода моя программулина будет покороче.

Что касается языка, то мой вариант реализуем на любом языке LAD, FBD, STL, Pascal, C, Java без привлечения навороченных функций. Есть опыт использования функций объектно-ориентированного программирования на высоком языке программирования (Delphi).

****
Есть еще один более изящный вариант построения кода с использованием #STATE_WORD и без маркеров ST_0...ST_9. Также есть интересное предложение по приоритетам переходов. Сегодня вечером, надеюсь, напишу что-нибудь по этому поводу.

Михайло
эксперт
эксперт
Сообщения: 3643
Зарегистрирован: 10 ноя 2009, 04:58
Имя: Толмачев Михаил Алексеевич
город/регион: г. Чехов, МО
Благодарил (а): 8 раз
Поблагодарили: 286 раз

Re: Примеры подхода "Монограф"

Сообщение Михайло »

Михайло писал(а):Есть еще один более изящный вариант построения кода с использованием #STATE_WORD и без маркеров ST_0...ST_9.
:ext_secret: Текущее состояние храним не в битах ST_0...ST_9, а более компактно в слове #STATE_WORD. Таким образом возможна реализация графа с 65536 вершинами. Используем функцию MOVE 0, #STATE_WORD языка LAD, которая в STL записывается как

Код: Выделить всё

           A       I0.0
           JNB    _001
           L       0
           T       #STATE_WORD
_001:   NOP   0
А также функцию CMP.

Недостаток этой реализации состоит в том, что бифуркации в данной системе невозможны. (В относительно простых алгоритмах бифуркации часто могут не использоваться. Кто бы вообще ими пользовался?) :D
Михайло писал(а):Также есть интересное предложение по приоритетам переходов.
Вместо использования в нотации порядкового номера перехода предлагается другой вариант. Например, рассмотрим переходы 4-8, 4-0 и два параллельных перехода 4-5 на Вашем графе.
Переход 4-8: 1. ALARM_BOIL || ALARM_BURNER || NOT_RUN_NK || !POWER
Переход 4-0: 2. ALARM_SHARED || ALARM_C_BOILS
Переход 4-5: 3. !NORMA_BURNER & A_RUN_BOIL & !ALARM_BURNER
Переход 4-5: 4. !A_RUN_BOIL & !ALARM_BURNER

Нужно расставить приоритеты переходов. Как это сделать? Для этого пишем в условиях переходов дополнения:
Переход 4-8: ALARM_BOIL || ALARM_BURNER || NOT_RUN_NK || !POWER
Переход 4-0: ALARM_SHARED || ALARM_C_BOILS & !ALARM_BOIL & !ALARM_BURNER & !NOT_RUN_NK & POWER
Переход 4-5: !NORMA_BURNER & A_RUN_BOIL & !ALARM_BURNER & !ALARM_BOIL & !NOT_RUN_NK & POWER & !ALARM_SHARED & !ALARM_C_BOILS
Переход 4-5: (!A_RUN_BOIL || NORMA_BURNER) & !ALARM_BURNER & !ALARM_BOIL & !NOT_RUN_NK & POWER & !ALARM_SHARED & !ALARM_C_BOILS

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

P.S. Только сейчас увидел у Вас теоретическую ошибку: два параллельных перехода 4-5. Действия при этом выполняются одинаковые. Это означает, что переходы 4-5 можно объединить следующим образом:
Переход 4-5: (!NORMA_BURNER || !A_RUN_BOIL) & !ALARM_BURNER & !ALARM_BOIL & !NOT_RUN_NK & POWER & !ALARM_SHARED & !ALARM_C_BOILS
Аватара пользователя

CHANt
эксперт
эксперт
Сообщения: 1467
Зарегистрирован: 25 июл 2008, 10:25
Имя: Эдуард Владимирович
Страна: СССР
город/регион: Оренбург
Благодарил (а): 46 раз
Поблагодарили: 105 раз

Re: Примеры подхода "Монограф"

Сообщение CHANt »

Михайло писал(а): :ext_secret: Текущее состояние храним не в битах ST_0...ST_9, а более компактно в слове #STATE_WORD. Таким образом возможна реализация графа с 65536 вершинами.
Да, можно и так. Кстати, в случае большого монографа, на верхний уровень можно пересылать значение #STATE_WORD – в СКАДА уже реализовывать визуализацию согласно номера состояния. И для протоколирования работы алгоритма пригодится. Особенно в процессе наладки.
Михайло писал(а): Недостаток этой реализации состоит в том, что бифуркации в данной системе невозможны. (В относительно простых алгоритмах бифуркации часто могут не использоваться. Кто бы вообще ими пользовался?) :D
Можно и без этой системы, для этого есть SFC.
На мой взгляд, не удобно писать программу на одном языке, обычно используешь и LAD, и FBD, и STL/SCL. Каждый имеет свое преимущество в той или иной задаче..Скажем, математические вычисления в LAD очень не наглядны. :)
Михайло писал(а):Также есть интересное предложение по приоритетам переходов.
Тут будут замечания
Михайло писал(а):Переход 4-0: ALARM_SHARED || ALARM_C_BOILS & !ALARM_BOIL & !ALARM_BURNER & !NOT_RUN_NK & POWER
Это дело технологии –если пришел общий сигнал отключения котельной (ALARM_SHARED) или сигнал аварии котлового контура (АLARM_C_BOILS),т.е. от датчиков предельные параметры, то и проверять дальнейшие условия нет необходимости. Надо идти на выключение котла, проверять всю таблицу истинности не нужно.
Михайло писал(а):Переход 4-5: !NORMA_BURNER & A_RUN_BOIL & !ALARM_BURNER & !ALARM_BOIL & !NOT_RUN_NK & POWER & !ALARM_SHARED & !ALARM_C_BOILS
В данной ветке отрабатывался следующий момент – какой-то удод подошел к котлу и тупо отключил его (заводская автоматика безопасности имеет такой ключ). При этом нет аварийной ситуации, контроллер продолжает посылать команду включения, а вот сигнала NORMA_BURNER (горелка в работе) уже нет.
Михайло писал(а):Переход 4-5: (!A_RUN_BOIL || NORMA_BURNER) & !ALARM_BURNER & !ALARM_BOIL & !NOT_RUN_NK & POWER & !ALARM_SHARED & !ALARM_C_BOILS
Тот же случай, дополненные Вами условия не нужны – в данном случае происходит штатный останов котла – снята команда включения A_RUN_BOIL, и нет аварийной ситуации, дальнейшие проверки излишни….

В нотации Switch, приоритеты расставляются по номерам дуг и в коде в строгой последовательности. А Вы предлагаете фактически решать таблицу истинности...
Михайло писал(а):P.S. Только сейчас увидел у Вас теоретическую ошибку: два параллельных перехода 4-5. Действия при этом выполняются одинаковые.

Все верно, только переход 4-5 с индексом 3 появился уже в процессе опытной эксплуатации, когда, вышеупомянутый удод так и поступил, тупо отключил котел :) , для устранения замечания эксплуатации и была дополнительно введена дуга в граф, для заказчика…
--------------------------------------------------------------------------------------------

Михайло
эксперт
эксперт
Сообщения: 3643
Зарегистрирован: 10 ноя 2009, 04:58
Имя: Толмачев Михаил Алексеевич
город/регион: г. Чехов, МО
Благодарил (а): 8 раз
Поблагодарили: 286 раз

Re: Примеры подхода "Монограф"

Сообщение Михайло »

Как видите, переход 4-0 иногда имеет приоритет над переходом 4-8, но нотация у Вас этот факт не отражает.

Ну теперь, если Вы поняли все мои идеи, то остались бифуркации состояний. Но для этого нужно рассматривать несколько графов установки в комплексе.
Аватара пользователя

CHANt
эксперт
эксперт
Сообщения: 1467
Зарегистрирован: 25 июл 2008, 10:25
Имя: Эдуард Владимирович
Страна: СССР
город/регион: Оренбург
Благодарил (а): 46 раз
Поблагодарили: 105 раз

Re: Примеры подхода "Монограф"

Сообщение CHANt »

Михайло писал(а):Как видите, переход 4-0 иногда имеет приоритет над переходом 4-8, но нотация у Вас этот факт не отражает.
Это разная входная информация, входные данные по переходу 4-8 не поступят одновременно с информацией по переходу 4-0. Даже находясь в состояниях 5 и 6, при поступлении общего сигнала аварии (который не рассматривается в этих состояниях), прекращения выполнения режима расхолаживания котла не требуется. При наступлении события по переходу 4-0, функцию состояний 5-6 берет на себя оборудование другого контура, расположенное в другом месте. В общем тут ПБ рулят. Нарушений тут нет. Я могу расписать еще кучу состояний по переходу 4-0, но не вижу смысла и решаю это в другом месте. Это уже мои личные предпочтения при проектировании. В любом случае эти два перехода (4-8, 4-0) в приоритете над некорректными действиями и штатным отключением.
Михайло писал(а):Ну теперь, если Вы поняли все мои идеи, то остались бифуркации состояний. Но для этого нужно рассматривать несколько графов установки в комплексе.
Структура вышестоящей программы.
Изображение
И это только котловой контур, есть еще контур системы отопления, горячего водоснабжения, холодного водоснабжения, подпитки котлового контура...Есть где разгуляться. Я к тому, что без стадии проектирования программы и документирования этой стадии, при написании всего кода в одну кучу (а попадались подобные проекты, где все подряд валили в ОВ1 и месяцами разбирались что к чему) такая каша получается. При структурированном подходе в проектировании программы, и не важно в какой нотации, и стадия создания программы и ПНР проходят гораздо быстрей, да и работы (так как код в строгом шаблоне) проходят по графическому отображения графа, а не в выискивании нужной строчки в коде.
--------------------------------------------------------------------------------------------

Михайло
эксперт
эксперт
Сообщения: 3643
Зарегистрирован: 10 ноя 2009, 04:58
Имя: Толмачев Михаил Алексеевич
город/регион: г. Чехов, МО
Благодарил (а): 8 раз
Поблагодарили: 286 раз

Re: Примеры подхода "Монограф"

Сообщение Михайло »

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

CHANt
эксперт
эксперт
Сообщения: 1467
Зарегистрирован: 25 июл 2008, 10:25
Имя: Эдуард Владимирович
Страна: СССР
город/регион: Оренбург
Благодарил (а): 46 раз
Поблагодарили: 105 раз

Re: Примеры подхода "Монограф"

Сообщение CHANt »

Граф (на трех страницах, и схема входов/выходов)
Описание алгоритмов переключения котлов.vsd

Небольшая часть организации взаимодействия функциональных блоков.
Изображение
--------------------------------------------------------------------------------------------

Михайло
эксперт
эксперт
Сообщения: 3643
Зарегистрирован: 10 ноя 2009, 04:58
Имя: Толмачев Михаил Алексеевич
город/регион: г. Чехов, МО
Благодарил (а): 8 раз
Поблагодарили: 286 раз

Re: Примеры подхода "Монограф"

Сообщение Михайло »

visio открыть не могу... :( Не установлен.

Замолвлю словечко про бифуркацию. Попытаюсь создать представление о ней.

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

Пример. Два привода ПР1 и ПР2 должны начать движение в один момент времени, а остановиться по сигналу от путевых выключателей ВК1 и ВК2. При чем заранее неизвестно, какой из путевых выключателей сработает раньше.
В этом случае алгоритм будет выглядеть следующим образом:
Состояние 0. Приводы работают (включены).
Переход 0-1. Если сработает ВК1, то выключить привод ПР1.
Переход 0-2. Если сработает ВК2, то выключить привод ПР2.
Состояние 1. Включен привод ПР2, привод ПР1 выключен.
Переход 1-3. Если сработает ВК2, то выключить привод ПР2.
Состояние 2. Включен привод ПР1, привод ПР2 выключен.
Переход 2-3. Если сработает ВК1, то выключить привод ПР1.
Состояние 3. Оба привода отключены.

Если бы управление было многозадачным, то алгоритм был бы следующим:
Задача А.
Состояние А0. Привод ПР1 включен.
Переход А0-А1. Если сработал выключатель ВК1, то отключить привод ПР1.
Состояние А1. Привод ПР1 отключен.
Задача Б.
Состояние Б0. Привод ПР2 включен.
Переход Б0-Б1. Если сработал выключатель ВК2, то отключить привод ПР2.
Состояние Б1. Привод ПР2 отключен.

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

Недостатком многозадачного управления является тот факт, что должна существовать надсистема, которая должна управлять задачами А и Б. Управление задачами сводится к управлению запуском задачи и контролю исполнения задачи (как правило, это контроль окончания выполнения задачи).
В настоящее время общепринято решение в виде вложенных графов. Графическая нотация такого решения требует использования дополнительных знаков и правил нотации, что значительно усложняет задачу.
Предлагаемое новое решение ("Монограф") не требует запоминания новых правил нотации, для записи используются стандартные элементы - вершина, переход, условие перехода и т.п. Других особых преимуществ у решения "Монограф" перед вложенными графами нет: объем памяти программы и ее быстродействие приблизительно в обоих случаях одинаково.

О программном счетчике процессора PC.

Некоторым специалистам, которые владеют навыками программирования на машинном языке программирования (ассемблере), наверное известно о существовании программного счетчика-регистра, который присутствует практически в любом процессоре. Этот счетчик представляет собой специализированный регистр, в котором хранится адрес памяти с командой, подлежащей текущему выполнению. При выполнении очередной команды счетчик увеличивается на некоторую величину, переходя таким образом к адресу следующей команды. И так процесс происходит циклически.
Очевидна аналогия между программным счетчиком и состоянием графа, а именно: текущее значение в программном счетчике соответствует состоянию графа.
Программная имитация программного счетчика (который фактически является аппаратным) - это и есть программная реализация графа. Таким же образом решается многозадачное управление: два и более программных счетчика позволяют реализовать многозадачность в графах.
Аватара пользователя

CHANt
эксперт
эксперт
Сообщения: 1467
Зарегистрирован: 25 июл 2008, 10:25
Имя: Эдуард Владимирович
Страна: СССР
город/регион: Оренбург
Благодарил (а): 46 раз
Поблагодарили: 105 раз

Re: Примеры подхода "Монограф"

Сообщение CHANt »

Для полноты первого графа есть смысл предусмотреть переход по состоянию 0-3
Изображение

А если приводов много много штук (десятки)? Более оптимальным будет вариант №2, т.е.реализация задачи А (Б) в обобщенном виде и вызов экземпляра этой программы нужное количество раз. Надстройка позволит произвести активацию нужных автоматов, в случае одного графа, придется перебирать кучу дуг из каждой вершины...
--------------------------------------------------------------------------------------------

Михайло
эксперт
эксперт
Сообщения: 3643
Зарегистрирован: 10 ноя 2009, 04:58
Имя: Толмачев Михаил Алексеевич
город/регион: г. Чехов, МО
Благодарил (а): 8 раз
Поблагодарили: 286 раз

Re: Примеры подхода "Монограф"

Сообщение Михайло »

А теперь представьте, что нужно выполнить не много параллельных задач, а те же две задачи, но более длинные. Ну например, включить пускатель KM1, по конечнику S1 отключить, тут же включить другой пускатель KM2, по другому конечнику S2 его отключить, но при этом выполнять еще параллельную задачу с пускателями KM3, KM4 и конечниками S3, S4...
Аватара пользователя

CHANt
эксперт
эксперт
Сообщения: 1467
Зарегистрирован: 25 июл 2008, 10:25
Имя: Эдуард Владимирович
Страна: СССР
город/регион: Оренбург
Благодарил (а): 46 раз
Поблагодарили: 105 раз

Re: Примеры подхода "Монограф"

Сообщение CHANt »

Есть еще тема для продолжения.
Для текстовых языков (я уже упоминал сайт http://is.ifmo.ru/progeny/ ) есть пара конвертеров с изображения графа в MS Visio в исходник Си, C#, Ассемблер. То есть возможно получить по изображению в Визио исходник.
http://is.ifmo.ru/progeny/visio2switch/
http://is.ifmo.ru/projects/metaauto/
Такой конвертер, как минимум, сократит время написания программы и позволит избежать механических ошибок при наборе кода.
--------------------------------------------------------------------------------------------

Степа
осмотрелся
осмотрелся
Сообщения: 158
Зарегистрирован: 25 окт 2010, 10:30
Имя: Капуста Степан Степанович
Поблагодарили: 7 раз

Re: Примеры подхода "Монограф"

Сообщение Степа »

CHANt писал(а):Такой конвертер, как минимум, сократит время написания программы и позволит избежать механических ошибок при наборе кода.
А потом все это время потеряется при отладке... И еще на порядок больше потратится.
Именно этот конвертер не использовал /нотация, используемая Шалыто, мне кажется очень громоздкой и неудобной, поэтому у меня графы совсем другие/. Но доводилось работать с конвертерами для PIC-контроллеров: пишешь задачку на паскале\си, конвертируешь в асм, компилишь и заливаешь... Для простеньких задачек /ну там лампочкой помигать, управление выходом по состоянию на входе/, которые чаще всего в один проход решаются - написал, компильнул, залил, работает, отдал - здорово время сокращают. В таких задачах "отладка" обычно означает "написание заново".
Но на больших задачах, которые требуют отлова ошибок и содержат несколько итераций "написал-компильнул-залил"..... При отработке проги на эмуляторе находишь место ошибки и начинаешь заниматься мозгоимением: как же должна выглядеть высокоуровневая строка, чтобы вот тут не возникала ошибка /а ошибка чаще всего не в знаке - вместо "плюс", вставлен "минус"/...
Аватара пользователя

CHANt
эксперт
эксперт
Сообщения: 1467
Зарегистрирован: 25 июл 2008, 10:25
Имя: Эдуард Владимирович
Страна: СССР
город/регион: Оренбург
Благодарил (а): 46 раз
Поблагодарили: 105 раз

Re: Примеры подхода "Монограф"

Сообщение CHANt »

Степа писал(а): А потом все это время потеряется при отладке... И еще на порядок больше потратится.
Что-то у Вас совсем какой-то печальный опыт со средами МЭК61131-3. Обычно, если разобрался с технологией, количество состояний графа не изменяется, могут меняться варианты условий или действий в вершинах графов (как у Михайло), либо на дугах графов (как у меня). Распространенные инструментальные среды МЭК61131-3 поддерживают загрузку изменений на "ходу". Здесь, я вел речь о первичном создании FC или FB. Не, я конечно в курсе, что системы типа TM с драйверами под DOS/Linux по другому и не внесешь изменений - останавливай контроллер, компилируй, заливай заново и начинай сначала. Но, мы тут не обсуждали конкретные среды и их возможности, че какашки всякие вспоминать... Обсуждали подходы в проектировании программы, ее структуру.
Степа писал(а):Именно этот конвертер не использовал /нотация, используемая Шалыто, мне кажется очень громоздкой и неудобной, поэтому у меня графы совсем другие/.
Приводите пример Вашего подхода. Посмотрим, поспрашиваем :) Всегда интересно узнать что-то новое.
--------------------------------------------------------------------------------------------
Ответить

Вернуться в «Обсуждение F.A.Q. по PLC»