--    ======================
--    Description
--    ======================
--
--    Logika sterująca systemem solarnym, jedna połać kolektorów, dwa zbiorniki z priorytetem.
--   
--    Priorytet dogrzewania solar:
--   
--    Priorytet dla c.w.u. - Dopóki zbiornik wewnętrzny (czujnik T2) nie osiągnie zadanej Temperatury optymalnej c.w.u.
--    zbiornik buforowy nie jest ładowany (wyjątkiem jest przypadek, gdy regulator stwierdzi, że nie ma możliwości załadować
--    wewnętrznego zbiornika do zadanej optymalnej temperatury). Kiedy wewnętrzny zbiornik zostaje załadowany do optymalnej temperatury,
--    zbiorniki ładowane są wg zasady największej efektywności.
--   
--    Największa efektywność - Zbiorniki wewnętrzny oraz zbiornik buforowy mają równe priorytety. System ładuje ten zbiornik,
--    który zapewni największy uzysk energii (UWAGA! Nie należy mylić energii z temperaturą!)
--
--    Jeżeli priorytet c.w.u. jest poniżej temperatury optymalnej system próbuje w pierwszej kolejności ładować zbiornik c.w.u.
--    W przypadku gdy nie ma takiej możliwości układ przełącza się na ładowanie zbiornika buforowego na określony Czas ładowania zbiornika buforowego
--    poniżej temperatury optymalnej w c.w.u. [s]. Po tym czasie układ sprawdza czy jest możliwość ładowania c.w.u. Jeżeli tak zawór przestawia się na c.w.u.
--    i zbiornik ładowany jest do momentu osiągnięcia temperatury optymalnej. W przeciwnym przypadku pompa kolektora jest wyłączana na Czas próby dogrzewania
--    zbiornika c.w.u. poniżej temperatury optymalnej w c.w.u. [s] w celu dogrzania kolektorów. Jeżeli przed upłynięciem tego czasu kolektory osiągną odpowiednią
--    temperaturę do ładowania c.w.u. pompa jest włączana. Jeżeli nie układ próbuje ładować bufor.
--
--    ======================
--    Parameters
--    ======================
--
--    SubLogic ThermostatAF_2o supports following variables:
--
--    input.solar.output.t.value
--    input.solar.output.t.err                - temperatura solar (wyjścia)
--
--    input.tank.dw.t.value
--    input.tank.dw.t.err                     - temperatura w zbiorniku c.w.u.
--
--    input.tank.buff.t.value
--    input.tank.buff.t.err                   - temperatura w zbiorniku buforowym
--
--    input.cooldown.tank.dw                  - stan informujący logikę o konieczności wychłodzenia zbiornika c.w.u. Jeżeli ta wartość jest == 1 to logika stara się wychłodzić
--                                              zbiornik c.w.u. [0..1]. Procedura wychładzania ma wyższy priorytet niż ładowania. 
--                                              W pierwszej kolejności jest wychładzany zbiornik c.w.u.
--
--    input.cooldown.tank.buff                - stan informujący logikę o konieczności wychłodzenia zbiornika buforowego Jeżeli ta wartość jest == 1 to logika stara się wychłodzić
--                                              zbiornik buforowy [0..1]. Procedura wychładzania ma wyższy priorytet niż ładowania. 
--                                              W pierwszej kolejności jest wychładzany zbiornik c.w.u.
--
--    input.season                            - 0 - winter, 1 - summer.
--
--    output.pump                             - pompa solarna [0..1].
--
--    output.valve                            - zawór trójdrogowy (0 - ładowanie bufora, 1 - ładowanie c.w.u.) [0..1].
--
--    setting.priority.mode                   - tryb priorytetu (0 - ładowanie w/g największej efektywności, 1 - priorytet dla c.w.u, 2 - auto: latem 1; zimą 0)
--
--    setting.priority.time.on                - czas próby dogrzewania zbiornika c.w.u. poniżej temperatury optymalnej w c.w.u.
--
--    setting.priority.time.off               - czas ładowania zbiornika buforowego poniżej temperatury optymalnej w c.w.u.
--
--    setting.dw.t.optimal                    - temperatura optymalna dla c.w.u.
--
--    setting.pump.dt.on
--    setting.pump.dt.off                     - delta włączenia/wyłączenia pompy kolektora
--
--    setting.alert.t.err.pump.state          - jaką ma przyjąć wartość pompa solarna w razie uszkodzenia któregoś czujnika
--
--    setting.alert.t.solar.max.pump.state    - jaką ma przyjąć wartość pompa solarna w razie przekroczenia wysokiej temperatury na solarze
--
--    setting.alert.t.solar.max.value 
--    setting.alert.t.solar.max.hyst          - przy jakiej temperaturze solarnej uaktywnia się counter.alert.t.solar.max, wartość i histereza
--
--    setting.alert.t.tank.max.value 
--    setting.alert.t.tank.max.hyst           - przy jakiej temperaturze w dowolnym zbiorniku uaktywnia się counter.alert.t.tank.max, wartość i histereza
--
--    setting.valve.switch.time               - czas między możliwością kolejnego przełączenia zaworu (zawór nie przełączy się częściej niż co ten odcinek czasu). W sekundach
--
--    counter.priority.main.mode              - aktualny priorytet w/g nastawy setting.priority.mode (0 - ładowanie w/g największej efektywności, 1 - priorytet dla c.w.u)
--
--    counter.priority.current.time           - licznik czasu dla setting.priority.time.on i setting.priority.time.off
--
--    counter.priority.current.mode           - aktualny tryb (0 - ładuję bufor bo nie da się ładować c.w.u., 1 - robię postój w ładowaniu bufora bo próbuję załadować c.w.u.)
--
--    counter.alert.t.tank.max                - wartość 1 oznacza przekroczenie maksymalnej temperatury czujnika w dowolnym zbiorniku - odpowiedni błąd na czujniku lub 
--                                              przekroczenie wartości setting.alert.t.tank.max.value. Aktywny alert wyłącza pompę solarną. Ten alert ma najwyższy priorytet.
--
--    counter.alert.t.err                     - wartość 1 oznacza uszkodzenie jakiegokolwiek czujnika. Powyżej i poniżej maksymalnej nie są traktowane jako błędy. Aktywny
--                                              alert ustawia pompę solarną zgodnie z nastawą setting.alert.t.err.pump.state. Ten alert ma druki w kolejności priorytet.
--
--    counter.alert.t.solar.max               - wartość 1 oznacza przekroczenie maksymalnej temperatury czujnika na kolektorze - odpowiedni błąd na czujniku lub 
--                                              przekroczenie wartości setting.alert.t.solar.max.value. Aktywny alert ustawia pompę solarną zgodnie 
--                                              z nastawą setting.alert.t.solar.max.pump.state. ten alert ma trzeci w kolejności priorytet.
--
--    counter.cooldown.tank.dw.ready          - czy jest możliwe wychładzanie zbiornika c.w.u. Temperatura w zbiorniku jest niższa niż w kolektorze z uwzględnieniem setting.pump.dt 
--
--    counter.cooldown.tank.buff.ready        - czy jest możliwe wychładzanie zbiornika buforowego. Temperatura w zbiorniku jest niższa niż w kolektorze z uwzględnieniem setting.pump.dt 
--
--    counter.valve.switch.downcounter        - licznik powiązany z setting.valve.switch.time
--
--    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
--    ======================
--
--    2018-07-07 ver 0.0.2
--
--    # zabezpieczenie delty
--
--    2017-03-29 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_outputPump = nil;
g_outputValve = nil;
g_counterPriorityMainMode = nil;
g_counterAlertTSolarMax = nil;
g_counterAlertTTankMax = nil;
g_counterCooldownTankDwReady = nil;
g_counterCooldownTankBuffReady = nil;
g_counterPriorityCurrentMode = nil;

