MissedBolusCondition.swift 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. // LoopFollow
  2. // MissedBolusCondition.swift
  3. import Foundation
  4. /// Fires when a carb entry is logged but **no** qualifying bolus is given
  5. /// within the user-defined “delay” (after allowing for a pre-bolus window).
  6. /// • Ignores small-carb treatments, tiny boluses, and low-BG scenarios.
  7. /// • Triggers once per carb entry.
  8. struct MissedBolusCondition: AlarmCondition {
  9. static let type: AlarmType = .missedBolus
  10. init() {}
  11. func evaluate(alarm: Alarm, data: AlarmData, now: Date) -> Bool {
  12. // ────────────────────────────────
  13. // 0. pull user settings
  14. // ────────────────────────────────
  15. guard
  16. let delayMin = alarm.monitoringWindow, delayMin > 0,
  17. let prebolusMin = alarm.predictiveMinutes,
  18. let minBolusU = alarm.delta, // ignore bolus ≤
  19. let minCarbGr = alarm.threshold, // ignore carbs ≤
  20. let minBG = alarm.aboveBG // ignore BG ≤
  21. else { return false }
  22. // ────────────────────────────────
  23. // 1. get most-recent carb entry
  24. // ────────────────────────────────
  25. guard let carb = data.recentCarbs.last else { return false }
  26. // – must be at least `delayMin` old, but not older than 60 min
  27. guard carb.date > now.addingTimeInterval(-3600),
  28. carb.date < now.addingTimeInterval(-Double(delayMin) * 60)
  29. else { return false }
  30. // – ignore tiny carbs
  31. guard carb.grams > minCarbGr else { return false }
  32. // – ignore if BG is low
  33. if let latestBG = data.bgReadings.last,
  34. Double(latestBG.sgv) <= minBG { return false }
  35. // ────────────────────────────────
  36. // 2. already alerted for this carb?
  37. // ────────────────────────────────
  38. if let lastFired = Storage.shared.lastMissedBolusNotified.value,
  39. carb.date <= lastFired { return false }
  40. // ────────────────────────────────
  41. // 3. look for a valid bolus
  42. // ────────────────────────────────
  43. let windowStart = carb.date.addingTimeInterval(-Double(prebolusMin) * 60)
  44. let hasBolus = data.recentBoluses.contains { bolus in
  45. bolus.date >= windowStart && bolus.units > minBolusU
  46. }
  47. guard !hasBolus else { return false }
  48. // ────────────────────────────────
  49. // 4. trigger!
  50. // ────────────────────────────────
  51. Storage.shared.lastMissedBolusNotified.value = carb.date
  52. return true
  53. }
  54. }