| | |
| | | import com.core.exception.CoolException; |
| | | import com.zy.asrs.entity.*; |
| | | import com.zy.asrs.enums.LocStsType; |
| | | import com.zy.asrs.mapper.BasDevpMapper; |
| | | import com.zy.asrs.service.*; |
| | | import com.zy.asrs.service.impl.BasStationServiceImpl; |
| | | import com.zy.asrs.task.AbstractHandler; |
| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.transaction.interceptor.TransactionAspectSupport; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.*; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | |
| | | |
| | | @Autowired |
| | | private AgvProperties agvProperties; |
| | | |
| | | @Autowired |
| | | private WrkMastLogService wrkMastLogService; |
| | | |
| | | @Autowired |
| | | private BasDevpMapper basDevpMapper; |
| | | |
| | | /** |
| | | * 站点轮询计数器,用于平均分配站点 |
| | | * Key: 站点组标识(如 "east" 或 "west"),Value: 当前轮询索引 |
| | | */ |
| | | private final Map<String, AtomicInteger> siteRoundRobinCounters = new ConcurrentHashMap<>(); |
| | | |
| | | public ReturnT<String> start(WrkMast wrkMast) { |
| | | // 4.入库完成 |
| | |
| | | |
| | | log.info("出库任务完成,生成{}任务,任务ID:{},托盘码:{}", isEmptyPallet ? "空托出库" : "满托出库", outTask.getId(), outTask.getBarcode()); |
| | | |
| | | // 分配缓存库位(whs_type=2) |
| | | LocCache cacheLoc = locCacheService.selectOne(new EntityWrapper<LocCache>() |
| | | .eq("whs_type", agvProperties.getWhsTypeMapping().getCacheArea()) // whs_type=2 缓存区 |
| | | .eq("frozen", 0) |
| | | .eq("loc_sts", LocStsType.LOC_STS_TYPE_O.type) // O.闲置 |
| | | .ne("full_plt", isEmptyPallet ? "Y" : "N") // 空托不选满板库位,满托不选空板库位 |
| | | .orderAsc(Arrays.asList("row1", "bay1", "lev1")) |
| | | .last("OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY")); |
| | | |
| | | if (cacheLoc == null) { |
| | | log.warn("没有可用的缓存库位,无法生成{}任务,任务ID:{}", isEmptyPallet ? "空托出库" : "满托出库", outTask.getId()); |
| | | return; |
| | | } |
| | | |
| | | // 获取出库站点(出库任务的staNo是出库站点,将作为空托/满托出库任务的源站点) |
| | | String outboundStaNo = outTask.getStaNo(); |
| | | if (outboundStaNo == null || outboundStaNo.isEmpty()) { |
| | |
| | | return; |
| | | } |
| | | |
| | | // 根据缓存区配置选择站点和机器人组(西侧) |
| | | List<String> cacheStations = agvProperties.getWestStations(); |
| | | String robotGroup = agvProperties.getRobotGroupWest(); |
| | | // 根据出库站点判断是东侧还是西侧 |
| | | Set<String> eastStations = new HashSet<>(agvProperties.getEastStations()); |
| | | Set<String> westStations = new HashSet<>(agvProperties.getWestStations()); |
| | | |
| | | if (cacheStations.isEmpty()) { |
| | | log.warn("缓存区没有配置站点,无法生成{}任务,任务ID:{}", isEmptyPallet ? "空托出库" : "满托出库", outTask.getId()); |
| | | List<String> cacheStations; |
| | | String robotGroup; |
| | | Long targetWhsType; |
| | | String sideName; |
| | | |
| | | if (eastStations.contains(outboundStaNo)) { |
| | | // 东侧出库站点,查找东侧WA库位(whs_type=1) |
| | | cacheStations = agvProperties.getEastStations(); |
| | | robotGroup = agvProperties.getRobotGroupEast(); |
| | | targetWhsType = agvProperties.getWhsTypeMapping().getInboundArea(); // whs_type=1 |
| | | sideName = agvProperties.getEastDisplayName(); // 从配置读取显示名称 |
| | | log.info("出库站点{}在{},查找{}WA库位(whs_type={})", outboundStaNo, sideName, sideName, targetWhsType); |
| | | } else if (westStations.contains(outboundStaNo)) { |
| | | // 西侧出库站点,查找西侧WA库位(whs_type=2) |
| | | cacheStations = agvProperties.getWestStations(); |
| | | robotGroup = agvProperties.getRobotGroupWest(); |
| | | targetWhsType = agvProperties.getWhsTypeMapping().getCacheArea(); // whs_type=2 |
| | | sideName = agvProperties.getWestDisplayName(); // 从配置读取显示名称 |
| | | log.info("出库站点{}在{},查找{}WA库位(whs_type={})", outboundStaNo, sideName, sideName, targetWhsType); |
| | | } else { |
| | | log.warn("出库站点{}不在配置的站点列表中,无法判断{}/{},任务ID:{}", |
| | | outboundStaNo, agvProperties.getEastDisplayName(), agvProperties.getWestDisplayName(), outTask.getId()); |
| | | return; |
| | | } |
| | | |
| | | // 选择缓存区目标站点(使用第一个可用站点,或可以优化为选择任务最少的站点) |
| | | String cacheStaNo = cacheStations.get(0); |
| | | if (cacheStations.isEmpty()) { |
| | | log.warn("{}侧没有配置站点,无法生成{}任务,任务ID:{}", |
| | | sideName, isEmptyPallet ? "空托出库" : "满托出库", outTask.getId()); |
| | | return; |
| | | } |
| | | |
| | | // 分配缓存库位:只查找WA开头的库位(CA开头只做入库,WA开头才会被出库分配缓存区) |
| | | // 使用新的分配逻辑:按列优先级(第三列→第二列→第一列)分配 |
| | | String cacheAreaPrefix = agvProperties.getLocationPrefix().getCacheArea(); |
| | | LocCache cacheLoc = allocateCacheLocationByPriority(targetWhsType, cacheAreaPrefix, isEmptyPallet); |
| | | |
| | | if (cacheLoc == null) { |
| | | log.warn("{}侧没有可用的{}缓存库位,不生成{}AGV任务,任务ID:{}", |
| | | sideName, cacheAreaPrefix, isEmptyPallet ? "空托出库" : "满托出库", outTask.getId()); |
| | | return; |
| | | } |
| | | |
| | | // 检查工作档是否已完成或已转历史档 |
| | | boolean workCompleted = false; |
| | | if (outTask.getWrkNo() != null) { |
| | | // 检查工作档是否存在且已完成 |
| | | WrkMast wrkMast = wrkMastService.selectOne( |
| | | new EntityWrapper<WrkMast>().eq("wrk_no", outTask.getWrkNo()) |
| | | ); |
| | | |
| | | if (wrkMast != null) { |
| | | Long wrkSts = wrkMast.getWrkSts(); |
| | | // 出库任务完成状态:14(已出库未确认)或15(出库更新完成) |
| | | if (wrkSts != null && (wrkSts == 14L || wrkSts == 15L)) { |
| | | workCompleted = true; |
| | | log.debug("工作档{}已完成,状态:{}", outTask.getWrkNo(), wrkSts); |
| | | } |
| | | } else { |
| | | // 如果工作档不存在,检查历史档 |
| | | WrkMastLog wrkMastLog = wrkMastLogService.selectOne( |
| | | new EntityWrapper<WrkMastLog>().eq("wrk_no", outTask.getWrkNo()) |
| | | ); |
| | | if (wrkMastLog != null) { |
| | | long logWrkSts = wrkMastLog.getWrkSts(); |
| | | // 出库任务历史档完成状态:15(出库更新完成) |
| | | if (logWrkSts == 15L) { |
| | | workCompleted = true; |
| | | log.debug("工作档{}已转历史档并完结,历史档状态:{}", outTask.getWrkNo(), logWrkSts); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 检查是否有从该出库站点到缓存区的正在搬运任务(状态8:已呼叫AGV,正在搬运) |
| | | // 出库到缓存区的任务类型:101(全板出库)或110(空板出库) |
| | | List<Task> transportingTasks = taskService.selectList( |
| | | new EntityWrapper<Task>() |
| | | .eq("source_sta_no", outboundStaNo) // 源站点是出库站点 |
| | | .in("sta_no", cacheStations) // 目标站点是缓存区站点 |
| | | .eq("task_type", "agv") |
| | | .eq("wrk_sts", 8L) // 只检查正在搬运状态的任务 |
| | | .in("io_type", 101, 110) // 出库到缓存区的任务类型 |
| | | ); |
| | | |
| | | // 如果有正在搬运的任务,且工作档未完成,则不分配缓存库位 |
| | | if (!transportingTasks.isEmpty() && !workCompleted) { |
| | | log.info("出库站点{}到缓存区有{}个正在搬运的AGV任务,且工作档未完成,暂不分配缓存库位,等待搬运完成。出库任务ID:{}", |
| | | outboundStaNo, transportingTasks.size(), outTask.getId()); |
| | | return; // 有正在搬运的任务且工作档未完成,不分配缓存库位,等待下次检查 |
| | | } |
| | | |
| | | if (!transportingTasks.isEmpty() && workCompleted) { |
| | | log.info("出库站点{}到缓存区有{}个正在搬运的AGV任务,但工作档已完成或已转历史档,允许分配缓存库位。出库任务ID:{}", |
| | | outboundStaNo, transportingTasks.size(), outTask.getId()); |
| | | } |
| | | |
| | | // 选择缓存区目标站点(使用和入库一样的分配策略:轮询、最少任务数) |
| | | String cacheStaNo = allocateCacheStation(cacheStations, ioType); |
| | | if (cacheStaNo == null) { |
| | | log.warn("无法为出库任务ID:{}分配缓存区站点,所有站点都在使用中", outTask.getId()); |
| | | return; |
| | | } |
| | | |
| | | // 生成工作号 |
| | | int workNo = commonService.getWorkNo(WorkNoType.PAKOUT.type); |
| | |
| | | log.info("成功生成{}任务,任务ID:{},工作号:{},源站点:{},目标站点:{},缓存库位:{}", |
| | | isEmptyPallet ? "空托出库" : "满托出库", cacheTask.getId(), workNo, outboundStaNo, cacheStaNo, cacheLoc.getLocNo()); |
| | | } |
| | | |
| | | /** |
| | | * 按优先级分配缓存库位 |
| | | * 优先级规则: |
| | | * 1. 优先分配第三列(bay1=3),且该排的1、2、3列都是空的 |
| | | * 2. 如果所有第三列都有货,则分配第二列(bay1=2),且该排的1、2列都是空的 |
| | | * 3. 如果所有排的第二第三列都满了,则分配第一列(bay1=1) |
| | | * 4. 如果所有第一列都满了,再检查第二列和第三列 |
| | | * 5. 层(lev1)从第一层开始 |
| | | * |
| | | * @param whsType 库区类型 |
| | | * @param cacheAreaPrefix 缓存区库位前缀(如"WA") |
| | | * @param isEmptyPallet 是否空托 |
| | | * @return 分配的缓存库位,如果无法分配则返回null |
| | | */ |
| | | private LocCache allocateCacheLocationByPriority(Long whsType, String cacheAreaPrefix, boolean isEmptyPallet) { |
| | | // 查询所有符合条件的空库位 |
| | | List<LocCache> allLocations = locCacheService.selectList(new EntityWrapper<LocCache>() |
| | | .eq("whs_type", whsType) |
| | | .like("loc_no", cacheAreaPrefix + "%") |
| | | .eq("frozen", 0) |
| | | .eq("loc_sts", LocStsType.LOC_STS_TYPE_O.type) // O.闲置 |
| | | .ne("full_plt", isEmptyPallet ? "Y" : "N") // 空托不选满板库位,满托不选空板库位 |
| | | ); |
| | | |
| | | if (allLocations == null || allLocations.isEmpty()) { |
| | | return null; |
| | | } |
| | | |
| | | // 按row1分组 |
| | | Map<Integer, List<LocCache>> locationsByRow = allLocations.stream() |
| | | .filter(loc -> loc.getRow1() != null) |
| | | .collect(Collectors.groupingBy(LocCache::getRow1)); |
| | | |
| | | if (locationsByRow.isEmpty()) { |
| | | return null; |
| | | } |
| | | |
| | | // 对每个排,检查1、2、3列的状态 |
| | | // 列状态:true表示该列有空库位,false表示该列已满 |
| | | Map<Integer, Map<Integer, Boolean>> rowColumnStatus = new HashMap<>(); |
| | | for (Map.Entry<Integer, List<LocCache>> entry : locationsByRow.entrySet()) { |
| | | Integer row = entry.getKey(); |
| | | List<LocCache> rowLocs = entry.getValue(); |
| | | |
| | | Map<Integer, Boolean> columnStatus = new HashMap<>(); |
| | | // 检查第1、2、3列是否有空库位 |
| | | for (int bay = 1; bay <= 3; bay++) { |
| | | final int bayFinal = bay; // 创建final副本供lambda使用 |
| | | boolean hasEmpty = rowLocs.stream() |
| | | .anyMatch(loc -> loc.getBay1() != null && loc.getBay1() == bayFinal); |
| | | columnStatus.put(bay, hasEmpty); |
| | | } |
| | | rowColumnStatus.put(row, columnStatus); |
| | | } |
| | | |
| | | // 优先级1:分配第三列(bay1=3),且该排的1、2、3列都是空的 |
| | | for (Map.Entry<Integer, List<LocCache>> entry : locationsByRow.entrySet()) { |
| | | Integer row = entry.getKey(); |
| | | Map<Integer, Boolean> columnStatus = rowColumnStatus.get(row); |
| | | |
| | | // 检查该排的1、2、3列是否都是空的 |
| | | if (Boolean.TRUE.equals(columnStatus.get(1)) && |
| | | Boolean.TRUE.equals(columnStatus.get(2)) && |
| | | Boolean.TRUE.equals(columnStatus.get(3))) { |
| | | // 分配该排的第三列,从第一层开始 |
| | | List<LocCache> bay3Locs = entry.getValue().stream() |
| | | .filter(loc -> loc.getBay1() != null && loc.getBay1() == 3) |
| | | .sorted(Comparator.comparing(loc -> loc.getLev1() != null ? loc.getLev1() : 0)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (!bay3Locs.isEmpty()) { |
| | | log.debug("优先级1:分配排{}的第三列,库位:{}", row, bay3Locs.get(0).getLocNo()); |
| | | return bay3Locs.get(0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 优先级2:分配第二列(bay1=2),且该排的1、2列都是空的(第三列可能已满) |
| | | for (Map.Entry<Integer, List<LocCache>> entry : locationsByRow.entrySet()) { |
| | | Integer row = entry.getKey(); |
| | | Map<Integer, Boolean> columnStatus = rowColumnStatus.get(row); |
| | | |
| | | // 检查该排的1、2列是否都是空的 |
| | | if (Boolean.TRUE.equals(columnStatus.get(1)) && |
| | | Boolean.TRUE.equals(columnStatus.get(2))) { |
| | | // 分配该排的第二列,从第一层开始 |
| | | List<LocCache> bay2Locs = entry.getValue().stream() |
| | | .filter(loc -> loc.getBay1() != null && loc.getBay1() == 2) |
| | | .sorted(Comparator.comparing(loc -> loc.getLev1() != null ? loc.getLev1() : 0)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (!bay2Locs.isEmpty()) { |
| | | log.debug("优先级2:分配排{}的第二列,库位:{}", row, bay2Locs.get(0).getLocNo()); |
| | | return bay2Locs.get(0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 优先级3:分配第一列(bay1=1),所有排的第二第三列都满了 |
| | | for (Map.Entry<Integer, List<LocCache>> entry : locationsByRow.entrySet()) { |
| | | Integer row = entry.getKey(); |
| | | Map<Integer, Boolean> columnStatus = rowColumnStatus.get(row); |
| | | |
| | | // 检查该排的第一列是否有空库位 |
| | | if (Boolean.TRUE.equals(columnStatus.get(1))) { |
| | | // 分配该排的第一列,从第一层开始 |
| | | List<LocCache> bay1Locs = entry.getValue().stream() |
| | | .filter(loc -> loc.getBay1() != null && loc.getBay1() == 1) |
| | | .sorted(Comparator.comparing(loc -> loc.getLev1() != null ? loc.getLev1() : 0)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (!bay1Locs.isEmpty()) { |
| | | log.debug("优先级3:分配排{}的第一列,库位:{}", row, bay1Locs.get(0).getLocNo()); |
| | | return bay1Locs.get(0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 优先级4:如果所有第一列都满了,再检查第二列和第三列(不要求该排的1、2列都是空的) |
| | | // 先检查第二列 |
| | | for (Map.Entry<Integer, List<LocCache>> entry : locationsByRow.entrySet()) { |
| | | Integer row = entry.getKey(); |
| | | Map<Integer, Boolean> columnStatus = rowColumnStatus.get(row); |
| | | |
| | | // 检查该排的第二列是否有空库位 |
| | | if (Boolean.TRUE.equals(columnStatus.get(2))) { |
| | | List<LocCache> bay2Locs = entry.getValue().stream() |
| | | .filter(loc -> loc.getBay1() != null && loc.getBay1() == 2) |
| | | .sorted(Comparator.comparing(loc -> loc.getLev1() != null ? loc.getLev1() : 0)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (!bay2Locs.isEmpty()) { |
| | | log.debug("优先级4:分配排{}的第二列,库位:{}", row, bay2Locs.get(0).getLocNo()); |
| | | return bay2Locs.get(0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 优先级5:最后检查第三列(不要求该排的1、2、3列都是空的) |
| | | for (Map.Entry<Integer, List<LocCache>> entry : locationsByRow.entrySet()) { |
| | | Integer row = entry.getKey(); |
| | | Map<Integer, Boolean> columnStatus = rowColumnStatus.get(row); |
| | | |
| | | // 检查该排的第三列是否有空库位 |
| | | if (Boolean.TRUE.equals(columnStatus.get(3))) { |
| | | List<LocCache> bay3Locs = entry.getValue().stream() |
| | | .filter(loc -> loc.getBay1() != null && loc.getBay1() == 3) |
| | | .sorted(Comparator.comparing(loc -> loc.getLev1() != null ? loc.getLev1() : 0)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (!bay3Locs.isEmpty()) { |
| | | log.debug("优先级5:分配排{}的第三列,库位:{}", row, bay3Locs.get(0).getLocNo()); |
| | | return bay3Locs.get(0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 如果所有列都满了,返回null |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 为出库到缓存区的任务分配站点(使用和入库一样的分配策略) |
| | | * @param cacheStations 缓存区站点列表 |
| | | * @param ioType 任务类型(101=全板出库,110=空板出库) |
| | | * @return 分配的站点编号,如果无法分配则返回null |
| | | */ |
| | | private String allocateCacheStation(List<String> cacheStations, Integer ioType) { |
| | | if (cacheStations == null || cacheStations.isEmpty()) { |
| | | log.warn("缓存区站点列表为空,无法分配站点"); |
| | | return null; |
| | | } |
| | | |
| | | // 将站点字符串列表转换为整数列表 |
| | | List<Integer> siteIntList = cacheStations.stream() |
| | | .map(Integer::parseInt) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 查询所有缓存区站点的设备信息(包含任务数) |
| | | List<BasDevp> devList = basDevpMapper.selectList(new EntityWrapper<BasDevp>() |
| | | .in("dev_no", siteIntList) |
| | | ); |
| | | |
| | | if (devList.isEmpty()) { |
| | | log.warn("缓存区站点{}在设备表中不存在", cacheStations); |
| | | return cacheStations.get(0); // 降级:返回第一个站点 |
| | | } |
| | | |
| | | // 按入库任务数排序(出库到缓存区也使用in_qty字段) |
| | | devList.sort(Comparator.comparing(BasDevp::getInQty)); |
| | | |
| | | // 获取最少任务数 |
| | | int minInQty = devList.get(0).getInQty(); |
| | | |
| | | // 筛选出任务数最少的站点列表 |
| | | List<BasDevp> minTaskSites = devList.stream() |
| | | .filter(dev -> dev.getInQty() == minInQty) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 根据配置选择分配策略 |
| | | String strategy = agvProperties.getSiteAllocation().getStrategy(); |
| | | boolean enableRoundRobin = agvProperties.getSiteAllocation().isEnableRoundRobin(); |
| | | |
| | | List<BasDevp> orderedSites = new ArrayList<>(); |
| | | String groupKey = "west"; // 缓存区使用西侧 |
| | | |
| | | if (minTaskSites.size() > 1 && enableRoundRobin && "round-robin".equals(strategy)) { |
| | | // 轮询分配:当多个站点任务数相同时,使用轮询 |
| | | AtomicInteger counter = siteRoundRobinCounters.computeIfAbsent(groupKey, k -> new AtomicInteger(0)); |
| | | int startIndex = counter.get() % minTaskSites.size(); |
| | | // 将轮询选中的站点放在最前面 |
| | | orderedSites.addAll(minTaskSites.subList(startIndex, minTaskSites.size())); |
| | | orderedSites.addAll(minTaskSites.subList(0, startIndex)); |
| | | // 添加其他站点(任务数更多的) |
| | | orderedSites.addAll(devList.stream() |
| | | .filter(dev -> dev.getInQty() > minInQty) |
| | | .collect(Collectors.toList())); |
| | | log.debug("使用轮询分配策略,站点组:{},轮询起始索引:{}", groupKey, startIndex); |
| | | } else if (minTaskSites.size() > 1 && enableRoundRobin && "random".equals(strategy)) { |
| | | // 随机分配:先随机排序任务数最少的站点 |
| | | List<BasDevp> shuffledMinSites = new ArrayList<>(minTaskSites); |
| | | Collections.shuffle(shuffledMinSites); |
| | | orderedSites.addAll(shuffledMinSites); |
| | | // 添加其他站点(任务数更多的) |
| | | orderedSites.addAll(devList.stream() |
| | | .filter(dev -> dev.getInQty() > minInQty) |
| | | .collect(Collectors.toList())); |
| | | log.debug("使用随机分配策略"); |
| | | } else { |
| | | // 默认:按入库任务数排序(已经排序好了) |
| | | orderedSites = devList; |
| | | } |
| | | |
| | | // 依次检查每个站点是否在搬运,找到第一个空闲站点就分配 |
| | | String selectedSite = null; |
| | | List<Integer> checkIoTypes = Arrays.asList(101, 110); // 出库到缓存区的任务类型 |
| | | |
| | | for (BasDevp dev : orderedSites) { |
| | | String staNo = String.valueOf(dev.getDevNo()); |
| | | |
| | | // 检查该站点是否有正在搬运的同类型任务 |
| | | List<Task> transportingTasks = taskService.selectList( |
| | | new EntityWrapper<Task>() |
| | | .eq("sta_no", staNo) |
| | | .eq("task_type", "agv") |
| | | .eq("wrk_sts", 8L) // 只检查正在搬运状态的任务 |
| | | .in("io_type", checkIoTypes) |
| | | ); |
| | | |
| | | if (!transportingTasks.isEmpty()) { |
| | | log.debug("缓存区站点{}有{}个正在搬运的出库AGV任务,检查下一个站点", |
| | | staNo, transportingTasks.size()); |
| | | continue; // 该站点正在搬运,检查下一个站点 |
| | | } |
| | | |
| | | // 找到第一个空闲站点,分配 |
| | | selectedSite = staNo; |
| | | log.info("出库到缓存区任务按规则应分配到站点{},该站点空闲,分配成功", staNo); |
| | | break; |
| | | } |
| | | |
| | | // 如果所有站点都在搬运,则不分配站点 |
| | | if (selectedSite == null) { |
| | | // log.warn("所有缓存区站点都有正在搬运的出库任务,暂不分配站点,等待空闲"); |
| | | return null; |
| | | } |
| | | |
| | | // 更新站点任务数(出库到缓存区也使用in_qty字段) |
| | | basDevpMapper.incrementInQty(Integer.parseInt(selectedSite)); |
| | | |
| | | return selectedSite; |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public ReturnT<String> agvDoIn(Task wrkMast) { |
| | | if (wrkMast.getIoType().equals(1)) { |
| | | LocCache locCache = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", wrkMast.getLocNo())); |
| | | if (Objects.isNull(locCache)) { |
| | | throw new CoolException("数据错误,库位不存在!!"); |
| | | } |
| | | if (!locCache.getLocSts().equals(LocStsType.LOC_STS_TYPE_S.type)) { |
| | | throw new CoolException("当前库位状态为:" + LocStsType.LOC_STS_TYPE_S.type + "." + LocStsType.LOC_STS_TYPE_S.desc + ",不是出库预约状态"); |
| | | } |
| | | List<TaskDetl> taskDetls = taskDetlService.selectList(new EntityWrapper<TaskDetl>().eq("task_id", wrkMast.getId())); |
| | | if (Objects.isNull(taskDetls)) { |
| | | throw new CoolException("数据错误:组托数据不存在!!"); |
| | | } |
| | | taskDetls.forEach(pakin -> { |
| | | LocCacheDetl detl = new LocCacheDetl(); |
| | | BeanUtils.copyProperties(pakin, detl); |
| | | detl.setBarcode(pakin.getBarcode()) |
| | | .setLocId(locCache.getId()) |
| | | .setAnfme(pakin.getAnfme()) |
| | | .setBrand(pakin.getBrand()) |
| | | .setAppeTime(new Date()) |
| | | .setSpecs(pakin.getSpecs()) |
| | | .setColor(pakin.getColor()) |
| | | .setLocNo(locCache.getLocNo()) |
| | | .setAreaId(locCache.getAreaId()) |
| | | .setAreaName(locCache.getAreaName()) |
| | | .setUnit(pakin.getUnit()) |
| | | .setBatch(pakin.getBatch()); |
| | | if (!locCacheDetlService.insert(detl)) { |
| | | throw new CoolException("库位明细保存失败!!"); |
| | | Integer ioType = wrkMast.getIoType(); |
| | | if (ioType == null) { |
| | | throw new CoolException("数据错误:ioType为空!!"); |
| | | } |
| | | |
| | | // 处理入库任务类型:1(实托入库)、10(空托入库)、53、57 |
| | | if (ioType == 1 || ioType == 10 || ioType == 53 || ioType == 57) { |
| | | // ioType == 1 需要处理组托数据 |
| | | if (ioType == 1) { |
| | | LocCache locCache = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", wrkMast.getLocNo())); |
| | | if (Objects.isNull(locCache)) { |
| | | throw new CoolException("数据错误,库位不存在!!"); |
| | | } |
| | | }); |
| | | if (!locCache.getLocSts().equals(LocStsType.LOC_STS_TYPE_S.type)) { |
| | | throw new CoolException("当前库位状态为:" + LocStsType.LOC_STS_TYPE_S.type + "." + LocStsType.LOC_STS_TYPE_S.desc + ",不是出库预约状态"); |
| | | } |
| | | List<TaskDetl> taskDetls = taskDetlService.selectList(new EntityWrapper<TaskDetl>().eq("task_id", wrkMast.getId())); |
| | | if (Objects.isNull(taskDetls)) { |
| | | throw new CoolException("数据错误:组托数据不存在!!"); |
| | | } |
| | | taskDetls.forEach(pakin -> { |
| | | LocCacheDetl detl = new LocCacheDetl(); |
| | | BeanUtils.copyProperties(pakin, detl); |
| | | detl.setBarcode(pakin.getBarcode()) |
| | | .setLocId(locCache.getId()) |
| | | .setAnfme(pakin.getAnfme()) |
| | | .setBrand(pakin.getBrand()) |
| | | .setAppeTime(new Date()) |
| | | .setSpecs(pakin.getSpecs()) |
| | | .setColor(pakin.getColor()) |
| | | .setLocNo(locCache.getLocNo()) |
| | | .setAreaId(locCache.getAreaId()) |
| | | .setAreaName(locCache.getAreaName()) |
| | | .setUnit(pakin.getUnit()) |
| | | .setBatch(pakin.getBatch()); |
| | | if (!locCacheDetlService.insert(detl)) { |
| | | throw new CoolException("库位明细保存失败!!"); |
| | | } |
| | | }); |
| | | |
| | | locCache.setLocSts(LocStsType.LOC_STS_TYPE_F.type); |
| | | locCache.setModiTime(new Date()); |
| | | locCache.setBarcode(wrkMast.getBarcode()); |
| | | locCache.setModiTime(new Date()); |
| | | locCache.setIoTime(new Date()); |
| | | if (!locCacheService.updateById(locCache)) { |
| | | throw new CoolException("库位状态修改失败!"); |
| | | // 根据fullPlt设置库位状态:满托设置为"F"(在库),空托设置为"D"(空桶/空栈板) |
| | | boolean isFullPlt = wrkMast.getFullPlt() != null && wrkMast.getFullPlt().equals("Y"); |
| | | locCache.setLocSts(isFullPlt ? LocStsType.LOC_STS_TYPE_F.type : LocStsType.LOC_STS_TYPE_D.type); |
| | | locCache.setFullPlt(isFullPlt ? "Y" : "N"); |
| | | locCache.setModiTime(new Date()); |
| | | locCache.setBarcode(wrkMast.getBarcode()); |
| | | locCache.setIoTime(new Date()); |
| | | if (!locCacheService.updateById(locCache)) { |
| | | throw new CoolException("库位状态修改失败!"); |
| | | } |
| | | } else if (ioType == 10 || ioType == 53 || ioType == 57) { |
| | | // 空托入库或其他入库类型,也需要更新缓存库位状态 |
| | | LocCache locCache = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", wrkMast.getLocNo())); |
| | | if (locCache != null) { |
| | | // 根据fullPlt设置库位状态:满托设置为"F"(在库),空托设置为"D"(空桶/空栈板) |
| | | // ioType == 10 是空托入库,默认设置为"D" |
| | | boolean isFullPlt = (ioType != 10) && (wrkMast.getFullPlt() != null && wrkMast.getFullPlt().equals("Y")); |
| | | locCache.setLocSts(isFullPlt ? LocStsType.LOC_STS_TYPE_F.type : LocStsType.LOC_STS_TYPE_D.type); |
| | | locCache.setFullPlt(isFullPlt ? "Y" : "N"); |
| | | locCache.setModiTime(new Date()); |
| | | locCache.setBarcode(wrkMast.getBarcode()); |
| | | locCache.setIoTime(new Date()); |
| | | if (!locCacheService.updateById(locCache)) { |
| | | throw new CoolException("库位状态修改失败!"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 更新任务状态为5(库存更新完成) |
| | | wrkMast.setWrkSts(5L); |
| | | wrkMast.setModiTime(new Date()); |
| | | if (!taskService.updateById(wrkMast)) { |
| | | throw new CoolException("任务状态修改失败!!"); |
| | | } |
| | | }else { |
| | | throw new CoolException("数据错误:ioType不存在!!"); |
| | | } else { |
| | | // 非入库任务类型,记录日志但不抛出异常,允许其他类型的任务通过 |
| | | log.warn("agvDoIn方法收到非入库任务类型,ioType:{},taskId:{},跳过处理", ioType, wrkMast.getId()); |
| | | } |
| | | |
| | | |
| | | return SUCCESS; |
| | | } |