Kaynağa Gözat

新模型合并

fangpy 4 ay önce
ebeveyn
işleme
d51de7042d

+ 23 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/taskassigning/service/DdApsTaService.java

@@ -0,0 +1,23 @@
+package com.rongwei.rwapsserver.aps.taskassigning.service;
+
+import com.rongwei.rwapsserver.aps.domain.ApsFurnaceInstallationDo;
+import com.rongwei.rwapsserver.aps.domain.ApsSolution;
+import com.rongwei.rwapsserver.aps.domain.Equipment;
+import com.rongwei.rwapsserver.aps.taskassigning.tado.EquipmentTa;
+import com.rongwei.rwapsserver.aps.taskassigning.tado.ProductionProcessesTa;
+import com.rongwei.rwapsserver.aps.vo.ProductionScheduleVo;
+
+import java.util.List;
+import java.util.Map;
+
+public interface DdApsTaService {
+
+    List<ProductionProcessesTa> thOtherAllMerge(List<ProductionProcessesTa> mergeprocesses, List<ProductionProcessesTa> otherThproces);
+    
+    List<ProductionProcessesTa> thOtherMerge(List<ProductionProcessesTa> mergeprocesses, List<ProductionProcessesTa> otherThproces);
+
+    List<ProductionProcessesTa> seriesLzBzMerge(List<ProductionProcessesTa> mergeprocesses, List<ProductionProcessesTa> otherSerProces);
+
+    void equipmentRunTimeMerge(EquipmentTa equipment, List<ApsFurnaceInstallationDo> furnaceInstallations);
+
+}

+ 10 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/taskassigning/service/ProductionScheduleTaService.java

