添加新版状态机

This commit is contained in:
孙小云 2025-12-17 10:23:45 +08:00
parent 4a3b9dc2ea
commit 9e6eacaf72
209 changed files with 4030 additions and 854 deletions

View File

@ -11,7 +11,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
* - @EnableAutoConfiguration: 启用自动配置 * - @EnableAutoConfiguration: 启用自动配置
* - @ComponentScan: 自动扫描当前包及子包下的所有组件 * - @ComponentScan: 自动扫描当前包及子包下的所有组件
* *
* 放在 com.tuoheng 包下可以自动扫描到 com.tuoheng.machine.* 下的所有组件 * 放在 com.tuoheng 包下可以自动扫描到 com.tuoheng.old.* 下的所有组件
*/ */
@SpringBootApplication @SpringBootApplication
public class ThingsboardClientApplication { public class ThingsboardClientApplication {

View File

@ -0,0 +1,290 @@
package com.tuoheng.machine;
import com.tuoheng.machine.command.*;
import com.tuoheng.machine.instruction.InstructionContext;
import com.tuoheng.machine.state.MachineStates;
import com.tuoheng.machine.statemachine.MachineStateManager;
import com.tuoheng.machine.statemachine.StateChangeListener;
import com.tuoheng.machine.vendor.VendorConfig;
import com.tuoheng.machine.vendor.VendorRegistry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
/**
* 设备命令管理器框架使用者的主要入口
*/
@Slf4j
@Component
public class MachineCommandManager {
private final VendorRegistry vendorRegistry;
private final MachineStateManager stateManager;
private final TransactionExecutor transactionExecutor;
/**
* SN -> 当前正在执行的命令
*/
private final Map<String, CommandExecution> executingCommands = new ConcurrentHashMap<>();
/**
* 命令执行监听器
*/
private final Map<String, CommandExecutionListener> commandListeners = new ConcurrentHashMap<>();
public MachineCommandManager(VendorRegistry vendorRegistry,
MachineStateManager stateManager,
TransactionExecutor transactionExecutor) {
this.vendorRegistry = vendorRegistry;
this.stateManager = stateManager;
this.transactionExecutor = transactionExecutor;
}
/**
* 绑定设备到厂家
*
* @param sn 设备SN号
* @param vendorType 厂家类型
*/
public void bindMachine(String sn, String vendorType) {
vendorRegistry.bindSnToVendor(sn, vendorType);
log.info("绑定设备到厂家: sn=, vendorType={}", sn, vendorType);
}
/**
* 解绑设备
*
* @param sn 设备SN号
*/
public void unbindMachine(String sn) {
vendorRegistry.unbindSn(sn);
executingCommands.remove(sn);
stateManager.removeStates(sn);
log.info("解绑设备: sn={}", sn);
}
/**
* 获取设备当前状态
*
* @param sn 设备SN号
* @return 设备状态
*/
public MachineStates getMachineStates(String sn) {
return stateManager.getStates(sn);
}
/**
* 更新设备状态通常在心跳中调用
*
* @param sn 设备SN号
* @param newStates 新状态
*/
public void updateMachineStates(String sn, MachineStates newStates) {
stateManager.updateStates(sn, newStates);
}
/**
* 判断设备是否正在执行命令
*
* @param sn 设备SN号
* @return 是否正在执行命令
*/
public boolean isExecutingCommand(String sn) {
CommandExecution execution = executingCommands.get(sn);
return execution != null && !execution.getFuture().isDone();
}
/**
* 获取设备当前正在执行的命令类型
*
* @param sn 设备SN号
* @return 命令类型如果没有正在执行的命令则返回null
*/
public CommandType getExecutingCommandType(String sn) {
CommandExecution execution = executingCommands.get(sn);
if (execution != null && !execution.getFuture().isDone()) {
return execution.getCommandType();
}
return null;
}
/**
* 获取设备在当前状态下可以执行的命令列表
*
* @param sn 设备SN号
* @return 可执行的命令列表
*/
public List<CommandType> getAvailableCommands(String sn) {
VendorConfig vendorConfig = vendorRegistry.getVendorConfig(sn);
if (vendorConfig == null) {
log.warn("设备未绑定厂家: sn={}", sn);
return List.of();
}
MachineStates currentStates = stateManager.getStates(sn);
return vendorConfig.getAvailableCommands(currentStates);
}
/**
* 执行命令
*
* @param sn 设备SN号
* @param commandType 命令类型
* @return 命令执行结果的Future
*/
public CompletableFuture<CommandResult> executeCommand(String sn, CommandType commandType) {
return executeCommand(sn, commandType, Map.of());
}
/**
* 执行命令带参数
*
* @param sn 设备SN号
* @param commandType 命令类型
* @param params 命令参数
* @return 命令执行结果的Future
*/
public CompletableFuture<CommandResult> executeCommand(String sn, CommandType commandType, Map<String, Object> params) {
log.info("收到命令执行请求: sn={}, commandType={}, params={}", sn, commandType, params);
// 1. 检查设备是否已绑定厂家
VendorConfig vendorConfig = vendorRegistry.getVendorConfig(sn);
if (vendorConfig == null) {
String error = "设备未绑定厂家";
log.error("{}: sn={}", error, sn);
return CompletableFuture.completedFuture(CommandResult.failure(commandType, error));
}
// 2. 检查是否正在执行其他命令
if (isExecutingCommand(sn)) {
String error = "设备正在执行其他命令: " + getExecutingCommandType(sn);
log.warn("{}: sn={}", error, sn);
return CompletableFuture.completedFuture(CommandResult.failure(commandType, error));
}
// 3. 检查当前状态是否可以执行该命令
MachineStates currentStates = stateManager.getStates(sn);
if (!vendorConfig.canExecuteCommand(currentStates, commandType)) {
String error = "当前状态不允许执行该命令";
log.warn("{}: sn={}, commandType={}, currentStates={}", error, sn, commandType, currentStates);
return CompletableFuture.completedFuture(CommandResult.failure(commandType, error));
}
// 4. 获取事务定义
Transaction transaction = vendorConfig.getTransaction(commandType);
if (transaction == null) {
String error = "厂家不支持该命令";
log.error("{}: sn={}, commandType={}, vendorType={}", error, sn, commandType, vendorConfig.getVendorType());
return CompletableFuture.completedFuture(CommandResult.failure(commandType, error));
}
// 5. 创建指令上下文
InstructionContext context = new InstructionContext(sn, vendorConfig.getVendorType());
params.forEach(context::putCommandParam);
// 6. 执行事务
CompletableFuture<CommandResult> future = transactionExecutor.executeTransaction(transaction, context);
// 7. 记录正在执行的命令
executingCommands.put(sn, new CommandExecution(commandType, future, System.currentTimeMillis()));
// 8. 添加完成回调
future.whenComplete((result, throwable) -> {
executingCommands.remove(sn);
if (throwable != null) {
log.error("命令执行异常: sn={}, commandType={}", sn, commandType, throwable);
notifyCommandComplete(sn, CommandResult.failure(commandType, "命令执行异常: " + throwable.getMessage()));
} else {
log.info("命令执行完成: sn={}, commandType={}, success={}", sn, commandType, result.isSuccess());
notifyCommandComplete(sn, result);
}
});
return future;
}
/**
* 注册命令执行监听器
*
* @param listenerId 监听器ID
* @param listener 监听器
*/
public void registerCommandListener(String listenerId, CommandExecutionListener listener) {
commandListeners.put(listenerId, listener);
log.debug("注册命令执行监听器: listenerId={}", listenerId);
}
/**
* 取消注册命令执行监听器
*
* @param listenerId 监听器ID
*/
public void unregisterCommandListener(String listenerId) {
commandListeners.remove(listenerId);
log.debug("取消注册命令执行监听器: listenerId={}", listenerId);
}
/**
* 注册状态变化监听器
*
* @param listenerId 监听器ID
* @param listener 监听器
*/
public void registerStateChangeListener(String listenerId, StateChangeListener listener) {
stateManager.registerStateChangeListener(listenerId, listener);
}
/**
* 取消注册状态变化监听器
*
* @param listenerId 监听器ID
*/
public void unregisterStateChangeListener(String listenerId) {
stateManager.unregisterStateChangeListener(listenerId);
}
/**
* 通知命令执行完成
*/
private void notifyCommandComplete(String sn, CommandResult result) {
for (CommandExecutionListener listener : commandListeners.values()) {
try {
listener.onCommandComplete(sn, result);
} catch (Exception e) {
log.error("命令执行监听器执行失败: sn={}, commandType={}", sn, result.getCommandType(), e);
}
}
}
/**
* 命令执行信息
*/
private static class CommandExecution {
private final CommandType commandType;
private final CompletableFuture<CommandResult> future;
private final long startTime;
public CommandExecution(CommandType commandType, CompletableFuture<CommandResult> future, long startTime) {
this.commandType = commandType;
this.future = future;
this.startTime = startTime;
}
public CommandType getCommandType() {
return commandType;
}
public CompletableFuture<CommandResult> getFuture() {
return future;
}
public long getStartTime() {
return startTime;
}
}
}

View File

@ -0,0 +1,414 @@
# 设备命令执行框架
## 概述
这是一个通用的设备命令执行框架,用于管理无人机、机巢等设备的状态和命令执行。框架采用状态机模式,支持多厂家接入,提供了完整的指令执行、回调处理和状态管理功能。
## 核心概念
### 1. 四套大状态
框架管理四套独立的状态系统:
- **DroneState无人机状态**:准备中、飞行中、返航、急停、指点飞行等
- **AirportState机巢状态**:在线、待机、调试模式、重启中等
- **CoverState舱门状态**:关闭、打开中、已打开、关闭中等
- **DrcState飞行控制模式**:未知、退出、进入中、已进入、退出中
### 2. 命令Command
命令是用户发起的操作请求,如起飞、降落、返航等。每个命令由一个或多个指令组成。
### 3. 事务Transaction
事务是命令的具体实现,包含一系列按顺序执行的指令。事务定义了:
- 指令列表(按顺序执行)
- 超时时间
- 命令类型
### 4. 指令Instruction
指令是事务的最小执行单元,包含四个部分:
**a. canExecute** - 判断是否可以执行该指令
```java
boolean canExecute(InstructionContext context)
```
**b. executeRemoteCall** - 执行远程调用如MQTT发送
```java
void executeRemoteCall(InstructionContext context)
```
**c. getMethodCallbackConfig** - 方法回调配置(等待方法执行结果)
```java
CallbackConfig getMethodCallbackConfig(InstructionContext context)
```
**d. getStateCallbackConfig** - 状态回调配置(等待状态变化)
```java
CallbackConfig getStateCallbackConfig(InstructionContext context)
```
### 5. 回调机制
框架提供了基于MQTT的回调注册机制
- 指令执行后自动注册回调
- 支持字段匹配和自定义判断逻辑
- 支持超时处理
- 支持短路(跳过某些回调)
- 回调完成后自动取消注册
## 架构设计
```
┌─────────────────────────────────────────────────────────────┐
│ MachineCommandManager │
│ (框架使用者入口) │
└─────────────────────────────────────────────────────────────┘
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ VendorRegistry│ │StateManager │ │Transaction │
│ (厂家注册) │ │ (状态管理) │ │ Executor │
└──────────────┘ └──────────────┘ └──────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ VendorConfig │ │ Instruction │
│ (厂家配置) │ │ (指令) │
└──────────────┘ └──────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Transaction │ │ Callback │
│ (事务) │ │ Registry │
└──────────────┘ └──────────────┘
```
## 目录结构
```
com.tuoheng.machine/
├── state/ # 状态定义
│ ├── DroneState.java # 无人机状态
│ ├── AirportState.java # 机巢状态
│ ├── CoverState.java # 舱门状态
│ ├── DrcState.java # DRC状态
│ └── MachineStates.java # 状态容器
├── instruction/ # 指令框架
│ ├── Instruction.java # 指令接口
│ ├── AbstractInstruction.java # 抽象指令基类
│ ├── InstructionContext.java # 指令上下文
│ ├── InstructionResult.java # 指令执行结果
│ └── CallbackConfig.java # 回调配置
├── command/ # 命令框架
│ ├── CommandType.java # 命令类型枚举
│ ├── CommandResult.java # 命令执行结果
│ ├── Transaction.java # 事务定义
│ ├── TransactionExecutor.java # 事务执行器
│ └── CommandExecutionListener.java # 命令执行监听器
├── mqtt/ # MQTT回调系统
│ ├── MqttCallbackHandler.java # 回调处理器
│ └── MqttCallbackRegistry.java # 回调注册中心
├── statemachine/ # 状态机
│ ├── MachineStateManager.java # 状态管理器
│ └── StateChangeListener.java # 状态变化监听器
├── vendor/ # 厂家配置
│ ├── VendorConfig.java # 厂家配置接口
│ ├── VendorRegistry.java # 厂家注册中心
│ └── dji/ # 大疆实现
│ ├── DjiVendorConfig.java # 大疆厂家配置
│ └── instruction/ # 大疆指令实现
│ ├── DjiTakeOffInstruction.java
│ ├── DjiLandInstruction.java
│ ├── DjiReturnHomeInstruction.java
│ ├── DjiEmergencyStopInstruction.java
│ ├── DjiResumeFlightInstruction.java
│ ├── DjiPointFlyInstruction.java
│ ├── DjiCancelPointInstruction.java
│ ├── DjiStartMissionInstruction.java
│ ├── DjiOpenCoverInstruction.java
│ └── DjiCloseCoverInstruction.java
├── config/ # 配置
│ └── MachineFrameworkConfig.java
├── example/ # 使用示例
│ └── MachineFrameworkUsageExample.java
├── MachineCommandManager.java # 主入口
└── README.md # 本文档
```
## 使用指南
### 1. 绑定设备到厂家
```java
@Autowired
private MachineCommandManager commandManager;
// 绑定设备
String sn = "DJI-12345678";
commandManager.bindMachine(sn, "DJI");
```
### 2. 更新设备状态(心跳中调用)
```java
// 创建新状态
MachineStates newStates = new MachineStates();
newStates.setDroneState(DroneState.PREPARING);
newStates.setAirportState(AirportState.STANDBY);
newStates.setCoverState(CoverState.OPENED);
newStates.setDrcState(DrcState.EXITED);
// 更新状态
commandManager.updateMachineStates(sn, newStates);
```
### 3. 查询设备状态
```java
// 获取当前状态
MachineStates currentStates = commandManager.getMachineStates(sn);
// 检查是否正在执行命令
boolean isExecuting = commandManager.isExecutingCommand(sn);
// 获取可执行的命令列表
List<CommandType> availableCommands = commandManager.getAvailableCommands(sn);
```
### 4. 执行命令
```java
// 执行简单命令
commandManager.executeCommand(sn, CommandType.TAKE_OFF)
.thenAccept(result -> {
if (result.isSuccess()) {
log.info("起飞成功");
} else {
log.error("起飞失败: {}", result.getErrorMessage());
}
});
// 执行带参数的命令
Map<String, Object> params = new HashMap<>();
params.put("latitude", 39.9042);
params.put("longitude", 116.4074);
params.put("altitude", 100.0);
commandManager.executeCommand(sn, CommandType.POINT_FLY, params)
.thenAccept(result -> {
// 处理结果
});
```
### 5. 注册监听器
```java
// 注册命令执行监听器
commandManager.registerCommandListener("my-listener", (sn, result) -> {
log.info("命令执行完成: sn={}, commandType={}, success={}",
sn, result.getCommandType(), result.isSuccess());
// 记录到数据库
});
// 注册状态变化监听器
commandManager.registerStateChangeListener("state-listener", (sn, newStates) -> {
log.info("状态变化: sn={}, droneState={}",
sn, newStates.getDroneState());
// 记录到数据库
});
```
### 6. 处理MQTT消息
```java
@Autowired
private MqttCallbackRegistry mqttCallbackRegistry;
// 在MQTT消费者中调用
public void onMqttMessage(String topic, Object messageBody) {
// 分发给回调注册中心处理
mqttCallbackRegistry.handleMessage(topic, messageBody);
// 如果是状态消息,更新设备状态
if (topic.endsWith("/state")) {
String sn = extractSnFromTopic(topic);
MachineStates newStates = parseStatesFromMessage(messageBody);
commandManager.updateMachineStates(sn, newStates);
}
}
```
## 接入新厂家
### 1. 创建厂家配置类
```java
@Component
public class MyVendorConfig implements VendorConfig {
@Override
public String getVendorType() {
return "MY_VENDOR";
}
@Override
public String getVendorName() {
return "我的厂家";
}
@Override
public Transaction getTransaction(CommandType commandType) {
// 返回命令对应的事务定义
return transactionMap.get(commandType);
}
@Override
public boolean canExecuteCommand(MachineStates currentStates, CommandType commandType) {
// 判断当前状态是否可以执行该命令
return true;
}
@Override
public List<CommandType> getAvailableCommands(MachineStates currentStates) {
// 返回当前状态下可执行的命令列表
return availableCommands;
}
}
```
### 2. 实现指令
```java
@Slf4j
public class MyTakeOffInstruction extends AbstractInstruction {
@Override
public String getName() {
return "MY_TAKE_OFF";
}
@Override
public void executeRemoteCall(InstructionContext context) throws Exception {
String sn = context.getSn();
// 发送MQTT消息
mqttClient.publish("my/" + sn + "/command", "{\"cmd\":\"takeoff\"}");
}
@Override
public CallbackConfig getMethodCallbackConfig(InstructionContext context) {
return CallbackConfig.builder()
.topic("my/" + context.getSn() + "/response")
.fieldPath("cmd")
.expectedValue("takeoff")
.canShortCircuit(false)
.timeoutMs(10000)
.build();
}
@Override
public CallbackConfig getStateCallbackConfig(InstructionContext context) {
return CallbackConfig.builder()
.topic("my/" + context.getSn() + "/state")
.fieldPath("droneState")
.expectedValue("FLYING")
.canShortCircuit(false)
.timeoutMs(60000)
.build();
}
}
```
### 3. 注册厂家配置
```java
@Configuration
public class MyVendorAutoConfig {
@Bean
public CommandLineRunner registerMyVendor(VendorRegistry vendorRegistry,
MyVendorConfig myVendorConfig) {
return args -> {
vendorRegistry.registerVendor(myVendorConfig);
};
}
}
```
## 核心特性
### 1. 状态驱动
- 命令执行前自动检查状态是否允许
- 状态变化自动通知监听器
- 支持四套独立的状态系统
### 2. 异步执行
- 所有命令异步执行返回CompletableFuture
- 支持链式调用和组合
- 不阻塞主线程
### 3. 回调机制
- 自动注册和取消MQTT回调
- 支持超时处理
- 支持自定义匹配逻辑
### 4. 厂家隔离
- 不同厂家的实现完全隔离
- 框架使用者无需关心厂家差异
- 通过SN自动路由到对应厂家
### 5. 可扩展性
- 易于添加新命令
- 易于接入新厂家
- 易于自定义指令逻辑
### 6. 监控和日志
- 完整的命令执行日志
- 状态变化记录
- 支持自定义监听器
## 注意事项
1. **MQTT发送逻辑**当前DJI指令中的MQTT发送是示例代码需要替换为实际的MQTT客户端调用
2. **消息格式**回调配置中的字段路径需要根据实际的MQTT消息格式调整
3. **状态同步**:建议在心跳中定期调用`updateMachineStates`同步设备状态
4. **并发控制**:框架自动确保同一设备同时只能执行一个命令
5. **超时处理**:合理设置指令和事务的超时时间,避免长时间等待
6. **错误处理**:建议注册命令执行监听器,记录失败的命令以便排查问题
## 后续优化建议
1. 添加命令队列功能,支持命令排队执行
2. 添加命令取消功能
3. 添加命令重试机制
4. 添加更详细的执行进度回调
5. 支持命令优先级
6. 添加命令执行历史查询
7. 添加性能监控和统计
## 联系方式
如有问题或建议,请联系开发团队。

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.action.airport;
import com.tuoheng.machine.events.AirportEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.AirportState;
/**
* Base action for machine offline handling; platform implementations extend this.
*/
public abstract class OfflineAction implements PlatformAction<AirportState, AirportEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.action.airport;
import com.tuoheng.machine.events.AirportEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.AirportState;
/**
* Base action for machine online handling; platform implementations extend this.
*/
public abstract class OnlineAction implements PlatformAction<AirportState, AirportEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.action.cover;
import com.tuoheng.machine.events.CoverEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.CoverState;
/**
* Base action for closing the cover; platform implementations extend this.
*/
public abstract class CloseCoverAction implements PlatformAction<CoverState, CoverEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.action.cover;
import com.tuoheng.machine.events.CoverEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.CoverState;
/**
* Base action for cover-closed handling; platform implementations extend this.
*/
public abstract class CoverClosedAction implements PlatformAction<CoverState, CoverEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.action.cover;
import com.tuoheng.machine.events.CoverEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.CoverState;
/**
* Base action for cover-opened handling; platform implementations extend this.
*/
public abstract class CoverOpenedAction implements PlatformAction<CoverState, CoverEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.action.cover;
import com.tuoheng.machine.events.CoverEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.CoverState;
/**
* Base action for opening the cover; platform implementations extend this.
*/
public abstract class OpenCoverAction implements PlatformAction<CoverState, CoverEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.action.debug;
import com.tuoheng.machine.events.AirportEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.AirportState;
/**
* Base action for closing debug mode; platform implementations extend this.
*/
public abstract class CloseDebugModeAction implements PlatformAction<AirportState, AirportEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.action.debug;
import com.tuoheng.machine.events.AirportEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.AirportState;
/**
* Base action for opening debug mode; platform implementations extend this.
*/
public abstract class OpenDebugModeAction implements PlatformAction<AirportState, AirportEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drc;
import com.tuoheng.machine.events.DrcEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DrcState;
/**
* Base action for DRC enter handling; platform implementations extend this.
*/
public abstract class EnterAction implements PlatformAction<DrcState, DrcEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drc;
import com.tuoheng.machine.events.DrcEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DrcState;
/**
* Base action for DRC entered handling; platform implementations extend this.
*/
public abstract class EnteredAction implements PlatformAction<DrcState, DrcEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drc;
import com.tuoheng.machine.events.DrcEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DrcState;
/**
* Base action for DRC exit handling; platform implementations extend this.
*/
public abstract class ExitAction implements PlatformAction<DrcState, DrcEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drc;
import com.tuoheng.machine.events.DrcEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DrcState;
/**
* Base action for DRC exited handling; platform implementations extend this.
*/
public abstract class ExitedAction implements PlatformAction<DrcState, DrcEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drone;
import com.tuoheng.machine.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState;
/**
* Base action for drone arrive at destination handling; platform implementations extend this.
*/
public abstract class ArriveAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drone;
import com.tuoheng.machine.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState;
/**
* Base action for drone emergency stop handling; platform implementations extend this.
*/
public abstract class EmergencyStopAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drone;
import com.tuoheng.machine.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState;
/**
* Base action for drone offline handling; platform implementations extend this.
*/
public abstract class OfflineAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drone;
import com.tuoheng.machine.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState;
/**
* Base action for drone point to flying handling; platform implementations extend this.
*/
public abstract class PointToFlyingAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drone;
import com.tuoheng.machine.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState;
/**
* Base action for drone point to return handling; platform implementations extend this.
*/
public abstract class PointToReturnAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drone;
import com.tuoheng.machine.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState;
/**
* Base action for drone resume flying handling; platform implementations extend this.
*/
public abstract class ResumeFlyingAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drone;
import com.tuoheng.machine.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState;
/**
* Base action for drone resume return handling; platform implementations extend this.
*/
public abstract class ResumeReturnAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drone;
import com.tuoheng.machine.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState;
/**
* Base action for drone start flying handling; platform implementations extend this.
*/
public abstract class StartFlyingAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drone;
import com.tuoheng.machine.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState;
/**
* Base action for drone start prepare handling; platform implementations extend this.
*/
public abstract class StartPrepareAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.action.drone;
import com.tuoheng.machine.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState;
/**
* Base action for drone start return handling; platform implementations extend this.
*/
public abstract class StartReturnAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.action.reboot;
import com.tuoheng.machine.events.AirportEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.AirportState;
/**
* Base action for rebooting; platform implementations extend this.
*/
public abstract class RebootAction implements PlatformAction<AirportState, AirportEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.action.reboot;
import com.tuoheng.machine.events.AirportEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.AirportState;
/**
* Base action for completing reboot; platform implementations extend this.
*/
public abstract class RebootCompletedAction implements PlatformAction<AirportState, AirportEvent> {
}

View File

@ -0,0 +1,15 @@
package com.tuoheng.machine.command;
/**
* 命令执行监听器
*/
@FunctionalInterface
public interface CommandExecutionListener {
/**
* 命令执行完成回调
*
* @param sn 设备SN号
* @param result 命令执行结果
*/
void onCommandComplete(String sn, CommandResult result);
}

View File

@ -0,0 +1,58 @@
package com.tuoheng.machine.command;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 命令执行结果
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommandResult {
/**
* 命令类型
*/
private CommandType commandType;
/**
* 是否成功
*/
private boolean success;
/**
* 错误信息
*/
private String errorMessage;
/**
* 失败的指令名称
*/
private String failedInstructionName;
/**
* 结果数据
*/
private Object data;
public static CommandResult success(CommandType commandType) {
return new CommandResult(commandType, true, null, null, null);
}
public static CommandResult success(CommandType commandType, Object data) {
return new CommandResult(commandType, true, null, null, data);
}
public static CommandResult failure(CommandType commandType, String errorMessage) {
return new CommandResult(commandType, false, errorMessage, null, null);
}
public static CommandResult failure(CommandType commandType, String errorMessage, String failedInstructionName) {
return new CommandResult(commandType, false, errorMessage, failedInstructionName, null);
}
public static CommandResult timeout(CommandType commandType) {
return new CommandResult(commandType, false, "命令执行超时", null, null);
}
}

View File

@ -0,0 +1,91 @@
package com.tuoheng.machine.command;
/**
* 命令类型枚举
*/
public enum CommandType {
/**
* 起飞
*/
TAKE_OFF,
/**
* 降落
*/
LAND,
/**
* 返航
*/
RETURN_HOME,
/**
* 急停
*/
EMERGENCY_STOP,
/**
* 继续飞行
*/
RESUME_FLIGHT,
/**
* 指点飞行
*/
POINT_FLY,
/**
* 取消指点
*/
CANCEL_POINT,
/**
* 开始航线任务
*/
START_MISSION,
/**
* 暂停航线任务
*/
PAUSE_MISSION,
/**
* 恢复航线任务
*/
RESUME_MISSION,
/**
* 打开舱门
*/
OPEN_COVER,
/**
* 关闭舱门
*/
CLOSE_COVER,
/**
* 进入调试模式
*/
ENTER_DEBUG_MODE,
/**
* 退出调试模式
*/
EXIT_DEBUG_MODE,
/**
* 进入DRC模式
*/
ENTER_DRC_MODE,
/**
* 退出DRC模式
*/
EXIT_DRC_MODE,
/**
* 重启机巢
*/
REBOOT_AIRPORT
}

View File

@ -0,0 +1,54 @@
package com.tuoheng.machine.command;
import com.tuoheng.machine.instruction.Instruction;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 事务由多个指令组成
*/
@Data
public class Transaction {
/**
* 事务名称
*/
private String name;
/**
* 命令类型
*/
private CommandType commandType;
/**
* 指令列表按顺序执行
*/
private List<Instruction> instructions = new ArrayList<>();
/**
* 事务超时时间毫秒
*/
private long timeoutMs = 120000; // 默认2分钟
public Transaction(String name, CommandType commandType) {
this.name = name;
this.commandType = commandType;
}
/**
* 添加指令
*/
public Transaction addInstruction(Instruction instruction) {
this.instructions.add(instruction);
return this;
}
/**
* 设置超时时间
*/
public Transaction setTimeout(long timeoutMs) {
this.timeoutMs = timeoutMs;
return this;
}
}

View File

@ -0,0 +1,166 @@
package com.tuoheng.machine.command;
import com.tuoheng.machine.instruction.*;
import com.tuoheng.machine.mqtt.MqttCallbackRegistry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 事务执行器
*/
@Slf4j
@Component
public class TransactionExecutor {
private final MqttCallbackRegistry callbackRegistry;
public TransactionExecutor(MqttCallbackRegistry callbackRegistry) {
this.callbackRegistry = callbackRegistry;
}
/**
* 执行事务
*/
public CompletableFuture<CommandResult> executeTransaction(Transaction transaction, InstructionContext context) {
log.info("开始执行事务: transaction={}, sn={}", transaction.getName(), context.getSn());
CompletableFuture<CommandResult> future = new CompletableFuture<>();
long startTime = System.currentTimeMillis();
// 在新线程中执行事务
CompletableFuture.runAsync(() -> {
try {
// 依次执行每个指令
for (Instruction instruction : transaction.getInstructions()) {
// 检查事务是否超时
if (System.currentTimeMillis() - startTime > transaction.getTimeoutMs()) {
log.warn("事务执行超时: transaction={}, sn={}", transaction.getName(), context.getSn());
future.complete(CommandResult.timeout(transaction.getCommandType()));
return;
}
// 执行指令
InstructionResult result = executeInstruction(instruction, context);
// 如果指令失败终止事务
if (!result.isSuccess()) {
log.error("指令执行失败: instruction={}, error={}", instruction.getName(), result.getErrorMessage());
future.complete(CommandResult.failure(
transaction.getCommandType(),
result.getErrorMessage(),
instruction.getName()
));
return;
}
log.debug("指令执行成功: instruction={}", instruction.getName());
}
// 所有指令执行成功
log.info("事务执行成功: transaction={}, sn={}", transaction.getName(), context.getSn());
future.complete(CommandResult.success(transaction.getCommandType()));
} catch (Exception e) {
log.error("事务执行异常: transaction={}, sn={}", transaction.getName(), context.getSn(), e);
future.complete(CommandResult.failure(transaction.getCommandType(), "事务执行异常: " + e.getMessage()));
}
});
return future;
}
/**
* 执行单个指令
*/
private InstructionResult executeInstruction(Instruction instruction, InstructionContext context) {
log.debug("开始执行指令: instruction={}, sn={}", instruction.getName(), context.getSn());
try {
// a. 判断是否可以执行
if (!instruction.canExecute(context)) {
String error = "指令不满足执行条件";
log.warn("指令不满足执行条件: instruction={}, sn={}", instruction.getName(), context.getSn());
InstructionResult result = InstructionResult.failure(error);
instruction.onComplete(context, result);
return result;
}
// b. 执行远程调用
instruction.executeRemoteCall(context);
log.debug("远程调用已发送: instruction={}", instruction.getName());
// c. 等待方法回调
CallbackConfig methodCallback = instruction.getMethodCallbackConfig(context);
if (methodCallback != null && !methodCallback.isCanShortCircuit()) {
InstructionResult methodResult = waitForCallback(methodCallback, context);
if (!methodResult.isSuccess()) {
instruction.onComplete(context, methodResult);
return methodResult;
}
}
// d. 等待状态回调
CallbackConfig stateCallback = instruction.getStateCallbackConfig(context);
if (stateCallback != null && !stateCallback.isCanShortCircuit()) {
InstructionResult stateResult = waitForCallback(stateCallback, context);
if (!stateResult.isSuccess()) {
instruction.onComplete(context, stateResult);
return stateResult;
}
}
// 指令执行成功
InstructionResult result = InstructionResult.success();
instruction.onComplete(context, result);
return result;
} catch (Exception e) {
log.error("指令执行异常: instruction={}, sn={}", instruction.getName(), context.getSn(), e);
InstructionResult result = InstructionResult.failure("指令执行异常: " + e.getMessage());
instruction.onComplete(context, result);
return result;
}
}
/**
* 等待回调
*/
private InstructionResult waitForCallback(CallbackConfig callbackConfig, InstructionContext context) {
CompletableFuture<InstructionResult> future = new CompletableFuture<>();
AtomicBoolean callbackReceived = new AtomicBoolean(false);
// 注册回调
String callbackId = callbackRegistry.registerCallback(
callbackConfig.getTopic(),
messageBody -> {
if (callbackReceived.get()) {
return; // 已经收到回调忽略后续消息
}
// 判断消息是否匹配
if (callbackConfig.matches(messageBody)) {
callbackReceived.set(true);
future.complete(InstructionResult.success(messageBody));
log.debug("收到匹配的回调消息: topic={}", callbackConfig.getTopic());
}
},
callbackConfig.getTimeoutMs()
);
try {
// 等待回调或超时
InstructionResult result = future.get(callbackConfig.getTimeoutMs(), TimeUnit.MILLISECONDS);
return result;
} catch (Exception e) {
log.warn("等待回调超时: topic={}, timeout={}ms", callbackConfig.getTopic(), callbackConfig.getTimeoutMs());
return InstructionResult.timeout();
} finally {
// 取消注册回调
callbackRegistry.unregisterCallback(callbackId);
}
}
}

View File

@ -0,0 +1,28 @@
package com.tuoheng.machine.config;
import com.tuoheng.machine.vendor.VendorRegistry;
import com.tuoheng.machine.vendor.dji.DjiVendorConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 设备框架配置类
*/
@Slf4j
@Configuration
public class MachineFrameworkConfig {
/**
* 自动注册所有厂家配置
*/
@Bean
public CommandLineRunner registerVendors(VendorRegistry vendorRegistry, DjiVendorConfig djiVendorConfig) {
return args -> {
// 注册大疆厂家配置
vendorRegistry.registerVendor(djiVendorConfig);
log.info("设备框架初始化完成,已注册厂家: {}", vendorRegistry.getAllVendorTypes());
};
}
}

View File

@ -0,0 +1,257 @@
package com.tuoheng.machine.example;
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.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 设备框架使用示例
*/
@Slf4j
@Component
public class MachineFrameworkUsageExample {
private final MachineCommandManager commandManager;
private final MqttCallbackRegistry mqttCallbackRegistry;
public MachineFrameworkUsageExample(MachineCommandManager commandManager,
MqttCallbackRegistry mqttCallbackRegistry) {
this.commandManager = commandManager;
this.mqttCallbackRegistry = mqttCallbackRegistry;
}
/**
* 示例1绑定设备到厂家
*/
public void example1_bindMachine() {
String sn = "DJI-12345678";
String vendorType = "DJI";
// 绑定设备到大疆厂家
commandManager.bindMachine(sn, vendorType);
log.info("设备已绑定: sn={}, vendorType={}", sn, vendorType);
}
/**
* 示例2更新设备状态通常在心跳中调用
*/
public void example2_updateStates() {
String sn = "DJI-12345678";
// 创建新状态
MachineStates newStates = new MachineStates();
newStates.setDroneState(DroneState.PREPARING);
newStates.setAirportState(AirportState.STANDBY);
newStates.setCoverState(CoverState.OPENED);
newStates.setDrcState(DrcState.EXITED);
// 更新状态
commandManager.updateMachineStates(sn, newStates);
log.info("设备状态已更新: sn={}, states={}", sn, newStates);
}
/**
* 示例3查询设备当前状态
*/
public void example3_queryStates() {
String sn = "DJI-12345678";
// 获取当前状态
MachineStates currentStates = commandManager.getMachineStates(sn);
log.info("设备当前状态: sn={}, droneState={}, airportState={}, coverState={}, drcState={}",
sn,
currentStates.getDroneState(),
currentStates.getAirportState(),
currentStates.getCoverState(),
currentStates.getDrcState());
}
/**
* 示例4查询设备可执行的命令
*/
public void example4_queryAvailableCommands() {
String sn = "DJI-12345678";
// 获取可执行的命令列表
List<CommandType> availableCommands = commandManager.getAvailableCommands(sn);
log.info("设备可执行的命令: sn={}, commands={}", sn, availableCommands);
}
/**
* 示例5检查设备是否正在执行命令
*/
public void example5_checkExecutingCommand() {
String sn = "DJI-12345678";
// 检查是否正在执行命令
boolean isExecuting = commandManager.isExecutingCommand(sn);
CommandType executingCommand = commandManager.getExecutingCommandType(sn);
log.info("设备命令执行状态: sn={}, isExecuting={}, executingCommand=",
sn, isExecuting, executingCommand);
}
/**
* 示例6执行简单命令起飞
*/
public void example6_executeSimpleCommand() {
String sn = "DJI-12345678";
// 执行起飞命令
commandManager.executeCommand(sn, CommandType.TAKE_OFF)
.thenAccept(result -> {
if (result.isSuccess()) {
log.info("起飞命令执行成功: sn={}", sn);
} else {
log.error("起飞命令执行失败: sn={}, error={}", sn, result.getErrorMessage());
}
});
}
/**
* 示例7执行带参数的命令指点飞行
*/
public void example7_executeCommandWithParams() {
String sn = "DJI-12345678";
// 准备命令参数
Map<String, Object> params = new HashMap<>();
params.put("latitude", 39.9042);
params.put("longitude", 116.4074);
params.put("altitude", 100.0);
// 执行指点飞行命令
commandManager.executeCommand(sn, CommandType.POINT_FLY, params)
.thenAccept(result -> {
if (result.isSuccess()) {
log.info("指点飞行命令执行成功: sn={}", sn);
} else {
log.error("指点飞行命令执行失败: sn={}, error={}, failedInstruction={}",
sn, result.getErrorMessage(), result.getFailedInstructionName());
}
});
}
/**
* 示例8注册命令执行监听器
*/
public void example8_registerCommandListener() {
String listenerId = "command-logger";
// 注册命令执行监听器
commandManager.registerCommandListener(listenerId, (sn, result) -> {
log.info("命令执行完成回调: sn={}, commandType={}, success={}, error={}",
sn, result.getCommandType(), result.isSuccess(), result.getErrorMessage());
// 这里可以记录到数据库
// commandResultRepository.save(new CommandResultEntity(sn, result));
});
}
/**
* 示例9注册状态变化监听器
*/
public void example9_registerStateChangeListener() {
String listenerId = "state-logger";
// 注册状态变化监听器
commandManager.registerStateChangeListener(listenerId, (sn, newStates) -> {
log.info("设备状态变化回调: sn={}, droneState={}, airportState={}, coverState={}, drcState={}",
sn,
newStates.getDroneState(),
newStates.getAirportState(),
newStates.getCoverState(),
newStates.getDrcState());
// 这里可以记录到数据库
// stateChangeRepository.save(new StateChangeEntity(sn, newStates));
});
}
/**
* 示例10处理MQTT消息在MQTT消费者中调用
*/
public void example10_handleMqttMessage(String topic, Object messageBody) {
// 当收到MQTT消息时调用回调注册中心处理
mqttCallbackRegistry.handleMessage(topic, messageBody);
// 如果消息包含状态信息更新设备状态
// 这里需要根据实际的消息格式解析
// 示例
// if (topic.endsWith("/state")) {
// String sn = extractSnFromTopic(topic);
// MachineStates newStates = parseStatesFromMessage(messageBody);
// commandManager.updateMachineStates(sn, newStates);
// }
}
/**
* 示例11完整的业务流程
*/
public void example11_completeWorkflow() {
String sn = "DJI-12345678";
// 1. 绑定设备
commandManager.bindMachine(sn, "DJI");
// 2. 设置初始状态
MachineStates initialStates = new MachineStates();
initialStates.setDroneState(DroneState.PREPARING);
initialStates.setAirportState(AirportState.STANDBY);
initialStates.setCoverState(CoverState.OPENED);
initialStates.setDrcState(DrcState.EXITED);
commandManager.updateMachineStates(sn, initialStates);
// 3. 检查是否可以起飞
if (!commandManager.isExecutingCommand(sn)) {
List<CommandType> availableCommands = commandManager.getAvailableCommands(sn);
if (availableCommands.contains(CommandType.TAKE_OFF)) {
// 4. 执行起飞命令
commandManager.executeCommand(sn, CommandType.TAKE_OFF)
.thenAccept(result -> {
if (result.isSuccess()) {
log.info("起飞成功,开始执行任务");
// 5. 起飞成功后可以执行其他命令
executeNextCommand(sn);
} else {
log.error("起飞失败: {}", result.getErrorMessage());
}
});
} else {
log.warn("当前状态不允许起飞");
}
} else {
log.warn("设备正在执行其他命令");
}
}
/**
* 执行下一个命令
*/
private void executeNextCommand(String sn) {
// 等待一段时间后执行返航
try {
Thread.sleep(60000); // 等待60秒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 执行返航命令
commandManager.executeCommand(sn, CommandType.RETURN_HOME)
.thenAccept(result -> {
if (result.isSuccess()) {
log.info("返航成功");
} else {
log.error("返航失败: {}", result.getErrorMessage());
}
});
}
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.guard.airport;
import com.tuoheng.machine.events.AirportEvent;
import com.tuoheng.machine.platform.strategy.PlatformGuard;
import com.tuoheng.machine.status.AirportState;
/**
* Base guard for checking if an machine can go offline; platform implementations extend this.
*/
public abstract class CanOfflineGuard implements PlatformGuard<AirportState, AirportEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.guard.airport;
import com.tuoheng.machine.events.AirportEvent;
import com.tuoheng.machine.platform.strategy.PlatformGuard;
import com.tuoheng.machine.status.AirportState;
/**
* Base guard for checking if an machine can go online; platform implementations extend this.
*/
public abstract class CanOnlineGuard implements PlatformGuard<AirportState, AirportEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.guard.airport;
import com.tuoheng.machine.events.AirportEvent;
import com.tuoheng.machine.platform.strategy.PlatformGuard;
import com.tuoheng.machine.status.AirportState;
/**
* Base guard for checking machine online status; platform implementations extend this.
*/
public abstract class IsAirportOnlineGuard implements PlatformGuard<AirportState, AirportEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.guard.cover;
import com.tuoheng.machine.events.CoverEvent;
import com.tuoheng.machine.platform.strategy.PlatformGuard;
import com.tuoheng.machine.status.CoverState;
/**
* Base guard for closing the cover; platform implementations extend this.
*/
public abstract class CanCloseCoverGuard implements PlatformGuard<CoverState, CoverEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.guard.cover;
import com.tuoheng.machine.events.CoverEvent;
import com.tuoheng.machine.platform.strategy.PlatformGuard;
import com.tuoheng.machine.status.CoverState;
/**
* Base guard for opening the cover; platform implementations extend this.
*/
public abstract class CanOpenCoverGuard implements PlatformGuard<CoverState, CoverEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.guard.cover;
import com.tuoheng.machine.events.CoverEvent;
import com.tuoheng.machine.platform.strategy.PlatformGuard;
import com.tuoheng.machine.status.CoverState;
/**
* Base guard for verifying the cover is closed; platform implementations extend this.
*/
public abstract class IsCoverClosedGuard implements PlatformGuard<CoverState, CoverEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.guard.cover;
import com.tuoheng.machine.events.CoverEvent;
import com.tuoheng.machine.platform.strategy.PlatformGuard;
import com.tuoheng.machine.status.CoverState;
/**
* Base guard for verifying the cover is opened; platform implementations extend this.
*/
public abstract class IsCoverOpenedGuard implements PlatformGuard<CoverState, CoverEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.guard.debug;
import com.tuoheng.machine.events.AirportEvent;
import com.tuoheng.machine.platform.strategy.PlatformGuard;
import com.tuoheng.machine.status.AirportState;
/**
* Base guard for closing debug mode; platform implementations extend this.
*/
public abstract class CanCloseDebugModeGuard implements PlatformGuard<AirportState, AirportEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.guard.debug;
import com.tuoheng.machine.events.AirportEvent;
import com.tuoheng.machine.platform.strategy.PlatformGuard;
import com.tuoheng.machine.status.AirportState;
/**
* Base guard for checking debug mode; platform implementations extend this.
*/
public abstract class IsDebugModeGuard implements PlatformGuard<AirportState, AirportEvent> {
}

View File

@ -1,12 +0,0 @@
package com.tuoheng.machine.guard.debug;
import com.tuoheng.machine.events.AirportEvent;
import com.tuoheng.machine.platform.strategy.PlatformGuard;
import com.tuoheng.machine.status.AirportState;
/**
* Base guard for checking non-debug mode; platform implementations extend this.
*/
public abstract class IsNotDebugModeGuard implements PlatformGuard<AirportState, AirportEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.guard.drc;
import com.tuoheng.machine.events.DrcEvent;
import com.tuoheng.machine.platform.strategy.PlatformGuard;
import com.tuoheng.machine.status.DrcState;
/**
* Base guard for checking if can enter DRC mode; platform implementations extend this.
*/
public abstract class CanEnterGuard implements PlatformGuard<DrcState, DrcEvent> {
}

View File

@ -1,11 +0,0 @@
package com.tuoheng.machine.guard.drc;
import com.tuoheng.machine.events.DrcEvent;
import com.tuoheng.machine.platform.strategy.PlatformGuard;
import com.tuoheng.machine.status.DrcState;
/**
* Base guard for checking if can exit DRC mode; platform implementations extend this.
*/
public abstract class CanExitGuard implements PlatformGuard<DrcState, DrcEvent> {
}

View File

@ -0,0 +1,35 @@
package com.tuoheng.machine.instruction;
/**
* 抽象指令基类提供默认实现
*/
public abstract class AbstractInstruction implements Instruction {
@Override
public boolean canExecute(InstructionContext context) {
// 默认可以执行
return true;
}
@Override
public CallbackConfig getMethodCallbackConfig(InstructionContext context) {
// 默认不需要方法回调
return null;
}
@Override
public CallbackConfig getStateCallbackConfig(InstructionContext context) {
// 默认不需要状态回调
return null;
}
@Override
public long getTimeoutMs() {
return 60000; // 默认60秒
}
@Override
public void onComplete(InstructionContext context, InstructionResult result) {
// 默认空实现
}
}

View File

@ -0,0 +1,91 @@
package com.tuoheng.machine.instruction;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.function.Predicate;
/**
* 回调配置用于方法回调和状态回调
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CallbackConfig {
/**
* 监听的MQTT主题
*/
private String topic;
/**
* 字段路径支持嵌套 "data.status"
*/
private String fieldPath;
/**
* 期望的字段值
*/
private Object expectedValue;
/**
* 自定义判断逻辑如果设置则优先使用此逻辑
*/
private Predicate<Object> customPredicate;
/**
* 是否可以被短路跳过此回调
*/
private boolean canShortCircuit;
/**
* 超时时间毫秒
*/
private long timeoutMs = 30000;
/**
* 判断消息是否匹配
*/
public boolean matches(Object messageBody) {
if (customPredicate != null) {
return customPredicate.test(messageBody);
}
Object fieldValue = extractFieldValue(messageBody, fieldPath);
return expectedValue == null || expectedValue.equals(fieldValue);
}
/**
* 从消息体中提取字段值
*/
private Object extractFieldValue(Object messageBody, String path) {
if (messageBody == null || path == null) {
return null;
}
String[] parts = path.split("\\.");
Object current = messageBody;
for (String part : parts) {
if (current == null) {
return null;
}
if (current instanceof java.util.Map) {
current = ((java.util.Map<?, ?>) current).get(part);
} else {
try {
java.lang.reflect.Field field = current.getClass().getDeclaredField(part);
field.setAccessible(true);
current = field.get(current);
} catch (Exception e) {
return null;
}
}
}
return current;
}
}

View File

@ -0,0 +1,52 @@
package com.tuoheng.machine.instruction;
/**
* 指令接口
* 一个指令包含四个部分
* a. 判断是否可以执行该指令
* b. 执行远程调用如MQTT发送
* c. 等待方法回调并判断该方法是否成功执行
* d. 等待状态回调并判断结果是否OK
*/
public interface Instruction {
/**
* 获取指令名称
*/
String getName();
/**
* a. 判断是否可以执行该指令
*/
boolean canExecute(InstructionContext context);
/**
* b. 执行远程调用如MQTT发送
*/
void executeRemoteCall(InstructionContext context) throws Exception;
/**
* c. 获取方法回调配置可选
* 返回null表示不需要方法回调
*/
CallbackConfig getMethodCallbackConfig(InstructionContext context);
/**
* d. 获取状态回调配置可选
* 返回null表示不需要状态回调
*/
CallbackConfig getStateCallbackConfig(InstructionContext context);
/**
* 获取指令超时时间毫秒
*/
default long getTimeoutMs() {
return 60000; // 默认60秒
}
/**
* 指令执行完成回调无论成功失败都会调用
*/
default void onComplete(InstructionContext context, InstructionResult result) {
// 默认空实现
}
}

View File

@ -0,0 +1,53 @@
package com.tuoheng.machine.instruction;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* 指令执行上下文
*/
@Data
public class InstructionContext {
/**
* 设备SN号
*/
private String sn;
/**
* 厂家类型
*/
private String vendorType;
/**
* 上下文数据用于在指令间传递数据
*/
private Map<String, Object> contextData = new HashMap<>();
/**
* 命令参数
*/
private Map<String, Object> commandParams = new HashMap<>();
public InstructionContext(String sn, String vendorType) {
this.sn = sn;
this.vendorType = vendorType;
}
public void putContextData(String key, Object value) {
contextData.put(key, value);
}
public Object getContextData(String key) {
return contextData.get(key);
}
public void putCommandParam(String key, Object value) {
commandParams.put(key, value);
}
public Object getCommandParam(String key) {
return commandParams.get(key);
}
}

View File

@ -0,0 +1,44 @@
package com.tuoheng.machine.instruction;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 指令执行结果
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class InstructionResult {
/**
* 是否成功
*/
private boolean success;
/**
* 错误信息
*/
private String errorMessage;
/**
* 结果数据
*/
private Object data;
public static InstructionResult success() {
return new InstructionResult(true, null, null);
}
public static InstructionResult success(Object data) {
return new InstructionResult(true, null, data);
}
public static InstructionResult failure(String errorMessage) {
return new InstructionResult(false, errorMessage, null);
}
public static InstructionResult timeout() {
return new InstructionResult(false, "指令执行超时", null);
}
}

View File

@ -0,0 +1,45 @@
package com.tuoheng.machine.mqtt;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.function.Consumer;
/**
* MQTT回调处理器
*/
@Data
@AllArgsConstructor
public class MqttCallbackHandler {
/**
* 回调ID用于取消注册
*/
private String callbackId;
/**
* 监听的主题
*/
private String topic;
/**
* 消息处理器
*/
private Consumer<Object> messageHandler;
/**
* 超时时间毫秒
*/
private long timeoutMs;
/**
* 注册时间
*/
private long registerTime;
/**
* 是否已超时
*/
public boolean isTimeout() {
return System.currentTimeMillis() - registerTime > timeoutMs;
}
}

View File

@ -0,0 +1,125 @@
package com.tuoheng.machine.mqtt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
/**
* MQTT回调注册中心
* 用于注册和管理MQTT消息的回调处理器
*/
@Slf4j
@Component
public class MqttCallbackRegistry {
/**
* 主题 -> 回调处理器列表
*/
private final Map<String, CopyOnWriteArrayList<MqttCallbackHandler>> topicHandlers = new ConcurrentHashMap<>();
/**
* 回调ID -> 回调处理器
*/
private final Map<String, MqttCallbackHandler> handlerMap = new ConcurrentHashMap<>();
/**
* 注册回调
*
* @param topic 监听的主题
* @param messageHandler 消息处理器
* @param timeoutMs 超时时间毫秒
* @return 回调ID用于取消注册
*/
public String registerCallback(String topic, Consumer<Object> messageHandler, long timeoutMs) {
String callbackId = UUID.randomUUID().toString();
MqttCallbackHandler handler = new MqttCallbackHandler(
callbackId,
topic,
messageHandler,
timeoutMs,
System.currentTimeMillis()
);
topicHandlers.computeIfAbsent(topic, k -> new CopyOnWriteArrayList<>()).add(handler);
handlerMap.put(callbackId, handler);
log.debug("注册MQTT回调: callbackId={}, topic={}, timeoutMs={}", callbackId, topic, timeoutMs);
return callbackId;
}
/**
* 取消注册回调
*
* @param callbackId 回调ID
*/
public void unregisterCallback(String callbackId) {
MqttCallbackHandler handler = handlerMap.remove(callbackId);
if (handler != null) {
CopyOnWriteArrayList<MqttCallbackHandler> handlers = topicHandlers.get(handler.getTopic());
if (handlers != null) {
handlers.remove(handler);
if (handlers.isEmpty()) {
topicHandlers.remove(handler.getTopic());
}
}
log.debug("取消注册MQTT回调: callbackId={}, topic={}", callbackId, handler.getTopic());
}
}
/**
* 处理接收到的MQTT消息
*
* @param topic 主题
* @param messageBody 消息体
*/
public void handleMessage(String topic, Object messageBody) {
CopyOnWriteArrayList<MqttCallbackHandler> handlers = topicHandlers.get(topic);
if (handlers == null || handlers.isEmpty()) {
return;
}
log.debug("处理MQTT消息: topic={}, handlerCount={}", topic, handlers.size());
for (MqttCallbackHandler handler : handlers) {
try {
// 检查是否超时
if (handler.isTimeout()) {
log.warn("MQTT回调已超时: callbackId={}, topic={}", handler.getCallbackId(), topic);
unregisterCallback(handler.getCallbackId());
continue;
}
// 执行回调
handler.getMessageHandler().accept(messageBody);
} catch (Exception e) {
log.error("执行MQTT回调失败: callbackId={}, topic={}", handler.getCallbackId(), topic, e);
}
}
}
/**
* 清理超时的回调
*/
public void cleanupTimeoutCallbacks() {
handlerMap.values().removeIf(handler -> {
if (handler.isTimeout()) {
log.warn("清理超时的MQTT回调: callbackId={}, topic={}", handler.getCallbackId(), handler.getTopic());
unregisterCallback(handler.getCallbackId());
return true;
}
return false;
});
}
/**
* 获取当前注册的回调数量
*/
public int getCallbackCount() {
return handlerMap.size();
}
}

View File

@ -0,0 +1,46 @@
package com.tuoheng.machine.state;
/**
* 机巢状态枚举
*/
public enum AirportState {
/**
* 未知状态服务器重启后的初始状态等待第一次心跳同步
*/
UNKNOWN,
/**
* 离线
*/
OFFLINE,
/**
* 在线父状态
*/
ONLINE,
/**
* 待机
*/
STANDBY,
/**
* 进入调试模式中
*/
ENTERING_DEBUG_MODE,
/**
* 调试模式
*/
DEBUG_MODE,
/**
* 退出调试模式中
*/
EXITING_DEBUG_MODE,
/**
* 重启中
*/
REBOOTING
}

View File

@ -1,4 +1,4 @@
package com.tuoheng.machine.status; package com.tuoheng.machine.state;
/** /**
* 舱门状态枚举 * 舱门状态枚举

View File

@ -0,0 +1,31 @@
package com.tuoheng.machine.state;
/**
* 飞行控制模式DRC状态枚举
*/
public enum DrcState {
/**
* 未知状态服务器重启后的初始状态等待第一次心跳同步
*/
UNKNOWN,
/**
* 退出状态DRC模式已退出
*/
EXITED,
/**
* 进入中正在进入DRC模式
*/
ENTERING,
/**
* 进入状态已进入DRC模式
*/
ENTERED,
/**
* 退出中正在退出DRC模式
*/
EXITING
}

View File

@ -0,0 +1,91 @@
package com.tuoheng.machine.state;
/**
* 无人机状态枚举
* 分为准备中 -> 飞行中 -> 返航 三个大状态
*/
public enum DroneState {
/**
* 未知状态服务器重启后的初始状态等待第一次心跳同步
*/
UNKNOWN,
/**
* 离线
*/
OFFLINE,
// ==================== 准备阶段 ====================
/**
* 准备中
*/
PREPARING,
// ==================== 飞行阶段父状态 ====================
/**
* 飞行中父状态
*/
FLYING_PARENT,
/**
* 飞行中
*/
FLYING,
/**
* 急停飞行阶段
*/
EMERGENCY_STOP,
/**
* 到达目的地
*/
ARRIVED,
// ==================== 返航阶段父状态 ====================
/**
* 返航父状态
*/
RETURNING_PARENT,
/**
* 返航中
*/
RETURNING,
/**
* 急停返航阶段
*/
RETURN_EMERGENCY_STOP,
/**
* 返航完成
*/
RETURN_COMPLETED,
// ==================== 指点操作可在飞行和返航阶段使用 ====================
/**
* 指点操作父状态
*/
POINTING_PARENT,
/**
* 准备指点
*/
POINT_PREPARING,
/**
* 指点飞行中
*/
POINT_FLYING,
/**
* 指点完成
*/
POINT_COMPLETED,
/**
* 指点被取消
*/
POINT_CANCELLED
}

View File

@ -0,0 +1,40 @@
package com.tuoheng.machine.state;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 设备的四套大状态
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MachineStates {
/**
* 无人机状态
*/
private DroneState droneState = DroneState.UNKNOWN;
/**
* 机巢状态
*/
private AirportState airportState = AirportState.UNKNOWN;
/**
* 舱门状态
*/
private CoverState coverState = CoverState.UNKNOWN;
/**
* DRC状态
*/
private DrcState drcState = DrcState.UNKNOWN;
/**
* 复制当前状态
*/
public MachineStates copy() {
return new MachineStates(droneState, airportState, coverState, drcState);
}
}

View File

@ -0,0 +1,159 @@
package com.tuoheng.machine.statemachine;
import com.tuoheng.machine.state.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 设备状态管理器
*/
@Slf4j
@Component
public class MachineStateManager {
/**
* SN -> 设备状态
*/
private final Map<String, MachineStates> stateMap = new ConcurrentHashMap<>();
/**
* 状态变化监听器
*/
private final Map<String, StateChangeListener> stateChangeListeners = new ConcurrentHashMap<>();
/**
* 获取设备状态
*/
public MachineStates getStates(String sn) {
return stateMap.computeIfAbsent(sn, k -> new MachineStates());
}
/**
* 设置无人机状态
*/
public void setDroneState(String sn, DroneState newState) {
MachineStates states = getStates(sn);
DroneState oldState = states.getDroneState();
if (oldState != newState) {
states.setDroneState(newState);
log.info("无人机状态变化: sn={}, {} -> {}", sn, oldState, newState);
notifyStateChange(sn, states.copy());
}
}
/**
* 设置机巢状态
*/
public void setAirportState(String sn, AirportState newState) {
MachineStates states = getStates(sn);
AirportState oldState = states.getAirportState();
if (oldState != newState) {
states.setAirportState(newState);
log.info("机巢状态变化: sn={}, {} -> {}", sn, oldState, newState);
notifyStateChange(sn, states.copy());
}
}
/**
* 设置舱门状态
*/
public void setCoverState(String sn, CoverState newState) {
MachineStates states = getStates(sn);
CoverState oldState = states.getCoverState();
if (oldState != newState) {
states.setCoverState(newState);
log.info("舱门状态变化: sn={}, {} -> {}", sn, oldState, newState);
notifyStateChange(sn, states.copy());
}
}
/**
* 设置DRC状态
*/
public void setDrcState(String sn, DrcState newState) {
MachineStates states = getStates(sn);
DrcState oldState = states.getDrcState();
if (oldState != newState) {
states.setDrcState(newState);
log.info("DRC状态变化: sn={}, {} -> {}", sn, oldState, newState);
notifyStateChange(sn, states.copy());
}
}
/**
* 批量更新状态用于心跳同步
*/
public void updateStates(String sn, MachineStates newStates) {
MachineStates currentStates = getStates(sn);
boolean changed = false;
if (currentStates.getDroneState() != newStates.getDroneState()) {
currentStates.setDroneState(newStates.getDroneState());
changed = true;
}
if (currentStates.getAirportState() != newStates.getAirportState()) {
currentStates.setAirportState(newStates.getAirportState());
changed = true;
}
if (currentStates.getCoverState() != newStates.getCoverState()) {
currentStates.setCoverState(newStates.getCoverState());
changed = true;
}
if (currentStates.getDrcState() != newStates.getDrcState()) {
currentStates.setDrcState(newStates.getDrcState());
changed = true;
}
if (changed) {
log.info("设备状态批量更新: sn={}, states={}", sn, currentStates);
notifyStateChange(sn, currentStates.copy());
}
}
/**
* 注册状态变化监听器
*/
public void registerStateChangeListener(String listenerId, StateChangeListener listener) {
stateChangeListeners.put(listenerId, listener);
log.debug("注册状态变化监听器: listenerId={}", listenerId);
}
/**
* 取消注册状态变化监听器
*/
public void unregisterStateChangeListener(String listenerId) {
stateChangeListeners.remove(listenerId);
log.debug("取消注册状态变化监听器: listenerId={}", listenerId);
}
/**
* 通知状态变化
*/
private void notifyStateChange(String sn, MachineStates newStates) {
for (StateChangeListener listener : stateChangeListeners.values()) {
try {
listener.onStateChange(sn, newStates);
} catch (Exception e) {
log.error("状态变化监听器执行失败: sn={}", sn, e);
}
}
}
/**
* 移除设备状态设备下线时调用
*/
public void removeStates(String sn) {
stateMap.remove(sn);
log.info("移除设备状态: sn={}", sn);
}
}

View File

@ -0,0 +1,17 @@
package com.tuoheng.machine.statemachine;
import com.tuoheng.machine.state.MachineStates;
/**
* 状态变化监听器
*/
@FunctionalInterface
public interface StateChangeListener {
/**
* 状态变化回调
*
* @param sn 设备SN号
* @param newStates 新状态
*/
void onStateChange(String sn, MachineStates newStates);
}

View File

@ -0,0 +1,49 @@
package com.tuoheng.machine.vendor;
import com.tuoheng.machine.command.CommandType;
import com.tuoheng.machine.command.Transaction;
import com.tuoheng.machine.state.MachineStates;
import java.util.List;
import java.util.Map;
/**
* 厂家配置接口
* 每个厂家需要实现此接口定义其支持的命令和状态转换规则
*/
public interface VendorConfig {
/**
* 获取厂家类型
*/
String getVendorType();
/**
* 获取厂家名称
*/
String getVendorName();
/**
* 获取指定命令的事务定义
*
* @param commandType 命令类型
* @return 事务定义如果不支持该命令则返回null
*/
Transaction getTransaction(CommandType commandType);
/**
* 判断在当前状态下是否可以执行指定命令
*
* @param currentStates 当前状态
* @param commandType 命令类型
* @return 是否可以执行
*/
boolean canExecuteCommand(MachineStates currentStates, CommandType commandType);
/**
* 获取在当前状态下可以执行的命令列表
*
* @param currentStates 当前状态
* @return 可执行的命令列表
*/
List<CommandType> getAvailableCommands(MachineStates currentStates);
}

View File

@ -0,0 +1,93 @@
package com.tuoheng.machine.vendor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 厂家注册中心
* 管理SN到厂家的映射关系
*/
@Slf4j
@Component
public class VendorRegistry {
/**
* 厂家类型 -> 厂家配置
*/
private final Map<String, VendorConfig> vendorConfigs = new ConcurrentHashMap<>();
/**
* SN -> 厂家类型
*/
private final Map<String, String> snToVendorMap = new ConcurrentHashMap<>();
/**
* 注册厂家配置
*/
public void registerVendor(VendorConfig vendorConfig) {
vendorConfigs.put(vendorConfig.getVendorType(), vendorConfig);
log.info("注册厂家配置: vendorType={}, vendorName={}",
vendorConfig.getVendorType(), vendorConfig.getVendorName());
}
/**
* 绑定SN到厂家
*/
public void bindSnToVendor(String sn, String vendorType) {
if (!vendorConfigs.containsKey(vendorType)) {
throw new IllegalArgumentException("未注册的厂家类型: " + vendorType);
}
snToVendorMap.put(sn, vendorType);
log.debug("绑定SN到厂家: sn={}, vendorType={}", sn, vendorType);
}
/**
* 解绑SN
*/
public void unbindSn(String sn) {
snToVendorMap.remove(sn);
log.debug("解绑SN: sn={}", sn);
}
/**
* 获取SN对应的厂家类型
*/
public String getVendorType(String sn) {
return snToVendorMap.get(sn);
}
/**
* 获取SN对应的厂家配置
*/
public VendorConfig getVendorConfig(String sn) {
String vendorType = getVendorType(sn);
if (vendorType == null) {
return null;
}
return vendorConfigs.get(vendorType);
}
/**
* 根据厂家类型获取厂家配置
*/
public VendorConfig getVendorConfigByType(String vendorType) {
return vendorConfigs.get(vendorType);
}
/**
* 判断SN是否已绑定厂家
*/
public boolean isSnBound(String sn) {
return snToVendorMap.containsKey(sn);
}
/**
* 获取所有已注册的厂家类型
*/
public java.util.Set<String> getAllVendorTypes() {
return vendorConfigs.keySet();
}
}

View File

@ -0,0 +1,198 @@
package com.tuoheng.machine.vendor.dji;
import com.tuoheng.machine.command.CommandType;
import com.tuoheng.machine.command.Transaction;
import com.tuoheng.machine.state.*;
import com.tuoheng.machine.vendor.VendorConfig;
import com.tuoheng.machine.vendor.dji.instruction.*;
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 DjiVendorConfig implements VendorConfig {
private final Map<CommandType, Transaction> transactionMap = new HashMap<>();
public DjiVendorConfig() {
initTransactions();
}
@Override
public String getVendorType() {
return "DJI";
}
@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();
CoverState coverState = currentStates.getCoverState();
switch (commandType) {
case TAKE_OFF:
// 准备中状态可以起飞
return droneState == DroneState.PREPARING;
case LAND:
// 飞行中到达目的地急停状态可以降落
return droneState == DroneState.FLYING ||
droneState == DroneState.ARRIVED ||
droneState == DroneState.EMERGENCY_STOP;
case RETURN_HOME:
// 飞行中到达目的地急停指点飞行中可以返航
return droneState == DroneState.FLYING ||
droneState == DroneState.ARRIVED ||
droneState == DroneState.EMERGENCY_STOP ||
droneState == DroneState.POINT_FLYING;
case EMERGENCY_STOP:
// 飞行中返航中指点飞行中可以急停
return droneState == DroneState.FLYING ||
droneState == DroneState.RETURNING ||
droneState == DroneState.POINT_FLYING;
case RESUME_FLIGHT:
// 急停状态可以继续飞行
return droneState == DroneState.EMERGENCY_STOP ||
droneState == DroneState.RETURN_EMERGENCY_STOP;
case POINT_FLY:
// 飞行中到达目的地返航中可以指点飞行
return droneState == DroneState.FLYING ||
droneState == DroneState.ARRIVED ||
droneState == DroneState.RETURNING;
case CANCEL_POINT:
// 指点飞行中可以取消指点
return droneState == DroneState.POINT_FLYING;
case START_MISSION:
// 准备中状态可以开始航线任务
return droneState == DroneState.PREPARING;
case OPEN_COVER:
// 舱门关闭状态可以打开
return coverState == CoverState.CLOSED;
case CLOSE_COVER:
// 舱门打开状态可以关闭
return coverState == CoverState.OPENED;
case ENTER_DEBUG_MODE:
// 待机状态可以进入调试模式
return airportState == AirportState.STANDBY;
case EXIT_DEBUG_MODE:
// 调试模式可以退出
return airportState == AirportState.DEBUG_MODE;
case REBOOT_AIRPORT:
// 在线状态可以重启
return airportState == AirportState.ONLINE ||
airportState == AirportState.STANDBY;
default:
return false;
}
}
@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 takeOffTransaction = new Transaction("起飞", CommandType.TAKE_OFF)
.addInstruction(new DjiTakeOffInstruction())
.setTimeout(90000);
transactionMap.put(CommandType.TAKE_OFF, takeOffTransaction);
// 降落命令
Transaction landTransaction = new Transaction("降落", CommandType.LAND)
.addInstruction(new DjiLandInstruction())
.setTimeout(90000);
transactionMap.put(CommandType.LAND, landTransaction);
// 返航命令
Transaction returnHomeTransaction = new Transaction("返航", CommandType.RETURN_HOME)
.addInstruction(new DjiReturnHomeInstruction())
.setTimeout(120000);
transactionMap.put(CommandType.RETURN_HOME, returnHomeTransaction);
// 急停命令
Transaction emergencyStopTransaction = new Transaction("急停", CommandType.EMERGENCY_STOP)
.addInstruction(new DjiEmergencyStopInstruction())
.setTimeout(30000);
transactionMap.put(CommandType.EMERGENCY_STOP, emergencyStopTransaction);
// 继续飞行命令
Transaction resumeFlightTransaction = new Transaction("继续飞行", CommandType.RESUME_FLIGHT)
.addInstruction(new DjiResumeFlightInstruction())
.setTimeout(60000);
transactionMap.put(CommandType.RESUME_FLIGHT, resumeFlightTransaction);
// 指点飞行命令
Transaction pointFlyTransaction = new Transaction("指点飞行", CommandType.POINT_FLY)
.addInstruction(new DjiPointFlyInstruction())
.setTimeout(90000);
transactionMap.put(CommandType.POINT_FLY, pointFlyTransaction);
// 取消指点命令
Transaction cancelPointTransaction = new Transaction("取消指点", CommandType.CANCEL_POINT)
.addInstruction(new DjiCancelPointInstruction())
.setTimeout(30000);
transactionMap.put(CommandType.CANCEL_POINT, cancelPointTransaction);
// 开始航线任务命令
Transaction startMissionTransaction = new Transaction("开始航线任务", CommandType.START_MISSION)
.addInstruction(new DjiStartMissionInstruction())
.setTimeout(120000);
transactionMap.put(CommandType.START_MISSION, startMissionTransaction);
// 打开舱门命令
Transaction openCoverTransaction = new Transaction("打开舱门", CommandType.OPEN_COVER)
.addInstruction(new DjiOpenCoverInstruction())
.setTimeout(60000);
transactionMap.put(CommandType.OPEN_COVER, openCoverTransaction);
// 关闭舱门命令
Transaction closeCoverTransaction = new Transaction("关闭舱门", CommandType.CLOSE_COVER)
.addInstruction(new DjiCloseCoverInstruction())
.setTimeout(60000);
transactionMap.put(CommandType.CLOSE_COVER, closeCoverTransaction);
log.info("大疆厂家配置初始化完成,共配置{}个命令", transactionMap.size());
}
}

View File

@ -0,0 +1,59 @@
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 DjiCancelPointInstruction extends AbstractInstruction {
@Override
public String getName() {
return "DJI_CANCEL_POINT";
}
@Override
public void executeRemoteCall(InstructionContext context) throws Exception {
String sn = context.getSn();
log.info("发送大疆取消指点指令: sn={}", sn);
String topic = "dji/" + sn + "/command";
String payload = "{\"cmd\":\"cancelPoint\"}";
log.debug("MQTT发送: topic={}, payload={}", topic, payload);
}
@Override
public CallbackConfig getMethodCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/response")
.fieldPath("cmd")
.expectedValue("cancelPoint")
.canShortCircuit(false)
.timeoutMs(10000)
.build();
}
@Override
public CallbackConfig getStateCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/state")
.fieldPath("droneState")
.expectedValue("POINT_CANCELLED")
.canShortCircuit(false)
.timeoutMs(30000)
.build();
}
@Override
public long getTimeoutMs() {
return 30000;
}
}

