From 3b5f168b457908e466841e1d70acc55c67d9f7d7 Mon Sep 17 00:00:00 2001 From: zgw <13623675548@163.com> Date: Thu, 15 Jan 2026 08:49:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=BF=87=E7=A8=8B=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ent Collision Avoidance Logic for SRGVs.md | 22 ++ .../bas/srgvmap/service/RoadCacheService.java | 2 + .../service/impl/RoadCacheServiceImpl.java | 24 +- .../com/ruoyi/schedule/ScheduleThread.java | 20 +- .../schedule/plcRead/cdz/PlcCdzRead.java | 5 +- .../schedule/scheduleTask/cdz/CdzThread.java | 43 ++- .../scheduleTask/monit/MonitoringThread.java | 8 +- .../scheduleTask/srgv/BaseSRgvThread.java | 302 ++++++++++++++++-- .../mapper/device/DeviceCdzMapper.xml | 4 +- 9 files changed, 376 insertions(+), 54 deletions(-) create mode 100644 .trae/documents/Implement Collision Avoidance Logic for SRGVs.md diff --git a/.trae/documents/Implement Collision Avoidance Logic for SRGVs.md b/.trae/documents/Implement Collision Avoidance Logic for SRGVs.md new file mode 100644 index 0000000..bd74b25 --- /dev/null +++ b/.trae/documents/Implement Collision Avoidance Logic for SRGVs.md @@ -0,0 +1,22 @@ +I will implement collision avoidance logic in `BaseSRgvThread.java` to handle scenarios where a path is blocked by another SRGV. + +**Implementation Plan:** +1. **Modify `sendPointToPointTask` method in `BaseSRgvThread.java`**: + * Locate the block where `shortestPath` is null or empty (indicating a blocked path). + * After the existing specific logic for "Floor 1", add a generic avoidance strategy. + * **Avoidance Strategy**: + 1. Query `WcsSrgvMap` for all sites with `remark="cache"` (avoidance points) on the current floor. + 2. Filter out sites that are currently occupied by other SRGVs. + 3. Find the nearest reachable avoidance point to the current car. + 4. **Task Splitting (Yielding Logic)**: + * **Create a new "Continuation" Queue**: Clone the current task but set its `StartSite` to the avoidance point and keep the original `EndSite`. Insert this as a new pending task. + * **Update Current Queue**: Change the `EndSite` of the current blocked task to the **Avoidance Point**. This effectively converts the current task into a "Yielding" task. + 5. Return `false` to allow the scheduler to pick up the modified task in the next cycle. + +**Technical Details:** +* Use `wcsSrgvMapService` to fetch avoidance points. +* Use `PathFinder` to verify reachability of the avoidance point. +* Use `BeanUtil` (Hutool) or manual setters to clone the queue. +* Ensure the new logic does not interfere with the existing "Floor 1" logic (put it after or as an `else` condition). + +This approach ensures that when blocked, the car moves to a safe spot (Yielding Queue) and then resumes its original journey (Continuation Queue). \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/srgvmap/service/RoadCacheService.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/srgvmap/service/RoadCacheService.java index 419543a..37941b1 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/bas/srgvmap/service/RoadCacheService.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/srgvmap/service/RoadCacheService.java @@ -23,6 +23,8 @@ public interface RoadCacheService { * @return */ int[][] handleMap(Map arrByRoadSite, DeviceSrgv deviceSrgv); + + int[][] handleMap1(Map arrByRoadSite, DeviceSrgv deviceSrgv); /** * 根据x y z 获取指定位置的缓存 * @param x diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/srgvmap/service/impl/RoadCacheServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/srgvmap/service/impl/RoadCacheServiceImpl.java index 3c8d73e..c258c98 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/bas/srgvmap/service/impl/RoadCacheServiceImpl.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/srgvmap/service/impl/RoadCacheServiceImpl.java @@ -61,7 +61,25 @@ public class RoadCacheServiceImpl implements RoadCacheService { //3.锁定离线小车当前位置 lockedOfflineCarCurrSite(maps, deviceSrgv); - int[][] mapsInt = new int[29][15]; + int[][] mapsInt = new int[29][16]; + for (String site : maps.keySet()) { + WcsSrgvMap wcsSrgvMap = maps.get(site); + mapsInt[wcsSrgvMap.getX()][wcsSrgvMap.getY()] = wcsSrgvMap.getValue(); + } + return mapsInt; + } + + @Override + public int[][] handleMap1(Map maps, DeviceSrgv deviceSrgv) { + +// //1.锁定其他车任务路径 +// lockedOtherCarQueuePath(maps, deviceSrgv); +// //2.锁定无任务小车当前位置 +// lockedOtherCarCurrSite(maps, deviceSrgv); + //3.锁定离线小车当前位置 +// lockedOfflineCarCurrSite(maps, deviceSrgv); + + int[][] mapsInt = new int[29][16]; for (String site : maps.keySet()) { WcsSrgvMap wcsSrgvMap = maps.get(site); mapsInt[wcsSrgvMap.getX()][wcsSrgvMap.getY()] = wcsSrgvMap.getValue(); @@ -141,7 +159,9 @@ public class RoadCacheServiceImpl implements RoadCacheService { //添加其他小车路径列表 HashSet otherCarSiteList = new HashSet<>(); for (WcsSrgvQueue wcsSrgvQueue : otherCarQueue) { - otherCarSiteList.addAll(Arrays.asList(wcsSrgvQueue.getPathInfo().split(","))); + if (wcsSrgvQueue.getPathInfo()!=null) { + otherCarSiteList.addAll(Arrays.asList(wcsSrgvQueue.getPathInfo().split(","))); + } } //锁定其他小车路径 if (CollUtil.isNotEmpty(otherCarSiteList)) { diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/ScheduleThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/ScheduleThread.java index b4c372d..39a1f7f 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/schedule/ScheduleThread.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/ScheduleThread.java @@ -149,14 +149,14 @@ public class ScheduleThread { plcSsjReadCustomThread = new CustomThread(plcSsjReadThread, plcSsjRead); //plc充电桩读取线程 -// ConcurrentHashMap cdzList = new ConcurrentHashMap<>(); -// List deviceCdzList = deviceCdzService.selectDeviceCdzList(new DeviceCdz()); -// for (DeviceCdz deviceCdz : deviceCdzList) { -// cdzList.put(String.valueOf(deviceCdz.getId()), deviceCdz); -// } -// PlcCdzRead plcCdzRead = new PlcCdzRead(cdzList, new PlcConnectInfo("192.168.10.100", 502)); -// Thread plcCdzReadThread = new Thread(plcCdzRead, "plcCdzjReadCustomThread"); -// plcCdzjReadCustomThread = new CustomThread(plcCdzReadThread, plcCdzRead); + ConcurrentHashMap cdzList = new ConcurrentHashMap<>(); + List deviceCdzList = deviceCdzService.selectDeviceCdzList(new DeviceCdz()); + for (DeviceCdz deviceCdz : deviceCdzList) { + cdzList.put(String.valueOf(deviceCdz.getId()), deviceCdz); + } + PlcCdzRead plcCdzRead = new PlcCdzRead(cdzList, new PlcConnectInfo("192.168.10.100", 502)); + Thread plcCdzReadThread = new Thread(plcCdzRead, "plcCdzjReadCustomThread"); + plcCdzjReadCustomThread = new CustomThread(plcCdzReadThread, plcCdzRead); ConcurrentHashMap tsjList = new ConcurrentHashMap<>(); @@ -197,7 +197,7 @@ public class ScheduleThread { public void refreshThreadMap() { registerThread("plcSsjReadCustomThread", plcSsjReadCustomThread, "PLC输送机读取线程", "read"); -// registerThread("plcCdzjReadCustomThread", plcCdzjReadCustomThread, "PLC充电桩读取线程", "read"); + registerThread("plcCdzjReadCustomThread", plcCdzjReadCustomThread, "PLC充电桩读取线程", "read"); registerThread("plcTsjReadCustomThread", plcTsjReadCustomThread, "PLC提升机读取线程", "read"); registerThread("plcSrgvReadCustomThread1", plcSrgvReadCustomThread1, "PLC四向车1读取线程", "read"); registerThread("plcSrgvReadCustomThread2", plcSrgvReadCustomThread2, "PLC四向车2读取线程", "read"); @@ -226,7 +226,7 @@ public class ScheduleThread { plcTsjReadCustomThread.getThread().start(); monitoringCustomThread.getThread().start(); //充电桩读取线程 -// plcCdzjReadCustomThread.getThread().start(); + plcCdzjReadCustomThread.getThread().start(); isRun = true; } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/cdz/PlcCdzRead.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/cdz/PlcCdzRead.java index 41f7285..143f546 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/cdz/PlcCdzRead.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/cdz/PlcCdzRead.java @@ -90,8 +90,11 @@ public class PlcCdzRead implements Runnable { if (read1.IsSuccess) { cdzDevice.setModel(Convert.toInt(byteTransform.TransInt16(read1.Content, 0))); cdzDevice.setStatus(Convert.toInt(byteTransform.TransInt16(read1.Content, 2))); + if (cdzDevice.getStatus()==0){ + cdzDevice.setSpare1("0"); + } log.info("=====读取到充电桩充电模式:{}======", Convert.toInt(byteTransform.TransInt16(read1.Content, 0))); - log.info("=====读取到充电桩充电状态:{}======", Convert.toInt(byteTransform.TransInt16(read1.Content, 0))); + log.info("=====读取到充电桩充电状态:{}======", Convert.toInt(byteTransform.TransInt16(read1.Content, 2))); } } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/cdz/CdzThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/cdz/CdzThread.java index 2080e84..58791e2 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/cdz/CdzThread.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/cdz/CdzThread.java @@ -61,7 +61,7 @@ public class CdzThread implements Runnable { void dispatch() { - + SrgvChargingJob srgvChargingJobParam = new SrgvChargingJob(); //获取低电量小车 List littleBatterySRgv = new ArrayList<>(); //获取中电量小车 @@ -98,7 +98,6 @@ public class CdzThread implements Runnable { //充电任务下发 - SrgvChargingJob srgvChargingJobParam = new SrgvChargingJob(); srgvChargingJobParam.setChargingStatus("1"); srgvChargingJobParam.setJobType("1"); srgvChargingJobParam.setJobStatus("4"); @@ -145,6 +144,24 @@ public class CdzThread implements Runnable { } } } + //充电桩关闭 完成充电任务 + DeviceCdz deviceCdz = deviceCdzService.selectDeviceCdzById(1L); + if (deviceCdz.getStatus()==0){ + SrgvChargingJob srgvChargingJobDdParam = new SrgvChargingJob(); + srgvChargingJobDdParam.setChargingStatus("2"); + srgvChargingJobDdParam.setJobStatus("4"); + List srgvChargingJobDds = chargingJobService.selectSrgvChargingJobList(srgvChargingJobDdParam); + if (CollUtil.isNotEmpty(srgvChargingJobDds)) { + for (SrgvChargingJob job :srgvChargingJobDds){ + job.setChargingStatus("1"); + job.setChargingDate(DateUtil.formatDateTime(DateUtil.date())); + chargingJobService.updateSrgvChargingJob(job); + //插入历史 + chargingJobService.insertHistory(job.getGuid()); + chargingJobService.deleteSrgvChargingJobByGuid(job.getGuid()); + } + } + } //断电任务下发 @@ -191,19 +208,19 @@ public class CdzThread implements Runnable { if (CollUtil.isNotEmpty(cdzList)) { //寻找最接近的充电桩 Integer floor = deviceSrgv.getCurrFloor(); - DeviceCdz cdz = null; - Optional min = cdzList.stream().filter(c -> c.getSzcs().equals(floor)).min((o1, o2) -> { - Integer distance1 = Math.abs(o1.getSzcs()); - Integer distance2 = Math.abs(o2.getSzcs()); - return distance1 - distance2; - }); - if (min.isPresent()) { - cdz = min.get(); - } + DeviceCdz cdz = cdzList.get(0); +// Optional min = cdzList.stream().filter(c -> c.getSzcs().equals(floor)).min((o1, o2) -> { +// Integer distance1 = Math.abs(o1.getSzcs()); +// Integer distance2 = Math.abs(o2.getSzcs()); +// return distance1 - distance2; +// }); +// if (min.isPresent()) { +// cdz = min.get(); +// } if (ObjUtil.isNotNull(cdz)) { //生成充电任务 SrgvChargingJob srgvChargingJobParam = new SrgvChargingJob(); - srgvChargingJobParam.setAgvId(deviceSrgv.getDeviceId().toString()); +// srgvChargingJobParam.setAgvId(deviceSrgv.getDeviceId().toString()); srgvChargingJobParam.setChargingSite(cdz.getSiteId()); srgvChargingJobParam.setChargingId(cdz.getId().toString()); List wcsChargingJobs = chargingJobService.selectSrgvChargingJobList(srgvChargingJobParam); @@ -231,7 +248,7 @@ public class CdzThread implements Runnable { } /** - * 创建充电任务 + * 创建断电任务 * */ private void createChargingOutJob(DeviceSrgv deviceSrgv, DeviceCdz cdz) { diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/monit/MonitoringThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/monit/MonitoringThread.java index 53b19ed..8f0be4c 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/monit/MonitoringThread.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/monit/MonitoringThread.java @@ -44,10 +44,10 @@ public class MonitoringThread implements Runnable { deviceSsjService.updateDeviceSsj(deviceSsj); } -// for (String cdzName : PlcCdzRead.deviceCdzMap.keySet()) { -// DeviceCdz deviceCdz = PlcCdzRead.deviceCdzMap.get(cdzName); -// deviceCdzService.updateDeviceCdz(deviceCdz); -// } + for (String cdzName : PlcCdzRead.deviceCdzMap.keySet()) { + DeviceCdz deviceCdz = PlcCdzRead.deviceCdzMap.get(cdzName); + deviceCdzService.updateDeviceCdz(deviceCdz); + } for (String sRgv : PlcSrgvRead.deviceSrgvMap.keySet()) { deviceSrgvService.updateDeviceSrgv(PlcSrgvRead.deviceSrgvMap.get(sRgv)); diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/srgv/BaseSRgvThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/srgv/BaseSRgvThread.java index 40217b6..eabb8f3 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/srgv/BaseSRgvThread.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/srgv/BaseSRgvThread.java @@ -3,6 +3,7 @@ package com.ruoyi.schedule.scheduleTask.srgv; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import com.ruoyi.bas.port.service.IWcsPortService; @@ -72,6 +73,7 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { public JobCallBackServiceImpl jobCallBackService; + private int taskCache = 450001; public void init() { sRgvCodeList = new HashMap<>(); @@ -149,8 +151,8 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { //下发断电任务 // int i = PlcCdzRead.writeToCdz("2"); // if (i == 1) { - srgvChargingJobMapper.insertHistory(srgvChargingJob.getGuid()); - srgvChargingJobMapper.deleteSrgvChargingJobByGuid(srgvChargingJob.getGuid()); + srgvChargingJobMapper.insertHistory(srgvChargingJob.getGuid()); + srgvChargingJobMapper.deleteSrgvChargingJobByGuid(srgvChargingJob.getGuid()); // } } } @@ -178,7 +180,7 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { String chargingId = srgvChargingJob.getChargingId(); DeviceSrgv srgv = PlcSrgvRead.deviceSrgvMap.get(agvId); // DeviceCdz cdz = PlcCdzRead.deviceCdzMap.get(chargingId); - if (srgv.getCurrFloor()==1) { + if (srgv.getCurrFloor() == 1) { //生成四向小车充电队列 String chargingSite = srgvChargingJob.getChargingSite();//充电桩站点 WcsSrgvQueue wcsSrgvQueueParam = new WcsSrgvQueue(); @@ -198,8 +200,8 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { List wcsSrgvQueues = wcsSagvQueueMapper.selectWcsSrgvQueueList(wcsSrgvQueueParam); if (CollUtil.isEmpty(wcsSrgvQueues)) { createSrgvChargingQueue(deviceSrgv, chargingSite, "8", srgvChargingJob.getTaskNo()); - srgvChargingJob.setJobStatus("2"); - srgvChargingJobMapper.updateSrgvChargingJob(srgvChargingJob); +// srgvChargingJob.setJobStatus("2"); +// srgvChargingJobMapper.updateSrgvChargingJob(srgvChargingJob); } } @@ -212,7 +214,7 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { WcsSrgvQueue wcsSrgvQueue = new WcsSrgvQueue(); wcsSrgvQueue.setGuid(UUID.randomUUID().toString()); wcsSrgvQueue.setTaskNo(taskNo); - wcsSrgvQueue.setTaskType("3"); + wcsSrgvQueue.setTaskType(type); wcsSrgvQueue.setPriority(Long.parseLong("2")); wcsSrgvQueue.setCargo("0"); wcsSrgvQueue.setIssued("0"); @@ -223,14 +225,15 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { WcsSrgvMap wcsSrgvMap = wcsSrgvMapService.selectWcsSrgvMapBySiteId(chargingSite); if ("8".equals(type)) { wcsSrgvQueue.setEndSite("tsj"); + wcsSrgvQueue.setDefault03("040601"); } else if ("3".equals(type)) { wcsSrgvQueue.setEndSite(chargingSite); + wcsSrgvQueue.setDefault03(wcsSrgvQueue.getStartSite()); } wcsSrgvQueue.setTheFloor(deviceSrgv.getCurrFloor().toString()); wcsSrgvQueue.setCreateDate(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); wcsSrgvQueue.setAgvId(deviceSrgv.getDeviceId().toString()); wcsSrgvQueue.setDefault02("3"); - wcsSrgvQueue.setDefault03(wcsSrgvQueue.getStartSite()); wcsSrgvQueue.setDefault07(Convert.toStr(wcsSrgvMap.getZ())); wcsSagvQueueMapper.insertWcsSrgvQueue(wcsSrgvQueue); } @@ -255,7 +258,7 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { * 生成入库小车队列 */ private void createCarQueue(DeviceSrgv deviceSrgv) { - + //充电队列处理 if (deviceSrgv.getWorkMode() == 5 && deviceSrgv.getStatus() == 1) { WcsSrgvQueue wcsSrgvQueue = wcsSagvQueueMapper.selectWcsSrgvQueueByAgvId(deviceSrgv.getDeviceId().toString()); if (wcsSrgvQueue == null) { @@ -331,13 +334,18 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { } else { if ("1".equals(wcsSrgvQueue.getIssued())) { + if (wcsSrgvQueue.getDefault08()!=null&&wcsSrgvQueue.getDefault08().equals("1")){ + if (deviceSrgv.getCurrSite().equals(wcsSrgvQueue.getEndSite()) && "4".equals(wcsSrgvQueue.getTaskType())){ + wcsSagvQueueMapper.insertHistory(wcsSrgvQueue.getTaskNo()); + wcsSagvQueueMapper.deleteWcsSrgvQueueByGuid(wcsSrgvQueue.getGuid()); + } + } MydoJob mydoJob = mydoJobService.selectMydoJobByTaskNo(Convert.toLong(wcsSrgvQueue.getTaskNo())); if (mydoJob != null) { if (deviceSrgv.getCurrSite().equals(wcsSrgvQueue.getEndSite()) && "1".equals(wcsSrgvQueue.getTaskType())) { //TODO 调用WMS完成作业接口 定时器回传完成作业 JobCallBack jobCallBack = new JobCallBack(); jobCallBack.setType("1"); - jobCallBack.setMydojobId(mydoJob.getJobId()); jobCallBack.setStatus("0"); jobCallBack.setTaskNo(Convert.toLong(mydoJob.getTaskNo())); jobCallBack.setCreateTime(new Date()); @@ -395,6 +403,25 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { deviceCdzService.updateDeviceCdz(deviceCdz1); } } + srgvChargingJob.setAgvId(String.valueOf(wcsSrgvQueue.getAgvId())); + srgvChargingJob.setTaskNo(wcsSrgvQueue.getTaskNo()); + srgvChargingJob.setJobStatus("1"); + srgvChargingJob.setJobType("1"); + List srgvChargingJobs1 = srgvChargingJobMapper.selectSrgvChargingJobList(srgvChargingJob); + if (!srgvChargingJobs1.isEmpty()) { + SrgvChargingJob srgvChargingJob1 = srgvChargingJobs1.get(0); + if (deviceSrgv.getCurrSite().equals(wcsSrgvQueue.getDefault03()) && "8".equals(wcsSrgvQueue.getTaskType())) { + wcsSagvQueueMapper.insertHistory(wcsSrgvQueue.getTaskNo()); + wcsSagvQueueMapper.deleteWcsSrgvQueueByGuid(wcsSrgvQueue.getGuid()); +// srgvChargingJob1.setJobStatus("4"); +// srgvChargingJobMapper.updateSrgvChargingJob(srgvChargingJob1); +// DeviceCdz deviceCdz = new DeviceCdz(); +// DeviceCdz deviceCdz1 = deviceCdzService.selectDeviceCdzById(1L); +// deviceCdz1.setSpare1(String.valueOf(deviceSrgv.getDeviceId())); +// deviceCdzService.updateDeviceCdz(deviceCdz1); + } + } + } } } @@ -506,6 +533,7 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { } } } + /** * 小车发送点对点任务 */ @@ -522,6 +550,234 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { //判断是否能规划出最短路径 if (shortestPath == null || shortestPath.isEmpty()) { + // ========================================================= + // 特殊优先策略:若目标点被其他小车占用,直接调度占用车到缓存点 + // 场景示例:1号车目标为 040701,但 2号车正停在 040701 + // 处理思路:不等待、不绕行,直接把占用目标点的小车移开 + // ========================================================= + try { + // 查找占用目标点的阻挡车(排除当前车) + String endBlockAgv = null; + DeviceSrgv endBlockDevice = null; + for (Map.Entry entry : PlcSrgvRead.deviceSrgvMap.entrySet()) { + DeviceSrgv other = entry.getValue(); + if (other == null) continue; + if (Objects.equals(other.getCurrSite(), endSite) + && !Objects.equals(Convert.toStr(deviceSrgv.getDeviceId()), entry.getKey())) { + endBlockAgv = entry.getKey(); + endBlockDevice = other; + break; + } + } + WcsSrgvQueue wcsSrgvQueue1 =new WcsSrgvQueue(); + wcsSrgvQueue1.setAgvId(endBlockAgv); + List wcsSrgvQueues = wcsSagvQueueMapper.selectWcsSrgvQueueList(wcsSrgvQueue1); + if (endBlockDevice != null&&wcsSrgvQueues.isEmpty()) { + // 选择当前层最近且可达的缓存点(避让点) + WcsSrgvMap cacheParam = new WcsSrgvMap(); + cacheParam.setRemark("cache"); + cacheParam.setTheLayer(floor); + List cacheMaps = wcsSrgvMapService.selectWcsSrgvMapList(cacheParam); + // 生成阻挡车的避让任务(点到点,高优先级,未下发) + WcsSrgvQueue taskB = new WcsSrgvQueue(); + taskB.setGuid(UUID.randomUUID().toString()); + // 生成纯数字任务号(PLC要求),9 + HHmmss + taskB.setTaskNo(String.valueOf(taskCache)); + taskB.setAgvId(endBlockAgv); + taskB.setTaskType("4"); // 点到点移动 + taskB.setDefault02("4"); // 业务类型标识为点到点 + taskB.setPriority(9L); // 高优先级,尽快避让 + taskB.setIssued("0"); // 未下发 + taskB.setExecutionStatus("1");// 初始状态 + taskB.setStartSite(endBlockDevice.getCurrSite()); + taskB.setEndSite("050501"); + taskB.setTheFloor(floor.toString()); + taskB.setDefault03(endBlockDevice.getCurrSite()); + taskB.setDefault07(floor.toString()); + taskB.setDefault08("1");//避让任务,直接完成 + taskB.setCreateDate(DateUtil.now()); + wcsSagvQueueMapper.insertWcsSrgvQueue(taskB); + taskCache++; + log.info("目标点 {} 被小车 {} 占用,已调度其前往避让点 {}", endSite, endBlockAgv, "050501"); + // 当前车等待下一轮调度(待阻挡车移开后自然可达) + return false; + } else { + log.warn("目标点 {} 被小车 {} 占用,但无可用避让点", endSite, endBlockAgv); + // 无法直接移开阻挡车,后续进入常规策略(寻找阻挡者或自身避让) + } + } catch (Exception e) { + log.error("终点占用直接避让策略执行异常", e); + } + // --------------------------------------------------------- + // 策略1:尝试调度阻挡车避让 (新增逻辑) + // --------------------------------------------------------- + try { + // 1. 获取纯净静态地图 (不含动态障碍物) + // 重新获取原始地图数据,避免受 handleMap 修改的影响 + Map cleanArrByRoadSite = roadCacheService.getArrByRoadSite(floor); + int[][] staticMap = new int[29][16]; + for (WcsSrgvMap site : cleanArrByRoadSite.values()) { + if (site.getX() != null && site.getY() != null) { + staticMap[site.getX()][site.getY()] = site.getValue(); + } + } + // 2. 规划理想路径 (忽略其他小车) + int sx = cleanArrByRoadSite.get(startSite).getX(); + int sy = cleanArrByRoadSite.get(startSite).getY(); + int ex = cleanArrByRoadSite.get(endSite).getX(); + int ey = cleanArrByRoadSite.get(endSite).getY(); + staticMap[sx][sy] = 2; + staticMap[ex][ey] = 2; + List idealPath = PathFinder.findShortestPath( + staticMap, + sx, sy, + ex, ey + ); + // 3. 寻找阻挡车 + String blockingAgv = null; + if (idealPath == null || idealPath.isEmpty()) { + log.warn("理想路径为空,sx={},sy={},ex={},ey={},startVal={},endVal={}", + sx, sy, ex, ey, staticMap[sx][sy], staticMap[ex][ey]); + } + if (idealPath != null && !idealPath.isEmpty()) { + for (int[] point : idealPath) { + // 通过坐标反查站点 (简单遍历) + // 注意:这里假设同一层坐标唯一 + String pointSiteId = null; + for (Map.Entry entry : arrByRoadSite.entrySet()) { + WcsSrgvMap s = entry.getValue(); + if (s.getX() == point[0] && s.getY() == point[1]) { + pointSiteId = s.getSiteId(); + break; + } + } + + if (pointSiteId != null) { + // 检查是否有其他车在这个点 + for (Map.Entry entry : PlcSrgvRead.deviceSrgvMap.entrySet()) { + String otherAgvCode = entry.getKey(); + DeviceSrgv otherAgv = entry.getValue(); + + if (!otherAgvCode.equals(wcsSrgvQueue.getAgvId()) && // 不是自己 + pointSiteId.equals(otherAgv.getCurrSite())) { // 是别人 + + blockingAgv = otherAgvCode; + break; + } + } + } + if (blockingAgv != null) break; // 找到第一个阻挡者即可 + } + } + + // 4. 调度阻挡车 + if (blockingAgv != null) { + log.info("发现阻挡车: {}, 尝试调度避让", blockingAgv); + + // 检查 B 是否忙碌 (是否有未完成任务) + WcsSrgvQueue blockingParam = new WcsSrgvQueue(); + blockingParam.setAgvId(blockingAgv); // 注意:这里用 AgvId 还是 SrgvCode 需要确认,假设是一致的 + // 通常数据库存的是 ID,这里 blockingAgv 来自 deviceSrgvMap 的 key,通常是 code + // 如果 code != id,需要转换。假设 code 就是 id。 + + List tasks = wcsSagvQueueMapper.selectWcsSrgvQueueList(blockingParam); + boolean isBusy = CollUtil.isNotEmpty(tasks); + // 这里简化判断:只要有队列记录就算忙。更精细的判断可以看 status。 + + if (isBusy) { + log.info("阻挡车 {} 正在执行任务,当前车 {} 等待", blockingAgv, wcsSrgvQueue.getAgvId()); + return false; // B 忙,A 等待 (不做任何操作,下一轮循环再试) + } else { + // B 空闲,寻找避让点 + // 获取当前层所有避让点 + WcsSrgvMap cacheParam = new WcsSrgvMap(); + cacheParam.setRemark("cache"); + cacheParam.setTheLayer(floor); + List cacheMaps = wcsSrgvMapService.selectWcsSrgvMapList(cacheParam); + + // 获取所有小车位置 (用于避让点占用检查) + Set occupiedSites = new HashSet<>(); + for (DeviceSrgv d : PlcSrgvRead.deviceSrgvMap.values()) { + occupiedSites.add(d.getCurrSite()); + } + + String bestCacheForB = null; + int minLenForB = Integer.MAX_VALUE; + + DeviceSrgv deviceB = PlcSrgvRead.deviceSrgvMap.get(blockingAgv); + if (deviceB != null) { + WcsSrgvMap startNodeB = arrByRoadSite.get(deviceB.getCurrSite()); + + for (WcsSrgvMap cache : cacheMaps) { + String siteId = cache.getSiteId(); + if (occupiedSites.contains(siteId)) continue; // 已被占 + + // 关键:避让点不能在 A 的理想路径上 + boolean onIdealPath = false; + for (int[] p : idealPath) { + if (p[0] == cache.getX() && p[1] == cache.getY()) { + onIdealPath = true; + break; + } + } + if (onIdealPath) continue; + + // 检查 B 到避让点的可达性 (在静态地图上即可,因为其他车可能就是 A) + // 此时 A 还没动,所以 B 应该能动。 + // 为了安全,还是用 emptyMap (含 A) 检查 B 的路径? + // 不,用 emptyMap 检查 B 的路径,因为 A 还在原位。 + if (startNodeB != null && cache.getX() != null && cache.getY() != null) { + List pathB = PathFinder.findShortestPath(emptyMap, + startNodeB.getX(), startNodeB.getY(), + cache.getX(), cache.getY()); + + if (pathB != null && !pathB.isEmpty() && pathB.size() < minLenForB) { + minLenForB = pathB.size(); + bestCacheForB = siteId; + } + } + } + + if (bestCacheForB != null) { + // 生成 B 的避让任务 + WcsSrgvQueue taskB = new WcsSrgvQueue(); + taskB.setGuid(UUID.randomUUID().toString()); + // 生成纯数字任务号,避免 PLC 解析错误 + // 格式:9 + HHmmss + 随机位 (防止重复) + String timeStr = DateUtil.format(new Date(), "HHmmss"); + taskB.setTaskNo(String.valueOf(taskCache)); + taskB.setAgvId(blockingAgv); + taskB.setTaskType("4"); // 点到点 + taskB.setPriority(9L); // 高优先级 + taskB.setIssued("0"); + taskB.setExecutionStatus("1"); + taskB.setStartSite(deviceB.getCurrSite()); + taskB.setEndSite(bestCacheForB); + taskB.setTheFloor(floor.toString()); + taskB.setCreateDate(DateUtil.now()); + taskB.setDefault02("4"); + taskB.setDefault03(deviceB.getCurrSite()); // 默认值填充 + taskB.setDefault07(floor.toString()); + taskB.setDefault08("1"); + wcsSagvQueueMapper.insertWcsSrgvQueue(taskB); + taskCache++; + log.info("成功调度阻挡车 {} 前往避让点 {}", blockingAgv, bestCacheForB); + return false; // A 等待 + } else { + log.warn("阻挡车 {} 无可用避让点", blockingAgv); + // Fallback to Strategy 2 (A moves) + } + } + } + } + } catch (Exception e) { + log.error("调度阻挡车避让逻辑异常", e); + // Fallback to Strategy 2 + } + + // --------------------------------------------------------- + // 策略2:自己避让 (原有逻辑) + // --------------------------------------------------------- //判断是否是1层 空车取货 或者 满车放货 if (floor == 1) { if ("2".equals(wcsSrgvQueue.getDefault02()) && "tsj".equals(wcsSrgvQueue.getStartSite()) && wcsSrgvQueue.getEndSite().equals(wcsSrgvQueue.getCargoPlaceSite())) { @@ -659,7 +915,7 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { } else { SendData2 path = new SendData2(); path.setAddress("4004");//地址 - path.setData(startSite + "," + endSite+",0,0,0,0,0,0"); + path.setData(startSite + "," + endSite + ",0,0,0,0,0,0"); path.setType(2); sendDataList.add(path); } @@ -982,8 +1238,8 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { // } // } startSite = wcsSrgvQueue.getStartSite(); - if (startSite.equals("tsj")){ - startSite=getSite(wcsSrgvQueue.getStartSite(),wcsSrgvQueue.getTheFloor(),wcsSrgvQueue.getTaskType()); + if (startSite.equals("tsj")) { + startSite = getSite(wcsSrgvQueue.getStartSite(), wcsSrgvQueue.getTheFloor(), wcsSrgvQueue.getTaskType()); } if (!startSite.equals(currSite)) { wcsSrgvQueue.setIssued("1"); @@ -994,18 +1250,20 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface { mydoJob.setExecutionPhase("4");//小车搬运 mydoJobService.updateMydoJob(mydoJob); } - String endSite = wcsSrgvQueue.getEndSite(); - if (endSite.equals("tsj")){ - endSite=getSite(wcsSrgvQueue.getEndSite(),wcsSrgvQueue.getTheFloor(),wcsSrgvQueue.getTaskType()); + String endSite = wcsSrgvQueue.getEndSite(); + if (endSite.equals("tsj")) { + endSite = getSite(wcsSrgvQueue.getEndSite(), wcsSrgvQueue.getTheFloor(), wcsSrgvQueue.getTaskType()); } if (endSite.equals(currSite)) { - wcsSrgvQueue.setIssued("1"); - wcsSrgvQueue.setExecutionStatus("3"); - wcsSrgvQueue.setStartDate(DateUtil.now()); - wcsSagvQueueMapper.updateWcsSrgvQueue(wcsSrgvQueue); - MydoJob mydoJob = mydoJobService.selectMydoJobByTaskNo(Long.valueOf(wcsSrgvQueue.getTaskNo())); - mydoJob.setExecutionPhase("4");//小车搬运 - mydoJobService.updateMydoJob(mydoJob); + if (!wcsSrgvQueue.getTaskType().equals("8")) { + wcsSrgvQueue.setIssued("1"); + wcsSrgvQueue.setExecutionStatus("3"); + wcsSrgvQueue.setStartDate(DateUtil.now()); + wcsSagvQueueMapper.updateWcsSrgvQueue(wcsSrgvQueue); + MydoJob mydoJob = mydoJobService.selectMydoJobByTaskNo(Long.valueOf(wcsSrgvQueue.getTaskNo())); + mydoJob.setExecutionPhase("4");//小车搬运 + mydoJobService.updateMydoJob(mydoJob); + } } } diff --git a/ruoyi-admin/src/main/resources/mapper/device/DeviceCdzMapper.xml b/ruoyi-admin/src/main/resources/mapper/device/DeviceCdzMapper.xml index 4405b27..e85e453 100644 --- a/ruoyi-admin/src/main/resources/mapper/device/DeviceCdzMapper.xml +++ b/ruoyi-admin/src/main/resources/mapper/device/DeviceCdzMapper.xml @@ -52,11 +52,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"