Newer Older
147 lines | 4.914kb
Bogdan Timofte authored 2 weeks ago
1
# ChargedDevice Entity
2

            
3
Reprezentarea unui dispozitiv care se încarcă prin meter.
4

            
5
## Responsabilități
6

            
7
- Modelarea unui dispozitiv (iPhone, Watch, Charger, Powerbank, Other)
8
- Gestionarea sesiunilor de încărcare
9
- Colectarea de măsurători durante sesiune
10
- Calculul integrității şi detectării conflictelor
11

            
12
## Invarianţi
13

            
14
- **MUST**: Fiecare ChargedDevice are un `id` (UUID) unic
15
- **MUST**: O sesiune de încărcare are timestamp-uri valide: `startTime <= currentTime <= endTime`
16
- **MUST**: Măsurătorile într-o sesiune sunt ordonate cronologic
17
- **MUST**: O sesiune activă pe un dispozitiv trebuie să aibă un singur Meter asociat
18
- **MUST**: Energia calculată (Wh) trebuie să fie ne-negativă
19
- **SHOULD**: Un device cu 0 măsurători ar trebui marcat pentru curatare pe CloudKit
20
- **MAY**: Duplicate sessions pe același MAC pot fi mergate după detectare
21

            
22
## API Public
23

            
24
### Proprietăţi
25

            
26
| Proprietate | Tip | Descriere | Observaţii |
27
|---|---|---|---|
28
| `id` | UUID | Identificator unic | Generat la creare |
29
| `deviceName` | String | Nume ales de utilizator | De ex: "My iPhone 15" |
30
| `deviceClass` | ChargedDeviceClass | Tip: iPhone, Watch, Charger, Powerbank, Other | |
31
| `kind` | ChargedDeviceKind | Device sau Charger | Derivat din `deviceClass` |
32
| `createdAt` | Date | Data creării | Immutable |
33
| `chargeRecords` | [ChargeRecord] | Lista sesiunilor de încărcare | Sorted by startTime |
34
| `totalEnergy` | Double | Total Wh consumat/furnizat | Calculat din sesiuni |
35
| `lastChargedAt` | Date? | Data ultimei sesiuni | Nullable |
36

            
37
### Metode
38

            
39
```swift
40
// Gestionare sesiuni
41
func startSession(meter: Meter) -> ChargeRecord
42
// MUST: sessionID este UUID unic
43
// MUST: startTime = now()
44
// MUST: retur ChargeRecord cu status "active"
45

            
46
func recordMeasurement(_ measurement: Measurement, in session: ChargeRecord)
47
// MUST: measurement.timestamp > session.startTime
48
// SHOULD: measure-urile sunt colectate la ~1Hz
49
// MUST: nu merge dacă sesiunea e finalizată
50

            
51
func endSession(_ record: ChargeRecord)
52
// MUST: endTime = now()
53
// MUST: calculează total energy din măsurători
54
// MUST: marchez sesiunea ca "completed"
55

            
56
func recalculateTotalEnergy()
57
// Recalculează sum-ul de Wh din toate sesiunile
58
// SHOULD: se apelează după conflict resolution
59

            
60
// Naming
61
func renameToDevice(_ newName: String)
62
// MUST: actualizează proprietatea şi persistă
63

            
64
// Conflict handling
65
func mergeWith(_ other: ChargedDevice, keepingRecords: Bool = true)
66
// MUST: combină sessions de pe ambele device-uri
67
// MUST: remove-ă duplicates după MAC address
68
// SHOULD: logs merge operation pentru debug
69
```
70

            
71
## Comportamente critice
72

            
73
### Sesiuni de încărcare
74

            
75
- **MUST**: Doar o singură sesiune per device poate fi activă la orice moment
76
- **MUST**: Sesiunile completate sunt imutable
77
- **SHOULD**: Sesiunile care durează > 24h sunt marcate cu warning
78
- **MAY**: Sesiuni orphaned (meter deconectat > 1h) pot fi finalizate automat
79

            
80
### Calculul energiei
81

            
82
- **MUST**: Energia = ∑(V * A * Δt) pentru fiecare interval
83
- **MUST**: Unitatea trebuie să fie Wh (Watt-hours)
84
- **SHOULD**: Energia negativă în sesiune = problema în măsurători, loghează warning
85

            
86
### Conflict detection și rezoluție
87

            
88
Se declanşează când:
89
1. Două sesiuni cu aceeași categorie de device în same time window
90
2. Două sesiuni pe aceeași meter cu overlap temporal
91

            
92
Rezoluţie:
93
- **MUST**: Sesiunea cu mai multe măsurători = "winner"
94
- **MUST**: Energiile sunt combinate (∑)
95
- **SHOULD**: Old session data este archived (Core Data snapshot)
96
- **MAY**: Notifică utilizator despre merge
97

            
98
### State management
99

            
100
- **MUST**: Active session nu poate fi schimbat în mod normal (read-only)
101
- **MUST**: State tranzițion: idle → active → completed (nu poate reveni)
102
- **SHOULD**: Completed sesiuni sunt persistent în Core Data
103

            
104
## Testare
105

            
106
### Unit tests
107

            
108
```swift
109
// Sesiuni
110
test_startSessionCreatesValidRecord()
111
test_endSessionCalculatesEnergy()
112
test_activeSessions_CanOnlyBeOne()
113
test_sessionTimeValidation()
114

            
115
// Măsurători
116
test_recordMeasurementFailsIfSessionEnded()
117
test_measurementsAreOrdered()
118
test_emptySessionsCalculateZeroEnergy()
119

            
120
// Conflict
121
test_mergeDetectsDuplicateSessions()
122
test_mergeKeepsMoreCompleteMeasurements()
123
test_mergeRecalculates_TotalEnergy()
124

            
125
// Naming
126
test_renameUpdatesPersistence()
127
```
128

            
129
### Integration tests
130

            
131
- [ ] Device apare în Charged Devices list după start sesiune
132
- [ ] Sesiuni complete salvate în Core Data
133
- [ ] CloudKit sync nu pierde sesiuni
134
- [ ] Energiile sunt consistente după conflict merge
135
- [ ] Device name e persistent
136

            
137
## Dependenţe
138

            
139
- `Meter`: conectare şi măsurători
140
- `ChargeInsightsStore`: persistență sesiuni
141
- `CloudKitSync`: replicare iCloud
142

            
143
## Notes
144

            
145
Documentaţie asoc:
146
- [Charge Session Integrity](../Charge%20Session%20Integrity%20and%20Conflict%20Healing.md)
147
- [No Ampere-Hours in UI](../No%20Ampere-Hours%20in%20UI%20or%20Model.md)