package com.ruoyi.device.controller; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.web.controller.BaseController; import com.ruoyi.device.api.domain.*; import com.ruoyi.device.api.enums.DroneCurrentStatusEnum; import com.ruoyi.device.api.enums.DroneMissionStatusEnum; import com.ruoyi.device.domain.impl.machine.MachineCommandManager; import com.ruoyi.device.domain.impl.machine.command.CommandResult; import com.ruoyi.device.domain.impl.machine.command.CommandType; import com.ruoyi.device.domain.impl.machine.state.MachineStates; import com.ruoyi.device.service.FlightService; import com.ruoyi.task.api.enums.StatusEnum; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.Objects; import java.util.UUID; import java.util.concurrent.CompletableFuture; /** * 无人机飞控Controller * * @author ruoyi * @date 2026-02-04 */ @Slf4j @Tag(name = "无人机飞控管理", description = "无人机飞控相关接口") @RestController @RequestMapping("/drone") public class AircraftFlyController extends BaseController { @Autowired private MachineCommandManager machineCommandManager; @Autowired private com.ruoyi.device.domain.impl.machine.statemachine.MachineStateManager machineStateManager; @Autowired private FlightService flightService; /** * 无人机飞控命令 * * @param request 飞控命令请求 * @return 结果 */ @Operation(summary = "无人机飞控命令", description = "发送飞控指令") @PostMapping("/flight-control") public R flightControl(@RequestBody DroneFlightControlRequest request) { if (request.getCommand() == null || request.getSn() == null) { return R.fail("飞控命令和机场SN号不能为空"); } String sn = request.getSn(); log.info("收到飞控命令: sn={}, command={}", sn, request.getCommand()); try { CommandType commandType; java.util.Map params = new java.util.HashMap<>(); // 处理消息ID if (request.getMessageID() != null) { params.put("messageID", request.getMessageID()); } else { params.put("messageID", System.currentTimeMillis()); } // 处理扩展参数 if (request.getEvalue() != null) { params.put("evalue", request.getEvalue()); } if (request.getValue() != null) { params.put("value", request.getValue()); } if (request.getLightMode() != null) { params.put("lightMode", request.getLightMode()); } // 处理航线飞行、悬停、继续任务所需的参数 if (request.getAirlineFileUrl() != null) { params.put("airlineFileUrl", request.getAirlineFileUrl()); } if (request.getFlyBatteryMin() != null) { params.put("flyBatteryMin", request.getFlyBatteryMin()); } if (request.getIsMustFly() != null) { params.put("isMustFly", request.getIsMustFly()); } if (request.getTaskId() != null) { params.put("taskId", request.getTaskId()); } if (request.getZhilin() != null) { params.put("zhilin", request.getZhilin()); } switch (request.getCommand()) { case FORWARD: commandType = CommandType.FORWARD; break; case BACKWARD: commandType = CommandType.BACKWARD; break; case LEFT: commandType = CommandType.LEFT; break; case RIGHT: commandType = CommandType.RIGHT; break; case ROTATE_LEFT: commandType = CommandType.ROTATE_LEFT; break; case ROTATE_RIGHT: commandType = CommandType.ROTATE_RIGHT; break; case UP: commandType = CommandType.UP; break; case DOWN: commandType = CommandType.DOWN; break; case SWITCH_VISIBLE_LIGHT: commandType = CommandType.SWITCH_VISIBLE_LIGHT; break; case GIMBAL_ZOOM: commandType = CommandType.GIMBAL_ZOOM; break; case SWITCH_IR: commandType = CommandType.SWITCH_IR; break; case SWITCH_WIDE_ANGLE: commandType = CommandType.SWITCH_WIDE_ANGLE; break; case GIMBAL_MOVE_RIGHT: commandType = CommandType.GIMBAL_MOVE_RIGHT; break; case GIMBAL_MOVE_LEFT: commandType = CommandType.GIMBAL_MOVE_LEFT; break; case GIMBAL_PITCH_UP: commandType = CommandType.GIMBAL_PITCH_UP; break; case GIMBAL_PITCH_DOWN: commandType = CommandType.GIMBAL_PITCH_DOWN; break; case GIMBAL_RESET: commandType = CommandType.GIMBAL_RESET; break; case AIRLINE_FLIGHT: commandType = CommandType.AIRLINE_FLIGHT; break; case HOVER: commandType = CommandType.HOVER; break; case CONTINUE_TASK: commandType = CommandType.CONTINUE_TASK; break; case EMERGENCY_STOP: return R.fail("急停命令暂不支持"); default: return R.fail("不支持的飞控命令"); } CompletableFuture future = machineCommandManager.executeCommand(sn, commandType, params); CommandResult result = future.get(); if (result.isSuccess()) { log.info("飞控命令执行成功: sn={}, command={}", sn, request.getCommand()); return R.ok(); } else { log.error("飞控命令执行失败: sn={}, command={}, reason={}", sn, request.getCommand(), result.getErrorMessage()); return R.fail("飞控命令执行失败: " + result.getErrorMessage()); } } catch (Exception e) { log.error("飞控命令执行异常: sn={}, command={}", sn, request.getCommand(), e); return R.fail("飞控命令执行异常: " + e.getMessage()); } } /** * 无人机实时信息展示 * * @param taskId 任务ID * @return 实时信息 */ @Operation(summary = "无人机实时信息展示", description = "根据任务ID获取无人机的实时飞行信息,包括速度、高度、姿态角等") @GetMapping("/realtime-info/{taskId}") public R getRealtimeInfo( @Parameter(description = "任务ID", required = true, example = "1") @PathVariable("taskId") Long taskId) { // TODO: 实现获取实时信息逻辑 DroneRealtimeInfoVO vo = new DroneRealtimeInfoVO(); vo.setClimbSpeed(0); vo.setCruiseSpeed(0); vo.setDistanceToAirport(0); vo.setAltitude(0); vo.setPitch(0); vo.setYaw(0); vo.setGimbalPitch(0); vo.setGimbalYaw(0); vo.setMissionStatus(DroneMissionStatusEnum.IDLE); return R.ok(vo); } /** * 无人机当前状态查询 * * @param dockId 机场ID * @return 当前状态 */ @Operation(summary = "无人机当前状态查询", description = "根据机场ID查询无人机的当前飞行状态") @GetMapping("/current-status/{dockId}") public R getCurrentStatus( @Parameter(description = "机场ID", required = true, example = "1") @PathVariable("dockId") Long dockId) { // TODO: 实现查询当前状态逻辑 DroneCurrentStatusVO vo = new DroneCurrentStatusVO(); vo.setDockId(dockId); vo.setCurrentStatus(DroneCurrentStatusEnum.HOVERING); return R.ok(vo); } //从配置文件获取 private final static String airlineFileUrl = "https://minio-dx.t-aaron.com:2443/th-airport/testFile/191ec54c-062c-4828-aab6-cefc901add78.waypoints"; /** * 无人机一键起飞 * * @param request 起飞请求对象 * @return 起飞响应 */ @Operation(summary = "无人机一键起飞", description = "控制指定机场的无人机执行起飞操作") @PostMapping("/takeoff") public R takeoff(@RequestBody DroneTakeoffRequest request) { // Long taskId = flightService.createClickTakeOffTask(request.getSn(),airlineFileUrl); log.info("一键起飞,生成一键起飞任务 sn={} ", request.getSn()); try { java.util.Map params = new java.util.HashMap<>(); params.put("airlineFileUrl", airlineFileUrl); params.put("flyBatteryMin", request.getFlyBatteryMin()); params.put("messageID", request.getTaskId()); CompletableFuture future = machineCommandManager.executeCommand(request.getSn(), CommandType.TAKE_OFF, params); CommandResult result = future.get(); if (result.isSuccess()) { log.info("无人机起飞成功: sn={}", request.getSn()); flightService.updateFlightStatus(request.getTaskId(), StatusEnum.CHECKING); return R.ok("起飞命令执行成功"); } else { log.error("无人机起飞失败: sn={}, reason={}", request.getSn(), result.getErrorMessage()); flightService.updateFlightStatus(request.getTaskId(), StatusEnum.FAILED); return R.fail("起飞命令执行失败: " + result.getErrorMessage()); } } catch (Exception e) { log.error("无人机起飞异常: sn={}", request.getSn(), e); flightService.updateFlightStatus(request.getTaskId(), StatusEnum.FAILED); return R.fail("起飞命令执行异常: " + e.getMessage()); } } /** * 无人机开机接口 * * @param sn 机场SN号 * @return 开机响应 */ @Operation(summary = "无人机开机", description = "控制指定机场的无人机执行开机操作") @PostMapping("/power-on/{sn}") public R powerOn( @Parameter(description = "机场SN号", required = true, example = "THJSQ03B2309DN7VQN43") @PathVariable("sn") String sn) { log.info("收到无人机开机请求: sn={}", sn); try { // 调用机器命令管理器执行开机命令 CompletableFuture future = machineCommandManager.executeCommand(sn, CommandType.POWER_ON); // 等待命令执行完成 CommandResult result = future.get(); if (result.isSuccess()) { log.info("无人机开机成功: sn={}", sn); return R.ok("开机命令执行成功"); } else { log.error("无人机开机失败: sn={}, reason={}", sn, result.getErrorMessage()); return R.fail("开机命令执行失败: " + result.getErrorMessage()); } } catch (Exception e) { log.error("无人机开机异常: sn={}", sn, e); return R.fail("开机命令执行异常: " + e.getMessage()); } } /** * 无人机关机接口 * * @param sn 机场SN号 * @return 关机响应 */ @Operation(summary = "无人机关机", description = "控制指定机场的无人机执行关机操作") @PostMapping("/power-off/{sn}") public R powerOff( @Parameter(description = "机场SN号", required = true, example = "THJSQ03B2309DN7VQN43") @PathVariable("sn") String sn) { log.info("收到无人机关机请求: sn={}", sn); try { // 调用机器命令管理器执行关机命令 CompletableFuture future = machineCommandManager.executeCommand(sn, CommandType.POWER_OFF); // 等待命令执行完成 CommandResult result = future.get(); if (result.isSuccess()) { log.info("无人机关机成功: sn={}", sn); return R.ok("关机命令执行成功"); } else { log.error("无人机关机失败: sn={}, reason={}", sn, result.getErrorMessage()); return R.fail("关机命令执行失败: " + result.getErrorMessage()); } } catch (Exception e) { log.error("无人机关机异常: sn={}", sn, e); return R.fail("关机命令执行异常: " + e.getMessage()); } } /** * 查询无人机状态 * * @param sn 设备SN号 * @return 状态信息 */ @Operation(summary = "查询无人机状态", description = "根据设备SN号查询无人机的实时状态信息") @GetMapping("/machine-state/{sn}") public R getMachineState( @Parameter(description = "设备SN号", required = true, example = "TH001") @PathVariable("sn") String sn) { log.info("查询无人机状态: sn={}", sn); try { // 从 MachineStateManager 获取状态 MachineStates states = machineStateManager.getStates(sn); // 转换为 VO 对象 MachineStateVO vo = new MachineStateVO(); vo.setSn(sn); vo.setDroneState(states.getDroneState().name()); vo.setAirportState(states.getAirportState().name()); vo.setCoverState(states.getCoverState().name()); vo.setDrcState(states.getDrcState().name()); vo.setDebugModeState(states.getDebugModeState().name()); log.info("查询到状态: sn={}, vo={}", sn, vo); return R.ok(vo); } catch (Exception e) { log.error("查询无人机状态异常: sn={}", sn, e); return R.fail("查询状态失败: " + e.getMessage()); } } /** * 出舱接口 * * @param sn 机场SN号 * @return 出舱响应 */ @Operation(summary = "出舱", description = "控制指定机场执行出舱操作") @PostMapping("/cover-open/{sn}") public R coverOpen( @Parameter(description = "机场SN号", required = true, example = "THJSQ03B2309DN7VQN43") @PathVariable("sn") String sn) { log.info("收到出舱请求: sn={}", sn); try { // 调用机器命令管理器执行出舱命令 CompletableFuture future = machineCommandManager.executeCommand(sn, CommandType.OPEN_COVER); // 等待命令执行完成 CommandResult result = future.get(); if (result.isSuccess()) { log.info("出舱成功: sn={}", sn); return R.ok("出舱命令执行成功"); } else { log.error("出舱失败: sn={}, reason={}", sn, result.getErrorMessage()); return R.fail("出舱命令执行失败: " + result.getErrorMessage()); } } catch (Exception e) { log.error("出舱异常: sn={}", sn, e); return R.fail("出舱命令执行异常: " + e.getMessage()); } } /** * 回舱接口 * * @param sn 机场SN号 * @return 回舱响应 */ @Operation(summary = "回舱", description = "控制指定机场执行回舱操作") @PostMapping("/cover-close/{sn}") public R coverClose( @Parameter(description = "机场SN号", required = true, example = "THJSQ03B2309DN7VQN43") @PathVariable("sn") String sn) { log.info("收到回舱请求: sn={}", sn); try { // 调用机器命令管理器执行回舱命令 CompletableFuture future = machineCommandManager.executeCommand(sn, CommandType.CLOSE_COVER); // 等待命令执行完成 CommandResult result = future.get(); if (result.isSuccess()) { log.info("回舱成功: sn={}", sn); return R.ok("回舱命令执行成功"); } else { log.error("回舱失败: sn={}, reason={}", sn, result.getErrorMessage()); return R.fail("回舱命令执行失败: " + result.getErrorMessage()); } } catch (Exception e) { log.error("回舱异常: sn={}", sn, e); return R.fail("回舱命令执行异常: " + e.getMessage()); } } /** * 无人机返航接口 * * @param request 返航请求对象 * @return 返航响应 */ @Operation(summary = "无人机返航", description = "控制指定机场的无人机执行返航操作") @PostMapping("/return-home") public R returnHome(@RequestBody DroneReturnHomeRequest request) { log.info("收到无人机返航请求: sn={} ", request.getSn()); try { Long currentTaskId = flightService.currentRunningTask(request.getSn()); java.util.Map params = new java.util.HashMap<>(); if(Objects.isNull(currentTaskId)){ params.put("messageID", UUID.randomUUID().toString()); }else { params.put("messageID",currentTaskId); } params.put("taskId", 9074); params.put("zhilin", "03"); CompletableFuture future = machineCommandManager.executeCommand(request.getSn(), CommandType.RETURN_HOME, params); CommandResult result = future.get(); if (result.isSuccess()) { log.info("无人机返航成功: sn={}", request.getSn()); return R.ok("返航命令执行成功"); } else { log.error("无人机返航失败: sn={}, reason={}", request.getSn(), result.getErrorMessage()); return R.fail("返航命令执行失败: " + result.getErrorMessage()); } } catch (Exception e) { log.error("无人机返航异常: sn={}", request.getSn(), e); return R.fail("返航命令执行异常: " + e.getMessage()); } } }