Blucon finished
This commit is contained in:
parent
b874a0bb0d
commit
b1aacda83d
|
@ -100,9 +100,13 @@ extension UserDefaults {
|
|||
/// timestamp of last bgreading that was stored in healthkit
|
||||
case timeStampLatestHealthKitStoreBgReading = "timeStampLatestHealthKitStoreBgReading"
|
||||
|
||||
// Dexcom Share
|
||||
/// timestamp of latest reading uploaded to Dexcom Share
|
||||
case timeStampLatestDexcomShareUploadedBgReading = "timeStampLatestDexcomShareUploadedBgReading"
|
||||
|
||||
// Sensor
|
||||
/// sensor Serial Number, for now only applicable to Libre
|
||||
case sensorSerialNumber = "sensorSerialNumber"
|
||||
}
|
||||
|
||||
// MARK: - ===== User Configurable Settings ======
|
||||
|
@ -527,6 +531,15 @@ extension UserDefaults {
|
|||
}
|
||||
}
|
||||
|
||||
/// sensor serial number, for now only useful for Libre sensor
|
||||
var sensorSerialNumber:String? {
|
||||
get {
|
||||
return string(forKey: Key.sensorSerialNumber.rawValue)
|
||||
}
|
||||
set {
|
||||
set(newValue, forKey: Key.sensorSerialNumber.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
|
|||
if let level = result.batteryLevel {
|
||||
transmitterBatteryInfo = TransmitterBatteryInfo.DexcomG4(level: level)
|
||||
}
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
}
|
||||
case .beaconPacket?:
|
||||
os_log(" in peripheral didUpdateValueFor, received beaconPacket", log: log, type: .info)
|
||||
|
@ -149,7 +149,7 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
|
|||
if let batteryLevel = result.batteryLevel {
|
||||
transmitterBatteryInfo = TransmitterBatteryInfo.DexcomG4(level: batteryLevel)
|
||||
}
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -165,7 +165,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
let testdata = RawGlucoseData(timeStamp: Date(), glucoseLevelRaw: testAmount, glucoseLevelFiltered: testAmount)
|
||||
debuglogging("timestamp testdata = " + testdata.timeStamp.description + ", with amount = " + testAmount.description)
|
||||
var testdataasarray = [testdata]
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &testdataasarray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &testdataasarray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
testAmount = testAmount + 1
|
||||
}
|
||||
|
||||
|
@ -437,7 +437,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
timeStampOfLastG5Reading = Date()
|
||||
let glucoseData = RawGlucoseData(timeStamp: sensorDataRxMessage.timestamp, glucoseLevelRaw: sensorDataRxMessage.unfiltered * scalingFactor, glucoseLevelFiltered: sensorDataRxMessage.filtered * scalingFactor)
|
||||
var glucoseDataArray = [glucoseData]
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -538,7 +538,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
|
||||
private func processBatteryStatusRxMessage(value:Data) {
|
||||
if let batteryStatusRxMessage = BatteryStatusRxMessage(data: value) {
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.DexcomG5(voltageA: batteryStatusRxMessage.voltageA, voltageB: batteryStatusRxMessage.voltageB, resist: batteryStatusRxMessage.resist, runtime: batteryStatusRxMessage.runtime, temperature: batteryStatusRxMessage.temperature), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.DexcomG5(voltageA: batteryStatusRxMessage.voltageA, voltageB: batteryStatusRxMessage.voltageB, resist: batteryStatusRxMessage.resist, runtime: batteryStatusRxMessage.runtime, temperature: batteryStatusRxMessage.temperature), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
} else {
|
||||
os_log("batteryStatusRxMessage is nil", log: log, type: .error)
|
||||
}
|
||||
|
@ -546,7 +546,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
|
||||
private func processTransmitterVersionRxMessage(value:Data) {
|
||||
if let transmitterVersionRxMessage = TransmitterVersionRxMessage(data: value) {
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: transmitterVersionRxMessage.firmwareVersion.hexEncodedString(), hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: transmitterVersionRxMessage.firmwareVersion.hexEncodedString(), hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
// assign transmitterVersion
|
||||
transmitterVersion = transmitterVersionRxMessage.firmwareVersion.hexEncodedString()
|
||||
} else {
|
||||
|
|
|
@ -90,6 +90,8 @@ enum CGMTransmitterType:String, CaseIterable {
|
|||
/// if true, then a class conforming to the protocol CGMTransmitterDelegate will call newSensorDetected if it detects a new sensor is placed. Means there's no need to let the user start and stop a sensor
|
||||
///
|
||||
/// example MiaoMiao can detect new sensor, implementation should return true, Dexcom transmitter's can't
|
||||
///
|
||||
/// if true, then transmitterType must also be able to give the sensor age, ie sensorTimeInMinutes
|
||||
func canDetectNewSensor() -> Bool {
|
||||
|
||||
switch self {
|
||||
|
@ -107,7 +109,7 @@ enum CGMTransmitterType:String, CaseIterable {
|
|||
return false
|
||||
|
||||
case .Blucon:
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,9 +29,10 @@ protocol CGMTransmitterDelegate:AnyObject {
|
|||
/// - sensorTimeInMinutes : sensor age in minutes, only if transmitter can give that info, eg MiaoMiao, otherwise nil
|
||||
/// - firmware : only if transmitter can give that info, eg G5, otherwise nil
|
||||
/// - hardware : only if transmitter can give that info, eg G5, otherwise nil
|
||||
/// - serialNumber : only if transmitter can give that info, eg G5, otherwise nil
|
||||
/// - serialNumber : transmitter serial number, only if transmitter can give that info, eg G5, otherwise nil
|
||||
/// - bootloader : for the moment only used by GNSentry, otherwise nil
|
||||
func cgmTransmitterInfoReceived(glucoseData:inout [RawGlucoseData], transmitterBatteryInfo:TransmitterBatteryInfo?, sensorState:LibreSensorState?, sensorTimeInMinutes:Int?, firmware:String?, hardware:String?, serialNumber:String?, bootloader:String?)
|
||||
/// - sensorSerialNumber : serial number of the sensor, only applicable for Libre transmitters (MiaoMiao, Blucon, ...)
|
||||
func cgmTransmitterInfoReceived(glucoseData:inout [RawGlucoseData], transmitterBatteryInfo:TransmitterBatteryInfo?, sensorState:LibreSensorState?, sensorTimeInMinutes:Int?, firmware:String?, hardware:String?, hardwareSerialNumber:String?, bootloader:String?, sensorSerialNumber:String?)
|
||||
|
||||
/// transmitter needs bluetooth pairing
|
||||
func cgmTransmitterNeedsPairing()
|
||||
|
|
|
@ -2,7 +2,9 @@ import Foundation
|
|||
|
||||
enum BluconTransmitterOpCode: String, CaseIterable {
|
||||
|
||||
case wakeUp = "cb010000"
|
||||
case wakeUpRequest = "cb010000"
|
||||
|
||||
case wakeUpResponse = "810a00"
|
||||
|
||||
case getPatchInfoRequest = "010d0900"
|
||||
|
||||
|
@ -12,6 +14,32 @@ enum BluconTransmitterOpCode: String, CaseIterable {
|
|||
|
||||
case sensorNotDetected = "8b1a02000f"
|
||||
|
||||
case sleep = "010c0e00"
|
||||
|
||||
case bluconAckResponse = "8b0a00"
|
||||
|
||||
case unknown1Command = "010d0b00"
|
||||
|
||||
case unknown1CommandResponse = "8bdb"
|
||||
|
||||
case unknown2Command = "010d0a00"
|
||||
|
||||
case unknown2CommandResponse = "8bda"
|
||||
|
||||
case getHistoricDataAllBlocksCommand = "010d0f02002b"
|
||||
|
||||
case multipleBlockResponseIndex = "8bdf"
|
||||
|
||||
case getNowDataIndex = "010d0e0103"
|
||||
|
||||
case singleBlockInfoResponsePrefix = "8bde"
|
||||
|
||||
case singleBlockInfoPrefix = "010d0e010"
|
||||
|
||||
case bluconBatteryLowIndication1 = "cb020000"
|
||||
|
||||
case bluconBatteryLowIndication2 = "cbdb0000"
|
||||
|
||||
/// iterates through all cases, and as soon as one is found that starts with valueReceived, then initializes with that case. If none found then returns nil
|
||||
public init?(withOpCodeValue: String) {
|
||||
for opCode in BluconTransmitterOpCode.allCases {
|
||||
|
@ -24,14 +52,16 @@ enum BluconTransmitterOpCode: String, CaseIterable {
|
|||
return nil
|
||||
}
|
||||
|
||||
|
||||
public var description:String {
|
||||
|
||||
switch self {
|
||||
|
||||
case .wakeUp:
|
||||
case .wakeUpRequest:
|
||||
return "wakeUp"
|
||||
|
||||
case .wakeUpResponse:
|
||||
return "wakeUpResponse"
|
||||
|
||||
case .getPatchInfoRequest:
|
||||
return "getPatchInfo"
|
||||
|
||||
|
@ -43,6 +73,46 @@ enum BluconTransmitterOpCode: String, CaseIterable {
|
|||
|
||||
case .getPatchInfoResponse:
|
||||
return "getPatchInfoResponse"
|
||||
|
||||
case .sleep:
|
||||
return "sleep"
|
||||
|
||||
case .bluconAckResponse:
|
||||
return "bluconAckResponse"
|
||||
|
||||
case .unknown1Command:
|
||||
return "unknown1Command"
|
||||
|
||||
case .unknown1CommandResponse:
|
||||
return "unknown1CommandResponse"
|
||||
|
||||
case .unknown2Command:
|
||||
return "unknown2Command"
|
||||
|
||||
case .unknown2CommandResponse:
|
||||
return "unknown2CommandResponse"
|
||||
|
||||
case .getHistoricDataAllBlocksCommand:
|
||||
return "getHistoricDataAllBlocksCommand"
|
||||
|
||||
case .multipleBlockResponseIndex:
|
||||
return "multipleBlockResponseIndex"
|
||||
|
||||
case .getNowDataIndex:
|
||||
return "getNowDataIndex"
|
||||
|
||||
case .singleBlockInfoResponsePrefix:
|
||||
return "singleBlockInfoResponsePrefixResponse"
|
||||
|
||||
case .singleBlockInfoPrefix:
|
||||
return "singleBlockInfoPrefix"
|
||||
|
||||
case .bluconBatteryLowIndication1:
|
||||
return "bluconBatteryLowIndication1"
|
||||
|
||||
case .bluconBatteryLowIndication2:
|
||||
return "bluconBatteryLowIndication2"
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,39 +1,46 @@
|
|||
import Foundation
|
||||
|
||||
fileprivate let lookupTable = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "T", "U", "V", "W", "X", "Y", "Z"]
|
||||
|
||||
/// static functions for Blucon
|
||||
class BluconUtilities {
|
||||
|
||||
/*public static func decodeSerialNumber(input: Data) -> String{
|
||||
|
||||
let uuidShort = Data(bytes: <#T##Sequence#>)//new byte[]{0, 0, 0, 0, 0, 0, 0, 0};
|
||||
int i;
|
||||
|
||||
for (i = 2; i < 8; i++) uuidShort[i - 2] = input[(2 + 8) - i];
|
||||
uuidShort[6] = 0x00;
|
||||
uuidShort[7] = 0x00;
|
||||
|
||||
String binary = "";
|
||||
String binS = "";
|
||||
for (i = 0; i < 8; i++) {
|
||||
binS = String.format("%8s", Integer.toBinaryString(uuidShort[i] & 0xFF)).replace(' ', '0');
|
||||
binary += binS;
|
||||
}
|
||||
|
||||
String v = "0";
|
||||
char[] pozS = {0, 0, 0, 0, 0};
|
||||
for (i = 0; i < 10; i++) {
|
||||
for (int k = 0; k < 5; k++) pozS[k] = binary.charAt((5 * i) + k);
|
||||
int value = (pozS[0] - '0') * 16 + (pozS[1] - '0') * 8 + (pozS[2] - '0') * 4 + (pozS[3] - '0') * 2 + (pozS[4] - '0') * 1;
|
||||
v += lookupTable[value];
|
||||
}
|
||||
Log.e(TAG, "decodeSerialNumber=" + v);
|
||||
|
||||
return v;
|
||||
}*/
|
||||
/// - parameters:
|
||||
/// - input : data received from Blucon
|
||||
/// - returns: The sensor serial number
|
||||
///
|
||||
/// decodes serial number, copied forp xdripplus , commit 2b25bfdf6a563aea16de63053aec5e0e3be16e5f
|
||||
public static func decodeSerialNumber(input: Data) -> String {
|
||||
|
||||
var uuidShort = Data(bytes: [0, 0, 0, 0, 0, 0, 0, 0])
|
||||
|
||||
for i in 2..<8 {
|
||||
uuidShort[i - 2] = input[(2 + 8) - i]
|
||||
}
|
||||
|
||||
uuidShort[6] = 0x00
|
||||
uuidShort[7] = 0x00
|
||||
|
||||
var binary = ""
|
||||
for i in 0..<8 {
|
||||
var binS = String(uuidShort[i] & 0xFF, radix: 2)
|
||||
while binS.count < 8 {
|
||||
binS = "0" + binS
|
||||
}
|
||||
binary = binary + binS
|
||||
}
|
||||
var v = "0"
|
||||
var pozS = [0, 0, 0, 0, 0]
|
||||
for i in 0..<10 {
|
||||
for k in 0..<5 {
|
||||
let index = (5 * i) + k
|
||||
pozS[k] = binary[index..<(index + 1)] == "0" ? 0:1
|
||||
}
|
||||
|
||||
let value = pozS[0] * 16 + pozS[1] * 8 + pozS[2] * 4 + pozS[3] * 2 + pozS[4] * 1
|
||||
v += lookupTable[value]
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let lookupTable = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
"A", "C", "D", "E", "F", "G", "H", "J", "K", "L",
|
||||
"M", "N", "P", "Q", "R", "T", "U", "V", "W", "X",
|
||||
"Y", "Z"]
|
||||
|
|
|
@ -10,13 +10,16 @@ class CGMBluconTransmitter: BluetoothTransmitter {
|
|||
private(set) weak var cgmTransmitterDelegate:CGMTransmitterDelegate?
|
||||
|
||||
/// Blucon Service
|
||||
let CBUUID_BluconService = "436A62C0-082E-4CE8-A08B-01D81F195B24"
|
||||
private let CBUUID_BluconService = "436A62C0-082E-4CE8-A08B-01D81F195B24"
|
||||
|
||||
/// receive characteristic
|
||||
let CBUUID_ReceiveCharacteristic_Blucon: String = "436A0C82-082E-4CE8-A08B-01D81F195B24"
|
||||
private let CBUUID_ReceiveCharacteristic_Blucon: String = "436A0C82-082E-4CE8-A08B-01D81F195B24"
|
||||
|
||||
/// write characteristic
|
||||
let CBUUID_WriteCharacteristic_Blucon: String = "436AA6E9-082E-4CE8-A08B-01D81F195B24"
|
||||
private let CBUUID_WriteCharacteristic_Blucon: String = "436AA6E9-082E-4CE8-A08B-01D81F195B24"
|
||||
|
||||
/// if value starts with this string, then it's assume that a battery low indication is sent by the Blucon
|
||||
private let unknownCommand2BatteryLowIndicator = "8bda02"
|
||||
|
||||
/// for OS_log
|
||||
private let log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryBlucon)
|
||||
|
@ -30,12 +33,47 @@ class CGMBluconTransmitter: BluetoothTransmitter {
|
|||
// waiting successful pairing yes or not
|
||||
private var waitingSuccessfulPairing:Bool = false
|
||||
|
||||
// current sensor serial number, if nil then it's not known yet
|
||||
private var sensorSerialNumber:String?
|
||||
|
||||
/// used as parameter in call to cgmTransmitterDelegate.cgmTransmitterInfoReceived, when there's no glucosedata to send
|
||||
private var emptyArray: [RawGlucoseData] = []
|
||||
|
||||
/// timestamp when wakeUpResponse was sent to Blucon
|
||||
private var timeStampLastWakeUpResponse:Date?
|
||||
|
||||
/// BluconACKResponse will come in two different situations
|
||||
/// - after we have sent an ackwakeup command
|
||||
/// - after we have a sleep command
|
||||
/// shouldSendUnknown1CommandAfterReceivingWakeUpResponse and timeStampLastWakeUpResponsetime work together to determine the case
|
||||
private var shouldSendUnknown1CommandAfterReceivingBluconAckResponse = true
|
||||
|
||||
/// used when processing Blucon data packet
|
||||
private var timestampFirstPacketReception:Date?
|
||||
|
||||
// how long to wait for next packet before considering the session as failed
|
||||
private let maxWaitForHistoricDataInSeconds = 5.0
|
||||
|
||||
// receive buffer for Blucon packets
|
||||
private var rxBuffer:Data
|
||||
|
||||
// used in Blucon protocol
|
||||
private var nowGlucoseOffset:UInt8 = 0
|
||||
|
||||
// used in Blucon protocol - corresponds to m_getNowGlucoseDataCommand
|
||||
private var waitingForGlucoseData = false
|
||||
|
||||
// timestamp of sending singleBlockInfoPrefix command, if time of receiving singleBlockInfoResponsePrefix is too late, then reading will be ignored
|
||||
private var timeStampOfSendingSingleBlockInfoPrefix:Date?
|
||||
|
||||
// MARK: - public functions
|
||||
|
||||
/// - parameters:
|
||||
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
|
||||
/// - transmitterID: expected transmitterID
|
||||
init?(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date) {
|
||||
/// - delegate : CGMTransmitterDelegate
|
||||
/// - sensorSerialNumber : is needed to allow detection of a new sensor.
|
||||
init?(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, sensorSerialNumber:String?) {
|
||||
|
||||
// assign addressname and name or expected devicename
|
||||
// start by using expected device name
|
||||
|
@ -46,8 +84,14 @@ class CGMBluconTransmitter: BluetoothTransmitter {
|
|||
actualDeviceAddress = address
|
||||
}
|
||||
|
||||
//initialize timeStampLastBgReading
|
||||
// initialize timeStampLastBgReading
|
||||
self.timeStampLastBgReading = timeStampLastBgReading
|
||||
|
||||
// initialize sensorSerialNumber
|
||||
self.sensorSerialNumber = sensorSerialNumber
|
||||
|
||||
// initialize rxbuffer
|
||||
rxBuffer = Data()
|
||||
|
||||
// initialize
|
||||
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_BluconService)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_Blucon, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_Blucon, startScanningAfterInit: CGMTransmitterType.Blucon.startScanningAfterInit())
|
||||
|
@ -71,14 +115,124 @@ class CGMBluconTransmitter: BluetoothTransmitter {
|
|||
|
||||
if !returnValue.uppercased().startsWith("BLU") {
|
||||
while returnValue.count < 5 {
|
||||
returnValue = "0" + returnValue;
|
||||
returnValue = "0" + returnValue
|
||||
}
|
||||
returnValue = "BLU" + returnValue;
|
||||
returnValue = "BLU" + returnValue
|
||||
}
|
||||
|
||||
return returnValue
|
||||
}
|
||||
|
||||
/// writes command to blucon, withResponse, and also logs the command
|
||||
private func sendCommandToBlucon(opcode:BluconTransmitterOpCode) {
|
||||
|
||||
os_log(" send opcode %{public}@ to Blucon", log: log, type: .info, opcode.description)
|
||||
_ = writeDataToPeripheral(data: Data(hexadecimalString: opcode.rawValue)!, type: .withResponse)
|
||||
|
||||
}
|
||||
|
||||
/// reset rxBuffer, reset timestampFirstPacketReception, stop packetRxMonitorTimer
|
||||
private func resetRxBuffer() {
|
||||
rxBuffer = Data()
|
||||
timestampFirstPacketReception = Date()
|
||||
}
|
||||
|
||||
/// process new historic data block received from Blucon, one block is the contents when receiving multipleBlockResponseIndex, inclusive the opcode - this is used if we ask all Libre data from the transmitter, which includes sensorTime and sensorStatus
|
||||
/// - returns:
|
||||
/// - did receive all data yes or no, if yes, then blucon can go to sleep
|
||||
/// also calls delegate with result of new readings
|
||||
private func handleNewHistoricData(block: Data) -> Bool {
|
||||
|
||||
//check if buffer needs to be reset
|
||||
if let timestampFirstPacketReception = timestampFirstPacketReception {
|
||||
if (Date() > timestampFirstPacketReception.addingTimeInterval(maxWaitForHistoricDataInSeconds - 1)) {
|
||||
os_log("in handleNewHistoricData, more than %{public}d seconds since last update - or first update since app launch, resetting buffer", log: log, type: .info,maxWaitForHistoricDataInSeconds)
|
||||
resetRxBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
//add new packet to buffer, ignoring the opcode (2 bytes), the number of the next block (1 byte), and the number of blocks in the data (1 byte)
|
||||
rxBuffer.append(block[4..<block.count])
|
||||
|
||||
// if rxBuffer has reached minimum lenght, then start processing
|
||||
if rxBuffer.count >= 344 {
|
||||
|
||||
os_log("in handleNewHistoricData, reached minimum length, processing data", log: log, type: .info)
|
||||
|
||||
// crc check
|
||||
guard Crc.LibreCrc(data: &rxBuffer, headerOffset: 0) else {
|
||||
|
||||
os_log(" crc check failed, no further processing", log: log, type: .error)
|
||||
|
||||
// transmitter can go to sleep
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
//get readings from buffer and send to delegate
|
||||
var result = parseLibreData(data: &rxBuffer, timeStampLastBgReadingStoredInDatabase: timeStampLastBgReading, headerOffset: 0)
|
||||
|
||||
//TODO: sort glucosedata before calling newReadingsReceived
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &result.glucoseData, transmitterBatteryInfo: nil, sensorState: result.sensorState, sensorTimeInMinutes: result.sensorTimeInMinutes, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
//set timeStampLastBgReading to timestamp of latest reading in the response so that next time we parse only the more recent readings
|
||||
if result.glucoseData.count > 0 {
|
||||
timeStampLastBgReading = result.glucoseData[0].timeStamp
|
||||
}
|
||||
|
||||
//reset the buffer
|
||||
resetRxBuffer()
|
||||
|
||||
// transmitter can go to sleep
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// transmitter should send more data
|
||||
return false
|
||||
}
|
||||
|
||||
private func blockNumberForNowGlucoseData(input:Data) -> String {
|
||||
|
||||
// caculate byte position in sensor body, decrement index to get the index where the last valid BG reading is stored
|
||||
var nowGlucoseIndex2 = input[5] * 6 + 4 - 6
|
||||
|
||||
// adjust round robin
|
||||
if nowGlucoseIndex2 < 4 {
|
||||
nowGlucoseIndex2 = nowGlucoseIndex2 + 96
|
||||
}
|
||||
|
||||
// calculate the absolute block number which correspond to trend index
|
||||
let nowGlucoseIndex3 = 3 + (nowGlucoseIndex2/8)
|
||||
|
||||
// calculate offset of the 2 bytes in the block
|
||||
nowGlucoseOffset = nowGlucoseIndex2 % 8
|
||||
|
||||
|
||||
let nowGlucoseDataAsHexString = nowGlucoseIndex3.description
|
||||
|
||||
return nowGlucoseDataAsHexString
|
||||
}
|
||||
|
||||
private func nowGetGlucoseValue(input:Data) -> Double {
|
||||
|
||||
// example 8BDE07DB010F04C868DB01
|
||||
// value 1 = 0F
|
||||
// value 2 = 04
|
||||
//rawGlucose = (input[3 + nowGlucoseOffset + 1] & 0x0F) * 256 + input[3 + nowGlucoseOffset] = 1039
|
||||
let value1 = input[3 + Int(nowGlucoseOffset)]
|
||||
let value2 = input[3 + Int(nowGlucoseOffset) + 1]
|
||||
|
||||
let rawGlucose = Double((UInt16(value2 & 0x0F)<<8) | UInt16(value1 & 0xFF))
|
||||
|
||||
// rescale for Libre
|
||||
let curGluc = rawGlucose * Constants.BloodGlucose.libreMultiplier
|
||||
|
||||
return(curGluc)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension CGMBluconTransmitter: CGMTransmitter {
|
||||
|
@ -164,29 +318,220 @@ extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
|
|||
// get Opcode
|
||||
if let opCode = BluconTransmitterOpCode(withOpCodeValue: valueAsString) {
|
||||
|
||||
os_log(" opcode = %{public}@", log: log, type: .info, opCode.description)
|
||||
os_log(" received opcode = %{public}@ from Blucon", log: log, type: .info, opCode.description)
|
||||
|
||||
switch opCode {
|
||||
|
||||
case .wakeUp:
|
||||
// send getPatchInfo command
|
||||
_ = writeDataToPeripheral(data: Data(hexadecimalString: BluconTransmitterOpCode.getPatchInfoRequest.rawValue)!, type: .withResponse)
|
||||
|
||||
case .getPatchInfoRequest:
|
||||
// shouldn't receive this ?
|
||||
return
|
||||
case .getPatchInfoRequest, .wakeUpResponse, .sleep, .unknown1Command, .unknown2Command, .getHistoricDataAllBlocksCommand, .getNowDataIndex, .singleBlockInfoPrefix:
|
||||
// these are commands that app sends to Blucon, shouldn't receive any of them
|
||||
break
|
||||
|
||||
case .wakeUpRequest:
|
||||
|
||||
// start by setting waitingForGlucoseData to false, it might still have value true due to protocol error
|
||||
waitingForGlucoseData = false
|
||||
|
||||
// send getPatchInfoRequest
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.getPatchInfoRequest)
|
||||
|
||||
// by default set battery level to 100
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 100), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
|
||||
case .error14:
|
||||
|
||||
// Blucon didn't receive the next command it was waiting for, need to wait 5 minutes
|
||||
os_log(" Timeout received, need to wait 5 minutes or push button to restart!", log: log, type: .error)
|
||||
|
||||
|
||||
// and send Blucon to sleep
|
||||
sendCommandToBlucon(opcode: .sleep)
|
||||
|
||||
case .sensorNotDetected:
|
||||
// Blucon didn't detected sensor, call delegate
|
||||
|
||||
// Blucon didn't detect sensor, call delegate
|
||||
cgmTransmitterDelegate?.sensorNotDetected()
|
||||
|
||||
// and send Blucon to sleep
|
||||
sendCommandToBlucon(opcode: .sleep)
|
||||
|
||||
case .getPatchInfoResponse:
|
||||
|
||||
// get serial number
|
||||
let newSerialNumber = BluconUtilities.decodeSerialNumber(input: value)
|
||||
|
||||
// verify serial number and if changed inform delegate
|
||||
if newSerialNumber != sensorSerialNumber {
|
||||
|
||||
os_log(" new sensor detected : %{public}@", log: log, type: .info, newSerialNumber)
|
||||
|
||||
sensorSerialNumber = newSerialNumber
|
||||
|
||||
// inform delegate about new sensor detected
|
||||
cgmTransmitterDelegate?.newSensorDetected()
|
||||
|
||||
// also reset timestamp last reading, to be sure that if new sensor is started, we get historic data
|
||||
timeStampLastBgReading = Date(timeIntervalSince1970: 0)
|
||||
|
||||
}
|
||||
|
||||
// read sensorState
|
||||
let sensorState = LibreSensorState(stateByte: value[17])
|
||||
|
||||
// if sensor is ready then send Ack, otherwise send sleep
|
||||
if sensorState == LibreSensorState.ready {
|
||||
|
||||
timeStampLastWakeUpResponse = Date()
|
||||
shouldSendUnknown1CommandAfterReceivingBluconAckResponse = true
|
||||
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.wakeUpResponse)
|
||||
|
||||
} else {
|
||||
|
||||
os_log(" sensorState = %{public}@", log: log, type: .info, sensorState.description)
|
||||
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.sleep)
|
||||
|
||||
}
|
||||
|
||||
// inform delegate about sensorSerialNumber and sensorState
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: sensorState, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: sensorSerialNumber)
|
||||
|
||||
return
|
||||
|
||||
case .bluconAckResponse:
|
||||
|
||||
// BluconACKResponse will come in two different situations
|
||||
// 1) after we have sent an ackwakeup command ==> need to send unknown1Command
|
||||
// 2) after we have sent a sleep command ==> no command to send
|
||||
// to verify in which case we are, timeStampLastWakeUpResponse is used
|
||||
// assuming bluconAckResponse will arrive less than 5 seconds after having send wakeUpResponse
|
||||
if let timeStampLastWakeUpResponse = timeStampLastWakeUpResponse, abs(timeStampLastWakeUpResponse.timeIntervalSinceNow) < 5 && shouldSendUnknown1CommandAfterReceivingBluconAckResponse {
|
||||
|
||||
// set to false to be sure
|
||||
shouldSendUnknown1CommandAfterReceivingBluconAckResponse = false
|
||||
|
||||
// send unknown1Command
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.unknown1Command)
|
||||
|
||||
} else {
|
||||
|
||||
os_log(" no further processing, Blucon is sleeping now and should send a new reading in 5 minutes", log: log, type: .info)
|
||||
|
||||
}
|
||||
|
||||
case .unknown1CommandResponse:
|
||||
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.unknown2Command)
|
||||
|
||||
case .unknown2CommandResponse:
|
||||
|
||||
// check if there's a battery low indication
|
||||
if valueAsString.startsWith(unknownCommand2BatteryLowIndicator) {
|
||||
|
||||
// this is considered as battery level 5%
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 5), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
}
|
||||
|
||||
// if timeStampLastBgReading > 5 minutes ago, then we'll get historic data, otherwise just get the latest reading
|
||||
if abs(timeStampLastBgReading.timeIntervalSinceNow) > 5 * 60 + 10 {
|
||||
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.getHistoricDataAllBlocksCommand)
|
||||
|
||||
} else {
|
||||
|
||||
// not asking for sensorAge as in Spike and xdripplus, we know the sensorAge because we started with getHistoricDataAllBlocksCommand
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.getNowDataIndex)
|
||||
|
||||
}
|
||||
|
||||
case .multipleBlockResponseIndex:
|
||||
|
||||
if handleNewHistoricData(block: value) {
|
||||
|
||||
// when Blucon responds with bluconAckResponse, then there's no need to send unknown1Command
|
||||
shouldSendUnknown1CommandAfterReceivingBluconAckResponse = false
|
||||
|
||||
// send sleep command
|
||||
sendCommandToBlucon(opcode: .sleep)
|
||||
|
||||
}
|
||||
|
||||
case .singleBlockInfoResponsePrefix:
|
||||
|
||||
if !waitingForGlucoseData {
|
||||
|
||||
// get blockNumber and compose command
|
||||
let commandToSend = BluconTransmitterOpCode.singleBlockInfoPrefix.rawValue + blockNumberForNowGlucoseData(input: value)
|
||||
|
||||
// convert command to hexstring, might fail if blockNumberForNowGlucoseData returned an invalid value
|
||||
if let commandToSendAsData = Data(hexadecimalString: commandToSend) {
|
||||
|
||||
os_log(" send %{public}@ to Blucon", log: log, type: .info, commandToSend)
|
||||
_ = writeDataToPeripheral(data: commandToSendAsData, type: .withResponse)
|
||||
|
||||
waitingForGlucoseData = true
|
||||
|
||||
} else {
|
||||
|
||||
os_log(" failed to convert commandToSend to Data", log: log, type: .error)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// reset waitingForGlucoseData to false as we will not wait for glucosedata, after having processed this reading
|
||||
waitingForGlucoseData = false
|
||||
|
||||
// to be sure that waitingForGlucoseData is not having value true due to having broken protcol, verify when SingleBlockInfoPrefix was sent
|
||||
if let timeStampOfSendingSingleBlockInfoPrefix = timeStampOfSendingSingleBlockInfoPrefix {
|
||||
// should be a matter of milliseconds, so take 2 seconds
|
||||
if abs(timeStampOfSendingSingleBlockInfoPrefix.timeIntervalSinceNow) > 2 {
|
||||
|
||||
os_log(" time since sending SingleBlockInfoPrefix is more than 2 seconds, ignoring this reading", log: log, type: .error)
|
||||
|
||||
// send sleep command
|
||||
sendCommandToBlucon(opcode: .sleep)
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// checking now timestamp of last reading, if less than 30 seconds old, then reading will be ignored, seems a bit late to do that check, but after a few tests it seems to be the best to continue up to here to make sure the Blucon stays in a consistent state.
|
||||
if abs(timeStampLastBgReading.timeIntervalSinceNow) < 30 {
|
||||
os_log(" last reading less than 30 seconds old, ignoring this one", log: log, type: .info)
|
||||
} else {
|
||||
|
||||
os_log(" creating glucoseValue", log: log, type: .info)
|
||||
|
||||
// create glucose reading with timestamp now
|
||||
timeStampLastBgReading = Date()
|
||||
|
||||
// get glucoseValue from value
|
||||
let glucoseValue = nowGetGlucoseValue(input: value)
|
||||
|
||||
let glucoseData = RawGlucoseData(timeStamp: timeStampLastBgReading, glucoseLevelRaw: glucoseValue, glucoseLevelFiltered: glucoseValue)
|
||||
var glucoseDataArray = [glucoseData]
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
}
|
||||
|
||||
sendCommandToBlucon(opcode: .sleep)
|
||||
|
||||
}
|
||||
|
||||
case .bluconBatteryLowIndication1:
|
||||
|
||||
// this is considered as battery level 3%
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 3), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
case .bluconBatteryLowIndication2:
|
||||
|
||||
// this is considered as battery level 2%
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 2), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
|
@ -154,22 +154,22 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
actualSerialNumber = String(data: value, encoding: String.Encoding.utf8)
|
||||
// TODO : is this the serial number of the sensor ? if yes we can use this to detect new sensor ? as with blucon ?
|
||||
if let actualSerialNumber = actualSerialNumber {
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: actualSerialNumber, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: actualSerialNumber, bootloader: nil, sensorSerialNumber: nil)
|
||||
}
|
||||
case .CBUUID_Firmware:
|
||||
actualFirmWareVersion = String(data: value, encoding: String.Encoding.utf8)
|
||||
if let actualFirmWareVersion = actualFirmWareVersion {
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: actualFirmWareVersion, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: actualFirmWareVersion, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
}
|
||||
case .CBUUID_Bootloader:
|
||||
actualBootLoader = String(data: value, encoding: String.Encoding.utf8)
|
||||
if let actualBootLoader = actualBootLoader {
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: actualBootLoader)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: actualBootLoader, sensorSerialNumber: nil)
|
||||
}
|
||||
case .CBUUID_BatteryLevel:
|
||||
let dataAsString = value.hexEncodedString()
|
||||
if let batteryLevel = Int(dataAsString, radix: 16) {
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryLevel), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryLevel), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
} else {
|
||||
os_log(" in peripheralDidUpdateValueFor, could not read batterylevel, received hex value = %{public}@", log: log, type: .error , dataAsString)
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
i = i + 1
|
||||
}
|
||||
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &readings, transmitterBatteryInfo: nil, sensorState: sensorStatus, sensorTimeInMinutes: Int(sensorElapsedTimeInMinutes), firmware: actualFirmWareVersion, hardware: nil, serialNumber: actualSerialNumber, bootloader: actualBootLoader)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &readings, transmitterBatteryInfo: nil, sensorState: sensorStatus, sensorTimeInMinutes: Int(sensorElapsedTimeInMinutes), firmware: actualFirmWareVersion, hardware: nil, hardwareSerialNumber: actualSerialNumber, bootloader: actualBootLoader, sensorSerialNumber: nil)
|
||||
|
||||
//set timeStampLastBgReading to timestamp of latest reading in the response so that next time we parse only the more recent readings
|
||||
if readings.count > 0 {
|
||||
|
|
|
@ -31,7 +31,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
private var resendPacketCounter:Int = 0
|
||||
|
||||
/// used when processing MiaoMiao data packet
|
||||
private var startDate:Date
|
||||
private var timestampFirstPacketReception:Date
|
||||
// receive buffer for miaomiao packets
|
||||
private var rxBuffer:Data
|
||||
// how long to wait for next packet before sending startreadingcommand
|
||||
|
@ -56,7 +56,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
|
||||
// initialize rxbuffer
|
||||
rxBuffer = Data()
|
||||
startDate = Date()
|
||||
timestampFirstPacketReception = Date()
|
||||
|
||||
//initialize timeStampLastBgReading
|
||||
self.timeStampLastBgReading = timeStampLastBgReading
|
||||
|
@ -106,7 +106,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
if let value = characteristic.value {
|
||||
|
||||
//check if buffer needs to be reset
|
||||
if (Date() > startDate.addingTimeInterval(CGMMiaoMiaoTransmitter.maxWaitForpacketInSeconds - 1)) {
|
||||
if (Date() > timestampFirstPacketReception.addingTimeInterval(CGMMiaoMiaoTransmitter.maxWaitForpacketInSeconds - 1)) {
|
||||
os_log("in peripheral didUpdateValueFor, more than %{public}d seconds since last update - or first update since app launch, resetting buffer", log: log, type: .info, CGMMiaoMiaoTransmitter.maxWaitForpacketInSeconds)
|
||||
resetRxBuffer()
|
||||
}
|
||||
|
@ -129,11 +129,11 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
let firmware = String(describing: rxBuffer[14...15].hexEncodedString())
|
||||
let hardware = String(describing: rxBuffer[16...17].hexEncodedString())
|
||||
let batteryPercentage = Int(rxBuffer[13])
|
||||
|
||||
|
||||
//get readings from buffer and send to delegate
|
||||
var result = parseLibreData(data: &rxBuffer, timeStampLastBgReadingStoredInDatabase: timeStampLastBgReading, headerOffset: miaoMiaoHeaderLength)
|
||||
//TODO: sort glucosedata before calling newReadingsReceived
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &result.glucoseData, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorState: result.sensorState, sensorTimeInMinutes: result.sensorTimeInMinutes, firmware: firmware, hardware: hardware, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &result.glucoseData, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorState: result.sensorState, sensorTimeInMinutes: result.sensorTimeInMinutes, firmware: firmware, hardware: hardware, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
//set timeStampLastBgReading to timestamp of latest reading in the response so that next time we parse only the more recent readings
|
||||
if result.glucoseData.count > 0 {
|
||||
|
@ -212,10 +212,10 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
|
||||
// MARK: - helpers
|
||||
|
||||
/// reset rxBuffer, reset startDate, stop packetRxMonitorTimer, set resendPacketCounter to 0
|
||||
/// reset rxBuffer, reset startDate, set resendPacketCounter to 0
|
||||
private func resetRxBuffer() {
|
||||
rxBuffer = Data()
|
||||
startDate = Date()
|
||||
timestampFirstPacketReception = Date()
|
||||
resendPacketCounter = 0
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ import Foundation
|
|||
/// - notYetStarted: 0x01 sensor not yet started
|
||||
/// - starting: 0x02 sensor is in the starting phase
|
||||
/// - ready: 0x03 sensor is ready, i.e. in normal operation mode
|
||||
/// - stateFour: 0x04 state with yet unknown meaning
|
||||
/// - expired: 0x05 sensor is expired
|
||||
/// - expired: 0x04 sensor is expired, status after 14 days, less than 14,5 days
|
||||
/// - shutdown: 0x05 sensor stops operation after 15d after start
|
||||
/// - failure: 0x06 sensor has an error
|
||||
/// - unknown: any other state
|
||||
enum LibreSensorState {
|
||||
|
|
|
@ -52,6 +52,9 @@ final class RootViewController: UIViewController {
|
|||
/// constant for key in ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground - initiate pairing
|
||||
private let applicationManagerKeyInitiatePairing = "RootViewController-InitiatePairing"
|
||||
|
||||
/// constant for key in ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground - initial calibration
|
||||
private let applicationManagerKeyInitialCalibration = "RootViewController-InitialCalibration"
|
||||
|
||||
// MARK: - Properties - other private properties
|
||||
|
||||
/// a reference to the CGMTransmitter currently in use - nil means there's none, because user hasn't selected yet all required settings
|
||||
|
@ -251,7 +254,11 @@ final class RootViewController: UIViewController {
|
|||
})
|
||||
}
|
||||
|
||||
private func processNewCGMInfo(glucoseData: inout [RawGlucoseData], sensorState: LibreSensorState?, firmware: String?, hardware: String?, transmitterBatteryInfo: TransmitterBatteryInfo?, sensorTimeInMinutes: Int?) {
|
||||
/// process new glucose data received from transmitter.
|
||||
/// - parameters:
|
||||
/// - glucoseData : array with new readings
|
||||
/// - sensorTimeInMinutes : should be present only if it's the first reading(s) being processed for a specific sensor and is needed if it's a transmitterType that returns true to the function canDetectNewSensor
|
||||
private func processNewGlucoseData(glucoseData: inout [RawGlucoseData], sensorTimeInMinutes: Int?) {
|
||||
|
||||
// check that calibrations and coredata manager is not nil
|
||||
guard let calibrationsAccessor = calibrationsAccessor, let coreDataManager = coreDataManager else {
|
||||
|
@ -354,10 +361,6 @@ final class RootViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
// check transmitterBatteryInfo and if available store in settings
|
||||
if let transmitterBatteryInfo = transmitterBatteryInfo {
|
||||
UserDefaults.standard.transmitterBatteryInfo = transmitterBatteryInfo
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- observe function
|
||||
|
@ -598,7 +601,7 @@ final class RootViewController: UIViewController {
|
|||
|
||||
case .Blucon:
|
||||
if let currentTransmitterId = UserDefaults.standard.transmitterId {
|
||||
cgmTransmitter = CGMBluconTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0))
|
||||
cgmTransmitter = CGMBluconTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), sensorSerialNumber: UserDefaults.standard.sensorSerialNumber)
|
||||
calibrator = Libre1Calibrator()
|
||||
}
|
||||
|
||||
|
@ -622,7 +625,7 @@ final class RootViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
// creates initial calibration request notification
|
||||
/// creates initial calibration request notification
|
||||
private func createInitialCalibrationRequest() {
|
||||
|
||||
// first remove existing notification if any
|
||||
|
@ -649,9 +652,25 @@ final class RootViewController: UIViewController {
|
|||
os_log("Unable to Add Notification Request : %{public}@", log: self.log, type: .error, error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
// we will not just count on it that the user will click the notification to open the app (assuming the app is in the background, if the app is in the foreground, then we come in another flow)
|
||||
// whenever app comes from-back to foreground, requestCalibration needs to be called
|
||||
ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground(key: applicationManagerKeyInitialCalibration, closure: {
|
||||
|
||||
// first of all reremove from application key manager
|
||||
ApplicationManager.shared.removeClosureToRunWhenAppWillEnterForeground(key: self.applicationManagerKeyInitialCalibration)
|
||||
|
||||
// remove existing notification if any
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [Constants.Notifications.NotificationIdentifiersForCalibration.initialCalibrationRequest])
|
||||
|
||||
// request the calibration
|
||||
self.requestCalibration(userRequested: false)
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// creates bgreading notification
|
||||
/// creates bgreading notification
|
||||
private func createBgReadingNotification() {
|
||||
|
||||
// bgReadingsAccessor should not be nil at all, but let's not create a fatal error for that, there's already enough checks for it
|
||||
|
@ -921,16 +940,21 @@ final class RootViewController: UIViewController {
|
|||
|
||||
}
|
||||
|
||||
// stop the active sensor
|
||||
// stops the active sensor and sets sensorSerialNumber in UserDefaults to nil
|
||||
private func stopSensor() {
|
||||
|
||||
if let activeSensor = activeSensor, let coreDataManager = coreDataManager {
|
||||
activeSensor.endDate = Date()
|
||||
coreDataManager.saveChanges()
|
||||
}
|
||||
activeSensor = nil
|
||||
|
||||
// save the changes
|
||||
coreDataManager?.saveChanges()
|
||||
|
||||
activeSensor = nil
|
||||
|
||||
// reset also serialNubmer to nil
|
||||
UserDefaults.standard.sensorSerialNumber = nil
|
||||
|
||||
}
|
||||
|
||||
// start a new sensor, ask user for starttime
|
||||
|
@ -1120,8 +1144,7 @@ extension RootViewController:CGMTransmitterDelegate {
|
|||
let maxTimeUserCanOpenApp = Date(timeIntervalSinceNow: TimeInterval(Constants.DexcomG5.maxTimeToAcceptPairingInSeconds - 1))
|
||||
|
||||
// we will not just count on it that the user will click the notification to open the app (assuming the app is in the background, if the app is in the foreground, then we come in another flow)
|
||||
// we will
|
||||
// whenever app comes from-back to freground, updateLabels needs to be called
|
||||
// whenever app comes from-back to foreground, updateLabels needs to be called
|
||||
ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground(key: applicationManagerKeyInitiatePairing, closure: {
|
||||
|
||||
// first of all reremove from application key manager
|
||||
|
@ -1175,18 +1198,32 @@ extension RootViewController:CGMTransmitterDelegate {
|
|||
|
||||
/// - parameters:
|
||||
/// - readings: first entry is the most recent
|
||||
func cgmTransmitterInfoReceived(glucoseData: inout [RawGlucoseData], transmitterBatteryInfo: TransmitterBatteryInfo?, sensorState: LibreSensorState?, sensorTimeInMinutes: Int?, firmware: String?, hardware: String?, serialNumber: String?, bootloader: String?) {
|
||||
func cgmTransmitterInfoReceived(glucoseData: inout [RawGlucoseData], transmitterBatteryInfo: TransmitterBatteryInfo?, sensorState: LibreSensorState?, sensorTimeInMinutes: Int?, firmware: String?, hardware: String?, hardwareSerialNumber: String?, bootloader: String?, sensorSerialNumber:String?) {
|
||||
|
||||
os_log("sensorstate %{public}@", log: log, type: .debug, sensorState?.description ?? "no sensor state found")
|
||||
os_log("firmware %{public}@", log: log, type: .debug, firmware ?? "no firmware version found")
|
||||
os_log("bootloader %{public}@", log: log, type: .debug, bootloader ?? "no bootloader found")
|
||||
os_log("serialNumber %{public}@", log: log, type: .debug, serialNumber ?? "no serialNumber found")
|
||||
os_log("hardwareSerialNumber %{public}@", log: log, type: .debug, hardwareSerialNumber ?? "no serialNumber found")
|
||||
os_log("sensorSerialNumber %{public}@", log: log, type: .debug, sensorSerialNumber ?? "no sensorSerialNumber found")
|
||||
os_log("hardware %{public}@", log: log, type: .debug, hardware ?? "no hardware version found")
|
||||
os_log("transmitterBatteryInfo %{public}@", log: log, type: .debug, transmitterBatteryInfo?.description ?? 0)
|
||||
os_log("sensor time in minutes %{public}@", log: log, type: .debug, sensorTimeInMinutes?.description ?? "not received")
|
||||
os_log("glucoseData size = %{public}@", log: log, type: .debug, glucoseData.count.description)
|
||||
|
||||
// if received sensorSerialNumber not nil, and if value different from currently stored value, then store it
|
||||
if let sensorSerialNumber = sensorSerialNumber {
|
||||
if sensorSerialNumber != UserDefaults.standard.sensorSerialNumber {
|
||||
UserDefaults.standard.sensorSerialNumber = sensorSerialNumber
|
||||
}
|
||||
}
|
||||
|
||||
processNewCGMInfo(glucoseData: &glucoseData, sensorState: sensorState, firmware: firmware, hardware: hardware, transmitterBatteryInfo: transmitterBatteryInfo, sensorTimeInMinutes: sensorTimeInMinutes)
|
||||
// if received transmitterBatteryInfo not nil, then store it
|
||||
if let transmitterBatteryInfo = transmitterBatteryInfo {
|
||||
UserDefaults.standard.transmitterBatteryInfo = transmitterBatteryInfo
|
||||
}
|
||||
|
||||
// process new readings
|
||||
processNewGlucoseData(glucoseData: &glucoseData, sensorTimeInMinutes: sensorTimeInMinutes)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1217,6 +1254,9 @@ extension RootViewController:UNUserNotificationCenterDelegate {
|
|||
// request calibration
|
||||
requestCalibration(userRequested: false)
|
||||
|
||||
/// remove applicationManagerKeyInitialCalibration from application key manager - there's no need to initiate the calibration via this closure
|
||||
ApplicationManager.shared.removeClosureToRunWhenAppWillEnterForeground(key: self.applicationManagerKeyInitialCalibration)
|
||||
|
||||
// call completionhandler to avoid that notification is shown to the user
|
||||
completionHandler([])
|
||||
|
||||
|
@ -1262,9 +1302,8 @@ extension RootViewController:UNUserNotificationCenterDelegate {
|
|||
|
||||
if response.notification.request.identifier == Constants.Notifications.NotificationIdentifiersForCalibration.initialCalibrationRequest {
|
||||
|
||||
os_log(" userNotificationCenter didReceive, user pressed calibration notification to open the app", log: log, type: .info)
|
||||
// request calibration
|
||||
requestCalibration(userRequested: false)
|
||||
// nothing required, the requestCalibration function will be called as it's been added to ApplicationManager
|
||||
os_log(" userNotificationCenter didReceive, user pressed calibration notification to open the app, requestCalibration should be called because closure is added in ApplicationManager.shared", log: log, type: .info)
|
||||
|
||||
} else if response.notification.request.identifier == Constants.Notifications.NotificationIdentifierForSensorNotDetected.sensorNotDetected {
|
||||
|
||||
|
|
Loading…
Reference in New Issue