IOBCondition.swift 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. // LoopFollow
  2. // IOBCondition.swift
  3. // Created by Jonas Björkert on 2025-05-19.
  4. //
  5. // IOBCondition.swift
  6. // LoopFollow
  7. //
  8. // Created by Jonas Björkert on 2025-05-17.
  9. //
  10. import Foundation
  11. /// Fires when **any** of three rules is true
  12. /// ──────────────────────────────────────────
  13. /// 1. latest IOB ≥ `threshold`
  14. /// 2. within the last `lookbackMinutes`, the **count** of boluses
  15. /// ≥ `monitoringWindow` **and** each bolus ≥ `delta` units
  16. /// 3. within that same window, the **sum** of boluses ≥ `threshold`
  17. struct IOBCondition: AlarmCondition {
  18. static let type: AlarmType = .iob
  19. init() {}
  20. /// Convenience accessors to keep the code tidy
  21. private struct Params {
  22. let iobMax: Double // alarm.threshold (units)
  23. let minBolus: Double // alarm.delta (units)
  24. let countNeeded: Int // alarm.monitoringWindow (bolus count)
  25. let lookbackMin: Int // alarm.predictiveMinutes (minutes)
  26. init?(alarm: Alarm) {
  27. guard
  28. let iobMax = alarm.threshold,
  29. let minBolus = alarm.delta,
  30. let count = alarm.monitoringWindow,
  31. let mins = alarm.predictiveMinutes,
  32. iobMax > 0, minBolus > 0, count > 0, mins > 0
  33. else { return nil }
  34. self.iobMax = iobMax
  35. self.minBolus = minBolus
  36. countNeeded = count
  37. lookbackMin = mins
  38. }
  39. }
  40. func evaluate(alarm: Alarm, data: AlarmData, now _: Date) -> Bool {
  41. // ── 0. pull and sanity-check parameters ────────────────────────────
  42. guard let p = Params(alarm: alarm) else { return false }
  43. // ── 1. latest IOB alone high enough? ───────────────────────────────
  44. if let iob = data.IOB, iob >= p.iobMax { return true }
  45. // ── 2. look at the recent boluses ──────────────────────────────────
  46. let cutoff = Date().addingTimeInterval(-Double(p.lookbackMin) * 60)
  47. var count = 0
  48. var total = 0.0
  49. for b in data.recentBoluses where b.date >= cutoff && b.units >= p.minBolus {
  50. count += 1
  51. total += b.units
  52. if count >= p.countNeeded || total >= p.iobMax { return true }
  53. }
  54. return false
  55. }
  56. }