6 "github.com/go-kit/kit/examples/shipping/location"
7 "github.com/go-kit/kit/examples/shipping/voyage"
10 // Delivery is the actual transportation of the cargo, as opposed to the
11 // customer requirement (RouteSpecification) and the plan (Itinerary).
12 type Delivery struct {
14 RouteSpecification RouteSpecification
15 RoutingStatus RoutingStatus
16 TransportStatus TransportStatus
17 NextExpectedActivity HandlingActivity
18 LastEvent HandlingEvent
19 LastKnownLocation location.UNLocode
20 CurrentVoyage voyage.Number
23 IsUnloadedAtDestination bool
26 // UpdateOnRouting creates a new delivery snapshot to reflect changes in
27 // routing, i.e. when the route specification or the itinerary has changed but
28 // no additional handling of the cargo has been performed.
29 func (d Delivery) UpdateOnRouting(rs RouteSpecification, itinerary Itinerary) Delivery {
30 return newDelivery(d.LastEvent, itinerary, rs)
33 // IsOnTrack checks if the delivery is on track.
34 func (d Delivery) IsOnTrack() bool {
35 return d.RoutingStatus == Routed && !d.IsMisdirected
38 // DeriveDeliveryFrom creates a new delivery snapshot based on the complete
39 // handling history of a cargo, as well as its route specification and
41 func DeriveDeliveryFrom(rs RouteSpecification, itinerary Itinerary, history HandlingHistory) Delivery {
42 lastEvent, _ := history.MostRecentlyCompletedEvent()
43 return newDelivery(lastEvent, itinerary, rs)
46 // newDelivery creates a up-to-date delivery based on an handling event,
47 // itinerary and a route specification.
48 func newDelivery(lastEvent HandlingEvent, itinerary Itinerary, rs RouteSpecification) Delivery {
50 routingStatus = calculateRoutingStatus(itinerary, rs)
51 transportStatus = calculateTransportStatus(lastEvent)
52 lastKnownLocation = calculateLastKnownLocation(lastEvent)
53 isMisdirected = calculateMisdirectedStatus(lastEvent, itinerary)
54 isUnloadedAtDestination = calculateUnloadedAtDestination(lastEvent, rs)
55 currentVoyage = calculateCurrentVoyage(transportStatus, lastEvent)
61 RouteSpecification: rs,
62 RoutingStatus: routingStatus,
63 TransportStatus: transportStatus,
64 LastKnownLocation: lastKnownLocation,
65 IsMisdirected: isMisdirected,
66 IsUnloadedAtDestination: isUnloadedAtDestination,
67 CurrentVoyage: currentVoyage,
70 d.NextExpectedActivity = calculateNextExpectedActivity(d)
71 d.ETA = calculateETA(d)
76 // Below are internal functions used when creating a new delivery.
78 func calculateRoutingStatus(itinerary Itinerary, rs RouteSpecification) RoutingStatus {
79 if itinerary.Legs == nil {
83 if rs.IsSatisfiedBy(itinerary) {
90 func calculateMisdirectedStatus(event HandlingEvent, itinerary Itinerary) bool {
91 if event.Activity.Type == NotHandled {
95 return !itinerary.IsExpected(event)
98 func calculateUnloadedAtDestination(event HandlingEvent, rs RouteSpecification) bool {
99 if event.Activity.Type == NotHandled {
103 return event.Activity.Type == Unload && rs.Destination == event.Activity.Location
106 func calculateTransportStatus(event HandlingEvent) TransportStatus {
107 switch event.Activity.Type {
111 return OnboardCarrier
124 func calculateLastKnownLocation(event HandlingEvent) location.UNLocode {
125 return event.Activity.Location
128 func calculateNextExpectedActivity(d Delivery) HandlingActivity {
130 return HandlingActivity{}
133 switch d.LastEvent.Activity.Type {
135 return HandlingActivity{Type: Receive, Location: d.RouteSpecification.Origin}
137 l := d.Itinerary.Legs[0]
138 return HandlingActivity{Type: Load, Location: l.LoadLocation, VoyageNumber: l.VoyageNumber}
140 for _, l := range d.Itinerary.Legs {
141 if l.LoadLocation == d.LastEvent.Activity.Location {
142 return HandlingActivity{Type: Unload, Location: l.UnloadLocation, VoyageNumber: l.VoyageNumber}
146 for i, l := range d.Itinerary.Legs {
147 if l.UnloadLocation == d.LastEvent.Activity.Location {
148 if i < len(d.Itinerary.Legs)-1 {
149 return HandlingActivity{Type: Load, Location: d.Itinerary.Legs[i+1].LoadLocation, VoyageNumber: d.Itinerary.Legs[i+1].VoyageNumber}
152 return HandlingActivity{Type: Claim, Location: l.UnloadLocation}
157 return HandlingActivity{}
160 func calculateCurrentVoyage(transportStatus TransportStatus, event HandlingEvent) voyage.Number {
161 if transportStatus == OnboardCarrier && event.Activity.Type != NotHandled {
162 return event.Activity.VoyageNumber
165 return voyage.Number("")
168 func calculateETA(d Delivery) time.Time {
173 return d.Itinerary.FinalArrivalTime()