Selaa lähdekoodia

排程算法优化

fangpy 1 vuosi sitten
vanhempi
commit
c21dd47455

+ 69 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/domain/ApsFurnaceInstallationDo.java

@@ -0,0 +1,69 @@
+package com.rongwei.rwapsserver.aps.domain;
+
+import lombok.Data;
+import java.math.BigDecimal;
+
+/**
+ * <p>
+ * 小卷成退合并装炉分组
+ * </p>
+ *
+ * @author fpy
+ * @since 2024-07-27
+ */
+@Data
+public class ApsFurnaceInstallationDo {
+
+    private static final long serialVersionUID=1L;
+
+    /**
+     * 主键ID
+     */
+    private String id;
+    /**
+     * 租户ID
+     */
+    private String tenantid;
+    /**
+     * 扩展json格式配置
+     */
+    private String roption;
+    /**
+     * 分组编号
+     */
+    private String groupnumber;
+    /**
+     * 合金
+     */
+    private String alloy;
+    /**
+     * 输出合金状态
+     */
+    private String alloystatus;
+    /**
+     * 厚度-起(毫米)
+     */
+    private BigDecimal startthickness;
+    /**
+     * 厚度-止(毫米)
+     */
+    private BigDecimal endthickness;
+    /**
+     * 宽度-起(毫米)
+     */
+    private Integer startwidth;
+    /**
+     * 宽度-止(毫米)
+     */
+    private Integer endwidth;
+    /**
+     * 主表id
+     */
+    private String mainid;
+    /**
+     * 分组描述
+     */
+    private String description;
+
+
+}

+ 44 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/domain/ApsMergeFurnaceDo.java

@@ -0,0 +1,44 @@
+package com.rongwei.rwapsserver.aps.domain;
+
+import lombok.Data;
+
+/**
+ * <p>
+ * 小卷成退合并装炉兼容
+ * </p>
+ *
+ * @author fpy
+ * @since 2024-07-27
+ */
+@Data
+public class ApsMergeFurnaceDo {
+
+    private static final long serialVersionUID=1L;
+
+    /**
+     * 主键ID
+     */
+    private String id;
+    /**
+     * 租户ID
+     */
+    private String tenantid;
+    /**
+     * 扩展json格式配置
+     */
+    private String roption;
+    /**
+     * 兼容组
+     */
+    private String compatibilitygroup;
+    /**
+     * 主表id
+     */
+    private byte[] mainid;
+    /**
+     * 兼容组描述
+     */
+    private String comdescription;
+
+
+}

+ 14 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/domain/ApsOverallConfig.java

@@ -34,6 +34,16 @@ public class ApsOverallConfig implements Serializable {
      */
     private List<WashingMetal> closealloynames;
 
+    /**
+     * 小卷成退合并装炉分组规则
+     */
+    private List<ApsFurnaceInstallationDo> furnaceInstallations;
+
+    /**
+     * 小卷成退合并装炉兼容规则
+     */
+    private List<ApsMergeFurnaceDo> mergeFurnaces;
+
     /**
      * 洗炉时间(小时)
      */
@@ -42,6 +52,10 @@ public class ApsOverallConfig implements Serializable {
      * 立板时间(小时)
      */
     private Integer standingtime;
+    /**
+     * 周期立板产量(吨)
+     */
+    private Integer standingyield;
     /**
      * 松散度
      */

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

@@ -115,6 +115,16 @@ public class Equipment implements Serializable {
      */
     private Integer lastProcessCutfinishmin;
 
+    /**
+     * 当前设备最后工序的单卷加工时间
+     */
+    private Integer onceprocessmin;
+
+    /**
+     * 当前设备最后工序的已立板总重量
+     */
+    private BigDecimal lastSerialLbWeight;
+
     /**
      * 关联设备,铸轧机关联的熔炼炉
      */

+ 43 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/domain/EquipmentRunTime.java

@@ -6,6 +6,7 @@ import lombok.Data;
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
+import java.util.Set;
 
 /**
  * 设备运行中时间,表示排程锁定后设备运行时间段
@@ -56,15 +57,45 @@ public class EquipmentRunTime implements Serializable {
      */
     private BigDecimal totalVolumeWidth;
 
+    /**
+     * 最小单卷宽度
+     */
+    private BigDecimal minVolumeWidth;
+    /**
+     * 最大单卷宽度
+     */
+    private BigDecimal maxVolumeWidth;
+
     /**
      * 总卷重
      */
     private BigDecimal totalSinglerollweight;
+    /**
+     * 最小单卷重量
+     */
+    private BigDecimal minSinglerollweight;
+    /**
+     * 最大单卷重量
+     */
+    private BigDecimal maxSinglerollweight;
 
     /**
      * 厚度
      */
     private BigDecimal totalThickness;
+    /**
+     * 最小单卷厚度
+     */
+    private BigDecimal minThickness;
+    /**
+     * 最大单卷厚度
+     */
+    private BigDecimal maxThickness;
+
+    /**
+     * 单次加工时间
+     */
+    private Integer onceprocessmin;
 
     /**
      * 是否锁定标识,排程中的是false
@@ -76,4 +107,16 @@ public class EquipmentRunTime implements Serializable {
      */
     private String occupyType;
 
+    /**
+     * 当前设备最后工序的已立板总重量
+     */
+    private BigDecimal lastSerialLbWeight;
+
+    /**
+     * 产品类型
+     */
+    private String producttype;
+
+    private Set<String> minThGroupNames;
+
 }

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

@@ -148,6 +148,11 @@ public class ProductionProcesses implements Serializable {
      */
     private Integer producePcNum;
 
+    /**
+     * 小卷退火加工卷数
+     */
+    private Integer minThPcNum;
+
     /**
      * 当前工序一批次生产的是否拆分为下一工序批次数量
      */
@@ -243,6 +248,9 @@ public class ProductionProcesses implements Serializable {
     // 合金状态
     private String volumeMetalstate;
 
+    // 产品类型
+    private String producttype;
+
     /**
      * 输入物料
      */
@@ -284,6 +292,11 @@ public class ProductionProcesses implements Serializable {
      */
     private Integer cutfinishmin;
 
+    /**
+     * 当前设备最后工序的已立板总重量
+     */
+    private BigDecimal lastSerialLbWeight;
+
     private Map<String, String> conflictRoptions;
 
     public String getEquipmentType() {
@@ -340,7 +353,7 @@ public class ProductionProcesses implements Serializable {
         }*/
         Integer maxDelay = 1;
         if(!this.ifLock){
-            if(this.processType.equals("成退") || this.processType.equals("中退")){
+            if(this.processType.equals("成退") || this.processType.equals("中退") || this.processType.equals("小卷成退")){
                 maxDelay = 2000;
             }else if(this.processType.equals("铸轧")){
                 maxDelay = 500;
@@ -754,6 +767,30 @@ public class ProductionProcesses implements Serializable {
         this.orderId = orderId;
     }
 
+    public Integer getMinThPcNum() {
+        return minThPcNum;
+    }
+
+    public void setMinThPcNum(Integer minThPcNum) {
+        this.minThPcNum = minThPcNum;
+    }
+
+    public BigDecimal getLastSerialLbWeight() {
+        return lastSerialLbWeight;
+    }
+
+    public void setLastSerialLbWeight(BigDecimal lastSerialLbWeight) {
+        this.lastSerialLbWeight = lastSerialLbWeight;
+    }
+
+    public String getProducttype() {
+        return producttype;
+    }
+
+    public void setProducttype(String producttype) {
+        this.producttype = producttype;
+    }
+
     public String getSeriSort(){
         String sortStr = this.getId();
         if(this.getStartTime() != null){

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

@@ -3,10 +3,7 @@ package com.rongwei.rwapsserver.aps.listener;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
-import com.rongwei.rwapsserver.aps.domain.ApsSolution;
-import com.rongwei.rwapsserver.aps.domain.Equipment;
-import com.rongwei.rwapsserver.aps.domain.EquipmentRunTime;
-import com.rongwei.rwapsserver.aps.domain.ProductionProcesses;
+import com.rongwei.rwapsserver.aps.domain.*;
 import lombok.extern.slf4j.Slf4j;
 import org.optaplanner.core.api.domain.variable.VariableListener;
 import org.optaplanner.core.api.score.director.ScoreDirector;
@@ -93,7 +90,7 @@ public class TaskStartTimeListener implements VariableListener<ApsSolution, Prod
                 LocalDateTime startDateTime = startTimeNewSet(process,scoreDirector);
 
                 // 随机延时时间处理
-                if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退")){
+                if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退") || process.getProcessType().equals("小卷成退")){
                     startDateTime = startDateTime.plusMinutes(process.getDelay() * 60);
                 } else if (process.getProcessType().equals("铸轧")) {
                     startDateTime = startDateTime.plusMinutes(process.getDelay() * 60);
@@ -519,7 +516,7 @@ public class TaskStartTimeListener implements VariableListener<ApsSolution, Prod
 
             // 当前工序最小开始时间、结束时间
             LocalDateTime proStartTime = endTime.plusMinutes(lzTimes);
-            if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退")){
+            if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退") || process.getProcessType().equals("小卷成退")){
                 proStartTime = proStartTime.minusSeconds(proStartTime.getSecond()) // 减去当前秒数
                         .minusNanos(proStartTime.getNano()) // 减去当前纳秒数
                         .plusMinutes(-proStartTime.getMinute() % 60) // 减去当前分钟数的余数,向下调整到最近的10的倍数
@@ -584,7 +581,7 @@ public class TaskStartTimeListener implements VariableListener<ApsSolution, Prod
             if(filterProcess != null && filterProcess.size()>0){
                 for (ProductionProcesses productionProcesses : filterProcess) {
                     boolean merged = false;
-                    if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退")){
+                    if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退") || process.getProcessType().equals("小卷成退")){
                         if(equipmentRunTimes != null && equipmentRunTimes.size()>0){
                             for (EquipmentRunTime equipmentRunTime : equipmentRunTimes) {
                                 if(equipmentRunTime.getOccupyType() != null && "maintenance".equals(equipmentRunTime.getOccupyType())){
@@ -638,84 +635,41 @@ public class TaskStartTimeListener implements VariableListener<ApsSolution, Prod
                 for(int i = 0; i < equipmentRunTimes.size(); i++){
                     EquipmentRunTime equipmentRunTime = equipmentRunTimes.get(i);
                     // 退火工序
-                    if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退")){
+                    if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退") || process.getProcessType().equals("小卷成退")){
                         // 没有交叉直接跳过
                         if(equipmentRunTime.getEndRunTime().compareTo(proStartTime)<=0 || equipmentRunTime.getStartRunTime().compareTo(proEndTime)>=0){
                             continue;
                         }else{
-                            /*boolean isJump = true;
-                            // 待排程工序开始时间大于当前占用工序开始时间
-                            if(equipmentRunTime.getStartRunTime().compareTo(proStartTime)<0){
-                                List<ProductionProcesses> runPro = filterProcess.stream().filter(v -> v.getStartTime().compareTo(equipmentRunTime.getStartRunTime()) == 0).collect(Collectors.toList());
-                                Boolean hasMerge = false;
-                                if(runPro != null && runPro.size()>0){
-                                    BigDecimal equipmentWidth = pe.getEquipmentParameter().getEquipmentWidth();
-                                    BigDecimal equipmentBearing = pe.getEquipmentParameter().getEquipmentBearing();
-
-                                    BigDecimal totalWidth = process.getVolumeWidth();
-                                    BigDecimal totalBearing = process.getSinglerollweight();
-                                    for (ProductionProcesses productionProcesses : runPro) {
-                                        if(productionProcesses.getPreviousProcesses() == null){
-                                            continue;
-                                        }
-                                        ProductionProcesses preRunPro = productionProcesses.getPreviousProcesses().get(0);
-                                        boolean a = true;
-                                        if(preRunPro.getMaxWaitTime() != null && preRunPro.getMaxWaitTime()>0){
-                                            if(preRunPro.getEndTime().plusMinutes(preRunPro.getMaxWaitTime()).compareTo(proStartTime)<=0){
-                                                a = false;
-                                            }
-                                        }
-                                        if(a){
-                                            if(totalWidth.add(productionProcesses.getVolumeWidth()).compareTo(equipmentWidth)<0
-                                                    && totalBearing.add(productionProcesses.getSinglerollweight()).compareTo(equipmentBearing)<0){
-                                                hasMerge = true;
-                                                scoreDirector.beforeVariableChanged(process, "startTime");
-                                                productionProcesses.setStartTime(proStartTime);
-                                                scoreDirector.afterVariableChanged(process, "startTime");
-                                                if(productionProcesses.getNextProcesses() != null && productionProcesses.getNextProcesses().size()>0){
-                                                    for (ProductionProcesses nextProcess : productionProcesses.getNextProcesses()) {
-                                                        setNextAllStartTime(scoreDirector,nextProcess);
-                                                    }
-                                                }
-                                                totalWidth = totalWidth.add(productionProcesses.getVolumeWidth());
-                                                totalBearing = totalBearing.add(productionProcesses.getSinglerollweight());
-                                            }
-                                        }
-                                    }
-                                }
-                                if(!hasMerge){
-                                    proStartTime = equipmentRunTime.getEndRunTime().plusMinutes(1);
-                                }
+
+                        }
+
+                        // 退火工序合并处理
+                        // 开始时间一样
+                        /*if(equipmentRunTime.getStartRunTime().compareTo(proStartTime) == 0){
+                            boolean hasMergeTh = thMerge(process,equipmentRunTime);
+                            if(!hasMergeTh){
+                                proStartTime = equipmentRunTime.getEndRunTime().plusMinutes(60);
+                                proEndTime = proStartTime.plusMinutes(process.getProduceTime());
                             }
-                            // 待排程工序开始时间小于当前占用工序开始时间
-                            else if (equipmentRunTime.getStartRunTime().compareTo(proStartTime)>0) {
-                                boolean merge = isMerge(pe, equipmentRunTime, process);
-                                if(merge){
-                                    proStartTime = equipmentRunTime.getStartRunTime();
+                        }else{
+                            if(equipmentRunTime.getEndRunTime().compareTo(proStartTime)<=0 || equipmentRunTime.getStartRunTime().compareTo(proEndTime)>=0){
+                                continue;
+                            }else{
+                                if(proStartTime.compareTo(equipmentRunTime.getStartRunTime())<0){
+                                    boolean hasMergeTh = thMerge(process,equipmentRunTime);
+                                    if(!hasMergeTh){
+                                        proStartTime = equipmentRunTime.getEndRunTime().plusMinutes(60);
+                                        proEndTime = proStartTime.plusMinutes(process.getProduceTime());
+                                    }else {
+                                        proStartTime = equipmentRunTime.getStartRunTime();
+                                        proEndTime = proStartTime.plusMinutes(process.getProduceTime());
+                                    }
                                 }else{
-                                    proStartTime = equipmentRunTime.getEndRunTime().plusMinutes(1);
+                                    proStartTime = equipmentRunTime.getEndRunTime().plusMinutes(60);
+                                    proEndTime = proStartTime.plusMinutes(process.getProduceTime());
                                 }
                             }
-                            // 待排程工序开始时间等于当前占用工序开始时间
-                            else{
-                                boolean merge = isMerge(pe, equipmentRunTime, process);
-                                if(!merge){
-                                    proStartTime = equipmentRunTime.getEndRunTime().plusMinutes(1);
-                                }
-                            }*/
-
-                            /*if((equipmentRunTime.getStartRunTime().compareTo(proStartTime)<=0 && equipmentRunTime.getEndRunTime().compareTo(proEndTime)>=0)
-                                    || (equipmentRunTime.getStartRunTime().compareTo(proStartTime)>=0 && equipmentRunTime.getEndRunTime().compareTo(proEndTime)<=0)){
-                                boolean merge = isMerge(pe, equipmentRunTime, process);
-                                if(merge){
-                                    proStartTime = equipmentRunTime.getStartRunTime();
-                                }else{
-                                    proStartTime = equipmentRunTime.getEndRunTime().plusMinutes(1);
-                                }
-                            }else{
-                                proStartTime = equipmentRunTime.getEndRunTime().plusMinutes(1);
-                            }*/
-                        }
+                        }*/
                     }
                     // 冷轧工序
                     else if (process.getProcessType().equals("冷轧")) {
@@ -799,6 +753,92 @@ public class TaskStartTimeListener implements VariableListener<ApsSolution, Prod
         return toUpdateStartTime;
     }
 
+    private Boolean thMerge(ProductionProcesses process,EquipmentRunTime equipmentRunTime){
+        Boolean hasMergeTh = false;
+        if(process.getProcessType().equals(equipmentRunTime.getProcessType())){
+            if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退")){
+                if(process.getProcessType().equals(equipmentRunTime.getProcessType())
+                        && process.getVolumeMetal().equals(equipmentRunTime.getVolumeMetal())
+                        && process.getVolumeMetalstate().equals(equipmentRunTime.getVolumeMetalstate())){
+                    BigDecimal btVolumeWidth = null;
+                    BigDecimal btVolumeThickness = null;
+                    BigDecimal btSinglerollweight = null;
+                    if("成退".equals(process.getProcessType())){
+                        btVolumeWidth = new BigDecimal("50");
+                        btVolumeThickness = new BigDecimal("0.05");
+                        btSinglerollweight = new BigDecimal("1");
+                    }else if ("中退".equals(process.getProcessType())){
+                        btVolumeWidth = new BigDecimal("100");
+                        btVolumeThickness = new BigDecimal("0.08");
+                        btSinglerollweight = new BigDecimal("1");
+                    }
+                    if(equipmentRunTime.getMaxVolumeWidth().subtract(equipmentRunTime.getMinVolumeWidth()).compareTo(btVolumeWidth)<=0
+                            && equipmentRunTime.getMaxThickness().subtract(equipmentRunTime.getMinThickness()).compareTo(btVolumeThickness)<=0
+                            && equipmentRunTime.getMaxSinglerollweight().subtract(equipmentRunTime.getMinSinglerollweight()).compareTo(btSinglerollweight)<=0){
+                        hasMergeTh = true;
+                    }
+                }
+            }else{
+                // 小卷成退合并工序
+                String groupname = null;
+                for (ApsFurnaceInstallationDo furnaceInstallation : process.getApsOverallConfig().getFurnaceInstallations()) {
+                    if(process.getVolumeMetal().equals(furnaceInstallation.getAlloy())){
+                        boolean a = true;
+                        if(furnaceInstallation.getAlloystatus() != null && !furnaceInstallation.getAlloystatus().contains(process.getVolumeMetalstate())){
+                            a = false;
+                        }
+                        if(furnaceInstallation.getStartthickness() != null && furnaceInstallation.getStartthickness().compareTo(process.getVolumeThickness())>0){
+                            a = false;
+                        }
+                        if(furnaceInstallation.getEndthickness() != null && furnaceInstallation.getEndthickness().compareTo(process.getVolumeThickness())<0){
+                            a = false;
+                        }
+                        if(furnaceInstallation.getStartwidth() != null && new BigDecimal(furnaceInstallation.getStartwidth()).compareTo(process.getVolumeWidth())>0){
+                            a = false;
+                        }
+                        if(furnaceInstallation.getEndwidth() != null && new BigDecimal(furnaceInstallation.getEndwidth()).compareTo(process.getVolumeWidth())<0){
+                            a = false;
+                        }
+                        if(a){
+                            groupname = furnaceInstallation.getId();
+                            break;
+                        }
+                    }
+                }
+                if(groupname == null){
+                    groupname = "group-self-" + process.getVolumeMetal() + process.getVolumeMetalstate() + process.getProducttype() + process.getVolumeWidth() + process.getVolumeThickness();
+                }
+                Set<String> minThGroupNames = equipmentRunTime.getMinThGroupNames();
+                minThGroupNames.add(groupname);
+                BigDecimal totalWeight = process.getSinglerollweight().add(equipmentRunTime.getTotalSinglerollweight());
+                if(totalWeight.compareTo(process.getEquipment().getEquipmentParameter().getEquipmentBearing())<=0){
+                    if(minThGroupNames.size() == 1){
+                        hasMergeTh = true;
+                    }else if(minThGroupNames.size() > 1){
+                        boolean a = false;
+                        for (ApsMergeFurnaceDo mergeFurnace : process.getApsOverallConfig().getMergeFurnaces()) {
+                            boolean ab = true;
+                            for (String groupname1 : minThGroupNames) {
+                                if(!mergeFurnace.getCompatibilitygroup().contains(groupname1)){
+                                    ab = false;
+                                    break;
+                                }
+                            }
+                            if(ab){
+                                a = true;
+                                break;
+                            }
+                        }
+                        if(a){
+                            hasMergeTh = true;
+                        }
+                    }
+                }
+            }
+        }
+        return hasMergeTh;
+    }
+
     /**
      *
      * @param equipmentRunTime

+ 711 - 5
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/score/ApsConstraintProvider.java

@@ -9,6 +9,7 @@ import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
 import org.optaplanner.core.api.score.stream.*;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
@@ -34,6 +35,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
                 seriesProduceWashTimeWait(constraintFactory),
                 equipmentRunTime(constraintFactory),
                 eqTimeCrossTuihuo(constraintFactory),
+                eqTimeCrossMinTuihuo(constraintFactory),
 
                 deliveryDate(constraintFactory),
                 seriesProduce(constraintFactory),
@@ -41,9 +43,14 @@ public class ApsConstraintProvider implements ConstraintProvider {
                 mergeTuihuo(constraintFactory),
                 sameEquipment(constraintFactory),
 
+                processLzNear(constraintFactory),
+                processBtNear(constraintFactory),
+                seriesZzLb(constraintFactory),
+
+
 //                sameProcessSeries(constraintFactory),
                 processNear(constraintFactory),
-
+                eqTimeCrossMinTuihuoSoft(constraintFactory),
                 //                balancedEqUse(constraintFactory),
         };
     }
@@ -201,7 +208,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
     private Constraint equipmentRunTime(ConstraintFactory constraintFactory) {
         return constraintFactory.forEach(ProductionProcesses.class)
                 .filter(pro->{
-                    return !pro.getProcessType().equals("成退") && !pro.getProcessType().equals("中退");
+                    return !pro.getProcessType().equals("成退") && !pro.getProcessType().equals("中退") && !pro.getProcessType().equals("小卷成退");
                 })
                 .filter(productionProcesses -> {
                     boolean bol = false;
@@ -403,6 +410,163 @@ public class ApsConstraintProvider implements ConstraintProvider {
                 .asConstraint("seriesProduceTimeWait");
     }
 
+    /**
+     * 铸轧预留连续排产时间
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint seriesZzLb(ConstraintFactory constraintFactory) {
+        return constraintFactory.forEach(ProductionProcesses.class)
+                .filter((processe) -> {
+                    return "铸轧".equals(processe.getProcessType());
+                })
+                .groupBy(ProductionProcesses::getEquipmentId, ConstraintCollectors.toList())
+                .filter((equipmentId,processes) -> {
+                    if(processes != null && processes.size()>0){
+                        for (ProductionProcesses process : processes) {
+                            if(process.getConflictRoptions().containsKey("soft-seriesZzLb")){
+                                process.getConflictRoptions().remove("soft-seriesZzLb");
+                            }
+                        }
+                        return true;
+                    }
+                    /*int counNum = seriesProduceTimeWaitCount(processes);
+                    if(counNum>0){
+                        return true;
+                    }*/
+                    return false;
+                })
+                .penalize(HardMediumSoftScore.ONE_MEDIUM,(equipmentId,processes)->{
+                    int counNum = seriesZzLbCount(processes);
+                    return counNum*1000;
+                })
+                .asConstraint("seriesZzLb");
+    }
+
+    private int seriesZzLbCount(List<ProductionProcesses> processes){
+        int b = 0;
+        Equipment equipment = null;
+        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){
+            for (EquipmentRunTime equipmentRunTime : equipment.getEquipmentRunTimes()) {
+                ProductionProcesses pp = new ProductionProcesses();
+                pp.setStartTime(equipmentRunTime.getStartRunTime());
+                pp.setEndTime(equipmentRunTime.getEndRunTime());
+                pp.setSeriesProduceMark(equipmentRunTime.getSeriesProduceMark());
+                pp.setProcessType(equipmentRunTime.getProcessType());
+                pp.setPrepressworkmin(equipmentRunTime.getPrepressworkmin() == null ? 0 : equipmentRunTime.getPrepressworkmin());
+                pp.setCutfinishmin(equipmentRunTime.getCutfinishmin() == null ? 0 : equipmentRunTime.getCutfinishmin());
+                pp.setApsOverallConfig(hasStartTimeProcess.get(0).getApsOverallConfig());
+                pp.setConflictRoptions(new HashMap<>());
+                pp.setProduceTime(equipmentRunTime.getOnceprocessmin());
+                pp.setSinglerollweight(equipmentRunTime.getTotalSinglerollweight());
+                pp.setLastSerialLbWeight(equipmentRunTime.getLastSerialLbWeight());
+                hasStartTimeProcess.add(pp);
+            }
+        }
+        // 按照开始时间排序
+        Collections.sort(hasStartTimeProcess, Comparator.comparing(ProductionProcesses::getSeriSort));
+        // 获取设备已排程好的最后一个作业,计算连续加工
+        if(equipment.getLastProcessType() != null && equipment.getLastSeriesProduceMark() != null){
+            ProductionProcesses pp = new ProductionProcesses();
+            pp.setSeriesProduceMark(equipment.getLastSeriesProduceMark());
+            pp.setProcessType(equipment.getLastProcessType());
+            pp.setCutfinishmin(equipment.getLastProcessCutfinishmin() == null ? 0 : equipment.getLastProcessCutfinishmin());
+            pp.setEndTime(equipment.getLastProcessEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
+            pp.setApsOverallConfig(hasStartTimeProcess.get(0).getApsOverallConfig());
+            pp.setProduceTime(equipment.getOnceprocessmin());
+            pp.setConflictRoptions(new HashMap<>());
+            pp.setLastSerialLbWeight(equipment.getLastSerialLbWeight());
+            hasStartTimeProcess.add(0,pp);
+        }
+        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(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();
+                        }
+                    }else{
+                        zqlb = zqlb.add(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){
+                                b++;
+                                conflictRoptions1.put("soft-seriesZzLb","和下道工序没有预留足够的周期立板时间");
+                                conflictRoptions2.put("soft-seriesZzLb","和下道工序没有预留足够的周期立板时间");
+                                zqlb = new BigDecimal("0");
+                            }
+                        }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){
+                                        b++;
+                                        conflictRoptions1.put("soft-seriesZzLb","和下道工序没有预留足够的周期立板时间");
+                                        conflictRoptions2.put("soft-seriesZzLb","和下道工序没有预留足够的周期立板时间");
+                                        zqlb = new BigDecimal("0");
+                                    }
+                                }else if(i1.compareTo(i2)>0){
+                                    if(hasStartTimeProcess.get(i).getEndTime().plusMinutes(standingtime).plusMinutes(syLbTime).compareTo(hasStartTimeProcess.get(i+1).getStartTime())>0){
+                                        b++;
+                                        conflictRoptions1.put("soft-seriesZzLb","和下道工序没有预留足够的周期立板时间");
+                                        conflictRoptions2.put("soft-seriesZzLb","和下道工序没有预留足够的周期立板时间");
+                                        zqlb = new BigDecimal("0");
+                                    }
+                                }
+                            }catch (Exception e){
+                                e.printStackTrace();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return b;
+    }
+
     /**
      * 换辊和立板时间约束
      * @param processes
@@ -427,6 +591,9 @@ public class ApsConstraintProvider implements ConstraintProvider {
                 pp.setCutfinishmin(equipmentRunTime.getCutfinishmin() == null ? 0 : equipmentRunTime.getCutfinishmin());
                 pp.setApsOverallConfig(hasStartTimeProcess.get(0).getApsOverallConfig());
                 pp.setConflictRoptions(new HashMap<>());
+                pp.setProduceTime(equipmentRunTime.getOnceprocessmin());
+                pp.setSinglerollweight(equipmentRunTime.getTotalSinglerollweight());
+                pp.setLastSerialLbWeight(equipmentRunTime.getLastSerialLbWeight());
                 hasStartTimeProcess.add(pp);
             }
         }
@@ -440,7 +607,9 @@ public class ApsConstraintProvider implements ConstraintProvider {
             pp.setCutfinishmin(equipment.getLastProcessCutfinishmin() == null ? 0 : equipment.getLastProcessCutfinishmin());
             pp.setEndTime(equipment.getLastProcessEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
             pp.setApsOverallConfig(hasStartTimeProcess.get(0).getApsOverallConfig());
+            pp.setProduceTime(equipment.getOnceprocessmin());
             pp.setConflictRoptions(new HashMap<>());
+            pp.setLastSerialLbWeight(equipment.getLastSerialLbWeight());
             hasStartTimeProcess.add(0,pp);
         }
         for(int i=0;i<hasStartTimeProcess.size()-1;i++){
@@ -453,6 +622,8 @@ public class ApsConstraintProvider implements ConstraintProvider {
             Map<String, String> conflictRoptions2 = nextPro.getConflictRoptions();
             if(hasStartTimeProcess.get(i).getSeriesProduceMark() != null && hasStartTimeProcess.get(i+1).getSeriesProduceMark() != null){
                 if("铸轧".equals(hasStartTimeProcess.get(i).getProcessType())){
+                    // 周期立板已连续生产吨数
+                    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("\\^_\\^");
@@ -460,11 +631,24 @@ public class ApsConstraintProvider implements ConstraintProvider {
                         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();
+                            }
+                        }else{
+                            zqlb = zqlb.add(hasStartTimeProcess.get(i).getSinglerollweight());
+                        }
                         if(serspre.length == 5 && sersafter.length == 5){
                             // 合金不同则需要换辊和立板
                             if(!serspre[0].equals(sersafter[0]) || !serspre[1].equals(sersafter[1])){
@@ -472,6 +656,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
                                     b++;
                                     conflictRoptions1.put("hard-seriesProduceTimeWait","和下道工序没有预留足够的换辊和立板时间");
                                     conflictRoptions2.put("hard-seriesProduceTimeWait","和上道工序没有预留足够的换辊和立板时间");
+                                    zqlb = new BigDecimal("0");
                                 }
                             }else{
                                 // 合金相同情况下后面的宽度大于前面的宽度需要换辊和立板
@@ -487,12 +672,24 @@ public class ApsConstraintProvider implements ConstraintProvider {
                                             b++;
                                             conflictRoptions1.put("hard-seriesProduceTimeWait","和下道工序没有预留足够的换辊和立板时间");
                                             conflictRoptions2.put("hard-seriesProduceTimeWait","和上道工序没有预留足够的换辊和立板时间");
+                                            zqlb = new BigDecimal("0");
                                         }
                                     }else if(i1.compareTo(i2)>0){
                                         if(hasStartTimeProcess.get(i).getEndTime().plusMinutes(standingtime).compareTo(hasStartTimeProcess.get(i+1).getStartTime())>0){
                                             b++;
                                             conflictRoptions1.put("hard-seriesProduceTimeWait","和下道工序没有预留足够的立板时间");
                                             conflictRoptions2.put("hard-seriesProduceTimeWait","和上道工序没有预留足够的立板时间");
+                                            zqlb = new BigDecimal("0");
+                                        }
+                                    }else{
+                                        // 是否需要周期立板
+                                        if(zqlb.add(hasStartTimeProcess.get(i+1).getSinglerollweight()).compareTo(standingyieldbig)>0){
+                                            if(hasStartTimeProcess.get(i).getEndTime().plusMinutes(standingtime).compareTo(hasStartTimeProcess.get(i+1).getStartTime())>0){
+                                                b++;
+                                                conflictRoptions1.put("hard-seriesProduceTimeWait","和下道工序没有预留足够的立板时间");
+                                                conflictRoptions2.put("hard-seriesProduceTimeWait","和上道工序没有预留足够的立板时间");
+                                                zqlb = new BigDecimal("0");
+                                            }
                                         }
                                     }
                                 }catch (Exception e){
@@ -545,7 +742,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
     private Constraint eqTimeCross(ConstraintFactory constraintFactory) {
         return constraintFactory.forEach(ProductionProcesses.class)
                 .filter(pro->{
-                    return !pro.getProcessType().equals("成退") && !pro.getProcessType().equals("中退");
+                    return !pro.getProcessType().equals("成退") && !pro.getProcessType().equals("中退") && !pro.getProcessType().equals("小卷成退");
                 })
                 .join(ProductionProcesses.class,Joiners.equal(ProductionProcesses::getEquipmentId))
                 .filter((proc1,proc2)->{
@@ -568,6 +765,221 @@ public class ApsConstraintProvider implements ConstraintProvider {
                 .asConstraint("eqTimeCross");
     }
 
+    /**
+     * 硬约束:小卷成退合并约束
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint eqTimeCrossMinTuihuo(ConstraintFactory constraintFactory) {
+        return constraintFactory.forEach(ProductionProcesses.class)
+                .filter(pro->{
+                    return pro.getProcessType().equals("小卷成退");
+                })
+                .groupBy(ProductionProcesses::getEquipmentId,ConstraintCollectors.toList())
+                .filter((equipmentId,processes) -> {
+                    if(processes != null && processes.size()>0){
+                        for (ProductionProcesses process : processes) {
+                            if(process.getConflictRoptions().containsKey("hard-eqTimeCrossMinTuihuo")){
+                                process.getConflictRoptions().remove("hard-eqTimeCrossMinTuihuo");
+                            }
+                        }
+                        /*Integer num = eqTimeCrossMinTuihuoCount(processes,"1");
+                        if(num>0){
+                            return true;
+                        }*/
+                        return true;
+                    }else{
+                        return false;
+                    }
+                })
+                .penalize(HardMediumSoftScore.ONE_HARD,(equipmentId,processes)->{
+                    Integer num = eqTimeCrossMinTuihuoCount(processes,"2");
+                    return num*100;
+                })
+                .asConstraint("eqTimeCrossMinTuihuo");
+    }
+
+    /**
+     * 退火工序冲突计算
+     * @param processes
+     * @param type(1:是否存在冲突,2:冲突得分计算)
+     * @return
+     */
+    private Integer eqTimeCrossMinTuihuoCount(List<ProductionProcesses> processes,String type){
+        if(processes.size()>3){
+            int a = 1;
+        }
+        if(processes.size()>1){
+            int a = 1;
+        }
+        int b = 0;
+        Equipment equipment = null;
+        if(processes != null && processes.size()>0){
+            equipment = processes.get(0).getEquipment();
+        }
+        // 设备承重
+        BigDecimal equipmentBearing = equipment.getEquipmentParameter().getEquipmentBearing();
+        List<ProductionProcesses> hasStartTimeProcess = processes.stream().filter(v -> v.getStartTime() != null).collect(Collectors.toList());
+        // 设备占用时间参与连续生产排程
+        if(equipment != null && equipment.getEquipmentRunTimes() != null && equipment.getEquipmentRunTimes().size()>0){
+            for (EquipmentRunTime equipmentRunTime : equipment.getEquipmentRunTimes()) {
+                ProductionProcesses pp = new ProductionProcesses();
+                pp.setStartTime(equipmentRunTime.getStartRunTime());
+                pp.setEquipment(equipment);
+                pp.setEndTime(equipmentRunTime.getEndRunTime());
+                pp.setSeriesProduceMark(equipmentRunTime.getSeriesProduceMark());
+                pp.setProcessType(equipmentRunTime.getProcessType());
+                pp.setVolumeMetal(equipmentRunTime.getVolumeMetal());
+                pp.setVolumeMetalstate(equipmentRunTime.getVolumeMetalstate());
+                pp.setBsProcessesId(Arrays.asList(new String[]{"haspcprocess"}));
+                pp.setVolumeWidth(equipmentRunTime.getTotalVolumeWidth());
+                pp.setSinglerollweight(equipmentRunTime.getTotalSinglerollweight());
+                pp.setVolumeThickness(equipmentRunTime.getTotalThickness());
+                pp.setConflictRoptions(new HashMap<>());
+                if(equipmentRunTime.getOccupyType().equals("process")){
+                    pp.setTaskType("processes");
+                }else {
+                    pp.setTaskType("maintenance");
+                }
+                hasStartTimeProcess.add(pp);
+            }
+        }
+        // 按照开始时间排序
+        Collections.sort(hasStartTimeProcess, Comparator.comparing(ProductionProcesses::getSeriSort));
+        // 退火合并工序
+        Map<String,List<ProductionProcesses>> ppMap = new HashMap<>();
+        for(int i=0;i<hasStartTimeProcess.size()-1;i++){
+            ProductionProcesses prePro = hasStartTimeProcess.get(i);
+            ProductionProcesses nextPro = hasStartTimeProcess.get(i+1);
+            Map<String, String> conflictRoptions1 = prePro.getConflictRoptions();
+            Map<String, String> conflictRoptions2 = nextPro.getConflictRoptions();
+            // 开始时间相等为合并工序
+            if (prePro.getStartTime().compareTo(nextPro.getStartTime()) == 0){
+                if(prePro.getTaskType().equals("maintenance") || nextPro.getTaskType().equals("maintenance")){
+                    if("2".equals(type)){
+//                        System.out.println("1:作业加工时间有交叉");
+                        conflictRoptions1.put("hard-eqTimeCrossMinTuihuo","作业加工时间有交叉");
+                        conflictRoptions2.put("hard-eqTimeCrossMinTuihuo","作业加工时间有交叉");
+                    }
+                    b++;
+                }else{
+                    List<ProduceOrder> produceOrder1 = prePro.getProduceOrder();
+                    List<ProduceOrder> produceOrder2 = nextPro.getProduceOrder();
+                    if(produceOrder1 != null && produceOrder1.size()>0 && produceOrder2 != null && produceOrder2.size()>0
+                            && produceOrder1.get(0).getId().equals(produceOrder2.get(0).getId())
+                            && !prePro.getUniqueBsProcessesId().equals(nextPro.getUniqueBsProcessesId())){
+                        conflictRoptions1.put("hard-eqTimeCrossMinTuihuo","同一坯料计划不同工序退火不能排一起");
+                        conflictRoptions2.put("hard-eqTimeCrossMinTuihuo","同一坯料计划不同工序退火不能排一起");
+                        b++;
+                    }else{
+                        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+                        String startTimeKey = prePro.getStartTime().format(formatter);
+                        if(!ppMap.containsKey(startTimeKey)){
+                            List<ProductionProcesses> pps = new ArrayList<>();
+                            ppMap.put(startTimeKey,pps);
+                        }
+                        List<ProductionProcesses> processesList = ppMap.get(startTimeKey);
+                        List<ProductionProcesses> preHas = processesList.stream().filter(v -> v.getId().equals(prePro.getId())).collect(Collectors.toList());
+                        List<ProductionProcesses> nextHas = processesList.stream().filter(v -> v.getId().equals(nextPro.getId())).collect(Collectors.toList());
+                        if(preHas == null || preHas.size() == 0){
+                            processesList.add(prePro);
+                        }
+                        if(nextHas == null || nextHas.size() == 0){
+                            processesList.add(nextPro);
+                        }
+                    }
+                }
+            }else{
+                if(prePro.getEndTime().compareTo(nextPro.getStartTime()) > 0){
+                    if("2".equals(type)){
+//                        System.out.println("1:作业加工时间有交叉");
+                        conflictRoptions1.put("hard-eqTimeCrossMinTuihuo","作业加工时间有交叉");
+                        conflictRoptions2.put("hard-eqTimeCrossMinTuihuo","作业加工时间有交叉");
+                    }
+                    b++;
+                }
+            }
+        }
+
+        List<ApsFurnaceInstallationDo> furnaceInstallations = processes.get(0).getApsOverallConfig().getFurnaceInstallations();
+        List<ApsMergeFurnaceDo> mergeFurnaces = processes.get(0).getApsOverallConfig().getMergeFurnaces();
+
+        if(ppMap.size()>1){
+            int a = 1;
+        }
+        //  检查合并工序是否超过容量
+        for (Map.Entry<String,List<ProductionProcesses>> entry: ppMap.entrySet()) {
+            List<ProductionProcesses> v = entry.getValue();
+            Set<String> groupnames = new HashSet<>();
+            BigDecimal totalWeight = new BigDecimal(0);
+
+            boolean mergeRule = true;
+            for (ProductionProcesses productionProcesses : v) {
+                totalWeight = totalWeight.add(productionProcesses.getSinglerollweight());
+                // 检查小卷退火在哪个组
+                String groupname = null;
+                for (ApsFurnaceInstallationDo furnaceInstallation : furnaceInstallations) {
+                    if(productionProcesses.getVolumeMetal().equals(furnaceInstallation.getAlloy())){
+                        boolean a = true;
+                        if(furnaceInstallation.getAlloystatus() != null && !furnaceInstallation.getAlloystatus().contains(productionProcesses.getVolumeMetalstate())){
+                            a = false;
+                        }
+                        if(furnaceInstallation.getStartthickness() != null && furnaceInstallation.getStartthickness().compareTo(productionProcesses.getVolumeThickness())>0){
+                            a = false;
+                        }
+                        if(furnaceInstallation.getEndthickness() != null && furnaceInstallation.getEndthickness().compareTo(productionProcesses.getVolumeThickness())<0){
+                            a = false;
+                        }
+                        if(furnaceInstallation.getStartwidth() != null && new BigDecimal(furnaceInstallation.getStartwidth()).compareTo(productionProcesses.getVolumeWidth())>0){
+                            a = false;
+                        }
+                        if(furnaceInstallation.getEndwidth() != null && new BigDecimal(furnaceInstallation.getEndwidth()).compareTo(productionProcesses.getVolumeWidth())<0){
+                            a = false;
+                        }
+                        if(a){
+                            groupname = furnaceInstallation.getId();
+                            break;
+                        }
+                    }
+                }
+                if(groupname == null){
+                    groupname = "group-self-" + productionProcesses.getVolumeMetal() + productionProcesses.getVolumeMetalstate() + productionProcesses.getProducttype() + productionProcesses.getVolumeWidth() + productionProcesses.getVolumeThickness();
+                }
+                groupnames.add(groupname);
+            }
+            if(totalWeight.compareTo(equipmentBearing)>0){
+                b++;
+                mergeRule = false;
+            }
+            if(groupnames.size()>1){
+                boolean a = false;
+                for (ApsMergeFurnaceDo mergeFurnace : mergeFurnaces) {
+                    boolean ab = true;
+                    for (String groupname : groupnames) {
+                        if(!mergeFurnace.getCompatibilitygroup().contains(groupname)){
+                            ab = false;
+                            break;
+                        }
+                    }
+                    if(ab){
+                        a = true;
+                        break;
+                    }
+                }
+                if(!a){
+                    b++;
+                    mergeRule = false;
+                }
+            }
+            if(!mergeRule){
+                for (ProductionProcesses productionProcesses : v) {
+                    productionProcesses.getConflictRoptions().put("hard-eqTimeCrossMinTuihuo","不符合小卷退火合并规则");
+                }
+            }
+        }
+        return b;
+    }
+
     /**
      * 硬约束:同一个退火炉设备的不同的工步运行时间约束
      * @param constraintFactory
@@ -609,6 +1021,12 @@ public class ApsConstraintProvider implements ConstraintProvider {
      * @return
      */
     private Integer eqTimeCrossTuihuoCount(List<ProductionProcesses> processes,String type){
+        if(processes.size()>3){
+            int a = 1;
+        }
+        if(processes.size()>1){
+            int a = 1;
+        }
         int b = 0;
         Equipment equipment = null;
         if(processes != null && processes.size()>0){
@@ -698,6 +1116,9 @@ public class ApsConstraintProvider implements ConstraintProvider {
         //  检查合并工序是否超过容量
         for (Map.Entry<String,List<ProductionProcesses>> entry: ppMap.entrySet()) {
             List<ProductionProcesses> v = entry.getValue();
+            if(v.size()>3){
+                int a = 1;
+            }
             BigDecimal totalWidth = new BigDecimal("0");
             BigDecimal totalWeight = new BigDecimal("0");
             Set<String> metalSet = new HashSet<>();
@@ -1355,6 +1776,121 @@ public class ApsConstraintProvider implements ConstraintProvider {
                 .asConstraint("processNear");
     }
 
+    /**
+     * 排程时间尽量靠前
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint processBtNear(ConstraintFactory constraintFactory){
+        return constraintFactory.forEach(ProductionProcesses.class)
+                .filter((process)->{
+                    return "冷轧".equals(process.getProcessType()) || "铸轧".equals(process.getProcessType());
+                })
+                .groupBy(ProductionProcesses::getEquipmentId,ConstraintCollectors.toList())
+                .filter((equipmentId,processs)->{
+                    if(processs != null && processs.size()>0){
+                        return true;
+                    }
+                    return false;
+                })
+                .penalize(HardMediumSoftScore.ONE_MEDIUM,(equipmentId,processs) -> {
+                    int b = 0;
+                    Equipment equipment = null;
+                    if(processs != null && processs.size()>0){
+                        equipment = processs.get(0).getEquipment();
+                    }
+                    List<ProductionProcesses> hasStartTimeProcess = processs.stream().filter(v -> v.getStartTime() != null).collect(Collectors.toList());
+                    // 设备占用时间参与连续生产排程
+                    if(equipment != null && equipment.getEquipmentRunTimes() != null && equipment.getEquipmentRunTimes().size()>0){
+                        for (EquipmentRunTime equipmentRunTime : equipment.getEquipmentRunTimes()) {
+                            if(equipmentRunTime.getOccupyType().equals("process")){
+                                ProductionProcesses pp = new ProductionProcesses();
+                                pp.setStartTime(equipmentRunTime.getStartRunTime());
+                                pp.setEndTime(equipmentRunTime.getEndRunTime());
+                                pp.setSeriesProduceMark(equipmentRunTime.getSeriesProduceMark());
+                                pp.setProcessType(equipmentRunTime.getProcessType());
+                                pp.setBsProcessesId(Arrays.asList(new String[]{"haspcprocess"}));
+                                pp.setConflictRoptions(new HashMap<>());
+                                hasStartTimeProcess.add(pp);
+                            }
+                        }
+                    }
+                    // 按照开始时间排序
+                    Collections.sort(hasStartTimeProcess, Comparator.comparing(ProductionProcesses::getSeriSort));
+                    // 获取设备已排程好的最后一个作业,计算连续加工
+                    if(equipment.getLastProcessType() != null && equipment.getLastSeriesProduceMark() != null){
+                        ProductionProcesses pp = new ProductionProcesses();
+                        pp.setSeriesProduceMark(equipment.getLastSeriesProduceMark());
+                        pp.setProcessType(equipment.getLastProcessType());
+                        pp.setBsProcessesId(Arrays.asList(new String[]{"lastprocess"}));
+                        pp.setConflictRoptions(new HashMap<>());
+                        pp.setEndTime(equipment.getLastProcessEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
+                        hasStartTimeProcess.add(0,pp);
+                    }
+                    for(int i=0;i<hasStartTimeProcess.size()-1;i++){
+                        if(hasStartTimeProcess.get(i).getId() == null && hasStartTimeProcess.get(i+1).getId() == null){
+                            continue;
+                        }
+                        if("铸轧".equals(hasStartTimeProcess.get(i).getProcessType())){
+                            if(hasStartTimeProcess.get(i+1).getStartTime().compareTo(hasStartTimeProcess.get(i).getEndTime().plusHours(10))>0){
+                                b++;
+                            }
+                        } else if ("冷轧".equals(hasStartTimeProcess.get(i).getProcessType())) {
+                            if(hasStartTimeProcess.get(i+1).getStartTime().compareTo(hasStartTimeProcess.get(i).getEndTime().plusHours(10))>0){
+                                b++;
+                            }
+                        }
+                    }
+                    return b*300;
+                })
+                .asConstraint("processBtNear");
+    }
+
+    /**
+     * 排程时间尽量靠前
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint processLzNear(ConstraintFactory constraintFactory){
+        return constraintFactory.forEach(ProductionProcesses.class)
+                .filter((process)->{
+                    return "冷轧".equals(process.getProcessType()) || "铸轧".equals(process.getProcessType());
+                })
+                .filter((process)->{
+                    if(process.getPreviousProcesses() == null || process.getPreviousProcesses().size() == 0){
+                        if(process.getStartTime().compareTo(process.getApsOverallConfig().getStartTime())>0){
+                            return true;
+                        }else{
+                            return false;
+                        }
+                    }else{
+                        ProductionProcesses preProcess = process.getPreviousProcesses().get(0);
+                        // 流转时间
+                        Integer lzTimes = 0;
+                        if(preProcess != null && preProcess.getEquipment() != null){
+                            if(preProcess.getEquipment().getWorkshopid() != null && preProcess.getEquipment().getWorkshopid().equals(process.getEquipment().getWorkshopid())){
+                                lzTimes = process.getApsOverallConfig().getRoamTime().get("WORKSHOP_IN");
+                            }else{
+                                lzTimes = process.getApsOverallConfig().getRoamTime().get("WORKSHOP_CROSS");
+                            }
+                            // 最小等待时间对比流转时间
+                            if(preProcess.getMinWaitTime() != null && lzTimes<preProcess.getMinWaitTime()){
+                                lzTimes = preProcess.getMinWaitTime();
+                            }
+                            LocalDateTime minStartTime = preProcess.getEndTime().plusMinutes(lzTimes);
+                            if(process.getStartTime().compareTo(minStartTime.plusHours(48))>0){
+                                return true;
+                            }
+                        }
+                        return false;
+                    }
+                })
+                .penalize(HardMediumSoftScore.ONE_MEDIUM,(process) -> {
+                    return 300;
+                })
+                .asConstraint("processLzNear");
+    }
+
     /**
      * 退火工序组炉规则
      * @param constraintFactory
@@ -1363,7 +1899,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
     private Constraint mergeTuihuo(ConstraintFactory constraintFactory){
         return constraintFactory.forEach(ProductionProcesses.class)
                 .filter(pro->{
-                    return pro.getProcessType().equals("成退") || pro.getProcessType().equals("中退");
+                    return pro.getProcessType().equals("成退") || pro.getProcessType().equals("中退") || pro.getProcessType().equals("小卷成退");
                 })
                 .groupBy(ProductionProcesses::getEquipmentId,ConstraintCollectors.toList())
                 .filter((equipmentId,processes) -> {
@@ -1397,7 +1933,7 @@ public class ApsConstraintProvider implements ConstraintProvider {
                             }
                         }
                     }
-                    return ppMap.size()*100;
+                    return ppMap.size()*10000;
                 })
                 .asConstraint("mergeTuihuo");
     }
@@ -1438,4 +1974,174 @@ public class ApsConstraintProvider implements ConstraintProvider {
                 })
                 .asConstraint("sameEquipment");
     }
+
+    /**
+     * 硬约束:小卷成退合并约束
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint eqTimeCrossMinTuihuoSoft(ConstraintFactory constraintFactory) {
+        return constraintFactory.forEach(ProductionProcesses.class)
+                .filter(pro->{
+                    return pro.getProcessType().equals("小卷成退");
+                })
+                .groupBy(ProductionProcesses::getEquipmentId,ConstraintCollectors.toList())
+                .filter((equipmentId,processes) -> {
+                    if(processes != null && processes.size()>0){
+                        return true;
+                    }else{
+                        return false;
+                    }
+                })
+                .penalize(HardMediumSoftScore.ONE_SOFT,(equipmentId,processes)->{
+                    Integer num = eqTimeCrossMinTuihuoCountSoft(processes,"2");
+                    return num*100;
+                })
+                .asConstraint("eqTimeCrossMinTuihuoSoft");
+    }
+
+    /**
+     * 退火工序冲突计算
+     * @param processes
+     * @param type(1:是否存在冲突,2:冲突得分计算)
+     * @return
+     */
+    private Integer eqTimeCrossMinTuihuoCountSoft(List<ProductionProcesses> processes,String type){
+        int b = 0;
+        Equipment equipment = null;
+        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){
+            for (EquipmentRunTime equipmentRunTime : equipment.getEquipmentRunTimes()) {
+                ProductionProcesses pp = new ProductionProcesses();
+                pp.setStartTime(equipmentRunTime.getStartRunTime());
+                pp.setEquipment(equipment);
+                pp.setEndTime(equipmentRunTime.getEndRunTime());
+                pp.setSeriesProduceMark(equipmentRunTime.getSeriesProduceMark());
+                pp.setProcessType(equipmentRunTime.getProcessType());
+                pp.setVolumeMetal(equipmentRunTime.getVolumeMetal());
+                pp.setVolumeMetalstate(equipmentRunTime.getVolumeMetalstate());
+                pp.setBsProcessesId(Arrays.asList(new String[]{"haspcprocess"}));
+                pp.setVolumeWidth(equipmentRunTime.getTotalVolumeWidth());
+                pp.setSinglerollweight(equipmentRunTime.getTotalSinglerollweight());
+                pp.setVolumeThickness(equipmentRunTime.getTotalThickness());
+                pp.setConflictRoptions(new HashMap<>());
+                if(equipmentRunTime.getOccupyType().equals("process")){
+                    pp.setTaskType("processes");
+                }else {
+                    pp.setTaskType("maintenance");
+                }
+                hasStartTimeProcess.add(pp);
+            }
+        }
+        // 按照开始时间排序
+        Collections.sort(hasStartTimeProcess, Comparator.comparing(ProductionProcesses::getSeriSort));
+        // 退火合并工序
+        Map<String,List<ProductionProcesses>> ppMap = new HashMap<>();
+        for(int i=0;i<hasStartTimeProcess.size()-1;i++){
+            ProductionProcesses prePro = hasStartTimeProcess.get(i);
+            ProductionProcesses nextPro = hasStartTimeProcess.get(i+1);
+            Map<String, String> conflictRoptions1 = prePro.getConflictRoptions();
+            Map<String, String> conflictRoptions2 = nextPro.getConflictRoptions();
+            // 开始时间相等为合并工序
+            if (prePro.getStartTime().compareTo(nextPro.getStartTime()) == 0){
+                if(prePro.getTaskType().equals("maintenance") || nextPro.getTaskType().equals("maintenance")){
+                    b = b+100;
+                }else{
+                    List<ProduceOrder> produceOrder1 = prePro.getProduceOrder();
+                    List<ProduceOrder> produceOrder2 = nextPro.getProduceOrder();
+                    if(produceOrder1 != null && produceOrder1.size()>0 && produceOrder2 != null && produceOrder2.size()>0
+                            && produceOrder1.get(0).getId().equals(produceOrder2.get(0).getId())
+                            && !prePro.getUniqueBsProcessesId().equals(nextPro.getUniqueBsProcessesId())){
+                        b = b+100;
+                    }else{
+                        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+                        String startTimeKey = prePro.getStartTime().format(formatter);
+                        if(!ppMap.containsKey(startTimeKey)){
+                            List<ProductionProcesses> pps = new ArrayList<>();
+                            ppMap.put(startTimeKey,pps);
+                        }
+                        List<ProductionProcesses> processesList = ppMap.get(startTimeKey);
+                        List<ProductionProcesses> preHas = processesList.stream().filter(v -> v.getId().equals(prePro.getId())).collect(Collectors.toList());
+                        List<ProductionProcesses> nextHas = processesList.stream().filter(v -> v.getId().equals(nextPro.getId())).collect(Collectors.toList());
+                        if(preHas == null || preHas.size() == 0){
+                            processesList.add(prePro);
+                        }
+                        if(nextHas == null || nextHas.size() == 0){
+                            processesList.add(nextPro);
+                        }
+                    }
+                }
+            }else{
+                if(prePro.getEndTime().compareTo(nextPro.getStartTime()) > 0){
+                    b = b+100;
+                }
+            }
+        }
+
+        List<ApsFurnaceInstallationDo> furnaceInstallations = processes.get(0).getApsOverallConfig().getFurnaceInstallations();
+        List<ApsMergeFurnaceDo> mergeFurnaces = processes.get(0).getApsOverallConfig().getMergeFurnaces();
+
+        //  检查合并工序是否超过容量
+        for (Map.Entry<String,List<ProductionProcesses>> entry: ppMap.entrySet()) {
+            List<ProductionProcesses> v = entry.getValue();
+            Set<String> groupnames = new HashSet<>();
+            for (ProductionProcesses productionProcesses : v) {
+                // 检查小卷退火在哪个组
+                String groupname = null;
+                for (ApsFurnaceInstallationDo furnaceInstallation : furnaceInstallations) {
+                    if(productionProcesses.getVolumeMetal().equals(furnaceInstallation.getAlloy())){
+                        boolean a = true;
+                        if(furnaceInstallation.getAlloystatus() != null && !furnaceInstallation.getAlloystatus().contains(productionProcesses.getVolumeMetalstate())){
+                            a = false;
+                        }
+                        if(furnaceInstallation.getStartthickness() != null && furnaceInstallation.getStartthickness().compareTo(productionProcesses.getVolumeThickness())>0){
+                            a = false;
+                        }
+                        if(furnaceInstallation.getEndthickness() != null && furnaceInstallation.getEndthickness().compareTo(productionProcesses.getVolumeThickness())<0){
+                            a = false;
+                        }
+                        if(furnaceInstallation.getStartwidth() != null && new BigDecimal(furnaceInstallation.getStartwidth()).compareTo(productionProcesses.getVolumeWidth())>0){
+                            a = false;
+                        }
+                        if(furnaceInstallation.getEndwidth() != null && new BigDecimal(furnaceInstallation.getEndwidth()).compareTo(productionProcesses.getVolumeWidth())<0){
+                            a = false;
+                        }
+                        if(a){
+                            groupname = furnaceInstallation.getId();
+                            break;
+                        }
+                    }
+                }
+                if(groupname == null){
+                    groupname = "group-self";
+                }
+                groupnames.add(groupname);
+            }
+            if(groupnames.size()>1){
+                b = b+10;
+                boolean a = false;
+                for (ApsMergeFurnaceDo mergeFurnace : mergeFurnaces) {
+                    boolean ab = true;
+                    for (String groupname : groupnames) {
+                        if(!mergeFurnace.getCompatibilitygroup().contains(groupname)){
+                            ab = false;
+                            break;
+                        }
+                    }
+                    if(ab){
+                        a = true;
+                        break;
+                    }
+                }
+                if(!a){
+                    b = b+100;
+                }
+            }
+        }
+        return b;
+    }
 }

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

@@ -43,7 +43,7 @@ public class ApsServiceImpl implements ApsService {
         ApsSolution apsSolutionTuihuo = new ApsSolution();
         List<ProductionProcesses> tuihuos = new ArrayList<>();
         for (ProductionProcesses process : apsSolution.getProcessesList()) {
-            if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退")){
+            if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退") || process.getProcessType().equals("小卷成退")){
                 List<Integer> preTimes = new ArrayList<>();
                 getAllPreTime(process,preTimes);
                 if(preTimes != null && preTimes.size()>0){

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

@@ -126,6 +126,9 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
                 }
             }
         }
+        if(notLocks.size() == 0){
+            throw new ApsException("没有可排程的工序");
+        }
         apsSolution.setProcessesList(notLocks);
         ApsSolution solvedBalance = solver.solve(apsSolution);
         log.info("**************排程评分分析***************");
@@ -213,7 +216,7 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
         List<ProductionProcesses> previousProcesses = process.getPreviousProcesses();
         if(previousProcesses != null){
             ProductionProcesses preProcess = previousProcesses.get(0);
-            if(!preProcess.getProcessType().equals("成退") && !preProcess.getProcessType().equals("中退")){
+            if(!preProcess.getProcessType().equals("成退") && !preProcess.getProcessType().equals("中退") && !preProcess.getProcessType().equals("小卷成退")){
                 if(process.getId().equals("2294c17bb20549d28cbe9af149669988") || process.getId().equals("e23ea38146fb4f7bbb4a042c9a84dcba")){
                     int a = 0;
                 }
@@ -615,7 +618,10 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
         apsOverallConfig.setWashingtime(productionScheduleVo.getWashingtime());
         apsOverallConfig.setStandingtime(productionScheduleVo.getStandingtime());
         apsOverallConfig.setClosealloynames(productionScheduleVo.getClosealloynames());
+        apsOverallConfig.setFurnaceInstallations(productionScheduleVo.getFurnaceInstallations());
+        apsOverallConfig.setMergeFurnaces(productionScheduleVo.getMergeFurnaces());
         apsOverallConfig.setLooseness(productionScheduleVo.getLooseness());
+        apsOverallConfig.setStandingyield(productionScheduleVo.getStandingyield());
         unsolvedCloudBalance.setStartTime(startTime);
 
         // 根据前道、后道工序ID,转换为前道、后道工序对象
@@ -716,32 +722,87 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
             List<EquipmentRunTime> equipmentRunTimesMerge = new ArrayList<>();
             List<EquipmentRunTime> equipmentRunTimes = equipment.getEquipmentRunTimes();
             if(equipmentRunTimes != null && equipmentRunTimes.size()>0){
-                for (EquipmentRunTime equipmentRunTime : equipmentRunTimes) {
-                    if("maintenance".equals(equipmentRunTime.getOccupyType())){
-                        equipmentRunTimesMerge.add(equipmentRunTime);
-                    }else{
-                        boolean isMerged = false;
-                        for (EquipmentRunTime runTime : equipmentRunTimesMerge) {
-                            if((runTime.getStartRunTime().compareTo(equipmentRunTime.getStartRunTime())<=0 && runTime.getEndRunTime().compareTo(equipmentRunTime.getEndRunTime())>=0)
-                                    || (runTime.getStartRunTime().compareTo(equipmentRunTime.getStartRunTime())>=0 && runTime.getEndRunTime().compareTo(equipmentRunTime.getEndRunTime())<=0)){
-                                runTime.setTotalVolumeWidth(runTime.getTotalVolumeWidth().add(equipmentRunTime.getTotalVolumeWidth()));
-                                runTime.setTotalSinglerollweight(runTime.getTotalSinglerollweight().add(equipmentRunTime.getTotalSinglerollweight()));
-                                if(equipmentRunTime.getStartRunTime().compareTo(runTime.getStartRunTime())<0){
-                                    runTime.setStartRunTime(equipmentRunTime.getStartRunTime());
+                if("成退,中退,小卷成退".contains(equipmentRunTimes.get(0).getProcessType())){
+                    for (EquipmentRunTime equipmentRunTime : equipmentRunTimes) {
+                        if("maintenance".equals(equipmentRunTime.getOccupyType())){
+                            equipmentRunTimesMerge.add(equipmentRunTime);
+                        }else{
+                            boolean isMerged = false;
+                            String groupname = null;
+                            for (ApsFurnaceInstallationDo furnaceInstallation : productionScheduleVo.getFurnaceInstallations()) {
+                                if(equipmentRunTime.getVolumeMetal().equals(furnaceInstallation.getAlloy())){
+                                    boolean a = true;
+                                    if(furnaceInstallation.getAlloystatus() != null && !furnaceInstallation.getAlloystatus().contains(equipmentRunTime.getVolumeMetalstate())){
+                                        a = false;
+                                    }
+                                    if(furnaceInstallation.getStartthickness() != null && furnaceInstallation.getStartthickness().compareTo(equipmentRunTime.getTotalThickness())>0){
+                                        a = false;
+                                    }
+                                    if(furnaceInstallation.getEndthickness() != null && furnaceInstallation.getEndthickness().compareTo(equipmentRunTime.getTotalThickness())<0){
+                                        a = false;
+                                    }
+                                    if(furnaceInstallation.getStartwidth() != null && new BigDecimal(furnaceInstallation.getStartwidth()).compareTo(equipmentRunTime.getTotalVolumeWidth())>0){
+                                        a = false;
+                                    }
+                                    if(furnaceInstallation.getEndwidth() != null && new BigDecimal(furnaceInstallation.getEndwidth()).compareTo(equipmentRunTime.getTotalVolumeWidth())<0){
+                                        a = false;
+                                    }
+                                    if(a){
+                                        groupname = furnaceInstallation.getId();
+                                        break;
+                                    }
                                 }
-                                if(equipmentRunTime.getEndRunTime().compareTo(runTime.getEndRunTime())>0){
-                                    runTime.setEndRunTime(equipmentRunTime.getEndRunTime());
+                            }
+                            if(groupname == null){
+                                groupname = "group-self-" + equipmentRunTime.getVolumeMetal() + equipmentRunTime.getVolumeMetalstate() + equipmentRunTime.getProducttype() + equipmentRunTime.getTotalVolumeWidth() + equipmentRunTime.getTotalThickness();
+                            }
+                            Set<String> minThGroupNames = new HashSet<>();
+                            minThGroupNames.add(groupname);
+                            for (EquipmentRunTime runTime : equipmentRunTimesMerge) {
+                                // 时间相等
+                                if(runTime.getStartRunTime().compareTo(equipmentRunTime.getStartRunTime())==0){
+                                    runTime.setTotalVolumeWidth(runTime.getTotalVolumeWidth().add(equipmentRunTime.getTotalVolumeWidth()));
+                                    runTime.setTotalSinglerollweight(runTime.getTotalSinglerollweight().add(equipmentRunTime.getTotalSinglerollweight()));
+                                    // 最大、最小宽度
+                                    if(equipmentRunTime.getTotalVolumeWidth().compareTo(runTime.getMinVolumeWidth())<0){
+                                        runTime.setMinVolumeWidth(equipmentRunTime.getTotalVolumeWidth());
+                                    }
+                                    if(equipmentRunTime.getTotalVolumeWidth().compareTo(runTime.getMaxVolumeWidth())>0){
+                                        runTime.setMaxVolumeWidth(equipmentRunTime.getTotalVolumeWidth());
+                                    }
+                                    // 最大、最小厚度
+                                    if(equipmentRunTime.getTotalThickness().compareTo(runTime.getMinThickness())<0){
+                                        runTime.setMinThickness(equipmentRunTime.getTotalThickness());
+                                    }
+                                    if(equipmentRunTime.getTotalThickness().compareTo(runTime.getMaxThickness())>0){
+                                        runTime.setMaxThickness(equipmentRunTime.getTotalThickness());
+                                    }
+                                    // 最大、最小重量
+                                    if(equipmentRunTime.getTotalSinglerollweight().compareTo(runTime.getMinSinglerollweight())<0){
+                                        runTime.setMinSinglerollweight(equipmentRunTime.getTotalSinglerollweight());
+                                    }
+                                    if(equipmentRunTime.getTotalSinglerollweight().compareTo(runTime.getMaxSinglerollweight())>0){
+                                        runTime.setMaxSinglerollweight(equipmentRunTime.getTotalSinglerollweight());
+                                    }
+                                    runTime.getMinThGroupNames().addAll(minThGroupNames);
+                                    isMerged = true;
+                                    break;
                                 }
-                                isMerged = true;
-                                break;
                             }
-                        }
-                        if(!isMerged){
-                            equipmentRunTimesMerge.add(equipmentRunTime);
+                            if(!isMerged){
+                                equipmentRunTime.setMinVolumeWidth(equipmentRunTime.getTotalVolumeWidth());
+                                equipmentRunTime.setMaxVolumeWidth(equipmentRunTime.getTotalVolumeWidth());
+                                equipmentRunTime.setMinThickness(equipmentRunTime.getTotalThickness());
+                                equipmentRunTime.setMaxThickness(equipmentRunTime.getTotalThickness());
+                                equipmentRunTime.setMinSinglerollweight(equipmentRunTime.getTotalSinglerollweight());
+                                equipmentRunTime.setMaxSinglerollweight(equipmentRunTime.getTotalSinglerollweight());
+                                equipmentRunTime.setMinThGroupNames(minThGroupNames);
+                                equipmentRunTimesMerge.add(equipmentRunTime);
+                            }
                         }
                     }
+                    equipment.setEquipmentRunTimes(equipmentRunTimesMerge);
                 }
-                equipment.setEquipmentRunTimes(equipmentRunTimesMerge);
             }
         }
         // 设备列表初始化
@@ -817,7 +878,7 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
     private void nextProSort(Map<String,Integer> sortMap,Map<String,List<ProductionProcesses>> bsMap,String uniqueBsProcessesId){
         List<ProductionProcesses> processesList1 = bsMap.get(uniqueBsProcessesId);
         if(processesList1 != null && processesList1.size()>0){
-            if(processesList1.get(0).getProcessType().equals("成退") || processesList1.get(0).getProcessType().equals("中退")
+            if(processesList1.get(0).getProcessType().equals("成退") || processesList1.get(0).getProcessType().equals("中退") || processesList1.get(0).getProcessType().equals("小卷成退")
 //                    || processesList1.get(0).getProcessType().equals("冷轧") || processesList1.get(0).getProcessType().equals("铸轧")
             ){
 //                List<ProductionProcesses> pres = bsMap.get(processesList1.get(0).getPreviousProcesses().get(0).getUniqueBsProcessesId());

+ 15 - 3
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/vo/ProductionScheduleVo.java

@@ -1,9 +1,7 @@
 package com.rongwei.rwapsserver.aps.vo;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
-import com.rongwei.rwapsserver.aps.domain.Equipment;
-import com.rongwei.rwapsserver.aps.domain.ProductionProcesses;
-import com.rongwei.rwapsserver.aps.domain.WashingMetal;
+import com.rongwei.rwapsserver.aps.domain.*;
 import lombok.Data;
 
 import java.math.BigDecimal;
@@ -51,6 +49,16 @@ public class ProductionScheduleVo {
      */
     private List<WashingMetal> closealloynames;
 
+    /**
+     * 小卷成退合并装炉分组规则
+     */
+    private List<ApsFurnaceInstallationDo> furnaceInstallations;
+
+    /**
+     * 小卷成退合并装炉兼容规则
+     */
+    private List<ApsMergeFurnaceDo> mergeFurnaces;
+
     /**
      * 洗炉时间(小时)
      */
@@ -59,6 +67,10 @@ public class ProductionScheduleVo {
      * 立板时间(小时)
      */
     private Integer standingtime;
+    /**
+     * 周期立板产量(吨)
+     */
+    private Integer standingyield;
     /**
      * 松散度
      */