a-tuoheng-device/src/main/java/com/ruoyi/device/service/impl/TuohengBufferDeviceImpl.java

546 lines
24 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.ruoyi.device.service.impl;
import com.ruoyi.device.domain.api.*;
import com.ruoyi.device.domain.model.*;
import com.ruoyi.device.domain.model.thingsboard.AttributeMap;
import com.ruoyi.device.domain.model.thingsboard.TelemetryMap;
import com.ruoyi.device.domain.model.thingsboard.TelemetryValue;
import com.ruoyi.device.domain.model.thingsboard.tuoheng.constants.TuohengDeviceAttributes;
import com.ruoyi.device.domain.model.thingsboard.tuoheng.constants.TuohengDeviceTelemetry;
import com.ruoyi.device.service.api.IBufferDeviceService;
import com.ruoyi.device.service.dto.AircraftDetailDTO;
import com.ruoyi.device.service.dto.DockDetailDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 拓恒设备缓冲服务实现
* 专门处理拓恒设备的数据整合
*
* @author ruoyi
* @date 2026-02-04Ï
*/
@Service("tuohengBufferDeviceService")
@Slf4j
public class TuohengBufferDeviceImpl implements IBufferDeviceService {
@Autowired
private IDockDomain dockDomain;
@Autowired
private IDeviceDomain deviceDomain;
@Autowired
private IAircraftDomain aircraftDomain;
@Autowired
private IDockAircraftDomain dockAircraftDomain;
@Autowired
private IThingsBoardDomain thingsBoardDomain;
@Override
public DockDetailDTO getDockDetailById(Long dockId) {
log.info("获取拓恒机场详情: dockId={}", dockId);
// 查询机场基础信息
Dock dock = dockDomain.selectDockByDockId(dockId);
if (dock == null) {
log.warn("机场不存在: dockId={}", dockId);
return null;
}
// 查询设备信息
Device device = deviceDomain.selectDeviceByDeviceId(dock.getDeviceId());
if (device == null) {
log.warn("机场对应的设备不存在: deviceId={}", dock.getDeviceId());
return null;
}
// 检查是否是拓恒设备
if (!"tuoheng".equals(device.getDeviceManufacturer())) {
log.warn("设备不是拓恒厂商: deviceId={}, manufacturer={}",
device.getDeviceId(), device.getDeviceManufacturer());
return null;
}
// 构建机场详情DTO
DockDetailDTO dto = new DockDetailDTO();
dto.setDockId(dock.getDockId());
dto.setDockName(dock.getDockName());
dto.setDockLocation(dock.getDockLocation());
dto.setDockIotId(device.getIotDeviceId());
dto.setSnNumber(device.getDeviceSn());
dto.setBindTime(device.getCreateTime().getTime());
dto.setDockManufacturer(device.getDeviceManufacturer());
// 查询关联的无人机,获取无人机的 iotDeviceId
String aircraftIotDeviceId = null;
List<DockAircraft> aircrafts = dockAircraftDomain.selectDockAircraftByDockId(dockId);
if (!CollectionUtils.isEmpty(aircrafts)) {
DockAircraft dockAircraft = aircrafts.get(0);
Aircraft aircraft = aircraftDomain.selectAircraftByAircraftId(dockAircraft.getAircraftId());
if (aircraft != null) {
dto.setAircraftId(aircraft.getAircraftId());
dto.setAircraftName(aircraft.getAircraftName());
Device airDevice = deviceDomain.selectDeviceByDeviceId(aircraft.getDeviceId());
if (airDevice != null) {
aircraftIotDeviceId = airDevice.getIotDeviceId();
dto.setAircraftIotId(aircraftIotDeviceId);
dto.setAircraftManufacturer(airDevice.getDeviceManufacturer());
dto.setAircraftModel(airDevice.getDeviceModel());
}
}
}
// 获取ThingsBoard数据并填充到DTO传入无人机的 iotDeviceId 用于判断工作状态)
fillTuohengDockDetail(dto, device.getIotDeviceId(), aircraftIotDeviceId);
return dto;
}
@Override
public AircraftDetailDTO getAircraftDetailById(Long aircraftId) {
log.info("获取拓恒无人机详情: aircraftId={}", aircraftId);
// 查询无人机基础信息
Aircraft aircraft = aircraftDomain.selectAircraftByAircraftId(aircraftId);
if (aircraft == null) {
log.warn("无人机不存在: aircraftId={}", aircraftId);
return null;
}
// 查询设备信息
Device device = deviceDomain.selectDeviceByDeviceId(aircraft.getDeviceId());
if (device == null) {
log.warn("无人机对应的设备不存在: deviceId={}", aircraft.getDeviceId());
return null;
}
// 检查是否是拓恒设备
if (!"tuoheng".equals(device.getDeviceManufacturer())) {
log.warn("设备不是拓恒厂商: deviceId={}, manufacturer={}",
device.getDeviceId(), device.getDeviceManufacturer());
return null;
}
// 构建无人机详情DTO
AircraftDetailDTO dto = new AircraftDetailDTO();
dto.setAircraftId(aircraft.getAircraftId());
dto.setAircraftName(aircraft.getAircraftName());
dto.setSnNumber(device.getDeviceSn());
dto.setBindTime(device.getCreateTime().getTime());
dto.setAircraftManufacturer(device.getDeviceManufacturer());
// 获取ThingsBoard数据并填充到DTO
fillTuohengAircraftDetail(dto, device.getIotDeviceId());
return dto;
}
@Override
public Map<Long, DockDetailDTO> getDockDetailsByIds(List<Long> dockIds) {
if (CollectionUtils.isEmpty(dockIds)) {
return new HashMap<>();
}
Map<Long, DockDetailDTO> resultMap = new HashMap<>(dockIds.size());
for (Long dockId : dockIds) {
try {
DockDetailDTO dto = getDockDetailById(dockId);
if (dto != null) {
resultMap.put(dockId, dto);
}
} catch (Exception e) {
log.error("获取拓恒机场详情失败, dockId: {}", dockId, e);
}
}
return resultMap;
}
@Override
public Map<Long, AircraftDetailDTO> getAircraftDetailsByIds(List<Long> aircraftIds) {
if (CollectionUtils.isEmpty(aircraftIds)) {
return new HashMap<>();
}
Map<Long, AircraftDetailDTO> resultMap = new HashMap<>(aircraftIds.size());
for (Long aircraftId : aircraftIds) {
try {
AircraftDetailDTO dto = getAircraftDetailById(aircraftId);
if (dto != null) {
resultMap.put(aircraftId, dto);
}
} catch (Exception e) {
log.error("获取拓恒无人机详情失败, aircraftId: {}", aircraftId, e);
}
}
return resultMap;
}
/**
* 填充拓恒机场详情数据
*
* @param dto 机场详情DTO
* @param iotDeviceId ThingsBoard设备ID机场
* @param aircraftIotDeviceId 无人机ThingsBoard设备ID用于判断工作状态
*/
private void fillTuohengDockDetail(DockDetailDTO dto, String iotDeviceId, String aircraftIotDeviceId) {
try {
log.info("========== 开始填充拓恒机场详情 ==========");
log.info("iotDeviceId: {}", iotDeviceId);
// 获取拓恒设备属性
AttributeMap attributes = thingsBoardDomain.getPredefinedTuohengDeviceAttributes(iotDeviceId);
log.info("拓恒设备属性数据: {}", attributes);
// 获取拓恒设备遥测数据
TelemetryMap telemetry = thingsBoardDomain.getPredefinedTuohengDeviceTelemetry(iotDeviceId);
log.info("拓恒设备遥测数据: {}", telemetry);
// 设置固件版本(从属性中获取 hardware_version
attributes.get(TuohengDeviceAttributes.HARDWARE_VERSION)
.ifPresent(value -> {
log.info("HARDWARE_VERSION 固件版本: {}", value);
dto.setFirmwareVersion(value);
});
// 设置在线状态 - 基于心跳时间戳判断离线基于无人机mode判断工作状态
telemetry.get(TuohengDeviceTelemetry.STATUS).ifPresentOrElse(statusValue -> {
long lastHeartbeatTime = statusValue.getTimestamp();
long currentTime = System.currentTimeMillis();
long timeDiff = currentTime - lastHeartbeatTime;
log.info("STATUS 心跳时间戳: {}, 当前时间: {}, 时间差: {}ms ({}秒)",
lastHeartbeatTime, currentTime, timeDiff, timeDiff / 1000);
// 5分钟 = 300000 毫秒
if (timeDiff > 300000) {
dto.setDockStatus("OFFLINE");
log.info("心跳超时(>5分钟),设置机场状态为 OFFLINE");
} else {
// 心跳正常机场在线通过无人机mode和舱门状态判断是IDLE还是WORKING
String dockStatus = "IDLE"; // 默认空闲
if (aircraftIotDeviceId != null) {
try {
// 获取无人机遥测数据
TelemetryMap aircraftTelemetry = thingsBoardDomain.getPredefinedTuohengDeviceTelemetry(aircraftIotDeviceId);
// 通过mode字段判断工作状态
String mode = aircraftTelemetry.get(TuohengDeviceTelemetry.MODE)
.map(TelemetryValue::getValue)
.orElse("");
log.info("无人机MODE值: {}", mode);
// 获取舱门状态(从机场遥测数据中获取)
Integer doorStatus = telemetry.get(TuohengDeviceTelemetry.NEST_DOOR_STATUS)
.map(TelemetryValue::getValue)
.orElse(null);
log.info("机场舱门状态: {}", doorStatus);
// WORKING状态需要同时满足mode=="auto" 且舱门打开(doorStatus==0)
if ("auto".equalsIgnoreCase(mode) && doorStatus != null && doorStatus == 0) {
dockStatus = "WORKING"; // auto模式且舱门打开表示正在执行任务
log.info("无人机处于auto模式且舱门打开设置机场状态为 WORKING");
} else {
if ("auto".equalsIgnoreCase(mode) && (doorStatus == null || doorStatus != 0)) {
log.info("无人机处于auto模式但舱门未打开(doorStatus={}), 设置机场状态为 IDLE", doorStatus);
} else {
log.info("无人机处于{}模式,设置机场状态为 IDLE", mode);
}
}
} catch (Exception e) {
log.warn("获取无人机mode或舱门状态失败默认设置为IDLE: {}", e.getMessage());
}
} else {
log.info("机场未绑定无人机,设置机场状态为 IDLE");
}
dto.setDockStatus(dockStatus);
log.info("心跳正常,设置机场状态: {}", dockStatus);
}
}, () -> {
dto.setDockStatus("OFFLINE");
log.info("没有心跳数据,设置机场状态为 OFFLINE");
});
// 设置舱内温度和湿度
log.info("---------- 解析舱内环境数据 ----------");
telemetry.get(TuohengDeviceTelemetry.NEST_INNER_TEMP)
.ifPresent(value -> {
log.info("NEST_INNER_TEMP 舱内温度: {}", value.getValue());
dto.setCabinTemperature(value.getValue());
});
telemetry.get(TuohengDeviceTelemetry.NEST_INNER_HUM)
.ifPresent(value -> {
log.info("NEST_INNER_HUM 舱内湿度: {}", value.getValue());
dto.setCabinHumidity(value.getValue());
});
// 设置环境数据
log.info("---------- 解析气象数据 ----------");
telemetry.get(TuohengDeviceTelemetry.NEST_INNER_TEMP)
.ifPresent(value -> {
log.info("环境温度(使用舱内温度): {}", value.getValue());
dto.setEnvironmentTemperature(value.getValue());
});
telemetry.get(TuohengDeviceTelemetry.WEATHER_WIND_SPEED)
.ifPresent(value -> {
log.info("WEATHER_WIND_SPEED 风速: {}", value.getValue());
dto.setWindSpeed(value.getValue());
});
telemetry.get(TuohengDeviceTelemetry.WEATHER_RAINFALL)
.ifPresent(value -> {
log.info("WEATHER_RAINFALL 降雨量: {}", value.getValue());
dto.setRainfall(value.getValue());
});
// 设置电池信息
log.info("---------- 解析电池数据 ----------");
telemetry.get(TuohengDeviceTelemetry.BATTERY_LEVEL)
.ifPresent(value -> {
log.info("BATTERY_LEVEL 电池电量: {}", value.getValue());
dto.setCapacity_percent(value.getValue());
});
// 设置充电状态
telemetry.get(TuohengDeviceTelemetry.BATTERY_B_CHARGING)
.ifPresent(value -> {
log.info("BATTERY_B_CHARGING 充电状态原始值: {}", value.getValue());
String chargingStatus = value.getValue() == 1 ? "CHARGING" : "FREE";
dto.setChargingStatus(chargingStatus);
log.info("设置充电状态: {}", chargingStatus);
});
// 填充无人机状态信息
if (aircraftIotDeviceId != null) {
fillTuohengAircraftStatus(dto, aircraftIotDeviceId);
}
log.info("拓恒机场详情填充完成: iotDeviceId={}, dockStatus={}", iotDeviceId, dto.getDockStatus());
log.info("========== 拓恒机场详情填充结束 ==========");
} catch (Exception e) {
log.error("填充拓恒机场详情失败: iotDeviceId={}, error={}", iotDeviceId, e.getMessage(), e);
}
}
/**
* 填充拓恒无人机状态信息(用于机场详情中的无人机状态)
*
* @param dto 机场详情DTO
* @param aircraftIotDeviceId 无人机ThingsBoard设备ID
*/
private void fillTuohengAircraftStatus(DockDetailDTO dto, String aircraftIotDeviceId) {
try {
log.info("========== 开始填充拓恒无人机状态(机场详情中) ==========");
log.info("aircraftIotDeviceId: {}", aircraftIotDeviceId);
// 获取拓恒无人机遥测数据
TelemetryMap aircraftTelemetry = thingsBoardDomain.getPredefinedTuohengDeviceTelemetry(aircraftIotDeviceId);
log.info("拓恒无人机遥测数据: {}", aircraftTelemetry);
// 获取机场遥测数据(用于获取舱门状态)
// 注意aircraftIotDeviceId 是无人机的ID我们需要获取机场的舱门状态
// 舱门状态在机场设备的遥测数据中,需要从 dto.getDockIotId() 获取
String dockIotId = dto.getDockIotId();
TelemetryMap dockTelemetry = null;
if (dockIotId != null) {
dockTelemetry = thingsBoardDomain.getPredefinedTuohengDeviceTelemetry(dockIotId);
log.info("拓恒机场遥测数据(用于获取舱门状态): {}", dockTelemetry);
}
// 获取 mode 字段
String mode = aircraftTelemetry.get(TuohengDeviceTelemetry.MODE)
.map(TelemetryValue::getValue)
.orElse("");
log.info("无人机 MODE 值: {}", mode);
// 获取 tsingal图传信号强度用于判断开关机
Integer tsingal = aircraftTelemetry.get(TuohengDeviceTelemetry.TSINGAL)
.map(TelemetryValue::getValue)
.orElse(0);
log.info("无人机 TSINGAL 值: {}", tsingal);
boolean isPowerOn = tsingal > 60; // tsingal > 60 表示开机
log.info("无人机开关机状态: {}", isPowerOn ? "开机" : "关机");
// 获取 nest_door_status舱门状态
Integer doorStatus = null;
if (dockTelemetry != null) {
doorStatus = dockTelemetry.get(TuohengDeviceTelemetry.NEST_DOOR_STATUS)
.map(TelemetryValue::getValue)
.orElse(null);
log.info("机场舱门状态: {}", doorStatus);
}
// 判断逻辑IN_MISSION 需要同时满足 mode=="auto" 且舱门打开
String aircraftStatus;
if ("auto".equalsIgnoreCase(mode) && doorStatus != null && doorStatus == 0) {
// mode == "auto" 且舱门打开,表示正在执行任务
aircraftStatus = "IN_MISSION";
log.info("无人机处于 auto 模式且舱门打开,设置状态: IN_MISSION");
} else {
// 其他情况,根据 tsingal 和 nest_door_status 判断
if (doorStatus != null && doorStatus == 1) {
// 舱门关闭(舱内)
if (isPowerOn) {
aircraftStatus = "POWER_ON_IN_CABIN";
log.info("舱门关闭 + 开机 → POWER_ON_IN_CABIN");
} else {
aircraftStatus = "POWER_OFF_IN_CABIN";
log.info("舱门关闭 + 关机 → POWER_OFF_IN_CABIN");
}
} else if (doorStatus != null && doorStatus == 0) {
// 舱门打开(舱外),但不是 auto 模式
if (isPowerOn) {
aircraftStatus = "POWER_ON_OUT_CABIN";
log.info("舱门打开 + 开机(非auto模式) → POWER_ON_OUT_CABIN");
} else {
aircraftStatus = "POWER_OFF_OUT_CABIN";
log.info("舱门打开 + 关机 → POWER_OFF_OUT_CABIN");
}
} else {
// 无法获取舱门状态,默认根据开关机状态判断
if (isPowerOn) {
aircraftStatus = "POWER_ON_IN_CABIN";
log.warn("无法获取舱门状态,默认设置: POWER_ON_IN_CABIN");
} else {
aircraftStatus = "POWER_OFF_IN_CABIN";
log.warn("无法获取舱门状态,默认设置: POWER_OFF_IN_CABIN");
}
}
}
dto.setAircraftStatus(aircraftStatus);
// 设置作业架次 - 暂时设置为0拓恒设备可能没有这个数据
dto.setMissionCount(0);
log.info("设置作业架次: 0 (拓恒设备暂无此数据)");
log.info("拓恒无人机状态填充完成: aircraftIotDeviceId={}, aircraftStatus={}",
aircraftIotDeviceId, dto.getAircraftStatus());
log.info("========== 拓恒无人机状态填充结束 ==========");
} catch (Exception e) {
log.error("填充拓恒无人机状态失败: aircraftIotDeviceId={}, error={}",
aircraftIotDeviceId, e.getMessage(), e);
}
}
/**
* 填充拓恒无人机详情数据
*
* @param dto 无人机详情DTO
* @param iotDeviceId ThingsBoard设备ID
*/
private void fillTuohengAircraftDetail(AircraftDetailDTO dto, String iotDeviceId) {
try {
log.info("========== 开始填充拓恒无人机详情 ==========");
log.info("iotDeviceId: {}", iotDeviceId);
// 获取拓恒设备属性
AttributeMap attributes = thingsBoardDomain.getPredefinedTuohengDeviceAttributes(iotDeviceId);
log.info("拓恒无人机属性数据: {}", attributes);
// 获取拓恒设备遥测数据
TelemetryMap telemetry = thingsBoardDomain.getPredefinedTuohengDeviceTelemetry(iotDeviceId);
log.info("拓恒无人机遥测数据: {}", telemetry);
// 设置无人机状态
log.info("---------- 解析无人机状态 ----------");
// 获取 mode 字段
String mode = telemetry.get(TuohengDeviceTelemetry.MODE)
.map(TelemetryValue::getValue)
.orElse("");
log.info("无人机 MODE 值: {}", mode);
// 获取 tsingal图传信号强度用于判断开关机
Integer tsingal = telemetry.get(TuohengDeviceTelemetry.TSINGAL)
.map(TelemetryValue::getValue)
.orElse(0);
log.info("无人机 TSINGAL 值: {}", tsingal);
boolean isPowerOn = tsingal > 60; // tsingal > 60 表示开机
log.info("无人机开关机状态: {}", isPowerOn ? "开机" : "关机");
// 判断逻辑
// 注意:无人机详情接口无法获取机场舱门状态
// 如果 mode == "auto" 且开机,推测为 IN_MISSION
// 否则根据开关机状态判断
String aircraftStatus;
if ("auto".equalsIgnoreCase(mode) && isPowerOn) {
// mode == "auto" 且开机,推测正在执行任务
aircraftStatus = "IN_MISSION";
log.info("无人机处于 auto 模式且开机,推测状态: IN_MISSION");
} else {
// 其他情况,根据开关机状态判断(默认舱内)
if (isPowerOn) {
aircraftStatus = "POWER_ON_IN_CABIN";
log.info("开机状态,默认设置: POWER_ON_IN_CABIN");
} else {
aircraftStatus = "POWER_OFF_IN_CABIN";
log.info("关机状态,默认设置: POWER_OFF_IN_CABIN");
}
}
dto.setAircraftStatus(aircraftStatus);
// 设置作业架次 - 暂时设置为0
dto.setMissionCount(0);
log.info("设置作业架次: 0");
// 设置GPS信号
log.info("---------- 解析GPS数据 ----------");
telemetry.get(TuohengDeviceTelemetry.SAT_COUNT)
.ifPresent(value -> {
log.info("SAT_COUNT 卫星数量: {}", value.getValue());
dto.setGpsSignal(value.getValue());
});
// 设置电池信息
log.info("---------- 解析电池数据 ----------");
telemetry.get(TuohengDeviceTelemetry.BATTERY_REMAIN)
.ifPresent(value -> {
log.info("BATTERY_REMAIN 剩余电量: {}", value.getValue());
dto.setBatteryLevel(value.getValue());
});
telemetry.get(TuohengDeviceTelemetry.VOLTAGE)
.ifPresent(value -> {
log.info("VOLTAGE 电压原始值: {}", value.getValue());
Double voltage = value.getValue();
if (voltage != null) {
dto.setVoltage(voltage.intValue());
log.info("VOLTAGE 电压转换后: {}", voltage.intValue());
}
});
// 设置飞行时长(秒)
log.info("---------- 解析飞行数据 ----------");
telemetry.get(TuohengDeviceTelemetry.FLIGHT_TIME)
.ifPresent(value -> {
log.info("FLIGHT_TIME 飞行时长(秒): {}", value.getValue());
dto.setFlightDuration(value.getValue());
});
log.info("拓恒无人机详情填充完成: iotDeviceId={}, aircraftStatus={}", iotDeviceId, dto.getAircraftStatus());
log.info("========== 拓恒无人机详情填充结束 ==========");
} catch (Exception e) {
log.error("填充拓恒无人机详情失败: iotDeviceId={}, error={}", iotDeviceId, e.getMessage(), e);
}
}
}