Commit 6c291a36 authored by Tong Li's avatar Tong Li

CP

parent fef1ef87
......@@ -193,4 +193,15 @@ public class Order {
* 多级排序路径(用于精确字典序排序,避免浮点精度问题)
*/
private List<Integer> sortKey;
/**
* 合并来源订单ID列表(合并订单时记录原始订单ID,用于结果追溯)
*/
private List<String> mergedChildOrderIds;
/**
* 是否为合并订单
*/
private boolean merged = false;
}
\ No newline at end of file
......@@ -74,8 +74,12 @@ public class CpSatInitializer {
} else if (opCount <= LARGE_SCALE_THRESHOLD) {
return largeScaleDecomposed(globalOpList, targetCount, timeBudgetSec);
} else {
FileHelper.writeLogFile("[CP-SAT] 工序数超过阈值,跳过CP-SAT,回退启发式");
return Collections.emptyList();
return largeScaleDecomposed(globalOpList, targetCount, timeBudgetSec);
// FileHelper.writeLogFile("[CP-SAT] 工序数超过阈值,跳过CP-SAT,回退启发式");
// return Collections.emptyList();
}
} catch (Exception e) {
FileHelper.writeLogFile("[CP-SAT] 求解异常,回退启发式: " + e.getMessage());
......
......@@ -98,8 +98,10 @@ public class HybridAlgorithm {
// .filter(t->existIds.add(t.getId()))//HashSet.add() 方法:添加成功返回 true,重复返回 false;
// .collect(Collectors.toList());
// }
LocalDateTime starttime=LocalDateTime.now();
FileHelper.writeLogFile("排产-----------开始-----------"+allOperations.get(0).getSceneId());
FileHelper.writeLogFile("排产-----------开始-----"+ (_GlobalParam.isJit()?"倒排":"正排")+"------"+allOperations.get(0).getSceneId());
// 在整个流程开始时创建一个全局 shared decoder,所有阶段共享解码缓存
GeneticDecoder sharedDecoder = new GeneticDecoder(_GlobalParam, param.getBaseTime(), machines, orders, materials, machineScheduler, materialRequirementService, sceneId);
......
......@@ -63,6 +63,11 @@ public class RoutingDataService {
private EquipinfoService _equipinfoService;
public Map<Integer, Object> InitEntrys(String SceneId, List<ProdEquipment> ProdEquipments, List<Order> ProdLaunchOrders)
{
return InitEntrys(SceneId, ProdEquipments, ProdLaunchOrders, null);
}
public Map<Integer, Object> InitEntrys(String SceneId, List<ProdEquipment> ProdEquipments, List<Order> ProdLaunchOrders, Map<String, Order> mergeMap)
{
Map<Integer, Object> list=new HashMap<>();
List<ProdProcessExec> ProdProcessExecs= _prodProcessExecService.lambdaQuery()
......@@ -111,6 +116,11 @@ public class RoutingDataService {
}
public Map<Integer, Object> CreateEntry(String SceneId, List<ProdEquipment> ProdEquipments, List<Order> ProdLaunchOrders, List<RoutingDiscreteParam> routingDiscreteParams, List<ProdOrderProcess> ProdOrderProcesss, List<ProdProcessExec> ProdProcessExecs, List<GroupResult> existingResults,int FinishOpertionID)
{
return CreateEntry(SceneId, ProdEquipments, ProdLaunchOrders, routingDiscreteParams, ProdOrderProcesss, ProdProcessExecs, existingResults, FinishOpertionID, null);
}
public Map<Integer, Object> CreateEntry(String SceneId, List<ProdEquipment> ProdEquipments, List<Order> ProdLaunchOrders, List<RoutingDiscreteParam> routingDiscreteParams, List<ProdOrderProcess> ProdOrderProcesss, List<ProdProcessExec> ProdProcessExecs, List<GroupResult> existingResults,int FinishOpertionID, Map<String, Order> mergeMap)
{
Map<Integer, Object> list=new HashMap<>();
List<String> soutceExecId = ProdOrderProcesss.stream()
......@@ -240,9 +250,9 @@ public class RoutingDataService {
entry.setDepartmentId(op.getDepartmentId());
}
entry.setIsInterrupt(op.getCanInterrupt());
Order order = ProdLaunchOrders.stream()
.filter(t -> t.getOrderId().equals(op.getOrderId()))
.findFirst().orElse(null);
Order order = findOrderByOrderId(ProdLaunchOrders, mergeMap, op.getOrderId());
if (order != null) {
entry.setOrderCode(order.getOrderCode());
entry.setProductId(order.getMaterialId());
......@@ -315,7 +325,14 @@ if(entry.getMachineOptions()!=null)
return list;
}
private Order findOrderByOrderId(List<Order> orderList, Map<String, Order> mergeMap, String orderId) {
if (mergeMap != null && mergeMap.containsKey(orderId)) {
return mergeMap.get(orderId);
}
return orderList.stream()
.filter(t -> t.getOrderId().equals(orderId))
.findFirst().orElse(null);
}
public List<Machine> InitCalendarToAllMachines(String SceneId, List<ProdEquipment> ProdEquipments, MachineSchedulerService machineScheduler,boolean IsUseCalendar) {
// 按设备分组
......
......@@ -188,7 +188,7 @@ public class PlanResultService {
try {
ScheduleParams param = InitScheduleParams();
// param.setBaseTime(LocalDateTime.of(2026, 5, 20, 0, 0, 0));
// param.setBaseTime(LocalDateTime.of(2026, 5, 20, 0, 0, 0));
this.baseTime=param.getBaseTime();
// 策略读取入口:优先使用前端传入的 userId;没传时用 sceneId 查场景创建人。
Long effectiveUserId = scheduleStrategyService.resolveScheduleUserId(SceneId, userId);
......@@ -275,9 +275,9 @@ public class PlanResultService {
// 5. 执行调度算法
HybridAlgorithm scheduler =new HybridAlgorithm(globalParam,machines,orders,materialmap,materialIds,machineScheduler,entryRel,materialRequirementService,_sceneService,SceneId); //new GeneticAlgorithm(products, machines, orders, machineScheduler);
param.initAdaptiveParams(entrys.size());
double[] customWeights = new double[] { 0.4, 0.1, 0.1, 0.1, 0.3 }; // 延迟时间权重提升到0.5
// double[] customWeights = new double[] { 0.4, 0.1, 0.1, 0.1, 0.3 }; // 延迟时间权重提升到0.5
//完工时间、总流程时间、总换型时间、机器负载标准差、延迟时间
scheduler.Init();
// scheduler.Init();
Chromosome chromosome =scheduler.Run(param,entrys);
KpiCalculator kpiCalculator=new KpiCalculator(chromosome);
......@@ -2402,6 +2402,184 @@ if(job.getGeneDetails()!=null)
return orders;
}
/**
* 排产前合并兼容订单:相同物料+相同工艺路线+相同系列+时间窗口重叠且交付日期相近的订单合并为一个
* @param orders 原始订单列表
* @param materials 物料列表(用于 maxProduction 上限校验)
* @return entry: key=合并后的订单列表, value=originalOrderId→mergedOrder 映射表
*/
public AbstractMap.SimpleEntry<List<Order>, Map<String, Order>> mergeOrders(List<Order> orders, List<Material> materials) {
return mergeOrders(orders, materials, DEFAULT_MERGE_DUE_DATE_RANGE_DAYS);
}
/**
* 使用 OR-Tools CP-SAT 求解最优合并方案(替代贪心算法)
* 将兼容订单建模为 Bin Packing,最小化合并组数
*
* @param orders 原始订单列表
* @param materials 物料列表
* @param dueDateRangeDays 交付日期相差上限(天)
* @return entry: key=合并后的订单列表, value=originalOrderId→mergedOrder 映射表
*/
public AbstractMap.SimpleEntry<List<Order>, Map<String, Order>> mergeOrdersWithCpSat(
List<Order> orders, List<Material> materials, int dueDateRangeDays) {
CpSatOrderMergeService solver = new CpSatOrderMergeService();
return solver.mergeOrders(orders, materials, dueDateRangeDays);
}
/**
* @param dueDateRangeDays 允许合并的交付日期最大相差天数
*/
public AbstractMap.SimpleEntry<List<Order>, Map<String, Order>> mergeOrders(List<Order> orders, List<Material> materials, int dueDateRangeDays) {
if (orders == null || orders.size() <= 1) {
return new AbstractMap.SimpleEntry<>(orders, new HashMap<>());
}
Map<String, Material> materialMap = buildMaterialMap(materials);
Map<String, Order> mergeMap = new HashMap<>();
List<Order> mergedOrders = new ArrayList<>();
List<Order> remaining = new ArrayList<>(orders);
while (!remaining.isEmpty()) {
Order base = remaining.remove(0);
List<Order> mergeGroup = new ArrayList<>();
mergeGroup.add(base);
double groupQty = base.getQuantity();
Material material = materialMap.get(base.getMaterialId());
BigDecimal maxProduction = (material != null && material.getMaxProduction() != null
&& material.getMaxProduction().compareTo(BigDecimal.ZERO) > 0)
? material.getMaxProduction() : null;
Iterator<Order> it = remaining.iterator();
while (it.hasNext()) {
Order candidate = it.next();
if (!canMerge(base, candidate, dueDateRangeDays)) {
continue;
}
double newQty = groupQty + candidate.getQuantity();
if (maxProduction != null && BigDecimal.valueOf(newQty).compareTo(maxProduction) > 0) {
continue;
}
mergeGroup.add(candidate);
groupQty = newQty;
it.remove();
}
if (mergeGroup.size() == 1) {
mergedOrders.add(base);
} else {
Order merged = createMergedOrder(mergeGroup);
mergedOrders.add(merged);
for (Order child : mergeGroup) {
mergeMap.put(child.getOrderId(), merged);
}
}
}
FileHelper.writeLogFile(String.format("订单合并完成:原始%d个 → 合并后%d个,合并组%d组",
orders.size(), mergedOrders.size(),
(int) mergeMap.values().stream().distinct().count()));
return new AbstractMap.SimpleEntry<>(mergedOrders, mergeMap);
}
private static final int DEFAULT_MERGE_DUE_DATE_RANGE_DAYS = 7;
private Map<String, Material> buildMaterialMap(List<Material> materials) {
if (materials == null || materials.isEmpty()) {
return Collections.emptyMap();
}
return materials.stream()
.filter(m -> m.getId() != null)
.collect(Collectors.toMap(Material::getId, m -> m, (existing, replacement) -> existing));
}
private boolean canMerge(Order a, Order b, int dueDateRangeDays) {
if (a == null || b == null) {
return false;
}
if (a.getRoutingId() == null || b.getRoutingId() == null) {
return false;
}
if (!a.getRoutingId().equals(b.getRoutingId())) {
return false;
}
if (a.getMaterialCode() == null || b.getMaterialCode() == null) {
return false;
}
if (!a.getMaterialCode().equals(b.getMaterialCode())) {
return false;
}
if (a.getSerie() == null || b.getSerie() == null) {
return false;
}
if (!a.getSerie().equals(b.getSerie())) {
return false;
}
if (a.isMerged() || b.isMerged()) {
return false;
}
if (a.getStartDate() == null || a.getDueDate() == null
|| b.getStartDate() == null || b.getDueDate() == null) {
return false;
}
boolean overlap = !a.getDueDate().isBefore(b.getStartDate())
&& !b.getDueDate().isBefore(a.getStartDate());
if (!overlap) {
return false;
}
long daysDiff = Math.abs(ChronoUnit.DAYS.between(
a.getDueDate().toLocalDate(), b.getDueDate().toLocalDate()));
return daysDiff <= dueDateRangeDays;
}
private Order createMergedOrder(List<Order> group) {
Order merged = new Order();
Order first = group.get(0);
merged.setOrderId(first.getOrderId());
merged.setOrderCode(first.getOrderCode() + "-MG");
merged.setMaterialCode(first.getMaterialCode());
merged.setMaterialName(first.getMaterialName());
merged.setMaterialId(first.getMaterialId());
merged.setSerie(first.getSerie());
merged.setRoutingId(first.getRoutingId());
merged.setRoutingCode(first.getRoutingCode());
merged.setProductId(first.getProductId());
merged.setMerged(true);
double totalQty = group.stream().mapToDouble(Order::getQuantity).sum();
merged.setQuantity(totalQty);
merged.setSYQuantity(totalQty);
LocalDateTime maxStartDate = group.stream()
.map(Order::getStartDate)
.filter(Objects::nonNull)
.max(LocalDateTime::compareTo)
.orElse(null);
merged.setStartDate(maxStartDate);
LocalDateTime minDueDate = group.stream()
.map(Order::getDueDate)
.filter(Objects::nonNull)
.min(LocalDateTime::compareTo)
.orElse(null);
merged.setDueDate(minDueDate);
int minPriority = group.stream()
.mapToInt(Order::getPriority)
.min()
.orElse(first.getPriority());
merged.setPriority(minPriority);
merged.setActualPriority(minPriority);
List<String> childIds = group.stream()
.map(Order::getOrderId)
.collect(Collectors.toList());
merged.setMergedChildOrderIds(childIds);
return merged;
}
public List<Material> InitMaterial() {
List<Material> materials=new ArrayList<>();
FileHelper.writeLogFile("初始化物料-----------开始-------");
......
......@@ -42,8 +42,8 @@ public class PlanResultServiceTest {
// nsgaiiUtils.Test();
// planResultService.execute2("64E64F6B68094AF38CEDC418630C3CC2");//2000
planResultService.execute2("E1448B3C9C8743DEAB39708F2CFE348A");//2000
// planResultService.execute2("E1448B3C9C8743DEAB39708F2CFE348A");//倒排bomces
planResultService.execute2("29C78D0363244300BBB149E7D7873AF1");
// planResultService.execute2("15210B13B88A453F8B84AAC7F16C7541");//2000
// planResultService.execute2("E29F2B3ADA8149F6B916B5119296A92B");//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