-- ======================
-- Description
-- ======================
--
-- Obsługa systemu klimatyzacji z sygnałem wydajności pracy agregatu.
--
-- Funkcja ogrzewania i chłodzenia.
--
-- Załączenie ogrzewania:
--
-- - jeżeli funkcja ogrzewania jest możliwa do realizacji (setting.heating.enabled)
-- - jeżeli jest zima
-- - jeżeli temperatura wewnętrzna spadnie poniżej wymaganej (counter.inside.t.demand.heating)
--
-- Załączenie chłodzenia:
--
-- - jeżeli funkcja chłodzenia jest możliwa do realizacji (setting.cooling.enabled)
-- - jeżeli jest lato
-- - jeżeli temperatura wewnętrzna wzrośnie ponwyżej wymaganej (counter.inside.t.demand.cooling)
--
-- Jeżeli jest ogrzewane chłodzenie lub ogrzewanie to wystawiany jest odpowiedni sygnał (output.heating.signal lub input.free.cooling)
-- a sygnał sterujący wydajnością agregatu (output.ac.signal) jest ustawiany w taki sposób aby tamperatura zasilania osiągnęła
-- zadany poziom (setting.ac.cool.supply.t.demand lub setting.ac.heat.supply.t.demand).
--
-- W trybie chłodzenia:
--
-- - jeżeli temperatura zasilania jest za wysoka to sygnał sterujący wydajnością agregatu zostanie zwiększony
-- o wartość setting.ac.signal.change.value. Kolejna zmiana wartości sygnału sterującego wydajnością agregatu będzie możliwa dopiero po
-- czasie setting.ac.signal.timer.idle.
--
-- - jeżeli temperatura zasilania jest za niska to sygnał sterujący wydajnością agregatu zostanie zmniejszony
-- o wartość setting.ac.signal.change.value. Kolejna zmiana wartości sygnału sterującego wydajnością agregatu będzie możliwa dopiero po
-- czasie setting.ac.signal.timer.idle.
--
-- W trybie ogrzewania zależność jest odwrócona.
--
-- Jeżeli jakikolwiek czujnik temperatury jest uszkodzony to wszystkie wyjścia przyjmują wartość 0.
--
-- ======================
-- Parameters
-- ======================
--
-- SubLogic supports following variables:
--
-- input.inside.t.value
-- input.inside.t.err - temperatura wewnętrzna
--
-- input.vent.ac.supply.t.value
-- input.vent.ac.supply.t.err - temperatura nawiewu za chłodnicą
--
-- input.free.cooling - czy freecooling jest aktualnie realizowany [0..1]
--
-- input.free.heating - czy freeheating jest aktualnie realizowany [0..1]
--
-- input.season - 0 - winter, 1 - summer [0..1]
--
-- output.heating.signal - sygnał wyjściowy na agregat załączenia chłodzenia [0..1]
--
-- output.cooling.signal - sygnał wyjściowy na agregat załączenia chłodzenia [0..1]
--
-- output.ac.signal - sygnał sterujący wydajnością agregatu [0..100%]
--
-- setting.heating.enabled - czy jest możliwe realizowanie procesu ogrzewania
--
-- setting.cooling.enabled - czy jest możliwe realizowanie procesu chłodzenia
--
-- setting.ac.cool.supply.t.demand - temperatura wymagana na zasilaniu podczas chłodzenia
--
-- setting.ac.heat.supply.t.demand - temperatura wymagana na zasilaniu podczas ogrzewania
--
-- setting.ac.supply.t.hyst - histereza dla operacji powiązanych z temperaturą zasilania ac (w zakresie
-- histerezy sygnał sterujący wydajnością agregatu nie zmienia wartości)
--
-- setting.inside.t.demand - temperatura wymagana wewnętrzna
--
-- setting.inside.t.hyst - histereza dla operacji powiązanych z temperaturą wewnętrzną
--
-- setting.ac.signal.max - maksymalna wartość sygnału sterującego wydajnością agregatu [0..100%]
--
-- setting.ac.signal.min - minimalna wartość sygnału sterującego wydajnością agregatu [0..100%]
--
-- setting.ac.signal.change.value - wartość zmiany sygnału sterującego wydajnością agregatu w każdym kroku [%]
--
-- setting.ac.signal.timer.idle - czas bezczynności sygnału sterującego wydajnością agregatu [s]
--
-- setting.free.cooling.mode - tryb obsługi freecooling
-- 0 - chłodzenie/ogrzewanie zawsze wyłączone przy freecooling
-- 1 - chłodzenie/ogrzewanie niezależne od freecooling
--
-- setting.free.heating.mode - tryb obsługi freeheating
-- 0 - chłodzenie/ogrzewanie zawsze wyłączone przy freeheating
-- 1 - chłodzenie/ogrzewanie niezależne od freeheating
--
-- counter.ac.signal.idle.downcounter - licznik czasu bezczynności dla sygnału sterującego wydajnością agregatu [s]
--
-- counter.inside.t.demand.heating - flaga: czy jest wymagane ogrzewanie.
--
-- counter.inside.t.demand.cooling - flaga: czy jest wymagane chłodzenie.
--
-- counter.supply.t.demand - aktualnie wymagana temperatura na zasilaniu
--
-- counter.alert.t.state - flaga: wartość 1 oznacza uszkodzenie jakiegokolwiek czujnika. Uszkodzenie czujnika nie ma wpływu na prace kotła gazowego.
--
-- 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
-- ======================
--
-- 2019-05-30 ver 1.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_outputAcSignalOutput = nil;
g_outputHeatingSignal = nil;
g_outputCoolingSignal = nil;
g_counterAcSignalTimerIdle = nil;
g_counterInsideTDemandHeating = nil;
g_counterInsideTDemandCooling = nil;
SUPPORTED_SUBLOGIC_TYPE = "AC01";
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 inputVentAcSupplyTValue = getLogicValue("input.vent.ac.supply.t.value");
local inputVentAcSupplyTErr = getLogicValue("input.vent.ac.supply.t.err");
local inputFreeCooling = getLogicValue("input.free.cooling");
local inputFreeHeating = getLogicValue("input.free.heating");
local inputSeason = getLogicValue("input.season");
local settingHeatingEnabled = getLogicValue("setting.heating.enabled");
local settingCoolingEnabled = getLogicValue("setting.cooling.enabled");
local settingAcCoolSupplyTDemand = getLogicValue("setting.ac.cool.supply.t.demand");
local settingAcHeatSupplyTDemand = getLogicValue("setting.ac.heat.supply.t.demand");
local settingAcSupplyTHyst = getLogicValue("setting.ac.supply.t.hyst");
local settingInsideTDemand = getLogicValue("setting.inside.t.demand");
local settingInsideTHyst = getLogicValue("setting.inside.t.hyst");
local settingAcSignalMax = getLogicValue("setting.ac.signal.max");
local settingAcSignalMin = getLogicValue("setting.ac.signal.min");
local settingAcSignalChangeValue = getLogicValue("setting.ac.signal.change.value");
local settingAcSignalTimerIdle = getLogicValue("setting.ac.signal.timer.idle");
local settingFreeCoolingMode = getLogicValue("setting.free.cooling.mode");
local settingFreeHeatingMode = getLogicValue("setting.free.heating.mode");
local counterAcSignalIdleDowncounter = 0;
local counterSupplyTDemand = 0;
local counterAlertTState = 0;
-- timers
if g_counterAcSignalTimerIdle == nil or firstCall then
g_counterAcSignalTimerIdle = DownCounter.create();
end
--g_counterAcSignalTimerIdle:updateParams(settingAcSignalTimerIdle * 1000);
-- if g_counterValveTimerIdle:elapsed() then
-- g_counterValveTimerIdle:reset();
-- end
counterAcSignalIdleDowncounter = g_counterAcSignalTimerIdle:timeTo0() / 1000;
counterAcSignalIdleDowncounter = (counterAcSignalIdleDowncounter < 0) and 0 or counterAcSignalIdleDowncounter;
-- states
if g_outputAcSignalOutput == nil or firstCall then
g_outputAcSignalOutput = State.create(0, 0, 100, true);
end
g_outputAcSignalOutput:call();
if g_outputHeatingSignal == nil or firstCall then
g_outputHeatingSignal = State.create(0, 0, 1, true);
end
g_outputHeatingSignal:call();
if g_outputCoolingSignal == nil or firstCall then
g_outputCoolingSignal = State.create(0, 0, 1, true);
end
g_outputCoolingSignal:call();
if g_counterInsideTDemandHeating == nil or firstCall then
g_counterInsideTDemandHeating = State.create(0, 0, 1, true);
end
g_counterInsideTDemandHeating:call();
if g_counterInsideTDemandCooling == nil or firstCall then
g_counterInsideTDemandCooling = State.create(0, 0, 1, true);
end
g_counterInsideTDemandCooling:call();
-- obsługa alarmu temperatury
if ((inputVentAcSupplyTErr ~= 0) or (inputInsideTErr ~=0)) then
counterAlertTState = 1;
g_outputAcSignalOutput:setValue(0);
g_outputHeatingSignal:setValue(0);
g_outputCoolingSignal:setValue(0);
end
if (settingHeatingEnabled == 0) then
g_outputHeatingSignal:setValue(0);
end
if (settingCoolingEnabled == 0) then
g_outputCoolingSignal:setValue(0);
end
g_counterInsideTDemandHeating:set1IfLowerThan(inputInsideTValue, settingInsideTDemand, settingInsideTHyst);
g_counterInsideTDemandCooling:set1IfHigherThan(inputInsideTValue, settingInsideTDemand, settingInsideTHyst);
-- blokuj przy aktywnym freeheating
if (settingFreeHeatingMode == 0) and (inputFreeHeating == 1) then
g_outputHeatingSignal:setValue(0);
end
-- blokuj przy aktywnym freecooling
if (settingFreeCoolingMode == 0) and (inputFreeCooling == 1) then
g_outputCoolingSignal:setValue(0);
end
if (inputSeason == 0) then
-- winter
counterSupplyTDemand = settingAcHeatSupplyTDemand;
g_outputCoolingSignal:setValue(0);
g_outputHeatingSignal:setValue(g_counterInsideTDemandHeating:getValue());
else
-- summer
counterSupplyTDemand = settingAcCoolSupplyTDemand;
g_outputHeatingSignal:setValue(0);
g_outputCoolingSignal:setValue(g_counterInsideTDemandCooling:getValue());
end
if (g_outputCoolingSignal:getValue() == 0) and (g_outputHeatingSignal:getValue() == 0) then
g_outputAcSignalOutput:setValue(0);
else
if (g_outputAcSignalOutput:getValue() < settingAcSignalMin) then
g_outputAcSignalOutput:setValue(settingAcSignalMin);
end
if (g_outputAcSignalOutput:getValue() > settingAcSignalMax) then
g_outputAcSignalOutput:setValue(settingAcSignalMax);
end
end
if ((inputVentAcSupplyTValue < counterSupplyTDemand + settingAcSupplyTHyst / 2)
and (inputVentAcSupplyTValue > counterSupplyTDemand - settingAcSupplyTHyst / 2)
) then
g_counterAcSignalTimerIdle:updateParams(1);
g_counterAcSignalTimerIdle:reset();
else
g_counterAcSignalTimerIdle:updateParams(settingAcSignalTimerIdle * 1000);
end
if (g_outputHeatingSignal:getValue() == 1) then
-- ogrzewanie (za zimno to zwiększ wartość)
-- sterowanie wartość sygnału
if g_counterAcSignalTimerIdle:elapsed() then
if (inputVentAcSupplyTValue > counterSupplyTDemand + settingAcSupplyTHyst / 2) then
-- zmniejsz wartość sygnału
if ((g_outputAcSignalOutput:getValue() - settingAcSignalChangeValue) < settingAcSignalMin) then
g_outputAcSignalOutput:setValue(settingAcSignalMin);
else
g_outputAcSignalOutput:setValue(g_outputAcSignalOutput:getValue() - settingAcSignalChangeValue);
end
elseif (inputVentAcSupplyTValue < counterSupplyTDemand - settingAcSupplyTHyst / 2) then
-- zwiększ wartość sygnału
if ((g_outputAcSignalOutput:getValue() + settingAcSignalChangeValue) > settingAcSignalMax) then
g_outputAcSignalOutput:setValue(settingAcSignalMax);
else
g_outputAcSignalOutput:setValue(g_outputAcSignalOutput:getValue() + settingAcSignalChangeValue);
end
else
-- bez zmian
end
g_counterAcSignalTimerIdle:updateParams(settingAcSignalTimerIdle * 1000);
g_counterAcSignalTimerIdle:reset();
end
else
-- chłodzenie (za zimno to mniejsz wartość)
-- sterowanie wartość sygnału
if g_counterAcSignalTimerIdle:elapsed() then
if (inputVentAcSupplyTValue < counterSupplyTDemand + settingAcSupplyTHyst / 2) then
-- zmniejsz wartość sygnału
if ((g_outputAcSignalOutput:getValue() - settingAcSignalChangeValue) < settingAcSignalMin) then
g_outputAcSignalOutput:setValue(settingAcSignalMin);
else
g_outputAcSignalOutput:setValue(g_outputAcSignalOutput:getValue() - settingAcSignalChangeValue);
end
elseif (inputVentAcSupplyTValue > counterSupplyTDemand - settingAcSupplyTHyst / 2) then
-- zwiększ wartość sygnału
if ((g_outputAcSignalOutput:getValue() + settingAcSignalChangeValue) > settingAcSignalMax) then
g_outputAcSignalOutput:setValue(settingAcSignalMax);
else
g_outputAcSignalOutput:setValue(g_outputAcSignalOutput:getValue() + settingAcSignalChangeValue);
end
else
-- bez zmian
end
g_counterAcSignalTimerIdle:updateParams(settingAcSignalTimerIdle * 1000);
g_counterAcSignalTimerIdle:reset();
end
end
-- send logic variables to ibmanager
setLogicValue("output.heating.signal", g_outputHeatingSignal:getValue());
setLogicValue("output.cooling.signal", g_outputCoolingSignal:getValue());
setLogicValue("output.ac.signal", g_outputAcSignalOutput:getValue());
setLogicValue("counter.ac.signal.idle.downcounter", counterAcSignalIdleDowncounter);
setLogicValue("counter.inside.t.demand.heating", g_counterInsideTDemandHeating:getValue());
setLogicValue("counter.inside.t.demand.cooling", g_counterInsideTDemandCooling:getValue());
setLogicValue("counter.supply.t.demand", counterSupplyTDemand);
setLogicValue("counter.alert.t.state", counterAlertTState);
end