liusuyi
2025-03-17 a82c9a2633c519d5d7d868d3ec89832f77849700
增加飞行高度控制切面
3 files added
5 files modified
1 files deleted
1 files renamed
202 ■■■■■ changed files
ard-admin/src/main/resources/application.yml 2 ●●●●● patch | view | raw | blame | history
ard-common/src/main/java/com/ard/common/exception/ControlException.java 12 ●●●●● patch | view | raw | blame | history
ard-common/src/main/java/com/ard/common/exception/NoPermissionException.java 12 ●●●●● patch | view | raw | blame | history
ard-framework/src/main/java/com/ard/framework/web/exception/GlobalExceptionHandler.java 6 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/aspect/UavControlAspect.java 44 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/aspect/annotation/CheckControlRight.java 2 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/aspect/annotation/CheckTakeoffAltitude.java 12 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/service/control/UavControlService.java 8 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/service/impl/TyServiceImpl.java 29 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/util/CameraUtil.java 75 ●●●●● patch | view | raw | blame | history
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);
    }
}