Calculate all possible critical paths in MonteCarlo
Is it possible that there are more than one critical path in a scheduling. MonteCarlo method allows the user to pick up a critical path from a listbox to do the calculation. FEA: ItEr63OTS06MonteCarlo
This commit is contained in:
parent
b45c3e00b0
commit
71460ae53a
5 changed files with 294 additions and 48 deletions
|
|
@ -35,12 +35,14 @@ import org.navalplanner.business.planner.entities.TaskElement;
|
|||
*/
|
||||
public interface IMonteCarloModel {
|
||||
|
||||
List<MonteCarloTask> getCriticalPathTasks();
|
||||
|
||||
Map<LocalDate, BigDecimal> calculateMonteCarlo(int times);
|
||||
Map<LocalDate, BigDecimal> calculateMonteCarlo(List<MonteCarloTask> tasks, int times);
|
||||
|
||||
void setCriticalPath(Order order, List<TaskElement> criticalPath);
|
||||
|
||||
String getOrderName();
|
||||
|
||||
List<String> getCriticalPathNames();
|
||||
|
||||
List<MonteCarloTask> getCriticalPath(String name);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import java.util.Map;
|
|||
import org.joda.time.LocalDate;
|
||||
import org.navalplanner.business.orders.entities.Order;
|
||||
import org.navalplanner.web.common.Util;
|
||||
import org.navalplanner.web.planner.allocation.streches.IStretchesFunctionModel;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
|
@ -48,11 +47,12 @@ import org.zkoss.zul.Decimalbox;
|
|||
import org.zkoss.zul.Grid;
|
||||
import org.zkoss.zul.Intbox;
|
||||
import org.zkoss.zul.Label;
|
||||
import org.zkoss.zul.Listbox;
|
||||
import org.zkoss.zul.Listitem;
|
||||
import org.zkoss.zul.Row;
|
||||
import org.zkoss.zul.RowRenderer;
|
||||
import org.zkoss.zul.Rows;
|
||||
import org.zkoss.zul.SimpleListModel;
|
||||
import org.zkoss.zul.XYModel;
|
||||
import org.zkoss.zul.api.Window;
|
||||
|
||||
/**
|
||||
|
|
@ -81,6 +81,8 @@ public class MonteCarloController extends GenericForwardComposer {
|
|||
|
||||
private Checkbox cbGroupByWeeks;
|
||||
|
||||
private Listbox lbCriticalPaths;
|
||||
|
||||
private Window monteCarloChartWindow;
|
||||
|
||||
public MonteCarloController() {
|
||||
|
|
@ -90,7 +92,17 @@ public class MonteCarloController extends GenericForwardComposer {
|
|||
@Override
|
||||
public void doAfterCompose(org.zkoss.zk.ui.Component comp) throws Exception {
|
||||
super.doAfterCompose(comp);
|
||||
|
||||
ibIterations.setValue(DEFAULT_ITERATIONS);
|
||||
lbCriticalPaths.addEventListener(Events.ON_SELECT, new EventListener() {
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event) throws Exception {
|
||||
reloadGridCritialPathTasks();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
btnRunMonteCarlo.addEventListener(Events.ON_CLICK, new EventListener() {
|
||||
|
||||
@Override
|
||||
|
|
@ -98,7 +110,7 @@ public class MonteCarloController extends GenericForwardComposer {
|
|||
int iterations = getIterations();
|
||||
validateRowsPercentages();
|
||||
Map<LocalDate, BigDecimal> monteCarloData = monteCarloModel
|
||||
.calculateMonteCarlo(iterations);
|
||||
.calculateMonteCarlo(getSelectedCriticalPath(), iterations);
|
||||
showMonteCarloGraph(monteCarloData);
|
||||
}
|
||||
|
||||
|
|
@ -169,35 +181,39 @@ public class MonteCarloController extends GenericForwardComposer {
|
|||
}
|
||||
|
||||
});
|
||||
feedCriticalPathsList();
|
||||
reloadGridCritialPathTasks();
|
||||
}
|
||||
|
||||
private void feedCriticalPathsList() {
|
||||
lbCriticalPaths.setModel(new SimpleListModel(monteCarloModel
|
||||
.getCriticalPathNames()));
|
||||
}
|
||||
|
||||
private void reloadGridCritialPathTasks() {
|
||||
gridCriticalPathTasks.setModel(new SimpleListModel(
|
||||
getCriticalPathTasks()));
|
||||
List<MonteCarloTask> selectedCriticalPath = getSelectedCriticalPath();
|
||||
if (selectedCriticalPath != null) {
|
||||
gridCriticalPathTasks.setModel(new SimpleListModel(
|
||||
selectedCriticalPath));
|
||||
}
|
||||
if (gridCriticalPathTasks.getRowRenderer() == null) {
|
||||
gridCriticalPathTasks.setRowRenderer(gridCriticalPathTasksRender);
|
||||
}
|
||||
}
|
||||
|
||||
public List<MonteCarloTask> getCriticalPathTasks() {
|
||||
return monteCarloModel.getCriticalPathTasks();
|
||||
public List<MonteCarloTask> getSelectedCriticalPath() {
|
||||
Listitem selectedItem = lbCriticalPaths.getSelectedItem();
|
||||
String selectedPath = selectedItem != null ? selectedItem.getLabel()
|
||||
: null;
|
||||
return monteCarloModel.getCriticalPath(selectedPath);
|
||||
}
|
||||
|
||||
public void setCriticalPath(Order order, List criticalPath) {
|
||||
monteCarloModel.setCriticalPath(order, criticalPath);
|
||||
}
|
||||
|
||||
public interface IGraphicGenerator {
|
||||
|
||||
public boolean areChartsEnabled(IStretchesFunctionModel model);
|
||||
|
||||
XYModel getDedicationChart(
|
||||
IStretchesFunctionModel stretchesFunctionModel);
|
||||
|
||||
XYModel getAccumulatedHoursChartData(
|
||||
IStretchesFunctionModel stretchesFunctionModel);
|
||||
|
||||
if (lbCriticalPaths != null) {
|
||||
feedCriticalPathsList();
|
||||
reloadGridCritialPathTasks();
|
||||
}
|
||||
}
|
||||
|
||||
private class CriticalPathTasksRender implements RowRenderer {
|
||||
|
|
|
|||
|
|
@ -20,9 +20,12 @@
|
|||
|
||||
package org.navalplanner.web.montecarlo;
|
||||
|
||||
import static org.navalplanner.web.I18nHelper._;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
|
@ -33,10 +36,16 @@ import java.util.Random;
|
|||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.navalplanner.business.orders.daos.IOrderDAO;
|
||||
import org.navalplanner.business.orders.entities.Order;
|
||||
import org.navalplanner.business.planner.entities.Dependency;
|
||||
import org.navalplanner.business.planner.entities.Task;
|
||||
import org.navalplanner.business.planner.entities.TaskElement;
|
||||
import org.navalplanner.business.planner.entities.TaskGroup;
|
||||
import org.navalplanner.business.scenarios.IScenarioManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
|
@ -51,35 +60,248 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
|
||||
public class MonteCarloModel implements IMonteCarloModel {
|
||||
|
||||
private List<MonteCarloTask> criticalPath = new ArrayList<MonteCarloTask>();
|
||||
@Autowired
|
||||
private IOrderDAO orderDAO;
|
||||
|
||||
@Autowired
|
||||
private IScenarioManager scenarioManager;
|
||||
|
||||
private String CRITICAL_PATH = _("Critical path");
|
||||
|
||||
private String DEFAULT_CRITICAL_PATH = CRITICAL_PATH + " 1";
|
||||
|
||||
private Map<MonteCarloTask, Set<EstimationRange>> estimationRangesForTasks = new HashMap<MonteCarloTask, Set<EstimationRange>>();
|
||||
|
||||
private Map<String, List<MonteCarloTask>> criticalPaths = new HashMap<String, List<MonteCarloTask>>();
|
||||
|
||||
private String orderName = "";
|
||||
|
||||
private List<TaskElement> orderTasks = new ArrayList<TaskElement>();
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public void setCriticalPath(Order order, List<TaskElement> criticalPath) {
|
||||
this.criticalPath.clear();
|
||||
for (TaskElement each : sortByStartDate(criticalPath)) {
|
||||
if (each instanceof Task) {
|
||||
this.criticalPath.add(MonteCarloTask.create((Task) each));
|
||||
}
|
||||
public void setCriticalPath(Order order, List<TaskElement> tasksInCriticalPath) {
|
||||
useSchedulingDataForCurrentScenario(order);
|
||||
orderTasks.addAll(order.getAllChildrenAssociatedTaskElements());
|
||||
initializeTasks(orderTasks);
|
||||
|
||||
initializeOrderName(tasksInCriticalPath);
|
||||
|
||||
int i = 1;
|
||||
criticalPaths.clear();
|
||||
List<List<Task>> allCriticalPaths = buildAllPossibleCriticalPaths(sortByStartDate(onlyTasks((tasksInCriticalPath))));
|
||||
for (List<Task> path : allCriticalPaths) {
|
||||
criticalPaths.put(CRITICAL_PATH + " " + i++, toMonteCarloTaskList(path));
|
||||
}
|
||||
}
|
||||
|
||||
private List<TaskElement> sortByStartDate(List<TaskElement> tasks) {
|
||||
private void useSchedulingDataForCurrentScenario(Order order) {
|
||||
orderDAO.reattach(order);
|
||||
order.useSchedulingDataFor(scenarioManager.getCurrent());
|
||||
}
|
||||
|
||||
private void initializeTasks(List<TaskElement> taskElements) {
|
||||
for (TaskElement each: taskElements) {
|
||||
initializeTaskElement(each);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeTaskElement(TaskElement taskElement) {
|
||||
if (taskElement == null) {
|
||||
return;
|
||||
}
|
||||
Hibernate.initialize(taskElement);
|
||||
initializeTaskElement(taskElement.getParent());
|
||||
initializeDependencies(taskElement);
|
||||
}
|
||||
|
||||
private void initializeDependencies(TaskElement taskElement) {
|
||||
for (Dependency each: taskElement.getDependenciesWithThisDestination()) {
|
||||
Hibernate.initialize(each.getOrigin());
|
||||
Hibernate.initialize(each.getDestination());
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeOrderName(List<TaskElement> tasksInCriticalPath) {
|
||||
orderName = tasksInCriticalPath.isEmpty() ? "" : tasksInCriticalPath
|
||||
.get(0).getOrderElement().getOrder().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getCriticalPathNames() {
|
||||
List<String> result = new ArrayList(criticalPaths.keySet());
|
||||
Collections.sort(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MonteCarloTask> getCriticalPath(String name) {
|
||||
if (name == null || name.isEmpty()) {
|
||||
return criticalPaths.get(DEFAULT_CRITICAL_PATH);
|
||||
}
|
||||
return criticalPaths.get(name);
|
||||
}
|
||||
|
||||
private List<Task> sortByStartDate(List<Task> tasks) {
|
||||
Collections.sort(tasks, Task.getByStartDateComparator());
|
||||
return tasks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MonteCarloTask> getCriticalPathTasks() {
|
||||
return criticalPath;
|
||||
private List<Task> onlyTasks(List<TaskElement> tasks) {
|
||||
List<Task> result = new ArrayList<Task>();
|
||||
for (TaskElement each: tasks) {
|
||||
if (each instanceof Task) {
|
||||
result.add((Task) each);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private List<List<Task>> buildAllPossibleCriticalPaths(
|
||||
List<Task> tasksInCriticalPath) {
|
||||
|
||||
List<List<Task>> result = new ArrayList<List<Task>>();
|
||||
|
||||
List<List<Task>> allPaths = new ArrayList<List<Task>>();
|
||||
for (Task each: tasksWithoutIncomingDependencies(tasksInCriticalPath)) {
|
||||
buildAllPossiblePaths(each, new ArrayList<Task>(), allPaths);
|
||||
}
|
||||
for (List<Task> path: allPaths) {
|
||||
if (isCriticalPath(path, tasksInCriticalPath)) {
|
||||
result.add(path);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Task> getDestinationsFrom(Task _task) {
|
||||
List<Task> result = new ArrayList<Task>(), tasks;
|
||||
|
||||
Task task = retrieveTaskFromModel(_task);
|
||||
tasks = getDestinationsFrom(task.getDependenciesWithThisOrigin());
|
||||
if (!tasks.isEmpty()) {
|
||||
result.addAll(tasks);
|
||||
} else {
|
||||
tasks = getDestinationsFrom(task.getParent().getDependenciesWithThisOrigin());
|
||||
if (!tasks.isEmpty()) {
|
||||
result.addAll(tasks);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Task> getDestinationsFrom(Set<Dependency> dependencies) {
|
||||
List<Task> result = new ArrayList<Task>();
|
||||
for (Dependency each: dependencies) {
|
||||
TaskElement taskElement = each.getDestination();
|
||||
if (taskElement instanceof TaskGroup) {
|
||||
final TaskGroup taskGroup = (TaskGroup) taskElement;
|
||||
result.addAll(toTaskIfNecessary(taskGroup.getChildren()));
|
||||
}
|
||||
if (taskElement instanceof Task) {
|
||||
result.add((Task) taskElement);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Collection<? extends Task> toTaskIfNecessary(
|
||||
List<TaskElement> taskElements) {
|
||||
List<Task> result = new ArrayList<Task>();
|
||||
for (TaskElement each: taskElements) {
|
||||
if (each instanceof TaskGroup) {
|
||||
final TaskGroup taskGroup = (TaskGroup) each;
|
||||
result.addAll(toTaskIfNecessary(taskGroup.getChildren()));
|
||||
}
|
||||
if (each instanceof Task) {
|
||||
result.add((Task) each);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Task> tasksWithoutIncomingDependencies(List<Task> tasks) {
|
||||
List<Task> result = new ArrayList<Task>();
|
||||
for (Task each: tasks) {
|
||||
if (getIncomingDependencies(each).isEmpty()) {
|
||||
result.add(each);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Dependency> getIncomingDependencies(Task _task) {
|
||||
Task task = retrieveTaskFromModel(_task);
|
||||
if (task == null) {
|
||||
throw new RuntimeException(_("Couldn't find %s in model", _task.getName()));
|
||||
}
|
||||
List<Dependency> result = new ArrayList<Dependency>();
|
||||
result.addAll(task.getDependenciesWithThisDestination());
|
||||
result.addAll(task.getParent().getDependenciesWithThisDestination());
|
||||
return result;
|
||||
}
|
||||
|
||||
private Task retrieveTaskFromModel(Task task) {
|
||||
for (TaskElement each: orderTasks) {
|
||||
if (each.getId().equals(task.getId())) {
|
||||
return (Task) each;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isCriticalPath(List<Task> path,
|
||||
List<Task> tasksInCriticalPath) {
|
||||
final List<Long> tasksInCriticalPathIds = getIds(tasksInCriticalPath);
|
||||
for (Task each: path) {
|
||||
if (!tasksInCriticalPathIds.contains(each.getId())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<Long> getIds(List<Task> tasks) {
|
||||
List<Long> result = new ArrayList<Long>();
|
||||
for (Task each: tasks) {
|
||||
result.add(each.getId());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void buildAllPossiblePaths(Task task, List<Task> path, List<List<Task>> allPaths) {
|
||||
List<Task> destinations = getDestinationsFrom(task);
|
||||
if (destinations.size() == 0) {
|
||||
path.add(task);
|
||||
allPaths.add(path);
|
||||
return;
|
||||
}
|
||||
for (Task each: destinations) {
|
||||
List<Task> oldPath = copyPath(path);
|
||||
path.add(task);
|
||||
buildAllPossiblePaths((Task) each, path, allPaths);
|
||||
path = oldPath;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Task> copyPath(List<Task> path) {
|
||||
List<Task> result = new ArrayList<Task>();
|
||||
for (Task each: path) {
|
||||
result.add(each);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<MonteCarloTask> toMonteCarloTaskList(List<Task> path) {
|
||||
List<MonteCarloTask> result = new ArrayList<MonteCarloTask>();
|
||||
for (Task each: path) {
|
||||
result.add(MonteCarloTask.create(each));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<LocalDate, BigDecimal> calculateMonteCarlo(int iterations) {
|
||||
public Map<LocalDate, BigDecimal> calculateMonteCarlo(List<MonteCarloTask> tasks, int iterations) {
|
||||
Map<LocalDate, BigDecimal> monteCarloValues = new HashMap<LocalDate, BigDecimal>();
|
||||
List<MonteCarloTask> tasks = copy(criticalPath);
|
||||
adjustDurationDays(tasks);
|
||||
initializeEstimationRanges(tasks);
|
||||
|
||||
|
|
@ -104,18 +326,7 @@ public class MonteCarloModel implements IMonteCarloModel {
|
|||
}
|
||||
|
||||
public String getOrderName() {
|
||||
if (criticalPath.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return criticalPath.get(0).getOrderName();
|
||||
}
|
||||
|
||||
private List<MonteCarloTask> copy(List<MonteCarloTask> tasks) {
|
||||
List<MonteCarloTask> result = new ArrayList<MonteCarloTask>();
|
||||
for (MonteCarloTask each: tasks) {
|
||||
result.add(MonteCarloTask.copy(each));
|
||||
}
|
||||
return result;
|
||||
return orderName;
|
||||
}
|
||||
|
||||
private void adjustDurationDays(List<MonteCarloTask> tasks) {
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ public class MonteCarloTask {
|
|||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("%s:%f:(%f,%f):(%f,%f):(%f,%f)", task.getName(),
|
||||
return String.format("%s:%f:(%f,%d):(%f,%d):(%f,%d)", task.getName(),
|
||||
duration, pessimisticDuration, pessimisticDurationPercentage,
|
||||
normalDuration, normalDurationPercentage, optimisticDuration,
|
||||
optimisticDurationPercentage);
|
||||
|
|
|
|||
|
|
@ -55,6 +55,23 @@
|
|||
|
||||
<separator bar="false" spacing="100px" orient="vertical"/>
|
||||
|
||||
<hbox>
|
||||
<grid>
|
||||
<columns>
|
||||
<column width="200px" />
|
||||
<column />
|
||||
</columns>
|
||||
<rows>
|
||||
<row>
|
||||
<label value="${i18n:_('Critical path')}" />
|
||||
<listbox id="lbCriticalPaths" mold="select"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</hbox>
|
||||
|
||||
<separator bar="false" spacing="100px" orient="vertical"/>
|
||||
|
||||
<!-- Launch montecarlo -->
|
||||
<hbox>
|
||||
<grid>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue