diff --git a/src/main/java/com/tuoheng/status/machine/action/drc/EnterAction.java b/src/main/java/com/tuoheng/status/machine/action/drc/EnterAction.java new file mode 100644 index 0000000..aa7f420 --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/action/drc/EnterAction.java @@ -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 { +} diff --git a/src/main/java/com/tuoheng/status/machine/action/drc/EnteredAction.java b/src/main/java/com/tuoheng/status/machine/action/drc/EnteredAction.java new file mode 100644 index 0000000..7229bb7 --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/action/drc/EnteredAction.java @@ -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 { +} diff --git a/src/main/java/com/tuoheng/status/machine/action/drc/ExitAction.java b/src/main/java/com/tuoheng/status/machine/action/drc/ExitAction.java new file mode 100644 index 0000000..b2018dc --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/action/drc/ExitAction.java @@ -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 { +} diff --git a/src/main/java/com/tuoheng/status/machine/action/drc/ExitedAction.java b/src/main/java/com/tuoheng/status/machine/action/drc/ExitedAction.java new file mode 100644 index 0000000..3e637f4 --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/action/drc/ExitedAction.java @@ -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 { +} diff --git a/src/main/java/com/tuoheng/status/machine/config/DrcMachineConfig.java b/src/main/java/com/tuoheng/status/machine/config/DrcMachineConfig.java new file mode 100644 index 0000000..5707ac2 --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/config/DrcMachineConfig.java @@ -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动态获取平台特定的Guard、Action和Listener + */ +@Configuration +public class DrcMachineConfig { + + @Autowired + private PlatformStrategyFactory platformStrategyFactory; + + @Bean(name = "drcStateMachineFactory") + public StateMachineFactory drcStateMachineFactory() throws Exception { + return new StateMachineFactory() { + @Override + public StateMachine getStateMachine() { + return null; + } + + @Override + public StateMachine getStateMachine(String machineId) { + try { + // 根据机巢SN获取平台策略 + DrcPlatformStrategy strategy = platformStrategyFactory.getDrcStrategy(machineId); + + StateMachineBuilder.Builder builder = StateMachineBuilder.builder(); + configureDrcStateMachine(builder, strategy); + configureDrcStates(builder); + configureDrcTransitions(builder, strategy); + + StateMachine 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 getStateMachine(UUID uuid) { + return null; + } + }; + } + + private void configureDrcStateMachine( + StateMachineBuilder.Builder builder, + DrcPlatformStrategy strategy) throws Exception { + builder.configureConfiguration() + .withConfiguration() + .autoStartup(true) + .listener(strategy.getListener()); + } + + private void configureDrcStates(StateMachineBuilder.Builder builder) throws Exception { + builder.configureStates() + .withStates() + .initial(DrcState.UNKNOWN) + .states(EnumSet.allOf(DrcState.class)); + } + + private void configureDrcTransitions( + StateMachineBuilder.Builder 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()); + } +} diff --git a/src/main/java/com/tuoheng/status/machine/events/DrcEvent.java b/src/main/java/com/tuoheng/status/machine/events/DrcEvent.java new file mode 100644 index 0000000..825d945 --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/events/DrcEvent.java @@ -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 +} diff --git a/src/main/java/com/tuoheng/status/machine/guard/drc/CanEnterGuard.java b/src/main/java/com/tuoheng/status/machine/guard/drc/CanEnterGuard.java new file mode 100644 index 0000000..70c4d9f --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/guard/drc/CanEnterGuard.java @@ -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 { +} diff --git a/src/main/java/com/tuoheng/status/machine/guard/drc/CanExitGuard.java b/src/main/java/com/tuoheng/status/machine/guard/drc/CanExitGuard.java new file mode 100644 index 0000000..ea90df9 --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/guard/drc/CanExitGuard.java @@ -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 { +} diff --git a/src/main/java/com/tuoheng/status/machine/listener/DefaultDrcListener.java b/src/main/java/com/tuoheng/status/machine/listener/DefaultDrcListener.java new file mode 100644 index 0000000..11f0b7f --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/listener/DefaultDrcListener.java @@ -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 { + + @Override + public void stateChanged(State from, State to) { + if (from != null && to != null) { + System.out.println(String.format("[%s] 状态变化: %s -> %s", + getName(), from.getId(), to.getId())); + } + } + + @Override + public void stateEntered(State state) { + System.out.println(String.format("[%s] 进入状态: %s", getName(), state.getId())); + } + + @Override + public void stateExited(State state) { + System.out.println(String.format("[%s] 退出状态: %s", getName(), state.getId())); + } + + @Override + public void eventNotAccepted(Message event) { + System.out.println(String.format("[%s] 事件未被接受: %s", getName(), event.getPayload())); + } + + @Override + public void transition(Transition transition) { + // 默认不处理 + } + + @Override + public void transitionStarted(Transition 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 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 stateMachine) { + String machineId = (String) stateMachine.getExtendedState().getVariables().get("machineId"); + System.out.println(String.format("[%s] 状态机启动: %s", getName(), machineId)); + } + + @Override + public void stateMachineStopped(StateMachine stateMachine) { + String machineId = (String) stateMachine.getExtendedState().getVariables().get("machineId"); + System.out.println(String.format("[%s] 状态机停止: %s", getName(), machineId)); + } + + @Override + public void stateMachineError(StateMachine 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 stateContext) { + // 默认不处理 + } +} diff --git a/src/main/java/com/tuoheng/status/machine/platform/factory/PlatformStrategyFactory.java b/src/main/java/com/tuoheng/status/machine/platform/factory/PlatformStrategyFactory.java index e2fe033..c30a533 100644 --- a/src/main/java/com/tuoheng/status/machine/platform/factory/PlatformStrategyFactory.java +++ b/src/main/java/com/tuoheng/status/machine/platform/factory/PlatformStrategyFactory.java @@ -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 droneStrategyMap = new ConcurrentHashMap<>(); + /** + * 存储所有DRC平台策略实现 + * Key: PlatformType + * Value: DrcPlatformStrategy实现 + */ + private final Map drcStrategyMap = new ConcurrentHashMap<>(); + /** * 存储各平台对应的系统管理器实现 * Key: PlatformType @@ -53,13 +61,14 @@ public class PlatformStrategyFactory { /** * 注册所有平台策略 - * Spring会自动注入所有实现了AirportPlatformStrategy、CoverPlatformStrategy和DronePlatformStrategy的Bean + * Spring会自动注入所有实现了AirportPlatformStrategy、CoverPlatformStrategy、DronePlatformStrategy和DrcPlatformStrategy的Bean */ @Autowired public void registerStrategies( List airportStrategies, List coverStrategies, List droneStrategies, + List drcStrategies, List 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); + } + /** * 获取机巢的平台类型 * diff --git a/src/main/java/com/tuoheng/status/machine/platform/impl/dji/DjiDrcPlatformStrategy.java b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/DjiDrcPlatformStrategy.java new file mode 100644 index 0000000..a447487 --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/DjiDrcPlatformStrategy.java @@ -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 getCanEnterGuard() { + return canEnterGuard; + } + + @Override + public PlatformGuard getCanExitGuard() { + return canExitGuard; + } + + @Override + public PlatformAction getEnterAction() { + return enterAction; + } + + @Override + public PlatformAction getEnteredAction() { + return enteredAction; + } + + @Override + public PlatformAction getExitAction() { + return exitAction; + } + + @Override + public PlatformAction getExitedAction() { + return exitedAction; + } + + @Override + public PlatformListener getListener() { + return djiDrcListener; + } +} diff --git a/src/main/java/com/tuoheng/status/machine/platform/impl/dji/action/drc/DjiEnterAction.java b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/action/drc/DjiEnterAction.java new file mode 100644 index 0000000..afb58cc --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/action/drc/DjiEnterAction.java @@ -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 context) { + String machineId = (String) context.getExtendedState().getVariables().get("machineId"); + System.out.println(String.format("[DJI] 进入DRC模式: %s", machineId)); + } +} diff --git a/src/main/java/com/tuoheng/status/machine/platform/impl/dji/action/drc/DjiEnteredAction.java b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/action/drc/DjiEnteredAction.java new file mode 100644 index 0000000..c997e96 --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/action/drc/DjiEnteredAction.java @@ -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 context) { + String machineId = (String) context.getExtendedState().getVariables().get("machineId"); + System.out.println(String.format("[DJI] 已进入DRC模式: %s", machineId)); + } +} diff --git a/src/main/java/com/tuoheng/status/machine/platform/impl/dji/action/drc/DjiExitAction.java b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/action/drc/DjiExitAction.java new file mode 100644 index 0000000..0f35206 --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/action/drc/DjiExitAction.java @@ -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 context) { + String machineId = (String) context.getExtendedState().getVariables().get("machineId"); + System.out.println(String.format("[DJI] 退出DRC模式: %s", machineId)); + } +} diff --git a/src/main/java/com/tuoheng/status/machine/platform/impl/dji/action/drc/DjiExitedAction.java b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/action/drc/DjiExitedAction.java new file mode 100644 index 0000000..5d6cea8 --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/action/drc/DjiExitedAction.java @@ -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 context) { + String machineId = (String) context.getExtendedState().getVariables().get("machineId"); + System.out.println(String.format("[DJI] 已退出DRC模式: %s", machineId)); + } +} diff --git a/src/main/java/com/tuoheng/status/machine/platform/impl/dji/guard/drc/DjiCanEnterGuard.java b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/guard/drc/DjiCanEnterGuard.java new file mode 100644 index 0000000..fc2debc --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/guard/drc/DjiCanEnterGuard.java @@ -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 context) { + // DJI平台特定的进入DRC模式检查逻辑 + return true; + } +} diff --git a/src/main/java/com/tuoheng/status/machine/platform/impl/dji/guard/drc/DjiCanExitGuard.java b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/guard/drc/DjiCanExitGuard.java new file mode 100644 index 0000000..4b5a778 --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/guard/drc/DjiCanExitGuard.java @@ -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 context) { + // DJI平台特定的退出DRC模式检查逻辑 + return true; + } +} diff --git a/src/main/java/com/tuoheng/status/machine/platform/impl/dji/listener/DjiDrcListener.java b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/listener/DjiDrcListener.java new file mode 100644 index 0000000..5f7f641 --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/platform/impl/dji/listener/DjiDrcListener.java @@ -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"; + } +} diff --git a/src/main/java/com/tuoheng/status/machine/platform/strategy/DrcPlatformStrategy.java b/src/main/java/com/tuoheng/status/machine/platform/strategy/DrcPlatformStrategy.java new file mode 100644 index 0000000..6d9f275 --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/platform/strategy/DrcPlatformStrategy.java @@ -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(飞行控制模式)平台策略接口 + * 定义各平台需要实现的Guard、Action和Listener + */ +public interface DrcPlatformStrategy { + + /** + * 获取平台类型 + */ + PlatformType getPlatformType(); + + // ==================== Guards ==================== + + /** + * 是否可以进入DRC模式 + */ + PlatformGuard getCanEnterGuard(); + + /** + * 是否可以退出DRC模式 + */ + PlatformGuard getCanExitGuard(); + + // ==================== Actions ==================== + + /** + * 进入DRC模式动作 + */ + PlatformAction getEnterAction(); + + /** + * 进入DRC模式完成动作 + */ + PlatformAction getEnteredAction(); + + /** + * 退出DRC模式动作 + */ + PlatformAction getExitAction(); + + /** + * 退出DRC模式完成动作 + */ + PlatformAction getExitedAction(); + + // ==================== Listener ==================== + + /** + * 获取平台Listener + * 每个平台有一个Listener实例,所有该平台的DRC共享 + */ + PlatformListener getListener(); +} diff --git a/src/main/java/com/tuoheng/status/machine/redis/RedisStateStore.java b/src/main/java/com/tuoheng/status/machine/redis/RedisStateStore.java index 01fc75c..93561fc 100644 --- a/src/main/java/com/tuoheng/status/machine/redis/RedisStateStore.java +++ b/src/main/java/com/tuoheng/status/machine/redis/RedisStateStore.java @@ -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 airportStateMap = new ConcurrentHashMap<>(); private final Map coverStateMap = new ConcurrentHashMap<>(); private final Map droneStateMap = new ConcurrentHashMap<>(); + private final Map 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 loadAirportState(String airportSn) { return Optional.ofNullable(airportStateMap.get(airportSn)); } @@ -53,6 +61,10 @@ public class RedisStateStore { return Optional.ofNullable(droneStateMap.get(droneSn)); } + public Optional loadDrcState(String airportSn) { + return Optional.ofNullable(drcStateMap.get(airportSn)); + } + public Set allAirportIds() { // 合并两张表的 key,防止有只存机场或只存舱门的情况 Set ids = ConcurrentHashMap.newKeySet(); @@ -64,5 +76,9 @@ public class RedisStateStore { public Set allDroneIds() { return droneStateMap.keySet(); } + + public Set allDrcIds() { + return drcStateMap.keySet(); + } } diff --git a/src/main/java/com/tuoheng/status/machine/service/DrcMachineService.java b/src/main/java/com/tuoheng/status/machine/service/DrcMachineService.java new file mode 100644 index 0000000..a0d7c2b --- /dev/null +++ b/src/main/java/com/tuoheng/status/machine/service/DrcMachineService.java @@ -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 drcStateMachineFactory; + + @Autowired + private RedisStateStore redisStateStore; + + /** + * 存储所有机巢的DRC状态机实例 + * Key: 机巢ID (airportSn) + * Value: 状态机实例 + */ + private final Map> stateMachineMap = new ConcurrentHashMap<>(); + + /** + * 获取或创建状态机 + * 如果状态机不存在,则创建新的状态机实例 + * + * @param airportSn 机巢序列号 + * @return 状态机实例 + */ + public StateMachine getOrCreateStateMachine(String airportSn) { + return stateMachineMap.computeIfAbsent(airportSn, id -> { + StateMachine 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 getStateMachine(String airportSn) { + return stateMachineMap.get(airportSn); + } + + /** + * 获取状态机的当前状态 + * + * @param airportSn 机巢序列号 + * @return 当前状态,如果状态机不存在返回null + */ + public DrcState getCurrentState(String airportSn) { + StateMachine 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 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 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 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 getAllAirportIds() { + return stateMachineMap.keySet(); + } + + /** + * 检查状态机是否处于指定状态 + * + * @param airportSn 机巢序列号 + * @param state 要检查的状态 + * @return 是否处于指定状态 + */ + public boolean isInState(String airportSn, DrcState state) { + StateMachine 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); + } +} diff --git a/src/main/java/com/tuoheng/status/machine/status/DrcState.java b/src/main/java/com/tuoheng/status/machine/status/DrcState.java index 24b2ba7..5b477db 100644 --- a/src/main/java/com/tuoheng/status/machine/status/DrcState.java +++ b/src/main/java/com/tuoheng/status/machine/status/DrcState.java @@ -5,5 +5,28 @@ package com.tuoheng.status.machine.status; * 未知状态 退出状态 进入中 进入状态 退出中 */ public enum DrcState { + /** + * 未知状态(服务器重启后的初始状态,等待第一次心跳同步) + */ + UNKNOWN, + /** + * 退出状态(DRC模式已退出) + */ + EXITED, + + /** + * 进入中(正在进入DRC模式) + */ + ENTERING, + + /** + * 进入状态(已进入DRC模式) + */ + ENTERED, + + /** + * 退出中(正在退出DRC模式) + */ + EXITING }