完成路径规划发送的握手机制-一对一校验应答-待测试

完成路径规划握手机制的开关功能。
This commit is contained in:
2026-03-17 14:19:41 +08:00
parent d5cde33b3e
commit f31a20f1d9
5 changed files with 364 additions and 9 deletions

20
lib/core/env/path_planning_config.dart vendored Normal file
View File

@@ -0,0 +1,20 @@
/// 路径规划配置类
/// 用于控制路径规划功能的各种开关和配置
class PathPlanningConfig {
/// 🔥 全局开关:控制使用哪套路径规划逻辑
/// - `false`: 使用现有逻辑(不等待 ACK 确认)
/// - `true`: 使用新的 ACK 握手机制(等待 0x02 和 0x01 确认)
static bool useAckHandshake = false;
/// 切换路径规划模式
/// [useAck] true = 使用 ACK 握手机制,false = 使用现有逻辑
static void setMode(bool useAck) {
useAckHandshake = useAck;
print('🔧 [PathPlanningConfig] 路径规划模式已切换:${useAck ? "ACK 握手机制" : "现有逻辑"}');
}
/// 获取当前模式描述
static String getModeDescription() {
return useAckHandshake ? "ACK 握手机制 (等待 0x02 + 0x01)" : "现有逻辑 (仅等待 0x01)";
}
}

View File

