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

836 lines
40 KiB
Java
Raw Normal View History

2026-02-04 16:20:50 +08:00
package com.ruoyi.device.service.impl;
import com.ruoyi.device.domain.api.*;
import com.ruoyi.device.domain.impl.machine.state.CoverState;
import com.ruoyi.device.domain.impl.machine.state.DroneState;
import com.ruoyi.device.domain.impl.machine.state.MachineStates;
import com.ruoyi.device.domain.impl.machine.statemachine.MachineStateManager;
2026-02-04 16:20:50 +08:00
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
2026-02-04 16:36:18 +08:00
* @date 2026-02-04Ï
2026-02-04 16:20:50 +08:00
*/
@Service("tuohengBufferDeviceService")
@Slf4j
public class TuohengBufferDeviceImpl implements IBufferDeviceService {
2026-02-06 13:56:09 +08:00
/**
* 默认经纬度 - 南京市
*/
private static final Double DEFAULT_LONGITUDE = 118.796877;
private static final Double DEFAULT_LATITUDE = 32.060255;
2026-02-04 16:20:50 +08:00
@Autowired
private IDockDomain dockDomain;
@Autowired
private IDeviceDomain deviceDomain;
@Autowired
private IAircraftDomain aircraftDomain;
@Autowired
private IDockAircraftDomain dockAircraftDomain;
@Autowired
private IThingsBoardDomain thingsBoardDomain;
@Autowired
private MachineStateManager machineStateManager;
2026-02-04 16:20:50 +08:00
@Override
public DockDetailDTO getDockDetailById(Long dockId) {
log.info("获取拓恒机场详情: dockId={}", dockId);
// 查询机场基础信息
Dock dock = dockDomain.selectDockByDockId(dockId);
if (dock == null) {
log.warn("机场不存在: dockId={}", dockId);
return null;
}
2026-03-24 13:40:59 +08:00
log.info("Dock对象详细信息: dockId={}, cabinVideoUrl={}, outsideVideoUrl={}, liveVideoUrl={}",
dock.getDockId(), dock.getCabinVideoUrl(), dock.getOutsideVideoUrl(), dock.getLiveVideoUrl());
2026-02-04 16:20:50 +08:00
// 查询设备信息
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());
2026-03-17 15:18:41 +08:00
dto.setInternalCamera(dock.getCabinVideoUrl());
dto.setExternalCamera(dock.getOutsideVideoUrl());
dto.setLiveCamera(dock.getLiveVideoUrl());
2026-03-24 13:40:59 +08:00
log.info("设置DTO视频地址: internalCamera={}, externalCamera={}, liveCamera={}",
dto.getInternalCamera(), dto.getExternalCamera(), dto.getLiveCamera());
2026-02-04 16:20:50 +08:00
dto.setDockIotId(device.getIotDeviceId());
dto.setSnNumber(device.getDeviceSn());
dto.setBindTime(device.getCreateTime().getTime());
2026-02-06 09:20:58 +08:00
dto.setDockManufacturer(device.getDeviceManufacturer());
2026-02-04 16:20:50 +08:00
2026-02-06 10:21:34 +08:00
// 查询关联的无人机,获取无人机的 iotDeviceId
String aircraftIotDeviceId = null;
2026-02-04 16:20:50 +08:00
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) {
2026-02-06 10:21:34 +08:00
aircraftIotDeviceId = airDevice.getIotDeviceId();
dto.setAircraftIotId(aircraftIotDeviceId);
2026-02-06 10:30:14 +08:00
dto.setAircraftManufacturer(airDevice.getDeviceManufacturer());
dto.setAircraftModel(airDevice.getDeviceModel());
2026-02-04 16:20:50 +08:00
}
}
}
2026-02-06 10:21:34 +08:00
// 获取ThingsBoard数据并填充到DTO传入无人机的 iotDeviceId 用于判断工作状态)
fillTuohengDockDetail(dto, device.getIotDeviceId(), aircraftIotDeviceId);
2026-02-04 16:20:50 +08:00
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());
2026-02-06 10:23:28 +08:00
dto.setAircraftManufacturer(device.getDeviceManufacturer());
2026-02-04 16:20:50 +08:00
// 查询无人机关联的机场获取机场SN用于从MachineStateManager获取状态
2026-02-11 16:37:27 +08:00
String dockSn = null;
List<DockAircraft> dockAircrafts = dockAircraftDomain.selectDockAircraftByAircraftId(aircraftId);
if (!CollectionUtils.isEmpty(dockAircrafts)) {
DockAircraft dockAircraft = dockAircrafts.get(0);
Dock dock = dockDomain.selectDockByDockId(dockAircraft.getDockId());
if (dock != null) {
Device dockDevice = deviceDomain.selectDeviceByDeviceId(dock.getDeviceId());
if (dockDevice != null) {
dockSn = dockDevice.getDeviceSn();
}
}
}
// 获取ThingsBoard数据并填充到DTO传入机场SN用于获取状态
2026-02-11 16:37:27 +08:00
fillTuohengAircraftDetail(dto, device.getIotDeviceId(), dockSn);
2026-02-04 16:20:50 +08:00
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
2026-02-06 10:21:34 +08:00
* @param iotDeviceId ThingsBoard设备ID机场
* @param aircraftIotDeviceId 无人机ThingsBoard设备ID用于判断工作状态
2026-02-04 16:20:50 +08:00
*/
2026-02-06 10:21:34 +08:00
private void fillTuohengDockDetail(DockDetailDTO dto, String iotDeviceId, String aircraftIotDeviceId) {
2026-02-04 16:20:50 +08:00
try {
2026-02-04 17:17:53 +08:00
log.info("========== 开始填充拓恒机场详情 ==========");
log.info("iotDeviceId: {}", iotDeviceId);
2026-02-04 16:20:50 +08:00
// 获取拓恒设备属性
AttributeMap attributes = thingsBoardDomain.getPredefinedTuohengDeviceAttributes(iotDeviceId);
2026-03-19 15:06:57 +08:00
// log.info("拓恒设备属性数据: {}", attributes);
2026-02-04 17:17:53 +08:00
2026-02-04 16:20:50 +08:00
// 获取拓恒设备遥测数据
TelemetryMap telemetry = thingsBoardDomain.getPredefinedTuohengDeviceTelemetry(iotDeviceId);
2026-03-19 15:06:57 +08:00
// log.info("拓恒设备遥测数据: {}", telemetry);
2026-02-04 16:20:50 +08:00
2026-02-06 11:46:36 +08:00
// 设置固件版本(从属性中获取 hardware_version
attributes.get(TuohengDeviceAttributes.HARDWARE_VERSION)
.ifPresent(value -> {
log.info("HARDWARE_VERSION 固件版本: {}", value);
dto.setFirmwareVersion(value);
});
2026-02-06 13:56:09 +08:00
// 设置机场位置(从属性中获取 home.longitude 和 home.latitude取不到则使用南京市默认值
2026-02-06 13:48:13 +08:00
log.info("---------- 解析机场位置数据 ----------");
2026-02-06 13:56:09 +08:00
Double homeLongitude = attributes.get(TuohengDeviceAttributes.HOME_LONGITUDE)
.orElse(null);
if (homeLongitude != null) {
log.info("HOME_LONGITUDE 机场经度: {}", homeLongitude);
dto.setLongitude(homeLongitude);
} else {
log.info("HOME_LONGITUDE 未设置,使用默认值(南京市): {}", DEFAULT_LONGITUDE);
dto.setLongitude(DEFAULT_LONGITUDE);
}
Double homeLatitude = attributes.get(TuohengDeviceAttributes.HOME_LATITUDE)
.orElse(null);
if (homeLatitude != null) {
log.info("HOME_LATITUDE 机场纬度: {}", homeLatitude);
dto.setLatitude(homeLatitude);
} else {
log.info("HOME_LATITUDE 未设置,使用默认值(南京市): {}", DEFAULT_LATITUDE);
dto.setLatitude(DEFAULT_LATITUDE);
}
// 设置备降点位置(从属性中获取 backup.longitude 和 backup.latitude取不到则使用南京市默认值
log.info("---------- 解析备降点位置数据 ----------");
Double backupLongitude = attributes.get(TuohengDeviceAttributes.BACKUP_LONGITUDE)
.orElse(null);
if (backupLongitude != null) {
log.info("BACKUP_LONGITUDE 备降点经度: {}", backupLongitude);
dto.setBackupLongitude(backupLongitude);
} else {
log.info("BACKUP_LONGITUDE 未设置,使用默认值(南京市): {}", DEFAULT_LONGITUDE);
dto.setBackupLongitude(DEFAULT_LONGITUDE);
}
Double backupLatitude = attributes.get(TuohengDeviceAttributes.BACKUP_LATITUDE)
.orElse(null);
if (backupLatitude != null) {
log.info("BACKUP_LATITUDE 备降点纬度: {}", backupLatitude);
dto.setBackupLatitude(backupLatitude);
} else {
log.info("BACKUP_LATITUDE 未设置,使用默认值(南京市): {}", DEFAULT_LATITUDE);
dto.setBackupLatitude(DEFAULT_LATITUDE);
}
2026-02-06 13:48:13 +08:00
2026-02-06 14:12:34 +08:00
// 设置运行数据(从属性中获取 runningDuration 和 missionCount取不到则默认为 0
log.info("---------- 解析运行数据 ----------");
Integer runningDuration = attributes.get(TuohengDeviceAttributes.RUNNING_DURATION)
.orElse(0);
log.info("RUNNING_DURATION 运行时长: {} 小时", runningDuration);
dto.setRunningDuration(runningDuration);
Integer missionCount = attributes.get(TuohengDeviceAttributes.MISSION_COUNT)
.orElse(0);
log.info("MISSION_COUNT 作业架次: {}", missionCount);
dto.setMissionCount(missionCount);
2026-02-06 10:21:34 +08:00
// 设置在线状态 - 基于心跳时间戳判断离线基于无人机mode判断工作状态
2026-02-06 09:44:30 +08:00
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 {
2026-02-06 11:16:20 +08:00
// 心跳正常机场在线通过无人机mode和舱门状态判断是IDLE还是WORKING
2026-02-06 10:21:34 +08:00
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);
2026-03-14 10:06:19 +08:00
// 通过 MachineStateManager 获取舱门状态
MachineStates machineStates =
machineStateManager.getStates(dto.getSnNumber());
CoverState coverState =
machineStates.getCoverState();
log.info("机场舱门状态(从MachineStateManager): {}", coverState);
2026-02-06 11:16:20 +08:00
2026-03-24 14:20:10 +08:00
// 通过MODE判断飞行模式暂时注释掉数据不准确
// boolean isFlyingMode = "auto".equalsIgnoreCase(mode) ||
// "guided".equalsIgnoreCase(mode) ||
// "loiter".equalsIgnoreCase(mode) ||
// "rtl".equalsIgnoreCase(mode);
// if (isFlyingMode) {
// dockStatus = "WORKING";
// log.info("无人机处于{}模式,设置机场状态为 WORKING", mode);
// } else if (coverState == CoverState.OPENED) {
if (coverState == CoverState.OPENED) {
2026-03-14 10:06:19 +08:00
dockStatus = "WORKING";
log.info("舱门打开,设置机场状态为 WORKING");
} else {
2026-03-24 14:20:10 +08:00
log.info("舱门关闭,设置机场状态为 IDLE");
2026-02-06 10:21:34 +08:00
}
} catch (Exception e) {
2026-02-06 11:16:20 +08:00
log.warn("获取无人机mode或舱门状态失败默认设置为IDLE: {}", e.getMessage());
2026-02-06 10:21:34 +08:00
}
} else {
log.info("机场未绑定无人机,设置机场状态为 IDLE");
}
2026-02-04 17:17:53 +08:00
dto.setDockStatus(dockStatus);
2026-02-06 09:44:30 +08:00
log.info("心跳正常,设置机场状态: {}", dockStatus);
}
}, () -> {
2026-02-04 16:20:50 +08:00
dto.setDockStatus("OFFLINE");
2026-02-06 09:44:30 +08:00
log.info("没有心跳数据,设置机场状态为 OFFLINE");
});
2026-02-04 16:20:50 +08:00
// 设置舱内温度和湿度
2026-02-04 17:17:53 +08:00
log.info("---------- 解析舱内环境数据 ----------");
2026-02-04 16:20:50 +08:00
telemetry.get(TuohengDeviceTelemetry.NEST_INNER_TEMP)
2026-02-04 17:17:53 +08:00
.ifPresent(value -> {
log.info("NEST_INNER_TEMP 舱内温度: {}", value.getValue());
dto.setCabinTemperature(value.getValue());
});
2026-02-04 16:20:50 +08:00
telemetry.get(TuohengDeviceTelemetry.NEST_INNER_HUM)
2026-02-04 17:17:53 +08:00
.ifPresent(value -> {
log.info("NEST_INNER_HUM 舱内湿度: {}", value.getValue());
dto.setCabinHumidity(value.getValue());
});
2026-02-04 16:20:50 +08:00
/**
* 通过 MachineStateManager 获取舱门状态
*/
2026-02-06 14:15:24 +08:00
// 设置舱门状态
log.info("---------- 解析舱门状态 ----------");
try {
MachineStates machineStates =
machineStateManager.getStates(dto.getSnNumber());
CoverState coverState =
machineStates.getCoverState();
log.info("舱门状态(从MachineStateManager): {}", coverState);
String cabinDoorStatus;
if (coverState == CoverState.OPENED) {
cabinDoorStatus = "OPEN";
} else if (coverState == CoverState.CLOSED) {
cabinDoorStatus = "CLOSED";
} else {
cabinDoorStatus = "UNKNOWN";
}
dto.setCabinDoorStatus(cabinDoorStatus);
log.info("设置舱门状态: {}", cabinDoorStatus);
} catch (Exception e) {
log.warn("从MachineStateManager获取舱门状态失败: {}", e.getMessage());
dto.setCabinDoorStatus("UNKNOWN");
}
2026-02-06 14:15:24 +08:00
2026-02-06 14:24:39 +08:00
// 设置空调状态(从属性中获取 airConditionerStatus取不到则默认为 IDLE
log.info("---------- 解析空调状态 ----------");
String airConditionerStatus = attributes.get(TuohengDeviceAttributes.AIR_CONDITIONER_STATUS)
.orElse("IDLE");
log.info("AIR_CONDITIONER_STATUS 空调状态: {}", airConditionerStatus);
dto.setAirConditionerStatus(airConditionerStatus);
2026-02-04 16:20:50 +08:00
// 设置环境数据
2026-02-04 17:17:53 +08:00
log.info("---------- 解析气象数据 ----------");
2026-02-06 14:38:49 +08:00
// 环境温度和湿度从属性中获取(手动维护),取不到则为 null
attributes.get(TuohengDeviceAttributes.ENVIRONMENT_TEMPERATURE)
.ifPresent(value -> {
log.info("ENVIRONMENT_TEMPERATURE 环境温度: {}", value);
dto.setEnvironmentTemperature(value);
});
attributes.get(TuohengDeviceAttributes.ENVIRONMENT_HUMIDITY)
2026-02-04 17:17:53 +08:00
.ifPresent(value -> {
2026-02-06 14:38:49 +08:00
log.info("ENVIRONMENT_HUMIDITY 环境湿度: {}", value);
dto.setEnvironmentHumidity(value);
2026-02-04 17:17:53 +08:00
});
2026-02-06 14:38:49 +08:00
// 风速和降雨量从遥测数据中获取
2026-02-04 16:20:50 +08:00
telemetry.get(TuohengDeviceTelemetry.WEATHER_WIND_SPEED)
2026-02-04 17:17:53 +08:00
.ifPresent(value -> {
log.info("WEATHER_WIND_SPEED 风速: {}", value.getValue());
dto.setWindSpeed(value.getValue());
});
2026-02-04 16:20:50 +08:00
telemetry.get(TuohengDeviceTelemetry.WEATHER_RAINFALL)
2026-02-04 17:17:53 +08:00
.ifPresent(value -> {
log.info("WEATHER_RAINFALL 降雨量: {}", value.getValue());
dto.setRainfall(value.getValue());
});
2026-02-04 16:20:50 +08:00
2026-02-06 16:16:43 +08:00
// 设置电池信息(从无人机设备获取)
2026-02-04 17:17:53 +08:00
log.info("---------- 解析电池数据 ----------");
2026-02-06 16:16:43 +08:00
if (aircraftIotDeviceId != null) {
try {
// 获取无人机遥测数据
TelemetryMap aircraftTelemetry = thingsBoardDomain.getPredefinedTuohengDeviceTelemetry(aircraftIotDeviceId);
// 从无人机获取电池电量(使用 battery_remain
aircraftTelemetry.get(TuohengDeviceTelemetry.BATTERY_REMAIN)
.ifPresent(value -> {
log.info("BATTERY_REMAIN 无人机电池电量: {}", value.getValue());
dto.setCapacity_percent(value.getValue());
});
} catch (Exception e) {
log.warn("从无人机获取电池电量失败,尝试从机场获取: {}", e.getMessage());
// 如果无人机数据获取失败,降级使用机场的 BATTERY_LEVEL
telemetry.get(TuohengDeviceTelemetry.BATTERY_LEVEL)
.ifPresent(value -> {
log.info("BATTERY_LEVEL 机场电池电量(降级): {}", value.getValue());
dto.setCapacity_percent(value.getValue());
});
}
} else {
// 如果没有绑定无人机,使用机场的 BATTERY_LEVEL
log.info("机场未绑定无人机,使用机场电池电量");
telemetry.get(TuohengDeviceTelemetry.BATTERY_LEVEL)
.ifPresent(value -> {
log.info("BATTERY_LEVEL 机场电池电量: {}", value.getValue());
dto.setCapacity_percent(value.getValue());
});
}
2026-02-04 16:20:50 +08:00
// 设置充电状态
telemetry.get(TuohengDeviceTelemetry.BATTERY_B_CHARGING)
.ifPresent(value -> {
2026-02-04 17:17:53 +08:00
log.info("BATTERY_B_CHARGING 充电状态原始值: {}", value.getValue());
String chargingStatus = value.getValue() == 1 ? "CHARGING" : "FREE";
dto.setChargingStatus(chargingStatus);
log.info("设置充电状态: {}", chargingStatus);
2026-02-04 16:20:50 +08:00
});
2026-02-07 09:17:04 +08:00
// 设置机场设备自检数据升降架、X轴夹器、Y轴夹器
log.info("---------- 解析机场设备自检数据 ----------");
// 升降架状态,取不到值时默认为 NORMAL
String elevatorPosition = telemetry.get(TuohengDeviceTelemetry.LIFTER_STATUS)
.map(value -> {
2026-02-07 09:17:04 +08:00
Integer lifterStatus = value.getValue();
log.info("LIFTER_STATUS 升降架状态原始值: {}", lifterStatus);
// 0=正常, 非0=异常
return (lifterStatus != null && lifterStatus == 0) ? "NORMAL" : "ABNORMAL";
})
.orElse("NORMAL");
dto.setElevatorPosition(elevatorPosition);
log.info("设置升降架位置: {}", elevatorPosition);
// X轴夹器状态取不到值时默认为 NORMAL
String xAxisClampStatus = telemetry.get(TuohengDeviceTelemetry.HOLDER_X_STATUS)
.map(value -> {
2026-02-07 09:17:04 +08:00
Integer holderXStatus = value.getValue();
log.info("HOLDER_X_STATUS X轴夹器状态原始值: {}", holderXStatus);
// 0=正常, 非0=异常
return (holderXStatus != null && holderXStatus == 0) ? "NORMAL" : "ABNORMAL";
})
.orElse("NORMAL");
dto.setXAxisClampStatus(xAxisClampStatus);
log.info("设置X轴夹器状态: {}", xAxisClampStatus);
// Y轴夹器状态取不到值时默认为 NORMAL
String yAxisClampStatus = telemetry.get(TuohengDeviceTelemetry.HOLDER_Y_STATUS)
.map(value -> {
2026-02-07 09:17:04 +08:00
Integer holderYStatus = value.getValue();
log.info("HOLDER_Y_STATUS Y轴夹器状态原始值: {}", holderYStatus);
// 0=正常, 非0=异常
return (holderYStatus != null && holderYStatus == 0) ? "NORMAL" : "ABNORMAL";
})
.orElse("NORMAL");
dto.setYAxisClampStatus(yAxisClampStatus);
log.info("设置Y轴夹器状态: {}", yAxisClampStatus);
2026-02-07 09:17:04 +08:00
2026-02-06 11:02:45 +08:00
// 填充无人机状态信息
if (aircraftIotDeviceId != null) {
fillTuohengAircraftStatus(dto, aircraftIotDeviceId);
}
2026-02-04 17:17:53 +08:00
log.info("拓恒机场详情填充完成: iotDeviceId={}, dockStatus={}", iotDeviceId, dto.getDockStatus());
log.info("========== 拓恒机场详情填充结束 ==========");
2026-02-04 16:20:50 +08:00
} 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 {
2026-02-04 17:17:53 +08:00
log.info("========== 开始填充拓恒无人机状态(机场详情中) ==========");
log.info("aircraftIotDeviceId: {}", aircraftIotDeviceId);
2026-02-04 16:20:50 +08:00
// 获取拓恒无人机遥测数据
2026-02-06 11:02:45 +08:00
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);
// 通过 MachineStateManager 获取舱门状态使用机场SN
CoverState coverState = null;
try {
MachineStates machineStates =
machineStateManager.getStates(dto.getSnNumber());
coverState = machineStates.getCoverState();
log.info("机场舱门状态(从MachineStateManager): {}", coverState);
} catch (Exception e) {
log.warn("从MachineStateManager获取舱门状态失败: {}", e.getMessage());
}
2026-02-06 11:09:23 +08:00
// 从 MachineStateManager 获取无人机开关机状态
DroneState droneState = null;
try {
MachineStates machineStates =
machineStateManager.getStates(dto.getSnNumber());
droneState = machineStates.getDroneState();
log.info("无人机状态(从MachineStateManager): {}", droneState);
} catch (Exception e) {
log.warn("从MachineStateManager获取无人机状态失败: {}", e.getMessage());
2026-02-06 11:09:23 +08:00
}
2026-02-04 16:20:50 +08:00
boolean isPowerOn = (droneState != null && droneState != DroneState.POWER_OFF && droneState != DroneState.UNKNOWN);
log.info("无人机开关机状态: {}", isPowerOn ? "开机" : "关机");
2026-02-11 16:42:46 +08:00
// 判断逻辑:开仓且开机才是任务中
2026-02-06 11:09:23 +08:00
String aircraftStatus;
2026-02-11 16:42:46 +08:00
if (coverState == CoverState.OPENED && isPowerOn) {
// 舱门打开且开机,表示正在执行任务
2026-02-06 11:09:23 +08:00
aircraftStatus = "IN_MISSION";
2026-02-11 16:42:46 +08:00
log.info("舱门打开 + 开机 → IN_MISSION");
} else if (coverState == CoverState.OPENED && !isPowerOn) {
// 舱门打开但关机
aircraftStatus = "POWER_OFF_OUT_CABIN";
log.info("舱门打开 + 关机 → POWER_OFF_OUT_CABIN");
} else if (coverState == CoverState.CLOSED) {
// 舱门关闭(舱内),根据开关机状态判断
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");
}
2026-02-06 11:09:23 +08:00
} else {
// 无法获取舱门状态,默认根据开关机状态判断
if (isPowerOn) {
aircraftStatus = "POWER_ON_IN_CABIN";
log.warn("无法获取舱门状态,默认设置: POWER_ON_IN_CABIN");
2026-02-04 16:20:50 +08:00
} else {
aircraftStatus = "POWER_OFF_IN_CABIN";
log.warn("无法获取舱门状态,默认设置: POWER_OFF_IN_CABIN");
2026-02-04 16:20:50 +08:00
}
2026-02-06 11:02:45 +08:00
}
2026-02-04 16:20:50 +08:00
2026-02-06 11:09:23 +08:00
dto.setAircraftStatus(aircraftStatus);
2026-02-04 17:17:53 +08:00
log.info("拓恒无人机状态填充完成: aircraftIotDeviceId={}, aircraftStatus={}",
aircraftIotDeviceId, dto.getAircraftStatus());
log.info("========== 拓恒无人机状态填充结束 ==========");
2026-02-04 16:20:50 +08:00
} catch (Exception e) {
log.error("填充拓恒无人机状态失败: aircraftIotDeviceId={}, error={}",
aircraftIotDeviceId, e.getMessage(), e);
}
}
/**
* 填充拓恒无人机详情数据
*
* @param dto 无人机详情DTO
* @param iotDeviceId ThingsBoard设备ID
* @param dockSn 机场SN号用于从MachineStateManager获取状态
2026-02-04 16:20:50 +08:00
*/
private void fillTuohengAircraftDetail(AircraftDetailDTO dto, String iotDeviceId, String dockSn) {
2026-02-04 16:20:50 +08:00
try {
2026-02-04 17:17:53 +08:00
log.info("========== 开始填充拓恒无人机详情 ==========");
log.info("iotDeviceId: {}", iotDeviceId);
2026-02-04 16:20:50 +08:00
// 获取拓恒设备属性
AttributeMap attributes = thingsBoardDomain.getPredefinedTuohengDeviceAttributes(iotDeviceId);
2026-02-04 17:17:53 +08:00
log.info("拓恒无人机属性数据: {}", attributes);
2026-02-04 16:20:50 +08:00
// 获取拓恒设备遥测数据
TelemetryMap telemetry = thingsBoardDomain.getPredefinedTuohengDeviceTelemetry(iotDeviceId);
2026-02-04 17:17:53 +08:00
log.info("拓恒无人机遥测数据: {}", telemetry);
2026-02-04 16:20:50 +08:00
// 设置无人机状态
2026-02-04 17:17:53 +08:00
log.info("---------- 解析无人机状态 ----------");
2026-02-06 11:02:45 +08:00
// 获取 mode 字段
String mode = telemetry.get(TuohengDeviceTelemetry.MODE)
.map(TelemetryValue::getValue)
.orElse("");
log.info("无人机 MODE 值: {}", mode);
2026-02-11 16:28:03 +08:00
// // 获取 tsingal图传信号强度用于判断开关机
// Integer tsingal = telemetry.get(TuohengDeviceTelemetry.TSINGAL)
// .map(TelemetryValue::getValue)
// .orElse(0);
// log.info("无人机 TSINGAL 值: {}", tsingal);
2026-02-06 11:02:45 +08:00
// 从 MachineStateManager 获取无人机开关机状态
DroneState droneState = null;
if (dockSn != null) {
try {
MachineStates machineStates = machineStateManager.getStates(dockSn);
droneState = machineStates.getDroneState();
log.info("无人机状态(从MachineStateManager): {}", droneState);
} catch (Exception e) {
log.warn("从MachineStateManager获取无人机状态失败: {}", e.getMessage());
}
} else {
log.warn("机场SN为空无法从MachineStateManager获取无人机状态");
}
boolean isPowerOn = (droneState != null && droneState != DroneState.POWER_OFF && droneState != DroneState.UNKNOWN);
2026-02-06 11:16:20 +08:00
log.info("无人机开关机状态: {}", isPowerOn ? "开机" : "关机");
2026-02-06 11:02:45 +08:00
2026-02-11 16:28:03 +08:00
// 从 MachineStateManager 获取舱门状态
CoverState coverState = null;
if (dockSn != null) {
try {
MachineStates machineStates = machineStateManager.getStates(dockSn);
coverState = machineStates.getCoverState();
log.info("机场舱门状态(从MachineStateManager): {}", coverState);
} catch (Exception e) {
log.warn("从MachineStateManager获取舱门状态失败: {}", e.getMessage());
}
} else {
log.warn("机场SN为空无法从MachineStateManager获取舱门状态");
}
2026-02-11 16:42:46 +08:00
// 判断逻辑:开仓且开机才是任务中
2026-02-06 11:16:20 +08:00
String aircraftStatus;
2026-02-11 16:42:46 +08:00
if (coverState == CoverState.OPENED && isPowerOn) {
// 舱门打开且开机,表示正在执行任务
2026-02-06 11:16:20 +08:00
aircraftStatus = "IN_MISSION";
2026-02-11 16:42:46 +08:00
log.info("舱门打开 + 开机 → IN_MISSION");
} else if (coverState == CoverState.OPENED && !isPowerOn) {
// 舱门打开但关机
aircraftStatus = "POWER_OFF_OUT_CABIN";
log.info("舱门打开 + 关机 → POWER_OFF_OUT_CABIN");
2026-02-11 16:28:03 +08:00
} else if (coverState == CoverState.CLOSED) {
// 舱门关闭(舱内),根据开关机状态判断
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");
}
2026-02-06 11:16:20 +08:00
} else {
2026-02-11 16:28:03 +08:00
// 无法获取舱门状态,默认根据开关机状态判断
2026-02-06 11:02:45 +08:00
if (isPowerOn) {
aircraftStatus = "POWER_ON_IN_CABIN";
2026-02-11 16:28:03 +08:00
log.warn("无法获取舱门状态,默认设置: POWER_ON_IN_CABIN");
2026-02-04 16:20:50 +08:00
} else {
2026-02-06 11:02:45 +08:00
aircraftStatus = "POWER_OFF_IN_CABIN";
2026-02-11 16:28:03 +08:00
log.warn("无法获取舱门状态,默认设置: POWER_OFF_IN_CABIN");
2026-02-04 16:20:50 +08:00
}
2026-02-06 11:02:45 +08:00
}
2026-02-04 16:20:50 +08:00
2026-02-06 11:16:20 +08:00
dto.setAircraftStatus(aircraftStatus);
2026-02-06 16:42:08 +08:00
// 设置作业架次(从属性中获取,取不到则默认为 0
log.info("---------- 解析作业架次 ----------");
Integer missionCount = attributes.get(TuohengDeviceAttributes.MISSION_COUNT)
.orElse(0);
log.info("MISSION_COUNT 作业架次: {}", missionCount);
dto.setMissionCount(missionCount);
2026-02-04 16:20:50 +08:00
// 设置GPS信号
2026-02-04 17:17:53 +08:00
log.info("---------- 解析GPS数据 ----------");
2026-02-04 16:20:50 +08:00
telemetry.get(TuohengDeviceTelemetry.SAT_COUNT)
2026-02-04 17:17:53 +08:00
.ifPresent(value -> {
log.info("SAT_COUNT 卫星数量: {}", value.getValue());
dto.setGpsSignal(value.getValue());
});
2026-02-04 16:20:50 +08:00
// 设置电池信息
2026-02-04 17:17:53 +08:00
log.info("---------- 解析电池数据 ----------");
2026-02-04 16:20:50 +08:00
telemetry.get(TuohengDeviceTelemetry.BATTERY_REMAIN)
2026-02-04 17:17:53 +08:00
.ifPresent(value -> {
log.info("BATTERY_REMAIN 剩余电量: {}", value.getValue());
dto.setBatteryLevel(value.getValue());
});
2026-02-04 16:20:50 +08:00
2026-02-06 15:41:40 +08:00
// 优先使用 battery_totalVoltageBMS电池电压如果没有则使用 voltage无人机电压
2026-02-06 16:16:43 +08:00
// 注意电压需要乘以1000转换为毫伏mV
2026-02-06 15:41:40 +08:00
Double voltageValue = telemetry.get(TuohengDeviceTelemetry.BATTERY_TOTAL_VOLTAGE)
.map(TelemetryValue::getValue)
.orElse(null);
if (voltageValue != null) {
2026-02-06 16:16:43 +08:00
log.info("BATTERY_TOTAL_VOLTAGE 总电压: {} V", voltageValue);
dto.setVoltage((int) (voltageValue * 1000)); // 转换为毫伏
log.info("设置电压: {} mV", dto.getVoltage());
2026-02-06 15:41:40 +08:00
} else {
telemetry.get(TuohengDeviceTelemetry.VOLTAGE)
.ifPresent(value -> {
2026-02-06 16:16:43 +08:00
log.info("VOLTAGE 电压(备用): {} V", value.getValue());
2026-02-06 15:41:40 +08:00
Double voltage = value.getValue();
if (voltage != null) {
2026-02-06 16:16:43 +08:00
dto.setVoltage((int) (voltage * 1000)); // 转换为毫伏
log.info("设置电压: {} mV", dto.getVoltage());
2026-02-06 15:41:40 +08:00
}
});
}
telemetry.get(TuohengDeviceTelemetry.BATTERY_CELL_TEMP)
2026-02-04 16:20:50 +08:00
.ifPresent(value -> {
2026-02-06 15:41:40 +08:00
log.info("BATTERY_CELL_TEMP 电池温度: {}", value.getValue());
dto.setBatteryTemperature(value.getValue());
});
telemetry.get(TuohengDeviceTelemetry.BATTERY_NUM_CYCLES)
.ifPresent(value -> {
log.info("BATTERY_NUM_CYCLES 循环次数: {}", value.getValue());
dto.setCycleCount(value.getValue());
2026-02-04 16:20:50 +08:00
});
2026-02-06 16:42:08 +08:00
// 设置飞行时长(从属性中获取,取不到则默认为 null
2026-02-04 17:17:53 +08:00
log.info("---------- 解析飞行数据 ----------");
2026-02-06 16:42:08 +08:00
Integer flightDuration = attributes.get(TuohengDeviceAttributes.FLIGHT_DURATION)
.orElse(null);
if (flightDuration != null) {
log.info("FLIGHT_DURATION 飞行时长: {} 秒", flightDuration);
dto.setFlightDuration(flightDuration);
} else {
log.info("FLIGHT_DURATION 未设置");
}
// 设置最大飞行高度(从属性中获取,取不到则默认为 null
log.info("---------- 解析飞行限制 ----------");
Integer maxAltitude = attributes.get(TuohengDeviceAttributes.MAX_ALTITUDE)
.orElse(null);
if (maxAltitude != null) {
log.info("MAX_ALTITUDE 最大飞行高度: {} 米", maxAltitude);
dto.setMaxAltitude(maxAltitude);
} else {
log.info("MAX_ALTITUDE 未设置");
}
// 设置最大飞行距离(从属性中获取,取不到则默认为 null
Integer maxDistance = attributes.get(TuohengDeviceAttributes.MAX_DISTANCE)
.orElse(null);
if (maxDistance != null) {
log.info("MAX_DISTANCE 最大飞行距离: {} 米", maxDistance);
dto.setMaxDistance(maxDistance);
} else {
log.info("MAX_DISTANCE 未设置");
}
2026-02-04 16:20:50 +08:00
2026-02-04 17:17:53 +08:00
log.info("拓恒无人机详情填充完成: iotDeviceId={}, aircraftStatus={}", iotDeviceId, dto.getAircraftStatus());
log.info("========== 拓恒无人机详情填充结束 ==========");
2026-02-04 16:20:50 +08:00
} catch (Exception e) {
log.error("填充拓恒无人机详情失败: iotDeviceId={}, error={}", iotDeviceId, e.getMessage(), e);
}
}
}