فهرست منبع

Merge 'bdb_dev' into bdb

Jon B.M 3 سال پیش
والد
کامیت
ab403f4551
39فایلهای تغییر یافته به همراه740 افزوده شده و 329 حذف شده
  1. 4 0
      BGaverages+CoreDataClass.swift
  2. 15 0
      BGaverages+CoreDataProperties.swift
  3. 4 0
      BGmedian+CoreDataClass.swift
  4. 15 0
      BGmedian+CoreDataProperties.swift
  5. 4 0
      Carbohydrates+CoreDataClass.swift
  6. 12 0
      Carbohydrates+CoreDataProperties.swift
  7. 1 1
      Config.xcconfig
  8. 58 0
      Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents
  9. 5 5
      Dependencies/rileylink_ios/MinimedKitUI/tr.lproj/Localizable.strings
  10. 2 2
      Dependencies/rileylink_ios/RileyLink.xcodeproj/project.pbxproj
  11. 23 8
      FreeAPS.xcodeproj/project.pbxproj
  12. 61 0
      FreeAPS.xcworkspace/contents.xcworkspacedata
  13. 1 1
      FreeAPS/Resources/javascript/bundle/determine-basal.js
  14. 1 1
      FreeAPS/Resources/javascript/middleware/determine_basal.js
  15. 5 4
      FreeAPS/Resources/javascript/prepare/determine-basal.js
  16. 289 254
      FreeAPS/Sources/APS/APSManager.swift
  17. 1 3
      FreeAPS/Sources/APS/OpenAPS/Constants.swift
  18. 27 4
      FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift
  19. 23 0
      FreeAPS/Sources/APS/Storage/CarbsStorage.swift
  20. 18 8
      FreeAPS/Sources/APS/Storage/GlucoseStorage.swift
  21. 36 0
      FreeAPS/Sources/Helpers/CoreDataStack.swift
  22. 1 1
      FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings
  23. 1 1
      FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings
  24. 0 6
      FreeAPS/Sources/Models/GlucoseDataForStats.swift
  25. 0 25
      FreeAPS/Sources/Models/TDD.swift
  26. 0 4
      FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift
  27. 1 1
      FreeAPS/Sources/Services/Storage/Disk/Disk+Codable.swift
  28. 4 0
      HbA1c+CoreDataClass.swift
  29. 15 0
      HbA1c+CoreDataProperties.swift
  30. 4 0
      InsulinDistribution+CoreDataClass.swift
  31. 14 0
      InsulinDistribution+CoreDataProperties.swift
  32. 4 0
      LoopStatRecord+CoreDataClass.swift
  33. 13 0
      LoopStatRecord+CoreDataProperties.swift
  34. 4 0
      Oref0Suggestion+CoreDataClass.swift
  35. 43 0
      Oref0Suggestion+CoreDataProperties.swift
  36. 4 0
      Readings+CoreDataClass.swift
  37. 11 0
      Readings+CoreDataProperties.swift
  38. 4 0
      TDD+CoreDataClass.swift
  39. 12 0
      TDD+CoreDataProperties.swift

+ 4 - 0
BGaverages+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(BGaverages) public class BGaverages: NSManagedObject {}

+ 15 - 0
BGaverages+CoreDataProperties.swift

@@ -0,0 +1,15 @@
+import CoreData
+import Foundation
+
+public extension BGaverages {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<BGaverages> {
+        NSFetchRequest<BGaverages>(entityName: "BGaverages")
+    }
+
+    @NSManaged var date: Date?
+    @NSManaged var average: NSDecimalNumber?
+    @NSManaged var average_1: NSDecimalNumber?
+    @NSManaged var average_7: NSDecimalNumber?
+    @NSManaged var average_30: NSDecimalNumber?
+    @NSManaged var average_90: NSDecimalNumber?
+}

+ 4 - 0
BGmedian+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(BGmedian) public class BGmedian: NSManagedObject {}

+ 15 - 0
BGmedian+CoreDataProperties.swift

@@ -0,0 +1,15 @@
+import CoreData
+import Foundation
+
+public extension BGmedian {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<BGmedian> {
+        NSFetchRequest<BGmedian>(entityName: "BGmedian")
+    }
+
+    @NSManaged var date: Date?
+    @NSManaged var median: NSDecimalNumber?
+    @NSManaged var median_1: NSDecimalNumber?
+    @NSManaged var median_30: NSDecimalNumber?
+    @NSManaged var median_90: NSDecimalNumber?
+    @NSManaged var median_7: NSDecimalNumber?
+}

+ 4 - 0
Carbohydrates+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(Carbohydrates) public class Carbohydrates: NSManagedObject {}

+ 12 - 0
Carbohydrates+CoreDataProperties.swift

@@ -0,0 +1,12 @@
+import CoreData
+import Foundation
+
+public extension Carbohydrates {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<Carbohydrates> {
+        NSFetchRequest<Carbohydrates>(entityName: "Carbohydrates")
+    }
+
+    @NSManaged var date: Date?
+    @NSManaged var carbs: NSDecimalNumber?
+    @NSManaged var enteredBy: String?
+}

+ 1 - 1
Config.xcconfig

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

+ 58 - 0
Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22C65" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
+    <entity name="BGaverages" representedClassName="BGaverages" syncable="YES" codeGenerationType="class">
+        <attribute name="average" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="average_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="average_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="average_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="average_90" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+    </entity>
+    <entity name="BGmedian" representedClassName="BGmedian" syncable="YES" codeGenerationType="class">
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="median" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="median_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="median_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="median_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="median_90" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+    </entity>
+    <entity name="Carbohydrates" representedClassName="Carbohydrates" syncable="YES" codeGenerationType="class">
+        <attribute name="carbs" optional="YES" attributeType="Decimal" defaultValueString="0"/>
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="enteredBy" optional="YES" attributeType="String"/>
+    </entity>
+    <entity name="HbA1c" representedClassName="HbA1c" syncable="YES" codeGenerationType="class">
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="hba1c" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="hba1c_1" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="hba1c_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="hba1c_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="hba1c_90" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+    </entity>
+    <entity name="InsulinDistribution" representedClassName="InsulinDistribution" syncable="YES" codeGenerationType="class">
+        <attribute name="bolus" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="scheduledBasal" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="tempBasal" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <relationship name="insulin" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Oref0Suggestion" inverseName="computedInsulinDistribution" inverseEntity="Oref0Suggestion"/>
+    </entity>
+    <entity name="LoopStatRecord" representedClassName="LoopStatRecord" syncable="YES" codeGenerationType="class">
+        <attribute name="duration" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
+        <attribute name="end" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="loopStatus" optional="YES" attributeType="String"/>
+        <attribute name="start" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+    </entity>
+    <entity name="Oref0Suggestion" representedClassName="Oref0Suggestion" syncable="YES" codeGenerationType="class">
+        <relationship name="computedInsulinDistribution" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="InsulinDistribution" inverseName="insulin" inverseEntity="InsulinDistribution"/>
+        <relationship name="computedTDD" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="TDD" inverseName="computed" inverseEntity="TDD"/>
+    </entity>
+    <entity name="Readings" representedClassName="Readings" syncable="YES" codeGenerationType="class">
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="glucose" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
+    </entity>
+    <entity name="TDD" representedClassName="TDD" syncable="YES" codeGenerationType="class">
+        <attribute name="tdd" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <relationship name="computed" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Oref0Suggestion" inverseName="computedTDD" inverseEntity="Oref0Suggestion"/>
+    </entity>
+</model>

