/*
File:        Controller.h
Description: This is a C++ source file containing implementation code for a program or library component.
Rev:         0.1
Created:     nn.03.2026
Author:      [Your Name]
Copyright:   Binder GmbH TUT 2026
Purpose:     [Briefly describe the file's functionality, e.g., "Implements a class for managing user data."]
Notes:       [Any relevant details, such as dependencies, usage examples, or known issues.]
*/

#pragma once

#ifdef BUILD_TARGET_SOM

#if defined(_WIN32)
#define BINDER_SIMULINK_API_EXPORT __declspec(dllexport)
#else
#define BINDER_SIMULINK_API_EXPORT __attribute__((visibility("default")))
#endif
#include "algorithm/ControllerBase.hpp"
#include "asio/SignalConnection.hpp"

#endif

#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>

//UJ: not yet needed, future use
// #include <limits>
// #include <memory>
// #include <vector>

#include <QList>

#include "datamodel.h"
#include "util.h"

const double PI_CONST = 3.14159265358979323846;


// -------------------------
// Regelkern (ST -> C++)
// -------------------------

class PWM {
public:
    PWM(double zyklus_min, double zyklus_max, double initial_zykluszeit = -1.0) {
        Zykluszeit = 0.0;
        Ausgabe = 0.0;
        Zyklus_MAX = zyklus_max;
        Zyklus_MIN = zyklus_min;
    }
    /*(* Puls Pause Generierung Kühlung *)
  Zyklus_Kuehlventil := Zyklus_Kuehlventil + sampling_time;

  IF Ausgabe_Kuehlventil > 0.0 THEN
          Kuehlventil := NOT Status_Untertemperatur;
          Ausgabe_Kuehlventil := Ausgabe_Kuehlventil - sampling_time;
          IF Ausgabe_Kuehlventil < 0.0 THEN
                  Zykluszeit_Kuehlventil := Zykluszeit_Kuehlventil -
  Ausgabe_Kuehlventil * 100.0 / MAX(Stellgrad_Kuehlventil, sampling_time /
  Zykluszeit_Kuehlventil_MAX * 100.0); Ausgabe_Kuehlventil := 0.0; END_IF; ELSE
          Kuehlventil := FALSE;
  END_IF;

  IF Stellgrad_Kuehlventil > sampling_time / Zykluszeit_Kuehlventil_MAX * 100.0
  THEN IF Zyklus_Kuehlventil >= Zykluszeit_Kuehlventil OR Zyklus_Kuehlventil >=
  MAX(2.0 * Zykluszeit_Kuehlventil_MIN, sampling_time * 100.0 /
  Stellgrad_Kuehlventil) THEN Ausgabe_Kuehlventil := MIN(Ausgabe_Kuehlventil +
  Stellgrad_Kuehlventil / 100.0 * Zykluszeit_Kuehlventil_MIN,
  Zykluszeit_Kuehlventil); Zykluszeit_Kuehlventil := Zykluszeit_Kuehlventil_MIN;
                  Zyklus_Kuehlventil := 0.0;
          END_IF;
  END_IF;*/

    bool step(double Stellgrad, double samplingTime) {
        Zyklus += samplingTime;
        bool output_bool;
        if (Ausgabe > 0.0) {
            output_bool = true;
            Ausgabe -= samplingTime;
            if (Ausgabe < 0.0) {
                Zykluszeit -=
                    Ausgabe * 100 / MAX(Stellgrad, samplingTime / Zyklus_MAX * 100.0);
                Ausgabe = 0.0;
            }

        } else {
            output_bool = false;
        }
        if (Stellgrad > samplingTime / Zyklus_MAX * 100.0) {
            if (Zyklus >= Zykluszeit ||
                Zyklus >= MAX(2.0 * Zyklus_MIN, samplingTime * 100.0 / Stellgrad)) {
                Ausgabe = MIN(Ausgabe + Stellgrad / 100.0 * Zyklus_MIN, Zyklus);
                Zykluszeit = Zyklus_MIN;
                Zyklus = 0.0;
            }
        }
        return output_bool;
    }

private:
    double Zyklus = 0.0;
    double Ausgabe = 0.0;
    double Zykluszeit = 0.0;
    double Zyklus_MAX = 0.0;
    double Zyklus_MIN = 0.0;
};



class ClimateAlgorithm {
public:
    struct BinInputs {
        // Eingangswerte aus bool_inXX / real_inXX / reset_flag
        bool reset_flag{};
        bool Sammelalarm_quittiert{};
        bool Tuer_offen{};
        bool Sollwert_Feuchte_aktiv{};
        bool Steuerkontakt_Standby{};
        bool Steuerkontakt_Befeuchtung_aus{};
        bool Steuerkontakt_Entfeuchtung_aus{};
        bool Entleerbehaelter_Oben{};
        bool Wasserkanister_leer{};
        bool Entleerbehaelter_Unten{};
        bool Sammelalarm{};
    } binInput;

    struct AnalogInputs {
        double Istwert_Temperatur{};
        double Sollwert_Temperatur{};
        double Temperaturband{};
        double Istwert_Ueberwachungsregler{};
        double Klasse_Ueberwachungsregler{};
        double Grenzwert_Untertemperatur{};
        double Istwert_Feuchte{};
        double Sollwert_Feuchte{};
        double Feuchteband{};
        double Sollwert_Luefter{};
        double Istwert_Temperatur_Tuer{};
        double Istwert_Temperatur_Verdampferausgang{};
        double Temperatur_Feuchtemodul{};
        double Bandalarm_nach{};
        double Betauungsschutz{};
        double real_in11{}; // du nutzt real_in11 später direkt in alarm_15
    } analogInput;

    struct BinOutputs {
        // bool_outXX
        bool bool_out01{};
        bool bool_out02{};
        bool bool_out03{};
        bool bool_out07{};
        bool bool_out09{};
        bool bool_out11{};
        bool bool_out12{};
        bool bool_out13{};
        bool bool_out14{};
        bool bool_out16{};
        bool bool_out17{};
        bool bool_out18{};
        bool bool_out19{};
        bool bool_out20{};
        bool bool_out21{};
        bool bool_out23{};
    } binOut;

    struct AnalogOutputs {
        // real_outXX (nur die, die du im ST setzt)
        double real_out01{};
        double real_out02{};
        double real_out04{};
        double real_out05{};
        double real_out06{};
        double real_out07{};
        double real_out08{};
        double real_out09{};
        double real_out10{};
        double real_out11{};
        double real_out12{};
        double real_out13{};
        double real_out14{};
        double real_out16{};
        double Stellgrad_Feuchtemodul{}; // ...out17
        double real_out18{};
        double real_out19{};
        double real_out20{};
        double real_out21{};
        double real_out25{};
        double real_out26{};
        double Stellgrad_Heizung{};
        double Stellgrad_Kuehlung{};
        double Stellgrad_Befeuchtung{};
        double Stellgrad_Entfeuchtung{};
        double Counter_Tuer{};
    } analogOut;

    struct Alarm {
        // alarm_01..alarm_15
        bool Startup{};
        bool alarm_01{};
        bool alarm_02{};
        bool alarm_03{};
        bool alarm_04{};
        bool alarm_05{};
        bool alarm_06{};
        bool alarm_07{};
        bool alarm_08{};
        bool alarm_09{};
        bool alarm_10{};
        bool alarm_11{};
        bool alarm_12{};
        bool alarm_13{};
        bool alarm_14{};
        bool alarm_15{};
    } alarmOut;

