Ana içeriğe geç

Faz 6 — Advanced Features

PR #270 ile geldi. Frontend MVP'nin (Faz 5) üzerine session drilldown, raporlama, SLD canlı entegrasyonu ve SmartCharging schedule editor eklendi. Toplam ~4 305 satır kod, 4 paralel agent (A/B/C/D), 130 yeni i18n key (TR + EN parite).

Durum: Tamamlandı (PR #270 merged, prod canary öncesi).


1. Genel Bakış

BileşenLinesSorumluluk
Faz 6-A — Session History + Drilldown~1 080Sessions liste + filter + Recharts drawer
Faz 6-B — Reports + CSV Export~900Backend calculate_charging_report + frontend BarChart
Faz 6-C — SLD Charger Node~570React Flow custom node + WS canlı update
Faz 6-D — SmartCharging Editor~1 120RHF + Zod cross-field refine, period swap
i18n parite130 keysessions 53 + reports 34 + sld 11 + profile 32

Faz 6 tüm bileşenlerinde:

  • siteConfig.apiUrl/wsUrl zorunlu (CLAUDE.md §2 build-time bake tuzağı koruması).
  • Idempotency-Key her mutation'da crypto.randomUUID().
  • Tenant scope her query'de zorunlu.
  • TR + EN i18n parite (eksik key build'te fail).

2. Faz 6-A — Session History + Drilldown

2.1 Sayfa: app/chargers/[id]/sessions/page.tsx

Bir charger'a özel session geçmişi:

  • Filter: tarih aralığı (DateRangePicker), status (active/completed/aborted), id_tag son-4 mask.
  • Pagination: offset-based, default limit=50, max 200 (Faz 3 contract).
  • Routing: session satırına click → drawer açılır (URL query param session_id ile shareable).

2.2 Component: SessionTable.tsx

KolonDetay
Başlangıç / BitişLocale-aware (tr-TR / en-US) tarih + saat
Süreduration_secondsH:mm:ss formatlı
id_tagSon-4 mask (KVKK; UI hiçbir zaman plaintext göstermez)
ConnectorConnector ID + status pill
Enerji (kWh)Wh→kWh dönüşüm UI'da; backend Wh dönmez (Faz 6-B)
StatusBadge: completed (yeşil), active (mavi pulse), aborted (kırmızı)

2.3 Component: SessionDetailDrawer.tsx

shadcn Sheet + Recharts entegrasyonu:

  • Selectors: measurand (Power.Active.Import / Voltage / Current.Import / Energy.Active.Import.Register / SoC), downsample (30s/1m/5m/15m/1h/4h/1d), aggregate (avg/min/max/p95).
  • TanStack auto-refetch: active session ise refetchInterval=10s; completed ise stale.
  • CSV Export: session-scoped — drawer içinden GET /api/ocpp/sessions/{id}/export.csv (Faz 3 endpoint).
  • Performans: Recharts <LineChart> + React.memo, sample array referans-stabil.

3. Faz 6-B — Reports + CSV Export

3.1 Backend: calculate_charging_report()

backend/app/features/reports/service.py içinde — 4 group_by stratejisi:

group_byDetay
daydate_trunc('day', started_at) — günlük breakdown
chargerocpp_charger_id GROUP BY — charger ranking
id_tagid_tag (son-4 mask UI'a kadar taşınır) — kullanıcı ranking
tariffLEFT JOIN tariffs ON sessions.tariff_id — tariff eşlemesi (NULL'lar "tariff_unknown" bucket'ında)

Önemli karar:

  • Wh → kWh ve sec → min dönüşümü SQL içinde yapılır ((SUM(energy_wh) / 1000.0) AS energy_kwh, (SUM(duration_sec) / 60.0) AS duration_min). Frontend dönüşüm yapmaz; precision drift riskini yok eder.
  • Tenant filter: WHERE charger.tenant_id = :tenant_id zorunlu.
  • Date range filter: started_at BETWEEN :from AND :to (UTC ISO-8601).

3.2 Endpoint: GET /api/reports/charging-sessions

  • Permission: READ_CHARGER (Faz 3 RBAC matrix).
  • Query params: from (ISO-8601), to (ISO-8601), group_by (day/charger/id_tag/tariff), charger_id (opsiyonel filter).
  • Response: rows + summary aggregates (total_energy_kwh, total_sessions, total_duration_min, avg_session_kwh).
  • CSV variant: ?format=csv query param → Content-Type: text/csv; charset=utf-8.

3.3 Frontend: ChargingReport.tsx

app/reports/page.tsx içine yeni "EV Şarj" <Tabs> sekmesi olarak yerleşir.

  • Filter card: DateRangePicker + 2 <Select> (group_by, charger filter).
  • 4 SummaryCard: toplam enerji, toplam oturum, toplam süre, ortalama oturum kWh.
  • Recharts BarChart: çift Y-axis (sol: enerji kWh, sağ: oturum sayısı). Bar + Line composed.
  • Detay tablo: group_by'a göre dinamik kolon (day/charger/id_tag/tariff).
  • CSV indirme: "İndir" butonu ?format=csv query'sini çağırır, Content-Disposition filename respect edilir.

4. Faz 6-C — SLD Charger Node + Live Energy Flow

4.1 Backend: sld/service.py

device_role='charger' olan device'lar SLD payload'ında özel olarak işlenir:

  • JOIN ocpp_chargers ON device.external_id = ocpp_charger.id ile metadata (vendor, model, connector_count) eklenir.
  • Active session bilgisi: subquery WHERE charger_id = ? AND status = 'active' LIMIT 1.
  • Live power lookup: Redis GET ocpp:live_power:{charger_id} (Faz 4 meter publisher) — son 30sn'lik kW snapshot. NULL ise 0.0.
  • Tenant scope: cross-tenant SLD probe → 404.

4.2 Frontend: OcppChargerNode.tsx

React Flow custom node:

StatusGörünüm
chargingMavi pulse animation (charging→aktif enerji akışı)
online (idle)Yeşil sabit
offlineGri sabit
faultedKırmızı pulse
unavailableSarı sabit

İçerik:

  • CPID kısaltılmış (8 char + ...).
  • Güç chip (kW, 1 ondalık).
  • Toplam enerji chip (kWh, lifetime).
  • Connector chip (occupied/total).
  • Hover tooltip: vendor, model, firmware version, son boot zamanı.
  • Click → /chargers/{id} route'a yönlendirir (Next.js useRouter).

4.3 SLD Canvas Live Update

DeviceHierarchySldCanvas Faz 6-C için genişletildi:

  • useOcppLiveStream({ eventTypes: ['ocpp.meter', 'ocpp.status', 'ocpp.session_started', 'ocpp.session_stopped'] }) ile event subscribe.
  • Event geldikçe setNodes (charger node data güncelle) + setEdges (charger→bus edge animation hızı kW'a göre değişir).
  • Performans: event throttle 200ms; üst üste birden fazla meter event olursa yalnızca son value uygulanır.
  • Subscription cleanup: component unmount'ta WS unsubscribe + memo'lu callback'ler dispose edilir.

5. Faz 6-D — SmartCharging Schedule Editor

5.1 Component: ChargingProfileEditor.tsx

React Hook Form (useForm + useFieldArray) + Zod cross-field refine:

Profile metadata alanları:

  • connector_id (number, 0 = whole charger)
  • stack_level (number, 0-127)
  • purpose (ChargePointMaxProfile / TxDefaultProfile / TxProfile)
  • kind (Absolute / Recurring / Relative)
  • recurrency_kind (Daily / Weekly) — yalnızca kind=Recurring'de
  • valid_from / valid_to (ISO-8601 timestamp)
  • transaction_id — yalnızca purpose=TxProfile'da

Schedule metadata:

  • charging_rate_unit (A / W)
  • duration (saniye)
  • start_schedule (ISO-8601)
  • min_charging_rate (number)

Period listesi (useFieldArray):

  • Yukarı/aşağı swap: move(index, index ± 1).
  • Add/remove butonları.
  • Her period: start_period (saniye), limit (rate), number_phases (1/3).

Cross-field Zod refine:

.refine((data) => {
if (data.kind === 'Recurring' && !data.recurrency_kind) {
return false; // recurrency_kind zorunlu
}
if (data.purpose === 'TxProfile' && !data.transaction_id) {
return false; // transaction_id zorunlu
}
// periods start_period artan
for (let i = 1; i < data.schedule.periods.length; i++) {
if (data.schedule.periods[i].start_period <= data.schedule.periods[i-1].start_period) {
return false;
}
}
return true;
})

Idempotency-Key: her submit'te crypto.randomUUID() üretilir; React Query retry'ları aynı key ile gönderir (dedup window 24h).

5.2 Component: ChargingProfileList.tsx

  • Charger detay sayfasında yeni "Şarj Profilleri" tab.
  • Liste: profile_id, purpose, kind, valid_from/to, period count, push status.
  • Silme: shadcn AlertDialog onaylı — kullanıcı "ClearChargingProfile push edilecek, geri alınamaz" uyarısını onaylar → DELETE /api/ocpp/chargers/{id}/charging-profiles/{profile_id}.

5.3 Permission Gating

  • WRITE_CHARGER: profile create/update.
  • CONFIGURE_CHARGER: period editor erişimi.
  • Eksik permission'da editor read-only mode (form disabled + "Yetkiniz yok" uyarısı).

6. i18n Parite

130 yeni key, TR + EN parite zorunlu (eksik key build'te fail):

ModülKey sayısı
Sessions (drawer + tablo + filter)53
Reports (filter + summary + tablo)34
SLD (charger node + tooltip)11
Profile editor (form + AlertDialog)32

Locale dosyaları:

  • frontend/src/locales/tr/ocpp.json
  • frontend/src/locales/en/ocpp.json

7. Faz 6 Çıktı Listesi (tamamlandı — PR #270)

#İşDosya
1Sessions sayfası + tabloapp/chargers/[id]/sessions/page.tsx, components/chargers/SessionTable.tsx
2Session drilldown drawercomponents/chargers/SessionDetailDrawer.tsx
3Reports backend servicebackend/app/features/reports/service.py (calculate_charging_report)
4Reports endpointbackend/app/features/reports/router.py (GET /api/reports/charging-sessions)
5Charging report UIcomponents/reports/ChargingReport.tsx
6SLD backend joinbackend/app/features/sld/service.py
7SLD charger nodecomponents/sld/OcppChargerNode.tsx
8SLD canvas livecomponents/sld/DeviceHierarchySldCanvas.tsx
9SmartCharging editorcomponents/chargers/ChargingProfileEditor.tsx
10Profile listcomponents/chargers/ChargingProfileList.tsx
11i18nlocales/{tr,en}/ocpp.json (130 key)

8. Sonraki Adımlar

Faz 7 — Production Deployment:

  • Nginx /ocpp/ location bloğu (TLS, WS upgrade, 4h timeout).
  • Docker Compose 11 OCPP env x 3 servis (backend + celery-worker + celery-beat).
  • Prometheus 5 alert + Grafana 10 panel.
  • CI integration test (mock charger e2e).
  • Security audit + release gate.

Detaylar: Faz 7 — Production Deployment.


İlgili Dökümanlar