自动化立体仓库 - WMS系统
pang.jiabao
2024-10-12 906378a251838ab4d4f3b7f7ecc461764ea7090f
src/main/java/com/zy/asrs/task/handler/GhjtHandler.java
@@ -4,25 +4,26 @@
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.core.common.Cools;
import com.core.exception.CoolException;
import com.zy.asrs.entity.WrkDetl;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.mapper.OrderDetlMapper;
import com.zy.asrs.mapper.OrderMapper;
import com.zy.asrs.mapper.WrkMastMapper;
import com.zy.asrs.entity.*;
import com.zy.asrs.mapper.*;
import com.zy.asrs.service.ApiLogService;
import com.zy.asrs.service.WorkService;
import com.zy.asrs.service.WrkDetlService;
import com.zy.asrs.utils.Utils;
import com.zy.common.constant.MesConstant;
import com.zy.common.model.StartupDto;
import com.zy.common.properties.SlaveProperties;
import com.zy.common.service.CommonService;
import com.zy.common.utils.HttpHandler;
import com.zy.system.entity.Config;
import com.zy.system.mapper.ConfigMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
@@ -32,6 +33,7 @@
 */
@Slf4j
@Service
@Transactional
public class GhjtHandler {
    @Resource
@@ -49,7 +51,27 @@
    @Resource
    private OrderDetlMapper orderDetlMapper;
    @Transactional
    @Resource
    private LocDetlMapper locDetlMapper;
    @Resource
    private LocMastMapper locMastMapper;
    @Resource
    private WorkService workService;
    @Resource
    private ConfigMapper configMapper;
    @Autowired
    private SlaveProperties slaveProperties;
    @Resource
    private CommonService commonService;
    // 堆垛机对应一楼出库站点
    private static final int[] oneFloorOutSite = new int[]{0,3002,3003,3006,3008,3009,3012};
    public void startCkrwPushGwcs(WrkMast wrkMast) {
        // 获取请求头
@@ -85,6 +107,9 @@
                } else if (wrkMast.getIoType() == 3) {
                    // 修改工作主档状态
                    wrkMast.setWrkSts(15L);
                    wrkMast.setModiTime(new Date());
                } else if(wrkMast.getIoType() == 12) { // 跨巷道转移
                    wrkMast.setWrkSts(1L); // 状态改为1.生成入库id
                    wrkMast.setModiTime(new Date());
                }
                wrkMastMapper.updateById(wrkMast);
@@ -149,13 +174,380 @@
        } else if (wrkMast.getIoType() == 103 && (wrkMast.getStaNo() == 3077 || wrkMast.getStaNo() == 3106)) { // 两箱出一箱,需桁架理货
            // 都要先去理货
            flag = 2;
//            if (wrkDetls.size() == 1) { // 一卷贴标出库
//                flag = 4;
//            } else { // 多卷直接出库
//
//            }
        }
        return flag;
    }
    /**
     * 自动备货处理
     */
    public void autoStockUpHandler(List<String> list,int columnNum) {
        // 根据包装组号获取所在库位
        List<LocDetl> locDetls = locDetlMapper.selectLocNoByGroupNo(list);
        if (locDetls.isEmpty()) {
            return;
        }
        // 相同则合并,一个库位两个包装组号
        Map<String,List<String>> map = new HashMap<>();
        for(LocDetl locDetl : locDetls) {
            List<String> brand = map.get(locDetl.getLocNo());
            if (brand == null) {
                map.put(locDetl.getLocNo(),new ArrayList<String>(){{add(locDetl.getBrand());}});
            } else {
                brand.add(locDetl.getBrand());
                map.put(locDetl.getLocNo(),brand);
            }
        }
        // 遍历堆垛机并且判断是否存在任务
        for (int i = 1; i <= 6; i++) {
            // 要备货的库位
            String sourceLocNo = null;
            // 备货目标库位
            String staLocNo = null;
            Integer wrkCount = wrkMastMapper.selectCount(new EntityWrapper<WrkMast>().eq("crn_no", i));
            if(wrkCount > 0) {
                log.warn("{}号堆垛机已存在任务",i);
                continue;
            }
            // 根据堆垛机号查询到对应的深库位和浅库位  深库位4*n-3和4*n 浅库位4*n-2和4*n-1
            int s1 = 4*i-3;
            int s2 = 4*i;
            int q1 = 4 * i - 2;
            int q2 = 4 * i -1;
            // 根据堆垛机号获取一个浅库位
            for(String key : map.keySet()) {
                int row = Integer.parseInt(key.substring(0, 2));
                if (row == q1 || row == q2) {
                    sourceLocNo = key;
                    break;
                }
            }
            // 浅库位没有则找一个深库位
            if (sourceLocNo == null) {
                for(String key : map.keySet()) {
                    int row = Integer.parseInt(key.substring(0, 2));
                    if (row == s1 || row == s2) {
                        sourceLocNo = key;
                        break;
                    }
                }
            }
            // 没有找到源库位
            if (sourceLocNo == null) {
                log.warn("没有找到源库位,堆垛机:{}",i);
                continue;
            }
            // 寻找一个备货的目标库位,先深后浅
            List<LocMast> locMasts1 = locMastMapper.selectList(new EntityWrapper<LocMast>().eq("loc_sts", "O").eq("crn_no", i).in("row1", s1, s2)
                    .le("bay1", columnNum));
            if (locMasts1.isEmpty()) {
                // 深库位为空了,取浅库位
                List<LocMast> locMasts2 = locMastMapper.selectList(new EntityWrapper<LocMast>().eq("loc_sts", "O").eq("crn_no", i).in("row1", q1, q2)
                        .le("bay1", columnNum));
                if (!locMasts2.isEmpty()) {
                    staLocNo = locMasts2.get(0).getLocNo();
                }
            } else {
                staLocNo = locMasts1.get(0).getLocNo();
            }
            if(staLocNo == null) {
                log.warn("{}号堆垛机备货区满了",i);
                continue;
            }
            // 备货的源库位,目标库位都获取到了,生成移库任务
            workService.locMove(sourceLocNo,staLocNo,29L);
            // 订单明细改成备货中,在任务完成时候改成备货完成,并判断整个订单是否完成
            orderDetlMapper.updateOrderDetlStatusByPackageNo(map.get(sourceLocNo), 1);
            // 移除已生成备货库位
            map.remove(sourceLocNo);
        }
    }
    public void autoMoveLoc(List<OrderDetl> orderDetlList) {
        // 判断是否已经有执行的移库任务
        Integer count = wrkMastMapper.selectCount(new EntityWrapper<WrkMast>().in("io_type", 11, 12));
        if (count > 0) {
            return;
        }
        OrderDetl detl = null; // 要移库的明细
        String staLoc = null; // 移库目标库位
        // 从待移库位列表中先浅后深获取一个待移库位
        Optional<OrderDetl> any = orderDetlList.stream().filter(orderDetl -> Utils.isShallowLoc(slaveProperties,orderDetl.getSpecs())).findAny();
        if (any.isPresent()){
            detl = any.get();
        }
        // 剩下的应该都是深库位,获取第一个
        if (detl == null) {
            detl = orderDetlList.get(0);
            // 对应浅库位有货,在堆垛机出库的时候会检测到,在那里生成移库任务
        }
        // 获取备货区配置
        Config config = configMapper.selectConfigByCode("auto_stock_up");
        if (config == null) {
            return;
        }
        // 备货取是前几列
        int bay1 = Integer.parseInt(config.getValue());
        // 指定的目标库位
        String model = detl.getModel();
        // 指定目标库位
        if (!Cools.isEmpty(model)) {
            // 目标库位是深库位
            if (Utils.isDeepLoc(slaveProperties,model)) {
                // 目标库位
                LocMast locMast2 = locMastMapper.selectById(model);
                if (locMast2 == null || !locMast2.getLocSts().equals("O")) {
                    log.error("指定的目标库位【{}】状不为空", model);
                    return;
                }
                // 获取到对应浅库位
                String shallowLoc = Utils.getShallowLoc(slaveProperties, model);
                LocMast locMast = locMastMapper.selectById(shallowLoc);
                // 浅库位有货
                if (locMast.getLocSts().equals("F") || locMast.getLocSts().equals("D")) {
                    log.error("选择的深库位【{}】,但是浅库位【{}】有货", model, locMast.getLocNo());
                    return;
                }
                // 避开备货区
                if (Utils.getBay(model) <= bay1) {
                    log.error("指定的目标库位【{}】在备货区,不能转移", model);
                    return;
                }
            }
            // 深库位检测通过,或者是浅库位
            staLoc = model;
        } else if ( detl.getBeBatch() != null) { // 指定目标巷道
            // 目标巷道
            Integer beBatch = detl.getBeBatch();
            // 从巷道里面先深后浅取一个空库位,不要占用备货区
            List<LocMast> locMasts = locMastMapper.selectList(new EntityWrapper<LocMast>().eq("crn_no", beBatch).eq("loc_sts", "O").gt("bay1", bay1));
            if (locMasts.isEmpty()) {
                log.error("指定巷道【{}】没有能移库的空库位了",beBatch);
                return;
            }
            // 先过滤出深库位
            Optional<LocMast> a = locMasts.stream().filter(locMast -> Utils.isDeepLoc(slaveProperties,locMast.getLocNo())).findAny();
            if (a.isPresent()){
                staLoc = a.get().getLocNo();
                // 获取到对应浅库位
                String shallowLoc = Utils.getShallowLoc(slaveProperties, staLoc);
                LocMast locMast = locMastMapper.selectById(shallowLoc);
                // 浅库位有货
                if (locMast.getLocSts().equals("F") || locMast.getLocSts().equals("D")) {
                    log.error("指定的目标巷道深库位【{}】,但是浅库位【{}】有货",staLoc,locMast.getLocNo());
                    return;
                }
            }
            // 深库位没有则取浅库位
            if (staLoc == null) {
                staLoc = locMasts.get(0).getLocNo();
            }
        } else {
            log.error("目标库位或目标巷道有误:{}",detl);
            return;
        }
        // 生成跨巷道移库任务
        workService.autoLocMove(detl.getOrderNo(),detl.getSpecs(),staLoc,29L);
        // 更新单据明细移库状态为移库中
        detl.setDanger(1);
        if (detl.getBeBatch() != null) {
            detl.setModel(staLoc); // 补充目标库位
        }
        orderDetlMapper.updateById(detl);
        // 更新单据状态为2处理中
        orderMapper.updatePendingSettleByOrderNo(detl.getOrderNo(),2L);
    }
    /**
     * 给指定桁架生成理货任务,按规则寻找到能理货的两个库位
     * 1.能理货的都是一箱一卷的
     * 2.规则:木箱型号相同-管芯类型相同-实测宽幅相同-生箔厚度相同-分切下料时间相近
     */
    public void autoTallyGoods(int flag) {
        // 获取备货区配置
        Config config = configMapper.selectConfigByCode("auto_stock_up");
        if (config == null) {
            throw new CoolException("理货获取备货区配置错误!!!");
        }
        // 前几列是备货区
        int columnNum = Integer.parseInt(config.getValue());
        // 寻找一箱一卷的,没有理货的,不在备货区的物料明细
        List<LocDetl> tallyGoosList = locDetlMapper.selectTallyGoosList(columnNum);
        // 寻找满足理货条件的两个木箱
        LocDetl leftLocDetl = null;
        LocDetl rightLocDetl = null;
        for (int i = 0; i < tallyGoosList.size(); i++) {
            leftLocDetl = tallyGoosList.get(i);
            for (int j = i + 1; j < tallyGoosList.size(); j++) {
                LocDetl tempLocDetl = tallyGoosList.get(j);
                if (leftLocDetl.getColor().equals(tempLocDetl.getColor()) && leftLocDetl.getManu().equals(tempLocDetl.getManu()) &&
                        leftLocDetl.getSku().equals(tempLocDetl.getSku()) && leftLocDetl.getItemNum().equals(tempLocDetl.getItemNum())) {
                    rightLocDetl = tempLocDetl;
                    break;
                }
            }
            if (rightLocDetl != null) {
                break;
            }
        }
        if (leftLocDetl == null || rightLocDetl == null) {
//            log.warn("没有找到两个能理货的木箱");
            return;
        }
        // 找到的两个木箱刚好在一个托盘上,直接更改理货状态为2
        if (leftLocDetl.getLocNo().equals(rightLocDetl.getLocNo())) {
            locDetlMapper.updateLhStsByLocNo(leftLocDetl.getLocNo(), 2);
        } else {
            // 判断是去哪套桁架的哪个站点 t0组盘点,t1左换盘点,t2右换盘点
            int t0 = 3046, t1 = 3045, t2 = 3044;
            if (flag == 2) {
                t0 = 3042;
                t1 = 3041;
                t2 = 3040;
            }
            // 生成空闲理货任务
            WrkDetl wrkDetl1 = tallyGoodsGenerate(t1, leftLocDetl);
            WrkDetl wrkDetl2 = tallyGoodsGenerate(t2, rightLocDetl);
            Date now = new Date();
            // 寻找一个空库位
            StartupDto dto = commonService.getLocNo(1, t0, null, 0);
            // 生成组盘入库任务
            WrkMast wrkMast = new WrkMast();
            wrkMast.setWrkNo(dto.getWorkNo());
            wrkMast.setIoTime(now);
            wrkMast.setWrkSts(1L); // 工作状态
            wrkMast.setIoType(1); // 入出库类型
            wrkMast.setIoPri(13D); // 优先级:13
            wrkMast.setCrnNo(dto.getCrnNo());
            wrkMast.setSourceStaNo(dto.getSourceStaNo()); // 源站
            wrkMast.setStaNo(dto.getStaNo()); // 目标站
            wrkMast.setSourceLocNo(""); // 源库位
            wrkMast.setLocNo(dto.getLocNo());
            wrkMast.setFullPlt("Y"); // 满板:Y
            wrkMast.setPicking("N"); // 拣料
            wrkMast.setExitMk("N"); // 退出
            wrkMast.setEmptyMk("N"); // 空板
            wrkMast.setLinkMis("N");
            wrkMast.setBarcode(""); // zwcs执行入库时更新托盘码
            wrkMast.setAppeUser(29L); // 操作人员 root
            wrkMast.setAppeTime(now);
            wrkMast.setModiUser(29L);
            wrkMast.setModiTime(now);
            if (wrkMastMapper.insert(wrkMast) != 1) {
                throw new CoolException("空闲理货组盘保存工作档失败,详情:" + wrkMast);
            }
            // 生成工作明细
            wrkDetl1.setWrkNo(wrkMast.getWrkNo());
            wrkDetl1.setOrigin("左"); // 固定拆到左边
            wrkDetl1.setDeadWarn(2); // 已理货标识
            wrkDetl2.setWrkNo(wrkMast.getWrkNo());
            wrkDetl2.setOrigin("右");
            wrkDetl2.setDeadWarn(2);
            wrkDetlService.insert(wrkDetl1); // 入库完成时更新托盘码
            wrkDetlService.insert(wrkDetl2);
            // 更新目标库位状态
            LocMast locMast = locMastMapper.selectById(dto.getLocNo());
            locMast.setLocSts("S");
            locMastMapper.updateById(locMast); // 入库完成时更新托盘码
        }
    }
    /**
     * 生成空闲理货到换盘桁架的任务
     *
     * @param site    换盘点
     * @param locDetl 库存明细
     */
    private WrkDetl tallyGoodsGenerate(int site, LocDetl locDetl) {
        // 判断库位状态
        LocMast locMast = locMastMapper.selectById(locDetl.getLocNo());
        if (!locMast.getLocSts().equals("F")) {
            throw new CoolException("理货库位状态有误,不为F,库位号:" + locMast.getLocNo());
        }
        Date now = new Date();
        // 生成工作档
        WrkMast wrkMast = new WrkMast();
        // 获取工作号
        int workNo = commonService.getWorkNo(5);
        wrkMast.setWrkNo(workNo);
        wrkMast.setIoTime(now);
        wrkMast.setWrkSts(11L); // 工作状态:11.生成出库ID
        wrkMast.setIoType(109); // 入出库状态 109.空闲理货
        wrkMast.setIoPri(13D); // 优先级:13
        wrkMast.setCrnNo(locMast.getCrnNo());
        wrkMast.setSourceStaNo(oneFloorOutSite[locMast.getCrnNo()]); // 源站
        wrkMast.setStaNo(site); // 目标站
        wrkMast.setSourceLocNo(locDetl.getLocNo()); // 源库位
        wrkMast.setFullPlt("Y"); // 满板:Y
        wrkMast.setPicking("N"); // 拣料
        wrkMast.setExitMk("N"); // 退出
        wrkMast.setEmptyMk("N"); // 空板
        wrkMast.setLinkMis("N");
        wrkMast.setSheetNo("0");
        wrkMast.setBarcode(locDetl.getZpallet());
        wrkMast.setAppeUser(29L); // 操作人员 root
        wrkMast.setAppeTime(now);
        wrkMast.setModiUser(29L);
        wrkMast.setModiTime(now);
        if (wrkMastMapper.insert(wrkMast) != 1) {
            throw new CoolException("空闲理货保存工作档失败,详情:" + wrkMast);
        }
        // 生成工作明细
        WrkDetl wrkDetl = new WrkDetl();
        wrkDetl.sync(locDetl);
        wrkDetl.setWrkNo(workNo);
        wrkDetl.setIoTime(now);
        wrkDetl.setAppeTime(now);
        wrkDetl.setAppeUser(29L);
        wrkDetl.setModiTime(now);
        wrkDetl.setModiUser(29L);
        if (!wrkDetlService.insert(wrkDetl)) {
            throw new CoolException("空闲理货保存工作档明细失败,详情:" + wrkDetl);
        }
        // 修改出库状态
        locMast.setLocSts("R");
        locMast.setModiUser(29L);
        locMast.setModiTime(now);
        if (locMastMapper.updateById(locMast) != 1) {
            throw new CoolException("空闲理货预约库位状态失败,库位号:" + locMast.getLocNo());
        }
        // 更新库存明细为理货中
        locDetlMapper.updateLhStsByLocNo(locDetl.getLocNo(), 1);
        return wrkDetl;
    }
}