Message.swift 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. //
  2. // Message.swift
  3. // OmniKit
  4. //
  5. // Created by Pete Schwamb on 10/14/17.
  6. // Copyright © 2017 Pete Schwamb. All rights reserved.
  7. //
  8. import Foundation
  9. public enum MessageError: Error {
  10. case notEnoughData
  11. case invalidCrc
  12. case invalidSequence
  13. case invalidAddress(address: UInt32)
  14. case parsingError(offset: Int, data: Data, error: Error)
  15. case unknownValue(value: UInt8, typeDescription: String)
  16. case validationFailed(description: String)
  17. }
  18. extension MessageError: LocalizedError {
  19. public var errorDescription: String? {
  20. switch self {
  21. case .notEnoughData:
  22. return LocalizedString("Not enough data", comment: "Description for MessageError notEnoughData")
  23. case .invalidCrc:
  24. return LocalizedString("Invalid CRC", comment: "Description for MessageError invalidCrc")
  25. case .parsingError:
  26. return LocalizedString("Parsing Error: ", comment: "Description for MessageError parsingError")
  27. case .unknownValue(let value, let typeDescription):
  28. return String(format: LocalizedString("Unknown Value (%1$@) for type %2$@", comment: "Format string for description of MessageError unknownValue. (1: value) (2: Type)"), String(describing: value), typeDescription)
  29. case .validationFailed(let description):
  30. return String(format: LocalizedString("Validation failed: %1$@", comment: "Format string for description of MessageError validationFailed. (1: description of validation failure)"), description)
  31. case .invalidSequence:
  32. return LocalizedString("Unexpected message sequence number", comment: "Description for MessageError invalidSequence")
  33. case .invalidAddress(address: let address):
  34. return String(format: LocalizedString("Invalid address: (%1$@)", comment: "Description for MessageError invalidSequence"), String(format: "%08x", address))
  35. }
  36. }
  37. }
  38. struct Message {
  39. let address: UInt32
  40. let messageBlocks: [MessageBlock]
  41. let sequenceNum: Int
  42. let expectFollowOnMessage: Bool
  43. init(address: UInt32, messageBlocks: [MessageBlock], sequenceNum: Int, expectFollowOnMessage: Bool = false) {
  44. self.address = address
  45. self.messageBlocks = messageBlocks
  46. self.sequenceNum = sequenceNum
  47. self.expectFollowOnMessage = expectFollowOnMessage
  48. }
  49. init(encodedData: Data) throws {
  50. guard encodedData.count >= 10 else {
  51. throw MessageError.notEnoughData
  52. }
  53. self.address = encodedData[0...].toBigEndian(UInt32.self)
  54. let b9 = encodedData[4]
  55. let bodyLen = encodedData[5]
  56. if bodyLen > encodedData.count - 8 {
  57. throw MessageError.notEnoughData
  58. }
  59. self.expectFollowOnMessage = (b9 & 0b10000000) != 0
  60. self.sequenceNum = Int((b9 >> 2) & 0b11111)
  61. let crc = (UInt16(encodedData[encodedData.count-2]) << 8) + UInt16(encodedData[encodedData.count-1])
  62. let msgWithoutCrc = encodedData.prefix(encodedData.count - 2)
  63. guard msgWithoutCrc.crc16() == crc else {
  64. throw MessageError.invalidCrc
  65. }
  66. self.messageBlocks = try Message.decodeBlocks(data: Data(msgWithoutCrc.suffix(from: 6)))
  67. }
  68. static private func decodeBlocks(data: Data) throws -> [MessageBlock] {
  69. var blocks = [MessageBlock]()
  70. var idx = 0
  71. repeat {
  72. guard let blockType = MessageBlockType(rawValue: data[idx]) else {
  73. throw MessageBlockError.unknownBlockType(rawVal: data[idx])
  74. }
  75. do {
  76. let block = try blockType.blockType.init(encodedData: Data(data.suffix(from: idx)))
  77. blocks.append(block)
  78. idx += Int(block.data.count)
  79. } catch (let error) {
  80. throw MessageError.parsingError(offset: idx, data: data.suffix(from: idx), error: error)
  81. }
  82. } while idx < data.count
  83. return blocks
  84. }
  85. func encoded() -> Data {
  86. var bytes = Data(bigEndian: address)
  87. var cmdData = Data()
  88. for cmd in messageBlocks {
  89. cmdData.append(cmd.data)
  90. }
  91. let b9: UInt8 = ((expectFollowOnMessage ? 1 : 0) << 7) + (UInt8(sequenceNum & 0b11111) << 2) + UInt8((cmdData.count >> 8) & 0b11)
  92. bytes.append(b9)
  93. bytes.append(UInt8(cmdData.count & 0xff))
  94. var data = Data(bytes) + cmdData
  95. let crc = data.crc16()
  96. data.appendBigEndian(crc)
  97. return data
  98. }
  99. var fault: DetailedStatus? {
  100. if messageBlocks.count > 0 && messageBlocks[0].blockType == .podInfoResponse,
  101. let infoResponse = messageBlocks[0] as? PodInfoResponse,
  102. infoResponse.podInfoResponseSubType == .detailedStatus,
  103. let detailedStatus = infoResponse.podInfo as? DetailedStatus,
  104. detailedStatus.isFaulted
  105. {
  106. return detailedStatus
  107. } else {
  108. return nil
  109. }
  110. }
  111. }
  112. extension Message: CustomDebugStringConvertible {
  113. var debugDescription: String {
  114. let sequenceNumStr = String(format: "%02d", sequenceNum)
  115. return "Message(\(Data(bigEndian: address).hexadecimalString) seq:\(sequenceNumStr) \(messageBlocks))"
  116. }
  117. }