package com.zy.asrs.wcs.core.utils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.zy.asrs.framework.common.Cools; import com.zy.asrs.framework.common.SnowflakeIdWorker; import com.zy.asrs.framework.exception.CoolException; import com.zy.asrs.wcs.core.entity.*; import com.zy.asrs.wcs.core.kernel.AnalyzeService; import com.zy.asrs.wcs.core.model.NavigateNode; import com.zy.asrs.wcs.core.model.enums.DeviceCtgType; import com.zy.asrs.wcs.core.model.enums.LiftCodeType; import com.zy.asrs.wcs.core.model.enums.NavigationMapType; import com.zy.asrs.wcs.core.model.enums.TaskStsType; import com.zy.asrs.wcs.core.service.*; import com.zy.asrs.wcs.rcs.News; import com.zy.asrs.wcs.rcs.cache.SlaveConnection; import com.zy.asrs.wcs.rcs.entity.Device; import com.zy.asrs.wcs.rcs.model.enums.SlaveType; import com.zy.asrs.wcs.rcs.model.protocol.ShuttleProtocol; import com.zy.asrs.wcs.rcs.service.DeviceService; import com.zy.asrs.wcs.rcs.thread.ShuttleThread; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; /** * Created by vincent on 2023/10/12 */ @Service public class ShuttleDispatcher { private static final Integer INF = 9999999; private static final Integer WEIGHT = 1000000; @Autowired private TaskService taskService; @Autowired private LiftDispatcher liftDispatcher; @Autowired private SnowflakeIdWorker snowflakeIdWorker; @Autowired private AnalyzeService analyzeService; @Autowired private MotionService motionService; @Autowired private DeviceService deviceService; @Autowired private BasShuttleService basShuttleService; @Autowired private TaskCtgService taskCtgService; @Autowired private ShuttleStandbyService shuttleStandbyService; public ShuttleThread queryShuttleWhichConvenient(Task task, Integer liftNo) { String locNo = taskService.judgeInbound(task) ? task.getDestLoc() : task.getOriginLoc(); ShuttleThread resThread = null; Integer finalDistance = ShuttleDispatcher.INF; List list = deviceService.list(new LambdaQueryWrapper() .eq(Device::getDeviceType, DeviceCtgType.SHUTTLE.val()) .eq(Device::getHostId, task.getHostId()) .eq(Device::getStatus, 1)); for (Device device : list) { if (taskService.hasBusyOutboundByShuttle(Integer.parseInt(device.getDeviceNo()))) { continue; } //获取四向穿梭车线程 ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getId().intValue()); ShuttleProtocol shuttleProtocol = shuttleThread.getStatus(); if (shuttleProtocol == null || shuttleProtocol.getShuttleNo() == null) { continue; } if (!shuttleThread.isIdle()) { continue; } //检测是否存在充电任务 Task taskCharge = taskService.selectChargeWorking(Integer.valueOf(device.getDeviceNo())); if (taskCharge != null) { continue; } // 有没有被其他任务调度 int currentLev = Utils.getLev(shuttleProtocol.getCurrentLocNo());//小车当前层高 String currentLocNo = shuttleProtocol.getCurrentLocNo();//小车当前库位号 if (currentLocNo.equals(locNo)) { resThread = shuttleThread; break; } String targetLocNo = LiftCodeType.getStandbyLocNo(liftNo, currentLev);//默认到提升机待机位 // 同楼层直接计算到目标库位 if (currentLev == Utils.getLev(locNo)) { targetLocNo = locNo; } //当前穿梭车线程到当前车子所在楼层的提升机口距离 List currentShuttlePath = NavigateUtils.calc( currentLocNo , targetLocNo , NavigationMapType.NORMAL.id , Utils.getShuttlePoints(Integer.parseInt(shuttleThread.getDevice().getDeviceNo()), currentLev) );//搜索空闲穿梭车,使用正常通道地图 if (currentShuttlePath == null) { continue; } Integer currDistance = NavigateUtils.getOriginPathAllDistance(currentShuttlePath);//计算当前路径行走总距离 // 不同楼层权重 if (currentLev != Utils.getLev(locNo)) { currDistance += WEIGHT; } // 挂载任务权重 List tasks = taskService.selectWorkingByShuttle(Integer.valueOf(device.getDeviceNo())); if (!Cools.isEmpty(tasks)) { currDistance += tasks.size() * WEIGHT; } if (currDistance < finalDistance) { finalDistance = currDistance; resThread = shuttleThread; } } return resThread; } public synchronized ShuttleThread searchIdleShuttle(Task task) { String locNo = taskService.judgeInbound(task) ? task.getDestLoc() : task.getOriginLoc(); ShuttleThread resThread = null; Integer finalDistance = ShuttleDispatcher.INF; List list = deviceService.list(new LambdaQueryWrapper() .eq(Device::getDeviceType, DeviceCtgType.SHUTTLE.val()) .eq(Device::getHostId, task.getHostId()) .eq(Device::getStatus, 1)); for (Device device : list) { if (taskService.hasBusyOutboundByShuttle(Integer.parseInt(device.getDeviceNo()))) { continue; } //获取四向穿梭车线程 ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getId().intValue()); ShuttleProtocol shuttleProtocol = shuttleThread.getStatus(); if (shuttleProtocol == null || shuttleProtocol.getShuttleNo() == null) { continue; } if (!shuttleThread.isIdle()) { continue; } //检测是否存在充电任务 Task taskCharge = taskService.selectChargeWorking(Integer.valueOf(device.getDeviceNo())); if (taskCharge != null) { continue; } // 有没有被其他任务调度 int currentLev = Utils.getLev(shuttleProtocol.getCurrentLocNo());//小车当前层高 String currentLocNo = shuttleProtocol.getCurrentLocNo();//小车当前库位号 if (currentLocNo.equals(locNo)) { resThread = shuttleThread; break; } String targetLocNo = null;//默认到提升机待机位 // 同楼层直接计算到目标库位 if (currentLev == Utils.getLev(locNo)) { targetLocNo = locNo; }else { Device recentTransferLift = Utils.getRecentTransferLift(locNo, Integer.parseInt(device.getDeviceNo())); if (recentTransferLift == null) { continue; } //获取小车楼层提升机待机位 ShuttleStandby shuttleStandby = shuttleStandbyService.getOne(new LambdaQueryWrapper() .eq(ShuttleStandby::getDeviceId, recentTransferLift.getId()) .eq(ShuttleStandby::getDeviceLev, currentLev) .eq(ShuttleStandby::getStatus, 1)); targetLocNo = shuttleStandby.getDeviceLoc(); } //当前穿梭车线程到当前车子所在楼层的提升机口距离 List currentShuttlePath = NavigateUtils.calc( currentLocNo , targetLocNo , NavigationMapType.NORMAL.id , Utils.getShuttlePoints(Integer.parseInt(shuttleThread.getDevice().getDeviceNo()), currentLev) );//搜索空闲穿梭车,使用正常通道地图 if (currentShuttlePath == null) { continue; } Integer currDistance = NavigateUtils.getOriginPathAllDistance(currentShuttlePath);//计算当前路径行走总距离 // 不同楼层权重 if (currentLev != Utils.getLev(locNo)) { currDistance += WEIGHT; } // 挂载任务权重 List tasks = taskService.selectWorkingByShuttle(Integer.valueOf(device.getDeviceNo())); if (!Cools.isEmpty(tasks)) { currDistance += tasks.size() * WEIGHT; } if (currDistance < finalDistance) { finalDistance = currDistance; resThread = shuttleThread; } } return resThread; } //生成迁移任务 public synchronized Task generateMoveTask(Device device, String locNo) { // 已有迁移任务 if (taskService.selectMoveWorking(Integer.valueOf(device.getDeviceNo())) != null) { return null; } //获取迁移任务类型 TaskCtg taskCtg = taskCtgService.getOne(new LambdaQueryWrapper() .eq(TaskCtg::getFlag, "MOVE") .eq(TaskCtg::getStatus, 1)); if (taskCtg == null) { return null; } ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getId().intValue()); if (shuttleThread == null) { return null; } //获取避让位置 String standByLocNo = this.searchStandByLocNo(Integer.valueOf(device.getDeviceNo()), device.getHostId(), shuttleThread.getStatus().getCurrentLocNo()); Task task = new Task(); task.setUuid(String.valueOf(snowflakeIdWorker.nextId())); task.setTaskNo(String.valueOf(Utils.getTaskNo("MOVE"))); task.setTaskSts(TaskStsType.NEW_MOVE.sts); task.setTaskCtg(taskCtg.getId()); task.setPriority(10); task.setOriginSite(null); task.setOriginLoc(null); task.setDestSite(null); task.setDestLoc(standByLocNo); // 避让位置 task.setIoTime(new Date()); task.setStartTime(new Date()); task.setHostId(device.getHostId()); task.setStatus(1); task.setShuttleNo(Integer.valueOf(device.getDeviceNo())); // generate motion list List motionList = analyzeService.generateShuttleMoveMotion(task); if (Cools.isEmpty(motionList)) { News.error("保存{}号四向穿梭车迁移任务失败!!!", device.getDeviceNo()); return null; } motionService.batchInsert(motionList, task.getUuid(), Integer.valueOf(task.getTaskNo())); task.setTaskSts(TaskStsType.ANALYZE_MOVE.sts); if (!taskService.save(task)) { News.error("保存{}号四向穿梭车迁移任务失败!!!", device.getDeviceNo()); return null; } return task; } /** * 搜索避让库位,通过小车号和目标库位 */ public String searchStandByLocNo(Integer shuttleNo, Long hostId, String locNo) { BasShuttle basShuttle = basShuttleService.getOne(new LambdaQueryWrapper() .eq(BasShuttle::getShuttleNo, shuttleNo) .eq(BasShuttle::getHostId, hostId)); if (basShuttle == null) { throw new CoolException("小车基础数据不存在"); } String idleLoc = basShuttle.getIdleLoc(); if (Cools.isEmpty(idleLoc)) { throw new CoolException("小车避让数据不存在"); } int lev = Utils.getLev(locNo);//当前楼层 JSONArray standbyLoc = JSON.parseArray(idleLoc); if (lev > standbyLoc.size()) { throw new CoolException("避让数据异常"); } Object object = standbyLoc.get(lev - 1); List locs = JSON.parseArray(object.toString(), String.class); if (locs.isEmpty()) { throw new CoolException("避让数据为空"); } Integer finalDistance = ShuttleDispatcher.INF; String recentLoc = null; for (String loc : locs) { //当前穿梭车到避让位计算 List currentShuttlePath = NavigateUtils.calc( locNo , loc , NavigationMapType.NORMAL.id , Utils.getShuttlePoints(shuttleNo, lev) );//使用正常通道地图 if (currentShuttlePath == null) { continue; } Integer currDistance = NavigateUtils.getOriginPathAllDistance(currentShuttlePath);//计算当前路径行走总距离 if (currDistance < finalDistance) { finalDistance = currDistance; recentLoc = loc; } } if (recentLoc == null) { throw new CoolException("搜索避让位置失败"); } return recentLoc; } }