实训场景编制时,将控制权转换相关消息暂存,后续再发送;增加[车站站长]和[站务员]角色

This commit is contained in:
joylink_zhangsai 2023-09-27 15:47:05 +08:00
parent d7b42ef478
commit e2cd83d298
12 changed files with 164 additions and 88 deletions

View File

@ -612,4 +612,9 @@ public class SimulationV1Controller {
public SimulationVO querySimulationJoinedByMe(@RequestAttribute LoginUserInfoVO loginInfo) {
return simulationService.querySimulationJoinedByUser(loginInfo.getAccountVO().getId());
}
@GetMapping("/{simulationId}/{memberId}/unreceivedMessages")
public void sendUnreceivedMessages(@PathVariable String simulationId, @PathVariable String memberId) {
simulationService.sendUnreceivedMessages(simulationId, memberId);
}
}

View File

@ -248,15 +248,22 @@ public class ATSMessageCollectAndDispatcher {
* 仿真设备操作消息
*/
public void handlerOperateMessage(Simulation simulation, OperationMessage operationMessage) {
SocketMessageVO<OperationMessage> socketMessageVO =
SocketMessageFactory.build(WebSocketMessageType.Simulation_ApplyHandle, simulation.getId(), operationMessage);
Set<SimulationMember> humanMembers = operationMessage.getTargetMembers().stream().filter(m -> !m.isRobot()).collect(Collectors.toSet());
//当在场景实训设计中并且无真人成员时将数据暂存等待后续角色有人扮演时再发送
if (simulation.isInSceneTraining() && CollectionUtils.isEmpty(humanMembers)) {
for (SimulationMember targetMember : operationMessage.getTargetMembers()) {
targetMember.getUnreceivedMessages().offer(socketMessageVO);
}
return;
}
if (CollectionUtils.isEmpty(humanMembers)) {
//目标都是机器人选择一个机器人发送事件处理
applicationContext.publishEvent(new SimulationOperationMessageEvent(this, simulation, operationMessage));
return;
}
//目标成员有真人推送对应真人处理
SocketMessageVO<OperationMessage> socketMessageVO =
SocketMessageFactory.build(WebSocketMessageType.Simulation_ApplyHandle, simulation.getId(), operationMessage);
stompMessageService.sendToUser(humanMembers.stream().map(SimulationMember::getUserId).collect(Collectors.toSet()), socketMessageVO);
}

View File

@ -16,6 +16,7 @@ import club.joylink.rtss.simulation.cbtc.data.vo.ControlTransferReplyVO;
import club.joylink.rtss.simulation.cbtc.exception.SimulationException;
import club.joylink.rtss.simulation.cbtc.exception.SimulationExceptionType;
import club.joylink.rtss.simulation.cbtc.member.SimulationMember;
import club.joylink.rtss.simulation.cbtc.vo.ControlModelApplyResult;
import club.joylink.rtss.vo.client.SocketMessageVO;
import club.joylink.rtss.vo.client.WebSocketMessageType;
import club.joylink.rtss.vo.client.factory.SocketMessageFactory;
@ -27,7 +28,6 @@ import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;
@ -110,7 +110,7 @@ public class AtsStationService {
*/
private void controlTransfer(Simulation simulation, SimulationMember fromMember, List<String> stationCodes, Station.ControlMode controlMode) {
if (CollectionUtils.isEmpty(stationCodes)) {
throw new SimulationException(SimulationExceptionType.Operation_Handle_FAIL, "请求控的车站列表不能为空");
throw new SimulationException(SimulationExceptionType.Operation_Handle_FAIL, "请求制权转换的车站列表不能为空");
}
for (String stationCode : stationCodes) {
Station station = simulation.getRepository().getByCode(stationCode, Station.class);
@ -126,12 +126,15 @@ public class AtsStationService {
if (CollectionUtils.isEmpty(stationCodes)) {
throw new SimulationException(SimulationExceptionType.Operation_Handle_FAIL, "强制站控的车站不能为空");
}
List<ControlModelApplyResult> results = new ArrayList<>();
stationCodes.forEach(stationCode -> {
Station station = getStation(simulation, stationCode);
station.setControlMode(Station.ControlMode.Local);
results.add(new ControlModelApplyResult(stationCode, ControlModelApplyResult.Application.FORCE_LOCAL, true));
});
atsMessageCollectAndDispatcher.handlerOperateMessage(simulation, new OperationMessage(fromMember.getId(), new HashSet<>(Collections.singleton(fromMember)), Operation.Type.CM_Reply_Station_Control, null, true, stationCodes));
sendControlModelApplyResult(simulation, fromMember, results);
atsMessageCollectAndDispatcher.handlerOperateMessage(simulation, new OperationMessage(fromMember.getId(),
new HashSet<>(Collections.singleton(fromMember)), Operation.Type.CM_Reply_Station_Control, null, true, stationCodes));
}
@ -144,12 +147,15 @@ public class AtsStationService {
throw new SimulationException(SimulationExceptionType.Operation_Handle_FAIL, "紧急站控的车站不能为空");
}
List<ControlModelApplyResult> results = new ArrayList<>();
stationCodes.forEach(stationCode -> {
Station station = getStation(simulation, stationCode);
station.setControlMode(Station.ControlMode.Emergency);
results.add(new ControlModelApplyResult(stationCode, ControlModelApplyResult.Application.EMERGENCY_LOCAL, true));
});
atsMessageCollectAndDispatcher.handlerOperateMessage(simulation, new OperationMessage(fromMember.getId(), new HashSet<>(Collections.singleton(fromMember)), Operation.Type.CM_Reply_Station_Control, null, true, stationCodes));
sendControlModelApplyResult(simulation, fromMember, results);
atsMessageCollectAndDispatcher.handlerOperateMessage(simulation, new OperationMessage(fromMember.getId(),
new HashSet<>(Collections.singleton(fromMember)), Operation.Type.CM_Reply_Station_Control, null, true, stationCodes));
}
/**
@ -159,12 +165,15 @@ public class AtsStationService {
if (CollectionUtils.isEmpty(stationCodes)) {
throw new SimulationException(SimulationExceptionType.Operation_Handle_FAIL, "连锁控的车站不能为空");
}
List<ControlModelApplyResult> results = new ArrayList<>();
stationCodes.forEach(stationCode -> {
Station station = getStation(simulation, stationCode);
station.setControlMode(Station.ControlMode.Interlock);
results.add(new ControlModelApplyResult(stationCode, ControlModelApplyResult.Application.INTERLOCK, true));
});
atsMessageCollectAndDispatcher.handlerOperateMessage(simulation, new OperationMessage(fromMember.getId(), new HashSet<>(Collections.singleton(fromMember)), Operation.Type.CM_Reply_Interlock_Control, null, true, stationCodes));
sendControlModelApplyResult(simulation, fromMember, results);
atsMessageCollectAndDispatcher.handlerOperateMessage(simulation, new OperationMessage(fromMember.getId(),
new HashSet<>(Collections.singleton(fromMember)), Operation.Type.CM_Reply_Interlock_Control, null, true, stationCodes));
}
/**
@ -189,25 +198,34 @@ public class AtsStationService {
throw new SimulationException(SimulationExceptionType.Invalid_Operation, "回复结果列表不能为空");
}
List<String> stationCodes = new ArrayList<>(replyVOList.size());
SimulationMember simulationMember = null;
SimulationMember applicant = null;
boolean replyResult = false;
List<ControlModelApplyResult> results = new ArrayList<>();
for (ControlTransferReplyVO reply : replyVOList) {
Station station = simulation.getRepository().getByCode(reply.getStationCode(), Station.class);
if (station.isControlTransferApplying()) {
if (Station.ControlMode.Center.equals(controlMode)) {
results.add(new ControlModelApplyResult(station.getCode(), ControlModelApplyResult.Application.CENTER, reply.isAgree()));
} else {
results.add(new ControlModelApplyResult(station.getCode(), ControlModelApplyResult.Application.LOCAL, reply.isAgree()));
}
if (reply.isAgree()) {
station.setControlMode(controlMode);
}
if (!Station.ControlMode.Center.equals(controlMode)) {
simulationMember = station.getApplicant();
applicant = station.getApplicant();
replyResult = reply.isAgree();
stationCodes.add(station.getCode());
}
station.cancelControlTransferApplication();
}
}
if (applicant != null) {
sendControlModelApplyResult(simulation, applicant, results);
}
if (!CollectionUtils.isEmpty(stationCodes)) {
atsMessageCollectAndDispatcher.handlerOperateMessage(simulation,
new OperationMessage(simulationMember.getId(), new HashSet<>(Collections.singleton(simulationMember)),
new OperationMessage(applicant.getId(), new HashSet<>(Collections.singleton(applicant)),
Operation.Type.CM_Reply_Station_Control, null, replyResult, stationCodes));
}
}
@ -574,6 +592,7 @@ public class AtsStationService {
/**
* 车站状态选择切换
*
* @param simulation 仿真实体
* @param routeSetModeParams 转换参数
*/
@ -601,6 +620,7 @@ public class AtsStationService {
/**
* 车站在分散自律时操作模式转换
*
* @param simulation 仿真
* @param fromMember 操作角色
* @param params 转换参数
@ -664,6 +684,7 @@ public class AtsStationService {
/**
* 发送申请转换操作模式申请
*
* @param simulation
*/
public void replyOperationMode(Simulation simulation) {
@ -720,4 +741,18 @@ public class AtsStationService {
station.setOperationModeApplication(null);
}
}
/**
* 发送控制模式申请结果
*/
private void sendControlModelApplyResult(Simulation sim, SimulationMember applier, List<ControlModelApplyResult> results) {
SocketMessageVO<List<ControlModelApplyResult>> message = SocketMessageFactory.buildControlModelApplyResultMessage(sim.getId(), results);
if (!applier.isRobot()) {
stompMessageService.sendToUser(applier.getUserId(), message);
return;
}
if (sim.isInSceneTraining() && applier.isRobot()) {
applier.getUnreceivedMessages().offer(message);
}
}
}

View File

@ -40,6 +40,7 @@ import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.time.LocalDate;
import java.time.LocalDateTime;
@ -812,6 +813,11 @@ public class Simulation extends club.joylink.rtss.simulation.Simulation<Simulati
return mapFunctionVO == null ? null : mapFunctionVO.getId();
}
public boolean isInSceneTraining() {
return training2 != null && training2.isScene() && StringUtils.hasText(training2.getBgSceneJson())
&& !training2.isRunning();
}
public interface JobName {
String script = "Script";
String checkLpf = "checkLpf";

View File

@ -83,4 +83,6 @@ public interface SimulationService {
List<SimulationInfoVO> listSimulationByOrg(Long topOrgId, SimulationInfoQueryVO queryVO);
PageVO<SimulationInfoVO> pagedSimulationByOrg(Long topOrgId, SimulationInfoQueryVO queryVO);
void sendUnreceivedMessages(String simulationId, String memberId);
}

View File

@ -12,6 +12,7 @@ import club.joylink.rtss.simulation.cbtc.discriminate.VoiceDiscriminateRule;
import club.joylink.rtss.simulation.cbtc.event.SimulationCreateSuccessEvent;
import club.joylink.rtss.simulation.cbtc.exception.SimulationException;
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.vo.SimulationWorkParamVO;
import club.joylink.rtss.simulation.cbtc.work.SimulationWorkService;
@ -422,4 +423,17 @@ public class SimulationServiceImpl implements SimulationService {
List<SimulationInfoVO> collect = stream.skip(skipNum).limit(queryVO.getPageSize()).collect(Collectors.toList());
return new PageVO<>(queryVO.getPageNum(), queryVO.getPageSize(), list.size(), collect);
}
@Override
public void sendUnreceivedMessages(String simulationId, String memberId) {
Simulation simulation = simulationManager.getById(simulationId, Simulation.class);
SimulationMember member = simulation.getSimulationMemberById(memberId);
while (true) {
SocketMessageVO<?> message = member.getUnreceivedMessages().poll();
if (message == null) {
break;
}
stompMessageService.sendToUser(member.getUserId(), message);
}
}
}

View File

@ -12,6 +12,7 @@ import club.joylink.rtss.simulation.cbtc.event.SimulationMemberAddEvent;
import club.joylink.rtss.simulation.cbtc.event.SimulationUserPlayChangeEvent;
import club.joylink.rtss.simulation.cbtc.exception.SimulationException;
import club.joylink.rtss.simulation.cbtc.exception.SimulationExceptionType;
import club.joylink.rtss.websocket.StompMessageService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
@ -32,6 +33,8 @@ public class MemberManager {
@Autowired
private SimulationManager simulationManager;
@Autowired
private StompMessageService stompMessageService;
/**
* 初始化仿真成员

View File

@ -4,51 +4,20 @@ import club.joylink.rtss.simulation.cbtc.command.CommandBO;
import club.joylink.rtss.simulation.cbtc.data.map.MapElement;
import club.joylink.rtss.simulation.cbtc.data.map.MapNamedElement;
import club.joylink.rtss.simulation.vo.SimulationMemberVO;
import club.joylink.rtss.vo.client.SocketMessageVO;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import javax.validation.constraints.NotNull;
import java.util.Objects;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* 仿真成员
*/
@Getter
public class SimulationMember extends club.joylink.rtss.simulation.SimulationMember<SimulationMember.Type> {
// {
// //地铁角色
// Type[] metroTypes = {
// Type.DISPATCHER,
// Type.MAINTAINER,
// Type.STATION_SUPERVISOR,
// Type.DEPOT_DISPATCHER,
// Type.DRIVER,
// Type.ELECTRIC_DISPATCHER,
// Type.PARENT_DEPARTMENT,
// Type.SCHEDULING,
// Type.TRAIN_MASTER,
// Type.SIGNAL_BUILDING
// };
// //大铁角色
// Type[] railTypes = {
// Type.MAINTAINER,
// Type.DRIVER,
// Type.DISPATCHER,
// Type.MAINTAINER,
// Type.STATION_SUPERVISOR,
// Type.STATION_ASSISTANT,
// Type.STATION_MASTER,
// Type.STATION_SIGNALER,
// Type.STATION_PASSENGER,
// Type.STATION_SWITCH_MAN,
// Type.STATION_FACILITATOR,
// Type.STATION_WORKER,
// Type.DEVICE_MANAGER
// };
// }
private String id;
private Type type;
@ -77,6 +46,8 @@ public class SimulationMember extends club.joylink.rtss.simulation.SimulationMem
*/
private Integer departureDelay = 0;
private ConcurrentLinkedQueue<SocketMessageVO<?>> unreceivedMessages = new ConcurrentLinkedQueue<>();
public SimulationMember(String id, Type type, MapElement device) {
this(id, type, device, null);
}
@ -186,7 +157,7 @@ public class SimulationMember extends club.joylink.rtss.simulation.SimulationMem
/**
* 中心调度员
*/
DISPATCHER(Area.OCC, "值班主任"),
DISPATCHER(Area.OCC, "中心调度员"),
/**
* 电力调度
*/
@ -203,6 +174,14 @@ public class SimulationMember extends club.joylink.rtss.simulation.SimulationMem
* 车站值班员
*/
STATION_SUPERVISOR(Area.STATION, "车站值班员"),
/**
* 值班站长
*/
STATION_HEAD(Area.STATION, "值班站长"),
/**
* 站务员
*/
STATION_ATTENDANT(Area.STATION, "站务员"),
/**
* 司机
*/

View File

@ -1,7 +1,5 @@
package club.joylink.rtss.simulation.cbtc.robot;
import static club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealityTrain.ConfirmationMessage.Confirm_Release_Speed;
import club.joylink.rtss.services.psl.IVirtualRealityPslService;
import club.joylink.rtss.simulation.cbtc.ATP.ground.MaService;
import club.joylink.rtss.simulation.cbtc.ATS.operation.AtsOperationDispatcher;
@ -17,40 +15,27 @@ import club.joylink.rtss.simulation.cbtc.constant.SimulationConstants;
import club.joylink.rtss.simulation.cbtc.constant.SimulationModule;
import club.joylink.rtss.simulation.cbtc.data.CalculateService;
import club.joylink.rtss.simulation.cbtc.data.SimulationDataRepository;
import club.joylink.rtss.simulation.cbtc.data.map.PSD;
import club.joylink.rtss.simulation.cbtc.data.map.Section;
import club.joylink.rtss.simulation.cbtc.data.map.Signal;
import club.joylink.rtss.simulation.cbtc.data.map.Stand;
import club.joylink.rtss.simulation.cbtc.data.map.Station;
import club.joylink.rtss.simulation.cbtc.data.map.*;
import club.joylink.rtss.simulation.cbtc.data.support.SectionPosition;
import club.joylink.rtss.simulation.cbtc.data.vo.ControlTransferReplyVO;
import club.joylink.rtss.simulation.cbtc.data.vo.TrainInfo;
import club.joylink.rtss.simulation.cbtc.data.vr.StandParkedTrainActivity;
import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealityPsl;
import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealityScreenDoor;
import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealitySignal;
import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealityTrain;
import club.joylink.rtss.simulation.cbtc.data.vr.*;
import club.joylink.rtss.simulation.cbtc.member.SimulationMember;
import club.joylink.rtss.simulation.cbtc.onboard.ATO.SpeedCurve;
import club.joylink.rtss.simulation.cbtc.onboard.ATO.service.ATOService;
import club.joylink.rtss.simulation.cbtc.onboard.ATP.ATPService;
import club.joylink.rtss.simulation.cbtc.training2.Training2;
import club.joylink.rtss.vo.client.operation.DriveParamVO;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.stream.Collectors;
import static club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealityTrain.ConfirmationMessage.Confirm_Release_Speed;
/**
* 仿真机器人逻辑循环
*/
@ -442,9 +427,7 @@ public class SimulationRobotService {
return;
}
// 实训场景模式已设置背景处于编制状态的实训不做自动转换处理
Training2 training2 = simulation.getTraining2();
if (training2 != null && training2.isScene() && StringUtils.hasText(training2.getBgSceneJson())
&& !training2.isRunning()) {
if (simulation.isInSceneTraining()) {
return;
}
Map<SimulationMember, List<Station>> collect = simulation.getRepository().getStationList()

View File

@ -0,0 +1,35 @@
package club.joylink.rtss.simulation.cbtc.vo;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 控制模式转换申请结果
*/
@Getter
@AllArgsConstructor
public class ControlModelApplyResult {
private String stationCode;
private Application application;
private Result result;
public ControlModelApplyResult(String stationCode, Application application, boolean agree) {
this.stationCode = stationCode;
this.application = application;
this.result = agree ? Result.AGREE : Result.REJECT;
}
public enum Application {
LOCAL,
CENTER,
FORCE_LOCAL,
EMERGENCY_LOCAL,
INTERLOCK,
}
public enum Result {
AGREE,
REJECT,
TIMEOUT,
}
}

View File

@ -138,10 +138,6 @@ public enum WebSocketMessageType {
* 仿真-请求处理消息
*/
Simulation_ApplyHandle,
/**
* 仿真-控制权转换申请回复
*/
Simulation_ControlTransfer_Reply,
/**
* 仿真-结束消息
*/
@ -176,6 +172,11 @@ public enum WebSocketMessageType {
*/
Simulation_Conversation_Group,
/**
* 控制模式转换申请的结果
*/
Simulation_Control_Mode_Apply_Result,
//------------------ 仿真剧本 ------------------
/**
* 仿真-剧本已加载

View File

@ -22,6 +22,7 @@ import club.joylink.rtss.simulation.cbtc.data.vo.TrainIsAbout2ArriveVO;
import club.joylink.rtss.simulation.cbtc.member.SimulationMember;
import club.joylink.rtss.simulation.cbtc.member.SimulationUser;
import club.joylink.rtss.simulation.cbtc.message.SimulationSubscribeTopic;
import club.joylink.rtss.simulation.cbtc.vo.ControlModelApplyResult;
import club.joylink.rtss.simulation.cbtc.vo.SimulationWorkParamVO;
import club.joylink.rtss.util.JsonUtils;
import club.joylink.rtss.vo.LoginUserInfoVO;
@ -109,7 +110,8 @@ public class SocketMessageFactory {
case SIMULATION_RAIL_TICKET:
case Simulation_Alarm:
case Simulation_Operation_Mode_Apply:
case Simulation_Conversation_Group: {
case Simulation_Conversation_Group:
case Simulation_Control_Mode_Apply_Result:{
topicList.add(SimulationSubscribeTopic.Main.buildDestination(group));
break;
}
@ -502,4 +504,8 @@ public class SocketMessageFactory {
return build(WebSocketMessageType.SIMULATION_NCC_STATION_DIAGRAM,sId,stationDiagramList);
}
public static SocketMessageVO<List<ControlModelApplyResult>> buildControlModelApplyResultMessage(String sId, List<ControlModelApplyResult> results) {
return build(WebSocketMessageType.Simulation_Control_Mode_Apply_Result, sId, results);
}
}