MessageTests.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. //
  2. // MessageTests.swift
  3. // OmniBLE
  4. //
  5. // Created by Pete Schwamb on 10/14/17.
  6. // Copyright © 2017 Pete Schwamb. All rights reserved.
  7. // From OmniKitTests/MessageTests.swift
  8. //
  9. import XCTest
  10. @testable import OmniBLE
  11. class MessageTests: XCTestCase {
  12. func testMessageData() {
  13. // 2016-06-26T20:33:28.412197 ID1:1f01482a PTYPE:PDM SEQ:13 ID2:1f01482a B9:10 BLEN:3 BODY:0e0100802c CRC:88
  14. let msg = Message(address: 0x1f01482a, messageBlocks: [GetStatusCommand()], sequenceNum: 4)
  15. XCTAssertEqual("1f01482a10030e0100802c", msg.encoded().hexadecimalString)
  16. }
  17. func testMessageDecoding() {
  18. do {
  19. let msg = try Message(encodedData: Data(hexadecimalString: "1f00ee84300a1d18003f1800004297ff8128")!)
  20. XCTAssertEqual(0x1f00ee84, msg.address)
  21. XCTAssertEqual(12, msg.sequenceNum)
  22. let messageBlocks = msg.messageBlocks
  23. XCTAssertEqual(1, messageBlocks.count)
  24. let statusResponse = messageBlocks[0] as! StatusResponse
  25. XCTAssertEqual(Pod.reservoirLevelAboveThresholdMagicNumber, statusResponse.reservoirLevel, accuracy: 0.01)
  26. XCTAssertEqual(TimeInterval(minutes: 4261), statusResponse.timeActive)
  27. XCTAssertEqual(.scheduledBasal, statusResponse.deliveryStatus)
  28. XCTAssertEqual(.aboveFiftyUnits, statusResponse.podProgressStatus)
  29. XCTAssertEqual(6.3, statusResponse.insulinDelivered, accuracy: 0.01)
  30. XCTAssertEqual(0, statusResponse.bolusNotDelivered)
  31. XCTAssertEqual(3, statusResponse.lastProgrammingMessageSeqNum)
  32. XCTAssert(statusResponse.alerts.isEmpty)
  33. XCTAssertEqual("1f00ee84300a1d18003f1800004297ff8128", msg.encoded().hexadecimalString)
  34. } catch (let error) {
  35. XCTFail("message decoding threw error: \(error)")
  36. }
  37. }
  38. func testParsingShortErosVersionResponse() {
  39. do {
  40. let config = try VersionResponse(encodedData: Data(hexadecimalString: "011502070002070002020000a64000097c279c1f08ced2")!)
  41. XCTAssertEqual(23, config.data.count)
  42. XCTAssertEqual("2.7.0", String(describing: config.firmwareVersion))
  43. XCTAssertEqual("2.7.0", String(describing: config.iFirmwareVersion))
  44. XCTAssertEqual(42560, config.lot)
  45. XCTAssertEqual(621607, config.tid)
  46. XCTAssertEqual(0x1f08ced2, config.address)
  47. XCTAssertEqual(erosProductId, config.productId)
  48. XCTAssertEqual(.reminderInitialized, config.podProgressStatus)
  49. XCTAssertEqual(2, config.gain)
  50. XCTAssertEqual(0x1c, config.rssi)
  51. XCTAssertNil(config.pulseSize)
  52. XCTAssertNil(config.secondsPerBolusPulse)
  53. XCTAssertNil(config.secondsPerPrimePulse)
  54. XCTAssertNil(config.primeUnits)
  55. XCTAssertNil(config.cannulaInsertionUnits)
  56. XCTAssertNil(config.serviceDuration)
  57. } catch (let error) {
  58. XCTFail("message decoding threw error: \(error)")
  59. }
  60. }
  61. func testParsingLongErosVersionResponse() {
  62. do {
  63. let message = try Message(encodedData: Data(hexadecimalString: "ffffffff041d011b13881008340a5002070002070002030000a62b000447941f00ee878352")!)
  64. let config = message.messageBlocks[0] as! VersionResponse
  65. XCTAssertEqual(29, config.data.count)
  66. XCTAssertEqual("2.7.0", String(describing: config.firmwareVersion))
  67. XCTAssertEqual("2.7.0", String(describing: config.iFirmwareVersion))
  68. XCTAssertEqual(42539, config.lot)
  69. XCTAssertEqual(280468, config.tid)
  70. XCTAssertEqual(0x1f00ee87, config.address)
  71. XCTAssertEqual(erosProductId, config.productId)
  72. XCTAssertEqual(.pairingCompleted, config.podProgressStatus)
  73. XCTAssertNil(config.rssi)
  74. XCTAssertNil(config.gain)
  75. XCTAssertEqual(Pod.pulseSize, config.pulseSize)
  76. XCTAssertEqual(Pod.secondsPerBolusPulse, config.secondsPerBolusPulse)
  77. XCTAssertEqual(Pod.secondsPerPrimePulse, config.secondsPerPrimePulse)
  78. XCTAssertEqual(Pod.primeUnits, config.primeUnits)
  79. XCTAssertEqual(Pod.cannulaInsertionUnits, config.cannulaInsertionUnits)
  80. XCTAssertEqual(Pod.serviceDuration, config.serviceDuration)
  81. } catch (let error) {
  82. XCTFail("message decoding threw error: \(error)")
  83. }
  84. }
  85. func testParsingShortDashVersionResponse() {
  86. do {
  87. let config = try VersionResponse(encodedData: Data(hexadecimalString: "0115031b0008080004020812a011000c175700ffffffff")!)
  88. XCTAssertEqual(23, config.data.count)
  89. XCTAssertEqual("3.27.0", String(describing: config.firmwareVersion))
  90. XCTAssertEqual("8.8.0", String(describing: config.iFirmwareVersion))
  91. XCTAssertEqual(135438353, config.lot)
  92. XCTAssertEqual(792407, config.tid)
  93. XCTAssertEqual(0xFFFFFFFF, config.address)
  94. XCTAssertEqual(dashProductId, config.productId)
  95. XCTAssertEqual(.reminderInitialized, config.podProgressStatus)
  96. XCTAssertEqual(0, config.gain)
  97. XCTAssertEqual(0, config.rssi)
  98. XCTAssertNil(config.pulseSize)
  99. XCTAssertNil(config.secondsPerBolusPulse)
  100. XCTAssertNil(config.secondsPerPrimePulse)
  101. XCTAssertNil(config.primeUnits)
  102. XCTAssertNil(config.cannulaInsertionUnits)
  103. XCTAssertNil(config.serviceDuration)
  104. } catch (let error) {
  105. XCTFail("message decoding threw error: \(error)")
  106. }
  107. }
  108. func testParsingLongDashVersionResponse() {
  109. do {
  110. let message = try Message(encodedData: Data(hexadecimalString: "ffffffff0c1d011b13881008340a50031b0008080004030812a011000c175717244389816c")!)
  111. let config = message.messageBlocks[0] as! VersionResponse
  112. XCTAssertEqual(29, config.data.count)
  113. XCTAssertEqual("3.27.0", String(describing: config.firmwareVersion))
  114. XCTAssertEqual("8.8.0", String(describing: config.iFirmwareVersion))
  115. XCTAssertEqual(135438353, config.lot)
  116. XCTAssertEqual(792407, config.tid)
  117. XCTAssertEqual(0x17244389, config.address)
  118. XCTAssertEqual(dashProductId, config.productId)
  119. XCTAssertEqual(.pairingCompleted, config.podProgressStatus)
  120. XCTAssertNil(config.rssi)
  121. XCTAssertNil(config.gain)
  122. XCTAssertEqual(Pod.pulseSize, config.pulseSize)
  123. XCTAssertEqual(Pod.secondsPerBolusPulse, config.secondsPerBolusPulse)
  124. XCTAssertEqual(Pod.secondsPerPrimePulse, config.secondsPerPrimePulse)
  125. XCTAssertEqual(Pod.primeUnits, config.primeUnits)
  126. XCTAssertEqual(Pod.cannulaInsertionUnits, config.cannulaInsertionUnits)
  127. XCTAssertEqual(Pod.serviceDuration, config.serviceDuration)
  128. } catch (let error) {
  129. XCTFail("message decoding threw error: \(error)")
  130. }
  131. }
  132. func testParsingConfigWithPairingExpired() {
  133. do {
  134. let message = try Message(encodedData: Data(hexadecimalString: "ffffffff04170115020700020700020e0000a5ad00053030971f08686301fd")!)
  135. let config = message.messageBlocks[0] as! VersionResponse
  136. XCTAssertEqual("2.7.0", String(describing: config.firmwareVersion))
  137. XCTAssertEqual("2.7.0", String(describing: config.iFirmwareVersion))
  138. XCTAssertEqual(0x0000a5ad, config.lot)
  139. XCTAssertEqual(0x00053030, config.tid)
  140. XCTAssertEqual(0x1f086863, config.address)
  141. XCTAssertEqual(erosProductId, config.productId)
  142. XCTAssertEqual(.activationTimeExceeded, config.podProgressStatus)
  143. XCTAssertEqual(2, config.gain)
  144. XCTAssertEqual(0x17, config.rssi)
  145. XCTAssertNil(config.pulseSize)
  146. XCTAssertNil(config.secondsPerBolusPulse)
  147. XCTAssertNil(config.secondsPerPrimePulse)
  148. XCTAssertNil(config.primeUnits)
  149. XCTAssertNil(config.cannulaInsertionUnits)
  150. XCTAssertNil(config.serviceDuration)
  151. } catch (let error) {
  152. XCTFail("message decoding threw error: \(error)")
  153. }
  154. }
  155. func testAssignAddressCommand() {
  156. do {
  157. // Encode
  158. let encoded = AssignAddressCommand(address: 0x1f01482a)
  159. XCTAssertEqual("07041f01482a", encoded.data.hexadecimalString)
  160. // Decode
  161. let decoded = try AssignAddressCommand(encodedData: Data(hexadecimalString: "07041f01482a")!)
  162. XCTAssertEqual(0x1f01482a, decoded.address)
  163. } catch (let error) {
  164. XCTFail("message decoding threw error: \(error)")
  165. }
  166. }
  167. func testSetupPodCommand() {
  168. do {
  169. var components = DateComponents()
  170. components.day = 12
  171. components.month = 6
  172. components.year = 2016
  173. components.hour = 13
  174. components.minute = 47
  175. // Decode
  176. let decoded = try SetupPodCommand(encodedData: Data(hexadecimalString: "03131f0218c31404060c100d2f0000a4be0004e4a1")!)
  177. XCTAssertEqual(0x1f0218c3, decoded.address)
  178. XCTAssertEqual(components, decoded.dateComponents)
  179. XCTAssertEqual(0x0000a4be, decoded.lot)
  180. XCTAssertEqual(0x0004e4a1, decoded.tid)
  181. // Encode
  182. let encoded = SetupPodCommand(address: 0x1f0218c3, dateComponents: components, lot: 0x0000a4be, tid: 0x0004e4a1)
  183. XCTAssertEqual("03131f0218c31404060c100d2f0000a4be0004e4a1", encoded.data.hexadecimalString)
  184. } catch (let error) {
  185. XCTFail("message decoding threw error: \(error)")
  186. }
  187. }
  188. func testPrime() {
  189. do {
  190. // 1a LL NNNNNNNN 02 CCCC HH SSSS PPPP 0ppp
  191. // 1a 0e bed2e16b 02 010a 01 01a0 0034 0034
  192. // Decode
  193. let cmd = try SetInsulinScheduleCommand(encodedData: Data(hexadecimalString: "1a0ebed2e16b02010a0101a000340034")!)
  194. XCTAssertEqual(0xbed2e16b, cmd.nonce)
  195. if case SetInsulinScheduleCommand.DeliverySchedule.bolus(let units, let timeBetweenPulses, let table) = cmd.deliverySchedule {
  196. XCTAssertEqual(Pod.primeUnits, units)
  197. XCTAssertEqual(Pod.secondsPerPrimePulse, timeBetweenPulses)
  198. XCTAssertEqual(1, table.entries.count)
  199. XCTAssertEqual(1, table.entries[0].segments)
  200. XCTAssertEqual(Int(Pod.primeUnits / Pod.pulseSize), table.entries[0].pulses)
  201. XCTAssertEqual(false, table.entries[0].alternateSegmentPulse)
  202. } else {
  203. XCTFail("Expected ScheduleEntry.bolus type")
  204. }
  205. } catch (let error) {
  206. XCTFail("message decoding threw error: \(error)")
  207. }
  208. }
  209. func testInsertCannula() {
  210. do {
  211. // 1a LL NNNNNNNN 02 CCCC HH SSSS PPPP 0ppp
  212. // 1a 0e 7e30bf16 02 0065 01 0050 000a 000a
  213. // Decode
  214. let cmd = try SetInsulinScheduleCommand(encodedData: Data(hexadecimalString: "1a0e7e30bf16020065010050000a000a")!)
  215. XCTAssertEqual(0x7e30bf16, cmd.nonce)
  216. if case SetInsulinScheduleCommand.DeliverySchedule.bolus(let units, let timeBetweenPulses, let table) = cmd.deliverySchedule {
  217. XCTAssertEqual(Pod.cannulaInsertionUnits, units)
  218. XCTAssertEqual(Pod.secondsPerPrimePulse, timeBetweenPulses)
  219. XCTAssertEqual(1, table.entries.count)
  220. XCTAssertEqual(1, table.entries[0].segments)
  221. XCTAssertEqual(Int(Pod.cannulaInsertionUnits / Pod.pulseSize), table.entries[0].pulses)
  222. XCTAssertEqual(false, table.entries[0].alternateSegmentPulse)
  223. } else {
  224. XCTFail("Expected ScheduleEntry.bolus type")
  225. }
  226. } catch (let error) {
  227. XCTFail("message decoding threw error: \(error)")
  228. }
  229. }
  230. func testStatusResponseAlarmsParsing() {
  231. // 1d 28 0082 00 0044 46eb ff
  232. do {
  233. // Decode
  234. let status = try StatusResponse(encodedData: Data(hexadecimalString: "1d28008200004446ebff")!)
  235. XCTAssert(status.alerts.contains(.slot3))
  236. XCTAssert(status.alerts.contains(.slot7))
  237. } catch (let error) {
  238. XCTFail("message decoding threw error: \(error)")
  239. }
  240. }
  241. func testConfigureAlertsCommand() {
  242. // 79a4 10df 0502
  243. // Pod expires 1 minute short of 3 days
  244. let podSoftExpirationTime = TimeInterval(hours:72) - TimeInterval(minutes:1)
  245. let alertConfig1 = AlertConfiguration(alertType: .slot7, active: true, autoOffModifier: false, duration: .hours(7), trigger: .timeUntilAlert(podSoftExpirationTime), beepRepeat: .every60Minutes, beepType: .bipBeepBipBeepBipBeepBipBeep)
  246. XCTAssertEqual("79a410df0502", alertConfig1.data.hexadecimalString)
  247. // 2800 1283 0602
  248. let podHardExpirationTime = TimeInterval(hours:79) - TimeInterval(minutes:1)
  249. let alertConfig2 = AlertConfiguration(alertType: .slot2, active: true, autoOffModifier: false, duration: .minutes(0), trigger: .timeUntilAlert(podHardExpirationTime), beepRepeat: .every15Minutes, beepType: .bipBeepBipBeepBipBeepBipBeep)
  250. XCTAssertEqual("280012830602", alertConfig2.data.hexadecimalString)
  251. // 020f 0000 0202
  252. let alertConfig3 = AlertConfiguration(alertType: .slot0, active: false, autoOffModifier: true, duration: .minutes(15), trigger: .timeUntilAlert(0), beepRepeat: .every1MinuteFor15Minutes, beepType: .bipBeepBipBeepBipBeepBipBeep)
  253. XCTAssertEqual("020f00000202", alertConfig3.data.hexadecimalString)
  254. let configureAlerts = ConfigureAlertsCommand(nonce: 0xfeb6268b, configurations:[alertConfig1, alertConfig2, alertConfig3])
  255. XCTAssertEqual("1916feb6268b79a410df0502280012830602020f00000202", configureAlerts.data.hexadecimalString)
  256. do {
  257. let decoded = try ConfigureAlertsCommand(encodedData: Data(hexadecimalString: "1916feb6268b79a410df0502280012830602020f00000202")!)
  258. XCTAssertEqual(3, decoded.configurations.count)
  259. let config1 = decoded.configurations[0]
  260. XCTAssertEqual(.slot7, config1.slot)
  261. XCTAssertEqual(true, config1.active)
  262. XCTAssertEqual(false, config1.autoOffModifier)
  263. XCTAssertEqual(.hours(7), config1.duration)
  264. if case AlertTrigger.timeUntilAlert(let duration) = config1.trigger {
  265. XCTAssertEqual(podSoftExpirationTime, duration)
  266. }
  267. XCTAssertEqual(.every60Minutes, config1.beepRepeat)
  268. XCTAssertEqual(.bipBeepBipBeepBipBeepBipBeep, config1.beepType)
  269. let cfg = try AlertConfiguration(encodedData: Data(hexadecimalString: "4c0000640102")!)
  270. XCTAssertEqual(.slot4, cfg.slot)
  271. XCTAssertEqual(true, cfg.active)
  272. XCTAssertEqual(false, cfg.autoOffModifier)
  273. XCTAssertEqual(0, cfg.duration)
  274. if case AlertTrigger.unitsRemaining(let volume) = cfg.trigger {
  275. XCTAssertEqual(10, volume)
  276. }
  277. XCTAssertEqual(.every1MinuteFor3MinutesAndRepeatEvery60Minutes, cfg.beepRepeat)
  278. XCTAssertEqual(.bipBeepBipBeepBipBeepBipBeep, cfg.beepType)
  279. } catch (let error) {
  280. XCTFail("message decoding threw error: \(error)")
  281. }
  282. }
  283. }