Timery#
Timery w vCLU dzialaja na dwoch poziomach: helpery (after/every/cancel - najczesciej uzywane), niskopoziomowe (setTimeout/setInterval via Go) i wysokopoziomowe (modul Timer w stylu Grenton).
Helpery - after / every / cancel#
To jest zalecany sposob uzywania timerow w user.lua. Wszystkie sa asynchroniczne - rejestruja callback i wracaja natychmiast.
after(ms, fn)#
Wykonaj funkcje raz po opoznieniu. Zwraca ID timera.
-- Wlacz lampe za 5 sekund
after(5000, function()
RELAY1:execute(DOUT.METHOD_SWITCH_ON)
end)
-- Z anulowaniem
local id = after(10000, function()
log.info("za pozno!")
end)
cancel(id) -- anuluj zanim sie odpalievery(ms, fn)#
Wykonuj funkcje cyklicznie. Zwraca ID timera.
-- Loguj temperature co minute
local id = every(60000, function()
local temp = _:get("CLU.ANA1"):get(0)
log.info("Temperatura: " .. temp)
end)
-- Zatrzymaj po godzinie
after(3600000, function()
cancel(id)
end)cancel(id)#
Anuluj timer (jednorazowy lub cykliczny).
local id = every(1000, function() print("tick") end)
cancel(id)Asynchronicznosc#
after() i every() nie blokuja - rejestruja callback w Go i wracaja natychmiast:
log.info("przed")
after(1000, function()
log.info("po sekundzie")
end)
log.info("po after()") -- to wykona sie NATYCHMIAST, nie po sekundzieWynik:
[INFO] przed
[INFO] po after()
... (1 sekunda) ...
[INFO] po sekundzieLua w vCLU jest jednowatkowy. **Nigdy nie uzywaj petli oczekujacej** do opoznien - zablokuje caly runtime (zdarzenia, MQTT, GPIO, timery). ```lua -- ZLE - zawiesi CLU! local start = os.clock() while os.clock() - start < 1 do end doSomething() -- DOBRZE after(1000, function() doSomething() end) ```
Kaskada z after#
-- Wlaczanie 3 lamp po kolei, co 500ms
r1:execute(DOUT.METHOD_SWITCH_ON)
after(500, function() r2:execute(DOUT.METHOD_SWITCH_ON) end)
after(1000, function() r3:execute(DOUT.METHOD_SWITCH_ON) end)Oba after() rejestruja sie natychmiast - czasy licza sie od momentu wywolania, nie od siebie nawzajem.
Dla dluzszych sekwencji uzyj `sequence()` z [Helperow](../helpers) - jest czytelniejszy niz zagniezdzony `after()`.
Niskopoziomowe - setTimeout / setInterval#
Zarzadzane przez TimerManager w Go. Helpery after/every/cancel sa nakładką na te funkcje.
Przez EventBus#
local eb = EventBus:getShared()
-- Jednorazowy (odpala raz po 2s)
local id = eb:setTimeout(function()
print("boom!")
end, 2000)
-- Powtarzajacy sie (co 500ms)
local id = eb:setInterval(function()
print("tick")
end, 500)
-- Anulowanie
eb:clearInterval(id)Przez TIMERS#
local id = TIMERS:setTimeout(1000, function() print("raz") end)
local id = TIMERS:setInterval(500, function() print("tick") end)
TIMERS:clearTimer(id)Limity#
| Parametr | Wartosc |
|---|---|
| Max liczba timerow | 1000 |
| Min interwal (setInterval/every) | 10ms |
| Min timeout (setTimeout/after) | 1ms |
| Max okres | 24h (86 400 000ms) |
Po przekroczeniu limitu 1000 timerow nowe sa odrzucane z logiem [TIMER] REJECTED. Jesli za szybko ustawiasz interwaly (< 10ms), sa automatycznie podnoszone do 10ms.
Jak dziala pod spodem#
┌──────────────┐ fireQueue ┌──────────────┐
│ TimerManager │ ──────────────────▶ │ RunLoop │
│ goroutine │ channel (1000) │ (main Lua) │
│ 10ms ticker │ │ │
└──────────────┘ └──────────────┘- TimerManager dziala w osobnej goroutine z tickerem 10ms
- Sprawdza
NextFirekazdego aktywnego timera - Gotowe timery trafiaja do
fireQueue(buffered channel, rozmiar 1000) - RunLoop w main Lua thread pobiera timery z kolejki i wykonuje callbacki
- Timeout - usuwany po odpaleniu; interval - przesuwa
NextFireoPeriod
Wysokopoziomowe - modul Timer#
Modul Timer emuluje zachowanie timera z systemu Grenton. Uzywany w om.lua i user.lua. Kompatybilny z OM - Object Manager moze tworzyc timery w om.lua.
Tworzenie#
-- Recznie w user.lua
local t = Timer:new("MojTimer", 0, EventBus:getShared())
t:set(Timer.FEATURE_TIME, 5000) -- 5 sekund
t:set(Timer.FEATURE_MODE, Timer.MODE_INTERVAL)
t:execute(Timer.METHOD_START)Tryby#
| Stala | Wartosc | Opis |
|---|---|---|
Timer.MODE_COUNT_DOWN | 0 | Jednorazowy - odpala raz i zatrzymuje sie |
Timer.MODE_INTERVAL | 1 | Cykliczny - odpala powtarzalnie |
Stany#
| Stala | Wartosc | Opis |
|---|---|---|
Timer.STATE_STOPPED | 0 | Zatrzymany |
Timer.STATE_COUNTING | 1 | Liczy (aktywny) |
Timer.STATE_PAUSED | 2 | Wstrzymany (pauza) |
Cechy (Features)#
| Stala | Wartosc | R/W | Opis |
|---|---|---|---|
Timer.FEATURE_TIME | 0 | R/W | Czas w milisekundach |
Timer.FEATURE_MODE | 1 | R/W | Tryb (COUNT_DOWN / INTERVAL) |
Timer.FEATURE_STATE | 2 | R | Aktualny stan |
Timer.FEATURE_VALUE | 3 | R | Pozostaly czas do odpalenia (ms) |
Metody (execute)#
| Stala | Wartosc | Opis |
|---|---|---|
Timer.METHOD_START | 0 | Uruchom / wznow po pauzie |
Timer.METHOD_STOP | 1 | Zatrzymaj calkowicie |
Timer.METHOD_PAUSE | 2 | Wstrzymaj (mozna wznowic) |
Zdarzenia#
| Stala | Wartosc | Opis |
|---|---|---|
Timer.EVENT_ON_TIMER | 0 | Timer odpalil (countdown zakonczony lub interwal) |
Timer.EVENT_ON_START | 1 | Timer uruchomiony |
Timer.EVENT_ON_STOP | 2 | Timer zatrzymany |
Timer.EVENT_ON_PAUSE | 3 | Timer wstrzymany |
API#
-- Sterowanie
t:execute(Timer.METHOD_START)
t:execute(Timer.METHOD_PAUSE)
t:execute(Timer.METHOD_START) -- wznow po pauzie (kontynuuje od miejsca)
t:execute(Timer.METHOD_STOP)
-- Odczyt
t:get(Timer.FEATURE_STATE) -- 0=stopped, 1=counting, 2=paused
t:get(Timer.FEATURE_VALUE) -- pozostaly czas w ms
-- Konfiguracja
t:set(Timer.FEATURE_TIME, 10000) -- 10 sekund
t:set(Timer.FEATURE_MODE, Timer.MODE_INTERVAL) -- cykliczny
-- Zdarzenia
t:add_event(Timer.EVENT_ON_TIMER, function()
log.info("Timer odpalil!")
end)
t:add_event(Timer.EVENT_ON_STOP, function()
log.info("Timer zatrzymany")
end)Pauza i wznowienie#
Timer wspiera pauze - przy wznowieniu kontynuuje od miejsca, nie od poczatku:
local t = Timer:new("MojTimer", 0, EventBus:getShared())
t:set(Timer.FEATURE_TIME, 10000) -- 10 sekund
t:set(Timer.FEATURE_MODE, Timer.MODE_COUNT_DOWN)
t:execute(Timer.METHOD_START)
-- Po 3 sekundach: pauza
after(3000, function()
t:execute(Timer.METHOD_PAUSE)
log.info("Pozostalo: " .. t:get(Timer.FEATURE_VALUE) .. "ms") -- ~7000ms
end)
-- Po 8 sekundach: wznow (doliczy pozostale ~7s)
after(8000, function()
t:execute(Timer.METHOD_START) -- wznawia od ~7000ms
end)Przyklady#
Cykliczne odpytywanie czujnika#
local timer = Timer:new("SensorPoll", 0, EventBus:getShared())
timer:set(Timer.FEATURE_TIME, 30000) -- co 30 sekund
timer:set(Timer.FEATURE_MODE, Timer.MODE_INTERVAL)
timer:add_event(Timer.EVENT_ON_TIMER, function()
local temp = _:get("CLU.ANA1"):get(0)
if temp > 28 then
_:get("CLU.DOU5048"):execute(DOUT.METHOD_SWITCH_ON)
end
end)
timer:execute(Timer.METHOD_START)Countdown - wylacz po czasie#
local autoOff = Timer:new("AutoOff", 0, EventBus:getShared())
autoOff:set(Timer.FEATURE_TIME, 300000) -- 5 minut
autoOff:set(Timer.FEATURE_MODE, Timer.MODE_COUNT_DOWN)
autoOff:add_event(Timer.EVENT_ON_TIMER, function()
_:get("CLU.DOU5048"):execute(DOUT.METHOD_SWITCH_OFF)
log.info("Auto-wylaczenie po 5 minutach")
end)
-- Uruchom countdown gdy lampa sie wlaczy
_:get("CLU.DOU5048"):add_event(DOUT.EVENT_ON_SWITCH_ON, function()
autoOff:execute(Timer.METHOD_START)
end)Timer z OM#
Timery tworzone przez Object Manager w om.lua uzywaja tego samego API:
-- W om.lua (generowane przez OM):
Timer1 = Timer:new("Timer1", 0, EventBus:getShared())
Timer1:set(Timer.FEATURE_TIME, 5000)
Timer1:set(Timer.FEATURE_MODE, Timer.MODE_INTERVAL)
-- W user.lua:
Timer1:add_event(Timer.EVENT_ON_TIMER, function()
log.info("Timer z OM odpalil!")
end)
Timer1:execute(Timer.METHOD_START)Kiedy uzywac czego#
| Potrzeba | Uzyj |
|---|---|
| Proste opoznienie | after(ms, fn) |
| Cykliczne powtarzanie | every(ms, fn) |
| Kaskada akcji | sequence() z Helperow |
| Pauza / wznowienie | Modul Timer |
| Odczyt pozostalego czasu | Modul Timer (FEATURE_VALUE) |
| Zdarzenia start/stop/pause | Modul Timer (EVENT_ON_*) |
| Kompatybilnosc z OM | Modul Timer |
Czyszczenie timerow#
Przy resecie silnika Lua (Reinitialize()), ClearAll() anuluje wszystkie aktywne timery - zapobiega zombie callbackom ze starego stanu.