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

679 lines
32 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.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;
@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());
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
// 获取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
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-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-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-02-06 11:16:20 +08:00
// 获取舱门状态(从机场遥测数据中获取)
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");
2026-02-06 10:21:34 +08:00
} else {
2026-02-06 11:16:20 +08:00
if ("auto".equalsIgnoreCase(mode) && (doorStatus == null || doorStatus != 0)) {
log.info("无人机处于auto模式但舱门未打开(doorStatus={}), 设置机场状态为 IDLE", doorStatus);
} else {
log.info("无人机处于{}模式,设置机场状态为 IDLE", mode);
}
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
2026-02-06 14:15:24 +08:00
// 设置舱门状态
log.info("---------- 解析舱门状态 ----------");
telemetry.get(TuohengDeviceTelemetry.NEST_DOOR_STATUS)
.ifPresent(value -> {
Integer doorStatus = value.getValue();
log.info("NEST_DOOR_STATUS 舱门状态原始值: {}", doorStatus);
// 0=打开, 1=关闭
String cabinDoorStatus = (doorStatus != null && doorStatus == 0) ? "OPEN" : "CLOSED";
dto.setCabinDoorStatus(cabinDoorStatus);
log.info("设置舱门状态: {}", cabinDoorStatus);
});
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-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);
2026-02-06 11:09:23 +08:00
// 获取 tsingal图传信号强度用于判断开关机
Integer tsingal = aircraftTelemetry.get(TuohengDeviceTelemetry.TSINGAL)
.map(TelemetryValue::getValue)
.orElse(0);
log.info("无人机 TSINGAL 值: {}", tsingal);
2026-02-06 11:02:45 +08:00
2026-02-06 11:09:23 +08:00
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)
2026-02-06 11:02:45 +08:00
.map(TelemetryValue::getValue)
2026-02-06 11:09:23 +08:00
.orElse(null);
log.info("机场舱门状态: {}", doorStatus);
}
2026-02-04 16:20:50 +08:00
2026-02-06 11:09:23 +08:00
// 判断逻辑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 判断
2026-02-06 11:02:45 +08:00
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) {
2026-02-06 11:09:23 +08:00
// 舱门打开(舱外),但不是 auto 模式
2026-02-06 11:02:45 +08:00
if (isPowerOn) {
aircraftStatus = "POWER_ON_OUT_CABIN";
2026-02-06 11:09:23 +08:00
log.info("舱门打开 + 开机(非auto模式) → POWER_ON_OUT_CABIN");
2026-02-06 11:02:45 +08:00
} else {
aircraftStatus = "POWER_OFF_OUT_CABIN";
log.info("舱门打开 + 关机 → POWER_OFF_OUT_CABIN");
}
2026-02-04 16:20:50 +08:00
} else {
2026-02-06 11:02:45 +08:00
// 无法获取舱门状态,默认根据开关机状态判断
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");
}
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
*/
private void fillTuohengAircraftDetail(AircraftDetailDTO dto, String iotDeviceId) {
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-06 11:16:20 +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
2026-02-06 11:16:20 +08:00
boolean isPowerOn = tsingal > 60; // tsingal > 60 表示开机
log.info("无人机开关机状态: {}", isPowerOn ? "开机" : "关机");
2026-02-06 11:02:45 +08:00
2026-02-06 11:16:20 +08:00
// 判断逻辑
// 注意:无人机详情接口无法获取机场舱门状态
// 如果 mode == "auto" 且开机,推测为 IN_MISSION
// 否则根据开关机状态判断
String aircraftStatus;
if ("auto".equalsIgnoreCase(mode) && isPowerOn) {
// mode == "auto" 且开机,推测正在执行任务
aircraftStatus = "IN_MISSION";
log.info("无人机处于 auto 模式且开机,推测状态: IN_MISSION");
} else {
// 其他情况,根据开关机状态判断(默认舱内)
2026-02-06 11:02:45 +08:00
if (isPowerOn) {
aircraftStatus = "POWER_ON_IN_CABIN";
log.info("开机状态,默认设置: 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";
log.info("关机状态,默认设置: 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-04 16:20:50 +08:00
// 设置作业架次 - 暂时设置为0
dto.setMissionCount(0);
2026-02-04 17:17:53 +08:00
log.info("设置作业架次: 0");
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-04 17:17:53 +08:00
log.info("---------- 解析飞行数据 ----------");
2026-02-04 16:20:50 +08:00
telemetry.get(TuohengDeviceTelemetry.FLIGHT_TIME)
2026-02-04 17:17:53 +08:00
.ifPresent(value -> {
log.info("FLIGHT_TIME 飞行时长(秒): {}", value.getValue());
dto.setFlightDuration(value.getValue());
});
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);
}
}
}