Skip to content
Open
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
12 changes: 12 additions & 0 deletions LoopFollow/Charts/BGChartModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ final class BGChartModel: ObservableObject {
var id: TimeInterval { date.timeIntervalSince1970 }
}

struct ScheduledTargetPoint: Identifiable {
let date: Date
let value: Double
var id: TimeInterval { date.timeIntervalSince1970 }
}

struct BandRect: Identifiable {
let start: Date
let end: Date
Expand Down Expand Up @@ -131,6 +137,7 @@ final class BGChartModel: ObservableObject {

@Published var basal: [BasalStep] = []
@Published var basalScheduled: [ScheduledBasalPoint] = []
@Published var targetScheduled: [ScheduledTargetPoint] = []

@Published var boluses: [TreatmentPoint] = []
@Published var carbs: [TreatmentPoint] = []
Expand Down Expand Up @@ -421,6 +428,7 @@ final class BGChartModel: ObservableObject {
// collect the data regardless (it also feeds the info rows), so hidden
// kinds are dropped here at render time.
let showBasal = Storage.shared.graphBasal.value
let showTargetLine = Storage.shared.graphTargetLine.value
let showBolus = Storage.shared.graphBolus.value
let showCarbs = Storage.shared.graphCarbs.value
let showOtherTreatments = Storage.shared.graphOtherTreatments.value
Expand Down Expand Up @@ -520,6 +528,10 @@ final class BGChartModel: ObservableObject {
basalScheduled = (showBasal ? vc.basalScheduleData : []).map {
ScheduledBasalPoint(date: Date(timeIntervalSince1970: $0.date), rate: $0.basalRate)
}

targetScheduled = (showTargetLine ? vc.targetScheduleData : []).map {
ScheduledTargetPoint(date: Date(timeIntervalSince1970: $0.date), value: $0.targetHigh)
}

var steps: [BasalStep] = []
let sortedBasal = (showBasal ? vc.basalData : []).sorted { $0.date < $1.date }
Expand Down
1 change: 1 addition & 0 deletions LoopFollow/Charts/BGChartStubs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ extension MainViewController {
func updateBolusGraph() { chartModel.rebuild() }
func updateCarbGraph() { chartModel.rebuild() }
func updateBasalScheduledGraph() { chartModel.rebuild() }
func updateTargetScheduledGraph() { chartModel.rebuild() }
func updateOverrideGraph() { chartModel.rebuild() }
func updateBGCheckGraph() { chartModel.rebuild() }
func updateSuspendGraph() { chartModel.rebuild() }
Expand Down
14 changes: 14 additions & 0 deletions LoopFollow/Charts/BGChartView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,7 @@ private struct BGChartCanvas: View, Equatable {
bgBandMarks
basalMarks
scheduledBasalMarks
scheduledTargetMarks
}
coneMarks
if !isSmall {
Expand Down Expand Up @@ -1209,6 +1210,19 @@ private struct BGChartCanvas: View, Equatable {
.foregroundStyle(Color.blue.opacity(0.8))
}
}

@ChartContentBuilder
private var scheduledTargetMarks: some ChartContent {
ForEach(windowedLine(model.targetScheduled) { $0.date }) { pt in
LineMark(
x: .value("time", pt.date),
y: .value("target", pt.value),
series: .value("series", "targetScheduled")
)
.lineStyle(StrokeStyle(lineWidth: 2, dash: [10, 5]))
.foregroundStyle(Color.green.opacity(0.8))
}
}

@ChartContentBuilder
private var coneMarks: some ChartContent {
Expand Down
6 changes: 6 additions & 0 deletions LoopFollow/Controllers/NightScout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ extension MainViewController {
var date: TimeInterval
}

// NS Target Data Struct
struct targetGraphStruct: Codable {
var targetHigh: Double
var date: TimeInterval
}

// NS Bolus Data Struct
struct bolusGraphStruct: Codable {
var value: Double
Expand Down
4 changes: 3 additions & 1 deletion LoopFollow/Controllers/Nightscout/Profile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,10 @@ extension MainViewController {
}
}

if Storage.shared.graphBasal.value {
if Storage.shared.graphBasal.value {
updateBasalScheduledGraph()
}

updateTargetScheduleData()
}
}
95 changes: 95 additions & 0 deletions LoopFollow/Controllers/Nightscout/TargetSchedule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// LoopFollow
// TargetSchedule.swift

import Foundation

extension MainViewController {
/// Builds `targetScheduleData`, the routine (non-Override) target-of-the-day line shown on
/// the graph -- mirrors `basalScheduleData` in Profile.swift exactly, substituting the
/// profile's target-high schedule (via `profileManager.targetHighSchedule`, already
/// unit-converted) for the basal schedule. Uses target-high specifically to match
/// `currentTargetHigh()`, the same value already shown in the "Target" info row, so the line
/// never disagrees with the number beside it.
///
/// Like the basal line, this only ever reflects the day's *routine* schedule -- an active
/// Override, Temp Target, or Weekend Profile is shown separately as a colored band
/// (`overrides`/`tempTargets` in BGChartModel), the same relationship Trio's own target line
/// has with its own Override bands.
func updateTargetScheduleData() {
let targetSchedule = profileManager.targetHighSchedule
guard !targetSchedule.isEmpty else {
targetScheduleData.removeAll()
return
}

var targetSegments: [DataStructs.targetProfileSegment] = []

let graphHours = 24 * Storage.shared.downloadDays.value
// Build scheduled target segments from right to left by moving pointers to the current
// midnight and current target -- same walk as the basal schedule builder.
var midnight = dateTimeUtils.getTimeIntervalMidnightToday()
var targetIndex = targetSchedule.count - 1
var start = midnight + Double(targetSchedule[targetIndex].timeAsSeconds)
var end = dateTimeUtils.getNowTimeIntervalUTC()
while start > end {
targetIndex -= 1
start = midnight + Double(targetSchedule[targetIndex].timeAsSeconds)
}
let graphStart = dateTimeUtils.getTimeIntervalNHoursAgo(N: graphHours)
while end >= graphStart {
let entry = DataStructs.targetProfileSegment(
targetHigh: targetSchedule[targetIndex].value.doubleValue(for: .milligramsPerDeciliter),
startDate: start, endDate: end
)
targetSegments.append(entry)

targetIndex -= 1
if targetIndex < 0 {
targetIndex = targetSchedule.count - 1
midnight = midnight.advanced(by: -24 * 60 * 60)
}
end = start - 1
start = midnight + Double(targetSchedule[targetIndex].timeAsSeconds)
}
targetSegments.reverse()

var firstPass = true
let predictionEndTime = dateTimeUtils.getNowTimeIntervalUTC() + (3600 * Storage.shared.predictionToLoad.value)
targetScheduleData.removeAll()

for i in 0 ..< targetSegments.count {
let timeStart = dateTimeUtils.getTimeIntervalNHoursAgo(N: graphHours)

if firstPass == false,
targetSegments[i].startDate <= predictionEndTime
{
let startDot = targetGraphStruct(targetHigh: targetSegments[i].targetHigh, date: targetSegments[i].startDate)
targetScheduleData.append(startDot)
var endDate = targetSegments[i].endDate

if endDate > predictionEndTime || i == targetSegments.count - 1 {
endDate = Double(predictionEndTime)
}

let endDot = targetGraphStruct(targetHigh: targetSegments[i].targetHigh, date: endDate)
targetScheduleData.append(endDot)
}

if firstPass == true {
if timeStart >= targetSegments[i].startDate, timeStart < targetSegments[i].endDate {
let startDot = targetGraphStruct(targetHigh: targetSegments[i].targetHigh, date: Double(timeStart + (60 * 5)))
targetScheduleData.append(startDot)

let endDate = targetSegments[i].endDate
let endDot = targetGraphStruct(targetHigh: targetSegments[i].targetHigh, date: endDate)
targetScheduleData.append(endDot)
firstPass = false
}
}
}

if Storage.shared.graphTargetLine.value {
updateTargetScheduledGraph()
}
}
}
6 changes: 6 additions & 0 deletions LoopFollow/Helpers/DataStructs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ class DataStructs {
var endDate: TimeInterval
}

struct targetProfileSegment: Codable {
var targetHigh: Double
var startDate: TimeInterval
var endDate: TimeInterval
}

// NS Timestamp Only Data Struct
struct timestampOnlyStruct: Codable {
var date: TimeInterval
Expand Down
1 change: 1 addition & 0 deletions LoopFollow/Settings/AdvancedSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ struct AdvancedSettingsView: View {
Toggle("Download Treatments", isOn: $viewModel.downloadTreatments)
Toggle("Download Prediction", isOn: $viewModel.downloadPrediction)
Toggle("Graph Basal", isOn: $viewModel.graphBasal)
Toggle("Graph Target Line", isOn: $viewModel.graphTargetLine)
Toggle("Graph Bolus", isOn: $viewModel.graphBolus)
Toggle("Graph Carbs", isOn: $viewModel.graphCarbs)
Toggle("Graph Other Treatments", isOn: $viewModel.graphOtherTreatments)
Expand Down
8 changes: 8 additions & 0 deletions LoopFollow/Settings/AdvancedSettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ class AdvancedSettingsViewModel: ObservableObject {
Observable.shared.chartSettingsChanged.value = true
}
}

@Published var graphTargetLine: Bool {
didSet {
Storage.shared.graphTargetLine.value = graphTargetLine
Observable.shared.chartSettingsChanged.value = true
}
}

@Published var graphBolus: Bool {
didSet {
Expand Down Expand Up @@ -60,6 +67,7 @@ class AdvancedSettingsViewModel: ObservableObject {
downloadTreatments = Storage.shared.downloadTreatments.value
downloadPrediction = Storage.shared.downloadPrediction.value
graphBasal = Storage.shared.graphBasal.value
graphTargetLine = Storage.shared.graphTargetLine.value
graphBolus = Storage.shared.graphBolus.value
graphCarbs = Storage.shared.graphCarbs.value
graphOtherTreatments = Storage.shared.graphOtherTreatments.value
Expand Down
1 change: 1 addition & 0 deletions LoopFollow/Storage/Storage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ class Storage {
var downloadPrediction = StorageValue<Bool>(key: "downloadPrediction", defaultValue: true)
var graphOtherTreatments = StorageValue<Bool>(key: "graphOtherTreatments", defaultValue: true)
var graphBasal = StorageValue<Bool>(key: "graphBasal", defaultValue: true)
var graphTargetLine = StorageValue<Bool>(key: "graphTargetLine", defaultValue: true)
var graphBolus = StorageValue<Bool>(key: "graphBolus", defaultValue: true)
var graphCarbs = StorageValue<Bool>(key: "graphCarbs", defaultValue: true)
var bgUpdateDelay = StorageValue<Int>(key: "bgUpdateDelay", defaultValue: 10)
Expand Down
1 change: 1 addition & 0 deletions LoopFollow/ViewControllers/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class MainViewController: UIViewController, UNUserNotificationCenterDelegate {
var basalProfile: [basalProfileStruct] = []
var basalData: [basalGraphStruct] = []
var basalScheduleData: [basalGraphStruct] = []
var targetScheduleData: [targetGraphStruct] = []
var bolusData: [bolusGraphStruct] = []
var smbData: [bolusGraphStruct] = []
var carbData: [carbGraphStruct] = []
Expand Down
Loading