View File

@ -0,0 +1,59 @@
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 DjiCloseCoverInstruction extends AbstractInstruction {
@Override
public String getName() {
return "DJI_CLOSE_COVER";
}
@Override
public void executeRemoteCall(InstructionContext context) throws Exception {
String sn = context.getSn();
log.info("发送大疆关闭舱门指令: sn={}", sn);
String topic = "dji/" + sn + "/command";
String payload = "{\"cmd\":\"closeCover\"}";
log.debug("MQTT发送: topic={}, payload={}", topic, payload);
}
@Override
public CallbackConfig getMethodCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/response")
.fieldPath("cmd")
.expectedValue("closeCover")
.canShortCircuit(false)
.timeoutMs(10000)
.build();
}
@Override
public CallbackConfig getStateCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/state")
.fieldPath("coverState")
.expectedValue("CLOSED")
.canShortCircuit(false)
.timeoutMs(60000)
.build();
}
@Override
public long getTimeoutMs() {
return 60000;
}
}

View File

@ -0,0 +1,62 @@
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 DjiEmergencyStopInstruction extends AbstractInstruction {
@Override
public String getName() {
return "DJI_EMERGENCY_STOP";
}
@Override
public void executeRemoteCall(InstructionContext context) throws Exception {
String sn = context.getSn();
log.info("发送大疆急停指令: sn={}", sn);
String topic = "dji/" + sn + "/command";
String payload = "{\"cmd\":\"emergencyStop\"}";
log.debug("MQTT发送: topic={}, payload={}", topic, payload);
}
@Override
public CallbackConfig getMethodCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/response")
.fieldPath("cmd")
.expectedValue("emergencyStop")
.canShortCircuit(false)
.timeoutMs(5000)
.build();
}
@Override
public CallbackConfig getStateCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/state")
.fieldPath("droneState")
.customPredicate(state -> {
// 急停状态可能是 EMERGENCY_STOP RETURN_EMERGENCY_STOP
return "EMERGENCY_STOP".equals(state) || "RETURN_EMERGENCY_STOP".equals(state);
})
.canShortCircuit(false)
.timeoutMs(30000)
.build();
}
@Override
public long getTimeoutMs() {
return 30000;
}
}

View File

@ -0,0 +1,60 @@
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 DjiLandInstruction extends AbstractInstruction {
@Override
public String getName() {
return "DJI_LAND";
}
@Override
public void executeRemoteCall(InstructionContext context) throws Exception {
String sn = context.getSn();
log.info("发送大疆降落指令: sn={}", sn);
// TODO: 实际的MQTT发送逻辑
String topic = "dji/" + sn + "/command";
String payload = "{\"cmd\":\"land\"}";
log.debug("MQTT发送: topic={}, payload={}", topic, payload);
}
@Override
public CallbackConfig getMethodCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/response")
.fieldPath("cmd")
.expectedValue("land")
.canShortCircuit(false)
.timeoutMs(10000)
.build();
}
@Override
public CallbackConfig getStateCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/state")
.fieldPath("droneState")
.expectedValue("PREPARING")
.canShortCircuit(false)
.timeoutMs(60000)
.build();
}
@Override
public long getTimeoutMs() {
return 90000;
}
}

View File

@ -0,0 +1,59 @@
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 DjiOpenCoverInstruction extends AbstractInstruction {
@Override
public String getName() {
return "DJI_OPEN_COVER";
}
@Override
public void executeRemoteCall(InstructionContext context) throws Exception {
String sn = context.getSn();
log.info("发送大疆打开舱门指令: sn={}", sn);
String topic = "dji/" + sn + "/command";
String payload = "{\"cmd\":\"openCover\"}";
log.debug("MQTT发送: topic={}, payload={}", topic, payload);
}
@Override
public CallbackConfig getMethodCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/response")
.fieldPath("cmd")
.expectedValue("openCover")
.canShortCircuit(false)
.timeoutMs(10000)
.build();
}
@Override
public CallbackConfig getStateCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/state")
.fieldPath("coverState")
.expectedValue("OPENED")
.canShortCircuit(false)
.timeoutMs(60000)
.build();
}
@Override
public long getTimeoutMs() {
return 60000;
}
}

