-- ======================
-- Description
-- ======================
--
-- Wodny grzebieniowy GWC, pojedynczy, bez recyrkulacji.
--
-- ======================
-- Parameters
-- ======================
--
-- SubLogic supports following variables:
--
-- input.t.outdoor.value
-- input.t.outdoor.value.mode
-- input.t.outdoor.err                     - temperatura zewnętrzna
--
-- input.t.vent.supply.value
-- input.t.vent.supply.value.mode
-- input.t.vent.supply.err                 - temperatura powietrza nawiewanego za GWC
--
-- input.t.ghe.inner.value
-- input.t.ghe.inner.value.mode
-- input.t.ghe.inner.err                   - temperatura złoża GWC
--
-- input.t.water.value
-- input.t.water.value.mode
-- input.t.water.err                       - temperatura wody zraszającej
--
-- input.water.level                       - czujnik poziomu wody (0 - OK; 1 - wysoki poziom wody)
-- input.water.level.mode                  - tryb pracy wyjścia (0 - AUTO; 1 - MANU)
--
-- input.water.pulse                       - aktualny przepływ wody zraszającej (impulsy)
--
-- input.output.water.valve.mode
-- input.output.signal.vent.disable.mode   - tryb pracy wyjścia (0 - AUTO; 1 - MANU)
--
-- output.water.valve                      - elektrozawór polewania złoża wodą (0 - zamknięty; 1 - otwarty)
--
-- output.signal.vent.disable              - sygnał wyłączenia nawiewu (ochrona przed zamarzaniem złoża zimą) (1 - wstrzymaj nawiew)
--
-- setting.logic.delay                     - czas zwłoki działania logiki. Dopiero po tym czasie logika zacznie pracować [s]
--
-- setting.kontrola_naw                    - czy ma być realizowana kontrola nawiewu (nastawa użytkownika) [0..1]
--
-- setting.kontrola_zlo                    - czy ma być realizowana kontrola złoża (nastawa użytkownika) [0..1]
--
-- setting.T_lato                          - od kiedy lato
--
-- setting.T_nawiew_lato                   - nawiew latem
--
-- setting.Nawiew_lato                     - czas pomiędzy zraszaniami lato [min]
--
-- setting.Zraszanie_lato                  - ilość wody do zraszania lato [l]
--
-- setting.Regeneracja_lato                - ilość wody do regeneracji latem [l]
--
-- setting.T_upal                          - od kiedy upal
--
-- setting.T_nawiew_upal                   - nawiew jak upał
--
-- setting.Nawiew_upal                     - czas pomiędzy zraszaniami upał [min]
--
-- setting.Zraszanie_upal                  - ilość wody do zraszania upał [l]
--
-- setting.Regeneracja_upal                - ilość wody do regeneracji upał [l]
--
-- setting.T_zima                          - od kiedy zima
--
-- setting.T_nawiew_zima                   - nawiew jak zima
--
-- setting.Nawiew_zima                     - czas pomiędzy zraszaniami zima [min]
--
-- setting.Zraszanie_zima                  - ilość wody do zraszania zimą [l]
--
-- setting.Regeneracja_zima                - ilość wody do regeneracji zimą [l]
--
-- setting.T_mroz                          - od kiedy mróz
--
-- setting.T_nawiew_mroz                   - nawiew jak mróz
--
-- setting.Nawiew_mroz                     - czas pomiędzy zraszaniami mróz [min]
--
-- setting.Zraszanie_mroz                  - ilość wody do zraszania mróz [l]
--
-- setting.Regeneracja_mroz                - ilość wody do regeneracji mróz [l]
--
-- setting.Histereza_mroz                  - histereza powiązana z odmrazaniem złoża [*C]
--
-- setting.T_zamarzaGWC                    - temperatura złoża poniżej której może zamarzać
--
-- setting.Pauza_zima                      - pauza po odmrażaniu przed włączeniem nawiewu [min]
--
-- setting.CzasMAX                         - po tym czasie alarm braku wody [min]
--
-- setting.Wsp_przeplywu                   - ile impulsów na jeden litr z przepływomierza [imp/l]
--
-- setting.reset.woda_zuzycie              - resetuje licznik zużycia wody
--
-- setting.reset.alert.przeciek            - resetuje alarm "alert.przeciek"
--
-- setting.reset.alert.historyczne_zalane_zloze - resetuje alarm "alert.historyczne_zalane_zloze"
--
-- setting.alert.woda_przeciek_max         - maksymalna dopuszczalna wartość licznika "counter.woda_przeciek", powyżej aktywowany jest alarm "counter.alert.przeciek" [l]
--
-- counter.logic.delay                     - licznik czas zwłoki działania logiki. [s]
--
-- counter.aktualnySezon                   - aktualny sezon:
--                                           0 - przejściowy
--                                           1 - lato
--                                           2 - upał
--                                           3 - zima
--                                           4 - mróz
--
-- counter.aktualnyTryb                   - aktualny tryb:
--                                           0 - brak pracy
--                                           1 - lato (cykl główny)
--                                           2 - regeneracja lato
--                                           3 - zraszanie lato
--                                           4 - zima (cykl główny)
--                                           5 - regeneracja zima
--                                           6 - zraszanie zima
--                                           7 - odmrażanie
--                                           8 - pauza po odmrażaniu
--
-- counter.demand.t_kryt                   - wymagana temperatura nawiewu od której uruchomiamy regenerację - właściwy dla lato/upał/zima/mróz [min]
--
-- counter.demand.okres_nawiewu            - wymagany czas nawiewu (bez zraszania) - właściwy dla lato/upał/zima/mróz [min]
--
-- counter.demand.zraszanie                - wymagana ilość wody do zraszania [l] - właściwy dla lato/upał/zima/mróz [min]
--
-- counter.demand.regeneracja              - wymagana ilość wody do regeneracji [l] - właściwy dla lato/upał/zima/mróz [min]
--
-- counter.regeneracja_dodatek             - dodatkowa ilość wody dla regeneracji złoża (na podstawie temp. zewnętrznej) [l]
--
-- counter.czas_nawiewu                    - czas, który upłynął od rozpoczęcia nawiewu [min]
--
-- counter.czas_zawor                      - czas, który upłynął od włączenie elektrozaworu [min]
--
-- counter.woda_przeplyw_l                 - pomiar przepływu wody litry=impulsy / wsp. przepływu
--
-- counter.woda_cykl_zuzycie_imp           - licznik zużycia wody w aktualnym cyklu (jeżeli zakończono cykl to w ostatnim cyklu) [impulsy]
--
-- counter.woda_cykl_zuzycie_l             - licznik zużycia wody w aktualnym cyklu (jeżeli zakończono cykl to w ostatnim cyklu) [l]
--
-- counter.woda_zuzycie_imp                - licznik zużycia wody (sumarycznie) [impulsy]
--
-- counter.woda_zuzycie_l                  - licznik zużycia wody (sumarycznie) [l]
--
-- counter.woda_przeciek_imp               - licznik zużycia wody od ostatniego zamknięcia zaworu (wartość zerowana przy otwartym zaworze) [impulsy]
--
-- counter.woda_przeciek_l                 - licznik zużycia wody od ostatniego zamknięcia zaworu (wartość zerowana przy otwartym zaworze) [l]
--
-- counter.t.water                         - temperatura wody (uaktualniana tylko podczas przepływu wody)
--
-- counter.kontrola_naw                    - czy ma być realizowana kontrola nawiewu w bieżącym cyklu [0..1]
--
-- counter.kontrola_zlo                    - czy ma być realizowana kontrola złoża w bieżącym cyklu [0..1]
--
-- counter.Pauza_zima                      - licznik: pauza po odmrażaniu przed włączeniem nawiewu [min]
--
-- counter.alert.brak_wody                 - alarm aktywowany jeżeli po otwarciu zaworu przez "setting.CzasMAX" nie osiągnięto założonych parametrów
--                                           "Za mały przepływ wody! Przez czas [counter.czas_zawor] minut przepłynęło tylko [counter.woda_cykl_zuzycie_l] litrów wody. Sprawdź pompę i zasilanie wody."
--
-- counter.alert.przeciek                  - alarm "Nieszczelny zawór! Mimo zamkniętego zaworu przepłynęło [przeciek] litrów wody. Sprawdź działanie elektrozaworu."
--
-- counter.alert.brak_regeneracji_nawiew   - alarm aktywowany jeżeli "counter.woda_cykl_zuzycie_l" jest większy niż "counter.demand.regeneracja"
--                                           oraz "input.t.vent.supply.value" nie osiągnęła wartości "setting.t_kryt"
--                                           Komunikat: "Słaba regeneracja złoża. Pomimo zroszenia złoża [counter.woda_cykl_zuzycie_l] litrami wody,
--                                           temperatura nawiewu [input.t.vent.supply.value] *C  nie osiągnęła wymaganej [setting.t_kryt] *C."
--
-- counter.alert.odmrazanie_zloza          - alarm nieudanego odmrażania złoża
--                                           Komunikat: "Bardzo słaba regeneracja złoża. Pomimo zroszenia złoża [counter.woda_cykl_zuzycie_l] litrami wody,
--                                           temperatura złoża GWC [input.t.ghe.inner.value] *C jest poniżej minimalnej [setting.T_zamarzaGWC] *C".
--
-- counter.alert.zalane_zloze              - alarm "input.water.level" == 1: "Złoże GWC jest zalane wodą. Zbyt wolne wsiąkanie wody w grunt." (przerwanie pracy)
--
-- counter.alert.historyczne_zalane_zloze  - wystąpił w przeszłości alarm "counter.alert.zalane_zloz".: "Złoże GWC było przez chwilę zalane wodą. Zbyt wolne wsiąkanie wody w grunt."
--
--
-- Logic uses lua language to implement own behaviour
--
-- ======================
-- mandatory variables
-- ======================
--
-- Logic expects following mandatory variables:
--
-- reload.trigger                          - causes reloading lua script
--
-- memcnt                                  - current amount of memory used by lua in bytes
--
-- Logic expects following kv settings:
--
-- LuaScriptPath                           - path to the lua script - must be absolute
--
-- ======================
-- ChangeLog
-- ======================
--
-- 2024-05-11 ver 1.0.0
--
-- # First release
--
-- user can use some functions provided by ibmanager.
-- user can use some functions provided by ibmanager.
-- ibmanager provides following functions to use:
--
-- function returns value of required ibmanager variable
-- @param fullName - string - variable name - name of variable of which value must be returned, for example "rs.0.id.255.input.t.0.value"
-- @return         - string or integer - variable value or "nil" if variable not exist or is not readable
--
-- getValue(fullName)
-- function set value of given ibmanager variable
-- @param fullName - string - variable name - name of variable of which value we want to set, for example "rs.0.id.255.input.t.0.value"
-- @param value    - string, int or boolean - value to set
-- @return         - nothing
--
-- setValue(fullName, value)
-- function returns value of required ibmanager variable
-- @param key      - placed in xml logic configuration file:
--                   * as attribute: Name, in Var, RemoteVar and ImportVar elements in the case of stand alone variables
--                   * as concatenation of two attributes: ListName.Postfix in VarListItem, RemoteVarListItem and ImportVarListItem elements
--                     in the case of variables that are placed in lists
-- @return         - string or integer - variable value or "nil" if variable not exist or is not readable
--
-- getLogicValue(key)
-- function set value of given ibmanager variable
-- @param key      - placed in xml logic configuration file:
--                   * as attribute: Name, in Var, RemoteVar and ImportVar elements in the case of stand alone variables
--                   * as concatenation of two attributes: ListName.Postfix in VarListItem, RemoteVarListItem and ImportVarListItem elements
--                     in the case of variables that are placed in lists
-- @param value    - string, int or boolean - value to set
-- @return         - nothing
--
-- setLogicValue(key, value)
-- function returns two sections of kvsettings from xml configuration file
-- returned value is two element table, each of these elements is table too.
-- indices of returned table are strings and equal "instance" and "global"
-- values of returned tables are tables and contain KVsettings for applicable section.
-- nested tables have form key = value, where key is index in nested table and value is value.
-- example: {"instance" = {"ikey1" = "ivalue1", "ikey2" = "gvalue2"}, "global" = {"gkey1" = "gvalue1", "gkey2" = "gvalue2", "gkey3" = "gvalue3"}}
-- @return          - two dimensional array - kvsettings for global and instance sections
-- getKvSettings()
-- function schedules alert to send.
-- rules are defined in separated alert configuration files and are described in ibmanager instruction manual
-- @param id - alert identifier - must be defined in current logic configuration file in section: <Alert Id="any_identifier" ...
-- scheduleAlert(id)
-- function cancels alert sending, if was previously scheduled. if not then only wakes up alerts handling thread, so if there is no need to call this function, then do not call it.
-- rules are defined in separated alert configuration files and are described in ibmanager instruction manual
-- @param id - alert identifier - must be defined in current logic configuration file in section: <Alert Id="any_identifier" ...
-- cancelAlert(name)
-- function returns table, containing Variables that belongs to required list.
-- @param listName - Name attribute of VarList, RemoteVarList or ImportVarList elements in configurationfile
-- @return array of key-value pairs. Key - variable postfix, Value - Variable value
-- getVarList(listName)
-- function returns monotonic system clock value, that elapsed since specific epoch
-- returned time is expressed in milliseconds.
-- getClock()
-- function logs message to file, if defined in configuration file log level is less than passed to this function
-- @param logLevel - one of:
--        LogLevel.TraceLo
--        LogLevel.Trace
--        LogLevel.TraceHi
--        LogLevel.DebugLo
--        LogLevel.Debug
--        LogLevel.DebugHi
--        LogLevel.Info
--        LogLevel.Notice
--        LogLevel.Warning
--        LogLevel.Error
--        LogLevel.Critical
-- @param logMessage - string to log
-- log(logLevel, logMessage)
-- ibmanager provides following global variables:
-- logic type, (in this case it will always be "Lua") - the same as in logic configuration file in section: <Logic Type="Lua" ...
-- LOGIC_TYPE
-- logic version, the same as in logic configuration file in section: <Logic ... Version="x.y.z" ...
-- LOGIC_VERSION
-- logic sub-type, the same as in logic configuration file in section: <Logic ... SubType="Hysteresis" ...
-- LOGIC_SUBTYPE
-- logic sub-version, the same as in logic configuration file in section: <Logic ... SubVersion="x.y.z" ...
-- LOGIC_SUBVERSION
-- logic instance name - the same as in logic configuration file, in section: <Instance Name="0">
-- LOGIC_INSTANCE_NAME
-- add script directory to package path
package.path = package.path .. ";./logic/scripts/utils/?.lua"

