DoseStore.swift 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. //
  2. // DoseStore.swift
  3. // Loop
  4. //
  5. // Created by Nate Racklyeft on 7/31/16.
  6. // Copyright © 2016 Nathan Racklyeft. All rights reserved.
  7. //
  8. import LoopKit
  9. // Bridges support for MinimedKit data types
  10. extension Collection where Element == TimestampedHistoryEvent {
  11. func pumpEvents(from model: PumpModel) -> [NewPumpEvent] {
  12. var events: [NewPumpEvent] = []
  13. var lastTempBasal: DoseEntry?
  14. var lastSuspend: DoseEntry?
  15. // Always assume the sequence may have started rewound. LoopKit will ignore unmatched resume events.
  16. var isRewound = true
  17. var title: String
  18. let now = Date()
  19. for event in self {
  20. var dose: DoseEntry?
  21. var eventType: LoopKit.PumpEventType?
  22. title = String(describing: type(of: event.pumpEvent))
  23. switch event.pumpEvent {
  24. case let bolus as BolusNormalPumpEvent:
  25. title = LocalizedString("Bolus", comment: "Event title for bolus")
  26. let bolusEndDate: Date
  27. if let lastSuspend = lastSuspend, bolus.programmed != bolus.amount, lastSuspend.startDate > event.date {
  28. bolusEndDate = lastSuspend.startDate
  29. } else if bolus.duration > 0 {
  30. bolusEndDate = event.date.addingTimeInterval(bolus.duration)
  31. } else {
  32. bolusEndDate = event.date.addingTimeInterval(model.bolusDeliveryTime(units: bolus.amount))
  33. }
  34. var automatic: Bool?
  35. if !bolus.wasRemotelyTriggered {
  36. automatic = false
  37. }
  38. dose = DoseEntry(type: .bolus, startDate: event.date, endDate: bolusEndDate, value: bolus.programmed, unit: .units, deliveredUnits: bolus.amount, automatic: automatic, isMutable: event.isMutable(atDate: now, forPump: model), wasProgrammedByPumpUI: !bolus.wasRemotelyTriggered)
  39. case let suspendEvent as SuspendPumpEvent:
  40. title = LocalizedString("Suspend", comment: "Event title for suspend")
  41. dose = DoseEntry(suspendDate: event.date, wasProgrammedByPumpUI: !suspendEvent.wasRemotelyTriggered)
  42. lastSuspend = dose
  43. case let resumeEvent as ResumePumpEvent:
  44. title = LocalizedString("Resume", comment: "Event title for resume")
  45. dose = DoseEntry(resumeDate: event.date, wasProgrammedByPumpUI: !resumeEvent.wasRemotelyTriggered)
  46. case let temp as TempBasalPumpEvent:
  47. if case .Absolute = temp.rateType {
  48. lastTempBasal = DoseEntry(type: .tempBasal, startDate: event.date, value: temp.rate, unit: .unitsPerHour, isMutable: event.isMutable(atDate: now, forPump: model), wasProgrammedByPumpUI: !temp.wasRemotelyTriggered)
  49. continue
  50. } else {
  51. title = LocalizedString("Percent Temp Basal", comment: "Event title for percent based temp basal")
  52. }
  53. case let tempDuration as TempBasalDurationPumpEvent:
  54. if let lastTemp = lastTempBasal, lastTemp.startDate == event.date {
  55. if tempDuration.duration == 0 {
  56. title = LocalizedString("Cancel Temp Basal", comment: "Event title for temp basal cancel")
  57. } else {
  58. title = LocalizedString("Temp Basal", comment: "Event title for temporary basal rate start")
  59. }
  60. dose = DoseEntry(
  61. type: .tempBasal,
  62. startDate: event.date,
  63. endDate: event.date.addingTimeInterval(TimeInterval(minutes: Double(tempDuration.duration))),
  64. value: lastTemp.unitsPerHour,
  65. unit: .unitsPerHour,
  66. automatic: false, // If this was automatic dose, it should be set as such during reconciliation
  67. isMutable: event.isMutable(atDate: now, forPump: model),
  68. wasProgrammedByPumpUI: lastTemp.wasProgrammedByPumpUI
  69. )
  70. }
  71. case let basal as BasalProfileStartPumpEvent:
  72. title = LocalizedString("Scheduled Basal", comment: "Event title for starting scheduled basal")
  73. dose = DoseEntry(
  74. type: .basal,
  75. startDate: event.date,
  76. // Use the maximum-possible duration for a basal entry; its true duration will be reconciled against other entries.
  77. endDate: event.date.addingTimeInterval(.hours(24)),
  78. value: basal.scheduleEntry.rate,
  79. unit: .unitsPerHour,
  80. isMutable: event.isMutable(atDate: now, forPump: model)
  81. )
  82. case is RewindPumpEvent:
  83. title = LocalizedString("Rewind", comment: "Event title for rewind")
  84. eventType = .rewind
  85. /*
  86. No insulin is delivered between the beginning of a rewind until the suggested fixed prime is delivered or cancelled.
  87. If the fixed prime is cancelled, it is never recorded in history. It is possible to cancel a fixed prime and perform one manually some time later, but basal delivery will have resumed during that period.
  88. We take the conservative approach and assume delivery is paused only between the Rewind and the first Prime event.
  89. */
  90. dose = DoseEntry(suspendDate: event.date)
  91. isRewound = true
  92. case is PrimePumpEvent:
  93. title = LocalizedString("Prime", comment: "Event title for rewind")
  94. eventType = .prime
  95. if isRewound {
  96. isRewound = false
  97. dose = DoseEntry(resumeDate: event.date)
  98. }
  99. case let alarm as PumpAlarmPumpEvent:
  100. title = alarm.alarmType.localizedString
  101. eventType = .alarm
  102. if case .noDelivery = alarm.alarmType {
  103. dose = DoseEntry(suspendDate: event.date)
  104. }
  105. break
  106. case let alarm as ClearAlarmPumpEvent:
  107. title = "Clear Alarm"
  108. eventType = .alarmClear
  109. if case .noDelivery = alarm.alarmType {
  110. dose = DoseEntry(resumeDate: event.date)
  111. }
  112. break
  113. case is JournalEntryMealMarkerPumpEvent:
  114. title = "Meal"
  115. break
  116. case is JournalEntryPumpLowBatteryPumpEvent:
  117. title = "Low Battery"
  118. break
  119. case is JournalEntryPumpLowReservoirPumpEvent:
  120. title = "Low Reservoir"
  121. break
  122. case is ChangeBasalProfilePumpEvent:
  123. title = "Change Basal Schedule"
  124. break
  125. case is ChangeBasalProfilePatternPumpEvent:
  126. title = "Change Basal Profile Schedule"
  127. break
  128. case is SelectBasalProfilePumpEvent:
  129. title = "Select Profile"
  130. break
  131. case is ChangeTimePumpEvent:
  132. title = "Change Time"
  133. break
  134. case is NewTimePumpEvent:
  135. title = "New Time"
  136. break
  137. default:
  138. break
  139. }
  140. events.append(NewPumpEvent(date: event.date, dose: dose, raw: event.pumpEvent.rawData, title: title, type: eventType))
  141. }
  142. return events
  143. }
  144. }