    ClimateAlgorithm() = default;


void Step(void)
{
    using UTILITY_FUNCTIONS::is_valid;
    using UTILITY_FUNCTIONS::limit;

    bool Wasserkanister_leer        = false;
    double Istwert_Temperatur_Verdampferausgang = analogInput.Istwert_Temperatur_Verdampferausgang;
    double Betauungsschutz      = 50.0;
    double Feuchteband          = 10;
    double Temperaturband       = 5;
    bool reset_flag             = binInput.reset_flag;

    // ---------------------------
    // Eingangswerte (ST Zuweisungen)
    // ---------------------------
    Sammelalarm_quittiert           = binInput.Sammelalarm_quittiert;
    Tuer_offen                      = binInput.Tuer_offen;
    Sollwert_Feuchte_aktiv          = true;
    Steuerkontakt_Standby           = false;
    Steuerkontakt_Befeuchtung_aus   = false;
    Steuerkontakt_Entfeuchtung_aus  = false;
    Entleerbehaelter_Oben           = binInput.Entleerbehaelter_Oben;
    Entleerbehaelter_Unten          = binInput.Entleerbehaelter_Unten;
    Sammelalarm                     = false;
    Istwert_Temperatur              = analogInput.Istwert_Temperatur;
    Istwert_Ueberwachungsregler     = Istwert_Temperatur;
    Sollwert_Temperatur             = analogInput.Sollwert_Temperatur;
    Istwert_Feuchte                 = analogInput.Istwert_Feuchte;
    Sollwert_Feuchte                = analogInput.Sollwert_Feuchte;
    // uj - not yet needed - silence compiler
//        double Sollwert_Luefter = analogInput.Sollwert_Luefter;
    Temperatur_Feuchtemodul     = analogInput.Temperatur_Feuchtemodul;
    Istwert_Temperatur_Tuer         = analogInput.Istwert_Temperatur_Tuer;

    /////////////////////////////////////////////////////////////////////////////
    // Ende Zuweisungen aus ST-Code Zeilen 1 ... 355
    /////////////////////////////////////////////////////////////////////////////


    // Zähler reset_flag
    // -----------------
    if (reset_flag) {
        Counter_reset_flag = 0;
    }
    else {
        Counter_reset_flag += 1;
    }
    Startup_finished = (Counter_reset_flag > static_cast<std::uint32_t>(10 * 4));

    /* -------------------------------------------------------------------------------
     *  Selbsttest
     *  Entfällt hier, da anderer Kontext und Maschine: Findet an anderer Stelle statt!
     *  Gilt evtl. auch für die Zeilen oben : 'Zähler reset_flag'
     */

    /* Quittierung Sammelalarm */
    //    F_TRIG01(Sammelalarm_quittiert);  UJ: todo - kären !

    // ------------------------------------------
    // Verzögerte Abschaltung Regelung bei Türöffnung
    // ------------------------------------------
#if 1
    // f_PostDoorOpeningCutOffDelaed(...)
    if (Tuer_offen || Steuerkontakt_Standby)
    {
        Counter_Regelung_AUS += sampling_time;
    }
    else
    {
        Counter_Regelung_AUS = 0.0;
    }
    Regelung_AUS = (Counter_Regelung_AUS > 60.0);
#else
    // Original ST Code
    // UJ: todo Ist das wirklich äquivalent ?
    (* Verzögerte Abschaltung Regelung bei Türöffnung *)
    TON01(Tuer_offen OR Steuerkontakt_Standby, 60, 2);
    Regelung_AUS := TON01.Q;
#endif

#if 1
    // todo: insert C++ code here
#else
    // UJ:  Enfällt dieser ST Code ?
    (* ------------------------------- *)
    (* Erkennung Typ Befeuchtungsmodul *)
    (* ------------------------------- *)

    IF NOT is_valid(Counter_Typ_Feuchtemodul) THEN
            Counter_Typ_Feuchtemodul := 0.0;
    ELSIF Entleerbehaelter_Unten OR Entleerbehaelter_Oben THEN
        Counter_Typ_Feuchtemodul := MIN(Counter_Typ_Feuchtemodul + sampling_time, 10.0);
    ELSE
        Counter_Typ_Feuchtemodul := MAX(Counter_Typ_Feuchtemodul - sampling_time, -10.0);
    END_IF;
    BDR_Feuchtemodul := Counter_Typ_Feuchtemodul > 5.0;
    BDR_300W_Feuchtemodul := Counter_Typ_Feuchtemodul < -5.0;
#endif


    // -------------------
    // Nachlauf Türöffnung
    // -------------------
    // f_PostDoorOpening(..)
    if (!Startup_finished)
    {
        Counter_Tuer_offen = 0.0;
        Counter_Tuer_geschlossen = 600.0;
    }
    else if (Tuer_offen)
    {
        Counter_Tuer_offen += sampling_time;
        Counter_Tuer_geschlossen = 0.0;
    }
    else
    {
        Counter_Tuer_offen = 0.0;
        Counter_Tuer_geschlossen += sampling_time;
    }

    ///////////////////////////////////////////////////////////////////////////////
    /// UJ: Warum fehlt folgender ST-Code ?
    ///////////////////////////////////////////////////////////////////////////////
    /*

    (* -------------------- *)
    (* LQC Lichtintegration *)
    (* -------------------- *)

    R_TRIG08(get_second(rtc.cdt) > 0);
    IF (R_TRIG08.Q AND Lichtintegral_AN) THEN
        IF (Istwert_UVA > 0.0 AND Istwert_UVA < 100.0) THEN
            Integral_UVA := LIMIT(Integral_UVA + Istwert_UVA / 60.0, 0.0, 99999.0) ;
        END_IF;
        IF (Istwert_VIS > 0.0 AND Istwert_VIS < 100.0) THEN
            Integral_VIS := LIMIT(Integral_VIS + Istwert_VIS / (1000.0 * 60.0), 0.0, 99999.0);
        END_IF;
    END_IF;

    TON07(Lichtintegral_RESET_UVA, 5, 2);
    TOF06(NOT Lichtintegral_RESET_UVA, 6, 2);
    TOF05(reset_flag, 8, 2);
    IF TON07.Q AND TOF06.Q AND NOT TOF05.Q THEN
        Integral_UVA := 0.0;
    END_IF;

    TON08(Lichtintegral_RESET_VIS, 5, 2);
    TOF08(NOT Lichtintegral_RESET_VIS, 6, 2);
    TOF07(reset_flag, 8, 2);
    IF TON08.Q AND TOF08.Q AND NOT TOF07.Q THEN
        Integral_VIS := 0.0;
    END_IF;

    UVA_Dosis_erreicht := Integral_UVA >= Grenzwert_UVA AND Lichtintegral_AN;
    VIS_Dosis_erreicht := Integral_VIS >= Grenzwert_VIS AND Lichtintegral_AN;

    (* Steuerung Lichtkassetten *)
    IF (Steuerkontakt_Standby) THEN
        Beleuchtung_UVA := FALSE;
        Beleuchtung_VIS := FALSE;
    ELSE
        Beleuchtung_UVA := (Integral_UVA < Grenzwert_UVA AND Lichtintegral_AN) OR Steuerkontakt_Beleuchtung_UVA;
        Beleuchtung_VIS := (Integral_VIS < Grenzwert_VIS AND Lichtintegral_AN) OR Steuerkontakt_Beleuchtung_VIS;
    END_IF;

    (* Fehler UVA-Sensor *)
    IF NOT Startup_finished OR NOT is_valid (Counter_Alarm_Lichtsensor_UVA) OR (is_valid(Istwert_UVA) AND Lichtsensor_UVA) OR NOT Lichtintegral_AN THEN
        Counter_Alarm_Lichtsensor_UVA := 0.0;
    ELSE
        Counter_Alarm_Lichtsensor_UVA := Counter_Alarm_Lichtsensor_UVA + sampling_time;
    END_IF;

    (* Fehler VIS-Sensor *)
    IF NOT Startup_finished OR NOT is_valid (Counter_Alarm_Lichtsensor_VIS) OR (is_valid(Istwert_VIS) AND Lichtsensor_VIS) OR NOT Lichtintegral_AN THEN
        Counter_Alarm_Lichtsensor_VIS := 0.0;
    ELSE
        Counter_Alarm_Lichtsensor_VIS := Counter_Alarm_Lichtsensor_VIS + sampling_time;
    END_IF;

    (* Sollwertbegrenzung Temperatur *)
    IF Lichtbox OR Lichtsensor_UVA OR is_valid(Istwert_UVA) OR Lichtsensor_VIS OR is_valid(Istwert_VIS) OR Steuerkontakt_Beleuchtung_UVA OR Steuerkontakt_Beleuchtung_VIS THEN
        Sollwert_Temperatur_MIN := 5.0;
        Sollwert_Temperatur_MAX := 60.0;
    ELSE

    */
    ///////////////////////////////////////////////////////////////////////////////

    /*
    (* Sollwertbegrenzung Temperatur *)
    */
    Sollwert_Temperatur_MIN = -5.0;
    Sollwert_Temperatur_MAX = 100.0;


    ///////////////////////////////////////////////////////////////////////////////
    /// UJ: Warum fehlt folgender ST-Code ?
    ///////////////////////////////////////////////////////////////////////////////
    /*

    (* ---------- *)
    (* CO2-Regler *)
    (* ---------- *)

    (* Überprüfung ob Sensor gesteckt oder defekt *)
    IF Istwert_CO2 < -3.75 OR NOT is_valid(Istwert_CO2) THEN (* < 1mA *)
        CO2_Sensor_gesteckt := FALSE;
        CO2_Sensor_defekt := FALSE;
        Istwert_CO2 := 3.0e37;
    ELSIF Istwert_CO2 >= -3.75 AND Istwert_CO2 < -1.25 THEN (* >=1mA && < 3mA *)
        CO2_Sensor_gesteckt := TRUE;
        CO2_Sensor_defekt := TRUE;
        Istwert_CO2 := 3.0e37;
    ELSE
        CO2_Sensor_gesteckt := TRUE;
        CO2_Sensor_defekt := FALSE;
    END_IF;

    (* Korrektur CO2-Istwert *)
    (* Als quadratische Funktion gefittete Internationale Höhenformel
       p = 1013.25*(1-(0.0065*h)/288.15).^5.255;
       maximaler Fehler: +/- 0.053 im Bereich von 0..2000m*)
    Luftdruck_NHN := LIMIT(5.3532e-06 * Hoehe_NHN * Hoehe_NHN - 0.11979 * Hoehe_NHN + 1013.2, 702.0088, 1013.2); (* mbar *)

    (* Korrekturformel *)
    (* Korrektur auf Referenzwert bei 0m (1013.25mbar) und 37°C *)
    (* Formel laut Vaisala PDF: W:\FuE_Projekte\2016\16-031_PDP_Entwicklung C170-E7\A12 Elektrokonstruktion, MSR\Wie misst man CO2.pdf *)
    IF is_valid(Istwert_CO2) THEN
        Istwert_CO2 := LIMIT(Istwert_CO2 * 1013.25 / Luftdruck_NHN * (273.15 + Istwert_Temperatur) / (273.15 + 37.0), 0.0, 20.0); (* Vol.% *)
    END_IF;

    Regler_CO2_Xp := F_Xp_CO2;

    IF Steuerkontakt_Messbereich_0bis1 THEN
        Istwert_CO2 := Istwert_CO2 * 1.0 / 20.0;
        Sollwert_CO2 := MIN(Sollwert_CO2, 1.0);
        Regler_CO2_Xp := F_Xp_CO2 * 1.0 / 20.0;
    END_IF;

    (*Regelabweichung*)
    IF NOT Tuer_offen OR ABS(Regelabweichung_CO2) > ABS(Sollwert_CO2 - Istwert_CO2) OR NOT is_valid(Regelabweichung_CO2) THEN
        Regelabweichung_CO2 := Sollwert_CO2 - Istwert_CO2;
    END_IF;
    (* P-Anteil *)
    IF NOT is_valid(Regelabweichung_CO2) THEN
        P_Anteil_CO2 := 0.0;
    ELSE
        P_Anteil_CO2 := 100.0 / Regler_CO2_Xp * Regelabweichung_CO2;
    END_IF;
    (* I-Anteil *)
    (* Anti-wind-up über dynamisches Integrator Clamping *)
    IF NOT Startup_finished OR NOT is_valid(I_Anteil_CO2) THEN
        I_Anteil_CO2 := 0.0;
    ELSIF is_valid(Regelabweichung_CO2) THEN
        I_Anteil_CO2 := LIMIT(I_Anteil_CO2 + 100.0 / Regler_CO2_Xp / Regler_CO2_Tn * sampling_time * Regelabweichung_CO2, Regler_CO2_I_Anteil_MIN, MAX(Regler_CO2_I_Anteil_MAX - P_Anteil_CO2, Regler_CO2_I_Anteil_MIN));
    END_IF;
    (* D-Anteil *)
    IF NOT Startup_finished OR NOT is_valid(D_Anteil_CO2) THEN
        D_Anteil_CO2 := 0.0;
    ELSIF is_valid(Regelabweichung_CO2) AND is_valid(Regelabweichung_CO2_alt) THEN
        D_Anteil_CO2 := D_Anteil_CO2 + (100.0 / Regler_CO2_Xp * Regler_CO2_Tv * (Regelabweichung_CO2 - Regelabweichung_CO2_alt) / sampling_time - D_Anteil_CO2) / (Tau_CO2_Tv / sampling_time);
    END_IF;
    (* Berechnung Stellgrad *)
    IF Tuer_offen OR Regelung_AUS OR NOT Startup_finished OR NOT Sollwert_CO2_aktiv OR NOT is_valid(Regelabweichung_CO2) THEN
        Stellgrad_CO2 := 0.0;
    ELSE
        Stellgrad_CO2 := LIMIT(P_Anteil_CO2 + I_Anteil_CO2 + D_Anteil_CO2, Regler_CO2_MIN, Regler_CO2_MAX);
    END_IF;

    IF Steuerkontakt_Messbereich_0bis1 THEN
        Stellgrad_CO2 := Stellgrad_CO2 / 20.0;
    END_IF;

    (* Fehler CO2-Sensor *)
    IF NOT Startup_finished OR NOT is_valid (Counter_Fehler_Sensor_CO2) OR NOT Sollwert_CO2_aktiv OR NOT CO2_Sensor_defekt THEN
        Counter_Fehler_Sensor_CO2 := 0.0;
    ELSE
        Counter_Fehler_Sensor_CO2 := Counter_Fehler_Sensor_CO2 + sampling_time;
    END_IF;


    (* -------------------- *)
    (* Heißluftdesinfektion *)
    (* -------------------- *)

    IF Automatikbetrieb AND Programmnummer < 1.5 AND Programmabschnitt < 1.5 THEN
        Desinfektion_aktiv := TRUE;
        Sollwert_Temperatur_Offset := Sollwert_Temperatur_MAX;
        Signal_Programm_Stopp := Istwert_Temperatur < Sollwert_Temperatur_Geber - 1.0;
    ELSIF Automatikbetrieb AND Programmnummer < 1.5 AND Programmabschnitt > 1.5 THEN
        Desinfektion_aktiv := TRUE;
        Sollwert_Temperatur_Offset := GET_VALUE_REAL(82,0,0,0);
        Signal_Programm_Stopp := Istwert_Temperatur > Sollwert_Temperatur_Offset + 1.0;;
    ELSIF Programmendesignal AND Programmnummer < 1.5 THEN
        Desinfektion_aktiv := FALSE;
        Sollwert_Temperatur_Offset := GET_VALUE_REAL(82,0,0,0);
        Signal_Programm_Stopp := FALSE;
    ELSE
        Desinfektion_aktiv := FALSE;
        Sollwert_Temperatur_Offset := 0.0;
        Signal_Programm_Stopp := FALSE;
    END_IF;


    (* -------------- *)
    (* Kaskadenregler *)
    (* -------------- *)

    (* Begrenzung der Sensitivität *)
    Sensitivitaet_Kaskade := MAX(Sensitivitaet_Kaskade, 1.0);

    (* Regelabweichung *)
    Regelabweichung_Kaskade := Sollwert_Temperatur_Geber - Istwert_Objekttemperatur;

    (* Gradient Regelabweichung Temperatur *)
    IF NOT Startup_finished OR NOT IS_VALID(Regelabweichung_Kaskade_Gradient) THEN
        Regelabweichung_Kaskade_Gradient := 0.0;
    ELSIF IS_VALID(Regelabweichung_Kaskade) AND IS_VALID(Regelabweichung_Kaskade_alt) THEN
        Regelabweichung_Kaskade_Gradient := LIMIT(Regelabweichung_Kaskade_Gradient + ((Regelabweichung_Kaskade - Regelabweichung_Kaskade_alt) * 60.0 / sampling_time - Regelabweichung_Kaskade_Gradient) / (60.0 * 100.0 / Sensitivitaet_Kaskade) * sampling_time, -1.0 * Sensitivitaet_Kaskade / 100.0, 1.0 * Sensitivitaet_Kaskade / 100.0);
    END_IF;

    (* Xp, Tn und Tv Berechnung *)
    Regler_Kaskade_Xp := F_Xp_Kaskade;
    IF Regelabweichung_Kaskade > 0.0 THEN
        Regler_Kaskade_Tn := 300.0 * 100.0 / Sensitivitaet_Kaskade * LIMIT(Regelabweichung_Kaskade_Gradient / (-0.05 * Sensitivitaet_Kaskade / 100.0), 1.0, 100.0);
    ELSE
        Regler_Kaskade_Tn := 300.0 * 100.0 / Sensitivitaet_Kaskade * LIMIT(Regelabweichung_Kaskade_Gradient / (0.05 * Sensitivitaet_Kaskade / 100.0), 1.0, 100.0);
    END_IF;
    Regler_Kaskade_Tv := 60.0 * 100.0 / Sensitivitaet_Kaskade;

    (* Anti-wind-up über dynamisches Clamping *)
    Regler_Kaskade_MIN 						:= -Korrektur_Temperatur_MAX;
    Regler_Kaskade_MAX 						:= Korrektur_Temperatur_MAX;
    Regler_Kaskade_I_Anteil_MIN 	:= LIMIT(Regler_Kaskade_MIN - Regler_Kaskade_MIN * Regelabweichung_Kaskade_Gradient / (1.0 * Sensitivitaet_Kaskade / 100.0) - P_Anteil_Kaskade, Regler_Kaskade_MIN, 0.0);
    Regler_Kaskade_I_Anteil_MAX 	:= LIMIT(Regler_Kaskade_MAX - Regler_Kaskade_MAX * Regelabweichung_Kaskade_Gradient / (-1.0 * Sensitivitaet_Kaskade / 100.0) - P_Anteil_Kaskade, 0.0, Regler_Kaskade_MAX);

    (* P-Anteil *)
    IF NOT is_valid(Regelabweichung_Kaskade) THEN
        P_Anteil_Kaskade := 0.0;
    ELSE
        P_Anteil_Kaskade := LIMIT(Korrektur_Temperatur_MAX / Regler_Kaskade_Xp * Regelabweichung_Kaskade, -100.0, 100.0);
    END_IF;
    (* I-Anteil *)
    IF NOT Startup_finished OR NOT is_valid(I_Anteil_Kaskade) THEN
        I_Anteil_Kaskade := 0.0;
    ELSIF is_valid(Regelabweichung_Kaskade) AND Counter_Tuer_geschlossen > 300.0 AND NOT Temperatursprung THEN
        I_Anteil_Kaskade := LIMIT(I_Anteil_Kaskade + (D_Anteil_Kaskade + Korrektur_Temperatur_MAX / Regler_Kaskade_Xp * Regelabweichung_Kaskade) / Regler_Kaskade_Tn * sampling_time, Regler_Kaskade_I_Anteil_MIN, Regler_Kaskade_I_Anteil_MAX);
    END_IF;
    (* D-Anteil *)
    IF NOT Startup_finished OR NOT is_valid(D_Anteil_Kaskade) THEN
        D_Anteil_Kaskade := 0.0;
    ELSIF is_valid(Regelabweichung_Kaskade) AND is_valid(Regelabweichung_Kaskade_alt) AND Counter_Tuer_geschlossen > 60.0 THEN
        D_Anteil_Kaskade := D_Anteil_Kaskade + (Korrektur_Temperatur_MAX / Regler_Kaskade_Xp * Regler_Kaskade_Tv * (Regelabweichung_Kaskade - Regelabweichung_Kaskade_alt) / sampling_time - D_Anteil_Kaskade) / (Tau_Kaskade_Tv / sampling_time);
    END_IF;
    (* Berechnung Stellgrad *)
    IF Regelung_AUS OR NOT Startup_finished OR NOT Steuerkontakt_Kaskade OR NOT is_valid(Regelabweichung_Kaskade) THEN
        Stellgrad_Kaskade := 0.0;
    ELSE
        Stellgrad_Kaskade := LIMIT(P_Anteil_Kaskade + I_Anteil_Kaskade + D_Anteil_Kaskade, Regler_Kaskade_MIN, Regler_Kaskade_MAX);
    END_IF;

    (* Objektfeuchte *)
    p_H2O_MAX_Objekttemperatur := 6.112 * EXP(17.62 * Istwert_Objekttemperatur / (243.12 + Istwert_Objekttemperatur));
    p_H2O_MAX_Temperatur := 6.112 * EXP(17.62 * Istwert_Temperatur / (243.12 + Istwert_Temperatur));

    IF NOT Startup_finished OR NOT is_valid(Stellgrad_Kaskade_Feuchte) OR NOT Steuerkontakt_Kaskade THEN
        Istwert_Objektfeuchte := Istwert_Feuchte;
        Stellgrad_Kaskade_Feuchte := 0.0;
    ELSIF IS_VALID(p_H2O_MAX_Objekttemperatur) AND IS_VALID(p_H2O_MAX_Temperatur) THEN
        Istwert_Objektfeuchte := Istwert_Feuchte * p_H2O_MAX_Temperatur / p_H2O_MAX_Objekttemperatur;
        Stellgrad_Kaskade_Feuchte := LIMIT(Stellgrad_Kaskade_Feuchte + ((Istwert_Feuchte - Istwert_Objektfeuchte) - Stellgrad_Kaskade_Feuchte) / (600.0 / sampling_time), -Korrektur_Feuchte_MAX, Korrektur_Feuchte_MAX);
    END_IF;

    (* Sollwerte für Temperatur und Feuchteregelung im Seitenteil *)
    Sollwert_Temperatur := LIMIT(Sollwert_Temperatur_Geber + Stellgrad_Kaskade, Sollwert_Temperatur_MIN, Sollwert_Temperatur_MAX);
    Sollwert_Feuchte := Sollwert_Feuchte_Geber + Stellgrad_Kaskade_Feuchte ;


    */

    // ------------------------------------------
    // (* Sollwert Feuchteregelung (absolut/relativ) *)
    // Sollwert Feuchte (Limit-Kennfeld)
    // ------------------------------------------
    // (* Begrenzung Feuchtesollwert*)
    Index_Sollwert_Feuchte_Limit = 1;
    while ((Kennfeld_Sollwert_Feuchte_Limit[0][Index_Sollwert_Feuchte_Limit] < Sollwert_Temperatur) &&
           (Index_Sollwert_Feuchte_Limit < 10))
    {
        Index_Sollwert_Feuchte_Limit++;
    }

    Interpolation_Sollwert_Feuchte_Limit =
        limit((Kennfeld_Sollwert_Feuchte_Limit[0][Index_Sollwert_Feuchte_Limit - 1] - Sollwert_Temperatur) /
              (Kennfeld_Sollwert_Feuchte_Limit[0][Index_Sollwert_Feuchte_Limit - 1] - Kennfeld_Sollwert_Feuchte_Limit[0][Index_Sollwert_Feuchte_Limit]),
              0.0,
              1.0);

    Sollwert_Feuchte_Max =
        Kennfeld_Sollwert_Feuchte_Limit[1][Index_Sollwert_Feuchte_Limit] * Interpolation_Sollwert_Feuchte_Limit +
        Kennfeld_Sollwert_Feuchte_Limit[1][Index_Sollwert_Feuchte_Limit - 1] * (1.0 - Interpolation_Sollwert_Feuchte_Limit);

    Sollwert_Feuchte_Min =
        Kennfeld_Sollwert_Feuchte_Limit[2][Index_Sollwert_Feuchte_Limit] * Interpolation_Sollwert_Feuchte_Limit +
        Kennfeld_Sollwert_Feuchte_Limit[2][Index_Sollwert_Feuchte_Limit - 1] * (1.0 - Interpolation_Sollwert_Feuchte_Limit);

    Sollwert_Feuchte = limit(Sollwert_Feuchte, Sollwert_Feuchte_Min, Sollwert_Feuchte_Max);



    // Abschaltung Feuchteregelung
    // f_CutOffHumidityControl(...)
    Befeuchtung_aus = !Sollwert_Feuchte_aktiv || Steuerkontakt_Befeuchtung_aus ||
                      (Sollwert_Temperatur > 95.0) || (Sollwert_Temperatur < 5.0);
    Entfeuchtung_aus = !Sollwert_Feuchte_aktiv || Steuerkontakt_Entfeuchtung_aus ||
                       (Sollwert_Temperatur > 95.0) || (Sollwert_Temperatur < 5.0);

    // (p_H2Omax, Taupunkt etc.)
    p_H2Omax_Sollwert = 6.112 * std::exp(17.62 * Sollwert_Temperatur / (243.12 + Sollwert_Temperatur));
    p_H2Omax_Istwert  = 6.112 * std::exp(17.62 * Istwert_Temperatur /  (243.12 + Istwert_Temperatur));

    Taupunkt_Istwert = (241.2 * std::log(std::max(Istwert_Feuchte, 0.001) / 100.0) +
                       (4222.03716 * Istwert_Temperatur) / (241.2 + Istwert_Temperatur)) / (17.5043 - std::log(std::max(Istwert_Feuchte, 0.001) / 100.0) -
                       (17.5043 * Istwert_Temperatur) / (241.2 + Istwert_Temperatur));

    if (Temperatursprung)
    {
        Sollwert_Feuchte_Absolut = limit(Sollwert_Feuchte * p_H2Omax_Sollwert / p_H2Omax_Istwert, 0.0, Sollwert_Feuchte);
    }
    else
    {
        Sollwert_Feuchte_Absolut = Sollwert_Feuchte;
    }

    if (!is_valid(Sollwert_Feuchte_Absolut))
    {
        Sollwert_Feuchte_Absolut = Sollwert_Feuchte;
    }

    // ------------------
    // Überwachungsregler
    // ------------------
    Modus_Ueberwachungsregler  = static_cast<std::uint32_t>(UTILITY_FUNCTIONS::GET_VALUE_DINT(71,0,3,0));
    Grenzwert_Uebertemperatur  = UTILITY_FUNCTIONS::GET_VALUE_REAL(71,0,4,0);
    Offset_Ueberwachungsregler = UTILITY_FUNCTIONS::GET_VALUE_REAL(71,0,5,0);

    ///////////////////////////////////////////////////////////////////////////////
    /// UJ: Warum fehlt folgender ST-Code ?
    ///////////////////////////////////////////////////////////////////////////////
    /*
    IF Desinfektion_aktiv THEN
        Schaltwert_Uebertemperatur := 110.0;
        Schaltwert_Untertemperatur := -30.0;
    ~~ ELSIF Modus_Ueberwachungsregler > 0 THEN
    */

    if (Modus_Ueberwachungsregler > 0)
    {
        Schaltwert_Uebertemperatur = limit(Sollwert_Temperatur + Offset_Ueberwachungsregler, -30.0, 110.0);
        Schaltwert_Untertemperatur = limit(Sollwert_Temperatur - Offset_Ueberwachungsregler, -30.0, 110.0);
    }
    else
    {
        Schaltwert_Uebertemperatur = Grenzwert_Uebertemperatur;
        Schaltwert_Untertemperatur = Grenzwert_Untertemperatur;
    }

    const double maxUeberw = std::max(Istwert_Ueberwachungsregler, Istwert_Temperatur);
    const double minUeberw = std::min(Istwert_Ueberwachungsregler, Istwert_Temperatur);

    if ( (maxUeberw > Schaltwert_Uebertemperatur) || (Status_Uebertemperatur && (maxUeberw > Schaltwert_Uebertemperatur - 1.0)) ||
         (StatusQuittierung_Uebertemperatur && (Klasse_Ueberwachungsregler < 3.0)) )
    {
        Counter_Status_Uebertemperatur += 1;
    }
    else
    {
        Counter_Status_Uebertemperatur = 0;
    }

    Status_Uebertemperatur = (Counter_Status_Uebertemperatur >= static_cast<std::uint32_t>(10 * 4));
    StatusQuittierung_Uebertemperatur = (Status_Uebertemperatur && Counter_Status_Uebertemperatur <  static_cast<std::uint32_t>(11 * 4)) ||
                                        (StatusQuittierung_Uebertemperatur && Sammelalarm_quittiert);

    if ( ((minUeberw < Schaltwert_Untertemperatur) || (Status_Untertemperatur && (minUeberw < Schaltwert_Untertemperatur + 1.0))) &&
                                                      (Klasse_Ueberwachungsregler >= 3.0) )
    {
        Counter_Status_Untertemperatur += 1;
    }
    else
    {
        Counter_Status_Untertemperatur = 0;
    }

    Status_Untertemperatur = (Counter_Status_Untertemperatur >= static_cast<std::uint32_t>(10 * 4));
    StatusQuittierung_Untertemperatur = Status_Untertemperatur && ( Counter_Status_Untertemperatur < static_cast<std::uint32_t>(11 * 4)) ||
                                        (StatusQuittierung_Untertemperatur && Sammelalarm_quittiert);

    if (Status_Untertemperatur || StatusQuittierung_Untertemperatur)
    {
      Sollwert_Temperatur = std::max(Sollwert_Temperatur, Schaltwert_Untertemperatur);
    }

    if (Status_Uebertemperatur || StatusQuittierung_Uebertemperatur)
    {
        Sollwert_Temperatur = std::min(Sollwert_Temperatur,
        Schaltwert_Uebertemperatur);
    }

    // ------------------------
    // Parametersatzumschaltung
    // ------------------------
    // (* Parametersatzumschaltung über Kennfeld *)
    Index_Feuchte = static_cast<std::uint16_t>( 8 - UTILITY_FUNCTIONS::real_to_int(limit((Sollwert_Feuchte - 10.0) /
                                                (90.0 - 10.0) * 8.0, 0.0, 8.0) + 0.5));
    Index_Temperatur = static_cast<std::uint16_t>( UTILITY_FUNCTIONS::real_to_int(limit((Sollwert_Temperatur - 10.0) /
                                                  (90.0 - 10.0) * 8.0, 0.0, 8.0) + 0.5));
    Index_Temperatur_ohne_Feuchte = static_cast<std::uint16_t>(UTILITY_FUNCTIONS::real_to_int( limit((Sollwert_Temperatur - (-20.0)) /
                                                               (100.0 - (-20.0)) * 12.0, 0.0, 12.0) + 0.5));

    // (* Zuweisung der Regelparameter *)
    Regler_Heizung_Xp      = F_Xp_Heizung *            Kennfeld_Regler_Heizung_Xp[Index_Temperatur_ohne_Feuchte];
    Regler_Kuehlung_Xp     = F_Xp_Kuehlung *           Kennfeld_Regler_Kuehlung_Xp[Index_Temperatur_ohne_Feuchte];
    /*
     * UJ : Warum fehlt das?
    IF BDR_Feuchtemodul THEN
        Regler_Befeuchtung_Xp := F_Xp_Befeuchtung_BDR * Kennfeld_Regler_Befeuchtung_Xp[Index_Feuchte, Index_Temperatur];
    ELSIF BDR_300W_Feuchtemodul THEN
        Regler_Befeuchtung_Xp := F_Xp_Befeuchtung_BDR_300W * Kennfeld_Regler_Befeuchtung_Xp[Index_Feuchte, Index_Temperatur];
    END_IF;
     */
    Regler_Befeuchtung_Xp  = F_Xp_Befeuchtung_BINDER * Kennfeld_Regler_Befeuchtung_Xp[Index_Feuchte][Index_Temperatur];
    Regler_Entfeuchtung_Xp = F_Xp_Entfeuchtung *       Kennfeld_Regler_Entfeuchtung_Xp[Index_Feuchte][Index_Temperatur];
    Regler_Tuer_Xp         = F_Xp_Tuer; // *           Kennfeld_Regler_Tuer_Xp[Index_Temperatur_ohne_Feuchte];  UJ : Warum fehlt das?

    // ---------------------
    // PID Regler Temperatur
    // ---------------------
    // (* Regelabweichung *)
    Regelabweichung_Temperatur = Sollwert_Temperatur - Istwert_Temperatur;

    // Zykluszeit Regelabweichung Temperatur
    // (* Berechnung Regelabweichung Temperatur Mittelwert *)
    if (!Startup_finished || !is_valid(Standardregelabweichung_Temperatur) ||
        !is_valid(Standardgradient_Regelabweichung_Temperatur) ||
        Standardgradient_Regelabweichung_Temperatur <= 0.0)
    {
        Zykluszeit_Regelabweichung_Temperatur = 600.0;
    }
    else
    {
        Zykluszeit_Regelabweichung_Temperatur = limit( 2.0 * 3.1415 * Standardregelabweichung_Temperatur / Standardgradient_Regelabweichung_Temperatur * 60.0,
                                                       120.0,
                                                       1200.0);
    }
    Zykluszeit_Regelabweichung_Heizung   = std::min(Zykluszeit_Regelabweichung_Temperatur, 300.0);
    Zykluszeit_Regelabweichung_Kuehlung  = std::max(Zykluszeit_Regelabweichung_Temperatur, 600.0);
    Amplitude_Regelabweichung_Temperatur = 1.4142 * Standardregelabweichung_Temperatur;

    // Mittelwert Regelabweichung Temperatur
    if (!Startup_finished || !is_valid(Regelabweichung_Temperatur_Mittelwert) || Temperatursprung)
    {
        Regelabweichung_Temperatur_Mittelwert = 0.0;
    }
    else if (is_valid(Regelabweichung_Temperatur))
    {
        const double alpha = (Zykluszeit_Regelabweichung_Temperatur / sampling_time);
        Regelabweichung_Temperatur_Mittelwert = limit( Regelabweichung_Temperatur_Mittelwert + (Regelabweichung_Temperatur - Regelabweichung_Temperatur_Mittelwert) / alpha,
                                                       Regelabweichung_Temperatur - 1.0,
                                                       Regelabweichung_Temperatur + 1.0);
    }

    // Standardregelabweichung Temperatur
    if (!Startup_finished || !is_valid(Standardregelabweichung_Temperatur) || Temperatursprung)
    {
        Standardregelabweichung_Temperatur = 0.0;
    }
    else if (is_valid(Regelabweichung_Temperatur))
    {
        const double n = (Zykluszeit_Regelabweichung_Temperatur / sampling_time);
        Standardregelabweichung_Temperatur = std::min( std::sqrt(((Regelabweichung_Temperatur - Regelabweichung_Temperatur_Mittelwert) *
                                                                   (Regelabweichung_Temperatur - Regelabweichung_Temperatur_Mittelwert) +
                                                                   Standardregelabweichung_Temperatur * Standardregelabweichung_Temperatur * (n - 1.0)) / n),
                                                                   1.0);
    }

    // Gradient Regelabweichung Temperatur
    // f_PID_Temperaure.GradientDifference(...)
    if (!Startup_finished || !is_valid(Regelabweichung_Temperatur_Gradient))
    {
        Regelabweichung_Temperatur_Gradient = 0.0;
    }
    else if (is_valid(Regelabweichung_Temperatur) && is_valid(Regelabweichung_Temperatur_alt))
    {
        Regelabweichung_Temperatur_Gradient = limit(Regelabweichung_Temperatur_Gradient + ((Regelabweichung_Temperatur - Regelabweichung_Temperatur_alt) *
                                                    (60.0 / sampling_time) - Regelabweichung_Temperatur_Gradient) / (10.0 / sampling_time),
                                                    -4.0,
                                                    2.0);
    }

    // Standardgradient Temperatur
    // f_PID_Temperaure.StandardGradient(...)
    if (!Startup_finished || !is_valid(Standardgradient_Regelabweichung_Temperatur) || Temperatursprung)
    {
        Standardgradient_Regelabweichung_Temperatur = 0.0;
    }
    else if (is_valid(Regelabweichung_Temperatur))
    {
        const double n = (Zykluszeit_Regelabweichung_Temperatur / sampling_time);
        Standardgradient_Regelabweichung_Temperatur = limit(std::sqrt((Regelabweichung_Temperatur_Gradient * Regelabweichung_Temperatur_Gradient +
                                                                       Standardgradient_Regelabweichung_Temperatur *
                                                                       Standardgradient_Regelabweichung_Temperatur * (n - 1.0)) / n),
                                                                       0.01,
                                                                       1.0);
    }

    // Temperatursprung
    // f_TemperatureJumpHandler(...)
    if (!Startup_finished)
    {
        Counter_Temperatursprung = 0.0;
    }
    else if (((Sollwert_Temperatur != Sollwert_Temperatur_alt) && (std::abs(Regelabweichung_Temperatur) > 1.0)) || Tuer_offen)
    {
        Counter_Temperatursprung = std::max(Counter_Temperatursprung + sampling_time, 0.0);
    }
    else if (Regelabweichung_Temperatur <  std::max(0.2, Amplitude_Regelabweichung_Temperatur) &&
             Regelabweichung_Temperatur > -std::max(0.2, Amplitude_Regelabweichung_Temperatur))
    {
        Counter_Temperatursprung = std::min(Counter_Temperatursprung - sampling_time, 0.0);
    }
    else if (Regelabweichung_Temperatur >  std::max(0.5, 2.0 * Amplitude_Regelabweichung_Temperatur) ||
             Regelabweichung_Temperatur < -std::max(0.5, 2.0 * Amplitude_Regelabweichung_Temperatur))
    {
        Counter_Temperatursprung = std::max(Counter_Temperatursprung + sampling_time, -120.0);
    }
    Temperatursprung = (Counter_Temperatursprung > 0.0) || (Temperatursprung && Counter_Temperatursprung > -60.0);

    // Anti-wind-up Heizung (dynamisches Clamping)
    // ------ F_PID_Temperatuer.AntiWindUpHeater(...)
    if ((Counter_Tuer_geschlossen < 180.0) || (Counter_Tuer_offen > 0.0))
    {
        Regler_Heizung_MIN = 0.0;
        Regler_Heizung_I_Anteil_MIN = 0.0;
        Regler_Heizung_MAX = 100.0;
        Regler_Heizung_I_Anteil_MAX = limit(100.0 - P_Anteil_Heizung, Regler_Heizung_I_Anteil_MIN, Stellgrad_Heizung_Mittelwert);
    }
    else
    {
        Regler_Heizung_MIN = 0.0;
        Regler_Heizung_I_Anteil_MIN = 0.0;
        Regler_Heizung_MAX = 100.0;
        if (Temperatursprung)
        {
            Regler_Heizung_I_Anteil_MAX = limit(100.0 - P_Anteil_Heizung, Regler_Heizung_I_Anteil_MIN, Regler_Heizung_MAX);
        }
        else
        {
            Regler_Heizung_I_Anteil_MAX = 100.0;
        }
}
    //----------------------

    // Anpassung Xp Heizung
    // // ------------F_PID_Temperatuer.AdaptXp(...)
    if (Regelabweichung_Temperatur > Amplitude_Regelabweichung_Temperatur &&
        Regelabweichung_Temperatur_Mittelwert > 0.05 &&
        !Temperatursprung)
    {
        Regler_Heizung_Xp /= 2.0;
    }
    // Achtung: du referenzierst hier Zykluszeit_Regelabweichung_Feuchte später
    // – steht unten. Wir setzen default, falls noch uninitialisiert:
    if (!is_valid(Zykluszeit_Regelabweichung_Feuchte) || Zykluszeit_Regelabweichung_Feuchte <= 0.0)
    {
        Zykluszeit_Regelabweichung_Feuchte = 600.0;
    }

    Regler_Heizung_Xp = Regler_Heizung_Xp * limit(Zykluszeit_Regelabweichung_Feuchte / 300.0, 1.0, 2.0) *
                                            limit(1.0 + Amplitude_Regelabweichung_Temperatur / 0.5 + Stellgrad_Kuehlung_Mittelwert / -100.0,
                                                  1.0,
                                                  2.0);

    // P/I/D Heizung
    //----------------- F_PID_Temperature.step(...)
    if (!Startup_finished || !is_valid(Regelabweichung_Temperatur)) {
        P_Anteil_Heizung = 0.0;
    } else {
        P_Anteil_Heizung = 100.0 / Regler_Heizung_Xp * Regelabweichung_Temperatur;
    }

    if (!Startup_finished || !is_valid(I_Anteil_Heizung)) {
        I_Anteil_Heizung = 0.0;
    } else if (!Regelung_AUS && is_valid(Regelabweichung_Temperatur) &&
               Counter_Tuer_geschlossen > 60.0) {
        I_Anteil_Heizung = limit(
            I_Anteil_Heizung + 100.0 / Regler_Heizung_Xp /
                                   (1.0 * Zykluszeit_Regelabweichung_Heizung) *
                                   sampling_time * Regelabweichung_Temperatur,
            Regler_Heizung_I_Anteil_MIN, Regler_Heizung_I_Anteil_MAX);
    }

    if (!Startup_finished || !is_valid(D_Anteil_Heizung))
    {
        D_Anteil_Heizung = 0.0;
    }
    else if (is_valid(Regelabweichung_Temperatur) &&
             is_valid(Regelabweichung_Temperatur_alt) &&
             Counter_Tuer_geschlossen > 0.0)
    {
        D_Anteil_Heizung = D_Anteil_Heizung + (100.0 / Regler_Heizung_Xp * (0.1 * Zykluszeit_Regelabweichung_Heizung) *
                                              (Regelabweichung_Temperatur - Regelabweichung_Temperatur_alt) / sampling_time -
                                               D_Anteil_Heizung) / (Tau_Heizung_Tv / sampling_time);
    }

    if (Counter_Tuer_offen > 60.0 || Regelung_AUS || !Startup_finished)
    {
        Stellgrad_Heizung = 0.0;
    }
    else
    {
        Stellgrad_Heizung = limit(P_Anteil_Heizung + I_Anteil_Heizung + D_Anteil_Heizung,
                                  Regler_Heizung_MIN,
                                  Regler_Heizung_MAX);
    }

    if (!Startup_finished || !is_valid(Stellgrad_Heizung_Mittelwert))
    {
        Stellgrad_Heizung_Mittelwert = 0.0;
    }
    else if (Temperatursprung)
    {
        Stellgrad_Heizung_Mittelwert = Stellgrad_Heizung;
    }
    else if (is_valid(Stellgrad_Heizung))
    {
        Stellgrad_Heizung_Mittelwert = Stellgrad_Heizung_Mittelwert +
                                       (Stellgrad_Heizung - Stellgrad_Heizung_Mittelwert) / (Zykluszeit_Regelabweichung_Temperatur / sampling_time);
    }


        // ------------ F_PID_Temperature.step(...)  !!!!! todo uj umbenennen in Heizung !!!!!!!!!!!!!!!!!!!


    // ----------------------------------------------------
    // Ab hier: Kühlung + Feuchte-PIDs + Tür-PID + Aktorlogik
    // ----------------------------------------------------
    // => in gleicher Art übersetzen (Muster ist identisch).
    //
    // Für dieses Snippet: wir übernehmen für die Outputs zumindest die Werte,
    // die schon berechnet sind, und lassen Stellgrade der restlichen Aktoren,
    // falls nicht berechnet, auf gültigen Default stehen.

    // Regelabweichung Kühlung
    // F_ PID PID_Cooling()
    double Regelabweichung_Kuehlung = Regelabweichung_Temperatur;

    // Gradient  Regelabweichgun Kuehlung

    if (!Startup_finished)
    {
        Regelabweichung_Kuehlung_Gradient = 0.0;

    }
    else
    {
        Regelabweichung_Kuehlung_Gradient = limit((Regelabweichung_Kuehlung - Regelabweichung_Kuehlung_alt) * 60.0 / sampling_time - Regelabweichung_Kuehlung_Gradient,
                                                   -4.0,
                                                   2.0);
    }

    // Faktor Kuehlung

    if (!Startup_finished || Temperatursprung)
    {
        Faktor_Kuehlung = 5.0;
    }
    else if (Regelabweichung_Kuehlung < -0.1)
    {
        Faktor_Kuehlung = std::min(Faktor_Kuehlung + sampling_time, 5.0);
    }
    else
    {
        Faktor_Kuehlung = std::max(Faktor_Kuehlung - sampling_time / 60.0, 0.2);
    }

    // ---------------------------   Gegenheizen Band   !!! Überwacher !!!!!
    double Regelabweichung_Entfeuchtung = Regelabweichung_Feuchte;
    if (!Startup_finished || Temperatursprung || (Counter_Temperatursprung > -600.0 && Faktor_Gegenheizen_Band == 10.0))
    {
        Faktor_Gegenheizen_Band = 10.0;
    }
    else if (Stellgrad_Heizung_Mittelwert - Sollwert_Gegenheizen < -1.0)
    {
        if (Regelabweichung_Entfeuchtung > 0.5)
        {
            Faktor_Gegenheizen_Band = std::max(Faktor_Gegenheizen_Band - sampling_time / 10.0, 0.0);
        } else
        {
            Faktor_Gegenheizen_Band = std::min(Faktor_Gegenheizen_Band + sampling_time / 60.0, 10.0);
        }

    } else if (Stellgrad_Heizung_Mittelwert - Sollwert_Gegenheizen > 1.0 && Stellgrad_Kuehlung_Mittelwert < -5.0)
    {
        if (Regelabweichung_Entfeuchtung < -0.5)
        {
            Faktor_Gegenheizen_Band = std::max(Faktor_Gegenheizen_Band - sampling_time / 10.0, 0.0);
        }
        else
        {
            Faktor_Gegenheizen_Band = std::min(Faktor_Gegenheizen_Band + sampling_time / 600.0, 5.0);
        }
    }
    else
    {
        Faktor_Gegenheizen_Band = std::max(Faktor_Gegenheizen_Band - sampling_time / 60.0, 0.1);
    }
    // ---------------------------   Gegenheizen Band

    // -----------------------Sollwert Gegenheizen für I-Anteilverschiebung

    double Faktor_Entfeuchtung_Kuehlung = 0.0;
    double Offset_Kuehlung = Faktor_Entfeuchtung_Kuehlung;
    Offset_Kuehlung = Faktor_Entfeuchtung_Kuehlung * Stellgrad_Entfeuchtung_Mittelwert *
                      std::max((50.0 - Stellgrad_Heizung_Mittelwert) / (50 - 0.0), 0.0) * (8.0 - Stellgrad_Befeuchtung_Standby) / (8.0 - 2.0);

    // P-Anteil Kühlung
    double P_Anteil_Kuehlung = 0.0;
    if (!Startup_finished)
    {
        P_Anteil_Kuehlung = 0.0;
    }
    else
    {
        P_Anteil_Kuehlung = 100.0 / Regler_Kuehlung_Xp * Faktor_Kuehlung * Regelabweichung_Kuehlung;
    }

    // Begrenzung I-Anteil Kühlung bei Türöffnung
    double Regler_Kuehlung_MAX = 0;
    double Regler_Kuehlung_MIN = 0;
    double Regler_Kuehlung_I_Anteil_MAX = 0;
    double Regler_Kuehlung_I_Anteil_MIN = 0;

    if ((Counter_Tuer_geschlossen < 180.0) || (Counter_Tuer_offen > 0.0))
    {
        Regler_Kuehlung_MAX = I_Anteil_Kuehlung;
        Regler_Kuehlung_MIN = -100.0;
        Regler_Kuehlung_I_Anteil_MAX = I_Anteil_Kuehlung;
        Regler_Kuehlung_I_Anteil_MIN = limit( -100.0 - P_Anteil_Kuehlung, Regler_Kuehlung_MIN, Regler_Kuehlung_MAX);
    }
    else
    {
        Regler_Kuehlung_MAX = 0.0;
        Regler_Kuehlung_MIN = -100.0;
        Regler_Kuehlung_I_Anteil_MAX = 0.1 / Regler_Kuehlung_Xp * 100.0;
        Regler_Kuehlung_I_Anteil_MIN = limit( -100.0 - P_Anteil_Kuehlung + Regelabweichung_Kuehlung_Gradient / 0.5 * 100.0,
                                             Regler_Kuehlung_MIN,
                                             Regler_Kuehlung_MAX);
    }

    // f_PID_Cooling.Step(...)
    // I Anteil Kühlung
    if (!Startup_finished)
    {
        I_Anteil_Kuehlung = 0.0;
    }
    else if (Counter_Tuer_geschlossen > 60.0)
    {
        I_Anteil_Kuehlung = limit(I_Anteil_Kuehlung + 100.0 / Regler_Kuehlung_Tn * sampling_time *
                                  (Regelabweichung_Kuehlung + (Stellgrad_Heizung - Sollwert_Gegenheizen) / 100.0 *
                                  Regler_Heizung_Xp * Theta_I_Kuehlung * Faktor_Gegenheizen_Band),
                                  Regler_Kuehlung_I_Anteil_MIN,
                                  Regler_Kuehlung_I_Anteil_MAX);
    }

    // D-Anteil Kühlung

    if (!Startup_finished)
    {
        D_Anteil_Kuehlung = 0.0;
    }
    else if (Counter_Tuer_geschlossen > 0.0)
    {
        D_Anteil_Kuehlung += (100.0 / Regler_Kuehlung_Xp * Regler_Kuehlung_Tv * (Regelabweichung_Kuehlung -
                              Regelabweichung_Kuehlung_alt) / sampling_time -
                              D_Anteil_Kuehlung) / (Tau_Kuehlung_Tv / sampling_time);
    }

    // Berechnung Stellgrad Kühlung
    // f_
    if (Counter_Tuer_offen > 60.0 || !Startup_finished)
    {
        Stellgrad_Kuehlung = 0.0;
    }
    else
    {
        Stellgrad_Kuehlung = limit(P_Anteil_Kuehlung + I_Anteil_Kuehlung + D_Anteil_Kuehlung + Offset_Kuehlung,
                                   Regler_Kuehlung_MIN,
                                   Regler_Kuehlung_MAX);
    }

    // Berechnung Stellgrad Mittelwert

    if (!Startup_finished)
    {
        Stellgrad_Kuehlung_Mittelwert = 0.0;
    }
    else
    {
        Stellgrad_Kuehlung_Mittelwert = limit(Stellgrad_Kuehlung_Mittelwert + (Stellgrad_Kuehlung - Stellgrad_Kuehlung_Mittelwert) / (600.0 / sampling_time),
                                               Stellgrad_Kuehlung - 0.5 * 100.0 * Regler_Kuehlung_Xp,
                                               Stellgrad_Kuehlung + 0.5 * 100.0 / Regler_Kuehlung_Xp);
    }
    // -------------------  F_ PID PID_Cooling()


    // PID Regler Feuchte
    // f_PID_Humidiy
    Regelabweichung_Feuchte = Sollwert_Feuchte_Absolut - Istwert_Feuchte;

    // Berechnung Regelabweichung Feuchte Mittelwert
    if (!Startup_finished)
    {
        Regelabweichung_Feuchte_Mittelwert = 0.0;
    }
    else
    {
        Regelabweichung_Feuchte_Mittelwert = limit(Regelabweichung_Feuchte_Mittelwert + (Regelabweichung_Feuchte - Regelabweichung_Feuchte_Mittelwert) / (60.0 / sampling_time),
                                                   Regelabweichung_Feuchte - 10.0,
                                                   Regelabweichung_Feuchte + 10.0);
    }

    // Feuchtesprung
    // f_PID_Hunidity.HandleJump(...)
    if (!Startup_finished)
    {
        Counter_Feuchtesprung = 0.0;
    }
    else if (Sollwert_Feuchte != Sollwert_Feuchte_alt && std::abs(Regelabweichung_Feuchte) > 2.0)
    {
        Counter_Feuchtesprung = std::max(Counter_Feuchtesprung + sampling_time, 0.0);
    }
    else if (std::abs(Regelabweichung_Feuchte) < 2.0 || std::abs(Regelabweichung_Feuchte_Mittelwert) < 2.0)
    {
        Counter_Feuchtesprung = std::min(Counter_Feuchtesprung - sampling_time, 0.0);
    } else if (std::abs(Regelabweichung_Feuchte_Mittelwert) > 5.0)
    {
        Counter_Feuchtesprung = std::max(Counter_Feuchtesprung + sampling_time, -120.0);
    }

    Feuchtesprung = (Counter_Feuchtesprung > 0.0 || (Feuchtesprung && Counter_Feuchtesprung > -120.0));

    // Regelabweichung Befeuchtung

    double Regelabweichung_Befeuchtung = Regelabweichung_Feuchte;

    // Gradient regelabweichung Feuchte

    if (!Startup_finished)
    {
        Regelabweichung_Befeuchtung_Gradient = 0.0;
    }
    else
    {
        Regelabweichung_Befeuchtung_Gradient = limit(Regelabweichung_Befeuchtung_Gradient +
                                                     ((Regelabweichung_Befeuchtung - Regelabweichung_Befeuchtung_alt) * 60.0 / sampling_time -
                                                     Regelabweichung_Befeuchtung_Gradient) / 60.0 * sampling_time,
                                                     -20.0,
                                                     5.0);
    }

    // Begrenzung I-Anteil bei Türöffnung
    double Regler_Befeuchtung_MIN = 0.0;
    double Regler_Befeuchtung_MAX = 0.0;
    double Regler_Befeuchtung_I_Anteil_MIN = 0.0;
    double Regler_Befeuchtung_I_Anteil_MAX = 0.0;

    if ((Counter_Tuer_geschlossen < 180.0) || (Counter_Tuer_offen > 0.0))
    {
        Regler_Befeuchtung_MIN = std::max(Stellgrad_Befeuchtung_Standby - 2.0, Standby_Feuchtemodul);
        Regler_Befeuchtung_MAX = std::min(100.0, Stellgrad_Feuchtemodul_MAX);
        Regler_Befeuchtung_I_Anteil_MIN = 0.0;
        Regler_Befeuchtung_I_Anteil_MAX = limit(100.0 - P_Anteil_Befeuchtung,
                                                Regler_Befeuchtung_I_Anteil_MIN,
                                                Stellgrad_Befeuchtung_Mittelwert);
    } else
    {
        if (Feuchtesprung)
        {
            Regler_Befeuchtung_MIN = std::max(Stellgrad_Befeuchtung_Standby - 2.0, Standby_Feuchtemodul);
        }
        else
        {
            Regler_Befeuchtung_MIN = std::max(std::min(Stellgrad_Befeuchtung_Standby + 0.25 * P_Anteil_Befeuchtung + D_Anteil_Befeuchtung + Z_Anteil_Befeuchtung,
                                                       Stellgrad_Befeuchtung_Standby + 2.0),
                                              std::max(Stellgrad_Befeuchtung_Standby - 1.0,
                                                       Standby_Feuchtemodul));
        }
        Regler_Befeuchtung_MAX = std::min(100.0, Stellgrad_Feuchtemodul_MAX);
        Regler_Befeuchtung_I_Anteil_MIN = 0.0;
        Regler_Befeuchtung_I_Anteil_MAX = limit(100.0, Regler_Befeuchtung_I_Anteil_MIN, Stellgrad_Befeuchtung_Mittelwert);
    }

    // Anteil P-Anteil Befeuchtung bei Feuchtesprung und Türöffnung

    if (Counter_Tuer_geschlossen < 180.0 && Feuchtesprung)
    {
        Regler_Befeuchtung_Xp = Regler_Befeuchtung_Xp * 2.0;
    }
    else if (Counter_Tuer_geschlossen > 300.0 && Feuchtesprung)
    {
        Regler_Befeuchtung_Xp = Regler_Befeuchtung_Xp / 2.0;
    }
    else
    {
        Regler_Befeuchtung_Xp = Regler_Befeuchtung_Xp * (1.0 + Stellgrad_Entfeuchtung_Mittelwert / -100.0);
    }

    // P-Anteil Befeuchtung

    if (!Startup_finished)
    {
        P_Anteil_Befeuchtung = 0.0;
    }
    else
    {
        P_Anteil_Befeuchtung = 100.0 / Regler_Befeuchtung_Xp * Regelabweichung_Befeuchtung;
    }

    // I-Anteil Befeuchtung

    if (!Startup_finished)
    {
        I_Anteil_Befeuchtung = 0.0;
    }
    else if (Counter_Tuer_geschlossen > 60.0)
    {
        I_Anteil_Befeuchtung = limit(I_Anteil_Befeuchtung + 100.0 / Regler_Befeuchtung_Xp / Regler_Befeuchtung_Tn * sampling_time * Regelabweichung_Befeuchtung,
                                     Regler_Befeuchtung_I_Anteil_MIN,
                                     Regler_Befeuchtung_I_Anteil_MAX);
    }

    // D-Anteil Befeuchtung
    if (!Startup_finished)
    {
        D_Anteil_Befeuchtung = 0.0;
    }
    else if (Counter_Tuer_geschlossen)
    {
        D_Anteil_Befeuchtung += (100.0 / Regler_Befeuchtung_Xp * Regler_Befeuchtung_Tv * (Regelabweichung_Befeuchtung - Regelabweichung_Befeuchtung_alt) / sampling_time -
                                 D_Anteil_Befeuchtung) / (Tau_Befeuchtung_Tv / sampling_time);
    }

    // Z-Anteil Befeuchtung

    if (!Startup_finished || Feuchtesprung)
    {
        Z_Anteil_Befeuchtung = 0.0;
    }
    else if (Counter_Tuer_geschlossen > 0.0)
    {
        Z_Anteil_Befeuchtung += (100.0 / Regler_Befeuchtung_Xp * Regler_Befeuchtung_Tz * ((Regelabweichung_Befeuchtung -Regelabweichung_Befeuchtung_alt) -
                                (Regelabweichung_Befeuchtung_alt -
                                Regelabweichung_Befeuchtung_alt_alt)) / sampling_time -
                                Z_Anteil_Befeuchtung) / (Tau_Befeuchtung_Tz / sampling_time);
    }
    // Regler_Befeuchtung_MAX = 10.0;
    //  Berechnung Stellgrad Befeuchtung
    if (Counter_Tuer_offen > 60.0 || !Startup_finished)
    {
        Stellgrad_Befeuchtung = 0.0;
    }
    else
    {
        Stellgrad_Befeuchtung = limit(P_Anteil_Befeuchtung + I_Anteil_Befeuchtung + D_Anteil_Befeuchtung + Z_Anteil_Befeuchtung,
                                      Regler_Befeuchtung_MIN,
                                      Regler_Befeuchtung_MAX);
    }

    // Berechnung Stellgrad befeuchtung Mittelwert

    if (!Startup_finished)
    {
        Stellgrad_Befeuchtung_Mittelwert = 0.0;
    }
    else
    {
        Stellgrad_Befeuchtung_Mittelwert = limit(Stellgrad_Befeuchtung_Mittelwert + (Stellgrad_Befeuchtung - Stellgrad_Befeuchtung_Mittelwert) / (600.0 / sampling_time),
                                                 Stellgrad_Befeuchtung - 5.0 * 100.0 / Regler_Befeuchtung_Xp,
                                                 Stellgrad_Befeuchtung + 5.0 * 100.0 / Regler_Befeuchtung_Xp);
    }

    if (!Startup_finished)
    {
        Stellgrad_Befeuchtung_Standby = 4.0;
    }
    else if (!(Feuchtesprung && Counter_Feuchtesprung < 300.0))
    {
        Stellgrad_Befeuchtung_Standby = limit(Stellgrad_Befeuchtung_Standby + (Stellgrad_Befeuchtung - Stellgrad_Befeuchtung_Standby) / (600.0 / sampling_time),
                                              2.0,
                                              8.0);
    }

    // Regelabweichung Entfeuchtung

    Regelabweichung_Entfeuchtung = Regelabweichung_Feuchte;

    // Gradient Regelabweichung Entfeuchtung

    if (!Startup_finished)
    {
        Regelabweichung_Entfeuchtung_Gradient = 0.0;
    }
    else
    {
        Regelabweichung_Entfeuchtung_Gradient = limit(Regelabweichung_Entfeuchtung_Gradient +
                                                      ((Regelabweichung_Entfeuchtung - Regelabweichung_Entfeuchtung_alt) * 60.0 / sampling_time -
                                                      Regelabweichung_Entfeuchtung_Gradient) / 60.0 * sampling_time,
                                                      -20.0,
                                                      5.0);
    }

    // Faktor Entfeuchtung

    if (!Startup_finished)
    {
        Faktor_Entfeuchtung = 5.0;
    }
    else if (Regelabweichung_Entfeuchtung < -0.5)
    {
        Faktor_Entfeuchtung = std::min(Faktor_Entfeuchtung + sampling_time / 60.0, 5.0);
    }
    else
    {
        Faktor_Entfeuchtung = std::max(Faktor_Entfeuchtung - sampling_time, 0.2);
    }

    // Gegenbefeuchten Band

    if (!Startup_finished || Feuchtesprung || (Counter_Feuchtesprung > -600.0 &&
         Faktor_Gegenbefeuchten_Band == 5.0))
    {
        Faktor_Gegenbefeuchten_Band = 5.0;
    }
    else if (Stellgrad_Befeuchtung_Mittelwert - Sollwert_Gegenbefeuchten < -1.0)
    {
        if (Regelabweichung_Kuehlung > 0.05)
        {
            Faktor_Gegenbefeuchten_Band = std::max(Faktor_Gegenbefeuchten_Band - sampling_time / 10.0, 0.0);
        }
        else
        {
            Faktor_Gegenbefeuchten_Band = std::min(Faktor_Gegenbefeuchten_Band + sampling_time / 60.0, 5.0);
        }

    }
    else if (Stellgrad_Befeuchtung_Mittelwert - Sollwert_Gegenbefeuchten > 1.0 && Stellgrad_Entfeuchtung_Mittelwert < -10.0)
    {
        if (Regelabweichung_Kuehlung < -0.05)
        {
            Faktor_Gegenbefeuchten_Band = std::max(Faktor_Gegenbefeuchten_Band - sampling_time / 10.0, 0.0);
        }
        else
        {
            Faktor_Gegenbefeuchten_Band = std::min(Faktor_Gegenbefeuchten_Band + sampling_time / 600.0, 2.0);
        }
    }
    else
    {
        Faktor_Gegenbefeuchten_Band = std::max(Faktor_Gegenbefeuchten_Band - sampling_time / 60.0, 0.2);
    }

    // Sollwert Gegenbefeuchten für I-Anteilverschiebung

    Sollwert_Gegenbefeuchten   = 6.0 - 4.0 * (-Stellgrad_Entfeuchtung_Mittelwert / 100.0);
    double Offset_Entfeuchtung = 0.0 * Stellgrad_Kuehlung_Mittelwert;

    //(* P-Anteil Entfeuchtung *)

    double P_Anteil_Entfeuchtung = 0.0;
    if (!Startup_finished)
    {
        P_Anteil_Entfeuchtung = 0.0;
    }
    else
    {
        P_Anteil_Entfeuchtung = 100.0 / Regler_Entfeuchtung_Xp * Faktor_Entfeuchtung * Regelabweichung_Entfeuchtung;
    }
    // I-Anteil Entfeuchtung
    if (!Startup_finished)
    {
        I_Anteil_Entfeuchtung = 0.0;
    }
    else if (Counter_Tuer_geschlossen > 60.0)
    {
        I_Anteil_Entfeuchtung = limit(I_Anteil_Entfeuchtung + 100.0 / Regler_Entfeuchtung_Xp / Regler_Entfeuchtung_Tn * sampling_time * (Regelabweichung_Entfeuchtung +
                                      (Stellgrad_Befeuchtung - Sollwert_Gegenbefeuchten) / 100.0 * Regler_Befeuchtung_Xp * Theta_I_Entfeuchtung * Faktor_Gegenbefeuchten_Band),
                                      Regler_Entfeuchtung_I_Anteil_MIN,
                                      Regler_Entfeuchtung_I_Anteil_MAX);
    }
    // D-Anteil Entfeuchtung
    if (!Startup_finished)
    {
        D_Anteil_Entfeuchtung = 0.0;
    }
    else if (Counter_Tuer_geschlossen > 0.0)
    {
        D_Anteil_Entfeuchtung = D_Anteil_Entfeuchtung + (100.0 / Regler_Entfeuchtung_Xp * Regler_Entfeuchtung_Tv *
                                (Regelabweichung_Entfeuchtung - Regelabweichung_Entfeuchtung_alt) / sampling_time -
                                D_Anteil_Entfeuchtung) / (Tau_Entfeuchtung_Tv / sampling_time);
    }

    // Begrenzung Stellgrad und I-Anteil

    if ((Counter_Tuer_geschlossen < 180.0) || (Counter_Tuer_offen > 0.0))
    {
        Regler_Entfeuchtung_MAX = I_Anteil_Entfeuchtung;
        Regler_Entfeuchtung_MIN = -100.0;
        Regler_Entfeuchtung_I_Anteil_MAX = I_Anteil_Entfeuchtung;
        Regler_Entfeuchtung_I_Anteil_MIN = limit(-100.0 - P_Anteil_Entfeuchtung,
                                                 Regler_Entfeuchtung_MIN,
                                                 Regler_Entfeuchtung_I_Anteil_MAX);
    }
    else
    {
        Regler_Entfeuchtung_MAX = 0.0;
        Regler_Entfeuchtung_MIN = -100.0;
        Regler_Entfeuchtung_I_Anteil_MAX = 1.5 / Regler_Entfeuchtung_Xp * 100.0;
        Regler_Entfeuchtung_I_Anteil_MIN = limit(-100.0 - P_Anteil_Entfeuchtung + Regelabweichung_Entfeuchtung_Gradient / 20.0 * 100.0,
                                                 Regler_Entfeuchtung_MIN,
                                                 Regler_Entfeuchtung_I_Anteil_MAX);
    }

    // Berechnung Stellgrad Entfeuchtung

    if (Counter_Tuer_offen > 60.0 || !Startup_finished)
    {
        Stellgrad_Entfeuchtung = 0.0;
    }
    else
    {
        Stellgrad_Entfeuchtung = limit(P_Anteil_Entfeuchtung + I_Anteil_Entfeuchtung + Offset_Entfeuchtung + D_Anteil_Entfeuchtung,
                                       Regler_Entfeuchtung_MIN,
                                       Regler_Entfeuchtung_MAX);
    }

    // Berechnung Stellgrad entfeuchtung Mittelwert

    if (!Startup_finished)
    {
        Stellgrad_Entfeuchtung_Mittelwert = 0.0;
    }
    else
    {
        Stellgrad_Entfeuchtung_Mittelwert = limit((Stellgrad_Entfeuchtung - Stellgrad_Entfeuchtung_Mittelwert) / (600.0 / sampling_time),
                                                   Stellgrad_Entfeuchtung - 5.0 * 100.0 / Regler_Entfeuchtung_Xp,
                                                   Stellgrad_Entfeuchtung + 5.0 * 100.0 / Regler_Entfeuchtung_Xp);
    }

    // PID Regler Tür
    // f_PID_Door(...)
    double Offset_Temperatur_Tuer;
    if (Feuchtesprung)
    {
        Offset_Temperatur_Tuer = 0.0;
    }
    else
    {
        Offset_Temperatur_Tuer = 2.0 * Betauungsschutz / 100.0 * limit((90.0 + Stellgrad_Kuehlung_Mittelwert) / (90.0 - 80.0),
                                                                        0.0,
                                                                        1.0);
    }

    // Regelabweichung
    if (!Tuer_offen || std::abs(Regelabweichung_Tuer) > std::abs(Sollwert_Temperatur + Offset_Temperatur_Tuer - Istwert_Temperatur_Tuer))
    {
        Regelabweichung_Tuer = Sollwert_Temperatur + Offset_Temperatur_Tuer - Istwert_Temperatur_Tuer;
    }

    // P-Anteil

    // Anti-Windup über dynamisches Integrator Clamping

    double Regler_Tuer_MAX          = limit( (100.0 + Stellgrad_Kuehlung) / (100.0 - 80.0) * 100.0, 0.0, 100.0);
    double Regler_Tuer_MIN          = 0.0;
    double Regler_Tuer_I_Anteil_MAX = limit(100.0 - P_Anteil_Tuer, 0.0, Regler_Tuer_MAX);
    double Regler_Tuer_I_Anteil_MIN = 0.0;

    if (!Startup_finished)
    {
        P_Anteil_Tuer = 0.0;
    }
    else
    {
        P_Anteil_Tuer = 100.0 / Regler_Tuer_Xp * Regelabweichung_Tuer;
    }

    // I-Anteil
    if (!Startup_finished)
    {
        I_Anteil_Tuer = 0.0;
    } else if (Temperatursprung && Regelabweichung_Tuer > 0.0)
    {
        I_Anteil_Tuer = limit(I_Anteil_Tuer,
                              Regler_Tuer_I_Anteil_MIN,
                              Regler_Tuer_I_Anteil_MAX);
    }
    else if (!Tuer_offen)
    {
        I_Anteil_Tuer = limit(I_Anteil_Tuer + 100.0 / Regler_Tuer_Xp / Regler_Tuer_Tn * sampling_time * Regelabweichung_Tuer,
                              Regler_Tuer_I_Anteil_MIN,
                              Regler_Tuer_I_Anteil_MAX);
    }
    // D-Anteil

    if (!Startup_finished)
    {
        D_Anteil_Tuer = 0.0;
    }
    else
    {
        D_Anteil_Tuer = D_Anteil_Tuer + (100.0 / Regler_Tuer_Xp * Regler_Tuer_Tv * (Regelabweichung_Tuer - Regelabweichung_Tuer_alt) / sampling_time -
                        D_Anteil_Tuer) / (Tau_Tuer_Tv / sampling_time);
    }

    double Stellgrad_Tuer;
    // Berechnung Stellgrad Tür
    if (Tuer_offen || !Startup_finished)
    {
        Stellgrad_Tuer = limit(P_Anteil_Tuer + I_Anteil_Tuer + D_Anteil_Tuer,
                               Regler_Tuer_MIN,
                               Regler_Tuer_MAX);
    }

    // Kennfelder Kühlung/Entfeuchtung

    int Index_Ueberhitzung = 1;
    while (Kennfeld_Ueberhitzung[0][Index_Ueberhitzung] < Istwert_Temperatur && Index_Ueberhitzung < 12)
    {
        Index_Ueberhitzung++;
    }
    double Interpolation_Ueberhitzung = limit((Kennfeld_Ueberhitzung[0][Index_Ueberhitzung - 1] - Istwert_Temperatur) / (Kennfeld_Ueberhitzung[0][Index_Ueberhitzung - 1] - Kennfeld_Ueberhitzung[0][Index_Ueberhitzung]),
                                               0.0,
                                              1.0);
    double Skalierung_Ueberhitzung_Kennfeld_Min = Kennfeld_Ueberhitzung[1][Index_Ueberhitzung] *
                                                  (Interpolation_Ueberhitzung + Kennfeld_Ueberhitzung[1][Index_Ueberhitzung - 1]) *
                                                  (1.0 - Interpolation_Ueberhitzung);
    double Skalierung_Ueberhitzung_Kennfeld_Max = Kennfeld_Ueberhitzung[2][Index_Ueberhitzung] *
                                                  (Interpolation_Ueberhitzung + Kennfeld_Ueberhitzung[2][Index_Ueberhitzung - 1]) *
                                                  (1.0 - Interpolation_Ueberhitzung);
    double Sollwert_Ueberhitzung = Kennfeld_Ueberhitzung[3][Index_Ueberhitzung] *
                                   (Interpolation_Ueberhitzung + Kennfeld_Ueberhitzung[3][Index_Ueberhitzung - 1]) *
                                   (1.0 - Interpolation_Ueberhitzung);

    //(* Entfeuchtung und Abtauung aus Kennfeld *)
    int Index_Entfeuchtung = 1; // todo uj - warum nicht mit 0 starten?
    while ((Kennfeld_Entfeuchtung[0][Index_Entfeuchtung] < Istwert_Temperatur) && (Index_Ueberhitzung < 12))
    {
        Index_Entfeuchtung++;
    }
    double Interpolation_Entfeuchtung = limit((Kennfeld_Entfeuchtung[0][Index_Entfeuchtung - 1] -
                                               (Istwert_Temperatur) / (Kennfeld_Entfeuchtung[0][Index_Entfeuchtung - 1] ) -
                                               Kennfeld_Entfeuchtung[0][Index_Entfeuchtung]),
                                               0.0,
                                              1.0);
    double Skalierung_Entfeuchtungsventil = Kennfeld_Entfeuchtung[1][Index_Entfeuchtung] * Interpolation_Entfeuchtung +
                                            Kennfeld_Entfeuchtung[1][Index_Entfeuchtung - 1] * (1.0 - Interpolation_Entfeuchtung);

    double Abtauzyklus_Pause = Kennfeld_Entfeuchtung[2][Index_Entfeuchtung] * Interpolation_Entfeuchtung +
                               Kennfeld_Entfeuchtung[2][Index_Entfeuchtung - 1] * (1.0 - Interpolation_Entfeuchtung);

    double Abtauzyklus_Dauer = Kennfeld_Entfeuchtung[3][Index_Entfeuchtung] * Interpolation_Entfeuchtung +
                               Kennfeld_Entfeuchtung[3][Index_Entfeuchtung - 1] * (1.0 - Interpolation_Entfeuchtung);

    // Ende - f_PID_Door(...)

    // Überhitzungsregelung

    // Temperatur Verdampferausgang PT1
    // f_PT1.Vapourize(...)
    if (!Startup_finished)
    {
        Istwert_Temperatur_Verdampferausgang_PT1 = Istwert_Temperatur_Verdampferausgang;
    }
    else
    {
        Istwert_Temperatur_Verdampferausgang_PT1 += (Istwert_Temperatur_Verdampferausgang -
                                                    Istwert_Temperatur_Verdampferausgang_PT1) / (60.0 * 4.0);
    }

    // Skalierung Überhitzung
    if (Istwert_Temperatur_Verdampferausgang < (Istwert_Temperatur + Sollwert_Ueberhitzung))
    {
        Counter_Durchzug = MIN(Counter_Durchzug + sampling_time, 10.0);
    }
    else
    {
        // uj-todo; template for MAX
        // Counter_Durchzug = MAX(MAX(Counter_Durchzug-sampling_time,-10),0);
    }
    if (Counter_Durchzug >= 10.0)
    {
        Durchzug = true;
    }
    else if (Counter_Durchzug <= -10.0)
    {
        Durchzug = false;
    }

    double Skalierung_Ueberhitzung_PT1 = Skalierung_Ueberhitzung_Kennfeld_Max;

    // Magnetventil Kühlung

    // Stellgrad Kühlung

    if (Counter_Verdichter_aus > 60.0 && Counter_Verdichter_aus < 600.0)
    {
        Stellgrad_Kuehlventil = 1.0;
    }
    else if (Istwert_Temperatur > 120.0 || Counter_Kuehlung_Freigabe <= 0.0)
    {
        Stellgrad_Kuehlventil = 0.0;
    }
    else
    {
        Stellgrad_Kuehlventil = MAX(-Stellgrad_Kuehlung, 0.0) * Skalierung_Ueberhitzung_PT1 * Counter_Kuehlung_Freigabe;
    }

    // Ende - f_PT1.Vapourize(...)

    // PWM Generierung Kuehlventil
    // bool step(double Stellgrad,double sampling_time){

    // f_PWMCoolingValve.Step(...)
    Kuehlventil = PWM_Kuehlventil.step(Stellgrad_Kuehlventil, sampling_time);

    // Magnetventil Entfeuchtung
    double T_Entfeuchtung;

    if (!Startup_finished)
    {
        T_Entfeuchtung = Istwert_Temperatur;
    }
    else if (Entfeuchtungsventil)
    {
        T_Entfeuchtung = limit(T_Entfeuchtung + ((Istwert_Temperatur - T_Entfeuchtung) * U_Entfeuchtung -
                               P_Entfeuchtung) / (4.0 * C_Entfeuchtung),
                               -30.0,
                               Istwert_Temperatur);
    }
    else
    {
        T_Entfeuchtung = limit(T_Entfeuchtung + ((Istwert_Temperatur - T_Entfeuchtung) * U_Entfeuchtung) / (4.0 * C_Entfeuchtung),
                               -30.0,
                               Istwert_Temperatur);
    }
    // Stellgrad Entfeuchtung

    if (Istwert_Temperatur > 120.0 || Counter_Entfeuchtung_Freigabe <= 0.0)
    {
        Stellgrad_Entfeuchtungsventil = 0.0;
    } else
    {
        Stellgrad_Entfeuchtungsventil = MAX(-Stellgrad_Entfeuchtung, 0.0) *
                                            Skalierung_Entfeuchtungsventil *
                                            Counter_Entfeuchtung_Freigabe;
    }

    // Abtauzyklus
    bool Freigabe_Abtauzyklus = Istwert_Feuchte > Sollwert_Feuchte_Absolut + 2.5;
    if (!Freigabe_Abtauzyklus)
    {
        Counter_Freigabe_Abtauzyklus = 0.0;
    } else
    {
        Counter_Freigabe_Abtauzyklus += sampling_time;
    }
    double Skalierung_Abtauzyklus_Dauer = 0.0;
    ;
    if (Abtauzyklus_Pause > 0.0 && Abtauzyklus_Dauer > 0.0)
    {
        Skalierung_Abtauzyklus_Dauer = limit(Counter_Freigabe_Abtauzyklus / (10.0 * (Abtauzyklus_Pause + Abtauzyklus_Dauer) * 60.0),
                                             1.0,
                                             5.0);
    }
    if (Counter_Abtauzyklus > (Abtauzyklus_Pause + Abtauzyklus_Dauer * Skalierung_Abtauzyklus_Dauer) * 60.0)
    {
        Counter_Abtauzyklus = 0.0;
    }
    else if ((T_Entfeuchtung < 0.0 && Freigabe_Abtauzyklus) || Abtauzyklus)
    {
        Counter_Abtauzyklus += sampling_time;
    }
    else
    {
        Counter_Abtauzyklus = MAX(Counter_Abtauzyklus - sampling_time, 0.0);
    }
    Abtauzyklus = Counter_Abtauzyklus > Abtauzyklus_Pause * 60.0 && Abtauzyklus_Pause > 0.0;

    if (Abtauzyklus)
    {
        Stellgrad_Entfeuchtungsventil = 0.0;
    }
    // Ende -f_PWMCoolingValve.Step(...)


    // PWM Entfeuchtungsventil
    // f_PWMDeHumizeValve.Step(...)
    Entfeuchtungsventil = PWM_Entfeuchtungsventil.step(Stellgrad_Entfeuchtungsventil, sampling_time);

    // Druckausgleich vor Start Verdichter
    if (!Verdichter && Counter_Entfeuchtung_Freigabe > 5.0)
    {
        Entfeuchtungsventil = true;
    }

    // Heizung Kessel

    if (Freigabe_Heizung)
    {
        Stellgrad_HeizungKessel = MAX(Stellgrad_Heizung * Counter_Heizung_Freigabe, 0.0);
    }
    else
    {
        Stellgrad_HeizungKessel = 0.0;
    }

    // PWM Heizung Kessel
    HeizungKessel = PWM_Heizung_Kessel.step(Stellgrad_HeizungKessel, 0.25);

    // Betauungsschutz

    double Stellgrad_Betauungsschutz_MAX = limit( (90.0 + Stellgrad_Kuehlung_Mittelwert) / (90.0 - 80.0) * 100.0,
                                                  0.0,
                                                  limit((Sollwert_Temperatur - 25.0) / (-20.0 - 25.0) * Betauungsschutz,
                                                         0.0,
                                                         Betauungsschutz));

    if (!Startup_finished)
    {
        Stellgrad_Betauungsschutz = 0.0;
    }

    // Tuerheizung
    Stellgrad_HeizungTuer = limit(Stellgrad_Tuer + Stellgrad_Betauungsschutz / 10.0, 0.0, 100.0);

    HeizungTuer = PWM_Heizung_Tuer.step(Stellgrad_HeizungTuer, 0.25);

    // Heizung Kesselrand

    Stellgrad_HeizungKesselrand = limit(Stellgrad_Tuer * limit(1.0 + Betauungsschutz / 100.0 * 2.0 * (90.0 + Stellgrad_Kuehlung_Mittelwert) / (90.0 - 80.0),
                                                               1.0,
                                                               2.0) + Stellgrad_Betauungsschutz,
                                        0.0,
                                        100.0);

    // Ende - f_PWMDeHumizeValve.Step(...)


    // PWM Kesselrand
    // Ende - f_PWMKesselRand.Step(...)
    HeizungKesselrand = PWM_Heizung_Kesselrand.step(Stellgrad_HeizungKesselrand, 0.25);
    // Ende - f_PWMKesselRand.Step(...)

    {
    // Binder Feuchtemodul

    // Sperrsignal Feuchtemodul

    bool Sperrsignal_Feuchte = false;

    // Gradient Feuchtemodul

    if (!Startup_finished)
    {
        Temperatur_Feuchtemodul_Gradient = 0;
    }
    else
    {
        Temperatur_Feuchtemodul_Gradient += ((Temperatur_Feuchtemodul - Temperatur_Feuchtemodul_alt) * 60.0 * 4.0 -
                                             Temperatur_Feuchtemodul_Gradient) / (4.0 * 10.0);
    }

    Sollwert_Temperatur_Feuchtemodul = Sollwert_Temperatur_Feuchtemodul_MIN + Stellgrad_Feuchtemodul_Mittelwert / 100.0 *
                                      (Sollwert_Temperatur_Feuchtemodul_MAX - Sollwert_Temperatur_Feuchtemodul_MIN);

    // Offset Stellgrad Feuchtemodul

    double Temperatur_Feuchtemodul_Standby = Sollwert_Temperatur_Feuchtemodul - (Stellgrad_Entfeuchtung_Mittelwert - 0.0) / (-100.0 - 0.0) * 60.0;
    double Standby_Feuchtemodul            = limit((Temperatur_Feuchtemodul_Standby - Temperatur_Feuchtemodul) / 10.0 * 5.0,
                                                    0.0,
                                                    5.0);

    // Stellgrad Feuchtemodul
    Stellgrad_Feuchtemodul = limit(Stellgrad_Befeuchtung + 2.0 * limit(Stellgrad_Befeuchtung - Stellgrad_Feuchtemodul_PT1,
                                                                       -2.0,
                                                                       2.0) + Stellgrad_Feuchtemodul_Nachfuellen,
                                   0.0,
                                   100.0);

    // Begrenzung Temperatur Feuchtemodul über Temperatur
    double Stellgrad_Feuchtemodul_MAX_Temperatur = limit((Temperatur_Feuchtemodul_MAX - Temperatur_Feuchtemodul) / 40.0 * 100.0,
                                                         0.0,
                                                         100.0);

    double Temperatur_Feuchtemodul_Gradient_MAX = 10.0;
    if (Temperatur_Feuchtemodul < 90.0)
    {
        Temperatur_Feuchtemodul_Gradient_MAX = 10.0;
    }
    else
    {
        Temperatur_Feuchtemodul_Gradient_MAX = limit(20.0 - (Temperatur_Feuchtemodul - (Temperatur_Feuchtemodul_MAX - 60.0)) / 60.0 * 20.0 - Stellgrad_Feuchtemodul_MAX / 100.0 * 10.0,
                                                     5.0,
                                                     20.0);
    }

    double Stellgrad_Feuchtemodul_MAX_adaptiv = 10.0;
    if (!Startup_finished)
    {
        Stellgrad_Feuchtemodul_MAX_adaptiv = 10.0;
    }
    else if (Temperatur_Feuchtemodul < 80.0)
    {
        Stellgrad_Feuchtemodul_MAX_adaptiv = 10.0;
    }
    else if (Stellgrad_Feuchtemodul + 20.0 > Stellgrad_Feuchtemodul_MAX_adaptiv &&
             Stellgrad_Feuchtemodul_MAX_adaptiv < Stellgrad_Feuchtemodul_MAX_Temperatur &&
               Temperatur_Feuchtemodul_Gradient < 2.0 * Temperatur_Feuchtemodul_Gradient_MAX &&
               !Sperrsignal_Feuchte)
    {
        Stellgrad_Feuchtemodul_MAX_adaptiv = limit(Stellgrad_Feuchtemodul_MAX_adaptiv + 20.0 * limit((Temperatur_Feuchtemodul_Gradient_MAX -
                                                                                                      Temperatur_Feuchtemodul_Gradient) / Temperatur_Feuchtemodul_Gradient_MAX,
                                                                                                      -2.0,
                                                                                                      1.0) / (60.0 * 4.0),
                                                   0.0,
                                                   Stellgrad_Feuchtemodul_MAX_Temperatur);
    }
    else
    {
        Stellgrad_Feuchtemodul_MAX_adaptiv = limit(Stellgrad_Feuchtemodul_MAX_adaptiv - 5.0 / (60.0 * 4.0),
                                                   10.0,
                                                   Stellgrad_Feuchtemodul_MAX_Temperatur);
    }

    // Begrenzung Stellgrad_Feuchtemodul

    Stellgrad_Feuchtemodul_MAX = MIN(Stellgrad_Feuchtemodul_MAX_Temperatur - 5.0,
                                     Stellgrad_Feuchtemodul_MAX_adaptiv + Stellgrad_Feuchtemodul_Boost);

    Stellgrad_Feuchtemodul = MIN(Stellgrad_Feuchtemodul, Stellgrad_Feuchtemodul_MAX);

    // Stellgrad Feuchtemodul Boost

    if (!Startup_finished || Temperatur_Feuchtemodul < 80.0)
    {
        Stellgrad_Feuchtemodul_Boost = 0.0;
    }
    else if (Stellgrad_Feuchtemodul < Stellgrad_Feuchtemodul_MAX_adaptiv - 10.0)
    {
        Stellgrad_Feuchtemodul_Boost = MIN(Stellgrad_Feuchtemodul_Boost + 10.0 / 60.0 * sampling_time,
                                           2.0 * Stellgrad_Feuchtemodul_MAX_Temperatur);
    } else {
        Stellgrad_Feuchtemodul_Boost = MAX(Stellgrad_Feuchtemodul_Boost - (Stellgrad_Feuchtemodul + 10.0 -
                                           Stellgrad_Feuchtemodul_MAX_adaptiv) / 60.0 * sampling_time,
                                           0.0);
    }
    if (!Startup_finished)
    {
        Stellgrad_Feuchtemodul_PT1 = Stellgrad_Feuchtemodul;
    }
    else if (is_valid(Stellgrad_Feuchtemodul_PT1))
    {
        Stellgrad_Feuchtemodul_PT1 = 0.0;
    }
    else
    {
        Stellgrad_Feuchtemodul_PT1 = Stellgrad_Feuchtemodul_PT1 +
                                     (Stellgrad_Feuchtemodul - Stellgrad_Feuchtemodul_PT1) / (4.0 * 30.0);
    }

    // Nachfüllen Befeuchtungsmodul

    double Stellgrad_Nachfuellen_Temperatur           = 0.0;
    double Skalierung_Nachfuellen_Temperatur_P_Anteil = 0.0;
    double Skalierung_Nachfuellen_Temperatur_D_Anteil = 0.0;

    if (!Startup_finished)
    {
        Stellgrad_Nachfuellen_Temperatur = 0.0;
        Skalierung_Nachfuellen_Temperatur_D_Anteil = 0.0;
        Skalierung_Nachfuellen_Temperatur_D_Anteil = 0.0;
        Skalierung_Nachfuellen_Temperatur_I_Anteil = 0.0;
    }
    else
    {
        Stellgrad_Nachfuellen_Temperatur = limit(MAX(Temperatur_Feuchtemodul - Sollwert_Temperatur_Feuchtemodul, 0.0) * 2.0 +
                                                 MIN(Temperatur_Feuchtemodul - Sollwert_Temperatur_Feuchtemodul, 0.0) * 1.0,
                                                 -20.0,
                                                 50.0);
        Skalierung_Nachfuellen_Temperatur_P_Anteil = limit(MAX(Temperatur_Feuchtemodul - Sollwert_Temperatur_Feuchtemodul, 0.0) / 20.0 +
                                                               MIN(Temperatur_Feuchtemodul - Sollwert_Temperatur_Feuchtemodul, 0.0) / 40.0,
                                                           -0.2,
                                                           5.0);
        Skalierung_Nachfuellen_Temperatur_D_Anteil = limit(Temperatur_Feuchtemodul_Gradient / 10.0, -5.0, 5.0);
        Skalierung_Nachfuellen_Temperatur_I_Anteil = limit(Skalierung_Nachfuellen_Temperatur_I_Anteil + limit(Skalierung_Nachfuellen_Temperatur_P_Anteil +
                                                                                                              Skalierung_Nachfuellen_Temperatur_D_Anteil,
                                                                                                              -0.2,
                                                                                                              25.0) *
                                                           sampling_time / 1200.0,
                                                           0.0,
                                                           5.0);
    }
    double Stellgrad_Nachfuellen = 0.0;
    Stellgrad_Feuchtemodul_Nachfuellen = 0.0;

    if (Temperatur_Feuchtemodul > Temperatur_Feuchtemodul_MAX && Temperatur_Feuchtemodul_Gradient > 0.0)
    {
        Stellgrad_Nachfuellen = 20.0;
        Stellgrad_Feuchtemodul_Nachfuellen = 0.0;
    }
    else
    {
        Stellgrad_Nachfuellen = limit(((Stellgrad_Feuchtemodul_PT1 - Stellgrad_Feuchtemodul_Nachfuellen) *
                                        (MAX(Skalierung_Nachfuellen_Temperatur_P_Anteil + Skalierung_Nachfuellen_Temperatur_D_Anteil,
                                         0.0) + Skalierung_Nachfuellen_Temperatur_I_Anteil) + Stellgrad_Nachfuellen_Temperatur) * 2.0 * 7.9681 / Durchflussmenge_Einlassventil_Feuchtemodul,
                                         0.0,
                                         100.0);
        Stellgrad_Feuchtemodul_Nachfuellen = 0.5 * Stellgrad_Nachfuellen * Durchflussmenge_Einlassventil_Feuchtemodul / 60.0 * 4.19 * 75.0 / 600.0;
    }

    //(* Entleeren des Kessels *)
    bool Entleeren_Kessel = false;
    if (Startup_finished && Temperatur_Feuchtemodul < 70.0) {
        Entleeren_Kessel = Counter_Entleeren_Kessel < 60.0;
        Counter_Entleeren_Kessel += sampling_time;
    } else {
        Counter_Entleeren_Kessel = 0.0;
        Entleeren_Kessel = false;
    }

    //(* Abschlämmen des Kessels *)
    Abschlaemmintegral = Abschlaemmintegral + 0.1 * Durchflussmenge_Einlassventil_Feuchtemodul / Durchflussmenge_Abschlaemmventil_Feuchtemodul *
                         Stellgrad_Nachfuellen / 100.0 * sampling_time;

    if (Entleeren_Kessel)
    {
        Abschlaemmventil = true;
        Stellgrad_Abschlaemmventil_zyklisch = 100.0;
    }
    else
    {
        Counter_Zyklus_Abschlaemmventil += sampling_time;
        if (Abschlaemmintegral > 0.25 && Temperatur_Feuchtemodul_Gradient < 0.0)
        {
            Abschlaemmventil = true;
            Stellgrad_Abschlaemmventil_zyklisch = 2.5;
            Counter_Zyklus_Abschlaemmventil = 0.0;
            Abschlaemmintegral -= 0.25;
        }
        else
        {
            Abschlaemmventil = false;
            if (Counter_Zyklus_Abschlaemmventil >= 10.0)
            {
                Stellgrad_Abschlaemmventil_zyklisch = 0.0;
            }
        }
    }

    // Stellgrad_Feuchtemodul = 2.0;

    // PWM Befeuchtungsmodul
    HeizungFeuchtemodul = PWM_Heizung_Feuchtemodul.step(Stellgrad_Feuchtemodul, 0.25);
    // PWM Nachfüllen
    Nachfuellen = PWM_Nachfuellen.step(Stellgrad_Nachfuellen, 0.25);

    // Abwassermanagement
    if (!Entleerbehaelter_Unten || !Entleerbehaelter_Oben || Entleeren_Kessel)
    {
        Counter_Nachlauf_Entleeren = 10.0;
        Counter_Pause_Entleeren += sampling_time;
        if (Counter_Pause_Entleeren > 30.0)
        {
            Counter_Pause_Entleeren = 0.0;
        }

    }
    else
    {
        Counter_Nachlauf_Entleeren = MAX(Counter_Nachlauf_Entleeren - sampling_time, 0.0);
        Counter_Pause_Entleeren = 0.0;
    }

    bool Entleeren_Pause = false;
    Entleeren = Counter_Nachlauf_Entleeren > 0.0;
    Entleeren_Pause = Counter_Pause_Entleeren > 27.0;
    Entleerpumpe = Entleeren && !Entleeren_Pause;

    if (!Startup_finished)
    {
        Zyklus_Entleeren_Mittelwert = 600.0;
    }
    else if (Entleeren && !Entleeren_alt)
    {
        Zyklus_Entleeren_Mittelwert = limit(Zyklus_Entleeren_Mittelwert + (Counter_Zyklus_Entleeren - Zyklus_Entleeren_Mittelwert) / 10.0,
                                            10.0,
                                            3600.0);
        Counter_Zyklus_Entleeren = 0.0;
    }
    else
    {
        Counter_Zyklus_Entleeren += sampling_time;
    }

    if (Steuerkontakt_Standby || Istwert_Temperatur > 110.0)
    {
        Counter_Kuehlung_Freigabe = 0.0;
    } else
    {
        if (Stellgrad_Kuehlung < 0.0 || Stellgrad_Heizung < 2.0 || Counter_Tuer_geschlossen < 300.0)
        {
            Counter_Kuehlung_Freigabe = MIN(Counter_Kuehlung_Freigabe + sampling_time / 10.0, 1.0);
        }
        else
        {
            Counter_Kuehlung_Freigabe = MAX(Counter_Kuehlung_Freigabe - sampling_time / 300.0, 0.0);
        }
    }
    Freigabe_Kuehlung = Counter_Kuehlung_Freigabe >= 1.0 || (Freigabe_Kuehlung && Counter_Kuehlung_Freigabe > 0.0);
    // * Freigabe Kühlung *)

    // (* Freigabe Entfeuchtung *)
    if (Steuerkontakt_Standby || Istwert_Temperatur > 110.0)
    {
        Counter_Entfeuchtung_Freigabe = 0.0;
    }
    else
    {
        if (Stellgrad_Entfeuchtung < 0.0 || Stellgrad_Befeuchtung < 2.0 || Counter_Tuer_geschlossen < 300.0) {
            Counter_Entfeuchtung_Freigabe = MIN(Counter_Entfeuchtung_Freigabe + sampling_time / 10.0, 10.0);
        }
        else
        {
            Counter_Entfeuchtung_Freigabe = MAX(Counter_Entfeuchtung_Freigabe - sampling_time / 300.0, 0.0);
        }
    }
    Freigabe_Entfeuchtung = Counter_Entfeuchtung_Freigabe >= 1.0 || (Freigabe_Entfeuchtung && Counter_Entfeuchtung_Freigabe > 0.0);

    // (* Freigabe Heizung *)
    if (Steuerkontakt_Standby || Istwert_Temperatur > 110.0)
    {
        Counter_Heizung_Freigabe = 0.0;
    }
    else
    {
        if (Stellgrad_Heizung < 0.0 || Stellgrad_Kuehlung > -2.0 || Counter_Tuer_geschlossen < 300.0)
        {
            Counter_Heizung_Freigabe = MIN(Counter_Heizung_Freigabe + sampling_time / 10.0, 10.0);
        }
        else
        {
            Counter_Heizung_Freigabe = MAX(Counter_Heizung_Freigabe - sampling_time / 300.0, 0.0);
        }
    }
    Freigabe_Heizung = Counter_Heizung_Freigabe >= 1.0 || (Freigabe_Heizung && Counter_Heizung_Freigabe > 0.0);
    // (* Freigabe Befeuchtung *)
    if (Steuerkontakt_Standby || Istwert_Temperatur > 110.0)
    {
        Counter_Befeuchtung_Freigabe = 0.0;
    }
    else
    {
        if (Stellgrad_Befeuchtung < 0.0 || Stellgrad_Entfeuchtung > -2.0 || Counter_Tuer_geschlossen < 300.0)
        {
            Counter_Befeuchtung_Freigabe = MIN(Counter_Befeuchtung_Freigabe + sampling_time / 10.0, 10.0);
        }
        else
        {
            Counter_Befeuchtung_Freigabe = MAX(Counter_Befeuchtung_Freigabe - sampling_time / 300.0, 0.0);
        }
    }
    Freigabe_Befeuchtung = Counter_Befeuchtung_Freigabe >= 1.0 || (Freigabe_Befeuchtung && Counter_Befeuchtung_Freigabe > 0.0);

    // Verdichterabscaltung

    if (!Startup_finished)
    {
        Drehzahl_Verdichter_adaptiv = Drehzahl_Verdichter_MAX;
    }
    else if (Temperatursprung)
    {
        Drehzahl_Verdichter_adaptiv = MAX(Drehzahl_Verdichter_Kuehlung, Drehzahl_Verdichter_Entfeuchtung);
    }
    else if (is_valid(Stellgrad_Kuehlung))
    {
        Drehzahl_Verdichter_adaptiv = limit(Drehzahl_Verdichter_adaptiv + (-Stellgrad_Kuehlung - 25.0) / 100.0 * (Drehzahl_Verdichter_MAX - Drehzahl_Verdichter_MIN) / (4.0 * 600.0),
                                            Drehzahl_Verdichter_MIN,
                                            Drehzahl_Verdichter_MAX);
    }

    Drehzahl_Verdichter_Kuehlung = Drehzahl_Verdichter_MIN + (MAX(-Stellgrad_Kuehlung_Mittelwert, -Stellgrad_Kuehlung) - 30.0) / (90.0 - 30.0) * (Drehzahl_Verdichter_MAX - Drehzahl_Verdichter_MIN);
    Drehzahl_Verdichter_Entfeuchtung = Drehzahl_Verdichter_MIN + 0.5 * (MAX(-Stellgrad_Entfeuchtung_Mittelwert, -Stellgrad_Entfeuchtung) -
                                                                             50.0) / (90.0 - 50.0) * (Drehzahl_Verdichter_MAX - Drehzahl_Verdichter_MIN);

    if (Verdichter)
    {
        Drehzahl_Verdichter = limit(MAX(Drehzahl_Verdichter_Kuehlung, MAX(Drehzahl_Verdichter_Entfeuchtung, Drehzahl_Verdichter_adaptiv)),
                                    Drehzahl_Verdichter_MIN,
                                    Drehzahl_Verdichter_MAX);
    }
    else
    {
        Drehzahl_Verdichter = 1000.0;
    }

    Stellgrad_Verdichter = Drehzahl_Verdichter / Drehzahl_Verdichter_MAX * 100.0 * (160.0 / 200.0);
    /*
    (* -------------------- *)
    (* Verflüssigerregelung *)
    (* -------------------- *) */

    Verfluessiger = Verfluessiger && ((Drehzahl_Verdichter_adaptiv - Drehzahl_Verdichter_MIN) / ((Drehzahl_Verdichter_MAX - Drehzahl_Verdichter_MIN) < 0.30) &&
                    (-Stellgrad_Kuehlung_Mittelwert < 30.0) &&
                    (-Stellgrad_Entfeuchtung_Mittelwert < 60.0) || ((Drehzahl_Verdichter_adaptiv - Drehzahl_Verdichter_MIN) /  (Drehzahl_Verdichter_MAX - Drehzahl_Verdichter_MIN) < 0.20) &&
                    (-Stellgrad_Kuehlung_Mittelwert < 20.0) &&
                     -Stellgrad_Entfeuchtung_Mittelwert < 50.0);

    }// Ende Feuchtemodul

    // ----------------------------------------
    // Werte die auf Veränderung geprüft werden
    // ----------------------------------------
    Sammelalarm_quittiert_alt       = Sammelalarm_quittiert;
    Istwert_Temperatur_alt          = Istwert_Temperatur;
    Regelabweichung_Temperatur_alt  = Regelabweichung_Temperatur;
    Sollwert_Temperatur_alt         = Sollwert_Temperatur;
    Istwert_Feuchte_alt             = Istwert_Feuchte;
    Regelabweichung_Feuchte_alt_alt = Regelabweichung_Feuchte_alt;
    Regelabweichung_Feuchte_alt     = Regelabweichung_Feuchte;
    Sollwert_Feuchte_alt            = Sollwert_Feuchte;
    Durchzug_alt                    = Durchzug;
    Regelabweichung_Tuer_alt        = Regelabweichung_Tuer;
    HeizungFeuchtemodul_alt         = HeizungFeuchtemodul;
    Regelabweichung_Befeuchtung_alt_alt = Regelabweichung_Befeuchtung_alt;
    Regelabweichung_Entfeuchtung_alt    = Regelabweichung_Entfeuchtung;



    // ----------------
    // Rückgabewerte
    // ----------------
    // F_Outputs out{};

    binOut.bool_out01 = (std::abs((Sollwert_Temperatur - Istwert_Temperatur)) > Temperaturband); // du nutzt Temperaturband-Alarm
    // separat; hier nur Beispiel
    binOut.bool_out02 = (std::abs((Sollwert_Feuchte - Istwert_Feuchte)) > Feuchteband) &&
                        (!Befeuchtung_aus) && (!Entfeuchtung_aus);
    binOut.bool_out03 = Befeuchtung_aus || Entfeuchtung_aus;
    binOut.bool_out07 = (!Status_Uebertemperatur) &&
                        (!(std::abs(Istwert_Ueberwachungsregler - Istwert_Temperatur) > 20.0));
    alarmOut.Startup  = Startup_finished;

    binOut.bool_out09 = HeizungKessel;
    binOut.bool_out11 = HeizungTuer;
    binOut.bool_out12 = HeizungKesselrand;
    binOut.bool_out13 = Verfluessiger;
    binOut.bool_out14 = Nachfuellen;
    binOut.bool_out16 = Entleerpumpe;
    binOut.bool_out17 = HeizungFeuchtemodul;
    binOut.bool_out20 = Abschlaemmventil;
    binOut.bool_out18 = Kuehlventil;
    binOut.bool_out19 = Entfeuchtungsventil;
    binOut.bool_out21 = Verdichter;
    binOut.bool_out23 = !Sammelalarm_quittiert;

    analogOut.real_out01 = Istwert_Temperatur;
    analogOut.real_out02 = Istwert_Temperatur;
    analogOut.real_out04 = Istwert_Temperatur;
    analogOut.real_out05 = limit(Istwert_Feuchte, 0.0, 100.0);
    analogOut.real_out06 = limit(Istwert_Feuchte, 0.0, 100.0);
    analogOut.real_out07 = Sollwert_Feuchte;
    analogOut.real_out08 = limit(Istwert_Feuchte, 0.0, 100.0);
    analogOut.real_out09 = Stellgrad_HeizungKessel;
    analogOut.real_out10 = Stellgrad_Pumpe;
    analogOut.real_out11 = Stellgrad_HeizungTuer;
    analogOut.real_out12 = Stellgrad_HeizungKesselrand;
    analogOut.real_out13 = Stellgrad_Feuchtemodul;
    analogOut.real_out14 = Stellgrad_Abschlaemmventil_zyklisch;
    analogOut.real_out16 = Temperatur_Feuchtemodul;
    analogOut.Stellgrad_Feuchtemodul = Stellgrad_Feuchtemodul;
    analogOut.real_out18 = Stellgrad_Kuehlventil;
    analogOut.real_out19 = Stellgrad_Entfeuchtungsventil;
    analogOut.real_out20 = Stellgrad_Luefter;
    analogOut.real_out21 = Stellgrad_Verdichter;
    analogOut.real_out25 = Schaltwert_Uebertemperatur;
    analogOut.real_out26 = Schaltwert_Untertemperatur;
    analogOut.Stellgrad_Heizung = Stellgrad_Heizung;
    analogOut.Stellgrad_Kuehlung = Stellgrad_Kuehlung;
    analogOut.Stellgrad_Befeuchtung = Stellgrad_Befeuchtung;
    analogOut.Stellgrad_Entfeuchtung = Stellgrad_Entfeuchtung;
    analogOut.Counter_Tuer = Counter_Tuer_offen;

    // alarms (wie ST)
    alarmOut.alarm_01 = Bandalarm_Temperatur;
    alarmOut.alarm_02 = Bandalarm_Feuchte;
    alarmOut.alarm_03 = Tueralarm;
    alarmOut.alarm_04 = false;
    alarmOut.alarm_05 = false;
    alarmOut.alarm_06 = Alarm_Wasser_Befeuchtungsmodul && !Befeuchtung_aus;
    alarmOut.alarm_07 = Alarm_Abwasser_Befeuchtungsmodul;
    alarmOut.alarm_08 = (Alarm_Heizung_Befeuchtungsmodul || Alarm_PT100_Befeuchtungsmodul) && !Befeuchtung_aus;

    // alarm_09..alarm_15 sind im ST mit Selbsthaltung/Quittierung etc.:
    alarmOut.alarm_09 = (((Istwert_Temperatur_Tuer < -50.0) || (Istwert_Temperatur_Tuer > 120.0)) &&
                         Startup_finished) ||(alarm_09 && Sammelalarm_quittiert);

    alarmOut.alarm_10 = (std::abs(Istwert_Ueberwachungsregler - Istwert_Temperatur) > 20.0) ||
                        ((((Istwert_Ueberwachungsregler < -50.0) ||
                           (Istwert_Ueberwachungsregler > 120.0) && Startup_finished))) ||
                        (alarm_10 && Sammelalarm_quittiert);

    alarmOut.alarm_11 = Status_Uebertemperatur || StatusQuittierung_Uebertemperatur;
    alarmOut.alarm_12 = Status_Untertemperatur || StatusQuittierung_Untertemperatur;

    alarmOut.alarm_13 = (((Istwert_Temperatur_Verdampferausgang < -50.0) || (Istwert_Temperatur_Verdampferausgang > 120.0)) && Startup_finished) ||
                        (alarm_13 && Sammelalarm_quittiert);

    alarmOut.alarm_14 = (Counter_Wasserkanister_leer > 10.0 && Startup_finished) || (alarm_14 && Sammelalarm_quittiert);

    alarmOut.alarm_15 = ((analogInput.real_in11 > Sollwert_Feuchte_Max && !Befeuchtung_aus) ||
                         (analogInput.real_in11 < Sollwert_Feuchte_Min && !Entfeuchtung_aus)) ||
                         (alarm_15 && Sammelalarm_quittiert);

    // Selbsthaltung der Alarm-Bits (wie ST-OR alarm_x ...)
    alarm_09 = alarmOut.alarm_09;
    alarm_10 = alarmOut.alarm_10;
    alarm_13 = alarmOut.alarm_13;
    alarm_14 = alarmOut.alarm_14;
    alarm_15 = alarmOut.alarm_15;

    // return out;
}

private:
    // ===== ST: VAR CONSTANT =====
    static constexpr double sampling_time = 0.25;

    static constexpr std::array<std::array<double, 13>, 4> Kennfeld_Ueberhitzung{
                                                                                 {// 0: Temperatur
                                                                                  std::array<double, 13>{-20.0, -10.0, 0.0, 10.0, 20.0, 30.0, 40.0, 50.0,
                                                                                                         60.0, 70.0, 80.0, 90.0, 100.0},
                                                                                  // 1: Skalierung Min
                                                                                  std::array<double, 13>{0.01, 0.02, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7,
                                                                                                         0.8, 0.9, 1.0, 1.0},
                                                                                  // 2: Skalierung Max
                                                                                  std::array<double, 13>{0.01, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
                                                                                                         1.0, 1.0, 1.0},
                                                                                  // 3: Sollwert Überhitzung
                                                                                  std::array<double, 13>{-6.0, -8.0, -10.0, -8.0, -6.0, -8.0, -10.0, -12.0,
                                                                                                         -16.0, -20.0, -24.0, -28.0, -32.0}}};
    static constexpr std::array<std::array<double, 13>, 4> Kennfeld_Entfeuchtung{
                                                                                 {// 0: Temperatur
                                                                                  std::array<double, 13>{-20.0, -10.0, 0.0, 10.0, 20.0, 30.0, 40.0, 50.0,
                                                                                                         60.0, 70.0, 80.0, 90.0, 100.0},
                                                                                  // 1: Skalierung Entfeuchtung
                                                                                  std::array<double, 13>{0.08, 0.08, 0.08, 0.10, 0.12, 0.14, 0.16, 0.18,
                                                                                                         0.20, 0.22, 0.24, 0.26, 0.28},
                                                                                  // 2: Abtauzyklus Pause
                                                                                  std::array<double, 13>{120.0, 120.0, 120.0, 120.0, 120.0, 90.0, 60.0,
                                                                                                         60.0, 60.0, 0.0, 0.0, 0.0, 0.0},
                                                                                  // 3: Abtauzyklus Dauer
                                                                                  std::array<double, 13>{60.0, 60.0, 60.0, 30.0, 20.0, 10.0, 8.0, 4.0, 2.0,
                                                                                                         0.0, 0.0, 0.0, 0.0}}};

