| | |
| | | import com.zy.asrs.domain.vo.StationTaskTraceEventVo; |
| | | import com.zy.asrs.domain.vo.StationTaskTraceSegmentVo; |
| | | import com.zy.asrs.domain.vo.StationTaskTraceVo; |
| | | import com.zy.asrs.entity.WrkMast; |
| | | import com.zy.asrs.service.WrkMastService; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.enums.WrkStsType; |
| | | import com.zy.core.model.command.StationCommand; |
| | | import com.zy.core.enums.RedisKeyType; |
| | | import lombok.Data; |
| | |
| | | |
| | | @Autowired |
| | | private RedisUtil redisUtil; |
| | | |
| | | @Autowired |
| | | private WrkMastService wrkMastService; |
| | | |
| | | private final Map<Integer, TraceTaskState> taskStateMap = new ConcurrentHashMap<>(); |
| | | private volatile boolean loadedFromRedis = false; |
| | |
| | | persistState(state); |
| | | } |
| | | |
| | | public void updateLoopHint(Integer taskNo, |
| | | Boolean loopAlertActive, |
| | | String loopAlertType, |
| | | String loopAlertText, |
| | | Integer loopAlertCount, |
| | | Map<String, Object> details) { |
| | | ensureCacheLoaded(); |
| | | TraceTaskState state = taskStateMap.get(taskNo); |
| | | if (state == null) { |
| | | return; |
| | | } |
| | | state.updateLoopHint(loopAlertActive, loopAlertType, loopAlertText, loopAlertCount, details); |
| | | persistState(state); |
| | | } |
| | | |
| | | public List<StationTaskTraceVo> listLatestTraces() { |
| | | ensureCacheLoaded(); |
| | | cleanupExpired(); |
| | | reconcileInactiveBusinessTasks(); |
| | | List<StationTaskTraceVo> result = new ArrayList<>(); |
| | | for (TraceTaskState state : taskStateMap.values()) { |
| | | if (state != null) { |
| | |
| | | return result; |
| | | } |
| | | |
| | | public List<StationTaskTraceVo> listActiveTraces() { |
| | | List<StationTaskTraceVo> latestTraces = listLatestTraces(); |
| | | List<StationTaskTraceVo> result = new ArrayList<>(); |
| | | for (StationTaskTraceVo traceVo : latestTraces) { |
| | | if (traceVo == null) { |
| | | continue; |
| | | } |
| | | String status = traceVo.getStatus(); |
| | | if (STATUS_WAITING.equals(status) |
| | | || STATUS_RUNNING.equals(status) |
| | | || STATUS_REROUTED.equals(status)) { |
| | | result.add(traceVo); |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private void cleanupExpired() { |
| | | long now = System.currentTimeMillis(); |
| | | for (Map.Entry<Integer, TraceTaskState> entry : taskStateMap.entrySet()) { |
| | |
| | | removePersistedState(entry.getKey()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void reconcileInactiveBusinessTasks() { |
| | | if (wrkMastService == null) { |
| | | return; |
| | | } |
| | | for (TraceTaskState state : taskStateMap.values()) { |
| | | if (state == null || state.isTerminal()) { |
| | | continue; |
| | | } |
| | | WrkMast wrkMast; |
| | | try { |
| | | wrkMast = wrkMastService.selectByWorkNo(state.taskNo); |
| | | } catch (Exception ignore) { |
| | | continue; |
| | | } |
| | | if (wrkMast == null) { |
| | | Integer currentStationId = state.currentStationId != null ? state.currentStationId : state.finalTargetStationId; |
| | | Map<String, Object> details = new LinkedHashMap<>(); |
| | | details.put("reason", "wrk_missing"); |
| | | state.markTerminal(state.traceVersion, STATUS_FINISHED, currentStationId, null, |
| | | "AUTO_FINISHED", "输送任务档已不存在,轨迹自动结束", details); |
| | | persistState(state); |
| | | continue; |
| | | } |
| | | if (isStationTraceActiveWrkStatus(wrkMast.getWrkSts())) { |
| | | continue; |
| | | } |
| | | Integer currentStationId = state.currentStationId != null ? state.currentStationId : state.finalTargetStationId; |
| | | Map<String, Object> details = new LinkedHashMap<>(); |
| | | details.put("reason", "wrk_status_transition"); |
| | | details.put("wrkSts", wrkMast.getWrkSts()); |
| | | details.put("wrkStsDesc", wrkMast.getWrkSts$()); |
| | | if (isManualWrkStatus(wrkMast.getWrkSts())) { |
| | | state.markTerminal(state.traceVersion, STATUS_CANCELLED, currentStationId, null, |
| | | "AUTO_CANCELLED", "输送任务已退出运行,轨迹自动结束", details); |
| | | } else { |
| | | state.markTerminal(state.traceVersion, STATUS_FINISHED, currentStationId, null, |
| | | "AUTO_FINISHED", "输送任务已结束,轨迹自动结束", details); |
| | | } |
| | | persistState(state); |
| | | } |
| | | } |
| | | |
| | | private boolean isStationTraceActiveWrkStatus(Long wrkSts) { |
| | | return Objects.equals(wrkSts, WrkStsType.INBOUND_DEVICE_RUN.sts) |
| | | || Objects.equals(wrkSts, WrkStsType.STATION_RUN.sts); |
| | | } |
| | | |
| | | private boolean isManualWrkStatus(Long wrkSts) { |
| | | return Objects.equals(wrkSts, WrkStsType.INBOUND_MANUAL.sts) |
| | | || Objects.equals(wrkSts, WrkStsType.OUTBOUND_MANUAL.sts) |
| | | || Objects.equals(wrkSts, WrkStsType.LOC_MOVE_MANUAL.sts); |
| | | } |
| | | |
| | | private void ensureCacheLoaded() { |
| | |
| | | private List<StationTaskTraceSegmentVo> segmentList = new ArrayList<>(); |
| | | private Integer issuedSegmentCount; |
| | | private Integer totalSegmentCount; |
| | | private Boolean loopAlertActive; |
| | | private String loopAlertType; |
| | | private String loopAlertText; |
| | | private Integer loopAlertCount; |
| | | private Long updatedAt; |
| | | private Long terminalExpireAt; |
| | | private List<StationTaskTraceEventVo> events = new ArrayList<>(); |
| | |
| | | private List<StationTaskTraceSegmentVo> segmentList = new ArrayList<>(); |
| | | private Integer issuedSegmentCount = 0; |
| | | private Integer totalSegmentCount = 0; |
| | | private Boolean loopAlertActive = Boolean.FALSE; |
| | | private String loopAlertType; |
| | | private String loopAlertText; |
| | | private Integer loopAlertCount; |
| | | private final List<StationTaskTraceEventVo> events = new ArrayList<>(); |
| | | private Long updatedAt = System.currentTimeMillis(); |
| | | private Long terminalExpireAt; |
| | |
| | | List<Integer> localPathStationIds, |
| | | List<StationTaskTraceSegmentVo> localSegmentList) { |
| | | TraceRegistration registration = new TraceRegistration(); |
| | | boolean firstPlan = this.traceVersion == null || this.traceVersion <= 0; |
| | | boolean rerouted = !isTerminalStatus(this.status) && this.traceVersion != null && this.traceVersion > 0; |
| | | int nextTraceVersion = rerouted ? this.traceVersion + 1 : 1; |
| | | int pathOffset = rerouted ? this.passedStationIds.size() : 0; |
| | |
| | | this.latestIssuedSegmentPath = new ArrayList<>(); |
| | | this.status = rerouted ? STATUS_REROUTED : STATUS_WAITING; |
| | | this.terminalExpireAt = null; |
| | | if (firstPlan) { |
| | | clearLoopHintState(); |
| | | } |
| | | rebuildProgress(planCurrentStationId); |
| | | this.updatedAt = System.currentTimeMillis(); |
| | | |
| | |
| | | details.put("segmentCount", this.totalSegmentCount); |
| | | details.put("pathOffset", pathOffset); |
| | | details.put("currentStationId", this.currentStationId); |
| | | appendLoopHintDetails(details); |
| | | appendEvent(rerouted ? "REROUTED" : "PLAN_READY", |
| | | rerouted ? "输送任务路径已重算并续接轨迹" : "输送任务分段计划已建立", |
| | | details); |
| | |
| | | appendEvent(eventType, message, nextDetails); |
| | | } |
| | | |
| | | private synchronized void updateLoopHint(Boolean loopAlertActive, |
| | | String loopAlertType, |
| | | String loopAlertText, |
| | | Integer loopAlertCount, |
| | | Map<String, Object> details) { |
| | | boolean active = Boolean.TRUE.equals(loopAlertActive) |
| | | && loopAlertCount != null |
| | | && loopAlertCount > 1 |
| | | && loopAlertText != null |
| | | && !loopAlertText.trim().isEmpty(); |
| | | String nextType = active ? loopAlertType : null; |
| | | String nextText = active ? loopAlertText.trim() : null; |
| | | Integer nextCount = active ? loopAlertCount : null; |
| | | boolean changed = !Objects.equals(this.loopAlertActive, active) |
| | | || !Objects.equals(this.loopAlertType, nextType) |
| | | || !Objects.equals(this.loopAlertText, nextText) |
| | | || !Objects.equals(this.loopAlertCount, nextCount); |
| | | if (!changed) { |
| | | return; |
| | | } |
| | | |
| | | this.loopAlertActive = active; |
| | | this.loopAlertType = nextType; |
| | | this.loopAlertText = nextText; |
| | | this.loopAlertCount = nextCount; |
| | | this.updatedAt = System.currentTimeMillis(); |
| | | if (!active) { |
| | | return; |
| | | } |
| | | |
| | | Map<String, Object> nextDetails = copyDetails(details); |
| | | nextDetails.put("loopAlertActive", Boolean.TRUE); |
| | | nextDetails.put("loopAlertType", this.loopAlertType); |
| | | nextDetails.put("loopAlertText", this.loopAlertText); |
| | | nextDetails.put("loopAlertCount", this.loopAlertCount); |
| | | appendEvent("LOOP_REPEAT_ALERT", this.loopAlertText, nextDetails); |
| | | } |
| | | |
| | | private void clearPathState() { |
| | | this.fullPathStationIds = new ArrayList<>(); |
| | | this.issuedStationIds = new ArrayList<>(); |
| | |
| | | return terminalExpireAt != null && terminalExpireAt <= now; |
| | | } |
| | | |
| | | private synchronized boolean isTerminal() { |
| | | return isTerminalStatus(this.status); |
| | | } |
| | | |
| | | private synchronized StationTaskTraceVo toVo() { |
| | | StationTaskTraceVo vo = new StationTaskTraceVo(); |
| | | vo.setTaskNo(taskNo); |
| | |
| | | vo.setSegmentList(copySegmentListWithIssued(segmentList, issuedSegmentCount)); |
| | | vo.setIssuedSegmentCount(issuedSegmentCount); |
| | | vo.setTotalSegmentCount(totalSegmentCount); |
| | | vo.setLoopAlertActive(loopAlertActive); |
| | | vo.setLoopAlertType(loopAlertType); |
| | | vo.setLoopAlertText(loopAlertText); |
| | | vo.setLoopAlertCount(loopAlertCount); |
| | | vo.setUpdatedAt(updatedAt); |
| | | vo.setEvents(copyEventList(events)); |
| | | return vo; |
| | |
| | | snapshot.setSegmentList(copySegmentList(segmentList)); |
| | | snapshot.setIssuedSegmentCount(issuedSegmentCount); |
| | | snapshot.setTotalSegmentCount(totalSegmentCount); |
| | | snapshot.setLoopAlertActive(loopAlertActive); |
| | | snapshot.setLoopAlertType(loopAlertType); |
| | | snapshot.setLoopAlertText(loopAlertText); |
| | | snapshot.setLoopAlertCount(loopAlertCount); |
| | | snapshot.setUpdatedAt(updatedAt); |
| | | snapshot.setTerminalExpireAt(terminalExpireAt); |
| | | snapshot.setEvents(copyEventList(events)); |
| | |
| | | state.segmentList = copySegmentList(snapshot.getSegmentList()); |
| | | state.issuedSegmentCount = snapshot.getIssuedSegmentCount() == null ? 0 : snapshot.getIssuedSegmentCount(); |
| | | state.totalSegmentCount = snapshot.getTotalSegmentCount() == null ? state.segmentList.size() : snapshot.getTotalSegmentCount(); |
| | | state.loopAlertActive = Boolean.TRUE.equals(snapshot.getLoopAlertActive()); |
| | | state.loopAlertType = snapshot.getLoopAlertType(); |
| | | state.loopAlertText = snapshot.getLoopAlertText(); |
| | | state.loopAlertCount = snapshot.getLoopAlertCount(); |
| | | state.updatedAt = snapshot.getUpdatedAt() == null ? System.currentTimeMillis() : snapshot.getUpdatedAt(); |
| | | state.terminalExpireAt = snapshot.getTerminalExpireAt(); |
| | | state.events.clear(); |
| | |
| | | this.pendingStationIds = copyIntegerList(fullPath.subList(currentIndex + 1, fullPath.size())); |
| | | } |
| | | |
| | | private void clearLoopHintState() { |
| | | this.loopAlertActive = Boolean.FALSE; |
| | | this.loopAlertType = null; |
| | | this.loopAlertText = null; |
| | | this.loopAlertCount = null; |
| | | } |
| | | |
| | | private void appendLoopHintDetails(Map<String, Object> details) { |
| | | if (details == null || !Boolean.TRUE.equals(this.loopAlertActive) || this.loopAlertCount == null || this.loopAlertCount <= 1) { |
| | | return; |
| | | } |
| | | details.put("loopAlertActive", Boolean.TRUE); |
| | | details.put("loopAlertType", this.loopAlertType); |
| | | details.put("loopAlertText", this.loopAlertText); |
| | | details.put("loopAlertCount", this.loopAlertCount); |
| | | } |
| | | |
| | | private boolean acceptTraceVersion(Integer incomingTraceVersion) { |
| | | return incomingTraceVersion != null |
| | | && this.traceVersion != null |