添加状态初始化能力
This commit is contained in:
parent
89e49dd17d
commit
51fe7cf816
|
|
@ -70,8 +70,9 @@ public class AirportMachineConfig {
|
||||||
private void configureStates(StateMachineBuilder.Builder<AirportState, AirportEvent> builder) throws Exception {
|
private void configureStates(StateMachineBuilder.Builder<AirportState, AirportEvent> builder) throws Exception {
|
||||||
builder.configureStates()
|
builder.configureStates()
|
||||||
.withStates()
|
.withStates()
|
||||||
.initial(AirportState.OFFLINE)
|
.initial(AirportState.UNKNOWN)
|
||||||
.states(EnumSet.of(
|
.states(EnumSet.of(
|
||||||
|
AirportState.UNKNOWN,
|
||||||
AirportState.OFFLINE,
|
AirportState.OFFLINE,
|
||||||
AirportState.ONLINE,
|
AirportState.ONLINE,
|
||||||
AirportState.REBOOTING
|
AirportState.REBOOTING
|
||||||
|
|
@ -90,6 +91,43 @@ public class AirportMachineConfig {
|
||||||
StateMachineBuilder.Builder<AirportState, AirportEvent> builder,
|
StateMachineBuilder.Builder<AirportState, AirportEvent> builder,
|
||||||
AirportPlatformStrategy strategy) throws Exception {
|
AirportPlatformStrategy strategy) throws Exception {
|
||||||
builder.configureTransitions()
|
builder.configureTransitions()
|
||||||
|
// ========== 从 UNKNOWN 到所有状态的转换(服务器重启后状态同步) ==========
|
||||||
|
// UNKNOWN -> OFFLINE
|
||||||
|
.withExternal()
|
||||||
|
.source(AirportState.UNKNOWN)
|
||||||
|
.target(AirportState.OFFLINE)
|
||||||
|
.event(AirportEvent.AIRPORT_OFFLINE)
|
||||||
|
.and()
|
||||||
|
|
||||||
|
// UNKNOWN -> ONLINE(STANDBY)
|
||||||
|
.withExternal()
|
||||||
|
.source(AirportState.UNKNOWN)
|
||||||
|
.target(AirportState.ONLINE)
|
||||||
|
.event(AirportEvent.AIRPORT_ONLINE)
|
||||||
|
.and()
|
||||||
|
|
||||||
|
// UNKNOWN -> STANDBY
|
||||||
|
.withExternal()
|
||||||
|
.source(AirportState.UNKNOWN)
|
||||||
|
.target(AirportState.STANDBY)
|
||||||
|
.event(AirportEvent.AIRPORT_ONLINE)
|
||||||
|
.and()
|
||||||
|
|
||||||
|
// UNKNOWN -> DEBUG_MODE
|
||||||
|
.withExternal()
|
||||||
|
.source(AirportState.UNKNOWN)
|
||||||
|
.target(AirportState.DEBUG_MODE)
|
||||||
|
.event(AirportEvent.DEBUG_MODE_OPEN)
|
||||||
|
.and()
|
||||||
|
|
||||||
|
// UNKNOWN -> REBOOTING
|
||||||
|
.withExternal()
|
||||||
|
.source(AirportState.UNKNOWN)
|
||||||
|
.target(AirportState.REBOOTING)
|
||||||
|
.event(AirportEvent.AIRPORT_REBOOT)
|
||||||
|
.and()
|
||||||
|
|
||||||
|
// ========== 正常状态转换(带 Guard 和 Action) ==========
|
||||||
// OFFLINE -> ONLINE(STANDBY)
|
// OFFLINE -> ONLINE(STANDBY)
|
||||||
.withExternal()
|
.withExternal()
|
||||||
.source(AirportState.OFFLINE)
|
.source(AirportState.OFFLINE)
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ public class CoverMachineConfig {
|
||||||
private void configureCoverStates(StateMachineBuilder.Builder<CoverState, CoverEvent> builder) throws Exception {
|
private void configureCoverStates(StateMachineBuilder.Builder<CoverState, CoverEvent> builder) throws Exception {
|
||||||
builder.configureStates()
|
builder.configureStates()
|
||||||
.withStates()
|
.withStates()
|
||||||
.initial(CoverState.CLOSED)
|
.initial(CoverState.UNKNOWN)
|
||||||
.states(EnumSet.allOf(CoverState.class));
|
.states(EnumSet.allOf(CoverState.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,6 +78,50 @@ public class CoverMachineConfig {
|
||||||
StateMachineBuilder.Builder<CoverState, CoverEvent> builder,
|
StateMachineBuilder.Builder<CoverState, CoverEvent> builder,
|
||||||
CoverPlatformStrategy strategy) throws Exception {
|
CoverPlatformStrategy strategy) throws Exception {
|
||||||
builder.configureTransitions()
|
builder.configureTransitions()
|
||||||
|
// ========== 从 UNKNOWN 到所有状态的转换(服务器重启后状态同步) ==========
|
||||||
|
// UNKNOWN -> CLOSED
|
||||||
|
.withExternal()
|
||||||
|
.source(CoverState.UNKNOWN)
|
||||||
|
.target(CoverState.CLOSED)
|
||||||
|
.event(CoverEvent.CLOSED)
|
||||||
|
.and()
|
||||||
|
|
||||||
|
// UNKNOWN -> OPENING
|
||||||
|
.withExternal()
|
||||||
|
.source(CoverState.UNKNOWN)
|
||||||
|
.target(CoverState.OPENING)
|
||||||
|
.event(CoverEvent.OPEN)
|
||||||
|
.and()
|
||||||
|
|
||||||
|
// UNKNOWN -> OPENED
|
||||||
|
.withExternal()
|
||||||
|
.source(CoverState.UNKNOWN)
|
||||||
|
.target(CoverState.OPENED)
|
||||||
|
.event(CoverEvent.OPENED)
|
||||||
|
.and()
|
||||||
|
|
||||||
|
// UNKNOWN -> CLOSING
|
||||||
|
.withExternal()
|
||||||
|
.source(CoverState.UNKNOWN)
|
||||||
|
.target(CoverState.CLOSING)
|
||||||
|
.event(CoverEvent.CLOSE)
|
||||||
|
.and()
|
||||||
|
|
||||||
|
// UNKNOWN -> HALF_OPEN
|
||||||
|
.withExternal()
|
||||||
|
.source(CoverState.UNKNOWN)
|
||||||
|
.target(CoverState.HALF_OPEN)
|
||||||
|
.event(CoverEvent.OPENED)
|
||||||
|
.and()
|
||||||
|
|
||||||
|
// UNKNOWN -> ERROR
|
||||||
|
.withExternal()
|
||||||
|
.source(CoverState.UNKNOWN)
|
||||||
|
.target(CoverState.ERROR)
|
||||||
|
.event(CoverEvent.ERROR)
|
||||||
|
.and()
|
||||||
|
|
||||||
|
// ========== 正常状态转换(带 Guard 和 Action) ==========
|
||||||
// CLOSED -> OPENING
|
// CLOSED -> OPENING
|
||||||
.withExternal()
|
.withExternal()
|
||||||
.source(CoverState.CLOSED)
|
.source(CoverState.CLOSED)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ public class MultiPlatformDemo {
|
||||||
|
|
||||||
// 获取必要的Bean
|
// 获取必要的Bean
|
||||||
PlatformStrategyFactory strategyFactory = context.getBean(PlatformStrategyFactory.class);
|
PlatformStrategyFactory strategyFactory = context.getBean(PlatformStrategyFactory.class);
|
||||||
AirportPlatformRepository repository = context.getBean(AirportPlatformRepository.class);
|
|
||||||
|
|
||||||
System.out.println("\n========== DJI 机巢系统演示开始 ==========\n");
|
System.out.println("\n========== DJI 机巢系统演示开始 ==========\n");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
package com.tuoheng.status.machine.manager;
|
package com.tuoheng.status.machine.manager;
|
||||||
|
|
||||||
|
import com.tuoheng.status.machine.events.AirportEvent;
|
||||||
|
import com.tuoheng.status.machine.events.CoverEvent;
|
||||||
import com.tuoheng.status.machine.platform.PlatformType;
|
import com.tuoheng.status.machine.platform.PlatformType;
|
||||||
import com.tuoheng.status.machine.service.AirportMachineService;
|
import com.tuoheng.status.machine.service.AirportMachineService;
|
||||||
import com.tuoheng.status.machine.service.CoverMachineService;
|
import com.tuoheng.status.machine.service.CoverMachineService;
|
||||||
|
import com.tuoheng.status.machine.status.AirportState;
|
||||||
|
import com.tuoheng.status.machine.status.CoverState;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -22,5 +26,137 @@ public abstract class AbstractAirportSystemManager implements AirportSystemManag
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected CoverMachineService coverService;
|
protected CoverMachineService coverService;
|
||||||
|
|
||||||
|
|
||||||
|
public boolean sendEvent(String airportSn, AirportEvent event) {
|
||||||
|
return airportService.sendEvent(airportSn, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean sendEvent(String airportSn, CoverEvent event) {
|
||||||
|
return coverService.sendEvent(airportSn, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步机巢状态
|
||||||
|
* 仅在当前状态为 UNKNOWN 时才同步到目标状态
|
||||||
|
*
|
||||||
|
* @param airportSn 机巢序列号
|
||||||
|
* @param targetState 目标状态
|
||||||
|
* @return 是否同步成功
|
||||||
|
*/
|
||||||
|
public boolean syncAirportState(String airportSn, AirportState targetState) {
|
||||||
|
AirportState currentState = airportService.getCurrentState(airportSn);
|
||||||
|
|
||||||
|
if (currentState == null) {
|
||||||
|
System.out.println(String.format("同步机巢状态失败 - 机巢: %s, 状态机不存在", airportSn));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentState != AirportState.UNKNOWN) {
|
||||||
|
System.out.println(String.format("同步机巢状态跳过 - 机巢: %s, 当前状态: %s (非UNKNOWN状态,无需同步)",
|
||||||
|
airportSn, currentState));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据目标状态发送相应的事件
|
||||||
|
AirportEvent event = getAirportEventForState(targetState);
|
||||||
|
if (event == null) {
|
||||||
|
System.out.println(String.format("同步机巢状态失败 - 机巢: %s, 无法为目标状态 %s 找到对应事件",
|
||||||
|
airportSn, targetState));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean result = airportService.sendEvent(airportSn, event);
|
||||||
|
if (result) {
|
||||||
|
System.out.println(String.format("同步机巢状态成功 - 机巢: %s, 从 UNKNOWN 同步到 %s",
|
||||||
|
airportSn, targetState));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步舱门状态
|
||||||
|
* 仅在当前状态为 UNKNOWN 时才同步到目标状态
|
||||||
|
*
|
||||||
|
* @param airportSn 机巢序列号
|
||||||
|
* @param targetState 目标状态
|
||||||
|
* @return 是否同步成功
|
||||||
|
*/
|
||||||
|
public boolean syncCoverState(String airportSn, CoverState targetState) {
|
||||||
|
CoverState currentState = coverService.getCurrentState(airportSn);
|
||||||
|
|
||||||
|
if (currentState == null) {
|
||||||
|
System.out.println(String.format("同步舱门状态失败 - 机巢: %s, 状态机不存在", airportSn));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentState != CoverState.UNKNOWN) {
|
||||||
|
System.out.println(String.format("同步舱门状态跳过 - 机巢: %s, 当前状态: %s (非UNKNOWN状态,无需同步)",
|
||||||
|
airportSn, currentState));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据目标状态发送相应的事件
|
||||||
|
CoverEvent event = getCoverEventForState(targetState);
|
||||||
|
if (event == null) {
|
||||||
|
System.out.println(String.format("同步舱门状态失败 - 机巢: %s, 无法为目标状态 %s 找到对应事件",
|
||||||
|
airportSn, targetState));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean result = coverService.sendEvent(airportSn, event);
|
||||||
|
if (result) {
|
||||||
|
System.out.println(String.format("同步舱门状态成功 - 机巢: %s, 从 UNKNOWN 同步到 %s",
|
||||||
|
airportSn, targetState));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据目标状态获取对应的机巢事件
|
||||||
|
* 子类可以重写此方法以提供自定义的状态到事件的映射
|
||||||
|
*
|
||||||
|
* @param targetState 目标状态
|
||||||
|
* @return 对应的事件,如果没有对应事件则返回null
|
||||||
|
*/
|
||||||
|
protected AirportEvent getAirportEventForState(AirportState targetState) {
|
||||||
|
switch (targetState) {
|
||||||
|
case ONLINE:
|
||||||
|
case STANDBY:
|
||||||
|
return AirportEvent.AIRPORT_ONLINE;
|
||||||
|
case OFFLINE:
|
||||||
|
return AirportEvent.AIRPORT_OFFLINE;
|
||||||
|
case DEBUG_MODE:
|
||||||
|
return AirportEvent.DEBUG_MODE_OPEN;
|
||||||
|
case REBOOTING:
|
||||||
|
return AirportEvent.AIRPORT_REBOOT;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据目标状态获取对应的舱门事件
|
||||||
|
* 子类可以重写此方法以提供自定义的状态到事件的映射
|
||||||
|
*
|
||||||
|
* @param targetState 目标状态
|
||||||
|
* @return 对应的事件,如果没有对应事件则返回null
|
||||||
|
*/
|
||||||
|
protected CoverEvent getCoverEventForState(CoverState targetState) {
|
||||||
|
switch (targetState) {
|
||||||
|
case CLOSED:
|
||||||
|
return CoverEvent.CLOSED;
|
||||||
|
case OPENED:
|
||||||
|
return CoverEvent.OPENED;
|
||||||
|
case OPENING:
|
||||||
|
return CoverEvent.OPEN;
|
||||||
|
case CLOSING:
|
||||||
|
return CoverEvent.CLOSE;
|
||||||
|
case ERROR:
|
||||||
|
return CoverEvent.ERROR;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package com.tuoheng.status.machine.manager;
|
package com.tuoheng.status.machine.manager;
|
||||||
|
|
||||||
|
import com.tuoheng.status.machine.events.AirportEvent;
|
||||||
|
import com.tuoheng.status.machine.events.CoverEvent;
|
||||||
import com.tuoheng.status.machine.platform.PlatformType;
|
import com.tuoheng.status.machine.platform.PlatformType;
|
||||||
import com.tuoheng.status.machine.status.AirportState;
|
import com.tuoheng.status.machine.status.AirportState;
|
||||||
import com.tuoheng.status.machine.status.CoverState;
|
import com.tuoheng.status.machine.status.CoverState;
|
||||||
|
|
@ -15,6 +17,18 @@ public interface AirportSystemManager {
|
||||||
*/
|
*/
|
||||||
PlatformType getPlatformType();
|
PlatformType getPlatformType();
|
||||||
|
|
||||||
|
public boolean sendEvent(String airportSn, AirportEvent event);
|
||||||
|
|
||||||
|
public boolean sendEvent(String airportSn, CoverEvent event);
|
||||||
|
|
||||||
|
public boolean syncAirportState(String airportSn, AirportState targetState);
|
||||||
|
|
||||||
|
public boolean syncCoverState(String airportSn, CoverState targetState);
|
||||||
|
/**
|
||||||
|
* 有心跳的时候调用
|
||||||
|
* @param airportSn
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
boolean airportOnline(String airportSn);
|
boolean airportOnline(String airportSn);
|
||||||
|
|
||||||
boolean airportOffline(String airportSn);
|
boolean airportOffline(String airportSn);
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,19 @@ public class DjiAirportSystemManager extends AbstractAirportSystemManager {
|
||||||
return PlatformType.DJI;
|
return PlatformType.DJI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param airportSn
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean airportOnline(String airportSn) {
|
public boolean airportOnline(String airportSn) {
|
||||||
return airportService.sendEvent(airportSn, AirportEvent.AIRPORT_ONLINE);
|
return airportService.sendEvent(airportSn, AirportEvent.AIRPORT_ONLINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param airportSn
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean airportOffline(String airportSn) {
|
public boolean airportOffline(String airportSn) {
|
||||||
return airportService.sendEvent(airportSn, AirportEvent.AIRPORT_OFFLINE);
|
return airportService.sendEvent(airportSn, AirportEvent.AIRPORT_OFFLINE);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.tuoheng.status.machine.redis;
|
||||||
|
|
||||||
|
import com.tuoheng.status.machine.status.AirportState;
|
||||||
|
import com.tuoheng.status.machine.status.CoverState;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 Redis 记录和恢复机巢/舱门状态的存储组件。
|
||||||
|
*
|
||||||
|
* 当前实现采用内存 Map 占位,便于无 Redis 环境下直接运行。
|
||||||
|
* 如果接入真正的 Redis,只需将存取逻辑替换为 RedisTemplate 等实现。
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class RedisStateStore {
|
||||||
|
|
||||||
|
private final Map<String, AirportState> airportStateMap = new ConcurrentHashMap<>();
|
||||||
|
private final Map<String, CoverState> coverStateMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public void saveAirportState(String airportSn, AirportState state) {
|
||||||
|
if (airportSn != null && state != null) {
|
||||||
|
airportStateMap.put(airportSn, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveCoverState(String airportSn, CoverState state) {
|
||||||
|
if (airportSn != null && state != null) {
|
||||||
|
coverStateMap.put(airportSn, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<AirportState> loadAirportState(String airportSn) {
|
||||||
|
return Optional.ofNullable(airportStateMap.get(airportSn));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<CoverState> loadCoverState(String airportSn) {
|
||||||
|
return Optional.ofNullable(coverStateMap.get(airportSn));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> allAirportIds() {
|
||||||
|
// 合并两张表的 key,防止有只存机场或只存舱门的情况
|
||||||
|
Set<String> ids = ConcurrentHashMap.newKeySet();
|
||||||
|
ids.addAll(airportStateMap.keySet());
|
||||||
|
ids.addAll(coverStateMap.keySet());
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
package com.tuoheng.status.machine.service;
|
package com.tuoheng.status.machine.service;
|
||||||
|
|
||||||
import com.tuoheng.status.machine.events.AirportEvent;
|
import com.tuoheng.status.machine.events.AirportEvent;
|
||||||
|
import com.tuoheng.status.machine.redis.RedisStateStore;
|
||||||
import com.tuoheng.status.machine.status.AirportState;
|
import com.tuoheng.status.machine.status.AirportState;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.statemachine.StateMachine;
|
import org.springframework.statemachine.StateMachine;
|
||||||
import org.springframework.statemachine.config.StateMachineFactory;
|
import org.springframework.statemachine.config.StateMachineFactory;
|
||||||
|
import org.springframework.statemachine.support.DefaultStateMachineContext;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -20,6 +22,9 @@ public class AirportMachineService {
|
||||||
@Autowired
|
@Autowired
|
||||||
StateMachineFactory<AirportState, AirportEvent> stateMachineFactory;
|
StateMachineFactory<AirportState, AirportEvent> stateMachineFactory;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisStateStore redisStateStore;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 存储所有机巢的状态机实例
|
* 存储所有机巢的状态机实例
|
||||||
* Key: 机巢ID (airportSn)
|
* Key: 机巢ID (airportSn)
|
||||||
|
|
@ -37,8 +42,11 @@ public class AirportMachineService {
|
||||||
public StateMachine<AirportState, AirportEvent> getOrCreateStateMachine(String airportSn) {
|
public StateMachine<AirportState, AirportEvent> getOrCreateStateMachine(String airportSn) {
|
||||||
return stateMachineMap.computeIfAbsent(airportSn, id -> {
|
return stateMachineMap.computeIfAbsent(airportSn, id -> {
|
||||||
StateMachine<AirportState, AirportEvent> stateMachine = stateMachineFactory.getStateMachine(id);
|
StateMachine<AirportState, AirportEvent> stateMachine = stateMachineFactory.getStateMachine(id);
|
||||||
|
// 服务器重启后,状态机初始化为 UNKNOWN 状态
|
||||||
|
// 不从 Redis 恢复旧状态,等待第一次心跳同步真实状态
|
||||||
|
// 这样可以避免服务器重启期间丢失心跳导致的状态不一致问题
|
||||||
stateMachine.start();
|
stateMachine.start();
|
||||||
System.out.println("创建并启动状态机: " + id);
|
System.out.println(String.format("创建并启动状态机: %s, 初始状态: UNKNOWN (等待心跳同步)", id));
|
||||||
return stateMachine;
|
return stateMachine;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -103,6 +111,8 @@ public class AirportMachineService {
|
||||||
boolean result = stateMachine.sendEvent(event);
|
boolean result = stateMachine.sendEvent(event);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
// 持久化最新状态
|
||||||
|
redisStateStore.saveAirportState(airportSn, stateMachine.getState().getId());
|
||||||
System.out.println(String.format("事件发送成功 - 机巢: %s, 事件: %s, 当前状态: %s",
|
System.out.println(String.format("事件发送成功 - 机巢: %s, 事件: %s, 当前状态: %s",
|
||||||
airportSn, event, getCurrentStates(airportSn)));
|
airportSn, event, getCurrentStates(airportSn)));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
package com.tuoheng.status.machine.service;
|
package com.tuoheng.status.machine.service;
|
||||||
|
|
||||||
import com.tuoheng.status.machine.events.CoverEvent;
|
import com.tuoheng.status.machine.events.CoverEvent;
|
||||||
|
import com.tuoheng.status.machine.redis.RedisStateStore;
|
||||||
import com.tuoheng.status.machine.status.CoverState;
|
import com.tuoheng.status.machine.status.CoverState;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.statemachine.StateMachine;
|
import org.springframework.statemachine.StateMachine;
|
||||||
import org.springframework.statemachine.config.StateMachineFactory;
|
import org.springframework.statemachine.config.StateMachineFactory;
|
||||||
|
import org.springframework.statemachine.support.DefaultStateMachineContext;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -19,13 +21,19 @@ public class CoverMachineService {
|
||||||
@Autowired
|
@Autowired
|
||||||
StateMachineFactory<CoverState, CoverEvent> coverStateMachineFactory;
|
StateMachineFactory<CoverState, CoverEvent> coverStateMachineFactory;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisStateStore redisStateStore;
|
||||||
|
|
||||||
private final Map<String, StateMachine<CoverState, CoverEvent>> stateMachineMap = new ConcurrentHashMap<>();
|
private final Map<String, StateMachine<CoverState, CoverEvent>> stateMachineMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public StateMachine<CoverState, CoverEvent> getOrCreateStateMachine(String airportSn) {
|
public StateMachine<CoverState, CoverEvent> getOrCreateStateMachine(String airportSn) {
|
||||||
return stateMachineMap.computeIfAbsent(airportSn, id -> {
|
return stateMachineMap.computeIfAbsent(airportSn, id -> {
|
||||||
StateMachine<CoverState, CoverEvent> stateMachine = coverStateMachineFactory.getStateMachine(id);
|
StateMachine<CoverState, CoverEvent> stateMachine = coverStateMachineFactory.getStateMachine(id);
|
||||||
|
// 服务器重启后,状态机初始化为 UNKNOWN 状态
|
||||||
|
// 不从 Redis 恢复旧状态,等待第一次心跳同步真实状态
|
||||||
|
// 这样可以避免服务器重启期间丢失心跳导致的状态不一致问题
|
||||||
stateMachine.start();
|
stateMachine.start();
|
||||||
System.out.println("创建并启动舱门状态机: " + id);
|
System.out.println(String.format("创建并启动舱门状态机: %s, 初始状态: UNKNOWN (等待心跳同步)", id));
|
||||||
return stateMachine;
|
return stateMachine;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -43,6 +51,8 @@ public class CoverMachineService {
|
||||||
boolean result = stateMachine.sendEvent(event);
|
boolean result = stateMachine.sendEvent(event);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
// 持久化最新状态
|
||||||
|
redisStateStore.saveCoverState(airportSn, stateMachine.getState().getId());
|
||||||
System.out.println(String.format("舱门事件发送成功 - 机巢: %s, 事件: %s, 当前状态: %s",
|
System.out.println(String.format("舱门事件发送成功 - 机巢: %s, 事件: %s, 当前状态: %s",
|
||||||
airportSn, event, getCurrentState(airportSn)));
|
airportSn, event, getCurrentState(airportSn)));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,11 @@ package com.tuoheng.status.machine.status;
|
||||||
* 机巢状态枚举(简化版 - 舱门状态已分离)
|
* 机巢状态枚举(简化版 - 舱门状态已分离)
|
||||||
*/
|
*/
|
||||||
public enum AirportState {
|
public enum AirportState {
|
||||||
|
/**
|
||||||
|
* 未知状态(服务器重启后的初始状态,等待第一次心跳同步)
|
||||||
|
*/
|
||||||
|
UNKNOWN,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 离线
|
* 离线
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,11 @@ package com.tuoheng.status.machine.status;
|
||||||
* 舱门状态枚举
|
* 舱门状态枚举
|
||||||
*/
|
*/
|
||||||
public enum CoverState {
|
public enum CoverState {
|
||||||
|
/**
|
||||||
|
* 未知状态(服务器重启后的初始状态,等待第一次心跳同步)
|
||||||
|
*/
|
||||||
|
UNKNOWN,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 舱门已关闭
|
* 舱门已关闭
|
||||||
*/
|
*/
|
||||||
|
|
@ -24,11 +29,6 @@ public enum CoverState {
|
||||||
*/
|
*/
|
||||||
CLOSING,
|
CLOSING,
|
||||||
|
|
||||||
/**
|
|
||||||
* 舱门半开
|
|
||||||
*/
|
|
||||||
HALF_OPEN,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 舱门状态异常
|
* 舱门状态异常
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue