All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rinde.sim.pdptw.common.PDPRoadModel Maven / Gradle / Ivy

The newest version!
package rinde.sim.pdptw.common;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Maps.newHashMap;

import java.util.Map;
import java.util.Queue;

import javax.annotation.Nullable;

import org.apache.commons.lang3.builder.EqualsBuilder;

import rinde.sim.core.TimeLapse;
import rinde.sim.core.graph.Point;
import rinde.sim.core.model.ModelProvider;
import rinde.sim.core.model.ModelReceiver;
import rinde.sim.core.model.pdp.PDPModel;
import rinde.sim.core.model.pdp.PDPModel.ParcelState;
import rinde.sim.core.model.pdp.Parcel;
import rinde.sim.core.model.road.AbstractRoadModel;
import rinde.sim.core.model.road.ForwardingRoadModel;
import rinde.sim.core.model.road.MoveProgress;
import rinde.sim.core.model.road.MovingRoadUser;
import rinde.sim.core.model.road.RoadUser;

import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;

/**
 * A decorator for {@link AbstractRoadModel} which provides a more convenient
 * API for PDP problems and it can control whether vehicles are allowed to
 * divert.
 * 

* Pickup and delivery problem convenience API
* This model allows the following: {@code * moveTo(v1,p1,..) * pickup(v1,p1,..) * moveTo(v1,p1,..) * deliver(v1,p1,..) * } This means that you only need the reference to the parcel which you are * interested in, depending on its state the vehicle will automatically move to * the pickup location or the delivery location. *

* Diversion
* This model can optionally disallow vehicle diversion. Vehicle diversion is * defined as a vehicle changing its destination service location before it has * completed servicing that service location. More concretely if a vehicle * v1 is moving to the pickup location of parcel p1 * but then changes its destination to the pickup location of parcel * p2 we say that vehicle v1 has diverted. * * @author Rinde van Lon */ public class PDPRoadModel extends ForwardingRoadModel implements ModelReceiver { final Map destinations; final Multimap destinationHistory; final boolean allowDiversion; Optional pdpModel; /** * Decorates the {@link AbstractRoadModel} * @param rm The road model that is being decorated by this model. * @param allowVehicleDiversion Should the model allow vehicle diversion or * not. See {@link PDPRoadModel} for more information about * diversion. */ public PDPRoadModel(AbstractRoadModel rm, boolean allowVehicleDiversion) { super(rm); allowDiversion = allowVehicleDiversion; destinations = newHashMap(); // does not allow duplicates: WE NEED THIS destinationHistory = LinkedHashMultimap.create(); pdpModel = Optional.absent(); } /** * @return true when diversion is allowed, false * otherwise. See {@link PDPRoadModel} for more information about * diversion. */ public boolean isVehicleDiversionAllowed() { return allowDiversion; } private void checkType(RoadUser ru) { checkArgument( ru instanceof DefaultVehicle || ru instanceof DefaultDepot || ru instanceof DefaultParcel, "This RoadModel only allows instances of DefaultVehicle, DefaultDepot and DefaultParcel."); } @Override public boolean equalPosition(RoadUser obj1, RoadUser obj2) { return getParcelPos(obj1).equals(getParcelPos(obj2)); } Point getParcelPos(RoadUser obj) { if (!containsObject(obj) && obj instanceof DefaultParcel) { final ParcelState state = pdpModel.get().getParcelState( (DefaultParcel) obj); checkArgument( state == ParcelState.IN_CARGO, "Can only move to parcels which are either on the map or in cargo, state is %s, obj is %s.", state, obj); return ((DefaultParcel) obj).getDestination(); } return getPosition(obj); } @Override public void addObjectAt(RoadUser newObj, Point pos) { checkType(newObj); delegate.addObjectAt(newObj, pos); } @Override public void addObjectAtSamePosition(RoadUser newObj, RoadUser existingObj) { checkType(newObj); delegate.addObjectAtSamePosition(newObj, existingObj); } private boolean isAlreadyServiced(DestType type, RoadUser ru) { final PDPModel pm = pdpModel.get(); boolean alreadyServiced = false; if (type == DestType.PICKUP) { alreadyServiced = pm.getParcelState((DefaultParcel) ru) == ParcelState.PICKING_UP || pm.getParcelState((DefaultParcel) ru) == ParcelState.IN_CARGO; } else if (type == DestType.DELIVERY) { alreadyServiced = pm.getParcelState((DefaultParcel) ru) == ParcelState.DELIVERING || pm.getParcelState((DefaultParcel) ru) == ParcelState.DELIVERED; } return alreadyServiced; } @Override public MoveProgress moveTo(MovingRoadUser object, RoadUser destinationRoadUser, TimeLapse time) { final PDPModel pm = pdpModel.get(); DestinationObject newDestinationObject; if (destinationRoadUser instanceof DefaultParcel) { final DefaultParcel dp = (DefaultParcel) destinationRoadUser; final DestType type = containsObject(dp) ? DestType.PICKUP : DestType.DELIVERY; final Point pos = getParcelPos(destinationRoadUser); if (type == DestType.DELIVERY) { checkArgument( pm.containerContains((DefaultVehicle) object, (DefaultParcel) destinationRoadUser), "A vehicle can only move to the delivery location of a parcel if it is carrying it."); } newDestinationObject = new DestinationObject(type, pos, dp); } else { newDestinationObject = new DestinationObject(DestType.DEPOT, getPosition(destinationRoadUser), destinationRoadUser); } boolean destChange = true; if (destinations.containsKey(object) && !allowDiversion) { final DestinationObject prev = destinations.get(object); final boolean atDestination = getPosition(object).equals(prev.dest); if (!atDestination && prev.type != DestType.DEPOT) { // when we haven't reached our destination and the destination // isn't the depot we are not allowed to change destination checkArgument( prev.roadUser == destinationRoadUser || isAlreadyServiced(prev.type, prev.roadUser), "Diversion from the current destination is not allowed. Prev: %s, new: %s.", prev, destinationRoadUser); destChange = false; } else // change destination if (prev.type == DestType.PICKUP) { // when we are at the prev destination, and it was a pickup, we are // allowed to move if it has been picked up checkArgument( pm.getParcelState((DefaultParcel) prev.roadUser) != ParcelState.AVAILABLE, "Can not move away before the parcel has been picked up: %s.", prev.roadUser); } else if (prev.type == DestType.DELIVERY) { // when we are at the prev destination and it was a delivery, we are // allowed to move to other objects only, and only if the parcel is // delivered checkArgument(prev.roadUser != destinationRoadUser, "Can not move to the same parcel since we are already there: %s.", prev.roadUser); checkArgument( pm.getParcelState((DefaultParcel) prev.roadUser) == ParcelState.DELIVERED, "The parcel needs to be delivered before moving away: %s.", prev.roadUser); } else { // it is a depot // the destination is only changed in case we are no longer going // towards the depot destChange = newDestinationObject.type != DestType.DEPOT; } } destinations.put(object, newDestinationObject); if (destChange && newDestinationObject.type != DestType.DEPOT) { checkArgument( allowDiversion || !destinationHistory .containsEntry(object, newDestinationObject), "It is not allowed to revisit this parcel, it should have been picked up and been delivered already: %s.", newDestinationObject.roadUser); destinationHistory.put(object, newDestinationObject); } return delegate.moveTo(object, newDestinationObject.dest, time); } /** * Returns the destination of the specified {@link MovingRoadUser} if * all of the following holds: *

    *
  • The {@link MovingRoadUser} is moving to some destination.
  • *
  • The destination is a {@link DefaultParcel}.
  • *
  • The {@link MovingRoadUser} has not yet started servicing at its * destination.
  • *
* Returns null otherwise. * @param obj The {@link MovingRoadUser} to check its destination for. * @return The parcel the road user is heading to or null * otherwise. */ @Nullable public DefaultParcel getDestinationToParcel(MovingRoadUser obj) { if (destinations.containsKey(obj) && destinations.get(obj).type != DestType.DEPOT) { final ParcelState parcelState = pdpModel.get().getParcelState( (Parcel) destinations.get(obj).roadUser); // if it is a pickup destination it must still be available if ((destinations.get(obj).type == DestType.PICKUP && parcelState == ParcelState.AVAILABLE || parcelState == ParcelState.ANNOUNCED) // if it is a delivery destination it must still be in cargo || (destinations.get(obj).type == DestType.DELIVERY && parcelState == ParcelState.IN_CARGO)) { return (DefaultParcel) destinations.get(obj).roadUser; } } return null; } /** * {@inheritDoc} * @throws UnsupportedOperationException when diversion is not allowed. */ @Override public MoveProgress moveTo(MovingRoadUser object, Point destination, TimeLapse time) { if (allowDiversion) { return delegate.moveTo(object, destination, time); } else { throw new UnsupportedOperationException( "This road model doesn't allow diversion and therefore only supports the moveTo(MovingRoadUser,RoadUser,TimeLapse) method."); } } /** * {@inheritDoc} * @throws UnsupportedOperationException when diversion is not allowed. */ @Override public final MoveProgress followPath(MovingRoadUser object, Queue path, TimeLapse time) { if (allowDiversion) { return delegate.followPath(object, path, time); } else { throw new UnsupportedOperationException( "This road model doesn't allow diversion and therefore only supports the moveTo(MovingRoadUser,RoadUser,TimeLapse) method."); } } private static final class DestinationObject { final DestType type; final Point dest; final RoadUser roadUser; private final int hashCode; DestinationObject(DestType type, Point dest, RoadUser obj) { this.type = type; this.dest = dest; roadUser = obj; hashCode = Objects.hashCode(type, dest, obj); } @Override public int hashCode() { return hashCode; } @Override public boolean equals(@Nullable Object obj) { if (obj == null || getClass() != this.getClass()) { return false; } if (obj == this) { return true; } final DestinationObject other = (DestinationObject) obj; return new EqualsBuilder().append(type, other.type) .append(dest, other.dest).append(roadUser, other.roadUser).isEquals(); } @Override public String toString() { return Objects.toStringHelper(this).add("type", type).add("dest", dest) .add("roadUser", roadUser).toString(); } } enum DestType { PICKUP, DELIVERY, DEPOT; } @Override public void registerModelProvider(ModelProvider mp) { pdpModel = Optional.fromNullable(mp.getModel(PDPModel.class)); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy