mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-19 13:03:36 +00:00
[traffic] Update routing MWMs as route changes
Signed-off-by: mvglasow <michael -at- vonglasow.com>
This commit is contained in:
@@ -75,12 +75,12 @@ TrafficManager::CacheEntry::CacheEntry(time_point<steady_clock> const & requestT
|
||||
TrafficManager::TrafficManager(DataSource & dataSource, CountryInfoGetterFn countryInfoGetter,
|
||||
const CountryParentNameGetterFn &countryParentNameGetter,
|
||||
GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes,
|
||||
traffic::TrafficObserver & observer)
|
||||
routing::RoutingSession & routingSession)
|
||||
: m_dataSource(dataSource)
|
||||
, m_countryInfoGetterFn(countryInfoGetter)
|
||||
, m_countryParentNameGetterFn(countryParentNameGetter)
|
||||
, m_getMwmsByRectFn(getMwmsByRectFn)
|
||||
, m_observer(observer)
|
||||
, m_routingSession(routingSession)
|
||||
, m_currentDataVersion(0)
|
||||
, m_state(TrafficState::Disabled)
|
||||
// TODO no longer needed
|
||||
@@ -92,6 +92,11 @@ TrafficManager::TrafficManager(DataSource & dataSource, CountryInfoGetterFn coun
|
||||
, m_thread(&TrafficManager::ThreadRoutine, this)
|
||||
{
|
||||
CHECK(m_getMwmsByRectFn != nullptr, ());
|
||||
GetPlatform().RunTask(Platform::Thread::Gui, [this]() {
|
||||
m_routingSession.SetChangeSessionStateCallback([this](routing::SessionState previous, routing::SessionState current) {
|
||||
OnChangeRoutingSessionState(previous, current);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
TrafficManager::~TrafficManager()
|
||||
@@ -170,7 +175,7 @@ void TrafficManager::SetEnabled(bool enabled)
|
||||
m_canSetMode = false;
|
||||
}
|
||||
else
|
||||
m_observer.OnTrafficInfoClear();
|
||||
m_routingSession.OnTrafficInfoClear();
|
||||
}
|
||||
|
||||
void TrafficManager::Clear()
|
||||
@@ -194,7 +199,7 @@ void TrafficManager::Clear()
|
||||
|
||||
// TODO figure out which of the ones below we still need
|
||||
m_lastDrapeMwmsByRect.clear();
|
||||
m_lastRoutingMwmsByRect.clear();
|
||||
m_lastPositionMwmsByRect.clear();
|
||||
#ifdef traffic_dead_code
|
||||
m_requestedMwms.clear();
|
||||
m_trafficETags.clear();
|
||||
@@ -253,6 +258,65 @@ void TrafficManager::OnRecoverSurface()
|
||||
Resume();
|
||||
}
|
||||
|
||||
void TrafficManager::OnChangeRoutingSessionState(routing::SessionState previous, routing::SessionState current)
|
||||
{
|
||||
// TODO assert we’re running on the UI thread
|
||||
LOG(LINFO, ("Routing session state changed from", previous, "to", current));
|
||||
LOG(LINFO, ("Running on thread", std::this_thread::get_id()));
|
||||
/*
|
||||
* Filter based on session state (see routing_callbacks.hpp for states and transitions).
|
||||
*
|
||||
* GetAllRegions() seems to get fresh data during build (presumably also rebuild), which will be
|
||||
* available on the next state transition (to RouteNotStarted or OnRoute) and remain unchanged
|
||||
* until the next route (re)build. Therefore, calling GetAllRegions() when new state is one of
|
||||
* RouteNotStarted, OnRoute or RouteNoFollowing, and clearing MWMs when the new state is
|
||||
* NoValidRoute, and ignoring all other transitions, should work for our purposes.
|
||||
* There may be cases where we first calculate the route, then subscribe to the regions and only
|
||||
* then get notified about a traffic problem on the route, requiring us to recalculate.
|
||||
*/
|
||||
std::set<std::string> mwmNames;
|
||||
if (current == routing::SessionState::RouteNotStarted
|
||||
|| current == routing::SessionState::OnRoute
|
||||
|| current == routing::SessionState::RouteNoFollowing)
|
||||
/*
|
||||
* GetAllRegions() may block when run for the first time. This should happen during routing, so
|
||||
* the call here would always return cached results without blocking, causing no problems on the
|
||||
* UI thread.
|
||||
*
|
||||
* Note that this method is called before the state is updated internally, thus `GetAllRegions()`
|
||||
* and any other functions would still have `previous` as their state.
|
||||
*/
|
||||
m_routingSession.GetAllRegions(mwmNames);
|
||||
else if (current == routing::SessionState::NoValidRoute)
|
||||
mwmNames.clear();
|
||||
else
|
||||
// for all other states, keep current set
|
||||
return;
|
||||
|
||||
LOG(LINFO, ("Router MWMs:", mwmNames));
|
||||
|
||||
std::set<MwmSet::MwmId> mwms;
|
||||
for (auto const & mwmName : mwmNames)
|
||||
{
|
||||
MwmSet::MwmId mwmId = m_dataSource.GetMwmIdByCountryFile(platform::CountryFile(mwmName));
|
||||
if (mwmId.IsAlive())
|
||||
mwms.insert(mwmId);
|
||||
}
|
||||
|
||||
LOG(LINFO, ("MWM set:", mwms));
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
if (mwms != m_activeRoutingMwms)
|
||||
{
|
||||
m_activeMwmsChanged = true;
|
||||
std::swap(mwms, m_activeRoutingMwms);
|
||||
RequestTrafficData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrafficManager::RecalculateSubscription()
|
||||
{
|
||||
if (!IsEnabled())
|
||||
@@ -262,6 +326,7 @@ void TrafficManager::RecalculateSubscription()
|
||||
UpdateViewport(m_currentModelView.first);
|
||||
if (m_currentPosition.second)
|
||||
UpdateMyPosition(m_currentPosition.first);
|
||||
// TODO update routing – if needed
|
||||
}
|
||||
|
||||
void TrafficManager::Invalidate(MwmSet::MwmId const & mwmId)
|
||||
@@ -351,9 +416,7 @@ void TrafficManager::UpdateMyPosition(MyPosition const & myPosition)
|
||||
m2::RectD const rect =
|
||||
mercator::RectByCenterXYAndSizeInMeters(myPosition.m_position, kSquareSideM / 2.0);
|
||||
// Request traffic.
|
||||
UpdateActiveMwms(rect, m_lastRoutingMwmsByRect, m_activeRoutingMwms);
|
||||
|
||||
// @TODO Do all routing stuff.
|
||||
UpdateActiveMwms(rect, m_lastPositionMwmsByRect, m_activePositionMwms);
|
||||
}
|
||||
|
||||
void TrafficManager::UpdateViewport(ScreenBase const & screen)
|
||||
@@ -849,8 +912,8 @@ void TrafficManager::RequestTrafficData(MwmSet::MwmId const & mwmId, bool force)
|
||||
|
||||
void TrafficManager::RequestTrafficData()
|
||||
{
|
||||
if ((m_activeDrapeMwms.empty() && m_activeRoutingMwms.empty()) || !IsEnabled() ||
|
||||
IsInvalidState() || m_isPaused)
|
||||
if ((m_activeDrapeMwms.empty() && m_activePositionMwms.empty() && m_activeRoutingMwms.empty())
|
||||
|| !IsEnabled() || IsInvalidState() || m_isPaused)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1019,7 +1082,7 @@ void TrafficManager::OnTrafficDataUpdate()
|
||||
if (notifyObserver)
|
||||
{
|
||||
// Update traffic colors for routing.
|
||||
m_observer.OnTrafficInfoAdded(std::move(info));
|
||||
m_routingSession.OnTrafficInfoAdded(std::move(info));
|
||||
m_lastObserverUpdate = steady_clock::now();
|
||||
}
|
||||
}
|
||||
@@ -1036,7 +1099,7 @@ void TrafficManager::OnTrafficDataUpdate()
|
||||
if (notifyObserver)
|
||||
{
|
||||
// Update traffic colors for routing.
|
||||
m_observer.OnTrafficInfoRemoved(mwmId);
|
||||
m_routingSession.OnTrafficInfoRemoved(mwmId);
|
||||
m_lastObserverUpdate = steady_clock::now();
|
||||
}
|
||||
}
|
||||
@@ -1086,6 +1149,7 @@ void TrafficManager::OnTrafficDataResponse(traffic::TrafficInfo && info)
|
||||
void TrafficManager::UniteActiveMwms(std::set<MwmSet::MwmId> & activeMwms) const
|
||||
{
|
||||
activeMwms.insert(m_activeDrapeMwms.cbegin(), m_activeDrapeMwms.cend());
|
||||
activeMwms.insert(m_activePositionMwms.cbegin(), m_activePositionMwms.cend());
|
||||
activeMwms.insert(m_activeRoutingMwms.cbegin(), m_activeRoutingMwms.cend());
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include "indexer/mwm_set.hpp"
|
||||
|
||||
#include "routing/routing_session.hpp"
|
||||
|
||||
#include "storage/country_info_getter.hpp"
|
||||
|
||||
#include "traffxml/traff_decoder.hpp"
|
||||
@@ -107,7 +109,7 @@ public:
|
||||
CountryInfoGetterFn countryInfoGetter,
|
||||
CountryParentNameGetterFn const & countryParentNameGetter,
|
||||
GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes,
|
||||
traffic::TrafficObserver & observer);
|
||||
routing::RoutingSession & routingSession);
|
||||
~TrafficManager();
|
||||
|
||||
void Teardown();
|
||||
@@ -477,8 +479,8 @@ private:
|
||||
* @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`).
|
||||
* (`m_lastDrapeMwmsByRect`/`m_activeDrapeMwms`) or to those around the current position.
|
||||
* (`m_lastPositionMwmsByRect`/`m_activePositionMwms`).
|
||||
*
|
||||
* 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
|
||||
@@ -561,6 +563,8 @@ private:
|
||||
|
||||
bool IsInvalidState() const;
|
||||
|
||||
void OnChangeRoutingSessionState(routing::SessionState previous, routing::SessionState current);
|
||||
|
||||
void UniteActiveMwms(std::set<MwmSet::MwmId> & activeMwms) const;
|
||||
|
||||
void Pause();
|
||||
@@ -586,7 +590,14 @@ private:
|
||||
CountryInfoGetterFn m_countryInfoGetterFn;
|
||||
CountryParentNameGetterFn m_countryParentNameGetterFn;
|
||||
GetMwmsByRectFn m_getMwmsByRectFn;
|
||||
traffic::TrafficObserver & m_observer;
|
||||
|
||||
/*
|
||||
* Originally this was m_observer, of type traffic::TrafficObserver. Since routing::RoutingSession
|
||||
* inherits from that class, and an interface to the routing session is needed in order to
|
||||
* determine the MWMs for which we need traffic information, the type was changed and the member
|
||||
* renamed to reflect that.
|
||||
*/
|
||||
routing::RoutingSession & m_routingSession;
|
||||
|
||||
df::DrapeEngineSafePtr m_drapeEngine;
|
||||
std::atomic<int64_t> m_currentDataVersion;
|
||||
@@ -624,14 +635,16 @@ private:
|
||||
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).
|
||||
* To determine for which MWMs we need traffic data, we need to keep track of 3 groups of MWMs:
|
||||
* those used by the renderer (i.e. in or just around the viewport), those within a certain area
|
||||
* around the current position, and those used by the routing engine (only if currently routing).
|
||||
*
|
||||
* 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.
|
||||
* Routing MWMs are stored as a set.
|
||||
*
|
||||
* The other groups arestored 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:
|
||||
*
|
||||
@@ -642,14 +655,15 @@ private:
|
||||
*
|
||||
* Methods which use both, but in a different way:
|
||||
*
|
||||
* * ClearCache(), removes the requested MWM from the set but clears the vector completely.
|
||||
* * UpdateActiveMwms(), uses the vector to detect changes. If so, it updates both vector and set.
|
||||
* * (dead code) ClearCache(), removes the requested MWM from the set but clears the vector completely.
|
||||
* * UpdateActiveMwms(), uses the vector to detect changes (not for routing MWMs). If so, it updates both vector and set.
|
||||
*
|
||||
* Clear() clears both the set and the vector. (Clearing the set is currently disabled as it breaks ForEachActiveMwm.)
|
||||
*/
|
||||
std::vector<MwmSet::MwmId> m_lastDrapeMwmsByRect;
|
||||
std::set<MwmSet::MwmId> m_activeDrapeMwms;
|
||||
std::vector<MwmSet::MwmId> m_lastRoutingMwmsByRect;
|
||||
std::vector<MwmSet::MwmId> m_lastPositionMwmsByRect;
|
||||
std::set<MwmSet::MwmId> m_activePositionMwms;
|
||||
std::set<MwmSet::MwmId> m_activeRoutingMwms;
|
||||
|
||||
// TODO no longer needed
|
||||
|
||||
@@ -19,6 +19,8 @@ AbsentRegionsFinder::AbsentRegionsFinder(CountryFileGetterFn const & countryFile
|
||||
void AbsentRegionsFinder::GenerateAbsentRegions(Checkpoints const & checkpoints,
|
||||
RouterDelegate const & delegate)
|
||||
{
|
||||
m_regions.clear();
|
||||
|
||||
if (m_routerThread)
|
||||
{
|
||||
m_routerThread->Cancel();
|
||||
@@ -52,20 +54,21 @@ void AbsentRegionsFinder::GetAbsentRegions(std::set<std::string> & regions)
|
||||
|
||||
void AbsentRegionsFinder::GetAllRegions(std::set<std::string> & countries)
|
||||
{
|
||||
countries.clear();
|
||||
|
||||
if (!m_routerThread)
|
||||
return;
|
||||
|
||||
m_routerThread->Join();
|
||||
|
||||
for (auto const & mwmName : m_routerThread->GetRoutineAs<RegionsRouter>()->GetMwmNames())
|
||||
// Note: if called from `RoutingSession` callback, m_state will still have its pre-update value.
|
||||
if (m_routerThread)
|
||||
{
|
||||
if (!mwmName.empty())
|
||||
countries.emplace(mwmName);
|
||||
m_routerThread->Join();
|
||||
|
||||
for (auto const & mwmName : m_routerThread->GetRoutineAs<RegionsRouter>()->GetMwmNames())
|
||||
{
|
||||
if (!mwmName.empty())
|
||||
m_regions.emplace(mwmName);
|
||||
}
|
||||
|
||||
m_routerThread.reset();
|
||||
}
|
||||
|
||||
m_routerThread.reset();
|
||||
countries = m_regions;
|
||||
}
|
||||
|
||||
bool AbsentRegionsFinder::AreCheckpointsInSameMwm(Checkpoints const & checkpoints) const
|
||||
|
||||
@@ -25,9 +25,27 @@ public:
|
||||
|
||||
// Creates new thread |m_routerThread| and starts routing in it.
|
||||
void GenerateAbsentRegions(Checkpoints const & checkpoints, RouterDelegate const & delegate);
|
||||
// Waits for the routing thread |m_routerThread| to finish and returns results from it.
|
||||
|
||||
/**
|
||||
* @brief Retrieves the MWMs needed to build the route.
|
||||
*
|
||||
* When called for the first time after `GenerateAbsentRegions()`, this method waits for the
|
||||
* routing thread `m_routerThread` to finish and returns results from it. Results are cached and
|
||||
* subsequent calls are served from the cache.
|
||||
*
|
||||
* @param countries Receives the list of MWM names.
|
||||
*/
|
||||
void GetAllRegions(std::set<std::string> & countries);
|
||||
// Waits for the results from GetAllRegions() and returns only regions absent on the device.
|
||||
|
||||
/**
|
||||
* @brief Retrieves the missing MWMs needed to build the route.
|
||||
*
|
||||
* This calls `GetAllRegions()` and strips from the result all regions already present on the
|
||||
* device, leaving only the missing ones. If the call to `GetAllRegions()` is the first one after
|
||||
* calling `GenerateAbsentRegions()`, this involves waiting for the router thread to finish.
|
||||
*
|
||||
* @param absentCountries Receives the list of missing MWM names.
|
||||
*/
|
||||
void GetAbsentRegions(std::set<std::string> & absentCountries);
|
||||
|
||||
private:
|
||||
@@ -40,5 +58,19 @@ private:
|
||||
DataSource & m_dataSource;
|
||||
|
||||
std::unique_ptr<threads::Thread> m_routerThread;
|
||||
|
||||
/**
|
||||
* @brief Mutex for access to `m_regions`.
|
||||
*
|
||||
* Threads which access `m_regions` must lock this mutex while doing so.
|
||||
*/
|
||||
std::mutex m_mutex;
|
||||
|
||||
/**
|
||||
* @brief Regions required for building the last route.
|
||||
*
|
||||
* This member is cleared by `GenerateAbsentRegions()` and populated by `GetAllRegions()`.
|
||||
*/
|
||||
std::set<std::string> m_regions;
|
||||
};
|
||||
} // namespace routing
|
||||
|
||||
@@ -83,6 +83,13 @@ bool AsyncRouter::FindClosestProjectionToRoad(m2::PointD const & point,
|
||||
return m_router->FindClosestProjectionToRoad(point, direction, radius, proj);
|
||||
}
|
||||
|
||||
void AsyncRouter::GetAllRegions(std::set<std::string> & countries)
|
||||
{
|
||||
if (!m_absentRegionsFinder)
|
||||
return;
|
||||
m_absentRegionsFinder->GetAllRegions(countries);
|
||||
}
|
||||
|
||||
void AsyncRouter::RouterDelegateProxy::OnProgress(float progress)
|
||||
{
|
||||
ProgressCallback onProgress = nullptr;
|
||||
|
||||
@@ -62,6 +62,15 @@ public:
|
||||
bool FindClosestProjectionToRoad(m2::PointD const & point, m2::PointD const & direction,
|
||||
double radius, EdgeProj & proj);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the MWMs needed to build the route.
|
||||
*
|
||||
* Waits for the routing thread to finish and returns the list of MWM names from it.
|
||||
*
|
||||
* @param countries Receives the list of MWM names.
|
||||
*/
|
||||
void GetAllRegions(std::set<std::string> & countries);
|
||||
|
||||
private:
|
||||
/// Worker thread function
|
||||
void ThreadFunc();
|
||||
|
||||
@@ -470,6 +470,13 @@ double RoutingSession::GetCompletionPercent() const
|
||||
return percent;
|
||||
}
|
||||
|
||||
void RoutingSession::GetAllRegions(std::set<std::string> & countries)
|
||||
{
|
||||
if (!m_router)
|
||||
return;
|
||||
m_router->GetAllRegions(countries);
|
||||
}
|
||||
|
||||
void RoutingSession::PassCheckpoints()
|
||||
{
|
||||
CHECK_THREAD_CHECKER(m_threadChecker, ());
|
||||
|
||||
@@ -258,6 +258,15 @@ public:
|
||||
|
||||
double GetCompletionPercent() const;
|
||||
|
||||
/**
|
||||
* @brief Retrieves the MWMs needed to build the route.
|
||||
*
|
||||
* Waits for the `RegionsRouter` thread to finish and returns the list of MWM names from it.
|
||||
*
|
||||
* @param countries Receives the list of MWM names.
|
||||
*/
|
||||
void GetAllRegions(std::set<std::string> & countries);
|
||||
|
||||
private:
|
||||
struct DoReadyCallback
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user