package com.ruoyi.device.service.impl; import com.alibaba.fastjson2.JSON; import com.ruoyi.device.api.domain.PayloadVO; import com.ruoyi.device.api.enums.PayloadStatusEnum; 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.constants.DeviceAttributes; import com.ruoyi.device.domain.model.thingsboard.constants.DeviceTelemetry; import com.ruoyi.device.service.api.IBufferDeviceService; import com.ruoyi.device.service.dto.AircraftDTO; import com.ruoyi.device.service.dto.AircraftDetailDTO; import com.ruoyi.device.service.dto.DockDetailDTO; import com.ruoyi.device.api.enums.AircraftStatusEnum; import com.ruoyi.device.api.enums.DockStatusEnum; 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.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * 设备缓冲服务实现 * 整合数据库数据和ThingsBoard数据 * * @author ruoyi * @date 2026-01-20 */ @Service @Slf4j public class BufferDeviceImpl implements IBufferDeviceService { @Autowired private IDockDomain dockDomain; @Autowired private IDeviceDomain deviceDomain; @Autowired private IAircraftDomain aircraftDomain; @Autowired private IAircraftPayloadDomain aircraftPayloadDomain; @Autowired private IDockAircraftDomain dockAircraftDomain; @Autowired private IThingsBoardDomain thingsBoardDomain; @Autowired private IPayloadDomain payloadDomain; @Override public DockDetailDTO getDockDetailById(Long dockId) { Dock dock = dockDomain.selectDockByDockId(dockId); log.info("selectDockByDockId {}",JSON.toJSONString(dock)); if (dock == null) { return null; } Device device = deviceDomain.selectDeviceByDeviceId(dock.getDeviceId()); log.info("selectDeviceByDeviceId {}",JSON.toJSONString(device)); DockDetailDTO dto = getChangeAbleDockDetailDTO(device.getIotDeviceId()); dto.setDockId(dock.getDockId()); dto.setDockName(dock.getDockName()); dto.setDockLocation(dock.getDockLocation()); dto.setDockIotId(device.getIotDeviceId()); List aircrafts = dockAircraftDomain.selectDockAircraftByDockId(dockId); if(!CollectionUtils.isEmpty(aircrafts)){ DockAircraft dockAircraft = aircrafts.get(0); Aircraft aircraft = aircraftDomain.selectAircraftByAircraftId(dockAircraft.getAircraftId()); dto.setAircraftId(aircraft.getAircraftId()); dto.setAircraftName(aircraft.getAircraftName()); Device airDevice = deviceDomain.selectDeviceByDeviceId(aircraft.getDeviceId()); dto.setAircraftIotId(airDevice.getIotDeviceId()); List aircraftPayloads = aircraftPayloadDomain.selectAircraftPayloadByAircraftId(aircraft.getAircraftId()); if(!CollectionUtils.isEmpty(aircraftPayloads)){ List payloads = new ArrayList<>(aircraftPayloads.size()); for(AircraftPayload aircraftPayload : aircraftPayloads){ Payload payload = payloadDomain.selectPayloadByPayloadId(aircraftPayload.getPayloadId()); PayloadVO payloadVO = new PayloadVO(); payloadVO.setPayloadId(aircraftPayload.getPayloadId()); payloadVO.setPayloadName(payload.getPayloadName()); payloadVO.setPayloadType(payload.getPayloadType()); payloadVO.setPayloadStatus(PayloadStatusEnum.ONLINE.getCode()); payloads.add(payloadVO); } dto.setPayloadList(payloads); } AircraftDetailDTO aircraftDetailDTO = getChangeAbleAirDetailDTO(airDevice.getIotDeviceId()); /** * 设置无人机状态 */ dto.setAircraftStatus(aircraftDetailDTO.getAircraftStatus()); } return dto; } @Override public AircraftDetailDTO getAircraftDetailById(Long aircraftId) { return null; } private DockDetailDTO getChangeAbleDockDetailDTO(String deviceIotId) { DockDetailDTO dto = new DockDetailDTO(); AttributeMap attributeMap = thingsBoardDomain.getPredefinedDeviceAttributes(deviceIotId); TelemetryMap telemetryMap = thingsBoardDomain.getPredefinedDeviceTelemetry(deviceIotId); // 根据 MODE_CODE 设置机场状态 // MODE_CODE 定义: {"0":"空闲中","1":"现场调试","2":"远程调试","3":"固件升级中","4":"作业中","5":"待标定"} attributeMap.get(DeviceAttributes.MODE_CODE).ifPresent(modeCode -> { String dockStatus = mapModeCodeToDockStatus(modeCode); dto.setDockStatus(dockStatus); }); return dto; } /** * 将 MODE_CODE 映射到机场状态 * @param modeCode 模式代码 * @return 机场状态字符串 */ private String mapModeCodeToDockStatus(Integer modeCode) { switch (modeCode) { case 0: return DockStatusEnum.IDLE.getCode(); case 1: case 2: return DockStatusEnum.Debugging.getCode(); case 3: return DockStatusEnum.FIRMWARE_UPGRADING.getCode(); case 4: return DockStatusEnum.WORKING.getCode(); case 5: default: return DockStatusEnum.UNKNOWN.getCode(); } } private AircraftDetailDTO getChangeAbleAirDetailDTO(String deviceIotId) { AircraftDetailDTO dto = new AircraftDetailDTO(); AttributeMap attributeMap = thingsBoardDomain.getPredefinedDeviceAttributes(deviceIotId); TelemetryMap telemetryMap = thingsBoardDomain.getPredefinedDeviceTelemetry(deviceIotId); //设置aircraftStatus // drone_in_dock 表示是否在舱外 // device_online_status 表示是否开机 // 先判断机场的状态是否是 // 任务中 和 调试, 这两个状态用机场的, 暂时不要用离线状态 // String aircraftStatus = determineAircraftStatus(attributeMap, telemetryMap); dto.setAircraftStatus(aircraftStatus); return dto; } /** * 根据设备属性和遥测数据确定无人机状态 * * @param attributeMap 设备属性映射 * @param telemetryMap 设备遥测数据映射 * @return 无人机状态字符串 */ private String determineAircraftStatus(AttributeMap attributeMap, TelemetryMap telemetryMap) { // 先检查 MODE_CODE,判断是否处于任务中或调试状态 // MODE_CODE 定义: {"0":"待机","1":"起飞准备","2":"起飞准备完毕","3":"手动飞行","4":"自动起飞", // "5":"航线飞行","6":"全景拍照","7":"智能跟随","8":"ADS-B 躲避","9":"自动返航", // "10":"自动降落","11":"强制降落","12":"三桨叶降落","13":"升级中","14":"未连接", // "15":"APAS","16":"虚拟摇杆状态","17":"指令飞行","18":"空中 RTK 收敛模式", // "19":"机场选址中","20":"POI环绕"} Integer modeCode = attributeMap.get(DeviceAttributes.MODE_CODE).orElse(null); if (modeCode != null) { // 14 = 未连接,直接返回离线状态 if (modeCode == 14) { return AircraftStatusEnum.OFFLINE.getCode(); } // 判断是否在任务中 (飞行相关状态: 1-12, 15-20) if (modeCode >= 1 && modeCode <= 12 || modeCode >= 15 && modeCode <= 20) { return AircraftStatusEnum.IN_MISSION.getCode(); } } // 获取无人机是否在舱内: {"0":"舱外","1":"舱内"} Integer droneInDock = telemetryMap.get(DeviceTelemetry.Drone_In_Dock) .map(telemetryValue -> telemetryValue.getValue()) .orElse(null); // 获取无人机是否开机: {"0":"关机","1":"开机"} Integer deviceOnlineStatus = telemetryMap.get(DeviceTelemetry.Sub_Device_Online_Status) .map(telemetryValue -> telemetryValue.getValue()) .orElse(null); // 根据舱内/舱外和开机/关机状态确定无人机状态 boolean isInCabin = droneInDock != null && droneInDock == 1; boolean isPowerOn = deviceOnlineStatus != null && deviceOnlineStatus == 1; if (isInCabin) { // 舱内 if (isPowerOn) { return AircraftStatusEnum.POWER_ON_IN_CABIN.getCode(); } else { return AircraftStatusEnum.POWER_OFF_IN_CABIN.getCode(); } } else { // 舱外 if (isPowerOn) { return AircraftStatusEnum.POWER_ON_OUT_CABIN.getCode(); } else { return AircraftStatusEnum.POWER_OFF_OUT_CABIN.getCode(); } } } }