liusuyi
2025-12-26 22f306496b583d3c25f2d3354441b0e29d6840cf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package com.ard.work.aspect;
 
import com.ard.common.constant.CacheConstants;
import com.ard.common.core.redis.RedisCache;
import com.ard.common.exception.ControlException;
import com.ard.common.utils.SecurityUtils;
import com.ard.common.utils.StringUtils;
import com.ard.common.utils.reflect.ReflectUtils;
import com.ard.data.noflyzone.domain.ArdNoFlyZone;
import com.ard.data.noflyzone.domain.Vertex;
import com.ard.data.noflyzone.service.IArdNoFlyZoneService;
import com.ard.data.noflyzone.utils.NoFlyZoneUtils;
import com.ard.work.aspect.annotation.CheckControlRight;
import com.ard.work.aspect.annotation.CheckDroneStatus;
import com.ard.work.aspect.annotation.CheckNoFlyZone;
import com.ard.work.aspect.annotation.CheckTakeoffAltitude;
import com.ard.work.domain.cache.UavTelemetryDataCache;
import com.ard.work.domain.dto.telemetry.TelemetryData;
import com.ard.work.domain.message.UavControlType;
import com.ard.work.domain.receiver.BoxInfoReceiver;
import com.ard.work.service.control.UavControlService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
 
/**
 * 无人机控制切面
 * <p>
 * 注意切面顺序:同类方法注解按字母ABCD顺序,不同类注解按order(1)顺序 越小优先级越高
 */
@Aspect
@Component
public class UavControlAspect {
 
    @Resource
    private UavControlService uavControlService;
    @Resource
    private IArdNoFlyZoneService ardNoFlyZoneService;
    @Resource
    private RedisCache redisCache;
 
    /**
     * 控制权检查
     */
    @Before("@annotation(checkControlRight)")
    public void checkUavControl(JoinPoint joinPoint, CheckControlRight checkControlRight) {
        // 获取当前用户 ID
        Long userId = SecurityUtils.getUserId();
 
        // 获取控制类型
        UavControlType controlType = checkControlRight.value();
 
        // 获取方法参数
        Object[] args = joinPoint.getArgs();
 
        for (Object arg : args) {
            if (arg == null) {
                continue;
            }
 
            // 1️⃣ 获取 boxSn
            String boxSn = ReflectUtils.getFieldValue(arg, "boxSn");
            if (StringUtils.isBlank(boxSn)) {
                continue;
            }
 
            // 2️⃣ 检查控制权
            Long currentController = uavControlService.getCurrentController(boxSn,
                    controlType.name().toLowerCase());
            if (currentController == null || !currentController.equals(userId)) {
                throw new ControlException("无权执行该操作,需要 " + controlType + " 控制权限");
            }
 
            // 3️⃣ 检查在线状态
            BoxInfoReceiver obj = redisCache.getCacheObject(CacheConstants.UAV_ONLINE_KEY + boxSn);
            if (obj == null || !obj.getOnLine().equals(1)) {
                throw new ControlException("无人机离线,无法执行操作!boxSn: " + boxSn);
            }
 
        }
    }
 
    /**
     * 高度检查
     */
    @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 = ReflectUtils.getFieldValue(arg, "boxSn");  // 调用之前实现的 getBoxSn 方法
            if (boxSn != null) {
                TelemetryData uavTelemetryData = UavTelemetryDataCache.get(boxSn);
                if (uavTelemetryData == null) {
                    throw new ControlException("遥测数据异常,无法执行!");
                }
                // 检查高度
                if (uavTelemetryData.getUltrasonic() < minAltitude) {
                    throw new ControlException("高度太低,无法执行!最低高度: " + minAltitude);
                }
            }
        }
    }
 
    /**
     * 状态检查
     */
    @Before("@annotation(checkDroneStatus)")
    public void checkDroneStatus(JoinPoint joinPoint, CheckDroneStatus checkDroneStatus) {
        for (Object arg : joinPoint.getArgs()) {
            if (arg == null) {
                continue;
            }
 
            String boxSn = ReflectUtils.getFieldValue(arg, "boxSn");
            if (boxSn == null) {
                continue;
            }
            // 检查在线状态
            if (checkDroneStatus.checkOnline()) {
                // 3️⃣ 检查在线状态
                BoxInfoReceiver obj = redisCache.getCacheObject(CacheConstants.UAV_ONLINE_KEY + boxSn);
                if (obj == null || !obj.getOnLine().equals(1)) {
                    throw new ControlException("无人机离线,无法执行操作!boxSn: " + boxSn);
                }
            }
        }
    }
 
    /**
     * 禁飞区检查
     */
    @Before("@annotation(checkNoFlyZone)")
    public void checkNoFlyZone(JoinPoint joinPoint, CheckNoFlyZone checkNoFlyZone) {
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            return;
        }
 
        // ① 提取 boxSn(无人机编号)和目标点坐标
        String boxSn = null;
        Double targetLon = null, targetLat = null;
 
        for (Object arg : args) {
            if (arg == null) continue;
 
            if (boxSn == null) {
                String sn = ReflectUtils.getFieldValue(arg, "boxSn");
                if (sn != null) boxSn = sn;
            }
 
            if (targetLon == null || targetLat == null) {
                Double lon = ReflectUtils.getFieldValue(arg, "longitude");
                Double lat = ReflectUtils.getFieldValue(arg, "latitude");
                if (lon != null && lat != null) {
                    targetLon = lon;
                    targetLat = lat;
                }
            }
        }
 
        if (targetLon == null || targetLat == null) {
            throw new ControlException("方法参数中未找到有效目标经纬度字段,无法判断禁飞区!");
        }
 
        // ② 获取无人机当前位置(从缓存)
        Vertex current = null;
        if (boxSn != null) {
            TelemetryData telemetry = UavTelemetryDataCache.get(boxSn);
            if (telemetry != null) {
                current = new Vertex(telemetry.getLng(), telemetry.getLat());
                //current = new Vertex(125.03, 46.58);
            }
        }
 
        Vertex target = new Vertex(targetLon, targetLat);
 
        // ③ 查询禁飞区
        List<ArdNoFlyZone> zones = ardNoFlyZoneService.selectArdNoFlyZoneList(new ArdNoFlyZone());
        if (zones.isEmpty()) return;
 
        for (ArdNoFlyZone zone : zones) {
            List<Vertex> polygon = zone.getVertices();
            if (polygon == null || polygon.size() < 3) continue;
 
            // 检查目标点是否在禁飞区
            if (NoFlyZoneUtils.isPointInPolygon(target, polygon)) {
                throw new ControlException("目标点在禁飞区【" + zone.getName() + "】内,无法执行操作!");
            }
 
            // 检查路径是否穿越禁飞区
            if (current != null && NoFlyZoneUtils.isLineIntersectPolygon(current, target, polygon)) {
                throw new ControlException("路径穿越禁飞区【" + zone.getName() + "】,无法执行操作!");
            }
        }
    }
}