@@ -0,0 +1,10 @@
+package com.rongwei.rwapsserver.aps.taskassigning.service;
+
+import com.rongwei.rwapsserver.aps.taskassigning.vo.ProductionScheduleRetTaVo;
+import com.rongwei.rwapsserver.aps.taskassigning.vo.ProductionScheduleTaVo;
+
+public interface ProductionScheduleTaService {
+
+    ProductionScheduleRetTaVo productionSchedule(ProductionScheduleTaVo productionScheduleVo) throws Exception;
+
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1020 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/taskassigning/service/impl/DdApsTaServiceImpl.java


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1382 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/taskassigning/service/impl/ProductionScheduleTaServiceImpl.java


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

@@ -0,0 +1,862 @@
+package com.rongwei.rwapsserver.aps.taskassigning.tado;
+
+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.stream.*;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+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 ApsConstraintListProvider 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),
+                seriesProduce(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){
+                        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;
+                    }
+                    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 100;
+                })
+                .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 seriesProduce(ConstraintFactory constraintFactory){
+        return constraintFactory.forEach(EquipmentTa.class)
+                .filter((equipment) -> {
+                    if(equipment.getTasks() != null && equipment.getTasks().size()>0){
+                        String processType = equipment.getTasks().get(0).getProcessType();
+                        if(processType.equals("冷轧") || processType.equals("箔轧")){
+                            for (ProductionProcessesTa process : equipment.getTasks()) {
+                                process.getConflictRoptions().remove("soft-seriesProduceZz");
+                                process.getConflictRoptions().remove("soft-seriesProduceLz");
+                            }
+                            return true;
+                        }else{
+                            return false;
+                        }
+                    }else{
+                        return false;
+                    }
+                    /*int countNum = seriesProduceCount(processes);
+                    if(countNum>0){
+                        return true;
+                    }
+                    return false;*/
+                })
+                .penalize(HardMediumSoftScore.ONE_MEDIUM,(equipment)->{
+                    int countNum = seriesProduceCount(equipment.getTasks());
+                    return countNum;
+                })
+                .asConstraint("seriesProduce");
+
+    }
+
+    private int seriesProduceCount(List<ProductionProcessesTa> processes){
+        int b = 0;
+        EquipmentTa equipment = null;
+        List<ApsNochangeRollerDo> apsNochangeRollerDos = null;
+        if(processes != null && processes.size()>0){
+            equipment = processes.get(0).getEquipment();
+            apsNochangeRollerDos = processes.get(0).getApsOverallConfig().getApsNochangeRollerDos();
+        }
+        if(equipment.getId().equals("0001be252874536843730b100024")){
+            int abc = 1;
+            if(processes.get(0).getDelay() == 270){
+                int abcd = 1;
+            }
+        }
+        // 设置作业辊
+        for (ProductionProcessesTa process : processes) {
+            if(process.getOptionalEquipmentZg() != null && process.getOptionalEquipmentZg().size()>0){
+                process.setProZg(process.getOptionalEquipmentZg().get(equipment.getId()));
+            }
+        }
+
+        List<ProductionProcessesTa> hasStartTimeProcess = processes.stream().filter(v -> v.getStartTime() != null).collect(Collectors.toList());
+        if(hasStartTimeProcess != null && hasStartTimeProcess.size()>0){
+            /*if(hasStartTimeProcess.get(0).getApsStatus().equals("CH") && hasStartTimeProcess.get(0).getProcessType().equals("冷轧")){
+                return b;
+            }*/
+            // 设备占用时间参与连续生产排程
+            if(equipment != null && equipment.getEquipmentRunTimes() != null && equipment.getEquipmentRunTimes().size()>0){
+                for (EquipmentRunTime equipmentRunTime : equipment.getEquipmentRunTimes()) {
+                    if(equipmentRunTime.getOccupyType().equals("process")){
+                        ProductionProcessesTa pp = new ProductionProcessesTa();
+                        pp.setStartTime(equipmentRunTime.getStartRunTime());
+                        pp.setSeriesProduceMark(equipmentRunTime.getSeriesProduceMark());
+                        pp.setProcessType(equipmentRunTime.getProcessType());
+                        pp.setSinglerollweight(equipmentRunTime.getTotalSinglerollweight());
+                        pp.setLastSerialLbWeight(equipmentRunTime.getLastSerialLbWeight());
+                        pp.setBsProcessesId(Arrays.asList(new String[]{"haspcprocess"}));
+                        pp.setConflictRoptions(new HashMap<>());
+                        pp.setProZg(equipmentRunTime.getZjgid());
+                        hasStartTimeProcess.add(pp);
+                    }
+                }
+            }
+            // 按照开始时间排序
+            Collections.sort(hasStartTimeProcess, Comparator.comparing(ProductionProcessesTa::getSeriSort));
+            // 获取设备已排程好的最后一个作业,计算连续加工
+            if(equipment.getLastProcessType() != null && equipment.getLastSeriesProduceMark() != null){
+                ProductionProcessesTa pp = new ProductionProcessesTa();
+                pp.setSeriesProduceMark(equipment.getLastSeriesProduceMark());
+                pp.setProcessType(equipment.getLastProcessType());
+                pp.setSinglerollweight(equipment.getLastSinglerollweight());
+                pp.setLastSerialLbWeight(equipment.getLastSerialLbWeight());
+                pp.setBsProcessesId(Arrays.asList(new String[]{"lastprocess"}));
+                pp.setConflictRoptions(new HashMap<>());
+                pp.setProZg(equipment.getLastZjgid());
+                hasStartTimeProcess.add(0,pp);
+            }else{
+                if(equipment.getEquipmentRunTimes() == null || equipment.getEquipmentRunTimes().size() == 0){
+                    b = b+2;
+                }
+            }
+            // 周期铸轧立板已连续生产吨数
+            BigDecimal zqlb = new BigDecimal("0");
+            for(int i=0;i<hasStartTimeProcess.size();i++){
+                if(i == hasStartTimeProcess.size()-1){
+                    if(hasStartTimeProcess.get(i).getId() == null){
+                        continue;
+                    }else{
+                        if("铸轧".equals(hasStartTimeProcess.get(i).getProcessType())){
+//                            b = b+15;
+                        }else if ("冷轧".equals(hasStartTimeProcess.get(i).getProcessType())) {
+                            b = b+10;
+                        }else if ("箔轧".equals(hasStartTimeProcess.get(i).getProcessType())) {
+                            b = b+5;
+                        }
+
+                    }
+                }else{
+                    if(hasStartTimeProcess.get(i).getId() == null && hasStartTimeProcess.get(i+1).getId() == null){
+                        continue;
+                    }
+                    if(hasStartTimeProcess.get(i).getSeriesProduceMark() != null && hasStartTimeProcess.get(i+1).getSeriesProduceMark() != null){
+                        Map<String, String> conflictRoptions1 = hasStartTimeProcess.get(i).getConflictRoptions();
+                        Map<String, String> conflictRoptions2 = hasStartTimeProcess.get(i+1).getConflictRoptions();
+                        if("铸轧".equals(hasStartTimeProcess.get(i).getProcessType())){
+                            if(hasStartTimeProcess.get(i+1).getId() == null){
+                                continue;
+                            }
+                            String[] serspre = hasStartTimeProcess.get(i).getSeriesProduceMark().split("\\^_\\^");
+                            String[] sersafter = hasStartTimeProcess.get(i+1).getSeriesProduceMark().split("\\^_\\^");
+                            // 获取产品类型二级
+                            String bfcplx = serspre[1].split("-")[0];
+                            // 获取产品类型二级
+                            String afcplx = sersafter[1].split("-")[0];
+                            // 周期立板总重量统计
+                            if(zqlb.compareTo(new BigDecimal("0")) == 0){
+                                if(hasStartTimeProcess.get(i).getLastSerialLbWeight() != null){
+                                    zqlb = hasStartTimeProcess.get(i).getLastSerialLbWeight();
+                                }else{
+                                    zqlb = hasStartTimeProcess.get(i).getSinglerollweight();
+                                }
+                            }else{
+                                zqlb = zqlb.add(hasStartTimeProcess.get(i).getSinglerollweight());
+                            }
+                            // 铸轧立板是否连续
+                            boolean lbserice = true;
+                            if(serspre.length == 5 && sersafter.length == 5){
+                                // 铸轧换辊兼容规则
+                                if(!zzSeriesJr(hasStartTimeProcess.get(i),hasStartTimeProcess.get(i+1),apsNochangeRollerDos)){
+                                    b = b+15;
+                                    if(hasStartTimeProcess.get(i).getId() != null){
+                                        conflictRoptions1.put("soft-seriesProduceZz",conflictRoptions1.get("soft-seriesProduceZz") == null ? "和后一道工序违反换辊和立板的连续约束" : conflictRoptions1.get("soft-seriesProduceZz")+";和后一道工序违反换辊和立板的连续约束");
+                                    }else{
+                                        conflictRoptions2.put("soft-seriesProduceZz","和前一道工序违反换辊和立板的连续约束");
+                                    }
+                                    zqlb = new BigDecimal("0");
+                                    lbserice = false;
+                                }else{
+                                    // 合金相同情况下后面的宽度大于前面的宽度需要换辊和立板
+                                    // 合金相同情况下后面的宽度小于前面的宽度需要立板
+                                    // 合金相同情况下后面的宽度等于前面的宽度换辊和立板都不需要
+                                    String s1 = serspre[2];
+                                    String s2 = sersafter[2];
+                                    try{
+                                        BigDecimal i1 = new BigDecimal(s1);
+                                        BigDecimal i2 = new BigDecimal(s2);
+                                        if(i1.compareTo(i2)<0){
+                                            b = b+15;
+                                            if(hasStartTimeProcess.get(i).getId() != null){
+                                                conflictRoptions1.put("soft-seriesProduceZz",conflictRoptions1.get("soft-seriesProduceZz") == null ? "和后一道工序违反换辊和立板的连续约束" : conflictRoptions1.get("soft-seriesProduceZz")+";和后一道工序违反换辊和立板的连续约束");
+                                            }else{
+                                                conflictRoptions2.put("soft-seriesProduceZz","和前一道工序违反换辊和立板的连续约束");
+                                            }
+                                            zqlb = new BigDecimal("0");
+                                            lbserice = false;
+                                        }else if(i1.compareTo(i2)>0){
+                                            b = b+8;
+                                            if(hasStartTimeProcess.get(i).getId() != null){
+                                                conflictRoptions1.put("soft-seriesProduceZz",conflictRoptions1.get("soft-seriesProduceZz") == null ? "和后一道工序违反立板的连续约束" : conflictRoptions1.get("soft-seriesProduceZz")+";和后一道工序违反立板的连续约束");
+                                            }else{
+                                                conflictRoptions2.put("soft-seriesProduceZz","和前一道工序违反立板的连续约束");
+                                            }
+                                            zqlb = new BigDecimal("0");
+                                            lbserice = false;
+                                        }else{
+                                            // 二级产品类型不一样的也需要立板
+                                            if(!bfcplx.equals(afcplx)){
+                                                b = b+8;
+                                                if(hasStartTimeProcess.get(i).getId() != null){
+                                                    conflictRoptions1.put("soft-seriesProduceZz",conflictRoptions1.get("soft-seriesProduceZz") == null ? "和后一道工序违反立板的连续约束" : conflictRoptions1.get("soft-seriesProduceZz")+";和后一道工序违反立板的连续约束");
+                                                }else{
+                                                    conflictRoptions2.put("soft-seriesProduceZz","和前一道工序违反立板的连续约束");
+                                                }
+                                                zqlb = new BigDecimal("0");
+                                                lbserice = false;
+                                            }
+                                        }
+                                    }catch (Exception e){
+                                        e.printStackTrace();
+                                    }
+                                }
+                            }
+                        } else if ("冷轧".equals(hasStartTimeProcess.get(i).getProcessType()) || "箔轧".equals(hasStartTimeProcess.get(i).getProcessType())) {
+                            ProductionProcessesTa prepro = hasStartTimeProcess.get(i);
+                            ProductionProcessesTa nextpro = hasStartTimeProcess.get(i+1);
+                            String[] serspre = hasStartTimeProcess.get(i).getSeriesProduceMark().split("\\^_\\^");
+                            String[] sersafter = hasStartTimeProcess.get(i+1).getSeriesProduceMark().split("\\^_\\^");
+                            // 前道工序宽度、输入物料厚度、输出物料厚度
+                            String s1 = serspre[2];
+                            BigDecimal t1 = new BigDecimal(serspre[3]);
+                            BigDecimal to1 = new BigDecimal(serspre[4]);
+                            // 后道工序宽度、输入物料厚度、输出物料厚度
+                            String s2 = sersafter[2];
+                            BigDecimal t2 = new BigDecimal(sersafter[3]);
+                            BigDecimal to2 = new BigDecimal(sersafter[4]);
+                            BigDecimal i1 = new BigDecimal(s1);
+                            BigDecimal i2 = new BigDecimal(s2);
+                            // 前后道所属作业ID
+                            String processId1 = hasStartTimeProcess.get(i).getBsProcessesId().get(0);
+                            String processId2 = hasStartTimeProcess.get(i+1).getBsProcessesId().get(0);
+                            // 判断原有前后两道历史工序是否连续
+                            boolean ispreafterLx = false;
+                            if(prepro.getId() == null && nextpro.getId() != null && hasStartTimeProcess.size()>i+2
+                                    && hasStartTimeProcess.get(i+2).getId() == null){
+                                ProductionProcessesTa nextnextpro = hasStartTimeProcess.get(i + 2);
+                                if(StrUtil.isNotBlank(prepro.getProZg()) && StrUtil.isNotBlank(nextnextpro.getProZg())){
+                                    if(prepro.getProZg().equals(nextnextpro.getProZg())){
+                                        String[] sersafterafter = nextnextpro.getSeriesProduceMark().split("\\^_\\^");
+                                        BigDecimal i3 = new BigDecimal(sersafterafter[2]);
+                                        if(i1.compareTo(i3)>=0){
+                                            ispreafterLx = true;
+                                        }
+                                    }
+                                }
+                            } else if (prepro.getId() != null && nextpro.getId() == null && i>0
+                                    && hasStartTimeProcess.get(i-1).getId() == null) {
+                                ProductionProcessesTa preprepro = hasStartTimeProcess.get(i-1);
+                                if(StrUtil.isNotBlank(preprepro.getProZg()) && StrUtil.isNotBlank(nextpro.getProZg())){
+                                    if(preprepro.getProZg().equals(nextpro.getProZg())){
+                                        String[] sersprepre = preprepro.getSeriesProduceMark().split("\\^_\\^");
+                                        BigDecimal i0 = new BigDecimal(sersprepre[2]);
+                                        if(i0.compareTo(i2)>=0){
+                                            ispreafterLx = true;
+                                        }
+                                    }
+                                }
+                            }
+
+                            if(StrUtil.isNotBlank(prepro.getProZg()) && StrUtil.isNotBlank(nextpro.getProZg())){
+                                String preZg = prepro.getProZg();
+                                String nextZg = nextpro.getProZg();
+                                if(!preZg.equals(nextZg)){
+                                    b = b+10;
+                                    if(hasStartTimeProcess.get(i).getId() != null){
+                                        conflictRoptions1.put("soft-seriesProduceLz",conflictRoptions1.get("soft-seriesProduceLz") == null ? "和后一道工序违反换辊的连续约束" : conflictRoptions1.get("soft-seriesProduceLz")+";和后一道工序违反换辊的连续约束");
+                                    }else{
+                                        conflictRoptions2.put("soft-seriesProduceLz","和前一道工序违反换辊的连续约束");
+                                    }
+                                }else{
+                                    if((i1.add(new BigDecimal("30"))).compareTo(i2)<0){
+                                        b = b+8;
+                                        if(ispreafterLx){
+                                            b = b+8;
+                                        }
+                                        if(hasStartTimeProcess.get(i).getId() != null){
+                                            conflictRoptions1.put("soft-seriesProduceLz",conflictRoptions1.get("soft-seriesProduceLz") == null ? "和后一道工序违反换辊的连续约束" : conflictRoptions1.get("soft-seriesProduceLz")+";和后一道工序违反换辊的连续约束");
+                                        }else{
+                                            conflictRoptions2.put("soft-seriesProduceLz","和前一道工序违反换辊的连续约束");
+                                        }
+                                    }else if(i1.compareTo(i2)<0 && (i1.add(new BigDecimal("30"))).compareTo(i2)>=0){
+                                        b = b+7;
+                                        if(ispreafterLx){
+                                            b = b+8;
+                                        }
+                                        if(hasStartTimeProcess.get(i).getId() != null){
+                                            conflictRoptions1.put("soft-seriesProduceLz",conflictRoptions1.get("soft-seriesProduceLz") == null ? "和后一道工序违反换辊的连续约束" : conflictRoptions1.get("soft-seriesProduceLz")+";和后一道工序违反换辊的连续约束");
+                                        }else{
+                                            conflictRoptions2.put("soft-seriesProduceLz","和前一道工序违反换辊的连续约束");
+                                        }
+                                    }else if(i1.compareTo(i2)>0){
+                                        b = b+3;
+                                    }
+                                }
+                            }else{
+                                if(serspre.length == 5 && sersafter.length == 5){
+                                    try{
+                                        // 后端工序大于前道工序,并且大于30mm
+                                        if((i1.add(new BigDecimal("30"))).compareTo(i2)<0){
+                                            b = b+10;
+                                            if(hasStartTimeProcess.get(i).getId() != null){
+                                                conflictRoptions1.put("soft-seriesProduceLz",conflictRoptions1.get("soft-seriesProduceLz") == null ? "和后一道工序违反换辊的连续约束" : conflictRoptions1.get("soft-seriesProduceLz")+";和后一道工序违反换辊的连续约束");
+                                            }else{
+                                                conflictRoptions2.put("soft-seriesProduceLz","和前一道工序违反换辊的连续约束");
+                                            }
+                                        }
+                                        // 后端工序大于前道工序,并且小于30mm
+                                        else if(i1.compareTo(i2)<0 && (i1.add(new BigDecimal("30"))).compareTo(i2)>=0){
+                                            b = b+8;
+                                            if(hasStartTimeProcess.get(i).getId() != null){
+                                                conflictRoptions1.put("soft-seriesProduceLz",conflictRoptions1.get("soft-seriesProduceLz") == null ? "和后一道工序违反换辊的连续约束" : conflictRoptions1.get("soft-seriesProduceLz")+";和后一道工序违反换辊的连续约束");
+                                            }else{
+                                                conflictRoptions2.put("soft-seriesProduceLz","和前一道工序违反换辊的连续约束");
+                                            }
+                                        }
+                                        // 后端工序小于前道工序
+                                    /*else if(i1.compareTo(i2)>0){
+                                        b = b+5;
+                                    }*/
+                                        // 后端工序小于等于前道工序的情况下
+                                        else{
+                                            if(!serspre[1].equals(sersafter[1])){
+                                                b = b+5;
+                                                if(hasStartTimeProcess.get(i).getId() != null){
+                                                    conflictRoptions1.put("soft-seriesProduceLz",conflictRoptions1.get("soft-seriesProduceLz") == null ? "和后一道工序违反换辊的连续约束" : conflictRoptions1.get("soft-seriesProduceLz")+";和后一道工序违反换辊的连续约束");
+                                                }else{
+                                                    conflictRoptions2.put("soft-seriesProduceLz","和前一道工序违反换辊的连续约束");
+                                                }
+                                            }else{
+                                                if(i1.compareTo(i2)>0){
+                                                    b = b+2;
+                                                }
+                                            }
+                                            // 输入厚度不同
+                                        /*if(t1.compareTo(t2) != 0){
+                                            b = b+4;
+                                        }else{
+                                            // 输出厚度不同
+                                            if(to1.compareTo(to2) != 0){
+                                                b = b+3;
+                                            }else{
+                                                // 不属于同一作业
+                                                if(!processId1.equals(processId2)){
+                                                    b = b+1;
+                                                }
+                                            }
+                                        }*/
+                                        }
+                                    }catch (Exception e){
+                                        e.printStackTrace();
+                                    }
+                                }
+                            }
+                        }
+                        /*else if ("箔轧".equals(hasStartTimeProcess.get(i).getProcessType())) {
+                            // 宽度从大到小
+                            String[] serspre = hasStartTimeProcess.get(i).getSeriesProduceMark().split("\\^_\\^");
+                            String[] sersafter = hasStartTimeProcess.get(i+1).getSeriesProduceMark().split("\\^_\\^");
+                            if(serspre.length == 5 && sersafter.length == 5){
+                                String s1 = serspre[2];
+                                String s2 = sersafter[2];
+                                try {
+                                    BigDecimal volumeWidth = new BigDecimal(s1);
+                                    BigDecimal volumeWidth1 = new BigDecimal(s2);
+                                    if(volumeWidth.compareTo(volumeWidth1)<0){
+                                        b = b+5;
+                                        if(hasStartTimeProcess.get(i).getId() != null){
+                                            conflictRoptions1.put("soft-seriesProduceLz",conflictRoptions1.get("soft-seriesProduceLz") == null ? "和后一道工序违反换辊的连续约束" : conflictRoptions1.get("soft-seriesProduceLz")+";和后一道工序违反换辊的连续约束");
+                                        }else{
+                                            conflictRoptions2.put("soft-seriesProduceLz","和前一道工序违反换辊的连续约束");
+                                        }
+                                    } else if (volumeWidth.compareTo(volumeWidth1)<0) {
+                                        b = b+1;
+                                    }
+                                }catch (Exception e){
+                                    e.printStackTrace();
+                                }
+                            }
+                        }*/
+                    }
+                }
+            }
+        }
+        return b;
+    }
+
+    /**
+     * 铸轧判断合金产品类型是否兼容
+     * @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().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");
+    }
+
+}

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

@@ -0,0 +1,99 @@
+package com.rongwei.rwapsserver.aps.taskassigning.tado;
+
+import com.rongwei.rwapsserver.aps.domain.ApsAbstractPersistable;
+import lombok.*;
+import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
+import org.optaplanner.core.api.domain.solution.PlanningScore;
+import org.optaplanner.core.api.domain.solution.PlanningSolution;
+import org.optaplanner.core.api.domain.solution.ProblemFactCollectionProperty;
+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.score.buildin.hardmediumsoft.HardMediumSoftScore;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@NoArgsConstructor
+@PlanningSolution
+public class ApsSolutionTa extends ApsAbstractPersistable {
+
+    /*@Getter(value = AccessLevel.NONE)
+    private List<ProductionProcessesTa> processesList;
+
+    @Getter(value = AccessLevel.NONE)
+    private List<EquipmentTa> equipmentList;*/
+
+    @ValueRangeProvider
+    @ProblemFactCollectionProperty
+    private List<ProductionProcessesTa> processesList;
+
+    @PlanningEntityCollectionProperty
+    private List<EquipmentTa> equipmentList;
+
+    @PlanningScore
+    private HardMediumSoftScore score;
+
+    private Long incrementUnitAmount;
+
+    public LocalDateTime startTime;
+    // 每一步骤最优规划实体
+    private List<ProductionProcessesTa> stepBestProcessesList;
+    // 是否初始结束
+    private boolean constructionHeuristicEnd;
+
+    public ApsSolutionTa(String id, List<ProductionProcessesTa> processesList, List<EquipmentTa> equipmentList) {
+        super(id);
+        this.processesList = processesList;
+        this.equipmentList = equipmentList;
+    }
+
+//    @ValueRangeProvider(id="taskRange")
+//    @PlanningEntityCollectionProperty
+    public List<ProductionProcessesTa> getProcessesList() {
+        return processesList;
+    }
+
+//    @ProblemFactCollectionProperty
+    public List<EquipmentTa> getEquipmentList() {
+        return equipmentList;
+    }
+
+//    @ValueRangeProvider(id="timeRange")
+    public CountableValueRange<LocalDateTime> getxRange(){
+        Long produceTimeTotal = 0L;
+        for (ProductionProcessesTa productionProcesses : processesList) {
+            produceTimeTotal = produceTimeTotal + productionProcesses.getProduceTime();
+        }
+        LocalDateTime fromTime = LocalDateTime.now();
+        if(startTime != null){
+            fromTime = startTime;
+        }
+        Long incrementUnit = this.incrementUnitAmount;
+        if(incrementUnit == null){
+            incrementUnit = 5L;
+        }
+
+        long sy = produceTimeTotal % incrementUnit;
+        if(sy>0){
+            produceTimeTotal = produceTimeTotal - sy;
+        }
+
+        System.out.println("startTime:"+startTime);
+        return ValueRangeFactory.createLocalDateTimeValueRange(fromTime,fromTime.plusMinutes(produceTimeTotal),incrementUnit, ChronoUnit.MINUTES);
+    }
+
+    public HardMediumSoftScore getScore() {
+        return score;
+    }
+
+    public Long getIncrementUnitAmount() {
+        return incrementUnitAmount;
+    }
+
+    public void setIncrementUnitAmount(Long incrementUnitAmount) {
+        this.incrementUnitAmount = incrementUnitAmount;
+    }
+}

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

