自动化立体仓库 - WMS系统
pang.jiabao
2024-10-12 906378a251838ab4d4f3b7f7ecc461764ea7090f
空闲理货
11个文件已修改
1个文件已添加
628 ■■■■ 已修改文件
src/main/java/com/zy/asrs/controller/OpenController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/LocDetl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/param/KxlhcdwcParam.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/result/ZphjcdgzVo.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/LocDetlMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/OpenService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java 273 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/WorkServiceImpl.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/GhjtScheduler.java 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/handler/GhjtHandler.java 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/handler/WorkMastHandler.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/LocDetlMapper.xml 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/OpenController.java
@@ -172,6 +172,16 @@
        return openService.requestXhd((String) param.get("barcode"));
    }
    @PostMapping("/kxlhcdwc")
    @AppAuth(memo = "空闲理货拆垛完成")
    public synchronized R kxlhCdwc(@RequestHeader(required = false) String appkey,
                                   @RequestBody KxlhcdwcParam param,
                                   HttpServletRequest request) {
        auth(appkey, param, request);
        return openService.kxlhCdwc(param);
    }
    /*@PostMapping("/order/matSync/default/v1")
    @AppAuth(memo = "商品信息同步接口")
    public synchronized R syncMatInfo(@RequestHeader(required = false) String appkey,
src/main/java/com/zy/asrs/entity/LocDetl.java
@@ -128,7 +128,7 @@
    @TableField("dead_time")
    private String deadTime;
    @ApiModelProperty(value= "预警天数")
    @ApiModelProperty(value= "理货标识 0未理货,1理货中,2理货完成")
    @TableField("dead_warn")
    private Integer deadWarn;
src/main/java/com/zy/asrs/entity/param/KxlhcdwcParam.java
New file
@@ -0,0 +1,21 @@
package com.zy.asrs.entity.param;
import lombok.Data;
/**
 * @author pang.jiabao
 * @description 空闲理货拆垛完成
 * @createDate 2024/7/16 9:37
 */
@Data
public class KxlhcdwcParam {
    private Integer workNo; // 工作号
    // 换盘站点
    private Integer sourceStaNo;
    // 组盘站点
    private Integer site;
}
src/main/java/com/zy/asrs/entity/result/ZphjcdgzVo.java
@@ -17,4 +17,9 @@
    private String position; // 要拆的位置,左或右
    /**
     * 拆垛类型 1.出库理货,2.空闲理货
     */
    private Integer cdType;
}
src/main/java/com/zy/asrs/mapper/LocDetlMapper.java
@@ -91,4 +91,18 @@
     * 按包装组号列表查询库位号列表
     */
    List<LocDetl> selectLocNoByGroupNo(@Param("packageGroupNos") List<String> packageGroupNos);
    /**
     * 查找不在备货区,没有理货的,不是一箱多卷的物料集合(只会是一托一箱一卷或一托两箱都一卷),按分切时间排序,确保最先匹配的日期最接近
     * @param columnNum 备货区前几列
     * @return 库存明细
     */
    List<LocDetl> selectTallyGoosList(@Param("columnNum") int columnNum);
    /**
     * 按库位号更新库存明细理货状态
     * @param locNo 库位号
     * @param status 状态
     */
    void updateLhStsByLocNo(@Param("locNo") String locNo,@Param("status") int status);
}
src/main/java/com/zy/asrs/service/OpenService.java
@@ -76,6 +76,11 @@
    R zphjCdwc(ZphjcdwcParam param);
    /**
     * 空闲理货拆垛完成
     */
    R kxlhCdwc(KxlhcdwcParam param);
    /**
     * gwcs到达盘点位置传递托盘条码获取亮灯
     */
    R requestXhd(String barcode);
src/main/java/com/zy/asrs/service/impl/OpenServiceImpl.java
@@ -2,6 +2,7 @@
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.core.common.Cools;
import com.core.common.DateUtils;
import com.core.common.R;
@@ -26,6 +27,8 @@
import com.zy.common.service.CommonService;
import com.zy.common.utils.HttpHandler;
import com.zy.common.utils.NodeUtils;
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;
@@ -100,6 +103,9 @@
    @Resource
    private LocDetlMapper locDetlMapper;
    @Resource
    private ConfigMapper configMapper;
    @Override
    @Transactional
@@ -1232,10 +1238,6 @@
//        StartupDto dto = commonService.getLocNo(1, 1, param.getPalletizingNo(), null,null,null, locTypeDto);
        StartupDto dto = commonService.getLocNo(iotype, param.getPalletizingNo(), locTypeDto,0);
        // 返回GWCS目标信息
        pushStaNoToGwcs(param.getPalletizingNo(),dto.getStaNo(),dto.getWorkNo(),param.getBarcode(),"gwms申请入库后推送gwcs");
        int workNo = dto.getWorkNo();
        Date now = new Date();
        // 生成工作档
@@ -1323,7 +1325,11 @@
        } else {
            throw new CoolException(dto.getLocNo()+"目标库位已被占用");
        }
        return null;
        // 返回GWCS目标信息
        pushStaNoToGwcs(param.getPalletizingNo(),dto.getStaNo(),dto.getWorkNo(),param.getBarcode(),"gwms申请入库后推送gwcs");
        return R.ok();
    }
    @Override
@@ -1343,6 +1349,8 @@
            wrkMast.setBarcode(param.getBarcode());
        } else if(wrkMast.getIoType() == 12) { // 跨巷道转移入库
        } else if(wrkMast.getSourceStaNo() == 3046 || wrkMast.getSourceStaNo() == 3042) {
            wrkMast.setBarcode(param.getBarcode());
        }
        wrkMastService.updateById(wrkMast);
@@ -1566,7 +1574,7 @@
        if (wrkMast == null) {
            throw new CoolException("工作档不存在!");
        }
        if (wrkMast.getWrkSts() != 2 || wrkMast.getIoType() != 103) {
        if (wrkMast.getWrkSts() != 2 || wrkMast.getIoType() != 103 && wrkMast.getIoType() != 109) {
            throw new CoolException("工作档当前状态不为2/出库类型不为拣料!");
        }
        // 更新工作档状态2.设备上走->42.等待码垛
@@ -1582,6 +1590,7 @@
        zphjcdgzVo.setWorkNo(wrkDetl.getWrkNo());
        zphjcdgzVo.setBoxType(wrkDetl.getColor());
        zphjcdgzVo.setPosition(wrkDetl.getOrigin());
        zphjcdgzVo.setCdType(wrkMast.getIoType() == 103 ? 1 : 2);
        return R.ok(zphjcdgzVo);
    }
