Написал код для ворот. Проверить на реальном объекте на текущий момент не могу - надо там кое-что по "физике" допилить. Попинайте код кому не сложно:
Код:
FUNCTION_BLOCK TSH_Portal //префикс: tshp
VAR_INPUT
xEnabled: BOOL:=TRUE; //Включение/выключение управляющего блока ворот
xButtonEnabled: BOOL:=FALSE; //Включение/выключение кнопки управления воротами на столбе
xManual: BOOL:=FALSE; //флаг режима ручного управления (чтобы можно было самому дергать релюшки в щите)
xRepeatCommand: BOOL:=FALSE; //повторять ли команду, если ворота не закрылись через установленное время
xgpioIsOpen: BOOL:=FALSE; //вход от концевика "открыто"
xgpioIsClosed: BOOL:=FALSE; //вход от концевика "закрыто"
timMovingDutarion: TIME:=T#35S; //время движения ворот между конечными положениями
timCommandImpulseDuration: TIME:=T#500MS; //длительность импульса команды открывания/закрывания ворот
timCommandRepeatTime: TIME:=T#150S; //время ожидания между повторной попыткой открытия/закрытия, если в предыдущий раз не получилось (препятствие и тд)
timNotClosedLimit: TIME:=T#5M; //максимальная допустимая продолжительность нахождения ворот в не закрытом состоянии
END_VAR
VAR_IN_OUT
xOpen: BOOL:=FALSE; //команда "открыть"
xClose: BOOL:=FALSE; //команда "закрыть"
END_VAR
VAR_OUTPUT
xgpioEnabled: BOOL; //выход на реле "вкл/выкл"
xgpioButtonEnabled: BOOL; //выход на реле "вкл/выкл кнопки управления воротами"
xgpioOpen: BOOL; //выход на реле сигнала команды "открыть"
xgpioClose: BOOL; //выход на реле сигнала команды "закрыть"
eState: TSH_PortalStates; //состояние ворот
xerrLimitSwitches: BOOL; //ошибка концевиков - оба показывают, что замкнуты
xerrForbidden: BOOL; //запрещенная операция (одновременная подача команд октрыто и закрыто)
xerrNoAction: BOOL; //нет реакции на команду
xerrObstacle: BOOL; //препятствие в створе ворот
xerrNotClosedLimitExceed: BOOL; //слишком долго открыты
timNotClosed: TIME; //количество времени, которое ворота не закрыты
END_VAR
VAR
//сигнал концевика "закрыто"
ftrigIsClosed: F_TRIG; //триггер спада
rtrigIsClosed: R_TRIG; //триггер фронта
xRaiseIsClosed: BOOL; //флаг фронта
xFadeIsClosed: BOOL; //флаг спада
//сигнал концевика "открыто"
rtrigIsOpen: R_TRIG; //триггер фронта
ftrigIsOpen: F_TRIG; //триггер спада
xRaiseIsOpen: BOOL; //флаг фронта
xFadeIsOpen: BOOL; //флаг спада
//команды
rtrigOpen: R_TRIG; //триггер фронта команды Open
rtrigClose: R_TRIG; //триггер фронта команды Close
xRaiseOpen: BOOL; //флаг фронта сигнала "открыть"
xRaiseClose: BOOL; //флаг фронта сигнала "закрыть"
xLastCommandIsOpen: BOOL; //какая была последняя команда: true - открытие
//таймеры
tpCMDImpulse: TP; //импульс команды
tpMoving: TP; //перемещение ворот между конечными положениями
tpCMDRepeat: TP; //пауза между повторной отправкой команды, если ворота в ожидаемое время не пришли в конечное положение
timStartMoving: TIME; //время начала движения ворот
END_VAR
xgpioEnabled:=xEnabled;
//если ворота выключены, то остальные сигналы также false
xgpioButtonEnabled:=xButtonEnabled AND xEnabled;
xOpen:=xOpen AND xEnabled AND NOT xManual;
xClose:=xClose AND xEnabled AND NOT xManual;
//******ОБРАБОТКА КОМАНД******
//определяем наличие фронтов сигналов команд
rtrigOpen(CLK:=xOpen, Q=>xRaiseOpen);
rtrigClose(CLK:=xClose, Q=>xRaiseClose);
IF xRaiseOpen AND xRaiseClose THEN //два фронта одновременно - ошибка
xerrForbidden:=TRUE;
xRaiseOpen:=FALSE;
xRaiseClose:=FALSE;
xOpen:=FALSE;
xClose:=FALSE;
END_IF
//Если вторая команда пришла во время импульса первой, отменить первую запустить вторую
IF xOpen AND xClose AND (xRaiseClose XOR xRaiseOpen) THEN
//актуализируем активную команду
xOpen:=xRaiseOpen;
xClose:=xRaiseClose;
//сбрасываем таймеры
tpCMDImpulse(IN:=FALSE, PT:=T#0S);
tpMoving(IN:=FALSE, PT:=T#0S);
tpCMDRepeat(IN:=FALSE, PT:=T#0S);
END_IF
tpCMDImpulse(IN:=xRaiseOpen XOR xRaiseClose, PT:=timCommandImpulseDuration); //если прошла команда, запускаем таймер длительности импульса команды
xOpen:=xOpen AND tpCMDImpulse.Q; //приводим импульс команды на входе ФБ в соответствие реальному импульсу
xClose:=xClose AND tpCMDImpulse.Q; //приводим импульс команды на входе ФБ в соответствие реальному импульсу
xgpioOpen:=xOpen;
xgpioClose:=xClose;
//запоминаем последнюю команду (для того, чтобы ее повторить, если потребуется)
IF xOpen THEN
xLastCommandIsOpen:=TRUE;
ELSIF xClose THEN
xLastCommandIsOpen:=FALSE;
END_IF
//******ОБРАБОТКА КОМАНД******
//******ОБРАБОТКА СИГНАЛОВ КОНЦЕВИКОВ******
xerrLimitSwitches:=xgpioIsOpen AND xgpioIsClosed; //оба концевика включены - ошибка
//Определение изменений состояний концевиков
rtrigIsOpen(CLK:=xgpioIsOpen, Q=>xRaiseIsOpen);
ftrigIsOpen(CLK:=xgpioIsOpen, Q=>xFadeIsOpen);
rtrigIsOpen(CLK:=xgpioIsClosed, Q=>xRaiseIsClosed);
ftrigIsOpen(CLK:=xgpioIsClosed, Q=>xFadeIsClosed);
//обработка начала движения (по спаду сигнала концевика)
IF xFadeIsOpen OR xFadeIsClosed THEN
timStartMoving:=TIME();
tpMoving(IN:=xEnabled, PT:=2*timMovingDutarion); //запускаем таймер длительности перемещения ворот между конечными положениями, на двойное время, т.к. часто в процессе движения напрвление движения меняется через другие каналы (кнопка, пульт)
END_IF
timNotClosed:=SEL(NOT xgpioIsOpen AND NOT xgpioIsClosed, 0, TIME()-timStartMoving);
xerrNotClosedLimitExceed:=(timNotClosed>timNotClosedLimit);
xerrNotClosedLimitExceed:=(timNotClosed>timNotClosedLimit);
//обработка прихода ворот в конечное положение
IF xRaiseIsOpen OR xRaiseIsClosed THEN
//сбрасываем таймеры
tpCMDImpulse(IN:=FALSE, PT:=T#0S);
tpMoving(IN:=FALSE, PT:=T#0S);
tpCMDRepeat(IN:=FALSE, PT:=T#0S);
//сбрасываем сигналы команд
xOpen:=FALSE;
xClose:=FALSE;
//и ошибки "препятствие в створе" и предела времени в незакрытом состоянии
xerrObstacle:=FALSE;
xerrNotClosedLimitExceed:=FALSE;
END_IF
//импульс команды закончился, а ворота так и не сдвинулись с места - ошибка
//выражение идет после обработки прихода ворот в конечное положение, т.к. за время
//импульса они могли начать движение и успеть вернуться обратно, что не является ошибкой
xerrNoAction:=tpCMDImpulse.IN AND (tpCMDImpulse.ET=tpCMDImpulse.PT) AND (xgpioIsOpen OR xgpioIsClosed);
//******ОБРАБОТКА СИГНАЛОВ КОНЦЕВИКОВ******
//******ОБРАБОТКА ТАЙМЕРОВ ******
//ожидаемое время движения ворот между конечными положениями истекло дважды
IF tpMoving.IN AND tpMoving.ET=tpMoving.PT THEN
xerrObstacle:=TRUE; //считается, что обнаружено препятствие
tpCMDRepeat(IN:=xRepeatCommand AND xEnabled, PT:=timCommandRepeatTime); //запускаем таймер ожидания перед повторной отправкой команды
tpMoving(IN:=FALSE, PT:=T#0S); //сбрасываем таймер движения
END_IF
//время ожидания перед повторной отправкой команды истекло
IF tpCMDRepeat.IN AND tpCMDRepeat.ET=tpCMDRepeat.PT THEN
xOpen:=xLastCommandIsOpen;
xClose:=NOT xLastCommandIsOpen;
//сбрасываем таймер
tpCMDRepeat(IN:=FALSE, PT:=T#0S);
END_IF
//******ОБРАБОТКА ТАЙМЕРОВ******
//Устанавливаем состояние ворот
IF xgpioIsOpen THEN
eState:=TSH_PortalStates.tshpsIsOpen;
ELSIF xgpioIsClosed THEN
eState:=TSH_PortalStates.tshpsIsClosed;
ELSE
eState:=TSH_PortalStates.tshpsInBetween;
END_IF