Files
ems/db/routines/R__fn_set_mode.sql
Dusan Vojacek abd6f484c6
All checks were successful
test / smoke-test (push) Successful in 5s
deploy / deploy (push) Successful in 18s
fix recreate function fn_expire_modes
2026-04-06 21:03:17 +02:00

169 lines
5.6 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- =============================================================
-- R__fn_set_mode.sql
-- EMS Platform přepínání provozních režimů
-- Repeatable migration
-- =============================================================
CREATE OR REPLACE FUNCTION ems.fn_set_mode(
p_site_id INT,
p_mode_code TEXT,
p_activated_by TEXT DEFAULT 'system',
p_valid_until TIMESTAMPTZ DEFAULT NULL,
p_notes TEXT DEFAULT NULL
)
RETURNS TEXT
LANGUAGE plpgsql
AS $$
DECLARE
v_current_mode TEXT;
v_mode_exists BOOLEAN;
BEGIN
-- Ověřit že režim existuje
SELECT EXISTS(SELECT 1 FROM ems.operating_mode_def WHERE code = p_mode_code)
INTO v_mode_exists;
IF NOT v_mode_exists THEN
RAISE EXCEPTION 'Neznámý provozní režim: %', p_mode_code;
END IF;
-- Zjistit aktuální režim (pro log a previous_mode)
SELECT mode_code INTO v_current_mode
FROM ems.site_operating_mode
WHERE site_id = p_site_id;
-- Pokud se režim nemění, nic nedělat
IF v_current_mode = p_mode_code THEN
RETURN p_mode_code;
END IF;
-- Uzavřít předchozí záznam v logu
UPDATE ems.site_operating_mode_log
SET deactivated_at = now()
WHERE site_id = p_site_id
AND deactivated_at IS NULL;
-- Upsert aktivního režimu
INSERT INTO ems.site_operating_mode
(site_id, mode_code, activated_at, activated_by, valid_until, previous_mode, notes)
VALUES
(p_site_id, p_mode_code, now(), p_activated_by, p_valid_until, v_current_mode, p_notes)
ON CONFLICT (site_id) DO UPDATE SET
mode_code = EXCLUDED.mode_code,
activated_at = EXCLUDED.activated_at,
activated_by = EXCLUDED.activated_by,
valid_until = EXCLUDED.valid_until,
previous_mode = EXCLUDED.previous_mode,
notes = EXCLUDED.notes;
-- Přidat záznam do logu
INSERT INTO ems.site_operating_mode_log
(site_id, mode_code, activated_at, activated_by, notes)
VALUES
(p_site_id, p_mode_code, now(), p_activated_by, p_notes);
RETURN p_mode_code;
END;
$$;
COMMENT ON FUNCTION ems.fn_set_mode(INT, TEXT, TEXT, TIMESTAMPTZ, TEXT) IS
'Přepne provozní režim lokality. Atomicky aktualizuje site_operating_mode a zapíše do audit logu.
Ignoruje přepnutí na stejný režim. Vyhodí výjimku pro neznámý kód režimu.
Příklad: SELECT ems.fn_set_mode(1, ''SELF_SUSTAIN'', ''user:jan'', NULL, ''Odjezd na dovolenou'');';
-- ============================================================
CREATE OR REPLACE FUNCTION ems.fn_restore_previous_mode(
p_site_id INT,
p_activated_by TEXT DEFAULT 'system'
)
RETURNS TEXT
LANGUAGE plpgsql
AS $$
DECLARE
v_previous TEXT;
BEGIN
SELECT previous_mode INTO v_previous
FROM ems.site_operating_mode
WHERE site_id = p_site_id;
IF v_previous IS NULL THEN
-- Fallback na AUTO pokud není předchozí režim
v_previous := 'AUTO';
END IF;
RETURN ems.fn_set_mode(p_site_id, v_previous, p_activated_by, NULL, 'Obnova předchozího režimu');
END;
$$;
COMMENT ON FUNCTION ems.fn_restore_previous_mode(INT, TEXT) IS
'Přepne lokalitu zpět na předchozí provozní režim (uložený v previous_mode).
Pokud předchozí režim neexistuje, přepne na AUTO. Používat po skončení dočasného přepisu.';
-- ============================================================
drop function if exists ems.fn_expire_modes();
CREATE OR REPLACE FUNCTION ems.fn_expire_modes()
RETURNS TABLE(site_id INT, site_code TEXT, old_mode TEXT, new_mode TEXT)
LANGUAGE plpgsql
AS $$
DECLARE
v_rec RECORD;
v_new_mode TEXT;
BEGIN
FOR v_rec IN
SELECT som.site_id,
s.code AS site_code,
som.mode_code AS old_mode,
som.previous_mode
FROM ems.site_operating_mode som
JOIN ems.site s ON s.id = som.site_id
WHERE som.valid_until IS NOT NULL
AND som.valid_until <= now()
AND som.mode_code <> 'AUTO'
LOOP
v_new_mode := COALESCE(v_rec.previous_mode, 'AUTO');
PERFORM ems.fn_set_mode(
v_rec.site_id,
v_new_mode,
'system:expiry',
NULL,
'Automatické vypršení dočasného režimu'
);
site_id := v_rec.site_id;
site_code := v_rec.site_code;
old_mode := v_rec.old_mode;
new_mode := v_new_mode;
RETURN NEXT;
END LOOP;
END;
$$;
COMMENT ON FUNCTION ems.fn_expire_modes() IS
'Zkontroluje všechny lokality s dočasným režimem (valid_until IS NOT NULL) a přepne zpět ty s prosahlým časem.
Volat každou minutu jako scheduled task. Vrátí řádky (site_id, site_code, old_mode, new_mode) pro každé provedené přepnutí — backend z toho pošle Discord notifikace.';
-- ============================================================
CREATE OR REPLACE FUNCTION ems.fn_update_heartbeat(
p_site_id INT,
p_status TEXT DEFAULT 'ok',
p_ems_version TEXT DEFAULT NULL
)
RETURNS VOID
LANGUAGE sql
AS $$
INSERT INTO ems.site_heartbeat (site_id, last_seen, status, ems_version)
VALUES (p_site_id, now(), p_status, p_ems_version)
ON CONFLICT (site_id) DO UPDATE SET
last_seen = now(),
status = EXCLUDED.status,
ems_version = COALESCE(EXCLUDED.ems_version, ems.site_heartbeat.ems_version);
$$;
COMMENT ON FUNCTION ems.fn_update_heartbeat(INT, TEXT, TEXT) IS
'Aktualizuje informační heartbeat záznam EMS pro danou lokalitu.
Volat každou minutu z backend service po úspěšném odeslání pulzu do Loxone.
Slouží pouze pro EMS dashboard Loxone watchdog nezávisí na této tabulce,
sleduje HTTP pulzy přímo a nezávisle na dostupnosti DB.';