ard-admin/src/main/resources/application.yml
@@ -123,7 +123,7 @@ # 数据库索引 database: 0 # 密码 password: password: xzx12345 # 连接超时时间 timeout: 10s lettuce: ard-common/src/main/java/com/ard/common/exception/ControlException.java
New file @@ -0,0 +1,12 @@ package com.ard.common.exception; /** * 无人机控制异常 * * @author ruoyi */ public class ControlException extends RuntimeException { public ControlException(String message) { super(message); } } ard-common/src/main/java/com/ard/common/exception/NoPermissionException.java
File was deleted ard-framework/src/main/java/com/ard/framework/web/exception/GlobalExceptionHandler.java
@@ -2,7 +2,7 @@ import javax.servlet.http.HttpServletRequest; import com.ard.common.exception.NoPermissionException; import com.ard.common.exception.ControlException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.access.AccessDeniedException; @@ -148,8 +148,8 @@ /** * 无人机无控制权异常 */ @ExceptionHandler(NoPermissionException.class) public AjaxResult handleNoPermissionException(NoPermissionException e) @ExceptionHandler(ControlException.class) public AjaxResult handleNoPermissionException(ControlException e) { return AjaxResult.error(e.getMessage()); } ard-work/src/main/java/com/ard/tianyu/aspect/UavControlAspect.java
@@ -1,8 +1,11 @@ package com.ard.tianyu.aspect; import com.ard.common.exception.NoPermissionException; import com.ard.common.exception.ControlException; import com.ard.common.utils.SecurityUtils; import com.ard.tianyu.aspect.annotation.RequireUavControl; import com.ard.tianyu.aspect.annotation.CheckTakeoffAltitude; import com.ard.tianyu.aspect.annotation.CheckControlRight; import com.ard.tianyu.domain.cache.UavTelemetryDataCache; import com.ard.tianyu.domain.dto.telemetry.TelemetryData; import com.ard.tianyu.service.control.UavControlService; import com.ard.tianyu.websocket.domain.UavControlType; import org.aspectj.lang.JoinPoint; @@ -15,6 +18,8 @@ /** * AOP 切面:拦截 @RequireUavControl,检查用户是否拥有 UAV 控制权限 * * 注意切面顺序:同类方法注解按字母ABCD顺序,不同类注解按order(1)顺序 越小优先级越高 */ @Aspect @Component @@ -24,15 +29,15 @@ private UavControlService uavControlService; /** * 拦截 @RequireUavControl 注解的方法 * 前置通知:控制权检测 */ @Before("@annotation(requireUavControl)") public void checkUavControl(JoinPoint joinPoint, RequireUavControl requireUavControl) { @Before("@annotation(checkControlRight)") public void checkUavControl(JoinPoint joinPoint, CheckControlRight checkControlRight) { // 获取当前用户 ID Long userId = SecurityUtils.getUserId(); // 获取控制类型 UavControlType controlType = requireUavControl.value(); UavControlType controlType = checkControlRight.value(); // 获取方法参数 Object[] args = joinPoint.getArgs(); @@ -45,12 +50,37 @@ Long currentController = uavControlService.getCurrentController(boxSn, controlType.name().toLowerCase()); if (currentController == null || !currentController.equals(userId)) { throw new NoPermissionException("无权执行该操作,需要 " + controlType + " 控制权限"); throw new ControlException("无权执行该操作,需要 " + controlType + " 控制权限"); } } } } /** * 前置通知:高度检查 */ @Before("@annotation(checkTakeoffAltitude)") public void takeoffAltitudeCheck(JoinPoint joinPoint, CheckTakeoffAltitude checkTakeoffAltitude) { // 获取最小高度 double minAltitude = checkTakeoffAltitude.minAltitude(); // 获取方法参数 Object[] args = joinPoint.getArgs(); // 遍历参数,使用反射获取每个参数中的 boxSn for (Object arg : args) { String boxSn = getBoxSn(arg); // 调用之前实现的 getBoxSn 方法 if (boxSn != null) { TelemetryData uavTelemetryData = UavTelemetryDataCache.getUavTelemetryData(boxSn); if (uavTelemetryData == null) { throw new ControlException("遥测数据异常,无法执行!"); } // 检查高度 if (uavTelemetryData.getAltitude() < minAltitude) { throw new ControlException("高度太低,无法执行!最低高度: " + minAltitude); } } } } /** * 通过反射获取对象中的 boxSn 字段 ard-work/src/main/java/com/ard/tianyu/aspect/annotation/CheckControlRight.java
File was renamed from ard-work/src/main/java/com/ard/tianyu/aspect/annotation/RequireUavControl.java @@ -12,6 +12,6 @@ */ @Target(ElementType.METHOD) // 作用于方法 @Retention(RetentionPolicy.RUNTIME) // 运行时生效 public @interface RequireUavControl { public @interface CheckControlRight { UavControlType value(); // 控制类型:"fly"、"payload"、"both" } ard-work/src/main/java/com/ard/tianyu/aspect/annotation/CheckTakeoffAltitude.java
New file @@ -0,0 +1,12 @@ package com.ard.tianyu.aspect.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CheckTakeoffAltitude { double minAltitude() default 20.0; // 默认最低起飞高度为10米 } ard-work/src/main/java/com/ard/tianyu/service/control/UavControlService.java
@@ -1,6 +1,6 @@ package com.ard.tianyu.service.control; import com.ard.common.exception.NoPermissionException; import com.ard.common.exception.ControlException; import com.ard.common.utils.SecurityUtils; import com.ard.tianyu.websocket.domain.UavControlType; import com.ard.tianyu.websocket.handler.UavControlManager; @@ -66,7 +66,7 @@ requestMap.put(boxSn, requester); uavControlManager.sendControlRequest(controller, boxSn, controlType.getType(), requester); } else { throw new NoPermissionException("当前已拥有控制权"); throw new ControlException("当前已拥有控制权"); } return false; } @@ -94,7 +94,7 @@ Long requester = requestMap.get(boxSn); if (requester == null || requester.equals(controller)) { throw new NoPermissionException("无控制请求"); throw new ControlException("无控制请求"); } if (approve) { @@ -128,7 +128,7 @@ ConcurrentHashMap<String, Long> controlMap = getControlMap(controlType); if (!controller.equals(controlMap.get(boxSn))) { throw new NoPermissionException("没有该设备的" + controlType.getType().toUpperCase() + "控制权"); throw new ControlException("没有该设备的" + controlType.getType().toUpperCase() + "控制权"); } controlMap.remove(boxSn); ard-work/src/main/java/com/ard/tianyu/service/impl/TyServiceImpl.java
@@ -1,7 +1,8 @@ package com.ard.tianyu.service.impl; import com.ard.common.utils.ByteUtils; import com.ard.tianyu.aspect.annotation.RequireUavControl; import com.ard.tianyu.aspect.annotation.CheckTakeoffAltitude; import com.ard.tianyu.aspect.annotation.CheckControlRight; import com.ard.tianyu.config.TyConfiguration; import com.ard.tianyu.domain.dto.camera.*; import com.ard.tianyu.domain.dto.manual.*; @@ -47,7 +48,6 @@ import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import com.ard.tianyu.service.ITyService; import com.ard.tianyu.websocket.domain.UavControlType; import com.ard.tianyu.websocket.utils.WebSocketUtil; import com.ard.vtdu.service.IMediaService; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; @@ -197,7 +197,7 @@ /** * 云台控制基于角度 */ @RequireUavControl(UavControlType.PAYLOAD) @CheckControlRight(UavControlType.PAYLOAD) @Override public Boolean angleControl(AngleControlDTO angleControlDto) { AngleControlCmd controlAngleCommand = new AngleControlCmd(angleControlDto); @@ -208,7 +208,7 @@ /** * 云台绝对值控制(基于角度) */ @RequireUavControl(UavControlType.PAYLOAD) @CheckControlRight(UavControlType.PAYLOAD) @Override public Boolean absAngleControl(AbsAngleControlDTO absAngleControlDto) { AbsAngleControlCmd absAngleControlCommand = new AbsAngleControlCmd(absAngleControlDto); @@ -219,7 +219,7 @@ /** * 云台转动(基于速度) */ @RequireUavControl(UavControlType.PAYLOAD) @CheckControlRight(UavControlType.PAYLOAD) @Override public Boolean speedControl(SpeedControlDTO speedControlDto) { SpeedControlCmd speedControlCommand = new SpeedControlCmd(speedControlDto); @@ -267,6 +267,7 @@ /** * 变倍 */ @CheckControlRight(UavControlType.PAYLOAD) @Override public Boolean zoom(ZoomDTO zoomDTO) { ZoomCmd zoomCommand = new ZoomCmd(zoomDTO); @@ -277,6 +278,7 @@ /** * 变倍到指定倍数 */ @CheckControlRight(UavControlType.PAYLOAD) @Override public Boolean zoomToLevel(ZoomToLevelDTO zoomToLevelDTO) { ZoomToLevelCmd zoomToLevelCommand = new ZoomToLevelCmd(zoomToLevelDTO); @@ -327,7 +329,7 @@ /** * 起飞 */ @RequireUavControl(UavControlType.FLY) @CheckControlRight(UavControlType.FLY) @Override public Boolean takeOff(TakeOffDTO takeOffDTO) { TakeOffCmd takeOffCmd = new TakeOffCmd(takeOffDTO); @@ -338,6 +340,7 @@ /** * 返航 */ @CheckControlRight(UavControlType.FLY) @Override public Boolean returnHome(ReturnHomeDTO returnHomeDTO) { ReturnHomeCmd returnHomeCmd = new ReturnHomeCmd(returnHomeDTO); @@ -348,6 +351,7 @@ /** * 取消返航 */ @CheckControlRight(UavControlType.FLY) @Override public Boolean returnHomeCancel(ReturnHomeCancelDTO returnHomeCancelDTO) { ReturnHomeCancelCmd returnHomeCancelCmd = new ReturnHomeCancelCmd(returnHomeCancelDTO); @@ -358,6 +362,7 @@ /** * 降落 */ @CheckControlRight(UavControlType.FLY) @Override public Boolean land(LandDTO landDTO) { LandCmd landCmd = new LandCmd(landDTO); @@ -368,6 +373,7 @@ /** * 取消降落 */ @CheckControlRight(UavControlType.FLY) @Override public Boolean landCancel(LandCancelDTO landCancelDTO) { LandCancelCmd landCancelCmd = new LandCancelCmd(landCancelDTO); @@ -378,6 +384,7 @@ /** * 强制降落 */ @CheckControlRight(UavControlType.FLY) @Override public Boolean landForced(LandForcedDTO landForcedDTO) { LandForcedCmd landForcedCmd = new LandForcedCmd(landForcedDTO); @@ -388,6 +395,8 @@ /** * 方向控制 */ @CheckControlRight(UavControlType.FLY) @CheckTakeoffAltitude(minAltitude = 30.0) @Override public Boolean flyControl(FlyControlDTO flyControlDTO) { FlyControlCmd flyControlCmd = new FlyControlCmd(flyControlDTO); @@ -398,6 +407,8 @@ /** * 指点飞行控制 */ @CheckControlRight(UavControlType.FLY) @CheckTakeoffAltitude(minAltitude = 30.0) @Override public Boolean flyPointControl(FlyPointControlDTO flyPointControlDTO) { FlyPointControlCmd flyPointControlCmd = new FlyPointControlCmd(flyPointControlDTO); @@ -408,6 +419,7 @@ /** * 停止指点飞行控制 */ @CheckControlRight(UavControlType.FLY) @Override public Boolean flyPointControlCancel(FlyPointControlCancelDTO flyPointControlCancelDTO) { FlyPointControlCancelCmd flyPointControlCancelCmd = new FlyPointControlCancelCmd(flyPointControlCancelDTO); @@ -418,6 +430,7 @@ /** * 紧急制动 */ @CheckControlRight(UavControlType.FLY) @Override public Boolean emeragencyBrake(EmeragencyBrakeDTO emeragencyBrakeDTO) { EmergencyBrakeCmd emergencyBrakeCmd = new EmergencyBrakeCmd(emeragencyBrakeDTO); @@ -559,6 +572,7 @@ /** * 航线开始 */ @CheckControlRight(UavControlType.FLY) @Override public void wayLineBegin(WayLineBeginDTO wayLineBeginDTO) { WayLineBeginCmd wayLineBeginCmd = new WayLineBeginCmd(wayLineBeginDTO); @@ -568,6 +582,7 @@ /** * 航线暂停 */ @CheckControlRight(UavControlType.FLY) @Override public void wayLinePause(WayLinePauseDTO wayLinePauseDTO) { WayLinePauseCmd wayLinePauseCmd = new WayLinePauseCmd(wayLinePauseDTO); @@ -577,6 +592,7 @@ /** * 航线恢复 */ @CheckControlRight(UavControlType.FLY) @Override public void wayLineResume(WayLineResumeDTO wayLineResumeDTO) { WayLineResumeCmd wayLineResumeCmd = new WayLineResumeCmd(wayLineResumeDTO); @@ -586,6 +602,7 @@ /** * 航线结束 */ @CheckControlRight(UavControlType.FLY) @Override public void wayLineEnd(WayLineEndDTO wayLineEndDTO) { WayLineEndCmd wayLineEndCmd = new WayLineEndCmd(wayLineEndDTO); ard-work/src/main/java/com/ard/tianyu/util/CameraUtil.java
New file @@ -0,0 +1,75 @@ package com.ard.tianyu.util; public class CameraUtil { // 根据相机类型和变焦系数计算视场角度 public static Float getFieldAngleFromZoom(Integer source, Float zoomFactor) { //处理视场角度 Float fieldAngle = 66.6F; //空值的情况 if (source == null) fieldAngle = 66.6F; else if (source == 0) { //广角 fieldAngle = (float) 82.9; } else if (source == 2) { //红外 fieldAngle = (float) 40.6; } else { //变焦相机 float zoom; if (zoomFactor.isNaN()) zoom = 1; else if (zoomFactor < 1) zoom = 1; else if (zoomFactor > 200) zoom = 200; else zoom = zoomFactor; fieldAngle = (float) (70.395 * Math.pow(zoom, -0.952)); if (fieldAngle < 4) fieldAngle = 4F; else if (fieldAngle > 66.6) fieldAngle = 66.6F; } return fieldAngle; } // 根据相机类型和视场角度计算变焦系数 public static Float getZoomFromFieldAngle(Integer source, Float fieldAngle) { //处理视场角度 Float zoomFactor = 1F; //空值的情况 if (source == null) zoomFactor = 1F; else if (source == 0) { //广角 zoomFactor = 1F; } else if (source == 2) { //红外 zoomFactor = 1F; } else { //变焦相机 float fieldAngleModify; if (fieldAngle.isNaN()) fieldAngleModify = 66.6F; else if (fieldAngle < 4) fieldAngleModify = 4; else if (fieldAngle > 66.6) fieldAngleModify = 66.6F; else fieldAngleModify = fieldAngle; zoomFactor = (float) (Math.pow((fieldAngleModify / 70.395), 1 / -0.952)); if (zoomFactor < 1) zoomFactor = 1F; else if (zoomFactor > 200) zoomFactor = 200F; } return zoomFactor; } public static void main(String[] args) { Float fieldAngleFromZoom = getFieldAngleFromZoom(1, 3F); System.out.println(fieldAngleFromZoom); } }