Загрузка данных


// ===================== Инициализация и мигание =====================
IF BlinkState_05S THEN
    C := NOT(C);
END_IF

// ===================== Аварийный останов =====================
IF Stop OR Flow_alarm OR Pump_fault OR NOT(phase_ok) THEN
    PV.On_chiller := FALSE;
END_IF

Alarm_comp := (pressure_real > 32) OR NOT(Pressure_low) OR NOT(Pressure_up);

// ===================== Насос и контроль протока =====================
IF PV.On_chiller THEN
    On_sw_chiller := "СТОП";
    Pump_on := TRUE;
ELSE
    On_sw_chiller := "ПУСК";
    Pump_on := FALSE;
END_IF

Time_pump(IN := Pump_on, PT := PV.Pump_time);
IF Time_pump.Q THEN
    Flow_alarm := NOT(Flow);
    Time_pump(IN := FALSE);
END_IF
IF NOT Pump_on THEN
    Flow_alarm := FALSE;   // автоматический сброс аварии протока при останове насоса
END_IF

// ===================== ПИД водяного контура (всегда активен) =====================
Water_PID(
    ACT  := temp_in,
    SET  := Pv.temp,
    SUP  := 0.1,
    OFS  := 0,
    M_I  := ,             // или FALSE
    MAN  := ,             // или FALSE
    RST  := NOT(PV.On_chiller),
    KP   := Water_KP,
    TN   := Water_KI,
    TV   := Water_KD,
    LL   := Water_LL,
    LH   := Water_LH,
    Y    => Water_PI_out,
    DIFF => ,
    LIM  => Water_LIM
);
Water_PI_real_out := Water_LH - Water_PI_out;

// Постоянные времени (доступны всегда)
Pump1_time := PV.Comp_on_time;
Pump2_time := PV.Comp_on_time;   // при необходимости заменить на отдельную уставку

// ===================== Формирование команд Comp_x_on =====================
IF PV.On_chiller THEN
    IF NOT(Alarm_comp) THEN
        // Гистерезисное определение требуемого числа компрессоров (req_comp)
        CASE req_comp OF
            0:  // сейчас 0
                IF Water_PI_real_out > Hyst_On_1comp THEN
                    req_comp := 1;
                END_IF
            1:  // сейчас 1
                IF Water_PI_real_out < Hyst_Off_1comp THEN
                    req_comp := 0;
                ELSIF Water_PI_real_out > Hyst_On_2comp THEN
                    req_comp := 2;
                END_IF
            2:  // сейчас 2
                IF Water_PI_real_out < Hyst_Off_2comp THEN
                    req_comp := 1;
                END_IF
        END_CASE

        // --- Определение ведущего (lead) и ведомого (lag) компрессора ---
        IF req_comp = 0 THEN
            lead_comp := 0;
            lag_comp  := 0;
        ELSIF req_comp = 1 THEN
            // Один компрессор: если один уже работает – оставляем его,
            // иначе выбираем с меньшей наработкой
            IF Comp_1_RUN AND NOT Comp_2_RUN THEN
                lead_comp := 1;
            ELSIF NOT Comp_1_RUN AND Comp_2_RUN THEN
                lead_comp := 2;
            ELSE
                // Оба стоят: включить с меньшей наработкой
                IF PV.WorkTime_drill_comp1_MIN <= PV.WorkTime_drill_comp2_MIN THEN
                    lead_comp := 1;
                ELSE
                    lead_comp := 2;
                END_IF
            END_IF
            lag_comp := 0;
        ELSE  // req_comp = 2
            // Если один уже работает – он остаётся ведущим, второй становится ведомым
            // Если работают оба – сохраняем статус-кво (lead=1, lag=2 для определённости)
            // Если оба стоят – выбираем ведущего с меньшей наработкой
            IF Comp_1_RUN AND NOT Comp_2_RUN THEN
                lead_comp := 1;
                lag_comp  := 2;
            ELSIF NOT Comp_1_RUN AND Comp_2_RUN THEN
                lead_comp := 2;
                lag_comp  := 1;
            ELSIF Comp_1_RUN AND Comp_2_RUN THEN
                // Оба уже работают – формально lead=1, lag=2 (не важно)
                lead_comp := 1;
                lag_comp  := 2;
            ELSE
                // Оба выключены – ведущий с меньшей наработкой
                IF PV.WorkTime_drill_comp1_MIN <= PV.WorkTime_drill_comp2_MIN THEN
                    lead_comp := 1;
                    lag_comp  := 2;
                ELSE
                    lead_comp := 2;
                    lag_comp  := 1;
                END_IF
            END_IF
        END_IF

        // --- Таймер задержки пуска ведомого ---
        Delay_2nd_comp(IN := (lag_comp <> 0) AND 
            ((lead_comp = 1 AND Comp_1_RUN) OR (lead_comp = 2 AND Comp_2_RUN)),
            PT := PV.Delay_between_comps);

        // --- Назначение команд Comp_x_on ---
        // Ведущий включается всегда, когда req_comp >= 1
        IF lead_comp = 1 THEN
            Comp_1_on := TRUE;
        ELSE
            Comp_1_on := FALSE;
        END_IF;
        IF lead_comp = 2 THEN
            Comp_2_on := TRUE;
        ELSE
            Comp_2_on := FALSE;
        END_IF;

        // Ведомый включается, когда req_comp = 2 и истекла задержка после пуска ведущего
        IF req_comp = 2 AND lag_comp <> 0 THEN
            IF lag_comp = 1 THEN
                Comp_1_on := Delay_2nd_comp.Q;   // если ведомый – первый, перезаписываем
            ELSE
                Comp_2_on := Delay_2nd_comp.Q;
            END_IF
        END_IF

    ELSE
        // Авария по давлению – снимаем команды
        Comp_1_on := FALSE;
        Comp_2_on := FALSE;
    END_IF

    // Аварийное отключение: немедленный сброс реле, таймеров и задержки
    IF Alarm_comp THEN
        Comp_1_on := FALSE;
        Comp_2_on := FALSE;
        Time_comp_1_RUN(IN := FALSE);
        Time_comp_1_STOP(IN := FALSE);
        Comp_1_RUN := FALSE;
        Time_comp_2_RUN(IN := FALSE);
        Time_comp_2_STOP(IN := FALSE);
        Comp_2_RUN := FALSE;
        Delay_2nd_comp(IN := FALSE);
    END_IF

