--    ======================
--    Description
--    ======================
--
--    Logika sterująca rekuperatorem.
--   
--    Osobny wentylator nawiewny i wywiewny, każdy sterowany proporcjonalnie. Dowolna ilość nastawialnych poziomów wentylacji.
--    
--    Obsługa okapu.
--
--    Obsługa trybu override (obejście).
--
--    Obsługa bypassu. Sterowanie bypassem polega na przepływie powietrza przez wymiennik (odbiór ciepła lub chłodu z powietrza wywiewanego) 
--    lub pomijając go (powietrze nawiewane wprost z powietrza wchodzącego na rekuperator). System ustawia tak bypass aby powietrze 
--    nawiewane miało wyższe parametry (ogrzewanie pasywne) lub niższe parametry (chłodzenie pasywne).
--
--    Tryb ogrzewania i chłodzenia przez wentylację.
--
--    W okresie zimowym jest możliwe tylko ogrzewanie pasywne.
--
--    W okresie letnim jest możliwe chłodzenie pasywne lub ogrzewanie pasywne. Jeżeli temperatura wewnętrzna jest powyżej temperatury 
--    minimalnej to następuje chłodzenie pasywne. Jeżeli temperatura wewnętrzna jest poniżej temperatury minimalnej to zaprzestawane 
--    jest chłodzenie pasywne a bypass jest wysterowany tak aby podczas wentylacji wybierać cieplejsze źródło.
--
--
--    ======================
--    Parameters
--    ======================
--
--    SubLogic supports following variables:
--
--    input.inside.t.value
--    input.inside.t.err                      - temperatura wewnętrzna
--
--    input.vent.in.t.value
--    input.vent.in.t.err                     - temperatura powietrza wejścia na rekuperator (z GWC lub czerpni)
--
--    input.vent.exhaust.t.value
--    input.vent.exhaust.t.err                - temperatura powietrza wywiewanego
--
--    input.outdoor.t.value
--    input.outdoor.t.err                     - temperatura zewnętrzna
--
--    input.season                            - 0 - winter, 1 - summer [0..1]
--
--    input.override                          - sygnał tymczasowego obejścia wentylacji [0 - brak sygnału; różne od 0 - jest sygnał]
--
--    input.hood                              - sygnał z okapu (1 - ma być realizowana funkcja okapu) [0 - brak sygnału; różne od 0 - jest sygnał]
--
--    output.fan.supply                       - wentylator nawiewny [0..100]
--
--    output.fan.exhaust                      - wentylator wywiewny [0..100]
--
--    output.hood.airdamper                   - przepustnica okapu (0 - wyciąg normalnie; 1 - wyciąg z okapu) [0..1]
--
--    output.bypass.airdamper                 - by-pass wymiennika (0 - przez wymiennik; 1 - przez by-pass) [0..1]
--
--    setting.inside.t.max                    - maksymalna temperatura wewnętrzna zimą, powyżej której nie jest realizowane 
--                                              ogrzewanie przez wentylację
--
--    setting.inside.t.min                    - minimalna temperatura wewnętrzna latem, poniżej której wybierane jest włączane 
--                                              ogrzewanie pasywne (bypass)
--
--    setting.inside.t.hyst                   - histereza załączania/zmiany biegu wentylatora w funkcji różnicy temperatury 
--                                              zadanej a panującej
--
--    setting.inside.dt.on
--    setting.inside.dt.off                   - wymagana różnica temperatury między powietrzem wewnętrznym i zewnętrznym 
--                                              do obsługi ogrzewania lub chłodzenia przez wentylację
--
--    setting.fan.level.demand                - aktualnie wymagany bieg wentylacji
--
--    setting.heating.enabled                 - ogrzewanie przez wentylację (freeheating) (0 - nieaktywne; 1 - aktywne). [0..1]
--                                              Ogrzewanie przez wentylacje jest realizowane:
--                                               - tylko zimą 
--                                               - tylko gdy nie ma błędu temperatury wewnętrzne i zewnętrznej (counter.alert.t.state)
--                                               - jeżeli w pomieszczeniu jest poniżej maksymalnej wewnętrznej (counter.inside.t.max.status = 0)
--                                               - temperatura zewnętrzna jest wyższa niż wewnętrzna (counter.alert.inside.dt.t.state = 1)
--
--    setting.cooling.enabled                 - chłodzenie przez wentylację (freecooling) (0 - nieaktywne; 1 - aktywne). [0..1]
--                                              Chłodzenie przez wentylacje jest realizowane 
--                                               - tylko latem 
--                                               - tylko gdy nie ma błędu temperatury wewnętrzne i zewnętrznej (counter.alert.t.state)
--                                               - jeżeli w pomieszczeniu jest powyżej minimalnej wewnętrznej (counter.inside.t.min.status = 0)
--                                               - temperatura zewnętrzna jest niższa niż wewnętrzna (counter.alert.inside.dt.t.state = 1)
--
--    setting.heating.fan.level               - poziom wentylacji przy ogrzewaniu pasywnym (freeheating)
--
--    setting.cooling.fan.level               - poziom wentylacji przy chłodzeniu pasywnym (freecooling)
--
--    setting.hood.fan.supply                 - wartość wentylatora nawiewnego przy aktywnym okapie [0..100]
--
--    setting.hood.fan.exhaust                - wartość wentylatora wywiewnego przy aktywnym okapie [0..100]
--
--    setting.bypass.dt.on
--    setting.bypass.dt.off                   - wymagana różnica temperatury między powietrzem nawiewanym a wywiewanym do obsługi bypass'u
--
--    setting.fan.level.supply                - lista wartości wentylatora nawiewnego na danym biegu [0..100]
--
--    setting.fan.level.exhaust               - lista wartości wentylatora wywiewnego na danym biegu [0..100]
--
--    setting.override.fan.level              - poziom wentylacji w trybie override
--
--    counter.inside.t.min.status             - flaga: czy warunek minimalna temperatury wewnętrznej latem, poniżej której 
--                                              zaprzestawane jest chłodzenie przez wentylację a podczas normalnej wentylacji 
--                                              wybierane jest cieplejsze źródła ciepła (sterowanie bypass'em). 
--                                              Zimą wartość zawsze 1. [0..1]
--
--    counter.inside.t.max.status             - flaga: czy warunek maksymalnej temperatury wewnętrznej zimą, powyżej której 
--                                              zaprzestawane jest ogrzewanie przez wentylację. Latem wartość zawsze 0. [0..1]
--
--    counter.bypass.heating.dt.status        - flaga: czy warunek dt bypassu dla ogrzewania jest spełniony [0..1]
--                                              (dt temperatury wejścia na rekuperator i wyciągu) [0..1]
--
--    counter.bypass.cooling.dt.status        - flaga: czy warunek dt bypassu dla chłodzenia jest spełniony
--                                              (dt temperatury wyciągu i wejścia na rekuperator) [0..1]
--
--    counter.inside.heating.dt.status        - flaga: czy warunek dt dla ogrzewania przez wentylację jest spełniony (freeheating)
--                                              (dt temperatury wewnętrznej i zewnętrznej). Latem zawsze 0. [0..1]
--
--    counter.inside.cooling.dt.status        - flaga: czy warunek dt dla chłodzenia przez wentylację jest spełniony (freecooling)
--                                              (dt temperatury wewnętrznej i zewnętrznej). Zimą zawsze 0. [0..1]
--
--    counter.inside.cooling.status           - flaga: czy aktualnie jest realizowany proces chłodzenia przez wentylację (freecooling)
--
--    counter.inside.heating.status           - flaga: czy aktualnie jest realizowany proces ogrzewania przez wentylację (freeheating)
--
--    counter.fan.levels.count                - ilość biegów wentylacji
--
--    counter.alert.bypass.t.state            - jeżeli któryś z czujników, niezbędny do obsługi bypassu jest uszkodzony to ta flaga 
--                                              jest ustawiana na 1. W takim przypadku bypass jest zawsze zamknięty [0..1]
--
--    counter.alert.inside.dt.t.state         - jeżeli któryś z czujników, niezbędny do obsługi ogrzewania lub chłodzenia przez
--                                              wentylacje jest uszkodzony to ta flaga jest ustawiana na 1. W takim przypadku 
--                                              nie jest realizowane ogrzewanie i chłodzenie przez wentylację [0..1]
--
--    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-06-24 ver 1.2.5
--
--    # zmiana nazwy parametru outside -> outdoor
--
--    2019-06-04 ver 0.1.4
--
--    + dodanie flag freecooling i freeheating
--
--    2018-07-07 ver 0.0.3
--
--    # zabezpieczenie delt
--
--    2017-07-26 ver 0.0.2
--
--    # input.override, input.hood - sygnał załączenia >0
--
--    2017-06-07 ver 0.0.1
--
--    # obsługa automatycznego build'a
--    # opis logiki
--
--    2017-06-05 ver 0.0.0
--
--    # First release
--
--
-- 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 elemetnst 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("DownCounter");

g_outputFanSupply = nil;
g_outputFanExhaust = nil;
g_outputHoodAirdamper = nil;
g_outputBypassAirdamper = nil;
g_counterInsideTMinStatus = nil;
g_counterInsideTMaxStatus = nil;
g_counterBypassHeatingDtStatus = nil;
g_counterBypassCoolingDtStatus = nil;
g_counterInsideHeatingDtStatus = nil;
g_counterInsideCoolingDtStatus = nil;

g_settingInsideDtOn = nil;
g_settingInsideDtOff = nil;
g_settingBypassDtOn = nil;
g_settingBypassDtOff = nil;

SUPPORTED_SUBLOGIC_TYPE = "Recu01";
SUPPORTED_SUBLOGIC_VERSION = "1.2.5";
g_versionChecked = false;

-- entry point to the logic
-- @param firstCall - tells if logic is called first time
-- @return        - nothing
function onLogicCall(firstCall)

  if not g_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
    g_versionChecked = true;
  end

  local inputInsideTValue = getLogicValue("input.inside.t.value");
  local inputInsideTErr = getLogicValue("input.inside.t.err");
  local inputVentInTValue = getLogicValue("input.vent.in.t.value");
  local inputVentInTErr = getLogicValue("input.vent.in.t.err");
  local inputVentExhaustTValue = getLogicValue("input.vent.exhaust.t.value");
  local inputVentExhaustTErr = getLogicValue("input.vent.exhaust.t.err");
  local inputOutdoorTValue = getLogicValue("input.outdoor.t.value");
  local inputOutdoorTErr = getLogicValue("input.outdoor.t.err");
  local inputSeason = getLogicValue("input.season");
  local inputOverride = getLogicValue("input.override");
  local inputHood = getLogicValue("input.hood");
  local settingInsideTMax = getLogicValue("setting.inside.t.max");
  local settingInsideTMin = getLogicValue("setting.inside.t.min");
  local settingInsideTHyst = getLogicValue("setting.inside.t.hyst");
  local settingInsideDtOn = getLogicValue("setting.inside.dt.on");
  local settingInsideDtOff = getLogicValue("setting.inside.dt.off");
  local settingFanLevelDemand = getLogicValue("setting.fan.level.demand");
  local settingHeatingEnabled = getLogicValue("setting.heating.enabled");
  local settingCoolingEnabled = getLogicValue("setting.cooling.enabled");
  local settingHeatingFanLevel = getLogicValue("setting.heating.fan.level");
  local settingCoolingFanLevel = getLogicValue("setting.cooling.fan.level");
  local settingHoodFanSupply = getLogicValue("setting.hood.fan.supply");
  local settingHoodFanExhaust = getLogicValue("setting.hood.fan.exhaust");
  local settingBypassDtOn = getLogicValue("setting.bypass.dt.on");
  local settingBypassDtOff = getLogicValue("setting.bypass.dt.off");
  local settingFanLevelSupplyList = getVarList("setting.fan.level.supply");
  local settingFanLevelExhaustList = getVarList("setting.fan.level.exhaust");
  local settingOverrideFanLevel = getLogicValue("setting.override.fan.level");

  local counterFanLevelsCount = 0;
  local counterAlertBypassTState = 0;
  local counterAlertInsideDtTState = 0;
  local counterInsideCoolingStatus = 0;
  local counterInsideHeatingStatus = 0;
  local FanLevelSupplyList = {};
  local FanLevelExhaust = {};

  -- states
  if g_outputFanSupply == nil or firstCall then
    g_outputFanSupply = State.create(0, 0, 100, true);
  end
  g_outputFanSupply:call();

  if g_outputFanExhaust == nil or firstCall then
    g_outputFanExhaust = State.create(0, 0, 100, true);
  end
  g_outputFanExhaust:call();

  if g_outputHoodAirdamper == nil or firstCall then
    g_outputHoodAirdamper = State.create(0, 0, 1, true);
  end
  g_outputHoodAirdamper:call();

  if g_outputBypassAirdamper == nil or firstCall then
    g_outputBypassAirdamper = State.create(0, 0, 1, true);
  end
  g_outputBypassAirdamper:call();

  if g_counterInsideTMinStatus == nil or firstCall then
    g_counterInsideTMinStatus = State.create(0, 0, 1, true);
  end
  g_counterInsideTMinStatus:call();

  if g_counterInsideTMaxStatus == nil or firstCall then
    g_counterInsideTMaxStatus = State.create(0, 0, 1, true);
  end
  g_counterInsideTMaxStatus:call();

  if g_counterBypassHeatingDtStatus == nil or firstCall then
    g_counterBypassHeatingDtStatus = State.create(0, 0, 1, true);
  end
  g_counterBypassHeatingDtStatus:call();

  if g_counterBypassCoolingDtStatus == nil or firstCall then
    g_counterBypassCoolingDtStatus = State.create(0, 0, 1, true);
  end
  g_counterBypassCoolingDtStatus:call();

  if g_counterInsideHeatingDtStatus == nil or firstCall then
    g_counterInsideHeatingDtStatus = State.create(0, 0, 1, true);
  end
  g_counterInsideHeatingDtStatus:call();

  if g_counterInsideCoolingDtStatus == nil or firstCall then
    g_counterInsideCoolingDtStatus = State.create(0, 0, 1, true);
  end
  g_counterInsideCoolingDtStatus:call();
  
  -- zabezpieczenie delt 

  --InsideDt
  if g_settingInsideDtOn == nil or firstCall then
    g_settingInsideDtOn = settingInsideDtOn;
  end

  if g_settingInsideDtOff == nil or firstCall then
    g_settingInsideDtOff = settingInsideDtOff;
  end 

  if (g_settingInsideDtOn ~= settingInsideDtOn) then
    if (settingInsideDtOn <= settingInsideDtOff) then
      settingInsideDtOn = settingInsideDtOff + 1;
      setLogicValue("setting.inside.dt.on", settingInsideDtOn);
    end
  end

  if (g_settingInsideDtOff ~= settingInsideDtOff) then
    if (settingInsideDtOff >= settingInsideDtOn) then
      settingInsideDtOff = settingInsideDtOn - 1;
      setLogicValue("setting.inside.dt.off", settingInsideDtOff);
    end
  end

  -- w przypadku pozostałości ze storage
  if (settingInsideDtOn <= settingInsideDtOff) then
    settingInsideDtOn = settingInsideDtOff + 1;
    setLogicValue("setting.inside.dt.on", settingInsideDtOn);
  end

  g_settingInsideDtOn = settingInsideDtOn;
  g_settingInsideDtOff = settingInsideDtOff;

  --BypassDt
  if g_settingBypassDtOn == nil or firstCall then
    g_settingBypassDtOn = settingBypassDtOn;
  end

  if g_settingBypassDtOff == nil or firstCall then
    g_settingBypassDtOff = settingBypassDtOff;
  end 

  if (g_settingBypassDtOn ~= settingBypassDtOn) then
    if (settingBypassDtOn <= settingBypassDtOff) then
      settingBypassDtOn = settingBypassDtOff + 1;
      setLogicValue("setting.bypass.dt.on", settingBypassDtOn);
    end
  end

  if (g_settingBypassDtOff ~= settingBypassDtOff) then
    if (settingBypassDtOff >= settingBypassDtOn) then
      settingBypassDtOff = settingBypassDtOn - 1;
      setLogicValue("setting.bypass.dt.off", settingBypassDtOff);
    end
  end

  -- w przypadku pozostałości ze storage
  if (settingBypassDtOn <= settingBypassDtOff) then
    settingBypassDtOn = settingBypassDtOff + 1;
    setLogicValue("setting.bypass.dt.on", settingBypassDtOn);
  end

  g_settingBypassDtOn = settingBypassDtOn;
  g_settingBypassDtOff = settingBypassDtOff;

  -- obsługa alarmowa
  if ((inputInsideTErr ~= 0) or (inputVentInTErr ~= 0) or (inputVentExhaustTErr ~= 0)) then
    counterAlertBypassTState = 1;
    g_counterInsideTMinStatus:setValue(0);
    g_counterBypassCoolingDtStatus:setValue(0);
    g_counterBypassHeatingDtStatus:setValue(0);
    g_outputBypassAirdamper:setValue(0);
  end

  if ((inputInsideTErr ~= 0) or (inputOutdoorTErr ~= 0)) then
    counterAlertInsideDtTState = 1;
    g_counterInsideHeatingDtStatus:setValue(0);
    g_counterInsideCoolingDtStatus:setValue(0);
  end

  -- demand temperatures states
  if (inputSeason == 0) then
    g_counterInsideTMinStatus:setValue(0);
    g_counterInsideTMaxStatus:set1IfHigherThan(inputInsideTValue, settingInsideTMax, settingInsideTHyst);
    
    g_counterInsideHeatingDtStatus:setIfDelta(inputOutdoorTValue, inputInsideTValue, settingInsideDtOn, settingInsideDtOff);
    g_counterInsideCoolingDtStatus:setValue(0);

    g_counterBypassCoolingDtStatus:setValue(0);
    g_counterBypassHeatingDtStatus:setIfDelta(inputVentInTValue, inputVentExhaustTValue, settingBypassDtOn, settingBypassDtOff);
  else
    g_counterInsideTMinStatus:set1IfLowerThan(inputInsideTValue, settingInsideTMin, settingInsideTHyst);
    g_counterInsideTMaxStatus:setValue(0);

    g_counterInsideHeatingDtStatus:setValue(0);
    g_counterInsideCoolingDtStatus:setIfDelta(inputInsideTValue, inputOutdoorTValue, settingInsideDtOn, settingInsideDtOff);
    
    if (g_counterInsideTMinStatus:getValue() == 0) then
      g_counterBypassCoolingDtStatus:setIfDelta(inputVentExhaustTValue, inputVentInTValue, settingBypassDtOn, settingBypassDtOff);
      g_counterBypassHeatingDtStatus:setValue(0);
    else
      g_counterBypassCoolingDtStatus:setValue(0);
      g_counterBypassHeatingDtStatus:setIfDelta(inputVentInTValue, inputVentExhaustTValue, settingBypassDtOn, settingBypassDtOff);
    end
  end

  -- sterowanie bypassem
  if (g_counterBypassHeatingDtStatus:getValue() == 1) or (g_counterBypassCoolingDtStatus:getValue() == 1) then
    g_outputBypassAirdamper:setValue(1);
  else
    g_outputBypassAirdamper:setValue(0);
  end

  -- sterowanie okapem
  if (inputHood ~= 0) then
    g_outputHoodAirdamper:setValue(1);
    g_outputFanSupply:setValue(settingHoodFanSupply);
    g_outputFanExhaust:setValue(settingHoodFanExhaust);
  else
    g_outputHoodAirdamper:setValue(0);
  end

  -- tablica wartości wentylacji na podstawie ustawień
  if settingFanLevelSupplyList ~= nil then
    for var, value in pairs(settingFanLevelSupplyList) do
      FanLevelSupplyList[var] = value;
      counterFanLevelsCount = counterFanLevelsCount + 1;
    end
  end

  if settingFanLevelExhaustList ~= nil then
    for var, value in pairs(settingFanLevelExhaustList) do
      FanLevelExhaust[var] = value;
    end
  end

  -- zabezpieczenie przed zbyt wysokimi biegami nastaw
  if (settingFanLevelDemand >= counterFanLevelsCount) then
    settingFanLevelDemand = counterFanLevelsCount - 1;
    setLogicValue("setting.fan.level.demand", settingFanLevelDemand);
  end

  if (settingOverrideFanLevel >= counterFanLevelsCount) then
    settingOverrideFanLevel = counterFanLevelsCount - 1;
    setLogicValue("setting.override.fan.level", settingOverrideFanLevel);
  end

  if (settingCoolingFanLevel >= counterFanLevelsCount) then
    settingCoolingFanLevel = counterFanLevelsCount - 1;
    setLogicValue("setting.cooling.fan.level", settingCoolingFanLevel);
  end

  -- tryb override
  if (inputOverride ~= 0) then
    g_outputFanSupply:setValue(FanLevelSupplyList['' .. settingOverrideFanLevel]);
    g_outputFanExhaust:setValue(FanLevelExhaust['' .. settingOverrideFanLevel]);
  end

  -- tryb ogrzewania przez wentylację
  if (settingHeatingEnabled == 1) and (g_counterInsideHeatingDtStatus:getValue() == 1) and (g_counterInsideTMaxStatus:getValue() == 0) then
    g_outputFanSupply:setValue(FanLevelSupplyList['' .. settingHeatingFanLevel]);
    g_outputFanExhaust:setValue(FanLevelExhaust['' .. settingHeatingFanLevel]);
    counterInsideHeatingStatus = 1;
  end
 
  -- tryb chłodzenia przez wentylację
  if (settingCoolingEnabled == 1) and (g_counterInsideCoolingDtStatus:getValue() == 1) and (g_counterInsideTMinStatus:getValue() == 0) then
    g_outputFanSupply:setValue(FanLevelSupplyList['' .. settingCoolingFanLevel]);
    g_outputFanExhaust:setValue(FanLevelExhaust['' .. settingCoolingFanLevel]);
    counterInsideCoolingStatus = 1;
  end

  -- sterowanie normalną pracą wentylacji
  g_outputFanSupply:setValue(FanLevelSupplyList['' .. settingFanLevelDemand]);
  g_outputFanExhaust:setValue(FanLevelExhaust['' .. settingFanLevelDemand]);

  -- send logic variables to ibmanager
  setLogicValue("output.fan.supply", g_outputFanSupply:getValue());
  setLogicValue("output.fan.exhaust", g_outputFanExhaust:getValue());
  setLogicValue("output.hood.airdamper", g_outputHoodAirdamper:getValue());
  setLogicValue("output.bypass.airdamper", g_outputBypassAirdamper:getValue());
  setLogicValue("counter.inside.t.min.status", g_counterInsideTMinStatus:getValue());
  setLogicValue("counter.inside.t.max.status", g_counterInsideTMaxStatus:getValue());
  setLogicValue("counter.bypass.heating.dt.status", g_counterBypassHeatingDtStatus:getValue());
  setLogicValue("counter.bypass.cooling.dt.status", g_counterBypassCoolingDtStatus:getValue());
  setLogicValue("counter.inside.heating.dt.status", g_counterInsideHeatingDtStatus:getValue());
  setLogicValue("counter.inside.cooling.dt.status", g_counterInsideCoolingDtStatus:getValue());
  setLogicValue("counter.inside.cooling.status", counterInsideCoolingStatus);
  setLogicValue("counter.inside.heating.status", counterInsideHeatingStatus);
  setLogicValue("counter.fan.levels.count", counterFanLevelsCount);
  setLogicValue("counter.alert.bypass.t.state", counterAlertBypassTState);
  setLogicValue("counter.alert.inside.dt.t.state", counterAlertInsideDtTState);

end
