Skip to content

Commit 858fb2c

Browse files
committed
Merge branch 'main' into add_stelo_support
1 parent 6ee0cb4 commit 858fb2c

File tree

26 files changed

+201
-111
lines changed

26 files changed

+201
-111
lines changed

G7SensorKit.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
3B0FD2A52D803BF100E5E921 /* LoopKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B0FD2A42D803BF000E5E921 /* LoopKitUI.framework */; };
1011
6515DB522E695F77005C42DC /* G7SensorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6515DB512E695F77005C42DC /* G7SensorType.swift */; };
1112
B60BB2E42BC649DA00D2BB39 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B60BB2E32BC649DA00D2BB39 /* Bundle.swift */; };
1213
C109F14A291ECCE2008EA5B6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C109F149291ECCE2008EA5B6 /* Assets.xcassets */; };
@@ -108,6 +109,7 @@
108109
/* End PBXCopyFilesBuildPhase section */
109110

110111
/* Begin PBXFileReference section */
112+
3B0FD2A42D803BF000E5E921 /* LoopKitUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LoopKitUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
111113
6515DB512E695F77005C42DC /* G7SensorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = G7SensorType.swift; sourceTree = "<group>"; };
112114
B60BB2E32BC649DA00D2BB39 /* Bundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Bundle.swift; path = G7SensorKitUI/Extensions/Bundle.swift; sourceTree = SOURCE_ROOT; };
113115
C1086B0E29C9169100D46E65 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -206,6 +208,7 @@
206208
isa = PBXFrameworksBuildPhase;
207209
buildActionMask = 2147483647;
208210
files = (
211+
3B0FD2A52D803BF100E5E921 /* LoopKitUI.framework in Frameworks */,
209212
);
210213
runOnlyForDeploymentPostprocessing = 0;
211214
};
@@ -341,6 +344,7 @@
341344
C17F5128291EAFA100555EB5 /* Frameworks */ = {
342345
isa = PBXGroup;
343346
children = (
347+
3B0FD2A42D803BF000E5E921 /* LoopKitUI.framework */,
344348
);
345349
name = Frameworks;
346350
sourceTree = "<group>";

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/G7BackfillMessage.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,19 @@ public struct G7BackfillMessage: Equatable {
3232
return nil
3333
}
3434

35-
timestamp = data[0..<4].toInt()
35+
36+
timestamp = data[0..<3].toInt()
3637

3738
let glucoseBytes = data[4..<6].to(UInt16.self)
3839

3940
if glucoseBytes != 0xffff {
4041
glucose = glucoseBytes & 0xfff
41-
glucoseIsDisplayOnly = (glucoseBytes & 0xf000) > 0
4242
} else {
4343
glucose = nil
44-
glucoseIsDisplayOnly = false
4544
}
4645

46+
glucoseIsDisplayOnly = data[7] & 0x10 != 0
47+
4748
algorithmState = AlgorithmState(rawValue: data[6])
4849

4950
if data[8] == 0x7f {

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
@@ -327,6 +327,11 @@ extension G7CGMManager: G7SensorDelegate {
327327
}
328328
}
329329

330+
public func sensor(_ sensor: G7Sensor, logComms comms: String) {
331+
logDeviceCommunication("Sensor comms \(comms)", type: .receive)
332+
}
333+
334+
330335
public func sensor(_ sensor: G7Sensor, didError error: Error) {
331336
logDeviceCommunication("Sensor error \(error)", type: .error)
332337
}
@@ -339,6 +344,17 @@ extension G7CGMManager: G7SensorDelegate {
339344
return
340345
}
341346

347+
if message.algorithmState.sensorFailed {
348+
logDeviceCommunication("Detected failed sensor... scanning for new sensor.", type: .receive)
349+
scanForNewSensor()
350+
}
351+
352+
if message.algorithmState == .known(.sessionEnded) {
353+
logDeviceCommunication("Detected session ended... scanning for new sensor.", type: .receive)
354+
scanForNewSensor()
355+
}
356+
357+
342358
guard let activationDate = sensor.activationDate else {
343359
logDeviceCommunication("Unable to process sensor reading without activation date.", type: .error)
344360
return

G7SensorKit/G7CGMManager/G7Sensor.swift

Lines changed: 21 additions & 9 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])
@@ -197,8 +199,13 @@ public final class G7Sensor: G7BluetoothManagerDelegate {
197199
func peripheralDidDisconnect(_ manager: G7BluetoothManager, peripheralManager: G7PeripheralManager, wasRemoteDisconnect: Bool) {
198200
if let sensorID = sensorID, sensorID == peripheralManager.peripheral.name {
199201

202+
// Sometimes we do not receive the backfillFinished message before disconnect
203+
flushBackfillBuffer()
204+
200205
let suspectedEndOfSession: Bool
201-
if pendingAuth && wasRemoteDisconnect {
206+
207+
self.log.info("Sensor disconnected: wasRemoteDisconnect:%{public}@", String(describing: wasRemoteDisconnect))
208+
if pendingAuth, wasRemoteDisconnect {
202209
suspectedEndOfSession = true // Normal disconnect without auth is likely that G7 app stopped this session
203210
} else {
204211
suspectedEndOfSession = false
@@ -241,7 +248,7 @@ public final class G7Sensor: G7BluetoothManagerDelegate {
241248

242249
guard response.count > 0 else { return }
243250

244-
log.debug("Received control response: %{public}@", response.hexadecimalString)
251+
log.default("Received control response: %{public}@", response.hexadecimalString)
245252

246253
switch G7Opcode(rawValue: response[0]) {
247254
case .glucoseTx?:
@@ -253,18 +260,23 @@ public final class G7Sensor: G7BluetoothManagerDelegate {
253260
}
254261
}
255262
case .backfillFinished:
256-
if backfillBuffer.count > 0 {
257-
delegateQueue.async {
258-
self.delegate?.sensor(self, didReadBackfill: self.backfillBuffer)
259-
self.backfillBuffer = []
260-
}
261-
}
263+
flushBackfillBuffer()
262264
default:
263-
// We ignore all other known opcodes
265+
self.delegate?.sensor(self, logComms: response.hexadecimalString)
264266
break
265267
}
266268
}
267269

270+
func flushBackfillBuffer() {
271+
if backfillBuffer.count > 0 {
272+
let backfill = backfillBuffer
273+
self.backfillBuffer = []
274+
delegateQueue.async {
275+
self.delegate?.sensor(self, didReadBackfill: backfill)
276+
}
277+
}
278+
}
279+
268280
func bluetoothManager(_ manager: G7BluetoothManager, didReceiveBackfillResponse response: Data) {
269281

270282
log.debug("Received backfill response: %{public}@", response.hexadecimalString)

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
}

G7SensorKit/it.lproj/Localizable.strings

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@
88
"Sensor expired" = "Sensore scaduto";
99

1010
/* The description of sensor algorithm state when sensor failed. */
11-
"Sensor failed" = "Sensore Fallito";
11+
"Sensor failed" = "Sensore guasto";
1212

1313
/* The description of sensor algorithm state when raw value is unknown. (1: missing data details) */
14-
"Sensor is in unknown state %1$d" = "Il Sensore e' in un stato%1$d sconosciuto";
14+
"Sensor is in unknown state %1$d" = "Il sensore è in stato sconosciuto %1$d";
1515

1616
/* The description of sensor algorithm state when sensor is ok. */
17-
"Sensor is OK" = "Sensore è OK";
17+
"Sensor is OK" = "Il sensore è OK";
1818

1919
/* The description of sensor algorithm state when sensor is stopped. */
20-
"Sensor is stopped" = "Il Sensore e' fermato";
20+
"Sensor is stopped" = "Il sensore è arrestato";
2121

2222
/* The description of sensor algorithm state when sensor is warming up. */
2323
"Sensor is warming up" = "Il Sensore si sta riscaldando";

G7SensorKitTests/G7GlucoseMessageTests.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,23 @@ final class G7GlucoseMessageTests: XCTestCase {
131131
let data = Data(hexadecimalString: "cf5802008f00060f10")!
132132
let message = G7BackfillMessage(data: data)!
133133
XCTAssertEqual(153807, message.timestamp)
134+
XCTAssertEqual(143, message.glucose)
135+
XCTAssertEqual(.known(.ok), message.algorithmState)
136+
XCTAssertNil(message.condition)
137+
XCTAssertEqual(false, message.glucoseIsDisplayOnly)
138+
XCTAssertEqual(true, message.hasReliableGlucose)
139+
}
140+
141+
func testBackfillTimestampWithHighByte() {
142+
let data = Data(hexadecimalString: "f20e0d00ba00060ffb")!
143+
let message = G7BackfillMessage(data: data)!
144+
XCTAssertEqual(855794, message.timestamp)
145+
}
146+
147+
func testBackfillCalibration() {
148+
let data = Data(hexadecimalString: "f63d00008500061efe")!
149+
let message = G7BackfillMessage(data: data)!
150+
XCTAssertEqual(true, message.glucoseIsDisplayOnly)
134151
}
135152

136153
}

0 commit comments

Comments
 (0)