package com.zy.core.plugin; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.zy.asrs.entity.*; import com.zy.asrs.service.*; import com.zy.common.utils.RedisUtil; import com.zy.core.News; import com.zy.core.cache.MessageQueue; import com.zy.core.cache.SlaveConnection; import com.zy.core.dispatch.StationCommandDispatchResult; import com.zy.core.dispatch.StationCommandDispatcher; import com.zy.core.enums.*; import com.zy.core.model.StationObjModel; import com.zy.core.model.Task; import com.zy.core.model.command.CrnCommand; import com.zy.core.model.command.StationCommand; import com.zy.core.model.protocol.CrnProtocol; import com.zy.core.network.fake.FakeConfigKeys; import com.zy.core.network.fake.FakeConfigSupport; import com.zy.core.plugin.api.MainProcessPluginApi; import com.zy.core.plugin.station.FakeStationMonitor; import com.zy.core.plugin.station.FakeTaskGenerator; import com.zy.core.plugin.store.StoreInTaskContext; import com.zy.core.plugin.store.StoreInTaskPolicy; import com.zy.core.task.MainProcessLane; import com.zy.core.task.MainProcessTaskSubmitter; import com.zy.core.thread.CrnThread; import com.zy.core.thread.StationThread; import com.zy.core.utils.CrnOperateProcessUtils; import com.zy.core.utils.DualCrnOperateProcessUtils; import com.zy.core.utils.StationOperateProcessUtils; import com.zy.system.entity.Config; import com.zy.system.service.ConfigService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.*; @Slf4j @Component public class FakeProcess implements MainProcessPluginApi, StoreInTaskPolicy { private long getMainDispatchIntervalMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_IDLE_LOOP_DELAY_MS, 200L); } private long getAsyncDispatchIntervalMs() { return Math.max(50L, FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_INITIALIZE_DELAY_MS, 50L)); } private long getOutStationStayTimeoutMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_OUT_STATION_STAY_TIMEOUT_MS, 3000L); } private static volatile String enableFake = "N"; private static volatile String fakeRealTaskRequestWms = "N"; private static volatile String fakeGenerateInTask = "Y"; private static volatile String fakeGenerateOutTask = "Y"; private static final Map OUT_STATION_STAY_MARKER = new HashMap(); private final Object outStationStayLock = new Object(); private boolean shouldDispatchOutStationWriteInfo(Integer wrkNo) { long now = System.currentTimeMillis(); long timeoutMs = getOutStationStayTimeoutMs(); synchronized (outStationStayLock) { Long lastTime = OUT_STATION_STAY_MARKER.get(wrkNo); if (lastTime == null || now - lastTime >= timeoutMs) { OUT_STATION_STAY_MARKER.put(wrkNo, now); return true; } return false; } } private void clearOutStationStayMarker(Integer wrkNo) { synchronized (outStationStayLock) { OUT_STATION_STAY_MARKER.remove(wrkNo); } } private void dispatchOutStationWriteInfoIfReady(WrkMast wrkMast, BasCrnp basCrnp) { Integer wrkNo = wrkMast == null ? null : wrkMast.getWrkNo(); if (wrkNo == null) { News.error("仿真出库站点写入跳过,WCS任务号为空。sourceStaNo={},crnNo={}", wrkMast == null ? null : wrkMast.getSourceStaNo(), basCrnp == null ? null : basCrnp.getCrnNo()); return; } if (!shouldDispatchOutStationWriteInfo(wrkNo)) { News.error("仿真出库站点写入跳过,命中节流限制。wrkNo={},sourceStaNo={},crnNo={}", wrkNo, wrkMast == null ? null : wrkMast.getSourceStaNo(), basCrnp == null ? null : basCrnp.getCrnNo()); return; } String taskNo = String.valueOf(wrkNo); List outStationList = basCrnp.getOutStationList$(); if (outStationList.isEmpty()) { News.error("仿真出库站点写入失败,堆垛机未配置出库站点。crnNo={},wrkNo={},taskNo={}", basCrnp.getCrnNo(), wrkMast == null ? null : wrkMast.getWrkNo(), taskNo); return; } boolean matched = false; for (StationObjModel stationObjModel : outStationList) { if (!stationObjModel.getStationId().equals(wrkMast.getSourceStaNo())) { continue; } matched = true; StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo()); if (stationThread == null) { News.error("仿真出库站点写入失败,站点线程不存在。taskNo={},wrkNo={},stationId={},deviceNo={}", taskNo, wrkMast.getWrkNo(), stationObjModel.getStationId(), stationObjModel.getDeviceNo()); continue; } StationCommand command = stationThread.getCommand(StationCommandType.WRITE_INFO, 9998, wrkMast.getSourceStaNo(), 0, 0); StationCommandDispatchResult dispatchResult = stationCommandDispatcher.dispatch( stationObjModel.getDeviceNo(), command, "fake-process", "crn-out-complete-write-info"); News.info("仿真出库站点写入命令分发结果。taskNo={},wrkNo={},crnNo={},stationId={},deviceNo={},accepted={},reason={},queueDepth={}", taskNo, wrkMast.getWrkNo(), basCrnp.getCrnNo(), stationObjModel.getStationId(), stationObjModel.getDeviceNo(), dispatchResult != null && dispatchResult.isAccepted(), dispatchResult == null ? null : dispatchResult.getReason(), dispatchResult == null ? null : dispatchResult.getQueueDepth()); } if (!matched) { News.error("仿真出库站点写入失败,未匹配到sourceStaNo对应出库站点。taskNo={},wrkNo={},sourceStaNo={},crnNo={}", taskNo, wrkMast == null ? null : wrkMast.getWrkNo(), wrkMast == null ? null : wrkMast.getSourceStaNo(), basCrnp == null ? null : basCrnp.getCrnNo()); } } @Autowired private ConfigService configService; @Autowired private WrkMastService wrkMastService; @Autowired private WrkAnalysisService wrkAnalysisService; @Autowired private BasCrnpService basCrnpService; @Autowired private RedisUtil redisUtil; @Autowired private CrnOperateProcessUtils crnOperateUtils; @Autowired private StationOperateProcessUtils stationOperateProcessUtils; @Autowired private DualCrnOperateProcessUtils dualCrnOperateProcessUtils; @Autowired private StationCommandDispatcher stationCommandDispatcher; @Autowired private MainProcessTaskSubmitter mainProcessTaskSubmitter; @Autowired private FakeStationMonitor fakeStationMonitor; @Autowired private FakeTaskGenerator fakeTaskGenerator; @Override public void run() { long startTime = System.currentTimeMillis(); refreshFakeConfig(); syncConfigToComponents(); long asyncDispatchIntervalMs = getAsyncDispatchIntervalMs(); long mainDispatchIntervalMs = getMainDispatchIntervalMs(); // 仿真异步任务 fakeStationMonitor.submitMonitorTasks(asyncDispatchIntervalMs); // 仿真输送线堵塞检测 stationOperateProcessUtils.submitCheckStationRunBlockTasks(MainProcessLane.FAKE_STATION_RUN_BLOCK, asyncDispatchIntervalMs); // 请求生成入库任务 fakeTaskGenerator.submitGenerateTasks(); // 执行堆垛机任务 crnOperateUtils.submitCrnIoTasks(MainProcessLane.FAKE_CRN_IO, mainDispatchIntervalMs); // 堆垛机任务执行完成 submitCrnIoExecuteFinishTasks(mainDispatchIntervalMs); // 执行输送站点入库任务 stationOperateProcessUtils.submitStationInTasks(MainProcessLane.FAKE_STATION_IN, mainDispatchIntervalMs); // 检测入库任务是否已经到达目标站台 stationOperateProcessUtils.submitInboundStationArrivalTasks(mainDispatchIntervalMs); // 输送线执行堆垛机出库后的站台流转 stationOperateProcessUtils.submitCrnStationOutTasks(MainProcessLane.FAKE_STATION_OUT, mainDispatchIntervalMs); // 检测出库任务是否已经到达目标站台 stationOperateProcessUtils.submitStationOutExecuteFinishTasks(mainDispatchIntervalMs); // 检测站台运行完成后的任务转完成 stationOperateProcessUtils.submitCheckTaskToCompleteTasks(mainDispatchIntervalMs); // 检测并处理出库排序 stationOperateProcessUtils.submitCheckStationOutOrderTasks(MainProcessLane.FAKE_STATION_OUT_ORDER, mainDispatchIntervalMs); // 执行双工位堆垛机任务 dualCrnOperateProcessUtils.submitDualCrnIoTasks(MainProcessLane.FAKE_DUAL_CRN_IO, mainDispatchIntervalMs); // 双工位堆垛机任务执行完成 dualCrnOperateProcessUtils.submitDualCrnIoExecuteFinishTasks(MainProcessLane.FAKE_DUAL_CRN_IO_FINISH, mainDispatchIntervalMs); News.info("[WCS Debug] 主线程Run执行完成,耗时:{}ms", System.currentTimeMillis() - startTime); } private void refreshFakeConfig() { Config enableFakeConfig = configService.getOne(new QueryWrapper().eq("code", "enableFake")); if (enableFakeConfig != null) { enableFake = enableFakeConfig.getValue(); } Config fakeRealTaskRequestWmsConfig = configService .getOne(new QueryWrapper().eq("code", "fakeRealTaskRequestWms")); if (fakeRealTaskRequestWmsConfig != null) { fakeRealTaskRequestWms = fakeRealTaskRequestWmsConfig.getValue(); } Config fakeGenerateInTaskConfig = configService .getOne(new QueryWrapper().eq("code", "fakeGenerateInTask")); if (fakeGenerateInTaskConfig != null) { fakeGenerateInTask = fakeGenerateInTaskConfig.getValue(); } Config fakeGenerateOutTaskConfig = configService .getOne(new QueryWrapper().eq("code", "fakeGenerateOutTask")); if (fakeGenerateOutTaskConfig != null) { fakeGenerateOutTask = fakeGenerateOutTaskConfig.getValue(); } } private void syncConfigToComponents() { fakeStationMonitor.setEnableFake(enableFake); fakeStationMonitor.setFakeGenerateInTask(fakeGenerateInTask); fakeTaskGenerator.setFakeRealTaskRequestWms(fakeRealTaskRequestWms); fakeTaskGenerator.setFakeGenerateInTask(fakeGenerateInTask); fakeTaskGenerator.setFakeGenerateOutTask(fakeGenerateOutTask); } private void submitCrnIoExecuteFinishTasks(long minIntervalMs) { List basCrnps = basCrnpService.list(new QueryWrapper<>()); for (BasCrnp basCrnp : basCrnps) { Integer crnNo = basCrnp == null ? null : basCrnp.getCrnNo(); if (crnNo == null) { continue; } mainProcessTaskSubmitter.submitKeyedSerialTask( MainProcessLane.FAKE_CRN_IO_FINISH, crnNo, "crnIoExecuteFinish", minIntervalMs, () -> crnIoExecuteFinish(basCrnp) ); } } @Override public boolean isEnabled() { return fakeTaskGenerator.isEnabled(); } @Override public void onRequestPermitGranted(StoreInTaskContext context) { fakeTaskGenerator.onRequestPermitGranted(context); } @Override public void afterTaskCreated(StoreInTaskContext context, WrkMast wrkMast) { fakeTaskGenerator.afterTaskCreated(context, wrkMast); } // 堆垛机任务执行完成 public void crnIoExecuteFinish() { List basCrnps = basCrnpService.list(new QueryWrapper<>()); for (BasCrnp basCrnp : basCrnps) { crnIoExecuteFinish(basCrnp); } } private void crnIoExecuteFinish(BasCrnp basCrnp) { CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, basCrnp.getCrnNo()); if (crnThread == null) { return; } CrnProtocol crnProtocol = crnThread.getStatus(); if (crnProtocol == null) { return; } if (crnProtocol.getMode() != CrnModeType.AUTO.id || crnProtocol.getTaskNo() <= 0 || crnProtocol.getStatus() != CrnStatusType.WAITING.id) { return; } Object lock = redisUtil.get(RedisKeyType.CRN_IO_EXECUTE_FINISH_LIMIT.key + basCrnp.getCrnNo()); if (lock != null) { return; } // 获取待确认工作档 WrkMast wrkMast = wrkMastService.selectByWorkNo(crnProtocol.getTaskNo()); if (wrkMast == null) { News.error("堆垛机处于等待确认且任务完成状态,但未找到工作档。堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo()); return; } Long updateWrkSts = null; if (wrkMast.getWrkSts() == WrkStsType.INBOUND_RUN.sts) { updateWrkSts = WrkStsType.COMPLETE_INBOUND.sts; } else if (wrkMast.getWrkSts() == WrkStsType.OUTBOUND_RUN.sts) { updateWrkSts = WrkStsType.OUTBOUND_RUN_COMPLETE.sts; dispatchOutStationWriteInfoIfReady(wrkMast, basCrnp); } else if (wrkMast.getWrkSts() == WrkStsType.LOC_MOVE_RUN.sts) { updateWrkSts = WrkStsType.COMPLETE_LOC_MOVE.sts; } else if (wrkMast.getWrkSts() == WrkStsType.CRN_MOVE_RUN.sts) { updateWrkSts = WrkStsType.COMPLETE_CRN_MOVE.sts; } else { News.error("堆垛机处于等待确认且任务完成状态,但工作状态异常。堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo()); return; } Date now = new Date(); wrkMast.setWrkSts(updateWrkSts); wrkMast.setSystemMsg(""); wrkMast.setIoTime(now); wrkMast.setModiTime(now); if (wrkMastService.updateById(wrkMast)) { clearOutStationStayMarker(wrkMast.getWrkNo()); wrkAnalysisService.markCraneComplete(wrkMast, now, updateWrkSts); CrnCommand resetCommand = crnThread.getResetCommand(crnProtocol.getTaskNo(), crnProtocol.getCrnNo()); MessageQueue.offer(SlaveType.Crn, crnProtocol.getCrnNo(), new Task(2, resetCommand)); News.info("堆垛机任务状态更新成功,堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo()); } redisUtil.set(RedisKeyType.CRN_IO_EXECUTE_FINISH_LIMIT.key + basCrnp.getCrnNo(), "lock", 10); } }