-- use script - without .lua extension - delta class
require("State")
require("Counter")
require("DownCounter");

-- elektrozawór polewania złoża wodą (0 - zamknięty; 1 - otwarty)
OUTPUTWATERVALVE = nil

-- sygnał wyłączenia nawiewu (ochrona przed zamarzaniem złoża zimą) (1 - wstrzymaj nawiew)
OUTPUTSIGNALVENTDISABLE = nil

-- aktualny sezon:
-- 0 - przejściowy
-- 1 - lato
-- 2 - upał
-- 3 - zima
-- 4 - mróz
COUNTERAKTUALNYSEZON = nil

-- aktualny tryb:
-- 0 - brak pracy
-- 1 - lato (cykl główny)
-- 2 - regeneracja lato
-- 3 - zraszanie lato
-- 4 - zima (cykl główny)
-- 5 - regeneracja zima
-- 6 - zraszanie zima
-- 7 - odmrażanie
-- 8 - pauza po odmrażaniu

COUNTERAKTUALNYTRYB = nil

-- licznik (downcounter) zwłoki do rpopoczęcia działania logiki [s]
DOWNCOUNTERLOGICDELAY = nil;

-- czas, który upłynął od rozpoczęcia nawiewu [s]
COUNTERCZASNAWIEWU = nil

