# Charging Monitoring Operation

Monitorizarea unei sesiuni de încărcare: colectarea măsurătorilor, izolarea curbei şi calculul energiei.

## Responsabilități

- Orchestrează o sesiune de încărcare complet (start → record → end)
- Colectează măsurători de la meter la intervale regulate (~1Hz)
- Izolează porţiunea relevantă a curbei (fără pre/post charging noise)
- Calculează totalul de energie consumată (Wh)
- Persistă date în Core Data + iCloud

## Invarianţi

- **MUST**: Doar o sesiune activă per meter la orice moment
- **MUST**: Măsurătorile sunt cronologice (sortate după timestamp)
- **MUST**: Start time < end time (sauf pentru sesiunile active)
- **MUST**: Energia totală ≥ 0 (nu poate fi negativă)
- **MUST**: O sesiune completată este imutabilă
- **SHOULD**: O sesiune durează între 5 minute şi 48 ore
- **MAY**: Sesiuni orphane (meter deconectat) pot fi finalizate automat

## Stadiile sesiunii

```
idle
  ↓
startSession() → starting
  ↓
recordMeasurement() → active (repeat)
  ↓
endSession() → ended
  ↓
completed (immutable)
```

## Lifecycle

### 1. Start sesiune

```swift
let session = meter.startChargeRecord(for: device)
// ⟹ ChargeRecord(
//    id: UUID(),
//    sessionID: UUID(),
//    startTime: Date(),
//    chargedDeviceID: device.id,
//    meterID: meter.id,
//    measurements: [],
//    totalEnergy: 0
// )
```

**MUST:**
- `startTime` = `Date.now`
- `sessionID` = UUID unic
- `measurements` array = empty
- `totalEnergy` = 0
- Marchez sesiunea ca "active"

**SHOULD:**
- Notify UI: "Recording started"
- Start periodic measurement requests (1Hz timer)
- Log sesiune în analytics

### 2. Record measurement

```swift
while session.isActive {
    let measurement = meter.lastDataPoint
    session.addMeasurement(measurement)
}
```

**Frecvenţă**: 1Hz (1000ms interval)

**MUST:**
- `measurement.timestamp > session.startTime`
- Măsurătorile ordonate cronologic
- Ignore duplicate timestamps
- Validare: voltage, current ≥ 0

**SHOULD:**
- Drop măsurători dacă queue > 500 items
- Skip interval dacă meter nu are date nouă
- Log invalid measurements (don't crash)

**MAY:**
- Adjust frecvenţă dacă battery low (500ms → 2s)
- Throttle dacă temperatura critică

### 3. End sesiune

```swift
meter.endChargeRecord(session)
```

**MUST:**
- `endTime` = `Date.now`
- Calculate `totalEnergy` = ∑(V × A × Δt) pentru fiecare interval
- Call [Charge Curve Isolation](./ChargeCurveIsolation.md) pentru obţine valid range
- Marchez sesiunea ca "completed"
- Salvează în Core Data sync

**SHOULD:**
- Recalculează media power (watts)
- Calculează duration (seconds)
- Notify UI: "Recording completed"
- Archive sesiunea (backup iCloud)

**MAY:**
- Trigger conflict resolution (dacă detecta duplicate-uri)
- Trigger capacity learning (dacă sesiune completă)

## API Public

### Proprietăţi sesiune

| Proprietate | Tip | Descriere |
|---|---|---|
| `id` | UUID | Identificator unic |
| `sessionID` | UUID | Reference în persistență |
| `chargedDeviceID` | UUID | Device monitorizat |
| `meterID` | UUID | Meter folosit |
| `startTime` | Date | Moment start |
| `endTime` | Date? | Moment end (nil dacă active) |
| `measurements` | [Measurement] | Puncte colectate |
| `totalEnergy` | Double | Wh total |
| `isActive` | Bool | Status curent |
| `duration` | TimeInterval | end - start |
| `peakPower` | Double | Max watts |
| `averagePower` | Double | Mean watts |

### Metode

```swift
// Orchestrare
func startChargeRecord(for device: ChargedDevice) -> ChargeRecord
func recordMeasurement(_ measurement: Measurement, in session: ChargeRecord)
func endChargeRecord(_ session: ChargeRecord) -> ChargeRecord

// Calculaţii
func calculateTotalEnergy(measurements: [Measurement]) -> Double
func calculatePeakPower(measurements: [Measurement]) -> Double
func calculateAveragePower(measurements: [Measurement]) -> Double

// Persistență
func saveSession(_ record: ChargeRecord)
func loadSession(id: UUID) -> ChargeRecord?
```

## Comportamente critice

### Timeout pe sesiuni

- **MUST**: Dacă nu primim măsurători > 5 min, sesiunea e considerat "stale"
- **SHOULD**: Notifică utilizator: "No data received, disconnect?"
- **MAY**: Auto-finalizează după 10 min inactivitate

### Validare măsurători

```swift
func isValidMeasurement(_ m: Measurement) -> Bool {
    // Voltage: 0V — 30V (USB + other)
    // Current: 0A — 10A (typical chargers)
    // Power: 0W — 300W (practical limit)
    return m.voltage >= 0 && m.voltage <= 30 &&
           m.current >= 0 && m.current <= 10 &&
           m.power >= 0 && m.power <= 300
}
```

**MUST**: Drop invalide measurements
**SHOULD**: Log warnings pentru edge-case values

### Merge duplicate sessions

Se declanşează când:
- Două sesiuni cu overlap temporal > 50%
- Aceeaşi device + meter

Rezoluţie:
- **MUST**: Keep sesiunea cu mai multe măsurători
- **MUST**: Combină energiile: `total = session1.energy + session2.energy`
- **SHOULD**: Archive old session version

### Energia negativă (edge case)

Cauze posibile:
1. Meter inversează polarity (rare)
2. Măsurători corupte
3. Bug în parsing

**SHOULD**: Log warning
**MAY**: Absolute value: `energy = abs(energy)`
**NEVER**: Discard sesiune

## Testare

### Unit tests

```swift
// Start/end
test_startSessionCreatesValidRecord()
test_endSessionCalculatesTotalEnergy()

// Measurements
test_recordMeasurement_AddsToSession()
test_recordMeasurement_FailsIfSessionEnded()
test_measurementsAreOrdered()
test_invalidMeasurement_IsDropped()

// Calculations
test_calculateTotalEnergy_WithValidData()
test_calculateTotalEnergy_WithGaps()
test_calculatePeakPower()
test_calculateAveragePower()

// Validation
test_sessionTimeValidity()
test_sessionDuration_Max48Hours()
test_activeSessions_OnlyOne()
```

### Integration tests

- [ ] Full cycle: start → record 100 samples → end
- [ ] Sesiune pe device A, apoi device B (same meter)
- [ ] Meter disconnect → auto-finalize
- [ ] Energy = ∑ = match manual calculation
- [ ] Sesiune persistent după app restart

## Dependenţe

- [Meter.md](./Meter.md): `meter.lastDataPoint`, state
- [Charge Curve Isolation](./ChargeCurveIsolation.md): extract valid range
- [Capacity Measurement](./CapacityMeasurement.md): capacity learning
- [Charge Curve Storage](./ChargeCurveStorage.md): persistence
- Core Data: `ChargeSession` entity

## Notes

- Time-series resolution: 1Hz (1000ms)
- Energy formula: `E = ∑(V × A × Δt)` (integral numerical)
- Legat: [Charge Session Integrity](../Charge%20Session%20Integrity%20and%20Conflict%20Healing.md)