@@ -1590,7 +1599,7 @@
    @Transactional
    public R zphjCdwc(ZphjcdwcParam param) {
        if (Cools.isEmpty(param.getWorkNo(),param.getBarcode(),param.getSourceStaNo(),param.getSite())) {
        if (Cools.isEmpty(param.getWorkNo(),param.getSourceStaNo(),param.getSite())) {
            return R.parse("参数不能为空,请检查入参");
        }
        WrkMast wrkMast = wrkMastService.selectById(param.getWorkNo());
@@ -1604,74 +1613,228 @@
        // 获取出库物料
        List<WrkDetl> wrkDetls = wrkDetlService.selectList(new EntityWrapper<WrkDetl>().eq("wrk_no", wrkMast.getWrkNo()));
        // 源库位
        LocMast locMast = locMastService.selectById(wrkMast.getSourceLocNo());
        // 创建桁架理货出库的工作档
        int work = create111Work(wrkMast, wrkDetls, param.getSite(), wrkMast.getStaNo(), param.getBarcode());
        int work = create111Work(wrkMast, wrkDetls, param.getSite(), wrkMast.getStaNo(), "1001");
        int descFlag = getType(wrkDetls);
        // get0原托盘回流信息,get1拆垛出来物料出库信息
        List<ZphjcdwcVo> zphjcdwcVos = new ArrayList<>();
        ZphjcdwcVo zphjcdwcVo1 = new ZphjcdwcVo();
        zphjcdwcVo1.setWorkNo(wrkMast.getWrkNo());
        zphjcdwcVo1.setStaNo(wrkMast.getSourceStaNo());
        zphjcdwcVo1.setSourceStaNo(param.getSourceStaNo());
        zphjcdwcVo1.setBarcode(wrkMast.getBarcode());
        zphjcdwcVos.add(zphjcdwcVo1);
        ZphjcdwcVo zphjcdwcVo2 = new ZphjcdwcVo();
        zphjcdwcVo2.setWorkNo(work);
        zphjcdwcVo2.setSourceStaNo(param.getSite());
        zphjcdwcVo2.setStaNo(wrkMast.getStaNo());
        zphjcdwcVo2.setBarcode(param.getBarcode());
        zphjcdwcVo2.setDescFlag(descFlag);
        zphjcdwcVos.add(zphjcdwcVo2);
        // 保存工作主档历史档
        if (!wrkMastLogService.save(wrkMast.getWrkNo())) {
            throw new CoolException("保存工作主档历史档失败");
        }
        // 获取目标站
//        Wrapper<StaDesc> wrapper = new EntityWrapper<StaDesc>()
//                .eq("type_no", wrkMast.getIoType() - 50)
//                .eq("stn_no", wrkMast.getSourceStaNo()) // 作业站点 = 拣料出库的目标站
//                .eq("crn_no", wrkMast.getCrnNo()); // 堆垛机号
//        StaDesc staDesc = staDescService.selectOne(wrapper);
//        if (Cools.isEmpty(staDesc)) {
//            throw new CoolException("入库路径不存在");
//        }
        // 堆垛机站点(目标站)
//        Integer staNo = staDesc.getCrnStn();
        // 更新工作类型103->53
        wrkMast.setIoType(53);
        wrkMast.setWrkSts(52L); // 工作状态42->52.设备上走(拆垛完成)
        // 目标站点源站点转换
        wrkMast.setStaNo(oneFloorIn[wrkMast.getCrnNo()]);
        wrkMast.setSourceStaNo(param.getSourceStaNo());
        // 目标库位=源库位
        wrkMast.setLocNo(wrkMast.getSourceLocNo());
        // 源库位清除
        wrkMast.setSourceLocNo("");
        wrkMast.setModiTime(new Date());
        // 获取备货区配置
        Config config = configMapper.selectConfigByCode("auto_stock_up");
        if (config == null) {
            throw new CoolException("理货获取备货区配置错误!!!");
        }
        // 前几列是备货区
        int columnNum = Integer.parseInt(config.getValue());
        // get0原托盘回流信息,get1拆垛出来物料出库信息
        List<ZphjcdwcVo> zphjcdwcVos = new ArrayList<>();
        ZphjcdwcVo zphjcdwcVo2 = new ZphjcdwcVo();
        zphjcdwcVo2.setWorkNo(work);
        zphjcdwcVo2.setSourceStaNo(param.getSite());
        zphjcdwcVo2.setStaNo(wrkMast.getStaNo());
        zphjcdwcVo2.setBarcode(param.getSite() == 3046 ? "T1146" : "T1142");
        zphjcdwcVo2.setDescFlag(descFlag);
        zphjcdwcVos.add(zphjcdwcVo2);
        // 如果从备货区来的,则不能回原库位
        if (locMast.getBay1() <= columnNum) {
            // 寻找一个新库位
            LocTypeDto locTypeDto = new LocTypeDto();
            locTypeDto.setLocType1((short) 1);
            StartupDto dto = commonService.getLocNo(53, param.getSourceStaNo(), locTypeDto, 0);
            // 新库位
            LocMast staLocMast = locMastService.selectById(dto.getLocNo());
            // 更新旧库位状态 O
            locMast.setLocSts("O");
            locMast.setBarcode("");
            locMast.setModiTime(new Date());
            locMastService.updateById(locMast);
            // 把旧库位明细转到新库位
            locDetlService.updateLocNo(staLocMast.getLocNo(), wrkMast.getSourceLocNo());
            // 更新新库位状态 Q
            staLocMast.setLocSts("Q");
            staLocMast.setBarcode(wrkMast.getBarcode());
            staLocMast.setModiTime(new Date());
            locMastService.updateById(staLocMast);
            // 更新工作工作主档
            wrkMast.setIoType(53);
            wrkMast.setWrkSts(52L); // 工作状态42->52.设备上走(拆垛完成)
            // 目标站点源站点转换
            wrkMast.setStaNo(dto.getStaNo());
            wrkMast.setSourceStaNo(dto.getSourceStaNo());
            wrkMast.setCrnNo(dto.getCrnNo());
            // 目标库位=源库位
            wrkMast.setLocNo(dto.getLocNo());
            // 源库位清除
            wrkMast.setSourceLocNo("");
            wrkMast.setModiTime(new Date());
        } else {
            // 更新工作工作主档
            wrkMast.setIoType(53);
            wrkMast.setWrkSts(52L); // 工作状态42->52.设备上走(拆垛完成)
            wrkMast.setSourceStaNo(param.getSourceStaNo());
            wrkMast.setStaNo(oneFloorIn[wrkMast.getCrnNo()]);
            // 目标库位=源库位
            wrkMast.setLocNo(wrkMast.getSourceLocNo());
            // 源库位清除
            wrkMast.setSourceLocNo("");
            wrkMast.setModiTime(new Date());
        }
        // 更新工作主档
        wrkMastService.updateById(wrkMast);
        // 修改库位状态 Q.拣料/盘点/并板再入库
        LocMast locMast = locMastService.selectById(wrkMast.getLocNo());
        locMast.setLocSts("Q");
        locMast.setModiTime(new Date());
        if (!locMastService.updateById(locMast)) {
            throw new CoolException("修改库位状态失败");
        }
        // 回库
        ZphjcdwcVo zphjcdwcVo1 = new ZphjcdwcVo();
        zphjcdwcVo1.setWorkNo(wrkMast.getWrkNo());
        zphjcdwcVo1.setStaNo(wrkMast.getStaNo());
        zphjcdwcVo1.setSourceStaNo(wrkMast.getSourceStaNo());
        zphjcdwcVo1.setBarcode(wrkMast.getBarcode());
        zphjcdwcVos.add(0,zphjcdwcVo1);
        // 下发回库的目标站点和拆垛的出库目标站点给gwcs
        return R.ok(zphjcdwcVos);
    }
    @Override
    public R kxlhCdwc(KxlhcdwcParam param) {
        if (Cools.isEmpty(param.getWorkNo(),param.getSourceStaNo(),param.getSite())) {
            return R.parse("参数不能为空,请检查入参");
        }
        WrkMast wrkMast = wrkMastService.selectById(param.getWorkNo());
        if (wrkMast == null) {
            throw new CoolException("工作档不存在!");
        }
        if (wrkMast.getWrkSts() != 42 || wrkMast.getIoType() != 109) {
            throw new CoolException("工作档当前状态不为42/出库类型不为空闲理货!");
        }
        Date now = new Date();
        // 获取出库物料
        List<WrkDetl> wrkDetls = wrkDetlService.selectList(new EntityWrapper<WrkDetl>().eq("wrk_no", wrkMast.getWrkNo()));
        // 获取库存物料
        List<LocDetl> locDetls = locDetlMapper.selectList(new EntityWrapper<LocDetl>().eq("loc_no", wrkMast.getSourceLocNo()));
        // 源库位
        LocMast locMast = locMastService.selectById(wrkMast.getSourceLocNo());
        List<ZphjcdwcVo> zphjcdwcVos = new ArrayList<>();
        if (wrkDetls.size() == locDetls.size()) { // 不需要回库,拆完后剩空托盘去叠盘机
            // 删除源库位库存明细
            locDetlMapper.delete(new EntityWrapper<LocDetl>().eq("loc_no", wrkMast.getSourceLocNo()));
            // 修改源库位状态
            if (locMast.getLocSts().equals("R")) {
                locMast.setLocSts("O");
                locMast.setBarcode("");
                locMast.setSheetNo("0");
                locMast.setModiTime(now);
                locMast.setIoTime(now);
                locMastService.updateById(locMast);
            }
            // 修改工作主档状态
            wrkMast.setWrkSts(15L);
            wrkMast.setModiTime(now);
            wrkMastService.updateById(wrkMast);
            // 下发去叠盘位置
            ZphjcdwcVo zphjcdwcVo = new ZphjcdwcVo();
            zphjcdwcVo.setWorkNo(wrkMast.getWrkNo());
            zphjcdwcVo.setStaNo(3013);
            zphjcdwcVo.setSourceStaNo(param.getSourceStaNo());
            zphjcdwcVo.setBarcode(wrkMast.getBarcode());
            zphjcdwcVos.add(zphjcdwcVo);
        } else { // 拆完后还有物料,需要回库
            // 先删除库存明细,避免还未回库但组盘的先入库了
            for (WrkDetl wrkDetl : wrkDetls) {
                locDetlMapper.delete(new EntityWrapper<LocDetl>().eq("brand",wrkDetl.getBrand()));
            }
            // 修改工作主档
            wrkMast.setWrkSts(52L);
            wrkMast.setIoType(59);
            wrkMast.setSourceStaNo(wrkMast.getStaNo());
            wrkMast.setStaNo(oneFloorIn[wrkMast.getCrnNo()]);
            wrkMast.setLocNo(wrkMast.getSourceLocNo());
            wrkMast.setSourceLocNo("");
            wrkMast.setModiTime(now);
            wrkMastService.updateById(wrkMast);
            // 修改源库位状态
            if (locMast.getLocSts().equals("R")) {
                locMast.setLocSts("S");
                locMast.setModiTime(now);
                locMastService.updateById(locMast);
            }
            // 下发回库命令
            ZphjcdwcVo zphjcdwcVo = new ZphjcdwcVo();
            zphjcdwcVo.setWorkNo(wrkMast.getWrkNo());
            zphjcdwcVo.setStaNo(oneFloorIn[wrkMast.getCrnNo()]);
            zphjcdwcVo.setSourceStaNo(param.getSourceStaNo());
            zphjcdwcVo.setBarcode(wrkMast.getBarcode());
            zphjcdwcVos.add(zphjcdwcVo);
        }
        // 组盘点
        Integer site = param.getSite();
        // 判断组盘是否完成:两个到换盘站点的任务是否完成
        Wrapper<WrkMast> wrapper = new EntityWrapper<WrkMast>().eq("io_type", 109).ne("wrk_sts",15);
        if (site == 3046) {
            wrapper.in("sta_no",3045,3044);
        } else {
            wrapper.in("sta_no",3041,3040);
        }
        int count = wrkMastService.selectCount(wrapper);
        // 已经完成则下发组盘点入库命令
        if (count == 0) {
            // 获取到组盘点入库任务
            WrkMast wrkMast1 = wrkMastService.selectOne(new EntityWrapper<WrkMast>().eq("wrk_sts", 1).eq("io_type", 1).eq("source_sta_no", site));
            ZphjcdwcVo zphjcdwcVo = new ZphjcdwcVo();
            zphjcdwcVo.setWorkNo(wrkMast1.getWrkNo());
            zphjcdwcVo.setStaNo(wrkMast1.getStaNo());
            zphjcdwcVo.setSourceStaNo(site);
            zphjcdwcVo.setBarcode(site == 3046 ? "T1046" : "T1042");
            zphjcdwcVos.add(zphjcdwcVo);
        }
        return R.ok(zphjcdwcVos);
    }
    @Override
    public R requestXhd(String barcode) {
        // 没有任务或者不是盘点任务的托盘经过则返回-1,忽略
        WrkMast wrkMast = wrkMastService.selectOne(new EntityWrapper<WrkMast>().eq("barcode", barcode));
        if (wrkMast == null || wrkMast.getIoType() != 107) {
            return R.ok(-1);
        }
        // 库存明细木箱位置集合
        List<LocDetl> locDetls = locDetlMapper.selectList(new EntityWrapper<LocDetl>().eq("zpallet", barcode));
        List<String> collect1 = locDetls.stream().map(LocDetl::getOrigin).distinct().collect(Collectors.toList());
src/main/java/com/zy/asrs/service/impl/WorkServiceImpl.java
@@ -854,12 +854,6 @@
        if (Cools.isEmpty(loc)){
            throw new CoolException("未找到库位");
        }
        if (!loc.getLocSts().equals("O") || (!sourceLoc.getLocSts().equals("F") && !sourceLoc.getLocSts().equals("D"))){
            throw new CoolException("库位状态已改变");
        }
//        if (!sourceLoc.getCrnNo().equals(loc.getCrnNo())) {
//            throw new CoolException("移转库位属于不同堆垛机");
//        }
        Date now = new Date();
        // 获取工作号
@@ -1180,6 +1174,12 @@
            }
        }
