Files
ems/docs/02-architecture.md
Dusan Vojacek 02f0ab66e4
Some checks failed
CI and deploy / migration-check (pull_request) Failing after 27s
CI and deploy / deploy (pull_request) Has been skipped
gpt5.5 - odladeni dokumentace dle kodu
2026-05-02 19:17:04 +02:00

10 KiB
Raw Blame History

EMS Platform Architektura

Vrstvy systému

┌─────────────────────────────────────────────┐
│  React Frontend (Vite + TypeScript)          │
│  Dashboard, plány, telemetrie; výběr site    │
│  (combobox → /api/v1/me/sites + PostgREST)   │
└─────────────┬───────────────────────────────┘
              │ HTTP/REST
┌─────────────▼───────────────────────────────┐
│  PostgREST                                   │
│  Auto-REST API z PostgreSQL schématu ems     │
│  Read-only: views, vybrané tabulky           │
│  Write operace jdou přes FastAPI             │
└─────────────┬───────────────────────────────┘
              │ SQL
┌─────────────▼───────────────────────────────┐
│  PostgreSQL 18 + TimescaleDB                 │
│  Schéma: ems                                 │
│  Funkce, views, hypertables                  │
└─────────────┬───────────────────────────────┘
              │
┌─────────────▼───────────────────────────────┐
│  FastAPI (Python)                            │
│   Scheduled tasks (APScheduler)            │
│   telemetry_collector  (každých 60s)        │
│   heartbeat            (každých 60s)        │
│   price_importer       (13:25, 13/14:12,   │
│                          13/14:45, 14:00,   │
│                          00:05)             │
│   forecast_service     (každé 2h, minute 05)│
│   planning_engine      (denně 15:00)        │
│   rolling_replan       (každých 15min)      │
│   control_exporter     (každých 15min)      │
│   audit_filler         (každých 15min)      │
│   forecast_accuracy    (:02,:17,:32,:47)   │
│   plan_actual_slot_guard (:05,:20,:35,:50) │
│   verify_modbus        (každé 2 min)        │
│   signal_outbound      (každých 15s)        │
└──────┬──────────────────────────┬────────────┘
       │ Modbus TCP               │ HTTP
┌──────▼──────┐           ┌───────▼────────────┐
│  Waveshare  │           │  Loxone Miniserver  │
│  WS-ETH     │           │  (setpoint přijímač)│
└──────┬──────┘           └────────────────────┘
       │ RS485
┌──────┼──────────────────────────────┐
│  Deye SUN-20K  │  Teltonika 2×      │  Samsung TČ  │
└────────────────┴────────────────────┴──────────────┘

Read-model JSONB (ems.fn_*)

FastAPI endpointy pro dashboard a konfiguraci preferují jedno volání select ems.fn_*(…) vracející jsonb (pole řádků, agregace, merge locků), aby v Pythonu nezůstávaly ad-hoc SELECT/JOIN/WITH. Pomocník app.db_json.fetch_json vrací dict/list. Telemetrie a IO zůstávají v Pythonu; čisté agregace a sjednocení TZ patří do SQL. Opakované migrace: db/routines/R__NNN_fn_*.sql, db/views/R__NNN_vw_*.sql (prefix NNN = pořadí závislostí pro Flyway).

SQL-first (stejné pravidlo jako v CLAUDE.md): doménová logika ve funkcích a view ve schématu ems; Python neskladá vlastní joiny nad tabulkami — nová data z více zdrojů = nová fn_* nebo vw_*, ne řetězení SQL v aplikaci.

Health, joby po aktivních lokalitách a Loxone po změně režimu jsou v repeatable db/routines/R__073_fn_health_site_jobs_mode_bundle.sql: fn_health_summary, fn_health_detailed_db, fn_vw_site_directory_active, fn_site_economics_yesterday_notification, fn_site_mode_loxone_bundle. FastAPI je rozdělené: backend/app/main.py (routery, CORS, WebSockety, health, POST …/mode) a backend/app/lifespan.py (DB pool, APScheduler joby, telemetrická smyčka).

Komponenty

Komponenta Technologie Port Popis
db PostgreSQL 18 + TimescaleDB 5432 Datová vrstva
postgrest PostgREST 12 3000 Auto-REST API
backend Python 3.12 / FastAPI 8000 Logika, scheduled tasks
frontend React + Vite + TypeScript 5173 (dev) / 80 (prod) UI

Adresář projektu

