Browse Source

将地图存进文件系统,去掉 lcc_model_floor / lcc_model_world 表

master
修宁 6 months ago
parent
commit
0c180a58e4
  1. 327
      servo/src/main/java/com/galaxis/rcs/RCSService.java
  2. 2
      servo/src/main/java/com/galaxis/rcs/plan/PlanTaskSequence.java
  3. 3
      servo/src/main/java/com/galaxis/rcs/plan/path/NavigationGraph.java
  4. 2
      servo/src/main/java/com/galaxis/rcs/plan/planner/PTRTaskPlanner.java
  5. 4
      servo/src/main/java/com/galaxis/rcs/ptr/AmrMessageHandler.java
  6. 30
      servo/src/main/java/com/galaxis/rcs/ptr/PtrAgvItem.java
  7. 9
      servo/src/main/java/com/galaxis/rcs/ptr/PtrMqttClient.java
  8. 6
      servo/src/main/java/com/galaxis/rcs/task/TaskService.java
  9. 37
      servo/src/main/java/com/yvan/logisticsEnv/EnvConfig.java
  10. 18
      servo/src/main/java/com/yvan/logisticsEnv/EnvPayload.java
  11. 29
      servo/src/main/java/com/yvan/logisticsEnv/EnvStartParam.java
  12. 88
      servo/src/main/java/com/yvan/logisticsEnv/LogisticsEnv.java
  13. 30
      servo/src/main/java/com/yvan/logisticsEnv/LogisticsEnvManager.java
  14. 6
      servo/src/main/java/com/yvan/logisticsModel/BaseItem.java
  15. 6
      servo/src/main/java/com/yvan/logisticsModel/ExecutorItem.java
  16. 5
      servo/src/main/java/com/yvan/logisticsModel/Floor.java
  17. 221
      servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntime.java
  18. 44
      servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntimeService.java
  19. 7
      servo/src/main/java/com/yvan/workbench/autoconfigure/AppAutoConfiguration.java
  20. 31
      servo/src/main/java/com/yvan/workbench/autoconfigure/LccAutoConfiguration.java
  21. 18
      servo/src/main/java/com/yvan/workbench/autoconfigure/LccConfigProperties.java
  22. 21
      servo/src/main/java/com/yvan/workbench/controller/EnvController.java
  23. 13
      servo/src/main/java/com/yvan/workbench/controller/LccController.java
  24. 424
      servo/src/main/java/com/yvan/workbench/controller/LccModelManager.java
  25. 30
      servo/src/main/java/com/yvan/workbench/controller/RcsController.java
  26. 34
      servo/src/main/java/com/yvan/workbench/model/entity/LccModelFloor.java
  27. 35
      servo/src/main/java/com/yvan/workbench/model/entity/LccModelWorld.java
  28. 27
      servo/src/main/java/com/yvan/workbench/model/entity/LccProject.java
  29. 15
      servo/src/main/java/com/yvan/workbench/model/entity/LccProjectEnv.java
  30. 80
      servo/src/main/java/com/yvan/workbench/model/query/QLccModelFloor.java
  31. 86
      servo/src/main/java/com/yvan/workbench/model/query/QLccModelWorld.java
  32. 43
      servo/src/main/java/com/yvan/workbench/service/LccAutoStartService.java
  33. 291
      servo/src/main/java/com/yvan/workbench/service/LccMapService.java
  34. 6
      servo/src/main/resources/application-dev.yml

