Штабелер – устройство, расположенное после гильотины. Для отражения своего состояния он может иметь один или два датчика. Таких состояний обычно два - закрытое и открытое. Находясь в исходном состоянии - закрытом, штабелер принимает лист металла и затем - в открытом состоянии сбрасывает его в накопительное устройство. После этого возвращается в исходное состояние. Мы рассмотрим штабелер, содержащий один датчик. Для правильной трактовки текущего состояния штабелера нужно в ручном режиме установить его в исходное состояние и далее вести отсчет состояний уже от него.
Запуск и режимы работы штабелера
Система управления линией профилирования металла поддерживает три базовых режима - ручной, полуавтоматический и автоматический. В программе им соответствуют реле - M9, M10, M11. Штабелер имеет всего два режима работы, названных далее ручным и автоматическим. В ручном режиме работы системы он работает соответственно в ручном режиме, а в остальных - в автоматическом.
В ручном режиме штабелер управляется только кнопкой "Штабелер", при каждом нажатии которой он отрабатывает половину своего цикла работы, поочередно переходя при каждом нажатии кнопки из одного состояния в другое. В автоматическом режиме штабелер отрабатывает полный цикл. В этом случае он принимает лист, находясь в исходном состоянии, затем сбрасывает его на приемный стол и после этого возвращается в исходное состояние, где ожидает поступление очередного листа.
На рис.1 представлен код, который запускает в работу штабелер, устанавливая единичное значение флагу bПускШтабелера. При этом текущий режим ему задается флагом bРежимШтабелера, нулевое значение которого определяет ручной режим работы, единичное - автоматический.
![Рис. 1. Запуск и режим работы штабелера Рис. 1. Запуск и режим работы штабелера](https://habrastorage.org/getpro/habr/upload_files/f40/491/354/f40491354e98ea07452b7bd9312d29f3.png)
В режиме системы управления «Автомат» запуск штабелера должен осуществляться автоматически после срабатывания гильотины. Реализует такую последовательность код на рис. 2, где флаг M44 отражает текущее состояние гильотины: когда он равен единице, гильотина находится в рабочем состоянии. Функциональный блок ЗапускШтабелера определяет выполняется ли прокат и если это так, то, дожидавшись единичного состояния флага M44 (гильотина начала работу), а после этого - нулевого (гильотина завершила работу), выдает сигнал штабелеру, установив единичное значение флагу bПускОтГильотины (см. также рис. 1). Но, заметим, разрешение на формирование этого сигнала выдает флаг bРаботаОтГильотины.
![Рис. 2. Запуск и штабелера от гильотины Рис. 2. Запуск и штабелера от гильотины](https://habrastorage.org/getpro/habr/upload_files/274/7cf/8cf/2747cf8cfeac20f3242a18cd38ebf780.png)
На рис. 3 представлена схема, которая содержит функциональные блоки управления непосредственно штабелером. Функциональный блок «Датчик» работает напрямую с датчиком штабелера, реализуя простейший фильтр ложных срабатываний датчика (тот же дребезг контактов). Алгоритм работы данного ФБ в форме автоматного графа показан на рис. 5. Текущее состояние автомата, равное 0, соответствует нулевому значению датчика, а равное 1 - единичному (см. также формирование сигнала состояния датчика штабелера - bSensor на рис. 4). Состояние 2 – промежуточное. В нем ожидается подтверждение того или иного значения датчика. Время этого ожидания (задержки ответа датчика) определяется настройками.
![Рис. 4. Формирование сигнала bSensor от датчика штабелера Рис. 4. Формирование сигнала bSensor от датчика штабелера](https://habrastorage.org/getpro/habr/upload_files/d7a/3e2/aba/d7a3e2abac9e89c09d42c8bd061e9e37.png)
Функциональный блок Штабелер1ДБУ реализует алгоритм управления. Он в форме автоматного графа представлен на рис. 6. Номера состояний автомата можно наблюдать на выходе ФБ и тем самым судить о работе системы управления штабелером.
Рис. 5. Алгоритм работы с датчиком
![](https://habrastorage.org/getpro/habr/upload_files/dbf/5a1/d78/dbf5a1d783705089cdb517c95467cd5f.jpg)
Рис. 6. Алгоритм управления штабелером
![](https://habrastorage.org/getpro/habr/upload_files/9f7/037/a22/9f7037a2278c019626b3e7649696aaf4.jpg)
Выводы
Выше мы рассмотрели лишь малую часть реального проекта на ПЛК (в проекте подобных – почти 40 подобных автоматных ФБ). Более того, данное описание можно рассматривать и как подход к документированию автоматных программных проектов. И не только на базе ПЛК – любых. Предложенный метод программной реализации автоматов позволяет в полной мере ощутить преимущества параллельного программирования. Все упомянутые выше ФБ это параллельные процессы. Параллельно отслеживается датчик штабелера – это ФБ «Датчик», параллельно анализируются процессы в профилегибочной линии – ФБ «ЗапускШтабелера», параллельно организуется управление штабелером – ФБ «Штабелер1ДБУ». В наши задачи входит создание автоматных алгоритмов ФБ и определить взаимодействие между ними. За корректность взаимной работы программных автоматов будет отвечать реализованная модель параллельных вычислений.
Конечно, не устанем повторять, созданные параллельные процессы существуют в рамках определенной модели параллельного программирования. При этом мерой аппроксимации реальных процессов на вычислительные процессы, реализуемые даже на простейших ПЛК, будет служить длительность дискретного такта сетевой автоматной модели. Это аналогично, например, частотной характеристике какого-нибудь цифрового прибора. Того же осциллографа. Чем меньше длительность дискретного такта модели (в ПЛК это длительность его дискретного такта), тем с большей достоверностью мы сможем судить о модели реальных параллельных процессов, тем более точную можем создать модель. По аналогии, чем выше частота осциллографа, тем точнее он отражает протекание реальных процессов.
PS. Засады ПЛК
Статья была бы закончена много раньше, если бы не возникшая неожиданно проблема. Она, может быть, специфична и характерна, скорее всего, только для ПЛК. Но, на мой взгляд, о ней должны знать программисты. В том числе и те, которые на ПЛК не работают.
Итак. Вдруг, ни с того ни с сего, перестала работать часть проекта, отвечающая за реализацию проката линии. Пришлось отложить все дела и заняться выяснением ее причин. На первый взгляд ошибался ФБ, который успешно работает в составе других проектов. Перестала работать функция паузы, отключающая двигатели линии на какое-то время. Ей соответствовало реле, значение которого поступало на отдельный вход подозреваемого ФБ. И, как убедила отладка, на входе этого блока реле исправно меняло свое значение, отражая состояние паузы, а локальная переменная внутри ФБ, поставленная ей в соответствие, – нет.
Для обычного программиста такая ситуация – нонсенс. Что может повлиять на значение локальной переменной, которая должна и просто обязана отражать значение входного параметра ФБ? Ни что! Но это в обычном программировании. В ПЛК эта аксиома, оказывается, не действует. Почему? – объясню далее…
Начнем немного издалека. По крайней мере, в той среде программирования, которую я использую (ISP Soft), не существует понятия подпрограммы. Поначалу это вызвало недоумение, но с этим пришлось смириться. Нет и нет. Вместо подпрограмм можно использовать процессы - постоянно работающие ФБ, которым, когда это нужно, посылается сигнал запуска. А программный блок, выдавший подобный сигнал, просто ожидает завершение его работы. Автоматы в этом помогают прекрасно: выполнение процесса (после сигнала) – состояние ФБ не равное нулю, завершение работы – исходное состояние равное нулю. Все это прекрасно отработано и даже не на одном проекте.
Но тут другая беда - локальная переменная не отражает состояние входа?! Локальная, ведь?! Отладчика помог выяснить это достаточно быстро. Но как понять, что влияет на ее значение столь фатально? Кто на ее значение может повлиять, если не только сам ФБ? Так думал бы обычный программист, так думал и я. Но, ведь, отладчик явно демонстрирует: входной параметр и локальная переменная живут каждый своей жизнью.
Путем «научного тыка», т.е. постепенным исключения всего, что только можно было исключить, удалось установить, что флаг изменяется еще в одном месте. Это было поправлено и все заработало. Уф!!! Но как обнаруженное изменение (пусть и в другом месте) влияет на локальную переменную внутри некоторого ФБ? Ведь, это нарушает принцип локальности?
Безусловно, можно было бы дальше продолжить «вскрытие» и разобраться окончательно в проблеме, но смысла в этом особого нет. Просто потому, что причина, казалось бы, понятна. В ПЛК нет подпрограмм, но и того, что с ними достаточно тесно связано – локальных переменных. Но, если в отношении первых это ясно из контекста, т.к. о подпрограммах в описании на язык не говорится ни слова, то понятие локальных переменных используется сплошь и рядом. Вот уж, действительно: «Если на клетке слона прочтешь надпись: буйвол – не верь глазам своим».
Но, что интересно, в рамках другой среды программирования – WPL Soft понятие подпрограммы есть. Но у этих подпрограмм нет параметров. Отсюда можно сделать вывод: подпрограммы есть, но стека нет, т.к. именно через него организуется механизм передачи параметров подпрограммам. Другого столь же простого механизма, я просто не знаю.
Итак, итог. Ну, нет подпрограмм и нет. С этим можно смириться. Параллелизм ФБ позволяем это обойти достаточно легко. Кроме того, понятие подпрограмм оно не из реальной жизни. Все, что находится вокруг нас – натуральное и природное обходится без них. Теперь, похоже, я так же, как к подпрограммам, будут относиться и к локальным переменным. Правда, буду все же их использовать. Просто потому, что они в языке есть и они удобны. Хотя случившееся будет уроком. Но, конечно же, пока я работаю на ПЛК и на языке релейных схем. Может, правда, на ПЛК других фирм ситуация иная? Просветите, если не сложно.
Воистину, «Век живи – век учись». Но … что-то мне, вдруг (опять это вдруг?), подсказало, что проблема совсем не в локальных переменных, хотя и они вносят здесь свою «копейку», а в параллельных процессах. Ну, а как же отладчик? А он, вольно или нет, но запутывает или даже, как в нашем случае, искажает реальную картину. Как плохой осциллограф. А потому проблема больше в нас самих и в правильном понимании происходящего. Особенно, когда речь идет о параллельных процессах. Но об этом, если такое случится, в следующий раз.
Комментарии (18)
lws0954 Автор
15.09.2022 19:32Прошу прощения за пропущенный рис.3:
Рис. 3. Функциональные блоки управления штабелером
Astronom71
15.09.2022 20:21В других языках, на других контроллерах, все в порядке с локальными переменными. Очень смущает что у вас не так. Тяжело читать статью из за вашей терминологии, которая немного не соответствует терминологии систем управления с которыми я знаком.
lws0954 Автор
15.09.2022 21:04Да и здесь с локальными переменными все нормально (но в любом случае это не те локальные переменные из обычного языка). Просто впечатление было такое. Причем очень убедительное. Но все же это была в большей степени "вина" отладчика, который так представил этот процесс. А все, конечно, из-за "кривых ручек" и лени. Лени добавить еще один очень простой автомат. Когда я его сделал, то проблема стала более очевидно и более понятной. Поэтому в статье это просто пример того, как легко можно запутать и заставить поверить даже в невозможное. Вот как-то так.
А что с терминологией не так?
AndreyDmitriev
16.09.2022 07:33Здесь даже не с терминологией проблема, а с объяснением того, что же пошло не так - довольно сумбурное изложение, если честно. Вы-то сами в теме, но попробуйте взклянуть на всё это глазами того, кто вашу систему не видел.
удалось установить, что флаг изменяется еще в одном месте.
Я так и не понял, какой флаг - то ли вы на состояние гонки налетели (что в ПЛК вполне возможно), то ли на баг в самом ПЛК (что тоже бывает).
Мы делаем подобную автоматику на двух датчиках обычно (штабелер открыт/штабелер закрыт) и двух управляющих сигналах (открыть штабелер/закрыть штабелер) - сильно упрощает жизнь.
В LD я уже давно не заходил, с тек пор как мы переехали на ПЛК от B&R.
Я тоже всеми руками за автоматы, и мы программируем всё в основном на С/С++, благо Automation Studio это позволяет. Кое-что ещё на ST.
Соответственно всё строится на основе switch структуры, как-то так:switch (currentState) { case ST_IDLE: // do something in the idle state break; case ST_STOP: // do something in the stop state break; // etc... }
lws0954 Автор
16.09.2022 10:50Я так и не понял, какой флаг - то ли вы на состояние гонки налетели (что в ПЛК вполне возможно), то ли на баг в самом ПЛК (что тоже бывает).
Почти согласен. Но только выше шла речь о реле "пауза". Ниже я его обозвал флагом. Это путает. Все дело в спешке. Надо, конечно, вычитывать и быть внимательнее (речь обо мне, естественно).
Я тоже всеми руками за автоматы, и мы программируем всё в основном на С/С++
И это правильно. Только попробуйте реализовать автоматы так, как описано у меня, т.е. не спешить изменять состояние в пределах case, а запоминать его где-то (у меня - массивы), а уж потом делать его текущим. У вас, во-первых, появится параллелизм, а, во-вторых, будет понадежнее. А чтобы было совсем надежно, нужно действия привязывать к состоянию, т.е. по типу автоматов Мура. В этом случае просто исключаются "гонки" в пределах параллельных переходов множества автоматов. Такой прием заменяет собой теневую память для действий.
AndreyDmitriev
16.09.2022 14:59А, тогда ясно, если речь шла о реле "пауза".
С параллелизмом у нас всё хорошо и так - у B&R это работает "из коробки" - есть таски-задачи, которым я даже могу задать различное время цикла, между задачами можно обмениваться данными и так далее. Всё это детерминированно и в реалтайме. Там можно местами к среде разработки придраться, но в общем норм. Мне Automation Studio даже чем-то больше чем TIA Portal нравится (хотя и не всем).lws0954 Автор
16.09.2022 19:13С параллелизмом у нас всё хорошо...
Если это так, то Вы счастливый человек ;) Но, может, все же проверим?
Создайте модель RS-триггера, которую я привел. А к нему для полноты еще и счетчик.
Если получится такой же результат, как у меня, то я соберу все свои статьи (тогда уж статейки :( ), выброшу их на фиг и повинюсь перед хабровчанами за то, что "парил" им мозги. Как Вам такой исход?
Ну, что - попробуем?.
AndreyDmitriev
18.09.2022 10:07У меня дома нет B&R Automation Studio, так как я живу под Линукс, а виртуалку ставить честно говоря немножко лень.
Но мне не очень понятно ваше ТЗ - что именно вы хотите проверить в смысле параллелизма?
В простейшем случае, если я захочу ловить фронты некоторого сигнала и считать их, то я сделаю это буквально парой строчек:if (Signal ^ oldSignal && Signal) Counter++; oldSignal = Signal;
oldSignal это локальная переменная, хранящая предыдущее состояние сигнала. Вот и всё. Какого, собственно, результата вы ожидаете "такого же как у вас"?
dubovcevd
16.09.2022 10:14Подобная "магическая" ошибка у меня возникала после некоректного использования указателя. Проблема была в том, что на вход библиотечной функции был передан адрес переменной не того типа. Функция просто писала 4 байта по указанному адресу, не обращая внимания, что затирала 2 байта следующей переменной. Такие ошибки компилятор отловить не в состоянии. Поиск их в большом проекте, тот еще квест.
К сожелению традиция использовать указатели где угодно, живет и процветает. Для себя сделал вывод, что указатели всегда можно заменить ссылкой или переменной типа VAR_IN_OUT, когда ссылки не доступны. Указатели только при написании универсальных функций, для передачи данных размер которых не известен заранее.
phar
16.09.2022 10:33"Засада" скорее всего связана с тем фактом, что переменная объявленная в глобальной таблице тегов, оказывается важнее локальной с таким же именем внутри FB - чтение и запись идёт в неё, отсюда и возможность изменить вроде бы локальную переменную в другом блоке. Это противоречит привычной картине с областями видимости локальных переменных в других языках программирования, но однажды именно подобное наблюдал в среде разработки под PLC Дельты. Подсказкой может быть, что переменная FB и глобальная разную подсветку имеют.
Yukr
17.09.2022 03:32если нужны подпрограммы, можно использовать те же POU, только с задачей прерывания:
Я так понял - вставить вместо переменнй таймер TMR , с переменной задержкой, если требуется, по условиям работы не подходит?
lws0954 Автор
17.09.2022 17:00Подпрограммы у ПЛК Дельта все же есть (за другие ПЛК не отвечаю). Только параметров они не имеют. Подпрограммы используются достаточно активно, а как выкручиваются с передачей параметров можно подсмотреть, если, например, 1) реализовать любую программу в ISP Soft с использованием ФБ, 2) загрузить программу в ПЛК, а затем 3) считать ее в ПЛК, используя уже WPL Soft (не ISP Soft!). И тайное станет явным. Описывать все достаточно муторно - лучше посмотреть.
amarao
Если бы у каждой переменной был владелец, то при передаче управления в подпрограмму можно было бы указывать к каким значениям она может получить доступ. Дальше просто - в один момент времени менять может только один код. А читать могут сколько угодно, но только если никто не пишет.
Ну, обычные
&
и&mut
, и хороший компилятор, который бы такой фигни не допустил.Но зачем на Rust, если можно продолжать кушать этот вкусный колючий кактус?
select26
PLC - это другой мир. Там ваши стоны просто не поймут.
Релейный язык жив до сих пор не просто так - многие сценарии на нем описать очень просто.
flx0
Сценарии, которые просто описать на релейном языке, не менее просто описать и на любом другом языке. Это ж тупо логическое выражение. Живучесть IEC 61131-3 меня просто поражает. Кому это вообще удобно?
Gutt
Тем, кто привык делать ещё настоящие релейные схемы, которые потом на электромеханических реле реализовывались. Но для тех же Siemens S7 язык релейных схем -- просто один из возможных вариантов написания схем, никто не заставляет им пользоваться.
IBAH_II
Объяснение очень простое. Программисты PLC не утруждают себя обработкой исключений установки, сбитых концевиков, заклинивших моторов итд.
Поиск такой неисправности осуществляется анализом в отладчике. И тут язык LD подходит идеально.
Usul
Ну вот вам реальный пример логического выражения в LD (Step7)
Пример
Конечно, его можно представить в "обычном" языке программирования в виде цепочки операторов &&, || или в виде вложенных блоков if. Но это сильно усложнит чтение и отладку программы.
А вот что можно увидеть в этом примере:
Логическое выражение с дюжиной элементов представлено очень компактно. Сразу видно логические связи между компонентами. Одна из самых типовых задач поддержки - выяснить, почему нажатие кнопки не запускает механизм. При запущенном отладчике вы сразу увидите, какие "контакты" замкнуты, а какие - нет. Кнопка - слева, выход - справа. Сразу видно, где не проходит "сигнал". А теперь представьте, как вы будете отлаживать это же выражение в языке программирования на основе текста.
В логике автоматизации есть свои паттерны и стандартные элементы: "передний фронт", "задержка включения", "триггер" и т.д. В примере элемент -(P)- выделяет передний фронт. Да, это тоже можно реализовать в "обычном" ЯП. Но код будет гораздо менее читаемым. В качестве паттерна здесь же можно увидеть так называемый "подхват": по нажатию кнопки I762.6 логический сигнал на механизм M440.4 "закорачивает" кнопку и удерживает механизм в рабочем состоянии, пока он не доедет до конца. В LD этот паттерн виден с первого взгляда. Чтобы это же увидеть в логическом выражении из &&, II, if, вам нужно будет прочитать его целиком.
В плане "синтаксического сахара", в LD очень легко вывести дополнительную текстовую информацию о каждом элементе. Например, по кнопке мы видим логический номер входа, текстовое описание-комментарий и обозначение кнопки на принципиальной схеме. Можно ли сделать нечто подобное, если вся программа представлена текстом?
Я бы не сказал, что "релейная логика" очень уж удобная. Самый адский недостаток - невозможность использования со стандартным инструментарием типа git. Но достоинства LD-логики **в её области применения** перевешивают недостатки, и никто пока не спешит предоставить достойной алтернативы.
Отмечу также, что, по моим наблюдениям, логика промышленной установки процентов на 80..90 состоит из выражений, подобных приведенному примеру, и "релейная логика" работает в них очень хорошо. Но в некоторых частях программы больше подходит как раз "традиционный" императивный код. Например, без этого сложно обойтись, когда реализуешь коммуникацию с приводами. И конкретно в Step7 тут засада: приходится писать на ассемблероподобном STL. В других реализациях МЭК с этим попроще: есть язык ST. К чему это я? МЭК-языки создавали неглупые люди, и всем тамошним инструментам есть свое применение.