/** * 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.model.calendar; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import org.bonitasoft.simulation.engine.SimulationEngine; import org.bonitasoft.simulation.model.Period; import org.bonitasoft.simulation.model.process.ResourceAssignement; import org.bonitasoft.simulation.model.process.SimActivity; import org.bonitasoft.simulation.model.process.SimProcess; import org.bonitasoft.simulation.model.process.SimTransition; /** * @author Romain Bioteau * */ public class SimCalendarInstance extends SimCalendar { private SortedSet<Period> busyPeriods; //key=executionTime, value=startTime of the free period private Map<Long, Long> contiguousIndexes; private Map<Long, Long> allIndexes; private SimProcess simProc; private String resourceName; public SimCalendarInstance(final long startTime,SimProcess process,String resourceName, Map<Integer, SimCalendarDay> daysOfWeek) { super(); busyPeriods = new TreeSet<Period>(); this.simProc = process ; this.resourceName = resourceName ; this.daysOfWeek = daysOfWeek ; } private void initIndexes() throws Exception { if (simProc != null && contiguousIndexes == null) { SortedSet<Long> durations = new TreeSet<Long>(); List<SimActivity> parsed = new ArrayList<SimActivity>(); findAllIndexDuration(simProc.getStartElements(),parsed,durations ); contiguousIndexes = new TreeMap<Long, Long>(); allIndexes = new TreeMap<Long, Long>(); for(Long duration : durations){ if(isContigousDurationPossible(duration)){ contiguousIndexes.put(duration, getFirstAvailableDate(SimulationEngine.currentTime, duration, true)); } allIndexes.put(duration, getFirstAvailableDate(SimulationEngine.currentTime, duration, false)); } } } private boolean isContigousDurationPossible(Long duration) { if(getNextPlanningUnavailable(new Date().getTime()) == -1){ return true ; } Set<Long> durations = getAvailablePlanningDurations() ; for(Long l : durations){ if(l >= duration){ return true ; } } return false; } private void findAllIndexDuration(Set<SimActivity> elements,List<SimActivity> parsed,SortedSet<Long> durations) { for(SimActivity a : elements){ if(!parsed.contains(a)){ for(ResourceAssignement ra : a.getAssignedResources()){ if(ra.getResource().getName().equals(resourceName)){ if(ra.getDuration() > 0){ durations.add(ra.getDuration()) ; } } } parsed.add(a) ; for(SimTransition t : a.getOutgoingTransitions()){ findAllIndexDuration(Collections.singleton(t.getTarget()),parsed, durations); } } } } public SortedSet<Period> getWorkingPeriods(){ return Collections.unmodifiableSortedSet(busyPeriods) ; } public void addBusyPeriod(final Period busyPeriod, boolean flushOldPeriod) throws Exception { if(!busyPeriods.add(busyPeriod)){ throw new Exception("Impossible to add period :"+busyPeriod); //$NON-NLS-1$ } updateIndexes(busyPeriod, true); updateIndexes(busyPeriod , false); if(flushOldPeriod){ flushBusyPeriods() ; } } private void flushBusyPeriods() { SortedSet<Period> wp = busyPeriods.tailSet(new Period(SimulationEngine.currentTime, SimulationEngine.currentTime)); busyPeriods.removeAll(new TreeSet<Period>(wp)); } private void updateIndexes(final Period busyPeriod, final boolean contiguous) throws Exception { if(simProc != null){ initIndexes(); Map<Long, Long> indexes = allIndexes; if (contiguous) { indexes = contiguousIndexes; } final Map<Long, Long> newIndexes = new HashMap<Long, Long>(); for (Map.Entry<Long, Long> index : indexes.entrySet()) { final Long indexPeriodDuration = index.getKey(); final Long indexTime = index.getValue(); final Period indexPeriod = new Period(indexTime, indexTime + indexPeriodDuration); long newIndexTime = indexTime; if (busyPeriod.overlaps(indexPeriod)) { //calculate new index position from busyPeriod end newIndexTime = getFirstAvailableDate(busyPeriod.getEnd(), indexPeriodDuration, contiguous); } else if (indexTime < SimulationEngine.currentTime) { newIndexTime = getFirstAvailableDate(SimulationEngine.currentTime, indexPeriodDuration, contiguous); } newIndexes.put(indexPeriodDuration, newIndexTime); } if (contiguous) { contiguousIndexes = newIndexes; } else { allIndexes = newIndexes; } } } private long getIndexTime(final long executionTime, final boolean contiguous) throws Exception { if(simProc != null){ initIndexes(); Map<Long, Long> indexes = allIndexes; if (contiguous) { indexes = contiguousIndexes; } if( indexes.get(executionTime) != null){ return indexes.get(executionTime) ; } } return SimulationEngine.currentTime ; } public long getNotWorkingDate(long current) { for(Period p : busyPeriods){ if(p.contains(current)){ current = p.getEnd() ; } } if(!isPlanningAvailable(current)){ current = getNextPlanningAvailable(current); } return current ; } public boolean isWorkingDuring(Period period){ for (Period wp : busyPeriods) { if (wp.overlaps(period)) { return true ; } } return false ; } /** * Check resource availability regarding its working charge and its planning * @param period * @return */ public boolean isAvailable(Period period) { return !isWorkingDuring(period)&& isPlanningAvailable(period); } public long getFirstAvailableDate(long start, long executionTime, boolean contigous) throws Exception { long current = start ; long index = getIndexTime(executionTime, contigous); current = getNextPlanningAvailable(current); Period p = new Period(current, current + executionTime); if (index >= start) { current = index; } else if (isWorkingDuring(p)) { current = getNotWorkingPeriod(p) ; } if (!contigous) { boolean available = false ; while (!SimulationEngine.isStopped && !available){//CHECK PERIOD AVAILABILITY p = new Period(current, current + executionTime); List<Period> periods = split(p) ; available = true ; for (Period per : periods){//CHECK FOR EACH SPLIITED PERIODS IF THERE ARE VALID (NOT WORKING) if (isWorkingDuring(per)){ available = false ; //AS THE PERIOD IS NOT AVAILABLE, UPDATE CURRENT DATE TO A FREE ONE AND RE-LOOP current = getNotWorkingPeriod(per); break; } } } } else { boolean available = false ; while(!SimulationEngine.isStopped && !available){//CHECK PERIOD AVAILABILITY p = new Period(current, current+executionTime); available = true ; if(isWorkingDuring(p) ){ available = false ; current = getNotWorkingPeriod(p); p = new Period(current, current+executionTime); } if(split(p).size() != 1){ available = false ; current = getNextPlanningUnavailable(p.getBegin()) ; current = getNextPlanningAvailable(current) ; } } } return current ; } public long getNotWorkingPeriod(Period currentPeriod) { long newStart = currentPeriod.getBegin() ; long last = 0 ; for (Period p : busyPeriods){ if(currentPeriod.overlaps(p)){ if(last < p.getEnd()){ last = p.getEnd() ; } } } if (last != 0 ){ newStart = last ; } if (!isPlanningAvailable(newStart)){ newStart = getNextPlanningAvailable(newStart); } return newStart ; } }