From df25a620492f38568234a6302436df3fb048bf4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bj=C3=B6rkert?= Date: Wed, 19 Jun 2024 21:47:09 +0200 Subject: [PATCH 1/4] Differentiating the display of SMBs from boluses. --- LoopFollow.xcodeproj/project.pbxproj | 4 + LoopFollow/Controllers/Graphs.swift | 123 ++++++++++++++++++ LoopFollow/Controllers/NightScout.swift | 6 + .../Controllers/Nightscout/Treatments.swift | 12 +- .../Nightscout/Treatments/SMB.swift | 44 +++++++ .../ViewControllers/MainViewController.swift | 1 + 6 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 LoopFollow/Controllers/Nightscout/Treatments/SMB.swift diff --git a/LoopFollow.xcodeproj/project.pbxproj b/LoopFollow.xcodeproj/project.pbxproj index 8025dd0f..7dedc6c9 100644 --- a/LoopFollow.xcodeproj/project.pbxproj +++ b/LoopFollow.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ DD493AE72ACF23CF009A6922 /* DeviceStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD493AE62ACF23CF009A6922 /* DeviceStatus.swift */; }; DD493AE92ACF2445009A6922 /* BGData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD493AE82ACF2445009A6922 /* BGData.swift */; }; DD608A082C1F584900F91132 /* DeviceStatusLoop.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD608A072C1F584900F91132 /* DeviceStatusLoop.swift */; }; + DD608A0A2C23593900F91132 /* SMB.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD608A092C23593900F91132 /* SMB.swift */; }; DD6A935E2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6A935D2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift */; }; DD7E19842ACDA50C00DBD158 /* Overrides.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7E19832ACDA50C00DBD158 /* Overrides.swift */; }; DD7E19862ACDA59700DBD158 /* BGCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7E19852ACDA59700DBD158 /* BGCheck.swift */; }; @@ -214,6 +215,7 @@ DD493AE62ACF23CF009A6922 /* DeviceStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceStatus.swift; sourceTree = ""; }; DD493AE82ACF2445009A6922 /* BGData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGData.swift; sourceTree = ""; }; DD608A072C1F584900F91132 /* DeviceStatusLoop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceStatusLoop.swift; sourceTree = ""; }; + DD608A092C23593900F91132 /* SMB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMB.swift; sourceTree = ""; }; DD6A935D2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceStatusOpenAPS.swift; sourceTree = ""; }; DD7E19832ACDA50C00DBD158 /* Overrides.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Overrides.swift; sourceTree = ""; }; DD7E19852ACDA59700DBD158 /* BGCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGCheck.swift; sourceTree = ""; }; @@ -440,6 +442,7 @@ DD493ADA2ACF21A3009A6922 /* Bolus.swift */, DD493AD42ACF2109009A6922 /* ResumePump.swift */, DDF9676D2AD08C6E00C5EB95 /* SiteChange.swift */, + DD608A092C23593900F91132 /* SMB.swift */, ); path = Treatments; sourceTree = ""; @@ -989,6 +992,7 @@ FCC6886724898F8000A0279D /* UserDefaultsValue.swift in Sources */, DDCF979E24C2382A002C9752 /* AppStateController.swift in Sources */, FC97881E2485969B00A7906C /* NightScoutViewController.swift in Sources */, + DD608A0A2C23593900F91132 /* SMB.swift in Sources */, DDCF979824C1489C002C9752 /* GraphSettingsViewController.swift in Sources */, FC3AE7B5249E8E0E00AAE1E0 /* LoopFollow.xcdatamodeld in Sources */, FCC6886F2489A53800A0279D /* AppConstants.swift in Sources */, diff --git a/LoopFollow/Controllers/Graphs.swift b/LoopFollow/Controllers/Graphs.swift index 5d4da598..133ddd4c 100644 --- a/LoopFollow/Controllers/Graphs.swift +++ b/LoopFollow/Controllers/Graphs.swift @@ -350,6 +350,32 @@ extension MainViewController { } ZTlinePrediction.setDrawHighlightIndicators(false) ZTlinePrediction.valueFont.withSize(50) + + // SMB + let chartEntrySmb = [ChartDataEntry]() + let lineSmb = LineChartDataSet(entries:chartEntrySmb, label: "") + lineSmb.circleRadius = CGFloat(globalVariables.dotBolus) + lineSmb.circleColors = [NSUIColor.red.withAlphaComponent(0.75)] + lineSmb.drawCircleHoleEnabled = false + lineSmb.setDrawHighlightIndicators(false) + lineSmb.setColor(NSUIColor.red, alpha: 1.0) + lineSmb.lineWidth = 0 + lineSmb.axisDependency = YAxis.AxisDependency.right + lineSmb.valueFormatter = ChartYDataValueFormatter() + lineSmb.valueTextColor = NSUIColor.label + lineSmb.fillColor = NSUIColor.red + lineSmb.fillAlpha = 0.6 + + lineSmb.drawCirclesEnabled = true + lineSmb.drawFilledEnabled = false + + if UserDefaultsRepository.showValues.value { + lineSmb.drawValuesEnabled = true + lineSmb.highlightEnabled = false + } else { + lineSmb.drawValuesEnabled = false + lineSmb.highlightEnabled = true + } // Setup the chart data of all lines let data = LineChartData() @@ -370,6 +396,7 @@ extension MainViewController { data.append(IOBlinePrediction) // Dataset 13 data.append(COBlinePrediction) // Dataset 14 data.append(UAMlinePrediction) // Dataset 15 + data.append(lineSmb) // Dataset 16 data.setValueFont(UIFont.systemFont(ofSize: 12)) @@ -819,6 +846,82 @@ extension MainViewController { } } + func updateSmbGraph() { + var dataIndex = 16 + var yTop: Double = 370 + var yBottom: Double = 345 + var mainChart = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet + var smallChart = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet + mainChart.clear() + smallChart.clear() + let lightBlue = NSUIColor(red: 135/255, green: 206/255, blue: 235/255, alpha: 1.0) // Light Sky Blue + + var colors = [NSUIColor]() + for i in 0.. 6 { + colors.append(lightBlue.withAlphaComponent(0.25)) + } else { + let thisAlpha = 1.0 - (0.15 * diffTimeHours) + colors.append(lightBlue.withAlphaComponent(CGFloat(thisAlpha))) + } + + if bolusShift { + // Move it half the distance between BG readings + dateTimeStamp = dateTimeStamp - 150 + } + + // skip if outside of visible area + let graphHours = 24 * UserDefaultsRepository.downloadDays.value + if dateTimeStamp < dateTimeUtils.getTimeIntervalNHoursAgo(N: graphHours) { continue } + + let dot = ChartDataEntry(x: Double(dateTimeStamp), y: Double(smbData[i].sgv), data: formatter.string(from: NSNumber(value: smbData[i].value))) + mainChart.addEntry(dot) + if UserDefaultsRepository.smallGraphTreatments.value { + smallChart.addEntry(dot) + } + } + + // Set Colors + let lineBolus = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet + let lineBolusSmall = BGChartFull.lineData!.dataSets[dataIndex] as! LineChartDataSet + lineBolus.colors.removeAll() + lineBolus.circleColors.removeAll() + lineBolusSmall.colors.removeAll() + lineBolusSmall.circleColors.removeAll() + + if colors.count > 0 { + for i in 0.. 0 { + processNSSmb(entries: smb) + } else { + if smbData.count > 0 { + clearOldSmb() + } + } updateTodaysCarbsFromEntries(entries: carbs) if carbs.count > 0 { processNSCarbs(entries: carbs) diff --git a/LoopFollow/Controllers/Nightscout/Treatments/SMB.swift b/LoopFollow/Controllers/Nightscout/Treatments/SMB.swift new file mode 100644 index 00000000..cb6f201b --- /dev/null +++ b/LoopFollow/Controllers/Nightscout/Treatments/SMB.swift @@ -0,0 +1,44 @@ +// +// SMB.swift +// LoopFollow +// +// Created by Jonas Björkert on 2024-06-19. +// Copyright © 2024 Jon Fawcett. All rights reserved. +// + +import Foundation +extension MainViewController { + // NS SMB Processor + func processNSSmb(entries: [[String:AnyObject]]) { + smbData.removeAll() + var lastFoundIndex = 0 + + entries.reversed().forEach { currentEntry in + var bolusDate: String + if currentEntry["timestamp"] != nil { + bolusDate = currentEntry["timestamp"] as! String + } else if currentEntry["created_at"] != nil { + bolusDate = currentEntry["created_at"] as! String + } else { + return + } + + guard let parsedDate = NightscoutUtils.parseDate(bolusDate), + let bolus = currentEntry["insulin"] as? Double else { return } + + let dateTimeStamp = parsedDate.timeIntervalSince1970 + let sgv = findNearestBGbyTime(needle: dateTimeStamp, haystack: bgData, startingIndex: lastFoundIndex) + lastFoundIndex = sgv.foundIndex + + if dateTimeStamp < (dateTimeUtils.getNowTimeIntervalUTC() + (60 * 60)) { + // Make the dot + let dot = bolusGraphStruct(value: bolus, date: Double(dateTimeStamp), sgv: Int(sgv.sgv + 20)) + smbData.append(dot) + } + } + + if UserDefaultsRepository.graphBolus.value { + updateSmbGraph() + } + } +} diff --git a/LoopFollow/ViewControllers/MainViewController.swift b/LoopFollow/ViewControllers/MainViewController.swift index ccb51421..f2608469 100644 --- a/LoopFollow/ViewControllers/MainViewController.swift +++ b/LoopFollow/ViewControllers/MainViewController.swift @@ -109,6 +109,7 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele var basalData: [basalGraphStruct] = [] var basalScheduleData: [basalGraphStruct] = [] var bolusData: [bolusGraphStruct] = [] + var smbData: [bolusGraphStruct] = [] var carbData: [carbGraphStruct] = [] var overrideGraphData: [DataStructs.overrideStruct] = [] var predictionData: [ShareGlucoseData] = [] From 3f68c285cfbeda1b7775de94952e51395571c03a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bj=C3=B6rkert?= Date: Mon, 24 Jun 2024 16:14:26 +0200 Subject: [PATCH 2/4] Triangles for smb and auto bolus --- LoopFollow/Controllers/Graphs.swift | 76 ++++++++++++------- .../Controllers/Nightscout/Treatments.swift | 6 +- 2 files changed, 53 insertions(+), 29 deletions(-) diff --git a/LoopFollow/Controllers/Graphs.swift b/LoopFollow/Controllers/Graphs.swift index 133ddd4c..b078dd33 100644 --- a/LoopFollow/Controllers/Graphs.swift +++ b/LoopFollow/Controllers/Graphs.swift @@ -10,6 +10,46 @@ import Foundation import Charts import UIKit +import Charts + +class TriangleRenderer: LineChartRenderer { + let smbDataSetIndex: Int + + init(dataProvider: LineChartDataProvider?, animator: Animator?, viewPortHandler: ViewPortHandler?, smbDataSetIndex: Int) { + self.smbDataSetIndex = smbDataSetIndex + super.init(dataProvider: dataProvider!, animator: animator!, viewPortHandler: viewPortHandler!) + } + + override func drawExtras(context: CGContext) { + super.drawExtras(context: context) + + guard let dataProvider = dataProvider else { return } + + if dataProvider.lineData?.dataSets.count ?? 0 > smbDataSetIndex, let lineDataSet = dataProvider.lineData?.dataSets[smbDataSetIndex] as? LineChartDataSet { + let trans = dataProvider.getTransformer(forAxis: lineDataSet.axisDependency) + let phaseY = animator.phaseY + + for j in 0 ..< lineDataSet.entryCount { + guard let e = lineDataSet.entryForIndex(j) else { continue } + + let pt = trans.pixelForValues(x: e.x, y: e.y * phaseY) + + context.saveGState() + context.beginPath() + context.move(to: CGPoint(x: pt.x, y: pt.y + 9)) + context.addLine(to: CGPoint(x: pt.x - 5, y: pt.y - 1)) + context.addLine(to: CGPoint(x: pt.x + 5, y: pt.y - 1)) + context.closePath() + + context.setFillColor(lineDataSet.circleColors.first!.cgColor) + context.fillPath() + + context.restoreGState() + } + } + } +} + let ScaleXMax:Float = 150.0 extension MainViewController { @@ -353,9 +393,9 @@ extension MainViewController { // SMB let chartEntrySmb = [ChartDataEntry]() - let lineSmb = LineChartDataSet(entries:chartEntrySmb, label: "") + let lineSmb = LineChartDataSet(entries: chartEntrySmb, label: "") lineSmb.circleRadius = CGFloat(globalVariables.dotBolus) - lineSmb.circleColors = [NSUIColor.red.withAlphaComponent(0.75)] + lineSmb.circleColors = [NSUIColor.systemBlue.withAlphaComponent(1.0)] lineSmb.drawCircleHoleEnabled = false lineSmb.setDrawHighlightIndicators(false) lineSmb.setColor(NSUIColor.red, alpha: 1.0) @@ -363,13 +403,11 @@ extension MainViewController { lineSmb.axisDependency = YAxis.AxisDependency.right lineSmb.valueFormatter = ChartYDataValueFormatter() lineSmb.valueTextColor = NSUIColor.label - lineSmb.fillColor = NSUIColor.red - lineSmb.fillAlpha = 0.6 - lineSmb.drawCirclesEnabled = true + lineSmb.drawCirclesEnabled = false lineSmb.drawFilledEnabled = false - if UserDefaultsRepository.showValues.value { + if UserDefaultsRepository.showValues.value { lineSmb.drawValuesEnabled = true lineSmb.highlightEnabled = false } else { @@ -855,19 +893,17 @@ extension MainViewController { mainChart.clear() smallChart.clear() let lightBlue = NSUIColor(red: 135/255, green: 206/255, blue: 235/255, alpha: 1.0) // Light Sky Blue - + var colors = [NSUIColor]() - for i in 0.. 0 { - for i in 0.. Date: Mon, 24 Jun 2024 19:07:29 +0200 Subject: [PATCH 3/4] Removed fading bolus feature --- LoopFollow/Controllers/Graphs.swift | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/LoopFollow/Controllers/Graphs.swift b/LoopFollow/Controllers/Graphs.swift index b078dd33..5cb1db1d 100644 --- a/LoopFollow/Controllers/Graphs.swift +++ b/LoopFollow/Controllers/Graphs.swift @@ -829,17 +829,7 @@ extension MainViewController { let bolusShift = findNextBolusTime(timeWithin: 240, needle: bolusData[i].date, haystack: bolusData, startingIndex: i) var dateTimeStamp = bolusData[i].date - // Alpha colors for DIA - let nowTime = dateTimeUtils.getNowTimeIntervalUTC() - let diffTimeHours = (nowTime - dateTimeStamp) / 60 / 60 - if diffTimeHours <= 1 { - colors.append(NSUIColor.systemBlue.withAlphaComponent(1.0)) - } else if diffTimeHours > 6 { - colors.append(NSUIColor.systemBlue.withAlphaComponent(0.25)) - } else { - let thisAlpha = 1.0 - (0.15 * diffTimeHours) - colors.append(NSUIColor.systemBlue.withAlphaComponent(CGFloat(thisAlpha))) - } + colors.append(NSUIColor.systemBlue.withAlphaComponent(1.0)) if bolusShift { // Move it half the distance between BG readings From d671f69b7c922ba276ccda87fa0739eebb729673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bj=C3=B6rkert?= Date: Mon, 24 Jun 2024 19:14:32 +0200 Subject: [PATCH 4/4] Removed fading carbs feature --- LoopFollow/Controllers/Graphs.swift | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/LoopFollow/Controllers/Graphs.swift b/LoopFollow/Controllers/Graphs.swift index 5cb1db1d..5cf047b7 100644 --- a/LoopFollow/Controllers/Graphs.swift +++ b/LoopFollow/Controllers/Graphs.swift @@ -959,17 +959,7 @@ extension MainViewController { let carbShift = findNextCarbTime(timeWithin: 250, needle: carbData[i].date, haystack: carbData, startingIndex: i) var dateTimeStamp = carbData[i].date - // Alpha colors for DIA - let nowTime = dateTimeUtils.getNowTimeIntervalUTC() - let diffTimeHours = (nowTime - dateTimeStamp) / 60 / 60 - if diffTimeHours <= 0.5 { - colors.append(NSUIColor.systemOrange.withAlphaComponent(1.0)) - } else if diffTimeHours > Double(hours) { - colors.append(NSUIColor.systemOrange.withAlphaComponent(0.25)) - } else { - let thisAlpha = 1.0 - ((0.75 / Double(hours)) * diffTimeHours) - colors.append(NSUIColor.systemOrange.withAlphaComponent(CGFloat(thisAlpha))) - } + colors.append(NSUIColor.systemOrange.withAlphaComponent(1.0)) // skip if outside of visible area let graphHours = 24 * UserDefaultsRepository.downloadDays.value @@ -979,7 +969,6 @@ extension MainViewController { dateTimeStamp = dateTimeStamp - 250 } - let dot = ChartDataEntry(x: Double(dateTimeStamp), y: Double(carbData[i].sgv), data: valueString) BGChart.data?.dataSets[dataIndex].addEntry(dot) if UserDefaultsRepository.smallGraphTreatments.value {