738 lines
35 KiB
Java
738 lines
35 KiB
Java
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 {
|
||
|
||
/**
|
||
* 默认经纬度 - 南京市
|
||
*/
|
||
private static final Double DEFAULT_LONGITUDE = 118.796877;
|
||
private static final Double DEFAULT_LATITUDE = 32.060255;
|
||
|
||
@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);
|
||
});
|
||
|
||
// 设置机场位置(从属性中获取 home.longitude 和 home.latitude,取不到则使用南京市默认值)
|
||
log.info("---------- 解析机场位置数据 ----------");
|
||
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);
|
||
}
|
||
|
||
// 设置运行数据(从属性中获取 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);
|
||
|
||
// 设置在线状态 - 基于心跳时间戳判断离线,基于无人机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_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);
|
||
});
|
||
|
||
// 设置空调状态(从属性中获取 airConditionerStatus,取不到则默认为 IDLE)
|
||
log.info("---------- 解析空调状态 ----------");
|
||
String airConditionerStatus = attributes.get(TuohengDeviceAttributes.AIR_CONDITIONER_STATUS)
|
||
.orElse("IDLE");
|
||
log.info("AIR_CONDITIONER_STATUS 空调状态: {}", airConditionerStatus);
|
||
dto.setAirConditionerStatus(airConditionerStatus);
|
||
|
||
// 设置环境数据
|
||
log.info("---------- 解析气象数据 ----------");
|
||
// 环境温度和湿度从属性中获取(手动维护),取不到则为 null
|
||
attributes.get(TuohengDeviceAttributes.ENVIRONMENT_TEMPERATURE)
|
||
.ifPresent(value -> {
|
||
log.info("ENVIRONMENT_TEMPERATURE 环境温度: {}", value);
|
||
dto.setEnvironmentTemperature(value);
|
||
});
|
||
attributes.get(TuohengDeviceAttributes.ENVIRONMENT_HUMIDITY)
|
||
.ifPresent(value -> {
|
||
log.info("ENVIRONMENT_HUMIDITY 环境湿度: {}", value);
|
||
dto.setEnvironmentHumidity(value);
|
||
});
|
||
|
||
// 风速和降雨量从遥测数据中获取
|
||
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("---------- 解析电池数据 ----------");
|
||
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());
|
||
});
|
||
}
|
||
|
||
// 设置充电状态
|
||
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);
|
||
});
|
||
|
||
// 设置机场设备自检数据(升降架、X轴夹器、Y轴夹器)
|
||
log.info("---------- 解析机场设备自检数据 ----------");
|
||
telemetry.get(TuohengDeviceTelemetry.LIFTER_STATUS)
|
||
.ifPresent(value -> {
|
||
Integer lifterStatus = value.getValue();
|
||
log.info("LIFTER_STATUS 升降架状态原始值: {}", lifterStatus);
|
||
// 0=正常, 非0=异常
|
||
String elevatorPosition = (lifterStatus != null && lifterStatus == 0) ? "NORMAL" : "ABNORMAL";
|
||
dto.setElevatorPosition(elevatorPosition);
|
||
log.info("设置升降架位置: {}", elevatorPosition);
|
||
});
|
||
|
||
telemetry.get(TuohengDeviceTelemetry.HOLDER_X_STATUS)
|
||
.ifPresent(value -> {
|
||
Integer holderXStatus = value.getValue();
|
||
log.info("HOLDER_X_STATUS X轴夹器状态原始值: {}", holderXStatus);
|
||
// 0=正常, 非0=异常
|
||
String xAxisClampStatus = (holderXStatus != null && holderXStatus == 0) ? "NORMAL" : "ABNORMAL";
|
||
dto.setXAxisClampStatus(xAxisClampStatus);
|
||
log.info("设置X轴夹器状态: {}", xAxisClampStatus);
|
||
});
|
||
|
||
telemetry.get(TuohengDeviceTelemetry.HOLDER_Y_STATUS)
|
||
.ifPresent(value -> {
|
||
Integer holderYStatus = value.getValue();
|
||
log.info("HOLDER_Y_STATUS Y轴夹器状态原始值: {}", holderYStatus);
|
||
// 0=正常, 非0=异常
|
||
String yAxisClampStatus = (holderYStatus != null && holderYStatus == 0) ? "NORMAL" : "ABNORMAL";
|
||
dto.setYAxisClampStatus(yAxisClampStatus);
|
||
log.info("设置Y轴夹器状态: {}", yAxisClampStatus);
|
||
});
|
||
|
||
// 填充无人机状态信息
|
||
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);
|
||
|
||
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)
|
||
log.info("---------- 解析作业架次 ----------");
|
||
Integer missionCount = attributes.get(TuohengDeviceAttributes.MISSION_COUNT)
|
||
.orElse(0);
|
||
log.info("MISSION_COUNT 作业架次: {}", missionCount);
|
||
dto.setMissionCount(missionCount);
|
||
|
||
// 设置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());
|
||
});
|
||
|
||
// 优先使用 battery_totalVoltage(BMS电池电压),如果没有则使用 voltage(无人机电压)
|
||
// 注意:电压需要乘以1000转换为毫伏(mV)
|
||
Double voltageValue = telemetry.get(TuohengDeviceTelemetry.BATTERY_TOTAL_VOLTAGE)
|
||
.map(TelemetryValue::getValue)
|
||
.orElse(null);
|
||
|
||
if (voltageValue != null) {
|
||
log.info("BATTERY_TOTAL_VOLTAGE 总电压: {} V", voltageValue);
|
||
dto.setVoltage((int) (voltageValue * 1000)); // 转换为毫伏
|
||
log.info("设置电压: {} mV", dto.getVoltage());
|
||
} else {
|
||
telemetry.get(TuohengDeviceTelemetry.VOLTAGE)
|
||
.ifPresent(value -> {
|
||
log.info("VOLTAGE 电压(备用): {} V", value.getValue());
|
||
Double voltage = value.getValue();
|
||
if (voltage != null) {
|
||
dto.setVoltage((int) (voltage * 1000)); // 转换为毫伏
|
||
log.info("设置电压: {} mV", dto.getVoltage());
|
||
}
|
||
});
|
||
}
|
||
|
||
telemetry.get(TuohengDeviceTelemetry.BATTERY_CELL_TEMP)
|
||
.ifPresent(value -> {
|
||
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());
|
||
});
|
||
|
||
// 设置飞行时长(从属性中获取,取不到则默认为 null)
|
||
log.info("---------- 解析飞行数据 ----------");
|
||
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 未设置");
|
||
}
|
||
|
||
log.info("拓恒无人机详情填充完成: iotDeviceId={}, aircraftStatus={}", iotDeviceId, dto.getAircraftStatus());
|
||
log.info("========== 拓恒无人机详情填充结束 ==========");
|
||
} catch (Exception e) {
|
||
log.error("填充拓恒无人机详情失败: iotDeviceId={}, error={}", iotDeviceId, e.getMessage(), e);
|
||
}
|
||
}
|
||
}
|