|
|
@@ -1,4 +1,6 @@
|
|
|
+// import Accelerate
|
|
|
import Combine
|
|
|
+import CoreData
|
|
|
import Foundation
|
|
|
import LoopKit
|
|
|
import LoopKitUI
|
|
|
@@ -78,6 +80,8 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
|
|
|
+
|
|
|
private var openAPS: OpenAPS!
|
|
|
|
|
|
private var lifetime = Lifetime()
|
|
|
@@ -239,11 +243,6 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
|
|
|
loopStats(loopStatRecord: loopStatRecord)
|
|
|
|
|
|
- // Create a statistics.json. Don't run in backgound
|
|
|
- if settings.displayStatistics, UIApplication.shared.applicationState != .background {
|
|
|
- statistics()
|
|
|
- }
|
|
|
-
|
|
|
if settings.closedLoop {
|
|
|
reportEnacted(received: error == nil)
|
|
|
}
|
|
|
@@ -662,9 +661,6 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
|
|
|
storage.save(enacted, as: OpenAPS.Enact.enacted)
|
|
|
|
|
|
- // Create a tdd.json
|
|
|
- tdd(enacted_: enacted)
|
|
|
-
|
|
|
debug(.apsManager, "Suggestion enacted. Received: \(received)")
|
|
|
DispatchQueue.main.async {
|
|
|
self.broadcaster.notify(EnactedSuggestionObserver.self, on: .main) {
|
|
|
@@ -672,63 +668,79 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
}
|
|
|
}
|
|
|
nightscout.uploadStatus()
|
|
|
+
|
|
|
+ // Update the tdd.json
|
|
|
+ tdd(enacted_: enacted)
|
|
|
+ // Update statistics.json. Only run if enabled in preferences
|
|
|
+ if settingsManager.settings.displayStatistics {
|
|
|
+ statistics()
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private func tdd(enacted_: Suggestion) {
|
|
|
+ let tddStartedAt = Date()
|
|
|
// Add to tdd.json:
|
|
|
let preferences = settingsManager.preferences
|
|
|
let currentTDD = enacted_.tdd ?? 0
|
|
|
- let file = OpenAPS.Monitor.tdd
|
|
|
- let tdd = TDD(
|
|
|
- TDD: currentTDD,
|
|
|
- timestamp: Date(),
|
|
|
- id: UUID().uuidString
|
|
|
- )
|
|
|
+
|
|
|
+ // MARK: Add new data to Core Data:TDD Entity. TEST:
|
|
|
+ debug(.apsManager, "Writing TDD to CoreData")
|
|
|
+
|
|
|
+ let nTDD = TDD(context: coredataContext)
|
|
|
+ nTDD.timestamp = Date()
|
|
|
+ nTDD.tdd = NSDecimalNumber(decimal: currentTDD)
|
|
|
+
|
|
|
+ try? coredataContext.save()
|
|
|
+
|
|
|
+ let twoWeeksAgo = Date().addingTimeInterval(-14.days.timeInterval)
|
|
|
+ let twoHoursAgo = Date().addingTimeInterval(-2.hours.timeInterval)
|
|
|
+ let requestTDD = TDD.fetchRequest() as NSFetchRequest<TDD>
|
|
|
+ requestTDD.predicate = NSPredicate(format: "timestamp > %@ AND tdd > 0", twoWeeksAgo as NSDate)
|
|
|
var uniqEvents: [TDD] = []
|
|
|
- storage.transaction { storage in
|
|
|
- storage.append(tdd, to: file, uniqBy: \.id)
|
|
|
- uniqEvents = storage.retrieve(file, as: [TDD].self)?
|
|
|
- .filter { $0.timestamp.addingTimeInterval(14.days.timeInterval) > Date() }
|
|
|
- .sorted { $0.timestamp > $1.timestamp } ?? []
|
|
|
- var total: Decimal = 0
|
|
|
- var indeces: Decimal = 0
|
|
|
- for uniqEvent in uniqEvents {
|
|
|
- if uniqEvent.TDD > 0 {
|
|
|
- total += uniqEvent.TDD
|
|
|
- indeces += 1
|
|
|
- }
|
|
|
- }
|
|
|
- let entriesPast2hours = storage.retrieve(file, as: [TDD].self)?
|
|
|
- .filter { $0.timestamp.addingTimeInterval(2.hours.timeInterval) > Date() }
|
|
|
- .sorted { $0.timestamp > $1.timestamp } ?? []
|
|
|
- var totalAmount: Decimal = 0
|
|
|
- var nrOfIndeces: Decimal = 0
|
|
|
- for entry in entriesPast2hours {
|
|
|
- if entry.TDD > 0 {
|
|
|
- totalAmount += entry.TDD
|
|
|
- nrOfIndeces += 1
|
|
|
- }
|
|
|
- }
|
|
|
- if indeces == 0 {
|
|
|
- indeces = 1
|
|
|
- }
|
|
|
- if nrOfIndeces == 0 {
|
|
|
- nrOfIndeces = 1
|
|
|
+
|
|
|
+ try? uniqEvents = coredataContext.fetch(requestTDD)
|
|
|
+
|
|
|
+ var total: Decimal = 0
|
|
|
+ var indeces: Decimal = 0
|
|
|
+ for uniqEvent in uniqEvents {
|
|
|
+ debug(.apsManager, "Read TDD from CoreData: \(uniqEvent.tdd!.decimalValue)")
|
|
|
+ total += uniqEvent.tdd!.decimalValue
|
|
|
+ indeces += 1
|
|
|
+ }
|
|
|
+
|
|
|
+ requestTDD.predicate = NSPredicate(format: "timestamp > %@ AND tdd > 0", twoHoursAgo as NSDate)
|
|
|
+ var entriesPast2hours: [TDD] = []
|
|
|
+
|
|
|
+ try? entriesPast2hours = coredataContext.fetch(requestTDD)
|
|
|
+
|
|
|
+ var totalAmount: Decimal = 0
|
|
|
+ var nrOfIndeces: Decimal = 0
|
|
|
+ for entry in entriesPast2hours {
|
|
|
+ if (entry.tdd?.decimalValue ?? 0) > 0 {
|
|
|
+ totalAmount += entry.tdd?.decimalValue ?? 0
|
|
|
+ nrOfIndeces += 1
|
|
|
}
|
|
|
- let average14 = total / indeces
|
|
|
- let average2hours = totalAmount / nrOfIndeces
|
|
|
- let weight = preferences.weightPercentage
|
|
|
- let weighted_average = weight * average2hours + (1 - weight) * average14
|
|
|
- let averages = TDD_averages(
|
|
|
- average_total_data: roundDecimal(average14, 1),
|
|
|
- weightedAverage: roundDecimal(weighted_average, 1),
|
|
|
- past2hoursAverage: roundDecimal(average2hours, 1),
|
|
|
- date: Date()
|
|
|
- )
|
|
|
- storage.save(averages, as: OpenAPS.Monitor.tdd_averages)
|
|
|
- storage.save(Array(uniqEvents), as: file)
|
|
|
}
|
|
|
+
|
|
|
+ if indeces == 0 {
|
|
|
+ indeces = 1
|
|
|
+ }
|
|
|
+ if nrOfIndeces == 0 {
|
|
|
+ nrOfIndeces = 1
|
|
|
+ }
|
|
|
+ let average14 = total / indeces
|
|
|
+ let average2hours = totalAmount / nrOfIndeces
|
|
|
+ let weight = preferences.weightPercentage
|
|
|
+ let weighted_average = weight * average2hours + (1 - weight) * average14
|
|
|
+ let averages = TDD_averages(
|
|
|
+ average_total_data: roundDecimal(average14, 1),
|
|
|
+ weightedAverage: roundDecimal(weighted_average, 1),
|
|
|
+ past2hoursAverage: roundDecimal(average2hours, 1),
|
|
|
+ date: Date()
|
|
|
+ )
|
|
|
+ storage.save(averages, as: OpenAPS.Monitor.tdd_averages)
|
|
|
+ print("Test time of TDD: \(-1 * tddStartedAt.timeIntervalSinceNow) s")
|
|
|
}
|
|
|
|
|
|
private func roundDecimal(_ decimal: Decimal, _ digits: Double) -> Decimal {
|
|
|
@@ -756,16 +768,15 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
|
|
|
// Add to statistics.JSON
|
|
|
private func statistics() {
|
|
|
+ let statisticsStartedAt = Date()
|
|
|
var testFile: [Statistics] = []
|
|
|
var testIfEmpty = 0
|
|
|
storage.transaction { storage in
|
|
|
testFile = storage.retrieve(OpenAPS.Monitor.statistics, as: [Statistics].self) ?? []
|
|
|
testIfEmpty = testFile.count
|
|
|
}
|
|
|
-
|
|
|
let updateThisOften = Int(settingsManager.preferences.updateInterval)
|
|
|
-
|
|
|
- // Only run every 30 minutesl
|
|
|
+ // Only run every 30 minutes of according to setting.
|
|
|
if testIfEmpty != 0 {
|
|
|
guard testFile[0].created_at.addingTimeInterval(updateThisOften.minutes.timeInterval) < Date()
|
|
|
else {
|
|
|
@@ -776,22 +787,23 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
let units = settingsManager.settings.units
|
|
|
let preferences = settingsManager.preferences
|
|
|
let carbs = storage.retrieve(OpenAPS.Monitor.carbHistory, as: [CarbsEntry].self)
|
|
|
- let tdds = storage.retrieve(OpenAPS.Monitor.tdd, as: [TDD].self)
|
|
|
+
|
|
|
+ let requestTDD = TDD.fetchRequest() as NSFetchRequest<TDD>
|
|
|
+ requestTDD.predicate = NSPredicate(format: "tdd > 0")
|
|
|
+ requestTDD.fetchLimit = 1
|
|
|
+ let sort = NSSortDescriptor(key: "timestamp", ascending: false)
|
|
|
+ requestTDD.sortDescriptors = [sort]
|
|
|
+
|
|
|
+ var tdds: [TDD] = []
|
|
|
+ try? tdds = coredataContext.fetch(requestTDD)
|
|
|
+
|
|
|
var currentTDD: Decimal = 0
|
|
|
- if tdds?.count ?? 0 > 0 {
|
|
|
- currentTDD = tdds?[0].TDD ?? 0
|
|
|
- }
|
|
|
- let carbs_length = carbs?.count ?? 0
|
|
|
- var carbTotal: Decimal = 0
|
|
|
- if carbs_length != 0 {
|
|
|
- for each in carbs! {
|
|
|
- if each.carbs != 0 {
|
|
|
- carbTotal += each.carbs
|
|
|
- }
|
|
|
- }
|
|
|
+ if tdds.count == 1 {
|
|
|
+ currentTDD = tdds[0].tdd?.decimalValue ?? 0
|
|
|
}
|
|
|
- var algo_ = "Oref0"
|
|
|
|
|
|
+ var algo_ = "Oref0"
|
|
|
+ let carbTotal = carbs?.map({ carbs in carbs.carbs }).reduce(0, +) ?? 0
|
|
|
if preferences.sigmoid, preferences.enableDynamicCR {
|
|
|
algo_ = "Dynamic ISF + CR: Sigmoid"
|
|
|
} else if preferences.sigmoid, !preferences.enableDynamicCR {
|
|
|
@@ -842,11 +854,9 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
|
|
|
if !lsData.isEmpty {
|
|
|
var i = 0.0
|
|
|
-
|
|
|
if let loopEnd = lsData[0].end {
|
|
|
previousTimeLoop = loopEnd
|
|
|
}
|
|
|
-
|
|
|
for each in lsData {
|
|
|
if let loopEnd = each.end, let loopDuration = each.duration {
|
|
|
if each.loopStatus.contains("Success") {
|
|
|
@@ -855,36 +865,30 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
errorNR += 1
|
|
|
}
|
|
|
i += 1
|
|
|
-
|
|
|
timeIntervalLoops = (previousTimeLoop - each.start).timeInterval / 60
|
|
|
+
|
|
|
if timeIntervalLoops > 0.0, i != 1 {
|
|
|
timeIntervalLoopArray.append(timeIntervalLoops)
|
|
|
}
|
|
|
-
|
|
|
if timeIntervalLoops > maximumInt {
|
|
|
maximumInt = timeIntervalLoops
|
|
|
}
|
|
|
if timeIntervalLoops < minimumInt, i != 1 {
|
|
|
minimumInt = timeIntervalLoops
|
|
|
}
|
|
|
-
|
|
|
timeForOneLoop = loopDuration
|
|
|
-
|
|
|
timeForOneLoopArray.append(timeForOneLoop)
|
|
|
averageLoopTime += timeForOneLoop
|
|
|
|
|
|
if timeForOneLoop >= maximumLoopTime, timeForOneLoop != 0.0 {
|
|
|
maximumLoopTime = timeForOneLoop
|
|
|
}
|
|
|
-
|
|
|
if timeForOneLoop <= minimumLoopTime, timeForOneLoop != 0.0 {
|
|
|
minimumLoopTime = timeForOneLoop
|
|
|
}
|
|
|
-
|
|
|
previousTimeLoop = loopEnd
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
successRate = (successNR / Double(i)) * 100
|
|
|
averageIntervalLoops = ((lsData[0].end ?? lsData[lsData.count - 1].start) - lsData[lsData.count - 1].start)
|
|
|
.timeInterval / 60 / Double(i)
|
|
|
@@ -899,71 +903,73 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
if minimumLoopTime == 9999.0 {
|
|
|
minimumLoopTime = 0.0
|
|
|
}
|
|
|
+
|
|
|
+ let requestGFS = Readings.fetchRequest() as NSFetchRequest<Readings>
|
|
|
+ let sortGlucose = NSSortDescriptor(key: "date", ascending: true)
|
|
|
+ requestGFS.sortDescriptors = [sortGlucose]
|
|
|
+
|
|
|
+ var glucose: [Readings] = []
|
|
|
+ try? glucose = coredataContext.fetch(requestGFS)
|
|
|
+
|
|
|
+ let firstElementTime = glucose.first?.date ?? Date()
|
|
|
+ let lastElementTime = glucose.last?.date ?? Date()
|
|
|
+ let numberOfDays = (lastElementTime - firstElementTime).timeInterval / 8.64E4
|
|
|
+
|
|
|
// Time In Range (%) and Average Glucose (24 hours). This will be refactored later after some testing.
|
|
|
- let glucose = storage.retrieve(OpenAPS.Monitor.glucose_data, as: [GlucoseDataForStats].self)
|
|
|
- let length_ = glucose?.count ?? 0
|
|
|
+ let length_ = glucose.count
|
|
|
let endIndex = length_ - 1
|
|
|
var bg: Decimal = 0
|
|
|
+
|
|
|
var bgArray: [Double] = []
|
|
|
var bgArray_1_: [Double] = []
|
|
|
var bgArray_7_: [Double] = []
|
|
|
var bgArray_30_: [Double] = []
|
|
|
+
|
|
|
var bgArrayForTIR: [(bg_: Double, date_: Date)] = []
|
|
|
var bgArray_1: [(bg_: Double, date_: Date)] = []
|
|
|
var bgArray_7: [(bg_: Double, date_: Date)] = []
|
|
|
var bgArray_30: [(bg_: Double, date_: Date)] = []
|
|
|
+
|
|
|
var medianBG = 0.0
|
|
|
var nr_bgs: Decimal = 0
|
|
|
- var nr_bgs_1: Decimal = 0
|
|
|
- var nr_bgs_7: Decimal = 0
|
|
|
- var nr_bgs_30: Decimal = 0
|
|
|
|
|
|
var startDate = Date("1978-02-22T11:43:54.659Z")
|
|
|
if endIndex >= 0 {
|
|
|
- startDate = glucose?[0].date
|
|
|
+ startDate = glucose[0].date
|
|
|
}
|
|
|
- var end1 = false
|
|
|
- var end7 = false
|
|
|
- var end30 = false
|
|
|
var bg_1: Decimal = 0
|
|
|
var bg_7: Decimal = 0
|
|
|
var bg_30: Decimal = 0
|
|
|
var bg_total: Decimal = 0
|
|
|
var j = -1
|
|
|
+ var conversionFactor: Decimal = 1
|
|
|
+ if units == .mmolL {
|
|
|
+ conversionFactor = 0.0555
|
|
|
+ }
|
|
|
|
|
|
// Make arrays for median calculations and calculate averages
|
|
|
if endIndex >= 0 {
|
|
|
- for entry in glucose! {
|
|
|
+ for entry in glucose {
|
|
|
j += 1
|
|
|
if entry.glucose > 0 {
|
|
|
- bg += Decimal(entry.glucose)
|
|
|
- bgArray.append(Double(entry.glucose))
|
|
|
- bgArrayForTIR.append((Double(entry.glucose), entry.date))
|
|
|
+ bg += Decimal(entry.glucose) * conversionFactor
|
|
|
+ bgArray.append(Double(entry.glucose) * Double(conversionFactor))
|
|
|
+ bgArrayForTIR.append((Double(entry.glucose), entry.date!))
|
|
|
nr_bgs += 1
|
|
|
-
|
|
|
- if (startDate! - entry.date).timeInterval >= 8.64E4, !end1 {
|
|
|
- end1 = true
|
|
|
+ if (startDate! - entry.date!).timeInterval <= 24.hours.timeInterval {
|
|
|
bg_1 = bg / nr_bgs
|
|
|
bgArray_1 = bgArrayForTIR
|
|
|
bgArray_1_ = bgArray
|
|
|
- nr_bgs_1 = nr_bgs
|
|
|
- // time_1 = ((startDate ?? Date()) - entry.date).timeInterval
|
|
|
}
|
|
|
- if (startDate! - entry.date).timeInterval >= 6.048E5, !end7 {
|
|
|
- end7 = true
|
|
|
+ if (startDate! - entry.date!).timeInterval <= 7.days.timeInterval {
|
|
|
bg_7 = bg / nr_bgs
|
|
|
bgArray_7 = bgArrayForTIR
|
|
|
bgArray_7_ = bgArray
|
|
|
- nr_bgs_7 = nr_bgs
|
|
|
- // time_7 = ((startDate ?? Date()) - entry.date).timeInterval
|
|
|
}
|
|
|
- if (startDate! - entry.date).timeInterval >= 2.592E6, !end30 {
|
|
|
- end30 = true
|
|
|
+ if (startDate! - entry.date!).timeInterval <= 30.days.timeInterval {
|
|
|
bg_30 = bg / nr_bgs
|
|
|
bgArray_30 = bgArrayForTIR
|
|
|
bgArray_30_ = bgArray
|
|
|
- nr_bgs_30 = nr_bgs
|
|
|
- // time_30 = ((startDate ?? Date()) - entry.date).timeInterval
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -972,25 +978,10 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
if nr_bgs > 0 {
|
|
|
// Up to 91 days
|
|
|
bg_total = bg / nr_bgs
|
|
|
-
|
|
|
- // If less then 24 hours of glucose data, use total instead
|
|
|
- if bg_1 == 0 {
|
|
|
- bg_1 = bg_total
|
|
|
- bgArray_1 = bgArrayForTIR
|
|
|
- end1 = true
|
|
|
- nr_bgs_1 = nr_bgs
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
// Total median
|
|
|
medianBG = medianCalculation(array: bgArray)
|
|
|
- var daysBG = 0.0
|
|
|
- var fullTime = 0.0
|
|
|
-
|
|
|
- if length_ > 0 {
|
|
|
- fullTime = (startDate! - glucose![endIndex].date).timeInterval
|
|
|
- daysBG = fullTime / 8.64E4
|
|
|
- }
|
|
|
|
|
|
func tir(_ array: [(bg_: Double, date_: Date)]) -> (TIR: Double, hypos: Double, hypers: Double) {
|
|
|
var timeInHypo = 0.0
|
|
|
@@ -1000,24 +991,20 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
var i = -1
|
|
|
var lastIndex = false
|
|
|
let endIndex = array.count - 1
|
|
|
-
|
|
|
var hypoLimit = settingsManager.preferences.low
|
|
|
var hyperLimit = settingsManager.preferences.high
|
|
|
if units == .mmolL {
|
|
|
hypoLimit = hypoLimit / 0.0555
|
|
|
hyperLimit = hyperLimit / 0.0555
|
|
|
}
|
|
|
-
|
|
|
var full_time = 0.0
|
|
|
if endIndex > 0 {
|
|
|
full_time = (array[0].date_ - array[endIndex].date_).timeInterval
|
|
|
}
|
|
|
-
|
|
|
while i < endIndex {
|
|
|
i += 1
|
|
|
let currentTime = array[i].date_
|
|
|
var previousTime = currentTime
|
|
|
-
|
|
|
if i + 1 <= endIndex {
|
|
|
previousTime = array[i + 1].date_
|
|
|
} else {
|
|
|
@@ -1044,43 +1031,40 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
// HbA1c estimation (%, mmol/mol) 1 day
|
|
|
var NGSPa1CStatisticValue: Decimal = 0.0
|
|
|
var IFCCa1CStatisticValue: Decimal = 0.0
|
|
|
- if end1, bg_1 > 0 {
|
|
|
- NGSPa1CStatisticValue = (46.7 + bg_1) / 28.7 // NGSP (%)
|
|
|
+ if nr_bgs > 0 {
|
|
|
+ NGSPa1CStatisticValue = ((bg_1 / conversionFactor) + 46.7) / 28.7 // NGSP (%)
|
|
|
IFCCa1CStatisticValue = 10.929 *
|
|
|
(NGSPa1CStatisticValue - 2.152) // IFCC (mmol/mol) A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
|
|
|
}
|
|
|
// 7 days
|
|
|
var NGSPa1CStatisticValue_7: Decimal = 0.0
|
|
|
var IFCCa1CStatisticValue_7: Decimal = 0.0
|
|
|
- if end7 {
|
|
|
- NGSPa1CStatisticValue_7 = (46.7 + bg_7) / 28.7 // NGSP (%)
|
|
|
- IFCCa1CStatisticValue_7 = 10.929 *
|
|
|
- (NGSPa1CStatisticValue_7 - 2.152) // IFCC (mmol/mol) A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
|
|
|
+ if nr_bgs > 0 {
|
|
|
+ NGSPa1CStatisticValue_7 = ((bg_7 / conversionFactor) + 46.7) / 28.7
|
|
|
+ IFCCa1CStatisticValue_7 = 10.929 * (NGSPa1CStatisticValue_7 - 2.152)
|
|
|
}
|
|
|
// 30 days
|
|
|
var NGSPa1CStatisticValue_30: Decimal = 0.0
|
|
|
var IFCCa1CStatisticValue_30: Decimal = 0.0
|
|
|
- if end30 {
|
|
|
- NGSPa1CStatisticValue_30 = (46.7 + bg_30) / 28.7 // NGSP (%)
|
|
|
- IFCCa1CStatisticValue_30 = 10.929 *
|
|
|
- (NGSPa1CStatisticValue_30 - 2.152) // IFCC (mmol/mol) A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
|
|
|
+ if nr_bgs > 0 {
|
|
|
+ NGSPa1CStatisticValue_30 = ((bg_30 / conversionFactor) + 46.7) / 28.7
|
|
|
+ IFCCa1CStatisticValue_30 = 10.929 * (NGSPa1CStatisticValue_30 - 2.152)
|
|
|
}
|
|
|
// Total days
|
|
|
var NGSPa1CStatisticValue_total: Decimal = 0.0
|
|
|
var IFCCa1CStatisticValue_total: Decimal = 0.0
|
|
|
if nr_bgs > 0 {
|
|
|
- NGSPa1CStatisticValue_total = (46.7 + bg_total) / 28.7 // NGSP (%)
|
|
|
+ NGSPa1CStatisticValue_total = ((bg_total / conversionFactor) + 46.7) / 28.7
|
|
|
IFCCa1CStatisticValue_total = 10.929 *
|
|
|
- (NGSPa1CStatisticValue_total - 2.152) // IFCC (mmol/mol) A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
|
|
|
+ (NGSPa1CStatisticValue_total - 2.152)
|
|
|
}
|
|
|
|
|
|
- var median = Durations(
|
|
|
- day: roundDecimal(Decimal(medianCalculation(array: bgArray_1.map(\.bg_))), 1),
|
|
|
- week: roundDecimal(Decimal(medianCalculation(array: bgArray_7.map(\.bg_))), 1),
|
|
|
- month: roundDecimal(Decimal(medianCalculation(array: bgArray_30.map(\.bg_))), 1),
|
|
|
+ let median = Durations(
|
|
|
+ day: roundDecimal(Decimal(medianCalculation(array: bgArray_1_)), 1),
|
|
|
+ week: roundDecimal(Decimal(medianCalculation(array: bgArray_7_)), 1),
|
|
|
+ month: roundDecimal(Decimal(medianCalculation(array: bgArray_30_)), 1),
|
|
|
total: roundDecimal(Decimal(medianBG), 1)
|
|
|
)
|
|
|
-
|
|
|
var hbs = Durations(
|
|
|
day: roundDecimal(NGSPa1CStatisticValue, 1),
|
|
|
week: roundDecimal(NGSPa1CStatisticValue_7, 1),
|
|
|
@@ -1090,20 +1074,7 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
|
|
|
// Convert to user-preferred unit
|
|
|
let overrideHbA1cUnit = settingsManager.preferences.overrideHbA1cUnit
|
|
|
-
|
|
|
if units == .mmolL {
|
|
|
- bg_1 = bg_1.asMmolL
|
|
|
- bg_7 = bg_7.asMmolL
|
|
|
- bg_30 = bg_30.asMmolL
|
|
|
- bg_total = bg_total.asMmolL
|
|
|
-
|
|
|
- median = Durations(
|
|
|
- day: roundDecimal(Decimal(medianCalculation(array: bgArray_1.map(\.bg_))).asMmolL, 1),
|
|
|
- week: roundDecimal(Decimal(medianCalculation(array: bgArray_7.map(\.bg_))).asMmolL, 1),
|
|
|
- month: roundDecimal(Decimal(medianCalculation(array: bgArray_30.map(\.bg_))).asMmolL, 1),
|
|
|
- total: roundDecimal(Decimal(medianBG).asMmolL, 1)
|
|
|
- )
|
|
|
-
|
|
|
// Override if users sets overrideHbA1cUnit: true
|
|
|
if !overrideHbA1cUnit {
|
|
|
hbs = Durations(
|
|
|
@@ -1122,9 +1093,6 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
- // round output values
|
|
|
- daysBG = roundDouble(daysBG, 1)
|
|
|
-
|
|
|
let glucose24Hours = storage.retrieve(OpenAPS.Monitor.glucose, as: [BloodGlucose].self)
|
|
|
let nrOfCGMReadings = glucose24Hours?.count ?? 0
|
|
|
|
|
|
@@ -1150,16 +1118,10 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
var totalDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
|
|
|
|
|
|
// Get all TIR calcs for every case
|
|
|
- if end1 {
|
|
|
+ if nr_bgs > 0 {
|
|
|
oneDay_ = tir(bgArray_1)
|
|
|
- }
|
|
|
- if end7 {
|
|
|
sevenDays_ = tir(bgArray_7)
|
|
|
- }
|
|
|
- if end30 {
|
|
|
thirtyDays_ = tir(bgArray_30)
|
|
|
- }
|
|
|
- if nr_bgs > 0 {
|
|
|
totalDays_ = tir(bgArrayForTIR)
|
|
|
}
|
|
|
|
|
|
@@ -1204,8 +1166,6 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
scheduled_basal: suggestion?.insulin?.scheduled_basal ?? 0
|
|
|
)
|
|
|
|
|
|
- // SD and CV calculations for all durations:
|
|
|
-
|
|
|
var sumOfSquares: Decimal = 0
|
|
|
var sumOfSquares_1: Decimal = 0
|
|
|
var sumOfSquares_7: Decimal = 0
|
|
|
@@ -1213,27 +1173,19 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
|
|
|
// Total
|
|
|
for array in bgArray {
|
|
|
- if units == .mmolL {
|
|
|
- sumOfSquares += pow(Decimal(array).asMmolL - bg_total, 2)
|
|
|
- } else { sumOfSquares += pow(Decimal(array) - bg_total, 2) }
|
|
|
+ sumOfSquares += pow(Decimal(array) - bg_total, 2)
|
|
|
}
|
|
|
// One day
|
|
|
for array_1 in bgArray_1_ {
|
|
|
- if units == .mmolL {
|
|
|
- sumOfSquares_1 += pow(Decimal(array_1).asMmolL - bg_1, 2)
|
|
|
- } else { sumOfSquares_1 += pow(Decimal(array_1) - bg_1, 2) }
|
|
|
+ sumOfSquares_1 += pow(Decimal(array_1) - bg_1, 2)
|
|
|
}
|
|
|
// week
|
|
|
for array_7 in bgArray_7_ {
|
|
|
- if units == .mmolL {
|
|
|
- sumOfSquares_7 += pow(Decimal(array_7).asMmolL - bg_7, 2)
|
|
|
- } else { sumOfSquares_7 += pow(Decimal(array_7) - bg_7, 2) }
|
|
|
+ sumOfSquares_7 += pow(Decimal(array_7) - bg_7, 2)
|
|
|
}
|
|
|
// month
|
|
|
for array_30 in bgArray_30_ {
|
|
|
- if units == .mmolL {
|
|
|
- sumOfSquares_30 += pow(Decimal(array_30).asMmolL - bg_30, 2)
|
|
|
- } else { sumOfSquares_30 += pow(Decimal(array_30) - bg_30, 2) }
|
|
|
+ sumOfSquares_30 += pow(Decimal(array_30) - bg_30, 2)
|
|
|
}
|
|
|
|
|
|
// Standard deviation and Coefficient of variation
|
|
|
@@ -1247,27 +1199,20 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
var cv_30 = 0.0
|
|
|
|
|
|
// Avoid division by zero
|
|
|
- if avgs.total < 1 || nr_bgs < 1 { sd_total = 0
|
|
|
- cv_total = 0 } else {
|
|
|
+ if bg_total > 0 {
|
|
|
sd_total = sqrt(Double(sumOfSquares / nr_bgs))
|
|
|
cv_total = sd_total / Double(bg_total) * 100
|
|
|
}
|
|
|
- if avgs.day < 1 || nr_bgs_1 < 1 {
|
|
|
- sd_1 = 0
|
|
|
- cv_1 = 0
|
|
|
- } else {
|
|
|
- sd_1 = sqrt(Double(sumOfSquares_1 / nr_bgs_1))
|
|
|
+ if bg_1 > 0 {
|
|
|
+ sd_1 = sqrt(Double(sumOfSquares_1) / Double(bgArray_1_.count))
|
|
|
cv_1 = sd_1 / Double(bg_1) * 100
|
|
|
}
|
|
|
- if avgs.week < 1 || nr_bgs_7 < 1 {
|
|
|
- sd_7 = 0
|
|
|
- cv_7 = 0
|
|
|
- } else {
|
|
|
- sd_7 = sqrt(Double(sumOfSquares_7 / nr_bgs_7))
|
|
|
+ if bg_7 > 0 {
|
|
|
+ sd_7 = sqrt(Double(sumOfSquares_7) / Double(bgArray_7_.count))
|
|
|
cv_7 = sd_7 / Double(bg_7) * 100
|
|
|
}
|
|
|
- if avgs.month < 1 || nr_bgs_30 < 1 { sd_30 = 0
|
|
|
- cv_30 = 0 } else { sd_30 = sqrt(Double(sumOfSquares_30 / nr_bgs_30))
|
|
|
+ if bg_30 > 0 {
|
|
|
+ sd_30 = sqrt(Double(sumOfSquares_30) / Double(bgArray_30_.count))
|
|
|
cv_30 = sd_30 / Double(bg_30) * 100
|
|
|
}
|
|
|
|
|
|
@@ -1305,7 +1250,7 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
insulinType: insulin_type.rawValue,
|
|
|
peakActivityTime: iPa,
|
|
|
Carbs_24h: carbTotal,
|
|
|
- GlucoseStorage_Days: Decimal(daysBG),
|
|
|
+ GlucoseStorage_Days: Decimal(roundDouble(numberOfDays, 1)),
|
|
|
Statistics: Stats(
|
|
|
Distribution: TimeInRange,
|
|
|
Glucose: avg,
|
|
|
@@ -1318,30 +1263,28 @@ final class BaseAPSManager: APSManager, Injectable {
|
|
|
|
|
|
storage.transaction { storage in
|
|
|
storage.append(dailystat, to: file, uniqBy: \.created_at)
|
|
|
- var uniqeEvents: [Statistics] = storage.retrieve(file, as: [Statistics].self)?
|
|
|
+ let uniqeEvents: [Statistics] = storage.retrieve(file, as: [Statistics].self)?
|
|
|
.filter { $0.created_at.addingTimeInterval(24.hours.timeInterval) > Date() }
|
|
|
.sorted { $0.created_at > $1.created_at } ?? []
|
|
|
-
|
|
|
storage.save(Array(uniqeEvents), as: file)
|
|
|
}
|
|
|
-
|
|
|
nightscout.uploadStatistics(dailystat: dailystat)
|
|
|
nightscout.uploadPreferences()
|
|
|
+ print("Test time of statistics computation: \(-1 * statisticsStartedAt.timeIntervalSinceNow) s")
|
|
|
}
|
|
|
|
|
|
private func loopStats(loopStatRecord: LoopStats) {
|
|
|
+ let LoopStatsStartedAt = Date()
|
|
|
let file = OpenAPS.Monitor.loopStats
|
|
|
-
|
|
|
var uniqEvents: [LoopStats] = []
|
|
|
-
|
|
|
storage.transaction { storage in
|
|
|
storage.append(loopStatRecord, to: file, uniqBy: \.start)
|
|
|
uniqEvents = storage.retrieve(file, as: [LoopStats].self)?
|
|
|
.filter { $0.start.addingTimeInterval(24.hours.timeInterval) > Date() }
|
|
|
.sorted { $0.start > $1.start } ?? []
|
|
|
-
|
|
|
storage.save(Array(uniqEvents), as: file)
|
|
|
}
|
|
|
+ print("Test time of LoopStats computation: \(-1 * LoopStatsStartedAt.timeIntervalSinceNow) s")
|
|
|
}
|
|
|
|
|
|
private func processError(_ error: Error) {
|