Skip to content

Commit 67c5523

Browse files
authored
G7 End of session detection bugfix (#34)
* Continue with sensor even after auth without control msg * Add handling for failed sensor, and add more logs * Add additional failure types * Handle possible new state of session ended * Additional logs * Check for connection event, for cases where iOS fails to call didDiscover * Use remote disconnect without auth/data as end-of-session detection again.
1 parent 2be3eb2 commit 67c5523

File tree

7 files changed

+77
-23
lines changed

7 files changed

+77
-23
lines changed

G7SensorKit/AlgorithmError.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ extension AlgorithmState {
3737
return LocalizedString("Sensor is OK", comment: "The description of sensor algorithm state when sensor is ok.")
3838
case .stopped:
3939
return LocalizedString("Sensor is stopped", comment: "The description of sensor algorithm state when sensor is stopped.")
40-
case .warmup, .questionMarks:
40+
case .warmup, .temporarySensorIssue:
4141
return LocalizedString("Sensor is warming up", comment: "The description of sensor algorithm state when sensor is warming up.")
4242
case .expired:
4343
return LocalizedString("Sensor expired", comment: "The description of sensor algorithm state when sensor is expired.")
4444
case .sensorFailed:
4545
return LocalizedString("Sensor failed", comment: "The description of sensor algorithm state when sensor failed.")
46+
default:
47+
return "Sensor state: \(String(describing: state))"
4648
}
4749
case .unknown(let rawValue):
4850
return String(format: LocalizedString("Sensor is in unknown state %1$d", comment: "The description of sensor algorithm state when raw value is unknown. (1: missing data details)"), rawValue)

G7SensorKit/AlgorithmState.swift

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,29 @@ public enum AlgorithmState: RawRepresentable {
1515
public enum State: RawValue {
1616
case stopped = 1
1717
case warmup = 2
18+
case excessNoise = 3
19+
case firstOfTwoBGsNeeded = 4
20+
case secondOfTwoBGsNeeded = 5
1821
case ok = 6
19-
case questionMarks = 18
22+
case needsCalibration = 7
23+
case calibrationError1 = 8
24+
case calibrationError2 = 9
25+
case calibrationLinearityFitFailure = 10
26+
case sensorFailedDuetoCountsAberration = 11
27+
case sensorFailedDuetoResidualAberration = 12
28+
case outOfCalibrationDueToOutlier = 13
29+
case outlierCalibrationRequest = 14
30+
case sessionExpired = 15
31+
case sessionFailedDueToUnrecoverableError = 16
32+
case sessionFailedDueToTransmitterError = 17
33+
case temporarySensorIssue = 18
34+
case sensorFailedDueToProgressiveSensorDecline = 19
35+
case sensorFailedDueToHighCountsAberration = 20
36+
case sensorFailedDueToLowCountsAberration = 21
37+
case sensorFailedDueToRestart = 22
2038
case expired = 24
2139
case sensorFailed = 25
40+
case sessionEnded = 26
2241
}
2342

2443
case known(State)
@@ -48,7 +67,7 @@ public enum AlgorithmState: RawRepresentable {
4867
}
4968

5069
switch state {
51-
case .sensorFailed:
70+
case .sensorFailed, .sensorFailedDuetoCountsAberration, .sensorFailedDuetoResidualAberration, .sessionFailedDueToTransmitterError, .sessionFailedDueToUnrecoverableError, .sensorFailedDueToProgressiveSensorDecline, .sensorFailedDueToHighCountsAberration, .sensorFailedDueToLowCountsAberration, .sensorFailedDueToRestart:
5271
return true
5372
default:
5473
return false
@@ -68,13 +87,13 @@ public enum AlgorithmState: RawRepresentable {
6887
}
6988
}
7089

71-
public var isInSensorError: Bool {
90+
public var hasTemporaryError: Bool {
7291
guard case .known(let state) = self else {
7392
return false
7493
}
7594

7695
switch state {
77-
case .questionMarks:
96+
case .temporarySensorIssue:
7897
return true
7998
default:
8099
return false
@@ -88,14 +107,10 @@ public enum AlgorithmState: RawRepresentable {
88107
}
89108

90109
switch state {
91-
case .stopped,
92-
.warmup,
93-
.questionMarks,
94-
.expired,
95-
.sensorFailed:
96-
return false
97110
case .ok:
98111
return true
112+
default:
113+
return false
99114
}
100115
}
101116
}

G7SensorKit/G7CGMManager/G7BluetoothManager.swift

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class G7BluetoothManager: NSObject {
156156

157157
private func managerQueue_stopScanning() {
158158
if centralManager.isScanning {
159-
log.debug("Stopping scan")
159+
log.default("Stopping scan")
160160
centralManager.stopScan()
161161
delegate?.bluetoothManagerScanningStatusDidChange(self)
162162
}
@@ -167,7 +167,7 @@ class G7BluetoothManager: NSObject {
167167

168168
managerQueue.sync {
169169
if centralManager.isScanning {
170-
log.debug("Stopping scan on disconnect")
170+
log.default("Stopping scan on disconnect")
171171
centralManager.stopScan()
172172
delegate?.bluetoothManagerScanningStatusDidChange(self)
173173
}
@@ -178,6 +178,15 @@ class G7BluetoothManager: NSObject {
178178
}
179179
}
180180

181+
func centralManager(_ central: CBCentralManager, connectionEventDidOccur event: CBConnectionEvent, for peripheral: CBPeripheral) {
182+
managerQueue.async {
183+
if self.activePeripheralIdentifier == nil {
184+
self.log.default("Discovered peripheral from connectionEventDidOccur %{public}@", peripheral.identifier.uuidString)
185+
self.handleDiscoveredPeripheral(peripheral)
186+
}
187+
}
188+
}
189+
181190
private func managerQueue_scanForPeripheral() {
182191
dispatchPrecondition(condition: .onQueue(managerQueue))
183192

@@ -191,19 +200,26 @@ class G7BluetoothManager: NSObject {
191200
}
192201

193202
if let peripheralID = activePeripheralIdentifier, let peripheral = centralManager.retrievePeripherals(withIdentifiers: [peripheralID]).first {
194-
log.debug("Retrieved peripheral %{public}@", peripheral.identifier.uuidString)
203+
log.default("Retrieved peripheral %{public}@", peripheral.identifier.uuidString)
195204
handleDiscoveredPeripheral(peripheral)
196205
} else {
197206
for peripheral in centralManager.retrieveConnectedPeripherals(withServices: [
198207
SensorServiceUUID.advertisement.cbUUID,
199208
SensorServiceUUID.cgmService.cbUUID
200209
]) {
210+
log.default("Found system-connected peripheral: %{public}@", peripheral.identifier.uuidString)
201211
handleDiscoveredPeripheral(peripheral)
202212
}
203213
}
204214

205215
if activePeripheral == nil {
206-
log.debug("Scanning for peripherals")
216+
log.default("Scanning for peripherals and listening for connection events")
217+
218+
centralManager.registerForConnectionEvents(options: [CBConnectionEventMatchingOption.serviceUUIDs: [
219+
SensorServiceUUID.advertisement.cbUUID,
220+
SensorServiceUUID.cgmService.cbUUID
221+
]])
222+
207223
centralManager.scanForPeripherals(withServices: [
208224
SensorServiceUUID.advertisement.cbUUID
209225
],
@@ -257,7 +273,7 @@ class G7BluetoothManager: NSObject {
257273
if let delegate = delegate {
258274
switch delegate.bluetoothManager(self, shouldConnectPeripheral: peripheral) {
259275
case .makeActive:
260-
log.debug("Making peripheral active: %{public}@", peripheral.identifier.uuidString)
276+
log.default("Making peripheral active: %{public}@", peripheral.identifier.uuidString)
261277

262278
if let peripheralManager = activePeripheralManager {
263279
peripheralManager.peripheral = peripheral
@@ -273,7 +289,7 @@ class G7BluetoothManager: NSObject {
273289
self.centralManager.connect(peripheral)
274290

275291
case .connect:
276-
log.debug("Connecting to peripheral: %{public}@", peripheral.identifier.uuidString)
292+
log.default("Connecting to peripheral: %{public}@", peripheral.identifier.uuidString)
277293
self.centralManager.connect(peripheral)
278294
let peripheralManager = G7PeripheralManager(
279295
peripheral: peripheral,
@@ -311,7 +327,7 @@ extension G7BluetoothManager: CBCentralManagerDelegate {
311327
fallthrough
312328
@unknown default:
313329
if central.isScanning {
314-
log.debug("Stopping scan on central not powered on")
330+
log.default("Stopping scan on central not powered on")
315331
central.stopScan()
316332
delegate?.bluetoothManagerScanningStatusDidChange(self)
317333
}
@@ -332,7 +348,7 @@ extension G7BluetoothManager: CBCentralManagerDelegate {
332348
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
333349
dispatchPrecondition(condition: .onQueue(managerQueue))
334350

335-
log.info("%{public}@: %{public}@, data = %{public}@", #function, peripheral, String(describing: advertisementData))
351+
log.default("%{public}@: %{public}@, data = %{public}@", #function, peripheral, String(describing: advertisementData))
336352

337353
managerQueue.async {
338354
self.handleDiscoveredPeripheral(peripheral)

G7SensorKit/G7CGMManager/G7CGMManager.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,11 @@ extension G7CGMManager: G7SensorDelegate {
323323
}
324324
}
325325

326+
public func sensor(_ sensor: G7Sensor, logComms comms: String) {
327+
logDeviceCommunication("Sensor comms \(comms)", type: .receive)
328+
}
329+
330+
326331
public func sensor(_ sensor: G7Sensor, didError error: Error) {
327332
logDeviceCommunication("Sensor error \(error)", type: .error)
328333
}
@@ -335,6 +340,17 @@ extension G7CGMManager: G7SensorDelegate {
335340
return
336341
}
337342

343+
if message.algorithmState.sensorFailed {
344+
logDeviceCommunication("Detected failed sensor... scanning for new sensor.", type: .receive)
345+
scanForNewSensor()
346+
}
347+
348+
if message.algorithmState == .known(.sessionEnded) {
349+
logDeviceCommunication("Detected session ended... scanning for new sensor.", type: .receive)
350+
scanForNewSensor()
351+
}
352+
353+
338354
guard let activationDate = sensor.activationDate else {
339355
logDeviceCommunication("Unable to process sensor reading without activation date.", type: .error)
340356
return

G7SensorKit/G7CGMManager/G7Sensor.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public protocol G7SensorDelegate: AnyObject {
1919

2020
func sensor(_ sensor: G7Sensor, didError error: Error)
2121

22+
func sensor(_ sensor: G7Sensor, logComms comms: String)
23+
2224
func sensor(_ sensor: G7Sensor, didRead glucose: G7GlucoseMessage)
2325

2426
func sensor(_ sensor: G7Sensor, didReadBackfill backfill: [G7BackfillMessage])
@@ -194,7 +196,9 @@ public final class G7Sensor: G7BluetoothManagerDelegate {
194196
if let sensorID = sensorID, sensorID == peripheralManager.peripheral.name {
195197

196198
let suspectedEndOfSession: Bool
197-
if pendingAuth && wasRemoteDisconnect {
199+
200+
self.log.info("Sensor disconnected: wasRemoteDisconnect:%{public}@", String(describing: wasRemoteDisconnect))
201+
if pendingAuth, wasRemoteDisconnect {
198202
suspectedEndOfSession = true // Normal disconnect without auth is likely that G7 app stopped this session
199203
} else {
200204
suspectedEndOfSession = false
@@ -233,7 +237,7 @@ public final class G7Sensor: G7BluetoothManagerDelegate {
233237

234238
guard response.count > 0 else { return }
235239

236-
log.debug("Received control response: %{public}@", response.hexadecimalString)
240+
log.default("Received control response: %{public}@", response.hexadecimalString)
237241

238242
switch G7Opcode(rawValue: response[0]) {
239243
case .glucoseTx?:
@@ -252,7 +256,7 @@ public final class G7Sensor: G7BluetoothManagerDelegate {
252256
}
253257
}
254258
default:
255-
// We ignore all other known opcodes
259+
self.delegate?.sensor(self, logComms: response.hexadecimalString)
256260
break
257261
}
258262
}

G7SensorKit/Messages/G7Opcode.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Foundation
1010

1111
enum G7Opcode: UInt8 {
1212
case authChallengeRx = 0x05
13+
case sessionStopTx = 0x28
1314
case glucoseTx = 0x4e
1415
case backfillFinished = 0x59
1516
}

G7SensorKitUI/G7CGMManager/G7CGMManager+UI.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ extension G7CGMManager: CGMManagerUI {
7474
state: .warning)
7575
}
7676

77-
if let latestReading = latestReading, latestReading.algorithmState.isInSensorError {
77+
if let latestReading = latestReading, latestReading.algorithmState.hasTemporaryError {
7878
return G7DeviceStatusHighlight(
7979
localizedMessage: LocalizedString("Sensor\nIssue", comment: "G7 Status highlight text for sensor error"),
8080
imageName: "exclamationmark.circle.fill",

0 commit comments

Comments
 (0)