Targets.swift 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import Foundation
  2. struct Targets {
  3. // The Javascript implementation was hard to port because it
  4. // mutates the inputs in a way that is visible in the Profile.
  5. //
  6. // TODO: See if we can get rid of the logic that mutates inputs in Javascript
  7. static func lookup(
  8. targets: BGTargets,
  9. tempTargets: [TempTarget],
  10. profile: Profile,
  11. now: Date
  12. ) throws -> (ComputedBGTargets, Int) {
  13. // Find current target
  14. var bgComputedTargets = targets.targets
  15. .map { ComputedBGTargetEntry(low: $0.low, high: $0.high, start: $0.start, offset: $0.offset) }
  16. guard !bgComputedTargets.isEmpty else {
  17. throw ProfileError.invalidBgTargets
  18. }
  19. var targetIdx = bgComputedTargets.count - 1
  20. for (idx, (curr, next)) in zip(bgComputedTargets, bgComputedTargets.dropFirst()).enumerated() {
  21. if try now.isMinutesFromMidnightWithinRange(lowerBound: curr.offset, upperBound: next.offset) {
  22. targetIdx = idx
  23. break
  24. }
  25. }
  26. // Apply profile target if specified
  27. if let targetBg = profile.targetBg {
  28. bgComputedTargets[targetIdx].low = targetBg
  29. }
  30. bgComputedTargets[targetIdx].high = bgComputedTargets[targetIdx].low
  31. // Handle temp targets
  32. let sortedTempTargets = tempTargets.sorted { $0.createdAt > $1.createdAt }
  33. for target in sortedTempTargets {
  34. let start = target.createdAt
  35. let expires = start.addingTimeInterval(Double(target.duration) * 60)
  36. if now >= start, target.duration == 0 {
  37. // Cancel temp targets
  38. break
  39. } else if let targetBottom = target.targetBottom,
  40. let targetTop = target.targetTop
  41. {
  42. if now >= start, now < expires {
  43. bgComputedTargets[targetIdx].high = targetTop
  44. bgComputedTargets[targetIdx].low = targetBottom
  45. bgComputedTargets[targetIdx].temptargetSet = true
  46. break
  47. }
  48. } else {
  49. warning(.openAPS, "eventualBG target range invalid: \(target.targetBottom ?? -1)-\(target.targetTop ?? -1)")
  50. break
  51. }
  52. }
  53. return (
  54. ComputedBGTargets(units: targets.units, userPreferredUnits: targets.userPreferredUnits, targets: bgComputedTargets),
  55. targetIdx
  56. )
  57. }
  58. static func boundTargetRange(_ entry: ComputedBGTargetEntry) -> ComputedBGTargetEntry {
  59. var target = entry
  60. // hard-code lower bounds for min_bg and max_bg in case pump is set too low, or units are wrong
  61. var maxBg = max(80, target.high)
  62. var minBg = max(80, target.low)
  63. // hard-code upper bound for min_bg in case pump is set too high
  64. minBg = min(200, minBg)
  65. maxBg = min(200, maxBg)
  66. target.minBg = minBg
  67. target.maxBg = maxBg
  68. return target
  69. }
  70. static func bgTargetsLookup(
  71. targets: BGTargets,
  72. tempTargets: [TempTarget],
  73. profile: Profile,
  74. now: Date = Date()
  75. ) throws -> (ComputedBGTargets, ComputedBGTargetEntry) {
  76. var (computedBgTargets, targetIdx) = try lookup(targets: targets, tempTargets: tempTargets, profile: profile, now: now)
  77. let currentTarget = boundTargetRange(computedBgTargets.targets[targetIdx])
  78. computedBgTargets.targets[targetIdx] = currentTarget
  79. return (computedBgTargets, currentTarget)
  80. }
  81. }