Commit 7d322d24 authored by Tong Li's avatar Tong Li

Merge remote-tracking branch 'origin/master' into tl

# Conflicts:
#	src/test/java/com/aps/demo/PlanResultServiceTest.java
parents c6275d54 8cd195f9
package com.aps.config; package com.aps.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
...@@ -51,6 +52,9 @@ public class RedisConfiguration { ...@@ -51,6 +52,9 @@ public class RedisConfiguration {
// 设置可见性 // 设置可见性
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 忽略空值(包括空数组、空字符串、null)
om.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
// 注册 JavaTimeModule 支持 Java 8 日期时间 // 注册 JavaTimeModule 支持 Java 8 日期时间
om.registerModule(new JavaTimeModule()); om.registerModule(new JavaTimeModule());
......
...@@ -88,6 +88,9 @@ public class LanuchController { ...@@ -88,6 +88,9 @@ public class LanuchController {
// 这些参数前端可以不传;不传时后端会根据 sceneId 找场景创建人,再查这个人的策略。 // 这些参数前端可以不传;不传时后端会根据 sceneId 找场景创建人,再查这个人的策略。
Long userId = getLongParam(params, "userId"); Long userId = getLongParam(params, "userId");
Long baseRuleId = getLongParam(params, "baseRuleId"); Long baseRuleId = getLongParam(params, "baseRuleId");
if (baseRuleId == null) {
baseRuleId = getLongParam(params, "ruleId");
}
if (baseRuleId == null) { if (baseRuleId == null) {
baseRuleId = getLongParam(params, "referenceId"); baseRuleId = getLongParam(params, "referenceId");
} }
......
...@@ -215,7 +215,8 @@ public class ChromosomeDataController { ...@@ -215,7 +215,8 @@ public class ChromosomeDataController {
*/ */
private boolean isFileEntity(String entityName) { private boolean isFileEntity(String entityName) {
// 这里列出所有文件实体的名称 // 这里列出所有文件实体的名称
String[] fileEntities = {"order", "entry", "machine", "globaloperationinfo", "groupresult", "prodprocessexec", "machineoption"}; String[] fileEntities = {"order", "entry", "machine", "globaloperationinfo", "groupresult", "prodprocessexec", "machineoption",
"task"};
for (String fileEntity : fileEntities) { for (String fileEntity : fileEntities) {
if (fileEntity.equalsIgnoreCase(entityName)) { if (fileEntity.equalsIgnoreCase(entityName)) {
return true; return true;
......
...@@ -421,6 +421,73 @@ public class ResourceGanttController { ...@@ -421,6 +421,73 @@ public class ResourceGanttController {
@PostMapping("/orderInsertAuto") @PostMapping("/orderInsertAuto")
@Operation( @Operation(
summary = "自动插单", summary = "自动插单",
description = "按neworder数据结构自动插入新订单",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "订单插单参数",
required = true,
content = @io.swagger.v3.oas.annotations.media.Content(
mediaType = "application/json",
examples = @io.swagger.v3.oas.annotations.media.ExampleObject(
name = "neworder示例",
value = "{\n" +
" \"sceneId\": \"C4EF4B6DC74E43F78D2EA4102B12066D\",\n" +
" \"neworder\": {\n" +
" \"zoneid\": \"36\",\n" +
" \"isconsume\": 0,\n" +
" \"settle\": 0,\n" +
" \"delay\": 0,\n" +
" \"part\": 0,\n" +
" \"islock\": 0,\n" +
" \"isinsert\": 0,\n" +
" \"mmid\": \"fff9efd4-8fc3-495f-acfe-dbefee3dc9cc\",\n" +
" \"mmname\": \"0.9%氯化钠注射液\",\n" +
" \"mmcode\": \"153004_ZHJ24046\",\n" +
" \"unit\": \"万袋\",\n" +
" \"unitId\": 12,\n" +
" \"series\": \"153004_ZHJ24046\",\n" +
" \"seriesId\": 648,\n" +
" \"seriesName\": \"153004_ZHJ24046\",\n" +
" \"code\": \"DDBH_20260522_2\",\n" +
" \"zone\": \"33331\",\n" +
" \"customerid\": 41,\n" +
" \"customer\": \"远景\",\n" +
" \"deliverytime\": \"2026-05-05T00:00:00.00+08:00\",\n" +
" \"prioritry\": \"5\",\n" +
" \"stockid\": 617,\n" +
" \"stock\": \"测试\",\n" +
" \"price\": 5,\n" +
" \"quantity\": 55,\n" +
" \"begintime\": \"2026-05-01T00:00:00.00+08:00\"\n" +
" }\n" +
"}"
)
)
)
)
public R<String> insertOrderAuto(@RequestBody Map<String, Object> params) {
log.info("insertOrderAuto 请求参数: {}", params);
String sceneId = ParamValidator.getString(params, "sceneId", "场景ID");
@SuppressWarnings("unchecked")
Map<String, Object> newOrderData = (Map<String, Object>) params.get("neworder");
if (newOrderData == null) {
@SuppressWarnings("unchecked")
Map<String, Object> fallbackNewOrderData = (Map<String, Object>) params.get("newOrder");
newOrderData = fallbackNewOrderData;
}
if (newOrderData == null) {
throw new IllegalArgumentException("neworder 不能为空");
}
ParamValidator.validateSceneExists(sceneService, sceneId);
planResultService.InsertOrderAuto(sceneId, convertNewOrderPayload(newOrderData));
return R.ok("插单成功");
}
@PostMapping("/orderInsertAutoOld")
@Operation(
summary = "自动插单旧接口",
description = "创建新工单并按基准时间+冻结期自动排程。若锚点被占用,则占位工单及后续工单后移;若前面有空挡,新工单自动前移到设备最早可用时间", description = "创建新工单并按基准时间+冻结期自动排程。若锚点被占用,则占位工单及后续工单后移;若前面有空挡,新工单自动前移到设备最早可用时间",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "自动插单参数", description = "自动插单参数",
...@@ -456,8 +523,8 @@ public class ResourceGanttController { ...@@ -456,8 +523,8 @@ public class ResourceGanttController {
) )
) )
) )
public R<String> insertOrderAuto(@RequestBody Map<String, Object> params) { public R<String> insertOrderAutoOld(@RequestBody Map<String, Object> params) {
log.info("insertOrderAuto 请求参数: {}", params); log.info("insertOrderAutoOld 请求参数: {}", params);
String sceneId = ParamValidator.getString(params, "sceneId", "场景ID"); String sceneId = ParamValidator.getString(params, "sceneId", "场景ID");
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
...@@ -782,8 +849,8 @@ public class ResourceGanttController { ...@@ -782,8 +849,8 @@ public class ResourceGanttController {
} }
// 获取分页参数 // 获取分页参数
Integer pageindex = paged.getPageIndex(); Integer pageindex = paged.getPageIndex() != null ? Math.max(1, paged.getPageIndex()) : 1;
Integer pagesize = paged.getPageSize(); Integer pagesize = paged.getPageSize() != null ? paged.getPageSize() : 1000;
// 校验能否获取对应的文件 // 校验能否获取对应的文件
Chromosome schedule = sceneService.loadChromosomeFromFile(sceneId); Chromosome schedule = sceneService.loadChromosomeFromFile(sceneId);
...@@ -886,8 +953,8 @@ public class ResourceGanttController { ...@@ -886,8 +953,8 @@ public class ResourceGanttController {
} }
// 获取分页参数 // 获取分页参数
Integer pageindex = paged.getPageIndex(); Integer pageindex = paged.getPageIndex() != null ? Math.max(1, paged.getPageIndex()) : 1;
Integer pagesize = paged.getPageSize(); Integer pagesize = paged.getPageSize() != null ? paged.getPageSize() : 1000;
// 校验能否获取对应的文件 // 校验能否获取对应的文件
Chromosome schedule = sceneService.loadChromosomeFromFile(sceneId); Chromosome schedule = sceneService.loadChromosomeFromFile(sceneId);
...@@ -1108,5 +1175,49 @@ public class ResourceGanttController { ...@@ -1108,5 +1175,49 @@ public class ResourceGanttController {
return R.ok(data); return R.ok(data);
} }
private Map<String, Object> convertNewOrderPayload(Map<String, Object> source) {
Map<String, Object> target = new HashMap<>(source);
target.put("orderCode", requireValue(source, "订单编号", "orderCode", "code"));
target.put("materialId", requireValue(source, "物料ID", "materialId", "mmid"));
target.put("quantity", requireValue(source, "数量", "quantity"));
Object startDate = firstValue(source, "startDate", "begintime");
if (startDate != null) {
target.put("startDate", startDate);
}
Object endDate = firstValue(source, "endDate", "deliverytime");
if (endDate != null) {
target.put("endDate", endDate);
}
Object priority = firstValue(source, "priority", "prioritry");
if (priority != null) {
target.put("priority", priority);
}
return target;
}
private Object requireValue(Map<String, Object> source, String fieldName, String... keys) {
Object value = firstValue(source, keys);
if (value == null || String.valueOf(value).trim().isEmpty()) {
throw new IllegalArgumentException(fieldName + "不能为空");
}
return value;
}
private Object firstValue(Map<String, Object> source, String... keys) {
if (source == null || keys == null) {
return null;
}
for (String key : keys) {
Object value = source.get(key);
if (value != null && !String.valueOf(value).trim().isEmpty()) {
return value;
}
}
return null;
}
} }
...@@ -1407,9 +1407,12 @@ public class GeneticDecoder { ...@@ -1407,9 +1407,12 @@ public class GeneticDecoder {
GAScheduleResult lastGeneOnMachine = getLastMachineTask(machineTasks); GAScheduleResult lastGeneOnMachine = getLastMachineTask(machineTasks);
int bomtime = 0; int bomtime = 0;
if (needMaterialCheck&&islockMachineTime) { if (needMaterialCheck&&islockMachineTime) {
bomtime = getOperationBOMTime(operation, chromosome, 2);
if(!isJit) {
bomtime = getOperationBOMTime(operation, chromosome, earliestStartTime, 2);
earliestStartTime = Math.max(earliestStartTime, bomtime); earliestStartTime = Math.max(earliestStartTime, bomtime);
} }
}
// 正式落排前,再取一次当前机台最后一道工序,保证换型计算基于最新排程结果。 // 正式落排前,再取一次当前机台最后一道工序,保证换型计算基于最新排程结果。
...@@ -1521,7 +1524,16 @@ public class GeneticDecoder { ...@@ -1521,7 +1524,16 @@ public class GeneticDecoder {
// } // }
//扣库存 //扣库存
if (needMaterialCheck && islockMachineTime) { if (needMaterialCheck && islockMachineTime) {
if(!isJit) {
EditOperationBOMTime(operation, chromosome, startTime, machineTasksCache, entryIndexById, scheduleIndexById); EditOperationBOMTime(operation, chromosome, startTime, machineTasksCache, entryIndexById, scheduleIndexById);
}else {
int bomtime1 = getOperationBOMTime(operation, chromosome, startTime, 2);
if(bomtime1<=startTime)
{
EditOperationBOMTime(operation, chromosome, startTime, machineTasksCache, entryIndexById, scheduleIndexById);
}
}
} }
//换型时间是否占用设备加工时间 //换型时间是否占用设备加工时间
//10:00 开始上班 前面的任务:24:00 结束 开始换型 休息时间 10小时 //10:00 开始上班 前面的任务:24:00 结束 开始换型 休息时间 10小时
...@@ -1959,7 +1971,7 @@ if(geneDetails!=null&&geneDetails.size()>0) ...@@ -1959,7 +1971,7 @@ if(geneDetails!=null&&geneDetails.size()>0)
* 汇总当前工序的物料约束,返回所有原料/半成品中最晚满足的那个时刻。 * 汇总当前工序的物料约束,返回所有原料/半成品中最晚满足的那个时刻。
* 返回值含义是“这道工序最早允许因物料而开工的秒数”。 * 返回值含义是“这道工序最早允许因物料而开工的秒数”。
*/ */
private int getOperationBOMTime(Entry currentOp, Chromosome chromosome,int bomtype) { private int getOperationBOMTime(Entry currentOp, Chromosome chromosome,int estimatedStartTime,int bomtype) {
List<OrderMaterialRequirement> opboms= currentOp.getMaterialRequirements(); List<OrderMaterialRequirement> opboms= currentOp.getMaterialRequirements();
if(opboms==null||!_globalParam.isIsCheckMp()) if(opboms==null||!_globalParam.isIsCheckMp())
...@@ -2012,8 +2024,42 @@ if(geneDetails!=null&&geneDetails.size()>0) ...@@ -2012,8 +2024,42 @@ if(geneDetails!=null&&geneDetails.size()>0)
sfTime = Math.max(sfTime, sfTime1); sfTime = Math.max(sfTime, sfTime1);
} }
} }
}else {
if ( bomtype == 2) {
Map<String, OrderMaterialRequirement> totalNeededByMaterial = new HashMap<>();
for (OrderMaterialRequirement req : opboms) {
OrderMaterialRequirement reqq = totalNeededByMaterial.get(req.getMaterialId());
if (reqq != null) {
double allneeded = req.getSpentQty() / req.getMainQty() * currentOp.getQuantity();
allneeded = reqq.getRequiredQuantity() + allneeded;
reqq.setRequiredQuantity(allneeded);
reqq.setQjQty(allneeded);
} else {
OrderMaterialRequirement reqq1 = ProductionDeepCopyUtil.deepCopy(req);
double allneeded = req.getSpentQty() / req.getMainQty() * currentOp.getQuantity();
reqq1.setRequiredQuantity(allneeded);
reqq1.setQjQty(allneeded);
totalNeededByMaterial.put(req.getMaterialId(), reqq1);
}
}
List<OrderMaterialRequirement> remove=new ArrayList<>();
materialRequirementService.CalBom(chromosome,0, totalNeededByMaterial,chromosome.getMaterials(),baseTime.plusSeconds(estimatedStartTime) ,false,remove,null,false);
Optional<LocalDateTime> rawDateOpt = totalNeededByMaterial.values().stream()
.map(OrderMaterialRequirement::getUseTime)
.filter(Objects::nonNull)
.max(LocalDateTime::compareTo);
if (rawDateOpt.isPresent()) {
rawTime = (int) Duration.between(baseTime, rawDateOpt.get()).getSeconds();
}
} }
} }
}
if (bomtype == 0 || bomtype == 1) { if (bomtype == 0 || bomtype == 1) {
Optional<LocalDateTime> sfDateOpt = opboms.stream() Optional<LocalDateTime> sfDateOpt = opboms.stream()
.filter(t -> !"MP".equals(t.getMaterialTypeName()) && (t.getProductOrderID() == null || t.getProductOrderID().isEmpty())) .filter(t -> !"MP".equals(t.getMaterialTypeName()) && (t.getProductOrderID() == null || t.getProductOrderID().isEmpty()))
...@@ -2936,6 +2982,7 @@ if(geneDetails!=null&&geneDetails.size()>0) ...@@ -2936,6 +2982,7 @@ if(geneDetails!=null&&geneDetails.size()>0)
double totalSetupTime = calculateTotalSetupTime(chromosome); double totalSetupTime = calculateTotalSetupTime(chromosome);
chromosome.setTotalChangeoverTime(totalSetupTime); chromosome.setTotalChangeoverTime(totalSetupTime);
Objectives[i] = totalSetupTime; Objectives[i] = totalSetupTime;
} }
if (GlobalParam.OBJECTIVE_FLOW_TIME.equals(config.getName())) { if (GlobalParam.OBJECTIVE_FLOW_TIME.equals(config.getName())) {
// 4. 最小化总流程时间 所有工序加工时间的总和 // 4. 最小化总流程时间 所有工序加工时间的总和
......
...@@ -337,9 +337,10 @@ if(isJit) ...@@ -337,9 +337,10 @@ if(isJit)
} }
} }
} }
if(totalNeededByMaterial!=null&&totalNeededByMaterial.size()>0) {
//结合库存计算半成品生产数量,因为可能会修改生产数量所以和原材料分开 //结合库存计算半成品生产数量,因为可能会修改生产数量所以和原材料分开
materialRequirementService.CalBom(chromosome,0, totalNeededByMaterial,materials,baseTime.plusSeconds(estimatedStartTime) ,false,remove,entrysBygroupId,true); materialRequirementService.CalBom(chromosome, 0, totalNeededByMaterial, materials, baseTime.plusSeconds(estimatedStartTime), false, remove, entrysBygroupId, true);
}
totalNeededByMaterial.clear(); totalNeededByMaterial.clear();
for (int groupId : chainGroupIds) { for (int groupId : chainGroupIds) {
List<Entry> ops = entrysBygroupId.get(groupId); List<Entry> ops = entrysBygroupId.get(groupId);
......
...@@ -444,6 +444,24 @@ public class HillClimbing { ...@@ -444,6 +444,24 @@ public class HillClimbing {
} }
FileHelper.writeLogFile(String.format("爬山法%s - kpi:%s",chromosome.getGeneStr(), fitness)); FileHelper.writeLogFile(String.format("爬山法%s - kpi:%s",chromosome.getGeneStr(), fitness));
if(chromosome.getMakespan()!=0) {
FileHelper.writeLogFile(String.format("爬山法%s - kpi-Makespan: %f", chromosome.getGeneStr(), chromosome.getMakespan()));
}
if(chromosome.getDelayTime()!=0) {
FileHelper.writeLogFile(String.format("爬山法%s - kpi-DelayTime: %f", chromosome.getGeneStr(), chromosome.getDelayTime()));
}
if(chromosome.getTotalChangeoverTime()!=0) {
FileHelper.writeLogFile(String.format("爬山法%s - kpi-ChangeoverTime: %f",chromosome.getGeneStr(), chromosome.getTotalChangeoverTime()));
}
if(chromosome.getMachineLoadStd()!=0) {
FileHelper.writeLogFile(String.format("爬山法%s - kpi-MachineLoad: %f",chromosome.getGeneStr(), chromosome.getMachineLoadStd()));
}
if(chromosome.getTotalFlowTime()!=0) {
FileHelper.writeLogFile(String.format("爬山法%s - kpi-FlowTime: %f",chromosome.getGeneStr(), chromosome.getTotalFlowTime()));
}
} }
/** /**
...@@ -613,7 +631,20 @@ public class HillClimbing { ...@@ -613,7 +631,20 @@ public class HillClimbing {
// 假设Machine类有拷贝方法,或使用MapStruct等工具进行映射 // 假设Machine类有拷贝方法,或使用MapStruct等工具进行映射
chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(machines,Machine.class)); // 简单拷贝,实际可能需要深拷贝 chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(machines,Machine.class)); // 简单拷贝,实际可能需要深拷贝
chromosome.setOrders(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(orders), Order.class) ); // 简单拷贝,实际可能需要深拷贝
chromosome.setOperatRel(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(_entryRel), GroupResult.class) ); // 简单拷贝,实际可能需要深拷贝
chromosome.setMaterials(ProductionDeepCopyUtil.deepCopyTreeMap(materials,String.class,Material.class)); // 简单拷贝,实际可能需要深拷贝
chromosome.setAllOperations(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(allOperations), Entry.class) ); // 简单拷贝,实际可能需要深拷贝
// 加载锁定工单到ResultOld
List<GAScheduleResult> lockedOrders = GlobalCacheUtil.get("locked_orders_" + chromosome.getScenarioID());
if (lockedOrders != null && !lockedOrders.isEmpty()) {
chromosome.setResultOld(ProductionDeepCopyUtil.deepCopyList(lockedOrders, GAScheduleResult.class));
FileHelper.writeLogFile("将 " + lockedOrders.size() + " 个锁定工单加载到初始种群中");
} else {
chromosome.setResultOld(new CopyOnWriteArrayList<>());
}
} }
// 并行解码所有候选方案 // 并行解码所有候选方案
......
...@@ -490,6 +490,7 @@ public class HybridAlgorithm { ...@@ -490,6 +490,7 @@ public class HybridAlgorithm {
chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(machines,Machine.class)); // 简单拷贝,实际可能需要深拷贝 chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(machines,Machine.class)); // 简单拷贝,实际可能需要深拷贝
chromosome.setOrders(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(orders), Order.class) ); // 简单拷贝,实际可能需要深拷贝 chromosome.setOrders(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(orders), Order.class) ); // 简单拷贝,实际可能需要深拷贝
chromosome.setOperatRel(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(_entryRel), GroupResult.class) ); // 简单拷贝,实际可能需要深拷贝 chromosome.setOperatRel(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(_entryRel), GroupResult.class) ); // 简单拷贝,实际可能需要深拷贝
chromosome.setMaterials(ProductionDeepCopyUtil.deepCopyTreeMap(materials, String.class, Material.class)); // 简单拷贝,实际可能需要深拷贝 chromosome.setMaterials(ProductionDeepCopyUtil.deepCopyTreeMap(materials, String.class, Material.class)); // 简单拷贝,实际可能需要深拷贝
if (chromosome.getAllOperations() == null || chromosome.getAllOperations().isEmpty()) { if (chromosome.getAllOperations() == null || chromosome.getAllOperations().isEmpty()) {
......
...@@ -365,31 +365,32 @@ public class Initialization { ...@@ -365,31 +365,32 @@ public class Initialization {
chromo.setOrders(new CopyOnWriteArrayList<>(orders)); chromo.setOrders(new CopyOnWriteArrayList<>(orders));
if (i < sptCount) { if (i < sptCount) {
// SPT规则 // SPT规则 // 1 SPT(最短加工时间)
chromo.setGenerateType("SPT"); chromo.setGenerateType("SPT");
generateChromosomeByType(chromo, 1); generateChromosomeByType(chromo, 1);
} else if (i < eddsptCount) { } else if (i < eddsptCount) {
// EDD+SPT混合策略 // EDD+SPT混合策略 先按截止日期,再按加工时间
chromo.setGenerateType("EDD+SPT"); chromo.setGenerateType("EDD+SPT");
generateEDDSPTChromosome(chromo); generateEDDSPTChromosome(chromo);
} else if (i < eddCount) { } else if (i < eddCount) {
// EDD规则 // EDD规则 截止时间
chromo.setGenerateType("EDD"); chromo.setGenerateType("EDD");
generateChromosomeByType(chromo, 3); generateChromosomeByType(chromo, 3);
} else if (i < bottleneckCount) { } else if (i < bottleneckCount) {
// 瓶颈优先策略 // 瓶颈优先策略
chromo.setGenerateType("BottleneckFirst"); chromo.setGenerateType("BottleneckFirst");
generateBottleneckFirstChromosome(chromo); generateBottleneckFirstChromosome(chromo);
} else if (i < sstCount) { } else if (i < sstCount) {
// SST规则(已有物料排序) // SST规则(已有物料排序):优先选择准备时间最短的工序
chromo.setGenerateType("SST"); chromo.setGenerateType("SST");
generateSSTChromosome(chromo); generateSSTChromosome(chromo);
} else if (i < crCount) { } else if (i < crCount) {
// CR规则 // CR规则 CR(关键比率)规则:关键比率 = (截止日期 - 当前日期) / 剩余加工时间
chromo.setGenerateType("CR"); chromo.setGenerateType("CR");
generateCRChromosome(chromo); generateCRChromosome(chromo);
} else if (i < lptCount) { } else if (i < lptCount) {
// LPT规则 // LPT规则 2 LPT(最长加工时间)
chromo.setGenerateType("LPT"); chromo.setGenerateType("LPT");
generateChromosomeByType(chromo, 2); generateChromosomeByType(chromo, 2);
} else { } else {
...@@ -461,6 +462,7 @@ public class Initialization { ...@@ -461,6 +462,7 @@ public class Initialization {
} }
} else if (sortType == 3) { } else if (sortType == 3) {
//截止时间
if (considerSequence) { if (considerSequence) {
sortedOps.sort(Comparator.comparing((Entry op) -> op.getPriority()) sortedOps.sort(Comparator.comparing((Entry op) -> op.getPriority())
.thenComparing(Entry::getSequence) .thenComparing(Entry::getSequence)
......
...@@ -1254,6 +1254,9 @@ if(demand==null) ...@@ -1254,6 +1254,9 @@ if(demand==null)
if (needed <= 0) { if (needed <= 0) {
orderMaterial.setYpQty(allneeded - needed); orderMaterial.setYpQty(allneeded - needed);
orderMaterial.setQjQty(needed); orderMaterial.setQjQty(needed);
orderMaterial.setUseTime(baseTime);
orderMaterial.setPurchaseStartTime(baseTime);
orderMaterial.setPurchaseEndTime(baseTime);
if (commitChanges) { if (commitChanges) {
remove.add(orderMaterial); remove.add(orderMaterial);
} }
...@@ -1286,13 +1289,15 @@ if(demand==null) ...@@ -1286,13 +1289,15 @@ if(demand==null)
if (material1 == null) { if (material1 == null) {
break; break;
} }
FileHelper.writeLogFile("RoutingSupportingReplace: "+(commitChanges?"1":"0")+" " +material1.getCode()+": "+useStock);
OrderMaterialRequirement orderMaterial1 = MaterialStock(material1, rsr.getTargetmaterialid(), orderMaterial.getOrderId(), orderMaterial.getChildOrderId(), operationId, allneeded, needed, earliestStartTime, commitChanges); OrderMaterialRequirement orderMaterial1 = MaterialStock(material1, rsr.getTargetmaterialid(), orderMaterial.getOrderId(), orderMaterial.getChildOrderId(), operationId, allneeded, needed, earliestStartTime, commitChanges);
if (orderMaterial1 != null) { if (orderMaterial1 != null) {
useStock = orderMaterial1.getUseStock(); useStock = orderMaterial1.getUseStock();
needed -= useStock; needed -= useStock;
FileHelper.writeLogFile("RoutingSupportingReplace: "+material1.getCode()+": "+useStock); FileHelper.writeLogFile("RoutingSupportingReplace: "+(commitChanges?"1":"0")+" " +material1.getCode()+": "+useStock);
orderMaterial.setUseStock(orderMaterial.getUseStock() + useStock); orderMaterial.setUseStock(orderMaterial.getUseStock() + useStock);
orderMaterial.getReplaceMaterial().add(orderMaterial1); orderMaterial.getReplaceMaterial().add(orderMaterial1);
if (needed <= 0) { if (needed <= 0) {
...@@ -1307,6 +1312,9 @@ if(demand==null) ...@@ -1307,6 +1312,9 @@ if(demand==null)
if (needed <= 0) { if (needed <= 0) {
orderMaterial.setYpQty(allneeded - needed); orderMaterial.setYpQty(allneeded - needed);
orderMaterial.setQjQty(needed); orderMaterial.setQjQty(needed);
orderMaterial.setUseTime(baseTime);
orderMaterial.setPurchaseStartTime(baseTime);
orderMaterial.setPurchaseEndTime(baseTime);
if (commitChanges) { if (commitChanges) {
remove.add(orderMaterial); remove.add(orderMaterial);
} }
...@@ -1376,6 +1384,9 @@ if(demand==null) ...@@ -1376,6 +1384,9 @@ if(demand==null)
if (needed <= 0) { if (needed <= 0) {
orderMaterial.setYpQty(allneeded - needed); orderMaterial.setYpQty(allneeded - needed);
orderMaterial.setQjQty(needed); orderMaterial.setQjQty(needed);
orderMaterial.setUseTime(orderMaterial.getArrivalTime());
orderMaterial.setPurchaseStartTime(baseTime);
orderMaterial.setPurchaseEndTime(baseTime);
if (commitChanges) { if (commitChanges) {
remove.add(orderMaterial); remove.add(orderMaterial);
} }
...@@ -1537,6 +1548,7 @@ if(demand==null) ...@@ -1537,6 +1548,7 @@ if(demand==null)
.sorted(Comparator.comparing(MaterialSupply::getArrivalTime, Comparator.nullsLast(LocalDateTime::compareTo))) .sorted(Comparator.comparing(MaterialSupply::getArrivalTime, Comparator.nullsLast(LocalDateTime::compareTo)))
.collect(Collectors.toList()); .collect(Collectors.toList());
if(sortedInTransit!=null&&sortedInTransit.size()>0) {
for (MaterialSupply supply : sortedInTransit) { for (MaterialSupply supply : sortedInTransit) {
double useq = Math.min(needed, supply.getQuantity()); double useq = Math.min(needed, supply.getQuantity());
if (useq <= 0) { if (useq <= 0) {
...@@ -1544,7 +1556,7 @@ if(demand==null) ...@@ -1544,7 +1556,7 @@ if(demand==null)
} }
useTransit += useq; useTransit += useq;
needed -= useq; needed -= useq;
if(commitChanges) { if (commitChanges) {
supply.setQuantity(supply.getQuantity() - useq); supply.setQuantity(supply.getQuantity() - useq);
} }
if (earliestTime == null || (supply.getArrivalTime() != null && supply.getArrivalTime().isAfter(earliestTime))) { if (earliestTime == null || (supply.getArrivalTime() != null && supply.getArrivalTime().isAfter(earliestTime))) {
...@@ -1554,7 +1566,7 @@ if(demand==null) ...@@ -1554,7 +1566,7 @@ if(demand==null)
break; break;
} }
} }
}
orderMaterial.setUseTransit(useTransit); orderMaterial.setUseTransit(useTransit);
orderMaterial.setArrivalTime(earliestTime); orderMaterial.setArrivalTime(earliestTime);
......
...@@ -1936,6 +1936,18 @@ if(targetOp.getSequence()>1) { ...@@ -1936,6 +1936,18 @@ if(targetOp.getSequence()>1) {
} }
resetDecodeMaterials(chromosome, baseMaterialsSnapshot); resetDecodeMaterials(chromosome, baseMaterialsSnapshot);
// 重解码前重新初始化物料需求缓存,同时给MaterialRequirementService设置本次排产基准时间。
List<Material> decodeMaterials = chromosome.getMaterials() == null
? new ArrayList<>()
: new ArrayList<>(chromosome.getMaterials().values());
materialRequirementService.preloadRoutingCache(
chromosome.getScenarioID(),
baseTime,
chromosome.getOrders(),
decodeMaterials,
chromosome.getAllOperations(),
globalParam != null && globalParam.isIsCheckSf()
);
MachineSchedulerService machineScheduler = new MachineSchedulerService(baseTime); MachineSchedulerService machineScheduler = new MachineSchedulerService(baseTime);
chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(chromosome.getInitMachines(),Machine.class) ); chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(chromosome.getInitMachines(),Machine.class) );
...@@ -1943,8 +1955,9 @@ if(targetOp.getSequence()>1) { ...@@ -1943,8 +1955,9 @@ if(targetOp.getSequence()>1) {
chromosome.getResult().clear(); chromosome.getResult().clear();
chromosome.setResult(new CopyOnWriteArrayList<>()); chromosome.setResult(new CopyOnWriteArrayList<>());
// BOM计算会使用decoder里的materials,自动插单重排也必须传入当前物料快照。
return new GeneticDecoder(globalParam,baseTime, chromosome.getMachines(), return new GeneticDecoder(globalParam,baseTime, chromosome.getMachines(),
chromosome.getOrders(), null, machineScheduler,materialRequirementService,chromosome.getScenarioID()); chromosome.getOrders(), chromosome.getMaterials(), machineScheduler,materialRequirementService,chromosome.getScenarioID());
} }
private TreeMap<String, Material> resolveDecodeMaterials(Chromosome chromosome) { private TreeMap<String, Material> resolveDecodeMaterials(Chromosome chromosome) {
......
...@@ -5,6 +5,8 @@ import com.aps.common.util.R; ...@@ -5,6 +5,8 @@ import com.aps.common.util.R;
import com.aps.entity.Algorithm.Chromosome; import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GAScheduleResult; import com.aps.entity.Algorithm.GAScheduleResult;
import com.aps.entity.Algorithm.OrderMaterialRequirement; import com.aps.entity.Algorithm.OrderMaterialRequirement;
import com.aps.entity.Gantt.ResourceGanttVO;
import com.aps.entity.Gantt.TaskVO;
import com.aps.entity.ProdProcessExec; import com.aps.entity.ProdProcessExec;
import com.aps.entity.Algorithm.GlobalOperationInfo; import com.aps.entity.Algorithm.GlobalOperationInfo;
import com.aps.entity.Algorithm.IDAndChildID.GroupResult; import com.aps.entity.Algorithm.IDAndChildID.GroupResult;
...@@ -307,6 +309,7 @@ public class ChromosomeDataService { ...@@ -307,6 +309,7 @@ public class ChromosomeDataService {
entityClassMap.put("prodprocessexec", ProdProcessExec.class); entityClassMap.put("prodprocessexec", ProdProcessExec.class);
entityClassMap.put("globaloperationinfo", GlobalOperationInfo.class); entityClassMap.put("globaloperationinfo", GlobalOperationInfo.class);
entityClassMap.put("groupresult", GroupResult.class); entityClassMap.put("groupresult", GroupResult.class);
entityClassMap.put("task", TaskVO.class);
return entityClassMap.get(entityName.toLowerCase()); return entityClassMap.get(entityName.toLowerCase());
} }
...@@ -418,7 +421,7 @@ public class ChromosomeDataService { ...@@ -418,7 +421,7 @@ public class ChromosomeDataService {
Map<String, Object> emptyResult = new HashMap<>(); Map<String, Object> emptyResult = new HashMap<>();
emptyResult.put("records", Collections.emptyList()); emptyResult.put("records", Collections.emptyList());
emptyResult.put("totalCount", 0); emptyResult.put("totalCount", 0);
emptyResult.put("pageIndex", paged.getPageIndex() != null ? paged.getPageIndex() : 1); emptyResult.put("pageIndex", paged.getPageIndex() != null ? Math.max(1, paged.getPageIndex()) : 1);
emptyResult.put("size", paged.getPageSize() != null ? paged.getPageSize() : 10); emptyResult.put("size", paged.getPageSize() != null ? paged.getPageSize() : 10);
return emptyResult; return emptyResult;
} }
...@@ -817,7 +820,7 @@ public class ChromosomeDataService { ...@@ -817,7 +820,7 @@ public class ChromosomeDataService {
} }
// 应用分页 // 应用分页
if (paged.getPageIndex() != null && paged.getPageSize() != null && paged.getPageSize() > 0) { if (paged.getPageIndex() != null && paged.getPageSize() != null && paged.getPageSize() > 0) {
int pageIndex = paged.getPageIndex(); int pageIndex = Math.max(1, paged.getPageIndex());
int pageSize = paged.getPageSize(); int pageSize = paged.getPageSize();
int start = (pageIndex - 1) * pageSize; int start = (pageIndex - 1) * pageSize;
int end = Math.min(start + pageSize, result.size()); int end = Math.min(start + pageSize, result.size());
...@@ -1180,6 +1183,13 @@ public class ChromosomeDataService { ...@@ -1180,6 +1183,13 @@ public class ChromosomeDataService {
config.setEntityName(entityName); config.setEntityName(entityName);
config.setDataSource(DataSourceType.FILE); config.setDataSource(DataSourceType.FILE);
config.setFieldName("orders"); config.setFieldName("orders");
}
// 特殊处理:当实体是Task时,取资源甘特图中的TaskVO列表。
else if ("task".equalsIgnoreCase(key)) {
config = new EntityConfig();
config.setEntityName(entityName);
config.setDataSource(DataSourceType.FILE);
config.setFieldName("task");
} else { } else {
// 自动创建数据库配置(默认行为) // 自动创建数据库配置(默认行为)
config = createDefaultDbConfig(entityName); config = createDefaultDbConfig(entityName);
...@@ -1277,6 +1287,12 @@ public class ChromosomeDataService { ...@@ -1277,6 +1287,12 @@ public class ChromosomeDataService {
} }
try { try {
if ("task".equalsIgnoreCase(config.getEntityName())) {
Object result = extractResourceGanttTasks(chromosome);
fileDataCache.put(cacheKey, result);
return result;
}
String fieldName = config.getFieldName(); String fieldName = config.getFieldName();
// 特殊处理:当实体是MachineOption时,使用allOperations字段 // 特殊处理:当实体是MachineOption时,使用allOperations字段
...@@ -1304,6 +1320,22 @@ public class ChromosomeDataService { ...@@ -1304,6 +1320,22 @@ public class ChromosomeDataService {
} }
} }
private List<TaskVO> extractResourceGanttTasks(Chromosome chromosome) {
if (chromosome == null) {
return new ArrayList<>();
}
List<Machine> machineList = planResultService.InitCalendarToAllMachines3(chromosome);
List<ResourceGanttVO> resourceGanttVOs = planResultService.convertToResourceGanttVO1(chromosome, machineList);
if (resourceGanttVOs == null || resourceGanttVOs.isEmpty()) {
return new ArrayList<>();
}
return resourceGanttVOs.stream()
.filter(Objects::nonNull)
.filter(vo -> vo.getList() != null)
.flatMap(vo -> vo.getList().stream())
.collect(Collectors.toList());
}
/** /**
* 从缓存中获取反射字段,避免重复反射操作 * 从缓存中获取反射字段,避免重复反射操作
*/ */
...@@ -1573,7 +1605,7 @@ public class ChromosomeDataService { ...@@ -1573,7 +1605,7 @@ public class ChromosomeDataService {
} }
Map<String, Object> result = new HashMap<>(); Map<String, Object> result = new HashMap<>();
int page = paged.getPageIndex() != null ? paged.getPageIndex() : 1; int page = paged.getPageIndex() != null ? Math.max(1, paged.getPageIndex()) : 1;
int size = paged.getPageSize() != null ? paged.getPageSize() : 10; int size = paged.getPageSize() != null ? paged.getPageSize() : 10;
if (data instanceof List) { if (data instanceof List) {
......
...@@ -65,7 +65,7 @@ public class DatabaseQueryService { ...@@ -65,7 +65,7 @@ public class DatabaseQueryService {
String orderBy = buildOrderBy(paged); String orderBy = buildOrderBy(paged);
// 分页参数 // 分页参数
int page = paged.getPageIndex() != null ? paged.getPageIndex() : 1; int page = paged.getPageIndex() != null ? Math.max(1, paged.getPageIndex()) : 1;
int size = paged.getPageSize() != null ? paged.getPageSize() : 10; int size = paged.getPageSize() != null ? paged.getPageSize() : 10;
// 返回列:fields 有值时只查指定列,否则 SELECT * // 返回列:fields 有值时只查指定列,否则 SELECT *
...@@ -157,7 +157,7 @@ public class DatabaseQueryService { ...@@ -157,7 +157,7 @@ public class DatabaseQueryService {
Map<String, Object> empty = new HashMap<>(); Map<String, Object> empty = new HashMap<>();
empty.put("records", Collections.emptyList()); empty.put("records", Collections.emptyList());
empty.put("totalCount", 0); empty.put("totalCount", 0);
empty.put("pageIndex", paged.getPageIndex() != null ? paged.getPageIndex() : 1); empty.put("pageIndex", paged.getPageIndex() != null ? Math.max(1, paged.getPageIndex()) : 1);
empty.put("size", paged.getPageSize() != null ? paged.getPageSize() : 10); empty.put("size", paged.getPageSize() != null ? paged.getPageSize() : 10);
return empty; return empty;
} }
...@@ -165,7 +165,7 @@ public class DatabaseQueryService { ...@@ -165,7 +165,7 @@ public class DatabaseQueryService {
String groupColsStr = String.join(", ", groupCols); String groupColsStr = String.join(", ", groupCols);
// 修复分组查询时的ORDER BY语法错误 // 修复分组查询时的ORDER BY语法错误
String orderBy = buildOrderByForGroupBy(paged, groupCols); String orderBy = buildOrderByForGroupBy(paged, groupCols);
int page = paged.getPageIndex() != null ? paged.getPageIndex() : 1; int page = paged.getPageIndex() != null ? Math.max(1, paged.getPageIndex()) : 1;
int size = paged.getPageSize() != null ? paged.getPageSize() : 10; int size = paged.getPageSize() != null ? paged.getPageSize() : 10;
String countSql = "SELECT COUNT(*) FROM (SELECT " + groupColsStr + " FROM " + tableName + whereClause + " GROUP BY " + groupColsStr + ")"; String countSql = "SELECT COUNT(*) FROM (SELECT " + groupColsStr + " FROM " + tableName + whereClause + " GROUP BY " + groupColsStr + ")";
...@@ -218,8 +218,10 @@ public class DatabaseQueryService { ...@@ -218,8 +218,10 @@ public class DatabaseQueryService {
} }
private String buildOraclePaginationSqlForGroupBy(String innerSql, int page, int size) { private String buildOraclePaginationSqlForGroupBy(String innerSql, int page, int size) {
int startRow = (page - 1) * size + 1; int safePage = Math.max(1, page);
int endRow = page * size; int offsetPage = safePage - 1;
int startRow = offsetPage * size + 1;
int endRow = safePage * size;
return "SELECT * FROM (SELECT a.*, ROWNUM rn FROM (" + innerSql + ") a WHERE ROWNUM <= " + endRow + ") WHERE rn >= " + startRow; return "SELECT * FROM (SELECT a.*, ROWNUM rn FROM (" + innerSql + ") a WHERE ROWNUM <= " + endRow + ") WHERE rn >= " + startRow;
} }
...@@ -441,8 +443,10 @@ public class DatabaseQueryService { ...@@ -441,8 +443,10 @@ public class DatabaseQueryService {
* 构建Oracle分页SQL;selectClause 为 null 或空时使用 *。 * 构建Oracle分页SQL;selectClause 为 null 或空时使用 *。
*/ */
private String buildOraclePaginationSql(String tableName, String whereClause, String orderBy, int page, int size, String selectClause) { private String buildOraclePaginationSql(String tableName, String whereClause, String orderBy, int page, int size, String selectClause) {
int startRow = (page - 1) * size + 1; int safePage = Math.max(1, page);
int endRow = page * size; int offsetPage = safePage - 1;
int startRow = offsetPage * size + 1;
int endRow = safePage * size;
String cols = (selectClause != null && !selectClause.trim().isEmpty()) ? selectClause : "*"; String cols = (selectClause != null && !selectClause.trim().isEmpty()) ? selectClause : "*";
StringBuilder sql = new StringBuilder(); StringBuilder sql = new StringBuilder();
......
...@@ -2000,13 +2000,8 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -2000,13 +2000,8 @@ public class LanuchServiceImpl implements LanuchService {
throw new RuntimeException("物料不存在"); throw new RuntimeException("物料不存在");
} }
// 4. 查询工艺路线(按创建时间倒序,获取最新的) // 4. 查询工艺路线:插入已有场景时优先沿用场景中同物料已使用的路线,避免误取到最新但未维护完整的路线。
RoutingHeader routingHeader = routingHeaderService.lambdaQuery() RoutingHeader routingHeader = resolveRoutingHeaderForInsert(sceneId, materialId);
.eq(RoutingHeader::getMaterialId, materialId)
.eq(RoutingHeader::getIsDeleted, 0)
.orderByDesc(RoutingHeader::getCreationTime)
.last("FETCH FIRST 1 ROWS ONLY")
.one();
if (routingHeader == null) { if (routingHeader == null) {
throw new RuntimeException("物料没有配置工艺路线"); throw new RuntimeException("物料没有配置工艺路线");
} }
...@@ -2050,4 +2045,87 @@ public class LanuchServiceImpl implements LanuchService { ...@@ -2050,4 +2045,87 @@ public class LanuchServiceImpl implements LanuchService {
log.info("插单成功,场景ID: {}, 订单编码: {}", sceneId, orderCode); log.info("插单成功,场景ID: {}, 订单编码: {}", sceneId, orderCode);
return R.ok("插单成功,订单ID: " + orderId); return R.ok("插单成功,订单ID: " + orderId);
} }
private RoutingHeader resolveRoutingHeaderForInsert(String sceneId, String materialId) {
List<RoutingHeader> candidates = routingHeaderService.lambdaQuery()
.eq(RoutingHeader::getMaterialId, materialId)
.eq(RoutingHeader::getIsDeleted, 0)
.orderByDesc(RoutingHeader::getCreationTime)
.list();
if (CollectionUtils.isEmpty(candidates)) {
return null;
}
Set<Integer> sceneRoutingIds = prodLaunchOrderService.lambdaQuery()
.eq(ProdLaunchOrder::getSceneId, sceneId)
.eq(ProdLaunchOrder::getMaterialId, materialId)
.isNotNull(ProdLaunchOrder::getRoutingId)
.list()
.stream()
.map(ProdLaunchOrder::getRoutingId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
Optional<RoutingHeader> sceneRouting = candidates.stream()
.filter(header -> sceneRoutingIds.contains(header.getId()))
.filter(this::hasResolvableProcessResources)
.findFirst();
if (sceneRouting.isPresent()) {
return sceneRouting.get();
}
Optional<RoutingHeader> namedValidRouting = candidates.stream()
.filter(header -> !isUndefinedRoutingCode(header))
.filter(this::hasResolvableProcessResources)
.findFirst();
if (namedValidRouting.isPresent()) {
return namedValidRouting.get();
}
Optional<RoutingHeader> anyValidRouting = candidates.stream()
.filter(this::hasResolvableProcessResources)
.findFirst();
return anyValidRouting.orElse(candidates.get(0));
}
private boolean hasResolvableProcessResources(RoutingHeader routingHeader) {
if (routingHeader == null || routingHeader.getId() == null) {
return false;
}
Long routingHeaderId = routingHeader.getId().longValue();
List<RoutingDetail> details = routingDetailMapper.selectList(new LambdaQueryWrapper<RoutingDetail>()
.eq(RoutingDetail::getRoutingHeaderId, routingHeaderId)
.eq(RoutingDetail::getIsDeleted, 0));
if (CollectionUtils.isEmpty(details)) {
return false;
}
Map<Long, List<RoutingDetailEquip>> equipsByDetailId = routingDetailEquipService.lambdaQuery()
.eq(RoutingDetailEquip::getRoutingHeaderId, routingHeaderId)
.eq(RoutingDetailEquip::getIsdeleted, 0)
.list()
.stream()
.filter(Objects::nonNull)
.filter(equip -> equip.getRoutingDetailId() != null)
.collect(Collectors.groupingBy(RoutingDetailEquip::getRoutingDetailId));
for (RoutingDetail detail : details) {
if (detail.getEquipTypeId() != null) {
continue;
}
boolean hasDetailEquipType = equipsByDetailId.getOrDefault(detail.getId(), Collections.emptyList())
.stream()
.anyMatch(equip -> equip.getType1() != null);
if (!hasDetailEquipType) {
return false;
}
}
return true;
}
private boolean isUndefinedRoutingCode(RoutingHeader routingHeader) {
String code = routingHeader == null ? null : routingHeader.getCode();
return code != null && code.trim().toLowerCase(Locale.ROOT).startsWith("undefined");
}
} }
...@@ -27,6 +27,7 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; ...@@ -27,6 +27,7 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.Color; import java.awt.Color;
...@@ -39,7 +40,9 @@ import java.io.IOException; ...@@ -39,7 +40,9 @@ import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.*; import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
...@@ -173,7 +176,7 @@ public class PlanResultService { ...@@ -173,7 +176,7 @@ public class PlanResultService {
* 后续会按场景创建人自动回退到可用的策略配置。 * 后续会按场景创建人自动回退到可用的策略配置。
*/ */
public Chromosome execute2(String SceneId) { public Chromosome execute2(String SceneId) {
return execute2(SceneId, null, null, null); return execute2(SceneId, 2361l, 241l, null);
} }
/** /**
...@@ -1393,13 +1396,84 @@ public class PlanResultService { ...@@ -1393,13 +1396,84 @@ public class PlanResultService {
return releasedCount; return releasedCount;
} }
private String getStringValue(Map<String, Object> data, String key) {
Object value = data == null ? null : data.get(key);
if (value == null) {
return null;
}
String text = String.valueOf(value).trim();
return text.isEmpty() ? null : text;
}
private String getFirstStringValue(Map<String, Object> data, String... keys) {
if (keys == null) {
return null;
}
for (String key : keys) {
String value = getStringValue(data, key);
if (value != null) {
return value;
}
}
return null;
}
private LocalDateTime parseOrderDateTime(Object value) {
if (value == null) {
return null;
}
if (value instanceof LocalDateTime) {
return (LocalDateTime) value;
}
String text = String.valueOf(value).trim();
if (text.isEmpty()) {
return null;
}
try {
return LocalDateTime.parse(text);
} catch (DateTimeParseException ignored) {
return OffsetDateTime.parse(text).toLocalDateTime();
}
}
private Integer parseOrderPriority(Object value) {
if (value == null || String.valueOf(value).trim().isEmpty()) {
return 1;
}
return Integer.valueOf(String.valueOf(value).trim());
}
private void applyNewOrderPayload(ProdLaunchOrder launchOrder, Map<String, Object> newOrderData) {
if (launchOrder == null || newOrderData == null) {
return;
}
String materialCode = getFirstStringValue(newOrderData, "mmcode", "materialCode");
if (materialCode != null) {
launchOrder.setMaterialCode(materialCode);
}
String materialName = getFirstStringValue(newOrderData, "mmname", "materialName");
if (materialName != null) {
launchOrder.setMaterialName(materialName);
}
String serie = getFirstStringValue(newOrderData, "series", "seriesName", "serie");
if (serie != null) {
launchOrder.setSerie(serie);
}
String groupCode = getFirstStringValue(newOrderData, "zone", "zoneid", "groupCode");
if (groupCode != null) {
launchOrder.setGroupCode(groupCode);
}
}
@Transactional(rollbackFor = Exception.class)
public Chromosome InsertOrderAuto(String sceneId, Map<String, Object> newOrderData) { public Chromosome InsertOrderAuto(String sceneId, Map<String, Object> newOrderData) {
if (newOrderData == null) { if (newOrderData == null) {
throw new RuntimeException("newOrder 不能为空"); throw new RuntimeException("newOrder 不能为空");
} }
String orderCode = String.valueOf(newOrderData.get("orderCode")); String orderCode = getStringValue(newOrderData, "orderCode");
String materialId = String.valueOf(newOrderData.get("materialId")); String materialId = getStringValue(newOrderData, "materialId");
Object qtyObj = newOrderData.get("quantity"); Object qtyObj = newOrderData.get("quantity");
if (orderCode == null || orderCode.trim().isEmpty()) { if (orderCode == null || orderCode.trim().isEmpty()) {
throw new RuntimeException("orderCode 不能为空"); throw new RuntimeException("orderCode 不能为空");
...@@ -1411,9 +1485,13 @@ public class PlanResultService { ...@@ -1411,9 +1485,13 @@ public class PlanResultService {
throw new RuntimeException("quantity 不能为空"); throw new RuntimeException("quantity 不能为空");
} }
Double quantity = Double.valueOf(String.valueOf(qtyObj)); Double quantity = Double.valueOf(String.valueOf(qtyObj));
LocalDateTime startDate = parseOrderDateTime(newOrderData.get("startDate"));
LocalDateTime endDate = parseOrderDateTime(newOrderData.get("endDate"));
Integer priority = parseOrderPriority(newOrderData.get("priority"));
// 1. 创建新订单(沿用现有创建逻辑) // 1. 创建新订单(沿用现有创建逻辑)
R<String> insertResp = lanuchService.insertOrder(sceneId, orderCode, materialId, null, null, 1, quantity); R<String> insertResp = lanuchService.insertOrder(sceneId, orderCode, materialId,
startDate, endDate, priority, quantity);
String insertMsg = insertResp != null ? insertResp.getData() : null; String insertMsg = insertResp != null ? insertResp.getData() : null;
if (insertMsg == null || insertMsg.trim().isEmpty()) { if (insertMsg == null || insertMsg.trim().isEmpty()) {
throw new RuntimeException("创建订单失败:未返回订单ID"); throw new RuntimeException("创建订单失败:未返回订单ID");
...@@ -1438,6 +1516,8 @@ public class PlanResultService { ...@@ -1438,6 +1516,8 @@ public class PlanResultService {
if (newLaunchOrder == null) { if (newLaunchOrder == null) {
throw new RuntimeException("新订单不存在:" + newOrderId); throw new RuntimeException("新订单不存在:" + newOrderId);
} }
applyNewOrderPayload(newLaunchOrder, newOrderData);
_prodLaunchOrderService.updateById(newLaunchOrder);
List<ProdProcessExec> newProcessExecs = _prodProcessExecService.lambdaQuery() List<ProdProcessExec> newProcessExecs = _prodProcessExecService.lambdaQuery()
.eq(ProdProcessExec::getSceneId, sceneId) .eq(ProdProcessExec::getSceneId, sceneId)
...@@ -1477,6 +1557,11 @@ public class PlanResultService { ...@@ -1477,6 +1557,11 @@ public class PlanResultService {
if (apsTimeConfig != null && apsTimeConfig.getBaseTime() != null) { if (apsTimeConfig != null && apsTimeConfig.getBaseTime() != null) {
baseTime = apsTimeConfig.getBaseTime(); baseTime = apsTimeConfig.getBaseTime();
} }
if (baseTime == null) {
throw new RuntimeException("自动插单失败:排产基准时间为空");
}
// 自动插单后会重解码,染色体里的基准时间也要同步,避免后续按空基准时间计算。
chromosome.setBaseTime(baseTime);
LocalDateTime anchorTime = baseTime.plusSeconds(Math.max(freezeSeconds, 0L)); LocalDateTime anchorTime = baseTime.plusSeconds(Math.max(freezeSeconds, 0L));
// 5. 自动插单(占位后推 + 空挡前移) // 5. 自动插单(占位后推 + 空挡前移)
...@@ -2179,6 +2264,7 @@ if(job.getGeneDetails()!=null) ...@@ -2179,6 +2264,7 @@ if(job.getGeneDetails()!=null)
GlobalParam globalParam = InitGlobalParam(); GlobalParam globalParam = InitGlobalParam();
Object kpiConfig = scheduleStrategyService.loadEffectiveKpiConfig(userId, baseRuleId, sceneId, userRuleId); Object kpiConfig = scheduleStrategyService.loadEffectiveKpiConfig(userId, baseRuleId, sceneId, userRuleId);
globalParam.applyKpiConfig(kpiConfig); globalParam.applyKpiConfig(kpiConfig);
globalParam.setJit(scheduleStrategyService.loadEffectiveIsJit(userId, baseRuleId, sceneId, userRuleId));
return globalParam; return globalParam;
} }
......
...@@ -90,6 +90,29 @@ public class ScheduleStrategyService { ...@@ -90,6 +90,29 @@ public class ScheduleStrategyService {
} }
} }
public boolean loadEffectiveIsJit(Long userId, Long baseRuleId, String sceneId, String userRuleId) {
try {
Map<String, Object> params = new HashMap<>();
Long effectiveUserId = resolveScheduleUserId(sceneId, userId);
if (effectiveUserId != null) {
params.put("userId", effectiveUserId);
}
if (baseRuleId != null) {
params.put("baseRuleId", baseRuleId);
}
if (userRuleId != null && !userRuleId.trim().isEmpty()) {
params.put("userRuleId", userRuleId);
}
Map<String, Object> effectiveRule = userStrategyRuleService.getEffectiveRule(params);
return effectiveRule != null && asBoolean(effectiveRule.get("isjit"), false);
} catch (Exception e) {
log.warn("Load JIT strategy failed, use GlobalParam default isJit=false. sceneId={}, userId={}, baseRuleId={}, userRuleId={}",
sceneId, userId, baseRuleId, userRuleId, e);
return false;
}
}
public OrderSortRule createMultiConditionRule(List<Order> orders) { public OrderSortRule createMultiConditionRule(List<Order> orders) {
return createMultiConditionRule(orders, Collections.emptyList()); return createMultiConditionRule(orders, Collections.emptyList());
} }
...@@ -241,4 +264,24 @@ public class ScheduleStrategyService { ...@@ -241,4 +264,24 @@ public class ScheduleStrategyService {
condition.setReverse(reverse); condition.setReverse(reverse);
return condition; return condition;
} }
private boolean asBoolean(Object value, boolean defaultValue) {
if (value == null) {
return defaultValue;
}
if (value instanceof Boolean) {
return (Boolean) value;
}
if (value instanceof Number) {
return ((Number) value).intValue() != 0;
}
String text = String.valueOf(value).trim();
if ("1".equals(text)) {
return true;
}
if ("0".equals(text)) {
return false;
}
return text.isEmpty() ? defaultValue : Boolean.parseBoolean(text);
}
} }
...@@ -41,7 +41,8 @@ public class PlanResultServiceTest { ...@@ -41,7 +41,8 @@ public class PlanResultServiceTest {
// sortService.test1(); // sortService.test1();
// nsgaiiUtils.Test(); // nsgaiiUtils.Test();
planResultService.execute2("72744D094BAB45948F5172E84C78B260");//2000 // planResultService.execute2("64E64F6B68094AF38CEDC418630C3CC2");//2000
planResultService.execute2("E1448B3C9C8743DEAB39708F2CFE348A");//2000
// planResultService.execute2("15210B13B88A453F8B84AAC7F16C7541");//2000 // planResultService.execute2("15210B13B88A453F8B84AAC7F16C7541");//2000
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment