2025-12-17 10:23:45 +08:00
|
|
|
|
package com.tuoheng.old;
|
2025-12-16 14:37:16 +08:00
|
|
|
|
|
2025-12-18 13:22:34 +08:00
|
|
|
|
import com.tuoheng.machine.MachineCommandManager;
|
|
|
|
|
|
import com.tuoheng.machine.command.CommandResult;
|
|
|
|
|
|
import com.tuoheng.machine.command.CommandType;
|
|
|
|
|
|
import com.tuoheng.machine.mqtt.MqttCallbackRegistry;
|
|
|
|
|
|
import com.tuoheng.machine.state.*;
|
2025-12-16 16:00:14 +08:00
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
2025-12-18 13:22:34 +08:00
|
|
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
|
|
|
|
import org.junit.jupiter.api.MethodOrderer;
|
2025-12-16 14:37:16 +08:00
|
|
|
|
import org.junit.jupiter.api.Test;
|
2025-12-18 13:22:34 +08:00
|
|
|
|
import org.junit.jupiter.api.TestMethodOrder;
|
2025-12-16 14:37:16 +08:00
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
|
import org.springframework.boot.test.context.SpringBootTest;
|
2025-12-18 13:22:34 +08:00
|
|
|
|
import org.junit.jupiter.api.*;
|
|
|
|
|
|
import org.springframework.util.Assert;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
|
import java.util.concurrent.*;
|
|
|
|
|
|
|
|
|
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
|
|
|
|
|
2025-12-16 14:37:16 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* DRC状态机测试
|
|
|
|
|
|
* 测试DRC模式的完整状态转换流程
|
|
|
|
|
|
*/
|
|
|
|
|
|
@SpringBootTest
|
2025-12-16 16:00:14 +08:00
|
|
|
|
@Slf4j
|
2025-12-18 13:22:34 +08:00
|
|
|
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
2025-12-16 14:37:16 +08:00
|
|
|
|
public class DrcStateMachineTest {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
2025-12-18 13:22:34 +08:00
|
|
|
|
MachineCommandManager machineCommandManager;
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
MqttCallbackRegistry mqttCallbackRegistry;
|
|
|
|
|
|
|
|
|
|
|
|
private static final ScheduledExecutorService scheduler =
|
|
|
|
|
|
Executors.newScheduledThreadPool(2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Boolean initState = false;
|
|
|
|
|
|
|
|
|
|
|
|
private static final String SN = "SN9527";
|
|
|
|
|
|
|
|
|
|
|
|
@BeforeEach
|
|
|
|
|
|
public void init(){
|
|
|
|
|
|
if(initState){
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
initState = true;
|
|
|
|
|
|
machineCommandManager.bindMachine(SN,"DJI");
|
|
|
|
|
|
}
|
2025-12-16 14:37:16 +08:00
|
|
|
|
|
2025-12-16 17:40:31 +08:00
|
|
|
|
@Test
|
2025-12-18 13:22:34 +08:00
|
|
|
|
@Order(1)
|
|
|
|
|
|
public void checkInitState(){
|
|
|
|
|
|
MachineStates machineStates = machineCommandManager.getMachineStates(SN);
|
|
|
|
|
|
assertNotNull(machineStates);
|
|
|
|
|
|
assertEquals(AirportState.UNKNOWN, machineStates.getAirportState());
|
|
|
|
|
|
assertEquals(DrcState.UNKNOWN, machineStates.getDrcState());
|
|
|
|
|
|
assertEquals(CoverState.UNKNOWN, machineStates.getCoverState());
|
|
|
|
|
|
assertEquals(DroneState.UNKNOWN, machineStates.getDroneState());
|
|
|
|
|
|
assertEquals(StopState.UNKNOWN, machineStates.getStopState());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 非在线状态下不可起飞
|
|
|
|
|
|
* @throws ExecutionException
|
|
|
|
|
|
* @throws InterruptedException
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Test
|
|
|
|
|
|
@Order(2)
|
|
|
|
|
|
public void checkTakeOffCommand() throws ExecutionException, InterruptedException {
|
|
|
|
|
|
CompletableFuture<CommandResult> future =
|
|
|
|
|
|
machineCommandManager.executeCommand(SN, CommandType.TAKE_OFF,new HashMap<>());
|
|
|
|
|
|
assertFalse(future.get().isSuccess());
|
2025-12-16 14:37:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-18 13:22:34 +08:00
|
|
|
|
@Test
|
|
|
|
|
|
@Order(3)
|
|
|
|
|
|
public void setState() {
|
|
|
|
|
|
|
|
|
|
|
|
MachineStates machineStates = new MachineStates();
|
|
|
|
|
|
machineStates.setAirportState(AirportState.ONLINE);
|
|
|
|
|
|
machineStates.setDroneState(DroneState.ONLINE);
|
|
|
|
|
|
machineCommandManager.updateMachineStates(SN, machineStates);
|
|
|
|
|
|
|
|
|
|
|
|
machineStates = machineCommandManager.getMachineStates(SN);
|
|
|
|
|
|
assertNotNull(machineStates);
|
|
|
|
|
|
assertEquals(AirportState.ONLINE, machineStates.getAirportState());
|
|
|
|
|
|
assertEquals(DroneState.ONLINE, machineStates.getDroneState());
|
|
|
|
|
|
assertEquals(DrcState.UNKNOWN, machineStates.getDrcState());
|
|
|
|
|
|
assertEquals(CoverState.UNKNOWN, machineStates.getCoverState());
|
|
|
|
|
|
assertEquals(StopState.UNKNOWN, machineStates.getStopState());
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// @Test
|
|
|
|
|
|
// @Order(4)
|
|
|
|
|
|
// public void checkTakeOffOverTime1() throws ExecutionException, InterruptedException, TimeoutException {
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 指令执行超时;因为缺乏指令成功的回调
|
|
|
|
|
|
// */
|
|
|
|
|
|
// CompletableFuture<CommandResult> future =
|
|
|
|
|
|
// machineCommandManager.executeCommand(SN, CommandType.TAKE_OFF,new HashMap<>());
|
|
|
|
|
|
// assertFalse(future.get().isSuccess());
|
|
|
|
|
|
//
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 指令执行成功,因为mqttCallbackRegistry模拟了发送回调
|
|
|
|
|
|
* 需要将 DjiTakeOffInstruction 的 getStateCallbackConfig 中直接返回null
|
|
|
|
|
|
*/
|
|
|
|
|
|
// @Test
|
|
|
|
|
|
// @Order(5)
|
|
|
|
|
|
// public void checkTakeOffOverTime2() throws ExecutionException, InterruptedException, TimeoutException {
|
|
|
|
|
|
//
|
|
|
|
|
|
// CompletableFuture<CommandResult> future =
|
|
|
|
|
|
// machineCommandManager.executeCommand(SN, CommandType.TAKE_OFF,new HashMap<>());
|
|
|
|
|
|
//
|
|
|
|
|
|
// scheduler.schedule(new Runnable() {
|
|
|
|
|
|
// @Override
|
|
|
|
|
|
// public void run() {
|
|
|
|
|
|
// String response = "{\"data\":{\"result\":\"takeoff\"}}";
|
|
|
|
|
|
// mqttCallbackRegistry.handleMessage("dji/SN9527/response",response);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// },100,TimeUnit.MILLISECONDS);
|
|
|
|
|
|
//
|
|
|
|
|
|
// assertTrue(future.get().isSuccess());
|
|
|
|
|
|
//
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 指令执行失败
|
|
|
|
|
|
* 需要将 DjiTakeOffInstruction 的 getStateCallbackConfig 中的注释放开
|
|
|
|
|
|
*/
|
|
|
|
|
|
// @Test
|
|
|
|
|
|
// @Order(5)
|
|
|
|
|
|
// public void checkTakeOffOverTime2() throws ExecutionException, InterruptedException, TimeoutException {
|
|
|
|
|
|
//
|
|
|
|
|
|
// CompletableFuture<CommandResult> future =
|
|
|
|
|
|
// machineCommandManager.executeCommand(SN, CommandType.TAKE_OFF,new HashMap<>());
|
|
|
|
|
|
//
|
|
|
|
|
|
// scheduler.schedule(new Runnable() {
|
|
|
|
|
|
// @Override
|
|
|
|
|
|
// public void run() {
|
|
|
|
|
|
// String response = "{\"data\":{\"result\":\"takeoff\"}}";
|
|
|
|
|
|
// mqttCallbackRegistry.handleMessage("dji/SN9527/response",response);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// },100,TimeUnit.MILLISECONDS);
|
|
|
|
|
|
//
|
|
|
|
|
|
// assertFalse(future.get().isSuccess());
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 需要将 DjiTakeOffInstruction 的 getStateCallbackConfig 中的注释放开
|
|
|
|
|
|
* @throws ExecutionException
|
|
|
|
|
|
* @throws InterruptedException
|
|
|
|
|
|
* @throws TimeoutException
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Test
|
|
|
|
|
|
@Order(5)
|
|
|
|
|
|
public void checkTakeOffOverTime2() throws ExecutionException, InterruptedException, TimeoutException {
|
|
|
|
|
|
|
|
|
|
|
|
CompletableFuture<CommandResult> future =
|
|
|
|
|
|
machineCommandManager.executeCommand(SN, CommandType.TAKE_OFF,new HashMap<>());
|
|
|
|
|
|
|
|
|
|
|
|
scheduler.schedule(new Runnable() {
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public void run() {
|
|
|
|
|
|
String response = "{\"data\":{\"result\":\"takeoff\"}}";
|
|
|
|
|
|
mqttCallbackRegistry.handleMessage("dji/SN9527/response",response);
|
|
|
|
|
|
|
|
|
|
|
|
// 添加延迟,等待状态回调监听器注册
|
|
|
|
|
|
try {
|
|
|
|
|
|
Thread.sleep(50); // 等待50ms
|
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response = "{\"droneState\":\"FLYING\"}";
|
|
|
|
|
|
mqttCallbackRegistry.handleMessage("dji/SN9527/state",response);
|
|
|
|
|
|
}
|
|
|
|
|
|
},100,TimeUnit.MILLISECONDS);
|
|
|
|
|
|
|
|
|
|
|
|
assertTrue(future.get().isSuccess());
|
|
|
|
|
|
}
|
2025-12-18 14:06:35 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 测试开仓命令 - 场景1:设备已在调试模式,直接开仓成功
|
|
|
|
|
|
* 流程:
|
|
|
|
|
|
* 1. 检查调试模式(成功)
|
|
|
|
|
|
* 2. 执行开仓命令
|
|
|
|
|
|
* 3. 收到开仓命令ACK
|
|
|
|
|
|
* 4. 收到舱门状态变为OPENED
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Test
|
|
|
|
|
|
@Order(6)
|
|
|
|
|
|
public void testOpenCoverWithDebugModeEnabled() throws ExecutionException, InterruptedException {
|
|
|
|
|
|
log.info("=== 测试开仓命令 - 场景1:设备已在调试模式 ===");
|
|
|
|
|
|
|
|
|
|
|
|
CompletableFuture<CommandResult> future =
|
|
|
|
|
|
machineCommandManager.executeCommand(SN, CommandType.OPEN_COVER, new HashMap<>());
|
|
|
|
|
|
|
|
|
|
|
|
scheduler.schedule(() -> {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 1. 模拟设备已在调试模式的状态回调(100ms后)
|
|
|
|
|
|
String response = "{\"debugMode\":\"ENABLED\"}";
|
|
|
|
|
|
mqttCallbackRegistry.handleMessage("dji/SN9527/state", response);
|
|
|
|
|
|
log.info("发送调试模式状态: {}", response);
|
|
|
|
|
|
|
|
|
|
|
|
Thread.sleep(50);
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 模拟开仓命令的ACK响应
|
|
|
|
|
|
response = "{\"cmd\":\"openCover\"}";
|
|
|
|
|
|
mqttCallbackRegistry.handleMessage("dji/SN9527/response", response);
|
|
|
|
|
|
log.info("发送开仓命令ACK: {}", response);
|
|
|
|
|
|
|
|
|
|
|
|
Thread.sleep(50);
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 模拟舱门状态变为OPENED
|
|
|
|
|
|
response = "{\"coverState\":\"OPENED\"}";
|
|
|
|
|
|
mqttCallbackRegistry.handleMessage("dji/SN9527/state", response);
|
|
|
|
|
|
log.info("发送舱门状态: {}", response);
|
|
|
|
|
|
|
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 100, TimeUnit.MILLISECONDS);
|
|
|
|
|
|
|
|
|
|
|
|
CommandResult result = future.get();
|
|
|
|
|
|
assertTrue(result.isSuccess(), "开仓命令应该执行成功");
|
|
|
|
|
|
log.info("=== 测试通过:设备已在调试模式,开仓成功 ===");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 测试开仓命令 - 场景2:设备不在调试模式,先开启调试模式再开仓
|
|
|
|
|
|
* 流程:
|
|
|
|
|
|
* 1. 检查调试模式(失败/超时)
|
|
|
|
|
|
* 2. 开启调试模式
|
|
|
|
|
|
* 3. 收到开启调试模式ACK
|
|
|
|
|
|
* 4. 执行开仓命令
|
|
|
|
|
|
* 5. 收到开仓命令ACK
|
|
|
|
|
|
* 6. 收到舱门状态变为OPENED
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Test
|
|
|
|
|
|
@Order(7)
|
|
|
|
|
|
public void testOpenCoverWithDebugModeDisabled() throws ExecutionException, InterruptedException {
|
|
|
|
|
|
log.info("=== 测试开仓命令 - 场景2:设备不在调试模式 ===");
|
|
|
|
|
|
|
|
|
|
|
|
CompletableFuture<CommandResult> future =
|
|
|
|
|
|
machineCommandManager.executeCommand(SN, CommandType.OPEN_COVER, new HashMap<>());
|
|
|
|
|
|
|
|
|
|
|
|
scheduler.schedule(() -> {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 1. 不发送调试模式状态,让检查调试模式超时(等待3秒超时)
|
|
|
|
|
|
log.info("等待检查调试模式超时...");
|
|
|
|
|
|
Thread.sleep(3500); // 等待超过3秒,让检查调试模式超时
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 模拟开启调试模式命令的ACK响应
|
|
|
|
|
|
String response = "{\"cmd\":\"enableDebugMode\"}";
|
|
|
|
|
|
mqttCallbackRegistry.handleMessage("dji/SN9527/response", response);
|
|
|
|
|
|
log.info("发送开启调试模式ACK: {}", response);
|
|
|
|
|
|
|
|
|
|
|
|
Thread.sleep(50);
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 模拟开仓命令的ACK响应
|
|
|
|
|
|
response = "{\"cmd\":\"openCover\"}";
|
|
|
|
|
|
mqttCallbackRegistry.handleMessage("dji/SN9527/response", response);
|
|
|
|
|
|
log.info("发送开仓命令ACK: {}", response);
|
|
|
|
|
|
|
|
|
|
|
|
Thread.sleep(50);
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 模拟舱门状态变为OPENED
|
|
|
|
|
|
response = "{\"coverState\":\"OPENED\"}";
|
|
|
|
|
|
mqttCallbackRegistry.handleMessage("dji/SN9527/state", response);
|
|
|
|
|
|
log.info("发送舱门状态: {}", response);
|
|
|
|
|
|
|
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 100, TimeUnit.MILLISECONDS);
|
|
|
|
|
|
|
|
|
|
|
|
CommandResult result = future.get();
|
|
|
|
|
|
assertTrue(result.isSuccess(), "开仓命令应该执行成功(先开启调试模式再开仓)");
|
|
|
|
|
|
log.info("=== 测试通过:设备不在调试模式,先开启调试模式再开仓成功 ===");
|
|
|
|
|
|
}
|
2025-12-16 14:37:16 +08:00
|
|
|
|
}
|