package cz.agents.agentpolis.darptestbed.simmodel.agent.data; import cz.agents.agentpolis.darptestbed.global.Utils; import cz.agents.agentpolis.darptestbed.simmodel.entity.vehicle.TestbedVehicle; import java.util.ArrayList; import java.util.List; /** * A list of start and target request nodes in order. This plan is easy to work * with during planning process. * <p/> * TODO the behavior when more passengers somehow share the same node (some get * in, some get off) hasn't been implemented yet * * @author Lukas Canda */ public class FlexiblePlan implements Cloneable { /** * A set of useful methods for searching paths, distances etc. */ protected final Utils utils; /** * The vehicle that will drive according to this plan */ public final TestbedVehicle vehicle; /** * The time at the beginning of the plan */ public long currentTime; /** * The taxi driver's initial node */ public long firstNode; /** * The node where the driver wants to go after finishing the plan */ public long lastNode; /** * Nodes of the plan, where passengers either get in or get off */ public List<PlanItem> planItems; public FlexiblePlan(Utils utils, TestbedVehicle vehicle, long currentTime, long firstNode) { this(utils, vehicle, currentTime, firstNode, -1); } public FlexiblePlan(Utils utils, TestbedVehicle vehicle, long currentTime, long firstNode, long lastNode) { this.utils = utils; this.vehicle = vehicle; this.currentTime = currentTime; this.planItems = new ArrayList<PlanItem>(); this.firstNode = firstNode; this.lastNode = lastNode; } /** * Removes the item from the specified index (other items will automatically * move back) * * @param index */ public void removeItem(int index) { if (index < 0 || getSize() == 0) { return; } PlanItem prevItem = null; PlanItem nextItem = null; long drivingTime = -1; long departTimeDiff = -1; // remove it physically this.planItems.remove(index); if (getSize() == 0 || getSize() == index) { return; } nextItem = this.planItems.get(index); // first item if (index == 0) { if (firstNode == -1) { departTimeDiff = nextItem.takeTime(nextItem.getArrivalTime() - currentTime); } else { drivingTime = utils.computeDrivingTime(firstNode, nextItem.getNode()); departTimeDiff = nextItem.takeTime(nextItem.getArrivalTime() - (currentTime + drivingTime)); } } else { prevItem = planItems.get(index - 1); drivingTime = utils.computeDrivingTime(prevItem.getNode(), nextItem.getNode()); departTimeDiff = nextItem.takeTime(nextItem.getArrivalTime() - (prevItem.getDepartureTime() + drivingTime)); } // recount arrival times in all following nodes for (int i = index + 1; i < this.getSize(); i++) { nextItem = this.planItems.get(i); departTimeDiff = nextItem.takeTime(departTimeDiff); } } /** * Get the passenger that gets in on the node from the specified index * * @param nodeIndex index in my list of plan items * @return id of the passenger who gets in (or null if nobody gets in) */ public String getBoardingPassenger(int nodeIndex) { if (!this.planItems.get(nodeIndex).isBoarding) { return null; } return this.planItems.get(nodeIndex).getPassengerId(); } /** * Get the passenger that gets in on the node from the specified index * * @param nodeIndex index in my list of plan items * @return id of the passenger who gets in (or null if nobody gets in) */ public String getLeavingPassenger(int nodeIndex) { if (this.planItems.get(nodeIndex).isBoarding) { return null; } return this.planItems.get(nodeIndex).getPassengerId(); } public long getNode(int nodeIndex) { return this.planItems.get(nodeIndex).getNode(); } /** * Removes all such nodes from the plan, that are visited before the given * node (including starting node) * * @param node node that will become the first node of the plan * @return false, if the given node is not found in the plan */ public boolean removeNodesBefore(long node) { // remove all nodes if (lastNode == node) { firstNode = -1; this.planItems = new ArrayList<PlanItem>(); return true; } // first, search for the node boolean found = false; for (PlanItem item : this.planItems) { if (item.getNode() == node) { found = true; } } if (!found) { return false; } // remove all nodes before the given node firstNode = -1; long nodeToDelete = this.planItems.get(0).getNode(); while (nodeToDelete != node) { removeItem(0); nodeToDelete = this.planItems.get(0).getNode(); } return true; } // /** // * Finds and removes the passenger's target node from the plan // * // * @param passengerId // * the passenger, from whom we want to remove target node // * @return true, if the passenger's target node has been found and removed // */ // public boolean removePassengerTarget(String passengerId) { // PlanItem item; // for (int i = 0; i < getSize(); i++) { // item = this.planItems.get(i); // if (!item.isBoarding && item.getPassengerId().equals(passengerId)) { // this.removeItem(i); // return true; // } // } // return false; // } /** * @return the number of nodes to visit by this plan (except by the special * initial and ending node) */ public int getSize() { return this.planItems.size(); } /** * @return either starting node, or, if there's no such node, return the * node represented by the first plan item */ public long getFirstNode() { if (this.firstNode != -1) { return this.firstNode; } if (getSize() > 0) { return this.planItems.get(0).getNode(); } return -1; } /** * @return the list of requests, that are fully contained in this plan (both * beginning and the end of the request) */ public List<Request> getRequests() { List<Request> beginningReqs = new ArrayList<Request>(); List<Request> confirmedReqs = new ArrayList<Request>(); for (PlanItem item : this.planItems) { if (item.isBoarding) { beginningReqs.add(item.request); } else if (beginningReqs.contains(item.request)) { confirmedReqs.add(item.request); } } return confirmedReqs; } /** * Finds out, when the passenger arrives to his target node according to the * plan * * @param passengerId the id of the passenger * @return the arrival time (in milliseconds), or -1, if the passenger * hasn't been found in the plan */ public long getArrivalTime(String passengerId) { for (PlanItem item : this.planItems) { if (!item.isBoarding && item.getPassengerId().equals(passengerId)) { return item.getArrivalTime(); } } return -1; } /** * @return the number of the last node of this plan */ public long getLastNode() { if (lastNode != -1) { return lastNode; } if (getSize() > 0) { return this.planItems.get(getSize() - 1).getNode(); } return -1; } public List<PlanItem> getPlanItems() { return planItems; } /** * @return the departure time from the last node of the plan */ public long getEndOfPlanTime() { if (getSize() == 0) { return currentTime; } PlanItem lastItem = this.planItems.get(getSize() - 1); if (lastNode != -1) { long drivingTime = utils.computeDrivingTime(lastItem.getNode(), lastNode); return lastItem.getDepartureTime() + drivingTime; } return lastItem.getDepartureTime(); } @Override public Object clone() throws CloneNotSupportedException { FlexiblePlan planClone = new FlexiblePlan(utils, vehicle, currentTime, firstNode, lastNode); for (int i = 0; i < getSize(); i++) { planClone.planItems.add((PlanItem) this.planItems.get(i).clone()); } return planClone; } // /** // * Remove both get in and get off nodes, that belong to the request given // * // * @param reqToRemove // * the request to be removed from the plan // * @return true, if the request has been found // */ // public boolean removeRequest(Request reqToRemove) { // int removedItems = 0; // for (int i = getSize() - 1; i >= 0; i--) { // if (this.planItems.get(i).request == reqToRemove) { // removeItem(i); // removedItems++; // if (removedItems == 2) { // break; // } // } // } // return removedItems > 0 ? true : false; // } }