BleCmdManager.kt 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. package com.grkj.iscs.ble
  2. import com.clj.fastble.data.BleDevice
  3. import com.clj.fastble.exception.BleException
  4. import com.grkj.iscs.extentions.crc16
  5. import com.grkj.iscs.extentions.startsWith
  6. import com.grkj.iscs.extentions.toByteArray
  7. import com.grkj.iscs.extentions.toHexStrings
  8. import com.grkj.iscs.ble.BleConst.REQ_CURRENT_STATUS
  9. import com.grkj.iscs.ble.BleConst.REQ_GET_TOKEN
  10. import com.grkj.iscs.ble.BleConst.REQ_GET_VERSION
  11. import com.grkj.iscs.ble.BleConst.REQ_POWER_STATUS
  12. import com.grkj.iscs.ble.BleConst.REQ_SEND_WORK_TICKET
  13. import com.grkj.iscs.ble.BleConst.REQ_SWITCH_MODE
  14. import com.grkj.iscs.ble.BleConst.REQ_TRANSFER_FILE
  15. import com.grkj.iscs.ble.BleConst.REQ_WORK_TICKET_RESULT
  16. import com.grkj.iscs.ble.BleConst.REQ_WORK_TICKET_RESULT_PART
  17. import com.grkj.iscs.ble.BleConst.WRITE_UUID
  18. import com.grkj.iscs.util.Executor
  19. import com.grkj.iscs.util.log.LogUtil
  20. import java.io.File
  21. /**
  22. * 指令操作类
  23. */
  24. object BleCmdManager {
  25. /**
  26. * 拼接时间戳
  27. */
  28. private fun assembleTimeStamp(byteArray: ByteArray): ByteArray {
  29. return byteArray + getTimeStamp()
  30. }
  31. /**
  32. * 拼接时间戳 + token
  33. */
  34. private fun assembleData(bleBean: BleBean, byteArray: ByteArray): ByteArray? {
  35. bleBean.token?.let {
  36. return assembleTimeStamp(byteArray) + it
  37. } ?: run {
  38. // TODO 有问题,一直循环
  39. // getToken(bleBean.bleDevice.mac, object : CustomBleWriteCallback() {
  40. // override fun onPrompt(promptStr: String?) {}
  41. //
  42. // override fun onConnectPrompt(promptStr: String?) {}
  43. //
  44. // override fun onDisConnectPrompt(promptStr: String?) {}
  45. //
  46. // override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {}
  47. //
  48. // override fun onWriteFailure(exception: BleException?) {}
  49. //
  50. // })
  51. // // TODO 临时方案
  52. // Thread.sleep(100)
  53. // return assembleData(bleBean, byteArray)
  54. return null
  55. }
  56. }
  57. private fun getTimeStamp(): ByteArray {
  58. val tempArr = (System.currentTimeMillis() / 1000).toByteArray()
  59. val timeStampArr = byteArrayOf(tempArr[0], tempArr[1], tempArr[2], tempArr[3])
  60. return timeStampArr
  61. }
  62. fun handleRsp(bleBean: BleBean, byteArray: ByteArray) {
  63. val len = byteArray[2].toInt()
  64. val token = byteArray.copyOfRange(len + 7, len + 11)
  65. if (token.contentEquals(bleBean.token)) {
  66. LogUtil.i("Token is right")
  67. } else {
  68. LogUtil.e("Token is wrong")
  69. }
  70. when {
  71. // 获取令牌
  72. byteArray.startsWith(BleConst.RSP_GET_TOKEN) -> handleToken(bleBean.bleDevice, byteArray)
  73. // 工作模式切换
  74. byteArray.startsWith(BleConst.RSP_SWITCH_MODE) -> handleSwitchModeResult(byteArray)
  75. // 工作票下发
  76. byteArray.startsWith(BleConst.RSP_SEND_WORK_TICKET) -> handleWorkTicketResult(bleBean, byteArray)
  77. // 获取设备当前状态
  78. byteArray.startsWith(BleConst.RSP_CURRENT_STATUS) -> handleCurrentStatus(byteArray)
  79. // 获取钥匙电量
  80. byteArray.startsWith(BleConst.RSP_POWER_STATUS) -> handlePowerStatus(byteArray)
  81. // 传输文件
  82. byteArray.startsWith(BleConst.RSP_TRANSFER_FILE) && byteArray[3] == 0x01.toByte() -> handleFileRsp(bleBean, byteArray)
  83. // 获取固件版本号
  84. byteArray.startsWith(BleConst.RSP_GET_VERSION) -> handleVersion(byteArray)
  85. // 获取设备工作票完成情况
  86. byteArray.startsWith(BleConst.RSP_WORK_TICKET_RESULT) && byteArray[3] == 0x02.toByte() ->
  87. handleTicketStatus(bleBean.bleDevice, byteArray)
  88. }
  89. }
  90. /**
  91. * 获取令牌
  92. */
  93. fun getToken(mac: String?, callback: CustomBleWriteCallback?) {
  94. LogUtil.i("$mac")
  95. BleUtil.instance?.getBleDeviceByMac(mac)?.bleDevice?.let {
  96. LogUtil.i("Get token : $mac")
  97. BleUtil.instance?.write(it, cmd = assembleTimeStamp(REQ_GET_TOKEN), writeCallback = callback)
  98. }
  99. }
  100. /**
  101. * 令牌处理
  102. */
  103. private fun handleToken(bleDevice: BleDevice, byteArray: ByteArray) {
  104. LogUtil.i("handleToken : ${byteArray.toHexStrings()}")
  105. BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let {
  106. it.token = byteArrayOf(byteArray[11], byteArray[12], byteArray[13], byteArray[14])
  107. println("Token 赋值 ${it.token?.toHexStrings()} : ${bleDevice.mac}")
  108. }
  109. }
  110. /**
  111. * 工作模式切换
  112. *
  113. * @param mode 0x01:工作模式 0x02:待机模式
  114. */
  115. fun switchMode(mode: ByteArray, bleDevice: BleDevice, callback: CustomBleWriteCallback?) {
  116. BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let {
  117. BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, REQ_SWITCH_MODE + mode), writeCallback = callback)
  118. }
  119. }
  120. /**
  121. * 工作模式切换结果
  122. * job : 0x01:工作模式 0x02:待机模式
  123. * res : 0x01:成功 0x02:失败
  124. */
  125. private fun handleSwitchModeResult(byteArray: ByteArray) {
  126. LogUtil.i("handleSwitchModeResult : ${byteArray.toHexStrings()}")
  127. val job = byteArray[4]
  128. val res = byteArray[5]
  129. }
  130. /**
  131. * 工作票下发
  132. */
  133. fun sendWorkTicket(json: String, idx: Int = 0, bleDevice: BleDevice, callback: CustomBleWriteCallback?) {
  134. LogUtil.i("sendWorkTicket : $idx")
  135. BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let {
  136. it.ticketSend = json
  137. }
  138. val totalLength = json.toByteArray().size
  139. val totalPackets = (totalLength + 128 - 1) / 128
  140. val total = totalPackets.toByteArray()
  141. val data = if (idx == totalPackets - 1) {
  142. json.toByteArray().copyOfRange(idx * 128, json.toByteArray().size - 1)
  143. } else {
  144. json.toByteArray().copyOfRange(idx * 128, (idx + 1) * 128)
  145. }
  146. // val jsonInfo = total + idx.toByteArray() + CRC16.crc16(data, 0, data.size - 1).toByteArray() + data.size.toByteArray() + data
  147. val jsonInfo = total + idx.toByteArray() + data.crc16(0, data.size) + data.size.toByteArray() + data
  148. println("xixi1 : ${total.size} : ${idx.toByteArray().size} : ${data.crc16(0, data.size).size} : ${data.size.toByteArray().size} : ${data.size}")
  149. println("xixi2 : ${(jsonInfo.size + 1).toByteArray(1).size} : ${0x02.toByteArray(1).size} : ${jsonInfo.size}")
  150. val cmd = REQ_SEND_WORK_TICKET + (jsonInfo.size + 1).toByteArray(1) + 0x02.toByteArray(1) + jsonInfo
  151. BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let {
  152. BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, cmd), writeCallback = callback)
  153. }
  154. }
  155. /**
  156. * 工作票下发结果
  157. * res:0x00:成功 0x01:失败 0x02:传输超时 0x0D:当前IDX超出范围 0x0E:当前数据CRC校验失败 0x14:JSON结构错误 0x63:未知错误
  158. */
  159. private fun handleWorkTicketResult(bleBean: BleBean, byteArray: ByteArray) {
  160. LogUtil.i("handleWorkTicketResult : ${byteArray.toHexStrings()}")
  161. val idx = byteArray[4] + byteArray[5]
  162. val total = byteArray[6] + byteArray[7]
  163. val res = byteArray[8]
  164. if (idx != total - 1 && (res == 0x00.toByte() || res == 0x02.toByte())) {
  165. // TODO 要判断res
  166. sendWorkTicket(
  167. BleUtil.instance?.getBleDeviceByMac(bleBean.bleDevice.mac)?.ticketSend!!,
  168. if (res == 0x00.toByte()) idx + 1 else idx,
  169. bleBean.bleDevice,
  170. object : CustomBleWriteCallback() {
  171. override fun onPrompt(promptStr: String?) {}
  172. override fun onConnectPrompt(promptStr: String?) {}
  173. override fun onDisConnectPrompt(promptStr: String?) {}
  174. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {}
  175. override fun onWriteFailure(exception: BleException?) {}
  176. })
  177. }
  178. }
  179. /**
  180. * 获取设备当前状态
  181. */
  182. fun getCurrentStatus(bleDevice: BleDevice, callback: CustomBleWriteCallback?) {
  183. BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let {
  184. BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, REQ_CURRENT_STATUS), writeCallback = callback)
  185. }
  186. }
  187. /**
  188. * 处理设备当前状态
  189. * 0x01:工作模式 0x02:待机模式 0x03:故障状态
  190. */
  191. private fun handleCurrentStatus(byteArray: ByteArray) {
  192. LogUtil.i("handleCurrentStatus : ${byteArray.toHexStrings()}")
  193. val job = byteArray[4]
  194. }
  195. /**
  196. * 获取工作票完成情况
  197. */
  198. fun getTicketStatus(bleDevice: BleDevice, callback: CustomBleWriteCallback?) {
  199. BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let {
  200. BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, REQ_WORK_TICKET_RESULT), writeCallback = callback)
  201. }
  202. }
  203. /**
  204. * 处理工作票完成情况
  205. */
  206. private fun handleTicketStatus(bleDevice: BleDevice, byteArray: ByteArray) {
  207. // TODO 需要有超时重传机制
  208. LogUtil.i("handleTicketStatus : ${byteArray.toHexStrings()}")
  209. val total = byteArray[4] + byteArray[5]
  210. val idx = byteArray[6] + byteArray[7]
  211. val crc = byteArray[8] + byteArray[9]
  212. val size = byteArray[10].toUByte() + byteArray[11].toUByte()
  213. println("工作票数据 : $total : $idx : $size")
  214. // 数据组装
  215. BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let {
  216. it.ticketStatus += byteArray.copyOfRange(12, 12 + size.toInt())
  217. }
  218. // TODO 缺少res处理
  219. if (idx != total - 1) {
  220. getTicketStatusPart((idx + 1).toByteArray(), total.toByteArray(), byteArrayOf(0x01.toByte()), bleDevice, object : CustomBleWriteCallback() {
  221. override fun onPrompt(promptStr: String?) {}
  222. override fun onConnectPrompt(promptStr: String?) {}
  223. override fun onDisConnectPrompt(promptStr: String?) {}
  224. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
  225. println("getTicketStatusPart success")
  226. }
  227. override fun onWriteFailure(exception: BleException?) {
  228. println("getTicketStatusPart fail")
  229. }
  230. })
  231. } else {
  232. BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let {
  233. println("工作票完成接收 : ${String(it.ticketStatus)}")
  234. // TODO 清空ticket
  235. // it.ticket = byteArrayOf()
  236. }
  237. }
  238. }
  239. /**
  240. * 获取工作票完成情况分包
  241. */
  242. private fun getTicketStatusPart(idx: ByteArray, total: ByteArray, res: ByteArray, bleDevice: BleDevice, callback: CustomBleWriteCallback?) {
  243. BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let {
  244. BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, REQ_WORK_TICKET_RESULT_PART + idx + total + res), writeCallback = callback)
  245. }
  246. }
  247. /**
  248. * 获取钥匙电量
  249. */
  250. fun getPower(mac: String?, callback: CustomBleWriteCallback?) {
  251. BleUtil.instance?.getBleDeviceByMac(mac)?.let {
  252. BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, REQ_POWER_STATUS), writeCallback = callback)
  253. }
  254. }
  255. /**
  256. * 处理钥匙电量
  257. * bat:电量百分比,范围 0-100,单位:%
  258. * chg:0x01:未充电 0x02:充电中 0x03:充满
  259. */
  260. private fun handlePowerStatus(byteArray: ByteArray) {
  261. LogUtil.i("handlePowerStatus : ${byteArray.toHexStrings()}")
  262. val bat = byteArray[4].toInt()
  263. val chg = byteArray[5]
  264. }
  265. /**
  266. * 发送文件
  267. * type: 1:固件文件 2:点位PNG文件
  268. * FLNM:文件名(不含扩展名)
  269. * FLSZ:文件大小(字节)
  270. * FLCRC:文件的CRC-16
  271. * PGTOTAL:文件总包数
  272. * PGIDX:当前包idx
  273. * PGCRC:当前包CRC-16
  274. * PGSZ:当前包长度(字节)
  275. * PGDATA:当前包数据
  276. */
  277. fun sendFile(type: Int, file: File, idx: Int = 0, mac: String?, callback: CustomBleWriteCallback?) {
  278. Executor.runOnIO {
  279. LogUtil.i("sendFile : $idx")
  280. BleUtil.instance?.getBleDeviceByMac(mac)?.let {
  281. it.fileSend = file
  282. }
  283. val pgtotal = (file.readBytes().size + 128 - 1) / 128
  284. if (idx == pgtotal) {
  285. LogUtil.i("Send finish")
  286. return@runOnIO
  287. }
  288. val flnm = file.name.substringBeforeLast(".").toByteArray(8)
  289. val flsz = file.readBytes().size.toByteArray(4)
  290. val flcrc = file.readBytes().crc16()
  291. val pgdata = if (idx == pgtotal - 1) {
  292. file.readBytes().copyOfRange(idx * 128, file.readBytes().size - 1)
  293. } else {
  294. file.readBytes().copyOfRange(idx * 128, (idx + 1) * 128)
  295. }
  296. val pgsz = pgdata.size.toByteArray()
  297. val pgcrc = pgdata.crc16()
  298. val fileInfo = byteArrayOf(type.toByte()) + flnm + flsz + flcrc + pgtotal.toByteArray() + idx.toByteArray() + pgcrc + pgsz + pgdata
  299. // println("______________________________________________________________________________")
  300. // println("${file.readBytes().size}")
  301. // println("${byteArrayOf(type.toByte()).size} : ${flnm.size} : ${flsz.size} : ${flcrc.size} : ${pgtotal.toByteArray().size} : ${idx.toByteArray().size} : ${pgcrc.size} : ${pgsz.size} : ${pgdata.size}")
  302. // println("______________________________________________________________________________")
  303. // println("file info size : ${fileInfo.size}")
  304. val cmd = REQ_TRANSFER_FILE + (fileInfo.size + 1).toByteArray(1) + 0x01.toByteArray(1) + fileInfo
  305. // println("cmd size : ${REQ_TRANSFER_FILE.size} : ${(fileInfo.size + 1).toByteArray(1).size} : ${0x01.toByteArray(1).size} : ${fileInfo.size}")
  306. BleUtil.instance?.getBleDeviceByMac(mac)?.let {
  307. BleUtil.instance?.write(it.bleDevice, writeUUID = WRITE_UUID, cmd = assembleData(it, cmd), writeCallback = callback)
  308. }
  309. Thread.sleep(50)
  310. sendFile(type, file, idx + 1, mac, callback)
  311. }
  312. }
  313. /**
  314. * 处理发送分包文件响应
  315. * type: 0x01:固件文件 0x02:点位PNG文件
  316. */
  317. private fun handleFileRsp(bleBean: BleBean, byteArray: ByteArray) {
  318. LogUtil.i("handleFileRsp : ${byteArray.toHexStrings()}")
  319. val type = byteArray[4]
  320. val res = byteArray[17]
  321. val total = byteArray[15] + byteArray[16]
  322. val idx = byteArray[13] + byteArray[14]
  323. if (idx != total - 1 && (res == 0x00.toByte() || res == 0x02.toByte())) {
  324. // TODO 不用等回复再发
  325. // sendFile(type.toInt(),
  326. // BleUtil.instance?.getBleDeviceByMac(bleBean.bleDevice.mac)?.fileSend!!,
  327. // if (res == 0x00.toByte()) idx + 1 else idx,
  328. // bleBean.bleDevice.mac,
  329. // object : CustomBleWriteCallback() {
  330. // override fun onPrompt(promptStr: String?) {}
  331. //
  332. // override fun onConnectPrompt(promptStr: String?) {}
  333. //
  334. // override fun onDisConnectPrompt(promptStr: String?) {}
  335. //
  336. // override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {}
  337. //
  338. // override fun onWriteFailure(exception: BleException?) {}
  339. //
  340. // })
  341. }
  342. }
  343. /**
  344. * 获取版本
  345. */
  346. fun getVersion(mac: String?, callback: CustomBleWriteCallback?) {
  347. BleUtil.instance?.getBleDeviceByMac(mac)?.let {
  348. BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, REQ_GET_VERSION), writeCallback = callback)
  349. }
  350. }
  351. /**
  352. * 处理软件/硬件版本
  353. */
  354. private fun handleVersion(byteArray: ByteArray) {
  355. val sofVersion = parseVersion(byteArray[4])
  356. val hardVersion = parseVersion(byteArray[5])
  357. LogUtil.i("$sofVersion : $hardVersion")
  358. }
  359. /**
  360. * 版本解析
  361. */
  362. private fun parseVersion(byte: Byte): String {
  363. // 将 Byte 转换为 Int 以便更容易进行位操作
  364. val intValue = byte.toInt()
  365. // 提取高 4 位作为主版本号
  366. val majorVersion = (intValue and 0xF0) ushr 4
  367. // 提取低 4 位作为次版本号
  368. val minorVersion = intValue and 0x0F
  369. return "V$majorVersion.$minorVersion"
  370. }
  371. }