/**
* Copyright (C) 2010 BonitaSoft S.A.
* BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.simulation.engine;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.bonitasoft.simulation.model.Period;
import org.bonitasoft.simulation.model.calendar.SimCalendarInstance;
import org.bonitasoft.simulation.model.instance.ResourceInstance;
import org.bonitasoft.simulation.model.instance.SimActivityInstance;
import org.bonitasoft.simulation.model.process.ResourceAssignement;
import org.bonitasoft.simulation.model.process.SimProcess;
import org.bonitasoft.simulation.model.resource.Resource;
/**
* @author Romain Bioteau
*
*/
public class ResourcePool {
private static ResourcePool INSTANCE;
private Map<String, RuntimeResource> resourceInstances ;
private ResourcePool(){
resourceInstances = new HashMap<String, RuntimeResource>();
}
public static ResourcePool getInstance(){
if(INSTANCE == null){
INSTANCE = new ResourcePool();
}
return INSTANCE ;
}
public void addResource(final SimProcess process, final Resource resource, final long startTime, long timespan){
final List<ResourceInstance> instances = new ArrayList<ResourceInstance>() ;
if(resource.getMaximumQuantity() > 0) {//DEFINED RESOURCE NUMBER MODE
for(int i = 0 ; i< resource.getMaximumQuantity() ; i++){
final SimCalendarInstance planning = new SimCalendarInstance(startTime,process,resource.getName(),resource.getPlanning().getDaysOfWeek()) ;
instances.add(new ResourceInstance(UUID.randomUUID().toString(),resource,planning));
}
resourceInstances.put(resource.getName(), new RuntimeResource(resource,instances,startTime,timespan));
}else{//UNLIMITED RESOURCE MODE
resourceInstances.put(resource.getName(), new DynamicRuntimeResource(resource,process,resource.getPlanning().getDaysOfWeek(),startTime,timespan));
}
}
public void createResources(final SimProcess process , final List<Resource> inputResources, final long startTime,final long timespan) {
for(Resource r : inputResources){
addResource(process ,r, startTime,timespan);
DefinitionPool.getInstance().addResourceDefinition(r);
}
}
public List<ResourceInstanceAvailability> findAvailableResource(int quantity, Resource r, long startDate, long executionTime, boolean contigous) throws Exception {
List<ResourceInstance> riList = getResourceInstances(r) ;
if(riList.size() < quantity){
if(r.getMaximumQuantity() >= 0){
throw new Exception("Too many resources need regarding the resource pool");
}else{//Dynamic Pool of Resource
int needed = quantity - riList.size() ;
DynamicRuntimeResource dr = (DynamicRuntimeResource) getResourceInstances().get(r.getName()) ;
dr.createInstances(needed) ;
riList = getResourceInstances(r) ;
}
}
final List<ResourceInstanceAvailability> instances = new ArrayList<ResourceInstanceAvailability>();
boolean allInstancesAvailable = false ;
while(!SimulationEngine.isStopped && !allInstancesAvailable){
instances.clear() ;
while(!SimulationEngine.isStopped && instances.size() != quantity){
long lastAvailableStart = Long.MAX_VALUE;
ResourceInstance lastResource = null;
List<ResourceInstance> searchList = new ArrayList<ResourceInstance>(riList) ;
for(ResourceInstanceAvailability ria : instances){
searchList.remove(ria.getResource()) ;
}
for(ResourceInstance instance : searchList){
long availableStart = startDate ;
availableStart = instance.getFirstAvailableDate(availableStart,executionTime,contigous);
if (lastResource == null || lastAvailableStart > availableStart) {
lastResource = instance;
lastAvailableStart = availableStart;
}
if (availableStart == startDate) {//SHORTEN THE LOOP, NO NEED TO GET ALL AVAILABLE RESOURCE INSTANCE
break ;
}
}
if(lastAvailableStart != startDate && r.getMaximumQuantity() < 0){//DYNAMIC RESOURCE
DynamicRuntimeResource dr = (DynamicRuntimeResource) getResourceInstances().get(r.getName()) ;
if(dr.isResourceAvailable(startDate, executionTime, contigous)){//THE RESOURCE IS AT WORK
List<ResourceInstance> createdInstances = dr.createInstances(1) ;
lastResource = createdInstances.get(0) ;
lastAvailableStart = startDate ;
}
}
if(lastResource == null){
throw new Exception("Impossible to find an available Date for "+r.getName()) ; //$NON-NLS-1$
}
instances.add(new ResourceInstanceAvailability(lastAvailableStart, lastResource,executionTime));
}
long lastStart = instances.get(0).getTime() ;
allInstancesAvailable = true ;
long newStartDate = 0 ;
for(ResourceInstanceAvailability ria : instances){
if( lastStart != ria.getTime()){
allInstancesAvailable = false ;
if(newStartDate < ria.getTime()){
newStartDate = ria.getTime() ;
}
}
}
if(!allInstancesAvailable){
startDate = newStartDate ;
}
}
return instances ;
}
public List<ResourceInstance> getResourceInstances(Resource r) {
if (resourceInstances.get(r.getName()) == null){
return new ArrayList<ResourceInstance>();
}
return resourceInstances.get(r.getName()).getInstances();
}
public void lockAvailableResourceInstances(final SimActivityInstance activity, final List<ResourceAssignement> resources, final boolean isContigous, final Set<ResourceInstanceAvailability> instances) throws Exception {
if (resources == null || resources.isEmpty()) {
return;
}
for (ResourceInstanceAvailability resourceInstanceAvailability : instances){
final ResourceInstance resourceInstance = resourceInstanceAvailability.getResource();
final String resourceName = resourceInstance.getDefinition().getName();
long executionTime = 0;
for (ResourceAssignement resourceAssignment : resources){
if (resourceAssignment.getResource().getName().equals(resourceName)) {
executionTime = resourceAssignment.getDuration();
break;
}
}
if(executionTime > 0){
lockResourceIntance(resourceInstance, resourceInstanceAvailability.getTime(), executionTime, activity);
}
}
}
private void lockResourceIntance(final ResourceInstance resourceInstance, final long startDate, final long executionTime, final SimActivityInstance activity) throws Exception {
final Period period = new Period(startDate, startDate + executionTime);
final List<Period> periods = resourceInstance.getPlanning().split(period);
if(periods.isEmpty()){
throw new Exception("period can't be empty"); //$NON-NLS-1$
}
RuntimeResource runtimeResource = getResourceInstances().get(resourceInstance.getDefinition().getName()) ;
for (Period p : periods){
resourceInstance.getPlanning().addBusyPeriod(p,true) ;
runtimeResource.updateInstancesCount(p.getBegin()+1,resourceInstance.getPlanning().getNextPlanningAvailable(p.getEnd()+1));
}
activity.addAssignedResourceInstance(resourceInstance) ;
}
public Set<ResourceInstanceAvailability> getNextAvailableDateForAllResources(String processUUID, String activityName, long startDate, List<ResourceAssignement> resources, boolean isContigous) throws Exception {
boolean allResourceAvailable = false ;
long currentStartDate = startDate;
final Set<ResourceInstanceAvailability> instances = new HashSet<ResourceInstanceAvailability>();
int numberOfInstances = 0 ;
for (ResourceAssignement resourceAssignment : resources){
numberOfInstances = numberOfInstances + resourceAssignment.getQuantity() ;
}
do {
for (ResourceAssignement resourceAssignment : resources){
final List<ResourceInstanceAvailability> firstAvailability = findAvailableResource(resourceAssignment.getQuantity(),resourceAssignment.getResource(), currentStartDate, resourceAssignment.getDuration(), isContigous);
if (firstAvailability.get(0).getTime() > currentStartDate) {//Resource not available for currentStartDate
storeWaitingForResource(resourceAssignment.getResource(),activityName,processUUID,firstAvailability.get(0).getTime()-currentStartDate) ;
currentStartDate = firstAvailability.get(0).getTime();
instances.clear();
allResourceAvailable = false;
break;
}
instances.addAll(firstAvailability);
}
if (instances.size() == numberOfInstances){
allResourceAvailable = true ;
}
} while (!SimulationEngine.isStopped && !allResourceAvailable);
return instances;
}
private void storeWaitingForResource(Resource resource,String activityName, String processUUID, long waitingTime) {
RuntimeResource runtimeResource = getResourceInstances().get(resource.getName()) ;
runtimeResource.updateWaitingFor(activityName,waitingTime) ;
runtimeResource.updateProcessInstanceWaitingFor(processUUID,waitingTime) ;
}
public Map<String,RuntimeResource> getResourceInstances() {
return resourceInstances;
}
public void updateResourceConsumption(long interval) {
for(RuntimeResource resource : getResourceInstances().values()){
resource.updateInstancesCount(interval) ;
resource.updateWaitingFor(interval);
resource.updateProcessInstanceWaitingFor(interval);
}
}
}