1.新增Q20飞机数据上报和飞机OSD消息
This commit is contained in:
parent
b99551d0a2
commit
24591c0eff
|
|
@ -2,6 +2,9 @@ package com.multictrl.modules.business.handler;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.multictrl.common.constant.BusinessConstant;
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
|
import com.multictrl.modules.business.q20.handler.Q20EventsHandler;
|
||||||
|
import com.multictrl.modules.business.q20.handler.Q20OsdTopicHandler;
|
||||||
|
import com.multictrl.modules.business.q20.handler.Q20StateTopicHandler;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
@ -26,7 +29,13 @@ public class TopicDistributor {
|
||||||
private final StateHandler stateHandler;
|
private final StateHandler stateHandler;
|
||||||
private final ServicesReplyHandler servicesReplyHandler;
|
private final ServicesReplyHandler servicesReplyHandler;
|
||||||
private final EventsHandler eventsHandler;
|
private final EventsHandler eventsHandler;
|
||||||
|
private final Q20OsdTopicHandler q20OsdTopicHandler;
|
||||||
|
private final Q20EventsHandler q20EventsHandler;
|
||||||
|
private final Q20StateTopicHandler q20StateTopicHandler;
|
||||||
private final Map<String, MessageHandler> handlerMap = new ConcurrentHashMap<>();
|
private final Map<String, MessageHandler> handlerMap = new ConcurrentHashMap<>();
|
||||||
|
private final Map<String, MessageHandler> q20HandlerMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private static final String Q20_TOPIC_PREFIX = "thing/device/";
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
|
|
@ -36,13 +45,18 @@ public class TopicDistributor {
|
||||||
handlerMap.put(BusinessConstant.STATE, stateHandler);
|
handlerMap.put(BusinessConstant.STATE, stateHandler);
|
||||||
handlerMap.put(BusinessConstant.EVENTS, eventsHandler);
|
handlerMap.put(BusinessConstant.EVENTS, eventsHandler);
|
||||||
handlerMap.put(BusinessConstant.SERVICES_REPLY, servicesReplyHandler);
|
handlerMap.put(BusinessConstant.SERVICES_REPLY, servicesReplyHandler);
|
||||||
|
|
||||||
|
q20HandlerMap.put(BusinessConstant.OSD, q20OsdTopicHandler);
|
||||||
|
q20HandlerMap.put(BusinessConstant.EVENTS, q20EventsHandler);
|
||||||
|
q20HandlerMap.put(BusinessConstant.STATE, q20StateTopicHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void route(String topic, String payload) {
|
public void route(String topic, String payload) {
|
||||||
String method = StrUtil.subAfter(topic, "/", true);
|
String method = StrUtil.subAfter(topic, "/", true);
|
||||||
String gateway = StrUtil.subAfter(StrUtil.subBefore(topic, "/", true),
|
String gateway = StrUtil.subAfter(StrUtil.subBefore(topic, "/", true),
|
||||||
"/", true);
|
"/", true);
|
||||||
MessageHandler handler = handlerMap.get(method);
|
Map<String, MessageHandler> map = topic.startsWith(Q20_TOPIC_PREFIX) ? q20HandlerMap : handlerMap;
|
||||||
|
MessageHandler handler = map.get(method);
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
handler.handleMessage(topic, payload, gateway);
|
handler.handleMessage(topic, payload, gateway);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.multictrl.modules.business.q20.handler;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
|
import com.multictrl.common.utils.CacheUtils;
|
||||||
|
import com.multictrl.common.utils.JsonUtils;
|
||||||
|
import com.multictrl.modules.business.handler.MessageHandler;
|
||||||
|
import com.multictrl.modules.business.q20.influxdb.Q20BatteryReport;
|
||||||
|
import com.multictrl.modules.business.service.InfluxService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20电池数据处理(method: battery)
|
||||||
|
* 上报智能电池详细状态:id, full_charge_capacity, charge_remaining, voltage, current,
|
||||||
|
* low_volt_warn_value, status(0异常/1开机/2充电中/3需保养), temperature, cycle_index,
|
||||||
|
* time_flying, time_remaining, charge_time_remaining, health, flags, uid, version
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Q20BatteryHandler implements MessageHandler {
|
||||||
|
|
||||||
|
private final InfluxService influxService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String topic, String payload, String gateway) {
|
||||||
|
JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message != null) {
|
||||||
|
String deviceSn = message.getStr(BusinessConstant.GATEWAY);
|
||||||
|
JSONObject data = message.getJSONObject(BusinessConstant.DATA);
|
||||||
|
if (data != null) {
|
||||||
|
// 以电池id区分多块电池,缓存key加上电池id后缀
|
||||||
|
Integer batteryId = data.getInt("id");
|
||||||
|
String cacheKey = Q20Constant.Q20_BATTERY + deviceSn
|
||||||
|
+ (batteryId != null ? "_" + batteryId : "");
|
||||||
|
CacheUtils.set(cacheKey, data);
|
||||||
|
saveToInflux(deviceSn, batteryId, data);
|
||||||
|
log.debug("Q20 battery --> deviceSn: {}, id: {}, charge_remaining: {}%, status: {}",
|
||||||
|
deviceSn, batteryId, data.getFloat("charge_remaining"), data.getInt("status"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("Q20 battery --> payload解析失败,解析后为null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveToInflux(String deviceSn, Integer batteryId, JSONObject data) {
|
||||||
|
Q20BatteryReport report = Q20BatteryReport.builder()
|
||||||
|
.deviceSn(deviceSn)
|
||||||
|
.battery_id(batteryId != null ? String.valueOf(batteryId) : null)
|
||||||
|
.order_id(data.getStr("order_id"))
|
||||||
|
.full_charge_capacity(data.getInt("full_charge_capacity"))
|
||||||
|
.charge_remaining(data.getFloat("charge_remaining"))
|
||||||
|
.voltage(data.getInt("voltage"))
|
||||||
|
.current(data.getInt("current"))
|
||||||
|
.low_volt_warn_value(data.getInt("low_volt_warn_value"))
|
||||||
|
.status(data.getInt("status"))
|
||||||
|
.temperature(data.getFloat("temperature"))
|
||||||
|
.cycle_index(data.getInt("cycle_index"))
|
||||||
|
.time_flying(data.getLong("time_flying"))
|
||||||
|
.time_remaining(data.getInt("time_remaining"))
|
||||||
|
.charge_time_remaining(data.getInt("charge_time_remaining"))
|
||||||
|
.health(data.getInt("health"))
|
||||||
|
.flags(data.getInt("flags"))
|
||||||
|
.uid(data.getStr("uid"))
|
||||||
|
.version(data.getStr("version"))
|
||||||
|
.build();
|
||||||
|
influxService.addRecord(report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.multictrl.modules.business.q20.handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20设备相关常量
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
public interface Q20Constant {
|
||||||
|
|
||||||
|
// Q20 osd topic method字段值
|
||||||
|
String METHOD_OSD = "osd";
|
||||||
|
String METHOD_RTK = "rtk";
|
||||||
|
String METHOD_RC = "rc";
|
||||||
|
String METHOD_MOUNT = "mount";
|
||||||
|
String METHOD_BATTERY = "battery";
|
||||||
|
String METHOD_OBS = "obs";
|
||||||
|
// Q20 state topic method字段值
|
||||||
|
String METHOD_VERSION = "version";
|
||||||
|
String METHOD_NOTIFY = "notify";
|
||||||
|
String METHOD_HMS = "hms";
|
||||||
|
// Q20 events topic method字段值
|
||||||
|
String METHOD_LIFTOFF = "liftoff";
|
||||||
|
String METHOD_ARRIVAL = "arrival";
|
||||||
|
String METHOD_TAKEOFF = "takeoff";
|
||||||
|
String METHOD_LAND = "land";
|
||||||
|
String METHOD_NAVIGATE = "navigate";
|
||||||
|
String METHOD_ROUTE_UPLOAD = "route_upload";
|
||||||
|
String METHOD_ROUTE_EXECUTE = "route_execute";
|
||||||
|
String METHOD_ROUTE_AUTO = "route_auto";
|
||||||
|
String METHOD_OTA = "ota";
|
||||||
|
|
||||||
|
// Q20 缓存key前缀
|
||||||
|
String Q20_OSD = "q20_osd_";
|
||||||
|
String Q20_RTK = "q20_rtk_";
|
||||||
|
String Q20_RC = "q20_rc_";
|
||||||
|
String Q20_MOUNT = "q20_mount_";
|
||||||
|
String Q20_BATTERY = "q20_battery_";
|
||||||
|
String Q20_OBS = "q20_obs_";
|
||||||
|
String Q20_VERSION = "q20_version_";
|
||||||
|
String Q20_HMS = "q20_hms_";
|
||||||
|
String Q20_IN_WORK = "q20_in_work_";
|
||||||
|
String Q20_LIFTOFF = "q20_liftoff_";
|
||||||
|
String Q20_ARRIVAL = "q20_arrival_";
|
||||||
|
String Q20_ROUTE_EXECUTE = "q20_route_execute_";
|
||||||
|
String Q20_ROUTE_AUTO = "q20_route_auto_";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,193 @@
|
||||||
|
package com.multictrl.modules.business.q20.handler;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
|
import com.multictrl.common.utils.CacheUtils;
|
||||||
|
import com.multictrl.common.utils.JsonUtils;
|
||||||
|
import com.multictrl.modules.business.handler.MessageHandler;
|
||||||
|
import com.multictrl.modules.business.service.MqttPushService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20设备事件消息处理(topic: thing/device/{device_sn}/events)
|
||||||
|
* 若need_reply=1需回复 events_reply 主题
|
||||||
|
* method: liftoff(起飞事件) | arrival(到达事件) | takeoff(起飞指令进度) | land(降落指令进度) |
|
||||||
|
* navigate(指点飞行进度) | route_upload(航线上传结果) | route_execute(航线执行进度) |
|
||||||
|
* route_auto(一键飞行进度) | ota(固件升级进度)
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Q20EventsHandler implements MessageHandler {
|
||||||
|
|
||||||
|
private final MqttPushService mqttPushService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String topic, String payload, String gateway) {
|
||||||
|
JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message == null) {
|
||||||
|
log.debug("Q20 events --> payload解析失败,解析后为null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String deviceSn = message.getStr(BusinessConstant.GATEWAY);
|
||||||
|
Integer needReply = message.getInt(BusinessConstant.NEED_REPLY);
|
||||||
|
if (needReply != null && needReply == 1) {
|
||||||
|
JSONObject data;
|
||||||
|
Object dataObject = message.get(BusinessConstant.DATA);
|
||||||
|
if (dataObject instanceof JSONObject) {
|
||||||
|
data = (JSONObject) dataObject;
|
||||||
|
} else {
|
||||||
|
data = new JSONObject();
|
||||||
|
message.set(BusinessConstant.DATA, data);
|
||||||
|
}
|
||||||
|
data.clear();
|
||||||
|
data.set("result", 0);
|
||||||
|
message.remove(BusinessConstant.NEED_REPLY);
|
||||||
|
message.remove(BusinessConstant.GATEWAY);
|
||||||
|
mqttPushService.pushMessageByClient1(topic + BusinessConstant._REPLY, message.toString());
|
||||||
|
log.debug("Q20 events --> 已回复, deviceSn: {}, topic: {}", deviceSn, topic + BusinessConstant._REPLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新解析,避免回复时修改了data对象
|
||||||
|
message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String method = message.getStr(BusinessConstant.METHOD);
|
||||||
|
if (method == null) {
|
||||||
|
log.debug("Q20 events --> method字段缺失, topic: {}", topic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JSONObject data = message.getJSONObject(BusinessConstant.DATA);
|
||||||
|
if (data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (method) {
|
||||||
|
case Q20Constant.METHOD_LIFTOFF:
|
||||||
|
handleLiftoff(deviceSn, data);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_ARRIVAL:
|
||||||
|
handleArrival(deviceSn, data);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_TAKEOFF:
|
||||||
|
handleProgress(deviceSn, "起飞指令", data);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_LAND:
|
||||||
|
handleProgress(deviceSn, "降落指令", data);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_NAVIGATE:
|
||||||
|
handleProgress(deviceSn, "指点飞行", data);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_ROUTE_UPLOAD:
|
||||||
|
handleRouteUpload(deviceSn, data);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_ROUTE_EXECUTE:
|
||||||
|
handleRouteExecute(deviceSn, data);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_ROUTE_AUTO:
|
||||||
|
handleRouteAuto(deviceSn, data);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_OTA:
|
||||||
|
handleOta(deviceSn, data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log.debug("Q20 events --> 未知method: {}, deviceSn: {}", method, deviceSn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 起飞事件:飞机从停机状态转变到起飞状态时上报
|
||||||
|
* type: command(指令飞行) | waypoint(航线飞行) | leapfrog(蛙跳飞行) | manual(手动飞行)
|
||||||
|
*/
|
||||||
|
private void handleLiftoff(String deviceSn, JSONObject data) {
|
||||||
|
CacheUtils.set(Q20Constant.Q20_LIFTOFF + deviceSn, data);
|
||||||
|
log.info("Q20 events --> 起飞事件, deviceSn: {}, type: {}, wayline: {}",
|
||||||
|
deviceSn, data.getStr("type"), data.getStr("wayline"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 到达事件:飞机飞到某位置停降时上报
|
||||||
|
* type: land(直接降落) | preLand(准备降落) | emergency(应急降落)
|
||||||
|
*/
|
||||||
|
private void handleArrival(String deviceSn, JSONObject data) {
|
||||||
|
CacheUtils.set(Q20Constant.Q20_ARRIVAL + deviceSn, data);
|
||||||
|
log.info("Q20 events --> 到达事件, deviceSn: {}, type: {}, code: {}",
|
||||||
|
deviceSn, data.getStr("type"), data.getStr("code"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用指令进度:takeoff / land / navigate
|
||||||
|
* status: in_progress | ok | failed | canceled 等
|
||||||
|
*/
|
||||||
|
private void handleProgress(String deviceSn, String label, JSONObject data) {
|
||||||
|
String status = data.getStr("status");
|
||||||
|
JSONObject progress = data.getJSONObject("progress");
|
||||||
|
int percent = progress != null ? progress.getInt("percent", 0) : 0;
|
||||||
|
log.debug("Q20 events --> {}进度, deviceSn: {}, status: {}, percent: {}%",
|
||||||
|
label, deviceSn, status, percent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 航线上传结果(need_reply=1已在上方统一处理)
|
||||||
|
*/
|
||||||
|
private void handleRouteUpload(String deviceSn, JSONObject data) {
|
||||||
|
String status = data.getStr("status");
|
||||||
|
JSONObject progress = data.getJSONObject("progress");
|
||||||
|
String stepKey = progress != null ? progress.getStr("step_key") : null;
|
||||||
|
log.info("Q20 events --> 航线上传结果, deviceSn: {}, status: {}, step: {}",
|
||||||
|
deviceSn, status, stepKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 航线执行进度:缓存最新进度供前端轮询
|
||||||
|
* progress含: step_key, percent, remaining_distance, first_point_distance,
|
||||||
|
* first_point_remaining_time, first_point_progress, landing_progress
|
||||||
|
*/
|
||||||
|
private void handleRouteExecute(String deviceSn, JSONObject data) {
|
||||||
|
CacheUtils.set(Q20Constant.Q20_ROUTE_EXECUTE + deviceSn, data);
|
||||||
|
String status = data.getStr("status");
|
||||||
|
JSONObject progress = data.getJSONObject("progress");
|
||||||
|
if (progress != null) {
|
||||||
|
log.debug("Q20 events --> 航线执行进度, deviceSn: {}, status: {}, step: {}, percent: {}%, remain: {}m",
|
||||||
|
deviceSn, status, progress.getStr("step_key"),
|
||||||
|
progress.getInt("percent"), progress.getInt("remaining_distance"));
|
||||||
|
} else {
|
||||||
|
log.debug("Q20 events --> 航线执行进度, deviceSn: {}, status: {}", deviceSn, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一键飞行执行进度:缓存最新进度供前端轮询
|
||||||
|
*/
|
||||||
|
private void handleRouteAuto(String deviceSn, JSONObject data) {
|
||||||
|
CacheUtils.set(Q20Constant.Q20_ROUTE_AUTO + deviceSn, data);
|
||||||
|
String status = data.getStr("status");
|
||||||
|
JSONObject progress = data.getJSONObject("progress");
|
||||||
|
if (progress != null) {
|
||||||
|
log.debug("Q20 events --> 一键飞行进度, deviceSn: {}, status: {}, step: {}, percent: {}%, remain: {}m",
|
||||||
|
deviceSn, status, progress.getStr("step_key"),
|
||||||
|
progress.getInt("percent"), progress.getInt("remaining_distance"));
|
||||||
|
} else {
|
||||||
|
log.debug("Q20 events --> 一键飞行进度, deviceSn: {}, status: {}", deviceSn, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 固件升级进度
|
||||||
|
*/
|
||||||
|
private void handleOta(String deviceSn, JSONObject data) {
|
||||||
|
String status = data.getStr("status");
|
||||||
|
JSONObject progress = data.getJSONObject("progress");
|
||||||
|
int percent = progress != null ? progress.getInt("percent", 0) : 0;
|
||||||
|
log.info("Q20 events --> 固件升级进度, deviceSn: {}, status: {}, step: {}, percent: {}%",
|
||||||
|
deviceSn, status,
|
||||||
|
progress != null ? progress.getStr("step_key") : null, percent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.multictrl.modules.business.q20.handler;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
|
import com.multictrl.common.utils.CacheUtils;
|
||||||
|
import com.multictrl.common.utils.JsonUtils;
|
||||||
|
import com.multictrl.modules.business.handler.MessageHandler;
|
||||||
|
import com.multictrl.modules.business.service.MqttPushService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20系统健康状态处理(method: hms,state topic)
|
||||||
|
* 设备状态变化时触发上报,包含各子系统故障码数组:
|
||||||
|
* gnss, ins, battery, power, gimbal, system, structure, radar, vision, flight_control
|
||||||
|
* 以及故障状态位码:code2, code3, code4
|
||||||
|
* 若need_reply=1需回复 state_reply 主题
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Q20HmsHandler implements MessageHandler {
|
||||||
|
|
||||||
|
private final MqttPushService mqttPushService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String topic, String payload, String gateway) {
|
||||||
|
JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message != null) {
|
||||||
|
String deviceSn = message.getStr(BusinessConstant.GATEWAY);
|
||||||
|
JSONObject data = message.getJSONObject(BusinessConstant.DATA);
|
||||||
|
Integer needReply = message.getInt(BusinessConstant.NEED_REPLY);
|
||||||
|
if (needReply != null && needReply == 1) {
|
||||||
|
data.clear();
|
||||||
|
data.set("result", 0);
|
||||||
|
message.remove(BusinessConstant.NEED_REPLY);
|
||||||
|
mqttPushService.pushMessageByClient2(topic + BusinessConstant._REPLY, message.toString());
|
||||||
|
log.debug("Q20 hms --> 已回复, deviceSn: {}", deviceSn);
|
||||||
|
}
|
||||||
|
// 重新解析,避免回复时修改了data对象
|
||||||
|
message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data = message.getJSONObject(BusinessConstant.DATA);
|
||||||
|
if (data != null) {
|
||||||
|
CacheUtils.set(Q20Constant.Q20_HMS + deviceSn, data);
|
||||||
|
log.debug("Q20 hms --> deviceSn: {}, gnss: {}, ins: {}, battery: {}",
|
||||||
|
deviceSn, data.getJSONArray("gnss"), data.getJSONArray("ins"),
|
||||||
|
data.getJSONArray("battery"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("Q20 hms --> payload解析失败,解析后为null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
package com.multictrl.modules.business.q20.handler;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
|
import com.multictrl.common.utils.CacheUtils;
|
||||||
|
import com.multictrl.common.utils.JsonUtils;
|
||||||
|
import com.multictrl.modules.business.handler.MessageHandler;
|
||||||
|
import com.multictrl.modules.business.q20.influxdb.Q20MountReport;
|
||||||
|
import com.multictrl.modules.business.service.InfluxService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20负载数据处理(method: mount)
|
||||||
|
* 上报挂载负载状态:mount_id, mount_port, mount_type, payload(yaw/pitch/roll/mode/zoom等)
|
||||||
|
* 支持ZT30(四光)、Z40T(双光)、A30(三光)、GL60P(探照灯)、4C(抛投器)、SZY/YKX/EayLoad10(索降)、
|
||||||
|
* LC(物流箱)、PARACHUTE(降落伞)等各类型负载
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Q20MountHandler implements MessageHandler {
|
||||||
|
|
||||||
|
private final InfluxService influxService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String topic, String payload, String gateway) {
|
||||||
|
JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message != null) {
|
||||||
|
String deviceSn = message.getStr(BusinessConstant.GATEWAY);
|
||||||
|
JSONObject data = message.getJSONObject(BusinessConstant.DATA);
|
||||||
|
if (data != null) {
|
||||||
|
CacheUtils.set(Q20Constant.Q20_MOUNT + deviceSn, data);
|
||||||
|
saveToInflux(deviceSn, data);
|
||||||
|
log.debug("Q20 mount --> deviceSn: {}, mount_type: {}, mount_port: {}",
|
||||||
|
deviceSn, data.getStr("mount_type"), data.getInt("mount_port"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("Q20 mount --> payload解析失败,解析后为null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveToInflux(String deviceSn, JSONObject data) {
|
||||||
|
JSONObject payload = data.getJSONObject("payload");
|
||||||
|
|
||||||
|
Q20MountReport report = Q20MountReport.builder()
|
||||||
|
.deviceSn(deviceSn)
|
||||||
|
.order_id(data.getStr("order_id"))
|
||||||
|
.mount_id(data.getStr("mount_id"))
|
||||||
|
.mount_port(data.getInt("mount_port"))
|
||||||
|
.mount_type(data.getStr("mount_type"))
|
||||||
|
.payload_yaw(payload != null ? payload.getFloat("yaw") : null)
|
||||||
|
.payload_pitch(payload != null ? payload.getFloat("pitch") : null)
|
||||||
|
.payload_roll(payload != null ? payload.getFloat("roll") : null)
|
||||||
|
.payload_mode(payload != null ? payload.getInt("mode") : null)
|
||||||
|
.payload_zoom(payload != null ? payload.getFloat("zoom") : null)
|
||||||
|
.build();
|
||||||
|
influxService.addRecord(report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.multictrl.modules.business.q20.handler;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
|
import com.multictrl.common.utils.CacheUtils;
|
||||||
|
import com.multictrl.common.utils.JsonUtils;
|
||||||
|
import com.multictrl.modules.business.handler.MessageHandler;
|
||||||
|
import com.multictrl.modules.business.service.MqttPushService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20设备通知事件处理(method: notify,state topic)
|
||||||
|
* 设备状态变化时触发上报,包含:index, type, level(0紧急/2严重/4告警/6提示),
|
||||||
|
* message, timestamp, value(故障码), clear_codes(清除码)
|
||||||
|
* 若need_reply=1需回复 state_reply 主题
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Q20NotifyHandler implements MessageHandler {
|
||||||
|
|
||||||
|
private final MqttPushService mqttPushService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String topic, String payload, String gateway) {
|
||||||
|
JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message != null) {
|
||||||
|
String deviceSn = message.getStr(BusinessConstant.GATEWAY);
|
||||||
|
JSONObject data = message.getJSONObject(BusinessConstant.DATA);
|
||||||
|
Integer needReply = message.getInt(BusinessConstant.NEED_REPLY);
|
||||||
|
if (needReply != null && needReply == 1) {
|
||||||
|
data.clear();
|
||||||
|
data.set("result", 0);
|
||||||
|
message.remove(BusinessConstant.NEED_REPLY);
|
||||||
|
mqttPushService.pushMessageByClient2(topic + BusinessConstant._REPLY, message.toString());
|
||||||
|
log.debug("Q20 notify --> 已回复, deviceSn: {}", deviceSn);
|
||||||
|
}
|
||||||
|
// 重新解析,避免回复时修改了data对象
|
||||||
|
message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data = message.getJSONObject(BusinessConstant.DATA);
|
||||||
|
if (data != null) {
|
||||||
|
log.warn("Q20 notify --> deviceSn: {}, type: {}, level: {}, message: {}, value: {}",
|
||||||
|
deviceSn, data.getStr("type"), data.getInt("level"),
|
||||||
|
data.getStr("message"), data.getStr("value"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("Q20 notify --> payload解析失败,解析后为null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
package com.multictrl.modules.business.q20.handler;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONArray;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
|
import com.multictrl.common.utils.CacheUtils;
|
||||||
|
import com.multictrl.common.utils.JsonUtils;
|
||||||
|
import com.multictrl.modules.business.handler.MessageHandler;
|
||||||
|
import com.multictrl.modules.business.q20.influxdb.Q20ObsReport;
|
||||||
|
import com.multictrl.modules.business.service.InfluxService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20避障数据处理(method: obs)
|
||||||
|
* 上报避障传感器状态:cp_distance(避障距离/m), cp_enable(避障开关),
|
||||||
|
* ver_distances(垂直方向[下方,上方]障碍物距离数组/-1表示无障碍物),
|
||||||
|
* around_distances(水平方向8方位障碍物距离数组/-1表示无障碍物,从正前方顺时针)
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Q20ObsHandler implements MessageHandler {
|
||||||
|
|
||||||
|
private final InfluxService influxService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String topic, String payload, String gateway) {
|
||||||
|
JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message != null) {
|
||||||
|
String deviceSn = message.getStr(BusinessConstant.GATEWAY);
|
||||||
|
JSONObject data = message.getJSONObject(BusinessConstant.DATA);
|
||||||
|
if (data != null) {
|
||||||
|
CacheUtils.set(Q20Constant.Q20_OBS + deviceSn, data);
|
||||||
|
saveToInflux(deviceSn, data);
|
||||||
|
log.debug("Q20 obs --> deviceSn: {}, cp_distance: {}m, cp_enable: {}",
|
||||||
|
deviceSn, data.getFloat("cp_distance"), data.getInt("cp_enable"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("Q20 obs --> payload解析失败,解析后为null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveToInflux(String deviceSn, JSONObject data) {
|
||||||
|
JSONArray verDistances = data.getJSONArray("ver_distances");
|
||||||
|
JSONArray aroundDistances = data.getJSONArray("around_distances");
|
||||||
|
|
||||||
|
Q20ObsReport report = Q20ObsReport.builder()
|
||||||
|
.deviceSn(deviceSn)
|
||||||
|
.order_id(data.getStr("order_id"))
|
||||||
|
.cp_distance(data.getFloat("cp_distance"))
|
||||||
|
.cp_enable(data.getInt("cp_enable"))
|
||||||
|
.ver_down(getFloat(verDistances, 0))
|
||||||
|
.ver_up(getFloat(verDistances, 1))
|
||||||
|
.around_0(getFloat(aroundDistances, 0))
|
||||||
|
.around_1(getFloat(aroundDistances, 1))
|
||||||
|
.around_2(getFloat(aroundDistances, 2))
|
||||||
|
.around_3(getFloat(aroundDistances, 3))
|
||||||
|
.around_4(getFloat(aroundDistances, 4))
|
||||||
|
.around_5(getFloat(aroundDistances, 5))
|
||||||
|
.around_6(getFloat(aroundDistances, 6))
|
||||||
|
.around_7(getFloat(aroundDistances, 7))
|
||||||
|
.build();
|
||||||
|
influxService.addRecord(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Float getFloat(JSONArray array, int index) {
|
||||||
|
if (array == null || array.size() <= index) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Object val = array.get(index);
|
||||||
|
if (val == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ((Number) val).floatValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
package com.multictrl.modules.business.q20.handler;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
|
import com.multictrl.common.utils.CacheUtils;
|
||||||
|
import com.multictrl.common.utils.JsonUtils;
|
||||||
|
import com.multictrl.modules.business.handler.MessageHandler;
|
||||||
|
import com.multictrl.modules.business.q20.influxdb.Q20OsdReport;
|
||||||
|
import com.multictrl.modules.business.service.FlightTaskService;
|
||||||
|
import com.multictrl.modules.business.service.InfluxService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20基础遥测数据处理(method: osd)
|
||||||
|
* 定频上报飞行数据:longitude, latitude, height, altitude, vs, gs, heading,
|
||||||
|
* pitch, roll, battery, voltage, mode, flying, fly_time, fly_distance, home_location, state等
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Q20OsdHandler implements MessageHandler {
|
||||||
|
|
||||||
|
private final FlightTaskService flightTaskService;
|
||||||
|
private final InfluxService influxService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String topic, String payload, String gateway) {
|
||||||
|
JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message != null) {
|
||||||
|
String deviceSn = message.getStr(BusinessConstant.GATEWAY);
|
||||||
|
JSONObject data = message.getJSONObject(BusinessConstant.DATA);
|
||||||
|
if (data != null) {
|
||||||
|
CacheUtils.set(Q20Constant.Q20_OSD + deviceSn, data);
|
||||||
|
trackFlyingState(deviceSn, data);
|
||||||
|
saveToInflux(deviceSn, data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("Q20 osd --> payload解析失败,解析后为null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void trackFlyingState(String deviceSn, JSONObject data) {
|
||||||
|
Integer flying = data.getInt("flying");
|
||||||
|
if (flying == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 起飞中,首次检测到飞行状态时更新架次开始
|
||||||
|
if (flying == 1) {
|
||||||
|
if (CacheUtils.get(Q20Constant.Q20_IN_WORK + deviceSn) == null) {
|
||||||
|
flightTaskService.updateTaskStart(deviceSn);
|
||||||
|
log.debug("{}设备架次开始,更新架次表", deviceSn);
|
||||||
|
}
|
||||||
|
CacheUtils.set(Q20Constant.Q20_IN_WORK + deviceSn, true);
|
||||||
|
}
|
||||||
|
// 刚从飞行切到非飞行,架次结束
|
||||||
|
if (flying == 0 && CacheUtils.get(Q20Constant.Q20_IN_WORK + deviceSn) != null) {
|
||||||
|
CacheUtils.delete(Q20Constant.Q20_IN_WORK + deviceSn);
|
||||||
|
flightTaskService.updateTaskComplete(deviceSn);
|
||||||
|
log.debug("{}设备架次结束,更新架次表", deviceSn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveToInflux(String deviceSn, JSONObject data) {
|
||||||
|
Double longitude = data.getDouble("longitude");
|
||||||
|
Double latitude = data.getDouble("latitude");
|
||||||
|
if (longitude == null || latitude == null || longitude == 0 || latitude == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JSONObject homeLocation = data.getJSONObject("home_location");
|
||||||
|
JSONObject state = data.getJSONObject("state");
|
||||||
|
|
||||||
|
Q20OsdReport report = Q20OsdReport.builder()
|
||||||
|
.deviceSn(deviceSn)
|
||||||
|
.order_id(data.getStr("order_id"))
|
||||||
|
.longitude(longitude)
|
||||||
|
.latitude(latitude)
|
||||||
|
.height(data.getFloat("height"))
|
||||||
|
.altitude(data.getFloat("altitude"))
|
||||||
|
.vs(data.getFloat("vs"))
|
||||||
|
.gs(data.getFloat("gs"))
|
||||||
|
.heading(data.getFloat("heading"))
|
||||||
|
.pitch(data.getFloat("pitch"))
|
||||||
|
.roll(data.getFloat("roll"))
|
||||||
|
.battery(data.getInt("battery"))
|
||||||
|
.voltage(data.getInt("voltage"))
|
||||||
|
.mode(data.getStr("mode"))
|
||||||
|
.flying(data.getInt("flying"))
|
||||||
|
.fly_time(data.getLong("fly_time"))
|
||||||
|
.fly_distance(data.getFloat("fly_distance"))
|
||||||
|
.home_set(data.getInt("home_set"))
|
||||||
|
.home_distance(data.getFloat("home_distance"))
|
||||||
|
.home_altitude(homeLocation != null ? homeLocation.getFloat("altitude") : null)
|
||||||
|
.home_latitude(homeLocation != null ? homeLocation.getFloat("latitude") : null)
|
||||||
|
.home_longitude(homeLocation != null ? homeLocation.getFloat("longitude") : null)
|
||||||
|
.state_safe_enabled(state != null ? state.getInt("safe_enabled") : null)
|
||||||
|
.state_rtk_enabled(state != null ? state.getInt("rtk_enabled") : null)
|
||||||
|
.state_rtk_connected(state != null ? state.getInt("rtk_connected") : null)
|
||||||
|
.state_rc_connected(state != null ? state.getInt("rc_connected") : null)
|
||||||
|
.state_obs_enabled(state != null ? state.getInt("obs_enabled") : null)
|
||||||
|
.state_accel_cal(state != null ? state.getInt("accel_cal") : null)
|
||||||
|
.state_gyro_cal(state != null ? state.getInt("gyro_cal") : null)
|
||||||
|
.state_hor_cal(state != null ? state.getInt("hor_cal") : null)
|
||||||
|
.state_mag_cal(state != null ? state.getInt("mag_cal") : null)
|
||||||
|
.state_fpv_live(state != null ? state.getInt("fpv_live") : null)
|
||||||
|
.state_stream_live(state != null ? state.getInt("stream_live") : null)
|
||||||
|
.build();
|
||||||
|
influxService.addRecord(report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
package com.multictrl.modules.business.q20.handler;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
|
import com.multictrl.common.utils.JsonUtils;
|
||||||
|
import com.multictrl.modules.business.handler.MessageHandler;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20设备OSD主题分发器
|
||||||
|
* 接收 thing/device/{device_sn}/osd 主题消息,根据payload中的method字段分发到对应处理器
|
||||||
|
* method: osd(基础遥测) | rtk(RTK定位) | rc(遥控链路) | mount(负载) | battery(电池) | obs(避障)
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Q20OsdTopicHandler implements MessageHandler {
|
||||||
|
|
||||||
|
private final Q20OsdHandler q20OsdHandler;
|
||||||
|
private final Q20RtkHandler q20RtkHandler;
|
||||||
|
private final Q20RcHandler q20RcHandler;
|
||||||
|
private final Q20MountHandler q20MountHandler;
|
||||||
|
private final Q20BatteryHandler q20BatteryHandler;
|
||||||
|
private final Q20ObsHandler q20ObsHandler;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String topic, String payload, String gateway) {
|
||||||
|
JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message == null) {
|
||||||
|
log.debug("Q20 osd topic --> payload解析失败,解析后为null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String method = message.getStr(BusinessConstant.METHOD);
|
||||||
|
if (method == null) {
|
||||||
|
log.debug("Q20 osd topic --> method字段缺失, topic: {}", topic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (method) {
|
||||||
|
case Q20Constant.METHOD_OSD:
|
||||||
|
q20OsdHandler.handleMessage(topic, payload, gateway);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_RTK:
|
||||||
|
q20RtkHandler.handleMessage(topic, payload, gateway);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_RC:
|
||||||
|
q20RcHandler.handleMessage(topic, payload, gateway);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_MOUNT:
|
||||||
|
q20MountHandler.handleMessage(topic, payload, gateway);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_BATTERY:
|
||||||
|
q20BatteryHandler.handleMessage(topic, payload, gateway);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_OBS:
|
||||||
|
q20ObsHandler.handleMessage(topic, payload, gateway);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log.debug("Q20 osd topic --> 未知method: {}", method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
package com.multictrl.modules.business.q20.handler;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONArray;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
|
import com.multictrl.common.utils.CacheUtils;
|
||||||
|
import com.multictrl.common.utils.JsonUtils;
|
||||||
|
import com.multictrl.modules.business.handler.MessageHandler;
|
||||||
|
import com.multictrl.modules.business.q20.influxdb.Q20RcReport;
|
||||||
|
import com.multictrl.modules.business.service.InfluxService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20遥控链路数据处理(method: rc)
|
||||||
|
* 上报遥控与链路信息:priority, rc_channel_state, rc_state,
|
||||||
|
* link数组(id/type/connected/inuse/sqe/band), up_loss_rate, down_loss_rate, network信息
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Q20RcHandler implements MessageHandler {
|
||||||
|
|
||||||
|
private final InfluxService influxService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String topic, String payload, String gateway) {
|
||||||
|
JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message != null) {
|
||||||
|
String deviceSn = message.getStr(BusinessConstant.GATEWAY);
|
||||||
|
JSONObject data = message.getJSONObject(BusinessConstant.DATA);
|
||||||
|
if (data != null) {
|
||||||
|
CacheUtils.set(Q20Constant.Q20_RC + deviceSn, data);
|
||||||
|
saveToInflux(deviceSn, data);
|
||||||
|
log.debug("Q20 rc --> deviceSn: {}, priority: {}, rc_state: {}",
|
||||||
|
deviceSn, data.getInt("priority"), data.getStr("rc_state"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("Q20 rc --> payload解析失败,解析后为null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveToInflux(String deviceSn, JSONObject data) {
|
||||||
|
// 从link数组中取inuse=1的链路,无则取第一条
|
||||||
|
JSONObject inuseLink = null;
|
||||||
|
JSONArray links = data.getJSONArray("link");
|
||||||
|
if (links != null) {
|
||||||
|
for (int i = 0; i < links.size(); i++) {
|
||||||
|
JSONObject link = links.getJSONObject(i);
|
||||||
|
if (link != null && Integer.valueOf(1).equals(link.getInt("inuse"))) {
|
||||||
|
inuseLink = link;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inuseLink == null && !links.isEmpty()) {
|
||||||
|
inuseLink = links.getJSONObject(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSONObject network = data.getJSONObject("network");
|
||||||
|
|
||||||
|
Q20RcReport report = Q20RcReport.builder()
|
||||||
|
.deviceSn(deviceSn)
|
||||||
|
.order_id(data.getStr("order_id"))
|
||||||
|
.priority(data.getInt("priority"))
|
||||||
|
.rc_channel_state(data.getStr("rc_channel_state"))
|
||||||
|
.rc_state(data.getStr("rc_state"))
|
||||||
|
.up_loss_rate(data.getFloat("up_loss_rate"))
|
||||||
|
.down_loss_rate(data.getFloat("down_loss_rate"))
|
||||||
|
.link_inuse_id(inuseLink != null ? inuseLink.getInt("id") : null)
|
||||||
|
.link_inuse_type(inuseLink != null ? inuseLink.getInt("type") : null)
|
||||||
|
.link_inuse_sqe(inuseLink != null ? inuseLink.getInt("sqe") : null)
|
||||||
|
.link_inuse_band(inuseLink != null ? inuseLink.getInt("band") : null)
|
||||||
|
.network_type(network != null ? network.getInt("type") : null)
|
||||||
|
.network_quality(network != null ? network.getInt("quality") : null)
|
||||||
|
.build();
|
||||||
|
influxService.addRecord(report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
package com.multictrl.modules.business.q20.handler;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
|
import com.multictrl.common.utils.CacheUtils;
|
||||||
|
import com.multictrl.common.utils.JsonUtils;
|
||||||
|
import com.multictrl.modules.business.handler.MessageHandler;
|
||||||
|
import com.multictrl.modules.business.q20.influxdb.Q20RtkReport;
|
||||||
|
import com.multictrl.modules.business.service.InfluxService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20 RTK数据处理(method: rtk)
|
||||||
|
* 上报RTK定位信息:fix_type, latitude, longitude, altitude, ellipsoid_height,
|
||||||
|
* satellite_visible, eph, epv, velocity, yaw, cog, horizontal_acc, vertical_acc, velocity_acc
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Q20RtkHandler implements MessageHandler {
|
||||||
|
|
||||||
|
private final InfluxService influxService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String topic, String payload, String gateway) {
|
||||||
|
JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message != null) {
|
||||||
|
String deviceSn = message.getStr(BusinessConstant.GATEWAY);
|
||||||
|
JSONObject data = message.getJSONObject(BusinessConstant.DATA);
|
||||||
|
if (data != null) {
|
||||||
|
CacheUtils.set(Q20Constant.Q20_RTK + deviceSn, data);
|
||||||
|
saveToInflux(deviceSn, data);
|
||||||
|
log.debug("Q20 rtk --> deviceSn: {}, fix_type: {}, satellites: {}",
|
||||||
|
deviceSn, data.getInt("fix_type"), data.getInt("satellite_visible"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("Q20 rtk --> payload解析失败,解析后为null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveToInflux(String deviceSn, JSONObject data) {
|
||||||
|
Q20RtkReport report = Q20RtkReport.builder()
|
||||||
|
.deviceSn(deviceSn)
|
||||||
|
.order_id(data.getStr("order_id"))
|
||||||
|
.fix_type(data.getInt("fix_type"))
|
||||||
|
.latitude(data.getDouble("latitude"))
|
||||||
|
.longitude(data.getDouble("longitude"))
|
||||||
|
.altitude(data.getFloat("altitude"))
|
||||||
|
.ellipsoid_height(data.getFloat("ellipsoid_height"))
|
||||||
|
.satellite_visible(data.getInt("satellite_visible"))
|
||||||
|
.eph(data.getFloat("eph"))
|
||||||
|
.epv(data.getFloat("epv"))
|
||||||
|
.velocity(data.getFloat("velocity"))
|
||||||
|
.yaw(data.getFloat("yaw"))
|
||||||
|
.cog(data.getFloat("cog"))
|
||||||
|
.horizontal_acc(data.getFloat("horizontal_acc"))
|
||||||
|
.vertical_acc(data.getFloat("vertical_acc"))
|
||||||
|
.velocity_acc(data.getFloat("velocity_acc"))
|
||||||
|
.build();
|
||||||
|
influxService.addRecord(report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
package com.multictrl.modules.business.q20.handler;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
|
import com.multictrl.common.utils.JsonUtils;
|
||||||
|
import com.multictrl.modules.business.handler.MessageHandler;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20设备State主题分发器
|
||||||
|
* 接收 thing/device/{device_sn}/state 主题消息,根据payload中的method字段分发到对应处理器
|
||||||
|
* method: version(固件版本) | notify(设备通知事件) | hms(系统健康状态)
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Q20StateTopicHandler implements MessageHandler {
|
||||||
|
|
||||||
|
private final Q20VersionHandler q20VersionHandler;
|
||||||
|
private final Q20NotifyHandler q20NotifyHandler;
|
||||||
|
private final Q20HmsHandler q20HmsHandler;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String topic, String payload, String gateway) {
|
||||||
|
JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message == null) {
|
||||||
|
log.debug("Q20 state topic --> payload解析失败,解析后为null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String method = message.getStr(BusinessConstant.METHOD);
|
||||||
|
if (method == null) {
|
||||||
|
log.debug("Q20 state topic --> method字段缺失, topic: {}", topic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (method) {
|
||||||
|
case Q20Constant.METHOD_VERSION:
|
||||||
|
q20VersionHandler.handleMessage(topic, payload, gateway);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_NOTIFY:
|
||||||
|
q20NotifyHandler.handleMessage(topic, payload, gateway);
|
||||||
|
break;
|
||||||
|
case Q20Constant.METHOD_HMS:
|
||||||
|
q20HmsHandler.handleMessage(topic, payload, gateway);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log.debug("Q20 state topic --> 未知method: {}", method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.multictrl.modules.business.q20.handler;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
|
import com.multictrl.common.utils.CacheUtils;
|
||||||
|
import com.multictrl.common.utils.JsonUtils;
|
||||||
|
import com.multictrl.modules.business.handler.MessageHandler;
|
||||||
|
import com.multictrl.modules.business.service.MqttPushService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20固件版本状态处理(method: version,state topic)
|
||||||
|
* 设备状态变化时触发上报,包含:firmware_version, offboard_version, upgrade_status,
|
||||||
|
* vision_version, protocol_version, vehicle_type
|
||||||
|
* 若need_reply=1需回复 state_reply 主题
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Q20VersionHandler implements MessageHandler {
|
||||||
|
|
||||||
|
private final MqttPushService mqttPushService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String topic, String payload, String gateway) {
|
||||||
|
JSONObject message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message != null) {
|
||||||
|
String deviceSn = message.getStr(BusinessConstant.GATEWAY);
|
||||||
|
JSONObject data = message.getJSONObject(BusinessConstant.DATA);
|
||||||
|
Integer needReply = message.getInt(BusinessConstant.NEED_REPLY);
|
||||||
|
if (needReply != null && needReply == 1) {
|
||||||
|
data.clear();
|
||||||
|
data.set("result", 0);
|
||||||
|
message.remove(BusinessConstant.NEED_REPLY);
|
||||||
|
mqttPushService.pushMessageByClient2(topic + BusinessConstant._REPLY, message.toString());
|
||||||
|
log.debug("Q20 version --> 已回复, deviceSn: {}", deviceSn);
|
||||||
|
}
|
||||||
|
// 重新解析,避免回复时修改了data对象
|
||||||
|
message = JsonUtils.parseObject(payload, JSONObject.class);
|
||||||
|
if (message == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data = message.getJSONObject(BusinessConstant.DATA);
|
||||||
|
if (data != null) {
|
||||||
|
CacheUtils.set(Q20Constant.Q20_VERSION + deviceSn, data);
|
||||||
|
log.debug("Q20 version --> deviceSn: {}, firmware: {}, vehicle_type: {}",
|
||||||
|
deviceSn, data.getStr("firmware_version"), data.getStr("vehicle_type"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("Q20 version --> payload解析失败,解析后为null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.multictrl.modules.business.q20.influxdb;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.influxdb.annotations.Column;
|
||||||
|
import com.influxdb.annotations.Measurement;
|
||||||
|
import com.multictrl.common.utils.DateUtils;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20设备负载数据 InfluxDB记录
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Data
|
||||||
|
@Schema(name = "Q20设备负载上报数据")
|
||||||
|
@Measurement(name = "q20_mount")
|
||||||
|
public class Q20MountReport {
|
||||||
|
|
||||||
|
@Schema(description = "设备SN")
|
||||||
|
@Column(tag = true)
|
||||||
|
private String deviceSn;
|
||||||
|
|
||||||
|
@Schema(description = "架次号")
|
||||||
|
@Column(tag = true)
|
||||||
|
private String order_id;
|
||||||
|
|
||||||
|
@Schema(hidden = true)
|
||||||
|
@Column(timestamp = true)
|
||||||
|
@JsonProperty("_time")
|
||||||
|
private Instant time;
|
||||||
|
|
||||||
|
@Schema(description = "负载ID")
|
||||||
|
@Column
|
||||||
|
private String mount_id;
|
||||||
|
|
||||||
|
@Schema(description = "负载挂载口")
|
||||||
|
@Column
|
||||||
|
private Integer mount_port;
|
||||||
|
|
||||||
|
@Schema(description = "负载类型:ZT30/Z40T/A30/GL60P/4C/SZY/YKX/EayLoad10/LC/PARACHUTE等")
|
||||||
|
@Column
|
||||||
|
private String mount_type;
|
||||||
|
|
||||||
|
@Schema(description = "云台偏航角,单位°")
|
||||||
|
@Column
|
||||||
|
private Float payload_yaw;
|
||||||
|
|
||||||
|
@Schema(description = "云台俯仰角,单位°")
|
||||||
|
@Column
|
||||||
|
private Float payload_pitch;
|
||||||
|
|
||||||
|
@Schema(description = "云台横滚角,单位°")
|
||||||
|
@Column
|
||||||
|
private Float payload_roll;
|
||||||
|
|
||||||
|
@Schema(description = "云台模式")
|
||||||
|
@Column
|
||||||
|
private Integer payload_mode;
|
||||||
|
|
||||||
|
@Schema(description = "变焦倍数")
|
||||||
|
@Column
|
||||||
|
private Float payload_zoom;
|
||||||
|
|
||||||
|
private String timeStr;
|
||||||
|
|
||||||
|
public void setTime(Instant time) {
|
||||||
|
this.time = time;
|
||||||
|
this.timeStr = DateUtils.utcToTime(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
package com.multictrl.modules.business.q20.influxdb;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.influxdb.annotations.Column;
|
||||||
|
import com.influxdb.annotations.Measurement;
|
||||||
|
import com.multictrl.common.utils.DateUtils;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20设备避障数据 InfluxDB记录
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Data
|
||||||
|
@Schema(name = "Q20设备避障上报数据")
|
||||||
|
@Measurement(name = "q20_obs")
|
||||||
|
public class Q20ObsReport {
|
||||||
|
|
||||||
|
@Schema(description = "设备SN")
|
||||||
|
@Column(tag = true)
|
||||||
|
private String deviceSn;
|
||||||
|
|
||||||
|
@Schema(description = "架次号")
|
||||||
|
@Column(tag = true)
|
||||||
|
private String order_id;
|
||||||
|
|
||||||
|
@Schema(hidden = true)
|
||||||
|
@Column(timestamp = true)
|
||||||
|
@JsonProperty("_time")
|
||||||
|
private Instant time;
|
||||||
|
|
||||||
|
@Schema(description = "避障距离,单位m")
|
||||||
|
@Column
|
||||||
|
private Float cp_distance;
|
||||||
|
|
||||||
|
@Schema(description = "避障开关:1开启/0关闭")
|
||||||
|
@Column
|
||||||
|
private Integer cp_enable;
|
||||||
|
|
||||||
|
@Schema(description = "下方障碍物距离,单位m,-1表示无障碍物")
|
||||||
|
@Column
|
||||||
|
private Float ver_down;
|
||||||
|
|
||||||
|
@Schema(description = "上方障碍物距离,单位m,-1表示无障碍物")
|
||||||
|
@Column
|
||||||
|
private Float ver_up;
|
||||||
|
|
||||||
|
@Schema(description = "水平正前方障碍物距离,单位m,-1表示无障碍物")
|
||||||
|
@Column
|
||||||
|
private Float around_0;
|
||||||
|
|
||||||
|
@Schema(description = "水平右前方障碍物距离,单位m,-1表示无障碍物")
|
||||||
|
@Column
|
||||||
|
private Float around_1;
|
||||||
|
|
||||||
|
@Schema(description = "水平正右方障碍物距离,单位m,-1表示无障碍物")
|
||||||
|
@Column
|
||||||
|
private Float around_2;
|
||||||
|
|
||||||
|
@Schema(description = "水平右后方障碍物距离,单位m,-1表示无障碍物")
|
||||||
|
@Column
|
||||||
|
private Float around_3;
|
||||||
|
|
||||||
|
@Schema(description = "水平正后方障碍物距离,单位m,-1表示无障碍物")
|
||||||
|
@Column
|
||||||
|
private Float around_4;
|
||||||
|
|
||||||
|
@Schema(description = "水平左后方障碍物距离,单位m,-1表示无障碍物")
|
||||||
|
@Column
|
||||||
|
private Float around_5;
|
||||||
|
|
||||||
|
@Schema(description = "水平正左方障碍物距离,单位m,-1表示无障碍物")
|
||||||
|
@Column
|
||||||
|
private Float around_6;
|
||||||
|
|
||||||
|
@Schema(description = "水平左前方障碍物距离,单位m,-1表示无障碍物")
|
||||||
|
@Column
|
||||||
|
private Float around_7;
|
||||||
|
|
||||||
|
private String timeStr;
|
||||||
|
|
||||||
|
public void setTime(Instant time) {
|
||||||
|
this.time = time;
|
||||||
|
this.timeStr = DateUtils.utcToTime(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
package com.multictrl.modules.business.q20.influxdb;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.influxdb.annotations.Column;
|
||||||
|
import com.influxdb.annotations.Measurement;
|
||||||
|
import com.multictrl.common.utils.DateUtils;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20设备OSD基础遥测数据 InfluxDB记录
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Data
|
||||||
|
@Schema(name = "Q20设备OSD上报数据")
|
||||||
|
@Measurement(name = "q20_osd")
|
||||||
|
public class Q20OsdReport {
|
||||||
|
|
||||||
|
@Schema(description = "设备SN")
|
||||||
|
@Column(tag = true)
|
||||||
|
private String deviceSn;
|
||||||
|
|
||||||
|
@Schema(description = "架次号")
|
||||||
|
@Column(tag = true)
|
||||||
|
private String order_id;
|
||||||
|
|
||||||
|
@Schema(hidden = true)
|
||||||
|
@Column(timestamp = true)
|
||||||
|
@JsonProperty("_time")
|
||||||
|
private Instant time;
|
||||||
|
|
||||||
|
@Schema(description = "位置经度,WGS84,精确到小数点后7位")
|
||||||
|
@Column
|
||||||
|
private Double longitude;
|
||||||
|
|
||||||
|
@Schema(description = "位置纬度,WGS84,精确到小数点后7位")
|
||||||
|
@Column
|
||||||
|
private Double latitude;
|
||||||
|
|
||||||
|
@Schema(description = "相对起飞点高度,单位m")
|
||||||
|
@Column
|
||||||
|
private Float height;
|
||||||
|
|
||||||
|
@Schema(description = "海拔高度,单位m")
|
||||||
|
@Column
|
||||||
|
private Float altitude;
|
||||||
|
|
||||||
|
@Schema(description = "垂直飞行速度,单位m/s,向上为负向下为正")
|
||||||
|
@Column
|
||||||
|
private Float vs;
|
||||||
|
|
||||||
|
@Schema(description = "水平飞行速度(对地速度),单位m/s")
|
||||||
|
@Column
|
||||||
|
private Float gs;
|
||||||
|
|
||||||
|
@Schema(description = "航向,取值范围[0,360],正北为0顺时针递增,单位°")
|
||||||
|
@Column
|
||||||
|
private Float heading;
|
||||||
|
|
||||||
|
@Schema(description = "俯仰角,取值范围[-90,90],仰为正俯为负,单位°")
|
||||||
|
@Column
|
||||||
|
private Float pitch;
|
||||||
|
|
||||||
|
@Schema(description = "横滚角,取值范围[-90,90],向左为正向右为负,单位°")
|
||||||
|
@Column
|
||||||
|
private Float roll;
|
||||||
|
|
||||||
|
@Schema(description = "电池容量百分比,多电池为融合总电量,单位%")
|
||||||
|
@Column
|
||||||
|
private Integer battery;
|
||||||
|
|
||||||
|
@Schema(description = "电池电压,多电池为平均电压,单位V")
|
||||||
|
@Column
|
||||||
|
private Integer voltage;
|
||||||
|
|
||||||
|
@Schema(description = "飞行模式:MANUAL/ALTCTL/POSCTL/AUTO.MISSION/AUTO.RTL/AUTO.LAND等")
|
||||||
|
@Column
|
||||||
|
private String mode;
|
||||||
|
|
||||||
|
@Schema(description = "是否飞行中:1飞行中,0非飞行中")
|
||||||
|
@Column
|
||||||
|
private Integer flying;
|
||||||
|
|
||||||
|
@Schema(description = "飞行时长,单位秒")
|
||||||
|
@Column
|
||||||
|
private Long fly_time;
|
||||||
|
|
||||||
|
@Schema(description = "飞行里程,单位m")
|
||||||
|
@Column
|
||||||
|
private Float fly_distance;
|
||||||
|
|
||||||
|
@Schema(description = "返航点是否已设置:1已设置,0未设置")
|
||||||
|
@Column
|
||||||
|
private Integer home_set;
|
||||||
|
|
||||||
|
@Schema(description = "距离返航点距离,单位m")
|
||||||
|
@Column
|
||||||
|
private Float home_distance;
|
||||||
|
|
||||||
|
@Schema(description = "返航点相对高度,单位m")
|
||||||
|
@Column
|
||||||
|
private Float home_altitude;
|
||||||
|
|
||||||
|
@Schema(description = "返航点纬度")
|
||||||
|
@Column
|
||||||
|
private Float home_latitude;
|
||||||
|
|
||||||
|
@Schema(description = "返航点经度")
|
||||||
|
@Column
|
||||||
|
private Float home_longitude;
|
||||||
|
|
||||||
|
@Schema(description = "安全开关:1已启用,0未启用")
|
||||||
|
@Column
|
||||||
|
private Integer state_safe_enabled;
|
||||||
|
|
||||||
|
@Schema(description = "RTK是否启用:1已启用,0未启用")
|
||||||
|
@Column
|
||||||
|
private Integer state_rtk_enabled;
|
||||||
|
|
||||||
|
@Schema(description = "RTCM是否连接:1已连接,0未连接")
|
||||||
|
@Column
|
||||||
|
private Integer state_rtk_connected;
|
||||||
|
|
||||||
|
@Schema(description = "遥控是否连接:1已连接,0未连接")
|
||||||
|
@Column
|
||||||
|
private Integer state_rc_connected;
|
||||||
|
|
||||||
|
@Schema(description = "避障是否开启:1已启用,0未启用")
|
||||||
|
@Column
|
||||||
|
private Integer state_obs_enabled;
|
||||||
|
|
||||||
|
@Schema(description = "加速度计校准状态:1已校准,0未校准")
|
||||||
|
@Column
|
||||||
|
private Integer state_accel_cal;
|
||||||
|
|
||||||
|
@Schema(description = "陀螺仪校准状态:1已校准,0未校准")
|
||||||
|
@Column
|
||||||
|
private Integer state_gyro_cal;
|
||||||
|
|
||||||
|
@Schema(description = "水平校准状态:1已校准,0未校准")
|
||||||
|
@Column
|
||||||
|
private Integer state_hor_cal;
|
||||||
|
|
||||||
|
@Schema(description = "指南针校准状态:1已校准,0未校准")
|
||||||
|
@Column
|
||||||
|
private Integer state_mag_cal;
|
||||||
|
|
||||||
|
@Schema(description = "FPV推流状态:1开启,0关闭")
|
||||||
|
@Column
|
||||||
|
private Integer state_fpv_live;
|
||||||
|
|
||||||
|
@Schema(description = "云台推流状态:0关闭,1主码流,2子码流")
|
||||||
|
@Column
|
||||||
|
private Integer state_stream_live;
|
||||||
|
|
||||||
|
private String timeStr;
|
||||||
|
|
||||||
|
public void setTime(Instant time) {
|
||||||
|
this.time = time;
|
||||||
|
this.timeStr = DateUtils.utcToTime(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
package com.multictrl.modules.business.q20.influxdb;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.influxdb.annotations.Column;
|
||||||
|
import com.influxdb.annotations.Measurement;
|
||||||
|
import com.multictrl.common.utils.DateUtils;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20设备遥控链路数据 InfluxDB记录
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Data
|
||||||
|
@Schema(name = "Q20设备RC链路上报数据")
|
||||||
|
@Measurement(name = "q20_rc")
|
||||||
|
public class Q20RcReport {
|
||||||
|
|
||||||
|
@Schema(description = "设备SN")
|
||||||
|
@Column(tag = true)
|
||||||
|
private String deviceSn;
|
||||||
|
|
||||||
|
@Schema(description = "架次号")
|
||||||
|
@Column(tag = true)
|
||||||
|
private String order_id;
|
||||||
|
|
||||||
|
@Schema(hidden = true)
|
||||||
|
@Column(timestamp = true)
|
||||||
|
@JsonProperty("_time")
|
||||||
|
private Instant time;
|
||||||
|
|
||||||
|
@Schema(description = "遥控优先级")
|
||||||
|
@Column
|
||||||
|
private Integer priority;
|
||||||
|
|
||||||
|
@Schema(description = "遥控通道状态")
|
||||||
|
@Column
|
||||||
|
private String rc_channel_state;
|
||||||
|
|
||||||
|
@Schema(description = "遥控状态")
|
||||||
|
@Column
|
||||||
|
private String rc_state;
|
||||||
|
|
||||||
|
@Schema(description = "上行丢包率,单位%")
|
||||||
|
@Column
|
||||||
|
private Float up_loss_rate;
|
||||||
|
|
||||||
|
@Schema(description = "下行丢包率,单位%")
|
||||||
|
@Column
|
||||||
|
private Float down_loss_rate;
|
||||||
|
|
||||||
|
@Schema(description = "当前使用链路id")
|
||||||
|
@Column
|
||||||
|
private Integer link_inuse_id;
|
||||||
|
|
||||||
|
@Schema(description = "当前使用链路类型:0未知/1RC遥控/2图传/3网络")
|
||||||
|
@Column
|
||||||
|
private Integer link_inuse_type;
|
||||||
|
|
||||||
|
@Schema(description = "当前使用链路信号质量,单位%")
|
||||||
|
@Column
|
||||||
|
private Integer link_inuse_sqe;
|
||||||
|
|
||||||
|
@Schema(description = "当前使用链路频段,单位MHz")
|
||||||
|
@Column
|
||||||
|
private Integer link_inuse_band;
|
||||||
|
|
||||||
|
@Schema(description = "网络类型:0未知/1WiFi/24G/35G")
|
||||||
|
@Column
|
||||||
|
private Integer network_type;
|
||||||
|
|
||||||
|
@Schema(description = "网络信号质量,单位%")
|
||||||
|
@Column
|
||||||
|
private Integer network_quality;
|
||||||
|
|
||||||
|
private String timeStr;
|
||||||
|
|
||||||
|
public void setTime(Instant time) {
|
||||||
|
this.time = time;
|
||||||
|
this.timeStr = DateUtils.utcToTime(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
package com.multictrl.modules.business.q20.influxdb;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.influxdb.annotations.Column;
|
||||||
|
import com.influxdb.annotations.Measurement;
|
||||||
|
import com.multictrl.common.utils.DateUtils;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Q20设备RTK定位数据 InfluxDB记录
|
||||||
|
*
|
||||||
|
* @author 938693313@qq.com
|
||||||
|
* @since 1.0.0 2026/5/12
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Data
|
||||||
|
@Schema(name = "Q20设备RTK上报数据")
|
||||||
|
@Measurement(name = "q20_rtk")
|
||||||
|
public class Q20RtkReport {
|
||||||
|
|
||||||
|
@Schema(description = "设备SN")
|
||||||
|
@Column(tag = true)
|
||||||
|
private String deviceSn;
|
||||||
|
|
||||||
|
@Schema(description = "架次号")
|
||||||
|
@Column(tag = true)
|
||||||
|
private String order_id;
|
||||||
|
|
||||||
|
@Schema(hidden = true)
|
||||||
|
@Column(timestamp = true)
|
||||||
|
@JsonProperty("_time")
|
||||||
|
private Instant time;
|
||||||
|
|
||||||
|
@Schema(description = "定位类型:0未定位/1单点定位/2差分定位/4固定解")
|
||||||
|
@Column
|
||||||
|
private Integer fix_type;
|
||||||
|
|
||||||
|
@Schema(description = "纬度,WGS84")
|
||||||
|
@Column
|
||||||
|
private Double latitude;
|
||||||
|
|
||||||
|
@Schema(description = "经度,WGS84")
|
||||||
|
@Column
|
||||||
|
private Double longitude;
|
||||||
|
|
||||||
|
@Schema(description = "海拔高度,单位m")
|
||||||
|
@Column
|
||||||
|
private Float altitude;
|
||||||
|
|
||||||
|
@Schema(description = "椭球高,单位m")
|
||||||
|
@Column
|
||||||
|
private Float ellipsoid_height;
|
||||||
|
|
||||||
|
@Schema(description = "可见卫星数")
|
||||||
|
@Column
|
||||||
|
private Integer satellite_visible;
|
||||||
|
|
||||||
|
@Schema(description = "水平位置精度因子,单位m")
|
||||||
|
@Column
|
||||||
|
private Float eph;
|
||||||
|
|
||||||
|
@Schema(description = "垂直位置精度因子,单位m")
|
||||||
|
@Column
|
||||||
|
private Float epv;
|
||||||
|
|
||||||
|
@Schema(description = "速度,单位m/s")
|
||||||
|
@Column
|
||||||
|
private Float velocity;
|
||||||
|
|
||||||
|
@Schema(description = "偏航角,单位°")
|
||||||
|
@Column
|
||||||
|
private Float yaw;
|
||||||
|
|
||||||
|
@Schema(description = "航迹角,单位°")
|
||||||
|
@Column
|
||||||
|
private Float cog;
|
||||||
|
|
||||||
|
@Schema(description = "水平精度,单位m")
|
||||||
|
@Column
|
||||||
|
private Float horizontal_acc;
|
||||||
|
|
||||||
|
@Schema(description = "垂直精度,单位m")
|
||||||
|
@Column
|
||||||
|
private Float vertical_acc;
|
||||||
|
|
||||||
|
@Schema(description = "速度精度,单位m/s")
|
||||||
|
@Column
|
||||||
|
private Float velocity_acc;
|
||||||
|
|
||||||
|
private String timeStr;
|
||||||
|
|
||||||
|
public void setTime(Instant time) {
|
||||||
|
this.time = time;
|
||||||
|
this.timeStr = DateUtils.utcToTime(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -45,7 +45,7 @@ mqtt:
|
||||||
username: admin
|
username: admin
|
||||||
password: Aros2023
|
password: Aros2023
|
||||||
subClientId: dj-one-sub${random.int(10)}
|
subClientId: dj-one-sub${random.int(10)}
|
||||||
subTopic: thing/product/#,sys/product/#
|
subTopic: thing/product/#,sys/product/#,thing/device/#
|
||||||
pubClientId: dj-one-pub${random.int(10)}
|
pubClientId: dj-one-pub${random.int(10)}
|
||||||
client2:
|
client2:
|
||||||
url: tcp://${host.ip}:61637
|
url: tcp://${host.ip}:61637
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue