添加拓恒机场和无人机状态
This commit is contained in:
parent
7a7af0a496
commit
20f11d581b
|
|
@ -8,22 +8,32 @@ import com.ruoyi.device.api.domain.DroneRealtimeInfoVO;
|
|||
import com.ruoyi.device.api.domain.DroneTakeoffResponseVO;
|
||||
import com.ruoyi.device.api.enums.DroneCurrentStatusEnum;
|
||||
import com.ruoyi.device.api.enums.DroneMissionStatusEnum;
|
||||
import com.ruoyi.device.domain.impl.machine.MachineCommandManager;
|
||||
import com.ruoyi.device.domain.impl.machine.command.CommandResult;
|
||||
import com.ruoyi.device.domain.impl.machine.command.CommandType;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* 无人机飞控Controller
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-04
|
||||
*/
|
||||
@Slf4j
|
||||
@Tag(name = "无人机飞控管理", description = "无人机飞控相关接口")
|
||||
@RestController
|
||||
@RequestMapping("/drone")
|
||||
public class AircraftFlyController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private MachineCommandManager machineCommandManager;
|
||||
/**
|
||||
* 无人机飞控命令
|
||||
*
|
||||
|
|
@ -101,4 +111,38 @@ public class AircraftFlyController extends BaseController
|
|||
vo.setMissionStatus(DroneMissionStatusEnum.TAKING_OFF);
|
||||
return R.ok(vo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 无人机开机接口
|
||||
*
|
||||
* @param sn 机场SN号
|
||||
* @return 开机响应
|
||||
*/
|
||||
@Operation(summary = "无人机开机", description = "控制指定机场的无人机执行开机操作")
|
||||
@PostMapping("/power-on/{sn}")
|
||||
public R<String> powerOn(
|
||||
@Parameter(description = "机场SN号", required = true, example = "THJSQ03B2309DN7VQN43")
|
||||
@PathVariable("sn") String sn)
|
||||
{
|
||||
log.info("收到无人机开机请求: sn={}", sn);
|
||||
|
||||
try {
|
||||
// 调用机器命令管理器执行开机命令
|
||||
CompletableFuture<CommandResult> future = machineCommandManager.executeCommand(sn, CommandType.POWER_ON);
|
||||
|
||||
// 等待命令执行完成
|
||||
CommandResult result = future.get();
|
||||
|
||||
if (result.isSuccess()) {
|
||||
log.info("无人机开机成功: sn={}", sn);
|
||||
return R.ok("开机命令执行成功");
|
||||
} else {
|
||||
log.error("无人机开机失败: sn={}, reason={}", sn, result.getErrorMessage());
|
||||
return R.fail("开机命令执行失败: " + result.getErrorMessage());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("无人机开机异常: sn={}", sn, e);
|
||||
return R.fail("开机命令执行异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.ruoyi.device.domain.impl.machine.config;
|
|||
|
||||
import com.ruoyi.device.domain.impl.machine.vendor.VendorRegistry;
|
||||
import com.ruoyi.device.domain.impl.machine.vendor.dji.DjiVendorConfig;
|
||||
import com.ruoyi.device.domain.impl.machine.vendor.tuoheng.TuohengVendorConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
|
@ -19,10 +20,16 @@ public class MachineFrameworkConfig {
|
|||
* 自动注册所有厂家配置
|
||||
*/
|
||||
@Bean
|
||||
public CommandLineRunner registerVendors(VendorRegistry vendorRegistry, DjiVendorConfig djiVendorConfig) {
|
||||
public CommandLineRunner registerVendors(VendorRegistry vendorRegistry,
|
||||
DjiVendorConfig djiVendorConfig,
|
||||
TuohengVendorConfig tuohengVendorConfig) {
|
||||
return args -> {
|
||||
// 注册大疆厂家配置
|
||||
vendorRegistry.registerVendor(djiVendorConfig);
|
||||
|
||||
// 注册拓恒厂家配置
|
||||
vendorRegistry.registerVendor(tuohengVendorConfig);
|
||||
|
||||
log.info("设备框架初始化完成,已注册厂家: {}", vendorRegistry.getAllVendorTypes());
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,17 @@ package com.ruoyi.device.domain.impl.machine.state;
|
|||
*/
|
||||
public enum AirportState {
|
||||
/**
|
||||
* 未知状态(服务器重启后的初始状态,等待第一次心跳同步),同时也是离线状态
|
||||
* 未知状态(服务器重启后的初始状态,等待第一次心跳同步)
|
||||
*/
|
||||
UNKNOWN,
|
||||
|
||||
/**
|
||||
* 在线
|
||||
*/
|
||||
ONLINE
|
||||
ONLINE,
|
||||
|
||||
/**
|
||||
* 离线(心跳超时)
|
||||
*/
|
||||
OFFLINE
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,17 @@ package com.ruoyi.device.domain.impl.machine.state;
|
|||
*/
|
||||
public enum DroneState {
|
||||
/**
|
||||
* 未知状态(服务器重启后的初始状态,等待第一次心跳同步),同时也是离线状态
|
||||
* 未知状态(服务器重启后的初始状态,等待第一次心跳同步)
|
||||
*/
|
||||
UNKNOWN,
|
||||
|
||||
/**
|
||||
* 在线
|
||||
* 关机状态
|
||||
*/
|
||||
POWER_OFF,
|
||||
|
||||
/**
|
||||
* 在线(已开机)
|
||||
*/
|
||||
ONLINE,
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,25 @@ public class InMemorySnVendorMappingStore implements SnVendorMappingStore {
|
|||
|
||||
@Override
|
||||
public String getVendorType(String sn) {
|
||||
// 先从缓存中获取
|
||||
String vendorType = snToVendorMap.get(sn);
|
||||
log.debug("从内存获取 SN 映射: sn={}, vendorType={}", sn, vendorType);
|
||||
|
||||
if (vendorType != null) {
|
||||
log.debug("从内存缓存获取 SN 映射: sn={}, vendorType={}", sn, vendorType);
|
||||
return vendorType;
|
||||
}
|
||||
|
||||
// 根据 SN 前缀自动判断厂商类型
|
||||
vendorType = detectVendorTypeBySn(sn);
|
||||
|
||||
if (vendorType != null) {
|
||||
// 缓存判断结果
|
||||
snToVendorMap.put(sn, vendorType);
|
||||
log.debug("根据 SN 前缀自动识别厂商: sn={}, vendorType={}", sn, vendorType);
|
||||
} else {
|
||||
log.warn("无法识别 SN 对应的厂商类型: sn={}", sn);
|
||||
}
|
||||
|
||||
return vendorType;
|
||||
}
|
||||
|
||||
|
|
@ -44,4 +61,24 @@ public class InMemorySnVendorMappingStore implements SnVendorMappingStore {
|
|||
public boolean exists(String sn) {
|
||||
return snToVendorMap.containsKey(sn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 SN 前缀自动识别厂商类型
|
||||
*
|
||||
* @param sn 设备SN号
|
||||
* @return 厂商类型,无法识别返回 null
|
||||
*/
|
||||
private String detectVendorTypeBySn(String sn) {
|
||||
if (sn == null || sn.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 拓恒设备:SN 以 "TH" 开头
|
||||
if (sn.startsWith("TH")) {
|
||||
return "TUOHENG";
|
||||
}
|
||||
|
||||
// 大疆设备:其他情况默认为大疆
|
||||
return "DJI";
|
||||
}
|
||||
}
|
||||
95
src/main/java/com/ruoyi/device/domain/impl/machine/vendor/tuoheng/TuohengVendorConfig.java
vendored
Normal file
95
src/main/java/com/ruoyi/device/domain/impl/machine/vendor/tuoheng/TuohengVendorConfig.java
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
package com.ruoyi.device.domain.impl.machine.vendor.tuoheng;
|
||||
|
||||
import com.ruoyi.device.domain.impl.machine.command.CommandType;
|
||||
import com.ruoyi.device.domain.impl.machine.command.Transaction;
|
||||
import com.ruoyi.device.domain.impl.machine.state.*;
|
||||
import com.ruoyi.device.domain.impl.machine.vendor.VendorConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 拓恒无人机厂家配置
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TuohengVendorConfig implements VendorConfig {
|
||||
|
||||
private final Map<CommandType, Transaction> transactionMap = new HashMap<>();
|
||||
|
||||
public TuohengVendorConfig() {
|
||||
initTransactions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVendorType() {
|
||||
return "TUOHENG";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVendorName() {
|
||||
return "拓恒";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transaction getTransaction(CommandType commandType) {
|
||||
return transactionMap.get(commandType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canExecuteCommand(MachineStates currentStates, CommandType commandType) {
|
||||
DroneState droneState = currentStates.getDroneState();
|
||||
AirportState airportState = currentStates.getAirportState();
|
||||
DebugModeState debugModeState = currentStates.getDebugModeState();
|
||||
|
||||
switch (commandType) {
|
||||
case POWER_ON:
|
||||
// 开机前置条件:机场在线、无人机关机
|
||||
// 注:拓恒无人机没有调试模式概念
|
||||
return airportState == AirportState.ONLINE
|
||||
&& droneState == DroneState.POWER_OFF;
|
||||
|
||||
case TAKE_OFF:
|
||||
// 起飞前置条件:无人机已开机、机场在线
|
||||
return droneState == DroneState.ONLINE
|
||||
&& airportState == AirportState.ONLINE;
|
||||
|
||||
case RETURN_HOME:
|
||||
// 返航前置条件:无人机飞行中
|
||||
return droneState == DroneState.FLYING
|
||||
|| droneState == DroneState.ARRIVED;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommandType> getAvailableCommands(MachineStates currentStates) {
|
||||
List<CommandType> availableCommands = new ArrayList<>();
|
||||
|
||||
for (CommandType commandType : CommandType.values()) {
|
||||
if (canExecuteCommand(currentStates, commandType)) {
|
||||
availableCommands.add(commandType);
|
||||
}
|
||||
}
|
||||
return availableCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化事务定义
|
||||
*/
|
||||
private void initTransactions() {
|
||||
// 开机命令
|
||||
Transaction powerOnTransaction = new Transaction("开机", CommandType.POWER_ON)
|
||||
.root(new com.ruoyi.device.domain.impl.machine.vendor.tuoheng.instruction.TuohengPowerOnInstruction())
|
||||
.setTimeout(60000);
|
||||
transactionMap.put(powerOnTransaction.getCommandType(), powerOnTransaction);
|
||||
|
||||
log.info("拓恒厂家配置初始化完成,共配置{}个命令", transactionMap.size());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package com.ruoyi.device.domain.impl.machine.vendor.tuoheng.instruction;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.ruoyi.device.domain.impl.machine.instruction.AbstractInstruction;
|
||||
import com.ruoyi.device.domain.impl.machine.instruction.CallbackConfig;
|
||||
import com.ruoyi.device.domain.impl.machine.instruction.InstructionContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 拓恒无人机开机指令
|
||||
*/
|
||||
@Slf4j
|
||||
public class TuohengPowerOnInstruction extends AbstractInstruction {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "TUOHENG_POWER_ON";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeRemoteCall(InstructionContext context) throws Exception {
|
||||
String sn = context.getSn();
|
||||
log.info("发送拓恒无人机开机指令: sn={}", sn);
|
||||
|
||||
// 构建MQTT消息
|
||||
JSONObject payload = new JSONObject();
|
||||
payload.put("messageID", System.currentTimeMillis());
|
||||
payload.put("timestamp", System.currentTimeMillis());
|
||||
payload.put("code", "DronePower");
|
||||
payload.put("value", "1"); // 1=开机, 0=关机
|
||||
payload.put("channel", 1);
|
||||
|
||||
String topic = "/topic/v1/airportControl/" + sn;
|
||||
|
||||
context.getMqttClient().sendMessage(topic, payload.toJSONString());
|
||||
log.info("拓恒开机指令发送成功: topic={}, payload={}", topic, payload.toJSONString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallbackConfig getMethodCallbackConfig(InstructionContext context) {
|
||||
String sn = context.getSn();
|
||||
|
||||
// 监听机场确认消息
|
||||
return CallbackConfig.builder()
|
||||
.topic("/topic/v1/airportNest/" + sn + "/confirm")
|
||||
.fieldPath("code")
|
||||
.expectedValue("DronePower")
|
||||
.timeoutMs(10000)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallbackConfig getStateCallbackConfig(InstructionContext context) {
|
||||
String sn = context.getSn();
|
||||
|
||||
// 监听无人机开机状态变化
|
||||
return CallbackConfig.builder()
|
||||
.topic("/topic/v1/airportNest/" + sn + "/realTime/data")
|
||||
.fieldPath("droneBattery.bPowerON")
|
||||
.expectedValue("2") // 2表示已开机
|
||||
.timeoutMs(60000)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeoutMs() {
|
||||
return 60000; // 总超时时间60秒
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|||
* @author ruoyi
|
||||
*/
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class AirportOsdData {
|
||||
|
||||
@JsonProperty("working_current")
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ import com.ruoyi.device.domain.api.IDockAircraftDomain;
|
|||
import com.ruoyi.device.domain.api.IDockDomain;
|
||||
import com.ruoyi.device.domain.api.IAircraftDomain;
|
||||
import com.ruoyi.device.domain.api.IDeviceDomain;
|
||||
import com.ruoyi.device.domain.impl.machine.state.DroneState;
|
||||
import com.ruoyi.device.domain.impl.machine.state.AirportState;
|
||||
import com.ruoyi.device.domain.impl.machine.state.MachineStates;
|
||||
import com.ruoyi.device.domain.impl.machine.statemachine.MachineStateManager;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.ITuohengEventsCallback;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.ITuohengOsdCallback;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.ITuohengRealTimeDataCallback;
|
||||
|
|
@ -23,6 +27,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
|
@ -51,8 +56,21 @@ public class TuohengService {
|
|||
@Autowired
|
||||
private IDeviceDomain deviceDomain;
|
||||
|
||||
@Autowired
|
||||
private MachineStateManager stateManager;
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 机场心跳时间戳记录 (deviceSn -> lastHeartbeatTime)
|
||||
*/
|
||||
private final Map<String, Long> airportHeartbeatMap = new java.util.concurrent.ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 心跳超时时间:5分钟
|
||||
*/
|
||||
private static final long HEARTBEAT_TIMEOUT = 5 * 60 * 1000;
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void onApplicationReady() {
|
||||
TuohengMqttClientConfig config = TuohengMqttClientConfig.builder()
|
||||
|
|
@ -82,8 +100,15 @@ public class TuohengService {
|
|||
log.info("设备SN: {}", deviceSn);
|
||||
try {
|
||||
log.info("数据内容: {}", objectMapper.writeValueAsString(data));
|
||||
|
||||
// 更新机场心跳时间戳
|
||||
updateAirportHeartbeat(deviceSn);
|
||||
|
||||
// 同步无人机开关机状态
|
||||
syncDronePowerState(deviceSn, data);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("序列化数据失败", e);
|
||||
log.error("处理实时数据失败", e);
|
||||
}
|
||||
log.info("=====================================");
|
||||
}
|
||||
|
|
@ -96,6 +121,10 @@ public class TuohengService {
|
|||
log.info("设备SN: {}", deviceSn);
|
||||
try {
|
||||
log.info("数据内容: {}", objectMapper.writeValueAsString(data));
|
||||
|
||||
// 同步飞行状态到 MachineStateManager
|
||||
syncFlightState(deviceSn, data);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("序列化数据失败", e);
|
||||
}
|
||||
|
|
@ -157,4 +186,129 @@ public class TuohengService {
|
|||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 同步飞行状态到 MachineStateManager
|
||||
* 根据 OSD 数据中的 flighttask_step_code 和 mode_code 判断飞行状态
|
||||
*/
|
||||
private void syncFlightState(String deviceSn, AirportOsdData data) {
|
||||
try {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String flighttaskStepCode = data.getFlighttaskStepCode();
|
||||
String modeCode = data.getModeCode();
|
||||
|
||||
// 同步无人机状态
|
||||
DroneState droneState = determineDroneState(flighttaskStepCode, modeCode);
|
||||
if (droneState != null) {
|
||||
stateManager.setDroneState(deviceSn, droneState);
|
||||
log.debug("同步飞行状态: sn={}, flighttaskStepCode={}, modeCode={}, state={}",
|
||||
deviceSn, flighttaskStepCode, modeCode, droneState);
|
||||
}
|
||||
|
||||
// 注意:机场在线状态由 IOT 平台的心跳机制判断(5分钟超时)
|
||||
// 不在这里简单地根据收到数据就判断为在线
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("同步飞行状态失败: sn={}", deviceSn, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务状态码和模式码判断无人机状态
|
||||
*/
|
||||
private DroneState determineDroneState(String flighttaskStepCode, String modeCode) {
|
||||
// 优先根据 flighttask_step_code 判断
|
||||
if (flighttaskStepCode != null) {
|
||||
switch (flighttaskStepCode) {
|
||||
case "1":
|
||||
// 飞行作业中
|
||||
return DroneState.FLYING;
|
||||
case "2":
|
||||
// 作业后状态恢复,可能是返航或已到达
|
||||
return DroneState.RETURNING;
|
||||
case "5":
|
||||
// 任务空闲
|
||||
return DroneState.ONLINE;
|
||||
case "255":
|
||||
// 飞行器异常
|
||||
return DroneState.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据 mode_code 辅助判断
|
||||
if (modeCode != null) {
|
||||
if (modeCode.equals("3") || modeCode.equals("4") || modeCode.equals("5")) {
|
||||
// 飞行中状态
|
||||
return DroneState.FLYING;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新机场心跳时间戳并设置在线状态
|
||||
*/
|
||||
private void updateAirportHeartbeat(String deviceSn) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
airportHeartbeatMap.put(deviceSn, currentTime);
|
||||
|
||||
// 收到心跳,设置机场为在线
|
||||
stateManager.setAirportState(deviceSn, AirportState.ONLINE);
|
||||
log.debug("更新机场心跳: sn={}, time={}", deviceSn, currentTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步无人机开关机状态
|
||||
* 注意:只有关机时才更新状态,其他情况保持当前状态不变
|
||||
*/
|
||||
private void syncDronePowerState(String deviceSn, TuohengRealTimeData data) {
|
||||
try {
|
||||
if (data == null || data.getDroneBattery() == null || data.getDroneBattery().getData() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Integer powerOn = data.getDroneBattery().getData().getBPowerON();
|
||||
if (powerOn == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 只有关机时才更新状态为 POWER_OFF
|
||||
// 其他情况(开机、飞行中等)保持当前状态不变
|
||||
if (powerOn == 2) {
|
||||
stateManager.setDroneState(deviceSn, DroneState.POWER_OFF);
|
||||
log.debug("同步无人机关机状态: sn={}, powerOn={}", deviceSn, powerOn);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("同步无人机开关机状态失败: sn={}", deviceSn, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时检查机场心跳超时
|
||||
* 每分钟执行一次
|
||||
*/
|
||||
@Scheduled(fixedRate = 60000)
|
||||
public void checkAirportHeartbeatTimeout() {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
|
||||
for (Map.Entry<String, Long> entry : airportHeartbeatMap.entrySet()) {
|
||||
String deviceSn = entry.getKey();
|
||||
Long lastHeartbeatTime = entry.getValue();
|
||||
|
||||
long timeDiff = currentTime - lastHeartbeatTime;
|
||||
|
||||
if (timeDiff > HEARTBEAT_TIMEOUT) {
|
||||
// 超时,设置为离线
|
||||
stateManager.setAirportState(deviceSn, AirportState.OFFLINE);
|
||||
log.warn("机场心跳超时,设置为离线: sn={}, 超时时长={}秒",
|
||||
deviceSn, timeDiff / 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue