添加状态机

This commit is contained in:
孙小云 2025-12-15 16:27:55 +08:00
parent 6d55dbfea5
commit 6501986e3d
49 changed files with 4543 additions and 0 deletions

30
pom.xml
View File

@ -32,6 +32,18 @@
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>3.2.0</version>
</dependency>
<!-- Spring Context 依赖,用于 AnnotationConfigApplicationContext -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- 显式声明 commons-lang3 版本以解决 NoSuchMethodError -->
<dependency>
<groupId>org.apache.commons</groupId>
@ -45,6 +57,24 @@
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.4.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.4.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.4.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,20 @@
package com.tuoheng.status.airport.action.airport;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.stereotype.Component;
/**
* 机巢离线动作
*/
@Component
public class OfflineAction implements Action<AirportState, AirportEvent> {
@Override
public void execute(StateContext<AirportState, AirportEvent> stateContext) {
System.out.println("执行机巢离线操作");
// TODO: 实现机巢离线的具体逻辑
}
}

View File

@ -0,0 +1,18 @@
package com.tuoheng.status.airport.action.airport;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.guard.Guard;
import org.springframework.stereotype.Component;
@Component
public class OnlineAction implements Action<AirportState, AirportEvent> {
@Override
public void execute(StateContext<AirportState, AirportEvent> stateContext) {
}
}

View File

@ -0,0 +1,20 @@
package com.tuoheng.status.airport.action.cover;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.stereotype.Component;
/**
* 关舱动作
*/
@Component
public class CloseCoverAction implements Action<AirportState, AirportEvent> {
@Override
public void execute(StateContext<AirportState, AirportEvent> stateContext) {
System.out.println("执行关舱操作");
// TODO: 发送 cover_close 指令
}
}

View File

@ -0,0 +1,21 @@
package com.tuoheng.status.airport.action.cover;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.stereotype.Component;
/**
* 舱门关闭完成动作
* 处理舱门完全关闭后的逻辑
*/
@Component
public class CoverClosedAction implements Action<AirportState, AirportEvent> {
@Override
public void execute(StateContext<AirportState, AirportEvent> stateContext) {
System.out.println("舱门已完全关闭,执行后续处理");
// TODO: 舱门关闭完成后的处理逻辑例如记录日志自动关闭调试模式等
}
}

View File

@ -0,0 +1,21 @@
package com.tuoheng.status.airport.action.cover;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.stereotype.Component;
/**
* 舱门打开完成动作
* 处理舱门完全打开后的逻辑
*/
@Component
public class CoverOpenedAction implements Action<AirportState, AirportEvent> {
@Override
public void execute(StateContext<AirportState, AirportEvent> stateContext) {
System.out.println("舱门已完全打开,执行后续处理");
// TODO: 舱门打开完成后的处理逻辑例如记录日志通知相关模块等
}
}

View File

@ -0,0 +1,20 @@
package com.tuoheng.status.airport.action.cover;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.stereotype.Component;
/**
* 开舱动作
*/
@Component
public class OpenCoverAction implements Action<AirportState, AirportEvent> {
@Override
public void execute(StateContext<AirportState, AirportEvent> stateContext) {
System.out.println("执行开舱操作");
// TODO: 发送 cover_open 指令
}
}

View File

@ -0,0 +1,20 @@
package com.tuoheng.status.airport.action.debug;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.stereotype.Component;
/**
* 关闭调试模式动作
*/
@Component
public class CloseDebugModeAction implements Action<AirportState, AirportEvent> {
@Override
public void execute(StateContext<AirportState, AirportEvent> stateContext) {
System.out.println("执行关闭调试模式操作");
// TODO: 发送 debug_mode_close 指令
}
}

View File

@ -0,0 +1,20 @@
package com.tuoheng.status.airport.action.debug;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.stereotype.Component;
/**
* 开启调试模式动作
*/
@Component
public class OpenDebugModeAction implements Action<AirportState, AirportEvent> {
@Override
public void execute(StateContext<AirportState, AirportEvent> stateContext) {
System.out.println("执行开启调试模式操作");
// TODO: 发送 debug_mode_open 指令
}
}

View File

@ -0,0 +1,20 @@
package com.tuoheng.status.airport.action.reboot;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.stereotype.Component;
/**
* 机巢重启动作
*/
@Component
public class RebootAction implements Action<AirportState, AirportEvent> {
@Override
public void execute(StateContext<AirportState, AirportEvent> stateContext) {
System.out.println("执行机巢重启操作");
// TODO: 发送 device_reboot 指令
}
}

View File

@ -0,0 +1,21 @@
package com.tuoheng.status.airport.action.reboot;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.stereotype.Component;
/**
* 机巢重启完成动作
* 处理机巢重启完成后的逻辑
*/
@Component
public class RebootCompletedAction implements Action<AirportState, AirportEvent> {
@Override
public void execute(StateContext<AirportState, AirportEvent> stateContext) {
System.out.println("机巢重启完成,执行初始化处理");
// TODO: 机巢重启完成后的处理逻辑例如重新初始化恢复状态等
}
}

View File

@ -0,0 +1,187 @@
package com.tuoheng.status.airport.config;
import com.tuoheng.status.airport.action.airport.OfflineAction;
import com.tuoheng.status.airport.action.airport.OnlineAction;
import com.tuoheng.status.airport.action.debug.CloseDebugModeAction;
import com.tuoheng.status.airport.action.debug.OpenDebugModeAction;
import com.tuoheng.status.airport.action.reboot.RebootAction;
import com.tuoheng.status.airport.action.reboot.RebootCompletedAction;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.guard.airport.CanOfflineGuard;
import com.tuoheng.status.airport.guard.airport.CanOnlineGuard;
import com.tuoheng.status.airport.guard.debug.CanCloseDebugModeGuard;
import com.tuoheng.status.airport.guard.debug.IsDebugModeGuard;
import com.tuoheng.status.airport.guard.debug.IsNotDebugModeGuard;
import com.tuoheng.status.airport.guard.reboot.IsRebootCompletedGuard;
import com.tuoheng.status.airport.listener.AirPortStatusListener;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineBuilder;
import org.springframework.statemachine.config.StateMachineFactory;
import java.util.EnumSet;
import java.util.UUID;
/**
* 机巢状态机配置简化版 - 舱门状态已分离
*/
@Configuration
public class AirportMachineConfig {
@Autowired
AirPortStatusListener airPortStatusListener;
// ==================== Actions ====================
@Autowired
OnlineAction onlineAction;
@Autowired
OfflineAction offlineAction;
@Autowired
OpenDebugModeAction openDebugModeAction;
@Autowired
CloseDebugModeAction closeDebugModeAction;
@Autowired
RebootAction rebootAction;
@Autowired
RebootCompletedAction rebootCompletedAction;
// ==================== Guards ====================
@Autowired
CanOnlineGuard canOnlineGuard;
@Autowired
CanOfflineGuard canOfflineGuard;
@Autowired
IsDebugModeGuard isDebugModeGuard;
@Autowired
IsNotDebugModeGuard isNotDebugModeGuard;
@Autowired
CanCloseDebugModeGuard canCloseDebugModeGuard;
@Autowired
IsRebootCompletedGuard isRebootCompletedGuard;
@Bean(name = "airportStateMachineFactory")
public StateMachineFactory<AirportState, AirportEvent> stateMachineFactory() throws Exception {
return new StateMachineFactory<AirportState, AirportEvent>() {
@Override
public StateMachine<AirportState, AirportEvent> getStateMachine() {
return null;
}
@Override
public StateMachine<AirportState, AirportEvent> getStateMachine(String machineId) {
try {
StateMachineBuilder.Builder<AirportState, AirportEvent> builder = StateMachineBuilder.builder();
configureStateMachine(builder);
configureStates(builder);
configureTransitions(builder);
StateMachine<AirportState, AirportEvent> stateMachine = builder.build();
stateMachine.getExtendedState().getVariables().put("machineId", machineId);
return stateMachine;
} catch (Exception e) {
throw new RuntimeException("Failed to create state machine", e);
}
}
@Override
public StateMachine<AirportState, AirportEvent> getStateMachine(UUID uuid) {
return null;
}
};
}
private void configureStateMachine(StateMachineBuilder.Builder<AirportState, AirportEvent> builder) throws Exception {
builder.configureConfiguration()
.withConfiguration()
.autoStartup(true)
.listener(airPortStatusListener);
}
private void configureStates(StateMachineBuilder.Builder<AirportState, AirportEvent> builder) throws Exception {
builder.configureStates()
.withStates()
.initial(AirportState.OFFLINE)
.states(EnumSet.of(
AirportState.OFFLINE,
AirportState.ONLINE,
AirportState.REBOOTING
))
.and()
.withStates()
.parent(AirportState.ONLINE)
.initial(AirportState.STANDBY)
.states(EnumSet.of(
AirportState.STANDBY,
AirportState.DEBUG_MODE
));
}
private void configureTransitions(StateMachineBuilder.Builder<AirportState, AirportEvent> builder) throws Exception {
builder.configureTransitions()
// OFFLINE -> ONLINE(STANDBY)
.withExternal()
.source(AirportState.OFFLINE)
.target(AirportState.ONLINE)
.event(AirportEvent.AIRPORT_ONLINE)
.action(onlineAction)
.guard(canOnlineGuard)
.and()
// ONLINE -> OFFLINE
.withExternal()
.source(AirportState.ONLINE)
.target(AirportState.OFFLINE)
.event(AirportEvent.AIRPORT_OFFLINE)
.action(offlineAction)
.guard(canOfflineGuard)
.and()
// STANDBY -> DEBUG_MODE
.withExternal()
.source(AirportState.STANDBY)
.target(AirportState.DEBUG_MODE)
.event(AirportEvent.DEBUG_MODE_OPEN)
.action(openDebugModeAction)
.guard(isNotDebugModeGuard)
.and()
// DEBUG_MODE -> STANDBY
.withExternal()
.source(AirportState.DEBUG_MODE)
.target(AirportState.STANDBY)
.event(AirportEvent.DEBUG_MODE_CLOSE)
.action(closeDebugModeAction)
.guard(canCloseDebugModeGuard)
.and()
// DEBUG_MODE -> REBOOTING
.withExternal()
.source(AirportState.DEBUG_MODE)
.target(AirportState.REBOOTING)
.event(AirportEvent.AIRPORT_REBOOT)
.action(rebootAction)
.guard(isDebugModeGuard)
.and()
// REBOOTING -> ONLINE(STANDBY)
.withExternal()
.source(AirportState.REBOOTING)
.target(AirportState.ONLINE)
.event(AirportEvent.REBOOT_COMPLETED)
.action(rebootCompletedAction)
.guard(isRebootCompletedGuard);
}
}

View File

@ -0,0 +1,113 @@
package com.tuoheng.status.airport.config;
import com.tuoheng.status.airport.events.CoverEvent;
import com.tuoheng.status.airport.status.CoverState;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineBuilder;
import org.springframework.statemachine.config.StateMachineFactory;
import java.util.EnumSet;
import java.util.UUID;
/**
* 舱门状态机配置
*/
@Configuration
public class CoverMachineConfig {
@Bean
public StateMachineFactory<CoverState, CoverEvent> coverStateMachineFactory() throws Exception {
return new StateMachineFactory<CoverState, CoverEvent>() {
@Override
public StateMachine<CoverState, CoverEvent> getStateMachine() {
return null;
}
@Override
public StateMachine<CoverState, CoverEvent> getStateMachine(String machineId) {
try {
StateMachineBuilder.Builder<CoverState, CoverEvent> builder = StateMachineBuilder.builder();
configureCoverStateMachine(builder);
configureCoverStates(builder);
configureCoverTransitions(builder);
StateMachine<CoverState, CoverEvent> stateMachine = builder.build();
stateMachine.getExtendedState().getVariables().put("machineId", machineId);
return stateMachine;
} catch (Exception e) {
throw new RuntimeException("Failed to create cover state machine", e);
}
}
@Override
public StateMachine<CoverState, CoverEvent> getStateMachine(UUID uuid) {
return null;
}
};
}
private void configureCoverStateMachine(StateMachineBuilder.Builder<CoverState, CoverEvent> builder) throws Exception {
builder.configureConfiguration()
.withConfiguration()
.autoStartup(true);
}
private void configureCoverStates(StateMachineBuilder.Builder<CoverState, CoverEvent> builder) throws Exception {
builder.configureStates()
.withStates()
.initial(CoverState.CLOSED)
.states(EnumSet.allOf(CoverState.class));
}
private void configureCoverTransitions(StateMachineBuilder.Builder<CoverState, CoverEvent> builder) throws Exception {
builder.configureTransitions()
// CLOSED -> OPENING
.withExternal()
.source(CoverState.CLOSED)
.target(CoverState.OPENING)
.event(CoverEvent.OPEN)
.and()
// OPENING -> OPENED
.withExternal()
.source(CoverState.OPENING)
.target(CoverState.OPENED)
.event(CoverEvent.OPENED)
.and()
// OPENED -> CLOSING
.withExternal()
.source(CoverState.OPENED)
.target(CoverState.CLOSING)
.event(CoverEvent.CLOSE)
.and()
// CLOSING -> CLOSED
.withExternal()
.source(CoverState.CLOSING)
.target(CoverState.CLOSED)
.event(CoverEvent.CLOSED)
.and()
// ERROR handling
.withExternal()
.source(CoverState.OPENING)
.target(CoverState.ERROR)
.event(CoverEvent.ERROR)
.and()
.withExternal()
.source(CoverState.CLOSING)
.target(CoverState.ERROR)
.event(CoverEvent.ERROR)
.and()
// RESET from ERROR
.withExternal()
.source(CoverState.ERROR)
.target(CoverState.CLOSED)
.event(CoverEvent.RESET);
}
}

View File

@ -0,0 +1,128 @@
package com.tuoheng.status.airport.demo;
import com.tuoheng.status.airport.manager.AirportSystemManager;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* 机巢系统演示组合状态机方案
*/
public class AirportSystemDemo {
public static void main(String[] args) throws InterruptedException {
// 创建 Spring 上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 扫描配置类服务类和监听器类
context.scan("com.tuoheng.status.airport");
context.refresh();
// 获取 AirportSystemManager
AirportSystemManager systemManager = context.getBean(AirportSystemManager.class);
System.out.println("\n========== 机巢系统演示开始(组合状态机方案) ==========\n");
String airportSn = "airport-001";
// ==================== 场景1: 机巢上线 ====================
System.out.println("【场景1】机巢上线");
System.out.println("----------------------------------------");
systemManager.airportOnline(airportSn);
System.out.println(" " + systemManager.getFullStatus(airportSn));
System.out.println();
Thread.sleep(500);
// ==================== 场景2: 开舱流程 ====================
System.out.println("【场景2】开舱流程");
System.out.println("----------------------------------------");
System.out.println("1. 开启调试模式");
systemManager.openDebugMode(airportSn);
System.out.println(" " + systemManager.getFullStatus(airportSn));
System.out.println();
Thread.sleep(500);
System.out.println("2. 开舱");
systemManager.openCover(airportSn);
System.out.println(" " + systemManager.getFullStatus(airportSn));
System.out.println();
Thread.sleep(500);
System.out.println("3. 舱门打开完成");
systemManager.coverOpened(airportSn);
System.out.println(" " + systemManager.getFullStatus(airportSn));
System.out.println();
Thread.sleep(500);
// ==================== 场景3: 关舱流程 ====================
System.out.println("【场景3】关舱流程");
System.out.println("----------------------------------------");
System.out.println("4. 关舱");
systemManager.closeCover(airportSn);
System.out.println(" " + systemManager.getFullStatus(airportSn));
System.out.println();
Thread.sleep(500);
System.out.println("5. 舱门关闭完成");
systemManager.coverClosed(airportSn);
System.out.println(" " + systemManager.getFullStatus(airportSn));
System.out.println();
Thread.sleep(500);
System.out.println("6. 关闭调试模式");
systemManager.closeDebugMode(airportSn);
System.out.println(" " + systemManager.getFullStatus(airportSn));
System.out.println();
Thread.sleep(500);
// ==================== 场景4: 状态查询 ====================
System.out.println("【场景4】状态查询");
System.out.println("----------------------------------------");
System.out.println("机巢状态: " + systemManager.getAirportState(airportSn));
System.out.println("舱门状态: " + systemManager.getCoverState(airportSn));
System.out.println("完整状态: " + systemManager.getFullStatus(airportSn));
System.out.println();
// ==================== 场景5: 机巢重启 ====================
System.out.println("【场景5】机巢重启");
System.out.println("----------------------------------------");
System.out.println("7. 开启调试模式");
systemManager.openDebugMode(airportSn);
System.out.println(" " + systemManager.getFullStatus(airportSn));
System.out.println();
Thread.sleep(500);
System.out.println("8. 机巢重启");
systemManager.rebootAirport(airportSn);
System.out.println(" " + systemManager.getFullStatus(airportSn));
System.out.println();
Thread.sleep(500);
System.out.println("9. 重启完成");
systemManager.rebootCompleted(airportSn);
System.out.println(" " + systemManager.getFullStatus(airportSn));
System.out.println();
// ==================== 场景6: 错误处理 ====================
System.out.println("【场景6】错误处理Guard拦截");
System.out.println("----------------------------------------");
System.out.println("10. 尝试在待机状态下开舱(应该失败)");
boolean result = systemManager.openCover(airportSn);
System.out.println(" 操作结果: " + (result ? "成功" : "失败"));
System.out.println(" " + systemManager.getFullStatus(airportSn));
System.out.println();
System.out.println("\n========== 机巢系统演示结束 ==========\n");
System.out.println("✅ 组合状态机方案优势:");
System.out.println(" 1. 机巢状态和舱门状态独立管理");
System.out.println(" 2. 状态层级扁平,易于理解");
System.out.println(" 3. 易于扩展(可添加推杆、无人机等状态机)");
System.out.println(" 4. 职责清晰,维护方便");
context.close();
}
}

View File

@ -0,0 +1,47 @@
package com.tuoheng.status.airport.events;
/**
* 机巢事件枚举
* 定义机巢状态机中的所有事件
*/
public enum AirportEvent {
// ==================== 机巢基本事件 ====================
/**
* 机巢上线事件
* 触发源: OSD心跳
*/
AIRPORT_ONLINE,
/**
* 机巢离线事件
* 触发源: OSD心跳
*/
AIRPORT_OFFLINE,
// ==================== 调试模式事件 ====================
/**
* 开启调试模式
* 触发源: 用户指令
*/
DEBUG_MODE_OPEN,
/**
* 关闭调试模式
* 触发源: 用户指令/自动
*/
DEBUG_MODE_CLOSE,
// ==================== 机巢重启事件 ====================
/**
* 机巢重启指令
* 触发源: 用户指令
*/
AIRPORT_REBOOT,
/**
* 重启完成
* 触发源: Events事件
*/
REBOOT_COMPLETED
}

View File

@ -0,0 +1,36 @@
package com.tuoheng.status.airport.events;
/**
* 舱门事件枚举
*/
public enum CoverEvent {
/**
* 开舱指令
*/
OPEN,
/**
* 开舱完成
*/
OPENED,
/**
* 关舱指令
*/
CLOSE,
/**
* 关舱完成
*/
CLOSED,
/**
* 舱门异常
*/
ERROR,
/**
* 重置舱门状态
*/
RESET
}

View File

@ -0,0 +1,21 @@
package com.tuoheng.status.airport.guard.airport;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;
import org.springframework.stereotype.Component;
/**
* 检查机巢是否可以离线
*/
@Component
public class CanOfflineGuard implements Guard<AirportState, AirportEvent> {
@Override
public boolean evaluate(StateContext<AirportState, AirportEvent> stateContext) {
// TODO: 检查机巢是否可以安全离线例如无人机已降落舱门已关闭等
System.out.println("检查机巢是否可以离线");
return true;
}
}

View File

@ -0,0 +1,21 @@
package com.tuoheng.status.airport.guard.airport;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;
import org.springframework.stereotype.Component;
/**
* 检查机巢是否可以上线
*/
@Component
public class CanOnlineGuard implements Guard<AirportState, AirportEvent> {
@Override
public boolean evaluate(StateContext<AirportState, AirportEvent> stateContext) {
// TODO: 检查机巢是否可以上线例如设备状态正常网络连接正常等
System.out.println("检查机巢是否可以上线");
return true;
}
}

View File

@ -0,0 +1,21 @@
package com.tuoheng.status.airport.guard.airport;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;
import org.springframework.stereotype.Component;
/**
* 检查机巢是否在线
*/
@Component
public class IsAirportOnlineGuard implements Guard<AirportState, AirportEvent> {
@Override
public boolean evaluate(StateContext<AirportState, AirportEvent> stateContext) {
// TODO: 检查机巢是否在线
System.out.println("检查机巢是否在线");
return true;
}
}

View File

@ -0,0 +1,22 @@
package com.tuoheng.status.airport.guard.cover;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;
import org.springframework.stereotype.Component;
/**
* 检查是否可以关舱
* 条件舱门状态不是已关闭
*/
@Component
public class CanCloseCoverGuard implements Guard<AirportState, AirportEvent> {
@Override
public boolean evaluate(StateContext<AirportState, AirportEvent> stateContext) {
// TODO: 检查当前舱门状态不是已关闭
System.out.println("检查是否可以关舱");
return true;
}
}

View File

@ -0,0 +1,22 @@
package com.tuoheng.status.airport.guard.cover;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;
import org.springframework.stereotype.Component;
/**
* 检查是否可以开舱
* 条件舱门状态不是已打开
*/
@Component
public class CanOpenCoverGuard implements Guard<AirportState, AirportEvent> {
@Override
public boolean evaluate(StateContext<AirportState, AirportEvent> stateContext) {
// TODO: 检查当前舱门状态不是已打开
System.out.println("检查是否可以开舱");
return true;
}
}

View File

@ -0,0 +1,22 @@
package com.tuoheng.status.airport.guard.cover;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;
import org.springframework.stereotype.Component;
/**
* 检查舱门是否已完全关闭
* 用于验证 COVER_CLOSED 事件的有效性
*/
@Component
public class IsCoverClosedGuard implements Guard<AirportState, AirportEvent> {
@Override
public boolean evaluate(StateContext<AirportState, AirportEvent> stateContext) {
// TODO: 检查舱门是否已完全关闭通过OSD状态确认
System.out.println("检查舱门是否已完全关闭");
return true;
}
}

View File

@ -0,0 +1,22 @@
package com.tuoheng.status.airport.guard.cover;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;
import org.springframework.stereotype.Component;
/**
* 检查舱门是否已完全打开
* 用于验证 COVER_OPENED 事件的有效性
*/
@Component
public class IsCoverOpenedGuard implements Guard<AirportState, AirportEvent> {
@Override
public boolean evaluate(StateContext<AirportState, AirportEvent> stateContext) {
// TODO: 检查舱门是否已完全打开通过OSD状态确认
System.out.println("检查舱门是否已完全打开");
return true;
}
}

View File

@ -0,0 +1,21 @@
package com.tuoheng.status.airport.guard.debug;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;
import org.springframework.stereotype.Component;
/**
* 检查是否可以关闭调试模式
*/
@Component
public class CanCloseDebugModeGuard implements Guard<AirportState, AirportEvent> {
@Override
public boolean evaluate(StateContext<AirportState, AirportEvent> stateContext) {
// TODO: 检查是否可以关闭调试模式例如没有正在进行的操作
System.out.println("检查是否可以关闭调试模式");
return true;
}
}

View File

@ -0,0 +1,21 @@
package com.tuoheng.status.airport.guard.debug;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;
import org.springframework.stereotype.Component;
/**
* 检查是否处于调试模式
*/
@Component
public class IsDebugModeGuard implements Guard<AirportState, AirportEvent> {
@Override
public boolean evaluate(StateContext<AirportState, AirportEvent> stateContext) {
// TODO: 检查当前是否处于调试模式
System.out.println("检查是否处于调试模式");
return true;
}
}

View File

@ -0,0 +1,21 @@
package com.tuoheng.status.airport.guard.debug;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;
import org.springframework.stereotype.Component;
/**
* 检查是否不处于调试模式
*/
@Component
public class IsNotDebugModeGuard implements Guard<AirportState, AirportEvent> {
@Override
public boolean evaluate(StateContext<AirportState, AirportEvent> stateContext) {
// TODO: 检查当前不处于调试模式
System.out.println("检查是否不处于调试模式");
return true;
}
}

View File

@ -0,0 +1,21 @@
package com.tuoheng.status.airport.guard.reboot;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;
import org.springframework.stereotype.Component;
/**
* 检查机巢重启是否完成
*/
@Component
public class IsRebootCompletedGuard implements Guard<AirportState, AirportEvent> {
@Override
public boolean evaluate(StateContext<AirportState, AirportEvent> stateContext) {
// TODO: 检查机巢重启是否完成通过Events事件确认
System.out.println("检查机巢重启是否完成");
return true;
}
}

View File

@ -0,0 +1,77 @@
package com.tuoheng.status.airport.listener;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import com.tuoheng.status.statemachine.events.Event;
import com.tuoheng.status.statemachine.status.Status;
import org.springframework.messaging.Message;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.transition.Transition;
import org.springframework.stereotype.Component;
@Component
public class AirPortStatusListener implements StateMachineListener<AirportState, AirportEvent> {
@Override
public void stateChanged(State<AirportState, AirportEvent> state, State<AirportState, AirportEvent> state1) {
}
@Override
public void stateEntered(State<AirportState, AirportEvent> state) {
}
@Override
public void stateExited(State<AirportState, AirportEvent> state) {
}
@Override
public void eventNotAccepted(Message<AirportEvent> message) {
}
@Override
public void transition(Transition<AirportState, AirportEvent> transition) {
}
@Override
public void transitionStarted(Transition<AirportState, AirportEvent> transition) {
}
@Override
public void transitionEnded(Transition<AirportState, AirportEvent> transition) {
}
@Override
public void stateMachineStarted(StateMachine<AirportState, AirportEvent> stateMachine) {
}
@Override
public void stateMachineStopped(StateMachine<AirportState, AirportEvent> stateMachine) {
}
@Override
public void stateMachineError(StateMachine<AirportState, AirportEvent> stateMachine, Exception e) {
}
@Override
public void extendedStateChanged(Object o, Object o1) {
}
@Override
public void stateContext(StateContext<AirportState, AirportEvent> stateContext) {
}
}

View File

@ -0,0 +1,154 @@
package com.tuoheng.status.airport.manager;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.events.CoverEvent;
import com.tuoheng.status.airport.service.AirportMachineService;
import com.tuoheng.status.airport.service.CoverMachineService;
import com.tuoheng.status.airport.status.AirportState;
import com.tuoheng.status.airport.status.CoverState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 机巢系统管理器
* 协调机巢状态机和舱门状态机
*/
@Component
public class AirportSystemManager {
@Autowired
private AirportMachineService airportManager;
@Autowired
private CoverMachineService coverManager;
/**
* 机巢上线
*/
public boolean airportOnline(String airportSn) {
return airportManager.sendEvent(airportSn, AirportEvent.AIRPORT_ONLINE);
}
/**
* 机巢离线
*/
public boolean airportOffline(String airportSn) {
return airportManager.sendEvent(airportSn, AirportEvent.AIRPORT_OFFLINE);
}
/**
* 开启调试模式
*/
public boolean openDebugMode(String airportSn) {
if (!airportManager.isInState(airportSn, AirportState.ONLINE)) {
System.out.println("机巢未在线,无法开启调试模式");
return false;
}
return airportManager.sendEvent(airportSn, AirportEvent.DEBUG_MODE_OPEN);
}
/**
* 关闭调试模式
*/
public boolean closeDebugMode(String airportSn) {
return airportManager.sendEvent(airportSn, AirportEvent.DEBUG_MODE_CLOSE);
}
/**
* 开舱
*/
public boolean openCover(String airportSn) {
// 检查机巢是否在调试模式
if (!airportManager.isInState(airportSn, AirportState.DEBUG_MODE)) {
System.out.println("必须在调试模式下才能开舱");
return false;
}
// 检查舱门是否已经打开
if (coverManager.isInState(airportSn, CoverState.OPENED)) {
System.out.println("舱门已经打开");
return false;
}
// 发送开舱指令到舱门状态机
return coverManager.sendEvent(airportSn, CoverEvent.OPEN);
}
/**
* 舱门打开完成
*/
public boolean coverOpened(String airportSn) {
return coverManager.sendEvent(airportSn, CoverEvent.OPENED);
}
/**
* 关舱
*/
public boolean closeCover(String airportSn) {
// 检查机巢是否在调试模式
if (!airportManager.isInState(airportSn, AirportState.DEBUG_MODE)) {
System.out.println("必须在调试模式下才能关舱");
return false;
}
// 检查舱门是否已经关闭
if (coverManager.isInState(airportSn, CoverState.CLOSED)) {
System.out.println("舱门已经关闭");
return false;
}
// 发送关舱指令到舱门状态机
return coverManager.sendEvent(airportSn, CoverEvent.CLOSE);
}
/**
* 舱门关闭完成
*/
public boolean coverClosed(String airportSn) {
return coverManager.sendEvent(airportSn, CoverEvent.CLOSED);
}
/**
* 机巢重启
*/
public boolean rebootAirport(String airportSn) {
if (!airportManager.isInState(airportSn, AirportState.DEBUG_MODE)) {
System.out.println("必须在调试模式下才能重启");
return false;
}
return airportManager.sendEvent(airportSn, AirportEvent.AIRPORT_REBOOT);
}
/**
* 重启完成
*/
public boolean rebootCompleted(String airportSn) {
return airportManager.sendEvent(airportSn, AirportEvent.REBOOT_COMPLETED);
}
/**
* 获取机巢状态
*/
public AirportState getAirportState(String airportSn) {
return airportManager.getCurrentState(airportSn);
}
/**
* 获取舱门状态
*/
public CoverState getCoverState(String airportSn) {
return coverManager.getCurrentState(airportSn);
}
/**
* 获取完整状态信息
*/
public String getFullStatus(String airportSn) {
AirportState airportState = getAirportState(airportSn);
CoverState coverState = getCoverState(airportSn);
return String.format("机巢状态: %s, 舱门状态: %s",
airportState != null ? airportState : "未知",
coverState != null ? coverState : "未知");
}
}

View File

@ -0,0 +1,182 @@
package com.tuoheng.status.airport.service;
import com.tuoheng.status.airport.events.AirportEvent;
import com.tuoheng.status.airport.status.AirportState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 机巢状态机管理器
* 负责管理多个机巢的状态机实例
*/
@Component
public class AirportMachineService {
@Autowired
StateMachineFactory<AirportState, AirportEvent> stateMachineFactory;
/**
* 存储所有机巢的状态机实例
* Key: 机巢ID (airportSn)
* Value: 状态机实例
*/
private final Map<String, StateMachine<AirportState, AirportEvent>> stateMachineMap = new ConcurrentHashMap<>();
/**
* 获取或创建状态机
* 如果状态机不存在则创建新的状态机实例
*
* @param airportSn 机巢序列号
* @return 状态机实例
*/
public StateMachine<AirportState, AirportEvent> getOrCreateStateMachine(String airportSn) {
return stateMachineMap.computeIfAbsent(airportSn, id -> {
StateMachine<AirportState, AirportEvent> stateMachine = stateMachineFactory.getStateMachine(id);
stateMachine.start();
System.out.println("创建并启动状态机: " + id);
return stateMachine;
});
}
/**
* 获取状态机不创建
*
* @param airportSn 机巢序列号
* @return 状态机实例如果不存在返回null
*/
public StateMachine<AirportState, AirportEvent> getStateMachine(String airportSn) {
return stateMachineMap.get(airportSn);
}
/**
* 获取状态机的当前状态
*
* @param airportSn 机巢序列号
* @return 当前状态如果状态机不存在返回null
*/
public AirportState getCurrentState(String airportSn) {
StateMachine<AirportState, AirportEvent> stateMachine = stateMachineMap.get(airportSn);
if (stateMachine == null) {
System.out.println("状态机不存在: " + airportSn);
return null;
}
return stateMachine.getState().getId();
}
/**
* 获取状态机的所有当前状态包括子状态
*
* @param airportSn 机巢序列号
* @return 当前状态集合的字符串表示
*/
public String getCurrentStates(String airportSn) {
StateMachine<AirportState, AirportEvent> stateMachine = stateMachineMap.get(airportSn);
if (stateMachine == null) {
return "状态机不存在";
}
StringBuilder states = new StringBuilder();
stateMachine.getState().getIds().forEach(state -> {
if (states.length() > 0) {
states.append(" -> ");
}
states.append(state);
});
return states.toString();
}
/**
* 发送事件到状态机
*
* @param airportSn 机巢序列号
* @param event 事件
* @return 是否发送成功
*/
public boolean sendEvent(String airportSn, AirportEvent event) {
StateMachine<AirportState, AirportEvent> stateMachine = getOrCreateStateMachine(airportSn);
boolean result = stateMachine.sendEvent(event);
if (result) {
System.out.println(String.format("事件发送成功 - 机巢: %s, 事件: %s, 当前状态: %s",
airportSn, event, getCurrentStates(airportSn)));
} else {
System.out.println(String.format("事件发送失败 - 机巢: %s, 事件: %s, 当前状态: %s",
airportSn, event, getCurrentStates(airportSn)));
}
return result;
}
/**
* 移除状态机
*
* @param airportSn 机巢序列号
*/
public void removeStateMachine(String airportSn) {
StateMachine<AirportState, AirportEvent> stateMachine = stateMachineMap.remove(airportSn);
if (stateMachine != null) {
stateMachine.stop();
System.out.println("停止并移除状态机: " + airportSn);
}
}
/**
* 检查状态机是否存在
*
* @param airportSn 机巢序列号
* @return 是否存在
*/
public boolean hasStateMachine(String airportSn) {
return stateMachineMap.containsKey(airportSn);
}
/**
* 获取所有状态机的数量
*
* @return 状态机数量
*/
public int getStateMachineCount() {
return stateMachineMap.size();
}
/**
* 获取所有机巢ID
*
* @return 机巢ID集合
*/
public java.util.Set<String> getAllAirportIds() {
return stateMachineMap.keySet();
}
/**
* 检查状态机是否处于指定状态
*
* @param airportSn 机巢序列号
* @param state 要检查的状态
* @return 是否处于指定状态
*/
public boolean isInState(String airportSn, AirportState state) {
StateMachine<AirportState, AirportEvent> stateMachine = stateMachineMap.get(airportSn);
if (stateMachine == null) {
return false;
}
return stateMachine.getState().getIds().contains(state);
}
/**
* 重启状态机
*
* @param airportSn 机巢序列号
*/
public void restartStateMachine(String airportSn) {
removeStateMachine(airportSn);
getOrCreateStateMachine(airportSn);
System.out.println("重启状态机: " + airportSn);
}
}

View File

@ -0,0 +1,71 @@
package com.tuoheng.status.airport.service;
import com.tuoheng.status.airport.events.CoverEvent;
import com.tuoheng.status.airport.status.CoverState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 舱门状态机管理器
*/
@Component
public class CoverMachineService {
@Autowired
StateMachineFactory<CoverState, CoverEvent> coverStateMachineFactory;
private final Map<String, StateMachine<CoverState, CoverEvent>> stateMachineMap = new ConcurrentHashMap<>();
public StateMachine<CoverState, CoverEvent> getOrCreateStateMachine(String airportSn) {
return stateMachineMap.computeIfAbsent(airportSn, id -> {
StateMachine<CoverState, CoverEvent> stateMachine = coverStateMachineFactory.getStateMachine(id);
stateMachine.start();
System.out.println("创建并启动舱门状态机: " + id);
return stateMachine;
});
}
public CoverState getCurrentState(String airportSn) {
StateMachine<CoverState, CoverEvent> stateMachine = stateMachineMap.get(airportSn);
if (stateMachine == null) {
return null;
}
return stateMachine.getState().getId();
}
public boolean sendEvent(String airportSn, CoverEvent event) {
StateMachine<CoverState, CoverEvent> stateMachine = getOrCreateStateMachine(airportSn);
boolean result = stateMachine.sendEvent(event);
if (result) {
System.out.println(String.format("舱门事件发送成功 - 机巢: %s, 事件: %s, 当前状态: %s",
airportSn, event, getCurrentState(airportSn)));
} else {
System.out.println(String.format("舱门事件发送失败 - 机巢: %s, 事件: %s, 当前状态: %s",
airportSn, event, getCurrentState(airportSn)));
}
return result;
}
public boolean isInState(String airportSn, CoverState state) {
StateMachine<CoverState, CoverEvent> stateMachine = stateMachineMap.get(airportSn);
if (stateMachine == null) {
return false;
}
return stateMachine.getState().getId() == state;
}
public void removeStateMachine(String airportSn) {
StateMachine<CoverState, CoverEvent> stateMachine = stateMachineMap.remove(airportSn);
if (stateMachine != null) {
stateMachine.stop();
System.out.println("停止并移除舱门状态机: " + airportSn);
}
}
}

View File

@ -0,0 +1,31 @@
package com.tuoheng.status.airport.status;
/**
* 机巢状态枚举简化版 - 舱门状态已分离
*/
public enum AirportState {
/**
* 离线
*/
OFFLINE,
/**
* 在线父状态
*/
ONLINE,
/**
* 待机
*/
STANDBY,
/**
* 调试模式
*/
DEBUG_MODE,
/**
* 重启中
*/
REBOOTING
}

View File

@ -0,0 +1,36 @@
package com.tuoheng.status.airport.status;
/**
* 舱门状态枚举
*/
public enum CoverState {
/**
* 舱门已关闭
*/
CLOSED,
/**
* 舱门打开中
*/
OPENING,
/**
* 舱门已打开
*/
OPENED,
/**
* 舱门关闭中
*/
CLOSING,
/**
* 舱门半开
*/
HALF_OPEN,
/**
* 舱门状态异常
*/
ERROR
}

View File

@ -0,0 +1,4 @@
package com.tuoheng.status.airport.status;
public enum DroneState {
}

View File

