Commit 07b3795c 张少辉

1.电子仓CTU出库逻辑修改

1 个父辈 2f39a58c
......@@ -153,6 +153,12 @@ public class BarcodeDto implements Serializable {
private String pidBarcode;
//出库增加的参数
private String stockoutNo; //出库单号
private String stockoutNoLine; //出库对应的行数
private String lastStockOutNum; //上一次出库的数量
private int needStockNum;
/**
* 关联条码,夹具时关联相关的物料,用于入库完成时插入相关物料
*/
......
......@@ -544,4 +544,17 @@ public class Barcode extends BasePo implements Serializable {
}
}
}
public Barcode getSubCodeByBarcode(String barcodeStr) {
if (subCodeList == null) {
return null;
}
for (Barcode barcode : subCodeList
) {
if (barcode.getBarcode().equals(barcodeStr)) {
return barcode;
}
}
return null;
}
}
......@@ -451,17 +451,16 @@ public class LiteOrderCache {
/**
* 执行工单出库
*/
public synchronized String checkOutLiteOrder(String orderNo, boolean outBom, boolean singleOut, boolean needCheck, List<String> cidList) {
public synchronized String checkOutLiteOrder(String orderNo, boolean outBom, boolean singleOut, boolean needCheck, List<String> cidList){
//判断工单是否存在
LiteOrder cacheOrder = liteOrderMap.get(orderNo);
if (cacheOrder == null) {
cacheOrder = liteOrderManager.findByOrderNo(orderNo);
}
if (cacheOrder == null) {
return "smfcore.order.out.notFound";
}
//判断工单状态是否可执行
if (!cacheOrder.isTaskFinished() && !cacheOrder.isNew()) {
log.info("工单[" + orderNo + "]正在执行");
return "smfcore.order.out.executing";
......@@ -470,22 +469,17 @@ public class LiteOrderCache {
log.info("工单[" + orderNo + "]已关闭,无法出库");
return "smfcore.order.hasClose";
}
//判断是否达到可执行的最大数量
ORDER_COLOR nextColor = getNextColor();
if (nextColor == null) {
log.info("执行工单[" + orderNo + "] outBom=" + outBom + "时,已达最大可执行工单数");
return "smfcore.order.out.maxOrder";
}
//先查找是否已经锁定过库位,如果已经锁定过,出锁定的库位
List<StoragePos> lockPosList = storagePosManager.findLockPos(cacheOrder.getOrderNo());
if (lockPosList != null && lockPosList.size() > 0) {
return checkOutOrder(cacheOrder).getMsgKey();
}
//缓存料架信息
Map<Integer, ShelfInfo> cacheShelfInfoMap = new HashMap<>();
log.info("开始执行工单[" + orderNo + "] outBom=" + outBom);
cacheOrder.setTaskReelCount(0);
cacheOrder.setTaskFinishedTime(-1);
......@@ -495,34 +489,26 @@ public class LiteOrderCache {
} else {
cacheOrder.setStatus(LITEORDER_STATUS.TAILS);
}
//liteOrderMap.put(cacheOrder.getOrderNo(), cacheOrder);
int taskReelCount = 0;
CHECKOUT_TYPE checkoutType = dataCache.getCheckOutType();
//缺料检查
if (needCheck && (shortageCheck(cacheOrder, outBom, cidList))) {
return "smfcore.order.out.noTask";
}
String type = cacheOrder.getType();
int taskReelCount = 0;
CHECKOUT_TYPE checkoutType = dataCache.getCheckOutType();
List<String> availableStorageIds = dataCache.getAvailableStorageIds(cidList);
boolean shortage = false;
//其他出库模式一次性全部生成任务
List<String> needOutPosId = new ArrayList<>();
//开始循环处理数据
for (LiteOrderItem orderItem : cacheOrder.getOrderItems()) {
//当前已出数量置为空
orderItem.setOutNum(0);
orderItem.setOutReelCount(0);
liteOrderItemManager.save(orderItem);
//剩余未出数量
Float totalNum = orderItem.getNeedNum() * cacheOrder.getOrderTimes();
int remainNum = totalNum.intValue() - orderItem.getTotalOutNum();
//剩余未出盘数
int remainReelCount = orderItem.getNeedReelCount() - orderItem.getTotalOutReelCount();
//此PN未完成
if (remainNum > 0 || remainReelCount > 0) {
if (outBom) {
//套料出库,设置剩余数量为1,这样就只会出一盘
......@@ -532,145 +518,125 @@ public class LiteOrderCache {
int assignNum = 0;
int assignReelCount = 0;
while (assignNum < remainNum || assignReelCount < remainReelCount) {
// 1. 计算当前还需要出库的总剩余量(这是你要出的目标值,比如100)
int remainingToOut = remainNum - assignNum;
// 防护:确保剩余量非负(避免出现负数)
remainingToOut = Math.max(remainingToOut, 0);
Collection<String> excludePosIds = excludeOutPosIds();
String partNumber = orderItem.getPn();
String reelId = orderItem.getRi();
//判断是否ri出库
String ri = orderItem.getRi();
String pn = orderItem.getPn();
String mpn = orderItem.getMpn();
StoragePos pos = null;
if (!Strings.isNullOrEmpty(reelId)) {
//RI
pos = storagePosManager.getByBarcode(reelId);
if (pos == null) {
pos = storagePosManager.getByPidBarcode(reelId);
}
if (pos != null) {
if (excludePosIds.contains(pos.getId())) {
log.info("工单[" + orderNo + "]RI出库,任务数[" + taskReelCount + "]出库位置仓位【" + pos.getPosName() + "】RI=[" + pos.getBarcode().getBarcode() + "]已在操作队列中,跳过不处理");
break;
}
} else {
shortage = true;
log.info("工单[" + orderNo + "]RI出库时,库存中未找到料盘[" + reelId + "]");
}
} else if (Strings.isNullOrEmpty(reelId) && !Strings.isNullOrEmpty(partNumber)) {
//PN
pos = storagePosManager.findPartNumberInStorages(availableStorageIds, "", partNumber, excludePosIds, checkoutType, orderItem.getAppendData());
} else if (Strings.isNullOrEmpty(reelId) && Strings.isNullOrEmpty(partNumber) && !Strings.isNullOrEmpty(mpn)) {
pos = storagePosManager.findMpnInStorages(availableStorageIds, mpn, excludePosIds, checkoutType, orderItem.getAppendData());
}
if (StringUtils.isNotEmpty(ri)) {
StoragePos pos = findPosByRi(ri, excludeOutPosIds(), orderNo, taskReelCount);
if (pos == null) {
// log.error("未找到可以出库的物料[" + partNumber + "]");
shortage = true;
log.info("工单[" + orderNo + "]RI出库时,库存中未找到料盘[" + ri + "]");
break;
} else {
//判断是在料箱,还是在物料中
Barcode barcode = pos.getBarcode();
if (barcode != null) {
List<Barcode> subCodeList = barcode.getSubCodeList();
if (subCodeList != null && !subCodeList.isEmpty()) {
Barcode subCode = barcode.getSubCodeByBarcode(ri);
if (subCode != null) {
log.info(subCode.getBarcode() + "对应的料箱为:" + barcode.getBarcode() + ",需要进行标记,进行出库,工单为:" + orderNo);
subCode = updateSubCode(cacheOrder, orderItem, subCode,subCode.getAmount());
//同时更新barcode和库位信息
barcode.UpdateSubCode(subCode);
barcodeManager.save(subCode);
barcodeManager.save(barcode);
pos.setBarcode(barcode);
storagePosManager.save(pos);
// 2. 获取当前料箱的实际库存数量
int boxStock = pos.getBarcode().getAmount();
// 3. 核心逻辑:needStockNum = 取「剩余需要出库量」和「料箱库存」的较小值
int needStockNum = Math.min(remainingToOut, boxStock);
// 再次防护:确保结果非负(极端情况兜底)
needStockNum = Math.max(needStockNum, 0);
assignNum = assignNum + needStockNum;
needOutPosId.add(pos.getId());
assignReelCount = assignReelCount + 1;
taskReelCount = taskReelCount + 1;
log.info("工单[" + orderNo + "],任务数[" + taskReelCount + "]出库位置仓位【" + pos.getPosName() + "】RI=[" + pos.getBarcode().getBarcode() + "] PN=[" + partNumber + "] num:" + pos.getBarcode().getAmount());
//首套出到皮带线,补料出到对应的料架
String locInfo = "";
if (ZhongCheOrderType.FIRST.equals(type)) {
locInfo = orderNo + "-" + ZhongCheOrderLoc.BELTLINE;
} else if (ZhongCheOrderType.REMAINING.equals(type)) {
//判断是大料盘还是小料盘
Barcode barcode = pos.getBarcode();
boolean bigReel = false;
if (barcode.getPlateSize() > 7) {
bigReel = true;
}
if (cacheShelfInfoMap.isEmpty()) {
cacheShelfInfoMap.put(1, new ShelfInfo());
}
//判断是否有料架信息
int hasCapacityshelf = 0;
for (int shelfNo : cacheShelfInfoMap.keySet()) {
ShelfInfo shelfInfo = cacheShelfInfoMap.get(shelfNo);
if (bigReel) {
if (shelfInfo.hasBigCapacity()) {
hasCapacityshelf = shelfNo;
break;
assignNum = assignNum + subCode.getAmount();
}
} else {
if (shelfInfo.hasSmallCapacity()) {
hasCapacityshelf = shelfNo;
log.info(barcode.getBarcode() + "对应的库位为:" + barcode.getPosName() + ",需要进行标记,进行出库,工单为:" + orderNo);
assignNum = assignNum + pos.getBarcode().getAmount();
assignReelCount = assignReelCount + 1;
taskReelCount = taskReelCount + 1;
generateOrderTask(pos, cacheOrder, orderItem, nextColor, singleOut);
}
}
}
} else if (StringUtils.isEmpty(ri) && StringUtils.isNotEmpty(pn)) {
List<Barcode> barcodeList = findPosByPartNumberAndSort(availableStorageIds, pn, excludeOutPosIds(), checkoutType, orderItem.getAppendData());
if (barcodeList == null || barcodeList.isEmpty()) {
break;
}
for (Barcode barcode : barcodeList) {
//判断库位是否存在
if (assignNum >= remainNum && assignReelCount >= remainReelCount) {
break;
}
if (StringUtils.isEmpty(barcode.getPosName())) {
continue;
}
if (hasCapacityshelf > 0) {
ShelfInfo shelfInfo = cacheShelfInfoMap.get(hasCapacityshelf);
if (bigReel) {
shelfInfo.setBigReelCount(shelfInfo.getBigReelCount() + 1);
if (StringUtils.isEmpty(barcode.getHostBarcodeId())) {
log.info(barcode.getBarcode() + "对应的库位为:" + barcode.getPosName() + ",需要进行标记,进行出库,工单为:" + orderNo);
StoragePos pos = storagePosManager.getByBarcode(barcode.getBarcode());
int needOutNum = remainNum - assignNum;
if (barcode.getAmount() - needOutNum > 0) {
barcode = updateSubCode(cacheOrder, orderItem, barcode, needOutNum);
assignNum = assignNum + needOutNum;
} else {
shelfInfo.setSmallReelCount(shelfInfo.getSmallReelCount() + 1);
barcode = updateSubCode(cacheOrder, orderItem, barcode, barcode.getAmount());
assignNum = assignNum + barcode.getAmount();
}
locInfo = orderNo + "-" + hasCapacityshelf;
cacheShelfInfoMap.put(hasCapacityshelf, shelfInfo);
barcodeManager.save(barcode);
pos.setBarcode(barcode);
storagePosManager.save(pos);
generateOrderTask(pos, cacheOrder, orderItem, nextColor, singleOut);
assignReelCount = assignReelCount + 1;
taskReelCount = taskReelCount + 1;
} else {
//新创建一个料架出来
int maxShelfNo = Collections.max(cacheShelfInfoMap.keySet());
maxShelfNo = maxShelfNo + 1;
ShelfInfo shelfInfo = new ShelfInfo();
if (bigReel) {
shelfInfo.setBigReelCount(shelfInfo.getBigReelCount() + 1);
StoragePos pos = storagePosManager.getByBarcodeId(barcode.getHostBarcodeId());
Barcode posBarcode = pos.getBarcode();
if (posBarcode != null) {
Barcode subCode = posBarcode.getSubCodeByBarcode(barcode.getBarcode());
if (subCode != null) {
log.info(subCode.getBarcode() + "对应的料箱为:" + posBarcode.getBarcode() + ",需要进行标记,进行出库,工单为:" + orderNo);
int needOutNum = remainNum - assignNum;
if (subCode.getAmount() - needOutNum > 0) {
subCode = updateSubCode(cacheOrder, orderItem, subCode, needOutNum);
assignNum = assignNum + needOutNum;
} else {
shelfInfo.setSmallReelCount(shelfInfo.getSmallReelCount() + 1);
subCode = updateSubCode(cacheOrder, orderItem, subCode, subCode.getAmount());
assignNum = assignNum + subCode.getAmount();
}
//同时更新barcode和库位信息
posBarcode.UpdateSubCode(subCode);
barcodeManager.save(subCode);
barcodeManager.save(posBarcode);
pos.setBarcode(posBarcode);
storagePosManager.save(pos);
needOutPosId.add(pos.getId());
assignReelCount = assignReelCount + 1;
taskReelCount = taskReelCount + 1;
}
locInfo = orderNo + "-" + maxShelfNo;
cacheShelfInfoMap.put(maxShelfNo, shelfInfo);
}
}
DataLog task = newTask(pos);
task.setSourceId(cacheOrder.getId());
task.setSourceName(cacheOrder.getOrderNo());
task.setSubSourceId(orderItem.getId());
task.setSubSourceInfo(orderItem.getFeederInfo());
task.setType(OP.CHECKOUT);
task.setLightColor(nextColor.getRgb());
task.setStatus(OP_STATUS.WAIT.name());
task.setSingleOut(singleOut);
task.setLine(cacheOrder.getLine());
task.setLocInfo(locInfo);
task.setSo(cacheOrder.getSo());
task.setSoltNum(orderItem.getSlotNum());
taskService.addTaskToExecute(task);
//修改barcode的信息
Barcode barcode = pos.getBarcode();
barcode.setStockoutNo(orderNo);
barcode.setStockoutNoLine(orderItem.getRowNumber());
barcode.setLastStockOutNum(barcode.getAmount() + "");
barcode.setNeedStockNum(needStockNum);
barcodeManager.saveBarcode(barcode);
pos.setBarcode(barcode);
storagePosManager.save(pos);
}
//如果是RI出库,只有一盘,出完就结束
if (!Strings.isNullOrEmpty(reelId)) {
}
if (assignNum < remainNum || assignReelCount < remainReelCount) {
shortage = true;
break;
}
}
}
}
//判断要出的箱子是否为空
if (needOutPosId != null && !needOutPosId.isEmpty()) {
needOutPosId = needOutPosId.stream().distinct().collect(Collectors.toList());
for (String posId : needOutPosId) {
StoragePos pos = storagePosManager.get(posId);
generateOrderTask(pos, cacheOrder, null, nextColor, singleOut);
taskReelCount = taskReelCount + 1;
}
}
//标记工单是否缺料
if (shortage) {
cacheOrder.addAppendDate("shortage", "true");
} else {
......@@ -680,13 +646,10 @@ public class LiteOrderCache {
cacheOrder.setTotalTaskReelCount(cacheOrder.getTotalTaskReelCount() + taskReelCount);
log.info("工单[" + orderNo + "]任务分配结束,任务数[" + taskReelCount + "]");
smfApi.onOrderStatusChange(cacheOrder);
//有需要出库的
if (taskReelCount <= 0) {
finishedOrderTasks(cacheOrder);
}
liteOrderManager.save(cacheOrder);
liteOrderMap.put(cacheOrder.getOrderNo(), cacheOrder);
if (taskReelCount <= 0) {
//return "工单无可执行的任务";
......@@ -1111,4 +1074,73 @@ public class LiteOrderCache {
return "";
}
public StoragePos findPosByRi(String reelId, Collection<String> excludeOutPosIds, String orderNo, int taskReelCount) {
StoragePos pos = storagePosManager.getByBarcode(reelId);
if (pos == null) {
return null;
}
if (excludeOutPosIds.contains(pos.getId())) {
log.info("工单[" + orderNo + "]RI出库,任务数[" + taskReelCount + "]出库位置仓位【" + pos.getPosName() + "】RI=[" + pos.getBarcode().getBarcode() + "]已在操作队列中,跳过不处理");
return null;
}
return pos;
}
public List<Barcode> findPosByPartNumberAndSort(List<String> availableStorageIds, String partNumber, Collection<String> excludePosIds, CHECKOUT_TYPE checkoutType, Map<String, String> appendDate) {
List<Barcode> barcodeList = new ArrayList<>();
List<StoragePos> posList = storagePosManager.findPosListByPartNumber(availableStorageIds, partNumber, excludePosIds, checkoutType, appendDate);
if (posList == null || posList.isEmpty()) {
return barcodeList;
}
//提取出来所有Barcode信息
for (StoragePos pos : posList) {
Barcode barcode = pos.getBarcode();
if (barcode != null) {
List<Barcode> subCodeList = barcode.getSubCodeList();
if (subCodeList != null && !subCodeList.isEmpty()) {
for (Barcode subCode : subCodeList) {
String stockoutNo = subCode.getStockoutNo();
if (StringUtils.isNotEmpty(stockoutNo)){
continue;
}
if (subCode.isHold()){
continue;
}
if (partNumber.equals(subCode.getPartNumber())) {
barcodeList.add(subCode);
}
}
} else {
barcodeList.add(barcode);
}
}
}
barcodeList = taskService.barcodeListByCheckOutType(checkoutType,barcodeList);
return barcodeList;
}
public void generateOrderTask(StoragePos pos,LiteOrder cacheOrder,LiteOrderItem orderItem,ORDER_COLOR nextColor,boolean singleOut){
DataLog task = newTask(pos);
task.setSourceId(cacheOrder.getId());
task.setSourceName(cacheOrder.getOrderNo());
if (orderItem != null) {
task.setSubSourceId(orderItem.getId());
task.setSubSourceInfo(orderItem.getFeederInfo());
}
task.setType(OP.CHECKOUT);
task.setLightColor(nextColor.getRgb());
task.setStatus(OP_STATUS.WAIT.name());
task.setSingleOut(singleOut);
task.setLine(cacheOrder.getLine());
taskService.addTaskToExecute(task);
}
private Barcode updateSubCode(LiteOrder cacheOrder, LiteOrderItem orderItem, Barcode subCode, int needOutNum) {
//设置barcode的信息
subCode.setStockoutNo(cacheOrder.getOrderNo());
subCode.setStockoutNoLine(orderItem.getRowNumber());
subCode.setLastStockOutNum(subCode.getAmount() + "");
subCode.setNeedStockNum(needOutNum);
return subCode;
}
}
......@@ -108,4 +108,6 @@ public interface IStoragePosManager extends IBaseManager<StoragePos> {
StoragePos dualPosNameGetEmptyPosByStorage(Storage storage, Barcode barcode, Collection<String> operatingPosIds, String lastPosId, String needMovePosName, String endStr, List<String> needExcludePosName);
StoragePos getBySubBarcode(String container);
List<StoragePos> findPosListByPartNumber(List<String> availableStorageIds, String partNumber, Collection<String> excludePosIds, CHECKOUT_TYPE checkoutType, Map<String, String> appendDate);
}
......@@ -517,6 +517,29 @@ public class StoragePosManagerImpl implements IStoragePosManager {
}
@Override
public List<StoragePos> findPosListByPartNumber(List<String> storageIdList, String pn, Collection<String> excludePosIds, CHECKOUT_TYPE checkOutType,Map<String,String> appendData) {
Criteria c = Criteria.where("id").nin(excludePosIds)
.and("enabled").is(true)//可用
.and("barcode.lockId").is(null);//没有被锁定的仓位;
if (storageIdList != null) {
c = c.and("storageId").in(storageIdList);
}
if (appendData != null) {
c = addAppendData(c, appendData);
}
List<Criteria> orCriterialList = Lists.newArrayList();
orCriterialList.add(Criteria.where("barcode.partNumber").is(pn));
orCriterialList.add(Criteria.where("barcode.subCodeList.partNumber").is(pn));
c.orOperator(orCriterialList);
Query q = new Query(c);
Sort sort = getSortByCheckOutType(checkOutType);
q.with(sort);
return storagePosDao.findByQuery(q);
}
@Override
public PageData<StoragePos> findByPage(Query query, Pageable pageable) {
int totalCount = storagePosDao.countByQuery(query);
List<StoragePos> list = storagePosDao.findByQuery(query, pageable);
......
......@@ -35,6 +35,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
* Created by sunke on 2021/7/13.
......@@ -1572,4 +1573,46 @@ public class TaskService {
return dualPosNameFindEmptyPosInStorages(barcode, availbleStorageList, hasOutTaskStorageIds, lastPosId,needMovePosName,endStr);
}
public List<Barcode> barcodeListByCheckOutType(CHECKOUT_TYPE checkoutType, List<Barcode> barcodeList) {
// 处理barcodeList为null的情况
if (barcodeList == null) {
return Collections.emptyList();
}
// 使用Comparator.nullsFirst或nullsLast处理可能的null值
Comparator<Barcode> comparator = null;
if (CHECKOUT_TYPE.EXPIRE_FIRST.equals(checkoutType)) {
// 先过期先出
comparator = Comparator.comparing(Barcode::getExpireDate, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Barcode::getPutInDate, Comparator.nullsFirst(Comparator.naturalOrder()));
} else if (CHECKOUT_TYPE.FIFO.equals(checkoutType)) {
// 严格的先进先出
comparator = Comparator.comparing(Barcode::getPutInDate, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Barcode::getUsedCount);
} else if (CHECKOUT_TYPE.USED_FIRST.equals(checkoutType)) {
// 尾料优先
comparator = Comparator.comparing(Barcode::getAmount, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Barcode::getPutInDate, Comparator.nullsFirst(Comparator.naturalOrder()));
} else if (CHECKOUT_TYPE.PRODUCE_DATE.equals(checkoutType)) {
comparator = Comparator.comparing(Barcode::getProduceDate, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Barcode::getAmount, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Barcode::getPutInDate, Comparator.nullsFirst(Comparator.naturalOrder()));
} else if (CHECKOUT_TYPE.BATCH_FIRST.equals(checkoutType)) {
comparator = Comparator.comparing(Barcode::getBatch, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Barcode::getProduceDate, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Barcode::getAmount, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Barcode::getPutInDate, Comparator.nullsFirst(Comparator.naturalOrder()));
} else {
// 效率优先
comparator = Comparator.comparing(Barcode::getPutInDate, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Barcode::getCreateDate, Comparator.nullsFirst(Comparator.naturalOrder()));
}
return barcodeList.stream()
.filter(Objects::nonNull) // 过滤掉可能的null元素
.sorted(comparator)
.collect(Collectors.toList());
}
}
......@@ -75,7 +75,6 @@ public class AgvElecDeviceController {
if (StringUtils.isNotEmpty(barcode.getPosName())) {
return ResultBean.newErrorResult(-1, "smfcore.materialBox.inPos", "物料已在库位{0}中", new String[]{barcode.getBarcode()});
}
//判断出库任务有没有结束
List<DataLog> dataLogList = taskService.getAllTasks();
for (DataLog dataLog : dataLogList) {
......@@ -223,10 +222,10 @@ public class AgvElecDeviceController {
opTask.setOutFromPos(true);
}
taskService.updateFinishedTask(opTask);
if (!opTask.isNotifyMomo()) {
/*if (!opTask.isNotifyMomo()) {
opTask.setNotifyMomo(true);
taskService.updateFinishedTask(opTask);
}
}*/
if (OP_STATUS.FINISHED.name().equals(statusStr)) {
//已完成,从完成缓存中清除
taskService.removeFinishedTask(opTask);
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!