package cz.agents.agentpolis.darptestbed.simmodel.agent.driver.logic;
import cz.agents.agentpolis.darptestbed.global.Utils;
import cz.agents.agentpolis.darptestbed.siminfrastructure.communication.protocol.GeneralMessageProtocol;
import cz.agents.agentpolis.darptestbed.siminfrastructure.communication.receiver.BaseReceiverVisitor;
import cz.agents.agentpolis.darptestbed.simmodel.agent.AgentLogic;
import cz.agents.agentpolis.darptestbed.simmodel.agent.data.FlexiblePlan;
import cz.agents.agentpolis.darptestbed.simmodel.agent.data.TripPlan;
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.ondemandtransport.siminfrastructure.communication.protocol.AMessageProtocol;
import cz.agents.agentpolis.siminfrastructure.planner.trip.Trip;
import cz.agents.agentpolis.simmodel.agent.activity.movement.DriveVehicleActivity;
import cz.agents.agentpolis.simmodel.agent.activity.movement.callback.DrivingFinishedActivityCallback;
import cz.agents.agentpolis.simmodel.environment.model.citymodel.transportnetwork.AllNetworkNodes;
import cz.agents.agentpolis.simmodel.environment.model.query.AgentPositionQuery;
import org.apache.log4j.Logger;
import java.util.HashSet;
import java.util.Set;
/**
* This class contains all methods, that are common for both centralized and
* decentralized version. Mainly, this applies for communication concerning
* notifying passenger of my arrival and also driving.
*
* @author Lukas Canda
*/
public abstract class DriverLogic<TMessageProtocol extends AMessageProtocol<? extends BaseReceiverVisitor>> extends
AgentLogic<TMessageProtocol>
implements DrivingFinishedActivityCallback {
private static final Logger LOGGER = Logger.getLogger(DriverLogic.class);
/**
* The taxi I'm driving
*/
private final TestbedVehicle vehicle;
/**
* Using this activity I can drive a vehicle
*/
private final DriveVehicleActivity drivingActivity;
/**
* My trips to be gone on
*/
private TripPlan tripPlan = null;
// TODO: get rid of special values!!!!! Getters should never return special, obscure values!!!!
/**
* The number of boarding passengers we're waiting for (if there's nobody to
* get in, lets set it to -1)
*/
private int numOfPassenToGetIn = -1;
/**
* The number of boarding passengers we're waiting for to get off (if there's nobody to
* get off, lets set it to -1)
*/
private int numOfPassenToGetOff = -1;
/**
* The next trip to be driven (while we're waiting for boarding passengers)
*/
private Trip tripToDrive = null;
/**
* Algorithms, that allow diversion, can use this property to re-plan the
* plan of a taxi on the way
*/
protected FlexiblePlan flexiblePlan = null;
private final Set<String> informedBoardingPassengersAtCurrentNode = new HashSet<>();
private final Set<String> informedDisembarkingPassengersAtCurrentNode = new HashSet<>();
private final Set<String> passengersOnBoard = new HashSet<>();
private long lastLoadingNode = -1;
public DriverLogic(String agentId, TMessageProtocol sender, GeneralMessageProtocol generalMessageProtocol,
TestbedModel serviceModel,
AgentPositionQuery positionQuery, AllNetworkNodes allNetworkNodes, Utils utils, TestbedVehicle vehicle,
DriveVehicleActivity drivingActivity) {
super(agentId, sender, generalMessageProtocol, serviceModel, positionQuery, utils);
this.vehicle = vehicle;
this.drivingActivity = drivingActivity;
}
/**
* @return the driver's current position node
*/
protected Long getCurrentPositionNode() {
return this.positionQuery.getCurrentPositionByNodeId(this.getAgentId());
}
/**
* @return the current number of passengers this driver has on board
*/
protected int getCurrentPassengersOnBoard() {
return taxiModel.getNumOfPassenOnBoard(this.getVehicle().getId());
}
/**
* @return the driver's vehicle instance
*/
protected TestbedVehicle getVehicle() {
return this.vehicle;
}
/**
* @return the driver's trip plan
*/
protected TripPlan getTripPlan() {
return this.tripPlan;
}
/**
* Set the trip plan of this driver
*
* @param newTripPlan TripPlan object that the driver should take
*/
protected void setTripPlan(TripPlan newTripPlan) {
this.tripPlan = newTripPlan;
if (lastLoadingNode != -1) {
if (lastLoadingNode == getCurrentPositionNode()) {
Long currentPos = null;
if (!informedBoardingPassengersAtCurrentNode.isEmpty()) {
currentPos = getCurrentPositionNode();
for (String passengerId : informedBoardingPassengersAtCurrentNode) {
tripPlan.removePassengerFromBoardingPassengersAtNode(passengerId, currentPos);
// LOGGER.debug("Removed from boarding: " + passengerId);
}
}
if (!informedDisembarkingPassengersAtCurrentNode.isEmpty()) {
if (currentPos == null)
currentPos = getCurrentPositionNode();
for (String passengerId : informedBoardingPassengersAtCurrentNode) {
tripPlan.removePassengerFromDisembarkingPassengersAtNode(passengerId, currentPos);
// LOGGER.debug("Removed from disembarking: " + passengerId);
}
}
} else {
informedBoardingPassengersAtCurrentNode.clear();
informedDisembarkingPassengersAtCurrentNode.clear();
// LOGGER.debug("Cleared informed - " + getVehicle().getId() + ": " +
// informedDisembarkingPassengersAtCurrentNode);
}
}
LOGGER.debug("Assigned to " + getAgentId() + " " + tripPlan);
}
/**
* Extend current trip plan of this driver by a new one
*
* @param newTripPlan TripPlan object that the driver should take
*/
protected void extendTripPlan(TripPlan newTripPlan) {
if (this.tripPlan != null) {
this.extendTripPlan(newTripPlan);
} else {
this.setTripPlan(newTripPlan);
}
}
/**
* @return the driver's vehicle drivingActivity
*/
protected DriveVehicleActivity getDrivingActivity() {
return this.drivingActivity;
}
/**
* After all passengers from the current node get in, the taxi can drive
* another part of the trip.
*
* @param passengerId id of the passenger that's just gotten in
*/
public void processPassengerGotInVehicle(String passengerId) {
numOfPassenToGetIn--;
if (numOfPassenToGetIn == 0 && numOfPassenToGetOff == 0) {
driveNextPartOfTripPlan();
}
}
/**
* After all passengers from the current node get off, the taxi can drive
* another part of the trip.
*
* @param passengerId id of the passenger that's just gotten off
*/
public void processPassengerGotOffVehicle(String passengerId) {
numOfPassenToGetOff--;
if (numOfPassenToGetOff == 0 && numOfPassenToGetIn == 0) {
driveNextPartOfTripPlan();
}
}
/**
* The passenger notifies the taxi driver of delay
*
* @param passengerId message sender
* @param departure true, if the delay was during departure, false if during
* arrival
* @param delay the delay in milliseconds
*/
public void processVehicleIsTooLate(String passengerId, boolean departure, long delay) {
// lets just skip the passenger
numOfPassenToGetIn--;
tripPlan.removeLatePickupPassengerFromTripPlan(passengerId, getVehicle().getId(), utils);
sendTaxiArrivedLateMessageToDispatching(passengerId);
if (numOfPassenToGetIn == 0) {
driveNextPartOfTripPlan();
}
}
protected abstract void sendTaxiArrivedLateMessageToDispatching(String passengerId);
/**
* This callback method is called after finishing driving a part of the trip
*/
@Override
public void finishedDriving() {
driveNextPartOfTripPlan();
}
/**
* After finishing a part of the trip, the driver gets on a node, where he
* can pick up passengers according to their boarding positions.
* <p/>
* After all the passengers finish boarding, he will continue driving.
*/
protected void driveNextPartOfTripPlan() {
if (lastLoadingNode != -1) {
if (lastLoadingNode != getCurrentPositionNode()) {
informedBoardingPassengersAtCurrentNode.clear();
informedDisembarkingPassengersAtCurrentNode.clear();
LOGGER.debug("Cleared informed - " + getVehicle().getId() + ": " +
informedDisembarkingPassengersAtCurrentNode);
}
}
// LOGGER.debug(getVehicle().getId() + ": " + "Location - " + getCurrentPositionNode() + " Drive TripPlan: " + tripPlan);
Long currentPos = this.getCurrentPositionNode();
PassengersInAndOutPair passengersToGetInAndOut =
tripPlan.getNodeWithBoardingAndDisembarkingPassengers(currentPos);
// if there are passengers waiting to get in on this node
if (passengersToGetInAndOut != null) {
lastLoadingNode = currentPos;
boolean waitOnBoarding = false;
if (numOfPassenToGetIn == -1 || !isEverybodyOnBoard()) {
processBoardingPassengers(currentPos, passengersToGetInAndOut);
if (!isEverybodyOnBoard())
waitOnBoarding = true;
}
if (numOfPassenToGetOff == -1 || !isEverybodyOffBoard()) {
if (processDisembarkingPassengers(currentPos, passengersToGetInAndOut))
return;
}
if (waitOnBoarding)
return;
// LOGGER.debug("Leaving with boarding: " + informedBoardingPassengersAtCurrentNode);
// LOGGER.debug("Leaving with disembarking: " + informedDisembarkingPassengersAtCurrentNode);
//
// if (tripPlan == null || !tripPlan.getTrips().hasTrip())
// tripFinished();
}
this.numOfPassenToGetIn = -1;
this.numOfPassenToGetOff = -1;
// LOGGER.debug(getVehicle().getId() + ": " + "Late = Location - " + getCurrentPositionNode() + " Drive " +
// "TripPlan: " + tripPlan);
// skip null trips (e.g. if there are two passengers at the same place)
do {
tripToDrive = tripPlan.getTrips().getAndRemoveFirstTrip();
} while (tripToDrive == null && tripPlan.getTrips().hasTrip());
if (tripToDrive == null) {
tripFinished();
} else {
taxiModel.setTaxiBusy(vehicle.getId());
// only some algorithms use these properties (here, we need to
// refresh them)
if (this.flexiblePlan != null) {
taxiModel.setEndOfTripPosition(vehicle.getId(), this.flexiblePlan.getLastNode());
this.flexiblePlan.removeNodesBefore(tripToDrive.showLastTripItem().tripPositionByNodeId);
}
LOGGER.debug("Drive: " + vehicle.getId() + " " + tripToDrive);
drivingActivity.drive(getAgentId(), vehicle, tripToDrive, this);
}
}
private boolean processDisembarkingPassengers(Long currentPos, PassengersInAndOutPair passengersToGetInAndOut) {
Set<String> passengersToGetOut = passengersToGetInAndOut.getOff();
this.numOfPassenToGetOff = passengersToGetOut.size();
LOGGER.debug("Processing off - driver: " + getVehicle().getId() + " " + tripPlan + " " +
this.numOfPassenToGetOff);
if (this.numOfPassenToGetOff > 0) {
for (String passengerId : passengersToGetOut) {
if (!passengersOnBoard.contains(passengerId)) {
informedDisembarkingPassengersAtCurrentNode.add(passengerId);
--numOfPassenToGetOff;
continue;
}
LOGGER.debug("Driver: DISEMBARK of " + passengerId + ", used " + getVehicle().getId() +
", driven by " + getAgentId() + " at " + currentPos + ". " +
passengersToGetOut);
tripPlan.removePassengerFromDisembarkingPassengersAtNode(passengerId, currentPos);
sendTaxiArrivedToDropOff(passengerId);
informedDisembarkingPassengersAtCurrentNode.add(passengerId);
passengersOnBoard.remove(passengerId);
}
return (this.numOfPassenToGetOff > 0);
} else {
return false;
}
}
private void processBoardingPassengers(Long currentPos, PassengersInAndOutPair passengersToGetInAndOut) {
Set<String> passengersToGetIn = passengersToGetInAndOut.getIn();
this.numOfPassenToGetIn = passengersToGetIn.size();
LOGGER.debug("Processing in - driver: " + getVehicle().getId() + " " + this.numOfPassenToGetIn);
for (String passengerId : passengersToGetIn) {
LOGGER.debug("Driver: PICKUP of " + passengerId + ", used " + getVehicle().getId() +
", driven by " + getAgentId() + " at " + currentPos + " - " +
passengersToGetIn.toString());
tripPlan.removePassengerFromBoardingPassengersAtNode(passengerId, currentPos);
sendTaxiArrivedToPickup(passengerId);
//
// LOGGER.debug("After PICKUP: " + passengersToGetIn.toString() + " " + tripPlan + " " +
// this.numOfPassenToGetIn);
informedBoardingPassengersAtCurrentNode.add(passengerId);
passengersOnBoard.add(passengerId);
}
}
protected Set<? extends String> getPassengersOnBoard() {
return passengersOnBoard;
}
/**
* Notify a passenger of arriving this taxi (so he can get in)
*
* @param passengerId the passenger to be notified
*/
protected abstract void sendTaxiArrivedToPickup(String passengerId);
/**
* Notify a passenger of arriving this taxi (so he can get off)
*
* @param passengerId the passenger to be notified
*/
protected abstract void sendTaxiArrivedToDropOff(String passengerId);
// TaxiArrivedToPassengerMessage
// sender.sendTaxiArrivedToPassenger(agentId, vehicle.getId(), passengerId);
// }
/**
* This method should be called after finishing the whole trip and having
* nothing else to do
*/
private void tripFinished() {
tripPlan = null;
flexiblePlan = null;
taxiModel.setTaxiFree(vehicle.getId());
LOGGER.debug(this.agentId + ": I've finished my trip at " + utils.toHoursAndMinutes(utils.getCurrentTime()));
}
/**
* @return true, if the driver currently has a non-empty trip plan
*/
protected boolean isOnTheWay() {
if (this.tripPlan == null || this.tripPlan.getTrips().numTrips() == 0) {
return false;
} else {
return true;
}
}
/**
* @return true, if the driver is currently busy for some reason (on a trip or communicating with passenger)
*/
protected boolean isBusy() {
if (!taxiModel.getTaxiDriversFree().contains(this.getAgentId())) {
return true;
} else {
return false;
}
}
/**
* Moves this taxi from list of free taxis into the list
* "busy". And also does the same with its driver.
*/
protected void setBusy() {
taxiModel.setTaxiBusy(this.getVehicle().getId());
}
/**
* @return true, if the driver is currently NOT busy for any reason (not on a trip or communicating with passenger)
*/
protected boolean isFree() {
return !isBusy();
}
/**
* Moves this taxi from list of busy taxis into the list
* "free". And also does the same with its driver.
*/
protected void setFree() {
taxiModel.setTaxiFree(this.getVehicle().getId());
}
/**
* @return true, if everybody who was supposed to, has gotten in
*/
protected boolean isEverybodyOnBoard() {
return this.numOfPassenToGetIn == 0 ? true : false;
}
/**
* @return true, if everybody who was supposed to, has gotten in
*/
protected boolean isEverybodyOffBoard() {
return this.numOfPassenToGetOff == 0 ? true : false;
}
public String toString() {
return getAgentId();
}
}