1
0
Fork 0
mirror of https://github.com/graphhopper/jsprit.git synced 2020-01-24 07:45:05 +01:00

BreakForMultipleTimeWindows (#68)

* BreakForMultipleTimeWindows

* location fix

* builder

* extend from service

* oops

* extends break

* extends service

* BreakForMultipleTimeWindowsActivity

* so

* oops

* cr

* location with same id different coordinate

* leave skills as is :O

* test

* fixes with tests
This commit is contained in:
kandelirina 2018-11-07 18:16:54 +02:00 committed by GitHub
parent 07944d6dd6
commit c75cd5df9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 473 additions and 11 deletions

View file

@ -0,0 +1,141 @@
package com.graphhopper.jsprit.core.algorithm.recreate;
import com.graphhopper.jsprit.core.problem.JobActivityFactory;
import com.graphhopper.jsprit.core.problem.Location;
import com.graphhopper.jsprit.core.problem.constraint.*;
import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts;
import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts;
import com.graphhopper.jsprit.core.problem.driver.Driver;
import com.graphhopper.jsprit.core.problem.job.BreakForMultipleTimeWindows;
import com.graphhopper.jsprit.core.problem.job.Job;
import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
import com.graphhopper.jsprit.core.problem.solution.route.activity.BreakForMultipleTimeWindowsActivity;
import com.graphhopper.jsprit.core.problem.solution.route.activity.End;
import com.graphhopper.jsprit.core.problem.solution.route.activity.Start;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Iterator;
final class BreakForMultipleTimeWindowsInsertionCalculator implements JobInsertionCostsCalculator {
private static final Logger logger = LoggerFactory.getLogger(BreakInsertionCalculator.class);
private ConstraintManager constraintManager;
private VehicleRoutingTransportCosts transportCosts;
private final VehicleRoutingActivityCosts activityCosts;
private ActivityInsertionCostsCalculator additionalTransportCostsCalculator;
private JobActivityFactory activityFactory;
private AdditionalAccessEgressCalculator additionalAccessEgressCalculator;
public BreakForMultipleTimeWindowsInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator additionalTransportCostsCalculator, ConstraintManager constraintManager) {
this.transportCosts = routingCosts;
this.activityCosts = activityCosts;
this.constraintManager = constraintManager;
this.additionalTransportCostsCalculator = additionalTransportCostsCalculator;
additionalAccessEgressCalculator = new AdditionalAccessEgressCalculator(routingCosts);
logger.debug("initialise " + this);
}
public void setJobActivityFactory(JobActivityFactory jobActivityFactory) {
this.activityFactory = jobActivityFactory;
}
@Override
public String toString() {
return "[name=calculatesServiceInsertion]";
}
/**
* Calculates the marginal cost of inserting job i locally. This is based on the
* assumption that cost changes can entirely covered by only looking at the predecessor i-1 and its successor i+1.
*/
@Override
public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double bestKnownCosts) {
BreakForMultipleTimeWindows breakToInsert = (BreakForMultipleTimeWindows) jobToInsert;
JobInsertionContext insertionContext = new JobInsertionContext(currentRoute, jobToInsert, newVehicle, newDriver, newVehicleDepartureTime);
int insertionIndex = InsertionData.NO_INDEX;
BreakForMultipleTimeWindowsActivity breakAct2Insert = (BreakForMultipleTimeWindowsActivity) activityFactory.createActivities(breakToInsert).get(0);
insertionContext.getAssociatedActivities().add(breakAct2Insert);
/*
check hard constraints at route level
*/
if (!constraintManager.fulfilled(insertionContext)) {
return InsertionData.createEmptyInsertionData();
}
/*
check soft constraints at route level
*/
double additionalICostsAtRouteLevel = constraintManager.getCosts(insertionContext);
double bestCost = bestKnownCosts;
additionalICostsAtRouteLevel += additionalAccessEgressCalculator.getCosts(insertionContext);
/*
generate new start and end for new vehicle
*/
Start start = new Start(newVehicle.getStartLocation(), newVehicle.getEarliestDeparture(), Double.MAX_VALUE);
start.setEndTime(newVehicleDepartureTime);
End end = new End(newVehicle.getEndLocation(), 0.0, newVehicle.getLatestArrival());
Location bestLocation = null;
TourActivity prevAct = start;
double prevActStartTime = newVehicleDepartureTime;
int actIndex = 0;
Iterator<TourActivity> activityIterator = currentRoute.getActivities().iterator();
boolean tourEnd = false;
while (!tourEnd) {
TourActivity nextAct;
if (activityIterator.hasNext()) nextAct = activityIterator.next();
else {
nextAct = end;
tourEnd = true;
}
boolean breakThis = true;
final Location location = Location.Builder.newInstance().setId(breakAct2Insert.getJob().getLocation().getId()).setCoordinate(prevAct.getLocation().getCoordinate()).build();
breakAct2Insert.setLocation(location);
breakAct2Insert.setTheoreticalEarliestOperationStartTime(breakToInsert.getTimeWindow().getStart());
breakAct2Insert.setTheoreticalLatestOperationStartTime(breakToInsert.getTimeWindow().getEnd());
HardActivityConstraint.ConstraintsStatus status = constraintManager.fulfilled(insertionContext, prevAct, breakAct2Insert, nextAct, prevActStartTime);
if (status.equals(HardActivityConstraint.ConstraintsStatus.FULFILLED)) {
//from job2insert induced costs at activity level
double additionalICostsAtActLevel = constraintManager.getCosts(insertionContext, prevAct, breakAct2Insert, nextAct, prevActStartTime);
double additionalTransportationCosts = additionalTransportCostsCalculator.getCosts(insertionContext, prevAct, nextAct, breakAct2Insert, prevActStartTime);
if (additionalICostsAtRouteLevel + additionalICostsAtActLevel + additionalTransportationCosts < bestCost) {
bestCost = additionalICostsAtRouteLevel + additionalICostsAtActLevel + additionalTransportationCosts;
insertionIndex = actIndex;
bestLocation = location;
}
breakThis = false;
} else if (status.equals(HardActivityConstraint.ConstraintsStatus.NOT_FULFILLED)) {
breakThis = false;
}
double nextActArrTime = prevActStartTime + transportCosts.getTransportTime(prevAct.getLocation(), nextAct.getLocation(), prevActStartTime, newDriver, newVehicle);
prevActStartTime = Math.max(nextActArrTime, nextAct.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(prevAct, nextAct,nextActArrTime,newDriver,newVehicle);
prevAct = nextAct;
actIndex++;
if (breakThis) break;
}
if (insertionIndex == InsertionData.NO_INDEX) {
return InsertionData.createEmptyInsertionData();
}
InsertionData insertionData = new InsertionData(bestCost, InsertionData.NO_INDEX, insertionIndex, newVehicle, newDriver);
breakAct2Insert.setLocation(bestLocation);
insertionData.getEvents().add(new InsertBreak(currentRoute, newVehicle, breakAct2Insert, insertionIndex));
insertionData.getEvents().add(new SwitchVehicle(currentRoute, newVehicle, newVehicleDepartureTime));
insertionData.setVehicleDepartureTime(newVehicleDepartureTime);
return insertionData;
}
}