ELSE
    // Чиллер выключен – полный сброс команд и состояния
    Comp_1_on := FALSE;
    Comp_2_on := FALSE;
    req_comp := 0;
    Delay_2nd_comp(IN := FALSE);
    lead_comp := 0;
    lag_comp  := 0;
END_IF

// ===================== Защитные таймеры (работают всегда) =====================
// Компрессор 1
Time_comp_1_RUN(IN := Comp_1_on, PT := Pump1_time);
IF Time_comp_1_RUN.Q THEN
    Time_comp_1_STOP(IN := FALSE);
    Comp_1_RUN := TRUE;
END_IF

Time_comp_1_STOP(IN := NOT Comp_1_on, PT := PV.Comp_off_time);
IF Time_comp_1_STOP.Q THEN
    Time_comp_1_RUN(IN := FALSE);
    Comp_1_RUN := FALSE;
END_IF

// Компрессор 2
Time_comp_2_RUN(IN := Comp_2_on, PT := Pump2_time);
IF Time_comp_2_RUN.Q THEN
    Time_comp_2_STOP(IN := FALSE);
    Comp_2_RUN := TRUE;
END_IF

Time_comp_2_STOP(IN := NOT Comp_2_on, PT := PV.Comp_off_time);
IF Time_comp_2_STOP.Q THEN
    Time_comp_2_RUN(IN := FALSE);
    Comp_2_RUN := FALSE;
END_IF

// ===================== Управление клапаном =====================
// Открытие: по команде на любой компрессор с опережением Valve_on_time
// Удержание: пока хотя бы один компрессор в RUN
Valve(
    IN := (Comp_1_on OR Comp_2_on) OR (Comp_1_RUN OR Comp_2_RUN),
    PT := PV.Comp_on_time - PV.Valve_on_time   // Valve_on_time = 5 с для опережения
);

// Ручное открытие по кнопке на 3 секунды
Valve_Manual_Timer(IN := Valve_RES, PT := T#3S);
Valve_on := Valve.Q OR Valve_Manual_Timer.Q;

// ===================== Счётчики наработки (всегда) =====================
Time_comp1(IN := Comp_1_RUN, PT := T#1M);
Time_comp1_TRIG(CLK := Time_comp1.Q);
IF Time_comp1_TRIG.Q THEN
    PV.WorkTime_drill_comp1_MIN := PV.WorkTime_drill_comp1_MIN + 1;
    Time_comp1(IN := FALSE);
END_IF
IF PV.WorkTime_drill_comp1_MIN = 1440 THEN
    PV.WorkTime_drill_comp1_D := PV.WorkTime_drill_comp1_D + 1;
    PV.WorkTime_drill_comp1_MIN := 0;
END_IF

Time_comp2(IN := Comp_2_RUN, PT := T#1M);
Time_comp2_TRIG(CLK := Time_comp2.Q);
IF Time_comp2_TRIG.Q THEN
    PV.WorkTime_drill_comp2_MIN := PV.WorkTime_drill_comp2_MIN + 1;
    Time_comp2(IN := FALSE);
END_IF
IF PV.WorkTime_drill_comp2_MIN = 1440 THEN
    PV.WorkTime_drill_comp2_D := PV.WorkTime_drill_comp2_D + 1;
    PV.WorkTime_drill_comp2_MIN := 0;
END_IF

// ===================== ПИД вентиляторов =====================
Vent_PID(
    ACT  := pressure_real,
    SET  := Pv.pressure,
    SUP  := 0.1,
    OFS  := 0,
    M_I  := ,
    MAN  := ,
    RST  := NOT(Comp_1_RUN OR Comp_2_RUN),
    KP   := Vent_KP,
    TN   := Vent_KI,
    TV   := Vent_KD,
    LL   := Vent_LL,
    LH   := Vent_LH,
    Y    => Vent_PI_out,
    DIFF => ,
    LIM  => Vent_LIM
);
Vent_PI_real_out := Vent_LH - Vent_PI_out;
Vent_PI_INT_out  := TO_UINT(Vent_PI_real_out);
Vent_1_on := Comp_1_RUN OR Comp_2_RUN;
Vent_2_on := Comp_1_RUN OR Comp_2_RUN;