Step 4: Cargo arrival & departure
1. Update the DockArea
class
Add the following methods to the DockArea
class (the 'simulation' bundle):
public boolean tryAddCargo(int palletsToAdd) {
return tryPerformPalletOperation(palletsToAdd, PalletPosition.Operation.LOADING);
}
public boolean tryRemoveCargo(int palletsToRemove) {
return tryPerformPalletOperation(palletsToRemove, PalletPosition.Operation.UNLOADING);
}
private boolean tryPerformPalletOperation(int palletsCount, PalletPosition.Operation operation) {
var emptyPalletPositions = palletPositions.stream().filter(pp -> pp.isAvailableFor(operation)).toList();
if (emptyPalletPositions.size() < palletsCount) {
return false;
}
for (int i = 0; i < palletsCount; i++) {
emptyPalletPositions.get(i).performPalletOperation(operation);
}
return true;
}
2. Update the Model
class
In the Model
class, add the following fields and methods:
//...
private int receiptAttempts = 0;
private int successfulReceipts = 0;
private int shipmentAttempts = 0;
private int successfulShipments = 0;
//...
private void attemptReceipt() {
final int receiptSize = 6;
final double receiptInterval = 1 * day();
for (var dockArea : dockAreas.getOrDefault(Direction.IN, List.of())) {
receiptAttempts++;
if (dockArea.tryAddCargo(receiptSize)) {
successfulReceipts++;
dispatchIdleForklifts();
}
}
engine().scheduleRelative(receiptInterval, this::attemptReceipt);
}
private void attemptShipment() {
final int shipmentSize = 1;
final double shipmentInterval = 4 * hour();
for (var dockArea : dockAreas.getOrDefault(Direction.OUT, List.of())) {
shipmentAttempts++;
if (dockArea.tryRemoveCargo(shipmentSize)) {
successfulShipments++;
dispatchIdleForklifts();
}
}
engine().scheduleRelative(shipmentInterval, this::attemptShipment);
}
public int getReceiptAttempts() {
return receiptAttempts;
}
public int getSuccessfulReceipts() {
return successfulReceipts;
}
public int getShipmentAttempts() {
return shipmentAttempts;
}
public int getSuccessfulShipments() {
return successfulShipments;
}
public double getServiceLevel() {
return Utils.zidz(successfulReceipts + successfulShipments, receiptAttempts + shipmentAttempts);
}
public double getUtilization() {
return getAgents().stream().mapToDouble(agent -> agent.getUtilization()).average().orElse(0.0);
}
Append the attemptReceipt()
and attemptShipment()
method calls in the end of the Model
class constructor:
engine.scheduleRelative(0, this::attemptReceipt);
engine.scheduleRelative(0, this::attemptShipment);
Run the simulation.
During the previous runs, the inbound dock area (left-hand side) became empty by the end of the simulation. Now it gets replenished (receives new cargo from some external source).
Also, the right-hand side dock area can now ship its cargo.
3. Display service level and utilization
In the SimulationStatisticsPart
class, add new indicators in the onShowModel()
method (inside the List.of()
call):
// ...
new Indicator("Successful receipts", () -> (double) model.getSuccessfulReceipts(), Formats.getDefaultFormats()::noDecimals, false),
new Indicator("Receipts attempted", () -> (double) model.getReceiptAttempts(), Formats.getDefaultFormats()::noDecimals, false),
new Indicator("Successful shipments", () -> (double) model.getSuccessfulShipments(), Formats.getDefaultFormats()::noDecimals, false),
new Indicator("Shipments attempted", () -> (double) model.getShipmentAttempts(), Formats.getDefaultFormats()::noDecimals, false),
new Indicator("Service level", () -> model.getServiceLevel(), Formats.getDefaultFormats()::percent, true),
new Indicator("Forklift utilization", () -> model.getUtilization(), Formats.getDefaultFormats()::percent, true)
// ...
New statistics items are added and shown in the bottom left corner in the simulation mode:

In the SimulationStatusShape
class, add the following two new TextShapes (see class constructor):
withShape(new TextShape(() -> "Service level: %s".formatted(Formats.getDefaultFormats().percent(model.getServiceLevel())))
.withPoint(new Point(10, 55))
.withFontColor(Colors.DARK_GREEN)
.withFontSize(18)
.withHorizontalAlignment(HorizontalAlignment.RIGHT)
.withVerticalAlignment(VerticalAlignment.BOTTOM));
withShape(new TextShape(() -> "Utilization: %s".formatted(Formats.getDefaultFormats().percent(model.getUtilization())))
.withPoint(new Point(10, 100))
.withFontColor(Colors.DARK_SLATE_BLUE)
.withFontSize(18)
.withHorizontalAlignment(HorizontalAlignment.RIGHT)
.withVerticalAlignment(VerticalAlignment.BOTTOM));
Add the missing import
statements.
The two new items are displayed in the top left part int the simulation mode:
