RouteFollowingVehicle extends Vehicle { public void setRoute(Iterable<? extends Parcel> r) { final Iterable<Parcel> adjustedRoute = routeAdjuster.adjust(r, this); LOGGER.trace("{} setRoute {}", this, adjustedRoute); final Multiset<Parcel> routeSet = LinkedHashMultiset.create(adjustedRoute); for (final Parcel dp : routeSet.elementSet()) { final ParcelState state = getPDPModel().getParcelState(dp); checkArgument( !state.isDelivered(), "A parcel that is already delivered can not be part of a route. " + "Parcel %s in route %s.", dp, adjustedRoute); if (state.isTransitionState()) { if (state == ParcelState.PICKING_UP) { checkArgument( getPDPModel().getVehicleState(this) == VehicleState.PICKING_UP, "When a parcel in the route is in PICKING UP state the vehicle " + "must also be in that state, route: %s.", adjustedRoute, getPDPModel().getVehicleState(this)); } else { checkArgument( getPDPModel().getVehicleState(this) == VehicleState.DELIVERING, "When a parcel in the route is in DELIVERING state the vehicle" + " must also be in that state."); } checkArgument( getPDPModel().getVehicleActionInfo(this).getParcel() == dp, "A parcel in the route that is being serviced should be serviced by" + " this truck. This truck is servicing %s.", getPDPModel().getVehicleActionInfo(this).getParcel()); } final int frequency = routeSet.count(dp); if (state.isPickedUp()) { checkArgument(getPDPModel().getContents(this).contains(dp), "A parcel that is in cargo state must be in cargo of this " + "vehicle."); checkArgument( frequency <= 1, "A parcel that is in cargo may not occur more than once in a route," + " found %s instance(s) of %s.", frequency, dp, state); } else { checkArgument( frequency <= 2, "A parcel that is available may not occur more than twice in a " + "route, found %s instance(s) of %s (with state %s). Route: %s.", frequency, dp, state, adjustedRoute); } } final boolean firstEqualsFirst = firstEqualsFirstInRoute(adjustedRoute); final boolean divertable = isDiversionAllowed && !stateMachine.stateIs(serviceState); if (stateMachine.stateIs(waitState) || route.isEmpty() || divertable || firstEqualsFirst) { route = newLinkedList(adjustedRoute); newRoute = Optional.absent(); } else { checkArgument( allowDelayedRouteChanges, "Diversion is not allowed in current state and delayed route changes " + "are also not allowed, rejected route: %s.", adjustedRoute); newRoute = Optional.of(newLinkedList(adjustedRoute)); } } RouteFollowingVehicle(VehicleDTO dto,
      boolean allowDelayedRouteChanging);  RouteFollowingVehicle(VehicleDTO dto,
      boolean allowDelayedRouteChanging, RouteAdjuster adjuster,
      GeomHeuristic heuristic);  RouteFollowingVehicle(VehicleDTO dto,
      boolean allowDelayedRouteChanging, RouteAdjuster adjuster); boolean isDiversionAllowed(); boolean isDelayedRouteChangingAllowed(); void setRoute(Iterable<? extends Parcel> r); Collection<Parcel> getRoute(); @OverridingMethodsMustInvokeSuper @Override void initRoadPDP(RoadModel pRoadModel, PDPModel pPdpModel); static RouteAdjuster delayAdjuster(); static RouteAdjuster nopAdjuster();  }
