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.getAltitude() < 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()) {
|
Boolean online = redisCache.getCacheObject(CacheConstants.UAV_ONLINE_KEY + boxSn);
|
if (online == null || !online) {
|
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() + "】,无法执行操作!");
|
}
|
}
|
}
|
}
|