telemetrie per pv_array, fix predictinos
This commit is contained in:
6
.idea/data_source_mapping.xml
generated
6
.idea/data_source_mapping.xml
generated
@@ -7,8 +7,14 @@
|
||||
<file url="file://$PROJECT_DIR$/db/migration/V009__postgrest_roles.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||
<file url="file://$PROJECT_DIR$/db/migration/V065__forecast_pv_interval_interval_start_index.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||
<file url="file://$PROJECT_DIR$/db/migration/V066__latest_telemetry_distinct_on_indexes.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||
<file url="file://$PROJECT_DIR$/db/migration/V070__forecast_accuracy_delta_profile_index.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||
<file url="file://$PROJECT_DIR$/db/migration/V071__forecast_pv_interval_pv_array_interval.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||
<file url="file://$PROJECT_DIR$/db/routines/R__023_fn_forecast_pv_split.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||
<file url="file://$PROJECT_DIR$/db/routines/R__066_fn_site_notifications_context.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||
<file url="file://$PROJECT_DIR$/db/routines/R__068_fn_economics_daily_month.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||
<file url="file://$PROJECT_DIR$/db/routines/R__078_fn_pv_forecast_delta_profile.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||
<file url="file://$PROJECT_DIR$/db/routines/R__079_fn_forecast_pv_slots_range_corrected.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||
<file url="file://$PROJECT_DIR$/db/views/R__058_vw_latest_telemetry.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||
<file url="file://$PROJECT_DIR$/db/views/R__072_z_postgrest_ems_anon_grants.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||
<file url="file://$PROJECT_DIR$/scripts/analysis/ote_arbitrage_proxy.sql" value="26ebef46-ff23-475b-8adc-082723263a02" />
|
||||
</component>
|
||||
|
||||
52
db/migration/V072__pv_array_telemetry_group_and_sources.sql
Normal file
52
db/migration/V072__pv_array_telemetry_group_and_sources.sql
Normal file
@@ -0,0 +1,52 @@
|
||||
-- =============================================================
|
||||
-- V072 – asset_pv_array.telemetry_group + rozšíření telemetry_source
|
||||
--
|
||||
-- Cíl:
|
||||
-- - umožnit mapování PV pole → měřicí kanál (pv1/pv2/pv_strings/pv_total/gen_port),
|
||||
-- - umožnit sdílené měření pro více polí (telemetry_group) a následnou alokaci (v routines).
|
||||
-- =============================================================
|
||||
|
||||
alter table ems.asset_pv_array
|
||||
add column if not exists telemetry_group text;
|
||||
|
||||
comment on column ems.asset_pv_array.telemetry_source is
|
||||
'Který sloupec v telemetry_inverter odpovídá tomuto poli.
|
||||
gen_port = gen_port_power_w (AC-coupled pole na GEN portu),
|
||||
pv1 = pv1_power_w (DC string 1 / MPPT1),
|
||||
pv2 = pv2_power_w (DC string 2 / MPPT2),
|
||||
pv_strings = pv1_power_w + pv2_power_w (souhrn DC stringů, pokud nejde rozlišit),
|
||||
pv_total = pv_power_w (souhrnné PV, pokud nejde rozlišit).
|
||||
NULL = pole nemá přímou telemetrii (fallback na forecast).';
|
||||
|
||||
comment on column ems.asset_pv_array.telemetry_group is
|
||||
'Volitelná skupina pro sdílené měření: pokud více pv_array sdílí jeden telemetrický kanál (např. GEN port rozdělený do více orientací),
|
||||
pak mají shodné (site_id, telemetry_source, telemetry_group) a routines alokují actual proporčně podle forecastu.';
|
||||
|
||||
-- --- Seed / upgrade stávajících referenčních lokalit ---
|
||||
|
||||
-- home-01: dvě GEN pole sdílí jeden GEN port → stejné telemetry_group
|
||||
update ems.asset_pv_array
|
||||
set telemetry_source = 'gen_port',
|
||||
telemetry_group = 'gen_port_1'
|
||||
where site_id = (select id from ems.site where code = 'home-01')
|
||||
and code in ('pv-b', 'pv-b-flat');
|
||||
|
||||
-- BA81: stringy mapujeme na PV1/PV2, mikroinvertory sdílí GEN port (alokace podle forecastu).
|
||||
update ems.asset_pv_array
|
||||
set telemetry_source = 'pv1',
|
||||
telemetry_group = null
|
||||
where site_id = (select id from ems.site where code = 'BA81')
|
||||
and code = 'pv-str-1';
|
||||
|
||||
update ems.asset_pv_array
|
||||
set telemetry_source = 'pv2',
|
||||
telemetry_group = null
|
||||
where site_id = (select id from ems.site where code = 'BA81')
|
||||
and code = 'pv-str-2';
|
||||
|
||||
update ems.asset_pv_array
|
||||
set telemetry_source = 'gen_port',
|
||||
telemetry_group = 'gen_port_1'
|
||||
where site_id = (select id from ems.site where code = 'BA81')
|
||||
and code in ('pv-mi-1', 'pv-mi-2');
|
||||
|
||||
56
db/migration/V073__pv_telemetry_source_def_fk.sql
Normal file
56
db/migration/V073__pv_telemetry_source_def_fk.sql
Normal file
@@ -0,0 +1,56 @@
|
||||
-- =============================================================
|
||||
-- V073 – číselník PV telemetrie + FK na asset_pv_array.telemetry_source
|
||||
--
|
||||
-- Cíl: referenční integrita pro telemetry_source (povolené kódy),
|
||||
-- aby se zabránilo překlepům a nekonzistentním datům.
|
||||
-- =============================================================
|
||||
|
||||
create table if not exists ems.pv_telemetry_source_def (
|
||||
code text primary key,
|
||||
description text not null,
|
||||
telemetry_inverter_expr text null,
|
||||
active boolean not null default true
|
||||
);
|
||||
|
||||
comment on table ems.pv_telemetry_source_def is
|
||||
'Číselník zdrojů PV telemetrie (kanálů) pro asset_pv_array.telemetry_source.';
|
||||
|
||||
comment on column ems.pv_telemetry_source_def.code is
|
||||
'Stabilní kód zdroje telemetrie (FK z asset_pv_array.telemetry_source).';
|
||||
|
||||
comment on column ems.pv_telemetry_source_def.telemetry_inverter_expr is
|
||||
'Volitelně: lidsky čitelný výraz, jak se kanál počítá z telemetry_inverter (informativní; runtime logika je v routines).';
|
||||
|
||||
insert into ems.pv_telemetry_source_def (code, description, telemetry_inverter_expr) values
|
||||
('gen_port', 'AC-coupled výroba na GEN portu (souhrn).', 'gen_port_power_w'),
|
||||
('pv1', 'DC string/MPPT 1 (samostatně).', 'pv1_power_w'),
|
||||
('pv2', 'DC string/MPPT 2 (samostatně).', 'pv2_power_w'),
|
||||
('pv_strings', 'Součet DC stringů (pv1+pv2).', 'pv1_power_w + pv2_power_w'),
|
||||
('pv_total', 'Souhrnná PV výroba (pokud nelze rozlišit).','pv_power_w')
|
||||
on conflict (code) do update
|
||||
set description = excluded.description,
|
||||
telemetry_inverter_expr = excluded.telemetry_inverter_expr,
|
||||
active = true;
|
||||
|
||||
-- FK (idempotentně): NULL povolen (pole bez přímé telemetrie / fallback na forecast).
|
||||
do $$
|
||||
begin
|
||||
if not exists (
|
||||
select 1
|
||||
from pg_constraint c
|
||||
join pg_class t on t.oid = c.conrelid
|
||||
join pg_namespace n on n.oid = t.relnamespace
|
||||
where n.nspname = 'ems'
|
||||
and t.relname = 'asset_pv_array'
|
||||
and c.conname = 'asset_pv_array_telemetry_source_fk'
|
||||
) then
|
||||
alter table ems.asset_pv_array
|
||||
add constraint asset_pv_array_telemetry_source_fk
|
||||
foreign key (telemetry_source)
|
||||
references ems.pv_telemetry_source_def(code)
|
||||
on update cascade
|
||||
on delete restrict;
|
||||
end if;
|
||||
end;
|
||||
$$;
|
||||
|
||||
@@ -188,6 +188,8 @@ BEGIN
|
||||
SELECT AVG(
|
||||
CASE r_bonus.telemetry_source
|
||||
WHEN 'gen_port' THEN ti.gen_port_power_w
|
||||
WHEN 'pv1' THEN ti.pv1_power_w
|
||||
WHEN 'pv2' THEN ti.pv2_power_w
|
||||
WHEN 'pv_strings' THEN COALESCE(ti.pv1_power_w, 0)
|
||||
+ COALESCE(ti.pv2_power_w, 0)
|
||||
WHEN 'pv_total' THEN ti.pv_power_w
|
||||
|
||||
@@ -50,7 +50,9 @@ BEGIN
|
||||
v.learning_exclude_reason
|
||||
FROM ems.forecast_pv_interval fpi
|
||||
JOIN ems.forecast_pv_run fpr ON fpr.id = fpi.run_id
|
||||
JOIN ems.asset_pv_array pa ON pa.id = fpr.pv_array_id
|
||||
JOIN ems.asset_pv_array pa
|
||||
ON pa.id = fpr.pv_array_id
|
||||
AND pa.site_id = fpr.site_id
|
||||
LEFT JOIN ems.site_pv_forecast_calibration cal
|
||||
ON cal.site_id = fpr.site_id
|
||||
LEFT JOIN LATERAL (
|
||||
@@ -115,16 +117,47 @@ BEGIN
|
||||
(flags.is_curtailed_learning_slot OR flags.is_telemetry_derated_slot) AS exclude_actual_for_learning
|
||||
) v ON true
|
||||
LEFT JOIN LATERAL (
|
||||
WITH base AS (
|
||||
SELECT AVG(
|
||||
CASE
|
||||
WHEN pa.controllable = false THEN ti.gen_port_power_w::NUMERIC
|
||||
ELSE (COALESCE(ti.pv1_power_w, 0) + COALESCE(ti.pv2_power_w, 0))::NUMERIC
|
||||
CASE coalesce(pa.telemetry_source, '')
|
||||
WHEN 'pv1' THEN ti.pv1_power_w::NUMERIC
|
||||
WHEN 'pv2' THEN ti.pv2_power_w::NUMERIC
|
||||
WHEN 'pv_strings' THEN (COALESCE(ti.pv1_power_w, 0) + COALESCE(ti.pv2_power_w, 0))::NUMERIC
|
||||
WHEN 'pv_total' THEN ti.pv_power_w::NUMERIC
|
||||
WHEN 'gen_port' THEN ti.gen_port_power_w::NUMERIC
|
||||
ELSE NULL
|
||||
END
|
||||
) AS avg_actual_w
|
||||
FROM ems.telemetry_inverter ti
|
||||
WHERE ti.site_id = fpr.site_id
|
||||
AND ti.measured_at >= fpi.interval_start
|
||||
AND ti.measured_at < fpi.interval_start + INTERVAL '15 minutes'
|
||||
),
|
||||
grp AS (
|
||||
-- Pokud více pv_array sdílí stejné měření (např. GEN port rozdělený do více orientací),
|
||||
-- rozdělíme actual proporčně podle forecastu v daném slotu.
|
||||
SELECT
|
||||
sum(fpi2.power_w)::numeric AS forecast_group_w
|
||||
FROM ems.forecast_pv_interval fpi2
|
||||
JOIN ems.forecast_pv_run fpr2 ON fpr2.id = fpi2.run_id
|
||||
JOIN ems.asset_pv_array pa2
|
||||
ON pa2.id = fpi2.pv_array_id
|
||||
AND pa2.site_id = fpr2.site_id
|
||||
WHERE pa.telemetry_group IS NOT NULL
|
||||
AND pa2.site_id = fpr.site_id
|
||||
AND pa2.telemetry_group = pa.telemetry_group
|
||||
AND pa2.telemetry_source = pa.telemetry_source
|
||||
AND fpi2.interval_start = fpi.interval_start
|
||||
AND fpr2.id = fpr.id
|
||||
)
|
||||
SELECT
|
||||
CASE
|
||||
WHEN pa.telemetry_group IS NULL THEN (SELECT avg_actual_w FROM base)
|
||||
WHEN (SELECT forecast_group_w FROM grp) IS NULL THEN NULL
|
||||
WHEN (SELECT forecast_group_w FROM grp) <= 0 THEN NULL
|
||||
WHEN (SELECT avg_actual_w FROM base) IS NULL THEN NULL
|
||||
ELSE (SELECT avg_actual_w FROM base) * (fpi.power_w::numeric / (SELECT forecast_group_w FROM grp))
|
||||
END AS avg_actual_w
|
||||
) slot ON true
|
||||
WHERE fpr.site_id = p_site_id
|
||||
AND fpr.status = 'ok'
|
||||
|
||||
@@ -25,23 +25,30 @@ begin
|
||||
and ti.measured_at >= p_window_start
|
||||
and ti.measured_at < p_window_end;
|
||||
|
||||
with pv_arrays as (
|
||||
select apa.id as pv_array_id
|
||||
from ems.asset_pv_array apa
|
||||
where apa.site_id = p_site_id
|
||||
),
|
||||
latest_run as (
|
||||
select distinct on (fpr.pv_array_id)
|
||||
fpr.pv_array_id,
|
||||
fpr.id as run_id
|
||||
from pv_arrays pa
|
||||
join ems.forecast_pv_run fpr
|
||||
on fpr.pv_array_id = pa.pv_array_id
|
||||
and fpr.site_id = p_site_id
|
||||
where fpr.status = 'ok'
|
||||
and fpr.created_at <= p_window_start
|
||||
order by fpr.pv_array_id, fpr.created_at desc
|
||||
)
|
||||
select coalesce(sum(fpi.power_w) * 0.25 / 1000.0, 0)
|
||||
into v_forecast
|
||||
from ems.forecast_pv_interval fpi
|
||||
join ems.forecast_pv_run fpr on fpr.id = fpi.run_id
|
||||
where fpr.site_id = p_site_id
|
||||
and fpi.interval_start >= p_window_start
|
||||
join latest_run lr on lr.run_id = fpi.run_id
|
||||
where fpi.interval_start >= p_window_start
|
||||
and fpi.interval_start < p_window_end
|
||||
and fpr.status = 'ok'
|
||||
and fpr.id = (
|
||||
select fpr2.id
|
||||
from ems.forecast_pv_run fpr2
|
||||
where fpr2.site_id = p_site_id
|
||||
and fpr2.status = 'ok'
|
||||
and fpr2.created_at <= p_window_start
|
||||
order by fpr2.created_at desc
|
||||
limit 1
|
||||
);
|
||||
and fpi.pv_array_id = lr.pv_array_id;
|
||||
|
||||
if v_forecast < 0.1 or coalesce(v_actual, 0) < 0.05 then
|
||||
return jsonb_build_object(
|
||||
|
||||
Reference in New Issue
Block a user