@Override public void addParcelIn(Container container, Parcel parcel) { synchronized (this) { /* 1 */ checkArgument( !roadModel.get().containsObject(parcel), "this parcel is already added to the roadmodel"); /* 2 */ checkArgument( parcelState.getKeys(parcel) == ParcelState.AVAILABLE, "parcel must be registered and in AVAILABLE state, current state: %s", parcelState.getKeys(parcel)); /* 3 */ checkArgument( containerCapacities.containsKey(container), "the parcel container is not registered"); /* 4 */ checkArgument( roadModel.get().containsObject(container), "the parcel container is not on the roadmodel"); final double newSize = containerContentsSize.get(container) + parcel.getMagnitude(); /* 5 */ checkArgument( newSize <= containerCapacities.get(container), "parcel does not fit in container. Capacity is %s, current content size is %s, new parcel size is %s", containerCapacities.get(container), containerContentsSize.get(container), parcel.getMagnitude()); containerContents.put(container, parcel); containerContentsSize.put(container, newSize); parcelState.put(ParcelState.IN_CARGO, parcel); } }
@Override public void drop(Vehicle vehicle, Parcel parcel, TimeLapse time) { synchronized (this) { /* 1 */ checkVehicleInRoadModel(vehicle); /* 2 */ checkArgument( vehicleState.get(vehicle).equals(VehicleState.IDLE), "vehicle must be idle but is: %s ", vehicleState.get(vehicle)); /* 3 */ checkArgument( containerContents.get(vehicle).contains(parcel), "vehicle does not contain parcel"); eventDispatcher.dispatchEvent( new PDPModelEvent( PDPModelEventType.START_DELIVERY, self, time.getTime(), parcel, vehicle)); if (time.getTimeLeft() < parcel.getDeliveryDuration()) { vehicleState.put(vehicle, VehicleState.DELIVERING); parcelState.put(ParcelState.DELIVERING, parcel); pendingVehicleActions.put( vehicle, new DropAction( this, vehicle, parcel, parcel.getDeliveryDuration() - time.getTimeLeft())); time.consumeAll(); } else { time.consume(parcel.getDeliveryDuration()); doDrop(vehicle, parcel, time.getTime()); } } }
@Override public void tick(TimeLapse timeLapse) { synchronized (this) { // TODO this can be optimized by scheduling events upon registering currentTime = timeLapse.getStartTime(); final Collection<Parcel> parcels = parcelState.get(ParcelState.ANNOUNCED); final List<Parcel> newAvailables = newArrayList(); for (final Parcel p : parcels) { if (timeLapse.getStartTime() >= p.getPickupTimeWindow().begin) { newAvailables.add(p); } } for (final Parcel p : newAvailables) { parcelState.put(ParcelState.AVAILABLE, p); eventDispatcher.dispatchEvent( new PDPModelEvent(PDPModelEventType.PARCEL_AVAILABLE, self, currentTime, p, null)); } } }
/** * The actual dropping of the specified {@link rinde.sim.core.model.pdp.Parcel} by the specified * {@link rinde.sim.core.model.pdp.Vehicle}. * * @param vehicle The {@link rinde.sim.core.model.pdp.Vehicle} that performs the dropping. * @param parcel The {@link rinde.sim.core.model.pdp.Parcel} that is dropped. * @param time The current time. */ protected void doDrop(Vehicle vehicle, Parcel parcel, long time) { synchronized (this) { containerContents.remove(vehicle, parcel); containerContentsSize.put( vehicle, containerContentsSize.get(vehicle) - parcel.getMagnitude()); roadModel.get().addObjectAtSamePosition(parcel, vehicle); parcelState.put(ParcelState.AVAILABLE, parcel); LOGGER.info("{} dropped {} by {}", time, parcel, vehicle); eventDispatcher.dispatchEvent( new PDPModelEvent(PDPModelEventType.PARCEL_AVAILABLE, self, time, parcel, null)); } }
/** * The actual delivery of the specified {@link rinde.sim.core.model.pdp.Parcel} by the specified * {@link rinde.sim.core.model.pdp.Vehicle}. * * @param vehicle The {@link rinde.sim.core.model.pdp.Vehicle} that performs the delivery. * @param parcel The {@link rinde.sim.core.model.pdp.Parcel} that is delivered. * @param time The current time. */ protected void doDeliver(Vehicle vehicle, Parcel parcel, long time) { synchronized (this) { containerContents.remove(vehicle, parcel); containerContentsSize.put( vehicle, containerContentsSize.get(vehicle) - parcel.getMagnitude()); parcelState.put(ParcelState.DELIVERED, parcel); LOGGER.info("{} end delivery of {} by {}", time, parcel, vehicle); eventDispatcher.dispatchEvent( new PDPModelEvent(PDPModelEventType.END_DELIVERY, self, time, parcel, vehicle)); } }
/** * Actual pickup, updates the {@link rinde.sim.core.model.pdp.Vehicle} contents. * * @param vehicle The {@link rinde.sim.core.model.pdp.Vehicle} that performs the pickup. * @param parcel The {@link rinde.sim.core.model.pdp.Parcel} that is picked up. * @param time The current time. * @see #pickup(rinde.sim.core.model.pdp.Vehicle, rinde.sim.core.model.pdp.Parcel, * rinde.sim.core.TimeLapse) */ protected void doPickup(Vehicle vehicle, Parcel parcel, long time) { synchronized (this) { containerContents.put(vehicle, parcel); containerContentsSize.put( vehicle, containerContentsSize.get(vehicle) + parcel.getMagnitude()); parcelState.put(ParcelState.IN_CARGO, parcel); LOGGER.info("{} end pickup of {} by {}", time, parcel, vehicle); eventDispatcher.dispatchEvent( new PDPModelEvent(PDPModelEventType.END_PICKUP, self, time, parcel, vehicle)); } }
@Override protected boolean doRegister(PDPObject element) { synchronized (this) { LOGGER.info("{} register {}", currentTime, element); if (element.getType() == PDPType.PARCEL) { checkArgument(!parcelState.containsValue(element)); final Parcel p = (Parcel) element; final ParcelState state = currentTime < p.getPickupTimeWindow().begin ? ParcelState.ANNOUNCED : ParcelState.AVAILABLE; parcelState.put(state, (Parcel) element); eventDispatcher.dispatchEvent( new PDPModelEvent(PDPModelEventType.NEW_PARCEL, self, currentTime, p, null)); // if the parcel is immediately available, we send this event as // well if (state == ParcelState.AVAILABLE) { eventDispatcher.dispatchEvent( new PDPModelEvent(PDPModelEventType.PARCEL_AVAILABLE, self, currentTime, p, null)); } } else { // it is a vehicle or a depot final Container container = (Container) element; checkArgument(!containerCapacities.containsKey(container)); containerCapacities.put(container, container.getCapacity()); containerContentsSize.put(container, 0d); if (element.getType() == PDPType.VEHICLE) { final Vehicle v = (Vehicle) element; vehicleState.put(v, VehicleState.IDLE); eventDispatcher.dispatchEvent( new PDPModelEvent(PDPModelEventType.NEW_VEHICLE, self, currentTime, null, v)); } } element.initPDPObject(self); return true; } }
/** * Initializes the PDPModel. * * @param twp The {@link rinde.sim.core.model.pdp.twpolicy.TimeWindowPolicy} which is used in the * model. */ public DefaultTripleDJPDPModel(TimeWindowPolicy twp) { timeWindowPolicy = twp; containerContents = LinkedHashMultimap.create(); containerContentsSize = newLinkedHashMap(); containerCapacities = newLinkedHashMap(); pendingVehicleActions = newLinkedHashMap(); vehicleState = newLinkedHashMap(); parcelState = CategoryMap.create(); eventDispatcher = new EventDispatcher(PDPModelEventType.values()); roadModel = Optional.absent(); }
@Override public void deliver(Vehicle vehicle, Parcel parcel, TimeLapse time) { synchronized (this) { /* 1 */ checkVehicleInRoadModel(vehicle); /* 2 */ checkArgument( vehicleState.get(vehicle).equals(VehicleState.IDLE), "vehicle must be idle but is: %s ", vehicleState.get(vehicle)); /* 3 */ checkArgument( containerContents.get(vehicle).contains(parcel), "vehicle does not contain parcel"); /* 4 */ checkArgument( parcel.getDestination().equals(roadModel.get().getPosition(vehicle)), "parcel must be delivered at its destination, vehicle should move there first"); checkArgument( timeWindowPolicy.canDeliver( parcel.getDeliveryTimeWindow(), time.getTime(), parcel.getDeliveryDuration()), "parcel delivery is not allowed at this time (%s) according to the time window policy: %s", time.getTime(), timeWindowPolicy); checkArgument( parcel.canBeDelivered(vehicle, time.getTime()), "the parcel does not allow a delivery now"); eventDispatcher.dispatchEvent( new PDPModelEvent( PDPModelEventType.START_DELIVERY, self, time.getTime(), parcel, vehicle)); if (time.getTimeLeft() < parcel.getDeliveryDuration()) { vehicleState.put(vehicle, VehicleState.DELIVERING); parcelState.put(ParcelState.DELIVERING, parcel); pendingVehicleActions.put( vehicle, new DeliverAction( this, vehicle, parcel, parcel.getDeliveryDuration() - time.getTimeLeft())); time.consumeAll(); } else { time.consume(parcel.getDeliveryDuration()); doDeliver(vehicle, parcel, time.getTime()); } } }
@Override public boolean unregister(PDPObject element) { synchronized (this) { LOGGER.info("unregister {}", element); if (element instanceof Container) { containerCapacities.remove(element); containerContentsSize.remove(element); containerContents.removeAll(element); } if (element instanceof Parcel) { parcelState.removeValue((Parcel) element); } if (element instanceof Vehicle) { vehicleState.remove(element); pendingVehicleActions.remove(element); } } return true; }
@Override public ParcelState getParcelState(Parcel parcel) { synchronized (this) { return parcelState.getKeys(parcel); } }
@Override public Collection<Parcel> getParcels(ParcelState... states) { synchronized (this) { return parcelState.getMultiple(states); } }
@Override public Collection<Parcel> getParcels(ParcelState state) { synchronized (this) { return parcelState.get(state); } }
@Override public void pickup(Vehicle vehicle, Parcel parcel, TimeLapse time) { synchronized (this) { /* 1 */ checkVehicleInRoadModel(vehicle); /* 2 */ checkArgument( roadModel.get().containsObject(parcel), "parcel does not exist in RoadModel"); final ParcelState ps = parcelState.getKeys(parcel); /* 3 */ checkArgument( ps == ParcelState.AVAILABLE || ps == ParcelState.ANNOUNCED, "Parcel must be registered and must be either ANNOUNCED or AVAILABE, it is: %s. Parcel: %s.", ps, parcel); /* 4 */ checkArgument( vehicleState.get(vehicle) == VehicleState.IDLE, "vehicle must be registered and must be available"); /* 5 */ checkArgument( roadModel.get().equalPosition(vehicle, parcel), "vehicle must be at the same location as the parcel it wishes to pickup"); final double newSize = containerContentsSize.get(vehicle) + parcel.getMagnitude(); /* 6 */ checkArgument( newSize <= containerCapacities.get(vehicle), "parcel does not fit in vehicle. Parcel size: %s, current contents size: %s, capacity: %s.", parcel.getMagnitude(), containerContentsSize.get(vehicle), containerCapacities.get(vehicle)); checkArgument( timeWindowPolicy.canPickup( parcel.getPickupTimeWindow(), time.getTime(), parcel.getPickupDuration()), "parcel pickup is not allowed according to the time window policy: %s, current time: %s, time window %s.", timeWindowPolicy, time.getTime(), parcel.getPickupTimeWindow()); checkArgument( parcel.canBePickedUp(vehicle, time.getTime()), "the parcel does not allow pickup now"); eventDispatcher.dispatchEvent( new PDPModelEvent(PDPModelEventType.START_PICKUP, self, time.getTime(), parcel, vehicle)); // remove the parcel such that no other attempts to pickup can be // made roadModel.get().removeObject(parcel); // in this case we know we cannot finish this action with the // available time. We must continue in the nextDeliverable tick. if (time.getTimeLeft() < parcel.getPickupDuration()) { vehicleState.put(vehicle, VehicleState.PICKING_UP); parcelState.put(ParcelState.PICKING_UP, parcel); pendingVehicleActions.put( vehicle, new PickupAction( this, vehicle, parcel, parcel.getPickupDuration() - time.getTimeLeft())); time.consumeAll(); } else { time.consume(parcel.getPickupDuration()); doPickup(vehicle, parcel, time.getTime()); } } }