Часть 3, каркас архитектуры
Часть 1, введение
Часть 2, приоритеты и базовые сигналы
Мда, я дико извиняюсь за большое количество опечаток в предыдщих постах. Руки не поспевают за мыслью, а при редактировании потеряется часть оформления регулярно. Не зналь(
Имея на руках пример того, с чем предлагается работать, впору поговорить об архитектуре, которая и будет обеспечивать корректное и стабильное исполнение кода, однако, перед этим сделаем ряд важных оговорок:1) концепт ориентирован на работу с жёстким распределением памяти (Siemens, OMRON CP/CJ series)
2) ввиду пункта 1 внутри одной структуры могут быть переменные которые И читаются, И пишутся, что существенно усложняет их проброс в коммуникацию(особенно тех, которые пишутся И с панели оператора, И из программы, например запуск в ручном режиме и сброс аварий в структуре управления)
3) объём памяти оперативность и для хранения кода, а также мощность CPU–считаются достаточно большими, чтобы не обращать на них внимания, в иных случаях требуется оптимизация кода
4) при работе с контроллерами, имеющими «классическое» распределение памяти, для обеспечения корректного управления моим кодом по ModBus RTU/TCP требуется дополнительная прослойка, которая будет пересобирать структуры в WORD и обратно, когда-нибудь я это исправлю, но явно не сейчас
5) вся концепция рассчитана на применение в обще-промышленной области, где потерять 10-20-30 мс роли не сыграет никакой, если вам нужна более высокая точность – добро пожаловать в чудный мир оптимизации и распределения кусков кода по разным циклам
Ещё одно лирическое отступление
Я осознанно не буду вам рассказывать как работает Промышленный Логический Контроллер (ПЛК), распределение его памяти, времени CPU по задачам системным и пользовательским, типы данных и прочую лабуду – это вы можете узнать на любых вводных курсах от любого производителя железа. Моя задача – показать пример прикладной реализации тех или иных задач.
Кстати, на ардуине в своё время использовал Union, который прекрасно решал проблему компоновки памяти, однако подавляющее большинство сред разработки для промышленного оборудования его или не поддерживают, или имеют кастрированную реализацию.
Генерация импульсов
Начнём с терминологии:
- импульс – это нечто, существующее предельно короткое время, в нашем случае сие ровно 1 цикл контроллера.
Большинство контроллеров предлагает свои системные генераторы импульсов в том или ином виде, но выглядят они следующим образом на примере 1 секунды:
Ёбушки воробушки, наш самописный таймер то проверяет когда сигнал = 1, а он равен 1 в течении 50 циклов, а за это время он насчитает 50 секунд, вместо 1! Лажа какая-то. Мы должны получить следующее:
Для этого есть несколько вариантов, и сначала мы отметём в сторону самый очевидный: мы НЕ будем в каждом таймере проверять фронт системного генератора импульсов, потому что каждая проверка фронта это +1 переменная в памяти ПЛК, а мы не хотим засирать его бесполезным хламом.
Итого остаётся:
1) вызывать системный импульс, проверять его передний или задний фронт и закидывать в pulse_1s;
2) вызвать стандартный таймер TON с длительностью 1 секунда и зациклить на самого себя, при срабатывании таймера взводить pulse_1s;
3) читать системное время и по внутренним часам ПЛК и при смене секунды однократно взводить pulse_1s, потребуется буфер для хранения и сравнения предыдущего времени;
4) прочитать из недр контроллера время предыдущего цикла, насуммировать несчастные наносекунды до 1 секунды и тоже записать в pulse_1s единичку, после чего очистить буфер.
5) ещё что-нибудь на ваше усмотрение.
Мне глубоко фиолетово, каким способом вы получаете в итоге pulse_1s, но вам достаточно получить его корректно 1 раз и все таймеры во всей программе сразу же заработают, причём корректно и, вау, синхронно. Т.е. если у вас в двух местах с разбегом менее 1 секунды начался счёт до 5, то закончится он тоже одновременно. В этом и плюс, и минус. Мы теряем точность. Но так ли она нужна?
Хотите точность до 100 мс? Пишем таймер Time_R для дробных значений генерируем pulse_01s.
Хотите точность до 10 мс? Ну… сделайте отдельный цикл ПЛК с такой частотой и там считайте. Или убедитесь, что ваше время цикла менее 10 мс на всю программу.Обратите внимание, что этот таймер сложнее – мы считаем прогресс времени от 0 до 1,который можно использовать для вспомогательных операций (типа на 30% всего времени моргнуть правой пяткой) и красивого прогресс-бара на экране.
Архитектура
Перейдём к тому, ради чего был затеян данный раздел. Следите за руками…
Где-то там приютилось ещё регулирование, типа ПИДов, но о них как-нибудь в другой раз.На повестке дня вопрос распределения переменных по областям памяти. Что будет глобальным, а что локальным?
К глобальным относится всё, что ранее было названо глобальным, а также туда настоятельно рекомендуется выносить:
- все структуры управления драйверами
- все структуры параметров драйверами
- все структуры состояний драйверами
- общие параметры техпроцесса
- общее управление техпроцессом, типа включить/выключить установку
Из них в раздел энергонезависимой памяти попадают только структуры параметров.
REQ – Request, запрос запуска. Сюда мы из авторежима будем писать команду для устройства, а затем скармливать драйверам в CMD.Start_A.
DONE – состояние устройств, если оно выключено или в аварии то соответствующий флаг =0, если успешно запущен = 1. Это хорошо заходит для минимизации алгоритма авторежима и всяких вспомогательных операций.
А что же тогда попадает в call’ы?Нерадивые любители звонков с автонабором? Увы, их неплохо бы там запереть, но места маловато. Там будут вызваны непосредственно экземпляры драйверов на исполнение со всей обвязкой. На примере уже рассмотренных дискретных входов это выглядит так… опс, в этом проекте их нет, вот вам выхода:
Подробнее
0.5 сек <-----► <----> 0.5 сек ------> время
50 циклов <-----> <---> 50 циклов <-----------> 100 циклов -----> циклы
о 1 цикл циклы >
1 2 3 4 5 € 7 3 S 10 11 12 TYPE Time_R : STRUCT Start :BOOL. Q :BOOL. Pause :BOOL. Reset :BOOL. SP :REÀL, AP :REAL, One :REÀL, Percent :REAL, END_STRUCT END TYPE //вес импульса //прогресс выполнения
FUNCTION drv_Time_R : bool VAR_IN_OUT T :Time_R; END_VAR VAR END VAR // Таймер с произвольной кратностью счёта T.One := 0.1; IF T.Start AND NOT T.Reset THEN // Работа таймера IF T.AP < T.SP THEN IF NOT T.Pause THEN IF sys.pulse_01s THEN T.AP := T.AP + T.One; END_IF; END_IF; T.Q := FALSE; ELSE // Заданное время достигнуто T.Q := TRUE; END_IF; ELSE // Выключение и сброс таймера T.AP := 0.0; T.Q := FALSE; T.Reset := FALSE; END_IF; // Прогресс таймера IF T.AP > 0 AND T.SP > 0 THEN T.Percent := T.AP / T.SP; ELSE T.Percent := 0.0; END IF
1 цикл ПЛК
1 2 3 4 5 € 7 3 з 10 11 12 13 14 15 1€ 17 13 13 20 21 22 23 24 25 2 € 27 23 23 30 31 32 VAR GLOBAL RETAIN //Дискретные входа DIx_cfg ¡ARRAY //Дискретные вы:-:ода D0x_cfg ¡ARRAY //Приводы с ЧП MDx_cfg ¡ARRAY //Пневмоцилиндр >ы VDx_cfg ¡ARRAY [0..10] OF cfg_DI; [0..10] OF cfg_D0; [0..2] OF cfg_MD; [0..4] OF cfg_VD; ENDJVAR [VAR GLOBAL //Сбшее управление All_Block :BOOL; All_Reset All_Sim All_Auto sys second :BOOL; :BOOL; :BOOL; :mt_system; ¡REAL; //Управление устройствами и их состояние REQ :REQ_list; DONE :REQ list; //Дискретные входа DIx_CMD :ARRAY //Дискретные вы:-:ода D0x_CHD :ARRAY //Приводы с ЧП MDx_CHD :ARRAY MDx_state ¡ARRAY //Пневмоцилиндры VDx_CHD ¡ARRAY VDx_state ¡ARRAY end_vaf) [0..10] OF mt_CMD; [0..10] OF mt_CMD; [0..2] OF mt_CMD; [0..2] OF state_MD; [0..4] OF mt_CHD; [0..4] OF state_VD;
FUNCTION_BLOCK call_DO VAR_INPUT END_VAR VAR_OUTPUT END_VAR VAR DOx ¡ARRAY [0..10] OF drv_DO; END VAR //Пневматика //выталкиватель трубы из захвата dev := 0; DOx[dev]( Signal := REQ.KC[0], CHD := DOx_CHD[dev], cfg := DOx_cfg[dev], Result => KC_0_CMD ); //выталкиватель трубы из рольганга dev := 1; DOx[dev]( Signal := REQ.KC[1], CHD := D0x_CHD[dev], cfg := DOx_cfg[dev], Result => KC_1_CMD ); //сдвиг рольганга dev := 2; DOx[dev]( Signal := REQ.KC[2], CHD := D0x_CHD [dev], cfg := DOx_cfg[dev], Result => KC_2_CHD ); //резерв dev := 3; DOx[dev]( Signal := REQ.KC[3], CHD := DOx_CHD [dev], cfg := DOx_cfg[dev], Result => KC_3_CHD );
песочница,АСУ ТП,программирование,geek,Прикольные гаджеты. Научный, инженерный и айтишный юмор,реактор образовательный,длиннопост
Если работаешь с сименсом - могу огромной библиотекой PLC+HMI на LAD поделиться, приближенной к функционалу PCS7 во многом и хорошо отлаженной. На ST баги мелкие каждый проект вылавливаю пока что ^_^