-- czas, który upłynął od włączenie elektrozaworu [s]
COUNTERCZASZAWOR = nil

-- licznik zużycia wody w aktualnym cyklu (jeżeli zakończono cykl to w ostatnim cyklu) [impulsy]
COUNTERWODACYKLZUZYCIE_IMP = 0

-- licznik przecieków wody (przy zamkniętym zaworze) [impulsy]
COUNTERWODAPRZECIEK_IMP = 0

-- licznik (downcounter) 1s cyklu
DOWNCOUNTER_1S = nil

-- licznik (downcounter) pauzy po odmrażaniu przed włączeniem nawiewu [min]
DOWNCOUNTERPAUZAZIMA = nil

SUPPORTED_SUBLOGIC_TYPE = "GHE03"
SUPPORTED_SUBLOGIC_VERSION = "1.0.0"
VERSIONCHECKED = false

-- entry point to the logic
-- @param firstCall - tells if logic is called first time
-- @return        - nothing
function onLogicCall(firstCall)
    if not VERSIONCHECKED then
        -- checking sublogic type and sublogic version
        if LOGIC_SUBTYPE ~= SUPPORTED_SUBLOGIC_TYPE then
            error("Wrong logic sub-type. expected " .. SUPPORTED_SUBLOGIC_TYPE .. " but used " .. LOGIC_SUBTYPE)
        end
        local versionWithoutBuild = string.match(LOGIC_SUBVERSION, "[0-9]+%.[0-9]+%.[0-9]+")
        if versionWithoutBuild ~= SUPPORTED_SUBLOGIC_VERSION then
            error(
                "Wrong logic sub-version. expected " .. SUPPORTED_SUBLOGIC_VERSION .. " but used " .. LOGIC_SUBVERSION
            )
        end
        VERSIONCHECKED = true
    end

    local inputTOutdoorValue = getLogicValue("input.t.outdoor.value")
    local inputTOutdoorErr = getLogicValue("input.t.outdoor.err")
    local inputTVentSupplyValue = getLogicValue("input.t.vent.supply.value")
    local inputTVentSupplyErr = getLogicValue("input.t.vent.supply.err")
    local inputTGheInnerValue = getLogicValue("input.t.ghe.inner.value")
    local inputTGheInnerErr = getLogicValue("input.t.ghe.inner.err")
    local inputTWaterValue = getLogicValue("input.t.water.value")
    local inputTWaterErr = getLogicValue("input.t.water.err")
    local inputWaterLevel = getLogicValue("input.water.level")
    local inputWaterPulse = getLogicValue("input.water.pulse")

    local settingLogicDelay = getLogicValue("setting.logic.delay");
    local settingKontrolaNaw = getLogicValue("setting.kontrola_naw")
    local settingKontrolaZlo = getLogicValue("setting.kontrola_zlo")
    local settingTLato = getLogicValue("setting.T_lato")
    local settingTNawiewLato = getLogicValue("setting.T_nawiew_lato")
    local settingNawiewLato = getLogicValue("setting.Nawiew_lato")
    local settingZraszanieLato = getLogicValue("setting.Zraszanie_lato")
    local settingRegeneracjaLato = getLogicValue("setting.Regeneracja_lato")
    local settingTUpal = getLogicValue("setting.T_upal")
    local settingTNawiewUpal = getLogicValue("setting.T_nawiew_upal")
    local settingNawiewUpal = getLogicValue("setting.Nawiew_upal")
    local settingZraszanieUpal = getLogicValue("setting.Zraszanie_upal")
    local settingRegeneracjaUpal = getLogicValue("setting.Regeneracja_upal")
    local settingTZima = getLogicValue("setting.T_zima")
    local settingTNawiewZima = getLogicValue("setting.T_nawiew_zima")
    local settingNawiewZima = getLogicValue("setting.Nawiew_zima")
    local settingZraszanieZima = getLogicValue("setting.Zraszanie_zima")
    local settingRegeneracjaZima = getLogicValue("setting.Regeneracja_zima")
    local settingTMroz = getLogicValue("setting.T_mroz")
    local settingTNawiewMroz = getLogicValue("setting.T_nawiew_mroz")
    local settingNawiewMroz = getLogicValue("setting.Nawiew_mroz")
    local settingZraszanieMroz = getLogicValue("setting.Zraszanie_mroz")
    local settingRegeneracjaMroz = getLogicValue("setting.Regeneracja_mroz")
    local settingTZamarzaGWC = getLogicValue("setting.T_zamarzaGWC")
    local settingHisterezaMroz = getLogicValue("setting.Histereza_mroz")
    local settingPauzaZima = getLogicValue("setting.Pauza_zima")
    local settingCzasMAX = getLogicValue("setting.CzasMAX")
    local settingWspPrzeplywu = getLogicValue("setting.Wsp_przeplywu")
    local settingResetWodaZuzycie = getLogicValue("setting.reset.woda_zuzycie")
    local settingResetAlertPrzeciek = getLogicValue("setting.reset.alert.przeciek")
    local settingResetAlertHistoryczneZalaneZloze = getLogicValue("setting.reset.alert.historyczne_zalane_zloze")
    local settingAlertWodaPrzeciekMax = getLogicValue("setting.alert.woda_przeciek_max")

    local counterWodaZuzycie_imp = getLogicValue("counter.woda_zuzycie_imp")

    local counterDemandTKryt = 0
    local counterDemandOkresNawiewu = 0
    local counterDemandZraszanie = 0
    local counterDemandRegeneracja = 0
    local counterCzasNawiewu = 0
    local counterCzasZawor = 0
    local counterKontrolaNaw = 0
    local counterKontrolaZlo = 0
    local counterRegeneracjaDodatek = 0
    local counterPauzaZima = 0

    -- Test: 60 * 1000 to minuty; 1 * 1000 to sekundy
    local globalTimerFactor = 60 * 1000

    -- states

    -- OUTPUTWATERVALVE - stan
    if OUTPUTWATERVALVE == nil or firstCall then
        OUTPUTWATERVALVE = State.create(0, 0, 1, true)
    end
    OUTPUTWATERVALVE:call()

    -- OUTPUTSIGNALVENTDISABLE - stan
    if OUTPUTSIGNALVENTDISABLE == nil or firstCall then
        OUTPUTSIGNALVENTDISABLE = State.create(0, 0, 1, true)
    end
    OUTPUTSIGNALVENTDISABLE:call()

    -- COUNTERAKTUALNYSEZON - stan
    if COUNTERAKTUALNYSEZON == nil or firstCall then
        COUNTERAKTUALNYSEZON = State.create(0, 0, 4, true)
    end
    COUNTERAKTUALNYSEZON:call()

    -- COUNTERAKTUALNYTRYB - stan
    if COUNTERAKTUALNYTRYB == nil or firstCall then
        COUNTERAKTUALNYTRYB = State.create(0, 0, 8, true)
    end
    COUNTERAKTUALNYTRYB:call()

    -- counters

    -- DOWNCOUNTERLOGICDELAY - downcounter
    if DOWNCOUNTERLOGICDELAY == nil or firstCall then
        DOWNCOUNTERLOGICDELAY = DownCounter.create();
    end
    DOWNCOUNTERLOGICDELAY:updateParams(settingLogicDelay * 1000);

    counterLogicDelay = DOWNCOUNTERLOGICDELAY:timeTo0() / 1000;
    counterLogicDelay = (counterLogicDelay < 0) and 0 or counterLogicDelay;

    if (counterLogicDelay == 0) then
        -- COUNTERCZASNAWIEWU - counter
        if COUNTERCZASNAWIEWU == nil or firstCall then
            COUNTERCZASNAWIEWU = Counter:create();
        end
        counterCzasNawiewu = COUNTERCZASNAWIEWU:elapsed() / globalTimerFactor;

        -- COUNTERCZASZAWOR - counter
        if COUNTERCZASZAWOR == nil or firstCall then
            COUNTERCZASZAWOR = Counter:create();
        end
        counterCzasZawor = COUNTERCZASZAWOR:elapsed() / globalTimerFactor;

        -- DOWNCOUNTER_1S - downcounter
        if DOWNCOUNTER_1S == nil or firstCall then
            DOWNCOUNTER_1S = DownCounter.create();
            DOWNCOUNTER_1S:updateParams(1000);
        end

        -- DOWNCOUNTERPAUZAZIMA - downcounter
        if DOWNCOUNTERPAUZAZIMA == nil or firstCall then
            DOWNCOUNTERPAUZAZIMA = DownCounter.create();
            DOWNCOUNTERPAUZAZIMA:updateParams(0);
        end
        counterPauzaZima = DOWNCOUNTERPAUZAZIMA:timeTo0() / globalTimerFactor;
        counterPauzaZima = (counterPauzaZima < 0) and 0 or counterPauzaZima;

        -- resetowanie licznika zużycia wody
        if settingResetWodaZuzycie == 1 then
            counterWodaZuzycie_imp = 0
            setLogicValue("setting.reset.woda_zuzycie", 0)
            setLogicValue("counter.woda_zuzycie_imp", counterWodaZuzycie_imp);
        end

        -- resetowanie alarmu przecieku wody
        if settingResetAlertPrzeciek == 1 then
            setLogicValue("counter.alert.przeciek", 0)
            setLogicValue("setting.reset.alert.przeciek", 0)
            -- zeruj licznik ilości wody dla przecieku
            COUNTERWODAPRZECIEK_IMP = 0
        end

        -- resetowanie alarmu historycznego zalane złoże
        if settingResetAlertHistoryczneZalaneZloze == 1 then
            setLogicValue("counter.alert.historyczne_zalane_zloze", 0)
            setLogicValue("setting.reset.alert.historyczne_zalane_zloze", 0)
        end

        -- upływa licznik 1s
        if DOWNCOUNTER_1S:elapsed() then
            -- aktualizacja zużycia wody (sumarycznie)
            counterWodaZuzycie_imp = counterWodaZuzycie_imp + inputWaterPulse;
            -- zapisz zużycie wody (sumarycznie)
            setLogicValue("counter.woda_zuzycie_imp", counterWodaZuzycie_imp);

            -- aktualizacja przepływu wody (cykl)
            if OUTPUTWATERVALVE:getValue() == 1 then
                COUNTERWODACYKLZUZYCIE_IMP = COUNTERWODACYKLZUZYCIE_IMP + inputWaterPulse;
            end

            -- aktualizacja przepływu dla przecieku wody (przy zamkniętym zaworze)
            if OUTPUTWATERVALVE:getValue() == 0 then
                COUNTERWODAPRZECIEK_IMP = COUNTERWODAPRZECIEK_IMP + inputWaterPulse;
            end

            DOWNCOUNTER_1S:updateParams(1000);
            DOWNCOUNTER_1S:reset();
        end

        -- aktualny przepływ wody z impulsów na litry
        local counterWodaPrzeplywL = inputWaterPulse / settingWspPrzeplywu
        -- zużycie wody w aktualnym cyklu z impulsów na litry
        local counterWodaCyklZuzycie_l = COUNTERWODACYKLZUZYCIE_IMP / settingWspPrzeplywu
        -- zużycie wody sumarycznie z impulsów na litry
        local counterWodaZuzycie_l = counterWodaZuzycie_imp / settingWspPrzeplywu
        -- zużycie przecieku wody z impulsów na litry
        local counterWodaPrzeciek_l = COUNTERWODAPRZECIEK_IMP / settingWspPrzeplywu

        -- jeżeli jakikolwiek błąd temperatury
        if inputTOutdoorErr ~= 0 or inputTVentSupplyErr ~= 0 or inputTGheInnerErr ~= 0 or inputTWaterErr ~= 0 then
            -- zamknij zawór
            OUTPUTWATERVALVE:setValue(0)
            -- włącz sygnał blokowania nawiewu
            OUTPUTSIGNALVENTDISABLE:setValue(1)
            -- ustaw tryb na brak pracy
            COUNTERAKTUALNYTRYB:setValue(0)
        end

        -- alarm braku wody
        if getLogicValue("counter.alert.brak_wody") == 1 then
            -- zamknij zawór
            OUTPUTWATERVALVE:setValue(0)
            -- włącz sygnał blokowania nawiewu
            OUTPUTSIGNALVENTDISABLE:setValue(1)
            -- ustaw tryb na brak pracy
            COUNTERAKTUALNYTRYB:setValue(0)
        end

        -- alarm przecieku wody
        if counterWodaPrzeciek_l >= settingAlertWodaPrzeciekMax then
            -- ustaw alarm przecieku wody
            setLogicValue("counter.alert.przeciek", 1)
            -- zamknij zawór
            OUTPUTWATERVALVE:setValue(0)
            -- włącz sygnał blokowania nawiewu
            OUTPUTSIGNALVENTDISABLE:setValue(1)
            -- ustaw tryb na brak pracy
            COUNTERAKTUALNYTRYB:setValue(0)
        end

        -- alarm odmrażania złoża
        if getLogicValue("counter.alert.odmrazanie_zloza") == 1 then
            -- zamknij zawór
            OUTPUTWATERVALVE:setValue(0)
            -- włącz sygnał blokowania nawiewu
            OUTPUTSIGNALVENTDISABLE:setValue(1)
            -- ustaw tryb na brak pracy
            COUNTERAKTUALNYTRYB:setValue(0)
        end

        -- alarm zalane złoże
        if inputWaterLevel == 1 then
            -- ustaw alarm zalane złoże
            setLogicValue("counter.alert.zalane_zloze", 1)
            -- zamknij zawór
            OUTPUTWATERVALVE:setValue(0)
            -- włącz sygnał blokowania nawiewu
            OUTPUTSIGNALVENTDISABLE:setValue(1)
            -- ustaw tryb na brak pracy
            COUNTERAKTUALNYTRYB:setValue(0)
        else
            if getLogicValue("counter.alert.zalane_zloze") == 1 then
                -- ustaw alarm historyczne zalane złoże
                setLogicValue("counter.alert.historyczne_zalane_zloze", 1)
            end

            -- zresetuj alarm zalane złoże
            setLogicValue("counter.alert.zalane_zloze", 0)
        end


        -- aktualizuj temperaturę wody (tylko podczas przepływu wody)
        if inputWaterPulse > 0 then
            setLogicValue("counter.t.water", inputTWaterValue)
        end

        -- jeżeli nastawa użytkownika "kontroli nawiewu" nieaktywna lub aktywny alarm braku regeneracji nawiew to nie kontroluj temperatury nawiewu
        if settingKontrolaNaw == 0 or getLogicValue("counter.alert.brak_regeneracji_nawiew") == 1 then
            counterKontrolaNaw = 0
        else
            counterKontrolaNaw = 1
        end

        -- jeżeli nastawa użytkownika "kontroli złoża" nieaktywna to nie kontroluj temperatury złoża
        if settingKontrolaZlo == 0 then
            counterKontrolaZlo = 0
        else
            counterKontrolaZlo = 1
        end

        -- ustalaj sezon tylko gdy aktualny tryb to: 0 - Brak pracy
        if COUNTERAKTUALNYTRYB:getValue() == 0 then
            -- ustalenie sezonu
            if inputTOutdoorValue >= settingTLato then
                if inputTOutdoorValue >= settingTUpal then
                    -- upał
                    COUNTERAKTUALNYSEZON:setValue(2)
                else
                    -- lato
                    COUNTERAKTUALNYSEZON:setValue(1)
                end
            elseif inputTOutdoorValue < settingTZima then
                if inputTOutdoorValue < settingTMroz then
                    -- mróz
                    COUNTERAKTUALNYSEZON:setValue(4)
                else
                    -- zima
                    COUNTERAKTUALNYSEZON:setValue(3)
                end
            else
                -- przejściowy
                COUNTERAKTUALNYSEZON:setValue(0)
            end
        end

        if COUNTERAKTUALNYSEZON:getValue() == 1 then
            -- lato
            counterDemandTKryt = settingTNawiewLato
            counterDemandOkresNawiewu = settingNawiewLato
            counterDemandZraszanie = settingZraszanieLato
            counterDemandRegeneracja = settingRegeneracjaLato
        elseif COUNTERAKTUALNYSEZON:getValue() == 2 then
            -- upał
            counterDemandTKryt = settingTNawiewUpal
            counterDemandOkresNawiewu = settingNawiewUpal
            counterDemandZraszanie = settingZraszanieUpal
            counterDemandRegeneracja = settingRegeneracjaUpal
        elseif COUNTERAKTUALNYSEZON:getValue() == 3 then
            -- zima
            counterDemandTKryt = settingTNawiewZima
            counterDemandOkresNawiewu = settingNawiewZima
            counterDemandZraszanie = settingZraszanieZima

            -- dodatek do regeneracji złoża obliczany na podstawie temp. zewnętrznej
            -- jeżeli temperatura powyżej 0*C to dodatek 0, jeżeli poniżej 0*C to dodatek = regeneracja*t_zew/10
            if inputTOutdoorValue < 0 then
                counterRegeneracjaDodatek = settingRegeneracjaZima * -(inputTOutdoorValue / 100)
            else
                counterRegeneracjaDodatek = 0
            end

            counterDemandRegeneracja = settingRegeneracjaZima + counterRegeneracjaDodatek
        elseif COUNTERAKTUALNYSEZON:getValue() == 4 then
            -- mróz
            counterDemandTKryt = settingTNawiewMroz
            counterDemandOkresNawiewu = settingNawiewMroz
            counterDemandZraszanie = settingZraszanieMroz

            -- dodatek do regeneracji złoża obliczany na podstawie temp. zewnętrznej
            -- jeżeli temperatura powyżej 0*C to dodatek 0, jeżeli poniżej 0*C to dodatek = regeneracja*t_zew/10
            if inputTOutdoorValue < 0 then
                counterRegeneracjaDodatek = settingRegeneracjaMroz * -(inputTOutdoorValue / 100)
            else
                counterRegeneracjaDodatek = 0
            end

            counterDemandRegeneracja = settingRegeneracjaMroz + counterRegeneracjaDodatek
        else
            -- przejściowy
            counterDemandTKryt = 0
            counterDemandOkresNawiewu = 0
            counterDemandZraszanie = 0
            counterDemandRegeneracja = 0
        end


        -- jeżeli tryb to 0 - Brak pracy
        if COUNTERAKTUALNYTRYB:getValue() == 0 then
            -- zamknij zawór
            OUTPUTWATERVALVE:setValue(0)
            -- wyłącz sygnał blokowania nawiewu
            OUTPUTSIGNALVENTDISABLE:setValue(0)
            -- wyzeruj czas nawiewu
            COUNTERCZASNAWIEWU:reset()

            -- zresetuj licznik zużycia wody w aktualnym cyklu
            COUNTERWODACYKLZUZYCIE_IMP = 0

            -- jeżeli sezon to lato lub upał
            if COUNTERAKTUALNYSEZON:getValue() == 1 or COUNTERAKTUALNYSEZON:getValue() == 2 then
                -- zmień tryb na lato (cykl główny)
                COUNTERAKTUALNYTRYB:setValue(1)
            end

            -- jeżeli sezon to zima lub mróz
            if COUNTERAKTUALNYSEZON:getValue() == 3 or COUNTERAKTUALNYSEZON:getValue() == 4 then
                -- zmień tryb na zima (cykl główny)
                COUNTERAKTUALNYTRYB:setValue(4)
            end
        end

        -- jeżeli tryb to 1 - lato (cykl główny)
        if COUNTERAKTUALNYTRYB:getValue() == 1 then
            -- jeżeli kontrola nawiewu w tym cyklu i temperatura nawiewu większa niż temperatura krytyczna to ustaw tryb na regeneracja lato
            if counterKontrolaNaw == 1 and inputTVentSupplyValue >= counterDemandTKryt then
                COUNTERAKTUALNYTRYB:setValue(2)
            end

            -- jeżeli czas nawiewu większy niż okres nawiewu to ustaw tryb na zraszanie lato
            if counterCzasNawiewu >= counterDemandOkresNawiewu then
                COUNTERAKTUALNYTRYB:setValue(3)
            end
        end

        -- jeżeli tryb to 2 - regeneracja lato
        if COUNTERAKTUALNYTRYB:getValue() == 2 then
            -- otwórz zawór
            OUTPUTWATERVALVE:setValue(1)

            -- jeżeli zużycie wody w aktualnym cyklu większe niż wymagane do regeneracji
            if counterWodaCyklZuzycie_l >= counterDemandRegeneracja then
                -- jeżeli temperatura nawiewu spadła do wartości krytycznej lub niżej to zakończ regenerację
                if inputTVentSupplyValue <= counterDemandTKryt then
                    -- skasuj alarm braku regeneracji nawiewu
                    setLogicValue("counter.alert.brak_regeneracji_nawiew", 0)
                    -- zamknij zawór
                    OUTPUTWATERVALVE:setValue(0)
                    -- zakończ regenerację, tyb na 1 - lato (cykl główny)
                    COUNTERAKTUALNYTRYB:setValue(1)
                else
                    -- ustaw alarm braku regeneracji nawiew
                    setLogicValue("counter.alert.brak_regeneracji_nawiew", 1)
                    -- zamknij zawór
                    OUTPUTWATERVALVE:setValue(0)
                    -- zakończ regenerację, tyb na 1 - lato (cykl główny)
                    COUNTERAKTUALNYTRYB:setValue(1)
                end
            else
                -- jeżeli zawór jest otwarty i upłynął czas większy niż czas maksymalny to ustaw alarm braku wody
                if counterCzasZawor >= settingCzasMAX then
                    -- ustaw alarm braku wody
                    setLogicValue("counter.alert.brak_wody", 1)
                    -- ustaw tryb na brak pracy
                    COUNTERAKTUALNYTRYB:setValue(0)
                end
            end
        end

        -- jeżeli tryb to 3 - zraszanie lato
        if COUNTERAKTUALNYTRYB:getValue() == 3 then
            -- otwórz zawór
            OUTPUTWATERVALVE:setValue(1)

            -- jeżeli zroszono złoże odpowiednią ilością wody to zakończ zraszanie
            if counterWodaCyklZuzycie_l >= counterDemandZraszanie then
                -- zamknij zawór
                OUTPUTWATERVALVE:setValue(0)
                -- zakończ zraszanie, tyb na 0 - brak pracy
                COUNTERAKTUALNYTRYB:setValue(0)
            else
                -- jeżeli zawór jest otwarty i upłynął czas większy niż czas maksymalny to ustaw alarm braku wody
                if counterCzasZawor >= settingCzasMAX then
                    -- ustaw alarm braku wody
                    setLogicValue("counter.alert.brak_wody", 1)
                    -- ustaw tryb na brak pracy
                    COUNTERAKTUALNYTRYB:setValue(0)
                end
            end
        end

        -- jeżeli tryb to 4 - zima (cykl główny)
        if COUNTERAKTUALNYTRYB:getValue() == 4 then
            -- jeżeli kontrola złoża w tym cyklu i temperatura złoża mniejsza niż temperatura zamarzania to ustaw tryb na odmrażanie
            if counterKontrolaZlo == 1 and inputTGheInnerValue <= settingTZamarzaGWC then
                COUNTERAKTUALNYTRYB:setValue(7)
            end

            -- jeżeli kontrola nawiewu w tym cyklu i temperatura nawiewu większa niż temperatura krytyczna to ustaw tryb na regeneracja zima
            if counterKontrolaNaw == 1 and inputTVentSupplyValue <= counterDemandTKryt then
                COUNTERAKTUALNYTRYB:setValue(5)
            end

            -- jeżeli czas nawiewu większy niż okres nawiewu to ustaw tryb na zraszanie zima
            if counterCzasNawiewu >= counterDemandOkresNawiewu then
                COUNTERAKTUALNYTRYB:setValue(6)
            end
        end

        -- jeżeli tryb to 5 - regeneracja zima
        if COUNTERAKTUALNYTRYB:getValue() == 5 then
            -- otwórz zawór
            OUTPUTWATERVALVE:setValue(1)

            -- jeżeli zużycie wody w aktualnym cyklu większe niż wymagane do regeneracji
            if counterWodaCyklZuzycie_l >= counterDemandRegeneracja then
                -- jeżeli temperatura nawiewu wzrosła powyżej wartości krytycznej to zakończ regenerację
                if inputTVentSupplyValue >= counterDemandTKryt then
                    -- skasuj alarm braku regeneracji nawiewu
                    setLogicValue("counter.alert.brak_regeneracji_nawiew", 0)
                    -- zamknij zawór
                    OUTPUTWATERVALVE:setValue(0)
                    -- zakończ regenerację, tyb na 4 - zima (cykl główny)
                    COUNTERAKTUALNYTRYB:setValue(4)
                else
                    -- ustaw alarm braku regeneracji nawiew
                    setLogicValue("counter.alert.brak_regeneracji_nawiew", 1)
                    -- zamknij zawór
                    OUTPUTWATERVALVE:setValue(0)
                    -- zakończ regenerację, tyb na 4 - zima (cykl główny)
                    COUNTERAKTUALNYTRYB:setValue(4)
                end
            else
                -- jeżeli zawór jest otwarty i upłynął czas większy niż czas maksymalny to ustaw alarm braku wody
                if counterCzasZawor >= settingCzasMAX then
                    -- ustaw alarm braku wody
                    setLogicValue("counter.alert.brak_wody", 1)
                    -- ustaw tryb na brak pracy
                    COUNTERAKTUALNYTRYB:setValue(0)
                end
            end
        end

        -- jeżeli tryb to 6 - zraszanie zima
        if COUNTERAKTUALNYTRYB:getValue() == 6 then
            -- otwórz zawór
            OUTPUTWATERVALVE:setValue(1)

            -- jeżeli zroszono złoże odpowiednią ilością wody to zakończ zraszanie
            if counterWodaCyklZuzycie_l >= counterDemandZraszanie then
                -- zamknij zawór
                OUTPUTWATERVALVE:setValue(0)
                -- zakończ zraszanie, tyb na 0 - brak pracy
                COUNTERAKTUALNYTRYB:setValue(0)
            else
                -- jeżeli zawór jest otwarty i upłynął czas większy niż czas maksymalny to ustaw alarm braku wody
                if counterCzasZawor >= settingCzasMAX then
                    -- ustaw alarm braku wody
                    setLogicValue("counter.alert.brak_wody", 1)
                    -- ustaw tryb na brak pracy
                    COUNTERAKTUALNYTRYB:setValue(0)
                end
            end
        end

        -- jeżeli tryb to 7 - odmrażanie
        if COUNTERAKTUALNYTRYB:getValue() == 7 then
            -- sygnał blokady wentylacji
            OUTPUTSIGNALVENTDISABLE:setValue(1)

            -- otwórz zawór
            OUTPUTWATERVALVE:setValue(1)

            -- jeżeli zawór jest otwarty i upłynął czas większy niż czas maksymalny to ustaw alarm braku wody
            if counterCzasZawor >= settingCzasMAX then
                -- ustaw alarm braku wody
                setLogicValue("counter.alert.brak_wody", 1)
                -- ustaw tryb na brak pracy
                COUNTERAKTUALNYTRYB:setValue(0)
            end

            -- jeżeli ilość wody w cyklu większa niż dwukrotność wymaganej do regeneracji to alarm odmrażania
            if counterWodaCyklZuzycie_l >= counterDemandRegeneracja * 2 then
                -- zamknij zawór
                OUTPUTWATERVALVE:setValue(0)
                -- ustaw alarm odmrażania
                setLogicValue("counter.alert.odmrazanie_zloza", 1)
                -- zakończ odmrażanie, tyb na 0 - brak pracy
                COUNTERAKTUALNYTRYB:setValue(0)
            end

            -- jeżeli temperatura złoża wzrosła powyżej wartości zamarzania z uwzględnieniem histerezy to zakończ odmrażanie
            if inputTGheInnerValue >= settingTZamarzaGWC + settingHisterezaMroz then
                -- zamknij zawór
                OUTPUTWATERVALVE:setValue(0)
                -- zresetuj licznik pauzy zimowej
                DOWNCOUNTERPAUZAZIMA:updateParams(settingPauzaZima * globalTimerFactor);
                DOWNCOUNTERPAUZAZIMA:reset();

                -- zakończ odmrażanie, tyb na 8 - pauza po odmrażaniu
                COUNTERAKTUALNYTRYB:setValue(8)
            end
        end

        -- jeżeli tryb to 8 - pauza po odmrażaniu
        if COUNTERAKTUALNYTRYB:getValue() == 8 then
            -- jeżeli licznik pauzy zimowej zakończył odliczanie to
            if DOWNCOUNTERPAUZAZIMA:elapsed() then
                -- zresetuj licznik pauzy zimowej
                DOWNCOUNTERPAUZAZIMA:updateParams(0);
                DOWNCOUNTERPAUZAZIMA:reset();

                -- wyłącz blokadę wentylacji
                OUTPUTSIGNALVENTDISABLE:setValue(0)

                -- zakończ pauzę po odmrażaniu, tyb na 0 - brak pracy
                COUNTERAKTUALNYTRYB:setValue(0)
            end
        end

        -- jeżeli zawór zamknięty to
        if OUTPUTWATERVALVE:getValue() == 0 then
            -- zresetuj licznik czasu zaworu
            COUNTERCZASZAWOR:reset()
        end

        -- jeżeli stan zaworu uległ zmianie na otwarty to zresetuj licznik zużycia wody w aktualnym cyklu
        if OUTPUTWATERVALVE:isChanged() == 1 and OUTPUTWATERVALVE:getValue() == 1 then
            COUNTERWODACYKLZUZYCIE_IMP = 0
        end

        -- jeżeli stan zaworu uległ zmianie na otwarty to zresetuj licznik przecieku wody
        if OUTPUTWATERVALVE:isChanged() == 1 and OUTPUTWATERVALVE:getValue() == 1 then
            COUNTERWODAPRZECIEK_IMP = 0
        end

        -- wyłącz sygnał blokowania nawiewu jeżeli wcześniej nie został włączony
        OUTPUTSIGNALVENTDISABLE:setValue(0)

        -- send logic variables to ibmanager
        setLogicValue("output.water.valve", OUTPUTWATERVALVE:getValue())
        setLogicValue("output.signal.vent.disable", OUTPUTSIGNALVENTDISABLE:getValue())

        setLogicValue("counter.aktualnySezon", COUNTERAKTUALNYSEZON:getValue())
        setLogicValue("counter.aktualnyTryb", COUNTERAKTUALNYTRYB:getValue())
        setLogicValue("counter.demand.t_kryt", counterDemandTKryt)
        setLogicValue("counter.demand.okres_nawiewu", counterDemandOkresNawiewu)
        setLogicValue("counter.demand.zraszanie", counterDemandZraszanie)
        setLogicValue("counter.demand.regeneracja", counterDemandRegeneracja)
        setLogicValue("counter.regeneracja_dodatek", counterRegeneracjaDodatek)
        setLogicValue("counter.czas_nawiewu", counterCzasNawiewu)
        setLogicValue("counter.czas_zawor", counterCzasZawor)
        setLogicValue("counter.woda_przeplyw_l", counterWodaPrzeplywL)
        setLogicValue("counter.woda_cykl_zuzycie_imp", COUNTERWODACYKLZUZYCIE_IMP)
        setLogicValue("counter.woda_cykl_zuzycie_l", counterWodaCyklZuzycie_l)
        setLogicValue("counter.woda_zuzycie_imp", counterWodaZuzycie_imp)
        setLogicValue("counter.woda_zuzycie_l", counterWodaZuzycie_l)
        setLogicValue("counter.woda_przeciek_imp", COUNTERWODAPRZECIEK_IMP)
        setLogicValue("counter.woda_przeciek_l", counterWodaPrzeciek_l)
        setLogicValue("counter.kontrola_naw", counterKontrolaNaw)
        setLogicValue("counter.kontrola_zlo", counterKontrolaZlo)
        setLogicValue("counter.Pauza_zima", counterPauzaZima)
    end

    setLogicValue("counter.logic.delay", counterLogicDelay);
end
