Просмотр исходного кода

Refactor

Dont filter in glucose storage. Use same format to speed up.
Move statistics function to not interfere with looping. 
Refactor tdd() function. 
Reduce and refactor statistcs() function. 

Version update
Jon B.M 3 лет назад
Родитель
Сommit
6fb246f68d

+ 1 - 1
Config.xcconfig

@@ -1,5 +1,5 @@
 APP_DISPLAY_NAME = FreeAPS X
-APP_VERSION = 1.0.2
+APP_VERSION = 1.0.4
 APP_BUILD_NUMBER = 1
 COPYRIGHT_NOTICE = 
 DEVELOPER_TEAM = ##TEAM_ID##

+ 2 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -2845,6 +2845,7 @@
 				PRODUCT_NAME = "${TARGET_NAME}";
 				SDKROOT = watchos;
 				SKIP_INSTALL = YES;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG RUN_STATISTICS";
 				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = 4;
@@ -2884,6 +2885,7 @@
 				PRODUCT_NAME = "${TARGET_NAME}";
 				SDKROOT = watchos;
 				SKIP_INSTALL = YES;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = RUN_STATISTICS;
 				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = 4;

+ 86 - 192
FreeAPS/Sources/APS/APSManager.swift

@@ -1,3 +1,4 @@
+import Accelerate
 import Combine
 import Foundation
 import LoopKit
@@ -239,11 +240,6 @@ final class BaseAPSManager: APSManager, Injectable {
 
         loopStats(loopStatRecord: loopStatRecord)
 
-        // Create a statistics.json
-        if settings.displayStatistics {
-            statistics()
-        }
-
         if settings.closedLoop {
             reportEnacted(received: error == nil)
         }
@@ -662,9 +658,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,10 +665,18 @@ 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
@@ -685,39 +686,24 @@ final class BaseAPSManager: APSManager, Injectable {
             timestamp: Date(),
             id: UUID().uuidString
         )
+
+        var allTDD: [Decimal] = [0]
         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() }
+                .filter { $0.timestamp.addingTimeInterval(14.days.timeInterval) > Date() && $0.TDD != 0 }
                 .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)?
+
+            var totalAmount: [Decimal] = [0]
+            allTDD = uniqEvents.map({ allTDD in allTDD.TDD }) // .reduce(0, +)
+            let average14 = allTDD.reduce(0, +) / Decimal(allTDD.count)
+
+            let entriesPast2hours = uniqEvents // 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
-            }
-            let average14 = total / indeces
-            let average2hours = totalAmount / nrOfIndeces
+            totalAmount = entriesPast2hours.map({ totalAmount in totalAmount.TDD }) // .reduce(0, +)
+            let average2hours = totalAmount.reduce(0, +) / Decimal(totalAmount.count)
+
             let weight = preferences.weightPercentage
             let weighted_average = weight * average2hours + (1 - weight) * average14
             let averages = TDD_averages(
@@ -729,6 +715,7 @@ final class BaseAPSManager: APSManager, Injectable {
             storage.save(averages, as: OpenAPS.Monitor.tdd_averages)
             storage.save(Array(uniqEvents), as: file)
         }
+        print("Test time of tdd() computation: \(-1 * tddStartedAt.timeIntervalSinceNow) s")
     }
 
     private func roundDecimal(_ decimal: Decimal, _ digits: Double) -> Decimal {
@@ -756,16 +743,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 {
@@ -781,17 +767,9 @@ final class BaseAPSManager: APSManager, Injectable {
         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
-                }
-            }
-        }
-        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 +820,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 +831,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 +869,69 @@ final class BaseAPSManager: APSManager, Injectable {
         if minimumLoopTime == 9999.0 {
             minimumLoopTime = 0.0
         }
+
+        // Filter glucose storage to 90 days
+        let uniqEvents_ = storage.retrieve(OpenAPS.Monitor.glucose_data, as: [BloodGlucose].self)?
+            .filter { $0.dateString.addingTimeInterval(90.days.timeInterval) > Date() }
+            .sorted { $0.dateString > $1.dateString } ?? []
+        let glucose = Array(uniqEvents_)
+        storage.save(glucose, as: OpenAPS.Monitor.glucose_data)
+
         // 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].dateString
         }
-        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))
+                if entry.glucose ?? 0 > 0 {
+                    bg += Decimal(entry.glucose ?? 0) * conversionFactor
+                    bgArray.append(Double(entry.glucose ?? 0) * Double(conversionFactor))
+                    bgArrayForTIR.append((Double(entry.glucose ?? 0), entry.dateString))
                     nr_bgs += 1
-
-                    if (startDate! - entry.date).timeInterval >= 8.64E4, !end1 {
-                        end1 = true
+                    if (startDate! - entry.dateString).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.dateString).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.dateString).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,14 +940,6 @@ 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
@@ -988,7 +948,7 @@ final class BaseAPSManager: APSManager, Injectable {
         var fullTime = 0.0
 
         if length_ > 0 {
-            fullTime = (startDate! - glucose![endIndex].date).timeInterval
+            fullTime = (startDate! - glucose[endIndex].dateString).timeInterval
             daysBG = fullTime / 8.64E4
         }
 
@@ -1000,24 +960,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 +1000,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 +1043,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(
@@ -1137,10 +1077,10 @@ final class BaseAPSManager: APSManager, Injectable {
             median_interval: roundDecimal(Decimal(medianInterval), 1),
             min_interval: roundDecimal(Decimal(minimumInt), 1),
             max_interval: roundDecimal(Decimal(maximumInt), 1),
-            avg_duration: Decimal(roundDouble(averageLoopTime, 2)),
-            median_duration: Decimal(roundDouble(medianLoopTime, 2)),
-            min_duration: roundDecimal(Decimal(minimumLoopTime), 2),
-            max_duration: Decimal(roundDouble(maximumLoopTime, 1))
+            avg_duration: Decimal(roundDouble(averageLoopTime * 60, 2)), // Use seconds
+            median_duration: Decimal(roundDouble(medianLoopTime * 60, 2)), // Use seconds
+            min_duration: roundDecimal(Decimal(minimumLoopTime * 60), 2), // Use seconds
+            max_duration: Decimal(roundDouble(maximumLoopTime * 60, 1)) // Use seconds
         )
 
         // TIR calcs for every case
@@ -1150,16 +1090,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,38 +1138,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
-        var sumOfSquares_30: Decimal = 0
-
-        // Total
-        for array in bgArray {
-            if units == .mmolL {
-                sumOfSquares += pow(Decimal(array).asMmolL - bg_total, 2)
-            } else { 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) }
-        }
-        // 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) }
-        }
-        // 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) }
-        }
-
         // Standard deviation and Coefficient of variation
         var sd_total = 0.0
         var cv_total = 0.0