@@ -0,0 +1,161 @@
+package com.rongwei.rwapsserver.aps.taskassigning.tado;
+
+import com.rongwei.rwapsserver.aps.domain.EquipmentParameter;
+import com.rongwei.rwapsserver.aps.domain.EquipmentRunTime;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.optaplanner.core.api.domain.entity.PlanningEntity;
+import org.optaplanner.core.api.domain.lookup.PlanningId;
+import org.optaplanner.core.api.domain.variable.PlanningListVariable;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 设备
+ */
+@NoArgsConstructor
+@Data
+@PlanningEntity
+public class EquipmentTa implements Serializable {
+
+    private String id;
+
+    @PlanningId
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @PlanningListVariable
+    private List<ProductionProcessesTa> tasks;
+
+    /**
+     * 业务表订单工序ID
+     */
+    private String bsEquipmentId;
+
+    /**
+     * 设备名称
+     */
+    private String equipmentName;
+
+    /**
+     * 设备类型
+     */
+    private String equipmentType;
+
+    /**
+     * 设备优先级
+     */
+    private Integer eqOrder;
+
+    /**
+     * 设备所属工厂id
+     */
+    private String factoryid;
+
+    /**
+     * 设备所属工厂
+     */
+    private String factory;
+
+    /**
+     * 设备所属车间ID
+     */
+    private String workshopid;
+
+    /**
+     * 设备所属车间
+     */
+    private String workshopname;
+
+    /**
+     * 设备满负荷物料
+     */
+    private BigDecimal fullMaterial;
+
+    /**
+     * 单位时间生产产品的时间
+     */
+    private Integer unitProductTime;
+
+    /**
+     * 设备运行时间段
+     */
+    private List<EquipmentRunTime> equipmentRunTimes;
+
+    /**
+     * 设备参数实体类
+     */
+    private EquipmentParameter equipmentParameter;
+
+    /**
+     * 设备零件已运行时长
+     */
+    private Integer eqPartRunTime;
+
+    /**
+     * 当前设备最后连续生产标识
+     */
+    private String lastSeriesProduceMark;
+
+    /**
+     * 当前设备最后工序类型
+     */
+    private String lastProcessType;
+
+    /**
+     * 当前设备最后工序结束时间
+     */
+    private Date lastProcessEndTime;
+
+    /**
+     * 当前设备最后工序的下机收尾时间
+     */
+    private Integer lastProcessCutfinishmin;
+
+    /**
+     * 当前设备最后工序的单卷加工时间
+     */
+    private Integer onceprocessmin;
+
+    /**
+     * 当前设备最后工序的已立板总重量
+     */
+    private BigDecimal lastSerialLbWeight;
+
+    /**
+     * 当前设备最后工序的单卷重量
+     */
+    private BigDecimal lastSinglerollweight;
+
+    /**
+     * 关联设备,铸轧机关联的熔炼炉
+     */
+    private String equassociated;
+
+    /**
+     * 当前设备最后工序的轧机使用辊ID
+     */
+    private String lastZjgid;
+
+    /**
+     * 设备上排产的工序
+     */
+    private List<ProductionProcessesTa> processesList;
+
+    public EquipmentTa(String id, String equipmentType) {
+        this.id = id;
+        this.equipmentType = equipmentType;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getName().replaceAll(".*\\.", "") + "-" + id;
+    }
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1107 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/taskassigning/tado/ProductionProcessesTa.java


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

@@ -0,0 +1,247 @@
+package com.rongwei.rwapsserver.aps.taskassigning.tado;
+
+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.time.Month;
+import java.util.*;
+import java.util.stream.IntStream;
+
+/**
+ * 设备赋值后任务起止时间自动计算
+ */
+@Slf4j
+public class TaskStartTimeListenerTa implements ListVariableListener<ApsSolutionTa, EquipmentTa, ProductionProcessesTa> {
+
+    @Override
+    public void beforeEntityAdded(ScoreDirector<ApsSolutionTa> scoreDirector, EquipmentTa equipmentTa) {
+        throw new UnsupportedOperationException("This example does not support adding employees.");
+    }
+
+    @Override
+    public void afterEntityAdded(ScoreDirector<ApsSolutionTa> scoreDirector, EquipmentTa equipmentTa) {
+        throw new UnsupportedOperationException("This example does not support adding employees.");
+    }
+
+    @Override
+    public void beforeEntityRemoved(ScoreDirector<ApsSolutionTa> scoreDirector, EquipmentTa equipmentTa) {
+        throw new UnsupportedOperationException("This example does not support adding employees.");
+    }
+
+    @Override
+    public void afterEntityRemoved(ScoreDirector<ApsSolutionTa> scoreDirector, EquipmentTa equipmentTa) {
+        throw new UnsupportedOperationException("This example does not support adding employees.");
+    }
+
+    @Override
+    public void afterListVariableElementUnassigned(ScoreDirector<ApsSolutionTa> scoreDirector, ProductionProcessesTa productionProcessesTa) {
+        scoreDirector.beforeVariableChanged(productionProcessesTa, "startTime");
+        productionProcessesTa.setStartTime(null);
+        scoreDirector.afterVariableChanged(productionProcessesTa, "startTime");
+    }
+
+    @Override
+    public void beforeListVariableChanged(ScoreDirector<ApsSolutionTa> scoreDirector, EquipmentTa equipmentTa, int fromIndex, int toIndex) {
+        // Do nothing
+    }
+
+    @Override
+    public void afterListVariableChanged(ScoreDirector<ApsSolutionTa> scoreDirector, EquipmentTa equipmentTa, int fromIndex, int toIndex) {
+        updateStartTime(scoreDirector, equipmentTa, fromIndex,toIndex);
+//        updateTaskStartTime(scoreDirector, equipmentTa, fromIndex);
+    }
+
+    protected void updateStartTime(ScoreDirector<ApsSolutionTa> scoreDirector, EquipmentTa equipmentTa, int index, int toIndex) {
+        if(equipmentTa.getId().equals("0001be252874536843730b100152")){
+            int aa = 0;
+        }
+        List<ProductionProcessesTa> allProcessesList = scoreDirector.getWorkingSolution().getProcessesList();
+        List<ProductionProcessesTa> tasks = equipmentTa.getTasks();
+        if (ObjectNull.isNull(tasks) || tasks.size() - 1 < index) {
+            return;
+        }
+
+        index = 0;
+        ProductionProcessesTa productionProcessesTa = tasks.get(index);
+        LocalDateTime previousEndTime = index == 0 ? productionProcessesTa.getApsOverallConfig().getStartTime() : tasks.get(index - 1).getEndTime();
+
+        for (int i = index; i < tasks.size(); i++) {
+            ProductionProcessesTa t = tasks.get(i);
+            LocalDateTime previousLastEndTime = getEarlyStartTime(t, equipmentTa,allProcessesList);
+            if(previousLastEndTime != null && previousLastEndTime.compareTo(previousEndTime)>0){
+                previousEndTime = previousLastEndTime;
+            }
+            if (!Objects.equals(t.getStartTime(), previousEndTime)) {
+                scoreDirector.beforeVariableChanged(t, "startTime");
+                t.setStartTime(previousEndTime);
+                scoreDirector.afterVariableChanged(t, "startTime");
+            }
+
+            LocalDateTime specificDateTime = LocalDateTime.of(2026, Month.JANUARY, 1, 12, 30);
+            if(previousEndTime.compareTo(specificDateTime)>0){
+                int aa = 0;
+            }
+            previousEndTime = t.getEndTime();
+        }
+    }
+
+    /**
+     * 获取当前工序的最早开工时间
+     * @param t
+     * @param equipmentTa
+     * @return
+     */
+    private LocalDateTime getEarlyStartTime(ProductionProcessesTa t, EquipmentTa equipmentTa, List<ProductionProcessesTa> allProcessesList){
+        LocalDateTime previousLastEndTime = null;
+        if(t.getPreviousProcesses() != null && t.getPreviousProcesses().size()>0){
+            for (ProductionProcessesTa previousProcess : t.getPreviousProcesses()) {
+                if(previousProcess.getEquipment() != null && previousProcess.getEquipment().getId().equals(equipmentTa.getId()) && !previousProcess.getIfLock()){
+                    continue;
+                }
+                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);
+                    }
+                }
+            }
+        }else{
+            previousLastEndTime = t.getApsOverallConfig().getStartTime();
+        }
+        /*if(t.getMinWaitTime() != null && t.getMinWaitTime()>0 && previousLastEndTime != null){
+            previousLastEndTime = previousLastEndTime.plusMinutes(t.getMinWaitTime());
+        }*/
+        return previousLastEndTime;
+    }
+
+    public void updateTaskStartTime(ScoreDirector<ApsSolutionTa> scoreDirector, EquipmentTa resource, int fromIndex) {
+        List<ProductionProcessesTa> 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<ProductionProcessesTa> tasks = update.getResource().getTasks();
+            int fromIdx = update.getFromIndex();
+            if (ObjectNull.isNull(tasks) || tasks.size() - 1 < fromIdx) {
+                continue;
+            }
+            // 获取当前变更的任务,在当前资源前一个任务的时间
+            ProductionProcessesTa sourcePreviousTask = getSourcePreviousTask(tasks, fromIdx);
+            LocalDateTime sourceTaskPreviousEndTime = fromIdx == 0 ? beginTime : Optional.ofNullable(sourcePreviousTask).map(ProductionProcessesTa::getEndTime).orElseGet(() -> beginTime);
+            LocalDateTime resourcePreviousEndTime = sourceTaskPreviousEndTime;
+
+            Set<String> notUpdateFrontTaskIds = new HashSet<>();
+            for (int idx = fromIdx; idx < tasks.size(); idx++) {
+                ProductionProcessesTa 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 || startTime == null){
+                    int testa = 0;
+                }
+                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<ProductionProcessesTa> 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 ProductionProcessesTa getSourcePreviousTask(List<ProductionProcessesTa> tasks, int fromIndex) {
+        if (fromIndex == 0) {
+            return null;
+        }
+        ProductionProcessesTa previousTask = null;
+        for (int i = fromIndex - 1; i >= 0; i--) {
+            if (ObjectNull.isNotNull(previousTask)) {
+                break;
+            }
+            ProductionProcessesTa task = tasks.get(i);
+            if (ObjectNull.isNull(task.getEndTime())) {
+                continue;
+            }
+            previousTask = task;
+        }
+
+        return previousTask;
+    }
+
+    /**
+     * 记录已变更的任务id
+     *
+     * @param job         任务
+     * @param frontJobIds 前置任务id
+     */
+    private void addChangedTaskIds(ProductionProcessesTa job, Set<String> frontJobIds) {
+        if (job.getPreviousProcesses() == null) {
+            return;
+        }
+        ArrayDeque<ProductionProcessesTa> frontJobStack = new ArrayDeque<>();
+        job.getPreviousProcesses().forEach(frontJobStack::push);
+        while (!frontJobStack.isEmpty()) {
+            ProductionProcessesTa frontJob = frontJobStack.pop();
+            frontJobIds.add(frontJob.getId());
+            if (frontJob.getPreviousProcesses() != null) {
+                frontJob.getPreviousProcesses().forEach(frontJobStack::push);
+            }
+        }
+    }
+
+}

