Files
ems/db/routines/R__015_fn_ev_session_patch.sql
Dusan Vojacek 3b5f07b66e feat(planner): EV účtování v2 — headroom fix, deadline boundary, min. výkon WB, via-bat reporting
Hloubková diagnóza EV potvrdila: oportunitní ekonomika via-baterie je v LP
správně, ale okraje lhaly nebo byly nevykonatelné:

- V099 + R__039: ems.ev_session.opportunistic_value_czk_kwh (NULL = zdědit
  z asset_vehicle, 0 = vypnout pro session); headroom_wh z max(target_soc,
  soc_at_connect) — „nenabíjet" (nízký target) už paradoxně NEzvětšuje
  oportunistickou vrstvu; vehicles JSON nese min_power_w wallboxu.
- R__015: patch klíč opportunistic_value_czk_kwh (validace >= 0).
- solver_v2: (a) deadline suma range(t_dl) — slot začínající v deadline už
  nepatří „do deadline"; (b) Σ ev_direct <= gi + PV (fyzikální split);
  (c) binárka ev_on → setpoint ∈ {0} ∪ [min_power_w, max] (konec 400–900 W
  nevykonatelných setpointů); (d) bez session EV == 0 (stop-session i golden
  fixtures — žádné pumpování při buy<0); dekompozice total == needed − unmet
  + opp i pro needed = 0; (e) battery_arbitrage_czk = via_bat kWh × oportunitní
  cena (min sell exportního slotu téhož pražského dne, jinak terminal value)
  místo konstantní 0. Oportunismus PO deadline zůstává POVOLENÝ (rozhodnutí:
  auto často doma, odjezd řeší rolling replan).
- R__033: fn_plan_current_bundle.intervals + ev1/ev2_via_bat_w (UI nemá cenit
  EV kWh z baterie slotovým buy).

Golden gate beze změny snapshotů (v1 nedotčen, fixtures bez EV sessions);
solver_v2_eval před/po identický (CELKEM −1283.5 Kč, Δ −221.9 vs v1);
tests/test_solver_v2.py +7 testů; plná sada 310 passed / 4 xfailed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 19:31:56 +02:00

81 lines
2.7 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
create or replace function ems.fn_ev_session_apply_patch(
p_site_id int,
p_session_id int,
p_patch jsonb
)
returns jsonb
language plpgsql
as $fn$
declare
v_id int;
begin
if not (p_patch ? 'target_soc_pct')
and not (p_patch ? 'target_deadline')
and not (p_patch ? 'soc_at_connect_pct')
and not (p_patch ? 'opportunistic_value_czk_kwh') then
return jsonb_build_object('success', false, 'error', 'no_fields');
end if;
if (p_patch ? 'opportunistic_value_czk_kwh')
and jsonb_typeof(p_patch->'opportunistic_value_czk_kwh') <> 'null'
and (p_patch->>'opportunistic_value_czk_kwh')::numeric < 0 then
return jsonb_build_object(
'success', false, 'error', 'opportunistic_value_negative'
);
end if;
update ems.ev_session es
set
target_soc_pct = case
when p_patch ? 'target_soc_pct' then
case
when p_patch->'target_soc_pct' is null
or jsonb_typeof(p_patch->'target_soc_pct') = 'null' then null
else (p_patch->>'target_soc_pct')::double precision
end
else es.target_soc_pct
end,
-- skutečné SoC při připojení (Tesla Fleet API po příjezdu)
soc_at_connect_pct = case
when p_patch ? 'soc_at_connect_pct' then
case
when p_patch->'soc_at_connect_pct' is null
or jsonb_typeof(p_patch->'soc_at_connect_pct') = 'null' then null
else (p_patch->>'soc_at_connect_pct')::double precision
end
else es.soc_at_connect_pct
end,
target_deadline = case
when p_patch ? 'target_deadline' then
case
when p_patch->'target_deadline' is null
or jsonb_typeof(p_patch->'target_deadline') = 'null' then null
else (p_patch->>'target_deadline')::timestamptz
end
else es.target_deadline
end,
-- NULL = zdědit z asset_vehicle; 0 = oportunismus pro session vypnut
opportunistic_value_czk_kwh = case
when p_patch ? 'opportunistic_value_czk_kwh' then
case
when p_patch->'opportunistic_value_czk_kwh' is null
or jsonb_typeof(p_patch->'opportunistic_value_czk_kwh') = 'null' then null
else (p_patch->>'opportunistic_value_czk_kwh')::numeric
end
else es.opportunistic_value_czk_kwh
end
where es.id = p_session_id
and es.site_id = p_site_id
returning es.id into v_id;
if v_id is null then
return jsonb_build_object('success', false, 'session_id', null);
end if;
return jsonb_build_object('success', true, 'session_id', v_id);
end;
$fn$;
comment on function ems.fn_ev_session_apply_patch is
'PATCH EV session jen klíče přítomné v JSON (ISO string pro deadline; soc_at_connect_pct z Tesla API; opportunistic_value_czk_kwh >= 0, NULL = zdědit z vozidla, 0 = vypnout).';