@ -0,0 +1,506 @@
# Spring StateMachine 使用文档
本文档详细介绍了如何在项目中使用 Spring StateMachine Core 来构建状态机包括子状态、Action、Guard、事件监听器等核心功能。
## 目录
- [项目结构](#项目结构)
- [核心概念](#核心概念)
- [状态机配置](#状态机配置)
- [使用示例](#使用示例)
- [运行演示](#运行演示)
- [常见问题](#常见问题)
## 项目结构
```
com.tuoheng.status.statemachine/
├── status/
│ └── Status.java # 状态枚举(包含主状态和子状态)
├── events/
│ └── Event.java # 事件枚举
├── action/
│ └── TaskAction.java # Action 实现类
├── guard/
│ └── TaskGuard.java # Guard 实现类
├── listener/
│ └── TaskStateMachineListener.java # 状态机事件监听器
├── config/
│ └── TaskStateMachineConfig.java # 状态机配置类
├── service/
│ └── TaskStateMachineService.java # 服务类(使用示例)
└── demo/
└── StateMachineDemo.java # 演示主类
```
## 核心概念
### 1. 状态State
状态定义了状态机可能处于的各种情况。本示例包含:
**主状态:**
- `IDLE` - 空闲状态(初始状态)
- `PROCESSING` - 处理中(父状态,包含子状态)
- `COMPLETED` - 已完成
- `FAILED` - 失败
**子状态PROCESSING 的子状态):**
- `PREPARING` - 准备中
- `EXECUTING` - 执行中
- `VALIDATING` - 验证中
### 2. 事件Event
事件是触发状态转换的触发器:
- `START` - 开始处理
- `PREPARE` - 准备就绪
- `EXECUTE` - 执行
- `VALIDATE` - 验证
- `COMPLETE` - 完成
- `FAIL` - 失败
- `RETRY` - 重试
- `RESET` - 重置
### 3. Action动作
Action 在状态转换时执行用于执行业务逻辑。Action 在转换**过程中**执行。
**示例:**
```java
public static class StartAction implements Action<Status, Event> {
@Override
public void execute(StateContext<Status, Event> context) {
// 执行业务逻辑
logger.info("开始处理任务");
Object taskId = context.getExtendedState().getVariables().get("taskId");
// ...
}
}
```
### 4. Guard守卫
Guard 用于条件判断决定是否允许状态转换。Guard 在转换**之前**执行,如果返回 `false`,转换将被阻止。
**示例:**
```java
public static class CanStartGuard implements Guard<Status, Event> {
@Override
public boolean evaluate(StateContext<Status, Event> context) {
// 检查条件
Object taskId = context.getExtendedState().getVariables().get("taskId");
return taskId != null; // 只有 taskId 存在时才允许转换
}
}
```
### 5. 事件监听器Listener
监听器用于监听状态机的各种事件,如状态变化、转换等。
**监听的事件类型:**
- 状态机启动/停止
- 状态进入/退出
- 状态改变
- 转换开始/结束
- 事件未接受
- 扩展状态改变
## 状态机配置
### 状态转换流程
```
IDLE
└─[START]─> PREPARING (子状态)
└─[PREPARE]─> EXECUTING (子状态)
└─[EXECUTE]─> VALIDATING (子状态)
└─[VALIDATE]─> COMPLETED
└─[RESET]─> IDLE
PREPARING/EXECUTING/VALIDATING
└─[FAIL]─> FAILED
└─[RETRY]─> PREPARING (重试)
└─[RESET]─> IDLE (重置)
```
### 配置类说明
`TaskStateMachineConfig` 类负责配置状态机:
1. **配置状态和子状态:**
```java
builder.configureStates()
.withStates()
.initial(Status.IDLE) // 初始状态
.states(EnumSet.of(Status.IDLE, Status.COMPLETED, Status.FAILED))
.and()
.withStates()
.parent(Status.PROCESSING) // 设置父状态
.initial(Status.PREPARING) // 子状态的初始状态
.states(EnumSet.of(Status.PREPARING, Status.EXECUTING, Status.VALIDATING));
```
2. **配置转换:**
```java
builder.configureTransitions()
.withExternal()
.source(Status.IDLE)
.target(Status.PREPARING)
.event(Event.START)
.action(new TaskAction.StartAction()) // 添加 Action
.guard(new TaskGuard.CanStartGuard()) // 添加 Guard
.and()
// ... 更多转换配置
```
## 状态机管理器
当需要管理多个状态机实例时,可以使用 `StateMachineManager` 来统一管理。
### 核心功能
- **通过ID获取状态机**`getStateMachine(machineId)`
- **查询当前状态**`getCurrentStatus(machineId)`
- **获取详细信息**`getStateMachineInfo(machineId)`
- **创建/获取状态机**`getOrCreateStateMachine(machineId)`
- **移除状态机**`removeStateMachine(machineId)`
### 使用示例
```java
@Autowired
private StateMachineManager stateMachineManager;
// 1. 创建或获取状态机
StateMachine<Status, Event> stateMachine =
stateMachineManager.getOrCreateStateMachine("machine-001");
// 2. 通过ID获取状态机
StateMachine<Status, Event> sm =
stateMachineManager.getStateMachine("machine-001");
// 3. 查询当前状态
Status currentStatus = stateMachineManager.getCurrentStatus("machine-001");
System.out.println("当前状态: " + currentStatus);
// 4. 获取详细信息
StateMachineManager.StateMachineInfo info =
stateMachineManager.getStateMachineInfo("machine-001");
System.out.println("状态机ID: " + info.getMachineId());
System.out.println("当前状态: " + info.getCurrentStatus());
System.out.println("是否运行中: " + info.isRunning());
System.out.println("扩展状态: " + info.getExtendedState());
// 5. 发送事件
stateMachine.sendEvent(Event.START);
// 6. 移除状态机
stateMachineManager.removeStateMachine("machine-001");
```
### 使用服务类(推荐)
`StateMachineManagerService` 提供了更便捷的方法:
```java
@Autowired
private StateMachineManagerService stateMachineManagerService;
// 启动任务(自动创建状态机)
stateMachineManagerService.startTask("machine-001", "task-001");
// 发送事件
stateMachineManagerService.sendEvent("machine-001", Event.PREPARE);
// 查询状态
Status status = stateMachineManagerService.getCurrentStatus("machine-001");
// 获取详细信息
StateMachineManager.StateMachineInfo info =
stateMachineManagerService.getStateMachineInfo("machine-001");
// 打印状态机信息(调试用)
stateMachineManagerService.printStateMachineInfo("machine-001");
// 获取所有状态机ID
Set<String> allIds = stateMachineManagerService.getAllMachineIds();
```
### 管理多个状态机
```java
// 创建多个状态机
stateMachineManager.getOrCreateStateMachine("machine-001");
stateMachineManager.getOrCreateStateMachine("machine-002");
stateMachineManager.getOrCreateStateMachine("machine-003");
// 获取所有状态机ID
Set<String> allIds = stateMachineManager.getAllMachineIds();
System.out.println("管理的状态机数量: " + allIds.size());
// 为不同状态机设置不同状态
stateMachineManager.getStateMachine("machine-001")
.getExtendedState().getVariables().put("taskId", "task-001");
stateMachineManagerService.sendEvent("machine-001", Event.START);
// 查询不同状态机的状态
Status status1 = stateMachineManager.getCurrentStatus("machine-001");
Status status2 = stateMachineManager.getCurrentStatus("machine-002");
```
## 使用示例
### 1. 基本使用
```java
@Autowired
private StateMachineFactory<Status, Event> stateMachineFactory;
public void processTask(String taskId) {
// 创建状态机实例
StateMachine<Status, Event> stateMachine = stateMachineFactory.getStateMachine();
try {
// 设置扩展状态变量
stateMachine.getExtendedState().getVariables().put("taskId", taskId);
// 启动状态机
stateMachine.start();
// 发送事件触发转换
stateMachine.sendEvent(Event.START);
// 继续处理...
stateMachine.getExtendedState().getVariables().put("prepared", true);
stateMachine.sendEvent(Event.PREPARE);
} finally {
// 停止状态机
stateMachine.stop();
}
}
```
### 2. 使用 Guard 控制转换
```java
// Guard 会在转换前检查条件
public static class CanStartGuard implements Guard<Status, Event> {
@Override
public boolean evaluate(StateContext<Status, Event> context) {
Object taskId = context.getExtendedState().getVariables().get("taskId");
return taskId != null; // 只有满足条件才允许转换
}
}
// 使用:如果 taskId 不存在START 事件将被拒绝
stateMachine.sendEvent(Event.START); // Guard 检查失败,转换不会发生
```
### 3. 使用 Action 执行业务逻辑
```java
// Action 在转换时执行
public static class ExecuteAction implements Action<Status, Event> {
@Override
public void execute(StateContext<Status, Event> context) {
// 执行业务逻辑
logger.info("执行任务");
// 可以访问和修改扩展状态
context.getExtendedState().getVariables().put("executionResult", "success");
}
}
```
### 4. 监听状态机事件
监听器会自动监听所有状态机事件:
```java
// 监听器会自动记录:
// - 状态机启动/停止
// - 状态进入/退出
// - 状态改变
// - 转换开始/结束
// - 事件未接受
```
## 运行演示
### 方式一:运行演示主类
直接运行 `StateMachineDemo` 类的 `main` 方法:
```bash
# 在 IDE 中运行
com.tuoheng.status.statemachine.demo.StateMachineDemo
# 或使用 Maven
mvn exec:java -Dexec.mainClass="com.tuoheng.status.statemachine.demo.StateMachineDemo"
```
演示内容包括:
1. **正常流程**IDLE → PREPARING → EXECUTING → VALIDATING → COMPLETED
2. **失败和重试**:演示失败后的重试机制
3. **Guard 条件不满足**:演示 Guard 如何阻止转换
4. **重置流程**:演示如何重置状态机
### 方式二:运行测试类
运行 `TaskStateMachineTest` 测试类:
```bash
mvn test -Dtest=TaskStateMachineTest
```
### 方式三:在服务中使用
```java
@Service
public class YourService {
@Autowired
private TaskStateMachineService taskStateMachineService;
public void handleTask(String taskId) {
// 使用预定义的服务方法
taskStateMachineService.startTask(taskId);
}
}
```
## 扩展状态Extended State
扩展状态用于存储状态机相关的变量,可以在 Action、Guard 和监听器中访问:
```java
// 设置变量
stateMachine.getExtendedState().getVariables().put("taskId", "task-001");
stateMachine.getExtendedState().getVariables().put("retryCount", 0);
// 在 Action 或 Guard 中访问
Object taskId = context.getExtendedState().getVariables().get("taskId");
Integer retryCount = (Integer) context.getExtendedState().getVariables().getOrDefault("retryCount", 0);
```
## 常见问题
### 1. Guard 返回 false转换不执行
**原因:** Guard 在转换前检查条件,如果返回 `false`,转换将被阻止。
**解决:** 确保在发送事件前,扩展状态中的变量已正确设置。
```java
// 错误示例Guard 检查失败
stateMachine.sendEvent(Event.START); // taskId 未设置Guard 返回 false
// 正确示例:先设置变量
stateMachine.getExtendedState().getVariables().put("taskId", "task-001");
stateMachine.sendEvent(Event.START); // Guard 检查通过
```
### 2. 事件未接受
**原因:** 当前状态不支持该事件,或没有匹配的转换配置。
**解决:**
- 检查状态机当前状态
- 确认配置中是否有对应的转换
- 检查 Guard 条件是否满足
```java
// 检查事件是否被接受
boolean accepted = stateMachine.sendEvent(Event.START);
if (!accepted) {
logger.warning("事件未被接受,当前状态: " + stateMachine.getState().getId());
}
```
### 3. 子状态的使用
子状态是父状态的内部状态,当进入父状态时,会自动进入子状态的初始状态。
```java
// 从 IDLE 转换到 PREPARINGPROCESSING 的子状态)
stateMachine.sendEvent(Event.START);
// 状态机现在处于 PROCESSING 父状态,具体是 PREPARING 子状态
```
### 4. 状态机实例管理
每次调用 `getStateMachine()` 都会创建新的状态机实例。如果需要复用实例,需要自己管理:
```java
// 方式一:每次创建新实例(推荐用于独立任务)
StateMachine<Status, Event> sm1 = stateMachineFactory.getStateMachine();
StateMachine<Status, Event> sm2 = stateMachineFactory.getStateMachine();
// 方式二:复用实例(需要自己管理生命周期)
StateMachine<Status, Event> sm = stateMachineFactory.getStateMachine();
// ... 使用 sm
sm.stop(); // 使用完后停止
```
### 5. 监听器中的空指针异常
某些转换(如初始转换)可能没有 trigger需要检查 null
```java
@Override
public void transitionStarted(Transition<Status, Event> transition) {
if (transition.getTrigger() != null) {
logger.info("事件: " + transition.getTrigger().getEvent());
} else {
logger.info("事件: null (可能是初始转换)");
}
}
```
### 6. 通过ID获取状态机
使用 `StateMachineManager` 可以方便地管理多个状态机:
```java
@Autowired
private StateMachineManager stateMachineManager;
// 获取状态机
StateMachine<Status, Event> sm = stateMachineManager.getStateMachine("machine-id");
// 如果不存在,创建新实例
StateMachine<Status, Event> sm = stateMachineManager.getOrCreateStateMachine("machine-id");
// 查询当前状态
Status status = stateMachineManager.getCurrentStatus("machine-id");
// 获取详细信息
StateMachineManager.StateMachineInfo info =
stateMachineManager.getStateMachineInfo("machine-id");
```
## 最佳实践
1. **使用 Guard 进行条件检查**:在转换前验证条件,避免无效转换
2. **使用 Action 执行业务逻辑**:将业务逻辑放在 Action 中,保持代码清晰
3. **合理使用扩展状态**:存储任务相关的变量,便于在 Action 和 Guard 中使用
4. **添加监听器**:监听状态机事件,便于调试和监控
5. **管理状态机生命周期**:及时启动和停止状态机,避免资源泄漏
6. **处理异常情况**:在 Guard 中检查条件,在 Action 中处理异常
## 参考资源
- [Spring StateMachine 官方文档](https://docs.spring.io/spring-statemachine/docs/current/reference/)
- [Spring StateMachine API 文档](https://docs.spring.io/spring-statemachine/docs/current/api/)
## 示例代码位置
- **配置类**`com.tuoheng.status.statemachine.config.TaskStateMachineConfig`
- **状态机管理器**`com.tuoheng.status.statemachine.manager.StateMachineManager`
- **管理器服务类**`com.tuoheng.status.statemachine.service.StateMachineManagerService`
- **服务类**`com.tuoheng.status.statemachine.service.TaskStateMachineService`
- **演示类**`com.tuoheng.status.statemachine.demo.StateMachineDemo`
- **测试类**`com.tuoheng.status.statemachine.TaskStateMachineTest`
- **管理器测试类**`com.tuoheng.status.statemachine.StateMachineManagerTest`

View File

@ -0,0 +1,142 @@
package com.tuoheng.status.statemachine.action;
import com.tuoheng.status.statemachine.events.Event;
import com.tuoheng.status.statemachine.status.Status;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import java.util.logging.Logger;
/**
* 任务 Action 示例
* Action 在状态转换时执行用于执行业务逻辑
*/
public class TaskAction {
private static final Logger logger = Logger.getLogger(TaskAction.class.getName());
/**
* 开始处理 Action
*/
public static class StartAction implements Action<Status, Event> {
@Override
public void execute(StateContext<Status, Event> context) {
logger.info("=== Action: 开始处理任务 ===");
logger.info("从状态: " + context.getSource().getId());
logger.info("到状态: " + context.getTarget().getId());
logger.info("事件: " + context.getEvent());
// 可以在这里执行业务逻辑比如初始化任务数据
Object taskId = context.getExtendedState().getVariables().get("taskId");
logger.info("任务ID: " + taskId);
}
}
/**
* 准备 Action
*/
public static class PrepareAction implements Action<Status, Event> {
@Override
public void execute(StateContext<Status, Event> context) {
logger.info("=== Action: 准备任务资源 ===");
logger.info("当前状态: " + context.getStateMachine().getState().getId());
// 模拟准备资源
try {
Thread.sleep(100); // 模拟耗时操作
logger.info("资源准备完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
/**
* 执行 Action
*/
public static class ExecuteAction implements Action<Status, Event> {
@Override
public void execute(StateContext<Status, Event> context) {
logger.info("=== Action: 执行任务 ===");
logger.info("当前状态: " + context.getStateMachine().getState().getId());
// 模拟执行任务
try {
Thread.sleep(200); // 模拟耗时操作
logger.info("任务执行完成");
// 可以在扩展状态中存储执行结果
context.getExtendedState().getVariables().put("executionResult", "success");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
/**
* 验证 Action
*/
public static class ValidateAction implements Action<Status, Event> {
@Override
public void execute(StateContext<Status, Event> context) {
logger.info("=== Action: 验证任务结果 ===");
logger.info("当前状态: " + context.getStateMachine().getState().getId());
// 获取执行结果
Object result = context.getExtendedState().getVariables().get("executionResult");
logger.info("执行结果: " + result);
// 模拟验证
boolean isValid = result != null && "success".equals(result);
context.getExtendedState().getVariables().put("validationResult", isValid);
logger.info("验证结果: " + isValid);
}
}
/**
* 完成 Action
*/
public static class CompleteAction implements Action<Status, Event> {
@Override
public void execute(StateContext<Status, Event> context) {
logger.info("=== Action: 任务完成 ===");
logger.info("从状态: " + context.getSource().getId());
logger.info("到状态: " + context.getTarget().getId());
// 清理资源或执行完成后的操作
logger.info("清理资源...");
}
}
/**
* 失败 Action
*/
public static class FailAction implements Action<Status, Event> {
@Override
public void execute(StateContext<Status, Event> context) {
logger.warning("=== Action: 任务失败 ===");
logger.warning("从状态: " + context.getSource().getId());
logger.warning("到状态: " + context.getTarget().getId());
// 记录失败信息
Exception exception = context.getException();
if (exception != null) {
logger.warning("失败原因: " + exception.getMessage());
}
}
}
/**
* 重置 Action
*/
public static class ResetAction implements Action<Status, Event> {
@Override
public void execute(StateContext<Status, Event> context) {
logger.info("=== Action: 重置状态机 ===");
// 清理扩展状态中的变量
context.getExtendedState().getVariables().clear();
logger.info("状态机已重置");
}
}
}

View File

@ -0,0 +1,182 @@
package com.tuoheng.status.statemachine.config;
import com.tuoheng.status.statemachine.action.TaskAction;
import com.tuoheng.status.statemachine.events.Event;
import com.tuoheng.status.statemachine.guard.TaskGuard;
import com.tuoheng.status.statemachine.listener.TaskStateMachineListener;
import com.tuoheng.status.statemachine.status.Status;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineBuilder;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import java.util.EnumSet;
import java.util.UUID;
import java.util.logging.Logger;
/**
* 状态机配置类
* 配置状态子状态转换ActionGuard
*/
@Configuration
public class TaskStateMachineConfig {
private static final Logger logger = Logger.getLogger(TaskStateMachineConfig.class.getName());
/**
* 构建状态机工厂
* 使用工厂模式可以为每个任务创建独立的状态机实例
*/
@Bean
public StateMachineFactory<Status, Event> stateMachineFactory() throws Exception {
// 返回一个简单的 StateMachineFactory 实现
return new StateMachineFactory<Status, Event>() {
@Override
public StateMachine<Status, Event> getStateMachine() {
return getStateMachine(UUID.randomUUID().toString());
}
@Override
public StateMachine<Status, Event> getStateMachine(String machineId) {
try {
// 为每个请求创建新的状态机实例
StateMachineBuilder.Builder<Status, Event> newBuilder = StateMachineBuilder.builder();
configureStateMachine(newBuilder);
configureStates(newBuilder);
configureTransitions(newBuilder);
StateMachine<Status, Event> stateMachine = newBuilder.build();
stateMachine.getExtendedState().getVariables().put("machineId", machineId);
return stateMachine;
} catch (Exception e) {
throw new RuntimeException("Failed to create state machine", e);
}
}
@Override
public StateMachine<Status, Event> getStateMachine(UUID uuid) {
return getStateMachine(uuid.toString());
}
};
}
/**
* 配置状态机基本设置
*/
private void configureStateMachine(StateMachineBuilder.Builder<Status, Event> builder) throws Exception {
builder.configureConfiguration()
.withConfiguration()
.autoStartup(true) // 自动启动
.listener(new TaskStateMachineListener()); // 添加监听器
}
/**
* 配置状态和子状态
*/
private void configureStates(StateMachineBuilder.Builder<Status, Event> builder) throws Exception {
builder.configureStates()
.withStates()
// 初始状态
.initial(Status.IDLE)
// 定义主状态
.states(EnumSet.of(Status.IDLE, Status.COMPLETED, Status.FAILED))
// PROCESSING 是父状态包含子状态
.and()
.withStates()
.parent(Status.PROCESSING) // 设置 PROCESSING 为父状态
.initial(Status.PREPARING) // 子状态的初始状态是 PREPARING
.states(EnumSet.of(Status.PREPARING, Status.EXECUTING, Status.VALIDATING));
}
/**
* 配置状态转换
*/
private void configureTransitions(StateMachineBuilder.Builder<Status, Event> builder) throws Exception {
builder.configureTransitions()
// IDLE -> PROCESSING (PREPARING)
.withExternal()
.source(Status.IDLE)
.target(Status.PREPARING)
.event(Event.START)
.action(new TaskAction.StartAction())
.guard(new TaskGuard.CanStartGuard())
.and()
// PREPARING -> EXECUTING
.withExternal()
.source(Status.PREPARING)
.target(Status.EXECUTING)
.event(Event.PREPARE)
.action(new TaskAction.PrepareAction())
.guard(new TaskGuard.PrepareCompleteGuard())
.and()
// EXECUTING -> VALIDATING
.withExternal()
.source(Status.EXECUTING)
.target(Status.VALIDATING)
.event(Event.EXECUTE)
.action(new TaskAction.ExecuteAction())
.guard(new TaskGuard.ExecutionSuccessGuard())
.and()
// VALIDATING -> COMPLETED (从子状态退出到主状态)
.withExternal()
.source(Status.VALIDATING)
.target(Status.COMPLETED)
.event(Event.VALIDATE)
.action(new TaskAction.ValidateAction())
.guard(new TaskGuard.ValidationPassGuard())
.and()
// 任何 PROCESSING 子状态 -> FAILED
.withExternal()
.source(Status.PREPARING)
.target(Status.FAILED)
.event(Event.FAIL)
.action(new TaskAction.FailAction())
.and()
.withExternal()
.source(Status.EXECUTING)
.target(Status.FAILED)
.event(Event.FAIL)
.action(new TaskAction.FailAction())
.and()
.withExternal()
.source(Status.VALIDATING)
.target(Status.FAILED)
.event(Event.FAIL)
.action(new TaskAction.FailAction())
.and()
// FAILED -> PREPARING (重试)
.withExternal()
.source(Status.FAILED)
.target(Status.PREPARING)
.event(Event.RETRY)
.action(new TaskAction.StartAction())
.guard(new TaskGuard.CanRetryGuard())
.and()
// COMPLETED -> IDLE (重置)
.withExternal()
.source(Status.COMPLETED)
.target(Status.IDLE)
.event(Event.RESET)
.action(new TaskAction.ResetAction())
.and()
// FAILED -> IDLE (重置)
.withExternal()
.source(Status.FAILED)
.target(Status.IDLE)
.event(Event.RESET)
.action(new TaskAction.ResetAction());
}
}

View File

@ -0,0 +1,94 @@
package com.tuoheng.status.statemachine.demo;
import com.tuoheng.status.statemachine.service.TaskStateMachineService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* 状态机演示主类
* 可以直接运行此类的 main 方法来查看状态机的运行效果
*/
public class StateMachineDemo {
public static void main(String[] args) {
// 创建 Spring 上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 扫描配置类和服务类
context.scan(
"com.tuoheng.status.statemachine.config",
"com.tuoheng.status.statemachine.service"
);
context.refresh();
// 获取服务实例
TaskStateMachineService service = context.getBean(TaskStateMachineService.class);
System.out.println("\n\n");
System.out.println("╔════════════════════════════════════════════════════════════╗");
System.out.println("║ Spring StateMachine 完整示例演示 ║");
System.out.println("║ 包含子状态、Action、Guard、事件监听器 ║");
System.out.println("╚════════════════════════════════════════════════════════════╝");
System.out.println("\n");
// 演示1: 正常流程
System.out.println("═══════════════════════════════════════════════════════════════");
System.out.println("演示1: 正常流程");
System.out.println("流程: IDLE -> PREPARING -> EXECUTING -> VALIDATING -> COMPLETED");
System.out.println("═══════════════════════════════════════════════════════════════");
service.startTask("demo-task-001");
// 等待一下
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 演示2: 失败和重试
System.out.println("\n");
System.out.println("═══════════════════════════════════════════════════════════════");
System.out.println("演示2: 失败和重试流程");
System.out.println("流程: IDLE -> PREPARING -> FAILED -> PREPARING (重试)");
System.out.println("═══════════════════════════════════════════════════════════════");
service.demonstrateFailureAndRetry("demo-task-002");
// 等待一下
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 演示3: Guard 条件不满足
System.out.println("\n");
System.out.println("═══════════════════════════════════════════════════════════════");
System.out.println("演示3: Guard 条件不满足");
System.out.println("说明: 不设置 taskIdGuard 会阻止状态转换");
System.out.println("═══════════════════════════════════════════════════════════════");
service.demonstrateGuardFailure("demo-task-003");
// 等待一下
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 演示4: 重置流程
System.out.println("\n");
System.out.println("═══════════════════════════════════════════════════════════════");
System.out.println("演示4: 重置流程");
System.out.println("流程: COMPLETED -> IDLE (重置)");
System.out.println("═══════════════════════════════════════════════════════════════");
service.demonstrateReset("demo-task-004");
System.out.println("\n\n");
System.out.println("╔════════════════════════════════════════════════════════════╗");
System.out.println("║ 所有演示完成! ║");
System.out.println("╚════════════════════════════════════════════════════════════╝");
System.out.println("\n");
// 关闭上下文
context.close();
}
}

View File

@ -0,0 +1,161 @@
package com.tuoheng.status.statemachine.demo;
import com.tuoheng.status.statemachine.events.Event;
import com.tuoheng.status.statemachine.manager.StateMachineManager;
import com.tuoheng.status.statemachine.service.StateMachineManagerService;
import com.tuoheng.status.statemachine.status.Status;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Set;
/**
* 状态机管理器演示类
* 演示如何通过ID管理多个状态机实例
*/
public class StateMachineManagerDemo {
public static void main(String[] args) {
// 创建 Spring 上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan(
"com.tuoheng.status.statemachine.config",
"com.tuoheng.status.statemachine.manager",
"com.tuoheng.status.statemachine.service"
);
context.refresh();
// 获取服务实例
StateMachineManagerService service = context.getBean(StateMachineManagerService.class);
StateMachineManager manager = context.getBean(StateMachineManager.class);
System.out.println("\n\n");
System.out.println("╔════════════════════════════════════════════════════════════╗");
System.out.println("║ Spring StateMachine 管理器演示 ║");
System.out.println("║ 演示如何通过ID管理多个状态机实例 ║");
System.out.println("╚════════════════════════════════════════════════════════════╝");
System.out.println("\n");
// 演示1: 创建多个状态机
System.out.println("═══════════════════════════════════════════════════════════════");
System.out.println("演示1: 创建多个状态机实例");
System.out.println("═══════════════════════════════════════════════════════════════");
String machineId1 = "machine-001";
String machineId2 = "machine-002";
String machineId3 = "machine-003";
// 创建状态机
service.startTask(machineId1, "task-001");
service.startTask(machineId2, "task-002");
service.startTask(machineId3, "task-003");
System.out.println("创建了3个状态机:");
System.out.println(" - " + machineId1);
System.out.println(" - " + machineId2);
System.out.println(" - " + machineId3);
System.out.println("管理的状态机数量: " + manager.getStateMachineCount());
// 等待状态转换
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 演示2: 查询不同状态机的状态
System.out.println("\n");
System.out.println("═══════════════════════════════════════════════════════════════");
System.out.println("演示2: 查询不同状态机的当前状态");
System.out.println("═══════════════════════════════════════════════════════════════");
Status status1 = service.getCurrentStatus(machineId1);
Status status2 = service.getCurrentStatus(machineId2);
Status status3 = service.getCurrentStatus(machineId3);
System.out.println("状态机 " + machineId1 + " 当前状态: " + status1);
System.out.println("状态机 " + machineId2 + " 当前状态: " + status2);
System.out.println("状态机 " + machineId3 + " 当前状态: " + status3);
// 演示3: 获取状态机详细信息
System.out.println("\n");
System.out.println("═══════════════════════════════════════════════════════════════");
System.out.println("演示3: 获取状态机详细信息");
System.out.println("═══════════════════════════════════════════════════════════════");
StateMachineManager.StateMachineInfo info1 = service.getStateMachineInfo(machineId1);
System.out.println("状态机1详细信息:");
System.out.println(" ID: " + info1.getMachineId());
System.out.println(" 当前状态: " + info1.getCurrentStatus());
System.out.println(" 是否子状态: " + info1.isSubState());
System.out.println(" 是否运行中: " + info1.isRunning());
System.out.println(" 扩展状态: " + info1.getExtendedState());
// 演示4: 为不同状态机发送不同事件
System.out.println("\n");
System.out.println("═══════════════════════════════════════════════════════════════");
System.out.println("演示4: 为不同状态机发送不同事件");
System.out.println("═══════════════════════════════════════════════════════════════");
// machine-001 继续处理
manager.getStateMachine(machineId1)
.getExtendedState().getVariables().put("prepared", true);
service.sendEvent(machineId1, Event.PREPARE);
// machine-002 发送失败事件
service.sendEvent(machineId2, Event.FAIL);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("machine-001 发送 PREPARE 事件后状态: " + service.getCurrentStatus(machineId1));
System.out.println("machine-002 发送 FAIL 事件后状态: " + service.getCurrentStatus(machineId2));
System.out.println("machine-003 状态保持不变: " + service.getCurrentStatus(machineId3));
// 演示5: 获取所有状态机ID
System.out.println("\n");
System.out.println("═══════════════════════════════════════════════════════════════");
System.out.println("演示5: 获取所有状态机ID");
System.out.println("═══════════════════════════════════════════════════════════════");
Set<String> allIds = service.getAllMachineIds();
System.out.println("所有状态机ID: " + allIds);
System.out.println("状态机数量: " + allIds.size());
// 演示6: 通过ID直接获取状态机并操作
System.out.println("\n");
System.out.println("═══════════════════════════════════════════════════════════════");
System.out.println("演示6: 通过ID直接获取状态机并操作");
System.out.println("═══════════════════════════════════════════════════════════════");
var stateMachine = manager.getStateMachine(machineId1);
if (stateMachine != null) {
System.out.println("成功获取状态机: " + machineId1);
System.out.println("当前状态: " + stateMachine.getState().getId());
System.out.println("扩展状态中的 taskId: " +
stateMachine.getExtendedState().getVariables().get("taskId"));
}
// 演示7: 移除状态机
System.out.println("\n");
System.out.println("═══════════════════════════════════════════════════════════════");
System.out.println("演示7: 移除状态机");
System.out.println("═══════════════════════════════════════════════════════════════");
boolean removed = service.removeStateMachine(machineId3);
System.out.println("移除状态机 " + machineId3 + ": " + (removed ? "成功" : "失败"));
System.out.println("移除后管理的状态机数量: " + manager.getStateMachineCount());
System.out.println("状态机 " + machineId3 + " 是否存在: " + service.exists(machineId3));
System.out.println("\n\n");
System.out.println("╔════════════════════════════════════════════════════════════╗");
System.out.println("║ 所有演示完成! ║");
System.out.println("╚════════════════════════════════════════════════════════════╝");
System.out.println("\n");
// 关闭上下文
context.close();
}
}

View File

@ -0,0 +1,15 @@
package com.tuoheng.status.statemachine.events;
/**
* 事件枚举
*/
public enum Event {
START, // 开始处理
PREPARE, // 准备就绪
EXECUTE, // 执行
VALIDATE, // 验证
COMPLETE, // 完成
FAIL, // 失败
RETRY, // 重试
RESET // 重置
}

View File

@ -0,0 +1,112 @@
package com.tuoheng.status.statemachine.guard;
import com.tuoheng.status.statemachine.events.Event;
import com.tuoheng.status.statemachine.status.Status;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;
import java.util.logging.Logger;
/**
* 任务 Guard 示例
* Guard 用于条件判断决定是否允许状态转换
*/
public class TaskGuard {
private static final Logger logger = Logger.getLogger(TaskGuard.class.getName());
/**
* 检查是否可以开始处理
*/
public static class CanStartGuard implements Guard<Status, Event> {
@Override
public boolean evaluate(StateContext<Status, Event> context) {
logger.info("=== Guard: 检查是否可以开始处理 ===");
// 检查是否有任务ID
Object taskId = context.getExtendedState().getVariables().get("taskId");
boolean canStart = taskId != null;
logger.info("任务ID存在: " + canStart);
return canStart;
}
}
/**
* 检查准备是否完成
*/
public static class PrepareCompleteGuard implements Guard<Status, Event> {
@Override
public boolean evaluate(StateContext<Status, Event> context) {
logger.info("=== Guard: 检查准备是否完成 ===");
// 检查准备状态
Object prepared = context.getExtendedState().getVariables().get("prepared");
boolean isPrepared = prepared != null && Boolean.TRUE.equals(prepared);
logger.info("准备完成: " + isPrepared);
return isPrepared;
}
}
/**
* 检查执行是否成功
*/
public static class ExecutionSuccessGuard implements Guard<Status, Event> {
@Override
public boolean evaluate(StateContext<Status, Event> context) {
logger.info("=== Guard: 检查执行是否成功 ===");
// 检查执行结果
Object result = context.getExtendedState().getVariables().get("executionResult");
boolean isSuccess = result != null && "success".equals(result);
logger.info("执行成功: " + isSuccess);
return isSuccess;
}
}
/**
* 检查验证是否通过
*/
public static class ValidationPassGuard implements Guard<Status, Event> {
@Override
public boolean evaluate(StateContext<Status, Event> context) {
logger.info("=== Guard: 检查验证是否通过 ===");
// 检查验证结果
Object validationResult = context.getExtendedState().getVariables().get("validationResult");
boolean isValid = validationResult != null && Boolean.TRUE.equals(validationResult);
logger.info("验证通过: " + isValid);
return isValid;
}
}
/**
* 检查是否可以重试
*/
public static class CanRetryGuard implements Guard<Status, Event> {
@Override
public boolean evaluate(StateContext<Status, Event> context) {
logger.info("=== Guard: 检查是否可以重试 ===");
// 检查重试次数
Integer retryCount = (Integer) context.getExtendedState().getVariables().getOrDefault("retryCount", 0);
boolean canRetry = retryCount < 3; // 最多重试3次
logger.info("当前重试次数: " + retryCount + ", 可以重试: " + canRetry);
return canRetry;
}
}
/**
* 总是返回 true Guard用于无条件转换
*/
public static class AlwaysTrueGuard implements Guard<Status, Event> {
@Override
public boolean evaluate(StateContext<Status, Event> context) {
return true;
}
}
}

View File

@ -0,0 +1,157 @@
package com.tuoheng.status.statemachine.listener;
import com.tuoheng.status.statemachine.events.Event;
import com.tuoheng.status.statemachine.status.Status;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.transition.Transition;
import java.util.logging.Logger;
/**
* 状态机事件监听器
* 监听状态机的各种事件如状态变化转换等
*/
public class TaskStateMachineListener implements StateMachineListener<Status, Event> {
private static final Logger logger = Logger.getLogger(TaskStateMachineListener.class.getName());
/**
* 状态机启动时调用
*/
@Override
public void stateMachineStarted(StateMachine<Status, Event> stateMachine) {
logger.info("========== 状态机启动 ==========");
if (stateMachine.getInitialState() != null) {
logger.info("初始状态: " + stateMachine.getInitialState().getId());
} else {
logger.info("初始状态: null");
}
}
/**
* 状态机停止时调用
*/
@Override
public void stateMachineStopped(StateMachine<Status, Event> stateMachine) {
logger.info("========== 状态机停止 ==========");
if (stateMachine.getState() != null) {
logger.info("最终状态: " + stateMachine.getState().getId());
} else {
logger.info("最终状态: null");
}
}
/**
* 状态机错误时调用
*/
@Override
public void stateMachineError(StateMachine<Status, Event> stateMachine, Exception exception) {
logger.severe("========== 状态机错误 ==========");
logger.severe("错误信息: " + exception.getMessage());
exception.printStackTrace();
}
/**
* 状态进入时调用
*/
@Override
public void stateEntered(State<Status, Event> state) {
logger.info("========== 进入状态: " + state.getId() + " ==========");
}
/**
* 状态退出时调用
*/
@Override
public void stateExited(State<Status, Event> state) {
logger.info("========== 退出状态: " + state.getId() + " ==========");
}
/**
* 状态改变时调用
*/
@Override
public void stateChanged(State<Status, Event> from, State<Status, Event> to) {
logger.info("========== 状态改变 ==========");
logger.info("从: " + (from != null ? from.getId() : "null"));
logger.info("到: " + (to != null ? to.getId() : "null"));
}
/**
* 转换开始时调用
*/
@Override
public void transitionStarted(Transition<Status, Event> transition) {
logger.info("========== 转换开始 ==========");
logger.info("从: " + (transition.getSource() != null ? transition.getSource().getId() : "null"));
logger.info("到: " + (transition.getTarget() != null ? transition.getTarget().getId() : "null"));
// 检查 trigger 是否为 null某些转换可能没有 trigger比如初始转换
if (transition.getTrigger() != null) {
logger.info("事件: " + transition.getTrigger().getEvent());
} else {
logger.info("事件: null (可能是初始转换或内部转换)");
}
}
/**
* 转换结束时调用
*/
@Override
public void transitionEnded(Transition<Status, Event> transition) {
logger.info("========== 转换结束 ==========");
logger.info("从: " + (transition.getSource() != null ? transition.getSource().getId() : "null"));
logger.info("到: " + (transition.getTarget() != null ? transition.getTarget().getId() : "null"));
// 检查 trigger 是否为 null
if (transition.getTrigger() != null) {
logger.info("事件: " + transition.getTrigger().getEvent());
} else {
logger.info("事件: null (可能是初始转换或内部转换)");
}
}
/**
* 转换选择时调用
*/
@Override
public void transition(Transition<Status, Event> transition) {
logger.info("========== 转换选择 ==========");
logger.info("从: " + (transition.getSource() != null ? transition.getSource().getId() : "null"));
logger.info("到: " + (transition.getTarget() != null ? transition.getTarget().getId() : "null"));
// 检查 trigger 是否为 null
if (transition.getTrigger() != null) {
logger.info("事件: " + transition.getTrigger().getEvent());
} else {
logger.info("事件: null (可能是初始转换或内部转换)");
}
}
/**
* 事件未接受时调用没有匹配的转换
*/
@Override
public void eventNotAccepted(org.springframework.messaging.Message<Event> event) {
logger.warning("========== 事件未接受 ==========");
logger.warning("事件: " + event.getPayload());
logger.warning("当前状态可能不支持此事件");
}
/**
* 扩展状态改变时调用
*/
@Override
public void extendedStateChanged(Object key, Object value) {
logger.info("========== 扩展状态改变 ==========");
logger.info("键: " + key + ", 值: " + value);
}
/**
* 状态上下文入口时调用
*/
@Override
public void stateContext(StateContext<Status, Event> stateContext) {
// 可以在这里记录状态上下文信息
}
}

View File

@ -0,0 +1,262 @@
package com.tuoheng.status.statemachine.manager;
import com.tuoheng.status.statemachine.events.Event;
import com.tuoheng.status.statemachine.status.Status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.state.State;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
/**
* 状态机管理器
* 用于管理多个状态机实例通过ID获取和查询状态机
*/
@Component
public class StateMachineManager {
private static final Logger logger = Logger.getLogger(StateMachineManager.class.getName());
@Autowired
private StateMachineFactory<Status, Event> stateMachineFactory;
// 存储状态机实例的Mapkey为状态机ID
private final Map<String, StateMachine<Status, Event>> stateMachineMap = new ConcurrentHashMap<>();
/**
* 创建或获取状态机实例
* 如果ID对应的状态机不存在则创建新实例如果存在则返回现有实例
*
* @param machineId 状态机ID
* @return 状态机实例
*/
public StateMachine<Status, Event> getOrCreateStateMachine(String machineId) {
return stateMachineMap.computeIfAbsent(machineId, id -> {
logger.info("创建新状态机实例ID: " + id);
StateMachine<Status, Event> stateMachine = stateMachineFactory.getStateMachine(id);
// 将machineId存储到扩展状态中
stateMachine.getExtendedState().getVariables().put("machineId", id);
// 启动状态机
stateMachine.start();
return stateMachine;
});
}
/**
* 获取状态机实例如果不存在则返回null
*
* @param machineId 状态机ID
* @return 状态机实例如果不存在则返回null
*/
public StateMachine<Status, Event> getStateMachine(String machineId) {
return stateMachineMap.get(machineId);
}
/**
* 创建新的状态机实例如果已存在则先停止并移除旧的
*
* @param machineId 状态机ID
* @return 新创建的状态机实例
*/
public StateMachine<Status, Event> createStateMachine(String machineId) {
// 如果已存在先停止并移除
StateMachine<Status, Event> existing = stateMachineMap.remove(machineId);
if (existing != null) {
logger.info("停止并移除已存在的状态机ID: " + machineId);
try {
existing.stop();
} catch (Exception e) {
logger.warning("停止状态机时发生错误: " + e.getMessage());
}
}
// 创建新实例
logger.info("创建新状态机实例ID: " + machineId);
StateMachine<Status, Event> stateMachine = stateMachineFactory.getStateMachine(machineId);
stateMachine.getExtendedState().getVariables().put("machineId", machineId);
stateMachine.start();
stateMachineMap.put(machineId, stateMachine);
return stateMachine;
}
/**
* 获取状态机的当前状态
*
* @param machineId 状态机ID
* @return 当前状态如果状态机不存在则返回null
*/
public Status getCurrentStatus(String machineId) {
StateMachine<Status, Event> stateMachine = stateMachineMap.get(machineId);
if (stateMachine == null) {
logger.warning("状态机不存在ID: " + machineId);
return null;
}
State<Status, Event> state = stateMachine.getState();
if (state == null) {
return null;
}
return state.getId();
}
/**
* 获取状态机的详细信息包括当前状态和扩展状态
*
* @param machineId 状态机ID
* @return 状态机信息如果状态机不存在则返回null
*/
public StateMachineInfo getStateMachineInfo(String machineId) {
StateMachine<Status, Event> stateMachine = stateMachineMap.get(machineId);
if (stateMachine == null) {
return null;
}
StateMachineInfo info = new StateMachineInfo();
info.setMachineId(machineId);
State<Status, Event> state = stateMachine.getState();
if (state != null) {
info.setCurrentStatus(state.getId());
info.setIsSubState(state.isSubmachineState());
}
info.setExtendedState(stateMachine.getExtendedState().getVariables());
// 通过检查状态机是否有状态来判断是否在运行
// Spring StateMachine 3.2.0 没有 isRunning() 方法使用状态判断
info.setIsRunning(state != null);
return info;
}
/**
* 移除状态机实例
*
* @param machineId 状态机ID
* @return 是否成功移除
*/
public boolean removeStateMachine(String machineId) {
StateMachine<Status, Event> stateMachine = stateMachineMap.remove(machineId);
if (stateMachine != null) {
logger.info("移除状态机实例ID: " + machineId);
try {
stateMachine.stop();
return true;
} catch (Exception e) {
logger.warning("停止状态机时发生错误: " + e.getMessage());
return false;
}
}
return false;
}
/**
* 检查状态机是否存在
*
* @param machineId 状态机ID
* @return 是否存在
*/
public boolean exists(String machineId) {
return stateMachineMap.containsKey(machineId);
}
/**
* 获取所有状态机的ID列表
*
* @return 状态机ID集合
*/
public java.util.Set<String> getAllMachineIds() {
return stateMachineMap.keySet();
}
/**
* 获取当前管理的状态机数量
*
* @return 状态机数量
*/
public int getStateMachineCount() {
return stateMachineMap.size();
}
/**
* 清空所有状态机实例
*/
public void clearAll() {
logger.info("清空所有状态机实例,数量: " + stateMachineMap.size());
for (Map.Entry<String, StateMachine<Status, Event>> entry : stateMachineMap.entrySet()) {
try {
entry.getValue().stop();
} catch (Exception e) {
logger.warning("停止状态机时发生错误ID: " + entry.getKey() + ", 错误: " + e.getMessage());
}
}
stateMachineMap.clear();
}
/**
* 状态机信息类
*/
public static class StateMachineInfo {
private String machineId;
private Status currentStatus;
private boolean isSubState;
private boolean isRunning;
private Map<Object, Object> extendedState;
// Getters and Setters
public String getMachineId() {
return machineId;
}
public void setMachineId(String machineId) {
this.machineId = machineId;
}
public Status getCurrentStatus() {
return currentStatus;
}
public void setCurrentStatus(Status currentStatus) {
this.currentStatus = currentStatus;
}
public boolean isSubState() {
return isSubState;
}
public void setIsSubState(boolean subState) {
isSubState = subState;
}
public boolean isRunning() {
return isRunning;
}
public void setIsRunning(boolean running) {
isRunning = running;
}
public Map<Object, Object> getExtendedState() {
return extendedState;
}
public void setExtendedState(Map<Object, Object> extendedState) {
this.extendedState = extendedState;
}
@Override
public String toString() {
return "StateMachineInfo{" +
"machineId='" + machineId + '\'' +
", currentStatus=" + currentStatus +
", isSubState=" + isSubState +
", isRunning=" + isRunning +
", extendedState=" + extendedState +
'}';
}
}
}

View File

@ -0,0 +1,128 @@
package com.tuoheng.status.statemachine.service;
import com.tuoheng.status.statemachine.events.Event;
import com.tuoheng.status.statemachine.manager.StateMachineManager;
import com.tuoheng.status.statemachine.status.Status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.statemachine.StateMachine;
import org.springframework.stereotype.Service;
import java.util.Set;
import java.util.logging.Logger;
/**
* 状态机管理器服务类
* 提供便捷的方法来使用状态机管理器
*/
@Service
public class StateMachineManagerService {
private static final Logger logger = Logger.getLogger(StateMachineManagerService.class.getName());
@Autowired
private StateMachineManager stateMachineManager;
/**
* 通过ID获取状态机并发送事件
*
* @param machineId 状态机ID
* @param event 事件
* @return 事件是否被接受
*/
public boolean sendEvent(String machineId, Event event) {
StateMachine<Status, Event> stateMachine = stateMachineManager.getStateMachine(machineId);
if (stateMachine == null) {
logger.warning("状态机不存在ID: " + machineId);
return false;
}
return stateMachine.sendEvent(event);
}
/**
* 通过ID获取状态机的当前状态
*
* @param machineId 状态机ID
* @return 当前状态
*/
public Status getCurrentStatus(String machineId) {
return stateMachineManager.getCurrentStatus(machineId);
}
/**
* 通过ID获取状态机的详细信息
*
* @param machineId 状态机ID
* @return 状态机信息
*/
public StateMachineManager.StateMachineInfo getStateMachineInfo(String machineId) {
return stateMachineManager.getStateMachineInfo(machineId);
}
/**
* 创建或获取状态机并发送START事件
*
* @param machineId 状态机ID
* @param taskId 任务ID可选
* @return 是否成功
*/
public boolean startTask(String machineId, String taskId) {
StateMachine<Status, Event> stateMachine = stateMachineManager.getOrCreateStateMachine(machineId);
// 设置任务ID到扩展状态
if (taskId != null) {
stateMachine.getExtendedState().getVariables().put("taskId", taskId);
}
return stateMachine.sendEvent(Event.START);
}
/**
* 获取所有状态机的ID列表
*
* @return 状态机ID集合
*/
public Set<String> getAllMachineIds() {
return stateMachineManager.getAllMachineIds();
}
/**
* 移除状态机
*
* @param machineId 状态机ID
* @return 是否成功移除
*/
public boolean removeStateMachine(String machineId) {
return stateMachineManager.removeStateMachine(machineId);
}
/**
* 检查状态机是否存在
*
* @param machineId 状态机ID
* @return 是否存在
*/
public boolean exists(String machineId) {
return stateMachineManager.exists(machineId);
}
/**
* 打印状态机信息用于调试
*
* @param machineId 状态机ID
*/
public void printStateMachineInfo(String machineId) {
StateMachineManager.StateMachineInfo info = stateMachineManager.getStateMachineInfo(machineId);
if (info == null) {
logger.warning("状态机不存在ID: " + machineId);
return;
}
logger.info("========== 状态机信息 ==========");
logger.info("ID: " + info.getMachineId());
logger.info("当前状态: " + info.getCurrentStatus());
logger.info("是否子状态: " + info.isSubState());
logger.info("是否运行中: " + info.isRunning());
logger.info("扩展状态: " + info.getExtendedState());
logger.info("================================");
}
}

View File

@ -0,0 +1,194 @@
package com.tuoheng.status.statemachine.service;
import com.tuoheng.status.statemachine.events.Event;
import com.tuoheng.status.statemachine.status.Status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.stereotype.Service;
import java.util.logging.Logger;
/**
* 任务状态机服务示例
* 演示如何使用状态机
*/
@Service
public class TaskStateMachineService {
private static final Logger logger = Logger.getLogger(TaskStateMachineService.class.getName());
@Autowired
private StateMachineFactory<Status, Event> stateMachineFactory;
/**
* 启动任务处理流程
*/
public void startTask(String taskId) {
logger.info("\n========== 开始任务处理流程 ==========");
StateMachine<Status, Event> stateMachine = stateMachineFactory.getStateMachine();
try {
// 设置任务ID到扩展状态
stateMachine.getExtendedState().getVariables().put("taskId", taskId);
stateMachine.getExtendedState().getVariables().put("retryCount", 0);
// 启动状态机
stateMachine.start();
logger.info("状态机已启动,当前状态: " + stateMachine.getState().getId());
// 发送 START 事件
logger.info("\n--- 发送 START 事件 ---");
stateMachine.sendEvent(Event.START);
logger.info("当前状态: " + stateMachine.getState().getId());
// 模拟准备完成
Thread.sleep(100);
stateMachine.getExtendedState().getVariables().put("prepared", true);
logger.info("\n--- 发送 PREPARE 事件 ---");
stateMachine.sendEvent(Event.PREPARE);
logger.info("当前状态: " + stateMachine.getState().getId());
// 模拟执行完成在发送事件之前设置结果以便 Guard 可以检查
Thread.sleep(200);
stateMachine.getExtendedState().getVariables().put("executionResult", "success");
logger.info("\n--- 发送 EXECUTE 事件 ---");
stateMachine.sendEvent(Event.EXECUTE);
logger.info("当前状态: " + stateMachine.getState().getId());
// 模拟验证完成在发送事件之前设置验证结果以便 Guard 可以检查
Thread.sleep(100);
stateMachine.getExtendedState().getVariables().put("validationResult", true);
logger.info("\n--- 发送 VALIDATE 事件 ---");
stateMachine.sendEvent(Event.VALIDATE);
logger.info("当前状态: " + stateMachine.getState().getId());
logger.info("\n========== 任务处理流程完成 ==========\n");
} catch (Exception e) {
logger.severe("处理任务时发生错误: " + e.getMessage());
e.printStackTrace();
} finally {
// 停止状态机
stateMachine.stop();
}
}
/**
* 演示失败和重试流程
*/
public void demonstrateFailureAndRetry(String taskId) {
logger.info("\n========== 演示失败和重试流程 ==========");
StateMachine<Status, Event> stateMachine = stateMachineFactory.getStateMachine();
try {
// 设置任务ID
stateMachine.getExtendedState().getVariables().put("taskId", taskId);
stateMachine.getExtendedState().getVariables().put("retryCount", 0);
// 启动状态机
stateMachine.start();
logger.info("状态机已启动,当前状态: " + stateMachine.getState().getId());
// 发送 START 事件
logger.info("\n--- 发送 START 事件 ---");
stateMachine.sendEvent(Event.START);
logger.info("当前状态: " + stateMachine.getState().getId());
// 模拟失败
logger.info("\n--- 发送 FAIL 事件(模拟失败)---");
stateMachine.sendEvent(Event.FAIL);
logger.info("当前状态: " + stateMachine.getState().getId());
// 增加重试次数
Integer retryCount = (Integer) stateMachine.getExtendedState().getVariables().getOrDefault("retryCount", 0);
stateMachine.getExtendedState().getVariables().put("retryCount", retryCount + 1);
// 重试
logger.info("\n--- 发送 RETRY 事件 ---");
stateMachine.sendEvent(Event.RETRY);
logger.info("当前状态: " + stateMachine.getState().getId());
logger.info("\n========== 失败和重试流程演示完成 ==========\n");
} catch (Exception e) {
logger.severe("演示失败和重试流程时发生错误: " + e.getMessage());
e.printStackTrace();
} finally {
stateMachine.stop();
}
}
/**
* 演示 Guard 条件不满足的情况
*/
public void demonstrateGuardFailure(String taskId) {
logger.info("\n========== 演示 Guard 条件不满足 ==========");
StateMachine<Status, Event> stateMachine = stateMachineFactory.getStateMachine();
try {
// 不设置 taskId导致 CanStartGuard 失败
// stateMachine.getExtendedState().getVariables().put("taskId", taskId);
// 启动状态机
stateMachine.start();
logger.info("状态机已启动,当前状态: " + stateMachine.getState().getId());
// 发送 START 事件应该失败因为 Guard 条件不满足
logger.info("\n--- 发送 START 事件(没有 taskIdGuard 应该失败)---");
boolean accepted = stateMachine.sendEvent(Event.START);
logger.info("事件是否被接受: " + accepted);
logger.info("当前状态: " + stateMachine.getState().getId());
logger.info("\n========== Guard 条件不满足演示完成 ==========\n");
} catch (Exception e) {
logger.severe("演示 Guard 失败时发生错误: " + e.getMessage());
e.printStackTrace();
} finally {
stateMachine.stop();
}
}
/**
* 演示重置流程
*/
public void demonstrateReset(String taskId) {
logger.info("\n========== 演示重置流程 ==========");
StateMachine<Status, Event> stateMachine = stateMachineFactory.getStateMachine();
try {
// 设置任务ID
stateMachine.getExtendedState().getVariables().put("taskId", taskId);
// 启动状态机并完成流程
stateMachine.start();
stateMachine.sendEvent(Event.START);
stateMachine.getExtendedState().getVariables().put("prepared", true);
stateMachine.sendEvent(Event.PREPARE);
stateMachine.getExtendedState().getVariables().put("executionResult", "success");
stateMachine.sendEvent(Event.EXECUTE);
stateMachine.getExtendedState().getVariables().put("validationResult", true);
stateMachine.sendEvent(Event.VALIDATE);
logger.info("当前状态: " + stateMachine.getState().getId());
// 重置
logger.info("\n--- 发送 RESET 事件 ---");
stateMachine.sendEvent(Event.RESET);
logger.info("当前状态: " + stateMachine.getState().getId());
logger.info("\n========== 重置流程演示完成 ==========\n");
} catch (Exception e) {
logger.severe("演示重置流程时发生错误: " + e.getMessage());
e.printStackTrace();
} finally {
stateMachine.stop();
}
}
}

View File

@ -0,0 +1,19 @@
package com.tuoheng.status.statemachine.status;
/**
* 状态枚举
* 主状态IDLE, PROCESSING, COMPLETED, FAILED
* PROCESSING 包含子状态PREPARING, EXECUTING, VALIDATING
*/
public enum Status {
// 主状态
IDLE, // 空闲状态
PROCESSING, // 处理中父状态包含子状态
COMPLETED, // 已完成
FAILED, // 失败
// PROCESSING 的子状态
PREPARING, // 准备中PROCESSING 的子状态
EXECUTING, // 执行中PROCESSING 的子状态
VALIDATING // 验证中PROCESSING 的子状态
}

View File

@ -0,0 +1,765 @@
# 大疆无人机巢状态机设计文档
## 一、概述
本文档基于大疆无人机巢功能统计清单,设计了一套完整的状态机模型,用于管理无人机和机巢的各种状态转换,确保操作的安全性和正确性。
### 1.1 设计目标
- **状态管理**:清晰定义无人机和机巢的所有可能状态
- **状态转换**:明确状态之间的转换条件和规则
- **业务规则**:将业务限制条件转化为状态机 Guard 和 Action
- **安全控制**:通过状态机确保操作的安全执行顺序
### 1.2 状态机层次结构
状态机采用**分层设计**,包含以下层次:
1. **顶层状态机**:机巢整体状态
2. **机巢子状态机**:机巢设备状态(舱门、电源等)
3. **无人机子状态机**:无人机状态(电源、飞行、任务等)
4. **模式状态机**DRC模式、调试模式等
## 二、状态定义
### 2.1 机巢状态AirportState
#### 主状态
| 状态 | 枚举值 | 说明 |
|------|--------|------|
| 离线 | `OFFLINE` | 机巢设备离线,无法通信 |
| 在线 | `ONLINE` | 机巢设备在线,可接收指令 |
| 重启中 | `REBOOTING` | 机巢正在重启 |
#### 子状态ONLINE 的子状态)
| 状态 | 枚举值 | 说明 |
|------|--------|------|
| 待机 | `STANDBY` | 机巢待机状态(初始子状态) |
| 调试模式 | `DEBUG_MODE` | 调试模式开启中 |
| 操作中 | `OPERATING` | 正在执行操作(开舱、关舱等) |
#### 舱门状态OPERATING 的子状态)
| 状态 | 枚举值 | 说明 |
|------|--------|------|
| 已关闭 | `COVER_CLOSED` | 舱门已关闭 |
| 已打开 | `COVER_OPENED` | 舱门已打开 |
| 半开 | `COVER_HALF_OPEN` | 舱门半开 |
| 状态异常 | `COVER_ERROR` | 舱门状态异常 |
| 开舱中 | `COVER_OPENING` | 正在打开舱门 |
| 关舱中 | `COVER_CLOSING` | 正在关闭舱门 |
### 2.2 无人机状态DroneState
#### 主状态
| 状态 | 枚举值 | 说明 |
|------|--------|------|
| 关机 | `POWER_OFF` | 无人机电源关闭(初始状态) |
| 开机 | `POWER_ON` | 无人机电源已开启 |
| 飞行中 | `FLYING` | 无人机正在飞行(父状态) |
| 返航中 | `RETURNING_HOME` | 无人机正在返航 |
| 降落中 | `LANDING` | 无人机正在降落 |
| 已降落 | `LANDED` | 无人机已降落 |
#### 飞行子状态FLYING 的子状态)
| 状态 | 枚举值 | 说明 |
|------|--------|------|
| 待飞 | `READY_TO_FLY` | 准备起飞(初始子状态) |
| 一键起飞模式 | `ONE_KEY_TAKEOFF_MODE` | 一键起飞飞行模式(父状态) |
| 航线飞行模式 | `WAYLINE_FLIGHT_MODE` | 航线飞行模式(父状态) |
#### 一键起飞子状态ONE_KEY_TAKEOFF_MODE 的子状态)
| 状态 | 枚举值 | 说明 |
|------|--------|------|
| 起飞准备 | `TAKEOFF_PREPARING` | 一键起飞准备中(初始子状态) |
| 起飞飞行中 | `TAKEOFF_FLYING` | 正常起飞飞行中 |
| 起飞指点飞行 | `TAKEOFF_POINT_FLYING` | 起飞过程中的指点飞行(子状态) |
| 起飞急停 | `TAKEOFF_EMERGENCY_STOP` | 起飞过程中的急停(子状态) |
| 起飞完成 | `TAKEOFF_COMPLETED` | 一键起飞完成 |
| 起飞失败 | `TAKEOFF_FAILED` | 一键起飞失败 |
#### 航线飞行子状态WAYLINE_FLIGHT_MODE 的子状态)
| 状态 | 枚举值 | 说明 |
|------|--------|------|
| 航线准备 | `WAYLINE_PREPARING` | 航线任务准备中(初始子状态) |
| 航线飞行中 | `WAYLINE_FLYING` | 正常航线飞行中 |
| 航线指点飞行 | `WAYLINE_POINT_FLYING` | 航线过程中的指点飞行(子状态) |
| 航线急停 | `WAYLINE_EMERGENCY_STOP` | 航线过程中的急停(子状态) |
| 航线悬停 | `WAYLINE_HOVER` | 航线飞行中的悬停状态 |
| 航线完成 | `WAYLINE_COMPLETED` | 航线任务完成 |
| 航线失败 | `WAYLINE_FAILED` | 航线任务失败 |
##### 指点飞行子状态WAYLINE_POINT_FLYING 的子状态)
| 状态 | 枚举值 | 说明 |
|------|--------|------|
| 指点飞行中 | `POINT_FLYING` | 正在飞向指定目标点(初始子状态) |
| 到达目的地 | `POINT_ARRIVED` | 已到达指定目标点 |
| 指点急停 | `POINT_EMERGENCY_STOP` | 指点飞行过程中的急停 |
> 说明:
> - **一键起飞**和**航线飞行**是两种不同的飞行模式作为FLYING的并列子状态
> - **指点飞行**在两种模式中都作为子状态存在:
> - 起飞指点飞行TAKEOFF_POINT_FLYING在一键起飞过程中插入的指点飞行
> - 航线指点飞行WAYLINE_POINT_FLYING在航线飞行过程中插入的指点飞行
> - **悬停状态**WAYLINE_HOVER
> - 仅存在于航线飞行模式中
> - 急停或暂停后进入悬停状态
> - 悬停后可以恢复继续执行航线
> - **急停**有三个层级:
> - FLYING层级的急停FLYING_EMERGENCY_STOP可以在任何飞行状态下触发
> - 起飞急停TAKEOFF_EMERGENCY_STOP一键起飞模式内的急停子状态
> - 航线急停WAYLINE_EMERGENCY_STOP航线飞行模式内的急停子状态急停后进入WAYLINE_HOVER
> - **航线暂停**WAYLINE_PAUSED与急停不同暂停后也会进入WAYLINE_HOVER悬停状态
### 2.3 模式状态ModeState
#### DRC模式状态
| 状态 | 枚举值 | 说明 |
|------|--------|------|
| DRC未激活 | `DRC_INACTIVE` | DRC模式未激活初始状态 |
| DRC激活中 | `DRC_ENTERING` | 正在进入DRC模式 |
| DRC已激活 | `DRC_ACTIVE` | DRC模式已激活 |
| DRC退出中 | `DRC_EXITING` | 正在退出DRC模式 |
#### 调试模式状态
| 状态 | 枚举值 | 说明 |
|------|--------|------|
| 调试模式关闭 | `DEBUG_OFF` | 调试模式关闭(初始状态) |
| 调试模式开启中 | `DEBUG_OPENING` | 正在开启调试模式 |
| 调试模式已开启 | `DEBUG_ON` | 调试模式已开启 |
| 调试模式关闭中 | `DEBUG_CLOSING` | 正在关闭调试模式 |
#### 控制权状态
| 状态 | 枚举值 | 说明 |
|------|--------|------|
| 未获取 | `AUTHORITY_NONE` | 未获取控制权(初始状态) |
| 飞行控制权已获取 | `FLIGHT_AUTHORITY` | 已获取飞行控制权 |
| 负载控制权已获取 | `PAYLOAD_AUTHORITY` | 已获取负载控制权 |
| 全部控制权已获取 | `ALL_AUTHORITY` | 已获取飞行和负载控制权 |
## 三、事件定义
### 3.1 机巢事件AirportEvent
| 事件 | 枚举值 | 说明 | 触发源 |
|------|--------|------|---------|
| 机巢上线 | `AIRPORT_ONLINE` | 机巢设备上线 | OSD心跳 |
| 机巢离线 | `AIRPORT_OFFLINE` | 机巢设备离线 | OSD心跳 |
| 开启调试模式 | `DEBUG_MODE_OPEN` | 开启调试模式 | 用户指令 |
| 关闭调试模式 | `DEBUG_MODE_CLOSE` | 关闭调试模式 | 用户指令/自动 |
| 开舱指令 | `COVER_OPEN` | 开舱指令 | 用户指令 |
| 关舱指令 | `COVER_CLOSE` | 关舱指令 | 用户指令 |
| 开舱完成 | `COVER_OPENED` | 舱门已打开 | OSD状态 |
| 关舱完成 | `COVER_CLOSED` | 舱门已关闭 | OSD状态 |
| 机巢重启 | `AIRPORT_REBOOT` | 机巢重启指令 | 用户指令 |
| 重启完成 | `REBOOT_COMPLETED` | 重启完成 | Events事件 |
### 3.2 无人机事件DroneEvent
| 事件 | 枚举值 | 说明 | 触发源 |
|------|--------|------|---------|
| 无人机开机 | `DRONE_POWER_ON` | 无人机开机指令 | 用户指令 |
| 无人机关机 | `DRONE_POWER_OFF` | 无人机关机指令 | 用户指令 |
| 开机完成 | `POWER_ON_COMPLETED` | 开机完成 | OSD状态 |
| 关机完成 | `POWER_OFF_COMPLETED` | 关机完成 | OSD状态 |
| 获取飞行控制权 | `GRAB_FLIGHT_AUTHORITY` | 获取飞行控制权 | 自动/用户指令 |
| 获取负载控制权 | `GRAB_PAYLOAD_AUTHORITY` | 获取负载控制权 | 自动/用户指令 |
| 控制权获取完成 | `AUTHORITY_GRABBED` | 控制权获取完成 | Services回复 |
### 3.3 飞行事件FlightEvent
| 事件 | 枚举值 | 说明 | 触发源 |
|------|--------|------|---------|
| 准备起飞 | `READY_TO_TAKEOFF` | 准备起飞 | 任务准备完成 |
| 开始飞行 | `START_FLIGHT` | 开始飞行 | 用户指令/自动 |
| 进入手动模式 | `ENTER_MANUAL_MODE` | 进入手动飞行模式 | DRC模式激活 |
| 进入自动模式 | `ENTER_AUTO_MODE` | 进入自动飞行模式 | 任务开始 |
| 急停指令 | `EMERGENCY_STOP` | 急停指令 | 用户指令 |
| 取消急停 | `CANCEL_EMERGENCY_STOP` | 取消急停 | 用户指令/自动 |
| 返航指令 | `RETURN_HOME` | 返航指令 | 用户指令/自动 |
| 返航完成 | `RETURN_HOME_COMPLETED` | 返航完成 | OSD状态 |
| 开始降落 | `START_LANDING` | 开始降落 | 自动/用户指令 |
| 降落完成 | `LANDING_COMPLETED` | 降落完成 | OSD状态 |
### 3.4 任务事件TaskEvent
| 事件 | 枚举值 | 说明 | 触发源 |
|------|--------|------|---------|
| 准备航线任务 | `PREPARE_WAYLINE` | 准备航线任务 | 用户指令 |
| 执行航线任务 | `EXECUTE_WAYLINE` | 执行航线任务 | 用户指令 |
| 准备完成 | `PREPARE_COMPLETED` | 任务准备完成 | Services回复 |
| 任务开始 | `TASK_STARTED` | 任务开始执行 | Events事件 |
| 任务暂停 | `TASK_PAUSE` | 暂停任务 | 用户指令 |
| 任务恢复 | `TASK_RECOVERY` | 恢复任务 | 用户指令 |
| 任务完成 | `TASK_COMPLETED` | 任务完成 | Events事件 |
| 任务失败 | `TASK_FAILED` | 任务失败 | Events事件 |
| 指点飞行 | `FLY_TO_POINT` | 指点飞行指令 | 用户指令 |
| 更新指点目标 | `UPDATE_FLY_TO_POINT` | 更新指点飞行目标 | 用户指令 |
| 一键起飞 | `TAKEOFF_TO_POINT` | 一键起飞指令 | 用户指令 |
### 3.5 DRC模式事件DRCEvent
| 事件 | 枚举值 | 说明 | 触发源 |
|------|--------|------|---------|
| 进入DRC模式 | `ENTER_DRC_MODE` | 进入DRC模式指令 | 用户指令/自动 |
| 退出DRC模式 | `EXIT_DRC_MODE` | 退出DRC模式指令 | 用户指令/自动 |
| DRC模式已激活 | `DRC_ACTIVATED` | DRC模式已激活 | Services回复 |
| DRC模式已退出 | `DRC_DEACTIVATED` | DRC模式已退出 | Services回复 |
## 四、状态转换规则
### 4.1 机巢状态转换
```
OFFLINE
└─[AIRPORT_ONLINE]─> ONLINE(STANDBY)
└─[DEBUG_MODE_OPEN]─> ONLINE(DEBUG_MODE)
└─[COVER_OPEN]─> ONLINE(OPERATING(COVER_OPENING))
└─[COVER_OPENED]─> ONLINE(OPERATING(COVER_OPENED))
└─[DEBUG_MODE_CLOSE]─> ONLINE(STANDBY)
└─[AIRPORT_REBOOT]─> REBOOTING
└─[REBOOT_COMPLETED]─> ONLINE(STANDBY)
ONLINE
└─[AIRPORT_OFFLINE]─> OFFLINE
```
**Guard 规则:**
- `COVER_OPEN`:检查当前舱门状态不是已打开
- `COVER_CLOSE`:检查当前舱门状态不是已关闭
- `DEBUG_MODE_OPEN`:检查当前不是调试模式
- `AIRPORT_REBOOT`:检查当前是调试模式
### 4.2 无人机电源状态转换
```
POWER_OFF (初始状态)
└─[DRONE_POWER_ON]─> POWER_ON
└─[POWER_ON_COMPLETED]─> POWER_ON(READY_TO_FLY)
POWER_ON
└─[DRONE_POWER_OFF]─> POWER_OFF
└─[POWER_OFF_COMPLETED]─> POWER_OFF
```
**Guard 规则:**
- `DRONE_POWER_ON`:检查机巢在线且处于调试模式
- `DRONE_POWER_OFF`:检查机巢在线且处于调试模式
### 4.3 飞行状态转换
```
READY_TO_FLY
└─[START_FLIGHT]─> FLYING
├─[ENTER_MANUAL_MODE]─> FLYING(MANUAL_FLIGHT)
│ └─[EMERGENCY_STOP]─> FLYING(EMERGENCY_STOP)
│ └─[CANCEL_EMERGENCY_STOP]─> FLYING(MANUAL_FLIGHT)
└─[ENTER_AUTO_MODE]─> FLYING(AUTO_FLIGHT(TASK_PREPARING))
└─[TASK_STARTED]─> FLYING(AUTO_FLIGHT(TASK_EXECUTING))
├─[TASK_PAUSE]─> FLYING(AUTO_FLIGHT(TASK_PAUSED))
│ └─[TASK_RECOVERY]─> FLYING(AUTO_FLIGHT(TASK_EXECUTING))
├─[TASK_COMPLETED]─> FLYING(AUTO_FLIGHT(TASK_COMPLETED))
└─[TASK_FAILED]─> FLYING(AUTO_FLIGHT(TASK_FAILED))
FLYING
└─[RETURN_HOME]─> RETURNING_HOME
└─[RETURN_HOME_COMPLETED]─> RETURNING_HOME
└─[START_LANDING]─> LANDING
└─[LANDING_COMPLETED]─> LANDED
└─[DRONE_POWER_OFF]─> POWER_OFF
```
**Guard 规则:**
- `START_FLIGHT`:检查无人机已开机且机巢在线
- `ENTER_MANUAL_MODE`检查DRC模式已激活
- `ENTER_AUTO_MODE`:检查任务准备完成
- `RETURN_HOME`:检查不在指点飞行中(如果是指点飞行,需要先停止)
- `EMERGENCY_STOP`检查DRC模式已激活且电量充足
### 4.4 DRC模式状态转换
```
DRC_INACTIVE (初始状态)
└─[ENTER_DRC_MODE]─> DRC_ENTERING
└─[DRC_ACTIVATED]─> DRC_ACTIVE
└─[EXIT_DRC_MODE]─> DRC_EXITING
└─[DRC_DEACTIVATED]─> DRC_INACTIVE
```
**自动转换规则Action**
- 指点飞行中自动退出DRC模式
- 一键起飞中自动退出DRC模式
- 返航中自动退出DRC模式
- 降落中自动退出DRC模式
- 指点飞行停止后自动进入DRC模式
- 一键起飞停止后自动进入DRC模式
- 急停操作自动进入DRC模式
- 摇杆控制自动进入DRC模式
### 4.5 调试模式状态转换
```
DEBUG_OFF (初始状态)
└─[DEBUG_MODE_OPEN]─> DEBUG_OPENING
└─[DEBUG_OPENED]─> DEBUG_ON
└─[DEBUG_MODE_CLOSE]─> DEBUG_CLOSING
└─[DEBUG_CLOSED]─> DEBUG_OFF
```
**自动转换规则Action**
- 开舱/关舱操作:自动开启调试模式 → 执行操作 → 2秒后自动关闭
- 无人机开机/关机:自动开启调试模式 → 执行操作 → 2秒后自动关闭
- 机巢重启:自动开启调试模式 → 执行操作 → 2秒后自动关闭
### 4.6 控制权状态转换
```
AUTHORITY_NONE (初始状态)
└─[GRAB_FLIGHT_AUTHORITY]─> FLIGHT_AUTHORITY
└─[GRAB_PAYLOAD_AUTHORITY]─> ALL_AUTHORITY
```
**自动转换规则Action**
- 无人机起飞准备时:自动获取飞行控制权和负载控制权
## 五、Guard守卫设计
### 5.1 机巢操作 Guard
| Guard名称 | 检查条件 | 失败原因 |
|-----------|----------|----------|
| `CanOpenCover` | 舱门状态不是已打开 | 舱门已经打开 |
| `CanCloseCover` | 舱门状态不是已关闭 | 舱门已经关闭 |
| `IsDebugMode` | 当前处于调试模式 | 未开启调试模式 |
| `IsNotDebugMode` | 当前不处于调试模式 | 处于调试模式中 |
| `IsAirportOnline` | 机巢在线 | 机巢离线 |
### 5.2 无人机操作 Guard
| Guard名称 | 检查条件 | 失败原因 |
|-----------|----------|----------|
| `IsDronePowerOn` | 无人机已开机 | 无人机未开机 |
| `IsDronePowerOff` | 无人机已关机 | 无人机未关机 |
| `IsNotFlying` | 无人机不在飞行中 | 无人机正在飞行 |
| `IsNotReturningHome` | 无人机不在返航中 | 无人机正在返航 |
| `IsNotLanding` | 无人机不在降落中 | 无人机正在降落 |
| `CanFlyToPoint` | 不在返航且不在降落 | 返航中或降落中 |
| `HasEnoughBattery` | 电量充足(不低于返航电量) | 电量不足 |
| `IsDRCActive` | DRC模式已激活 | DRC模式未激活 |
| `IsNotDRCActive` | DRC模式未激活 | DRC模式已激活 |
### 5.3 任务操作 Guard
| Guard名称 | 检查条件 | 失败原因 |
|-----------|----------|----------|
| `IsValidReturnHeight` | 返航高度≥30米 | 返航高度不足30米 |
| `IsTaskPrepared` | 任务已准备完成 | 任务未准备 |
| `IsTaskExecuting` | 任务执行中 | 任务未执行 |
| `IsTaskPaused` | 任务已暂停 | 任务未暂停 |
| `HasFlightAuthority` | 已获取飞行控制权 | 未获取飞行控制权 |
| `HasPayloadAuthority` | 已获取负载控制权 | 未获取负载控制权 |
## 六、Action动作设计
### 6.1 机巢操作 Action
| Action名称 | 执行内容 | 说明 |
|------------|----------|------|
| `OpenDebugModeAction` | 发送 `debug_mode_open` 指令 | 开启调试模式 |
| `CloseDebugModeAction` | 发送 `debug_mode_close` 指令 | 关闭调试模式 |
| `OpenCoverAction` | 发送 `cover_open` 指令 | 开舱操作 |
| `CloseCoverAction` | 发送 `cover_close` 指令 | 关舱操作 |
| `RebootAirportAction` | 发送 `device_reboot` 指令 | 重启机巢 |
| `AutoCloseDebugModeAction` | 等待2秒后关闭调试模式 | 自动关闭调试模式 |
### 6.2 无人机操作 Action
| Action名称 | 执行内容 | 说明 |
|------------|----------|------|
| `PowerOnDroneAction` | 发送 `drone_open` 指令 | 无人机开机 |
| `PowerOffDroneAction` | 发送 `drone_close` 指令 | 无人机关机 |
| `GrabFlightAuthorityAction` | 发送 `flight_authority_grab` 指令 | 获取飞行控制权 |
| `GrabPayloadAuthorityAction` | 发送 `payload_authority_grab` 指令 | 获取负载控制权 |
| `ClearLiveStreamCacheAction` | 清除直播相关缓存 | 清除缓存 |
### 6.3 飞行操作 Action
| Action名称 | 执行内容 | 说明 |
|------------|----------|------|
| `EnterDRCModeAction` | 发送 `drc_mode_enter` 指令 | 进入DRC模式 |
| `ExitDRCModeAction` | 发送 `drc_mode_exit` 指令 | 退出DRC模式 |
| `EmergencyStopAction` | 发送 `drone_emergency_stop` 属性设置 | 急停操作 |
| `ReturnHomeAction` | 发送 `return_home` 指令 | 返航操作 |
| `StartLiveStreamAction` | 发送 `live_start_push` 指令 | 开启直播推流 |
| `StopLiveStreamAction` | 发送 `live_stop_push` 指令 | 关闭直播推流 |
### 6.4 任务操作 Action
| Action名称 | 执行内容 | 说明 |
|------------|----------|------|
| `PrepareWaylineTaskAction` | 发送 `flighttask_prepare` 指令 | 准备航线任务 |
| `ExecuteWaylineTaskAction` | 等待1秒 → 开启机巢推流 → 发送 `flighttask_execute` 指令 | 执行航线任务 |
| `PauseTaskAction` | 发送 `flighttask_pause` 指令 | 暂停任务 |
| `RecoveryTaskAction` | 发送 `flighttask_recovery` 指令 | 恢复任务 |
| `FlyToPointAction` | 检查是否在指点飞行中 → 发送 `fly_to_point``fly_to_point_update` 指令 | 指点飞行 |
| `TakeoffToPointAction` | 发送 `takeoff_to_point` 指令 | 一键起飞 |
| `AdjustReturnHeightAction` | 检查返航高度,<100米调整为100米 | 调整返航高度 |
### 6.5 自动管理 Action
| Action名称 | 执行内容 | 说明 |
|------------|----------|------|
| `AutoEnterDRCModeAction` | 自动进入DRC模式 | 指点飞行停止后、一键起飞停止后 |
| `AutoExitDRCModeAction` | 自动退出DRC模式 | 指点飞行中、一键起飞中、返航中、降落中 |
| `AutoGrabAuthorityAction` | 自动获取控制权 | 无人机起飞准备时 |
| `UpdateFlightStatusAction` | 更新飞行状态 | 根据OSD数据更新状态急停状态下不更新 |
## 七、状态机监听器设计
### 7.1 OSD心跳监听器
监听 `thing/product/{airportSn}/osd` Topic处理
- **机巢状态更新**`device_online_status` → 触发 `AIRPORT_ONLINE`/`AIRPORT_OFFLINE`
- **舱门状态更新**`cover_state` → 触发 `COVER_OPENED`/`COVER_CLOSED`
- **无人机电源状态**`device_online_status` → 触发 `POWER_ON_COMPLETED`/`POWER_OFF_COMPLETED`
- **飞行模式**`drone_mode_code` → 更新飞行状态
- **返航状态**`mode_code = "9"` → 触发 `RETURN_HOME`
- **降落状态**`mode_code = "LAND"` → 触发 `START_LANDING`
### 7.2 Events事件监听器
监听 `thing/product/{airportSn}/events` Topic处理
- **任务进度**`flighttask_progress` → 触发任务状态事件
- **指点飞行进度**`fly_to_point_progress` → 触发指点飞行状态事件
- **一键起飞进度**`takeoff_to_point_progress` → 触发一键起飞状态事件
- **设备重启**`device_reboot` → 触发重启状态事件
- **文件上传**`file_upload_callback` → 处理拍照文件上传
### 7.3 Services回复监听器
监听 `thing/product/{airportSn}/services_reply` Topic处理
- **指令接受确认**`result=0` → 确认指令已接受
- **DRC模式状态**`drc_mode_enter`/`drc_mode_exit` 回复 → 触发DRC模式状态事件
- **控制权获取**`flight_authority_grab`/`payload_authority_grab` 回复 → 触发控制权状态事件
### 7.4 Set回复监听器
监听 `thing/product/{airportSn}/set_reply` Topic处理
- **属性设置确认**:急停、调色盘等属性设置的确认
## 八、业务规则实现
### 8.1 调试模式规则
**规则:** 开舱、关舱、无人机开机、无人机关机、机巢重启需要先开启调试模式
**实现:**
```
Action: OpenDebugModeAction (Guard: IsNotDebugMode)
→ 执行操作 (Guard: IsDebugMode)
→ AutoCloseDebugModeAction (等待2秒后关闭)
```
### 8.2 飞行状态限制规则
**规则:**
- 返航中或降落中不可执行指点飞行
- 一键起飞与航线飞行过程中允许插入指点飞行(实时更新目标)
- 航线/一键起飞的“非悬停”状态下均可急停急停后进入悬停Hover
**实现:**
```
Guard: CanFlyToPoint
- 检查状态不是 RETURNING_HOME
- 检查状态不是 LANDING
- 允许在 TAKEOFF_TO_POINT / WAYLINE_TASK 中触发 FLY_TO_POINT 或 UPDATE_FLY_TO_POINT
Guard: CanEmergencyStop
- 当前飞行状态非悬停即可触发急停
Action: EmergencyStopAction
- 发送急停指令,进入 EMERGENCY_STOP→HOVER
```
### 8.3 航线任务规则
**规则:** 返航高度必须≥30米<100米时自动调整为100米
**实现:**
```
Guard: IsValidReturnHeight (检查≥30米)
Action: AdjustReturnHeightAction (检查<100米时调整为100米)
```
### 8.4 DRC模式规则
**规则:** 急停、摇杆控制依赖 DRC 模式;指点飞行中、一键起飞中、返航中、降落中自动退出 DRC 模式
**实现:**
```
自动退出DRC模式
- 指点飞行中Action监听任务状态自动发送 EXIT_DRC_MODE
- 一键起飞中Action监听任务状态自动发送 EXIT_DRC_MODE
- 返航中Action监听返航状态自动发送 EXIT_DRC_MODE
- 降落中Action监听降落状态自动发送 EXIT_DRC_MODE
自动进入DRC模式
- 指点飞行停止后Action监听任务完成自动发送 ENTER_DRC_MODE
- 一键起飞停止后Action监听任务完成自动发送 ENTER_DRC_MODE
- 急停操作Action自动发送 ENTER_DRC_MODE急停前确保 DRC 已激活)
- 摇杆控制Action自动发送 ENTER_DRC_MODE
```
### 8.5 控制权规则
**规则:** 无人机起飞准备时自动获取飞行控制权和负载控制权
**实现:**
```
Action: AutoGrabAuthorityAction
- 监听任务准备完成事件
- 自动发送 GRAB_FLIGHT_AUTHORITY
- 自动发送 GRAB_PAYLOAD_AUTHORITY
```
### 8.6 急停规则
**规则:**
- 急停依赖 DRC 模式;飞行中(非悬停)都可急停,急停后进入悬停
- 航线飞行的“非指点、非悬停”阶段可暂停,暂停后进入悬停(与急停后的悬停一致,但语义不同)
- 急停状态下不更新飞行状态,保持急停/悬停
**实现:**
```
Guard: CanEmergencyStop
- 检查 DRC_ACTIVE
- 检查当前飞行状态非悬停即可触发
Action: EmergencyStopAction
- 发送 drone_emergency_stop → 状态切换至 EMERGENCY_STOP → HOVER
Guard: CanPauseWayline
- 当前在 WAYLINE_TASK 且非指点、非悬停
Action: PauseTaskAction
- 发送 flighttask_pause → 状态切换至 TASK_PAUSED → HOVER
Guard: UpdateFlightStatusGuard
- 检查当前状态不是 EMERGENCY_STOP
- 如果是急停/悬停,阻止飞行状态更新
```
## 九、状态机配置示例
### 9.1 状态定义示例
```java
public enum AirportState {
// 主状态
OFFLINE,
ONLINE, // 父状态
REBOOTING,
// ONLINE 的子状态
STANDBY, // 初始子状态
DEBUG_MODE,
OPERATING, // 父状态
// OPERATING 的子状态(舱门状态)
COVER_CLOSED,
COVER_OPENED,
COVER_HALF_OPEN,
COVER_ERROR,
COVER_OPENING,
COVER_CLOSING
}
public enum DroneState {
// 主状态
POWER_OFF, // 初始状态
POWER_ON,
FLYING, // 父状态
RETURNING_HOME,
LANDING,
LANDED,
// FLYING 的子状态
READY_TO_FLY, // 初始子状态
MANUAL_FLIGHT,
AUTO_FLIGHT, // 父状态
EMERGENCY_STOP,
// AUTO_FLIGHT 的子状态
TASK_PREPARING, // 初始子状态
TASK_EXECUTING, // 父状态
TASK_PAUSED,
TASK_COMPLETED,
TASK_FAILED,
// TASK_EXECUTING 的子状态
WAYLINE_TASK,
FLY_TO_POINT,
TAKEOFF_TO_POINT
}
```
### 9.2 转换配置示例
```java
// 机巢状态转换
.withExternal()
.source(AirportState.OFFLINE)
.target(AirportState.STANDBY)
.event(AirportEvent.AIRPORT_ONLINE)
.guard(new IsAirportOnlineGuard())
.and()
// 开舱操作(需要调试模式)
.withExternal()
.source(AirportState.STANDBY)
.target(AirportState.DEBUG_MODE)
.event(AirportEvent.DEBUG_MODE_OPEN)
.action(new OpenDebugModeAction())
.guard(new IsNotDebugModeGuard())
.and()
.withExternal()
.source(AirportState.DEBUG_MODE)
.target(AirportState.COVER_OPENING)
.event(AirportEvent.COVER_OPEN)
.action(new OpenCoverAction())
.guard(new CanOpenCoverGuard())
.and()
// 无人机开机(需要调试模式)
.withExternal()
.source(DroneState.POWER_OFF)
.target(DroneState.POWER_ON)
.event(DroneEvent.DRONE_POWER_ON)
.action(new PowerOnDroneAction())
.guard(new IsDebugModeGuard())
.and()
// 进入DRC模式
.withExternal()
.source(ModeState.DRC_INACTIVE)
.target(ModeState.DRC_ENTERING)
.event(DRCEvent.ENTER_DRC_MODE)
.action(new EnterDRCModeAction())
.and()
```
## 十、使用场景示例
### 10.1 开舱场景
```
1. 用户点击开舱
2. 检查当前舱门状态Guard: CanOpenCover
3. 检查是否在调试模式Guard: IsNotDebugMode
4. 开启调试模式Action: OpenDebugModeAction
5. 发送开舱指令Action: OpenCoverAction
6. 监听OSD状态等待舱门打开
7. 2秒后自动关闭调试模式Action: AutoCloseDebugModeAction
```
### 10.2 航线任务场景
```
1. 用户下发航线任务
2. 检查返航高度≥30米Guard: IsValidReturnHeight
3. 调整返航高度<100米为100米Action: AdjustReturnHeightAction
4. 准备任务Action: PrepareWaylineTaskAction
5. 等待准备完成监听services_reply
6. 等待1秒 → 开启机巢推流 → 执行任务Action: ExecuteWaylineTaskAction
7. 监听任务进度events: flighttask_progress
8. 任务完成/失败后更新状态
```
### 10.3 指点飞行场景
```
1. 用户点击指点飞行
2. 检查不在返航中且不在降落中Guard: CanFlyToPoint
3. 检查是否已在指点飞行中判断events中的status
4. 如果在指点飞行中发送更新指令fly_to_point_update
5. 如果不在指点飞行中发送新指令fly_to_point
6. 自动退出DRC模式Action: AutoExitDRCModeAction
7. 监听飞行进度events: fly_to_point_progress
8. 飞行停止后自动进入DRC模式Action: AutoEnterDRCModeAction
```
### 10.4 急停场景
```
1. 用户点击急停
2. 检查无人机已开机Guard: IsDronePowerOn
3. 检查电量充足Guard: HasEnoughBattery
4. 检查是否在DRC模式Guard: IsNotDRCActive
5. 如果不在DRC模式先进入DRC模式Action: EnterDRCModeAction
6. 等待DRC模式激活监听services_reply
7. 发送急停指令Action: EmergencyStopAction
8. 进入急停状态阻止状态更新Guard: UpdateFlightStatusGuard
```
## 十一、状态机管理
### 11.1 多状态机实例管理
每个机巢airportSn对应一个状态机实例
```java
StateMachineManager manager = new StateMachineManager();
// 获取或创建状态机
StateMachine stateMachine = manager.getOrCreateStateMachine(airportSn);
// 发送事件
stateMachine.sendEvent(AirportEvent.COVER_OPEN);
// 查询状态
AirportState currentState = manager.getCurrentState(airportSn);
```
### 11.2 状态持久化
状态机状态可以持久化到数据库或Redis
- **状态快照**:定期保存状态机状态
- **事件日志**:记录所有状态转换事件
- **恢复机制**:系统重启后恢复状态机状态
## 十二、总结
### 12.1 设计特点
1. **分层设计**:主状态、子状态、子子状态,清晰表达复杂状态关系
2. **业务规则**通过Guard和Action实现业务限制条件
3. **自动管理**DRC模式、控制权等自动管理减少人工干预
4. **安全控制**:通过状态机确保操作的安全执行顺序
5. **事件驱动**基于MQTT事件驱动状态转换
### 12.2 扩展性
- **新增功能**:通过添加新状态、事件、转换即可扩展
- **业务规则变更**修改Guard和Action即可适应规则变化
- **多设备支持**:每个机巢独立的状态机实例
### 12.3 注意事项
1. **状态同步**确保MQTT事件与状态机状态同步
2. **异常处理**:处理网络异常、设备异常等情况
3. **超时处理**:操作超时后的状态恢复机制
4. **并发控制**:同一状态机的并发操作控制

View File

@ -0,0 +1,209 @@
package com.tuoheng.status.statemachine;
import com.tuoheng.status.statemachine.events.Event;
import com.tuoheng.status.statemachine.manager.StateMachineManager;
import com.tuoheng.status.statemachine.service.StateMachineManagerService;
import com.tuoheng.status.statemachine.status.Status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
/**
* 状态机管理器测试类
*/
@ContextConfiguration(classes = {
com.tuoheng.status.statemachine.config.TaskStateMachineConfig.class,
com.tuoheng.status.statemachine.manager.StateMachineManager.class,
StateMachineManagerService.class
})
public class StateMachineManagerTest extends AbstractTestNGSpringContextTests {
@Autowired
private StateMachineManager stateMachineManager;
@Autowired
private StateMachineManagerService stateMachineManagerService;
@Test
public void testCreateAndGetStateMachine() {
System.out.println("\n\n========================================");
System.out.println("测试1: 创建和获取状态机");
System.out.println("========================================\n");
String machineId = "test-machine-001";
// 创建状态机
assertFalse(stateMachineManager.exists(machineId), "状态机应该不存在");
Status status = stateMachineManager.getCurrentStatus(machineId);
assertNull(status, "不存在的状态机应该返回null");
// 创建状态机
stateMachineManager.getOrCreateStateMachine(machineId);
assertTrue(stateMachineManager.exists(machineId), "状态机应该存在");
// 获取状态
status = stateMachineManager.getCurrentStatus(machineId);
assertNotNull(status, "状态机状态不应该为null");
assertEquals(status, Status.IDLE, "初始状态应该是IDLE");
System.out.println("状态机ID: " + machineId);
System.out.println("当前状态: " + status);
}
@Test
public void testGetStateMachineInfo() {
System.out.println("\n\n========================================");
System.out.println("测试2: 获取状态机详细信息");
System.out.println("========================================\n");
String machineId = "test-machine-002";
stateMachineManager.getOrCreateStateMachine(machineId);
StateMachineManager.StateMachineInfo info = stateMachineManager.getStateMachineInfo(machineId);
assertNotNull(info, "状态机信息不应该为null");
assertEquals(info.getMachineId(), machineId);
assertEquals(info.getCurrentStatus(), Status.IDLE);
assertTrue(info.isRunning(), "状态机应该正在运行");
System.out.println("状态机信息: " + info);
}
@Test
public void testSendEventAndCheckStatus() {
System.out.println("\n\n========================================");
System.out.println("测试3: 发送事件并检查状态变化");
System.out.println("========================================\n");
String machineId = "test-machine-003";
// 创建状态机并设置taskId
stateMachineManager.getOrCreateStateMachine(machineId);
stateMachineManager.getStateMachine(machineId)
.getExtendedState().getVariables().put("taskId", "task-001");
// 初始状态应该是IDLE
Status status = stateMachineManager.getCurrentStatus(machineId);
assertEquals(status, Status.IDLE);
// 发送START事件
boolean accepted = stateMachineManagerService.sendEvent(machineId, Event.START);
assertTrue(accepted, "START事件应该被接受");
// 等待状态转换
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 检查状态应该变为PREPARING
status = stateMachineManager.getCurrentStatus(machineId);
assertEquals(status, Status.PREPARING, "状态应该变为PREPARING");
System.out.println("状态机ID: " + machineId);
System.out.println("发送事件: START");
System.out.println("当前状态: " + status);
}
@Test
public void testMultipleStateMachines() {
System.out.println("\n\n========================================");
System.out.println("测试4: 管理多个状态机");
System.out.println("========================================\n");
String machineId1 = "test-machine-004-1";
String machineId2 = "test-machine-004-2";
String machineId3 = "test-machine-004-3";
// 创建多个状态机
stateMachineManager.getOrCreateStateMachine(machineId1);
stateMachineManager.getOrCreateStateMachine(machineId2);
stateMachineManager.getOrCreateStateMachine(machineId3);
// 验证所有状态机都存在
assertTrue(stateMachineManager.exists(machineId1));
assertTrue(stateMachineManager.exists(machineId2));
assertTrue(stateMachineManager.exists(machineId3));
// 获取所有状态机ID
java.util.Set<String> allIds = stateMachineManager.getAllMachineIds();
assertTrue(allIds.contains(machineId1));
assertTrue(allIds.contains(machineId2));
assertTrue(allIds.contains(machineId3));
assertEquals(stateMachineManager.getStateMachineCount(), 3);
System.out.println("管理的状态机数量: " + stateMachineManager.getStateMachineCount());
System.out.println("所有状态机ID: " + allIds);
// 为不同的状态机设置不同的状态
stateMachineManager.getStateMachine(machineId1)
.getExtendedState().getVariables().put("taskId", "task-001");
stateMachineManagerService.sendEvent(machineId1, Event.START);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Status status1 = stateMachineManager.getCurrentStatus(machineId1);
Status status2 = stateMachineManager.getCurrentStatus(machineId2);
Status status3 = stateMachineManager.getCurrentStatus(machineId3);
System.out.println("状态机1 (" + machineId1 + ") 状态: " + status1);
System.out.println("状态机2 (" + machineId2 + ") 状态: " + status2);
System.out.println("状态机3 (" + machineId3 + ") 状态: " + status3);
assertNotEquals(status1, status2, "不同状态机应该有不同状态");
}
@Test
public void testRemoveStateMachine() {
System.out.println("\n\n========================================");
System.out.println("测试5: 移除状态机");
System.out.println("========================================\n");
String machineId = "test-machine-005";
// 创建状态机
stateMachineManager.getOrCreateStateMachine(machineId);
assertTrue(stateMachineManager.exists(machineId));
// 移除状态机
boolean removed = stateMachineManager.removeStateMachine(machineId);
assertTrue(removed, "应该成功移除状态机");
assertFalse(stateMachineManager.exists(machineId), "状态机应该不存在了");
System.out.println("状态机已移除: " + machineId);
}
@Test
public void testStateMachineService() {
System.out.println("\n\n========================================");
System.out.println("测试6: 使用StateMachineManagerService");
System.out.println("========================================\n");
String machineId = "test-machine-006";
String taskId = "task-006";
// 使用服务类启动任务
boolean started = stateMachineManagerService.startTask(machineId, taskId);
assertTrue(started, "任务应该成功启动");
// 检查状态
Status status = stateMachineManagerService.getCurrentStatus(machineId);
assertEquals(status, Status.PREPARING, "状态应该是PREPARING");
// 获取详细信息
StateMachineManager.StateMachineInfo info = stateMachineManagerService.getStateMachineInfo(machineId);
assertNotNull(info);
assertEquals(info.getCurrentStatus(), Status.PREPARING);
// 打印信息
stateMachineManagerService.printStateMachineInfo(machineId);
}
}

View File

@ -0,0 +1,65 @@
package com.tuoheng.status.statemachine;
import com.tuoheng.status.statemachine.service.TaskStateMachineService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;
/**
* 状态机测试类
* 演示 spring-statemachine-core 的各种功能
*/
@ContextConfiguration(classes = {
com.tuoheng.status.statemachine.config.TaskStateMachineConfig.class,
TaskStateMachineService.class
})
public class TaskStateMachineTest extends AbstractTestNGSpringContextTests {
@Autowired
private TaskStateMachineService taskStateMachineService;
/**
* 测试正常流程IDLE -> PREPARING -> EXECUTING -> VALIDATING -> COMPLETED
*/
@Test
public void testNormalFlow() {
System.out.println("\n\n========================================");
System.out.println("测试1: 正常流程");
System.out.println("========================================\n");
taskStateMachineService.startTask("task-001");
}
/**
* 测试失败和重试流程
*/
@Test
public void testFailureAndRetry() {
System.out.println("\n\n========================================");
System.out.println("测试2: 失败和重试流程");
System.out.println("========================================\n");
taskStateMachineService.demonstrateFailureAndRetry("task-002");
}
/**
* 测试 Guard 条件不满足的情况
*/
@Test
public void testGuardFailure() {
System.out.println("\n\n========================================");
System.out.println("测试3: Guard 条件不满足");
System.out.println("========================================\n");
taskStateMachineService.demonstrateGuardFailure("task-003");
}
/**
* 测试重置流程
*/
@Test
public void testReset() {
System.out.println("\n\n========================================");
System.out.println("测试4: 重置流程");
System.out.println("========================================\n");
taskStateMachineService.demonstrateReset("task-004");
}
}