+ 5 - 5
Dependencies/rileylink_ios/MinimedKitUI/tr.lproj/Localizable.strings

@@ -2,10 +2,10 @@
 "%1$@  %2$@/%3$@  %4$@" = "%1$@  %2$@/%3$@  %4$@";
 
 /* The format string describing number of basal schedule entries: (1: number of entries) */
-"%1$@ basal schedule entries\n" = "%1$@ basal schedule entries\n";
+"%1$@ basal schedule entries\n" = "%1$@ bazal program girişleri\n";
 
 /* The format string describing units of insulin remaining: (1: number of units) */
-"%1$@ Units of insulin remaining\n" = "%1$@ Units of insulin remaining\n";
+"%1$@ Units of insulin remaining\n" = "%1$@ Ünite insülin kaldı\n";
 
 /* The format string for displaying an offset from a time zone: (1: GMT)(2: -)(3: 4:00) */
 "%1$@%2$@%3$@" = "%1$@%2$@%3$@";
@@ -17,7 +17,7 @@
 "Are you sure you want to delete this pump?" = "Bu pompa'yı silmek istediğinden emin misin?";
 
 /* The title of the cell describing an awake radio */
-"Awake Until" = "Awake Until";
+"Awake Until" = "Kadar Etkin";
 
 /* The title text for the basal rate schedule */
 "Basal Rates" = "Bazal Oranları";
@@ -120,7 +120,7 @@
 "Listening Off" = "Dinleme kapalı";
 
 /* The title of the command to pair with mysentry */
-"MySentry Pair" = "MySentry Pair";
+"MySentry Pair" = "MySentry'e Bağlan";
 
 /* The title of the cell showing device name */
 "Name" = "İsim";
@@ -129,7 +129,7 @@
 "No response" = "Yanıt yok";
 
 /* The title of the cell showing the last idle */
-"On Idle" = "On Idle";
+"On Idle" = "Boşta";
 
 /* Pump find device instruction */
 "On your pump, go to the Find Device screen and select \"Find Device\".\n\nMain Menu >\nUtilities >\nConnect Devices >\nOther Devices >\nOn >\nFind Device" = "Pompanızda, Cihaz Bul ekranına gidin ve \"Cihaz Bul\"u seçin.\n\nAna Menü >\nAraçlar >\nCihazları Bağlayın >\nDiğer Cihazlar >\nAçık >\nCihaz Bul";

+ 2 - 2
Dependencies/rileylink_ios/RileyLink.xcodeproj/project.pbxproj

@@ -5659,7 +5659,7 @@
 				CODE_SIGN_IDENTITY = "Mac Developer";
 				CODE_SIGN_STYLE = Automatic;
 				DEBUG_INFORMATION_FORMAT = dwarf;
-				DEVELOPMENT_TEAM = UY678SP37Q;
+				DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
 				ENABLE_HARDENED_RUNTIME = YES;
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
 				GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -5686,7 +5686,7 @@
 				CODE_SIGN_STYLE = Automatic;
 				COPY_PHASE_STRIP = NO;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEVELOPMENT_TEAM = UY678SP37Q;
+				DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
 				ENABLE_HARDENED_RUNTIME = YES;
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
 				GCC_C_LANGUAGE_STANDARD = gnu11;

+ 23 - 8
FreeAPS.xcodeproj/project.pbxproj

@@ -16,13 +16,11 @@
 		19012CDC291D2CB900FB8210 /* LoopStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19012CDB291D2CB900FB8210 /* LoopStats.swift */; };
 		1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1927C8E82744606D00347C69 /* InfoPlist.strings */; };
 		1935364028496F7D001E0B16 /* TDD_averages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1935363F28496F7D001E0B16 /* TDD_averages.swift */; };
-		19788CAF293CE0F0002FC264 /* GlucoseDataForStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19788CAE293CE0F0002FC264 /* GlucoseDataForStats.swift */; };
 		19795118275953E50044850D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; };
 		198377D2266BFFF6004DE65E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; };
 		19854F492961C3E500941627 /* DurationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19854F482961C3E500941627 /* DurationButton.swift */; };
 		199561C1275E61A50077B976 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 199561C0275E61A50077B976 /* HealthKit.framework */; };
 		19B0EF2128F6D66200069496 /* Statistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B0EF2028F6D66200069496 /* Statistics.swift */; };
-		19F79FA9283AE7E000646323 /* TDD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F79FA8283AE7E000646323 /* TDD.swift */; };
 		1BBB001DAD60F3B8CEA4B1C7 /* ISFEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505E09DC17A0C3D0AF4B66FE /* ISFEditorStateModel.swift */; };
 		1D845DF2E3324130E1D95E67 /* DataTableProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60744C3E9BB3652895C908CC /* DataTableProvider.swift */; };
 		23888883D4EA091C88480FF2 /* BolusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C19984D62EFC0035A9E9644D /* BolusProvider.swift */; };
@@ -339,6 +337,8 @@
 		FE41E4D429463C660047FD55 /* NightscoutStatistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE41E4D329463C660047FD55 /* NightscoutStatistics.swift */; };
 		FE41E4D629463EE20047FD55 /* NightscoutPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */; };
 		FE66D16B291F74F8005D6F77 /* Bundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE66D16A291F74F8005D6F77 /* Bundle+Extensions.swift */; };
+		FEFA5C0F299F810B00765C17 /* Core_Data.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FEFA5C0D299F810B00765C17 /* Core_Data.xcdatamodeld */; };
+		FEFA5C11299F814A00765C17 /* CoreDataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFA5C10299F814A00765C17 /* CoreDataStack.swift */; };
 		FEFFA7A22929FE49007B8193 /* UIDevice+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFFA7A12929FE49007B8193 /* UIDevice+Extensions.swift */; };
 /* End PBXBuildFile section */
 
@@ -446,7 +446,6 @@
 		1927C8FB2744612600347C69 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1927C8FE274489BA00347C69 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1935363F28496F7D001E0B16 /* TDD_averages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TDD_averages.swift; sourceTree = "<group>"; };
-		19788CAE293CE0F0002FC264 /* GlucoseDataForStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseDataForStats.swift; sourceTree = "<group>"; };
 		198377D3266BFFF6004DE65E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
 		198377D5266C0A05004DE65E /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
 		198377D6266C0A0A004DE65E /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -471,7 +470,6 @@
 		19B0EF2028F6D66200069496 /* Statistics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Statistics.swift; sourceTree = "<group>"; };
 		19C166682756EFBD00ED12E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		19C166692756EFBD00ED12E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
-		19F79FA8283AE7E000646323 /* TDD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TDD.swift; sourceTree = "<group>"; };
 		1CAE81192B118804DCD23034 /* SnoozeProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SnoozeProvider.swift; sourceTree = "<group>"; };
 		212E8BFE6D66EE65AA26A114 /* CalibrationsProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CalibrationsProvider.swift; sourceTree = "<group>"; };
 		223EC0494F55A91E3EA69EF4 /* BolusStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BolusStateModel.swift; sourceTree = "<group>"; };
@@ -771,6 +769,8 @@
 		FE41E4D329463C660047FD55 /* NightscoutStatistics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutStatistics.swift; sourceTree = "<group>"; };
 		FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutPreferences.swift; sourceTree = "<group>"; };
 		FE66D16A291F74F8005D6F77 /* Bundle+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Extensions.swift"; sourceTree = "<group>"; };
+		FEFA5C0E299F810B00765C17 /* Core_Data.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Core_Data.xcdatamodel; sourceTree = "<group>"; };
+		FEFA5C10299F814A00765C17 /* CoreDataStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataStack.swift; sourceTree = "<group>"; };
 		FEFFA7A12929FE49007B8193 /* UIDevice+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Extensions.swift"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -1272,6 +1272,7 @@
 		388E594F25AD948C0019842D = {
 			isa = PBXGroup;
 			children = (
+				FEFA5C0D299F810B00765C17 /* Core_Data.xcdatamodeld */,
 				38F3783A2613555C009DB701 /* Config.xcconfig */,
 				3818AA42274BBC1100843DB3 /* ConfigOverride.xcconfig */,
 				388E595A25AD948C0019842D /* FreeAPS */,