    // Kennfelder (nur die, die oben genutzt werden; den Rest kannst du genauso
    // hinzufügen)
    static constexpr std::array<double, 13> Kennfeld_Regler_Heizung_Xp{
                                                                       0.2, 0.5, 0.8, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
    static constexpr std::array<double, 13> Kennfeld_Regler_Kuehlung_Xp{
                                                                        0.1, 0.1, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.0};
    static constexpr std::array<std::array<double, 9>, 9>
        Kennfeld_Regler_Befeuchtung_Xp{
                                       {std::array<double, 9>{1.00, 0.54, 0.31, 0.18, 0.11, 0.07, 0.05, 0.03,
                                                              0.02},
                                        std::array<double, 9>{1.00, 0.54, 0.31, 0.18, 0.11, 0.07, 0.05, 0.03,
                                                              0.02},
                                        std::array<double, 9>{1.00, 0.54, 0.31, 0.18, 0.11, 0.07, 0.05, 0.03,
                                                              0.02},
                                        std::array<double, 9>{1.00, 0.54, 0.31, 0.18, 0.11, 0.07, 0.05, 0.03,
                                                              0.02},
                                        std::array<double, 9>{1.00, 0.54, 0.31, 0.18, 0.11, 0.07, 0.05, 0.03,
                                                              0.02},
                                        std::array<double, 9>{1.00, 0.54, 0.31, 0.18, 0.11, 0.07, 0.05, 0.03,
                                                              0.02},
                                        std::array<double, 9>{1.00, 0.54, 0.31, 0.18, 0.11, 0.07, 0.05, 0.03,
                                                              0.02},
                                        std::array<double, 9>{1.00, 0.54, 0.31, 0.18, 0.11, 0.07, 0.05, 0.03,
                                                              0.02},
                                        std::array<double, 9>{1.00, 0.54, 0.31, 0.18, 0.11, 0.07, 0.05, 0.03,
                                                              0.02}}};
    static constexpr std::array<std::array<double, 9>, 9>
        Kennfeld_Regler_Entfeuchtung_Xp{
                                        {std::array<double, 9>{1.00, 0.80, 0.60, 0.50, 0.40, 0.35, 0.30, 0.25, 0.20},
                                         std::array<double, 9>{1.00, 0.80, 0.60, 0.50, 0.40, 0.35, 0.30, 0.25, 0.20},
                                         std::array<double, 9>{1.00, 0.80, 0.60, 0.50, 0.40, 0.35, 0.30, 0.25, 0.20},
                                         std::array<double, 9>{1.00, 0.80, 0.60, 0.50, 0.40, 0.35, 0.30, 0.25, 0.20},
                                         std::array<double, 9>{1.00, 0.80, 0.60, 0.50, 0.40, 0.35, 0.30, 0.25, 0.20},
                                         std::array<double, 9>{1.00, 0.80, 0.60, 0.50, 0.40, 0.35, 0.30, 0.25, 0.20},
                                         std::array<double, 9>{1.00, 0.80, 0.60, 0.50, 0.40, 0.35, 0.30, 0.25, 0.20},
                                         std::array<double, 9>{1.00, 0.80, 0.60, 0.50, 0.40, 0.35, 0.30, 0.25, 0.20},
                                         std::array<double, 9>{1.00, 0.80, 0.60, 0.50, 0.40, 0.35, 0.30, 0.25, 0.20}}};

    static constexpr std::array<std::array<double, 11>, 3>
        Kennfeld_Sollwert_Feuchte_Limit{
                                        {std::array<double, 11>{0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100},
                                         std::array<double, 11>{80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80},
                                         std::array<double, 11>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}};

    // Zeitkonstanten etc.
    static constexpr double Tau_Heizung_Tv = 10.0;

    // Faktoren
    static constexpr double F_Xp_Heizung = 3.8;
    static constexpr double F_Xp_Kuehlung = 33.6;
    static constexpr double F_Xp_Befeuchtung_BINDER = 131.0;
    static constexpr double F_Xp_Entfeuchtung = 100.0;
    static constexpr double F_Xp_Tuer = 10.0;

    // ===== ST: VAR (Zustände / Merker) =====
    // (ich halte hier nur die, die im übersetzten Teil genutzt werden; den Rest
    // kannst du 1:1 ergänzen)
    double Istwert_Temperatur{};
    double Istwert_Temperatur_alt{};
    double Sollwert_Temperatur{};
    double Sollwert_Temperatur_alt{};
    double Sollwert_Gegenbefeuchten = 0.0;
    double Regelabweichung_Befeuchtung_alt = 0.0;
    double P_Anteil_Befeuchtung = 0.0;
    double Counter_Entfeuchtung_Freigabe = 0.0;
    bool Freigabe_Entfeuchtung = false;
    double Counter_Heizung_Freigabe = 0.0;
    bool Freigabe_Heizung = false;
    double Counter_Befeuchtung_Freigabe = 0.0;
    bool Freigabe_Befeuchtung = false;
    bool Abtauzyklus = false;
    double Counter_Zyklus_Entleeren = 0.0;
    double Drehzahl_Verdichter_Kuehlung = 0.0;
    double Drehzahl_Verdichter_Entfeuchtung = 0.0;
    double Drehzahl_Verdichter = 0.0;

    /*PWM(double zyklus_min, double zyklus_max, double initial_zykluszeit = -1.0)
  noexcept : Zyklus(0.0), Ausgabe(0.0), Zykluszeit((initial_zykluszeit > 0.0) ?
  initial_zykluszeit : zyklus_max), Zyklus_MIN(zyklus_min),
      Zyklus_MAX(zyklus_max)
  {}

  bool step(double Stellgrad,double sampling_time,double Zyklus_MIN,double
  Zyklus_MAX){*/
    PWM PWM_Heizung_Kessel = {10.0, 600.0, 0.0};
    PWM PWM_Heizung_Tuer = {10.0, 600.0, 0.0};
    PWM PWM_Nachfuellen = {10.0, 600.0, 0.0};
    PWM PWM_Heizung_Kesselrand = {10.0, 600.0, 0.0};
    PWM PWM_Heizung_Feuchtemodul = {10.0, 600.0, 0.0};
    PWM PWM_Kuehlventil = {10.0, 600.0, 0.0};
    PWM PWM_Entfeuchtungsventil = {10.0, 600.0, 0.0};

    double Istwert_Ueberwachungsregler{};
    std::uint32_t Modus_Ueberwachungsregler{};
    double Offset_Ueberwachungsregler{};
    double Klasse_Ueberwachungsregler{};
    bool Wasserkanister_leer{};
    double Regler_Tuer_Tn = 700.0;
    double Regler_Tuer_Tv = 60.0;
    double Tau_Tuer_Tv = 60.0;
    double Regler_Entfeuchtung_MAX = 0.0;
    double Regler_Entfeuchtung_MIN = 0.0;
    double Regler_Entfeuchtung_I_Anteil_MAX = 0.0;
    double Regler_Entfeuchtung_I_Anteil_MIN = 0.0;
    bool Freigabe_Kuehlung = false;
    double Sollwert_Temperatur_Feuchtemodul_MIN = 110.0;
    double Sollwert_Temperatur_Feuchtemodul_MAX = 130.0;
    double Zyklus_Entleeren_Mittelwert = 600.0;

    double Grenzwert_Uebertemperatur{};
    double Schaltwert_Uebertemperatur{};
    bool Status_Uebertemperatur{};
    std::uint32_t Counter_Status_Uebertemperatur{};
    bool StatusQuittierung_Uebertemperatur{};

    double Grenzwert_Untertemperatur{};
    double Schaltwert_Untertemperatur{};
    bool Status_Untertemperatur{};
    std::uint32_t Counter_Status_Untertemperatur{};
    bool StatusQuittierung_Untertemperatur{};

    double Istwert_Feuchte{};
    double Istwert_Feuchte_alt{};
    double Sollwert_Feuchte{};
    double Sollwert_Feuchte_alt{};
    double Counter_Entleeren_Kessel{};

    bool Regelung_AUS{};
    double Counter_Regelung_AUS{};
    bool Steuerkontakt_Standby{};
    bool Sammelalarm{};
    bool Sammelalarm_quittiert{};
    bool Sammelalarm_quittiert_alt{};
    double Regler_Kuehlung_Tv = 5.0;
    double Tau_Kuehlung_Tv = 30.0;
    double Theta_I_Kuehlung = 1.0;

    // Feuchte absolut
    double p_H2Omax_Sollwert{};
    double p_H2Omax_Istwert{};
    double Taupunkt_Istwert{};
    double Sollwert_Feuchte_Absolut{};
    double Sollwert_Temperatur_Feuchtemodul{};
    double Counter_Kuehlung_Freigabe = 0.0;

    // Tür
    bool Tuer_offen{};
    double Counter_Tuer_offen{};
    double Counter_Tuer_geschlossen{};
    double Istwert_Temperatur_Tuer{};
    double Regelabweichung_Tuer{};
    double Regelabweichung_Tuer_alt{};
    double Abschlaemmintegral = 0.0;
    double Standby_Feuchtemodul = 0.0;
    double Regelabweichung_Befeuchtung_alt_alt = 0.0;
    double Regelabweichung_Entfeuchtung_alt = 0.0;
    double Durchflussmenge_Einlassventil_Feuchtemodul = 400.0;
    double Durchflussmenge_Abschlaemmventil_Feuchtemodul = 1500.0;

    // Startup / reset
    std::uint32_t Counter_reset_flag{};
    bool Startup_finished{};

    // Feuchte limit
    std::uint16_t Index_Sollwert_Feuchte_Limit{};
    double Interpolation_Sollwert_Feuchte_Limit{};
    double Sollwert_Feuchte_Min{};
    double Sollwert_Feuchte_Max{};

    // Abschaltung Feuchte-Regler
    bool Sollwert_Feuchte_aktiv{};
    bool Steuerkontakt_Befeuchtung_aus{};
    bool Steuerkontakt_Entfeuchtung_aus{};
    bool Befeuchtung_aus{};
    bool Entfeuchtung_aus{};
    double Sollwert_Gegenheizen = 2.0;
    // Parameter Indizes
    std::uint16_t Index_Feuchte{};
    std::uint16_t Index_Temperatur{};
    std::uint16_t Index_Temperatur_ohne_Feuchte{};

    // Reglerparameter
    double Regler_Heizung_Xp{};
    double Regler_Kuehlung_Xp{};
    double Regler_Befeuchtung_Xp{};
    double Regler_Entfeuchtung_Xp{};
    double Regler_Tuer_Xp{};
    double Regler_Befeuchtung_Tv = 30.0;
    double Regler_Befeuchtung_Tn = 100.0;
    double Regler_Befeuchtung_Tz = 1200.0;
    double Tau_Befeuchtung_Tv = 10.0;
    double Tau_Befeuchtung_Tz = 5.0;

    // Regelkern Temperatur
    double Sollwert_Temperatur_MIN{};
    double Sollwert_Temperatur_MAX{};

    double Regelabweichung_Temperatur{};
    double Regelabweichung_Temperatur_alt{};
    double Regelabweichung_Temperatur_Mittelwert{};
    double Standardregelabweichung_Temperatur{};
    double Regelabweichung_Temperatur_Gradient{};
    double Standardgradient_Regelabweichung_Temperatur{};
    double Zykluszeit_Regelabweichung_Temperatur{};
    double Amplitude_Regelabweichung_Temperatur{};
    double Zykluszeit_Regelabweichung_Heizung{};
    double Zykluszeit_Regelabweichung_Kuehlung{};
    double Regelabweichung_Kuehlung_Gradient{};
    double Regelabweichung_Kuehlung_alt{};
    double Faktor_Kuehlung{};
    double Faktor_Gegenheizen_Band{};
    double I_Anteil_Kuehlung{};
    double D_Anteil_Kuehlung{};
    double Regelabweichung_Befeuchtung_Gradient{};
    double I_Anteil_Befeuchtung{};
    double Istwert_Temperatur_Verdampferausgang_PT1 = 0.0;
    double D_Anteil_Befeuchtung{};
    bool Temperatursprung{};
    double Counter_Temperatursprung{};
    double Faktor_Entfeuchtung{};
    double Faktor_Gegenbefeuchten_Band = 5.0;
    double I_Anteil_Entfeuchtung = 0.0;
    double D_Anteil_Entfeuchtung = 0.0;
    double I_Anteil_Tuer = 0.0;
    double P_Anteil_Tuer;
    double Regler_Heizung_MIN{};
    double Regler_Heizung_MAX{};
    double Regler_Heizung_I_Anteil_MIN{};
    double Regler_Heizung_I_Anteil_MAX{};
    double P_Anteil_Heizung{};
    double I_Anteil_Heizung{};
    double D_Anteil_Heizung{};
    double Stellgrad_Heizung{};
    double Stellgrad_Heizung_Mittelwert{};
    double Regelabweichung_Entfeuchtung_Gradient = 0.0;
    double D_Anteil_Tuer = 0.0;
    double Counter_Verdichter_aus = 0.0;
    double Counter_Freigabe_Abtauzyklus = 0.0;
    double Counter_Abtauzyklus;
    double Stellgrad_Feuchtemodul_MAX{};
    double Skalierung_Nachfuellen_Temperatur_I_Anteil{};
    double Counter_Zyklus_Abschlaemmventil = 0.0;
    const double Regler_Kuehlung_Tn = 200.0;

    double Stellgrad_Feuchtemodul_Boost{};
    const double Temperatur_Feuchtemodul_MAX = 200.0;

    // Feuchte - nur Platzhalter (weil spätere Ausdrücke referenzieren)
    double Regelabweichung_Feuchte{};
    double Regelabweichung_Feuchte_alt{};
    double Regelabweichung_Feuchte_alt_alt{};
    double Regelabweichung_Feuchte_Mittelwert{};
    double Standardregelabweichung_Feuchte{};
    double Regelabweichung_Feuchte_Gradient{};
    double Standardgradient_Regelabweichung_Feuchte{};
    double Zykluszeit_Regelabweichung_Feuchte{};
    double Amplitude_Regelabweichung_Feuchte{};
    bool Feuchtesprung{};
    double Counter_Feuchtesprung = 0.0;
    double Offset_Regelabweichung_Befeuchtung{};
    double Offset_Regelabweichung_Entfeuchtung{};
    double Stellgrad_Kuehlung{};
    double Stellgrad_Kuehlung_Mittelwert{};
    double Stellgrad_Befeuchtung{};
    double Stellgrad_Befeuchtung_Mittelwert{};
    double Stellgrad_Befeuchtung_Standby{};
    double Stellgrad_Entfeuchtung{};
    double Stellgrad_Entfeuchtung_Mittelwert{};
    double Z_Anteil_Befeuchtung{};

    // Aktoren/Signale (Defaults, bis restliche Logik ergänzt ist)
    bool HeizungKessel{};
    double Stellgrad_HeizungKessel{};
    bool HeizungTuer{};
    double Stellgrad_HeizungTuer{};
    bool HeizungKesselrand{};
    double Stellgrad_HeizungKesselrand{};
    bool Verfluessiger{};
    bool Nachfuellen{};
    bool Entleerpumpe{};
    bool HeizungFeuchtemodul{};
    bool HeizungFeuchtemodul_alt{};
    bool Abschlaemmventil{};
    double Stellgrad_Abschlaemmventil_zyklisch{};
    bool Kuehlventil{};
    double Stellgrad_Kuehlventil{};
    bool Entfeuchtungsventil{};
    double Stellgrad_Entfeuchtungsventil{};
    bool Verdichter{};
    double Stellgrad_Verdichter{};
    double Stellgrad_Pumpe{};
    double Stellgrad_Luefter{};
    double Stellgrad_Betauungsschutz{};
    double Temperatur_Feuchtemodul_Gradient = 0.0;
    double Stellgrad_Feuchtemodul_MAX_adaptiv = 0.0;
    double Counter_Nachlauf_Entleeren = 0.0;
    double Counter_Pause_Entleeren = 0.0;

    // “Durchzug” (wird später in Überhitzungslogik gesetzt)
    bool Durchzug{};
    bool Durchzug_alt{};
    double Counter_Durchzug{};

    // Befeuchtungsmodul Zustände (Platzhalter, damit “Werte die auf Veränderung
    // geprüft werden” passt)
    double Temperatur_Feuchtemodul{};
    double Temperatur_Feuchtemodul_alt{};
    double Stellgrad_Feuchtemodul{};
    double Stellgrad_Feuchtemodul_Mittelwert{};
    double Stellgrad_Feuchtemodul_PT1{};
    double Stellgrad_Feuchtemodul_Nachfuellen{};
    bool Entleeren{};
    bool Entleeren_alt{};
    bool Entleerbehaelter_Oben{};
    bool Entleerbehaelter_Unten{};
    double Counter_Wasserkanister_leer{};
    double Regler_Entfeuchtung_Tv = 5.0;
    double Regler_Entfeuchtung_Tn = 200.0;
    double Tau_Entfeuchtung_Tv = 60.0;
    double Theta_I_Entfeuchtung = 0.1;
    double C_Entfeuchtung = 200.0;
    double U_Entfeuchtung = 0.4;
    double P_Entfeuchtung = 500.0;
    double Regler_Entfeuchten_I_Anteil_MIN = 0.0;
    double Drehzahl_Verdichter_MIN = 1800.0;
    double Drehzahl_Verdichter_MAX = 4500.0;
    double Drehzahl_Verdichter_adaptiv = 0.0;

    // Alarmbits mit Selbsthaltung
    bool alarm_09{};
    bool alarm_10{};
    bool alarm_13{};
    bool alarm_14{};
    bool alarm_15{};
    bool Alarm_Wasser_Befeuchtungsmodul{};
    bool Alarm_Abwasser_Befeuchtungsmodul{};
    bool Alarm_PT100_Befeuchtungsmodul{};
    bool Alarm_Heizung_Befeuchtungsmodul{};
    bool Bandalarm_Temperatur{};
    bool Bandalarm_Feuchte{};
    bool Tueralarm{};
};

// namespace UTILITY_FUNCTIONS

// double PT100_ResistanceToTemperature_Linear(double R)
// {
//     constexpr double R0 = 100.0;
//     constexpr double alpha = 0.00385; // IEC-Näherung

//     return (R - R0) / (R0 * alpha);
// }

class PT1 {
public:
    PT1() = default;
    void initialize(double fs, double t_cutoff) {
        fs_ = fs;
        ts_ = 1 / fs_;
        double f_cutoff = 1. / t_cutoff;
        tau_ = 1 / (2 * PI_CONST * f_cutoff);
        lambda_ = 2 * tau_ / ts_;
        b0_ = k_ / (1. + lambda_);
        b1_ = k_ / (1. + lambda_);
        a0_ = (1. - lambda_) / (1. + lambda_);
    };

