PumpModel.swift 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. //
  2. // PumpModel.swift
  3. // RileyLink
  4. //
  5. // Created by Pete Schwamb on 3/7/16.
  6. // Copyright © 2016 Pete Schwamb. All rights reserved.
  7. //
  8. /// Represents a pump model and its defining characteristics.
  9. /// This class implements the `RawRepresentable` protocol
  10. public enum PumpModel: String {
  11. case model508 = "508"
  12. case model511 = "511"
  13. case model711 = "711"
  14. case model512 = "512"
  15. case model712 = "712"
  16. case model515 = "515"
  17. case model715 = "715"
  18. case model522 = "522"
  19. case model722 = "722"
  20. case model523 = "523"
  21. case model723 = "723"
  22. case model530 = "530"
  23. case model730 = "730"
  24. case model540 = "540"
  25. case model740 = "740"
  26. case model551 = "551"
  27. case model751 = "751"
  28. case model554 = "554"
  29. case model754 = "754"
  30. private var size: Int {
  31. return Int(rawValue)! / 100
  32. }
  33. private var generation: Int {
  34. return Int(rawValue)! % 100
  35. }
  36. /// Identifies pumps that support a major-generation shift in record format, starting with the x23.
  37. /// Mirrors the "larger" flag as defined by decoding-carelink
  38. public var larger: Bool {
  39. return generation >= 23
  40. }
  41. // On newer pumps, square wave boluses are added to history on start of delivery, and updated in place
  42. // when delivery is finished
  43. public var appendsSquareWaveToHistoryOnStartOfDelivery: Bool {
  44. return generation >= 23
  45. }
  46. public var hasMySentry: Bool {
  47. return generation >= 23
  48. }
  49. var hasLowSuspend: Bool {
  50. return generation >= 51
  51. }
  52. public var recordsBasalProfileStartEvents: Bool {
  53. return generation >= 23
  54. }
  55. /// Newer models allow higher precision delivery, and have bit packing to accomodate this.
  56. public var insulinBitPackingScale: Int {
  57. return (generation >= 23) ? 40 : 10
  58. }
  59. /// Pulses per unit is the inverse of the minimum volume of delivery.
  60. public var pulsesPerUnit: Int {
  61. return (generation >= 23) ? 40 : 20
  62. }
  63. public var reservoirCapacity: Int {
  64. switch size {
  65. case 5:
  66. return 176
  67. case 7:
  68. return 300
  69. default:
  70. fatalError("Unknown reservoir capacity for PumpModel.\(self)")
  71. }
  72. }
  73. /// Even though this is capped by the system at 250 / 10 U, the message takes a UInt16.
  74. var usesTwoBytesForMaxBolus: Bool {
  75. return generation >= 23
  76. }
  77. public var supportedBasalRates: [Double] {
  78. if generation >= 23 {
  79. // 0.025 units (for rates between 0.0-0.975 U/h)
  80. let rateGroup1 = ((0...39).map { Double($0) / Double(pulsesPerUnit) })
  81. // 0.05 units (for rates between 1-9.95 U/h)
  82. let rateGroup2 = ((20...199).map { Double($0) / Double(pulsesPerUnit/2) })
  83. // 0.1 units (for rates between 10-35 U/h)
  84. let rateGroup3 = ((100...350).map { Double($0) / Double(pulsesPerUnit/4) })
  85. return rateGroup1 + rateGroup2 + rateGroup3
  86. } else {
  87. // 0.05 units for rates between 0.0-35U/hr
  88. return (0...700).map { Double($0) / Double(pulsesPerUnit) }
  89. }
  90. }
  91. public var maximumBolusVolume: Int {
  92. return 25
  93. }
  94. public var maximumBasalRate: Double {
  95. return 35
  96. }
  97. public var supportedBolusVolumes: [Double] {
  98. if generation >= 23 {
  99. let breakpoints: [Int] = [0,1,10,maximumBolusVolume]
  100. let scales: [Int] = [40,20,10]
  101. let scalingGroups = zip(scales, (zip(breakpoints, breakpoints[1...]).map {($0.0)...$0.1}))
  102. let segments = scalingGroups.map { (scale, range) -> [Double] in
  103. let scaledRanges = ((range.lowerBound*scale+1)...(range.upperBound*scale))
  104. return scaledRanges.map { Double($0) / Double(scale) }
  105. }
  106. return segments.flatMap { $0 }
  107. } else {
  108. return (1...(maximumBolusVolume*10)).map { Double($0) / 10.0 }
  109. }
  110. }
  111. public var maximumBasalScheduleEntryCount: Int {
  112. return 48
  113. }
  114. public var minimumBasalScheduleEntryDuration: TimeInterval {
  115. return .minutes(30)
  116. }
  117. public var isDeliveryRateVariable: Bool {
  118. return generation >= 23
  119. }
  120. public func bolusDeliveryTime(units: Double) -> TimeInterval {
  121. let unitsPerMinute: Double
  122. if isDeliveryRateVariable {
  123. switch units {
  124. case let u where u < 1.0:
  125. unitsPerMinute = 0.75
  126. case let u where u > 7.5:
  127. unitsPerMinute = units / 5
  128. default:
  129. unitsPerMinute = 1.5
  130. }
  131. } else {
  132. unitsPerMinute = 1.5
  133. }
  134. return TimeInterval(minutes: units / unitsPerMinute)
  135. }
  136. public func estimateTempBasalProgress(unitsPerHour: Double, duration: TimeInterval, elapsed: TimeInterval) -> (deliveredUnits: Double, progress: Double) {
  137. let roundedVolume = round(unitsPerHour * elapsed.hours * Double(pulsesPerUnit)) / Double(pulsesPerUnit)
  138. return (deliveredUnits: roundedVolume, progress: min(elapsed / duration, 1))
  139. }
  140. public func estimateBolusProgress(elapsed: TimeInterval, programmedUnits: Double) -> (deliveredUnits: Double, progress: Double) {
  141. let duration = bolusDeliveryTime(units: programmedUnits)
  142. let timeProgress = min(elapsed / duration, 1)
  143. let updateResolution: Double
  144. let unroundedVolume: Double
  145. if isDeliveryRateVariable {
  146. if programmedUnits < 1 {
  147. updateResolution = 40 // Resolution = 0.025
  148. unroundedVolume = timeProgress * programmedUnits
  149. } else {
  150. var remainingUnits = programmedUnits
  151. var baseDuration: TimeInterval = 0
  152. var overlay1Duration: TimeInterval = 0
  153. var overlay2Duration: TimeInterval = 0
  154. let baseDeliveryRate = 1.5 / TimeInterval(minutes: 1)
  155. baseDuration = min(duration, remainingUnits / baseDeliveryRate)
  156. remainingUnits -= baseDuration * baseDeliveryRate
  157. overlay1Duration = min(duration, remainingUnits / baseDeliveryRate)
  158. remainingUnits -= overlay1Duration * baseDeliveryRate
  159. overlay2Duration = min(duration, remainingUnits / baseDeliveryRate)
  160. remainingUnits -= overlay2Duration * baseDeliveryRate
  161. unroundedVolume = (min(elapsed, baseDuration) + min(elapsed, overlay1Duration) + min(elapsed, overlay2Duration)) * baseDeliveryRate
  162. if overlay1Duration > elapsed {
  163. updateResolution = 10 // Resolution = 0.1
  164. } else {
  165. updateResolution = 20 // Resolution = 0.05
  166. }
  167. }
  168. } else {
  169. updateResolution = 20 // Resolution = 0.05
  170. unroundedVolume = timeProgress * programmedUnits
  171. }
  172. let roundedVolume = round(unroundedVolume * updateResolution) / updateResolution
  173. return (deliveredUnits: roundedVolume, progress: roundedVolume / programmedUnits)
  174. }
  175. }
  176. extension PumpModel: CustomStringConvertible {
  177. public var description: String {
  178. return rawValue
  179. }
  180. }