diff --git a/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift index 84c3a4a44..d757e18ca 100644 --- a/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift +++ b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift @@ -891,22 +891,27 @@ private var cmdReplyType: UInt8 = 0 } /// 处理接收到的响应数据 + /// + /// - 功能: + /// - 解析设备响应帧,校验头、长度与 CRC + /// - 与 Android 保持一致的失败判定:当长度字段与实际长度不匹配时,上报 rawData = [length, data[data.count-2]] + /// - 在 CMD_CONTROL_CODEC 分支对齐安卓对错误响应的处理逻辑 private func handleResponseData(_ data: Data) { - guard let characteristic = writeCharacteristic, + guard let characteristic = writeCharacteristic, connectionState == BleConst.STATE_CONNECTED else { os_log(" 设备未连接", log: logger, type: .error) - return + return } guard data.count >= 4 else { os_log("响应数据格式错误:数据长度过短", log: logger, type: .error) return } - + // 提取帧信息 let header = data[0] let cmd = data[1] let length = data[2] - + // 检查帧头,确保是设备响应 guard header == BleConst.DEVICE_RESPONSE_HEADER else { // 如果是设备主动上报 @@ -915,35 +920,59 @@ private var cmdReplyType: UInt8 = 0 } return } - + // 记录响应详情 var hexString = data.map { String(format: "0x%02X", $0) }.joined(separator: ", ") os_log("收到设备响应: %@", log: logger, type: .debug, hexString) - - // 检查数据长度 + + // 检查数据长度(与 Android 统一失败判定保持一致) if data.count != Int(length) + 4 { // 帧头 + 命令 + 长度 + 数据 + CRC os_log("设备响应数据长度不匹配: 期望=%d, 实际=%d", log: logger, type: .error, Int(length) + 4, data.count) + + // 与 Android 保持一致:构造 rawData 为 [length, 倒数第二位(payload 最后一位)] + var raw: [UInt8] = [] + raw.append(length) + if data.count >= 2 { + raw.append(data[data.count - 2]) + } + + let resultMap: [String: Any] = [ + "success": true, // 与 Android 对齐(Dart 层按 rawData 判断) + "command": Int(cmd), + "rawData": Data(raw) + ] + + // 推进命令队列(与后续正常路径一致) + isReply = true + cmdReplyType = cmd + if !commandQueue.isEmpty { + let nextCmd = commandQueue.removeFirst() + peripheralDevice?.writeValue(nextCmd, for: characteristic, type: .withoutResponse) + } + + // 上报到 Dart 层 + notifyDeviceInfoReceived(infoType: Int(cmd), infoData: resultMap) return } - + // 计算CRC校验 - 不包括最后一个字节(CRC) let payload = data.prefix(data.count - 1) let calculatedCrc = calculateCRC8(data: payload) let receivedCrc = data[data.count - 1] - + if calculatedCrc != receivedCrc { os_log("设备响应CRC校验失败: 计算=0x%02X, 接收=0x%02X", log: logger, type: .error, calculatedCrc, receivedCrc) return } - + // 提取响应数据 let responseData = length > 0 ? data.subdata(in: 3..<(3 + Int(length))) : Data() - isReply = true; - cmdReplyType = cmd; - if !commandQueue.isEmpty { - let nextCmd = commandQueue.removeFirst() - peripheralDevice?.writeValue(nextCmd, for: characteristic, type: .withoutResponse) -} + isReply = true + cmdReplyType = cmd + if !commandQueue.isEmpty { + let nextCmd = commandQueue.removeFirst() + peripheralDevice?.writeValue(nextCmd, for: characteristic, type: .withoutResponse) + } // 根据命令类型处理设备响应 switch cmd { @@ -953,103 +982,98 @@ private var cmdReplyType: UInt8 = 0 "command": "versionInfo", "rawData": responseData ] - + if !responseData.isEmpty { - // 如果无法解析为字符串,以点分隔的数字表示版本 let versionNums = responseData .map { $0 } - .filter { $0 != 0xFF } // 过滤掉无效字节 + .filter { $0 != 0xFF } .map { String($0) } .joined(separator: ".") resultMap["version"] = versionNums os_log("接收到目标版本号(数字格式): %@", log: logger, type: .info, versionNums) } - + notifyDeviceInfoReceived(infoType: Int(cmd), infoData: resultMap) - + case BleConst.CMD_GET_PRODUCT_ID: var resultMap: [String: Any] = [ "success": true, "command": "productId", "rawData": responseData ] - + if !responseData.isEmpty { let productId = responseData.map { String($0 & 0xFF) }.joined(separator: ".") resultMap["productId"] = productId os_log("接收到产品ID: %d", log: logger, type: .info, productId) } - + notifyDeviceInfoReceived(infoType: Int(cmd), infoData: resultMap) - + case BleConst.CMD_GET_COLOR_ID: var resultMap: [String: Any] = [ "success": true, "command": "colorInfo", "rawData": responseData ] - + if !responseData.isEmpty { let colorId = Int(responseData[0]) resultMap["colorId"] = colorId os_log("接收到耳机颜色ID: %d", log: logger, type: .info, colorId) } - + notifyDeviceInfoReceived(infoType: Int(cmd), infoData: resultMap) - + case BleConst.CMD_GET_BATTERY_INFO: var resultMap: [String: Any] = [ "success": true, "command": "batteryInfo", "rawData": responseData ] - + if responseData.count >= 3 { - // 处理电池信息 - 左耳、右耳、充电仓 let leftEarBattery = Int(responseData[0] & 0x7F) let leftEarCharging = (responseData[0] & 0x80) != 0 - + let rightEarBattery = Int(responseData[1] & 0x7F) let rightEarCharging = (responseData[1] & 0x80) != 0 - + let caseBattery = Int(responseData[2] & 0x7F) let caseCharging = (responseData[2] & 0x80) != 0 - - // 添加到结果Map + resultMap["leftEarBattery"] = leftEarBattery resultMap["leftEarCharging"] = leftEarCharging resultMap["rightEarBattery"] = rightEarBattery resultMap["rightEarCharging"] = rightEarCharging resultMap["caseBattery"] = caseBattery resultMap["caseCharging"] = caseCharging - resultMap["batteryLevel"] = leftEarBattery // 整体电量用左耳表示 - - os_log("接收到电量信息: 左耳=%d%% (%@), 右耳=%d%% (%@), 充电仓=%d%% (%@)", + resultMap["batteryLevel"] = leftEarBattery + + os_log("接收到电量信息: 左耳=%d%% (%@), 右耳=%d%% (%@), 充电仓=%d%% (%@)", log: logger, type: .info, - leftEarBattery, leftEarCharging ? "充电中" : "未充电", + leftEarBattery, leftEarCharging ? "充电中" : "未充电", rightEarBattery, rightEarCharging ? "充电中" : "未充电", caseBattery, caseCharging ? "充电中" : "未充电") } else if !responseData.isEmpty { - // 兼容单字节电量 let batteryLevel = Int(responseData[0]) resultMap["batteryLevel"] = batteryLevel os_log("接收到电量信息(简化): %d%%", log: logger, type: .info, batteryLevel) } - + notifyDeviceInfoReceived(infoType: Int(cmd), infoData: resultMap) - + case BleConst.CMD_CONTROL_CODEC: var resultMap: [String: Any] = [ "success": true, "command": Int(cmd), "rawData": responseData ] - - if responseData.count >= 2 { + + if !responseData.isEmpty && responseData.count >= 2 { let codecStatus = Int(responseData[0]) let channelMode = Int(responseData[1]) - - // 解码状态描述 + let statusDesc: String switch codecStatus { case Int(BleConst.CODEC_CONTROL_CLOSE): @@ -1065,8 +1089,7 @@ private var cmdReplyType: UInt8 = 0 default: statusDesc = "未知状态(\(codecStatus))" } - - // 声道模式描述 + let channelDesc: String switch channelMode { case Int(BleConst.AUDIO_CHANNEL_LEFT): @@ -1078,19 +1101,29 @@ private var cmdReplyType: UInt8 = 0 default: channelDesc = "未知声道(\(channelMode))" } - + resultMap["codecStatus"] = codecStatus resultMap["channelMode"] = channelMode resultMap["statusDesc"] = statusDesc resultMap["channelDesc"] = channelDesc - + os_log("编解码控制响应: %@, %@", log: logger, type: .info, statusDesc, channelDesc) + } else if !responseData.isEmpty && responseData.count == 1 { + // 与 Android 保持一致:单字节且为 0 判定为错误 + let codecStatus = Int(responseData[0]) + if codecStatus == 0x00 { + os_log("收到设备响应: 失败, codecStatus: %d", log: logger, type: .debug, codecStatus) + resultMap = [ + "success": true, // 与 Android 对齐(Dart 层按 rawData 判断) + "command": "Error", + "rawData": responseData + ] + } } - + notifyDeviceInfoReceived(infoType: Int(cmd), infoData: resultMap) - + default: - // 对于未知命令,返回原始数据 let resultMap: [String: Any] = [ "success": true, "command": Int(cmd),