Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions Common/LocalizedString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ private class FrameworkBundle {
static let main = Bundle(for: FrameworkBundle.self)
}

func LocalizedString(_ key: String, tableName: String? = nil, value: String = "", comment: String) -> String {
return NSLocalizedString(key, tableName: tableName, bundle: FrameworkBundle.main, value: value, comment: comment)
func LocalizedString(_ key: String, tableName: String? = nil, value: String? = nil, comment: String) -> String {
if let value = value {
return NSLocalizedString(key, tableName: tableName, bundle: FrameworkBundle.main, value: value, comment: comment)
} else {
return NSLocalizedString(key, tableName: tableName, bundle: FrameworkBundle.main, comment: comment)
}
}
15 changes: 12 additions & 3 deletions Common/TimeInterval.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import Foundation


extension TimeInterval {

static func days(_ days: Double) -> TimeInterval {
return self.init(days: days)
}

static func hours(_ hours: Double) -> TimeInterval {
return self.init(hours: hours)
}
Expand All @@ -30,13 +35,17 @@ extension TimeInterval {
return self.init(milliseconds / 1000)
}

init(minutes: Double) {
self.init(minutes * 60)
init(days: Double) {
self.init(hours: days * 24)
}

init(hours: Double) {
self.init(minutes: hours * 60)
}

init(minutes: Double) {
self.init(minutes * 60)
}

init(seconds: Double) {
self.init(seconds)
Expand Down
2 changes: 1 addition & 1 deletion Crypto/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.0.3</string>
<string>2.0.4</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
Expand Down
2 changes: 1 addition & 1 deletion MinimedKit/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.0.3</string>
<string>2.0.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
3 changes: 3 additions & 0 deletions MinimedKit/Messages/PumpErrorMessageBody.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public enum PumpErrorCode: UInt8, CustomStringConvertible {
case commandRefused = 0x08
case maxSettingExceeded = 0x09
case bolusInProgress = 0x0c
case pageDoesNotExist = 0x0d

public var description: String {
switch self {
Expand All @@ -22,6 +23,8 @@ public enum PumpErrorCode: UInt8, CustomStringConvertible {
return LocalizedString("Max setting exceeded", comment: "Pump error code describing max setting exceeded")
case .bolusInProgress:
return LocalizedString("Bolus in progress", comment: "Pump error code when bolus is in progress")
case .pageDoesNotExist:
return LocalizedString("History page does not exist", comment: "Pump error code when invalid history page is requested")
}
}

Expand Down
56 changes: 33 additions & 23 deletions MinimedKit/PumpManager/MinimedPumpManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,42 +15,51 @@ import os.log
public class MinimedPumpManager: RileyLinkPumpManager, PumpManager {
public static let managerIdentifier: String = "Minimed500"

public init(state: MinimedPumpManagerState, rileyLinkManager: RileyLinkDeviceManager?) {
public init(state: MinimedPumpManagerState, rileyLinkDeviceProvider: RileyLinkDeviceProvider, rileyLinkConnectionManager: RileyLinkConnectionManager? = nil, pumpOps: PumpOps? = nil) {
self.state = state

super.init(rileyLinkPumpManagerState: state.rileyLinkPumpManagerState, rileyLinkManager: rileyLinkManager)
super.init(rileyLinkDeviceProvider: rileyLinkDeviceProvider, rileyLinkConnectionManager: rileyLinkConnectionManager)

// Pump communication
let idleListeningEnabled = state.pumpModel.hasMySentry
self.pumpOps = PumpOps(pumpSettings: state.pumpSettings, pumpState: state.pumpState, delegate: self)
self.pumpOps = pumpOps ?? PumpOps(pumpSettings: state.pumpSettings, pumpState: state.pumpState, delegate: self)

self.rileyLinkManager.idleListeningState = idleListeningEnabled ? MinimedPumpManagerState.idleListeningEnabledDefaults : .disabled
self.rileyLinkDeviceProvider.idleListeningState = idleListeningEnabled ? MinimedPumpManagerState.idleListeningEnabledDefaults : .disabled
}

public required convenience init?(rawState: PumpManager.RawStateValue) {
guard let state = MinimedPumpManagerState(rawValue: rawState) else {
guard let state = MinimedPumpManagerState(rawValue: rawState),
let connectionManagerState = state.rileyLinkConnectionManagerState else
{
return nil
}

self.init(state: state, rileyLinkManager: nil)

let rileyLinkConnectionManager = RileyLinkConnectionManager(state: connectionManagerState)

self.init(state: state, rileyLinkDeviceProvider: rileyLinkConnectionManager.deviceProvider, rileyLinkConnectionManager: rileyLinkConnectionManager)

rileyLinkConnectionManager.delegate = self
}

public var rawState: PumpManager.RawStateValue {
return state.rawValue
}

override public var rileyLinkPumpManagerState: RileyLinkPumpManagerState {
didSet {
state.rileyLinkPumpManagerState = rileyLinkPumpManagerState
}
}

// TODO: apply lock
public private(set) var state: MinimedPumpManagerState {
didSet {
pumpManagerDelegate?.pumpManagerDidUpdateState(self)
}
}

override public var rileyLinkConnectionManagerState: RileyLinkConnectionManagerState? {
get {
return state.rileyLinkConnectionManagerState
}
set {
state.rileyLinkConnectionManagerState = newValue
}
}

public weak var cgmManagerDelegate: CGMManagerDelegate?

Expand Down Expand Up @@ -106,7 +115,7 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager {
/// characteristic which can cause the app to wake. For most users, the G5 Transmitter and
/// G4 Receiver are reliable as hearbeats, but users who find their resources extremely constrained
/// due to greedy apps or older devices may choose to always enable the timer by always setting `true`
self.rileyLinkManager.timerTickEnabled = self.isPumpDataStale || (self.pumpManagerDelegate?.pumpManagerShouldProvideBLEHeartbeat(self) == true)
self.rileyLinkDeviceProvider.timerTickEnabled = self.isPumpDataStale || (self.pumpManagerDelegate?.pumpManagerShouldProvideBLEHeartbeat(self) == true)
}
}

Expand Down Expand Up @@ -204,14 +213,14 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager {
}
} catch let error {
self.log.error("Device %{public}@ auto-tune failed with error: %{public}@", device.name ?? "", String(describing: error))
self.rileyLinkManager.deprioritize(device)
self.rileyLinkDeviceProvider.deprioritize(device, completion: nil)
if let error = error as? LocalizedError {
self.pumpManagerDelegate?.pumpManager(self, didError: PumpManagerError.communication(MinimedPumpManagerError.tuneFailed(error)))
}
}
}
} else {
rileyLinkManager.deprioritize(device)
rileyLinkDeviceProvider.deprioritize(device, completion: nil)
}
}

Expand Down Expand Up @@ -343,7 +352,7 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager {
/// - completion: A closure called once upon completion
/// - error: An error describing why the fetch and/or store failed
private func fetchPumpHistory(_ completion: @escaping (_ error: Error?) -> Void) {
rileyLinkManager.getDevices { (devices) in
rileyLinkDeviceProvider.getDevices { (devices) in
guard let device = devices.firstConnected else {
completion(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink))
return
Expand Down Expand Up @@ -376,7 +385,7 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager {
/// TODO: Isolate to queue
private var isPumpDataStale: Bool {
// How long should we wait before we poll for new pump data?
let pumpStatusAgeTolerance = rileyLinkManager.idleListeningEnabled ? TimeInterval(minutes: 6) : TimeInterval(minutes: 4)
let pumpStatusAgeTolerance = rileyLinkDeviceProvider.idleListeningEnabled ? TimeInterval(minutes: 6) : TimeInterval(minutes: 4)

return isReservoirDataOlderThan(timeIntervalSinceNow: -pumpStatusAgeTolerance)
}
Expand All @@ -400,15 +409,15 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager {
*/
/// TODO: Isolate to queue
public func assertCurrentPumpData() {
rileyLinkManager.assertIdleListening(forcingRestart: true)
rileyLinkDeviceProvider.assertIdleListening(forcingRestart: true)

guard isPumpDataStale else {
return
}

self.log.debug("Pump data is stale, fetching.")

rileyLinkManager.getDevices { (devices) in
rileyLinkDeviceProvider.getDevices { (devices) in
guard let device = devices.firstConnected else {
let error = PumpManagerError.connection(MinimedPumpManagerError.noRileyLink)
self.log.error("No devices found while fetching pump data")
Expand Down Expand Up @@ -492,7 +501,7 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager {
// If we don't have recent pump data, or the pump was recently rewound, read new pump data before bolusing.
let shouldReadReservoir = isReservoirDataOlderThan(timeIntervalSinceNow: .minutes(-6))

pumpOps.runSession(withName: "Bolus", using: rileyLinkManager.firstConnectedDevice) { (session) in
pumpOps.runSession(withName: "Bolus", using: rileyLinkDeviceProvider.firstConnectedDevice) { (session) in
guard let session = session else {
completion(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink))
return
Expand Down Expand Up @@ -536,7 +545,7 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager {
}

public func enactTempBasal(unitsPerHour: Double, for duration: TimeInterval, completion: @escaping (PumpManagerResult<DoseEntry>) -> Void) {
pumpOps.runSession(withName: "Set Temp Basal", using: rileyLinkManager.firstConnectedDevice) { (session) in
pumpOps.runSession(withName: "Set Temp Basal", using: rileyLinkDeviceProvider.firstConnectedDevice) { (session) in
guard let session = session else {
completion(.failure(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink)))
return
Expand Down Expand Up @@ -646,7 +655,7 @@ extension MinimedPumpManager: CGMManager {
}

public func fetchNewDataIfNeeded(_ completion: @escaping (CGMResult) -> Void) {
rileyLinkManager.getDevices { (devices) in
rileyLinkDeviceProvider.getDevices { (devices) in
guard let device = devices.firstConnected else {
completion(.error(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink)))
return
Expand Down Expand Up @@ -685,3 +694,4 @@ extension MinimedPumpManager: CGMManager {
}
}
}

43 changes: 31 additions & 12 deletions MinimedKit/PumpManager/MinimedPumpManagerState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import RileyLinkBLEKit
public struct MinimedPumpManagerState: RawRepresentable, Equatable {
public typealias RawValue = PumpManager.RawStateValue

public static let version = 1
public static let version = 2

public var batteryChemistry: BatteryChemistryType

Expand Down Expand Up @@ -52,68 +52,87 @@ public struct MinimedPumpManagerState: RawRepresentable, Equatable {
}
}

public var rileyLinkPumpManagerState: RileyLinkPumpManagerState
public var rileyLinkConnectionManagerState: RileyLinkConnectionManagerState?

public var timeZone: TimeZone

public init(batteryChemistry: BatteryChemistryType = .alkaline, preferredInsulinDataSource: InsulinDataSource = .pumpHistory, pumpColor: PumpColor, pumpID: String, pumpModel: PumpModel, pumpRegion: PumpRegion, rileyLinkPumpManagerState: RileyLinkPumpManagerState, timeZone: TimeZone) {
public init(batteryChemistry: BatteryChemistryType = .alkaline, preferredInsulinDataSource: InsulinDataSource = .pumpHistory, pumpColor: PumpColor, pumpID: String, pumpModel: PumpModel, pumpRegion: PumpRegion, rileyLinkConnectionManagerState: RileyLinkConnectionManagerState?, timeZone: TimeZone) {
self.batteryChemistry = batteryChemistry
self.preferredInsulinDataSource = preferredInsulinDataSource
self.pumpColor = pumpColor
self.pumpID = pumpID
self.pumpModel = pumpModel
self.pumpRegion = pumpRegion
self.rileyLinkPumpManagerState = rileyLinkPumpManagerState
self.rileyLinkConnectionManagerState = rileyLinkConnectionManagerState
self.timeZone = timeZone
}

public init?(rawValue: RawValue) {
guard
let version = rawValue["version"] as? Int,
let batteryChemistryRaw = rawValue["batteryChemistry"] as? BatteryChemistryType.RawValue,
let insulinDataSourceRaw = rawValue["insulinDataSource"] as? InsulinDataSource.RawValue,
let pumpColorRaw = rawValue["pumpColor"] as? PumpColor.RawValue,
let pumpID = rawValue["pumpID"] as? String,
let pumpModelNumber = rawValue["pumpModel"] as? PumpModel.RawValue,
let pumpRegionRaw = rawValue["pumpRegion"] as? PumpRegion.RawValue,
let rileyLinkPumpManagerStateRaw = rawValue["rileyLinkPumpManagerState"] as? RileyLinkPumpManagerState.RawValue,
let timeZoneSeconds = rawValue["timeZone"] as? Int,

let batteryChemistry = BatteryChemistryType(rawValue: batteryChemistryRaw),
let insulinDataSource = InsulinDataSource(rawValue: insulinDataSourceRaw),
let pumpColor = PumpColor(rawValue: pumpColorRaw),
let pumpModel = PumpModel(rawValue: pumpModelNumber),
let pumpRegion = PumpRegion(rawValue: pumpRegionRaw),
let rileyLinkPumpManagerState = RileyLinkPumpManagerState(rawValue: rileyLinkPumpManagerStateRaw),
let timeZone = TimeZone(secondsFromGMT: timeZoneSeconds)
else {
return nil
}


var rileyLinkConnectionManagerState: RileyLinkConnectionManagerState? = nil

// Migrate
if version == 1
{
if let oldRileyLinkPumpManagerStateRaw = rawValue["rileyLinkPumpManagerState"] as? [String : Any],
let connectedPeripheralIDs = oldRileyLinkPumpManagerStateRaw["connectedPeripheralIDs"] as? [String]
{
rileyLinkConnectionManagerState = RileyLinkConnectionManagerState(autoConnectIDs: Set(connectedPeripheralIDs))
}
} else {
if let rawState = rawValue["rileyLinkConnectionManagerState"] as? RileyLinkConnectionManagerState.RawValue {
rileyLinkConnectionManagerState = RileyLinkConnectionManagerState(rawValue: rawState)
}
}

self.init(
batteryChemistry: batteryChemistry,
preferredInsulinDataSource: insulinDataSource,
pumpColor: pumpColor,
pumpID: pumpID,
pumpModel: pumpModel,
pumpRegion: pumpRegion,
rileyLinkPumpManagerState: rileyLinkPumpManagerState,
rileyLinkConnectionManagerState: rileyLinkConnectionManagerState,
timeZone: timeZone
)
}

public var rawValue: RawValue {
return [
var value: [String : Any] = [
"batteryChemistry": batteryChemistry.rawValue,
"insulinDataSource": preferredInsulinDataSource.rawValue,
"pumpColor": pumpColor.rawValue,
"pumpID": pumpID,
"pumpModel": pumpModel.rawValue,
"pumpRegion": pumpRegion.rawValue,
"rileyLinkPumpManagerState": rileyLinkPumpManagerState.rawValue,
"timeZone": timeZone.secondsFromGMT(),

"version": MinimedPumpManagerState.version,
]
]

if let rileyLinkConnectionManagerState = rileyLinkConnectionManagerState {
value["rileyLinkConnectionManagerState"] = rileyLinkConnectionManagerState.rawValue
}
return value
}
}

Expand All @@ -134,7 +153,7 @@ extension MinimedPumpManagerState: CustomDebugStringConvertible {
"pumpModel: \(pumpModel.rawValue)",
"pumpRegion: \(pumpRegion)",
"timeZone: \(timeZone)",
String(reflecting: rileyLinkPumpManagerState),
String(reflecting: rileyLinkConnectionManagerState),
].joined(separator: "\n")
}
}
2 changes: 1 addition & 1 deletion MinimedKitTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>2.0.3</string>
<string>2.0.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
Loading