package cz.agents.agentpolis.darptestbed.global;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import cz.agents.agentpolis.darptestbed.global.data.DriverAndDistance;
import cz.agents.agentpolis.darptestbed.global.data.DriverAndDistanceComparator;
import cz.agents.agentpolis.darptestbed.siminfrastructure.logger.UtilsLogger;
import cz.agents.agentpolis.darptestbed.siminfrastructure.planner.TestbedPlanner;
import cz.agents.agentpolis.darptestbed.simmodel.agent.data.*;
import cz.agents.agentpolis.darptestbed.simmodel.agent.data.comparator.RequestLatestDepComparator;
import cz.agents.agentpolis.darptestbed.simmodel.agent.data.comparator.RequestLatestDepComparatorWithFrontloadedSubset;
import cz.agents.agentpolis.darptestbed.simmodel.agent.data.generator.PassengersInAndOutPair;
import cz.agents.agentpolis.darptestbed.simmodel.entity.vehicle.TestbedVehicle;
import cz.agents.agentpolis.darptestbed.simmodel.environment.model.TestbedModel;
import cz.agents.agentpolis.darptestbed.simulator.initializator.osm.NodeExtendedFunction;
import cz.agents.agentpolis.siminfrastructure.planner.TripPlannerException;
import cz.agents.agentpolis.siminfrastructure.planner.trip.Trip;
import cz.agents.agentpolis.siminfrastructure.planner.trip.TripItem;
import cz.agents.agentpolis.siminfrastructure.planner.trip.Trips;
import cz.agents.agentpolis.simmodel.environment.model.citymodel.transportnetwork.AllNetworkNodes;
import cz.agents.agentpolis.simmodel.environment.model.citymodel.transportnetwork.elemets.Node;
import cz.agents.agentpolis.simmodel.environment.model.query.AgentPositionQuery;
import cz.agents.alite.common.event.EventProcessor;
import org.apache.log4j.Logger;
import java.util.*;
/**
* A set of independent methods, that are used throughout the whole application.
* This class includes an insertion algorithm for planning trips.
*
* @author Lukas Canda
*/
@Singleton
public class Utils {
private static final Logger LOGGER = Logger.getLogger(Utils.class);
/**
* All nodes in the map.
*/
protected final AllNetworkNodes allNodes;
/**
* A planner, which computes the shortest path between two nodes
*/
protected final TestbedPlanner pathPlanner;
/**
* Returns an agent's current position.
*/
protected final AgentPositionQuery positionQuery;
/**
* A storage to save all data concerning taxi drivers and passengers
*/
protected final TestbedModel taxiModel;
/**
* The event processor is used here just to figure out the current
* simulation time
*/
protected final EventProcessor eventProcessor;
/**
* This map is useful, when a passenger is looking for a few closest taxis
* format: Map< passengerId , TaxiAndDistance - driving time to the taxi >
*/
protected Map<String, DriverAndDistance[]> passenAndDistMap = null;
private final NodeExtendedFunction nodeExtendedFunction;
// /**
// * True, if the previous map was recently refreshed
// */
// private boolean mapRefreshedRecently = false;
/**
* The previous map computes with positions at the end of taxi trips
*/
private boolean useEndOfTripPositions = false;
private final UtilsLogger utilsLogger;
@Inject
public Utils(AllNetworkNodes allNodes, TestbedPlanner pathPlanner, AgentPositionQuery positionQuery,
TestbedModel taxiModel, EventProcessor eventProcessor, NodeExtendedFunction nodeExtendedFunction,
UtilsLogger utilsLogger) {
this.allNodes = allNodes;
this.pathPlanner = pathPlanner;
this.positionQuery = positionQuery;
this.taxiModel = taxiModel;
this.eventProcessor = eventProcessor;
this.nodeExtendedFunction = nodeExtendedFunction;
this.utilsLogger = utilsLogger;
}
/**
* Computes the length of a trip (node by node).
*
* @param trip trip to be measured
* @return trip length in meters
*/
private Double computeTripLength(Trip trip) {
Double len = 0.0;
TripItem item = trip.getAndRemoveFirstTripItem();
Node previosNode = allNodes.getAllNetworkNodes().get((item.tripPositionByNodeId));
item = trip.getAndRemoveFirstTripItem();
while (item != null) {
Node currenctNode = allNodes.getAllNetworkNodes().get((item.tripPositionByNodeId));
len += nodeExtendedFunction.computeDistanceBetweenNodes(previosNode.getId(), currenctNode.getId());
previosNode = currenctNode;
item = trip.getAndRemoveFirstTripItem();
}
return len;
}
/**
* Computes the distance between two nodes
*
* @param node1 number of the first node
* @param node2 number of the second node
* @return distance between the nodes in meters
*/
public Double computeDistance(long node1, long node2) {
if (node1 == node2) {
return 0.0;
}
Trips trips = null;
try {
trips = pathPlanner.findTrip(null, node1, node2);
} catch (TripPlannerException e) {
e.printStackTrace();
}
return computeTripLength(trips.getAndRemoveFirstTrip());
}
/**
* Computes the distance between the current positions of two agents
*
* @param agentId1 id of the first agent
* @param agentId2 id of the second agent
* @return distance between the agents in meters
*/
public Double computeDistance(String agentId1, String agentId2) {
long pos1 = positionQuery.getCurrentPositionByNodeId(agentId1);
long pos2 = positionQuery.getCurrentPositionByNodeId(agentId2);
return computeDistance(pos1, pos2);
}
/**
* Computes how much time it takes to drive between the current positions of
* two agents (using the given driving velocity)
*
* @param agentId1 id of the first agent
* @param agentId2 id of the second agent
* @param velocityKmph driving velocity (in kilometers per hour)
* @return time to drive (in milliseconds)
*/
public long computeDrivingTime(String agentId1, String agentId2, double velocityKmph) {
Double distance = computeDistance(agentId1, agentId2);
return (long) (3600 * distance / velocityKmph);
}
public long computeDrivingTime(String agentId1, String agentId2) {
return computeDrivingTime(agentId1, agentId2, GlobalParams.getVelocityInKmph());
}
/**
* Computes how much time it takes to drive between two nodes
*
* @param node1 number of the first node
* @param node2 number of the second node
* @param velocityKmph driving velocity (in kilometers per hour)
* @return time to drive (in milliseconds)
*/
public Long computeDrivingTime(long node1, long node2, double velocityKmph) {
Double distance = computeDistance(node1, node2);
if (distance != null && !distance.isNaN() && !distance.isInfinite())
return (long) (3600 * distance / velocityKmph);
else
return null;
}
public Long computeDrivingTime(long node1, long node2) {
return computeDrivingTime(node1, node2, GlobalParams.getVelocityInKmph());
}
/**
* Returns the given amount of time as a String in HH:MM format (the number
* of minutes is rounded up).
*
* @param millis the amount of time in milliseconds
* @return string, e.g. 9:52
*/
public String toHoursAndMinutes(long millis) {
// round up the number of minutes
int totalMinutes = (int) Math.ceil(millis / (double) 60000);
int hours = totalMinutes / 60;
int minutes = totalMinutes % 60;
if (minutes < 10) {
return hours + ":0" + minutes;
}
return hours + ":" + minutes;
}
/**
* Returns the given amount of time in minutes
*
* @param millis the amount of time in milliseconds
* @return number of minutes
*/
public int toMinutes(long millis) {
// round up the number of minutes
int minutes = (int) Math.round(millis / (double) 60000);
return minutes;
}
/**
* Returns the given amount of time in seconds
*
* @param millis the amount of time in milliseconds
* @return number of seconds
*/
public int toSeconds(long millis) {
// round up the number of seconds
int seconds = (int) Math.round(millis / (double) 1000);
return seconds;
}
@Deprecated
/**
* Map of distances from all passengers to all taxi drivers, sorted by the
* distance
*
* @param driversIds
* taxi drivers we want to measure distances to
* @param endOfTripPositions
* true = taxi positions will be replaced by the positions at the
* end of taxis trips
* @return map of distances
*/
public Map<String, DriverAndDistance[]> getPassenAndDistMap(List<String> driversIds, boolean useEndOfTripPositions) {
if (driversIds == null || driversIds.size() == 0) {
return null;
}
// mapRefreshedRecently = false;
// check out changes in required type of counting with positions
if (this.useEndOfTripPositions != useEndOfTripPositions) {
passenAndDistMap = null;
}
this.useEndOfTripPositions = useEndOfTripPositions;
// check out changes in required drivers (compared to the current map)
if (passenAndDistMap != null) {
DriverAndDistance[] taxADistArr = passenAndDistMap.get(taxiModel.getPassengers().get(0));
if (taxADistArr.length == driversIds.size()) {
for (DriverAndDistance taxADist : taxADistArr) {
if (!driversIds.contains(taxADist.getTaxiDriverId())) {
// a difference has been found, so lets refresh the map
passenAndDistMap = null;
break;
}
}
} else {
passenAndDistMap = null;
}
}
if (passenAndDistMap != null) {
return passenAndDistMap;
}
// build the whole map
passenAndDistMap = new HashMap<String, DriverAndDistance[]>();
List<String> passengers = this.taxiModel.getPassengers();
DriverAndDistance[] taxiAndDistArr;
long drivingTime;
String driverId;
for (String passId : passengers) {
taxiAndDistArr = new DriverAndDistance[driversIds.size()];
// compute distances to all taxis
for (int i = 0; i < driversIds.size(); i++) {
driverId = driversIds.get(i);
long passPos = positionQuery.getCurrentPositionByNodeId(passId);
long driverPos = positionQuery.getCurrentPositionByNodeId(driverId);
if (useEndOfTripPositions) {
driverPos = taxiModel.getEndOfTripPosition(taxiModel.getVehicleId(driverId));
}
drivingTime = this.computeDrivingTime(passPos, driverPos);
taxiAndDistArr[i] = new DriverAndDistance(driverId, drivingTime);
}
// sort them by the distance
Arrays.sort(taxiAndDistArr, new DriverAndDistanceComparator());
passenAndDistMap.put(passId, taxiAndDistArr);
}
return passenAndDistMap;
}
public Map<String, DriverAndDistance[]> getPassenAndDistMap(List<Request> requests, List<String> driversIds,
boolean useEndOfTripPositions) {
if (driversIds == null || driversIds.size() == 0) {
return null;
}
this.useEndOfTripPositions = useEndOfTripPositions;
for (Request request : requests) {
passenAndDistMap.put(request.getPassengerId(),
getDistMapForPassenger(request.getFromNode(), driversIds, useEndOfTripPositions));
}
return passenAndDistMap;
}
public DriverAndDistance[] getDistMapForPassenger(long passengerPositionByNodeId, List<String> driversIds,
boolean useEndOfTripPositions) {
if (driversIds == null || driversIds.size() == 0) {
return null;
}
this.useEndOfTripPositions = useEndOfTripPositions;
DriverAndDistance[] taxiAndDistArr = new DriverAndDistance[driversIds.size()];
// compute distances to all taxis
for (int i = 0; i < driversIds.size(); i++) {
String driverId = driversIds.get(i);
long driverPos = positionQuery.getCurrentPositionByNodeId(driverId);
if (useEndOfTripPositions) {
driverPos = taxiModel.getEndOfTripPosition(taxiModel.getVehicleId(driverId));
}
long drivingTime = this.computeDrivingTime(passengerPositionByNodeId, driverPos);
taxiAndDistArr[i] = new DriverAndDistance(driverId, drivingTime);
}
// sort them by the distance
Arrays.sort(taxiAndDistArr, new DriverAndDistanceComparator());
return taxiAndDistArr;
}
/**
* Transfer the plan changeable into a trip plan that is ready for a taxi
* driver
*
* @param flexiblePlan plan changeable (from planning algorithm)
* @return trip plan (for taxi driver)
*/
public TripPlan makeTripPlan(FlexiblePlan flexiblePlan) {
if (flexiblePlan == null || flexiblePlan.getSize() == 0) {
return null;
}
Trips trips = makeTrips(flexiblePlan);
Map<Long, PassengersInAndOutPair> mapOfBoardingAndLeavingPassengers = new HashMap<>();
String tmpPassen = null;
PassengersInAndOutPair tmpSet;
// make the map of boarding passengers
for (int i = 0; i < flexiblePlan.getSize(); i++) {
tmpSet = mapOfBoardingAndLeavingPassengers.get(i);
tmpPassen = flexiblePlan.getBoardingPassenger(i);
if (tmpPassen != null) {
if (tmpSet == null)
tmpSet = new PassengersInAndOutPair();
tmpSet.getIn().add(tmpPassen);
mapOfBoardingAndLeavingPassengers.put(flexiblePlan.getNode(i), tmpSet);
}
tmpPassen = flexiblePlan.getLeavingPassenger(i);
if (tmpPassen != null) {
if (tmpSet == null)
tmpSet = new PassengersInAndOutPair();
mapOfBoardingAndLeavingPassengers.put(flexiblePlan.getNode(i), tmpSet);
tmpSet.getOff().add(tmpPassen);
}
}
return new TripPlan(trips, mapOfBoardingAndLeavingPassengers, flexiblePlan);
}
/**
* Makes trips out of this plan
*
* @return trips
*/
public Trips makeTrips(FlexiblePlan flexiblePlan) {
if (flexiblePlan.getSize() == 0) {
return null;
}
String vehicleId = flexiblePlan.vehicle.getId();
Trips trips = new Trips();
Trips tmpTrips;
try {
// first node
if (flexiblePlan.firstNode >= 0) {
trips = pathPlanner
.findTrip(vehicleId, flexiblePlan.firstNode, flexiblePlan.planItems.get(0).getNode());
}
// all the other nodes
for (int i = 1; i < flexiblePlan.getSize(); i++) {
tmpTrips = pathPlanner.findTrip(vehicleId, flexiblePlan.planItems.get(i - 1).getNode(),
flexiblePlan.planItems.get(i).getNode());
trips.addEndCurrentTrips(tmpTrips.getAndRemoveFirstTrip());
}
// last node
if (flexiblePlan.lastNode >= 0) {
tmpTrips = pathPlanner.findTrip(vehicleId, flexiblePlan.planItems.get(flexiblePlan.getSize() - 1)
.getNode(), flexiblePlan.lastNode);
trips.addEndCurrentTrips(tmpTrips.getAndRemoveFirstTrip());
}
} catch (TripPlannerException e) {
e.printStackTrace();
// if it's run from a runner, we need to skip this attempt
System.exit(0);
}
return trips;
}
/**
* @return current simulation time in milliseconds
*/
public long getCurrentTime() {
return eventProcessor.getCurrentTime();
}
// ///////////////////// THE INSERTION ALGORITHM ///////////////////////
/**
* Make a trip plan to serve as many requests as possible (with regard to
* their time windows).
* <p/>
* It uses an insertion algorithm, that generates various possible sequences
* of get in and get off nodes and finds a local optimum (that is a plan
* that serves the most requests and takes the shortest time)
*
* @param listOfReqs requests to be served
* @param vehicle vehicle to drive according to the result plan
* @param plan if not null, this algorithm will try to add requests into this
* plan
* @return plan, how to serve as many requests as possible
*/
public FlexiblePlan planTrips(List<Request> listOfReqs, TestbedVehicle vehicle, FlexiblePlan plan,
boolean refreshCurrTime) {
listOfReqs = new ArrayList<Request>(listOfReqs);
String taxiDriverId = taxiModel.getTaxiDriverId(vehicle.getId());
// making a new plan
if (plan == null) {
long currPos = this.positionQuery.getCurrentPositionByNodeId(taxiDriverId);
if (GlobalParams.isDriverReturnsBack()) {
plan = new FlexiblePlan(this, vehicle, getCurrentTime(), currPos, currPos);
} else {
plan = new FlexiblePlan(this, vehicle, getCurrentTime(), currPos);
}
} else if (refreshCurrTime) {
// this usually needs to be done before changing an old plan
// (updating the start time according to when the driver can make
// it)
refreshPlannerTime(plan, taxiDriverId);
}
// delete requests which cannot be driven to on time
for (int i = listOfReqs.size() - 1; i >= 0; i--) {
Request req = listOfReqs.get(i);
long timeToDrive = computeDrivingTime(taxiDriverId, req.getPassengerId());
if (req.getTimeWindow() != null
&& getCurrentTime() + timeToDrive > req.getTimeWindow().getLatestDeparture()) {
listOfReqs.remove(i);
}
}
// put the most in hurry requests into the front
Collections.sort(listOfReqs, new RequestLatestDepComparator());
PlanItem itemGetIn;
PlanItem itemGetOff;
long minDepartTime;
int minDTimeGetInIndex = -1;
int minDTimeGetOffIndex = -1;
long tmpDepartTime;
// this property is never used, but it may be useful for logging
List<Request> listOfReqsInPlan = new ArrayList<Request>();
// try to place all requests
for (int i = 0; i < listOfReqs.size(); i++) {
minDepartTime = Long.MAX_VALUE;
itemGetIn = new PlanItem(listOfReqs.get(i), true);
itemGetOff = new PlanItem(listOfReqs.get(i), false);
// first, lets place the node where the passenger gets in
for (int j = 0; j <= plan.getSize(); j++) {
tmpDepartTime = insertItem(j, itemGetIn, true, plan);
// if the plan hasn't been broken by that
if (tmpDepartTime >= 0) {
// lets try to place the get off node
for (int k = j + 1; k <= plan.getSize(); k++) {
tmpDepartTime = insertItem(k, itemGetOff, false, plan);
// and remember the best combination
if (tmpDepartTime >= 0 && tmpDepartTime < minDepartTime) {
minDepartTime = tmpDepartTime;
minDTimeGetInIndex = j;
minDTimeGetOffIndex = k;
}
}
// change it back
plan.removeItem(j);
}
}
// if we've found at least one possible placement, put it on the
// best
if (minDepartTime < Long.MAX_VALUE) {
insertItem(minDTimeGetInIndex, itemGetIn, true, plan);
insertItem(minDTimeGetOffIndex, itemGetOff, true, plan);
// let's not forget checking the capacity
if (!checkOutCapacity(plan)) {
plan.removeItem(minDTimeGetOffIndex);
plan.removeItem(minDTimeGetInIndex);
} else {
listOfReqsInPlan.add(listOfReqs.get(i));
}
} else {
// here, you can do something if there is a request that we
// cannot make
// on time (for example, try to exchange it for another one from
// the beginning)
}
}
return plan;
}
/**
* Make a trip plan to serve as many requests as possible (with regard to
* their time windows).
* <p/>
* It uses an insertion algorithm, that generates various possible sequences
* of get in and get off nodes and finds a local optimum (that is a plan
* that serves the most requests and takes the shortest time)
*
* @param listOfReqs requests to be served
* @param requestsInExecution requests that already have passengers on board
* @param vehicle vehicle to drive according to the result plan
* @param plan if not null, this algorithm will try to add requests into this
* plan
* @return plan, how to serve as many requests as possible
*/
public FlexiblePlan planTrips(List<Request> listOfReqs, TestbedVehicle vehicle,
Set<Request> requestsInExecution, FlexiblePlan plan,
boolean refreshCurrTime) {
listOfReqs = new ArrayList<Request>(listOfReqs);
String taxiDriverId = taxiModel.getTaxiDriverId(vehicle.getId());
// making a new plan
if (plan == null) {
long currPos = this.positionQuery.getCurrentPositionByNodeId(taxiDriverId);
if (GlobalParams.isDriverReturnsBack()) {
plan = new FlexiblePlan(this, vehicle, getCurrentTime(), currPos, currPos);
} else {
plan = new FlexiblePlan(this, vehicle, getCurrentTime(), currPos);
}
} else if (refreshCurrTime) {
// this usually needs to be done before changing an old plan
// (updating the start time according to when the driver can make
// it)
refreshPlannerTime(plan, taxiDriverId);
}
// delete requests which cannot be driven to on time
for (int i = listOfReqs.size() - 1; i >= 0; i--) {
Request req = listOfReqs.get(i);
if (requestsInExecution.contains(req)) {
continue;
}
long timeToDrive = computeDrivingTime(taxiDriverId, req.getPassengerId());
if (req.getTimeWindow() != null
&& getCurrentTime() + timeToDrive > req.getTimeWindow().getLatestDeparture()) {
return null;
// listOfReqs.remove(i);
}
}
// put the most in hurry requests into the front
Collections.sort(listOfReqs, new RequestLatestDepComparatorWithFrontloadedSubset(requestsInExecution));
PlanItem itemGetIn;
PlanItem itemGetOff;
long minDepartTime;
int minDTimeGetInIndex = -1;
int minDTimeGetOffIndex = -1;
long tmpDepartTime;
// this property is never used, but it may be useful for logging
List<Request> listOfReqsInPlan = new ArrayList<Request>();
// try to place all requests
for (Request request : listOfReqs) {
minDepartTime = Long.MAX_VALUE;
if (!requestsInExecution.contains(request)) {
itemGetIn = new PlanItem(request, true);
itemGetOff = new PlanItem(request, false);
// first, lets place the node where the passenger gets in
for (int j = 0; j <= plan.getSize(); j++) {
tmpDepartTime = insertItem(j, itemGetIn, true, plan);
// if the plan hasn't been broken by that
if (tmpDepartTime >= 0) {
// lets try to place the get off node
for (int k = j + 1; k <= plan.getSize(); k++) {
// note the really flag
tmpDepartTime = insertItem(k, itemGetOff, false, plan);
// and remember the best combination
if (tmpDepartTime >= 0 && tmpDepartTime < minDepartTime) {
minDepartTime = tmpDepartTime;
minDTimeGetInIndex = j;
minDTimeGetOffIndex = k;
}
}
// change it back
plan.removeItem(j);
}
}
// if we've found at least one possible placement, put it on the
// best
if (minDepartTime < Long.MAX_VALUE) {
insertItem(minDTimeGetInIndex, itemGetIn, true, plan);
insertItem(minDTimeGetOffIndex, itemGetOff, true, plan);
// let's not forget checking the capacity
if (!checkOutCapacity(plan)) {
plan.removeItem(minDTimeGetOffIndex);
plan.removeItem(minDTimeGetInIndex);
} else {
listOfReqsInPlan.add(request);
}
} else {
// here, you can do something if there is a request that we
// cannot make
// on time (for example, try to exchange it for another one from
// the beginning)
}
} else {
itemGetOff = new PlanItem(request, false);
// lets try to place the get off node
for (int k = 0; k <= plan.getSize(); k++) {
// note the really flag
tmpDepartTime = insertItem(k, itemGetOff, false, plan);
// and remember the best combination
if (tmpDepartTime >= 0 && tmpDepartTime < minDepartTime) {
minDepartTime = tmpDepartTime;
minDTimeGetOffIndex = k;
}
}
// if we've found at least one possible placement, put it on the
// best
if (minDepartTime < Long.MAX_VALUE) {
insertItem(minDTimeGetOffIndex, itemGetOff, true, plan);
// let's not forget checking the capacity
if (!checkOutCapacity(plan)) {
plan.removeItem(minDTimeGetOffIndex);
} else {
listOfReqsInPlan.add(request);
}
}
}
}
return plan;
}
// ------- Insert
/**
* Inserts the item into the specified index (other items will automatically
* move further)
*
* @param index
* @param item (with arrival time not set)
* @param really true, if the item should REALLY be inserted into the plan
* (false means that it just computes the difference it would make)
* @return -1, if the item causes itself, or some other item inconsistency
* (the driver would arrive too late for it); if it's okay, it
* returns the departure time at the end of the plan
*/
public long insertItem(int index, PlanItem item, boolean really, FlexiblePlan flexiblePlan) {
// it only calls a different method, because we first need to check,
// if the item is possible to insert
long endDepartTime = insertItemBody(index, item, false, flexiblePlan);
if (really && endDepartTime >= 0) {
endDepartTime = insertItemBody(index, item, true, flexiblePlan);
}
return endDepartTime;
}
/**
* Inserts the item to the end of the plan
*
* @param item (with arrival time not set)
* @param really true, if the item should REALLY be inserted into the plan
* (false means that it just computes the difference it would make)
* @return -1, if the item causes itself, or some other item inconsistency
* (the driver would arrive too late for it); if it's okay, it
* returns the departure time at the end of the plan
*/
public long insertItem(PlanItem item, boolean really, FlexiblePlan flexiblePlan) {
return insertItem(flexiblePlan.getSize(), item, really, flexiblePlan);
}
/**
* The body of the previous method...
*/
protected long insertItemBody(int index, PlanItem item, boolean really, FlexiblePlan flexiblePlan) {
// wrong values
if (index < 0 || item == null) {
return -1;
}
// checking first item condition
if (isFixedFirst(flexiblePlan) && index == 0) {
return -1;
}
PlanItem prevItem = null;
PlanItem nextItem = null;
long drivingTime = -1;
long departTime = -1;
long departTimeDiff = -1;
// compute the arrival time from the previous node
if (index == 0) {
if (flexiblePlan.firstNode == -1) {
departTime = item.setArrivalTime(flexiblePlan.currentTime);
} else {
drivingTime = computeDrivingTime(flexiblePlan.firstNode, item.getNode());
departTime = item.setArrivalTime(flexiblePlan.currentTime + drivingTime);
}
} else {
prevItem = flexiblePlan.planItems.get(index - 1);
drivingTime = computeDrivingTime(prevItem.getNode(), item.getNode());
departTime = item.setArrivalTime(prevItem.getDepartureTime() + drivingTime);
}
if (departTime == -1) {
return -1;
}
// compute the departure time difference caused in all following nodes
if (index < flexiblePlan.getSize()) {
nextItem = flexiblePlan.planItems.get(index);
drivingTime = computeDrivingTime(item.getNode(), nextItem.getNode());
long arrivalTimeDiff = (departTime + drivingTime) - nextItem.getArrivalTime();
if (really) {
departTimeDiff = nextItem.addTime(arrivalTimeDiff);
} else {
departTimeDiff = nextItem.computeDepartureDifference(arrivalTimeDiff);
}
for (int i = index + 1; i < flexiblePlan.getSize(); i++) {
// the driver would arrive too late to a node
if (departTimeDiff == -1) {
return -1;
}
nextItem = flexiblePlan.planItems.get(i);
if (really) {
departTimeDiff = nextItem.addTime(departTimeDiff);
} else {
departTimeDiff = nextItem.computeDepartureDifference(departTimeDiff);
}
}
if (departTimeDiff == -1) {
return -1;
}
}
// compute the arrival time into the last node (the result)
long retVal = -1;
if (flexiblePlan.lastNode == -1) {
// if inserting the last node (any timeDiff hasn't been computed)
if (index == flexiblePlan.getSize()) {
retVal = item.getDepartureTime();
} else {
retVal = nextItem.getDepartureTime() + departTimeDiff;
}
} else {
if (index == flexiblePlan.getSize()) {
retVal = item.getDepartureTime() + computeDrivingTime(item.getNode(), flexiblePlan.lastNode);
} else {
retVal = nextItem.getDepartureTime() + departTimeDiff
+ computeDrivingTime(nextItem.getNode(), flexiblePlan.lastNode);
}
}
if (really) {
flexiblePlan.planItems.add(index, item);
}
return retVal;
}
/**
* @return true, if the first PlanItem has to stay first (we mustn't insert
* anything into the front of it)
*/
public boolean isFixedFirst(FlexiblePlan flexiblePlan) {
if (flexiblePlan.firstNode == -1 && flexiblePlan.getSize() > 0) {
return true;
}
return false;
}
// ------- Insert
/**
* Checks out, if the capacity of the vehicle hasn't been exceeded in any
* node of the plan
*
* @return true, if the capacity hasn't been exceeded
*/
public boolean checkOutCapacity(FlexiblePlan flexiblePlan) {
int currentOnBoard = taxiModel.getNumOfPassenOnBoard(flexiblePlan.vehicle.getId());
if (flexiblePlan.getSize() == 0) {
return true;
}
// the driver's seat is taken
int capacityLeft = flexiblePlan.vehicle.getCapacity() - 1 - currentOnBoard;
for (int i = 0; i < flexiblePlan.getSize(); i++) {
if (flexiblePlan.getPlanItems().get(i).isBoarding) {
capacityLeft--;
} else {
capacityLeft++;
}
if (capacityLeft < 0) {
return false;
}
}
return true;
}
public FlexiblePlan planTrips(List<Request> listOfReqs, TestbedVehicle vehicle, Set<Request> requestsInExecution) {
return planTrips(listOfReqs, vehicle, requestsInExecution, null, true);
}
public FlexiblePlan planTrips(List<Request> listOfReqs, TestbedVehicle vehicle) {
return planTrips(listOfReqs, vehicle, null, true);
}
/**
* Updates the time, from which the plan begins. Sets it up this way: the
* real current simulation time + time, that the driver needs to get to the
* first node of the plan
*
* @param plan the plan to be refreshed
* @param taxiDriverId the driver who drives according to this plan
* @return true, if the time was successfully updated, false if it would
* destroy the plan
*/
protected void refreshPlannerTime(FlexiblePlan plan, String taxiDriverId) {
long currentTime = getCurrentTime();
long firstNode = plan.getFirstNode();
long currPos = this.positionQuery.getCurrentPositionByNodeId(taxiDriverId);
long drivingTime = computeDrivingTime(currPos, firstNode);
updateCurrentTime(currentTime + drivingTime, plan);
}
/**
* Change the current time and refresh all timings according to it (this
* method is used for re-planning)
*
* @param currentTime time at the beginning of the plan
* @return true, if the time was successfully updated, false if it would
* destroy the plan
*/
public void updateCurrentTime(long currentTime, FlexiblePlan flexiblePlan) {
// add all plan items again (to refresh their time)
List<PlanItem> planItemsOld = flexiblePlan.planItems;
flexiblePlan.planItems = new ArrayList<PlanItem>();
/*
* note - it is possible, that the updated plan will be shorter, because
* some requests may not fit into the new timing
*/
for (int i = 0; i < planItemsOld.size(); i++) {
PlanItem item = planItemsOld.get(i);
item.resetArrivalTime();
insertItem(item, true, flexiblePlan);
}
}
public void logAlgRealTime(long realTime) {
utilsLogger.logAlgRealTime(realTime);
}
/**
* Uses the AgentPolis path planner to find a trip from origin to destination node, using a given vehicle.
*
* @param vehicleId id of a vehicle to be used
* @param originNodeId id of trip origin node
* @param destinationNodeId id of trip destination node
* @return single Trip object leading from origin to destination with a given vehicle, or null if not found.
*/
public Trip planTrip(String vehicleId, Long originNodeId, Long destinationNodeId) {
try {
return pathPlanner.findTrip(vehicleId, originNodeId, destinationNodeId).getAndRemoveFirstTrip();
} catch (TripPlannerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}