package com.yvan.logisticsModel; import com.galaxis.rcs.connector.cl2.Cl2Item; import com.galaxis.rcs.plan.path.NavigationGraph; import com.galaxis.rcs.plan.path.PtrPathPlanner; import com.galaxis.rcs.ptr.AmrMessageHandler; import com.galaxis.rcs.task.TaskDispatchFactory; import com.galaxis.rcs.task.TaskService; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.yvan.event.AgvEventManager; import com.yvan.event.AgvEventType; import com.yvan.mqtt.FrontendMessagePushService; import com.yvan.redis.LccRedisService; import com.yvan.workbench.model.entity.LccProject; import com.yvan.workbench.model.entity.LccProjectEnv; import lombok.extern.slf4j.Slf4j; import org.clever.core.BannerUtils; import org.clever.core.Conv; import org.clever.data.redis.Redis; import org.clever.data.redis.RedisAdmin; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; /** * 物流运行时 */ @Slf4j public class LogisticsRuntime { /** * 项目UUID */ public final String projectUuid; /** * 环境ID */ public final long envId; /** * 项目 */ public final LccProject project; /** * 环境 */ public final LccProjectEnv env; /** * 是否为虚拟环境 */ public final boolean isVirtual; /** * 服务器名_PID */ public final String serverId; /** * 是否正在运行 */ private boolean isRunning; /** * 环境开始时间 */ private long startTime; /** * 环境停止时间 */ private long stopTime; /** * 时间流速倍率 */ private float timeRate; private final Redis redis = RedisAdmin.getRedis(); /** * 任务服务 */ public final TaskService taskService = new TaskService(this); /** * 任务分配服务 */ public final TaskDispatchFactory taskDispatchFactory = new TaskDispatchFactory(this); /** * 物流任务执行单元(如拣货台、小车、AGV、堆垛机、人等) */ public final Map executorItemMap = Maps.newHashMap(); /** * AGV导航地图 车类型 t -> NavigationGraph */ public final Map pathPlannerMap = Maps.newHashMap(); /** * 楼层目录 catalogCode -> Floor */ public final Map floorMap = Maps.newHashMap(); public final AmrMessageHandler amrMessageHandler = new AmrMessageHandler(this); public final FrontendMessagePushService frontendMessagePushService = new FrontendMessagePushService(this); public final LccRedisService lccRedisService = new LccRedisService(this); public final AgvEventManager eventManager = new AgvEventManager(); public LogisticsRuntime(LccProject project, LccProjectEnv env, String serverId) { this.project = project; this.env = env; this.projectUuid = project.getProjectUuid(); this.envId = env.getEnvId(); this.isVirtual = env.getIsVirtual(); this.serverId = serverId; this.setupEventHandle(); } private void setupEventHandle() { eventManager.subscribe((AgvEventType type, Object... args) -> { ExecutorItem sender = (ExecutorItem) args[0]; String[] eventArgs = new String[args.length - 1]; for (int i = 1; i < args.length; i++) { eventArgs[i - 1] = Conv.asString(args[i]); } BannerUtils.printConfig(log, this.projectUuid + "(" + this.envId + ") " + type + " AGV:" + sender.getId(), eventArgs); }); } /** * 获取当前空闲的执行器列表 */ public List getFreeExecutorList() { return Lists.newArrayList(); // List freeExecutorList = Lists.newArrayList(); // for (ExecutorItem executorItem : executorItemMap.values()) { // if (executorItem.isFree()) { // freeExecutorList.add(executorItem); // } // } // return freeExecutorList; } /** * 根据 ID 获取静态物品(如路标点、货架等) */ public StaticItem getStaticItemById(String itemId) { // 到所有楼层寻找这个物品 for (Floor floor : this.floorMap.values()) { StaticItem item = floor.itemMap.get(itemId); if (item != null) { return item; } } return null; } /** * 根据逻辑坐标, 获取静态物品(如路标点、货架、地堆位等) */ public StaticItem getStaticItemByLogicXY(int logicX, int logicY) { // 到所有楼层寻找这个物品 for (Floor floor : this.floorMap.values()) { for (StaticItem item : floor.itemMap.values()) { if (item.logicX == logicX && item.logicY == logicY) { return item; } } } return null; } /** * 读取某个楼层的地图数据 * 格式必须是如下模式: *
     * {
     *     "catalogCode": "f2",
     *     "t": "floor",
     *     "items": []
     * }
     * 
*/ public void loadMap(String catalogCode, List> items) { Floor floor = new Floor(catalogCode); this.floorMap.put(floor.id, floor); for (Map itemObject : items) { String t = Conv.asString(itemObject.get("t")); BaseItem item; switch (t) { case "way": case "gstore": case "rack": case "charger": item = new StaticItem(this, itemObject); floor.itemMap.put(item.getId(), (StaticItem) item); break; case "pallet": case "carton": // 库存不在运行时加载 // item = new FlowItem(this, itemObject); // this.flowItemMap.put(item.getId(), (FlowItem) item); break; case "cl2": case "clx": item = new Cl2Item(this, itemObject); this.executorItemMap.put(item.getId(), (ExecutorItem) item); break; } } } /** * 启动物流环境 */ public void start() { // 启动环境计时 if (this.isRunning) { throw new RuntimeException("environment is already running"); } BannerUtils.printConfig(log, "LogisticsRuntime Start", new String[]{ "projectUUID: " + this.projectUuid, "envId: " + this.envId, "projectPath: " + this.project.getProjectFileLocation(), "envPath: " + this.env.getFileLocation(), "virtual: " + this.isVirtual, "floor: " + Joiner.on(",").join(this.floorMap.values().stream().map(Floor::toString).toList()), "mqtt: " + this.env.getEnvConfig().getMqtt().getBrokerUrl(), "serverId: " + this.serverId, "executors: " + this.executorItemMap.size(), }); this.isRunning = true; this.startTime = System.currentTimeMillis(); this.stopTime = 0L; this.timeRate = 1.0f; // 启动 MQTT 监听 this.amrMessageHandler.start(this.env.getEnvConfig().getMqtt(), this.serverId); this.frontendMessagePushService.start(this.env.getEnvConfig().getMqtt(), this.serverId + "_lcc_send"); this.lccRedisService.start(this.env.getEnvConfig().getRedis(), this.serverId); // 开启所有机器人的任务处理 Set executorTypes = Sets.newHashSet(); for (ExecutorItem executorItem : executorItemMap.values()) { executorTypes.add(executorItem.getT()); executorItem.start(); log.info("Executor {} started", executorItem.getId()); } // 初始化各类机器人的地图计算 NavigationGraph graph = new NavigationGraph(this); graph.init(); for (String type : executorTypes) { this.pathPlannerMap.put(type, new PtrPathPlanner(graph)); } // 启动任务指派服务 this.taskDispatchFactory.startPolling(); } public boolean isRunning() { return this.isRunning; } /** * 获取所有楼层的静态物品(如路标点、货架、地堆位等) */ public Iterable getStaticItems() { return () -> this.floorMap.values().stream() .flatMap(floor -> floor.itemMap.values().stream()) .iterator(); } public long currentTimeMillis() { if (!this.isVirtual) { // 正式环境,返回系统当前时间 return System.currentTimeMillis(); } if (!this.isRunning) { return this.stopTime; } long realCost = System.currentTimeMillis() - this.startTime; return Conv.asLong(this.startTime + (realCost * this.timeRate)); } public Date getCurrentDate() { return new Date(this.currentTimeMillis()); } public void stop() { if (!this.isRunning) { throw new RuntimeException("environment is not running"); } this.isRunning = false; this.stopTime = System.currentTimeMillis(); // 停止任务指派服务 this.taskDispatchFactory.stopPolling(); // 停止所有机器人的任务处理 for (ExecutorItem executorItem : executorItemMap.values()) { executorItem.stop(); log.info("Executor {} stopped", executorItem.getId()); } // 停止 MQTT 监听 this.amrMessageHandler.stop(); this.frontendMessagePushService.stop(); this.lccRedisService.stop(); BannerUtils.printConfig(log, "LogisticsRuntime stop.", new String[]{ "projectUUID: " + this.projectUuid, "envId: " + this.envId, "virtual: " + this.isVirtual, "serverId: " + this.serverId, "stopTime: " + new Date(this.stopTime), "timeRate: " + this.timeRate, }); } public RuntimeState getState() { RuntimeState state = new RuntimeState(); state.projectUuid = this.projectUuid; state.envId = this.envId; state.isVirtual = this.isVirtual; state.serverId = this.serverId; state.isRunning = this.isRunning; state.startTime = this.startTime; state.stopTime = this.stopTime; state.timeRate = this.timeRate; state.subSystemList = this.project.getSubSystemList(); state.fillSystemInfos(); return state; } }