Jelajahi Sumber

退火炉合并排程算法优化

fangpy 1 tahun lalu
induk
melakukan
b31d80f4e5

+ 14 - 1
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/ApsBalancingApplication.java

@@ -142,9 +142,22 @@ public class ApsBalancingApplication {
         /*BigDecimal a = new BigDecimal("516.78");
         System.out.println(a.intValue());*/
 
-        System.out.println(new BigDecimal(40).divide(new BigDecimal(10.9), 2, RoundingMode.HALF_UP).intValue());
+//        System.out.println(new BigDecimal(40).divide(new BigDecimal(10.9), 2, RoundingMode.HALF_UP).intValue());
+        Integer a1 = 1;
+        Integer a2 = 2;
+        List<Integer> list = new ArrayList<>();
+        list.add(a1);list.add(a2);
+        Collections.sort(list,(v1,v2)->{
+            return v1.compareTo(v2);
+        });
+        System.out.println(list);
+        Collections.sort(list,(v1,v2)->{
+            return v2.compareTo(v1);
+        });
+        System.out.println(list);
     }
 
+
     public static void constraintTest(){
         // 构建约束校验器,入参:约束供应者实例,规划方案类,规划实体类
         /*ConstraintVerifier<ApsConstraintProvider, TimeTable> constraintVerifier = ConstraintVerifier.build(

+ 5 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/domain/Equipment.java

@@ -125,6 +125,11 @@ public class Equipment implements Serializable {
      */
     private BigDecimal lastSerialLbWeight;
 
+    /**
+     * 当前设备最后工序的单卷重量
+     */
+    private BigDecimal lastSinglerollweight;
+
     /**
      * 关联设备,铸轧机关联的熔炼炉
      */

+ 4 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/domain/ProduceOrder.java

@@ -45,5 +45,9 @@ public class ProduceOrder implements Serializable {
      */
     private List<ProductionProcesses> processes;
 
+    /**
+     * 最大装炉卷数
+     */
+    private Integer maxheatroll;
 
 }

+ 3 - 3
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/domain/ProductionProcesses.java

@@ -364,13 +364,13 @@ public class ProductionProcesses implements Serializable {
         Integer maxDelay = 1;
         if(!this.ifLock){
             if(this.processType.equals("成退") || this.processType.equals("中退") || this.processType.equals("小卷成退")){
-                maxDelay = 2000;
+                maxDelay = 200;
             }else if(this.processType.equals("铸轧")){
                 maxDelay = 500;
             } else if (this.processType.equals("冷轧")) {
-                maxDelay = 2000;
-            } else{
                 maxDelay = 500;
+            } else{
+                maxDelay = 100;
             }
         }
         return ValueRangeFactory.createIntValueRange(0, maxDelay);

+ 4 - 4
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/listener/TaskStartTimeListener.java

@@ -644,15 +644,15 @@ public class TaskStartTimeListener implements VariableListener<ApsSolution, Prod
                     // 退火工序
                     if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退") || process.getProcessType().equals("小卷成退")){
                         // 没有交叉直接跳过
-                        if(equipmentRunTime.getEndRunTime().compareTo(proStartTime)<=0 || equipmentRunTime.getStartRunTime().compareTo(proEndTime)>=0){
+                        /*if(equipmentRunTime.getEndRunTime().compareTo(proStartTime)<=0 || equipmentRunTime.getStartRunTime().compareTo(proEndTime)>=0){
                             continue;
                         }else{
 
-                        }
+                        }*/
 
                         // 退火工序合并处理
                         // 开始时间一样
-                        /*if(equipmentRunTime.getStartRunTime().compareTo(proStartTime) == 0){
+                        if(equipmentRunTime.getStartRunTime().compareTo(proStartTime) == 0){
                             boolean hasMergeTh = thMerge(process,equipmentRunTime);
                             if(!hasMergeTh){
                                 proStartTime = equipmentRunTime.getEndRunTime().plusMinutes(60);
@@ -676,7 +676,7 @@ public class TaskStartTimeListener implements VariableListener<ApsSolution, Prod
                                     proEndTime = proStartTime.plusMinutes(process.getProduceTime());
                                 }
                             }
-                        }*/
+                        }
                     }
                     // 冷轧工序
                     else if (process.getProcessType().equals("冷轧")) {

+ 134 - 71
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/score/ApsConstraintProvider.java

@@ -33,6 +33,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
                 equipmentRunTime(constraintFactory),
                 eqTimeCrossTuihuo(constraintFactory),
                 eqTimeCrossMinTuihuo(constraintFactory),
+//                twoLineZz(constraintFactory),
                 // MEDIUM
                 deliveryDate(constraintFactory),
                 seriesProduce(constraintFactory),
@@ -40,8 +41,8 @@ public class ApsConstraintProvider implements ConstraintProvider {
                 mergeTuihuo(constraintFactory),
                 sameEquipment(constraintFactory),
 
-                processLzNear(constraintFactory),
-                processBtNear(constraintFactory),
+//                processLzNear(constraintFactory),
+//                processBtNear(constraintFactory),
                 seriesZzLb(constraintFactory),
 
                 beforeThClose(constraintFactory),
@@ -119,7 +120,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
                         int a = 1;
                     }*/
                     boolean bln = false;
-                    /*if(productionProcesses.getPreviousProcesses() != null && productionProcesses.getPreviousProcesses().size()>0){
+                    if(productionProcesses.getPreviousProcesses() != null && productionProcesses.getPreviousProcesses().size()>0){
                         // 此种情况简化为前道工序只有一个
                         for (ProductionProcesses previousProcess : productionProcesses.getPreviousProcesses()) {
                             ProductionProcesses preProcess = previousProcess;
@@ -147,8 +148,8 @@ public class ApsConstraintProvider implements ConstraintProvider {
                                 }
                             }
                         }
-                    }*/
-                    if(productionProcesses.getMaxWaitTime() != null && productionProcesses.getMaxWaitTime()>0
+                    }
+                    /*if(productionProcesses.getMaxWaitTime() != null && productionProcesses.getMaxWaitTime()>0
                             && (productionProcesses.getNextProcesses() != null
 //                            && productionProcesses.getNextProcesses().get(0).getIfLock()
                             && productionProcesses.getNextProcesses().size()>0 && "成退,中退,小卷成退".contains(productionProcesses.getNextProcesses().get(0).getProcessType()))){
@@ -179,7 +180,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
                                 }
                             }
                         }
-                    }
+                    }*/
                     return bln;
                 })
                 .penalize(HardMediumSoftScore.ONE_HARD,(productionProcesses) -> 40)
