oprava import/export kwh
Some checks failed
CI and deploy / migration-check (push) Failing after 11s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-05-01 14:58:29 +02:00
parent 91ee8a6adf
commit ed88ef8910
8 changed files with 158 additions and 40 deletions

View File

@@ -3,6 +3,74 @@
-- EMS Platform funkce pro výpočet efektivní ceny per site
-- Repeatable migration nasazuje se při každé změně
-- =============================================================
-- Pomocné (audit/ekonomika): sjednocení import/export Wh — musí běžet před R__012/R__056.
create or replace function ems.fn_audit_grid_import_wh_for_economics(
p_import_wh numeric,
p_export_wh numeric,
p_grid_power_w int
)
returns numeric
language sql
stable
as $$
select case
when coalesce(p_import_wh, 0) > 0 and coalesce(p_export_wh, 0) > 0 then
coalesce(
p_import_wh,
greatest(coalesce(p_grid_power_w, 0), 0)::numeric / 4
)
when coalesce(p_export_wh, 0) = 0 then
greatest(
coalesce(
p_import_wh,
greatest(coalesce(p_grid_power_w, 0), 0)::numeric / 4
),
greatest(coalesce(p_grid_power_w, 0), 0)::numeric / 4
)
else
coalesce(
p_import_wh,
greatest(coalesce(p_grid_power_w, 0), 0)::numeric / 4
)
end;
$$;
create or replace function ems.fn_audit_grid_export_wh_for_economics(
p_import_wh numeric,
p_export_wh numeric,
p_grid_power_w int
)
returns numeric
language sql
stable
as $$
select case
when coalesce(p_import_wh, 0) > 0 and coalesce(p_export_wh, 0) > 0 then
coalesce(
p_export_wh,
abs(least(coalesce(p_grid_power_w, 0), 0))::numeric / 4
)
when coalesce(p_import_wh, 0) = 0 then
greatest(
coalesce(
p_export_wh,
abs(least(coalesce(p_grid_power_w, 0), 0))::numeric / 4
),
abs(least(coalesce(p_grid_power_w, 0), 0))::numeric / 4
)
else
coalesce(
p_export_wh,
abs(least(coalesce(p_grid_power_w, 0), 0))::numeric / 4
)
end;
$$;
comment on function ems.fn_audit_grid_import_wh_for_economics(numeric, numeric, int) is
'Import Wh pro audit/ekonomiku: u čistého importu max(uložený čítač, max(0,P_grid)×¼ h).';
comment on function ems.fn_audit_grid_export_wh_for_economics(numeric, numeric, int) is
'Export Wh pro audit/ekonomiku: u čistého exportu max(uložený čítač, |min(0,P_grid)|×¼ h).';
CREATE OR REPLACE FUNCTION ems.fn_effective_buy_price(
p_site_id INT,

View File

@@ -14,13 +14,29 @@ as $fn$
end,
'grid_import_kwh',
case
when ai.actual_grid_import_wh is null then null
else round(ai.actual_grid_import_wh::numeric / 1000, 4)
when ai.actual_grid_import_wh is null
and ai.actual_grid_export_wh is null
and ai.actual_grid_power_w is null
then null
else round(
ems.fn_audit_grid_import_wh_for_economics(
ai.actual_grid_import_wh, ai.actual_grid_export_wh, ai.actual_grid_power_w
)::numeric / 1000,
4
)
end,
'grid_export_kwh',
case
when ai.actual_grid_export_wh is null then null
else round(ai.actual_grid_export_wh::numeric / 1000, 4)
when ai.actual_grid_import_wh is null
and ai.actual_grid_export_wh is null
and ai.actual_grid_power_w is null
then null
else round(
ems.fn_audit_grid_export_wh_for_economics(
ai.actual_grid_import_wh, ai.actual_grid_export_wh, ai.actual_grid_power_w
)::numeric / 1000,
4
)
end,
'batt_charge_kwh',
case

View File

@@ -45,6 +45,8 @@ DECLARE
v_counter_export_last BIGINT;
v_delta_import NUMERIC;
v_delta_export NUMERIC;
v_imp_before NUMERIC;
v_exp_before NUMERIC;
-- 7 směrových toků (prioritní alokace per minuta; součet W/60 = Wh)
r_flow RECORD;
@@ -141,6 +143,13 @@ BEGIN
v_grid_export_wh := v_delta_export;
END IF;
v_imp_before := v_grid_import_wh;
v_exp_before := v_grid_export_wh;
v_grid_import_wh := ems.fn_audit_grid_import_wh_for_economics(
v_imp_before, v_exp_before, v_avg_grid_power_w);
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
@@ -371,8 +380,9 @@ $$;
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.
Nově: per-minutový split pro 6 energetických veličin (import/export/batt/PV/load Wh);
grid import/export primárně z delta Deye total counterů (reg 522-525), fallback per-minute.
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).
7 směrových toků (flow_*_wh): prioritní alokace per minuta z telemetrie (PV→load→batt→export; baterie→load/export; síť→zbytek).
actual_cost_czk = per-direction (import_wh × buy - export_wh × sell).
Zelený bonus: součet přes pole s green_bonus_czk_kwh.