g_counterPriorityDownCounter = nil;
g_counterValveSwitchDowncounter = nil;

g_counterPrioryty = 0;

g_settingPumpDtOn = nil;
g_settingPumpDtOff = nil;

SUPPORTED_SUBLOGIC_TYPE = "Solar01";
SUPPORTED_SUBLOGIC_VERSION = "0.0.2";
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 inputSolarOutputTValue = getLogicValue("input.solar.output.t.value");
  local inputSolarOutputTErr = getLogicValue("input.solar.output.t.err");
  local inputTankDwTValue = getLogicValue("input.tank.dw.t.value");
  local inputTankDwTErr = getLogicValue("input.tank.dw.t.err");
  local inputTankBuffTValue = getLogicValue("input.tank.buff.t.value");
  local inputTankBuffTErr = getLogicValue("input.tank.buff.t.err");
  local inputSeason = getLogicValue("input.season");
  local inputCooldownTankDw = getLogicValue("input.cooldown.tank.dw");
  local inputCooldownTankBuff = getLogicValue("input.cooldown.tank.buff");
  local settingPriorityMode = getLogicValue("setting.priority.mode");
  local settingPriorityTimeOn = getLogicValue("setting.priority.time.on");
  local settingPriorityTimeOff = getLogicValue("setting.priority.time.off");
  local settingSettingDwTOptimal = getLogicValue("setting.dw.t.optimal");
  local settingPumpDtOn = getLogicValue("setting.pump.dt.on");
  local settingPumpDtOff = getLogicValue("setting.pump.dt.off");
  local settingAlertTErrPumpState = getLogicValue("setting.alert.t.err.pump.state");
  local settingAlertTSolarMaxPumpState = getLogicValue("setting.alert.t.solar.max.pump.state");
  local settingAlertTSolarMaxValue = getLogicValue("setting.alert.t.solar.max.value");
  local settingAlertTSolarMaxHyst = getLogicValue("setting.alert.t.solar.max.hyst");
  local settingAlertTTankMaxValue = getLogicValue("setting.alert.t.tank.max.value");
  local settingAlertTTankMaxHyst = getLogicValue("setting.alert.t.tank.max.hyst");
  local settingValveSwitchTime = getLogicValue("setting.valve.switch.time");

  local counterPriorityCurrentTime = 0;
  local counterAlertTErr = 0;
  local counterValveSwitchDowncounter = 0;

