Kaynağa Gözat

单卷模型改造

fangpy 1 yıl önce
ebeveyn
işleme
7c7d5d7e7a

+ 59 - 9
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/controller/ApsSchedulingController.java

@@ -1,16 +1,23 @@
 package com.rongwei.rwapsserver.aps.controller;
 
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.IdcardUtil;
 import com.rongwei.rwapsserver.aps.domain.EquipmentRunTime;
+import com.rongwei.rwapsserver.aps.domain.ProduceOrder;
+import com.rongwei.rwapsserver.aps.domain.ProductionProcesses;
 import com.rongwei.rwapsserver.aps.service.ProductionScheduleService;
 import com.rongwei.rwapsserver.aps.util.ApsException;
 import com.rongwei.rwapsserver.aps.vo.ProductionScheduleRetVo;
 import com.rongwei.rwapsserver.aps.vo.ProductionScheduleVo;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.cloud.commons.util.IdUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
 
 /**
  * APS排程接口
@@ -50,14 +57,57 @@ public class ApsSchedulingController {
 
     /**
      * test
-     * @param equipmentRunTime
      * @return
      */
     @PostMapping("/test")
-    public ProductionScheduleRetVo test(@RequestBody EquipmentRunTime equipmentRunTime) throws Exception{
-        System.out.println(equipmentRunTime.getStartRunTime());
-        System.out.println(equipmentRunTime.getEndRunTime());
-        return new ProductionScheduleRetVo();
+    public ProductionScheduleRetVo test(@RequestBody ProductionScheduleVo productionScheduleVo) throws Exception{
+        productionScheduleVo.setProcesses(processesInit());
+        ProductionScheduleRetVo productionScheduleRetVo = productionScheduleService.productionSchedule(productionScheduleVo);
+        return productionScheduleRetVo;
+    }
+
+    private List<ProductionProcesses> processesInit(){
+        List<ProductionProcesses> processes = new ArrayList<>();
+        for(int i=0;i<5;i++){
+            String id1 = (i*2+1)+"";
+            String id2 = (i*2+2)+"";
+            ProductionProcesses processes1 = new ProductionProcesses();
+            processes1.setId(id1);
+            processes1.setCutfinishmin(70);
+            processes1.setIfLock(false);
+            processes1.setSeriesProduceMark("1100^_^1100H14冷轧卷普料 厚6.8*宽1025mm 6吨^_^1025.00");
+            processes1.setProduceTime(720);
+            processes1.setPrepressworkmin(55);
+            processes1.setNextProcessesIds(Arrays.asList(new String[]{id2}));
+            ProduceOrder produceOrder1 = new ProduceOrder("34f200bcbac748fb8f817eea88fb9a5d","订单1", DateUtil.parseDateTime("2024-06-22 08:00:00"));
+            processes1.setProduceOrder(Arrays.asList(new ProduceOrder[]{produceOrder1}));
+            processes1.setMinWaitTime(600);
+            processes1.setVolumeMetal("1100");
+            processes1.setOptionalEquipments(Arrays.asList(new String[]{"0001be252874536843730b100018","0001be252874536843730b100017","0001be252874536843730b100016"}));
+            processes1.setUnitProduceTime(360);
+            processes1.setProcessType("铸轧");
+            processes1.setBsProcessesId(Arrays.asList(new String[]{"f29671831358404ab047848aade7e681"}));
+
+            ProductionProcesses processes2 = new ProductionProcesses();
+            processes2.setId(id2);
+            processes2.setIfLock(false);
+            processes2.setSeriesProduceMark("1100^_^1100H14冷轧卷普料 厚6.8*宽1025mm 6吨^_^1025.00");
+            processes2.setProduceTime(3100);
+            processes2.setPreviousProcessesIds(Arrays.asList(new String[]{id1}));
+            processes2.setProduceOrder(Arrays.asList(new ProduceOrder[]{produceOrder1}));
+            processes2.setMinWaitTime(310);
+            processes2.setMaxWaitTime(3000);
+            processes2.setVolumeMetal("1100");
+            processes2.setOptionalEquipments(Arrays.asList(new String[]{"0001be252874536843730b100158","0001be252874536843730b100162","0001be252874536843730b100160"}));
+            processes2.setUnitProduceTime(3100);
+            processes2.setBottleneck(true);
+            processes2.setProcessType("成退");
+            processes2.setBsProcessesId(Arrays.asList(new String[]{"d8ac56ea9335401f871cd691d1ba9442"}));
+
+            processes.add(processes1);
+            processes.add(processes2);
+        }
+        return processes;
     }
 
 }

