Model runtime#

Silnik Lua#

vCLU używa gopher-lua — czystej implementacji Lua 5.1 w Go. Silnik jest jednowątkowy — cały dostęp do LState chroniony jest mutexem.

Główna struktura to LuaEngine w pkg/lua/lua.go:

type LuaEngine struct {
    state         *lua.LState
    mu            sync.RWMutex
    timerManager  *TimerManager
    httpManager   *HTTPManager
    gpioManager   *GPIOManager
    kvManager     *KVManager
    customGlobals map[string]interface{}
    stopChan      chan struct{}
}

Inicjalizacja#

Tworzenie silnika:

engine := lua.NewLuaEngine()
// lub z niestandardową ścieżką runtime:
engine := lua.NewLuaEngineWithRuntimePath(cluName, runtimePath)

initializeLuaState() wykonuje następujące kroki:

  1. Tworzy nowy LState via lua.NewState()
  2. Blokuje niebezpieczne funkcjedofile() i loadfile() ustawiane na lua.LNil
  3. Rejestruje Go functions — bridże do timerów, HTTP, MQTT, KV, GPIO
  4. Ładuje bootstrapbootstrap_go.lua inicjalizuje cały runtime Lua

Bootstrap — 12 warstw#

Plik pkg/lua/runtime/bootstrap_go.lua ładuje runtime w określonej kolejności:

WarstwaCo ładujeOpis
1UtilsLogger, EventBus, StateBus, JSON, ModuleData, Module
2OM ModulesCLU, Timer, DIN, DOUT, ANALOG_IN/OUT, UNDEFINED
3ExposedObjectsAPI ekspozycji obiektów (MQTT, HomeKit)
4AccessControlUnified access control
5IntegrationsHTTP client, MQTT, Home Assistant
5bGPIOPolling wejść via setInterval
6RemoteSubscriptionProtokół zdalnej subskrypcji
7CoreLuaAPI, PluginSandbox, Plugin API
8FactoriesOBJECT, GATE
9Remote ObjectsHierarchia RemoteObject
10RegistryCentralny rejestr + RemoteGroup
11HelpersFunkcje pomocnicze
12SYSTEMSingleton SYSTEM, inicjalizacja modułów

Ładowanie danych CLU#

Po bootstrapie, LoadCLUData() ładuje konkretną konfigurację:

1. user.lua   → pcall(require "user")     — opcjonalny, błędy logowane
2. om.lua     → state.DoFile(omPath)       — wymagany
3. main.lua   → parsowanie checkAlive      — tylko serial, nie wykonywany
4. SYSTEM.Init()                           — uruchomienie EVENT_ON_INIT

Pętla główna — RunLoop#

RunLoop() działa na tickerze 10ms i nie blokuje:

func (le *LuaEngine) RunLoop() {
    ticker := time.NewTicker(10 * time.Millisecond)
    for {
        select {
        case <-le.stopChan:
            return
        case <-ticker.C:
            le.ProcessTimers()
            le.ProcessHTTPServer()
            le.PollGPIOInputs()
        }
    }
}

Każdy tick:

  • ProcessTimers — sprawdza kolejkę gotowych timerów, wykonuje callbacki Lua
  • ProcessHTTPServer — obsługuje przychodzące requesty do wbudowanego serwera
  • PollGPIOInputs — odczytuje piny wejściowe, wykrywa zmiany stanu

Go → Lua Bridge#

Silnik rejestruje zestaw funkcji Go dostępnych z poziomu Lua:

Funkcja GoCel
__go_setTimeout(cb, ms)Jednorazowy timer
__go_setInterval(cb, ms)Powtarzający się timer
__go_clearTimer(id)Anulowanie timera
__go_time_ms()Timestamp w ms
__go_http_request(...)Synchroniczny HTTP
__go_http_async_request(...)Asynchroniczny HTTP
__go_http_poll_async()Polling async HTTP
__go_kv_get/set/delete/has/list(...)KV store
__go_gpio_setup/write/read(...)Operacje GPIO
__go_send_command(ip, script)Zdalne wykonanie na innym CLU
__go_homekit_notify(path, val, type)Powiadomienie HomeKit

Te funkcje nie są wywoływane bezpośrednio — opakowane są przez moduły Lua (EventBus, HttpClient, itd.).

Reset i reinicjalizacja#

vCLU może zrestartować silnik Lua bez restartowania procesu Go:

  1. Zamyka stary LState
  2. Tworzy nowy (pełna reinicjalizacja)
  3. Przywraca customGlobals
  4. Ponownie rejestruje Go callbacki
  5. Ładuje dane CLU od nowa

Przydatne po zmianie konfiguracji w kreatorze lub po aktualizacji om.lua z OM.