Newer Older
152 lines | 4.726kb
Bogdan Timofte authored 2 weeks ago
1
# Meter Entity
2

            
3
Reprezentarea unui contor Bluetooth conectat (UM25C, UM34C, TC66C).
4

            
5
## Responsabilități
6

            
7
- Modelarea unui dispozitiv USB power meter BT
8
- Gestionarea stării de conectare şi comunicare
9
- Citirea măsurătorilor de la dispozitiv
10
- Persistență metadate conector (device, timestamp, expiry)
11

            
12
## Invarianți
13

            
14
- **MUST**: Un Meter are un `id` (UUID) unic în aplicaţie
15
- **MUST**: Un Meter cu `macAddress` nu poate fi duplicat în Core Data (Cloud-Kit safe)
16
- **MUST**: Starea `OperationalState` este monoton crescătoare: `notPresent → ... → dataIsAvailable`
17
- **MUST**: Dacă `OperationalState` este `peripheralConnected` sau mai mare, trebuie să existe o conexiune BT activă
18
- **SHOULD**: Un Meter inactiv (fără măsurători > 2 ore) ar trebui să se reconecteze automat
19
- **MAY**: Temperature unit preference (Celsius/Fahrenheit) poate fi schimbat oricând
20

            
21
## Estados operaţionale
22

            
23
```
24
notPresent
25
  ↓
26
peripheralNotConnected
27
  ↓
28
peripheralConnectionPending
29
  ↓
30
peripheralConnected
31
  ↓
32
peripheralReady
33
  ↓
34
comunicating ↔ dataIsAvailable
35
```
36

            
37
## API Public
38

            
39
### Proprietăţi
40

            
41
| Proprietate | Tip | Descriere | Observaţii |
42
|---|---|---|---|
43
| `id` | UUID | Identificator unic | Generat la creare |
44
| `macAddress` | String | Adresa MAC BT | De ex: "AA:BB:CC:DD:EE:FF" |
45
| `meter` | Model | Tipul: UM25C, UM34C, TC66C | Immutable după creare |
46
| `meterName` | String | Nume ales de utilizator | Persistent în Core Data |
47
| `operationalState` | OperationalState | Starea curentă | Published, triggers UI updates |
48
| `lastDataPoint` | Measurement? | Ultima măsurătoare | Nullable |
49
| `connectionMetadata` | ConnectionMetadata? | Info conexiune curentă | Device, timestamp, expiry |
50
| `discoveryMetadata` | DiscoveryMetadata? | Info descoperire BT | Last seen, seen by |
51
| `temperatureUnit` | TemperatureUnitPreference | Unitatei temperatură | Celsius/Fahrenheit |
52

            
53
### Metode
54

            
55
```swift
56
// Conectare
57
func connect()
58
// MUST: tranzițe starea la peripheralConnectionPending
59
// SHOULD: se conectează în max 5s
60

            
61
func disconnect()
62
// MUST: tranzițe starea la peripheralNotConnected
63
// MUST: eliberează resurse BT
64

            
65
// Citire măsurători
66
func requestMeasurement() -> Bool
67
// SHOULD: retur true dacă cererea a fost trimisă
68
// SHOULD: se așteaptă răspuns în max 3s (timeout)
69
// MUST: nu trimite dacă starea este < comunicating
70

            
71
func processMeasurementData(_ data: Data) -> Measurement?
72
// Parsează payload-ul de la dispozitiv
73
// MUST: retur nil dacă payload-ul e invalid
74
// SHOULD: loghează checksum errors
75

            
76
// Gestionare sesiuni
77
func startChargeRecord(for device: ChargedDevice) -> ChargeRecord
78
// Inițiază o nouă sesiune de încărcare
79

            
80
func endChargeRecord(_ record: ChargeRecord)
81
// Finalizează sesiunea
82

            
83
// Naming
84
func renameToMeter(_ newName: String)
85
// MUST: actualizează proprietatea şi persistă în Core Data
86
```
87

            
88
## Comportamente critice
89

            
90
### Reconexiune automată
91

            
92
- **MUST**: Dacă `OperationalState < peripheralConnected`, retry-ează conectarea
93
- **SHOULD**: Backoff exponential: 1s, 2s, 4s, 8s, max 60s
94
- **SHOULD**: Anulează retry-urile dacă utilizatorul deconectează manual
95

            
96
### Timeout pe măsurători
97

            
98
- **MUST**: Dacă nu primim răspuns în 3s după cerere, timeout-ul măsurătorii
99
- **SHOULD**: Loghează timeout-urile pentru debugging
100
- **MAY**: Incrementează contorul de failed requests
101

            
102
### Stare după disconnecţie accidentală
103

            
104
- **MUST**: Dacă BT drop-ă accidental, starea revine la `peripheralNotConnected`
105
- **SHOULD**: Încearcă reconexiune automată (backoff)
106
- **MAY**: Notifică UI-ul cu banner "Meter disconnected"
107

            
108
### Validare MAC address
109

            
110
- **MUST**: MAC address trebuie să fie format valid (XX:XX:XX:XX:XX:XX)
111
- **MUST**: Dacă MAC e invalid, Meter nu poate fi creat
112

            
113
## Testare
114

            
115
### Unit tests
116

            
117
```swift
118
// Stări operaţionale
119
test_operationalStateTransition()
120
test_stateMonotonicity()
121

            
122
// Conectare
123
test_connectInitiatesPeripheralConnectionPending()
124
test_disconnectCleansUpResources()
125
test_reconnectWithBackoff()
126

            
127
// Măsurători
128
test_requestMeasurementFailsIfStateInvalid()
129
test_processMeasurementDataWithValidPayload()
130
test_measurementTimeout()
131

            
132
// Naming
133
test_renameUpdatesPersistence()
134
```
135

            
136
### Integration tests
137

            
138
- [ ] Meter apare în Sidebar după scan
139
- [ ] Meter se reconectează după BT drop
140
- [ ] Măsurătorile se salvează pentru ChargedDevice
141
- [ ] Temperature unit e persistent între app restarts
142

            
143
## Dependenţe
144

            
145
- `BluetoothManager`: gestionează Core Bluetooth
146
- `ChargeInsightsStore`: salvează măsurătorile
147
- `AppData`: CloudKit sync
148

            
149
## Note
150

            
151
- Legat: [CloudKit Sync](./CloudKitSync.md) (persistență MAC, name)
152
- Legat: [Bluetooth Discovery](./BluetoothDiscovery.md) (scan, advertisement)