Bläddra i källkod

新模型调整

fangpy 5 månader sedan
förälder
incheckning
f10821df48

+ 1 - 1
pom.xml

@@ -39,7 +39,7 @@
         <dependency>
             <groupId>cn.hutool</groupId>
             <artifactId>hutool-all</artifactId>
-            <version>4.0.1</version>
+            <version>5.8.24</version>
         </dependency>
     </dependencies>
 

+ 17 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/domain/PendingChangeTaskDTO.java

@@ -0,0 +1,17 @@
+package com.rongwei.rwapsserver.aps.domain;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * @author jvs
+ * 待变更任务
+ */
+@Data
+@Accessors(chain = true)
+public class PendingChangeTaskDTO {
+    // 变更任务所在目标资源的下标
+    private int fromIndex;
+    // 变更任务所在目标主资源
+    private Equipment resource;
+}

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

@@ -8,6 +8,7 @@ import org.optaplanner.core.api.domain.entity.PlanningEntity;
 import org.optaplanner.core.api.domain.lookup.PlanningId;
 import org.optaplanner.core.api.domain.valuerange.CountableValueRange;
 import org.optaplanner.core.api.domain.valuerange.ValueRangeFactory;
+import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
 import org.optaplanner.core.api.domain.variable.*;
 import java.io.Serializable;
 import java.math.BigDecimal;
