From f4e1d04712995e48940fef8b1dd938385091dec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=B0=8F=E4=BA=91?= Date: Thu, 18 Dec 2025 14:06:35 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B5=8B=E8=AF=95=E7=94=A8?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../machine/vendor/dji/DjiVendorConfig.java | 30 ++++++ .../DjiCheckDebugModeInstruction.java | 50 +++++++++ .../DjiEnableDebugModeInstruction.java | 54 ++++++++++ .../instruction/DjiOpenCoverInstruction.java | 4 +- .../com/tuoheng/old/DrcStateMachineTest.java | 100 ++++++++++++++++++ 5 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiCheckDebugModeInstruction.java create mode 100644 src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiEnableDebugModeInstruction.java diff --git a/src/main/java/com/tuoheng/machine/vendor/dji/DjiVendorConfig.java b/src/main/java/com/tuoheng/machine/vendor/dji/DjiVendorConfig.java index becabf6..bbf102c 100644 --- a/src/main/java/com/tuoheng/machine/vendor/dji/DjiVendorConfig.java +++ b/src/main/java/com/tuoheng/machine/vendor/dji/DjiVendorConfig.java @@ -61,6 +61,8 @@ public class DjiVendorConfig implements VendorConfig { case RETURN_HOME: return droneState == DroneState.FLYING || droneState == DroneState.ARRIVED; + + default: return true; } @@ -88,7 +90,35 @@ public class DjiVendorConfig implements VendorConfig { .setTimeout(10000); transactionMap.put(takeOffTransaction.getCommandType(), takeOffTransaction); + /** + * 开仓命令 Transaction + * 流程说明: + * 1. root: 检查是否已在调试模式 + * - 成功:直接执行开仓命令 + * - 失败:先开启调试模式,再执行开仓命令 + */ + // 创建检查调试模式的指令(root) + DjiCheckDebugModeInstruction checkDebugMode = new DjiCheckDebugModeInstruction(); + // 创建开仓指令(成功分支) + DjiOpenCoverInstruction openCoverAfterCheck = new DjiOpenCoverInstruction(); + + // 创建开启调试模式的指令(失败分支) + DjiEnableDebugModeInstruction enableDebugMode = new DjiEnableDebugModeInstruction(); + + // 创建开仓指令(失败分支的子指令) + DjiOpenCoverInstruction openCoverAfterEnable = new DjiOpenCoverInstruction(); + + // 构建指令树 + checkDebugMode + .onSuccess(openCoverAfterCheck) // 如果已在调试模式,直接开仓 + .onFailure(enableDebugMode // 如果不在调试模式,先开启调试模式 + .onSuccess(openCoverAfterEnable)); // 开启调试模式成功后,再开仓 + + Transaction openCoverTransaction = new Transaction("开仓", CommandType.OPEN_COVER) + .root(checkDebugMode) + .setTimeout(80000); // 总超时时间80秒 + transactionMap.put(openCoverTransaction.getCommandType(), openCoverTransaction); log.info("大疆厂家配置初始化完成,共配置{}个命令", transactionMap.size()); } diff --git a/src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiCheckDebugModeInstruction.java b/src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiCheckDebugModeInstruction.java new file mode 100644 index 0000000..e84abb2 --- /dev/null +++ b/src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiCheckDebugModeInstruction.java @@ -0,0 +1,50 @@ +package com.tuoheng.machine.vendor.dji.instruction; + +import com.tuoheng.machine.instruction.AbstractInstruction; +import com.tuoheng.machine.instruction.CallbackConfig; +import com.tuoheng.machine.instruction.InstructionContext; +import lombok.extern.slf4j.Slf4j; + +/** + * 大疆检查调试模式指令 + * 用于判断设备是否已经进入调试模式 + */ +@Slf4j +public class DjiCheckDebugModeInstruction extends AbstractInstruction { + + @Override + public String getName() { + return "DJI_CHECK_DEBUG_MODE"; + } + + @Override + public void executeRemoteCall(InstructionContext context) throws Exception { + String sn = context.getSn(); + log.info("检查大疆设备调试模式状态: sn={}", sn); + // 不需要发送命令,只需要等待状态回调 + } + + @Override + public CallbackConfig getMethodCallbackConfig(InstructionContext context) { + // 不需要方法回调 + return null; + } + + @Override + public CallbackConfig getStateCallbackConfig(InstructionContext context) { + String sn = context.getSn(); + + // 等待设备状态回调,判断是否在调试模式 + return CallbackConfig.builder() + .topic("dji/" + sn + "/state") + .fieldPath("debugMode") + .expectedValue("ENABLED") + .timeoutMs(3000) // 3秒超时,如果没有收到说明不在调试模式 + .build(); + } + + @Override + public long getTimeoutMs() { + return 3000; + } +} \ No newline at end of file diff --git a/src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiEnableDebugModeInstruction.java b/src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiEnableDebugModeInstruction.java new file mode 100644 index 0000000..29483be --- /dev/null +++ b/src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiEnableDebugModeInstruction.java @@ -0,0 +1,54 @@ +package com.tuoheng.machine.vendor.dji.instruction; + +import com.tuoheng.machine.instruction.AbstractInstruction; +import com.tuoheng.machine.instruction.CallbackConfig; +import com.tuoheng.machine.instruction.InstructionContext; +import lombok.extern.slf4j.Slf4j; + +/** + * 大疆开启调试模式指令 + */ +@Slf4j +public class DjiEnableDebugModeInstruction extends AbstractInstruction { + + @Override + public String getName() { + return "DJI_ENABLE_DEBUG_MODE"; + } + + @Override + public void executeRemoteCall(InstructionContext context) throws Exception { + String sn = context.getSn(); + log.info("发送大疆开启调试模式指令: sn={}", sn); + + String topic = "dji/" + sn + "/command"; + String payload = "{\"cmd\":\"enableDebugMode\"}"; + + context.getMqttClient().sendMessage(topic, payload); + log.debug("MQTT发送成功: topic={}, payload={}", topic, payload); + } + + @Override + public CallbackConfig getMethodCallbackConfig(InstructionContext context) { + String sn = context.getSn(); + + // 等待开启调试模式命令的ACK响应 + return CallbackConfig.builder() + .topic("dji/" + sn + "/response") + .fieldPath("cmd") + .expectedValue("enableDebugMode") + .timeoutMs(10000) + .build(); + } + + @Override + public CallbackConfig getStateCallbackConfig(InstructionContext context) { + // 不需要状态回调,只要收到ACK就认为命令发送成功 + return null; + } + + @Override + public long getTimeoutMs() { + return 10000; + } +} \ No newline at end of file diff --git a/src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiOpenCoverInstruction.java b/src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiOpenCoverInstruction.java index 7767a8b..72100bd 100644 --- a/src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiOpenCoverInstruction.java +++ b/src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiOpenCoverInstruction.java @@ -23,7 +23,9 @@ public class DjiOpenCoverInstruction extends AbstractInstruction { String topic = "dji/" + sn + "/command"; String payload = "{\"cmd\":\"openCover\"}"; - log.debug("MQTT发送: topic={}, payload={}", topic, payload); + + context.getMqttClient().sendMessage(topic, payload); + log.debug("MQTT发送成功: topic={}, payload={}", topic, payload); } @Override diff --git a/src/test/java/com/tuoheng/old/DrcStateMachineTest.java b/src/test/java/com/tuoheng/old/DrcStateMachineTest.java index 0c79d85..2209cba 100644 --- a/src/test/java/com/tuoheng/old/DrcStateMachineTest.java +++ b/src/test/java/com/tuoheng/old/DrcStateMachineTest.java @@ -188,4 +188,104 @@ public class DrcStateMachineTest { assertTrue(future.get().isSuccess()); } + + /** + * 测试开仓命令 - 场景1:设备已在调试模式,直接开仓成功 + * 流程: + * 1. 检查调试模式(成功) + * 2. 执行开仓命令 + * 3. 收到开仓命令ACK + * 4. 收到舱门状态变为OPENED + */ + @Test + @Order(6) + public void testOpenCoverWithDebugModeEnabled() throws ExecutionException, InterruptedException { + log.info("=== 测试开仓命令 - 场景1:设备已在调试模式 ==="); + + CompletableFuture 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 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("=== 测试通过:设备不在调试模式,先开启调试模式再开仓成功 ==="); + } }