Merge branch 'scheduled-dispatch-sx' into jgrpp

pull/21/merge
Jonathan G Rennison 7 years ago
commit 4e963be69b

@ -30,12 +30,21 @@
#include "order_base.h"
#include "settings_type.h"
#include "core/smallvec_type.hpp"
#include "core/sort_func.hpp"
#include "date_type.h"
#include "company_type.h"
#include "cargo_type.h"
#include "departures_func.h"
#include "departures_type.h"
#include <map>
#include <set>
#include <vector>
#include <algorithm>
/* A cache of used departure time for scheduled dispatch in departure time calculation */
typedef std::map<uint32, std::set<DateTicksScaled>> schdispatch_cache_t;
/** A scheduled order. */
typedef struct OrderDate
{
@ -44,6 +53,7 @@ typedef struct OrderDate
DateTicks expected_date;///< The date on which the order is expected to complete
Ticks lateness; ///< How late this order is expected to finish
DepartureStatus status; ///< Whether the vehicle has arrived to carry out the order yet
uint scheduled_waiting_time; ///< Scheduled waiting time if scheduled dispatch is used
} OrderDate;
static bool IsDeparture(const Order *order, StationID station) {
@ -70,6 +80,119 @@ static bool IsArrival(const Order *order, StationID station) {
order->GetWaitTime() != 0);
}
static inline bool VehicleSetNextDepartureTime(DateTicks *previous_departure, uint *waiting_time, const DateTicksScaled date_only_scaled, const Vehicle *v, const Order *order, bool arrived_at_timing_point, schdispatch_cache_t &dept_schedule_last)
{
if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) {
/* Loop over all order to find the first waiting order */
for (int j = 0; j < v->orders.list->GetNumOrders(); ++j) {
Order* iterating_order = v->orders.list->GetOrderAt(j);
if (iterating_order->IsWaitTimetabled() && !iterating_order->IsType(OT_IMPLICIT)) {
/* This condition means that we want departure time for the first order */
/* but not if the vehicle has arrived at the first order because the timetable is already shifted */
if (iterating_order == order && !(arrived_at_timing_point && v->cur_implicit_order_index == j)) {
DateTicksScaled actual_departure = -1;
const DateTicksScaled begin_time = v->orders.list->GetScheduledDispatchStartTick();
const uint32 dispatch_duration = v->orders.list->GetScheduledDispatchDuration();
const int32 max_delay = v->orders.list->GetScheduledDispatchDelay();
/* Earliest possible departure according to schedue */
DateTicksScaled earliest_departure = begin_time + v->orders.list->GetScheduledDispatchLastDispatch();
/* Earliest possible departure according to vehicle current timetable */
if (earliest_departure + max_delay < date_only_scaled + *previous_departure + order->GetTravelTime()) {
earliest_departure = date_only_scaled + *previous_departure + order->GetTravelTime() - max_delay - 1;
/* -1 because this number is actually a moment before actual departure */
}
/* Find next available slots */
for (auto current_offset : v->orders.list->GetScheduledDispatch()) {
DateTicksScaled current_departure = begin_time + current_offset;
while (current_departure <= earliest_departure) {
current_departure += dispatch_duration;
}
/* Make sure the slots has not already been used previously in this departure board calculation */
while (dept_schedule_last[v->orders.list->index].count(current_departure) > 0) {
current_departure += dispatch_duration;
}
if (actual_departure == -1 || actual_departure > current_departure) {
actual_departure = current_departure;
}
}
*waiting_time = order->GetWaitTime() + actual_departure - date_only_scaled - *previous_departure - order->GetTravelTime();
*previous_departure = actual_departure - date_only_scaled + order->GetWaitTime();
dept_schedule_last[v->orders.list->index].insert(actual_departure);
/* Return true means that vehicle lateness should be clear from this point onward */
return true;
}
/* This is special case for proper calculation of arrival time. */
if (arrived_at_timing_point && v->cur_implicit_order_index == j) {
*previous_departure += order->GetTravelTime() + order->GetWaitTime();
*waiting_time = -v->lateness_counter + order->GetWaitTime();
return false;
}
break;
} /* if it is first waiting order */
} /* for in order list */
} /* if vehicle is on scheduled dispatch */
/* Not using schedule for this departure time */
*previous_departure += order->GetTravelTime() + order->GetWaitTime();
*waiting_time = 0;
return false;
}
static void ScheduledDispatchDepartureLocalFix(DepartureList *departure_list)
{
/* Seperate departure by each shared order group */
std::map<uint32, std::vector<Departure*>> separated_departure;
for (Departure** departure = departure_list->Begin(); departure != departure_list->End(); departure++) {
separated_departure[(*departure)->vehicle->orders.list->index].push_back(*departure);
}
for (auto& pair : separated_departure) {
auto d_list = pair.second;
/* If the group is scheduled dispatch, then */
if (HasBit(d_list[0]->vehicle->vehicle_flags, VF_SCHEDULED_DISPATCH)) {
/* Separate departure time and sort them ascendently */
std::vector<DateTicksScaled> departure_time_list;
for (const auto& d : d_list) {
departure_time_list.push_back(d->scheduled_date);
}
std::sort(departure_time_list.begin(), departure_time_list.end());
/* Sort the departure list by arrival time */
std::sort(d_list.begin(), d_list.end(), [](const Departure * const &a, const Departure * const &b) -> bool {
DateTicksScaled arr_a = a->scheduled_date - (a->scheduled_waiting_time > 0 ? a->scheduled_waiting_time : a->order->GetWaitTime());
DateTicksScaled arr_b = b->scheduled_date - (b->scheduled_waiting_time > 0 ? b->scheduled_waiting_time : b->order->GetWaitTime());
return arr_a < arr_b;
});
/* Re-assign them sequentially */
for (size_t i = 0; i < d_list.size(); i++) {
const DateTicksScaled arrival = d_list[i]->scheduled_date - (d_list[i]->scheduled_waiting_time > 0 ? d_list[i]->scheduled_waiting_time : d_list[i]->order->GetWaitTime());
d_list[i]->scheduled_waiting_time = departure_time_list[i] - arrival;
d_list[i]->scheduled_date = departure_time_list[i];
if (d_list[i]->scheduled_waiting_time == d_list[i]->order->GetWaitTime()) {
d_list[i]->scheduled_waiting_time = 0;
}
}
}
}
/* Re-sort the departure list */
QSortT<Departure*>(departure_list->Begin(), departure_list->Length(), [](Departure * const *a, Departure * const *b) -> int {
return (*a)->scheduled_date - (*b)->scheduled_date;
});
}
/**
* Compute an up-to-date list of departures for a station.
* @param station the station to compute the departures of
@ -102,6 +225,9 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
/* The scheduled order in next_orders with the earliest expected_date field. */
OrderDate *least_order = NULL;
/* Cache for scheduled departure time */
schdispatch_cache_t schdispatch_last_planned_dispatch;
/* Get all the vehicles stopping at this station. */
/* We do this to get the order which is the first time they will stop at this station. */
/* This order is stored along with some more information. */
@ -141,8 +267,10 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
}
const Order *order = (*v)->GetOrder((*v)->cur_implicit_order_index % (*v)->GetNumOrders());
DateTicksScaled start_date = date_fract_scaled - (*v)->current_order_time;
DateTicks start_date = date_fract_scaled - (*v)->current_order_time;
DepartureStatus status = D_TRAVELLING;
bool should_reset_lateness = false;
uint waiting_time = 0;
/* If the vehicle is stopped in a depot, ignore it. */
if ((*v)->IsStoppedInDepot()) {
@ -163,7 +291,9 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
/* Loop through the vehicle's orders until we've found a suitable order or we've determined that no such order exists. */
/* We only need to consider each order at most once. */
for (int i = (*v)->GetNumOrders(); i > 0; --i) {
start_date += order->GetTravelTime() + order->GetWaitTime();
if (VehicleSetNextDepartureTime(&start_date, &waiting_time, date_only_scaled, *v, order, status == D_ARRIVED, schdispatch_last_planned_dispatch)) {
should_reset_lateness = true;
}
/* If the scheduled departure date is too far in the future, stop. */
if (start_date - (*v)->lateness_counter > max_date) {
@ -230,16 +360,23 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
od->expected_date = start_date;
od->lateness = (*v)->lateness_counter > 0 ? (*v)->lateness_counter : 0;
od->status = status;
od->scheduled_waiting_time = waiting_time;
/* Reset lateness if timing is from scheduled dispatch */
if (should_reset_lateness) {
od->lateness = 0;
}
/* If we are early, use the scheduled date as the expected date. We also take lateness to be zero. */
if ((*v)->lateness_counter < 0 && !(*v)->current_order.IsType(OT_LOADING)) {
if (!should_reset_lateness && (*v)->lateness_counter < 0 && !(*v)->current_order.IsType(OT_LOADING)) {
od->expected_date -= (*v)->lateness_counter;
}
/* Update least_order if this is the current least order. */
if (least_order == NULL) {
least_order = od;
} else if (least_order->expected_date - least_order->lateness - (type == D_ARRIVAL ? least_order->order->GetWaitTime() : 0) > od->expected_date - od->lateness - (type == D_ARRIVAL ? od->order->GetWaitTime() : 0)) {
} else if (int(least_order->expected_date - least_order->lateness - (type == D_ARRIVAL ? (least_order->scheduled_waiting_time > 0 ? least_order->scheduled_waiting_time : least_order->order->GetWaitTime()) : 0)) > int(od->expected_date - od->lateness - (type == D_ARRIVAL ? (od->scheduled_waiting_time > 0 ? od->scheduled_waiting_time : od->order->GetWaitTime()) : 0))) {
/* Somehow my compiler perform an unsigned comparition above so integer cast is required */
least_order = od;
}
@ -289,6 +426,7 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
d->vehicle = least_order->v;
d->type = type;
d->order = least_order->order;
d->scheduled_waiting_time = least_order->scheduled_waiting_time;
/* We'll be going through the order list later, so we need a separate variable for it. */
const Order *order = least_order->order;
@ -366,7 +504,7 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
}
if (c.scheduled_date != 0 && (order->GetTravelTime() != 0 || order->IsTravelTimetabled())) {
c.scheduled_date += order->GetTravelTime();
c.scheduled_date += order->GetTravelTime(); /* TODO smart terminal may not work correctly */
} else {
c.scheduled_date = 0;
}
@ -467,7 +605,7 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
/* Again, we define a station as being called at if the vehicle loads from it. */
/* However, the very first thing we do is use the arrival time as the scheduled time instead of the departure time. */
d->scheduled_date -= order->GetWaitTime();
d->scheduled_date -= d->scheduled_waiting_time > 0 ? d->scheduled_waiting_time : order->GetWaitTime();
const Order *candidate_origin = (order->next == NULL) ? least_order->v->GetFirstOrder() : order->next;
bool found_origin = false;
@ -549,7 +687,9 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
/* Go to the next order so we don't add the current order again. */
order = (order->next == NULL) ? least_order->v->GetFirstOrder() : order->next;
least_order->expected_date += order->GetTravelTime() + order->GetWaitTime();
if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) {
least_order->lateness = 0;
}
/* Go through the order list to find the next candidate departure. */
/* We only need to consider each order at most once. */
@ -569,14 +709,18 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
break;
}
least_order->expected_date += order->GetWaitTime();
if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) {
least_order->lateness = 0;
}
continue;
}
case 2: {
/* Do not take the branch */
order = (order->next == NULL) ? least_order->v->GetFirstOrder() : order->next;
least_order->expected_date += order->GetTravelTime() + order->GetWaitTime();
if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) {
least_order->lateness = 0;
}
continue;
}
}
@ -608,7 +752,9 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
}
order = (order->next == NULL) ? least_order->v->GetFirstOrder() : order->next;
least_order->expected_date += order->GetTravelTime() + order->GetWaitTime();
if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) {
least_order->lateness = 0;
}
}
/* If we didn't find a suitable order for being a departure, then we can ignore this vehicle from now on. */
@ -632,8 +778,8 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
DateTicks odd = od->expected_date - od->lateness;
if (type == D_ARRIVAL) {
lod -= least_order->order->GetWaitTime();
odd -= od->order->GetWaitTime();
lod -= least_order->scheduled_waiting_time > 0 ? least_order->scheduled_waiting_time : least_order->order->GetWaitTime();
odd -= od->scheduled_waiting_time > 0 ? od->scheduled_waiting_time : od->order->GetWaitTime();
}
if (lod > odd && od->expected_date - od->lateness < max_date) {
@ -648,6 +794,8 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5],
delete od;
}
if (type == D_DEPARTURE) ScheduledDispatchDepartureLocalFix(result);
/* Done. Phew! */
return result;
}

