public void ChangeDestination(TrackingId trackingId, UnLocode destinationUnLocode) { Cargo cargo = _cargoRepository.Find(trackingId); Location destination = _locationRepository.Find(destinationUnLocode); RouteSpecification routeSpecification = new RouteSpecification(cargo.RouteSpecification.Origin, destination, cargo.RouteSpecification.ArrivalDeadline); cargo.SpecifyNewRoute(routeSpecification); }
private static RoutingStatus CalculateRoutingStatus(Itinerary itinerary, RouteSpecification specification) { if (itinerary == null) { return(RoutingStatus.NotRouted); } return(specification.IsSatisfiedBy(itinerary) ? RoutingStatus.Routed : RoutingStatus.Misrouted); }
/// <summary> /// Creates a new delivery snapshot to reflect changes in routing, i.e. when the route /// specification or the itinerary has changed but no additional handling of the /// cargo has been performed. /// </summary> /// <param name="routeSpecification">Current route specification.</param> /// <param name="itinerary">Current itinerary.</param> /// <returns>New delivery status description.</returns> public Delivery UpdateOnRouting(RouteSpecification routeSpecification, Itinerary itinerary) { if (routeSpecification == null) { throw new ArgumentNullException("routeSpecification"); } return(new Delivery(_lastEvent, itinerary, routeSpecification)); }
/// <summary> /// Specifies a new route for this cargo. /// </summary> /// <param name="routeSpecification">Route specification.</param> public virtual void SpecifyNewRoute(RouteSpecification routeSpecification) { if (routeSpecification == null) { throw new ArgumentNullException("routeSpecification"); } RouteSpecification = routeSpecification; Delivery = Delivery.UpdateOnRouting(RouteSpecification, Itinerary); }
private IEnumerable<Itinerary> GetAllPossibleRoutes(RouteSpecification routeSpecification) { IList<Voyage> voyages = _voyageRepository.FindBeginingBefore(routeSpecification.ArrivalDeadline); IList<TransitEdge> graph = voyages.SelectMany(v => v.Schedule.CarrierMovements.Select(m => ToEdge(v, m))).ToList(); var allPaths = _graphTraversalService.FindPaths( routeSpecification.Origin.UnLocode.CodeString, routeSpecification.Destination.UnLocode.CodeString, graph, new Constraints(routeSpecification.ArrivalDeadline)); return allPaths.Select(x => ToItinerary(x)); }
private Delivery(HandlingEvent lastHandlingEvent, Itinerary itinerary, RouteSpecification specification) { _calculatedAt = DateTime.Now; _lastEvent = lastHandlingEvent; _misdirected = CalculateMisdirectionStatus(itinerary); _routingStatus = CalculateRoutingStatus(itinerary, specification); _transportStatus = CalculateTransportStatus(); _lastKnownLocation = CalculateLastKnownLocation(); _eta = CalculateEta(itinerary); _nextExpectedActivity = CalculateNextExpectedActivity(specification, itinerary); _isUnloadedAtDestination = CalculateUnloadedAtDestination(specification); }
public TrackingId BookNewCargo(string customerLogin, UnLocode originUnLocode, UnLocode destinationUnLocode, DateTime arrivalDeadline) { var origin = _locationRepository.Find(originUnLocode); var destination = _locationRepository.Find(destinationUnLocode); var customer = _customerRepository.Find(customerLogin); var routeSpecification = new RouteSpecification(origin, destination, arrivalDeadline); var trackingId = _cargoRepository.NextTrackingId(); var cargo = new Cargo(trackingId, routeSpecification, customer); _cargoRepository.Store(cargo); return trackingId; }
/// <summary> /// Creates new <see cref="Cargo"/> object with provided tracking id and route specification. /// </summary> /// <param name="trackingId">Tracking id of this cargo.</param> /// <param name="routeSpecification">Route specification.</param> /// <param name="orderingCustomer">Customer who ordered this cargo.</param> public Cargo(TrackingId trackingId, RouteSpecification routeSpecification, Customer orderingCustomer) { if (trackingId == null) { throw new ArgumentNullException("trackingId"); } if (routeSpecification == null) { throw new ArgumentNullException("routeSpecification"); } OrderingCustomer = orderingCustomer; TrackingId = trackingId; RouteSpecification = routeSpecification; Delivery = Delivery.DerivedFrom(RouteSpecification, Itinerary, null); }
private HandlingActivity CalculateNextExpectedActivity(RouteSpecification routeSpecification, Itinerary itinerary) { if (!OnTrack) { return(null); } if (LastEvent == null) { return(new HandlingActivity(HandlingEventType.Receive, routeSpecification.Origin)); } switch (LastEvent.EventType) { case HandlingEventType.Load: Leg lastLeg = itinerary.Legs.FirstOrDefault(x => x.LoadLocation == LastEvent.Location); return(lastLeg != null ? new HandlingActivity(HandlingEventType.Unload, lastLeg.UnloadLocation) : null); case HandlingEventType.Unload: IEnumerator <Leg> enumerator = itinerary.Legs.GetEnumerator(); while (enumerator.MoveNext()) { if (enumerator.Current.UnloadLocation == LastEvent.Location) { Leg currentLeg = enumerator.Current; return(enumerator.MoveNext() ? new HandlingActivity(HandlingEventType.Load, enumerator.Current.LoadLocation) : new HandlingActivity(HandlingEventType.Claim, currentLeg.UnloadLocation)); } } return(null); case HandlingEventType.Receive: Leg firstLeg = itinerary.Legs.First(); return(new HandlingActivity(HandlingEventType.Load, firstLeg.LoadLocation)); default: return(null); } }
/// <summary> /// Creates a new delivery snapshot based on the complete handling history of a cargo, as well /// as its route specification and itinerary. /// </summary> /// <param name="specification">Current route specification.</param> /// <param name="itinerary">Current itinerary.</param> /// <param name="lastHandlingEvent">Most recent handling event.</param> /// <returns>Delivery status description.</returns> public static Delivery DerivedFrom(RouteSpecification specification, Itinerary itinerary, HandlingEvent lastHandlingEvent) { return new Delivery(lastHandlingEvent, itinerary, specification); }
private static RoutingStatus CalculateRoutingStatus(Itinerary itinerary, RouteSpecification specification) { if (itinerary == null) { return RoutingStatus.NotRouted; } return specification.IsSatisfiedBy(itinerary) ? RoutingStatus.Routed : RoutingStatus.Misrouted; }
private HandlingActivity CalculateNextExpectedActivity(RouteSpecification routeSpecification, Itinerary itinerary) { if (!OnTrack) { return null; } if (LastEvent == null) { return new HandlingActivity(HandlingEventType.Receive, routeSpecification.Origin); } switch (LastEvent.EventType) { case HandlingEventType.Load: Leg lastLeg = itinerary.Legs.FirstOrDefault(x => x.LoadLocation == LastEvent.Location); return lastLeg != null ? new HandlingActivity(HandlingEventType.Unload, lastLeg.UnloadLocation) : null; case HandlingEventType.Unload: IEnumerator<Leg> enumerator = itinerary.Legs.GetEnumerator(); while (enumerator.MoveNext()) { if (enumerator.Current.UnloadLocation == LastEvent.Location) { Leg currentLeg = enumerator.Current; return enumerator.MoveNext() ? new HandlingActivity(HandlingEventType.Load, enumerator.Current.LoadLocation) : new HandlingActivity(HandlingEventType.Claim, currentLeg.UnloadLocation); } } return null; case HandlingEventType.Receive: Leg firstLeg = itinerary.Legs.First(); return new HandlingActivity(HandlingEventType.Load, firstLeg.LoadLocation); default: return null; } }
private bool CalculateUnloadedAtDestination(RouteSpecification specification) { return LastEvent != null && LastEvent.EventType == HandlingEventType.Unload && specification.Destination == LastEvent.Location; }
/// <summary> /// Creates a new delivery snapshot to reflect changes in routing, i.e. when the route /// specification or the itinerary has changed but no additional handling of the /// cargo has been performed. /// </summary> /// <param name="routeSpecification">Current route specification.</param> /// <param name="itinerary">Current itinerary.</param> /// <returns>New delivery status description.</returns> public Delivery UpdateOnRouting(RouteSpecification routeSpecification, Itinerary itinerary) { if (routeSpecification == null) { throw new ArgumentNullException("routeSpecification"); } return new Delivery(_lastEvent, itinerary, routeSpecification); }
public void CanTransportFromHongkongToStockholm() { /* Test setup: A cargo should be shipped from Hongkong to Stockholm, and it should arrive in no more than two weeks. */ Location origin = SampleLocations.Hongkong; Location destination = SampleLocations.Stockholm; DateTime arrivalDeadline = DateTime.Now.AddDays(20);//new DateTime(2009, 3, 18); /* Use case 1: booking A new cargo is booked, and the unique tracking id is assigned to the cargo. */ TrackingId trackingId = BookingService.BookNewCargo("c1", origin.UnLocode, destination.UnLocode, arrivalDeadline ); /* The tracking id can be used to lookup the cargo in the repository. Important: The cargo, and thus the domain model, is responsible for determining the status of the cargo, whether it is on the right track or not and so on. This is core domain logic. Tracking the cargo basically amounts to presenting information extracted from the cargo aggregate in a suitable way. */ Cargo cargo = CargoRepository.Find(trackingId); Assert.IsNotNull(cargo); Assert.AreEqual(TransportStatus.NotReceived, cargo.Delivery.TransportStatus); Assert.AreEqual(RoutingStatus.NotRouted, cargo.Delivery.RoutingStatus); Assert.IsFalse(cargo.Delivery.IsMisdirected); Assert.IsNull(cargo.Delivery.EstimatedTimeOfArrival); //Assert.IsNull(cargo.Delivery.NextExpectedActivity); /* Use case 2: routing A number of possible routes for this cargo is requested and may be presented to the customer in some way for him/her to choose from. Selection could be affected by things like price and time of delivery, but this test simply uses an arbitrary selection to mimic that process. The cargo is then assigned to the selected route, described by an itinerary. */ IList<Itinerary> itineraries = BookingService.RequestPossibleRoutesForCargo(trackingId); Itinerary itinerary = SelectPreferedItinerary(itineraries); cargo.AssignToRoute(itinerary); Assert.AreEqual(TransportStatus.NotReceived, cargo.Delivery.TransportStatus); Assert.AreEqual(RoutingStatus.Routed, cargo.Delivery.RoutingStatus); Assert.IsNotNull(cargo.Delivery.EstimatedTimeOfArrival); //Assert.AreEqual(new HandlingActivity(RECEIVE, HONGKONG), cargo.Delivery.nextExpectedActivity()); /* Use case 3: handling A handling event registration attempt will be formed from parsing the data coming in as a handling report either via the web service interface or as an uploaded CSV file. The handling event factory tries to create a HandlingEvent from the attempt, and if the factory decides that this is a plausible handling event, it is stored. If the attempt is invalid, for example if no cargo exists for the specfied tracking id, the attempt is rejected. Handling begins: cargo is received in Hongkong. */ HandlingEventService.RegisterHandlingEvent( new DateTime(2009, 3, 1), trackingId, SampleLocations.Hongkong.UnLocode, HandlingEventType.Receive ); Assert.AreEqual(TransportStatus.InPort, cargo.Delivery.TransportStatus); Assert.AreEqual(SampleLocations.Hongkong, cargo.Delivery.LastKnownLocation); // Next event: Load onto voyage CM003 in Hongkong HandlingEventService.RegisterHandlingEvent( new DateTime(2009, 3, 3), trackingId, SampleLocations.Hongkong.UnLocode, HandlingEventType.Load ); // Check current state - should be ok //Assert.AreEqual(v100, cargo.Delivery.currentVoyage()); Assert.AreEqual(SampleLocations.Hongkong, cargo.Delivery.LastKnownLocation); Assert.AreEqual(TransportStatus.OnboardCarrier, cargo.Delivery.TransportStatus); Assert.IsFalse(cargo.Delivery.IsMisdirected); //Assert.AreEqual(new HandlingActivity(UNLOAD, NEWYORK, v100), cargo.Delivery.nextExpectedActivity()); /* Here's an attempt to register a handling event that's not valid because there is no voyage with the specified voyage number, and there's no location with the specified UN Locode either. This attempt will be rejected and will not affect the cargo delivery in any way. */ //VoyageNumber noSuchVoyageNumber = new VoyageNumber("XX000"); //UnLocode noSuchUnLocode = new UnLocode("ZZZZZ"); //try //{ // HandlingEventService.RegisterHandlingEvent( // new DateTime(2009, 3, 5), trackingId, noSuchUnLocode, HandlingEventType.Load // ); // Assert.Fail("Should not be able to register a handling event with invalid location and voyage"); //} //catch (ArgumentException) //{ //} // Cargo is now (incorrectly) unloaded in Tokyo HandlingEventService.RegisterHandlingEvent( new DateTime(2009, 3, 5), trackingId, SampleLocations.Tokyo.UnLocode, HandlingEventType.Unload ); // Check current state - cargo is misdirected! //Assert.AreEqual(NONE, cargo.Delivery.currentVoyage()); Assert.AreEqual(SampleLocations.Tokyo, cargo.Delivery.LastKnownLocation); Assert.AreEqual(TransportStatus.InPort, cargo.Delivery.TransportStatus); Assert.IsTrue(cargo.Delivery.IsMisdirected); //Assert.IsNull(cargo.Delivery.nextExpectedActivity()); // -- Cargo needs to be rerouted -- // TODO cleaner reroute from "earliest location from where the new route originates" // Specify a new route, this time from Tokyo (where it was incorrectly unloaded) to Stockholm RouteSpecification fromTokyo = new RouteSpecification(SampleLocations.Tokyo, SampleLocations.Stockholm, arrivalDeadline); cargo.SpecifyNewRoute(fromTokyo); // The old itinerary does not satisfy the new specification Assert.AreEqual(RoutingStatus.Misrouted, cargo.Delivery.RoutingStatus); //Assert.IsNull(cargo.Delivery.nextExpectedActivity()); // Repeat procedure of selecting one out of a number of possible routes satisfying the route spec IList<Itinerary> newItineraries = BookingService.RequestPossibleRoutesForCargo(cargo.TrackingId); Itinerary newItinerary = SelectPreferedItinerary(newItineraries); cargo.AssignToRoute(newItinerary); // New itinerary should satisfy new route Assert.AreEqual(RoutingStatus.Routed, cargo.Delivery.RoutingStatus); // TODO we can't handle the face that after a reroute, the cargo isn't misdirected anymore //Assert.IsFalse(cargo.isMisdirected()); //Assert.AreEqual(new HandlingActivity(LOAD, TOKYO), cargo.nextExpectedActivity()); // -- Cargo has been rerouted, shipping continues -- // Load in Tokyo HandlingEventService.RegisterHandlingEvent(new DateTime(2009, 3, 8), trackingId, SampleLocations.Tokyo.UnLocode, HandlingEventType.Load); // Check current state - should be ok //Assert.AreEqual(v300, cargo.Delivery.currentVoyage()); Assert.AreEqual(SampleLocations.Tokyo, cargo.Delivery.LastKnownLocation); Assert.AreEqual(TransportStatus.OnboardCarrier, cargo.Delivery.TransportStatus); Assert.IsFalse(cargo.Delivery.IsMisdirected); //Assert.AreEqual(new HandlingActivity(UNLOAD, HAMBURG, v300), cargo.Delivery.nextExpectedActivity()); // Unload in Hamburg //HandlingEventService.RegisterHandlingEvent(new DateTime(2009, 3, 12), trackingId, SampleLocations.Hamburg.UnLocode, HandlingEventType.Unload); //// Check current state - should be ok ////Assert.AreEqual(NONE, cargo.Delivery.currentVoyage()); //Assert.AreEqual(SampleLocations.Hamburg, cargo.Delivery.LastKnownLocation); //Assert.AreEqual(TransportStatus.InPort, cargo.Delivery.TransportStatus); //Assert.IsFalse(cargo.Delivery.IsMisdirected); ////Assert.AreEqual(new HandlingActivity(LOAD, HAMBURG, v400), cargo.Delivery.nextExpectedActivity()); //// Load in Hamburg //HandlingEventService.RegisterHandlingEvent(new DateTime(2009, 3, 14), trackingId, SampleLocations.Hamburg.UnLocode, HandlingEventType.Load); //// Check current state - should be ok ////Assert.AreEqual(v400, cargo.Delivery.currentVoyage()); //Assert.AreEqual(SampleLocations.Hamburg, cargo.Delivery.LastKnownLocation); //Assert.AreEqual(TransportStatus.OnboardCarrier, cargo.Delivery.TransportStatus); //Assert.IsFalse(cargo.Delivery.IsMisdirected); ////Assert.AreEqual(new HandlingActivity(UNLOAD, STOCKHOLM, v400), cargo.Delivery.nextExpectedActivity()); // Unload in Stockholm HandlingEventService.RegisterHandlingEvent(new DateTime(2009, 3, 15), trackingId, SampleLocations.Stockholm.UnLocode, HandlingEventType.Unload); // Check current state - should be ok //Assert.AreEqual(NONE, cargo.Delivery.currentVoyage()); Assert.AreEqual(SampleLocations.Stockholm, cargo.Delivery.LastKnownLocation); Assert.AreEqual(TransportStatus.InPort, cargo.Delivery.TransportStatus); Assert.IsFalse(cargo.Delivery.IsMisdirected); //Assert.AreEqual(new HandlingActivity(CLAIM, STOCKHOLM), cargo.Delivery.nextExpectedActivity()); // Finally, cargo is claimed in Stockholm. This ends the cargo lifecycle from our perspective. HandlingEventService.RegisterHandlingEvent(new DateTime(2009, 3, 16), trackingId, SampleLocations.Stockholm.UnLocode, HandlingEventType.Claim); // Check current state - should be ok //Assert.AreEqual(NONE, cargo.Delivery.currentVoyage()); Assert.AreEqual(SampleLocations.Stockholm, cargo.Delivery.LastKnownLocation); Assert.AreEqual(TransportStatus.Claimed, cargo.Delivery.TransportStatus); Assert.IsFalse(cargo.Delivery.IsMisdirected); //Assert.IsNull(cargo.Delivery.nextExpectedActivity()); }
private bool CalculateUnloadedAtDestination(RouteSpecification specification) { return(LastEvent != null && LastEvent.EventType == HandlingEventType.Unload && specification.Destination == LastEvent.Location); }
/// <summary> /// Creates a new delivery snapshot based on the complete handling history of a cargo, as well /// as its route specification and itinerary. /// </summary> /// <param name="specification">Current route specification.</param> /// <param name="itinerary">Current itinerary.</param> /// <param name="lastHandlingEvent">Most recent handling event.</param> /// <returns>Delivery status description.</returns> public static Delivery DerivedFrom(RouteSpecification specification, Itinerary itinerary, HandlingEvent lastHandlingEvent) { return(new Delivery(lastHandlingEvent, itinerary, specification)); }