@@ -213,7 +214,6 @@ public class ProductionProcesses implements Serializable {
     /**
      * 开始时间
      */
-    @ShadowVariable(variableListenerClass = TaskStartTimeListenerTa.class, sourceEntityClass = Equipment.class, sourceVariableName = "tasks")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime startTime;
 
@@ -464,6 +464,7 @@ public class ProductionProcesses implements Serializable {
         this.optionalProviderEquipments = optionalProviderEquipments;
     }
 
+    @ShadowVariable(variableListenerClass = TaskStartTimeListenerTa.class, sourceEntityClass = Equipment.class, sourceVariableName = "tasks")
     //    @PlanningVariable(valueRangeProviderRefs={"timeRange"})
 //    @ShadowVariable(variableListenerClass = TaskStartTimeListener.class, sourceVariableName = "equipment")
 //    @ShadowVariable(variableListenerClass = TaskStartTimeListener.class, sourceVariableName = "delay")

+ 21 - 2
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/score/ApsConstraintListProvider.java

@@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil;
 import com.rongwei.rwapsserver.aps.domain.*;
 import org.optaplanner.core.api.score.buildin.hardmediumsoft.HardMediumSoftScore;
 import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
+import org.optaplanner.core.api.score.buildin.hardsoftlong.HardSoftLongScore;
 import org.optaplanner.core.api.score.stream.*;
 
 import java.math.BigDecimal;
@@ -25,7 +26,7 @@ public class ApsConstraintListProvider implements ConstraintProvider {
     public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
 
         return new Constraint[]{
-
+                theEquipmentCanOnlyProcessOneProcessAtTheSameTime(constraintFactory),
                 optionalEquipment(constraintFactory),
                 // HARD
                 hasOnePreGbAfterNowNew(constraintFactory),
@@ -67,7 +68,7 @@ public class ApsConstraintListProvider implements ConstraintProvider {
                 .filter((equipment) -> {
                     return true;
                 })
-                .penalize(HardSoftScore.ONE_HARD,(equipment) -> {
+                .penalize(HardMediumSoftScore.ONE_HARD,(equipment) -> {
                     int b = 0;
                     List<ProductionProcesses> tasks = equipment.getTasks();
                     if(tasks != null && tasks.size()>0){
@@ -102,6 +103,18 @@ public class ApsConstraintListProvider implements ConstraintProvider {
                 .asConstraint("maxStartTime");
     }
 
+    /**
+     * 每个任务都要分配资源
+     * @param factory
+     * @return
+     */
+    public Constraint theEquipmentCanOnlyProcessOneProcessAtTheSameTime(ConstraintFactory factory) {
+        return factory.forEach(ProductionProcesses.class)
+                .filter(job -> job.getEquipment() == null || job.getStartTime() == null || job.getEndTime() == null)
+                .penalize(HardMediumSoftScore.ONE_HARD,(productionProcesses) -> 100)
+                .asConstraint("theEquipmentCanOnlyProcessOneProcessAtTheSameTime");
+    }
+
     /**
      * 单卷模式下前后道工序时间约束(新的等待时间调整)
      * @param constraintFactory
@@ -117,6 +130,9 @@ public class ApsConstraintListProvider implements ConstraintProvider {
                     List<ProductionProcesses> tasks = equipment.getTasks();
                     if(tasks != null && tasks.size()>0){
                         for (ProductionProcesses productionProcesses : tasks) {
+                            if(productionProcesses.getStartTime() == null){
+                                continue;
+                            }
                             if(productionProcesses.getPreviousProcesses() != null && productionProcesses.getPreviousProcesses().size()>0){
                                 // 此种情况简化为前道工序只有一个
                                 for (ProductionProcesses previousProcess : productionProcesses.getPreviousProcesses()) {
@@ -135,6 +151,9 @@ public class ApsConstraintListProvider implements ConstraintProvider {
                                         }
                                         // 最大等待时间
                                         Integer maxWaitTime = productionProcesses.getMaxWaitTime();
+                                        if(productionProcesses.getStartTime() == null){
+                                            int a = 0;
+                                        }
                                         if(productionProcesses.getStartTime().compareTo(preProcess.getEndTime().plusMinutes(lzTimes))<0){
                                             b++;
                                         }

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

@@ -324,13 +324,13 @@ public class ApsServiceImpl implements ApsService {
         // CPU核数
         String cores = Runtime.getRuntime().availableProcessors() + "";
         SolverFactory<ApsSolution> solverFactory1 = SolverFactory.create(new SolverConfig()
-                .withEnvironmentMode(EnvironmentMode.FULL_ASSERT)
-                .withSolutionClass(ApsSolution.class)
-                .withEntityClasses(ProductionProcesses.class)
-                .withConstraintProviderClass(ApsConstraintProvider.class)
+                        .withEnvironmentMode(EnvironmentMode.REPRODUCIBLE)
+                        .withSolutionClass(ApsSolution.class)
+                        .withEntityClasses(ProductionProcesses.class)
+                        .withConstraintProviderClass(ApsConstraintProvider.class)
 //                .withTerminationSpentLimit(Duration.ofSeconds(runPlanSeconds1))
-                .withTerminationConfig(new TerminationConfig().withUnimprovedSecondsSpentLimit(60L))
-                .withMoveThreadCount(cores)
+                        .withTerminationConfig(new TerminationConfig().withUnimprovedSecondsSpentLimit(60L))
+                        .withMoveThreadCount(cores)
         );
         Solver<ApsSolution> solver1 = solverFactory1.buildSolver();
         // 按照宽度排序
@@ -346,32 +346,41 @@ public class ApsServiceImpl implements ApsService {
             // 宽度
             BigDecimal prowidth1 = v1.getVolumeWidth();
             BigDecimal prowidth2 = v2.getVolumeWidth();
-            if(prowidth1.compareTo(prowidth2) == 0){
-                // 合金
-                if(alloy1.compareTo(alloy2) == 0){
-                    // 合金状态
-                    if(alloystatus1 == null || alloystatus2 == null || alloystatus1.compareTo(alloystatus2) == 0){
-                        if(v1.getApsOverallConfig().getStartTime().compareTo(v2.getApsOverallConfig().getStartTime()) == 0){
-                            a = v2.getVolumeWidth().compareTo(v1.getVolumeWidth());
-                        }else{
-                            a = v2.getApsOverallConfig().getStartTime().compareTo(v1.getApsOverallConfig().getStartTime());
-                        }
+//            if(prowidth1.compareTo(prowidth2) == 0){
+            // 合金
+            if(alloy1.compareTo(alloy2) == 0){
+                // 合金状态
+                if(alloystatus1 == null || alloystatus2 == null || alloystatus1.compareTo(alloystatus2) == 0){
+                    if(v1.getApsOverallConfig().getStartTime().compareTo(v2.getApsOverallConfig().getStartTime()) == 0){
+                        a = v2.getVolumeWidth().compareTo(v1.getVolumeWidth());
+//                            a = 0;
                     }else{
-                        a = alloystatus1.compareTo(alloystatus2);
+                        a = v2.getApsOverallConfig().getStartTime().compareTo(v1.getApsOverallConfig().getStartTime());
                     }
                 }else{
-                    a = alloy1.compareTo(alloy2);
+                    a = alloystatus1.compareTo(alloystatus2);
                 }
             }else{
-                a = prowidth2.compareTo(prowidth1);
+                a = alloy1.compareTo(alloy2);
             }
+            /*}else{
+                a = prowidth2.compareTo(prowidth1);
+            }*/
             return a;
         });
         ApsSolution solvedBalance1 = solver1.solve(apsSolutionTh);
         /*for (ProductionProcesses productionProcesses : solvedBalance1.getProcessesList()) {
             DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
-            LocalDateTime parsedDateTime = LocalDateTime.parse("2025-01-02 07:00:00", formatter);
-            productionProcesses.setStartTime(parsedDateTime);
+            if(productionProcesses.getId().equals("b80ec7b7fceb45658ba3db980ab25694")){
+                productionProcesses.setDelay(1);
+                LocalDateTime parsedDateTime = LocalDateTime.parse("2025-03-01 11:00:00", formatter);
+                productionProcesses.setStartTime(parsedDateTime);
+                for (Equipment optionalProviderEquipment : productionProcesses.getOptionalProviderEquipments()) {
+                    if(optionalProviderEquipment.getId().equals("ea772303eebe4feda4a83d5c9b31887b")){
+                        productionProcesses.setEquipment(optionalProviderEquipment);
+                    }
+                }
+            }
         }*/
         log.info("**************退火排程评分分析***************");
         SolutionManager<ApsSolution, HardSoftScore> scoreManager1 = SolutionManager.create(solverFactory1);
@@ -492,98 +501,132 @@ public class ApsServiceImpl implements ApsService {
                 ProductionProcesses mainTh = thOrdersMerge(hbThPros, otherThproces, allProMap,apsSolution);
                 apsSolution.getProcessesList().sort(Comparator.comparing(ProductionProcesses::getOrderMark));
                 // 退火后一道工序如果是精整类别的则合并
-                List<ProductionProcesses> lastProMerge = new ArrayList<>();
-                if(mainTh.getNextProcesses() != null && mainTh.getNextProcesses().size()>0){
-                    for (ProductionProcesses productionProcesses : mainTh.getNextProcesses()) {
-                        if(productionProcesses.getProcessCategory() != null && "40".equals(productionProcesses.getProcessCategory())
-                                && (productionProcesses.getNextProcesses() == null || productionProcesses.getNextProcesses().size() == 0)
-                                && !productionProcesses.getIfLock()
-                        ){
-                            lastProMerge.add(productionProcesses);
-                        }
-                    }
+                jzMerge(mainTh,apsSolution);
+                // 退火前一道工序合并
+
+            }
+        });
+    }
+
+    /**
+     * 退火后一道工序如果是精整类别的则合并
+     * @param mainTh
+     * @param apsSolution
+     */
+    private void jzMerge(ProductionProcesses mainTh,ApsSolution apsSolution){
+        List<ProductionProcesses> lastProMerge = new ArrayList<>();
+        if(mainTh.getNextProcesses() != null && mainTh.getNextProcesses().size()>0){
+            for (ProductionProcesses productionProcesses : mainTh.getNextProcesses()) {
+                if(productionProcesses.getProcessCategory() != null && "40".equals(productionProcesses.getProcessCategory())
+                        && (productionProcesses.getNextProcesses() == null || productionProcesses.getNextProcesses().size() == 0)
+                        && !productionProcesses.getIfLock()
+                ){
+                    lastProMerge.add(productionProcesses);
                 }
-                if(lastProMerge.size()>0){
-                    Map<String, List<ProductionProcesses>> orderProcess = lastProMerge.stream().collect(Collectors.groupingBy(ProductionProcesses::getProcessType));
-                    orderProcess.forEach((k1,v1)->{
-                        if(v1.size()>1){
-                            List<String> sameEqs = new ArrayList<>();
-                            boolean issameeq = true;
-                            for (ProductionProcesses productionProcesses : v1) {
-                                if(sameEqs.size() == 0){
-                                    sameEqs.addAll(productionProcesses.getOptionalEquipments());
-                                }else{
-                                    List<String> a = new ArrayList<>();
-                                    for (String sameEq : sameEqs) {
-                                        for (String optionalEquipment : productionProcesses.getOptionalEquipments()) {
-                                            if(sameEq.equals(optionalEquipment)){
-                                                a.add(sameEq);
-                                            }
-                                        }
-                                    }
-                                    if(a.size()>0){
-                                        sameEqs = a;
-                                    }else{
-                                        issameeq = false;
-                                        break;
+            }
+        }
+        if(lastProMerge.size()>0){
+            Map<String, List<ProductionProcesses>> orderProcess = lastProMerge.stream().collect(Collectors.groupingBy(ProductionProcesses::getProcessType));
+            orderProcess.forEach((k1,v1)->{
+                if(v1.size()>1){
+                    List<String> sameEqs = new ArrayList<>();
+                    boolean issameeq = true;
+                    for (ProductionProcesses productionProcesses : v1) {
+                        if(sameEqs.size() == 0){
+                            sameEqs.addAll(productionProcesses.getOptionalEquipments());
+                        }else{
+                            List<String> a = new ArrayList<>();
+                            for (String sameEq : sameEqs) {
+                                for (String optionalEquipment : productionProcesses.getOptionalEquipments()) {
+                                    if(sameEq.equals(optionalEquipment)){
+                                        a.add(sameEq);
                                     }
                                 }
                             }
-                            if(issameeq && sameEqs.size()>0){
-                                ProductionProcesses mergePro = v1.get(0);
-                                List<String> mergeProOrders = new ArrayList<>();
-                                if(mergePro.getMergeProOrders() != null && mergePro.getMergeProOrders().size()>0){
-                                    mergeProOrders.addAll(mergePro.getMergeProOrders());
+                            if(a.size()>0){
+                                sameEqs = a;
+                            }else{
+                                issameeq = false;
+                                break;
+                            }
+                        }
+                    }
+                    if(issameeq && sameEqs.size()>0){
+                        ProductionProcesses mergePro = v1.get(0);
+                        List<String> mergeProOrders = new ArrayList<>();
+                        if(mergePro.getMergeProOrders() != null && mergePro.getMergeProOrders().size()>0){
+                            mergeProOrders.addAll(mergePro.getMergeProOrders());
+                        }else{
+                            mergeProOrders.add(mergePro.getId());
+                        }
+                        mergePro.setMergeProOrders(mergeProOrders);
+                        mergePro.setOptionalEquipments(sameEqs);
+                        for (int i = 0; i < v1.size(); i++) {
+                            if(i>0){
+                                if(v1.get(i).getMergeProOrders() != null && v1.get(i).getMergeProOrders().size()>0){
+                                    mergePro.getMergeProOrders().addAll(v1.get(i).getMergeProOrders());
                                 }else{
-                                    mergeProOrders.add(mergePro.getId());
+                                    mergePro.getMergeProOrders().add(v1.get(i).getId());
                                 }
-                                mergePro.setMergeProOrders(mergeProOrders);
-                                mergePro.setOptionalEquipments(sameEqs);
-                                for (int i = 0; i < v1.size(); i++) {
-                                    if(i>0){
-                                        if(v1.get(i).getMergeProOrders() != null && v1.get(i).getMergeProOrders().size()>0){
-                                            mergePro.getMergeProOrders().addAll(v1.get(i).getMergeProOrders());
-                                        }else{
-                                            mergePro.getMergeProOrders().add(v1.get(i).getId());
-                                        }
-                                        // 设置待合并退火的主ID
-                                        v1.get(i).setMergeThMainId(v1.get(0).getId());
-                                        mergePro.setOpeProducePcNum(mergePro.getOpeProducePcNum()+v1.get(i).getOpeProducePcNum());
-                                        mergePro.setProduceTime(mergePro.getProduceTime()+v1.get(i).getProduceTime());
-                                        if(mergePro.getOrderMark()<v1.get(i).getOrderMark()){
-                                            mergePro.setOrderMark(v1.get(i).getOrderMark());
-                                        }
-                                        // 前后道工序关联关系设置
-                                        List<String> preids = new ArrayList<>();
-                                        preids.add(mainTh.getId());
-                                        List<ProductionProcesses> prepros = new ArrayList<>();
-                                        prepros.add(mainTh);
-                                        mergePro.setPreviousProcessesIds(preids);
-                                        mergePro.setPreviousProcesses(prepros);
-                                        String vid1 = v1.get(i).getId();
-                                        if(mainTh.getNextProcessesIds().contains(v1.get(i).getId())){
-                                            mainTh.getNextProcessesIds().remove(v1.get(i).getId());
-                                            List<ProductionProcesses> vidpros1 = mainTh.getNextProcesses().stream().filter(v2 -> !v2.getId().equals(vid1)).collect(Collectors.toList());
-                                            mainTh.setNextProcesses(vidpros1);
-                                        }
-                                        // 小卷退火卷数合并
-                                        if(v1.get(i).getProcessType().equals("小卷成退")){
-                                            v1.get(0).setMinThPcNum(v1.get(0).getMinThPcNum()+v1.get(i).getMinThPcNum());
-                                        }
-
-                                        List<ProductionProcesses> collect = apsSolution.getProcessesList().stream().filter(va -> !va.getId().equals(vid1)).collect(Collectors.toList());
-                                        apsSolution.setProcessesList(collect);
-                                    }
+                                // 设置待合并退火的主ID
+                                v1.get(i).setMergeThMainId(v1.get(0).getId());
+                                mergePro.setOpeProducePcNum(mergePro.getOpeProducePcNum()+v1.get(i).getOpeProducePcNum());
+                                mergePro.setProduceTime(mergePro.getProduceTime()+v1.get(i).getProduceTime());
+                                if(mergePro.getOrderMark()<v1.get(i).getOrderMark()){
+                                    mergePro.setOrderMark(v1.get(i).getOrderMark());
+                                }
+                                // 前后道工序关联关系设置
+                                List<String> preids = new ArrayList<>();
+                                preids.add(mainTh.getId());
+                                List<ProductionProcesses> prepros = new ArrayList<>();
+                                prepros.add(mainTh);
+                                mergePro.setPreviousProcessesIds(preids);
+                                mergePro.setPreviousProcesses(prepros);
+                                String vid1 = v1.get(i).getId();
+                                if(mainTh.getNextProcessesIds().contains(v1.get(i).getId())){
+                                    mainTh.getNextProcessesIds().remove(v1.get(i).getId());
+                                    List<ProductionProcesses> vidpros1 = mainTh.getNextProcesses().stream().filter(v2 -> !v2.getId().equals(vid1)).collect(Collectors.toList());
+                                    mainTh.setNextProcesses(vidpros1);
+                                }
+                                // 小卷退火卷数合并
+                                if(v1.get(i).getProcessType().equals("小卷成退")){
+                                    v1.get(0).setMinThPcNum(v1.get(0).getMinThPcNum()+v1.get(i).getMinThPcNum());
                                 }
+
+                                List<ProductionProcesses> collect = apsSolution.getProcessesList().stream().filter(va -> !va.getId().equals(vid1)).collect(Collectors.toList());
+                                apsSolution.setProcessesList(collect);
                             }
                         }
-                    });
+                    }
                 }
-                // 退火前一道工序合并
+            });
+        }
+    }
 
+    /**
+     * 小卷成退分组按宽度从大到小手动合并
+     * @param otherThproces
+     * @param allProMap
+     * @param apsSolution
+     */
+    /*private void xjctOrdersMerge(List<ProductionProcesses> otherThproces,Map<String,ProductionProcesses> allProMap,ApsSolution apsSolution){
+        // 小卷成退作业集合
+        List<ProductionProcesses> processesList = apsSolution.getProcessesList().stream().filter(v->"小卷成退".equals(v.getProcessType())).collect(Collectors.toList());
+        if(processesList != null && processesList.size()>0){
+            // 小卷成退分组
+            Map<String, List<ProductionProcesses>> ppss = processesList.stream().filter(v->StrUtil.isNotBlank(v.getGroupname()))
+                    .collect(Collectors.groupingBy(ProductionProcesses::getGroupname));
+            if(ppss != null && ppss.size()>0){
+                // 每一组待合并的工序
+                for(Map.Entry<String, List<ProductionProcesses>> entry : ppss.entrySet()){
+                    List<ProductionProcesses> processess = entry.getValue();
+                    // 每一组按照宽度从大到小合并
+                    Collections.sort(processess, (pro1, pro2) -> {return pro2.getVolumeWidth().compareTo(pro1.getVolumeWidth());});
+
+                }
             }
-        });
-    }
+        }
+    }*/
 
     private ProductionProcesses thOrdersMerge(List<ProductionProcesses> thps,List<ProductionProcesses> otherThproces
             ,Map<String,ProductionProcesses> allProMap,ApsSolution apsSolution){
@@ -591,6 +634,7 @@ public class ApsServiceImpl implements ApsService {
         if(thps != null && thps.size()>0){
             // 根据排程时间从大到小排序
             Collections.sort(thps,(v1,v2)->v2.getProduceTime().compareTo(v1.getProduceTime()));
+            mergePro = thps.get(0);
             for (int i = 0; i < thps.size(); i++) {
                 if(i>0){
                     // 设置待合并退火的主ID
@@ -603,6 +647,9 @@ public class ApsServiceImpl implements ApsService {
                             // 合并后关联关系重置
                             if(!prepro.getNextProcessesIds().contains(thps.get(0).getId())){
                                 int i1 = prepro.getNextProcessesIds().indexOf(thps.get(i).getId());
+                                if(i1 == -1){
+                                    int aa = 0;
+                                }
                                 prepro.getNextProcessesIds().set(i1,thps.get(0).getId());
                             }
                             List<String> list = new ArrayList<>();
@@ -653,6 +700,9 @@ public class ApsServiceImpl implements ApsService {
                             nextpro.setPreviousProcesses(nextprepros);
 
                             // 设置合并退火作业下一道工序
+                            if(thps.get(0).getNextProcessesIds() == null){
+                                thps.get(0).setNextProcessesIds(new ArrayList<>());
+                            }
                             thps.get(0).getNextProcessesIds().add(nextProcessesId);
                             List<ProductionProcesses> thnexts = new ArrayList<>();
                             for (String pid : thps.get(0).getNextProcessesIds()) {
@@ -665,6 +715,7 @@ public class ApsServiceImpl implements ApsService {
                     if(thps.get(i).getProcessType().equals("小卷成退")){
                         thps.get(0).setMinThPcNum(thps.get(0).getMinThPcNum()+thps.get(i).getMinThPcNum());
                     }
+                    mergePro.setTotalSinglerollweight(mergePro.getTotalSinglerollweight().add(thps.get(i).getTotalSinglerollweight()));
                     // 排程排序字段重新设置,取最小的排序字段值
                     if(thps.get(i).getOrderMark()<thps.get(0).getOrderMark()){
                         thps.get(0).setOrderMark(thps.get(i).getOrderMark());
@@ -675,9 +726,7 @@ public class ApsServiceImpl implements ApsService {
                 }
             }
             // 取第一个作业作为合并作业
-            mergePro = thps.get(0);
             mergePro.setVolumeWidth(mergePro.getVolumeWidth().multiply(new BigDecimal(thps.size())));
-            mergePro.setSinglerollweight(mergePro.getSinglerollweight().multiply(new BigDecimal(thps.size())));
             mergePro.setOpeProducePcNum(thps.size());
 
             // 重新设置前道工序的排序字段值
@@ -1013,10 +1062,17 @@ public class ApsServiceImpl implements ApsService {
         // 按订单分组
         Map<String, List<ProductionProcesses>> orderProcess = processes.stream().collect(Collectors.groupingBy(ProductionProcesses::getOrderId));
         orderProcess.forEach((orderId,orderpss)->{
+            if("6639310eb2204f77ac9d36a2f289039c".equals(orderId)){
+                int a = 1;
+            }
             // 退火作业集合
             Map<String,List<ProductionProcesses>> thproMap = new HashMap<>();
             for (ProductionProcesses process : orderpss) {
                 if("成退".equals(process.getProcessType()) || "中退".equals(process.getProcessType()) || "小卷成退".equals(process.getProcessType())){
+                    // 锁定的不参与合并
+                    /*if(process.getIfLock()){
+                        mergeprocesses.add(process);
+                    }else{*/
                     String bsproid = process.getBsProcessesId().get(0);
                     List<ProductionProcesses> bsprocess = thproMap.get(bsproid);
                     if(bsprocess == null){
@@ -1024,6 +1080,7 @@ public class ApsServiceImpl implements ApsService {
                     }
                     bsprocess.add(process);
                     thproMap.put(bsproid,bsprocess);
+//                    }
                 }else{
                     mergeprocesses.add(process);
 
@@ -1040,6 +1097,7 @@ public class ApsServiceImpl implements ApsService {
             // 退火合并
             if(thproMap != null && thproMap.size()>0){
                 thproMap.forEach((k,v)->{
+                    // 未锁定的退火合并
                     if(v.size()>1){
                         // 作业卷数大于1,合并退火
                         List<Equipment> equipments = equipmentList.stream().filter(eq -> eq.getId().equals(v.get(0).getOptionalEquipments().get(0))).collect(Collectors.toList());
@@ -1053,15 +1111,16 @@ public class ApsServiceImpl implements ApsService {
                                 if(b<a){
                                     a = b;
                                 }
-                            }
-                            // 最大装炉卷数
-                            Integer maxheatroll = v.get(0).getProduceOrder().get(0).getMaxheatroll();
-                            if(maxheatroll != null && maxheatroll < a){
-                                a = maxheatroll;
-                            }
-                            if(equipment.getEquipmentParameter() != null && equipment.getEquipmentParameter().getMaxfurance() != null && equipment.getEquipmentParameter().getMaxfurance()>0){
-                                if(equipment.getEquipmentParameter().getMaxfurance() < a){
-                                    a = equipment.getEquipmentParameter().getMaxfurance();
+
+                                // 最大装炉卷数
+                                Integer maxheatroll = v.get(0).getProduceOrder().get(0).getMaxheatroll();
+                                if(maxheatroll != null && maxheatroll < a){
+                                    a = maxheatroll;
+                                }
+                                if(equipment.getEquipmentParameter() != null && equipment.getEquipmentParameter().getMaxfurance() != null && equipment.getEquipmentParameter().getMaxfurance()>0){
+                                    if(equipment.getEquipmentParameter().getMaxfurance() < a){
+                                        a = equipment.getEquipmentParameter().getMaxfurance();
+                                    }
                                 }
                             }
 
@@ -1082,12 +1141,15 @@ public class ApsServiceImpl implements ApsService {
                             }
 
                             List<List<ProductionProcesses>> chunks = new ArrayList<>();
-                            if(v.get(0).getIfLock()){
-                                Collections.sort(v,(p1,p2)->{
+
+                            List<ProductionProcesses> lockpps = v.stream().filter(m -> m.getIfLock()).collect(Collectors.toList());
+                            List<ProductionProcesses> notlockpps = v.stream().filter(m -> !m.getIfLock()).collect(Collectors.toList());
+                            if(lockpps != null && lockpps.size()>0){
+                                Collections.sort(lockpps,(p1,p2)->{
                                     return p1.getStartTime().compareTo(p2.getStartTime());
                                 });
                                 Map<String, List<ProductionProcesses>> map = new TreeMap<>();
-                                for (ProductionProcesses productionProcesses : v) {
+                                for (ProductionProcesses productionProcesses : lockpps) {
                                     DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                                     String startStr = productionProcesses.getStartTime().format(formatter);
                                     if(map.containsKey(startStr)){
@@ -1103,10 +1165,11 @@ public class ApsServiceImpl implements ApsService {
                                         chunks.add(entry.getValue());
                                     }
                                 }
-                            }else {
-                                int listSize = v.size();
+                            }
+                            if(notlockpps != null && notlockpps.size()>0) {
+                                int listSize = notlockpps.size();
                                 for (int i = 0; i < listSize; i += a) {
-                                    chunks.add(v.subList(i, Math.min(i + a, listSize)));
+                                    chunks.add(notlockpps.subList(i, Math.min(i + a, listSize)));
                                 }
                             }
                             // 合并退火作业
@@ -1320,6 +1383,9 @@ public class ApsServiceImpl implements ApsService {
         // 根据订单分组
         Map<String, List<ProductionProcesses>> orderProcess = mergeprocesses.stream().collect(Collectors.groupingBy(ProductionProcesses::getOrderId));
         orderProcess.forEach((k,processes)->{
+            if("6639310eb2204f77ac9d36a2f289039c".equals(k)){
+                int a = 1;
+            }
             // 退火工序过滤
             List<ProductionProcesses> thpros = new ArrayList<>();
             // 所有单卷作业集合
@@ -1339,7 +1405,17 @@ public class ApsServiceImpl implements ApsService {
                 } else if (mergeprocess.getProcessType().equals("铸轧")) {
                     othermergeprocesses.add(mergeprocess);
                 } else {
-                    if(("是".equals(mergeprocess.getIssubsection()) && mergeprocess.getNextProcesses() != null && mergeprocess.getNextProcesses().size()>0) || mergeprocess.getIfLock()){
+                    boolean hasnotxjct = false;
+                    if(mergeprocess.getNextProcesses() != null && mergeprocess.getNextProcesses().size()>0){
+                        for (ProductionProcesses nextProcess : mergeprocess.getNextProcesses()) {
+                            if(!nextProcess.getProcessType().equals("小卷成退")){
+                                hasnotxjct = true;
+                                break;
+                            }
+                        }
+                    }
+                    if(("是".equals(mergeprocess.getIssubsection()) && mergeprocess.getNextProcesses() != null && mergeprocess.getNextProcesses().size()>0
+                            && hasnotxjct) || mergeprocess.getIfLock()){
                         othermergeprocesses.add(mergeprocess);
                     }else {
                         /*if(mergeprocess.getPreviousProcessesIds() == null || mergeprocess.getPreviousProcessesIds().size() == 0){
@@ -1358,7 +1434,9 @@ public class ApsServiceImpl implements ApsService {
             }
             // 两次退火卷数不一样、退火后道工序需要特殊处理
             List<ProductionProcesses> thNexts = new ArrayList<>();
-
+            /*if(othermergeprocesses != null && othermergeprocesses.size()>0){
+                hasMerge.addAll(othermergeprocesses.stream().map(ProductionProcesses::getId).collect(Collectors.toList()));
+            }*/
             // 根据退火合并作业
             if(thpros != null && thpros.size()>0){
                 for (ProductionProcesses thpro : thpros) {
@@ -1581,9 +1659,12 @@ public class ApsServiceImpl implements ApsService {
                     && !nextProcesses.get(0).getProcessType().equals("中退") && !nextProcesses.get(0).getProcessType().equals("小卷成退")
 //                    && nextProcesses.get(0).getPreviousProcessesIds() != null && nextProcesses.get(0).getPreviousProcessesIds().size()>0
             ){
+                // 判断是否存在锁定工序
+                List<ProductionProcesses> lockpps = nextProcesses.stream().filter(v -> v.getIfLock()).collect(Collectors.toList());
                 // 分卷工序
-                if(("是".equals(nextProcesses.get(0).getIssubsection()) && nextProcesses.get(0).getNextProcesses() != null && nextProcesses.get(0).getNextProcesses().size()>0)
-                        || nextProcesses.get(0).getIfLock()){
+                if(("是".equals(nextProcesses.get(0).getIssubsection()) && nextProcesses.get(0).getNextProcesses() != null && nextProcesses.get(0).getNextProcesses().size()>0
+                        && !nextProcesses.get(0).getNextProcesses().get(0).getProcessType().equals("小卷成退"))
+                        || (lockpps != null && lockpps.size()>0)){
                     for (ProductionProcesses nextProcess : nextProcesses) {
                         List<ProductionProcesses> pres = new ArrayList<>();
                         List<String> preids = new ArrayList<>();
@@ -1603,6 +1684,15 @@ public class ApsServiceImpl implements ApsService {
                             hasMerge.addAll(proids);
                         }
                     }
+                    // 判断合并后待排程的列表是否存在当前作业明细,不存在则新增
+                    if(nextProcesses != null && nextProcesses.size()>0){
+                        for (ProductionProcesses nextProcess : nextProcesses) {
+                            List<ProductionProcesses> pps = mergeprocesses.stream().filter(v -> v.getId().equals(nextProcess.getId())).collect(Collectors.toList());
+                            if(pps == null || pps.size() == 0){
+                                mergeprocesses.add(nextProcess);
+                            }
+                        }
+                    }
                 }else{
                     List<ProductionProcesses> mergePres = new ArrayList<>();
                     mergePres.addAll(nextProcesses);
@@ -1684,14 +1774,31 @@ public class ApsServiceImpl implements ApsService {
     private void thPreProMerge(ProductionProcesses thps,List<ProductionProcesses> notMergeProces,List<ProductionProcesses> mergeprocesses
             ,Map<String,List<ProductionProcesses>> bsProceses,List<String> hasMerge,Map<String,ProductionProcesses> allProMap,List<ProductionProcesses> thNexts){
         List<ProductionProcesses> previousProcesses = thps.getPreviousProcesses();
+
+        /*List<ProductionProcesses> pretestids = previousProcesses.stream().filter(v -> v.getId().equals("f24555b13a0349938e9fd72814c66199")).collect(Collectors.toList());
+        if(pretestids != null && pretestids.size()>0){
+            int a = 1;
+        }*/
         if(previousProcesses != null && previousProcesses.size()>0){
             // 铸轧、退火、和第一道工序不合并
             if(!previousProcesses.get(0).getProcessType().equals("铸轧") && !previousProcesses.get(0).getProcessType().equals("成退")
                     && !previousProcesses.get(0).getProcessType().equals("中退") && !previousProcesses.get(0).getProcessType().equals("小卷成退")
 //                    && previousProcesses.get(0).getPreviousProcessesIds() != null && previousProcesses.get(0).getPreviousProcessesIds().size()>0
             ){
+                //判断是否存在小卷成退其它的工序
+                boolean hasnotxjct = false;
+                if(previousProcesses.get(0).getNextProcesses() != null && previousProcesses.get(0).getNextProcesses().size()>0){
+                    for (ProductionProcesses nextProcess : previousProcesses.get(0).getNextProcesses()) {
+                        if(!nextProcess.getProcessType().equals("小卷成退")){
+                            hasnotxjct = true;
+                            break;
+                        }
+                    }
+                }
+                // 判断是否存在锁定工序
+                List<ProductionProcesses> lockpps = previousProcesses.stream().filter(v -> v.getIfLock()).collect(Collectors.toList());
                 // 分卷工序
-                if("是".equals(previousProcesses.get(0).getIssubsection()) || previousProcesses.get(0).getIfLock()){
+                if(("是".equals(previousProcesses.get(0).getIssubsection()) && hasnotxjct) || (lockpps != null && lockpps.size()>0)){
                     for (ProductionProcesses preProcess : previousProcesses) {
                         List<ProductionProcesses> nexts = new ArrayList<>();
                         if(preProcess.getNextProcesses().size()>1){
@@ -1719,6 +1826,15 @@ public class ApsServiceImpl implements ApsService {
                             hasMerge.addAll(proids);
                         }
                     }
+                    // 判断合并后待排程的列表是否存在当前作业明细,不存在则新增
+                    if(previousProcesses != null && previousProcesses.size()>0){
+                        for (ProductionProcesses previousProcess : previousProcesses) {
+                            List<ProductionProcesses> pps = mergeprocesses.stream().filter(v -> v.getId().equals(previousProcess.getId())).collect(Collectors.toList());
+                            if(pps == null || pps.size() == 0){
+                                mergeprocesses.add(previousProcess);
+                            }
+                        }
+                    }
                 }else{
                     List<ProductionProcesses> mergePres = new ArrayList<>();
                     mergePres.addAll(previousProcesses);
@@ -2125,6 +2241,9 @@ public class ApsServiceImpl implements ApsService {
                 // 已合并处理过作业ID
                 List<String> hasMid = new ArrayList<>();
                 for (ProductionProcesses rootPro : rootPros) {
+                    if(rootPro.getId().equals("deaf796d6e2b416dbc243201b9ad5795")){
+                        int a = 1;
+                    }
                     getLzBzSeries(rootPro,lzbzMerges,null,null,null,hasMid);
                 }
                 if(lzbzMerges != null && lzbzMerges.size()>0){
@@ -2394,7 +2513,7 @@ public class ApsServiceImpl implements ApsService {
             for (ProductionProcesses productionProcesses : zzprocesList) {
                 String cplx = productionProcesses.getProducttype().split("-")[0];
                 // 客户订单ID+合金+产品类型+宽度 作为合并的key
-                String keystr = productionProcesses.getProduceOrder().get(0).getCustomerOrderId()+productionProcesses.getVolumeMetal()+cplx+productionProcesses.getVolumeWidth();
+                String keystr = productionProcesses.getProduceOrder().get(0).getCustomerOrderId()+productionProcesses.getVolumeMetal()+productionProcesses.getProducttype()+cplx+productionProcesses.getVolumeWidth();
                 if(bsPros.containsKey(keystr)){
                     bsPros.get(keystr).add(productionProcesses);
                 }else{
@@ -2474,13 +2593,13 @@ public class ApsServiceImpl implements ApsService {
             // CPU核数
             String cores = Runtime.getRuntime().availableProcessors() + "";
             SolverFactory<ApsSolution> solverFactory1 = SolverFactory.create(new SolverConfig()
-                    .withEnvironmentMode(mode)
-                    .withSolutionClass(ApsSolution.class)
-                    .withEntityClasses(ProductionProcesses.class)
-                    .withConstraintProviderClass(ApsConstraintProvider.class)
+                            .withEnvironmentMode(mode)
+                            .withSolutionClass(ApsSolution.class)
+                            .withEntityClasses(ProductionProcesses.class)
+                            .withConstraintProviderClass(ApsConstraintProvider.class)
 //                    .withTerminationSpentLimit(Duration.ofSeconds(runPlanSeconds1))
-                    .withTerminationConfig(new TerminationConfig().withUnimprovedSecondsSpentLimit(120L))
-                    .withMoveThreadCount(cores)
+                            .withTerminationConfig(new TerminationConfig().withUnimprovedSecondsSpentLimit(120L))
+                            .withMoveThreadCount(cores)
             );
             Solver<ApsSolution> solver1 = solverFactory1.buildSolver();
             solver1.addEventListener(new SolverEventListener<ApsSolution>() {

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

@@ -44,6 +44,11 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
     @Override
     public ProductionScheduleRetVo productionLxSchedule(ProductionScheduleVo productionScheduleVo) throws Exception {
         log.info("*************** 排程开始(productionLxSchedule):"+productionScheduleVo.getProductionScheduleId()+" *******************");
+
+        List<ProductionProcesses> pdps = productionScheduleVo.getProcesses().stream().filter(v ->
+                v.getProduceOrder().get(0).getId().equals("eb93a65953e74dd8b73c3759acd208b2"))
+                .collect(Collectors.toList());
+        productionScheduleVo.setProcesses(pdps);
         // 排程结果对象
         ProductionScheduleRetVo productionScheduleRetVo = new ProductionScheduleRetVo();
         // optaplanner 求解器数据装配
@@ -69,6 +74,9 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
         List<ProductionProcesses> otherSerProcesMerges = apsService.seriesLzBzMerge(processesList, otherSerProces);
         apsSolution.setProcessesList(otherSerProcesMerges);
 
+        // 排程作业排序
+        sortProcess(apsSolution.getProcessesList(),1);
+
         // 去掉锁定工序
         List<ProductionProcesses> notLocks = new ArrayList<>();
         List<ProductionProcesses> hasLocks = new ArrayList<>();
@@ -83,11 +91,10 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
         }
         apsSolution.setProcessesList(notLocks);
 
-        // 排程作业排序
-        sortProcess(apsSolution.getProcessesList(),1);
         String cores = Runtime.getRuntime().availableProcessors() + "";
         SolverFactory<ApsSolution> solverFactory = SolverFactory.create(new SolverConfig()
-                        .withEnvironmentMode(EnvironmentMode.REPRODUCIBLE)
+//                        .withEnvironmentMode(EnvironmentMode.REPRODUCIBLE)
+                        .withEnvironmentMode(EnvironmentMode.FULL_ASSERT)
                         .withSolutionClass(ApsSolution.class)
                         .withEntityClasses(ProductionProcesses.class, Equipment.class)
                         .withConstraintProviderClass(ApsConstraintListProvider.class)
@@ -130,9 +137,17 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
                 }
             }
         });
-        List<Equipment> allequipmentList = apsSolution.getEquipmentList();
-        List<Equipment> equipments = allequipmentList.stream().filter(v -> v.getId().equals(apsSolution.getProcessesList().get(0).getOptionalEquipments().get(0))).collect(Collectors.toList());
-        equipments.get(0).setTasks(new ArrayList<>());
+        List<Equipment> equipments = new ArrayList<>();
+        for (ProductionProcesses productionProcesses : apsSolution.getProcessesList()) {
+            for (Equipment optionalProviderEquipment : productionProcesses.getOptionalProviderEquipments()) {
+                List<Equipment> esss = equipments.stream().filter(v -> v.getId().equals(optionalProviderEquipment.getId())).collect(Collectors.toList());
+                if(esss == null || esss.size() == 0){
+                    optionalProviderEquipment.setTasks(new ArrayList<>());
+                    equipments.add(optionalProviderEquipment);
+                }
+            }
+
+        }
         apsSolution.setEquipmentList(equipments);
         ApsSolution solvedBalance  = solver.solve(apsSolution);
         log.info("**************排程评分分析***************");
@@ -260,6 +275,7 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
             productionProcesses.setNextProcesses(null);
             productionProcesses.getEquipment().setProcessesList(null);
             productionProcesses.getEquipment().setEquipmentRunTimes(null);
+            productionProcesses.getEquipment().setTasks(null);
             productionProcesses.setPreviousStep(null);
             productionProcesses.setOptionalProviderEquipments(null);
             productionProcesses.setEquass(null);

+ 167 - 24
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/taskassigning/TaskStartTimeListenerTa.java

@@ -1,11 +1,13 @@
 package com.rongwei.rwapsserver.aps.taskassigning;
 
 import com.rongwei.rwapsserver.aps.domain.*;
+import com.rongwei.rwapsserver.aps.util.ObjectNull;
 import lombok.extern.slf4j.Slf4j;
 import org.optaplanner.core.api.domain.variable.ListVariableListener;
 import org.optaplanner.core.api.score.director.ScoreDirector;
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.stream.IntStream;
 
 /**
  * 设备赋值后任务起止时间自动计算
@@ -47,38 +49,23 @@ public class TaskStartTimeListenerTa implements ListVariableListener<ApsSolution
 
     @Override
     public void afterListVariableChanged(ScoreDirector<ApsSolution> scoreDirector, Equipment equipmentTa, int fromIndex, int toIndex) {
-        updateStartTime(scoreDirector, equipmentTa, fromIndex);
+//        updateStartTime(scoreDirector, equipmentTa, fromIndex,toIndex);
+        updateTaskStartTime(scoreDirector, equipmentTa, fromIndex);
     }
 
-    protected void updateStartTime(ScoreDirector<ApsSolution> scoreDirector, Equipment equipmentTa, int index) {
+    protected void updateStartTime(ScoreDirector<ApsSolution> scoreDirector, Equipment equipmentTa, int index, int toIndex) {
+        List<ProductionProcesses> allProcessesList = scoreDirector.getWorkingSolution().getProcessesList();
         List<ProductionProcesses> tasks = equipmentTa.getTasks();
+        if (ObjectNull.isNull(tasks) || tasks.size() - 1 < index) {
+            return;
+        }
+
         ProductionProcesses productionProcessesTa = tasks.get(index);
         LocalDateTime previousEndTime = index == 0 ? productionProcessesTa.getApsOverallConfig().getStartTime() : tasks.get(index - 1).getEndTime();
 
         for (int i = index; i < tasks.size(); i++) {
             ProductionProcesses t = tasks.get(i);
-            LocalDateTime previousLastEndTime = null;
-            if(t.getPreviousProcesses() != null && t.getPreviousProcesses().size()>0){
-                for (ProductionProcesses previousProcess : t.getPreviousProcesses()) {
-                    int lzTimes = 0;
-                    if(previousProcess.getEquipment().getWorkshopid() != null && previousProcess.getEquipment().getWorkshopid().equals(equipmentTa.getWorkshopid())){
-                        lzTimes = t.getApsOverallConfig().getRoamTime().get("WORKSHOP_IN");
-                    }else if(!previousProcess.getEquipment().getId().equals(equipmentTa.getId())){
-                        lzTimes = t.getApsOverallConfig().getRoamTime().get("WORKSHOP_CROSS");
-                    }
-
-                    if(previousLastEndTime == null){
-                        previousLastEndTime = previousProcess.getEndTime();
-                    }else{
-                        if(previousLastEndTime.compareTo(previousProcess.getEndTime().plusMinutes(lzTimes))<0){
-                            previousLastEndTime = previousProcess.getEndTime().plusMinutes(lzTimes);
-                        }
-                    }
-                }
-            }
-            if(t.getMinWaitTime() != null && t.getMinWaitTime()>0 && previousLastEndTime != null){
-                previousLastEndTime = previousLastEndTime.plusMinutes(t.getMinWaitTime());
-            }
+            LocalDateTime previousLastEndTime = getEarlyStartTime(t, equipmentTa,allProcessesList);
             if(previousLastEndTime != null && previousLastEndTime.compareTo(previousEndTime)>0){
                 previousEndTime = previousLastEndTime;
             }
@@ -87,7 +74,163 @@ public class TaskStartTimeListenerTa implements ListVariableListener<ApsSolution
                 t.setStartTime(previousEndTime);
                 scoreDirector.afterVariableChanged(t, "startTime");
             }
+
+            /*if(t.getNextProcesses() != null && t.getNextProcesses().size()>0){
+                for (ProductionProcesses nextProcess : t.getNextProcesses()) {
+
+                }
+            }*/
             previousEndTime = t.getEndTime();
         }
     }
+
+    /**
+     * 获取当前工序的最早开工时间
+     * @param t
+     * @param equipmentTa
+     * @return
+     */
+    private LocalDateTime getEarlyStartTime(ProductionProcesses t, Equipment equipmentTa,List<ProductionProcesses> allProcessesList){
+        LocalDateTime previousLastEndTime = null;
+        if(t.getPreviousProcesses() != null && t.getPreviousProcesses().size()>0){
+            for (ProductionProcesses previousProcess : t.getPreviousProcesses()) {
+                int lzTimes = 0;
+                if(previousProcess.getEquipment().getWorkshopid() != null && previousProcess.getEquipment().getWorkshopid().equals(equipmentTa.getWorkshopid())){
+                    lzTimes = t.getApsOverallConfig().getRoamTime().get("WORKSHOP_IN");
+                }else if(!previousProcess.getEquipment().getId().equals(equipmentTa.getId())){
+                    lzTimes = t.getApsOverallConfig().getRoamTime().get("WORKSHOP_CROSS");
+                }
+                // 最小等待时间对比流转时间
+                if(t.getMinWaitTime() != null && lzTimes<t.getMinWaitTime()){
+                    lzTimes = t.getMinWaitTime();
+                }
+                if(previousLastEndTime == null){
+                    previousLastEndTime = previousProcess.getEndTime().plusMinutes(lzTimes);
+                }else{
+                    if(previousLastEndTime.compareTo(previousProcess.getEndTime().plusMinutes(lzTimes))<0){
+                        previousLastEndTime = previousProcess.getEndTime().plusMinutes(lzTimes);
+                    }
+                }
+            }
+        }
+        if(t.getMinWaitTime() != null && t.getMinWaitTime()>0 && previousLastEndTime != null){
+            previousLastEndTime = previousLastEndTime.plusMinutes(t.getMinWaitTime());
+        }
+        return previousLastEndTime;
+    }
+
+    public void updateTaskStartTime(ScoreDirector<ApsSolution> scoreDirector, Equipment resource, int fromIndex) {
+        List<ProductionProcesses> allTasks = scoreDirector.getWorkingSolution().getProcessesList();
+        LocalDateTime beginTime = allTasks.get(0).getApsOverallConfig().getStartTime();
+        Set<String> historyQueueJobIds = new HashSet<>();
+        Queue<PendingChangeTaskDTO> uncheckedSuccessorQueue = new ArrayDeque<>();
+        PendingChangeTaskDTO pendingChangeTask = new PendingChangeTaskDTO()
+                .setResource(resource)
+                .setFromIndex(fromIndex);
+        uncheckedSuccessorQueue.add(pendingChangeTask);
+        while (!uncheckedSuccessorQueue.isEmpty()) {
+            PendingChangeTaskDTO update = uncheckedSuccessorQueue.remove();
+            List<ProductionProcesses> tasks = update.getResource().getTasks();
+            int fromIdx = update.getFromIndex();
+            if (ObjectNull.isNull(tasks) || tasks.size() - 1 < fromIdx) {
+                continue;
+            }
+            // 获取当前变更的任务,在当前资源前一个任务的时间
+            ProductionProcesses sourcePreviousTask = getSourcePreviousTask(tasks, fromIdx);
+            LocalDateTime sourceTaskPreviousEndTime = fromIdx == 0 ? beginTime : Optional.ofNullable(sourcePreviousTask).map(ProductionProcesses::getEndTime).orElseGet(() -> beginTime);
+            LocalDateTime resourcePreviousEndTime = sourceTaskPreviousEndTime;
+
+            Set<String> notUpdateFrontTaskIds = new HashSet<>();
+            for (int idx = fromIdx; idx < tasks.size(); idx++) {
+                ProductionProcesses t = tasks.get(idx);
+
+                historyQueueJobIds.add(t.getId());
+                if (t.getIfLock()) {
+                    resourcePreviousEndTime = t.getEndTime();
+                    continue;
+                }
+                // 改非前置任务的时间
+//                List<ProductionTask> currentResourcePreviousTasks = tasks.subList(0, idx);
+//                LocalDateTime startTime = calculateStartTime(update.getResource(), t, sourcePreviousTask, resourcePreviousEndTime, allTasks, currentResourcePreviousTasks);
+
+                LocalDateTime startTime = getEarlyStartTime(t, resource,allTasks);
+                if(resourcePreviousEndTime != null && resourcePreviousEndTime.compareTo(startTime)>0){
+                    startTime = resourcePreviousEndTime;
+                }
+                if (!notUpdateFrontTaskIds.contains(t.getId())) {
+                    scoreDirector.beforeVariableChanged(t, "startTime");
+                    t.setStartTime(startTime);
+                    scoreDirector.afterVariableChanged(t, "startTime");
+                    addChangedTaskIds(t, notUpdateFrontTaskIds);
+                    if (ObjectNull.isNotNull(startTime)) {
+                        resourcePreviousEndTime = t.getEndTime();
+                    }
+                    scoreDirector.getWorkingSolution().getProcessesList()
+                            .stream()
+//                            .filter(j -> ObjectNull.isNull(j.getMergeTaskCode()))
+                            .filter(j -> j.getPreviousProcessesIds() != null && j.getPreviousProcessesIds().contains(t.getId()))
+                            .filter(j -> j.getEquipment() != null)
+                            .forEach(nextJob -> {
+                                if (!historyQueueJobIds.contains(nextJob.getId())) {
+                                    List<ProductionProcesses> nextResourceTasks = nextJob.getEquipment().getTasks();
+                                    int optionalIndex = IntStream.range(0, nextResourceTasks.size()).filter(i -> nextResourceTasks.get(i).getId().equals(nextJob.getId())).findFirst().getAsInt();
+                                    PendingChangeTaskDTO updateNextJob = new PendingChangeTaskDTO()
+                                            .setResource(nextJob.getEquipment())
+                                            .setFromIndex(optionalIndex);
+                                    historyQueueJobIds.add(t.getId());
+                                    uncheckedSuccessorQueue.add(updateNextJob);
+                                }
+                            });
+                }
+            }
+        }
+    }
+
+    /**
+     * 找到指定下标前面第一个有结束时间的任务
+     *
+     * @param tasks     任务集合
+     * @param fromIndex 下标
+     * @return 下标前第一个有结束时间的任务
+     */
+    private ProductionProcesses getSourcePreviousTask(List<ProductionProcesses> tasks, int fromIndex) {
+        if (fromIndex == 0) {
+            return null;
+        }
+        ProductionProcesses previousTask = null;
+        for (int i = fromIndex - 1; i >= 0; i--) {
+            if (ObjectNull.isNotNull(previousTask)) {
+                break;
+            }
+            ProductionProcesses task = tasks.get(i);
+            if (ObjectNull.isNull(task.getEndTime())) {
+                continue;
+            }
+            previousTask = task;
+        }
+
+        return previousTask;
+    }
+
+    /**
+     * 记录已变更的任务id
+     *
+     * @param job         任务
+     * @param frontJobIds 前置任务id
+     */
+    private void addChangedTaskIds(ProductionProcesses job, Set<String> frontJobIds) {
+        if (job.getPreviousProcesses() == null) {
+            return;
+        }
+        ArrayDeque<ProductionProcesses> frontJobStack = new ArrayDeque<>();
+        job.getPreviousProcesses().forEach(frontJobStack::push);
+        while (!frontJobStack.isEmpty()) {
+            ProductionProcesses frontJob = frontJobStack.pop();
+            frontJobIds.add(frontJob.getId());
+            if (frontJob.getPreviousProcesses() != null) {
+                frontJob.getPreviousProcesses().forEach(frontJobStack::push);
+            }
+        }
+    }
+
 }

+ 82 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/util/ObjectNull.java

@@ -0,0 +1,82 @@
+package com.rongwei.rwapsserver.aps.util;
+
+import cn.hutool.core.util.ObjectUtil;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 多种方式判断是否为空,可以判断集合,对象,空串等
+ *
+ * @author guojing
+ * @describe
+ */
+public class ObjectNull {
+
+    /**
+     * 判断是否存在空对象 可以判断集合
+     * 如果存在一个为空的对象,即返回true
+     *
+     * @param objects 判断对象
+     * @return 判断结果
+     */
+    public static boolean isNull(Object... objects) {
+        if (objects == null) {
+            return true;
+        }
+        if (objects.length == 0) {
+            return true;
+        }
+        for (Object e : objects) {
+            if (ObjectUtil.isEmpty(e) || "".equals(e)) {
+                return true;
+            }
+            if (e instanceof Collection) {
+                Collection v = (Collection) e;
+                if (v.size() == 0) {
+                    return true;
+                }
+            }
+            if (e instanceof Map) {
+                return ((Map<?, ?>) e).isEmpty();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 是否不包含空对象,如果全部都不为空,则返回true|反之,任何一个为空,都返回false
+     *
+     * @param objects 判断对象
+     * @return 判断结果
+     */
+    public static boolean isNotNull(Object... objects) {
+        if (objects == null) {
+            return false;
+        }
+        return Arrays.stream(objects).allMatch(e -> !ObjectNull.isNull(e));
+    }
+
+    /**
+     * 只要存在一个不为空的数据,即返回true
+     *
+     * @param objects 判断对象
+     * @return 判断结果
+     */
+    public static boolean isNotNullOne(Object... objects) {
+        return Arrays.stream(objects).anyMatch(e -> ObjectUtil.isNotNull(e) && !"".equals(e));
+    }
+
+    /**
+     * 过滤所有不为空的数据
+     *
+     * @param objects 判断对象
+     * @return 判断结果
+     */
+    public static List filterNull(Object... objects) {
+        return Arrays.stream(objects).filter(e -> ObjectUtil.isNotNull(e) && !"".equals(e)).collect(Collectors.toList());
+    }
+}

+ 1 - 1
rw-aps-server/src/test/java/com/rongwei/rwapsserver/RwApsServerApplicationTests.java

@@ -60,7 +60,7 @@ class RwApsServerApplicationTests {
     private void test2(){
         String cores = Runtime.getRuntime().availableProcessors() + "";
         SolverFactory<TaskAssigningSolution> solverFactory = SolverFactory.create(new SolverConfig()
-                        .withEnvironmentMode(EnvironmentMode.REPRODUCIBLE)
+                        .withEnvironmentMode(EnvironmentMode.FULL_ASSERT)
                         .withSolutionClass(TaskAssigningSolution.class)
                         .withEntityClasses(Task.class, Employee.class)
                         .withConstraintProviderClass(TaskAssigningConstraintProvider.class)