Măsurarea consumului pe o perioadă arbitrară, cu statistici şi predicţii bazate pe interval selectat.
| 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 |
Energy (Wh) = ∫ P(t) dt
= ∑ (V × A × Δt)
Power (W) = V × A (instantaneous)
Average Power (W) = Total Energy / Duration
startTime < endTime (interval valid)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)
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()
)
}
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
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
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
// 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?
// Predict next interval
func predictConsumption(
for duration: TimeInterval
) -> ConsumptionPrediction?
// Extrapolate to full day/week
func extrapolateToDay() -> ConsumptionPrediction?
func extrapolateToWeek() -> ConsumptionPrediction?
// 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
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
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
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"
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%)
| 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)) |
| 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 |
Measured: P_avg = 10W over 1 hour
Predict next 1h: E = 10W × 1h = 10Wh
Bounds: ±(1-confidence)% = ±20% = [8Wh, 12Wh]
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%
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()
User selects: 10:00:00 - 10:00:05 (5 seconds)
⟹ Error: below 10s minimum
MUST: Reject
User selects: 01-Jan - 31-Dec (365 days)
⟹ Warning: "Extrapolation unreliable beyond 7 days"
Confidence: 20%
SHOULD: Accept but warn SHOULD: Reduce confidence
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
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"
{
"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"
}
❌ individual measurements
❌ voltage samples
❌ current samples
❌ power curve
❌ battery levels
❌ meter metadata
┌─────────────────────────────────┐
│ Power (W) │
│ 20 ├──────┐ │
│ │ ████ │ ← user selection │
│ 10 ├──████└────┐ │
│ │ │ │
│ 0 └──────────┴───────────────┘
│ 10:00 10:30 11:00
│ ↑start ↑end
│ (draggable) (draggable)
└─────────────────────────────────┘
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