#pragma once #include "traffic/traffic_info.hpp" #include "drape_frontend/drape_engine_safe_ptr.hpp" #include "drape_frontend/traffic_generator.hpp" #include "drape/pointers.hpp" #include "indexer/data_source.hpp" #include "indexer/mwm_set.hpp" #include "openlr/openlr_decoder.hpp" #include "traffxml/traff_model.hpp" #include "geometry/point2d.hpp" #include "geometry/polyline2d.hpp" #include "geometry/screenbase.hpp" #include "base/thread.hpp" #include #include #include #include #include #include #include #include #include #include #include #include class TrafficManager final { public: using CountryParentNameGetterFn = std::function; /** * @brief Global state of traffic information. */ enum class TrafficState { /** Traffic is disabled, no traffic data will be retrieved or considered for routing. */ Disabled, /** Traffic is enabled and working normally (the first request may not have been scheduled yet). */ Enabled, /** At least one request is currently pending. */ WaitingData, /** At least one MWM has stale traffic data. */ Outdated, /** Traffic data for at least one MWM was invalid or not found on the server. */ NoData, /** At least one request failed or timed out. */ NetworkError, /** Traffic data could not be retrieved because the map data is outdated. */ ExpiredData, /** Traffic data could not be retrieved because the app version is outdated. */ ExpiredApp }; struct MyPosition { m2::PointD m_position = m2::PointD(0.0, 0.0); bool m_knownPosition = false; MyPosition() = default; MyPosition(m2::PointD const & position) : m_position(position), m_knownPosition(true) {} }; using TrafficStateChangedFn = std::function; using GetMwmsByRectFn = std::function(m2::RectD const &)>; TrafficManager(DataSource & dataSource, CountryParentNameGetterFn const & countryParentNameGetter, GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes, traffic::TrafficObserver & observer); ~TrafficManager(); void Teardown(); TrafficState GetState() const; void SetStateListener(TrafficStateChangedFn const & onStateChangedFn); void SetDrapeEngine(ref_ptr engine); /** * @brief Sets the version of the MWM used locally. */ void SetCurrentDataVersion(int64_t dataVersion); /** * @brief Enables or disables the traffic manager. * * This sets the internal state and notifies the drape engine. Enabling the traffic manager will * invalidate its data, disabling it will notify the observer that traffic data has been cleared. * * Calling this function with `enabled` identical to the current state is a no-op. * * @param enabled True to enable, false to disable */ void SetEnabled(bool enabled); /** * @brief Whether the traffic manager is enabled. * * @return True if enabled, false if not */ bool IsEnabled() const; void UpdateViewport(ScreenBase const & screen); void UpdateMyPosition(MyPosition const & myPosition); void Invalidate(); void OnDestroySurface(); void OnRecoverSurface(); void OnMwmDeregistered(platform::LocalCountryFile const & countryFile); void OnEnterForeground(); void OnEnterBackground(); void SetSimplifiedColorScheme(bool simplified); bool HasSimplifiedColorScheme() const { return m_hasSimplifiedColorScheme; } private: /** * @brief Holds information about pending or previous traffic requests pertaining to an MWM. */ struct CacheEntry { CacheEntry(); explicit CacheEntry(std::chrono::time_point const & requestTime); /** * @brief Whether we have traffic data for this MWM. */ bool m_isLoaded; /** * @brief The amount of memory occupied by the coloring for this MWM. */ size_t m_dataSize; /** * @brief When the last update request occurred, not including forced updates. * * This timestamp is the basis for eliminating the oldest entries from the cache. */ std::chrono::time_point m_lastActiveTime; /** * @brief When the last update request occurred, including forced updates. * * This timestamp is the basis for determining whether an update is needed. */ std::chrono::time_point m_lastRequestTime; /** * @brief When the last response was received. * * This timestamp is the basis for determining whether a network request timed out, or if data is outdated. */ std::chrono::time_point m_lastResponseTime; /** * @brief The number of failed traffic requests for this MWM. * * Reset when the MWM becomes inactive. */ int m_retriesCount; /** * @brief Whether a request is currently pending for this MWM. * * Set to `true` when a request is scheduled, reverted to `false` when a response is received or the request fails. */ bool m_isWaitingForResponse; traffic::TrafficInfo::Availability m_lastAvailability; }; /** * @brief Returns a TraFF filter list for a set of MWMs. * * @param mwms The MWMs for which a filter list is to be created. * @return A `filter_list` in XML format. */ std::string GetMwmFilters(std::set & mwms); /** * @brief Subscribes to a traffic service. * * @param mwms The MWMs for which data is needed. * @return true on success, false on failure. */ bool Subscribe(std::set & mwms); /** * @brief Changes an existing traffic subscription. * * @param mwms The new set of MWMs for which data is needed. * @return true on success, false on failure. */ bool ChangeSubscription(std::set & mwms); /** * @brief Ensures we have a subscription covering all currently active MWMs. * * This method subscribes to a traffic service if not already subscribed, or changes the existing * subscription otherwise. * * @return true on success, false on failure. */ bool SetSubscriptionArea(); /** * @brief Unsubscribes from a traffic service we are subscribed to. */ void Unsubscribe(); /** * @brief Whether we are currently subscribed to a traffic service. * @return */ bool IsSubscribed(); /** * @brief Polls the traffic service for updates. * * @return true on success, false on failure. */ bool Poll(); /** * @brief Processes a traffic feed received through a push operation. * * Push operations are not supported on all platforms. * * @param feed The traffic feed. */ void Push(traffxml::TraffFeed feed); /** * @brief Consolidates the feed queue. * * If multiple feeds in the queue have the same message ID, only the message with the newest * update time is kept (if two messages have the same ID and update time, the one in the feed * with the higher index is kept); other messages with the same ID are discarded. Empty feeds * are discarded. */ void ConsolidateFeedQueue(); /** * @brief Removes the first message from the first feed and decodes it. */ void DecodeFirstMessage(); /** * @brief Decodes a single message to its segments and their speed groups. * * @param decoder The OpenLR decoder instance. * @param message The message to decode. * @param trafficCache The cache in which all decoded paths with their speed groups will be stored. */ void DecodeMessage(traffxml::TraffMessage & message); /** * @brief Event loop for the traffic worker thread. * * This method runs an event loop, which blocks until woken up or a timeout equivalent to the * update interval elapses. It cycles through the list of MWMs for which updates have been * scheduled, triggering a network request for each and processing the result. */ void ThreadRoutine(); /** * @brief Blocks until a request for traffic data is received or a timeout expires. * * This method acts as the loop condition for `ThreadRoutine()`. It blocks until woken up or the * update interval expires. In the latter case, it calls `RequestTrafficData()` to insert all * currently active MWMs into the list of MWMs to update; otherwise, it leaves the list as it is. * In either case, it populates `mwms` with the list and returns. * * @param mwms Receives a list of MWMs for which to update traffic data. * @return `true` during normal operation, `false` during teardown (signaling the event loop to exit). */ // TODO mwms argument is no longer needed bool WaitForRequest(); /** * @brief Processes new traffic data. * * @param trafficCache The new per-MWM colorings (preprocessed traffic information). */ void OnTrafficDataUpdate(std::map &trafficCache); // TODO no longer needed #ifdef traffic_dead_code void OnTrafficDataResponse(traffic::TrafficInfo && info); /** * @brief Processes a failed traffic request. * * This method gets called when a traffic request has failed. * * It updates the `m_isWaitingForResponse` and `m_lastAvailability` of `info. * * If the MWM is no longer active, this method returns immediately after that. * * If the retry limit has not been reached, the MWM is re-inserted into the list by calling * `RequestTrafficData(MwmSet::MwmId, bool)` with `force` set to true. Otherwise, the retry count * is reset and the state updated accordingly. * * @param info */ void OnTrafficRequestFailed(traffic::TrafficInfo && info); #endif /** * @brief Updates `activeMwms` and requests traffic data. * * The old and new list of active MWMs may refer either to those used by the rendering engine * (`m_lastDrapeMwmsByRect`/`m_activeDrapeMwms`) or to those used by the routing engine * (`m_lastRoutingMwmsByRect`/`m_activeRoutingMwms`). * * The method first determines the list of MWMs overlapping with `rect`. If it is identical to * `lastMwmsByRect`, the method returns immediately. Otherwise, it stores the new set in * `lastMwmsByRect` and populates `activeMwms` with the elements. * * This method locks `m_mutex` while populating `activeMwms`. There is no need for the caller to * do that. * * @param rect Rectangle covering the new active MWM set. * @param lastMwmsByRect Set of active MWMs, see description. * @param activeMwms Vector of active MWMs, see description. */ void UpdateActiveMwms(m2::RectD const & rect, std::vector & lastMwmsByRect, std::set & activeMwms); // This is a group of methods that haven't their own synchronization inside. /** * @brief Requests a refresh of traffic data for all currently active MWMs. * * This method is the entry point for periodic traffic data refresh operations. It cycles through * all active MWMs and calls `RequestTrafficData(MwmSet::MwmId, bool)` on each `MwmId`, * scheduling a refresh if needed. The actual network operation is performed asynchronously on a * separate thread. * * The method does nothing if the `TrafficManager` instance is disabled, paused, in an invalid * state (`NetworkError`) or if neither the rendering engine nor the routing engine have any * active MWMs. * * This method is unsynchronized; the caller must lock `m_mutex` prior to calling it. */ void RequestTrafficData(); /** * @brief Requests a refresh of traffic data for a single MWM. * * This method first checks if traffic data for the given MWM needs to be refreshed, which is the * case if no traffic data has ever been fetched for the given MWM, the update interval has * expired or `force` is true. In that case, the method inserts the `mwmId` into the list of MWMs * for which to update traffic and wakes up the worker thread. * * This method is unsynchronized; the caller must lock `m_mutex` prior to calling it. * * @param mwmId Describes the MWM for which traffic data is to be refreshed. * @param force If true, a refresh is requested even if the update interval has not expired. */ void RequestTrafficData(MwmSet::MwmId const & mwmId, bool force); void Clear(); void ClearCache(MwmSet::MwmId const & mwmId); // TODO no longer needed #ifdef traffic_dead_code void ShrinkCacheToAllowableSize(); #endif /** * @brief Updates the state of the traffic manager based on the state of all MWMs used by the renderer. * * This method cycles through the state of all MWMs used by the renderer (MWMs used by the * routing engine but not by the rendering engine are not considered), examines their traffic * state and sets the global state accordingly. * * For a description of states, see `TrafficState`. The order of states is as follows, the first * state whose conditions are fulfilled becomes the new state: `TrafficState::NetworkError`, * `TrafficState::WaitingData`, `TrafficState::ExpiredApp`, `TrafficState::ExpiredData`, * `TrafficState::NoData`, `TrafficState::Outdated`, `TrafficState::Enabled`. */ void UpdateState(); void ChangeState(TrafficState newState); bool IsInvalidState() const; void UniteActiveMwms(std::set & activeMwms) const; void Pause(); void Resume(); template void ForEachActiveMwm(F && f) const { std::set activeMwms; UniteActiveMwms(activeMwms); std::for_each(activeMwms.begin(), activeMwms.end(), std::forward(f)); } DataSource & m_dataSource; CountryParentNameGetterFn m_countryParentNameGetterFn; GetMwmsByRectFn m_getMwmsByRectFn; traffic::TrafficObserver & m_observer; df::DrapeEngineSafePtr m_drapeEngine; std::atomic m_currentDataVersion; // These fields have a flag of their initialization. std::pair m_currentPosition = {MyPosition(), false}; std::pair m_currentModelView = {ScreenBase(), false}; std::atomic m_state; TrafficStateChangedFn m_onStateChangedFn; bool m_hasSimplifiedColorScheme = true; // TODO no longer needed #ifdef traffic_dead_code size_t m_maxCacheSizeBytes; size_t m_currentCacheSizeBytes = 0; #endif std::map m_mwmCache; bool m_isRunning; std::condition_variable m_condition; /* * To determine for which MWMs we need traffic data, we need to keep track of two groups of MWMs: * those used by the renderer (i.e. in or just around the viewport) and those used by the routing * engine (i.e. those within a certain area around the route endpoints). * * Each group is stored twice: as a set and as a vector. The set always holds the MWMs which were * last seen in use. Both get updated together when active MWMs are added or removed. However, * the vector is used as a reference to detect changes. It may get cleared when the set is not, * which is used to invalidate the set without destroying its contents. * * Methods which use only the set: * * * RequestTrafficData(), exits if empty, otherwise cycles through the set. * * OnTrafficRequestFailed(), determines if an MWM is still active and the request should be retried. * * UniteActiveMwms(), build the list of active MWMs (used by RequestTrafficData() or to shrink the cache). * * UpdateState(), cycles through the set to determine the state of traffic requests (renderer only). * * Methods which use both, but in a different way: * * * ClearCache(), removes the requested MWM from the set but clears the vector completely. * * Invalidate(), clears the vector but not the set. * * UpdateActiveMwms(), uses the vector to detect changes. If so, it updates both vector and set. * * Clear() clears both the set and the vector. */ std::vector m_lastDrapeMwmsByRect; std::set m_activeDrapeMwms; std::vector m_lastRoutingMwmsByRect; std::set m_activeRoutingMwms; // The ETag or entity tag is part of HTTP, the protocol for the World Wide Web. // It is one of several mechanisms that HTTP provides for web cache validation, // which allows a client to make conditional requests. std::map m_trafficETags; /** * Whether the traffic manager should begin receiving information. */ std::atomic m_isStarted; std::atomic m_isPaused; /** * @brief MWMs for which to retrieve traffic data. */ std::vector m_requestedMwms; std::mutex m_mutex; /** * @brief Worker thread which fetches traffic updates. */ threads::SimpleThread m_thread; /** * @brief When the last response was received. */ std::chrono::time_point m_lastResponseTime; /** * @brief When the last update notification to the Drape engine was posted. */ std::chrono::time_point m_lastDrapeUpdate; /** * @brief When the last update notification to the traffic observer was posted. */ std::chrono::time_point m_lastObserverUpdate; /** * @brief Whether active MWMs have changed since the last request. */ bool m_activeMwmsChanged = false; /** * @brief The subscription ID received from the traffic server. * * An empty subscription ID means no subscription. */ std::string m_subscriptionId; /** * @brief Whether a poll operation is needed. * * Used in the worker thread. A poll operation is needed unless a subscription (or subscription * change) operation was performed before and a feed was received a part of it. */ bool m_isPollNeeded; /** * @brief Queue of feeds waiting to be processed. * * Threads must lock `m_mutex` before accessing `m_feedQueue`, as some platforms may receive feeds * on multiple threads. */ std::vector m_feedQueue; /** * @brief Cache of all currently active TraFF messages. * * Keys are message IDs, values are messages. */ std::map m_messageCache; /** * @brief The OpenLR decoder instance. * * Used to decode TraFF locations into road segments on the map. */ openlr::OpenLRDecoder m_openLrDecoder; /** * @brief Map between MWM IDs and their colorings. */ std::map m_allMwmColoring; }; extern std::string DebugPrint(TrafficManager::TrafficState state);