diff --git a/map/framework.cpp b/map/framework.cpp index 06b83b3c8..05f92c748 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -296,6 +296,7 @@ Framework::Framework(FrameworkParams const & params, bool loadMaps) [this]() -> power_management::PowerManager const & { return m_powerManager; }), static_cast(*this)) , m_trafficManager(m_featuresFetcher.GetDataSource(), + [this]() -> storage::CountryInfoGetter const & { return GetCountryInfoGetter(); }, [this](string const & id) -> string { return m_storage.GetParentIdFor(id); }, bind(&Framework::GetMwmsByRect, this, _1, false /* rough */), kMaxTrafficCacheSizeBytes, m_routingManager.RoutingSession()) @@ -388,6 +389,8 @@ Framework::Framework(FrameworkParams const & params, bool loadMaps) if (loadMaps) LoadMapsSync(); + + m_trafficManager.Start(); } Framework::~Framework() diff --git a/map/traffic_manager.cpp b/map/traffic_manager.cpp index dfcb55898..6d1dde719 100644 --- a/map/traffic_manager.cpp +++ b/map/traffic_manager.cpp @@ -56,13 +56,13 @@ TrafficManager::CacheEntry::CacheEntry(time_point const & requestT , m_lastAvailability(traffic::TrafficInfo::Availability::Unknown) {} -TrafficManager::TrafficManager(DataSource & dataSource, +TrafficManager::TrafficManager(DataSource & dataSource, CountryInfoGetterFn countryInfoGetter, const CountryParentNameGetterFn &countryParentNameGetter, GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes, traffic::TrafficObserver & observer) : m_dataSource(dataSource) + , m_countryInfoGetterFn(countryInfoGetter) , m_countryParentNameGetterFn(countryParentNameGetter) - , m_traffDecoder(dataSource, countryParentNameGetter, m_messageCache) , m_getMwmsByRectFn(getMwmsByRectFn) , m_observer(observer) , m_currentDataVersion(0) @@ -129,6 +129,13 @@ void TrafficManager::SetEnabled(bool enabled) m_observer.OnTrafficInfoClear(); } +void TrafficManager::Start() +{ + m_traffDecoder = make_unique(m_dataSource, m_countryInfoGetterFn, + m_countryParentNameGetterFn, m_messageCache); + m_isStarted = true; +} + void TrafficManager::Clear() { // TODO no longer needed @@ -211,7 +218,6 @@ void TrafficManager::UpdateActiveMwms(m2::RectD const & rect, { std::lock_guard lock(m_mutex); - m_isStarted = true; m_activeMwmsChanged = true; activeMwms.clear(); for (auto const & mwm : mwms) @@ -462,7 +468,7 @@ void TrafficManager::DecodeFirstMessage() } LOG(LINFO, (" ", message.m_id, ":", message)); - m_traffDecoder.DecodeMessage(message); + m_traffDecoder->DecodeMessage(message); // store message in cache m_messageCache.insert_or_assign(message.m_id, message); // store message coloring in AllMwmColoring diff --git a/map/traffic_manager.hpp b/map/traffic_manager.hpp index b7f3eca65..7625fc2b9 100644 --- a/map/traffic_manager.hpp +++ b/map/traffic_manager.hpp @@ -9,6 +9,8 @@ #include "indexer/mwm_set.hpp" +#include "storage/country_info_getter.hpp" + #include "traffxml/traff_decoder.hpp" #include "traffxml/traff_model.hpp" @@ -34,6 +36,7 @@ class TrafficManager final { public: + using CountryInfoGetterFn = std::function; using CountryParentNameGetterFn = std::function; /** @@ -75,6 +78,7 @@ public: using GetMwmsByRectFn = std::function(m2::RectD const &)>; TrafficManager(DataSource & dataSource, + CountryInfoGetterFn countryInfoGetter, CountryParentNameGetterFn const & countryParentNameGetter, GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes, traffic::TrafficObserver & observer); @@ -109,6 +113,26 @@ public: */ bool IsEnabled() const; + /** + * @brief Starts the traffic manager. + * + * After creation, the traffic manager will not poll any sources or process any feeds until it is + * started. Feeds received through `Push()` will be added to the queue before the traffic manager + * is started, but will not be processed any further until the traffic manager is started. + * + * MWMs must be loaded before starting the traffic manager. + * + * @todo Currently, all MWMs must be loaded before calling `Start()`, as MWMs loaded after that + * will not get picked up. We need to extend `TrafficManager` to react to MWMs being added (and + * removed) – note that this affects the data source, not the set of active MWMs. + * + * @todo Start is currently not integrated with state or pause/resume logic (all of which might + * not be fully implemented ATM). If the traffic manager is not started, no message processing + * (other than filling the queue and deduplication) will take place, regardless of state. Starting + * the traffic manager will not change the state it reports. + */ + void Start(); + void UpdateViewport(ScreenBase const & screen); void UpdateMyPosition(MyPosition const & myPosition); @@ -400,6 +424,7 @@ private: } DataSource & m_dataSource; + CountryInfoGetterFn m_countryInfoGetterFn; CountryParentNameGetterFn m_countryParentNameGetterFn; GetMwmsByRectFn m_getMwmsByRectFn; traffic::TrafficObserver & m_observer; @@ -535,7 +560,7 @@ private: * * Used to decode TraFF locations into road segments on the map. */ - traffxml::DefaultTraffDecoder m_traffDecoder; + std::unique_ptr m_traffDecoder; /** * @brief Map between MWM IDs and their colorings. diff --git a/routing/index_router.cpp b/routing/index_router.cpp index d87ed514a..7f30fe0bd 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -276,6 +276,40 @@ IndexRouter::IndexRouter(VehicleType vehicleType, bool loadAltitudes, CHECK(m_directionsEngine, ()); } +IndexRouter::IndexRouter(VehicleType vehicleType, bool loadAltitudes, + CountryParentNameGetterFn const & countryParentNameGetterFn, + TCountryFileFn const & countryFileFn, CountryRectFn const & countryRectFn, + shared_ptr numMwmIds, unique_ptr> numMwmTree, + DataSource & dataSource) + : m_vehicleType(vehicleType) + , m_loadAltitudes(loadAltitudes) + , m_name("astar-bidirectional-" + ToString(m_vehicleType)) + , m_dataSource(dataSource, numMwmIds) + , m_vehicleModelFactory(CreateVehicleModelFactory(m_vehicleType, countryParentNameGetterFn)) + , m_countryFileFn(countryFileFn) + , m_countryRectFn(countryRectFn) + , m_numMwmIds(std::move(numMwmIds)) + , m_numMwmTree(std::move(numMwmTree)) + , m_trafficStash(nullptr) + , m_roadGraph(m_dataSource, + vehicleType == VehicleType::Pedestrian || vehicleType == VehicleType::Transit + ? IRoadGraph::Mode::IgnoreOnewayTag + : IRoadGraph::Mode::ObeyOnewayTag, + m_vehicleModelFactory) + , m_estimator(EdgeEstimator::Create( + m_vehicleType, CalcMaxSpeed(*m_numMwmIds, *m_vehicleModelFactory, m_vehicleType), + CalcOffroadSpeed(*m_vehicleModelFactory), m_trafficStash, + &dataSource, m_numMwmIds)) + , m_directionsEngine(CreateDirectionsEngine(m_vehicleType, m_numMwmIds, m_dataSource)) + , m_countryParentNameGetterFn(countryParentNameGetterFn) +{ + CHECK(!m_name.empty(), ()); + CHECK(m_numMwmIds, ()); + CHECK(m_numMwmTree, ()); + CHECK(m_estimator, ()); + CHECK(m_directionsEngine, ()); +} + unique_ptr IndexRouter::MakeSingleMwmWorldGraph() { auto worldGraph = MakeWorldGraph(); diff --git a/routing/index_router.hpp b/routing/index_router.hpp index 6590854f9..4d433584f 100644 --- a/routing/index_router.hpp +++ b/routing/index_router.hpp @@ -65,6 +65,23 @@ public: m2::PointD const m_direction; }; + /** + * @brief Creates a new `IndexRouter` instance. + * + * This is the constructor intended for normal routing. It requires a `TrafficCache` argument, + * from which it may create a traffic stash so the traffic situation can be considered for the + * route, depending on the vehicle type. + * + * @param vehicleType The vehichle type + * @param loadAltitudes Whether to load altitudes + * @param countryParentNameGetterFn Function which converts a country name into the name of its parent country) + * @param countryFileFn Function which converts a pointer to its country name + * @param countryRectFn Function which returns the rect for a country + * @param numMwmIds + * @param numMwmTree + * @param trafficCache The traffic cache (used only if `vehicleType` is `VehicleType::Car`) + * @param dataSource The MWM data source + */ IndexRouter(VehicleType vehicleType, bool loadAltitudes, CountryParentNameGetterFn const & countryParentNameGetterFn, TCountryFileFn const & countryFileFn, CountryRectFn const & countryRectFn, @@ -89,6 +106,29 @@ public: VehicleType GetVehicleType() const { return m_vehicleType; } +protected: + /** + * @brief Creates a new `IndexRouter` instance. + * + * This constructor is intended for use by the TraFF decoder, not for normal routing. It lacks the + * `TrafficCache` argument and never creates a traffic stash. This creates a router instance which + * ignores the traffic situation, regardless of the vehicle type. + * + * @param vehicleType The vehichle type + * @param loadAltitudes Whether to load altitudes + * @param countryParentNameGetterFn Function which converts a country name into the name of its parent country) + * @param countryFileFn Function which converts a pointer to its country name + * @param countryRectFn Function which returns the rect for a country + * @param numMwmIds + * @param numMwmTree + * @param dataSource The MWM data source + */ + IndexRouter(VehicleType vehicleType, bool loadAltitudes, + CountryParentNameGetterFn const & countryParentNameGetterFn, + TCountryFileFn const & countryFileFn, CountryRectFn const & countryRectFn, + std::shared_ptr numMwmIds, std::unique_ptr> numMwmTree, + DataSource & dataSource); + private: RouterResultCode CalculateSubrouteJointsMode(IndexGraphStarter & starter, RouterDelegate const & delegate, diff --git a/traffxml/traff_decoder.cpp b/traffxml/traff_decoder.cpp index 00093b01b..4b8557217 100644 --- a/traffxml/traff_decoder.cpp +++ b/traffxml/traff_decoder.cpp @@ -6,10 +6,18 @@ #include "openlr/openlr_decoder.hpp" #include "openlr/openlr_model.hpp" +#include "routing/async_router.hpp" +#include "routing/checkpoints.hpp" #include "routing/maxspeeds.hpp" +#include "routing/route.hpp" +#include "routing/router_delegate.hpp" #include "routing_common/maxspeed_conversion.hpp" +#include "storage/routing_helpers.hpp" + +#include "traffic/traffic_cache.hpp" + namespace traffxml { // Number of worker threads for the OpenLR decoder @@ -21,10 +29,15 @@ namespace traffxml */ auto constexpr kNumDecoderThreads = 1; -TraffDecoder::TraffDecoder(DataSource & dataSource, +// Timeout for the router in seconds, used by RoutingTraffDecoder +// TODO set to a sensible value +auto constexpr kRouterTimeoutSec = 30; + +TraffDecoder::TraffDecoder(DataSource & dataSource, CountryInfoGetterFn countryInfoGetter, const CountryParentNameGetterFn & countryParentNameGetter, std::map & messageCache) : m_dataSource(dataSource) + , m_countryInfoGetterFn(countryInfoGetter) , m_countryParentNameGetterFn(countryParentNameGetter) , m_messageCache(messageCache) {} @@ -121,10 +134,10 @@ void TraffDecoder::DecodeMessage(traffxml::TraffMessage & message) } } -OpenLrV3TraffDecoder::OpenLrV3TraffDecoder(DataSource & dataSource, +OpenLrV3TraffDecoder::OpenLrV3TraffDecoder(DataSource & dataSource, CountryInfoGetterFn countryInfoGetter, const CountryParentNameGetterFn & countryParentNameGetter, std::map & messageCache) - : TraffDecoder(dataSource, countryParentNameGetter, messageCache) + : TraffDecoder(dataSource, countryInfoGetter, countryParentNameGetter, messageCache) , m_openLrDecoder(dataSource, countryParentNameGetter) {} @@ -336,4 +349,263 @@ void OpenLrV3TraffDecoder::DecodeLocation(traffxml::TraffMessage & message, traf decoded[paths[i].m_path[j].GetFeatureId().m_mwmId][traffic::TrafficInfo::RoadSegmentId(fid, segment, direction)] = traffic::SpeedGroup::Unknown; } } + +RoutingTraffDecoder::DecoderRouter::DecoderRouter(CountryParentNameGetterFn const & countryParentNameGetterFn, + routing::TCountryFileFn const & countryFileFn, + routing::CountryRectFn const & countryRectFn, + std::shared_ptr numMwmIds, + std::unique_ptr> numMwmTree, + DataSource & dataSource) + : routing::IndexRouter(routing::VehicleType::Car /* VehicleType vehicleType */, + false /* bool loadAltitudes */, + countryParentNameGetterFn, + countryFileFn, + countryRectFn, + std::move(numMwmIds), + std::move(numMwmTree), + //std::nullopt /* std::optional const & trafficCache */, + dataSource) + /* TODO build our own edge estimator for TraFF decoding purposes + , m_estimator(EdgeEstimator::Create( + VehicleType::Car, CalcMaxSpeed(*m_numMwmIds, *m_vehicleModelFactory, m_vehicleType), + CalcOffroadSpeed(*m_vehicleModelFactory), m_trafficStash, + &dataSource, m_numMwmIds)) + */ + //, m_directionsEngine(CreateDirectionsEngine(m_vehicleType, m_numMwmIds, m_dataSource)) // TODO we don’t need directions, can we disable that? +{} + +RoutingTraffDecoder::RoutingTraffDecoder(DataSource & dataSource, CountryInfoGetterFn countryInfoGetter, + const CountryParentNameGetterFn & countryParentNameGetter, + std::map & messageCache) + : TraffDecoder(dataSource, countryInfoGetter, countryParentNameGetter, messageCache) +{ + InitRouter(); +} + +bool RoutingTraffDecoder::InitRouter() +{ + if (m_router) + return true; + + // code mostly from RoutingManager::SetRouterImpl(RouterType) + /* + * RoutingManager::SetRouterImpl(RouterType) calls m_delegate.RegisterCountryFilesOnRoute(numMwmIds). + * m_delegate is the framework, and the routine cycles through the countries in storage. + * As we don’t have access to storage, we get our country files from the data source. + */ + std::vector> mwmsInfo; + m_dataSource.GetMwmsInfo(mwmsInfo); + for (auto mwmInfo : mwmsInfo) + m_numMwmIds->RegisterFile(mwmInfo->GetLocalFile().GetCountryFile()); + + if (m_numMwmIds->IsEmpty()) + return false; + + auto const countryFileGetter = [this](m2::PointD const & p) -> std::string { + // TODO (@gorshenin): fix CountryInfoGetter to return CountryFile + // instances instead of plain strings. + return m_countryInfoGetterFn().GetRegionCountryId(p); + }; + + auto const getMwmRectByName = [this](std::string const & countryId) -> m2::RectD { + return m_countryInfoGetterFn().GetLimitRectForLeaf(countryId); + }; + + m_router = + make_unique(m_countryParentNameGetterFn, + countryFileGetter, getMwmRectByName, m_numMwmIds, + routing::MakeNumMwmTree(*m_numMwmIds, m_countryInfoGetterFn()), + m_dataSource); + + return true; +} + +// Copied from AsyncRouter +// static +void RoutingTraffDecoder::LogCode(routing::RouterResultCode code, double const elapsedSec) +{ + switch (code) + { + case routing::RouterResultCode::StartPointNotFound: + LOG(LWARNING, ("Can't find start or end node")); + break; + case routing::RouterResultCode::EndPointNotFound: + LOG(LWARNING, ("Can't find end point node")); + break; + case routing::RouterResultCode::PointsInDifferentMWM: + LOG(LWARNING, ("Points are in different MWMs")); + break; + case routing::RouterResultCode::RouteNotFound: + LOG(LWARNING, ("Route not found")); + break; + case routing::RouterResultCode::RouteFileNotExist: + LOG(LWARNING, ("There is no routing file")); + break; + case routing::RouterResultCode::NeedMoreMaps: + LOG(LINFO, + ("Routing can find a better way with additional maps, elapsed seconds:", elapsedSec)); + break; + case routing::RouterResultCode::Cancelled: + LOG(LINFO, ("Route calculation cancelled, elapsed seconds:", elapsedSec)); + break; + case routing::RouterResultCode::NoError: + LOG(LINFO, ("Route found, elapsed seconds:", elapsedSec)); + break; + case routing::RouterResultCode::NoCurrentPosition: + LOG(LINFO, ("No current position")); + break; + case routing::RouterResultCode::InconsistentMWMandRoute: + LOG(LINFO, ("Inconsistent mwm and route")); + break; + case routing::RouterResultCode::InternalError: + LOG(LINFO, ("Internal error")); + break; + case routing::RouterResultCode::FileTooOld: + LOG(LINFO, ("File too old")); + break; + case routing::RouterResultCode::IntermediatePointNotFound: + LOG(LWARNING, ("Can't find intermediate point node")); + break; + case routing::RouterResultCode::TransitRouteNotFoundNoNetwork: + LOG(LWARNING, ("No transit route is found because there's no transit network in the mwm of " + "the route point")); + break; + case routing::RouterResultCode::TransitRouteNotFoundTooLongPedestrian: + LOG(LWARNING, ("No transit route is found because pedestrian way is too long")); + break; + case routing::RouterResultCode::RouteNotFoundRedressRouteError: + LOG(LWARNING, ("Route not found because of a redress route error")); + break; + case routing::RouterResultCode::HasWarnings: + LOG(LINFO, ("Route has warnings, elapsed seconds:", elapsedSec)); + break; + } +} + +void RoutingTraffDecoder::DecodeLocationDirection(traffxml::TraffMessage & message, + traffxml::MultiMwmColoring & decoded, bool backwards) +{ + bool adjustToPrevRoute = false; // calculate a fresh route, no adjustments to previous one + uint64_t routeId = 0; // used in callbacks to identify the route, we might not need it at all + std::string routerName; // set later + + std::vector points; + if (message.m_location.value().m_from) + points.push_back(mercator::FromLatLon(message.m_location.value().m_from.value().m_coordinates)); + if (message.m_location.value().m_at) + points.push_back(mercator::FromLatLon(message.m_location.value().m_at.value().m_coordinates)); + else if (message.m_location.value().m_via) + points.push_back(mercator::FromLatLon(message.m_location.value().m_via.value().m_coordinates)); + if (message.m_location.value().m_to) + points.push_back(mercator::FromLatLon(message.m_location.value().m_to.value().m_coordinates)); + if (backwards) + std::reverse(points.begin(), points.end()); + // m_notVia is ignored as OpenLR does not support this functionality. + CHECK_GREATER(points.size(), 1, ("At least two reference points must be given")); + + /* + * startDirection is the direction of travel at start. Can be m2::PointD::Zero() to ignore + * direction, or PositionAccumulator::GetDirection(), which basically returns the difference + * between the last position and an earlier one (offset between two points from which the + * direction of travel can be inferred). + * + * For our purposes, points[1] - points[0] would be as close as we could get to a start direction. + * This would be accurate on very straight roads, less accurate on not-so-straight ones. However, + * even on a near-straight road, the standard router (with the default EdgeEstimator) seemed quite + * unimpressed by the direction and insisted on starting off on the carriageway closest to the + * start point and sending us on a huge detour, instead of taking the direct route on the opposite + * carriageway. + */ + m2::PointD startDirection = m2::PointD::Zero(); + + routing::Checkpoints checkpoints(std::move(points)); + + /* + * This code is mostly lifted from: + * - AsyncRouter::CalculateRoute(Checkpoints const &, m2::PointD const &, bool, ReadyCallbackOwnership const &, + * NeedMoreMapsCallback const &, RemoveRouteCallback const &, ProgressCallback, uint32_t) + * - AsyncRouter::CalculateRoute() + */ + + // AsyncRouter::CalculateRoute(with args) + /* + * AsyncRouter::CalculateRoute() has a `DelegateProxy`, which is private. We just need the return + * value of GetDelegate(), which is a `routing::RouterDelegate`, so use that instead. We don’t + * nedd any of the callbacks, therefore we don’t set them. + */ + routing::RouterDelegate delegate; + delegate.SetTimeout(kRouterTimeoutSec); + + // AsyncRouter::CalculateRoute() + if (!m_router && !InitRouter()) + return; + + routerName = m_router->GetName(); + // TODO is that for following a track? If so, can we use that with just 2–3 reference points? + //router->SetGuides(std::move(m_guides)); + //m_guides.clear(); + + auto route = std::make_shared(m_router->GetName(), routeId); + routing::RouterResultCode code; + + base::Timer timer; + double elapsedSec = 0.0; + + try + { + LOG(LINFO, ("Calculating the route of direct length", checkpoints.GetSummaryLengthBetweenPointsMeters(), + "m. checkpoints:", checkpoints, "startDirection:", startDirection, "router name:", m_router->GetName())); + + // Run basic request. + code = m_router->CalculateRoute(checkpoints, startDirection, adjustToPrevRoute, + delegate, *route); + m_router->SetGuides({}); + elapsedSec = timer.ElapsedSeconds(); // routing time + LogCode(code, elapsedSec); + LOG(LINFO, ("ETA:", route->GetTotalTimeSec(), "sec.")); + } + catch (RootException const & e) + { + code = routing::RouterResultCode::InternalError; + LOG(LERROR, ("Exception happened while calculating route:", e.Msg())); + return; + } + + if (code == routing::RouterResultCode::NoError) + { + LOG(LINFO, ("Decoded route:")); + for (auto rsegment : route->GetRouteSegments()) + { + routing::Segment segment = rsegment.GetSegment(); + if (segment.GetMwmId() == routing::kFakeNumMwmId) + { + LOG(LINFO, (" Fake segment:", segment)); + continue; + } + + LOG(LINFO, (" segment:", segment)); + + auto const countryFile = m_numMwmIds->GetFile(segment.GetMwmId()); + MwmSet::MwmId mwmId = m_dataSource.GetMwmIdByCountryFile(countryFile); + + auto const fid = segment.GetFeatureId(); + auto const sid = segment.GetSegmentIdx(); + uint8_t direction = segment.IsForward() ? + traffic::TrafficInfo::RoadSegmentId::kForwardDirection : + traffic::TrafficInfo::RoadSegmentId::kReverseDirection; + + decoded[mwmId][traffic::TrafficInfo::RoadSegmentId(fid, sid, direction)] = traffic::SpeedGroup::Unknown; + } + } +} + +void RoutingTraffDecoder::DecodeLocation(traffxml::TraffMessage & message, traffxml::MultiMwmColoring & decoded) +{ + ASSERT(message.m_location, ("Message has no location")); + decoded.clear(); + + int dirs = (message.m_location.value().m_directionality == Directionality::BothDirections) ? 2 : 1; + for (int dir = 0; dir < dirs; dir++) + DecodeLocationDirection(message, decoded, dir == 0 ? false : true /* backwards */); +} } // namespace traffxml diff --git a/traffxml/traff_decoder.hpp b/traffxml/traff_decoder.hpp index 5e46d1ecf..5c9686b1d 100644 --- a/traffxml/traff_decoder.hpp +++ b/traffxml/traff_decoder.hpp @@ -7,6 +7,14 @@ #include "openlr/openlr_decoder.hpp" #include "openlr/openlr_model.hpp" +#include "routing/index_router.hpp" +#include "routing/regions_decl.hpp" +#include "routing/router.hpp" + +#include "routing_common/num_mwm_id.hpp" + +#include "storage/country_info_getter.hpp" + namespace traffxml { /** @@ -15,9 +23,10 @@ namespace traffxml class TraffDecoder { public: + using CountryInfoGetterFn = std::function; using CountryParentNameGetterFn = std::function; - TraffDecoder(DataSource & dataSource, + TraffDecoder(DataSource & dataSource, CountryInfoGetterFn countryInfoGetter, const CountryParentNameGetterFn & countryParentNameGetter, std::map & messageCache); @@ -53,6 +62,7 @@ protected: void ApplyTrafficImpact(traffxml::TrafficImpact & impact, traffxml::MultiMwmColoring & decoded); DataSource & m_dataSource; + CountryInfoGetterFn m_countryInfoGetterFn; CountryParentNameGetterFn m_countryParentNameGetterFn; /** @@ -71,7 +81,7 @@ private: class OpenLrV3TraffDecoder : public TraffDecoder { public: - OpenLrV3TraffDecoder(DataSource & dataSource, + OpenLrV3TraffDecoder(DataSource & dataSource, CountryInfoGetterFn countryInfoGetter, const CountryParentNameGetterFn & countryParentNameGetter, std::map & messageCache); @@ -151,6 +161,80 @@ private: openlr::OpenLRDecoder m_openLrDecoder; }; +/** + * @brief A `TraffDecoder` implementation which internally uses the routing engine. + */ +class RoutingTraffDecoder : public TraffDecoder +{ +public: + class DecoderRouter : public routing::IndexRouter + { + public: + /** + * @brief Creates a new `DecoderRouter` instance. + * + * @param countryParentNameGetterFn Function which converts a country name into the name of its parent country) + * @param countryFileFn Function which converts a pointer to its country name + * @param countryRectFn Function which returns the rect for a country + * @param numMwmIds + * @param numMwmTree + * @param trafficCache Tre traffic cache (used only if `vehicleType` is `VehicleType::Car`) + * @param dataSource The MWM data source + */ + DecoderRouter(CountryParentNameGetterFn const & countryParentNameGetterFn, + routing::TCountryFileFn const & countryFileFn, + routing::CountryRectFn const & countryRectFn, + std::shared_ptr numMwmIds, + std::unique_ptr> numMwmTree, + DataSource & dataSource); + protected: + private: + }; + + RoutingTraffDecoder(DataSource & dataSource, CountryInfoGetterFn countryInfoGetter, + const CountryParentNameGetterFn & countryParentNameGetter, + std::map & messageCache); + +protected: + /** + * @brief Initializes the router. + * + * This is usually done in the constructor but fails if no maps are loaded (attempting to + * construct a router without maps results in a crash, hence we check for maps and exit with an + * error if we have none). It can be repeated any time. + * + * Attempting to initialize a router which has already been succesfully initialized is a no-op. It + * will be reported as success. + * + * @return true if successful, false if not. + */ + bool InitRouter(); + + /** + * @brief Decodes one direction of a TraFF location. + * + * @param message The message to decode. + * @param decoded Receives the decoded segments. The speed group will be `Unknown`. + * @param backwards If true, decode the backward direction, else the forward direction. + */ + void DecodeLocationDirection(traffxml::TraffMessage & message, + traffxml::MultiMwmColoring & decoded, bool backwards); + + /** + * @brief Decodes a TraFF location. + * + * @param message The message to decode. + * @param decoded Receives the decoded segments. The speed group will be `Unknown`. + */ + void DecodeLocation(traffxml::TraffMessage & message, traffxml::MultiMwmColoring & decoded); + +private: + static void LogCode(routing::RouterResultCode code, double const elapsedSec); + + std::shared_ptr m_numMwmIds = std::make_shared(); + std::unique_ptr m_router; +}; + /** * @brief The default TraFF decoder implementation, recommended for production use. */