package com.grkj.iscs.ble import com.clj.fastble.data.BleDevice import com.clj.fastble.exception.BleException import com.grkj.iscs.extentions.crc16 import com.grkj.iscs.extentions.startsWith import com.grkj.iscs.extentions.toByteArray import com.grkj.iscs.extentions.toHexStrings import com.grkj.iscs.ble.BleConst.REQ_CURRENT_STATUS import com.grkj.iscs.ble.BleConst.REQ_GET_TOKEN import com.grkj.iscs.ble.BleConst.REQ_GET_VERSION import com.grkj.iscs.ble.BleConst.REQ_POWER_STATUS import com.grkj.iscs.ble.BleConst.REQ_SEND_WORK_TICKET import com.grkj.iscs.ble.BleConst.REQ_SWITCH_MODE import com.grkj.iscs.ble.BleConst.REQ_TRANSFER_FILE import com.grkj.iscs.ble.BleConst.REQ_WORK_TICKET_RESULT import com.grkj.iscs.ble.BleConst.REQ_WORK_TICKET_RESULT_PART import com.grkj.iscs.ble.BleConst.WRITE_UUID import com.grkj.iscs.util.Executor import com.grkj.iscs.util.log.LogUtil import java.io.File /** * 指令操作类 */ object BleCmdManager { /** * 拼接时间戳 */ private fun assembleTimeStamp(byteArray: ByteArray): ByteArray { return byteArray + getTimeStamp() } /** * 拼接时间戳 + token */ private fun assembleData(bleBean: BleBean, byteArray: ByteArray): ByteArray? { bleBean.token?.let { return assembleTimeStamp(byteArray) + it } ?: run { // TODO 有问题,一直循环 // getToken(bleBean.bleDevice.mac, object : CustomBleWriteCallback() { // override fun onPrompt(promptStr: String?) {} // // override fun onConnectPrompt(promptStr: String?) {} // // override fun onDisConnectPrompt(promptStr: String?) {} // // override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {} // // override fun onWriteFailure(exception: BleException?) {} // // }) // // TODO 临时方案 // Thread.sleep(100) // return assembleData(bleBean, byteArray) return null } } private fun getTimeStamp(): ByteArray { val tempArr = (System.currentTimeMillis() / 1000).toByteArray() val timeStampArr = byteArrayOf(tempArr[0], tempArr[1], tempArr[2], tempArr[3]) return timeStampArr } fun handleRsp(bleBean: BleBean, byteArray: ByteArray) { val len = byteArray[2].toInt() val token = byteArray.copyOfRange(len + 7, len + 11) if (token.contentEquals(bleBean.token)) { LogUtil.i("Token is right") } else { LogUtil.e("Token is wrong") } when { // 获取令牌 byteArray.startsWith(BleConst.RSP_GET_TOKEN) -> handleToken(bleBean.bleDevice, byteArray) // 工作模式切换 byteArray.startsWith(BleConst.RSP_SWITCH_MODE) -> handleSwitchModeResult(byteArray) // 工作票下发 byteArray.startsWith(BleConst.RSP_SEND_WORK_TICKET) -> handleWorkTicketResult(bleBean, byteArray) // 获取设备当前状态 byteArray.startsWith(BleConst.RSP_CURRENT_STATUS) -> handleCurrentStatus(byteArray) // 获取钥匙电量 byteArray.startsWith(BleConst.RSP_POWER_STATUS) -> handlePowerStatus(byteArray) // 传输文件 byteArray.startsWith(BleConst.RSP_TRANSFER_FILE) && byteArray[3] == 0x01.toByte() -> handleFileRsp(bleBean, byteArray) // 获取固件版本号 byteArray.startsWith(BleConst.RSP_GET_VERSION) -> handleVersion(byteArray) // 获取设备工作票完成情况 byteArray.startsWith(BleConst.RSP_WORK_TICKET_RESULT) && byteArray[3] == 0x02.toByte() -> handleTicketStatus(bleBean.bleDevice, byteArray) } } /** * 获取令牌 */ fun getToken(mac: String?, callback: CustomBleWriteCallback?) { LogUtil.i("$mac") BleUtil.instance?.getBleDeviceByMac(mac)?.bleDevice?.let { LogUtil.i("Get token : $mac") BleUtil.instance?.write(it, cmd = assembleTimeStamp(REQ_GET_TOKEN), writeCallback = callback) } } /** * 令牌处理 */ private fun handleToken(bleDevice: BleDevice, byteArray: ByteArray) { LogUtil.i("handleToken : ${byteArray.toHexStrings()}") BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let { it.token = byteArrayOf(byteArray[11], byteArray[12], byteArray[13], byteArray[14]) println("Token 赋值 ${it.token?.toHexStrings()} : ${bleDevice.mac}") } } /** * 工作模式切换 * * @param mode 0x01:工作模式 0x02:待机模式 */ fun switchMode(mode: ByteArray, bleDevice: BleDevice, callback: CustomBleWriteCallback?) { BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let { BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, REQ_SWITCH_MODE + mode), writeCallback = callback) } } /** * 工作模式切换结果 * job : 0x01:工作模式 0x02:待机模式 * res : 0x01:成功 0x02:失败 */ private fun handleSwitchModeResult(byteArray: ByteArray) { LogUtil.i("handleSwitchModeResult : ${byteArray.toHexStrings()}") val job = byteArray[4] val res = byteArray[5] } /** * 工作票下发 */ fun sendWorkTicket(json: String, idx: Int = 0, bleDevice: BleDevice, callback: CustomBleWriteCallback?) { LogUtil.i("sendWorkTicket : $idx") BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let { it.ticketSend = json } val totalLength = json.toByteArray().size val totalPackets = (totalLength + 128 - 1) / 128 val total = totalPackets.toByteArray() val data = if (idx == totalPackets - 1) { json.toByteArray().copyOfRange(idx * 128, json.toByteArray().size - 1) } else { json.toByteArray().copyOfRange(idx * 128, (idx + 1) * 128) } // val jsonInfo = total + idx.toByteArray() + CRC16.crc16(data, 0, data.size - 1).toByteArray() + data.size.toByteArray() + data val jsonInfo = total + idx.toByteArray() + data.crc16(0, data.size) + data.size.toByteArray() + data println("xixi1 : ${total.size} : ${idx.toByteArray().size} : ${data.crc16(0, data.size).size} : ${data.size.toByteArray().size} : ${data.size}") println("xixi2 : ${(jsonInfo.size + 1).toByteArray(1).size} : ${0x02.toByteArray(1).size} : ${jsonInfo.size}") val cmd = REQ_SEND_WORK_TICKET + (jsonInfo.size + 1).toByteArray(1) + 0x02.toByteArray(1) + jsonInfo BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let { BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, cmd), writeCallback = callback) } } /** * 工作票下发结果 * res:0x00:成功 0x01:失败 0x02:传输超时 0x0D:当前IDX超出范围 0x0E:当前数据CRC校验失败 0x14:JSON结构错误 0x63:未知错误 */ private fun handleWorkTicketResult(bleBean: BleBean, byteArray: ByteArray) { LogUtil.i("handleWorkTicketResult : ${byteArray.toHexStrings()}") val idx = byteArray[4] + byteArray[5] val total = byteArray[6] + byteArray[7] val res = byteArray[8] if (idx != total - 1 && (res == 0x00.toByte() || res == 0x02.toByte())) { // TODO 要判断res sendWorkTicket( BleUtil.instance?.getBleDeviceByMac(bleBean.bleDevice.mac)?.ticketSend!!, if (res == 0x00.toByte()) idx + 1 else idx, bleBean.bleDevice, object : CustomBleWriteCallback() { override fun onPrompt(promptStr: String?) {} override fun onConnectPrompt(promptStr: String?) {} override fun onDisConnectPrompt(promptStr: String?) {} override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {} override fun onWriteFailure(exception: BleException?) {} }) } } /** * 获取设备当前状态 */ fun getCurrentStatus(bleDevice: BleDevice, callback: CustomBleWriteCallback?) { BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let { BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, REQ_CURRENT_STATUS), writeCallback = callback) } } /** * 处理设备当前状态 * 0x01:工作模式 0x02:待机模式 0x03:故障状态 */ private fun handleCurrentStatus(byteArray: ByteArray) { LogUtil.i("handleCurrentStatus : ${byteArray.toHexStrings()}") val job = byteArray[4] } /** * 获取工作票完成情况 */ fun getTicketStatus(bleDevice: BleDevice, callback: CustomBleWriteCallback?) { BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let { BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, REQ_WORK_TICKET_RESULT), writeCallback = callback) } } /** * 处理工作票完成情况 */ private fun handleTicketStatus(bleDevice: BleDevice, byteArray: ByteArray) { // TODO 需要有超时重传机制 LogUtil.i("handleTicketStatus : ${byteArray.toHexStrings()}") val total = byteArray[4] + byteArray[5] val idx = byteArray[6] + byteArray[7] val crc = byteArray[8] + byteArray[9] val size = byteArray[10].toUByte() + byteArray[11].toUByte() println("工作票数据 : $total : $idx : $size") // 数据组装 BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let { it.ticketStatus += byteArray.copyOfRange(12, 12 + size.toInt()) } // TODO 缺少res处理 if (idx != total - 1) { getTicketStatusPart((idx + 1).toByteArray(), total.toByteArray(), byteArrayOf(0x01.toByte()), bleDevice, object : CustomBleWriteCallback() { override fun onPrompt(promptStr: String?) {} override fun onConnectPrompt(promptStr: String?) {} override fun onDisConnectPrompt(promptStr: String?) {} override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { println("getTicketStatusPart success") } override fun onWriteFailure(exception: BleException?) { println("getTicketStatusPart fail") } }) } else { BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let { println("工作票完成接收 : ${String(it.ticketStatus)}") // TODO 清空ticket // it.ticket = byteArrayOf() } } } /** * 获取工作票完成情况分包 */ private fun getTicketStatusPart(idx: ByteArray, total: ByteArray, res: ByteArray, bleDevice: BleDevice, callback: CustomBleWriteCallback?) { BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let { BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, REQ_WORK_TICKET_RESULT_PART + idx + total + res), writeCallback = callback) } } /** * 获取钥匙电量 */ fun getPower(mac: String?, callback: CustomBleWriteCallback?) { BleUtil.instance?.getBleDeviceByMac(mac)?.let { BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, REQ_POWER_STATUS), writeCallback = callback) } } /** * 处理钥匙电量 * bat:电量百分比,范围 0-100,单位:% * chg:0x01:未充电 0x02:充电中 0x03:充满 */ private fun handlePowerStatus(byteArray: ByteArray) { LogUtil.i("handlePowerStatus : ${byteArray.toHexStrings()}") val bat = byteArray[4].toInt() val chg = byteArray[5] } /** * 发送文件 * type: 1:固件文件 2:点位PNG文件 * FLNM:文件名(不含扩展名) * FLSZ:文件大小(字节) * FLCRC:文件的CRC-16 * PGTOTAL:文件总包数 * PGIDX:当前包idx * PGCRC:当前包CRC-16 * PGSZ:当前包长度(字节) * PGDATA:当前包数据 */ fun sendFile(type: Int, file: File, idx: Int = 0, mac: String?, callback: CustomBleWriteCallback?) { Executor.runOnIO { LogUtil.i("sendFile : $idx") BleUtil.instance?.getBleDeviceByMac(mac)?.let { it.fileSend = file } val pgtotal = (file.readBytes().size + 128 - 1) / 128 if (idx == pgtotal) { LogUtil.i("Send finish") return@runOnIO } val flnm = file.name.substringBeforeLast(".").toByteArray(8) val flsz = file.readBytes().size.toByteArray(4) val flcrc = file.readBytes().crc16() val pgdata = if (idx == pgtotal - 1) { file.readBytes().copyOfRange(idx * 128, file.readBytes().size - 1) } else { file.readBytes().copyOfRange(idx * 128, (idx + 1) * 128) } val pgsz = pgdata.size.toByteArray() val pgcrc = pgdata.crc16() val fileInfo = byteArrayOf(type.toByte()) + flnm + flsz + flcrc + pgtotal.toByteArray() + idx.toByteArray() + pgcrc + pgsz + pgdata // println("______________________________________________________________________________") // println("${file.readBytes().size}") // println("${byteArrayOf(type.toByte()).size} : ${flnm.size} : ${flsz.size} : ${flcrc.size} : ${pgtotal.toByteArray().size} : ${idx.toByteArray().size} : ${pgcrc.size} : ${pgsz.size} : ${pgdata.size}") // println("______________________________________________________________________________") // println("file info size : ${fileInfo.size}") val cmd = REQ_TRANSFER_FILE + (fileInfo.size + 1).toByteArray(1) + 0x01.toByteArray(1) + fileInfo // println("cmd size : ${REQ_TRANSFER_FILE.size} : ${(fileInfo.size + 1).toByteArray(1).size} : ${0x01.toByteArray(1).size} : ${fileInfo.size}") BleUtil.instance?.getBleDeviceByMac(mac)?.let { BleUtil.instance?.write(it.bleDevice, writeUUID = WRITE_UUID, cmd = assembleData(it, cmd), writeCallback = callback) } Thread.sleep(50) sendFile(type, file, idx + 1, mac, callback) } } /** * 处理发送分包文件响应 * type: 0x01:固件文件 0x02:点位PNG文件 */ private fun handleFileRsp(bleBean: BleBean, byteArray: ByteArray) { LogUtil.i("handleFileRsp : ${byteArray.toHexStrings()}") val type = byteArray[4] val res = byteArray[17] val total = byteArray[15] + byteArray[16] val idx = byteArray[13] + byteArray[14] if (idx != total - 1 && (res == 0x00.toByte() || res == 0x02.toByte())) { // TODO 不用等回复再发 // sendFile(type.toInt(), // BleUtil.instance?.getBleDeviceByMac(bleBean.bleDevice.mac)?.fileSend!!, // if (res == 0x00.toByte()) idx + 1 else idx, // bleBean.bleDevice.mac, // object : CustomBleWriteCallback() { // override fun onPrompt(promptStr: String?) {} // // override fun onConnectPrompt(promptStr: String?) {} // // override fun onDisConnectPrompt(promptStr: String?) {} // // override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {} // // override fun onWriteFailure(exception: BleException?) {} // // }) } } /** * 获取版本 */ fun getVersion(mac: String?, callback: CustomBleWriteCallback?) { BleUtil.instance?.getBleDeviceByMac(mac)?.let { BleUtil.instance?.write(it.bleDevice, cmd = assembleData(it, REQ_GET_VERSION), writeCallback = callback) } } /** * 处理软件/硬件版本 */ private fun handleVersion(byteArray: ByteArray) { val sofVersion = parseVersion(byteArray[4]) val hardVersion = parseVersion(byteArray[5]) LogUtil.i("$sofVersion : $hardVersion") } /** * 版本解析 */ private fun parseVersion(byte: Byte): String { // 将 Byte 转换为 Int 以便更容易进行位操作 val intValue = byte.toInt() // 提取高 4 位作为主版本号 val majorVersion = (intValue and 0xF0) ushr 4 // 提取低 4 位作为次版本号 val minorVersion = intValue and 0x0F return "V$majorVersion.$minorVersion" } }