Step 6: Idling Task
Let’s make sure that idle forklifts return to the garage.
The IdlingTask
code will look a lot like a simplified version of the MoveCargoTask
.
However, it will have one important additional feature:
this task must be able to be canceled at any time during its execution.
This will allow us to assign a forklift to a new useful job
without having to wait for it to reach the garage.
package com.company.warehouse.simulation.tasks;
import com.amalgamasimulation.engine.Engine;
import com.amalgamasimulation.engine.StateMachine;
import com.company.warehouse.simulation.equipment.Forklift;
import com.company.warehouse.simulation.equipment.IEquipmentState;
public class IdlingTask extends ForkliftTask {
public enum State implements IEquipmentState {
PENDING,
MOVE_TO_BASE,
IDLING,
CANCELLED,
;
/**
* Idling task is non utilizing by definition.
*/
@Override
public boolean isUtilized() {
return false;
}
}
private StateMachine<State> control;
public IdlingTask(Engine engine, Forklift forklift) {
super(engine, forklift);
control = new StateMachine<>(State.values(), State.PENDING, engine)
.addTransition(State.PENDING, State.MOVE_TO_BASE)
.addTransition(State.MOVE_TO_BASE, State.IDLING)
.addTransition(State.PENDING, State.CANCELLED)
.addTransition(State.MOVE_TO_BASE, State.CANCELLED)
.addTransition(State.IDLING, State.CANCELLED)
.addEnterAction(State.MOVE_TO_BASE, state -> moveToBase())
.addEnterAction(State.CANCELLED, state -> cancelling())
;
}
@Override
public void run() {
control.receiveMessage(State.MOVE_TO_BASE);
}
/**
* Cancels task execution and also aborts current action of the forklift.
*/
public void cancel() {
control.receiveMessage(State.CANCELLED);
}
private void moveToBase() {
forklift.moveToBase(
() -> control.receiveMessage(State.IDLING));
}
private void cancelling() {
forklift.cancelCurrentAction();
}
public Forklift getForklift() {
return forklift;
}
}
Now let’s update the main loop in the Model::makeAssignments()
method:
...
private void makeAssignments() {
...
int i = 0;
for (var forklift : forklifts) {
final var startTime = i++ * 2 * minute();
busyPositions.tryAdvance(from ->
freePositions.tryAdvance(to -> {
final var movingTask = new MovePalletTask(engine(), forklift, from, to);
final var idlingTask = new IdlingTask(engine(), forklift);
engine().scheduleRelative(startTime,
() -> movingTask.start(
() -> idlingTask.start(null)
)
);
})
);
}
}
...
Add appropriate imports.
When we start MoveCargoTask
, we ask it to start an IdlingTask
upon completion.
To improve the visibility of the process, we increased the task start interval to 2 minutes. This removes too much crowding.
Run the simulation and make sure that after hard work, the forklifts return home and have a rest.