添加DRC控制

This commit is contained in:
孙小云 2025-12-16 13:56:36 +08:00
parent a53c531b47
commit a377485b23
22 changed files with 912 additions and 2 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,144 @@
package com.tuoheng.status.machine.config;
import com.tuoheng.status.machine.events.DrcEvent;
import com.tuoheng.status.machine.platform.factory.PlatformStrategyFactory;
import com.tuoheng.status.machine.platform.strategy.DrcPlatformStrategy;
import com.tuoheng.status.machine.status.DrcState;
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;
/**
* DRC飞行控制模式状态机配置多平台支持版本
* 通过PlatformStrategyFactory动态获取平台特定的GuardAction和Listener
*/
@Configuration
public class DrcMachineConfig {
@Autowired
private PlatformStrategyFactory platformStrategyFactory;
@Bean(name = "drcStateMachineFactory")
public StateMachineFactory<DrcState, DrcEvent> drcStateMachineFactory() throws Exception {
return new StateMachineFactory<DrcState, DrcEvent>() {
@Override
public StateMachine<DrcState, DrcEvent> getStateMachine() {
return null;
}
@Override
public StateMachine<DrcState, DrcEvent> getStateMachine(String machineId) {
try {
// 根据机巢SN获取平台策略
DrcPlatformStrategy strategy = platformStrategyFactory.getDrcStrategy(machineId);
StateMachineBuilder.Builder<DrcState, DrcEvent> builder = StateMachineBuilder.builder();
configureDrcStateMachine(builder, strategy);
configureDrcStates(builder);
configureDrcTransitions(builder, strategy);
StateMachine<DrcState, DrcEvent> stateMachine = builder.build();
stateMachine.getExtendedState().getVariables().put("machineId", machineId);
return stateMachine;
} catch (Exception e) {
throw new RuntimeException("Failed to create DRC state machine for: " + machineId, e);
}
}
@Override
public StateMachine<DrcState, DrcEvent> getStateMachine(UUID uuid) {
return null;
}
};
}
private void configureDrcStateMachine(
StateMachineBuilder.Builder<DrcState, DrcEvent> builder,
DrcPlatformStrategy strategy) throws Exception {
builder.configureConfiguration()
.withConfiguration()
.autoStartup(true)
.listener(strategy.getListener());
}
private void configureDrcStates(StateMachineBuilder.Builder<DrcState, DrcEvent> builder) throws Exception {
builder.configureStates()
.withStates()
.initial(DrcState.UNKNOWN)
.states(EnumSet.allOf(DrcState.class));
}
private void configureDrcTransitions(
StateMachineBuilder.Builder<DrcState, DrcEvent> builder,
DrcPlatformStrategy strategy) throws Exception {
builder.configureTransitions()
// ========== UNKNOWN 到所有状态的转换服务器重启后状态同步 ==========
// UNKNOWN -> EXITED
.withExternal()
.source(DrcState.UNKNOWN)
.target(DrcState.EXITED)
.event(DrcEvent.EXITED)
.and()
// UNKNOWN -> ENTERING
.withExternal()
.source(DrcState.UNKNOWN)
.target(DrcState.ENTERING)
.event(DrcEvent.ENTER)
.and()
// UNKNOWN -> ENTERED
.withExternal()
.source(DrcState.UNKNOWN)
.target(DrcState.ENTERED)
.event(DrcEvent.ENTERED)
.and()
// UNKNOWN -> EXITING
.withExternal()
.source(DrcState.UNKNOWN)
.target(DrcState.EXITING)
.event(DrcEvent.EXIT)
.and()
// ========== 正常状态转换 Guard Action ==========
// EXITED -> ENTERING
.withExternal()
.source(DrcState.EXITED)
.target(DrcState.ENTERING)
.event(DrcEvent.ENTER)
.action(strategy.getEnterAction())
.guard(strategy.getCanEnterGuard())
.and()
// ENTERING -> ENTERED
.withExternal()
.source(DrcState.ENTERING)
.target(DrcState.ENTERED)
.event(DrcEvent.ENTERED)
.action(strategy.getEnteredAction())
.and()
// ENTERED -> EXITING
.withExternal()
.source(DrcState.ENTERED)
.target(DrcState.EXITING)
.event(DrcEvent.EXIT)
.action(strategy.getExitAction())
.guard(strategy.getCanExitGuard())
.and()
// EXITING -> EXITED
.withExternal()
.source(DrcState.EXITING)
.target(DrcState.EXITED)
.event(DrcEvent.EXITED)
.action(strategy.getExitedAction());
}
}

