VersionResponse.swift 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. //
  2. // VersionResponse.swift
  3. // OmniKit
  4. //
  5. // Created by Pete Schwamb on 2/12/18.
  6. // Copyright © 2018 Pete Schwamb. All rights reserved.
  7. //
  8. import Foundation
  9. fileprivate let assignAddressVersionLength: UInt8 = 0x15
  10. fileprivate let setupPodVersionLength: UInt8 = 0x1B
  11. public struct VersionResponse : MessageBlock {
  12. public struct FirmwareVersion : CustomStringConvertible {
  13. let major: UInt8
  14. let minor: UInt8
  15. let patch: UInt8
  16. public init(encodedData: Data) {
  17. major = encodedData[0]
  18. minor = encodedData[1]
  19. patch = encodedData[2]
  20. }
  21. public var description: String {
  22. return "\(major).\(minor).\(patch)"
  23. }
  24. }
  25. public let blockType: MessageBlockType = .versionResponse
  26. public let pmVersion: FirmwareVersion
  27. public let piVersion: FirmwareVersion
  28. public let lot: UInt32
  29. public let tid: UInt32
  30. public let address: UInt32
  31. public let productID: UInt8 // always 2 (for PM = PI = 2.7.0), 2nd gen Omnipod?
  32. public let podProgressStatus: PodProgressStatus
  33. // These values only included in the shorter 0x15 VersionResponse for the AssignAddress command.
  34. public let gain: UInt8? // 2-bit value, max gain is at 0, min gain is at 2
  35. public let rssi: UInt8? // 6-bit value, max rssi seen 61
  36. // These values only included in the longer 0x1B VersionResponse for the SetupPod command.
  37. public let pulseSize: Double? // VVVV / 100,000, must be 0x1388 / 100,000 = 0.05U
  38. public let secondsPerBolusPulse: Double? // BR / 8, nominally 0x10 / 8 = 2 seconds per pulse
  39. public let secondsPerPrimePulse: Double? // PR / 8, nominally 0x08 / 8 = 1 seconds per priming pulse
  40. public let primeUnits: Double? // PP * pulseSize, nominally 0x34 * 0.05U = 2.6U
  41. public let cannulaInsertionUnits: Double? // CP * pulseSize, nominally 0x0A * 0.05U = 0.5U
  42. public let serviceDuration: TimeInterval? // PL hours, nominally 0x50 = 80 hours
  43. public let data: Data
  44. public init(encodedData: Data) throws {
  45. let responseLength = encodedData[1]
  46. data = encodedData.subdata(in: 0..<Int(responseLength + 2))
  47. switch responseLength {
  48. case assignAddressVersionLength:
  49. // This is the shorter 0x15 response for the 07 AssignAddress command.
  50. // 0 1 2 5 8 9 10 14 18 19
  51. // 01 LL MXMYMZ IXIYIZ ID 0J LLLLLLLL TTTTTTTT GS IIIIIIII
  52. // 01 15 020700 020700 02 02 0000a377 0003ab37 9f 1f00ee87
  53. //
  54. // LL = 0x15 (assignAddressVersionLength)
  55. // PM MX.MY.MZ = 02.07.02 (for PM 2.7.0)
  56. // PI IX.IY.IZ = 02.07.02 (for PI 2.7.0)
  57. // ID = Product ID (always 02 for PM = PI = 2.7.0)
  58. // 0J = Pod progress state (typically 02, but could be 01, for this particular response)
  59. // LLLLLLLL = Lot
  60. // TTTTTTTT = Tid
  61. // GS = ggssssss (Gain/RSSI)
  62. // IIIIIIII = connection ID address
  63. pmVersion = FirmwareVersion(encodedData: encodedData.subdata(in: 2..<5))
  64. piVersion = FirmwareVersion(encodedData: encodedData.subdata(in: 5..<8))
  65. productID = encodedData[8]
  66. guard let progressStatus = PodProgressStatus(rawValue: encodedData[9]) else {
  67. throw MessageBlockError.parseError
  68. }
  69. podProgressStatus = progressStatus
  70. lot = encodedData[10...].toBigEndian(UInt32.self)
  71. tid = encodedData[14...].toBigEndian(UInt32.self)
  72. gain = (encodedData[18] & 0xc0) >> 6
  73. rssi = encodedData[18] & 0x3f
  74. address = encodedData[19...].toBigEndian(UInt32.self)
  75. // These values only included in the longer 0x1B VersionResponse for the 03 SetupPod command.
  76. pulseSize = nil
  77. secondsPerBolusPulse = nil
  78. secondsPerPrimePulse = nil
  79. primeUnits = nil
  80. cannulaInsertionUnits = nil
  81. serviceDuration = nil
  82. case setupPodVersionLength:
  83. // This is the longer 0x1B response for the 03 SetupPod command.
  84. // 0 1 2 4 5 6 7 8 9 12 15 16 17 21 25
  85. // 01 LL VVVV BR PR PP CP PL MXMYMZ IXIYIZ ID 0J LLLLLLLL TTTTTTTT IIIIIIII
  86. // 01 1b 1388 10 08 34 0a 50 020700 020700 02 03 0000a62b 00044794 1f00ee87
  87. //
  88. // LL = 0x1b (setupPodVersionMessageLength)
  89. // VVVV = 0x1388, pulse Volume in micro-units of U100 insulin per tenth of pulse (5000/100000 = 0.05U per pulse)
  90. // BR = 0x10, Basic pulse Rate in # of eighth secs per pulse (16/8 = 2 seconds per pulse)
  91. // PR = 0x08, Prime pulse Rate in # of eighth secs per pulse for priming boluses (8/8 = 1 second per priming pulse)
  92. // PP = 0x34 = 52, # of Prime Pulses (52 pulses x 0.05U/pulse = 2.6U)
  93. // CP = 0x0A = 10, # of Cannula insertion Pulses (10 pulses x 0.05U/pulse = 0.5U)
  94. // PL = 0x50 = 80, # of hours maximum Pod Life
  95. // PM = MX.MY.MZ = 02.07.02 (for PM 2.7.0)
  96. // PI = IX.IY.IZ = 02.07.02 (for PI 2.7.0)
  97. // ID = Product ID (always 02 for PM = PI = 2.7.0)
  98. // 0J = Pod progress state (should be 03 for this particular response)
  99. // LLLLLLLL = Lot
  100. // TTTTTTTT = Tid
  101. // IIIIIIII = connection ID address
  102. pmVersion = FirmwareVersion(encodedData: encodedData.subdata(in: 9..<12))
  103. piVersion = FirmwareVersion(encodedData: encodedData.subdata(in: 12..<15))
  104. productID = encodedData[15]
  105. guard let progressStatus = PodProgressStatus(rawValue: encodedData[16]) else {
  106. throw MessageBlockError.parseError
  107. }
  108. podProgressStatus = progressStatus
  109. lot = encodedData[17...].toBigEndian(UInt32.self)
  110. tid = encodedData[21...].toBigEndian(UInt32.self)
  111. address = encodedData[25...].toBigEndian(UInt32.self)
  112. // These values should be verified elsewhere and appropriately handled.
  113. pulseSize = Double(encodedData[2...].toBigEndian(UInt16.self)) / 100000
  114. secondsPerBolusPulse = Double(encodedData[4]) / 8
  115. secondsPerPrimePulse = Double(encodedData[5]) / 8
  116. primeUnits = Double(encodedData[6]) * Pod.pulseSize
  117. cannulaInsertionUnits = Double(encodedData[7]) * Pod.pulseSize
  118. serviceDuration = TimeInterval.hours(Double(encodedData[8]))
  119. // These values only included in the shorter 0x15 VersionResponse for the AssignAddress command.
  120. gain = nil
  121. rssi = nil
  122. default:
  123. throw MessageBlockError.parseError
  124. }
  125. }
  126. public var isAssignAddressVersionResponse: Bool {
  127. return self.data.count == assignAddressVersionLength + 2
  128. }
  129. public var isSetupPodVersionResponse: Bool {
  130. return self.data.count == setupPodVersionLength + 2
  131. }
  132. }
  133. extension VersionResponse: CustomDebugStringConvertible {
  134. public var debugDescription: String {
  135. return "VersionResponse(lot:\(lot), tid:\(tid), address:\(Data(bigEndian: address).hexadecimalString), pmVersion:\(pmVersion), piVersion:\(piVersion), productID:\(productID), podProgressStatus:\(podProgressStatus), gain:\(gain?.description ?? "NA"), rssi:\(rssi?.description ?? "NA"), pulseSize:\(pulseSize?.description ?? "NA"), secondsPerBolusPulse:\(secondsPerBolusPulse?.description ?? "NA"), secondsPerPrimePulse:\(secondsPerPrimePulse?.description ?? "NA"), primeUnits:\(primeUnits?.description ?? "NA"), cannulaInsertionUnits:\(cannulaInsertionUnits?.description ?? "NA"), serviceDuration:\(serviceDuration?.description ?? "NA"), )"
  136. }
  137. }