    double step(double x) {

        double y = b0_ * x + s1_;
        // s1 = b1*x - a1*y
        s1_ = b1_ * x - a0_ * y;
        return y;
    };

private:
    double s1_ = 0.;
    double fs_ = 1.;
    double ts_ = 1.;
    double tau_ = 1.;
    double a0_ = 0.;
    double b0_ = 0.;
    double b1_ = 0.;
    double k_ = 1.;
    double lambda_ = 1.;
};

#ifdef BUILD_TARGET_SOM
namespace appengine::state {
class IStateVariable;
}
#endif

#ifdef BUILD_TARGET_SOM
class Controller : public ControllerBase {
public:
    bool initialize(const char *configFilePath,
                    appengine::IAppLogic *appLogic) override;
    void step() override;
    void terminate() override;

private:
    appengine::IAppLogic *appLogic_{nullptr};

    // Beispiel: du wirst für diese ST-Logik sehr viele StateVars mappen;
    // hier nur exemplarisch:
    std::shared_ptr<appengine::state::IStateVariable> tempVariable_;
    std::shared_ptr<appengine::state::IStateVariable> tempSetPointVariable_;

    // TODO: bool_inXX / real_inXX / bool_outXX / real_outXX / alarm_XX als
    // StateVariables holen (oder je nach AppEngine: arrays/IO mapping)

    ClimateAlgorithm algo_{};
    PT1 FILTERED_TEMP{};
    PT1 FILTER_TUER{};
    // Eingänge
    std::shared_ptr<appengine::state::IStateVariable> const_duty_cycle_;
    std::shared_ptr<appengine::state::IStateVariable> const_freq_;
    std::shared_ptr<appengine::state::IStateVariable> Wasserstand_Oben_Config_;
    std::shared_ptr<appengine::state::IStateVariable> Wasserstand_Unten_Config_;
    std::shared_ptr<appengine::state::IStateVariable> Fuellstand_Config_;
    std::shared_ptr<appengine::state::IStateVariable> SV_Tuer_Variable_;
    std::shared_ptr<appengine::state::IStateVariable> SV_Verdampferausgang_;
    std::shared_ptr<appengine::state::IStateVariable> SV_Befeuchtungsmodul_;
    std::shared_ptr<appengine::state::IStateVariable> SV_Verdichter_on_;
    std::shared_ptr<appengine::state::IStateVariable> Startup_;
    std::shared_ptr<appengine::state::IStateVariable> CounterTuer_;

    // ausgänge
    std::vector<appengine::asio::SignalConnectionPtr> subscriptions_{};
    std::shared_ptr<appengine::state::IStateVariable> Heizung_Bef_Config_;
    std::shared_ptr<appengine::state::IStateVariable> MV_Kuehlung_Config_;
    std::shared_ptr<appengine::state::IStateVariable> Abschlaemmen_Config_;
    std::shared_ptr<appengine::state::IStateVariable> Entleerpumpe_Config_;
    std::shared_ptr<appengine::state::IStateVariable> Heizung_Kesselrand_Config_;
    std::shared_ptr<appengine::state::IStateVariable>
        Freigabe_Verfluessigerluefter_Config_;
    std::shared_ptr<appengine::state::IStateVariable> Heizung_Innenraum_Config_;
    std::shared_ptr<appengine::state::IStateVariable> Heizung_Tuer_Config_;
    std::shared_ptr<appengine::state::IStateVariable> Uebertemperatur_Config_;
    std::shared_ptr<appengine::state::IStateVariable> MV_Entfeuchtung_Config_;
    std::shared_ptr<appengine::state::IStateVariable> MV_Druckluft_Config_;
    std::shared_ptr<appengine::state::IStateVariable>
        Ansteuerung_Wassereinlass_Config_;
    std::shared_ptr<appengine::state::IStateVariable>
        Ansteuerung_Verdichter_Config_;
    std::shared_ptr<appengine::state::IStateVariable> Verdichter_Duty_Config;
    std::shared_ptr<appengine::state::IStateVariable> Luefter_Innenraum_Config_;

