Работа со структурами, массивами и циклами

Задача: Создать программу для диагностики работы поршневых исполнительных механизмов с двумя датчиками концевого положения штока.
Уточним условие задачи. Исполнительные механизмы (ИМ) управляются сигналом с ПЛК одним дискретным выходом (TRUE=открыть, FALSE=закрыть), а для обратной связи по положению на ПЛК приходит состояние 2-х датчиков концевого положения штоков: датчик полного открытия и датчик полного закрытия. Если после команды открытия через заданное время не сработал концевик полного открытия (или включен концевик закрытия), то должен сработать бит тревоги открытия. То же самое касается закрытия: если после отключения команды открытия через заданное время не сработал концевой датчик закрытия (или включен датчик открытия) - должен сработать бит тревоги закрытия. Биты тревоги сбрасываются при активном бите подтверждения. Заданное допустимое время закрытия и открытия одинаковы. 
Концепция.  
Если подобных исполнительных механизмов в программе должно быть много - есть смысл разработать для них структурный тип, на основании которого создать массив переменных, а потом их обрабатывать в одной подпрограмме с использованием циклов.  Второй вариант - создать производный функциональный блок (DFB) с готовой логикой обработки. Второй вариант может быть предпочтительным, если каждая переменная должна иметь уникальное имя, или компоненты структуры являются независимыми переменными. Поскольку этот пример является учебным, будем идти по первому варианту.   
Создание структурного типа.
В UNITY структурные типы принято называть производными типами данных (DDT, Derived Data Type). Создадим DDT с именем VALVE_D и со следующими полями:
ALRM_OPN - бит тревоги открытия, ALARM_CLS - бит тревоги закрытия, CMD_OPN - команда на открытие, GS_CLS -концевик закрытия, GS_OPN - концевик открытия, ALRM_ACK - команда квитирования, TimeV - текущее значение времени открытия/закрытия (в секундах), TimeSP - заданное максимальное время открытия/закрытия. О переменной CMD_PREV немного попозже.   
 
Программа для индивидуального подхода.  
Для единичного случая диагностики ИМ, то есть без работы с массивами, или для случая использования кода в DFB, можно использовать язык LD, так как для этой задачи наиболее подходящий (хотя тут у каждого своё мнение). Создаём переменную VA1 с типом VALVE_D, и в поле значение для TimeSP пропишем TimeSP=5, тем самым зададим максимальное время открытия/закрытия равным 5 секунд.
Ниже приведена программа на LD. Обратите внимание, что в программе вобще не используются таймеры. Вместо них, используется системный бит %S6 (секундный меандр), по переднему фронту которого увеличивается на 1 (функция INC) значение текущего времени TimeV. Учитывая, что это значения сохраняется для каждой переменной клапана, этот подход менее ресурсоёмкий и в некотором смысле даже удобнее.   
 
 
Таким образом, текущее значение TimeV всегда увеличивается, а при нормальной комбинации состояния концевиков и команды на клапан - обнуляется.   
Например, при открытии клапана (CMD_OPN:=TRUE), при TimeV>TimeSP срабатывает ALRM_OPN. Отключение бита ALRM_OPN произойдет в момент команды квитирования (ALRM_ACK=TRUE). Аналогично работает программа на закрытие. 
Значение TimeV обнуляется при срабатывании/отключении соответствующих концевиков, а также при смене направления открытие<->закрытие. Последнее нужно для предотвращения срабатывания обеих тревог при срабатывании хотя бы одной из них.  Для отлова момента начала закрытия и открытия нужно отловить фронты переменной ALRM_OPN. Контакты для отлова фронтов (-|P|-) и (-|N|-) работают только с переменными типа EBOOL, по этому в программе фронты ловятся классическим подходом: используем переменную CMD_PREV для сохранения предыдущего значения команды; сравниваем текущее и предыдущее значение.   Комбинация из разных состояний CMD_PREV и CMD_OPN для отлова одного из фронтов можно решить функцией XOR, но в этом примере оставим решение в таком варианте.
Создания массива.
Для групповой диагностики ИМ по наведенному высше алгоритму есть смысл создать массив из элементов типа VALVE_D. То есть каждый элемент массива будет отвечать за какой то ИМ. Объявляется этот массив в DataEditor как и обычные переменные. В примере используются 10 ИМ, массив будет выглядеть следующим образом.  
Для каждого элемента массива задаются свои значения уставок TimeSP.
Программа для групповой обработки.
Для групповой обработки с использованием массива есть смысл задействовать циклы. Для таких задач наиболее подходят языки IL и ST. Программа на ST может выглядеть следующим образом.
Приведенный листинг в основном повторяет логику программы, написанную на LD, немного ужатую для экономии ресурсов. Однако есть некоторые особенности.
В современных Modicon ячейки %S являются типа BOOL, хотя в ранних версиях они считались типа EBOOL. Мало того, замечено, что в новых версиях UNITY PRO, в LD контакты -|P|- и -|N|- нормально отлавливают фронты даже на переменных типа BOOL, хотя в справочной системе это не написано. Правда так себя ведет симулятор ПЛК, на реальных ПЛК с новой ОС проверить не было возможности. Однако функции RE и FE, которые предназначенные для отлова фронтов работают только с переменными типа EBOOL. По этому, в начале программы идет переприсвоение %S6 в ячейку %M6 типа EBOOL. Адрес ячейки выбран произвольно и вместо неё можно использовать нелокализированную переменную типа EBOOL.
Следующей особенностью является использования функции XOR для отлова разности предыдущего и текущего значений команд на клапан, что означает передний либо задний фронт сигнала.
Всё остальное аналогично предыдущему примеру. Как видите, объем программы совсем не зависит от количества ИМ. Однако следует отметить, что отлаживать код с использованием циклов намного труднее, чем без них, особенно когда нет возможности вставлять точки останова, приводящие к останову программы. По этому, желательно отладить программу на одном цикле (как в примере с одним клапаном) а потом "обернуть" её инструкциями цикла. 
 
Comments