@@ -438,7 +439,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
                 })
                 .penalize(HardMediumSoftScore.ONE_MEDIUM,(equipmentId,processes)->{
                     int counNum = seriesZzLbCount(processes);
-                    return counNum*1000;
+                    return counNum;
                 })
                 .asConstraint("seriesZzLb");
     }
@@ -449,6 +450,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
         if(processes != null && processes.size()>0){
             equipment = processes.get(0).getEquipment();
         }
+
         List<ProductionProcesses> hasStartTimeProcess = processes.stream().filter(v -> v.getStartTime() != null).collect(Collectors.toList());
         // 设备占用时间参与连续生产排程
         if(equipment != null && equipment.getEquipmentRunTimes() != null && equipment.getEquipmentRunTimes().size()>0){
@@ -480,84 +482,110 @@ public class ApsConstraintProvider implements ConstraintProvider {
             pp.setApsOverallConfig(hasStartTimeProcess.get(0).getApsOverallConfig());
             pp.setProduceTime(equipment.getOnceprocessmin());
             pp.setConflictRoptions(new HashMap<>());
+            pp.setSinglerollweight(equipment.getLastSinglerollweight());
             pp.setLastSerialLbWeight(equipment.getLastSerialLbWeight());
             hasStartTimeProcess.add(0,pp);
         }
+
+        // 周期立板已连续生产吨数
+        BigDecimal zqlb = new BigDecimal("0");
         for(int i=0;i<hasStartTimeProcess.size()-1;i++){
-            if(hasStartTimeProcess.get(i).getId() == null && hasStartTimeProcess.get(i).getId() == null){
-                continue;
-            }
             ProductionProcesses prePro = hasStartTimeProcess.get(i);
             ProductionProcesses nextPro = hasStartTimeProcess.get(i+1);
             Map<String, String> conflictRoptions1 = prePro.getConflictRoptions();
             Map<String, String> conflictRoptions2 = nextPro.getConflictRoptions();
+            /*if(equipment.getId().equals("0001be252874536843730b100052")){
+                int aa = 1;
+                if(processes.size() == 10){
+                    aa = 2;
+                    if(prePro.getId() != null && prePro.getId().equals("b99ce83b171948bf8728b735b1529986")){
+                        aa = 3;
+                    }
+                }
+            }*/
             if(hasStartTimeProcess.get(i).getSeriesProduceMark() != null && hasStartTimeProcess.get(i+1).getSeriesProduceMark() != null){
-                // 周期立板已连续生产吨数
-                BigDecimal zqlb = new BigDecimal("0");
-                if(!hasStartTimeProcess.get(i).getSeriesProduceMark().equals(hasStartTimeProcess.get(i+1).getSeriesProduceMark())){
-                    String[] serspre = hasStartTimeProcess.get(i).getSeriesProduceMark().split("\\^_\\^");
-                    String[] sersafter = hasStartTimeProcess.get(i+1).getSeriesProduceMark().split("\\^_\\^");
-                    // 换辊时长(分钟)
-                    int jgtime = hasStartTimeProcess.get(i).getCutfinishmin() + hasStartTimeProcess.get(i+1).getPrepressworkmin();
-                    // 立板时长(分钟)
-                    int standingtime = hasStartTimeProcess.get(i).getApsOverallConfig().getStandingtime()*60;
-                    // 周期立板最大重量
-                    Integer standingyield = hasStartTimeProcess.get(i).getApsOverallConfig().getStandingyield();
-                    BigDecimal standingyieldbig = new BigDecimal(standingyield);
-                    // 取最大值
-                    int maxTime = jgtime;
-                    if(standingtime>jgtime){
-                        maxTime = standingtime;
-                    }
-                    // 周期立板总重量统计
-                    if(zqlb.compareTo(new BigDecimal("0")) == 0){
-                        if(hasStartTimeProcess.get(i).getLastSerialLbWeight() != null){
-                            zqlb = hasStartTimeProcess.get(i).getLastSerialLbWeight();
-                        }else{
-                            zqlb = hasStartTimeProcess.get(i).getSinglerollweight();
-                        }
+                String[] serspre = hasStartTimeProcess.get(i).getSeriesProduceMark().split("\\^_\\^");
+                String[] sersafter = hasStartTimeProcess.get(i+1).getSeriesProduceMark().split("\\^_\\^");
+                // 换辊时长(分钟)
+                int jgtime = hasStartTimeProcess.get(i).getCutfinishmin() + hasStartTimeProcess.get(i+1).getPrepressworkmin();
+                // 立板时长(分钟)
+                int standingtime = hasStartTimeProcess.get(i).getApsOverallConfig().getStandingtime()*60;
+                // 周期立板最大重量
+                Integer standingyield = hasStartTimeProcess.get(i).getApsOverallConfig().getStandingyield();
+                BigDecimal standingyieldbig = new BigDecimal(standingyield);
+                // 取最大值
+                int maxTime = jgtime;
+                if(standingtime>jgtime){
+                    maxTime = standingtime;
+                }
+                // 周期立板总重量统计
+                if(zqlb.compareTo(new BigDecimal("0")) == 0){
+                    if(hasStartTimeProcess.get(i).getLastSerialLbWeight() != null){
+                        zqlb = hasStartTimeProcess.get(i).getLastSerialLbWeight();
                     }else{
-                        zqlb = zqlb.add(hasStartTimeProcess.get(i).getSinglerollweight());
+                        zqlb = hasStartTimeProcess.get(i).getSinglerollweight();
                     }
-                    // 周期立板剩余时间
-                    BigDecimal syLb = zqlb.divide(standingyieldbig, 2, RoundingMode.HALF_UP);
-                    Integer syLbTime = (syLb.multiply(hasStartTimeProcess.get(i).getSinglerollweight()).divide(new BigDecimal(hasStartTimeProcess.get(i).getProduceTime()), 2, RoundingMode.HALF_UP)).intValue()+1;
-
-                    if(serspre.length == 5 && sersafter.length == 5){
-                        // 合金不同则需要换辊和立板
-                        if(!serspre[0].equals(sersafter[0]) || !serspre[1].equals(sersafter[1])){
-                            if(hasStartTimeProcess.get(i).getEndTime().plusMinutes(maxTime).plusMinutes(syLbTime).compareTo(hasStartTimeProcess.get(i+1).getStartTime())>0){
+                }else{
+                    zqlb = zqlb.add(hasStartTimeProcess.get(i).getSinglerollweight());
+                }
+                // 周期立板剩余时间
+                BigDecimal syLb = standingyieldbig.subtract(zqlb);
+                Integer syLbTime = (syLb.multiply(new BigDecimal(hasStartTimeProcess.get(i).getProduceTime()).divide(hasStartTimeProcess.get(i).getSinglerollweight(), 2, RoundingMode.HALF_UP)).intValue()+1);
+                // 铸轧立板是否连续
+                boolean lbserice = true;
+                if(serspre.length == 5 && sersafter.length == 5){
+                    // 合金不同则需要换辊和立板
+                    if(!serspre[0].equals(sersafter[0]) || !serspre[1].equals(sersafter[1])){
+                        if(hasStartTimeProcess.get(i).getEndTime().plusMinutes(maxTime).plusMinutes(syLbTime).compareTo(hasStartTimeProcess.get(i+1).getStartTime())>0){
+                            // 只有存在当前排程的作业才会记录扣分数
+                            if(nextPro.getId() != null){
                                 b++;
-                                conflictRoptions1.put("soft-seriesZzLb","和下道工序没有预留足够的周期立板时间");
-                                conflictRoptions2.put("soft-seriesZzLb","和下道工序没有预留足够的周期立板时间");
-                                zqlb = new BigDecimal("0");
+                                conflictRoptions2.put("soft-seriesZzLb","和上道工序没有预留足够的周期立板时间");
                             }
-                        }else{
-                            // 合金相同情况下后面的宽度大于前面的宽度需要换辊和立板
-                            // 合金相同情况下后面的宽度小于前面的宽度需要立板
-                            // 合金相同情况下后面的宽度等于前面的宽度换辊和立板都不需要
-                            String s1 = serspre[2];
-                            String s2 = sersafter[2];
-                            try{
-                                BigDecimal i1 = new BigDecimal(s1);
-                                BigDecimal i2 = new BigDecimal(s2);
-                                if(i1.compareTo(i2)<0){
-                                    if(hasStartTimeProcess.get(i).getEndTime().plusMinutes(maxTime).plusMinutes(syLbTime).compareTo(hasStartTimeProcess.get(i+1).getStartTime())>0){
+                        }
+                        zqlb = new BigDecimal("0");
+                        lbserice = false;
+                    }else{
+                        // 合金相同情况下后面的宽度大于前面的宽度需要换辊和立板
+                        // 合金相同情况下后面的宽度小于前面的宽度需要立板
+                        // 合金相同情况下后面的宽度等于前面的宽度换辊和立板都不需要
+                        String s1 = serspre[2];
+                        String s2 = sersafter[2];
+                        try{
+                            BigDecimal i1 = new BigDecimal(s1);
+                            BigDecimal i2 = new BigDecimal(s2);
+                            if(i1.compareTo(i2)<0){
+                                if(hasStartTimeProcess.get(i).getEndTime().plusMinutes(maxTime).plusMinutes(syLbTime).compareTo(hasStartTimeProcess.get(i+1).getStartTime())>0){
+                                    if(nextPro.getId() != null){
                                         b++;
-                                        conflictRoptions1.put("soft-seriesZzLb","和下道工序没有预留足够的周期立板时间");
-                                        conflictRoptions2.put("soft-seriesZzLb","和下道工序没有预留足够的周期立板时间");
-                                        zqlb = new BigDecimal("0");
+                                        conflictRoptions2.put("soft-seriesZzLb","和上道工序没有预留足够的周期立板时间");
                                     }
-                                }else if(i1.compareTo(i2)>0){
-                                    if(hasStartTimeProcess.get(i).getEndTime().plusMinutes(standingtime).plusMinutes(syLbTime).compareTo(hasStartTimeProcess.get(i+1).getStartTime())>0){
+                                }
+                                zqlb = new BigDecimal("0");
+                                lbserice = false;
+                            }else if(i1.compareTo(i2)>0){
+                                if(hasStartTimeProcess.get(i).getEndTime().plusMinutes(standingtime).plusMinutes(syLbTime).compareTo(hasStartTimeProcess.get(i+1).getStartTime())>0){
+                                    if(nextPro.getId() != null){
                                         b++;
-                                        conflictRoptions1.put("soft-seriesZzLb","和下道工序没有预留足够的周期立板时间");
-                                        conflictRoptions2.put("soft-seriesZzLb","和下道工序没有预留足够的周期立板时间");
-                                        zqlb = new BigDecimal("0");
+                                        conflictRoptions2.put("soft-seriesZzLb","和上道工序没有预留足够的周期立板时间");
                                     }
                                 }
-                            }catch (Exception e){
-                                e.printStackTrace();
+                                zqlb = new BigDecimal("0");
+                                lbserice = false;
+                            }
+                        }catch (Exception e){
+                            e.printStackTrace();
+                        }
+                    }
+                }
+                // 判断是否满足立板周期连续
+                if(lbserice){
+                    if(syLb.compareTo(nextPro.getSinglerollweight())<0){
+                        zqlb = new BigDecimal("0");
+                        if(hasStartTimeProcess.get(i).getEndTime().plusMinutes(standingtime).compareTo(hasStartTimeProcess.get(i+1).getStartTime())>0){
+                            if(nextPro.getId() != null){
+                                b++;
+                                conflictRoptions2.put("soft-seriesZzLb","和上道工序没有预留足够的周期立板时间");
                             }
                         }
                     }
@@ -873,6 +901,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
         if(equipment != null && equipment.getEquipmentRunTimes() != null && equipment.getEquipmentRunTimes().size()>0){
             for (EquipmentRunTime equipmentRunTime : equipment.getEquipmentRunTimes()) {
                 ProductionProcesses pp = new ProductionProcesses();
+                pp.setId("equipmentRunTime-"+UUID.randomUUID());
                 pp.setStartTime(equipmentRunTime.getStartRunTime());
                 pp.setEquipment(equipment);
                 pp.setEndTime(equipmentRunTime.getEndRunTime());
@@ -1086,6 +1115,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
         if(equipment != null && equipment.getEquipmentRunTimes() != null && equipment.getEquipmentRunTimes().size()>0){
             for (EquipmentRunTime equipmentRunTime : equipment.getEquipmentRunTimes()) {
                 ProductionProcesses pp = new ProductionProcesses();
+                pp.setId("equipmentRunTime-"+UUID.randomUUID());
                 pp.setStartTime(equipmentRunTime.getStartRunTime());
                 pp.setEquipment(equipment);
                 pp.setEndTime(equipmentRunTime.getEndRunTime());
@@ -1171,6 +1201,9 @@ public class ApsConstraintProvider implements ConstraintProvider {
             if(v.size()>2){
                 int a = 1;
             }
+            if(processes.size()>1){
+                int a = 1;
+            }
             BigDecimal totalWidth = new BigDecimal("0");
             BigDecimal totalWeight = new BigDecimal("0");
             Set<String> metalSet = new HashSet<>();
@@ -1460,14 +1493,17 @@ public class ApsConstraintProvider implements ConstraintProvider {
         if(processes != null && processes.size()>0){
             equipment = processes.get(0).getEquipment();
         }
-        /*if(equipment.getId().equals("0001be252874536843730b100151")){
+        if(equipment.getId().equals("0001be252874536843730b100124")){
             int abc = 1;
-        }*/
+            if(processes.get(0).getDelay() == 100){
+                int abcd = 1;
+            }
+        }
         List<ProductionProcesses> hasStartTimeProcess = processes.stream().filter(v -> v.getStartTime() != null).collect(Collectors.toList());
         if(hasStartTimeProcess != null && hasStartTimeProcess.size()>0){
-            if(hasStartTimeProcess.get(0).getApsStatus().equals("CH") && hasStartTimeProcess.get(0).getProcessType().equals("冷轧")){
+            /*if(hasStartTimeProcess.get(0).getApsStatus().equals("CH") && hasStartTimeProcess.get(0).getProcessType().equals("冷轧")){
                 return b;
-            }
+            }*/
             // 设备占用时间参与连续生产排程
             if(equipment != null && equipment.getEquipmentRunTimes() != null && equipment.getEquipmentRunTimes().size()>0){
                 for (EquipmentRunTime equipmentRunTime : equipment.getEquipmentRunTimes()) {
@@ -2102,6 +2138,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
         if(equipment != null && equipment.getEquipmentRunTimes() != null && equipment.getEquipmentRunTimes().size()>0){
             for (EquipmentRunTime equipmentRunTime : equipment.getEquipmentRunTimes()) {
                 ProductionProcesses pp = new ProductionProcesses();
+                pp.setId("equipmentRunTime"+UUID.randomUUID());
                 pp.setStartTime(equipmentRunTime.getStartRunTime());
                 pp.setEquipment(equipment);
                 pp.setEndTime(equipmentRunTime.getEndRunTime());
@@ -2229,4 +2266,30 @@ public class ApsConstraintProvider implements ConstraintProvider {
         }
         return b;
     }
+
+    /**
+     * 硬约束:铸轧最多排2条线
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint twoLineZz(ConstraintFactory constraintFactory) {
+        return constraintFactory.forEach(ProductionProcesses.class)
+                .filter(pro->{
+                    return pro.getProcessType().equals("铸轧");
+                })
+                .groupBy(ProductionProcesses::getUniqueBsProcessesId,ConstraintCollectors.toList())
+                .filter((bsProcessesId,processes) -> {
+                    if(processes != null && processes.size()>0){
+                        Map<String, List<ProductionProcesses>> listMap = processes.stream().collect(Collectors.groupingBy(ProductionProcesses::getEquipmentId));
+                        if(listMap.size()>2){
+                            return true;
+                        }
+                    }
+                    return false;
+                })
+                .penalize(HardMediumSoftScore.ONE_HARD,(bsProcessesId,processes)->{
+                    return 50;
+                })
+                .asConstraint("twoLineZz");
+    }
 }

+ 2 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/service/ApsService.java

@@ -16,4 +16,6 @@ public interface ApsService {
 
     List<ProductionProcesses> thProcessMerge(ProductionScheduleVo productionScheduleVo,List<ProductionProcesses> otherThproces);
 
+    List<ProductionProcesses> zzProcessAps(ProductionScheduleVo productionScheduleVo,ApsSolution apsSolution);
+
 }

+ 185 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/service/impl/ApsServiceImpl.java

@@ -1,10 +1,12 @@
 package com.rongwei.rwapsserver.aps.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.StrUtil;
 import com.rongwei.rwapsserver.aps.domain.*;
 import com.rongwei.rwapsserver.aps.score.ApsConstraintProvider;
 import com.rongwei.rwapsserver.aps.service.ApsService;
 import com.rongwei.rwapsserver.aps.util.ApsException;
+import com.rongwei.rwapsserver.aps.vo.ProductionScheduleRetVo;
 import com.rongwei.rwapsserver.aps.vo.ProductionScheduleVo;
 import lombok.extern.slf4j.Slf4j;
 import org.optaplanner.core.api.score.ScoreExplanation;
@@ -25,6 +27,7 @@ import org.springframework.stereotype.Service;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.Duration;
+import java.time.LocalDateTime;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -209,6 +212,12 @@ public class ApsServiceImpl implements ApsService {
                                 a = b;
                             }
                         }
+                        // 最大装炉卷数
+                        Integer maxheatroll = v.get(0).getProduceOrder().get(0).getMaxheatroll();
+                        if(maxheatroll != null && maxheatroll < a){
+                            a = maxheatroll;
+                        }
+
                         // 作业计划加工卷数是否大于一炉最大卷数
                         List<List<ProductionProcesses>> chunks = new ArrayList<>();
                         int listSize = v.size();
@@ -257,4 +266,180 @@ public class ApsServiceImpl implements ApsService {
         }
         return mergeprocesses;
     }
+
+    /**
+     * 多订单合并
+     * @param productionScheduleVo
+     * @param otherThproces
+     * @return
+     */
+    public List<ProductionProcesses> thMultipleProcessMerge(ProductionScheduleVo productionScheduleVo,List<ProductionProcesses> otherThproces){
+        List<ProductionProcesses> processes = productionScheduleVo.getProcesses();
+        // 退火作业集合
+        List<ProductionProcesses> mergeprocesses = new ArrayList<>();
+        // 退火合并后最终作业集合
+        List<ProductionProcesses> allmergeprocesses = new ArrayList<>();
+        // 所有作业集合
+        Map<String,ProductionProcesses> allProMap = new HashMap<>();
+        for (ProductionProcesses process : processes) {
+            if("成退".equals(process.getProcessType()) || "中退".equals(process.getProcessType()) || "小卷成退".equals(process.getProcessType())){
+                mergeprocesses.add(process);
+            }else{
+                allmergeprocesses.add(process);
+            }
+            allProMap.put(process.getId(),process);
+        }
+        // 排程运行时长
+        Integer maxPlanSeconds = productionScheduleVo.getPlanSeconds();
+        Integer prospeed = 20;
+        if(productionScheduleVo.getPcspeed() != null && productionScheduleVo.getPcspeed()>0){
+            prospeed = productionScheduleVo.getPcspeed();
+        }
+        int processNum = productionScheduleVo.getProcesses().size();
+        int runPlanSeconds = (processNum/prospeed + 1)*60;
+        if(maxPlanSeconds == null || maxPlanSeconds <= 0){
+            maxPlanSeconds = 1800;
+        }
+        if(runPlanSeconds > maxPlanSeconds){
+            throw new ApsException("所选排程作业太多,预计排程时间太长,请分开排程");
+        }
+        EnvironmentMode mode = EnvironmentMode.REPRODUCIBLE;
+        if(productionScheduleVo.getEnvironmentMode() != null && productionScheduleVo.getEnvironmentMode() == 1){
+            mode = EnvironmentMode.FULL_ASSERT;
+        }
+        // 退火提前排序
+        if(mergeprocesses != null && mergeprocesses.size()>0){
+            // 退火工序求解器运行
+            int processNum1 = mergeprocesses.size();
+            int runPlanSeconds1 = (processNum1)*10;
+            // CPU核数
+            String cores = Runtime.getRuntime().availableProcessors() + "";
+            SolverFactory<ApsSolution> solverFactory1 = SolverFactory.create(new SolverConfig()
+                    .withEnvironmentMode(mode)
+                    .withSolutionClass(ApsSolution.class)
+                    .withEntityClasses(ProductionProcesses.class)
+                    .withConstraintProviderClass(ApsConstraintProvider.class)
+                    .withTerminationSpentLimit(Duration.ofSeconds(runPlanSeconds1))
+                    .withMoveThreadCount(cores)
+            );
+            Solver<ApsSolution> solver1 = solverFactory1.buildSolver();
+            ApsSolution apsSolutionTh = new ApsSolution();
+            apsSolutionTh.setProcessesList(mergeprocesses);
+            apsSolutionTh.setEquipmentList(productionScheduleVo.getEquipmentList());
+            ApsSolution solvedBalance1 = solver1.solve(apsSolutionTh);
+            log.info("**************退火排程评分分析***************");
+            SolutionManager<ApsSolution, HardSoftScore> scoreManager1 = SolutionManager.create(solverFactory1);
+            ScoreExplanation<ApsSolution, HardSoftScore> explain1 = scoreManager1.explain(solvedBalance1);
+            log.info(explain1.toString());
+            log.info("**************退火排程评分分析***************");
+
+            // 根据退火排程,提前合并
+            Map<LocalDateTime, List<ProductionProcesses>> mergeThPros = solvedBalance1.getProcessesList().stream()
+                    .collect(Collectors.groupingBy(ProductionProcesses::getStartTime));
+            mergeThPros.forEach((k,thps)->{
+                if(thps.size()>1){
+                    Collections.sort(thps,(v1,v2)->v2.getOpeProducePcNum().compareTo(v1.getOpeProducePcNum()));
+
+                    for (int i = 0; i < thps.size(); i++) {
+                        if(i>0){
+                            otherThproces.add(thps.get(i));
+                            // 退火前一道作业设置下一作业ID
+                            for (ProductionProcesses prepro : thps.get(i).getPreviousProcesses()) {
+                                List<String> nextids = new ArrayList<>();
+                                nextids.add(thps.get(0).getId());
+                                prepro.setNextProcessesIds(nextids);
+
+                                thps.get(0).getPreviousProcessesIds().add(prepro.getId());
+                            }
+                            // 退火后一道作业设置上一道作业ID
+                            if(thps.get(i).getNextProcessesIds() != null && thps.get(i).getNextProcessesIds().size()>0){
+                                for (String nextProcessesId : thps.get(i).getNextProcessesIds()) {
+                                    ProductionProcesses nextpro = allProMap.get(nextProcessesId);
+                                    List<String> preids = new ArrayList<>();
+                                    preids.add(thps.get(0).getId());
+                                    nextpro.setPreviousProcessesIds(preids);
+                                }
+                                thps.get(0).getNextProcessesIds().addAll(thps.get(i).getNextProcessesIds());
+                            }
+                            // 小卷退火卷数合并
+                            if(thps.get(i).getProcessType().equals("小卷退火")){
+                                thps.get(0).setMinThPcNum(thps.get(0).getMinThPcNum()+thps.get(i).getMinThPcNum());
+                            }
+                            // 取第一个作业作为合并作业
+                            ProductionProcesses mergePro = thps.get(0);
+                            mergePro.setVolumeWidth(mergePro.getVolumeWidth().add(thps.get(i).getVolumeWidth()));
+                            mergePro.setSinglerollweight(mergePro.getSinglerollweight().add(thps.get(i).getSinglerollweight()));
+                            mergePro.setOpeProducePcNum(mergePro.getOpeProducePcNum()+thps.get(i).getOpeProducePcNum());
+                        }
+                    }
+                    allmergeprocesses.add(thps.get(0));
+                }
+            });
+        }
+        return allmergeprocesses;
+    }
+
+    /**
+     * 铸轧提前排程
+     * @param productionScheduleVo
+     * @param apsSolution
+     * @return
+     */
+    @Override
+    public List<ProductionProcesses> zzProcessAps(ProductionScheduleVo productionScheduleVo, ApsSolution apsSolution) {
+        ApsSolution apsSolutionZz = new ApsSolution();
+        List<ProductionProcesses> zzprocesList = new ArrayList<>();
+        for (ProductionProcesses process : apsSolution.getProcessesList()) {
+            if(process.getProcessType().equals("铸轧") && !process.getIfLock()){
+                zzprocesList.add(process);
+            }
+        }
+        if(zzprocesList != null && zzprocesList.size()>0){
+            apsSolutionZz.setStartTime(apsSolution.getStartTime());
+            apsSolutionZz.setEquipmentList(apsSolution.getEquipmentList());
+            apsSolutionZz.setProcessesList(zzprocesList);
+
+            // 排程运行时长
+            EnvironmentMode mode = EnvironmentMode.REPRODUCIBLE;
+            if(productionScheduleVo.getEnvironmentMode() != null && productionScheduleVo.getEnvironmentMode() == 1){
+                mode = EnvironmentMode.FULL_ASSERT;
+            }
+            List<ProductionProcesses> otherThproces = new ArrayList<>();
+            // 铸轧提前排序
+            int processNum1 = apsSolutionZz.getProcessesList().size();
+            int runPlanSeconds1 = (processNum1)*10;
+            // CPU核数
+            String cores = Runtime.getRuntime().availableProcessors() + "";
+            SolverFactory<ApsSolution> solverFactory1 = SolverFactory.create(new SolverConfig()
+                    .withEnvironmentMode(mode)
+                    .withSolutionClass(ApsSolution.class)
+                    .withEntityClasses(ProductionProcesses.class)
+                    .withConstraintProviderClass(ApsConstraintProvider.class)
+                    .withTerminationSpentLimit(Duration.ofSeconds(runPlanSeconds1))
+                    .withMoveThreadCount(cores)
+            );
+            Solver<ApsSolution> solver1 = solverFactory1.buildSolver();
+            ApsSolution solvedBalance1 = solver1.solve(apsSolutionZz);
+            log.info("**************铸轧排程评分分析***************");
+            SolutionManager<ApsSolution, HardSoftScore> scoreManager1 = SolutionManager.create(solverFactory1);
+            ScoreExplanation<ApsSolution, HardSoftScore> explain1 = scoreManager1.explain(solvedBalance1);
+            log.info(explain1.toString());
+            log.info("**************铸轧排程评分分析***************");
+
+            for (ProductionProcesses process : apsSolution.getProcessesList()) {
+                if(process.getProcessType().equals("铸轧")){
+                    List<ProductionProcesses> collect = solvedBalance1.getProcessesList().stream().filter(v -> v.getId().equals(process.getId())).collect(Collectors.toList());
+                    if(collect != null && collect.size()>0){
+                        process.setStartTime(collect.get(0).getStartTime());
+                        process.setEndTime(collect.get(0).getEndTime());
+                        process.setEquipmentId(collect.get(0).getEquipmentId());
+                        process.setEquipment(collect.get(0).getEquipment());
+                        process.setDelay(collect.get(0).getDelay());
+                        process.setIfLock(true);
+                    }
+                }
+            }
+        }
+        return null;
+    }
 }

+ 35 - 17
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/service/impl/ProductionScheduleServiceImpl.java

@@ -117,6 +117,12 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
         // optaplanner 求解器数据装配
         List<ProductionProcesses> otherThproces = new ArrayList<>();
         ApsSolution apsSolution = getPreApsSolution(productionScheduleVo,otherThproces);
+        // 铸轧提前排序
+        apsService.zzProcessAps(productionScheduleVo,apsSolution);
+        /*if(1 == 1){
+            return null;
+        }*/
+
         // 去掉锁定工序
         List<ProductionProcesses> notLocks = new ArrayList<>();
         List<ProductionProcesses> hasLocks = new ArrayList<>();
@@ -225,6 +231,10 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
             productionProcesses.getEquipment().setEquipmentRunTimes(null);
             productionProcesses.setPreviousStep(null);
             productionProcesses.setOptionalProviderEquipments(null);
+            if(productionProcesses.getApsOverallConfig() != null){
+                productionProcesses.getApsOverallConfig().setFurnaceInstallations(null);
+                productionProcesses.getApsOverallConfig().setMergeFurnaces(null);
+            }
             // 冲突约束补充处理
             if(productionProcesses.getConflictRoptions() != null && productionProcesses.getConflictRoptions().size()>0){
                 productionProcesses.getConflictRoptions().forEach((k,v)->{
@@ -257,26 +267,31 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
         ProductionProcesses newPre = null;
         List<ProductionProcesses> previousProcesses = process.getPreviousProcesses();
         if(previousProcesses != null){
-            ProductionProcesses preProcess = process;
-            if(!preProcess.getProcessType().equals("成退") && !preProcess.getProcessType().equals("中退") && !preProcess.getProcessType().equals("小卷成退")){
-                Equipment equipment = preProcess.getEquipment();
-                newPre = maxWaitSet(equipment, process.getNextProcesses().get(0), processes, preProcess);
-                if(newPre == null){
-                    for (Equipment equipment1 : equipments) {
-                        if(preProcess.getOptionalEquipments().contains(equipment1.getId()) && !equipment1.getId().equals(equipment.getId())){
-                            newPre = maxWaitSet(equipment1, process, processes, preProcess);
-                            if(newPre != null){
-                                break;
+            for (ProductionProcesses previousProcess : previousProcesses) {
+                if(previousProcess.getEndTime().plusMinutes(previousProcess.getMaxWaitTime()).compareTo(process.getStartTime())>0){
+                    continue;
+                }
+                ProductionProcesses preProcess = previousProcess;
+                if(!preProcess.getProcessType().equals("成退") && !preProcess.getProcessType().equals("中退") && !preProcess.getProcessType().equals("小卷成退")){
+                    Equipment equipment = preProcess.getEquipment();
+                    newPre = maxWaitSet(equipment, process, processes, preProcess);
+                    if(newPre == null){
+                        for (Equipment equipment1 : equipments) {
+                            if(preProcess.getOptionalEquipments().contains(equipment1.getId()) && !equipment1.getId().equals(equipment.getId())){
+                                newPre = maxWaitSet(equipment1, process, processes, preProcess);
+                                if(newPre != null){
+                                    break;
+                                }
                             }
                         }
                     }
-                }
-                if(newPre != null){
-                    preProcess.setStartTime(newPre.getStartTime());
-                    preProcess.setIfLock(false);
-                    preProcess.setEquipment(newPre.getEquipment());
-                    preProcess.setIfLock(true);
-                    maxSetPros.add(newPre);
+                    if(newPre != null){
+                        preProcess.setStartTime(newPre.getStartTime());
+                        preProcess.setIfLock(false);
+                        preProcess.setEquipment(newPre.getEquipment());
+                        preProcess.setIfLock(true);
+                        maxSetPros.add(newPre);
+                    }
                 }
             }
         }
@@ -761,6 +776,9 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
         }
         // 设备占用时间段合并处理
         for (Equipment equipment : productionScheduleVo.getEquipmentList()) {
+            if(equipment.getId().equals("07a5e263a79c47b48c6ff3ea6152e611")){
+                int a = 1;
+            }
             List<EquipmentRunTime> equipmentRunTimesMerge = new ArrayList<>();
             List<EquipmentRunTime> equipmentRunTimes = equipment.getEquipmentRunTimes();
             if(equipmentRunTimes != null && equipmentRunTimes.size()>0){