@@ -1345,12 +1346,10 @@
 				3871F39B25ED892B0013ECB5 /* TempTarget.swift */,
 				3811DE8E25C9D80400A708ED /* User.swift */,
 				E0D4F80427513ECF00BDF1FE /* HealthKitSample.swift */,
-				19F79FA8283AE7E000646323 /* TDD.swift */,
 				1935363F28496F7D001E0B16 /* TDD_averages.swift */,
 				CE82E02628E869DF00473A9C /* AlertEntry.swift */,
 				19B0EF2028F6D66200069496 /* Statistics.swift */,
 				19012CDB291D2CB900FB8210 /* LoopStats.swift */,
-				19788CAE293CE0F0002FC264 /* GlucoseDataForStats.swift */,
 				FE41E4D329463C660047FD55 /* NightscoutStatistics.swift */,
 				FE41E4D529463EE20047FD55 /* NightscoutPreferences.swift */,
 			);
@@ -1360,6 +1359,7 @@
 		388E5A5A25B6F05F0019842D /* Helpers */ = {
 			isa = PBXGroup;
 			children = (
+				FEFA5C10299F814A00765C17 /* CoreDataStack.swift */,
 				38F37827261260DC009DB701 /* Color+Extensions.swift */,
 				389ECE042601144100D86C4F /* ConcurrentMap.swift */,
 				38192E0C261BAF980094D973 /* ConvenienceExtensions.swift */,
@@ -2289,6 +2289,7 @@
 				38A9260525F012D8009E3739 /* CarbRatios.swift in Sources */,
 				38FCF3D625E8FDF40078B0D1 /* MD5.swift in Sources */,
 				3871F39C25ED892B0013ECB5 /* TempTarget.swift in Sources */,
+				FEFA5C11299F814A00765C17 /* CoreDataStack.swift in Sources */,
 				3811DEAB25C9D88300A708ED /* HTTPResponseStatus.swift in Sources */,
 				3811DE5F25C9D4D500A708ED /* ProgressBar.swift in Sources */,
 				38E87408274F9AD000975559 /* UserNotificationsManager.swift in Sources */,
@@ -2360,6 +2361,7 @@
 				1BBB001DAD60F3B8CEA4B1C7 /* ISFEditorStateModel.swift in Sources */,
 				F816826028DB441800054060 /* BluetoothTransmitter.swift in Sources */,
 				38192E0D261BAF980094D973 /* ConvenienceExtensions.swift in Sources */,
+				FEFA5C0F299F810B00765C17 /* Core_Data.xcdatamodeld in Sources */,
 				88AB39B23C9552BD6E0C9461 /* ISFEditorRootView.swift in Sources */,
 				F816825E28DB441200054060 /* HeartBeatManager.swift in Sources */,
 				38FEF413273B317A00574A46 /* HKUnit.swift in Sources */,
@@ -2376,7 +2378,6 @@
 				9702FF92A09C53942F20D7EA /* TargetsEditorRootView.swift in Sources */,
 				38E8754F275556FA00975559 /* WatchManager.swift in Sources */,
 				A228DF96647338139F152B15 /* PreferencesEditorDataFlow.swift in Sources */,
-				19F79FA9283AE7E000646323 /* TDD.swift in Sources */,
 				389ECE052601144100D86C4F /* ConcurrentMap.swift in Sources */,
 				E4984C5262A90469788754BB /* PreferencesEditorProvider.swift in Sources */,
 				DD399FB31EACB9343C944C4C /* PreferencesEditorStateModel.swift in Sources */,
@@ -2402,7 +2403,6 @@
 				38E98A2D25F52DC400C0CED0 /* NSLocking+Extensions.swift in Sources */,
 				38569353270B5E350002C50D /* CGMRootView.swift in Sources */,
 				69A31254F2451C20361D172F /* BolusStateModel.swift in Sources */,
-				19788CAF293CE0F0002FC264 /* GlucoseDataForStats.swift in Sources */,
 				0CEA2EA070AB041AF3E3745B /* BolusRootView.swift in Sources */,
 				FEFFA7A22929FE49007B8193 /* UIDevice+Extensions.swift in Sources */,
 				F90692D3274B9A130037068D /* AppleHealthKitRootView.swift in Sources */,
@@ -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;
@@ -3062,6 +3064,19 @@
 			productName = SwiftCharts;
 		};
 /* End XCSwiftPackageProductDependency section */
+
+/* Begin XCVersionGroup section */
+		FEFA5C0D299F810B00765C17 /* Core_Data.xcdatamodeld */ = {
+			isa = XCVersionGroup;
+			children = (
+				FEFA5C0E299F810B00765C17 /* Core_Data.xcdatamodel */,
+			);
+			currentVersion = FEFA5C0E299F810B00765C17 /* Core_Data.xcdatamodel */;
+			path = Core_Data.xcdatamodeld;
+			sourceTree = "<group>";
+			versionGroupType = wrapper.xcdatamodel;
+		};
+/* End XCVersionGroup section */
 	};
 	rootObject = 388E595025AD948C0019842D /* Project object */;
 }

+ 61 - 0
FreeAPS.xcworkspace/contents.xcworkspacedata

@@ -1,6 +1,67 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Workspace
    version = "1.0">
