Commit 37974856 sunke

1 自动绑定逻辑:a)按soseq对需求单进行一次绑定 b)入库时对料盘进行绑定

2  自动出库逻辑: 分盘料,指定料直接出库, 首盘和补料根据建议时间和必须出库时间进行出库(出库到料架上)
3  缺料重发: 首盘和补料缺料时,自动重发到料串上
4  完成任务数比分配任务多的问题修正(机器人正在放料时又进行了扫码)
5  维护料盘厚度与实测厚度不一致时,到机器人扫码位NG的问题(修改为只有在入库时与维护料盘尺寸进行比对,出库时不比对)
6  DN单绑定信息保持(更新不影响已绑定的料串)
7  机器人料架信息保持(更新不影响正在出库的工单)
8  料串信息保持(更新不影响分盘料/指定料/缺料重发)
9  出入库任务保持(更新会记录更新前的任务,更新后继续之前的任务)
10 料盘尺寸与维护尺寸不一致时的提示信息
1 个父辈 ea58a9ac
package com.myproject.bean.update.qisda;
import com.myproject.bean.BaseMongoBean;
import com.myproject.util.StorageConstants;
import org.springframework.data.annotation.Transient;
import java.util.*;
/**
* 缓存信息
*/
public class CacheInfo extends BaseMongoBean {
private String cacheKey = "";
private Object cacheValue = "";
public String getCacheKey() {
return cacheKey;
}
public void setCacheKey(String cacheKey) {
this.cacheKey = cacheKey;
}
public Object getCacheValue() {
return cacheValue;
}
public void setCacheValue(Object cacheValue) {
this.cacheValue = cacheValue;
}
}
package com.myproject.dao.mongo.qisda;
import com.myproject.bean.update.qisda.CacheInfo;
import com.myproject.bean.update.qisda.OutInfo;
import com.myproject.dao.mongo.IMongoDao;
import java.util.List;
public interface ICacheInfoDao extends IMongoDao {
void updateCacheItem(String cacheKey, Object cacheValue);
CacheInfo getCacheInfo(String cacheKey);
}
package com.myproject.dao.mongo.qisda.impl;
import com.myproject.bean.BaseMongoBean;
import com.myproject.bean.update.qisda.CacheInfo;
import com.myproject.bean.update.qisda.OutInfo;
import com.myproject.dao.mongo.AbstractMongoDao;
import com.myproject.dao.mongo.qisda.ICacheInfoDao;
import com.myproject.dao.mongo.qisda.IOutInfoDao;
import com.myproject.util.StorageConstants;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class CacheInfoDaoImpl extends AbstractMongoDao implements ICacheInfoDao {
@Override
public void updateCacheItem(String cacheKey, Object cacheValue){
CacheInfo cacheInfo = getCacheInfo(cacheKey);
if(cacheInfo == null){
cacheInfo = new CacheInfo();
cacheInfo.setCacheKey(cacheKey);
}
cacheInfo.setCacheValue(cacheValue);
save(cacheInfo);
// Update update = Update.update("cacheValue",cacheValue);
// Criteria c = Criteria.where("cacheKey").is(cacheKey);
// Query query = Query.query(c);
// upsert(query,update);
}
@Override
public CacheInfo getCacheInfo(String cacheKey){
return findOneByCondition(new String[]{"cacheKey"}, new String[]{cacheKey});
}
@Override
public Class getEntityClass() {
return CacheInfo.class;
}
}
package com.myproject.webapp.controller.qisda.util;
import com.google.common.collect.Lists;
import com.myproject.bean.qisda.AppendInfo;
import com.myproject.bean.qisda.InquiryShelfBean;
import com.myproject.bean.update.Barcode;
import com.myproject.bean.update.StoragePos;
import com.myproject.bean.update.qisda.OutInfo;
import com.myproject.bean.update.qisda.OutItem;
import com.myproject.dao.mongo.IStoragePosDao;
import com.myproject.dao.mongo.qisda.IOutInfoDao;
import com.myproject.dao.mongo.qisda.IOutItemDao;
import com.myproject.util.DateUtil;
import com.myproject.util.QisdaApi;
import com.myproject.util.StorageConstants;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created by sunke on 2020/3/5.
*/
@Service
public class SoseqCache {
protected final transient Logger log = LogManager.getLogger(getClass());
/**
* key为工单序号,Value为分盘需求单(存放整个工单序号的绑定和发料信息)
*/
private static Map<String,OutInfo> soSeqCacheMap = new ConcurrentHashMap<>();
@Autowired
private IOutInfoDao outInfoDao;
@Autowired
private IOutItemDao outItemDao;
public OutItem getCutOutItem(String soseq, int slotSeq){
OutInfo outInfo = getCutActionInfoFromCache(soseq);
if(outInfo != null){
OutItem cutOutItem = outInfo.findOutItem(slotSeq);
if(cutOutItem == null){
cutOutItem = outItemDao.findItem(outInfo.gethSerial(), slotSeq);
log.warn("未找到outItem,从数据库中加载soseq=" + soseq + " slotSeq=" + slotSeq);
}
return cutOutItem;
}
return null;
}
public void checkRealBindStatus(String soseq){
OutInfo outInfo = getCutActionInfoFromCache(soseq);
if(outInfo != null){
boolean hasBindItem = false;
boolean allItemsBindOk = true;
for (OutItem outItem : outInfo.getOutItems()) {
if(outItem.getRealLockQty() > 0 && !outItem.isCutMaterial()){
hasBindItem = true;
}
if(outItem.realBindLessQty() > 0){
//还未绑定完成
allItemsBindOk = false;
}
}
if(hasBindItem){
//已经执行过真实绑定
int bindStatus = StorageConstants.BIND_STATUS.REAL_BIND_LESS;
if(allItemsBindOk){
//所有的Item都绑定OK
bindStatus = StorageConstants.BIND_STATUS.REAL_BIND_OK;
log.info("工单soseq=["+outInfo.getSoseq()+"]真实绑定OK");
}
outInfoDao.updateStatus(outInfo.gethSerial(),bindStatus, -1);
outInfo.setBindStatus(bindStatus);
soSeqCacheMap.put(outInfo.getSoseq(), outInfo);
}
}
}
public void addToTotalSendQty(OutItem outItem, int sendQty){
OutInfo outInfo = getCutActionInfoFromCache(outItem.getSoseq());
if(outInfo != null){
OutItem cutOutItem = getCutOutItem(outItem.getSoseq(), outItem.getSlotlocation());
if(cutOutItem != null){
if(cutOutItem != null){
int totalSendQty = cutOutItem.getSendQty();
totalSendQty = totalSendQty + sendQty;
int outQty = 0;
if(outItem.isFirstReelAction()){
//首盘
outQty = sendQty;
}
cutOutItem.setOutQty(sendQty);
cutOutItem.setSendQty(totalSendQty);
outItemDao.updateQty(cutOutItem.getId(), outQty, totalSendQty);
outInfo.updateItem(cutOutItem);
soSeqCacheMap.put(outInfo.getSoseq(), outInfo);
}else{
log.error("更新总发料数量时,未找到分盘的 soseq="+outItem.getSoseq() +" slotSeq=" + outItem.getSlotlocation());
}
}
}
}
/**
* 更新总的预绑定数量
*/
public void udpateTotalPreLockQty(OutItem outItem, int totalPreLockQty){
updateTotalLockQty(outItem, totalPreLockQty, -1);
}
/**
* 更新总的真实绑定的数量
*/
public void updateTotalRealLockQty(OutItem outItem, int totalRealLockQty){
updateTotalLockQty(outItem, -1, totalRealLockQty);
}
private void updateTotalLockQty(OutItem outItem, int totalPreLockQty, int totalRealLockQty){
OutInfo outInfo = getCutActionInfoFromCache(outItem.getSoseq());
if(outInfo != null){
OutItem cutOutItem = outInfo.findOutItem(outItem.getSlotlocation());
if(cutOutItem == null){
cutOutItem = outItemDao.findItem(outInfo.gethSerial(), outItem.getSlotlocation());
log.warn("未找到outItem,从数据库中加载soseq=" + outItem.getSoseq() + " slotSeq=" + outItem.getSlotlocation());
}
if(cutOutItem != null){
if(cutOutItem != null){
if(totalPreLockQty > 0){
cutOutItem.setLockQty(totalPreLockQty);
}
if(totalRealLockQty > 0){
cutOutItem.setRealLockQty(totalRealLockQty);
}
outItemDao.updateLockQty(cutOutItem.getId(), totalPreLockQty, totalRealLockQty);
outInfo.updateItem(cutOutItem);
soSeqCacheMap.put(outInfo.getSoseq(), outInfo);
}else{
log.error("更新总绑定数量时,未找到分盘的 soseq="+outItem.getSoseq() +" slotSeq=" + outItem.getSlotlocation());
}
}
}
}
/**
* 将需求单加入到缓存
*/
public void addTotalOutInfo(OutInfo outInfo){
if(outInfo.isReelCutAction()){
log.info("将分盘需求单"+ outInfo +"加入到累计缓存");
soSeqCacheMap.put(outInfo.getSoseq(), outInfo);
}
}
/**
* 从缓存中清除
* @param soseq
*/
public void closeSoseq(String soseq){
soSeqCacheMap.remove(soseq);
}
/**
* 根据soseq从缓存中查找工单累计信息,如果未找到再从数据库中查找
*/
public OutInfo getCutActionInfoFromCache(String soseq){
if(Strings.isBlank(soseq)){
return null;
}
OutInfo outInfo = soSeqCacheMap.get(soseq);
if(outInfo == null){
log.info("未找到工单序号["+soseq+"]的缓存信息,从数据库中加载");
outInfo = outInfoDao.findCutActionOutInfo(soseq);
}
if(outInfo != null){
Map<String, OutItem> itemMap = outInfo.getOutItemMap();
if(itemMap == null){
List<OutItem> outItems = outItemDao.findByHSerial(outInfo.gethSerial());
outInfo.updateOutItems(outItems);
soSeqCacheMap.put(outInfo.getSoseq(), outInfo);
}
}
return outInfo;
}
}
package com.myproject.webapp.controller.webService;
import com.myproject.bean.qisda.AppendInfo;
import com.myproject.bean.update.Barcode;
import com.myproject.bean.update.StoragePos;
import com.myproject.bean.update.qisda.OutInfo;
import com.myproject.bean.update.qisda.OutItem;
import com.myproject.dao.mongo.IBarcodeDao;
import com.myproject.dao.mongo.IStoragePosDao;
import com.myproject.dao.mongo.qisda.IOutInfoDao;
import com.myproject.dao.mongo.qisda.IOutItemDao;
import com.myproject.webapp.controller.qisda.util.SoseqCache;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.*;
/**
* Created by sunke on 2020/4/26.
*/
@Repository
public class QisdaBindService {
protected final transient Logger log = LogManager.getLogger(getClass());
@Autowired
private IOutItemDao outItemDao;
@Autowired
private IStoragePosDao storagePosDao;
@Autowired
private IBarcodeDao barcodeDao;
@Autowired
private SoseqCache soseqCache;
/**
* 真实绑定工单(只对分盘需求单进行绑定)
*/
public void realBindOutInfo(OutInfo outInfo){
//只对分盘需求单进行绑定(分盘料,未关闭,未绑定OK,未发料完成)
if(outInfo.isReelCutAction() && !outInfo.isClosed() && !outInfo.isRealBindOk() && !outInfo.isSendEnd()){
log.info("需求单["+outInfo+"]开始进行真实绑定");
for (OutItem outItem : outInfo.getOutItems()) {
firstBindCutReel(outItem);
secondBindCutReel(outItem);
realBindNoCutReel(outItem);
bindSamePnFromOtherSlotForFirstAction(outItem,new ArrayList<String>());
}
soseqCache.checkRealBindStatus(outInfo.getSoseq());
}
}
/**
* 预绑定分盘料
*/
public void preBindCutOutInfoList(Collection<OutInfo> outInfoList){
for (OutInfo outInfo : outInfoList) {
if(outInfo.isReelCutAction()){
//分盘需求单进行预绑定
log.info("需求单["+outInfo+"]开始进行预绑定");
for (OutItem outItem : outInfo.getOutItems()) {
firstBindCutReel(outItem);
preBindReel(outItem);
}
}else{
log.info("需求单["+outInfo+"]不需要进行预绑定");
}
}
for (OutInfo outInfo : outInfoList) {
if(outInfo.isReelCutAction()){
//首盘和分盘进行缺料反馈
log.info("分盘需求单["+outInfo+"]开始进行二次分盘绑定");
for (OutItem outItem : outInfo.getOutItems()) {
if(outItem.isReelCutAction()){//分盘
secondBindCutReel(outItem);
}
}
}
}
}
/**
* 将绑定信息设置到条码上
*/
public Barcode addBindInfoToBarcode(Barcode barcode, OutItem outItem){
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);
return barcode;
}
/******************************** 以下是 Private 方法*******************************************/
/**
* 预绑定非分盘料,此方法只会在收到分盘需求单只执行一次
*/
private void preBindReel(OutItem outItem){
if(!outItem.isCutMaterial()){
log.info("预绑定非分盘料:So=["+outItem.getSo()+"]hSerial=["+outItem.gethSerial()+"]+refno=["+outItem.getRefno()+"]的slot"+outItem.getSlotlocation()+"]pn=["+outItem.getPn()+"]当前预绑定数量["+outItem.getLockQty()+"/"+outItem.getQty()+"]");
while (true){
StoragePos pos = storagePosDao.findNoBindMinQty(outItem.getPn(), outItem.getFacility());
if(pos == null){
log.info("\t预绑定"+outItem.getSlotlocation() + "的pn=["+outItem.getPn()+"]facility = "+outItem.getFacility()+"时无库存,跳出预绑定");
break;
}else{
int dbQty = pos.getBarcode().getAmount();
int lockQty = outItem.getLockQty();
int newLockQty = lockQty + dbQty;
log.info("\t预绑定["+pos.getBarcode().getBarcode()+"]到So=["+outItem.getSo()+"]hSerial=["+outItem.gethSerial()+"]refno=["+outItem.getRefno()+"]的["+outItem.getSlotlocation()+"] 数量:" + newLockQty +"/" + outItem.getQty());
Barcode barcode = pos.getBarcode();
AppendInfo appendInfo = barcode.getAppendInfo();
appendInfo.sethSerial(outItem.gethSerial());
appendInfo.setRefno(outItem.getRefno());
appendInfo.setSo(outItem.getSo());
appendInfo.setSoseq(outItem.getSoseq());
appendInfo.setPreBindSlot(outItem.getSlotlocation()+"");
barcode.setAppendInfo(appendInfo);
pos.setBarcode(barcode);
storagePosDao.save(pos);
outItem.setLockQty(newLockQty);
outItemDao.updateLockQty(outItem.getId(),newLockQty, 0);
//多出数量
int surplusQty = newLockQty - outItem.getQty();
if(surplusQty >= 0){
//已经满足需求了,剔除同日期的小料盘后直接跳出
//已经预绑定的所有料盘中与最大盘日期相同,且与超出数量最接近的料盘剔除掉
StoragePos needToUnBindPos = storagePosDao.findSurplusPreBindReel(outItem.getSoseq(), outItem.getSlotlocation(), surplusQty, barcode.getProduceDateStr());
if(needToUnBindPos != null){
int unbindAmount = needToUnBindPos.getBarcode().getAmount();
log.info("找到与最大料盘["+barcode.getBarcode()+"]相同日期["+barcode.getProduceDateStr()+"]与超绑数量["+surplusQty+"]接近的料盘["+needToUnBindPos.getBarcode().getBarcode()+"]数量为["+unbindAmount+"]解除预绑定");
storagePosDao.unbindReel(needToUnBindPos);
newLockQty = newLockQty - unbindAmount;
outItem.setLockQty(newLockQty);
outItemDao.updateLockQty(outItem.getId(),newLockQty, 0);
}
break;
}
}
}
}
}
/**
* 此方法只会在收到分盘需求单只执行一次
* 分盘需求第一次绑定: 第一轮挑料:
1.料卷数量等于需求
2.料卷数量,从小到大去挑选,加总数量小于等于需求
*/
private void firstBindCutReel(OutItem outItem){
if(outItem.isCutMaterial()){
log.info("第一轮绑定分盘料:So=["+outItem.getSo()+"]hSerial=["+outItem.gethSerial()+"]的slot"+outItem.getSlotlocation()+"]pn=["+outItem.getPn()+"]当前绑定数量["+outItem.getLockQty()+"/"+outItem.getQty()+"]");
//剩余需求数量
int needNum = outItem.getQty() - outItem.getLockQty();
while(needNum > 0){
//分盘料
StoragePos pos = storagePosDao.findNoBindNoCutMinQty(outItem.getPn(), outItem.getFacility());
if(pos == null){
log.info("\t第一轮绑定分盘料"+outItem.getSlotlocation() + "的pn=["+outItem.getPn()+"]facility = "+outItem.getFacility()+"时无库存,跳出绑定");
break;
}else{
Barcode barcode = pos.getBarcode();
int totalLockQty = outItem.getLockQty() + barcode.getAmount();
if(totalLockQty > outItem.getQty()){
log.info("加总数量["+totalLockQty+"]大于需求数量["+outItem.getQty()+"]跳出第一轮绑定");
break;
}
if(!barcode.hasCutInfo()){
log.info("\t第一轮绑定分盘料["+barcode.getBarcode()+"]绑定到So=["+outItem.getSo()+"]hSerial=["+outItem.gethSerial()+"]refno=["+outItem.getRefno()+"]的["+outItem.getSlotlocation()+"] 数量:" + totalLockQty +"/" + outItem.getQty());
//没有分盘信息,可以直接绑定
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);
pos.setBarcode(barcode);
storagePosDao.save(pos);
int realLockQty = outItem.getRealLockQty() + barcode.getAmount();
outItem.setRealLockQty(realLockQty);
outItem.setLockQty(totalLockQty);
outItemDao.updateLockQty(outItem.getId(),totalLockQty,realLockQty);
needNum = outItem.getQty() - totalLockQty;
}else{
log.error("这句不应该出现,第一轮绑定分盘料时查到分盘信息的物料,分盘料["+barcode.getBarcode()+"]分盘信息:" + barcode.getAppendInfo().getCutMap());
break;
}
}
}
}
}
/**
* 此方法只会在收到分盘需求单只执行一次
* 1.料卷数量等于余量(需分盘的数量)的总数量
2.By SO逐一挑选,料卷数量,从小到大去挑选
* @param outItem
*/
private void secondBindCutReel(OutItem outItem){
if(outItem.isCutMaterial()){
log.info("第二轮绑定分盘料:So=["+outItem.getSo()+"]hSerial=["+outItem.gethSerial()+"]+refno=["+outItem.getRefno()+"]的slot"+outItem.getSlotlocation()+"]pn=["+outItem.getPn()+"]当前预绑定数量["+outItem.getLockQty()+"/"+outItem.getQty()+"]");
//剩余需求数量
int needNum = outItem.getQty() - outItem.getLockQty();
while(needNum > 0){
//分盘料
//TODO: 第二轮应该查找未绑定过或者有分盘信息的物料
StoragePos pos = storagePosDao.findNoBindMinQty(outItem.getPn(), outItem.getFacility());
if(pos == null){
log.info("\t第二轮分盘料绑定"+outItem.getSlotlocation() + "的pn=["+outItem.getPn()+"]facility = "+outItem.getFacility()+"时无库存,跳出绑定");
break;
}else{
Barcode barcode = pos.getBarcode();
int lockQty = outItem.getLockQty();
int remainQty = outItem.getQty() - lockQty;
//还有未绑定的需求
int reelRemainNum = barcode.cutCount(outItem.getSoseq(), outItem.getSo(), outItem.getSlotlocation(), outItem.getSlotStr(), outItem.gethSerial(), remainQty);
if(reelRemainNum > 0){
//母盘还有剩余,说明该需求slot已经满足
lockQty = outItem.getQty();
}else {
//母盘正好用完或全部用完也达不到需求量,此母盘绑定slot后,继续寻找其他盘进行绑定
lockQty = lockQty + barcode.getAmount();
}
log.info("\t分盘料["+barcode.getBarcode()+"]绑定到Soseq=["+outItem.getSoseq()+"]So=["+outItem.getSo()+"]hSerial=["+outItem.gethSerial()+"]refno=["+outItem.getRefno()+"]的["+outItem.getSlotlocation()+"] 数量:" + 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);
int realLockQty = outItem.getRealLockQty() + barcode.getAmount();
outItem.setRealLockQty(realLockQty);
}else{
AppendInfo appendInfo = barcode.getAppendInfo();
appendInfo.setSo(outItem.getSo());
appendInfo.setSoseq(outItem.getSoseq());
barcode.setAppendInfo(appendInfo);
log.info("分盘料["+barcode.getBarcode()+"]分盘信息:" + barcode.getAppendInfo().getCutMap());
}
barcode = barcodeDao.save(barcode);
pos.setBarcode(barcode);
storagePosDao.save(pos);
outItem.setLockQty(lockQty);
outItemDao.updateLockQty(outItem.getId(),outItem.getLockQty(), outItem.getRealLockQty());
needNum = outItem.getQty() - lockQty;
}
}
}
}
/**
* 真实绑定非分盘料,此方法只会在首盘需求单状态变为即将执行时(状态为NEW且到达建议出库时间或者必须出库时间或人工提前执行)执行一次
*/
private void realBindNoCutReel(OutItem outItem){
if(!outItem.isCutMaterial()){
//updateRealLockQty(outItem);
//先从预绑定料盘中进行绑定,如果还有缺料的,从未使用的物料中查找,如果还缺料,从预绑定的物料中查找
log.info("绑定非分盘料:So=["+outItem.getSo()+"]hSerial=["+outItem.gethSerial()+"]+refno=["+outItem.getRefno()+"]的slotLoction"+outItem.getSlotlocation()+"]pn=["+outItem.getPn()+"]当前绑定数量["+outItem.getRealLockQty()+"/"+outItem.getQty()+"]当前出库/发料数量["+outItem.getOutQty()+"/"+outItem.getSendQty()+"]");
List<StoragePos> preBindPosList = storagePosDao.findPreBindList(outItem.getSoseq(), outItem.getSlotlocation());
for (StoragePos pos : preBindPosList) {
outItem = tryRealBindPos(pos, outItem);
}
//所需数量=需求单数量-已发料数量-真实绑定数量
int needNum = outItem.getQty() - outItem.getSendQty() - outItem.getRealLockQty();
log.info("将预绑定转为真实绑定结束,所需数量("+needNum+")=需求单数量("+outItem.getQty()+")-已发料数量("+ outItem.getSendQty()+")-真实绑定数量"+ outItem.getRealLockQty() +")");
if(needNum >= 0){
log.info("预绑定数量不足,查找未绑定料盘进行真实绑定结束,当前数量:"+outItem.getSendQty()+"+"+ outItem.getRealLockQty() +"/" + outItem.getQty());
while(needNum >= 0){
StoragePos pos = storagePosDao.findNoBindMinQty(outItem.getPn(), outItem.getFacility());
OutItem resultOutItem = tryRealBindPos(pos, outItem);
if(resultOutItem == null){
break;
}
outItem = resultOutItem;
if(outItem.getRealLockQty() > outItem.getQty()){
//已经满足需求了,直接跳出
break;
}
}
}
needNum = outItem.getQty() - outItem.getSendQty() - outItem.getRealLockQty();
if(needNum >= 0 ){
log.info("未绑定料盘数量不足,开始抢其他工单的预绑定物料进行真实绑定,当前数量:"+outItem.getSendQty()+"+"+ outItem.getRealLockQty() +"/" + outItem.getQty());
//抢其他工单的预绑定
while(needNum >= 0){
StoragePos pos = storagePosDao.findOtherPreBindMinQty(outItem.getPn(), outItem.getFacility());
OutItem resultOutItem = tryRealBindPos(pos, outItem);
if(resultOutItem == null){
break;
}
outItem = resultOutItem;
if(outItem.getRealLockQty() + outItem.getSendQty() > outItem.getQty()){
//已经满足需求了,直接跳出
break;
}
}
}
//多出数量
int surplusQty = -needNum;
if(surplusQty >= 0){
//已经预绑定的所有料盘中与最大盘日期相同,且与超出数量最接近的料盘剔除掉
StoragePos maxBindReelPos = storagePosDao.findMaxQtyBindReel(outItem.getSoseq(), outItem.getSlotlocation());
if(maxBindReelPos != null){
Barcode maxQtyBindReelBarcode = maxBindReelPos.getBarcode();
StoragePos needToUnBindPos = storagePosDao.findSurplusBindReel(outItem.getSoseq(), outItem.getSlotlocation(), surplusQty, maxQtyBindReelBarcode.getProduceDateStr());
if(needToUnBindPos != null){
int unbindAmount = needToUnBindPos.getBarcode().getAmount();
log.info("找到与最大料盘["+maxQtyBindReelBarcode.getBarcode()+"]相同日期["+maxQtyBindReelBarcode.getProduceDateStr()+"]与超绑数量["+surplusQty+"]接近的料盘["+needToUnBindPos.getBarcode().getBarcode()+"]数量为["+unbindAmount+"]解除绑定");
storagePosDao.unbindReel(needToUnBindPos);
int newLockQty = outItem.getRealLockQty() - unbindAmount;
outItem.setLockQty(newLockQty);
outItem = outItemDao.save(outItem);
}
}
}
//首盘,如果此站位一盘也没有,查找是否有同工单不同站位的PN,如果有,抢一盘过来
log.info("So=["+outItem.getSo()+"]hSerial=["+outItem.gethSerial()+"]+refno=["+outItem.getRefno()+"]的slot"+outItem.getSlotlocation()+"]pn=["+outItem.getPn()+"]真实绑定结束,当前数量:"+outItem.getSendQty()+"+"+ outItem.getRealLockQty() +"/" + outItem.getQty());
}
}
/**
* 尝试真实绑定库位
* @param pos 包含料盘的库位
* @param outItem 出库需求项
* @return 如果库位为null,返回null;否则返回更新后的出库需求项
*/
private OutItem tryRealBindPos(StoragePos pos, OutItem outItem){
if(pos == null){
log.info("\t真实绑定"+outItem.getSlotlocation() + "的pn=["+outItem.getPn()+"]facility = "+outItem.getFacility()+"时无库存,跳出绑定");
return null;
}else{
Barcode barcode = pos.getBarcode();
AppendInfo oldBindAppendInfo = barcode.getAppendInfo();
String oldBindSoSeq = oldBindAppendInfo.getSoseq();
int dbQty = barcode.getAmount();
if(Strings.isNotBlank(oldBindSoSeq)){
//已经有绑定信息
String oldBindSlotStr = oldBindAppendInfo.getBindSlot();
if(Strings.isNotBlank(oldBindSlotStr)){
int oldBindSlot = Integer.valueOf(oldBindSlotStr);
if(oldBindSlot > 0){
log.error("\t真实绑定"+outItem.getSlotlocation() + "的pn=["+outItem.getPn()+"]facility = "+outItem.getFacility()+"时 "+barcode.getBarcode()+" 已被soseq=["+oldBindSoSeq+"]slot=["+oldBindSlot+"]绑定");
return null;
}
}
String oldPreBindSlot = oldBindAppendInfo.getPreBindSlot();
if(Strings.isNotBlank(oldPreBindSlot)){
int oldSlotSeq = Integer.valueOf(oldPreBindSlot);
log.info("预绑定oldsoseq=["+oldBindSoSeq+"]oldpreslot=["+oldPreBindSlot+"]物料["+barcode.getBarcode()+"],更新其预绑定数量");
OutItem oldCutOutItem = soseqCache.getCutOutItem(oldBindSoSeq, oldSlotSeq);
int oldTotalPreBindQty = oldCutOutItem.getLockQty();
oldTotalPreBindQty = oldTotalPreBindQty - dbQty;
soseqCache.udpateTotalPreLockQty(oldCutOutItem, oldTotalPreBindQty);
}
}
int realLockQty = outItem.getRealLockQty();
int newRealLockQty = realLockQty + dbQty;
log.info("\t真实绑定["+barcode.getBarcode()+"]到So=["+outItem.getSo()+"]hSerial=["+outItem.gethSerial()+"]refno=["+outItem.getRefno()+"]的["+outItem.getSlotlocation()+"] 绑定数量:" + outItem.getRealLockQty() +"/" + outItem.getQty());
barcode = addBindInfoToBarcode(barcode, outItem);
pos.setBarcode(barcode);
storagePosDao.save(pos);
outItem.setRealLockQty(newRealLockQty);
outItemDao.updateLockQty(outItem.getId(), 0, newRealLockQty);
soseqCache.updateTotalRealLockQty(outItem, newRealLockQty);
return outItem;
}
}
private OutItem updateRealLockQty(OutItem outItem){
//查找真实绑定
List<StoragePos> bindPosList = storagePosDao.findBindList(outItem.getSoseq(), outItem.getSlotlocation());
int realBindQty = 0;
for (StoragePos bindPos : bindPosList) {
realBindQty = realBindQty + bindPos.getBarcode().getAmount();
}
outItem.setRealLockQty(realBindQty);
int lockQty = outItem.getLockQty();
if(lockQty < realBindQty){
lockQty = realBindQty;
outItem.setLockQty(lockQty);
}
if(outItem.isCutMaterial()){
//分盘料,需要将已绑定未出库的物料也计算进去
List<StoragePos> cutReels = storagePosDao.findCutList(outItem.getSo(), outItem.getSlotlocation(), outItem.getSoseq());
for (StoragePos cutReel : cutReels) {
List<Map<String, Object>> cutItems = cutReel.getBarcode().getCutItems();
for (Map<String, Object> cutItem : cutItems) {
String so = cutItem.get("so").toString();
String soseqStr = cutItem.get("soseq").toString();
String slotlocation = cutItem.get("slotlocation").toString();
if(outItem.getSo().equals(so) && outItem.getSoseq().equals(soseqStr) && outItem.getSlotlocation() == Integer.valueOf(slotlocation)){
log.info("查找到未出库的分盘料信息["+cutItem+"],更新预绑定数量");
String qty = cutItem.get("qty").toString();
lockQty = outItem.getLockQty() + Integer.valueOf(qty);
outItem.setLockQty(lockQty);
}
}
}
}
outItem = outItemDao.save(outItem);
log.info("更新["+outItem.getSlotlocation()+"]"+outItem.getPn()+"真实绑定数量为["+realBindQty+"/"+outItem.getQty()+"]");
return outItem;
}
/**
* 检查首盘料中不同Slot上相同的PN, 如果缺料,出首盘时要保证每一个Slot上都有料
*/
private void bindSamePnFromOtherSlotForFirstAction(OutItem outItem, Collection<String> excludeBarcodeList){
if(outItem.getRealLockQty() == 0){
//绑定数量为0,说明缺料,查找是否有绑定的本工单的其他Slot相同PN的料
List<StoragePos> posList = storagePosDao.findBindByPn(outItem.getSoseq(), outItem.getPn());
if(posList != null && !posList.isEmpty()){
//站位的绑定的料盘数量,大于2盘才可以抢
Map<String,Integer> slotReelCountMap = new HashMap<>();
for (StoragePos storagePos : posList) {
//其他工位绑定至少两盘才可以抢
Barcode barcode = storagePos.getBarcode();
String bindSlot = barcode.getAppendInfo().getBindSlot();
if(bindSlot != null){
Integer reelCount = slotReelCountMap.get(bindSlot);
if(reelCount == null){
reelCount = 0;
}
reelCount = reelCount + 1;
slotReelCountMap.put(bindSlot, reelCount);
}
}
List<String> canRobSlotList = new ArrayList<>();
for (StoragePos storagePos : posList) {
//其他工位绑定至少两盘才可以抢
Barcode barcode = storagePos.getBarcode();
String bindSlot = barcode.getAppendInfo().getBindSlot();
if(bindSlot != null){
Integer reelCount = slotReelCountMap.get(bindSlot);
if(reelCount >= 2){
canRobSlotList.add(bindSlot);
}
}
}
//抢最大的一盘
StoragePos robPos = null;
for (StoragePos storagePos : posList) {
//其他工位绑定至少两盘才可以抢
Barcode barcode = storagePos.getBarcode();
String bindSlot = barcode.getAppendInfo().getBindSlot();
if(bindSlot != null && canRobSlotList.contains(bindSlot)){
if(!excludeBarcodeList.contains(barcode.getBarcode())){
//这盘料可以抢
if(robPos == null || robPos.getBarcode().getAmount() < barcode.getAmount()){
robPos = storagePos;
}
}
}
}
if(robPos != null){
Barcode barcode = robPos.getBarcode();
AppendInfo appendInfo = barcode.getAppendInfo();
String hSerial = outItem.gethSerial();
int reelQty = barcode.getAmount();
String oldSlot = appendInfo.getBindSlot();
log.info("首盘需求单["+hSerial+"]站位["+outItem.getSlotlocation()+"]缺料,从站位["+oldSlot+"]绑定料盘中抢夺料盘"+barcode.getBarcode()+"["+reelQty+"]进行绑定");
OutItem oldItem = outItemDao.findItem(hSerial,Integer.valueOf(oldSlot));
if(oldItem != null){
//不是预绑定的物料
oldItem.setRealLockQty(oldItem.getRealLockQty() - reelQty);
outItemDao.updateLockQty(oldItem.getId(), 0, oldItem.getRealLockQty());
soseqCache.updateTotalRealLockQty(oldItem, oldItem.getRealLockQty());
}
appendInfo.setBindSlot(outItem.getSlotlocation() + "");
barcode.setAppendInfo(appendInfo);
barcodeDao.save(barcode);
robPos.setBarcode(barcode);
storagePosDao.save(robPos);
outItem.setRealLockQty(outItem.getRealLockQty() + reelQty);
outItemDao.updateLockQty(outItem.getId(), 0, outItem.getRealLockQty());
soseqCache.updateTotalRealLockQty(outItem, outItem.getRealLockQty());
}
}
}
}
}
package com.myproject.webapp.controller.webService;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.myproject.bean.CodeBean;
import com.myproject.bean.json.InventoryItem;
import com.myproject.bean.json.PlateSizeBean;
import com.myproject.bean.qisda.InquiryShelfBean;
import com.myproject.bean.qisda.ShelfInfo;
import com.myproject.bean.update.*;
import com.myproject.bean.update.qisda.CacheInfo;
import com.myproject.bean.update.qisda.DNInfo;
import com.myproject.bean.update.qisda.DNItem;
import com.myproject.dao.mongo.qisda.ICacheInfoDao;
import com.myproject.dao.mongo.qisda.IDNItemDao;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* 缓存
* Created by sunke on 17/3/17.
*/
@Repository
public class QisdaCache {
protected static final transient Logger log = LogManager.getLogger(QisdaCache.class);
private static ICacheInfoDao cacheInfoDao;
private static IDNItemDao dNItemDao;
private static String CURRENT_ORDER_HSERIAL_KEY = "currentOrderHSerial";
/**
* RFID绑定的DN单信息Key
*/
private static String RFID_DN_MAP_KEY = "rfidDnMap";
/**
* 料架信息缓存key
*/
private static String HSERIAL_SHELF_MAP_KEY = "hSerialShelfMap";
/**
* 当前正在执行的工单需求单(首盘,补料),未完成时,其他工单需求单不可执行
*/
private static String currentOrderHSerial;
/**
* RFID绑定DN单和Facility, Key为RFID, Value为DN单信息
*/
private static Map<String, DNInfo> rfidDnMap;
/**
* 保存料架缓存信息
* @param hSerialShelfMap
*/
public static void saveShelfMap(Map<String,Map<String,ShelfInfo>> hSerialShelfMap){
cacheInfoDao.updateCacheItem(HSERIAL_SHELF_MAP_KEY, hSerialShelfMap);
}
/**
* 加载料架缓存信息
*/
public static Map<String,Map<String,ShelfInfo>> loadShelfMap(){
Map<String,Map<String,ShelfInfo>> hSerialShelfMap = new ConcurrentHashMap<>();
CacheInfo cacheInfo = cacheInfoDao.getCacheInfo(HSERIAL_SHELF_MAP_KEY);
if(cacheInfo != null){
hSerialShelfMap = (Map<String,Map<String,ShelfInfo>>) cacheInfo.getCacheValue();
}
return hSerialShelfMap;
}
public void initRfidDnMap(){
if(rfidDnMap == null){
CacheInfo cacheInfo = cacheInfoDao.getCacheInfo(RFID_DN_MAP_KEY);
if(cacheInfo == null){
rfidDnMap = new ConcurrentHashMap<>();
}else{
rfidDnMap = (Map<String, DNInfo>) cacheInfo.getCacheValue();
}
}
}
private synchronized static void saveRfidDnMap(){
cacheInfoDao.updateCacheItem(RFID_DN_MAP_KEY,rfidDnMap);
}
/**
* 获取RFID绑定的DN单信息
* @param rfid
* @return
*/
public static DNInfo getDnInfo(String rfid){
if(rfid == null || rfid.equals("000")){
//rfid = "";
return null;
}
DNInfo dnInfo = rfidDnMap.get(rfid);
String usedRfid = "=" + rfid;
if(dnInfo == null){
dnInfo = rfidDnMap.get(usedRfid);
}else{
//已经使用过,清除掉
rfidDnMap.remove(rfid);
rfidDnMap.put(usedRfid, dnInfo);
saveRfidDnMap();
}
if(dnInfo == null){
//如果未找到料架对应的DNInfo,当作纯入库处理
dnInfo = new DNInfo();
dnInfo.setDnNo("");
}else{
if(dnInfo.getItems().isEmpty()){
List<DNItem> items = dNItemDao.findDnItems(dnInfo.getDnNo());
dnInfo.addItems(items);
}
}
return dnInfo;
}
public static void bindRfidDnInfo(String rfid, DNInfo dnInfo){
rfidDnMap.put(rfid, dnInfo);
saveRfidDnMap();
}
/**
* 清理RFID绑定的DN单信息
*/
public static void clearRfidDn(String rfid){
rfidDnMap.remove(rfid);
saveRfidDnMap();
}
/**
* 当前正在执行的工单需求单
* @param executeHSerial
*/
public static void setCurrentOrderHSerial(String executeHSerial) {
if(currentOrderHSerial == null){
currentOrderHSerial = "";
}
String oldHSerial = currentOrderHSerial;
log.info("设置当前正在执行的工单料需求为:" + executeHSerial+"清理之前["+oldHSerial+"]出库使用的料架");
InquiryShelfBean.clearShelf(oldHSerial);
currentOrderHSerial = executeHSerial;
cacheInfoDao.updateCacheItem(CURRENT_ORDER_HSERIAL_KEY, currentOrderHSerial);
}
/**
* 当前正在执行的工单需求单
*/
public static String getCurrentOrderHSerial() {
if(currentOrderHSerial == null){
CacheInfo cacheInfo = cacheInfoDao.getCacheInfo(CURRENT_ORDER_HSERIAL_KEY);
if(cacheInfo == null){
currentOrderHSerial = "";
}else{
currentOrderHSerial = cacheInfo.getCacheValue().toString();
}
}
return currentOrderHSerial;
}
@Autowired
public void setCacheInfoDao(ICacheInfoDao cacheInfoDao) {
QisdaCache.cacheInfoDao = cacheInfoDao;
}
@Autowired
public void setdNItemDao(IDNItemDao dNItemDao) {
QisdaCache.dNItemDao = dNItemDao;
}
}
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!