添加状态机
This commit is contained in:
parent
6d55dbfea5
commit
6501986e3d
30
pom.xml
30
pom.xml
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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: 实现机巢离线的具体逻辑
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 指令
|
||||
}
|
||||
}
|
||||
|
|
@ -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: 舱门关闭完成后的处理逻辑(例如:记录日志、自动关闭调试模式等)
|
||||
}
|
||||
}
|
||||
|
|
@ -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: 舱门打开完成后的处理逻辑(例如:记录日志、通知相关模块等)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 指令
|
||||
}
|
||||
}
|
||||
|
|
@ -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 指令
|
||||
}
|
||||
}
|
||||
|
|
@ -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 指令
|
||||
}
|
||||
}
|
||||
|
|
@ -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 指令
|
||||
}
|
||||
}
|
||||
|
|
@ -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: 机巢重启完成后的处理逻辑(例如:重新初始化、恢复状态等)
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tuoheng.status.airport.events;
|
||||
|
||||
/**
|
||||
* 舱门事件枚举
|
||||
*/
|
||||
public enum CoverEvent {
|
||||
/**
|
||||
* 开舱指令
|
||||
*/
|
||||
OPEN,
|
||||
|
||||
/**
|
||||
* 开舱完成
|
||||
*/
|
||||
OPENED,
|
||||
|
||||
/**
|
||||
* 关舱指令
|
||||
*/
|
||||
CLOSE,
|
||||
|
||||
/**
|
||||
* 关舱完成
|
||||
*/
|
||||
CLOSED,
|
||||
|
||||
/**
|
||||
* 舱门异常
|
||||
*/
|
||||
ERROR,
|
||||
|
||||
/**
|
||||
* 重置舱门状态
|
||||
*/
|
||||
RESET
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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 : "未知");
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tuoheng.status.airport.status;
|
||||
|
||||
/**
|
||||
* 机巢状态枚举(简化版 - 舱门状态已分离)
|
||||
*/
|
||||
public enum AirportState {
|
||||
/**
|
||||
* 离线
|
||||
*/
|
||||
OFFLINE,
|
||||
|
||||
/**
|
||||
* 在线(父状态)
|
||||
*/
|
||||
ONLINE,
|
||||
|
||||
/**
|
||||
* 待机
|
||||
*/
|
||||
STANDBY,
|
||||
|
||||
/**
|
||||
* 调试模式
|
||||
*/
|
||||
DEBUG_MODE,
|
||||
|
||||
/**
|
||||
* 重启中
|
||||
*/
|
||||
REBOOTING
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tuoheng.status.airport.status;
|
||||
|
||||
/**
|
||||
* 舱门状态枚举
|
||||
*/
|
||||
public enum CoverState {
|
||||
/**
|
||||
* 舱门已关闭
|
||||
*/
|
||||
CLOSED,
|
||||
|
||||
/**
|
||||
* 舱门打开中
|
||||
*/
|
||||
OPENING,
|
||||
|
||||
/**
|
||||
* 舱门已打开
|
||||
*/
|
||||
OPENED,
|
||||
|
||||
/**
|
||||
* 舱门关闭中
|
||||
*/
|
||||
CLOSING,
|
||||
|
||||
/**
|
||||
* 舱门半开
|
||||
*/
|
||||
HALF_OPEN,
|
||||
|
||||
/**
|
||||
* 舱门状态异常
|
||||
*/
|
||||
ERROR
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package com.tuoheng.status.airport.status;
|
||||
|
||||
public enum DroneState {
|
||||
}
|
||||
|
|
@ -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 转换到 PREPARING(PROCESSING 的子状态)
|
||||
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`
|
||||
|
|
@ -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("状态机已重置");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/**
|
||||
* 状态机配置类
|
||||
* 配置状态、子状态、转换、Action、Guard 等
|
||||
*/
|
||||
@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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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("说明: 不设置 taskId,Guard 会阻止状态转换");
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tuoheng.status.statemachine.events;
|
||||
|
||||
/**
|
||||
* 事件枚举
|
||||
*/
|
||||
public enum Event {
|
||||
START, // 开始处理
|
||||
PREPARE, // 准备就绪
|
||||
EXECUTE, // 执行
|
||||
VALIDATE, // 验证
|
||||
COMPLETE, // 完成
|
||||
FAIL, // 失败
|
||||
RETRY, // 重试
|
||||
RESET // 重置
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
// 可以在这里记录状态上下文信息
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
// 存储状态机实例的Map,key为状态机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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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("================================");
|
||||
}
|
||||
}
|
||||
|
|
@ -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 事件(没有 taskId,Guard 应该失败)---");
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 的子状态)
|
||||
}
|
||||
|
|
@ -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. **并发控制**:同一状态机的并发操作控制
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue