# Consumption Measurement Operation

Măsurarea consumului pe o perioadă arbitrară, cu statistici şi predicţii bazate pe interval selectat.

## Responsabilități

- Măsurare consum liber (orice interval, nu doar sesiuni complete)
- Afişare statistici pe interval selectat
- Generare predicţii (extrapolări pe bază de trend)
- Tăiere curbei ("tail trimming") pentru a selecta porţiunea relevantă
- Salvează statisticile, NU datele măsurătorilor brute

## Diferenţa faţă de Charging Monitoring

| Aspect | Charging Monitoring | Consumption Measurement |
|---|---|---|
| Interval | Fixed (start → end charge) | Arbitrary (user-selected) |
| Storage | Sesiune completă + measurements | Statistics only |
| UI | Timeline sesiune | Graph with selection |
| Predicţii | Capacity learning | Energy extrapolation |

## Concepte

### Consumption = Energy over time

```
Energy (Wh) = ∫ P(t) dt
            = ∑ (V × A × Δt)

Power (W) = V × A (instantaneous)
Average Power (W) = Total Energy / Duration
```

## Invarianţi

- **MUST**: `startTime < endTime` (interval valid)
- **MUST**: Interval ≥ 10 secunde (minim useful)
- **MUST**: Interval ≤ 30 zile (limit practic pentru extrapolări)
- **MUST**: Statisticile trebuie salvate, măsurătorile brute se discard
- **MUST**: Fără măsurători individuale în persistență, doar agregări
- **SHOULD**: Interval tipic: 1 minut — 24 ore
- **MAY**: Predicţii valide doar pe ±1 interval (ex: dacă measured 1h, predict next 1h)

## Lifecycle

### 1. Selection interval (user interaction)

Utilizatorul deschide graficul şi selectează interval:

```
User taps start position în grafic
User drags to end position
System highlights selected region
User taps "Measure consumption"
```

**Rezultat:**
```swift
struct ConsumptionSelection {
    let startTime: Date
    let endTime: Date
    let meter: Meter
    let device: ChargedDevice?  // Optional, may be unknown
}
```

**MUST:** Times trebuie din măsurători existente (bounded)

### 2. Compute statistics

```swift
func computeConsumptionStats(
    measurements: [Measurement],
    from startTime: Date,
    to endTime: Date
) -> ConsumptionStats {
    // 1. Filter measurements în interval
    let filtered = measurements.filter { m in
        m.timestamp >= startTime && m.timestamp <= endTime
    }
    
    guard !filtered.isEmpty else {
        return .empty
    }
    
    // 2. Calculate energy
    let energy = calculateEnergy(filtered)
    
    // 3. Calculate time-based metrics
    let duration = endTime.timeIntervalSince(startTime)
    let averagePower = energy * 3600 / duration  // Convert Wh to W
    
    // 4. Power statistics
    let powers = filtered.map { $0.power }
    let peakPower = powers.max() ?? 0
    let minPower = powers.min() ?? 0
    let stdDev = calculateStdDev(powers)
    
    // 5. Voltage/Current stats
    let voltages = filtered.map { $0.voltage }
    let currents = filtered.map { $0.current }
    
    return ConsumptionStats(
        energy: energy,
        duration: duration,
        averagePower: averagePower,
        peakPower: peakPower,
        minPower: minPower,
        stdDevPower: stdDev,
        sampleCount: filtered.count,
        startVoltage: filtered.first?.voltage,
        endVoltage: filtered.last?.voltage,
        averageVoltage: voltages.average(),
        averageCurrent: currents.average()
    )
}
```

### 3. Generate predictions

```swift
func predictFutureConsumption(
    stats: ConsumptionStats,
    duration: TimeInterval
) -> ConsumptionPrediction {
    // Linear extrapolation
    let predictedEnergy = stats.averagePower * duration / 3600
    
    // Confidence bounds (±20% for stability)
    let confidence = 0.8
    let lowerBound = predictedEnergy * (1 - (1 - confidence))
    let upperBound = predictedEnergy * (1 + (1 - confidence))
    
    return ConsumptionPrediction(
        energy: predictedEnergy,
        lowerBound: lowerBound,
        upperBound: upperBound,
        confidence: confidence,
        method: "linear"
    )
}
```

**Metode:**
- **Linear**: `E_predicted = P_avg × t` (simple, stable)
- **Polynomial**: `E_predicted = a + b×t + c×t²` (for trends)
- **Exponential**: (rarely used, for battery degradation)

**MUST:** Confidence ≤ 1.0 (0-100%)
**SHOULD:** Linear enough for 1 interval ahead
**MAY:** Polynomial dacă > 2 intervals

