From 0c180a58e4c078bdcd0a1726c445a89d03108041 Mon Sep 17 00:00:00 2001 From: luoyifan Date: Tue, 1 Jul 2025 21:25:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=86=E5=9C=B0=E5=9B=BE=E5=AD=98=E8=BF=9B?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=B3=BB=E7=BB=9F=EF=BC=8C=E5=8E=BB=E6=8E=89?= =?UTF-8?q?=20lcc=5Fmodel=5Ffloor=20/=20lcc=5Fmodel=5Fworld=20=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/galaxis/rcs/RCSService.java | 339 ++++++++++------ .../com/galaxis/rcs/plan/PlanTaskSequence.java | 2 +- .../com/galaxis/rcs/plan/path/NavigationGraph.java | 3 - .../galaxis/rcs/plan/planner/PTRTaskPlanner.java | 2 +- .../com/galaxis/rcs/ptr/AmrMessageHandler.java | 4 +- .../main/java/com/galaxis/rcs/ptr/PtrAgvItem.java | 30 +- .../java/com/galaxis/rcs/ptr/PtrMqttClient.java | 9 +- .../java/com/galaxis/rcs/task/TaskService.java | 6 +- .../main/java/com/yvan/logisticsEnv/EnvConfig.java | 37 ++ .../java/com/yvan/logisticsEnv/EnvPayload.java | 18 - .../java/com/yvan/logisticsEnv/EnvStartParam.java | 29 -- .../java/com/yvan/logisticsEnv/LogisticsEnv.java | 88 ----- .../com/yvan/logisticsEnv/LogisticsEnvManager.java | 30 -- .../java/com/yvan/logisticsModel/BaseItem.java | 6 +- .../java/com/yvan/logisticsModel/ExecutorItem.java | 6 + .../main/java/com/yvan/logisticsModel/Floor.java | 5 + .../com/yvan/logisticsModel/LogisticsRuntime.java | 221 ++++++----- .../logisticsModel/LogisticsRuntimeService.java | 44 ++- .../java/com/yvan/workbench/StartWorkbench.java | 24 +- .../autoconfigure/AppAutoConfiguration.java | 7 - .../autoconfigure/LccAutoConfiguration.java | 31 ++ .../autoconfigure/LccConfigProperties.java | 18 + .../yvan/workbench/controller/EnvController.java | 21 +- .../yvan/workbench/controller/LccController.java | 13 +- .../yvan/workbench/controller/LccModelManager.java | 426 +++++++++++---------- .../yvan/workbench/controller/RcsController.java | 30 +- .../yvan/workbench/model/entity/LccModelFloor.java | 34 -- .../yvan/workbench/model/entity/LccModelWorld.java | 35 -- .../yvan/workbench/model/entity/LccProject.java | 27 ++ .../yvan/workbench/model/entity/LccProjectEnv.java | 15 + .../yvan/workbench/model/query/QLccModelFloor.java | 80 ---- .../yvan/workbench/model/query/QLccModelWorld.java | 86 ----- .../workbench/service/LccAutoStartService.java | 43 +++ .../com/yvan/workbench/service/LccMapService.java | 291 ++++++++++++++ servo/src/main/resources/application-dev.yml | 6 + 35 files changed, 1207 insertions(+), 859 deletions(-) create mode 100644 servo/src/main/java/com/yvan/logisticsEnv/EnvConfig.java delete mode 100644 servo/src/main/java/com/yvan/logisticsEnv/EnvPayload.java delete mode 100644 servo/src/main/java/com/yvan/logisticsEnv/EnvStartParam.java delete mode 100644 servo/src/main/java/com/yvan/logisticsEnv/LogisticsEnv.java delete mode 100644 servo/src/main/java/com/yvan/logisticsEnv/LogisticsEnvManager.java create mode 100644 servo/src/main/java/com/yvan/workbench/autoconfigure/LccAutoConfiguration.java create mode 100644 servo/src/main/java/com/yvan/workbench/autoconfigure/LccConfigProperties.java delete mode 100644 servo/src/main/java/com/yvan/workbench/model/entity/LccModelFloor.java delete mode 100644 servo/src/main/java/com/yvan/workbench/model/entity/LccModelWorld.java create mode 100644 servo/src/main/java/com/yvan/workbench/model/entity/LccProject.java create mode 100644 servo/src/main/java/com/yvan/workbench/model/entity/LccProjectEnv.java delete mode 100644 servo/src/main/java/com/yvan/workbench/model/query/QLccModelFloor.java delete mode 100644 servo/src/main/java/com/yvan/workbench/model/query/QLccModelWorld.java create mode 100644 servo/src/main/java/com/yvan/workbench/service/LccAutoStartService.java create mode 100644 servo/src/main/java/com/yvan/workbench/service/LccMapService.java diff --git a/servo/src/main/java/com/galaxis/rcs/RCSService.java b/servo/src/main/java/com/galaxis/rcs/RCSService.java index 8ae56f4..51edb40 100644 --- a/servo/src/main/java/com/galaxis/rcs/RCSService.java +++ b/servo/src/main/java/com/galaxis/rcs/RCSService.java @@ -2,38 +2,29 @@ package com.galaxis.rcs; import com.galaxis.rcs.common.entity.AddTaskRequest; import com.galaxis.rcs.common.entity.AddTaskResult; -import com.galaxis.rcs.common.entity.RcsTaskBiz; -import com.galaxis.rcs.common.entity.StoreLocation; -import com.galaxis.rcs.common.enums.BizTaskStatus; -import com.galaxis.rcs.common.enums.BizTaskType; import com.galaxis.rcs.common.enums.LCCDirection; -import com.galaxis.rcs.plan.PlanTaskSequence; -import com.galaxis.rcs.plan.TaskPlannerFactory; -import com.galaxis.rcs.plan.planner.Planner; -import com.galaxis.rcs.plan.task.CarryTask; -import com.galaxis.rcs.ptr.AmrMessageHandler; +import com.galaxis.rcs.connector.cl2.Cl2Item; +import com.galaxis.rcs.plan.path.PathUtils; import com.google.common.base.Strings; -import com.google.common.collect.Maps; -import com.yvan.logisticsEnv.EnvPayload; -import com.yvan.logisticsEnv.EnvStartParam; import com.yvan.logisticsModel.LogisticsRuntime; import com.yvan.logisticsModel.LogisticsRuntimeService; -import com.yvan.workbench.model.query.QLccModelFloor; +import com.yvan.logisticsModel.StaticItem; +import com.yvan.workbench.SpringContext; +import com.yvan.workbench.model.entity.LccProject; +import com.yvan.workbench.service.LccMapService; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.clever.core.BannerUtils; import org.clever.core.Conv; import org.clever.core.json.JsonWrapper; +import org.clever.core.mapper.JacksonMapper; import org.clever.data.jdbc.DaoFactory; import org.clever.data.jdbc.QueryDSL; import org.clever.data.jdbc.querydsl.utils.QueryDslUtils; -import java.lang.management.ManagementFactory; -import java.net.InetAddress; +import java.util.List; import java.util.Map; -import static com.galaxis.rcs.common.query.QLccEnvInfo.lccEnvInfo; -import static com.yvan.workbench.model.query.QLccModelFloor.lccModelFloor; +import static com.galaxis.rcs.common.query.QLccBasExecutor.lccBasExecutor; /** * RCS 对外API调用类 @@ -44,126 +35,248 @@ public class RCSService { } /** - * 初始化RCS服务 + * 添加任务 */ - public void init() { - this.createAutoStartEnv(); + public static AddTaskResult addTask(String projectUuid, long envId, AddTaskRequest request) { + AddTaskResult result = new AddTaskResult(); + + LogisticsRuntime logisticsRuntime = LogisticsRuntimeService.INSTANCE.getByProjectEnv(projectUuid, envId); + String bizTaskId = logisticsRuntime.taskService.addBizTask(request); + result.bizTaskId = bizTaskId; + + return result; } - /** - * 加载所有自动启动的环境和楼层数据 - * 并启动对应的环境 - */ @SneakyThrows - private void createAutoStartEnv() { - QueryDSL queryDSL = DaoFactory.getQueryDSL(); - var list = queryDSL.select(QueryDslUtils.linkedMap( - lccEnvInfo.envId, - lccEnvInfo.worldId, - lccEnvInfo.envPayload, - lccModelFloor.catalogCode + public static void projectStart(String projectUuid, Long envId) { + LogisticsRuntime runtime = LogisticsRuntimeService.INSTANCE.getByProjectEnv(projectUuid, envId); - )).from(lccEnvInfo) - .innerJoin(lccModelFloor).on( - lccEnvInfo.worldId.eq(lccModelFloor.projectUuid) - ) - .where(lccEnvInfo.autoStart.eq(true)) - .where(lccModelFloor.autoStart.eq(true)) - .fetch(); + if (runtime != null) { + log.info("Project {} with envId {} is already started.", projectUuid, envId); + return; + } + + var lccMapService = SpringContext.HOLDER.getBean(LccMapService.class); + var project = lccMapService.getProjectById(projectUuid); + if (project == null) { + throw new RuntimeException("Project not found: " + projectUuid); + } + if (project.getOtherData().isEmpty()) { + throw new RuntimeException("Project " + projectUuid + " has no data."); + } + if (project.getDirectoryData().length == 0) { + throw new RuntimeException("Project " + projectUuid + " has no catalog data."); + } - Map runtimePayLoad = Maps.newHashMap(); - for (Map map : list) { - long envId = Conv.asLong(map.get("env_id")); - String projectUuid = Conv.asString(map.get("world_id")); - String envPayload = Conv.asString(map.get("env_payload")); - String catalogCode = Conv.asString(map.get("catalog_code")); + var env = lccMapService.getEnvById(projectUuid, envId); + if (env.getEnvConfig() == null) { + throw new RuntimeException("Project " + projectUuid + " envId " + envId + " has no envConfig data."); + } - EnvPayload payload; - if (!Strings.isNullOrEmpty(envPayload) && !"N/A".equals(envPayload)) { - payload = new JsonWrapper(envPayload).asObject(EnvPayload.class); - } else { - payload = new EnvPayload(); - } + runtime = LogisticsRuntimeService.INSTANCE.create(project, env); - var runtime = loadFloor(projectUuid, catalogCode, envId, payload); - runtimePayLoad.put(runtime, payload); + // 获取所有楼层数据 + for (LccProject.CatalogGroup floor : project.getDirectoryData()) { + for (LccProject.CatalogItem item : floor.getItems()) { + String catalogCode = item.getCatalogCode(); + if (Strings.isNullOrEmpty(catalogCode)) { + continue; + } + // 读取楼层数据 + String floorJson = lccMapService.loadFloor(projectUuid, catalogCode); + if (!Strings.isNullOrEmpty(floorJson)) { + List> items = JacksonMapper.getInstance().fromJson(floorJson, List.class); + runtime.loadMap(catalogCode, items); + } + } } - String[] logBanners = new String[runtimePayLoad.entrySet().size()]; - int i = 0; - for (Map.Entry entry : runtimePayLoad.entrySet()) { - var runtime = entry.getKey(); - var envPayload = entry.getValue(); + // 读取所有的库存 - logBanners[i++] = String.format("projectUUID: %s, envId: %d", runtime.projectUUID, runtime.envId); + // 读取所有的执行器 - EnvStartParam param = new EnvStartParam(); - param.setTimeRate(1); - param.setVirtual(false); - param.setEnvPayload(envPayload); - param.setClientId(InetAddress.getLocalHost().getHostName()); - runtime.start(param); - } + // 读取地图上所有的车 + final QueryDSL queryDSL = DaoFactory.getQueryDSL(); + var list = queryDSL.select(QueryDslUtils.linkedMap( + lccBasExecutor.executorId, + lccBasExecutor.virtualLocationAt, + lccBasExecutor.virtualExecutorPayload + )) + .from(lccBasExecutor) + .where(lccBasExecutor.envId.eq(envId)) + .where(lccBasExecutor.isActive.eq(true)) + .fetch(); - BannerUtils.printConfig(log, "RCS服务已启动", logBanners); - } + for (var item : list) { + // 车的属性 + String payload = Conv.asString(item.get("virtual_executor_payload")); + /** + * { + * "id": "3", + * "t": "cl2", + * "v": true, + * "dt": { + * "ptrWidth": 1.5, + * "ptrDepth": 1.5, + * "ptrHeight": 1.98 + * } + * } + */ + JsonWrapper jwPayload = new JsonWrapper(payload); - /** - * 添加任务, 示例 - *
-     * {
-     *   type: 'carry',          // 任务类型
-     *   agv: 'cl2',             // 指定车辆
-     *   lpn: 'pallet1124',      // 托盘ID, 用于校验
-     *   from: '27',             // 起始点位
-     *   priority: 1,            // 优先级
-     *   from: {
-     *     item: '27', bay: 0, level: 1, cell: 0 // 起始点位的详细信息
-     *   },
-     *   to:{
-     *     item: '20'
-     *   }
-     * }
-     * 
- */ - public static AddTaskResult addTask(String projectUuid, long envId, AddTaskRequest request) { - AddTaskResult result = new AddTaskResult(); + String wayPointId = ""; + String direction = ""; + if (env.getIsVirtual()) { + // 如果是虚拟车,需要读取虚拟姿态 + String virtualLocationAt = Conv.asString(item.get("virtual_location_at")); + if (Strings.isNullOrEmpty(virtualLocationAt)) { + log.warn("Virtual location at is null for executorId: {}", item.get("executor_id")); + continue; // 跳过虚拟位置为空的执行器 + } - LogisticsRuntime logisticsRuntime = LogisticsRuntimeService.INSTANCE.getByProjectEnv(projectUuid, envId); - String bizTaskId = logisticsRuntime.taskService.addBizTask(request); - result.bizTaskId = bizTaskId; + if (!virtualLocationAt.contains(":")) { + log.warn("Invalid virtual_location_at format: {}, id:{}", virtualLocationAt, item.get("executor_id")); + continue; // 跳过格式不正确的虚拟位置 + } - return result; - } + String[] parts = virtualLocationAt.split(":"); + wayPointId = parts[0]; + direction = parts[1]; - /** - * 加载项目的楼层数据 (只加载物理生产环境) - */ - @SneakyThrows - public static LogisticsRuntime loadFloor(String projectUUID, String catalogCode, long envId, EnvPayload envPayload) { - LogisticsRuntime runtime = LogisticsRuntimeService.INSTANCE.getByProjectEnv(projectUUID, envId); - if (runtime == null) { - QueryDSL queryDsl = DaoFactory.getQueryDSL(); - String floorPayload = queryDsl.select(QLccModelFloor.lccModelFloor.items) - .from(QLccModelFloor.lccModelFloor) - .where(QLccModelFloor.lccModelFloor.projectUuid.eq(projectUUID)) - .where(QLccModelFloor.lccModelFloor.catalogCode.eq(catalogCode)) - .fetchFirst(); + if(Strings.isNullOrEmpty(wayPointId)){ + log.warn("wayPointId is empty for executorId: {}", item.get("executor_id")); + continue; // 跳过wayPointId为空的执行器 + } - if (Strings.isNullOrEmpty(floorPayload)) { - throw new RuntimeException("not found floor data for projectUUID: " + projectUUID + ", catalogCode: " + catalogCode); + if(Strings.isNullOrEmpty(direction)) { + log.warn("direction is empty for executorId: {}", item.get("executor_id")); + continue; // 跳过方向为空的执行器 + } } - JsonWrapper jw = new JsonWrapper("{ \"catalogCode\": \"" + catalogCode + "\", \"t\": \"floor\", \"items\": " + floorPayload + "}"); + switch (jwPayload.asStr("t")) { + case "cl2": + case "clx": + // 处理 CL2 或 CLX 类型的执行器 + // 车所在的标记位置,及方向 11_4:RIGHT + var eitem = new Cl2Item(runtime, (Map) jwPayload.getInnerMap()); + + // 找到地标位置 + StaticItem staticItem = runtime.getStaticItemById(wayPointId); + if (staticItem != null) { + eitem.logicX = staticItem.logicX; + eitem.logicY = staticItem.logicY; + eitem.direction = PathUtils.convertDirectionToPtrDiretion(LCCDirection.fromString(direction)); - runtime = LogisticsRuntimeService.INSTANCE.createEnv(projectUUID, envId); + } else { + log.warn("Static item not found for wayPointId: {}", wayPointId); + } + runtime.executorItemMap.put(eitem.getId(), eitem); + break; - log.info("loadFloor: projectUUID={}, catalogCode={}, envId={}", projectUUID, catalogCode, envId); - runtime.loadMap(jw); + default: + log.warn("Unknown executor type: {}", jwPayload.asStr("t")); + continue; // 跳过未知类型 + } } - return runtime; + + runtime.start(); } +// /** +// * 初始化RCS服务 +// */ +// public void init() { +// this.createAutoStartEnv(); +// } +// +// /** +// * 加载所有自动启动的环境和楼层数据 +// * 并启动对应的环境 +// */ +// @SneakyThrows +// private void createAutoStartEnv() { +// QueryDSL queryDSL = DaoFactory.getQueryDSL(); +// var list = queryDSL.select(QueryDslUtils.linkedMap( +// lccEnvInfo.envId, +// lccEnvInfo.worldId, +// lccEnvInfo.envPayload, +// lccModelFloor.catalogCode +// +// )).from(lccEnvInfo) +// .innerJoin(lccModelFloor).on( +// lccEnvInfo.worldId.eq(lccModelFloor.projectUuid) +// ) +// .where(lccEnvInfo.autoStart.eq(true)) +// .where(lccModelFloor.autoStart.eq(true)) +// .fetch(); +// +// Map runtimePayLoad = Maps.newHashMap(); +// for (Map map : list) { +// long envId = Conv.asLong(map.get("env_id")); +// String projectUuid = Conv.asString(map.get("world_id")); +// String envPayload = Conv.asString(map.get("env_payload")); +// String catalogCode = Conv.asString(map.get("catalog_code")); +// +// EnvConfig payload; +// if (!Strings.isNullOrEmpty(envPayload) && !"N/A".equals(envPayload)) { +// payload = new JsonWrapper(envPayload).asObject(EnvConfig.class); +// } else { +// payload = new EnvConfig(); +// } +// +// var runtime = loadFloor(projectUuid, catalogCode, envId, payload); +// runtimePayLoad.put(runtime, payload); +// } +// +// String[] logBanners = new String[runtimePayLoad.entrySet().size()]; +// int i = 0; +// for (Map.Entry entry : runtimePayLoad.entrySet()) { +// var runtime = entry.getKey(); +// var envPayload = entry.getValue(); +// +// logBanners[i++] = String.format("projectUUID: %s, envId: %d", runtime.projectUUID, runtime.envId); +// +// EnvStartParam param = new EnvStartParam(); +// param.setTimeRate(1); +// param.setVirtual(false); +// param.setEnvConfig(envPayload); +// param.setClientId(InetAddress.getLocalHost().getHostName()); +// runtime.start(param); +// } +// +// BannerUtils.printConfig(log, "RCS服务已启动", logBanners); +// } + + +// /** +// * 加载项目的楼层数据 (只加载物理生产环境) +// */ +// @SneakyThrows +// public static LogisticsRuntime loadFloor(String projectUUID, String catalogCode, long envId, EnvConfig envConfig) { +// LogisticsRuntime runtime = LogisticsRuntimeService.INSTANCE.getByProjectEnv(projectUUID, envId); +// if (runtime == null) { +// QueryDSL queryDsl = DaoFactory.getQueryDSL(); +// String floorPayload = queryDsl.select(QLccModelFloor.lccModelFloor.items) +// .from(QLccModelFloor.lccModelFloor) +// .where(QLccModelFloor.lccModelFloor.projectUuid.eq(projectUUID)) +// .where(QLccModelFloor.lccModelFloor.catalogCode.eq(catalogCode)) +// .fetchFirst(); +// +// if (Strings.isNullOrEmpty(floorPayload)) { +// throw new RuntimeException("not found floor data for projectUUID: " + projectUUID + ", catalogCode: " + catalogCode); +// } +// +// JsonWrapper jw = new JsonWrapper("{ \"catalogCode\": \"" + catalogCode + "\", \"t\": \"floor\", \"items\": " + floorPayload + "}"); +// +// runtime = LogisticsRuntimeService.INSTANCE.createEnv(projectUUID, envId); +// +// log.info("loadFloor: projectUUID={}, catalogCode={}, envId={}", projectUUID, catalogCode, envId); +// runtime.loadMap(jw); +// } +// return runtime; +// } + // public static Object runPath() { // String executorId = "10"; // 执行器ID // String lpn = "pallet1124"; 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 b26a8cb..f3b68f2 100644 --- a/servo/src/main/java/com/galaxis/rcs/plan/PlanTaskSequence.java +++ b/servo/src/main/java/com/galaxis/rcs/plan/PlanTaskSequence.java @@ -45,7 +45,7 @@ public class PlanTaskSequence { RcsTaskPlan planTask = new RcsTaskPlan(); planTask.setPlanTaskId(snowFlake.nextId()); planTask.setBizTaskId(this.bizTask.getBizTaskId()); - planTask.setEnvId(logisticsRuntime.logisticsEnv.getEnvId()); + planTask.setEnvId(logisticsRuntime.envId); planTask.setPlanType(planType); planTask.setExecutorId(this.executorId); planTask.setSeq(taskList.size()); diff --git a/servo/src/main/java/com/galaxis/rcs/plan/path/NavigationGraph.java b/servo/src/main/java/com/galaxis/rcs/plan/path/NavigationGraph.java index 595f381..f12daf6 100644 --- a/servo/src/main/java/com/galaxis/rcs/plan/path/NavigationGraph.java +++ b/servo/src/main/java/com/galaxis/rcs/plan/path/NavigationGraph.java @@ -96,9 +96,6 @@ public class NavigationGraph { // 检查可旋转性 boolean rotatable = dt.containsKey("agvRotation"); - if (rotatable) { - log.info("Node {} is rotatable", id); - } // 提取邻居节点 List in = (List) dt.get("in"); diff --git a/servo/src/main/java/com/galaxis/rcs/plan/planner/PTRTaskPlanner.java b/servo/src/main/java/com/galaxis/rcs/plan/planner/PTRTaskPlanner.java index 2bf1c6a..bd43654 100644 --- a/servo/src/main/java/com/galaxis/rcs/plan/planner/PTRTaskPlanner.java +++ b/servo/src/main/java/com/galaxis/rcs/plan/planner/PTRTaskPlanner.java @@ -38,7 +38,7 @@ public class PTRTaskPlanner extends Planner { task.setBizTaskStatus(BizTaskStatus.DEVICE_EXECUTING.toString()); queryDSL.update(rcsTaskBiz) .populate(task) - .set(rcsTaskBiz.updateAt, runtime.logisticsEnv.getCurrentDate()) + .set(rcsTaskBiz.updateAt, runtime.getCurrentDate()) .set(rcsTaskBiz.updateBy, PTRTaskPlanner.class.getName()) .where(rcsTaskBiz.bizTaskId.eq(task.getBizTaskId())) .execute(); diff --git a/servo/src/main/java/com/galaxis/rcs/ptr/AmrMessageHandler.java b/servo/src/main/java/com/galaxis/rcs/ptr/AmrMessageHandler.java index 1f541f4..4f0120c 100644 --- a/servo/src/main/java/com/galaxis/rcs/ptr/AmrMessageHandler.java +++ b/servo/src/main/java/com/galaxis/rcs/ptr/AmrMessageHandler.java @@ -9,7 +9,7 @@ import com.galaxis.rcs.ptr.sendEntity.*; import com.google.common.base.Splitter; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import com.yvan.logisticsEnv.EnvPayload; +import com.yvan.logisticsEnv.EnvConfig; import com.yvan.logisticsModel.LogisticsRuntime; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -81,7 +81,7 @@ public class AmrMessageHandler { this.runtime = runtime; } - public void start(EnvPayload.MqttConfig mqttConfig, String clientId) { + public void start(EnvConfig.MqttConfig mqttConfig, String clientId) { this.ptrMqttClient = new PtrMqttClient(this, mqttConfig, clientId); } 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 3192dad..bb1ace2 100644 --- a/servo/src/main/java/com/galaxis/rcs/ptr/PtrAgvItem.java +++ b/servo/src/main/java/com/galaxis/rcs/ptr/PtrAgvItem.java @@ -107,16 +107,38 @@ public abstract class PtrAgvItem extends ExecutorItem { this.amrMessageHandler = logisticsRuntime.amrMessageHandler; } + + @Override + public void stop() { + // 停止连接器线程 + stopConnector(); + // 清理任务序列 + if (planTaskSequence != null) { + planTaskSequence = null; + } + // 清理设备任务队列 + deviceTaskQueue.clear(); + // 清理运行中的设备任务列表 + runningDeviceTaskList.clear(); + // 更新Redis状态 + updateRedisStatus(); + } + + @Override + public boolean isRunning() { + return connectorThread.isRunning(); + } + public abstract RcsConfigMessage getConfig(); - @SneakyThrows - public synchronized void initialize() { + @Override + public void start() { this.amrMessageHandler.registeHeartBeatSet(this); // 查询当前状态 requestCurrentStatus(); - this.isInitialized = true; + this.isRunning = true; this.startConnector(); } @@ -449,7 +471,6 @@ public abstract class PtrAgvItem extends ExecutorItem { public void startConnector() { if (!connectorThread.isRunning()) { connectorThread.start(); - System.out.println("Connector started for executor: " + this.getId()); } } @@ -458,7 +479,6 @@ public abstract class PtrAgvItem extends ExecutorItem { */ public void stopConnector() { connectorThread.stop(); - System.out.println("Connector stopped for executor: " + this.getId()); } private static final int speed = 1000; diff --git a/servo/src/main/java/com/galaxis/rcs/ptr/PtrMqttClient.java b/servo/src/main/java/com/galaxis/rcs/ptr/PtrMqttClient.java index bf43cb4..0c24f48 100644 --- a/servo/src/main/java/com/galaxis/rcs/ptr/PtrMqttClient.java +++ b/servo/src/main/java/com/galaxis/rcs/ptr/PtrMqttClient.java @@ -1,6 +1,6 @@ package com.galaxis.rcs.ptr; -import com.yvan.logisticsEnv.EnvPayload; +import com.yvan.logisticsEnv.EnvConfig; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.clever.core.BannerUtils; @@ -9,14 +9,13 @@ import org.eclipse.paho.mqttv5.common.MqttException; import org.eclipse.paho.mqttv5.common.MqttMessage; import org.eclipse.paho.mqttv5.common.packet.MqttProperties; -import java.net.InetAddress; import java.nio.charset.StandardCharsets; import java.util.concurrent.CountDownLatch; @Slf4j public class PtrMqttClient implements MqttCallback { public final AmrMessageHandler amrMessageHandler; - public final EnvPayload.MqttConfig mqttConfig; + public final EnvConfig.MqttConfig mqttConfig; private final MqttClient clientForSend; private final MqttClient client; private final String clientId; @@ -24,7 +23,7 @@ public class PtrMqttClient implements MqttCallback { @SneakyThrows - public PtrMqttClient(AmrMessageHandler handler, EnvPayload.MqttConfig mqttConfig, String clientId) { + public PtrMqttClient(AmrMessageHandler handler, EnvConfig.MqttConfig mqttConfig, String clientId) { this.amrMessageHandler = handler; this.mqttConfig = mqttConfig; this.clientId = clientId; @@ -75,7 +74,7 @@ public class PtrMqttClient implements MqttCallback { @SneakyThrows public static void main(String[] args) { - var config = new EnvPayload.MqttConfig(); + var config = new EnvConfig.MqttConfig(); config.setBrokerUrl("tcp://10.10.203.239:1885"); config.setUsername("admin"); config.setPassword("admin"); diff --git a/servo/src/main/java/com/galaxis/rcs/task/TaskService.java b/servo/src/main/java/com/galaxis/rcs/task/TaskService.java index b7677f4..b5892b4 100644 --- a/servo/src/main/java/com/galaxis/rcs/task/TaskService.java +++ b/servo/src/main/java/com/galaxis/rcs/task/TaskService.java @@ -40,7 +40,7 @@ public class TaskService { this.waitingTaskList = queryDSL.select(rcsTaskBiz) .from(rcsTaskBiz) .where(rcsTaskBiz.bizTaskStatus.eq(BizTaskStatus.WAITING_FOR_DISPATCH.toString())) - .where(rcsTaskBiz.envId.eq(logisticsRuntime.logisticsEnv.getEnvId())) + .where(rcsTaskBiz.envId.eq(logisticsRuntime.envId)) .orderBy(rcsTaskBiz.priority.desc()) .orderBy(rcsTaskBiz.bizTaskId.asc()) .fetch(); @@ -83,7 +83,7 @@ public class TaskService { RcsTaskBiz task = new RcsTaskBiz(); task.setBizTaskId(taskId); - task.setEnvId(logisticsRuntime.logisticsEnv.getEnvId()); + task.setEnvId(logisticsRuntime.envId); task.setBizType(request.type.toString()); task.setLpn(request.lpn); task.setPriority(request.priority); @@ -94,7 +94,7 @@ public class TaskService { task.setBizTaskErrorInfo("N/A"); task.setBizTaskDescription(request.description); task.setBizTaskStatus(BizTaskStatus.WAITING_FOR_DISPATCH.toString()); - task.setCreateAt(logisticsRuntime.logisticsEnv.getCurrentDate()); + task.setCreateAt(logisticsRuntime.getCurrentDate()); task.setCreateBy(request.createBy); queryDSL.insert(rcsTaskBiz) diff --git a/servo/src/main/java/com/yvan/logisticsEnv/EnvConfig.java b/servo/src/main/java/com/yvan/logisticsEnv/EnvConfig.java new file mode 100644 index 0000000..5e19ffa --- /dev/null +++ b/servo/src/main/java/com/yvan/logisticsEnv/EnvConfig.java @@ -0,0 +1,37 @@ +package com.yvan.logisticsEnv; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class EnvConfig implements Serializable { + + private MqttConfig mqtt; + + private MysqlConfig mysql; + + private RedisConfig redis; + + @Data + public static class MqttConfig { + private String brokerUrl; + private String username; + private String password; + private String websocket; + } + + @Data + public static class MysqlConfig { + private String jdbcUrl; + private String username; + private String password; + } + + @Data + public static class RedisConfig { + private String host; + private int port; + private String password; + } +} diff --git a/servo/src/main/java/com/yvan/logisticsEnv/EnvPayload.java b/servo/src/main/java/com/yvan/logisticsEnv/EnvPayload.java deleted file mode 100644 index 2692634..0000000 --- a/servo/src/main/java/com/yvan/logisticsEnv/EnvPayload.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.yvan.logisticsEnv; - -import lombok.Data; - -import java.io.Serializable; - -@Data -public class EnvPayload implements Serializable { - - private MqttConfig mqtt; - - @Data - public static class MqttConfig { - private String brokerUrl; - private String username; - private String password; - } -} diff --git a/servo/src/main/java/com/yvan/logisticsEnv/EnvStartParam.java b/servo/src/main/java/com/yvan/logisticsEnv/EnvStartParam.java deleted file mode 100644 index b9ed166..0000000 --- a/servo/src/main/java/com/yvan/logisticsEnv/EnvStartParam.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.yvan.logisticsEnv; - -import lombok.Getter; -import lombok.Setter; - -/** - * 环境启动参数 - */ -@Getter -@Setter -public class EnvStartParam { - - public String clientId; - - /** - * 是否虚拟仿真环境 - */ - private boolean isVirtual; - - /** - * 时间倍速(仿真环境专用) - */ - private Integer timeRate; - - /** - * Env环境配置 - */ - private EnvPayload envPayload; -} diff --git a/servo/src/main/java/com/yvan/logisticsEnv/LogisticsEnv.java b/servo/src/main/java/com/yvan/logisticsEnv/LogisticsEnv.java deleted file mode 100644 index c1b0347..0000000 --- a/servo/src/main/java/com/yvan/logisticsEnv/LogisticsEnv.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.yvan.logisticsEnv; - -import com.galaxis.rcs.common.enums.EnvStatus; -import com.google.common.collect.Lists; -import com.yvan.logisticsModel.ExecutorItem; -import lombok.Getter; -import lombok.Setter; - -import java.util.Date; -import java.util.List; - -/** - * 物流仓储环境上下文 - */ -@Getter -@Setter -public class LogisticsEnv { - /** - * 项目ID - */ - public final String projectUUID; - - /** - * 环境ID - */ - public final long envId; - - private EnvStartParam startParam; - - - /** - * 是否为虚拟环境 - */ - private boolean isVirtual; - - /** - * 是否正在运行 - */ - private boolean isRunning; - - /** - * 环境开始时间 - */ - private long startTime; - - /** - * 环境停止时间 - */ - private long stopTime; - - /** - * 时间流速倍率 - */ - private int timeRate; - - public LogisticsEnv(String projectUUID, long envId) { - this.projectUUID = projectUUID; - this.envId = envId; - } - - public void start(EnvStartParam param) { - if (this.isRunning) { - throw new IllegalStateException("环境已经在运行中"); - } - this.startParam = param; - this.isRunning = true; - this.startTime = System.currentTimeMillis(); - this.stopTime = 0L; - this.timeRate = this.startParam.getTimeRate(); - } - - public long currentTimeMillis() { - if (!this.isVirtual) { - // 正式环境,返回系统当前时间 - return System.currentTimeMillis(); - } - if (!this.isRunning) { - return this.stopTime; - } - - long realCost = System.currentTimeMillis() - this.startTime; - return this.startTime + (realCost * this.timeRate); - } - - public Date getCurrentDate() { - return new Date(this.currentTimeMillis()); - } -} diff --git a/servo/src/main/java/com/yvan/logisticsEnv/LogisticsEnvManager.java b/servo/src/main/java/com/yvan/logisticsEnv/LogisticsEnvManager.java deleted file mode 100644 index 5e6e480..0000000 --- a/servo/src/main/java/com/yvan/logisticsEnv/LogisticsEnvManager.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.yvan.logisticsEnv; - -import java.util.HashMap; -import java.util.Map; - -public class LogisticsEnvManager { - - Map envs = new HashMap<>(); - - /** - * 启动参数 - */ - public void startEnv(EnvStartParam param) { - - } - - /** - * 复制环境, 返回新环境ID - */ - public String cloneEnv(String envId) { - throw new RuntimeException(); - } - - /** - * 重置环境 - */ - public String resetEnv(String envId) { - throw new RuntimeException(); - } -} diff --git a/servo/src/main/java/com/yvan/logisticsModel/BaseItem.java b/servo/src/main/java/com/yvan/logisticsModel/BaseItem.java index 7c094ba..932f941 100644 --- a/servo/src/main/java/com/yvan/logisticsModel/BaseItem.java +++ b/servo/src/main/java/com/yvan/logisticsModel/BaseItem.java @@ -33,10 +33,10 @@ public abstract class BaseItem { public final LogisticsRuntime runtime; @JsonIgnore - public boolean isInitialized = false; + public boolean isRunning = false; - public synchronized void initialize() { - this.isInitialized = true; + public synchronized void start() { + this.isRunning = true; } public BaseItem(LogisticsRuntime runtime, Map map) { diff --git a/servo/src/main/java/com/yvan/logisticsModel/ExecutorItem.java b/servo/src/main/java/com/yvan/logisticsModel/ExecutorItem.java index 848fdf2..cfc54fa 100644 --- a/servo/src/main/java/com/yvan/logisticsModel/ExecutorItem.java +++ b/servo/src/main/java/com/yvan/logisticsModel/ExecutorItem.java @@ -27,4 +27,10 @@ public abstract class ExecutorItem extends BaseItem { abstract public boolean isFree(); abstract public void dispatchTask(PlanTaskSequence taskSequence); + + abstract public void stop(); + + abstract public void start(); + + abstract public boolean isRunning(); } diff --git a/servo/src/main/java/com/yvan/logisticsModel/Floor.java b/servo/src/main/java/com/yvan/logisticsModel/Floor.java index 60708b5..537cb38 100644 --- a/servo/src/main/java/com/yvan/logisticsModel/Floor.java +++ b/servo/src/main/java/com/yvan/logisticsModel/Floor.java @@ -19,4 +19,9 @@ public class Floor { public Floor(String id) { this.id = id; } + + @Override + public String toString() { + return id + "(" + itemMap.size() + ")"; + } } diff --git a/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntime.java b/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntime.java index 5c88674..1d5abb2 100644 --- a/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntime.java +++ b/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntime.java @@ -10,18 +10,21 @@ import com.galaxis.rcs.ptr.AgvEventManager; import com.galaxis.rcs.ptr.AmrMessageHandler; import com.galaxis.rcs.task.TaskDispatchFactory; import com.galaxis.rcs.task.TaskService; +import com.google.common.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import com.yvan.logisticsEnv.EnvStartParam; -import com.yvan.logisticsEnv.LogisticsEnv; +import com.yvan.workbench.model.entity.LccProject; +import com.yvan.workbench.model.entity.LccProjectEnv; import lombok.extern.slf4j.Slf4j; +import org.clever.core.BannerUtils; import org.clever.core.Conv; import org.clever.core.json.JsonWrapper; import org.clever.data.jdbc.DaoFactory; import org.clever.data.jdbc.QueryDSL; import org.clever.data.jdbc.querydsl.utils.QueryDslUtils; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; @@ -33,14 +36,55 @@ import static com.galaxis.rcs.common.query.QLccBasExecutor.lccBasExecutor; */ @Slf4j public class LogisticsRuntime { - /** - * 物流执行环境 + * 项目UUID */ - public final LogisticsEnv logisticsEnv; public final String projectUUID; + /** + * 环境ID + */ public final long envId; /** + * 项目 + */ + public final LccProject project; + + /** + * 环境 + */ + public final LccProjectEnv env; + + /** + * 是否为虚拟环境 + */ + public final boolean isVirtual; + + /** + * 是否为虚拟环境 + */ + public final String clientId; + + /** + * 是否正在运行 + */ + private boolean isRunning; + + /** + * 环境开始时间 + */ + private long startTime; + + /** + * 环境停止时间 + */ + private long stopTime; + + /** + * 时间流速倍率 + */ + private float timeRate; + + /** * 任务服务 */ @JsonIgnore @@ -55,7 +99,7 @@ public class LogisticsRuntime { /** * 物流流动单元(周转箱、托盘、纸箱等) */ - public final Map flowItemMap = Maps.newHashMap(); + public final Map flowItemMap = Maps.newConcurrentMap(); /** * 物流任务执行单元(如拣货台、小车、AGV、堆垛机、人等) @@ -78,10 +122,13 @@ public class LogisticsRuntime { public final AgvEventManager agvEventManager = new AgvEventManager(this); - public LogisticsRuntime(LogisticsEnv logisticsEnv) { - this.logisticsEnv = logisticsEnv; - this.projectUUID = logisticsEnv.getProjectUUID(); - this.envId = logisticsEnv.getEnvId(); + public LogisticsRuntime(LccProject project, LccProjectEnv env, String clientId) { + this.project = project; + this.env = env; + this.projectUUID = project.getProjectUuid(); + this.envId = env.getEnvId(); + this.isVirtual = env.getIsVirtual(); + this.clientId = clientId; } /** @@ -137,16 +184,9 @@ public class LogisticsRuntime { * "items": [] * } * - * - * @param jw 原始 Json 读取器 */ - public void loadMap(JsonWrapper jw) { - if (!"floor".equals(jw.asStr("t"))) { - throw new RuntimeException("loadMap error, not floor type: " + jw.asStr("t")); - } - List> items = (List>) jw.getInnerMap().get("items"); - - Floor floor = new Floor(jw.asStr("catalogCode")); + public void loadMap(String catalogCode, List> items) { + Floor floor = new Floor(catalogCode); this.floorMap.put(floor.id, floor); for (Map itemObject : items) { @@ -175,94 +215,29 @@ public class LogisticsRuntime { } } - // 读取地图上所有的车 - final QueryDSL queryDSL = DaoFactory.getQueryDSL(); - var list = queryDSL.select(QueryDslUtils.linkedMap( - lccBasExecutor.executorId, - lccBasExecutor.virtualLocationAt, - lccBasExecutor.virtualExecutorPayload - )) - .from(lccBasExecutor) - .where(lccBasExecutor.envId.eq(envId)) - .where(lccBasExecutor.isActive.eq(true)) - .fetch(); - - for (var item : list) { - // 车的属性 - String payload = Conv.asString(item.get("virtual_executor_payload")); - /** - * { - * "id": "3", - * "t": "cl2", - * "v": true, - * "dt": { - * "ptrWidth": 1.5, - * "ptrDepth": 1.5, - * "ptrHeight": 1.98 - * } - * } - */ - JsonWrapper jwPayload = new JsonWrapper(payload); - - switch (jwPayload.asStr("t")) { - case "cl2": - case "clx": - // 处理 CL2 或 CLX 类型的执行器 - // 车所在的标记位置,及方向 11_4:RIGHT - if (Conv.asString(item.get("virtual_location_at")).contains(":")) { - String[] parts = Conv.asString(item.get("virtual_location_at")).split(":"); - if (parts.length == 2) { - String wayPointId = parts[0]; - String direction = parts[1]; - var eitem = new Cl2Item(this, (Map) jwPayload.getInnerMap()); - - // 找到地标位置 - StaticItem staticItem = this.getStaticItemById(wayPointId); - if (staticItem != null) { - eitem.logicX = staticItem.logicX; - eitem.logicY = staticItem.logicY; - eitem.direction = PathUtils.convertDirectionToPtrDiretion(LCCDirection.fromString(direction)); - this.executorItemMap.put(eitem.getId(), eitem); - - } else { - log.warn("Static item not found for wayPointId: {}", wayPointId); - continue; // 跳过未找到的地标 - } - - - } else { - log.warn("Invalid virtual_location_at format: {}, id:{}", item.get("virtual_location_at"), item.get("id")); - continue; - } - - } else { - log.warn("Invalid virtual_location_at is null, id:{}", item.get("id")); - continue; - } - - break; - default: - log.warn("Unknown executor type: {}", jwPayload.asStr("t")); - continue; // 跳过未知类型 - } - } } /** * 启动物流环境 */ - public void start(EnvStartParam param) { + public void start() { // 启动环境计时 - this.logisticsEnv.start(param); + if (this.isRunning) { + throw new RuntimeException("environment is already running"); + } + this.isRunning = true; + this.startTime = System.currentTimeMillis(); + this.stopTime = 0L; + this.timeRate = 1.0f; // 启动 MQTT 监听 - this.amrMessageHandler.start(param.getEnvPayload().getMqtt(), param.clientId); + this.amrMessageHandler.start(this.env.getEnvConfig().getMqtt(), this.clientId); // 开启所有机器人的任务处理 Set executorTypes = Sets.newHashSet(); for (ExecutorItem executorItem : executorItemMap.values()) { - executorItem.initialize(); executorTypes.add(executorItem.getT()); + executorItem.start(); log.info("Executor {} started", executorItem.getId()); } @@ -275,10 +250,21 @@ public class LogisticsRuntime { // 启动任务指派服务 this.taskDispatchFactory.startPolling(); + + BannerUtils.printConfig(log, "LogisticsRuntime Start Success", new String[]{ + "projectUUID: " + this.projectUUID, + "envId: " + this.envId, + "virtual: " + this.isVirtual, + "floor: " + Joiner.on(",").join(this.floorMap.values().stream().map(Floor::toString).toList()), + "mqtt: " + this.env.getEnvConfig().getMqtt().getBrokerUrl(), + "clientId: " + this.clientId, + "executors: " + this.executorItemMap.size(), + "flowItems: " + this.flowItemMap.size(), + }); } public boolean isRunning() { - return this.logisticsEnv.isRunning(); + return this.isRunning; } /** @@ -289,4 +275,49 @@ public class LogisticsRuntime { .flatMap(floor -> floor.itemMap.values().stream()) .iterator(); } + + public long currentTimeMillis() { + if (!this.isVirtual) { + // 正式环境,返回系统当前时间 + return System.currentTimeMillis(); + } + if (!this.isRunning) { + return this.stopTime; + } + + long realCost = System.currentTimeMillis() - this.startTime; + return Conv.asLong(this.startTime + (realCost * this.timeRate)); + } + + public Date getCurrentDate() { + return new Date(this.currentTimeMillis()); + } + + public void stop() { + if (!this.isRunning) { + throw new RuntimeException("environment is not running"); + } + this.isRunning = false; + this.stopTime = System.currentTimeMillis(); + + // 停止任务指派服务 + this.taskDispatchFactory.stopPolling(); + + // 停止所有机器人的任务处理 + for (ExecutorItem executorItem : executorItemMap.values()) { + executorItem.stop(); + log.info("Executor {} stopped", executorItem.getId()); + } + + // 停止 MQTT 监听 + this.amrMessageHandler.stop(); + + BannerUtils.printConfig(log, "LogisticsRuntime stop.", new String[]{ + "projectUUID: " + this.projectUUID, + "envId: " + this.envId, + "virtual: " + this.isVirtual, + "stopTime: " + new Date(this.stopTime), + "timeRate: " + this.timeRate, + }); + } } diff --git a/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntimeService.java b/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntimeService.java index b6478db..274c4a0 100644 --- a/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntimeService.java +++ b/servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntimeService.java @@ -1,8 +1,11 @@ package com.yvan.logisticsModel; import com.google.common.collect.Maps; -import com.yvan.logisticsEnv.LogisticsEnv; +import com.yvan.workbench.model.entity.LccProject; +import com.yvan.workbench.model.entity.LccProjectEnv; +import lombok.SneakyThrows; +import java.net.InetAddress; import java.util.Map; /** @@ -13,6 +16,36 @@ public class LogisticsRuntimeService { private static final Object LOCK = new Object(); private final Map runtimeMap = Maps.newHashMap(); + public boolean isRunning() { + synchronized (LOCK) { + for (LogisticsRuntime runtime : runtimeMap.values()) { + if (runtime.isRunning()) { + return true; + } + } + return false; + } + } + + public void stopAll() { + synchronized (LOCK) { + for (LogisticsRuntime runtime : runtimeMap.values()) { + runtime.stop(); + } + runtimeMap.clear(); + } + } + + public void stopByProjectEnv(String projectUUID, long envId) { + synchronized (LOCK) { + final String key = projectUUID + "_" + envId; + LogisticsRuntime runtime = runtimeMap.remove(key); + if (runtime != null) { + runtime.stop(); + } + } + } + /** * 根据项目UUID和环境ID查找 物流运行时 */ @@ -23,16 +56,15 @@ public class LogisticsRuntimeService { } } - public LogisticsRuntime createEnv(String projectUUID, long envId) { + @SneakyThrows + public LogisticsRuntime create(LccProject project, LccProjectEnv env) { synchronized (LOCK) { - final String key = projectUUID + "_" + envId; + final String key = project.getProjectUuid() + "_" + env.getEnvId(); if (runtimeMap.containsKey(key)) { return runtimeMap.get(key); } - LogisticsEnv env = new LogisticsEnv(projectUUID, envId); - env.setVirtual(envId != 1L); - LogisticsRuntime runtime = new LogisticsRuntime(env); + LogisticsRuntime runtime = new LogisticsRuntime(project, env, InetAddress.getLocalHost().getHostName()); runtimeMap.put(key, runtime); return runtime; } diff --git a/servo/src/main/java/com/yvan/workbench/StartWorkbench.java b/servo/src/main/java/com/yvan/workbench/StartWorkbench.java index 320e55b..d40f8a7 100644 --- a/servo/src/main/java/com/yvan/workbench/StartWorkbench.java +++ b/servo/src/main/java/com/yvan/workbench/StartWorkbench.java @@ -12,18 +12,18 @@ import org.springframework.boot.context.event.ApplicationContextInitializedEvent import org.springframework.context.ApplicationListener; @SpringBootApplication( - scanBasePackages = { - "com.galaxis.rcs", - "com.yvan", - "com.yvan.workbench", - }, - exclude = { - DataSourceAutoConfiguration.class, - RedisAutoConfiguration.class, - ElasticsearchRestClientAutoConfiguration.class, - MongoAutoConfiguration.class, - MongoDataAutoConfiguration.class, - } + scanBasePackages = { + "com.galaxis.rcs", + "com.yvan", + "com.yvan.workbench", + }, + exclude = { + DataSourceAutoConfiguration.class, + RedisAutoConfiguration.class, + ElasticsearchRestClientAutoConfiguration.class, + MongoAutoConfiguration.class, + MongoDataAutoConfiguration.class, + } ) @Slf4j public class StartWorkbench { diff --git a/servo/src/main/java/com/yvan/workbench/autoconfigure/AppAutoConfiguration.java b/servo/src/main/java/com/yvan/workbench/autoconfigure/AppAutoConfiguration.java index 9845384..361b3a6 100644 --- a/servo/src/main/java/com/yvan/workbench/autoconfigure/AppAutoConfiguration.java +++ b/servo/src/main/java/com/yvan/workbench/autoconfigure/AppAutoConfiguration.java @@ -61,13 +61,6 @@ public class AppAutoConfiguration { private final Environment environment; @Bean - public RCSService rcsService() { - RCSService rcsService = new RCSService(); - rcsService.init(); - return rcsService; - } - - @Bean public AppBasicsConfig appBasicsConfig() { AppBasicsConfig appBasicsConfig = AppBasicsConfig.create(environment); appBasicsConfig.init(); diff --git a/servo/src/main/java/com/yvan/workbench/autoconfigure/LccAutoConfiguration.java b/servo/src/main/java/com/yvan/workbench/autoconfigure/LccAutoConfiguration.java new file mode 100644 index 0000000..85df021 --- /dev/null +++ b/servo/src/main/java/com/yvan/workbench/autoconfigure/LccAutoConfiguration.java @@ -0,0 +1,31 @@ +package com.yvan.workbench.autoconfigure; + +import com.yvan.workbench.service.LccAutoStartService; +import com.yvan.workbench.service.LccMapService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.AutoConfigureOrder; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; + +@Slf4j +@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE) +@Configuration +@EnableConfigurationProperties(LccConfigProperties.class) +public class LccAutoConfiguration { + @Bean + public LccConfigProperties lccConfigProperties() { + return new LccConfigProperties(); + } + + @Bean + public LccAutoStartService lccAutoStartService() { + return new LccAutoStartService(this.lccConfigProperties()); + } + + @Bean + public LccMapService lccMapService() { + return new LccMapService(this.lccConfigProperties()); + } +} diff --git a/servo/src/main/java/com/yvan/workbench/autoconfigure/LccConfigProperties.java b/servo/src/main/java/com/yvan/workbench/autoconfigure/LccConfigProperties.java new file mode 100644 index 0000000..0d9e092 --- /dev/null +++ b/servo/src/main/java/com/yvan/workbench/autoconfigure/LccConfigProperties.java @@ -0,0 +1,18 @@ +package com.yvan.workbench.autoconfigure; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Data +@ConfigurationProperties(prefix = LccConfigProperties.PREFIX) +public class LccConfigProperties { + public static final String PREFIX = "lcc"; + private String location; + private AutoStartItem[] autoStart; + + @Data + public static class AutoStartItem { + private String projectUuid; + private Long envId; + } +} diff --git a/servo/src/main/java/com/yvan/workbench/controller/EnvController.java b/servo/src/main/java/com/yvan/workbench/controller/EnvController.java index f1ef15d..ec03b0e 100644 --- a/servo/src/main/java/com/yvan/workbench/controller/EnvController.java +++ b/servo/src/main/java/com/yvan/workbench/controller/EnvController.java @@ -2,7 +2,9 @@ package com.yvan.workbench.controller; import com.galaxis.rcs.RCSService; import com.google.common.base.Strings; +import com.yvan.workbench.SpringContext; import com.yvan.workbench.model.entity.Model; +import com.yvan.workbench.service.LccMapService; import org.clever.core.Conv; import org.clever.core.model.response.R; import org.clever.data.jdbc.DaoFactory; @@ -23,11 +25,20 @@ import static com.galaxis.rcs.common.query.QLccInvLpn.lccInvLpn; public class EnvController { static final QueryDSL queryDSL = DaoFactory.getQueryDSL(); - public static Model>> getAllEnv(@RequestBody Map params) { - var list = queryDSL.select(QueryDslUtils.linkedMap(lccEnvInfo)) - .from(lccEnvInfo) - .where(lccEnvInfo.worldId.eq(Conv.asString(params.get("worldId")))) - .fetch(); + public static Model getAllEnv(@RequestBody Map params) { + // var list = queryDSL.select(QueryDslUtils.linkedMap(lccEnvInfo)) + // .from(lccEnvInfo) + // .where(lccEnvInfo.worldId.eq(Conv.asString(params.get("worldId")))) + // .fetch(); + + // 从文件系统中获取所有环境配置 + String worldId = Conv.asString(params.get("worldId")); + if (Strings.isNullOrEmpty(worldId)) { + return Model.newFail("worldId must not be null"); + } + + var lccMapService = SpringContext.HOLDER.getBean(LccMapService.class); + var list = lccMapService.getAllEnv(worldId); return Model.newSuccess(list); } diff --git a/servo/src/main/java/com/yvan/workbench/controller/LccController.java b/servo/src/main/java/com/yvan/workbench/controller/LccController.java index 0caf634..56b64e5 100644 --- a/servo/src/main/java/com/yvan/workbench/controller/LccController.java +++ b/servo/src/main/java/com/yvan/workbench/controller/LccController.java @@ -1,6 +1,15 @@ package com.yvan.workbench.controller; -public class LccController { - +import com.yvan.workbench.SpringContext; +import com.yvan.workbench.service.LccMapService; +import org.clever.core.model.response.R; +/** + * /api/workbench/LccController@get1 + */ +public class LccController { + public static R getAllProjects() { + var mapService = SpringContext.HOLDER.getBean(LccMapService.class); + return R.success(mapService.getAllProjects()); + } } 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 2c870ab..6107a1c 100644 --- a/servo/src/main/java/com/yvan/workbench/controller/LccModelManager.java +++ b/servo/src/main/java/com/yvan/workbench/controller/LccModelManager.java @@ -3,21 +3,18 @@ package com.yvan.workbench.controller; import com.galaxis.rcs.common.entity.LccBasLocation; import com.galaxis.rcs.common.entity.StoreLocation; import com.galaxis.rcs.ptr.JacksonUtils; -import com.yvan.workbench.model.entity.LccModelFloor; -import com.yvan.workbench.model.entity.LccModelWorld; +import com.yvan.workbench.SpringContext; +import com.yvan.workbench.model.entity.LccProject; +import com.yvan.workbench.service.LccMapService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.clever.core.Assert; import org.clever.core.Conv; -import org.clever.core.id.SnowFlake; -import org.clever.core.json.JsonArrayWrapper; -import org.clever.core.json.JsonWrapper; import org.clever.core.mapper.JacksonMapper; -import org.clever.core.model.request.QueryByPage; -import org.clever.core.model.request.page.Page; import org.clever.core.model.response.R; import org.clever.data.jdbc.DaoFactory; import org.clever.data.jdbc.QueryDSL; -import org.clever.data.jdbc.querydsl.utils.QueryDslUtils; import org.clever.web.mvc.annotation.RequestBody; import org.clever.web.mvc.annotation.RequestParam; import org.clever.web.mvc.annotation.Transactional; @@ -28,14 +25,12 @@ import java.util.List; import java.util.Map; import static com.galaxis.rcs.common.query.QLccBasLocation.lccBasLocation; -import static com.yvan.workbench.model.query.QLccModelFloor.lccModelFloor; -import static com.yvan.workbench.model.query.QLccModelWorld.lccModelWorld; public class LccModelManager { private static final QueryDSL QUERY_DSL = DaoFactory.getQueryDSL(); - private static String getOtherData(String projectUuid, String projectLabel) { - return StringUtils.trim(String.format(""" + private static Map getOtherData(String projectUuid, String projectLabel) { + String content = StringUtils.trim(String.format(""" { "project_uuid": "%s", "project_label": "%s", @@ -84,111 +79,157 @@ public class LccModelManager { "wall": [], "pillar": [] }""".stripIndent(), projectUuid, projectLabel)); + + return JacksonMapper.getInstance().fromJson(content, Map.class); } - public static Page projectList() { - return QueryDslUtils.queryByPage( - QUERY_DSL.selectFrom(lccModelWorld), - QueryByPage.getCurrent() - ); + public static R> projectList() { + // return QueryDslUtils.queryByPage( + // QUERY_DSL.selectFrom(lccModelWorld), + // QueryByPage.getCurrent() + // ); + var lccMapService = SpringContext.HOLDER.getBean(LccMapService.class); + return R.success(lccMapService.getAllProjects()); } - @Transactional - public static LccModelWorld addProject(@RequestBody LccModelWorld project) { - Assert.isNotBlank(project.getProjectUuid(), "项目编号必填"); - Assert.isNotBlank(project.getProjectLabel(), "项目标题必填"); - Assert.isTrue( - QUERY_DSL.selectFrom(lccModelWorld).where(lccModelWorld.projectUuid.eq(project.getProjectUuid())).fetchCount() <= 0, - "项目编号重复" - ); - project.setProjectVersion(1L); - project.setId(SnowFlake.SNOW_FLAKE.nextId()); - project.setDirectoryData(""" - [ - { - "label": "仓库楼层", - "items": [ - { - "catalogCode": "f1", - "label": "一楼 (f1)" - } - ] - } - ] - """.stripIndent()); + // @Transactional + public static LccProject addProject(@RequestBody Map entity) { + String projectUuid = Conv.asString(entity.get("projectUuid")); + String projectLabel = Conv.asString(entity.get("projectLabel")); + + var lccMapService = SpringContext.HOLDER.getBean(LccMapService.class); + lccMapService.checkProjectUuid(projectUuid, true); + Assert.isNotBlank(projectLabel, "projectLabel must not be blank"); + + LccProject project = new LccProject(); + + project.setProjectUuid(projectUuid); + project.setProjectLabel(projectLabel); + + LccProject.CatalogGroup[] directoryData = new LccProject.CatalogGroup[1]; + directoryData[0] = new LccProject.CatalogGroup(); + directoryData[0].setLabel("仓库楼层"); + directoryData[0].setItems(new LccProject.CatalogItem[1]); + directoryData[0].getItems()[0] = new LccProject.CatalogItem(); + directoryData[0].getItems()[0].setCatalogCode("f1"); + directoryData[0].getItems()[0].setLabel("一楼 (f1)"); + + project.setDirectoryData(directoryData); project.setOtherData(getOtherData(project.getProjectUuid(), project.getProjectLabel())); - QUERY_DSL.insert(lccModelWorld).populate(project).execute(); - return QUERY_DSL.selectFrom(lccModelWorld).where(lccModelWorld.id.eq(project.getId())).fetchOne(); + lccMapService.saveProject(project); + // QUERY_DSL.insert(lccModelWorld).populate(project).execute(); + // return QUERY_DSL.selectFrom(lccModelWorld).where(lccModelWorld.id.eq(project.getId())).fetchOne(); + return project; } - @Transactional - public static R addOrUpdateWorld(@RequestBody LccModelWorld params) { - Assert.isNotBlank(params.getProjectUuid(), "项目编号必填"); - long count = QUERY_DSL.selectFrom(lccModelWorld) - .where(lccModelWorld.projectUuid.eq(params.getProjectUuid())) - .where(lccModelWorld.projectVersion.eq(1L)) - .fetchCount(); - if (count >= 1) { - QUERY_DSL.update(lccModelWorld) - .set(lccModelWorld.otherData, params.getOtherData()) - .set(lccModelWorld.directoryData, params.getDirectoryData()) - .where(lccModelWorld.projectUuid.eq(params.getProjectUuid())) - .where(lccModelWorld.projectVersion.eq(1L)) - .execute(); - } else { - //noinspection unchecked - Map map = JacksonMapper.getInstance().fromJson(params.getOtherData(), Map.class); - params.setId(SnowFlake.SNOW_FLAKE.nextId()); - params.setProjectVersion(1L); - params.setProjectLabel(Conv.asString(map.get("project_label"))); - params.setServer(Conv.asString(map.get("server"))); - // params.setCreateBy(); - QUERY_DSL.insert(lccModelWorld).populate(params).execute(); - } + // @Transactional + public static R addOrUpdateWorld(@RequestBody Map entity) { + String projectUuid = Conv.asString(entity.get("projectUuid")); + String projectLabel = Conv.asString(entity.get("projectLabel")); + String directoryData = Conv.asString(entity.get("directoryData")); + String envId = Conv.asString(entity.get("envId")); + String otherData = Conv.asString(entity.get("otherData")); + + // 判断项目编号是否符合规范 + var lccMapService = SpringContext.HOLDER.getBean(LccMapService.class); + lccMapService.checkProjectUuid(projectUuid, false); + + LccProject project = new LccProject(); + project.setProjectUuid(projectUuid); + project.setProjectLabel(projectLabel); + project.setProjectFileLocation(""); + project.setDirectoryData(JacksonMapper.getInstance().fromJson(directoryData, LccProject.CatalogGroup[].class)); + project.setOtherData(JacksonMapper.getInstance().fromJson(otherData, Map.class)); + + lccMapService.saveProject(project); + + // long count = QUERY_DSL.selectFrom(lccModelWorld) + // .where(lccModelWorld.projectUuid.eq(params.getProjectUuid())) + // .where(lccModelWorld.projectVersion.eq(1L)) + // .fetchCount(); + // if (count >= 1) { + // QUERY_DSL.update(lccModelWorld) + // .set(lccModelWorld.otherData, params.getOtherData()) + // .set(lccModelWorld.directoryData, params.getDirectoryData()) + // .where(lccModelWorld.projectUuid.eq(params.getProjectUuid())) + // .where(lccModelWorld.projectVersion.eq(1L)) + // .execute(); + // } else { + // //noinspection unchecked + // Map map = JacksonMapper.getInstance().fromJson(params.getOtherData(), Map.class); + // params.setId(SnowFlake.SNOW_FLAKE.nextId()); + // params.setProjectVersion(1L); + // params.setProjectLabel(Conv.asString(map.get("project_label"))); + // params.setServer(Conv.asString(map.get("server"))); + // // params.setCreateBy(); + // QUERY_DSL.insert(lccModelWorld).populate(params).execute(); + // } return R.success(); } - public static LccModelFloor getFloor(@RequestParam Map params) { + @SneakyThrows + public static void getFloor(@RequestParam Map params, HttpServletResponse response) { String catalogCode = Conv.asString(params.get("catalogCode")); String project_uuid = Conv.asString(params.get("project_uuid")); - return QUERY_DSL.selectFrom(lccModelFloor) - .where(lccModelFloor.projectUuid.eq(project_uuid)) - .where(lccModelFloor.catalogCode.eq(catalogCode)) - .orderBy(lccModelFloor.projectVersion.desc()) - .fetchFirst(); + + // return QUERY_DSL.selectFrom(lccModelFloor) + // .where(lccModelFloor.projectUuid.eq(project_uuid)) + // .where(lccModelFloor.catalogCode.eq(catalogCode)) + // .orderBy(lccModelFloor.projectVersion.desc()) + // .fetchFirst(); + + // 从文件系统中加载楼层数据 + var lccMapService = SpringContext.HOLDER.getBean(LccMapService.class); + String content = lccMapService.loadFloor(project_uuid, catalogCode); + + response.setContentType("application/json; charset=UTF-8"); + response.setCharacterEncoding("UTF-8"); + response.getWriter().write(content); + response.getWriter().flush(); } @Transactional - public static R addOrUpdateFloor(@RequestBody LccModelFloor params) { - Assert.isNotBlank(params.getProjectUuid(), "项目编号必填"); - Assert.isNotBlank(params.getCatalogCode(), "楼层编号必填"); - Assert.notNull(params.getEnvId(), "环境编号必填"); - Assert.isNotBlank(params.getItems(), "楼层数据必填"); - if (params.getEnvId() == null || params.getEnvId() <= 0) { - throw new RuntimeException("环境编号必填"); - } - long count = QUERY_DSL.selectFrom(lccModelFloor) - .where(lccModelFloor.projectUuid.eq(params.getProjectUuid())) - .where(lccModelFloor.envId.eq(params.getEnvId())) - .where(lccModelFloor.catalogCode.eq(params.getCatalogCode())) - .fetchCount(); - - if (count >= 1) { - QUERY_DSL.update(lccModelFloor) - .set(lccModelFloor.items, params.getItems()) - .set(lccModelFloor.projectVersion, lccModelFloor.projectVersion.add(1L)) - .where(lccModelFloor.projectUuid.eq(params.getProjectUuid())) - .where(lccModelFloor.envId.eq(params.getEnvId())) - .where(lccModelFloor.catalogCode.eq(params.getCatalogCode())) - .execute(); - } else { - params.setId(SnowFlake.SNOW_FLAKE.nextId()); - params.setProjectVersion(1L); - // params.setCreateBy(); - QUERY_DSL.insert(lccModelFloor).populate(params).execute(); + public static R addOrUpdateFloor(@RequestBody Map params) { + String projectUuid = Conv.asString(params.get("projectUuid")); + String catalogCode = Conv.asString(params.get("catalogCode")); + Long envId = Conv.asLong(params.get("envId")); + String items = Conv.asString(params.get("items")); + + Assert.isNotBlank(projectUuid, "projectUuid must not be blank"); + Assert.isNotBlank(catalogCode, "catalogCode must not be blank"); + if (envId == null || envId < 0L) { + throw new RuntimeException("items must not be blank"); } - batchSaveBasLocation(params); + // if (params.getEnvId() == null || params.getEnvId() <= 0) { + // throw new RuntimeException("环境编号必填"); + // } + // long count = QUERY_DSL.selectFrom(lccModelFloor) + // .where(lccModelFloor.projectUuid.eq(params.getProjectUuid())) + // .where(lccModelFloor.envId.eq(params.getEnvId())) + // .where(lccModelFloor.catalogCode.eq(params.getCatalogCode())) + // .fetchCount(); + // + // if (count >= 1) { + // QUERY_DSL.update(lccModelFloor) + // .set(lccModelFloor.items, params.getItems()) + // .set(lccModelFloor.projectVersion, lccModelFloor.projectVersion.add(1L)) + // .where(lccModelFloor.projectUuid.eq(params.getProjectUuid())) + // .where(lccModelFloor.envId.eq(params.getEnvId())) + // .where(lccModelFloor.catalogCode.eq(params.getCatalogCode())) + // .execute(); + // } else { + // params.setId(SnowFlake.SNOW_FLAKE.nextId()); + // params.setProjectVersion(1L); + // // params.setCreateBy(); + // QUERY_DSL.insert(lccModelFloor).populate(params).execute(); + // } + + // 保存至文件系统 + var lccMapService = SpringContext.HOLDER.getBean(LccMapService.class); + lccMapService.saveFloor(projectUuid, catalogCode, items); + + batchSaveBasLocation(projectUuid, catalogCode, envId, items); return R.success(); } @@ -196,15 +237,101 @@ public class LccModelManager { /** * 保存库存货位数据 */ - private static void batchSaveBasLocation(LccModelFloor params) { + private static void batchSaveBasLocation(String projectUuid, String catalogCode, Long envId, String itemsJson) { // 删除旧的楼层数据 QUERY_DSL.delete(lccBasLocation) - .where(lccBasLocation.envId.eq(params.getEnvId())) - .where(lccBasLocation.catalogCode.eq(params.getCatalogCode())) + .where(lccBasLocation.envId.eq(envId)) + .where(lccBasLocation.catalogCode.eq(catalogCode)) .execute(); + // ================= 根据地图数据,生成货位基础资料,JSON 结构参考 + /* +提取货架信息: +1. t=rack 的情况下,读取 bays 数组,并且枚举里面的 levelHeight 数组,按数量生成 loc_code + +{ + "id": "rack1", + "t": "rack", + "v": true, + "dt": { + "bays": [ + { "levelHeight": [ 0.001, 1.21 ], }, + { "levelHeight": [ 0.001, 1.21 ], } + ], + }, +} + +2. t=gstore 的情况下 +{ + "id": "105_105", + "t": "gstore", +} + +提取路标信息 +{ + "id": "1_2", + "t": "way", + "dt": { + "linkStore": [ + { + "item": "rack1", + "bay": 0, "level": 0, "cell": 0, "direction": "up" + }, + { + "item": "rack1", + "bay": 0, "level": 1, "cell": 0, "direction": "up" + } + ] + } +} +{ + "id": "1_2", + "t": "way", + "dt": { + "linkStore": [ + { + "item": "rack1", + "bay": 0, "level": 0, "cell": 0, "direction": "up" + }, + { + "item": "rack1", + "bay": 0, "level": 1, "cell": 0, "direction": "up" + } + ] + } +} +{ + "id": "4_2", + "t": "way", + "dt": { + "linkStore": [ + { + "item": "105_105", + "bay": 0, + "level": 0, + "cell": 0, + "direction": "down" + } + ] + } +} +生成实体 + LccBasLocation basLocation = new LccBasLocation(); + basLocation.setEnvId(params.getEnvId()); + basLocation.setCatalogCode(params.getCatalogCode()); + basLocation.setLocType("t"); + basLocation.setWayPoint("way.id"); + basLocation.setLocDirection("linkStore.direction"); + basLocation.setRack("rack.id"); + basLocation.setBay("rack.id"); + basLocation.setLevel("rack.id"); + basLocation.setCell("rack.id"); + basLocation.setIsLock(0); + basLocation.setIsFrozen(0); + */ + // ================================== 从 params.items 中解析出货位数据 - var items = JacksonUtils.parseList(params.getItems(), HashMap.class); + var items = JacksonUtils.parseList(itemsJson, HashMap.class); var storeMap = new HashMap(); for (Map item : items) { String type = Conv.asString(item.get("t")); @@ -278,8 +405,8 @@ public class LccModelManager { // 全部写进数据库 var action = QUERY_DSL.insert(lccBasLocation); for (LccBasLocation basLocation : storeMap.values()) { - basLocation.setEnvId(params.getEnvId()); - basLocation.setCatalogCode(params.getCatalogCode()); + basLocation.setEnvId(envId); + basLocation.setCatalogCode(catalogCode); basLocation.setCell(0); basLocation.setIsLock(0); basLocation.setIsFrozen(0); @@ -293,92 +420,7 @@ public class LccModelManager { action.execute(); } - // ================= JSON 结构 - /* -提取货架信息: -1. t=rack 的情况下,读取 bays 数组,并且枚举里面的 levelHeight 数组,按数量生成 loc_code - -{ - "id": "rack1", - "t": "rack", - "v": true, - "dt": { - "bays": [ - { "levelHeight": [ 0.001, 1.21 ], }, - { "levelHeight": [ 0.001, 1.21 ], } - ], - }, -} - -2. t=gstore 的情况下 -{ - "id": "105_105", - "t": "gstore", -} - -提取路标信息 -{ - "id": "1_2", - "t": "way", - "dt": { - "linkStore": [ - { - "item": "rack1", - "bay": 0, "level": 0, "cell": 0, "direction": "up" - }, - { - "item": "rack1", - "bay": 0, "level": 1, "cell": 0, "direction": "up" - } - ] - } -} -{ - "id": "1_2", - "t": "way", - "dt": { - "linkStore": [ - { - "item": "rack1", - "bay": 0, "level": 0, "cell": 0, "direction": "up" - }, - { - "item": "rack1", - "bay": 0, "level": 1, "cell": 0, "direction": "up" - } - ] - } -} -{ - "id": "4_2", - "t": "way", - "dt": { - "linkStore": [ - { - "item": "105_105", - "bay": 0, - "level": 0, - "cell": 0, - "direction": "down" - } - ] - } -} -生成实体 - LccBasLocation basLocation = new LccBasLocation(); - basLocation.setEnvId(params.getEnvId()); - basLocation.setCatalogCode(params.getCatalogCode()); - basLocation.setLocType("t"); - basLocation.setWayPoint("way.id"); - basLocation.setLocDirection("linkStore.direction"); - basLocation.setRack("rack.id"); - basLocation.setBay("rack.id"); - basLocation.setLevel("rack.id"); - basLocation.setCell("rack.id"); - basLocation.setIsLock(0); - basLocation.setIsFrozen(0); - */ } } -// test === \ No newline at end of file +// test === 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 b5542f6..a36da5a 100644 --- a/servo/src/main/java/com/yvan/workbench/controller/RcsController.java +++ b/servo/src/main/java/com/yvan/workbench/controller/RcsController.java @@ -1,15 +1,21 @@ package com.yvan.workbench.controller; -import com.galaxis.rcs.common.entity.*; -import com.galaxis.rcs.common.enums.*; +import com.galaxis.rcs.RCSService; +import com.galaxis.rcs.common.entity.RcsTaskBiz; +import com.galaxis.rcs.common.entity.StoreLocation; +import com.galaxis.rcs.common.enums.BizTaskStatus; +import com.galaxis.rcs.common.enums.BizTaskType; +import com.galaxis.rcs.common.enums.LCCDirection; import com.galaxis.rcs.plan.PlanTaskSequence; 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.logisticsModel.*; +import com.yvan.logisticsModel.ExecutorItem; +import com.yvan.logisticsModel.LogisticsRuntime; +import com.yvan.logisticsModel.LogisticsRuntimeService; +import com.yvan.logisticsModel.StaticItem; import com.yvan.workbench.model.entity.Model; -import org.apache.commons.lang3.NotImplementedException; import org.clever.core.Conv; import org.clever.core.id.SnowFlake; import org.clever.core.model.response.R; @@ -20,6 +26,22 @@ import java.util.Map; public class RcsController { static final SnowFlake snowFlake = new SnowFlake(); + public static R projectStart(@RequestBody Map params) { + String projectUuid = Conv.asString(params.get("projectUUID")); + Long envId = Conv.asLong(params.get("envId")); + + if (Strings.isNullOrEmpty(projectUuid)) { + return R.fail("projectUUID Must not be empty"); + } + if (envId == null || envId < 0) { + return R.fail("envId Must not be empty"); + } + + RCSService.projectStart(projectUuid, envId); + return R.success("Project started successfully"); + + } + public static Model agvToCharger(@RequestBody Map params) { Object ret = getCommonParamAndCreateBizTask(params); if (ret instanceof Model) { diff --git a/servo/src/main/java/com/yvan/workbench/model/entity/LccModelFloor.java b/servo/src/main/java/com/yvan/workbench/model/entity/LccModelFloor.java deleted file mode 100644 index 715d090..0000000 --- a/servo/src/main/java/com/yvan/workbench/model/entity/LccModelFloor.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.yvan.workbench.model.entity; - -import lombok.Data; -import java.io.Serializable; -import java.util.Date; - -/** - * (lcc_model_floor) - */ -@Data -public class LccModelFloor implements Serializable { - /** */ - private Long id; - /** 项目编号 */ - private String projectUuid; - /** 项目版本 */ - private Long projectVersion; - /** 环境ID */ - private Long envId; - /** 楼层编号 */ - private String catalogCode; - /** 楼层数据 */ - private String items; - /** */ - private Boolean autoStart; - /** 创建时间,默认为当前时间 */ - private Date createAt; - /** 创建人 */ - private String createBy; - /** 最后更新时间,默认为当前时间,并在更新时自动更新 */ - private Date updateAt; - /** 更新人 */ - private String updateBy; -} diff --git a/servo/src/main/java/com/yvan/workbench/model/entity/LccModelWorld.java b/servo/src/main/java/com/yvan/workbench/model/entity/LccModelWorld.java deleted file mode 100644 index 35528bc..0000000 --- a/servo/src/main/java/com/yvan/workbench/model/entity/LccModelWorld.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.yvan.workbench.model.entity; - -import lombok.Data; - -import java.io.Serializable; -import java.util.Date; - -/** - * (lcc_model_world) - */ -@Data -public class LccModelWorld implements Serializable { - /** */ - private Long id; - /** 项目编号 */ - private String projectUuid; - /** 项目标题 */ - private String projectLabel; - /** 项目版本 */ - private Long projectVersion; - /** 所在服务器 */ - private String server; - /** 目录数据 */ - private String directoryData; - /** 其他数据 */ - private String otherData; - /** 创建时间,默认为当前时间 */ - private Date createAt; - /** 创建人 */ - private String createBy; - /** 最后更新时间,默认为当前时间,并在更新时自动更新 */ - private Date updateAt; - /** 更新人 */ - private String updateBy; -} 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 new file mode 100644 index 0000000..6361b97 --- /dev/null +++ b/servo/src/main/java/com/yvan/workbench/model/entity/LccProject.java @@ -0,0 +1,27 @@ +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 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 new file mode 100644 index 0000000..9e9fe5f --- /dev/null +++ b/servo/src/main/java/com/yvan/workbench/model/entity/LccProjectEnv.java @@ -0,0 +1,15 @@ +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/query/QLccModelFloor.java b/servo/src/main/java/com/yvan/workbench/model/query/QLccModelFloor.java deleted file mode 100644 index 19fa775..0000000 --- a/servo/src/main/java/com/yvan/workbench/model/query/QLccModelFloor.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.yvan.workbench.model.query; - -import static com.querydsl.core.types.PathMetadataFactory.*; -import com.querydsl.core.types.dsl.*; -import com.querydsl.core.types.*; -import com.querydsl.sql.*; -import java.sql.Types; -import com.yvan.workbench.model.entity.LccModelFloor; -import java.util.Date; - -/** - * (lcc_model_floor) - */ -@SuppressWarnings("ALL") -public class QLccModelFloor extends RelationalPathBase { - /** lcc_model_floor表 */ - public static final QLccModelFloor lccModelFloor = new QLccModelFloor("lcc_model_floor"); - - /** */ - public final NumberPath id = createNumber("id", Long.class); - /** 项目编号 */ - public final StringPath projectUuid = createString("projectUuid"); - /** 项目版本 */ - public final NumberPath projectVersion = createNumber("projectVersion", Long.class); - /** 环境ID */ - public final NumberPath envId = createNumber("envId", Long.class); - /** 楼层编号 */ - public final StringPath catalogCode = createString("catalogCode"); - /** 楼层数据 */ - public final StringPath items = createString("items"); - /** */ - public final BooleanPath autoStart = createBoolean("autoStart"); - /** 创建时间,默认为当前时间 */ - public final DateTimePath createAt = createDateTime("createAt", Date.class); - /** 创建人 */ - public final StringPath createBy = createString("createBy"); - /** 最后更新时间,默认为当前时间,并在更新时自动更新 */ - public final DateTimePath updateAt = createDateTime("updateAt", Date.class); - /** 更新人 */ - public final StringPath updateBy = createString("updateBy"); - - public QLccModelFloor(String variable) { - super(LccModelFloor.class, forVariable(variable), "rcs2_tw_zhanghui", "lcc_model_floor"); - addMetadata(); - } - - public QLccModelFloor(String variable, String schema, String table) { - super(LccModelFloor.class, forVariable(variable), schema, table); - addMetadata(); - } - - public QLccModelFloor(String variable, String schema) { - super(LccModelFloor.class, forVariable(variable), schema, "lcc_model_floor"); - addMetadata(); - } - - public QLccModelFloor(Path path) { - super(path.getType(), path.getMetadata(), "rcs2_tw_zhanghui", "lcc_model_floor"); - addMetadata(); - } - - public QLccModelFloor(PathMetadata metadata) { - super(LccModelFloor.class, metadata, "rcs2_tw_zhanghui", "lcc_model_floor"); - addMetadata(); - } - - private void addMetadata() { - addMetadata(id, ColumnMetadata.named("id").withIndex(1).ofType(Types.BIGINT).withSize(19)); - addMetadata(projectUuid, ColumnMetadata.named("project_uuid").withIndex(2).ofType(Types.VARCHAR).withSize(36)); - addMetadata(projectVersion, ColumnMetadata.named("project_version").withIndex(3).ofType(Types.BIGINT).withSize(19)); - addMetadata(envId, ColumnMetadata.named("env_id").withIndex(4).ofType(Types.BIGINT).withSize(19)); - addMetadata(catalogCode, ColumnMetadata.named("catalogCode").withIndex(5).ofType(Types.VARCHAR).withSize(255)); - addMetadata(items, ColumnMetadata.named("items").withIndex(6).ofType(Types.LONGVARCHAR).withSize(16777215)); - addMetadata(autoStart, ColumnMetadata.named("auto_start").withIndex(7).ofType(Types.BIT).withSize(3)); - addMetadata(createAt, ColumnMetadata.named("create_at").withIndex(8).ofType(Types.TIMESTAMP)); - addMetadata(createBy, ColumnMetadata.named("create_by").withIndex(9).ofType(Types.VARCHAR).withSize(255)); - addMetadata(updateAt, ColumnMetadata.named("update_at").withIndex(10).ofType(Types.TIMESTAMP)); - addMetadata(updateBy, ColumnMetadata.named("update_by").withIndex(11).ofType(Types.VARCHAR).withSize(255)); - } -} diff --git a/servo/src/main/java/com/yvan/workbench/model/query/QLccModelWorld.java b/servo/src/main/java/com/yvan/workbench/model/query/QLccModelWorld.java deleted file mode 100644 index d3aa39e..0000000 --- a/servo/src/main/java/com/yvan/workbench/model/query/QLccModelWorld.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.yvan.workbench.model.query; - -import com.querydsl.core.types.Path; -import com.querydsl.core.types.PathMetadata; -import com.querydsl.core.types.dsl.DateTimePath; -import com.querydsl.core.types.dsl.NumberPath; -import com.querydsl.core.types.dsl.StringPath; -import com.querydsl.sql.ColumnMetadata; -import com.querydsl.sql.RelationalPathBase; -import com.yvan.workbench.model.entity.LccModelWorld; - -import java.sql.Types; -import java.util.Date; - -import static com.querydsl.core.types.PathMetadataFactory.forVariable; - -/** - * (lcc_model_world) - */ -@SuppressWarnings("ALL") -public class QLccModelWorld extends RelationalPathBase { - /** lcc_model_world表 */ - public static final QLccModelWorld lccModelWorld = new QLccModelWorld("lcc_model_world"); - - /** */ - public final NumberPath id = createNumber("id", Long.class); - /** 项目编号 */ - public final StringPath projectUuid = createString("projectUuid"); - /** 项目标题 */ - public final StringPath projectLabel = createString("projectLabel"); - /** 项目版本 */ - public final NumberPath projectVersion = createNumber("projectVersion", Long.class); - /** 所在服务器 */ - public final StringPath server = createString("server"); - /** 目录数据 */ - public final StringPath directoryData = createString("directoryData"); - /** 其他数据 */ - public final StringPath otherData = createString("otherData"); - /** 创建时间,默认为当前时间 */ - public final DateTimePath createAt = createDateTime("createAt", Date.class); - /** 创建人 */ - public final StringPath createBy = createString("createBy"); - /** 最后更新时间,默认为当前时间,并在更新时自动更新 */ - public final DateTimePath updateAt = createDateTime("updateAt", Date.class); - /** 更新人 */ - public final StringPath updateBy = createString("updateBy"); - - public QLccModelWorld(String variable) { - super(LccModelWorld.class, forVariable(variable), "test", "lcc_model_world"); - addMetadata(); - } - - public QLccModelWorld(String variable, String schema, String table) { - super(LccModelWorld.class, forVariable(variable), schema, table); - addMetadata(); - } - - public QLccModelWorld(String variable, String schema) { - super(LccModelWorld.class, forVariable(variable), schema, "lcc_model_world"); - addMetadata(); - } - - public QLccModelWorld(Path path) { - super(path.getType(), path.getMetadata(), "test", "lcc_model_world"); - addMetadata(); - } - - public QLccModelWorld(PathMetadata metadata) { - super(LccModelWorld.class, metadata, "test", "lcc_model_world"); - addMetadata(); - } - - private void addMetadata() { - addMetadata(id, ColumnMetadata.named("id").withIndex(1).ofType(Types.BIGINT).withSize(19)); - addMetadata(projectUuid, ColumnMetadata.named("project_uuid").withIndex(2).ofType(Types.VARCHAR).withSize(36)); - addMetadata(projectLabel, ColumnMetadata.named("project_label").withIndex(3).ofType(Types.VARCHAR).withSize(255)); - addMetadata(projectVersion, ColumnMetadata.named("project_version").withIndex(4).ofType(Types.BIGINT).withSize(19)); - addMetadata(server, ColumnMetadata.named("server").withIndex(5).ofType(Types.VARCHAR).withSize(255)); - addMetadata(directoryData, ColumnMetadata.named("directory_data").withIndex(6).ofType(Types.LONGVARCHAR).withSize(65535)); - addMetadata(otherData, ColumnMetadata.named("other_data").withIndex(7).ofType(Types.LONGVARCHAR).withSize(65535)); - addMetadata(createAt, ColumnMetadata.named("create_at").withIndex(8).ofType(Types.TIMESTAMP)); - addMetadata(createBy, ColumnMetadata.named("create_by").withIndex(9).ofType(Types.VARCHAR).withSize(255)); - addMetadata(updateAt, ColumnMetadata.named("update_at").withIndex(10).ofType(Types.TIMESTAMP)); - addMetadata(updateBy, ColumnMetadata.named("update_by").withIndex(11).ofType(Types.VARCHAR).withSize(255)); - } -} diff --git a/servo/src/main/java/com/yvan/workbench/service/LccAutoStartService.java b/servo/src/main/java/com/yvan/workbench/service/LccAutoStartService.java new file mode 100644 index 0000000..1d7b785 --- /dev/null +++ b/servo/src/main/java/com/yvan/workbench/service/LccAutoStartService.java @@ -0,0 +1,43 @@ +package com.yvan.workbench.service; + +import com.galaxis.rcs.RCSService; +import com.google.common.base.Strings; +import com.yvan.logisticsModel.LogisticsRuntimeService; +import com.yvan.workbench.autoconfigure.LccConfigProperties; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.SmartLifecycle; + +@Slf4j +public class LccAutoStartService implements SmartLifecycle { + private final LccConfigProperties lccConfigProperties; + + public LccAutoStartService(LccConfigProperties lccConfigProperties) { + this.lccConfigProperties = lccConfigProperties; + } + + @Override + public void start() { + var autoStartItems = this.lccConfigProperties.getAutoStart(); + for (var item : autoStartItems) { + String projectUUID = item.getProjectUuid(); + Long envId = item.getEnvId(); + + if (Strings.isNullOrEmpty(projectUUID) || envId == null || envId <= 0) { + log.warn("Skipping auto start for project UUID '{}' and env ID '{}', invalid configuration.", projectUUID, envId); + continue; // 跳过无效的配置 + } + + RCSService.projectStart(projectUUID, envId); + } + } + + @Override + public void stop() { + LogisticsRuntimeService.INSTANCE.stopAll(); + } + + @Override + public boolean isRunning() { + return LogisticsRuntimeService.INSTANCE.isRunning(); + } +} diff --git a/servo/src/main/java/com/yvan/workbench/service/LccMapService.java b/servo/src/main/java/com/yvan/workbench/service/LccMapService.java new file mode 100644 index 0000000..2011e72 --- /dev/null +++ b/servo/src/main/java/com/yvan/workbench/service/LccMapService.java @@ -0,0 +1,291 @@ +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.workbench.autoconfigure.LccConfigProperties; +import com.yvan.workbench.model.entity.LccProject; +import com.yvan.workbench.model.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.mapper.JacksonMapper; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; + +@Slf4j +public class LccMapService { + private final LccConfigProperties config; + + public LccMapService(LccConfigProperties lccConfigProperties) { + this.config = lccConfigProperties; + } + + @SneakyThrows + public LccProject getProjectById(String projectUuid) { + checkProjectUuid(projectUuid, false); + var projectFile = new File(this.config.getLocation() + "/" + projectUuid + "/project.json"); + + if (!projectFile.exists() || !projectFile.isFile()) { + throw new RuntimeException( + String.format("LccMapService.getProjectById() - project '%s' does not exist or is not a file", projectFile.getAbsolutePath()) + ); + } + + log.info("load project file: {}", projectFile.getAbsolutePath()); + String content = Joiner.on("\n").join(FileUtils.readLines(projectFile, StandardCharsets.UTF_8)); + var project = JacksonMapper.getInstance().fromJson(content, LccProject.class); + project.setProjectFileLocation( + FilenameUtils.normalize(projectFile.getAbsolutePath(), true) + ); + return project; + } + + @SneakyThrows + public LccProjectEnv getEnvById(String projectUuid, Long envId) { + checkProjectUuid(projectUuid, false); + var projectFile = new File(this.config.getLocation() + "/" + projectUuid + "/envs/" + envId + ".json"); + if (!projectFile.exists() || !projectFile.isFile()) { + throw new RuntimeException( + String.format("LccMapService.getEnvById() - env '%s' does not exist or is not a file", projectFile.getAbsolutePath()) + ); + } + + log.info("load project file: {}", projectFile.getAbsolutePath()); + String content = Joiner.on("\n").join(FileUtils.readLines(projectFile, StandardCharsets.UTF_8)); + if (Strings.isNullOrEmpty(content)) { + throw new RuntimeException( + String.format("LccMapService.getEnvById() - env '%s' content is empty", projectFile.getAbsolutePath()) + ); + } + if (!content.contains("envConfig")) { + throw new RuntimeException( + String.format("LccMapService.getEnvById() - env '%s' content is not a valid LccProjectEnv", projectFile.getAbsolutePath()) + ); + } + var envInfo = JacksonMapper.getInstance().fromJson(content, LccProjectEnv.class); + envInfo.setFileLocation( + FilenameUtils.normalize(projectFile.getAbsolutePath(), true) + ); + return envInfo; + } + + @SneakyThrows + public List getAllProjects() { + var mapLoc = new File(this.config.getLocation()); + + // 扫描 mapLoc 目录下所有的文件夹,并且文件夹中包含有 project.json 文件存在,就讲她反序列化为 LccProject 对象 + if (!mapLoc.exists() || !mapLoc.isDirectory()) { + throw new RuntimeException( + String.format("LccMapService.getAllProjects() - mapLoc '%s' does not exist or is not a directory", mapLoc.getAbsolutePath()) + ); + } + + List list = Lists.newArrayList(); + for (File projectFolder : Objects.requireNonNull(mapLoc.listFiles())) { + if (projectFolder.isDirectory()) { + File projectFile = new File(projectFolder, "project.json"); + if (projectFile.exists() && projectFile.isFile()) { + // 反序列化 project.json 文件为 LccProject 对象 + log.info("load project file: {}", projectFile.getAbsolutePath()); + String content = Joiner.on("\n").join(FileUtils.readLines(projectFile, StandardCharsets.UTF_8)); + if (Strings.isNullOrEmpty(content)) { + continue; + } + if (!content.contains("directoryData")) { + continue; + } + + var modelWorld = JacksonMapper.getInstance().fromJson(content, LccProject.class); + modelWorld.setProjectFileLocation( + FilenameUtils.normalize(projectFile.getAbsolutePath(), true) + ); + list.add(modelWorld); + } + } + } + + return list; + } + + public void checkProjectUuid(String projectUuid, boolean checkExists) { + // 检查项目编号是否符合规范 + if (Strings.isNullOrEmpty(projectUuid)) { + throw new RuntimeException("projectUuid cannot be null or empty"); + } + if (!projectUuid.matches("^[a-zA-Z0-9_\\-]+$")) { + throw new RuntimeException("projectUuid must be alphanumeric, underscores or hyphens"); + } + + // 检查项目文件名是否存在 + if (checkExists) { + var names = getAllProjectNames(); + if (names.contains(projectUuid)) { + throw new RuntimeException("projectUuid '" + projectUuid + "' has already exists, please use another one"); + } + } + } + + @SneakyThrows + public void saveProject(LccProject project) { + // 检查项目编号是否符合规范 + checkProjectUuid(project.getProjectUuid(), false); + + // 检查项目目录是否存在 + var mapLoc = new File(this.config.getLocation()); + if (!mapLoc.exists() || !mapLoc.isDirectory()) { + throw new RuntimeException( + String.format("LccMapService.saveProject() - mapLoc '%s' does not exist or is not a directory", mapLoc.getAbsolutePath()) + ); + } + + // 创建项目目录 + var projectDir = new File(mapLoc, project.getProjectUuid()); + if (!projectDir.exists()) { + projectDir.mkdirs(); + } + + // 这个字段不保存 + project.setProjectFileLocation(""); + + // 保存 project.json 文件 + var projectFile = new File(projectDir, "project.json"); + + // 如果 project.json 文件与 content 内容不一致,则跟新覆盖写入 + String contentOrigin = projectFile.exists() ? Joiner.on("\n").join(FileUtils.readLines(projectFile, StandardCharsets.UTF_8)) : ""; + String content = JacksonMapper.getInstance().toJsonPretty(project); + if (!content.equals(contentOrigin)) { + // 内容不一致时,更新项目的更新时间和更新人 + log.info("write project file: {}", projectFile.getAbsolutePath()); + FileUtils.writeStringToFile(projectFile, content, StandardCharsets.UTF_8); + } else { + log.info("project file no change, ignore write, path:{}", projectFile.getAbsolutePath()); + } + } + + @SneakyThrows + public String loadFloor(String projectUuid, String catalogCode) { + // 检查项目编号/楼层编号是否符合规范, 防止注入漏洞 + checkProjectUuid(projectUuid, false); + checkProjectUuid(catalogCode, false); + + // 检查项目目录是否存在 + var mapLoc = new File(this.config.getLocation()); + if (!mapLoc.exists() || !mapLoc.isDirectory()) { + throw new RuntimeException( + String.format("LccMapService.saveProject() - mapLoc '%s' does not exist or is not a directory", mapLoc.getAbsolutePath()) + ); + } + + // 创建项目目录 + var floorFile = new File(mapLoc, projectUuid + "/floor/" + catalogCode + ".json"); + if (!floorFile.exists() || !floorFile.isFile()) { + throw new RuntimeException( + String.format("LccMapService.loadFloor() - '%s' does not exist or is not a file", floorFile.getAbsolutePath()) + ); + } + + log.info("load floor file: {}", floorFile.getAbsolutePath()); + String content = Joiner.on("\n").join(FileUtils.readLines(floorFile, StandardCharsets.UTF_8)); + if (Strings.isNullOrEmpty(content)) { + // 如果内容为空,则返回空数组 + content = "[]"; + } + return content; + } + + @SneakyThrows + public void saveFloor(String projectUuid, String catalogCode, String items) { + // 检查项目编号/楼层编号是否符合规范, 防止注入漏洞 + checkProjectUuid(projectUuid, false); + checkProjectUuid(catalogCode, false); + + if (Strings.isNullOrEmpty(items)) { + items = "[]"; + } + + // 检查项目目录是否存在 + var mapLoc = new File(this.config.getLocation()); + if (!mapLoc.exists() || !mapLoc.isDirectory()) { + throw new RuntimeException( + String.format("LccMapService.saveFloor() - mapLoc '%s' does not exist or is not a directory", mapLoc.getAbsolutePath()) + ); + } + + // 创建项目目录 + var floorDir = new File(mapLoc, projectUuid + "/floor"); + if (!floorDir.exists()) { + floorDir.mkdirs(); + } + + // 保存楼层数据到文件 + var floorFile = new File(floorDir, catalogCode + ".json"); + log.info("write floor file: {}", floorFile.getAbsolutePath()); + FileUtils.writeStringToFile(floorFile, items, StandardCharsets.UTF_8); + } + + @SneakyThrows + public List getAllEnv(String projectUuid) { + checkProjectUuid(projectUuid, false); + + var envLoc = new File(this.config.getLocation() + "/" + projectUuid + "/envs"); + + // 扫描 mapLoc 目录下所有的文件夹,并且文件夹中包含有 project.json 文件存在,就讲她反序列化为 LccProject 对象 + if (!envLoc.exists() || !envLoc.isDirectory()) { + throw new RuntimeException( + String.format("LccMapService.getAllProjects() - mapLoc '%s' does not exist or is not a directory", envLoc.getAbsolutePath()) + ); + } + + List list = Lists.newArrayList(); + for (File propFile : Objects.requireNonNull(envLoc.listFiles())) { + if (propFile.exists() && propFile.isFile() && propFile.getName().endsWith(".json")) { + // 反序列化 project.json 文件为 LccProject 对象 + log.info("load env file: {}", propFile.getAbsolutePath()); + String content = Joiner.on("\n").join(FileUtils.readLines(propFile, StandardCharsets.UTF_8)); + if (Strings.isNullOrEmpty(content)) { + continue; + } + if (!content.contains("envConfig")) { + continue; + } + + var envInfo = JacksonMapper.getInstance().fromJson(content, LccProjectEnv.class); + envInfo.setFileLocation( + FilenameUtils.normalize(propFile.getAbsolutePath(), true) + ); + list.add(envInfo); + } + } + return list; + } + + + public List getAllProjectNames() { + var mapLoc = new File(this.config.getLocation()); + + // 扫描 mapLoc 目录下所有的文件夹,并且文件夹中包含有 project.json 文件存在,就讲她反序列化为 LccProject 对象 + if (!mapLoc.exists() || !mapLoc.isDirectory()) { + throw new RuntimeException( + String.format("LccMapService.getAllProjects() - mapLoc '%s' does not exist or is not a directory", mapLoc.getAbsolutePath()) + ); + } + + List list = Lists.newArrayList(); + for (File projectFolder : Objects.requireNonNull(mapLoc.listFiles())) { + if (projectFolder.isDirectory()) { + File projectFile = new File(projectFolder, "project.json"); + if (projectFile.exists() && projectFile.isFile()) { + list.add(projectFolder.getName()); + } + } + } + return list; + } + + +} diff --git a/servo/src/main/resources/application-dev.yml b/servo/src/main/resources/application-dev.yml index 0f5a9d6..e187198 100644 --- a/servo/src/main/resources/application-dev.yml +++ b/servo/src/main/resources/application-dev.yml @@ -4,6 +4,12 @@ server: app: root-path: './' +lcc: + location: './lcc-map' + autoStart: + - projectUuid: tw_test + envId: 1 + mybatis: enable: true watcher: true