package com.dji.sample.control.service.impl; import com.alibaba.fastjson.JSONObject; import com.dji.sample.common.error.CommonErrorEnum; import com.dji.sample.common.model.ResponseResult; import com.dji.sample.common.util.SpringBeanUtils; import com.dji.sample.component.mqtt.model.*; import com.dji.sample.component.mqtt.service.IMessageSenderService; import com.dji.sample.component.redis.RedisConst; import com.dji.sample.component.redis.RedisOpsUtils; import com.dji.sample.component.websocket.model.BizCodeEnum; import com.dji.sample.component.websocket.service.ISendMessageService; import com.dji.sample.control.model.dto.FlyToProgressReceiver; import com.dji.sample.control.model.dto.PointDTO; import com.dji.sample.control.model.dto.ResultNotifyDTO; import com.dji.sample.control.model.dto.TakeoffProgressReceiver; import com.dji.sample.control.model.enums.*; import com.dji.sample.control.model.param.*; import com.dji.sample.control.service.IControlService; import com.dji.sample.manage.model.dto.DeviceDTO; import com.dji.sample.manage.model.enums.DeviceModeCodeEnum; import com.dji.sample.manage.model.enums.DockModeCodeEnum; import com.dji.sample.manage.model.enums.UserTypeEnum; import com.dji.sample.manage.service.IDevicePayloadService; import com.dji.sample.manage.service.IDeviceRedisService; import com.dji.sample.manage.service.IDeviceService; import com.dji.sample.wayline.model.enums.WaylineErrorCodeEnum; import com.dji.sample.wayline.model.param.PointPOJO; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.messaging.MessageHeaders; import org.springframework.stereotype.Service; import java.util.*; /** * @author sean * @version 1.2 * @date 2022/7/29 */ @Service @Slf4j public class ControlServiceImpl implements IControlService { @Autowired private IMessageSenderService messageSenderService; @Autowired private ISendMessageService webSocketMessageService; @Autowired private IDeviceService deviceService; @Autowired private IDeviceRedisService deviceRedisService; @Autowired private ObjectMapper mapper; @Autowired private IDevicePayloadService devicePayloadService; private RemoteDebugHandler checkDebugCondition(String sn, RemoteDebugParam param, RemoteDebugMethodEnum controlMethodEnum) { RemoteDebugHandler handler = Objects.nonNull(controlMethodEnum.getClazz()) ? mapper.convertValue(Objects.nonNull(param) ? param : new Object(), controlMethodEnum.getClazz()) : new RemoteDebugHandler(); if (!handler.canPublish(sn)) { throw new RuntimeException("当前的机场状态不支持此功能。"); } if (Objects.nonNull(param) && !handler.valid()) { throw new RuntimeException(CommonErrorEnum.ILLEGAL_ARGUMENT.getErrorMsg()); } return handler; } @Override public ResponseResult controlDockDebug(String sn, String serviceIdentifier, RemoteDebugParam param) { RemoteDebugMethodEnum controlMethodEnum = RemoteDebugMethodEnum.find(serviceIdentifier); if (RemoteDebugMethodEnum.UNKNOWN == controlMethodEnum) { return ResponseResult.error(" 没有找到" + serviceIdentifier + " 这个指令。"); } RemoteDebugHandler data = checkDebugCondition(sn, param, controlMethodEnum); boolean isExist = deviceRedisService.checkDeviceOnline(sn); if (!isExist) { return ResponseResult.error("设备离线"); } String bid = UUID.randomUUID().toString(); ServiceReply serviceReply = messageSenderService.publishServicesTopic(sn, serviceIdentifier, data, bid); if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { return ResponseResult.error(serviceReply.getResult(), "error: " + serviceIdentifier + serviceReply.getResult()); } if (controlMethodEnum.getProgress()) { RedisOpsUtils.setWithExpire(serviceIdentifier + RedisConst.DELIMITER + bid, sn, RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND); } return ResponseResult.success(); } /** * Handles multi-state command progress information. * * @param receiver * @param headers * @return */ @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) public CommonTopicReceiver handleControlProgress(CommonTopicReceiver receiver, MessageHeaders headers) { String key = receiver.getMethod() + RedisConst.DELIMITER + receiver.getBid(); if (RedisOpsUtils.getExpire(key) <= 0) { return receiver; } String sn = RedisOpsUtils.get(key).toString(); EventsReceiver eventsReceiver = mapper.convertValue(receiver.getData(), new TypeReference>() { }); eventsReceiver.setBid(receiver.getBid()); eventsReceiver.setSn(sn); log.info("SN: {}, {} ===> Control progress: {}", sn, receiver.getMethod(), eventsReceiver.getOutput().getProgress().toString()); if (eventsReceiver.getResult() != ResponseResult.CODE_SUCCESS) { log.error("SN: {}, {} ===> Error code: {}", sn, receiver.getMethod(), eventsReceiver.getResult()); } if (eventsReceiver.getOutput().getProgress().getPercent() == 100 || EventsResultStatusEnum.find(eventsReceiver.getOutput().getStatus()).getEnd()) { RedisOpsUtils.del(key); } Optional deviceOpt = deviceRedisService.getDeviceOnline(sn); if (deviceOpt.isEmpty()) { throw new RuntimeException("设备离线."); } DeviceDTO device = deviceOpt.get(); webSocketMessageService.sendBatch(device.getWorkspaceId(), UserTypeEnum.WEB.getVal(), receiver.getMethod(), eventsReceiver); return receiver; } private void checkFlyToCondition(String dockSn) { // TODO 设备固件版本不兼容情况 Optional dockOpt = deviceRedisService.getDeviceOnline(dockSn); if (dockOpt.isEmpty()) { throw new RuntimeException("机场离线请重启机场"); } DeviceModeCodeEnum deviceMode = deviceService.getDeviceMode(dockOpt.get().getChildDeviceSn()); if (DeviceModeCodeEnum.MANUAL != deviceMode) { throw new RuntimeException("无人机当前状态不支持此功能,请稍后再试"); } ResponseResult result = seizeAuthority(dockSn, DroneAuthorityEnum.FLIGHT, null); if (ResponseResult.CODE_SUCCESS != result.getCode()) { throw new IllegalArgumentException(result.getMessage()); } } @Override public ResponseResult flyToPoint(String sn, FlyToPointParam param) { checkFlyToCondition(sn); param.setFlyToId(UUID.randomUUID().toString()); ServiceReply reply = messageSenderService.publishServicesTopic(sn, DroneControlMethodEnum.FLY_TO_POINT.getMethod(), param, param.getFlyToId()); return ResponseResult.CODE_SUCCESS != reply.getResult() ? ResponseResult.error("飞向目标点失败。" + reply.getResult()) : ResponseResult.success(); } @Override public ResponseResult flyToPointStop(String sn) { ServiceReply reply = messageSenderService.publishServicesTopic(sn, DroneControlMethodEnum.FLY_TO_POINT_STOP.getMethod(), null); return ResponseResult.CODE_SUCCESS != reply.getResult() ? ResponseResult.error("飞向目标点的无人机停止失败" + reply.getResult()) : ResponseResult.success(); } @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLY_TO_POINT_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) public CommonTopicReceiver handleFlyToPointProgress(CommonTopicReceiver receiver, MessageHeaders headers) throws Exception { String dockSn = receiver.getGateway(); Optional deviceOpt = deviceRedisService.getDeviceOnline(dockSn); if (deviceOpt.isEmpty()) { log.error("机场离线"); return null; } FlyToProgressReceiver eventsReceiver = mapper.convertValue(receiver.getData(), new TypeReference() { }); webSocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), BizCodeEnum.FLY_TO_POINT_PROGRESS.getCode(), ResultNotifyDTO.builder().sn(dockSn) .message(WaylineErrorCodeEnum.SUCCESS == eventsReceiver.getResult() ? eventsReceiver.getStatus().getMessage() : eventsReceiver.getResult().getErrorMsg()) .result(eventsReceiver.getResult().getErrorCode()) .build()); if (eventsReceiver.getStatus().equals(FlyToStatusEnum.WAYLINE_OK)) { JSONObject jsonObject = (JSONObject) RedisOpsUtils.get("tuban:" + dockSn); if (jsonObject != null) { List targetList = (List) jsonObject.get("targetList"); int curIndex = (Integer) jsonObject.get("curIndex"); String payloadIndex = jsonObject.getString("payloadIndex"); flyToNextPoint(targetList, curIndex, dockSn, payloadIndex); } } return receiver; } private ResponseResult flyToNextPoint(List targetList, int curIndex, String sn, String payloadIndex) throws Exception { curIndex = curIndex + 1; //当无人机状态为人工时再发布下一个命令 while (true) { Optional dockOpt = deviceRedisService.getDeviceOnline(sn); DeviceModeCodeEnum deviceMode = deviceService.getDeviceMode(dockOpt.get().getChildDeviceSn()); if (DeviceModeCodeEnum.MANUAL == deviceMode) { if (curIndex == targetList.size()) { //当前是最后一个点,返航 ResponseResult returnHome = controlDockDebug(sn, "return_home", null); RedisOpsUtils.del("tuban:" + sn); return returnHome; } else { //当前不是最后一个点,飞行到下一个点 FlyToPointParam flyToPointParam = new FlyToPointParam(); flyToPointParam.setMaxSpeed(14); List pointDTOS = new ArrayList<>(); PointDTO pointDTO = new PointDTO(); pointDTO.setHeight(150.0); pointDTO.setLongitude(targetList.get(curIndex).getLon()); pointDTO.setLatitude(targetList.get(curIndex).getLat()); pointDTOS.add(pointDTO); flyToPointParam.setPoints(pointDTOS); //执行拍照 ResponseResult responseResult = takePhoto(sn, payloadIndex); //发布下一个飞行指令 ResponseResult flyToRes = flyToPoint(sn, flyToPointParam); if (flyToRes.getCode() == ResponseResult.CODE_SUCCESS) { JSONObject jsonObject = new JSONObject(); jsonObject.put("targetList", targetList); jsonObject.put("curIndex", curIndex); jsonObject.put("payloadIndex",payloadIndex); RedisOpsUtils.set("tuban:" + sn, jsonObject); } return flyToRes; } } } } @Override public ResponseResult takePhoto(String sn, String payloadIndex) throws Exception { //获取负载控制权 DronePayloadParam dronePayloadParam = new DronePayloadParam(); dronePayloadParam.setPayloadIndex(payloadIndex); ResponseResult seizeAuthorityRes = seizeAuthority(sn, DroneAuthorityEnum.PAYLOAD, dronePayloadParam); //切换为相机模式 if (seizeAuthorityRes.getCode() != ResponseResult.CODE_SUCCESS) { return seizeAuthorityRes; } PayloadCommandsParam payloadCommandsParam = new PayloadCommandsParam(); // DronePayloadParam switchParam = new DronePayloadParam(); // switchParam.setCameraMode(CameraModeEnum.PHOTO); // switchParam.setPayloadIndex(payloadIndex); // payloadCommandsParam.setSn(sn); // payloadCommandsParam.setCmd(PayloadCommandsEnum.CAMERA_MODE_SWitCH); // payloadCommandsParam.setData(switchParam); // // ResponseResult switchModeRes = payloadCommands(payloadCommandsParam); // if (switchModeRes.getCode() != ResponseResult.CODE_SUCCESS){ // return switchModeRes; // } //拍照 payloadCommandsParam.setCmd(PayloadCommandsEnum.CAMERA_PHOTO_TAKE); DronePayloadParam takePhotoParam = new DronePayloadParam(); takePhotoParam.setPayloadIndex(payloadIndex); payloadCommandsParam.setData(takePhotoParam); payloadCommandsParam.setSn(sn); ResponseResult responseResult = payloadCommands(payloadCommandsParam); return responseResult; } private void checkTakeoffCondition(String dockSn) { Optional dockOpt = deviceRedisService.getDeviceOnline(dockSn); if (dockOpt.isEmpty() || DockModeCodeEnum.IDLE != deviceService.getDockMode(dockSn)) { throw new RuntimeException("当前状态不支持起飞"); } ResponseResult result = seizeAuthority(dockSn, DroneAuthorityEnum.FLIGHT, null); if (ResponseResult.CODE_SUCCESS != result.getCode()) { throw new IllegalArgumentException(result.getMessage()); } } @Override public ResponseResult takeoffToPoint(String sn, TakeoffToPointParam param) { checkTakeoffCondition(sn); param.setFlightId(UUID.randomUUID().toString()); ServiceReply reply = messageSenderService.publishServicesTopic(sn, DroneControlMethodEnum.TAKE_OFF_TO_POINT.getMethod(), param, param.getFlightId()); return ResponseResult.CODE_SUCCESS != reply.getResult() ? ResponseResult.error("无人机起飞失败 " + reply.getResult()) : ResponseResult.success(); } @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_TAKE_OFF_TO_POINT_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) public CommonTopicReceiver handleTakeoffToPointProgress(CommonTopicReceiver receiver, MessageHeaders headers) { String dockSn = receiver.getGateway(); Optional deviceOpt = deviceRedisService.getDeviceOnline(dockSn); if (deviceOpt.isEmpty()) { log.error("机场离线"); return null; } TakeoffProgressReceiver eventsReceiver = mapper.convertValue(receiver.getData(), new TypeReference() { }); webSocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), BizCodeEnum.TAKE_OFF_TO_POINT_PROGRESS.getCode(), ResultNotifyDTO.builder().sn(dockSn) .message(WaylineErrorCodeEnum.SUCCESS == eventsReceiver.getResult() ? eventsReceiver.getStatus().getMessage() : eventsReceiver.getResult().getErrorMsg()) .result(eventsReceiver.getResult().getErrorCode()) .build()); return receiver; } @Override public ResponseResult seizeAuthority(String sn, DroneAuthorityEnum authority, DronePayloadParam param) { String method; switch (authority) { case FLIGHT: if (deviceService.checkAuthorityFlight(sn)) { return ResponseResult.success(); } method = DroneControlMethodEnum.FLIGHT_AUTHORITY_GRAB.getMethod(); break; case PAYLOAD: if (checkPayloadAuthority(sn, param.getPayloadIndex())) { return ResponseResult.success(); } method = DroneControlMethodEnum.PAYLOAD_AUTHORITY_GRAB.getMethod(); break; default: return ResponseResult.error(CommonErrorEnum.ILLEGAL_ARGUMENT); } ServiceReply serviceReply = messageSenderService.publishServicesTopic(sn, method, param); return ResponseResult.CODE_SUCCESS != serviceReply.getResult() ? ResponseResult.error(serviceReply.getResult(), "方法: " + method + " 错误码:" + serviceReply.getResult()) : ResponseResult.success(); } private Boolean checkPayloadAuthority(String sn, String payloadIndex) { Optional dockOpt = deviceRedisService.getDeviceOnline(sn); if (dockOpt.isEmpty()) { throw new RuntimeException("机场离线请重启机场"); } return devicePayloadService.checkAuthorityPayload(dockOpt.get().getChildDeviceSn(), payloadIndex); } @Override public ResponseResult payloadCommands(PayloadCommandsParam param) throws Exception { param.getCmd().getClazz() .getDeclaredConstructor(DronePayloadParam.class) .newInstance(param.getData()) .checkCondition(param.getSn()); ServiceReply serviceReply = messageSenderService.publishServicesTopic(param.getSn(), param.getCmd().getCmd(), param.getData()); return ResponseResult.CODE_SUCCESS != serviceReply.getResult() ? ResponseResult.error(serviceReply.getResult(), "错误码:" + serviceReply.getResult()) : ResponseResult.success(); } @Override public ResponseResult requestsConfig(String sn, String method, RequestsParam param) { ServiceReply serviceReply = messageSenderService.publishRequestsTopic(sn, method, param); return ResponseResult.CODE_SUCCESS != serviceReply.getResult() ? ResponseResult.error(serviceReply.getResult(), "错误码:" + serviceReply.getResult()) : ResponseResult.success(); } }