修改方法

This commit is contained in:
孙小云 2025-12-16 17:40:31 +08:00
parent 092043895c
commit 22a81cff7e
12 changed files with 114 additions and 36 deletions

View File

@ -4,11 +4,13 @@ import com.tuoheng.machine.events.AirportEvent;
import com.tuoheng.machine.platform.factory.PlatformStrategyFactory;
import com.tuoheng.machine.platform.strategy.AirportPlatformStrategy;
import com.tuoheng.machine.status.AirportState;
import lombok.extern.slf4j.Slf4j;
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.stereotype.Component;
import java.util.EnumSet;
import java.util.UUID;
@ -18,6 +20,7 @@ import java.util.UUID;
* 通过PlatformStrategyFactory动态获取平台特定的GuardAction和Listener
*/
@Configuration
@Slf4j
public class AirportMachineConfig {
@Bean(name = "airportStateMachineFactory")
@ -45,6 +48,7 @@ public class AirportMachineConfig {
stateMachine.getExtendedState().getVariables().put("machineId", machineId);
return stateMachine;
} catch (Exception e) {
log.error("创建AIRPORT机巢状态机失败 - 机器ID: {}", machineId, e);
throw new RuntimeException("Failed to create state machine for: " + machineId, e);
}
}

View File

@ -4,6 +4,7 @@ import com.tuoheng.machine.events.CoverEvent;
import com.tuoheng.machine.platform.factory.PlatformStrategyFactory;
import com.tuoheng.machine.platform.strategy.CoverPlatformStrategy;
import com.tuoheng.machine.status.CoverState;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachine;
@ -18,6 +19,7 @@ import java.util.UUID;
* 通过PlatformStrategyFactory动态获取平台特定的GuardAction和Listener
*/
@Configuration
@Slf4j
public class CoverMachineConfig {
@Bean
@ -44,6 +46,7 @@ public class CoverMachineConfig {
stateMachine.getExtendedState().getVariables().put("machineId", machineId);
return stateMachine;
} catch (Exception e) {
log.error("创建COVER状态机失败 - 机器ID: {}", machineId, e);
throw new RuntimeException("Failed to create cover state machine for: " + machineId, e);
}
}

View File