+   <Group
+      location = "container:"
+      name = "CoreDataClassesAndProperties">
+      <FileRef
+         location = "group:InsulinDistribution+CoreDataClass.swift">
+      </FileRef>
+      <FileRef
+         location = "group:InsulinDistribution+CoreDataProperties.swift">
+      </FileRef>
+      <FileRef
+         location = "group:Oref0Suggestion+CoreDataClass.swift">
+      </FileRef>
+      <FileRef
+         location = "group:Oref0Suggestion+CoreDataProperties.swift">
+      </FileRef>
+      <FileRef
+         location = "group:HbA1c+CoreDataClass.swift">
+      </FileRef>
+      <FileRef
+         location = "group:HbA1c+CoreDataProperties.swift">
+      </FileRef>
+      <FileRef
+         location = "group:BGmedian+CoreDataClass.swift">
+      </FileRef>
+      <FileRef
+         location = "group:BGmedian+CoreDataProperties.swift">
+      </FileRef>
+      <FileRef
+         location = "group:BGaverages+CoreDataClass.swift">
+      </FileRef>
+      <FileRef
+         location = "group:BGaverages+CoreDataProperties.swift">
+      </FileRef>
+      <FileRef
+         location = "group:Carbohydrates+CoreDataClass.swift">
+      </FileRef>
+      <FileRef
+         location = "group:Carbohydrates+CoreDataProperties.swift">
+      </FileRef>
+      <FileRef
+         location = "group:LoopStatRecord+CoreDataClass.swift">
+      </FileRef>
+      <FileRef
+         location = "group:LoopStatRecord+CoreDataProperties.swift">
+      </FileRef>
+      <FileRef
+         location = "group:Readings+CoreDataClass.swift">
+      </FileRef>
+      <FileRef
+         location = "group:Readings+CoreDataProperties.swift">
+      </FileRef>
+      <FileRef
+         location = "group:TDD+CoreDataClass.swift">
+      </FileRef>
+      <FileRef
+         location = "group:TDD+CoreDataProperties.swift">
+      </FileRef>
+      <FileRef
+         location = "group:FreeAPS/Sources/Helpers/CoreDataStack.swift">
+      </FileRef>
+   </Group>
    <FileRef
       location = "group:FreeAPS.xcodeproj">
    </FileRef>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
FreeAPS/Resources/javascript/bundle/determine-basal.js


+ 1 - 1
FreeAPS/Resources/javascript/middleware/determine_basal.js

@@ -1,4 +1,4 @@
-function middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoir, clock, pumphistory, preferences, basalprofile, tdd, tdd_averages) {
+function middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoir, clock, pumphistory, preferences, basalprofile) {
     // modify anything
     // return any reason what has changed.
     return "Nothing changed";

+ 5 - 4
FreeAPS/Resources/javascript/prepare/determine-basal.js

@@ -1,11 +1,11 @@
 //для enact/smb-suggested.json параметры: monitor/iob.json monitor/temp_basal.json monitor/glucose.json settings/profile.json settings/autosens.json --meal monitor/meal.json --microbolus --reservoir monitor/reservoir.json
 
-function generate(iob, currenttemp, glucose, profile, autosens = null, meal = null, microbolusAllowed = false, reservoir = null, clock = new Date(), pump_history, preferences, basalProfile, tdd, tdd_averages) {
+function generate(iob, currenttemp, glucose, profile, autosens = null, meal = null, microbolusAllowed = false, reservoir = null, clock = new Date(), pump_history, preferences, basalProfile, tdd_averages) {
 
     var clock = new Date();
     
     try {
-        var middlewareReason = middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoir, clock, pump_history, preferences, basalProfile, tdd, tdd_averages);
+        var middlewareReason = middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoir, clock, pump_history, preferences, basalProfile, tdd_averages);
         console.log("Middleware reason: " + (middlewareReason || "Nothing changed"));
     } catch (error) {
         console.log("Invalid middleware: " + error);
@@ -37,16 +37,17 @@ function generate(iob, currenttemp, glucose, profile, autosens = null, meal = nu
     if (basalProfile) {
         basalprofile = basalProfile;
     }
-    
+    /*
     var tdd_ = {};
     if (tdd) {
         tdd_ = tdd;
     }
+     */
     
     var tdd_averages_ = {};
     if (tdd_averages) {
         tdd_averages_ = tdd_averages;
     }
     
-    return freeaps_determineBasal(glucose_status, currenttemp, iob, profile, autosens_data, meal_data, freeaps_basalSetTemp, microbolusAllowed, reservoir_data, clock, pumphistory, preferences, basalprofile, tdd_, tdd_averages_);
+    return freeaps_determineBasal(glucose_status, currenttemp, iob, profile, autosens_data, meal_data, freeaps_basalSetTemp, microbolusAllowed, reservoir_data, clock, pumphistory, preferences, basalprofile, tdd_averages_);
 }

+ 289 - 254
FreeAPS/Sources/APS/APSManager.swift

@@ -1,4 +1,5 @@
 import Combine
+import CoreData
 import Foundation
 import LoopKit
 import LoopKitUI
@@ -78,6 +79,8 @@ final class BaseAPSManager: APSManager, Injectable {
         }
     }
 
+    let coredataContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
+
     private var openAPS: OpenAPS!
 
     private var lifetime = Lifetime()
@@ -239,11 +242,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 +660,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,52 +667,63 @@ final class BaseAPSManager: APSManager, Injectable {
                 }
             }
             nightscout.uploadStatus()
+
+            // Update the TDD value
+            tdd(enacted_: enacted)
+            // Update statistics. Only run if enabled in preferences
+            // if settingsManager.settings.displayStatistics {
+            statistics()
+            // }
         }
     }
 
     private func tdd(enacted_: Suggestion) {
-        // Add to tdd.json:
+        let tddStartedAt = Date()
         let preferences = settingsManager.preferences
         let currentTDD = enacted_.tdd ?? 0
-        let file = OpenAPS.Monitor.tdd
-        let tdd = TDD(
-            TDD: currentTDD,
-            timestamp: Date(),
-            id: UUID().uuidString
-        )
-        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 } ?? []
+
+        // MARK: Fetch data from Core Data: TDD Entity. TEST:
+
+        if currentTDD > 0 {
+            let tenDaysAgo = Date().addingTimeInterval(-10.days.timeInterval)
+            let twoHoursAgo = Date().addingTimeInterval(-2.hours.timeInterval)
+
+            var uniqEvents = [TDD]()
             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
-                }
+            var indeces: Int = 0
+            var nrOfIndeces: Int = 0
+
+            coredataContext.performAndWait {
+                let requestTDD = TDD.fetchRequest() as NSFetchRequest<TDD>
+
+                requestTDD.predicate = NSPredicate(format: "timestamp > %@ AND tdd > 0", tenDaysAgo as NSDate)
+
+                let sortTDD = NSSortDescriptor(key: "timestamp", ascending: true)
+                requestTDD.sortDescriptors = [sortTDD]
+
+                try? uniqEvents = coredataContext.fetch(requestTDD)
+
+                total = uniqEvents.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
+                indeces = uniqEvents.count
+
+                // Only fetch once. Use same (previous) fetch
+                let twoHoursArray = uniqEvents.filter({ ($0.timestamp ?? Date()) >= twoHoursAgo })
+                nrOfIndeces = twoHoursArray.count
+
+                totalAmount = twoHoursArray.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
             }
+
             if indeces == 0 {
                 indeces = 1
             }
             if nrOfIndeces == 0 {
                 nrOfIndeces = 1
             }
-            let average14 = total / indeces
-            let average2hours = totalAmount / nrOfIndeces
+
+            let average2hours = totalAmount / Decimal(nrOfIndeces)
+            let average14 = total / Decimal(indeces)
+
             let weight = preferences.weightPercentage
             let weighted_average = weight * average2hours + (1 - weight) * average14
             let averages = TDD_averages(
@@ -727,7 +733,8 @@ final class BaseAPSManager: APSManager, Injectable {
                 date: Date()
             )
             storage.save(averages, as: OpenAPS.Monitor.tdd_averages)
-            storage.save(Array(uniqEvents), as: file)
+
+            print("Test time of TDD: \(-1 * tddStartedAt.timeIntervalSinceNow) s")
         }
     }
 