View File

@ -0,0 +1,64 @@
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 DjiPointFlyInstruction extends AbstractInstruction {
@Override
public String getName() {
return "DJI_POINT_FLY";
}
@Override
public void executeRemoteCall(InstructionContext context) throws Exception {
String sn = context.getSn();
Object latitude = context.getCommandParam("latitude");
Object longitude = context.getCommandParam("longitude");
Object altitude = context.getCommandParam("altitude");
log.info("发送大疆指点飞行指令: sn={}, lat={}, lon={}, alt={}", sn, latitude, longitude, altitude);
String topic = "dji/" + sn + "/command";
String payload = String.format("{\"cmd\":\"pointFly\",\"latitude\":%s,\"longitude\":%s,\"altitude\":%s}",
latitude, longitude, altitude);
log.debug("MQTT发送: topic={}, payload={}", topic, payload);
}
@Override
public CallbackConfig getMethodCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/response")
.fieldPath("cmd")
.expectedValue("pointFly")
.canShortCircuit(false)
.timeoutMs(10000)
.build();
}
@Override
public CallbackConfig getStateCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/state")
.fieldPath("droneState")
.expectedValue("POINT_FLYING")
.canShortCircuit(false)
.timeoutMs(30000)
.build();
}
@Override
public long getTimeoutMs() {
return 90000;
}
}

View File

@ -0,0 +1,62 @@
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 DjiResumeFlightInstruction extends AbstractInstruction {
@Override
public String getName() {
return "DJI_RESUME_FLIGHT";
}
@Override
public void executeRemoteCall(InstructionContext context) throws Exception {
String sn = context.getSn();
log.info("发送大疆继续飞行指令: sn={}", sn);
String topic = "dji/" + sn + "/command";
String payload = "{\"cmd\":\"resumeFlight\"}";
log.debug("MQTT发送: topic={}, payload={}", topic, payload);
}
@Override
public CallbackConfig getMethodCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/response")
.fieldPath("cmd")
.expectedValue("resumeFlight")
.canShortCircuit(false)
.timeoutMs(10000)
.build();
}
@Override
public CallbackConfig getStateCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/state")
.fieldPath("droneState")
.customPredicate(state -> {
// 继续飞行后可能变为 FLYING RETURNING
return "FLYING".equals(state) || "RETURNING".equals(state);
})
.canShortCircuit(false)
.timeoutMs(30000)
.build();
}
@Override
public long getTimeoutMs() {
return 60000;
}
}

View File

@ -0,0 +1,59 @@
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 DjiReturnHomeInstruction extends AbstractInstruction {
@Override
public String getName() {
return "DJI_RETURN_HOME";
}
@Override
public void executeRemoteCall(InstructionContext context) throws Exception {
String sn = context.getSn();
log.info("发送大疆返航指令: sn={}", sn);
String topic = "dji/" + sn + "/command";
String payload = "{\"cmd\":\"returnHome\"}";
log.debug("MQTT发送: topic={}, payload={}", topic, payload);
}
@Override
public CallbackConfig getMethodCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/response")
.fieldPath("cmd")
.expectedValue("returnHome")
.canShortCircuit(false)
.timeoutMs(10000)
.build();
}
@Override
public CallbackConfig getStateCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/state")
.fieldPath("droneState")
.expectedValue("RETURNING")
.canShortCircuit(false)
.timeoutMs(60000)
.build();
}
@Override
public long getTimeoutMs() {
return 120000;
}
}

View File

@ -0,0 +1,61 @@
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 DjiStartMissionInstruction extends AbstractInstruction {
@Override
public String getName() {
return "DJI_START_MISSION";
}
@Override
public void executeRemoteCall(InstructionContext context) throws Exception {
String sn = context.getSn();
Object missionId = context.getCommandParam("missionId");
log.info("发送大疆开始航线任务指令: sn={}, missionId={}", sn, missionId);
String topic = "dji/" + sn + "/command";
String payload = String.format("{\"cmd\":\"startMission\",\"missionId\":\"%s\"}", missionId);
log.debug("MQTT发送: topic={}, payload={}", topic, payload);
}
@Override
public CallbackConfig getMethodCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/response")
.fieldPath("cmd")
.expectedValue("startMission")
.canShortCircuit(false)
.timeoutMs(10000)
.build();
}
@Override
public CallbackConfig getStateCallbackConfig(InstructionContext context) {
String sn = context.getSn();
return CallbackConfig.builder()
.topic("dji/" + sn + "/state")
.fieldPath("droneState")
.expectedValue("FLYING")
.canShortCircuit(false)
.timeoutMs(60000)
.build();
}
@Override
public long getTimeoutMs() {
return 120000;
}
}

View File

@ -0,0 +1,65 @@
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 DjiTakeOffInstruction extends AbstractInstruction {
@Override
public String getName() {
return "DJI_TAKE_OFF";
}
@Override
public void executeRemoteCall(InstructionContext context) throws Exception {
String sn = context.getSn();
log.info("发送大疆起飞指令: sn={}", sn);
// TODO: 实际的MQTT发送逻辑
// 示例mqttClient.publish("dji/" + sn + "/command", "{\"cmd\":\"takeoff\"}");
// 这里是示例代码实际使用时需要替换为真实的MQTT发送逻辑
String topic = "dji/" + sn + "/command";
String payload = "{\"cmd\":\"takeoff\"}";
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("takeoff")
.canShortCircuit(false) // 不可短路必须等待响应
.timeoutMs(10000) // 10秒超时
.build();
}
@Override
public CallbackConfig getStateCallbackConfig(InstructionContext context) {
String sn = context.getSn();
// 状态回调等待无人机状态变为飞行中
return CallbackConfig.builder()
.topic("dji/" + sn + "/state")
.fieldPath("droneState")
.expectedValue("FLYING")
.canShortCircuit(false) // 不可短路必须等待状态变化
.timeoutMs(60000) // 60秒超时
.build();
}
@Override
public long getTimeoutMs() {
return 90000; // 90秒总超时
}
}

View File

@ -0,0 +1,12 @@
package com.tuoheng.old.action.airport;
import com.tuoheng.old.events.AirportEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.AirportState;
/**
* Base action for old offline handling; platform implementations extend this.
*/
public abstract class OfflineAction implements PlatformAction<AirportState, AirportEvent> {
}

View File

@ -0,0 +1,12 @@
package com.tuoheng.old.action.airport;
import com.tuoheng.old.events.AirportEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.AirportState;
/**
* Base action for old online handling; platform implementations extend this.
*/
public abstract class OnlineAction implements PlatformAction<AirportState, AirportEvent> {
}

View File

@ -0,0 +1,12 @@
package com.tuoheng.old.action.cover;
import com.tuoheng.old.events.CoverEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.CoverState;
/**
* Base action for closing the cover; platform implementations extend this.
*/
public abstract class CloseCoverAction implements PlatformAction<CoverState, CoverEvent> {
}

View File

@ -0,0 +1,12 @@
package com.tuoheng.old.action.cover;
import com.tuoheng.old.events.CoverEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.CoverState;
/**
* Base action for cover-closed handling; platform implementations extend this.
*/
public abstract class CoverClosedAction implements PlatformAction<CoverState, CoverEvent> {
}

View File

@ -0,0 +1,12 @@
package com.tuoheng.old.action.cover;
import com.tuoheng.old.events.CoverEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.CoverState;
/**
* Base action for cover-opened handling; platform implementations extend this.
*/
public abstract class CoverOpenedAction implements PlatformAction<CoverState, CoverEvent> {
}

View File

@ -0,0 +1,12 @@
package com.tuoheng.old.action.cover;
import com.tuoheng.old.events.CoverEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.CoverState;
/**
* Base action for opening the cover; platform implementations extend this.
*/
public abstract class OpenCoverAction implements PlatformAction<CoverState, CoverEvent> {
}

View File

@ -0,0 +1,12 @@
package com.tuoheng.old.action.debug;
import com.tuoheng.old.events.AirportEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.AirportState;
/**
* Base action for closing debug mode; platform implementations extend this.
*/
public abstract class CloseDebugModeAction implements PlatformAction<AirportState, AirportEvent> {
}

View File

@ -0,0 +1,12 @@
package com.tuoheng.old.action.debug;
import com.tuoheng.old.events.AirportEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.AirportState;
/**
* Base action for opening debug mode; platform implementations extend this.
*/
public abstract class OpenDebugModeAction implements PlatformAction<AirportState, AirportEvent> {
}

View File

@ -0,0 +1,11 @@
package com.tuoheng.old.action.drc;
import com.tuoheng.old.events.DrcEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.DrcState;
/**
* Base action for DRC enter handling; platform implementations extend this.
*/
public abstract class EnterAction implements PlatformAction<DrcState, DrcEvent> {
}

View File

@ -0,0 +1,11 @@
package com.tuoheng.old.action.drc;
import com.tuoheng.old.events.DrcEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.DrcState;
/**
* Base action for DRC entered handling; platform implementations extend this.
*/
public abstract class EnteredAction implements PlatformAction<DrcState, DrcEvent> {
}

View File

@ -0,0 +1,11 @@
package com.tuoheng.old.action.drc;
import com.tuoheng.old.events.DrcEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.DrcState;
/**
* Base action for DRC exit handling; platform implementations extend this.
*/
public abstract class ExitAction implements PlatformAction<DrcState, DrcEvent> {
}

View File

@ -0,0 +1,11 @@
package com.tuoheng.old.action.drc;
import com.tuoheng.old.events.DrcEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.DrcState;
/**
* Base action for DRC exited handling; platform implementations extend this.
*/
public abstract class ExitedAction implements PlatformAction<DrcState, DrcEvent> {
}

View File

@ -0,0 +1,11 @@
package com.tuoheng.old.action.drone;
import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.DroneState;
/**
* Base action for drone arrive at destination handling; platform implementations extend this.
*/
public abstract class ArriveAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,8 +1,8 @@
package com.tuoheng.machine.action.drone; package com.tuoheng.old.action.drone;
import com.tuoheng.machine.events.DroneEvent; import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction; import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState; import com.tuoheng.old.status.DroneState;
/** /**
* Base action for drone cancel point operation handling; platform implementations extend this. * Base action for drone cancel point operation handling; platform implementations extend this.

View File

@ -0,0 +1,11 @@
package com.tuoheng.old.action.drone;
import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.DroneState;
/**
* Base action for drone emergency stop handling; platform implementations extend this.
*/
public abstract class EmergencyStopAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -0,0 +1,11 @@
package com.tuoheng.old.action.drone;
import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.DroneState;
/**
* Base action for drone offline handling; platform implementations extend this.
*/
public abstract class OfflineAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,8 +1,8 @@
package com.tuoheng.machine.action.drone; package com.tuoheng.old.action.drone;
import com.tuoheng.machine.events.DroneEvent; import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction; import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState; import com.tuoheng.old.status.DroneState;
/** /**
* Base action for drone point flying completed handling; platform implementations extend this. * Base action for drone point flying completed handling; platform implementations extend this.

View File

@ -1,8 +1,8 @@
package com.tuoheng.machine.action.drone; package com.tuoheng.old.action.drone;
import com.tuoheng.machine.events.DroneEvent; import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction; import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState; import com.tuoheng.old.status.DroneState;
/** /**
* Base action for drone point prepare completed handling; platform implementations extend this. * Base action for drone point prepare completed handling; platform implementations extend this.

View File

@ -0,0 +1,11 @@
package com.tuoheng.old.action.drone;
import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.DroneState;
/**
* Base action for drone point to flying handling; platform implementations extend this.
*/
public abstract class PointToFlyingAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -0,0 +1,11 @@
package com.tuoheng.old.action.drone;
import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.DroneState;
/**
* Base action for drone point to return handling; platform implementations extend this.
*/
public abstract class PointToReturnAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,8 +1,8 @@
package com.tuoheng.machine.action.drone; package com.tuoheng.old.action.drone;
import com.tuoheng.machine.events.DroneEvent; import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction; import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState; import com.tuoheng.old.status.DroneState;
/** /**
* Base action for drone prepare completed handling; platform implementations extend this. * Base action for drone prepare completed handling; platform implementations extend this.

View File

@ -0,0 +1,11 @@
package com.tuoheng.old.action.drone;
import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.DroneState;
/**
* Base action for drone resume flying handling; platform implementations extend this.
*/
public abstract class ResumeFlyingAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -0,0 +1,11 @@
package com.tuoheng.old.action.drone;
import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.DroneState;
/**
* Base action for drone resume return handling; platform implementations extend this.
*/
public abstract class ResumeReturnAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,8 +1,8 @@
package com.tuoheng.machine.action.drone; package com.tuoheng.old.action.drone;
import com.tuoheng.machine.events.DroneEvent; import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction; import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState; import com.tuoheng.old.status.DroneState;
/** /**
* Base action for drone return completed handling; platform implementations extend this. * Base action for drone return completed handling; platform implementations extend this.

View File

@ -1,8 +1,8 @@
package com.tuoheng.machine.action.drone; package com.tuoheng.old.action.drone;
import com.tuoheng.machine.events.DroneEvent; import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction; import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState; import com.tuoheng.old.status.DroneState;
/** /**
* Base action for drone return emergency stop handling; platform implementations extend this. * Base action for drone return emergency stop handling; platform implementations extend this.

View File

@ -0,0 +1,11 @@
package com.tuoheng.old.action.drone;
import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.old.status.DroneState;
/**
* Base action for drone start flying handling; platform implementations extend this.
*/
public abstract class StartFlyingAction implements PlatformAction<DroneState, DroneEvent> {
}

View File

@ -1,8 +1,8 @@
package com.tuoheng.machine.action.drone; package com.tuoheng.old.action.drone;
import com.tuoheng.machine.events.DroneEvent; import com.tuoheng.old.events.DroneEvent;
import com.tuoheng.machine.platform.strategy.PlatformAction; import com.tuoheng.old.platform.strategy.PlatformAction;
import com.tuoheng.machine.status.DroneState; import com.tuoheng.old.status.DroneState;
/** /**
* Base action for drone start pointing operation handling; platform implementations extend this. * Base action for drone start pointing operation handling; platform implementations extend this.

Some files were not shown because too many files have changed in this diff Show More