package com.zy.asrs.wcs.core.service.impl;
|
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONObject;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.zy.asrs.framework.common.Cools;
|
import com.zy.asrs.framework.common.SnowflakeIdWorker;
|
import com.zy.asrs.wcs.core.domain.dto.RedisMapDto;
|
import com.zy.asrs.wcs.core.entity.*;
|
import com.zy.asrs.wcs.core.kernel.AnalyzeService;
|
import com.zy.asrs.wcs.core.model.MapNode;
|
import com.zy.asrs.wcs.core.model.enums.DeviceCtgType;
|
import com.zy.asrs.wcs.core.model.enums.MotionStsType;
|
import com.zy.asrs.wcs.core.model.enums.TaskStsType;
|
import com.zy.asrs.wcs.core.service.*;
|
import com.zy.asrs.wcs.core.utils.RedisUtil;
|
import com.zy.asrs.wcs.core.utils.ShuttleDispatcher;
|
import com.zy.asrs.wcs.core.utils.Utils;
|
import com.zy.asrs.wcs.rcs.News;
|
import com.zy.asrs.wcs.rcs.cache.SlaveConnection;
|
import com.zy.asrs.wcs.rcs.constant.DeviceRedisConstant;
|
import com.zy.asrs.wcs.rcs.entity.Device;
|
import com.zy.asrs.wcs.rcs.model.enums.ShuttleProtocolStatusType;
|
import com.zy.asrs.wcs.rcs.model.enums.SlaveType;
|
import com.zy.asrs.wcs.rcs.model.protocol.ShuttleProtocol;
|
import com.zy.asrs.wcs.rcs.model.protocol.StaProtocol;
|
import com.zy.asrs.wcs.rcs.service.DeviceService;
|
import com.zy.asrs.wcs.rcs.thread.DevpThread;
|
import com.zy.asrs.wcs.rcs.thread.ShuttleThread;
|
import com.zy.asrs.wcs.system.entity.Dict;
|
import com.zy.asrs.wcs.system.service.DictService;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.util.*;
|
|
/**
|
* 立体仓库WCS系统主流程业务
|
* Created by vincent on 2020/8/6
|
*/
|
@Slf4j
|
@Service("mainService")
|
@Transactional
|
public class MainServiceImpl {
|
|
@Autowired
|
private TaskService taskService;
|
@Autowired
|
private AnalyzeService analyzeService;
|
@Autowired
|
private MotionService motionService;
|
@Autowired
|
private DeviceService deviceService;
|
@Autowired
|
private LocCtgService locCtgService;
|
@Autowired
|
private LocService locService;
|
@Autowired
|
private SnowflakeIdWorker snowflakeIdWorker;
|
@Autowired
|
private TaskCtgService taskCtgService;
|
@Autowired
|
private DictService dictService;
|
@Autowired
|
private ShuttleDispatcher shuttleDispatcher;
|
@Autowired
|
private RedisUtil redisUtil;
|
|
/**
|
* 组托
|
* 入库站,根据条码扫描生成入库工作档,工作状态 2
|
*/
|
public synchronized void generateInboundWrk() {
|
|
}
|
|
/**
|
* 初始化实时地图
|
*/
|
public synchronized void initRealtimeBasMap() {
|
try {
|
List<Dict> dicts = dictService.list(new LambdaQueryWrapper<Dict>()
|
.like(Dict::getFlag, "map-")
|
.eq(Dict::getStatus, 1));
|
|
TreeMap<Integer, ArrayList<ArrayList<MapNode>>> levData = new TreeMap<>();
|
for (Dict dict : dicts) {
|
String[] split = dict.getFlag().split("-");
|
int lev = Integer.parseInt(split[1]);
|
|
TreeMap<Integer, List<JSONObject>> rows = new TreeMap<>();
|
//排序Row
|
JSONArray value = JSON.parseArray(dict.getValue());
|
for (Object o : value) {
|
JSONObject item = JSON.parseObject(o.toString());
|
if (item.getString("type").equals("SHELF")) {
|
JSONObject property = JSON.parseObject(item.getString("property"));
|
Integer row1 = property.getInteger("row");
|
ArrayList<JSONObject> bays = new ArrayList<>();
|
if (rows.containsKey(row1)) {
|
bays.addAll(rows.get(row1));
|
}
|
bays.add(property);
|
rows.put(row1, bays);
|
}
|
}
|
|
ArrayList<ArrayList<MapNode>> list = new ArrayList<>();
|
//排序Bay
|
for (Map.Entry<Integer, List<JSONObject>> entry : rows.entrySet()) {
|
ArrayList<MapNode> nodes = new ArrayList<>();
|
for (JSONObject object : entry.getValue()) {
|
MapNode mapNode = new MapNode();
|
mapNode.setValue(object.getInteger("shelfType"));
|
mapNode.setTop(object.getInteger("top"));
|
mapNode.setBottom(object.getInteger("bottom"));
|
mapNode.setLeft(object.getInteger("left"));
|
mapNode.setRight(object.getInteger("right"));
|
mapNode.setRow(object.getInteger("row"));
|
mapNode.setBay(object.getInteger("bay"));
|
mapNode.setNo(object.getString("row") + "-" + object.getString("bay"));
|
mapNode.setXBase(object.getInteger("refx"));
|
mapNode.setYBase(object.getInteger("refy"));
|
nodes.add(mapNode);
|
}
|
|
Collections.sort(nodes, new Comparator<MapNode>() {
|
@Override
|
public int compare(MapNode o1, MapNode o2) {
|
return Integer.compare(o1.getBay(), o2.getBay());
|
}
|
});
|
|
list.add(nodes);
|
}
|
|
levData.put(lev, list);
|
}
|
|
for (Map.Entry<Integer, ArrayList<ArrayList<MapNode>>> entry : levData.entrySet()) {
|
ArrayList<ArrayList<MapNode>> lists = entry.getValue();//获取地图
|
|
MapNode mapNode = new MapNode();
|
mapNode.setValue(-1);
|
mapNode.setTop(1000);
|
mapNode.setBottom(1000);
|
mapNode.setLeft(1000);
|
mapNode.setRight(1000);
|
mapNode.setRow(0);
|
mapNode.setBay(0);
|
mapNode.setNo("0-0");
|
mapNode.setXBase(0);
|
mapNode.setYBase(0);
|
|
//获取最长row
|
int row = 0;
|
//给每个row首尾增加-1节点
|
for (ArrayList<MapNode> list : lists) {
|
if (list.size() > row) {
|
row = list.size();
|
}
|
|
list.add(0, mapNode.clone());
|
list.add(mapNode.clone());
|
}
|
|
ArrayList<MapNode> headNodes = new ArrayList<>();
|
ArrayList<MapNode> footerNodes = new ArrayList<>();
|
for (int i = 0; i < row+2; i++) {
|
headNodes.add(mapNode.clone());
|
footerNodes.add(mapNode.clone());
|
}
|
|
lists.add(0, headNodes);
|
lists.add(footerNodes);
|
|
Integer lev = entry.getKey();
|
Date now = new Date();
|
RedisMapDto map = new RedisMapDto();
|
map.setData(JSON.toJSONString(lists));
|
map.setCreateTime(now);
|
map.setUpdateTime(now);
|
map.setLev(lev);
|
|
Object data = redisUtil.get(DeviceRedisConstant.MAP + lev);
|
if (data != null) {
|
//将地图数据存入redis
|
redisUtil.set(DeviceRedisConstant.MAP + lev, JSON.toJSONString(map));
|
}
|
}
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
}
|
|
// 解析入库工作档
|
public synchronized void analyzeInBoundTask() {
|
for (Task task : taskService.selectWaitAnalyzeInBoundTask()) {
|
// generate motion list
|
List<Motion> motionList = analyzeService.generateMotion(task);
|
if (motionList.isEmpty()) {
|
continue;
|
}
|
motionService.batchInsert(motionList, task.getUuid(), Integer.valueOf(task.getTaskNo()));
|
|
// 更新工作主档
|
task.setTaskSts(TaskStsType.ANALYZE_INBOUND.sts); // 工作状态
|
task.setUpdateTime(new Date());
|
if (!taskService.updateById(task)) {
|
News.error("更新工作档失败!!! [工作号:{}]", task.getTaskNo());
|
}
|
}
|
}
|
|
/**
|
* 出库 ====>> 同一时间一台穿梭车只能有一个出库任务
|
*/
|
public synchronized void generateOutboundWrkMast() {
|
List<Task> tasks = taskService.selectPakOut();
|
if (tasks.isEmpty()) {
|
return;
|
}
|
|
for (Task task : tasks) {
|
DevpThread devpThread = (DevpThread) SlaveConnection.get(SlaveType.Devp, 1);
|
StaProtocol staProtocol = devpThread.getStation().get(Integer.parseInt(task.getOriginSite()));//源站
|
StaProtocol staProtocol1 = devpThread.getStation().get(Integer.parseInt(task.getDestSite()));//目标站
|
if (staProtocol == null || staProtocol1 == null) {
|
continue;
|
} else {
|
staProtocol = staProtocol.clone();
|
staProtocol1 = staProtocol1.clone();
|
}
|
|
// // 查询站点详细信息
|
// BasDevp staDetl = basDevpService.selectById(outSta.getStaNo());
|
// if (staDetl == null) {
|
// log.error("出库 ===>> 站点在数据库不存在, 站点编号={}", outSta.getStaNo());
|
// continue;
|
// }
|
// 判断堆垛机出库站状态
|
if (staProtocol.isAutoing() && !staProtocol.isLoading() && staProtocol.getWorkNo() == 0 && staProtocol.isOutEnable()) {
|
|
if (!(staProtocol1.isAutoing() && !staProtocol1.isLoading() && staProtocol1.getWorkNo() == 0 && staProtocol1.isOutEnable())) {
|
continue;
|
}
|
|
// //同库位组校验
|
// List<String> outerLoc = Utils.getGroupOuterLoc(wrkMast.getSourceLocNo());
|
// List<LocMast> outerLocMasts = locMastService.selectNotEmptyLocNos(outerLoc);
|
// if (!outerLocMasts.isEmpty()) {
|
// News.info("{}任务,浅库位存在货物,系统等待中", wrkMast.getWrkNo());
|
// continue;//浅库位存在未执行任务
|
// }
|
|
// generate motion list
|
List<Motion> motionList = analyzeService.generateMotion(task);
|
if (Cools.isEmpty(motionList)) {
|
log.error("出库 ===>> 暂时没有空闲小车, 任务号={}", task.getTaskNo());
|
continue;
|
}
|
motionService.batchInsert(motionList, task.getUuid(), Integer.valueOf(task.getTaskNo()));
|
|
// 更新工作主档
|
task.setTaskSts(TaskStsType.ANALYZE_OUTBOUND.sts); // 工作状态
|
task.setUpdateTime(new Date());
|
if (!taskService.updateById(task)) {
|
News.error("更新工作档失败!!! [工作号:{}]", task.getTaskNo());
|
}
|
|
}
|
}
|
}
|
|
/**
|
* 四向穿梭车电量检测 ===>> 发起充电
|
*/
|
public synchronized void loopShuttleCharge() {
|
// 获取充电桩库位类型
|
LocCtg locCtg = locCtgService.getOne(new LambdaQueryWrapper<LocCtg>()
|
.eq(LocCtg::getFlag, "CHARGE")
|
.eq(LocCtg::getStatus, 1));
|
if (locCtg == null) {
|
return;
|
}
|
|
//获取充电任务类型
|
TaskCtg taskCtg = taskCtgService.getOne(new LambdaQueryWrapper<TaskCtg>()
|
.eq(TaskCtg::getFlag, "CHARGE")
|
.eq(TaskCtg::getStatus, 1));
|
if (taskCtg == null) {
|
return;
|
}
|
|
List<Device> list = deviceService.list(new LambdaQueryWrapper<Device>()
|
.eq(Device::getDeviceType, DeviceCtgType.SHUTTLE.val())
|
.eq(Device::getStatus, 1));
|
for (Device device : list) {
|
//获取四向穿梭车线程
|
ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getId().intValue());
|
if (shuttleThread == null) {
|
continue;
|
}
|
|
ShuttleProtocol shuttleProtocol = shuttleThread.getStatus();
|
if (shuttleProtocol == null) {
|
continue;
|
}
|
|
if (!shuttleProtocol.getProtocolStatusType().equals(ShuttleProtocolStatusType.IDLE)) {
|
continue;
|
}
|
|
if (!shuttleThread.isRequireCharge()) {
|
continue;
|
}
|
|
String currentLocNo = shuttleProtocol.getCurrentLocNo();
|
int lev = Utils.getLev(currentLocNo);//获取小车楼层
|
//搜索小车当前楼层充电桩
|
ArrayList<Loc> allChargeLoc = new ArrayList<>();
|
List<Loc> list1 = locService.list(new LambdaQueryWrapper<Loc>()
|
.eq(Loc::getLocCtg, locCtg.getId())
|
.eq(Loc::getStatus, 1)
|
.eq(Loc::getLev, lev));
|
if (!list1.isEmpty()) {
|
allChargeLoc.addAll(list1);
|
}
|
|
//搜索其他楼层充电桩
|
List<Loc> list2 = locService.list(new LambdaQueryWrapper<Loc>()
|
.eq(Loc::getLocCtg, locCtg.getId())
|
.eq(Loc::getStatus, 1)
|
.notIn(Loc::getLev, lev));
|
if (!list2.isEmpty()) {
|
allChargeLoc.addAll(list2);
|
}
|
|
//没有找到充电桩
|
if (allChargeLoc.isEmpty()) {
|
continue;
|
}
|
|
//选择空闲充电桩
|
Loc chargeLoc = null;
|
for (Loc loc : allChargeLoc) {
|
// 判断充电位是否被占用(车辆位置)
|
if (Utils.hasShuttleInLoc(loc.getLocNo(), device.getId())) {
|
continue;
|
}
|
|
// 盘点充电位是否存在任务档
|
List<Task> tasks = taskService.hasChargeInLoc(loc.getLocNo());
|
if (!tasks.isEmpty()) {
|
continue;
|
}
|
|
chargeLoc = loc;
|
break;
|
}
|
|
if (chargeLoc == null) {
|
continue;//未找到充电桩
|
}
|
|
if (motionService.count(new LambdaQueryWrapper<Motion>()
|
.eq(Motion::getDeviceCtg, DeviceCtgType.SHUTTLE.val())
|
.eq(Motion::getDevice, device.getDeviceNo())
|
.eq(Motion::getMotionSts, MotionStsType.EXECUTING.val())) > 0) {
|
continue;
|
}
|
|
//判断当前小车是否满足需要充电要求
|
if (!shuttleThread.isRequireCharge()) {
|
continue;
|
}
|
|
Task taskCharge = taskService.selectChargeWorking(Integer.valueOf(device.getDeviceNo()));
|
if (taskCharge != null) {//已有充电任务
|
continue;
|
}
|
|
String chargeLocNo = chargeLoc.getLocNo();
|
Task task = new Task();
|
task.setUuid(String.valueOf(snowflakeIdWorker.nextId()));
|
task.setTaskNo(String.valueOf(Utils.getTaskNo("CHARGE")));
|
task.setTaskSts(TaskStsType.NEW_CHARGE.sts);
|
task.setTaskCtg(taskCtg.getId());
|
task.setPriority(10);
|
task.setOriginSite(null);
|
task.setOriginLoc(null);
|
task.setDestSite(null);
|
task.setDestLoc(chargeLocNo);
|
task.setIoTime(new Date());
|
task.setStartTime(new Date());
|
task.setHostId(device.getHostId());
|
task.setStatus(1);
|
task.setMemo("charge");
|
task.setShuttleNo(Integer.valueOf(device.getDeviceNo()));
|
|
// generate motion list
|
List<Motion> motionList = analyzeService.generateChargeMotion(task);
|
if (Cools.isEmpty(motionList)) {
|
News.error("保存{}号四向穿梭车充电任务失败!!!", device.getDeviceNo());
|
continue;
|
}
|
motionService.batchInsert(motionList, task.getUuid(), Integer.valueOf(task.getTaskNo()));
|
|
task.setTaskSts(TaskStsType.ANALYZE_CHARGE.sts);
|
if (!taskService.save(task)) {
|
News.error("保存{}号四向穿梭车充电任务失败!!!", device.getDeviceNo());
|
continue;
|
}
|
|
News.info("保存{}号四向穿梭车充电任务成功!!!", device.getDeviceNo());
|
}
|
}
|
|
/**
|
* 四向穿梭车电量检测 ===>> 满电后回到待机位
|
*/
|
public synchronized void loopShuttleToStandbyCauseCharge() {
|
Integer enoughPower = 90;
|
Dict dict = dictService.getOne(new LambdaQueryWrapper<Dict>()
|
.eq(Dict::getFlag, "chargeMaxValue")
|
.eq(Dict::getStatus, 1));
|
if (dict != null) {
|
enoughPower = Integer.parseInt(dict.getValue());
|
}
|
|
//获取迁移任务类型
|
TaskCtg taskCtg = taskCtgService.getOne(new LambdaQueryWrapper<TaskCtg>()
|
.eq(TaskCtg::getFlag, "MOVE")
|
.eq(TaskCtg::getStatus, 1));
|
if (taskCtg == null) {
|
return;
|
}
|
|
List<Device> list = deviceService.list(new LambdaQueryWrapper<Device>()
|
.eq(Device::getDeviceType, DeviceCtgType.SHUTTLE.val())
|
.eq(Device::getStatus, 1));
|
for (Device device : list) {
|
//获取四向穿梭车线程
|
ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getId().intValue());
|
if (shuttleThread == null) {
|
continue;
|
}
|
|
ShuttleProtocol shuttleProtocol = shuttleThread.getStatus();
|
if (shuttleProtocol == null) {
|
continue;
|
}
|
|
if (!shuttleThread.isCharging()) {
|
continue;
|
}
|
|
if (!shuttleThread.isChargingCompleted()) {
|
continue;
|
}
|
|
//查找充电任务
|
Task chargeTask = taskService.getOne(new LambdaQueryWrapper<Task>()
|
.eq(Task::getTaskSts, TaskStsType.CHARGE_WORKING.sts)
|
.eq(Task::getShuttleNo, device.getDeviceNo()));
|
if (chargeTask == null) {
|
continue;
|
}
|
|
//充电完成
|
// 已有迁移任务
|
if (taskService.selectMoveWorking(Integer.valueOf(device.getDeviceNo())) != null) {
|
continue;
|
}
|
|
//获取避让位置
|
String standByLocNo = shuttleDispatcher.searchStandByLocNo(Integer.valueOf(device.getDeviceNo()), device.getHostId(), shuttleThread.getStatus().getCurrentLocNo());
|
|
Task task = new Task();
|
task.setUuid(String.valueOf(snowflakeIdWorker.nextId()));
|
task.setTaskNo(String.valueOf(Utils.getTaskNo("MOVE")));
|
task.setTaskSts(TaskStsType.NEW_MOVE.sts);
|
task.setTaskCtg(taskCtg.getId());
|
task.setPriority(10);
|
task.setOriginSite(null);
|
task.setOriginLoc(null);
|
task.setDestSite(null);
|
task.setDestLoc(standByLocNo); // 避让位置
|
task.setIoTime(new Date());
|
task.setStartTime(new Date());
|
task.setHostId(device.getHostId());
|
task.setStatus(1);
|
task.setMemo("charge");
|
task.setShuttleNo(Integer.valueOf(device.getDeviceNo()));
|
|
// generate motion list
|
List<Motion> motionList = analyzeService.generateShuttleChargeWrkComplete(task);
|
if (Cools.isEmpty(motionList)) {
|
News.error("保存{}号四向穿梭车迁移任务失败!!!", device.getDeviceNo());
|
continue;
|
}
|
motionService.batchInsert(motionList, task.getUuid(), Integer.valueOf(task.getTaskNo()));
|
|
task.setTaskSts(TaskStsType.ANALYZE_MOVE.sts);
|
|
if (!taskService.save(task)) {
|
News.error("保存{}号四向穿梭车迁移任务失败!!!", device.getDeviceNo());
|
continue;
|
}
|
|
chargeTask.setTaskSts(TaskStsType.COMPLETE_CHARGE.sts);
|
chargeTask.setIoTime(new Date());
|
taskService.updateById(chargeTask);
|
}
|
}
|
|
}
|