-- timers
  if g_counterPriorityDownCounter == nil or firstCall then
    g_counterPriorityDownCounter = DownCounter.create();
  end
  g_counterPriorityDownCounter:updateParams(g_counterPrioryty);

--  if g_counterPriorityDownCounter:elapsed() then
--    g_counterPriorityDownCounter:reset();
--  end

  counterPriorityCurrentTime = g_counterPriorityDownCounter:timeTo0() / 1000;
  counterPriorityCurrentTime = (counterPriorityCurrentTime < 0) and 0 or counterPriorityCurrentTime;

  if g_counterValveSwitchDowncounter == nil or firstCall then
    g_counterValveSwitchDowncounter = DownCounter.create();
  end
  g_counterValveSwitchDowncounter:updateParams(settingValveSwitchTime * 1000);

--  if g_counterValveSwitchDowncounter:elapsed() then
--    g_counterValveSwitchDowncounter:reset();
--  end

  counterValveSwitchDowncounter = g_counterValveSwitchDowncounter:timeTo0() / 1000;
  counterValveSwitchDowncounter = (counterValveSwitchDowncounter < 0) and 0 or counterValveSwitchDowncounter;

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

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

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

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

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

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

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

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

  --PumpDt
  if g_settingPumpDtOn == nil or firstCall then
    g_settingPumpDtOn = settingPumpDtOn;
  end

  if g_settingPumpDtOff == nil or firstCall then
    g_settingPumpDtOff = settingPumpDtOff;
  end 

  if (g_settingPumpDtOn ~= settingPumpDtOn) then
    if (settingPumpDtOn <= settingPumpDtOff) then
      settingPumpDtOn = settingPumpDtOff + 1;
      setLogicValue("setting.pump.dt.on", settingPumpDtOn);
    end
  end

  if (g_settingPumpDtOff ~= settingPumpDtOff) then
    if (settingPumpDtOff >= settingPumpDtOn) then
      settingPumpDtOff = settingPumpDtOn - 1;
      setLogicValue("setting.pump.dt.off", settingPumpDtOff);
    end
  end

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

  g_settingPumpDtOn = settingPumpDtOn;
  g_settingPumpDtOff = settingPumpDtOff;

