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:
Diego Pino Garcia 2010-12-19 22:09:54 +01:00
parent e3d680e0b1
commit d7d552c07c
8 changed files with 193 additions and 69 deletions

View file

@ -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;
}
}
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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) {

View file

@ -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();

View file

@ -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);

View file

@ -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());

View file

@ -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) {