View file

@ -280,7 +280,6 @@ public class JobInsertionCostsCalculatorBuilder {
}
JobActivityFactory activityFactory = new JobActivityFactory() {
@Override
public List<AbstractActivity> createActivities(Job job) {
return vrp.copyAndGetActivities(job);
@ -295,12 +294,16 @@ public class JobInsertionCostsCalculatorBuilder {
BreakInsertionCalculator breakInsertionCalculator = new BreakInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), actInsertionCalc, constraintManager);
breakInsertionCalculator.setJobActivityFactory(activityFactory);
BreakForMultipleTimeWindowsInsertionCalculator breakForMultipleTimeWindowsInsertionCalculator = new BreakForMultipleTimeWindowsInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), actInsertionCalc, constraintManager);
breakForMultipleTimeWindowsInsertionCalculator.setJobActivityFactory(activityFactory);
JobCalculatorSwitcher switcher = new JobCalculatorSwitcher();
switcher.put(Shipment.class, shipmentInsertion);
switcher.put(Service.class, serviceInsertion);
switcher.put(Pickup.class, serviceInsertion);
switcher.put(Delivery.class, serviceInsertion);
switcher.put(Break.class, breakInsertionCalculator);
switcher.put(BreakForMultipleTimeWindows.class, breakForMultipleTimeWindowsInsertionCalculator);
switcher.put(ShipmentWithMutablePickupDeliverOptions.class, shipmentInsertion);
CalculatorPlusListeners calculatorPlusListeners = new CalculatorPlusListeners(switcher);

View file