@@ -756,16 +763,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 {
@@ -775,21 +781,44 @@ 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)
-        var currentTDD: Decimal = 0
-        if tdds?.count ?? 0 > 0 {
-            currentTDD = tdds?[0].TDD ?? 0
-        }
-        let carbs_length = carbs?.count ?? 0
+
+        // MARK: Fetch Carbs from CoreData
+
+        var carbs = [Carbohydrates]()
         var carbTotal: Decimal = 0
-        if carbs_length != 0 {
-            for each in carbs! {
-                if each.carbs != 0 {
-                    carbTotal += each.carbs
-                }
+
+        coredataContext.performAndWait {
+            let requestCarbs = Carbohydrates.fetchRequest() as NSFetchRequest<Carbohydrates>
+
+            let daysAgo = Date().addingTimeInterval(-1.days.timeInterval)
+            requestCarbs.predicate = NSPredicate(format: "carbs > 0 AND date > %@", daysAgo as NSDate)
+
+            let sortCarbs = NSSortDescriptor(key: "date", ascending: true)
+            requestCarbs.sortDescriptors = [sortCarbs]
+
+            try? carbs = coredataContext.fetch(requestCarbs)
+
+            carbTotal = carbs.map({ carbs in carbs.carbs as? Decimal ?? 0 }).reduce(0, +)
+        }
+
+        // MARK: Fetch TDD from CoreData
+
+        var tdds = [TDD]()
+        var currentTDD: Decimal = 0
+
+        coredataContext.performAndWait {
+            let requestTDD = TDD.fetchRequest() as NSFetchRequest<TDD>
+            let sort = NSSortDescriptor(key: "timestamp", ascending: false)
+            requestTDD.sortDescriptors = [sort]
+            requestTDD.fetchLimit = 1
+
+            try? tdds = coredataContext.fetch(requestTDD)
+
+            if !tdds.isEmpty {
+                currentTDD = tdds[0].tdd?.decimalValue ?? 0
             }
         }
+
         var algo_ = "Oref0"
 
         if preferences.sigmoid, preferences.enableDynamicCR {
@@ -820,12 +849,13 @@ final class BaseAPSManager: APSManager, Injectable {
         } else if preferences.curve.rawValue == "ultra-rapid" {
             iPa = 50
         }
-        // Retrieve the loopStats data
-        let lsData = storage.retrieve(OpenAPS.Monitor.loopStats, as: [LoopStats].self)?
-            .sorted { $0.start > $1.start } ?? []
+
+        // MARK: Fetch LoopStatRecords from CoreData
+
+        var lsr = [LoopStatRecord]()
         var successRate: Double?
-        var successNR = 0.0
-        var errorNR = 0.0
+        var successNR = 0
+        var errorNR = 0
         var minimumInt = 999.0
         var maximumInt = 0.0
         var minimumLoopTime = 9999.0
@@ -840,132 +870,152 @@ final class BaseAPSManager: APSManager, Injectable {
         var medianInterval = 0.0
         var averageIntervalLoops = 0.0
 
-        if !lsData.isEmpty {
-            var i = 0.0
+        coredataContext.performAndWait {
+            let requestLSR = LoopStatRecord.fetchRequest() as NSFetchRequest<LoopStatRecord>
+            requestLSR.predicate = NSPredicate(format: "start > %@", Date().addingTimeInterval(-24.hours.timeInterval) as NSDate)
+            let sortLSR = NSSortDescriptor(key: "start", ascending: false)
+            requestLSR.sortDescriptors = [sortLSR]
 
-            if let loopEnd = lsData[0].end {
-                previousTimeLoop = loopEnd
-            }
+            try? lsr = coredataContext.fetch(requestLSR)
 
-            for each in lsData {
-                if let loopEnd = each.end, let loopDuration = each.duration {
-                    if each.loopStatus.contains("Success") {
-                        successNR += 1
-                    } else {
-                        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
+            if lsr.isNotEmpty {
+                var i = 0.0
+                if let loopEnd = lsr[0].end {
+                    previousTimeLoop = loopEnd
+                }
+                for each in lsr {
+                    if let loopEnd = each.end {
+                        let loopDuration = each.duration
+
+                        if each.loopStatus!.contains("Success") {
+                            successNR += 1
+                        } else {
+                            errorNR += 1
+                        }
 
-                    timeForOneLoopArray.append(timeForOneLoop)
-                    averageLoopTime += timeForOneLoop
+                        i += 1
+                        timeIntervalLoops = (previousTimeLoop - (each.start ?? previousTimeLoop)).timeInterval / 60
 
-                    if timeForOneLoop >= maximumLoopTime, timeForOneLoop != 0.0 {
-                        maximumLoopTime = timeForOneLoop
-                    }
+                        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)
 
-                    if timeForOneLoop <= minimumLoopTime, timeForOneLoop != 0.0 {
-                        minimumLoopTime = timeForOneLoop
+                        if timeForOneLoop >= maximumLoopTime, timeForOneLoop != 0.0 {
+                            maximumLoopTime = timeForOneLoop
+                        }
+                        if timeForOneLoop <= minimumLoopTime, timeForOneLoop != 0.0 {
+                            minimumLoopTime = timeForOneLoop
+                        }
+                        previousTimeLoop = loopEnd
                     }
-
-                    previousTimeLoop = loopEnd
                 }
+                successRate = (Double(successNR) / Double(i)) * 100
+
+                // Average Loop Interval in minutes
+                let timeOfFirstIndex = lsr[0].start ?? Date()
+                let lastIndexWithTimestamp = lsr.count - 1
+                let timeOfLastIndex = lsr[lastIndexWithTimestamp].end ?? Date()
+                averageLoopTime = (timeOfFirstIndex - timeOfLastIndex).timeInterval / 60 / Double(errorNR + successNR)
+
+                // Median values
+                medianLoopTime = medianCalculation(array: timeForOneLoopArray)
+                medianInterval = medianCalculation(array: timeIntervalLoopArray)
+                // Average time interval between loops
+                averageIntervalLoops = timeIntervalLoopArray.reduce(0, +) / Double(timeIntervalLoopArray.count)
             }
-
-            successRate = (successNR / Double(i)) * 100
-            averageIntervalLoops = ((lsData[0].end ?? lsData[lsData.count - 1].start) - lsData[lsData.count - 1].start)
-                .timeInterval / 60 / Double(i)
-            averageLoopTime /= Double(i)
-            // Median values
-            medianLoopTime = medianCalculation(array: timeForOneLoopArray)
-            medianInterval = medianCalculation(array: timeIntervalLoopArray)
         }
+
         if minimumInt == 999.0 {
             minimumInt = 0.0
         }
         if minimumLoopTime == 9999.0 {
             minimumLoopTime = 0.0
         }
-        // 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 endIndex = length_ - 1
+
+        var glucose: [Readings] = []
+
+        var firstElementTime = Date()
+        var lastElementTime = Date()
+        var currentIndexTime = Date()
+
         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
-        }
-        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! {
-                j += 1
-                if entry.glucose > 0 {
-                    bg += Decimal(entry.glucose)
-                    bgArray.append(Double(entry.glucose))
-                    bgArrayForTIR.append((Double(entry.glucose), entry.date))
-                    nr_bgs += 1
-
-                    if (startDate! - entry.date).timeInterval >= 8.64E4, !end1 {
-                        end1 = true
-                        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
-                        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
-                        bg_30 = bg / nr_bgs
-                        bgArray_30 = bgArrayForTIR
-                        bgArray_30_ = bgArray
-                        nr_bgs_30 = nr_bgs
-                        // time_30 = ((startDate ?? Date()) - entry.date).timeInterval
+        var numberOfDays: Double = 0
+
+        coredataContext.performAndWait {
+            let requestGFS = Readings.fetchRequest() as NSFetchRequest<Readings>
+            let sortGlucose = NSSortDescriptor(key: "date", ascending: false)
+            requestGFS.sortDescriptors = [sortGlucose]
+
+            try? glucose = coredataContext.fetch(requestGFS)
+
+            firstElementTime = glucose.first?.date ?? Date()
+            lastElementTime = glucose.last?.date ?? Date()
+
+            currentIndexTime = firstElementTime
+
+            // Time In Range (%) and Average Glucose (24 hours). This will be refactored later after some testing.
+            let endIndex = glucose.count - 1
+
+            numberOfDays = (firstElementTime - lastElementTime).timeInterval / 8.64E4
+
+            // Make arrays for median calculations and calculate averages
+            if endIndex >= 0 {
+                repeat {
+                    j += 1
+                    if glucose[j].glucose > 0 {
+                        currentIndexTime = glucose[j].date ?? firstElementTime
+                        bg += Decimal(glucose[j].glucose) * conversionFactor
+                        bgArray.append(Double(glucose[j].glucose) * Double(conversionFactor))
+                        bgArrayForTIR.append((Double(glucose[j].glucose), glucose[j].date!))
+                        nr_bgs += 1
+                        if (firstElementTime - currentIndexTime).timeInterval / 60 <= 8.64E4 { // 1 day
+                            bg_1 = bg / nr_bgs
+                            bgArray_1 = bgArrayForTIR
+                            bgArray_1_ = bgArray
+                        }
+                        if (firstElementTime - currentIndexTime).timeInterval / 60 <= 6.048E5 { // 7 days
+                            bg_7 = bg / nr_bgs
+                            bgArray_7 = bgArrayForTIR
+                            bgArray_7_ = bgArray
+                        }
+                        if (firstElementTime - currentIndexTime).timeInterval / 60 <= 2.592E6 { // 30 days
+                            bg_30 = bg / nr_bgs
+                            bgArray_30 = bgArrayForTIR
+                            bgArray_30_ = bgArray
+                        }
                     }
-                }
+                } while j != glucose.count - 1
             }
         }
 
@@ -984,13 +1034,6 @@ final class BaseAPSManager: APSManager, Injectable {
 
         // 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 +1043,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 +1083,55 @@ 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)
         )
 
+        // MARK: Save to Median to CoreData
+
+        coredataContext.perform {
+            let saveMedianToCoreData = BGmedian(context: self.coredataContext)
+
+            saveMedianToCoreData.date = Date()
+            saveMedianToCoreData.median = median.total as NSDecimalNumber
+            saveMedianToCoreData.median_1 = median.day as NSDecimalNumber
+            saveMedianToCoreData.median_7 = median.week as NSDecimalNumber
+            saveMedianToCoreData.median_30 = median.month as NSDecimalNumber
+
+            try? self.coredataContext.save()
+        }
+
         var hbs = Durations(
             day: roundDecimal(NGSPa1CStatisticValue, 1),
             week: roundDecimal(NGSPa1CStatisticValue_7, 1),
@@ -1090,20 +1141,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,15 +1160,15 @@ 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
 
         let glucose24Hours = storage.retrieve(OpenAPS.Monitor.glucose, as: [BloodGlucose].self)
         let nrOfCGMReadings = glucose24Hours?.count ?? 0
 
         let loopstat = LoopCycles(
-            loops: Int(successNR + errorNR),
-            errors: Int(errorNR),
+            loops: successNR + errorNR,
+            errors: errorNR,
             readings: nrOfCGMReadings,
             success_rate: Decimal(round(successRate ?? 0)),
             avg_interval: roundDecimal(Decimal(averageIntervalLoops), 1),
@@ -1150,16 +1188,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)
         }
 
@@ -1195,45 +1227,54 @@ final class BaseAPSManager: APSManager, Injectable {
 
         let avg = Averages(Average: avgs, Median: median)
 
-        let suggestion = storage.retrieve(OpenAPS.Enact.suggested, as: Suggestion.self)
+        // MARK: Fetch InsulinDuration from CoreData
+
+        // let suggestion = storage.retrieve(OpenAPS.Enact.suggested, as: Suggestion.self)
 
-        let insulin = Ins(
-            TDD: roundDecimal(currentTDD, 2),
-            bolus: suggestion?.insulin?.bolus ?? 0,
-            temp_basal: suggestion?.insulin?.temp_basal ?? 0,
-            scheduled_basal: suggestion?.insulin?.scheduled_basal ?? 0
+        var insulinDistribution = [InsulinDistribution]()
+        var insulin = Ins(
+            TDD: 0,
+            bolus: 0,
+            temp_basal: 0,
+            scheduled_basal: 0
         )
 
-        // SD and CV calculations for all durations:
+        coredataContext.performAndWait {
+            let requestInsulinDistribution = InsulinDistribution.fetchRequest() as NSFetchRequest<InsulinDistribution>
+            let sortInsulin = NSSortDescriptor(key: "date", ascending: false)
+            requestInsulinDistribution.sortDescriptors = [sortInsulin]
+            requestInsulinDistribution.fetchLimit = 1
+
+            try? insulinDistribution = coredataContext.fetch(requestInsulinDistribution)
+
+            insulin = Ins(
+                TDD: roundDecimal(currentTDD, 2),
+                bolus: insulinDistribution.first != nil ? ((insulinDistribution[0].bolus ?? 0) as Decimal) : 0,
+                temp_basal: insulinDistribution.first != nil ? ((insulinDistribution[0].tempBasal ?? 0) as Decimal) : 0,
+                scheduled_basal: insulinDistribution.first != nil ? ((insulinDistribution[0].scheduledBasal ?? 0) as Decimal) : 0
+            )
+        }
 
-        var sumOfSquares: Decimal = 0
-        var sumOfSquares_1: Decimal = 0
-        var sumOfSquares_7: Decimal = 0
-        var sumOfSquares_30: Decimal = 0
+        var sumOfSquares = 0.0
+        var sumOfSquares_1 = 0.0
+        var sumOfSquares_7 = 0.0
+        var sumOfSquares_30 = 0.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) }
+            sumOfSquares += pow(array - Double(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(array_1 - Double(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(array_7 - Double(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(array_30 - Double(bg_30), 2)
         }
 
         // Standard deviation and Coefficient of variation
@@ -1247,27 +1288,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(sumOfSquares / Double(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(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(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(sumOfSquares_30 / Double(bgArray_30_.count))
             cv_30 = sd_30 / Double(bg_30) * 100
         }
 
@@ -1305,7 +1339,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 +1352,31 @@ 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 file = OpenAPS.Monitor.loopStats
+        let LoopStatsStartedAt = Date()
 
-        var uniqEvents: [LoopStats] = []
+        coredataContext.perform {
+            let nLS = LoopStatRecord(context: self.coredataContext)
 
-        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 } ?? []
+            nLS.start = loopStatRecord.start
+            nLS.end = loopStatRecord.end ?? Date()
+            nLS.loopStatus = loopStatRecord.loopStatus
+            nLS.duration = loopStatRecord.duration ?? 0.0
 
-            storage.save(Array(uniqEvents), as: file)
+            try? self.coredataContext.save()
         }
+
+        print("Test time of LoopStats computation: \(-1 * LoopStatsStartedAt.timeIntervalSinceNow) s")
     }
 
     private func processError(_ error: Error) {

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

@@ -54,12 +54,10 @@ extension OpenAPS {
         static let iob = "monitor/iob.json"
         static let cgmState = "monitor/cgm-state.json"
         static let podAge = "monitor/pod-age.json"
-        static let tdd = "monitor/tdd.json"
+        // static let tdd = "monitor/tdd.json"
         static let tdd_averages = "monitor/tdd_averages.json"
         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"
     }
 
     enum Enact {

+ 27 - 4
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -1,4 +1,5 @@
 import Combine
+import CoreData
 import Foundation
 import JavaScriptCore
 
@@ -8,6 +9,8 @@ final class OpenAPS {
 
     private let storage: FileStorage
 
+    let coredataContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
+
     init(storage: FileStorage) {
         self.storage = storage
     }
@@ -41,7 +44,6 @@ final class OpenAPS {
 
                 self.storage.save(meal, as: Monitor.meal)
 
-                let tdd = self.loadFileFromStorage(name: OpenAPS.Monitor.tdd)
                 let tdd_averages = self.loadFileFromStorage(name: OpenAPS.Monitor.tdd_averages)
 
                 // iob
@@ -72,7 +74,6 @@ final class OpenAPS {
                     pumpHistory: pumpHistory,
                     preferences: preferences,
                     basalProfile: basalProfile,
-                    tdd: tdd,
                     tdd_averages: tdd_averages
                 )
                 debug(.openAPS, "SUGGESTED: \(suggested)")
@@ -81,6 +82,30 @@ final class OpenAPS {
                     suggestion.timestamp = suggestion.deliverAt ?? clock
                     self.storage.save(suggestion, as: Enact.suggested)
 
+                    // MARK: Save to CoreData also. To do: Remove JSON saving
+
+                    if suggestion.tdd ?? 0 > 0 {
+                        self.coredataContext.perform {
+                            let saveToTDD = TDD(context: self.coredataContext)
+
+                            saveToTDD.timestamp = suggestion.timestamp ?? Date()
+                            saveToTDD.tdd = (suggestion.tdd ?? 0) as NSDecimalNumber?
+
+                            try? self.coredataContext.save()
+                        }
+
+                        self.coredataContext.perform {
+                            let saveToInsulin = InsulinDistribution(context: self.coredataContext)
+
+                            saveToInsulin.bolus = (suggestion.insulin?.bolus ?? 0) as NSDecimalNumber?
+                            saveToInsulin.scheduledBasal = (suggestion.insulin?.scheduled_basal ?? 0) as NSDecimalNumber?
+                            saveToInsulin.tempBasal = (suggestion.insulin?.temp_basal ?? 0) as NSDecimalNumber?
+                            saveToInsulin.date = Date()
+
+                            try? self.coredataContext.save()
+                        }
+                    }
+
                     promise(.success(suggestion))
                 } else {
                     promise(.success(nil))
@@ -305,7 +330,6 @@ final class OpenAPS {
         pumpHistory: JSON,
         preferences: JSON,
         basalProfile: JSON,
-        tdd: JSON,
         tdd_averages: JSON
     ) -> RawJSON {
         dispatchPrecondition(condition: .onQueue(processQueue))
@@ -335,7 +359,6 @@ final class OpenAPS {
                     pumpHistory,
                     preferences,
                     basalProfile,
-                    tdd,
                     tdd_averages
                 ]
             )

+ 23 - 0
FreeAPS/Sources/APS/Storage/CarbsStorage.swift

@@ -1,3 +1,4 @@
+import CoreData
 import Foundation
 import SwiftDate
 import Swinject
@@ -19,6 +20,8 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
     @Injected() private var storage: FileStorage!
     @Injected() private var broadcaster: Broadcaster!
 
+    let coredataContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
+
     init(resolver: Resolver) {
         injectServices(resolver)
     }
@@ -34,6 +37,26 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
                     .sorted { $0.createdAt > $1.createdAt } ?? []
                 storage.save(Array(uniqEvents), as: file)
             }
+
+            // MARK: Save to CoreData. TEST
+
+            var cbs: Decimal = 0
+            var carbDate = Date()
+            if carbs.isNotEmpty {
+                cbs = carbs[0].carbs
+                carbDate = carbs[0].createdAt
+            }
+            if cbs != 0 {
+                self.coredataContext.perform {
+                    let carbDataForStats = Carbohydrates(context: self.coredataContext)
+
+                    carbDataForStats.date = carbDate
+                    carbDataForStats.carbs = cbs as NSDecimalNumber
+
+                    try? self.coredataContext.save()
+                }
+            }
+
             broadcaster.notify(CarbsObserver.self, on: processQueue) {
                 $0.carbsDidUpdate(uniqEvents)
             }

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

@@ -1,4 +1,5 @@
 import AVFAudio
+import CoreData
 import Foundation
 import SwiftDate
 import SwiftUI
@@ -24,6 +25,8 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
     @Injected() private var broadcaster: Broadcaster!
     @Injected() private var settingsManager: SettingsManager!
 
+    let coredataContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
+
     private enum Config {
         static let filterTime: TimeInterval = 4.5 * 60
     }
@@ -33,10 +36,13 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
     }
 
     func storeGlucose(_ glucose: [BloodGlucose]) {
+        let storeGlucoseStarted = Date()
+
         processQueue.sync {
             let file = OpenAPS.Monitor.glucose
             self.storage.transaction { storage in
                 storage.append(glucose, to: file, uniqBy: \.dateString)
+
                 let uniqEvents = storage.retrieve(file, as: [BloodGlucose].self)?
                     .filter { $0.dateString.addingTimeInterval(24.hours.timeInterval) > Date() }
                     .sorted { $0.dateString > $1.dateString } ?? []
@@ -49,7 +55,8 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
                     }
                 }
 
-                // Save to glucoseForStats also.
+                // MARK: Save to CoreData. TEST
+
                 var bg_ = 0
                 var bgDate = Date()
 
@@ -57,14 +64,16 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
                     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.coredataContext.perform {
+                        let dataForForStats = Readings(context: self.coredataContext)
+
+                        dataForForStats.date = bgDate
+                        dataForForStats.glucose = Int16(bg_)
+
+                        try? self.coredataContext.save()
+                    }
                 }
             }
 
@@ -122,6 +131,7 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
                 }
             }
         }
+        print("Test time of glucoseStorage: \(-1 * storeGlucoseStarted.timeIntervalSinceNow) s")
     }
 
     func removeGlucose(ids: [String]) {

+ 36 - 0
FreeAPS/Sources/Helpers/CoreDataStack.swift

@@ -0,0 +1,36 @@
+import CoreData
+import Foundation
+
+class CoreDataStack {
+    private init() {}
+
+    static let shared = CoreDataStack()
+
+    lazy var persistentContainer: NSPersistentContainer = {
+        let container = NSPersistentContainer(name: "Core_Data")
+
+        container.loadPersistentStores(completionHandler: { _, error in
+            guard let error = error as NSError? else { return }
+            fatalError("Unresolved error: \(error), \(error.userInfo)")
+        })
+
+        return container
+    }()
+
+    func saveContext() {
+        let context = persistentContainer.viewContext
+
+        context.perform {
+            if context.hasChanges {
+                do {
+                    try context.save()
+                } catch {
+                    // Replace this implementation with code to handle the error appropriately.
+                    // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping    application, although it may be useful during development.
+                    let nserror = error as NSError
+                    fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
+                }
+            }
+        }
+    }
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings


+ 0 - 6
FreeAPS/Sources/Models/GlucoseDataForStats.swift

@@ -1,6 +0,0 @@
-import Foundation
-
-struct GlucoseDataForStats: JSON {
-    let date: Date
-    var glucose: Int
-}

+ 0 - 25
FreeAPS/Sources/Models/TDD.swift

@@ -1,25 +0,0 @@
-import Foundation
-
-struct TDD: JSON, Equatable {
-    var TDD: Decimal
-    var timestamp: Date
-    let id: String
-
-    init(
-        TDD: Decimal,
-        timestamp: Date,
-        id: String
-    ) {
-        self.TDD = TDD
-        self.timestamp = timestamp
-        self.id = id
-    }
-}
-
-extension TDD {
-    private enum CodingKeys: String, CodingKey {
-        case TDD
-        case timestamp
-        case id
-    }
-}

+ 0 - 4
FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift

@@ -98,10 +98,6 @@ extension Settings {
                                 .navigationLink(to: .configEditor(file: OpenAPS.Middleware.determineBasal), from: self)
                             Text("Statistics")
                                 .navigationLink(to: .configEditor(file: OpenAPS.Monitor.statistics), from: self)
-                            Text("Loop Cycles")
-                                .navigationLink(to: .configEditor(file: OpenAPS.Monitor.loopStats), from: self)
-                            Text("Glucose Data used for statistics")
-                                .navigationLink(to: .configEditor(file: OpenAPS.Monitor.glucose_data), from: self)
                             Text("Edit settings json")
                                 .navigationLink(to: .configEditor(file: OpenAPS.FreeAPS.settings), from: self)
                         }

+ 1 - 1
FreeAPS/Sources/Services/Storage/Disk/Disk+Codable.swift

@@ -66,7 +66,7 @@ public extension Disk {
                     try newData.write(to: url, options: .atomic)
                 }
             } else {
-                try save([value], to: directory, as: path, encoder: encoder)
+                try? save([value], to: directory, as: path, encoder: encoder)
             }
         } catch {
             throw error

+ 4 - 0
HbA1c+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(HbA1c) public class HbA1c: NSManagedObject {}

+ 15 - 0
HbA1c+CoreDataProperties.swift

@@ -0,0 +1,15 @@
+import CoreData
+import Foundation
+
+public extension HbA1c {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<HbA1c> {
+        NSFetchRequest<HbA1c>(entityName: "HbA1c")
+    }
+
+    @NSManaged var date: Date?
+    @NSManaged var hba1c: NSDecimalNumber?
+    @NSManaged var hba1c_1: NSDecimalNumber?
+    @NSManaged var hba1c_7: NSDecimalNumber?
+    @NSManaged var hba1c_30: NSDecimalNumber?
+    @NSManaged var hba1c_90: NSDecimalNumber?
+}

+ 4 - 0
InsulinDistribution+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(InsulinDistribution) public class InsulinDistribution: NSManagedObject {}

+ 14 - 0
InsulinDistribution+CoreDataProperties.swift

@@ -0,0 +1,14 @@
+import CoreData
+import Foundation
+
+public extension InsulinDistribution {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<InsulinDistribution> {
+        NSFetchRequest<InsulinDistribution>(entityName: "InsulinDistribution")
+    }
+
+    @NSManaged var bolus: NSDecimalNumber?
+    @NSManaged var tempBasal: NSDecimalNumber?
+    @NSManaged var scheduledBasal: NSDecimalNumber?
+    @NSManaged var date: Date?
+    @NSManaged var insulin: Oref0Suggestion?
+}

+ 4 - 0
LoopStatRecord+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(LoopStatRecord) public class LoopStatRecord: NSManagedObject {}

+ 13 - 0
LoopStatRecord+CoreDataProperties.swift

@@ -0,0 +1,13 @@
+import CoreData
+import Foundation
+
+public extension LoopStatRecord {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<LoopStatRecord> {
+        NSFetchRequest<LoopStatRecord>(entityName: "LoopStatRecord")
+    }
+
+    @NSManaged var duration: Double
+    @NSManaged var end: Date?
+    @NSManaged var start: Date?
+    @NSManaged var loopStatus: String?
+}

+ 4 - 0
Oref0Suggestion+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(Oref0Suggestion) public class Oref0Suggestion: NSManagedObject {}

+ 43 - 0
Oref0Suggestion+CoreDataProperties.swift

@@ -0,0 +1,43 @@
+import CoreData
+import Foundation
+
+public extension Oref0Suggestion {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<Oref0Suggestion> {
+        NSFetchRequest<Oref0Suggestion>(entityName: "Oref0Suggestion")
+    }
+
+    @NSManaged var computedTDD: NSSet?
+    @NSManaged var computedInsulinDistribution: NSSet?
+}
+
+// MARK: Generated accessors for computedTDD
+
+public extension Oref0Suggestion {
+    @objc(addComputedTDDObject:)
+    @NSManaged func addToComputedTDD(_ value: TDD)
+
+    @objc(removeComputedTDDObject:)
+    @NSManaged func removeFromComputedTDD(_ value: TDD)
+
+    @objc(addComputedTDD:)
+    @NSManaged func addToComputedTDD(_ values: NSSet)
+
+    @objc(removeComputedTDD:)
+    @NSManaged func removeFromComputedTDD(_ values: NSSet)
+}
+
+// MARK: Generated accessors for computedInsulinDistribution
+
+public extension Oref0Suggestion {
+    @objc(addComputedInsulinDistributionObject:)
+    @NSManaged func addToComputedInsulinDistribution(_ value: InsulinDistribution)
+
+    @objc(removeComputedInsulinDistributionObject:)
+    @NSManaged func removeFromComputedInsulinDistribution(_ value: InsulinDistribution)
+
+    @objc(addComputedInsulinDistribution:)
+    @NSManaged func addToComputedInsulinDistribution(_ values: NSSet)
+
+    @objc(removeComputedInsulinDistribution:)
+    @NSManaged func removeFromComputedInsulinDistribution(_ values: NSSet)
+}

+ 4 - 0
Readings+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(Readings) public class Readings: NSManagedObject {}

+ 11 - 0
Readings+CoreDataProperties.swift

@@ -0,0 +1,11 @@
+import CoreData
+import Foundation
+
+public extension Readings {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<Readings> {
+        NSFetchRequest<Readings>(entityName: "Readings")
+    }
+
+    @NSManaged var date: Date?
+    @NSManaged var glucose: Int16
+}

+ 4 - 0
TDD+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(TDD) public class TDD: NSManagedObject {}

+ 12 - 0
TDD+CoreDataProperties.swift

@@ -0,0 +1,12 @@
+import CoreData
+import Foundation
+
+public extension TDD {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<TDD> {
+        NSFetchRequest<TDD>(entityName: "TDD")
+    }
+
+    @NSManaged var tdd: NSDecimalNumber?
+    @NSManaged var timestamp: Date?
+    @NSManaged var computed: Oref0Suggestion?
+}