-- alert zbyt wysokiej temperatury w zbiorniku
  if (inputTankDwTErr == 3) or (inputTankBuffTErr == 3) then
    g_counterAlertTTankMax:setValue(1);
  else
    local highestTankTemp = inputTankDwTValue;
    if inputTankBuffTValue > highestTankTemp then
      highestTankTemp = inputTankBuffTValue;
    end

    g_counterAlertTTankMax:set1IfHigherThan(highestTankTemp, settingAlertTTankMaxValue, settingAlertTTankMaxHyst);
  end

-- stan pompy solarnej podczas alertu zbyt wysokiej temperatury zbiornika
  if g_counterAlertTTankMax:getValue() == 1 then
    g_outputPump:setValue(0);
  end

-- alert uszkodzonego czujnika
  if ((inputSolarOutputTErr ~= 0) and (inputSolarOutputTErr ~= 3)) or
     ((inputTankDwTErr ~= 0) and (inputTankDwTErr ~= 3)) or
     ((inputTankBuffTErr ~= 0) and (inputTankBuffTErr ~= 3)) then
    counterAlertTErr = 1;
  end

-- stan pompy solarnej podczas alertu uszkodzonego czujnika
  if counterAlertTErr == 1 then
    g_outputPump:setValue(settingAlertTErrPumpState);
  end

-- alert zbyt wysokiej temperatury na kolektorze
  if inputSolarOutputTErr == 3 then
    g_counterAlertTSolarMax:setValue(1);
  else
    g_counterAlertTSolarMax:set1IfHigherThan(inputSolarOutputTValue, settingAlertTSolarMaxValue, settingAlertTSolarMaxHyst);
  end

-- stan pompy solarnej podczas alertu zbyt wysokiej temperatury solar
  if g_counterAlertTSolarMax:getValue() == 1 then
    g_outputPump:setValue(settingAlertTSolarMaxPumpState);
  end

-- obsługa wychładzania
  g_counterCooldownTankDwReady:setIfDelta(inputTankDwTValue, inputSolarOutputTValue, settingPumpDtOn, settingPumpDtOff);
  g_counterCooldownTankBuffReady:setIfDelta(inputTankBuffTValue, inputSolarOutputTValue, settingPumpDtOn, settingPumpDtOff);

  if (inputCooldownTankDw == 1) and (g_counterCooldownTankDwReady:getValue() == 1) then
    g_outputPump:setValue(1);
    g_outputValve:setValue(1);
  end

  if (inputCooldownTankBuff == 1) and (g_counterCooldownTankBuffReady:getValue() == 1) then
    g_outputPump:setValue(1);
    g_outputValve:setValue(0);
  end

-- ustalenie głównego priorytetu
  if settingPriorityMode == 0 then
    g_counterPriorityMainMode:setValue(0);
  elseif settingPriorityMode == 1 then
    g_counterPriorityMainMode:setValue(1);
  elseif settingPriorityMode == 2 then
    if inputSeason == 0 then
      g_counterPriorityMainMode:setValue(0);
    else
      g_counterPriorityMainMode:setValue(1);
    end
  end

