-- ======================
-- Description
-- ======================
--
-- Logika sterująca grupą stref w celu aktywowania procesu wyrównania temperatur w strefach.
--
-- Dla każdej strefy logika oblicza wartość "Temperatury minimalną" (counter.t.min) i wynosi ona: "Zadana wewnętrzna" (setting.t.demand) - "DT minimalnej dla temperatury wewnętrznej" (setting.t.min.delta)
--
-- Jeżeli jest aktywny sygnał alarmowy (input.alert) to procesu wyrównania temperatur jest wyłączony (wszystkie output.eheating = 0)
--
-- W ramach stref ustalany jest priorytet stref zgłaszających żądanie realizacji funkcji wyrównania temperatury strefy w kolejności od strefy
-- z najniższą temperaturą wewnętrzną.
--
-- Jeżeli temperatura wewnątrz strefy spadnie poniżej "Temperatury minimalnej" (counter.t.min) z uwzględnieniem histerezy 1*C (setting.t.min.hyst)
-- to następuje żądanie realizacji funkcji wyrównania temperatury strefy.
--
-- Realizacja funkcji wyrównania temperatury stref może odbywać się jednocześnie w setting.max.eheating strefach. W trakcie realizacji funkcji wyrównania
-- temperatury stref może nastąpić zmiana priorytetu, co oznacza, że może nastąpić dogrzewanie innej strefy nawet jeżeli strefa, w której przed chwilą
-- była realizowana funkcja wyrównania temperatury nie przyniosła rezultatu.
--
-- ======================
-- Parameters
-- ======================
--
-- SubLogic supports following variables:
--
-- input.alert - sygnał alarmowy. Jeżeli jest aktywny to procesu wyrównania temperatur jest wyłączony (wszystkie output.eheating = 0)
--
-- input.t.value
-- input.t.err - lista wartości: temperatura wewnętrzna
--
-- output.eheating - lista wartości: wyjście: realizacja procesu wyrównania temperatury [0..1]. Uwzględnia counter.eheating.enabled i odpowiedni priorytet.
--
-- setting.t.demand - lista wartości: wymagana temperatura wewnętrzna
--
-- setting.t.min.delta - lista wartości: DT minimalnej dla temperatury wewnętrznej
--
-- setting.t.min.hyst - lista wartości: histereza dla minimalnej temperatury wewnętrznej
--
-- setting.eheating.enabled - lista wartości: czy funkcja wyrównania temperatury jest aktywna
--
-- setting.max.eheating - równoczesna ilość stref w których może być realizowany proces wyrównania temperatury
--
-- counter.t.min.value - lista wartości: minimalna temperatura wewnętrzna do rozpoczęcia procesu wyrównania temperatury
-- ("wymagana wewnętrzna" - "DT minimalnej dla temperatury wewnętrznej")
--
-- counter.t.min.status - lista wartości: czy temperatura panująca jest poniżej wartości minimalnej
--
-- counter.eheating.enabled - lista wartości: czy funkcja wyrównania temperatury jest aktywna. Aby funkcja była aktywna to muszą
-- być spełnione równocześnie warunki:
-- - input.t.err <> 0
-- - input.alert <> 0
-- - setting.eheating.enabled = 1
-- - counter.t.min.status = 1
--
-- counter.priority - lista wartości: priorytet realizacji funkcji wyrównania temperatury jest aktywna. Im niższa wartość tym wyższy priorytet.
-- Wartość wyższa niż setting.max.eheating oznacza brak priorytetu
--
-- counter.zones.count - ilość stref
--
-- 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-12-11 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");
require("Functions");
g_outputEheating = {};
g_counterTMinStatus = {};
g_counterEheatingEnabled = {};
g_counterPriority = {};
g_sortedInputTValue = {};
SUPPORTED_SUBLOGIC_TYPE = "MultiZoneManager01";
SUPPORTED_SUBLOGIC_VERSION = "1.0.0";
g_versionChecked = false;
function getKeysSortedByValue(tbl, sortFunction)
local keys = {}
for key in pairs(tbl) do
table.insert(keys, key)
end
table.sort(keys, function(a, b)
return sortFunction(tbl[a], tbl[b])
end)
return keys
end
-- 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 inputAlert = getLogicValue("input.alert");
local inputTValueL = getVarList("input.t.value");
local inputTErrL = getVarList("input.t.err");
local settingTDemandL = getVarList("setting.t.demand");
local settingTMinDeltaL = getVarList("setting.t.min.delta");
local settingTMinHystL = getVarList("setting.t.min.hyst");
local settingEheatingEnabledL = getVarList("setting.eheating.enabled");
local settingMaxEheating = getLogicValue("setting.max.eheating");
local counterZonesCount = 0;
local tableZonesPostfix = {};
local inputTValue = {};
local inputTErr = {};
local settingTDemand = {};
local settingTMinDelta = {};
local settingTMinHyst = {};
local settingEheatingEnabled = {};
local counterTMinValue = {};
local inputTValueForSorting = {};
-- tablice wartości
if inputTValue ~= nil then
for key, value in pairs(inputTValueL) do
inputTValue['' .. key] = value;
tableZonesPostfix[counterZonesCount] = key
counterZonesCount = counterZonesCount + 1;
end
end
if inputTErrL ~= nil then
for postfix, value in pairs(inputTErrL) do
inputTErr['' .. postfix] = value;
end
end
if settingTDemandL ~= nil then
for postfix, value in pairs(settingTDemandL) do
settingTDemand['' .. postfix] = value;
end
end
if settingTMinDeltaL ~= nil then
for postfix, value in pairs(settingTMinDeltaL) do
settingTMinDelta['' .. postfix] = value;
end
end
if settingTMinHystL ~= nil then
for postfix, value in pairs(settingTMinHystL) do
settingTMinHyst['' .. postfix] = value;
end
end
if settingEheatingEnabledL ~= nil then
for postfix, value in pairs(settingEheatingEnabledL) do
settingEheatingEnabled['' .. postfix] = value;
end
end
-- states
if g_outputEheating == nil or firstCall then
for i = 0, counterZonesCount-1, 1 do
g_outputEheating['' .. tableZonesPostfix[i]] = State.create(0, 0, 1, true);
end
end
for i = 0, counterZonesCount-1, 1 do
g_outputEheating['' .. tableZonesPostfix[i]]:call();
end
if g_counterTMinStatus == nil or firstCall then
for i = 0, counterZonesCount-1, 1 do
g_counterTMinStatus['' .. tableZonesPostfix[i]] = State.create(0, 0, 1, true);
end
end
for i = 0, counterZonesCount-1, 1 do
g_counterTMinStatus['' .. tableZonesPostfix[i]]:call();
end
if g_counterEheatingEnabled == nil or firstCall then
for i = 0, counterZonesCount-1, 1 do
g_counterEheatingEnabled['' .. tableZonesPostfix[i]] = State.create(0, 0, 1, true);
end
end
for i = 0, counterZonesCount-1, 1 do
g_counterEheatingEnabled['' .. tableZonesPostfix[i]]:call();
end
if g_counterPriority == nil or firstCall then
for i = 0,counterZonesCount-1,1 do
g_counterPriority['' .. tableZonesPostfix[i]] = State.create(0, 0, settingMaxEheating + 1, true);
end
end
for i = 0, counterZonesCount-1, 1 do
g_counterPriority['' .. tableZonesPostfix[i]]:call();
end
for i = 0, counterZonesCount-1, 1 do
-- minimalna temperatura wewnętrzna do rozpoczęcia procesu wyrównania temperatury
counterTMinValue['' .. tableZonesPostfix[i]] = settingTDemand['' .. tableZonesPostfix[i]] - settingTMinDelta['' .. tableZonesPostfix[i]];
-- jeśli błąd temperatury lub inputAlert to nie realizuj procesu wyrównania temperatury
if (inputTErr['' .. tableZonesPostfix[i]] ~= 0) then
g_counterEheatingEnabled['' .. tableZonesPostfix[i]]:setValue(0);
counterTMinValue['' .. tableZonesPostfix[i]] = 0;
g_counterTMinStatus['' .. tableZonesPostfix[i]]:setValue(0);
end
-- jeśli inputAlert lub funkcja wyrównania temperatury jest nie aktywna to nie realizuj procesu wyrównania temperatury
if ((inputAlert ~= 0) or (settingEheatingEnabled['' .. tableZonesPostfix[i]] == 0)) then
g_counterEheatingEnabled['' .. tableZonesPostfix[i]]:setValue(0);
end
-- sprawdzenie czy temperatura poniżej temperatury minimalnej
g_counterTMinStatus['' .. tableZonesPostfix[i]]:set1IfLowerThan(inputTValue['' .. tableZonesPostfix[i]], counterTMinValue['' .. tableZonesPostfix[i]], settingTMinHyst['' .. tableZonesPostfix[i]]);
-- czy funkcja wyrównania temperatury jest aktywna
g_counterEheatingEnabled['' .. tableZonesPostfix[i]]:setValue(g_counterTMinStatus['' .. tableZonesPostfix[i]]:getValue());
-- jeżeli funkcja wyrównania temperatury jest nieaktywna to ustaw brak priorytetu
if (g_counterEheatingEnabled['' .. tableZonesPostfix[i]]:getValue() == 0) then
g_counterPriority['' .. tableZonesPostfix[i]]:setValue(settingMaxEheating + 1);
else
inputTValueForSorting['' .. tableZonesPostfix[i]] = inputTValue['' .. tableZonesPostfix[i]];
end
end
-- poustawiaj priorytety
-- posortowana tablica z postfixami tabeli z temperaturami (najmniejsza wartość na początku)
g_sortedInputTValue = getKeysSortedByValue(inputTValueForSorting, function(a, b) return a < b end)
-- ustaw priorytety w/g czasu pracy
i = 0;
for _, key in ipairs(g_sortedInputTValue) do
g_counterPriority['' .. key]:setValue(i);
i = i + 1;
end
for i = 0, counterZonesCount-1, 1 do
if ((g_counterEheatingEnabled['' .. tableZonesPostfix[i]]:getValue() ~= 0) and (g_counterPriority['' .. tableZonesPostfix[i]]:getValue()<settingMaxEheating)) then
g_outputEheating['' .. tableZonesPostfix[i]]:setValue(1);
end
g_outputEheating['' .. tableZonesPostfix[i]]:setValue(0);
end
-- print ('======');
-- print (tableZonesPostfix[i] .. ' = ' .. counterTMinValue['' .. tableZonesPostfix[i]]);
-- send logic variables to ibmanager
for i = 0, counterZonesCount-1,1 do
setLogicValue("output.eheating." .. tableZonesPostfix[i], g_outputEheating['' .. tableZonesPostfix[i]]:getValue());
setLogicValue("counter.t.min.value." .. tableZonesPostfix[i], counterTMinValue['' .. tableZonesPostfix[i]]);
setLogicValue("counter.t.min.status." .. tableZonesPostfix[i], g_counterTMinStatus['' .. tableZonesPostfix[i]]:getValue());
setLogicValue("counter.eheating.enabled." .. tableZonesPostfix[i], g_counterEheatingEnabled['' .. tableZonesPostfix[i]]:getValue());
setLogicValue("counter.priority." .. tableZonesPostfix[i], g_counterPriority['' .. tableZonesPostfix[i]]:getValue());
end
setLogicValue("counter.zones.count", counterZonesCount);
end