From a751106535cf30a9aa80bed74d32a9e6368cd524 Mon Sep 17 00:00:00 2001 From: luoyifan Date: Fri, 4 Jul 2025 20:43:14 +0800 Subject: [PATCH] =?UTF-8?q?Load=20/=20Unload=20=E8=AE=B0=E5=BA=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/galaxis/rcs/RCSService.java | 2 +- .../main/java/com/galaxis/rcs/inv/InvManager.java | 179 ++++++----- .../com/galaxis/rcs/plan/PlanTaskSequence.java | 15 +- .../com/galaxis/rcs/plan/path/PtrPathPlanner.java | 4 +- .../java/com/galaxis/rcs/plan/task/CarryTask.java | 1 - .../main/java/com/galaxis/rcs/ptr/PtrAgvItem.java | 13 + .../main/java/com/yvan/entity/BasLocationVo.java | 60 ++++ .../src/main/java/com/yvan/entity/LccProject.java | 28 ++ .../main/java/com/yvan/entity/LccProjectEnv.java | 15 + servo/src/main/java/com/yvan/entity/Vector2.java | 13 + .../java/com/yvan/logisticsModel/ExecutorItem.java | 6 - .../com/yvan/logisticsModel/LogisticsRuntime.java | 60 +++- .../logisticsModel/LogisticsRuntimeService.java | 4 +- .../com/yvan/mqtt/FrontendMessagePushService.java | 349 -------------------- .../yvan/pusher/FrontendMessagePushService.java | 352 +++++++++++++++++++++ .../yvan/workbench/controller/LccModelManager.java | 2 +- .../yvan/workbench/controller/RcsController.java | 222 ++++++++++--- .../yvan/workbench/model/entity/LccProject.java | 28 -- .../yvan/workbench/model/entity/LccProjectEnv.java | 15 - .../com/yvan/workbench/model/entity/Vector2.java | 13 - .../com/yvan/workbench/service/LccMapService.java | 7 +- 21 files changed, 821 insertions(+), 567 deletions(-) create mode 100644 servo/src/main/java/com/yvan/entity/BasLocationVo.java create mode 100644 servo/src/main/java/com/yvan/entity/LccProject.java create mode 100644 servo/src/main/java/com/yvan/entity/LccProjectEnv.java create mode 100644 servo/src/main/java/com/yvan/entity/Vector2.java delete mode 100644 servo/src/main/java/com/yvan/mqtt/FrontendMessagePushService.java create mode 100644 servo/src/main/java/com/yvan/pusher/FrontendMessagePushService.java delete mode 100644 servo/src/main/java/com/yvan/workbench/model/entity/LccProject.java delete mode 100644 servo/src/main/java/com/yvan/workbench/model/entity/LccProjectEnv.java delete mode 100644 servo/src/main/java/com/yvan/workbench/model/entity/Vector2.java diff --git a/servo/src/main/java/com/galaxis/rcs/RCSService.java b/servo/src/main/java/com/galaxis/rcs/RCSService.java index c5fb6a0..2e5d267 100644 --- a/servo/src/main/java/com/galaxis/rcs/RCSService.java +++ b/servo/src/main/java/com/galaxis/rcs/RCSService.java @@ -10,7 +10,7 @@ import com.yvan.logisticsModel.LogisticsRuntime; import com.yvan.logisticsModel.LogisticsRuntimeService; import com.yvan.logisticsModel.StaticItem; import com.yvan.workbench.SpringContext; -import com.yvan.workbench.model.entity.LccProject; +import com.yvan.entity.LccProject; import com.yvan.workbench.service.LccMapService; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; diff --git a/servo/src/main/java/com/galaxis/rcs/inv/InvManager.java b/servo/src/main/java/com/galaxis/rcs/inv/InvManager.java index 6d52ec2..df779af 100644 --- a/servo/src/main/java/com/galaxis/rcs/inv/InvManager.java +++ b/servo/src/main/java/com/galaxis/rcs/inv/InvManager.java @@ -2,6 +2,7 @@ package com.galaxis.rcs.inv; import com.galaxis.rcs.common.entity.LccInvLpn; import com.querydsl.core.util.StringUtils; +import org.clever.core.id.SnowFlake; import org.clever.data.jdbc.DaoFactory; import org.clever.data.jdbc.QueryDSL; @@ -15,15 +16,16 @@ public class InvManager { /** * 记录库存 - * @param envId 环境ID - * @param lpn 容器号 - * @param locCode 库位号 + * + * @param envId 环境ID + * @param lpn 容器号 + * @param locCode 库位号 * @param layerIndex 堆叠层 - * @param qty 数量 >0 增加数量 <0 减数量 - * @param qtyIn 入库数量 >0 减数量 <0 减数量 - * @param qtyOut 出库数量 >0 减数量 <0 减数量 + * @param qty 数量 >0 增加数量 <0 减数量 + * @param qtyIn 入库数量 >0 减数量 <0 减数量 + * @param qtyOut 出库数量 >0 减数量 <0 减数量 */ - public static void invSave(Long envId, String lpn, String locCode, Integer layerIndex, int qty, int qtyIn, int qtyOut) { + public static void invSave(Long envId, Long bizTaskId, String lpn, String locCode, Integer layerIndex, int qty, int qtyIn, int qtyOut) { if (envId == null || envId <= 0 || lpn == null || StringUtils.isNullOrEmpty(lpn) || lpn.equals("N/A") || locCode == null || StringUtils.isNullOrEmpty(locCode) || locCode.equals("N/A") @@ -73,98 +75,117 @@ public class InvManager { } } - queryDSL.beginTX(status -> { - if (lccInvLpnData != null) { - queryDSL.update(lccInvLpn) - .set(lccInvLpn.qty, lccInvLpn.qty.add(qty)) - .set(lccInvLpn.qtyIn, lccInvLpn.qtyIn.add(qtyIn)) - .set(lccInvLpn.qtyOut, lccInvLpn.qtyOut.add(qtyOut)) - .set(lccInvLpn.layerIndex, layerIndex) -// .set(lccInvLpn.updateAt, ) - .where(lccInvLpn.envId.eq(envId).and(lccInvLpn.locCode.eq(locCode)).and(lccInvLpn.lpn.eq(lpn))) - .execute(); - // 记录账页 - queryDSL.insert(lccInvLedger) - .set(lccInvLedger.envId, envId) - .set(lccInvLedger.lpn, lpn) - .set(lccInvLedger.locCode, locCode) - .set(lccInvLedger.layerIndex, layerIndex) - .set(lccInvLedger.qtyChange, qty) - .set(lccInvLedger.qtyInChange, qtyIn) - .set(lccInvLedger.qtyOutChange, qtyOut) - .set(lccInvLedger.qty, lccInvLpnData.getQty() + qty) - .set(lccInvLedger.qtyIn, lccInvLpnData.getQtyIn() + qtyIn) - .set(lccInvLedger.qtyOut, lccInvLpnData.getQtyOut() + qtyOut) - .set(lccInvLedger.ledgerType, "记账") - .set(lccInvLedger.ledgerRemark, "N/A") - .execute(); - } else { - queryDSL.insert(lccInvLpn) - .set(lccInvLpn.envId, envId) - .set(lccInvLpn.lpn, lpn) - .set(lccInvLpn.locCode, locCode) - .set(lccInvLpn.layerIndex, layerIndex) - .set(lccInvLpn.qty, qty) - .set(lccInvLpn.qtyIn, qtyIn) - .set(lccInvLpn.qtyOut, qtyOut) - .execute(); - // 记录账页 - queryDSL.insert(lccInvLedger) - .set(lccInvLedger.envId, envId) - .set(lccInvLedger.lpn, lpn) - .set(lccInvLedger.locCode, locCode) - .set(lccInvLedger.layerIndex, layerIndex) - .set(lccInvLedger.qtyChange, qty) - .set(lccInvLedger.qtyInChange, qtyIn) - .set(lccInvLedger.qtyOutChange, qtyOut) - .set(lccInvLedger.qty, qty) - .set(lccInvLedger.qtyIn, qtyIn) - .set(lccInvLedger.qtyOut, qtyOut) - .set(lccInvLedger.ledgerType, "记账") - .set(lccInvLedger.ledgerRemark, "N/A") - .execute(); - } - // 删除所有数量预占预扣都为0的库存 - queryDSL.delete(lccInvLpn).where(lccInvLpn.qty.eq(0).and(lccInvLpn.qtyIn.eq(0)).and(lccInvLpn.qtyOut.eq(0))).execute(); - }); + + if (lccInvLpnData != null) { + queryDSL.update(lccInvLpn) + .set(lccInvLpn.qty, lccInvLpn.qty.add(qty)) + .set(lccInvLpn.qtyIn, lccInvLpn.qtyIn.add(qtyIn)) + .set(lccInvLpn.qtyOut, lccInvLpn.qtyOut.add(qtyOut)) + .set(lccInvLpn.layerIndex, layerIndex) + .set(lccInvLpn.updateAt, new Date(System.currentTimeMillis())) + .set(lccInvLpn.updateBy, "system") + .where(lccInvLpn.envId.eq(envId).and(lccInvLpn.locCode.eq(locCode)).and(lccInvLpn.lpn.eq(lpn))) + .execute(); + // 记录账页 + queryDSL.insert(lccInvLedger) + .set(lccInvLedger.ledgerId, SnowFlake.SNOW_FLAKE.nextId()) + .set(lccInvLedger.envId, envId) + .set(lccInvLedger.lpn, lpn) + .set(lccInvLedger.bizTaskId, bizTaskId) + .set(lccInvLedger.locCode, locCode) + .set(lccInvLedger.layerIndex, layerIndex) + .set(lccInvLedger.qtyChange, qty) + .set(lccInvLedger.qtyInChange, qtyIn) + .set(lccInvLedger.qtyOutChange, qtyOut) + .set(lccInvLedger.qty, lccInvLpnData.getQty() + qty) + .set(lccInvLedger.qtyIn, lccInvLpnData.getQtyIn() + qtyIn) + .set(lccInvLedger.qtyOut, lccInvLpnData.getQtyOut() + qtyOut) + .set(lccInvLedger.ledgerType, "记账") + .set(lccInvLedger.ledgerRemark, "N/A") + .set(lccInvLedger.createAt, new Date(System.currentTimeMillis())) + .set(lccInvLedger.createBy, "system") + .set(lccInvLedger.updateAt, new Date(System.currentTimeMillis())) + .set(lccInvLedger.updateBy, "system") + .execute(); + } else { + queryDSL.insert(lccInvLpn) + .set(lccInvLpn.envId, envId) + .set(lccInvLpn.lpn, lpn) + .set(lccInvLpn.locCode, locCode) + .set(lccInvLpn.layerIndex, layerIndex) + .set(lccInvLpn.qty, qty) + .set(lccInvLpn.qtyIn, qtyIn) + .set(lccInvLpn.qtyOut, qtyOut) + .set(lccInvLpn.createAt, new Date(System.currentTimeMillis())) + .set(lccInvLpn.createBy, "system") + .set(lccInvLpn.updateAt, new Date(System.currentTimeMillis())) + .set(lccInvLpn.updateBy, "system") + .execute(); + // 记录账页 + queryDSL.insert(lccInvLedger) + .set(lccInvLedger.ledgerId, SnowFlake.SNOW_FLAKE.nextId()) + .set(lccInvLedger.envId, envId) + .set(lccInvLedger.lpn, lpn) + .set(lccInvLedger.bizTaskId, bizTaskId) + .set(lccInvLedger.locCode, locCode) + .set(lccInvLedger.layerIndex, layerIndex) + .set(lccInvLedger.qtyChange, qty) + .set(lccInvLedger.qtyInChange, qtyIn) + .set(lccInvLedger.qtyOutChange, qtyOut) + .set(lccInvLedger.qty, qty) + .set(lccInvLedger.qtyIn, qtyIn) + .set(lccInvLedger.qtyOut, qtyOut) + .set(lccInvLedger.ledgerType, "记账") + .set(lccInvLedger.ledgerRemark, "N/A") + .set(lccInvLedger.createAt, new Date(System.currentTimeMillis())) + .set(lccInvLedger.createBy, "system") + .set(lccInvLedger.updateAt, new Date(System.currentTimeMillis())) + .set(lccInvLedger.updateBy, "system") + .execute(); + } + // 删除所有数量预占预扣都为0的库存 + queryDSL.delete(lccInvLpn).where(lccInvLpn.qty.eq(0).and(lccInvLpn.qtyIn.eq(0)).and(lccInvLpn.qtyOut.eq(0))).execute(); } /** * 记录库存 - * @param envId 环境ID - * @param lpn 容器号 + * + * @param envId 环境ID + * @param lpn 容器号 * @param locCode 库位号 - * @param qty 数量 >0 减数量 <0 减数量 - * @param qtyIn 入库数量 >0 减数量 <0 减数量 - * @param qtyOut 出库数量 >0 减数量 <0 减数量 + * @param qty 数量 >0 减数量 <0 减数量 + * @param qtyIn 入库数量 >0 减数量 <0 减数量 + * @param qtyOut 出库数量 >0 减数量 <0 减数量 */ - public static void invSave(Long envId, String lpn, String locCode, int qty, int qtyIn, int qtyOut) { - invSave(envId, lpn, locCode, 0, qty, qtyIn, qtyOut); + public static void invSave(Long envId, Long bizTaskId, String lpn, String locCode, int qty, int qtyIn, int qtyOut) { + invSave(envId, bizTaskId, lpn, locCode, 0, qty, qtyIn, qtyOut); } /** * 保存库存 - * @param envId 环境ID - * @param lpn 容器号 - * @param locCode 库位号 + * + * @param envId 环境ID + * @param lpn 容器号 + * @param locCode 库位号 * @param layerIndex 堆叠层 - * @param qty 数量 >0 减数量 <0 减数量 + * @param qty 数量 >0 减数量 <0 减数量 */ - public static void invSave(Long envId, String lpn, String locCode, int layerIndex, int qty) { - invSave(envId, lpn, locCode, layerIndex, qty, 0, 0); + public static void invSave(Long envId, Long bizTaskId, String lpn, String locCode, int layerIndex, int qty) { + invSave(envId, bizTaskId, lpn, locCode, layerIndex, qty, 0, 0); } /** * 保存库存 - * @param envId 环境ID - * @param lpn 容器号 + * + * @param envId 环境ID + * @param lpn 容器号 * @param locCode 库位号 - * @param qty 数量 >0 减数量 <0 减数量 + * @param qty 数量 >0 减数量 <0 减数量 */ - public static void invSave(Long envId, String lpn, String locCode, int qty) { - invSave(envId, lpn, locCode, 0, qty, 0, 0); + public static void invSave(Long envId, Long bizTaskId, String lpn, String locCode, int qty) { + invSave(envId, bizTaskId, lpn, locCode, 0, qty, 0, 0); } } diff --git a/servo/src/main/java/com/galaxis/rcs/plan/PlanTaskSequence.java b/servo/src/main/java/com/galaxis/rcs/plan/PlanTaskSequence.java index b73e4d6..34cbac7 100644 --- a/servo/src/main/java/com/galaxis/rcs/plan/PlanTaskSequence.java +++ b/servo/src/main/java/com/galaxis/rcs/plan/PlanTaskSequence.java @@ -6,12 +6,10 @@ import com.galaxis.rcs.common.entity.RcsTaskPlan; import com.galaxis.rcs.common.enums.PlanTaskStatus; import com.galaxis.rcs.common.enums.PlanTaskType; import com.google.common.base.Joiner; -import com.google.common.base.Splitter; import com.google.common.collect.Lists; +import com.yvan.entity.BasLocationVo; import com.yvan.logisticsModel.LogisticsRuntime; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.clever.core.BannerUtils; import org.clever.core.id.SnowFlake; import org.clever.core.json.JsonWrapper; @@ -33,7 +31,12 @@ public class PlanTaskSequence { public String lastWayPointId; public Float lastRotationAngle = null; - public String lastLoadLpn = ""; + + public String carryLpn; + public BasLocationVo loadBasLocationVo; + public BasLocationVo unloadBasLocationVo; + public BasLocationVo executorVo; + public int carryQty; public PlanTaskSequence(String executorId, LogisticsRuntime logisticsRuntime, RcsTaskBiz bizTask, String createBy) { this.executorId = executorId; @@ -90,13 +93,12 @@ public class PlanTaskSequence { } // 添加取货动作 - public RcsTaskPlan addLoad(String lpn, String rackId, int bay, int level, int cell) { + public RcsTaskPlan addLoad( String rackId, int bay, int level, int cell) { RcsTaskPlan task = this.createTaskPlanEntity(PlanTaskType.LOAD.toString()); task.setTargetId(rackId); task.setTargetBay(bay); task.setTargetLevel(level); task.setTargetCell(cell); - this.lastLoadLpn = lpn; return task; } @@ -107,7 +109,6 @@ public class PlanTaskSequence { task.setTargetBay(bay); task.setTargetLevel(level); task.setTargetCell(cell); - this.lastLoadLpn = ""; return task; } diff --git a/servo/src/main/java/com/galaxis/rcs/plan/path/PtrPathPlanner.java b/servo/src/main/java/com/galaxis/rcs/plan/path/PtrPathPlanner.java index 17f2cb4..d6f82a5 100644 --- a/servo/src/main/java/com/galaxis/rcs/plan/path/PtrPathPlanner.java +++ b/servo/src/main/java/com/galaxis/rcs/plan/path/PtrPathPlanner.java @@ -123,7 +123,7 @@ public class PtrPathPlanner { // 生成指令序列 generateMoves(plan, toLoadPath); - plan.addLoad(task.lpn(), loadRackId, pickupBay, task.from().level(), task.from().cell()); + plan.addLoad(loadRackId, pickupBay, task.from().level(), task.from().cell()); generateMoves(plan, toUnloadPath); plan.addUnload(unloadRackId, task.to().level(), task.to().bay(), task.to().cell()); @@ -218,7 +218,7 @@ public class PtrPathPlanner { // 生成指令序列 generateMoves(plan, toLoadPath); - plan.addLoad("N/A", loadRackId, task.to().level(), task.to().bay(), task.to().cell()); + plan.addLoad(loadRackId, task.to().level(), task.to().bay(), task.to().cell()); plan.addFinish(); } diff --git a/servo/src/main/java/com/galaxis/rcs/plan/task/CarryTask.java b/servo/src/main/java/com/galaxis/rcs/plan/task/CarryTask.java index 8a49546..ced33af 100644 --- a/servo/src/main/java/com/galaxis/rcs/plan/task/CarryTask.java +++ b/servo/src/main/java/com/galaxis/rcs/plan/task/CarryTask.java @@ -26,7 +26,6 @@ import com.galaxis.rcs.common.entity.StoreLocation; */ public record CarryTask( String agv, - String lpn, int priority, StoreLocation from, StoreLocation to diff --git a/servo/src/main/java/com/galaxis/rcs/ptr/PtrAgvItem.java b/servo/src/main/java/com/galaxis/rcs/ptr/PtrAgvItem.java index 15dea96..f4e7b0f 100644 --- a/servo/src/main/java/com/galaxis/rcs/ptr/PtrAgvItem.java +++ b/servo/src/main/java/com/galaxis/rcs/ptr/PtrAgvItem.java @@ -136,6 +136,19 @@ public abstract class PtrAgvItem extends ExecutorItem { public synchronized void dispatchTask(PlanTaskSequence taskSequence) { if (!isFree()) { + if (!this.runtime.isRunning()) { + throw new IllegalStateException("runtime is not running!"); + } + if (planTaskSequence != null && !planTaskSequence.isAllCompleted()) { + throw new IllegalStateException("AGV is busy with an existing task sequence"); + } + if (!deviceTaskQueue.isEmpty()) { + throw new IllegalStateException("AGV has pending device tasks in the queue"); + } + if (!isOnline) { + throw new IllegalStateException("AGV is not online and cannot accept new tasks"); + } + throw new IllegalStateException("AGV is not free to accept new tasks"); } diff --git a/servo/src/main/java/com/yvan/entity/BasLocationVo.java b/servo/src/main/java/com/yvan/entity/BasLocationVo.java new file mode 100644 index 0000000..bb703de --- /dev/null +++ b/servo/src/main/java/com/yvan/entity/BasLocationVo.java @@ -0,0 +1,60 @@ +package com.yvan.entity; + +import com.galaxis.rcs.common.entity.LccBasLocation; +import com.yvan.logisticsModel.ExecutorItem; +import lombok.Data; + +/** + * 用于表示货位的值对象 + */ +@Data +public class BasLocationVo { + /** + * 位置编码 + */ + String locCode; + + /** + * 货位类型 + */ + String locType; + + /** + * 货位 + */ + String rack; + + /** + * 列 + */ + int bay; + + /** + * 层 + */ + int level; + + /** + * 格 + */ + int cell; + + public BasLocationVo(LccBasLocation basLocation) { + this.locCode = basLocation.getLocCode(); + this.locType = basLocation.getLocType(); + this.rack = basLocation.getRack(); + this.bay = basLocation.getBay(); + this.level = basLocation.getLevel(); + this.cell = basLocation.getCell(); + } + + public BasLocationVo(ExecutorItem executorItem) { + // 从 ExecutorItem 中提取信息并赋值给 BasLocationVo + this.locCode = "AGV_" + executorItem.getId(); + this.locType = executorItem.getT(); + this.rack = "N/A"; + this.bay = 0; + this.level = 0; + this.cell = 0; + } +} diff --git a/servo/src/main/java/com/yvan/entity/LccProject.java b/servo/src/main/java/com/yvan/entity/LccProject.java new file mode 100644 index 0000000..93b33cc --- /dev/null +++ b/servo/src/main/java/com/yvan/entity/LccProject.java @@ -0,0 +1,28 @@ +package com.yvan.entity; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class LccProject { + private String projectUuid; + private String projectLabel; + private String[] subSystemList; + private String projectFileLocation; + private CatalogGroup[] directoryData; + private Map otherData; + + @Data + public static class CatalogGroup { + private String label; + private CatalogItem[] items; + } + + @Data + public static class CatalogItem { + private String catalogCode; + private String label; + } +} diff --git a/servo/src/main/java/com/yvan/entity/LccProjectEnv.java b/servo/src/main/java/com/yvan/entity/LccProjectEnv.java new file mode 100644 index 0000000..0c64b9f --- /dev/null +++ b/servo/src/main/java/com/yvan/entity/LccProjectEnv.java @@ -0,0 +1,15 @@ +package com.yvan.entity; + +import com.yvan.logisticsEnv.EnvConfig; +import lombok.Data; + +import java.util.Map; + +@Data +public class LccProjectEnv { + private Long envId; + private String envName; + private Boolean isVirtual; + private EnvConfig envConfig; + private String fileLocation; +} diff --git a/servo/src/main/java/com/yvan/entity/Vector2.java b/servo/src/main/java/com/yvan/entity/Vector2.java new file mode 100644 index 0000000..8c8e646 --- /dev/null +++ b/servo/src/main/java/com/yvan/entity/Vector2.java @@ -0,0 +1,13 @@ +package com.yvan.entity; + +public record Vector2( + /** + * X坐标 + */ + float x, + /** + * Y坐标 + */ + float y) { + +} diff --git a/servo/src/main/java/com/yvan/logisticsModel/ExecutorItem.java b/servo/src/main/java/com/yvan/logisticsModel/ExecutorItem.java index cfc54fa..00a720f 100644 --- a/servo/src/main/java/com/yvan/logisticsModel/ExecutorItem.java +++ b/servo/src/main/java/com/yvan/logisticsModel/ExecutorItem.java @@ -1,14 +1,8 @@ package com.yvan.logisticsModel; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.galaxis.rcs.common.entity.RcsTaskPlan; import com.galaxis.rcs.plan.PlanTaskSequence; -import com.google.common.collect.Queues; -import com.yvan.workbench.model.entity.Vector2; import java.util.Map; -import java.util.Queue; -import java.util.concurrent.BlockingQueue; /** * 物流任务执行单元(如拣货台、小车、AGV、堆垛机、人等) diff --git a/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntime.java b/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntime.java index 069c961..e471c72 100644 --- a/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntime.java +++ b/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntime.java @@ -1,6 +1,10 @@ package com.yvan.logisticsModel; +import com.galaxis.rcs.common.entity.RcsTaskPlan; +import com.galaxis.rcs.common.enums.PlanTaskType; import com.galaxis.rcs.connector.cl2.Cl2Item; +import com.galaxis.rcs.inv.InvManager; +import com.galaxis.rcs.plan.PlanTaskSequence; import com.galaxis.rcs.plan.path.NavigationGraph; import com.galaxis.rcs.plan.path.PtrPathPlanner; import com.galaxis.rcs.ptr.AmrMessageHandler; @@ -12,26 +16,27 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.yvan.event.AgvEventManager; import com.yvan.event.AgvEventType; -import com.yvan.mqtt.FrontendMessagePushService; +import com.yvan.pusher.FrontendMessagePushService; import com.yvan.redis.LccRedisService; -import com.yvan.workbench.model.entity.LccProject; -import com.yvan.workbench.model.entity.LccProjectEnv; +import com.yvan.entity.LccProject; +import com.yvan.entity.LccProjectEnv; import lombok.extern.slf4j.Slf4j; import org.clever.core.BannerUtils; import org.clever.core.Conv; +import org.clever.data.jdbc.DaoFactory; +import org.clever.data.jdbc.QueryDSL; import org.clever.data.redis.Redis; import org.clever.data.redis.RedisAdmin; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * 物流运行时 */ @Slf4j public class LogisticsRuntime { + public final QueryDSL queryDSL = DaoFactory.getQueryDSL(); + /** * 项目UUID */ @@ -134,12 +139,53 @@ public class LogisticsRuntime { for (int i = 1; i < args.length; i++) { eventArgs[i - 1] = Conv.asString(args[i]); } + if (type == AgvEventType.PLAN_TASK_COMPLETE) { + PlanTaskSequence taskSequence = (PlanTaskSequence) args[1]; + RcsTaskPlan taskPlan = (RcsTaskPlan) args[2]; + PlanTaskType planType = PlanTaskType.valueOf(taskPlan.getPlanType()); + if (planType == PlanTaskType.LOAD) { + // 处理库存变化 rack-> agv + changeInvOfLoad(taskSequence, taskPlan); + } else if (planType == PlanTaskType.UNLOAD) { + // 处理库存变化 agv -> rack + changeInvOfUnload(taskSequence, taskPlan); + } + + } BannerUtils.printConfig(log, this.projectUuid + "(" + this.envId + ") " + type + " AGV:" + sender.getId(), eventArgs); }); } /** + * 库存转移 AGV->货架 + */ + private void changeInvOfUnload(PlanTaskSequence taskSequence, RcsTaskPlan taskPlan) { + queryDSL.beginTX(status -> { + String lpn = taskSequence.carryLpn; + + InvManager.invSave(this.envId, taskSequence.bizTask.getBizTaskId(), lpn, taskSequence.executorVo.getLocCode(), -taskSequence.carryQty); + InvManager.invSave(this.envId, taskSequence.bizTask.getBizTaskId(), lpn, taskSequence.unloadBasLocationVo.getLocCode(), taskSequence.carryQty); + + this.frontendMessagePushService.pushInventoryUpdate(lpn, taskSequence.executorVo, taskSequence.unloadBasLocationVo, taskSequence.carryQty); + }); + } + + /** + * 库存转移 货架->AGV + */ + private void changeInvOfLoad(PlanTaskSequence taskSequence, RcsTaskPlan taskPlan) { + queryDSL.beginTX(status -> { + String lpn = taskSequence.carryLpn; + + InvManager.invSave(this.envId, taskSequence.bizTask.getBizTaskId(), lpn, taskSequence.loadBasLocationVo.getLocCode(), -taskSequence.carryQty); + InvManager.invSave(this.envId, taskSequence.bizTask.getBizTaskId(), lpn, taskSequence.executorVo.getLocCode(), taskSequence.carryQty); + + this.frontendMessagePushService.pushInventoryUpdate(lpn, taskSequence.loadBasLocationVo, taskSequence.executorVo, taskSequence.carryQty); + }); + } + + /** * 获取当前空闲的执行器列表 */ public List getFreeExecutorList() { diff --git a/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntimeService.java b/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntimeService.java index 274c4a0..21c811f 100644 --- a/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntimeService.java +++ b/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntimeService.java @@ -1,8 +1,8 @@ package com.yvan.logisticsModel; import com.google.common.collect.Maps; -import com.yvan.workbench.model.entity.LccProject; -import com.yvan.workbench.model.entity.LccProjectEnv; +import com.yvan.entity.LccProject; +import com.yvan.entity.LccProjectEnv; import lombok.SneakyThrows; import java.net.InetAddress; diff --git a/servo/src/main/java/com/yvan/mqtt/FrontendMessagePushService.java b/servo/src/main/java/com/yvan/mqtt/FrontendMessagePushService.java deleted file mode 100644 index a22c7c5..0000000 --- a/servo/src/main/java/com/yvan/mqtt/FrontendMessagePushService.java +++ /dev/null @@ -1,349 +0,0 @@ -package com.yvan.mqtt; - -import com.yvan.logisticsEnv.EnvConfig; -import com.yvan.logisticsModel.LogisticsRuntime; -import lombok.Getter; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.clever.core.BannerUtils; -import org.clever.core.mapper.JacksonMapper; -import org.eclipse.paho.mqttv5.client.*; -import org.eclipse.paho.mqttv5.client.persist.MemoryPersistence; -import org.eclipse.paho.mqttv5.common.MqttException; -import org.eclipse.paho.mqttv5.common.MqttMessage; -import org.eclipse.paho.mqttv5.common.packet.MqttProperties; - -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -@Slf4j -public class FrontendMessagePushService implements MqttCallback { - private volatile EnvConfig.MqttConfig mqttConfig; - private volatile String clientId; - private volatile MqttClient mqttClient; - private final LogisticsRuntime runtime; - private final MemoryPersistence persistence = new MemoryPersistence(); - - private final Lock connectionLock = new ReentrantLock(); - - // QoS 配置 - private static final int QOS = 1; - - // 重连参数 - private static final int MAX_RETRIES = 5; - private static final int RETRY_INTERVAL = 3; // 秒 - - // 连接状态 - @Getter - private volatile boolean connected = false; - - public FrontendMessagePushService(LogisticsRuntime runtime) { - this.runtime = runtime; - } - - /** - * 启动MQTT服务 - */ - @SneakyThrows - public void start(EnvConfig.MqttConfig mqttConfig, String clientId) { - this.mqttConfig = mqttConfig; - this.clientId = clientId; - connectionLock.lock(); - try { - if (connected) { - log.warn("LCC_MQTT service is already started"); - return; - } - - log.info("Starting LCC_MQTT service for project: {}, env: {}", this.runtime.projectUuid, this.runtime.envId); - - // 创建MQTT客户端 - mqttClient = new MqttClient(mqttConfig.getBrokerUrl(), clientId, persistence); - mqttClient.setCallback(this); - - // 配置连接选项 - MqttConnectionOptions options = new MqttConnectionOptions(); - options.setServerURIs(new String[]{mqttConfig.getBrokerUrl()}); - options.setUserName(mqttConfig.getUsername()); - options.setPassword(mqttConfig.getPassword().getBytes()); - options.setAutomaticReconnect(true); - options.setConnectionTimeout(10); - options.setKeepAliveInterval(60); - options.setExecutorServiceTimeout(1); - - // 尝试连接 - int attempts = 0; - while (attempts < MAX_RETRIES && !connected) { - attempts++; - try { - log.info("Connecting to LCC_MQTT broker (attempt {}/{})", attempts, MAX_RETRIES); - mqttClient.connect(options); - connected = true; - log.info("LCC_MQTT connected successfully"); - } catch (MqttException e) { - log.error("LCC_MQTT connection failed (attempt " + attempts + "/{" + MAX_RETRIES + "})", e); - - // 重试前等待 - if (attempts < MAX_RETRIES) { - try { - TimeUnit.SECONDS.sleep(RETRY_INTERVAL); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - break; - } - } - } - } - - if (!connected) { - log.error("Failed to connect to LCC_MQTT broker after {} attempts", MAX_RETRIES); - } - } finally { - connectionLock.unlock(); - } - } - - /** - * 停止MQTT服务 - */ - public void stop() { - connectionLock.lock(); - try { - if (mqttClient != null && mqttClient.isConnected()) { - try { - mqttClient.disconnect(); - mqttClient.close(); - log.info("LCC_MQTT disconnected"); - } catch (MqttException e) { - log.error("Error disconnecting LCC_MQTT", e); - } - } - connected = false; - } finally { - connectionLock.unlock(); - } - } - - // ==================== 消息推送方法 ==================== - - /** - * 推送服务器状态 - * /lcc/{proj_id}/{env_id}/server - * - * @param statusData 状态数据 - */ - public void pushServerState(Map statusData) { - String topic = buildTopic("server"); - publishJson(topic, statusData); - } - - /** - * 推送客户端状态 - * /lcc/{proj_id}/{env_id}/client - * - * @param clientData 客户端数据 - */ - public void pushClientState(Map clientData) { - String topic = buildTopic("client"); - publishJson(topic, clientData); - } - - /** - * 推送任务更新 - * /lcc/{proj_id}/{env_id}/task - * - * @param taskData 任务数据 - */ - public void pushTaskUpdate(Object taskData) { - String topic = buildTopic("task"); - publishJson(topic, taskData); - } - - /** - * 推送库存更新 - * /lcc/{proj_id}/{env_id}/inv/{catalogCode} - * - * @param catalogCode 货位目录编码 - * @param before 更新前库存 - * @param after 更新后库存 - */ - public void pushInventoryUpdate(String catalogCode, Object before, Object after) { - String topic = buildTopic("inv/" + catalogCode); - - Map data = new HashMap<>(); - data.put("before", before); - data.put("after", after); - - publishJson(topic, data); - } - - /** - * 推送设备状态 - * /lcc/{proj_id}/{env_id}/device/{id}/status - * - * @param deviceId 设备ID - * @param statusData 状态数据 - */ - public void pushDeviceStatus(String deviceId, Map statusData) { - String topic = buildTopic("device/" + deviceId + "/status"); - publishJson(topic, statusData); - } - - /** - * 推送设备存活状态 - * - * @param deviceId 设备ID - * @param online 是否在线 - */ - public void pushDeviceAlive(String deviceId, boolean online) { - String topic = buildTopic("device/" + deviceId + "/alive"); - publishString(topic, online ? "online" : "offline"); - } - - /** - * 推送日志 - * /lcc/{proj_id}/{env_id}/log/{type} - * - * @param logType 日志类型 - * @param logData 日志数据 - */ - public void pushLogs(String logType, Object logData) { - String topic = buildTopic("log/" + logType); - publishJson(topic, logData); - } - - /** - * 推送告警 - * /lcc/{proj_id}/{env_id}/alarm - * - * @param alarmData 告警数据 - */ - public void pushAlarm(Object alarmData) { - String topic = buildTopic("alarm"); - publishJson(topic, alarmData); - } - - /** - * 推送脚本更新 - * /lcc/{proj_id}/script - * - * @param scriptData 脚本数据 - */ - public void pushScriptUpdate(Object scriptData) { - // 脚本系统没有环境ID - String topic = "/lcc/" + this.runtime.projectUuid + "/script"; - publishJson(topic, scriptData); - } - - // ==================== 内部工具方法 ==================== - - /** - * 构建主题路径 - */ - private String buildTopic(String suffix) { - return "/lcc/" + this.runtime.projectUuid + "/" + this.runtime.envId + "/" + suffix; - } - - /** - * 发布JSON数据 - */ - private void publishJson(String topic, Object data) { - try { - String json = JacksonMapper.getInstance().toJson(data); - publish(topic, json); - } catch (Exception e) { - log.error("Failed to serialize JSON for topic: " + topic, e); - } - } - - /** - * 发布字符串数据 - */ - private void publishString(String topic, String message) { - publish(topic, message); - } - - /** - * 通用发布方法 - */ - private void publish(String topic, String payload) { - if (!connected) { - log.error("Attempted to publish while disconnected: {}", topic); - return; - } - - try { - MqttMessage message = new MqttMessage(payload.getBytes(StandardCharsets.UTF_8)); - message.setQos(QOS); - message.setRetained(false); - - mqttClient.publish(topic, message); - - log.debug("Published to {}: {}", topic, payload); - } catch (MqttException e) { - log.error("Failed to publish to topic: " + topic, e); - } - } - - // ==================== MQTT 回调方法 ==================== - - @Override - public void disconnected(MqttDisconnectResponse disconnectResponse) { - log.warn("LCC_MQTT disconnected: {}", disconnectResponse); - connectionLock.lock(); - try { - connected = false; - } finally { - connectionLock.unlock(); - } - } - - @Override - public void mqttErrorOccurred(MqttException exception) { - log.error("LCC_MQTT error occurred", exception); - } - - @Override - public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception { - log.debug("Message arrived on topic: {}", topic); - // 作为服务端,我们只发布消息,不接收消息 - // 可以记录或处理意外收到的消息 - } - - @Override - public void deliveryComplete(IMqttToken iMqttToken) { - try { - String[] topics = iMqttToken.getTopics(); - if (topics != null && topics.length > 0) { - log.info("Message delivery confirmed for topic: {}", topics[0]); - } else { - log.info("Message delivery confirmed (no topic info)"); - } - } catch (Exception e) { - log.warn("Error getting delivery token info", e); - } - } - - @Override - public void connectComplete(boolean reconnect, String serverURI) { - BannerUtils.printConfig(log, "LCC_MQTT 开启监听成功", new String[]{ - "brokerUrl: " + serverURI, - "userName: " + this.mqttConfig.getUsername(), - "clientId: " + clientId}); - connectionLock.lock(); - try { - connected = true; - } finally { - connectionLock.unlock(); - } - } - - @Override - public void authPacketArrived(int i, MqttProperties mqttProperties) { - log.info("LCC_MQTT authPacketArrived({}, {})", i, mqttProperties); - } -} diff --git a/servo/src/main/java/com/yvan/pusher/FrontendMessagePushService.java b/servo/src/main/java/com/yvan/pusher/FrontendMessagePushService.java new file mode 100644 index 0000000..c58ddff --- /dev/null +++ b/servo/src/main/java/com/yvan/pusher/FrontendMessagePushService.java @@ -0,0 +1,352 @@ +package com.yvan.pusher; + +import com.yvan.entity.BasLocationVo; +import com.yvan.logisticsEnv.EnvConfig; +import com.yvan.logisticsModel.LogisticsRuntime; +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.clever.core.BannerUtils; +import org.clever.core.mapper.JacksonMapper; +import org.eclipse.paho.mqttv5.client.*; +import org.eclipse.paho.mqttv5.client.persist.MemoryPersistence; +import org.eclipse.paho.mqttv5.common.MqttException; +import org.eclipse.paho.mqttv5.common.MqttMessage; +import org.eclipse.paho.mqttv5.common.packet.MqttProperties; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +@Slf4j +public class FrontendMessagePushService implements MqttCallback { + private volatile EnvConfig.MqttConfig mqttConfig; + private volatile String clientId; + private volatile MqttClient mqttClient; + private final LogisticsRuntime runtime; + private final MemoryPersistence persistence = new MemoryPersistence(); + + private final Lock connectionLock = new ReentrantLock(); + + // QoS 配置 + private static final int QOS = 1; + + // 重连参数 + private static final int MAX_RETRIES = 5; + private static final int RETRY_INTERVAL = 3; // 秒 + + // 连接状态 + @Getter + private volatile boolean connected = false; + + public FrontendMessagePushService(LogisticsRuntime runtime) { + this.runtime = runtime; + } + + /** + * 启动MQTT服务 + */ + @SneakyThrows + public void start(EnvConfig.MqttConfig mqttConfig, String clientId) { + this.mqttConfig = mqttConfig; + this.clientId = clientId; + connectionLock.lock(); + try { + if (connected) { + log.warn("LCC_MQTT service is already started"); + return; + } + + log.info("Starting LCC_MQTT service for project: {}, env: {}", this.runtime.projectUuid, this.runtime.envId); + + // 创建MQTT客户端 + mqttClient = new MqttClient(mqttConfig.getBrokerUrl(), clientId, persistence); + mqttClient.setCallback(this); + + // 配置连接选项 + MqttConnectionOptions options = new MqttConnectionOptions(); + options.setServerURIs(new String[]{mqttConfig.getBrokerUrl()}); + options.setUserName(mqttConfig.getUsername()); + options.setPassword(mqttConfig.getPassword().getBytes()); + options.setAutomaticReconnect(true); + options.setConnectionTimeout(10); + options.setKeepAliveInterval(60); + options.setExecutorServiceTimeout(1); + + // 尝试连接 + int attempts = 0; + while (attempts < MAX_RETRIES && !connected) { + attempts++; + try { + log.info("Connecting to LCC_MQTT broker (attempt {}/{})", attempts, MAX_RETRIES); + mqttClient.connect(options); + connected = true; + log.info("LCC_MQTT connected successfully"); + } catch (MqttException e) { + log.error("LCC_MQTT connection failed (attempt " + attempts + "/{" + MAX_RETRIES + "})", e); + + // 重试前等待 + if (attempts < MAX_RETRIES) { + try { + TimeUnit.SECONDS.sleep(RETRY_INTERVAL); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + break; + } + } + } + } + + if (!connected) { + log.error("Failed to connect to LCC_MQTT broker after {} attempts", MAX_RETRIES); + } + } finally { + connectionLock.unlock(); + } + } + + /** + * 停止MQTT服务 + */ + public void stop() { + connectionLock.lock(); + try { + if (mqttClient != null && mqttClient.isConnected()) { + try { + mqttClient.disconnect(); + mqttClient.close(); + log.info("LCC_MQTT disconnected"); + } catch (MqttException e) { + log.error("Error disconnecting LCC_MQTT", e); + } + } + connected = false; + } finally { + connectionLock.unlock(); + } + } + + // ==================== 消息推送方法 ==================== + + /** + * 推送服务器状态 + * /lcc/{proj_id}/{env_id}/server + * + * @param statusData 状态数据 + */ + public void pushServerState(Map statusData) { + String topic = buildTopic("server"); + publishJson(topic, statusData); + } + + /** + * 推送客户端状态 + * /lcc/{proj_id}/{env_id}/client + * + * @param clientData 客户端数据 + */ + public void pushClientState(Map clientData) { + String topic = buildTopic("client"); + publishJson(topic, clientData); + } + + /** + * 推送任务更新 + * /lcc/{proj_id}/{env_id}/task + * + * @param taskData 任务数据 + */ + public void pushTaskUpdate(Object taskData) { + String topic = buildTopic("task"); + publishJson(topic, taskData); + } + + /** + * 推送库存更新 + * /lcc/{proj_id}/{env_id}/inv + * + * @param lpn 容器号 + * @param before 更新前库存 + * @param after 更新后库存 + */ + public void pushInventoryUpdate(String lpn, BasLocationVo before, BasLocationVo after, int qty) { + String topic = buildTopic("inv"); + + Map data = new HashMap<>(); + data.put("lpn", lpn); + data.put("before", before); + data.put("after", after); + data.put("qty", qty); + + publishJson(topic, data); + } + + /** + * 推送设备状态 + * /lcc/{proj_id}/{env_id}/device/{id}/status + * + * @param deviceId 设备ID + * @param statusData 状态数据 + */ + public void pushDeviceStatus(String deviceId, Map statusData) { + String topic = buildTopic("device/" + deviceId + "/status"); + publishJson(topic, statusData); + } + + /** + * 推送设备存活状态 + * + * @param deviceId 设备ID + * @param online 是否在线 + */ + public void pushDeviceAlive(String deviceId, boolean online) { + String topic = buildTopic("device/" + deviceId + "/alive"); + publishString(topic, online ? "online" : "offline"); + } + + /** + * 推送日志 + * /lcc/{proj_id}/{env_id}/log/{type} + * + * @param logType 日志类型 + * @param logData 日志数据 + */ + public void pushLogs(String logType, Object logData) { + String topic = buildTopic("log/" + logType); + publishJson(topic, logData); + } + + /** + * 推送告警 + * /lcc/{proj_id}/{env_id}/alarm + * + * @param alarmData 告警数据 + */ + public void pushAlarm(Object alarmData) { + String topic = buildTopic("alarm"); + publishJson(topic, alarmData); + } + + /** + * 推送脚本更新 + * /lcc/{proj_id}/script + * + * @param scriptData 脚本数据 + */ + public void pushScriptUpdate(Object scriptData) { + // 脚本系统没有环境ID + String topic = "/lcc/" + this.runtime.projectUuid + "/script"; + publishJson(topic, scriptData); + } + + // ==================== 内部工具方法 ==================== + + /** + * 构建主题路径 + */ + private String buildTopic(String suffix) { + return "/lcc/" + this.runtime.projectUuid + "/" + this.runtime.envId + "/" + suffix; + } + + /** + * 发布JSON数据 + */ + private void publishJson(String topic, Object data) { + try { + String json = JacksonMapper.getInstance().toJson(data); + publish(topic, json); + } catch (Exception e) { + log.error("Failed to serialize JSON for topic: " + topic, e); + } + } + + /** + * 发布字符串数据 + */ + private void publishString(String topic, String message) { + publish(topic, message); + } + + /** + * 通用发布方法 + */ + private void publish(String topic, String payload) { + if (!connected) { + log.error("Attempted to publish while disconnected: {}", topic); + return; + } + + try { + MqttMessage message = new MqttMessage(payload.getBytes(StandardCharsets.UTF_8)); + message.setQos(QOS); + message.setRetained(false); + + mqttClient.publish(topic, message); + + log.debug("Published to {}: {}", topic, payload); + } catch (MqttException e) { + log.error("Failed to publish to topic: " + topic, e); + } + } + + // ==================== MQTT 回调方法 ==================== + + @Override + public void disconnected(MqttDisconnectResponse disconnectResponse) { + log.warn("LCC_MQTT disconnected: {}", disconnectResponse); + connectionLock.lock(); + try { + connected = false; + } finally { + connectionLock.unlock(); + } + } + + @Override + public void mqttErrorOccurred(MqttException exception) { + log.error("LCC_MQTT error occurred", exception); + } + + @Override + public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception { + log.debug("Message arrived on topic: {}", topic); + // 作为服务端,我们只发布消息,不接收消息 + // 可以记录或处理意外收到的消息 + } + + @Override + public void deliveryComplete(IMqttToken iMqttToken) { + try { + String[] topics = iMqttToken.getTopics(); + if (topics != null && topics.length > 0) { + log.info("Message delivery confirmed for topic: {}", topics[0]); + } else { + log.info("Message delivery confirmed (no topic info)"); + } + } catch (Exception e) { + log.warn("Error getting delivery token info", e); + } + } + + @Override + public void connectComplete(boolean reconnect, String serverURI) { + BannerUtils.printConfig(log, "LCC_MQTT 开启监听成功", new String[]{ + "brokerUrl: " + serverURI, + "userName: " + this.mqttConfig.getUsername(), + "clientId: " + clientId}); + connectionLock.lock(); + try { + connected = true; + } finally { + connectionLock.unlock(); + } + } + + @Override + public void authPacketArrived(int i, MqttProperties mqttProperties) { + log.info("LCC_MQTT authPacketArrived({}, {})", i, mqttProperties); + } +} diff --git a/servo/src/main/java/com/yvan/workbench/controller/LccModelManager.java b/servo/src/main/java/com/yvan/workbench/controller/LccModelManager.java index 6107a1c..cec4143 100644 --- a/servo/src/main/java/com/yvan/workbench/controller/LccModelManager.java +++ b/servo/src/main/java/com/yvan/workbench/controller/LccModelManager.java @@ -4,7 +4,7 @@ import com.galaxis.rcs.common.entity.LccBasLocation; import com.galaxis.rcs.common.entity.StoreLocation; import com.galaxis.rcs.ptr.JacksonUtils; import com.yvan.workbench.SpringContext; -import com.yvan.workbench.model.entity.LccProject; +import com.yvan.entity.LccProject; import com.yvan.workbench.service.LccMapService; import jakarta.servlet.http.HttpServletResponse; import lombok.SneakyThrows; diff --git a/servo/src/main/java/com/yvan/workbench/controller/RcsController.java b/servo/src/main/java/com/yvan/workbench/controller/RcsController.java index 1db96a6..f82aa07 100644 --- a/servo/src/main/java/com/yvan/workbench/controller/RcsController.java +++ b/servo/src/main/java/com/yvan/workbench/controller/RcsController.java @@ -1,6 +1,6 @@ package com.yvan.workbench.controller; -import com.galaxis.rcs.RCSService; +import com.galaxis.rcs.common.entity.LccBasLocation; import com.galaxis.rcs.common.entity.RcsTaskBiz; import com.galaxis.rcs.common.entity.StoreLocation; import com.galaxis.rcs.common.enums.BizTaskStatus; @@ -11,6 +11,7 @@ import com.galaxis.rcs.plan.task.*; import com.galaxis.rcs.ptr.PtrAgvItem; import com.google.common.base.Strings; import com.google.common.collect.Maps; +import com.yvan.entity.BasLocationVo; import com.yvan.logisticsModel.ExecutorItem; import com.yvan.logisticsModel.LogisticsRuntime; import com.yvan.logisticsModel.LogisticsRuntimeService; @@ -22,6 +23,9 @@ import org.clever.web.mvc.annotation.RequestBody; import java.util.Map; +import static com.galaxis.rcs.common.query.QLccBasLocation.lccBasLocation; +import static com.galaxis.rcs.common.query.QLccInvLpn.lccInvLpn; + public class RcsController { static final SnowFlake snowFlake = new SnowFlake(); @@ -130,6 +134,101 @@ public class RcsController { return R.success(executorItem); } + public static R agvCarry(@RequestBody Map params) { + Object ret = getCommonParamAndCreateBizTask(params); + if (ret instanceof R) { + // 异常 + return (R) ret; + } + RcsCommonParam ps = (RcsCommonParam) ret; + + + // ==================== 查找来源货位 ==================== + String fromStoreLoc = Conv.asString(params.get("fromStoreLoc")); + if (Strings.isNullOrEmpty(fromStoreLoc)) { + return R.fail("fromStoreLoc Must not be empty"); + } + StoreLocation sourceLocation = StoreLocation.of(fromStoreLoc, "/"); + StaticItem sourceItem = ps.runtime.getStaticItemById(sourceLocation.rackId()); + if (sourceItem == null) { + return R.fail("fromStoreLoc storePoint not found!"); + } + // 查找货位基础资料 + LccBasLocation loadBasLocation = ps.runtime.queryDSL.selectFrom( + lccBasLocation + ) + .where(lccBasLocation.envId.eq(ps.envId)) + .where(lccBasLocation.rack.eq(sourceLocation.rackId())) + .where(lccBasLocation.bay.eq(sourceLocation.bay())) + .where(lccBasLocation.level.eq(sourceLocation.level())) + .where(lccBasLocation.cell.eq(sourceLocation.cell())) + .fetchFirst(); + if (loadBasLocation == null) { + return R.fail("fromStoreLoc location not found!"); + } + + + // ==================== 查找卸货货位 ==================== + String targetStoreLoc = Conv.asString(params.get("targetStoreLoc")); + if (Strings.isNullOrEmpty(targetStoreLoc)) { + return R.fail("targetStoreLoc Must not be empty"); + } + StoreLocation targetLocation = StoreLocation.of(targetStoreLoc, "/"); + StaticItem targetItem = ps.runtime.getStaticItemById(targetLocation.rackId()); + if (targetItem == null) { + return R.fail("targetStoreLoc storePoint not found!"); + } + // 查找货位基础资料 + LccBasLocation unloadBasLocation = ps.runtime.queryDSL.selectFrom( + lccBasLocation + ) + .where(lccBasLocation.envId.eq(ps.envId)) + .where(lccBasLocation.rack.eq(targetLocation.rackId())) + .where(lccBasLocation.bay.eq(targetLocation.bay())) + .where(lccBasLocation.level.eq(targetLocation.level())) + .where(lccBasLocation.cell.eq(targetLocation.cell())) + .fetchFirst(); + if (unloadBasLocation == null) { + return R.fail("targetStoreLoc location not found!"); + } + + // ==================== 找到托盘号 ==================== + String lpn = ps.runtime.queryDSL + .select(lccInvLpn.lpn) + .from(lccInvLpn) + .where(lccInvLpn.envId.eq(ps.envId)) + .where(lccInvLpn.locCode.eq(loadBasLocation.getLocCode())) + .fetchFirst(); + if (Strings.isNullOrEmpty(lpn)) { + return R.fail("LPN not found at fromStoreLoc: " + fromStoreLoc); + } + + // ==================== 布置任务 ==================== + ps.bizTask.setLpn(lpn); + ps.bizTask.setTaskFrom(loadBasLocation.getCatalogCode()); + ps.bizTask.setTaskTo(unloadBasLocation.getLocCode()); + + CarryTask carryTask = new CarryTask( + ps.agvId, ps.bizTask.getPriority(), + sourceLocation, + targetLocation + ); + ps.planSequence.executorVo = new BasLocationVo(ps.agv); + ps.planSequence.loadBasLocationVo = new BasLocationVo(loadBasLocation); + ps.planSequence.unloadBasLocationVo = new BasLocationVo(unloadBasLocation); + ps.planSequence.carryLpn = lpn; + ps.planSequence.carryQty = 1; + + ps.runtime.pathPlannerMap.get(ps.agv.getT()) + .planCarryTask(ps.planSequence, ps.fromItem.getId(), ps.fromDirection, carryTask); + + ps.agv.logicX = ps.fromItem.logicX; + ps.agv.logicY = ps.fromItem.logicY; + ps.agv.dispatchTask(ps.planSequence); + + return R.success(ps.planSequence.toPrettyMap()); + } + public static R agvUnload(@RequestBody Map params) { Object ret = getCommonParamAndCreateBizTask(params); if (ret instanceof R) { @@ -138,16 +237,52 @@ public class RcsController { } RcsCommonParam ps = (RcsCommonParam) ret; + // ==================== 查找卸货货位 ==================== String targetStoreLoc = Conv.asString(params.get("targetStoreLoc")); if (Strings.isNullOrEmpty(targetStoreLoc)) { return R.fail("targetStoreLoc Must not be empty"); } StoreLocation targetLocation = StoreLocation.of(targetStoreLoc, "/"); + // 查找货位基础资料 + LccBasLocation unloadBasLocation = ps.runtime.queryDSL.selectFrom( + lccBasLocation + ) + .where(lccBasLocation.envId.eq(ps.envId)) + .where(lccBasLocation.rack.eq(targetLocation.rackId())) + .where(lccBasLocation.bay.eq(targetLocation.bay())) + .where(lccBasLocation.level.eq(targetLocation.level())) + .where(lccBasLocation.cell.eq(targetLocation.cell())) + .fetchFirst(); + if (unloadBasLocation == null) { + return R.fail("targetStoreLoc location not found!"); + } + + // ==================== 找到托盘号 ==================== + BasLocationVo executorVo = new BasLocationVo(ps.agv); + String lpn = ps.runtime.queryDSL + .select(lccInvLpn.lpn) + .from(lccInvLpn) + .where(lccInvLpn.envId.eq(ps.envId)) + .where(lccInvLpn.locCode.eq(executorVo.getLocCode())) + .fetchFirst(); + if (Strings.isNullOrEmpty(lpn)) { + return R.fail("LPN not found at StoreLoc: " + executorVo.getLocCode()); + } + + // ==================== 布置任务 ==================== + ps.bizTask.setLpn(lpn); + ps.bizTask.setTaskFrom(ps.fromItem.getId()); + ps.bizTask.setTaskTo(unloadBasLocation.getLocCode()); UnloadTask unloadTask = new UnloadTask( ps.agvId, ps.bizTask.getPriority(), targetLocation ); + ps.planSequence.executorVo = executorVo; + ps.planSequence.loadBasLocationVo = null; + ps.planSequence.unloadBasLocationVo = new BasLocationVo(unloadBasLocation); + ps.planSequence.carryLpn = lpn; + ps.planSequence.carryQty = 1; ps.runtime.pathPlannerMap.get(ps.agv.getT()) .planUnloadTask(ps.planSequence, ps.fromItem.getId(), ps.fromDirection, unloadTask); @@ -167,16 +302,51 @@ public class RcsController { } RcsCommonParam ps = (RcsCommonParam) ret; + // ==================== 查找来源货位 ==================== String targetStoreLoc = Conv.asString(params.get("targetStoreLoc")); if (Strings.isNullOrEmpty(targetStoreLoc)) { return R.fail("targetStoreLoc Must not be empty"); } StoreLocation targetLocation = StoreLocation.of(targetStoreLoc, "/"); + // 查找货位基础资料 + LccBasLocation loadBasLocation = ps.runtime.queryDSL.selectFrom( + lccBasLocation + ) + .where(lccBasLocation.envId.eq(ps.envId)) + .where(lccBasLocation.rack.eq(targetLocation.rackId())) + .where(lccBasLocation.bay.eq(targetLocation.bay())) + .where(lccBasLocation.level.eq(targetLocation.level())) + .where(lccBasLocation.cell.eq(targetLocation.cell())) + .fetchFirst(); + if (loadBasLocation == null) { + return R.fail("targetStoreLoc location not found!"); + } + + // ==================== 找到托盘号 ==================== + String lpn = ps.runtime.queryDSL + .select(lccInvLpn.lpn) + .from(lccInvLpn) + .where(lccInvLpn.envId.eq(ps.envId)) + .where(lccInvLpn.locCode.eq(loadBasLocation.getLocCode())) + .fetchFirst(); + if (Strings.isNullOrEmpty(lpn)) { + return R.fail("LPN not found at targetStoreLoc: " + loadBasLocation.getLocCode()); + } + + // ==================== 布置任务 ==================== + ps.bizTask.setLpn(lpn); + ps.bizTask.setTaskFrom(ps.fromItem.getId()); + ps.bizTask.setTaskTo(loadBasLocation.getLocCode()); LoadTask loadTask = new LoadTask( ps.agvId, ps.bizTask.getPriority(), targetLocation ); + ps.planSequence.executorVo = new BasLocationVo(ps.agv); + ps.planSequence.loadBasLocationVo = new BasLocationVo(loadBasLocation); + ps.planSequence.unloadBasLocationVo = null; + ps.planSequence.carryLpn = lpn; + ps.planSequence.carryQty = 1; ps.runtime.pathPlannerMap.get(ps.agv.getT()) .planLoadTask(ps.planSequence, ps.fromItem.getId(), ps.fromDirection, loadTask); @@ -224,56 +394,6 @@ public class RcsController { return R.success(cost); } - public static R agvCarry(@RequestBody Map params) { - Object ret = getCommonParamAndCreateBizTask(params); - if (ret instanceof R) { - // 异常 - return (R) ret; - } - RcsCommonParam ps = (RcsCommonParam) ret; - - String fromStoreLoc = Conv.asString(params.get("fromStoreLoc")); - String targetStoreLoc = Conv.asString(params.get("targetStoreLoc")); - - if (Strings.isNullOrEmpty(fromStoreLoc)) { - return R.fail("fromStoreLoc Must not be empty"); - } - if (Strings.isNullOrEmpty(targetStoreLoc)) { - return R.fail("targetStoreLoc Must not be empty"); - } - - StoreLocation sourceLocation = StoreLocation.of(fromStoreLoc, "/"); - StoreLocation targetLocation = StoreLocation.of(targetStoreLoc, "/"); - - StaticItem sourceItem = ps.runtime.getStaticItemById(sourceLocation.rackId()); - if (sourceItem == null) { - return R.fail("fromStoreLoc storePoint not found!"); - } - StaticItem targetItem = ps.runtime.getStaticItemById(targetLocation.rackId()); - if (targetItem == null) { - return R.fail("targetStoreLoc storePoint not found!"); - } - - ps.bizTask.setTaskFrom(fromStoreLoc); - ps.bizTask.setTaskTo(targetStoreLoc); - - CarryTask carryTask = new CarryTask( - ps.agvId, "N/A", ps.bizTask.getPriority(), - sourceLocation, - targetLocation - ); - - ps.runtime.pathPlannerMap.get(ps.agv.getT()) - .planCarryTask(ps.planSequence, ps.fromItem.getId(), ps.fromDirection, carryTask); - - ps.agv.logicX = ps.fromItem.logicX; - ps.agv.logicY = ps.fromItem.logicY; - ps.agv.dispatchTask(ps.planSequence); - - return R.success(ps.planSequence.toPrettyMap()); - } - - public static Object getCommonParamAndCreateBizTask(@RequestBody Map params) { String projectUUID = Conv.asString(params.get("projectUUID")); Long envId = Conv.asLong(params.get("envId")); diff --git a/servo/src/main/java/com/yvan/workbench/model/entity/LccProject.java b/servo/src/main/java/com/yvan/workbench/model/entity/LccProject.java deleted file mode 100644 index 9bc29be..0000000 --- a/servo/src/main/java/com/yvan/workbench/model/entity/LccProject.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.yvan.workbench.model.entity; - -import lombok.Data; - -import java.util.List; -import java.util.Map; - -@Data -public class LccProject { - private String projectUuid; - private String projectLabel; - private String[] subSystemList; - private String projectFileLocation; - private CatalogGroup[] directoryData; - private Map otherData; - - @Data - public static class CatalogGroup { - private String label; - private CatalogItem[] items; - } - - @Data - public static class CatalogItem { - private String catalogCode; - private String label; - } -} diff --git a/servo/src/main/java/com/yvan/workbench/model/entity/LccProjectEnv.java b/servo/src/main/java/com/yvan/workbench/model/entity/LccProjectEnv.java deleted file mode 100644 index 9e9fe5f..0000000 --- a/servo/src/main/java/com/yvan/workbench/model/entity/LccProjectEnv.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.yvan.workbench.model.entity; - -import com.yvan.logisticsEnv.EnvConfig; -import lombok.Data; - -import java.util.Map; - -@Data -public class LccProjectEnv { - private Long envId; - private String envName; - private Boolean isVirtual; - private EnvConfig envConfig; - private String fileLocation; -} diff --git a/servo/src/main/java/com/yvan/workbench/model/entity/Vector2.java b/servo/src/main/java/com/yvan/workbench/model/entity/Vector2.java deleted file mode 100644 index c81c29a..0000000 --- a/servo/src/main/java/com/yvan/workbench/model/entity/Vector2.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.yvan.workbench.model.entity; - -public record Vector2( - /** - * X坐标 - */ - float x, - /** - * Y坐标 - */ - float y) { - -} diff --git a/servo/src/main/java/com/yvan/workbench/service/LccMapService.java b/servo/src/main/java/com/yvan/workbench/service/LccMapService.java index 554c62b..c0f31c3 100644 --- a/servo/src/main/java/com/yvan/workbench/service/LccMapService.java +++ b/servo/src/main/java/com/yvan/workbench/service/LccMapService.java @@ -1,21 +1,18 @@ package com.yvan.workbench.service; -import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.yvan.LccUtils; import com.yvan.workbench.autoconfigure.LccConfigProperties; -import com.yvan.workbench.model.entity.LccProject; -import com.yvan.workbench.model.entity.LccProjectEnv; +import com.yvan.entity.LccProject; +import com.yvan.entity.LccProjectEnv; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.clever.core.Conv; import org.clever.core.mapper.JacksonMapper; import java.io.File; -import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.Objects;