sjednoceni forecastu
Some checks failed
CI and deploy / migration-check (push) Failing after 13s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-05-05 10:42:49 +02:00
parent 459f33d55c
commit 5b383e9028
9 changed files with 461 additions and 253 deletions

View File

@@ -65,27 +65,6 @@ declare
begin
drop table if exists _ems_plan_slot_wk;
create temp table _ems_plan_slot_wk on commit drop as
with prof as (
select ems.fn_pv_forecast_delta_profile(
p_site_id,
greatest(p_from, now() - interval '120 days'),
now()
) as j
),
delta_unnest as (
select (kv.key)::int as pv_array_id,
(x->>'slot_of_day')::int as slot_of_day,
(x->>'delta_w')::int as delta_w
from prof
cross join lateral jsonb_each((prof.j)->'deltas_by_array') kv(key, value)
cross join lateral jsonb_array_elements(kv.value->'deltas') x
),
legacy_slot_delta as (
select (x->>'slot_of_day')::int as slot_of_day,
(x->>'delta_w')::int as delta_w
from prof
cross join lateral jsonb_array_elements((prof.j)->'deltas') x
),
slot_spine as (
select gs as interval_start
from generate_series(
@@ -93,6 +72,26 @@ begin
(p_to - interval '15 minutes')::timestamptz,
interval '15 minutes'
) as gs
),
pv_canon as (
select
r.interval_start,
coalesce(r.pv_a_forecast_canonical_w, 0)::int as pv_a_forecast_w,
coalesce(r.pv_b_forecast_canonical_w, 0)::int as pv_b_forecast_w
from jsonb_to_recordset(
ems.fn_forecast_pv_slots_range_canonical_ab(
p_site_id,
p_from,
p_to,
now(),
greatest(p_from, now() - interval '120 days'),
now()
)
) as r(
interval_start timestamptz,
pv_a_forecast_canonical_w bigint,
pv_b_forecast_canonical_w bigint
)
)
select
(row_number() over (order by s.interval_start) - 1)::int as slot_ord,
@@ -106,8 +105,8 @@ begin
ems.fn_get_predicted_price(p_site_id, s.interval_start) * 0.85
) as sell_price,
(ep.effective_buy_price_czk_kwh is null) as is_predicted_price,
coalesce(fpi_a.power_w, 0)::int as pv_a_forecast_w,
coalesce(fpi_b.power_w, 0)::int as pv_b_forecast_w,
coalesce(pv.pv_a_forecast_w, 0)::int as pv_a_forecast_w,
coalesce(pv.pv_b_forecast_w, 0)::int as pv_b_forecast_w,
coalesce(
(
select bs.avg_power_w
@@ -127,7 +126,7 @@ begin
(coalesce(ev2.status, 'available') not in ('available', 'unavailable')) as ev2_connected,
greatest(
0,
coalesce(fpi_a.power_w, 0) + coalesce(fpi_b.power_w, 0)
coalesce(pv.pv_a_forecast_w, 0) + coalesce(pv.pv_b_forecast_w, 0)
- coalesce(
(
select bs.avg_power_w
@@ -147,106 +146,9 @@ begin
false::boolean as allow_charge,
false::boolean as allow_discharge_export
from slot_spine s
left join pv_canon pv on pv.interval_start = s.interval_start
left join ems.vw_site_effective_price ep
on ep.site_id = p_site_id and ep.interval_start = s.interval_start
left join lateral (
with uq as (
select distinct on (apa.id)
apa.id as pv_array_id,
fpi.power_w
from ems.asset_pv_array apa
join ems.forecast_pv_run fpr
on fpr.pv_array_id = apa.id
and fpr.site_id = apa.site_id
and fpr.status = 'ok'
join ems.forecast_pv_interval fpi
on fpi.run_id = fpr.id
and fpi.pv_array_id = apa.id
and fpi.interval_start = s.interval_start
where apa.site_id = p_site_id
and apa.controllable is true
order by apa.id, fpr.created_at desc
),
slot_of as (
select (
(extract(hour from (s.interval_start at time zone 'Europe/Prague'))::int * 60)
+ extract(minute from (s.interval_start at time zone 'Europe/Prague'))::int
) / 15 as slot_of_day
),
tot as (select coalesce(sum(uq.power_w), 0)::numeric as w from uq)
select coalesce(sum(
greatest(
0,
uq.power_w - coalesce(
du.delta_w,
case
when exists (select 1 from delta_unnest limit 1) then null
else round(
ld.delta_w::numeric * uq.power_w::numeric / nullif((select w from tot), 0)
)::int
end,
0
)
)
), 0)::int as power_w
from uq
cross join slot_of
cross join tot
left join delta_unnest du
on du.pv_array_id = uq.pv_array_id
and du.slot_of_day = slot_of.slot_of_day
left join legacy_slot_delta ld
on ld.slot_of_day = slot_of.slot_of_day
) fpi_a on true
left join lateral (
with uq as (
select distinct on (apa.id)
apa.id as pv_array_id,
fpi.power_w
from ems.asset_pv_array apa
join ems.forecast_pv_run fpr
on fpr.pv_array_id = apa.id
and fpr.site_id = apa.site_id
and fpr.status = 'ok'
join ems.forecast_pv_interval fpi
on fpi.run_id = fpr.id
and fpi.pv_array_id = apa.id
and fpi.interval_start = s.interval_start
where apa.site_id = p_site_id
and apa.controllable is false
order by apa.id, fpr.created_at desc
),
slot_of as (
select (
(extract(hour from (s.interval_start at time zone 'Europe/Prague'))::int * 60)
+ extract(minute from (s.interval_start at time zone 'Europe/Prague'))::int
) / 15 as slot_of_day
),
tot as (select coalesce(sum(uq.power_w), 0)::numeric as w from uq)
select coalesce(sum(
greatest(
0,
uq.power_w - coalesce(
du.delta_w,
case
when exists (select 1 from delta_unnest limit 1) then null
else round(
ld.delta_w::numeric * uq.power_w::numeric / nullif((select w from tot), 0)
)::int
end,
0
)
)
), 0)::int as power_w
from uq
cross join slot_of
cross join tot
left join delta_unnest du
on du.pv_array_id = uq.pv_array_id
and du.slot_of_day = slot_of.slot_of_day
left join legacy_slot_delta ld
on ld.slot_of_day = slot_of.slot_of_day
) fpi_b on true
left join lateral (
select t.status
from ems.telemetry_ev_charger t