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.
212 lines
6.4 KiB
212 lines
6.4 KiB
package com.yvan.logisticsModel;
|
|
|
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
import com.galaxis.rcs.connector.cl2.Cl2Item;
|
|
import com.galaxis.rcs.plan.path2.NavigationGraph;
|
|
import com.galaxis.rcs.plan.path2.PtrPathPlanner;
|
|
import com.galaxis.rcs.ptr.AgvEventManager;
|
|
import com.galaxis.rcs.ptr.AmrMessageHandler;
|
|
import com.galaxis.rcs.task.TaskDispatchFactory;
|
|
import com.galaxis.rcs.task.TaskService;
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.collect.Maps;
|
|
import com.google.common.collect.Sets;
|
|
import com.yvan.logisticsEnv.EnvStartParam;
|
|
import com.yvan.logisticsEnv.LogisticsEnv;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.clever.core.Conv;
|
|
import org.clever.core.json.JsonWrapper;
|
|
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* 物流上下文运行时
|
|
*/
|
|
@Slf4j
|
|
public class LogisticsRuntime {
|
|
|
|
/**
|
|
* 物流执行环境
|
|
*/
|
|
public final LogisticsEnv logisticsEnv;
|
|
public final String projectUUID;
|
|
public final long envId;
|
|
/**
|
|
* 任务服务
|
|
*/
|
|
@JsonIgnore
|
|
public final TaskService taskService = new TaskService(this);
|
|
|
|
/**
|
|
* 任务分配服务
|
|
*/
|
|
@JsonIgnore
|
|
public final TaskDispatchFactory taskDispatchFactory = new TaskDispatchFactory(this);
|
|
|
|
/**
|
|
* 物流流动单元(周转箱、托盘、纸箱等)
|
|
*/
|
|
public final Map<String, FlowItem> flowItemMap = Maps.newHashMap();
|
|
|
|
/**
|
|
* 物流任务执行单元(如拣货台、小车、AGV、堆垛机、人等)
|
|
*/
|
|
public final Map<String, ExecutorItem> executorItemMap = Maps.newHashMap();
|
|
|
|
/**
|
|
* AGV导航地图 车类型 t -> NavigationGraph
|
|
*/
|
|
@JsonIgnore
|
|
public final Map<String, PtrPathPlanner> pathPlannerMap = Maps.newHashMap();
|
|
|
|
/**
|
|
* 楼层目录 catalogCode -> Floor
|
|
*/
|
|
public final Map<String, Floor> floorMap = Maps.newHashMap();
|
|
|
|
@JsonIgnore
|
|
public final AmrMessageHandler amrMessageHandler = new AmrMessageHandler(this);
|
|
|
|
public final AgvEventManager agvEventManager = new AgvEventManager(this);
|
|
|
|
public LogisticsRuntime(LogisticsEnv logisticsEnv) {
|
|
this.logisticsEnv = logisticsEnv;
|
|
this.projectUUID = logisticsEnv.getProjectUUID();
|
|
this.envId = logisticsEnv.getEnvId();
|
|
}
|
|
|
|
/**
|
|
* 获取当前空闲的执行器列表
|
|
*/
|
|
public List<ExecutorItem> getFreeExecutorList() {
|
|
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>
|
|
*
|
|
* @param jw 原始 Json 读取器
|
|
*/
|
|
public void loadMap(JsonWrapper jw) {
|
|
if (!"floor".equals(jw.asStr("t"))) {
|
|
throw new RuntimeException("loadMap error, not floor type: " + jw.asStr("t"));
|
|
}
|
|
List<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);
|
|
|
|
for (Map<String, Object> itemObject : items) {
|
|
String t = Conv.asString(itemObject.get("t"));
|
|
BaseItem item;
|
|
switch (t) {
|
|
case "way":
|
|
case "gstore":
|
|
case "rack":
|
|
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(EnvStartParam param) {
|
|
// 启动环境计时
|
|
this.logisticsEnv.start(param);
|
|
|
|
// 启动 MQTT 监听
|
|
this.amrMessageHandler.start(param.getEnvPayload().getMqtt(), param.clientId);
|
|
|
|
// 开启所有机器人的任务处理
|
|
Set<String> executorTypes = Sets.newHashSet();
|
|
for (ExecutorItem executorItem : executorItemMap.values()) {
|
|
executorItem.initialize();
|
|
executorTypes.add(executorItem.getT());
|
|
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.logisticsEnv.isRunning();
|
|
}
|
|
|
|
/**
|
|
* 获取所有楼层的静态物品(如路标点、货架、地堆位等)
|
|
*/
|
|
public Iterable<StaticItem> getStaticItems() {
|
|
return () -> this.floorMap.values().stream()
|
|
.flatMap(floor -> floor.itemMap.values().stream())
|
|
.iterator();
|
|
}
|
|
}
|
|
|