Prechádzať zdrojové kódy

退火调度排程优化

fangpengyuan 3 mesiacov pred
rodič
commit
9eaf51f694

+ 55 - 6
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/taskassigning/service/impl/ProductionScheduleTaServiceImpl.java

@@ -8,10 +8,7 @@ import com.rongwei.rwapsserver.aps.service.ApsService;
 import com.rongwei.rwapsserver.aps.service.DdApsService;
 import com.rongwei.rwapsserver.aps.taskassigning.service.DdApsTaService;
 import com.rongwei.rwapsserver.aps.taskassigning.service.ProductionScheduleTaService;
-import com.rongwei.rwapsserver.aps.taskassigning.tado.ApsConstraintListProvider;
-import com.rongwei.rwapsserver.aps.taskassigning.tado.ApsSolutionTa;
-import com.rongwei.rwapsserver.aps.taskassigning.tado.EquipmentTa;
-import com.rongwei.rwapsserver.aps.taskassigning.tado.ProductionProcessesTa;
+import com.rongwei.rwapsserver.aps.taskassigning.tado.*;
 import com.rongwei.rwapsserver.aps.taskassigning.vo.ProductionScheduleRetTaVo;
 import com.rongwei.rwapsserver.aps.taskassigning.vo.ProductionScheduleTaVo;
 import com.rongwei.rwapsserver.aps.util.ApsConstants;
@@ -1605,7 +1602,26 @@ public class ProductionScheduleTaServiceImpl implements ProductionScheduleTaServ
         }
 
         apsSolutionTh.setProcessesList(tuihuos);
-        apsSolutionTh.setEquipmentList(apsSolution.getEquipmentList());
+        apsSolutionTh.setEquipmentList(new ArrayList<>());
+        // 设备任务列表初始化
+        for (EquipmentTa equipmentTa : apsSolution.getEquipmentList()) {
+            boolean a = true;
+            if(equipmentTa.getEquipmentRunTimes() != null && !equipmentTa.getEquipmentRunTimes().isEmpty()){
+                for (EquipmentRunTime equipmentRunTime : equipmentTa.getEquipmentRunTimes()) {
+                    if("maintenance".equals(equipmentRunTime.getOccupyType())){
+                        if(equipmentRunTime.getEndRunTime() == null
+                                && equipmentRunTime.getStartRunTime().compareTo(apsSolutionTh.getProcessesList().get(0).getApsOverallConfig().getStartTime())<0){
+                            a = false;
+                            break;
+                        }
+                    }
+                }
+            }
+            if(a){
+                equipmentTa.setTasks(new ArrayList<>());
+                apsSolutionTh.getEquipmentList().add(equipmentTa);
+            }
+        }
 
         // 退火工序求解器运行
         int processNum1 = apsSolutionTh.getProcessesList().size();
@@ -1625,11 +1641,43 @@ public class ProductionScheduleTaServiceImpl implements ProductionScheduleTaServ
                         .withEnvironmentMode(EnvironmentMode.REPRODUCIBLE)
                         .withSolutionClass(ApsSolutionTa.class)
                         .withEntityClasses(ProductionProcessesTa.class, EquipmentTa.class)
-                        .withConstraintProviderClass(ApsConstraintListProvider.class)
+                        .withConstraintProviderClass(ApsConstraintListThProvider.class)
                         .withTerminationConfig(new TerminationConfig().withUnimprovedSecondsSpentLimit(60L))
                         .withMoveThreadCount(cores)
         );
         Solver<ApsSolutionTa> solver1 = solverFactory1.buildSolver();