ems-platform/
  CLAUDE.md
  docker-compose.yml
  docker-compose.dev.yml
  .env.example
  .env                          ← gitignore!

  db/
    migration/
      V001__init_schema.sql
      V002__timescale_hypertables.sql
      V003__seed_site_home01.sql
    routines/
      R__003_fn_baseline_consumption.sql
      R__005_fn_cop_estimate.sql
      R__011_fn_effective_price.sql
      R__019_fn_fill_audit_interval.sql
      R__073_fn_health_site_jobs_mode_bundle.sql
      (historicky) R__fn_plan_day.sql  primární plánování je PuLP v Pythonu
      R__037_fn_planning_run_commit.sql
      R__063_fn_load_planning_slots_full.sql
    views/
      R__061_vw_site_effective_price.sql
      R__058_vw_latest_telemetry.sql
      R__071_vw_telemetry_15m_7d.sql
      R__vw_actual_baseline.sql
      R__053_vw_audit_summary.sql
      R__vw_heat_pump_cop_history.sql
    flyway.conf

  backend/
    Dockerfile
    requirements.txt
    app/
      main.py                   ← FastAPI app, routery, health, změna režimu
      lifespan.py             ← lifespan: pool, APScheduler, telemetry task
      refresh_negative_prices.py  ← sdílený hook po importu cen / forecastu
      routers/
        sites.py               ← /api/v1/sites (ceny, control, forecast)
        me.py                    ← /api/v1/me/sites
      config.py                 ← settings z env
      database.py               ← asyncpg (alternativní / legacy helper)
    services/
      telemetry_collector.py
      price_importer.py
      forecast_service.py
      planning_engine.py        ← PuLP solver, ukládá přes ems.fn_planning_run_commit()
      control_exporter.py
      audit_filler.py
    modbus/
      deye_client.py
      ev_charger_client.py
      heat_pump_client.py
    models/
      site.py
      assets.py
      setpoints.py

  frontend/
    Dockerfile
    package.json
    vite.config.ts
    src/
      main.tsx
      App.tsx
      api/
        postgrest.ts             ← PostgREST client
        backend.ts               ← FastAPI client (/me/sites, …)
      context/
        SiteSelectionContext.tsx ← výběr lokality, localStorage
      pages/
        Dashboard.tsx
        Planning.tsx
        Telemetry.tsx
        Settings.tsx
      components/
        PowerFlowChart.tsx
        PriceChart.tsx
        SocGauge.tsx
        OverridePanel.tsx

  docs/
    01-overview.md
    02-architecture.md           ← tento soubor
    03-data-model.md
    04-modules/
      market-prices.md
      forecast.md
      consumption.md
      heat-pump.md
      telemetry.md
      control.md
      planning.md
    06-open-questions.md

Komunikační toky

Sběr dat (každých 60s)

Zařízení → Waveshare → Modbus TCP → telemetry_collector → PostgreSQL

Denní plánování (15:00) a rolling replan

PostgreSQL (ceny + forecast + telemetrie) → planning_engine (PuLP)
  → fn_planning_run_commit() → planning_interval

Operátorské manuální akce (UI)

Browser → FastAPI:
  GET  /api/v1/me/sites                         ← seznam aktivních lokalit (UI combobox; bez auth zatím = všechny aktivní)
  POST /api/v1/sites/{site_id}/prices/import?date=YYYY-MM-DD   ← zapisuje globální market_interval_price; site_id jen validace URL
  POST /api/v1/sites/{site_id}/forecast/run
  POST /api/v1/sites/{site_id}/plan/run?type=daily|rolling

Export setpointů (každých 15min)

PostgreSQL (planning_interval + overrides) → control_exporter
  → Modbus TCP → Waveshare → Deye / Teltonika / Samsung
  → HTTP → Loxone

Frontend

Browser → PostgREST (čtení views/tabulek, filtr site_id dle výběru v UI)
Browser → FastAPI (seznam lokalit /me/sites, triggery: replanning, import cen, …)

PostgREST je v aktuální produkční konfiguraci určený pro čtení (ems_anonSELECT na vybrané views/tabulky). Zápisy a operátorské akce se provádí přes FastAPI, které používá vlastní DB connection.


Deployment: single-site (výchozí)

Vše na jednom stroji (x86 mini PC nebo silnější RPi 5):

Docker Compose:
  db         (PostgreSQL + TimescaleDB)
  postgrest  (PostgREST)
  backend    (FastAPI + všechny services)
  frontend   (Nginx + React build)
  flyway     (migrace při startu)

Minimální HW požadavky

Parametr Minimum Doporučeno
CPU 2 jádra 4 jádra (x86)
RAM 4 GB 8 GB
Storage SSD 64 GB NVMe 256 GB
OS Debian 12 / Ubuntu 22.04 Ubuntu 22.04 LTS
Síť 100 Mbps Gigabit Ethernet

Raspberry Pi 5 (8GB): Použitelné pro single-site s SSD přes USB 3 nebo NVMe HAT. Nedoporučovat microSD TimescaleDB zápisy microSD rychle opotřebují.


Flyway konfigurace

# db/flyway.conf
flyway.url=jdbc:postgresql://db:5432/ems
flyway.user=${DB_USER}
flyway.password=${DB_PASSWORD}
flyway.schemas=ems
flyway.locations=filesystem:/flyway/migration,filesystem:/flyway/routines,filesystem:/flyway/views
flyway.validateOnMigrate=true
flyway.outOfOrder=false

Flyway se spouští jako jednorázový kontejner při docker-compose up.