# 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`](../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`](../backend/app/main.py) (routery, CORS, WebSockety, health, `POST …/mode`) a [`backend/app/lifespan.py`](../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_anon` má
`SELECT` 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

```properties
# 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`.