+
+        solver1.addEventListener(new SolverEventListener<ApsSolutionTa>() {
+            public void bestSolutionChanged(BestSolutionChangedEvent<ApsSolutionTa> event) {
+                if(solver1.isEveryProblemChangeProcessed()) {
+                    event.getNewBestSolution().setConstructionHeuristicEnd(true);
+                    if(event.getNewBestSolution().getStepBestProcessesList() == null){
+                        List<ProductionProcessesTa> processesList = new ArrayList<>();
+                        for (ProductionProcessesTa productionProcesses : event.getNewBestSolution().getProcessesList()) {
+                            ProductionProcessesTa newPro = new ProductionProcessesTa();
+                            newPro.setId(productionProcesses.getId());
+                            newPro.setEquipmentId(productionProcesses.getEquipmentId());
+                            EquipmentTa eq = new EquipmentTa();
+                            if(productionProcesses.getEquipment() != null){
+                                eq.setId(productionProcesses.getEquipment().getId());
+                            }
+                            newPro.setEquipment(eq);
+                            newPro.setProduceTime(productionProcesses.getProduceTime());
+                            newPro.setStartTime(productionProcesses.getStartTime());
+                            newPro.setEndTime(productionProcesses.getEndTime());
+                            newPro.setVolumeWidth(productionProcesses.getVolumeWidth());
+                            newPro.setSinglerollweight(productionProcesses.getSinglerollweight());
+                            processesList.add(newPro);
+
+                            productionProcesses.setApsStatus("LS");
+                        }
+                        event.getNewBestSolution().setStepBestProcessesList(processesList);
+                    }
+                    System.out.println("************"+event.getNewBestScore()+"************");
+                    log.info("************"+event.getNewBestScore()+"************");
+                }
+            }
+        });
         // 退火调度排序
         List<ProductionProcessesTa> hapres = new ArrayList<>();
         List<ProductionProcessesTa> nothapres = new ArrayList<>();
@@ -1729,6 +1777,7 @@ public class ProductionScheduleTaServiceImpl implements ProductionScheduleTaServ
             productionProcesses.setNextProcesses(null);
             productionProcesses.getEquipment().setProcessesList(null);
             productionProcesses.getEquipment().setEquipmentRunTimes(null);
+            productionProcesses.getEquipment().setTasks(null);
             productionProcesses.setPreviousStep(null);
             productionProcesses.setOptionalProviderEquipments(null);
             productionProcesses.setEquass(null);

+ 643 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/taskassigning/tado/ApsConstraintListThProvider.java

