蓝牙增强+个更换绑定逻辑接口
This commit is contained in:
@@ -68,27 +68,38 @@ class BleProtocolDecoder {
|
||||
static BleDecodedResult _decodeStatusInfo(Uint8List data, String hex) {
|
||||
final fields = <BleField>[];
|
||||
|
||||
// 🔥 关键修复:协议帧为 AB AA 02 [24个逗号字段] CRC_lo CRC_hi AA AB。
|
||||
// ProtocolParser 只剥离了帧尾 AA AB,payload 末尾仍带 2 字节 CRC16(小端)。
|
||||
// 障碍物标志位是最后一个字段(fields[23]),若不先去掉 CRC,
|
||||
// 这 2 字节会粘到障碍物字段上导致乱码。此处先剥离尾部 2 字节 CRC。
|
||||
final Uint8List body =
|
||||
data.length > 2 ? Uint8List.sublistView(data, 0, data.length - 2) : data;
|
||||
|
||||
try {
|
||||
final text = utf8.decode(data);
|
||||
// allowMalformed 兜底:非法字节不再抛异常、不再落入 latin1 造成整段乱码
|
||||
var text = utf8.decode(body, allowMalformed: true);
|
||||
// 去掉控制字符(\0 \r \n 等)与替换符(U+FFFD):固件常在串尾补 \0,
|
||||
// 或残留 CRC 被解码成非法字符,粘在最后一个字段(障碍物)上造成乱码
|
||||
text = text.replaceAll(RegExp(r'[\u0000-\u001F\uFFFD]'), '');
|
||||
if (text.contains(',')) {
|
||||
final parts = text.split(',');
|
||||
return BleDecodedResult(fields: _parseStatusFields(parts), rawHex: hex);
|
||||
}
|
||||
} catch (e) {
|
||||
try {
|
||||
final text = latin1.decode(data);
|
||||
if (text.contains(',')) {
|
||||
final parts = text.split(',');
|
||||
return BleDecodedResult(
|
||||
fields: _parseStatusFields(parts),
|
||||
rawHex: hex,
|
||||
// 诊断:仅当控制模式槽位(parts[20])出现协议外码(如6)时,打印全字段索引对齐,
|
||||
// 用于判断是固件扩展码还是字段错位;正常包不打印,避免刷屏
|
||||
if (parts.length > 20 &&
|
||||
!const ['0', '1', '2', '3', '4'].contains(parts[20].trim())) {
|
||||
developer.log(
|
||||
'[BLE-0x02] 字段对齐诊断: ' +
|
||||
List.generate(parts.length, (i) => '[$i]=${parts[i]}')
|
||||
.join(' '),
|
||||
name: 'BleProtocolDecoder',
|
||||
);
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
return BleDecodedResult(fields: _parseStatusFields(parts), rawHex: hex);
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
if (data.isNotEmpty) {
|
||||
final status = data[0];
|
||||
if (body.isNotEmpty) {
|
||||
final status = body[0];
|
||||
fields.add(
|
||||
BleField(
|
||||
label: '状态码',
|
||||
@@ -96,8 +107,8 @@ class BleProtocolDecoder {
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.length >= 2) {
|
||||
final mode = data[1];
|
||||
if (body.length >= 2) {
|
||||
final mode = body[1];
|
||||
const modes = {0x00: '待机', 0x01: '遥控', 0x02: '自动', 0x03: '急停'};
|
||||
fields.add(
|
||||
BleField(
|
||||
@@ -106,10 +117,10 @@ class BleProtocolDecoder {
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.length >= 3)
|
||||
fields.add(BleField(label: '电量', value: '${data[2]}%'));
|
||||
if (data.length >= 4) {
|
||||
final fault = data[3];
|
||||
if (body.length >= 3)
|
||||
fields.add(BleField(label: '电量', value: '${body[2]}%'));
|
||||
if (body.length >= 4) {
|
||||
final fault = body[3];
|
||||
fields.add(
|
||||
BleField(
|
||||
label: '故障状态',
|
||||
@@ -119,13 +130,13 @@ class BleProtocolDecoder {
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.length >= 6) {
|
||||
final speed = (data[5] << 8) | data[4];
|
||||
if (body.length >= 6) {
|
||||
final speed = (body[5] << 8) | body[4];
|
||||
fields.add(BleField(label: '速度', value: '$speed'));
|
||||
}
|
||||
if (data.length > 8) {
|
||||
if (body.length > 8) {
|
||||
try {
|
||||
final text = String.fromCharCodes(data.sublist(8));
|
||||
final text = String.fromCharCodes(body.sublist(8));
|
||||
if (text.isNotEmpty && !text.contains('\x00')) {
|
||||
fields.add(BleField(label: '附加', value: text));
|
||||
}
|
||||
@@ -141,24 +152,32 @@ class BleProtocolDecoder {
|
||||
parts.add('');
|
||||
}
|
||||
|
||||
// 协议定义 控制模式: 0无控制 1本地遥控 2蓝牙 3TCP 4其他接口;
|
||||
// 先按整数解析(容忍前后空格),有映射的一律显示对应文字;
|
||||
// 超出协议范围的码(如固件扩展值)显示为 未知(n),避免被误当成有效模式
|
||||
String controlModeText(String v) {
|
||||
return switch (v) {
|
||||
'0' => '待机',
|
||||
'1' => '遥控',
|
||||
'2' => '自动',
|
||||
'3' => '急停',
|
||||
_ => v.isNotEmpty ? v : '--',
|
||||
final n = int.tryParse(v.trim());
|
||||
if (n == null) return v.isEmpty ? '--' : v;
|
||||
return switch (n) {
|
||||
0 => '无控制',
|
||||
1 => '本地遥控',
|
||||
2 => '蓝牙',
|
||||
3 => 'TCP',
|
||||
4 => '其他接口',
|
||||
_ => '未知($n)',
|
||||
};
|
||||
}
|
||||
|
||||
// 协议定义 定位质量: 0无效 1GPS单点 2DGPS差分/SBAS 4RTK固定解 5RTK浮点解 7手动输入
|
||||
String qualText(String v) {
|
||||
final q = int.tryParse(v) ?? -1;
|
||||
return switch (q) {
|
||||
0 => '无效',
|
||||
1 => '单点定位',
|
||||
2 => '差分定位',
|
||||
4 => '固定解',
|
||||
5 => '浮点解',
|
||||
1 => 'GPS 单点定位',
|
||||
2 => 'DGPS 差分/SBAS',
|
||||
4 => 'RTK 固定解',
|
||||
5 => 'RTK 浮点解',
|
||||
7 => '手动输入模式',
|
||||
_ => v.isNotEmpty ? v : '--',
|
||||
};
|
||||
}
|
||||
@@ -173,11 +192,14 @@ class BleProtocolDecoder {
|
||||
}
|
||||
|
||||
String obstacleText(String v) {
|
||||
final o = int.tryParse(v) ?? -1;
|
||||
return switch (o) {
|
||||
// 障碍物标志位为单个数字 0/1;字段尾部可能粘有 CRC/\0 等杂质,
|
||||
// 取第一个数字字符作为标志,无数字则显示 '--',杜绝杂质乱码
|
||||
final d = RegExp(r'[0-9]').firstMatch(v);
|
||||
if (d == null) return '--';
|
||||
return switch (int.parse(d.group(0)!)) {
|
||||
0 => '无障碍',
|
||||
1 => '有障碍',
|
||||
_ => v.isNotEmpty ? v : '--',
|
||||
_ => v,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ import '../../features/v2/device_list/domain/repositories/device_repository.dart
|
||||
import '../../features/v2/device_list/domain/usecases/get_device_status_data_usecase.dart'
|
||||
as device_v2_usecase;
|
||||
import '../../features/v2/device_list/domain/usecases/get_all_devices_usecase.dart';
|
||||
import '../../features/v2/device_list/domain/usecases/get_device_by_spilt_code_usecase.dart';
|
||||
import '../../features/v2/device_list/presentation/bloc/device_status_bloc.dart'
|
||||
as device_v2_bloc;
|
||||
import '../../features/v2/device_list/data/datasources/drone_station_datasource.dart';
|
||||
@@ -380,6 +381,9 @@ Future<void> init() async {
|
||||
sl.registerLazySingleton<GetAllDevicesUseCase>(
|
||||
() => GetAllDevicesUseCase(repository: sl()),
|
||||
);
|
||||
sl.registerLazySingleton<GetDeviceBySpiltCodeUseCase>(
|
||||
() => GetDeviceBySpiltCodeUseCase(repository: sl()),
|
||||
);
|
||||
sl.registerFactory<device_v2_bloc.DeviceStatusBloc>(
|
||||
() => device_v2_bloc.DeviceStatusBloc(sl()),
|
||||
);
|
||||
|
||||
2
lib/core/env/env_config.dart
vendored
2
lib/core/env/env_config.dart
vendored
@@ -1,7 +1,7 @@
|
||||
class EnvConfig {
|
||||
static const String environment = String.fromEnvironment(
|
||||
'ENV',
|
||||
defaultValue: 'test',
|
||||
defaultValue: 'prod',
|
||||
);
|
||||
|
||||
static String get sentryDsn {
|
||||
|
||||
Reference in New Issue
Block a user