    std::shared_ptr<appengine::state::IStateVariable> SV_Stellgrad_Heizung;
    std::shared_ptr<appengine::state::IStateVariable> SV_Stellgrad_Kuehlung;
    std::shared_ptr<appengine::state::IStateVariable> SV_Stellgrad_Befeuchtung;
    std::shared_ptr<appengine::state::IStateVariable> SV_Stellgrad_Entfeuchtung;
    // Regelgrößen
    std::shared_ptr<appengine::state::IStateVariable> SV_Sollwert_Temperatur_;
    std::shared_ptr<appengine::state::IStateVariable> SV_Temperatur_Variable_;
    std::shared_ptr<appengine::state::IStateVariable> SV_Sollwert_Feuchte_;
    std::shared_ptr<appengine::state::IStateVariable> SV_Feuchte_Variable_;

    // std::vector<appengine::asio::SignalConnectionPtr> subscriptions_{};
};

extern "C" BINDER_SIMULINK_API_EXPORT ControllerBase *create();

#else
class Controller {
public:
    Controller(){
        inputs=nullptr;
    };
    ~Controller(){};
    bool initialize(const char *configFilePath);
    bool initialize( QList<Input_t>* inputs);
    bool initialize( DataModel* instance);
    void step(void);
    void terminate();
    void PID_Temperature(void);

private:

