GlucosePage.swift 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. //
  2. // GlucosePage.swift
  3. // RileyLink
  4. //
  5. // Created by Timothy Mecklem on 10/16/16.
  6. // Copyright © 2016 Pete Schwamb. All rights reserved.
  7. //
  8. import Foundation
  9. public enum GlucosePageError: Error {
  10. case invalidCRC
  11. case unknownEventType(eventType: UInt8)
  12. }
  13. extension GlucosePageError: LocalizedError {
  14. public var errorDescription: String? {
  15. switch self {
  16. case .invalidCRC:
  17. return LocalizedString("Glucose page failed crc check", comment: "Error description for glucose page failing crc check")
  18. case .unknownEventType(let eventType):
  19. return String(format: LocalizedString("Unknown glucose record type: %$1@", comment: "Format string for error description for an unknown record type in a glucose page. (1: event type number)"), eventType)
  20. }
  21. }
  22. }
  23. public class GlucosePage {
  24. public let events: [GlucoseEvent]
  25. public let needsTimestamp: Bool
  26. public init(pageData: Data) throws {
  27. guard checkCRC16(pageData) else {
  28. self.events = [GlucoseEvent]()
  29. throw GlucosePageError.invalidCRC
  30. }
  31. //glucose page parsing happens in reverse byte order because
  32. //opcodes are at the end of each event
  33. let pageData = Data(pageData.subdata(in: 0..<1022).reversed())
  34. var offset = 0
  35. let length = pageData.count
  36. var events = [GlucoseEvent]()
  37. let calendar = Calendar.current
  38. func matchEvent(_ offset: Int, relativeTimestamp: DateComponents) -> GlucoseEvent {
  39. let remainingData = pageData.subdata(in: offset..<pageData.count)
  40. let opcode = pageData[offset]
  41. if let eventType = GlucoseEventType(rawValue: opcode) {
  42. if let event = eventType.eventType.init(availableData: remainingData, relativeTimestamp: relativeTimestamp) {
  43. return event
  44. }
  45. }
  46. if opcode >= 20 {
  47. return GlucoseSensorDataGlucoseEvent(availableData: remainingData, relativeTimestamp: relativeTimestamp)!
  48. }
  49. return UnknownGlucoseEvent(availableData: remainingData, relativeTimestamp: relativeTimestamp)!
  50. }
  51. while offset < length {
  52. // ignore null bytes
  53. if pageData[offset] == 0 {
  54. offset += 1
  55. continue
  56. } else {
  57. break
  58. }
  59. }
  60. func initialTimestamp() -> DateComponents? {
  61. var tempOffset = offset
  62. var relativeEventCount: Double = 0
  63. while tempOffset < length {
  64. let event = matchEvent(tempOffset, relativeTimestamp: DateComponents())
  65. if event is RelativeTimestampedGlucoseEvent {
  66. relativeEventCount += 1
  67. } else if let sensorTimestampEvent = event as? SensorTimestampGlucoseEvent,
  68. relativeEventCount == 0 || sensorTimestampEvent.isForwardOffsetReference() {
  69. let offsetDate = sensorTimestampEvent.timestamp.date!.addingTimeInterval(TimeInterval(minutes: relativeEventCount * 5))
  70. return calendar.dateComponents([.year, .month, .day, .hour, .minute, .calendar], from: offsetDate)
  71. } else if !(event is NineteenSomethingGlucoseEvent /* seems to be a filler byte */ || event is DataEndGlucoseEvent) {
  72. return nil
  73. }
  74. tempOffset += event.length
  75. }
  76. return nil
  77. }
  78. guard var relativeTimestamp = initialTimestamp() else {
  79. self.needsTimestamp = true
  80. self.events = events
  81. return
  82. }
  83. while offset < length {
  84. // ignore null bytes
  85. if pageData[offset] == 0 {
  86. offset += 1
  87. continue
  88. }
  89. let event = matchEvent(offset, relativeTimestamp: relativeTimestamp)
  90. if let event = event as? SensorTimestampGlucoseEvent {
  91. relativeTimestamp = event.timestamp
  92. } else if event is RelativeTimestampedGlucoseEvent {
  93. let offsetDate = relativeTimestamp.date!.addingTimeInterval(TimeInterval(minutes: -5))
  94. relativeTimestamp = calendar.dateComponents([.year, .month, .day, .hour, .minute, .calendar], from: offsetDate)
  95. }
  96. events.insert(event, at: 0)
  97. offset += event.length
  98. }
  99. self.needsTimestamp = false
  100. self.events = events
  101. }
  102. }