package cz.agents.agentpolis.darptestbed.simmodel.environment.model;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import cz.agents.agentpolis.darptestbed.simmodel.agent.timer.Timer;
import cz.agents.agentpolis.darptestbed.simmodel.entity.vehicle.TestbedVehicle;
import cz.agents.agentpolis.simmodel.agent.Agent;
import cz.agents.agentpolis.simmodel.environment.model.query.AgentPositionQuery;
import java.util.*;
/**
* A storage that contains current information about taxis and their drivers,
* e.g. free taxis list etc. This storage also enables to change this
* information, e.q. when a taxi is taken.
*
* @author Lukas Canda
*/
@Singleton
public class TestbedModel {
/**
* Returns an agent's current position.
*/
protected final AgentPositionQuery positionQuery;
/**
* Storage for all vehicles (you can get them by ids)
*/
protected final TestbedVehicleStorage vehicleStorage;
/**
* The dispatching that serves as the central communication hub between
* passengers and taxi drivers (if using centralized communication).
*/
protected Agent dispatching;
/**
* The time when the simulation started
*/
protected long simulationStartTime;
/**
* The time when the simulation finished
*/
protected long simulationEndTime;
/**
* Timers are used to call timer callback methods on all agents in a regular
* manner. It enables agents to process requests and proposals only once in
* a while.
*/
private Timer dispatchingTimer;
private Timer taxiDriversTimer;
private Timer passengersTimer;
// TODO: Refactor to set
/**
* Following properties saves the current state of the environment:
* taxisFree - a list of ids of taxis, which have currently no task assigned
* taxiDriversFree - a list of ids of free taxis' drivers (the same order as
* previous list) taxisAtWork - a list of ids of taxis, which have been
* assigned a task passengers - a list of ids of all passengers
*/
private List<String> taxisFree;
private List<String> taxiDriversFree;
private List<String> taxisBusy;
private List<String> taxiDriversBusy;
private List<String> passengers;
/**
* Map of taxi ids along with their current passengers on board
*/
protected Map<String, List<String>> taxiWithPassengersOnBoard;
/**
* Busy taxi driver save their future positions at the end of their trips
* into this map (<Taxi ID, The number of node = position at the end>)
*/
protected Map<String, Long> taxiWithEndOfTripPositions;
@Inject
public TestbedModel(AgentPositionQuery positionQuery, TestbedVehicleStorage vehicleStorage) {
this.positionQuery = positionQuery;
this.vehicleStorage = vehicleStorage;
this.taxisFree = new ArrayList<String>();
this.taxiDriversFree = new ArrayList<String>();
this.taxisBusy = new ArrayList<String>();
this.taxiDriversBusy = new ArrayList<String>();
this.passengers = new ArrayList<String>();
this.taxiWithPassengersOnBoard = new HashMap<String, List<String>>();
this.taxiWithEndOfTripPositions = new HashMap<String, Long>();
}
public void setDispatching(Agent dispatching) {
this.dispatching = dispatching;
}
public void setTimers(Timer dispatchingTimer, Timer taxiDriversTimer, Timer passengersTimer) {
this.dispatchingTimer = dispatchingTimer;
this.taxiDriversTimer = taxiDriversTimer;
this.passengersTimer = passengersTimer;
}
/**
* Starts all timers, that've been set, and also remembers the time when the
* simulation started (now)
*/
public void startTimers() {
if (this.dispatchingTimer != null) {
this.dispatchingTimer.start();
}
if (this.taxiDriversTimer != null) {
this.taxiDriversTimer.start();
}
if (this.passengersTimer != null) {
this.passengersTimer.start();
}
this.simulationStartTime = System.currentTimeMillis();
}
/**
* Adds a free vehicle into the list, along with its driver
*
* @param vehicleId
* @param taxiDriverId
*/
public void addFreeTaxi(String vehicleId, String taxiDriverId) {
this.taxisFree.add(vehicleId);
this.taxiDriversFree.add(taxiDriverId);
// in the case it was busy before
this.taxisBusy.remove(vehicleId);
this.taxiDriversBusy.remove(vehicleId);
}
/**
* Adds a passenger into the list
*
* @param passengerId
*/
public void addPassenger(String passengerId) {
this.passengers.add(passengerId);
}
public List<String> getTaxisFree() {
return taxisFree;
}
public List<String> getTaxiDriversFree() {
return taxiDriversFree;
}
public List<String> getAllTaxiDrivers() {
List<String> allDrivers = new ArrayList<String>(this.taxiDriversFree);
allDrivers.addAll(this.taxiDriversBusy);
return allDrivers;
}
/**
* Moves the taxi given as parameter from list of free taxis into the list
* "busy". And also does the same with its driver.
*
* @param taxiIndex the index of the taxi in the list
*/
public void setTaxiBusy(int taxiIndex) {
this.taxisBusy.add(this.taxisFree.remove(taxiIndex));
this.taxiDriversBusy.add(this.taxiDriversFree.remove(taxiIndex));
}
/**
* Moves the taxi given as parameter from list of free taxis into the list
* "busy". And also does the same with its driver.
*
* @param taxiId the id of the taxi in the list
*/
public void setTaxiBusy(String taxiId) {
if (this.taxisFree.contains(taxiId)) {
int taxiIndex = this.taxisFree.lastIndexOf(taxiId);
setTaxiBusy(taxiIndex);
}
}
/**
* Moves the taxi given as parameter from list of busy taxis into the list
* "free". And also does the same with its driver.
*
* @param taxiId the id of the taxi in the list
*/
public void setTaxiFree(String taxiId) {
if (this.taxisBusy.contains(taxiId)) {
int taxiIndex = this.taxisBusy.lastIndexOf(taxiId);
this.taxisFree.add(this.taxisBusy.remove(taxiIndex));
this.taxiDriversFree.add(this.taxiDriversBusy.remove(taxiIndex));
}
}
/**
* Get the id of the vehicle driven by the given driver
*
* @param taxiDriverId taxi driver id
* @return taxi id, or null, if there's no such id
*/
public String getVehicleId(String taxiDriverId) {
for (int i = 0; i < this.taxiDriversFree.size(); i++) {
if (this.taxiDriversFree.get(i).equals(taxiDriverId)) {
return this.taxisFree.get(i);
}
}
for (int i = 0; i < this.taxiDriversBusy.size(); i++) {
if (this.taxiDriversBusy.get(i).equals(taxiDriverId)) {
return this.taxisBusy.get(i);
}
}
return null;
}
/**
* Get the id of the taxi driver who drives the given vehicle
*
* @param vehicleId vehicle id
* @return taxi driver id, or null, if there's no such id
*/
public String getTaxiDriverId(String vehicleId) {
for (int i = 0; i < this.taxisFree.size(); i++) {
if (this.taxisFree.get(i).equals(vehicleId)) {
return this.taxiDriversFree.get(i);
}
}
for (int i = 0; i < this.taxisBusy.size(); i++) {
if (this.taxisBusy.get(i).equals(vehicleId)) {
return this.taxiDriversBusy.get(i);
}
}
return null;
}
/**
* Add a passenger on board of a taxi (just to remember it)
*
* @param passenId passenger getting in a taxi
* @param vehicleId taxi id
*/
public void addPassengerOnBoard(String passenId, String vehicleId) {
List<String> passenIds = this.taxiWithPassengersOnBoard.get(vehicleId);
if (passenIds == null) {
passenIds = new ArrayList<String>();
}
passenIds.add(passenId);
this.taxiWithPassengersOnBoard.put(vehicleId, passenIds);
}
/**
* Remove a passenger from a taxi (just to remember its current state)
*
* @param passenId passenger getting off a taxi
* @param vehicleId taxi id
* @return true, if the passenger was found in the taxi
*/
public boolean removePassengerOnBoard(String passenId, String vehicleId) {
List<String> passenIds = this.taxiWithPassengersOnBoard.get(vehicleId);
if (passenIds == null) {
return false;
}
boolean ret = passenIds.remove(passenId);
this.taxiWithPassengersOnBoard.put(vehicleId, passenIds); // not needed! working with references
return ret;
}
/**
* Get number of passengers currently on board of the taxi
*
* @param vehicleId the taxi we're asking for
* @return the number of passengers on board
*/
public int getNumOfPassenOnBoard(String vehicleId) {
List<String> passenOnBoard = this.taxiWithPassengersOnBoard.get(vehicleId);
if (passenOnBoard == null) {
return 0;
}
return passenOnBoard.size();
}
/**
* Save the position at the end of my trip (usually used by busy taxi
* drivers, that are currently on the way)
*
* @param vehicleId the id of the taxi
* @param endOfTripPos the number of node - position at the end of taxi's trip
*/
public void setEndOfTripPosition(String vehicleId, Long endOfTripPos) {
this.taxiWithEndOfTripPositions.put(vehicleId, endOfTripPos);
}
/**
* Get the future position of the taxi after finishing its trip
*
* @param taxiId id of the taxi
* @return number of node - position at the end of the trip
*/
public Long getEndOfTripPosition(String taxiId) {
Long position = this.taxiWithEndOfTripPositions.get(taxiId);
if (position == null) {
return positionQuery.getCurrentPositionByNodeId(taxiId);
}
return position;
}
public Timer getDispatchingTimer() {
return dispatchingTimer;
}
public Timer getTaxiDriversTimer() {
return taxiDriversTimer;
}
public Timer getPassengersTimer() {
return passengersTimer;
}
public Agent getDispatching() {
return dispatching;
}
public List<String> getPassengers() {
return passengers;
}
public List<String> getPassengers(String vehicleId) {
List<String> retrieved = this.taxiWithPassengersOnBoard.get(vehicleId);
if (retrieved != null)
return Collections.unmodifiableList(retrieved);
else
return null;
}
public long getSimulationStartTime() {
return simulationStartTime;
}
public void setSimulationEndTime(long simulationEndTime) {
this.simulationEndTime = simulationEndTime;
}
/**
* Returns how much time the simulation took
*
* @return simulation runtime (in milliseconds)
*/
public long getSimulationRuntime() {
long time;
if (simulationEndTime == 0) {
time = System.currentTimeMillis() - simulationStartTime;
} else {
time = simulationEndTime - simulationStartTime;
}
return time;
}
public boolean checkAdditionalRequirementsWithIncomingVehilce(String vehicleId,
Set<String> passengerAdditionalRequirements) {
TestbedVehicle vehicle = vehicleStorage.getEntityById(vehicleId);
return vehicle.getVehicleEquipments().containsAll(passengerAdditionalRequirements);
}
public boolean isFree(String taxiDriverId) {
return taxiDriversFree.contains(taxiDriverId) && !taxiDriversBusy.contains(taxiDriverId);
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("TestbedModel - Taxis with passengers: ");
sb.append(taxiWithPassengersOnBoard);
sb.append("End locations: ");
sb.append(taxiWithEndOfTripPositions);
return sb.toString();
}
}