MessageTransport.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. //
  2. // MessageTransport.swift
  3. // OmniKit
  4. //
  5. // Created by Pete Schwamb on 8/5/18.
  6. // Copyright © 2018 Pete Schwamb. All rights reserved.
  7. //
  8. import Foundation
  9. import os.log
  10. import RileyLinkBLEKit
  11. protocol MessageLogger: AnyObject {
  12. // Comms logging
  13. func didSend(_ message: Data)
  14. func didReceive(_ message: Data)
  15. }
  16. public struct MessageTransportState: Equatable, RawRepresentable {
  17. public typealias RawValue = [String: Any]
  18. public var packetNumber: Int
  19. public var messageNumber: Int
  20. init(packetNumber: Int, messageNumber: Int) {
  21. self.packetNumber = packetNumber
  22. self.messageNumber = messageNumber
  23. }
  24. // RawRepresentable
  25. public init?(rawValue: RawValue) {
  26. guard
  27. let packetNumber = rawValue["packetNumber"] as? Int,
  28. let messageNumber = rawValue["messageNumber"] as? Int
  29. else {
  30. return nil
  31. }
  32. self.packetNumber = packetNumber
  33. self.messageNumber = messageNumber
  34. }
  35. public var rawValue: RawValue {
  36. return [
  37. "packetNumber": packetNumber,
  38. "messageNumber": messageNumber
  39. ]
  40. }
  41. }
  42. protocol MessageTransportDelegate: AnyObject {
  43. func messageTransport(_ messageTransport: MessageTransport, didUpdate state: MessageTransportState)
  44. }
  45. protocol MessageTransport {
  46. var delegate: MessageTransportDelegate? { get set }
  47. var messageNumber: Int { get }
  48. func sendMessage(_ message: Message) throws -> Message
  49. /// Asserts that the caller is currently on the session's queue
  50. func assertOnSessionQueue()
  51. }
  52. class PodMessageTransport: MessageTransport {
  53. private let session: CommandSession
  54. private let log = OSLog(category: "PodMessageTransport")
  55. private var state: MessageTransportState {
  56. didSet {
  57. self.delegate?.messageTransport(self, didUpdate: state)
  58. }
  59. }
  60. private(set) var packetNumber: Int {
  61. get {
  62. return state.packetNumber
  63. }
  64. set {
  65. state.packetNumber = newValue
  66. }
  67. }
  68. private(set) var messageNumber: Int {
  69. get {
  70. return state.messageNumber
  71. }
  72. set {
  73. state.messageNumber = newValue
  74. }
  75. }
  76. private let address: UInt32
  77. private var ackAddress: UInt32 // During pairing, PDM acks with address it is assigning to channel
  78. weak var messageLogger: MessageLogger?
  79. weak var delegate: MessageTransportDelegate?
  80. init(session: CommandSession, address: UInt32 = 0xffffffff, ackAddress: UInt32? = nil, state: MessageTransportState) {
  81. self.session = session
  82. self.address = address
  83. self.ackAddress = ackAddress ?? address
  84. self.state = state
  85. }
  86. private func incrementPacketNumber(_ count: Int = 1) {
  87. packetNumber = (packetNumber + count) & 0b11111
  88. }
  89. private func incrementMessageNumber(_ count: Int = 1) {
  90. messageNumber = (messageNumber + count) & 0b1111
  91. }
  92. func makeAckPacket() -> Packet {
  93. return Packet(address: address, packetType: .ack, sequenceNum: packetNumber, data: Data(bigEndian: ackAddress))
  94. }
  95. func ackUntilQuiet() {
  96. let packetData = makeAckPacket().encoded()
  97. var lastHeardAt = Date()
  98. let quietWindow = TimeInterval(milliseconds: 300)
  99. while lastHeardAt.timeIntervalSinceNow > -quietWindow {
  100. do {
  101. let rfPacket = try session.sendAndListen(packetData, repeatCount: 1, timeout: quietWindow, retryCount: 0, preambleExtension: TimeInterval(milliseconds: 40))
  102. let packet = try Packet(rfPacket: rfPacket)
  103. if packet.address == address {
  104. lastHeardAt = Date() // Pod still sending
  105. }
  106. } catch RileyLinkDeviceError.responseTimeout {
  107. // Haven't heard anything in 300ms. POD heard our ack.
  108. break
  109. } catch {
  110. continue
  111. }
  112. }
  113. incrementPacketNumber()
  114. }
  115. /// Encodes and sends a packet to the pod, and receives and decodes its response
  116. ///
  117. /// - Parameters:
  118. /// - packet: The packet to send
  119. /// - repeatCount: Number of times to repeat packet before listening for a response. 0 = send once and do not repeat.
  120. /// - packetResponseTimeout: The amount of time to wait before retrying
  121. /// - exchangeTimeout: The amount of time to continue retrying before giving up
  122. /// - preambleExtension: Duration of preamble. Default is 127ms
  123. /// - Returns: The received response packet
  124. /// - Throws:
  125. /// - PodCommsError.noResponse
  126. /// - RileyLinkDeviceError
  127. func exchangePackets(packet: Packet, repeatCount: Int = 0, packetResponseTimeout: TimeInterval = .milliseconds(333), exchangeTimeout:TimeInterval = .seconds(9), preambleExtension: TimeInterval = .milliseconds(127)) throws -> Packet {
  128. let packetData = packet.encoded()
  129. let radioRetryCount = 9
  130. let start = Date()
  131. incrementPacketNumber()
  132. while (-start.timeIntervalSinceNow < exchangeTimeout) {
  133. do {
  134. let rfPacket = try session.sendAndListen(packetData, repeatCount: repeatCount, timeout: packetResponseTimeout, retryCount: radioRetryCount, preambleExtension: preambleExtension)
  135. let candidatePacket: Packet
  136. do {
  137. candidatePacket = try Packet(rfPacket: rfPacket)
  138. log.default("Received packet (%d): %@", rfPacket.rssi, rfPacket.data.hexadecimalString)
  139. } catch PacketError.insufficientData {
  140. log.default("Insufficient packet data: %@", rfPacket.data.hexadecimalString)
  141. continue
  142. } catch let error {
  143. log.default("Packet error: %@", String(describing: error))
  144. continue
  145. }
  146. guard candidatePacket.address == packet.address || candidatePacket.address == 0xFFFFFFFF else {
  147. log.default("Packet address 0x%x does not match 0x%x", candidatePacket.address, packet.address)
  148. continue
  149. }
  150. guard candidatePacket.sequenceNum == ((packet.sequenceNum + 1) & 0b11111) else {
  151. log.default("Packet sequence %@ does not match %@", String(describing: candidatePacket.sequenceNum), String(describing: ((packet.sequenceNum + 1) & 0b11111)))
  152. continue
  153. }
  154. // Once we have verification that the POD heard us, we can increment our counters
  155. incrementPacketNumber()
  156. return candidatePacket
  157. } catch RileyLinkDeviceError.responseTimeout {
  158. continue
  159. }
  160. }
  161. throw PodCommsError.noResponse
  162. }
  163. /// Packetizes a message, and performs a set of packet exchanges to send a message and receive the response
  164. ///
  165. /// - Parameters:
  166. /// - message: The message to send
  167. /// - Returns: The received message response
  168. /// - Throws:
  169. /// - PodCommsError.noResponse
  170. /// - PodCommsError.podAckedInsteadOfReturningResponse
  171. /// - PodCommsError.unexpectedPacketType
  172. /// - PodCommsError.emptyResponse
  173. /// - MessageError.invalidCrc
  174. /// - MessageError.invalidSequence
  175. /// - MessageError.invalidAddress
  176. /// - RileyLinkDeviceError
  177. func sendMessage(_ message: Message) throws -> Message {
  178. messageNumber = message.sequenceNum
  179. incrementMessageNumber()
  180. do {
  181. let responsePacket = try { () throws -> Packet in
  182. var firstPacket = true
  183. log.debug("Send: %@", String(describing: message))
  184. var dataRemaining = message.encoded()
  185. log.default("Send(Hex): %@", dataRemaining.hexadecimalString)
  186. messageLogger?.didSend(dataRemaining)
  187. while true {
  188. let packetType: PacketType = firstPacket ? .pdm : .con
  189. let sendPacket = Packet(address: address, packetType: packetType, sequenceNum: self.packetNumber, data: dataRemaining)
  190. dataRemaining = dataRemaining.subdata(in: sendPacket.data.count..<dataRemaining.count)
  191. firstPacket = false
  192. let response = try self.exchangePackets(packet: sendPacket)
  193. if dataRemaining.count == 0 {
  194. return response
  195. }
  196. }
  197. }()
  198. guard responsePacket.packetType != .ack else {
  199. messageLogger?.didReceive(responsePacket.encoded())
  200. log.default("Pod responded with ack instead of response: %@", String(describing: responsePacket))
  201. throw PodCommsError.podAckedInsteadOfReturningResponse
  202. }
  203. // Assemble fragmented message from multiple packets
  204. let response = try { () throws -> Message in
  205. var responseData = responsePacket.data
  206. while true {
  207. do {
  208. let msg = try Message(encodedData: responseData)
  209. log.default("Recv(Hex): %@", responseData.hexadecimalString)
  210. guard msg.address == address else {
  211. throw MessageError.invalidAddress(address: msg.address)
  212. }
  213. guard msg.sequenceNum == messageNumber else {
  214. throw MessageError.invalidSequence
  215. }
  216. messageLogger?.didReceive(responseData)
  217. return msg
  218. } catch MessageError.notEnoughData {
  219. log.debug("Sending ACK for CON")
  220. let conPacket = try self.exchangePackets(packet: makeAckPacket(), repeatCount: 3, preambleExtension:TimeInterval(milliseconds: 40))
  221. guard conPacket.packetType == .con else {
  222. log.default("Expected CON packet, received; %@", String(describing: conPacket))
  223. throw PodCommsError.unexpectedPacketType(packetType: conPacket.packetType)
  224. }
  225. responseData += conPacket.data
  226. } catch MessageError.invalidCrc {
  227. // throw the error without any logging for a garbage message
  228. throw MessageError.invalidCrc
  229. } catch let error {
  230. // log any other non-garbage messages that generate errors
  231. log.error("Error (%{public}@) Recv(Hex): %@", String(describing: error), responseData.hexadecimalString)
  232. messageLogger?.didReceive(responseData)
  233. throw error
  234. }
  235. }
  236. }()
  237. ackUntilQuiet()
  238. guard response.messageBlocks.count > 0 else {
  239. log.default("Empty response")
  240. throw PodCommsError.emptyResponse
  241. }
  242. incrementMessageNumber()
  243. return response
  244. } catch let error {
  245. log.error("Error during communication with POD: %@", String(describing: error))
  246. throw error
  247. }
  248. }
  249. func assertOnSessionQueue() {
  250. session.assertOnSessionQueue()
  251. }
  252. }