孔洞逻辑完成
This commit is contained in:
@@ -19,22 +19,24 @@ class PathHttpDatasourceImpl implements PathHttpDatasource {
|
||||
// 1. 先打印基础信息
|
||||
print('=== 最终发送给后端的请求体 ===');
|
||||
print('请求体类型:${body.runtimeType}');
|
||||
print('是否包含holes:${body.containsKey('holes')}, holes长度:${(body['holes'] as List).length}');
|
||||
|
||||
// 2. 格式化打印JSON(带缩进,清晰展示嵌套结构)
|
||||
final jsonString = const JsonEncoder.withIndent(' ').convert(body);
|
||||
print('完整JSON请求体:\n$jsonString');
|
||||
|
||||
final _jsonString = jsonEncode(body);
|
||||
print('完整JSON请求体:\n$jsonString');
|
||||
|
||||
// 3. 可选:单独打印holes的JSON(重点关注)
|
||||
final holesJson = body['holes'] as List;
|
||||
print('=== 单独打印holes的JSON ===');
|
||||
if (holesJson.isEmpty) {
|
||||
print('holes 为空数组');
|
||||
} else {
|
||||
for (int i = 0; i < holesJson.length; i++) {
|
||||
print('第${i + 1}组hole:\n${const JsonEncoder.withIndent(' ').convert(holesJson[i])}');
|
||||
}
|
||||
}
|
||||
//final holesJson = body['holes'] as List;
|
||||
//print('=== 单独打印holes的JSON ===');
|
||||
//if (holesJson.isEmpty) {
|
||||
// print('holes 为空数组');
|
||||
//} else {
|
||||
// for (int i = 0; i < holesJson.length; i++) {
|
||||
// print('第${i + 1}组hole:\n${const JsonEncoder.withIndent(' ').convert(holesJson[i])}');
|
||||
// }
|
||||
//}
|
||||
} catch (e) {
|
||||
// 防止JSON序列化失败导致崩溃
|
||||
print('打印请求体失败:$e');
|
||||
|
||||
@@ -4,10 +4,7 @@ class ReferencePoint {
|
||||
|
||||
ReferencePoint({required this.lat, required this.lon});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'lat': lat,
|
||||
'lon': lon,
|
||||
};
|
||||
Map<String, dynamic> toJson() => {'lat': lat, 'lon': lon};
|
||||
}
|
||||
|
||||
class OuterBoundary {
|
||||
@@ -16,22 +13,55 @@ class OuterBoundary {
|
||||
|
||||
OuterBoundary({required this.position, required this.sideWidth});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'position': position.map((p) => p.toJson()).toList(),
|
||||
'sideWidth': sideWidth,
|
||||
};
|
||||
Map<String, dynamic> toJson() => {'position': position.map((p) => p.toJson()).toList(), 'sideWidth': sideWidth};
|
||||
}
|
||||
|
||||
class HoleBoundary {
|
||||
final List<Position> position;
|
||||
// 核心修改:position 改为二维列表(List<List<Position>>)
|
||||
final List<List<Position>> position;
|
||||
final double sideWidth;
|
||||
|
||||
HoleBoundary({required this.position, required this.sideWidth});
|
||||
// 构造函数:支持传入一维/二维Position列表,自动适配格式
|
||||
HoleBoundary({
|
||||
required dynamic position, // 兼容一维/二维输入
|
||||
required this.sideWidth,
|
||||
}) : position = _normalizePosition(position);
|
||||
|
||||
// 辅助方法:将任意格式的position转为标准二维数组
|
||||
static List<List<Position>> _normalizePosition(dynamic input) {
|
||||
if (input is List<List<Position>>) {
|
||||
// 已经是二维数组,直接返回
|
||||
return input;
|
||||
} else if (input is List<Position>) {
|
||||
// 一维数组,包装成二维数组 [[p1,p2,p3]]
|
||||
return [input];
|
||||
} else {
|
||||
throw ArgumentError('position 必须是 List<Position> 或 List<List<Position>> 类型');
|
||||
}
|
||||
}
|
||||
|
||||
// 核心修改:toJson 输出二维数组格式
|
||||
Map<String, dynamic> toJson() => {
|
||||
'position': position.map((p) => p.toJson()).toList(),
|
||||
'position': position.map((innerList) => innerList.map((p) => p.toJson()).toList()).toList(), // 二维数组:[[{lat,lon}], ...]
|
||||
'sideWidth': sideWidth,
|
||||
};
|
||||
|
||||
// 可选:从JSON反序列化(方便解析返回值)
|
||||
factory HoleBoundary.fromJson(Map<String, dynamic> json) {
|
||||
// 1. 提取position数组(分步处理,更易读)
|
||||
final List<dynamic> outerList = json['position'] as List;
|
||||
// 2. 遍历外层数组,将每个内层数组转为 List<Position>
|
||||
final List<List<Position>> position = outerList.map((innerList) {
|
||||
// 3. 处理内层数组,转为 Position 对象列表
|
||||
return (innerList as List).map((p) => Position.fromJson(p as Map<String, dynamic>)).toList();
|
||||
}).toList();
|
||||
|
||||
// 4. 提取sideWidth(确保数字类型)
|
||||
final double sideWidth = (json['sideWidth'] as num).toDouble();
|
||||
|
||||
// 5. 返回实例
|
||||
return HoleBoundary(position: position, sideWidth: sideWidth);
|
||||
}
|
||||
}
|
||||
|
||||
class Position {
|
||||
@@ -39,9 +69,12 @@ class Position {
|
||||
final double lon;
|
||||
|
||||
Position({required this.lat, required this.lon});
|
||||
factory Position.fromJson(Map<String, dynamic> json) {
|
||||
// 安全解析经纬度,避免空值/非数字
|
||||
final double lat = (json['lat'] as num?)?.toDouble() ?? 0.0;
|
||||
final double lon = (json['lon'] as num?)?.toDouble() ?? 0.0;
|
||||
return Position(lat: lat, lon: lon);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'lat': lat,
|
||||
'lon': lon,
|
||||
};
|
||||
Map<String, dynamic> toJson() => {'lat': lat, 'lon': lon};
|
||||
}
|
||||
|
||||
@@ -19,14 +19,17 @@ class PathRepositoryImpl implements PathRepository {
|
||||
required ReferencePoint reference,
|
||||
required int heading,
|
||||
required OuterBoundary outer,
|
||||
required List<HoleBoundary> holes,
|
||||
required Map<String, HoleBoundary> holes,
|
||||
required int workType,
|
||||
}) async {
|
||||
final body = {
|
||||
'reference': reference.toJson(),
|
||||
'heading': heading,
|
||||
'outer': outer.toJson(),
|
||||
'holes': holes.map((hole) => hole.toJson()).toList(),
|
||||
'holes': holes != null && holes.isNotEmpty
|
||||
? holes.values.first
|
||||
.toJson() // 取第一个值并序列化,去掉外层 Map
|
||||
: {},
|
||||
'workType': workType,
|
||||
};
|
||||
|
||||
|
||||
@@ -7,30 +7,19 @@ abstract class PathRepository {
|
||||
required ReferencePoint reference,
|
||||
required int heading,
|
||||
required OuterBoundary outer,
|
||||
required List<HoleBoundary> holes,
|
||||
required Map<String, HoleBoundary> holes,
|
||||
required int workType,
|
||||
});
|
||||
|
||||
// Save work record (新增)
|
||||
Future<Map<String, dynamic>> saveWorkRecord({
|
||||
required String workName,
|
||||
required String userId,
|
||||
required String jsonData,
|
||||
});
|
||||
Future<Map<String, dynamic>> saveWorkRecord({required String workName, required String userId, required String jsonData});
|
||||
|
||||
// Get work record (查询)
|
||||
Future<List<Map<String, dynamic>>> getWorkRecord({
|
||||
required String userId,
|
||||
});
|
||||
|
||||
Future<List<Map<String, dynamic>>> getWorkRecord({required String userId});
|
||||
|
||||
// Delete work record (删除)
|
||||
Future<Map<String, dynamic>> deleteWorkRecord({
|
||||
required String workName,
|
||||
});
|
||||
Future<Map<String, dynamic>> deleteWorkRecord({required String workName});
|
||||
|
||||
/// 根据作业名查询路径记录(用于“选择一个路径”)
|
||||
Future<List<Map<String, dynamic>>> selectWorkRecordByName({
|
||||
required String workName,
|
||||
});
|
||||
}
|
||||
Future<List<Map<String, dynamic>>> selectWorkRecordByName({required String workName});
|
||||
}
|
||||
|
||||
@@ -11,10 +11,11 @@ abstract class GeneratePathUseCase {
|
||||
required ReferencePoint reference,
|
||||
required int heading,
|
||||
required OuterBoundary outer,
|
||||
required List<HoleBoundary> holes,
|
||||
required Map<String, HoleBoundary> holes,
|
||||
required int workType,
|
||||
});
|
||||
}
|
||||
|
||||
class GeneratePathUseCaseImpl implements GeneratePathUseCase {
|
||||
final PathRepository _repository;
|
||||
|
||||
@@ -25,22 +26,14 @@ class GeneratePathUseCaseImpl implements GeneratePathUseCase {
|
||||
required ReferencePoint reference,
|
||||
required int heading,
|
||||
required OuterBoundary outer,
|
||||
required List<HoleBoundary> holes,
|
||||
required Map<String, HoleBoundary> holes,
|
||||
required int workType,
|
||||
}) async {
|
||||
try {
|
||||
final result = await _repository.generatePath(
|
||||
reference: reference,
|
||||
heading: heading,
|
||||
outer: outer,
|
||||
holes: holes,
|
||||
workType: workType,
|
||||
);
|
||||
final result = await _repository.generatePath(reference: reference, heading: heading, outer: outer, holes: holes, workType: workType);
|
||||
return Right(result); // 成功时返回 Right
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(e.toString(),1)); // 失败时返回 Left
|
||||
return Left(ServerFailure(e.toString(), 1)); // 失败时返回 Left
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -233,13 +233,13 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
required ReferencePoint reference,
|
||||
required int heading,
|
||||
required OuterBoundary outer,
|
||||
required List<HoleBoundary> holes,
|
||||
required Map<String, HoleBoundary> holes,
|
||||
required int workType,
|
||||
}) async {
|
||||
holes.asMap().forEach((index, hole) {
|
||||
final pointsStr = hole.position.map((p) => "(${p.lat.toStringAsFixed(6)}, ${p.lon.toStringAsFixed(6)})").join(', ');
|
||||
debugPrint('第${index + 1}组holes:$pointsStr');
|
||||
});
|
||||
//holes.asMap().forEach((index, hole) {
|
||||
// final pointsStr = hole.position.map((p) => "(${p.lat.toStringAsFixed(6)}, ${p.lon.toStringAsFixed(6)})").join(', ');
|
||||
// debugPrint('第${index + 1}组holes:$pointsStr');
|
||||
//});
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final result = await _generatePathUseCase.execute(reference: reference, heading: heading, outer: outer, holes: holes, workType: workType);
|
||||
|
||||
|
||||
@@ -544,6 +544,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
|
||||
// 重置选中状态
|
||||
_selectedPlot = null;
|
||||
|
||||
_selectedPlotPath = null;
|
||||
|
||||
// 重置作业模式
|
||||
@@ -1364,33 +1365,30 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
sideWidth: _workDistance,
|
||||
);
|
||||
|
||||
// ========== 核心新增:转换障碍物为holes参数 ==========
|
||||
final holes = <work_area_model.HoleBoundary>[];
|
||||
work_area_model.HoleBoundary? holeBoundary;
|
||||
|
||||
if (_obstacleHoles.isNotEmpty) {
|
||||
// 1. 构造二维 Position 数组
|
||||
List<List<work_area_model.Position>> positionList = [];
|
||||
|
||||
for (var holePoints in _obstacleHoles) {
|
||||
// 转换每组障碍物点为WGS84坐标系
|
||||
final holePosition = holePoints.map((latLng) {
|
||||
List<work_area_model.Position> innerPositionList = holePoints.map((latLng) {
|
||||
final wgs84Point = gcj02ToWgs84(latLng.latitude, latLng.longitude);
|
||||
return work_area_model.Position(lat: wgs84Point.latitude, lon: wgs84Point.longitude);
|
||||
}).toList();
|
||||
|
||||
// 添加到holes数组(sideWidth固定为0)
|
||||
holes.add(work_area_model.HoleBoundary(position: holePosition, sideWidth: 0.0));
|
||||
positionList.add(innerPositionList);
|
||||
}
|
||||
}
|
||||
try {
|
||||
// 将holes转换为普通Map,便于JSON序列化
|
||||
final holesJson = holes.map((hole) {
|
||||
return {
|
||||
'sideWidth': hole.sideWidth,
|
||||
'position': hole.position.map((p) => {'lat': p.lat, 'lon': p.lon}).toList(),
|
||||
};
|
||||
}).toList();
|
||||
|
||||
// 格式化打印JSON(带缩进,更易读)
|
||||
debugPrint('holes参数(JSON格式):\n${const JsonEncoder.withIndent(' ').convert(holesJson)}');
|
||||
} catch (e) {
|
||||
debugPrint('holes转JSON失败:$e');
|
||||
// 2. 创建 HoleBoundary 对象(无外层 Map)
|
||||
holeBoundary = work_area_model.HoleBoundary(position: positionList, sideWidth: 0.0);
|
||||
}
|
||||
|
||||
// 🔥 第二步:构造符合方法参数要求的 Map<String, HoleBoundary>
|
||||
// 用一个临时 Map 来满足类型要求,后续在 Cubit 中取值即可
|
||||
Map<String, work_area_model.HoleBoundary> holes = {};
|
||||
if (holeBoundary != null) {
|
||||
holes = {'tempKey': holeBoundary}; // 键名任意,仅为满足类型要求
|
||||
}
|
||||
final workType = _currentWorkMode == WorkMode.bow ? 0 : 2;
|
||||
|
||||
@@ -1399,7 +1397,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
reference: referencePoint,
|
||||
heading: heading,
|
||||
outer: outerBoundary,
|
||||
holes: holes, // 传入障碍物参数
|
||||
holes: holes.isNotEmpty ? holes : {}, // 无障碍物时传空对象
|
||||
workType: workType,
|
||||
);
|
||||
|
||||
@@ -1560,7 +1558,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_obstacleHoles.isNotEmpty)
|
||||
if (_obstacleHoles.isNotEmpty && _isObstacleEditing)
|
||||
PolygonLayer(
|
||||
polygons: _obstacleHoles.map((holePoints) {
|
||||
return Polygon(
|
||||
@@ -1591,7 +1589,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
),
|
||||
|
||||
// 3. 障碍物打点标记(红色)
|
||||
if (_currentAreaMode == AreaMode.obstacle)
|
||||
if (_currentAreaMode == AreaMode.obstacle && _isObstacleEditing)
|
||||
MarkerLayer(
|
||||
markers: [
|
||||
// 已完成的障碍物打点
|
||||
@@ -1653,42 +1651,42 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!_isWorkAreaCompleted)
|
||||
/// 历史打点的绿色标记
|
||||
MarkerLayer(
|
||||
markers: _markedPoints.asMap().entries.map((entry) {
|
||||
int index = entry.key + 1; // 打点序号(从1开始)
|
||||
LatLng point = entry.value;
|
||||
|
||||
/// 历史打点的绿色标记
|
||||
MarkerLayer(
|
||||
markers: _markedPoints.asMap().entries.map((entry) {
|
||||
int index = entry.key + 1; // 打点序号(从1开始)
|
||||
LatLng point = entry.value;
|
||||
|
||||
return Marker(
|
||||
point: point,
|
||||
width: 80,
|
||||
height: 40,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 8), // 向下偏移8px(抵消默认的顶部对齐)
|
||||
// 绿色打点标记
|
||||
Container(
|
||||
width: 16,
|
||||
height: 16,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF00C853), // 绿色主题色
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 2)],
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'$index',
|
||||
style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold),
|
||||
return Marker(
|
||||
point: point,
|
||||
width: 80,
|
||||
height: 40,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 8), // 向下偏移8px(抵消默认的顶部对齐)
|
||||
// 绿色打点标记
|
||||
Container(
|
||||
width: 16,
|
||||
height: 16,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF00C853), // 绿色主题色
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 2)],
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'$index',
|
||||
style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user