生成路径 对接
This commit is contained in:
@@ -28,6 +28,7 @@ class PathRepositoryImpl implements PathRepository {
|
||||
'holes': holes.map((hole) => hole.toJson()).toList(),
|
||||
'workType': workType,
|
||||
});
|
||||
print('请求体: $body'); // 调试输出请求体
|
||||
|
||||
try {
|
||||
final response = await http.post(url, headers: headers, body: body);
|
||||
@@ -134,14 +135,12 @@ class PathRepositoryImpl implements PathRepository {
|
||||
required String workName,
|
||||
}) async {
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||
final url = Uri.parse(
|
||||
'https://serviceri.satabot.com/iot/workRecord/selectByWorkName',
|
||||
).replace(
|
||||
queryParameters: {
|
||||
'workName': workName,
|
||||
'_t': timestamp.toString(),
|
||||
},
|
||||
);
|
||||
final url =
|
||||
Uri.parse(
|
||||
'https://serviceri.satabot.com/iot/workRecord/selectByWorkName',
|
||||
).replace(
|
||||
queryParameters: {'workName': workName, '_t': timestamp.toString()},
|
||||
);
|
||||
print('请求URL: $url');
|
||||
|
||||
try {
|
||||
@@ -167,20 +166,22 @@ class PathRepositoryImpl implements PathRepository {
|
||||
} else if (rawData is Map) {
|
||||
records = [Map<String, dynamic>.from(rawData)];
|
||||
} else {
|
||||
throw Exception('Unexpected data type for "data": ${rawData.runtimeType}');
|
||||
throw Exception(
|
||||
'Unexpected data type for "data": ${rawData.runtimeType}',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return records;
|
||||
} else {
|
||||
throw Exception('API error: ${data['msg'] ?? 'Unknown'}');
|
||||
}
|
||||
} else {
|
||||
throw Exception('HTTP ${response.statusCode}: ${response.reasonPhrase}');
|
||||
throw Exception(
|
||||
'HTTP ${response.statusCode}: ${response.reasonPhrase}',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Network error in selectWorkRecordByName: $e');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import 'package:maibu_satabot_v2/core/app/app_user_cubit.dart';
|
||||
import 'package:maibu_satabot_v2/core/di/injection.dart';
|
||||
import 'package:maibu_satabot_v2/core/network/net_message_dispatcher.dart';
|
||||
import 'package:maibu_satabot_v2/core/network/protocol_decoder.dart';
|
||||
import 'package:maibu_satabot_v2/features/devices/data/models/device_work_area_param_model.dart'
|
||||
as work_area_model;
|
||||
import 'package:maibu_satabot_v2/features/devices/data/repositories/generate_path_repository_Impl.dart';
|
||||
import 'package:maibu_satabot_v2/features/devices/domain/usecases/get_work_record_usecase.dart';
|
||||
import 'package:maibu_satabot_v2/features/devices/domain/usecases/select_work_record_usecase.dart';
|
||||
@@ -1235,10 +1237,151 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
|
||||
child: BottomOperationPanel(
|
||||
initialRobotMode: RobotMode.point,
|
||||
initialAreaMode: AreaMode.work,
|
||||
onComplete: () {
|
||||
onComplete: () async {
|
||||
debugPrint(
|
||||
'操作完成回调:当前机器人模式=$_currentRobotMode,当前区域模式=$_currentAreaMode,当前作业模式:$_currentWorkMode,作业区域点:$_markedPoints,作业行距=$_workDistance,航线方向角=$_angle',
|
||||
);
|
||||
|
||||
// 1. 校验必要条件(至少需要3个打点才能形成闭合区域)
|
||||
if (_markedPoints.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('请先添加作业区域打点!'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (_markedPoints.length < 3) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('作业区域至少需要3个打点才能生成路径!'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (_currentWorkMode == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('请先选择作业模式!'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 2. 转换页面数据为 generatePath 所需的参数格式
|
||||
// 2.1 参考点(取第一个打点,转换为 WGS84)
|
||||
final firstPoint = _markedPoints.first;
|
||||
final wgs84FirstPoint = gcj02ToWgs84(
|
||||
firstPoint.latitude,
|
||||
firstPoint.longitude,
|
||||
);
|
||||
final referencePoint = work_area_model.ReferencePoint(
|
||||
lat: wgs84FirstPoint.latitude,
|
||||
lon: wgs84FirstPoint.longitude,
|
||||
);
|
||||
|
||||
// 2.2 航向角(转换为整型,-1表示最优航向则取-1)
|
||||
final heading = _angle == -1 ? -1 : _angle.toInt();
|
||||
|
||||
// 2.3 外边界(转换为 OuterBoundary 格式,所有点转 WGS84)
|
||||
final outerBoundary = work_area_model.OuterBoundary(
|
||||
// 将 LatLng 打点转换为 Position 列表(先转 WGS84)
|
||||
position: _markedPoints.map((latLng) {
|
||||
// 关键:每个点都转换为 WGS84 坐标系
|
||||
final wgs84Point = gcj02ToWgs84(
|
||||
latLng.latitude,
|
||||
latLng.longitude,
|
||||
);
|
||||
return work_area_model.Position(
|
||||
lat: wgs84Point.latitude,
|
||||
lon: wgs84Point.longitude,
|
||||
);
|
||||
}).toList(),
|
||||
sideWidth: _workDistance,
|
||||
);
|
||||
|
||||
final holes = <work_area_model.HoleBoundary>[];
|
||||
|
||||
final workType = _currentWorkMode == WorkMode.bow
|
||||
? 0
|
||||
: 2; // 注意:这里弓字模式是0,自定义是2(确认和后端一致)
|
||||
|
||||
// 3. 调用 Cubit 的 generatePath 方法生成路径
|
||||
await context.read<DevicesCubit>().generatePath(
|
||||
reference: referencePoint,
|
||||
heading: heading,
|
||||
outer: outerBoundary,
|
||||
holes: holes,
|
||||
workType: workType,
|
||||
);
|
||||
|
||||
// 4. 获取生成结果并处理
|
||||
final cubitState = context.read<DevicesCubit>().state;
|
||||
if (cubitState.errorMessage != null) {
|
||||
// 生成失败
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('路径生成失败:${cubitState.errorMessage}'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
} else if (cubitState.generatedPath != null) {
|
||||
// 生成成功
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('路径生成成功!'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
debugPrint('生成的路径数据:${cubitState.generatedPath}');
|
||||
|
||||
// 可选:将生成的路径转换为地图可显示的坐标(WGS84 → GCJ02)
|
||||
if (cubitState.generatedPath is List) {
|
||||
setState(() {
|
||||
// 关键:生成的路径是 WGS84,需转回 GCJ02 才能在高德地图正确显示
|
||||
gcjPathPoints = (cubitState.generatedPath as List)
|
||||
.map((point) {
|
||||
// 先取 WGS84 坐标
|
||||
double wgs84Lat =
|
||||
safeToDouble(point['lat']) ?? 0.0;
|
||||
double wgs84Lon =
|
||||
safeToDouble(point['lon']) ?? 0.0;
|
||||
// 转换为 GCJ02
|
||||
final gcjPoint = wgs84ToGcj02(
|
||||
wgs84Lat,
|
||||
wgs84Lon,
|
||||
);
|
||||
return LatLng(
|
||||
gcjPoint.latitude,
|
||||
gcjPoint.longitude,
|
||||
);
|
||||
})
|
||||
.toList();
|
||||
// 外边界也转回 GCJ02 显示(可选,保持和地图坐标系一致)
|
||||
gcjOuterPoints = _markedPoints;
|
||||
});
|
||||
// 移动地图到生成的路径中心
|
||||
moveMapToPointsCenter(gcjPathPoints);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 异常处理
|
||||
debugPrint('路径生成异常:$e');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
'路径生成异常:${e.toString().substring(0, 50)}',
|
||||
),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 5. 关闭操作面板
|
||||
setState(() => _isPanelOpen = false);
|
||||
},
|
||||
onUndoTap: _undoLastPoint,
|
||||
@@ -1480,6 +1623,24 @@ LatLng wgs84ToGcj02(double lat, double lon) {
|
||||
return LatLng(mgLat, mgLon);
|
||||
}
|
||||
|
||||
LatLng gcj02ToWgs84(double lat, double lon) {
|
||||
if (_outOfChina(lat, lon)) {
|
||||
return LatLng(lat, lon);
|
||||
}
|
||||
|
||||
double dLat = _transformLat(lon - 105.0, lat - 35.0);
|
||||
double dLon = _transformLon(lon - 105.0, lat - 35.0);
|
||||
double radLat = lat / 180.0 * _pi;
|
||||
double magic = sin(radLat);
|
||||
magic = 1 - _ee * magic * magic;
|
||||
double sqrtMagic = sqrt(magic);
|
||||
dLat = (dLat * 180.0) / ((_a * (1 - _ee)) / (magic * sqrtMagic) * _pi);
|
||||
dLon = (dLon * 180.0) / (_a / sqrtMagic * cos(radLat) * _pi);
|
||||
double mgLat = lat - dLat;
|
||||
double mgLon = lon - dLon;
|
||||
return LatLng(mgLat, mgLon);
|
||||
}
|
||||
|
||||
bool _outOfChina(double lat, double lon) {
|
||||
return lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user