fix(db): čtecí cesty telemetrie robustní vůči řídkým řádkům (idle-skip)
- fn_fill_audit_interval: EV a TČ agregace sum(power_w)/15 místo avg přes přítomné řádky — avg by při řídké telemetrii nadhodnotil aktivitu části slotu; chybějící minuta = 0 W (idle). TČ drží NULL bez power_w (MIM-B19N). - fn_update_tuv_usage_stats: delta TUV normalizovaná na °C/min délkou mezery mezi řádky (gap_min), mezery > 30 min vyloučeny; pro hustá 1min data numericky identické s původním LAG. - vw_pool_pump_day_energy: komentář — on_minutes drží invariant „zapnuté čerpadlo se ukládá každou minutu". Pro hustá 1min data beze změny výsledků; připravuje idle-skip zápisů v telemetry_collector (navazující commit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -66,34 +66,40 @@ LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE v_count INT;
|
||||
BEGIN
|
||||
INSERT INTO ems.tuv_usage_stats
|
||||
-- Telemetrie TČ je idle-skip (telemetry_collector): rozestup řádků je
|
||||
-- 1–14 min. Delta se proto normalizuje na °C/min (gap_min); hustá 1min
|
||||
-- data dávají identické hodnoty jako dřív. Mezery > 30 min (výpadek
|
||||
-- sběru) se z učení vylučují.
|
||||
insert into ems.tuv_usage_stats
|
||||
(site_id, day_of_week, hour_of_day,
|
||||
avg_temp_delta_c, stddev_temp_delta,
|
||||
sample_count, last_updated)
|
||||
WITH deltas AS (
|
||||
SELECT
|
||||
with deltas as (
|
||||
select
|
||||
measured_at,
|
||||
EXTRACT(DOW FROM measured_at AT TIME ZONE 'Europe/Prague')::INT AS dow,
|
||||
EXTRACT(HOUR FROM measured_at AT TIME ZONE 'Europe/Prague')::INT AS hour,
|
||||
tuv_tank_temp_c - LAG(tuv_tank_temp_c) OVER (
|
||||
PARTITION BY site_id ORDER BY measured_at
|
||||
) AS temp_delta_c
|
||||
FROM ems.telemetry_heat_pump
|
||||
WHERE site_id = p_site_id
|
||||
AND measured_at >= now() - make_interval(days => p_lookback_days)
|
||||
AND tuv_tank_temp_c IS NOT NULL
|
||||
extract(dow from measured_at at time zone 'Europe/Prague')::int as dow,
|
||||
extract(hour from measured_at at time zone 'Europe/Prague')::int as hour,
|
||||
tuv_tank_temp_c - lag(tuv_tank_temp_c) over w as temp_delta_c,
|
||||
extract(epoch from measured_at - lag(measured_at) over w) / 60.0 as gap_min
|
||||
from ems.telemetry_heat_pump
|
||||
where site_id = p_site_id
|
||||
and measured_at >= now() - make_interval(days => p_lookback_days)
|
||||
and tuv_tank_temp_c is not null
|
||||
window w as (partition by site_id order by measured_at)
|
||||
)
|
||||
SELECT
|
||||
select
|
||||
p_site_id, dow, hour,
|
||||
AVG(temp_delta_c),
|
||||
STDDEV(temp_delta_c),
|
||||
COUNT(*)::INT,
|
||||
avg(temp_delta_c / gap_min),
|
||||
stddev(temp_delta_c / gap_min),
|
||||
count(*)::int,
|
||||
now()
|
||||
FROM deltas
|
||||
WHERE temp_delta_c IS NOT NULL
|
||||
AND ABS(temp_delta_c) < 5
|
||||
GROUP BY dow, hour
|
||||
HAVING COUNT(*) >= 4
|
||||
from deltas
|
||||
where temp_delta_c is not null
|
||||
and gap_min > 0
|
||||
and gap_min <= 30
|
||||
and abs(temp_delta_c) < 5
|
||||
group by dow, hour
|
||||
having count(*) >= 4
|
||||
ON CONFLICT (site_id, day_of_week, hour_of_day) DO UPDATE SET
|
||||
avg_temp_delta_c = 0.7 * ems.tuv_usage_stats.avg_temp_delta_c
|
||||
+ 0.3 * EXCLUDED.avg_temp_delta_c,
|
||||
@@ -112,6 +118,8 @@ $$;
|
||||
|
||||
COMMENT ON FUNCTION ems.fn_update_tuv_usage_stats IS
|
||||
'Aktualizuje statistiku poklesu teploty TUV zásobníku per DOW+hodina.
|
||||
avg_temp_delta_c je v °C/min (delta normalizovaná délkou mezery mezi řádky —
|
||||
robustní vůči idle-skip telemetrii; mezery > 30 min vyloučeny).
|
||||
Záporné avg_temp_delta_c = zásobník se ochlazuje (spotřeba teplé vody).
|
||||
Potřeba min. 1 měsíc telemetrie TČ. Volat denně.';
|
||||
|
||||
|
||||
@@ -150,25 +150,28 @@ BEGIN
|
||||
v_grid_export_wh := ems.fn_audit_grid_export_wh_for_economics(
|
||||
v_imp_before, v_exp_before, v_avg_grid_power_w);
|
||||
|
||||
-- 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 EV nabíječky: sum(W·min) / 15 = průměrný výkon slotu.
|
||||
-- Telemetrie je idle-skip (telemetry_collector): vypnutá nabíječka má jen
|
||||
-- heartbeat řádky — avg přes přítomné řádky by nabíjení části slotu
|
||||
-- NADHODNOTILO; chybějící minuta = 0 W (zařízení idle).
|
||||
select round(coalesce(sum(power_w), 0) / 15.0)::int
|
||||
into v_sum_ev_power_w
|
||||
from ems.telemetry_ev_charger
|
||||
where site_id = p_site_id
|
||||
and measured_at >= p_interval_start
|
||||
and measured_at < v_interval_end;
|
||||
|
||||
-- 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;
|
||||
-- Agregovat tepelné čerpadlo: stejný sumový přístup (idle-skip řádky);
|
||||
-- NULL pokud žádný řádek nenese power_w (MIM-B19N příkon neměří).
|
||||
select case
|
||||
when count(power_w) = 0 then null
|
||||
else round(sum(power_w) / 15.0)::int
|
||||
end
|
||||
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);
|
||||
@@ -377,9 +380,10 @@ BEGIN
|
||||
END;
|
||||
$$;
|
||||
|
||||
COMMENT ON FUNCTION ems.fn_fill_audit_interval(INT, TIMESTAMPTZ) IS
|
||||
COMMENT ON FUNCTION ems.fn_fill_audit_interval 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.
|
||||
Agreguje telemetrii (střídač průměry; EV a TČ sum/15 — idle-skip zápisy, chybějící minuta = 0 W),
|
||||
porovná se skutečným plánem a spočítá odchylky.
|
||||
Per-minutový split pro 6 energetických veličin (import/export/batt/PV/load Wh);
|
||||
grid import/export nejprve z delta Deye total counterů (reg 522-525), fallback per-minute; poté sjednocení
|
||||
fn_audit_grid_*_wh_for_economics (u jednosměrného toku max s odhadem z průměrného grid_power_w).
|
||||
|
||||
@@ -41,7 +41,9 @@ where tp.measured_at >= now() - interval '8 days'
|
||||
group by 1, 2, 3;
|
||||
|
||||
comment on view ems.vw_pool_pump_day_energy is
|
||||
'Denní kWh čerpadla (delta čítače energy_wh_total) a minuty běhu, 8 dní zpět.';
|
||||
'Denní kWh čerpadla (delta čítače energy_wh_total) a minuty běhu, 8 dní zpět.
|
||||
on_minutes = počet ON řádků: drží invariant idle-skip telemetrie (zapnuté
|
||||
čerpadlo se ukládá každou minutu, vypnuté jen změna/heartbeat).';
|
||||
|
||||
grant select on ems.vw_latest_pool_pump to ems_anon;
|
||||
grant select on ems.vw_pool_pump_day_energy to ems_anon;
|
||||
|
||||
Reference in New Issue
Block a user