1.满足均衡排产问题

2.在满足批次完整性的同时,当产能不足时需整批量调整
master
董哲奇 2 years ago
parent 00b3471abe
commit 2a13568185

@ -1,6 +1,5 @@
package com.dsic.gj_erp.controller.jhgk;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.dsic.gj_erp.bean.ResultBean;

@ -37,14 +37,15 @@ public interface Constant {
龙门01(工序.切割,"龙门01","",2.4,3.6,40,15.6,25.2),
火焰01(工序.切割,"火焰01","",0.3,11.15,40.0,0,2.0),
数控01(工序.切割,"数控01","",1.5,3.0,0,8.5,8.5),
火焰03(工序.切割, "火焰03", "", 0.3, 11.5, 40.0, 0, 2.0),
数控03(工序.切割, "数控03", "", 2.0, 4.2, 0, 14.0, 14.0),
龙门03(工序.切割, "龙门03", "", 1.6, 3.2, 40.0, 11.2, 18.4),
火焰02(工序.切割, "火焰02", "", 0.3, 11.5, 40.0, 0, 3.9),
数控02(工序.切割, "数控02", "", 1.4, 3.0, 0, 18.2, 18.2),
龙门02(工序.切割, "龙门02", "", 2.0, 3.0, 40.0, 13.0, 13.0),
火焰03(工序.切割, "火焰03", "", 0.3, 11.5, 40.0, 0, 2.0),
数控03(工序.切割, "数控03", "", 2.0, 4.2, 0, 14.0, 14.0),
龙门03(工序.切割, "龙门03", "", 1.6, 3.2, 40.0, 11.2, 18.4),
坡口01(工序.坡口, "坡口01", "", 2.0, 3.0, 100.0, 13.0, 13.0),
型材01(工序.型材面板, "型材01", "", 2.0, 3.0, 100.0, 13.0, 13.0),
曲加工01(工序.曲型, "曲加工01", "", 2.0, 3.0, 100.0, 13.0, 13.0),

@ -1,5 +1,6 @@
package com.dsic.gj_erp.pc;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjUtil;
@ -13,8 +14,8 @@ import com.dsic.gj_erp.bean.jcsj.EmSbcnp;
import com.dsic.gj_erp.bean.jcsj.EmSbjbb;
import com.dsic.gj_erp.bean.jhgk.DmSygdxq;
import com.dsic.gj_erp.bean.jhgk.DmZrcjh;
import com.dsic.gj_erp.bean.pgd.PgdSljh;
import com.dsic.gj_erp.pc.dto.data.Bom;
import com.dsic.gj_erp.pc.dto.data.按批次合并的钢料需求;
import com.dsic.gj_erp.pc.dto.data.钢料需求;
import com.dsic.gj_erp.pc.dto.gx.*;
import com.dsic.gj_erp.pc.dto.sb.*;
@ -29,8 +30,8 @@ import com.dsic.gj_erp.service.jcsj.EmSbcnpService;
import com.dsic.gj_erp.service.jcsj.EmSbjbbService;
import com.dsic.gj_erp.service.jhgk.DmSygdxqService;
import com.dsic.gj_erp.service.jhgk.DmZrcjhService;
import com.dsic.gj_erp.service.pgd.PgdSljhService;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.AtomicDouble;
import lombok.extern.slf4j.Slf4j;
import java.io.FileOutputStream;
@ -38,7 +39,6 @@ import java.io.OutputStream;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import static com.dsic.gj_erp.pc.Manager.排产;
@ -49,16 +49,24 @@ public class DataFactory {
public static void 排产2() {
装载中日程数据();
套料图所占工序的资源.clear();
Manager.排产结果=new ArrayList<>();
List<钢料需求> list=读取钢料需求();
工序CD接口 _工序CD接口 = getBean(工序CD接口.class);
List<工序CD> 工序CDS = _工序CD接口.get();
Map<Constant.工序,工序CD> 工序CDMap=new HashMap<>();
工序CDS.forEach(item->工序CDMap.put(item.getGx(),item));
执行排产(list,工序CDMap);
清理冗余数据(list);
if (Manager.排产结果.size()>0){
log.info("排产异常--{}个未完整排产,{}",Manager.排产结果.size(),Manager.排产结果);
}
}
private static void 执行排产(List<钢料需求> list, Map<Constant.工序, 工序CD> 工序CDMap){
list.forEach(钢料需求 -> {
CountDownLatch latch = new CountDownLatch(钢料需求.getBomList().size());
String xzglxq = 钢料需求.getXzglxq();
@ -92,22 +100,6 @@ public class DataFactory {
throw new RuntimeException(e);
}
});
清理冗余数据(list);
if (Manager.排产结果.size()>0){
log.info("排产异常--{}个未完整排产,{}",Manager.排产结果.size(),Manager.排产结果);
}
}
public static List<PgdSljh> 获取完工的上料计划(Bom bom){
return getBean(PgdSljhService.class).已排产上料计划装载(bom.getDcch(),bom.getPl(),bom.getFd(),bom.getTzbh());
}
public static Map<String,List<订货计划>> 获取订货计划(){
List<订货计划> _订货计划 = getBean(订货计划滚动信息接口.class).get();
Map<String,List<订货计划>> map= _订货计划.stream().collect(Collectors.groupingBy(订货计划::getDcch));
return map;
}
private static DmZrcjh 获取中日程(String key){
@ -131,11 +123,154 @@ public class DataFactory {
});
}
public static void 排产3(){
装载中日程数据();
套料图所占工序的资源.clear();
Manager.排产结果=new ArrayList<>();
List<钢料需求> list=读取钢料需求();
Collection<按批次合并的钢料需求> 按批次合并 = 按批次合并(list);
计算需求产能(按批次合并);
try {
均衡预占(按批次合并);
}catch (Exception e){
e.printStackTrace();
}finally {
清理冗余数据(list);
}
}
public static void 均衡预占(Collection<按批次合并的钢料需求> _按批次合并的钢料需求){
工序CD接口 _工序CD接口 = getBean(工序CD接口.class);
List<工序CD> 工序CDS = _工序CD接口.get();
Map<Constant.工序,工序CD> 工序CDMap=new HashMap<>();
工序CDS.forEach(item->工序CDMap.put(item.getGx(),item));
for (按批次合并的钢料需求 item:_按批次合并的钢料需求){
if (ObjUtil.isNotEmpty(item.getBomList())){
均衡计算(item,工序CDMap);
}
}
}
private static void 均衡计算(按批次合并的钢料需求 item,Map<Constant.工序,工序CD> 工序CDMap){
Constant.工序 依据工序=Constant.工序.切割;
//需求日期可利用范围是±3天
int times=-3;
List<资源> tmpList=new ArrayList<>();
AtomicDouble atomicDouble = item.get产能需求().get(依据工序);
资源 _资源=null;
do {
//因为第一次计算时候就是当前批量小组需求日期,所以再次循环到的时候不需要再次计算
if (times==0){
times+=1;
continue;
}
if (times>3){
break;
}
DateTime xzglxq = item.get均衡需求日期().offset(DateField.DAY_OF_YEAR,工序CDMap.get(依据工序).getCd());
_资源 = 提取资源(xzglxq);
tmpList.add(_资源);
if (_资源.判断工序是否可以被占用(依据工序,atomicDouble.get())){
// DateTime 上料日期 = item.get均衡需求日期().offset(DateField.DAY_OF_YEAR, -14);
// 资源 上料 = 提取资源(上料日期);
// for (Bom bom:item.getBomList()){
// 上料.占用资源(Constant.工序.上料,bom,item.get产能需求().get(Constant.工序.上料).get());
// _资源.占用资源(Constant.工序.切割,bom,1);
// }
执行排产(item.getBomList(),item.get均衡需求日期(),工序CDMap);
break;
}
//需求日期可利用范围是±3天,排产是需要根据产能进行调整
item.set均衡需求日期(DateUtil.offsetDay(item.get均衡需求日期(),times));
times+=1;
}while (true);
//若在需求期±3天内无法正常排产时,查找最大可用空闲资源进行占用
if (times>3){
//查找最小可用资源,进行占用
tmpList.sort(Comparator.comparingDouble((tmp)->tmp.get工序产能MAP().get(Constant.工序.切割).get占用()));
tmpList.stream().findFirst().ifPresent(tmp->{
// DateTime 上料日期 = item.get均衡需求日期().offset(DateField.DAY_OF_YEAR, -14);
// 资源 上料 = 提取资源(上料日期);
// for (Bom bom:item.getBomList()){
// 上料.占用资源(Constant.工序.上料,bom,item.get产能需求().get(Constant.工序.上料).get());
// tmp.占用资源(Constant.工序.切割,bom,1);
// }
//由于资源日为切割日期,这里需要还原一下,修正传递的参照需求日期
执行排产(item.getBomList(),tmp.get_date().offset(DateField.DAY_OF_YEAR,-工序CDMap.get(依据工序).getCd()),工序CDMap);
});
}
log.info("船号:{},批次:{},占用资源成功,切割日期:{}",item.getDcCh(),item.getDcPl(),_资源.getDate());
}
private static void 执行排产(List<Bom> list,DateTime 参照需求日期,Map<Constant.工序,工序CD> 工序CDMap){
CountDownLatch latch = new CountDownLatch(list.size());
for (Bom bom:list){
套料图工序 _套料图工序 = 套料图工序工厂(bom);
if (_套料图工序==null){
latch.countDown();
continue;
}
_套料图工序.setXzglxq(bom.getXzglxq());
_套料图工序.setDzglxq(bom.getDzglxq());
_套料图工序.set工序CD(工序CDMap);
_套料图工序.set中日程(获取中日程(bom.getDcch() + bom.getPl() + bom.getFd()));
_套料图工序.set参照需求日期(参照需求日期);
es.execute(() -> {
try{
_套料图工序.run();
}finally {
latch.countDown();
}
});
}
try {
latch.await(30, TimeUnit.SECONDS);// 指定超时时间
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private static void 计算需求产能(Collection<按批次合并的钢料需求> _按批次合并的钢料需求){
_按批次合并的钢料需求.forEach(按批次合并的钢料需求::计算各工序产能需求);
}
private static Collection<按批次合并的钢料需求> 按批次合并(List<钢料需求> list){
Map<String,按批次合并的钢料需求> map=new HashMap<>();
list.forEach(item->{
String key=item.getDcCh() + item.getDcPl();
按批次合并的钢料需求 _按批次合并的钢料需求 = map.get(key);
if (_按批次合并的钢料需求==null){
_按批次合并的钢料需求=new 按批次合并的钢料需求();
map.put(key,_按批次合并的钢料需求);
}
_按批次合并的钢料需求.of(item);
});
return map.values();
}
public static List<钢料需求> 读取钢料需求(){
List<DmSygdxq> 按需求日期排序的钢料需求 = getBean(DmSygdxqService.class).getXqWithBom(from, to);
return 按需求日期排序的钢料需求.stream().map(钢料需求::of).collect(Collectors.toList());
}
private static Map<String,List<订货计划>> _订货计划=new HashMap<>();
public static List<订货计划> 获取订货计划(String dcch){
List<订货计划> list=_订货计划.get(dcch);
if (list!=null){
return list;
}
List<订货计划> _订货计划 = getBean(订货计划滚动信息接口.class).get();
Map<String,List<订货计划>> map= _订货计划.stream().collect(Collectors.groupingBy(订货计划::getDcch));
return map.get(dcch);
}
public static void 排产() {
套料图所占工序的资源.clear();
List<排产结果> list = 排产.list;
@ -256,10 +391,7 @@ public class DataFactory {
return 资源;
}
public static void 创建产能资源池(){
资源池.clear();
工厂日历.stream().filter(item -> "1".equals(item.getXxr()))
.forEach(item -> {
public static Map<Constant.工序, 工序产能> 计算日产能(){
Map<Constant.工序, 工序产能> map = new HashMap<>();
Arrays.stream(Constant.工序.values()).forEach(_item -> {
if (StrUtil.isNotEmpty(_item.getCode())) {
@ -269,6 +401,14 @@ public class DataFactory {
}
}
});
return map;
}
public static void 创建产能资源池(){
资源池.clear();
工厂日历.stream().filter(item -> "1".equals(item.getXxr()))
.forEach(item -> {
Map<Constant.工序, 工序产能> map = 计算日产能();
资源池.put(item.getGl(), 资源.of(item.getGl(), map));
});
}

@ -49,6 +49,39 @@ public class Bom {
return _bom;
}
public double 根据工序获取产能需求量(Constant.工序 _工序){
switch (_工序) {
case 上料:
return 1;
case 抛丸:
return 1;
case 理料:
return 1;
case 划线:
return 1;
case 切割:
return 1;
case 坡口:
return 1;
case 型材面板:
return 1;
case 曲型:
return 1;
case 打磨:
return 1;
case 自由边处理:
return 1;
case 光电结束:
return 1;
// case 预配盘:
// return new 工序01上料(_工序,_设备);
// case 配送:
// return new 工序01上料(_工序,_设备);
default:
return 0;
}
}
public String toString() {
return JSONObject.toJSONString(this);
}

@ -0,0 +1,78 @@
package com.dsic.gj_erp.pc.dto.data;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjUtil;
import com.dsic.gj_erp.pc.Constant;
import com.dsic.gj_erp.pc.DataFactory;
import com.dsic.gj_erp.pc.dto.套料图工序;
import com.google.common.util.concurrent.AtomicDouble;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Getter
@Setter
public class 按批次合并的钢料需求 {
private String dcCh;
private String dcPl;
private List<String> dcFd;
private String xzglxq;
private String dzglxq;
private DateTime 均衡需求日期;
private List<Bom> bomList;
private String 预占资源日期;
private Map<Constant.工序, AtomicDouble> 产能需求;//fixme 要求必须所有工序都不可以打乱才能用
private Map<Constant.工序, String> 预占结果;
public void 计算各工序产能需求(){
产能需求=new HashMap<>();
this.bomList.forEach(item->{
套料图工序 _套料图工序 = DataFactory.套料图工序工厂(item);
_套料图工序.get工序().forEach(_工序->{
AtomicDouble aDouble = 产能需求.get(_工序);
if (aDouble==null){
aDouble=new AtomicDouble(0);
产能需求.put(_工序,aDouble);
}
aDouble.addAndGet(item.根据工序获取产能需求量(_工序));
});
});
}
public 按批次合并的钢料需求 of(钢料需求 _钢料需求){
if (this.dcCh==null){
this.dcCh= _钢料需求.getDcCh();
}
if (this.dcPl==null){
this.dcPl=_钢料需求.getDcPl();
}
if (!ObjUtil.equals(_钢料需求.getDcCh(),this.dcCh)||!ObjUtil.equals(_钢料需求.getDcPl(),this.dcPl)){
return this;
}
if (dcFd==null){
dcFd=new ArrayList<>();
}
this.dcFd.add(_钢料需求.getDcFd());
this.xzglxq= _钢料需求.getXzglxq();
this.dzglxq= _钢料需求.getDzglxq();
if (this.bomList==null){
this.bomList=new ArrayList<>();
}
this.bomList.addAll(_钢料需求.getBomList());
return this;
}
public DateTime get均衡需求日期(){
if (this.均衡需求日期 ==null){
return DateUtil.parse(this.xzglxq,"yyyy/MM/dd");
}
return this.均衡需求日期;
}
}

@ -35,6 +35,7 @@ public abstract class 套料图工序 implements 套料图工序接口 {
protected double 需求数量 = 1;//默认数量为1,切割处理时候按照实际切割长度计算,切割产能在设备产能中单独计算
protected String 需求日期;
protected DateTime _需求日期;
protected DateTime 参照需求日期;//当排产无法满足时,临时需求日期生效,±3天查找可用资源
protected DmZrcjh 中日程;
protected List<Constant.工序> 工序;
protected Map<Constant.工序,工序CD> 工序CD;
@ -49,13 +50,25 @@ public abstract class 套料图工序 implements 套料图工序接口 {
}
protected boolean 基础设置(){
if (ObjUtil.isEmpty(this.bom)||StrUtil.isEmpty(this.bom.getTzbh())){
return false;
}
String xzxq=this.xzglxq;
if (this.参照需求日期!=null){
xzxq=this.参照需求日期.toString("yyyy/MM/dd");
}
//为了批次完整性,目前无论大小组都采用小组需求期
this.set需求日期(xzxq);
if (this.zlType==ZlType.小组||this.zlType==ZlType.焊接机器人肘板){
this.set需求日期(this.xzglxq);
this.set需求日期(xzxq);
}
if (this.zlType==ZlType.大组){
if (this.dzglxq==null){
this.set需求日期(this.xzglxq);
this.set需求日期(xzxq);
this.bom.缺少大组需求期使用小组需求替代();
}else {
this.set需求日期(this.dzglxq);
@ -65,35 +78,31 @@ public abstract class 套料图工序 implements 套料图工序接口 {
if (this.zlType==ZlType.大组&&this.工序.contains(Constant.工序.曲型)){
// 大组需求有曲外板,需要按照大组需求期计算
if (this.dzglxq==null){
this.set需求日期(this.xzglxq);
this.bom.缺少大组需求期使用小组需求替代();
this.set需求日期(xzxq);
}else {
this.set需求日期(this.dzglxq);
}
//曲外板批量名称设置
//曲外板批量名称设置.
this.bom.set_pl(this.bom.getPl()+"Q");
}
//当大组需求期无法满足小组需求时,以小组需求期为准
if (StrUtil.isAllNotEmpty(xzglxq,dzglxq)){
if (DateUtil.parse(xzglxq,"yyyy/MM/dd").isAfter(DateUtil.parse(dzglxq,"yyyy/MM/dd"))){
if (StrUtil.isAllNotEmpty(xzxq,dzglxq)){
if (DateUtil.parse(xzxq,"yyyy/MM/dd").isAfter(DateUtil.parse(dzglxq,"yyyy/MM/dd"))){
this.set需求日期(dzglxq);
}
}
if (ObjUtil.isEmpty(this.bom)||StrUtil.isEmpty(this.bom.getTzbh())){
return false;
}
this._需求日期 = DateUtil.parse(this.需求日期, "yyyy/MM/dd");
Map<String,List<订货计划>> _订货计划=DataFactory.获取订货计划();
this.订货计划s = _订货计划.get(bom.getDcch());
this.订货计划s = DataFactory.获取订货计划(bom.getDcch());
return true;
}
public void run(AtomicBoolean atomicBoolean) {
if (!this.订货()){
System.out.println(String.format("船号:%s,批量:%s,未订货",this.bom.getDcch(),this.bom.getPl()));
// System.out.println(String.format("船号:%s,批量:%s,未订货",this.bom.getDcch(),this.bom.getPl()));
bom.没有订货();
}

@ -31,6 +31,17 @@ public class 资源 {
});
}
public boolean 判断工序是否可以被占用(Constant.工序 _工序 ,double 产能消耗){
工序产能 工序产能 = 工序产能MAP.get(_工序);
AtomicBoolean atomicBoolean = new AtomicBoolean(false);
Optional.ofNullable(工序产能).ifPresent(it->{
if (it.判断是否可以占用(产能消耗)){
atomicBoolean.set(true);
}
});
return atomicBoolean.get();
}
public boolean 占用资源(Constant.工序 工序, Bom bom, double 产能消耗){
AtomicBoolean atomicBoolean = new AtomicBoolean(false);
工序产能 工序产能 = 工序产能MAP.get(工序);

@ -25,7 +25,7 @@ public class 排产 {
DataFactory.加载套料图工序对应关系();
DataFactory.加载设备信息();
DataFactory.创建产能资源池();
DataFactory.排产2();
DataFactory.排产3();
}
private static 排产 of(@NotNull String from, @NotNull String to){

Loading…
Cancel
Save