@ -18,6 +18,7 @@
package com.graphhopper.jsprit.core.algorithm.recreate;
import com.graphhopper.jsprit.core.problem.JobActivityFactory;
import com.graphhopper.jsprit.core.problem.Location;
import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager;
import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint.ConstraintsStatus;
import com.graphhopper.jsprit.core.problem.constraint.SoftActivityConstraint;
@ -30,10 +31,7 @@ import com.graphhopper.jsprit.core.problem.job.Shipment;
import com.graphhopper.jsprit.core.problem.misc.ActivityContext;
import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
import com.graphhopper.jsprit.core.problem.solution.route.activity.End;
import com.graphhopper.jsprit.core.problem.solution.route.activity.Start;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
import com.graphhopper.jsprit.core.problem.solution.route.activity.*;
import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -175,7 +173,6 @@ final class ShipmentInsertionCalculator extends AbstractInsertionCalculator {
insertionContext.setRelatedActivityContext(pickupContext);
double prevActEndTime_deliveryLoop = shipmentPickupEndTime;
/*
--------------------------------
*/
@ -220,6 +217,14 @@ final class ShipmentInsertionCalculator extends AbstractInsertionCalculator {
//update prevAct and endTime
double nextActArrTime = prevActEndTime_deliveryLoop + transportCosts.getTransportTime(prevAct_deliveryLoop.getLocation(), nextAct_deliveryLoop.getLocation(), prevActEndTime_deliveryLoop, newDriver, newVehicle);
prevActEndTime_deliveryLoop = Math.max(nextActArrTime, nextAct_deliveryLoop.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(prevAct_deliveryLoop, nextAct_deliveryLoop,nextActArrTime,newDriver,newVehicle);
if (i == j && nextAct_deliveryLoop instanceof BreakForMultipleTimeWindowsActivity) {
final BreakForMultipleTimeWindowsActivity breakForMultipleTimeWindowsActivity = (BreakForMultipleTimeWindowsActivity) nextAct_deliveryLoop.duplicate();
final Location location = Location.Builder.newInstance()
.setId(breakForMultipleTimeWindowsActivity.getJob().getLocation().getId())
.setCoordinate(prevAct_deliveryLoop.getLocation().getCoordinate()).build();
breakForMultipleTimeWindowsActivity.setLocation(location);
nextAct_deliveryLoop = breakForMultipleTimeWindowsActivity;
}
prevAct_deliveryLoop = nextAct_deliveryLoop;
j++;
}

View file

@ -0,0 +1,57 @@
package com.graphhopper.jsprit.core.problem.job;
import com.graphhopper.jsprit.core.problem.Capacity;
public class BreakForMultipleTimeWindows extends Service {
public static class Builder extends Service.Builder<BreakForMultipleTimeWindows> {
/**
* Returns a new instance of builder that builds a pickup.
*
* @param id the id of the pickup
* @return the builder
*/
public static BreakForMultipleTimeWindows.Builder newInstance(String id) {
return new BreakForMultipleTimeWindows.Builder(id);
}
private boolean variableLocation = true;
Builder(String id) {
super(id);
}
/**
* Builds Pickup.
* <p>
* <p>Pickup type is "pickup"
*
* @return pickup
* @throws IllegalStateException if neither locationId nor coordinate has been set
*/
public BreakForMultipleTimeWindows build() {
if (location != null) {
variableLocation = false;
}
this.setType("break");
super.capacity = Capacity.Builder.newInstance().build();
super.skills = skillBuilder.build();
return new BreakForMultipleTimeWindows(this);
}
}
private boolean variableLocation = true;
BreakForMultipleTimeWindows(BreakForMultipleTimeWindows.Builder builder) {
super(builder);
this.variableLocation = builder.variableLocation;
}
public boolean hasVariableLocation() {
return variableLocation;
}
}

View file

@ -0,0 +1,179 @@
package com.graphhopper.jsprit.core.problem.solution.route.activity;
import com.graphhopper.jsprit.core.problem.AbstractActivity;
import com.graphhopper.jsprit.core.problem.Capacity;
import com.graphhopper.jsprit.core.problem.Location;
import com.graphhopper.jsprit.core.problem.job.BreakForMultipleTimeWindows;
import com.graphhopper.jsprit.core.problem.job.Service;
public class BreakForMultipleTimeWindowsActivity extends AbstractActivity implements TourActivity.JobActivity{
public static int counter = 0;
public double arrTime;
public double endTime;
private Location location;
private double duration;
/**
* @return the arrTime
*/
public double getArrTime() {
return arrTime;
}
/**
* @param arrTime the arrTime to set
*/
public void setArrTime(double arrTime) {
this.arrTime = arrTime;
}
/**
* @return the endTime
*/
public double getEndTime() {
return endTime;
}
/**
* @param endTime the endTime to set
*/
public void setEndTime(double endTime) {
this.endTime = endTime;
}
public static BreakForMultipleTimeWindowsActivity copyOf(BreakForMultipleTimeWindowsActivity breakActivity) {
return new BreakForMultipleTimeWindowsActivity(breakActivity);
}
public static BreakForMultipleTimeWindowsActivity newInstance(BreakForMultipleTimeWindows aBreak) {
return new BreakForMultipleTimeWindowsActivity(aBreak);
}
private final BreakForMultipleTimeWindows aBreak;
private double earliest = 0;
private double latest = Double.MAX_VALUE;
protected BreakForMultipleTimeWindowsActivity(BreakForMultipleTimeWindows aBreak) {
counter++;
this.aBreak = aBreak;
this.duration = aBreak.getServiceDuration();
this.location = aBreak.getLocation();
}
protected BreakForMultipleTimeWindowsActivity(BreakForMultipleTimeWindowsActivity breakActivity) {
counter++;
this.aBreak = (BreakForMultipleTimeWindows) breakActivity.getJob();
this.arrTime = breakActivity.getArrTime();
this.endTime = breakActivity.getEndTime();
this.location = breakActivity.getLocation();
setIndex(breakActivity.getIndex());
this.earliest = breakActivity.getTheoreticalEarliestOperationStartTime();
this.latest = breakActivity.getTheoreticalLatestOperationStartTime();
this.duration = breakActivity.getOperationTime();
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((aBreak == null) ? 0 : aBreak.hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BreakForMultipleTimeWindowsActivity other = (BreakForMultipleTimeWindowsActivity) obj;
if (aBreak == null) {
if (other.aBreak != null)
return false;
} else if (!aBreak.equals(other.aBreak))
return false;
return true;
}
public double getTheoreticalEarliestOperationStartTime() {
return earliest;
}
public double getTheoreticalLatestOperationStartTime() {
return latest;
}
@Override
public double getOperationTime() {
return duration;
}
public void setOperationTime(double duration){
this.duration = duration;
}
@Override
public Location getLocation() {
return location;
}
public void setLocation(Location breakLocation) {
this.location = breakLocation;
}
@Override
public Service getJob() {
return aBreak;
}
@Override
public String toString() {
return "[type=" + getName() + "][location=" + getLocation()
+ "][size=" + getSize().toString()
+ "][twStart=" + Activities.round(getTheoreticalEarliestOperationStartTime())
+ "][twEnd=" + Activities.round(getTheoreticalLatestOperationStartTime()) + "]";
}
@Override
public void setTheoreticalEarliestOperationStartTime(double earliest) {
this.earliest = earliest;
}
@Override
public void setTheoreticalLatestOperationStartTime(double latest) {
this.latest = latest;
}
@Override
public String getName() {
return aBreak.getType();
}
@Override
public TourActivity duplicate() {
return new BreakForMultipleTimeWindowsActivity(this);
}
@Override
public Capacity getSize() {
return aBreak.getSize();
}
}

View file

@ -18,6 +18,7 @@
package com.graphhopper.jsprit.core.problem.solution.route.activity;
import com.graphhopper.jsprit.core.problem.AbstractActivity;
import com.graphhopper.jsprit.core.problem.job.BreakForMultipleTimeWindows;
import com.graphhopper.jsprit.core.problem.job.Delivery;
import com.graphhopper.jsprit.core.problem.job.Pickup;
import com.graphhopper.jsprit.core.problem.job.Service;
@ -27,7 +28,9 @@ public class DefaultTourActivityFactory implements TourActivityFactory {
@Override
public AbstractActivity createActivity(Service service) {
AbstractActivity act;
if (service instanceof Pickup) {
if (service instanceof BreakForMultipleTimeWindows) {
act = new BreakForMultipleTimeWindowsActivity((BreakForMultipleTimeWindows) service);
} else if (service instanceof Pickup) {
act = new PickupService((Pickup) service);
} else if (service instanceof Delivery) {
act = new DeliverService((Delivery) service);

View file

@ -31,27 +31,27 @@ import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts;
import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts;
import com.graphhopper.jsprit.core.problem.driver.Driver;
import com.graphhopper.jsprit.core.problem.driver.DriverImpl;
import com.graphhopper.jsprit.core.problem.job.BreakForMultipleTimeWindows;
import com.graphhopper.jsprit.core.problem.job.Pickup;
import com.graphhopper.jsprit.core.problem.job.Service;
import com.graphhopper.jsprit.core.problem.job.Shipment;
import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
import com.graphhopper.jsprit.core.problem.solution.route.activity.DeliverShipment;
import com.graphhopper.jsprit.core.problem.solution.route.activity.PickupService;
import com.graphhopper.jsprit.core.problem.solution.route.activity.PickupShipment;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
import com.graphhopper.jsprit.core.problem.solution.route.activity.*;
import com.graphhopper.jsprit.core.problem.solution.route.state.RouteAndActivityStateGetter;
import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleType;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl;
import com.graphhopper.jsprit.core.util.CostFactory;
import com.graphhopper.jsprit.core.util.ManhattanCosts;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@ -316,5 +316,48 @@ public class ShipmentInsertionCalculatorTest {
assertEquals(3, iData.getDeliveryInsertionIndex());
}
@Test
public void whenPickupInsertedBeforeBreak() {
final BreakForMultipleTimeWindows breakForMultipleTimeWindows =
BreakForMultipleTimeWindows.Builder.newInstance("break")
.setLocation(Location.newInstance(5, 10))
.addTimeWindow(20, 20)
.setServiceTime(5)
.build();
final VehicleImpl vehicle = VehicleImpl.Builder.newInstance(UUID.randomUUID().toString())
.setStartLocation(Location.newInstance(0, 0))
.build();
final VehicleRoute route = VehicleRoute.Builder.newInstance(vehicle)
.addService(breakForMultipleTimeWindows)
.build();
route.getActivities().get(0).setArrTime(5);
route.getActivities().get(0).setEndTime(10);
Shipment shipment = Shipment.Builder.newInstance("shipment")
.setPickupLocation(Location.newInstance(0,10))
.setPickupTimeWindow(new TimeWindow(0, 20))
.setPickupServiceTime(10)
.setDeliveryLocation(Location.newInstance(10,10))
.setDeliveryTimeWindow(new TimeWindow(60, 160))
.setDeliveryServiceTime(10)
.build();
VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().addJob(shipment).addJob(breakForMultipleTimeWindows).build();
StateManager stateManager = new StateManager(vrp);
final ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager);
ShipmentInsertionCalculator insertionCalculator = new ShipmentInsertionCalculator(new ManhattanCosts() {
@Override
public double getTransportTime(Location from, Location to, double departureTime, Driver driver, Vehicle vehicle) {
return (to.getId() != null && to.getId().equals("break")) ? .0 : super.getTransportTime(from, to, departureTime, driver, vehicle);
}
}, activityCosts,
activityInsertionCostsCalculator, constraintManager);
insertionCalculator.setJobActivityFactory(vrp.getJobActivityFactory());
InsertionData iData = insertionCalculator.getInsertionData(route, shipment, vehicle, 0.0, null, Double.MAX_VALUE);
assertEquals(0, iData.getPickupInsertionIndex());
assertEquals(0, iData.getDeliveryInsertionIndex());
}
}

View file

@ -0,0 +1,31 @@
package com.graphhopper.jsprit.core.problem.job;
import com.graphhopper.jsprit.core.problem.Location;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow;
import org.junit.Test;
import java.util.UUID;
import static org.junit.Assert.*;
public class BreakForMultipleTimeWindowsTest {
@Test
public void breakCreatedWithDriverIdAndSkills() {
final String serviceId = UUID.randomUUID().toString();
final String skill = UUID.randomUUID().toString();
final BreakForMultipleTimeWindows breakForMultipleTimeWindows =
BreakForMultipleTimeWindows.Builder.newInstance(serviceId)
.setLocation(Location.newInstance(-0.25, 0.25))
.addTimeWindow(0, 60)
.setServiceTime(20)
.addRequiredSkill(skill)
.setPriority(1)
.build();
assertTrue(breakForMultipleTimeWindows.getRequiredSkills().containsSkill(skill));
assertEquals(Location.newInstance(-0.25, 0.25), breakForMultipleTimeWindows.getLocation());
assertEquals(new TimeWindow(0, 60), breakForMultipleTimeWindows.getTimeWindow());
assertEquals(20, breakForMultipleTimeWindows.getServiceDuration(), .0001);
}
}