+ 28 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/taskassigning/vo/ProductionScheduleRetTaVo.java

@@ -0,0 +1,28 @@
+package com.rongwei.rwapsserver.aps.taskassigning.vo;
+
+import com.rongwei.rwapsserver.aps.domain.ProductionProcesses;
+import com.rongwei.rwapsserver.aps.taskassigning.tado.ProductionProcessesTa;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ProductionScheduleRetTaVo {
+
+    /**
+     * 返回状态码
+     */
+    private String code;
+    /**
+     * 返回错误信息
+     */
+    private String msg;
+
+    private String scoreResult;
+
+    /**
+     * 排程后的生产工序集合
+     */
+    private List<ProductionProcessesTa> processes;
+
+}

+ 124 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/taskassigning/vo/ProductionScheduleTaVo.java

@@ -0,0 +1,124 @@
+package com.rongwei.rwapsserver.aps.taskassigning.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.rongwei.rwapsserver.aps.domain.*;
+import com.rongwei.rwapsserver.aps.taskassigning.tado.EquipmentTa;
+import com.rongwei.rwapsserver.aps.taskassigning.tado.ProductionProcessesTa;
+import com.rongwei.rwapsserver.aps.vo.ApsTypeVo;
+import lombok.Data;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class ProductionScheduleTaVo {
+
+    private String productionScheduleId;
+
+    /**
+     * APS 排程计划开始时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date apsPlanStartDate;
+
+    /**
+     * 指定运行秒数
+     */
+    private Integer planSeconds;
+
+    /**
+     * 排程模式:
+     * default:分批排程(默认排程)
+     * mix:混合排程
+     * mixNotThMerge: 混合排程不提前合并退火
+     */
+    private ApsTypeVo scheduleType;
+
+    /**
+     * 生产工序集合
+     */
+    private List<ProductionProcessesTa> processes;
+
+    /**
+     * 生产设备集合
+     */
+    private List<EquipmentTa> equipmentList;
+
+    /**
+     * 工序之间流转时间设置(暂时不考虑跨工厂)
+     * 车间内流转时间:WORKSHOP_IN
+     * 跨车间流转时间:WORKSHOP_CROSS
+     */
+    private Map<String,Integer> roamTime;
+
+    private Integer environmentMode;
+
+    /**
+     * 洗炉合金
+     */
+    private List<WashingMetal> closealloynames;
+
+    /**
+     * 小卷成退合并装炉分组规则
+     */
+    private List<ApsFurnaceInstallationDo> furnaceInstallations;
+
+    /**
+     * 小卷成退合并装炉兼容规则
+     */
+    private List<ApsMergeFurnaceDo> mergeFurnaces;
+
+    /**
+     * 退火合炉厚差配置表
+     */
+    private List<ApsAnnealingDifferenceDo> apsAnnealingDifferences;
+
+    /**
+     * 铸轧兼容配置表
+     */
+    private List<ApsNochangeRollerDo> apsNochangeRollerDos;
+
+    /**
+     * 洗炉时间(小时)
+     */
+    private Integer washingtime;
+    /**
+     * 立板时间(小时)
+     */
+    private Integer standingtime;
+    /**
+     * 周期立板产量(吨)
+     */
+    private Integer standingyield;
+    /**
+     * 松散度
+     */
+    private BigDecimal looseness;
+    /**
+     * 排程速度
+     */
+    private Integer pcspeed;
+
+    /**
+     * 除铸轧外期望交货天数
+     */
+    private Integer expecteddays;
+    /**
+     * 中退组炉宽差,默认:250mm
+     */
+    private Integer middifference;
+    /**
+     * 成退组炉宽差,默认:200mm
+     */
+    private Integer furnacedifference;
+    /**
+     * 中退组炉卷重差(吨)
+     */
+    private BigDecimal weightdifference;
+    /**
+     * 成退组炉卷重差(吨)
+     */
+    private BigDecimal midweightdifference;
+
+}

+ 66 - 2
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/util/ApsUtils.java

@@ -1,13 +1,15 @@
 package com.rongwei.rwapsserver.aps.util;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.StrUtil;
 import com.rongwei.rwapsserver.aps.domain.ApsSolution;
 import com.rongwei.rwapsserver.aps.domain.ProductionProcesses;
+import com.rongwei.rwapsserver.aps.taskassigning.tado.ProductionProcessesTa;
 import org.optaplanner.core.api.score.ScoreExplanation;
 import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
 import org.optaplanner.core.api.score.constraint.ConstraintMatch;
-import java.util.ArrayList;
-import java.util.List;
+
+import java.util.*;
 
 public class ApsUtils {
 
@@ -80,4 +82,66 @@ public class ApsUtils {
         }
     }
 
+
+    /**
+     * 根据可选设备分组排程
+     * @param processes
+     * @param equPros
+     */
+    public void groupProsByEqu(List<ProductionProcessesTa> processes, Map<String,List<ProductionProcessesTa>> equPros){
+        // 根据可选设备分组排程
+        for (ProductionProcessesTa productionProcesses : processes) {
+            if(productionProcesses.getOptionalEquipments().size()>1){
+                int aaa = 0;
+            }
+            String equids = CollUtil.join(productionProcesses.getOptionalEquipments(), ",");
+            if(equPros.size() == 0){
+                List<ProductionProcessesTa> pps = new ArrayList<>();
+                pps.add(productionProcesses);
+                equPros.put(equids,pps);
+            }else {
+                boolean hasSameKeys = false;
+                List<String> haskey = new ArrayList<>();
+                Set<String> keys = equPros.keySet();
+                for (String key : keys) {
+                    for (String optionalEquipment : productionProcesses.getOptionalEquipments()) {
+                        if(key.contains(optionalEquipment)){
+                            hasSameKeys = true;
+                            haskey.add(key);
+                        }
+                    }
+                }
+                if(hasSameKeys){
+                    List<ProductionProcessesTa> mergePros = new ArrayList<>();
+                    Set<String> keyIds = new HashSet<>();
+
+                    for (String s : haskey) {
+                        List<ProductionProcessesTa> processesList1 = equPros.get(s);
+                        if(processesList1 != null && processesList1.size() > 0){
+                            mergePros.addAll(processesList1);
+                            equPros.remove(s);
+
+                            String[] keysps = s.split(",");
+                            for (String key : keysps) {
+                                keyIds.add(key);
+                            }
+                        }
+                    }
+                    mergePros.add(productionProcesses);
+                    String keyIdStrs = CollUtil.join(keyIds, ",");
+
+                    for (String optionalEquipment : productionProcesses.getOptionalEquipments()) {
+                        if(!keyIdStrs.contains(optionalEquipment)){
+                            keyIdStrs = keyIdStrs + "," + optionalEquipment;
+                        }
+                    }
+                    equPros.put(keyIdStrs,mergePros);
+                }else{
+                    List<ProductionProcessesTa> pps = new ArrayList<>();
+                    pps.add(productionProcesses);
+                    equPros.put(equids,pps);
+                }
+            }
+        }
+    }
 }