Step 5: Transportation request generation
The TransportationRequest
class
A request to move cargo from a Warehouse
to a Store
will be presented by an object of a new TransportationRequest
class.
The class will contain
several constructor-initialized fields
backed up by getter methods:
-
'id' (unique request identifier);
-
'sourceAsset' (a
Warehouse
, transportation source); -
'destAsset' (a
Store
, transportation destination); -
'beginTime' (model time when the request has appeared);
-
'deadlineTime' (model time when we want the request to be fulfilled).
Besides, the TransportationRequest
will also need a boolean
field to denote whether the request has been fulfilled ('completed'),
and a double
-valued field to store the model time when the request has been completed ('completedTime').
Create the following 'TransportationRequest.java' file in the 'tutorial.model' package:
package tutorial.model;
public class TransportationRequest {
private final int id;
private final Warehouse sourceAsset;
private final Store destAsset;
private final double createdTime;
private final double deadlineTime;
private boolean completed;
private double completedTime;
public TransportationRequest(int id, Warehouse sourceAsset, Store destAsset, double beginTime, double deadlineTime) {
this.id = id;
this.sourceAsset = sourceAsset;
this.destAsset = destAsset;
this.createdTime = beginTime;
this.deadlineTime = deadlineTime;
//System.out.println(String.format("%.3f\tRequest #%s created", createdTime, id));
}
public int getId() {
return id;
}
public Asset getSourceAsset() {
return sourceAsset;
}
public Asset getDestAsset() {
return destAsset;
}
public double getCreatedTime() {
return createdTime;
}
public double getDeadlineTime() {
return deadlineTime;
}
public double getCompletedTime() {
return completedTime;
}
public boolean isCompleted() {
return completed;
}
public void setCompletedTime(double completedTime) {
this.completedTime = completedTime;
this.completed = true;
}
}
Uncomment the System.out.println
call in the class constructor to see more debug information.
The RequestGenerator
class
The 'outer world' that supplies new transportation requests to our model will be presented
by an object of the new RequestGenerator
class.
This object will generate transportation requests with varying parameters at random moments,
as defined by the Scenario
object.
The following parameters should be passed to the RequestGenerator
class constructor:
-
The
Model
instance, that will act as the source ofEngine
instance (needed to schedule request generation during modeling); -
Time interval between requests distibution;
-
Max delivery time.
Note that we do not pass the Scenario
object here:
the RequestGenerator
does not use any class from the tutorial.scenario
package.
We will let other objects subscribe to new requests via a special addNewRequestHandler()
method.
Create the following 'RequestGenerator.java' file in the 'tutorial.model' package:
package tutorial.model;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.apache.commons.math3.distribution.RealDistribution;
public class RequestGenerator {
private final Model model;
private final RealDistribution newRequestIntervalDistribution;
private final double maxDeliveryTimeHrs;
private List<Consumer<TransportationRequest>> newRequestHandlers = new ArrayList<>();
private int lastUsedRequestId = 0;
public RequestGenerator(Model model, RealDistribution newRequestIntervalDistribution, double maxDeliveryTimeHrs) {
this.model = model;
this.newRequestIntervalDistribution = newRequestIntervalDistribution;
this.maxDeliveryTimeHrs = maxDeliveryTimeHrs;
model.engine().scheduleRelative(0, this::createTransportationRequest);
}
public void addNewRequestHandler(Consumer<TransportationRequest> newRequestHandler) {
newRequestHandlers.add(newRequestHandler);
}
private void createTransportationRequest() {
Warehouse from = model.getWarehouses().get(model.getRandomGenerator().nextInt(model.getWarehouses().size()));
Store to = model.getStores().get(model.getRandomGenerator().nextInt(model.getStores().size()));
TransportationRequest newRequest = new TransportationRequest(getNextRequestId(), from, to,
model.engine().time(), model.engine().time() + maxDeliveryTimeHrs * model.engine().hour());
newRequestHandlers.forEach(handler -> handler.accept(newRequest));
model.engine().scheduleRelative(newRequestIntervalDistribution.sample(), this::createTransportationRequest);
}
private int getNextRequestId() {
return ++lastUsedRequestId;
}
}
Let’s discuss the features of the createTransportationRequest()
method:
-
To make the request generation a recurring process we place request creation inside a
createTransportationRequest()
method that uses theEngine.scheduleRelative()
method to schedule a self-call. The nextcreateTransportationRequest()
call will happen after a period of model time drawn from the distribution object. This is how the delay between consecutive requests is modeled. -
Note how a source
Warehouse
and a destinationStore
are chosen: therandomGenerator
object is used to enforce model reproducibility.
Changes to the Model
class
Now let’s get back to the Model
class and create a new RequestGenerator
object inside the Model
class constructor.
RequestGenerator
object in class constructorRealDistribution newRequestIntervalDistribution = new ExponentialDistribution(randomGenerator, scenario.getIntervalBetweenRequestsHrs());
RequestGenerator requestGenerator = new RequestGenerator(this, newRequestIntervalDistribution, scenario.getMaxDeliveryTimeHrs());
It is important that the same 'randomness source' (randomGenerator) be used in each and every place of the model that uses random values. |
Check the result
Start the program. You should see the following output printed to the console:
0,000 Request #1 created 1,024 Request #2 created 3,733 Request #3 created 3,871 Request #4 created 3,877 Request #5 created 4,037 Request #6 created 4,789 Request #7 created 7,358 Request #8 created 8,120 Request #9 created 8,197 Request #10 created 9,129 Request #11 created 10,034 Request #12 created 10,803 Request #13 created