EV spotřební forecast: týdenní rytmus vozidla → target SoC a deadline session
All checks were successful
CI and deploy / migration-check (push) Successful in 19s
CI and deploy / deploy (push) Successful in 56s

Myšlenka uživatele: pondělní služebka ~150 km (~35 kWh) chce skoro plnou,
konec týdne stačí míň, víkend = levné sloty na přípravu pondělka.

- V089: ev_vehicle_obs (odometer+SoC při příjezdu/ODJEZDU — auto v obou
  okamžicích vzhůru, žádné buzení navíc), ev_trip (km z odometru, kWh z ΔSoC;
  nabíjení cestou → charged_away flag), ev_usage_stats per (vozidlo, DOW);
  asset_vehicle: target_soc_forecast_enabled (default false), min_target_soc_pct
- R__096: fn_ev_build_trips (párování), fn_update_ev_usage_stats (job 00:50),
  fn_ev_next_departure (příští typický odjezd, >=4 vzorky, >=3 km),
  fn_ev_required_soc (P80 spotřeby dne + 10 p.b., clamp [min_target, 100])
- R__016: session při příjezdu bere forecast target+deadline (za per-vozidlo
  flagem, fallback defaulty, ruční patch vždy vyhrává) → víkendová session
  s pondělním deadline = v2 solver přirozeně nabije v levných slotech
- tesla_client: + vehicle_state endpoint (odometer v MÍLÍCH → km), collector:
  departure hook, lifespan: job 00:50

Aktivace po nasbírání dat: update asset_vehicle set target_soc_forecast_enabled=true.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dusan Vojacek
2026-06-12 09:06:10 +02:00
parent 002566ae5f
commit 4095f0f912
7 changed files with 386 additions and 11 deletions

View File

@@ -0,0 +1,63 @@
-- EV spotřební forecast: pozorování (odometer+SoC při příjezdu/odjezdu, auto je
-- vzhůru — žádné buzení navíc), jízdy, statistiky per den v týdnu. Cíl: target
-- SoC a deadline session z reálného týdenního rytmu (pondělí služebka ~150 km
-- → skoro plná; konec týdne míň; víkend = levné sloty na přípravu pondělka).
-- Zapnutí per vozidlo: target_soc_forecast_enabled (default false = sbírá se,
-- session jedou na defaultech).
alter table ems.asset_vehicle
add column if not exists target_soc_forecast_enabled boolean not null default false,
add column if not exists min_target_soc_pct numeric(5,2) not null default 30.0;
comment on column ems.asset_vehicle.target_soc_forecast_enabled is
'true = target SoC + deadline session z ev_usage_stats (fn_ev_required_soc); false = default_target_soc_pct/default_deadline_hour. Forecast vyžaduje >= 4 vzorky pro daný den v týdnu, jinak fallback.';
comment on column ems.asset_vehicle.min_target_soc_pct is
'Komfortní spodní mez forecast targetu (%). Forecast smí jít pod default_target_soc_pct (např. pátek), ne pod tuto mez.';
create table ems.ev_vehicle_obs (
id bigserial primary key,
site_id int not null references ems.site (id),
vehicle_id int not null references ems.asset_vehicle (id),
observed_at timestamptz not null default now(),
trigger text not null check (trigger in ('arrival', 'departure', 'manual')),
odometer_km numeric(10, 1),
soc_pct numeric(5, 2),
charging_state text
);
create index idx_ev_vehicle_obs_vehicle_time
on ems.ev_vehicle_obs (vehicle_id, observed_at desc);
comment on table ems.ev_vehicle_obs is
'Pozorování vozidla z API výrobce (Tesla Fleet) při příjezdu/odjezdu — auto je v těch okamžicích vzhůru, čtení nebudí. Zdroj pro ev_trip.';
create table ems.ev_trip (
id serial primary key,
vehicle_id int not null references ems.asset_vehicle (id),
departure_obs_id bigint not null references ems.ev_vehicle_obs (id),
arrival_obs_id bigint not null references ems.ev_vehicle_obs (id),
departed_at timestamptz not null,
arrived_at timestamptz not null,
km numeric(8, 1),
kwh_est numeric(7, 2),
charged_away boolean not null default false,
constraint uq_ev_trip_departure unique (departure_obs_id)
);
comment on table ems.ev_trip is
'Jízda = pár odjezd→příjezd: km z odometru, kWh z ΔSoC × kapacita. charged_away = SoC po cestě vzrostlo (nabíjení mimo dům) — kWh nevypovídá, vyloučit ze statistik.';
create table ems.ev_usage_stats (
vehicle_id int not null references ems.asset_vehicle (id),
day_of_week int not null check (day_of_week between 0 and 6),
avg_day_kwh numeric(7, 2),
stddev_day_kwh numeric(7, 2),
avg_day_km numeric(8, 1),
avg_departure_hour numeric(4, 2),
sample_count int not null default 0,
last_updated timestamptz not null default now(),
primary key (vehicle_id, day_of_week)
);
comment on table ems.ev_usage_stats is
'Týdenní rytmus vozidla per den v týdnu (0=neděle, Europe/Prague): průměrná denní spotřeba jízdou (kWh), km, typická hodina prvního odjezdu. Plní fn_update_ev_usage_stats (job 00:50). Vstup fn_ev_required_soc / fn_ev_expected_departure.';