@@ -1,12 +1,14 @@
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:maibu_satabot_v2/core/logging/i_logger_service.dart';
import 'package:maibu_satabot_v2/features/devices/data/models/device_add_path_point_model.dart';
import '../../features/devices/domain/repositories/route_planning_repository.dart';
import '../../features/devices/presentation/bloc/devices_cubit.dart';
import '../../features/devices/services/path_planning_service.dart';
import '../di/injection.dart';
import '../env/path_planning_config.dart';
import 'protocol_decoder.dart';
import 'tcp/tcp_client.dart';
// import '../../features/auth/data/models/user_model.dart';
@@ -15,9 +17,23 @@ class NetMessageDispatcher {
final TcpClient tcpClient;
final PathPlanningService _pathPlanningService;
final RoutePlanningRepository routePlanningRepository;
// 🔥 修改:使用回调函数获取 AppState
// 修改:使用回调函数获取 AppState
final Function? getAppState;
// 🔥 新增:ACK 握手状态管理 (仅新模式使用)
int _expectedPointIndex = -1; // 期望收到的点编号
bool _waitingForAck = false; // 是否正在等待 ACK 确认
bool _waitingForArrived = false; // 是否正在等待到达确认
int _currentPointIndex = -1; // 当前发送的点索引
// 🔥 新增:设置期望收到的点编号(供 PathPlanner 调用)
void setExpectedPointIndex(int index) {
_expectedPointIndex = index;
debugPrint('🎯 [Dispatcher-ACK] 设置期望点编号:$index');
}
NetMessageDispatcher(this.tcpClient, this.routePlanningRepository, this._pathPlanningService, {this.getAppState});
/// 过滤特定指令的流
@@ -79,7 +95,7 @@ class NetMessageDispatcher {
/// 协议格式:AB AA 01 [DATA] CRC(2) AA AB
/// 验证头部:data[0]=0xAB, data[1]=0xAA, data[2]=0x01
/// 检查状态位:data[5] == 0x01 表示回复成功
Stream<RawPacket> onPathPlanningResponse() {
/* Stream<RawPacket> onPathPlanningResponse() {
final devicesCubit = sl<DevicesCubit>();
debugPrint('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x01)');
@@ -168,13 +184,118 @@ class NetMessageDispatcher {
return false;
});
}*/
///新版的ack和现有的融合
/// 监听路径规划指令应答 (现有逻辑 - 不修改)
/// 协议格式:AB AA 01 [DATA] CRC(2) AA AB
/// 验证头部:data[0]=0xAB, data[1]=0xAA, data[2]=0x01
/// 检查状态位:data[5] == 0x01 表示回复成功
Stream<RawPacket> onPathPlanningResponse() {
final devicesCubit = sl<DevicesCubit>();
debugPrint('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x01) - 模式:${PathPlanningConfig.getModeDescription()}');
// 🔥 根据配置决定使用哪个流
if (PathPlanningConfig.useAckHandshake) {
debugPrint('⚙️ [Dispatcher] 使用 ACK 握手机制');
return onPathPlanningResponseWithAck();
} else {
debugPrint('⚙️ [Dispatcher] 使用现有逻辑');
return onCommand(0x01).where((packet) {
// 🔥 修改:使用回调检查 AppState
if (getAppState != null) {
final currentState = getAppState!();
if (currentState.toString() == 'AppState.none') {
debugPrint('⚠️ [Dispatcher] 当前 AppState 为 none,停止处理路径规划指令');
return false;
}
}
// 构造完整数据包用于验证
final fullData = <int>[
0xAB, 0xAA, 0x01,
...packet.payload,
0x00, 0x00, // CRC 占位
0xAA, 0xAB
];
// 验证头部协议
if (fullData[0] == 0xAB &&
fullData[1] == 0xAA &&
fullData[2] == 0x01) {
debugPrint('[Dispatcher] 下位机回复成功 - 完整数据包:${fullData.join(" ")}');
// 检查状态位 (索引 5 对应 payload 的第 2 个字节)
if (fullData.length > 5 && fullData[5] == 0x01) {
debugPrint('✅ [Dispatcher] 状态位验证通过:0x01 - 可以发送下一个指令');
///取全局的路径规划发送实体 queue
/// 发送下一个指令 移除上一条数据
var queue = _pathPlanningService.getQueue();
// 🔥 关键:必须先检查空队列!
if (queue.isEmpty) {
devicesCubit.finishWork();
Future.delayed(Duration(seconds: 1), () {
devicesCubit.resetWorkStatus();
});
// 手动停止
//routePlanningRepository.stopRoutePlanning();
debugPrint('⚠️ [Dispatcher] 队列已经为空,跳过本次响应处理');
return false;
}
///更新完成 jwd
var deviceAddPathPointModel = queue.first;
var d = deviceAddPathPointModel.latitude;
var e = deviceAddPathPointModel.longitude;
devicesCubit.setArrivedLocation(
deviceAddPathPointModel.latitude,
deviceAddPathPointModel.longitude
);
debugPrint("准确更新完成的路径点经纬度,$d,$e");
//去除上一条数据
queue.removeFirst();
/// _pathPlanningService.updateQueue(queue as List<DeviceAddPathPointModel>);
_pathPlanningService.updateQueue(queue.toList());
DateTime now = DateTime.now();
String timeStr = "${now.hour.toString().padLeft(2, '0')}:"
"${now.minute.toString().padLeft(2, '0')}:"
"${now.second.toString().padLeft(2, '0')}";
debugPrint(' $timeStr [Dispatcher] 准备触发下一个路径点发送,队列剩余:${queue.length}');
///如果还剩 0 个数据 就停止发送
if (queue.length==0) {
debugPrint('⚠️ [Dispatcher] 队列已空,停止发送');
// final devicesCubit = sl<DevicesCubit>();
devicesCubit.finishWork();
Future.delayed(Duration(seconds: 1), () {
devicesCubit.resetWorkStatus();
});
// 手动停止
routePlanningRepository.stopRoutePlanning();
debugPrint('⚠️ [Dispatcher] 队列已空,发送停止指令已发送');
return false;
}
routePlanningRepository.startRoutePlanning(queue);
return true;
} else {
debugPrint('⚠️ [Dispatcher] 状态位异常:${fullData.length > 5 ? fullData[5].toRadixString(16) : "N/A"}');
}
} else {
debugPrint('❌ [Dispatcher] 协议头验证失败');
}
return false;
});
}
}
/// 监听特定状态的回复(可复用)
/// [commandCode] 指令码
/// [statusIndex] 状态字节在 payload 中的索引
/// [expectedStatus] 期望的状态值
Stream<RawPacket> onStatusResponse({
/* Stream<RawPacket> onStatusResponse({
required int commandCode,
required int statusIndex,
required int expectedStatus,
@@ -195,5 +316,138 @@ class NetMessageDispatcher {
}
return false;
});
}*/
/// 监听特定状态的回复(ack握手机制)
// 🔥 新增:ACK 握手机制 (新模式)
/// 监听路径规划指令应答,实现完整的 ACK 握手机制
/// 协议格式:AB AA 01 [DATA] CRC(2) AA AB
/// 上传协议(0x01 路径规划上传):
/// - Byte 3: pointCounts (点编号)
/// - Byte 4: 未使用
/// - Byte 5: 状态
/// - 0x01: 已到达
/// - 0x02: 收到指令
Stream<RawPacket> onPathPlanningResponseWithAck() {
final devicesCubit = sl<DevicesCubit>();
debugPrint('[Dispatcher-ACK] 开始监听路径规划指令应答 (CMD: 0x01) - ACK 握手机制已启用');
return onCommand(0x01).where((packet) {
// 修改:使用回调检查 AppState
if (getAppState != null) {
final currentState = getAppState!();
if (currentState.toString() == 'AppState.none') {
debugPrint('️ [Dispatcher-ACK] 当前 AppState 为 none,停止处理路径规划指令');
return false;
}
}
// 🔥 构造完整数据包用于验证
final fullData = <int>[
0xAB, 0xAA, 0x01,
...packet.payload,
0x00, 0x00, // CRC 占位
0xAA, 0xAB
];
// 验证头部协议
if (fullData[0] == 0xAB &&
fullData[1] == 0xAA &&
fullData[2] == 0x01) {
debugPrint('[Dispatcher-ACK] 收到路径规划应答 - 完整数据包:${fullData.join(" ")}');
// 🔥 检查数据包长度是否足够
if (fullData.length < 6) {
debugPrint('❌ [Dispatcher-ACK] 数据包长度不足,无法解析状态位');
return false;
}
final status = fullData[5];
final pointIndex = fullData.length > 3 ? fullData[3] : -1;
debugPrint('📊 [Dispatcher-ACK] 状态位:0x${status.toRadixString(16)}, 点编号:$pointIndex, 期望编号:$_expectedPointIndex');
// 🔥 核心改进:精准匹配点编号
// 如果正在等待 ACK,但收到的点编号不匹配,直接丢弃
if ((_waitingForAck || _waitingForArrived) && pointIndex != _expectedPointIndex) {
debugPrint('⚠️ [Dispatcher-ACK] 点编号不匹配!收到:$pointIndex, 期望:$_expectedPointIndex,丢弃');
return false;
}
// 🔥 ACK 握手机制核心逻辑
if (status == 0x02) {
// ✅ 下位机回复"收到指令"
debugPrint('✅ [Dispatcher-ACK] ACK 确认收到 - 点编号:$pointIndex');
_waitingForAck = false;
_waitingForArrived = true; // 开始等待"已到达"信号
return false; // 继续监听,不返回给流
}
else if (status == 0x01) {
// ✅ 下位机回复"已到达"
debugPrint('✅ [Dispatcher-ACK] 已到达确认 - 点编号:$pointIndex');
_waitingForArrived = false;
// 🔥 关键:处理完成后的队列操作
final queue = _pathPlanningService.getQueue();
if (queue.isEmpty) {
devicesCubit.finishWork();
Future.delayed(Duration(seconds: 1), () {
devicesCubit.resetWorkStatus();
});
debugPrint('⚠️ [Dispatcher-ACK] 队列已经为空,作业完成');
return false;
}
// 更新已完成的经纬度
var deviceAddPathPointModel = queue.first;
var d = deviceAddPathPointModel.latitude;
var e = deviceAddPathPointModel.longitude;
devicesCubit.setArrivedLocation(
deviceAddPathPointModel.latitude,
deviceAddPathPointModel.longitude
);
debugPrint(" 准确更新完成的路径点经纬度:$d, $e");
// 移除已完成的点
queue.removeFirst();
_pathPlanningService.updateQueue(queue.toList());
DateTime now = DateTime.now();
String timeStr = "${now.hour.toString().padLeft(2, '0')}:"
"${now.minute.toString().padLeft(2, '0')}:"
"${now.second.toString().padLeft(2, '0')}";
debugPrint(' $timeStr [Dispatcher-ACK] 准备触发下一个路径点,队列剩余:${queue.length}');
// 🔥 如果队列已空,停止发送
if (queue.isEmpty) {
debugPrint('️ [Dispatcher-ACK] 队列已空,停止发送');
devicesCubit.finishWork();
Future.delayed(Duration(seconds: 1), () {
devicesCubit.resetWorkStatus();
});
routePlanningRepository.stopRoutePlanning();
debugPrint('⚠️ [Dispatcher-ACK] 停止指令已发送');
return false;
}
// 🔥 发送下一个点(触发 ACK 握手循环)
routePlanningRepository.startRoutePlanning(queue);
return false; // 不返回给流,避免重复处理
}
else {
debugPrint('⚠️ [Dispatcher-ACK] 未知状态位:0x${status.toRadixString(16)}');
return false;
}
} else {
debugPrint(' [Dispatcher-ACK] 协议头验证失败');
return false;
}
});
}
}

View File

@@ -1,6 +1,10 @@
import 'dart:collection';
import 'package:maibu_satabot_v2/core/logging/i_logger_service.dart';
import 'package:maibu_satabot_v2/features/devices/data/models/device_add_path_point_model.dart';
import '../../../../core/di/injection.dart';
import '../../../../core/env/path_planning_config.dart';
import '../../../../core/network/net_message_dispatcher.dart';
import '../../../../core/network/tcp/tcp_client.dart';
import '../../domain/repositories/route_planning_repository.dart';
import '../models/route_plan_send_entity.dart';
@@ -8,6 +12,7 @@ import '../models/route_plan_send_entity.dart';
class RoutePlanningRepositoryImpl implements RoutePlanningRepository {
final TcpClient tcp;
late PathPlanner _planner;
// 🔥 新增:存储 AppState 的引用
dynamic _appState;
@@ -50,15 +55,26 @@ class RoutePlanningRepositoryImpl implements RoutePlanningRepository {
}
}
// 全局开关:控制使用哪套路径规划逻辑
// true = 使用新的 ACK 握手机制 (将来)
// false = 使用现有逻辑 (现在)
class PathPlanningMode {
static bool useAckHandshake = false; // 默认使用现有逻辑
}
// PathPlanner
class PathPlanner {
final Queue<DeviceAddPathPointModel> locationQueue = Queue();
final TcpClient tcpClient;
bool isStart = false;
// 🔥 新增:作业控制状态
// 新增:作业控制状态
bool _isPaused = false;
bool _isStopped = false;
// 新增:ACK 握手状态 (仅新模式使用)
bool _waitingForAck = false; // 是否正在等待 ACK 确认
int _currentPointIndex = 0; // 🔥 当前发送的点编号(从 0 开始递增)
PathPlanner(this.tcpClient);
@@ -70,7 +86,7 @@ class PathPlanner {
// tcpClient.disconnect();
// return;
// }
// 🔥 关键检查:暂停或停止时不再发送
// 关键检查:暂停或停止时不再发送
if (_isPaused) {
print("⏸️ 当前处于暂停状态,停止发送指令");
return;
@@ -136,7 +152,15 @@ class PathPlanner {
return;
}
print("[发送指令要转换类型了完成]");
sendNextLocation();
// sendNextLocation();
// 🔥 根据全局开关选择使用哪套发送逻辑
if (PathPlanningConfig.useAckHandshake) {
print("🔥 使用 ACK 握手机制发送路径点");
sendNextLocationWithAck(sl<NetMessageDispatcher>());
} else {
print("🔥 使用现有逻辑发送路径点");
sendNextLocation();
}
}
/// 暂停
@@ -170,9 +194,17 @@ class PathPlanner {
tcpClient.sendDeviceStateChange(entity);
// 恢复后继续发送下一个点
// if (locationQueue.isNotEmpty) {
// print("📍 恢复发送下一个路径点");
// sendNextLocation();
// }
if (locationQueue.isNotEmpty) {
print("📍 恢复发送下一个路径点");
sendNextLocation();
if (PathPlanningConfig.useAckHandshake) {
sendNextLocationWithAck(sl<NetMessageDispatcher>());
} else {
sendNextLocation();
}
}
}
/// 停止
@@ -199,7 +231,55 @@ class PathPlanner {
tcpClient.sendDeviceStateChange(entity);
}
// 🔥 新增:ACK 握手机制的发送方法 (新模式)
void sendNextLocationWithAck(NetMessageDispatcher dispatcher) {
print("[在发送指令 sendNextLocationWithAck 方法中 - ACK 模式]");
// 🔥 关键检查:如果正在等待 ACK,不要重复发送
if (_waitingForAck) {
print("⏳ 正在等待 ACK 确认,跳过发送");
return;
}
// 🔥 关键检查:暂停或停止时不再发送
if (_isPaused) {
print("⏸️ 当前处于暂停状态,停止发送指令");
return;
}
if (_isStopped || (isStart && locationQueue.isEmpty)) {
isStart = false;
if (_isStopped) {
print("️ 已停止作业,清空队列");
locationQueue.clear(); // 清空剩余队列
} else {
print("[路径点发送完毕]");
}
_isStopped = false; // 重置停止标志
return;
}
final entity = locationQueue.removeFirst(); // 类型:DeviceAddPathPointModel
// 🔥 核心改进:使用递增的点编号,确保精准匹配
final pointIndex = _currentPointIndex++;
final routePlanSendEntity = RoutePlanSendEntity(
commandType: 0x01,
pointCounts: pointIndex, // 🔥 使用点编号,而不是固定的 1
targetLatitude: entity.latitude,
targetLongitude: entity.longitude,
speed: 1000,
);
print("====Lat:${entity.latitude} ====Lng:${entity.longitude}");
tcpClient.sendPathPoint( routePlanSendEntity);
isStart = true;
// 🔥 关键:标记为正在等待 ACK
_waitingForAck = true;
dispatcher.setExpectedPointIndex(pointIndex); // 🔥 通知 Dispatcher
print(" 已锁定发送,等待 ACK 确认 (0x02)...");
}
}

View File

@@ -70,7 +70,7 @@ class CenterControlArea extends StatelessWidget {
width: expandedWidth,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [_buildSliderBox(expandedWidth, "备用", false)],
children: [_buildSliderBox(expandedWidth, "割刀", false)],
),
),
],

View File

@@ -72,7 +72,8 @@ class MyApp extends StatelessWidget {
),
// 其他 Cubit...
],
child: MaterialApp.router(title: 'Maibu Satabot', theme: AppTheme.lightTheme, routerConfig: sl<GoRouter>(),
child: MaterialApp.router(title: 'Maibu Satabot',
theme: AppTheme.lightTheme, routerConfig: sl<GoRouter>(),
builder: (context, child) {
return _LifecycleListener(child: child);
},),