useful_programs
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
useful_programs [2023/10/05 12:14] – [Table functions] emozolyak | useful_programs [2024/03/19 09:39] (current) – [Detection of change of state] emozolyak | ||
---|---|---|---|
Line 155: | Line 155: | ||
<code lua> | <code lua> | ||
+ | -- converts bytes to integer value | ||
+ | table.int = function(t) | ||
+ | local HEX_NUMBERING = 16 | ||
+ | | ||
+ | end | ||
+ | |||
+ | -- converts a table to hex view | ||
+ | function table.hexStr(hextab, | ||
+ | local hex = {} | ||
+ | for _, hexbyte in ipairs(hextab) do | ||
+ | table.insert(hex, | ||
+ | end | ||
+ | return table.concat(hex, | ||
+ | end | ||
+ | |||
+ | -- converts tab to bcd number | ||
+ | table.bcd = function(t) | ||
+ | local hex = table.hexStr(t) | ||
+ | local out = tonumber(hex) | ||
+ | return out | ||
+ | end | ||
+ | |||
+ | -- gets 2-char hex string from a byte | ||
+ | function getHexByteAsStr(inputByte) | ||
+ | local strByte = string.format(" | ||
+ | return (#strByte == 1 and ' | ||
+ | end | ||
+ | |||
+ | |||
+ | |||
-- reverses a table | -- reverses a table | ||
table.reverse = function (tab) | table.reverse = function (tab) | ||
Line 222: | Line 252: | ||
return newTab | return newTab | ||
end | end | ||
+ | |||
+ | --[[ convenient print function for tables | ||
+ | @ t - table, indent - indent between key & value | ||
+ | --]] | ||
+ | function tprint(t, indent) | ||
+ | if not indent then indent = 0 end | ||
+ | for k, v in pairs(t) do | ||
+ | local formatting = string.rep(' | ||
+ | if type(v) == " | ||
+ | ERROR(formatting) | ||
+ | tprint(v, indent + 1) -- recursive call | ||
+ | else | ||
+ | if type(v) == " | ||
+ | v = v and " | ||
+ | end | ||
+ | ERROR(formatting .. v) | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | |||
</ | </ | ||
Line 300: | Line 350: | ||
<code lua> | <code lua> | ||
- | -- concats 2 tables processing boolean values | + | --[[ universal print function |
- | function | + | |
- | for i, v in ipairs(inputTable) do | + | |
- | if type(v) == ' | + | if arguments have | symbol treats data as a 2-row data |
- | inputTable[i] = v and " | + | " |
- | end | + | , arg1, arg2, arg3 ... |
- | end | + | if arg type == table print all using tprint |
- | | + | |
- | end | + | --]] |
- | -- find an element in a table, return index or false if not found | + | |
- | function table.find(inputTable, | + | |
- | for i, tv in pairs(inputTable) do | + | |
- | | + | |
- | return i | + | |
- | end | + | |
- | end | + | |
- | return false | + | |
- | end | + | |
- | -- centers two strings of diff length | + | |
- | function align2Strings(s1, s2) | + | |
- | local delta = #s1 - #s2 | + | |
- | local absDelta = math.abs(delta) | + | |
- | local prefix = string.rep(' | + | |
- | local suffix = prefix | + | |
- | if (delta > 0) then | + | |
- | s2 = prefix | + | |
- | elseif (delta < 0) then | + | |
- | s1 = prefix .. s1 .. suffix | + | |
- | end | + | |
- | | + | |
- | end | + | |
- | -- print a list of arguments with a header if ' | + | |
function DBG(...) | function DBG(...) | ||
- | if ENABLE_DEBUG then | + | |
+ | | ||
+ | |||
+ | -- piped printing | ||
local pipePosition = table.find(arg, | local pipePosition = table.find(arg, | ||
- | | + | |
- | DEBUG(myTabConcat(arg, ' ')) | + | -- make header row |
+ | local th = {} | ||
+ | for word in string.gmatch(arg[1], " | ||
+ | | ||
+ | end | ||
+ | -- make data row | ||
+ | local td = table.sub(arg, pipePosition + 1, #arg) | ||
+ | if (#th ~= #td) then | ||
+ | ERROR('th & td in DBG are not equal!' | ||
else | else | ||
- | if (pipePosition ~= 2) then | + | for i = 1, #th do |
- | ERROR(" | + | local thl, tdl = string.len(th[i]), string.len(td[i]) |
- | end | + | local diff = math.abs(thl - tdl) |
- | local thead, tdata = {}, {} | + | if (thl > tdl) then |
- | local valStartIndex, | + | |
- | for th in string.gmatch(arg[1], " | + | |
- | valCurIndex | + | |
- | if (valCurIndex | + | |
- | | + | |
end | end | ||
- | th, td = align2Strings(th, tostring(arg[valCurIndex])) | + | |
- | | + | |
- | end | + | end |
- | if (valCurIndex < valEndIndex) then | + | end |
- | ERROR(" | + | DEBUG(table.concat(th, ' ')) ; DEBUG(table.concat(td, ' ')) |
- | end | + | |
- | DEBUG(myTabConcat(thead, ' ') ) ; DEBUG(myTabConcat(tdata, ' ') ) | + | |
end | end | ||
- | | + | return |
+ | | ||
+ | -- table print | ||
+ | if table.some(arg, | ||
+ | tprint(arg) ; return | ||
+ | end | ||
+ | -- print in a row | ||
+ | DEBUG(table.concat(arg, | ||
end | end | ||
+ | --[[ add extra spaces on both sides of string | ||
+ | @ s - string | ||
+ | n - number of spaces | ||
+ | --> string wrapped in spaces | ||
+ | --]] | ||
+ | function wrapToSpaces(s, | ||
+ | local spaces = string.rep(' | ||
+ | return | ||
+ | .. ((n % 2 == 0) and spaces or spaces .. ' ') | ||
+ | end | ||
</ | </ | ||
Then the print output can be enhanced like this: | Then the print output can be enhanced like this: | ||
Line 416: | Line 465: | ||
| | ||
end | end | ||
+ | </ | ||
+ | |||
+ | <code lua> | ||
+ | -- converting utc timestamp in human readable string | ||
+ | function getDateString(utc) | ||
+ | return os.date(" | ||
+ | end | ||
</ | </ | ||
Line 466: | Line 522: | ||
==== Simple TON timer ==== | ==== Simple TON timer ==== | ||
<code lua> | <code lua> | ||
- | -------------------------------- TON simple timer ----------------------------- | + | -- Timer constructor |
- | TonClass | + | Timer = {} ; setmetatable(Timer, |
- | function | + | local obj = {stamp = os.time(), out = false} |
- | | + | |
- | return setmetatable({stamp = os.time(), out = false,}, self) | + | end }) |
+ | -- Timer loop | ||
+ | function Timer : run(in_, delay) | ||
+ | local now = os.time() | ||
+ | if (not in_) then | ||
+ | self.out = false ; self.stamp = now | ||
+ | else | ||
+ | if (not self.out and (now - self.stamp) >= delay) then | ||
+ | | ||
+ | end | ||
+ | end | ||
+ | | ||
end | end | ||
- | + | function | |
- | function | + | |
- | local now = os.time() | + | |
- | if (inputCondition ~= self.out) then | + | |
- | if (not inputCondition) then | + | |
- | | + | |
- | else | + | |
- | local timeDifference = now - self.stamp | + | |
- | if (timeDifference < onDelay) then | + | |
- | DEBUG(" | + | |
- | return self.out | + | |
- | else | + | |
- | self.out = true ; DEBUG(" | + | |
- | end | + | |
- | end | + | |
- | end | + | |
- | + | ||
- | | + | |
- | return self.out | + | |
- | end -------------------------------- end of timer ------------------------------ | + | |
- | + | ||
- | function main (userId) | + | |
- | local REQUIRED_DELAY = 15 -- seconds | + | |
- | if not TonTimer then | + | |
- | TonTimer = TonClass :new() | + | |
- | end | + | |
- | + | ||
- | DEBUG(" | + | |
- | + | ||
- | end | + | |
</ | </ | ||
Line 622: | Line 660: | ||
| | ||
+ | end | ||
+ | |||
+ | </ | ||
+ | |||
+ | This one uses simper timer: | ||
+ | <code lua> | ||
+ | include " | ||
+ | CHAT_ID = 569335646 | ||
+ | MSG_DELAY = 5 * 60 | ||
+ | |||
+ | function main (userId) | ||
+ | | ||
+ | reportConnError(2, | ||
+ | | ||
+ | end | ||
+ | |||
+ | function reportConnError(connId, | ||
+ | | ||
+ | if (not tmr) then | ||
+ | tmr = Timer() | ||
+ | end | ||
+ | | ||
+ | if tmr : run(GetConnectionErrors(connId) > 0, delay) then | ||
+ | SendTelegramMessage(chatId, | ||
+ | tmr : reset() | ||
+ | end | ||
end | end | ||
Line 855: | Line 919: | ||
Another way of creating objects: | Another way of creating objects: | ||
<code lua> | <code lua> | ||
- | ObjConstructor | + | TimerConstructor |
- | setmetatable(ObjConstructor, { | + | return |
- | __call = function(self, | + | __call = function(self, |
- | local o = { | + | local now = os.time() |
- | | + | if (not self.stamp or now - self.stamp >= delay) then |
- | } | + | |
- | | + | return |
- | return | + | else |
- | end | + | return false |
- | }) | + | end |
+ | end | ||
+ | }) end | ||
- | function ObjConstructor : oneShot() | + | -- usage |
- | + | ||
- | local now = os.time() | + | function main (userId) |
- | + | | |
- | if (not self.stamp or (now - self.stamp >= self.delay) ) | + | if (not timer) then |
- | self.stamp | + | |
- | return true | + | |
- | else | + | |
- | return false | + | |
end | end | ||
| | ||
+ | if timer (60) then | ||
+ | INFO(' | ||
+ | for i = 1, 8 do | ||
+ | toggle(' | ||
+ | end | ||
+ | end | ||
end | end | ||
- | function | + | function |
- | + | W(v, 1 - R(v)) | |
- | if (not tmr) then | + | end |
- | local ONE_SHOT_DELAY = 10 | + | |
- | tmr = ObjConstructor(ONE_SHOT_DELAY) | + | |
- | end | + | |
- | + | ||
- | DEBUG( tostring(tmr : oneShot() ) | + | |
- | | + | |
- | end | + | |
- | + | ||
</ | </ | ||
Line 1454: | Line 1515: | ||
===== Detection of change of state ===== | ===== Detection of change of state ===== | ||
- | Sometimes you may need take actions upon changing any of the registers in a set. The following | + | Sometimes you may need take actions upon changing any of the registers in a set. The following function |
<code lua> | <code lua> | ||
- | include "lib.lib" | + | OnChange = {} ; setmetatable(OnChange, |
+ | return function(v, cb) | ||
+ | local out = false | ||
+ | if (not self.prev) then | ||
+ | self.prev = v | ||
+ | end | ||
+ | if (self.prev and v ~= self.prev ) then | ||
+ | out = true | ||
+ | if cb then cb() end | ||
+ | end | ||
+ | self.prev = v | ||
+ | return out | ||
+ | end | ||
+ | end | ||
+ | }) | ||
function main (userId) | function main (userId) | ||
- | ------------------------ INIT (create globals) --------------------- | + | if (not onchange1) then |
- | if (not startedFlag) then | + | |
- | + | ||
- | detect = { curMinute = {regId = 1, prevValue = 0}, -- register' | + | |
- | | + | |
- | } | + | |
- | mt = {__call = function(self, | + | |
- | local me = self[row] | + | |
- | local curValue = R(me.regId) | + | |
- | + | ||
- | if (not curValue) then return false end | + | |
- | + | ||
- | local result | + | |
- | if (not startedFlag) then | + | |
- | | + | |
- | else | + | |
- | if (R(me.regId) ~= me.prevValue) then | + | |
- | | + | |
- | | + | |
- | else | + | |
- | | + | |
- | end | + | |
- | end | + | |
- | return result | + | |
- | end | + | |
- | } | + | |
- | setmetatable(detect, | + | |
- | | + | |
- | startedFlag = true -- avoid this block further | + | |
end | end | ||
- | | ||
- | |||
- | local alerts = GetCurrentAlerts() | ||
- | local alertCount = #alerts | ||
- | |||
- | if (alertCount > prevAlerts) or detect(" | ||
- | -- YOUR CODE FOR ACTIONS MIGHT BE HERE -- | ||
- | local jsonToSend = cjson.encode({stateRegister = R(2), activeAlerts = (#alerts > 0) and 1 or 0}) | ||
- | | ||
- | |||
- | W(4, jsonToSend) -- to external register | ||
- | W(6, jsonToSend) -- to custom protocol | ||
- | |||
- | | ||
- | | ||
- | | ||
| | ||
+ | onchange1(R(110), | ||
+ | ------- Detecting changes of a set of registers ---------------- | ||
+ | local regSet = {{110, function() | ||
+ | INFO(" | ||
+ | end }, | ||
+ | {1, function() | ||
+ | | ||
+ | end }, | ||
+ | {200, function() | ||
+ | INFO(" | ||
+ | end }, | ||
+ | |||
+ | } | ||
+ | | ||
+ | local handlers = {} | ||
+ | | ||
+ | -- registereing handlers | ||
+ | if (not handlers[regSet[1][1]]) then | ||
+ | for _, s in ipairs(regSet) do | ||
+ | handlers[s[1]] = OnChange() | ||
+ | end | ||
+ | end | ||
+ | |||
+ | -- using handlers | ||
+ | for _, s in ipairs(regSet) do | ||
+ | local reg, func = s[1], s[2] | ||
+ | handlers[reg](R(reg), | ||
+ | end | ||
end | end | ||
</ | </ | ||
Line 1525: | Line 1585: | ||
INVALID_VALUE | INVALID_VALUE | ||
- | filter = { {reg = 1, safeCopy = 2, errCount = 0}, -- what to filter | + | filter = { {reg = 1, safeCopy = 2, errCount = 0}, -- reg - source register |
- | {reg = 3, safeCopy = 4, errCount = 0} } -- and where to store | + | {reg = 3, safeCopy = 4, errCount = 0} } -- safeCopy - last read ok value |
function main (userId) | function main (userId) |
useful_programs.1696508064.txt.gz · Last modified: 2023/10/05 12:14 by emozolyak