From 4e96150f888f070ceabfe0b9b1f89b3a9c2ed6a5 Mon Sep 17 00:00:00 2001 From: innocenat Date: Tue, 27 Jun 2017 00:15:32 +0700 Subject: [PATCH 1/4] Scheduled Dispatch: Fix wrong tooltip on delay button --- src/lang/english.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index fd264eb7f0..c1e7baa763 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5826,7 +5826,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 From 87fdd7059a5303a08a84abf4b27e975e8a684132 Mon Sep 17 00:00:00 2001 From: innocenat Date: Wed, 28 Jun 2017 01:15:30 +0700 Subject: [PATCH 2/4] Partial fix for departure board with scheduled dispatch In case that the scheduled point is a station, the departure board cannot map arrival->departure correctly when showing both in same line is enabled. For individial departure, the departure time and vehicle are not correspondence to each other. There will be train departing at that time, but might not be the vehicle shown in the board. --- src/departures.cpp | 104 ++++++++++++++++++++++++++++++++++++++--- src/departures_gui.cpp | 2 +- src/departures_type.h | 1 + 3 files changed, 100 insertions(+), 7 deletions(-) diff --git a/src/departures.cpp b/src/departures.cpp index 133ea4e9d1..42da3d7a02 100644 --- a/src/departures.cpp +++ b/src/departures.cpp @@ -36,6 +36,12 @@ #include "departures_func.h" #include "departures_type.h" +#include +#include + +/* A cache of used departure time for scheduled dispatch in departure time calculation */ +typedef std::map> schdispatch_cache_t; + /** A scheduled order. */ typedef struct OrderDate { @@ -44,6 +50,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 +77,73 @@ 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; +} + /** * Compute an up-to-date list of departures for a station. * @param station the station to compute the departures of @@ -102,6 +176,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 +218,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 +242,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,9 +311,15 @@ 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; } @@ -289,6 +376,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; @@ -467,7 +555,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 +637,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. */ @@ -608,7 +698,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. */ diff --git a/src/departures_gui.cpp b/src/departures_gui.cpp index c4ac0c5e4a..25305d772b 100644 --- a/src/departures_gui.cpp +++ b/src/departures_gui.cpp @@ -606,7 +606,7 @@ void DeparturesWindow::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); diff --git a/src/departures_type.h b/src/departures_type.h index b71ea62d7e..8ee178e140 100644 --- a/src/departures_type.h +++ b/src/departures_type.h @@ -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() { From 19408cc2c7b5fd109eb97978cf4fff9432862ac8 Mon Sep 17 00:00:00 2001 From: innocenat Date: Wed, 28 Jun 2017 21:05:13 +0700 Subject: [PATCH 3/4] Scheduled Dispatch: Fix mismatch departure board Also fix wrong arrival calculation that could happen. This still does not guarantee in general that vehicle actually departing will be the same as the one shown on the board. --- src/departures.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/src/departures.cpp b/src/departures.cpp index 42da3d7a02..c1b84d6867 100644 --- a/src/departures.cpp +++ b/src/departures.cpp @@ -30,6 +30,7 @@ #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" @@ -38,6 +39,8 @@ #include #include +#include +#include /* A cache of used departure time for scheduled dispatch in departure time calculation */ typedef std::map> schdispatch_cache_t; @@ -144,6 +147,52 @@ static inline bool VehicleSetNextDepartureTime(DateTicks *previous_departure, ui return false; } +static void ScheduledDispatchDepartureLocalFix(DepartureList *departure_list) +{ + /* Seperate departure by each shared order group */ + std::map> 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 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_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 @@ -326,7 +375,8 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5], /* 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; } @@ -454,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; } @@ -659,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; } } @@ -724,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) { @@ -740,6 +794,8 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5], delete od; } + if (type == D_DEPARTURE) ScheduledDispatchDepartureLocalFix(result); + /* Done. Phew! */ return result; } From 708514dc35b7f079fdf1ff93ca0a630844117077 Mon Sep 17 00:00:00 2001 From: innocenat Date: Mon, 3 Jul 2017 16:49:58 +0700 Subject: [PATCH 4/4] Scheduled Dispatch: Fix bug resulting in a hang If a vechile order is decloned. --- src/order_cmd.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index c55ef402e1..453245a35d 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -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);