Przeglądaj źródła

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 lat temu
rodzic
commit
6fb246f68d

+ 1 - 1
Config.xcconfig

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

+ 2 - 0
FreeAPS.xcodeproj/project.pbxproj

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

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

@@ -1,3 +1,4 @@
+import Accelerate
 import Combine
 import Combine
 import Foundation
 import Foundation
 import LoopKit
 import LoopKit
@@ -239,11 +240,6 @@ final class BaseAPSManager: APSManager, Injectable {
 
 
         loopStats(loopStatRecord: loopStatRecord)
         loopStats(loopStatRecord: loopStatRecord)
 
 
-        // Create a statistics.json
-        if settings.displayStatistics {
-            statistics()
-        }
-
         if settings.closedLoop {
         if settings.closedLoop {
             reportEnacted(received: error == nil)
             reportEnacted(received: error == nil)
         }
         }
@@ -662,9 +658,6 @@ final class BaseAPSManager: APSManager, Injectable {
 
 
             storage.save(enacted, as: OpenAPS.Enact.enacted)
             storage.save(enacted, as: OpenAPS.Enact.enacted)
 
 
-            // Create a tdd.json
-            tdd(enacted_: enacted)
-
             debug(.apsManager, "Suggestion enacted. Received: \(received)")
             debug(.apsManager, "Suggestion enacted. Received: \(received)")
             DispatchQueue.main.async {
             DispatchQueue.main.async {
                 self.broadcaster.notify(EnactedSuggestionObserver.self, on: .main) {
                 self.broadcaster.notify(EnactedSuggestionObserver.self, on: .main) {
@@ -672,10 +665,18 @@ final class BaseAPSManager: APSManager, Injectable {
                 }
                 }
             }
             }
             nightscout.uploadStatus()
             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) {
     private func tdd(enacted_: Suggestion) {
+        let tddStartedAt = Date()
         // Add to tdd.json:
         // Add to tdd.json:
         let preferences = settingsManager.preferences
         let preferences = settingsManager.preferences
         let currentTDD = enacted_.tdd ?? 0
         let currentTDD = enacted_.tdd ?? 0
@@ -685,39 +686,24 @@ final class BaseAPSManager: APSManager, Injectable {
             timestamp: Date(),
             timestamp: Date(),
             id: UUID().uuidString
             id: UUID().uuidString
         )
         )
+
+        var allTDD: [Decimal] = [0]
         var uniqEvents: [TDD] = []
         var uniqEvents: [TDD] = []
         storage.transaction { storage in
         storage.transaction { storage in
             storage.append(tdd, to: file, uniqBy: \.id)
             storage.append(tdd, to: file, uniqBy: \.id)
             uniqEvents = storage.retrieve(file, as: [TDD].self)?
             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 } ?? []
                 .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() }
                 .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 weight = preferences.weightPercentage
             let weighted_average = weight * average2hours + (1 - weight) * average14
             let weighted_average = weight * average2hours + (1 - weight) * average14
             let averages = TDD_averages(
             let averages = TDD_averages(
@@ -729,6 +715,7 @@ final class BaseAPSManager: APSManager, Injectable {
             storage.save(averages, as: OpenAPS.Monitor.tdd_averages)
             storage.save(averages, as: OpenAPS.Monitor.tdd_averages)
             storage.save(Array(uniqEvents), as: file)
             storage.save(Array(uniqEvents), as: file)
         }
         }
+        print("Test time of tdd() computation: \(-1 * tddStartedAt.timeIntervalSinceNow) s")
     }
     }
 
 
     private func roundDecimal(_ decimal: Decimal, _ digits: Double) -> Decimal {
     private func roundDecimal(_ decimal: Decimal, _ digits: Double) -> Decimal {
@@ -756,16 +743,15 @@ final class BaseAPSManager: APSManager, Injectable {
 
 
     // Add to statistics.JSON
     // Add to statistics.JSON
     private func statistics() {
     private func statistics() {
+        let statisticsStartedAt = Date()
         var testFile: [Statistics] = []
         var testFile: [Statistics] = []
         var testIfEmpty = 0
         var testIfEmpty = 0
         storage.transaction { storage in
         storage.transaction { storage in
             testFile = storage.retrieve(OpenAPS.Monitor.statistics, as: [Statistics].self) ?? []
             testFile = storage.retrieve(OpenAPS.Monitor.statistics, as: [Statistics].self) ?? []
             testIfEmpty = testFile.count
             testIfEmpty = testFile.count
         }
         }
-
         let updateThisOften = Int(settingsManager.preferences.updateInterval)
         let updateThisOften = Int(settingsManager.preferences.updateInterval)
-
-        // Only run every 30 minutesl
+        // Only run every 30 minutes of according to setting.
         if testIfEmpty != 0 {
         if testIfEmpty != 0 {
             guard testFile[0].created_at.addingTimeInterval(updateThisOften.minutes.timeInterval) < Date()
             guard testFile[0].created_at.addingTimeInterval(updateThisOften.minutes.timeInterval) < Date()
             else {
             else {
@@ -781,17 +767,9 @@ final class BaseAPSManager: APSManager, Injectable {
         if tdds?.count ?? 0 > 0 {
         if tdds?.count ?? 0 > 0 {
             currentTDD = tdds?[0].TDD ?? 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 {
         if preferences.sigmoid, preferences.enableDynamicCR {
             algo_ = "Dynamic ISF + CR: Sigmoid"
             algo_ = "Dynamic ISF + CR: Sigmoid"
         } else if preferences.sigmoid, !preferences.enableDynamicCR {
         } else if preferences.sigmoid, !preferences.enableDynamicCR {
@@ -842,11 +820,9 @@ final class BaseAPSManager: APSManager, Injectable {
 
 
         if !lsData.isEmpty {
         if !lsData.isEmpty {
             var i = 0.0
             var i = 0.0
-
             if let loopEnd = lsData[0].end {
             if let loopEnd = lsData[0].end {
                 previousTimeLoop = loopEnd
                 previousTimeLoop = loopEnd
             }
             }
-
             for each in lsData {
             for each in lsData {
                 if let loopEnd = each.end, let loopDuration = each.duration {
                 if let loopEnd = each.end, let loopDuration = each.duration {
                     if each.loopStatus.contains("Success") {
                     if each.loopStatus.contains("Success") {
@@ -855,36 +831,30 @@ final class BaseAPSManager: APSManager, Injectable {
                         errorNR += 1
                         errorNR += 1
                     }
                     }
                     i += 1
                     i += 1
-
                     timeIntervalLoops = (previousTimeLoop - each.start).timeInterval / 60
                     timeIntervalLoops = (previousTimeLoop - each.start).timeInterval / 60
+
                     if timeIntervalLoops > 0.0, i != 1 {
                     if timeIntervalLoops > 0.0, i != 1 {
                         timeIntervalLoopArray.append(timeIntervalLoops)
                         timeIntervalLoopArray.append(timeIntervalLoops)
                     }
                     }
-
                     if timeIntervalLoops > maximumInt {
                     if timeIntervalLoops > maximumInt {
                         maximumInt = timeIntervalLoops
                         maximumInt = timeIntervalLoops
                     }
                     }
                     if timeIntervalLoops < minimumInt, i != 1 {
                     if timeIntervalLoops < minimumInt, i != 1 {
                         minimumInt = timeIntervalLoops
                         minimumInt = timeIntervalLoops
                     }
                     }
-
                     timeForOneLoop = loopDuration
                     timeForOneLoop = loopDuration
-
                     timeForOneLoopArray.append(timeForOneLoop)
                     timeForOneLoopArray.append(timeForOneLoop)
                     averageLoopTime += timeForOneLoop
                     averageLoopTime += timeForOneLoop
 
 
                     if timeForOneLoop >= maximumLoopTime, timeForOneLoop != 0.0 {
                     if timeForOneLoop >= maximumLoopTime, timeForOneLoop != 0.0 {
                         maximumLoopTime = timeForOneLoop
                         maximumLoopTime = timeForOneLoop
                     }
                     }
-
                     if timeForOneLoop <= minimumLoopTime, timeForOneLoop != 0.0 {
                     if timeForOneLoop <= minimumLoopTime, timeForOneLoop != 0.0 {
                         minimumLoopTime = timeForOneLoop
                         minimumLoopTime = timeForOneLoop
                     }
                     }
-
                     previousTimeLoop = loopEnd
                     previousTimeLoop = loopEnd
                 }
                 }
             }
             }
-
             successRate = (successNR / Double(i)) * 100
             successRate = (successNR / Double(i)) * 100
             averageIntervalLoops = ((lsData[0].end ?? lsData[lsData.count - 1].start) - lsData[lsData.count - 1].start)
             averageIntervalLoops = ((lsData[0].end ?? lsData[lsData.count - 1].start) - lsData[lsData.count - 1].start)
                 .timeInterval / 60 / Double(i)
                 .timeInterval / 60 / Double(i)
@@ -899,71 +869,69 @@ final class BaseAPSManager: APSManager, Injectable {
         if minimumLoopTime == 9999.0 {
         if minimumLoopTime == 9999.0 {
             minimumLoopTime = 0.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.
         // 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
         let endIndex = length_ - 1
         var bg: Decimal = 0
         var bg: Decimal = 0
+
         var bgArray: [Double] = []
         var bgArray: [Double] = []
         var bgArray_1_: [Double] = []
         var bgArray_1_: [Double] = []
         var bgArray_7_: [Double] = []
         var bgArray_7_: [Double] = []
         var bgArray_30_: [Double] = []
         var bgArray_30_: [Double] = []
+
         var bgArrayForTIR: [(bg_: Double, date_: Date)] = []
         var bgArrayForTIR: [(bg_: Double, date_: Date)] = []
         var bgArray_1: [(bg_: Double, date_: Date)] = []
         var bgArray_1: [(bg_: Double, date_: Date)] = []
         var bgArray_7: [(bg_: Double, date_: Date)] = []
         var bgArray_7: [(bg_: Double, date_: Date)] = []
         var bgArray_30: [(bg_: Double, date_: Date)] = []
         var bgArray_30: [(bg_: Double, date_: Date)] = []
+
         var medianBG = 0.0
         var medianBG = 0.0
         var nr_bgs: Decimal = 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")
         var startDate = Date("1978-02-22T11:43:54.659Z")
         if endIndex >= 0 {
         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_1: Decimal = 0
         var bg_7: Decimal = 0
         var bg_7: Decimal = 0
         var bg_30: Decimal = 0
         var bg_30: Decimal = 0
         var bg_total: Decimal = 0
         var bg_total: Decimal = 0
         var j = -1
         var j = -1
+        var conversionFactor: Decimal = 1
+        if units == .mmolL {
+            conversionFactor = 0.0555
+        }
 
 
         // Make arrays for median calculations and calculate averages
         // Make arrays for median calculations and calculate averages
         if endIndex >= 0 {
         if endIndex >= 0 {
-            for entry in glucose! {
+            for entry in glucose {
                 j += 1
                 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
                     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
                         bg_1 = bg / nr_bgs
                         bgArray_1 = bgArrayForTIR
                         bgArray_1 = bgArrayForTIR
                         bgArray_1_ = bgArray
                         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
                         bg_7 = bg / nr_bgs
                         bgArray_7 = bgArrayForTIR
                         bgArray_7 = bgArrayForTIR
                         bgArray_7_ = bgArray
                         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
                         bg_30 = bg / nr_bgs
                         bgArray_30 = bgArrayForTIR
                         bgArray_30 = bgArrayForTIR
                         bgArray_30_ = bgArray
                         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 {
         if nr_bgs > 0 {
             // Up to 91 days
             // Up to 91 days
             bg_total = bg / nr_bgs
             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
         // Total median
@@ -988,7 +948,7 @@ final class BaseAPSManager: APSManager, Injectable {
         var fullTime = 0.0
         var fullTime = 0.0
 
 
         if length_ > 0 {
         if length_ > 0 {
-            fullTime = (startDate! - glucose![endIndex].date).timeInterval
+            fullTime = (startDate! - glucose[endIndex].dateString).timeInterval
             daysBG = fullTime / 8.64E4
             daysBG = fullTime / 8.64E4
         }
         }
 
 
@@ -1000,24 +960,20 @@ final class BaseAPSManager: APSManager, Injectable {
             var i = -1
             var i = -1
             var lastIndex = false
             var lastIndex = false
             let endIndex = array.count - 1
             let endIndex = array.count - 1
-
             var hypoLimit = settingsManager.preferences.low
             var hypoLimit = settingsManager.preferences.low
             var hyperLimit = settingsManager.preferences.high
             var hyperLimit = settingsManager.preferences.high
             if units == .mmolL {
             if units == .mmolL {
                 hypoLimit = hypoLimit / 0.0555
                 hypoLimit = hypoLimit / 0.0555
                 hyperLimit = hyperLimit / 0.0555
                 hyperLimit = hyperLimit / 0.0555
             }
             }
-
             var full_time = 0.0
             var full_time = 0.0
             if endIndex > 0 {
             if endIndex > 0 {
                 full_time = (array[0].date_ - array[endIndex].date_).timeInterval
                 full_time = (array[0].date_ - array[endIndex].date_).timeInterval
             }
             }
-
             while i < endIndex {
             while i < endIndex {
                 i += 1
                 i += 1
                 let currentTime = array[i].date_
                 let currentTime = array[i].date_
                 var previousTime = currentTime
                 var previousTime = currentTime
-
                 if i + 1 <= endIndex {
                 if i + 1 <= endIndex {
                     previousTime = array[i + 1].date_
                     previousTime = array[i + 1].date_
                 } else {
                 } else {
@@ -1044,43 +1000,40 @@ final class BaseAPSManager: APSManager, Injectable {
         // HbA1c estimation (%, mmol/mol) 1 day
         // HbA1c estimation (%, mmol/mol) 1 day
         var NGSPa1CStatisticValue: Decimal = 0.0
         var NGSPa1CStatisticValue: Decimal = 0.0
         var IFCCa1CStatisticValue: 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 *
             IFCCa1CStatisticValue = 10.929 *
                 (NGSPa1CStatisticValue - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
                 (NGSPa1CStatisticValue - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
         }
         }
         // 7 days
         // 7 days
         var NGSPa1CStatisticValue_7: Decimal = 0.0
         var NGSPa1CStatisticValue_7: Decimal = 0.0
         var IFCCa1CStatisticValue_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
         // 30 days
         var NGSPa1CStatisticValue_30: Decimal = 0.0
         var NGSPa1CStatisticValue_30: Decimal = 0.0
         var IFCCa1CStatisticValue_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
         // Total days
         var NGSPa1CStatisticValue_total: Decimal = 0.0
         var NGSPa1CStatisticValue_total: Decimal = 0.0
         var IFCCa1CStatisticValue_total: Decimal = 0.0
         var IFCCa1CStatisticValue_total: Decimal = 0.0
         if nr_bgs > 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 *
             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)
             total: roundDecimal(Decimal(medianBG), 1)
         )
         )
-
         var hbs = Durations(
         var hbs = Durations(
             day: roundDecimal(NGSPa1CStatisticValue, 1),
             day: roundDecimal(NGSPa1CStatisticValue, 1),
             week: roundDecimal(NGSPa1CStatisticValue_7, 1),
             week: roundDecimal(NGSPa1CStatisticValue_7, 1),
@@ -1090,20 +1043,7 @@ final class BaseAPSManager: APSManager, Injectable {
 
 
         // Convert to user-preferred unit
         // Convert to user-preferred unit
         let overrideHbA1cUnit = settingsManager.preferences.overrideHbA1cUnit
         let overrideHbA1cUnit = settingsManager.preferences.overrideHbA1cUnit
-
         if units == .mmolL {
         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
             // Override if users sets overrideHbA1cUnit: true
             if !overrideHbA1cUnit {
             if !overrideHbA1cUnit {
                 hbs = Durations(
                 hbs = Durations(
@@ -1137,10 +1077,10 @@ final class BaseAPSManager: APSManager, Injectable {
             median_interval: roundDecimal(Decimal(medianInterval), 1),
             median_interval: roundDecimal(Decimal(medianInterval), 1),
             min_interval: roundDecimal(Decimal(minimumInt), 1),
             min_interval: roundDecimal(Decimal(minimumInt), 1),
             max_interval: roundDecimal(Decimal(maximumInt), 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
         // 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)
         var totalDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
 
 
         // Get all TIR calcs for every case
         // Get all TIR calcs for every case
-        if end1 {
+        if nr_bgs > 0 {
             oneDay_ = tir(bgArray_1)
             oneDay_ = tir(bgArray_1)
-        }
-        if end7 {
             sevenDays_ = tir(bgArray_7)
             sevenDays_ = tir(bgArray_7)
-        }
-        if end30 {
             thirtyDays_ = tir(bgArray_30)
             thirtyDays_ = tir(bgArray_30)
-        }
-        if nr_bgs > 0 {
             totalDays_ = tir(bgArrayForTIR)
             totalDays_ = tir(bgArrayForTIR)
         }
         }
 
 
@@ -1204,38 +1138,6 @@ final class BaseAPSManager: APSManager, Injectable {
             scheduled_basal: suggestion?.insulin?.scheduled_basal ?? 0
             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
         // Standard deviation and Coefficient of variation
         var sd_total = 0.0
         var sd_total = 0.0
         var cv_total = 0.0
         var cv_total = 0.0
@@ -1247,27 +1149,20 @@ final class BaseAPSManager: APSManager, Injectable {
         var cv_30 = 0.0
         var cv_30 = 0.0
 
 
         // Avoid division by zero
         // 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
             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
             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
             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
             cv_30 = sd_30 / Double(bg_30) * 100
         }
         }
 
 
@@ -1306,6 +1201,7 @@ final class BaseAPSManager: APSManager, Injectable {
             peakActivityTime: iPa,
             peakActivityTime: iPa,
             Carbs_24h: carbTotal,
             Carbs_24h: carbTotal,
             GlucoseStorage_Days: Decimal(daysBG),
             GlucoseStorage_Days: Decimal(daysBG),
+            TimeOfComputation: Decimal(-1 * roundDouble(statisticsStartedAt.timeIntervalSinceNow, 1)),
             Statistics: Stats(
             Statistics: Stats(
                 Distribution: TimeInRange,
                 Distribution: TimeInRange,
                 Glucose: avg,
                 Glucose: avg,
@@ -1318,30 +1214,28 @@ final class BaseAPSManager: APSManager, Injectable {
 
 
         storage.transaction { storage in
         storage.transaction { storage in
             storage.append(dailystat, to: file, uniqBy: \.created_at)
             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() }
                 .filter { $0.created_at.addingTimeInterval(24.hours.timeInterval) > Date() }
                 .sorted { $0.created_at > $1.created_at } ?? []
                 .sorted { $0.created_at > $1.created_at } ?? []
-
             storage.save(Array(uniqeEvents), as: file)
             storage.save(Array(uniqeEvents), as: file)
         }
         }
-
         nightscout.uploadStatistics(dailystat: dailystat)
         nightscout.uploadStatistics(dailystat: dailystat)
-        nightscout.uploadPreferences()
+        // nightscout.uploadPreferences()
+        print("Test time of statistics computation: \(-1 * statisticsStartedAt.timeIntervalSinceNow) s")
     }
     }
 
 
     private func loopStats(loopStatRecord: LoopStats) {
     private func loopStats(loopStatRecord: LoopStats) {
+        let LoopStatsStartedAt = Date()
         let file = OpenAPS.Monitor.loopStats
         let file = OpenAPS.Monitor.loopStats
-
         var uniqEvents: [LoopStats] = []
         var uniqEvents: [LoopStats] = []
-
         storage.transaction { storage in
         storage.transaction { storage in
             storage.append(loopStatRecord, to: file, uniqBy: \.start)
             storage.append(loopStatRecord, to: file, uniqBy: \.start)
             uniqEvents = storage.retrieve(file, as: [LoopStats].self)?
             uniqEvents = storage.retrieve(file, as: [LoopStats].self)?
                 .filter { $0.start.addingTimeInterval(24.hours.timeInterval) > Date() }
                 .filter { $0.start.addingTimeInterval(24.hours.timeInterval) > Date() }
                 .sorted { $0.start > $1.start } ?? []
                 .sorted { $0.start > $1.start } ?? []
-
             storage.save(Array(uniqEvents), as: file)
             storage.save(Array(uniqEvents), as: file)
         }
         }
+        print("Test time of LoopStats computation: \(-1 * LoopStatsStartedAt.timeIntervalSinceNow) s")
     }
     }
 
 
     private func processError(_ error: Error) {
     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 alertHistory = "monitor/alerthistory.json"
         static let statistics = "monitor/statistics.json"
         static let statistics = "monitor/statistics.json"
         static let loopStats = "monitor/loopStats.json"
         static let loopStats = "monitor/loopStats.json"
-        static let glucose_data = "monitor/glucoseForStats.json"
+        static let glucose_data = "monitor/glucose_date.json"
     }
     }
 
 
     enum Enact {
     enum Enact {

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

@@ -37,6 +37,9 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
             let file = OpenAPS.Monitor.glucose
             let file = OpenAPS.Monitor.glucose
             self.storage.transaction { storage in
             self.storage.transaction { storage in
                 storage.append(glucose, to: file, uniqBy: \.dateString)
                 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)?
                 let uniqEvents = storage.retrieve(file, as: [BloodGlucose].self)?
                     .filter { $0.dateString.addingTimeInterval(24.hours.timeInterval) > Date() }
                     .filter { $0.dateString.addingTimeInterval(24.hours.timeInterval) > Date() }
                     .sorted { $0.dateString > $1.dateString } ?? []
                     .sorted { $0.dateString > $1.dateString } ?? []
@@ -48,24 +51,6 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
                         $0.glucoseDidUpdate(glucose.reversed())
                         $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
             self.storage.transaction { storage in

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

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