| | |
| | | import com.zy.asrs.entity.BasCrnp; |
| | | import com.zy.asrs.entity.BasDualCrnp; |
| | | import com.zy.asrs.entity.BasStation; |
| | | import com.zy.asrs.entity.StationFlowCapacity; |
| | | import com.zy.asrs.entity.WrkMast; |
| | | import com.zy.asrs.service.BasCrnpService; |
| | | import com.zy.asrs.service.BasDualCrnpService; |
| | | import com.zy.asrs.service.BasStationService; |
| | | import com.zy.asrs.service.StationFlowCapacityService; |
| | | import com.zy.asrs.service.WrkMastService; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.enums.RedisKeyType; |
| | | import com.zy.core.enums.WrkStsType; |
| | | import com.zy.system.entity.Config; |
| | | import com.zy.system.service.ConfigService; |
| | | import org.slf4j.Logger; |
| | |
| | | import org.springframework.transaction.support.TransactionTemplate; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Date; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | |
| | | |
| | | private static final Logger LOGGER = LoggerFactory.getLogger(AutoTuneApplyServiceImpl.class); |
| | | private static final String PROMPT_SCENE_CODE = "auto_tune_apply"; |
| | | private static final String DIRECTION_OUT = "OUT"; |
| | | private static final long APPLY_LOCK_SECONDS = 120L; |
| | | private static final String APPLY_LOCK_BUSY_REASON = "申请调参锁失败,锁不可用,可能已有任务或 Redis 异常"; |
| | | private static final List<Long> FINAL_WRK_STS_LIST = Arrays.asList( |
| | | WrkStsType.COMPLETE_INBOUND.sts, |
| | | WrkStsType.SETTLE_INBOUND.sts, |
| | | WrkStsType.COMPLETE_OUTBOUND.sts, |
| | | WrkStsType.SETTLE_OUTBOUND.sts, |
| | | WrkStsType.COMPLETE_LOC_MOVE.sts, |
| | | WrkStsType.COMPLETE_CRN_MOVE.sts |
| | | ); |
| | | |
| | | @Autowired |
| | | private AiAutoTuneJobService aiAutoTuneJobService; |
| | |
| | | @Autowired |
| | | private BasDualCrnpService basDualCrnpService; |
| | | @Autowired |
| | | private StationFlowCapacityService stationFlowCapacityService; |
| | | private WrkMastService wrkMastService; |
| | | @Autowired |
| | | private PlatformTransactionManager transactionManager; |
| | | @Autowired |
| | |
| | | now, |
| | | true |
| | | ); |
| | | refreshSystemConfigCacheIfNeeded(persistenceResult); |
| | | refreshSystemConfigCacheSafely(persistenceResult); |
| | | return buildResult(job, persistenceResult.getAuditChanges(), false); |
| | | } catch (RuntimeException exception) { |
| | | markWriteFailure(validatedChanges, exception); |
| | |
| | | sourceChanges, |
| | | now |
| | | ); |
| | | refreshRollbackConfigCacheIfNeeded(persistenceResult); |
| | | refreshRollbackConfigCacheSafely(persistenceResult); |
| | | return buildResult(rollbackJob, persistenceResult.getRollbackChanges(), false); |
| | | } catch (RuntimeException exception) { |
| | | Date failureNow = new Date(); |
| | |
| | | Integer maxValue = resolveMaxValue(validatedChange, rule, requestedValue); |
| | | if (maxValue == null) { |
| | | return validatedChange.reject("站点 " + validatedChange.getTargetId() |
| | | + " 缺少 OUT 方向 bufferCapacity,无法证明 outTaskLimit 上限"); |
| | | + " 缺少 outBufferCapacity,无法证明 outTaskLimit 上限"); |
| | | } |
| | | if (requestedValue < rule.getMinValue() || requestedValue > maxValue) { |
| | | return validatedChange.reject(validatedChange.getTargetKey() + " 必须在 " |
| | |
| | | if (config == null) { |
| | | return CurrentValue.rejected("运行参数不存在: " + validatedChange.getTargetKey()); |
| | | } |
| | | return numericCurrentValue(config.getValue(), false, validatedChange.getTargetKey()); |
| | | return numericCurrentValue(config.getValue(), validatedChange.getTargetKey()); |
| | | } |
| | | if (AutoTuneTargetType.STATION.equals(targetType)) { |
| | | BasStation station = basStationService.getById(targetId); |
| | | if (station == null) { |
| | | return CurrentValue.rejected("站点不存在: " + validatedChange.getTargetId()); |
| | | } |
| | | return numericCurrentValue(toText(station.getOutTaskLimit()), true, validatedChange.getTargetKey()); |
| | | return stationOutTaskLimitCurrentValue(station.getOutTaskLimit(), validatedChange.getTargetKey()); |
| | | } |
| | | if (AutoTuneTargetType.CRN.equals(targetType)) { |
| | | BasCrnp crnp = basCrnpService.getById(targetId); |
| | |
| | | return CurrentValue.rejected("堆垛机不存在: " + validatedChange.getTargetId()); |
| | | } |
| | | Integer value = "maxOutTask".equals(validatedChange.getTargetKey()) ? crnp.getMaxOutTask() : crnp.getMaxInTask(); |
| | | return numericCurrentValue(toText(value), false, validatedChange.getTargetKey()); |
| | | return numericCurrentValue(toText(value), validatedChange.getTargetKey()); |
| | | } |
| | | BasDualCrnp dualCrnp = basDualCrnpService.getById(targetId); |
| | | if (dualCrnp == null) { |
| | | return CurrentValue.rejected("双工位堆垛机不存在: " + validatedChange.getTargetId()); |
| | | } |
| | | Integer value = "maxOutTask".equals(validatedChange.getTargetKey()) ? dualCrnp.getMaxOutTask() : dualCrnp.getMaxInTask(); |
| | | return numericCurrentValue(toText(value), false, validatedChange.getTargetKey()); |
| | | return numericCurrentValue(toText(value), validatedChange.getTargetKey()); |
| | | } |
| | | |
| | | private CurrentValue numericCurrentValue(String oldValue, boolean nullOrNegativeAsZero, String targetKey) { |
| | | private CurrentValue stationOutTaskLimitCurrentValue(Integer outTaskLimit, String targetKey) { |
| | | if (outTaskLimit == null || outTaskLimit < 0) { |
| | | return CurrentValue.rejected(targetKey + " 当前为不限制,需要人工先初始化为有限值后才能自动调参"); |
| | | } |
| | | return numericCurrentValue(toText(outTaskLimit), targetKey); |
| | | } |
| | | |
| | | private CurrentValue numericCurrentValue(String oldValue, String targetKey) { |
| | | if (oldValue == null || oldValue.trim().isEmpty()) { |
| | | if (nullOrNegativeAsZero) { |
| | | return CurrentValue.accepted(null, 0); |
| | | } |
| | | return CurrentValue.rejected(targetKey + " 当前值为空,无法计算步长"); |
| | | } |
| | | try { |
| | | Integer parsedValue = Integer.valueOf(oldValue.trim()); |
| | | if (nullOrNegativeAsZero && parsedValue < 0) { |
| | | return CurrentValue.accepted(oldValue, 0); |
| | | } |
| | | return CurrentValue.accepted(oldValue, parsedValue); |
| | | } catch (Exception exception) { |
| | | return CurrentValue.rejected(targetKey + " 当前值不是整数,无法计算步长"); |
| | |
| | | return rule.getMaxValue(); |
| | | } |
| | | Integer targetId = parseTargetId(validatedChange.getTargetId(), rule.getTargetType()); |
| | | StationFlowCapacity capacity = stationFlowCapacityService.getOne( |
| | | new QueryWrapper<StationFlowCapacity>() |
| | | .eq("station_id", targetId) |
| | | .eq("direction_code", DIRECTION_OUT) |
| | | .last("limit 1") |
| | | ); |
| | | if (capacity == null || capacity.getBufferCapacity() == null) { |
| | | BasStation station = basStationService.getById(targetId); |
| | | if (station == null || station.getOutBufferCapacity() == null) { |
| | | return requestedValue == 0 ? 0 : null; |
| | | } |
| | | return Math.max(0, capacity.getBufferCapacity()); |
| | | return Math.max(0, station.getOutBufferCapacity()); |
| | | } |
| | | |
| | | private Date findCooldownExpireTime(ValidatedChange validatedChange, Date now) { |
| | |
| | | } |
| | | } |
| | | |
| | | private void refreshSystemConfigCacheIfNeeded(ApplyPersistenceResult persistenceResult) { |
| | | private void refreshSystemConfigCacheSafely(ApplyPersistenceResult persistenceResult) { |
| | | if (persistenceResult != null && persistenceResult.isRefreshConfigCache()) { |
| | | configService.refreshSystemConfigCache(); |
| | | refreshSystemConfigCacheSafely("apply"); |
| | | } |
| | | } |
| | | |
| | |
| | | return List.of(rollbackChange); |
| | | } |
| | | |
| | | private void refreshRollbackConfigCacheIfNeeded(RollbackPersistenceResult persistenceResult) { |
| | | private void refreshRollbackConfigCacheSafely(RollbackPersistenceResult persistenceResult) { |
| | | if (persistenceResult != null && persistenceResult.isRefreshConfigCache()) { |
| | | refreshSystemConfigCacheSafely("rollback"); |
| | | } |
| | | } |
| | | |
| | | private void refreshSystemConfigCacheSafely(String scene) { |
| | | try { |
| | | configService.refreshSystemConfigCache(); |
| | | } catch (RuntimeException exception) { |
| | | LOGGER.warn("AI自动调参{}已提交,但刷新系统配置缓存失败", scene, exception); |
| | | } |
| | | } |
| | | |
| | |
| | | job.setTriggerType(AutoTuneTriggerType.normalize(request.getTriggerType())); |
| | | job.setStatus(AutoTuneJobStatus.RUNNING.getCode()); |
| | | job.setStartTime(now); |
| | | job.setHasActiveTasks(0); |
| | | job.setHasActiveTasks(resolveHasActiveTasksForAudit()); |
| | | job.setPromptSceneCode(PROMPT_SCENE_CODE); |
| | | job.setSummary(dryRun ? "AI自动调参 dry-run: " + safeReason(request.getReason()) : safeReason(request.getReason())); |
| | | job.setIntervalBefore(readIntervalMinutes()); |
| | |
| | | job.setTriggerType(AutoTuneTriggerType.ROLLBACK.getCode()); |
| | | job.setStatus(AutoTuneJobStatus.RUNNING.getCode()); |
| | | job.setStartTime(now); |
| | | job.setHasActiveTasks(0); |
| | | job.setHasActiveTasks(resolveHasActiveTasksForAudit()); |
| | | job.setPromptSceneCode(PROMPT_SCENE_CODE); |
| | | job.setSummary(safeReason(reason)); |
| | | job.setIntervalBefore(readIntervalMinutes()); |
| | |
| | | return job; |
| | | } |
| | | |
| | | private int resolveHasActiveTasksForAudit() { |
| | | if (wrkMastService == null) { |
| | | LOGGER.warn("AI自动调参审计无法获取 WrkMastService,hasActiveTasks 按 0 记录"); |
| | | return 0; |
| | | } |
| | | try { |
| | | QueryWrapper<WrkMast> queryWrapper = new QueryWrapper<>(); |
| | | queryWrapper.and(wrapper -> wrapper.notIn("wrk_sts", FINAL_WRK_STS_LIST).or().isNull("wrk_sts")); |
| | | return wrkMastService.count(queryWrapper) > 0 ? 1 : 0; |
| | | } catch (RuntimeException exception) { |
| | | LOGGER.warn("AI自动调参审计查询未完成任务失败,hasActiveTasks 按 0 记录", exception); |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | private void finishJob(AiAutoTuneJob job, |
| | | AutoTuneApplyRequest request, |
| | | List<AiAutoTuneChange> auditChanges, |