Compare commits

...

2 Commits

Author SHA1 Message Date
孙小云 30f3ec344d 处理机场飞行控制数据 2026-02-27 15:30:09 +08:00
孙小云 9f6b5f21bb 处理机场飞行控制数据 2026-02-27 14:15:31 +08:00
16 changed files with 425 additions and 248 deletions

View File

@ -2,11 +2,7 @@ package com.ruoyi.device.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.device.api.domain.DroneCurrentStatusVO;
import com.ruoyi.device.api.domain.DroneFlightControlRequest;
import com.ruoyi.device.api.domain.DroneRealtimeInfoVO;
import com.ruoyi.device.api.domain.DroneTakeoffResponseVO;
import com.ruoyi.device.api.domain.MachineStateVO;
import com.ruoyi.device.api.domain.*;
import com.ruoyi.device.api.enums.DroneCurrentStatusEnum;
import com.ruoyi.device.api.enums.DroneMissionStatusEnum;
import com.ruoyi.device.domain.impl.machine.MachineCommandManager;
@ -19,6 +15,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
/**
@ -156,32 +154,38 @@ public class AircraftFlyController extends BaseController
}
/**
* 无人机起飞接口
* 无人机一键起飞
*
* @param sn 机场SN号
* @param request 起飞请求对象
* @return 起飞响应
*/
@Operation(summary = "无人机起飞", description = "控制指定机场的无人机执行起飞操作")
@PostMapping("/takeoff/{sn}")
public R<String> takeoff(
@Parameter(description = "机场SN号", required = true, example = "THJSQ03B2309DN7VQN43")
@PathVariable("sn") String sn)
@Operation(summary = "无人机一键起飞", description = "控制指定机场的无人机执行起飞操作")
@PostMapping("/takeoff")
public R<String> takeoff(@RequestBody DroneTakeoffRequest request)
{
log.info("收到无人机起飞请求: sn={}", sn);
log.info("收到无人机起飞请求: sn={}, messageID={}", request.getSn(), request.getMessageID());
try {
CompletableFuture<CommandResult> future = machineCommandManager.executeCommand(sn, CommandType.TAKE_OFF);
java.util.Map<String, Object> params = new java.util.HashMap<>();
if(Objects.isNull(request.getMessageID())){
params.put("messageID", UUID.randomUUID().toString());
}else {
params.put("messageID", request.getMessageID());
}
params.put("airlineFileUrl", request.getAirlineFileUrl());
params.put("flyBatteryMin", request.getFlyBatteryMin());
CompletableFuture<CommandResult> future = machineCommandManager.executeCommand(request.getSn(), CommandType.TAKE_OFF, params);
CommandResult result = future.get();
if (result.isSuccess()) {
log.info("无人机起飞成功: sn={}", sn);
log.info("无人机起飞成功: sn={}", request.getSn());
return R.ok("起飞命令执行成功");
} else {
log.error("无人机起飞失败: sn={}, reason={}", sn, result.getErrorMessage());
log.error("无人机起飞失败: sn={}, reason={}", request.getSn(), result.getErrorMessage());
return R.fail("起飞命令执行失败: " + result.getErrorMessage());
}
} catch (Exception e) {
log.error("无人机起飞异常: sn={}", sn, e);
log.error("无人机起飞异常: sn={}", request.getSn(), e);
return R.fail("起飞命令执行异常: " + e.getMessage());
}
}
@ -360,30 +364,36 @@ public class AircraftFlyController extends BaseController
/**
* 无人机返航接口
*
* @param sn 机场SN号
* @param request 返航请求对象
* @return 返航响应
*/
@Operation(summary = "无人机返航", description = "控制指定机场的无人机执行返航操作")
@PostMapping("/return-home/{sn}")
public R<String> returnHome(
@Parameter(description = "机场SN号", required = true, example = "THJSQ03B2309DN7VQN43")
@PathVariable("sn") String sn)
@PostMapping("/return-home")
public R<String> returnHome(@RequestBody DroneReturnHomeRequest request)
{
log.info("收到无人机返航请求: sn={}", sn);
log.info("收到无人机返航请求: sn={}, messageID={}, taskId={}", request.getSn(), request.getMessageID(), request.getTaskId());
try {
CompletableFuture<CommandResult> future = machineCommandManager.executeCommand(sn, CommandType.RETURN_HOME);
java.util.Map<String, Object> params = new java.util.HashMap<>();
if(Objects.isNull(request.getMessageID())){
params.put("messageID", UUID.randomUUID().toString());
}else {
params.put("messageID", request.getMessageID());
}
params.put("taskId", 9074);
params.put("zhilin", request.getZhilin());
CompletableFuture<CommandResult> future = machineCommandManager.executeCommand(request.getSn(), CommandType.RETURN_HOME, params);
CommandResult result = future.get();
if (result.isSuccess()) {
log.info("无人机返航成功: sn={}", sn);
log.info("无人机返航成功: sn={}", request.getSn());
return R.ok("返航命令执行成功");
} else {
log.error("无人机返航失败: sn={}, reason={}", sn, result.getErrorMessage());
log.error("无人机返航失败: sn={}, reason={}", request.getSn(), result.getErrorMessage());
return R.fail("返航命令执行失败: " + result.getErrorMessage());
}
} catch (Exception e) {
log.error("无人机返航异常: sn={}", sn, e);
log.error("无人机返航异常: sn={}", request.getSn(), e);
return R.fail("返航命令执行异常: " + e.getMessage());
}
}