@@ -0,0 +1,643 @@
+package com.rongwei.rwapsserver.aps.taskassigning.tado;
+
+import cn.hutool.core.util.StrUtil;
+import com.rongwei.rwapsserver.aps.domain.ApsNochangeRollerDo;
+import com.rongwei.rwapsserver.aps.domain.EquipmentRunTime;
+import com.rongwei.rwapsserver.aps.domain.ProduceOrder;
+import com.rongwei.rwapsserver.aps.vo.ApsTypeVo;
+import org.optaplanner.core.api.score.buildin.hardmediumsoft.HardMediumSoftScore;
+import org.optaplanner.core.api.score.stream.Constraint;
+import org.optaplanner.core.api.score.stream.ConstraintFactory;
+import org.optaplanner.core.api.score.stream.ConstraintProvider;
+
+import java.math.BigDecimal;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class ApsConstraintListThProvider implements ConstraintProvider {
+
+    private ZoneId zoneId = ZoneId.systemDefault();
+
+    @Override
+    public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
+
+        return new Constraint[]{
+                theEquipmentCanOnlyProcessOneProcessAtTheSameTime(constraintFactory),
+                optionalEquipment(constraintFactory),
+                // HARD
+                hasOnePreGbAfterNowNew(constraintFactory),
+//                nextLockProNew(constraintFactory),
+//                eqTimeCross(constraintFactory),
+//                seriesProduceTimeWait(constraintFactory),
+//                seriesProduceWashTimeWait(constraintFactory),
+//                equipmentRunTime(constraintFactory),
+//                eqTimeCrossTuihuo(constraintFactory),
+//                eqTimeCrossTuihuoType(constraintFactory),
+//                eqTimeCrossMinTuihuo(constraintFactory),
+//                maxStartTime(constraintFactory),
+                // MEDIUM
+                deliveryDate(constraintFactory),
+//                expecteddays(constraintFactory),
+                freeTimeEquipment(constraintFactory),
+//                seriesProduceWashingFurnace(constraintFactory),
+                mergeTuihuo(constraintFactory),
+//                tuihuoSp(constraintFactory),
+//                sameEquipment(constraintFactory),
+//                seriesZzLb(constraintFactory),
+//
+//                preNextProcessSameWorkShop(constraintFactory),
+                // SOFT
+                processNear(constraintFactory),
+//                lzbzSp(constraintFactory),
+//                eqTimeCrossMinTuihuoSoft(constraintFactory),
+//                preNextProcessSameEq(constraintFactory),
+        };
+    }
+
+    /**
+     * 设备生产的任务都在可选设备范围内
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint optionalEquipment(ConstraintFactory constraintFactory) {
+        return constraintFactory.forEach(EquipmentTa.class)
+                .filter((equipment) -> {
+                    return true;
+                })
+                .penalize(HardMediumSoftScore.ONE_HARD,(equipment) -> {
+                    int b = 0;
+                    List<ProductionProcessesTa> tasks = equipment.getTasks();
+                    if(tasks != null && tasks.size()>0){
+                        for (ProductionProcessesTa task : tasks) {
+                            if(!task.getOptionalEquipments().contains(equipment.getId())){
+                                b++;
+                            }
+                        }
+                    }
+                    return b*100;
+                })
+                .asConstraint("optionalEquipment");
+    }
+
+    /**
+     * 每个任务都要分配资源
+     * @param factory
+     * @return
+     */
+    public Constraint theEquipmentCanOnlyProcessOneProcessAtTheSameTime(ConstraintFactory factory) {
+        return factory.forEach(ProductionProcessesTa.class)
+                .filter(job -> job.getEquipment() == null || job.getStartTime() == null || job.getEndTime() == null)
+                .penalize(HardMediumSoftScore.ONE_HARD,(productionProcesses) -> 100)
+                .asConstraint("theEquipmentCanOnlyProcessOneProcessAtTheSameTime");
+    }
+
+    /**
+     * 单卷模式下前后道工序时间约束(新的等待时间调整)
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint hasOnePreGbAfterNowNew(ConstraintFactory constraintFactory) {
+        return constraintFactory.forEach(ProductionProcessesTa.class)
+                .filter(productionProcesses -> {
+                    boolean bln = false;
+                    if(productionProcesses.getEquipment() != null && productionProcesses.getPreviousProcesses() != null && productionProcesses.getPreviousProcesses().size()>0){
+                        // 此种情况简化为前道工序只有一个
+                        for (ProductionProcessesTa previousProcess : productionProcesses.getPreviousProcesses()) {
+                            ProductionProcessesTa preProcess = previousProcess;
+                            // 流转时间(最小等待时间)
+                            Integer lzTimes = 0;
+                            if(preProcess.getEquipment() != null){
+                                if(preProcess.getEquipment().getWorkshopid() != null && preProcess.getEquipment().getWorkshopid().equals(productionProcesses.getEquipment().getWorkshopid())){
+                                    lzTimes = productionProcesses.getApsOverallConfig().getRoamTime().get("WORKSHOP_IN");
+                                }else{
+                                    lzTimes = productionProcesses.getApsOverallConfig().getRoamTime().get("WORKSHOP_CROSS");
+                                }
+                                // 最小等待时间对比流转时间
+                                if(productionProcesses.getMinWaitTime() != null && lzTimes<productionProcesses.getMinWaitTime()){
+                                    lzTimes = productionProcesses.getMinWaitTime();
+                                }
+                                // 最大等待时间
+                                Integer maxWaitTime = productionProcesses.getMaxWaitTime();
+                                if(productionProcesses.getStartTime().compareTo(preProcess.getEndTime().plusMinutes(lzTimes))<0){
+                                    bln = true;
+                                }
+                                if(maxWaitTime != null && maxWaitTime>0){
+                                    if(productionProcesses.getStartTime().compareTo(preProcess.getEndTime().plusMinutes(maxWaitTime))>0){
+                                        bln = true;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    return bln;
+                })
+                .penalize(HardMediumSoftScore.ONE_HARD,(productionProcesses) -> 40)
+                .asConstraint("hasOnePreGbAfterNow");
+    }
+
+    /**
+     * 递归计算是否比所有下道工序开始时间晚
+     * @param productionProcesses
+     * @param nextStartTimes
+     * @param totalMinWaitTime
+     */
+    private void getAllNextProTime(ProductionProcessesTa productionProcesses, List<LocalDateTime> nextStartTimes, Integer totalMinWaitTime){
+        List<ProductionProcessesTa> nextProcesses = productionProcesses.getNextProcesses();
+        if(nextProcesses != null && nextProcesses.size()>0){
+            Integer nowTotalMinWaitTime = totalMinWaitTime;
+            for (ProductionProcessesTa nextProcess : nextProcesses) {
+                if(nextProcess.getStartTime() != null){
+                    LocalDateTime startTime = nextProcess.getStartTime();
+                    if(nextProcess.getMinWaitTime() != null && nextProcess.getMinWaitTime()>0){
+                        nowTotalMinWaitTime = totalMinWaitTime + nextProcess.getMinWaitTime();
+                        startTime = startTime.minusMinutes(nowTotalMinWaitTime);
+                    }
+                    nextStartTimes.add(startTime);
+                }
+                getAllNextProTime(nextProcess,nextStartTimes,nowTotalMinWaitTime);
+            }
+        }
+    }
+
+    /**
+     * 设备运行时间段,不可再排产
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint equipmentRunTime(ConstraintFactory constraintFactory) {
+        return constraintFactory.forEach(ProductionProcessesTa.class)
+                .filter(pro->{
+                    return !pro.getProcessType().equals("成退") && !pro.getProcessType().equals("中退") && !pro.getProcessType().equals("小卷成退");
+                })
+                .filter(productionProcesses -> {
+                    if(productionProcesses.getEquipment().getId().equals("0001be252874536843730b100024")){
+                        int abc = 1;
+                        if(productionProcesses.getDelay() == 270){
+                            int abcd = 1;
+                        }
+                    }
+                    boolean bol = false;
+                    if(productionProcesses.getEquipment() != null && productionProcesses.getEquipment().getEquipmentRunTimes() != null &&
+                            productionProcesses.getEquipment().getEquipmentRunTimes().size()>0
+                            && productionProcesses.getStartTime() != null && productionProcesses.getEndTime() != null){
+                        for (EquipmentRunTime equipmentRunTime : productionProcesses.getEquipment().getEquipmentRunTimes()) {
+                            LocalDateTime startTime = equipmentRunTime.getStartRunTime();
+                            LocalDateTime endRunTime = equipmentRunTime.getEndRunTime();
+                            if(equipmentRunTime.getStartRunTime() != null && equipmentRunTime.getEndRunTime() != null){
+                                if(productionProcesses.getStartTime().compareTo(endRunTime)>=0 || productionProcesses.getEndTime().compareTo(startTime)<=0){
+                                    continue;
+                                }else{
+                                    bol = true;
+                                    break;
+                                }
+                            }else{
+                                if(startTime != null && endRunTime == null){
+                                    if(productionProcesses.getEndTime().compareTo(startTime)>0){
+                                        bol = true;
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    return bol;
+                })
+                .penalize(HardMediumSoftScore.ONE_HARD,(proc1)->80)
+                .asConstraint("equipmentRunTime");
+    }
+
+    /**
+     * 软约束:交货日期,根据延迟交货日期的天数来做惩罚分数计算
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint deliveryDate(ConstraintFactory constraintFactory) {
+        return constraintFactory.forEach(ProductionProcessesTa.class)
+                .filter(productionProcesses -> {
+                    /*
+                        获取最后一步工步的结束时间(最后一步工步的结束时间即此产品生产的实际结束时间)
+                        并且获取结束时间大于生产订单的交货日期
+                     */
+                    if(productionProcesses.getEndTime() == null || productionProcesses.getStartTime() == null || productionProcesses.getEquipment() == null){
+                        return false;
+                    }
+                    // 取最小订单交货日期
+                    Date deliveryMinDate = null;
+                    for (ProduceOrder produceOrder : productionProcesses.getProduceOrder()) {
+                        if(deliveryMinDate == null){
+                            deliveryMinDate = produceOrder.getDeliveryDate();
+                        }else{
+                            if(deliveryMinDate.compareTo(produceOrder.getDeliveryDate())>0){
+                                deliveryMinDate = produceOrder.getDeliveryDate();
+                            }
+                        }
+                    }
+                    if(deliveryMinDate == null){
+                        return false;
+                    }else{
+                        if(productionProcesses.getApsOverallConfig().getStartTime().atZone(zoneId).toInstant().toEpochMilli()>deliveryMinDate.getTime()){
+                            return false;
+                        }
+                    }
+                    Boolean bol = productionProcesses.getEndTime().atZone(zoneId).toInstant().toEpochMilli()>deliveryMinDate.getTime();
+                    return bol;
+                })
+                .penalize(HardMediumSoftScore.ONE_MEDIUM,(productionProcesses) ->{
+                    /*Date deliveryMinDate = null;
+                    for (ProduceOrder produceOrder : productionProcesses.getProduceOrder()) {
+                        if(deliveryMinDate == null){
+                            deliveryMinDate = produceOrder.getDeliveryDate();
+                        }else{
+                            if(deliveryMinDate.compareTo(produceOrder.getDeliveryDate())>0){
+                                deliveryMinDate = produceOrder.getDeliveryDate();
+                            }
+                        }
+                    }
+                    int i = (int) (DateUtil.between(deliveryMinDate, Date.from(productionProcesses.getEndTime().atZone(zoneId).toInstant()), DateUnit.MINUTE) + 1) * 10;
+                    if(i<0){
+                        int aa = 0;
+                    }
+                    return i;*/
+                    return 1000;
+                })
+                .asConstraint("deliveryDate");
+    }
+
+    /**
+     * 软约束:交货日期,根据延迟交货日期的天数来做惩罚分数计算
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint expecteddays(ConstraintFactory constraintFactory) {
+        return constraintFactory.forEach(ProductionProcessesTa.class)
+//                .filter(productionProcesses -> productionProcesses.getNextProcesses() == null || productionProcesses.getNextProcesses().size() == 0)
+                .filter(productionProcesses -> {
+                    // 非最后一道工序或者最后一道工序没有排程时间的工序直接跳过
+                    if(productionProcesses.getEndTime() == null){
+                        return false;
+                    }
+                    Long expecteddays = expecteddaysCount(productionProcesses);
+                    if(expecteddays>0){
+                        return true;
+                    }else{
+                        return false;
+                    }
+                })
+                .penalize(HardMediumSoftScore.ONE_MEDIUM,(productionProcesses) ->{
+                    Long expecteddays = expecteddaysCount(productionProcesses);
+                    return (int)(expecteddays*10000);
+                })
+                .asConstraint("expecteddays");
+    }
+
+    private Long expecteddaysCount(ProductionProcessesTa productionProcesses){
+        // 取最小订单交货天数
+        Integer deliveryMinDate = null;
+        for (ProduceOrder produceOrder : productionProcesses.getProduceOrder()) {
+            if(deliveryMinDate == null){
+                deliveryMinDate = produceOrder.getExpecteddays();
+            }else{
+                if(produceOrder.getExpecteddays() != null && produceOrder.getExpecteddays()<deliveryMinDate){
+                    deliveryMinDate = produceOrder.getExpecteddays();
+                }
+            }
+        }
+        if(deliveryMinDate == null){
+            deliveryMinDate = 15;
+        }
+        // 有铸轧的工序取铸轧后第一道工序开始时间,没有铸轧的直接取第一道工序开始时间
+        LocalDateTime startTime = null;
+        ProductionProcessesTa rooprocess = productionProcesses.getRooprocess();
+        if(rooprocess != null){
+            if("铸轧".equals(rooprocess.getProcessType())){
+                if("成退".equals(rooprocess.getNextProcesses().get(0).getProcessType()) || "中退".equals(rooprocess.getNextProcesses().get(0).getProcessType())
+                        || "小卷成退".equals(rooprocess.getNextProcesses().get(0).getProcessType())){
+                    startTime = null;
+                }else{
+                    startTime = rooprocess.getNextProcesses().get(0).getStartTime();
+                }
+            }else{
+                if ("成退".equals(rooprocess.getProcessType()) || "中退".equals(rooprocess.getProcessType()) || "小卷成退".equals(rooprocess.getProcessType())){
+                    if(rooprocess.getNextProcesses() != null && rooprocess.getNextProcesses().size()>0){
+                        startTime = rooprocess.getNextProcesses().get(0).getStartTime();
+                    }
+                }else{
+                    startTime = rooprocess.getStartTime();
+                }
+            }
+        }else{
+            return 0L;
+        }
+        if(startTime == null){
+            return 0L;
+        }
+        long daysBetween = ChronoUnit.DAYS.between(startTime, productionProcesses.getEndTime());
+        return daysBetween-deliveryMinDate;
+    }
+
+    /**
+     * 设备生产的任务都在可选设备范围内
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint freeTimeEquipment(ConstraintFactory constraintFactory) {
+        return constraintFactory.forEach(EquipmentTa.class)
+                .filter((equipment) -> {
+                    return true;
+                })
+                .penalize(HardMediumSoftScore.ONE_MEDIUM,(equipment) -> {
+                    int b = 0;
+                    List<ProductionProcessesTa> tasks = new ArrayList<>();
+
+                    if(equipment.getTasks() != null && equipment.getTasks().size()>0){
+                        if(equipment.getLastProcessEndTime() != null){
+                            ProductionProcessesTa pta = new ProductionProcessesTa();
+                            LocalDateTime eqlastTime = equipment.getLastProcessEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+                            LocalDateTime startTime = equipment.getTasks().get(0).getApsOverallConfig().getStartTime();
+                            if(startTime.compareTo(eqlastTime)>0){
+                                pta.setStartTime(startTime.plusMinutes(-10));
+                                pta.setEndTime(startTime);
+                            }else{
+                                pta.setStartTime(eqlastTime.plusMinutes(-10));
+                                pta.setEndTime(eqlastTime);
+                            }
+                            tasks.add(pta);
+                        }else{
+                            if(equipment.getEquipmentRunTimes() != null && equipment.getEquipmentRunTimes().size()>0){
+                                for (EquipmentRunTime equipmentRunTime : equipment.getEquipmentRunTimes()) {
+                                    ProductionProcessesTa pta = new ProductionProcessesTa();
+                                    pta.setStartTime(equipmentRunTime.getStartRunTime());
+                                    pta.setEndTime(equipmentRunTime.getEndRunTime());
+                                    tasks.add(pta);
+                                }
+                            }
+                        }
+                        tasks.addAll(equipment.getTasks());
+
+                        // 根据开始时间排序
+                        if(tasks.size()>1){
+                            Collections.sort(tasks, Comparator.comparing(ProductionProcessesTa::getStartTime));
+                            for (int i = 0; i < tasks.size()-1; i++) {
+                                ProductionProcessesTa preTask = tasks.get(i);
+                                ProductionProcessesTa nextTask = tasks.get(i + 1);
+                                if(preTask.getEndTime().plusMinutes(60).compareTo(nextTask.getStartTime())<0){
+                                    b = b + 100;
+                                    // 上道工序结束时间和下道开始时间的分钟差,再减去1小时
+                                    long durationInMinutes = Duration.between(preTask.getEndTime(), nextTask.getStartTime()).toMinutes()-60;
+                                    if(durationInMinutes>0){
+                                        long hs = durationInMinutes / 60;
+                                        long hsm = durationInMinutes % 60;
+                                        if(hsm>0){
+                                            hs = hs + 1;
+                                        }
+                                        b = (int) (b + hs);
+                                    }
+                                }
+                            }
+                        }
+
+                    }
+                    return b;
+                })
+                .asConstraint("freeTimeEquipment");
+    }
+
+    /**
+     * 铸轧判断合金产品类型是否兼容
+     * @param beforePro
+     * @param afterPro
+     * @param apsNochangeRollerDos
+     * @return
+     */
+    private boolean zzSeriesJr(ProductionProcessesTa beforePro, ProductionProcessesTa afterPro, List<ApsNochangeRollerDo> apsNochangeRollerDos){
+        boolean ifJr = false;
+        String[] serspre = beforePro.getSeriesProduceMark().split("\\^_\\^");
+        String[] sersafter = afterPro.getSeriesProduceMark().split("\\^_\\^");
+        if(serspre.length == 5 && sersafter.length == 5){
+            // 合金和产品类型
+            // 前一道作业合金、产品类型
+            String bfhj = serspre[0];
+            // 获取产品类型二级
+            String bfcplx = serspre[1].split("-")[0];
+            // 后一道作业合金、产品类型
+            String afhj = sersafter[0];
+            // 获取产品类型二级
+            String afcplx = sersafter[1].split("-")[0];
+            // 合金和二级产品类型一样的无需换辊
+            if(bfhj.equals(afhj) && bfcplx.equals(afcplx)){
+                ifJr = true;
+            }else{
+                // 铸轧合金兼容配置
+                if(apsNochangeRollerDos != null && apsNochangeRollerDos.size()>0){
+                    for (ApsNochangeRollerDo apsNochangeRollerDo : apsNochangeRollerDos) {
+                        // 判断合金是否都包含,且相同
+                        if(bfhj.equals(afhj)){
+                            if(StrUtil.isNotBlank(apsNochangeRollerDo.getWorkingalloy())){
+                                if(apsNochangeRollerDo.getWorkingalloy().contains(bfhj) && apsNochangeRollerDo.getWorkingalloy().contains(afhj)){
+                                    // 判断产品类型是包含还是不包含的关系
+                                    if("包含".equals(apsNochangeRollerDo.getCompatibilityrules())){
+                                        // 产品类型不为空
+                                        if(StrUtil.isNotBlank(apsNochangeRollerDo.getJobproducttype())){
+                                            if(apsNochangeRollerDo.getJobproducttype().contains(bfcplx) && apsNochangeRollerDo.getJobproducttype().contains(afcplx)){
+                                                ifJr = true;
+                                                break;
+                                            }
+                                        }
+                                        // 产品类型为空
+                                        else{
+                                            ifJr = true;
+                                            break;
+                                        }
+                                    } else if ("不包含".equals(apsNochangeRollerDo.getCompatibilityrules())) {
+                                        // 产品类型不为空
+                                        if(StrUtil.isNotBlank(apsNochangeRollerDo.getJobproducttype())){
+                                            if(!apsNochangeRollerDo.getJobproducttype().contains(bfcplx) && !apsNochangeRollerDo.getJobproducttype().contains(afcplx)){
+                                                ifJr = true;
+                                                break;
+                                            }
+                                        }
+                                        // 产品类型为空
+                                        else{
+                                            continue;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return ifJr;
+    }
+
+    /**
+     * 排程时间尽量靠前
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint processNear(ConstraintFactory constraintFactory){
+        return constraintFactory.forEach(ProductionProcessesTa.class)
+                .filter((process)->{
+                    if(process.getPreviousProcesses() == null || process.getPreviousProcesses().size() == 0){
+                        if(process.getStartTime() == null || process.getApsOverallConfig().getStartTime() == null){
+                            int ma = 0;
+                        }
+                        if(process.getStartTime() != null && process.getStartTime().compareTo(process.getApsOverallConfig().getStartTime())>0){
+                            return true;
+                        }else{
+                            return false;
+                        }
+                    }else{
+                        if(process.getStartTime() != null){
+                            return true;
+                        }else{
+                            return false;
+                        }
+                    }
+                })
+                .penalize(HardMediumSoftScore.ONE_SOFT,(process) -> {
+                    int processNearNum = 0;
+                    processNearNum = (int)ChronoUnit.MINUTES.between(process.getApsOverallConfig().getStartTime(), process.getStartTime());
+                    if(processNearNum<=0){
+                        processNearNum = 0;
+                    }
+                    return processNearNum;
+                })
+                .asConstraint("processNear");
+    }
+
+    /**
+     * 前后工序可以在一个设备上的尽量在一个设备上
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint preNextProcessSameEq(ConstraintFactory constraintFactory) {
+        return constraintFactory.forEach(ProductionProcessesTa.class)
+                .filter(pro->{
+                    boolean a = false;
+                    if(pro.getPreviousProcesses() != null && pro.getPreviousProcesses().size()>0){
+                        a = true;
+                    }
+                    return a;
+                })
+                .penalize(HardMediumSoftScore.ONE_SOFT,(processes)->{
+                    int a = 0;
+                    EquipmentTa equipmentthis = processes.getEquipment();
+                    EquipmentTa equipmentpre = processes.getPreviousProcesses().get(0).getEquipment();
+                    if(!equipmentpre.getId().equals(equipmentthis.getId())){
+                        a = a + 10000;
+                    }
+                    /*if(!equipmentpre.getWorkshopid().equals(equipmentthis.getWorkshopid())){
+                        a = a + 10000;
+                    }*/
+                    return a;
+                })
+                .asConstraint("preNextProcessSameEq");
+    }
+
+    /**
+     * 前后工序可以在一个设备上的尽量在一个设备上
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint preNextProcessSameWorkShop(ConstraintFactory constraintFactory) {
+        return constraintFactory.forEach(ProductionProcessesTa.class)
+                .filter(pro->{
+                    boolean a = false;
+                    if(pro.getPreviousProcesses() != null && pro.getPreviousProcesses().size()>0){
+                        a = true;
+                    }
+                    return a;
+                })
+                .penalize(HardMediumSoftScore.ONE_MEDIUM,(processes)->{
+                    int a = 0;
+                    EquipmentTa equipmentthis = processes.getEquipment();
+                    EquipmentTa equipmentpre = processes.getPreviousProcesses().get(0).getEquipment();
+                    if(!equipmentpre.getWorkshopid().equals(equipmentthis.getWorkshopid())){
+                        a = a + 5000;
+                    }
+                    return a;
+                })
+                .asConstraint("preNextProcessSameWorkShop");
+    }
+
+    /**
+     * 退火工序组炉规则
+     * @param constraintFactory
+     * @return
+     */
+    public Constraint mergeTuihuo(ConstraintFactory constraintFactory){
+        return constraintFactory.forEach(EquipmentTa.class)
+                .filter(equipmentTa->{
+                    boolean a = false;
+                    List<ProductionProcessesTa> tasks = equipmentTa.getTasks();
+                    if(!tasks.isEmpty()){
+                        if("成退,中退,小卷成退".contains(tasks.get(0).getProcessType())){
+                            a = true;
+                        }
+                    }
+                    return a;
+                })
+                .penalize(HardMediumSoftScore.ONE_MEDIUM,(equipmentTa)->{
+                    // 已占用的
+                    List<EquipmentRunTime> equipmentRunTimes = equipmentTa.getEquipmentRunTimes();
+                    // 同一工序的优先组炉
+                    List<ProductionProcessesTa> ths = new ArrayList<>();
+                    equipmentTa.getTasks().forEach(v->{
+                        ths.add(v);
+                    });
+                    // 退火合并工序
+                    Map<String,List<ProductionProcessesTa>> ppMap = new HashMap<>();
+                    if(ths.size()>0){
+                        for(int i=0;i<ths.size();i++){
+                            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+                            String startTimeKey = ths.get(i).getStartTime().format(formatter)+ths.get(i).getEquipmentId();
+                            if(ppMap.containsKey(startTimeKey)){
+                                ppMap.get(startTimeKey).add(ths.get(i));
+                            }else{
+                                List<ProductionProcessesTa> ps = new ArrayList<>();
+                                ps.add(ths.get(i));
+                                ppMap.put(startTimeKey,ps);
+                            }
+                        }
+                    }
+
+                    List<String> hasMergeOldKeys = new ArrayList<>();
+                    ppMap.forEach((k,v)->{
+                        if(equipmentRunTimes != null && equipmentRunTimes.size()>0){
+                            for (EquipmentRunTime equipmentRunTime : equipmentRunTimes) {
+                                if(equipmentRunTime.getStartRunTime().compareTo(v.get(0).getStartTime()) == 0){
+                                    hasMergeOldKeys.add(k);
+                                    break;
+                                }
+                            }
+                        }
+//                            }
+//                        }
+                    });
+                    if(hasMergeOldKeys != null && hasMergeOldKeys.size()>0){
+                        for (String hasMergeOldKey : hasMergeOldKeys) {
+                            ppMap.remove(hasMergeOldKey);
+                        }
+                    }
+                    int qz = 1;
+                    ApsTypeVo scheduleType = equipmentTa.getTasks().get(0).getApsOverallConfig().getScheduleType();
+                    if(scheduleType == null || scheduleType.getConstraintMode() == null || "th".equals(scheduleType.getConstraintMode())){
+                        qz = 1100;
+                    }
+                    int zzdf = ppMap.size()*qz;
+                    if(hasMergeOldKeys != null && hasMergeOldKeys.size()>0){
+                        zzdf = zzdf + hasMergeOldKeys.size();
+                    }
+                    return zzdf;
+                })
+                .asConstraint("mergeTuihuo");
+    }
+
+}