327
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.AddTaskRequest;
import com.galaxis.rcs.common.entity.AddTaskResult; 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.common.enums.LCCDirection;
import com.galaxis.rcs.plan.PlanTaskSequence; import com.galaxis.rcs.connector.cl2.Cl2Item;
import com.galaxis.rcs.plan.TaskPlannerFactory; import com.galaxis.rcs.plan.path.PathUtils;
import com.galaxis.rcs.plan.planner.Planner;
import com.galaxis.rcs.plan.task.CarryTask;
import com.galaxis.rcs.ptr.AmrMessageHandler;
import com.google.common.base.Strings; 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.LogisticsRuntime;
import com.yvan.logisticsModel.LogisticsRuntimeService; 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.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.clever.core.BannerUtils;
import org.clever.core.Conv; import org.clever.core.Conv;
import org.clever.core.json.JsonWrapper; import org.clever.core.json.JsonWrapper;
import org.clever.core.mapper.JacksonMapper;
import org.clever.data.jdbc.DaoFactory; import org.clever.data.jdbc.DaoFactory;
import org.clever.data.jdbc.QueryDSL; import org.clever.data.jdbc.QueryDSL;
import org.clever.data.jdbc.querydsl.utils.QueryDslUtils; import org.clever.data.jdbc.querydsl.utils.QueryDslUtils;
import java.lang.management.ManagementFactory; import java.util.List;
import java.net.InetAddress;
import java.util.Map; import java.util.Map;
import static com.galaxis.rcs.common.query.QLccEnvInfo.lccEnvInfo; import static com.galaxis.rcs.common.query.QLccBasExecutor.lccBasExecutor;
import static com.yvan.workbench.model.query.QLccModelFloor.lccModelFloor;
/** /**
* RCS 对外API调用类 * RCS 对外API调用类
@ -44,126 +35,248 @@ public class RCSService {
} }
/** /**
* 初始化RCS服 * 添加任
*/ */
public void init() { public static AddTaskResult addTask(String projectUuid, long envId, AddTaskRequest request) {
this.createAutoStartEnv(); AddTaskResult result = new AddTaskResult();
}
/** LogisticsRuntime logisticsRuntime = LogisticsRuntimeService.INSTANCE.getByProjectEnv(projectUuid, envId);
* 加载所有自动启动的环境和楼层数据 String bizTaskId = logisticsRuntime.taskService.addBizTask(request);
* 并启动对应的环境 result.bizTaskId = bizTaskId;
*/
@SneakyThrows
private void createAutoStartEnv() {
QueryDSL queryDSL = DaoFactory.getQueryDSL();
var list = queryDSL.select(QueryDslUtils.linkedMap(
lccEnvInfo.envId,
lccEnvInfo.worldId,
lccEnvInfo.envPayload,
lccModelFloor.catalogCode
)).from(lccEnvInfo) return result;
.innerJoin(lccModelFloor).on( }
lccEnvInfo.worldId.eq(lccModelFloor.projectUuid)
)
.where(lccEnvInfo.autoStart.eq(true))
.where(lccModelFloor.autoStart.eq(true))
.fetch();
Map<LogisticsRuntime, EnvPayload> runtimePayLoad = Maps.newHashMap(); @SneakyThrows
for (Map<String, Object> map : list) { public static void projectStart(String projectUuid, Long envId) {
long envId = Conv.asLong(map.get("env_id")); LogisticsRuntime runtime = LogisticsRuntimeService.INSTANCE.getByProjectEnv(projectUuid, envId);
String projectUuid = Conv.asString(map.get("world_id"));
String envPayload = Conv.asString(map.get("env_payload"));
String catalogCode = Conv.asString(map.get("catalog_code"));
EnvPayload payload; if (runtime != null) {
if (!Strings.isNullOrEmpty(envPayload) && !"N/A".equals(envPayload)) { log.info("Project {} with envId {} is already started.", projectUuid, envId);
payload = new JsonWrapper(envPayload).asObject(EnvPayload.class); return;
} else {
payload = new EnvPayload();
} }
var runtime = loadFloor(projectUuid, catalogCode, envId, payload); var lccMapService = SpringContext.HOLDER.getBean(LccMapService.class);
runtimePayLoad.put(runtime, payload); 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.");
} }
String[] logBanners = new String[runtimePayLoad.entrySet().size()]; var env = lccMapService.getEnvById(projectUuid, envId);
int i = 0; if (env.getEnvConfig() == null) {
for (Map.Entry<LogisticsRuntime, EnvPayload> entry : runtimePayLoad.entrySet()) { throw new RuntimeException("Project " + projectUuid + " envId " + envId + " has no envConfig data.");
var runtime = entry.getKey(); }
var envPayload = entry.getValue();
logBanners[i++] = String.format("projectUUID: %s, envId: %d", runtime.projectUUID, runtime.envId); runtime = LogisticsRuntimeService.INSTANCE.create(project, env);
EnvStartParam param = new EnvStartParam(); // 获取所有楼层数据
param.setTimeRate(1); for (LccProject.CatalogGroup floor : project.getDirectoryData()) {
param.setVirtual(false); for (LccProject.CatalogItem item : floor.getItems()) {
param.setEnvPayload(envPayload); String catalogCode = item.getCatalogCode();
param.setClientId(InetAddress.getLocalHost().getHostName()); if (Strings.isNullOrEmpty(catalogCode)) {
runtime.start(param); continue;
}
// 读取楼层数据
String floorJson = lccMapService.loadFloor(projectUuid, catalogCode);
if (!Strings.isNullOrEmpty(floorJson)) {
List<Map<String, Object>> items = JacksonMapper.getInstance().fromJson(floorJson, List.class);
runtime.loadMap(catalogCode, items);
}
} }
BannerUtils.printConfig(log, "RCS服务已启动", logBanners);
} }
// 读取所有的库存
// 读取所有的执行器
// 读取地图上所有的车
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"));
/** /**
* 添加任务, 示例
* <pre>
* { * {
* type: 'carry', // 任务类型 * "id": "3",
* agv: 'cl2', // 指定车辆 * "t": "cl2",
* lpn: 'pallet1124', // 托盘ID, 用于校验 * "v": true,
* from: '27', // 起始点位 * "dt": {
* priority: 1, // 优先级 * "ptrWidth": 1.5,
* from: { * "ptrDepth": 1.5,
* item: '27', bay: 0, level: 1, cell: 0 // 起始点位的详细信息 * "ptrHeight": 1.98
* },
* to:{
* item: '20'
* } * }
* } * }
* </pre>
*/ */
public static AddTaskResult addTask(String projectUuid, long envId, AddTaskRequest request) { JsonWrapper jwPayload = new JsonWrapper(payload);
AddTaskResult result = new AddTaskResult();
LogisticsRuntime logisticsRuntime = LogisticsRuntimeService.INSTANCE.getByProjectEnv(projectUuid, envId); String wayPointId = "";
String bizTaskId = logisticsRuntime.taskService.addBizTask(request); String direction = "";
result.bizTaskId = bizTaskId; 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; // 跳过虚拟位置为空的执行器
}
return result; if (!virtualLocationAt.contains(":")) {
log.warn("Invalid virtual_location_at format: {}, id:{}", virtualLocationAt, item.get("executor_id"));
continue; // 跳过格式不正确的虚拟位置
} }
/** 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(floorPayload)) { if(Strings.isNullOrEmpty(wayPointId)){
throw new RuntimeException("not found floor data for projectUUID: " + projectUUID + ", catalogCode: " + catalogCode); log.warn("wayPointId is empty for executorId: {}", item.get("executor_id"));
continue; // 跳过wayPointId为空的执行器
} }
JsonWrapper jw = new JsonWrapper("{ \"catalogCode\": \"" + catalogCode + "\", \"t\": \"floor\", \"items\": " + floorPayload + "}"); if(Strings.isNullOrEmpty(direction)) {
log.warn("direction is empty for executorId: {}", item.get("executor_id"));
continue; // 跳过方向为空的执行器
}
}
switch (jwPayload.asStr("t")) {
case "cl2":
case "clx":
// 处理 CL2 或 CLX 类型的执行器
// 车所在的标记位置,及方向 11_4:RIGHT
var eitem = new Cl2Item(runtime, (Map<String, Object>) jwPayload.getInnerMap());
runtime = LogisticsRuntimeService.INSTANCE.createEnv(projectUUID, envId); // 找到地标位置
StaticItem staticItem = runtime.getStaticItemById(wayPointId);
if (staticItem != null) {
eitem.logicX = staticItem.logicX;
eitem.logicY = staticItem.logicY;
eitem.direction = PathUtils.convertDirectionToPtrDiretion(LCCDirection.fromString(direction));
} 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); default:
runtime.loadMap(jw); 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<LogisticsRuntime, EnvConfig> runtimePayLoad = Maps.newHashMap();
// for (Map<String, Object> 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<LogisticsRuntime, EnvConfig> 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() { // public static Object runPath() {
// String executorId = "10"; // 执行器ID // String executorId = "10"; // 执行器ID
// String lpn = "pallet1124"; // String lpn = "pallet1124";

2
servo/src/main/java/com/galaxis/rcs/plan/PlanTaskSequence.java

@ -45,7 +45,7 @@ public class PlanTaskSequence {
RcsTaskPlan planTask = new RcsTaskPlan(); RcsTaskPlan planTask = new RcsTaskPlan();
planTask.setPlanTaskId(snowFlake.nextId()); planTask.setPlanTaskId(snowFlake.nextId());
planTask.setBizTaskId(this.bizTask.getBizTaskId()); planTask.setBizTaskId(this.bizTask.getBizTaskId());
planTask.setEnvId(logisticsRuntime.logisticsEnv.getEnvId()); planTask.setEnvId(logisticsRuntime.envId);
planTask.setPlanType(planType); planTask.setPlanType(planType);
planTask.setExecutorId(this.executorId); planTask.setExecutorId(this.executorId);
planTask.setSeq(taskList.size()); planTask.setSeq(taskList.size());

3
servo/src/main/java/com/galaxis/rcs/plan/path/NavigationGraph.java

@ -96,9 +96,6 @@ public class NavigationGraph {
// 检查可旋转性 // 检查可旋转性
boolean rotatable = dt.containsKey("agvRotation"); boolean rotatable = dt.containsKey("agvRotation");
if (rotatable) {
log.info("Node {} is rotatable", id);
}
// 提取邻居节点 // 提取邻居节点
List<String> in = (List<String>) dt.get("in"); List<String> in = (List<String>) dt.get("in");

2
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()); task.setBizTaskStatus(BizTaskStatus.DEVICE_EXECUTING.toString());
queryDSL.update(rcsTaskBiz) queryDSL.update(rcsTaskBiz)
.populate(task) .populate(task)
.set(rcsTaskBiz.updateAt, runtime.logisticsEnv.getCurrentDate()) .set(rcsTaskBiz.updateAt, runtime.getCurrentDate())
.set(rcsTaskBiz.updateBy, PTRTaskPlanner.class.getName()) .set(rcsTaskBiz.updateBy, PTRTaskPlanner.class.getName())
.where(rcsTaskBiz.bizTaskId.eq(task.getBizTaskId())) .where(rcsTaskBiz.bizTaskId.eq(task.getBizTaskId()))
.execute(); .execute();

4
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.base.Splitter;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.yvan.logisticsEnv.EnvPayload; import com.yvan.logisticsEnv.EnvConfig;
import com.yvan.logisticsModel.LogisticsRuntime; import com.yvan.logisticsModel.LogisticsRuntime;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -81,7 +81,7 @@ public class AmrMessageHandler {
this.runtime = runtime; 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); this.ptrMqttClient = new PtrMqttClient(this, mqttConfig, clientId);
} }

30
servo/src/main/java/com/galaxis/rcs/ptr/PtrAgvItem.java

@ -107,16 +107,38 @@ public abstract class PtrAgvItem extends ExecutorItem {
this.amrMessageHandler = logisticsRuntime.amrMessageHandler; 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(); public abstract RcsConfigMessage getConfig();
@SneakyThrows @Override
public synchronized void initialize() { public void start() {
this.amrMessageHandler.registeHeartBeatSet(this); this.amrMessageHandler.registeHeartBeatSet(this);
// 查询当前状态 // 查询当前状态
requestCurrentStatus(); requestCurrentStatus();
this.isInitialized = true; this.isRunning = true;
this.startConnector(); this.startConnector();
} }
@ -449,7 +471,6 @@ public abstract class PtrAgvItem extends ExecutorItem {
public void startConnector() { public void startConnector() {
if (!connectorThread.isRunning()) { if (!connectorThread.isRunning()) {
connectorThread.start(); connectorThread.start();
System.out.println("Connector started for executor: " + this.getId());
} }
} }
@ -458,7 +479,6 @@ public abstract class PtrAgvItem extends ExecutorItem {
*/ */
public void stopConnector() { public void stopConnector() {
connectorThread.stop(); connectorThread.stop();
System.out.println("Connector stopped for executor: " + this.getId());
} }
private static final int speed = 1000; private static final int speed = 1000;

9
servo/src/main/java/com/galaxis/rcs/ptr/PtrMqttClient.java

@ -1,6 +1,6 @@
package com.galaxis.rcs.ptr; package com.galaxis.rcs.ptr;
import com.yvan.logisticsEnv.EnvPayload; import com.yvan.logisticsEnv.EnvConfig;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.clever.core.BannerUtils; 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.MqttMessage;
import org.eclipse.paho.mqttv5.common.packet.MqttProperties; import org.eclipse.paho.mqttv5.common.packet.MqttProperties;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@Slf4j @Slf4j
public class PtrMqttClient implements MqttCallback { public class PtrMqttClient implements MqttCallback {
public final AmrMessageHandler amrMessageHandler; public final AmrMessageHandler amrMessageHandler;
public final EnvPayload.MqttConfig mqttConfig; public final EnvConfig.MqttConfig mqttConfig;
private final MqttClient clientForSend; private final MqttClient clientForSend;
private final MqttClient client; private final MqttClient client;
private final String clientId; private final String clientId;
@ -24,7 +23,7 @@ public class PtrMqttClient implements MqttCallback {
@SneakyThrows @SneakyThrows
public PtrMqttClient(AmrMessageHandler handler, EnvPayload.MqttConfig mqttConfig, String clientId) { public PtrMqttClient(AmrMessageHandler handler, EnvConfig.MqttConfig mqttConfig, String clientId) {
this.amrMessageHandler = handler; this.amrMessageHandler = handler;
this.mqttConfig = mqttConfig; this.mqttConfig = mqttConfig;
this.clientId = clientId; this.clientId = clientId;
@ -75,7 +74,7 @@ public class PtrMqttClient implements MqttCallback {
@SneakyThrows @SneakyThrows
public static void main(String[] args) { 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.setBrokerUrl("tcp://10.10.203.239:1885");
config.setUsername("admin"); config.setUsername("admin");
config.setPassword("admin"); config.setPassword("admin");

6
servo/src/main/java/com/galaxis/rcs/task/TaskService.java

@ -40,7 +40,7 @@ public class TaskService {
this.waitingTaskList = queryDSL.select(rcsTaskBiz) this.waitingTaskList = queryDSL.select(rcsTaskBiz)
.from(rcsTaskBiz) .from(rcsTaskBiz)
.where(rcsTaskBiz.bizTaskStatus.eq(BizTaskStatus.WAITING_FOR_DISPATCH.toString())) .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.priority.desc())
.orderBy(rcsTaskBiz.bizTaskId.asc()) .orderBy(rcsTaskBiz.bizTaskId.asc())
.fetch(); .fetch();
@ -83,7 +83,7 @@ public class TaskService {
RcsTaskBiz task = new RcsTaskBiz(); RcsTaskBiz task = new RcsTaskBiz();
task.setBizTaskId(taskId); task.setBizTaskId(taskId);
task.setEnvId(logisticsRuntime.logisticsEnv.getEnvId()); task.setEnvId(logisticsRuntime.envId);
task.setBizType(request.type.toString()); task.setBizType(request.type.toString());
task.setLpn(request.lpn); task.setLpn(request.lpn);
task.setPriority(request.priority); task.setPriority(request.priority);
@ -94,7 +94,7 @@ public class TaskService {
task.setBizTaskErrorInfo("N/A"); task.setBizTaskErrorInfo("N/A");
task.setBizTaskDescription(request.description); task.setBizTaskDescription(request.description);
task.setBizTaskStatus(BizTaskStatus.WAITING_FOR_DISPATCH.toString()); task.setBizTaskStatus(BizTaskStatus.WAITING_FOR_DISPATCH.toString());
task.setCreateAt(logisticsRuntime.logisticsEnv.getCurrentDate()); task.setCreateAt(logisticsRuntime.getCurrentDate());
task.setCreateBy(request.createBy); task.setCreateBy(request.createBy);
queryDSL.insert(rcsTaskBiz) queryDSL.insert(rcsTaskBiz)

37
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;
}
}

18
servo/src/main/java/com/yvan/logisticsEnv/EnvPayload.java

@ -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;
}
}

29
servo/src/main/java/com/yvan/logisticsEnv/EnvStartParam.java

@ -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;
}

88
servo/src/main/java/com/yvan/logisticsEnv/LogisticsEnv.java

@ -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());
}
}

30
servo/src/main/java/com/yvan/logisticsEnv/LogisticsEnvManager.java

@ -1,30 +0,0 @@
package com.yvan.logisticsEnv;
import java.util.HashMap;
import java.util.Map;
public class LogisticsEnvManager {
Map<String, LogisticsEnv> 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();
}
}

6
servo/src/main/java/com/yvan/logisticsModel/BaseItem.java

@ -33,10 +33,10 @@ public abstract class BaseItem {
public final LogisticsRuntime runtime; public final LogisticsRuntime runtime;
@JsonIgnore @JsonIgnore
public boolean isInitialized = false; public boolean isRunning = false;
public synchronized void initialize() { public synchronized void start() {
this.isInitialized = true; this.isRunning = true;
} }
public BaseItem(LogisticsRuntime runtime, Map<String, Object> map) { public BaseItem(LogisticsRuntime runtime, Map<String, Object> map) {

6
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 boolean isFree();
abstract public void dispatchTask(PlanTaskSequence taskSequence); abstract public void dispatchTask(PlanTaskSequence taskSequence);
abstract public void stop();
abstract public void start();
abstract public boolean isRunning();
} }

5
servo/src/main/java/com/yvan/logisticsModel/Floor.java

@ -19,4 +19,9 @@ public class Floor {
public Floor(String id) { public Floor(String id) {
this.id = id; this.id = id;
} }
@Override
public String toString() {
return id + "(" + itemMap.size() + ")";
}
} }

221
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.ptr.AmrMessageHandler;
import com.galaxis.rcs.task.TaskDispatchFactory; import com.galaxis.rcs.task.TaskDispatchFactory;
import com.galaxis.rcs.task.TaskService; import com.galaxis.rcs.task.TaskService;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.yvan.logisticsEnv.EnvStartParam; import com.yvan.workbench.model.entity.LccProject;
import com.yvan.logisticsEnv.LogisticsEnv; import com.yvan.workbench.model.entity.LccProjectEnv;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.clever.core.BannerUtils;
import org.clever.core.Conv; import org.clever.core.Conv;
import org.clever.core.json.JsonWrapper; import org.clever.core.json.JsonWrapper;
import org.clever.data.jdbc.DaoFactory; import org.clever.data.jdbc.DaoFactory;
import org.clever.data.jdbc.QueryDSL; import org.clever.data.jdbc.QueryDSL;
import org.clever.data.jdbc.querydsl.utils.QueryDslUtils; import org.clever.data.jdbc.querydsl.utils.QueryDslUtils;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -33,14 +36,55 @@ import static com.galaxis.rcs.common.query.QLccBasExecutor.lccBasExecutor;
*/ */
@Slf4j @Slf4j
public class LogisticsRuntime { public class LogisticsRuntime {
/** /**
* 物流执行环境 * 项目UUID
*/ */
public final LogisticsEnv logisticsEnv;
public final String projectUUID; public final String projectUUID;
/**
* 环境ID
*/
public final long envId; 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 @JsonIgnore
@ -55,7 +99,7 @@ public class LogisticsRuntime {
/** /**
* 物流流动单元周转箱托盘纸箱等 * 物流流动单元周转箱托盘纸箱等
*/ */
public final Map<String, FlowItem> flowItemMap = Maps.newHashMap(); public final Map<String, FlowItem> flowItemMap = Maps.newConcurrentMap();
/** /**
* 物流任务执行单元(如拣货台小车AGV堆垛机人等) * 物流任务执行单元(如拣货台小车AGV堆垛机人等)
@ -78,10 +122,13 @@ public class LogisticsRuntime {
public final AgvEventManager agvEventManager = new AgvEventManager(this); public final AgvEventManager agvEventManager = new AgvEventManager(this);
public LogisticsRuntime(LogisticsEnv logisticsEnv) { public LogisticsRuntime(LccProject project, LccProjectEnv env, String clientId) {
this.logisticsEnv = logisticsEnv; this.project = project;
this.projectUUID = logisticsEnv.getProjectUUID(); this.env = env;
this.envId = logisticsEnv.getEnvId(); this.projectUUID = project.getProjectUuid();
this.envId = env.getEnvId();
this.isVirtual = env.getIsVirtual();
this.clientId = clientId;
} }
/** /**
@ -137,16 +184,9 @@ public class LogisticsRuntime {
* "items": [] * "items": []
* } * }
* </pre> * </pre>
*
* @param jw 原始 Json 读取器
*/ */
public void loadMap(JsonWrapper jw) { public void loadMap(String catalogCode, List<Map<String, Object>> items) {
if (!"floor".equals(jw.asStr("t"))) { Floor floor = new Floor(catalogCode);
throw new RuntimeException("loadMap error, not floor type: " + jw.asStr("t"));
}
List<Map<String, Object>> items = (List<Map<String, Object>>) jw.getInnerMap().get("items");
Floor floor = new Floor(jw.asStr("catalogCode"));
this.floorMap.put(floor.id, floor); this.floorMap.put(floor.id, floor);
for (Map<String, Object> itemObject : items) { for (Map<String, Object> 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<String, Object>) 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 监听 // 启动 MQTT 监听
this.amrMessageHandler.start(param.getEnvPayload().getMqtt(), param.clientId); this.amrMessageHandler.start(this.env.getEnvConfig().getMqtt(), this.clientId);
// 开启所有机器人的任务处理 // 开启所有机器人的任务处理
Set<String> executorTypes = Sets.newHashSet(); Set<String> executorTypes = Sets.newHashSet();
for (ExecutorItem executorItem : executorItemMap.values()) { for (ExecutorItem executorItem : executorItemMap.values()) {
executorItem.initialize();
executorTypes.add(executorItem.getT()); executorTypes.add(executorItem.getT());
executorItem.start();
log.info("Executor {} started", executorItem.getId()); log.info("Executor {} started", executorItem.getId());
} }
@ -275,10 +250,21 @@ public class LogisticsRuntime {
// 启动任务指派服务 // 启动任务指派服务
this.taskDispatchFactory.startPolling(); 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() { public boolean isRunning() {
return this.logisticsEnv.isRunning(); return this.isRunning;
} }
/** /**
@ -289,4 +275,49 @@ public class LogisticsRuntime {
.flatMap(floor -> floor.itemMap.values().stream()) .flatMap(floor -> floor.itemMap.values().stream())
.iterator(); .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,
});
}
} }

44
servo/src/main/java/com/yvan/logisticsModel/LogisticsRuntimeService.java

@ -1,8 +1,11 @@
package com.yvan.logisticsModel; package com.yvan.logisticsModel;
import com.google.common.collect.Maps; 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; import java.util.Map;
/** /**
@ -13,6 +16,36 @@ public class LogisticsRuntimeService {
private static final Object LOCK = new Object(); private static final Object LOCK = new Object();
private final Map<String, LogisticsRuntime> runtimeMap = Maps.newHashMap(); private final Map<String, LogisticsRuntime> 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查找 物流运行时 * 根据项目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) { synchronized (LOCK) {
final String key = projectUUID + "_" + envId; final String key = project.getProjectUuid() + "_" + env.getEnvId();
if (runtimeMap.containsKey(key)) { if (runtimeMap.containsKey(key)) {
return runtimeMap.get(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); runtimeMap.put(key, runtime);
return runtime; return runtime;
} }

7
servo/src/main/java/com/yvan/workbench/autoconfigure/AppAutoConfiguration.java

@ -61,13 +61,6 @@ public class AppAutoConfiguration {
private final Environment environment; private final Environment environment;
@Bean @Bean
public RCSService rcsService() {
RCSService rcsService = new RCSService();
rcsService.init();
return rcsService;
}
@Bean
public AppBasicsConfig appBasicsConfig() { public AppBasicsConfig appBasicsConfig() {
AppBasicsConfig appBasicsConfig = AppBasicsConfig.create(environment); AppBasicsConfig appBasicsConfig = AppBasicsConfig.create(environment);
appBasicsConfig.init(); appBasicsConfig.init();

31
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());
}
}

18
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;
}
}

21
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.galaxis.rcs.RCSService;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.yvan.workbench.SpringContext;
import com.yvan.workbench.model.entity.Model; import com.yvan.workbench.model.entity.Model;
import com.yvan.workbench.service.LccMapService;
import org.clever.core.Conv; import org.clever.core.Conv;
import org.clever.core.model.response.R; import org.clever.core.model.response.R;
import org.clever.data.jdbc.DaoFactory; import org.clever.data.jdbc.DaoFactory;
@ -23,11 +25,20 @@ import static com.galaxis.rcs.common.query.QLccInvLpn.lccInvLpn;
public class EnvController { public class EnvController {
static final QueryDSL queryDSL = DaoFactory.getQueryDSL(); static final QueryDSL queryDSL = DaoFactory.getQueryDSL();
public static Model<List<LinkedHashMap<String, Object>>> getAllEnv(@RequestBody Map<String, Object> params) { public static Model<?> getAllEnv(@RequestBody Map<String, Object> params) {
var list = queryDSL.select(QueryDslUtils.linkedMap(lccEnvInfo)) // var list = queryDSL.select(QueryDslUtils.linkedMap(lccEnvInfo))
.from(lccEnvInfo) // .from(lccEnvInfo)
.where(lccEnvInfo.worldId.eq(Conv.asString(params.get("worldId")))) // .where(lccEnvInfo.worldId.eq(Conv.asString(params.get("worldId"))))
.fetch(); // .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); return Model.newSuccess(list);
} }

13
servo/src/main/java/com/yvan/workbench/controller/LccController.java

@ -1,6 +1,15 @@
package com.yvan.workbench.controller; 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());
}
} }

424
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.LccBasLocation;
import com.galaxis.rcs.common.entity.StoreLocation; import com.galaxis.rcs.common.entity.StoreLocation;
import com.galaxis.rcs.ptr.JacksonUtils; import com.galaxis.rcs.ptr.JacksonUtils;
import com.yvan.workbench.model.entity.LccModelFloor; import com.yvan.workbench.SpringContext;
import com.yvan.workbench.model.entity.LccModelWorld; 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.apache.commons.lang3.StringUtils;
import org.clever.core.Assert; import org.clever.core.Assert;
import org.clever.core.Conv; 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.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.core.model.response.R;
import org.clever.data.jdbc.DaoFactory; import org.clever.data.jdbc.DaoFactory;
import org.clever.data.jdbc.QueryDSL; 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.RequestBody;
import org.clever.web.mvc.annotation.RequestParam; import org.clever.web.mvc.annotation.RequestParam;
import org.clever.web.mvc.annotation.Transactional; import org.clever.web.mvc.annotation.Transactional;
@ -28,14 +25,12 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import static com.galaxis.rcs.common.query.QLccBasLocation.lccBasLocation; 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 { public class LccModelManager {
private static final QueryDSL QUERY_DSL = DaoFactory.getQueryDSL(); private static final QueryDSL QUERY_DSL = DaoFactory.getQueryDSL();
private static String getOtherData(String projectUuid, String projectLabel) { private static Map<String, Object> getOtherData(String projectUuid, String projectLabel) {
return StringUtils.trim(String.format(""" String content = StringUtils.trim(String.format("""
{ {
"project_uuid": "%s", "project_uuid": "%s",
"project_label": "%s", "project_label": "%s",
@ -84,111 +79,157 @@ public class LccModelManager {
"wall": [], "wall": [],
"pillar": [] "pillar": []
}""".stripIndent(), projectUuid, projectLabel)); }""".stripIndent(), projectUuid, projectLabel));
}
public static Page<LccModelWorld> projectList() { return JacksonMapper.getInstance().fromJson(content, Map.class);
return QueryDslUtils.queryByPage(
QUERY_DSL.selectFrom(lccModelWorld),
QueryByPage.getCurrent()
);
} }
@Transactional public static R<List<LccProject>> projectList() {
public static LccModelWorld addProject(@RequestBody LccModelWorld project) { // return QueryDslUtils.queryByPage(
Assert.isNotBlank(project.getProjectUuid(), "项目编号必填"); // QUERY_DSL.selectFrom(lccModelWorld),
Assert.isNotBlank(project.getProjectLabel(), "项目标题必填"); // QueryByPage.getCurrent()
Assert.isTrue( // );
QUERY_DSL.selectFrom(lccModelWorld).where(lccModelWorld.projectUuid.eq(project.getProjectUuid())).fetchCount() <= 0, var lccMapService = SpringContext.HOLDER.getBean(LccMapService.class);
"项目编号重复" return R.success(lccMapService.getAllProjects());
);
project.setProjectVersion(1L);
project.setId(SnowFlake.SNOW_FLAKE.nextId());
project.setDirectoryData("""
[
{
"label": "仓库楼层",
"items": [
{
"catalogCode": "f1",
"label": "一楼 (f1)"
} }
]
} // @Transactional
] public static LccProject addProject(@RequestBody Map<String, Object> entity) {
""".stripIndent()); 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())); project.setOtherData(getOtherData(project.getProjectUuid(), project.getProjectLabel()));
QUERY_DSL.insert(lccModelWorld).populate(project).execute(); lccMapService.saveProject(project);
return QUERY_DSL.selectFrom(lccModelWorld).where(lccModelWorld.id.eq(project.getId())).fetchOne(); // QUERY_DSL.insert(lccModelWorld).populate(project).execute();
// return QUERY_DSL.selectFrom(lccModelWorld).where(lccModelWorld.id.eq(project.getId())).fetchOne();
return project;
} }
@Transactional // @Transactional
public static R<?> addOrUpdateWorld(@RequestBody LccModelWorld params) { public static R<?> addOrUpdateWorld(@RequestBody Map<String, Object> entity) {
Assert.isNotBlank(params.getProjectUuid(), "项目编号必填"); String projectUuid = Conv.asString(entity.get("projectUuid"));
long count = QUERY_DSL.selectFrom(lccModelWorld) String projectLabel = Conv.asString(entity.get("projectLabel"));
.where(lccModelWorld.projectUuid.eq(params.getProjectUuid())) String directoryData = Conv.asString(entity.get("directoryData"));
.where(lccModelWorld.projectVersion.eq(1L)) String envId = Conv.asString(entity.get("envId"));
.fetchCount(); String otherData = Conv.asString(entity.get("otherData"));
if (count >= 1) {
QUERY_DSL.update(lccModelWorld) // 判断项目编号是否符合规范
.set(lccModelWorld.otherData, params.getOtherData()) var lccMapService = SpringContext.HOLDER.getBean(LccMapService.class);
.set(lccModelWorld.directoryData, params.getDirectoryData()) lccMapService.checkProjectUuid(projectUuid, false);
.where(lccModelWorld.projectUuid.eq(params.getProjectUuid()))
.where(lccModelWorld.projectVersion.eq(1L)) LccProject project = new LccProject();
.execute(); project.setProjectUuid(projectUuid);
} else { project.setProjectLabel(projectLabel);
//noinspection unchecked project.setProjectFileLocation("");
Map<String, Object> map = JacksonMapper.getInstance().fromJson(params.getOtherData(), Map.class); project.setDirectoryData(JacksonMapper.getInstance().fromJson(directoryData, LccProject.CatalogGroup[].class));
params.setId(SnowFlake.SNOW_FLAKE.nextId()); project.setOtherData(JacksonMapper.getInstance().fromJson(otherData, Map.class));
params.setProjectVersion(1L);
params.setProjectLabel(Conv.asString(map.get("project_label"))); lccMapService.saveProject(project);
params.setServer(Conv.asString(map.get("server")));
// params.setCreateBy(); // long count = QUERY_DSL.selectFrom(lccModelWorld)
QUERY_DSL.insert(lccModelWorld).populate(params).execute(); // .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<String, Object> 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(); return R.success();
} }
public static LccModelFloor getFloor(@RequestParam Map<String, Object> params) { @SneakyThrows
public static void getFloor(@RequestParam Map<String, Object> params, HttpServletResponse response) {
String catalogCode = Conv.asString(params.get("catalogCode")); String catalogCode = Conv.asString(params.get("catalogCode"));
String project_uuid = Conv.asString(params.get("project_uuid")); String project_uuid = Conv.asString(params.get("project_uuid"));
return QUERY_DSL.selectFrom(lccModelFloor)
.where(lccModelFloor.projectUuid.eq(project_uuid)) // return QUERY_DSL.selectFrom(lccModelFloor)
.where(lccModelFloor.catalogCode.eq(catalogCode)) // .where(lccModelFloor.projectUuid.eq(project_uuid))
.orderBy(lccModelFloor.projectVersion.desc()) // .where(lccModelFloor.catalogCode.eq(catalogCode))
.fetchFirst(); // .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 @Transactional
public static R<?> addOrUpdateFloor(@RequestBody LccModelFloor params) { public static R<?> addOrUpdateFloor(@RequestBody Map<String, Object> params) {
Assert.isNotBlank(params.getProjectUuid(), "项目编号必填"); String projectUuid = Conv.asString(params.get("projectUuid"));
Assert.isNotBlank(params.getCatalogCode(), "楼层编号必填"); String catalogCode = Conv.asString(params.get("catalogCode"));
Assert.notNull(params.getEnvId(), "环境编号必填"); Long envId = Conv.asLong(params.get("envId"));
Assert.isNotBlank(params.getItems(), "楼层数据必填"); String items = Conv.asString(params.get("items"));
if (params.getEnvId() == null || params.getEnvId() <= 0) {
throw new RuntimeException("环境编号必填"); Assert.isNotBlank(projectUuid, "projectUuid must not be blank");
} Assert.isNotBlank(catalogCode, "catalogCode must not be blank");
long count = QUERY_DSL.selectFrom(lccModelFloor) if (envId == null || envId < 0L) {
.where(lccModelFloor.projectUuid.eq(params.getProjectUuid())) throw new RuntimeException("items must not be blank");
.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();
} }
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(); 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) QUERY_DSL.delete(lccBasLocation)
.where(lccBasLocation.envId.eq(params.getEnvId())) .where(lccBasLocation.envId.eq(envId))
.where(lccBasLocation.catalogCode.eq(params.getCatalogCode())) .where(lccBasLocation.catalogCode.eq(catalogCode))
.execute(); .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 中解析出货位数据 // ================================== 从 params.items 中解析出货位数据
var items = JacksonUtils.parseList(params.getItems(), HashMap.class); var items = JacksonUtils.parseList(itemsJson, HashMap.class);
var storeMap = new HashMap<String, LccBasLocation>(); var storeMap = new HashMap<String, LccBasLocation>();
for (Map<String, Object> item : items) { for (Map<String, Object> item : items) {
String type = Conv.asString(item.get("t")); String type = Conv.asString(item.get("t"));
@ -278,8 +405,8 @@ public class LccModelManager {
// 全部写进数据库 // 全部写进数据库
var action = QUERY_DSL.insert(lccBasLocation); var action = QUERY_DSL.insert(lccBasLocation);
for (LccBasLocation basLocation : storeMap.values()) { for (LccBasLocation basLocation : storeMap.values()) {
basLocation.setEnvId(params.getEnvId()); basLocation.setEnvId(envId);
basLocation.setCatalogCode(params.getCatalogCode()); basLocation.setCatalogCode(catalogCode);
basLocation.setCell(0); basLocation.setCell(0);
basLocation.setIsLock(0); basLocation.setIsLock(0);
basLocation.setIsFrozen(0); basLocation.setIsFrozen(0);
@ -293,91 +420,6 @@ public class LccModelManager {
action.execute(); 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);
*/
} }
} }

30
servo/src/main/java/com/yvan/workbench/controller/RcsController.java

@ -1,15 +1,21 @@
package com.yvan.workbench.controller; package com.yvan.workbench.controller;
import com.galaxis.rcs.common.entity.*; import com.galaxis.rcs.RCSService;
import com.galaxis.rcs.common.enums.*; 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.PlanTaskSequence;
import com.galaxis.rcs.plan.task.*; import com.galaxis.rcs.plan.task.*;
import com.galaxis.rcs.ptr.PtrAgvItem; import com.galaxis.rcs.ptr.PtrAgvItem;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Maps; 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 com.yvan.workbench.model.entity.Model;
import org.apache.commons.lang3.NotImplementedException;
import org.clever.core.Conv; import org.clever.core.Conv;
import org.clever.core.id.SnowFlake; import org.clever.core.id.SnowFlake;
import org.clever.core.model.response.R; import org.clever.core.model.response.R;
@ -20,6 +26,22 @@ import java.util.Map;
public class RcsController { public class RcsController {
static final SnowFlake snowFlake = new SnowFlake(); static final SnowFlake snowFlake = new SnowFlake();
public static R<?> projectStart(@RequestBody Map<String, Object> 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<String, Object> params) { public static Model<?> agvToCharger(@RequestBody Map<String, Object> params) {
Object ret = getCommonParamAndCreateBizTask(params); Object ret = getCommonParamAndCreateBizTask(params);
if (ret instanceof Model) { if (ret instanceof Model) {

34
servo/src/main/java/com/yvan/workbench/model/entity/LccModelFloor.java

@ -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;
}

35
servo/src/main/java/com/yvan/workbench/model/entity/LccModelWorld.java

@ -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;
}

27
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<String, Object> otherData;
@Data
public static class CatalogGroup {
private String label;
private CatalogItem[] items;
}
@Data
public static class CatalogItem {
private String catalogCode;
private String label;
}
}

15
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;
}

80
servo/src/main/java/com/yvan/workbench/model/query/QLccModelFloor.java

@ -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<LccModelFloor> {
/** lcc_model_floor表 */
public static final QLccModelFloor lccModelFloor = new QLccModelFloor("lcc_model_floor");
/** */
public final NumberPath<Long> id = createNumber("id", Long.class);
/** 项目编号 */
public final StringPath projectUuid = createString("projectUuid");
/** 项目版本 */
public final NumberPath<Long> projectVersion = createNumber("projectVersion", Long.class);
/** 环境ID */
public final NumberPath<Long> 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<Date> createAt = createDateTime("createAt", Date.class);
/** 创建人 */
public final StringPath createBy = createString("createBy");
/** 最后更新时间,默认为当前时间,并在更新时自动更新 */
public final DateTimePath<Date> 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<? extends LccModelFloor> 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));
}
}

86
servo/src/main/java/com/yvan/workbench/model/query/QLccModelWorld.java

@ -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<LccModelWorld> {
/** lcc_model_world表 */
public static final QLccModelWorld lccModelWorld = new QLccModelWorld("lcc_model_world");
/** */
public final NumberPath<Long> id = createNumber("id", Long.class);
/** 项目编号 */
public final StringPath projectUuid = createString("projectUuid");
/** 项目标题 */
public final StringPath projectLabel = createString("projectLabel");
/** 项目版本 */
public final NumberPath<Long> 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<Date> createAt = createDateTime("createAt", Date.class);
/** 创建人 */
public final StringPath createBy = createString("createBy");
/** 最后更新时间,默认为当前时间,并在更新时自动更新 */
public final DateTimePath<Date> 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<? extends LccModelWorld> 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));
}
}

43
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();
}
}

291
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<LccProject> 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<LccProject> 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<LccProjectEnv> 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<LccProjectEnv> 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<String> 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<String> 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;
}
}

6
servo/src/main/resources/application-dev.yml

@ -4,6 +4,12 @@ server:
app: app:
root-path: './' root-path: './'
lcc:
location: './lcc-map'
autoStart:
- projectUuid: tw_test
envId: 1
mybatis: mybatis:
enable: true enable: true
watcher: true watcher: true

Loading…
Cancel
Save