diff --git a/db/routines/R__018_fn_extended_planning.sql b/db/routines/R__018_fn_extended_planning.sql index 75d06f2..8a0fc1f 100644 --- a/db/routines/R__018_fn_extended_planning.sql +++ b/db/routines/R__018_fn_extended_planning.sql @@ -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ě.'; diff --git a/db/routines/R__019_fn_fill_audit_interval.sql b/db/routines/R__019_fn_fill_audit_interval.sql index 63a50e5..7527e94 100644 --- a/db/routines/R__019_fn_fill_audit_interval.sql +++ b/db/routines/R__019_fn_fill_audit_interval.sql @@ -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). diff --git a/db/views/R__097_vw_pool_pump.sql b/db/views/R__097_vw_pool_pump.sql index 359e456..10e86c4 100644 --- a/db/views/R__097_vw_pool_pump.sql +++ b/db/views/R__097_vw_pool_pump.sql @@ -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;