BasalScheduleTests.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. //
  2. // BasalScheduleTests.swift
  3. // OmniKitTests
  4. //
  5. // Created by Pete Schwamb on 4/4/18.
  6. // Copyright © 2018 Pete Schwamb. All rights reserved.
  7. //
  8. import XCTest
  9. @testable import OmniKit
  10. class BasalScheduleTests: XCTestCase {
  11. func testBasalTableEntry() {
  12. let entry = BasalTableEntry(segments: 2, pulses: 300, alternateSegmentPulse: false)
  13. // $01 $2c $01 $2c = 1 + 44 + 1 + 44 = 90 = $5a
  14. XCTAssertEqual(0x5a, entry.checksum())
  15. let entry2 = BasalTableEntry(segments: 2, pulses: 260, alternateSegmentPulse: true)
  16. // $01 $04 $01 $04 = 1 + 4 + 1 + 5 = 1 = $0b
  17. XCTAssertEqual(0x0b, entry2.checksum())
  18. }
  19. func testSetBasalScheduleCommand() {
  20. do {
  21. // Decode 1a 12 77a05551 00 0062 2b 1708 0000 f800 f800 f800
  22. let cmd = try SetInsulinScheduleCommand(encodedData: Data(hexadecimalString: "1a1277a055510000622b17080000f800f800f800")!)
  23. XCTAssertEqual(0x77a05551, cmd.nonce)
  24. if case SetInsulinScheduleCommand.DeliverySchedule.basalSchedule(let currentSegment, let secondsRemaining, let pulsesRemaining, let table) = cmd.deliverySchedule {
  25. XCTAssertEqual(0x2b, currentSegment)
  26. XCTAssertEqual(737, secondsRemaining)
  27. XCTAssertEqual(0, pulsesRemaining)
  28. XCTAssertEqual(3, table.entries.count)
  29. } else {
  30. XCTFail("Expected ScheduleEntry.basalSchedule type")
  31. }
  32. } catch (let error) {
  33. XCTFail("message decoding threw error: \(error)")
  34. }
  35. // Encode
  36. let scheduleEntry = BasalTableEntry(segments: 16, pulses: 0, alternateSegmentPulse: true)
  37. let table = BasalDeliveryTable(entries: [scheduleEntry, scheduleEntry, scheduleEntry])
  38. let deliverySchedule = SetInsulinScheduleCommand.DeliverySchedule.basalSchedule(currentSegment: 0x2b, secondsRemaining: 737, pulsesRemaining: 0, table: table)
  39. let cmd = SetInsulinScheduleCommand(nonce: 0x77a05551, deliverySchedule: deliverySchedule)
  40. XCTAssertEqual("1a1277a055510000622b17080000f800f800f800", cmd.data.hexadecimalString)
  41. }
  42. func testBasalScheduleCommandFromSchedule() {
  43. // Encode from schedule
  44. let entry = BasalScheduleEntry(rate: 0.05, startTime: 0)
  45. let schedule = BasalSchedule(entries: [entry])
  46. let cmd = SetInsulinScheduleCommand(nonce: 0x01020304, basalSchedule: schedule, scheduleOffset: .hours(8.25))
  47. XCTAssertEqual(0x01020304, cmd.nonce)
  48. if case SetInsulinScheduleCommand.DeliverySchedule.basalSchedule(let currentSegment, let secondsRemaining, let pulsesRemaining, let table) = cmd.deliverySchedule {
  49. XCTAssertEqual(16, currentSegment)
  50. XCTAssertEqual(UInt16(TimeInterval(minutes: 15)), secondsRemaining)
  51. XCTAssertEqual(0, pulsesRemaining)
  52. XCTAssertEqual(3, table.entries.count)
  53. let tableEntry = table.entries[0]
  54. XCTAssertEqual(true, tableEntry.alternateSegmentPulse)
  55. XCTAssertEqual(0, tableEntry.pulses)
  56. XCTAssertEqual(16, tableEntry.segments)
  57. } else {
  58. XCTFail("Expected ScheduleEntry.basalSchedule type")
  59. }
  60. // 1a LL NNNNNNNN 00 CCCC HH SSSS PPPP napp napp napp napp
  61. // 1a 12 01020304 00 0065 10 1c20 0001 f800 f800 f800
  62. XCTAssertEqual("1a1201020304000064101c200000f800f800f800", cmd.data.hexadecimalString)
  63. }
  64. func testBasalScheduleExtraCommand() {
  65. do {
  66. // Decode 130e40 00 1aea 001e8480 3840005b8d80
  67. let cmd = try BasalScheduleExtraCommand(encodedData: Data(hexadecimalString: "130e40001aea001e84803840005b8d80")!)
  68. XCTAssertEqual(false, cmd.acknowledgementBeep)
  69. XCTAssertEqual(true, cmd.completionBeep)
  70. XCTAssertEqual(0, cmd.programReminderInterval)
  71. XCTAssertEqual(0, cmd.currentEntryIndex)
  72. XCTAssertEqual(689, cmd.remainingPulses)
  73. XCTAssertEqual(TimeInterval(seconds: 20), cmd.delayUntilNextTenthOfPulse)
  74. XCTAssertEqual(1, cmd.rateEntries.count)
  75. let entry = cmd.rateEntries[0]
  76. XCTAssertEqual(TimeInterval(seconds: 60), entry.delayBetweenPulses)
  77. XCTAssertEqual(1440, entry.totalPulses)
  78. XCTAssertEqual(3.0, entry.rate)
  79. XCTAssertEqual(TimeInterval(hours: 24), entry.duration)
  80. } catch (let error) {
  81. XCTFail("message decoding threw error: \(error)")
  82. }
  83. // Encode
  84. let rateEntries = RateEntry.makeEntries(rate: 3.0, duration: TimeInterval(hours: 24))
  85. let cmd = BasalScheduleExtraCommand(currentEntryIndex: 0, remainingPulses: 689, delayUntilNextTenthOfPulse: TimeInterval(seconds: 20), rateEntries: rateEntries, acknowledgementBeep: false, completionBeep: true, programReminderInterval: 0)
  86. XCTAssertEqual("130e40001aea01312d003840005b8d80", cmd.data.hexadecimalString)
  87. }
  88. func testBasalScheduleExtraCommandFromSchedule() {
  89. // Encode from schedule
  90. let entry = BasalScheduleEntry(rate: 0.05, startTime: 0)
  91. let schedule = BasalSchedule(entries: [entry])
  92. let cmd = BasalScheduleExtraCommand(schedule: schedule, scheduleOffset: .hours(8.25), acknowledgementBeep: false, completionBeep: true, programReminderInterval: 60)
  93. XCTAssertEqual(false, cmd.acknowledgementBeep)
  94. XCTAssertEqual(true, cmd.completionBeep)
  95. XCTAssertEqual(60, cmd.programReminderInterval)
  96. XCTAssertEqual(0, cmd.currentEntryIndex)
  97. XCTAssertEqual(15.8, cmd.remainingPulses, accuracy: 0.01)
  98. XCTAssertEqual(TimeInterval(minutes: 3), cmd.delayUntilNextTenthOfPulse)
  99. XCTAssertEqual(1, cmd.rateEntries.count)
  100. let rateEntry = cmd.rateEntries[0]
  101. XCTAssertEqual(TimeInterval(minutes: 60), rateEntry.delayBetweenPulses)
  102. XCTAssertEqual(24, rateEntry.totalPulses, accuracy: 0.001)
  103. XCTAssertEqual(0.05, rateEntry.rate)
  104. XCTAssertEqual(TimeInterval(hours: 24), rateEntry.duration, accuracy: 0.001)
  105. }
  106. func testBasalExtraEncoding() {
  107. // Encode
  108. let schedule = BasalSchedule(entries: [
  109. BasalScheduleEntry(rate: 1.05, startTime: 0),
  110. BasalScheduleEntry(rate: 0.9, startTime: .hours(10.5)),
  111. BasalScheduleEntry(rate: 1, startTime: .hours(18.5))
  112. ])
  113. let hh = 0x2e
  114. let ssss = 0x1be8
  115. let offset = TimeInterval(minutes: Double((hh + 1) * 30)) - TimeInterval(seconds: Double(ssss / 8))
  116. // 1a LL NNNNNNNN 00 CCCC HH SSSS PPPP napp napp napp napp
  117. // 1a 14 0d6612db 00 0310 2e 1be8 0005 f80a 480a f009 a00a
  118. let cmd1 = SetInsulinScheduleCommand(nonce: 0x0d6612db, basalSchedule: schedule, scheduleOffset: offset)
  119. XCTAssertEqual("1a140d6612db0003102e1be80005f80a480af009a00a", cmd1.data.hexadecimalString)
  120. // 13 LL RR MM NNNN XXXXXXXX YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ
  121. // 13 1a 40 02 0096 00a7d8c0 089d 01059449 05a0 01312d00 044c 0112a880 * PDM
  122. // 13 1a 40 02 0095 00a7d8c0 089d 01059449 05a0 01312d00 044c 0112a880
  123. let cmd2 = BasalScheduleExtraCommand(schedule: schedule, scheduleOffset: offset, acknowledgementBeep: false, completionBeep: true, programReminderInterval: 0)
  124. XCTAssertEqual("131a4002009600a7d8c0089d0105944905a001312d00044c0112a880", cmd2.data.hexadecimalString) // PDM
  125. }
  126. func checkBasalScheduleExtraCommandDataWithLessPrecision(_ data: Data, _ expected: Data, line: UInt = #line) {
  127. // The XXXXXXXX field is in thousands of a millisecond. Since we use TimeIntervals (floating point) for
  128. // recreating the offset, we can have small errors in reproducing the the encoded output, which we really
  129. // don't care about.
  130. func extractXXXXXXXX(_ data: Data) -> TimeInterval {
  131. return TimeInterval(Double(data[6...].toBigEndian(UInt32.self)) / 1000000.0)
  132. }
  133. let xxxxxxxx1 = extractXXXXXXXX(data)
  134. let xxxxxxxx2 = extractXXXXXXXX(expected)
  135. XCTAssertEqual(xxxxxxxx1, xxxxxxxx2, accuracy: 0.01, line: line)
  136. func blurXXXXXXXX(_ inStr: String) -> String {
  137. let start = inStr.index(inStr.startIndex, offsetBy:12)
  138. let end = inStr.index(start, offsetBy:8)
  139. return inStr.replacingCharacters(in: start..<end, with: "........")
  140. }
  141. print(blurXXXXXXXX(data.hexadecimalString))
  142. XCTAssertEqual(blurXXXXXXXX(data.hexadecimalString), blurXXXXXXXX(expected.hexadecimalString), line: line)
  143. }
  144. func testBasalExtraEncoding1() {
  145. // Encode
  146. let schedule = BasalSchedule(entries: [BasalScheduleEntry(rate: 1.05, startTime: 0)])
  147. let hh = 0x20 // 16:00, rate = 1.05
  148. let ssss = 0x33c0 // 1656s left, 144s into segment
  149. let offset = TimeInterval(minutes: Double((hh + 1) * 30)) - TimeInterval(seconds: Double(ssss / 8))
  150. // 1a LL NNNNNNNN 00 CCCC HH SSSS PPPP napp napp napp
  151. // 1a 12 2a845e17 00 0314 20 33c0 0009 f80a f80a f80a
  152. // 1a 12 2a845e17 00 0315 20 33c0 000a f80a f80a f80a
  153. let cmd1 = SetInsulinScheduleCommand(nonce: 0x2a845e17, basalSchedule: schedule, scheduleOffset: offset)
  154. XCTAssertEqual("1a122a845e170003142033c00009f80af80af80a", cmd1.data.hexadecimalString)
  155. // 13 LL RR MM NNNN XXXXXXXX YYYY ZZZZZZZZ
  156. // 13 0e 40 00 0688 009cf291 13b0 01059449
  157. let cmd2 = BasalScheduleExtraCommand(schedule: schedule, scheduleOffset: offset, acknowledgementBeep: false, completionBeep: true, programReminderInterval: 0)
  158. checkBasalScheduleExtraCommandDataWithLessPrecision(Data(hexadecimalString: "130e40000688009cf29113b001059449")!, cmd2.data)
  159. }
  160. func testBasalExtraEncoding2() {
  161. // Encode
  162. let schedule = BasalSchedule(entries: [BasalScheduleEntry(rate: 1.05, startTime: 0)])
  163. // 17:47:27 1a 12 0a229e93 00 02d6 23 17a0 0004 f80a f80a f80a 13 0e 40 00 0519 001a2865 13b0 01059449 0220
  164. let hh = 0x23 // 17:30, rate = 1.05
  165. let ssss = 0x17a0 // 756s left, 1044s into segment
  166. let offset = TimeInterval(minutes: Double((hh + 1) * 30)) - TimeInterval(seconds: Double(ssss / 8))
  167. // 1a LL NNNNNNNN 00 CCCC HH SSSS PPPP napp napp napp
  168. // 1a 12 0a229e93 00 02d6 23 17a0 0004 f80a f80a f80a
  169. let cmd1 = SetInsulinScheduleCommand(nonce: 0x0a229e93, basalSchedule: schedule, scheduleOffset: offset)
  170. XCTAssertEqual("1a120a229e930002d62317a00004f80af80af80a", cmd1.data.hexadecimalString)
  171. // 13 LL RR MM NNNN XXXXXXXX YYYY ZZZZZZZZ
  172. // 13 0e 40 00 0519 001a2865 13b0 01059449
  173. // 13 0e 40 00 0519 001a286e 13b0 01059449
  174. let cmd2 = BasalScheduleExtraCommand(schedule: schedule, scheduleOffset: offset, acknowledgementBeep: false, completionBeep: true, programReminderInterval: 0)
  175. checkBasalScheduleExtraCommandDataWithLessPrecision(Data(hexadecimalString: "130e40000519001a286513b001059449")!, cmd2.data)
  176. }
  177. func testSuspendBasalCommand() {
  178. do {
  179. // Decode 1f 05 6fede14a 01
  180. let cmd = try CancelDeliveryCommand(encodedData: Data(hexadecimalString: "1f056fede14a01")!)
  181. XCTAssertEqual(0x6fede14a, cmd.nonce)
  182. XCTAssertEqual(.noBeep, cmd.beepType)
  183. XCTAssertEqual(.basal, cmd.deliveryType)
  184. } catch (let error) {
  185. XCTFail("message decoding threw error: \(error)")
  186. }
  187. // Encode
  188. let cmd = CancelDeliveryCommand(nonce: 0x6fede14a, deliveryType: .basal, beepType: .noBeep)
  189. XCTAssertEqual("1f056fede14a01", cmd.data.hexadecimalString)
  190. }
  191. func testSegmentMerging() {
  192. let entries = [
  193. BasalScheduleEntry(rate: 0.80, startTime: 0),
  194. BasalScheduleEntry(rate: 0.90, startTime: .hours(3)),
  195. BasalScheduleEntry(rate: 0.85, startTime: .hours(5)),
  196. BasalScheduleEntry(rate: 0.85, startTime: .hours(7.5)),
  197. BasalScheduleEntry(rate: 0.85, startTime: .hours(12.5)),
  198. BasalScheduleEntry(rate: 0.70, startTime: .hours(15)),
  199. BasalScheduleEntry(rate: 0.90, startTime: .hours(18)),
  200. BasalScheduleEntry(rate: 1.10, startTime: .hours(20)),
  201. ]
  202. let schedule = BasalSchedule(entries: entries)
  203. // 1a LL NNNNNNNN 00 CCCC HH SSSS PPPP napp napp napp napp napp napp napp
  204. // PDM: 1a 1a 851072aa 00 0242 2a 1e50 0006 5008 3009 f808 3808 5007 3009 700b
  205. let hh = 0x2a
  206. let ssss = 0x1e50
  207. let offset = TimeInterval(minutes: Double((hh + 1) * 30)) - TimeInterval(seconds: Double(ssss / 8))
  208. let cmd1 = SetInsulinScheduleCommand(nonce: 0x851072aa, basalSchedule: schedule, scheduleOffset: offset)
  209. XCTAssertEqual("1a1a851072aa0002422a1e50000650083009f808380850073009700b", cmd1.data.hexadecimalString)
  210. // 13 LL RR MM NNNN XXXXXXXX YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ
  211. // PDM: 13 2c 40 05 0262 00455b9c 01e0 015752a0 0168 01312d00 06a4 01432096 01a4 01885e6d 0168 01312d00 0370 00f9b074
  212. let cmd2 = BasalScheduleExtraCommand(schedule: schedule, scheduleOffset: offset, acknowledgementBeep: false, completionBeep: true, programReminderInterval: 0)
  213. checkBasalScheduleExtraCommandDataWithLessPrecision(Data(hexadecimalString: "132c4005026200455b9c01e0015752a0016801312d0006a40143209601a401885e6d016801312d00037000f9b074")!, cmd2.data)
  214. }
  215. func testRounding() {
  216. let entries = [
  217. BasalScheduleEntry(rate: 2.75, startTime: 0),
  218. BasalScheduleEntry(rate: 20.25, startTime: .hours(1)),
  219. BasalScheduleEntry(rate: 5.00, startTime: .hours(1.5)),
  220. BasalScheduleEntry(rate: 10.10, startTime: .hours(2)),
  221. BasalScheduleEntry(rate: 0.05, startTime: .hours(2.5)),
  222. BasalScheduleEntry(rate: 3.50, startTime: .hours(15.5)),
  223. ]
  224. let schedule = BasalSchedule(entries: entries)
  225. // 1a LL NNNNNNNN 00 CCCC HH SSSS PPPP napp napp napp napp napp napp napp napp napp
  226. // PDM: 1a 1e c2a32da8 00 053a 28 1af0 0010 181b 00ca 0032 0065 0001 f800 8800 f023 0023
  227. let hh = 0x28
  228. let ssss = 0x1af0
  229. let offset = TimeInterval(minutes: Double((hh + 1) * 30)) - TimeInterval(seconds: Double(ssss / 8))
  230. let cmd1 = SetInsulinScheduleCommand(nonce: 0xc2a32da8, basalSchedule: schedule, scheduleOffset: offset)
  231. XCTAssertEqual("1a1ec2a32da800053a281af00010181b00ca003200650001f8008800f0230023", cmd1.data.hexadecimalString)
  232. }
  233. func testRounding2() {
  234. let entries = [
  235. BasalScheduleEntry(rate: 0.60, startTime: 0),
  236. BasalScheduleEntry(rate: 0.65, startTime: .hours(7.5)),
  237. BasalScheduleEntry(rate: 0.50, startTime: .hours(8.5)),
  238. BasalScheduleEntry(rate: 0.65, startTime: .hours(9.5)),
  239. BasalScheduleEntry(rate: 0.15, startTime: .hours(15.5)),
  240. BasalScheduleEntry(rate: 0.80, startTime: .hours(16.3)),
  241. ]
  242. let schedule = BasalSchedule(entries: entries)
  243. // 1a LL NNNNNNNN 00 CCCC HH SSSS PPPP napp napp napp napp napp napp
  244. // PDM: 1a 18 851072aa 00 021b 2c 2190 0004 f006 0007 1005 b806 1801 e008
  245. let hh = 0x2c
  246. let ssss = 0x2190
  247. let offset = TimeInterval(minutes: Double((hh + 1) * 30)) - TimeInterval(seconds: Double(ssss / 8))
  248. let cmd1 = SetInsulinScheduleCommand(nonce: 0x851072aa, basalSchedule: schedule, scheduleOffset: offset)
  249. XCTAssertEqual("1a18851072aa00021b2c21900004f00600071005b8061801e008", cmd1.data.hexadecimalString)
  250. }
  251. func testThirteenEntries() {
  252. let entries = [
  253. BasalScheduleEntry(rate: 1.30, startTime: 0),
  254. BasalScheduleEntry(rate: 0.05, startTime: .hours(0.5)),
  255. BasalScheduleEntry(rate: 1.70, startTime: .hours(2.0)),
  256. BasalScheduleEntry(rate: 0.85, startTime: .hours(2.5)),
  257. BasalScheduleEntry(rate: 1.00, startTime: .hours(3.0)),
  258. BasalScheduleEntry(rate: 0.65, startTime: .hours(7.5)),
  259. BasalScheduleEntry(rate: 0.50, startTime: .hours(8.5)),
  260. BasalScheduleEntry(rate: 0.65, startTime: .hours(9.5)),
  261. BasalScheduleEntry(rate: 0.60, startTime: .hours(10.5)),
  262. BasalScheduleEntry(rate: 0.65, startTime: .hours(11.5)),
  263. BasalScheduleEntry(rate: 1.65, startTime: .hours(14.0)),
  264. BasalScheduleEntry(rate: 0.15, startTime: .hours(15.5)),
  265. BasalScheduleEntry(rate: 0.85, startTime: .hours(16.5)),
  266. ]
  267. let schedule = BasalSchedule(entries: entries)
  268. // 1a LL NNNNNNNN 00 CCCC HH SSSS PPPP napp napp napp napp napp napp napp napp napp napp napp napp napp napp napp
  269. // PDM: 1a 2a 851072aa 00 01dd 27 1518 0003 000d 2800 0011 1809 700a 1806 1005 2806 1006 0007 2806 0011 1810 1801 e808
  270. let hh = 0x27
  271. let ssss = 0x1518
  272. let offset = TimeInterval(minutes: Double((hh + 1) * 30)) - TimeInterval(seconds: Double(ssss / 8))
  273. let cmd = SetInsulinScheduleCommand(nonce: 0x851072aa, basalSchedule: schedule, scheduleOffset: offset)
  274. XCTAssertEqual("1a2a851072aa0001dd2715180003000d280000111809700a180610052806100600072806001118101801e808", cmd.data.hexadecimalString)
  275. // 13 LL RR MM NNNN XXXXXXXX YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ YYYY ZZZZZZZZ
  276. // PDM: 13 56 40 0c 02c8 011abc64 0082 00d34689 000f 15752a00 00aa 00a1904b 0055 01432096 0384 0112a880 0082 01a68d13 0064 02255100 0082 01a68d13 0078 01c9c380 0145 01a68d13 01ef 00a675a2 001e 07270e00 04fb 01432096
  277. let cmd2 = BasalScheduleExtraCommand(schedule: schedule, scheduleOffset: offset, acknowledgementBeep: false, completionBeep: true, programReminderInterval: 0)
  278. checkBasalScheduleExtraCommandDataWithLessPrecision(Data(hexadecimalString: "1356400c02c8011abc64008200d34689000f15752a0000aa00a1904b00550143209603840112a880008201a68d13006402255100008201a68d13007801c9c380014501a68d1301ef00a675a2001e07270e0004fb01432096")!, cmd2.data)
  279. }
  280. func testJoe12Entries() {
  281. let entries = [
  282. BasalScheduleEntry(rate: 1.30, startTime: 0),
  283. BasalScheduleEntry(rate: 0.05, startTime: .hours(0.5)),
  284. BasalScheduleEntry(rate: 1.70, startTime: .hours(2.0)),
  285. BasalScheduleEntry(rate: 0.85, startTime: .hours(2.5)),
  286. BasalScheduleEntry(rate: 1.00, startTime: .hours(3.0)),
  287. BasalScheduleEntry(rate: 0.65, startTime: .hours(7.5)),
  288. BasalScheduleEntry(rate: 0.50, startTime: .hours(8.5)),
  289. BasalScheduleEntry(rate: 0.65, startTime: .hours(9.5)),
  290. BasalScheduleEntry(rate: 0.60, startTime: .hours(10.5)),
  291. BasalScheduleEntry(rate: 0.65, startTime: .hours(11.5)),
  292. BasalScheduleEntry(rate: 1.65, startTime: .hours(14.0)),
  293. BasalScheduleEntry(rate: 0.85, startTime: .hours(16)),
  294. ]
  295. let schedule = BasalSchedule(entries: entries)
  296. // 1a LL NNNNNNNN 00 CCCC HH SSSS PPPP napp napp napp napp napp napp napp napp napp napp napp napp napp napp napp
  297. // PDM: 1a 2a f36a23a3 00 0291 03 0ae8 0000 000d 2800 0011 1809 700a 1806 1005 2806 1006 0007 2806 0011 2810 0009 e808
  298. let hh = 0x03
  299. let ssss = 0x0ae8
  300. let offset = TimeInterval(minutes: Double((hh + 1) * 30)) - TimeInterval(seconds: Double(ssss / 8))
  301. let cmd1 = SetInsulinScheduleCommand(nonce: 0xf36a23a3, basalSchedule: schedule, scheduleOffset: offset)
  302. XCTAssertEqual("1a2af36a23a3000291030ae80000000d280000111809700a180610052806100600072806001128100009e808", cmd1.data.hexadecimalString)
  303. }
  304. func testBasalScheduleExtraCommandRoundsToNearestSecond() {
  305. let schedule = BasalSchedule(entries: [BasalScheduleEntry(rate: 1.0, startTime: 0)])
  306. let hh = 0x2b
  307. let ssss = 0x1b38
  308. // Add 0.456 to the clock to have a non-integer # of seconds, and verify that it still produces valid results
  309. let offset = TimeInterval(minutes: Double((hh + 1) * 30)) - TimeInterval(seconds: Double(ssss / 8)) + .seconds(0.456)
  310. // 13 LL RR MM NNNN XXXXXXXX YYYY ZZZZZZZZ
  311. // 13 0e 40 00 01c1 006acfc0 12c0 0112a880
  312. let cmd = BasalScheduleExtraCommand(schedule: schedule, scheduleOffset: offset, acknowledgementBeep: false, completionBeep: true, programReminderInterval: 0)
  313. checkBasalScheduleExtraCommandDataWithLessPrecision(Data(hexadecimalString: "130e400001c1006acfc012c00112a880")!, cmd.data)
  314. }
  315. }