Własne moduły#

vCLU pozwala tworzyć własne moduły Lua w katalogu modules/. Moduły są ładowane po wszystkich plikach systemowych i proxy, więc mają dostęp do całego runtime - registry, obiektów zdalnych, helperów, timerów itd.

Struktura katalogu#

modules/
  init.lua              ← punkt wejścia (ładowany automatycznie)
  lighting.lua          ← moduł jako pojedynczy plik
  heating/
    init.lua            ← moduł jako katalog (require("heating"))
    zones.lua           ← podmoduł (require("heating.zones"))
    schedule.lua        ← podmoduł (require("heating.schedule"))

Katalog modules/ jest widoczny i edytowalny w edytorze (/editor). Można tam tworzyć pliki, katalogi i podkatalogi.

init.lua#

Plik modules/init.lua jest ładowany automatycznie przy starcie vCLU. Służy jako punkt wejścia - tutaj ładujemy własne moduły przez require():

-- modules/init.lua

local lighting = require("lighting")
local heating = require("heating")

print("[MODULES] User modules initialized")

Jeżeli modules/init.lua nie istnieje, system pomija ładowanie modułów bez błędu.

require()#

require() szuka plików w katalogu modules/ według dwóch wzorców:

WywołanieSzukany plik
require("lighting")modules/lighting.lua
require("heating")modules/heating/init.lua
require("heating.zones")modules/heating/zones.lua

Lua cachuje wynik require() w package.loaded - każdy moduł jest wykonywany tylko raz, niezależnie od tego ile razy go zaimportujemy.

Tworzenie modułu#

Moduł to plik Lua, który zwraca tablicę z funkcjami i danymi:

-- modules/lighting.lua

local M = {}

function M.allOff()
    local lights = _:byTag("lights")
    lights:execute(DOUT.METHOD_SWITCH_OFF)
    log.info("Wszystkie swiatla wylaczone")
end

function M.allOn()
    local lights = _:byTag("lights")
    lights:execute(DOUT.METHOD_SWITCH_ON)
end

function M.toggle(name)
    local obj = _:get(name)
    if obj then
        obj:toggle()
    end
end

return M

Przykład: moduł ogrzewania#

Moduł podzielony na kilka plików w katalogu:

modules/
  init.lua
  heating/
    init.lua
    zones.lua
    schedule.lua

heating/zones.lua#

-- modules/heating/zones.lua

local M = {}

M.config = {
    salon   = { thermostat = "CLU.THERM1", target = 21.5 },
    sypialnia = { thermostat = "CLU.THERM2", target = 20.0 },
    lazienka  = { thermostat = "CLU.THERM3", target = 23.0 },
}

function M.setTarget(zone, temp)
    local cfg = M.config[zone]
    if not cfg then
        log.warn("Nieznana strefa: %s", zone)
        return
    end
    cfg.target = temp
    local obj = _:get(cfg.thermostat)
    if obj then
        obj:set(0, temp)
        log.info("Strefa %s: cel %.1f°C", zone, temp)
    end
end

function M.getAll()
    local result = {}
    for zone, cfg in pairs(M.config) do
        local obj = _:get(cfg.thermostat)
        result[zone] = {
            target = cfg.target,
            current = obj and obj:get(0) or nil,
        }
    end
    return result
end

return M

heating/schedule.lua#

-- modules/heating/schedule.lua

local zones = require("heating.zones")

local M = {}

function M.nightMode()
    zones.setTarget("salon", 19.0)
    zones.setTarget("sypialnia", 20.0)
    zones.setTarget("lazienka", 19.0)
    log.info("Tryb nocny aktywny")
end

function M.dayMode()
    zones.setTarget("salon", 21.5)
    zones.setTarget("sypialnia", 20.0)
    zones.setTarget("lazienka", 23.0)
    log.info("Tryb dzienny aktywny")
end

return M

heating/init.lua#

-- modules/heating/init.lua

local M = {}

M.zones = require("heating.zones")
M.schedule = require("heating.schedule")

return M

modules/init.lua#

-- modules/init.lua

local lighting = require("lighting")
local heating = require("heating")

-- Harmonogram ogrzewania
after(1000, function()
    local hour = os.date("*t").hour
    if hour >= 23 or hour < 6 then
        heating.schedule.nightMode()
    else
        heating.schedule.dayMode()
    end
end)

print("[MODULES] Loaded: lighting, heating")

Użycie w user.lua#

Moduły załadowane w init.lua są dostępne globalnie jeśli przypiszemy je do zmiennych globalnych, albo można użyć require() ponownie (zwróci wersję z cache):

-- user.lua

function onButtonPress()
    local lighting = require("lighting")
    lighting.allOff()
end

function onSceneEvening()
    local heating = require("heating")
    heating.schedule.nightMode()

    local lighting = require("lighting")
    lighting.toggle("LAMPA_SALON")
end

Kolejność ładowania#

1. bootstrap       ← runtime (EventBus, Registry, klasy...)
2. user.lua        ← funkcje użytkownika
3. om.lua          ← obiekty, event bindingi
4. proxy_*.lua     ← obiekty zdalne
5. modules/init.lua  ← własne moduły (require)
6. SYSTEM.Init()   ← EVENT_ON_INIT

Moduły ładują się jako ostatnie przed inicjalizacją - mają dostęp do wszystkich obiektów (lokalnych i zdalnych), registry, helperów i timerów.

Dobre praktyki#

  • Każdy moduł zwraca tablicę (return M) - nie zanieczyszcza globalnej przestrzeni nazw
  • Logika biznesowa w modułach, funkcje eventowe w user.lua
  • Podprojekty w podkatalogach z własnym init.lua
  • require() zamiast kopiowania kodu między plikami