-- ======================
-- Description
-- ======================
--
-- Logika sterująca cyrkulacją c.w.u.
--
-- Praca cyrkulacji jest możliwa tylko przy zewnętrznym zezwoleniu na pracę cyrkulacji (np. przez harmonogram).
-- Brak zezwolenia na pracę ma najwyższy priorytet
--
-- tryb 0: cyrkulacja cały czas wyłączona - drugi priorytet
-- tryb 1: praca w/g temperatury powrotu
-- jeżeli temperatura powrotu spadnie poniżej zadanej (z uwzględnieniem histerezy) to pompa zostanie uruchomiona ale
-- równocześnie musi być spełniony warunek różnicy temperatury temperaturą w zbiorniku a temperaturą powrotu.
-- tryb 2: praca w/g odcinków czasowych
-- pompa pracuje przez określony czas pracy (setting_dw_circ_pump_time_on), następnie robi przerwę przez
-- określony czas przerwy (setting_dw_circ_pump_time_off)*
-- tryb 3: cyrkulacja cały czas włączona
--
--
-- ======================
-- Parameters
-- ======================
--
-- SubLogic supports following variables:
--
-- input.tank.dw.t.value
-- input.tank.dw.t.err - temperatura w zbiorniku c.w.u.
--
-- input.return.t.value
-- input.return.t.err - temperatura powrotu cyrkulacji
--
-- input.enabled - czy logika zewnętrzna zezwala na uruchomienie cyrkulacji (najwyższy priorytet wyłączenia)
--
-- input.cooldown.tank.dw - stan informujący logikę o konieczności wychłodzenia zbiornika c.w.u. Jeżeli ta wartość == 1 to logika stara się wychłodzić
-- zbiornik c.w.u. [0..1]. Procedura wychładzania ma wyższy priorytet niż ładowania.
--
-- output.pump - pompa cyrkulacyjna [0..1].
--
-- setting.mode - tryb pracy pompy cyrkulacyjnej c.w.u.
-- 0 - wyłączona (drugi priorytet wyłączenia)
-- 1 - w/g temperatury powrotu
-- 2 - w/g odcinków czasowych
-- 3 - cały czas włączona
--
-- setting.return.t.demand - wymagana temperatura powrotu dla cyrkulacji (tryb 1)
--
-- setting.return.t.hyst - histereza związana z temperaturą powrotu z cyrkulacji
--
-- setting.dt.on
-- setting.dt.off - wymagana różnica temperatury między zbiornikiem a powrotem cyrkulacji do uruchomienia i zatrzymania pompy cyrkulacyjnej (tryb 1)
--
-- setting.time.on
-- setting.time.off - przez jaki czas pompa ma pracować i nie pracować (tryb 2)
--
-- setting.alert.t.err.pump.state - jaką ma przyjąć wartość pompa cyrkulacji w razie uszkodzenia któregoś czujnika
--
-- counter.time.on.downcounter
-- counter.time.off.downcounter - licznik czasu pracy i przerwy pompy cyrkulacyjnej c.w.u. (tryb 2)
--
-- counter.time.status - czy pompa cyrkulacyjna znajduje się w fazie pracy (1) czy przerwy (0) (tryb 2)
--
-- counter.dt.status - czy warunek różnicy między temp. w zbiorniku a temperaturą powrotu zezwala na uruchomienie pompy (tryb 1)
--
-- counter.return.t.demand.status - czy warunek załączenia pompy ze względu na temperaturę powrotu jest spełniony (tryb 1)
--
-- 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 priorytet po input.enabled i
-- po setting.mode == 0
--
-- 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ż powrotu z uwzględnieniem setting.dt
--
-- 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-08-13 ver 0.0.4
--
-- # komentarze
--
-- 2018-07-07 ver 0.0.3
--
-- # zabezpieczenie delty
--
-- 2017-06-07 ver 0.0.2.825
--
-- # obsługa automatycznego build'a
--
-- 2017-03-29 ver 0.0.1
--
-- # Priorytety wyłączenia cyrkulacji przed uszkodzeniem czujników
--
-- 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_counterTimeStatus = nil;
g_counterDtStatus = nil;
g_counterReturnTDemandStatus = nil;
g_counterCooldownTankDwReady = nil;
g_counterTimeOnDowncounter = nil;
g_counterTimeOffDowncounter = nil;
g_settingDtOn = nil;
g_settingDtOff = nil;
SUPPORTED_SUBLOGIC_TYPE = "CircDW";
SUPPORTED_SUBLOGIC_VERSION = "0.0.4";
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 inputTankDwTValue = getLogicValue("input.tank.dw.t.value");
local inputTankDwTErr = getLogicValue("input.tank.dw.t.err");
local inputReturnTValue = getLogicValue("input.return.t.value");
local inputReturnTErr = getLogicValue("input.return.t.err");
local inputEnabled = getLogicValue("input.enabled");
local inputCooldownTankDw = getLogicValue("input.cooldown.tank.dw");
local settingMode = getLogicValue("setting.mode");
local settingReturnTDemand = getLogicValue("setting.return.t.demand");
local settingReturnTHyst = getLogicValue("setting.return.t.hyst");
local settingDtOn = getLogicValue("setting.dt.on");
local settingDtOff = getLogicValue("setting.dt.off");
local settingTimeOn = getLogicValue("setting.time.on");
local settingTimeOff = getLogicValue("setting.time.off");
local settingAlertTErrPumpState = getLogicValue("setting.alert.t.err.pump.state");
local counterTimeOnDowncounter = 0;
local counterTimeOffDowncounter = 0;
local counterAlertTErr = 0;
-- states
if g_outputPump == nil or firstCall then
g_outputPump = State.create(0, 0, 1, true);
end
g_outputPump:call();
if g_counterTimeStatus == nil or firstCall then
g_counterTimeStatus = State.create(0, 0, 1, true);
end
g_counterTimeStatus:call();
if g_counterDtStatus == nil or firstCall then
g_counterDtStatus = State.create(0, 0, 1, true);
end
g_counterDtStatus:call();
if g_counterReturnTDemandStatus == nil or firstCall then
g_counterReturnTDemandStatus = State.create(0, 0, 1, true);
end
g_counterReturnTDemandStatus:call();
if g_counterCooldownTankDwReady == nil or firstCall then
g_counterCooldownTankDwReady = State.create(0, 0, 1, false);
end
g_counterCooldownTankDwReady:call();
-- timers
if g_counterTimeOnDowncounter == nil or firstCall then
g_counterTimeOnDowncounter = DownCounter.create();
end
g_counterTimeOnDowncounter:updateParams(settingTimeOn * 1000);
-- if g_counterTimeOnDowncounter:elapsed() then
-- g_counterTimeOnDowncounter:reset();
-- end
counterTimeOnDowncounter = g_counterTimeOnDowncounter:timeTo0() / 1000;
counterTimeOnDowncounter = (counterTimeOnDowncounter < 0) and 0 or counterTimeOnDowncounter;
if g_counterTimeOffDowncounter == nil or firstCall then
g_counterTimeOffDowncounter = DownCounter.create();
end
g_counterTimeOffDowncounter:updateParams(settingTimeOff * 1000);
counterTimeOffDowncounter = g_counterTimeOffDowncounter:timeTo0() / 1000;
counterTimeOffDowncounter = (counterTimeOffDowncounter < 0) and 0 or counterTimeOffDowncounter;
-- zabezpieczenie delt
--Dt
if g_settingDtOn == nil or firstCall then
g_settingDtOn = settingDtOn;
end
if g_settingDtOff == nil or firstCall then
g_settingDtOff = settingDtOff;
end
if (g_settingDtOn ~= settingDtOn) then
if (settingDtOn <= settingDtOff) then
settingDtOn = settingDtOff + 1;
setLogicValue("setting.dt.on", settingDtOn);
end
end
if (g_settingDtOff ~= settingDtOff) then
if (settingDtOff >= settingDtOn) then
settingDtOff = settingDtOn - 1;
setLogicValue("setting.dt.off", settingDtOff);
end
end
-- w przypadku pozostałości ze storage
if (settingDtOn <= settingDtOff) then
settingDtOn = settingDtOff + 1;
setLogicValue("setting.dt.on", settingDtOn);
end
g_settingDtOn = settingDtOn;
g_settingDtOff = settingDtOff;
-- wyłączenie cyrkulacji jeżeli nieaktywna
if inputEnabled == 0 then
g_outputPump:setValue(0);
end
if settingMode == 0 then
g_outputPump:setValue(0);
end
-- alert uszkodzonego czujnika
if (inputTankDwTErr ~= 0) or (inputReturnTErr ~= 0) then
counterAlertTErr = 1;
end
-- stan pompy cyrkulacyjnej podczas alertu uszkodzonego czujnika
if counterAlertTErr == 1 then
g_outputPump:setValue(settingAlertTErrPumpState);
end
-- obsługa wychładzania
g_counterCooldownTankDwReady:setIfDelta(inputTankDwTValue, inputReturnTValue, settingDtOn, settingDtOff);
if (inputCooldownTankDw == 1) and (g_counterCooldownTankDwReady:getValue() == 1) then
g_outputPump:setValue(1);
end
-- obsługa trybu 1
if g_counterTimeStatus:getValue() == 0 then
g_counterTimeOnDowncounter:reset();
else
g_counterTimeOffDowncounter:reset();
end
if g_counterTimeOffDowncounter:elapsed() then
g_counterTimeStatus:setValue(1);
end
if g_counterTimeOnDowncounter:elapsed() then
g_counterTimeStatus:setValue(0);
end
-- obsługa trybu 2
g_counterDtStatus:setIfDelta(inputTankDwTValue, inputReturnTValue, settingDtOn, settingDtOff);
g_counterReturnTDemandStatus:set1IfLowerThan(inputReturnTValue, settingReturnTDemand, settingReturnTHyst);
if settingMode == 3 then
g_outputPump:setValue(1);
elseif settingMode == 1 then
if (g_counterDtStatus:getValue() == 1) and (g_counterReturnTDemandStatus:getValue() == 1) then
g_outputPump:setValue(1);
else
g_outputPump:setValue(0);
end
elseif settingMode == 2 then
if (g_counterTimeStatus:getValue() == 1) then
g_outputPump:setValue(1);
else
g_outputPump:setValue(0);
end
else
g_outputPump:setValue(0);
end
-- send logic variables to ibmanager
setLogicValue("output.pump", g_outputPump:getValue());
setLogicValue("counter.time.on.downcounter", counterTimeOnDowncounter);
setLogicValue("counter.time.off.downcounter", counterTimeOffDowncounter);
setLogicValue("counter.time.status", g_counterTimeStatus:getValue());
setLogicValue("counter.dt.status", g_counterDtStatus:getValue());
setLogicValue("counter.return.t.demand.status", g_counterReturnTDemandStatus:getValue());
setLogicValue("counter.alert.t.err", counterAlertTErr);
setLogicValue("counter.cooldown.tank.dw.ready", g_counterCooldownTankDwReady:getValue());
end