    DataModel* model;
    QList<Input_t>* inputs;

    void stepSOM(void);
    void stepDesk(void);


#if 0
  appengine::IAppLogic *appLogic_{nullptr};

  // Beispiel: du wirst für diese ST-Logik sehr viele StateVars mappen;
  // hier nur exemplarisch:
  std::shared_ptr<appengine::state::IStateVariable> tempVariable_;
  std::shared_ptr<appengine::state::IStateVariable> tempSetPointVariable_;

  // TODO: bool_inXX / real_inXX / bool_outXX / real_outXX / alarm_XX als StateVariables holen
  // (oder je nach AppEngine: arrays/IO mapping)

  ClimateAlgorithm algo_{};
  PT1 FILTERED_TEMP{} ;
  PT1 FILTER_TUER{};
  // Eingänge
  std::shared_ptr<appengine::state::IStateVariable> const_duty_cycle_;
  std::shared_ptr<appengine::state::IStateVariable> const_freq_;
  std::shared_ptr<appengine::state::IStateVariable> Wasserstand_Oben_Config_;
  std::shared_ptr<appengine::state::IStateVariable> Wasserstand_Unten_Config_;
  std::shared_ptr<appengine::state::IStateVariable> Fuellstand_Config_;
  std::shared_ptr<appengine::state::IStateVariable> SV_Tuer_Variable_;
  std::shared_ptr<appengine::state::IStateVariable> SV_Verdampferausgang_;
  std::shared_ptr<appengine::state::IStateVariable> SV_Befeuchtungsmodul_;
  std::shared_ptr<appengine::state::IStateVariable> SV_Verdichter_on_;
  std::shared_ptr<appengine::state::IStateVariable> Startup_;
  std::shared_ptr<appengine::state::IStateVariable> CounterTuer_;

  // ausgänge
  std::vector<appengine::asio::SignalConnectionPtr> subscriptions_{};
  std::shared_ptr<appengine::state::IStateVariable> Heizung_Bef_Config_;
  std::shared_ptr<appengine::state::IStateVariable> MV_Kuehlung_Config_;
  std::shared_ptr<appengine::state::IStateVariable> Abschlaemmen_Config_;
  std::shared_ptr<appengine::state::IStateVariable> Entleerpumpe_Config_;
  std::shared_ptr<appengine::state::IStateVariable> Heizung_Kesselrand_Config_;
  std::shared_ptr<appengine::state::IStateVariable> Freigabe_Verfluessigerluefter_Config_;
  std::shared_ptr<appengine::state::IStateVariable> Heizung_Innenraum_Config_;
  std::shared_ptr<appengine::state::IStateVariable> Heizung_Tuer_Config_;
  std::shared_ptr<appengine::state::IStateVariable> Uebertemperatur_Config_;
  std::shared_ptr<appengine::state::IStateVariable> MV_Entfeuchtung_Config_;
  std::shared_ptr<appengine::state::IStateVariable> MV_Druckluft_Config_;
  std::shared_ptr<appengine::state::IStateVariable> Ansteuerung_Wassereinlass_Config_;
  std::shared_ptr<appengine::state::IStateVariable> Ansteuerung_Verdichter_Config_;
  std::shared_ptr<appengine::state::IStateVariable> Verdichter_Duty_Config;
  std::shared_ptr<appengine::state::IStateVariable> Luefter_Innenraum_Config_;

  std::shared_ptr<appengine::state::IStateVariable> SV_Stellgrad_Heizung;
  std::shared_ptr<appengine::state::IStateVariable> SV_Stellgrad_Kuehlung;
  std::shared_ptr<appengine::state::IStateVariable> SV_Stellgrad_Befeuchtung;
  std::shared_ptr<appengine::state::IStateVariable> SV_Stellgrad_Entfeuchtung;
  // Regelgrößen
  std::shared_ptr<appengine::state::IStateVariable> SV_Sollwert_Temperatur_;
  std::shared_ptr<appengine::state::IStateVariable> SV_Temperatur_Variable_;
  std::shared_ptr<appengine::state::IStateVariable> SV_Sollwert_Feuchte_;
  std::shared_ptr<appengine::state::IStateVariable> SV_Feuchte_Variable_;

  //std::vector<appengine::asio::SignalConnectionPtr> subscriptions_{};
#endif
};

#endif

