You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
398 lines
16 KiB
398 lines
16 KiB
package com.galaxis.rcs;
|
|
|
|
import com.galaxis.rcs.common.entity.AddTaskRequest;
|
|
import com.galaxis.rcs.common.entity.AddTaskResult;
|
|
import com.galaxis.rcs.common.enums.LCCDirection;
|
|
import com.galaxis.rcs.connector.cl2.Cl2Item;
|
|
import com.galaxis.rcs.plan.path.PathUtils;
|
|
import com.google.common.base.Strings;
|
|
import com.yvan.logisticsModel.LogisticsRuntime;
|
|
import com.yvan.logisticsModel.LogisticsRuntimeService;
|
|
import com.yvan.logisticsModel.StaticItem;
|
|
import com.yvan.workbench.SpringContext;
|
|
import com.yvan.workbench.model.entity.LccProject;
|
|
import com.yvan.workbench.service.LccMapService;
|
|
import lombok.SneakyThrows;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
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.util.List;
|
|
import java.util.Map;
|
|
|
|
import static com.galaxis.rcs.common.query.QLccBasExecutor.lccBasExecutor;
|
|
|
|
/**
|
|
* RCS 对外API调用类
|
|
*/
|
|
@Slf4j
|
|
public class RCSService {
|
|
public static void ok() {
|
|
}
|
|
|
|
/**
|
|
* 添加任务
|
|
*/
|
|
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
|
|
public static void projectStart(String projectUuid, Long envId) {
|
|
LogisticsRuntime runtime = LogisticsRuntimeService.INSTANCE.getByProjectEnv(projectUuid, envId);
|
|
|
|
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.");
|
|
}
|
|
|
|
var env = lccMapService.getEnvById(projectUuid, envId);
|
|
if (env.getEnvConfig() == null) {
|
|
throw new RuntimeException("Project " + projectUuid + " envId " + envId + " has no envConfig data.");
|
|
}
|
|
|
|
runtime = LogisticsRuntimeService.INSTANCE.create(project, env);
|
|
|
|
// 获取所有楼层数据
|
|
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<Map<String, Object>> items = JacksonMapper.getInstance().fromJson(floorJson, List.class);
|
|
runtime.loadMap(catalogCode, items);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 读取所有的库存
|
|
|
|
// 读取所有的执行器
|
|
|
|
// 读取地图上所有的车
|
|
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);
|
|
|
|
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; // 跳过虚拟位置为空的执行器
|
|
}
|
|
|
|
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];
|
|
|
|
if(Strings.isNullOrEmpty(wayPointId)){
|
|
log.warn("wayPointId is empty for executorId: {}", item.get("executor_id"));
|
|
continue; // 跳过wayPointId为空的执行器
|
|
}
|
|
|
|
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());
|
|
|
|
// 找到地标位置
|
|
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;
|
|
|
|
default:
|
|
log.warn("Unknown executor type: {}", jwPayload.asStr("t"));
|
|
continue; // 跳过未知类型
|
|
}
|
|
}
|
|
|
|
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() {
|
|
// String executorId = "10"; // 执行器ID
|
|
// String lpn = "pallet1124";
|
|
// long envId = 1;
|
|
//
|
|
// LogisticsRuntime logisticsRuntime = LogisticsRuntimeService.INSTANCE.findByEnvCode(envId);
|
|
// RcsTaskBiz bizTask = new RcsTaskBiz();
|
|
// bizTask.setBizTaskId(100L);
|
|
// bizTask.setEnvId(envId);
|
|
// bizTask.setBizType(BizTaskType.CARRY.toString());
|
|
// bizTask.setLpn(lpn);
|
|
// bizTask.setPriority(1);
|
|
// bizTask.setTaskFrom("rack1_0_1_0");
|
|
// bizTask.setTaskTo("54_0_0_0");
|
|
// bizTask.setAllocatedExecutorId(executorId);
|
|
// bizTask.setBizTaskPayload("N/A");
|
|
// bizTask.setBizTaskErrorInfo("N/A");
|
|
// bizTask.setBizTaskDescription("N/A");
|
|
// bizTask.setBizTaskStatus(BizTaskStatus.WAITING_FOR_DISPATCH.toString());
|
|
//
|
|
// PlanTaskSequence planSequence = new PlanTaskSequence(executorId, logisticsRuntime, bizTask, "demo");
|
|
//
|
|
// CarryTask carryTask = new CarryTask(
|
|
// executorId, lpn, 1,
|
|
// new StoreLocation("rack1", 0, 1, 0),
|
|
// new StoreLocation("54", 0, 0, 0)
|
|
// );
|
|
//
|
|
// logisticsRuntime.pathPlannerMap.get("cl2")
|
|
// .planCarryTask(planSequence, "17", LCCDirection.DOWN, carryTask);
|
|
// return planSequence.toPrettyMap();
|
|
// }
|
|
//
|
|
// public static Object runPath2() {
|
|
// String executorId = "10"; // 执行器ID
|
|
// String lpn = "pallet1124";
|
|
// long envId = 1;
|
|
//
|
|
// LogisticsRuntime logisticsRuntime = LogisticsRuntimeService.INSTANCE.findByEnvCode(envId);
|
|
// RcsTaskBiz bizTask = new RcsTaskBiz();
|
|
// bizTask.setBizTaskId(100L);
|
|
// bizTask.setEnvId(envId);
|
|
// bizTask.setBizType(BizTaskType.CARRY.toString());
|
|
// bizTask.setLpn(lpn);
|
|
// bizTask.setPriority(1);
|
|
// bizTask.setTaskFrom("rack1_0_1_0");
|
|
// bizTask.setTaskTo("54_0_0_0");
|
|
// bizTask.setAllocatedExecutorId(executorId);
|
|
// bizTask.setBizTaskPayload("N/A");
|
|
// bizTask.setBizTaskErrorInfo("N/A");
|
|
// bizTask.setBizTaskDescription("N/A");
|
|
// bizTask.setBizTaskStatus(BizTaskStatus.WAITING_FOR_DISPATCH.toString());
|
|
//
|
|
// PlanTaskSequence planSequence = new PlanTaskSequence(executorId, logisticsRuntime, bizTask, "demo");
|
|
//
|
|
// CarryTask carryTask = new CarryTask(
|
|
// executorId, lpn, 1,
|
|
// new StoreLocation("rack1", 0, 1, 0),
|
|
// new StoreLocation("54", 0, 0, 0)
|
|
// );
|
|
//
|
|
// logisticsRuntime.pathPlannerMap.get("cl2")
|
|
// .planCarryTask(planSequence, "27", LCCDirection.UP, carryTask);
|
|
// return planSequence.toPrettyMap();
|
|
// }
|
|
//
|
|
// public static void runDemo() {
|
|
// String executorId = "10"; // 执行器ID
|
|
// String lpn = "pallet1124";
|
|
// long envId = 1;
|
|
//
|
|
// AddTaskRequest request = new AddTaskRequest();
|
|
// request.setType(BizTaskType.CARRY);
|
|
// request.setTaskExecutorId(executorId);
|
|
// request.setLpn(lpn);
|
|
// request.setPriority(1);
|
|
// request.setFrom(new StoreLocation("rack1", 0, 1, 0));
|
|
// request.setTo(new StoreLocation("54", 0, 0, 0));
|
|
// request.setDescription("从rack1的0/1/0, 取货到地堆54");
|
|
// request.setCreateBy("runDemo");
|
|
//
|
|
// AddTaskResult result = RCSService.addTask(envId, request);
|
|
//
|
|
// LogisticsRuntime logisticsRuntime = LogisticsRuntimeService.INSTANCE.findByEnvCode(envId);
|
|
// RcsTaskBiz bizTask = logisticsRuntime.taskService.waitingTaskList.get(logisticsRuntime.taskService.waitingTaskList.size() - 1);
|
|
//
|
|
//
|
|
// Planner planner = TaskPlannerFactory.getPlanner("cl2");
|
|
//
|
|
// // 规划任务序列
|
|
// PlanTaskSequence planSequence = new PlanTaskSequence(executorId, logisticsRuntime, bizTask, "demo");
|
|
// planSequence.addMoveTo("20");
|
|
// planSequence.addMoveTo("21");
|
|
// planSequence.addMoveTo("22");
|
|
// planSequence.addMoveTo("23");
|
|
// planSequence.addRotationTo(90);
|
|
// planSequence.addMoveTo("24");
|
|
// planSequence.addMoveTo("25");
|
|
// planSequence.addMoveTo("26");
|
|
// planSequence.addMoveTo("27");
|
|
// planSequence.addLoad(lpn, "rack1", 0, 1, 0);
|
|
// planSequence.addMoveTo("26");
|
|
// planSequence.addMoveTo("25");
|
|
// planSequence.addMoveTo("24");
|
|
// planSequence.addMoveTo("23");
|
|
// planSequence.addRotationTo(-90);
|
|
// planSequence.addMoveTo("22");
|
|
// planSequence.addMoveTo("21");
|
|
// planSequence.addMoveTo("20");
|
|
// planSequence.addUnload("54", 0, 0, 0);
|
|
// planSequence.addFinish();
|
|
//
|
|
// planner.appendSequence(planSequence);
|
|
// }
|
|
//
|
|
// public static LogisticsRuntime getDemo() {
|
|
// return LogisticsRuntimeService.INSTANCE.findByEnvCode(1L);
|
|
// }
|
|
}
|
|
|