package com.ard.sample.wayline.service.impl; import com.ard.oss.service.impl.OssServiceContext; import com.ard.route.domain.route.ArdRouteInfo; import com.ard.route.service.IArdRouteInfoService; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ard.common.core.domain.model.LoginUser; import com.ard.sample.common.error.CommonErrorEnum; import com.ard.sample.common.model.Pagination; import com.ard.sample.common.model.PaginationData; import com.ard.sample.common.model.ResponseResult; import com.ard.sample.component.mqtt.model.*; import com.ard.sample.component.mqtt.service.IMessageSenderService; import com.ard.common.core.redis.RedisConst; import com.ard.common.core.redis.RedisOpsUtils; import com.ard.sample.manage.model.dto.DeviceDTO; import com.ard.sample.manage.service.IDeviceService; import com.ard.sample.media.model.MediaFileCountDTO; import com.ard.sample.media.model.MediaMethodEnum; import com.ard.sample.media.service.IFileService; import com.ard.sample.wayline.mapper.IWaylineJobMapper; import com.ard.sample.wayline.model.dto.*; import com.ard.sample.wayline.model.entity.WaylineJobEntity; import com.ard.sample.wayline.model.enums.WaylineJobStatusEnum; import com.ard.sample.wayline.model.enums.WaylineMethodEnum; import com.ard.sample.wayline.model.enums.WaylineTaskTypeEnum; import com.ard.sample.wayline.model.param.CreateJobParam; import com.ard.sample.wayline.service.IWaylineJobService; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.apache.ibatis.annotations.Param; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.mqtt.support.MqttHeaders; import org.springframework.messaging.MessageHeaders; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.sql.SQLException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.*; import java.util.stream.Collectors; import static com.ard.route.util.ToolUtils.kmzPath; import static com.ard.route.util.ToolUtils.subStringMinioUrlName; /** * @author sean * @version 1.1 * @date 2022/6/1 */ @Service @Transactional @Slf4j public class WaylineJobServiceImpl implements IWaylineJobService { @Autowired private IWaylineJobMapper mapper; // @Autowired // private IWaylineFileService waylineFileService; @Autowired private IArdRouteInfoService _ardRouteInfoService; @Resource private OssServiceContext _ossService; @Autowired private IDeviceService deviceService; @Autowired private IMessageSenderService messageSender; @Autowired private ObjectMapper objectMapper; @Autowired private IFileService fileService; /** * 插入航路作业 * * @param jobEntity 航路作业实体 * @return 航路作业DTO的Optional包装对象,如果插入失败则返回Optional.empty() */ private Optional insertWaylineJob(WaylineJobEntity jobEntity) { int id = mapper.insert(jobEntity); if (id <= 0) { return Optional.empty(); } return Optional.ofNullable(this.entity2Dto(jobEntity)); } /** * 创建航线任务 * * @param param 任务参数 * @param username 用户名 * @param beginTime 开始时间 * @param endTime 结束时间 * @return Optional 返回创建的航线任务对象,若创建失败则返回Optional.empty() */ @Override public Optional createWaylineJob(CreateJobParam param, String username, Long beginTime, Long endTime) { if (Objects.isNull(param)) { return Optional.empty(); } // Immediate tasks, allocating time on the backend. WaylineJobEntity jobEntity = WaylineJobEntity.builder() .name(param.getName()) .dockSn(param.getDockSn()) .fileId(param.getFileId()) .username(username) .jobId(UUID.randomUUID().toString()) .beginTime(beginTime) .endTime(endTime) .status(WaylineJobStatusEnum.PENDING.getVal()) .taskType(param.getTaskType()) .waylineType(param.getWaylineType()) .outOfControlAction(param.getOutOfControlAction()) .rthAltitude(param.getRthAltitude()) .mediaCount(0) .parentId(param.getParentId()) .loopSum(param.getLoopSum()) .build(); return insertWaylineJob(jobEntity); } /** * 根据父作业ID创建新的作业 * * @param parentId 父作业ID * @return 新创建的作业的DTO对象,如果父作业不存在,则返回Optional.empty() */ @Override public Optional createWaylineJobByParent(String parentId) { Optional parentJobOpt = this.getJobByJobId(parentId); if (parentJobOpt.isEmpty()) { return Optional.empty(); } WaylineJobEntity jobEntity = this.dto2Entity(parentJobOpt.get()); jobEntity.setJobId(UUID.randomUUID().toString()); jobEntity.setErrorCode(null); jobEntity.setCompletedTime(null); jobEntity.setExecuteTime(null); jobEntity.setStatus(WaylineJobStatusEnum.PENDING.getVal()); jobEntity.setParentId(parentId); return this.insertWaylineJob(jobEntity); } /** * 发布飞行任务 * * @param param 任务参数 * @param loginUser 登录用户 * @return ResponseResult 返回结果 * @throws SQLException SQL异常 */ @Override public ResponseResult publishFlightTask(CreateJobParam param, LoginUser loginUser) throws SQLException { if (WaylineTaskTypeEnum.IMMEDIATE.getVal() == param.getTaskType()) { param.setExecuteTime(System.currentTimeMillis()); } Optional waylineJobOpt = this.createWaylineJob(param, loginUser.getUsername(), param.getExecuteTime(), param.getExecuteTime()); // 循环任务 if (param.getTaskType() == WaylineTaskTypeEnum.LOOP.getVal()) { // 循环任务,不立即执行,由定时任务发布 return ResponseResult.success(); } if (waylineJobOpt.isEmpty()) { return ResponseResult.error("创建机场任务失败!"); } WaylineJobDTO waylineJob = waylineJobOpt.get(); boolean isOnline = deviceService.checkDeviceOnline(waylineJob.getDockSn()); if (!isOnline) { return ResponseResult.error("机场不在线!"); } // get wayline file // Optional waylineFile = waylineFileService.getWaylineByWaylineId(waylineJob.getFileId()); // if (waylineFile.isEmpty()) { // return ResponseResult.error("机场任务航线不存在!"); // } // 换航线表 ArdRouteInfo ardRouteOpt = _ardRouteInfoService.selectArdRouteInfoById(waylineJob.getFileId()); if (ardRouteOpt == null) { return ResponseResult.error("机场任务航线不存在!"); } // get file url // URL url = waylineFileService.getObjectUrl(waylineFile.get().getWaylineId()); URL url = null; String minioUrl = subStringMinioUrlName(ardRouteOpt.getPath()); String md5 = ""; try (InputStream inputStream = _ossService.getObject(kmzPath, minioUrl)) { md5 = DigestUtils.md5Hex(inputStream); url = new URL(ardRouteOpt.getPath()); } catch (Exception e) { return ResponseResult.error("机场任务航线文件地址错误!" + e.getMessage()); } WaylineTaskCreateDTO flightTask = WaylineTaskCreateDTO.builder() .flightId(waylineJob.getJobId()) .executeTime(waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) .taskType(waylineJob.getTaskType()) .waylineType(waylineJob.getWaylineType()) .rthAltitude(waylineJob.getRthAltitude()) .outOfControlAction(waylineJob.getOutOfControlAction()) .file(WaylineTaskFileDTO.builder() .url(url.toString()) // .fingerprint(waylineFile.get().getSign()) .fingerprint(md5) .build()) // .simulateMission(WaylineSimulateMissionDTO.builder() // .isEnable(1) // .latitude(46.558831038) // .longitude(125.148072319) // .build()) .build(); String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + waylineJob.getDockSn() + TopicConst.SERVICES_SUF; CommonTopicResponse response = CommonTopicResponse.builder() .tid(UUID.randomUUID().toString()) .bid(waylineJob.getJobId()) .timestamp(System.currentTimeMillis()) .data(flightTask) .method(WaylineMethodEnum.FLIGHT_TASK_PREPARE.getMethod()) .build(); ServiceReply serviceReply = messageSender.publishWithReply(topic, response); log.info("下发任务结果: {}", serviceReply.getResult()); if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { log.info("Prepare task ====> Error code: {}", serviceReply.getResult()); this.updateJob(WaylineJobDTO.builder() .jobId(waylineJob.getJobId()) .executeTime(LocalDateTime.now()) .status(WaylineJobStatusEnum.FAILED.getVal()) .completedTime(LocalDateTime.now()) .code(serviceReply.getResult()).build()); return ResponseResult.error("Prepare task ====> Error code: " + serviceReply.getResult()); } // 飞行命令先注释掉 // Issue an immediate task execution command. if (WaylineTaskTypeEnum.IMMEDIATE.getVal() == waylineJob.getTaskType()) { if (!executeFlightTask(waylineJob.getJobId())) { return ResponseResult.error("Failed to execute job."); } } if (WaylineTaskTypeEnum.TIMED.getVal() == waylineJob.getTaskType()) { boolean isAdd = RedisOpsUtils.zAdd(RedisConst.WAYLINE_JOB_TIMED_EXECUTE, waylineJob.getDockSn() + RedisConst.DELIMITER + waylineJob.getJobId(), waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); if (!isAdd) { return ResponseResult.error("Failed to create scheduled job."); } } return ResponseResult.success(); } public ResponseResult publishFlightTaskPrepare(Optional waylineJobOpt) throws SQLException{ if (waylineJobOpt.isEmpty()) { return ResponseResult.error("创建机场任务失败!"); } WaylineJobDTO waylineJob = waylineJobOpt.get(); boolean isOnline = deviceService.checkDeviceOnline(waylineJob.getDockSn()); if (!isOnline) { return ResponseResult.error("机场不在线!"); } // 换航线表 ArdRouteInfo ardRouteOpt = _ardRouteInfoService.selectArdRouteInfoById(waylineJob.getFileId()); if (ardRouteOpt == null) { return ResponseResult.error("机场任务航线不存在!"); } // get file url URL url = null; String minioUrl = subStringMinioUrlName(ardRouteOpt.getPath()); String md5 = ""; try (InputStream inputStream = _ossService.getObject(kmzPath, minioUrl)) { md5 = DigestUtils.md5Hex(inputStream); url = new URL(ardRouteOpt.getPath()); log.info("机场任务航线文件url地址:{}", url); log.info("机场任务航线文件md5值:{}", md5); } catch (Exception e) { log.error("机场任务航线文件地址错误!{}", e.getMessage()); return ResponseResult.error("机场任务航线文件地址错误!" + e.getMessage()); } WaylineTaskCreateDTO flightTask = WaylineTaskCreateDTO.builder() .flightId(waylineJob.getJobId()) .executeTime(waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) .taskType(waylineJob.getTaskType()) .waylineType(waylineJob.getWaylineType()) .rthAltitude(waylineJob.getRthAltitude()) .outOfControlAction(waylineJob.getOutOfControlAction()) .file(WaylineTaskFileDTO.builder() .url(url.toString()) // .fingerprint(waylineFile.get().getSign()) .fingerprint(md5) .build()) // .simulateMission(WaylineSimulateMissionDTO.builder() // .isEnable(1) // .latitude(46.558831038) // .longitude(125.148072319) // .build()) .build(); // 下发参数打印 try{ String json = objectMapper.writeValueAsString(flightTask); log.info("机场下发航线任务的参数flightTask:{}", json); } catch (Exception e){ log.error("机场下发航线任务的参数flightTask异常:{}", e.getMessage()); } String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + waylineJob.getDockSn() + TopicConst.SERVICES_SUF; CommonTopicResponse response = CommonTopicResponse.builder() .tid(UUID.randomUUID().toString()) .bid(waylineJob.getJobId()) .timestamp(System.currentTimeMillis()) .data(flightTask) .method(WaylineMethodEnum.FLIGHT_TASK_PREPARE.getMethod()) .build(); ServiceReply serviceReply = messageSender.publishWithReply(topic, response); log.info("下发任务结果: {}", serviceReply.getResult()); log.info("机场任务航线文件url地址:{}", url); log.info("机场任务航线文件md5值:{}", md5); if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { log.info("Prepare task ====> Error code: {}", serviceReply.getResult()); this.updateJob(WaylineJobDTO.builder() .jobId(waylineJob.getJobId()) .executeTime(LocalDateTime.now()) .status(WaylineJobStatusEnum.FAILED.getVal()) .completedTime(LocalDateTime.now()) .code(serviceReply.getResult()).build()); return ResponseResult.error("Prepare task ====> Error code: " + serviceReply.getResult()); } // 飞行命令先注释掉 // Issue an immediate task execution command. if (WaylineTaskTypeEnum.IMMEDIATE.getVal() == waylineJob.getTaskType()) { if (!executeFlightTask(waylineJob.getJobId())) { return ResponseResult.error("Failed to execute job."); } } if (WaylineTaskTypeEnum.TIMED.getVal() == waylineJob.getTaskType()) { boolean isAdd = RedisOpsUtils.zAdd(RedisConst.WAYLINE_JOB_TIMED_EXECUTE, waylineJob.getDockSn() + RedisConst.DELIMITER + waylineJob.getJobId(), waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); if (!isAdd) { return ResponseResult.error("Failed to create scheduled job."); } } return ResponseResult.success(); } /** * 执行飞行任务 * * @param jobId 任务ID * @return 如果任务执行成功返回true,否则返回false * @throws IllegalArgumentException 如果任务不存在 * @throws RuntimeException 如果设备处于离线状态 */ @Override public Boolean executeFlightTask(String jobId) { // get job Optional waylineJob = this.getJobByJobId(jobId); if (waylineJob.isEmpty()) { throw new IllegalArgumentException("Job doesn't exist."); } boolean isOnline = deviceService.checkDeviceOnline(waylineJob.get().getDockSn()); if (!isOnline) { throw new RuntimeException("Dock is offline."); } WaylineJobDTO job = waylineJob.get(); WaylineTaskCreateDTO flightTask = WaylineTaskCreateDTO.builder().flightId(jobId).build(); String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + job.getDockSn() + TopicConst.SERVICES_SUF; CommonTopicResponse response = CommonTopicResponse.builder() .tid(UUID.randomUUID().toString()) .bid(jobId) .timestamp(System.currentTimeMillis()) .data(flightTask) .method(WaylineMethodEnum.FLIGHT_TASK_EXECUTE.getMethod()) .build(); ServiceReply serviceReply = messageSender.publishWithReply(topic, response); if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { log.info("Execute job ====> Error code: {}", serviceReply.getResult()); this.updateJob(WaylineJobDTO.builder() .jobId(jobId) .executeTime(LocalDateTime.now()) .status(WaylineJobStatusEnum.FAILED.getVal()) .completedTime(LocalDateTime.now()) .code(serviceReply.getResult()).build()); return false; } this.updateJob(WaylineJobDTO.builder() .jobId(jobId) .executeTime(LocalDateTime.now()) .status(WaylineJobStatusEnum.IN_PROGRESS.getVal()) .build()); RedisOpsUtils.setWithExpire(RedisConst.WAYLINE_JOB_RUNNING_PREFIX + job.getDockSn(), EventsReceiver.builder().bid(jobId).sn(job.getDockSn()).build(), RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND); return true; } /** * 取消飞行任务 * * @param jobIds 需要取消的任务ID集合 * @throws IllegalArgumentException 如果任务状态不正确,抛出非法参数异常 */ @Override public void cancelFlightTask(Collection jobIds) { List waylineJobs = getJobsByConditions(jobIds, WaylineJobStatusEnum.PENDING); // Set waylineJobIds = waylineJobs.stream().map(WaylineJobDTO::getJobId).collect(Collectors.toSet()); // // Check if the task status is correct. // boolean isErr = !jobIds.removeAll(waylineJobIds) || !jobIds.isEmpty(); // if (isErr) { // throw new IllegalArgumentException("任务开始后,无法取消。" + Arrays.toString(jobIds.toArray())); //// throw new IllegalArgumentException("These tasks have an incorrect status and cannot be canceled. " //// + Arrays.toString(jobIds.toArray())); // } // Group job id by dock sn. Map> dockJobs = waylineJobs.stream() .collect(Collectors.groupingBy(WaylineJobDTO::getDockSn, Collectors.mapping(WaylineJobDTO::getJobId, Collectors.toList()))); dockJobs.forEach((dockSn, idList) -> this.publishCancelTask(dockSn, idList)); } /** * 发布取消任务 * * @param dockSn 设备序列号 * @param jobIds 任务ID列表 * @throws RuntimeException 如果设备离线或取消任务失败,则抛出运行时异常 */ @Override public void publishCancelTask(String dockSn, List jobIds) { boolean isOnline = deviceService.checkDeviceOnline(dockSn); if (!isOnline) { throw new RuntimeException("Dock is offline."); } String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + dockSn + TopicConst.SERVICES_SUF; CommonTopicResponse response = CommonTopicResponse.builder() .tid(UUID.randomUUID().toString()) .bid(UUID.randomUUID().toString()) .timestamp(System.currentTimeMillis()) .data(Map.of(MapKeyConst.FLIGHT_IDS, jobIds)) .method(WaylineMethodEnum.FLIGHT_TASK_CANCEL.getMethod()) .build(); ServiceReply serviceReply = messageSender.publishWithReply(topic, response); if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { log.info("Cancel job ====> Error code: {}", serviceReply.getResult()); throw new RuntimeException("Failed to cancel the wayline job of " + dockSn); } for (String jobId : jobIds) { this.updateJob(WaylineJobDTO.builder() .jobId(jobId) .status(WaylineJobStatusEnum.CANCEL.getVal()) .completedTime(LocalDateTime.now()) .build()); RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB_TIMED_EXECUTE, RedisConst.DELIMITER + dockSn + RedisConst.DELIMITER + jobId); // 原流程是修改表中数据的status状态值,现在的精力是直接删除 this.deleteTask(jobId); } } @Override public void deleteTask(String jobid) { mapper.delete(new LambdaQueryWrapper() .eq(WaylineJobEntity::getJobId, jobid)); } /** * 根据条件查询作业列表 * * @param jobIds 作业ID集合 * @param status 作业状态枚举 * @return 满足条件的作业DTO列表 */ @Override public List getJobsByConditions(Collection jobIds, WaylineJobStatusEnum status) { return mapper.selectList( new LambdaQueryWrapper() .eq(Objects.nonNull(status), WaylineJobEntity::getStatus, status.getVal()) .and(!CollectionUtils.isEmpty(jobIds), wrapper -> jobIds.forEach(id -> wrapper.eq(WaylineJobEntity::getJobId, id).or()))) .stream() .map(this::entity2Dto) .collect(Collectors.toList()); } /** * 根据任务ID获取任务详情 * * @param jobId 任务ID * @return Optional 包含任务详情的Optional对象,如果不存在则返回空的Optional */ @Override public Optional getJobByJobId(String jobId) { WaylineJobEntity jobEntity = mapper.selectOne( new LambdaQueryWrapper() .eq(WaylineJobEntity::getJobId, jobId)); return Optional.ofNullable(entity2Dto(jobEntity)); } /** * 更新作业信息 * * @param dto 作业信息传输对象 * @return 更新成功返回true,否则返回false */ @Override public Boolean updateJob(WaylineJobDTO dto) { return mapper.update(this.dto2Entity(dto), new LambdaUpdateWrapper() .eq(WaylineJobEntity::getJobId, dto.getJobId())) > 0; } /** * 根据工作区ID获取工作线任务分页数据 * * @param page 页码,从1开始 * @param pageSize 每页显示条数 * @return PaginationData 包含工作线任务分页数据的对象 */ @Override public PaginationData getJobsByWorkspaceId(long page, long pageSize) { Page pageData = mapper.selectPage( new Page(page, pageSize), new LambdaQueryWrapper() .orderByDesc(WaylineJobEntity::getId)); List records = pageData.getRecords() .stream() .map(this::entity2Dto) .collect(Collectors.toList()); return new PaginationData(records, new Pagination(pageData)); } /** * 根据条件筛选获取任务分页数据 * * @param page 页码,从1开始 * @param pageSize 每页显示条数 * @return PaginationData 包含工作线任务分页数据的对象 */ @Override public PaginationData getJobsList(long page, long pageSize, Integer status, Long startTime, Long endTime, Integer taskType, String name, Boolean isComplete) { Page pageData = mapper.selectPage( new Page(page, pageSize), new LambdaQueryWrapper() // 实际开始时间筛选,允许为空,为空则不进行筛选 .ge(startTime != null, WaylineJobEntity::getBeginTime, startTime) .le(endTime != null, WaylineJobEntity::getBeginTime, endTime) // 任务类型筛选,允许为空,为空则不进行筛选 .eq(taskType != null, WaylineJobEntity::getTaskType, taskType) // 任务名称筛选,允许为空,为空则不进行筛选;....模糊查询 .like(name != null, WaylineJobEntity::getName, "%"+name+"%") // 根据是否完成筛选,允许为空,为空则不进行筛选 .eq(isComplete != null && isComplete, WaylineJobEntity::getStatus, 3) .ne(isComplete != null && !isComplete, WaylineJobEntity::getStatus, 3) // 任务状态筛选,允许为空,为空则不进行筛选 .eq(status != null, WaylineJobEntity::getStatus, status) .orderByDesc(WaylineJobEntity::getId)); List records = pageData.getRecords() .stream() .map(this::entity2Dto) .collect(Collectors.toList()); return new PaginationData(records, new Pagination(pageData)); } public List selectList(Long taskType, Long executeTime, String parentId) { return mapper.selectList(new LambdaQueryWrapper() .eq(taskType != null,WaylineJobEntity::getTaskType, taskType) .ne(WaylineJobEntity::getStatus, WaylineJobStatusEnum.SUCCESS.getVal()) .eq(executeTime != null,WaylineJobEntity::getBeginTime, executeTime) .eq(parentId != null,WaylineJobEntity::getParentId,parentId) .ne(WaylineJobEntity::getStatus, 3) .orderByDesc(WaylineJobEntity::getId)); } /** * 根据航班任务ID获取航班任务资源 * * @param receiver 接收到的消息对象 * @param headers 消息头信息 * @Override 覆盖父类方法 * @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_FLIGHT_TASK_RESOURCE_GET, outputChannel = ChannelName.OUTBOUND) * 指定输入通道和输出通道 * @Transactional(isolation = Isolation.READ_UNCOMMITTED) * 设置事务隔离级别为未提交读 */ @Override @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_FLIGHT_TASK_RESOURCE_GET, outputChannel = ChannelName.OUTBOUND) @Transactional(isolation = Isolation.READ_UNCOMMITTED) public void flightTaskResourceGet(CommonTopicReceiver receiver, MessageHeaders headers) { Map jobIdMap = objectMapper.convertValue(receiver.getData(), new TypeReference>() { }); String jobId = jobIdMap.get(MapKeyConst.FLIGHT_ID); log.info("机场通过flightTaskResourceGet获取航线文件地址,jobId: " + jobId); CommonTopicResponse.CommonTopicResponseBuilder builder = CommonTopicResponse.builder() .tid(receiver.getTid()) .bid(receiver.getBid()) .method(RequestsMethodEnum.FLIGHT_TASK_RESOURCE_GET.getMethod()) .timestamp(System.currentTimeMillis()); String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString() + TopicConst._REPLY_SUF; DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway()); Optional waylineJobOpt = this.getJobByJobId(jobId); if (waylineJobOpt.isEmpty()) { builder.data(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); messageSender.publish(topic, builder.build()); return; } WaylineJobDTO waylineJob = waylineJobOpt.get(); // get wayline file // Optional waylineFile = waylineFileService.getWaylineByWaylineId(waylineJob.getFileId()); // if (waylineFile.isEmpty()) { // builder.data(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); // messageSender.publish(topic, builder.build()); // return; // } // 修改航线文件名称,从ard_route_info航线表中取; // 换航线表 ArdRouteInfo ardRouteOpt = _ardRouteInfoService.selectArdRouteInfoById(waylineJob.getFileId()); if (ardRouteOpt == null) { builder.data(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); messageSender.publish(topic, builder.build()); return; } // get file url URL url = null; String minioUrl = subStringMinioUrlName(ardRouteOpt.getPath()); String md5 = ""; try (InputStream inputStream = _ossService.getObject(kmzPath, minioUrl)) { md5 = DigestUtils.md5Hex(inputStream); url = new URL(ardRouteOpt.getPath()); log.info("机场通过flightTaskResourceGet获取航线文件地址,jobId: " + jobId + " 返回信息为url:" + url.toString() + " md5:" + md5); // } // try { // url = waylineFileService.getObjectUrl(waylineFile.get().getWaylineId()); // url = new URL(ardRouteOpt.getUpPath()); builder.data(RequestsReply.success(WaylineTaskCreateDTO.builder() .file(WaylineTaskFileDTO.builder() .url(url.toString()) // .fingerprint(waylineFile.get().getSign()) .fingerprint(md5) .build()) .build())); } catch (NullPointerException | IOException e) { e.printStackTrace(); builder.data(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); messageSender.publish(topic, builder.build()); return; } // 下发参数打印 try{ String json = objectMapper.writeValueAsString(builder.build()); log.info("机场通过flighttask_resource_get获取航线文件的参数flightTask:{}", json); } catch (Exception e){ log.error("机场通过flighttask_resource_get获取航线文件的参数flightTask异常:{}", e.getMessage()); } messageSender.publish(topic, builder.build()); } /** * 上传媒体文件的最高优先级 * * @param jobId 作业ID * @throws RuntimeException 如果作业ID不合法或设置优先级失败 */ @Override public void uploadMediaHighestPriority(String jobId) { Optional jobOpt = getJobByJobId(jobId); if (jobOpt.isEmpty()) { throw new RuntimeException(CommonErrorEnum.ILLEGAL_ARGUMENT.getErrorMsg()); } String dockSn = jobOpt.get().getDockSn(); String key = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + dockSn; if (RedisOpsUtils.checkExist(key) && jobId.equals(((MediaFileCountDTO) RedisOpsUtils.get(key)).getJobId())) { return; } ServiceReply reply = messageSender.publishWithReply(TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + dockSn + TopicConst.SERVICES_SUF, CommonTopicResponse.builder() .tid(UUID.randomUUID().toString()) .bid(UUID.randomUUID().toString()) .timestamp(System.currentTimeMillis()) .method(MediaMethodEnum.UPLOAD_FLIGHT_TASK_MEDIA_PRIORITIZE.getMethod()) .data(Map.of(MapKeyConst.FLIGHT_ID, jobId)) .build()); if (ResponseResult.CODE_SUCCESS != reply.getResult()) { throw new RuntimeException("Failed to set media job upload priority. Error Code: " + reply.getResult()); } } /** * 将WaylineJobDTO对象转换为WaylineJobEntity对象 * * @param dto WaylineJobDTO对象 * @return WaylineJobEntity对象 */ private WaylineJobEntity dto2Entity(WaylineJobDTO dto) { WaylineJobEntity.WaylineJobEntityBuilder builder = WaylineJobEntity.builder(); if (dto == null) { return builder.build(); } if (Objects.nonNull(dto.getBeginTime())) { builder.beginTime(dto.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); } if (Objects.nonNull(dto.getEndTime())) { builder.endTime(dto.getEndTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); } if (Objects.nonNull(dto.getExecuteTime())) { builder.executeTime(dto.getExecuteTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); } if (Objects.nonNull(dto.getCompletedTime())) { builder.completedTime(dto.getCompletedTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); } return builder.status(dto.getStatus()) .mediaCount(dto.getMediaCount()) .name(dto.getJobName()) .errorCode(dto.getCode()) .jobId(dto.getJobId()) .fileId(dto.getFileId()) .dockSn(dto.getDockSn()) .taskType(dto.getTaskType()) .waylineType(dto.getWaylineType()) .username(dto.getUsername()) .rthAltitude(dto.getRthAltitude()) .outOfControlAction(dto.getOutOfControlAction()) .parentId(dto.getParentId()) .loopSum(dto.getLoopSum()) .build(); } /** * 将实体对象转换为数据传输对象 * * @param entity WaylineJobEntity 实体对象 * @return WaylineJobDTO 数据传输对象 */ private WaylineJobDTO entity2Dto(WaylineJobEntity entity) { if (entity == null) { return null; } // 修改航线文件名称,从ard_route_info航线表中取; String fileId = entity.getFileId(); String fileName = ""; try { if (fileId != null) { ArdRouteInfo ardRouteInfo = _ardRouteInfoService.selectArdRouteInfoById(fileId); if (ardRouteInfo != null) { fileName = Optional.ofNullable(ardRouteInfo.getName()).orElse(""); } } } catch (Exception e) { log.info("获取航线名称失败,fileId:{},异常原因:{}", fileId,e.getMessage()); fileName = ""; } WaylineJobDTO.WaylineJobDTOBuilder builder = WaylineJobDTO.builder() .jobId(entity.getJobId()) .jobName(entity.getName()) .fileId(entity.getFileId()) // .fileName(waylineFileService.getWaylineByWaylineId(entity.getFileId()) // .orElse(WaylineFileDTO.builder().build()).getName()) .fileName(fileName) .dockSn(entity.getDockSn()) .dockName(deviceService.getDeviceBySn(entity.getDockSn()) .orElse(DeviceDTO.builder().build()).getNickname()) .username(entity.getUsername()) .status(WaylineJobStatusEnum.IN_PROGRESS.getVal() == entity.getStatus() && RedisOpsUtils.checkExist(RedisConst.WAYLINE_JOB_PAUSED_PREFIX + entity.getJobId()) ? WaylineJobStatusEnum.PAUSED.getVal() : entity.getStatus()) .code(entity.getErrorCode()) .beginTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getBeginTime()), ZoneId.systemDefault())) .endTime(Objects.nonNull(entity.getEndTime()) ? LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getEndTime()), ZoneId.systemDefault()) : null) .executeTime(Objects.nonNull(entity.getExecuteTime()) ? LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getExecuteTime()), ZoneId.systemDefault()) : null) .completedTime(WaylineJobStatusEnum.find(entity.getStatus()).getEnd() ? LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getUpdateTime()), ZoneId.systemDefault()) : null) .taskType(entity.getTaskType()) .waylineType(entity.getWaylineType()) .rthAltitude(entity.getRthAltitude()) .outOfControlAction(entity.getOutOfControlAction()) .mediaCount(entity.getMediaCount()) .loopSum(entity.getLoopSum()); if (Objects.nonNull(entity.getEndTime())) { builder.endTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getEndTime()), ZoneId.systemDefault())); } if (WaylineJobStatusEnum.IN_PROGRESS.getVal() == entity.getStatus() && RedisOpsUtils.getExpire(entity.getJobId()) > 0) { EventsReceiver taskProgress = (EventsReceiver) RedisOpsUtils.get(RedisConst.WAYLINE_JOB_RUNNING_PREFIX + entity.getDockSn()); if (Objects.nonNull(taskProgress.getOutput()) && Objects.nonNull(taskProgress.getOutput().getProgress())) { builder.progress(taskProgress.getOutput().getProgress().getPercent()); } } if (entity.getMediaCount() == 0) { return builder.build(); } // sync the number of media files String key = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + entity.getDockSn(); String countKey = RedisConst.MEDIA_FILE_PREFIX + entity.getDockSn(); Object mediaFileCount = RedisOpsUtils.hashGet(countKey, entity.getJobId()); if (Objects.nonNull(mediaFileCount)) { builder.uploadedCount(((MediaFileCountDTO) mediaFileCount).getUploadedCount()) .uploading(RedisOpsUtils.checkExist(key) && entity.getJobId().equals(((MediaFileCountDTO) RedisOpsUtils.get(key)).getJobId())); return builder.build(); } int uploadedSize = fileService.getFilesByWorkspaceAndJobId(entity.getJobId()).size(); // All media for this job have been uploaded. if (uploadedSize >= entity.getMediaCount()) { return builder.uploadedCount(uploadedSize).build(); } RedisOpsUtils.hashSet(countKey, entity.getJobId(), MediaFileCountDTO.builder() .jobId(entity.getJobId()) .mediaCount(entity.getMediaCount()) .uploadedCount(uploadedSize).build()); return builder.build(); } }