diff --git a/src/main/java/club/joylink/rtss/simulation/Simulation.java b/src/main/java/club/joylink/rtss/simulation/Simulation.java index a3a5bec3b..9f0ffc592 100644 --- a/src/main/java/club/joylink/rtss/simulation/Simulation.java +++ b/src/main/java/club/joylink/rtss/simulation/Simulation.java @@ -42,7 +42,7 @@ public abstract class Simulation { - log.info("logic"); - }, 500); - simulation.addFixedRateJob("c", () -> { - log.warn("fixed"); - }, 1000); - simulation.updateSpeed(5); - simulation.start(); - } private void runAsSpeed() { if (this.future == null) { -// if (!this.future.cancel(false)) { -// log.error(String.format("仿真旧主线程无法取消")); -// } ScheduledFuture scheduledFuture = EXECUTOR.scheduleAtFixedRate(()->this.logic(), this.SYSTEM_TIME_RATE, this.SYSTEM_TIME_RATE, TimeUnit.MILLISECONDS); this.future = scheduledFuture; @@ -119,7 +101,7 @@ public abstract class Simulation this.srTrainRun(rtSimulation.getRepository(SrdRepository.NAME, SrdRepository.class)), TRAIN_RUN_RATE); + rtSimulation.addJob("srDeviceLogic", + () -> this.srDeviceLogic(rtSimulation.getRepository(SrdRepository.NAME, SrdRepository.class), rtSimulation.getSystemTime()), + DEVICE_RUN_RATE); + } + + private void srDeviceLogic(SrdRepository repository, LocalDateTime systemTime) { + // 道岔转动 + List turnoutList = repository.getTurnoutList(); + for (SrTurnout turnout : turnoutList) { + if (turnout.isControlByReal()) { + continue; + } + if (turnout.isTurning()) { + turnout.tryFinishTurning(systemTime); + } + } + // 屏蔽门开/闭 + List psdList = repository.getPsdList(); + for (SrPSD psd : psdList) { + if (psd.isControlByReal()) { + continue; + } + if (psd.isTurning()) { + psd.tryFinishTurning(systemTime); + } + } + // 信号机控制 + List signalList = repository.getSignalList(); + for (SrSignal signal : signalList) { + if (signal.isControlByReal()) { + continue; + } + if (signal.isTurning()) { + signal.tryFinishTurning(systemTime); + } + } } public void srTrainRun(SrdRepository repository) { @@ -31,20 +71,35 @@ public class SrdService { if (!srTrain.isUsing()) { continue; } - TrackPosition position = srTrain.getHeadPosition(); + // 计算列车加速度,更新列车速度,计算列车运行距离,更新列车位置 boolean right = srTrain.isRight(); + TrackPosition position = srTrain.getHeadPosition(); int cv = this.calculateSpeed(srTrain); - int s = this.calculateLen(cv, TRAIN_RUN_RATE); - TrackPosition np = calculatePosition(position, s, right); + int s = this.calculateLen(srTrain, cv, TRAIN_RUN_RATE); + TrackPosition np = calculatePosition(position, s, right, new HashSet<>()); if (np.equals(position) && cv != 0) { cv = 0; } - srTrain.updatePositionAndSpeed(np, cv); + Set occupiedTrackSet = new HashSet<>(); + TrackPosition tailPosition = this.calculatePosition(np, srTrain.getLen(), !right, occupiedTrackSet); + srTrain.updatePositionAndSpeed(np, tailPosition, cv); + // 更新计轴占用 + Set oldOccupiedTrackSet = new HashSet<>(); + this.calculatePosition(position, srTrain.getLen(), !right, oldOccupiedTrackSet); + Set clearList = new HashSet<>(oldOccupiedTrackSet); + clearList.removeAll(occupiedTrackSet); + for (SrTrack srTrack : clearList) { + srTrack.getAxc().clear(); + } + for (SrTrack srTrack : occupiedTrackSet) { + srTrack.getAxc().occupy(); + } } } - private int calculateLen(int cv, int time) { - return cv * time; + private int calculateLen(SrTrain srTrain, int cv, int time) { + int s = cv * time; + return s * srTrain.getGear(); } private int calculateSpeed(SrTrain srTrain) { @@ -61,10 +116,21 @@ public class SrdService { if (cv < 0) { cv = 0; } + if (srTrain.isNeutralGear() && cv > 0) { // 空档位 + cv = 0; + } return cv; } - private TrackPosition calculatePosition(TrackPosition position, int s, boolean right) { + /** + * + * @param position 起始位置 + * @param s 距离 + * @param right 方向 + * @param trackSet 从position往方向[right]经过距离[s]所经过的轨道列表 + * @return + */ + private TrackPosition calculatePosition(TrackPosition position, int s, boolean right, Set trackSet) { if (s == 0) { return position; } @@ -76,7 +142,11 @@ public class SrdService { offset -= s; } SrTrack base = track; + trackSet.add(base); + int iter = 0; while (offset < 0 || offset > base.getLen()) { + BusinessExceptionAssertEnum.SYSTEM_EXCEPTION.assertTrue(iter < 10); + ++iter; SrTrack nextTrack = base.queryNextTrack(right); if (nextTrack == null) { // 下一区段为空,到达尽头或道岔失表处 log.debug(String.format("区段[%s][%s]区段不存在",base.debugStr(), right?"右向":"左向")); @@ -95,6 +165,7 @@ public class SrdService { offset += nextTrack.getLen(); } base = nextTrack; + trackSet.add(nextTrack); } } return new TrackPosition(base, offset); diff --git a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrAXC.java b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrAXC.java index b1f73be6c..26847f177 100644 --- a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrAXC.java +++ b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrAXC.java @@ -14,4 +14,19 @@ public class SrAXC extends SrDevice { public SrAXC(String id) { super(id, DeviceType.AXC); } + + public void clear() { + this.state.set(OFF); + } + + public void occupy() { + if (this.state.get() != ON) { + this.state.set(ON); + } + } + + @Override + public void applyState(int state) { + + } } diff --git a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrDevice.java b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrDevice.java index eb7a251f2..8f2c07115 100644 --- a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrDevice.java +++ b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrDevice.java @@ -3,6 +3,8 @@ package club.joylink.rtss.simulation.rt.srd.bo; import club.joylink.rtss.simulation.Debug; import lombok.Getter; +import java.util.Objects; + /** * 虚拟真实设备抽象父类 */ @@ -10,6 +12,10 @@ import lombok.Getter; public abstract class SrDevice implements Debug { String id; DeviceType deviceType; + /** + * 是否由真实设备控制 + */ + boolean controlByReal; public SrDevice() {} @@ -18,8 +24,23 @@ public abstract class SrDevice implements Debug { this.deviceType = deviceType; } + public abstract void applyState(int state); + @Override public String debugStr() { return String.format("%s:%s", this.deviceType, this.id); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SrDevice srDevice = (SrDevice) o; + return Objects.equals(id, srDevice.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } } diff --git a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrPSD.java b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrPSD.java index e72bc664f..371e83363 100644 --- a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrPSD.java +++ b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrPSD.java @@ -1,5 +1,7 @@ package club.joylink.rtss.simulation.rt.srd.bo; +import java.time.LocalDateTime; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** @@ -11,19 +13,56 @@ public class SrPSD extends SrDevice { AtomicInteger state = new AtomicInteger(CLOSE_LOCK); public static final int CLOSE_LOCK = 1;//关闭且锁闭 public static final int CLOSED = 2;//关闭未锁闭 - public static final int OPEN = 3;//打开未到位 + public static final int TURNING = 3;//转换中 public static final int OPEN_FINISH = 4;//打开到位 AtomicInteger command = new AtomicInteger(NONE); public static final int NONE = 0;//无动作 - public static final int C_OPEN = 1;//控制开门 - public static final int C_CLOSE = 2;//控制关门 /** - * 剩余动作时间 + * 转换完成时间 */ - int remain; + LocalDateTime finishTime; public SrPSD(String id) { super(id, DeviceType.PSD); } + + public boolean isTurning() { + return NONE != this.command.get(); + } + + public void open(LocalDateTime systemTime) { + this.startTurn(systemTime, OPEN_FINISH); + } + + public void close(LocalDateTime systemTime) { + this.startTurn(systemTime, CLOSE_LOCK); + } + + private void startTurn(LocalDateTime systemTime, int command) { + this.finishTime = systemTime.plusNanos(TimeUnit.MILLISECONDS.toNanos(turnTime)); + this.command.set(command); + this.state.set(TURNING); + } + + public void tryFinishTurning(LocalDateTime systemTime) { + if (this.finishTime != null && systemTime.compareTo(this.finishTime) >= 0) { + this.turnFinish(); + } + } + + public void turnFinish() { + this.state.set(this.command.get()); + this.command.set(NONE); + this.finishTime = null; + } + + @Override + public void applyState(int state) { + if (CLOSE_LOCK == state || CLOSED == state || TURNING == state || OPEN_FINISH == state) { + this.state.set(state); + } else { + throw new IllegalArgumentException(String.format("无效的屏蔽门状态:[%s]", state)); + } + } } diff --git a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrSignal.java b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrSignal.java index f61cf334f..2e3eb7516 100644 --- a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrSignal.java +++ b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrSignal.java @@ -2,6 +2,8 @@ package club.joylink.rtss.simulation.rt.srd.bo; import lombok.Getter; +import java.time.LocalDateTime; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** @@ -9,6 +11,7 @@ import java.util.concurrent.atomic.AtomicInteger; */ @Getter public class SrSignal extends SrDevice { + public static final int turnTime = 500; // 默认转换时间,单位ms /** * 所在区段 @@ -20,19 +23,18 @@ public class SrSignal extends SrDevice { int offset; AtomicInteger state = new AtomicInteger(CLOSE); - public static final int CLOSE = 1; - public static final int RED = 2; - public static final int GREEN = 3; - public static final int YELLOW = 4; - public static final int RED_YELLOW = 5; + public static final int CLOSE = 1; // 关闭 + public static final int RED = 2; // 红灯信号(禁止信号) + public static final int GREEN = 3; // 绿灯信号(直向通行) + public static final int YELLOW = 4; // 黄灯信号(侧向通行) + public static final int GUIDE = 5; // 引导信号(正线为黄红) - AtomicInteger command = new AtomicInteger(C_NONE); - public static final int C_NONE = 0; - public static final int C_CLOSE = 1; - public static final int C_RED = 2; - public static final int C_GREEN = 3; - public static final int C_YELLOW = 4; - public static final int C_RED_YELLOW = 5; + AtomicInteger command = new AtomicInteger(NONE); + public static final int NONE = 0; + /** + * 转换完成时间 + */ + LocalDateTime finishTime; public SrSignal(String id) { super(id, DeviceType.SIGNAL); @@ -42,4 +44,53 @@ public class SrSignal extends SrDevice { this.track = srTrack; this.offset = offset; } + + public boolean isTurning() { + return NONE != this.command.get(); + } + + public void close(LocalDateTime systemTime) { + this.startTurn(systemTime, CLOSE); + } + + public void openRed(LocalDateTime systemTime) { + this.startTurn(systemTime, RED); + } + + public void openGreen(LocalDateTime systemTime) { + this.startTurn(systemTime, GREEN); + } + + public void openYellow(LocalDateTime systemTime) { + this.startTurn(systemTime, YELLOW); + } + + public void openGuide(LocalDateTime systemTime) { + this.startTurn(systemTime, GUIDE); + } + + private void startTurn(LocalDateTime systemTime, int command) { + this.finishTime = systemTime.plusNanos(TimeUnit.MILLISECONDS.toNanos(turnTime)); + this.command.set(command); + } + + public void tryFinishTurning(LocalDateTime systemTime) { + if (this.finishTime != null && systemTime.compareTo(this.finishTime) >= 0) { + this.turnFinish(); + } + } + + public void turnFinish() { + this.state.set(this.command.get()); + this.command.set(NONE); + this.finishTime = null; + } + @Override + public void applyState(int state) { + if (CLOSE == state || RED == state || GREEN == state || YELLOW == state || GUIDE == state) { + this.state.set(state); + } else { + throw new IllegalArgumentException(String.format("无效的道岔状态:[%s]", state)); + } + } } diff --git a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrTrack.java b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrTrack.java index d8e2c5250..1aa38c7a9 100644 --- a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrTrack.java +++ b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrTrack.java @@ -14,14 +14,6 @@ public class SrTrack extends SrDevice implements Debug { * 轨道长度,单位mm */ int len; - /** - * 左停车点,单位mm - */ - int leftPs; - /** - * 右停车点,单位mm - */ - int rightPs; /** * 计轴器 */ @@ -104,8 +96,14 @@ public class SrTrack extends SrDevice implements Debug { return null; } + @Override + public void applyState(int state) { + + } + @Override public String debugStr() { return String.format("%s(%s)", this.name, this.id); } + } diff --git a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrTrain.java b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrTrain.java index 7633794d7..a1e187d9a 100644 --- a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrTrain.java +++ b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrTrain.java @@ -51,7 +51,7 @@ public class SrTrain extends SrDevice { /** * 档位 */ - int gear = NEUTRAL; + int gear; public static final int NEUTRAL = 0; //空挡 public static final int FORWARD = 1; //前进挡 public static final int REVERSE = -1; //后退档 @@ -72,8 +72,14 @@ public class SrTrain extends SrDevice { return NEUTRAL == this.gear; } - public void updatePositionAndSpeed(TrackPosition position, int v) { - this.headPosition = position; + public void updatePositionAndSpeed(TrackPosition headPosition, TrackPosition tailPosition, int v) { + this.headPosition = headPosition; + this.tailPosition = tailPosition; this.speed = v; } + + @Override + public void applyState(int state) { + + } } diff --git a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrTurnout.java b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrTurnout.java index a2aa90b8e..dff30455c 100644 --- a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrTurnout.java +++ b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrTurnout.java @@ -1,5 +1,7 @@ package club.joylink.rtss.simulation.rt.srd.bo; +import java.time.LocalDateTime; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** @@ -19,12 +21,10 @@ public class SrTurnout extends SrDevice { AtomicInteger command = new AtomicInteger(NONE); public static final int NONE = 0; - public static final int TN = 1; // 定操/转动到定位 - public static final int TR = 2; // 反操/转动到反位 /** - * 转换剩余时间,单位ms + * 转换完成时间 */ - int remain; + LocalDateTime finishTime; public SrTurnout(String id) { super(id, DeviceType.TURNOUT); @@ -53,4 +53,43 @@ public class SrTurnout extends SrDevice { public boolean isReversePosition() { return REVERSE == this.state.get(); } + + public boolean isTurning() { + return NONE != this.command.get(); + } + + public void turnToNormal(LocalDateTime systemTime) { + this.startTurn(systemTime, NORMAL); + } + + public void turnToReverse(LocalDateTime systemTime) { + this.startTurn(systemTime, REVERSE); + } + + private void startTurn(LocalDateTime systemTime, int command) { + this.finishTime = systemTime.plusNanos(TimeUnit.MILLISECONDS.toNanos(turnTime)); + this.command.set(command); + this.state.set(TURNING); + } + + public void tryFinishTurning(LocalDateTime systemTime) { + if (this.finishTime != null && systemTime.compareTo(this.finishTime) >= 0) { + this.turnFinish(); + } + } + + public void turnFinish() { + this.state.set(this.command.get()); + this.command.set(NONE); + this.finishTime = null; + } + + @Override + public void applyState(int state) { + if (NORMAL == state || REVERSE == state || TURNING == state) { + this.state.set(state); + } else { + throw new IllegalArgumentException(String.format("无效的道岔状态:[%s]", state)); + } + } } diff --git a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrdRepository.java b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrdRepository.java index 607ee244e..80db54bba 100644 --- a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrdRepository.java +++ b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/SrdRepository.java @@ -31,6 +31,18 @@ public class SrdRepository extends SimulationRepository { return new ArrayList<>(this.trainMap.values()); } + public List getTurnoutList() { + return new ArrayList<>(this.turnoutMap.values()); + } + + public List getPsdList() { + return new ArrayList<>(this.psdMap.values()); + } + + public List getSignalList() { + return new ArrayList<>(this.signalMap.values()); + } + @Override public void initState() { diff --git a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/TrackPosition.java b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/TrackPosition.java index 087442a85..963d22def 100644 --- a/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/TrackPosition.java +++ b/src/main/java/club/joylink/rtss/simulation/rt/srd/bo/TrackPosition.java @@ -2,6 +2,9 @@ package club.joylink.rtss.simulation.rt.srd.bo; public class TrackPosition { SrTrack track; + /** + * 轨道偏移量,单位mm + */ int offset; public TrackPosition(SrTrack track, int offset) { diff --git a/src/main/java/club/joylink/rtss/websocket/SessionAuthHandshakeInterceptor.java b/src/main/java/club/joylink/rtss/websocket/SessionAuthHandshakeInterceptor.java index e3544a319..c1c77af67 100644 --- a/src/main/java/club/joylink/rtss/websocket/SessionAuthHandshakeInterceptor.java +++ b/src/main/java/club/joylink/rtss/websocket/SessionAuthHandshakeInterceptor.java @@ -5,6 +5,7 @@ import club.joylink.rtss.services.LoginSessionManager; import club.joylink.rtss.vo.LoginUserInfoVO; import club.joylink.rtss.vo.UserVO; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.util.StringUtils; @@ -34,6 +35,7 @@ public class SessionAuthHandshakeInterceptor implements HandshakeInterceptor { } catch (Throwable e) { log.error("未登录或登陆已过期", e); } + response.setStatusCode(HttpStatus.UNAUTHORIZED); return false; } @@ -71,5 +73,6 @@ public class SessionAuthHandshakeInterceptor implements HandshakeInterceptor { @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { + } } diff --git a/src/main/java/club/joylink/rtss/websocket/client/SimulationSessionHandler.java b/src/main/java/club/joylink/rtss/websocket/client/SimulationSessionHandler.java new file mode 100644 index 000000000..76aa57332 --- /dev/null +++ b/src/main/java/club/joylink/rtss/websocket/client/SimulationSessionHandler.java @@ -0,0 +1,38 @@ +package club.joylink.rtss.websocket.client; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.messaging.simp.stomp.StompCommand; +import org.springframework.messaging.simp.stomp.StompHeaders; +import org.springframework.messaging.simp.stomp.StompSession; +import org.springframework.messaging.simp.stomp.StompSessionHandler; + +import java.lang.reflect.Type; + +@Slf4j +public class SimulationSessionHandler implements StompSessionHandler { + @Override + public void afterConnected(StompSession session, StompHeaders connectedHeaders) { + log.debug(String.format("stomp websocket connected")); + } + + @Override + public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload, Throwable exception) { + log.error("stomp handle exception", exception); + } + + @Override + public void handleTransportError(StompSession session, Throwable exception) { + log.error("stomp handle transport error", exception); + } + + @Override + public Type getPayloadType(StompHeaders headers) { + return String.class; + } + + @Override + public void handleFrame(StompHeaders headers, Object payload) { + String json = (String) payload; + log.debug(String.format("receive frame:%s", json)); + } +} diff --git a/src/main/java/club/joylink/rtss/websocket/client/StompClientManager.java b/src/main/java/club/joylink/rtss/websocket/client/StompClientManager.java new file mode 100644 index 000000000..79881a579 --- /dev/null +++ b/src/main/java/club/joylink/rtss/websocket/client/StompClientManager.java @@ -0,0 +1,27 @@ +package club.joylink.rtss.websocket.client; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.messaging.simp.stomp.StompSession; +import org.springframework.util.concurrent.ListenableFuture; +import org.springframework.web.socket.client.standard.StandardWebSocketClient; +import org.springframework.web.socket.messaging.WebSocketStompClient; + +import java.util.concurrent.ExecutionException; + +@Slf4j +public class StompClientManager { + +// private static final Map stompClientMap = new ConcurrentHashMap<>(); + + public static void main(String[] args) throws ExecutionException, InterruptedException { + StandardWebSocketClient socketClient = new StandardWebSocketClient(); + WebSocketStompClient stompClient = new WebSocketStompClient(socketClient); + SimulationSessionHandler handler = new SimulationSessionHandler(); + ListenableFuture future = stompClient + .connect("ws://192.168.8.129:9000/joylink-websocket?token=aaa", + handler, "null"); + StompSession stompSession = future.get(); + stompSession.subscribe("/user/queue/simulation/1/", handler); + } + +}