@ -4,6 +4,7 @@ import com.tuoheng.machine.events.DrcEvent;
import com.tuoheng.machine.platform.factory.PlatformStrategyFactory;
import com.tuoheng.machine.platform.strategy.DrcPlatformStrategy;
import com.tuoheng.machine.status.DrcState;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachine;
@ -18,11 +19,13 @@ import java.util.UUID;
* 通过PlatformStrategyFactory动态获取平台特定的GuardAction和Listener
*/
@Configuration
@Slf4j
public class DrcMachineConfig {
@Bean(name = "drcStateMachineFactory")
public StateMachineFactory<DrcState, DrcEvent> drcStateMachineFactory(
PlatformStrategyFactory platformStrategyFactory) throws Exception {
return new StateMachineFactory<DrcState, DrcEvent>() {
@Override
public StateMachine<DrcState, DrcEvent> getStateMachine() {
@ -44,6 +47,7 @@ public class DrcMachineConfig {
stateMachine.getExtendedState().getVariables().put("machineId", machineId);
return stateMachine;
} catch (Exception e) {
log.error("创建DRC状态机失败 - 机器ID: {}", machineId, e);
throw new RuntimeException("Failed to create DRC state machine for: " + machineId, e);
}
}
@ -87,7 +91,7 @@ public class DrcMachineConfig {
.withExternal()
.source(DrcState.UNKNOWN)
.target(DrcState.ENTERING)
.event(DrcEvent.ENTER)
.event(DrcEvent.ENTERING)
.and()
// UNKNOWN -> ENTERED
@ -101,7 +105,7 @@ public class DrcMachineConfig {
.withExternal()
.source(DrcState.UNKNOWN)
.target(DrcState.EXITING)
.event(DrcEvent.EXIT)
.event(DrcEvent.EXITING)
.and()
// ========== 正常状态转换 Guard Action ==========
@ -109,7 +113,7 @@ public class DrcMachineConfig {
.withExternal()
.source(DrcState.EXITED)
.target(DrcState.ENTERING)
.event(DrcEvent.ENTER)
.event(DrcEvent.ENTERING)
.action(strategy.getEnterAction())
.guard(strategy.getCanEnterGuard())
.and()
@ -126,7 +130,7 @@ public class DrcMachineConfig {
.withExternal()
.source(DrcState.ENTERED)
.target(DrcState.EXITING)
.event(DrcEvent.EXIT)
.event(DrcEvent.EXITING)
.action(strategy.getExitAction())
.guard(strategy.getCanExitGuard())
.and()

View File

@ -4,6 +4,7 @@ import com.tuoheng.machine.events.DroneEvent;
import com.tuoheng.machine.platform.factory.PlatformStrategyFactory;
import com.tuoheng.machine.platform.strategy.DronePlatformStrategy;
import com.tuoheng.machine.status.DroneState;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachine;
@ -18,6 +19,7 @@ import java.util.UUID;
* 通过PlatformStrategyFactory动态获取平台特定的GuardAction和Listener
*/
@Configuration
@Slf4j
public class DroneMachineConfig {
@Bean(name = "droneStateMachineFactory")
@ -44,6 +46,7 @@ public class DroneMachineConfig {
stateMachine.getExtendedState().getVariables().put("machineId", machineId);
return stateMachine;
} catch (Exception e) {
log.error("创建DRONE状态机失败 - 机器ID: {}", machineId, e);
throw new RuntimeException("Failed to create drone state machine for: " + machineId, e);
}
}

View File

@ -9,7 +9,7 @@ public enum DrcEvent {
* 进入DRC模式指令
* 触发源: 用户指令
*/
ENTER,
ENTERING,
/**
* 进入DRC模式完成
@ -21,7 +21,7 @@ public enum DrcEvent {
* 退出DRC模式指令
* 触发源: 用户指令/自动
*/
EXIT,
EXITING,
/**
* 退出DRC模式完成

View File

@ -8,6 +8,8 @@ import com.tuoheng.machine.status.DrcState;
import org.springframework.statemachine.StateContext;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
@Slf4j
@Component
public class DjiExitAction extends ExitAction {
@ -20,6 +22,10 @@ public class DjiExitAction extends ExitAction {
@Override
public void execute(StateContext<DrcState, DrcEvent> context) {
String machineId = (String) context.getExtendedState().getVariables().get("machineId");
log.info("[DJI] 退出DRC模式: %s", machineId);
log.info("[DJI] {} 退出DRC模式: %s 判断外部命令是否成功", machineId);
/**
* 发生命令的结果是失败或者超时了这边需要抛出异常
*/
throw new RuntimeException("");
}
}

View File

@ -22,7 +22,9 @@ public class DjiCanExitGuard extends CanExitGuard {
@Override
public boolean evaluate(StateContext<DrcState, DrcEvent> context) {
// DJI平台特定的退出DRC模式检查逻辑
String machineId = (String) context.getExtendedState().getVariables().get("machineId");
log.info("[DJI] {} 退出DRC模式: %s 执行外部命令", machineId);
// 外部命令执行成功返回true,执行失败返回false
return true;
}
}

View File

@ -1,16 +1,27 @@
package com.tuoheng.machine.impl.dji.listener;
import com.tuoheng.machine.events.DrcEvent;
import com.tuoheng.machine.listener.DefaultDrcListener;
import com.tuoheng.machine.status.DrcState;
import lombok.extern.slf4j.Slf4j;
import org.springframework.statemachine.state.State;
import org.springframework.stereotype.Component;
/**
* DJI平台DRC状态监听器
*/
@Component
@Slf4j
public class DjiDrcListener extends DefaultDrcListener {
@Override
public String getName() {
return "DJI-DRC";
}
@Override
public void stateEntered(State<DrcState, DrcEvent> state) {
log.debug("[{}] 大疆进入状态: {}", getName(), state.getId());
}
}

View File

@ -13,6 +13,8 @@ import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.transition.Transition;
import java.util.concurrent.CompletableFuture;
/**
* 默认DRC状态监听器
* 提供基础的状态变化监听功能各平台可以继承并定制
@ -31,6 +33,7 @@ public abstract class DefaultDrcListener implements PlatformListener<DrcState, D
@Override
public void stateEntered(State<DrcState, DrcEvent> state) {
log.debug("[{}] 进入状态: {}", getName(), state.getId());
}
@Override
@ -91,5 +94,6 @@ public abstract class DefaultDrcListener implements PlatformListener<DrcState, D
@Override
public void stateContext(StateContext<DrcState, DrcEvent> stateContext) {
// 默认不处理
}
}

View File

@ -315,11 +315,11 @@ public abstract class AbsSystemManager implements ISystemManager {
case EXITED:
return DrcEvent.EXITED;
case ENTERING:
return DrcEvent.ENTER;
return DrcEvent.ENTERING;
case ENTERED:
return DrcEvent.ENTERED;
case EXITING:
return DrcEvent.EXIT;
return DrcEvent.EXITING;
default:
return null;
}

View File

@ -0,0 +1,12 @@
spring:
application:
name: thingsboard-client-demo
logging:
level:
root: DEBUG
com.tuoheng: DEBUG
org.springframework: ERROR
org.springframework.web: ERROR
java.lang: ERROR
reactor.util: INFO

View File

@ -1,18 +1,13 @@
package com.tuoheng.machine;
import com.tuoheng.machine.platform.PlatformType;
import com.tuoheng.machine.platform.factory.PlatformStrategyFactory;
import com.tuoheng.machine.repository.MachinePlatTypeRepository;
import com.tuoheng.machine.events.DrcEvent;
import com.tuoheng.machine.service.DrcMachineService;
import com.tuoheng.machine.status.DrcState;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import org.springframework.statemachine.StateMachine;
/**
* DRC状态机测试
@ -22,31 +17,65 @@ import static org.mockito.Mockito.when;
@Slf4j
public class DrcStateMachineTest {
@Autowired
private DrcMachineService drcMachineService;
@Autowired
private PlatformStrategyFactory platformStrategyFactory;
DrcMachineService drcMachineService;
@MockBean
private MachinePlatTypeRepository machinePlatTypeRepository;
@Test
public void testDrcMachineService(){
private static final String TEST_AIRPORT_SN = "test-airport-001";
String sn = "airport-001";
/**
* 不存在的会报错,需要在 MachinePlatTypeRepository 里面定义有这个机场编号
*/
try {
StateMachine<DrcState, DrcEvent> stateMachine =
drcMachineService.getOrCreateStateMachine("airport-001--2");
}catch (RuntimeException runtimeException){}
StateMachine<DrcState, DrcEvent> stateMachine = drcMachineService.getStateMachine("airport-001");
drcMachineService.getOrCreateStateMachine("airport-001");
stateMachine = drcMachineService.getStateMachine("airport-001");
/**
* 测试 Spring Boot 依赖注入是否正常工作
* 打印一下当前的状态
*/
@Test
public void testAutowiredInjection() {
log.debug(drcMachineService.getCurrentStates("airport-001"));
// 验证 PlatformStrategyFactory 是否成功注入
assertNotNull(platformStrategyFactory, "PlatformStrategyFactory 应该被成功注入");
// 验证 DrcMachineService 是否成功注入
assertNotNull(drcMachineService, "DrcMachineService 应该被成功注入");
// 验证 AirportPlatformRepository Mock 是否成功
assertNotNull(machinePlatTypeRepository, "AirportPlatformRepository Mock 应该被成功创建");
/**
* 从UnKnown状态可以走到任意状态
*/
drcMachineService.sendEvent(sn,DrcEvent.ENTERED);
/**
* ENTERED无法进入ENTERING DrcMachineConfig 里面配置的
*/
log.debug(String.valueOf(drcMachineService.sendEvent(sn,DrcEvent.ENTERING)));
/**
* 现在是 ENTERED,但是你还想进入ENTERED ,这个是不可以的
*/
log.debug(String.valueOf(drcMachineService.sendEvent(sn,DrcEvent.ENTERED)));
/**
* 打印一下当前的状态
*/
log.debug(drcMachineService.getCurrentStates("airport-001"));
/**
* 变成退出中;这个时候需要在 DjiCanExitGuard 中编写退出的代码调用三方接口
* DjiExitAction 里面判断状态是否真的变化了
*/
log.debug(String.valueOf(drcMachineService.sendEvent(sn,DrcEvent.EXITING)));
/**
* 打印一下当前的状态
*/
log.debug(drcMachineService.getCurrentStates("airport-001"));
// log.debug(String.valueOf(drcMachineService.sendEvent(sn,DrcEvent.EXITED)));
//
// log.debug(drcMachineService.getCurrentStates("airport-001"));
}
}