### 4. Tail trimming (user action)

Utilizatorul poate "tăia coada" pentru a refina selecţia:

```
Initial selection: 00:00 - 10:00 (10h)
User drags start: 00:30 - 10:00 (9.5h)
User drags end: 00:30 - 09:30 (9h)
Result: statistics recalculate pe [00:30, 09:30]
```

**Interactiv:**
- Slider @ start time
- Slider @ end time
- Real-time stats update

**MUST:** Recalculate stats on every trim
**SHOULD:** Debounce updates (100ms)
**MUST:** Store final selection, not intermediate states

### 5. Save consumption record

**NU salvează:** Măsurătorile individuale

**SALVEAZĂ:**
```swift
struct ConsumptionRecord {
    let id: UUID
    let meterID: UUID
    let deviceID: UUID?
    let startTime: Date
    let endTime: Date
    
    // Statistics (aggregated)
    let totalEnergy: Double  // Wh
    let averagePower: Double  // W
    let peakPower: Double
    let minPower: Double
    let stdDevPower: Double
    let sampleCount: Int
    
    // Metadata
    let createdAt: Date
    let notes: String?  // User annotation
}
```

**NOT saved:**
- ❌ Individual measurements (voltage, current per sample)
- ❌ Timestamps of each sample
- ❌ Raw power values

**REASON:** Storage efficiency + privacy

## API Public

### Selection & Stats

```swift
// User selects interval from graph
func selectConsumptionInterval(
    start: Date,
    end: Date,
    meter: Meter
) -> ConsumptionStats?

// Refine selection via tail trimming
func trimConsumptionInterval(
    from newStart: Date,
    to newEnd: Date
) -> ConsumptionStats?

// Get current statistics
func currentStats() -> ConsumptionStats?
```

### Predictions

```swift
// Predict next interval
func predictConsumption(
    for duration: TimeInterval
) -> ConsumptionPrediction?

// Extrapolate to full day/week
func extrapolateToDay() -> ConsumptionPrediction?
func extrapolateToWeek() -> ConsumptionPrediction?
```

### Persistence

```swift
// Save aggregated stats only
func saveConsumptionRecord(_ record: ConsumptionRecord) -> Bool

// Load history
func loadConsumptionRecords(for meter: Meter) -> [ConsumptionRecord]

// Discard raw data (keep stats)
func discardMeasurements(before: Date) -> Int
```

## Comportamente critice

### Interval validation

```
User selects: 10:00 AM - 10:00 AM (zero duration)
⟹ Error: "Minimum 10 seconds"

User selects: 01-May - 01-June (31 days)
⟹ Warning: "Extrapolation unreliable >7 days"
```

**MUST:** duration ≥ 10s
**SHOULD:** Warn if duration > 7 days
**SHOULD:** Cap predictions to ±1 interval

### No tail = invalid

```
Selection: 10:00-11:00
User never presses "Save"
App terminates
⟹ Selection lost (no persistence of in-progress)
```

**MUST:** NU salvează interim selections
**SHOULD:** Keep in-memory during session
**MUST:** Explicit save() required

### Power variability

```
Measuring USB charger:
Min power: 0.1W (idle)
Max power: 18W (fast charge)
StdDev: high (~5W)

Prediction: linear avg = 8W
Confidence: 0.7 (high variance)
```

**SHOULD:** Calculate stdDev
**SHOULD:** Reflect confidence (lower if high variance)
**MAY:** Suggest "Power unstable, prediction unreliable"

### Measurement gaps

```
Recording: 10:00-11:00
Meter disconnect: 10:30-10:45
Data available: 10:00-10:30 (30min) + 10:45-11:00 (15min)
User selects: 10:00-11:00 (but only 45min data)

Stats: calculate on available data
Warning: "15 minutes missing"
```

**MUST:** Alert user to gaps
**SHOULD:** Calculate on available data only
**MAY:** Adjust confidence (lower if gaps > 10%)

## Statistici generate

### Basic

| Metric | Unit | Formula |
|---|---|---|
| Total Energy | Wh | ∑(V × A × Δt) |
| Duration | seconds | endTime - startTime |
| Average Power | W | Energy × 3600 / Duration |
| Peak Power | W | max(P(t)) |
| Min Power | W | min(P(t)) |

### Advanced

| Metric | Unit | Descriere |
|---|---|---|
| StdDev Power | W | Variabilitate în putere |
| Avg Voltage | V | Media tensiune |
| Avg Current | A | Media curent |
| Sample Count | # | Measurement count |
| Confidence | % | Reliability predicţie |

## Predicţii

### Linear extrapolation

```
Measured: P_avg = 10W over 1 hour
Predict next 1h: E = 10W × 1h = 10Wh
Bounds: ±(1-confidence)% = ±20% = [8Wh, 12Wh]
```

### Time scaling

```
Measured: 5W average over 10 minutes
Predict over 1 hour: 5W × 6 = 30Wh
Predict over 24h: 5W × 144 = 720Wh

But confidence decreases with time:
- 1×interval: 80% confidence
- 2×interval: 60% confidence
- 5×interval: 40% confidence
- 10+×interval: 20% confidence
```

**MUST:** Confidence degrades dengan scaling
**SHOULD:** Warn if confidence < 50%

## Testare

### Unit tests

```swift
test_selectConsumptionInterval_ValidDates()
test_selectConsumptionInterval_MinimumDuration()
test_trimConsumptionInterval_UpdatesStats()
test_computeConsumptionStats_ValidData()
test_computeConsumptionStats_EmptyRange()
test_calculateAveragePower()
test_predictConsumption_Linear()
test_predictConsumption_ConfidenceDegrades()
test_extrapolateToDay_WithinBounds()
test_recordDoesNotStoreMeasurements()
test_discardMeasurements_KeepsStats()
```

### Integration tests

- [ ] Select 1h interval → get stats
- [ ] Trim tail → stats recalculate
- [ ] Predict next 1h → confidence 80%
- [ ] Predict next 24h → confidence drops to 40%
- [ ] Save record → measurements discarded, stats kept
- [ ] Load record → only stats available
- [ ] Measurement gap detected → warning shown
- [ ] High power variance → confidence reduced

## Edge cases

### Very short interval

```
User selects: 10:00:00 - 10:00:05 (5 seconds)
⟹ Error: below 10s minimum
```

**MUST:** Reject

### Very long interval

```
User selects: 01-Jan - 31-Dec (365 days)
⟹ Warning: "Extrapolation unreliable beyond 7 days"
Confidence: 20%
```

**SHOULD:** Accept but warn
**SHOULD:** Reduce confidence

### Single measurement

```
Interval: 10:00:00 - 10:00:01
Data points: 1 (only start)
Energy: 0 (insufficient interval)
⟹ Error: "Need minimum 10 measurements"
```

**MUST:** Require ≥ 10 samples

### No voltage/current data

```
Meter type: Unknown or not reporting
Power available: yes
Voltage/Current: null

Stats: calculate energy from power only
Avg Voltage: null
Avg Current: null
```

**SHOULD:** Handle gracefully
**MAY:** Show "Limited data available"

## Storage model

### What IS saved

```json
{
  "id": "UUID",
  "meterID": "UUID",
  "startTime": "2026-05-23T10:00:00Z",
  "endTime": "2026-05-23T11:00:00Z",
  "totalEnergy": 12.5,
  "averagePower": 12.5,
  "peakPower": 18.0,
  "minPower": 1.2,
  "stdDevPower": 4.5,
  "sampleCount": 3600,
  "createdAt": "2026-05-23T11:02:00Z"
}
```

### What is NOT saved

```
❌ individual measurements
❌ voltage samples
❌ current samples  
❌ power curve
❌ battery levels
❌ meter metadata
```

## Dependenţe

- [Charging Monitoring](./ChargingMonitoring.md): access to meter data
- [Charge Curve Storage](./ChargeCurveStorage.md): query full measurements
- Core Data: ConsumptionRecord entity
- UI: Graph view with interval selection

## UI Components

### Graph with selection

```
┌─────────────────────────────────┐
│  Power (W)                      │
│  20 ├──────┐                    │
│     │ ████ │  ← user selection  │
│  10 ├──████└────┐               │
│     │          │               │
│   0 └──────────┴───────────────┘
│     10:00   10:30    11:00
│     ↑start           ↑end
│     (draggable)      (draggable)
└─────────────────────────────────┘
```

### Stats display

```
Selected Interval: 10:00 — 10:30 (30 min)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Energy:         6.3 Wh
Average Power:  12.6 W
Peak Power:     18.0 W
Min Power:      1.2 W
Variance:       ±4.5 W
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Prediction (next 30 min):
  Energy:       6.3 Wh (confidence: 80%)
  Range:        5.0 — 7.6 Wh
```

## Notes

- **Not a charge session**: No device pairing, no capacity learning
- **Independent operation**: Coexists with ChargeInsights
- **Low storage footprint**: Only aggregates, not raw curves
- **Privacy-friendly**: Discards detailed measurements
- **Real-time**: Updates on interval selection/trimming
- Related: [Charging Monitoring](./ChargingMonitoring.md) for full sessions
