11个文件已添加
13个文件已修改
1个文件已删除
| | |
| | | import com.core.exception.CoolException; |
| | | import com.zy.asrs.domain.Result.CancelTaskBatchResult; |
| | | import com.zy.asrs.domain.param.*; |
| | | import com.core.annotations.ManagerAuth; |
| | | import com.zy.asrs.entity.DeviceConfig; |
| | | import com.zy.asrs.entity.LocMast; |
| | | import com.zy.asrs.entity.WrkMast; |
| | |
| | | import com.zy.core.model.protocol.DualCrnProtocol; |
| | | import com.zy.core.model.protocol.RgvProtocol; |
| | | import com.zy.core.model.protocol.StationProtocol; |
| | | import com.zy.core.network.fake.FakeConfigKeys; |
| | | import com.zy.core.thread.CrnThread; |
| | | import com.zy.core.thread.DualCrnThread; |
| | | import com.zy.core.thread.RgvThread; |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | @Slf4j |
| | | @RestController |
| | |
| | | return R.ok(); |
| | | } |
| | | |
| | | @GetMapping("/fakeConfig/auth") |
| | | @ManagerAuth |
| | | public R getFakeConfig() { |
| | | LinkedHashMap<String, Object> map = new LinkedHashMap<>(); |
| | | for (String key : FakeConfigKeys.ALL_KEYS) { |
| | | String defaultValue = FakeConfigKeys.DEFAULTS.get(key); |
| | | map.put(key, configService.getConfigValue(key, defaultValue)); |
| | | } |
| | | return R.ok().add(map); |
| | | } |
| | | |
| | | @PostMapping("/fakeConfig/save/auth") |
| | | @ManagerAuth |
| | | public R saveFakeConfig(@RequestBody FakeConfigSaveParam param) { |
| | | if (param == null || param.getConfigMap() == null || param.getConfigMap().isEmpty()) { |
| | | return R.error("参数不能为空"); |
| | | } |
| | | Set<String> allowKeys = FakeConfigKeys.ALL_KEYS; |
| | | for (Map.Entry<String, Object> entry : param.getConfigMap().entrySet()) { |
| | | String key = entry.getKey(); |
| | | if (!allowKeys.contains(key)) { |
| | | return R.error("不支持的配置项: " + key); |
| | | } |
| | | String normalized = normalizeFakeConfigValue(key, entry.getValue()); |
| | | if (!configService.saveConfigValue(key, normalized)) { |
| | | return R.error("保存失败: " + key); |
| | | } |
| | | } |
| | | configService.refreshSystemConfigCache(); |
| | | return getFakeConfig(); |
| | | } |
| | | |
| | | private String normalizeFakeConfigValue(String key, Object rawValue) { |
| | | if (rawValue == null) { |
| | | throw new CoolException(key + " 参数不能为空"); |
| | | } |
| | | String value = String.valueOf(rawValue).trim(); |
| | | if (FakeConfigKeys.BOOLEAN_KEYS.contains(key)) { |
| | | if (!"Y".equalsIgnoreCase(value) && !"N".equalsIgnoreCase(value)) { |
| | | throw new CoolException(key + " 仅支持 Y/N"); |
| | | } |
| | | return value.toUpperCase(); |
| | | } |
| | | if (FakeConfigKeys.LONG_KEYS.contains(key)) { |
| | | long parsed; |
| | | try { |
| | | parsed = Long.parseLong(value); |
| | | } catch (Exception e) { |
| | | throw new CoolException(key + " 必须为非负整数"); |
| | | } |
| | | if (parsed < 0) { |
| | | throw new CoolException(key + " 必须为非负整数"); |
| | | } |
| | | return String.valueOf(parsed); |
| | | } |
| | | throw new CoolException("不支持的配置项: " + key); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.asrs.domain.param; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.util.Map; |
| | | |
| | | @Data |
| | | public class FakeConfigSaveParam { |
| | | |
| | | private Map<String, Object> configMap; |
| | | } |
| | |
| | | @Component |
| | | public class ServerBootstrap { |
| | | |
| | | private static boolean initThread = false; |
| | | |
| | | @Autowired |
| | | private MainProcess mainProcess; |
| | | @Autowired |
| | |
| | | |
| | | @Async |
| | | public void init() throws InterruptedException { |
| | | if (initThread) { |
| | | return; |
| | | } |
| | | News.info("核心控制层开始初始化..............................................."); |
| | | clearStartupRuntimeLocks(); |
| | | // 初始化消息队列 |
| | |
| | | initThread(); |
| | | // 开始主流程进程 |
| | | mainProcess.start(); |
| | | initThread = true; |
| | | News.info("核心控制层已启动..............................................."); |
| | | } |
| | | |
| | |
| | | import com.zy.core.network.entity.ZyStationStatusEntity; |
| | | import java.util.List; |
| | | import com.zy.core.network.fake.ZyStationFakeSegConnect; |
| | | import com.zy.core.network.fake.ZyStationV4FakeSegConnect; |
| | | import com.zy.core.network.real.ZyStationRealConnect; |
| | | import com.zy.core.network.real.ZyStationV3RealConnect; |
| | | import com.zy.core.network.real.ZyStationV5RealConnect; |
| | |
| | | public class ZyStationConnectDriver implements ThreadHandler { |
| | | |
| | | private static final ZyStationFakeSegConnect zyStationFakeSegConnect = new ZyStationFakeSegConnect(); |
| | | private static final ZyStationV4FakeSegConnect zyStationV4FakeSegConnect = new ZyStationV4FakeSegConnect(); |
| | | private static final long SEND_LOCK_WARN_MS = 500L; |
| | | private static final long SEND_COST_WARN_MS = 5_000L; |
| | | private static final long SEND_LOCK_POLL_MS = 20L; |
| | |
| | | connectApi = new ZyStationRealConnect(deviceConfig, redisUtil); |
| | | } |
| | | } else { |
| | | if ("ZyStationV3Thread".equals(deviceConfig.getThreadImpl())) { |
| | | zyStationFakeSegConnect.addFakeConnect(deviceConfig, redisUtil); |
| | | connectApi = zyStationFakeSegConnect; |
| | | } else if ("ZyStationV5Thread".equals(deviceConfig.getThreadImpl())) { |
| | | zyStationV4FakeSegConnect.addFakeConnect(deviceConfig, redisUtil); |
| | | connectApi = zyStationV4FakeSegConnect; |
| | | } else { |
| | | fakeConfigUnsupported = true; |
| | | zyStationConnectApi = null; |
| | | log.error("旧版输送站 fake 已移除,deviceNo={}, threadImpl={}, 请切换到 ZyStationV3Thread 或 ZyStationV5Thread", |
| | | deviceConfig.getDeviceNo(), deviceConfig.getThreadImpl()); |
| | | return false; |
| | | } |
| | | zyStationFakeSegConnect.addFakeConnect(deviceConfig, redisUtil); |
| | | connectApi = zyStationFakeSegConnect; |
| | | } |
| | | |
| | | boolean connect = connectApi.connect(); |
| New file |
| | |
| | | package com.zy.core.network.fake; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | public final class FakeConfigKeys { |
| | | |
| | | public static final String ENABLE_FAKE = "enableFake"; |
| | | public static final String FAKE_REAL_TASK_REQUEST_WMS = "fakeRealTaskRequestWms"; |
| | | public static final String FAKE_GENERATE_IN_TASK = "fakeGenerateInTask"; |
| | | public static final String FAKE_GENERATE_OUT_TASK = "fakeGenerateOutTask"; |
| | | public static final String FAKE_ALLOW_CHECK_BLOCK = "fakeAllowCheckBlock"; |
| | | public static final String FAKE_RUN_BLOCK_TIMEOUT_MS = "fakeRunBlockTimeoutMs"; |
| | | |
| | | public static final String FAKE_CRN_FETCH_PUT_DURATION_MS = "fakeCrnFetchPutDurationMs"; |
| | | public static final String FAKE_CRN_LEVEL_STEP_DURATION_MS = "fakeCrnLevelStepDurationMs"; |
| | | public static final String FAKE_CRN_BAY_STEP_DURATION_MS = "fakeCrnBayStepDurationMs"; |
| | | public static final String FAKE_CRN_RESET_DURATION_MS = "fakeCrnResetDurationMs"; |
| | | |
| | | public static final String FAKE_DUAL_CRN_COMMAND_WAIT_MS = "fakeDualCrnCommandWaitMs"; |
| | | public static final String FAKE_DUAL_CRN_TRANSFER_DURATION_MS = "fakeDualCrnTransferDurationMs"; |
| | | public static final String FAKE_DUAL_CRN_PICK_DURATION_MS = "fakeDualCrnPickDurationMs"; |
| | | public static final String FAKE_DUAL_CRN_PUT_DURATION_MS = "fakeDualCrnPutDurationMs"; |
| | | public static final String FAKE_DUAL_CRN_LEVEL_STEP_DURATION_MS = "fakeDualCrnLevelStepDurationMs"; |
| | | public static final String FAKE_DUAL_CRN_BAY_STEP_DURATION_MS = "fakeDualCrnBayStepDurationMs"; |
| | | public static final String FAKE_DUAL_CRN_RESET_DURATION_MS = "fakeDualCrnResetDurationMs"; |
| | | |
| | | public static final String FAKE_RGV_MOVE_STEP_DURATION_MS = "fakeRgvMoveStepDurationMs"; |
| | | public static final String FAKE_RGV_LOAD_DURATION_MS = "fakeRgvLoadDurationMs"; |
| | | public static final String FAKE_RGV_RESET_DURATION_MS = "fakeRgvResetDurationMs"; |
| | | |
| | | public static final String FAKE_STATION_SEGMENT_WAIT_TIMEOUT_MS = "fakeStationSegmentWaitTimeoutMs"; |
| | | public static final String FAKE_STATION_MOVE_STEP_DURATION_MS = "fakeStationMoveStepDurationMs"; |
| | | public static final String FAKE_STATION_IDLE_LOOP_DELAY_MS = "fakeStationIdleLoopDelayMs"; |
| | | public static final String FAKE_STATION_BLOCKED_LOOP_DELAY_MS = "fakeStationBlockedLoopDelayMs"; |
| | | public static final String FAKE_STATION_INITIALIZE_DELAY_MS = "fakeStationInitializeDelayMs"; |
| | | public static final String FAKE_STATION_FINISH_DELAY_MS = "fakeStationFinishDelayMs"; |
| | | public static final String FAKE_OUT_STATION_STAY_TIMEOUT_MS = "fakeOutStationStayTimeoutMs"; |
| | | |
| | | public static final Map<String, String> DEFAULTS; |
| | | public static final Set<String> BOOLEAN_KEYS; |
| | | public static final Set<String> LONG_KEYS; |
| | | public static final Set<String> ALL_KEYS; |
| | | |
| | | static { |
| | | Map<String, String> defaults = new LinkedHashMap<>(); |
| | | defaults.put(ENABLE_FAKE, "N"); |
| | | defaults.put(FAKE_REAL_TASK_REQUEST_WMS, "N"); |
| | | defaults.put(FAKE_GENERATE_IN_TASK, "Y"); |
| | | defaults.put(FAKE_GENERATE_OUT_TASK, "Y"); |
| | | defaults.put(FAKE_ALLOW_CHECK_BLOCK, "Y"); |
| | | defaults.put(FAKE_RUN_BLOCK_TIMEOUT_MS, "20000"); |
| | | defaults.put(FAKE_CRN_FETCH_PUT_DURATION_MS, "2000"); |
| | | defaults.put(FAKE_CRN_LEVEL_STEP_DURATION_MS, "1000"); |
| | | defaults.put(FAKE_CRN_BAY_STEP_DURATION_MS, "1000"); |
| | | defaults.put(FAKE_CRN_RESET_DURATION_MS, "0"); |
| | | defaults.put(FAKE_DUAL_CRN_COMMAND_WAIT_MS, "200"); |
| | | defaults.put(FAKE_DUAL_CRN_TRANSFER_DURATION_MS, "2000"); |
| | | defaults.put(FAKE_DUAL_CRN_PICK_DURATION_MS, "3000"); |
| | | defaults.put(FAKE_DUAL_CRN_PUT_DURATION_MS, "3000"); |
| | | defaults.put(FAKE_DUAL_CRN_LEVEL_STEP_DURATION_MS, "1000"); |
| | | defaults.put(FAKE_DUAL_CRN_BAY_STEP_DURATION_MS, "500"); |
| | | defaults.put(FAKE_DUAL_CRN_RESET_DURATION_MS, "0"); |
| | | defaults.put(FAKE_RGV_MOVE_STEP_DURATION_MS, "1000"); |
| | | defaults.put(FAKE_RGV_LOAD_DURATION_MS, "1000"); |
| | | defaults.put(FAKE_RGV_RESET_DURATION_MS, "0"); |
| | | defaults.put(FAKE_STATION_SEGMENT_WAIT_TIMEOUT_MS, "30000"); |
| | | defaults.put(FAKE_STATION_MOVE_STEP_DURATION_MS, "500"); |
| | | defaults.put(FAKE_STATION_IDLE_LOOP_DELAY_MS, "200"); |
| | | defaults.put(FAKE_STATION_BLOCKED_LOOP_DELAY_MS, "1000"); |
| | | defaults.put(FAKE_STATION_INITIALIZE_DELAY_MS, "0"); |
| | | defaults.put(FAKE_STATION_FINISH_DELAY_MS, "0"); |
| | | defaults.put(FAKE_OUT_STATION_STAY_TIMEOUT_MS, "3000"); |
| | | DEFAULTS = Collections.unmodifiableMap(defaults); |
| | | |
| | | BOOLEAN_KEYS = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList( |
| | | ENABLE_FAKE, |
| | | FAKE_REAL_TASK_REQUEST_WMS, |
| | | FAKE_GENERATE_IN_TASK, |
| | | FAKE_GENERATE_OUT_TASK, |
| | | FAKE_ALLOW_CHECK_BLOCK |
| | | ))); |
| | | LONG_KEYS = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList( |
| | | FAKE_RUN_BLOCK_TIMEOUT_MS, |
| | | FAKE_CRN_FETCH_PUT_DURATION_MS, |
| | | FAKE_CRN_LEVEL_STEP_DURATION_MS, |
| | | FAKE_CRN_BAY_STEP_DURATION_MS, |
| | | FAKE_CRN_RESET_DURATION_MS, |
| | | FAKE_DUAL_CRN_COMMAND_WAIT_MS, |
| | | FAKE_DUAL_CRN_TRANSFER_DURATION_MS, |
| | | FAKE_DUAL_CRN_PICK_DURATION_MS, |
| | | FAKE_DUAL_CRN_PUT_DURATION_MS, |
| | | FAKE_DUAL_CRN_LEVEL_STEP_DURATION_MS, |
| | | FAKE_DUAL_CRN_BAY_STEP_DURATION_MS, |
| | | FAKE_DUAL_CRN_RESET_DURATION_MS, |
| | | FAKE_RGV_MOVE_STEP_DURATION_MS, |
| | | FAKE_RGV_LOAD_DURATION_MS, |
| | | FAKE_RGV_RESET_DURATION_MS, |
| | | FAKE_STATION_SEGMENT_WAIT_TIMEOUT_MS, |
| | | FAKE_STATION_MOVE_STEP_DURATION_MS, |
| | | FAKE_STATION_IDLE_LOOP_DELAY_MS, |
| | | FAKE_STATION_BLOCKED_LOOP_DELAY_MS, |
| | | FAKE_STATION_INITIALIZE_DELAY_MS, |
| | | FAKE_STATION_FINISH_DELAY_MS, |
| | | FAKE_OUT_STATION_STAY_TIMEOUT_MS |
| | | ))); |
| | | ALL_KEYS = Collections.unmodifiableSet(new LinkedHashSet<>(defaults.keySet())); |
| | | } |
| | | |
| | | private FakeConfigKeys() { |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.core.network.fake; |
| | | |
| | | import com.zy.system.service.ConfigService; |
| | | import com.core.common.SpringUtils; |
| | | |
| | | public final class FakeConfigSupport { |
| | | |
| | | private FakeConfigSupport() { |
| | | } |
| | | |
| | | public static String getString(String code) { |
| | | return getString(code, FakeConfigKeys.DEFAULTS.get(code)); |
| | | } |
| | | |
| | | public static String getString(String code, String defaultValue) { |
| | | ConfigService configService = SpringUtils.getBean(ConfigService.class); |
| | | if (configService == null) { |
| | | return defaultValue; |
| | | } |
| | | return configService.getConfigValue(code, defaultValue); |
| | | } |
| | | |
| | | public static long getLong(String code) { |
| | | String defaultValue = FakeConfigKeys.DEFAULTS.get(code); |
| | | long fallback = 0L; |
| | | if (defaultValue != null) { |
| | | try { |
| | | fallback = Long.parseLong(defaultValue); |
| | | } catch (Exception ignore) { |
| | | } |
| | | } |
| | | return getLong(code, fallback); |
| | | } |
| | | |
| | | public static long getLong(String code, long defaultValue) { |
| | | ConfigService configService = SpringUtils.getBean(ConfigService.class); |
| | | if (configService == null) { |
| | | return defaultValue; |
| | | } |
| | | return configService.getConfigLongValue(code, defaultValue); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.core.network.fake; |
| | | |
| | | import com.zy.asrs.entity.BasCrnp; |
| | | import com.zy.asrs.entity.BasDevp; |
| | | import com.zy.asrs.entity.BasDualCrnp; |
| | | import com.zy.asrs.service.BasCrnpService; |
| | | import com.zy.asrs.service.BasDevpService; |
| | | import com.zy.asrs.service.BasDualCrnpService; |
| | | import com.zy.core.News; |
| | | import com.zy.core.enums.RedisKeyType; |
| | | import com.zy.core.model.StationObjModel; |
| | | import com.zy.core.network.entity.ZyStationStatusEntity; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.core.common.SpringUtils; |
| | | |
| | | import java.util.*; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | /** |
| | | * 仿真堵塞管理器 |
| | | * 管理 fakeBlockedStations 集合,并通过 FakeStationStateManager 统一维护站点 runBlock 状态 |
| | | * 提供堵塞判定配置读取(fakeAllowCheckBlock、fakeRunBlockTimeoutMs) |
| | | */ |
| | | public class FakeStationBlockManager { |
| | | |
| | | private static final long DEFAULT_FAKE_RUN_BLOCK_TIMEOUT_MS = 20000L; |
| | | |
| | | // 仿真内部堵塞站点集合,用于仿真侧快速判定 |
| | | // 站点 runBlock 仍通过 FakeStationStateManager 统一写入,避免绕过集中状态管理 |
| | | private final Set<Integer> fakeBlockedStations = ConcurrentHashMap.newKeySet(); |
| | | private final FakeStationStateManager stateManager; |
| | | |
| | | public FakeStationBlockManager(FakeStationStateManager stateManager) { |
| | | this.stateManager = stateManager; |
| | | } |
| | | |
| | | public boolean isBlocked(Integer stationId) { |
| | | return stationId != null && fakeBlockedStations.contains(stationId); |
| | | } |
| | | |
| | | public void markBlocked(Integer stationId) { |
| | | if (stationId != null) { |
| | | fakeBlockedStations.add(stationId); |
| | | } |
| | | } |
| | | |
| | | public void clearBlocked(Integer stationId) { |
| | | if (stationId != null) { |
| | | fakeBlockedStations.remove(stationId); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 标记站点为堵塞状态 |
| | | * 仿真场景也同步设置 runBlock=true,确保堵塞重算路径、更换库位等业务逻辑可生效。 |
| | | */ |
| | | public boolean runBlockStation(Integer lockTaskNo, Integer currentStationId, |
| | | Integer currentStationDeviceNo, Integer taskNo, Integer blockStationId) { |
| | | boolean updated = stateManager.updateStationDataInternal( |
| | | currentStationId, currentStationDeviceNo, taskNo, blockStationId, true, "", true); |
| | | if (updated) { |
| | | fakeBlockedStations.add(currentStationId); |
| | | stateManager.publishTaskLocation(taskNo, currentStationDeviceNo, currentStationId, true, true); |
| | | News.info("[WCS Debug] 仿真站点堵塞上报,站点号={},设备号={},任务号={},阻塞目标站={},runBlock=true", |
| | | currentStationId, currentStationDeviceNo, taskNo, blockStationId); |
| | | } |
| | | return updated; |
| | | } |
| | | |
| | | /** |
| | | * 清除站点堵塞状态 |
| | | * 同时清除内部 fakeBlockedStations,并通过 FakeStationStateManager 统一清除 runBlock 标记 |
| | | */ |
| | | public void clearRunBlock(Integer stationId, Integer deviceNo) { |
| | | ZyStationStatusEntity status = stateManager.findStationStatus(deviceNo, stationId); |
| | | if (status == null) { |
| | | fakeBlockedStations.remove(stationId); |
| | | return; |
| | | } |
| | | if (status.isRunBlock()) { |
| | | stateManager.updateStationDataInternal(stationId, deviceNo, null, null, null, null, false); |
| | | stateManager.syncTaskLocation(deviceNo, stationId); |
| | | News.info("[WCS Debug] 站点{}堵塞标记已清除", stationId); |
| | | } |
| | | fakeBlockedStations.remove(stationId); |
| | | } |
| | | |
| | | public boolean isSpecialStation(Integer stationId) { |
| | | if (stationId == null) { return false; } |
| | | Set<Integer> specialStationIds = new HashSet<>(); |
| | | |
| | | BasDevpService basDevpService = SpringUtils.getBean(BasDevpService.class); |
| | | List<BasDevp> basDevps = basDevpService.list(); |
| | | for (BasDevp basDevp : basDevps) { |
| | | for (StationObjModel station : basDevp.getInStationList$()) { |
| | | specialStationIds.add(station.getStationId()); |
| | | } |
| | | for (StationObjModel station : basDevp.getOutStationList$()) { |
| | | specialStationIds.add(station.getStationId()); |
| | | } |
| | | for (StationObjModel station : basDevp.getBarcodeStationList$()) { |
| | | specialStationIds.add(station.getStationId()); |
| | | } |
| | | } |
| | | |
| | | BasCrnpService basCrnpService = SpringUtils.getBean(BasCrnpService.class); |
| | | List<BasCrnp> basCrnps = basCrnpService.list(); |
| | | for (BasCrnp basCrnp : basCrnps) { |
| | | for (StationObjModel station : basCrnp.getInStationList$()) { |
| | | specialStationIds.add(station.getStationId()); |
| | | } |
| | | for (StationObjModel station : basCrnp.getOutStationList$()) { |
| | | specialStationIds.add(station.getStationId()); |
| | | } |
| | | } |
| | | |
| | | BasDualCrnpService basDualCrnpService = SpringUtils.getBean(BasDualCrnpService.class); |
| | | List<BasDualCrnp> basDualCrnps = basDualCrnpService.list(); |
| | | for (BasDualCrnp basDualCrnp : basDualCrnps) { |
| | | for (StationObjModel station : basDualCrnp.getInStationList$()) { |
| | | specialStationIds.add(station.getStationId()); |
| | | } |
| | | for (StationObjModel station : basDualCrnp.getOutStationList$()) { |
| | | specialStationIds.add(station.getStationId()); |
| | | } |
| | | } |
| | | |
| | | return specialStationIds.contains(stationId); |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | public boolean getFakeAllowCheckBlock(RedisUtil redisUtil) { |
| | | boolean fakeAllowCheckBlock = true; |
| | | Object systemConfigMapObj = redisUtil == null ? null : redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); |
| | | if (systemConfigMapObj instanceof Map) { |
| | | Map<String, String> systemConfigMap = (Map<String, String>) systemConfigMapObj; |
| | | String value = systemConfigMap.get("fakeAllowCheckBlock"); |
| | | if (value != null && !"Y".equals(value)) { |
| | | fakeAllowCheckBlock = false; |
| | | } |
| | | } |
| | | return fakeAllowCheckBlock; |
| | | } |
| | | |
| | | public long getFakeRunBlockTimeoutMs(RedisUtil redisUtil) { |
| | | long timeoutMs = DEFAULT_FAKE_RUN_BLOCK_TIMEOUT_MS; |
| | | Object systemConfigMapObj = redisUtil == null ? null : redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); |
| | | if (systemConfigMapObj instanceof Map) { |
| | | Map<?, ?> systemConfigMap = (Map<?, ?>) systemConfigMapObj; |
| | | Object value = systemConfigMap.get("fakeRunBlockTimeoutMs"); |
| | | if (value != null) { |
| | | try { |
| | | long parsed = Long.parseLong(String.valueOf(value).trim()); |
| | | if (parsed > 0) { timeoutMs = parsed; } |
| | | } catch (Exception ignore) { } |
| | | } |
| | | } |
| | | return timeoutMs; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.core.network.fake; |
| | | |
| | | import com.zy.core.News; |
| | | import com.zy.core.network.entity.ZyStationStatusEntity; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.locks.ReentrantLock; |
| | | |
| | | /** |
| | | * 仿真站点移动引擎 |
| | | * 负责站点间的移动执行:初始化、移动到下一站、清除站点 |
| | | * 使用 ReentrantLock 做站点级并发控制 |
| | | */ |
| | | public class FakeStationMoveEngine { |
| | | |
| | | private final Map<Integer, ReentrantLock> stationLocks = new ConcurrentHashMap<>(); |
| | | private final FakeStationStateManager stateManager; |
| | | |
| | | public FakeStationMoveEngine(FakeStationStateManager stateManager) { |
| | | this.stateManager = stateManager; |
| | | } |
| | | |
| | | public boolean initStationMove(Integer lockTaskNo, Integer currentStationId, |
| | | Integer currentStationDeviceNo, Integer taskNo, Integer targetStationId, |
| | | Boolean isLoading, String barcode) { |
| | | lockStations(currentStationId); |
| | | try { |
| | | ZyStationStatusEntity currentStatus = stateManager.findStationStatus(currentStationDeviceNo, currentStationId); |
| | | if (currentStatus == null) { return false; } |
| | | if (currentStatus.getTaskNo() != null && currentStatus.getTaskNo() > 0 |
| | | && !currentStatus.getTaskNo().equals(taskNo) && currentStatus.isLoading()) { |
| | | return false; |
| | | } |
| | | boolean updated = stateManager.updateStationDataWithHeldLock(currentStationId, currentStationDeviceNo, |
| | | taskNo, targetStationId, isLoading, barcode, false); |
| | | if (updated) { |
| | | stateManager.publishTaskLocation(taskNo, currentStationDeviceNo, currentStationId, |
| | | Boolean.TRUE.equals(isLoading), false); |
| | | } |
| | | return updated; |
| | | } finally { |
| | | unlockStations(currentStationId); |
| | | } |
| | | } |
| | | |
| | | public boolean stationMoveToNext(Integer lockTaskNo, Integer currentStationId, |
| | | Integer currentStationDeviceNo, Integer nextStationId, Integer nextStationDeviceNo, |
| | | Integer taskNo, Integer targetStaNo) { |
| | | News.info("[WCS Debug] fake stationMoveToNext开始,taskNo={},currentStationId={},currentDeviceNo={},nextStationId={},nextDeviceNo={},targetStaNo={}", |
| | | taskNo, currentStationId, currentStationDeviceNo, nextStationId, nextStationDeviceNo, targetStaNo); |
| | | lockStations(currentStationId, nextStationId); |
| | | try { |
| | | ZyStationStatusEntity currentStatus = stateManager.findStationStatus(currentStationDeviceNo, currentStationId); |
| | | ZyStationStatusEntity nextStatus = stateManager.findStationStatus(nextStationDeviceNo, nextStationId); |
| | | if (currentStatus == null || nextStatus == null) { |
| | | News.info("仿真站点移动失败,原因=station_status_missing,taskNo={},currentStationId={},currentDeviceNo={},nextStationId={},nextDeviceNo={},targetStaNo={},currentStatusNull={},nextStatusNull={}", |
| | | taskNo, currentStationId, currentStationDeviceNo, nextStationId, nextStationDeviceNo, targetStaNo, |
| | | currentStatus == null, nextStatus == null); |
| | | return false; |
| | | } |
| | | if ((nextStatus.getTaskNo() != null && nextStatus.getTaskNo() > 0) || nextStatus.isLoading()) { |
| | | News.info("仿真站点移动失败,原因=next_station_busy,taskNo={},currentStationId={},nextStationId={},targetStaNo={},currentTaskNo={},currentLoading={},currentRunBlock={},nextTaskNo={},nextLoading={},nextRunBlock={},nextTargetStaNo={}", |
| | | taskNo, currentStationId, nextStationId, targetStaNo, |
| | | currentStatus.getTaskNo(), currentStatus.isLoading(), currentStatus.isRunBlock(), |
| | | nextStatus.getTaskNo(), nextStatus.isLoading(), nextStatus.isRunBlock(), nextStatus.getTargetStaNo()); |
| | | return false; |
| | | } |
| | | if (currentStatus.getTaskNo() == null || !taskNo.equals(currentStatus.getTaskNo()) || !currentStatus.isLoading()) { |
| | | News.info("仿真站点移动失败,原因=current_station_task_mismatch,taskNo={},currentStationId={},nextStationId={},currentTaskNo={},currentLoading={},currentTargetStaNo={}", |
| | | taskNo, currentStationId, nextStationId, |
| | | currentStatus.getTaskNo(), currentStatus.isLoading(), currentStatus.getTargetStaNo()); |
| | | return false; |
| | | } |
| | | |
| | | String barcode = currentStatus.getBarcode(); |
| | | News.info("[WCS Debug] fake handoff准备提交,taskNo={},sourceStationId={},sourceTaskNo={},sourceLoading={},sourceTargetStaNo={},sourceBarcode={},sourceRunBlock={},targetStationId={},targetTaskNo={},targetLoading={},targetTargetStaNo={},targetBarcode={},targetRunBlock={}", |
| | | taskNo, |
| | | currentStationId, currentStatus.getTaskNo(), currentStatus.isLoading(), currentStatus.getTargetStaNo(), currentStatus.getBarcode(), currentStatus.isRunBlock(), |
| | | nextStationId, nextStatus.getTaskNo(), nextStatus.isLoading(), nextStatus.getTargetStaNo(), nextStatus.getBarcode(), nextStatus.isRunBlock()); |
| | | |
| | | boolean prepared = stateManager.updateStationDataWithHeldLock(nextStationId, nextStationDeviceNo, |
| | | taskNo, targetStaNo, false, barcode, false); |
| | | if (!prepared) { |
| | | News.info("仿真站点移动失败,原因=next_station_prepare_failed,taskNo={},currentStationId={},nextStationId={},targetStaNo={}", |
| | | taskNo, currentStationId, nextStationId, targetStaNo); |
| | | return false; |
| | | } |
| | | boolean loaded = stateManager.updateStationDataWithHeldLock(nextStationId, nextStationDeviceNo, |
| | | null, null, true, null, false); |
| | | if (!loaded) { |
| | | News.info("仿真站点移动失败,原因=next_station_loading_commit_failed,taskNo={},currentStationId={},nextStationId={},targetStaNo={}", |
| | | taskNo, currentStationId, nextStationId, targetStaNo); |
| | | stateManager.updateStationDataWithHeldLock(nextStationId, nextStationDeviceNo, 0, 0, false, "", false); |
| | | return false; |
| | | } |
| | | boolean cleared = stateManager.updateStationDataWithHeldLock(currentStationId, currentStationDeviceNo, |
| | | 0, 0, false, "", false); |
| | | if (!cleared) { |
| | | News.info("仿真站点移动失败,原因=current_station_clear_failed,taskNo={},currentStationId={},nextStationId={},targetStaNo={},currentTaskNo={},currentLoading={},currentRunBlock={},nextTaskNo={},nextLoading={},nextRunBlock={}", |
| | | taskNo, currentStationId, nextStationId, targetStaNo, |
| | | currentStatus.getTaskNo(), currentStatus.isLoading(), currentStatus.isRunBlock(), |
| | | taskNo, true, false); |
| | | stateManager.updateStationDataWithHeldLock(nextStationId, nextStationDeviceNo, 0, 0, false, "", false); |
| | | return false; |
| | | } |
| | | News.info("[WCS Debug] fake handoff提交成功,taskNo={},sourceStationId={},targetStationId={},targetStaNo={},sourceCleared=true,targetLoading=true", |
| | | taskNo, currentStationId, nextStationId, targetStaNo); |
| | | |
| | | stateManager.clearTaskLocationIfMatches(taskNo, currentStationDeviceNo, currentStationId); |
| | | stateManager.publishTaskLocation(taskNo, nextStationDeviceNo, nextStationId, true, false); |
| | | return true; |
| | | } finally { |
| | | unlockStations(currentStationId, nextStationId); |
| | | } |
| | | } |
| | | |
| | | public boolean clearStation(Integer deviceNo, Integer lockTaskNo, Integer currentStationId) { |
| | | lockStations(currentStationId); |
| | | try { |
| | | ZyStationStatusEntity currentStatus = stateManager.findStationStatus(deviceNo, currentStationId); |
| | | if (currentStatus == null) { return false; } |
| | | Integer taskNo = currentStatus.getTaskNo(); |
| | | boolean cleared = stateManager.updateStationDataWithHeldLock(currentStationId, deviceNo, 0, 0, false, "", false); |
| | | if (cleared) { |
| | | stateManager.clearTaskLocationIfMatches(taskNo, deviceNo, currentStationId); |
| | | } |
| | | return cleared; |
| | | } finally { |
| | | unlockStations(currentStationId); |
| | | } |
| | | } |
| | | |
| | | private ReentrantLock getStationLock(Integer stationId) { |
| | | return stationLocks.computeIfAbsent(stationId, key -> new ReentrantLock()); |
| | | } |
| | | |
| | | public void lockStations(Integer... stationIds) { |
| | | Integer[] sorted = Arrays.copyOf(stationIds, stationIds.length); |
| | | Arrays.sort(sorted); |
| | | for (Integer stationId : sorted) { |
| | | getStationLock(stationId).lock(); |
| | | } |
| | | } |
| | | |
| | | public void unlockStations(Integer... stationIds) { |
| | | Integer[] sorted = Arrays.copyOf(stationIds, stationIds.length); |
| | | Arrays.sort(sorted); |
| | | for (int i = sorted.length - 1; i >= 0; i--) { |
| | | getStationLock(sorted[i]).unlock(); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.core.network.fake; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.zy.asrs.entity.DeviceConfig; |
| | | import com.zy.core.News; |
| | | import com.zy.core.model.CommandResponse; |
| | | import com.zy.core.model.protocol.StationTaskBufferItem; |
| | | import com.zy.core.network.entity.ZyStationStatusEntity; |
| | | import com.zy.core.thread.support.StationTaskLocationRegistry; |
| | | import com.core.common.SpringUtils; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Random; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.CopyOnWriteArrayList; |
| | | import java.util.concurrent.locks.ReentrantLock; |
| | | |
| | | public class FakeStationStateManager { |
| | | |
| | | private final StationTaskLocationRegistry stationTaskLocationRegistry = SpringUtils.getBean(StationTaskLocationRegistry.class); |
| | | |
| | | private interface StationMutation { |
| | | void apply(ZyStationStatusEntity status); |
| | | } |
| | | |
| | | private interface StationRead<T> { |
| | | T apply(ZyStationStatusEntity status); |
| | | } |
| | | |
| | | private final Map<Integer, List<ZyStationStatusEntity>> deviceStatusMap = new ConcurrentHashMap<Integer, List<ZyStationStatusEntity>>(); |
| | | private final Map<Integer, DeviceConfig> deviceConfigMap = new ConcurrentHashMap<Integer, DeviceConfig>(); |
| | | private final Map<Integer, ReentrantLock> stationLocks = new ConcurrentHashMap<Integer, ReentrantLock>(); |
| | | |
| | | private interface StationLockedMutation { |
| | | void apply(ZyStationStatusEntity status); |
| | | } |
| | | |
| | | private interface StationLockedRead<T> { |
| | | T apply(ZyStationStatusEntity status); |
| | | } |
| | | |
| | | private ReentrantLock getStationLock(Integer stationId) { |
| | | return stationLocks.computeIfAbsent(stationId, key -> new ReentrantLock()); |
| | | } |
| | | |
| | | public void lockStations(Integer... stationIds) { |
| | | if (stationIds == null || stationIds.length == 0) { |
| | | return; |
| | | } |
| | | Integer[] sorted = java.util.Arrays.copyOf(stationIds, stationIds.length); |
| | | java.util.Arrays.sort(sorted); |
| | | for (Integer stationId : sorted) { |
| | | if (stationId != null) { |
| | | getStationLock(stationId).lock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | public void unlockStations(Integer... stationIds) { |
| | | if (stationIds == null || stationIds.length == 0) { |
| | | return; |
| | | } |
| | | Integer[] sorted = java.util.Arrays.copyOf(stationIds, stationIds.length); |
| | | java.util.Arrays.sort(sorted); |
| | | for (int i = sorted.length - 1; i >= 0; i--) { |
| | | Integer stationId = sorted[i]; |
| | | if (stationId != null) { |
| | | getStationLock(stationId).unlock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private boolean mutateLockedStation(Integer deviceNo, Integer stationId, StationLockedMutation mutation) { |
| | | if (stationId == null || mutation == null) { |
| | | return false; |
| | | } |
| | | lockStations(stationId); |
| | | try { |
| | | ZyStationStatusEntity status = findStationStatus(deviceNo, stationId); |
| | | if (status == null) { |
| | | return false; |
| | | } |
| | | mutation.apply(status); |
| | | return true; |
| | | } finally { |
| | | unlockStations(stationId); |
| | | } |
| | | } |
| | | |
| | | private <T> T readLockedStation(Integer deviceNo, Integer stationId, StationLockedRead<T> read) { |
| | | if (stationId == null || read == null) { |
| | | return null; |
| | | } |
| | | lockStations(stationId); |
| | | try { |
| | | ZyStationStatusEntity status = findStationStatus(deviceNo, stationId); |
| | | if (status == null) { |
| | | return null; |
| | | } |
| | | return read.apply(status); |
| | | } finally { |
| | | unlockStations(stationId); |
| | | } |
| | | } |
| | | |
| | | private boolean updateStationDataWithinLock(Integer stationId, Integer deviceNo, Integer taskNo, Integer targetStaNo, |
| | | Boolean isLoading, String barcode, Boolean runBlock) { |
| | | ZyStationStatusEntity currentStatus = findStationStatus(deviceNo, stationId); |
| | | if (currentStatus == null) { |
| | | return false; |
| | | } |
| | | if (taskNo != null) { |
| | | currentStatus.setTaskNo(taskNo); |
| | | } |
| | | if (targetStaNo != null) { |
| | | currentStatus.setTargetStaNo(targetStaNo); |
| | | } |
| | | if (isLoading != null) { |
| | | currentStatus.setLoading(isLoading); |
| | | } |
| | | if (barcode != null) { |
| | | currentStatus.setBarcode(barcode); |
| | | } |
| | | if (runBlock != null) { |
| | | currentStatus.setRunBlock(runBlock); |
| | | } |
| | | syncTaskLocationInternal(deviceNo, currentStatus); |
| | | return true; |
| | | } |
| | | |
| | | |
| | | public void registerDevice(DeviceConfig deviceConfig) { |
| | | if (deviceConfigMap.containsKey(deviceConfig.getDeviceNo())) { |
| | | return; |
| | | } |
| | | deviceConfigMap.put(deviceConfig.getDeviceNo(), deviceConfig); |
| | | deviceStatusMap.put(deviceConfig.getDeviceNo(), new CopyOnWriteArrayList<ZyStationStatusEntity>()); |
| | | } |
| | | |
| | | public Map<Integer, List<ZyStationStatusEntity>> getDeviceStatusMap() { |
| | | return deviceStatusMap; |
| | | } |
| | | |
| | | public Map<Integer, DeviceConfig> getDeviceConfigMap() { |
| | | return deviceConfigMap; |
| | | } |
| | | |
| | | public List<ZyStationStatusEntity> getStatus(Integer deviceNo) { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return new ArrayList<ZyStationStatusEntity>(); |
| | | } |
| | | |
| | | DeviceConfig deviceConfig = deviceConfigMap.get(deviceNo); |
| | | if (statusList.isEmpty() && deviceConfig != null) { |
| | | List<ZyStationStatusEntity> init = JSON.parseArray(deviceConfig.getFakeInitStatus(), |
| | | ZyStationStatusEntity.class); |
| | | if (init != null) { |
| | | statusList.addAll(init); |
| | | for (ZyStationStatusEntity status : statusList) { |
| | | status.setAutoing(true); |
| | | status.setLoading(false); |
| | | status.setInEnable(true); |
| | | status.setOutEnable(true); |
| | | status.setIoMode(2); |
| | | status.setEmptyMk(false); |
| | | status.setFullPlt(false); |
| | | status.setRunBlock(false); |
| | | status.setPalletHeight(0); |
| | | status.setError(0); |
| | | status.setBarcode(""); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return statusList; |
| | | } |
| | | |
| | | public ZyStationStatusEntity findStationStatus(Integer deviceNo, Integer stationId) { |
| | | if (deviceNo == null || stationId == null) { |
| | | return null; |
| | | } |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return null; |
| | | } |
| | | return statusList.stream() |
| | | .filter(item -> item.getStationId().equals(stationId)) |
| | | .findFirst().orElse(null); |
| | | } |
| | | |
| | | public Integer getDeviceNoByStationId(Integer stationId) { |
| | | for (Map.Entry<Integer, List<ZyStationStatusEntity>> entry : deviceStatusMap.entrySet()) { |
| | | List<ZyStationStatusEntity> list = entry.getValue(); |
| | | if (list == null) { |
| | | continue; |
| | | } |
| | | for (ZyStationStatusEntity entity : list) { |
| | | if (entity.getStationId() != null && entity.getStationId().equals(stationId)) { |
| | | return entry.getKey(); |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | public Integer findCurrentStationIdByTask(Integer taskNo) { |
| | | for (List<ZyStationStatusEntity> list : deviceStatusMap.values()) { |
| | | if (list == null) { |
| | | continue; |
| | | } |
| | | for (ZyStationStatusEntity entity : list) { |
| | | Integer stationId = entity == null ? null : entity.getStationId(); |
| | | Integer matchedStationId = stationId == null ? null : readStation(getDeviceNoByStationId(stationId), stationId, |
| | | new StationRead<Integer>() { |
| | | @Override |
| | | public Integer apply(ZyStationStatusEntity status) { |
| | | if (status.getTaskNo() != null && status.getTaskNo().equals(taskNo) && status.isLoading()) { |
| | | return status.getStationId(); |
| | | } |
| | | return null; |
| | | } |
| | | }); |
| | | if (matchedStationId != null) { |
| | | return matchedStationId; |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | public String getThreadImpl(Integer deviceNo) { |
| | | DeviceConfig deviceConfig = deviceConfigMap.get(deviceNo); |
| | | return deviceConfig == null ? "" : deviceConfig.getThreadImpl(); |
| | | } |
| | | |
| | | public boolean isStationLoadingTask(Integer deviceNo, Integer stationId, Integer taskNo) { |
| | | Boolean result = readStation(deviceNo, stationId, new StationRead<Boolean>() { |
| | | @Override |
| | | public Boolean apply(ZyStationStatusEntity status) { |
| | | return status.getTaskNo() != null && status.getTaskNo().equals(taskNo) && status.isLoading(); |
| | | } |
| | | }); |
| | | return Boolean.TRUE.equals(result); |
| | | } |
| | | |
| | | public boolean isStationClearedForTask(Integer deviceNo, Integer stationId, Integer taskNo) { |
| | | Boolean result = readStation(deviceNo, stationId, new StationRead<Boolean>() { |
| | | @Override |
| | | public Boolean apply(ZyStationStatusEntity status) { |
| | | boolean emptyStation = !status.isLoading() |
| | | && (status.getTaskNo() == null || status.getTaskNo() <= 0); |
| | | return emptyStation; |
| | | } |
| | | }); |
| | | return result == null || result; |
| | | } |
| | | |
| | | public boolean isFinalStationOwnerConflict(Integer deviceNo, Integer stationId, Integer taskNo) { |
| | | Boolean result = readStation(deviceNo, stationId, new StationRead<Boolean>() { |
| | | @Override |
| | | public Boolean apply(ZyStationStatusEntity status) { |
| | | return status.isLoading() |
| | | && taskNo != null |
| | | && status.getTaskNo() != null |
| | | && status.getTaskNo() > 0 |
| | | && !taskNo.equals(status.getTaskNo()); |
| | | } |
| | | }); |
| | | return Boolean.TRUE.equals(result); |
| | | } |
| | | |
| | | public boolean isOccupiedByOtherLoadingTask(Integer deviceNo, Integer stationId, Integer taskNo) { |
| | | Boolean result = readStation(deviceNo, stationId, new StationRead<Boolean>() { |
| | | @Override |
| | | public Boolean apply(ZyStationStatusEntity status) { |
| | | return status.isLoading() |
| | | && status.getTaskNo() != null |
| | | && status.getTaskNo() > 0 |
| | | && (taskNo == null || !taskNo.equals(status.getTaskNo())); |
| | | } |
| | | }); |
| | | return Boolean.TRUE.equals(result); |
| | | } |
| | | |
| | | public ZyStationStatusEntity snapshotStation(Integer deviceNo, Integer stationId) { |
| | | return readStation(deviceNo, stationId, new StationRead<ZyStationStatusEntity>() { |
| | | @Override |
| | | public ZyStationStatusEntity apply(ZyStationStatusEntity status) { |
| | | return JSON.parseObject(JSON.toJSONString(status), ZyStationStatusEntity.class); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private boolean mutateStation(Integer deviceNo, Integer stationId, StationMutation mutation) { |
| | | if (mutation == null) { |
| | | return false; |
| | | } |
| | | return mutateLockedStation(deviceNo, stationId, new StationLockedMutation() { |
| | | @Override |
| | | public void apply(ZyStationStatusEntity status) { |
| | | mutation.apply(status); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private <T> T readStation(Integer deviceNo, Integer stationId, StationRead<T> read) { |
| | | if (read == null) { |
| | | return null; |
| | | } |
| | | return readLockedStation(deviceNo, stationId, new StationLockedRead<T>() { |
| | | @Override |
| | | public T apply(ZyStationStatusEntity status) { |
| | | return read.apply(status); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | public boolean updateStationDataWithHeldLock(Integer stationId, Integer deviceNo, Integer taskNo, Integer targetStaNo, |
| | | Boolean isLoading, String barcode, Boolean runBlock) { |
| | | return updateStationDataWithinLock(stationId, deviceNo, taskNo, targetStaNo, isLoading, barcode, runBlock); |
| | | } |
| | | |
| | | public boolean updateStationDataInternal(Integer stationId, Integer deviceNo, Integer taskNo, Integer targetStaNo, |
| | | Boolean isLoading, String barcode, Boolean runBlock) { |
| | | return mutateLockedStation(deviceNo, stationId, new StationLockedMutation() { |
| | | @Override |
| | | public void apply(ZyStationStatusEntity currentStatus) { |
| | | updateStationDataWithinLock(stationId, deviceNo, taskNo, targetStaNo, isLoading, barcode, runBlock); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | public void syncTaskLocation(Integer deviceNo, Integer stationId) { |
| | | if (deviceNo == null || stationId == null) { |
| | | return; |
| | | } |
| | | readStation(deviceNo, stationId, new StationRead<Void>() { |
| | | @Override |
| | | public Void apply(ZyStationStatusEntity status) { |
| | | syncTaskLocationInternal(deviceNo, status); |
| | | return null; |
| | | } |
| | | }); |
| | | } |
| | | |
| | | public void removeTaskLocation(Integer taskNo, Integer deviceNo, Integer stationId) { |
| | | if (stationTaskLocationRegistry == null || taskNo == null || taskNo <= 0) { |
| | | return; |
| | | } |
| | | stationTaskLocationRegistry.remove(taskNo, deviceNo, stationId); |
| | | } |
| | | |
| | | private void syncTaskLocationInternal(Integer deviceNo, ZyStationStatusEntity status) { |
| | | if (stationTaskLocationRegistry == null || deviceNo == null || status == null) { |
| | | return; |
| | | } |
| | | Integer taskNo = status.getTaskNo(); |
| | | if (taskNo != null && taskNo > 0 && status.isLoading() && status.getStationId() != null) { |
| | | stationTaskLocationRegistry.update(taskNo, deviceNo, status.getStationId(), true, |
| | | status.isRunBlock(), System.currentTimeMillis()); |
| | | } |
| | | } |
| | | |
| | | public void clearTaskLocationIfMatches(Integer taskNo, Integer deviceNo, Integer stationId) { |
| | | if (taskNo == null || taskNo <= 0 || deviceNo == null || stationId == null) { |
| | | return; |
| | | } |
| | | readStation(deviceNo, stationId, new StationRead<Void>() { |
| | | @Override |
| | | public Void apply(ZyStationStatusEntity status) { |
| | | if (status.getTaskNo() == null || !taskNo.equals(status.getTaskNo()) || !status.isLoading()) { |
| | | removeTaskLocation(taskNo, deviceNo, stationId); |
| | | } |
| | | return null; |
| | | } |
| | | }); |
| | | } |
| | | |
| | | public void publishTaskLocation(Integer taskNo, Integer deviceNo, Integer stationId, boolean loading, boolean runBlock) { |
| | | if (stationTaskLocationRegistry == null || taskNo == null || taskNo <= 0 || deviceNo == null || stationId == null || !loading) { |
| | | return; |
| | | } |
| | | stationTaskLocationRegistry.update(taskNo, deviceNo, stationId, true, runBlock, System.currentTimeMillis()); |
| | | } |
| | | |
| | | public void generateStationData(Integer deviceNo, Integer taskNo, Integer stationId, Integer targetStationId) { |
| | | mutateStation(deviceNo, stationId, new StationMutation() { |
| | | @Override |
| | | public void apply(ZyStationStatusEntity status) { |
| | | News.info("仿真站点生成任务数据,设备号={},站点号={},原taskNo={},新taskNo={},原targetStaNo={},新targetStaNo={},原loading={},barcode={}", |
| | | deviceNo, stationId, status.getTaskNo(), taskNo, status.getTargetStaNo(), targetStationId, |
| | | status.isLoading(), status.getBarcode()); |
| | | status.setTaskNo(taskNo); |
| | | status.setTargetStaNo(targetStationId); |
| | | status.setLoading(false); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | public void generateFakeOutStationData(Integer deviceNo, Integer stationId) { |
| | | mutateStation(deviceNo, stationId, new StationMutation() { |
| | | @Override |
| | | public void apply(ZyStationStatusEntity status) { |
| | | status.setLoading(true); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | public void resetStation(Integer deviceNo, Integer stationId) { |
| | | if (findStationStatus(deviceNo, stationId) == null) { |
| | | return; |
| | | } |
| | | clearStationForDispatch(deviceNo, stationId, "resetStation"); |
| | | } |
| | | |
| | | public void clearStationForDispatch(Integer deviceNo, Integer stationId, String reason) { |
| | | mutateStation(deviceNo, stationId, new StationMutation() { |
| | | @Override |
| | | public void apply(ZyStationStatusEntity status) { |
| | | News.info("仿真站点状态清除,设备号={},站点号={},原taskNo={},原targetStaNo={},原loading={},原barcode={},动作={}", |
| | | deviceNo, stationId, status.getTaskNo(), status.getTargetStaNo(), status.isLoading(), status.getBarcode(), reason); |
| | | status.setTaskNo(0); |
| | | status.setTargetStaNo(0); |
| | | status.setLoading(false); |
| | | status.setBarcode(""); |
| | | status.setRunBlock(false); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | public void handoffBarcodeStation(Integer deviceNo, Integer taskNo, Integer stationId, Integer targetStationId) { |
| | | mutateLockedStation(deviceNo, stationId, new StationLockedMutation() { |
| | | @Override |
| | | public void apply(ZyStationStatusEntity status) { |
| | | News.info("仿真站点生成任务数据,设备号={},站点号={},原taskNo={},新taskNo={},原targetStaNo={},新targetStaNo={},原loading={},barcode={}", |
| | | deviceNo, stationId, status.getTaskNo(), taskNo, status.getTargetStaNo(), targetStationId, |
| | | status.isLoading(), status.getBarcode()); |
| | | status.setTaskNo(taskNo); |
| | | status.setTargetStaNo(targetStationId); |
| | | status.setLoading(false); |
| | | News.info("仿真站点状态清除,设备号={},站点号={},原taskNo={},原targetStaNo={},原loading={},原barcode={},动作={}", |
| | | deviceNo, stationId, status.getTaskNo(), status.getTargetStaNo(), status.isLoading(), status.getBarcode(), "barcodeTaskHandoff"); |
| | | status.setTaskNo(0); |
| | | status.setTargetStaNo(0); |
| | | status.setLoading(false); |
| | | status.setBarcode(""); |
| | | status.setRunBlock(false); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | public void updateStationBarcode(Integer deviceNo, Integer stationId, String barcode) { |
| | | mutateStation(deviceNo, stationId, new StationMutation() { |
| | | @Override |
| | | public void apply(ZyStationStatusEntity status) { |
| | | status.setBarcode(barcode); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | public boolean generateStationBarcode(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo) { |
| | | ZyStationStatusEntity currentStatus = findStationStatus(currentStationDeviceNo, currentStationId); |
| | | if (currentStatus == null) { |
| | | return false; |
| | | } |
| | | |
| | | Random random = new Random(); |
| | | String barcodeTime = String.valueOf(System.currentTimeMillis()); |
| | | String barcode = String.valueOf(random.nextInt(10)) + String.valueOf(random.nextInt(10)) |
| | | + barcodeTime.substring(7); |
| | | return updateStationDataInternal(currentStationId, currentStationDeviceNo, null, null, null, barcode, null); |
| | | } |
| | | |
| | | public CommandResponse clearTaskBufferSlot(Integer deviceNo, Integer stationId, Integer slotIdx) { |
| | | if (deviceNo == null || stationId == null || slotIdx == null || slotIdx <= 0) { |
| | | return new CommandResponse(false, "清理缓存区槽位参数无效"); |
| | | } |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return new CommandResponse(false, "未找到设备状态"); |
| | | } |
| | | for (ZyStationStatusEntity status : statusList) { |
| | | if (status == null || !stationId.equals(status.getStationId())) { |
| | | continue; |
| | | } |
| | | List<StationTaskBufferItem> taskBufferItems = status.getTaskBufferItems(); |
| | | if (taskBufferItems == null || taskBufferItems.isEmpty()) { |
| | | return new CommandResponse(true, "缓存区槽位已为空"); |
| | | } |
| | | for (StationTaskBufferItem item : taskBufferItems) { |
| | | if (item != null && slotIdx.equals(item.getSlotIdx())) { |
| | | item.setTaskNo(0); |
| | | item.setTargetStaNo(0); |
| | | return new CommandResponse(true, "缓存区槽位清理成功"); |
| | | } |
| | | } |
| | | return new CommandResponse(false, "未找到缓存区槽位"); |
| | | } |
| | | return new CommandResponse(false, "未找到站点状态"); |
| | | } |
| | | } |
| | |
| | | private DeviceConfig deviceConfig; |
| | | private final ExecutorService executor = Executors.newSingleThreadExecutor(); |
| | | |
| | | private long fetchPutDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_CRN_FETCH_PUT_DURATION_MS); } |
| | | private long levelStepDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_CRN_LEVEL_STEP_DURATION_MS); } |
| | | private long bayStepDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_CRN_BAY_STEP_DURATION_MS); } |
| | | private long resetDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_CRN_RESET_DURATION_MS); } |
| | | |
| | | public ZyCrnFakeConnect(DeviceConfig deviceConfig) { |
| | | this.deviceConfig = deviceConfig; |
| | | this.crnStatus = JSON.parseObject(deviceConfig.getFakeInitStatus(), ZyCrnStatusEntity.class); |
| | |
| | | } |
| | | |
| | | private void commandTaskComplete(CrnCommand command) { |
| | | sleep(resetDurationMs()); |
| | | this.crnStatus.setLoaded(0); |
| | | this.crnStatus.setTaskNo(0); |
| | | this.crnStatus.setStatus(CrnStatusType.IDLE.id); |
| | | } |
| | |
| | | moveY(this.crnStatus.getBay(), sourcePosY); |
| | | moveZ(this.crnStatus.getLevel(), sourcePosZ); |
| | | this.crnStatus.setStatus(CrnStatusType.FETCHING.id); |
| | | sleep(2000); |
| | | sleep(fetchPutDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | moveY(this.crnStatus.getBay(), destinationPosY); |
| | | moveZ(this.crnStatus.getLevel(), destinationPosZ); |
| | | this.crnStatus.setStatus(CrnStatusType.PUTTING.id); |
| | | sleep(2000); |
| | | sleep(fetchPutDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | for(int i = 0; i < moveLength; i++) { |
| | | initSourcePosZ++; |
| | | this.crnStatus.setLevel(initSourcePosZ); |
| | | sleep(1000); |
| | | sleep(levelStepDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | for(int i = 0; i < moveLength; i++) { |
| | | initSourcePosZ--; |
| | | this.crnStatus.setLevel(initSourcePosZ); |
| | | sleep(1000); |
| | | sleep(levelStepDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | for(int i = 0; i < moveLength; i++) { |
| | | initSourcePosY++; |
| | | this.crnStatus.setBay(initSourcePosY); |
| | | sleep(1000); |
| | | sleep(bayStepDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | for(int i = 0; i < moveLength; i++) { |
| | | initSourcePosY--; |
| | | this.crnStatus.setBay(initSourcePosY); |
| | | sleep(1000); |
| | | sleep(bayStepDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | private DualCrnCommand station1LastCommand = null; |
| | | private DualCrnCommand station2LastCommand = null; |
| | | |
| | | private long commandWaitMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_DUAL_CRN_COMMAND_WAIT_MS); } |
| | | private long transferDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_DUAL_CRN_TRANSFER_DURATION_MS); } |
| | | private long pickDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_DUAL_CRN_PICK_DURATION_MS); } |
| | | private long putDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_DUAL_CRN_PUT_DURATION_MS); } |
| | | private long levelStepDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_DUAL_CRN_LEVEL_STEP_DURATION_MS); } |
| | | private long bayStepDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_DUAL_CRN_BAY_STEP_DURATION_MS); } |
| | | private long resetDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_DUAL_CRN_RESET_DURATION_MS); } |
| | | |
| | | public ZyDualCrnFakeConnect(DeviceConfig deviceConfig) { |
| | | this.deviceConfig = deviceConfig; |
| | | this.crnStatus = JSON.parseObject(deviceConfig.getFakeInitStatus(), ZyDualCrnStatusEntity.class); |
| | |
| | | } |
| | | |
| | | private void commandTaskComplete(DualCrnCommand command) { |
| | | sleep(resetDurationMs()); |
| | | if(command.getStation() == 1) { |
| | | if (crnStatus.getLoaded() == 0) { |
| | | this.crnStatus.setTaskNo(0); |
| | |
| | | if (station2LastCommand == null) { |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.IDLE.id); |
| | | }else { |
| | | if (station1LastCommand.getTaskMode().intValue() == DualCrnTaskModeType.PICK.id) { |
| | | if (station2LastCommand.getTaskMode().intValue() == DualCrnTaskModeType.PICK.id) { |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.FETCH_COMPLETE.id); |
| | | }else { |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.IDLE.id); |
| | |
| | | break; |
| | | } |
| | | |
| | | sleep(200); |
| | | sleep(commandWaitMs()); |
| | | } |
| | | |
| | | if(command.getStation() == 1) { |
| | |
| | | }else { |
| | | this.crnStatus.setTaskNoTwo(taskNo); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.PUT_MOVING.id); |
| | | this.crnStatus.setTaskReceive(1); |
| | | this.crnStatus.setTaskReceiveTwo(1); |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY, this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.WAITING.id); |
| | | } |
| | |
| | | break; |
| | | } |
| | | |
| | | sleep(200); |
| | | sleep(commandWaitMs()); |
| | | } |
| | | |
| | | if(command.getStation() == 1) { |
| | |
| | | |
| | | moveYZ(this.crnStatus.getBay(), sourcePosY,this.crnStatus.getLevel(), sourcePosZ,command.getStation()); |
| | | this.crnStatus.setStatus(DualCrnStatusType.FETCHING.id); |
| | | sleep(2000); |
| | | sleep(transferDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | this.crnStatus.setStatus(DualCrnStatusType.PUT_MOVING.id); |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY,this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatus(DualCrnStatusType.PUTTING.id); |
| | | sleep(2000); |
| | | sleep(transferDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | |
| | | moveYZ(this.crnStatus.getBay(), sourcePosY,this.crnStatus.getLevel(), sourcePosZ, command.getStation()); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.FETCHING.id); |
| | | sleep(2000); |
| | | sleep(transferDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.PUT_MOVING.id); |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY,this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.PUTTING.id); |
| | | sleep(2000); |
| | | sleep(transferDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | break; |
| | | } |
| | | |
| | | sleep(200); |
| | | sleep(commandWaitMs()); |
| | | } |
| | | |
| | | if(command.getStation() == 1) { |
| | |
| | | |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY,this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatus(DualCrnStatusType.FETCHING.id); |
| | | sleep(3000); |
| | | sleep(pickDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY,this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.FETCHING.id); |
| | | sleep(3000); |
| | | sleep(pickDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | break; |
| | | } |
| | | |
| | | sleep(200); |
| | | sleep(commandWaitMs()); |
| | | } |
| | | |
| | | if(command.getStation() == 1) { |
| | |
| | | |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY,this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatus(DualCrnStatusType.PUTTING.id); |
| | | sleep(3000); |
| | | sleep(putDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY,this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.PUTTING.id); |
| | | sleep(3000); |
| | | sleep(putDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | for(int i = 0; i < moveLength; i++) { |
| | | initSourcePosZ++; |
| | | this.crnStatus.setLevel(initSourcePosZ); |
| | | sleep(1000); |
| | | sleep(levelStepDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | initSourcePosZ--; |
| | | this.crnStatus.setLevel(initSourcePosZ); |
| | | this.crnStatus.setLevel(initSourcePosZ); |
| | | sleep(1000); |
| | | sleep(levelStepDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | for(int i = 0; i < moveLength; i++) { |
| | | initSourcePosY++; |
| | | this.crnStatus.setBay(initSourcePosY); |
| | | sleep(500); |
| | | sleep(bayStepDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | for(int i = 0; i < moveLength; i++) { |
| | | initSourcePosY--; |
| | | this.crnStatus.setBay(initSourcePosY); |
| | | sleep(500); |
| | | sleep(bayStepDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | private final DeviceConfig deviceConfig; |
| | | private final ExecutorService executor = Executors.newSingleThreadExecutor(); |
| | | |
| | | private long moveStepDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_RGV_MOVE_STEP_DURATION_MS); } |
| | | private long loadDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_RGV_LOAD_DURATION_MS); } |
| | | private long resetDurationMs() { return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_RGV_RESET_DURATION_MS); } |
| | | |
| | | public ZyRgvFakeConnect(DeviceConfig deviceConfig) { |
| | | this.deviceConfig = deviceConfig; |
| | | ZyRgvStatusEntity init = JSON.parseObject(deviceConfig.getFakeInitStatus(), ZyRgvStatusEntity.class); |
| | |
| | | Integer currentTrackSiteNo = valueObject.getInteger("trackSiteNo"); |
| | | |
| | | status.setRgvPos(currentTrackSiteNo); |
| | | sleep(1000); |
| | | sleep(moveStepDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | |
| | | status.setStatus(RgvStatusType.PUTTING.id); |
| | | status.setLoaded(1); |
| | | sleep(1000); |
| | | sleep(loadDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | Integer currentTrackSiteNo = valueObject.getInteger("trackSiteNo"); |
| | | |
| | | status.setRgvPos(currentTrackSiteNo); |
| | | sleep(1000); |
| | | sleep(moveStepDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | | } |
| | | |
| | | sleep(1000); |
| | | sleep(loadDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | Integer currentTrackSiteNo = valueObject.getInteger("trackSiteNo"); |
| | | |
| | | status.setRgvPos(currentTrackSiteNo); |
| | | sleep(1000); |
| | | sleep(moveStepDurationMs()); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | |
| | | } |
| | | |
| | | private void commandTaskComplete(RgvCommand command) { |
| | | sleep(resetDurationMs()); |
| | | status.setTaskNo(0); |
| | | status.setStatus(RgvStatusType.IDLE.id); |
| | | } |
| | |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.core.common.SpringUtils; |
| | | import com.zy.asrs.entity.BasDevp; |
| | | import com.zy.asrs.entity.DeviceConfig; |
| | | import com.zy.asrs.service.BasDevpService; |
| | | import com.zy.core.model.StationObjModel; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.News; |
| | | import com.zy.core.enums.RedisKeyType; |
| | | import com.zy.core.enums.StationCommandType; |
| | | import com.zy.core.model.CommandResponse; |
| | | import com.zy.core.model.command.StationCommand; |
| | | import com.zy.core.model.protocol.StationTaskBufferItem; |
| | | import com.zy.core.network.api.ZyStationConnectApi; |
| | | import com.zy.core.network.entity.ZyStationStatusEntity; |
| | | |
| | |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Random; |
| | | import java.util.Set; |
| | | import java.util.concurrent.BlockingQueue; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.CopyOnWriteArrayList; |
| | | import java.util.concurrent.ExecutorService; |
| | | import java.util.concurrent.Executors; |
| | | import java.util.concurrent.LinkedBlockingQueue; |
| | | import java.util.concurrent.ScheduledExecutorService; |
| | | import java.util.concurrent.ThreadFactory; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.concurrent.locks.ReentrantLock; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | public class ZyStationFakeSegConnect implements ZyStationConnectApi { |
| | | |
| | | private static final long DEFAULT_FAKE_RUN_BLOCK_TIMEOUT_MS = 10000L; |
| | | private static final long WAIT_SEGMENT_TIMEOUT_MS = 30000L; |
| | | private long getWaitSegmentTimeoutMs() { |
| | | return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_SEGMENT_WAIT_TIMEOUT_MS, 30000L); |
| | | } |
| | | |
| | | private long getMoveStepDurationMs() { |
| | | return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_MOVE_STEP_DURATION_MS, 500L); |
| | | } |
| | | |
| | | private long getIdleLoopDelayMs() { |
| | | return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_IDLE_LOOP_DELAY_MS, 200L); |
| | | } |
| | | |
| | | private long getBlockedLoopDelayMs() { |
| | | return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_BLOCKED_LOOP_DELAY_MS, 1000L); |
| | | } |
| | | |
| | | private long getInitializeDelayMs() { |
| | | return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_INITIALIZE_DELAY_MS, 0L); |
| | | } |
| | | |
| | | private long getFinishDelayMs() { |
| | | return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_FINISH_DELAY_MS, 0L); |
| | | } |
| | | |
| | | private static final String STATUS_WAITING = "WAITING"; |
| | | private static final String STATUS_RUNNING = "RUNNING"; |
| | |
| | | private static final String STATUS_TIMEOUT = "TIMEOUT"; |
| | | private static final String STATUS_FINISHED = "FINISHED"; |
| | | |
| | | private final Map<Integer, ReentrantLock> stationLocks = new ConcurrentHashMap<Integer, ReentrantLock>(); |
| | | private final Map<Integer, List<ZyStationStatusEntity>> deviceStatusMap = new ConcurrentHashMap<Integer, List<ZyStationStatusEntity>>(); |
| | | private final Map<Integer, DeviceConfig> deviceConfigMap = new ConcurrentHashMap<Integer, DeviceConfig>(); |
| | | private static final String SEGMENT_MERGE_APPEND = "APPEND"; |
| | | private static final String SEGMENT_MERGE_REPLACE_TARGET_CHANGED = "REPLACE_TARGET_CHANGED"; |
| | | private static final String SEGMENT_MERGE_REPLACE_REROUTE = "REPLACE_REROUTE"; |
| | | private static final String SEGMENT_MERGE_IGNORE_DISCONNECTED = "IGNORE_DISCONNECTED"; |
| | | private static final String SEGMENT_MERGE_IGNORE_CURRENT_MISSING = "IGNORE_CURRENT_MISSING"; |
| | | |
| | | private final FakeStationStateManager stateManager = new FakeStationStateManager(); |
| | | private final FakeStationMoveEngine moveEngine = new FakeStationMoveEngine(stateManager); |
| | | private final FakeStationBlockManager blockManager = new FakeStationBlockManager(stateManager); |
| | | private static final AtomicInteger DEVICE_EXECUTOR_THREAD_SEQ = new AtomicInteger(1); |
| | | private volatile ScheduledExecutorService loopScheduler = createLoopScheduler(); |
| | | private RedisUtil redisUtil; |
| | | private final Map<Integer, ExecutorService> deviceExecutors = new ConcurrentHashMap<Integer, ExecutorService>(); |
| | | |
| | | private final Map<Integer, BlockingQueue<StationCommand>> taskQueues = new ConcurrentHashMap<Integer, BlockingQueue<StationCommand>>(); |
| | | private final Map<Integer, Long> taskLastUpdateTime = new ConcurrentHashMap<Integer, Long>(); |
| | | private final Map<Integer, Boolean> taskRunning = new ConcurrentHashMap<Integer, Boolean>(); |
| | | private final ExecutorService executor = Executors.newCachedThreadPool(); |
| | | |
| | | private RedisUtil redisUtil; |
| | | private final Map<Integer, TaskRuntimeContext> taskContexts = new ConcurrentHashMap<Integer, TaskRuntimeContext>(); |
| | | private final Map<Integer, AtomicInteger> taskLoopGenerations = new ConcurrentHashMap<Integer, AtomicInteger>(); |
| | | private final Map<Integer, Object> taskLifecycleLocks = new ConcurrentHashMap<Integer, Object>(); |
| | | private volatile Set<Integer> legalClearStationIds = new HashSet<Integer>(); |
| | | private volatile Set<Integer> barcodeStationIds = new HashSet<Integer>(); |
| | | |
| | | public void addFakeConnect(DeviceConfig deviceConfig, RedisUtil redisUtil) { |
| | | this.redisUtil = redisUtil; |
| | | if (deviceConfigMap.containsKey(deviceConfig.getDeviceNo())) { |
| | | return; |
| | | } |
| | | deviceConfigMap.put(deviceConfig.getDeviceNo(), deviceConfig); |
| | | deviceStatusMap.put(deviceConfig.getDeviceNo(), new CopyOnWriteArrayList<ZyStationStatusEntity>()); |
| | | refreshLegalClearStationIds(); |
| | | refreshBarcodeStationIds(); |
| | | stateManager.registerDevice(deviceConfig); |
| | | } |
| | | |
| | | @Override |
| | | public boolean connect() { |
| | | if (loopScheduler == null || loopScheduler.isShutdown() || loopScheduler.isTerminated()) { |
| | | loopScheduler = createLoopScheduler(); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public boolean disconnect() { |
| | | executor.shutdownNow(); |
| | | ScheduledExecutorService scheduler = loopScheduler; |
| | | if (scheduler != null) { |
| | | scheduler.shutdownNow(); |
| | | } |
| | | for (ExecutorService executor : deviceExecutors.values()) { |
| | | executor.shutdownNow(); |
| | | } |
| | | deviceExecutors.clear(); |
| | | taskQueues.clear(); |
| | | taskLastUpdateTime.clear(); |
| | | taskRunning.clear(); |
| | | taskContexts.clear(); |
| | | taskLoopGenerations.clear(); |
| | | taskLifecycleLocks.clear(); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public List<ZyStationStatusEntity> getStatus(Integer deviceNo) { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return new ArrayList<ZyStationStatusEntity>(); |
| | | } |
| | | |
| | | DeviceConfig deviceConfig = deviceConfigMap.get(deviceNo); |
| | | if (statusList.isEmpty() && deviceConfig != null) { |
| | | List<ZyStationStatusEntity> init = JSON.parseArray(deviceConfig.getFakeInitStatus(), |
| | | ZyStationStatusEntity.class); |
| | | if (init != null) { |
| | | statusList.addAll(init); |
| | | for (ZyStationStatusEntity status : statusList) { |
| | | status.setAutoing(true); |
| | | status.setLoading(false); |
| | | status.setInEnable(true); |
| | | status.setOutEnable(true); |
| | | status.setEmptyMk(false); |
| | | status.setFullPlt(false); |
| | | status.setRunBlock(false); |
| | | status.setPalletHeight(0); |
| | | status.setError(0); |
| | | status.setBarcode(""); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return statusList; |
| | | return stateManager.getStatus(deviceNo); |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | |
| | | if (command.getCommandType() != StationCommandType.MOVE) { |
| | | handleCommand(deviceNo, command); |
| | | return new CommandResponse(true, "命令已受理(异步执行)"); |
| | | } |
| | | |
| | | if (isDirectMoveCommand(command)) { |
| | | handleDirectMoveCommand(deviceNo, command); |
| | | return new CommandResponse(true, "命令已受理(异步执行)"); |
| | | } |
| | | |
| | | taskQueues.computeIfAbsent(taskNo, key -> new LinkedBlockingQueue<StationCommand>()).offer(command); |
| | | taskLastUpdateTime.put(taskNo, System.currentTimeMillis()); |
| | | |
| | | if (taskRunning.putIfAbsent(taskNo, true) == null) { |
| | | executor.submit(new Runnable() { |
| | | News.info("[WCS Debug] fake sendCommand收到非MOVE命令,deviceNo={},taskNo={},stationId={},targetStaNo={},commandType={}", |
| | | deviceNo, taskNo, command.getStationId(), command.getTargetStaNo(), command.getCommandType()); |
| | | getDeviceExecutor(deviceNo).submit(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | runTaskLoop(deviceNo, taskNo); |
| | | handleCommand(deviceNo, command); |
| | | } |
| | | }); |
| | | return new CommandResponse(true, "命令已受理(异步执行)"); |
| | | } |
| | | |
| | | int loopGeneration = 0; |
| | | boolean shouldSchedule = false; |
| | | Object lifecycleLock = getTaskLifecycleLock(taskNo); |
| | | synchronized (lifecycleLock) { |
| | | BlockingQueue<StationCommand> queue = taskQueues.computeIfAbsent(taskNo, key -> new LinkedBlockingQueue<StationCommand>()); |
| | | queue.offer(command); |
| | | taskLastUpdateTime.put(taskNo, System.currentTimeMillis()); |
| | | News.info("[WCS Debug] fake sendCommand入队,deviceNo={},taskNo={},stationId={},targetStaNo={},segmentNo={},segmentCount={},queueSize={},running={}", |
| | | deviceNo, taskNo, command.getStationId(), command.getTargetStaNo(), command.getSegmentNo(), command.getSegmentCount(), |
| | | queue.size(), taskRunning.containsKey(taskNo)); |
| | | |
| | | if (taskRunning.putIfAbsent(taskNo, true) == null) { |
| | | TaskRuntimeContext context = new TaskRuntimeContext(taskNo, deviceNo, stateManager.getThreadImpl(deviceNo)); |
| | | taskContexts.put(taskNo, context); |
| | | AtomicInteger generation = taskLoopGenerations.computeIfAbsent(taskNo, key -> new AtomicInteger(0)); |
| | | loopGeneration = generation.incrementAndGet(); |
| | | context.loopGeneration = loopGeneration; |
| | | shouldSchedule = true; |
| | | News.info("[WCS Debug] fake task准备启动执行线程,deviceNo={},taskNo={},queueSize={},loopGeneration={}", |
| | | deviceNo, taskNo, queue.size(), loopGeneration); |
| | | } else { |
| | | News.info("[WCS Debug] fake task复用已存在执行线程,deviceNo={},taskNo={},queueSize={}", |
| | | deviceNo, taskNo, queue.size()); |
| | | } |
| | | } |
| | | if (shouldSchedule) { |
| | | scheduleTaskLoop(deviceNo, taskNo, loopGeneration); |
| | | } |
| | | |
| | | return new CommandResponse(true, "命令已受理(异步执行)"); |
| | |
| | | |
| | | @Override |
| | | public CommandResponse clearTaskBufferSlot(Integer deviceNo, Integer stationId, Integer slotIdx) { |
| | | if (deviceNo == null || stationId == null || slotIdx == null || slotIdx <= 0) { |
| | | return new CommandResponse(false, "清理缓存区槽位参数无效"); |
| | | } |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return new CommandResponse(false, "未找到设备状态"); |
| | | } |
| | | for (ZyStationStatusEntity status : statusList) { |
| | | if (status == null || !stationId.equals(status.getStationId())) { |
| | | continue; |
| | | } |
| | | List<StationTaskBufferItem> taskBufferItems = status.getTaskBufferItems(); |
| | | if (taskBufferItems == null || taskBufferItems.isEmpty()) { |
| | | return new CommandResponse(true, "缓存区槽位已为空"); |
| | | } |
| | | for (StationTaskBufferItem item : taskBufferItems) { |
| | | if (item != null && slotIdx.equals(item.getSlotIdx())) { |
| | | item.setTaskNo(0); |
| | | item.setTargetStaNo(0); |
| | | return new CommandResponse(true, "缓存区槽位清理成功"); |
| | | } |
| | | } |
| | | return new CommandResponse(false, "未找到缓存区槽位"); |
| | | } |
| | | return new CommandResponse(false, "未找到站点状态"); |
| | | return stateManager.clearTaskBufferSlot(deviceNo, stationId, slotIdx); |
| | | } |
| | | |
| | | @Override |
| | |
| | | return new byte[0]; |
| | | } |
| | | |
| | | private boolean isDirectMoveCommand(StationCommand command) { |
| | | if (command == null || command.getCommandType() != StationCommandType.MOVE) { |
| | | return false; |
| | | } |
| | | List<Integer> path = command.getNavigatePath(); |
| | | if (command.getStationId() == null || !command.getStationId().equals(command.getTargetStaNo())) { |
| | | return false; |
| | | } |
| | | if (path == null || path.isEmpty()) { |
| | | return true; |
| | | } |
| | | return path.size() == 1 && command.getStationId().equals(path.get(0)); |
| | | |
| | | private ScheduledExecutorService createLoopScheduler() { |
| | | return Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { |
| | | @Override |
| | | public Thread newThread(Runnable runnable) { |
| | | Thread thread = new Thread(runnable, "fake-station-loop-scheduler"); |
| | | thread.setDaemon(true); |
| | | return thread; |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private void handleDirectMoveCommand(Integer deviceNo, StationCommand command) { |
| | | Integer taskNo = command.getTaskNo(); |
| | | Integer stationId = command.getStationId(); |
| | | Integer targetStationId = command.getTargetStaNo(); |
| | | if (taskNo != null && taskNo > 0 && taskNo != 9999 && taskNo != 9998 && stationId != null |
| | | && stationId.equals(targetStationId)) { |
| | | generateStationData(deviceNo, taskNo, stationId, targetStationId); |
| | | } |
| | | |
| | | TaskRuntimeContext context = new TaskRuntimeContext(taskNo, getThreadImpl(deviceNo)); |
| | | context.startStationId = stationId; |
| | | context.currentStationId = stationId; |
| | | context.finalTargetStationId = targetStationId; |
| | | context.initialized = true; |
| | | context.status = STATUS_RUNNING; |
| | | context.appendStitchedPath(Arrays.asList(stationId)); |
| | | context.addPassedStation(stationId); |
| | | context.generateBarcode = checkTaskNoInArea(taskNo); |
| | | |
| | | traceEvent(deviceNo, context, "MOVE_INIT", "同站点任务直接到位", buildDetails("stationId", stationId), false); |
| | | if (context.generateBarcode) { |
| | | generateStationBarcode(taskNo, stationId, deviceNo); |
| | | } |
| | | traceEvent(deviceNo, context, "ARRIVED", "任务已在起点站点完成", buildDetails("barcodeGenerated", |
| | | context.generateBarcode, "stationId", stationId), false); |
| | | context.status = STATUS_FINISHED; |
| | | traceEvent(deviceNo, context, "TASK_END", "任务执行完成", buildDetails("reason", STATUS_FINISHED), true); |
| | | private ExecutorService getDeviceExecutor(Integer deviceNo) { |
| | | return deviceExecutors.computeIfAbsent(deviceNo, key -> Executors.newSingleThreadExecutor(new ThreadFactory() { |
| | | @Override |
| | | public Thread newThread(Runnable runnable) { |
| | | Thread thread = new Thread(runnable, |
| | | "fake-station-device-" + key + "-" + DEVICE_EXECUTOR_THREAD_SEQ.getAndIncrement()); |
| | | thread.setDaemon(true); |
| | | return thread; |
| | | } |
| | | })); |
| | | } |
| | | |
| | | private void runTaskLoop(Integer deviceNo, Integer taskNo) { |
| | | TaskRuntimeContext context = new TaskRuntimeContext(taskNo, getThreadImpl(deviceNo)); |
| | | try { |
| | | while (true) { |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | if (!isTerminalStatus(context.status)) { |
| | | context.status = STATUS_CANCELLED; |
| | | private void scheduleTaskLoop(Integer deviceNo, Integer taskNo, int loopGeneration) { |
| | | scheduleTaskLoop(deviceNo, taskNo, loopGeneration, 0L); |
| | | } |
| | | |
| | | private void scheduleTaskLoop(Integer deviceNo, Integer taskNo, int loopGeneration, long delayMs) { |
| | | Runnable task = new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | getDeviceExecutor(deviceNo).submit(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | runTaskLoop(deviceNo, taskNo, loopGeneration); |
| | | } |
| | | break; |
| | | } |
| | | }); |
| | | } |
| | | }; |
| | | if (delayMs <= 0L) { |
| | | task.run(); |
| | | return; |
| | | } |
| | | loopScheduler.schedule(task, delayMs, TimeUnit.MILLISECONDS); |
| | | } |
| | | |
| | | if (hasTaskReset(taskNo)) { |
| | | private void scheduleTaskLoopByDelay(Integer deviceNo, Integer taskNo, int loopGeneration, long delayMs) { |
| | | scheduleTaskLoop(deviceNo, taskNo, loopGeneration, Math.max(delayMs, 1L)); |
| | | } |
| | | |
| | | private void runTaskLoop(Integer deviceNo, Integer taskNo, int loopGeneration) { |
| | | TaskRuntimeContext context = taskContexts.get(taskNo); |
| | | if (context == null) { |
| | | News.info("[WCS Debug] fake task忽略无上下文续跑片段,deviceNo={},taskNo={},loopGeneration={}", |
| | | deviceNo, taskNo, loopGeneration); |
| | | return; |
| | | } |
| | | if (!deviceNo.equals(context.deviceNo)) { |
| | | News.info("[WCS Debug] fake task忽略跨设备续跑片段,taskNo={},expectedDeviceNo={},actualDeviceNo={},loopGeneration={}", |
| | | taskNo, context.deviceNo, deviceNo, loopGeneration); |
| | | return; |
| | | } |
| | | if (context.loopGeneration != loopGeneration || !isCurrentLoopGeneration(taskNo, loopGeneration)) { |
| | | News.info("[WCS Debug] fake task忽略过期续跑片段,deviceNo={},taskNo={},expectedLoopGeneration={},actualLoopGeneration={}", |
| | | deviceNo, taskNo, loopGeneration, context.loopGeneration); |
| | | return; |
| | | } |
| | | News.info("[WCS Debug] fake task进入runTaskLoop,deviceNo={},taskNo={},threadImpl={},queueExists={},queueSize={},loopGeneration={},status={},initialized={},currentStationId={},targetStationId={},blockedStationId={},pendingPath={}", |
| | | deviceNo, taskNo, context.threadImpl, taskQueues.containsKey(taskNo), |
| | | taskQueues.containsKey(taskNo) && taskQueues.get(taskNo) != null ? taskQueues.get(taskNo).size() : null, loopGeneration, |
| | | context.status, context.initialized, context.currentStationId, context.finalTargetStationId, |
| | | context.blockedStationId, context.getPendingStationIds()); |
| | | long nextDelayMs = 0L; |
| | | boolean shouldContinue = false; |
| | | try { |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | if (!isTerminalStatus(context.status)) { |
| | | context.status = STATUS_CANCELLED; |
| | | break; |
| | | } |
| | | |
| | | } else if (hasTaskReset(taskNo)) { |
| | | context.status = STATUS_CANCELLED; |
| | | } else { |
| | | BlockingQueue<StationCommand> commandQueue = taskQueues.get(taskNo); |
| | | if (commandQueue == null) { |
| | | break; |
| | | } |
| | | |
| | | StationCommand command = commandQueue.poll(100, TimeUnit.MILLISECONDS); |
| | | if (command != null) { |
| | | taskLastUpdateTime.put(taskNo, System.currentTimeMillis()); |
| | | context.lastCommandAt = System.currentTimeMillis(); |
| | | handleIncomingSegment(deviceNo, context, command); |
| | | } |
| | | |
| | | if (!context.pendingPathQueue.isEmpty()) { |
| | | if (!context.initialized || context.currentStationId == null) { |
| | | initializeTaskPosition(deviceNo, context); |
| | | continue; |
| | | context.status = STATUS_FINISHED; |
| | | } else { |
| | | StationCommand command = commandQueue.poll(); |
| | | if (command != null) { |
| | | taskLastUpdateTime.put(taskNo, System.currentTimeMillis()); |
| | | context.lastCommandAt = System.currentTimeMillis(); |
| | | handleIncomingSegment(deviceNo, context, command); |
| | | } |
| | | |
| | | if (!executeNextMove(deviceNo, context)) { |
| | | break; |
| | | if (!isTerminalStatus(context.status)) { |
| | | if (!context.pendingPathQueue.isEmpty()) { |
| | | News.info("[WCS Debug] fake task准备推进,deviceNo={},taskNo={},initialized={},currentStationId={},targetStationId={},blockedStationId={},pendingPath={}", |
| | | deviceNo, taskNo, context.initialized, context.currentStationId, |
| | | context.finalTargetStationId, context.blockedStationId, context.getPendingStationIds()); |
| | | if (!context.initialized || context.currentStationId == null) { |
| | | nextDelayMs = initializeTaskPosition(deviceNo, context); |
| | | } else { |
| | | MoveStepResult moveResult = executeNextMove(deviceNo, context); |
| | | if (!moveResult.shouldContinue()) { |
| | | context.status = STATUS_FINISHED; |
| | | } |
| | | nextDelayMs = moveResult.getNextDelayMs(); |
| | | } |
| | | } else { |
| | | News.info("[WCS Debug] fake task进入空闲态,deviceNo={},taskNo={},status={},currentStationId={},targetStationId={},blockedStationId={},pendingPath={}", |
| | | deviceNo, taskNo, context.status, context.currentStationId, |
| | | context.finalTargetStationId, context.blockedStationId, context.getPendingStationIds()); |
| | | IdleStepResult idleResult = handleIdleState(deviceNo, context); |
| | | if (idleResult.isFinished() && !isTerminalStatus(context.status)) { |
| | | context.status = STATUS_FINISHED; |
| | | } |
| | | nextDelayMs = idleResult.getNextDelayMs(); |
| | | } |
| | | } |
| | | continue; |
| | | shouldContinue = !isTerminalStatus(context.status) |
| | | && taskQueues.containsKey(taskNo) |
| | | && (commandQueue != null); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | context.status = STATUS_CANCELLED; |
| | | if (e instanceof InterruptedException) { |
| | | Thread.currentThread().interrupt(); |
| | | } |
| | | News.info("[WCS Debug] 任务{}执行异常,当前站点={},目标站={},待执行路径={},异常类型={},异常信息={}", |
| | | taskNo, context.currentStationId, context.finalTargetStationId, context.getPendingStationIds(), |
| | | e.getClass().getSimpleName(), e.getMessage()); |
| | | } |
| | | |
| | | if (handleIdleState(deviceNo, context)) { |
| | | break; |
| | | } |
| | | if (shouldContinue) { |
| | | if (nextDelayMs > 0L) { |
| | | scheduleTaskLoopByDelay(deviceNo, taskNo, loopGeneration, nextDelayMs); |
| | | } else { |
| | | scheduleTaskLoop(deviceNo, taskNo, loopGeneration); |
| | | } |
| | | } catch (InterruptedException e) { |
| | | Thread.currentThread().interrupt(); |
| | | if (!isTerminalStatus(context.status)) { |
| | | context.status = STATUS_CANCELLED; |
| | | return; |
| | | } |
| | | finishTaskLoop(deviceNo, taskNo, context, loopGeneration); |
| | | } |
| | | |
| | | private void finishTaskLoop(Integer deviceNo, Integer taskNo, TaskRuntimeContext context, int loopGeneration) { |
| | | Object lifecycleLock = getTaskLifecycleLock(taskNo); |
| | | synchronized (lifecycleLock) { |
| | | if (context.loopGeneration != loopGeneration || !isCurrentLoopGeneration(taskNo, loopGeneration)) { |
| | | News.info("[WCS Debug] fake task忽略过期结束清理,deviceNo={},taskNo={},expectedLoopGeneration={},actualLoopGeneration={}", |
| | | deviceNo, taskNo, loopGeneration, context.loopGeneration); |
| | | return; |
| | | } |
| | | } finally { |
| | | BlockingQueue<StationCommand> queue = taskQueues.get(taskNo); |
| | | if (queue != null && !queue.isEmpty()) { |
| | | News.info("[WCS Debug] fake task结束清理前发现新命令,恢复续跑,deviceNo={},taskNo={},queueSize={},loopGeneration={}", |
| | | deviceNo, taskNo, queue.size(), loopGeneration); |
| | | scheduleTaskLoop(deviceNo, taskNo, loopGeneration); |
| | | return; |
| | | } |
| | | News.info("[WCS Debug] fake task即将退出runTaskLoop,deviceNo={},taskNo={},status={},queueSizeBeforeCleanup={},lastCurrentStationId={},targetStationId={},loopGeneration={}", |
| | | deviceNo, taskNo, context.status, queue == null ? null : queue.size(), context.currentStationId, context.finalTargetStationId, loopGeneration); |
| | | taskQueues.remove(taskNo); |
| | | taskLastUpdateTime.remove(taskNo); |
| | | taskRunning.remove(taskNo); |
| | | |
| | | if (!isTerminalStatus(context.status)) { |
| | | context.status = STATUS_FINISHED; |
| | | } |
| | | traceEvent(deviceNo, context, "TASK_END", "任务执行结束并清理资源", |
| | | buildDetails("reason", context.status), true); |
| | | News.info("[WCS Debug] 任务{}执行结束并清理资源,状态={}", taskNo, context.status); |
| | | taskContexts.remove(taskNo); |
| | | taskLoopGenerations.remove(taskNo); |
| | | taskLifecycleLocks.remove(taskNo, lifecycleLock); |
| | | } |
| | | |
| | | if (!isTerminalStatus(context.status)) { |
| | | context.status = STATUS_FINISHED; |
| | | } |
| | | traceEvent(deviceNo, context, "TASK_END", "任务执行结束并清理资源", |
| | | buildDetails("reason", context.status, "loopGeneration", loopGeneration), true); |
| | | News.info("[WCS Debug] 任务{}执行结束并清理资源,状态={},loopGeneration={}", taskNo, context.status, loopGeneration); |
| | | } |
| | | |
| | | private void handleIncomingSegment(Integer deviceNo, TaskRuntimeContext context, StationCommand command) { |
| | | if (!deviceNo.equals(context.deviceNo)) { |
| | | traceEvent(deviceNo, context, "SEGMENT_IGNORED", "路径分段来自不同设备车道,已忽略", |
| | | buildDetails("expectedDeviceNo", context.deviceNo, "actualDeviceNo", deviceNo, |
| | | "segmentNo", command.getSegmentNo(), "segmentCount", command.getSegmentCount()), false); |
| | | return; |
| | | } |
| | | List<Integer> newPath = normalizePath(command.getNavigatePath()); |
| | | Integer lastInQueue = getLastInQueue(context.pendingPathQueue); |
| | | int startIndex = getPathAppendStartIndex(newPath, context.currentStationId, lastInQueue); |
| | | Integer previousTargetStationId = context.finalTargetStationId; |
| | | Integer commandTargetStationId = command.getTargetStaNo(); |
| | | boolean targetChanged = commandTargetStationId != null |
| | | && !commandTargetStationId.equals(previousTargetStationId); |
| | | boolean queueEmpty = context.pendingPathQueue.isEmpty(); |
| | | boolean newPathContainsCurrent = context.currentStationId != null && newPath.contains(context.currentStationId); |
| | | boolean pathConnectedToTail = startIndex >= 0; |
| | | List<Integer> oldPendingStations = context.getPendingStationIds(); |
| | | boolean shouldClearBarcodeSourceOnReroute = context.currentStationId != null |
| | | && context.currentStationId.equals(command.getStationId()) |
| | | && isBarcodeStation(context.currentStationId) |
| | | && previousTargetStationId != null |
| | | && previousTargetStationId.equals(context.currentStationId) |
| | | && commandTargetStationId != null |
| | | && !commandTargetStationId.equals(context.currentStationId); |
| | | |
| | | context.setStartStationIdIfAbsent(command.getStationId()); |
| | | if (!context.generateBarcode && checkTaskNoInArea(context.taskNo)) { |
| | |
| | | traceEvent(deviceNo, context, "SEGMENT_RECEIVED", "收到新的路径分段命令", |
| | | buildDetails("segmentPath", newPath, "appendStartIndex", startIndex, "currentStationId", |
| | | context.currentStationId, "queueTailStationId", lastInQueue, "commandStationId", |
| | | command.getStationId(), "commandTargetStationId", command.getTargetStaNo()), |
| | | command.getStationId(), "commandTargetStationId", commandTargetStationId, |
| | | "previousTargetStationId", previousTargetStationId), |
| | | false); |
| | | |
| | | Integer commandTargetStationId = command.getTargetStaNo(); |
| | | if (commandTargetStationId != null) { |
| | | if (!commandTargetStationId.equals(context.finalTargetStationId)) { |
| | | if (targetChanged) { |
| | | traceEvent(deviceNo, context, "TARGET_SWITCHED", |
| | | "任务目标站发生切换: " + context.finalTargetStationId + " -> " + commandTargetStationId, |
| | | buildDetails("fromTargetStationId", context.finalTargetStationId, "toTargetStationId", |
| | | "任务目标站发生切换: " + previousTargetStationId + " -> " + commandTargetStationId, |
| | | buildDetails("fromTargetStationId", previousTargetStationId, "toTargetStationId", |
| | | commandTargetStationId), |
| | | false); |
| | | context.arrivalHandled = false; |
| | |
| | | context.finalTargetStationId = commandTargetStationId; |
| | | syncCurrentStationTarget(context.taskNo, context.currentStationId, context.finalTargetStationId); |
| | | } |
| | | context.segmentNo = command.getSegmentNo(); |
| | | context.segmentCount = command.getSegmentCount(); |
| | | |
| | | if (!newPath.isEmpty() && startIndex < 0) { |
| | | traceEvent(deviceNo, context, "SEGMENT_IGNORED", "路径分段无法与当前运行上下文衔接,已忽略", |
| | | buildDetails("segmentPath", newPath, "currentStationId", context.currentStationId, |
| | | "queueTailStationId", lastInQueue, "ignoreReason", "PATH_NOT_CONNECTED"), |
| | | false); |
| | | context.latestAppendedPath.clear(); |
| | | return; |
| | | boolean tailConnectedAppend = !queueEmpty && pathConnectedToTail; |
| | | boolean shouldReplace = !tailConnectedAppend && !queueEmpty |
| | | && (targetChanged || (!pathConnectedToTail && newPathContainsCurrent)); |
| | | String ignoreReason = null; |
| | | if (!shouldReplace && !tailConnectedAppend && !newPath.isEmpty() && !pathConnectedToTail) { |
| | | ignoreReason = newPathContainsCurrent ? SEGMENT_MERGE_IGNORE_DISCONNECTED : SEGMENT_MERGE_IGNORE_CURRENT_MISSING; |
| | | if (queueEmpty && context.currentStationId == null) { |
| | | startIndex = 0; |
| | | } else { |
| | | traceEvent(deviceNo, context, "SEGMENT_IGNORED", "路径分段无法接入当前运行上下文,已忽略", |
| | | buildDetails("segmentPath", newPath, "currentStationId", context.currentStationId, |
| | | "queueTailStationId", lastInQueue, "ignoreReason", ignoreReason, |
| | | "queueEmpty", queueEmpty, "tailConnectedAppend", tailConnectedAppend, |
| | | "targetChanged", targetChanged), |
| | | false); |
| | | context.latestAppendedPath.clear(); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | if (tailConnectedAppend && startIndex < 0) { |
| | | startIndex = 0; |
| | | } |
| | | |
| | | List<Integer> appendedPath = new ArrayList<Integer>(); |
| | | for (int i = startIndex; i < newPath.size(); i++) { |
| | | Integer stationId = newPath.get(i); |
| | | context.pendingPathQueue.offer(stationId); |
| | | appendedPath.add(stationId); |
| | | } |
| | | context.appendStitchedPath(appendedPath); |
| | | |
| | | if (!appendedPath.isEmpty()) { |
| | | List<Integer> replacedFuturePath = new ArrayList<Integer>(); |
| | | Integer duplicateHeadStationId = null; |
| | | String mergeMode = SEGMENT_MERGE_APPEND; |
| | | if (shouldReplace) { |
| | | replacedFuturePath = rebuildPendingPathFromCurrent(context, newPath); |
| | | boolean atTargetAlready = context.currentStationId != null && context.currentStationId.equals(commandTargetStationId); |
| | | if (context.currentStationId != null && replacedFuturePath.isEmpty() && !atTargetAlready) { |
| | | traceEvent(deviceNo, context, "SEGMENT_IGNORED", "路径分段无法接入当前运行上下文,已忽略", |
| | | buildDetails("segmentPath", newPath, "currentStationId", context.currentStationId, |
| | | "queueTailStationId", lastInQueue, "ignoreReason", SEGMENT_MERGE_IGNORE_CURRENT_MISSING), |
| | | false); |
| | | context.latestAppendedPath.clear(); |
| | | return; |
| | | } |
| | | replacePendingPathQueue(context, replacedFuturePath); |
| | | context.replaceLatestPath(replacedFuturePath); |
| | | mergeMode = targetChanged ? SEGMENT_MERGE_REPLACE_TARGET_CHANGED : SEGMENT_MERGE_REPLACE_REROUTE; |
| | | traceEvent(deviceNo, context, "SEGMENT_REPLACED", |
| | | "新的 reroute 路径已覆盖旧 future queue", |
| | | buildDetails("mergeMode", mergeMode, "currentStationId", context.currentStationId, |
| | | "previousTargetStationId", previousTargetStationId, "newTargetStationId", commandTargetStationId, |
| | | "oldQueue", oldPendingStations, "replacedFuturePath", replacedFuturePath, |
| | | "queueSize", context.pendingPathQueue.size()), |
| | | false); |
| | | } else { |
| | | for (int i = startIndex; i < newPath.size(); i++) { |
| | | Integer stationId = newPath.get(i); |
| | | context.pendingPathQueue.offer(stationId); |
| | | appendedPath.add(stationId); |
| | | } |
| | | if (context.currentStationId != null && context.currentStationId.equals(context.pendingPathQueue.peek())) { |
| | | duplicateHeadStationId = context.pendingPathQueue.poll(); |
| | | if (!appendedPath.isEmpty() && duplicateHeadStationId.equals(appendedPath.get(0))) { |
| | | appendedPath.remove(0); |
| | | } |
| | | } |
| | | context.appendStitchedPath(appendedPath); |
| | | traceEvent(deviceNo, context, "SEGMENT_APPENDED", |
| | | "路径分段已追加到待执行队列,队列长度=" + context.pendingPathQueue.size(), |
| | | buildDetails("segmentPath", newPath, "appendedPath", appendedPath, "appendStartIndex", |
| | | startIndex, "queueSize", context.pendingPathQueue.size()), |
| | | buildDetails("mergeMode", mergeMode, "segmentPath", newPath, "appendedPath", appendedPath, |
| | | "appendStartIndex", startIndex, "queueSize", context.pendingPathQueue.size(), |
| | | "tailConnectedAppend", tailConnectedAppend, "targetChanged", targetChanged, |
| | | "duplicateHeadStationId", duplicateHeadStationId), |
| | | false); |
| | | } |
| | | |
| | | if (duplicateHeadStationId != null) { |
| | | traceEvent(deviceNo, context, "SEGMENT_TRIMMED", "待执行队列头部重复当前站点,已移除", |
| | | buildDetails("currentStationId", context.currentStationId, |
| | | "duplicateHeadStationId", duplicateHeadStationId, |
| | | "queueSize", context.pendingPathQueue.size(), |
| | | "remainingPendingPath", context.getPendingStationIds()), |
| | | false); |
| | | } |
| | | |
| | | if (context.currentStationId != null && context.currentStationId.equals(context.pendingPathQueue.peek())) { |
| | | Integer trimmedHead = context.pendingPathQueue.poll(); |
| | | traceEvent(deviceNo, context, "SEGMENT_TRIMMED", "待执行队列头部仍与当前站点重复,已再次移除", |
| | | buildDetails("currentStationId", context.currentStationId, |
| | | "duplicateHeadStationId", trimmedHead, |
| | | "queueSize", context.pendingPathQueue.size(), |
| | | "remainingPendingPath", context.getPendingStationIds()), |
| | | false); |
| | | } |
| | | |
| | | context.lastCommandAt = System.currentTimeMillis(); |
| | | |
| | | boolean segmentAccepted = !appendedPath.isEmpty() || !replacedFuturePath.isEmpty() |
| | | || (context.currentStationId != null && context.currentStationId.equals(commandTargetStationId)) |
| | | || (context.currentStationId != null && context.currentStationId.equals(command.getStationId()) |
| | | && commandTargetStationId != null && commandTargetStationId.equals(context.finalTargetStationId)); |
| | | if (segmentAccepted) { |
| | | if (shouldClearBarcodeSourceOnReroute && context.currentStationId != null) { |
| | | Integer currentDeviceNo = stateManager.getDeviceNoByStationId(context.currentStationId); |
| | | if (currentDeviceNo != null) { |
| | | guardedClearStationForDispatch(currentDeviceNo, context.currentStationId, context.taskNo, "barcodeSourceRerouteAccepted"); |
| | | traceEvent(deviceNo, context, "SOURCE_STATION_CLEARED", "条码源站已完成任务交接,源站状态已清除", |
| | | buildDetails("stationId", context.currentStationId, |
| | | "previousTargetStationId", previousTargetStationId, |
| | | "newTargetStationId", commandTargetStationId, |
| | | "mergeMode", mergeMode), |
| | | false); |
| | | } |
| | | } |
| | | resumeFromCurrentStation(deviceNo, context, mergeMode, appendedPath, replacedFuturePath, |
| | | shouldClearBarcodeSourceOnReroute); |
| | | } |
| | | } |
| | | |
| | | private void initializeTaskPosition(Integer deviceNo, TaskRuntimeContext context) { |
| | | private long initializeTaskPosition(Integer deviceNo, TaskRuntimeContext context) { |
| | | Integer nextStationId = context.pendingPathQueue.peek(); |
| | | if (nextStationId == null) { |
| | | return; |
| | | return 0L; |
| | | } |
| | | |
| | | if (context.currentStationId == null) { |
| | | Integer actualCurrentStationId = findCurrentStationIdByTask(context.taskNo); |
| | | Integer actualCurrentStationId = stateManager.findCurrentStationIdByTask(context.taskNo); |
| | | if (actualCurrentStationId != null) { |
| | | context.currentStationId = actualCurrentStationId; |
| | | context.initialized = true; |
| | | context.status = STATUS_RUNNING; |
| | | context.blockedStationId = null; |
| | | |
| | | Integer actualDeviceNo = getDeviceNoByStationId(actualCurrentStationId); |
| | | Integer actualDeviceNo = stateManager.getDeviceNoByStationId(actualCurrentStationId); |
| | | if (actualDeviceNo != null) { |
| | | clearRunBlock(actualCurrentStationId, actualDeviceNo); |
| | | guardedClearRunBlock(context.taskNo, actualCurrentStationId, actualDeviceNo); |
| | | } |
| | | |
| | | trimPendingPathToCurrent(context.pendingPathQueue, actualCurrentStationId); |
| | |
| | | |
| | | context.addPassedStation(actualCurrentStationId); |
| | | context.lastStepAt = System.currentTimeMillis(); |
| | | context.lastProgressAt = context.lastStepAt; |
| | | context.lastProgressStationId = context.currentStationId; |
| | | guardedPublishTaskLocation(context.taskNo, actualDeviceNo, actualCurrentStationId, true, false); |
| | | traceEvent(deviceNo, context, "MOVE_INIT", "任务从当前实际站点恢复执行", |
| | | buildDetails("stationId", actualCurrentStationId, "recovered", true), false); |
| | | return; |
| | | return 0L; |
| | | } |
| | | } |
| | | |
| | | context.currentStationId = nextStationId; |
| | | Integer currentDeviceNo = getDeviceNoByStationId(context.currentStationId); |
| | | Integer currentDeviceNo = stateManager.getDeviceNoByStationId(context.currentStationId); |
| | | if (currentDeviceNo == null) { |
| | | context.pendingPathQueue.poll(); |
| | | return; |
| | | return 0L; |
| | | } |
| | | |
| | | boolean result = initStationMove(context.taskNo, context.currentStationId, currentDeviceNo, context.taskNo, |
| | | boolean result = moveEngine.initStationMove(context.taskNo, context.currentStationId, currentDeviceNo, context.taskNo, |
| | | context.finalTargetStationId, true, null); |
| | | if (!result) { |
| | | sleep(200); |
| | | return; |
| | | return getIdleLoopDelayMs(); |
| | | } |
| | | |
| | | context.initialized = true; |
| | |
| | | context.pendingPathQueue.poll(); |
| | | context.addPassedStation(context.currentStationId); |
| | | context.lastStepAt = System.currentTimeMillis(); |
| | | context.lastProgressAt = context.lastStepAt; |
| | | context.lastProgressStationId = context.currentStationId; |
| | | guardedPublishTaskLocation(context.taskNo, currentDeviceNo, context.currentStationId, true, false); |
| | | traceEvent(deviceNo, context, "MOVE_INIT", "任务初始化起点站点", |
| | | buildDetails("stationId", context.currentStationId, "recovered", false), false); |
| | | sleep(500); |
| | | return Math.max(getInitializeDelayMs(), getMoveStepDurationMs()); |
| | | } |
| | | |
| | | private boolean executeNextMove(Integer deviceNo, TaskRuntimeContext context) { |
| | | private MoveStepResult executeNextMove(Integer deviceNo, TaskRuntimeContext context) { |
| | | Integer nextStationId = context.pendingPathQueue.peek(); |
| | | if (nextStationId == null || context.currentStationId == null) { |
| | | return true; |
| | | return MoveStepResult.continueNow(); |
| | | } |
| | | |
| | | Integer currentDeviceNo = getDeviceNoByStationId(context.currentStationId); |
| | | Integer nextDeviceNo = getDeviceNoByStationId(nextStationId); |
| | | Integer currentDeviceNo = stateManager.getDeviceNoByStationId(context.currentStationId); |
| | | Integer nextDeviceNo = stateManager.getDeviceNoByStationId(nextStationId); |
| | | if (currentDeviceNo == null || nextDeviceNo == null) { |
| | | context.pendingPathQueue.poll(); |
| | | return true; |
| | | return MoveStepResult.continueNow(); |
| | | } |
| | | |
| | | boolean moveSuccess = stationMoveToNext(context.taskNo, context.currentStationId, currentDeviceNo, |
| | | boolean moveSuccess = moveEngine.stationMoveToNext(context.taskNo, context.currentStationId, currentDeviceNo, |
| | | nextStationId, nextDeviceNo, context.taskNo, context.finalTargetStationId); |
| | | if (moveSuccess) { |
| | | Integer previousStationId = context.currentStationId; |
| | |
| | | context.blockedStationId = null; |
| | | context.status = STATUS_RUNNING; |
| | | context.lastStepAt = System.currentTimeMillis(); |
| | | context.lastProgressAt = context.lastStepAt; |
| | | context.lastProgressStationId = context.currentStationId; |
| | | blockManager.clearBlocked(previousStationId); |
| | | blockManager.clearBlocked(nextStationId); |
| | | traceEvent(deviceNo, context, "MOVE_STEP_OK", "任务完成一步站点移动", |
| | | buildDetails("fromStationId", previousStationId, "toStationId", nextStationId, |
| | | "remainingPendingPath", context.getPendingStationIds()), |
| | | false); |
| | | sleep(1000); |
| | | return true; |
| | | return MoveStepResult.continueAfter(getBlockedLoopDelayMs()); |
| | | } |
| | | |
| | | if (!checkTaskNoInArea(context.taskNo) && getFakeAllowCheckBlock() |
| | | && !isSpecialStation(context.currentStationId) |
| | | && System.currentTimeMillis() - context.lastStepAt > getFakeRunBlockTimeoutMs()) { |
| | | boolean blocked = runBlockStation(context.taskNo, context.currentStationId, currentDeviceNo, context.taskNo, |
| | | long now = System.currentTimeMillis(); |
| | | boolean sameStationNoProgress = context.currentStationId != null |
| | | && context.currentStationId.equals(context.lastProgressStationId); |
| | | long timeoutMs = blockManager.getFakeRunBlockTimeoutMs(redisUtil); |
| | | long dwellMs = now - context.lastProgressAt; |
| | | if (!checkTaskNoInArea(context.taskNo) && blockManager.getFakeAllowCheckBlock(redisUtil) |
| | | && !blockManager.isSpecialStation(context.currentStationId) |
| | | && !blockManager.isBlocked(context.currentStationId) |
| | | && sameStationNoProgress |
| | | && dwellMs > timeoutMs) { |
| | | List<Integer> clearedPendingPath = context.getPendingStationIds(); |
| | | News.info("[WCS Debug] fake task清空待执行路径,原因=RUN_BLOCKED,deviceNo={},taskNo={},currentStationId={},targetStationId={},blockedStationId={},pendingBeforeClear={}", |
| | | deviceNo, context.taskNo, context.currentStationId, context.finalTargetStationId, |
| | | context.blockedStationId, clearedPendingPath); |
| | | context.clearPendingPathWithLog(deviceNo, context.taskNo, context.currentStationId, |
| | | context.finalTargetStationId, context.blockedStationId, "RUN_BLOCKED"); |
| | | guardedRunBlockStation(context.taskNo, context.currentStationId, currentDeviceNo, context.taskNo, |
| | | context.currentStationId); |
| | | if (blocked) { |
| | | context.blockedStationId = context.currentStationId; |
| | | context.status = STATUS_BLOCKED; |
| | | context.pendingPathQueue.clear(); |
| | | traceEvent(deviceNo, context, "RUN_BLOCKED", "任务在当前站点被标记为堵塞", |
| | | buildDetails("blockedStationId", context.currentStationId), false); |
| | | return false; |
| | | } |
| | | context.blockedStationId = context.currentStationId; |
| | | context.status = STATUS_BLOCKED; |
| | | traceEvent(deviceNo, context, "RUN_BLOCKED", "任务在当前站点停留超时,被标记为堵塞,待执行路径已清空", |
| | | buildDetails("blockedStationId", context.currentStationId, |
| | | "lastProgressStationId", context.lastProgressStationId, |
| | | "dwellMs", dwellMs, |
| | | "timeoutMs", timeoutMs, |
| | | "clearedPendingPath", clearedPendingPath), false); |
| | | return MoveStepResult.continueAfter(getMoveStepDurationMs()); |
| | | } |
| | | |
| | | sleep(500); |
| | | return true; |
| | | context.status = STATUS_WAITING; |
| | | traceEvent(deviceNo, context, "MOVE_STEP_WAIT", "当前站已更新目标但尚未完成落站,保持当前位置等待下一次推进", |
| | | buildDetails("currentStationId", context.currentStationId, |
| | | "nextStationId", nextStationId, |
| | | "targetStationId", context.finalTargetStationId, |
| | | "blockedStationId", context.blockedStationId, |
| | | "pendingPath", context.getPendingStationIds()), false); |
| | | return MoveStepResult.continueAfter(500L); |
| | | } |
| | | |
| | | private boolean handleIdleState(Integer deviceNo, TaskRuntimeContext context) { |
| | | private void resumeFromCurrentStation(Integer deviceNo, TaskRuntimeContext context, String mergeMode, |
| | | List<Integer> appendedPath, List<Integer> replacedFuturePath, boolean sourceCleared) { |
| | | if (context.currentStationId != null) { |
| | | Integer currentDeviceNo = stateManager.getDeviceNoByStationId(context.currentStationId); |
| | | if (currentDeviceNo != null) { |
| | | guardedClearRunBlock(context.taskNo, context.currentStationId, currentDeviceNo); |
| | | guardedPublishTaskLocation(context.taskNo, currentDeviceNo, context.currentStationId, true, false); |
| | | } |
| | | } |
| | | context.blockedStationId = null; |
| | | context.status = STATUS_RUNNING; |
| | | context.lastProgressAt = System.currentTimeMillis(); |
| | | context.lastProgressStationId = context.currentStationId; |
| | | traceEvent(deviceNo, context, "BLOCK_RESET", "收到新的路径分段,已清除堵塞并重新计时", |
| | | buildDetails("mergeMode", mergeMode, "currentStationId", context.currentStationId, |
| | | "targetStationId", context.finalTargetStationId, "queueSize", context.pendingPathQueue.size(), |
| | | "appendedPath", appendedPath, "replacedFuturePath", replacedFuturePath, |
| | | "sourceCleared", sourceCleared), |
| | | false); |
| | | } |
| | | |
| | | private boolean shouldAdvanceFromCurrentState(TaskRuntimeContext context) { |
| | | return context != null |
| | | && context.initialized |
| | | && context.currentStationId != null |
| | | && !context.pendingPathQueue.isEmpty() |
| | | && !STATUS_BLOCKED.equals(context.status) |
| | | && !STATUS_CANCELLED.equals(context.status) |
| | | && !STATUS_TIMEOUT.equals(context.status) |
| | | && !STATUS_FINISHED.equals(context.status); |
| | | } |
| | | |
| | | private IdleStepResult handleIdleState(Integer deviceNo, TaskRuntimeContext context) { |
| | | if (shouldAdvanceFromCurrentState(context)) { |
| | | traceEvent(deviceNo, context, "IDLE_RESUME", "当前站点存在未完成路径,立即从当前站恢复推进", |
| | | buildDetails("currentStationId", context.currentStationId, |
| | | "targetStationId", context.finalTargetStationId, |
| | | "pendingPath", context.getPendingStationIds()), false); |
| | | context.status = STATUS_RUNNING; |
| | | return IdleStepResult.waitNext(1L); |
| | | } |
| | | |
| | | if (context.currentStationId != null && context.finalTargetStationId != null |
| | | && context.currentStationId.equals(context.finalTargetStationId)) { |
| | | if (!context.arrivalHandled) { |
| | | boolean barcodeGenerated = false; |
| | | if (context.generateBarcode) { |
| | | Integer targetDeviceNo = getDeviceNoByStationId(context.finalTargetStationId); |
| | | Integer targetDeviceNo = stateManager.getDeviceNoByStationId(context.finalTargetStationId); |
| | | if (targetDeviceNo != null) { |
| | | barcodeGenerated = generateStationBarcode(context.taskNo, context.finalTargetStationId, |
| | | barcodeGenerated = guardedGenerateStationBarcode(context.taskNo, context.finalTargetStationId, |
| | | targetDeviceNo); |
| | | } |
| | | } |
| | | context.arrivalHandled = true; |
| | | context.lastProgressAt = System.currentTimeMillis(); |
| | | context.lastProgressStationId = context.currentStationId; |
| | | traceEvent(deviceNo, context, "ARRIVED", "任务到达最终目标站点", |
| | | buildDetails("stationId", context.currentStationId, "barcodeGenerated", barcodeGenerated), |
| | | false); |
| | | } |
| | | context.status = STATUS_FINISHED; |
| | | return true; |
| | | |
| | | Integer targetDeviceNo = stateManager.getDeviceNoByStationId(context.finalTargetStationId); |
| | | if (targetDeviceNo != null) { |
| | | boolean ownerConflict = stateManager.isFinalStationOwnerConflict(targetDeviceNo, context.finalTargetStationId, |
| | | context.taskNo); |
| | | if (ownerConflict) { |
| | | logFinalStationOwnershipLost(deviceNo, context); |
| | | context.status = STATUS_BLOCKED; |
| | | return IdleStepResult.waitNext(getMoveStepDurationMs()); |
| | | } |
| | | boolean stationCleared = stateManager.isStationClearedForTask(targetDeviceNo, context.finalTargetStationId, |
| | | context.taskNo); |
| | | boolean crnTaken = isCrnTakenByTask(context.taskNo); |
| | | if (stationCleared || crnTaken) { |
| | | context.status = STATUS_FINISHED; |
| | | traceEvent(deviceNo, context, "TASK_COMPLETE", "堆垛机已取走货物,任务完成", |
| | | buildDetails("stationId", context.finalTargetStationId, "stationCleared", stationCleared, "crnTaken", crnTaken), false); |
| | | return IdleStepResult.finish(); |
| | | } |
| | | } |
| | | if (targetDeviceNo != null && !guardedArrivalCompletion(targetDeviceNo, context)) { |
| | | context.status = STATUS_BLOCKED; |
| | | return IdleStepResult.waitNext(getMoveStepDurationMs()); |
| | | } |
| | | |
| | | long dwellMs = System.currentTimeMillis() - context.lastProgressAt; |
| | | long timeoutMs = blockManager.getFakeRunBlockTimeoutMs(redisUtil); |
| | | if (!checkTaskNoInArea(context.taskNo) && blockManager.getFakeAllowCheckBlock(redisUtil) |
| | | && !blockManager.isSpecialStation(context.currentStationId) |
| | | && !blockManager.isBlocked(context.currentStationId) |
| | | && dwellMs > timeoutMs) { |
| | | context.status = STATUS_BLOCKED; |
| | | context.clearPendingPathWithLog(deviceNo, context.taskNo, context.currentStationId, |
| | | context.finalTargetStationId, context.blockedStationId, "TARGET_RUN_BLOCKED"); |
| | | guardedRunBlockStation(context.taskNo, context.currentStationId, targetDeviceNo, context.taskNo, |
| | | context.currentStationId); |
| | | context.blockedStationId = context.currentStationId; |
| | | traceEvent(deviceNo, context, "RUN_BLOCKED", "任务到达终点站后停留超时,被标记为堵塞,等待堆垛机取货", |
| | | buildDetails("blockedStationId", context.currentStationId, |
| | | "lastProgressStationId", context.lastProgressStationId, |
| | | "dwellMs", dwellMs, |
| | | "timeoutMs", timeoutMs), false); |
| | | return IdleStepResult.waitNext(getMoveStepDurationMs()); |
| | | } |
| | | |
| | | return IdleStepResult.waitNext(getFinishDelayMs()); |
| | | } |
| | | |
| | | Long lastTime = taskLastUpdateTime.get(context.taskNo); |
| | | if (lastTime != null && System.currentTimeMillis() - lastTime > WAIT_SEGMENT_TIMEOUT_MS) { |
| | | context.status = STATUS_TIMEOUT; |
| | | long waitSegmentTimeoutMs = getWaitSegmentTimeoutMs(); |
| | | if (lastTime != null && System.currentTimeMillis() - lastTime > waitSegmentTimeoutMs) { |
| | | traceEvent(deviceNo, context, "WAIT_TIMEOUT", "等待新的路径分段超时", |
| | | buildDetails("timeoutMs", WAIT_SEGMENT_TIMEOUT_MS, "currentStationId", context.currentStationId, |
| | | buildDetails("timeoutMs", waitSegmentTimeoutMs, "currentStationId", context.currentStationId, |
| | | "targetStationId", context.finalTargetStationId), |
| | | false); |
| | | return true; |
| | | taskLastUpdateTime.put(context.taskNo, System.currentTimeMillis()); |
| | | } |
| | | return false; |
| | | return IdleStepResult.waitNext(getIdleLoopDelayMs()); |
| | | } |
| | | |
| | | private List<Integer> normalizePath(List<Integer> path) { |
| | |
| | | } |
| | | } |
| | | |
| | | private boolean hasTaskReset(Integer taskNo) { |
| | | if (redisUtil == null || taskNo == null) { |
| | | return false; |
| | | private List<Integer> rebuildPendingPathFromCurrent(TaskRuntimeContext context, List<Integer> newPath) { |
| | | List<Integer> rebuilt = new ArrayList<Integer>(); |
| | | if (newPath == null || newPath.isEmpty()) { |
| | | return rebuilt; |
| | | } |
| | | Object cancel = redisUtil.get(RedisKeyType.DEVICE_STATION_MOVE_RESET.key + taskNo); |
| | | return cancel != null; |
| | | Integer currentStationId = context.currentStationId; |
| | | int startIndex = 0; |
| | | if (currentStationId != null) { |
| | | int currentIndex = newPath.indexOf(currentStationId); |
| | | if (currentIndex < 0) { |
| | | return rebuilt; |
| | | } |
| | | startIndex = currentIndex + 1; |
| | | } |
| | | for (int i = startIndex; i < newPath.size(); i++) { |
| | | rebuilt.add(newPath.get(i)); |
| | | } |
| | | return rebuilt; |
| | | } |
| | | |
| | | private void replacePendingPathQueue(TaskRuntimeContext context, List<Integer> futurePath) { |
| | | context.pendingPathQueue.clear(); |
| | | if (futurePath == null) { |
| | | return; |
| | | } |
| | | for (Integer stationId : futurePath) { |
| | | context.pendingPathQueue.offer(stationId); |
| | | } |
| | | } |
| | | |
| | | private boolean hasTaskReset(Integer taskNo) { |
| | | // 仿真系统不响应外部取消信号(如堵塞重路由触发的 signalSegmentReset), |
| | | // 避免任务在站点行走过程中被意外清除 |
| | | return false; |
| | | } |
| | | |
| | | private Integer getLastInQueue(LinkedBlockingQueue<Integer> queue) { |
| | |
| | | if (currentStationId == null || targetStationId == null) { |
| | | return; |
| | | } |
| | | Integer currentDeviceNo = getDeviceNoByStationId(currentStationId); |
| | | Integer currentDeviceNo = stateManager.getDeviceNoByStationId(currentStationId); |
| | | if (currentDeviceNo == null) { |
| | | return; |
| | | } |
| | | |
| | | lockStations(currentStationId); |
| | | moveEngine.lockStations(currentStationId); |
| | | try { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentDeviceNo); |
| | | if (statusList == null) { |
| | | return; |
| | | } |
| | | |
| | | ZyStationStatusEntity currentStatus = statusList.stream() |
| | | .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null); |
| | | ZyStationStatusEntity currentStatus = stateManager.findStationStatus(currentDeviceNo, currentStationId); |
| | | if (currentStatus == null) { |
| | | return; |
| | | } |
| | | |
| | | if (currentStatus.getTaskNo() != null && currentStatus.getTaskNo() > 0 |
| | | && !currentStatus.getTaskNo().equals(taskNo) && currentStatus.isLoading()) { |
| | | if (hasOwnerConflict(currentStatus, taskNo)) { |
| | | logOwnerConflict("syncCurrentStationTarget", currentStationId, currentStatus, taskNo, targetStationId, false, |
| | | "owner_conflict"); |
| | | return; |
| | | } |
| | | |
| | | updateStationDataInternal(currentStationId, currentDeviceNo, taskNo, targetStationId, null, null, null); |
| | | stateManager.updateStationDataInternal(currentStationId, currentDeviceNo, taskNo, targetStationId, null, null, null); |
| | | } finally { |
| | | unlockStations(currentStationId); |
| | | moveEngine.unlockStations(currentStationId); |
| | | } |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | private boolean getFakeAllowCheckBlock() { |
| | | boolean fakeAllowCheckBlock = true; |
| | | Object systemConfigMapObj = redisUtil == null ? null : redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); |
| | | if (systemConfigMapObj instanceof Map) { |
| | | Map<String, String> systemConfigMap = (Map<String, String>) systemConfigMapObj; |
| | | String value = systemConfigMap.get("fakeAllowCheckBlock"); |
| | | if (value != null && !"Y".equals(value)) { |
| | | fakeAllowCheckBlock = false; |
| | | } |
| | | } |
| | | return fakeAllowCheckBlock; |
| | | private boolean hasOwnerConflict(ZyStationStatusEntity currentStatus, Integer incomingTaskNo) { |
| | | return currentStatus != null |
| | | && currentStatus.isLoading() |
| | | && currentStatus.getTaskNo() != null |
| | | && currentStatus.getTaskNo() > 0 |
| | | && (incomingTaskNo == null || !currentStatus.getTaskNo().equals(incomingTaskNo)); |
| | | } |
| | | |
| | | private boolean isSpecialStation(Integer stationId) { |
| | | private void logOwnerConflict(String operation, Integer stationId, ZyStationStatusEntity currentStatus, |
| | | Integer incomingTaskNo, Integer incomingTargetStationId, boolean finalStation, String reason) { |
| | | News.info("[WCS Debug] fake station owner冲突,operation={},stationId={},currentTaskNo={},currentLoading={},currentTargetStaNo={},incomingTaskNo={},incomingTargetStaNo={},finalStation={},reason={}", |
| | | operation, |
| | | stationId, |
| | | currentStatus == null ? null : currentStatus.getTaskNo(), |
| | | currentStatus == null ? null : currentStatus.isLoading(), |
| | | currentStatus == null ? null : currentStatus.getTargetStaNo(), |
| | | incomingTaskNo, |
| | | incomingTargetStationId, |
| | | finalStation, |
| | | reason); |
| | | } |
| | | |
| | | private boolean ensureStationWritable(String operation, Integer deviceNo, Integer stationId, |
| | | Integer incomingTaskNo, Integer incomingTargetStationId, boolean finalStation) { |
| | | if (deviceNo == null || stationId == null) { |
| | | return false; |
| | | } |
| | | ZyStationStatusEntity currentStatus = stateManager.findStationStatus(deviceNo, stationId); |
| | | if (currentStatus == null) { |
| | | return false; |
| | | } |
| | | if (!hasOwnerConflict(currentStatus, incomingTaskNo)) { |
| | | return true; |
| | | } |
| | | logOwnerConflict(operation, stationId, currentStatus, incomingTaskNo, incomingTargetStationId, finalStation, |
| | | finalStation ? "final_station_owner_conflict" : "owner_conflict"); |
| | | return false; |
| | | } |
| | | |
| | | private boolean ensureStationClearable(String operation, Integer deviceNo, Integer stationId, Integer expectedTaskNo, |
| | | boolean finalStation) { |
| | | if (deviceNo == null || stationId == null) { |
| | | return false; |
| | | } |
| | | ZyStationStatusEntity currentStatus = stateManager.findStationStatus(deviceNo, stationId); |
| | | if (currentStatus == null) { |
| | | return false; |
| | | } |
| | | if (!hasOwnerConflict(currentStatus, expectedTaskNo)) { |
| | | return true; |
| | | } |
| | | if (isLegalClearStation(stationId)) { |
| | | return true; |
| | | } |
| | | logOwnerConflict(operation, stationId, currentStatus, expectedTaskNo, currentStatus.getTargetStaNo(), finalStation, |
| | | "illegal_clear_attempt"); |
| | | return false; |
| | | } |
| | | |
| | | private boolean isLegalClearStation(Integer stationId) { |
| | | if (stationId == null) { |
| | | return false; |
| | | } |
| | | BasDevpService basDevpService = SpringUtils.getBean(BasDevpService.class); |
| | | List<BasDevp> basDevps = basDevpService.list(); |
| | | Set<Integer> specialStationIds = new HashSet<>(); |
| | | for (BasDevp basDevp : basDevps) { |
| | | for (StationObjModel station : basDevp.getInStationList$()) { |
| | | specialStationIds.add(station.getStationId()); |
| | | } |
| | | for (StationObjModel station : basDevp.getOutStationList$()) { |
| | | specialStationIds.add(station.getStationId()); |
| | | } |
| | | for (StationObjModel station : basDevp.getBarcodeStationList$()) { |
| | | specialStationIds.add(station.getStationId()); |
| | | } |
| | | Set<Integer> stationIds = legalClearStationIds; |
| | | if (stationIds == null || stationIds.isEmpty()) { |
| | | refreshLegalClearStationIds(); |
| | | stationIds = legalClearStationIds; |
| | | } |
| | | return specialStationIds.contains(stationId); |
| | | return stationIds != null && stationIds.contains(stationId); |
| | | } |
| | | |
| | | private long getFakeRunBlockTimeoutMs() { |
| | | long timeoutMs = DEFAULT_FAKE_RUN_BLOCK_TIMEOUT_MS; |
| | | Object systemConfigMapObj = redisUtil == null ? null : redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); |
| | | if (systemConfigMapObj instanceof Map) { |
| | | Map<?, ?> systemConfigMap = (Map<?, ?>) systemConfigMapObj; |
| | | Object value = systemConfigMap.get("fakeRunBlockTimeoutMs"); |
| | | if (value != null) { |
| | | try { |
| | | long parsed = Long.parseLong(String.valueOf(value).trim()); |
| | | if (parsed > 0) { |
| | | timeoutMs = parsed; |
| | | } |
| | | } catch (Exception ignore) { |
| | | private boolean isBarcodeStation(Integer stationId) { |
| | | if (stationId == null) { |
| | | return false; |
| | | } |
| | | Set<Integer> stationIds = barcodeStationIds; |
| | | if (stationIds == null || stationIds.isEmpty()) { |
| | | refreshBarcodeStationIds(); |
| | | stationIds = barcodeStationIds; |
| | | } |
| | | return stationIds != null && stationIds.contains(stationId); |
| | | } |
| | | |
| | | private void refreshLegalClearStationIds() { |
| | | try { |
| | | Set<Integer> stationIds = new HashSet<Integer>(); |
| | | |
| | | com.zy.asrs.service.BasCrnpService basCrnpService = com.core.common.SpringUtils.getBean(com.zy.asrs.service.BasCrnpService.class); |
| | | List<com.zy.asrs.entity.BasCrnp> basCrnps = basCrnpService.list(); |
| | | for (com.zy.asrs.entity.BasCrnp basCrnp : basCrnps) { |
| | | if (basCrnp == null) { |
| | | continue; |
| | | } |
| | | collectStationIds(stationIds, basCrnp.getInStationList$()); |
| | | collectStationIds(stationIds, basCrnp.getOutStationList$()); |
| | | } |
| | | |
| | | com.zy.asrs.service.BasDevpService basDevpService = com.core.common.SpringUtils.getBean(com.zy.asrs.service.BasDevpService.class); |
| | | List<com.zy.asrs.entity.BasDevp> basDevps = basDevpService.list(); |
| | | for (com.zy.asrs.entity.BasDevp basDevp : basDevps) { |
| | | if (basDevp == null) { |
| | | continue; |
| | | } |
| | | collectStationIds(stationIds, basDevp.getBarcodeStationList$()); |
| | | collectStationIds(stationIds, basDevp.getInStationList$()); |
| | | collectStationIds(stationIds, basDevp.getOutStationList$()); |
| | | } |
| | | |
| | | legalClearStationIds = stationIds; |
| | | } catch (Exception e) { |
| | | News.info("[WCS Debug] fake 合法清站白名单刷新失败,异常类型={},异常信息={}", |
| | | e.getClass().getSimpleName(), e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private void refreshBarcodeStationIds() { |
| | | try { |
| | | Set<Integer> stationIds = new HashSet<Integer>(); |
| | | com.zy.asrs.service.BasDevpService basDevpService = com.core.common.SpringUtils.getBean(com.zy.asrs.service.BasDevpService.class); |
| | | List<com.zy.asrs.entity.BasDevp> basDevps = basDevpService.list(); |
| | | for (com.zy.asrs.entity.BasDevp basDevp : basDevps) { |
| | | if (basDevp == null) { |
| | | continue; |
| | | } |
| | | collectStationIds(stationIds, basDevp.getBarcodeStationList$()); |
| | | } |
| | | barcodeStationIds = stationIds; |
| | | } catch (Exception e) { |
| | | News.info("[WCS Debug] fake 条码站白名单刷新失败,异常类型={},异常信息={}", |
| | | e.getClass().getSimpleName(), e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private void collectStationIds(Set<Integer> stationIds, List<com.zy.core.model.StationObjModel> stations) { |
| | | if (stationIds == null || stations == null || stations.isEmpty()) { |
| | | return; |
| | | } |
| | | for (com.zy.core.model.StationObjModel station : stations) { |
| | | if (station != null && station.getStationId() != null) { |
| | | stationIds.add(station.getStationId()); |
| | | } |
| | | } |
| | | return timeoutMs; |
| | | } |
| | | |
| | | private boolean isFinalStation(Integer taskNo, Integer stationId) { |
| | | TaskRuntimeContext context = taskNo == null ? null : taskContexts.get(taskNo); |
| | | return context != null && stationId != null && stationId.equals(context.finalTargetStationId); |
| | | } |
| | | |
| | | private void logFinalStationOwnershipLost(Integer deviceNo, TaskRuntimeContext context) { |
| | | Integer stationId = context == null ? null : context.finalTargetStationId; |
| | | Integer targetDeviceNo = stateManager.getDeviceNoByStationId(stationId); |
| | | ZyStationStatusEntity currentStatus = targetDeviceNo == null ? null : stateManager.snapshotStation(targetDeviceNo, stationId); |
| | | News.info("[WCS Debug] fake 最终站所有权丢失,taskNo={},stationId={},currentTaskNo={},currentLoading={},currentTargetStaNo={},reason={}", |
| | | context == null ? null : context.taskNo, |
| | | stationId, |
| | | currentStatus == null ? null : currentStatus.getTaskNo(), |
| | | currentStatus == null ? null : currentStatus.isLoading(), |
| | | currentStatus == null ? null : currentStatus.getTargetStaNo(), |
| | | "final_station_owner_conflict"); |
| | | traceEvent(deviceNo, context, "FINAL_STATION_OWNER_CONFLICT", "最终站仍有物但所有者已被其他任务覆盖", |
| | | buildDetails("stationId", stationId, |
| | | "currentTaskNo", currentStatus == null ? null : currentStatus.getTaskNo(), |
| | | "currentLoading", currentStatus == null ? null : currentStatus.isLoading(), |
| | | "currentTargetStaNo", currentStatus == null ? null : currentStatus.getTargetStaNo()), false); |
| | | } |
| | | |
| | | private boolean guardedClearStationForDispatch(Integer deviceNo, Integer stationId, Integer expectedTaskNo, String reason) { |
| | | if (!ensureStationClearable("clearStationForDispatch", deviceNo, stationId, expectedTaskNo, |
| | | isFinalStation(expectedTaskNo, stationId))) { |
| | | return false; |
| | | } |
| | | stateManager.clearStationForDispatch(deviceNo, stationId, reason); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedResetStation(Integer deviceNo, Integer stationId, Integer expectedTaskNo) { |
| | | if (!ensureStationClearable("resetStation", deviceNo, stationId, expectedTaskNo, |
| | | isFinalStation(expectedTaskNo, stationId))) { |
| | | return false; |
| | | } |
| | | stateManager.resetStation(deviceNo, stationId); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedUpdateStationBarcode(Integer deviceNo, Integer stationId, Integer taskNo, String barcode) { |
| | | if (!ensureStationWritable("updateStationBarcode", deviceNo, stationId, taskNo, null, |
| | | isFinalStation(taskNo, stationId))) { |
| | | return false; |
| | | } |
| | | stateManager.updateStationBarcode(deviceNo, stationId, barcode); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedGenerateFakeOutStationData(Integer deviceNo, Integer stationId, Integer taskNo) { |
| | | if (!ensureStationWritable("generateFakeOutStationData", deviceNo, stationId, taskNo, null, |
| | | isFinalStation(taskNo, stationId))) { |
| | | return false; |
| | | } |
| | | stateManager.generateFakeOutStationData(deviceNo, stationId); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedHandoffBarcodeStation(Integer deviceNo, Integer taskNo, Integer stationId, Integer targetStationId) { |
| | | if (!ensureStationWritable("handoffBarcodeStation", deviceNo, stationId, taskNo, targetStationId, |
| | | isFinalStation(taskNo, stationId))) { |
| | | return false; |
| | | } |
| | | stateManager.handoffBarcodeStation(deviceNo, taskNo, stationId, targetStationId); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedRunBlockStation(Integer taskNo, Integer stationId, Integer deviceNo, Integer ownerTaskNo, |
| | | Integer targetStationId) { |
| | | if (!ensureStationWritable("runBlockStation", deviceNo, stationId, ownerTaskNo, targetStationId, |
| | | isFinalStation(ownerTaskNo, stationId))) { |
| | | return false; |
| | | } |
| | | blockManager.runBlockStation(taskNo, stationId, deviceNo, ownerTaskNo, targetStationId); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedClearRunBlock(Integer taskNo, Integer stationId, Integer deviceNo) { |
| | | if (!ensureStationWritable("clearRunBlock", deviceNo, stationId, taskNo, null, |
| | | isFinalStation(taskNo, stationId))) { |
| | | return false; |
| | | } |
| | | blockManager.clearRunBlock(stationId, deviceNo); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedPublishTaskLocation(Integer taskNo, Integer deviceNo, Integer stationId, boolean loading, boolean runBlock) { |
| | | if (loading && !ensureStationWritable("publishTaskLocation", deviceNo, stationId, taskNo, null, |
| | | isFinalStation(taskNo, stationId))) { |
| | | return false; |
| | | } |
| | | stateManager.publishTaskLocation(taskNo, deviceNo, stationId, loading, runBlock); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedGenerateStationData(Integer deviceNo, Integer taskNo, Integer stationId, Integer targetStationId) { |
| | | if (!ensureStationWritable("generateStationData", deviceNo, stationId, taskNo, targetStationId, |
| | | isFinalStation(taskNo, stationId))) { |
| | | return false; |
| | | } |
| | | stateManager.generateStationData(deviceNo, taskNo, stationId, targetStationId); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedSyncCurrentStationTarget(Integer taskNo, Integer currentStationId, Integer targetStationId) { |
| | | syncCurrentStationTarget(taskNo, currentStationId, targetStationId); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedClearTaskLocationIfMatches(Integer taskNo, Integer deviceNo, Integer stationId) { |
| | | stateManager.clearTaskLocationIfMatches(taskNo, deviceNo, stationId); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedPublishCurrentLocation(Integer taskNo, Integer deviceNo, Integer stationId) { |
| | | return guardedPublishTaskLocation(taskNo, deviceNo, stationId, true, false); |
| | | } |
| | | |
| | | private boolean guardedGenerateStationBarcode(Integer taskNo, Integer stationId, Integer deviceNo) { |
| | | if (!ensureStationWritable("generateStationBarcode", deviceNo, stationId, taskNo, null, |
| | | isFinalStation(taskNo, stationId))) { |
| | | return false; |
| | | } |
| | | return stateManager.generateStationBarcode(taskNo, stationId, deviceNo); |
| | | } |
| | | |
| | | private boolean guardedUpdateStationData(Integer stationId, Integer deviceNo, Integer taskNo, Integer targetStationId, |
| | | Boolean loading, String barcode, Boolean runBlock) { |
| | | if ((taskNo != null || Boolean.TRUE.equals(loading)) |
| | | && !ensureStationWritable("updateStationData", deviceNo, stationId, taskNo, targetStationId, |
| | | isFinalStation(taskNo, stationId))) { |
| | | return false; |
| | | } |
| | | return stateManager.updateStationDataInternal(stationId, deviceNo, taskNo, targetStationId, loading, barcode, runBlock); |
| | | } |
| | | |
| | | private boolean isFinalStationConflict(Integer taskNo, Integer stationId, Integer deviceNo) { |
| | | return isFinalStation(taskNo, stationId) && stateManager.isOccupiedByOtherLoadingTask(deviceNo, stationId, taskNo); |
| | | } |
| | | |
| | | private boolean guardedResetOrClearBlockedStation(Integer taskNo, Integer stationId, Integer deviceNo) { |
| | | return ensureStationClearable("blockedStationClear", deviceNo, stationId, taskNo, isFinalStation(taskNo, stationId)); |
| | | } |
| | | |
| | | private boolean guardedStationOccupied(Integer taskNo, Integer stationId, Integer deviceNo) { |
| | | return !stateManager.isOccupiedByOtherLoadingTask(deviceNo, stationId, taskNo); |
| | | } |
| | | |
| | | private boolean guardedFinalStationOwnership(Integer taskNo, Integer stationId, Integer deviceNo) { |
| | | return !stateManager.isFinalStationOwnerConflict(deviceNo, stationId, taskNo); |
| | | } |
| | | |
| | | private boolean guardedFinalStationWritable(Integer taskNo, Integer stationId, Integer deviceNo, String operation) { |
| | | if (!guardedFinalStationOwnership(taskNo, stationId, deviceNo)) { |
| | | ZyStationStatusEntity currentStatus = stateManager.snapshotStation(deviceNo, stationId); |
| | | logOwnerConflict(operation, stationId, currentStatus, taskNo, |
| | | currentStatus == null ? null : currentStatus.getTargetStaNo(), true, |
| | | "final_station_owner_conflict"); |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedFinalStationClearable(Integer taskNo, Integer stationId, Integer deviceNo, String operation) { |
| | | if (!guardedFinalStationOwnership(taskNo, stationId, deviceNo)) { |
| | | ZyStationStatusEntity currentStatus = stateManager.snapshotStation(deviceNo, stationId); |
| | | logOwnerConflict(operation, stationId, currentStatus, taskNo, |
| | | currentStatus == null ? null : currentStatus.getTargetStaNo(), true, |
| | | "illegal_clear_attempt"); |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private boolean shouldTreatAsFinalStationConflict(Integer taskNo, Integer stationId, Integer deviceNo) { |
| | | return isFinalStation(taskNo, stationId) && stateManager.isFinalStationOwnerConflict(deviceNo, stationId, taskNo); |
| | | } |
| | | |
| | | private boolean guardedFinalStationMutation(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId) { |
| | | if (shouldTreatAsFinalStationConflict(taskNo, stationId, deviceNo)) { |
| | | ZyStationStatusEntity currentStatus = stateManager.snapshotStation(deviceNo, stationId); |
| | | logOwnerConflict(operation, stationId, currentStatus, taskNo, targetStationId, true, |
| | | "final_station_owner_conflict"); |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedFinalStationClear(String operation, Integer taskNo, Integer stationId, Integer deviceNo) { |
| | | if (shouldTreatAsFinalStationConflict(taskNo, stationId, deviceNo)) { |
| | | ZyStationStatusEntity currentStatus = stateManager.snapshotStation(deviceNo, stationId); |
| | | logOwnerConflict(operation, stationId, currentStatus, taskNo, |
| | | currentStatus == null ? null : currentStatus.getTargetStaNo(), true, |
| | | "illegal_clear_attempt"); |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedStationWrite(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId) { |
| | | if (!guardedFinalStationMutation(operation, taskNo, stationId, deviceNo, targetStationId)) { |
| | | return false; |
| | | } |
| | | return ensureStationWritable(operation, deviceNo, stationId, taskNo, targetStationId, isFinalStation(taskNo, stationId)); |
| | | } |
| | | |
| | | private boolean guardedStationClear(String operation, Integer taskNo, Integer stationId, Integer deviceNo) { |
| | | if (!guardedFinalStationClear(operation, taskNo, stationId, deviceNo)) { |
| | | return false; |
| | | } |
| | | return ensureStationClearable(operation, deviceNo, stationId, taskNo, isFinalStation(taskNo, stationId)); |
| | | } |
| | | |
| | | private boolean guardedStateMutation(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId, |
| | | Runnable mutation) { |
| | | if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedStateClear(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Runnable mutation) { |
| | | if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedFinalStationCompletion(Integer deviceNo, TaskRuntimeContext context) { |
| | | if (context == null || context.finalTargetStationId == null || context.taskNo == null) { |
| | | return false; |
| | | } |
| | | return !stateManager.isFinalStationOwnerConflict(deviceNo, context.finalTargetStationId, context.taskNo); |
| | | } |
| | | |
| | | private boolean guardedCurrentStationOwnership(Integer taskNo, Integer currentStationId, Integer currentDeviceNo) { |
| | | return !stateManager.isOccupiedByOtherLoadingTask(currentDeviceNo, currentStationId, taskNo); |
| | | } |
| | | |
| | | private boolean guardedTargetStationOwnership(Integer taskNo, Integer stationId, Integer deviceNo) { |
| | | return !stateManager.isOccupiedByOtherLoadingTask(deviceNo, stationId, taskNo); |
| | | } |
| | | |
| | | private boolean guardedCommandWrite(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId, Runnable mutation) { |
| | | if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedCommandClear(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Runnable mutation) { |
| | | if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedRunBlockMutation(Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId) { |
| | | return guardedStationWrite("runBlockStation", taskNo, stationId, deviceNo, targetStationId); |
| | | } |
| | | |
| | | private boolean guardedClearMutation(Integer taskNo, Integer stationId, Integer deviceNo, String operation) { |
| | | return guardedStationClear(operation, taskNo, stationId, deviceNo); |
| | | } |
| | | |
| | | private boolean guardedArrivalOwnership(Integer deviceNo, TaskRuntimeContext context) { |
| | | return context != null && context.finalTargetStationId != null && context.taskNo != null |
| | | && !stateManager.isFinalStationOwnerConflict(deviceNo, context.finalTargetStationId, context.taskNo); |
| | | } |
| | | |
| | | private boolean guardedArrivalStationWritable(Integer deviceNo, TaskRuntimeContext context, String operation) { |
| | | if (!guardedArrivalOwnership(deviceNo, context)) { |
| | | logFinalStationOwnershipLost(deviceNo, context); |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedArrivalStationClearable(Integer deviceNo, TaskRuntimeContext context, String operation) { |
| | | if (!guardedArrivalOwnership(deviceNo, context)) { |
| | | logFinalStationOwnershipLost(deviceNo, context); |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedStationMutation(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId, |
| | | Runnable mutation, boolean clearOperation) { |
| | | boolean allowed = clearOperation |
| | | ? guardedStationClear(operation, taskNo, stationId, deviceNo) |
| | | : guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId); |
| | | if (!allowed) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedFinalArrivalState(Integer deviceNo, TaskRuntimeContext context) { |
| | | if (context == null || context.finalTargetStationId == null || context.taskNo == null) { |
| | | return false; |
| | | } |
| | | if (stateManager.isFinalStationOwnerConflict(deviceNo, context.finalTargetStationId, context.taskNo)) { |
| | | logFinalStationOwnershipLost(deviceNo, context); |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedArrivalCompletion(Integer deviceNo, TaskRuntimeContext context) { |
| | | return guardedFinalArrivalState(deviceNo, context); |
| | | } |
| | | |
| | | private boolean guardedStationOwnership(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId, |
| | | boolean clearOperation) { |
| | | return clearOperation |
| | | ? guardedStationClear(operation, taskNo, stationId, deviceNo) |
| | | : guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId); |
| | | } |
| | | |
| | | private boolean guardedStationMutationIfAllowed(String operation, Integer taskNo, Integer stationId, Integer deviceNo, |
| | | Integer targetStationId, boolean clearOperation, Runnable mutation) { |
| | | if (!guardedStationOwnership(operation, taskNo, stationId, deviceNo, targetStationId, clearOperation)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedArrivalStationState(Integer deviceNo, TaskRuntimeContext context) { |
| | | return guardedFinalArrivalState(deviceNo, context); |
| | | } |
| | | |
| | | private boolean guardedMutateFinalStation(String operation, Integer taskNo, Integer stationId, Integer deviceNo, |
| | | Integer targetStationId, Runnable mutation) { |
| | | if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedClearFinalStation(String operation, Integer taskNo, Integer stationId, Integer deviceNo, |
| | | Runnable mutation) { |
| | | if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedArrivalState(Integer deviceNo, TaskRuntimeContext context) { |
| | | if (!guardedFinalArrivalState(deviceNo, context)) { |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedCurrentStationWrite(Integer taskNo, Integer currentStationId, Integer currentDeviceNo, Integer targetStationId, |
| | | String operation) { |
| | | return guardedStationWrite(operation, taskNo, currentStationId, currentDeviceNo, targetStationId); |
| | | } |
| | | |
| | | private boolean guardedCurrentStationClear(Integer taskNo, Integer currentStationId, Integer currentDeviceNo, String operation) { |
| | | return guardedStationClear(operation, taskNo, currentStationId, currentDeviceNo); |
| | | } |
| | | |
| | | private boolean guardedFinalStationWrite(Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId, String operation) { |
| | | return guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId); |
| | | } |
| | | |
| | | private boolean guardedFinalStationClear(Integer taskNo, Integer stationId, Integer deviceNo, String operation) { |
| | | return guardedStationClear(operation, taskNo, stationId, deviceNo); |
| | | } |
| | | |
| | | private boolean guardedStationBarcodeWrite(Integer deviceNo, Integer stationId, Integer taskNo, String barcode) { |
| | | return guardedUpdateStationBarcode(deviceNo, stationId, taskNo, barcode); |
| | | } |
| | | |
| | | private boolean guardedStationOccupancyWrite(Integer deviceNo, Integer stationId, Integer taskNo, Integer targetStationId, |
| | | Runnable mutation, String operation) { |
| | | if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedStationOccupancyClear(Integer deviceNo, Integer stationId, Integer taskNo, |
| | | Runnable mutation, String operation) { |
| | | if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedStationWriteForTask(Integer deviceNo, Integer stationId, Integer taskNo, Integer targetStationId, |
| | | String operation) { |
| | | return guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId); |
| | | } |
| | | |
| | | private boolean guardedStationClearForTask(Integer deviceNo, Integer stationId, Integer taskNo, String operation) { |
| | | return guardedStationClear(operation, taskNo, stationId, deviceNo); |
| | | } |
| | | |
| | | private boolean guardedMutateStationIfOwned(Integer deviceNo, Integer stationId, Integer taskNo, Integer targetStationId, |
| | | String operation, Runnable mutation) { |
| | | if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedClearStationIfOwned(Integer deviceNo, Integer stationId, Integer taskNo, String operation, |
| | | Runnable mutation) { |
| | | if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedArrivalVisibility(Integer deviceNo, TaskRuntimeContext context) { |
| | | return guardedFinalArrivalState(deviceNo, context); |
| | | } |
| | | |
| | | private boolean guardedFinalStationNotOverwritten(Integer deviceNo, TaskRuntimeContext context) { |
| | | return guardedFinalArrivalState(deviceNo, context); |
| | | } |
| | | |
| | | private boolean guardedMutateOccupiedStation(String operation, Integer deviceNo, Integer stationId, Integer taskNo, |
| | | Integer targetStationId, Runnable mutation) { |
| | | if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedClearOccupiedStation(String operation, Integer deviceNo, Integer stationId, Integer taskNo, |
| | | Runnable mutation) { |
| | | if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedFinalStationStillOwned(Integer deviceNo, TaskRuntimeContext context) { |
| | | return guardedFinalArrivalState(deviceNo, context); |
| | | } |
| | | |
| | | private boolean guardedStateWrite(String operation, Integer deviceNo, Integer stationId, Integer taskNo, Integer targetStationId) { |
| | | return guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId); |
| | | } |
| | | |
| | | private boolean guardedStateClear(String operation, Integer deviceNo, Integer stationId, Integer taskNo) { |
| | | return guardedStationClear(operation, taskNo, stationId, deviceNo); |
| | | } |
| | | |
| | | private boolean guardedArrivalOwnerCheck(Integer deviceNo, TaskRuntimeContext context) { |
| | | return guardedFinalArrivalState(deviceNo, context); |
| | | } |
| | | |
| | | private boolean guardedArrivalConflict(Integer deviceNo, TaskRuntimeContext context) { |
| | | return stateManager.isFinalStationOwnerConflict(deviceNo, context.finalTargetStationId, context.taskNo); |
| | | } |
| | | |
| | | private boolean guardedCurrentOwnerCheck(Integer deviceNo, Integer stationId, Integer taskNo) { |
| | | return !stateManager.isOccupiedByOtherLoadingTask(deviceNo, stationId, taskNo); |
| | | } |
| | | |
| | | private boolean guardedMutateStationState(String operation, Integer deviceNo, Integer stationId, Integer taskNo, |
| | | Integer targetStationId, Runnable mutation) { |
| | | if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedClearStationState(String operation, Integer deviceNo, Integer stationId, Integer taskNo, |
| | | Runnable mutation) { |
| | | if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) { |
| | | return false; |
| | | } |
| | | mutation.run(); |
| | | return true; |
| | | } |
| | | |
| | | private boolean guardedArrivalCompletionState(Integer deviceNo, TaskRuntimeContext context) { |
| | | return guardedFinalArrivalState(deviceNo, context); |
| | | } |
| | | |
| | | private boolean guardedWriteToStation(String operation, Integer deviceNo, Integer stationId, Integer taskNo, Integer targetStationId) { |
| | | return guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId); |
| | | } |
| | | |
| | | private boolean guardedClearFromStation(String operation, Integer deviceNo, Integer stationId, Integer taskNo) { |
| | | return guardedStationClear(operation, taskNo, stationId, deviceNo); |
| | | } |
| | | |
| | | private void handleCommand(Integer deviceNo, StationCommand command) { |
| | | News.info("[WCS Debug] 站点仿真模拟(V3)已启动,命令数据={}", JSON.toJSONString(command)); |
| | | News.info("[WCS Debug] fake 非MOVE命令进入device串行执行,deviceNo={},stationId={},targetStaNo={},commandType={},命令数据={}", |
| | | deviceNo, command.getStationId(), command.getTargetStaNo(), command.getCommandType(), JSON.toJSONString(command)); |
| | | Integer taskNo = command.getTaskNo(); |
| | | Integer stationId = command.getStationId(); |
| | | Integer targetStationId = command.getTargetStaNo(); |
| | | |
| | | if (command.getCommandType() == StationCommandType.RESET) { |
| | | resetStation(deviceNo, stationId); |
| | | boolean reset = guardedResetStation(deviceNo, stationId, taskNo); |
| | | News.info("[WCS Debug] fake RESET已通过统一站点锁执行,deviceNo={},stationId={},accepted={}", deviceNo, stationId, reset); |
| | | return; |
| | | } |
| | | |
| | | if (command.getCommandType() == StationCommandType.WRITE_INFO) { |
| | | if (command.getBarcode() != null) { |
| | | updateStationBarcode(deviceNo, stationId, command.getBarcode()); |
| | | boolean updated = guardedUpdateStationBarcode(deviceNo, stationId, taskNo, command.getBarcode()); |
| | | News.info("[WCS Debug] fake WRITE_INFO条码写入已通过统一站点锁执行,deviceNo={},stationId={},barcode={},accepted={}", |
| | | deviceNo, stationId, command.getBarcode(), updated); |
| | | return; |
| | | } |
| | | if (taskNo == 9998 && targetStationId == 0) { |
| | | generateFakeOutStationData(deviceNo, stationId); |
| | | boolean generated = guardedGenerateFakeOutStationData(deviceNo, stationId, taskNo); |
| | | News.info("[WCS Debug] fake WRITE_INFO有物写入已通过统一站点锁执行,deviceNo={},stationId={},accepted={}", deviceNo, stationId, generated); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | if (taskNo != null && taskNo > 0 && taskNo != 9999 && taskNo != 9998 && stationId != null |
| | | && stationId.equals(targetStationId)) { |
| | | generateStationData(deviceNo, taskNo, stationId, targetStationId); |
| | | } |
| | | } |
| | | |
| | | private void generateFakeOutStationData(Integer deviceNo, Integer stationId) { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return; |
| | | } |
| | | ZyStationStatusEntity status = statusList.stream() |
| | | .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null); |
| | | if (status == null) { |
| | | return; |
| | | } |
| | | synchronized (status) { |
| | | status.setLoading(true); |
| | | } |
| | | } |
| | | |
| | | private void generateStationData(Integer deviceNo, Integer taskNo, Integer stationId, Integer targetStationId) { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return; |
| | | } |
| | | ZyStationStatusEntity status = statusList.stream() |
| | | .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null); |
| | | if (status == null) { |
| | | return; |
| | | } |
| | | synchronized (status) { |
| | | status.setTaskNo(taskNo); |
| | | status.setTargetStaNo(targetStationId); |
| | | } |
| | | } |
| | | |
| | | private void resetStation(Integer deviceNo, Integer stationId) { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return; |
| | | } |
| | | ZyStationStatusEntity status = statusList.stream() |
| | | .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null); |
| | | if (status == null) { |
| | | return; |
| | | } |
| | | synchronized (status) { |
| | | status.setTaskNo(0); |
| | | status.setLoading(false); |
| | | status.setBarcode(""); |
| | | } |
| | | } |
| | | |
| | | private void updateStationBarcode(Integer deviceNo, Integer stationId, String barcode) { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return; |
| | | } |
| | | ZyStationStatusEntity status = statusList.stream() |
| | | .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null); |
| | | if (status == null) { |
| | | return; |
| | | } |
| | | synchronized (status) { |
| | | status.setBarcode(barcode); |
| | | } |
| | | } |
| | | |
| | | private Integer getDeviceNoByStationId(Integer stationId) { |
| | | for (Map.Entry<Integer, List<ZyStationStatusEntity>> entry : deviceStatusMap.entrySet()) { |
| | | List<ZyStationStatusEntity> list = entry.getValue(); |
| | | if (list == null) { |
| | | continue; |
| | | } |
| | | for (ZyStationStatusEntity entity : list) { |
| | | if (entity.getStationId() != null && entity.getStationId().equals(stationId)) { |
| | | return entry.getKey(); |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private Integer findCurrentStationIdByTask(Integer taskNo) { |
| | | for (List<ZyStationStatusEntity> list : deviceStatusMap.values()) { |
| | | if (list == null) { |
| | | continue; |
| | | } |
| | | for (ZyStationStatusEntity entity : list) { |
| | | if (entity.getTaskNo() != null && entity.getTaskNo().equals(taskNo) && entity.isLoading()) { |
| | | return entity.getStationId(); |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private void sleep(long ms) { |
| | | try { |
| | | Thread.sleep(ms); |
| | | } catch (InterruptedException e) { |
| | | Thread.currentThread().interrupt(); |
| | | } |
| | | } |
| | | |
| | | private ReentrantLock getStationLock(Integer stationId) { |
| | | return stationLocks.computeIfAbsent(stationId, key -> new ReentrantLock()); |
| | | } |
| | | |
| | | private void lockStations(Integer... stationIds) { |
| | | Integer[] sorted = Arrays.copyOf(stationIds, stationIds.length); |
| | | Arrays.sort(sorted); |
| | | for (Integer stationId : sorted) { |
| | | getStationLock(stationId).lock(); |
| | | } |
| | | } |
| | | |
| | | private void unlockStations(Integer... stationIds) { |
| | | Integer[] sorted = Arrays.copyOf(stationIds, stationIds.length); |
| | | Arrays.sort(sorted); |
| | | for (int i = sorted.length - 1; i >= 0; i--) { |
| | | getStationLock(sorted[i]).unlock(); |
| | | } |
| | | } |
| | | |
| | | private boolean updateStationDataInternal(Integer stationId, Integer deviceNo, Integer taskNo, Integer targetStaNo, |
| | | Boolean isLoading, String barcode, Boolean runBlock) { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return false; |
| | | } |
| | | ZyStationStatusEntity currentStatus = statusList.stream() |
| | | .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null); |
| | | if (currentStatus == null) { |
| | | return false; |
| | | } |
| | | |
| | | if (taskNo != null) { |
| | | currentStatus.setTaskNo(taskNo); |
| | | } |
| | | if (targetStaNo != null) { |
| | | currentStatus.setTargetStaNo(targetStaNo); |
| | | } |
| | | if (isLoading != null) { |
| | | currentStatus.setLoading(isLoading); |
| | | } |
| | | if (barcode != null) { |
| | | currentStatus.setBarcode(barcode); |
| | | } |
| | | if (runBlock != null) { |
| | | currentStatus.setRunBlock(runBlock); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | public boolean initStationMove(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, |
| | | Integer taskNo, Integer targetStationId, Boolean isLoading, String barcode) { |
| | | lockStations(currentStationId); |
| | | try { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo); |
| | | if (statusList == null) { |
| | | return false; |
| | | } |
| | | ZyStationStatusEntity currentStatus = statusList.stream() |
| | | .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null); |
| | | if (currentStatus == null) { |
| | | return false; |
| | | } |
| | | if (currentStatus.getTaskNo() != null && currentStatus.getTaskNo() > 0 |
| | | && !currentStatus.getTaskNo().equals(taskNo) && currentStatus.isLoading()) { |
| | | return false; |
| | | } |
| | | |
| | | return updateStationDataInternal(currentStationId, currentStationDeviceNo, taskNo, targetStationId, |
| | | isLoading, barcode, false); |
| | | } finally { |
| | | unlockStations(currentStationId); |
| | | } |
| | | } |
| | | |
| | | public boolean stationMoveToNext(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, |
| | | Integer nextStationId, Integer nextStationDeviceNo, Integer taskNo, Integer targetStaNo) { |
| | | lockStations(currentStationId, nextStationId); |
| | | try { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo); |
| | | List<ZyStationStatusEntity> nextStatusList = deviceStatusMap.get(nextStationDeviceNo); |
| | | if (statusList == null || nextStatusList == null) { |
| | | return false; |
| | | } |
| | | |
| | | ZyStationStatusEntity currentStatus = statusList.stream() |
| | | .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null); |
| | | ZyStationStatusEntity nextStatus = nextStatusList.stream() |
| | | .filter(item -> item.getStationId().equals(nextStationId)).findFirst().orElse(null); |
| | | if (currentStatus == null || nextStatus == null) { |
| | | return false; |
| | | } |
| | | if (nextStatus.getTaskNo() != null && nextStatus.getTaskNo() > 0 || nextStatus.isLoading()) { |
| | | return false; |
| | | } |
| | | |
| | | boolean result = updateStationDataInternal(nextStationId, nextStationDeviceNo, taskNo, targetStaNo, true, |
| | | null, false); |
| | | if (!result) { |
| | | return false; |
| | | } |
| | | return updateStationDataInternal(currentStationId, currentStationDeviceNo, 0, 0, false, "", false); |
| | | } finally { |
| | | unlockStations(currentStationId, nextStationId); |
| | | } |
| | | } |
| | | |
| | | public boolean generateStationBarcode(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo) { |
| | | lockStations(currentStationId); |
| | | try { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo); |
| | | if (statusList == null) { |
| | | return false; |
| | | } |
| | | ZyStationStatusEntity currentStatus = statusList.stream() |
| | | .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null); |
| | | if (currentStatus == null) { |
| | | return false; |
| | | } |
| | | |
| | | Random random = new Random(); |
| | | String barcodeTime = String.valueOf(System.currentTimeMillis()); |
| | | String barcode = String.valueOf(random.nextInt(10)) + String.valueOf(random.nextInt(10)) |
| | | + barcodeTime.substring(7); |
| | | return updateStationDataInternal(currentStationId, currentStationDeviceNo, null, null, null, barcode, null); |
| | | } finally { |
| | | unlockStations(currentStationId); |
| | | } |
| | | } |
| | | |
| | | public boolean clearStation(Integer deviceNo, Integer lockTaskNo, Integer currentStationId) { |
| | | lockStations(currentStationId); |
| | | try { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return false; |
| | | } |
| | | ZyStationStatusEntity currentStatus = statusList.stream() |
| | | .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null); |
| | | if (currentStatus == null) { |
| | | return false; |
| | | } |
| | | return updateStationDataInternal(currentStationId, deviceNo, 0, 0, false, "", false); |
| | | } finally { |
| | | unlockStations(currentStationId); |
| | | } |
| | | } |
| | | |
| | | public boolean runBlockStation(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, |
| | | Integer taskNo, Integer blockStationId) { |
| | | lockStations(currentStationId); |
| | | try { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo); |
| | | if (statusList == null) { |
| | | return false; |
| | | } |
| | | ZyStationStatusEntity currentStatus = statusList.stream() |
| | | .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null); |
| | | if (currentStatus == null) { |
| | | return false; |
| | | } |
| | | return updateStationDataInternal(currentStationId, currentStationDeviceNo, taskNo, blockStationId, true, "", |
| | | true); |
| | | } finally { |
| | | unlockStations(currentStationId); |
| | | } |
| | | } |
| | | |
| | | public void clearRunBlock(Integer stationId, Integer deviceNo) { |
| | | lockStations(stationId); |
| | | try { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return; |
| | | } |
| | | ZyStationStatusEntity currentStatus = statusList.stream() |
| | | .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null); |
| | | if (currentStatus == null) { |
| | | return; |
| | | } |
| | | if (currentStatus.isRunBlock()) { |
| | | currentStatus.setRunBlock(false); |
| | | News.info("[WCS Debug] 站点{}堵塞标记已清除", stationId); |
| | | } |
| | | } finally { |
| | | unlockStations(stationId); |
| | | boolean handedOff = guardedHandoffBarcodeStation(deviceNo, taskNo, stationId, targetStationId); |
| | | News.info("[WCS Debug] fake 条码站任务交接完成,deviceNo={},stationId={},taskNo={},targetStationId={},动作=barcodeTaskHandoff,accepted={}", |
| | | deviceNo, stationId, taskNo, targetStationId, handedOff); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | private void traceEvent(Integer deviceNo, TaskRuntimeContext context, String eventType, String message, |
| | | Map<String, Object> details, boolean terminal) { |
| | | if (context != null && context.deviceNo != null && deviceNo != null && !context.deviceNo.equals(deviceNo)) { |
| | | deviceNo = context.deviceNo; |
| | | } |
| | | if (context == null || context.taskNo == null) { |
| | | return; |
| | | } |
| | | try { |
| | | FakeTaskTraceRegistry registry = SpringUtils.getBean(FakeTaskTraceRegistry.class); |
| | | if (registry == null) { |
| | | return; |
| | | } |
| | | registry.record(context.taskNo, context.threadImpl != null ? context.threadImpl : getThreadImpl(deviceNo), |
| | | context.status, context.startStationId, context.currentStationId, context.finalTargetStationId, |
| | | context.blockedStationId, context.getStitchedPathStationIds(), context.getPassedStationIds(), |
| | | context.getPendingStationIds(), context.getLatestAppendedPath(), eventType, message, details, |
| | | terminal); |
| | | } catch (Exception ignore) { |
| | | Map<String, Object> safeDetails = details == null ? new LinkedHashMap<String, Object>() : new LinkedHashMap<String, Object>(details); |
| | | if (!safeDetails.containsKey("segmentNo") && context.segmentNo != null) { |
| | | safeDetails.put("segmentNo", context.segmentNo); |
| | | } |
| | | } |
| | | |
| | | private String getThreadImpl(Integer deviceNo) { |
| | | DeviceConfig deviceConfig = deviceConfigMap.get(deviceNo); |
| | | return deviceConfig == null ? "" : deviceConfig.getThreadImpl(); |
| | | if (!safeDetails.containsKey("segmentCount") && context.segmentCount != null) { |
| | | safeDetails.put("segmentCount", context.segmentCount); |
| | | } |
| | | try { |
| | | FakeTaskTraceRegistry registry = com.core.common.SpringUtils.getBean(FakeTaskTraceRegistry.class); |
| | | if (registry != null) { |
| | | registry.record(context.taskNo, context.threadImpl != null ? context.threadImpl : stateManager.getThreadImpl(deviceNo), |
| | | context.status, context.startStationId, context.currentStationId, context.finalTargetStationId, |
| | | context.blockedStationId, context.getStitchedPathStationIds(), context.getPassedStationIds(), |
| | | context.getPendingStationIds(), context.getLatestAppendedPath(), eventType, message, safeDetails, |
| | | terminal); |
| | | } |
| | | } catch (Exception e) { |
| | | News.info("[WCS Debug] fake trace记录失败,taskNo={},eventType={},异常类型={},异常信息={}", |
| | | context.taskNo, eventType, e.getClass().getSimpleName(), e.getMessage()); |
| | | } |
| | | News.info("[WCS Debug] fake task event,taskNo={},eventType={},status={},currentStationId={},targetStationId={},pending={},details={}", |
| | | context.taskNo, eventType, context.status, context.currentStationId, context.finalTargetStationId, |
| | | context.getPendingStationIds(), JSON.toJSONString(safeDetails)); |
| | | } |
| | | |
| | | private boolean isTerminalStatus(String status) { |
| | | return STATUS_BLOCKED.equals(status) || STATUS_CANCELLED.equals(status) || STATUS_TIMEOUT.equals(status) |
| | | return STATUS_CANCELLED.equals(status) || STATUS_TIMEOUT.equals(status) |
| | | || STATUS_FINISHED.equals(status); |
| | | } |
| | | |
| | | private boolean isCurrentLoopGeneration(Integer taskNo, int loopGeneration) { |
| | | AtomicInteger generation = taskLoopGenerations.get(taskNo); |
| | | return generation != null && generation.get() == loopGeneration; |
| | | } |
| | | |
| | | private Object getTaskLifecycleLock(Integer taskNo) { |
| | | return taskLifecycleLocks.computeIfAbsent(taskNo, key -> new Object()); |
| | | } |
| | | |
| | | /** |
| | | * 检查堆垛机是否已取走指定任务的货物 |
| | | * 通过遍历所有堆垛机设备,检查是否有堆垛机正在执行该任务且已 loaded |
| | | */ |
| | | private boolean isCrnTakenByTask(Integer taskNo) { |
| | | if (taskNo == null || taskNo <= 0) { |
| | | return false; |
| | | } |
| | | try { |
| | | com.zy.asrs.service.BasCrnpService basCrnpService = com.core.common.SpringUtils.getBean(com.zy.asrs.service.BasCrnpService.class); |
| | | List<com.zy.asrs.entity.BasCrnp> basCrnps = basCrnpService.list(); |
| | | for (com.zy.asrs.entity.BasCrnp basCrnp : basCrnps) { |
| | | com.zy.core.thread.CrnThread ct = (com.zy.core.thread.CrnThread) com.zy.core.cache.SlaveConnection.get( |
| | | com.zy.core.enums.SlaveType.Crn, basCrnp.getCrnNo()); |
| | | if (ct == null) { |
| | | continue; |
| | | } |
| | | com.zy.core.model.protocol.CrnProtocol protocol = ct.getStatus(); |
| | | if (protocol != null && taskNo.equals(protocol.getTaskNo()) |
| | | && protocol.getLoaded() == 1) { |
| | | return true; |
| | | } |
| | | } |
| | | } catch (Exception ignore) { |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | |
| | | private static class MoveStepResult { |
| | | |
| | | private final boolean shouldContinue; |
| | | private final long nextDelayMs; |
| | | |
| | | private MoveStepResult(boolean shouldContinue, long nextDelayMs) { |
| | | this.shouldContinue = shouldContinue; |
| | | this.nextDelayMs = nextDelayMs; |
| | | } |
| | | |
| | | private static MoveStepResult continueNow() { |
| | | return new MoveStepResult(true, 0L); |
| | | } |
| | | |
| | | private static MoveStepResult continueAfter(long nextDelayMs) { |
| | | return new MoveStepResult(true, nextDelayMs); |
| | | } |
| | | |
| | | private boolean shouldContinue() { |
| | | return shouldContinue; |
| | | } |
| | | |
| | | private long getNextDelayMs() { |
| | | return nextDelayMs; |
| | | } |
| | | } |
| | | |
| | | private static class IdleStepResult { |
| | | |
| | | private final boolean finished; |
| | | private final long nextDelayMs; |
| | | |
| | | private IdleStepResult(boolean finished, long nextDelayMs) { |
| | | this.finished = finished; |
| | | this.nextDelayMs = nextDelayMs; |
| | | } |
| | | |
| | | private static IdleStepResult finish() { |
| | | return new IdleStepResult(true, 0L); |
| | | } |
| | | |
| | | private static IdleStepResult waitNext(long nextDelayMs) { |
| | | return new IdleStepResult(false, nextDelayMs); |
| | | } |
| | | |
| | | private boolean isFinished() { |
| | | return finished; |
| | | } |
| | | |
| | | private long getNextDelayMs() { |
| | | return nextDelayMs; |
| | | } |
| | | } |
| | | |
| | | private static class TaskRuntimeContext { |
| | | |
| | | private final Integer taskNo; |
| | | private final Integer deviceNo; |
| | | private final String threadImpl; |
| | | private final LinkedBlockingQueue<Integer> pendingPathQueue = new LinkedBlockingQueue<Integer>(); |
| | | private final List<Integer> stitchedPathStationIds = new ArrayList<Integer>(); |
| | |
| | | private boolean generateBarcode; |
| | | private boolean initialized; |
| | | private boolean arrivalHandled; |
| | | private Integer segmentNo; |
| | | private Integer segmentCount; |
| | | private long lastStepAt = System.currentTimeMillis(); |
| | | private long lastCommandAt = System.currentTimeMillis(); |
| | | private long lastProgressAt = System.currentTimeMillis(); |
| | | private Integer lastProgressStationId; |
| | | private int loopGeneration; |
| | | private String status = STATUS_WAITING; |
| | | |
| | | private TaskRuntimeContext(Integer taskNo, String threadImpl) { |
| | | private TaskRuntimeContext(Integer taskNo, Integer deviceNo, String threadImpl) { |
| | | this.taskNo = taskNo; |
| | | this.deviceNo = deviceNo; |
| | | this.threadImpl = threadImpl; |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | private void replaceLatestPath(List<Integer> path) { |
| | | latestAppendedPath.clear(); |
| | | if (path == null) { |
| | | return; |
| | | } |
| | | for (Integer stationId : path) { |
| | | if (stationId != null) { |
| | | latestAppendedPath.add(stationId); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void clearPendingPath() { |
| | | pendingPathQueue.clear(); |
| | | latestAppendedPath.clear(); |
| | | } |
| | | |
| | | private void clearPendingPathWithLog(Integer deviceNo, Integer taskNo, Integer currentStationId, |
| | | Integer targetStationId, Integer blockedStationId, String reason) { |
| | | News.info("[WCS Debug] fake task清空待执行路径,原因={},deviceNo={},taskNo={},currentStationId={},targetStationId={},blockedStationId={},pendingBeforeClear={}", |
| | | reason, deviceNo, taskNo, currentStationId, targetStationId, blockedStationId, getPendingStationIds()); |
| | | clearPendingPath(); |
| | | } |
| | | |
| | | private void addPassedStation(Integer stationId) { |
| | | if (stationId == null) { |
| | | return; |
| | |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.core.common.Cools; |
| | | import com.zy.asrs.domain.param.CreateInTaskParam; |
| | | import com.zy.asrs.domain.param.CreateOutTaskParam; |
| | | import com.zy.asrs.utils.Utils; |
| | | import com.zy.asrs.entity.*; |
| | | import com.zy.asrs.service.*; |
| | | import com.zy.common.entity.FindCrnNoResult; |
| | | import com.zy.common.service.CommonService; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.News; |
| | | import com.zy.core.cache.MessageQueue; |
| | |
| | | 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.model.protocol.DualCrnProtocol; |
| | | import com.zy.core.model.protocol.StationProtocol; |
| | | 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.StoreInTaskGenerationService; |
| | | 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.DualCrnThread; |
| | | import com.zy.core.thread.StationThread; |
| | | import com.zy.core.utils.CrnOperateProcessUtils; |
| | | import com.zy.core.utils.DualCrnOperateProcessUtils; |
| | |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.*; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | @Slf4j |
| | | @Component |
| | | public class FakeProcess implements MainProcessPluginApi, StoreInTaskPolicy { |
| | | private static final long MAIN_DISPATCH_INTERVAL_MS = 200L; |
| | | private static final long ASYNC_DISPATCH_INTERVAL_MS = 50L; |
| | | private long getMainDispatchIntervalMs() { |
| | | return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_IDLE_LOOP_DELAY_MS, 200L); |
| | | } |
| | | |
| | | private static final Map<Integer, Long> stationStayTimeMap = new ConcurrentHashMap<>(); |
| | | 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"; |
| | | |
| | | @Autowired |
| | | private WrkMastService wrkMastService; |
| | | @Autowired |
| | | private BasDevpService basDevpService; |
| | | private static final Map<String, Long> OUT_STATION_STAY_MARKER = new HashMap<String, Long>(); |
| | | |
| | | private final Object outStationStayLock = new Object(); |
| | | |
| | | private boolean shouldDispatchOutStationWriteInfo(String taskNo) { |
| | | long now = System.currentTimeMillis(); |
| | | long timeoutMs = getOutStationStayTimeoutMs(); |
| | | synchronized (outStationStayLock) { |
| | | Long lastTime = OUT_STATION_STAY_MARKER.get(taskNo); |
| | | if (lastTime == null || now - lastTime >= timeoutMs) { |
| | | OUT_STATION_STAY_MARKER.put(taskNo, now); |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | private void clearOutStationStayMarker(String taskNo) { |
| | | synchronized (outStationStayLock) { |
| | | OUT_STATION_STAY_MARKER.remove(taskNo); |
| | | } |
| | | } |
| | | |
| | | private void dispatchOutStationWriteInfoIfReady(WrkMast wrkMast, BasCrnp basCrnp) { |
| | | String taskNo = wrkMast == null ? null : wrkMast.getWmsWrkNo(); |
| | | if (taskNo == null || !shouldDispatchOutStationWriteInfo(taskNo)) { |
| | | return; |
| | | } |
| | | List<StationObjModel> outStationList = basCrnp.getOutStationList$(); |
| | | if (outStationList.isEmpty()) { |
| | | News.info("堆垛机:{} 出库站点未设置", basCrnp.getCrnNo()); |
| | | return; |
| | | } |
| | | for (StationObjModel stationObjModel : outStationList) { |
| | | if (!stationObjModel.getStationId().equals(wrkMast.getSourceStaNo())) { |
| | | continue; |
| | | } |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | StationCommand command = stationThread.getCommand(StationCommandType.WRITE_INFO, 9998, |
| | | wrkMast.getSourceStaNo(), 0, 0); |
| | | stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "crn-out-complete-write-info"); |
| | | } |
| | | } |
| | | |
| | | @Autowired |
| | | private ConfigService configService; |
| | | @Autowired |
| | | private LocMastService locMastService; |
| | | private WrkMastService wrkMastService; |
| | | @Autowired |
| | | private CommonService commonService; |
| | | private WrkAnalysisService wrkAnalysisService; |
| | | @Autowired |
| | | private BasCrnpService basCrnpService; |
| | | @Autowired |
| | | private BasDualCrnpService basDualCrnpService; |
| | | @Autowired |
| | | private RedisUtil redisUtil; |
| | | @Autowired |
| | |
| | | @Autowired |
| | | private StationOperateProcessUtils stationOperateProcessUtils; |
| | | @Autowired |
| | | private WrkAnalysisService wrkAnalysisService; |
| | | @Autowired |
| | | private DualCrnOperateProcessUtils dualCrnOperateProcessUtils; |
| | | @Autowired |
| | | private StoreInTaskGenerationService storeInTaskGenerationService; |
| | | @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(); |
| | | // 仿真异步任务 |
| | | submitAsyncTasks(ASYNC_DISPATCH_INTERVAL_MS); |
| | | fakeStationMonitor.submitMonitorTasks(asyncDispatchIntervalMs); |
| | | // 仿真输送线堵塞检测 |
| | | stationOperateProcessUtils.submitCheckStationRunBlockTasks(MainProcessLane.FAKE_STATION_RUN_BLOCK, ASYNC_DISPATCH_INTERVAL_MS); |
| | | stationOperateProcessUtils.submitCheckStationRunBlockTasks(MainProcessLane.FAKE_STATION_RUN_BLOCK, asyncDispatchIntervalMs); |
| | | |
| | | // 请求生成入库任务 |
| | | generateStoreWrkFile(); |
| | | fakeTaskGenerator.submitGenerateTasks(); |
| | | |
| | | // 执行堆垛机任务 |
| | | crnOperateUtils.submitCrnIoTasks(MainProcessLane.FAKE_CRN_IO, MAIN_DISPATCH_INTERVAL_MS); |
| | | crnOperateUtils.submitCrnIoTasks(MainProcessLane.FAKE_CRN_IO, mainDispatchIntervalMs); |
| | | // 堆垛机任务执行完成 |
| | | submitCrnIoExecuteFinishTasks(MAIN_DISPATCH_INTERVAL_MS); |
| | | submitCrnIoExecuteFinishTasks(mainDispatchIntervalMs); |
| | | // 执行输送站点入库任务 |
| | | stationOperateProcessUtils.submitStationInTasks(MainProcessLane.FAKE_STATION_IN, MAIN_DISPATCH_INTERVAL_MS); |
| | | stationOperateProcessUtils.submitStationInTasks(MainProcessLane.FAKE_STATION_IN, mainDispatchIntervalMs); |
| | | // 检测入库任务是否已经到达目标站台 |
| | | stationOperateProcessUtils.submitInboundStationArrivalTasks(MAIN_DISPATCH_INTERVAL_MS); |
| | | stationOperateProcessUtils.submitInboundStationArrivalTasks(mainDispatchIntervalMs); |
| | | // 输送线执行堆垛机出库后的站台流转 |
| | | stationOperateProcessUtils.submitCrnStationOutTasks(MainProcessLane.FAKE_STATION_OUT, MAIN_DISPATCH_INTERVAL_MS); |
| | | stationOperateProcessUtils.submitCrnStationOutTasks(MainProcessLane.FAKE_STATION_OUT, mainDispatchIntervalMs); |
| | | // 检测出库任务是否已经到达目标站台 |
| | | stationOperateProcessUtils.submitStationOutExecuteFinishTasks(MAIN_DISPATCH_INTERVAL_MS); |
| | | stationOperateProcessUtils.submitStationOutExecuteFinishTasks(mainDispatchIntervalMs); |
| | | // 检测站台运行完成后的任务转完成 |
| | | stationOperateProcessUtils.submitCheckTaskToCompleteTasks(MAIN_DISPATCH_INTERVAL_MS); |
| | | stationOperateProcessUtils.submitCheckTaskToCompleteTasks(mainDispatchIntervalMs); |
| | | // 检测并处理出库排序 |
| | | stationOperateProcessUtils.submitCheckStationOutOrderTasks(MainProcessLane.FAKE_STATION_OUT_ORDER, MAIN_DISPATCH_INTERVAL_MS); |
| | | stationOperateProcessUtils.submitCheckStationOutOrderTasks(MainProcessLane.FAKE_STATION_OUT_ORDER, mainDispatchIntervalMs); |
| | | // 执行双工位堆垛机任务 |
| | | dualCrnOperateProcessUtils.submitDualCrnIoTasks(MainProcessLane.FAKE_DUAL_CRN_IO, MAIN_DISPATCH_INTERVAL_MS); |
| | | dualCrnOperateProcessUtils.submitDualCrnIoTasks(MainProcessLane.FAKE_DUAL_CRN_IO, mainDispatchIntervalMs); |
| | | // 双工位堆垛机任务执行完成 |
| | | dualCrnOperateProcessUtils.submitDualCrnIoExecuteFinishTasks(MainProcessLane.FAKE_DUAL_CRN_IO_FINISH, MAIN_DISPATCH_INTERVAL_MS); |
| | | dualCrnOperateProcessUtils.submitDualCrnIoExecuteFinishTasks(MainProcessLane.FAKE_DUAL_CRN_IO_FINISH, mainDispatchIntervalMs); |
| | | |
| | | News.info("[WCS Debug] 主线程Run执行完成,耗时:{}ms", System.currentTimeMillis() - startTime); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | private void submitAsyncTasks(long minIntervalMs) { |
| | | submitAsyncTask("checkInStationHasTask", minIntervalMs, this::checkInStationHasTask); |
| | | submitAsyncTask("generateFakeInTask", minIntervalMs, this::generateFakeInTask); |
| | | submitAsyncTask("generateFakeOutTask", minIntervalMs, this::generateFakeOutTask); |
| | | submitAsyncTask("calcAllStationStayTime", minIntervalMs, this::calcAllStationStayTime); |
| | | submitAsyncTask("checkOutStationStayTimeOut", minIntervalMs, this::checkOutStationStayTimeOut); |
| | | submitAsyncTask("checkInStationCrnTake", minIntervalMs, this::checkInStationCrnTake); |
| | | } |
| | | |
| | | private void submitAsyncTask(String taskName, long minIntervalMs, Runnable task) { |
| | | mainProcessTaskSubmitter.submitKeyedSerialTask( |
| | | MainProcessLane.FAKE_ASYNC, |
| | | taskName, |
| | | taskName, |
| | | minIntervalMs, |
| | | task |
| | | ); |
| | | private void syncConfigToComponents() { |
| | | fakeStationMonitor.setEnableFake(enableFake); |
| | | fakeStationMonitor.setFakeGenerateInTask(fakeGenerateInTask); |
| | | fakeTaskGenerator.setFakeRealTaskRequestWms(fakeRealTaskRequestWms); |
| | | fakeTaskGenerator.setFakeGenerateInTask(fakeGenerateInTask); |
| | | fakeTaskGenerator.setFakeGenerateOutTask(fakeGenerateOutTask); |
| | | } |
| | | |
| | | private void submitCrnIoExecuteFinishTasks(long minIntervalMs) { |
| | |
| | | } |
| | | } |
| | | |
| | | private void submitGenerateStoreTasks() { |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap(); |
| | | if (stationMap == null || stationMap.isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | List<StationObjModel> barcodeStations = getBarcodeStations(basDevp); |
| | | for (StationObjModel stationObjModel : barcodeStations) { |
| | | Integer stationId = stationObjModel == null ? null : stationObjModel.getStationId(); |
| | | if (stationId == null || !stationMap.containsKey(stationId)) { |
| | | continue; |
| | | } |
| | | if (!canRequestStoreIn(stationMap.get(stationId))) { |
| | | continue; |
| | | } |
| | | storeInTaskGenerationService.submitGenerateStoreTask( |
| | | this, |
| | | basDevp, |
| | | stationObjModel, |
| | | MainProcessLane.FAKE_GENERATE_STORE, |
| | | MAIN_DISPATCH_INTERVAL_MS, |
| | | () -> storeInTaskGenerationService.generate(this, basDevp, stationObjModel) |
| | | ); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 检测入库站是否有任务生成,并仿真生成模拟入库站点数据 |
| | | private void checkInStationHasTask() { |
| | | if (!enableFake.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | if (!fakeGenerateInTask.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap(); |
| | | |
| | | List<StationObjModel> list = basDevp.getInStationList$(); |
| | | for (StationObjModel entity : list) { |
| | | Integer stationId = entity.getStationId(); |
| | | if (!stationMap.containsKey(stationId)) { |
| | | continue; |
| | | } |
| | | |
| | | StationProtocol stationProtocol = stationMap.get(stationId); |
| | | if (stationProtocol == null) { |
| | | continue; |
| | | } |
| | | |
| | | Object lock = redisUtil.get(RedisKeyType.GENERATE_FAKE_IN_STATION_DATA_LIMIT.key + stationId); |
| | | if (lock != null) { |
| | | continue; |
| | | } |
| | | |
| | | // 满足自动、无物、工作号0,生成入库数据 |
| | | if (stationProtocol.isAutoing() |
| | | && !stationProtocol.isLoading() |
| | | && stationProtocol.getTaskNo() == 0) { |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, |
| | | commonService.getWorkNo(WrkIoType.FAKE_TASK_NO.id), stationId, |
| | | entity.getBarcodeStation().getStationId(), 0); |
| | | stationCommandDispatcher.dispatch(basDevp.getDevpNo(), command, "fake-process", "fake-enable-in"); |
| | | if (entity.getBarcodeStation() != null && entity.getBarcodeStation().getStationId() != null) { |
| | | Utils.precomputeInTaskEnableRow(entity.getBarcodeStation().getStationId()); |
| | | } |
| | | redisUtil.set(RedisKeyType.GENERATE_FAKE_IN_STATION_DATA_LIMIT.key + stationId, "lock", 5); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 生成仿真模拟入库任务 |
| | | private void generateFakeInTask() { |
| | | if (!enableFake.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | if (fakeRealTaskRequestWms.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | if (!fakeGenerateInTask.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap(); |
| | | |
| | | List<StationObjModel> list = basDevp.getBarcodeStationList$(); |
| | | for (StationObjModel model : list) { |
| | | Integer stationId = model.getStationId(); |
| | | if (!stationMap.containsKey(stationId)) { |
| | | continue; |
| | | } |
| | | |
| | | StationProtocol stationProtocol = stationMap.get(stationId); |
| | | if (stationProtocol == null) { |
| | | continue; |
| | | } |
| | | |
| | | Object object = redisUtil.get(RedisKeyType.GENERATE_FAKE_IN_TASK_LIMIT.key + stationId); |
| | | if (object != null) { |
| | | continue; |
| | | } |
| | | |
| | | // 满足自动、有物、有工作号,生成入库数据 |
| | | if (stationProtocol.isAutoing() |
| | | && stationProtocol.isLoading() |
| | | && stationProtocol.getTaskNo() > 0) { |
| | | if (Cools.isEmpty(stationProtocol.getBarcode())) { |
| | | continue; |
| | | } |
| | | |
| | | // 检测任务是否生成 |
| | | List<WrkMast> wrkMasts = wrkMastService |
| | | .list(new QueryWrapper<WrkMast>().eq("barcode", stationProtocol.getBarcode())); |
| | | if (!wrkMasts.isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | List<LocMast> locMastList = locMastService |
| | | .list(new QueryWrapper<LocMast>().eq("loc_sts", String.valueOf(LocStsType.O))); |
| | | if (locMastList.isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | int nextInt = new Random().nextInt(locMastList.size()); |
| | | LocMast locMast = locMastList.get(nextInt); |
| | | |
| | | FindCrnNoResult findCrnNoResult = commonService.findCrnNoByLocNo(locMast.getLocNo()); |
| | | if (findCrnNoResult == null) { |
| | | continue; |
| | | } |
| | | |
| | | Integer targetStationId = commonService.findInStationId(findCrnNoResult, stationId); |
| | | if (targetStationId == null) { |
| | | continue; |
| | | } |
| | | |
| | | CreateInTaskParam taskParam = new CreateInTaskParam(); |
| | | taskParam.setTaskNo(String.valueOf(commonService.getWorkNo(WrkIoType.IN.id))); |
| | | taskParam.setSourceStaNo(stationId); |
| | | taskParam.setStaNo(targetStationId); |
| | | taskParam.setLocNo(locMast.getLocNo()); |
| | | taskParam.setBarcode(stationProtocol.getBarcode()); |
| | | WrkMast wrkMast = commonService.createInTask(taskParam); |
| | | |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), |
| | | stationId, stationId, 0); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | continue; |
| | | } |
| | | stationCommandDispatcher.dispatch(basDevp.getDevpNo(), command, "fake-process", "fake-in-task"); |
| | | redisUtil.set(RedisKeyType.GENERATE_FAKE_IN_TASK_LIMIT.key + stationId, "lock", 5); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 生成仿真模拟出库任务 |
| | | private void generateFakeOutTask() { |
| | | try { |
| | | if (!enableFake.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | if (fakeRealTaskRequestWms.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | if (!fakeGenerateOutTask.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap(); |
| | | |
| | | List<StationObjModel> list = basDevp.getOutStationList$(); |
| | | for (StationObjModel entity : list) { |
| | | Integer stationId = entity.getStationId(); |
| | | if (!stationMap.containsKey(stationId)) { |
| | | continue; |
| | | } |
| | | |
| | | StationProtocol stationProtocol = stationMap.get(stationId); |
| | | if (stationProtocol == null) { |
| | | continue; |
| | | } |
| | | |
| | | Object object = redisUtil.get(RedisKeyType.GENERATE_FAKE_OUT_TASK_LIMIT.key + stationId); |
| | | if (object != null) { |
| | | return; |
| | | } |
| | | |
| | | // 满足自动、无物、工作号0,生成出库数据 |
| | | if (stationProtocol.isAutoing() |
| | | && !stationProtocol.isLoading() |
| | | && stationProtocol.getTaskNo() == 0) { |
| | | List<LocMast> locMastList = locMastService |
| | | .list(new QueryWrapper<LocMast>().eq("loc_sts", String.valueOf(LocStsType.F))); |
| | | if (locMastList.isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | LocMast locMast = locMastList.get(0); |
| | | |
| | | CreateOutTaskParam taskParam = new CreateOutTaskParam(); |
| | | taskParam.setTaskNo(String.valueOf(commonService.getWorkNo(WrkIoType.OUT.id))); |
| | | taskParam.setStaNo(stationId); |
| | | taskParam.setLocNo(locMast.getLocNo()); |
| | | boolean result = commonService.createOutTask(taskParam); |
| | | redisUtil.set(RedisKeyType.GENERATE_FAKE_OUT_TASK_LIMIT.key + stationId, "lock", 10); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("generateFakeOutTask execute error", e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 请求生成入库任务 |
| | | * 入库站,根据条码扫描生成入库工作档 |
| | | */ |
| | | public void generateStoreWrkFile() { |
| | | submitGenerateStoreTasks(); |
| | | } |
| | | |
| | | @Override |
| | | public boolean isEnabled() { |
| | | return !"N".equals(fakeRealTaskRequestWms); |
| | | return fakeTaskGenerator.isEnabled(); |
| | | } |
| | | |
| | | @Override |
| | | public void onRequestPermitGranted(StoreInTaskContext context) { |
| | | redisUtil.set(getGenerateLockKey(context), "lock", 3); |
| | | fakeTaskGenerator.onRequestPermitGranted(context); |
| | | } |
| | | |
| | | @Override |
| | | public void afterTaskCreated(StoreInTaskContext context, WrkMast wrkMast) { |
| | | Integer stationId = context.getStationObjModel().getStationId(); |
| | | StationCommand command = context.getStationThread().getCommand(StationCommandType.WRITE_INFO, |
| | | wrkMast.getWrkNo(), stationId, stationId, 0); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | return; |
| | | } |
| | | stationCommandDispatcher.dispatch(context.getBasDevp().getDevpNo(), command, "fake-process", "write-info"); |
| | | } |
| | | |
| | | private boolean canRequestStoreIn(StationProtocol stationProtocol) { |
| | | return stationProtocol != null |
| | | && stationProtocol.isAutoing() |
| | | && stationProtocol.isLoading() |
| | | && stationProtocol.getTaskNo() > 0 |
| | | && !Cools.isEmpty(stationProtocol.getBarcode()); |
| | | } |
| | | |
| | | // 计算所有站点停留时间 |
| | | public void calcAllStationStayTime() { |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | List<StationProtocol> list = stationThread.getStatus(); |
| | | for (StationProtocol stationProtocol : list) { |
| | | if (stationProtocol.getTaskNo() > 0 |
| | | && !stationStayTimeMap.containsKey(stationProtocol.getStationId())) { |
| | | stationStayTimeMap.put(stationProtocol.getStationId(), System.currentTimeMillis()); |
| | | } |
| | | |
| | | if (stationProtocol.getTaskNo() <= 0 |
| | | && stationStayTimeMap.containsKey(stationProtocol.getStationId())) { |
| | | stationStayTimeMap.remove(stationProtocol.getStationId()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 检测出库站点停留是否超时 |
| | | public void checkOutStationStayTimeOut() { |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | List<StationObjModel> outStationList = basDevp.getOutStationList$(); |
| | | if (outStationList.isEmpty()) { |
| | | News.info("输送线:{} 出库站点未设置", basDevp.getDevpNo()); |
| | | continue; |
| | | } |
| | | |
| | | for (StationObjModel stationObjModel : outStationList) { |
| | | Object lock = redisUtil |
| | | .get(RedisKeyType.CHECK_OUT_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId()); |
| | | if (lock != null) { |
| | | continue; |
| | | } |
| | | |
| | | Long stayTime = stationStayTimeMap.get(stationObjModel.getStationId()); |
| | | if (stayTime == null) { |
| | | continue; |
| | | } |
| | | |
| | | if (System.currentTimeMillis() - stayTime > 1000 * 60) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, |
| | | stationObjModel.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | StationCommand command = stationThread.getCommand(StationCommandType.RESET, 0, |
| | | stationObjModel.getStationId(), 0, 0); |
| | | if (command == null) { |
| | | continue; |
| | | } |
| | | |
| | | stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "out-station-reset"); |
| | | redisUtil.set( |
| | | RedisKeyType.CHECK_OUT_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId(), |
| | | "lock", 10); |
| | | News.info("输送站点出库重置命令下发成功,站点号={},命令数据={}", stationObjModel.getStationId(), |
| | | JSON.toJSONString(command)); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 检测入库站点堆垛机是否取走货物 |
| | | public void checkInStationCrnTake() { |
| | | List<BasCrnp> basCrnps = basCrnpService.list(new QueryWrapper<>()); |
| | | for (BasCrnp basCrnp : basCrnps) { |
| | | List<StationObjModel> inStationList = basCrnp.getInStationList$(); |
| | | if (inStationList.isEmpty()) { |
| | | News.info("堆垛机:{} 入库站点未设置", basCrnp.getCrnNo()); |
| | | continue; |
| | | } |
| | | checkInStationListCrnTake(inStationList); |
| | | } |
| | | |
| | | List<BasDualCrnp> basDualCrnps = basDualCrnpService.list(new QueryWrapper<>()); |
| | | for (BasDualCrnp basDualCrnp : basDualCrnps) { |
| | | List<StationObjModel> inStationList = basDualCrnp.getInStationList$(); |
| | | if (inStationList.isEmpty()) { |
| | | News.info("双工位堆垛机:{} 入库站点未设置", basDualCrnp.getCrnNo()); |
| | | continue; |
| | | } |
| | | checkInStationListCrnTake(inStationList); |
| | | } |
| | | } |
| | | |
| | | private void checkInStationListCrnTake(List<StationObjModel> inStationList) { |
| | | for (StationObjModel stationObjModel : inStationList) { |
| | | Object lock = redisUtil |
| | | .get(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId()); |
| | | if (lock != null) { |
| | | continue; |
| | | } |
| | | |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, |
| | | stationObjModel.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap(); |
| | | StationProtocol stationProtocol = statusMap.get(stationObjModel.getStationId()); |
| | | if (stationProtocol == null) { |
| | | continue; |
| | | } |
| | | |
| | | if (stationProtocol.getTaskNo() > 0) { |
| | | StationCommand command = stationThread.getCommand(StationCommandType.RESET, 0, |
| | | stationObjModel.getStationId(), 0, 0); |
| | | if (command == null) { |
| | | continue; |
| | | } |
| | | |
| | | WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo()); |
| | | if (wrkMast == null) { |
| | | stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "in-station-reset-task-over"); |
| | | redisUtil.set( |
| | | RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId(), |
| | | "lock", 10); |
| | | News.info("输送站点重置命令下发成功(task_over),站点号={},命令数据={}", stationObjModel.getStationId(), |
| | | JSON.toJSONString(command)); |
| | | } else { |
| | | if (wrkMast.getWrkSts() != WrkStsType.NEW_INBOUND.sts |
| | | && wrkMast.getWrkSts() != WrkStsType.INBOUND_STATION_RUN.sts) { |
| | | Integer crnNo = wrkMast.getCrnNo(); |
| | | if (crnNo != null) { |
| | | CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, crnNo); |
| | | if (crnThread == null) { |
| | | continue; |
| | | } |
| | | CrnProtocol crnProtocol = crnThread.getStatus(); |
| | | if (!crnProtocol.getStatusType().equals(CrnStatusType.PUT_MOVING) |
| | | && !crnProtocol.getStatusType().equals(CrnStatusType.PUTTING)) { |
| | | continue; |
| | | } |
| | | |
| | | stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "in-station-reset-crn-fetch"); |
| | | redisUtil.set(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key |
| | | + stationObjModel.getStationId(), "lock", 10); |
| | | News.info("输送站点重置命令下发成功(crn_fetch),站点号={},命令数据={}", stationObjModel.getStationId(), |
| | | JSON.toJSONString(command)); |
| | | } else { |
| | | Integer dualCrnNo = wrkMast.getDualCrnNo(); |
| | | DualCrnThread dualCrnThread = (DualCrnThread) SlaveConnection.get(SlaveType.DualCrn, |
| | | dualCrnNo); |
| | | if (dualCrnThread == null) { |
| | | continue; |
| | | } |
| | | DualCrnProtocol dualCrnProtocol = dualCrnThread.getStatus(); |
| | | |
| | | boolean reset = false; |
| | | if (dualCrnProtocol.getTaskNo() > 0 && dualCrnProtocol.getLoaded() == 1) { |
| | | reset = true; |
| | | } |
| | | |
| | | if (dualCrnProtocol.getTaskNoTwo() > 0 && dualCrnProtocol.getLoadedTwo() == 1) { |
| | | reset = true; |
| | | } |
| | | |
| | | if (!reset) { |
| | | continue; |
| | | } |
| | | |
| | | stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "in-station-reset-dual-crn-fetch"); |
| | | redisUtil.set(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key |
| | | + stationObjModel.getStationId(), "lock", 10); |
| | | News.info("输送站点重置命令下发成功(crn_fetch),站点号={},命令数据={}", stationObjModel.getStationId(), |
| | | JSON.toJSONString(command)); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | fakeTaskGenerator.afterTaskCreated(context, wrkMast); |
| | | } |
| | | |
| | | // 堆垛机任务执行完成 |
| | |
| | | } else if (wrkMast.getWrkSts() == WrkStsType.OUTBOUND_RUN.sts) { |
| | | updateWrkSts = WrkStsType.OUTBOUND_RUN_COMPLETE.sts; |
| | | |
| | | // 生成仿真站点数据 |
| | | List<StationObjModel> outStationList = basCrnp.getOutStationList$(); |
| | | if (outStationList.isEmpty()) { |
| | | News.info("堆垛机:{} 出库站点未设置", basCrnp.getCrnNo()); |
| | | return; |
| | | } |
| | | |
| | | for (StationObjModel stationObjModel : outStationList) { |
| | | if (!stationObjModel.getStationId().equals(wrkMast.getSourceStaNo())) { |
| | | continue; |
| | | } |
| | | |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, |
| | | stationObjModel.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | // 生成仿真站点数据 |
| | | StationCommand command = stationThread.getCommand(StationCommandType.WRITE_INFO, 9998, |
| | | wrkMast.getSourceStaNo(), 0, 0); |
| | | stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "crn-out-complete-write-info"); |
| | | } |
| | | 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) { |
| | |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | clearOutStationStayMarker(wrkMast.getWmsWrkNo()); |
| | | wrkAnalysisService.markCraneComplete(wrkMast, now, updateWrkSts); |
| | | CrnCommand resetCommand = crnThread.getResetCommand(crnProtocol.getTaskNo(), crnProtocol.getCrnNo()); |
| | | MessageQueue.offer(SlaveType.Crn, crnProtocol.getCrnNo(), new Task(2, resetCommand)); |
| New file |
| | |
| | | package com.zy.core.plugin.station; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.core.common.Cools; |
| | | import com.zy.asrs.entity.*; |
| | | import com.zy.asrs.service.*; |
| | | import com.zy.asrs.utils.Utils; |
| | | import com.zy.common.service.CommonService; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.News; |
| | | import com.zy.core.dispatch.StationCommandDispatcher; |
| | | import com.zy.core.enums.*; |
| | | import com.zy.core.model.StationObjModel; |
| | | import com.zy.core.model.command.StationCommand; |
| | | import com.zy.core.model.protocol.CrnProtocol; |
| | | import com.zy.core.model.protocol.DualCrnProtocol; |
| | | import com.zy.core.model.protocol.StationProtocol; |
| | | import com.zy.core.thread.DualCrnThread; |
| | | 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.cache.SlaveConnection; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | @Slf4j |
| | | @Component |
| | | public class FakeStationMonitor { |
| | | |
| | | private static final Map<Integer, Long> stationStayTimeMap = new ConcurrentHashMap<>(); |
| | | |
| | | private volatile String enableFake = "N"; |
| | | private volatile String fakeGenerateInTask = "Y"; |
| | | |
| | | @Autowired |
| | | private BasDevpService basDevpService; |
| | | @Autowired |
| | | private BasCrnpService basCrnpService; |
| | | @Autowired |
| | | private BasDualCrnpService basDualCrnpService; |
| | | @Autowired |
| | | private WrkMastService wrkMastService; |
| | | @Autowired |
| | | private CommonService commonService; |
| | | @Autowired |
| | | private RedisUtil redisUtil; |
| | | @Autowired |
| | | private StationCommandDispatcher stationCommandDispatcher; |
| | | @Autowired |
| | | private MainProcessTaskSubmitter mainProcessTaskSubmitter; |
| | | |
| | | public void setEnableFake(String enableFake) { |
| | | this.enableFake = enableFake; |
| | | } |
| | | |
| | | public void setFakeGenerateInTask(String fakeGenerateInTask) { |
| | | this.fakeGenerateInTask = fakeGenerateInTask; |
| | | } |
| | | |
| | | public void submitMonitorTasks(long minIntervalMs) { |
| | | submitAsyncTask("checkInStationHasTask", minIntervalMs, this::checkInStationHasTask); |
| | | submitAsyncTask("calcAllStationStayTime", minIntervalMs, this::calcAllStationStayTime); |
| | | submitAsyncTask("checkOutStationStayTimeOut", minIntervalMs, this::checkOutStationStayTimeOut); |
| | | submitAsyncTask("checkInStationCrnTake", minIntervalMs, this::checkInStationCrnTake); |
| | | } |
| | | |
| | | private void submitAsyncTask(String taskName, long minIntervalMs, Runnable task) { |
| | | mainProcessTaskSubmitter.submitKeyedSerialTask( |
| | | MainProcessLane.FAKE_ASYNC, |
| | | taskName, |
| | | taskName, |
| | | minIntervalMs, |
| | | task |
| | | ); |
| | | } |
| | | |
| | | // 计算所有站点停留时间 |
| | | public void calcAllStationStayTime() { |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | List<StationProtocol> list = stationThread.getStatus(); |
| | | for (StationProtocol stationProtocol : list) { |
| | | if (stationProtocol.getTaskNo() > 0 |
| | | && !stationStayTimeMap.containsKey(stationProtocol.getStationId())) { |
| | | stationStayTimeMap.put(stationProtocol.getStationId(), System.currentTimeMillis()); |
| | | } |
| | | |
| | | if (stationProtocol.getTaskNo() <= 0 |
| | | && stationStayTimeMap.containsKey(stationProtocol.getStationId())) { |
| | | stationStayTimeMap.remove(stationProtocol.getStationId()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 检测出库站点停留是否超时 |
| | | public void checkOutStationStayTimeOut() { |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | List<StationObjModel> outStationList = basDevp.getOutStationList$(); |
| | | if (outStationList.isEmpty()) { |
| | | News.info("输送线:{} 出库站点未设置", basDevp.getDevpNo()); |
| | | continue; |
| | | } |
| | | |
| | | for (StationObjModel stationObjModel : outStationList) { |
| | | Object lock = redisUtil |
| | | .get(RedisKeyType.CHECK_OUT_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId()); |
| | | if (lock != null) { |
| | | continue; |
| | | } |
| | | |
| | | Long stayTime = stationStayTimeMap.get(stationObjModel.getStationId()); |
| | | if (stayTime == null) { |
| | | continue; |
| | | } |
| | | |
| | | if (System.currentTimeMillis() - stayTime > 1000 * 60) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, |
| | | stationObjModel.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | StationCommand command = stationThread.getCommand(StationCommandType.RESET, 0, |
| | | stationObjModel.getStationId(), 0, 0); |
| | | if (command == null) { |
| | | continue; |
| | | } |
| | | |
| | | stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "out-station-reset"); |
| | | redisUtil.set( |
| | | RedisKeyType.CHECK_OUT_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId(), |
| | | "lock", 10); |
| | | News.info("输送站点出库重置命令下发成功,站点号={},命令数据={}", stationObjModel.getStationId(), |
| | | JSON.toJSONString(command)); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 检测入库站点堆垛机是否取走货物 |
| | | public void checkInStationCrnTake() { |
| | | List<BasCrnp> basCrnps = basCrnpService.list(new QueryWrapper<>()); |
| | | for (BasCrnp basCrnp : basCrnps) { |
| | | List<StationObjModel> inStationList = basCrnp.getInStationList$(); |
| | | if (inStationList.isEmpty()) { |
| | | News.info("堆垛机:{} 入库站点未设置", basCrnp.getCrnNo()); |
| | | continue; |
| | | } |
| | | checkInStationListCrnTake(inStationList); |
| | | } |
| | | |
| | | List<BasDualCrnp> basDualCrnps = basDualCrnpService.list(new QueryWrapper<>()); |
| | | for (BasDualCrnp basDualCrnp : basDualCrnps) { |
| | | List<StationObjModel> inStationList = basDualCrnp.getInStationList$(); |
| | | if (inStationList.isEmpty()) { |
| | | News.info("双工位堆垛机:{} 入库站点未设置", basDualCrnp.getCrnNo()); |
| | | continue; |
| | | } |
| | | checkInStationListCrnTake(inStationList); |
| | | } |
| | | } |
| | | |
| | | private void checkInStationListCrnTake(List<StationObjModel> inStationList) { |
| | | for (StationObjModel stationObjModel : inStationList) { |
| | | Object lock = redisUtil |
| | | .get(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId()); |
| | | if (lock != null) { |
| | | continue; |
| | | } |
| | | |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, |
| | | stationObjModel.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap(); |
| | | StationProtocol stationProtocol = statusMap.get(stationObjModel.getStationId()); |
| | | if (stationProtocol == null) { |
| | | continue; |
| | | } |
| | | |
| | | if (stationProtocol.getTaskNo() > 0) { |
| | | StationCommand command = stationThread.getCommand(StationCommandType.RESET, 0, |
| | | stationObjModel.getStationId(), 0, 0); |
| | | if (command == null) { |
| | | continue; |
| | | } |
| | | |
| | | WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo()); |
| | | if (wrkMast == null) { |
| | | News.info("入库站点清理命中,站点号={},站点taskNo={},原因=wrk_missing,准备RESET", |
| | | stationObjModel.getStationId(), stationProtocol.getTaskNo()); |
| | | stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "in-station-reset-task-over"); |
| | | redisUtil.set( |
| | | RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId(), |
| | | "lock", 10); |
| | | News.info("输送站点重置命令下发成功(task_over),站点号={},命令数据={}", stationObjModel.getStationId(), |
| | | JSON.toJSONString(command)); |
| | | } else { |
| | | if (Objects.equals(wrkMast.getWrkSts(), WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts) |
| | | || Objects.equals(wrkMast.getWrkSts(), WrkStsType.INBOUND_RUN.sts)) { |
| | | Integer crnNo = wrkMast.getCrnNo(); |
| | | if (crnNo != null) { |
| | | CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, crnNo); |
| | | if (crnThread == null) { |
| | | continue; |
| | | } |
| | | CrnProtocol crnProtocol = crnThread.getStatus(); |
| | | boolean sameTaskRunning = stationProtocol.getTaskNo().equals(crnProtocol.getTaskNo()) |
| | | && Objects.equals(crnProtocol.getLoaded(), 1); |
| | | if (!sameTaskRunning) { |
| | | News.info("入库站点清理忽略,站点号={},站点taskNo={},工作状态={},堆垛机号={},堆垛机taskNo={},statusType={},loaded={},原因=crn_not_loaded_this_task", |
| | | stationObjModel.getStationId(), stationProtocol.getTaskNo(), wrkMast.getWrkSts(), crnNo, |
| | | crnProtocol.getTaskNo(), crnProtocol.getStatusType(), crnProtocol.getLoaded()); |
| | | continue; |
| | | } |
| | | |
| | | News.info("入库站点清理命中,站点号={},站点taskNo={},工作状态={},堆垛机号={},堆垛机taskNo={},statusType={},loaded={},原因=crn_loaded_running", |
| | | stationObjModel.getStationId(), stationProtocol.getTaskNo(), wrkMast.getWrkSts(), crnNo, |
| | | crnProtocol.getTaskNo(), crnProtocol.getStatusType(), crnProtocol.getLoaded()); |
| | | stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "in-station-reset-crn-running"); |
| | | redisUtil.set(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key |
| | | + stationObjModel.getStationId(), "lock", 10); |
| | | News.info("输送站点重置命令下发成功(crn_running),站点号={},命令数据={}", stationObjModel.getStationId(), |
| | | JSON.toJSONString(command)); |
| | | } else { |
| | | Integer dualCrnNo = wrkMast.getDualCrnNo(); |
| | | DualCrnThread dualCrnThread = (DualCrnThread) SlaveConnection.get(SlaveType.DualCrn, |
| | | dualCrnNo); |
| | | if (dualCrnThread == null) { |
| | | continue; |
| | | } |
| | | DualCrnProtocol dualCrnProtocol = dualCrnThread.getStatus(); |
| | | |
| | | Integer taskNo = stationProtocol.getTaskNo(); |
| | | boolean sameTaskRunning = taskNo != null && ( |
| | | taskNo.equals(dualCrnProtocol.getTaskNo()) && dualCrnProtocol.getLoaded() == 1 |
| | | || taskNo.equals(dualCrnProtocol.getTaskNoTwo()) && dualCrnProtocol.getLoadedTwo() == 1 |
| | | ); |
| | | if (!sameTaskRunning) { |
| | | News.info("入库站点清理忽略,站点号={},站点taskNo={},工作状态={},双工位堆垛机号={},taskNo1={},loaded1={},taskNo2={},loaded2={},原因=dual_crn_not_running_this_task", |
| | | stationObjModel.getStationId(), stationProtocol.getTaskNo(), wrkMast.getWrkSts(), dualCrnNo, |
| | | dualCrnProtocol.getTaskNo(), dualCrnProtocol.getLoaded(), |
| | | dualCrnProtocol.getTaskNoTwo(), dualCrnProtocol.getLoadedTwo()); |
| | | continue; |
| | | } |
| | | |
| | | News.info("入库站点清理命中,站点号={},站点taskNo={},工作状态={},双工位堆垛机号={},taskNo1={},loaded1={},taskNo2={},loaded2={},原因=dual_crn_running", |
| | | stationObjModel.getStationId(), stationProtocol.getTaskNo(), wrkMast.getWrkSts(), dualCrnNo, |
| | | dualCrnProtocol.getTaskNo(), dualCrnProtocol.getLoaded(), |
| | | dualCrnProtocol.getTaskNoTwo(), dualCrnProtocol.getLoadedTwo()); |
| | | stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, |
| | | "fake-process", "in-station-reset-dual-crn-running"); |
| | | redisUtil.set(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key |
| | | + stationObjModel.getStationId(), "lock", 10); |
| | | News.info("输送站点重置命令下发成功(dual_crn_running),站点号={},命令数据={}", |
| | | stationObjModel.getStationId(), JSON.toJSONString(command)); |
| | | } |
| | | } else { |
| | | News.info("入库站点清理忽略,站点号={},站点taskNo={},工作状态={},原因=wrk_sts_not_3", |
| | | stationObjModel.getStationId(), stationProtocol.getTaskNo(), wrkMast.getWrkSts()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 检测入库站是否有任务生成,并仿真生成模拟入库站点数据 |
| | | private void checkInStationHasTask() { |
| | | if (!enableFake.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | if (!fakeGenerateInTask.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap(); |
| | | |
| | | List<StationObjModel> list = basDevp.getInStationList$(); |
| | | for (StationObjModel entity : list) { |
| | | Integer stationId = entity.getStationId(); |
| | | if (!stationMap.containsKey(stationId)) { |
| | | continue; |
| | | } |
| | | |
| | | StationProtocol stationProtocol = stationMap.get(stationId); |
| | | if (stationProtocol == null) { |
| | | continue; |
| | | } |
| | | |
| | | Object lock = redisUtil.get(RedisKeyType.GENERATE_FAKE_IN_STATION_DATA_LIMIT.key + stationId); |
| | | if (lock != null) { |
| | | continue; |
| | | } |
| | | |
| | | // 满足自动、无物、工作号0,生成入库数据 |
| | | if (stationProtocol.isAutoing() |
| | | && !stationProtocol.isLoading() |
| | | && stationProtocol.getTaskNo() == 0) { |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, |
| | | commonService.getWorkNo(WrkIoType.FAKE_TASK_NO.id), stationId, |
| | | entity.getBarcodeStation().getStationId(), 0); |
| | | stationCommandDispatcher.dispatch(basDevp.getDevpNo(), command, "fake-process", "fake-enable-in"); |
| | | if (entity.getBarcodeStation() != null && entity.getBarcodeStation().getStationId() != null) { |
| | | Utils.precomputeInTaskEnableRow(entity.getBarcodeStation().getStationId()); |
| | | } |
| | | redisUtil.set(RedisKeyType.GENERATE_FAKE_IN_STATION_DATA_LIMIT.key + stationId, "lock", 5); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.core.plugin.station; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.core.common.Cools; |
| | | import com.zy.asrs.domain.param.CreateInTaskParam; |
| | | import com.zy.asrs.domain.param.CreateOutTaskParam; |
| | | import com.zy.asrs.entity.*; |
| | | import com.zy.asrs.service.*; |
| | | import com.zy.common.entity.FindCrnNoResult; |
| | | import com.zy.common.service.CommonService; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.News; |
| | | import com.zy.core.cache.SlaveConnection; |
| | | import com.zy.core.dispatch.StationCommandDispatcher; |
| | | import com.zy.core.enums.*; |
| | | import com.zy.core.model.StationObjModel; |
| | | import com.zy.core.model.command.StationCommand; |
| | | import com.zy.core.model.protocol.StationProtocol; |
| | | import com.zy.core.plugin.store.StoreInTaskContext; |
| | | import com.zy.core.plugin.store.StoreInTaskGenerationService; |
| | | import com.zy.core.plugin.store.StoreInTaskPolicy; |
| | | import com.zy.core.task.MainProcessLane; |
| | | import com.zy.core.task.MainProcessTaskSubmitter; |
| | | import com.zy.core.thread.StationThread; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Random; |
| | | |
| | | @Slf4j |
| | | @Component |
| | | public class FakeTaskGenerator implements StoreInTaskPolicy { |
| | | |
| | | private static final long MAIN_DISPATCH_INTERVAL_MS = 200L; |
| | | |
| | | private String fakeRealTaskRequestWms = "N"; |
| | | private String fakeGenerateInTask = "Y"; |
| | | private String fakeGenerateOutTask = "Y"; |
| | | |
| | | @Autowired |
| | | private WrkMastService wrkMastService; |
| | | @Autowired |
| | | private BasDevpService basDevpService; |
| | | @Autowired |
| | | private LocMastService locMastService; |
| | | @Autowired |
| | | private CommonService commonService; |
| | | @Autowired |
| | | private RedisUtil redisUtil; |
| | | @Autowired |
| | | private StationCommandDispatcher stationCommandDispatcher; |
| | | @Autowired |
| | | private MainProcessTaskSubmitter mainProcessTaskSubmitter; |
| | | @Autowired |
| | | private StoreInTaskGenerationService storeInTaskGenerationService; |
| | | |
| | | public void setFakeRealTaskRequestWms(String fakeRealTaskRequestWms) { |
| | | this.fakeRealTaskRequestWms = fakeRealTaskRequestWms; |
| | | } |
| | | |
| | | public void setFakeGenerateInTask(String fakeGenerateInTask) { |
| | | this.fakeGenerateInTask = fakeGenerateInTask; |
| | | } |
| | | |
| | | public void setFakeGenerateOutTask(String fakeGenerateOutTask) { |
| | | this.fakeGenerateOutTask = fakeGenerateOutTask; |
| | | } |
| | | |
| | | public void submitGenerateTasks() { |
| | | submitGenerateStoreTasks(); |
| | | mainProcessTaskSubmitter.submitKeyedSerialTask( |
| | | MainProcessLane.FAKE_ASYNC, "generateFakeInTask", "generateFakeInTask", |
| | | MAIN_DISPATCH_INTERVAL_MS, this::generateFakeInTask); |
| | | mainProcessTaskSubmitter.submitKeyedSerialTask( |
| | | MainProcessLane.FAKE_ASYNC, "generateFakeOutTask", "generateFakeOutTask", |
| | | MAIN_DISPATCH_INTERVAL_MS, this::generateFakeOutTask); |
| | | } |
| | | |
| | | private void submitGenerateStoreTasks() { |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap(); |
| | | if (stationMap == null || stationMap.isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | List<StationObjModel> barcodeStations = getBarcodeStations(basDevp); |
| | | for (StationObjModel stationObjModel : barcodeStations) { |
| | | Integer stationId = stationObjModel == null ? null : stationObjModel.getStationId(); |
| | | if (stationId == null || !stationMap.containsKey(stationId)) { |
| | | continue; |
| | | } |
| | | if (!canRequestStoreIn(stationMap.get(stationId))) { |
| | | continue; |
| | | } |
| | | storeInTaskGenerationService.submitGenerateStoreTask( |
| | | this, |
| | | basDevp, |
| | | stationObjModel, |
| | | MainProcessLane.FAKE_GENERATE_STORE, |
| | | MAIN_DISPATCH_INTERVAL_MS, |
| | | () -> storeInTaskGenerationService.generate(this, basDevp, stationObjModel) |
| | | ); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 生成仿真模拟入库任务 |
| | | public void generateFakeInTask() { |
| | | if (fakeRealTaskRequestWms.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | if (!fakeGenerateInTask.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap(); |
| | | |
| | | List<StationObjModel> list = basDevp.getBarcodeStationList$(); |
| | | for (StationObjModel model : list) { |
| | | Integer stationId = model.getStationId(); |
| | | if (!stationMap.containsKey(stationId)) { |
| | | continue; |
| | | } |
| | | |
| | | StationProtocol stationProtocol = stationMap.get(stationId); |
| | | if (stationProtocol == null) { |
| | | continue; |
| | | } |
| | | |
| | | Object object = redisUtil.get(RedisKeyType.GENERATE_FAKE_IN_TASK_LIMIT.key + stationId); |
| | | if (object != null) { |
| | | continue; |
| | | } |
| | | |
| | | // 满足自动、有物、有工作号,生成入库数据 |
| | | if (stationProtocol.isAutoing() |
| | | && stationProtocol.isLoading() |
| | | && stationProtocol.getTaskNo() > 0) { |
| | | if (Cools.isEmpty(stationProtocol.getBarcode())) { |
| | | continue; |
| | | } |
| | | |
| | | // 检测任务是否生成 |
| | | List<WrkMast> wrkMasts = wrkMastService |
| | | .list(new QueryWrapper<WrkMast>().eq("barcode", stationProtocol.getBarcode())); |
| | | if (!wrkMasts.isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | List<LocMast> locMastList = locMastService |
| | | .list(new QueryWrapper<LocMast>().eq("loc_sts", String.valueOf(LocStsType.O))); |
| | | if (locMastList.isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | int nextInt = new Random().nextInt(locMastList.size()); |
| | | LocMast locMast = locMastList.get(nextInt); |
| | | |
| | | FindCrnNoResult findCrnNoResult = commonService.findCrnNoByLocNo(locMast.getLocNo()); |
| | | if (findCrnNoResult == null) { |
| | | continue; |
| | | } |
| | | |
| | | Integer targetStationId = commonService.findInStationId(findCrnNoResult, stationId); |
| | | if (targetStationId == null) { |
| | | continue; |
| | | } |
| | | |
| | | CreateInTaskParam taskParam = new CreateInTaskParam(); |
| | | taskParam.setTaskNo(String.valueOf(commonService.getWorkNo(WrkIoType.IN.id))); |
| | | taskParam.setSourceStaNo(stationId); |
| | | taskParam.setStaNo(targetStationId); |
| | | taskParam.setLocNo(locMast.getLocNo()); |
| | | taskParam.setBarcode(stationProtocol.getBarcode()); |
| | | WrkMast wrkMast = commonService.createInTask(taskParam); |
| | | |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), |
| | | stationId, stationId, 0); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | continue; |
| | | } |
| | | stationCommandDispatcher.dispatch(basDevp.getDevpNo(), command, "fake-process", "fake-in-task"); |
| | | redisUtil.set(RedisKeyType.GENERATE_FAKE_IN_TASK_LIMIT.key + stationId, "lock", 5); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 生成仿真模拟出库任务 |
| | | public void generateFakeOutTask() { |
| | | try { |
| | | if (fakeRealTaskRequestWms.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | if (!fakeGenerateOutTask.equals("Y")) { |
| | | return; |
| | | } |
| | | |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap(); |
| | | |
| | | List<StationObjModel> list = basDevp.getOutStationList$(); |
| | | for (StationObjModel entity : list) { |
| | | Integer stationId = entity.getStationId(); |
| | | if (!stationMap.containsKey(stationId)) { |
| | | continue; |
| | | } |
| | | |
| | | StationProtocol stationProtocol = stationMap.get(stationId); |
| | | if (stationProtocol == null) { |
| | | continue; |
| | | } |
| | | |
| | | Object object = redisUtil.get(RedisKeyType.GENERATE_FAKE_OUT_TASK_LIMIT.key + stationId); |
| | | if (object != null) { |
| | | return; |
| | | } |
| | | |
| | | // 满足自动、无物、工作号0,生成出库数据 |
| | | if (stationProtocol.isAutoing() |
| | | && !stationProtocol.isLoading() |
| | | && stationProtocol.getTaskNo() == 0) { |
| | | List<LocMast> locMastList = locMastService |
| | | .list(new QueryWrapper<LocMast>().eq("loc_sts", String.valueOf(LocStsType.F))); |
| | | if (locMastList.isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | LocMast locMast = locMastList.get(0); |
| | | |
| | | CreateOutTaskParam taskParam = new CreateOutTaskParam(); |
| | | taskParam.setTaskNo(String.valueOf(commonService.getWorkNo(WrkIoType.OUT.id))); |
| | | taskParam.setStaNo(stationId); |
| | | taskParam.setLocNo(locMast.getLocNo()); |
| | | boolean result = commonService.createOutTask(taskParam); |
| | | redisUtil.set(RedisKeyType.GENERATE_FAKE_OUT_TASK_LIMIT.key + stationId, "lock", 10); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("generateFakeOutTask execute error", e); |
| | | } |
| | | } |
| | | |
| | | private boolean canRequestStoreIn(StationProtocol stationProtocol) { |
| | | return stationProtocol != null |
| | | && stationProtocol.isAutoing() |
| | | && stationProtocol.isLoading() |
| | | && stationProtocol.getTaskNo() > 0 |
| | | && !Cools.isEmpty(stationProtocol.getBarcode()); |
| | | } |
| | | |
| | | @Override |
| | | public boolean isEnabled() { |
| | | return !"N".equals(fakeRealTaskRequestWms); |
| | | } |
| | | |
| | | @Override |
| | | public void onRequestPermitGranted(StoreInTaskContext context) { |
| | | redisUtil.set(getGenerateLockKey(context), "lock", 3); |
| | | } |
| | | |
| | | @Override |
| | | public void afterTaskCreated(StoreInTaskContext context, WrkMast wrkMast) { |
| | | Integer stationId = context.getStationObjModel().getStationId(); |
| | | StationCommand command = context.getStationThread().getCommand(StationCommandType.WRITE_INFO, |
| | | wrkMast.getWrkNo(), stationId, stationId, 0); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | return; |
| | | } |
| | | stationCommandDispatcher.dispatch(context.getBasDevp().getDevpNo(), command, "fake-process", "write-info"); |
| | | } |
| | | |
| | | @Override |
| | | public String getGenerateLockKey(StoreInTaskContext context) { |
| | | return RedisKeyType.GENERATE_FAKE_IN_STATION_DATA_LIMIT.key + context.getStationObjModel().getStationId(); |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | private void refreshCacheData() { |
| | | HashMap<String, String> systemConfigMap = new HashMap<>(); |
| | | List<Config> configList = configService.list(new QueryWrapper<>()); |
| | | for (Config config : configList) { |
| | | systemConfigMap.put(config.getCode(), config.getValue()); |
| | | } |
| | | redisUtil.set(RedisKeyType.SYSTEM_CONFIG_MAP.key, systemConfigMap); |
| | | configService.refreshSystemConfigCache(); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | public interface ConfigService extends IService<Config> { |
| | | |
| | | String getConfigValue(String code, String defaultValue); |
| | | |
| | | long getConfigLongValue(String code, long defaultValue); |
| | | |
| | | boolean saveConfigValue(String code, String value); |
| | | |
| | | void refreshSystemConfigCache(); |
| | | } |
| | |
| | | package com.zy.system.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.zy.common.entity.Parameter; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.enums.RedisKeyType; |
| | | import com.zy.core.network.fake.FakeConfigKeys; |
| | | import com.zy.system.entity.Config; |
| | | import com.zy.system.mapper.ConfigMapper; |
| | | import com.zy.system.service.ConfigService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | |
| | | @Service("configService") |
| | | public class ConfigServiceImpl extends ServiceImpl<ConfigMapper, Config> implements ConfigService { |
| | | |
| | | @Autowired |
| | | private RedisUtil redisUtil; |
| | | |
| | | @Override |
| | | public String getConfigValue(String code, String defaultValue) { |
| | | if (code == null || code.trim().isEmpty()) { |
| | | return defaultValue; |
| | | } |
| | | Config config = getOne(new QueryWrapper<Config>().eq("code", code).last("limit 1")); |
| | | if (config == null || config.getValue() == null || config.getValue().trim().isEmpty()) { |
| | | return defaultValue; |
| | | } |
| | | return config.getValue(); |
| | | } |
| | | |
| | | @Override |
| | | public long getConfigLongValue(String code, long defaultValue) { |
| | | String value = getConfigValue(code, String.valueOf(defaultValue)); |
| | | try { |
| | | long parsed = Long.parseLong(value.trim()); |
| | | return parsed >= 0 ? parsed : defaultValue; |
| | | } catch (Exception e) { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public boolean saveConfigValue(String code, String value) { |
| | | Config config = getOne(new QueryWrapper<Config>().eq("code", code).last("limit 1")); |
| | | if (config == null) { |
| | | config = new Config(code, code, value, (short) 1, (short) 1); |
| | | if (FakeConfigKeys.ALL_KEYS.contains(code)) { |
| | | config.setSelectType("fake"); |
| | | } |
| | | return save(config); |
| | | } |
| | | config.setValue(value); |
| | | if (FakeConfigKeys.ALL_KEYS.contains(code) && (config.getSelectType() == null || config.getSelectType().trim().isEmpty())) { |
| | | config.setSelectType("fake"); |
| | | } |
| | | return updateById(config); |
| | | } |
| | | |
| | | @Override |
| | | public void refreshSystemConfigCache() { |
| | | HashMap<String, String> systemConfigMap = new HashMap<>(); |
| | | List<Config> configList = list(new QueryWrapper<>()); |
| | | for (Config config : configList) { |
| | | systemConfigMap.put(config.getCode(), config.getValue()); |
| | | } |
| | | redisUtil.set(RedisKeyType.SYSTEM_CONFIG_MAP.key, systemConfigMap); |
| | | Parameter.reset(); |
| | | } |
| | | } |
| | |
| | | # 系统版本信息 |
| | | app: |
| | | version: 3.0.0.1 |
| | | version: 3.0.0.2 |
| | | version-type: prd # prd 或 dev |
| | | i18n: |
| | | default-locale: zh-CN |
| New file |
| | |
| | | -- 仿真高级参数初始化 |
| | | -- 仅初始化仿真专属参数,不影响真实系统共享业务配置 |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '启用仿真系统', 'enableFake', 'N', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'enableFake'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '仿真时请求真实任务', 'fakeRealTaskRequestWms', 'N', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeRealTaskRequestWms'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '自动生成仿真入库任务', 'fakeGenerateInTask', 'Y', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeGenerateInTask'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '自动生成仿真出库任务', 'fakeGenerateOutTask', 'Y', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeGenerateOutTask'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '仿真堵塞检测开关', 'fakeAllowCheckBlock', 'Y', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeAllowCheckBlock'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '仿真堵塞判定超时(毫秒)', 'fakeRunBlockTimeoutMs', '20000', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeRunBlockTimeoutMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '单工位堆垛机取放货耗时(毫秒)', 'fakeCrnFetchPutDurationMs', '2000', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeCrnFetchPutDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '单工位堆垛机Z轴每步耗时(毫秒)', 'fakeCrnLevelStepDurationMs', '1000', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeCrnLevelStepDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '单工位堆垛机Y轴每步耗时(毫秒)', 'fakeCrnBayStepDurationMs', '1000', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeCrnBayStepDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '单工位堆垛机复位耗时(毫秒)', 'fakeCrnResetDurationMs', '0', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeCrnResetDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '双工位堆垛机互斥轮询间隔(毫秒)', 'fakeDualCrnCommandWaitMs', '200', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeDualCrnCommandWaitMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '双工位堆垛机TRANSFER耗时(毫秒)', 'fakeDualCrnTransferDurationMs', '2000', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeDualCrnTransferDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '双工位堆垛机PICK耗时(毫秒)', 'fakeDualCrnPickDurationMs', '3000', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeDualCrnPickDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '双工位堆垛机PUT耗时(毫秒)', 'fakeDualCrnPutDurationMs', '3000', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeDualCrnPutDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '双工位堆垛机Z轴每步耗时(毫秒)', 'fakeDualCrnLevelStepDurationMs', '1000', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeDualCrnLevelStepDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '双工位堆垛机Y轴每步耗时(毫秒)', 'fakeDualCrnBayStepDurationMs', '500', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeDualCrnBayStepDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '双工位堆垛机复位耗时(毫秒)', 'fakeDualCrnResetDurationMs', '0', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeDualCrnResetDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT 'RGV导航点移动耗时(毫秒)', 'fakeRgvMoveStepDurationMs', '1000', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeRgvMoveStepDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT 'RGV装卸货耗时(毫秒)', 'fakeRgvLoadDurationMs', '1000', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeRgvLoadDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT 'RGV复位耗时(毫秒)', 'fakeRgvResetDurationMs', '0', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeRgvResetDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '站点等待新分段超时(毫秒)', 'fakeStationSegmentWaitTimeoutMs', '30000', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeStationSegmentWaitTimeoutMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '站点移动推进间隔(毫秒)', 'fakeStationMoveStepDurationMs', '500', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeStationMoveStepDurationMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '站点空闲轮询间隔(毫秒)', 'fakeStationIdleLoopDelayMs', '200', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeStationIdleLoopDelayMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '站点堵塞轮询间隔(毫秒)', 'fakeStationBlockedLoopDelayMs', '1000', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeStationBlockedLoopDelayMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '站点初始化等待(毫秒)', 'fakeStationInitializeDelayMs', '0', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeStationInitializeDelayMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '站点结束缓冲等待(毫秒)', 'fakeStationFinishDelayMs', '0', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeStationFinishDelayMs'); |
| | | |
| | | INSERT INTO sys_config(name, code, value, type, status, select_type) |
| | | SELECT '出库站自动结束时间(毫秒)', 'fakeOutStationStayTimeoutMs', '3000', 1, 1, 'fake' |
| | | FROM dual |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_config WHERE code = 'fakeOutStationStayTimeoutMs'); |
| | | |
| | | SELECT id, name, code, value, type, status, select_type |
| | | FROM sys_config |
| | | WHERE code IN ( |
| | | 'enableFake', |
| | | 'fakeRealTaskRequestWms', |
| | | 'fakeGenerateInTask', |
| | | 'fakeGenerateOutTask', |
| | | 'fakeAllowCheckBlock', |
| | | 'fakeRunBlockTimeoutMs', |
| | | 'fakeCrnFetchPutDurationMs', |
| | | 'fakeCrnLevelStepDurationMs', |
| | | 'fakeCrnBayStepDurationMs', |
| | | 'fakeCrnResetDurationMs', |
| | | 'fakeDualCrnCommandWaitMs', |
| | | 'fakeDualCrnTransferDurationMs', |
| | | 'fakeDualCrnPickDurationMs', |
| | | 'fakeDualCrnPutDurationMs', |
| | | 'fakeDualCrnLevelStepDurationMs', |
| | | 'fakeDualCrnBayStepDurationMs', |
| | | 'fakeDualCrnResetDurationMs', |
| | | 'fakeRgvMoveStepDurationMs', |
| | | 'fakeRgvLoadDurationMs', |
| | | 'fakeRgvResetDurationMs', |
| | | 'fakeStationSegmentWaitTimeoutMs', |
| | | 'fakeStationMoveStepDurationMs', |
| | | 'fakeStationIdleLoopDelayMs', |
| | | 'fakeStationBlockedLoopDelayMs', |
| | | 'fakeStationInitializeDelayMs', |
| | | 'fakeStationFinishDelayMs', |
| | | 'fakeOutStationStayTimeoutMs' |
| | | ) |
| | | ORDER BY code; |
| | |
| | | </div> |
| | | <div :style="mapToolRowStyle()"> |
| | | <button type="button" @click="openStationColorConfigPage" :style="mapToolButtonStyle(false)">站点颜色</button> |
| | | <button v-if="fakeOperationVisible" type="button" @click="openFakeOperationConfigPage" :style="mapToolButtonStyle(false)">仿真操作</button> |
| | | </div> |
| | | <div v-if="levList && levList.length > 1" :style="mapToolFloorSectionStyle()"> |
| | | <div :style="mapToolSectionLabelStyle()">楼层</div> |
| | |
| | | 'machine-pakout': 0x97b400, |
| | | 'site-run-block': 0xe69138, |
| | | 'site-error': 0xDB2828 |
| | | } |
| | | }, |
| | | fakeOperationVisible: false |
| | | } |
| | | }, |
| | | mounted() { |
| | |
| | | this.startContainerResizeObserve(); |
| | | this.loadMapTransformConfig(); |
| | | this.loadStationColorConfig(); |
| | | this.loadFakeProcessStatus(); |
| | | this.loadLocList(); |
| | | this.connectWs(); |
| | | |
| | |
| | | } |
| | | window.open(url, '_blank'); |
| | | }, |
| | | loadFakeProcessStatus() { |
| | | if (typeof window === 'undefined' || typeof $ === 'undefined' || typeof baseUrl === 'undefined') { |
| | | this.fakeOperationVisible = false; |
| | | return; |
| | | } |
| | | $.ajax({ |
| | | url: baseUrl + '/openapi/getFakeSystemRunStatus', |
| | | method: 'get', |
| | | success: function (res) { |
| | | const data = res && res.data ? res.data : null; |
| | | this.fakeOperationVisible = !!(data && data.isFake); |
| | | }.bind(this), |
| | | error: function () { |
| | | this.fakeOperationVisible = false; |
| | | }.bind(this) |
| | | }); |
| | | }, |
| | | openFakeOperationConfigPage() { |
| | | if (typeof window === 'undefined' || !this.fakeOperationVisible) { return; } |
| | | const url = (typeof baseUrl !== 'undefined' ? baseUrl : '') + '/views/watch/fakeOperationConfig.html'; |
| | | const layerInstance = (window.top && window.top.layer) || window.layer; |
| | | if (layerInstance && typeof layerInstance.open === 'function') { |
| | | layerInstance.open({ |
| | | type: 2, |
| | | title: '仿真操作', |
| | | maxmin: true, |
| | | area: ['1180px', '820px'], |
| | | shadeClose: false, |
| | | content: url |
| | | }); |
| | | return; |
| | | } |
| | | window.open(url, '_blank'); |
| | | }, |
| | | parseRotation(value) { |
| | | const num = parseInt(value, 10); |
| | | if (!isFinite(num)) { return 0; } |
| New file |
| | |
| | | var fakeConfigSchema = { |
| | | basicGroups: [ |
| | | { |
| | | key: 'runtime', |
| | | title: '仿真总控', |
| | | desc: '控制仿真系统是否运行,以及仿真时是否继续向 WMS 请求真实任务。', |
| | | items: [ |
| | | { code: 'enableFake', name: '启用仿真系统', desc: '保存为是后表示仿真系统进入运行状态。', type: 'boolean' }, |
| | | { code: 'fakeRealTaskRequestWms', name: '仿真时请求真实任务', desc: '启用后,仿真运行时仍可向 WMS 请求真实任务。', type: 'boolean' } |
| | | ] |
| | | }, |
| | | { |
| | | key: 'task', |
| | | title: '自动任务', |
| | | desc: '控制仿真模式下是否自动生成入库、出库任务。', |
| | | items: [ |
| | | { code: 'fakeGenerateInTask', name: '自动生成入库任务', desc: '启用后,仿真系统会自动生成入库任务。', type: 'boolean' }, |
| | | { code: 'fakeGenerateOutTask', name: '自动生成出库任务', desc: '启用后,仿真系统会自动生成出库任务。', type: 'boolean' } |
| | | ] |
| | | }, |
| | | { |
| | | key: 'block', |
| | | title: '堵塞策略', |
| | | desc: '控制仿真中的堵塞检测开关和堵塞判定超时时间。', |
| | | items: [ |
| | | { code: 'fakeAllowCheckBlock', name: '启用堵塞检测', desc: '启用后按仿真规则检测运行堵塞。', type: 'boolean' }, |
| | | { code: 'fakeRunBlockTimeoutMs', name: '堵塞判定超时', desc: '站点持续无推进超过该时间后,按仿真逻辑判定为堵塞。', type: 'number', unit: 'ms', min: 0, max: 600000 } |
| | | ] |
| | | } |
| | | ], |
| | | advancedGroups: [ |
| | | { |
| | | key: 'crn', |
| | | title: '单工位堆垛机', |
| | | desc: '控制单工位堆垛机的取放货、Y 轴和 Z 轴节奏。', |
| | | items: [ |
| | | { code: 'fakeCrnFetchPutDurationMs', name: '取放货耗时', desc: '单次取货或放货动作耗时。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeCrnBayStepDurationMs', name: 'Y轴每步耗时', desc: '堆垛机 bay 方向单步移动耗时。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeCrnLevelStepDurationMs', name: 'Z轴每步耗时', desc: '堆垛机 level 方向单步移动耗时。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeCrnResetDurationMs', name: '复位耗时', desc: '单工位堆垛机复位命令耗时。', unit: 'ms', min: 0, max: 600000 } |
| | | ] |
| | | }, |
| | | { |
| | | key: 'dualCrn', |
| | | title: '双工位堆垛机', |
| | | desc: '控制双工位堆垛机的互斥等待、取放货和轴向移动节奏。', |
| | | items: [ |
| | | { code: 'fakeDualCrnCommandWaitMs', name: '工位互斥轮询间隔', desc: '双工位竞争执行权时的等待轮询间隔。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeDualCrnTransferDurationMs', name: 'TRANSFER耗时', desc: 'TRANSFER 模式单次取/放动作耗时。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeDualCrnPickDurationMs', name: 'PICK耗时', desc: 'PICK 模式动作耗时。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeDualCrnPutDurationMs', name: 'PUT耗时', desc: 'PUT 模式动作耗时。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeDualCrnBayStepDurationMs', name: 'Y轴每步耗时', desc: '双工位堆垛机 bay 方向单步移动耗时。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeDualCrnLevelStepDurationMs', name: 'Z轴每步耗时', desc: '双工位堆垛机 level 方向单步移动耗时。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeDualCrnResetDurationMs', name: '复位耗时', desc: '双工位堆垛机复位命令耗时。', unit: 'ms', min: 0, max: 600000 } |
| | | ] |
| | | }, |
| | | { |
| | | key: 'rgv', |
| | | title: 'RGV', |
| | | desc: '控制 RGV 导航移动、装卸货与复位节奏。', |
| | | items: [ |
| | | { code: 'fakeRgvMoveStepDurationMs', name: '导航点移动耗时', desc: 'RGV 每经过一个导航点的耗时。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeRgvLoadDurationMs', name: '装卸货耗时', desc: 'RGV 取货或放货动作耗时。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeRgvResetDurationMs', name: '复位耗时', desc: 'RGV 复位命令耗时。', unit: 'ms', min: 0, max: 600000 } |
| | | ] |
| | | }, |
| | | { |
| | | key: 'station', |
| | | title: '站点节奏', |
| | | desc: '控制站点初始化、移动推进、空闲轮询与堵塞轮询节奏。', |
| | | note: '这些参数只作用于仿真输送站点,不会修改真实系统共享运行逻辑。', |
| | | items: [ |
| | | { code: 'fakeStationSegmentWaitTimeoutMs', name: '等待新分段超时', desc: '等待新的路径分段超过该时间后按超时处理。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeStationMoveStepDurationMs', name: '移动推进间隔', desc: '站点每成功推进一步后的等待时间。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeStationIdleLoopDelayMs', name: '空闲轮询间隔', desc: '站点空闲时轮询推进的间隔。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeStationBlockedLoopDelayMs', name: '堵塞轮询间隔', desc: '站点阻塞或重试时的轮询间隔。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeStationInitializeDelayMs', name: '初始化等待', desc: '站点初始化前后的额外等待时间。', unit: 'ms', min: 0, max: 600000 }, |
| | | { code: 'fakeStationFinishDelayMs', name: '结束缓冲等待', desc: '站点动作完成后的额外等待时间。', unit: 'ms', min: 0, max: 600000 } |
| | | ] |
| | | }, |
| | | { |
| | | key: 'outTask', |
| | | title: '出库站停留策略', |
| | | desc: '控制出库任务在站点停留过久时的自动结束阈值。', |
| | | items: [ |
| | | { code: 'fakeOutStationStayTimeoutMs', name: '出库站自动结束时间', desc: '出库任务在站点停留超过该时间后自动 RESET/结束。', unit: 'ms', min: 0, max: 600000 } |
| | | ] |
| | | } |
| | | ] |
| | | }; |
| | | |
| | | function buildInitialForm() { |
| | | var form = {}; |
| | | fakeConfigSchema.basicGroups.concat(fakeConfigSchema.advancedGroups).forEach(function (group) { |
| | | group.items.forEach(function (item) { |
| | | form[item.code] = item.type === 'boolean' ? false : 0; |
| | | }); |
| | | }); |
| | | return form; |
| | | } |
| | | |
| | | var app = new Vue({ |
| | | el: '#app', |
| | | data: { |
| | | loading: false, |
| | | saving: false, |
| | | activeTab: 'basic', |
| | | statusInfo: { |
| | | isFake: false, |
| | | running: false |
| | | }, |
| | | form: buildInitialForm(), |
| | | basicGroups: fakeConfigSchema.basicGroups, |
| | | advancedGroups: fakeConfigSchema.advancedGroups |
| | | }, |
| | | mounted: function () { |
| | | this.reloadData(); |
| | | }, |
| | | methods: { |
| | | reloadData: function () { |
| | | var that = this; |
| | | this.loading = true; |
| | | $.when( |
| | | $.ajax({ |
| | | url: baseUrl + '/openapi/getFakeSystemRunStatus', |
| | | method: 'GET' |
| | | }), |
| | | $.ajax({ |
| | | url: baseUrl + '/openapi/fakeConfig/auth', |
| | | headers: { token: localStorage.getItem('token') }, |
| | | method: 'GET' |
| | | }) |
| | | ).done(function (statusRes, configRes) { |
| | | that.loading = false; |
| | | var statusPayload = statusRes && statusRes[0] ? statusRes[0] : {}; |
| | | var configPayload = configRes && configRes[0] ? configRes[0] : {}; |
| | | var statusData = statusPayload.data || {}; |
| | | that.statusInfo.isFake = !!statusData.isFake; |
| | | that.statusInfo.running = !!statusData.running; |
| | | if (configPayload.code === 200) { |
| | | that.applyConfig(configPayload.data || {}); |
| | | } else if (configPayload.code === 403) { |
| | | top.location.href = baseUrl + '/'; |
| | | } else { |
| | | that.$message.error(configPayload.msg || '加载仿真配置失败'); |
| | | } |
| | | }).fail(function () { |
| | | that.loading = false; |
| | | that.$message.error('加载仿真配置失败'); |
| | | }); |
| | | }, |
| | | applyConfig: function (configMap) { |
| | | var next = buildInitialForm(); |
| | | this.basicGroups.concat(this.advancedGroups).forEach(function (group) { |
| | | group.items.forEach(function (item) { |
| | | var rawValue = configMap[item.code]; |
| | | if (item.type === 'boolean') { |
| | | next[item.code] = String(rawValue || '').toUpperCase() === 'Y'; |
| | | } else { |
| | | var parsed = parseInt(rawValue, 10); |
| | | next[item.code] = isNaN(parsed) ? 0 : parsed; |
| | | } |
| | | }); |
| | | }); |
| | | this.form = next; |
| | | }, |
| | | buildSubmitPayload: function () { |
| | | var payload = {}; |
| | | this.basicGroups.concat(this.advancedGroups).forEach(function (group) { |
| | | group.items.forEach(function (item) { |
| | | var value = this.form[item.code]; |
| | | if (item.type === 'boolean') { |
| | | payload[item.code] = value ? 'Y' : 'N'; |
| | | } else { |
| | | var parsed = parseInt(value, 10); |
| | | if (isNaN(parsed) || parsed < item.min || parsed > item.max) { |
| | | throw new Error(item.name + ' 超出允许范围'); |
| | | } |
| | | payload[item.code] = String(parsed); |
| | | } |
| | | }, this); |
| | | }, this); |
| | | return payload; |
| | | }, |
| | | saveConfig: function () { |
| | | if (!this.statusInfo.isFake) { |
| | | this.$message.warning('当前不是仿真模式,不能保存仿真配置'); |
| | | return; |
| | | } |
| | | var payload; |
| | | try { |
| | | payload = this.buildSubmitPayload(); |
| | | } catch (e) { |
| | | this.$message.error(e.message || '参数校验失败'); |
| | | return; |
| | | } |
| | | var that = this; |
| | | this.saving = true; |
| | | $.ajax({ |
| | | url: baseUrl + '/openapi/fakeConfig/save/auth', |
| | | headers: { token: localStorage.getItem('token') }, |
| | | method: 'POST', |
| | | contentType: 'application/json;charset=UTF-8', |
| | | dataType: 'json', |
| | | data: JSON.stringify({ |
| | | configMap: payload |
| | | }), |
| | | success: function (res) { |
| | | that.saving = false; |
| | | if (res.code === 200) { |
| | | that.$message.success('仿真配置已保存'); |
| | | that.applyConfig(res.data || {}); |
| | | } else if (res.code === 403) { |
| | | top.location.href = baseUrl + '/'; |
| | | } else { |
| | | that.$message.error(res.msg || '保存仿真配置失败'); |
| | | } |
| | | }, |
| | | error: function () { |
| | | that.saving = false; |
| | | that.$message.error('保存仿真配置失败'); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | }); |
| New file |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="zh-CN"> |
| | | <head> |
| | | <meta charset="utf-8"> |
| | | <title>仿真操作</title> |
| | | <meta name="renderer" content="webkit"> |
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> |
| | | <link rel="stylesheet" href="../../static/vue/element/element.css"> |
| | | <link rel="stylesheet" href="../../static/css/cool.css"> |
| | | <style> |
| | | html, body { |
| | | height: 100%; |
| | | margin: 0; |
| | | background: linear-gradient(180deg, #eef4fa 0%, #e7edf5 100%); |
| | | } |
| | | body { |
| | | font-family: "PingFang SC", "Microsoft YaHei", sans-serif; |
| | | } |
| | | [v-cloak] { |
| | | display: none; |
| | | } |
| | | #app { |
| | | height: 100%; |
| | | box-sizing: border-box; |
| | | padding: 20px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 16px; |
| | | } |
| | | .hero { |
| | | padding: 22px 24px; |
| | | border-radius: 20px; |
| | | border: 1px solid rgba(255,255,255,0.82); |
| | | background: linear-gradient(135deg, rgba(255,255,255,0.94), rgba(244,249,255,0.88)); |
| | | box-shadow: 0 18px 44px rgba(62, 89, 120, 0.08); |
| | | } |
| | | .hero-title { |
| | | font-size: 26px; |
| | | font-weight: 700; |
| | | color: #213447; |
| | | } |
| | | .hero-desc { |
| | | margin-top: 8px; |
| | | color: #66788c; |
| | | font-size: 13px; |
| | | line-height: 1.8; |
| | | } |
| | | .panel { |
| | | flex: 1; |
| | | min-height: 0; |
| | | display: flex; |
| | | flex-direction: column; |
| | | border-radius: 20px; |
| | | border: 1px solid rgba(222, 231, 241, 0.92); |
| | | background: rgba(251, 253, 255, 0.9); |
| | | box-shadow: 0 18px 42px rgba(57, 84, 114, 0.08); |
| | | overflow: hidden; |
| | | } |
| | | .toolbar { |
| | | padding: 18px 22px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | gap: 16px; |
| | | border-bottom: 1px solid rgba(225, 233, 243, 0.95); |
| | | background: rgba(255,255,255,0.74); |
| | | } |
| | | .toolbar-left { |
| | | display: flex; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | gap: 10px; |
| | | } |
| | | .toolbar-tip { |
| | | font-size: 12px; |
| | | color: #73879b; |
| | | } |
| | | .content { |
| | | flex: 1; |
| | | min-height: 0; |
| | | overflow: auto; |
| | | padding: 18px 22px 24px; |
| | | } |
| | | .group-card { |
| | | border: 1px solid rgba(222, 231, 241, 0.95); |
| | | border-radius: 18px; |
| | | background: linear-gradient(180deg, rgba(255,255,255,0.96), rgba(246,250,255,0.92)); |
| | | box-shadow: 0 10px 24px rgba(75, 101, 130, 0.06); |
| | | padding: 18px; |
| | | margin-bottom: 16px; |
| | | } |
| | | .group-head { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | justify-content: space-between; |
| | | gap: 12px; |
| | | margin-bottom: 12px; |
| | | } |
| | | .group-title { |
| | | font-size: 16px; |
| | | font-weight: 700; |
| | | color: #25384c; |
| | | } |
| | | .group-desc { |
| | | margin-top: 6px; |
| | | font-size: 12px; |
| | | line-height: 1.8; |
| | | color: #6f8297; |
| | | } |
| | | .config-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); |
| | | gap: 14px; |
| | | } |
| | | .config-item { |
| | | border-radius: 16px; |
| | | border: 1px solid rgba(225, 233, 243, 0.96); |
| | | background: rgba(255,255,255,0.88); |
| | | padding: 14px 14px 12px; |
| | | } |
| | | .config-name { |
| | | font-size: 14px; |
| | | font-weight: 700; |
| | | color: #223548; |
| | | } |
| | | .config-desc { |
| | | margin-top: 6px; |
| | | min-height: 38px; |
| | | font-size: 12px; |
| | | line-height: 1.7; |
| | | color: #71859a; |
| | | } |
| | | .config-control { |
| | | margin-top: 12px; |
| | | } |
| | | .config-meta { |
| | | margin-top: 10px; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | } |
| | | .meta-chip { |
| | | padding: 4px 8px; |
| | | border-radius: 999px; |
| | | background: rgba(233, 240, 248, 0.92); |
| | | color: #60758b; |
| | | font-size: 12px; |
| | | } |
| | | .empty-box { |
| | | padding: 56px 16px; |
| | | text-align: center; |
| | | color: #7b8da0; |
| | | font-size: 14px; |
| | | } |
| | | .section-note { |
| | | margin-top: 8px; |
| | | font-size: 12px; |
| | | color: #8a9aab; |
| | | } |
| | | .el-tabs__item { |
| | | font-weight: 600; |
| | | } |
| | | .el-input-number, |
| | | .el-select { |
| | | width: 100%; |
| | | } |
| | | @media (max-width: 900px) { |
| | | #app { |
| | | padding: 14px; |
| | | } |
| | | .toolbar { |
| | | flex-direction: column; |
| | | align-items: stretch; |
| | | } |
| | | } |
| | | </style> |
| | | </head> |
| | | <body> |
| | | <div id="app" v-cloak> |
| | | <div class="hero"> |
| | | <div class="hero-title">仿真操作</div> |
| | | <div class="hero-desc"> |
| | | 仅管理仿真专属参数和仿真节奏参数,不混入真实系统业务配置。保存后直接写入系统配置,并刷新仿真读取缓存。 |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="panel"> |
| | | <div class="toolbar"> |
| | | <div class="toolbar-left"> |
| | | <el-tag size="small" :type="statusInfo.isFake ? 'success' : 'info'">{{ statusInfo.isFake ? '仿真模式' : '非仿真模式' }}</el-tag> |
| | | <el-tag size="small" :type="statusInfo.running ? 'danger' : 'warning'">{{ statusInfo.running ? '运行中' : '未运行' }}</el-tag> |
| | | <span class="toolbar-tip">开关项保存为 Y/N,时间项单位均为毫秒。</span> |
| | | </div> |
| | | <div> |
| | | <el-button size="small" @click="reloadData" :loading="loading">刷新</el-button> |
| | | <el-button type="primary" size="small" @click="saveConfig" :loading="saving" :disabled="!statusInfo.isFake">保存配置</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="content" v-loading="loading"> |
| | | <div v-if="!statusInfo.isFake && !loading" class="empty-box">当前不是仿真模式,仿真参数不可编辑。</div> |
| | | <template v-else> |
| | | <el-tabs v-model="activeTab"> |
| | | <el-tab-pane label="基础参数" name="basic"> |
| | | <div class="group-card" v-for="group in basicGroups" :key="group.key"> |
| | | <div class="group-head"> |
| | | <div> |
| | | <div class="group-title">{{ group.title }}</div> |
| | | <div class="group-desc">{{ group.desc }}</div> |
| | | </div> |
| | | </div> |
| | | <div class="config-grid"> |
| | | <div class="config-item" v-for="item in group.items" :key="item.code"> |
| | | <div class="config-name">{{ item.name }}</div> |
| | | <div class="config-desc">{{ item.desc }}</div> |
| | | <div class="config-control"> |
| | | <el-switch |
| | | v-if="item.type === 'boolean'" |
| | | v-model="form[item.code]" |
| | | active-text="是" |
| | | inactive-text="否"> |
| | | </el-switch> |
| | | <el-input-number |
| | | v-else |
| | | v-model="form[item.code]" |
| | | :min="item.min" |
| | | :max="item.max" |
| | | :step="item.step || 1" |
| | | controls-position="right"> |
| | | </el-input-number> |
| | | </div> |
| | | <div class="config-meta"> |
| | | <span class="meta-chip">{{ item.code }}</span> |
| | | <span class="meta-chip" v-if="item.unit">单位:{{ item.unit }}</span> |
| | | <span class="meta-chip" v-if="item.type !== 'boolean'">范围:{{ item.min }} - {{ item.max }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-tab-pane> |
| | | <el-tab-pane label="高级参数" name="advanced"> |
| | | <div class="group-card" v-for="group in advancedGroups" :key="group.key"> |
| | | <div class="group-head"> |
| | | <div> |
| | | <div class="group-title">{{ group.title }}</div> |
| | | <div class="group-desc">{{ group.desc }}</div> |
| | | </div> |
| | | </div> |
| | | <div class="config-grid"> |
| | | <div class="config-item" v-for="item in group.items" :key="item.code"> |
| | | <div class="config-name">{{ item.name }}</div> |
| | | <div class="config-desc">{{ item.desc }}</div> |
| | | <div class="config-control"> |
| | | <el-input-number |
| | | v-model="form[item.code]" |
| | | :min="item.min" |
| | | :max="item.max" |
| | | :step="item.step || 1" |
| | | controls-position="right"> |
| | | </el-input-number> |
| | | </div> |
| | | <div class="config-meta"> |
| | | <span class="meta-chip">{{ item.code }}</span> |
| | | <span class="meta-chip" v-if="item.unit">单位:{{ item.unit }}</span> |
| | | <span class="meta-chip">范围:{{ item.min }} - {{ item.max }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="section-note" v-if="group.note">{{ group.note }}</div> |
| | | </div> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </template> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script> |
| | | <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script> |
| | | <script type="text/javascript" src="../../static/vue/element/element.js"></script> |
| | | <script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script> |
| | | <script type="text/javascript" src="../../static/js/watch/fakeOperationConfig.js" charset="utf-8"></script> |
| | | </body> |
| | | </html> |