【实训预览操作】

This commit is contained in:
weizhihong 2022-08-18 16:03:43 +08:00
parent 51d1a2f665
commit 8243193972
7 changed files with 441 additions and 22 deletions

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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 {