Step 4: Pallet-aware environment
As you may recall, Forklift
inherits the GraphAgent
class.
When the moveTo()
method of this class is called, the optimal route for the upcoming movement through the graph is calculated.
We can inherit the GraphEnvironment
class in such a way that pallets are considered as obstacles when calculating routes.
package com.company.warehouse.simulation.graph;
import com.amalgamasimulation.graphagent.GraphEnvironment;
public class EnvironmentWithPallets extends GraphEnvironment<Node, Arc, Void> {
private static final double BUSY_NODE_WEIGHT = 10_000;
@Override
public double getNodeWeight(Node node, Void k) {
var busy = node.getPalletPosition()
.map(p -> p.isAvailableFor(false))
.orElse(false);
return busy ? BUSY_NODE_WEIGHT : 0;
}
}
GraphAgent
wants to travel the shortest path, so,
by adding a weight of 10000 to the nodes with pallets,
we increase the calculated length of the paths passing through these nodes.
Thus, the routes that avoid obstacles are considered "shorter" than the routes passing through pallets.
Since we used node.getPalletPosition()
, we have to implement it.
Open the .simulation.graph.Node
class.
public class Node extends AgentGraphNodeImpl {
private final String id;
private PalletPosition palletPosition;
public Node(Point point, String id) {
super(point);
this.id = id;
}
public Optional<PalletPosition> getPalletPosition() {
return Optional.ofNullable(palletPosition);
}
public void setPalletPosition(PalletPosition palletPosition) {
Objects.requireNonNull(palletPosition);
if (this.palletPosition != null) {
throw new RuntimeException("Can't assign " + palletPosition + " to " + this + " as " + this.palletPosition + " already assigned!");
}
this.palletPosition = palletPosition;
}
@Override
public String toString() {
return id;
}
}
By the way, we also added a new id
field to simplify debugging and monitoring.
We need to tell the node about the PalletPosition
that resides in it.
...
public PalletPosition(Node node) {
this.node = node;
node.setPalletPosition(this);
}
...
Finally, we need to initialize all these new things in the Model
class.
Define & instantiate the environment:
...
public class Model extends com.amalgamasimulation.engine.Model {
...
// protected GraphEnvironment<Node, Arc, Agent> graphEnvironment;
private EnvironmentWithPallets graphEnvironment;
public EnvironmentWithPallets getGraphEnvironment() {
return graphEnvironment;
}
...
public Model(Engine engine, Scenario scenario) {
...
// graphEnvironment = new GraphEnvironment<>();
graphEnvironment = new EnvironmentWithPallets();
initializeNodes();
initializeArcs();
initializeMainStorage();
initializeGates();
initializeForklifts();
makeAssignments();
}
...
Pass id
to the Node
constructor:
...
public Node addNode(Point point, String id) {
Node node = new Node(point, id);
graphEnvironment.addNode(node);
return node;
}
...
private void initializeNodes() {
for (var scenarioNode : scenario.getNodes()) {
Node node = addNode(new Point(scenarioNode.getX(), scenarioNode.getY()), scenarioNode.getId());
mapping.nodesMap.put(scenarioNode, node);
}
}
...
Now launch the simulation and take pride in the success.
See how the trajectory of the top-left forklift does not pass through any occupied 🟩 pallet positions anymore: