Basals.swift 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. //
  2. // Basals.swift
  3. // LoopFollow
  4. //
  5. // Created by Jonas Björkert on 2023-10-05.
  6. // Copyright © 2023 Jon Fawcett. All rights reserved.
  7. //
  8. import Foundation
  9. extension MainViewController {
  10. // NS Temp Basal Response Processor
  11. func processNSBasals(entries: [[String:AnyObject]]) {
  12. infoManager.clearInfoData(type: .basal)
  13. basalData.removeAll()
  14. var lastEndDot = 0.0
  15. var tempArray = entries
  16. tempArray.reverse()
  17. for i in 0..<tempArray.count {
  18. guard let currentEntry = tempArray[i] as [String : AnyObject]? else { continue }
  19. // Decide which field to parse
  20. let dateString = currentEntry["timestamp"] as? String
  21. ?? currentEntry["created_at"] as? String
  22. guard let rawDateStr = dateString,
  23. let dateParsed = NightscoutUtils.parseDate(rawDateStr) else {
  24. continue
  25. }
  26. let dateTimeStamp = dateParsed.timeIntervalSince1970
  27. guard let basalRate = currentEntry["absolute"] as? Double else {
  28. continue
  29. }
  30. let duration = currentEntry["duration"] as? Double ?? 0.0
  31. if i > 0 {
  32. let priorEntry = tempArray[i - 1] as [String : AnyObject]?
  33. let priorDateStr = priorEntry?["timestamp"] as? String
  34. ?? priorEntry?["created_at"] as? String
  35. if let rawPrior = priorDateStr,
  36. let priorDateParsed = NightscoutUtils.parseDate(rawPrior) {
  37. let priorDateTimeStamp = priorDateParsed.timeIntervalSince1970
  38. let priorDuration = priorEntry?["duration"] as? Double ?? 0.0
  39. if (dateTimeStamp - priorDateTimeStamp) > (priorDuration * 60) + 15 {
  40. var scheduled = 0.0
  41. var midGap = false
  42. var midGapTime: TimeInterval = 0
  43. var midGapValue: Double = 0
  44. for b in 0..<basalScheduleData.count {
  45. let priorEnd = priorDateTimeStamp + (priorDuration * 60)
  46. if priorEnd >= basalScheduleData[b].date {
  47. scheduled = basalScheduleData[b].basalRate
  48. if b < basalScheduleData.count - 1 {
  49. if dateTimeStamp > basalScheduleData[b + 1].date {
  50. midGap = true
  51. midGapTime = basalScheduleData[b + 1].date
  52. midGapValue = basalScheduleData[b + 1].basalRate
  53. }
  54. }
  55. }
  56. }
  57. let startDot = basalGraphStruct(basalRate: scheduled,
  58. date: priorDateTimeStamp + (priorDuration * 60))
  59. basalData.append(startDot)
  60. if midGap {
  61. let endDot1 = basalGraphStruct(basalRate: scheduled, date: midGapTime)
  62. basalData.append(endDot1)
  63. let startDot2 = basalGraphStruct(basalRate: midGapValue, date: midGapTime)
  64. basalData.append(startDot2)
  65. let endDot2 = basalGraphStruct(basalRate: midGapValue, date: dateTimeStamp)
  66. basalData.append(endDot2)
  67. } else {
  68. let endDot = basalGraphStruct(basalRate: scheduled, date: dateTimeStamp)
  69. basalData.append(endDot)
  70. }
  71. }
  72. }
  73. }
  74. // Start dot
  75. let startDot = basalGraphStruct(basalRate: basalRate, date: dateTimeStamp)
  76. basalData.append(startDot)
  77. // End dot
  78. var lastDot = dateTimeStamp + (duration * 60)
  79. if i == tempArray.count - 1, duration == 0.0 {
  80. lastDot = dateTimeStamp + (30 * 60)
  81. }
  82. latestBasal = Localizer.formatToLocalizedString(basalRate, maxFractionDigits: 2, minFractionDigits: 0)
  83. // Overlap check
  84. if i < tempArray.count - 1 {
  85. let nextEntry = tempArray[i + 1] as [String : AnyObject]?
  86. let nextDateStr = nextEntry?["timestamp"] as? String
  87. ?? nextEntry?["created_at"] as? String
  88. if let rawNext = nextDateStr,
  89. let nextDateParsed = NightscoutUtils.parseDate(rawNext) {
  90. let nextDateTimeStamp = nextDateParsed.timeIntervalSince1970
  91. if nextDateTimeStamp < (dateTimeStamp + (duration * 60)) {
  92. lastDot = nextDateTimeStamp
  93. }
  94. }
  95. }
  96. let endDot = basalGraphStruct(basalRate: basalRate, date: lastDot)
  97. basalData.append(endDot)
  98. lastEndDot = lastDot
  99. }
  100. // If last basal was prior to right now, we need to create one last scheduled entry
  101. if lastEndDot <= dateTimeUtils.getNowTimeIntervalUTC() {
  102. var scheduled = 0.0
  103. for b in 0..<basalProfile.count {
  104. let scheduleTimeToday = basalProfile[b].timeAsSeconds
  105. + dateTimeUtils.getTimeIntervalMidnightToday()
  106. if lastEndDot >= scheduleTimeToday {
  107. scheduled = basalProfile[b].value
  108. }
  109. }
  110. latestBasal = Localizer.formatToLocalizedString(scheduled,
  111. maxFractionDigits: 2,
  112. minFractionDigits: 0)
  113. let startDot = basalGraphStruct(basalRate: scheduled, date: lastEndDot)
  114. basalData.append(startDot)
  115. let endDot = basalGraphStruct(basalRate: scheduled,
  116. date: Date().timeIntervalSince1970 + (60 * 10))
  117. basalData.append(endDot)
  118. }
  119. if UserDefaultsRepository.graphBasal.value {
  120. updateBasalGraph()
  121. }
  122. if let profileBasal = profileManager.currentBasal(),
  123. profileBasal != latestBasal {
  124. latestBasal = "\(profileBasal) → \(latestBasal)"
  125. }
  126. infoManager.updateInfoData(type: .basal, value: latestBasal)
  127. }
  128. }