ard-work/src/main/java/com/ard/tianyu/controller/UavController.java
@@ -4,6 +4,9 @@ import com.ard.tianyu.model.dto.BoxHistoryDTO; import com.ard.tianyu.model.dto.BoxPhotosDTO; import com.ard.tianyu.model.dto.BoxSettingsDTO; import com.ard.tianyu.model.dto.signal.SignalInfo; import com.ard.tianyu.model.dto.state.UavState; import com.ard.tianyu.model.dto.telemetry.TelemetryData; import com.ard.tianyu.model.receiver.BoxHistoryReceiver; import com.ard.tianyu.model.receiver.BoxPhotosReceiver; import com.ard.tianyu.model.receiver.BoxInfoReceiver; @@ -63,22 +66,46 @@ List<BoxPhotosReceiver> boxPhotos = tyService.getBoxPhotos(boxPhotosDTO); return AjaxResult.success(boxPhotos); } @ApiOperation("修改云盒设置") @PostMapping("/updateBoxSettings") public AjaxResult updateBoxSettings(@RequestBody BoxSettingsDTO boxSettingsDTO) { Boolean state = tyService.updateBoxSettings(boxSettingsDTO); return AjaxResult.success(state); } @ApiOperation("无人机控制订阅") @GetMapping("/uavControlSubscribe") public AjaxResult uavControlSubscribe(String boxSn) { tyService.uavControlSubscribe(boxSn); return AjaxResult.success(); } @ApiOperation("云台转动(基于角度)") @GetMapping("/controlAngle") public AjaxResult controlAngle(String boxSn) { tyService.controlAngle(boxSn); public AjaxResult controlAngle(String boxSn,Integer actionId,Integer rotationAngle) { tyService.controlAngle(boxSn,actionId,rotationAngle); return AjaxResult.success(); } @ApiOperation("获取无人机状态") @GetMapping("/getUavState") public AjaxResult getUavState(String boxSn) { UavState uavState = tyService.getUavState(boxSn); return AjaxResult.success(uavState); } @ApiOperation("获取无人机遥测数据") @GetMapping("/getUavTelemetryData") public AjaxResult getUavTelemetryData(String boxSn) { TelemetryData telemetryData = tyService.getUavTelemetryData(boxSn); return AjaxResult.success(telemetryData); } @ApiOperation("获取无人机信道质量数据") @GetMapping("/getSignalInfo") public AjaxResult getSignalInfo(String boxSn) { SignalInfo signalInfo = tyService.getSignalInfo(boxSn); return AjaxResult.success(signalInfo); } } ard-work/src/main/java/com/ard/tianyu/model/cache/UavSignalInfoCache.java
New file @@ -0,0 +1,32 @@ package com.ard.tianyu.model.cache; import com.ard.tianyu.model.dto.signal.SignalInfo; import java.util.concurrent.ConcurrentHashMap; /** * 无人机遥测数据缓存 * * @author 刘苏义 * @date 2024/10/24 8:55 */ public class UavSignalInfoCache { // 使用 ConcurrentHashMap 来缓存无人机信道质量数据,key 是 boxSn(无人机序列号) private static final ConcurrentHashMap<String, SignalInfo> uavSignalInfoCache = new ConcurrentHashMap<>(); // 存储无人机信道质量数据到缓存 public static void putUavSignalInfoCache(SignalInfo signalInfo) { uavSignalInfoCache.put(signalInfo.getBoxSn(), signalInfo); } // 从缓存中获取信道质量数据 public static SignalInfo getUavSignalInfo(String boxSn) { return uavSignalInfoCache.get(boxSn); } // 删除缓存中的信道质量数据 public static void removeUavSignalInfo(String boxSn) { uavSignalInfoCache.remove(boxSn); } } ard-work/src/main/java/com/ard/tianyu/model/cache/UavStateCache.java
New file @@ -0,0 +1,31 @@ package com.ard.tianyu.model.cache; import com.ard.tianyu.model.dto.state.UavState; import java.util.concurrent.ConcurrentHashMap; /** * 无人机状态缓存 * * @author 刘苏义 * @date 2024/10/24 8:55 */ public class UavStateCache { // 使用 ConcurrentHashMap 来缓存无人机状态数据,key 是 boxSn(无人机序列号) private static final ConcurrentHashMap<String, UavState> uavStateCache = new ConcurrentHashMap<>(); // 存储无人机状态数据到缓存 public static void putUavState(UavState uavState) { uavStateCache.put(uavState.getBoxSn(), uavState); } // 从缓存中获取无人机状态数据 public static UavState getUavState(String boxSn) { return uavStateCache.get(boxSn); } // 删除缓存中的无人机状态数据 public static void removeUavState(String boxSn) { uavStateCache.remove(boxSn); } } ard-work/src/main/java/com/ard/tianyu/model/cache/UavTelemetryDataCache.java
New file @@ -0,0 +1,33 @@ package com.ard.tianyu.model.cache; import com.ard.tianyu.model.dto.state.UavState; import com.ard.tianyu.model.dto.telemetry.TelemetryData; import java.util.concurrent.ConcurrentHashMap; /** * 无人机遥测数据缓存 * * @author 刘苏义 * @date 2024/10/24 8:55 */ public class UavTelemetryDataCache { // 使用 ConcurrentHashMap 来缓存无人机遥测数据,key 是 boxSn(无人机序列号) private static final ConcurrentHashMap<String, TelemetryData> uavTelemetryDataCache = new ConcurrentHashMap<>(); // 存储无人机遥测数据到缓存 public static void putUavTelemetryData(TelemetryData telemetryData) { uavTelemetryDataCache.put(telemetryData.getBoxSn(), telemetryData); } // 从缓存中获取无人机遥测数据 public static TelemetryData getUavTelemetryData(String boxSn) { return uavTelemetryDataCache.get(boxSn); } // 删除缓存中的无人机遥测数据 public static void removeUavTelemetryData(String boxSn) { uavTelemetryDataCache.remove(boxSn); } } ard-work/src/main/java/com/ard/tianyu/model/dto/converter/UavSignalInfoConverter.java
New file @@ -0,0 +1,19 @@ package com.ard.tianyu.model.dto.converter; import com.ard.tianyu.model.dto.signal.SignalInfo; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import org.springframework.beans.BeanUtils; /** * SignalInfoConverter * * @author liusuyi * @date 2024年10月24日10:38 **/ public class UavSignalInfoConverter { public static SignalInfo convertProtoToEntity(TelemetryDataPB.SignalInfo proto) { SignalInfo signalInfo = new SignalInfo(); BeanUtils.copyProperties(proto, signalInfo); return signalInfo; } } ard-work/src/main/java/com/ard/tianyu/model/dto/converter/UavStateConverter.java
New file @@ -0,0 +1,139 @@ package com.ard.tianyu.model.dto.converter; import com.ard.tianyu.model.dto.state.*; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import org.springframework.beans.BeanUtils; import java.util.List; import java.util.stream.Collectors; public class UavStateConverter { // 将 Protobuf UavState 转换为 Java 实体类 UavState public static UavState convertProtoToEntity(TelemetryDataPB.UavState protobufUavState) { UavState uavState = new UavState(); // 复制简单字段 uavState.setBoxSn(protobufUavState.getBoxSn()); // 处理嵌套的 FlightControllerState if (protobufUavState.hasFlightControllerState()) { uavState.setFlightControllerState(convertFlightControllerState(protobufUavState.getFlightControllerState())); } // 处理嵌套的 BatteryState if (protobufUavState.hasBatteryState()) { uavState.setBatteryState(convertBatteryState(protobufUavState.getBatteryState())); } // 处理嵌套的 PtzState if (protobufUavState.hasPtzState()) { uavState.setPtzState(convertPtzState(protobufUavState.getPtzState())); } // 处理嵌套的 CameraState if (protobufUavState.hasCameraState()) { uavState.setCameraState(convertCameraState(protobufUavState.getCameraState())); } // 处理嵌套的 MissionState if (protobufUavState.hasMissionState()) { uavState.setMissionState(convertMissionState(protobufUavState.getMissionState())); } // 处理 AvoidanceData if (protobufUavState.hasAvoidanceData()) { uavState.setAvoidanceData(convertAvoidanceData(protobufUavState.getAvoidanceData())); } // 处理 List<HmsAlarmData> List<HmsAlarmData> hmsAlarmDataList = protobufUavState.getHmsAlarmDataList().stream() .map(UavStateConverter::convertHmsAlarmData) .collect(Collectors.toList()); uavState.setHmsAlarmData(hmsAlarmDataList); return uavState; } // 其他转换方法如下: // 处理 BatteryState 逻辑 public static BatteryState convertBatteryState(TelemetryDataPB.BatteryState protobufBatteryState) { BatteryState batteryState = new BatteryState(); // 复制简单字段 batteryState.setBatteryNum(protobufBatteryState.getBatteryNum()); batteryState.setBatteryPower(protobufBatteryState.getBatteryPower()); batteryState.setBatteryVoltage(protobufBatteryState.getBatteryVoltage()); // 处理嵌套的 BatteryStateInfo if (protobufBatteryState.hasFirstBatteryInfo()) { batteryState.setFirstBatteryInfo(convertBatteryStateInfo(protobufBatteryState.getFirstBatteryInfo())); } if (protobufBatteryState.hasSecondBatteryInfo()) { batteryState.setSecondBatteryInfo(convertBatteryStateInfo(protobufBatteryState.getSecondBatteryInfo())); } return batteryState; } // 处理 BatteryStateInfo 逻辑 public static BatteryStateInfo convertBatteryStateInfo(TelemetryDataPB.BatteryStateInfo protobufBatteryStateInfo) { BatteryStateInfo batteryStateInfo = new BatteryStateInfo(); BeanUtils.copyProperties(protobufBatteryStateInfo, batteryStateInfo); return batteryStateInfo; } // 处理 FlightControllerState 逻辑 public static FlightControllerState convertFlightControllerState(TelemetryDataPB.FlightControllerState proto) { FlightControllerState flightControllerState = new FlightControllerState(); // 处理 PointData(起飞点和返航点) if (proto.hasStartPoint()) { flightControllerState.setStartPoint(convertPointData(proto.getStartPoint())); } if (proto.hasHomePoint()) { flightControllerState.setHomePoint(convertPointData(proto.getHomePoint())); } BeanUtils.copyProperties(proto, flightControllerState); return flightControllerState; } // 处理 PointData 逻辑 public static PointData convertPointData(TelemetryDataPB.PointData protoPoint) { PointData pointData = new PointData(); pointData.setLat(protoPoint.getLat()); pointData.setLng(protoPoint.getLng()); return pointData; } public static PtzState convertPtzState(TelemetryDataPB.PtzState proto) { PtzState ptzState = new PtzState(); BeanUtils.copyProperties(proto, ptzState); return ptzState; } public static CameraState convertCameraState(TelemetryDataPB.CameraState proto) { CameraState cameraState = new CameraState(); BeanUtils.copyProperties(proto, cameraState); return cameraState; } public static MissionState convertMissionState(TelemetryDataPB.MissionState proto) { MissionState missionState = new MissionState(); BeanUtils.copyProperties(proto, missionState); return missionState; } public static AvoidanceData convertAvoidanceData(TelemetryDataPB.AvoidanceData proto) { AvoidanceData avoidanceData = new AvoidanceData(); BeanUtils.copyProperties(proto, avoidanceData); return avoidanceData; } public static HmsAlarmData convertHmsAlarmData(TelemetryDataPB.HmsAlarmData proto) { HmsAlarmData hmsAlarmData = new HmsAlarmData(); BeanUtils.copyProperties(proto, hmsAlarmData); return hmsAlarmData; } } ard-work/src/main/java/com/ard/tianyu/model/dto/converter/UavTelemetryDataConverter.java
New file @@ -0,0 +1,20 @@ package com.ard.tianyu.model.dto.converter; import com.ard.tianyu.model.dto.state.UavState; import com.ard.tianyu.model.dto.telemetry.TelemetryData; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import org.springframework.beans.BeanUtils; /** * UavTelemetryDataConverter * * @author liusuyi * @date 2024年10月24日10:38 **/ public class UavTelemetryDataConverter { public static TelemetryData convertProtoToEntity(TelemetryDataPB.TelemetryData proto) { TelemetryData telemetryData = new TelemetryData(); BeanUtils.copyProperties(proto, telemetryData); return telemetryData; } } ard-work/src/main/java/com/ard/tianyu/model/dto/init/UavInitInfo.java
New file @@ -0,0 +1,16 @@ package com.ard.tianyu.model.dto.init; import lombok.Builder; import lombok.Data; @Data @Builder public class UavInitInfo { private String uavModel; // 无人机型号 private String uavSn; // 无人机SN private String uavVersion; // 无人机固件版本号 private String ptModel; // 挂载相机型号 private String ptVersion; // 挂载相机固件版本号 private int ptPosition; // 挂载相机psdk日位置 private String moduleVersion; // 模组固件版本号 } ard-work/src/main/java/com/ard/tianyu/model/dto/signal/SignalInfo.java
New file @@ -0,0 +1,74 @@ package com.ard.tianyu.model.dto.signal; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; @Data public class SignalInfo { private String mode; // 入网模式: NR5G-SA, NR5G-NSA,LTE private String mcc; // NR5G-SA, NR5G-NSA, LTE private String mnc; // NR5G-SA, NR5G-NSA, LTE private String cellid; // NR5G-SA, LTE private String pcid; // NR5G-SA, NR5G-NSA, LTE private String tac; // NR5G-SA, LTE private String arfcn; // NR5G-SA, NR5G-NSA private String band; // NR5G-SA, NR5G-NSA @JsonProperty("nr_dl_bandwidth") private String nrDlBandwidth; // NR5G-SA private String rsrp; // NR5G-SA, NR5G-NSA, LTE private String rsrq; // NR5G-SA, NR5G-NSA, LTE private String sinr; // NR5G-SA, NR5G-NSA, LTE @JsonProperty("tx_power") private String txPower; // NR5G-SA, LTE private String srxlev; // NR5G-SA, LTE private String cqi; // LTE private String earfcn; // LTE @JsonProperty("freq_band_ind") private String freqBandInd; // LTE @JsonProperty("ul_bandwidth") private String ulBandwidth; // LTE @JsonProperty("dl_bandwidth") private String dlBandwidth; // LTE @JsonProperty("is_tdd") private String isTdd; // LTE private String rssi; // LTE @JsonProperty("duplex_mode") private String duplexMode; // NR5G-SA private double lng; // 经度 private double lat; // 纬度 private float height; // 高度(单位:米) private int timestamp; // 时间戳(单位:秒) private int boxstate; // 1=线缆故障,2=OSDK未激活,3=正常,4=云盒固件更新 private String isp; // 运营商 private int tempMax; // 模组芯片温度(单位:摄氏度) @JsonProperty("prx_rsrp") private String prxRsrp; // prx路径的rsrp值 @JsonProperty("drx_rsrp") private String drxRsrp; // drx路径的rsrp值 @JsonProperty("rx2_rsrp") private String rx2Rsrp; // rx2路径的rsrp值 @JsonProperty("rx3_rsrp") private String rx3Rsrp; // rx3路径的rsrp值 @JsonProperty("box_sn") private String boxSn; // 云盒编号 @JsonProperty("sim_use_seat") private int simUseSeat; // 当前使用的SIM卡在卡槽中的位置:1或2 } ard-work/src/main/java/com/ard/tianyu/model/dto/state/AvoidanceData.java
New file @@ -0,0 +1,23 @@ package com.ard.tianyu.model.dto.state; import lombok.Data; // 避障数据类 @Data public class AvoidanceData { private int downHealth; // 下方健康状态 private float down; // 距下障碍物的距离(单位:米),downHealth 为 1 时值有效 private int frontHealth; // 前方健康状态 private float front; // 距前障碍物的距离(单位:米),frontHealth 为 1 时值有效 private int rightHealth; // 右方健康状态 private float right; // 距右障碍物的距离(单位:米),rightHealth 为 1 时值有效 private int backHealth; // 后方健康状态 private float back; // 距后障碍物的距离(单位:米),backHealth 为 1 时值有效 private int leftHealth; // 左方健康状态 private float left; // 距左障碍物的距离(单位:米),leftHealth 为 1 时值有效 private int upHealth; // 上方健康状态 private float up; // 距上障碍物的距离(单位:米),upHealth 为 1 时值有效 private int avoidanceEnableStatusDown; // 下视觉避障开关状态:0-关闭,1-打开 private int avoidanceEnableStatusUp; // 上视觉避障开关状态:0-关闭,1-打开 private int avoidanceEnableStatusHorizontal; // 水平视觉避障开关状态:0-关闭,1-打开 } ard-work/src/main/java/com/ard/tianyu/model/dto/state/BatteryState.java
New file @@ -0,0 +1,19 @@ package com.ard.tianyu.model.dto.state; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Data @Builder @AllArgsConstructor @NoArgsConstructor public class BatteryState { private int batteryNum; // 电池数量 private String batteryPower; // 电池电量,多个电池用下划线分割 private String batteryVoltage; // 电池电压,多个电池用下划线分割(单位: V) private BatteryStateInfo firstBatteryInfo; // 第一块电池状态信息 private BatteryStateInfo secondBatteryInfo; // 第二块电池状态信息 } ard-work/src/main/java/com/ard/tianyu/model/dto/state/BatteryStateInfo.java
New file @@ -0,0 +1,30 @@ package com.ard.tianyu.model.dto.state; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * BatteryStateDto * * @author liusuyi * @date 2024年10月24日9:45 **/ @Data @AllArgsConstructor @NoArgsConstructor public class BatteryStateInfo { private int isBatteryEmbed; // 电池插入飞机状态:0=插入,1=未插入 private int batteryCapacityPercent; // 电池电量百分比 private int currentVoltage; // 电池电压 (单位: mV) private int currentElectric; // 当前电池电流 (单位: mA) private int fullCapacity; // 满电池容量 (单位: mAh) private int remainedCapacity; // 剩余电池容量 (单位: mAh) private float batteryTemperature; // 电池温度 (单位: 摄氏度) private int cellCount; // 电池组数 private int batSOHState; // 电池循环健康状态:0=正常,1=警报,2=注意安全 private int sop; // 相对功率百分比 (Relative power percentage) private int heatState; // 电池加热状态:0=未加热,1=加热中,2=保持恒温 private int socState; // 电池 SOC 荷电状态:0=正常,1=高,2=下降,3=上升,4=无效 private int selfCheckError; // 电池自检错误标志:0=正常,其他值表示错误 } ard-work/src/main/java/com/ard/tianyu/model/dto/state/CameraState.java
New file @@ -0,0 +1,21 @@ package com.ard.tianyu.model.dto.state; import lombok.Data; // 相机状态类 @Data public class CameraState { private int mode; // 相机模式:1=拍照模式,2=录像模式,0=未知 private int isRecording; // 是否录制中:1=录制中,0=未录制 private int recordDuration; // 录制时长(单位:秒) private int source; // 相机视源:0=可见,1=变焦,2=红外 private int camera; // 相机:0=FPV,1=相机(1),2=相机(2) private float zoomFactor; // 相机当前变焦倍数 private int width; // 水平像素(例如:1920) private int height; // 垂直像素(例如:1080) private int frameRate; // 帧率(例如:30) private int bitstream; // 码率(单位:0.001)(例如:6兆设置值为6000) private int pointThermometrying; // 单点测温状态:0=非单点测温中,1=单点测温中 private int areaThermometrying; // 区域测温状态:0=非区域测温中,1=区域测温中 private int laserRanging; // 激光测距状态:0=非激光测距中,1=激光测距中 } ard-work/src/main/java/com/ard/tianyu/model/dto/state/FlightControllerState.java
New file @@ -0,0 +1,23 @@ package com.ard.tianyu.model.dto.state; import lombok.Data; @Data public class FlightControllerState { private int satelliteCount; // 卫星计数 private int gpsSignalLevel; // GPS信号级别 (0-5) private int flightMode; // 飞行模式 private int flightStatus; // 飞行状态 private PointData startPoint; // 起飞点(包含经纬度) private PointData homePoint; // 返航点(包含经纬度) private int homeHeight; // 返航高度(单位:米) private int safeLine; // 飞行模式安全线 private int deviceStatus; // 控制权状态 private int uwbNodeCount; // UWB星数 private String rcMode; // 遥控器档位模式 private int isEmergencyBraking; // 是否紧急制动中 private int outFlyAreaFlag; // 是否在电子围栏外 private int inNoFlyAreaFlag; // 是否在禁飞区内 } ard-work/src/main/java/com/ard/tianyu/model/dto/state/HmsAlarmData.java
New file @@ -0,0 +1,11 @@ package com.ard.tianyu.model.dto.state; import lombok.Data; // Hms报警信息类 @Data public class HmsAlarmData { private int alarmId; // 报警信息ID private int reportLevel; // 等级 [1/2-警告 3/4/5-错误] private String alarmMssInfo; // 报警信息描述(支持中文和英文版本,默认中文,如需切换请联系技术支持) } ard-work/src/main/java/com/ard/tianyu/model/dto/state/MissionState.java
New file @@ -0,0 +1,22 @@ package com.ard.tianyu.model.dto.state; import lombok.Data; // 任务状态类 @Data public class MissionState { private int isPause; // 航线是否暂停中:0=未暂停,1=暂停中 private int targetWaypointIndex; // 目标航点下标 private int isWaypointFinished; // 是否航点完成:1完成、0未完成(此值废弃) private int pushVideo; // 云盒是否发送视频数据:0=不发送,1=发送 private int lock4g; // 是否锁定4G:0=不锁定,1=锁定 private int boxModel; // 是否开通图片自动回传功能:0=未开通,1=已开通 private int mapPlay; // 自动拍照间隔:0未开始,3~255间隔时间 private int loseAction; // 网络失联后动作:0=返回HOME点,1=继续航线 private int isPointControl; // 是否打点飞行控制中:0=否,1=是 private int isUwbLanding; // 是否UWB精准降落中:0=不是,1=是 private int isPushVideoing; // 视频是否推流中:0=不是,1=是 private int isDLanding; // 是否二维码精准降落中:0=不是,1=是 private int perceptionFlag; // 二维码精准降落模式:0=载荷降落,1=下视降落 private int isPositionControl; // 是否指定距离飞行控制中:0=否,1=是 } ard-work/src/main/java/com/ard/tianyu/model/dto/state/PointData.java
New file @@ -0,0 +1,10 @@ package com.ard.tianyu.model.dto.state; import lombok.Data; // 假设 PointData 类定义如下 @Data public class PointData { private double lat; // 纬度 private double lng; // 经度 } ard-work/src/main/java/com/ard/tianyu/model/dto/state/PtzState.java
New file @@ -0,0 +1,12 @@ package com.ard.tianyu.model.dto.state; import lombok.Data; // 云台状态类 @Data public class PtzState { private float pitch; // 俯仰角,范围:-120° ~ 30° private float roll; // 横滚角,范围:-90° ~ 60° private float yaw; // 偏航角,范围:-180° ~ 180° private int gimbalMode; // 云台模式:1=自由模式,2=跟随模式,3=FPV模式 } ard-work/src/main/java/com/ard/tianyu/model/dto/state/UavState.java
New file @@ -0,0 +1,24 @@ package com.ard.tianyu.model.dto.state; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; @Data @Builder @AllArgsConstructor @NoArgsConstructor public class UavState { private FlightControllerState flightControllerState; // 飞控状态 private BatteryState batteryState; // 电池状态 private PtzState ptzState; // 云台状态 private CameraState cameraState; // 相机状态 private MissionState missionState; // 任务状态 private String boxSn; // 云盒编号 private AvoidanceData avoidanceData; // 避障数据 private List<HmsAlarmData> hmsAlarmData; // HMS报警信息 (飞机告警信息) } ard-work/src/main/java/com/ard/tianyu/model/dto/telemetry/TelemetryData.java
New file @@ -0,0 +1,46 @@ package com.ard.tianyu.model.dto.telemetry; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Data @Builder @AllArgsConstructor @NoArgsConstructor public class TelemetryData { private double lng; // 经度 private double lat; // 纬度 private float altitude; // 海拔高度 (椭球高) (单位: 米) private float ultrasonic; // 相对高度 (单位: 米) private float pitch; // 俯仰角 private float roll; // 横滚角 private float yaw; // 偏航角 private float airspeed; // 空速 (单位: 米/秒) private float velocity; // 地速 (单位: 米/秒) private long timestamp; // 时间戳 private float ptpitch; // 载荷俯仰角 private float ptroll; // 载荷横滚角 private float ptyaw; // 载荷航向角 private float zoomfactor; // 载荷当前倍数 private String boxSn; // 云盒编号 private String batteryPower; // 电池电量 (多个电池以下划线分割) private int satelliteCount; // 卫星计数 private long taskId; // 任务编号 private double rtkLng; // RTK 经度 private double rtkLat; // RTK 纬度 private float rtkHFSL; // RTK 海拔高度 (平均海平面高度) (单位: 米) private int rtkPositionInfo; // RTK 状态 private int airFlyTimes; // 当前架次飞行时长 (单位: 秒) private float airFlyDistance; // 当前架次飞行实际距离 (单位: 米) private String uavSn; // 无人机编号 private String uavModel; // 无人机型号 private float homeRange; // 距降落点水平直线距离 (单位: 米) private int flightMode; // 飞行模式 private float targetDistance; // 距目标点实际距离 (单位: 米) private int predictFlyTime; // 预计剩余飞行时间 private float ultrasonicMax; // 本架次最大相对高度值 private float ultrasonicMin; // 本架次最小相对高度值 } ard-work/src/main/java/com/ard/tianyu/netty/command/ControlAngleCommand.java
@@ -32,14 +32,14 @@ public short getDataLength() { // 计算数据长度: 云盒编号 + 指令编号 + 加密标志 + 动作编号 + 转动角度 return (short) (2 + 15 + 1 + 1 + 1); // 帧头 + 数据内容 return (short) (1 + 15 + 1 + 1 + 1); // 帧头 + 数据内容 } public void encode(ByteBuf out) { out.writeShort(getDataHeader()); // 写入帧头 out.writeShort(getDataLength()); // 写入数据长度 out.writeByte(getCmdCode()); // 写入指令编号 out.writeBytes(boxSn); // 写入云盒编号 out.writeByte(getCmdCode()); // 写入指令编号 out.writeByte(encryptFlag); // 写入加密标志 out.writeByte(actionId); // 写入动作编号 out.writeByte(rotationAngle); // 写入转动角度 ard-work/src/main/java/com/ard/tianyu/netty/command/DroneReadyNotificationCommand.java
New file @@ -0,0 +1,31 @@ package com.ard.tianyu.netty.command; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import io.netty.buffer.ByteBuf; import lombok.Data; /** * 无人机就绪通知命令 * * @author 刘苏义 * @date 2024/10/24 17:18 */ @Data public class DroneReadyNotificationCommand extends BaseCommand { private byte actionCode; private TelemetryDataPB.UavInitInfo uavInitInfo; public DroneReadyNotificationCommand(TelemetryDataPB.UavInitInfo uavInitInfo, byte actionCode) { setDataHeader((short) 0x6A77); // 设置帧头 // setDataLength((short) (1 + uavState.toByteArray().length)); // 数据长度 setCmdCode((byte) 0xD1); // 设置命令编号 this.uavInitInfo = uavInitInfo; this.actionCode = actionCode; } @Override public void encode(ByteBuf out) { } } ard-work/src/main/java/com/ard/tianyu/netty/command/SignalInfoCommand.java
New file @@ -0,0 +1,25 @@ package com.ard.tianyu.netty.command; import com.ard.tianyu.netty.protobuf.TelemetryDataPB; import io.netty.buffer.ByteBuf; import lombok.Data; @Data public class SignalInfoCommand extends BaseCommand { private int delayTime; private TelemetryDataPB.SignalInfo signalInfo; // 构造函数 public SignalInfoCommand(TelemetryDataPB.SignalInfo signalInfo) { setDataHeader((short) 0x6A77); // 设置帧头 // setDataLength((short) (1 + uavState.toByteArray().length)); // 数据长度 setCmdCode((byte) 0x0C); // 设置命令编号 this.signalInfo = signalInfo; } // 实现 encode 方法,如果需要 @Override public void encode(ByteBuf out) { // 根据需要实现编码 } } ard-work/src/main/java/com/ard/tianyu/netty/command/UavInitInfoCommand.java
New file @@ -0,0 +1,37 @@ package com.ard.tianyu.netty.command; import io.netty.buffer.ByteBuf; import lombok.Data; /** * 无人机初始化回复命令 * * @author 刘苏义 * @date 2024/10/22 16:36 */ @Data public class UavInitInfoCommand extends BaseCommand{ private final byte encryptFlag; // 加密标志 private final byte actionId; // 动作编号 private final byte result; // 执行结果 public UavInitInfoCommand(byte encryptFlag, byte actionId, byte result) { setDataHeader((short)0x7479); setDataLength((short) 3); setCmdCode((byte)0xD1); this.encryptFlag = encryptFlag; this.actionId = actionId; this.result = result; } public void encode(ByteBuf out) { out.writeShort(getDataHeader()); // 写入帧头 out.writeShort(getDataLength()); // 写入数据长度 out.writeByte(getCmdCode()); // 写入指令编号 out.writeByte(encryptFlag); // 写入加密标志 out.writeByte(actionId); // 写入动作编号 out.writeByte(result); // 写入执行结果 } } ard-work/src/main/java/com/ard/tianyu/netty/command/UavStateCommand.java
@@ -9,6 +9,9 @@ // 构造函数 public UavStateCommand(TelemetryDataPB.UavState uavState) { setDataHeader((short) 0x6A77); // 设置帧头 // setDataLength((short) (1 + uavState.toByteArray().length)); // 数据长度 setCmdCode((byte) 0xA8); // 设置命令编号 this.uavState = uavState; } ard-work/src/main/java/com/ard/tianyu/netty/command/UavTelemetryDataCommand.java
File was renamed from ard-work/src/main/java/com/ard/tianyu/netty/command/UavDataCommand.java @@ -9,10 +9,13 @@ * @author liusuyi * @date 2024年10月23日9:33 **/ public class UavDataCommand extends BaseCommand{ public class UavTelemetryDataCommand extends BaseCommand{ private TelemetryDataPB.TelemetryData telemetryData; // 构造函数 public UavDataCommand(TelemetryDataPB.TelemetryData telemetryData) { public UavTelemetryDataCommand(TelemetryDataPB.TelemetryData telemetryData) { setDataHeader((short) 0x6A77); // 设置帧头 // setDataLength((short) (1 + uavState.toByteArray().length)); // 数据长度 setCmdCode((byte) 0xA9); // 设置命令编号 this.telemetryData = telemetryData; } ard-work/src/main/java/com/ard/tianyu/netty/handler/MessageProtocolDecoder.java
@@ -54,17 +54,52 @@ in.readBytes(uavStatus); // 读取 ProtoBuf 数据 TelemetryDataPB.UavState uavState = TelemetryDataPB.UavState.parseFrom(uavStatus); baseCommand = new UavStateCommand(uavState); log.warn("无人机状态数据: {}", uavState.getBoxSn()); log.debug("无人机状态数据: {}", uavState.getBoxSn()); break; case (byte) 0xA9: byte[] uavData = new byte[dataLength - 1]; in.readBytes(uavData); TelemetryDataPB.TelemetryData telemetryData = TelemetryDataPB.TelemetryData.parseFrom(uavData); baseCommand = new UavDataCommand(telemetryData); log.warn("无人机遥测数据: {}", telemetryData.getBoxSn()); baseCommand = new UavTelemetryDataCommand(telemetryData); log.debug("遥测数据: {}", telemetryData.getBoxSn()); break; case (byte) 0x0C: int delayTime = in.readInt(); uavData = new byte[15]; in.readBytes(uavData); // String boxSn12 = new String(uavData); uavData = new byte[dataLength - 20]; in.readBytes(uavData); TelemetryDataPB.SignalInfo signalInfo = TelemetryDataPB.SignalInfo.parseFrom(uavData); baseCommand = new SignalInfoCommand(signalInfo); log.debug("信道质量数据: {}", delayTime); break; case (byte) 0xD1: byte encryptionFlag = in.readByte(); byte actionCode = in.readByte(); switch (actionCode) { case 0x42: byte[] uavData1 = new byte[dataLength - 18]; in.readBytes(uavData1); byte[] uavData2 = new byte[15]; in.readBytes(uavData2); TelemetryDataPB.UavInitInfo uavInitInfo = TelemetryDataPB.UavInitInfo.parseFrom(uavData1); baseCommand = new DroneReadyNotificationCommand(uavInitInfo, actionCode); log.info("无人机初始化数据: {}", uavInitInfo.getUavModel()); break; default: log.warn("无法识别的命令码: {}", String.format("0x%02X", cmdCode & 0xFF));//String.format("0x%02X", cmdCode & 0xFF) log.warn("无法识别的命令码动作编号:{}", String.format("0x%02X", actionCode & 0xFF)); break; } break; default: encryptionFlag = in.readByte(); actionCode = in.readByte(); log.warn("无法识别的命令码: {},加密标志:{},动作编号:{}", String.format("0x%02X", cmdCode & 0xFF), String.format("0x%02X", encryptionFlag & 0xFF), String.format("0x%02X", actionCode & 0xFF));//String.format("0x%02X", cmdCode & 0xFF) } if (baseCommand != null) { out.add(baseCommand); ard-work/src/main/java/com/ard/tianyu/netty/protobuf/TelemetryDataBuf-new.proto
@@ -278,3 +278,14 @@ uint32 cameraId=3;//相机编号 uint32 isModeling=4;//是否实时建模:0=不,1=是 } //无人机初始化详情 message UavInitInfo{ string uavModel = 1; //无人机型号 string uavSn=2; //无人机SN string uavVersion = 3; //无人机固件版本号 string ptModel = 4; //挂载相机型号 string ptVersion = 5; //挂载相机固件版本号 uint32 ptPosition = 6; //挂载相机psdk日位置 string moduleVersion = 7; //模组固件版本号 } ard-work/src/main/java/com/ard/tianyu/netty/protobuf/TelemetryDataPB.java
Diff too large ard-work/src/main/java/com/ard/tianyu/service/ITyService.java
@@ -3,6 +3,9 @@ import com.ard.tianyu.model.dto.BoxHistoryDTO; import com.ard.tianyu.model.dto.BoxPhotosDTO; import com.ard.tianyu.model.dto.BoxSettingsDTO; import com.ard.tianyu.model.dto.signal.SignalInfo; import com.ard.tianyu.model.dto.state.UavState; import com.ard.tianyu.model.dto.telemetry.TelemetryData; import com.ard.tianyu.model.receiver.*; import com.ard.tianyu.netty.command.BaseCommand; import com.dtflys.forest.annotation.Var; @@ -67,9 +70,23 @@ /** * 云台转动(基于角度) */ void controlAngle(String boxSn); void controlAngle(String boxSn,Integer actionId,Integer rotationAngle) ; /** * 获取无人机状态 */ UavState getUavState(String boxSn); /** * 获取无人机遥测 */ TelemetryData getUavTelemetryData(String boxSn); /** * 获取信道质量遥测 */ SignalInfo getSignalInfo(String boxSn); /** * 解析天宇服务端发来的消息 */ void processMessage(String key, BaseCommand tyMessage); ard-work/src/main/java/com/ard/tianyu/service/impl/TyServiceImpl.java
@@ -2,9 +2,18 @@ import com.ard.tianyu.config.TyConfiguration; import com.ard.tianyu.http.TyClient; import com.ard.tianyu.model.cache.UavSignalInfoCache; import com.ard.tianyu.model.cache.UavStateCache; import com.ard.tianyu.model.cache.UavTelemetryDataCache; import com.ard.tianyu.model.dto.BoxHistoryDTO; import com.ard.tianyu.model.dto.BoxPhotosDTO; import com.ard.tianyu.model.dto.BoxSettingsDTO; import com.ard.tianyu.model.dto.converter.UavSignalInfoConverter; import com.ard.tianyu.model.dto.converter.UavStateConverter; import com.ard.tianyu.model.dto.converter.UavTelemetryDataConverter; import com.ard.tianyu.model.dto.signal.SignalInfo; import com.ard.tianyu.model.dto.state.UavState; import com.ard.tianyu.model.dto.telemetry.TelemetryData; import com.ard.tianyu.model.receiver.*; import com.ard.tianyu.netty.command.*; import com.ard.tianyu.netty.TyNettyTcpClient; @@ -154,9 +163,33 @@ * 云台控制基于角度 */ @Override public void controlAngle(String boxSn) { ControlAngleCommand controlAngleCommand = new ControlAngleCommand(boxSn.getBytes(), (byte) 0x00, (byte) 0x00, (byte) 0x05); public void controlAngle(String boxSn, Integer actionId, Integer rotationAngle) { ControlAngleCommand controlAngleCommand = new ControlAngleCommand(boxSn.getBytes(), (byte) 0x00, actionId.byteValue(), rotationAngle.byteValue()); this.sendMessage(tyConfiguration.getIpPort(), controlAngleCommand); } /** * 获取无人机状态 */ @Override public UavState getUavState(String boxSn) { return UavStateCache.getUavState(boxSn); } /** * 获取无人机遥测数据 */ @Override public TelemetryData getUavTelemetryData(String boxSn) { return UavTelemetryDataCache.getUavTelemetryData(boxSn); } /** * 获取无人机遥测数据 */ @Override public SignalInfo getSignalInfo(String boxSn) { return UavSignalInfoCache.getUavSignalInfo(boxSn); } @Override @@ -176,7 +209,7 @@ case 0x02: // 处理注册消息 HeartbeatReplyCommand heartbeatReplyCommand = (HeartbeatReplyCommand) tyCommand; log.info("收到心跳消息-->处理心跳消息{}", heartbeatReplyCommand.getTimestamp()); log.debug("收到心跳消息-->处理心跳消息{}", heartbeatReplyCommand.getTimestamp()); break; case 0x03: // 处理订阅消息 @@ -190,14 +223,37 @@ break; case (byte) 0xA8: // 处理无人机状态消息 log.info("收到无人机状态消息-->无人机状态消息"); log.debug("收到无人机状态消息-->无人机状态消息"); UavStateCommand uavStateCommand = (UavStateCommand) tyCommand; UavState uavState = UavStateConverter.convertProtoToEntity(uavStateCommand.getUavState()); UavStateCache.putUavState(uavState); break; case (byte) 0xA9: // 处理无人机遥测消息 log.info("收到无人机遥测消息-->无人机遥测消息"); log.debug("收到无人机遥测消息-->无人机遥测消息"); UavTelemetryDataCommand uavTelemetryDataCommand = (UavTelemetryDataCommand) tyCommand; TelemetryData telemetryData = UavTelemetryDataConverter.convertProtoToEntity(uavTelemetryDataCommand.getTelemetryData()); UavTelemetryDataCache.putUavTelemetryData(telemetryData); break; case (byte) 0x0C: // 处理无人机信道质量数据 log.debug("收到无人机信道质量数据消息-->无人机信道质量数据消息"); SignalInfoCommand signalInfoCommand = (SignalInfoCommand) tyCommand; SignalInfo signalInfo = UavSignalInfoConverter.convertProtoToEntity(signalInfoCommand.getSignalInfo()); UavSignalInfoCache.putUavSignalInfoCache(signalInfo); break; case (byte) 0xD1: // 处理无人机 DroneReadyNotificationCommand droneReadyNotificationCommand = (DroneReadyNotificationCommand) tyCommand; if (droneReadyNotificationCommand.getActionCode() == (byte) 0x42) { UavInitInfoCommand uavInitCommand = new UavInitInfoCommand((byte) 0x00, (byte) 0x42, (byte) 0x01); this.sendMessage(tyConfiguration.getIpPort(), uavInitCommand); log.info("收到无人机初始化详情-回复防止再次发送"); } break; default: // 处理其他消息 log.debug("收到其他消息-->其他消息"); } }