//        // 取消空闲理货任务时,回滚成未理货
//        if (wrkMast.getIoType() == 109) {
//            // 更新库存明细为理货中
//            locDetlMapper.updateLhStsByLocNo(wrkMast.getSourceLocNo(),0);
//        }
        //取消出库工作档时,查询单据管理表,回滚作业中数量
        if(wrkMast.getIoType() == 101 || wrkMast.getIoType() == 103 || wrkMast.getIoType() == 107) {
            List<WrkDetl> wrkDetls = wrkDetlService.selectByWrkNo(wrkMast.getWrkNo());
src/main/java/com/zy/asrs/task/GhjtScheduler.java
@@ -44,7 +44,7 @@
    // 定时任务获取待备货订单明细->获取堆垛机对应的源库位,获取备货区库位->生成移库任务11->
    // 执行移库任务12->入库完成4->更新工作档定时任务中更新订单备货状态和订单明细备货状态5->转储历史
    @Scheduled(cron = "0/10 * * * * ?")
    public synchronized void autoStockUp() {
    public void autoStockUp() {
        // 查询自动备货配置
        Config config = configMapper.selectConfigByCode("auto_stock_up");
        if (config == null || config.getStatus() == 0) {
@@ -64,7 +64,7 @@
    @Scheduled(cron = "0/2 * * * * ? ")
    public void ckrwPushGwcs() {
        // 查询状态为13的工作档
        List<WrkMast> wrkMasts = wrkMastMapper.selectList(new EntityWrapper<WrkMast>().in("io_type", 101,103,107,110,3,12).eq("wrk_sts", 13));
        List<WrkMast> wrkMasts = wrkMastMapper.selectList(new EntityWrapper<WrkMast>().in("io_type", 101,103,107,110,3,12,109).eq("wrk_sts", 13));
        for (WrkMast wrkMast : wrkMasts) {
            try {
                ghjtHandler.startCkrwPushGwcs(wrkMast);
@@ -79,7 +79,7 @@
    // wcs出库到堆垛机出库口->gwms给gwcs推送目标站1->gwcs到达堆垛机入库口请求入库->堆垛机执行入库->入库完成->更新单据状态
    // 11->12->13->1->2->3->4->5
    @Scheduled(cron = "0/10 * * * * ?")
    public synchronized void autoMoveLoc() {
    public void autoMoveLoc() {
        // 查询跨巷道移库配置
        Config config = configMapper.selectConfigByCode("auto_move_loc");
        if (config == null || config.getStatus() == 0) {
@@ -95,21 +95,41 @@
    }
    // 空闲理货
    @Scheduled(cron = "0/5 * * * * ? ")
    // 配置开启->查询可用桁架->获取到能理货的两个木箱->生成两个木箱的理货任务和组盘点的入库任务
    // wcs出库到堆垛机出库口->推送给gwms去桁架命令->到达理货桁架请求zms拆垛规则->返回拆垛规则gwcs执行拆垛->拆垛完成请求zwms->返回去叠盘/回库/组盘点入库命令->gwcs到达入库口请求入库->堆垛机执行入库
    // 11->12->13->2->42->52->2->3->4->5 // 回库
    //                  ->15 // 去叠盘
    //                  ->1->2->3->4->5 // 组盘点入库
    @Scheduled(cron = "0/10 * * * * ? ")
    public void autoTallyGoods() {
        // 系统配置界面启用
        Config config = configMapper.selectConfigByCode("auto_tally_goods");
        if (config == null || config.getStatus() == 0) {
            return;
        }
        // 四个换盘点,两个组盘点
        // 先查任务目标站为 3045/3044和3041/3040的换盘组 如果有为空的组则可以出,有一个或两个都不能出
        // todo 需新增两个入库路径 3046/3042
        // 寻找两个能组盘的库位,库位状态为F,满足组盘条件
        // 出库怎么出,才能把组好的盘一次出出去
        // gwcs怎么保证两个都拆完了才请求组盘完成
        // 标记哪一套桁架能用
        int flag = 0;
        // 分别查询两套桁架是否存在任务
        Integer count = wrkMastMapper.selectCount(new EntityWrapper<WrkMast>().eq("source_sta_no", 3046).or().in("sta_no", 3045, 3044));
        if (count == 0) {
            flag = 1;
        } else {
            count = wrkMastMapper.selectCount(new EntityWrapper<WrkMast>().in("source_sta_no", 3042).or().in("sta_no", 3041, 3040));
            if (count == 0) {
                flag = 2;
            }
        }
        // 没有找到可用桁架,桁架都都存在任务
        if (flag == 0) {
            return;
        }
        // 指定桁架生成理货任务
        ghjtHandler.autoTallyGoods(flag);
    }
}
src/main/java/com/zy/asrs/task/handler/GhjtHandler.java
@@ -11,7 +11,9 @@
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;
@@ -31,6 +33,7 @@
 */
@Slf4j
@Service
@Transactional
public class GhjtHandler {
    @Resource
@@ -63,7 +66,12 @@
    @Autowired
    private SlaveProperties slaveProperties;
    @Transactional
    @Resource
    private CommonService commonService;
    // 堆垛机对应一楼出库站点
    private static final int[] oneFloorOutSite = new int[]{0,3002,3003,3006,3008,3009,3012};
    public void startCkrwPushGwcs(WrkMast wrkMast) {
        // 获取请求头
@@ -166,11 +174,6 @@
        } else if (wrkMast.getIoType() == 103 && (wrkMast.getStaNo() == 3077 || wrkMast.getStaNo() == 3106)) { // 两箱出一箱,需桁架理货
            // 都要先去理货
            flag = 2;
//            if (wrkDetls.size() == 1) { // 一卷贴标出库
//                flag = 4;
//            } else { // 多卷直接出库
//
//            }
        }
        return flag;
    }
@@ -178,7 +181,6 @@
    /**
     * 自动备货处理
     */
    @Transactional
    public void autoStockUpHandler(List<String> list,int columnNum) {
        // 根据包装组号获取所在库位
@@ -269,7 +271,6 @@
    }
    @Transactional
    public void autoMoveLoc(List<OrderDetl> orderDetlList) {
        // 判断是否已经有执行的移库任务
@@ -373,4 +374,180 @@
        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;
    }
}
src/main/java/com/zy/asrs/task/handler/WorkMastHandler.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.core.common.Cools;
import com.zy.asrs.entity.*;
import com.zy.asrs.mapper.LocDetlMapper;
import com.zy.asrs.mapper.OrderDetlMapper;
import com.zy.asrs.mapper.OrderMapper;
import com.zy.asrs.service.*;
@@ -42,6 +43,9 @@
    private OrderDetlService orderDetlService;
    @Resource
    private OrderDetlMapper orderDetlMapper;
    @Resource
    private LocDetlMapper locDetlMapper;
    @Resource
    private OrderMapper orderMapper;
@@ -113,7 +117,8 @@
                            locDetl.sync(wrkDetl);
                            locDetl.setLocNo(wrkMast.getLocNo()); // 库位号
                            locDetl.setAnfme(wrkDetl.getAnfme()); // 数量
                            locDetl.setZpallet(wrkDetl.getZpallet()); // 托盘条码
                            locDetl.setZpallet(wrkMast.getBarcode()); // 托盘条码
                            locDetl.setBarcode(wrkMast.getBarcode());
                            locDetl.setModiTime(now);
                            locDetl.setAppeTime(now);
                            locDetl.setBatch("");
@@ -199,6 +204,10 @@
//                        } catch (Exception ignore){}
                    }
                    // 修改捡料入库的库存明细的理货状态为待理货
                    locDetlMapper.updateLhStsByLocNo(wrkMast.getLocNo(),1);
                    // 修改库位状态 Q ====>> F
                    if (locMast.getLocSts().equals("Q")) {
                        locMast.setLocSts("F");
@@ -453,6 +462,19 @@
                        return FAIL.setMsg("库位移转 ===>> 修改目标库位状态失败; [workNo=" + wrkMast.getWrkNo() + "],[locNo=" + wrkMast.getLocNo() + "]");
                    }
                    break;
                // 空闲理货入库
                case 59:
                    // 修改库位状态 S ====>> F
                    if (locMast.getLocSts().equals("S")) {
                        locMast.setLocSts("F");
                        locMast.setModiTime(now);
                        locMastService.updateById(locMast);
                    }
                    // 修改空闲理货入库的库存明细的理货状态为待理货
                    locDetlMapper.updateLhStsByLocNo(wrkMast.getLocNo(),0);
                    break;
                default:
                    break;
            }
src/main/resources/mapper/LocDetlMapper.xml
@@ -394,6 +394,32 @@
            #{item}
        </foreach>
    </select>
    <select id="selectTallyGoosList" resultMap="BaseResultMap">
        select
            a.*
        from
            asr_loc_detl a
        where
                a.loc_no in(
                select
                    DISTINCT ald.loc_no
                from
                    asr_loc_mast alm
                        inner join asr_loc_detl ald on
                        alm.loc_no = ald.loc_no
                where
                    alm.loc_sts = 'F'
                  and alm.bay1 > #{columnNum}
                  and ald.dead_warn = 0
                group by
                    ald.loc_no ,
                    ald.brand
                HAVING
                    count(ald.model) = 1
            )
        order by
            a.manu_date asc
    </select>
    <update id="updateMatTurn">
        UPDATE a
@@ -404,6 +430,9 @@
        FROM asr_loc_detl a
        INNER JOIN man_mat b ON a.matnr=#{matnrOld} AND b.matnr=#{matnr};
    </update>
    <update id="updateLhStsByLocNo">
        update asr_loc_detl set dead_warn = #{status},modi_time = getdate() where loc_no = #{locNo}
    </update>
</mapper>