View File

@ -0,0 +1,31 @@
package com.tuoheng.status.machine.events;
/**
* DRC飞行控制模式事件枚举
* 定义DRC状态机中的所有事件
*/
public enum DrcEvent {
/**
* 进入DRC模式指令
* 触发源: 用户指令
*/
ENTER,
/**
* 进入DRC模式完成
* 触发源: Events事件
*/
ENTERED,
/**
* 退出DRC模式指令
* 触发源: 用户指令/自动
*/
EXIT,
/**
* 退出DRC模式完成
* 触发源: Events事件
*/
EXITED
}

View File

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

View File

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

View File

@ -0,0 +1,90 @@
package com.tuoheng.status.machine.listener;
import com.tuoheng.status.machine.events.DrcEvent;
import com.tuoheng.status.machine.platform.strategy.PlatformListener;
import com.tuoheng.status.machine.status.DrcState;
import org.springframework.messaging.Message;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.transition.Transition;
/**
* 默认DRC状态监听器
* 提供基础的状态变化监听功能各平台可以继承并定制
*/
public abstract class DefaultDrcListener implements PlatformListener<DrcState, DrcEvent> {
@Override
public void stateChanged(State<DrcState, DrcEvent> from, State<DrcState, DrcEvent> to) {
if (from != null && to != null) {
System.out.println(String.format("[%s] 状态变化: %s -> %s",
getName(), from.getId(), to.getId()));
}
}
@Override
public void stateEntered(State<DrcState, DrcEvent> state) {
System.out.println(String.format("[%s] 进入状态: %s", getName(), state.getId()));
}
@Override
public void stateExited(State<DrcState, DrcEvent> state) {
System.out.println(String.format("[%s] 退出状态: %s", getName(), state.getId()));
}
@Override
public void eventNotAccepted(Message<DrcEvent> event) {
System.out.println(String.format("[%s] 事件未被接受: %s", getName(), event.getPayload()));
}
@Override
public void transition(Transition<DrcState, DrcEvent> transition) {
// 默认不处理
}
@Override
public void transitionStarted(Transition<DrcState, DrcEvent> transition) {
if (transition.getSource() != null && transition.getTarget() != null) {
System.out.println(String.format("[%s] 转换开始: %s -> %s",
getName(), transition.getSource().getId(), transition.getTarget().getId()));
}
}
@Override
public void transitionEnded(Transition<DrcState, DrcEvent> transition) {
if (transition.getSource() != null && transition.getTarget() != null) {
System.out.println(String.format("[%s] 转换结束: %s -> %s",
getName(), transition.getSource().getId(), transition.getTarget().getId()));
}
}
@Override
public void stateMachineStarted(StateMachine<DrcState, DrcEvent> stateMachine) {
String machineId = (String) stateMachine.getExtendedState().getVariables().get("machineId");
System.out.println(String.format("[%s] 状态机启动: %s", getName(), machineId));
}
@Override
public void stateMachineStopped(StateMachine<DrcState, DrcEvent> stateMachine) {
String machineId = (String) stateMachine.getExtendedState().getVariables().get("machineId");
System.out.println(String.format("[%s] 状态机停止: %s", getName(), machineId));
}
@Override
public void stateMachineError(StateMachine<DrcState, DrcEvent> stateMachine, Exception exception) {
String machineId = (String) stateMachine.getExtendedState().getVariables().get("machineId");
System.err.println(String.format("[%s] 状态机错误: %s, 异常: %s",
getName(), machineId, exception.getMessage()));
}
@Override
public void extendedStateChanged(Object key, Object value) {
System.out.println(String.format("[%s] 扩展状态变化: %s = %s", getName(), key, value));
}
@Override
public void stateContext(StateContext<DrcState, DrcEvent> stateContext) {
// 默认不处理
}
}

View File