+ 2 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/domain/EquipmentParameter.java

@@ -12,5 +12,7 @@ public class EquipmentParameter {
 
     // 设备宽度
     private BigDecimal equipmentWidth;
+    // 设备承重
+    private BigDecimal equipmentBearing;
 
 }

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

@@ -3,6 +3,7 @@ package com.rongwei.rwapsserver.aps.domain;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
 /**
@@ -42,4 +43,19 @@ public class EquipmentRunTime {
      */
     private Integer cutfinishmin;
 
+    /**
+     * 总卷宽度
+     */
+    private BigDecimal totalVolumeWidth;
+
+    /**
+     * 总卷重
+     */
+    private BigDecimal totalSinglerollweight;
+
+    /**
+     * 是否锁定标识,排程中的是false
+     */
+    private boolean locked;
+
 }

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

@@ -207,6 +207,11 @@ public class ProductionProcesses extends Step{
      */
     private BigDecimal volumeWidth;
 
+    /**
+     * 单卷重
+     */
+    private BigDecimal singlerollweight;
+
     /**
      * 合金
      */
@@ -285,7 +290,7 @@ public class ProductionProcesses extends Step{
     @ValueRangeProvider
     @JsonIgnore
     public CountableValueRange<Integer> getDelayRange() {
-        if(this.getPreviousProcesses() != null && this.getPreviousProcesses().size()>0){
+        /*if(this.getPreviousProcesses() != null && this.getPreviousProcesses().size()>0){
             Integer maxWaitTime = null;
             for (ProductionProcesses previousProcess : this.getPreviousProcesses()) {
                 if(previousProcess.getMaxWaitTime() != null && previousProcess.getMaxWaitTime()>0){
@@ -302,8 +307,12 @@ public class ProductionProcesses extends Step{
                 int maxrange = (maxWaitTime / 20) + 1;
                 return ValueRangeFactory.createIntValueRange(0, maxrange);
             }
+        }*/
+        Integer maxDelay = 1;
+        if(this.processType.equals("成退") || this.processType.equals("中退") || this.processType.equals("铸轧") || this.processType.equals("冷轧")){
+            maxDelay = 10;
         }
-        return ValueRangeFactory.createIntValueRange(0, 50);
+        return ValueRangeFactory.createIntValueRange(0, maxDelay);
     }
 
     public void setOptionalProviderEquipments(List<Equipment> optionalProviderEquipments) {
@@ -659,6 +668,14 @@ public class ProductionProcesses extends Step{
         this.equipmentEquassociated = equipmentEquassociated;
     }
 
+    public BigDecimal getSinglerollweight() {
+        return singlerollweight;
+    }
+
+    public void setSinglerollweight(BigDecimal singlerollweight) {
+        this.singlerollweight = singlerollweight;
+    }
+
     public String getSeriSort(){
         String sortStr = this.getId();
         if(this.getStartTime() != null){

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

@@ -10,6 +10,8 @@ import com.rongwei.rwapsserver.aps.domain.ProductionProcesses;
 import lombok.extern.slf4j.Slf4j;
 import org.optaplanner.core.api.domain.variable.VariableListener;
 import org.optaplanner.core.api.score.director.ScoreDirector;
+
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.temporal.ChronoUnit;
@@ -88,15 +90,16 @@ public class TaskStartTimeListener implements VariableListener<ApsSolution, Prod
             /*if(process.getId().equals("abdcff89d0164cd5a2b57e1bfaf3bc1d")){
                 process.getId();
             }*/
-            LocalDateTime startDateTime = startTimeSet(process,scoreDirector);
-            if(process.getDelay() != null && process.getDelay()>0){
+            LocalDateTime startDateTime = startTimeNewSet(process,scoreDirector);
+
+            /*if(process.getDelay() != null && process.getDelay()>0){
                 if(process.getProduceTime()>1000){
                     int bc = (int) Math.ceil((double) process.getProduceTime() / 20);
                     startDateTime = startDateTime.plusMinutes(process.getDelay() * bc);
                 }else{
                     startDateTime = startDateTime.plusMinutes(process.getDelay() * 30);
                 }
-            }
+            }*/
             scoreDirector.beforeVariableChanged(process, "startTime");
             process.setStartTime(startDateTime);
             scoreDirector.afterVariableChanged(process, "startTime");
@@ -469,6 +472,164 @@ public class TaskStartTimeListener implements VariableListener<ApsSolution, Prod
         return toUpdateStartTime;
     }
 
+    private LocalDateTime startTimeNewSet(ProductionProcesses process,ScoreDirector<ApsSolution> scoreDirector){
+        // 时间设定
+        LocalDateTime toUpdateStartTime = null;
+        if(process.getEquipment() != null){
+            LocalDateTime endTime = process.getApsOverallConfig().getStartTime();
+            ProductionProcesses preProcess = null;
+            if(process.getPreviousProcesses() != null && process.getPreviousProcesses().size()>0){
+                // 单卷情况下前道工序只有一个
+                preProcess = process.getPreviousProcesses().get(0);
+                // 前道工序结束时间
+                endTime = preProcess.getEndTime();
+            }
+
+            // 流转时间
+            Integer lzTimes = 0;
+            if(preProcess != null){
+                if(preProcess.getEquipment().getWorkshopid() != null && preProcess.getEquipment().getWorkshopid().equals(process.getEquipment().getWorkshopid())){
+                    lzTimes = process.getApsOverallConfig().getRoamTime().get("WORKSHOP_IN");
+                }else{
+                    lzTimes = process.getApsOverallConfig().getRoamTime().get("WORKSHOP_CROSS");
+                }
+                // 最小等待时间对比流转时间
+                if(preProcess.getMinWaitTime() != null && lzTimes<preProcess.getMinWaitTime()){
+                    lzTimes = preProcess.getMinWaitTime();
+                }
+            }
+
+            // 当前工序最小开始时间、结束时间
+            LocalDateTime proStartTime = endTime.plusMinutes(lzTimes);
+            LocalDateTime proEndTime = proStartTime.plusMinutes(process.getProduceTime());
+            // 随机延时时间处理
+            if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退")
+                    || process.getProcessType().equals("铸轧") || process.getProcessType().equals("冷轧")){
+                proStartTime = proStartTime.plusMinutes(process.getDelay() * 60);
+            }else{
+                proStartTime = proStartTime.plusMinutes(process.getDelay() * 10);
+            }
+            // 当前工序设备已占用时间
+            List<EquipmentRunTime> equipmentRunTimes = new ArrayList<>();
+            if(process.getEquipment().getEquipmentRunTimes() != null && process.getEquipment().getEquipmentRunTimes().size()>0){
+                for (EquipmentRunTime equipmentRunTime : process.getEquipment().getEquipmentRunTimes()) {
+                    EquipmentRunTime copy = new EquipmentRunTime();
+                    copy.setStartRunTime(equipmentRunTime.getStartRunTime());
+                    copy.setEndRunTime(equipmentRunTime.getEndRunTime());
+                    copy.setLocked(true);
+                    equipmentRunTimes.add(copy);
+                }
+            }
+            // 排程中当前设备其它作业占用时间
+            // 过滤同一设备存在开始时间不是当前工序的其它所有工序
+            // 获取所有规划实体对象数据
+            ApsSolution workingSolution = scoreDirector.getWorkingSolution();
+            Equipment pe = process.getEquipment();
+            List<ProductionProcesses> allProcessesList = workingSolution.getProcessesList();
+            List<ProductionProcesses> filterProcess = allProcessesList.stream().filter(v -> {
+                return v.getEquipment() != null && v.getEquipment().getId().equals(pe.getId()) && v.getStartTime() != null && !v.getId().equals(process.getId());
+            }).sorted(Comparator.comparing(ProductionProcesses::getProduceTime)).collect(Collectors.toList());
+            if(filterProcess != null && filterProcess.size()>0){
+                for (ProductionProcesses productionProcesses : filterProcess) {
+                    if(equipmentRunTimes == null || equipmentRunTimes.size() == 0){
+                        continue;
+                    }
+                    boolean merged = false;
+                    if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退")){
+                        for (EquipmentRunTime equipmentRunTime : equipmentRunTimes) {
+                            // 判断当前排程的退火作业哪些是合并工序,只要开始时间到结束时间是包含关系即认为是合并工序
+                            if(equipmentRunTime.getStartRunTime().compareTo(productionProcesses.getStartTime())<=0
+                                    && equipmentRunTime.getEndRunTime().compareTo(productionProcesses.getEndTime())>=0){
+                                // 合并退火作业总宽度、承重
+                                equipmentRunTime.setTotalVolumeWidth(equipmentRunTime.getTotalVolumeWidth().add(productionProcesses.getVolumeWidth()));
+                                equipmentRunTime.setTotalSinglerollweight(equipmentRunTime.getTotalSinglerollweight().add(productionProcesses.getSinglerollweight()));
+                                merged = true;
+                                break;
+                            }else{
+                                // 未锁定的是排程中的作业占用时间段,可以合并更大的加工时间
+                                if(!equipmentRunTime.isLocked()){
+                                    if(equipmentRunTime.getStartRunTime().compareTo(productionProcesses.getStartTime())>=0
+                                            && equipmentRunTime.getEndRunTime().compareTo(productionProcesses.getEndTime())<=0){
+                                        // 合并退火作业总宽度、承重
+                                        equipmentRunTime.setTotalVolumeWidth(equipmentRunTime.getTotalVolumeWidth().add(productionProcesses.getVolumeWidth()));
+                                        equipmentRunTime.setTotalSinglerollweight(equipmentRunTime.getTotalSinglerollweight().add(productionProcesses.getSinglerollweight()));
+                                        equipmentRunTime.setStartRunTime(productionProcesses.getStartTime());
+                                        equipmentRunTime.setEndRunTime(productionProcesses.getEndTime());
+                                        merged = true;
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    // 新增设备占用时间段
+                    if(!merged){
+                        EquipmentRunTime copy = new EquipmentRunTime();
+                        copy.setStartRunTime(productionProcesses.getStartTime());
+                        copy.setEndRunTime(productionProcesses.getEndTime());
+                        copy.setLocked(false);
+                        equipmentRunTimes.add(copy);
+                    }
+                }
+            }
+            // 过滤
+            // 按照开始时间排序
+            equipmentRunTimes.sort(Comparator.comparing(EquipmentRunTime::getStartRunTime));
+            // 有交叉的时间直接跳过
+            if(equipmentRunTimes.size()>0){
+                for (EquipmentRunTime equipmentRunTime : equipmentRunTimes) {
+                    if(proStartTime.compareTo(equipmentRunTime.getEndRunTime())>0 || proEndTime.compareTo(equipmentRunTime.getStartRunTime())<0){
+                        continue;
+                    }else{
+                        boolean jumpMark = false;
+                        // 退火工序特殊处理
+                        if(process.getProcessType().equals("成退") || process.getProcessType().equals("中退")){
+                            if(equipmentRunTime.getStartRunTime().compareTo(proStartTime)<=0
+                                    && equipmentRunTime.getEndRunTime().compareTo(proEndTime)>=0){
+                                // 宽度余量
+                                if (pe.getEquipmentParameter().getEquipmentWidth().subtract(equipmentRunTime.getTotalVolumeWidth()).compareTo(process.getVolumeWidth()) < 0) {
+                                    jumpMark = true;
+                                }
+                                // 重量余量
+                                if (pe.getEquipmentParameter().getEquipmentBearing().subtract(equipmentRunTime.getTotalSinglerollweight()).compareTo(process.getSinglerollweight()) < 0) {
+                                    jumpMark = true;
+                                }
+                            }else if(equipmentRunTime.getStartRunTime().compareTo(proStartTime)>=0 && equipmentRunTime.getEndRunTime().compareTo(proEndTime)<=0){
+                                // 未锁定的是排程中的作业占用时间段,可以合并更大的加工时间
+                                if(!equipmentRunTime.isLocked()){
+                                    // 宽度余量
+                                    if (pe.getEquipmentParameter().getEquipmentWidth().subtract(equipmentRunTime.getTotalVolumeWidth()).compareTo(process.getVolumeWidth()) < 0) {
+                                        jumpMark = true;
+                                    }
+                                    // 重量余量
+                                    if (pe.getEquipmentParameter().getEquipmentBearing().subtract(equipmentRunTime.getTotalSinglerollweight()).compareTo(process.getSinglerollweight()) < 0) {
+                                        jumpMark = true;
+                                    }
+                                    equipmentRunTime.setStartRunTime(proStartTime);
+                                    equipmentRunTime.setEndRunTime(proEndTime);
+                                }else{
+                                    jumpMark = true;
+                                }
+                            }else{
+                                jumpMark = true;
+                            }
+                        }else{
+                            jumpMark = true;
+                        }
+                        // 跳过当前占用时间
+                        if(jumpMark){
+                            proStartTime = equipmentRunTime.getEndRunTime().plusMinutes(1);
+                            proEndTime = proStartTime.plusMinutes(process.getProduceTime());
+                        }
+                    }
+                }
+            }
+
+            toUpdateStartTime = proStartTime;
+        }
+        return toUpdateStartTime;
+    }
+
     /**
      * 汇总所有下道工序的加工时间
      * @param nextProcess

+ 30 - 0
rw-aps-server/src/main/java/com/rongwei/rwapsserver/aps/score/ApsConstraintProvider.java

@@ -705,6 +705,9 @@ public class ApsConstraintProvider implements ConstraintProvider {
      */
     private Constraint eqTimeCross(ConstraintFactory constraintFactory) {
         return constraintFactory.forEach(ProductionProcesses.class)
+                .filter(pro->{
+                    return !pro.getProcessType().equals("成退") && !pro.getProcessType().equals("中退");
+                })
                 .join(ProductionProcesses.class,Joiners.equal(ProductionProcesses::getEquipmentId))
                 .filter((proc1,proc2)->{
                     if(proc1.getId() == proc2.getId() ||
@@ -722,6 +725,33 @@ public class ApsConstraintProvider implements ConstraintProvider {
                 .asConstraint("eqTimeCross");
     }
 
+    /**
+     * 硬约束:同一个退火炉设备的不同的工步运行时间约束
+     * @param constraintFactory
+     * @return
+     */
+    private Constraint eqTimeCrossTuihuo(ConstraintFactory constraintFactory) {
+        return constraintFactory.forEach(ProductionProcesses.class)
+                .filter(pro->{
+                    return pro.getProcessType().equals("成退") || pro.getProcessType().equals("中退");
+                })
+                .join(ProductionProcesses.class,Joiners.equal(ProductionProcesses::getEquipmentId))
+                .filter((proc1,proc2)->{
+                    if(proc1.getId() == proc2.getId() ||
+                            (StrUtil.isNotBlank(proc1.getMergeProcessMark()) && StrUtil.isNotBlank(proc2.getMergeProcessMark()) && proc1.getMergeProcessMark().equals(proc2.getMergeProcessMark()))){
+                        return false;
+                    }
+                    if(proc1.getStartTime() == null || proc2.getStartTime() == null){
+                        return false;
+                    }
+                    Boolean b1 = (proc1.getStartTime().compareTo(proc2.getStartTime())<0 && proc2.getStartTime().compareTo(proc1.getEndTime())<0);
+                    Boolean b2 = (proc2.getStartTime().compareTo(proc1.getStartTime())<0 && proc1.getStartTime().compareTo(proc2.getEndTime())<0);
+                    return b1 || b2;
+                })
+                .penalize(HardSoftScore.ONE_HARD,(proc1,proc2)->100)
+                .asConstraint("eqTimeCrossTuihuo");
+    }
+
     /**
      * 软约束:交货日期,根据延迟交货日期的天数来做惩罚分数计算
      * @param constraintFactory

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

@@ -191,7 +191,7 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
         // 根据前道、后道工序ID,转换为前道、后道工序对象
         Map<String,ProductionProcesses> idMaps = new HashMap<>();
         for (ProductionProcesses process : productionScheduleVo.getProcesses()) {
-            idMaps.put(String.join(",",process.getBsProcessesId()),process);
+            idMaps.put(String.join(",",process.getId()),process);
         }
         for (ProductionProcesses process : productionScheduleVo.getProcesses()) {
             // 全局配置设置
@@ -204,7 +204,7 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
                 List<ProductionProcesses> pres = new ArrayList<>();
                 for (String previousProcessesId : process.getPreviousProcessesIds()) {
                     for (ProductionProcesses productionScheduleVoProcess : productionScheduleVo.getProcesses()) {
-                        if(productionScheduleVoProcess.getBsProcessesId().contains(previousProcessesId)){
+                        if(productionScheduleVoProcess.getId().contains(previousProcessesId)){
                             pres.add(productionScheduleVoProcess);
                         }
                     }
@@ -222,7 +222,7 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
                 }*/
                 for (String previousProcessesId : process.getNextProcessesIds()) {
                     for (ProductionProcesses nextprocess : productionScheduleVo.getProcesses()) {
-                        if(nextprocess.getBsProcessesId().contains(previousProcessesId)){
+                        if(nextprocess.getId().contains(previousProcessesId)){
                             nexts.add(nextprocess);
                         }
                     }
@@ -249,7 +249,7 @@ public class ProductionScheduleServiceImpl implements ProductionScheduleService
                     }
                 }
                 if(providedEq.size() == 0){
-                    throw new ApsException("500","作业ID:"+ CollUtil.join(process.getBsProcessesId(),",") + "可选设备不能为空");
+                    throw new ApsException("500","作业ID:"+ process.getId() + "可选设备不能为空");
                 }
                 process.setOptionalProviderEquipments(providedEq);
             }