Change behaviour for appropriative and non-appropriative allocations
Now, when a queue element is allocated at a specific date: * Unschedule all elements in target queue from that date on. If there's already an element at that date, unschedule it too. * Schedule those elements back in the queue in topological order. This guarantees that dependencies are satisfied. As a result, the new element is allocated at specified date and all the elements after that day are shifted in the queue. If there were dependencies between those shifted elements and other elements in the queue, they will be shifted in their own queue too. Take into account scheduling time when moving a task to a specific date. Distinguish between how to generate day assigments between appropriate and non-appropriative allocations. FEA: ItEr66OTS03AlgoritmosLimitantesItEr65OTS05
This commit is contained in:
parent
e3d680e0b1
commit
d7d552c07c
8 changed files with 193 additions and 69 deletions
|
|
@ -51,6 +51,8 @@ public abstract class AllocationSpec {
|
|||
|
||||
private final GapOnQueue originalGap;
|
||||
|
||||
private List<LimitingResourceQueueElement> unscheduled = new ArrayList<LimitingResourceQueueElement>();
|
||||
|
||||
protected AllocationSpec(GapOnQueue originalGap) {
|
||||
Validate.notNull(originalGap);
|
||||
this.originalGap = originalGap;
|
||||
|
|
@ -77,6 +79,20 @@ public abstract class AllocationSpec {
|
|||
public LimitingResourceQueue getQueue() {
|
||||
return originalGap.getOriginQueue();
|
||||
}
|
||||
|
||||
public boolean isAppropriative() {
|
||||
return !unscheduled.isEmpty();
|
||||
}
|
||||
|
||||
public void setUnscheduledElements(
|
||||
List<LimitingResourceQueueElement> queueElements) {
|
||||
unscheduled.addAll(queueElements);
|
||||
}
|
||||
|
||||
public List<LimitingResourceQueueElement> getUnscheduledElements() {
|
||||
return unscheduled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class InvalidAllocationAttempt extends AllocationSpec {
|
||||
|
|
@ -206,4 +222,4 @@ class ValidAllocationAttempt extends AllocationSpec {
|
|||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,11 +51,13 @@ public class DateAndHour implements Comparable<DateAndHour> {
|
|||
private Integer hour;
|
||||
|
||||
public DateAndHour(LocalDate date, Integer hour) {
|
||||
Validate.notNull(date);
|
||||
this.date = date;
|
||||
this.hour = hour;
|
||||
}
|
||||
|
||||
public DateAndHour(DateAndHour dateAndHour) {
|
||||
Validate.notNull(date);
|
||||
this.date = dateAndHour.getDate();
|
||||
this.hour = dateAndHour.getHour();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,7 +242,11 @@ public class Gap implements Comparable<Gap> {
|
|||
}
|
||||
|
||||
public String toString() {
|
||||
String result = startTime.getDate() + " - " + startTime.getHour();
|
||||
String result = "";
|
||||
|
||||
if (startTime != null) {
|
||||
result = startTime.getDate() + " - " + startTime.getHour();
|
||||
}
|
||||
if (endTime != null) {
|
||||
result += "; " + endTime.getDate() + " - " + endTime.getHour();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,28 @@ public class InsertionRequirements {
|
|||
calculateEarliestPossibleEnd(element, dependenciesAffectingEnd));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a minimum startTime, earliestStart should be lower than this value
|
||||
*
|
||||
* @param element
|
||||
* @param dependenciesAffectingStart
|
||||
* @param dependenciesAffectingEnd
|
||||
* @param startAt
|
||||
* @return
|
||||
*/
|
||||
public static InsertionRequirements forElement(
|
||||
LimitingResourceQueueElement element,
|
||||
List<LimitingResourceQueueDependency> dependenciesAffectingStart,
|
||||
List<LimitingResourceQueueDependency> dependenciesAffectingEnd,
|
||||
DateAndHour startAt) {
|
||||
|
||||
DateAndHour earliesPossibleStart = calculateEarliestPossibleStart(
|
||||
element, dependenciesAffectingStart);
|
||||
return new InsertionRequirements(element, DateAndHour.max(
|
||||
earliesPossibleStart, startAt), calculateEarliestPossibleEnd(
|
||||
element, dependenciesAffectingEnd));
|
||||
}
|
||||
|
||||
private static DateAndHour calculateEarliestPossibleEnd(
|
||||
LimitingResourceQueueElement element,
|
||||
List<LimitingResourceQueueDependency> dependenciesAffectingEnd) {
|
||||
|
|
|
|||
|
|
@ -141,6 +141,19 @@ public class LimitingResourceQueue extends BaseEntity {
|
|||
return queueElements.subList(position + 1, queueElements.size());
|
||||
}
|
||||
|
||||
public List<LimitingResourceQueueElement> getElementsSince(DateAndHour time) {
|
||||
List<LimitingResourceQueueElement> result = new ArrayList<LimitingResourceQueueElement>();
|
||||
|
||||
for (LimitingResourceQueueElement each: getLimitingResourceQueueElements()) {
|
||||
if (each.getStartTime().isEquals(time)
|
||||
|| each.getStartTime().isAfter(time)) {
|
||||
result.add(each);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public void queueElementMoved(
|
||||
LimitingResourceQueueElement limitingResourceQueueElement) {
|
||||
invalidCachedGaps();
|
||||
|
|
|
|||
|
|
@ -133,8 +133,7 @@ public interface ILimitingResourceQueueModel {
|
|||
LimitingResourceQueueElement element, LimitingResourceQueue queue,
|
||||
DateAndHour allocationTime);
|
||||
|
||||
void unschedule(LimitingResourceQueueElement element);
|
||||
|
||||
LimitingResourceQueueElement unschedule(LimitingResourceQueueElement element);
|
||||
|
||||
void removeUnassignedLimitingResourceQueueElement(
|
||||
LimitingResourceQueueElement element);
|
||||
|
|
|
|||
|
|
@ -418,22 +418,26 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
|
||||
InsertionRequirements requirements = queuesState
|
||||
.getRequirementsFor(externalQueueElement);
|
||||
AllocationSpec allocationDone = insertAtGap(requirements);
|
||||
if (allocationDone == null) {
|
||||
AllocationSpec allocation = insertAtGap(requirements);
|
||||
if (allocation == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
applyAllocation(allocation);
|
||||
|
||||
assert allocationDone.isValid();
|
||||
assert allocation.isValid();
|
||||
List<LimitingResourceQueueElement> result = new ArrayList<LimitingResourceQueueElement>();
|
||||
result.add(requirements.getElement());
|
||||
|
||||
List<LimitingResourceQueueElement> moved = shift(
|
||||
queuesState
|
||||
.getPotentiallyAffectedByInsertion(externalQueueElement),
|
||||
requirements.getElement(),
|
||||
allocationDone);
|
||||
|
||||
.getPotentiallyAffectedByInsertion(externalQueueElement),
|
||||
requirements.getElement(), allocation);
|
||||
result.addAll(moved);
|
||||
|
||||
// Return all moved tasks (including the allocated one)
|
||||
if (allocation.isAppropriative()) {
|
||||
result.addAll(scheduleElements(allocation.getUnscheduledElements()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -589,22 +593,30 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
* otherwise
|
||||
*/
|
||||
private AllocationSpec insertAtGap(InsertionRequirements requirements) {
|
||||
AllocationSpec allocationStillNotDone = findAllocationSpecFor(requirements);
|
||||
return doAppropriativeIfNecessary(allocationStillNotDone, requirements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find valid {@link AllocationSpec} taking into account requirements
|
||||
*
|
||||
* @param requirements
|
||||
* @return
|
||||
*/
|
||||
private AllocationSpec findAllocationSpecFor(InsertionRequirements requirements) {
|
||||
List<GapOnQueue> potentiallyValidGapsFor = queuesState
|
||||
.getPotentiallyValidGapsFor(requirements);
|
||||
return findAllocationSpecFor(potentiallyValidGapsFor, requirements);
|
||||
}
|
||||
|
||||
private AllocationSpec findAllocationSpecFor(List<GapOnQueue> gapsOnQueue, InsertionRequirements requirements) {
|
||||
boolean generic = requirements.getElement().isGeneric();
|
||||
for (GapOnQueue each : potentiallyValidGapsFor) {
|
||||
for (GapOnQueue eachSubGap : getSubGaps(each, requirements
|
||||
.getElement(), generic)) {
|
||||
for (GapOnQueue each : gapsOnQueue) {
|
||||
for (GapOnQueue eachSubGap : getSubGaps(each,
|
||||
requirements.getElement(), generic)) {
|
||||
AllocationSpec allocation = requirements
|
||||
.guessValidity(eachSubGap);
|
||||
if (allocation.isValid()) {
|
||||
if (checkAllocationIsAppropriative()
|
||||
&& requirements
|
||||
.isAppropiativeAllocation(allocation)) {
|
||||
doAppropriativeAllocation(requirements, allocation);
|
||||
} else {
|
||||
applyAllocation(allocation);
|
||||
}
|
||||
return allocation;
|
||||
}
|
||||
}
|
||||
|
|
@ -612,17 +624,53 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
return null;
|
||||
}
|
||||
|
||||
private AllocationSpec doAppropriativeIfNecessary(AllocationSpec allocation,
|
||||
InsertionRequirements requirements) {
|
||||
if (allocation != null) {
|
||||
if (checkAllocationIsAppropriative()
|
||||
&& requirements.isAppropiativeAllocation(allocation)) {
|
||||
doAppropriativeAllocation(requirements, allocation);
|
||||
}
|
||||
return allocation;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private AllocationSpec insertAtGap(InsertionRequirements requirements, LimitingResourceQueue queue) {
|
||||
AllocationSpec allocationStillNotDone = findAllocationSpecForInQueue(requirements, queue);
|
||||
return doAppropriativeIfNecessary(allocationStillNotDone, requirements);
|
||||
}
|
||||
|
||||
private AllocationSpec findAllocationSpecForInQueue(
|
||||
InsertionRequirements requirements, LimitingResourceQueue queue) {
|
||||
|
||||
List<GapOnQueue> potentiallyValidGapsFor = new ArrayList<GapOnQueue>();
|
||||
|
||||
for (GapOnQueue each : queuesState
|
||||
.getPotentiallyValidGapsFor(requirements)) {
|
||||
if (each.getOriginQueue().equals(queue)) {
|
||||
potentiallyValidGapsFor.add(each);
|
||||
}
|
||||
}
|
||||
return findAllocationSpecFor(potentiallyValidGapsFor, requirements);
|
||||
}
|
||||
|
||||
private boolean checkAllocationIsAppropriative = true;
|
||||
|
||||
private void doAppropriativeAllocation(InsertionRequirements requirements,
|
||||
AllocationSpec allocation) {
|
||||
LimitingResourceQueueElement element = requirements.getElement();
|
||||
LimitingResourceQueue queue = queuesState.getQueueFor(element.getResource());
|
||||
DateAndHour allocationTime = requirements.getEarliestPossibleStart(allocation);
|
||||
private AllocationSpec doAppropriativeAllocation(
|
||||
InsertionRequirements requirements, AllocationSpec allocation) {
|
||||
|
||||
checkAllocationIsAppropriative(false);
|
||||
appropriativeAllocation(element, queue, allocationTime);
|
||||
checkAllocationIsAppropriative(true);
|
||||
LimitingResourceQueueElement element = requirements.getElement();
|
||||
LimitingResourceQueue queue = queuesState.getQueueFor(element
|
||||
.getResource());
|
||||
DateAndHour earliestPossibleStart = requirements
|
||||
.getEarliestPossibleStart(allocation);
|
||||
|
||||
List<LimitingResourceQueueElement> unscheduled = unscheduleElementsSince(
|
||||
queue, earliestPossibleStart);
|
||||
allocation.setUnscheduledElements(queuesState.inTopologicalOrder(unscheduled));
|
||||
|
||||
return allocation;
|
||||
}
|
||||
|
||||
private void checkAllocationIsAppropriative(boolean value) {
|
||||
|
|
@ -641,7 +689,7 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
return Collections.singletonList(each);
|
||||
}
|
||||
|
||||
private void applyAllocation(final AllocationSpec allocationStillNotDone) {
|
||||
private AllocationSpec applyAllocation(final AllocationSpec allocationStillNotDone) {
|
||||
applyAllocation(allocationStillNotDone, new IDayAssignmentBehaviour() {
|
||||
|
||||
@Override
|
||||
|
|
@ -663,6 +711,7 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
}
|
||||
|
||||
});
|
||||
return allocationStillNotDone;
|
||||
}
|
||||
|
||||
private void applyAllocation(AllocationSpec allocationStillNotDone,
|
||||
|
|
@ -693,16 +742,14 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
|
||||
private List<LimitingResourceQueueElement> assignLimitingResourceQueueElementToQueueAt(
|
||||
final LimitingResourceQueueElement element,
|
||||
final LimitingResourceQueue queue, final DateAndHour startTime,
|
||||
final LimitingResourceQueue queue,
|
||||
final DateAndHour startAt,
|
||||
final DateAndHour endsAfter) {
|
||||
|
||||
// Check if allocation is possible
|
||||
InsertionRequirements requirements = queuesState
|
||||
.getRequirementsFor(element);
|
||||
List<GapOnQueue> gapOnQueue = GapOnQueue.onQueue(queue, startTime,
|
||||
endsAfter);
|
||||
AllocationSpec allocation = requirements.guessValidity(gapOnQueue
|
||||
.iterator().next());
|
||||
InsertionRequirements requirements = queuesState.getRequirementsFor(
|
||||
element, startAt);
|
||||
AllocationSpec allocation = insertAtGap(requirements, queue);
|
||||
if (!allocation.isValid()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
|
@ -716,7 +763,7 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
List<DayAssignment> assignments = LimitingResourceAllocator
|
||||
.generateDayAssignments(
|
||||
element.getResourceAllocation(),
|
||||
queue.getResource(), startTime, endsAfter);
|
||||
queue.getResource(), startAt, endsAfter);
|
||||
element.getResourceAllocation().allocateLimitingDayAssignments(
|
||||
assignments);
|
||||
}
|
||||
|
|
@ -732,9 +779,25 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
List<LimitingResourceQueueElement> moved = shift(
|
||||
queuesState.getPotentiallyAffectedByInsertion(element),
|
||||
requirements.getElement(), allocation);
|
||||
result.addAll(moved);
|
||||
|
||||
// Return all moved tasks (including the allocated one)
|
||||
result.addAll(moved);
|
||||
if (allocation.isAppropriative()) {
|
||||
result.addAll(scheduleElements(allocation.getUnscheduledElements()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<LimitingResourceQueueElement> scheduleElements(
|
||||
List<LimitingResourceQueueElement> unscheduled) {
|
||||
|
||||
List<LimitingResourceQueueElement> result = new ArrayList<LimitingResourceQueueElement>();
|
||||
|
||||
checkAllocationIsAppropriative(false);
|
||||
for (LimitingResourceQueueElement each: unscheduled) {
|
||||
result.addAll(assignLimitingResourceQueueElement(each));
|
||||
}
|
||||
checkAllocationIsAppropriative(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -929,9 +992,10 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
* later added to the list of unassigned elements
|
||||
*/
|
||||
@Override
|
||||
public void unschedule(LimitingResourceQueueElement queueElement) {
|
||||
public LimitingResourceQueueElement unschedule(LimitingResourceQueueElement queueElement) {
|
||||
queuesState.unassingFromQueue(queueElement);
|
||||
markAsModified(queueElement);
|
||||
return queueElement;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -993,10 +1057,10 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
return beingEdited;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<LimitingResourceQueueElement> appropriativeAllocation(LimitingResourceQueueElement _element, LimitingResourceQueue _queue,
|
||||
DateAndHour allocationTime) {
|
||||
public Set<LimitingResourceQueueElement> appropriativeAllocation(
|
||||
LimitingResourceQueueElement _element,
|
||||
LimitingResourceQueue _queue, DateAndHour allocationTime) {
|
||||
|
||||
Set<LimitingResourceQueueElement> result = new HashSet<LimitingResourceQueueElement>();
|
||||
|
||||
|
|
@ -1007,38 +1071,32 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
unschedule(element);
|
||||
}
|
||||
|
||||
List<LimitingResourceQueueElement> unscheduledElements = new ArrayList<LimitingResourceQueueElement>();
|
||||
// Unschedule elements in queue since allocationTime
|
||||
List<LimitingResourceQueueElement> toSchedule = new ArrayList<LimitingResourceQueueElement>(
|
||||
unscheduleElementsSince(queue, allocationTime));
|
||||
|
||||
Gap gap;
|
||||
do {
|
||||
gap = LimitingResourceAllocator.getFirstValidGapSince(element, queue, allocationTime);
|
||||
result.addAll(assignLimitingResourceQueueElementToQueueAt(element,
|
||||
queue, allocationTime, getEndsAfterBecauseOfGantt(element)));
|
||||
|
||||
if (gap != null) {
|
||||
final LocalDate startDate = gap.getStartTime().getDate();
|
||||
|
||||
if (startDate.equals(allocationTime.getDate())) {
|
||||
result.addAll(assignLimitingResourceQueueElementToQueueAt(
|
||||
element, queue, allocationTime,
|
||||
getEndsAfterBecauseOfGantt(element)));
|
||||
break;
|
||||
} else {
|
||||
LimitingResourceQueueElement elementAtTime = getFirstElementFrom(
|
||||
queue, allocationTime);
|
||||
if (elementAtTime != null) {
|
||||
unschedule(elementAtTime);
|
||||
unscheduledElements.add(elementAtTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (gap != null);
|
||||
|
||||
for (LimitingResourceQueueElement each: unscheduledElements) {
|
||||
assignLimitingResourceQueueElement(each);
|
||||
for (LimitingResourceQueueElement each: queuesState
|
||||
.inTopologicalOrder(toSchedule)) {
|
||||
result.addAll(assignLimitingResourceQueueElement(each));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<LimitingResourceQueueElement> unscheduleElementsSince(
|
||||
LimitingResourceQueue queue, DateAndHour allocationTime) {
|
||||
|
||||
List<LimitingResourceQueueElement> result = new ArrayList<LimitingResourceQueueElement>();
|
||||
|
||||
for (LimitingResourceQueueElement each: queue.getElementsSince(allocationTime)) {
|
||||
result.add(unschedule(each));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public LimitingResourceQueueElement getFirstElementFrom(LimitingResourceQueue queue, DateAndHour allocationTime) {
|
||||
final List<LimitingResourceQueueElement> elements = new ArrayList(queue.getLimitingResourceQueueElements());
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import org.navalplanner.business.common.BaseEntity;
|
|||
import org.navalplanner.business.planner.entities.GenericResourceAllocation;
|
||||
import org.navalplanner.business.planner.entities.ResourceAllocation;
|
||||
import org.navalplanner.business.planner.entities.SpecificResourceAllocation;
|
||||
import org.navalplanner.business.planner.limiting.entities.DateAndHour;
|
||||
import org.navalplanner.business.planner.limiting.entities.Gap.GapOnQueue;
|
||||
import org.navalplanner.business.planner.limiting.entities.InsertionRequirements;
|
||||
import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueDependency;
|
||||
|
|
@ -221,6 +222,15 @@ public class QueuesState {
|
|||
dependenciesStart, dependenciesEnd);
|
||||
}
|
||||
|
||||
public InsertionRequirements getRequirementsFor(
|
||||
LimitingResourceQueueElement element, DateAndHour startAt) {
|
||||
List<LimitingResourceQueueDependency> dependenciesStart = new ArrayList<LimitingResourceQueueDependency>();
|
||||
List<LimitingResourceQueueDependency> dependenciesEnd = new ArrayList<LimitingResourceQueueDependency>();
|
||||
fillIncoming(element, dependenciesStart, dependenciesEnd);
|
||||
return InsertionRequirements.forElement(getEquivalent(element),
|
||||
dependenciesStart, dependenciesEnd, startAt);
|
||||
}
|
||||
|
||||
private void fillIncoming(LimitingResourceQueueElement element,
|
||||
List<LimitingResourceQueueDependency> dependenciesStart,
|
||||
List<LimitingResourceQueueDependency> dependenciesEnd) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue