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-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_totalVoltage(BMS电池电压),如果没有则使用 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|