台湾展会用
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

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