| | |
| | | import com.zy.asrs.entity.Task; |
| | | import com.zy.asrs.entity.TaskLog; |
| | | import com.zy.asrs.entity.WrkMast; |
| | | import com.zy.asrs.entity.BasDevp; |
| | | import com.zy.asrs.mapper.BasDevpMapper; |
| | | import com.zy.asrs.mapper.BasStationMapper; |
| | | import com.zy.asrs.mapper.WrkMastMapper; |
| | | import com.zy.asrs.service.ApiLogService; |
| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.*; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | |
| | | private BasStationMapper basStationMapper; |
| | | |
| | | @Resource |
| | | private BasDevpMapper basDevpMapper; |
| | | |
| | | @Resource |
| | | private AgvProperties agvProperties; |
| | | |
| | | /** |
| | | * 站点轮询计数器,用于平均分配站点 |
| | | * Key: 站点组标识(如 "east" 或 "west"),Value: 当前轮询索引 |
| | | */ |
| | | private final Map<String, AtomicInteger> siteRoundRobinCounters = new ConcurrentHashMap<>(); |
| | | |
| | | /** |
| | | * 呼叫agv搬运 |
| | |
| | | } |
| | | |
| | | for (Task task : taskList) { |
| | | // 如果任务没有分配站点,先分配站点 |
| | | String staNo = task.getStaNo(); |
| | | if (staNo == null || staNo.isEmpty()) { |
| | | Integer allocatedSite = allocateSiteForTask(task); |
| | | if (allocatedSite == null) { |
| | | log.warn("任务ID:{}无法分配站点,跳过本次发送", task.getId()); |
| | | continue; // 无法分配站点,跳过本次发送 |
| | | } |
| | | staNo = String.valueOf(allocatedSite); |
| | | task.setStaNo(staNo); |
| | | taskService.updateById(task); |
| | | log.info("任务ID:{}已分配站点:{}", task.getId(), staNo); |
| | | } |
| | | |
| | | // 检查目标站点是否有未完成的AGV任务 |
| | | // 如果站点有未完成的任务,则跳过本次发送,等待下次 |
| | | if (staNo != null && !staNo.isEmpty()) { |
| | | List<Task> unfinishedTasks = taskService.selectList(new EntityWrapper<Task>() |
| | | .eq("sta_no", staNo) |
| | | .eq("task_type", "agv") |
| | | .ne("id", task.getId()) // 排除当前任务本身 |
| | | .last("AND wrk_sts NOT IN (5, 15)") // 排除已完成状态 |
| | | ); |
| | | |
| | | if (!unfinishedTasks.isEmpty()) { |
| | | log.info("站点{}有{}个未完成的AGV任务,跳过本次发送,等待任务完成。当前任务ID:{}", |
| | | staNo, unfinishedTasks.size(), task.getId()); |
| | | continue; // 跳过本次发送,等待下次 |
| | | } |
| | | } |
| | | |
| | | // 呼叫agv |
| | | String response = ""; |
| | | boolean success = false; |
| | |
| | | default: |
| | | } |
| | | String body = getRequest(task,namespace); |
| | | // 打印请求信息 |
| | | log.info("{}呼叫agv搬运 - 请求地址:{}", namespace, url); |
| | | log.info("{}呼叫agv搬运 - 请求参数:{}", namespace, body); |
| | | try { |
| | | // 使用仙工M4接口 |
| | | response = new HttpHandler.Builder() |
| | |
| | | .setJson(body) |
| | | .build() |
| | | .doPost(); |
| | | // 打印返回参数 |
| | | log.info("{}呼叫agv搬运 - 返回参数:{}", namespace, response); |
| | | |
| | | // 检查响应是否为空 |
| | | if (response == null || response.trim().isEmpty()) { |
| | | log.error("{}呼叫agv搬运失败 - 任务ID:{},AGV接口返回为空", namespace, task.getId()); |
| | | continue; |
| | | } |
| | | |
| | | JSONObject jsonObject = JSON.parseObject(response); |
| | | if (jsonObject.getInteger("code").equals(200)) { |
| | | if (jsonObject == null) { |
| | | log.error("{}呼叫agv搬运失败 - 任务ID:{},响应JSON解析失败,响应内容:{}", namespace, task.getId(), response); |
| | | continue; |
| | | } |
| | | |
| | | Integer code = jsonObject.getInteger("code"); |
| | | if (code != null && code.equals(200)) { |
| | | success = true; |
| | | task.setWrkSts(8L); |
| | | taskService.updateById(task); |
| | | log.info(namespace + "呼叫agv搬运请求参数:{}", body); |
| | | log.info(namespace + "呼叫agv搬运成功:{}", task.getId()); |
| | | log.info("{}呼叫agv搬运成功 - 任务ID:{}", namespace, task.getId()); |
| | | } else { |
| | | log.error(namespace + "呼叫agv搬运失败!!!url:{};request:{};response:{}", url, body, response); |
| | | String message = jsonObject.getString("message"); |
| | | log.error("{}呼叫agv搬运失败 - 任务ID:{},错误码:{},错误信息:{}", |
| | | namespace, task.getId(), code, message); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error(namespace + "呼叫agv搬运异常", e); |
| | | log.error("{}呼叫agv搬运异常 - 任务ID:{},请求地址:{},请求参数:{},异常信息:{}", |
| | | namespace, task.getId(), url, body, e.getMessage(), e); |
| | | } finally { |
| | | try { |
| | | // 保存接口日志 |
| | |
| | | } |
| | | |
| | | /** |
| | | * 为任务分配站点(定时任务中调用) |
| | | * @param task 任务对象 |
| | | * @return 分配的站点编号,如果无法分配则返回null |
| | | */ |
| | | private Integer allocateSiteForTask(Task task) { |
| | | // 根据任务的invWh(机器人组)判断是东侧还是西侧 |
| | | String robotGroup = task.getInvWh(); |
| | | List<String> targetStations; |
| | | String groupKey; |
| | | |
| | | if (robotGroup != null && robotGroup.equals(agvProperties.getRobotGroupEast())) { |
| | | // 东侧站点 |
| | | targetStations = agvProperties.getEastStations(); |
| | | groupKey = "east"; |
| | | } else if (robotGroup != null && robotGroup.equals(agvProperties.getRobotGroupWest())) { |
| | | // 西侧站点 |
| | | targetStations = agvProperties.getWestStations(); |
| | | groupKey = "west"; |
| | | } else { |
| | | // 默认使用东侧 |
| | | targetStations = agvProperties.getEastStations(); |
| | | groupKey = "east"; |
| | | log.warn("任务ID:{}的机器人组{}未识别,使用默认东侧站点", task.getId(), robotGroup); |
| | | } |
| | | |
| | | if (targetStations.isEmpty()) { |
| | | log.warn("任务ID:{}没有可用的目标站点配置", task.getId()); |
| | | return null; |
| | | } |
| | | |
| | | // 将站点字符串列表转换为整数列表 |
| | | List<Integer> siteIntList = targetStations.stream() |
| | | .map(Integer::parseInt) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 判断能入站点(in_enable="Y"表示能入) |
| | | List<Integer> sites = basDevpMapper.selectList( |
| | | new EntityWrapper<BasDevp>() |
| | | .eq("in_enable", "Y") |
| | | .in("dev_no", siteIntList) |
| | | ).stream().map(BasDevp::getDevNo).collect(Collectors.toList()); |
| | | |
| | | if (sites.isEmpty()) { |
| | | log.warn("任务ID:{}没有能入站点", task.getId()); |
| | | return null; |
| | | } |
| | | |
| | | // 获取没有出库任务的站点 |
| | | List<Integer> canInSites = basDevpMapper.getCanInSites(sites); |
| | | if (canInSites.isEmpty()) { |
| | | log.warn("任务ID:{}没有可入站点(请等待出库完成)", task.getId()); |
| | | return null; |
| | | } |
| | | |
| | | // 寻找入库任务最少的站点(且必须in_enable="Y"能入 和 canining="Y"可入) |
| | | List<BasDevp> devList = basDevpMapper.selectList(new EntityWrapper<BasDevp>() |
| | | .in("dev_no", canInSites) |
| | | .eq("in_enable", "Y") |
| | | .eq("canining", "Y") |
| | | ); |
| | | |
| | | if (devList.isEmpty()) { |
| | | log.warn("任务ID:{}没有可入站点(in_enable='Y'且canining='Y')", task.getId()); |
| | | return null; |
| | | } |
| | | |
| | | // 入库任务数排序 |
| | | devList.sort(Comparator.comparing(BasDevp::getInQty)); |
| | | |
| | | // 选择站点 |
| | | BasDevp basDevp; |
| | | 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(); |
| | | |
| | | if (minTaskSites.size() > 1 && enableRoundRobin && "round-robin".equals(strategy)) { |
| | | // 轮询分配 |
| | | AtomicInteger counter = siteRoundRobinCounters.computeIfAbsent(groupKey, k -> new AtomicInteger(0)); |
| | | int index = counter.getAndIncrement() % minTaskSites.size(); |
| | | basDevp = minTaskSites.get(index); |
| | | log.info("使用轮询分配策略,站点组:{},轮询索引:{},选中站点:{}", groupKey, index, basDevp.getDevNo()); |
| | | } else if (minTaskSites.size() > 1 && enableRoundRobin && "random".equals(strategy)) { |
| | | // 随机分配 |
| | | Random random = new Random(); |
| | | int index = random.nextInt(minTaskSites.size()); |
| | | basDevp = minTaskSites.get(index); |
| | | log.info("使用随机分配策略,选中站点:{}", basDevp.getDevNo()); |
| | | } else { |
| | | // 默认:选择第一个(任务最少的) |
| | | basDevp = devList.get(0); |
| | | } |
| | | |
| | | Integer endSite = basDevp.getDevNo(); |
| | | |
| | | // 入库暂存+1 |
| | | basDevpMapper.incrementInQty(endSite); |
| | | |
| | | log.info("任务ID:{}已分配站点:{}", task.getId(), endSite); |
| | | return endSite; |
| | | } |
| | | |
| | | /** |
| | | * 根据站点编号判断机器人组 |
| | | * @param staNo 站点编号 |
| | | * @return 机器人组名称 |