View File

@@ -21,8 +21,10 @@ as $fn$
'day', (date_trunc('day', ai.interval_start at time zone 'Europe/Prague'))::date,
'interval_count', count(*)::int,
'pv_production_kwh', round(sum(coalesce(ai.actual_pv_production_wh, 0)) / 1000, 3),
'grid_import_kwh', round(sum(coalesce(ai.actual_grid_import_wh, 0)) / 1000, 3),
'grid_export_kwh', round(sum(coalesce(ai.actual_grid_export_wh, 0)) / 1000, 3),
'grid_import_kwh', round(sum(ems.fn_audit_grid_import_wh_for_economics(
ai.actual_grid_import_wh, ai.actual_grid_export_wh, ai.actual_grid_power_w)) / 1000, 3),
'grid_export_kwh', round(sum(ems.fn_audit_grid_export_wh_for_economics(
ai.actual_grid_import_wh, ai.actual_grid_export_wh, ai.actual_grid_power_w)) / 1000, 3),
'batt_charge_kwh', round(sum(coalesce(ai.actual_batt_charge_wh, 0)) / 1000, 3),
'batt_discharge_kwh', round(sum(coalesce(ai.actual_batt_discharge_wh, 0)) / 1000, 3),
'load_kwh', round(sum(coalesce(ai.actual_load_consumption_wh, 0)) / 1000, 3),
@@ -36,7 +38,9 @@ as $fn$
'grid_import_cashflow_czk',
round(
sum(
coalesce(ai.actual_grid_import_wh, 0) / 1000.0
ems.fn_audit_grid_import_wh_for_economics(
ai.actual_grid_import_wh, ai.actual_grid_export_wh, ai.actual_grid_power_w
) / 1000.0
* coalesce(ep.effective_buy_price_czk_kwh, 0)
),
2
@@ -44,7 +48,9 @@ as $fn$
'grid_export_revenue_czk',
round(
sum(
coalesce(ai.actual_grid_export_wh, 0) / 1000.0
ems.fn_audit_grid_export_wh_for_economics(
ai.actual_grid_import_wh, ai.actual_grid_export_wh, ai.actual_grid_power_w
) / 1000.0
* coalesce(ep.effective_sell_price_czk_kwh, 0)
),
2

View File

@@ -59,7 +59,13 @@ as $fn$
coalesce(dt.vat_rate, 0.21) as vat_rate,
mip.buy_raw_price_czk_kwh as buy_spot,
mip.sell_raw_price_czk_kwh as sell_spot,
pi.expected_cost_czk as planned_cost_czk
pi.expected_cost_czk as planned_cost_czk,
ems.fn_audit_grid_import_wh_for_economics(
ai.actual_grid_import_wh, ai.actual_grid_export_wh, ai.actual_grid_power_w
) as est_grid_import_wh,
ems.fn_audit_grid_export_wh_for_economics(
ai.actual_grid_import_wh, ai.actual_grid_export_wh, ai.actual_grid_power_w
) as est_grid_export_wh
from ems.audit_interval ai
left join lateral (
select smc_inner.*
@@ -202,29 +208,19 @@ as $fn$
select
s5.site_id,
s5.day_local,
round(s5.est_grid_import_wh / 1000, 4) as import_kwh,
round(s5.est_grid_export_wh / 1000, 4) as export_kwh,
round(
coalesce(s5.actual_grid_import_wh, greatest(s5.actual_grid_power_w, 0)::numeric / 4) / 1000,
4
) as import_kwh,
round(
coalesce(s5.actual_grid_export_wh, abs(least(s5.actual_grid_power_w, 0))::numeric / 4) / 1000,
4
) as export_kwh,
round(
coalesce(s5.actual_grid_import_wh, greatest(s5.actual_grid_power_w, 0)::numeric / 4) / 1000.0
* coalesce(s5.buy_p, 0),
s5.est_grid_import_wh / 1000.0 * coalesce(s5.buy_p, 0),
4
) as grid_import_cashflow_czk,
round(
coalesce(s5.actual_grid_export_wh, abs(least(s5.actual_grid_power_w, 0))::numeric / 4) / 1000.0
* coalesce(s5.sell_p, 0),
s5.est_grid_export_wh / 1000.0 * coalesce(s5.sell_p, 0),
4
) as grid_export_revenue_czk,
round(
coalesce(s5.actual_grid_import_wh, greatest(s5.actual_grid_power_w, 0)::numeric / 4) / 1000.0
* coalesce(s5.buy_p, 0)
- coalesce(s5.actual_grid_export_wh, abs(least(s5.actual_grid_power_w, 0))::numeric / 4) / 1000.0
* coalesce(s5.sell_p, 0),
s5.est_grid_import_wh / 1000.0 * coalesce(s5.buy_p, 0)
- s5.est_grid_export_wh / 1000.0 * coalesce(s5.sell_p, 0),
4
) as dynamic_cost_czk,
s5.green_bonus_czk,