MainChartHelper.swift 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import CoreData
  2. import Foundation
  3. enum MainChartHelper {
  4. // Calculates the glucose value thats the nearest to parameter 'time'
  5. /// -Returns: A NSManagedObject of GlucoseStored
  6. /// it is thread safe as everything is executed on the main thread
  7. static func timeToNearestGlucose(glucoseValues: [GlucoseStored], time: TimeInterval) -> GlucoseStored? {
  8. guard !glucoseValues.isEmpty else {
  9. return nil
  10. }
  11. var low = 0
  12. var high = glucoseValues.count - 1
  13. var closestGlucose: GlucoseStored?
  14. // binary search to find next glucose
  15. while low <= high {
  16. let mid = low + (high - low) / 2
  17. let midTime = glucoseValues[mid].date?.timeIntervalSince1970 ?? 0
  18. if midTime == time {
  19. return glucoseValues[mid]
  20. } else if midTime < time {
  21. low = mid + 1
  22. } else {
  23. high = mid - 1
  24. }
  25. // update if necessary
  26. if closestGlucose == nil || abs(midTime - time) < abs(closestGlucose!.date?.timeIntervalSince1970 ?? 0 - time) {
  27. closestGlucose = glucoseValues[mid]
  28. }
  29. }
  30. return closestGlucose
  31. }
  32. enum Config {
  33. static let bolusSize: CGFloat = 5
  34. static let bolusScale: CGFloat = 1.8
  35. static let carbsSize: CGFloat = 5
  36. static let maxCarbSize: CGFloat = 30
  37. static let carbsScale: CGFloat = 0.3
  38. static let fpuSize: CGFloat = 10
  39. static let maxGlucose = 270
  40. static let minGlucose = 45
  41. }
  42. static var bolusFormatter: NumberFormatter {
  43. let formatter = NumberFormatter()
  44. formatter.numberStyle = .decimal
  45. formatter.minimumIntegerDigits = 0
  46. formatter.maximumFractionDigits = 2
  47. formatter.decimalSeparator = "."
  48. return formatter
  49. }
  50. static var carbsFormatter: NumberFormatter {
  51. let formatter = NumberFormatter()
  52. formatter.numberStyle = .decimal
  53. formatter.maximumFractionDigits = 0
  54. return formatter
  55. }
  56. static func bolusOffset(units: GlucoseUnits) -> Decimal {
  57. units == .mgdL ? 30 : 1.66
  58. }
  59. static func calculateDuration(
  60. objectID: NSManagedObjectID,
  61. attribute: String,
  62. context: NSManagedObjectContext
  63. ) -> TimeInterval? {
  64. do {
  65. let object = try context.existingObject(with: objectID)
  66. if let attributeValue = object.value(forKey: attribute) as? NSDecimalNumber {
  67. let doubleValue = attributeValue.doubleValue
  68. if doubleValue != 0 {
  69. return TimeInterval(doubleValue * 60) // return seconds
  70. }
  71. } else {
  72. debugPrint("Attribute \(attribute) not found or not of type NSDecimalNumber")
  73. }
  74. } catch {
  75. debugPrint(
  76. "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to calculate duration for object with error: \(error.localizedDescription)"
  77. )
  78. }
  79. return nil
  80. }
  81. static func calculateTarget(objectID: NSManagedObjectID, attribute: String, context: NSManagedObjectContext) -> Decimal? {
  82. do {
  83. let object = try context.existingObject(with: objectID)
  84. if let attributeValue = object.value(forKey: attribute) as? NSDecimalNumber, attributeValue != 0 {
  85. return attributeValue.decimalValue
  86. }
  87. } catch {
  88. debugPrint(
  89. "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to calculate target for object with error: \(error.localizedDescription)"
  90. )
  91. }
  92. return nil
  93. }
  94. }