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