@@ -1247,27 +1149,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 {
-            sd_total = sqrt(Double(sumOfSquares / nr_bgs))
+        if bg_total > 0 {
+            sd_total = sqrt(vDSP.rootMeanSquare(bgArray))
             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(vDSP.rootMeanSquare(bgArray_1_))
             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(vDSP.rootMeanSquare(bgArray_7_))
             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(vDSP.rootMeanSquare(bgArray_30_))
             cv_30 = sd_30 / Double(bg_30) * 100
         }
 
@@ -1306,6 +1201,7 @@ final class BaseAPSManager: APSManager, Injectable {
             peakActivityTime: iPa,
             Carbs_24h: carbTotal,
             GlucoseStorage_Days: Decimal(daysBG),
+            TimeOfComputation: Decimal(-1 * roundDouble(statisticsStartedAt.timeIntervalSinceNow, 1)),
             Statistics: Stats(
                 Distribution: TimeInRange,
                 Glucose: avg,
@@ -1318,30 +1214,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()
+        // 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) {

+ 1 - 1
FreeAPS/Sources/APS/OpenAPS/Constants.swift

@@ -59,7 +59,7 @@ extension OpenAPS {
         static let alertHistory = "monitor/alerthistory.json"
         static let statistics = "monitor/statistics.json"
         static let loopStats = "monitor/loopStats.json"
-        static let glucose_data = "monitor/glucoseForStats.json"
+        static let glucose_data = "monitor/glucose_date.json"
     }
 
     enum Enact {

+ 3 - 18
FreeAPS/Sources/APS/Storage/GlucoseStorage.swift

@@ -37,6 +37,9 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
             let file = OpenAPS.Monitor.glucose
             self.storage.transaction { storage in
                 storage.append(glucose, to: file, uniqBy: \.dateString)
+                // Save for statistics also
+                storage.append(glucose, to: OpenAPS.Monitor.glucose_data, uniqBy: \.dateString)
+
                 let uniqEvents = storage.retrieve(file, as: [BloodGlucose].self)?
                     .filter { $0.dateString.addingTimeInterval(24.hours.timeInterval) > Date() }
                     .sorted { $0.dateString > $1.dateString } ?? []
@@ -48,24 +51,6 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
                         $0.glucoseDidUpdate(glucose.reversed())
                     }
                 }
-
-                // Save to glucoseForStats also.
-                var bg_ = 0
-                var bgDate = Date()
-
-                if glucose.isNotEmpty {
-                    bg_ = glucose[0].glucose ?? 0
-                    bgDate = glucose[0].dateString
-                }
-                if bg_ != 0 {
-                    let dataForStats = GlucoseDataForStats(date: bgDate, glucose: bg_)
-                    storage.append(dataForStats, to: OpenAPS.Monitor.glucose_data, uniqBy: \.date)
-                    let uniqEvents_1 = storage.retrieve(OpenAPS.Monitor.glucose_data, as: [GlucoseDataForStats].self)?
-                        .filter { $0.date.addingTimeInterval(90.days.timeInterval) > Date() }
-                        .sorted { $0.date > $1.date } ?? []
-                    let dataForStats_ = Array(uniqEvents_1)
-                    storage.save(dataForStats_, as: OpenAPS.Monitor.glucose_data)
-                }
             }
 
             self.storage.transaction { storage in

+ 4 - 0
FreeAPS/Sources/Models/Statistics.swift

@@ -17,6 +17,7 @@ struct Statistics: JSON, Equatable {
     var peakActivityTime: Decimal
     var Carbs_24h: Decimal
     var GlucoseStorage_Days: Decimal
+    var TimeOfComputation: Decimal
     var Statistics: Stats
 
     init(
@@ -36,6 +37,7 @@ struct Statistics: JSON, Equatable {
         peakActivityTime: Decimal,
         Carbs_24h: Decimal,
         GlucoseStorage_Days: Decimal,
+        TimeOfComputation: Decimal,
         Statistics: Stats
     ) {
         self.created_at = created_at
@@ -54,6 +56,7 @@ struct Statistics: JSON, Equatable {
         self.peakActivityTime = peakActivityTime
         self.Carbs_24h = Carbs_24h
         self.GlucoseStorage_Days = GlucoseStorage_Days
+        self.TimeOfComputation = TimeOfComputation
         self.Statistics = Statistics
     }
 
@@ -84,6 +87,7 @@ extension Statistics {
         case peakActivityTime
         case Carbs_24h
         case GlucoseStorage_Days
+        case TimeOfComputation
         case Statistics
     }
 }