Initial commit
Made-with: Cursor
This commit is contained in:
171
db/routines/R__fn_fill_audit_interval.sql
Normal file
171
db/routines/R__fn_fill_audit_interval.sql
Normal file
@@ -0,0 +1,171 @@
|
||||
-- =============================================================
|
||||
-- R__fn_fill_audit_interval.sql
|
||||
-- EMS Platform – plnění audit_interval ze skutečné telemetrie
|
||||
-- Repeatable migration
|
||||
-- =============================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION ems.fn_fill_audit_interval(
|
||||
p_site_id INT,
|
||||
p_interval_start TIMESTAMPTZ
|
||||
)
|
||||
RETURNS VOID
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_interval_end TIMESTAMPTZ := p_interval_start + INTERVAL '15 minutes';
|
||||
v_run_id INT;
|
||||
v_avg_pv_power_w INT;
|
||||
v_avg_battery_power_w INT;
|
||||
v_avg_grid_power_w INT;
|
||||
v_avg_load_power_w INT;
|
||||
v_last_soc NUMERIC(5,2);
|
||||
v_sum_ev_power_w INT;
|
||||
v_avg_hp_power_w INT;
|
||||
v_plan ems.planning_interval%ROWTYPE;
|
||||
v_buy_price NUMERIC;
|
||||
v_sell_price NUMERIC;
|
||||
v_actual_cost NUMERIC := NULL;
|
||||
BEGIN
|
||||
-- Najít aktivní plán pro tento interval
|
||||
SELECT pi.* INTO v_plan
|
||||
FROM ems.planning_interval pi
|
||||
JOIN ems.planning_run pr ON pr.id = pi.run_id
|
||||
WHERE pr.site_id = p_site_id
|
||||
AND pi.interval_start = p_interval_start
|
||||
AND pr.status IN ('active', 'superseded')
|
||||
ORDER BY pr.created_at DESC
|
||||
LIMIT 1;
|
||||
|
||||
v_run_id := v_plan.run_id;
|
||||
|
||||
-- Agregovat telemetrii střídače (průměr za 15min; agregace bez GROUP BY vrací vždy 1 řádek)
|
||||
SELECT
|
||||
AVG(pv_power_w)::INT,
|
||||
AVG(battery_power_w)::INT,
|
||||
AVG(grid_power_w)::INT,
|
||||
AVG(load_power_w)::INT,
|
||||
LAST(battery_soc_percent, measured_at)
|
||||
INTO
|
||||
v_avg_pv_power_w,
|
||||
v_avg_battery_power_w,
|
||||
v_avg_grid_power_w,
|
||||
v_avg_load_power_w,
|
||||
v_last_soc
|
||||
FROM ems.telemetry_inverter
|
||||
WHERE site_id = p_site_id
|
||||
AND measured_at >= p_interval_start
|
||||
AND measured_at < v_interval_end;
|
||||
|
||||
-- Agregovat EV nabíječky (součet průměrů po charger_id)
|
||||
SELECT COALESCE(SUM(avg_power), 0)::INT
|
||||
INTO v_sum_ev_power_w
|
||||
FROM (
|
||||
SELECT AVG(power_w) AS avg_power
|
||||
FROM ems.telemetry_ev_charger
|
||||
WHERE site_id = p_site_id
|
||||
AND measured_at >= p_interval_start
|
||||
AND measured_at < v_interval_end
|
||||
GROUP BY charger_id
|
||||
) sub;
|
||||
|
||||
-- Agregovat tepelné čerpadlo
|
||||
SELECT AVG(power_w)::INT
|
||||
INTO v_avg_hp_power_w
|
||||
FROM ems.telemetry_heat_pump
|
||||
WHERE site_id = p_site_id
|
||||
AND measured_at >= p_interval_start
|
||||
AND measured_at < v_interval_end;
|
||||
|
||||
-- Efektivní cena pro výpočet skutečných nákladů
|
||||
v_buy_price := ems.fn_effective_buy_price(p_site_id, p_interval_start);
|
||||
v_sell_price := ems.fn_effective_sell_price(p_site_id, p_interval_start);
|
||||
|
||||
-- Skutečné náklady (kladný grid = nákup, záporný = prodej)
|
||||
IF v_avg_grid_power_w IS NOT NULL THEN
|
||||
v_actual_cost := (v_avg_grid_power_w::NUMERIC / 1000.0 / 4.0)
|
||||
* CASE WHEN v_avg_grid_power_w >= 0
|
||||
THEN COALESCE(v_buy_price, 0)
|
||||
ELSE COALESCE(v_sell_price, 0) END;
|
||||
END IF;
|
||||
|
||||
-- Upsert do audit_interval
|
||||
INSERT INTO ems.audit_interval (
|
||||
site_id, interval_start, planning_run_id,
|
||||
actual_pv_power_w, actual_battery_power_w,
|
||||
actual_grid_power_w, actual_load_power_w,
|
||||
actual_battery_soc_pct,
|
||||
actual_ev_power_w,
|
||||
actual_heat_pump_power_w,
|
||||
actual_cost_czk,
|
||||
deviation_grid_w,
|
||||
deviation_cost_czk
|
||||
) VALUES (
|
||||
p_site_id, p_interval_start, v_run_id,
|
||||
v_avg_pv_power_w,
|
||||
v_avg_battery_power_w,
|
||||
v_avg_grid_power_w,
|
||||
v_avg_load_power_w,
|
||||
v_last_soc,
|
||||
v_sum_ev_power_w,
|
||||
v_avg_hp_power_w,
|
||||
ROUND(v_actual_cost, 4),
|
||||
CASE WHEN v_plan.run_id IS NOT NULL
|
||||
THEN v_avg_grid_power_w - v_plan.grid_setpoint_w
|
||||
ELSE NULL END,
|
||||
CASE WHEN v_plan.run_id IS NOT NULL
|
||||
THEN ROUND(v_actual_cost - COALESCE(v_plan.expected_cost_czk, 0), 4)
|
||||
ELSE NULL END
|
||||
)
|
||||
ON CONFLICT (site_id, interval_start) DO UPDATE SET
|
||||
planning_run_id = EXCLUDED.planning_run_id,
|
||||
actual_pv_power_w = EXCLUDED.actual_pv_power_w,
|
||||
actual_battery_power_w = EXCLUDED.actual_battery_power_w,
|
||||
actual_grid_power_w = EXCLUDED.actual_grid_power_w,
|
||||
actual_load_power_w = EXCLUDED.actual_load_power_w,
|
||||
actual_battery_soc_pct = EXCLUDED.actual_battery_soc_pct,
|
||||
actual_ev_power_w = EXCLUDED.actual_ev_power_w,
|
||||
actual_heat_pump_power_w = EXCLUDED.actual_heat_pump_power_w,
|
||||
actual_cost_czk = EXCLUDED.actual_cost_czk,
|
||||
deviation_grid_w = EXCLUDED.deviation_grid_w,
|
||||
deviation_cost_czk = EXCLUDED.deviation_cost_czk;
|
||||
END;
|
||||
$$;
|
||||
|
||||
COMMENT ON FUNCTION ems.fn_fill_audit_interval(INT, TIMESTAMPTZ) IS
|
||||
'Naplní nebo aktualizuje jeden řádek v audit_interval pro danou lokalitu a 15min interval.
|
||||
Agreguje průměry z telemetrie (střídač, EV, TČ), porovná se skutečným plánem a spočítá odchylky.
|
||||
Volat každých 15 minut pro interval který právě skončil.';
|
||||
|
||||
-- ============================================================
|
||||
-- Hromadné plnění auditu za historické období
|
||||
-- ============================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION ems.fn_fill_audit_range(
|
||||
p_site_id INT,
|
||||
p_from TIMESTAMPTZ,
|
||||
p_to TIMESTAMPTZ
|
||||
)
|
||||
RETURNS INT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_slot TIMESTAMPTZ;
|
||||
v_count INT := 0;
|
||||
BEGIN
|
||||
v_slot := date_trunc('hour', p_from)
|
||||
+ INTERVAL '15 min' * FLOOR(EXTRACT(MINUTE FROM p_from) / 15);
|
||||
|
||||
WHILE v_slot < p_to LOOP
|
||||
PERFORM ems.fn_fill_audit_interval(p_site_id, v_slot);
|
||||
v_slot := v_slot + INTERVAL '15 minutes';
|
||||
v_count := v_count + 1;
|
||||
END LOOP;
|
||||
|
||||
RETURN v_count;
|
||||
END;
|
||||
$$;
|
||||
|
||||
COMMENT ON FUNCTION ems.fn_fill_audit_range(INT, TIMESTAMPTZ, TIMESTAMPTZ) IS
|
||||
'Hromadně naplní audit_interval pro celé historické období.
|
||||
Volá fn_fill_audit_interval pro každý 15min slot v rozsahu p_from–p_to.
|
||||
Vrátí počet zpracovaných intervalů. Použít pro backfill po výpadku nebo prvním nasazení.';
|
||||
Reference in New Issue
Block a user