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 odpali

every(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 sekundzie

Wynik:

[INFO] przed
[INFO] po after()
... (1 sekunda) ...
[INFO] po sekundzie
Lua 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#

ParametrWartosc
Max liczba timerow1000
Min interwal (setInterval/every)10ms
Min timeout (setTimeout/after)1ms
Max okres24h (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 │                    │              │
└──────────────┘                    └──────────────┘
  1. TimerManager dziala w osobnej goroutine z tickerem 10ms
  2. Sprawdza NextFire kazdego aktywnego timera
  3. Gotowe timery trafiaja do fireQueue (buffered channel, rozmiar 1000)
  4. RunLoop w main Lua thread pobiera timery z kolejki i wykonuje callbacki
  5. Timeout - usuwany po odpaleniu; interval - przesuwa NextFire o Period

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#

StalaWartoscOpis
Timer.MODE_COUNT_DOWN0Jednorazowy - odpala raz i zatrzymuje sie
Timer.MODE_INTERVAL1Cykliczny - odpala powtarzalnie

Stany#

StalaWartoscOpis
Timer.STATE_STOPPED0Zatrzymany
Timer.STATE_COUNTING1Liczy (aktywny)
Timer.STATE_PAUSED2Wstrzymany (pauza)

Cechy (Features)#

StalaWartoscR/WOpis
Timer.FEATURE_TIME0R/WCzas w milisekundach
Timer.FEATURE_MODE1R/WTryb (COUNT_DOWN / INTERVAL)
Timer.FEATURE_STATE2RAktualny stan
Timer.FEATURE_VALUE3RPozostaly czas do odpalenia (ms)

Metody (execute)#

StalaWartoscOpis
Timer.METHOD_START0Uruchom / wznow po pauzie
Timer.METHOD_STOP1Zatrzymaj calkowicie
Timer.METHOD_PAUSE2Wstrzymaj (mozna wznowic)

Zdarzenia#

StalaWartoscOpis
Timer.EVENT_ON_TIMER0Timer odpalil (countdown zakonczony lub interwal)
Timer.EVENT_ON_START1Timer uruchomiony
Timer.EVENT_ON_STOP2Timer zatrzymany
Timer.EVENT_ON_PAUSE3Timer 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#

PotrzebaUzyj
Proste opoznienieafter(ms, fn)
Cykliczne powtarzanieevery(ms, fn)
Kaskada akcjisequence() z Helperow
Pauza / wznowienieModul Timer
Odczyt pozostalego czasuModul Timer (FEATURE_VALUE)
Zdarzenia start/stop/pauseModul Timer (EVENT_ON_*)
Kompatybilnosc z OMModul Timer

Czyszczenie timerow#

Przy resecie silnika Lua (Reinitialize()), ClearAll() anuluje wszystkie aktywne timery - zapobiega zombie callbackom ze starego stanu.