添加新版状态机
This commit is contained in:
parent
4a3b9dc2ea
commit
9e6eacaf72
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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. 添加性能监控和统计
|
||||||
|
|
||||||
|
## 联系方式
|
||||||
|
|
||||||
|
如有问题或建议,请联系开发团队。
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.tuoheng.machine.command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令执行监听器
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface CommandExecutionListener {
|
||||||
|
/**
|
||||||
|
* 命令执行完成回调
|
||||||
|
*
|
||||||
|
* @param sn 设备SN号
|
||||||
|
* @param result 命令执行结果
|
||||||
|
*/
|
||||||
|
void onCommandComplete(String sn, CommandResult result);
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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> {
|
|
||||||
}
|
|
||||||
|
|
@ -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) {
|
||||||
|
// 默认空实现
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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) {
|
||||||
|
// 默认空实现
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.tuoheng.machine.status;
|
package com.tuoheng.machine.state;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 舱门状态枚举
|
* 舱门状态枚举
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.tuoheng.machine.state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 飞行控制模式(DRC)状态枚举
|
||||||
|
*/
|
||||||
|
public enum DrcState {
|
||||||
|
/**
|
||||||
|
* 未知状态(服务器重启后的初始状态,等待第一次心跳同步)
|
||||||
|
*/
|
||||||
|
UNKNOWN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出状态(DRC模式已退出)
|
||||||
|
*/
|
||||||
|
EXITED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进入中(正在进入DRC模式)
|
||||||
|
*/
|
||||||
|
ENTERING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进入状态(已进入DRC模式)
|
||||||
|
*/
|
||||||
|
ENTERED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出中(正在退出DRC模式)
|
||||||
|
*/
|
||||||
|
EXITING
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiCancelPointInstruction.java
vendored
Normal file
59
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiCancelPointInstruction.java
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiCloseCoverInstruction.java
vendored
Normal file
59
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiCloseCoverInstruction.java
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiEmergencyStopInstruction.java
vendored
Normal file
62
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiEmergencyStopInstruction.java
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiLandInstruction.java
vendored
Normal file
60
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiLandInstruction.java
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiOpenCoverInstruction.java
vendored
Normal file
59
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiOpenCoverInstruction.java
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiPointFlyInstruction.java
vendored
Normal file
64
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiPointFlyInstruction.java
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiResumeFlightInstruction.java
vendored
Normal file
62
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiResumeFlightInstruction.java
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiReturnHomeInstruction.java
vendored
Normal file
59
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiReturnHomeInstruction.java
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiStartMissionInstruction.java
vendored
Normal file
61
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiStartMissionInstruction.java
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
65
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiTakeOffInstruction.java
vendored
Normal file
65
src/main/java/com/tuoheng/machine/vendor/dji/instruction/DjiTakeOffInstruction.java
vendored
Normal 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秒总超时
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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> {
|
||||||
|
}
|
||||||
|
|
@ -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
Loading…
Reference in New Issue