@Test public void setRouteTest1() { d.setRoute(asList(p2, p2)); tick(0, 25); assertEquals(ParcelState.DELIVERING, pm.getParcelState(p2)); d.setRoute(asList(p2)); tick(25, 30); assertEquals(ParcelState.DELIVERED, pm.getParcelState(p2)); boolean exception = false; try { d.setRoute(asList(p2)); } catch (final IllegalArgumentException e) { exception = true; } assertThat(exception).isSameAs(routeAdjuster == RouteAdjusters.NOP); }
@Test public void setRouteTest2() { d.setRoute(asList(p1, p1)); tick(12, 15); assertEquals(ParcelState.PICKING_UP, pm.getParcelState(p1)); assertEquals(ParcelState.ANNOUNCED, pm.getParcelState(p3)); assertEquals(VehicleState.PICKING_UP, pm.getVehicleState(d)); assertEquals(VehicleState.IDLE, pm.getVehicleState(d2)); boolean exception = false; try { d2.setRoute(asList(p1)); } catch (final IllegalArgumentException e) { exception = true; } assertThat(exception).isSameAs(routeAdjuster == RouteAdjusters.NOP); d2.setRoute(asList(p3)); d2.tick(time(minute(10), minute(16))); assertEquals(ParcelState.PICKING_UP, pm.getParcelState(p1)); assertEquals(ParcelState.PICKING_UP, pm.getParcelState(p3)); assertEquals(VehicleState.PICKING_UP, pm.getVehicleState(d)); assertEquals(VehicleState.PICKING_UP, pm.getVehicleState(d2)); assertThat(pm.getVehicleActionInfo(d2).getParcel()).isSameAs(p3); boolean exception2 = false; try { d2.setRoute(asList(p1)); } catch (final IllegalArgumentException e) { exception2 = true; } assert_() .withMessage( "diversion: " + diversionIsAllowed + ", delayed route changes: " + allowDelayedRouteChanges + ", routeAdjuster: " + routeAdjuster) .that(exception2) .isSameAs( routeAdjuster == RouteAdjusters.NOP || routeAdjuster == RouteAdjusters.DELAY_ADJUSTER && !allowDelayedRouteChanges); tick(16, 23, 10); assertEquals(ParcelState.DELIVERING, pm.getParcelState(p1)); assertEquals(ParcelState.PICKING_UP, pm.getParcelState(p3)); assertEquals(VehicleState.DELIVERING, pm.getVehicleState(d)); assertEquals(VehicleState.PICKING_UP, pm.getVehicleState(d2)); boolean exception3 = false; try { d2.setRoute(asList(p1)); } catch (final IllegalArgumentException e) { exception3 = true; } assertThat(exception3).isSameAs(routeAdjuster == RouteAdjusters.NOP || routeAdjuster == RouteAdjusters.DELAY_ADJUSTER && !allowDelayedRouteChanges); }
@Test public void setRouteTest3() { boolean exception = false; try { d.setRoute(asList(p1, p1, p1)); } catch (final IllegalArgumentException e) { exception = true; } assertTrue(exception); d.setRoute(asList(p1)); tick(0, 10); assertEquals(ParcelState.IN_CARGO, pm.getParcelState(p1)); boolean exception2 = false; try { d2.setRoute(asList(p1)); } catch (final IllegalArgumentException e) { exception2 = true; } assertThat(exception2).isSameAs(routeAdjuster == RouteAdjusters.NOP); boolean exception3 = false; try { d.setRoute(asList(p1, p1)); } catch (final IllegalArgumentException e) { exception3 = true; } assertThat(exception3).isSameAs(routeAdjuster == RouteAdjusters.NOP); }
@Test public void setRouteTest4() { d.setRoute(asList(p1, p1)); tick(12, 15); assertEquals(ParcelState.PICKING_UP, pm.getParcelState(p1)); assertEquals(VehicleState.PICKING_UP, pm.getVehicleState(d)); boolean exception = false; try { d.setRoute(asList(p2)); assertEquals(asList(p2), d.newRoute.get()); } catch (final IllegalArgumentException e) { exception = true; } assertThat(exception).isNotSameAs(allowDelayedRouteChanges); }
@Test public void testNogoEvent() { d.setRoute(asList(p2)); tick(0, 11); tick(11, 12); assertEquals(d.gotoState, d.stateMachine.getCurrentState()); boolean exception = false; try { d.setRoute(Arrays.<Parcel>asList()); tick(12, 13); if (diversionIsAllowed) { assertEquals(d.waitState, d.stateMachine.getCurrentState()); assertEquals(new LinkedList<>(), d.route); assertFalse(d.newRoute.isPresent()); } else { assertEquals(d.gotoState, d.stateMachine.getCurrentState()); assertEquals(new LinkedList<>(asList(p2)), d.route); assertEquals(asList(), d.newRoute.get()); } } catch (final IllegalArgumentException e) { exception = true; } assertEquals(!allowDelayedRouteChanges && !diversionIsAllowed, exception); }
@Test public void testRerouteEvent() { d.setRoute(asList(p1)); tick(0, 5, 20); assertEquals(d.waitForServiceState, d.stateMachine.getCurrentState()); boolean exception = false; try { d.setRoute(Arrays.<Parcel>asList(p2)); tick(12, 13); if (diversionIsAllowed) { assertEquals(d.gotoState, d.stateMachine.getCurrentState()); assertEquals(new LinkedList<>(asList(p2)), d.route); assertFalse(d.newRoute.isPresent()); } else { assertEquals(d.serviceState, d.stateMachine.getCurrentState()); assertEquals(new LinkedList<>(asList(p1)), d.route); assertEquals(asList(p2), d.newRoute.get()); } } catch (final IllegalArgumentException e) { exception = true; } assertEquals(!allowDelayedRouteChanges && !diversionIsAllowed, exception); }
@Test public void diversionTestInServiceState() { d.setRoute(asList(p1)); assertEquals(Optional.absent(), d.newRoute); tick(2, 3); assertEquals(d.waitState, d.stateMachine.getCurrentState()); tick(3, 4); assertEquals(d.gotoState, d.stateMachine.getCurrentState()); tick(4, 5); assertEquals(d.gotoState, d.stateMachine.getCurrentState()); tick(5, 6); assertEquals(d.serviceState, d.stateMachine.getCurrentState()); d.setRoute(asList(p1, p2, p1)); assertEquals(new LinkedList<>(asList(p1, p2, p1)), d.route); assertEquals(Optional.absent(), d.newRoute); d.setRoute(asList(p1)); assertEquals(new LinkedList<>(asList(p1)), d.route); assertEquals(Optional.absent(), d.newRoute); boolean excep = false; try { d.setRoute(asList(p2)); assertEquals(asList(p2), d.newRoute.get()); assertEquals(new LinkedList<>(asList(p1)), d.route); } catch (final IllegalArgumentException e) { excep = true; } assertThat(excep).isNotSameAs(allowDelayedRouteChanges); tick(6, 7); assertEquals(d.serviceState, d.stateMachine.getCurrentState()); tick(7, 8); assertEquals(d.waitState, d.stateMachine.getCurrentState()); assertEquals(asList(p1), pm.getContents(d).asList()); tick(8, 9); assertEquals(d.waitState, d.stateMachine.getCurrentState()); if (allowDelayedRouteChanges) { if (routeAdjuster == RouteAdjusters.DELAY_ADJUSTER) { assertEquals(new LinkedList<>(asList(p2, p1)), d.route); } else { assertEquals(new LinkedList<>(asList(p2)), d.route); } assertEquals(Optional.absent(), d.newRoute); } else { assertEquals(new LinkedList<>(asList()), d.route); assertEquals(Optional.absent(), d.newRoute); } }
@Test public void diversionTestInWaitState() { assertEquals(d.waitState, d.stateMachine.getCurrentState()); d.setRoute(asList(p1)); assertEquals(Optional.absent(), d.newRoute); assertEquals(new LinkedList<>(asList(p1)), d.route); d.setRoute(asList(p1, p2, p1, p2)); assertEquals(Optional.absent(), d.newRoute); assertEquals(new LinkedList<>(asList(p1, p2, p1, p2)), d.route); d.setRoute(asList(p2, p2, p1, p1)); assertEquals(Optional.absent(), d.newRoute); assertEquals(new LinkedList<>(asList(p2, p2, p1, p1)), d.route); }
@Test public void brokenWaitState() { d.setRoute(asList(p1)); tick(4, 5, 30); assertEquals(d.gotoState, d.stateMachine.getCurrentState()); if (allowDelayedRouteChanges) { d.setRoute(Arrays.<Parcel>asList()); rm.moveTo(d, p1, time(minute(0), minute(10))); assertTrue(rm.equalPosition(d, p1)); pm.pickup(d, p1, time(minute(16), minute(17))); assertEquals(VehicleState.PICKING_UP, pm.getVehicleState(d)); if (diversionIsAllowed) { boolean exception = false; try { tick(5, 6); } catch (final IllegalStateException e) { exception = true; } assertTrue(exception); } } }
@Test public void tooEarlyTest1() { final Parcel p4 = Parcel .builder(new Point(1, 2), new Point(1, 4)) .pickupTimeWindow(TimeWindow.create(minute(5) + second(30), minute(15))) .deliveryTimeWindow(TimeWindow.create(minute(16), minute(30))) .pickupDuration(minute(3)) .deliveryDuration(minute(1)) .build(); PDPTWTestUtil.register(rm, pm, p4); d.setRoute(asList(p4)); tick(0, 1); tick(1, 2); tick(2, 3); assertEquals(d.waitState, d.stateMachine.getCurrentState()); assertEquals(new Point(1, 1), rm.getPosition(d)); tick(3, 4); assertEquals(new Point(1, 1.5), rm.getPosition(d)); assertEquals(d.gotoState, d.stateMachine.getCurrentState()); tick(4, 5); assertEquals(new Point(1, 2.0), rm.getPosition(d)); assertEquals(d.gotoState, d.stateMachine.getCurrentState()); pm.getEventAPI().addListener(new Listener() { @Override public void handleEvent(Event e) { assertEquals(minute(5) + second(30), ((PDPModelEvent) e).time); } }, PDPModelEventType.START_PICKUP); tick(5, 6); assertEquals(d.serviceState, d.stateMachine.getCurrentState()); }
@Test public void tooEarlyTest2() { final Parcel p4 = Parcel .builder(new Point(1, 2.2), new Point(1, 4)) .pickupTimeWindow(TimeWindow.create(minute(5) + second(30), minute(15))) .deliveryTimeWindow(TimeWindow.create(minute(16), minute(30))) .pickupDuration(minute(3)) .deliveryDuration(minute(1)) .build(); PDPTWTestUtil.register(rm, pm, p4); d.setRoute(asList(p4)); tick(0, 1); tick(1, 2); tick(2, 3); assertEquals(d.waitState, d.stateMachine.getCurrentState()); assertEquals(new Point(1, 1), rm.getPosition(d)); tick(3, 4); assertEquals(new Point(1, 1.5), rm.getPosition(d)); assertEquals(d.gotoState, d.stateMachine.getCurrentState()); tick(4, 5); assertEquals(new Point(1, 2.0), rm.getPosition(d)); assertEquals(d.gotoState, d.stateMachine.getCurrentState()); pm.getEventAPI().addListener(new Listener() { @Override public void handleEvent(Event e) { assertEquals(minute(5) + second(30), ((PDPModelEvent) e).time); } }, PDPModelEventType.START_PICKUP); tick(5, 6); assertEquals(d.serviceState, d.stateMachine.getCurrentState()); }
@Test public void tooEarlyTest3() { final Parcel p4 = Parcel .builder(new Point(1, 1.99), new Point(1, 4)) .pickupTimeWindow(TimeWindow.create(minute(5) + second(30), minute(15))) .deliveryTimeWindow(TimeWindow.create(minute(16), minute(30))) .pickupDuration(minute(3)) .deliveryDuration(minute(1)) .build(); PDPTWTestUtil.register(rm, pm, p4); d.setRoute(asList(p4)); tick(0, 1); tick(1, 2); tick(2, 3); assertEquals(d.waitState, d.stateMachine.getCurrentState()); assertEquals(new Point(1, 1), rm.getPosition(d)); tick(3, 4); assertEquals(new Point(1, 1.5), rm.getPosition(d)); assertEquals(d.gotoState, d.stateMachine.getCurrentState()); tick(4, 5); assertEquals(new Point(1, 1.99), rm.getPosition(d)); assertEquals(d.waitForServiceState, d.stateMachine.getCurrentState()); pm.getEventAPI().addListener(new Listener() { @Override public void handleEvent(Event e) { assertEquals(minute(5) + second(30), ((PDPModelEvent) e).time); } }, PDPModelEventType.START_PICKUP); tick(5, 6); assertEquals(d.serviceState, d.stateMachine.getCurrentState()); tick(6, 7); }
@Test @SuppressWarnings("ModifyingCollectionWithItself") public void noServiceTime() { final Parcel noServ = Parcel .builder(new Point(1, 2), new Point(1, 4)) .pickupTimeWindow(TimeWindow.create(minute(5), minute(15))) .deliveryTimeWindow(TimeWindow.create(minute(16), minute(30))) .pickupDuration(minute(0)) .deliveryDuration(minute(0)) .build(); PDPTWTestUtil.register(rm, pm, noServ); final List<DefaultEvent> expected = new ArrayList<>(asList(GOTO, ARRIVED, READY_TO_SERVICE, DONE)); expected.addAll(expected); final List<DefaultEvent> actual = new ArrayList<>(); d.stateMachine.getEventAPI().addListener(new Listener() { @Override public void handleEvent(Event e) { actual.add((DefaultEvent) ((StateTransitionEvent) e).trigger); } }, StateMachineEvent.STATE_TRANSITION); d.setRoute(asList(noServ, noServ)); tick(0, 100); assertThat(actual).containsExactlyElementsIn(expected).inOrder(); }