From 6bb2d3d50ceac26a8ff46acf264915bb646e5fb9 Mon Sep 17 00:00:00 2001 From: Pablo Hoch Date: Thu, 23 Nov 2023 17:23:13 +0100 Subject: [PATCH] RSL: Fix revert forecast for routes with major delays (#410) --- docs/api/schemas/motis/paxmon.yaml | 22 ++++++++------- modules/paxforecast/src/monitoring_update.cc | 2 +- .../include/motis/paxmon/monitoring_event.h | 4 ++- .../include/motis/paxmon/rt_update_context.h | 8 +++++- .../paxmon/include/motis/paxmon/statistics.h | 2 ++ modules/paxmon/src/api/metrics.cc | 6 ++++- modules/paxmon/src/paxmon.cc | 5 ++-- modules/paxmon/src/rt_updates.cc | 27 +++++++++++++++++-- modules/paxmon/src/stats_writer.cc | 5 ++-- protocol/paxmon/PaxMonMetricsResponse.fbs | 1 + protocol/paxmon/PaxMonUpdate.fbs | 1 + ui/rsl/src/api/protocol/motis/paxmon.ts | 4 ++- .../components/transfers/TransferDetails.tsx | 11 +++++--- 13 files changed, 75 insertions(+), 23 deletions(-) diff --git a/docs/api/schemas/motis/paxmon.yaml b/docs/api/schemas/motis/paxmon.yaml index a7342e673..21ef78f9f 100644 --- a/docs/api/schemas/motis/paxmon.yaml +++ b/docs/api/schemas/motis/paxmon.yaml @@ -550,7 +550,7 @@ PaxMonFilterGroupsRequest: If set to `false`, the reroute log information in the response is empty. - examples: 0 + examples: false filter_by_start: description: | A list of station IDs. If non-empty, only groups departing at one @@ -558,7 +558,7 @@ PaxMonFilterGroupsRequest: Set to an empty array to disable this filter. examples: - - [] + - [ ] filter_by_destination: description: | A list of station IDs. If non-empty, only groups arriving at one @@ -566,7 +566,7 @@ PaxMonFilterGroupsRequest: Set to an empty array to disable this filter. examples: - - [] + - [ ] filter_by_via: description: | A list of station IDs. If non-empty, only groups with a transfer at one @@ -578,7 +578,7 @@ PaxMonFilterGroupsRequest: Set to an empty array to disable this filter. examples: - - [] + - [ ] filter_by_group_id: description: | A list of group IDs. If non-empty, only these groups are included @@ -586,15 +586,15 @@ PaxMonFilterGroupsRequest: Set to an empty array to disable this filter. examples: - - [] + - [ ] filter_by_data_source: description: | A list of group data sources. If non-empty, only groups with these - data sources are included in the repsonse. + data sources are included in the response. Set to an empty array to disable this filter. examples: - - [] + - [ ] filter_by_train_nr: description: | A list of train numbers. If non-empty, only groups using a train @@ -602,7 +602,7 @@ PaxMonFilterGroupsRequest: Set to an empty array to disable this filter. examples: - - [] + - [ ] filter_by_time: description: | Filter groups by time. @@ -629,7 +629,7 @@ PaxMonFilterGroupsRequest: Set to an empty array to disable this filter. examples: - - [] + - [ ] PaxMonGroupWithStats: description: TODO fields: @@ -1708,6 +1708,10 @@ PaxMonMetrics: total_timing: description: > Total time spent by passenger monitoring and forecast in milliseconds + reactivated_group_routes: + description: > + Number of passenger group routes that were affected by a real-time + update that are no longer broken, but were broken before this update PaxMonMetricsResponse: description: | Contains metrics for the requested universe. diff --git a/modules/paxforecast/src/monitoring_update.cc b/modules/paxforecast/src/monitoring_update.cc index b78936e55..44a874004 100644 --- a/modules/paxforecast/src/monitoring_update.cc +++ b/modules/paxforecast/src/monitoring_update.cc @@ -313,7 +313,7 @@ void handle_unbroken_transfers(paxforecast& mod, universe& uv, auto unbroken_transfers = std::vector{}; for (auto const& event : *mon_update->events()) { - if (event->type() != PaxMonEventType_NO_PROBLEM) { + if (event->type() != PaxMonEventType_REACTIVATED) { continue; } diff --git a/modules/paxmon/include/motis/paxmon/monitoring_event.h b/modules/paxmon/include/motis/paxmon/monitoring_event.h index acc607a73..8adfb5c02 100644 --- a/modules/paxmon/include/motis/paxmon/monitoring_event.h +++ b/modules/paxmon/include/motis/paxmon/monitoring_event.h @@ -16,7 +16,8 @@ namespace motis::paxmon { enum class monitoring_event_type : std::uint8_t { NO_PROBLEM, BROKEN_TRANSFER, - MAJOR_DELAY_EXPECTED + MAJOR_DELAY_EXPECTED, + REACTIVATED }; inline std::ostream& operator<<(std::ostream& out, @@ -27,6 +28,7 @@ inline std::ostream& operator<<(std::ostream& out, return out << "BROKEN_TRANSFER"; case monitoring_event_type::MAJOR_DELAY_EXPECTED: return out << "MAJOR_DELAY_EXPECTED"; + case monitoring_event_type::REACTIVATED: return out << "REACTIVATED"; } return out; } diff --git a/modules/paxmon/include/motis/paxmon/rt_update_context.h b/modules/paxmon/include/motis/paxmon/rt_update_context.h index 5ec7ef44e..34290f907 100644 --- a/modules/paxmon/include/motis/paxmon/rt_update_context.h +++ b/modules/paxmon/include/motis/paxmon/rt_update_context.h @@ -2,14 +2,20 @@ #include -#include "motis/core/schedule/trip.h" +#include "motis/hash_map.h" #include "motis/paxmon/index_types.h" namespace motis::paxmon { struct rt_update_context { + void reset() { + group_routes_affected_by_last_update_.clear(); + previous_broken_status_.clear(); + } + std::set group_routes_affected_by_last_update_; + mcd::hash_map previous_broken_status_; }; } // namespace motis::paxmon diff --git a/modules/paxmon/include/motis/paxmon/statistics.h b/modules/paxmon/include/motis/paxmon/statistics.h index 7c418f607..19694b0b7 100644 --- a/modules/paxmon/include/motis/paxmon/statistics.h +++ b/modules/paxmon/include/motis/paxmon/statistics.h @@ -46,6 +46,7 @@ struct tick_statistics { ok_group_routes_ += rhs.ok_group_routes_; broken_group_routes_ += rhs.broken_group_routes_; major_delay_group_routes_ += rhs.major_delay_group_routes_; + reactivated_group_routes_ += rhs.reactivated_group_routes_; t_reachability_ += rhs.t_reachability_; t_localization_ += rhs.t_localization_; @@ -86,6 +87,7 @@ struct tick_statistics { std::uint64_t ok_group_routes_{}; std::uint64_t broken_group_routes_{}; std::uint64_t major_delay_group_routes_{}; + std::uint64_t reactivated_group_routes_{}; // timing (ms) std::uint64_t t_reachability_{}; diff --git a/modules/paxmon/src/api/metrics.cc b/modules/paxmon/src/api/metrics.cc index a64ddc261..9a13bf18c 100644 --- a/modules/paxmon/src/api/metrics.cc +++ b/modules/paxmon/src/api/metrics.cc @@ -19,12 +19,14 @@ msg_ptr metrics(paxmon_data& data, msg_ptr const& msg) { auto const metrics_to_fbs = [&](metrics_storage const& m) { std::vector affected_group_routes, ok_group_routes, - broken_group_routes, major_delay_group_routes, total_timing; + broken_group_routes, major_delay_group_routes, reactivated_group_routes, + total_timing; affected_group_routes.reserve(m.size()); ok_group_routes.reserve(m.size()); broken_group_routes.reserve(m.size()); major_delay_group_routes.reserve(m.size()); + reactivated_group_routes.reserve(m.size()); total_timing.reserve(m.size()); for (auto i = 0UL; i < m.size(); ++i) { @@ -33,6 +35,7 @@ msg_ptr metrics(paxmon_data& data, msg_ptr const& msg) { ok_group_routes.push_back(entry.ok_group_routes_); broken_group_routes.push_back(entry.broken_group_routes_); major_delay_group_routes.push_back(entry.major_delay_group_routes_); + reactivated_group_routes.push_back(entry.reactivated_group_routes_); total_timing.push_back(entry.t_rt_updates_applied_total_); } @@ -40,6 +43,7 @@ msg_ptr metrics(paxmon_data& data, msg_ptr const& msg) { mc, m.start_time(), m.size(), mc.CreateVector(affected_group_routes), mc.CreateVector(ok_group_routes), mc.CreateVector(broken_group_routes), mc.CreateVector(major_delay_group_routes), + mc.CreateVector(reactivated_group_routes), mc.CreateVector(total_timing)); }; diff --git a/modules/paxmon/src/paxmon.cc b/modules/paxmon/src/paxmon.cc index f4b297e62..1e6e4a3cc 100644 --- a/modules/paxmon/src/paxmon.cc +++ b/modules/paxmon/src/paxmon.cc @@ -740,9 +740,10 @@ void paxmon::rt_updates_applied(universe& uv, schedule const& sched) { << uv.rt_update_ctx_.group_routes_affected_by_last_update_.size() << " passenger group routes"; - uv.rt_update_ctx_.group_routes_affected_by_last_update_.clear(); + uv.rt_update_ctx_.reset(); LOG(info) << "passenger group routes: " << uv.tick_stats_.ok_group_routes_ - << " ok, " << uv.tick_stats_.broken_group_routes_ << " broken"; + << " ok, " << uv.tick_stats_.broken_group_routes_ << " broken, " + << uv.tick_stats_.reactivated_group_routes_ << " reactivated"; MOTIS_STOP_TIMING(total); uv.tick_stats_.t_rt_updates_applied_total_ = diff --git a/modules/paxmon/src/rt_updates.cc b/modules/paxmon/src/rt_updates.cc index 34c3d5ac0..d85691aba 100644 --- a/modules/paxmon/src/rt_updates.cc +++ b/modules/paxmon/src/rt_updates.cc @@ -31,7 +31,17 @@ namespace motis::paxmon { void check_broken_interchanges( universe& uv, schedule const& sched, std::vector const& updated_interchange_edges) { - std::set broken_interchanges; + + auto broken_interchanges = std::set{}; + + auto const update_previous_broken_status = [&](auto const& pgwr, + auto const& gr) { + if (uv.rt_update_ctx_.previous_broken_status_.find(pgwr) == + end(uv.rt_update_ctx_.previous_broken_status_)) { + uv.rt_update_ctx_.previous_broken_status_[pgwr] = gr.broken_; + } + }; + for (auto& icei : updated_interchange_edges) { auto* ice = icei.get(uv); if (ice->type_ != edge_type::INTERCHANGE) { @@ -59,6 +69,7 @@ void check_broken_interchanges( } for (auto const& pgwr : uv.pax_connection_info_.group_routes(ice->pci_)) { auto& gr = uv.passenger_groups_.route(pgwr); + update_previous_broken_status(pgwr, gr); gr.broken_ = true; if (gr.probability_ == 0) { continue; @@ -71,10 +82,12 @@ void check_broken_interchanges( ice->broken_ = false; for (auto const& pgwr : uv.pax_connection_info_.group_routes(ice->pci_)) { + update_previous_broken_status(pgwr, uv.passenger_groups_.route(pgwr)); uv.rt_update_ctx_.group_routes_affected_by_last_update_.insert(pgwr); } for (auto const& pgwr : uv.pax_connection_info_.broken_group_routes(ice->pci_)) { + update_previous_broken_status(pgwr, uv.passenger_groups_.route(pgwr)); uv.rt_update_ctx_.group_routes_affected_by_last_update_.insert(pgwr); } } @@ -192,10 +205,17 @@ void handle_rt_update(universe& uv, schedule const& sched, } monitoring_event_type get_monitoring_event_type( + universe const& uv, passenger_group_with_route const& pgwr, group_route const& gr, reachability_info const& reachability, int const arrival_delay_threshold) { if (!reachability.ok_) { return monitoring_event_type::BROKEN_TRANSFER; + } else if (auto const it = + uv.rt_update_ctx_.previous_broken_status_.find(pgwr); + it != end(uv.rt_update_ctx_.previous_broken_status_) && + it->second) { + // route was broken before + return monitoring_event_type::REACTIVATED; } else if (arrival_delay_threshold >= 0 && gr.planned_arrival_time_ != INVALID_TIME && gr.estimated_delay_ >= arrival_delay_threshold) { @@ -286,7 +306,7 @@ std::vector update_affected_groups(universe& uv, MOTIS_STOP_TIMING(localization); auto const event_type = get_monitoring_event_type( - gr, reachability, uv.arrival_delay_threshold_); + uv, pgwr, gr, reachability, uv.arrival_delay_threshold_); auto const expected_arrival_time = event_type == monitoring_event_type::BROKEN_TRANSFER ? INVALID_TIME @@ -326,6 +346,9 @@ std::vector update_affected_groups(universe& uv, case monitoring_event_type::MAJOR_DELAY_EXPECTED: ++uv.tick_stats_.major_delay_group_routes_; break; + case monitoring_event_type::REACTIVATED: + ++uv.tick_stats_.reactivated_group_routes_; + break; } } diff --git a/modules/paxmon/src/stats_writer.cc b/modules/paxmon/src/stats_writer.cc index 59d4ab1a9..04f4d63e5 100644 --- a/modules/paxmon/src/stats_writer.cc +++ b/modules/paxmon/src/stats_writer.cc @@ -29,6 +29,7 @@ void stats_writer::write_header() { << "ok_group_routes" << "broken_group_routes" << "major_delay_group_routes" + << "reactivated_group_routes" // << "t_reachability" << "t_localization" @@ -53,8 +54,8 @@ void stats_writer::write_tick(const tick_statistics& ts) { << ts.rt_delay_schedule_updates_ // << ts.affected_group_routes_ << ts.ok_group_routes_ - << ts.broken_group_routes_ - << ts.major_delay_group_routes_ + << ts.broken_group_routes_ << ts.major_delay_group_routes_ + << ts.reactivated_group_routes_ // << ts.t_reachability_ << ts.t_localization_ << ts.t_update_load_ << ts.t_fbs_events_ << ts.t_publish_ diff --git a/protocol/paxmon/PaxMonMetricsResponse.fbs b/protocol/paxmon/PaxMonMetricsResponse.fbs index fa03b37e6..6b033eff0 100644 --- a/protocol/paxmon/PaxMonMetricsResponse.fbs +++ b/protocol/paxmon/PaxMonMetricsResponse.fbs @@ -8,6 +8,7 @@ table PaxMonMetrics { ok_group_routes: [ulong]; broken_group_routes: [ulong]; major_delay_group_routes: [ulong]; + reactivated_group_routes: [ulong]; total_timing: [ulong]; // ms } diff --git a/protocol/paxmon/PaxMonUpdate.fbs b/protocol/paxmon/PaxMonUpdate.fbs index c5343bd57..c87e9a412 100644 --- a/protocol/paxmon/PaxMonUpdate.fbs +++ b/protocol/paxmon/PaxMonUpdate.fbs @@ -8,6 +8,7 @@ enum PaxMonEventType : byte { NO_PROBLEM, BROKEN_TRANSFER, MAJOR_DELAY_EXPECTED, + REACTIVATED, } table PaxMonEvent { diff --git a/ui/rsl/src/api/protocol/motis/paxmon.ts b/ui/rsl/src/api/protocol/motis/paxmon.ts index 0008ba8d5..f15f6c50c 100644 --- a/ui/rsl/src/api/protocol/motis/paxmon.ts +++ b/ui/rsl/src/api/protocol/motis/paxmon.ts @@ -196,7 +196,8 @@ export interface PaxMonReachability { export type PaxMonEventType = | "NO_PROBLEM" | "BROKEN_TRANSFER" - | "MAJOR_DELAY_EXPECTED"; + | "MAJOR_DELAY_EXPECTED" + | "REACTIVATED"; // paxmon/PaxMonUpdate.fbs export interface PaxMonEvent { @@ -1079,6 +1080,7 @@ export interface PaxMonMetrics { ok_group_routes: number[]; broken_group_routes: number[]; major_delay_group_routes: number[]; + reactivated_group_routes: number[]; total_timing: number[]; } diff --git a/ui/rsl/src/components/transfers/TransferDetails.tsx b/ui/rsl/src/components/transfers/TransferDetails.tsx index ebcfae68a..b56a8cc96 100644 --- a/ui/rsl/src/components/transfers/TransferDetails.tsx +++ b/ui/rsl/src/components/transfers/TransferDetails.tsx @@ -87,6 +87,12 @@ function TransferDetails({ transferId }: TransferDetailsProps): ReactNode { ? (departure.current_time - arrival.current_time) / 60 : null; + const missingTransferTime = + availableTransferTime !== null && + availableTransferTime < data.info.transfer_time + ? data.info.transfer_time - availableTransferTime + : null; + const groupedByDestination = data.groups.reduce( (result, group) => { const destinationStationId = getDestinationStation(group).id; @@ -192,10 +198,9 @@ function TransferDetails({ transferId }: TransferDetailsProps): ReactNode { )} - {availableTransferTime !== null && ( + {missingTransferTime !== null && (
- Fehlende Umstiegszeit:{" "} - {formatShortDuration(data.info.transfer_time - availableTransferTime)} + Fehlende Umstiegszeit: {formatShortDuration(missingTransferTime)}
)}