-- ======================
-- Description
-- ======================
--
-- Logika sterująca nagrzewnicą wodną z przepustnicą powietrzną (by-pass). Wentylator i przepustnica tylko w stanie włącz/wyłącz (zamknij/otwórz).
--
-- W okresie zimowym wentylator jest włączany gdy jest temperatura wewnętrzna poniżej wymaganej wewnętrznej.
--
-- Zimą przepustnica jest otwarta (pobierane powietrze z zewnątrz) jeżeli różnica temperatur między zewnętrzna a wewnętrzna
-- jest odpowiednia (cieplej na zewnątrz). W innym przypadku przepustnica jest zamknięta - recyrkulacja (setting.airdamper.dt).
--
-- W okresie letnim przepustnica otwierana gdy wewnętrzna temperatura jest powyżej wymaganej i równocześnie temperatura zewnętrzna
-- nie jest za niska (counter.airdamper.outdoor.t.min.state) i nie jest za wysoka (counter.airdamper.outdoor.t.max.state).
--
-- W okresie letnim wentylator włączany jest zawsze gdy przepustnica jest otwarta.
--
-- ======================
-- Parameters
-- ======================
--
-- input.inside.t.value
-- input.inside.t.err - temperatura wewnętrzna
--
-- input.outdoor.t.value
-- input.outdoor.t.err - temperatura wewnętrzna
--
-- input.season - 0 - winter, 1 - summer.
--
-- output.fan - wentylator nagrzewnicy [0..1].
--
-- output.airdamper - przypustnica nagrzewnicy (0 - pow. z wewnątrz; 1 - z zewnątrz) [0..1].
--
-- setting.inside.demand.t - wymagana temperatura wewnętrzna
--
-- setting.inside.t.hyst - histereza dla operacji powiązanych z temperaturą wewnętrzną
--
-- setting.outdoor.t.min - latem jeżeli temperatura zewnętrzna jest poniżej tej wartości to przepustnica zawsze zamknięta
--
-- setting.outdoor.t.max - latem jeżeli temperatura zewnętrzna jest powyżej tej wartości to przepustnica zawsze zamknięta
--
-- setting.outdoor.t.hyst - histereza dla operacji powiązanych z temperaturą zewnętrzną
--
-- setting.alert.t.err.fan.state - jaką ma przyjąć wartość wentylator w przypadku uszkodzenia któregoś czujnika
--
-- setting.alert.t.err.airdamper.state - jaką ma przyjąć wartość przepustnica w przypadku uszkodzenia któregoś czujnika
--
-- setting.airdamper.dt.on
-- setting.airdamper.dt.off - delta zamknięcia (0)/otwarcia (1) przepustnicy zimą
-- jeżeli dt pomiędzy t zewnętrzną a t wewnętrzną jest większa to przepustnica jest otwarta (1)
--
-- counter.airdamper.outdoor.t.min.state - flaga: czy latem przepustnica ma być zamknięta (0) czy otwarta (1) z warunku minimalnej temperatury zewnętrznej
--
-- counter.airdamper.outdoor.t.max.state - flaga: czy latem przepustnica ma być zamknięta (0) czy otwarta (1) z warunku maksymalnej temperatury zewnętrznej
--
-- counter.airdamper.dt.state - flaga: czy zimą przepustnica ma być zamknięta (0) czy otwarta (1) z warunku różnicy temperatur (setting.airdamper.dt)
--
-- counter.alert.t.err - flaga: wartość 1 oznacza uszkodzenie jakiegokolwiek czujnika. Powyżej i poniżej maksymalnej nie są traktowane
-- jako błędy. Aktywny alert ustawia wentylator i przepustnicę zgodnie z nastawą setting.alert.t.err.fan.state
-- i setting.alert.t.err.airdamper.state.
--
-- 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.0.0
--
-- # zmiana nazwy parametru outside -> outdoor
--
-- 2019-01-31 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_outputFan = nil;
g_outputAirdamper = nil;
g_counterAirdamperOutdoorTMinState = nil;
g_counterAirdamperOutdoorTMaxState = nil;
g_counterAirdamperDtState = nil;
g_settingAirdamperDtOn = nil;
g_settingAirdamperDtOff = nil;
SUPPORTED_SUBLOGIC_TYPE = "Heater01";
SUPPORTED_SUBLOGIC_VERSION = "1.0.0";
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 inputOutdoorTValue = getLogicValue("input.outdoor.t.value");
local inputOutdoorTErr = getLogicValue("input.outdoor.t.err");
local inputSeason = getLogicValue("input.season");
local settingInsideDemandT = getLogicValue("setting.inside.demand.t");
local settingInsideTHyst = getLogicValue("setting.inside.t.hyst");
local settingOutdoorTMin = getLogicValue("setting.outdoor.t.min");
local settingOutdoorTMax = getLogicValue("setting.outdoor.t.max");
local settingOutdoorTHyst = getLogicValue("setting.outdoor.t.hyst");
local settingAlertTErrFanState = getLogicValue("setting.alert.t.err.fan.state");
local settingAlertTErrAirdamperState = getLogicValue("setting.alert.t.err.airdamper.state");
local settingAirdamperDtOn = getLogicValue("setting.airdamper.dt.on");
local settingAirdamperDtOff = getLogicValue("setting.airdamper.dt.off");
local counterAlertTErr = 0;
-- states
if g_outputFan == nil or firstCall then
g_outputFan = State.create(0, 0, 1, true);
end
g_outputFan:call();
if g_outputAirdamper == nil or firstCall then
g_outputAirdamper = State.create(0, 0, 1, true);
end
g_outputAirdamper:call();
if g_counterAirdamperOutdoorTMinState == nil or firstCall then
g_counterAirdamperOutdoorTMinState = State.create(0, 0, 1, true);
end
g_counterAirdamperOutdoorTMinState:call();
if g_counterAirdamperOutdoorTMaxState == nil or firstCall then
g_counterAirdamperOutdoorTMaxState = State.create(0, 0, 1, false);
end
g_counterAirdamperOutdoorTMaxState:call();
if g_counterAirdamperDtState == nil or firstCall then
g_counterAirdamperDtState = State.create(0, 0, 1, false);
end
g_counterAirdamperDtState:call();
--PumpDt
if g_settingAirdamperDtOn == nil or firstCall then
g_settingAirdamperDtOn = settingAirdamperDtOn;
end
if g_settingAirdamperDtOff == nil or firstCall then
g_settingAirdamperDtOff = settingAirdamperDtOff;
end
if (g_settingAirdamperDtOn ~= settingAirdamperDtOn) then
if (settingAirdamperDtOn <= settingAirdamperDtOff) then
settingAirdamperDtOn = settingAirdamperDtOff + 1;
setLogicValue("setting.pump.dt.on", settingAirdamperDtOn);
end
end
if (g_settingAirdamperDtOff ~= settingAirdamperDtOff) then
if (settingAirdamperDtOff >= settingAirdamperDtOn) then
settingAirdamperDtOff = settingAirdamperDtOn - 1;
setLogicValue("setting.airdamper.dt.off", settingAirdamperDtOff);
end
end
-- w przypadku pozostałości ze storage
if (settingAirdamperDtOn <= settingAirdamperDtOff) then
settingAirdamperDtOn = settingAirdamperDtOff + 1;
setLogicValue("setting.airdamper.dt.on", settingAirdamperDtOn);
end
g_settingAirdamperDtOn = settingAirdamperDtOn;
g_settingAirdamperDtOff = settingAirdamperDtOff;
-- alert uszkodzonego czujnika
if (inputInsideTErr ~= 0) or
(inputOutdoorTErr ~= 0) then
counterAlertTErr = 1;
end
-- stan wyjść podczas alertu uszkodzonego czujnika
if counterAlertTErr == 1 then
g_outputFan:setValue(settingAlertTErrFanState);
g_outputAirdamper:setValue(settingAlertTErrAirdamperState);
end
if ((inputSeason ~= 0)) then
-- lato
-- ustalenie czy ze względu na zewnętrzna temperaturę minimalną przepustnica ma być zamknięta czy otwarta
g_counterAirdamperOutdoorTMinState:set1IfHigherThan(inputOutdoorTValue, settingOutdoorTMin, settingOutdoorTHyst);
-- ustalenie czy ze względu na zewnętrzna temperaturę maksymalną przepustnica ma być zamknięta czy otwarta
g_counterAirdamperOutdoorTMaxState:set1IfLowerThan(inputOutdoorTValue, settingOutdoorTMax, settingOutdoorTHyst);
-- latem warunek otwarcia przepustnicy ze względu na różnicę temperatur - zamknięta
g_counterAirdamperDtState:setValue(0);
-- jeżeli którykolwiek warunek mówi ze przepustnica ma być zamknięta to należy ją zamknąć
if ((g_counterAirdamperOutdoorTMinState:getValue() == 1) and
(g_counterAirdamperOutdoorTMaxState:getValue() == 1)) then
g_outputAirdamper:setValue(1);
g_outputFan:set1IfHigherThan(inputInsideTValue, settingInsideDemandT, settingInsideTHyst);
else
g_outputAirdamper:setValue(0);
g_outputFan:setValue(0);
end
else
-- zima
-- ze względu na zewnętrzna temperaturę minimalną i maksymalną przepustnica ma być zamknięta
g_counterAirdamperOutdoorTMinState:setValue(0);
g_counterAirdamperOutdoorTMaxState:setValue(0);
-- warunek otwarcia przepustnicy ze względu na różnicę temperatur
g_counterAirdamperDtState:setIfDelta(inputOutdoorTValue, inputInsideTValue, settingAirdamperDtOn, settingAirdamperDtOff);
g_outputAirdamper:setValue(g_counterAirdamperDtState:getValue());
g_outputFan:set1IfLowerThan(inputInsideTValue, settingInsideDemandT, settingInsideTHyst);
end
-- send logic variables to ibmanager
setLogicValue("output.fan", g_outputFan:getValue());
setLogicValue("output.airdamper", g_outputAirdamper:getValue());
setLogicValue("counter.airdamper.outdoor.t.min.state", g_counterAirdamperOutdoorTMinState:getValue());
setLogicValue("counter.airdamper.outdoor.t.max.state", g_counterAirdamperOutdoorTMaxState:getValue());
setLogicValue("counter.airdamper.dt.state", g_counterAirdamperDtState:getValue());
setLogicValue("counter.alert.t.err", counterAlertTErr);
end