Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
a7808de
Merge pull request #1 from LoopKit/master
elnjensen Nov 1, 2016
2a80ed4
Merge remote-tracking branch 'LoopKit/dev' into dev
dm61 Oct 9, 2017
7658972
Merge remote-tracking branch 'LoopKit/dev' into dev
dm61 Nov 3, 2017
47ad706
Merge remote-tracking branch 'LoopKit/dev' into dev
dm61 Nov 4, 2017
f573283
Merge remote-tracking branch 'LoopKit/dev' into dev
dm61 Nov 18, 2017
fcf8b65
Merge pull request #2 from LoopKit/master
elnjensen Nov 19, 2017
a87fdc0
Merge remote-tracking branch 'LoopKit/dev' into dev
dm61 Dec 5, 2017
3d9f17b
Merge remote-tracking branch 'LoopKit/dev' into dev
dm61 Dec 5, 2017
155e0a1
Merge remote-tracking branch 'LoopKit/dev' into dev
dm61 Dec 16, 2017
9335bc3
Merge remote-tracking branch 'LoopKit/dev' into dev
dm61 Dec 20, 2017
b618eb3
Merge remote-tracking branch 'LoopKit/dev' into dev
dm61 Dec 22, 2017
325b21c
Merge pull request #4 from LoopKit/master
elnjensen Dec 27, 2017
d6e04fc
Merge remote-tracking branch 'LoopKit/dev' into dev
dm61 Jan 13, 2018
13f5bfa
Merge remote-tracking branch 'LoopKit/dev' into dev
dm61 Feb 18, 2018
ae9768d
Merge remote-tracking branch 'LoopKit/dev' into dev
dm61 Mar 13, 2018
da7b61d
integral retrospective correction
dm61 Mar 21, 2018
85326ab
integral retrospective correction, safer velocity calculation
dm61 Mar 26, 2018
cbd8a32
integral retrospective correct, reset if glucose data missing
dm61 Mar 27, 2018
09b67fe
Merge remote-tracking branch 'LoopKit/dev' into integral-retrospectiv…
dm61 Apr 10, 2018
35e34f2
Merge remote-tracking branch 'LoopKit/dev' into integral-retrospectiv…
dm61 Apr 16, 2018
38c744d
Merge branch 'LoopKit/dev' into integral-retrospective-correction
dm61 May 5, 2018
ce354f6
Merge branch 'LoopKit/dev' into integral-retrospective-correction
dm61 May 5, 2018
c4b17c6
Merge remote-tracking branch 'LoopKit/dev' into integral-retrospectiv…
dm61 May 10, 2018
0a70fdb
update integral RC only after glucose update
dm61 May 10, 2018
f52b0f0
display retrospective correction effect
dm61 May 12, 2018
993ad8c
integral RC indicator
dm61 May 12, 2018
e9150f1
-
dm61 May 13, 2018
46ea449
Retrospective correction indicator
dm61 May 13, 2018
80b9332
Retrospective correction indicator
dm61 May 13, 2018
ccec42f
Merge branch 'integral-retrospective-correction-indicator' into integ…
dm61 May 13, 2018
886b326
Merge pull request #6 from LoopKit/dev
elnjensen Jun 4, 2018
b3407f1
Add IOB and COB display to Apple Watch
elnjensen Jun 4, 2018
3ad2df6
Set COB to zero if undefined.
elnjensen Jun 4, 2018
5239bfc
Add basal rate to watch
elnjensen Jun 7, 2018
827514a
Only show basal rate if temp basal is running
elnjensen Jun 7, 2018
5aa91bb
Separate label for basal rate
elnjensen Jun 7, 2018
e5efd19
Tweak layout of labels
elnjensen Jun 8, 2018
4bab3e9
Pass graph as PNG image data
elnjensen Jun 9, 2018
8bd0b9b
Auto-range for graph, prediction line
elnjensen Jun 13, 2018
c1a583b
Plot targets (including temporary targets)
elnjensen Jun 14, 2018
62bfca6
Revert inadvertent changes to image assets for watch
elnjensen Jun 15, 2018
ec480c0
Merge remote-tracking branch 'LoopKit/dev' into integral-retrospectiv…
dm61 Jun 15, 2018
4f1dddf
Merge pull request #9 from dm61/integral-retrospective-correction
elnjensen Jun 15, 2018
92c43d5
Merge remote-tracking branch 'LoopKit/dev' into integral-retrospectiv…
dm61 Jun 15, 2018
aff2d1a
Provide default glucose unit if not set
elnjensen Jun 16, 2018
a9f4115
Reinitialize integral RC states after Loop restart
dm61 Jun 17, 2018
67b08f5
RC disabled vs inactive messages
dm61 Jun 18, 2018
03afa0d
Merge pull request #4 from dm61/integral-retrospective-correction-res…
dm61 Jun 18, 2018
709bbd1
Merge pull request #11 from dm61/integral-retrospective-correction
elnjensen Jun 19, 2018
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
3 changes: 3 additions & 0 deletions Common/Models/WatchContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ final class WatchContext: NSObject, RawRepresentable {
var reservoir: Double?
var reservoirPercentage: Double?
var batteryPercentage: Double?
var glucoseGraphImageData: Data?

override init() {
super.init()
Expand Down Expand Up @@ -90,6 +91,7 @@ final class WatchContext: NSObject, RawRepresentable {
recommendedBolusDose = rawValue["rbo"] as? Double
COB = rawValue["cob"] as? Double
maxBolus = rawValue["mb"] as? Double
glucoseGraphImageData = rawValue["gg"] as? Data
}

var rawValue: RawValue {
Expand Down Expand Up @@ -117,6 +119,7 @@ final class WatchContext: NSObject, RawRepresentable {
raw["r"] = reservoir
raw["rbo"] = recommendedBolusDose
raw["rp"] = reservoirPercentage
raw["gg"] = glucoseGraphImageData

return raw
}
Expand Down
4 changes: 2 additions & 2 deletions Loop Status Extension/Base.lproj/MainInterface.storyboard
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="64E-I5-5c4">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="64E-I5-5c4">
<device id="retina5_5" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
Expand Down
3 changes: 2 additions & 1 deletion Loop/Extensions/NSUserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ extension UserDefaults {
maximumBasalRatePerHour: maximumBasalRatePerHour,
maximumBolus: maximumBolus,
suspendThreshold: suspendThreshold,
retrospectiveCorrectionEnabled: bool(forKey: "com.loudnate.Loop.RetrospectiveCorrectionEnabled")
retrospectiveCorrectionEnabled: bool(forKey: "com.loudnate.Loop.RetrospectiveCorrectionEnabled"),
integralRetrospectiveCorrectionEnabled: bool(forKey: "com.loopkit.Loop.IntegralRetrospectiveCorrectionEnabled")
)
self.loopSettings = settings

Expand Down
4 changes: 4 additions & 0 deletions Loop/Managers/AnalyticsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ final class AnalyticsManager: IdentifiableClass {
if newValue.retrospectiveCorrectionEnabled != oldValue.retrospectiveCorrectionEnabled {
logEvent("Retrospective correction enabled change")
}

if newValue.integralRetrospectiveCorrectionEnabled != oldValue.integralRetrospectiveCorrectionEnabled {
logEvent("Integral retrospective correction enabled change")
}
}

// MARK: - Loop Events
Expand Down
282 changes: 276 additions & 6 deletions Loop/Managers/LoopDataManager.swift

Large diffs are not rendered by default.

319 changes: 314 additions & 5 deletions Loop/Managers/WatchDataManager.swift

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion Loop/Models/LoopSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ struct LoopSettings {
var suspendThreshold: GlucoseThreshold? = nil

var retrospectiveCorrectionEnabled = true

var integralRetrospectiveCorrectionEnabled = true
}


Expand Down Expand Up @@ -74,13 +76,18 @@ extension LoopSettings: RawRepresentable {
if let retrospectiveCorrectionEnabled = rawValue["retrospectiveCorrectionEnabled"] as? Bool {
self.retrospectiveCorrectionEnabled = retrospectiveCorrectionEnabled
}

if let integralRetrospectiveCorrectionEnabled = rawValue["integralRetrospectiveCorrectionEnabled"] as? Bool {
self.integralRetrospectiveCorrectionEnabled = integralRetrospectiveCorrectionEnabled
}
}

var rawValue: RawValue {
var raw: RawValue = [
"version": LoopSettings.version,
"dosingEnabled": dosingEnabled,
"retrospectiveCorrectionEnabled": retrospectiveCorrectionEnabled
"retrospectiveCorrectionEnabled": retrospectiveCorrectionEnabled,
"integralRetrospectiveCorrectionEnabled": integralRetrospectiveCorrectionEnabled
]

raw["glucoseTargetRangeSchedule"] = glucoseTargetRangeSchedule?.rawValue
Expand Down
101 changes: 79 additions & 22 deletions Loop/View Controllers/PredictionTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ class PredictionTableViewController: ChartsTableViewController, IdentifiableClas
static let count = 3
}

fileprivate enum SettingsRow: Int, CaseCountable {
case retrospectiveCorrection
case integralRetrospectiveCorrection

static let count = 2
}

private var eventualGlucoseDescription: String?

private var availableInputs: [PredictionInputEffect] = [.carbs, .insulin, .momentum, .retrospection]
Expand All @@ -204,7 +211,7 @@ class PredictionTableViewController: ChartsTableViewController, IdentifiableClas
case .inputs:
return availableInputs.count
case .settings:
return 1
return SettingsRow.count
}
}

Expand All @@ -231,12 +238,22 @@ class PredictionTableViewController: ChartsTableViewController, IdentifiableClas
case .settings:
let cell = tableView.dequeueReusableCell(withIdentifier: SwitchTableViewCell.className, for: indexPath) as! SwitchTableViewCell

cell.titleLabel?.text = NSLocalizedString("Enable Retrospective Correction", comment: "Title of the switch which toggles retrospective correction effects")
cell.subtitleLabel?.text = NSLocalizedString("This will more aggresively increase or decrease basal delivery when glucose movement doesn't match the carbohydrate and insulin-based model.", comment: "The description of the switch which toggles retrospective correction effects")
cell.`switch`?.isOn = deviceManager.loopManager.settings.retrospectiveCorrectionEnabled
cell.`switch`?.addTarget(self, action: #selector(retrospectiveCorrectionSwitchChanged(_:)), for: .valueChanged)

cell.contentView.layoutMargins.left = tableView.separatorInset.left
switch SettingsRow(rawValue: indexPath.row)! {
case .retrospectiveCorrection:
cell.titleLabel?.text = NSLocalizedString("Retrospective Correction", comment: "Title of the switch which toggles retrospective correction effects")
cell.subtitleLabel?.text = NSLocalizedString("More agressively increase or decrease basal delivery when glucose movement over past 30 min doesn't match the carbohydrate and insulin-based model.", comment: "The description of the switch which toggles retrospective correction effects")
cell.`switch`?.isOn = deviceManager.loopManager.settings.retrospectiveCorrectionEnabled
cell.`switch`?.addTarget(self, action: #selector(retrospectiveCorrectionSwitchChanged(_:)), for: .valueChanged)

cell.contentView.layoutMargins.left = tableView.separatorInset.left
case .integralRetrospectiveCorrection:
cell.titleLabel?.text = NSLocalizedString("Integral Retrospective Correction", comment: "Title of the switch which toggles integral retrospective correction effects")
cell.subtitleLabel?.text = NSLocalizedString("Respond more aggressively to persistent discrepancies in glucose movement.", comment: "The description of the switch which toggles integral retrospective correction effects")
cell.`switch`?.isOn = deviceManager.loopManager.settings.integralRetrospectiveCorrectionEnabled
cell.`switch`?.addTarget(self, action: #selector(integralRetrospectiveCorrectionSwitchChanged(_:)), for: .valueChanged)

cell.contentView.layoutMargins.left = tableView.separatorInset.left
}

return cell
}
Expand Down Expand Up @@ -267,20 +284,48 @@ class PredictionTableViewController: ChartsTableViewController, IdentifiableClas

var subtitleText = input.localizedDescription(forGlucoseUnit: charts.glucoseUnit) ?? ""

if input == .retrospection,
let startGlucose = retrospectivePredictedGlucose?.first,
let endGlucose = retrospectivePredictedGlucose?.last,
let currentGlucose = self.deviceManager.loopManager.glucoseStore.latestGlucose
{
let formatter = NumberFormatter.glucoseFormatter(for: charts.glucoseUnit)
let values = [startGlucose, endGlucose, currentGlucose].map { formatter.string(from: NSNumber(value: $0.quantity.doubleValue(for: charts.glucoseUnit))) ?? "?" }

let retro = String(
format: NSLocalizedString("Last comparison: %1$@ → %2$@ vs %3$@", comment: "Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose)"),
values[0], values[1], values[2]
)

subtitleText = String(format: "%@\n%@", subtitleText, retro)
if input == .retrospection {
if deviceManager.loopManager.settings.retrospectiveCorrectionEnabled,
let startGlucose = retrospectivePredictedGlucose?.first,
let endGlucose = retrospectivePredictedGlucose?.last,
let currentGlucose = self.deviceManager.loopManager.glucoseStore.latestGlucose
{
let formatter = NumberFormatter.glucoseFormatter(for: charts.glucoseUnit)
let values = [startGlucose, endGlucose, currentGlucose].map { formatter.string(from: NSNumber(value: $0.quantity.doubleValue(for: charts.glucoseUnit))) ?? "?" }
let endGlucoseValue = endGlucose.quantity.doubleValue(for: charts.glucoseUnit)
let currentGlucoseValue = currentGlucose.quantity.doubleValue(for: charts.glucoseUnit)
let currentDiscrepancyValue = currentGlucoseValue - endGlucoseValue
let currentDiscrepancy = formatter.string(from: NSNumber(value: currentDiscrepancyValue))!
var integralEffect = "none"
var retrospectiveCorrection = "none"
if self.deviceManager.loopManager.overallRetrospectiveCorrection != nil {
//Retrospective Correction effect included in glucose prediction
let overallRetrospectiveCorrectionValue = self.deviceManager.loopManager.overallRetrospectiveCorrection!.doubleValue(for: charts.glucoseUnit)
let integralEffectValue = overallRetrospectiveCorrectionValue - currentDiscrepancyValue
integralEffect = formatter.string(from: NSNumber(value: integralEffectValue))!
retrospectiveCorrection = formatter.string(from: NSNumber(value: overallRetrospectiveCorrectionValue))!
}
if !deviceManager.loopManager.settings.integralRetrospectiveCorrectionEnabled {
integralEffect = "disabled"
}
let retroComparison = String(
format: NSLocalizedString("Last 30 min comparison: %1$@ → %2$@ vs %3$@", comment: "Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose)"),
values[0], values[1], values[2])
let retroCorrection = String(
format: NSLocalizedString("RC effect: %1$@, Integral effect: %2$@\nTotal glucose effect: %3$@", comment: "Format string describing retrospective correction. (1: Current discrepancy)(2: Integral retrospective correction effect)(3: Total retrospective correction effect)"), currentDiscrepancy, integralEffect, retrospectiveCorrection)

subtitleText = String(format: "%@\n%@", retroComparison, retroCorrection)

} else {
// Retrospective Correction disabled or not included in glucose prediction for other reasons
if deviceManager.loopManager.settings.retrospectiveCorrectionEnabled {
let inactiveRetrospectiveCorrection = String(format: NSLocalizedString("Temporarily inactive due to recent calibration or missing data", comment: "Format string describing inactive retrospective correction."))
subtitleText = String(format: "%@", inactiveRetrospectiveCorrection)
} else {
let disabledRetrospectiveCorrection = String(format: NSLocalizedString("⚠️ Retrospective correction is disabled", comment: "Format string describing disabled retrospective correction."))
subtitleText = String(format: "%@\n%@", subtitleText, disabledRetrospectiveCorrection)
}
}
}

cell.subtitleLabel?.text = subtitleText
Expand Down Expand Up @@ -328,11 +373,23 @@ class PredictionTableViewController: ChartsTableViewController, IdentifiableClas

@objc private func retrospectiveCorrectionSwitchChanged(_ sender: UISwitch) {
deviceManager.loopManager.settings.retrospectiveCorrectionEnabled = sender.isOn

// if retrospective correction is disabled, integral retrospective correction must also be disabled
if !sender.isOn {
deviceManager.loopManager.settings.integralRetrospectiveCorrectionEnabled = sender.isOn
}

if let row = availableInputs.index(where: { $0 == .retrospection }),
let cell = tableView.cellForRow(at: IndexPath(row: row, section: Section.inputs.rawValue)) as? PredictionInputEffectTableViewCell
{
cell.enabled = self.deviceManager.loopManager.settings.retrospectiveCorrectionEnabled
}
}

@objc private func integralRetrospectiveCorrectionSwitchChanged(_ sender: UISwitch) {
deviceManager.loopManager.settings.integralRetrospectiveCorrectionEnabled = sender.isOn
// if integral retrospective correction is enabled, retrospective correction must also be enabled
if sender.isOn {
deviceManager.loopManager.settings.retrospectiveCorrectionEnabled = sender.isOn
}
}
}
64 changes: 64 additions & 0 deletions WatchApp Extension/Controllers/StatusInterfaceController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import WatchConnectivity

final class StatusInterfaceController: WKInterfaceController, ContextUpdatable {

@IBOutlet weak var glucoseGraph: WKInterfaceImage!
@IBOutlet weak var loopHUDImage: WKInterfaceImage!
@IBOutlet weak var loopTimer: WKInterfaceTimer!
@IBOutlet weak var glucoseLabel: WKInterfaceLabel!
@IBOutlet weak var eventualGlucoseLabel: WKInterfaceLabel!
@IBOutlet weak var statusLabel: WKInterfaceLabel!
@IBOutlet weak var basalLabel: WKInterfaceLabel!

@IBOutlet var preMealButton: WKInterfaceButton!
@IBOutlet var preMealButtonImage: WKInterfaceImage!
Expand Down Expand Up @@ -177,7 +179,69 @@ final class StatusInterfaceController: WKInterfaceController, ContextUpdatable {
}

// TODO: Other elements
let insulinFormatter: NumberFormatter = {
let numberFormatter = NumberFormatter()

numberFormatter.numberStyle = .decimal
numberFormatter.minimumFractionDigits = 1
numberFormatter.maximumFractionDigits = 1

return numberFormatter
}()

statusLabel.setHidden(true)
var statusLabelText = ""

if let activeInsulin = context?.IOB, let valueStr = insulinFormatter.string(from:NSNumber(value:activeInsulin))
{
statusLabelText = String(format: NSLocalizedString(
"IOB %1$@ U",
comment: "The subtitle format describing units of active insulin. (1: localized insulin value description)"),
valueStr)
}

if let carbsOnBoard = context?.COB
{
let carbFormatter = NumberFormatter()
carbFormatter.numberStyle = .decimal
carbFormatter.maximumFractionDigits = 0
let valueStr = carbFormatter.string(from:NSNumber(value:carbsOnBoard))

if statusLabelText != "" { // Not empty - add space; TODO layout properly
statusLabelText += " "
}
statusLabelText += String(format: NSLocalizedString(
"COB %1$@ g",
comment: "The subtitle format describing grams of active carbs. (1: localized carb value description)"),
valueStr!)
}

basalLabel.setHidden(true)
if let tempBasal = context?.lastNetTempBasalDose {
let basalFormatter = NumberFormatter()
basalFormatter.numberStyle = .decimal
basalFormatter.minimumFractionDigits = 1
basalFormatter.maximumFractionDigits = 3
basalFormatter.positivePrefix = basalFormatter.plusSign
let valueStr = basalFormatter.string(from:NSNumber(value:tempBasal))

let basalLabelText = String(format: NSLocalizedString(
"%1$@ U/hr",
comment: "The subtitle format describing the current temp basal rate. (1: localized basal rate description)"),
valueStr!)
basalLabel.setText(basalLabelText)
basalLabel.setHidden(false)
}

statusLabel.setText(statusLabelText)
statusLabel.setHidden(false)

glucoseGraph.setHidden(true)
if let glucoseGraphImageData = context?.glucoseGraphImageData, let glucoseGraphImage = UIImage(data: glucoseGraphImageData) {
glucoseGraph.setImage(glucoseGraphImage)
glucoseGraph.setHidden(false)
}

}

private func updateForOverrideContext(_ context: GlucoseRangeScheduleOverrideUserInfo.Context?) {
Expand Down
Loading