Monitorizarea unei sesiuni de încărcare: colectarea măsurătorilor, izolarea curbei şi calculul energiei.
idle
↓
startSession() → starting
↓
recordMeasurement() → active (repeat)
↓
endSession() → ended
↓
completed (immutable)
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
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ă
meter.endChargeRecord(session)
MUST:
- endTime = Date.now
- Calculate totalEnergy = ∑(V × A × Δt) pentru fiecare interval
- Call Charge Curve Isolation 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ă)
| 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 |
// 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?
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
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
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
// 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()
meter.lastDataPoint, stateChargeSession entityE = ∑(V × A × Δt) (integral numerical)