Есть ПИД-регулятор. Есть клапан, который управляется дискретными выходами Открыть, Закрыть, с него приходят дискретные Открыто/Закрыто.
Время полного хода клапана - 100 секунд.
Написал впервые что-то на ST, оцените, может кто-то что-то дополнит. Может кому-то понадобится.
Коротко: запоминается предыдущее воздействие, рассчитывается новое как разница предыдущее-новое.
В другой части программы обрабатываю условия, если ПИД выдал 0% или 100%, то выдаю на соответствующие выходы время равное 1,2 полного хода, чтоб клапан точно дошёл.
Как запихнуть подобное условие в этот блок - я не смог разобраться.
А может уже есть готовые кейсы подобного управления и я просто изобрёл велосипед.

Скрытый текст:
PHP код:
FUNCTION_BLOCK ValveControl

VAR_INPUT
    PIDOutput
: REAL;    // 0-100% от PID-регулятора
    
FullTime: REAL;    // Время полного хода клапана в секундах
    
OpenState: BOOL;    // Состояние клапана "Открыт"
    
CloseState: BOOL;    // Состояние клапана "Закрыт"
    
UnixTime: UDINT;     // Текущее время в Unix формате (секунды)
END_VAR

VAR_OUTPUT
    OpenOutput
: BOOL;    // Выход "Открыть"
    
CloseOutput: BOOL;    // Выход "Закрыть"
    
OpenTime: REAL;    // Рассчитанное время открытия (секунды)
    
CloseTime: REAL;   // Рассчитанное время закрытия (секунды)
END_VAR

VAR
    
PreviousOutput: REAL;    // Предыдущее значение сигнала PID
    
OpenTimeCounter: REAL;         // Счетчик времени открытия
    
CloseTimeCounter: REAL;       // Счетчик времени закрытия
    
OpenTimerStarted: BOOL;     // Флаг запуска таймера открытия
    
CloseTimerStarted: BOOL;    // Флаг запуска таймера закрытия
    
Initialized: BOOL;     // Флаг инициализации 
    
LastOpenTime: UDINT; // Время начала таймера открытия
    
LastCloseTime: UDINT; // Время начала таймера закрытия
END_VAR

// Инициализация переменных при первом вызове функции
IF NOT Initialized THEN
    PreviousOutput 
:= 0;
    
OpenTimeCounter := 0;
    
CloseTimeCounter := 0;
    
OpenTimerStarted := FALSE;
    
CloseTimerStarted := FALSE;
    
Initialized := TRUE;
END_IF

// Проверка состояния клапана
IF OpenState THEN
    OpenTimeCounter 
:= 0; // Сбрасываем счетчик времени открытия
    
OpenTimerStarted := FALSE;
    
OpenOutput := FALSE; // Сбрасываем выход "Открыть"
ELSIF CloseState THEN
    CloseTimeCounter 
:= 0; // Сбрасываем счетчик времени закрытия
    
CloseTimerStarted := FALSE;
    
CloseOutput := FALSE; // Сбрасываем выход "Закрыть"
END_IF

// Вычисление времени удержания выхода
IF PIDOutput > PreviousOutput THEN
    OpenTimeCounter 
:= OpenTimeCounter + (PIDOutput - PreviousOutput) / 100 * FullTime;
ELSIF PIDOutput < PreviousOutput THEN
    CloseTimeCounter 
:= CloseTimeCounter + (PreviousOutput - PIDOutput) / 100 * FullTime;
END_IF

// Установка выхода "Открыть"
IF OpenTimeCounter > 0 AND NOT OpenTimerStarted THEN
    OpenTimerStarted 
:= TRUE;
    
LastOpenTime := UnixTime; // Запоминаем начальное время
    
OpenOutput := TRUE;
END_IF

// Проверка таймера открытия
IF OpenTimerStarted THEN
    
// Сбрасываем выход "Открыть" только если таймер сработал
    
IF UnixTime - LastOpenTime >= REAL_TO_UDINT(OpenTimeCounter) THEN 
        OpenOutput 
:= FALSE;
        
OpenTimerStarted := FALSE;
        
OpenTimeCounter := 0;
    
END_IF

    
// Сбрасываем выход "Открыть" если клапан уже открыт
    
IF OpenState THEN
        OpenOutput 
:= FALSE;
        
OpenTimerStarted := FALSE;
        
OpenTimeCounter := 0;
    
END_IF
END_IF

// Установка выхода "Закрыть"
IF CloseTimeCounter > 0 AND NOT CloseTimerStarted THEN
    CloseTimerStarted 
:= TRUE;
    
LastCloseTime := UnixTime; // Запоминаем начальное время
    
CloseOutput := TRUE;
END_IF

// Проверка таймера закрытия
IF CloseTimerStarted THEN
    
// Сбрасываем выход "Закрыть" только если таймер сработал
    
IF UnixTime - LastCloseTime >= REAL_TO_UDINT(CloseTimeCounter) THEN
        CloseOutput 
:= FALSE;
        
CloseTimerStarted := FALSE;
        
CloseTimeCounter := 0;
    
END_IF

    
// Сбрасываем выход "Закрыть" если клапан уже закрыт
    
IF CloseState THEN
        CloseOutput 
:= FALSE;
        
CloseTimerStarted := FALSE;
        
CloseTimeCounter := 0;
    
END_IF
END_IF

// Сохранение текущего значения сигнала PID
PreviousOutput := PIDOutput;

// Установка выходных значений OpenTime и CloseTime
OpenTime := OpenTimeCounter; 
CloseTime := CloseTimeCounter; 

END_FUNCTION_BLOCK