diff --git a/README.md b/README.md index 88e0338..7281c80 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,15 @@ redis-server.exe --service-uninstall # 启动前端 ## 1. 在frontend目录中执行 ``` -#npm install +npm install + +npm install --save-dev cross-env + +npx update-browserslist-db@latest + +在 package.json + "serve": "cross-env NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve", + ``` ## 2. 拷贝resource目录中的dt-sdk到node-modules目录 ## 3. 编译前端代码 diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/pom.xml b/backend/Skyeye-sys-dev/skyeye-service-manager/pom.xml index 57e0701..68e6f3d 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/pom.xml +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/pom.xml @@ -12,6 +12,11 @@ skyeye-service-manager + + com.alibaba.fastjson2 + fastjson2 + 2.0.60 + com.zhangy diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/service/impl/JmAirlinePlanServiceImpl.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/service/impl/JmAirlinePlanServiceImpl.java index 2ba8fce..51fb19f 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/service/impl/JmAirlinePlanServiceImpl.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/service/impl/JmAirlinePlanServiceImpl.java @@ -1,6 +1,7 @@ package com.zhangy.skyeye.jm.service.impl; import cn.hutool.core.bean.BeanUtil; +import com.alibaba.fastjson2.JSON; import com.zhangy.skyeye.common.extend.exception.ServiceException; import com.zhangy.skyeye.common.extend.util.ObjectUtil; import com.zhangy.skyeye.device.consts.PayloadTypeEnum; @@ -18,14 +19,17 @@ import com.zhangy.skyeye.py.service.IPyAirlineService; import com.zhangy.skyeye.sar.consts.SarImageModeEnum; import com.zhangy.skyeye.sar.dto.SarFlightPlanDTO; import com.zhangy.skyeye.sar.util.SpotlightPlanner; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; +import java.util.stream.Collectors; /** * 航线规划 */ +@Slf4j @Service public class JmAirlinePlanServiceImpl implements JmAirlinePlanService { @@ -38,6 +42,8 @@ public class JmAirlinePlanServiceImpl implements JmAirlinePlanService { @Override public Map> plan(JmJobModeEnum jobMode, SarImageModeEnum imageMode, Integer targetType, List uavList, List> pointList) { + log.info("航线规划参数:{}||{}||{}||{}|{}", jobMode, imageMode, targetType, + JSON.toJSONString(uavList), JSON.toJSONString(pointList)); checkParam(jobMode, imageMode, targetType); // 非航线模式需要调算法生成航线,需要从缓存取sar坐标 Map> airlineGroup = null; @@ -56,8 +62,6 @@ public class JmAirlinePlanServiceImpl implements JmAirlinePlanService { * * @param jobMode * @param imageMode - * @param uavList - * @param pointList */ private void checkParam(JmJobModeEnum jobMode, SarImageModeEnum imageMode, Integer targetType) { if (imageMode == SarImageModeEnum.JS) { @@ -73,7 +77,7 @@ public class JmAirlinePlanServiceImpl implements JmAirlinePlanService { /** * 聚束模式 * - * @param uav 无人机 + * @param uav 无人机 * @param points 目标点 * @return 航线 */ @@ -90,7 +94,7 @@ public class JmAirlinePlanServiceImpl implements JmAirlinePlanService { uav.setStartLon(startLon); uav.setStartLat(startLat); JmJobPoint home = new JmJobPoint(startLon, startLat); - for (List targets: points) { + for (List targets : points) { JmJobPoint target = targets.get(0); if (target == null) { continue; @@ -107,9 +111,8 @@ public class JmAirlinePlanServiceImpl implements JmAirlinePlanService { /** * 聚束模式 * - * @param uav 无人机 - * @param home 起飞点 - * @param point 目标点 + * @param uav 无人机 + * @param home 起飞点 * @return 航线 */ public JmAirline planJs(JmJobUav uav, JmJobPoint home, JmJobPoint target) { @@ -119,7 +122,7 @@ public class JmAirlinePlanServiceImpl implements JmAirlinePlanService { home.getLongitude(), home.getLatitude(), uav.getHeight(), sar.getTheta(), sar.getDirection()); JmAirline airline = new JmAirline(); - airline.setFlightType((byte)0); + airline.setFlightType((byte) 0); double height = plan.getHeight(); airline.setFlightStartLon(plan.getPowerOnLon()); airline.setFlightStartLat(plan.getPowerOnLat()); @@ -162,54 +165,79 @@ public class JmAirlinePlanServiceImpl implements JmAirlinePlanService { * 转为航线算法参数 */ private PyAirlineParamDTO toKtkxParam(List> pointList, List uavList) { - List pyPointList = new ArrayList<>(); + log.info("开始转换 PyAirlineParamDTO,pointList 大小: {}, uavList 大小: {}", + pointList.size(), uavList.size()); + // 所有无人机共享相同的目标点集(扁平化所有区域的点) + List allPoints = pointList.stream() + .flatMap(List::stream) + .collect(Collectors.toList()); + // 转换为 Python 需要的 double[][] 坐标格式(经度在前,纬度在后) + double[][] coords = allPoints.stream() + .map(p -> new double[]{p.getLongitude(), p.getLatitude()}) + .toArray(double[][]::new); + log.info("提取到 {} 个目标点坐标", coords.length); + + List pyTargets = new ArrayList<>(); List pyUavList = new ArrayList<>(); for (int i = 0; i < uavList.size(); i++) { - JmJobUav u = uavList.get(i); - // 区域 - List points = pointList.get(i); - double[][] coords = points.stream() - .map(point -> new double[] { point.getLongitude(), point.getLatitude() }) - .toArray(double[][]::new); - pyPointList.add(new PyAirlineTargetDTO(i, coords)); - - // 载荷 - JmJobPayload sar = u.getPayloadList() - .stream() - .filter(jp -> { - Long payloadId = jp.getPayloadId(); - SkyeyePayload p = payloadService.getOne(payloadId); - if (p != null && p.getType().equals(PayloadTypeEnum.SAR.getCode())) { - jp.setIp(p.getIp()); - return true; - } - return false; - }) - .findFirst() - .orElseThrow( () -> ServiceException.errorLog("存在无人机缺少sar类型载荷")); - - PyAirlineUavDTO uav = BeanUtil.copyProperties(u, PyAirlineUavDTO.class); - JmSarStatusDTO statusDTO = payloadService.getLastStatus(sar.getIp()); - double startLon = statusDTO.getLongitude(); - double startLat = statusDTO.getLatitude(); - if (u.getStartAltitude() == null) { - u.setStartAltitude((double) statusDTO.getAltitude()); + JmJobUav uav = uavList.get(i); + // 1. 为当前无人机创建目标(共享坐标,但 target id 不同) + pyTargets.add(new PyAirlineTargetDTO(i, coords)); + // 2. 获取 SAR 载荷(必须存在,否则抛异常) + JmJobPayload sarPayload = findSarPayload(uav); + String sarIp = sarPayload.getIp(); // 已在 findSarPayload 中设置 + // 3. 获取载荷最新状态(用于起始位置) + JmSarStatusDTO status = payloadService.getLastStatus(sarIp); + if (status == null) { + throw ServiceException.errorLog("无法获取 SAR载荷的最新状态"); } - uav.setId(u.getUavId()); - uav.setStartCoord(new double[] {startLon, startLat}); - uav.setEndCoord(new double[] {startLon, startLat}); - uav.setConstraint(u.getHeight()); - PyAirlinePayloadDTO payload = BeanUtil.copyProperties(sar, PyAirlinePayloadDTO.class); - payload.setType(PayloadTypeEnum.SAR.getCode()); - uav.setPayload(payload); - pyUavList.add(uav); + // 4. 如果起飞高度为空,则使用载荷当前高度 + if (uav.getStartAltitude() == null) { + uav.setStartAltitude((double) status.getAltitude()); + log.info("无人机 {} 起飞高度为空,已使用载荷当前高度: {}", uav.getUavId(), uav.getStartAltitude()); + } + // 5. 构建 Python UAV DTO + PyAirlineUavDTO pyUav = BeanUtil.copyProperties(uav, PyAirlineUavDTO.class); + pyUav.setId(uav.getUavId()); + pyUav.setStartCoord(new double[]{status.getLongitude(), status.getLatitude()}); + pyUav.setEndCoord(new double[]{status.getLongitude(), status.getLatitude()}); // 目前起终点相同 + pyUav.setConstraint(uav.getHeight()); + // 6. 构建 Payload DTO + PyAirlinePayloadDTO payloadDto = BeanUtil.copyProperties(sarPayload, PyAirlinePayloadDTO.class); + payloadDto.setType(PayloadTypeEnum.SAR.getCode()); + pyUav.setPayload(payloadDto); + pyUavList.add(pyUav); } - PyAirlineUavDTO[] uavs = pyUavList.toArray(new PyAirlineUavDTO[pyUavList.size()]); - PyAirlineTargetDTO[] targets = pyPointList.toArray(new PyAirlineTargetDTO[pyPointList.size()]); - + // 转换为数组(Python 端需要数组格式) + PyAirlineTargetDTO[] targetListArray = pyTargets.toArray(new PyAirlineTargetDTO[0]); + PyAirlineUavDTO[] uavListArray = pyUavList.toArray(new PyAirlineUavDTO[0]); PyAirlineParamDTO param = new PyAirlineParamDTO(); - param.setTargets(targets); - param.setUavs(uavs); + param.setTargets(targetListArray); + param.setUavs(uavListArray); + log.info("转换完成,生成 PyAirlineParamDTO:{} 个目标组,{} 架无人机", + targetListArray.length, uavListArray.length); return param; } + + /** + * 从无人机载荷列表中查找 SAR 类型载荷,并设置 IP + * @param uav 无人机对象 + * @return SAR 载荷 + * @throws ServiceException 如果没有找到 SAR 载荷 + */ + private JmJobPayload findSarPayload(JmJobUav uav) { + return uav.getPayloadList().stream() + .filter(payload -> { + Long payloadId = payload.getPayloadId(); + SkyeyePayload skyeyePayload = payloadService.getOne(payloadId); + if (skyeyePayload != null && PayloadTypeEnum.SAR.getCode().equals(skyeyePayload.getType())) { + payload.setIp(skyeyePayload.getIp()); + return true; + } + return false; + }) + .findFirst() + .orElseThrow(() -> ServiceException.errorLog( + "存在无人机缺少sar类型载荷")); + } } diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/service/impl/JmJobServiceImpl.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/service/impl/JmJobServiceImpl.java index f606576..286ac7f 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/service/impl/JmJobServiceImpl.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/service/impl/JmJobServiceImpl.java @@ -2,6 +2,7 @@ package com.zhangy.skyeye.jm.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.zhangy.skyeye.common.extend.enums.EnumUtil; @@ -36,10 +37,6 @@ import com.zhangy.skyeye.sar.enums.SarIMUStatusEnum; import com.zhangy.skyeye.sar.service.ISarControlService; import com.zhangy.skyeye.sar.service.ISarMtiPointService; import com.zhangy.skyeye.sar.service.ISarMtiTrailService; -//import com.zhangy.skyeye.smp.dto.SmpSubscriptResDTO; -//import com.zhangy.skyeye.smp.dto.SmpWayPointDTO; -//import com.zhangy.skyeye.smp.service.ISmpSubscriptService; -//import com.zhangy.skyeye.smp.service.ISmpWayPointService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -56,7 +53,7 @@ import java.util.stream.Collectors; @Slf4j @Service public class JmJobServiceImpl implements JmJobService { - + @Autowired private JmJobMapper jobMapper; @Autowired @@ -69,43 +66,30 @@ public class JmJobServiceImpl implements JmJobService { private JmJobStatusService jobStatusService; @Autowired private JmJobExecService jmJobExecService; - -// @Autowired -// private ISmpWayPointService smpWayPointService; -// @Autowired -// private ISmpSubscriptService smpSubscriptService; - @Autowired private ISarControlService sarControlService; @Autowired private ISarMtiPointService sarMtiPointService; @Autowired private ISarMtiTrailService sarMtiTrailService; - @Autowired private IPayloadService payloadService; @Autowired private IUavService uavService; - @Autowired private SysFileTypeService fileTypeService; - - @Autowired - private IPyAirlineService ktkxService; - @Autowired private QuartzService quartzService; - @Autowired private JmAirlinePlanService jmAirlinePlanService; - + @Override public IPage selectPage(JmJobPageDTO param) { IPage jobPage = jobMapper.selectPage(param); loadDetail(param.getUavId(), jobPage.getRecords()); return jobPage; } - + @Override public List selectList(JmJobQueryDTO param) { List list = jobMapper.selectList(param); @@ -114,8 +98,7 @@ public class JmJobServiceImpl implements JmJobService { } @Override - public List selectExecJobs(JmJobQueryDTO param) - { + public List selectExecJobs(JmJobQueryDTO param) { List list = jobMapper.selectExecJobs(param); loadDetail(param.getUavId(), list); return list; @@ -165,6 +148,7 @@ public class JmJobServiceImpl implements JmJobService { @Transactional @Override public JmJobDTO save(JmJobModeEnum jobMode, JmJobDTO e) { + log.info("保存任务参数:{}||{}", JSON.toJSONString(jobMode), JSON.toJSONString(e)); SarImageModeEnum imageMode = EnumUtil.parseEx(SarImageModeEnum.class, e.getImageMode()); // 非航线模式需要调算法生成航线,需要从缓存取sar坐标 Map> airlineGroup = jmAirlinePlanService.plan(jobMode, imageMode, e.getTargetType(), @@ -210,7 +194,7 @@ public class JmJobServiceImpl implements JmJobService { */ private JmJobPayload checkAndSetPayload(List payloadParamList) { JmJobPayload sar = null; - for(JmJobPayload p : payloadParamList) { + for (JmJobPayload p : payloadParamList) { Long payloadId = p.getPayloadId(); SkyeyePayload payload = payloadService.getOne(payloadId); if (payload == null) { @@ -308,7 +292,7 @@ public class JmJobServiceImpl implements JmJobService { com.zhangy.skyeye.common.extend.util.BeanUtil.validationEx(airline); }); } - + @Transactional @Override public int delete(Long... ids) { @@ -347,7 +331,7 @@ public class JmJobServiceImpl implements JmJobService { @Transactional @Override public void start(JmJobDTO job) { - // SarBackImageFrameDTO.i = 1; + // SarBackImageFrameDTO.i = 1; // 校验无人机、载荷 jobStatusService.checkDeviceForNewJob(job.getUavList()); // 保存任务状态 @@ -375,6 +359,7 @@ public class JmJobServiceImpl implements JmJobService { /** * 起飞 kmz + * * @param job */ private void startKmz(JmJobDTO job) { @@ -447,7 +432,7 @@ public class JmJobServiceImpl implements JmJobService { controlParam.setIp(ip); sarControlService.sendUdp(controlParam); // 标记缓存状态,确保断连重新发送请求时不会重复执行 - // uav.setSarStatus(ExecStatusEnum.OVER); + // uav.setSarStatus(ExecStatusEnum.OVER); }); // 删除缓存任务信息 jobStatusService.remove(id); diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/py/service/impl/PyAirlineServiceImpl.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/py/service/impl/PyAirlineServiceImpl.java index 28ac9d9..331eafe 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/py/service/impl/PyAirlineServiceImpl.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/py/service/impl/PyAirlineServiceImpl.java @@ -13,6 +13,7 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import com.alibaba.fastjson2.*; import java.io.IOException; import java.net.ConnectException; @@ -37,6 +38,9 @@ public class PyAirlineServiceImpl implements IPyAirlineService { @Value("${skyeye.py.ktkxUrl}") private String url; + @Value("${skyeye.java.ktkxUrl}") + private String javaUrl; + // 判断是否为空,并剔除无需执行任务的无人机 private boolean checkEmpty(Map uavMap) { if (uavMap == null || uavMap.size() == 0) { @@ -72,7 +76,17 @@ public class PyAirlineServiceImpl implements IPyAirlineService { @Override public Map> getAirline(PyAirlineParamDTO param) { - HttpResponse httpResponse = null; + log.info("请求航线规划算法服务参数:{}", JSON.toJSONString(param)); +// HttpResponse post; +// try { +// post = HttpUtil.post(javaUrl, null, param); +// } catch (IOException e) { +// throw new RuntimeException(e); +// } +// PyAirlineResponse pyAirlineResponse; +// pyAirlineResponse = JsonUtil.parse(post.body(), PyAirlineResponse.class); +// log.info("请求航线规划算法服务响应:{}",JSON.toJSONString(pyAirlineResponse)); + HttpResponse httpResponse; try { httpResponse = HttpUtil.post(url, null, param); } catch (ConnectException ex) { @@ -82,18 +96,20 @@ public class PyAirlineServiceImpl implements IPyAirlineService { } PyAirlineResponse response; response = JsonUtil.parse(httpResponse.body(), PyAirlineResponse.class); + log.info("请求航线规划算法服务响应:{}", JSON.toJSONString(response)); int responseCode = httpResponse.statusCode(); if (responseCode >= 400) { - log.warn("调用航线规划算法错误["+ responseCode + "]:" + response.getMessage() + ",参数:" + JsonUtil.toString(param)); - throw ServiceException.noLog("调用航线规划算法错误["+ responseCode + "]:" + response.getMessage()); + log.warn("调用航线规划算法错误[{}]:{},参数:{}", responseCode, response.getMessage(), JsonUtil.toString(param)); + throw ServiceException.noLog("调用航线规划算法错误[" + responseCode + "]:" + response.getMessage()); } Map uavMap = response.getData(); + log.info("航线规划算法结果:{}", JSON.toJSONString(uavMap)); if (checkEmpty(uavMap)) { if ("success".endsWith(response.getMessage())) { throw ServiceException.noLog("无法生成航线,可能飞行区域超过25平方公里"); } - log.warn("调用航线规划算法未返回数据["+ responseCode + "]:" + response.getMessage() + ",参数:" + JsonUtil.toString(param)); - throw ServiceException.noLog("调用航线规划算法未返回数据["+ responseCode + "]:" + response.getMessage()); + log.warn("调用航线规划算法未返回数据[{}]:{},参数:{}", responseCode, response.getMessage(), JsonUtil.toString(param)); + throw ServiceException.noLog("调用航线规划算法未返回数据[" + responseCode + "]:" + response.getMessage()); } Map> map = new HashMap<>(); diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/resources/application-dev.yml b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/resources/application-dev.yml index 7a94824..583287c 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/resources/application-dev.yml +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/resources/application-dev.yml @@ -84,6 +84,8 @@ skyeye: py: ktkxUrl: http://127.0.0.1:18090/ktkx/UavPlanning/SAR detectUrl: http://127.0.0.1:18091/ktkx/detect/cpu/SARCoord + java: + ktkxUrl: http://127.0.0.1:9117/ktkx/UavPlanning/SAR weather: cityCode: 101120201 diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/resources/mapping/jm/JmJobMapping.xml b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/resources/mapping/jm/JmJobMapping.xml index 7e89e02..5f74079 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/resources/mapping/jm/JmJobMapping.xml +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/resources/mapping/jm/JmJobMapping.xml @@ -35,7 +35,7 @@