-- *
-- * Sterowanie pompa kolektora
-- *

  -- Sterowanie tylko wg największej efektywności
  if (g_counterPriorityMainMode:getValue() == 0) then
    g_counterPriorityCurrentMode:setValue(0);
  end

  -- Ustawienie na tryb pracy pompy z priorytetem dla zbiornika tank.dw
  if ((inputTankDwTValue < settingSettingDwTOptimal)
        and (
            (counterPriorityCurrentTime == 0)
            or (inputSolarOutputTValue - inputTankDwTValue >= settingPumpDtOn))
         )
        and (g_counterPriorityCurrentMode:getValue() == 0) 
        and (counterValveSwitchDowncounter == 0) 
    then
      if g_counterPriorityCurrentMode:setValue(1) then
        g_counterPrioryty = settingPriorityTimeOn * 1000;
        g_counterPriorityDownCounter:updateParams(g_counterPrioryty);
        g_counterPriorityDownCounter:reset();
      end
    end

    -- Ustawienie na tryb pracy pompy wg największej efektywności
    if ((inputTankDwTValue >= settingSettingDwTOptimal)
        or (
            (inputTankDwTValue < settingSettingDwTOptimal)
            and (counterPriorityCurrentTime == 0))
         )
        and (g_counterPriorityCurrentMode:getValue() == 1) 
        and (counterValveSwitchDowncounter == 0) 
    then
      if g_counterPriorityCurrentMode:setValue(0) then
        if (inputTankDwTValue < settingSettingDwTOptimal) then
          g_counterPrioryty = settingPriorityTimeOff * 1000;
          g_counterPriorityDownCounter:updateParams(g_counterPrioryty);
          g_counterPriorityDownCounter:reset();
        else
          g_counterPrioryty = 0;
          g_counterPriorityDownCounter:updateParams(g_counterPrioryty);
          g_counterPriorityDownCounter:reset();
        end
      end
    end

    -- Tryb pracy pompy z priorytetem dla zbiornika tank.dw
    if (g_counterPriorityCurrentMode:getValue() == 1) then

      -- Ustawienie zaworu na przepływ dla tank.dw (valve = 1)
      if (counterValveSwitchDowncounter == 0) then
        g_outputValve:setValue(1);
      end

      -- Obsługa pompy kolektora
      g_outputPump:setIfDelta(inputSolarOutputTValue, inputTankDwTValue, settingPumpDtOn, settingPumpDtOff);

      -- reset licznika
     if (g_outputPump:getValue() == 1) then
        g_counterPrioryty = settingPriorityTimeOn * 1000;
        g_counterPriorityDownCounter:updateParams(g_counterPrioryty);
        g_counterPriorityDownCounter:reset();
     end
    end

    -- Tryb pracy pompy wg największej efektywności
    if (g_counterPriorityCurrentMode:getValue() == 0) then

      -- *
      -- * Sterowanie zaworem (tank.dw/tank.buff)
      -- *

      -- Ustawienie zaworu na przepływ dla tank.dw (valve = 1)
      if ((inputTankDwTValue <= inputTankBuffTValue) and (counterValveSwitchDowncounter == 0)) then
        g_outputValve:setValue(1);
      end

      -- Ustawienie zaworu na przepływ dla tank.buff (valve = 0)
      if ((inputTankBuffTValue < inputTankDwTValue) and (counterValveSwitchDowncounter == 0)) then
        g_outputValve:setValue(0);
      end

      -- obsługa pompy kolektora w zależności od docelowo ładowanego zbiornika
      if (g_outputValve:getValue() == 1) then
        g_outputPump:setIfDelta(inputSolarOutputTValue, inputTankDwTValue, settingPumpDtOn, settingPumpDtOff);
      else
        g_outputPump:setIfDelta(inputSolarOutputTValue, inputTankBuffTValue, settingPumpDtOn, settingPumpDtOff);
      end
    end

-- zwłoka do zmiany pozycji zaworu
    if (g_outputValve:isChanged() == true) then
      g_counterValveSwitchDowncounter:reset();
    end

-- jeżeli żaden warunek wcześniej nie operował na pompie solarnej to ją wyłącz
  g_outputPump:setValue(0);

-- send logic variables to ibmanager
  setLogicValue("output.pump", g_outputPump:getValue());
  setLogicValue("output.valve", g_outputValve:getValue());
  setLogicValue("counter.priority.main.mode", g_counterPriorityMainMode:getValue());
  setLogicValue("counter.priority.current.time", counterPriorityCurrentTime);
  setLogicValue("counter.priority.current.mode", g_counterPriorityCurrentMode:getValue());
  setLogicValue("counter.alert.t.err", counterAlertTErr);
  setLogicValue("counter.alert.t.solar.max", g_counterAlertTSolarMax:getValue());
  setLogicValue("counter.alert.t.tank.max", g_counterAlertTTankMax:getValue());
  setLogicValue("counter.cooldown.tank.dw.ready", g_counterCooldownTankDwReady:getValue());
  setLogicValue("counter.cooldown.tank.buff.ready", g_counterCooldownTankBuffReady:getValue());
  setLogicValue("counter.valve.switch.downcounter", counterValveSwitchDowncounter);

end
