/** * */ package vroom.trsp.datamodel; import java.util.ArrayList; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; import vroom.common.modeling.dataModel.Depot; import vroom.common.modeling.dataModel.attributes.ITimeWindow; import vroom.common.utilities.ExtendedReentrantLock; import vroom.common.utilities.IToShortString; import vroom.trsp.datamodel.TRSPSolution.GiantPermutation; /** * <code>TRSPTourBase</code> is a common class for all implementations of {@link ITRSPTour} * <p> * Creation date: Sep 27, 2011 - 2:48:10 PM * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a * href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @version 1.0 */ public abstract class TRSPTourBase implements ITRSPTour, IToShortString { /** The parent solution */ private final TRSPSolution mSolution; /** * Gets the parent solution. * * @return the parent solution */ @Override public TRSPSolution getSolution() { return mSolution; } /** * Returns the giant permutation defined in the associated solution * * @return the giant permutation defined in the associated solution */ protected GiantPermutation getPermutation() { return mSolution.getGiantPermutation(); } /** The total cost. */ private double mTotalCost; @Override public double getTotalCost() { return mTotalCost; } /** * Sets the total cost. * * @param totalCost * the new total cost */ @Override public void setTotalCost(double totalCost) { mTotalCost = totalCost; } /** * Creates a new <code>TRSPTourBase</code> * * @param solution */ public TRSPTourBase(TRSPSolution solution) { super(); this.mSolution = solution; this.mLock = new ExtendedReentrantLock(); } /** * Creates a new <code>TRSPTourBase</code> */ public TRSPTourBase() { this(null); } @Override public Comparable<?> getObjective() { return getTotalCost(); } @Override public double getObjectiveValue() { return getTotalCost(); } /** * Gets the node sequence. * * @return the node sequence */ @Override public List<Integer> asList() { List<Integer> seq = new ArrayList<Integer>(length()); for (Integer i : this) { seq.add(i); } return seq; } @Override public int[] asArray() { int[] seq = new int[length()]; int idx = 0; for (Integer i : this) { seq[idx++] = i; } return seq; } @Override public String getNodeSeqString() { StringBuilder sb = new StringBuilder(length() * 3); sb.append("<"); Iterator<Integer> it = iterator(); while (it.hasNext()) { int n = it.next(); sb.append(n); if (it.hasNext()) sb.append(","); } sb.append(">"); return sb.toString(); } @Override public String toString() { return String.format("t:%s c:%.2f l:%s %s", getTechnicianId(), getTotalCost(), length(), getNodeSeqString()); } @Override public String toShortString() { return getNodeSeqString(); } @Override public String toDetailedString() { return toString(); } // ------------------------------------ // Utility methods // ------------------------------------ /** * Getter for the technician which is executing this tour * * @return the Technician associated with this tour */ public Technician getTechnician() { return getInstance().getTechnician(getTechnicianId()); } /** * Gets the earliest start time of this tour, as defined by the technician home time window start * * @return the earliest start time of this tour, as defined by the technician home time window start * @see Depot#getTimeWindow() * @see Technician#getHome() */ public double getEarliestStartTime() { return getTechnician().getHome().getTimeWindow().startAsDouble(); } /** * Utility method to get the time window associated with a node * * @param node * the considered node * @return the time window of <code>node</code> * @see TRSPInstance#getTimeWindow(int) */ public ITimeWindow getTimeWindow(int node) { return getInstance().getTimeWindow(node); } /** * Utility method to get the service time associated with a node. * <p> * In this implementation depots are assumed to have a service time of <code>0</code>. * </p> * * @param node * the considered node * @return the service time of <code>node</code> * @see TRSPInstance#getServiceTime(int) */ public double getServiceTime(int node) { return getInstance().getServiceTime(node); } /** * Utility method to calculate the travel time between two nodes * * @param pred * @param succ * @return the travel time between <code>pred</code> and <code>succ</code>. */ public double getTravelTime(int pred, int succ) { if (pred == ITRSPTour.UNDEFINED || succ == ITRSPTour.UNDEFINED) return 0; return getInstance().getCostDelegate().getTravelTime(pred, succ, getTechnician()); } /** * Creates a boolean array representing the tools initially available to this tour's technician * * @return a boolean array representing the tools initially available to this tour's technician */ public boolean[] getTechnicianToolSet() { boolean[] tools = new boolean[getInstance().getToolCount()]; for (int t : getTechnician().getToolSet()) tools[t] = true; return tools; } /** * Creates an integer array containing the spare parts initially available to this tour's technician * * @return an integer array containing the spare parts initially available to this tour's technician */ public int[] getTechnicianSpareParts() { int[] spare = new int[getInstance().getSpareCount()]; for (int t = 0; t < spare.length; t++) spare[t] = getTechnician().getAvailableSpareParts(t); return spare; } /** * Returns <code>true</code> if <code>node</code> is a main depot * * @param node * , the node id, can be {@link ITRSPTour#UNDEFINED} * @return <code>true</code> if <code>node</code> is a main depot */ protected boolean isMainDepot(int node) { return node != ITRSPTour.UNDEFINED && getInstance().isMainDepot(node); } // ------------------------------------ // ------------------------------------ // ILockable interface implementation // ------------------------------------ /** A lock to be used by this instance */ private final ExtendedReentrantLock mLock; private boolean mSelfLock = false; @Override public final boolean tryLock(long timeout) { try { return getLockInstance().tryLock(timeout, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { return false; } } @Override public final void acquireLock() { try { if (!getLockInstance().tryLock(TRY_LOCK_TIMOUT, TRY_LOCK_TIMOUT_UNIT)) { throw new IllegalStateException(String.format( "Unable to acquire lock on this instance of %s (%s) after %s %s, owner: %s", this.getClass() .getSimpleName(), hashCode(), TRY_LOCK_TIMOUT, TRY_LOCK_TIMOUT_UNIT, getLockInstance() .getOwnerName())); } } catch (InterruptedException e) { throw new IllegalStateException(String.format("Unable to acquire lock on this instance of %s (%s)", this .getClass().getSimpleName(), hashCode()), e); } ; } @Override public final void releaseLock() { if (mLock.isLocked()) { this.mLock.unlock(); } } public final void internalReleaseLock() { if (mSelfLock) { releaseLock(); mSelfLock = false; } } @Override public final boolean isLockOwnedByCurrentThread() { return this.mLock.isHeldByCurrentThread(); } @Override public final ExtendedReentrantLock getLockInstance() { return this.mLock; } /** * Check the lock state of this object * * @return <code>true</code> if there was no previous lock and lock was acquired, <code>false</code> if the lock was * already owned by the current thread * @throws ConcurrentModificationException */ public final boolean checkLock() throws ConcurrentModificationException { if (!isLockOwnedByCurrentThread() && mLock.isLocked()) { throw new ConcurrentModificationException(String.format( "The current thread (%s) does not have the lock on this instance of %s, owner: %s", Thread.currentThread(), this.getClass().getSimpleName(), getLockInstance().getOwnerName())); } else if (!mLock.isLocked()) { acquireLock(); mSelfLock = true; return true; } else { mSelfLock = false; return false; } } // ------------------------------------ @Override public abstract ITRSPTour clone(); }