LowBGCondition.swift 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. // LoopFollow
  2. // LowBGCondition.swift
  3. // Created by Jonas Björkert.
  4. import Foundation
  5. /// Fires when:
  6. /// • every BG in `persistentMinutes` (if set) **and** the latest BG are ≤ `threshold`; **or**
  7. /// • any predicted BG within `predictiveMinutes` is ≤ `threshold`.
  8. struct LowBGCondition: AlarmCondition {
  9. static let type: AlarmType = .low
  10. init() {}
  11. func evaluate(alarm: Alarm, data: AlarmData, now _: Date) -> Bool {
  12. // ────────────────────────────────
  13. // 0. sanity checks
  14. // ────────────────────────────────
  15. guard let belowBG = alarm.belowBG else { return false }
  16. func isLow(_ g: GlucoseValue) -> Bool {
  17. g.sgv > 0 && Double(g.sgv) <= belowBG
  18. }
  19. // ────────────────────────────────
  20. // 1. predictive low?
  21. // ────────────────────────────────
  22. var predictiveTrigger = false
  23. if let predictiveMinutes = alarm.predictiveMinutes,
  24. predictiveMinutes > 0,
  25. !data.predictionData.isEmpty
  26. {
  27. let lookAhead = min(
  28. data.predictionData.count,
  29. Int(ceil(Double(predictiveMinutes) / 5.0))
  30. )
  31. for i in 0 ..< lookAhead where isLow(data.predictionData[i]) {
  32. predictiveTrigger = true
  33. break
  34. }
  35. }
  36. // ────────────────────────────────
  37. // 2. persistent low window (ALL readings must be low)
  38. // ────────────────────────────────
  39. var persistentOK = true
  40. if let persistentMinutes = alarm.persistentMinutes,
  41. persistentMinutes > 0
  42. {
  43. let window = Int(ceil(Double(persistentMinutes) / 5.0))
  44. if data.bgReadings.count >= window {
  45. let recent = data.bgReadings.suffix(window)
  46. persistentOK = recent.allSatisfy(isLow)
  47. } else {
  48. // not enough samples to prove persistence ⇒ don’t alarm yet
  49. persistentOK = false
  50. }
  51. }
  52. // ────────────────────────────────
  53. // 3. final decision
  54. // ────────────────────────────────
  55. return persistentOK || predictiveTrigger
  56. }
  57. }