MinimedPumpMessageSender.swift 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. //
  2. // MinimedPumpMessageSender.swift
  3. // MinimedKit
  4. //
  5. // Created by Pete Schwamb on 9/3/22.
  6. // Copyright © 2022 Pete Schwamb. All rights reserved.
  7. //
  8. import Foundation
  9. import RileyLinkBLEKit
  10. import os.log
  11. public protocol CommsLogger: AnyObject {
  12. // Comms logging
  13. func willSend(_ message: String)
  14. func didReceive(_ message: String)
  15. func didError(_ message: String)
  16. }
  17. private let log = OSLog(category: "MinimedPumpMessageSender")
  18. struct MinimedPumpMessageSender: PumpMessageSender {
  19. static let standardPumpResponseWindow: TimeInterval = .milliseconds(200)
  20. var commandSession: CommandSession
  21. weak var commsLogger: CommsLogger?
  22. func resetRadioConfig() throws {
  23. try commandSession.resetRadioConfig()
  24. }
  25. func updateRegister(_ address: RileyLinkBLEKit.CC111XRegister, value: UInt8) throws {
  26. try commandSession.updateRegister(address, value: value)
  27. }
  28. func setBaseFrequency(_ frequency: Measurement<UnitFrequency>) throws {
  29. try commandSession.setBaseFrequency(frequency)
  30. }
  31. func listen(onChannel channel: Int, timeout: TimeInterval) throws -> RileyLinkBLEKit.RFPacket? {
  32. return try commandSession.listen(onChannel: channel, timeout: timeout)
  33. }
  34. func getRileyLinkStatistics() throws -> RileyLinkBLEKit.RileyLinkStatistics {
  35. return try commandSession.getRileyLinkStatistics()
  36. }
  37. /// - Throws: PumpOpsError.deviceError
  38. func send(_ msg: PumpMessage) throws {
  39. do {
  40. try commandSession.send(MinimedPacket(outgoingData: msg.txData).encodedData(), onChannel: 0, timeout: 0)
  41. } catch let error as LocalizedError {
  42. throw PumpOpsError.deviceError(error)
  43. }
  44. }
  45. /// Sends a message to the pump, expecting a PumpMessage with specific response body type
  46. ///
  47. /// - Parameters:
  48. /// - message: The message to send
  49. /// - responseType: The expected response message type
  50. /// - repeatCount: The number of times to repeat the message before listening begins
  51. /// - timeout: The length of time to listen for a pump response
  52. /// - retryCount: The number of times to repeat the send & listen sequence
  53. /// - Returns: The expected response message body
  54. /// - Throws:
  55. /// - PumpOpsError.couldNotDecode
  56. /// - PumpOpsError.crosstalk
  57. /// - PumpOpsError.deviceError
  58. /// - PumpOpsError.noResponse
  59. /// - PumpOpsError.pumpError
  60. /// - PumpOpsError.unexpectedResponse
  61. /// - PumpOpsError.unknownResponse
  62. func getResponse<T: MessageBody>(to message: PumpMessage, responseType: MessageType, repeatCount: Int, timeout: TimeInterval, retryCount: Int) throws -> T {
  63. commsLogger?.willSend(String(describing: message))
  64. do {
  65. let response = try sendAndListen(message, repeatCount: repeatCount, timeout: timeout, retryCount: retryCount)
  66. guard response.messageType == responseType, let body = response.messageBody as? T else {
  67. if let body = response.messageBody as? PumpErrorMessageBody {
  68. commsLogger?.didReceive(String(describing: response))
  69. switch body.errorCode {
  70. case .known(let code):
  71. throw PumpOpsError.pumpError(code)
  72. case .unknown(let code):
  73. throw PumpOpsError.unknownPumpErrorCode(code)
  74. }
  75. } else {
  76. throw PumpOpsError.unexpectedResponse(response, from: message)
  77. }
  78. }
  79. commsLogger?.didReceive(String(describing: response))
  80. usleep(200000) // 0.2s
  81. return body
  82. } catch {
  83. commsLogger?.didError(error.localizedDescription)
  84. throw error
  85. }
  86. }
  87. /// Sends a message to the pump, listening for a any known PumpMessage in reply
  88. ///
  89. /// - Parameters:
  90. /// - message: The message to send
  91. /// - repeatCount: The number of times to repeat the message before listening begins
  92. /// - timeout: The length of time to listen for a pump response
  93. /// - retryCount: The number of times to repeat the send & listen sequence
  94. /// - Returns: The message reply
  95. /// - Throws: An error describing a failure in the sending or receiving of a message:
  96. /// - PumpOpsError.couldNotDecode
  97. /// - PumpOpsError.crosstalk
  98. /// - PumpOpsError.deviceError
  99. /// - PumpOpsError.noResponse
  100. /// - PumpOpsError.unknownResponse
  101. func sendAndListen(_ message: PumpMessage, repeatCount: Int, timeout: TimeInterval, retryCount: Int) throws -> PumpMessage {
  102. let rfPacket = try sendAndListenForPacket(message, repeatCount: repeatCount, timeout: timeout, retryCount: retryCount)
  103. guard let packet = MinimedPacket(encodedData: rfPacket.data) else {
  104. throw PumpOpsError.couldNotDecode(rx: rfPacket.data, during: message)
  105. }
  106. guard let response = PumpMessage(rxData: packet.data) else {
  107. // Unknown packet type or message type
  108. throw PumpOpsError.unknownResponse(rx: packet.data, during: message)
  109. }
  110. guard response.address == message.address else {
  111. throw PumpOpsError.crosstalk(response, during: message)
  112. }
  113. return response
  114. }
  115. // Send a PumpMessage, and listens for a packet; used by callers who need to see RSSI
  116. /// - Throws:
  117. /// - PumpOpsError.noResponse
  118. /// - PumpOpsError.deviceError
  119. func sendAndListenForPacket(_ message: PumpMessage, repeatCount: Int, timeout: TimeInterval, retryCount: Int) throws -> RFPacket {
  120. let packet: RFPacket?
  121. do {
  122. packet = try commandSession.sendAndListen(MinimedPacket(outgoingData: message.txData).encodedData(), repeatCount: repeatCount, timeout: timeout, retryCount: retryCount)
  123. } catch let error as LocalizedError {
  124. throw PumpOpsError.deviceError(error)
  125. }
  126. guard let rfPacket = packet else {
  127. throw PumpOpsError.noResponse(during: message)
  128. }
  129. return rfPacket
  130. }
  131. /// - Throws: PumpOpsError.deviceError
  132. func listenForPacket(onChannel channel: Int, timeout: TimeInterval) throws -> RFPacket? {
  133. do {
  134. return try listen(onChannel: channel, timeout: timeout)
  135. } catch let error as LocalizedError {
  136. throw PumpOpsError.deviceError(error)
  137. }
  138. }
  139. }