【实训预览操作】
This commit is contained in:
parent
51d1a2f665
commit
8243193972
@ -61,4 +61,18 @@ public class TaskExecutorConfiguration {
|
||||
return taskExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新实训监控
|
||||
*/
|
||||
@Bean("trainingV2Executor")
|
||||
public TaskExecutor trainExecutor(Environment env) {
|
||||
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
|
||||
taskExecutor.setThreadNamePrefix("ns-training-v2-executor-");
|
||||
taskExecutor.setCorePoolSize(2);
|
||||
taskExecutor.setMaxPoolSize(2);
|
||||
taskExecutor.setQueueCapacity(100);
|
||||
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
taskExecutor.initialize();
|
||||
return taskExecutor;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
package club.joylink.rtss.controller.simulation;
|
||||
|
||||
import club.joylink.rtss.controller.advice.AuthenticateInterceptor;
|
||||
import club.joylink.rtss.services.training2.Training2Service;
|
||||
import club.joylink.rtss.vo.AccountVO;
|
||||
import club.joylink.rtss.vo.LoginUserInfoVO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/training2Simulation")
|
||||
public class SimulationTrainingV2Controller {
|
||||
|
||||
@Autowired
|
||||
private Training2Service training2Service;
|
||||
|
||||
/**
|
||||
* 预览实训信息
|
||||
*/
|
||||
@GetMapping("/{trainingId}/preview/draft")
|
||||
public String previewDraft(@PathVariable Long trainingId
|
||||
, @RequestAttribute(name = AuthenticateInterceptor.LOGIN_INFO_KEY) LoginUserInfoVO loginUserInfoVO) {
|
||||
return training2Service.previewDraft(trainingId, loginUserInfoVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览实训信息
|
||||
*/
|
||||
@PutMapping("/{group}/start")
|
||||
public void startTraining2(@PathVariable String group, @RequestAttribute AccountVO user) {
|
||||
training2Service.startTraining2(group, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束剧本演出
|
||||
*/
|
||||
@GetMapping("/{group}/finish")
|
||||
public Integer finishScript(@PathVariable String group) {
|
||||
return 1;
|
||||
}
|
||||
}
|
@ -1,30 +1,107 @@
|
||||
package club.joylink.rtss.services.training2;
|
||||
|
||||
import club.joylink.rtss.dao.DraftTraining2DAO;
|
||||
import club.joylink.rtss.dao.PublishedTraining2DAO;
|
||||
import club.joylink.rtss.entity.training2.DraftTraining2WithBLOBs;
|
||||
import club.joylink.rtss.simulation.cbtc.ATS.operation.AtsOperationDispatcher;
|
||||
import club.joylink.rtss.simulation.cbtc.GroupSimulationCache;
|
||||
import club.joylink.rtss.simulation.cbtc.GroupSimulationService;
|
||||
import club.joylink.rtss.simulation.cbtc.Simulation;
|
||||
import club.joylink.rtss.simulation.cbtc.SimulationLifeCycleService;
|
||||
import club.joylink.rtss.simulation.cbtc.event.SimulationOperationEvent;
|
||||
import club.joylink.rtss.simulation.cbtc.exception.SimulationException;
|
||||
import club.joylink.rtss.simulation.cbtc.exception.SimulationExceptionType;
|
||||
import club.joylink.rtss.simulation.cbtc.member.MemberManager;
|
||||
import club.joylink.rtss.simulation.cbtc.training2.Operation2;
|
||||
import club.joylink.rtss.simulation.cbtc.training2.Step2;
|
||||
import club.joylink.rtss.simulation.cbtc.training2.Training2;
|
||||
import club.joylink.rtss.vo.AccountVO;
|
||||
import club.joylink.rtss.vo.LoginUserInfoVO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Service
|
||||
public class Training2Service {
|
||||
public static final String JOB_NAME = "Training2";
|
||||
public static final int RATE = 1000;
|
||||
|
||||
@Autowired
|
||||
private AtsOperationDispatcher atsOperationDispatcher;
|
||||
|
||||
@Autowired
|
||||
private PublishedTraining2DAO training2DAO;
|
||||
|
||||
@Autowired
|
||||
private DraftTraining2DAO trainingDao;
|
||||
|
||||
@Autowired
|
||||
private MemberManager memberManager;
|
||||
|
||||
@Autowired
|
||||
private GroupSimulationService groupSimulationService;
|
||||
|
||||
@Autowired
|
||||
private GroupSimulationCache groupSimulationCache;
|
||||
|
||||
@Autowired
|
||||
private SimulationLifeCycleService simulationLifeCycleService;
|
||||
|
||||
public void run(Simulation simulation) {
|
||||
Training2 training2 = new Training2(null, null); //从仿真里拿到Training2
|
||||
for (Step2 step : training2.getSteps()) {
|
||||
if (!step.isCompletion()) {
|
||||
if (!step.isTrigger()) {
|
||||
if (step.getTriggerCondition().getValue(boolean.class)) {
|
||||
step.setTrigger(true);
|
||||
//前端提示
|
||||
}
|
||||
}
|
||||
if (step.getCompletionCondition().getValue(boolean.class)) {
|
||||
step.setCompletion(true);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
Training2 training2 = simulation.getTraining2();
|
||||
if (training2 == null || !training2.isStarted() || training2.isFinish()) {
|
||||
return;
|
||||
}
|
||||
// 获取运行步骤
|
||||
Step2 step = training2.getSteps().stream()
|
||||
.filter(s -> !s.isCompletion()).findFirst().orElseGet(null);
|
||||
if (step == null) { // 步骤已经运行完毕
|
||||
training2.finish();
|
||||
return;
|
||||
}
|
||||
// 步骤触发
|
||||
if (!step.checkTrigger()) {
|
||||
return;
|
||||
}
|
||||
// 获取步骤中未完成的操作
|
||||
Operation2 operation2 = step.getOperations().stream()
|
||||
.filter(o -> !o.isCompletion()).findFirst().orElseGet(null);
|
||||
if (operation2 == null) { // 未完成操作为空,检查步骤是否全部完成
|
||||
step.checkCompletion();
|
||||
return;
|
||||
}
|
||||
// 操作是否已触发
|
||||
if (!operation2.checkTrigger()) {
|
||||
return;
|
||||
}
|
||||
// 检验结果存在延迟,如果已操作过,200ms内重复检验
|
||||
if (operation2.isOperated()
|
||||
&& !operation2.getOperatedTime().plus(200, ChronoUnit.MILLIS).isBefore(LocalDateTime.now())) {
|
||||
operation2.checkCompletion();
|
||||
return;
|
||||
}
|
||||
if (!step.getSimulationMember().isRobot()) { // 是否是机器人
|
||||
// 发送前端提示信息
|
||||
if (!step.isPrompt()) {
|
||||
step.setPrompt(true); // 标识已发送过消息
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 机器人执行操作
|
||||
if (operation2 instanceof Operation2.SimOperation2) {
|
||||
Operation2.SimOperation2 simOperation2 = (Operation2.SimOperation2) operation2;
|
||||
atsOperationDispatcher.execute(simulation, step.getSimulationMember(),
|
||||
simOperation2.getOperationType().name(), simOperation2.getParams());
|
||||
} else {
|
||||
operation2.doOperated();
|
||||
operation2.checkCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,4 +110,92 @@ public class Training2Service {
|
||||
Training2 training2 = new Training2(draftTraining2, simulation);
|
||||
simulation.addJobIfAbsent(JOB_NAME, () -> this.run(simulation), RATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览实训草稿
|
||||
*/
|
||||
public String previewDraft(Long trainingId, LoginUserInfoVO loginUserInfoVO) {
|
||||
DraftTraining2WithBLOBs draftTraining2 = trainingDao.selectByPrimaryKey(trainingId);
|
||||
if (draftTraining2 == null) {
|
||||
throw new SimulationException(SimulationExceptionType.Data_Not_Exist, "实训不存在");
|
||||
}
|
||||
if (!StringUtils.hasText(draftTraining2.getBgSceneJson())) {
|
||||
throw new SimulationException(SimulationExceptionType.Invalid_Operation, String.format("实训{id:[%s]}没有背景", trainingId));
|
||||
}
|
||||
Simulation simulation = createSimulation(draftTraining2.getMapId(), loginUserInfoVO
|
||||
, Simulation.FunctionalType.SCRIPT_PREVIEW, (s -> new Training2(draftTraining2, s)));
|
||||
return simulation.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启实训
|
||||
*/
|
||||
public void startTraining2(String group, AccountVO user) {
|
||||
Simulation simulation = groupSimulationCache.getSimulationByGroup(group);
|
||||
if (simulation == null) {
|
||||
throw new SimulationException(SimulationExceptionType.Invalid_Operation, "仿真不存在");
|
||||
}
|
||||
if (!simulation.getCreator().getId().equals(user.getId())) {
|
||||
throw new SimulationException(SimulationExceptionType.Invalid_Operation, "无权限开启实训");
|
||||
}
|
||||
Training2 training2 = simulation.getTraining2();
|
||||
if (training2 == null) {
|
||||
throw new SimulationException(SimulationExceptionType.Invalid_Operation, "实训数据不存在");
|
||||
}
|
||||
if (training2.isNeedReloadScenes()) {
|
||||
groupSimulationService.loadScenes(simulation.getId(), training2.getBgSceneJson());
|
||||
}
|
||||
training2.start();
|
||||
simulationLifeCycleService.resume(simulation);
|
||||
}
|
||||
|
||||
@Async("trainingV2Executor")
|
||||
@EventListener
|
||||
public void handle(SimulationOperationEvent event) {
|
||||
Simulation simulation = event.getSimulation();
|
||||
Training2 training2 = simulation.getTraining2();
|
||||
if (training2 != null && Objects.equals(event.getSuccessful(), true)) {
|
||||
// 获取运行步骤
|
||||
Step2 step = training2.getSteps().stream().filter(s -> s.isTrigger() && !s.isCompletion())
|
||||
.findFirst().orElseGet(null);
|
||||
// 没找到返回
|
||||
if (step == null) {
|
||||
return;
|
||||
}
|
||||
// 角色不对
|
||||
if (!event.getMember().equals(step.getSimulationMember())) {
|
||||
return;
|
||||
}
|
||||
// 获取步骤中未完成的操作
|
||||
Operation2 operation2 = step.getOperations().stream().filter(o -> o.isTrigger() && !o.isCompletion())
|
||||
.findFirst().orElseGet(null);
|
||||
if (operation2 == null) {
|
||||
return;
|
||||
}
|
||||
Operation2.SimOperation2 simOperation2 = (Operation2.SimOperation2) operation2;
|
||||
if (simOperation2.getOperationType().name().equals(event.getOperate())) {
|
||||
operation2.doOperated(); // 标识已操作过
|
||||
operation2.checkCompletion(); // 检查是否已操作
|
||||
} else {
|
||||
// 错误操作数增加
|
||||
operation2.getCount().incrementAndGet();
|
||||
// 错误提示
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实训时创建仿真对象
|
||||
*/
|
||||
private Simulation createSimulation(Long mapId, LoginUserInfoVO loginUserInfoVO
|
||||
, Simulation.FunctionalType functionalType, Function<Simulation, Training2> function) {
|
||||
// 构建仿真,加载背景,
|
||||
Simulation simulation = groupSimulationService.create(loginUserInfoVO, mapId, null, functionalType);
|
||||
Training2 training2 = function.apply(simulation);
|
||||
groupSimulationService.loadScenes(simulation.getId(), training2.getBgSceneJson());
|
||||
simulationLifeCycleService.pause(simulation);
|
||||
simulation.setTraining2(training2);
|
||||
simulation.addJobIfAbsent(JOB_NAME, () -> this.run(simulation), RATE);
|
||||
return simulation;
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import club.joylink.rtss.simulation.cbtc.exception.SimulationExceptionType;
|
||||
import club.joylink.rtss.simulation.cbtc.member.SimulationMember;
|
||||
import club.joylink.rtss.simulation.cbtc.member.SimulationUser;
|
||||
import club.joylink.rtss.simulation.cbtc.script.ScriptBO;
|
||||
import club.joylink.rtss.simulation.cbtc.training2.Training2;
|
||||
import club.joylink.rtss.simulation.vo.SimulationInfoVO;
|
||||
import club.joylink.rtss.vo.AccountVO;
|
||||
import club.joylink.rtss.vo.client.fault.FaultRuleVO;
|
||||
@ -87,6 +88,11 @@ public class Simulation extends club.joylink.rtss.simulation.Simulation<Simulati
|
||||
*/
|
||||
private ScriptBO script;
|
||||
|
||||
/**
|
||||
* 实训信息
|
||||
*/
|
||||
private Training2 training2;
|
||||
|
||||
// /**
|
||||
// * 仿真成员map
|
||||
// * key-仿真成员id
|
||||
|
@ -3,7 +3,9 @@ package club.joylink.rtss.simulation.cbtc.training2;
|
||||
import club.joylink.rtss.simulation.cbtc.ATS.operation.Operation;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 实训操作
|
||||
@ -14,6 +16,41 @@ public abstract class Operation2 {
|
||||
|
||||
private Valuable completionCondition;
|
||||
|
||||
/**
|
||||
* 触发
|
||||
*/
|
||||
boolean trigger;
|
||||
|
||||
/**
|
||||
* 完成
|
||||
*/
|
||||
boolean completion;
|
||||
|
||||
/**
|
||||
* 用户是否已点击过
|
||||
*/
|
||||
boolean operated;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
LocalDateTime startTime;
|
||||
|
||||
/**
|
||||
* 操作时间
|
||||
*/
|
||||
LocalDateTime operatedTime;
|
||||
|
||||
/**
|
||||
* 用时
|
||||
*/
|
||||
Integer remainTime;
|
||||
|
||||
/**
|
||||
* 操作次数
|
||||
*/
|
||||
AtomicInteger count = new AtomicInteger(0);
|
||||
|
||||
public Operation2() {
|
||||
}
|
||||
|
||||
@ -22,6 +59,48 @@ public abstract class Operation2 {
|
||||
this.completionCondition = completionCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查触发状态
|
||||
*/
|
||||
public boolean checkTrigger() {
|
||||
if (!this.trigger) {
|
||||
boolean result = this.triggerCondition == null;
|
||||
if (!result) {
|
||||
result = this.triggerCondition.getValue(boolean.class);
|
||||
}
|
||||
this.trigger = result;
|
||||
if (this.trigger) {
|
||||
this.startTime = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
return this.trigger;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查完成状态
|
||||
*/
|
||||
public boolean checkCompletion() {
|
||||
if (this.operated && !this.completion) {
|
||||
boolean result = this.completionCondition == null;
|
||||
if (!result) {
|
||||
result = this.completionCondition.getValue(boolean.class);
|
||||
}
|
||||
this.completion = result;
|
||||
if (this.completion) {
|
||||
this.remainTime = this.operatedTime.getNano() - this.startTime.getNano();
|
||||
}
|
||||
}
|
||||
return this.completion;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户操作动作
|
||||
*/
|
||||
public void doOperated() {
|
||||
this.operated = true;
|
||||
this.operatedTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class SimOperation2 extends Operation2 {
|
||||
private club.joylink.rtss.simulation.cbtc.ATS.operation.Operation.Type operationType;
|
||||
|
@ -1,10 +1,13 @@
|
||||
package club.joylink.rtss.simulation.cbtc.training2;
|
||||
|
||||
import club.joylink.rtss.simulation.cbtc.data.SimulationDataRepository;
|
||||
import club.joylink.rtss.simulation.cbtc.Simulation;
|
||||
import club.joylink.rtss.simulation.cbtc.member.SimulationMember;
|
||||
import club.joylink.rtss.vo.client.training2.Step2VO;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@ -12,6 +15,8 @@ import java.util.List;
|
||||
public class Step2 {
|
||||
private Integer id;
|
||||
|
||||
private SimulationMember simulationMember;
|
||||
|
||||
private String description;
|
||||
|
||||
List<Operation2> operations;
|
||||
@ -22,20 +27,99 @@ public class Step2 {
|
||||
|
||||
private Valuable failureCondition;
|
||||
|
||||
/* -------------------------------------- 运行时参数 -------------------------------------- */
|
||||
/**
|
||||
* 触发
|
||||
*/
|
||||
private boolean trigger;
|
||||
|
||||
/**
|
||||
* 完成
|
||||
*/
|
||||
private boolean completion;
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
private boolean failure;
|
||||
|
||||
/**
|
||||
* 消息提示
|
||||
*/
|
||||
boolean prompt;
|
||||
|
||||
/**
|
||||
* 剧本演出开始时间
|
||||
*/
|
||||
private LocalDateTime startTime;
|
||||
|
||||
/**
|
||||
* 用时
|
||||
*/
|
||||
private long remainTime;
|
||||
|
||||
/**
|
||||
* 操作次数
|
||||
*/
|
||||
private int count;
|
||||
|
||||
public Step2(Step2VO vo) {
|
||||
this.id = vo.getId();
|
||||
this.description = vo.getDescription();
|
||||
}
|
||||
|
||||
public Step2(Step2VO vo, SimulationDataRepository repository) {
|
||||
public Step2(Step2VO vo, Simulation simulation) {
|
||||
this(vo);
|
||||
Valuable triggerCondition = vo.getTriggerCondition().convert2BO(repository);
|
||||
Valuable completionCondition = vo.getCompletionCondition().convert2BO(repository);
|
||||
Valuable triggerCondition = vo.getTriggerCondition().convert2BO(simulation.getRepository());
|
||||
Valuable completionCondition = vo.getCompletionCondition().convert2BO(simulation.getRepository());
|
||||
if (!StringUtils.isEmpty(vo.getMemberId())) {
|
||||
this.simulationMember = simulation.getSimulationMemberById(vo.getMemberId());
|
||||
}
|
||||
this.triggerCondition = triggerCondition;
|
||||
this.completionCondition = completionCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断设备是否准备就绪
|
||||
*/
|
||||
public boolean checkTrigger() {
|
||||
if (!this.trigger) {
|
||||
boolean result = this.triggerCondition == null;
|
||||
if (!result) {
|
||||
result = this.triggerCondition.getValue(boolean.class);
|
||||
}
|
||||
this.trigger = result;
|
||||
if (this.trigger) { // 触发时记录开始时间
|
||||
this.startTime = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
return this.trigger;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查完成状态
|
||||
*/
|
||||
public boolean checkCompletion() {
|
||||
if (!this.completion) {
|
||||
// 操作是否都已完成
|
||||
boolean result = this.operations.stream().allMatch(Operation2::isCompletion);
|
||||
if (result) {
|
||||
result = this.completionCondition == null;
|
||||
if (!result) {
|
||||
result = this.completionCondition.getValue(boolean.class);
|
||||
}
|
||||
this.completion = result;
|
||||
}
|
||||
// 步骤完成后检查是否失败
|
||||
if (result) {
|
||||
// 整步骤完成耗费时间
|
||||
this.remainTime = LocalDateTime.now().getNano() - this.startTime.getNano();
|
||||
// 操作错误总数
|
||||
this.count = this.operations.stream().mapToInt(o -> o.getCount().get()).sum();
|
||||
// 判断是否失败
|
||||
this.failure = this.failureCondition != null && this.failureCondition.getValue(boolean.class);
|
||||
}
|
||||
}
|
||||
return this.completion;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package club.joylink.rtss.simulation.cbtc.training2;
|
||||
|
||||
import club.joylink.rtss.entity.training2.DraftTraining2;
|
||||
import club.joylink.rtss.entity.training2.DraftTraining2WithBLOBs;
|
||||
import club.joylink.rtss.simulation.cbtc.Simulation;
|
||||
import club.joylink.rtss.util.JsonUtils;
|
||||
@ -52,12 +51,43 @@ public class Training2 {
|
||||
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/* -------------------------------------- 运行时参数 -------------------------------------- */
|
||||
|
||||
/**
|
||||
* 剧本背景是否需要重新加载(重新开始剧本时)
|
||||
*/
|
||||
private boolean needReloadScenes;
|
||||
|
||||
/**
|
||||
* 剧本是否已经开始演出
|
||||
*/
|
||||
private boolean started;
|
||||
|
||||
/**
|
||||
* 剧本是否已经完成
|
||||
*/
|
||||
private boolean finish;
|
||||
|
||||
/**
|
||||
* 剧本演出开始时间(现实时间)
|
||||
*/
|
||||
private LocalDateTime startTime;
|
||||
|
||||
public Training2(DraftTraining2WithBLOBs draftTraining2, Simulation simulation) {
|
||||
labels = JsonUtils.readCollection(draftTraining2.getLabelJson(), List.class, String.class);
|
||||
List<Step2VO> step2VOS = JsonUtils.readCollection(draftTraining2.getStepJson(), List.class, Step2VO.class);
|
||||
steps = step2VOS.stream()
|
||||
.map(vo -> new Step2(vo, simulation.getRepository()))
|
||||
.collect(Collectors.toList());
|
||||
steps = step2VOS.stream().map(vo -> new Step2(vo, simulation)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void start() {
|
||||
this.startTime = LocalDateTime.now();
|
||||
this.needReloadScenes = true;
|
||||
this.started = true;
|
||||
}
|
||||
|
||||
public boolean finish() {
|
||||
this.finish = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
|
Loading…
Reference in New Issue
Block a user