Commit 2e89c33b sunke

启动时加载最近12个小时未完成的任务

取消料架时,如果最后一个料架已无空位,查找上一个有空位的料架
发料任务结束时,保存上一个需求单使用的料架,用于判断是否是新的料架
包装仓AGV任务被取消时,导致锁定库位无法清理的问题
一个机器人工作时,无法放入大料架12或小料架70,71,72位置的问题
出库优先,入库查找没有出库任务的料仓,如果无空闲料仓,进行等待
1 个父辈 503d4410
......@@ -355,8 +355,8 @@ IP: 10.85.160.181
>>` {"code":0,"msg":"ok","data":""}`
>>
>> - code: 0为正常,其他为异常,
>> - msg:消息,
>> - data:
>> - msg:消息,
>> - data:
......@@ -46,7 +46,7 @@ public class InquiryShelfBean {
if(hSerial != null){
Map<String, ShelfInfo> shelfMap = hSerialShelfMap.get(hSerial);
if(shelfMap != null){
log.info("清理["+hSerial+"]使用的过料架");
log.info("清理["+hSerial+"]使用过的料架");
hSerialShelfMap.remove(hSerial);
QisdaCache.saveShelfMap(hSerialShelfMap);
}
......@@ -619,7 +619,7 @@ public class InquiryShelfBean {
//包装料还需要取消C料架
targetShelfType = StorageConstants.SHEFL_TYPE.C;
}
if(shelfInfo.getShelfType().equals(targetShelfType)){
if(shelfInfo.getShelfType().equals(targetShelfType) && !shelfInfo.isFull()){
if(maxShelf == null || shelfInfo.getRfidIndex() > maxShelf.getRfidIndex()){
maxShelf = shelfInfo;
}
......
......@@ -348,6 +348,16 @@ public class ShelfInfo {
}
}
}
if(shelfLoc == null && StorageConstants.SHEFL_TYPE.isCShelf(shelfType)){
//如果执行到这里还有空,说明另一个机器人停掉了只能分配了
for(int i=usedCount; i> 0; i--){
shelfLoc = lockLocation(i,barcode,reelType);
if(shelfLoc != null){
return shelfLoc;
}
}
}
return shelfLoc;
}else if(robotIndex.equals("2")){
......@@ -372,6 +382,16 @@ public class ShelfInfo {
}
}
}
if(shelfLoc == null && StorageConstants.SHEFL_TYPE.isDShelf(shelfType)){
//如果执行到这里还有空,说明另一个机器人停掉了只能分配了
for(int i=usedCount; i> 0; i--){
shelfLoc = lockLocation(i,barcode,reelType);
if(shelfLoc != null){
return shelfLoc;
}
}
}
return shelfLoc;
}else {
......
......@@ -115,9 +115,9 @@ public class DataLogDaoImpl extends AbstractMongoDao implements IDataLogDao {
//Criteria c = Criteria.where("appendInfo.hSerial").is(executingHSerial).and("lessSendReel").is(false).and("status").nin(StorageConstants.OP_STATUS.FINISHED.name(),StorageConstants.OP_STATUS.CANCEL.name());
Criteria c = Criteria.where("status").nin(StorageConstants.OP_STATUS.FINISHED.name(),StorageConstants.OP_STATUS.CANCEL.name()).and("stopSendToQisda").is(false);
//只查找近5个小时未完成的任务
//只查找近12个小时未完成的任务
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR_OF_DAY,-5);
calendar.add(Calendar.HOUR_OF_DAY,-12);
c.and("createDate").gte(calendar.getTime());
Query query = Query.query(c);
List<DataLog> unFinishedTasks = findByQuery(query);
......
......@@ -9,7 +9,7 @@ public interface IOutItemDao extends IMongoDao {
List<OutItem> findByHSerial(String hSerial);
List<OutItem> findCutItemList(List<String> cutHserialList, String pn, String facility);
List<OutItem> findCutItemList(List<String> soseqList, String pn, String facility);
OutItem findItem(String hSerial, int slotSeq);
......
......@@ -332,8 +332,9 @@ public class OutInfoCache {
* 任务开始时,初始化出库任务数
* @param hSerial
* @param outReelNum
* @param lessSend 是否是缺料补发
*/
public void resetTaskNum(String hSerial, int outReelNum){
public void resetTaskNum(String hSerial, int outReelNum, boolean lessSend){
OutInfo outInfo = getOutInfoFromCache(hSerial);
if(outInfo != null){
outInfo.setOutReelNum(0);
......@@ -440,12 +441,6 @@ public class OutInfoCache {
outInfoMap.put(hSerial, outInfo);
outInfoDao.updateTaskFinishNum(hSerial,taskFinishNum);
updateSendStatus(outInfo.gethSerial());
if(outInfo.isTaskEnd()){
//本次任务已结束,清理料架
InquiryShelfBean.clearShelf(outInfo.gethSerial());
}
return outInfo;
......@@ -476,7 +471,6 @@ public class OutInfoCache {
}
if(outInfo.isTaskEnd()){
outInfo.setTaskEndTime(System.currentTimeMillis());
if(sendEnd){
outInfo.setSendStatus(StorageConstants.SEND_STATUS.SEND_OK);
}else{
......@@ -484,6 +478,21 @@ public class OutInfoCache {
outInfo.setSendStatus(StorageConstants.SEND_STATUS.SEND_LESS);
}
//本次任务已结束,清理料架(首盘和补料使用的C和D料架不清理)
long lastTaskEndTime = outInfo.getTaskEndTime();
if(lastTaskEndTime <= 0){
//outInfo是第一次执行的首盘或补料需求单,更新上次需求单使用过的料架
if(outInfo.isFirstReelAction() || outInfo.isTailAction()){
List<String> usedRfidList = InquiryShelfBean.getUsedRfidList(outInfo.gethSerial());
QisdaCache.updateUsedRfid(usedRfidList);
log.info("更新需求单["+outInfo.gethSerial()+"]使用过的料架:" + String.join("," + usedRfidList));
}
}
InquiryShelfBean.clearShelf(outInfo.gethSerial());
outInfo.setTaskEndTime(System.currentTimeMillis());
outInfoDao.updateTaskEndTime(outInfo.gethSerial(), outInfo.getTaskEndTime());
outInfoDao.updateStatus(outInfo.gethSerial(), -1 , outInfo.getSendStatus());
}
......@@ -689,7 +698,7 @@ public class OutInfoCache {
if(outReelNum > 0){
log.info("需求单"+outInfo.gethSerial()+"已出("+outInfo.getOutReelNum()+")/已发("+outInfo.getTaskFinishNum()+") 本次出库料盘数量:" + outReelNum);
resetTaskNum(hSerial, outReelNum);
resetTaskNum(hSerial, outReelNum,outInfoExecuted);
//补料出库,且不是缺料重发,需要重新排序,按站位序号循环出,每次出最大的盘
if(outInfo.isTailAction() && !outInfoExecuted){
......@@ -713,7 +722,7 @@ public class OutInfoCache {
}
if(outInfo.isReelCutAction() || outInfo.isFirstReelAction()){
if(outReelNum == 0){
if(!outInfoExecuted && outReelNum == 0){
List<OutItem> outItemList = outItemDao.findByHSerial(outInfo.gethSerial());
boolean lessBind = false;
QisdaApi.VMILocationOutFeedback(outItemList, lessBind);
......
......@@ -235,15 +235,6 @@ public class QisdaBindService {
log.info("\t分盘料["+barcode.getBarcode()+"]绑定到So=["+outItem.getSo()+"]hSerial=["+outItem.getSourceName()+"]数量:" + lockQty +"/" + outItem.getQty());
if(!barcode.hasCutInfo()){
//没有分盘信息,可以直接绑定
// AppendInfo appendInfo = barcode.getAppendInfo();
// appendInfo.sethSerial(outItem.gethSerial());
// appendInfo.setRefno(outItem.getRefno());
// appendInfo.setSo(outItem.getSo());
// appendInfo.setSoseq(outItem.getSoseq());
// appendInfo.setSlotStr(outItem.getSlotStr());
// appendInfo.setBindSlot(outItem.getSlotlocation() + "");
// appendInfo.setSlotIndex(outItem.getSlotlocation());
// barcode.setAppendInfo(appendInfo);
barcode.realBindItem(outItem);
int realLockQty = outItem.getRealLockQty() + barcode.getAmount();
outItem.setRealLockQty(realLockQty);
......@@ -442,7 +433,7 @@ public class QisdaBindService {
List<String> lessSoseqList = new ArrayList<>();
List<OutInfo> cacheOutInfoList = outInfoCache.getCachedOutInfos();
for (OutInfo unEndOutInfo : cacheOutInfoList) {
if(!unEndOutInfo.isClosed() && unEndOutInfo.isSendLess()){
if(!unEndOutInfo.isClosed() && unEndOutInfo.isRealBindLess()){
String soseq = unEndOutInfo.getSoseq();
if(!lessSoseqList.contains(soseq)){
lessSoseqList.add(soseq);
......@@ -460,7 +451,7 @@ public class QisdaBindService {
for (OutItem outItem : cutItemList) {
if(!outItem.isBindOk()){
OutInfo outInfo = soseqCache.getCutActionInfoFromCache(outItem.gethSerial());
OutInfo outInfo = soseqCache.getCutActionInfoFromCache(outItem.getSoseq());
if(outInfo != null){
//真实绑定缺料
if(outItem.realBindLessQty() > 0){
......
......@@ -115,6 +115,30 @@ public class QisdaCache {
}
/**
* 根据料架和料架位置获取锁定的库位信息,如果不存在,返回null
* @param rfid
* @param rfidLoc
* @return
*/
public static ReelLockPosInfo getLockPosInfoByRfidLoc(String rfid, String rfidLoc){
if(Strings.isNotBlank(rfid) && Strings.isNotBlank(rfidLoc)){
for (ReelLockPosInfo reelLockPosInfo : reelLocKPosMap.values()) {
String lockRfid = reelLockPosInfo.getRfid();
String lockRfidLoc = reelLockPosInfo.getRfidLoc();
if(Strings.isNotBlank(lockRfid) && Strings.isNotBlank(lockRfidLoc)){
if(rfid.equals(lockRfid) && rfidLoc.equals(rfidLoc)){
return reelLockPosInfo;
}
}
}
}
return null;
}
/**
* 获取料架上料盘锁定的库位信息
* @param rfid
* @return
......@@ -278,12 +302,15 @@ public class QisdaCache {
}
String oldHSerial = currentOrderHSerial;
log.info("设置当前正在执行的工单料需求为:" + executeHSerial+"清理之前["+oldHSerial+"]出库使用的料架");
lastHSerialUsedRfidList = InquiryShelfBean.getUsedRfidList(oldHSerial);
InquiryShelfBean.clearShelf(oldHSerial);
currentOrderHSerial = executeHSerial;
cacheInfoDao.updateCacheItem(CURRENT_ORDER_HSERIAL_KEY, currentOrderHSerial);
}
public static void updateUsedRfid(List<String> usedRfidList){
lastHSerialUsedRfidList = usedRfidList;
}
/**
* 是否是上个工单需求单使用过的料架
*/
......
......@@ -954,6 +954,8 @@ public class QisdaDeviceController extends BaseController {
if(cacheTask != null){
hSerial = cacheTask.getAppendInfo().gethSerial();
}
List<String> usedRfidList = InquiryShelfBean.getUsedRfidList(hSerial);
ShelfInfo shelfInfo = InquiryShelfBean.findSameShelf(hSerial,rfid);
if(shelfInfo != null){
Map<Integer, ShelfLoc> locMap = shelfInfo.getLocMap();
......@@ -981,10 +983,17 @@ public class QisdaDeviceController extends BaseController {
smallEmpty = 100;
bigEmpty = 100;
packageEmpty = 100;
}else{
//为了料架能重用,如果当前执行的需求单料已经放上料架,则清空上一个需求单使用过的料架
if(!usedRfidList.isEmpty()){
log.info("料架["+rfid+"]上一个需求单已使用,当前需求单["+hSerial+"]物料已放上料架,清理上一个需求单使用过的料架");
QisdaCache.updateUsedRfid(null);
}
}
}
List<String> usedRfidList = InquiryShelfBean.getUsedRfidList(hSerial);
//剩余的任务数
Map<String,Object> resultMap = new HashMap<>();
......
......@@ -583,54 +583,53 @@ public class StorageDataController extends BaseController {
try {
Barcode barcode = dataCache.resolveOneValideBarcode(code);
String lockPosId = QisdaCache.getReelLockPosId(barcode.getBarcode());
StoragePos pos = null;
if(!Strings.isNullOrEmpty(lockPosId)){
//已有锁定库位
pos = storagePosManager.get(lockPosId);
if(pos != null ){
if(pos.getW() < barcode.getPlateSize() || pos.getH() < barcode.getHeight()){
log.info("条码["+barcode.getBarcode()+"]尺寸已改变,无法放入已锁定库位["+pos.getPosName()+"],重新查找库位");
pos = null;
}else{
Barcode posBarcode = pos.getBarcode();
if(posBarcode == null){
log.info("条码["+barcode.getBarcode()+"]已锁定库位["+pos.getPosName()+"],返回锁定中的库位");
}else{
log.info("条码["+barcode.getBarcode()+"]已锁定库位["+pos.getPosName()+"]中已有物料["+posBarcode.getBarcode()+"],重新查找库位");
pos = null;
}
}
}
}
if(pos == null){
pos = taskService.findEmptyPosForPutIn(storageList,barcode, rfid);
}
StoragePos pos = taskService.findEmptyPosForPutIn(storageList,barcode, rfid);
if(pos != null){
String result = "0";
//如果料仓有正在执行或者等待执行的任务,客户端先进行等待
Storage storage = dataCache.getStorageById(pos.getStorageId());
if(!storage.isPackage()){
//不是包装仓,如果所在料仓有出库任务,暂停入库
Collection<DataLog> tasks = taskService.getQueueTasks();
for (DataLog task : tasks) {
if(task.isCheckOutTask() && task.getStorageId().equals(pos.getStorageId())){
lineMsg = "库位["+ pos.getPosName() + "]所在料仓有出库任务,暂停入库";
result = "99";
break;
errorMsg = "库位["+ pos.getPosName() + "]所在料仓有出库任务,暂停入库";
lineMsg = errorMsg;
resultMap.put("result","99");
resultMap.put("msg",errorMsg);
return resultMap;
}
}
}
resultMap.put("result",result);
resultMap.put("msg","");
resultMap.put("pos",pos.getPosName());
resultMap.put("barcode",barcode.getBarcode());
Storage theStorage = dataCache.getStorageById(pos.getStorageId());
resultMap.put("cid",theStorage.getCid());
resultMap.put("result","0");
resultMap.put("msg","");
okMsg = "["+rfid+"]["+barcode.getBarcode()+"]锁定库位["+pos.getPosName()+"]";
ReelLockPosInfo oldLockInfo = QisdaCache.getLockPosInfoByRfidLoc(rfid, rfidLoc);
if(oldLockInfo != null){
if(!oldLockInfo.getBarcode().equals(barcode.getBarcode())){
String result = "-1";
okMsg = "["+rfid+"]["+barcode.getBarcode()+"]锁定库位["+pos.getPosName()+"],清理旧有锁定库位";
resultMap.put("result",result);
resultMap.put("msg",okMsg);
//已经锁定过库位,但不是同一个条码,需要把对应位置的锁定信息清理掉
List<ReelLockPosInfo> lockPosInfoList = QisdaCache.getShelfLockPosInfo(rfid);
for (ReelLockPosInfo reelLockPosInfo : lockPosInfoList) {
QisdaCache.removeReelLockPosInfo(reelLockPosInfo.getBarcode());
log.info("清理料架"+reelLockPosInfo.getRfid()+"["+reelLockPosInfo.getRfidLoc()+"]上物料["+reelLockPosInfo.getBarcode()+"]锁定的库位");
}
}
}
log.info(okMsg);
ReelLockPosInfo reelLocInfo = new ReelLockPosInfo();
reelLocInfo.setBarcode(barcode.getBarcode());
reelLocInfo.setCid(theStorage.getCid());
......@@ -639,14 +638,18 @@ public class StorageDataController extends BaseController {
reelLocInfo.setRfid(rfid);
reelLocInfo.setRfidLoc(rfidLoc);
QisdaCache.addReelLockPosInfo(reelLocInfo);
//taskService.addPutInTaskToExecute(theStorage, barcode, pos);
resultMap.put("pos",pos.getPosName());
resultMap.put("barcode",barcode.getBarcode());
resultMap.put("cid",theStorage.getCid());
}else{
resultMap.put("result","104");
errorMsg = "未找到可用的["+barcode.getPlateSize() + "x" + barcode.getHeight()+"]仓位";
errorMsg = "["+barcode.getBarcode()+"]未找到可用的["+barcode.getPlateSize() + "x" + barcode.getHeight()+"]仓位";
resultMap.put("msg",errorMsg);
}
} catch (ValidateException e) {
} catch (Exception e) {
errorMsg = e.getMessage();
log.info("查找空库位失败:" + errorMsg);
resultMap.put("result","105");
......
......@@ -470,6 +470,13 @@ public class TaskService implements ITaskService {
public StoragePos findEmptyPosForPutIn(List<Storage> storageList, Barcode barcode, String inRFID) throws ValidateException{
verifyBarcodePutIn(storageList ,barcode, inRFID);
//查找任务数最少的料仓
Map<String,Integer> storageTaskCountMap = new HashMap<>();
for (Storage storage : storageList) {
storageTaskCountMap.put(storage.getId(), 0);
}
Set<String> hasOutTaskStorageIds = new HashSet<>();
//如果有正在执行的任务,把库位发过去
Collection<DataLog> allTasks = taskMap.values();
for (DataLog task : allTasks) {
......@@ -478,32 +485,48 @@ public class TaskService implements ITaskService {
log.info(barcode.getBarcode() + " 已有任务,返回任务中的库位:" + task.getPosName());
return storagePosManager.get(posId);
}
}
//查找任务数最少的料仓
Map<String,Integer> executingTaskCountMap = new HashMap<>();
for (Storage storage : storageList) {
executingTaskCountMap.put(storage.getId(), 0);
}
Set<String> hasOutTaskStorageIds = new HashSet<>();
for (DataLog task : allTasks) {
String storageId = task.getStorageId();
if(!Strings.isNullOrEmpty(storageId)){
Integer taskCount = executingTaskCountMap.get(storageId);
Integer taskCount = storageTaskCountMap.get(storageId);
if(taskCount != null){
if(task.isExecuting()){
taskCount = taskCount + 1;
executingTaskCountMap.put(storageId, taskCount);
}
storageTaskCountMap.put(storageId, taskCount);
}
if(task.isCheckOutTask() && task.isExecuting()){
if(task.isCheckOutTask()){
hasOutTaskStorageIds.add(storageId);
}
}
}
String lockPosId = QisdaCache.getReelLockPosId(barcode.getBarcode());
StoragePos pos = null;
if(!Strings.isNullOrEmpty(lockPosId)){
//已有锁定库位
pos = storagePosManager.get(lockPosId);
if(pos != null ){
if(pos.getW() < barcode.getPlateSize() || pos.getH() < barcode.getHeight()){
log.info("条码["+barcode.getBarcode()+"]尺寸已改变,无法放入已锁定库位["+pos.getPosName()+"],重新查找库位");
pos = null;
}else{
Barcode posBarcode = pos.getBarcode();
if(posBarcode == null){
log.info("条码["+barcode.getBarcode()+"]已锁定库位["+pos.getPosName()+"],返回锁定中的库位");
}else{
log.info("条码["+barcode.getBarcode()+"]已锁定库位["+pos.getPosName()+"]中已有物料["+posBarcode.getBarcode()+"],重新查找库位");
pos = null;
}
}
}
}
if(pos != null){
return pos;
}
//可用的料仓(在线,且可以放入)
List<Storage> availbleStorageList = new ArrayList<>();
......@@ -512,14 +535,14 @@ public class TaskService implements ITaskService {
if(status.timeOut()){
continue;
}
if(!status.isBoxCanPutIn()){
//料仓不可入库
continue;
}
if(hasOutTaskStorageIds.contains(storage.getId())){
//有正在执行的出库任务
continue;
}
// if(!status.isBoxCanPutIn()){
// //料仓不可入库
// continue;
// }
// if(hasOutTaskStorageIds.contains(storage.getId())){
// //有出库任务
// continue;
// }
if(storage.canPutIn(barcode.getPlateSize(),barcode.getHeight())){
availbleStorageList.add(storage);
......@@ -530,49 +553,30 @@ public class TaskService implements ITaskService {
throw new ValidateException("料仓列表中未找到可用的料仓");
}
return findEmptyPosInStorages(barcode, availbleStorageList, executingTaskCountMap);
return findEmptyPosInStorages(barcode, availbleStorageList, storageTaskCountMap);
}
private synchronized StoragePos findEmptyPosInStorages(Barcode barcode, List<Storage> availbleStorageList, Map<String,Integer> executingTaskCountMap){
//优先从没有出库任务的料仓中查找库位
for (Storage storage : availbleStorageList) {
Integer executingTaskCount = executingTaskCountMap.get(storage.getId());
if(executingTaskCount >= 1){
continue;
}
try{
log.info("尝试从无出库任务的料仓["+storage.getCid()+"]中为["+barcode.getBarcode()+"]查找空位,当前料仓正在执行任务数:" + executingTaskCount);
return findLineEmptyPosForPutIn(storage,barcode);
}catch(Exception e){
log.info("尝试从["+storage.getCid()+"]中为["+barcode.getBarcode()+"]查找空位失败:" + e.getMessage());
}
private synchronized StoragePos findEmptyPosInStorages(Barcode barcode, List<Storage> availbleStorageList, final Map<String,Integer> executingTaskCountMap){
availbleStorageList.sort(new Comparator<Storage>() {
@Override
public int compare(Storage o1, Storage o2) {
Integer taskCount1 = executingTaskCountMap.get(o1.getId());
Integer taskCount2 = executingTaskCountMap.get(o2.getId());
return taskCount1.compareTo(taskCount2);
}
});
Collection<String> operatingPosIds = excludePosIds();
for (Storage storage : availbleStorageList) {
Integer executingTaskCount = executingTaskCountMap.get(storage.getId());
if(executingTaskCount >= 2){
continue;
}
try{
log.info("尝试从["+storage.getCid()+"]为["+barcode.getBarcode()+"]查找空位,当前料仓正在执行任务数:" + executingTaskCount);
return findLineEmptyPosForPutIn(storage,barcode);
}catch(Exception e){
log.info("尝试从["+storage.getCid()+"]中为["+barcode.getBarcode()+"]查找空位失败:" + e.getMessage());
}
log.info("尝试从["+storage.getCid()+"]中为["+barcode.getBarcode()+"]查找空位");
StoragePos pos = storagePosManager.getEmptyPosByStorage(storage, barcode , operatingPosIds);
if(pos != null){
return pos;
}
//只有一个料仓可以入时,只能继续往里面放
log.info("可用料仓太少,不按任务数分配,重新查找...");
for (Storage storage : availbleStorageList) {
try{
log.info("不按任务数分配,尝试从["+storage.getCid()+"]中为["+barcode.getBarcode()+"]查找空位");
return findLineEmptyPosForPutIn(storage,barcode);
}catch(Exception e){
log.info("尝试从["+storage.getCid()+"]中为["+barcode.getBarcode()+"]查找空位失败:" + e.getMessage());
}
}
return null;
}
/**
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!