View File

@ -82,4 +82,23 @@ public class InstructionContext {
public Object getCommandParam(String key) {
return commandParams.get(key);
}
/**
* 获取命令参数并转换为指定类型
* @param key 参数键
* @param type 目标类型
* @param <T> 泛型类型
* @return 转换后的参数值如果不存在或类型不匹配则返回null
*/
@SuppressWarnings("unchecked")
public <T> T getCommandParam(String key, Class<T> type) {
Object value = commandParams.get(key);
if (value == null) {
return null;
}
if (type.isInstance(value)) {
return (T) value;
}
return null;
}
}

View File

@ -59,9 +59,8 @@ public class TuohengVendorConfig implements VendorConfig {
&& droneState == DroneState.ONLINE;
case TAKE_OFF:
// 起飞前置条件无人机已开机机场在线
return droneState == DroneState.ONLINE
&& airportState == AirportState.ONLINE;
// 起飞前置条件机场在线
return airportState == AirportState.ONLINE;
case RETURN_HOME:
// 返航前置条件无人机飞行中

View File

@ -19,10 +19,35 @@ public class TuohengReturnHomeInstruction extends AbstractInstruction {
String sn = context.getSn();
log.info("发送拓恒无人机返航指令: sn={}", sn);
long timestamp = System.currentTimeMillis();
// 从上下文获取 messageID如果没有则使用时间戳
Long messageID = context.getCommandParam("messageID", Long.class);
if (messageID == null) {
messageID = timestamp;
}
// 从上下文获取 taskId如果没有则使用默认值
Long taskId = context.getCommandParam("taskId", Long.class);
if (taskId == null) {
taskId = 9074L;
}
// 从上下文获取 zhilin如果没有则使用默认值
String zhilin = context.getCommandParam("zhilin", String.class);
if (zhilin == null) {
zhilin = "03";
}
JSONObject param = new JSONObject();
param.put("zhilin", zhilin);
param.put("taskId", taskId);
JSONObject payload = new JSONObject();
payload.put("messageID", System.currentTimeMillis());
payload.put("timestamp", System.currentTimeMillis());
payload.put("zhilin", "03");
payload.put("param", param);
payload.put("messageID", messageID);
payload.put("action", "immediateReturn");
payload.put("timestamp", timestamp);
String topic = "/topic/v1/airportFly/" + sn + "/control";
context.getMqttClient().sendMessage(topic, payload.toJSONString());
@ -31,13 +56,7 @@ public class TuohengReturnHomeInstruction extends AbstractInstruction {
@Override
public CallbackConfig getMethodCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("/topic/v1/airportFly/" + sn + "/control/confirm")
.fieldPath("msg")
.expectedValue("[综管]立即返航指令接收成功")
.timeoutMs(10000)
.build();
return null; // 移除方法回调避免回调不匹配问题
}
@Override

View File

@ -21,16 +21,33 @@ public class TuohengTakeOffInstruction extends AbstractInstruction {
long timestamp = System.currentTimeMillis();
// 从上下文获取 messageID如果没有则使用时间戳
Long messageID = context.getCommandParam("messageID", Long.class);
if (messageID == null) {
messageID = timestamp;
}
// 从上下文获取 airlineFileUrl如果没有则使用默认值
String airlineFileUrl = context.getCommandParam("airlineFileUrl", String.class);
if (airlineFileUrl == null) {
airlineFileUrl = "https://minio-jndsj.t-aaron.com:2443/th-airport/testFile/13912c62-b96f-4df5-ab65-813c8c4b04eb.waypoints";
}
// 从上下文获取 flyBatteryMin如果没有则使用默认值
Double flyBatteryMin = context.getCommandParam("flyBatteryMin", Double.class);
if (flyBatteryMin == null) {
flyBatteryMin = 0.3;
}
JSONObject param = new JSONObject();
param.put("airlineFileUrl", "http://45.120.103.238:8899/waypoint/default.waypoints");
param.put("flyBatteryMin", 0.3);
param.put("isMustFly", 1);
param.put("flyBatteryMin", flyBatteryMin);
param.put("airlineFileUrl", airlineFileUrl);
JSONObject payload = new JSONObject();
payload.put("messageID", String.valueOf(timestamp));
payload.put("timestamp", timestamp);
payload.put("action", "airlineFlight");
payload.put("messageID", String.valueOf(messageID));
payload.put("param", param);
payload.put("timestamp", timestamp);
String topic = "/topic/v1/airportFly/" + sn + "/control";
context.getMqttClient().sendMessage(topic, payload.toJSONString());

View File

@ -0,0 +1,15 @@
package com.ruoyi.device.domain.impl.tuohengmqtt.callback;
/**
* 机场飞行控制回调接口
* 处理 /topic/v1/airportFly/+/control 主题消息
*/
public interface IAirportFlyControlCallback {
/**
* 处理机场飞行控制消息
* @param deviceSn 设备SN
* @param payload 消息内容
* @param topic 主题
*/
void onAirportFlyControl(String deviceSn, String payload, String topic);
}

View File

@ -2,6 +2,7 @@ package com.ruoyi.device.domain.impl.tuohengmqtt.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.device.domain.impl.machine.mqtt.MqttCallbackRegistry;
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IAirportFlyControlCallback;
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IDroneRealTimeCallback;
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IHeartbeatMessageCallback;
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IRealTimeBasicCallback;
@ -37,6 +38,7 @@ public class TuohengMqttMessageHandler {
private final List<IRealTimeBasicCallback> realTimeBasicCallbacks = new ArrayList<>();
private final List<IDroneRealTimeCallback> droneRealTimeCallbacks = new ArrayList<>();
private final List<IHeartbeatMessageCallback> heartbeatMessageCallbacks = new ArrayList<>();
private final List<IAirportFlyControlCallback> airportFlyControlCallbacks = new ArrayList<>();
private final List<IAirportFlyControlDataCallback> airportFlyControlDataCallbacks = new ArrayList<>();
private static final Pattern TUOHENG_SN_PATTERN = Pattern.compile("^TH[0-9A-Z]+");
@ -92,6 +94,13 @@ public class TuohengMqttMessageHandler {
}
}
public void registerAirportFlyControlCallback(IAirportFlyControlCallback callback) {
if (callback != null && !airportFlyControlCallbacks.contains(callback)) {
airportFlyControlCallbacks.add(callback);
log.info("注册机场飞行控制回调: {}", callback.getClass().getSimpleName());
}
}
public void registerAirportFlyControlDataCallback(IAirportFlyControlDataCallback callback) {
if (callback != null && !airportFlyControlDataCallbacks.contains(callback)) {
airportFlyControlDataCallbacks.add(callback);
@ -137,6 +146,8 @@ public class TuohengMqttMessageHandler {
log.debug("机场控制确认消息 - SN: {}", deviceSn);
} else if (matchTopic(topic, AIRPORT_DRONE_REALTIME_TOPIC)) {
handleAirportDroneRealTimeData(deviceSn, payload);
} else if (matchTopic(topic, AIRPORT_FLY_CONTROL_TOPIC)) {
handleAirportFlyControl(deviceSn, payload, topic);
} else if (matchTopic(topic, AIRPORT_FLY_DATA_TOPIC)) {
handleAirportFlyControlData(deviceSn, payload, topic);
} else if (matchTopic(topic, AIRPORT_FLY_CONFIRM_TOPIC)) {
@ -257,6 +268,22 @@ public class TuohengMqttMessageHandler {
}
}
private void handleAirportFlyControl(String deviceSn, String payload, String topic) {
try {
log.info("处理机场飞行控制 - 设备SN: {}, Topic: {}", deviceSn, topic);
for (IAirportFlyControlCallback callback : airportFlyControlCallbacks) {
try {
callback.onAirportFlyControl(deviceSn, payload, topic);
} catch (Exception e) {
log.error("机场飞行控制回调执行失败: {}", e.getMessage(), e);
}
}
} catch (Exception e) {
log.error("处理机场飞行控制失败 - SN: {}, Error: {}", deviceSn, e.getMessage(), e);
}
}
private void handleAirportFlyControlData(String deviceSn, String payload, String topic) {
try {
log.info("处理机场飞行控制数据 - 设备SN: {}, Topic: {}", deviceSn, topic);

View File

@ -24,6 +24,7 @@ public class TuohengMqttClientService {
public static final String AIRPORT_NEST_BASIC_TOPIC = "/topic/v1/airportNest/+/realTime/basic";
public static final String AIRPORT_NEST_CONFIRM_TOPIC = "/topic/v1/airportNest/+/control/confirm";
public static final String AIRPORT_DRONE_REALTIME_TOPIC = "/topic/v1/airportDrone/+/realTime/data";
public static final String AIRPORT_FLY_CONTROL_TOPIC = "/topic/v1/airportFly/+/control";
public static final String AIRPORT_FLY_DATA_TOPIC = "/topic/v1/airportFly/+/control/data";
public static final String AIRPORT_FLY_CONFIRM_TOPIC = "/topic/v1/airportFly/+/control/confirm";
public static final String HEARTBEAT_MESSAGE_TOPIC = "/topic/v1/heartbeat/+/message";
@ -126,6 +127,7 @@ public class TuohengMqttClientService {
AIRPORT_NEST_BASIC_TOPIC,
AIRPORT_NEST_CONFIRM_TOPIC,
AIRPORT_DRONE_REALTIME_TOPIC,
AIRPORT_FLY_CONTROL_TOPIC,
AIRPORT_FLY_DATA_TOPIC,
AIRPORT_FLY_CONFIRM_TOPIC,
HEARTBEAT_MESSAGE_TOPIC,

View File

@ -30,6 +30,14 @@ public interface FlightLogMapper
*/
List<FlightLogEntity> selectFlightLogListByFlightId(Long flightId);
/**
* 统计飞行日志数量
*
* @param flightId 飞行ID
* @return 日志数量
*/
int countFlightLogByFlightId(Long flightId);
/**
* 新增飞行日志
*

View File

@ -38,6 +38,15 @@ public interface FlightMapper
*/
FlightEntity selectFlightByDeviceSnAndStatus(@Param("deviceSn") String deviceSn, @Param("status") String status);
/**
* 根据设备SN和外部飞行ID查询飞行记录
*
* @param deviceSn 设备SN号
* @param flightIdExternal 外部飞行ID
* @return 飞行信息
*/
FlightEntity selectFlightByDeviceSnAndFlightIdExternal(@Param("deviceSn") String deviceSn, @Param("flightIdExternal") String flightIdExternal);
/**
* 新增飞行记录
*

View File

@ -93,4 +93,12 @@ public interface FlightService
* @param logEntity 飞行日志实体
*/
void insertFlightLog(com.ruoyi.device.mapper.entity.FlightLogEntity logEntity);
/**
* 检查飞行记录是否有飞行日志
*
* @param flightId 飞行ID
* @return true=有飞行日志已起飞false=无飞行日志未起飞
*/
boolean hasFlightLog(Long flightId);
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.device.service.impl;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IAirportFlyControlCallback;
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IAirportFlyControlDataCallback;
import com.ruoyi.device.mapper.entity.FlightEntity;
import com.ruoyi.device.mapper.entity.FlightLogEntity;
@ -10,16 +11,19 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Slf4j
@Component
public class FlightEventCallback implements IAirportFlyControlDataCallback {
public class FlightEventCallback implements IAirportFlyControlCallback, IAirportFlyControlDataCallback {
@Autowired
private FlightService flightService;
@Override
public void onAirportFlyControlData(String deviceSn, String payload, String topic) {
log.info("【FlightEventCallback】收到飞行事件: deviceSn={}, topic={}, payload={}", deviceSn, topic, payload);
public void onAirportFlyControl(String deviceSn, String payload, String topic) {
log.info("【FlightEventCallback】收到飞行控制消息: deviceSn={}, topic={}, payload={}", deviceSn, topic, payload);
try {
JSONObject data = JSONObject.parseObject(payload);
@ -28,61 +32,146 @@ public class FlightEventCallback implements IAirportFlyControlDataCallback {
return;
}
String msg = data.getString("msg");
String messageID = data.getString("messageID");
String action = data.getString("action");
handleControlMessage(deviceSn, data);
} catch (Exception e) {
log.error("【FlightEventCallback】处理飞行控制消息失败: deviceSn={}, topic={}, error={}", deviceSn, topic, e.getMessage(), e);
}
}
log.info("【FlightEventCallback】解析飞行事件数据: deviceSn={}, messageID={}, msg=, action={}", deviceSn, messageID, msg, action);
@Override
public void onAirportFlyControlData(String deviceSn, String payload, String topic) {
log.info("【FlightEventCallback】收到飞行控制数据消息: deviceSn={}, topic={}, payload={}", deviceSn, topic, payload);
if (action == null || action.isEmpty()) {
JSONObject dataObj = data.getJSONObject("data");
if (dataObj != null) {
action = dataObj.getString("action");
log.info("【FlightEventCallback】从data对象中获取action: deviceSn={}, action={}", deviceSn, action);
}
}
FlightEntity flight;
if (messageID != null && !messageID.isEmpty()) {
log.info("【FlightEventCallback】通过messageID获取飞行记录: deviceSn={}, messageID={}", deviceSn, messageID);
flight = handleFlightIdExternal(deviceSn, messageID);
} else {
log.info("【FlightEventCallback】获取当前飞行记录: deviceSn={}", deviceSn);
flight = flightService.getOrCreateCurrentFlight(deviceSn);
}
if (flight == null) {
log.error("【FlightEventCallback】获取飞行记录失败: deviceSn={}, messageID={}", deviceSn, messageID);
try {
JSONObject data = JSONObject.parseObject(payload);
if (data == null) {
log.warn("【FlightEventCallback】解析payload失败: deviceSn={}, topic={}, payload={}", deviceSn, topic, payload);
return;
}
log.info("【FlightEventCallback】获取到飞行记录: deviceSn={}, flightId={}, status={}", deviceSn, flight.getFlightId(), flight.getStatus());
if (msg != null && !msg.isEmpty()) {
if (msg.contains("自检")) {
log.info("【FlightEventCallback】检测到自检消息: deviceSn={}, msg={}", deviceSn, msg);
handlePreCheckLog(deviceSn, msg, data, flight);
} else {
log.info("【FlightEventCallback】检测到飞行日志消息: deviceSn={}, msg={}", deviceSn, msg);
handleFlightLog(deviceSn, msg, flight);
}
handleFlightStatus(deviceSn, action, data, flight);
} else {
log.warn("【FlightEventCallback】消息内容为空: deviceSn={}, messageID={}", deviceSn, messageID);
}
handleControlDataMessage(deviceSn, data);
} catch (Exception e) {
log.error("【FlightEventCallback】处理飞行事件失败: deviceSn={}, topic={}, error={}", deviceSn, topic, e.getMessage(), e);
log.error("【FlightEventCallback】处理飞行控制数据消息失败: deviceSn={}, topic={}, error={}", deviceSn, topic, e.getMessage(), e);
}
}
private FlightEntity handleFlightIdExternal(String deviceSn, String messageId) {
if (messageId != null && !messageId.isEmpty()) {
return flightService.getOrCreateFlightByMessageId(deviceSn, messageId);
/**
* 处理 /control 主题消息
* 收到 airlineFlight 动作时创建飞行记录
*/
private void handleControlMessage(String deviceSn, JSONObject data) {
String action = data.getString("action");
String messageID = data.getString("messageID");
log.info("【FlightEventCallback】处理control消息: deviceSn={}, action={}, messageID={}", deviceSn, action, messageID);
if ("airlineFlight".equals(action) && messageID != null && !messageID.isEmpty()) {
// 创建飞行记录如果不存在
flightService.getOrCreateFlightByMessageId(deviceSn, messageID);
log.info("【FlightEventCallback】创建/获取飞行记录: deviceSn={}, messageID={}", deviceSn, messageID);
} else if ("immediateReturn".equals(action) && messageID != null && !messageID.isEmpty()) {
FlightEntity flight = flightService.getOrCreateFlightByMessageId(deviceSn, messageID);
flightService.updateFlightStatus(flight.getFlightId(), "RETURNING");
log.info("【FlightEventCallback】更新飞行状态为RETURNING: deviceSn={}, flightId={}", deviceSn, flight.getFlightId());
}
}
/**
* 处理 /control/data 主题消息
* 根据消息内容写入自检日志或飞行日志并更新飞行状态
*/
private void handleControlDataMessage(String deviceSn, JSONObject data) {
String msg = data.getString("msg");
String messageID = data.getString("messageID");
Integer code = data.getInteger("code");
if (messageID == null || messageID.isEmpty()) {
log.warn("【FlightEventCallback】消息缺少messageID: deviceSn={}, msg={}", deviceSn, msg);
return;
}
// 通过 messageID 获取飞行记录
FlightEntity flight = flightService.getOrCreateFlightByMessageId(deviceSn, messageID);
if (flight == null) {
log.error("【FlightEventCallback】获取飞行记录失败: deviceSn={}, messageID={}", deviceSn, messageID);
return;
}
log.info("【FlightEventCallback】处理control/data消息: deviceSn={}, flightId={}, messageID={}, msg={}, code={}",
deviceSn, flight.getFlightId(), messageID, msg, code);
// 判断是否为 [地面站]无人机起飞成功
if (msg != null && msg.contains("[地面站]无人机起飞成功")) {
// 起飞成功存到 device_flight_log
handleFlightLog(deviceSn, msg, flight);
// 更新状态为 FLYING
log.info("【FlightEventCallback】检测到起飞成功更新状态为FLYING: deviceSn={}, flightId={}", deviceSn, flight.getFlightId());
flightService.updateFlightStatus(flight.getFlightId(), "FLYING");
return;
}
// 检查 device_flight_log 是否有数据判断是否已起飞
boolean hasTakenOff = flightService.hasFlightLog(flight.getFlightId());
if (hasTakenOff) {
// 已起飞所有消息存到 device_flight_log
log.info("【FlightEventCallback】已起飞存入飞行日志: deviceSn={}, flightId={}, msg={}", deviceSn, flight.getFlightId(), msg);
handleFlightLog(deviceSn, msg, flight);
// 检查是否任务完成
String dataContent = data.getString("data");
if ("操作成功".equals(msg) && "[地面站]任务飞行完成".equals(dataContent)) {
log.info("【FlightEventCallback】检测到任务完成更新状态为HOME: deviceSn={}, flightId={}", deviceSn, flight.getFlightId());
flightService.updateFlightStatus(flight.getFlightId(), "HOME");
}
} else {
return flightService.getOrCreateCurrentFlight(deviceSn);
// 未起飞所有消息存到 device_pre_check_log
log.info("【FlightEventCallback】未起飞存入自检日志: deviceSn={}, flightId={}, msg={}, code={}", deviceSn, flight.getFlightId(), msg, code);
handlePreCheckLog(deviceSn, msg, code, flight);
// 检查是否自检失败code=1 表示失败
if (code != null && (code == 1 || code == -1)) {
log.info("【FlightEventCallback】检测到自检失败(code=1)更新状态为ERROR: deviceSn={}, flightId={}", deviceSn, flight.getFlightId());
flightService.updateFlightStatus(flight.getFlightId(), "ERROR");
}
}
}
/**
* 保存自检日志
* @param code 0=成功1=失败
*/
private void handlePreCheckLog(String deviceSn, String msg, Integer code, FlightEntity flight) {
if (flight == null) {
log.error("【FlightEventCallback】飞行记录为空无法保存自检日志: deviceSn={}, msg={}", deviceSn, msg);
return;
}
log.info("【FlightEventCallback】准备保存自检日志: deviceSn={}, flightId={}, msg={}, code={}",
deviceSn, flight.getFlightId(), msg, code);
try {
// code=0 表示成功code=1 表示失败
Boolean success = (code != null && code == 0);
PreCheckLogEntity logEntity = new PreCheckLogEntity();
logEntity.setFlightId(flight.getFlightId());
logEntity.setLogContent(msg);
logEntity.setSuccess(success);
flightService.insertPreCheckLog(logEntity);
log.info("【FlightEventCallback】成功保存自检日志: deviceSn={}, flightId={}, logId={}, msg={}, code={}, success={}",
deviceSn, flight.getFlightId(), logEntity.getLogId(), msg, code, success);
} catch (Exception e) {
log.error("【FlightEventCallback】保存自检日志失败: deviceSn={}, flightId={}, msg={}, error={}",
deviceSn, flight.getFlightId(), msg, e.getMessage(), e);
}
}
/**
* 保存飞行日志
*/
private void handleFlightLog(String deviceSn, String message, FlightEntity flight) {
if (flight == null) {
log.error("【FlightEventCallback】飞行记录为空无法保存飞行日志: deviceSn={}, message={}", deviceSn, message);
@ -105,60 +194,4 @@ public class FlightEventCallback implements IAirportFlyControlDataCallback {
deviceSn, flight.getFlightId(), message, e.getMessage(), e);
}
}
private void handlePreCheckLog(String deviceSn, String msg, JSONObject data, FlightEntity flight) {
if (flight == null) {
log.error("【FlightEventCallback】飞行记录为空无法保存自检日志: deviceSn={}, msg={}", deviceSn, msg);
return;
}
log.info("【FlightEventCallback】准备保存自检日志: deviceSn={}, flightId={}, msg={}", deviceSn, flight.getFlightId(), msg);
try {
Boolean success = msg.contains("通过") || msg.contains("成功");
PreCheckLogEntity logEntity = new PreCheckLogEntity();
logEntity.setFlightId(flight.getFlightId());
logEntity.setLogContent(msg);
logEntity.setSuccess(success);
flightService.insertPreCheckLog(logEntity);
log.info("【FlightEventCallback】成功保存自检日志: deviceSn={}, flightId={}, logId={}, msg={}, success={}",
deviceSn, flight.getFlightId(), logEntity.getLogId(), msg, success);
} catch (Exception e) {
log.error("【FlightEventCallback】保存自检日志失败: deviceSn={}, flightId={}, msg={}, error={}",
deviceSn, flight.getFlightId(), msg, e.getMessage(), e);
}
}
private void handleFlightStatus(String deviceSn, String action, JSONObject data, FlightEntity flight) {
if (flight == null) {
log.error("【FlightEventCallback】飞行记录为空无法更新状态: deviceSn={}, action={}", deviceSn, action);
return;
}
log.debug("【FlightEventCallback】检查是否需要更新飞行状态: deviceSn={}, flightId={}, action={}", deviceSn, flight.getFlightId(), action);
try {
String msg = data.getString("msg");
String dataContent = data.getString("data");
if ((msg != null && msg.contains("起飞成功")) || (dataContent != null && dataContent.contains("起飞成功"))) {
log.info("【FlightEventCallback】检测到起飞成功更新状态为飞行中: deviceSn={}, flightId={}", deviceSn, flight.getFlightId());
flightService.updateFlightStatus(flight.getFlightId(), "飞行中");
log.info("【FlightEventCallback】飞行状态更新成功: deviceSn={}, flightId={}, status=飞行中", deviceSn, flight.getFlightId());
} else if ((msg != null && msg.contains("返航成功")) || (dataContent != null && dataContent.contains("返航成功")) ||
(dataContent != null && dataContent.contains("任务飞行完成"))) {
log.info("【FlightEventCallback】检测到返航成功更新状态为已返航: deviceSn={}, flightId={}", deviceSn, flight.getFlightId());
flightService.updateFlightStatus(flight.getFlightId(), "已返航");
log.info("【FlightEventCallback】飞行状态更新成功: deviceSn={}, flightId={}, status=已返航", deviceSn, flight.getFlightId());
} else {
log.debug("【FlightEventCallback】无需更新飞行状态: deviceSn={}, flightId={}, msg={}", deviceSn, flight.getFlightId(), msg);
}
} catch (Exception e) {
log.error("【FlightEventCallback】更新飞行状态失败: deviceSn={}, flightId={}, action={}, error={}",
deviceSn, flight.getFlightId(), action, e.getMessage(), e);
}
}
}

View File

@ -32,89 +32,91 @@ public class FlightLogCallback implements IDroneRealTimeCallback {
@Override
public void onDroneRealTimeData(String deviceSn, DroneRealTimeData data) {
if (data == null) {
log.warn("【FlightLogCallback】收到空数据: deviceSn={}", deviceSn);
return;
}
return;
if (data.getData() == null) {
log.warn("【FlightLogCallback】收到空数据体: deviceSn={}, messageId={}", deviceSn, data.getMessageID());
return;
}
log.info("【FlightLogCallback】处理实时消息: deviceSn={}, messageId={}, data={}", deviceSn, data.getMessageID(), JSON.toJSONString(data.getData()));
DroneRealTimeData.DroneInfo droneInfo = data.getData();
try {
if (droneInfo.getJiancha() != null && !droneInfo.getJiancha().isEmpty()) {
log.info("【FlightLogCallback】检测到自检数据: deviceSn={}, messageId={}, checkBody={}", deviceSn, data.getMessageID(), data.getData().getJiancha());
handlePreCheckLog(deviceSn, data.getMessageID(), droneInfo.getJiancha());
} else {
log.debug("【FlightLogCallback】实时消息中无自检数据: deviceSn={}, messageId={}", deviceSn, data.getMessageID());
}
} catch (Exception e) {
log.error("【FlightLogCallback】处理自检日志失败: deviceSn={}, messageId={}, error={}", deviceSn, data.getMessageID(), e.getMessage(), e);
}
// if (data == null) {
// log.warn("【FlightLogCallback】收到空数据: deviceSn={}", deviceSn);
// return;
// }
//
// if (data.getData() == null) {
// log.warn("【FlightLogCallback】收到空数据体: deviceSn={}, messageId={}", deviceSn, data.getMessageID());
// return;
// }
//
// log.info("【FlightLogCallback】处理实时消息: deviceSn={}, messageId={}, data={}", deviceSn, data.getMessageID(), JSON.toJSONString(data.getData()));
//
// DroneRealTimeData.DroneInfo droneInfo = data.getData();
//
// try {
// if (droneInfo.getJiancha() != null && !droneInfo.getJiancha().isEmpty()) {
// log.info("【FlightLogCallback】检测到自检数据: deviceSn={}, messageId={}, checkBody={}", deviceSn, data.getMessageID(), data.getData().getJiancha());
// handlePreCheckLog(deviceSn, data.getMessageID(), droneInfo.getJiancha());
// } else {
// log.debug("【FlightLogCallback】实时消息中无自检数据: deviceSn={}, messageId={}", deviceSn, data.getMessageID());
// }
// } catch (Exception e) {
// log.error("【FlightLogCallback】处理自检日志失败: deviceSn={}, messageId={}, error={}", deviceSn, data.getMessageID(), e.getMessage(), e);
// }
}
private void handlePreCheckLog(String deviceSn, String messageID, String jianchaJson) {
log.info("【FlightLogCallback】开始处理自检日志: deviceSn={}, messageId={}, jianchaJson={}", deviceSn, messageID, jianchaJson);
try {
FlightEntity flight;
if (messageID != null && !messageID.isEmpty()) {
log.info("【FlightLogCallback】通过messageId获取飞行记录: deviceSn={}, messageId={}", deviceSn, messageID);
flight = flightService.getOrCreateFlightByMessageId(deviceSn, messageID);
} else {
log.info("【FlightLogCallback】获取当前飞行记录: deviceSn={}", deviceSn);
flight = flightService.getOrCreateCurrentFlight(deviceSn);
}
if (flight == null) {
log.error("【FlightLogCallback】获取飞行记录失败无法保存自检日志: deviceSn={}, messageId={}", deviceSn, messageID);
return;
}
log.info("【FlightLogCallback】获取到飞行记录: deviceSn={}, flightId={}, status={}", deviceSn, flight.getFlightId(), flight.getStatus());
JSONArray checkItems = JSON.parseArray(jianchaJson);
if (checkItems == null || checkItems.isEmpty()) {
log.warn("【FlightLogCallback】自检数据为空或解析失败: deviceSn={}, jianchaJson={}", deviceSn, jianchaJson);
return;
}
log.info("【FlightLogCallback】解析到{}个自检项: deviceSn={}, flightId={}", checkItems.size(), deviceSn, flight.getFlightId());
for (int i = 0; i < checkItems.size(); i++) {
JSONObject item = checkItems.getJSONObject(i);
PreCheckLogEntity logEntity = new PreCheckLogEntity();
logEntity.setFlightId(flight.getFlightId());
String check = item.getString("check");
String value = item.getString("value");
Boolean result = item.getBoolean("result");
String statusText = result != null && result ? "自检成功" : "自检失败";
String logContent = check + " " + value + " " + statusText;
logEntity.setLogContent(logContent);
logEntity.setSuccess(result != null ? result : false);
log.info("【FlightLogCallback】准备插入自检日志[{}/{}]: deviceSn={}, flightId={}, check={}, value={}, result={}",
i + 1, checkItems.size(), deviceSn, flight.getFlightId(), check, value, result);
flightService.insertPreCheckLog(logEntity);
log.info("【FlightLogCallback】成功插入自检日志[{}/{}]: deviceSn={}, flightId={}, logId={}",
i + 1, checkItems.size(), deviceSn, flight.getFlightId(), logEntity.getLogId());
}
log.info("【FlightLogCallback】完成保存自检日志: deviceSn={}, flightId={}, 检查项数量={}",
deviceSn, flight.getFlightId(), checkItems.size());
} catch (Exception e) {
log.error("【FlightLogCallback】保存自检日志失败: deviceSn={}, messageId={}, jiancha={}, error={}",
deviceSn, messageID, jianchaJson, e.getMessage(), e);
}
// log.info("【FlightLogCallback】开始处理自检日志: deviceSn={}, messageId={}, jianchaJson={}", deviceSn, messageID, jianchaJson);
//
// try {
// FlightEntity flight;
//
// if (messageID != null && !messageID.isEmpty()) {
// log.info("【FlightLogCallback】通过messageId获取飞行记录: deviceSn={}, messageId={}", deviceSn, messageID);
// flight = flightService.getOrCreateFlightByMessageId(deviceSn, messageID);
// } else {
// log.info("【FlightLogCallback】获取当前飞行记录: deviceSn={}", deviceSn);
// flight = flightService.getOrCreateCurrentFlight(deviceSn);
// }
//
// if (flight == null) {
// log.error("【FlightLogCallback】获取飞行记录失败无法保存自检日志: deviceSn={}, messageId={}", deviceSn, messageID);
// return;
// }
//
// log.info("【FlightLogCallback】获取到飞行记录: deviceSn={}, flightId={}, status={}", deviceSn, flight.getFlightId(), flight.getStatus());
//
// JSONArray checkItems = JSON.parseArray(jianchaJson);
// if (checkItems == null || checkItems.isEmpty()) {
// log.warn("【FlightLogCallback】自检数据为空或解析失败: deviceSn={}, jianchaJson={}", deviceSn, jianchaJson);
// return;
// }
//
// log.info("【FlightLogCallback】解析到{}个自检项: deviceSn={}, flightId={}", checkItems.size(), deviceSn, flight.getFlightId());
//
// for (int i = 0; i < checkItems.size(); i++) {
// JSONObject item = checkItems.getJSONObject(i);
// PreCheckLogEntity logEntity = new PreCheckLogEntity();
// logEntity.setFlightId(flight.getFlightId());
//
// String check = item.getString("check");
// String value = item.getString("value");
// Boolean result = item.getBoolean("result");
//
// String statusText = result != null && result ? "自检成功" : "自检失败";
// String logContent = check + " " + value + " " + statusText;
//
// logEntity.setLogContent(logContent);
// logEntity.setSuccess(result != null ? result : false);
//
// log.info("【FlightLogCallback】准备插入自检日志[{}/{}]: deviceSn={}, flightId={}, check={}, value={}, result={}",
// i + 1, checkItems.size(), deviceSn, flight.getFlightId(), check, value, result);
//
// flightService.insertPreCheckLog(logEntity);
//
// log.info("【FlightLogCallback】成功插入自检日志[{}/{}]: deviceSn={}, flightId={}, logId={}",
// i + 1, checkItems.size(), deviceSn, flight.getFlightId(), logEntity.getLogId());
// }
// log.info("【FlightLogCallback】完成保存自检日志: deviceSn={}, flightId={}, 检查项数量={}",
// deviceSn, flight.getFlightId(), checkItems.size());
// } catch (Exception e) {
// log.error("【FlightLogCallback】保存自检日志失败: deviceSn={}, messageId={}, jiancha={}, error={}",
// deviceSn, messageID, jianchaJson, e.getMessage(), e);
// }
}
}

View File

@ -50,34 +50,26 @@ public class FlightServiceImpl implements FlightService
@Override
@Transactional(rollbackFor = Exception.class)
public FlightEntity getOrCreateFlightByMessageId(String deviceSn, String messageId) {
FlightEntity flight = flightMapper.selectLatestFlightByDeviceSn(deviceSn);
// 先查询是否存在相同 deviceSn flight_id_extern 的记录
FlightEntity flight = flightMapper.selectFlightByDeviceSnAndFlightIdExternal(deviceSn, messageId);
if (flight == null) {
flight = createFlight(deviceSn);
if (messageId != null && !messageId.isEmpty()) {
updateFlightIdExternal(flight.getFlightId(), messageId);
}
if (flight != null) {
// 如果存在直接返回
log.info("找到已存在的飞行记录: deviceSn={}, messageId={}, flightId={}",
deviceSn, messageId, flight.getFlightId());
return flight;
}
String existingFlightIdExternal = flight.getFlightIdExternal();
if (existingFlightIdExternal == null || existingFlightIdExternal.isEmpty()) {
if (messageId != null && !messageId.isEmpty()) {
updateFlightIdExternal(flight.getFlightId(), messageId);
}
return flight;
}
// 如果不存在创建新的飞行记录
flight = new FlightEntity();
flight.setDeviceSn(deviceSn);
flight.setFlightIdExternal(messageId);
flight.setStatus("CHECKING");
flightMapper.insertFlight(flight);
if (existingFlightIdExternal.equals(messageId)) {
return flight;
}
log.info("创建新的飞行记录: deviceSn={}, messageId={}, flightId={}, status=CHECKING",
deviceSn, messageId, flight.getFlightId());
flight = createFlight(deviceSn);
if (messageId != null && !messageId.isEmpty()) {
updateFlightIdExternal(flight.getFlightId(), messageId);
}
log.info("messageId不同创建新飞行 - deviceSn={}, flightId={}, oldMessageId={}, newMessageId={}",
deviceSn, flight.getFlightId(), existingFlightIdExternal, messageId);
return flight;
}
@ -177,4 +169,10 @@ public class FlightServiceImpl implements FlightService
logEntity.getFlightId(), logEntity.getLogContent());
}
}
@Override
public boolean hasFlightLog(Long flightId) {
int count = flightLogMapper.countFlightLogByFlightId(flightId);
return count > 0;
}
}

View File

@ -32,6 +32,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
order by create_time desc
</select>
<select id="countFlightLogByFlightId" parameterType="Long" resultType="int">
select count(*) from device_flight_log where flight_id = #{flightId}
</select>
<insert id="insertFlightLog" parameterType="com.ruoyi.device.mapper.entity.FlightLogEntity" useGeneratedKeys="true" keyProperty="logId" keyColumn="log_id">
insert into device_flight_log
<trim prefix="(" suffix=")" suffixOverrides=",">

View File

@ -43,6 +43,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
limit 1
</select>
<select id="selectFlightByDeviceSnAndFlightIdExternal" resultMap="FlightResult">
<include refid="selectFlightVo"/>
where device_sn = #{deviceSn}
and flight_id_external = #{flightIdExternal}
limit 1
</select>
<insert id="insertFlight" parameterType="com.ruoyi.device.mapper.entity.FlightEntity" useGeneratedKeys="true" keyProperty="flightId" keyColumn="flight_id">
insert into device_flight
<trim prefix="(" suffix=")" suffixOverrides=",">