@ -606,7 +606,7 @@ void DeparturesWindow<Twaypoint>::DrawDeparturesListItems(const Rect &r) const
/* Time */
SetDParam(0, d->scheduled_date);
SetDParam(1, d->scheduled_date - d->order->GetWaitTime());
SetDParam(1, d->scheduled_date - (d->scheduled_waiting_time > 0 ? d->scheduled_waiting_time : d->order->GetWaitTime()));
ltr ? DrawString( text_left, text_left + time_width, y + 1, time_str)
: DrawString(text_right - time_width, text_right, y + 1, time_str);

@ -74,6 +74,7 @@ typedef struct Departure {
DepartureType type; ///< The type of the departure (departure or arrival)
const Vehicle *vehicle; ///< The vehicle performing this departure
const Order *order; ///< The order corresponding to this departure
uint scheduled_waiting_time; ///< Scheduled waiting time if scheduled dispatch is used
Departure() : terminus(INVALID_STATION), via(INVALID_STATION), calling_at(), vehicle(NULL) { }
~Departure()
{

@ -5829,7 +5829,7 @@ STR_SCHDISPATCH_START :{BLACK}Start Da
STR_SCHDISPATCH_START_TOOLTIP :{BLACK}Select a date to start this schedule.
STR_SCHDISPATCH_START_CAPTION_MINUTE :{BLACK}Start time (hhmm)
STR_SCHDISPATCH_DELAY :{BLACK}Delay
STR_SCHDISPATCH_DELAY_TOOLTIP :{BLACK}Select a date to start this schedule.
STR_SCHDISPATCH_DELAY_TOOLTIP :{BLACK}Set maximum delay until a slot is skipped.
STR_SCHDISPATCH_DELAY_CAPTION_MINUTE :{BLACK}Delay (minute)
STR_SCHDISPATCH_DELAY_CAPTION_DAY :{BLACK}Delay (day)
STR_SCHDISPATCH_RESET_LAST_DISPATCH :{BLACK}Reset Last Dispatched

@ -1132,6 +1132,11 @@ void InsertOrder(Vehicle *v, Order *new_o, VehicleOrderID sel_ord)
static CommandCost DecloneOrder(Vehicle *dst, DoCommandFlag flags)
{
if (flags & DC_EXEC) {
/* Clear cheduled dispatch flag if any */
if (HasBit(dst->vehicle_flags, VF_SCHEDULED_DISPATCH)) {
ClrBit(dst->vehicle_flags, VF_SCHEDULED_DISPATCH);
}
DeleteVehicleOrders(dst);
InvalidateVehicleOrder(dst, VIWD_REMOVE_ALL_ORDERS);
InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0);

Loading…
Cancel
Save