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.
358 lines
11 KiB
358 lines
11 KiB
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<String, ExecutorItem> executorItemMap = Maps.newHashMap();
|
|
|
|
/**
|
|
* AGV导航地图 车类型 t -> NavigationGraph
|
|
*/
|
|
public final Map<String, PtrPathPlanner> pathPlannerMap = Maps.newHashMap();
|
|
|
|
/**
|
|
* 楼层目录 catalogCode -> Floor
|
|
*/
|
|
public final Map<String, Floor> 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<ExecutorItem> getFreeExecutorList() {
|
|
return Lists.newArrayList();
|
|
// List<ExecutorItem> 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;
|
|
}
|
|
|
|
/**
|
|
* 读取某个楼层的地图数据
|
|
* 格式必须是如下模式:
|
|
* <pre>
|
|
* {
|
|
* "catalogCode": "f2",
|
|
* "t": "floor",
|
|
* "items": []
|
|
* }
|
|
* </pre>
|
|
*/
|
|
public void loadMap(String catalogCode, List<Map<String, Object>> items) {
|
|
Floor floor = new Floor(catalogCode);
|
|
this.floorMap.put(floor.id, floor);
|
|
|
|
for (Map<String, Object> 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<String> 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<StaticItem> 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;
|
|
}
|
|
}
|
|
|