倒排冲突

parent 2f7a699b
...@@ -1612,6 +1612,28 @@ if(groupId==7) ...@@ -1612,6 +1612,28 @@ if(groupId==7)
.mapToInt(ScheduleResultDetail::getEndTime) .mapToInt(ScheduleResultDetail::getEndTime)
.max() .max()
.orElse(0); .orElse(0);
// 不可中断工序不能由多段空档拼接;如果试排结果断开,先归还占用,再倒排寻找最近连续窗口。
if (operation.getIsInterrupt() != 1 && hasDiscontinuousDetails(geneDetails)) {
AddMachineAvailable(machine, geneDetails);
if (isJit && islockMachineTime) {
CopyOnWriteArrayList<ScheduleResultDetail> continuousDetails =
scheduleLatestContinuousJit(machine, operation, earliestStartTime, processingTimeTotal);
if (continuousDetails == null || continuousDetails.isEmpty()) {
return OperationScheduleResult.success(null);
}
geneDetails = continuousDetails;
startTime = geneDetails.stream()
.mapToInt(ScheduleResultDetail::getStartTime)
.min()
.orElse(0);
endTime = geneDetails.stream()
.mapToInt(ScheduleResultDetail::getEndTime)
.max()
.orElse(0);
} else {
return OperationScheduleResult.success(null);
}
}
// if (isJit) { // if (isJit) {
// FileHelper.writeLogFile(" 半成品 " + operation.getGroupId() + " - " + operation.getSequence() + ",开始时间: " + startTime + ",结束时间: " + endTime + ",处理时间: " + processingTime + ", 后处理: " + teardownTime + // FileHelper.writeLogFile(" 半成品 " + operation.getGroupId() + " - " + operation.getSequence() + ",开始时间: " + startTime + ",结束时间: " + endTime + ",处理时间: " + processingTime + ", 后处理: " + teardownTime +
// ", 前处理: " + preTime + ", 换型: " + setupTime + ", 数量: " + operation.getQuantity() + ", 设备: " + machine.getId() + ", 是否可中断: " + operation.getIsInterrupt()); // ", 前处理: " + preTime + ", 换型: " + setupTime + ", 数量: " + operation.getQuantity() + ", 设备: " + machine.getId() + ", 是否可中断: " + operation.getIsInterrupt());
...@@ -1667,6 +1689,202 @@ if(groupId==7) ...@@ -1667,6 +1689,202 @@ if(groupId==7)
return OperationScheduleResult.success(result); return OperationScheduleResult.success(result);
} }
// 判断排程明细是否存在断档。不可中断工序如果出现断档,页面外层时间会跨住中间任务。
private boolean hasDiscontinuousDetails(CopyOnWriteArrayList<ScheduleResultDetail> geneDetails) {
if (geneDetails == null || geneDetails.size() <= 1) {
return false;
}
List<int[]> ranges = new ArrayList<>();
for (ScheduleResultDetail detail : geneDetails) {
if (detail == null) {
continue;
}
if (detail.getUsedSegment() != null && !detail.getUsedSegment().isEmpty()) {
for (TimeSegment segment : detail.getUsedSegment()) {
if (segment == null || segment.getStart() == null || segment.getEnd() == null) {
continue;
}
int segmentStart = (int) ChronoUnit.SECONDS.between(baseTime, segment.getStart());
int segmentEnd = (int) ChronoUnit.SECONDS.between(baseTime, segment.getEnd());
if (segmentEnd > segmentStart) {
ranges.add(new int[]{segmentStart, segmentEnd});
}
}
} else if (detail.getEndTime() > detail.getStartTime()) {
ranges.add(new int[]{detail.getStartTime(), detail.getEndTime()});
}
}
if (ranges.size() <= 1) {
return false;
}
ranges.sort(Comparator.comparingInt(range -> range[0]));
int lastEnd = ranges.get(0)[1];
for (int i = 1; i < ranges.size(); i++) {
int[] current = ranges.get(i);
if (current[0] > lastEnd + 1) {
return true;
}
lastEnd = Math.max(lastEnd, current[1]);
}
return false;
}
// 倒排不可中断兜底:从锚点往前找最近的一整段连续空闲窗口,避免失败后掉到基准日期正排。
private CopyOnWriteArrayList<ScheduleResultDetail> scheduleLatestContinuousJit(
Machine machine, Entry operation, int latestEndTime, int processingTimeTotal) {
if (machine == null || machine.getAvailability() == null || processingTimeTotal <= 0) {
return null;
}
LocalDateTime latestEnd = baseTime.plusSeconds(latestEndTime);
List<TimeSegment> availableSegments = machine.getAvailability().stream()
.filter(t -> t != null
&& !t.isUsed()
&& t.getStart() != null
&& t.getEnd() != null
&& t.getType() != SegmentType.MAINTENANCE
&& t.getStart().isBefore(latestEnd)
&& t.getEnd().isAfter(baseTime))
.sorted(Comparator.comparing(TimeSegment::getStart))
.collect(Collectors.toList());
List<TimeSegment> group = new ArrayList<>();
LocalDateTime groupStart = null;
LocalDateTime groupEnd = null;
long groupAvailableSeconds = 0L;
for (int i = availableSegments.size() - 1; i >= 0; i--) {
TimeSegment segment = availableSegments.get(i);
LocalDateTime segmentStart = segment.getStart().isBefore(baseTime) ? baseTime : segment.getStart();
LocalDateTime segmentEnd = segment.getEnd().isAfter(latestEnd) ? latestEnd : segment.getEnd();
if (!segmentEnd.isAfter(segmentStart)) {
continue;
}
boolean continuous = group.isEmpty() || !segmentEnd.plusSeconds(1).isBefore(groupStart);
if (!continuous) {
group.clear();
groupAvailableSeconds = 0L;
groupEnd = null;
}
group.add(0, segment);
groupStart = segmentStart;
if (groupEnd == null) {
groupEnd = segmentEnd;
}
groupAvailableSeconds += ChronoUnit.SECONDS.between(segmentStart, segmentEnd);
if (groupAvailableSeconds >= processingTimeTotal) {
return buildContinuousJitDetails(machine, operation, group, groupEnd, processingTimeTotal);
}
}
return null;
}
// 将选中的连续窗口转成明细,并同步占用设备日历。
private CopyOnWriteArrayList<ScheduleResultDetail> buildContinuousJitDetails(
Machine machine, Entry operation, List<TimeSegment> group, LocalDateTime latestEnd, int processingTimeTotal) {
CopyOnWriteArrayList<ScheduleResultDetail> details = new CopyOnWriteArrayList<>();
long remaining = processingTimeTotal;
LocalDateTime cursorEnd = latestEnd;
for (int i = group.size() - 1; i >= 0 && remaining > 0; i--) {
TimeSegment segment = group.get(i);
LocalDateTime useEnd = segment.getEnd().isAfter(cursorEnd) ? cursorEnd : segment.getEnd();
LocalDateTime useStart = segment.getStart();
if (!useEnd.isAfter(useStart)) {
continue;
}
long availableSeconds = ChronoUnit.SECONDS.between(useStart, useEnd);
if (availableSeconds > remaining) {
useStart = useEnd.minusSeconds(remaining);
}
TimeSegment occupied = occupyAvailabilityRange(machine, segment, useStart, useEnd);
if (occupied == null) {
return null;
}
ScheduleResultDetail detail = new ScheduleResultDetail();
detail.setKey(occupied.getKey());
detail.setStartTime((int) ChronoUnit.SECONDS.between(baseTime, useStart));
detail.setEndTime((int) ChronoUnit.SECONDS.between(baseTime, useEnd));
detail.setOneTime(1);
detail.setQuantity(operation.getQuantity());
detail.setEfficiency(occupied.getEfficiency());
detail.setUsedSegment(Collections.singletonList(occupied));
details.add(0, detail);
remaining -= ChronoUnit.SECONDS.between(useStart, useEnd);
cursorEnd = useStart.minusSeconds(1);
}
return remaining <= 0 ? details : null;
}
// 从设备可用片段里切出本次占用范围,保留前后未占用部分。
private TimeSegment occupyAvailabilityRange(
Machine machine, TimeSegment source, LocalDateTime useStart, LocalDateTime useEnd) {
if (machine == null || machine.getAvailability() == null || source == null
|| useStart == null || useEnd == null || !useEnd.isAfter(useStart)) {
return null;
}
int index = machine.getAvailability().stream()
.filter(t -> source.getKey().equals(t.getKey()))
.findFirst()
.map(machine.getAvailability()::indexOf)
.orElse(-1);
if (index < 0) {
return null;
}
TimeSegment current = machine.getAvailability().remove(index);
List<TimeSegment> replacement = new ArrayList<>();
if (current.getStart().isBefore(useStart)) {
TimeSegment before = copyAvailabilitySegment(current, current.getStart(), useStart.minusSeconds(1), false);
if (before.getEnd().isAfter(before.getStart())) {
replacement.add(before);
}
}
TimeSegment occupied = copyAvailabilitySegment(current, useStart, useEnd, true);
replacement.add(occupied);
if (current.getEnd().isAfter(useEnd)) {
TimeSegment after = copyAvailabilitySegment(current, useEnd.plusSeconds(1), current.getEnd(), false);
if (after.getEnd().isAfter(after.getStart())) {
replacement.add(after);
}
}
machine.getAvailability().addAll(replacement);
machine.getAvailability().sort(Comparator.comparing(TimeSegment::getStart));
return occupied;
}
private TimeSegment copyAvailabilitySegment(
TimeSegment source, LocalDateTime start, LocalDateTime end, boolean used) {
TimeSegment copy = new TimeSegment();
copy.setKey(UUID.randomUUID().toString());
copy.setStart(start);
copy.setEnd(end);
copy.setEarliestTime(source.getEarliestTime());
copy.setTotalTaskTime(source.getTotalTaskTime());
copy.setType(source.getType());
copy.setHoliday(source.isHoliday());
copy.setUsed(used);
copy.setEfficiency(source.getEfficiency());
copy.setProcessingTime(source.getProcessingTime());
return copy;
}
private GAScheduleResult CreateResult(Entry operation,long machineId private GAScheduleResult CreateResult(Entry operation,long machineId
,int startTime,int endTime,double processingTime ,int startTime,int endTime,double processingTime
,int setupTime,int preTime,int teardownTime,int bomtime ,int setupTime,int preTime,int teardownTime,int bomtime
......
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