@ -6,6 +6,7 @@ import com.tuoheng.status.machine.repository.AirportPlatformRepository;
import com.tuoheng.status.machine.platform.strategy.AirportPlatformStrategy;
import com.tuoheng.status.machine.platform.strategy.CoverPlatformStrategy;
import com.tuoheng.status.machine.platform.strategy.DronePlatformStrategy;
import com.tuoheng.status.machine.platform.strategy.DrcPlatformStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -44,6 +45,13 @@ public class PlatformStrategyFactory {
*/
private final Map<PlatformType, DronePlatformStrategy> droneStrategyMap = new ConcurrentHashMap<>();
/**
* 存储所有DRC平台策略实现
* Key: PlatformType
* Value: DrcPlatformStrategy实现
*/
private final Map<PlatformType, DrcPlatformStrategy> drcStrategyMap = new ConcurrentHashMap<>();
/**
* 存储各平台对应的系统管理器实现
* Key: PlatformType
@ -53,13 +61,14 @@ public class PlatformStrategyFactory {
/**
* 注册所有平台策略
* Spring会自动注入所有实现了AirportPlatformStrategyCoverPlatformStrategy和DronePlatformStrategy的Bean
* Spring会自动注入所有实现了AirportPlatformStrategyCoverPlatformStrategyDronePlatformStrategy和DrcPlatformStrategy的Bean
*/
@Autowired
public void registerStrategies(
List<AirportPlatformStrategy> airportStrategies,
List<CoverPlatformStrategy> coverStrategies,
List<DronePlatformStrategy> droneStrategies,
List<DrcPlatformStrategy> drcStrategies,
List<AirportSystemManager> systemManagers) {
// 注册机巢策略
@ -83,6 +92,13 @@ public class PlatformStrategyFactory {
strategy.getPlatformType().getName(), strategy.getClass().getSimpleName()));
}
// 注册DRC策略
for (DrcPlatformStrategy strategy : drcStrategies) {
drcStrategyMap.put(strategy.getPlatformType(), strategy);
System.out.println(String.format("注册DRC平台策略: %s -> %s",
strategy.getPlatformType().getName(), strategy.getClass().getSimpleName()));
}
// 注册系统管理器
for (AirportSystemManager manager : systemManagers) {
managerMap.put(manager.getPlatformType(), manager);
@ -223,6 +239,42 @@ public class PlatformStrategyFactory {
return droneStrategyMap.get(platformType);
}
/**
* 根据机巢SN获取DRC平台策略
*
* @param airportSn 机巢序列号
* @return DRC平台策略
* @throws IllegalArgumentException 如果机巢未注册或平台策略不存在
*/
public DrcPlatformStrategy getDrcStrategy(String airportSn) {
// 从数据库查询平台类型
PlatformType platformType = airportPlatformRepository.getPlatformType(airportSn);
if (platformType == null) {
throw new IllegalArgumentException(
String.format("机巢未注册或平台类型未配置: %s", airportSn));
}
DrcPlatformStrategy strategy = drcStrategyMap.get(platformType);
if (strategy == null) {
throw new IllegalArgumentException(
String.format("未找到DRC平台策略: %s (机巢: %s)", platformType.getName(), airportSn));
}
return strategy;
}
/**
* 根据平台类型获取DRC平台策略
*
* @param platformType 平台类型
* @return DRC平台策略
*/
public DrcPlatformStrategy getDrcStrategyByType(PlatformType platformType) {
return drcStrategyMap.get(platformType);
}
/**
* 获取机巢的平台类型
*

View File

@ -0,0 +1,82 @@
package com.tuoheng.status.machine.platform.impl.dji;
import com.tuoheng.status.machine.events.DrcEvent;
import com.tuoheng.status.machine.platform.PlatformType;
import com.tuoheng.status.machine.platform.impl.dji.action.drc.*;
import com.tuoheng.status.machine.platform.impl.dji.guard.drc.*;
import com.tuoheng.status.machine.platform.impl.dji.listener.DjiDrcListener;
import com.tuoheng.status.machine.platform.strategy.DrcPlatformStrategy;
import com.tuoheng.status.machine.platform.strategy.PlatformAction;
import com.tuoheng.status.machine.platform.strategy.PlatformGuard;
import com.tuoheng.status.machine.platform.strategy.PlatformListener;
import com.tuoheng.status.machine.status.DrcState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* DJI平台DRC策略实现
*/
@Component
public class DjiDrcPlatformStrategy implements DrcPlatformStrategy {
@Autowired
private DjiCanEnterGuard canEnterGuard;
@Autowired
private DjiCanExitGuard canExitGuard;
@Autowired
private DjiEnterAction enterAction;
@Autowired
private DjiEnteredAction enteredAction;
@Autowired
private DjiExitAction exitAction;
@Autowired
private DjiExitedAction exitedAction;
@Autowired
private DjiDrcListener djiDrcListener;
@Override
public PlatformType getPlatformType() {
return PlatformType.DJI;
}
@Override
public PlatformGuard<DrcState, DrcEvent> getCanEnterGuard() {
return canEnterGuard;
}
@Override
public PlatformGuard<DrcState, DrcEvent> getCanExitGuard() {
return canExitGuard;
}
@Override
public PlatformAction<DrcState, DrcEvent> getEnterAction() {
return enterAction;
}
@Override
public PlatformAction<DrcState, DrcEvent> getEnteredAction() {
return enteredAction;
}
@Override
public PlatformAction<DrcState, DrcEvent> getExitAction() {
return exitAction;
}
@Override
public PlatformAction<DrcState, DrcEvent> getExitedAction() {
return exitedAction;
}
@Override
public PlatformListener<DrcState, DrcEvent> getListener() {
return djiDrcListener;
}
}

View File

@ -0,0 +1,22 @@
package com.tuoheng.status.machine.platform.impl.dji.action.drc;
import com.tuoheng.status.machine.action.drc.EnterAction;
import com.tuoheng.status.machine.events.DrcEvent;
import com.tuoheng.status.machine.status.DrcState;
import org.springframework.statemachine.StateContext;
import org.springframework.stereotype.Component;
@Component
public class DjiEnterAction extends EnterAction {
@Override
public String getName() {
return "DjiEnterAction";
}
@Override
public void execute(StateContext<DrcState, DrcEvent> context) {
String machineId = (String) context.getExtendedState().getVariables().get("machineId");
System.out.println(String.format("[DJI] 进入DRC模式: %s", machineId));
}
}

View File

@ -0,0 +1,22 @@
package com.tuoheng.status.machine.platform.impl.dji.action.drc;
import com.tuoheng.status.machine.action.drc.EnteredAction;
import com.tuoheng.status.machine.events.DrcEvent;
import com.tuoheng.status.machine.status.DrcState;
import org.springframework.statemachine.StateContext;
import org.springframework.stereotype.Component;
@Component
public class DjiEnteredAction extends EnteredAction {
@Override
public String getName() {
return "DjiEnteredAction";
}
@Override
public void execute(StateContext<DrcState, DrcEvent> context) {
String machineId = (String) context.getExtendedState().getVariables().get("machineId");
System.out.println(String.format("[DJI] 已进入DRC模式: %s", machineId));
}
}

View File

@ -0,0 +1,22 @@
package com.tuoheng.status.machine.platform.impl.dji.action.drc;
import com.tuoheng.status.machine.action.drc.ExitAction;
import com.tuoheng.status.machine.events.DrcEvent;
import com.tuoheng.status.machine.status.DrcState;
import org.springframework.statemachine.StateContext;
import org.springframework.stereotype.Component;
@Component
public class DjiExitAction extends ExitAction {
@Override
public String getName() {
return "DjiExitAction";
}
@Override
public void execute(StateContext<DrcState, DrcEvent> context) {
String machineId = (String) context.getExtendedState().getVariables().get("machineId");
System.out.println(String.format("[DJI] 退出DRC模式: %s", machineId));
}
}

View File

@ -0,0 +1,22 @@
package com.tuoheng.status.machine.platform.impl.dji.action.drc;
import com.tuoheng.status.machine.action.drc.ExitedAction;
import com.tuoheng.status.machine.events.DrcEvent;
import com.tuoheng.status.machine.status.DrcState;
import org.springframework.statemachine.StateContext;
import org.springframework.stereotype.Component;
@Component
public class DjiExitedAction extends ExitedAction {
@Override
public String getName() {
return "DjiExitedAction";
}
@Override
public void execute(StateContext<DrcState, DrcEvent> context) {
String machineId = (String) context.getExtendedState().getVariables().get("machineId");
System.out.println(String.format("[DJI] 已退出DRC模式: %s", machineId));
}
}

View File

@ -0,0 +1,25 @@
package com.tuoheng.status.machine.platform.impl.dji.guard.drc;
import com.tuoheng.status.machine.events.DrcEvent;
import com.tuoheng.status.machine.guard.drc.CanEnterGuard;
import com.tuoheng.status.machine.status.DrcState;
import org.springframework.statemachine.StateContext;
import org.springframework.stereotype.Component;
/**
* DJI平台检查是否可以进入DRC模式
*/
@Component
public class DjiCanEnterGuard extends CanEnterGuard {
@Override
public String getName() {
return "DjiCanEnterGuard";
}
@Override
public boolean evaluate(StateContext<DrcState, DrcEvent> context) {
// DJI平台特定的进入DRC模式检查逻辑
return true;
}
}

View File

@ -0,0 +1,25 @@
package com.tuoheng.status.machine.platform.impl.dji.guard.drc;
import com.tuoheng.status.machine.events.DrcEvent;
import com.tuoheng.status.machine.guard.drc.CanExitGuard;
import com.tuoheng.status.machine.status.DrcState;
import org.springframework.statemachine.StateContext;
import org.springframework.stereotype.Component;
/**
* DJI平台检查是否可以退出DRC模式
*/
@Component
public class DjiCanExitGuard extends CanExitGuard {
@Override
public String getName() {
return "DjiCanExitGuard";
}
@Override
public boolean evaluate(StateContext<DrcState, DrcEvent> context) {
// DJI平台特定的退出DRC模式检查逻辑
return true;
}
}

View File

@ -0,0 +1,18 @@
package com.tuoheng.status.machine.platform.impl.dji.listener;
import com.tuoheng.status.machine.events.DrcEvent;
import com.tuoheng.status.machine.listener.DefaultDrcListener;
import com.tuoheng.status.machine.status.DrcState;
import org.springframework.stereotype.Component;
/**
* DJI平台DRC状态监听器
*/
@Component
public class DjiDrcListener extends DefaultDrcListener {
@Override
public String getName() {
return "DJI-DRC";
}
}

View File

@ -0,0 +1,59 @@
package com.tuoheng.status.machine.platform.strategy;
import com.tuoheng.status.machine.events.DrcEvent;
import com.tuoheng.status.machine.platform.PlatformType;
import com.tuoheng.status.machine.status.DrcState;
/**
* DRC飞行控制模式平台策略接口
* 定义各平台需要实现的GuardAction和Listener
*/
public interface DrcPlatformStrategy {
/**
* 获取平台类型
*/
PlatformType getPlatformType();
// ==================== Guards ====================
/**
* 是否可以进入DRC模式
*/
PlatformGuard<DrcState, DrcEvent> getCanEnterGuard();
/**
* 是否可以退出DRC模式
*/
PlatformGuard<DrcState, DrcEvent> getCanExitGuard();
// ==================== Actions ====================
/**
* 进入DRC模式动作
*/
PlatformAction<DrcState, DrcEvent> getEnterAction();
/**
* 进入DRC模式完成动作
*/
PlatformAction<DrcState, DrcEvent> getEnteredAction();
/**
* 退出DRC模式动作
*/
PlatformAction<DrcState, DrcEvent> getExitAction();
/**
* 退出DRC模式完成动作
*/
PlatformAction<DrcState, DrcEvent> getExitedAction();
// ==================== Listener ====================
/**
* 获取平台Listener
* 每个平台有一个Listener实例所有该平台的DRC共享
*/
PlatformListener<DrcState, DrcEvent> getListener();
}

View File

@ -3,6 +3,7 @@ package com.tuoheng.status.machine.redis;
import com.tuoheng.status.machine.status.AirportState;
import com.tuoheng.status.machine.status.CoverState;
import com.tuoheng.status.machine.status.DroneState;
import com.tuoheng.status.machine.status.DrcState;
import org.springframework.stereotype.Component;
import java.util.Map;
@ -11,7 +12,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 使用 Redis 记录和恢复机巢/舱门/无人机状态的存储组件
* 使用 Redis 记录和恢复机巢/舱门/无人机/DRC状态的存储组件
*
* 当前实现采用内存 Map 占位便于无 Redis 环境下直接运行
* 如果接入真正的 Redis只需将存取逻辑替换为 RedisTemplate 等实现
@ -22,6 +23,7 @@ public class RedisStateStore {
private final Map<String, AirportState> airportStateMap = new ConcurrentHashMap<>();
private final Map<String, CoverState> coverStateMap = new ConcurrentHashMap<>();
private final Map<String, DroneState> droneStateMap = new ConcurrentHashMap<>();
private final Map<String, DrcState> drcStateMap = new ConcurrentHashMap<>();
public void saveAirportState(String airportSn, AirportState state) {
if (airportSn != null && state != null) {
@ -41,6 +43,12 @@ public class RedisStateStore {
}
}
public void saveDrcState(String airportSn, DrcState state) {
if (airportSn != null && state != null) {
drcStateMap.put(airportSn, state);
}
}
public Optional<AirportState> loadAirportState(String airportSn) {
return Optional.ofNullable(airportStateMap.get(airportSn));
}
@ -53,6 +61,10 @@ public class RedisStateStore {
return Optional.ofNullable(droneStateMap.get(droneSn));
}
public Optional<DrcState> loadDrcState(String airportSn) {
return Optional.ofNullable(drcStateMap.get(airportSn));
}
public Set<String> allAirportIds() {
// 合并两张表的 key防止有只存机场或只存舱门的情况
Set<String> ids = ConcurrentHashMap.newKeySet();
@ -64,5 +76,9 @@ public class RedisStateStore {
public Set<String> allDroneIds() {
return droneStateMap.keySet();
}
public Set<String> allDrcIds() {
return drcStateMap.keySet();
}
}

View File

@ -0,0 +1,191 @@
package com.tuoheng.status.machine.service;
import com.tuoheng.status.machine.events.DrcEvent;
import com.tuoheng.status.machine.redis.RedisStateStore;
import com.tuoheng.status.machine.status.DrcState;
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;
/**
* DRC状态机管理器
* 负责管理多个机巢的DRC状态机实例
*/
@Component
public class DrcMachineService {
@Autowired
StateMachineFactory<DrcState, DrcEvent> drcStateMachineFactory;
@Autowired
private RedisStateStore redisStateStore;
/**
* 存储所有机巢的DRC状态机实例
* Key: 机巢ID (airportSn)
* Value: 状态机实例
*/
private final Map<String, StateMachine<DrcState, DrcEvent>> stateMachineMap = new ConcurrentHashMap<>();
/**
* 获取或创建状态机
* 如果状态机不存在则创建新的状态机实例
*
* @param airportSn 机巢序列号
* @return 状态机实例
*/
public StateMachine<DrcState, DrcEvent> getOrCreateStateMachine(String airportSn) {
return stateMachineMap.computeIfAbsent(airportSn, id -> {
StateMachine<DrcState, DrcEvent> stateMachine = drcStateMachineFactory.getStateMachine(id);
// 服务器重启后状态机初始化为 UNKNOWN 状态
// 不从 Redis 恢复旧状态等待第一次心跳同步真实状态
// 这样可以避免服务器重启期间丢失心跳导致的状态不一致问题
stateMachine.start();
System.out.println(String.format("创建并启动DRC状态机: %s, 初始状态: UNKNOWN (等待心跳同步)", id));
return stateMachine;
});
}
/**
* 获取状态机不创建
*
* @param airportSn 机巢序列号
* @return 状态机实例如果不存在返回null
*/
public StateMachine<DrcState, DrcEvent> getStateMachine(String airportSn) {
return stateMachineMap.get(airportSn);
}
/**
* 获取状态机的当前状态
*
* @param airportSn 机巢序列号
* @return 当前状态如果状态机不存在返回null
*/
public DrcState getCurrentState(String airportSn) {
StateMachine<DrcState, DrcEvent> stateMachine = stateMachineMap.get(airportSn);
if (stateMachine == null) {
System.out.println("DRC状态机不存在: " + airportSn);
return null;
}
return stateMachine.getState().getId();
}
/**
* 获取状态机的所有当前状态包括子状态
*
* @param airportSn 机巢序列号
* @return 当前状态集合的字符串表示
*/
public String getCurrentStates(String airportSn) {
StateMachine<DrcState, DrcEvent> 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, DrcEvent event) {
StateMachine<DrcState, DrcEvent> stateMachine = getOrCreateStateMachine(airportSn);
boolean result = stateMachine.sendEvent(event);
if (result) {
// 持久化最新状态
redisStateStore.saveDrcState(airportSn, stateMachine.getState().getId());
System.out.println(String.format("DRC事件发送成功 - 机巢: %s, 事件: %s, 当前状态: %s",
airportSn, event, getCurrentStates(airportSn)));
} else {
System.out.println(String.format("DRC事件发送失败 - 机巢: %s, 事件: %s, 当前状态: %s",
airportSn, event, getCurrentStates(airportSn)));
}
return result;
}
/**
* 移除状态机
*
* @param airportSn 机巢序列号
*/
public void removeStateMachine(String airportSn) {
StateMachine<DrcState, DrcEvent> stateMachine = stateMachineMap.remove(airportSn);
if (stateMachine != null) {
stateMachine.stop();
System.out.println("停止并移除DRC状态机: " + 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, DrcState state) {
StateMachine<DrcState, DrcEvent> 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("重启DRC状态机: " + airportSn);
}
}

View File

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