package com.zy.asrs.task.handler;
|
|
import com.alibaba.fastjson.JSONObject;
|
import com.baomidou.mybatisplus.mapper.EntityWrapper;
|
import com.core.common.Cools;
|
import com.core.exception.CoolException;
|
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.*;
|
import java.util.stream.Collectors;
|
|
/**
|
* @author pang.jiabao
|
* @description 冠鸿江铜定时任务实现
|
* @createDate 2024/7/3 9:09
|
*/
|
@Slf4j
|
@Service
|
@Transactional
|
public class GhjtHandler {
|
|
@Resource
|
private OrderMapper orderMapper;
|
|
@Autowired
|
private ApiLogService apiLogService;
|
|
@Resource
|
private WrkMastMapper wrkMastMapper;
|
|
@Resource
|
private WrkDetlService wrkDetlService;
|
|
@Resource
|
private OrderDetlMapper orderDetlMapper;
|
|
@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};
|
|
// 堆垛机对应一楼入库站点
|
private static final int[] oneFloorInSite = new int[]{0,3001,3004,3005,3007,3010,3011};
|
|
public void startCkrwPushGwcs(WrkMast wrkMast) {
|
|
// 获取请求头
|
Map<String, Object> headers = new HashMap<>();
|
headers.put("Content-Type", "application/json;charset=UTF-8");
|
|
// 下发给gwcs要走的路径标识
|
int descFlag = getDescToGwcs(wrkMast);
|
|
// 构造请求体
|
JSONObject jsonObject = new JSONObject();
|
jsonObject.put("workNo", wrkMast.getWrkNo());
|
jsonObject.put("staNo", wrkMast.getIoType() == 3 ? 3013 :wrkMast.getStaNo());
|
jsonObject.put("barcode", wrkMast.getBarcode());
|
jsonObject.put("sourceStaNo", wrkMast.getIoType() == 3 ? wrkMast.getStaNo() : wrkMast.getSourceStaNo());
|
jsonObject.put("descFlag", descFlag); // 101出库时用,0只有一条路径,1理货贴标路径,2贴标打带路径
|
String body = jsonObject.toJSONString();
|
|
boolean success = false;
|
String response = "";
|
try {
|
response = new HttpHandler.Builder()
|
.setUri(MesConstant.GWCS_IP_PORT)
|
.setPath(MesConstant.GWCS_DCKK_URL)
|
.setHeaders(headers)
|
.setJson(body)
|
.build()
|
.doPost();
|
if (!Cools.isEmpty(response)) {
|
wrkMast.setWrkSts(2L); // 更新为设备上走
|
if (wrkMast.getIoType() == 110) { // 空托盘出库直接完成任务
|
wrkMast.setWrkSts(14L);
|
} else if (wrkMast.getIoType() == 3) {
|
// 修改工作主档状态
|
wrkMast.setWrkSts(15L);
|
wrkMast.setModiTime(new Date());
|
} else if(wrkMast.getIoType() == 12) { // 跨巷道转移
|
wrkMast.setWrkSts(1L); // 状态改为1.生成入库id
|
wrkMast.setCrnNo(Arrays.binarySearch(oneFloorInSite,wrkMast.getStaNo()));// 堆垛机转换为入库的堆垛机
|
wrkMast.setModiTime(new Date());
|
}
|
wrkMastMapper.updateById(wrkMast);
|
success = true;
|
} else {
|
log.error("请求接口失败!!!url:{};request:{};response:{}", MesConstant.URL + MesConstant.PAKIN_URL, body, response);
|
throw new CoolException("下发出库任务给GWCS(从出库码头到出库口)失败");
|
}
|
} catch (Exception e) {
|
log.error("下发出库任务给GWCS(从出库码头到出库口)异常,工作号:{},{}", wrkMast.getWrkNo(), e.getMessage());
|
} finally {
|
try {
|
// 保存接口日志
|
apiLogService.save(
|
"从出库码头到出库口",
|
MesConstant.URL + MesConstant.PAKIN_URL,
|
null,
|
"127.0.0.1",
|
jsonObject.toJSONString(),
|
response,
|
success
|
);
|
} catch (Exception e) {
|
log.error("接口日志保存异常", e);
|
}
|
}
|
}
|
|
/**
|
* 全板出库到3077或3106的任务,判断出库要走的路径
|
* @param wrkMast 工作主档
|
* @return 1.直接出库,只有一条路径 2.理货贴标出库 3.贴标打带出库 4.贴标出库
|
*/
|
private int getDescToGwcs(WrkMast wrkMast) {
|
// todo 两条路线怎么选
|
int flag = 1;
|
List<WrkDetl> wrkDetls = wrkDetlService.selectList(new EntityWrapper<WrkDetl>().eq("wrk_no", wrkMast.getWrkNo()));
|
List<String> collect = wrkDetls.stream().map(WrkDetl::getBrand).distinct().collect(Collectors.toList());
|
|
if (wrkMast.getIoType() == 101 && (wrkMast.getStaNo() == 3077 || wrkMast.getStaNo() == 3106)) {
|
// 有几个木箱
|
if (collect.size() == 1) { // 一箱
|
if (wrkDetls.size() == 1) { // 一卷去贴标
|
flag = 4;
|
} else { // 多卷直接出
|
|
}
|
} else if(collect.size() == 2) { // 两箱去贴标打带
|
|
// 两箱肯定都是单卷情况 同型号(木箱型号-管芯类型-实测宽幅-生箔厚度-分切下料时间)去贴标,打带,不同型号贴标出库
|
WrkDetl wrkDetl1 = wrkDetls.get(0);
|
WrkDetl wrkDetl2 = wrkDetls.get(1);
|
if (wrkDetl1.getColor().equals(wrkDetl2.getColor())&&
|
wrkDetl1.getSku().equals(wrkDetl2.getSku())&&
|
wrkDetl1.getManu().equals(wrkDetl2.getManu())&&
|
wrkDetl1.getItemNum().equals(wrkDetl2.getItemNum())) {
|
flag = 3;
|
} else {
|
flag = 4;
|
}
|
}
|
} else if (wrkMast.getIoType() == 103 && (wrkMast.getStaNo() == 3077 || wrkMast.getStaNo() == 3106)) { // 两箱出一箱,需桁架理货
|
// 都要先去理货
|
flag = 2;
|
}
|
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).orderBy("lev1,bay1,row1"));
|
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).orderBy("lev1,bay1,row1"));
|
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);
|
|
// 过滤掉木箱规格宽度大于600的(大于600一个托盘只能放一箱)
|
tallyGoosList = tallyGoosList.stream().filter(locDetl -> {
|
String[] split = locDetl.getColor().split("\\*");
|
if (split.length == 3) {
|
return Integer.parseInt(split[1]) <= 600;
|
} else {
|
return false;
|
}
|
}).collect(Collectors.toList());
|
|
// 寻找满足理货条件的两个木箱
|
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;
|
}
|
|
}
|