本文主要参考大疆文档机场航线管理对执行航线任务进行具体拆分,以最简单的立即执行模式进行分析。(请提前准备好航线文件导入航线管理中)
- 下发任务(调用发布任务方法)
@Override
public HttpResultResponse publishFlightTask(CreateJobParam param, CustomClaim customClaim) throws SQLException {
// 如果是立即执行 填入taskDays和taskPeriods 为当前时间
// taskDays 代表执行日期 taskPeriods 代表执行时间段
fillImmediateTime(param);
// 获取具体的执行时间点
for (Long taskDay : param.getTaskDays()) {
LocalDate date = LocalDate.ofInstant(Instant.ofEpochSecond(taskDay), ZoneId.systemDefault());
for (List<Long> taskPeriod : param.getTaskPeriods()) {
long beginTime = LocalDateTime.of(date, LocalTime.ofInstant(Instant.ofEpochSecond(taskPeriod.get(0)), ZoneId.systemDefault()))
.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long endTime = taskPeriod.size() > 1 ?
LocalDateTime.of(date, LocalTime.ofInstant(Instant.ofEpochSecond(taskPeriod.get(1)), ZoneId.systemDefault()))
.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : beginTime; // 如果任务类型不是立即执行并且 执行时间小于当前系统时间 则不执行该时间点任务
if (TaskTypeEnum.IMMEDIATE != param.getTaskType() && endTime < System.currentTimeMillis()) {
continue;
}
// 创建航线任务
Optional<WaylineJobDTO> waylineJobOpt = waylineJobService.createWaylineJob(param, customClaim.getWorkspaceId(), customClaim.getUsername(), beginTime, endTime);
if (waylineJobOpt.isEmpty()) {
throw new SQLException("Failed to create wayline job.");
}
WaylineJobDTO waylineJob = waylineJobOpt.get();
// If it is a conditional task type, add conditions to the job parameters.
addConditions(waylineJob, param, beginTime, endTime);
// 发布航线任务
HttpResultResponse response = this.publishOneFlightTask(waylineJob);
if (HttpResultResponse.CODE_SUCCESS != response.getCode()) {
return response;
}
}
}
return HttpResultResponse.success();
}
/**
** 发布一个航线任务
*/
public HttpResultResponse publishOneFlightTask(WaylineJobDTO waylineJob) throws SQLException {
// redis 中判断设备是否在线
boolean isOnline = deviceRedisService.checkDeviceOnline(waylineJob.getDockSn());
if (!isOnline) {
throw new RuntimeException("Dock is offline.");
}
// 进行航线任务准备 下发任务 Topic: thing/product/{gateway_sn}/services Method: flighttask_prepare
boolean isSuccess = this.prepareFlightTask(waylineJob);
if (!isSuccess) {
return HttpResultResponse.error("Failed to prepare job.");
}
// Issue an immediate task execution command.
if (TaskTypeEnum.IMMEDIATE == waylineJob.getTaskType()) {
// 立即执行 航线任务
if (!executeFlightTask(waylineJob.getWorkspaceId(), waylineJob.getJobId())) {
return HttpResultResponse.error("Failed to execute job.");
}
}
// 定时任务放入redis中,定时扫描
if (TaskTypeEnum.TIMED == waylineJob.getTaskType()) {
// key: wayline_job_timed, value: {workspace_id}:{dock_sn}:{job_id}
boolean isAdd = RedisOpsUtils.zAdd(RedisConst.WAYLINE_JOB_TIMED_EXECUTE,
waylineJob.getWorkspaceId() + RedisConst.DELIMITER + waylineJob.getDockSn() + RedisConst.DELIMITER + waylineJob.getJobId(),
waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
if (!isAdd) {
return HttpResultResponse.error("Failed to create scheduled job.");
}
}
return HttpResultResponse.success();
/**
* 航线任务准备
*/
private Boolean prepareFlightTask(WaylineJobDTO waylineJob) throws SQLException {
// 获取航线文件
Optional<GetWaylineListResponse> waylineFile = waylineFileService.getWaylineByWaylineId(waylineJob.getWorkspaceId(), waylineJob.getFileId());
if (waylineFile.isEmpty()) {
// 航线文件不存在
throw new SQLException("Wayline file doesn't exist.");
}
// get file url
URL url = waylineFileService.getObjectUrl(waylineJob.getWorkspaceId(), waylineFile.get().getId());
FlighttaskPrepareRequest flightTask = new FlighttaskPrepareRequest()
.setFlightId(waylineJob.getJobId())
.setExecuteTime(waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli())
.setTaskType(waylineJob.getTaskType())
.setWaylineType(waylineJob.getWaylineType())
.setRthAltitude(waylineJob.getRthAltitude())
.setOutOfControlAction(waylineJob.getOutOfControlAction())
.setExitWaylineWhenRcLost(ExitWaylineWhenRcLostEnum.EXECUTE_RC_LOST_ACTION)
.setFile(new FlighttaskFile()
.setUrl(url.toString())
.setFingerprint(waylineFile.get().getSign()));
if (TaskTypeEnum.CONDITIONAL == waylineJob.getTaskType()) {
if (Objects.isNull(waylineJob.getConditions())) {
throw new IllegalArgumentException();
}
flightTask.setReadyConditions(waylineJob.getConditions().getReadyConditions());
flightTask.setExecutableConditions(waylineJob.getConditions().getExecutableConditions());
}
// 发送消息 获取flighttaskPrepare的响应结果
TopicServicesResponse<ServicesReplyData> serviceReply = abstractWaylineService.flighttaskPrepare(
SDKManager.getDeviceSDK(waylineJob.getDockSn()), flightTask);
// 如果失败 则更新航线任务失败
if (!serviceReply.getData().getResult().isSuccess()) {
log.info("Prepare task ====> Error code: {}", serviceReply.getData().getResult());
waylineJobService.updateJob(WaylineJobDTO.builder()
.workspaceId(waylineJob.getWorkspaceId())
.jobId(waylineJob.getJobId())
.executeTime(LocalDateTime.now())
.status(WaylineJobStatusEnum.FAILED.getVal())
.completedTime(LocalDateTime.now())
.code(serviceReply.getData().getResult().getCode()).build());
return false;
}
return true;
}
/**
执行航线任务
**/
@Override
public Boolean executeFlightTask(String workspaceId, String jobId) {
// 查询航线任务
Optional<WaylineJobDTO> waylineJob = waylineJobService.getJobByJobId(workspaceId, jobId);
if (waylineJob.isEmpty()) {
throw new IllegalArgumentException("Job doesn't exist.");
}
// redis 中获取机场在线状态
boolean isOnline = deviceRedisService.checkDeviceOnline(waylineJob.get().getDockSn());
if (!isOnline) {
throw new RuntimeException("Dock is offline.");
}
WaylineJobDTO job = waylineJob.get();
// 下发执行航线任务 Topic:thing/product/{gateway_sn}/services Method: flighttask_execute
TopicServicesResponse<ServicesReplyData> serviceReply = abstractWaylineService.flighttaskExecute(
SDKManager.getDeviceSDK(job.getDockSn()), new FlighttaskExecuteRequest().setFlightId(jobId));
if (!serviceReply.getData().getResult().isSuccess()) {
log.info("Execute job ====> Error: {}", serviceReply.getData().getResult());
waylineJobService.updateJob(WaylineJobDTO.builder()
.jobId(jobId)
.executeTime(LocalDateTime.now())
.status(WaylineJobStatusEnum.FAILED.getVal())
.completedTime(LocalDateTime.now())
.code(serviceReply.getData().getResult().getCode()).build());
// The conditional task fails and enters the blocking status.
if (TaskTypeEnum.CONDITIONAL == job.getTaskType()
&& WaylineErrorCodeEnum.find(serviceReply.getData().getResult().getCode()).isBlock()) {
waylineRedisService.setBlockedWaylineJob(job.getDockSn(), jobId);
}
return false;
}
- 如果上述步骤没出错,那么通过监听 Topic:thing/product/{gateway_sn}/events_reply
Method: flighttask_progress 获取任务执行进度
@Override
public TopicEventsResponse<MqttReply> flighttaskProgress(TopicEventsRequest<EventsDataRequest<FlighttaskProgress>> response, MessageHeaders headers) {
// 获取任务进度相关消息
EventsReceiver<FlighttaskProgress> eventsReceiver = new EventsReceiver<>();
eventsReceiver.setResult(response.getData().getResult());
eventsReceiver.setOutput(response.getData().getOutput());
eventsReceiver.setBid(response.getBid());
eventsReceiver.setSn(response.getGateway());
FlighttaskProgress output = eventsReceiver.getOutput();
log.info("Task progress: {}", output.getProgress().toString());
if (!eventsReceiver.getResult().isSuccess()) {
log.error("Task progress ===> Error: " + eventsReceiver.getResult());
}
Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(response.getGateway());
if (deviceOpt.isEmpty()) {
return new TopicEventsResponse<>();
}
// 获取当前任务状态
FlighttaskStatusEnum statusEnum = output.getStatus();
// 更新redis中的运行中的航线任务
waylineRedisService.setRunningWaylineJob(response.getGateway(), eventsReceiver);
// 航线任务已结束
if (statusEnum.isEnd()) {
WaylineJobDTO job = WaylineJobDTO.builder()
.jobId(response.getBid())
.status(WaylineJobStatusEnum.SUCCESS.getVal())
.completedTime(LocalDateTime.now())
.mediaCount(output.getExt().getMediaCount())
.build();
// 航线飞行过程中是否有媒体文件(拍照或者录像)
if (Objects.nonNull(job.getMediaCount()) && job.getMediaCount() != 0) {
mediaRedisService.setMediaCount(response.getGateway(), job.getJobId(),
MediaFileCountDTO.builder().deviceSn(deviceOpt.get().getChildDeviceSn())
.jobId(response.getBid()).mediaCount(job.getMediaCount()).uploadedCount(0).build());
}
if (FlighttaskStatusEnum.OK != statusEnum) {
job.setCode(eventsReceiver.getResult().getCode());
job.setStatus(WaylineJobStatusEnum.FAILED.getVal());
}
// 更新航线任务
waylineJobService.updateJob(job);
// 根据sn删除redis中运行中的任务
waylineRedisService.delRunningWaylineJob(response.getGateway());
// 根据bid 删除redis中暂停的任务
waylineRedisService.delPausedWaylineJob(response.getBid());
}
// 发送websocket 消息通知前端
webSocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(),
BizCodeEnum.FLIGHT_TASK_PROGRESS.getCode(), eventsReceiver);
return new TopicEventsResponse<>();
}