BasalSchedule.swift 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. //
  2. // BasalSchedule.swift
  3. // RileyLink
  4. //
  5. // Created by Pete Schwamb on 5/6/17.
  6. // Copyright © 2017 Pete Schwamb. All rights reserved.
  7. //
  8. import Foundation
  9. public struct BasalScheduleEntry {
  10. public let index: Int
  11. public let timeOffset: TimeInterval
  12. public let rate: Double // U/hour
  13. public init(index: Int, timeOffset: TimeInterval, rate: Double) {
  14. self.index = index
  15. self.timeOffset = timeOffset
  16. self.rate = rate
  17. }
  18. }
  19. public struct BasalSchedule {
  20. public let entries: [BasalScheduleEntry]
  21. public init(entries: [BasalScheduleEntry]) {
  22. self.entries = entries
  23. }
  24. }
  25. extension BasalSchedule {
  26. static let rawValueLength = 192
  27. public typealias RawValue = Data
  28. public init?(rawValue: RawValue) {
  29. var entries = [BasalScheduleEntry]()
  30. for tuple in sequence(first: (index: 0, offset: 0), next: { (index: $0.index + 1, $0.offset + 3) }) {
  31. let beginOfRange = tuple.offset
  32. let endOfRange = beginOfRange + 3
  33. guard endOfRange < rawValue.count else {
  34. break
  35. }
  36. if let entry = BasalScheduleEntry(
  37. index: tuple.index,
  38. rawValue: rawValue[beginOfRange..<endOfRange]
  39. ) {
  40. if let last = entries.last, last.timeOffset >= entry.timeOffset {
  41. // Stop if the new timeOffset isn't greater than the last one
  42. break
  43. }
  44. entries.append(entry)
  45. } else {
  46. // Stop if we can't decode the entry
  47. break
  48. }
  49. }
  50. guard entries.count > 0 else {
  51. return nil
  52. }
  53. self.init(entries: entries)
  54. }
  55. public var rawValue: RawValue {
  56. var buffer = Data(count: BasalSchedule.rawValueLength)
  57. var byteIndex = 0
  58. for rawEntry in entries.map({ $0.rawValue }) {
  59. buffer.replaceSubrange(byteIndex..<(byteIndex + rawEntry.count), with: rawEntry)
  60. byteIndex += rawEntry.count
  61. }
  62. // Send the special "empty" code to clear a schedule
  63. if entries.count == 0 {
  64. buffer[2] = 0x3f
  65. }
  66. return buffer
  67. }
  68. }
  69. private extension BasalScheduleEntry {
  70. static let rawValueLength = 3
  71. typealias RawValue = Data
  72. init?(index: Int, rawValue: RawValue) {
  73. guard rawValue.count == BasalScheduleEntry.rawValueLength else {
  74. return nil
  75. }
  76. let rawRate = rawValue[rawValue.startIndex..<rawValue.startIndex.advanced(by: 2)]
  77. let rate = Double(rawRate.to(UInt16.self)) / 40.0
  78. let offsetMinutes = Double(rawValue.last!) * 30
  79. let timeOffset = TimeInterval(minutes: offsetMinutes)
  80. // 0x3f *30*60 denotes a stop value
  81. guard timeOffset < .hours(24) else {
  82. return nil
  83. }
  84. self.init(index: index, timeOffset: timeOffset, rate: rate)
  85. }
  86. var rawValue: RawValue {
  87. var buffer = Data(count: type(of: self).rawValueLength)
  88. var rate = UInt16(clamping: Int(self.rate * 40))
  89. buffer.replaceSubrange(0..<2, with: Data(bytes: &rate, count: rate.bitWidth / 8))
  90. buffer[2] = UInt8(clamping: Int(timeOffset.minutes / 30))
  91. return buffer
  92. }
  93. }