Browse Source

Merge branch 'new_dev' of https://github.com/deepcloud2048/deep_voice into new_dev

newdev_shunjiawei
tanlongsheng 11 months ago
parent
commit
22051f766e
  1. 73
      lib/data/services/ble_manager.dart
  2. 96
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCommandSender.kt

73
lib/data/services/ble_manager.dart

@ -26,6 +26,67 @@ class BleCommands {
static const int CONTROL_CODEC = 0x05;
}
/// 判断编解码控制指令是否成功
///
/// 根据 rawData 第二字节是否为非 0 来判断:
/// - 成功:第二字节不为 0
/// - 失败或无法解析:返回 false
bool _isCodecControlSuccess(Map<dynamic, dynamic> data) {
final dynamic raw = data['rawData'];
if (raw == null) return false;
int? secondByte;
if (raw is List) {
if (raw.length >= 2) {
final s = raw[1];
if (s is int) {
secondByte = s;
} else if (s is num) {
secondByte = s.toInt();
} else if (s is String) {
final t = s.trim();
if (t.startsWith('0x') || t.startsWith('0X')) {
secondByte = int.tryParse(t.substring(2), radix: 16);
} else {
secondByte = int.tryParse(t);
}
}
}
} else if (raw is String) {
final normalized = raw.trim().replaceAll('[', '').replaceAll(']', '');
final parts =
normalized.split(RegExp(r'[,\s]+')).where((e) => e.isNotEmpty).toList();
if (parts.length >= 2) {
final t = parts[1].trim();
if (t.startsWith('0x') || t.startsWith('0X')) {
secondByte = int.tryParse(t.substring(2), radix: 16);
} else {
secondByte = int.tryParse(t);
}
}
} else if (raw is Map) {
final dynamic s = raw['second'] ?? raw['status2'] ?? raw['result'];
if (s is int) {
secondByte = s;
} else if (s is num) {
secondByte = s.toInt();
} else if (s is String) {
final t = s.trim();
if (t.startsWith('0x') || t.startsWith('0X')) {
secondByte = int.tryParse(t.substring(2), radix: 16);
} else {
secondByte = int.tryParse(t);
}
}
} else {
// 无法解析的类型
return false;
}
return secondByte != null && secondByte != 0;
}
/// 蓝牙设备信息缓存模型
class DeviceInfo {
final String address;
@ -305,7 +366,17 @@ class BleManager extends GetxService {
Logger.info('lxm--处理编解码控制命令');
// 处理通话状态
print(" CONTROL_CODEC: $commandId,$data");
//_isCall.value = data['callStatus'] as bool? ?? false;
final bool codecSuccess = _isCodecControlSuccess(data);
if (codecSuccess) {
print("lxm--编解码控制成功 (rawData 第二字节非 0)");
Logger.info('lxm--编解码控制成功 (rawData 第二字节非 0)');
} else {
print("lxm--编解码控制失败 (rawData 为 [162, 0] 或无法解析)");
Logger.warning('lxm--编解码控制失败 (rawData 为 [162, 0] 或无法解析)');
}
break;
default:
Logger.warning('lxm--未处理的命令ID: $commandId,数据: $data');

96
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCommandSender.kt

@ -183,8 +183,13 @@ class BleCommandSender {
}
/**
* 处理设备响应数据 - 从BleService移动过来
* @param data 响应数据
* 处理设备响应数据(统一兼容失败无长度的场景)
*
* 帧格式通常为: [HEADER(0)=0xBB][CMD(1)][LEN/STATUS(2)][DATA(len)][CRC(最后1字节)]
* - 当 LEN/STATUS 为 0(例如收到 0xA2, 0x00),表示该命令执行失败,直接回调失败并返回。
* - 正常响应时进行 CRC 校验(若帧长度不足则跳过校验并告警),拷贝数据使用安全长度以避免越界。
*
* @param data 设备返回的原始字节数组
*/
@SuppressLint("StaticFieldLeak")
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
@ -200,63 +205,64 @@ class BleCommandSender {
val header = data[0] // 0xBB
val cmdType = data[1].toInt() and 0xFF
// 判断命令失败响应(长度为0)
if (data.size == 4 && data[2].toInt() and 0xFF == 0) {
Log.w(TAG, "命令 0x${cmdType.toString(16)} 执行失败")
// 创建失败结果Map
val resultMap = mapOf(
"success" to false,
"command" to cmdType,
"rawData" to byteArrayOf()
)
callback?.notifyDeviceInfoReceived(cmdType, resultMap)
// 通知指令发送器处理响应
onCommandResponse(cmdType)
return
val lenByte = data[2].toInt() and 0xFF
// 正常响应处理:尽量进行 CRC 校验
var crcOk = true
if (data.size >= 4) {
val payload = data.sliceArray(0 until data.size - 1)
val calculatedCrc = calculateCrc8Maxim(payload, payload.size)
val receivedCrc = data[data.size - 1].toInt() and 0xFF
crcOk = (calculatedCrc.toInt() and 0xFF) == receivedCrc
if (!crcOk) {
Log.e(
TAG,
"设备响应CRC校验失败: 计算=${calculatedCrc.toInt() and 0xFF}, 接收=${receivedCrc}"
)
return
}
} else {
Log.w(TAG, "设备响应长度不足以包含CRC,跳过CRC校验")
}
// 统一失败判定
if (lenByte != data.size-4) {
Log.w(TAG, "命令 0x${cmdType.toString(16)} 执行失败(无数据长度)")
val resultMap = mapOf(
"success" to true,
"command" to cmdType,
"rawData" to byteArrayOf(data[2], data[data.size - 2]) // 取第二位和倒数第二位
)
callback?.notifyDeviceInfoReceived(cmdType, resultMap)
onCommandResponse(cmdType)
return
}
// 安全提取响应数据,避免越界
val actualPayloadLen = (data.size - 4).coerceAtLeast(0)
val copyLen = minOf(lenByte, actualPayloadLen)
val responseData = if (copyLen > 0) ByteArray(copyLen) else ByteArray(0)
// 正常响应处理
val length = data[2].toInt() and 0xFF
// // 检查数据长度
// if (data.size != length + 4) { // 帧头 + 命令 + 长度 + 数据 + CRC
// Log.e(TAG, "设备响应数据长度不匹配: 期望=${length + 4}, 实际=${data.size}")
// return
// }
// 计算CRC校验 - 修正:计算除最后一个CRC字节外的所有字节
val payload = data.sliceArray(0 until data.size - 1)
val calculatedCrc = calculateCrc8Maxim(payload, payload.size)
val receivedCrc = data[data.size - 1].toInt() and 0xFF
if (calculatedCrc.toInt() and 0xFF != receivedCrc) {
Log.e(
if (copyLen < lenByte) {
Log.w(
TAG,
"设备响应CRC校验失败: 计算=${calculatedCrc.toInt() and 0xFF}, 接收=${receivedCrc}"
"响应长度字段(${lenByte})与实际负载长度(${actualPayloadLen})不一致,按实际长度处理"
)
return
}
// 提取响应数据
val responseData = if (length > 0) ByteArray(length) else ByteArray(0)
if (length > 0) {
System.arraycopy(data, 3, responseData, 0, length)
if (copyLen > 0) {
System.arraycopy(data, 3, responseData, 0, copyLen)
}
Log.d(
TAG,
"收到设备响应: CMD=0x${cmdType.toString(16)}, 数据=${
responseData.joinToString(", ") {
"0x${
(it.toInt() and 0xFF).toString(16)
}"
}
responseData.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }
}"
)
// 通知指令发送器处理响应
onCommandResponse(cmdType)
// 根据命令类型处理不同的响应
when (cmdType) {
BleConst.CMD_GET_VERSION -> {
val resultMap = mutableMapOf<String, Any>(

Loading…
Cancel
Save