Droplet-1

This commit is contained in:
Johan Degraeve 2019-08-02 23:03:27 +02:00
parent 705fa98b8b
commit 032425f733
8 changed files with 192 additions and 3 deletions

View File

@ -181,6 +181,7 @@
F8BDD455221DEF22006EAB84 /* SettingsViews.strings in Resources */ = {isa = PBXBuildFile; fileRef = F8BDD457221DEF22006EAB84 /* SettingsViews.strings */; };
F8C5EBE522F297F000563B5F /* SensorSerialNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C5EBE422F297EF00563B5F /* SensorSerialNumber.swift */; };
F8C5EBE722F38F0E00563B5F /* Trace.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C5EBE622F38F0E00563B5F /* Trace.swift */; };
F8C5EBEA22F49AC700563B5F /* CGMDroplet1Transmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C5EBE922F49AC700563B5F /* CGMDroplet1Transmitter.swift */; };
F8E3C3AB21FE17B700907A04 /* StringProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E3C3AA21FE17B700907A04 /* StringProtocol.swift */; };
F8E3C3AD21FE551C00907A04 /* DexcomCalibrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E3C3AC21FE551C00907A04 /* DexcomCalibrator.swift */; };
F8EA6C8221B723BC0082976B /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8EA6C8121B723BC0082976B /* Date.swift */; };
@ -481,6 +482,7 @@
F8BDD458221DEF24006EAB84 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/SettingsViews.strings; sourceTree = "<group>"; };
F8C5EBE422F297EF00563B5F /* SensorSerialNumber.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SensorSerialNumber.swift; sourceTree = "<group>"; };
F8C5EBE622F38F0E00563B5F /* Trace.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Trace.swift; sourceTree = "<group>"; };
F8C5EBE922F49AC700563B5F /* CGMDroplet1Transmitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGMDroplet1Transmitter.swift; sourceTree = "<group>"; };
F8E3C3AA21FE17B700907A04 /* StringProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringProtocol.swift; sourceTree = "<group>"; };
F8E3C3AC21FE551C00907A04 /* DexcomCalibrator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomCalibrator.swift; sourceTree = "<group>"; };
F8EA6C8121B723BC0082976B /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = "<group>"; };
@ -825,6 +827,7 @@
F8A54AEC22D9156600934E7A /* Libre */ = {
isa = PBXGroup;
children = (
F8C5EBE822F49AB300563B5F /* Droplet */,
EE9947E622EEDD0700DCB876 /* Bubble */,
F8A54AFB22D9179100934E7A /* Utilities */,
F8A54AED22D9156600934E7A /* GNSEntry */,
@ -1085,6 +1088,14 @@
path = Texts;
sourceTree = "<group>";
};
F8C5EBE822F49AB300563B5F /* Droplet */ = {
isa = PBXGroup;
children = (
F8C5EBE922F49AC700563B5F /* CGMDroplet1Transmitter.swift */,
);
path = Droplet;
sourceTree = "<group>";
};
F8EA6C7D21B70DEA0082976B /* Constants */ = {
isa = PBXGroup;
children = (
@ -1355,6 +1366,7 @@
F8B3A7FA2278E0E8004BA588 /* SettingsViewModelProtocol.swift in Sources */,
F85DC2F521CFE3D400B9F74A /* BgReading+CoreDataClass.swift in Sources */,
F8A54AEA22D911BA00934E7A /* PairRequestTxMessage.swift in Sources */,
F8C5EBEA22F49AC700563B5F /* CGMDroplet1Transmitter.swift in Sources */,
F821CF56229BF43A005C1E43 /* AlertKind.swift in Sources */,
F85DC2ED21CFE2F500B9F74A /* BgReading+CoreDataProperties.swift in Sources */,
F8A54AE422D911BA00934E7A /* NSData+CRC.swift in Sources */,

View File

@ -7,6 +7,7 @@ enum ConstantsDefaultAlertLevels {
static let defaultBatteryAlertLevelBubble = 20
static let defaultBatteryAlertLevelGNSEntry = 20
static let defaultBatteryAlertLevelBlucon = 20
static let defaultBatteryAlertLevelDroplet = 20
// blood glucose level alert values in mgdl
static let veryHigh = 250

View File

@ -48,5 +48,7 @@ enum ConstantsLog {
static let categorySettingsViewHealthKitSettingsViewModel = "categorySettingsViewHealthKitSettingsViewModel"
/// dexcom share upload manager
static let categoryDexcomShareUploadManager = "categoryDexcomShareUploadManager"
/// droplet 1
static let categoryCGMDroplet1 = "categoryCGMDroplet1"
}

View File

@ -67,6 +67,9 @@ enum CGMTransmitterType:String, CaseIterable {
/// Bubble
case Bubble = "Bubble"
/// Droplet
case Droplet1 = "Droplet-1"
/// does the transmitter need a transmitter id ?
///
/// can be used in UI stuff, if reset not possible then there's no need to show that option in the settings UI
@ -87,6 +90,10 @@ enum CGMTransmitterType:String, CaseIterable {
case .Blucon:
return true
case .Droplet1:
return false
}
}
@ -113,6 +120,10 @@ enum CGMTransmitterType:String, CaseIterable {
case .Blucon:
return true
case .Droplet1:
return false
}
}
@ -142,12 +153,13 @@ enum CGMTransmitterType:String, CaseIterable {
}
return nil
case .miaomiao, .GNSentry, .Bubble:
case .miaomiao, .GNSentry, .Bubble, .Droplet1:
return nil
case .Blucon:
// todo: validate transmitter id for blucon
return nil
}
}
@ -173,6 +185,9 @@ enum CGMTransmitterType:String, CaseIterable {
case .Blucon:
return ConstantsDefaultAlertLevels.defaultBatteryAlertLevelBlucon
case .Droplet1:
return ConstantsDefaultAlertLevels.defaultBatteryAlertLevelDroplet
}
}
@ -180,6 +195,7 @@ enum CGMTransmitterType:String, CaseIterable {
///
/// for this type of devices, there's no need to give an option in the UI to manually start scanning.
func startScanningAfterInit() -> Bool {
switch self {
case .dexcomG4:
@ -196,6 +212,10 @@ enum CGMTransmitterType:String, CaseIterable {
case .Blucon:
return true
case .Droplet1:
return false
}
}
@ -203,6 +223,7 @@ enum CGMTransmitterType:String, CaseIterable {
///
/// to be used for UI stuff
func batteryUnit() -> String {
switch self {
case .dexcomG4:
@ -211,7 +232,7 @@ enum CGMTransmitterType:String, CaseIterable {
case .dexcomG5, .dexcomG6:
return "voltA"
case .miaomiao, .Bubble:
case .miaomiao, .Bubble, .Droplet1:
return "%"
case .GNSentry:
@ -226,6 +247,7 @@ enum CGMTransmitterType:String, CaseIterable {
///
/// can be used in UI stuff, if reset not possible then there's no need to show that option in the settings UI
func resetPossible() -> Bool {
switch self {
case .dexcomG4:
@ -234,7 +256,7 @@ enum CGMTransmitterType:String, CaseIterable {
case .dexcomG5, .dexcomG6:
return true
case .miaomiao, .Bubble:
case .miaomiao, .Bubble, .Droplet1:
return false
case .GNSentry:

View File

@ -3,6 +3,7 @@ import CoreBluetooth
import os
class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTransmitter {
// MARK: - properties
/// service to be discovered

View File

@ -0,0 +1,143 @@
import Foundation
import os
import CoreBluetooth
class CGMDroplet1Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTransmitter {
// MARK: - properties
/// service to be discovered
let CBUUID_Service_Droplet: String = "C97433F0-BE8F-4DC8-B6F0-5343E6100EB4"
/// receive characteristic
let CBUUID_ReceiveCharacteristic_Droplet: String = "c97433f1-be8f-4dc8-b6f0-5343e6100eb4"
/// write characteristic
let CBUUID_WriteCharacteristic_Droplet: String = "c97433f2-be8f-4dc8-b6f0-5343e6100eb4"
/// will be used to pass back bluetooth and cgm related events
private(set) weak var cgmTransmitterDelegate: CGMTransmitterDelegate?
/// for trace
private let log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryCGMDroplet1)
/// used as parameter in call to cgmTransmitterDelegate.cgmTransmitterInfoReceived, when there's no glucosedata to send
var emptyArray: [RawGlucoseData] = []
// MARK: - Initialization
/// - parameters:
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
init(address:String?, delegate:CGMTransmitterDelegate) {
// assign addressname and name or expected devicename
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: "limitter")
if let address = address {
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
}
// assign CGMTransmitterDelegate
cgmTransmitterDelegate = delegate
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_Droplet)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_Droplet, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_Droplet, startScanningAfterInit: CGMTransmitterType.Droplet1.startScanningAfterInit())
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
bluetoothTransmitterDelegate = self
}
// MARK: - BluetoothTransmitterDelegate functions
func centralManagerDidConnect(address:String?, name:String?) {
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: address, name: name)
}
func centralManagerDidFailToConnect(error: Error?) {
}
func centralManagerDidUpdateState(state: CBManagerState) {
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: state)
}
func centralManagerDidDisconnectPeripheral(error: Error?) {
cgmTransmitterDelegate?.cgmTransmitterDidDisconnect()
}
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
}
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?) {
trace("in peripheral didUpdateValueFor", log: log, type: .info)
if let value = characteristic.value {
guard let valueAsString = String(bytes: value, encoding: .utf8) else {
trace(" failed to convert value to string", log: log, type: .error)
return
}
trace(" value = %{public}@", log: log, type: .info, valueAsString)
//find indexes of " "
var indexesOfSplitter = valueAsString.indexes(of: " ")
// length of indexesOfSplitter should be minimum 3 (there should be minimum 3 spaces)
guard indexesOfSplitter.count >= 3 else {
trace(" there's less than 3 spaces", log: log, type: .error)
return
}
// get first field
let firstField = String(valueAsString[valueAsString.startIndex..<indexesOfSplitter[0]])
// if firstfield equals "000000" or "000999" then sensor detection failure - inform delegate and return
guard firstField != "000000" && firstField != "000999" else {
cgmTransmitterDelegate?.sensorNotDetected()
return
}
// first field : first digits are rawvalue, to be multiplied with 100, last two digits are sensor type indicator, 10=L1, 20=L2, 30=US 14 day, 40=Lpro/h
let rawValueAsString = firstField[0..<(firstField.count - 2)] + "00"
let sensorTypeIndicator = firstField[(firstField.count - 2)..<firstField.count]
trace(" sensor type indicator = %{public}@", log: log, type: .info, rawValueAsString, sensorTypeIndicator)
// convert rawValueAsString to double and stop if this fails
guard let rawValueAsDouble = rawValueAsString.toDouble() else {
trace(" failed to convert rawValueAsString to double", log: log, type: .error)
return
}
// third field is battery percentage, stop if convert to Int fails
guard let batteryPercentage = Int(String(valueAsString[valueAsString.index(after: indexesOfSplitter[1])..<indexesOfSplitter[2]])) else {
trace(" failed to convert batteryPercentage field to Int", log: log, type: .error)
return
}
// fourth field is sensor time in minutes, stop if convert to Int fails
guard let sensorTimeInMinutes = Int(String(valueAsString[valueAsString.index(after: indexesOfSplitter[2])..<valueAsString.endIndex])) else {
trace(" failed to convert sensorTimeInMinutes field to Int", log: log, type: .error)
return
}
// send to delegate
var glucoseDataArray = [RawGlucoseData(timeStamp: Date(), glucoseLevelRaw: rawValueAsDouble)]
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorState: nil, sensorTimeInMinutes: sensorTimeInMinutes * 10, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
} else {
trace(" value is nil, no further processing", log: log, type: .error)
}
}
// MARK: CGMTransmitter protocol functions
/// to ask pairing - empty function because Bubble doesn't need pairing
///
/// this function is not implemented in BluetoothTransmitter.swift, otherwise it might be forgotten to look at in future CGMTransmitter developments
func initiatePairing() {}
/// to ask transmitter reset - empty function because Bubble doesn't support reset
///
/// this function is not implemented in BluetoothTransmitter.swift, otherwise it might be forgotten to look at in future CGMTransmitter developments
func reset(requested:Bool) {}
}

View File

@ -18,6 +18,10 @@ func debuglogging(_ logtext:String) {
/// - args : optional list of parameters that will be used. MAXIMUM 10 !
func trace(_ message: StaticString, log:OSLog, type:OSLogType, _ args: CVarArg...) {
/*let message1 = message.description.replacingOccurrences(of: "{public}", with: "%").replacingOccurrences(of: "{private}", with: "%")
let toprint = String(format: message1, "test")
debuglogging("toprint = " + toprint)*/
switch args.count {
case 0:

View File

@ -609,6 +609,10 @@ final class RootViewController: UIViewController {
calibrator = Libre1Calibrator()
}
case .Droplet1:
cgmTransmitter = CGMDroplet1Transmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self)
calibrator = Libre1Calibrator()
}
}