Merge branch 'feature/my' of http://1.95.137.212:57001/APP/FlutterApp into feature/my
This commit is contained in:
@@ -29,13 +29,16 @@ import '../../features/devices/data/datasources/path_http_datasource.dart';
|
||||
import '../../features/devices/data/repositories/device_hostriry_work_impl.dart';
|
||||
import '../../features/devices/data/repositories/device_repository_impl.dart';
|
||||
import '../../features/devices/data/repositories/generate_path_repository_Impl.dart';
|
||||
import '../../features/devices/data/repositories/route_planning_repository_impl.dart';
|
||||
import '../../features/devices/domain/repositories/device_hostrity_work_repository.dart';
|
||||
import '../../features/devices/domain/repositories/path_repository.dart';
|
||||
import '../../features/devices/domain/repositories/route_planning_repository.dart';
|
||||
import '../../features/devices/domain/usecases/delete_work_record_usecase.dart';
|
||||
import '../../features/devices/domain/usecases/device_work_hostrirty_usecase.dart';
|
||||
import '../../features/devices/domain/usecases/generate_path_usecase.dart';
|
||||
import '../../features/devices/domain/usecases/get_device_location_usecase.dart';
|
||||
import '../../features/devices/domain/usecases/get_work_record_usecase.dart';
|
||||
import '../../features/devices/domain/usecases/route_planning_usecase.dart';
|
||||
import '../../features/devices/domain/usecases/save_work_record_usecase.dart';
|
||||
import '../../features/devices/domain/usecases/select_work_record_usecase.dart';
|
||||
import '../../features/remote_control/presentation/bloc/remote_control_cubit.dart';
|
||||
@@ -90,6 +93,17 @@ Future<void> init() async {
|
||||
localTokenStorage: sl(), // 自动寻找已注册的 TokenStorage
|
||||
),
|
||||
);
|
||||
|
||||
sl.registerLazySingleton<RoutePlanningRepository>(
|
||||
() => RoutePlanningRepositoryImpl(tcp: sl<TcpClient>()), //
|
||||
);
|
||||
|
||||
sl.registerLazySingleton<RoutePlanningUseCase>(
|
||||
() => RoutePlanningUseCase(sl<RoutePlanningRepository>()), //
|
||||
);
|
||||
|
||||
|
||||
|
||||
//工作历史
|
||||
sl.registerLazySingleton<DeviceHostrityWorkRepositoryRepository>(
|
||||
() => DeviceHostrityWorkRepositoryImpl(
|
||||
@@ -114,11 +128,12 @@ Future<void> init() async {
|
||||
sl.registerLazySingleton<SaveWorkRecordUseCase>(() => SaveWorkRecordUseCase(sl<PathRepository>()));
|
||||
|
||||
|
||||
|
||||
/// 5. 状态管理 (Cubit/Bloc)
|
||||
sl.registerLazySingleton(() => AppUserCubit()); // AuthCubit 依赖它,必须先注册
|
||||
sl.registerLazySingleton(() => GetDeviceLocationUseCase(sl()));
|
||||
sl.registerLazySingleton(
|
||||
() => DevicesCubit(sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(),sl()),
|
||||
() => DevicesCubit(sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(), sl(),sl(),sl()),
|
||||
);
|
||||
sl.registerFactory(() => RemoteControlCubit(sl()));
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@ import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:maibu_satabot_v2/features/devices/data/repositories/route_planning_repository_impl.dart';
|
||||
|
||||
import '../../../features/devices/data/models/route_plan_send_entity.dart';
|
||||
import '../../../features/devices/domain/entities/gps_entity.dart';
|
||||
import '../../../features/devices/domain/entities/running_status_entity.dart';
|
||||
import '../protocol_decoder.dart';
|
||||
@@ -49,7 +51,7 @@ class TcpClient {
|
||||
rethrow; // 向上抛出连接异常
|
||||
}
|
||||
}
|
||||
|
||||
/// 发送数据
|
||||
void send(Map<String, dynamic> data) {
|
||||
if (_socket == null) throw Exception("Socket not connected");
|
||||
_socket!.write(jsonEncode(data));
|
||||
@@ -109,12 +111,25 @@ class TcpClient {
|
||||
_socket = null;
|
||||
_controller.close();
|
||||
debugPrint("TCP Disconnected");
|
||||
|
||||
}
|
||||
|
||||
void sendPathPoint(RoutePlanSendEntity routePlanSendEntity) {
|
||||
if (_socket == null) return;
|
||||
final payload = routePlanSendEntity.toBytes();
|
||||
final packet = sendRaw(0x01, payload); // 第1种的测试 声明指令结构和类型0x01 为命令类型
|
||||
//_socket!.add(payload);//第二种的测试
|
||||
}
|
||||
|
||||
|
||||
void sendDeviceStateChange(RoutePlanSendEntity routePlanSendEntity) {
|
||||
if (_socket == null) return;
|
||||
final payload = routePlanSendEntity.toBytes();
|
||||
_socket!.add(payload);
|
||||
// final packet = sendRaw(0x02, payload);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@ abstract class PathHttpDatasource {
|
||||
Future<List<DeviceAddPathPointModel>> generatePathRaw({
|
||||
required Map<String, dynamic> body,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
38
lib/features/devices/data/models/route_plan_send_entity.dart
Normal file
38
lib/features/devices/data/models/route_plan_send_entity.dart
Normal file
@@ -0,0 +1,38 @@
|
||||
// lib/features/devices/data/models/route_plan_send_entity.dart
|
||||
import 'dart:typed_data'; // ✅ 必须添加
|
||||
|
||||
class RoutePlanSendEntity {
|
||||
final int commandType;
|
||||
final int pointCounts;
|
||||
final double targetLatitude;
|
||||
final double targetLongitude;
|
||||
final int speed;
|
||||
|
||||
RoutePlanSendEntity({
|
||||
required this.commandType,
|
||||
required this.pointCounts,
|
||||
required this.targetLatitude,
|
||||
required this.targetLongitude,
|
||||
required this.speed,
|
||||
});
|
||||
|
||||
|
||||
List<int> toBytes() {
|
||||
final bytes = <int>[];
|
||||
bytes.add(commandType);
|
||||
bytes.addAll(_shortToBytes(pointCounts));
|
||||
bytes.addAll(_doubleToBytes(targetLatitude));
|
||||
bytes.addAll(_doubleToBytes(targetLongitude));
|
||||
bytes.addAll(_shortToBytes(speed));
|
||||
return bytes;
|
||||
}
|
||||
|
||||
List<int> _shortToBytes(int value) {
|
||||
return [(value >> 8) & 0xFF, value & 0xFF];
|
||||
}
|
||||
|
||||
List<int> _doubleToBytes(double value) {
|
||||
final byteData = ByteData(8)..setFloat64(0, value);
|
||||
return byteData.buffer.asUint8List();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:maibu_satabot_v2/features/devices/data/models/device_add_path_point_model.dart';
|
||||
import '../../../../core/network/tcp/tcp_client.dart';
|
||||
import '../../domain/repositories/route_planning_repository.dart';
|
||||
import '../models/route_plan_send_entity.dart';
|
||||
|
||||
class RoutePlanningRepositoryImpl implements RoutePlanningRepository {
|
||||
final TcpClient tcp;
|
||||
late PathPlanner _planner;
|
||||
|
||||
RoutePlanningRepositoryImpl({required this.tcp}) { // ✅ 加 {required} 和类型注解
|
||||
_planner = PathPlanner(tcp);
|
||||
}
|
||||
@override
|
||||
Future<void> startRoutePlanning(Queue<DeviceAddPathPointModel> locationQueue) async {
|
||||
// 直接传入 DeviceAddPathPointModel 列表(无需转换)
|
||||
final List<DeviceAddPathPointModel> locations = locationQueue.toList();
|
||||
_planner.startRoutePlanning(locations);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> pauseRPWork() async {
|
||||
// TODO: implement pauseRPWork
|
||||
_planner.pauseRPWork();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> resumeRPWork() async {
|
||||
// TODO: implement resumeRPWork
|
||||
_planner.resumeRPWork();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stopRoutePlanning() async {
|
||||
// TODO: implement stopRoutePlanning
|
||||
_planner.stopRoutePlanning();
|
||||
}
|
||||
}
|
||||
|
||||
// PathPlanner
|
||||
class PathPlanner {
|
||||
final Queue<DeviceAddPathPointModel> locationQueue = Queue();
|
||||
final TcpClient tcpClient;
|
||||
bool isStart = false;
|
||||
|
||||
PathPlanner(this.tcpClient);
|
||||
|
||||
void sendNextLocation() {
|
||||
if (isStart && locationQueue.isEmpty) {
|
||||
isStart = false;
|
||||
print("[track]");
|
||||
tcpClient.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
final entity = locationQueue.removeFirst(); // 类型:DeviceAddPathPointModel
|
||||
final routePlanSendEntity = RoutePlanSendEntity(
|
||||
commandType: 0x01,
|
||||
pointCounts: 1,
|
||||
targetLatitude: entity.latitude,
|
||||
targetLongitude: entity.longitude,
|
||||
speed: 1000,
|
||||
);
|
||||
|
||||
print("====Lat:${entity.latitude} ====Lng:${entity.longitude}");
|
||||
tcpClient.sendPathPoint( routePlanSendEntity);
|
||||
isStart = true;
|
||||
}
|
||||
/// 开始
|
||||
void startRoutePlanning(List<DeviceAddPathPointModel> locations) {
|
||||
locationQueue.addAll(locations);
|
||||
isStart = true;
|
||||
sendNextLocation();
|
||||
}
|
||||
/// 暂停
|
||||
void pauseRPWork() {
|
||||
var entity = new RoutePlanSendEntity(
|
||||
commandType: 0x02,
|
||||
pointCounts: 2,
|
||||
targetLatitude: 0,
|
||||
targetLongitude: 0,
|
||||
speed: 0,
|
||||
);
|
||||
tcpClient.sendDeviceStateChange(entity);
|
||||
//更新APP状态
|
||||
}
|
||||
/// 恢复
|
||||
void resumeRPWork() {
|
||||
var entity = new RoutePlanSendEntity(
|
||||
commandType: 0x02,
|
||||
pointCounts: 3,
|
||||
targetLatitude: 0,
|
||||
targetLongitude: 0,
|
||||
speed: 0,
|
||||
);
|
||||
tcpClient.sendDeviceStateChange(entity);
|
||||
//更新APP状态
|
||||
}
|
||||
/// 停止
|
||||
void stopRoutePlanning() {
|
||||
var entity = new RoutePlanSendEntity(
|
||||
commandType: 0x02,
|
||||
pointCounts: 0,
|
||||
targetLatitude: 0,
|
||||
targetLongitude: 0,
|
||||
speed: 0,
|
||||
);
|
||||
tcpClient.sendDeviceStateChange(entity);
|
||||
//更新APP状态
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// lib/features/devices/domain/repositories/route_planning_repository.dart
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import '../../data/models/device_add_path_point_model.dart';
|
||||
|
||||
|
||||
abstract class RoutePlanningRepository {
|
||||
/// Start
|
||||
Future<void> startRoutePlanning(Queue<DeviceAddPathPointModel> locationQueue);
|
||||
/// Stop
|
||||
Future<void> stopRoutePlanning();
|
||||
/// Pause
|
||||
Future<void> pauseRPWork();
|
||||
/// Resume
|
||||
Future<void> resumeRPWork();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import '../../data/models/device_add_path_point_model.dart';
|
||||
import '../../domain/repositories/route_planning_repository.dart';
|
||||
|
||||
class RoutePlanningUseCase {
|
||||
final RoutePlanningRepository repository;
|
||||
|
||||
RoutePlanningUseCase(this.repository);
|
||||
|
||||
Future<Either<RoutePlanningFailure, Unit>> startRoutePlanning(
|
||||
Queue<DeviceAddPathPointModel> locationQueue,
|
||||
) async {
|
||||
try {
|
||||
await repository.startRoutePlanning(locationQueue);
|
||||
return right(unit);
|
||||
} catch (e) {
|
||||
return left(RoutePlanningFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<Either<RoutePlanningFailure, Unit>> pauseRPWork() async {
|
||||
try {
|
||||
await repository.pauseRPWork();
|
||||
return right(unit);
|
||||
} catch (e) {
|
||||
return left(RoutePlanningFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<Either<RoutePlanningFailure, Unit>> resumeRPWork() async {
|
||||
try {
|
||||
await repository.resumeRPWork();
|
||||
return right(unit);
|
||||
} catch (e) {
|
||||
return left(RoutePlanningFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<Either<RoutePlanningFailure, Unit>> stopRoutePlanning() async {
|
||||
try {
|
||||
await repository.stopRoutePlanning();
|
||||
return right(unit);
|
||||
} catch (e) {
|
||||
return left(RoutePlanningFailure(message: e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Failure 类型
|
||||
class RoutePlanningFailure {
|
||||
final String? message;
|
||||
|
||||
RoutePlanningFailure({this.message});
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_entity.dart';
|
||||
@@ -9,10 +11,12 @@ import 'package:maibu_satabot_v2/features/devices/domain/usecases/select_work_re
|
||||
import 'package:maibu_satabot_v2/features/devices/domain/usecases/unbind_device_usecase.dart';
|
||||
import 'package:maibu_satabot_v2/features/devices/domain/usecases/update_devicename_usecase.dart';
|
||||
|
||||
import '../../data/models/device_add_path_point_model.dart';
|
||||
import '../../data/models/device_work_area_param_model.dart';
|
||||
import '../../domain/usecases/delete_work_record_usecase.dart';
|
||||
import '../../domain/usecases/get_device_location_usecase.dart';
|
||||
import '../../domain/usecases/get_work_record_usecase.dart';
|
||||
import '../../domain/usecases/route_planning_usecase.dart';
|
||||
import 'devices_state.dart';
|
||||
|
||||
class DevicesCubit extends Cubit<DevicesState> {
|
||||
@@ -26,6 +30,7 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
final SelectWorkRecordUseCase _selectWorkRecordUseCase;
|
||||
final SaveWorkRecordUseCase _saveWorkRecordUseCase;
|
||||
final GeneratePathUseCase _generatePathUseCase;
|
||||
final RoutePlanningUseCase _routePlanningUseCase; //
|
||||
|
||||
DevicesCubit(
|
||||
this.repository,
|
||||
@@ -38,6 +43,7 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
this._selectWorkRecordUseCase,
|
||||
this._saveWorkRecordUseCase,
|
||||
this._generatePathUseCase,
|
||||
this._routePlanningUseCase
|
||||
) : super(const DevicesState());
|
||||
|
||||
Future<void> unbindDevice(String deviceId, String deviceName) async {
|
||||
@@ -239,4 +245,60 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
|
||||
result.fold((failure) => emit(state.copyWith(errorMessage: failure.message)), (pathData) => emit(state.copyWith(generatedPath: pathData)));
|
||||
}
|
||||
// 开始路径规划
|
||||
Future<void> startRoutePlanning(Queue<DeviceAddPathPointModel> locationQueue) async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final result = await _routePlanningUseCase.startRoutePlanning(locationQueue);
|
||||
result.fold(
|
||||
(failure) => emit(state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: failure.message ?? '路径规划启动失败',
|
||||
)),
|
||||
(_) => emit(state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: '',
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
// 暂停
|
||||
Future<void> pauseRoutePlanning() async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final result = await _routePlanningUseCase.pauseRPWork();
|
||||
result.fold(
|
||||
(failure) => emit(state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: failure.message ?? '暂停失败',
|
||||
)),
|
||||
(_) => emit(state.copyWith(isLoading: false)),
|
||||
);
|
||||
}
|
||||
|
||||
// 恢复
|
||||
Future<void> resumeRoutePlanning() async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final result = await _routePlanningUseCase.resumeRPWork();
|
||||
result.fold(
|
||||
(failure) => emit(state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: failure.message ?? '恢复失败',
|
||||
)),
|
||||
(_) => emit(state.copyWith(isLoading: false)),
|
||||
);
|
||||
}
|
||||
|
||||
// 停止
|
||||
Future<void> stopRoutePlanning() async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final result = await _routePlanningUseCase.stopRoutePlanning();
|
||||
result.fold(
|
||||
(failure) => emit(state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: failure.message ?? '停止失败',
|
||||
)),
|
||||
(_) => emit(state.copyWith(isLoading: false)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,12 @@ enum DeviceOperationType {
|
||||
updateName, // 修改设备名称
|
||||
}
|
||||
|
||||
enum AppState {
|
||||
none, //无作业状态
|
||||
routePlanning, //正在规划路径
|
||||
}
|
||||
|
||||
|
||||
|
||||
class DevicesState extends Equatable {
|
||||
final List<DeviceEntity> devices; // 名下所有设备列表
|
||||
@@ -20,6 +26,7 @@ class DevicesState extends Equatable {
|
||||
final DeviceOperationType operationType; // 标记当前操作类型
|
||||
final List<Map<String, dynamic>>? pathData;
|
||||
final List<DeviceAddPathPointModel>? generatedPath; // 新增字段
|
||||
final AppState appState;
|
||||
|
||||
|
||||
|
||||
@@ -34,6 +41,7 @@ class DevicesState extends Equatable {
|
||||
this.operationType = DeviceOperationType.none, // 默认无操作
|
||||
this.pathData,
|
||||
this.generatedPath,
|
||||
this.appState = AppState.none,
|
||||
});
|
||||
|
||||
// 使用 copyWith 方便局部更新状态
|
||||
|
||||
Reference in New Issue
Block a user