Logic of traffic manager's states.

This commit is contained in:
Daria Volvenkova 2016-11-29 17:02:29 +03:00
parent 961d4749e3
commit fb61ac89e6
12 changed files with 488 additions and 65 deletions

View file

@ -84,6 +84,8 @@ Framework::Framework()
{
ASSERT_EQUAL ( g_framework, 0, () );
g_framework = this;
m_work.GetTrafficManager().SetStateListener(bind(&Framework::TrafficStateChanged, this, _1));
}
void Framework::OnLocationError(int errorCode)
@ -127,6 +129,12 @@ void Framework::MyPositionModeChanged(location::EMyPositionMode mode, bool routi
m_myPositionModeSignal(mode, routingActive);
}
void Framework::TrafficStateChanged(TrafficManager::TrafficState state)
{
if (m_onTrafficStateChangedFn)
m_onTrafficStateChangedFn(state);
}
void Framework::SetMyPositionMode(location::EMyPositionMode mode)
{
OnMyPositionModeChanged(mode);
@ -186,6 +194,7 @@ void Framework::DetachSurface(bool destroyContext)
LOG(LINFO, ("Destroy context."));
m_isContextDestroyed = true;
m_work.EnterBackground();
m_work.OnDestroyGLContext();
}
m_work.SetRenderingDisabled(destroyContext);
@ -215,7 +224,7 @@ bool Framework::AttachSurface(JNIEnv * env, jobject jSurface)
if (m_isContextDestroyed)
{
LOG(LINFO, ("Recover GL resources, viewport size:", factory->GetWidth(), factory->GetHeight()));
m_work.UpdateDrapeEngine(factory->GetWidth(), factory->GetHeight());
m_work.OnRecoverGLContext(factory->GetWidth(), factory->GetHeight());
m_isContextDestroyed = false;
m_work.EnterForeground();
@ -412,6 +421,11 @@ void Framework::ShowTrack(int category, int track)
NativeFramework()->ShowTrack(*nTrack);
}
void Framework::SetTrafficStateListener(TrafficManager::TrafficStateChangedFn const & fn)
{
m_onTrafficStateChangedFn = fn;
}
void Framework::SetMyPositionModeListener(location::TMyPositionModeChanged const & fn)
{
m_myPositionModeSignal = fn;

View file

@ -49,6 +49,8 @@ namespace android
map<gui::EWidget, gui::Position> m_guiPositions;
void TrafficStateChanged(TrafficManager::TrafficState state);
void MyPositionModeChanged(location::EMyPositionMode mode, bool routingActive);
void SetMyPositionMode(location::EMyPositionMode mode);
@ -56,6 +58,8 @@ namespace android
location::EMyPositionMode m_currentMode;
bool m_isCurrentModeInitialized;
TrafficManager::TrafficStateChangedFn m_onTrafficStateChangedFn;
bool m_isChoosePositionMode;
place_page::Info m_info;
@ -141,6 +145,8 @@ namespace android
void OnMyPositionModeChanged(location::EMyPositionMode mode);
void SwitchMyPositionNextMode();
void SetTrafficStateListener(TrafficManager::TrafficStateChangedFn const & fn);
void Save3dMode(bool allow3d, bool allow3dBuildings);
void Set3dMode(bool allow3d, bool allow3dBuildings);
void Get3dMode(bool & allow3d, bool & allow3dBuildings);

View file

@ -0,0 +1,27 @@
#include "Framework.hpp"
#include "../core/jni_helper.hpp"
#include "../platform/Platform.hpp"
extern "C"
{
static void TrafficStateChanged(TrafficManager::TrafficState state, shared_ptr<jobject> const & listener)
{
JNIEnv * env = jni::GetEnv();
static jmethodID const = jni::GetMethodID(env, *listener.get(), "onTrafficStateChanged", "(I)V");
env->CallVoidMethod(*listener, jmethodID, static_cast<jint>(state));
}
JNIEXPORT void JNICALL
Java_com_mapswithme_maps_TrafficState_nativeSetListener(JNIEnv * env, jclass clazz, jobject listener)
{
g_framework->SetTrafficStateListener(bind(&TrafficStateChanged, _1, jni::make_global_ref(listener)));
}
JNIEXPORT void JNICALL
Java_com_mapswithme_maps_TrafficState_nativeRemoveListener(JNIEnv * env, jclass clazz)
{
g_framework->SetTrafficStateListener(TrafficManager::TrafficStateChangedFn());
}
} // extern "C"

View file

@ -46,23 +46,23 @@ public final class LocationState
{
switch (mode)
{
case PENDING_POSITION:
return "PENDING_POSITION";
case PENDING_POSITION:
return "PENDING_POSITION";
case NOT_FOLLOW_NO_POSITION:
return "NOT_FOLLOW_NO_POSITION";
case NOT_FOLLOW_NO_POSITION:
return "NOT_FOLLOW_NO_POSITION";
case NOT_FOLLOW:
return "NOT_FOLLOW";
case NOT_FOLLOW:
return "NOT_FOLLOW";
case FOLLOW:
return "FOLLOW";
case FOLLOW:
return "FOLLOW";
case FOLLOW_AND_ROTATE:
return "FOLLOW_AND_ROTATE";
case FOLLOW_AND_ROTATE:
return "FOLLOW_AND_ROTATE";
default:
return "Unknown: " + mode;
default:
return "Unknown: " + mode;
}
}
}

View file

@ -0,0 +1,60 @@
package com.mapswithme.maps;
public final class TrafficState
{
public interface StateChangeListener
{
// This method is called from JNI layer.
@SuppressWarnings("unused")
void onTrafficStateChanged(int newMode);
}
// These values should correspond to
// TrafficManager::TrafficState enum (from map/traffic_manager.hpp)
public static final int DISABLED = 0;
public static final int ENABLED = 1;
public static final int WAITING_DATA = 2;
public static final int OUTDATED = 3;
public static final int NO_DATA = 4;
public static final int NETWORK_ERROR = 5;
public static final int EXPIRED_DATA = 6;
public static final int EXPIRED_APP = 7;
public static native void nativeSetListener(StateChangeListener listener);
public static native void nativeRemoveListener();
private TrafficState() {}
public static String nameOf(int state)
{
switch (state)
{
case DISABLED:
return "DISABLED";
case ENABLED:
return "ENABLED";
case WAITING_DATA:
return "WAITING_DATA";
case OUTDATED:
return "OUTDATED";
case NO_DATA:
return "NO_DATA";
case NETWORK_ERROR:
return "NETWORK_ERROR";
case EXPIRED_DATA:
return "EXPIRED_DATA";
case EXPIRED_APP:
return "EXPIRED_APP";
default:
return "Unknown: " + state;
}
}
}

View file

@ -127,7 +127,7 @@ char const kTrafficEnabledKey[] = "TrafficEnabled";
double const kDistEqualQueryMeters = 100.0;
size_t constexpr kMaxTrafficCacheSizeBytes = 256 /* Mb */ * 1024 * 1024;
size_t constexpr kMaxTrafficCacheSizeBytes = 128 /* Mb */ * 1024 * 1024;
// Must correspond SearchMarkType.
vector<string> kSearchMarks =
@ -237,6 +237,11 @@ void Framework::SetMyPositionModeListener(TMyPositionModeChanged && fn)
m_myPositionListener = move(fn);
}
TrafficManager & Framework::GetTrafficManager()
{
return m_trafficManager;
}
bool Framework::IsTrackingReporterEnabled() const
{
if (m_currentRouterType != routing::RouterType::Vehicle)
@ -318,7 +323,10 @@ void Framework::Migrate(bool keepDownloaded)
// If we do not suspend drape, it tries to access framework fields (i.e. m_infoGetter) which are null
// while migration is performed.
if (m_drapeEngine && m_isRenderingEnabled)
{
m_drapeEngine->SetRenderingDisabled(true);
OnDestroyGLContext();
}
m_selectedFeature = FeatureID();
m_searchEngine.reset();
m_infoGetter.reset();
@ -330,11 +338,13 @@ void Framework::Migrate(bool keepDownloaded)
InitCountryInfoGetter();
InitSearchEngine();
RegisterAllMaps();
m_trafficManager.SetCurrentDataVersion(GetStorage().GetCurrentDataVersion());
if (m_drapeEngine && m_isRenderingEnabled)
{
m_drapeEngine->SetRenderingEnabled();
UpdateDrapeEngine(m_currentModelView.PixelRectIn3d().SizeX(),
m_currentModelView.PixelRectIn3d().SizeY());
OnRecoverGLContext(m_currentModelView.PixelRectIn3d().SizeX(),
m_currentModelView.PixelRectIn3d().SizeY());
}
InvalidateRect(MercatorBounds::FullRect());
}
@ -443,6 +453,8 @@ Framework::Framework()
m_model.GetIndex().AddObserver(editor);
LOG(LINFO, ("Editor initialized"));
m_trafficManager.SetCurrentDataVersion(m_storage.GetCurrentDataVersion());
}
Framework::~Framework()
@ -1711,7 +1723,7 @@ void Framework::CreateDrapeEngine(ref_ptr<dp::OGLContextFactory> contextFactory,
m_trafficManager.SetDrapeEngine(make_ref(m_drapeEngine));
}
void Framework::UpdateDrapeEngine(int width, int height)
void Framework::OnRecoverGLContext(int width, int height)
{
if (m_drapeEngine)
{
@ -1722,9 +1734,14 @@ void Framework::UpdateDrapeEngine(int width, int height)
UpdatePlacePageInfoForCurrentSelection();
m_drapeApi.Invalidate();
m_trafficManager.OnRecover();
}
m_trafficManager.OnRecoverGLContext();
}
void Framework::OnDestroyGLContext()
{
m_trafficManager.OnDestroyGLContext();
}
ref_ptr<df::DrapeEngine> Framework::GetDrapeEngine()

View file

@ -448,7 +448,8 @@ public:
void SetRenderingEnabled(ref_ptr<dp::OGLContextFactory> contextFactory = nullptr);
void SetRenderingDisabled(bool destroyContext);
void UpdateDrapeEngine(int width, int height);
void OnRecoverGLContext(int width, int height);
void OnDestroyGLContext();
void SetFontScaleFactor(double scaleFactor);
@ -780,6 +781,8 @@ public:
void AllowAutoZoom(bool allowAutoZoom);
void SaveAutoZoom(bool allowAutoZoom);
TrafficManager & GetTrafficManager();
bool LoadTrafficEnabled();
void SaveTrafficEnabled(bool trafficEnabled);

View file

@ -1,5 +1,7 @@
#include "map/traffic_manager.hpp"
#include "platform/platform.hpp"
#include "routing/routing_helpers.hpp"
#include "drape_frontend/drape_engine.hpp"
@ -10,14 +12,18 @@
namespace
{
auto const kUpdateInterval = minutes(1);
} // namespace
auto constexpr kUpdateInterval = minutes(1);
auto constexpr kOutdatedDataTimeout = minutes(5) + kUpdateInterval;
auto constexpr kNetworkErrorTimeout = minutes(20);
auto constexpr kMaxRetriesCount = 5;
} // namespace
TrafficManager::TrafficManager(GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes)
: m_isEnabled(false)
, m_getMwmsByRectFn(getMwmsByRectFn)
: m_getMwmsByRectFn(getMwmsByRectFn)
, m_state(TrafficState::Disabled)
, m_notifyStateChanged(false)
, m_maxCacheSizeBytes(maxCacheSizeBytes)
, m_currentCacheSizeBytes(0)
, m_isRunning(true)
, m_thread(&TrafficManager::ThreadRoutine, this)
{
@ -34,11 +40,41 @@ TrafficManager::~TrafficManager()
m_thread.join();
}
void TrafficManager::SetStateListener(TrafficStateChangedFn const & onStateChangedFn)
{
GetPlatform().RunOnGuiThread([this, onStateChangedFn]()
{
m_onStateChangedFn = onStateChangedFn;
});
}
void TrafficManager::SetEnabled(bool enabled)
{
m_isEnabled = enabled;
{
lock_guard<mutex> lock(m_requestedMwmsLock);
Clear();
ChangeState(enabled ? TrafficState::Enabled : TrafficState::Disabled);
}
if (m_drapeEngine != nullptr)
m_drapeEngine->EnableTraffic(enabled);
if (enabled)
{
if (m_currentModelView.second)
UpdateViewport(m_currentModelView.first);
if (m_currentPosition.second)
UpdateMyPosition(m_currentPosition.first);
}
NotifyStateChanged();
}
void TrafficManager::Clear()
{
m_mwmCache.clear();
m_activeMwms.clear();
m_requestedMwms.clear();
}
void TrafficManager::SetDrapeEngine(ref_ptr<df::DrapeEngine> engine)
@ -46,19 +82,38 @@ void TrafficManager::SetDrapeEngine(ref_ptr<df::DrapeEngine> engine)
m_drapeEngine = engine;
}
void TrafficManager::OnRecover()
void TrafficManager::SetCurrentDataVersion(int64_t dataVersion)
{
m_mwmCache.clear();
m_currentDataVersion = dataVersion;
}
UpdateViewport(m_currentModelView);
UpdateMyPosition(m_currentPosition);
void TrafficManager::OnDestroyGLContext()
{
if (!IsEnabled())
return;
{
lock_guard<mutex> lock(m_requestedMwmsLock);
Clear();
}
}
void TrafficManager::OnRecoverGLContext()
{
if (!IsEnabled())
return;
if (m_currentModelView.second)
UpdateViewport(m_currentModelView.first);
if (m_currentPosition.second)
UpdateMyPosition(m_currentPosition.first);
}
void TrafficManager::UpdateViewport(ScreenBase const & screen)
{
m_currentModelView = screen;
m_currentModelView = {screen, true};
if (!m_isEnabled)
if (!IsEnabled() || IsInvalidState())
return;
if (df::GetZoomLevel(screen.GetScale()) < df::kRoadClass0ZoomLevel)
@ -74,18 +129,20 @@ void TrafficManager::UpdateViewport(ScreenBase const & screen)
for (auto const & mwm : mwms)
{
if (mwm.IsAlive())
m_activeMwms.push_back(mwm);
m_activeMwms.insert(mwm);
}
RequestTrafficData();
}
NotifyStateChanged();
}
void TrafficManager::UpdateMyPosition(MyPosition const & myPosition)
{
m_currentPosition = myPosition;
m_currentPosition = {myPosition, true};
if (!m_isEnabled)
if (!IsEnabled() || IsInvalidState())
return;
// 1. Determine mwm's nearby "my position".
@ -102,12 +159,19 @@ void TrafficManager::ThreadRoutine()
{
for (auto const & mwm : mwms)
{
traffic::TrafficInfo info(mwm);
traffic::TrafficInfo info(mwm, m_currentDataVersion);
if (info.ReceiveTrafficData())
{
OnTrafficDataResponse(info);
}
else
{
LOG(LWARNING, ("Traffic request failed. Mwm =", mwm));
OnTrafficRequestFailed(info);
}
NotifyStateChanged();
}
mwms.clear();
}
@ -116,12 +180,20 @@ void TrafficManager::ThreadRoutine()
bool TrafficManager::WaitForRequest(vector<MwmSet::MwmId> & mwms)
{
unique_lock<mutex> lock(m_requestedMwmsLock);
bool const timeout = !m_condition.wait_for(lock, kUpdateInterval, [this] { return !m_isRunning || !m_requestedMwms.empty(); });
bool const timeout = !m_condition.wait_for(lock, kUpdateInterval, [this]
{
return !m_isRunning || !m_requestedMwms.empty();
});
if (!m_isRunning)
return false;
if (timeout)
if (timeout && IsEnabled() && !IsInvalidState())
{
mwms = m_activeMwms;
mwms.reserve(m_activeMwms.size());
for (auto const & mwmId : m_activeMwms)
mwms.push_back(mwmId);
}
else
{
@ -141,7 +213,7 @@ void TrafficManager::RequestTrafficData()
ASSERT(mwmId.IsAlive(), ());
bool needRequesting = false;
auto currentTime = steady_clock::now();
auto const currentTime = steady_clock::now();
auto it = m_mwmCache.find(mwmId);
if (it == m_mwmCache.end())
@ -156,6 +228,7 @@ void TrafficManager::RequestTrafficData()
if (passedSeconds >= kUpdateInterval)
{
needRequesting = true;
it->second.m_isWaitingForResponse = true;
it->second.m_lastRequestTime = currentTime;
}
}
@ -163,6 +236,8 @@ void TrafficManager::RequestTrafficData()
if (needRequesting)
RequestTrafficData(mwmId);
}
UpdateState();
}
void TrafficManager::RequestTrafficData(MwmSet::MwmId const & mwmId)
@ -171,16 +246,57 @@ void TrafficManager::RequestTrafficData(MwmSet::MwmId const & mwmId)
m_condition.notify_one();
}
void TrafficManager::OnTrafficDataResponse(traffic::TrafficInfo const & info)
void TrafficManager::OnTrafficRequestFailed(traffic::TrafficInfo const & info)
{
lock_guard<mutex> lock(m_requestedMwmsLock);
auto it = m_mwmCache.find(info.GetMwmId());
if (it == m_mwmCache.end())
return;
it->second.m_isWaitingForResponse = false;
it->second.m_lastAvailability = info.GetAvailability();
if (info.GetAvailability() == traffic::TrafficInfo::Availability::Unknown)
{
if (!it->second.m_isLoaded)
{
if (m_activeMwms.find(info.GetMwmId()) != m_activeMwms.end())
{
if (it->second.m_retriesCount < kMaxRetriesCount)
{
it->second.m_lastRequestTime = steady_clock::now();
it->second.m_isWaitingForResponse = true;
RequestTrafficData(info.GetMwmId());
}
++it->second.m_retriesCount;
}
else
{
it->second.m_retriesCount = 0;
}
}
}
UpdateState();
}
void TrafficManager::OnTrafficDataResponse(traffic::TrafficInfo const & info)
{
lock_guard<mutex> lock(m_requestedMwmsLock);
auto it = m_mwmCache.find(info.GetMwmId());
if (it == m_mwmCache.end())
return;
it->second.m_isLoaded = true;
it->second.m_lastResponseTime = steady_clock::now();
it->second.m_isWaitingForResponse = false;
it->second.m_lastAvailability = info.GetAvailability();
// Update cache.
size_t constexpr kElementSize = sizeof(traffic::TrafficInfo::RoadSegmentId) + sizeof(traffic::SpeedGroup);
size_t const dataSize = info.GetColoring().size() * kElementSize;
it->second.m_isLoaded = true;
m_currentCacheSizeBytes += (dataSize - it->second.m_dataSize);
it->second.m_dataSize = dataSize;
CheckCacheSize();
@ -189,6 +305,8 @@ void TrafficManager::OnTrafficDataResponse(traffic::TrafficInfo const & info)
df::TrafficSegmentsColoring coloring;
coloring[info.GetMwmId()] = info.GetColoring();
m_drapeEngine->UpdateTraffic(coloring);
UpdateState();
}
void TrafficManager::CheckCacheSize()
@ -215,3 +333,94 @@ void TrafficManager::CheckCacheSize()
}
}
}
bool TrafficManager::IsEnabled() const
{
return m_state != TrafficState::Disabled;
}
bool TrafficManager::IsInvalidState() const
{
return m_state == TrafficState::NetworkError;
}
void TrafficManager::UpdateState()
{
if (!IsEnabled() || IsInvalidState())
return;
auto const currentTime = steady_clock::now();
auto maxPassedTime = steady_clock::duration::zero();
bool waiting = false;
bool networkError = false;
bool expiredApp = false;
bool expiredMwm = false;
bool noData = false;
for (auto const & mwmId : m_activeMwms)
{
auto it = m_mwmCache.find(mwmId);
ASSERT(it != m_mwmCache.end(), ());
if (it->second.m_isWaitingForResponse)
{
waiting = true;
}
else
{
expiredApp |= it->second.m_lastAvailability == traffic::TrafficInfo::Availability::ExpiredApp;
expiredMwm |= it->second.m_lastAvailability == traffic::TrafficInfo::Availability::ExpiredMwm;
noData |= it->second.m_lastAvailability == traffic::TrafficInfo::Availability::NoData;
}
if (it->second.m_isLoaded)
{
auto const timeSinceLastResponse = currentTime - it->second.m_lastResponseTime;
if (timeSinceLastResponse > maxPassedTime)
maxPassedTime = timeSinceLastResponse;
}
else if (it->second.m_retriesCount >= kMaxRetriesCount)
{
networkError = true;
}
}
if (networkError || maxPassedTime >= kNetworkErrorTimeout)
ChangeState(TrafficState::NetworkError);
else if (waiting)
ChangeState(TrafficState::WaitingData);
else if (expiredApp)
ChangeState(TrafficState::ExpiredApp);
else if (expiredMwm)
ChangeState(TrafficState::ExpiredData);
else if (noData)
ChangeState(TrafficState::NoData);
else if (maxPassedTime >= kOutdatedDataTimeout)
ChangeState(TrafficState::Outdated);
else
ChangeState(TrafficState::Enabled);
}
void TrafficManager::ChangeState(TrafficState newState)
{
if (m_state == newState)
return;
m_state = newState;
m_notifyStateChanged = true;
}
void TrafficManager::NotifyStateChanged()
{
if (m_notifyStateChanged)
{
m_notifyStateChanged = false;
GetPlatform().RunOnGuiThread([this]()
{
if (m_onStateChangedFn != nullptr)
m_onStateChangedFn(m_state);
});
}
}

View file

@ -13,6 +13,7 @@
#include "indexer/index.hpp"
#include "indexer/mwm_set.hpp"
#include "std/atomic.hpp"
#include "std/chrono.hpp"
#include "std/map.hpp"
#include "std/mutex.hpp"
@ -28,6 +29,18 @@ class DrapeEngine;
class TrafficManager final
{
public:
enum class TrafficState
{
Disabled,
Enabled,
WaitingData,
Outdated,
NoData,
NetworkError,
ExpiredData,
ExpiredApp
};
struct MyPosition
{
m2::PointD m_position = m2::PointD(0.0, 0.0);
@ -40,57 +53,85 @@ public:
{}
};
using TrafficStateChangedFn = function<void(TrafficState)>;
using GetMwmsByRectFn = function<vector<MwmSet::MwmId>(m2::RectD const &)>;
TrafficManager(GetMwmsByRectFn const & getMwmsByRectFn, size_t maxCacheSizeBytes);
~TrafficManager();
void SetStateListener(TrafficStateChangedFn const & onStateChangedFn);
void SetDrapeEngine(ref_ptr<df::DrapeEngine> engine);
void SetCurrentDataVersion(int64_t dataVersion);
void SetEnabled(bool enabled);
void UpdateViewport(ScreenBase const & screen);
void UpdateMyPosition(MyPosition const & myPosition);
void OnRecover();
void SetDrapeEngine(ref_ptr<df::DrapeEngine> engine);
void OnDestroyGLContext();
void OnRecoverGLContext();
private:
void ThreadRoutine();
bool WaitForRequest(vector<MwmSet::MwmId> & mwms);
void RequestTrafficData();
void RequestTrafficData(MwmSet::MwmId const & mwmId);
void OnTrafficDataResponse(traffic::TrafficInfo const & info);
void OnTrafficRequestFailed(traffic::TrafficInfo const & info);
void Clear();
void CheckCacheSize();
bool m_isEnabled;
void UpdateState();
void ChangeState(TrafficState newState);
void NotifyStateChanged();
bool IsInvalidState() const;
bool IsEnabled() const;
GetMwmsByRectFn m_getMwmsByRectFn;
ref_ptr<df::DrapeEngine> m_drapeEngine;
int64_t m_currentDataVersion = 0;
MyPosition m_currentPosition;
ScreenBase m_currentModelView;
pair<MyPosition, bool> m_currentPosition = {MyPosition(), false};
pair<ScreenBase, bool> m_currentModelView = {ScreenBase(), false};
atomic<TrafficState> m_state;
atomic<bool> m_notifyStateChanged;
TrafficStateChangedFn m_onStateChangedFn;
struct CacheEntry
{
CacheEntry() = default;
CacheEntry(time_point<steady_clock> const & requestTime) : m_lastRequestTime(requestTime) {}
bool m_isLoaded = false;
size_t m_dataSize = 0;
time_point<steady_clock> m_lastSeenTime;
time_point<steady_clock> m_lastRequestTime;
size_t m_dataSize = 0;
time_point<steady_clock> m_lastResponseTime;
uint32_t m_retriesCount = 0;
bool m_isWaitingForResponse = false;
traffic::TrafficInfo::Availability m_lastAvailability =
traffic::TrafficInfo::Availability::Unknown;
};
size_t m_maxCacheSizeBytes;
size_t m_currentCacheSizeBytes;
size_t m_currentCacheSizeBytes = 0;
map<MwmSet::MwmId, CacheEntry> m_mwmCache;
bool m_isRunning;
condition_variable m_condition;
vector<MwmSet::MwmId> m_activeMwms;
set<MwmSet::MwmId> m_activeMwms;
vector<MwmSet::MwmId> m_requestedMwms;
mutex m_requestedMwmsLock;

View file

@ -22,18 +22,25 @@ namespace traffic
{
namespace
{
bool ReadRemoteFile(string const & url, vector<uint8_t> & result)
bool ReadRemoteFile(string const & url, string & result, int & errorCode)
{
platform::HttpClient request(url);
if (!request.RunHttpRequest() || request.ErrorCode() != 200)
if (!request.RunHttpRequest())
{
LOG(LINFO, ("Traffic request", url, "failed. HTTP Error:", request.ErrorCode()));
LOG(LINFO, ("Couldn't run traffic request", url));
errorCode = -1;
return false;
}
string const & response = request.ServerResponse();
result.resize(response.size());
for (size_t i = 0; i < response.size(); ++i)
result[i] = static_cast<uint8_t>(response[i]);
errorCode = request.ErrorCode();
result = request.ServerResponse();
if (errorCode != 200)
{
LOG(LINFO, ("Traffic request", url, "failed. HTTP Error:", errorCode));
return false;
}
return true;
}
@ -60,7 +67,10 @@ TrafficInfo::RoadSegmentId::RoadSegmentId(uint32_t fid, uint16_t idx, uint8_t di
}
// TrafficInfo --------------------------------------------------------------------------------
TrafficInfo::TrafficInfo(MwmSet::MwmId const & mwmId) : m_mwmId(mwmId) {}
TrafficInfo::TrafficInfo(MwmSet::MwmId const & mwmId, int64_t const & currentDataVersion)
: m_mwmId(mwmId)
, m_currentDataVersion(currentDataVersion)
{}
bool TrafficInfo::ReceiveTrafficData()
{
@ -73,9 +83,32 @@ bool TrafficInfo::ReceiveTrafficData()
if (url.empty())
return false;
vector<uint8_t> contents;
if (!ReadRemoteFile(url, contents))
string result;
int errorCode;
if (!ReadRemoteFile(url, result, errorCode))
{
if (errorCode == 404)
{
int64_t version = atoi(result.c_str());
if (version > info->GetVersion() && version <= m_currentDataVersion)
m_availabilityStatus = Availability::ExpiredMwm;
else if (version > m_currentDataVersion)
m_availabilityStatus = Availability::ExpiredApp;
else
m_availabilityStatus = Availability::NoData;
}
else
{
m_availabilityStatus = Availability::Unknown;
}
return false;
}
vector<uint8_t> contents;
contents.resize(result.size());
for (size_t i = 0; i < result.size(); ++i)
contents[i] = static_cast<uint8_t>(result[i]);
Coloring coloring;
try
@ -89,6 +122,7 @@ bool TrafficInfo::ReceiveTrafficData()
return false;
}
m_coloring.swap(coloring);
m_availabilityStatus = Availability::IsAvailable;
return true;
}

View file

@ -16,6 +16,15 @@ namespace traffic
class TrafficInfo
{
public:
enum class Availability
{
IsAvailable,
NoData,
ExpiredMwm,
ExpiredApp,
Unknown
};
struct RoadSegmentId
{
// m_dir can be kForwardDirection or kReverseDirection.
@ -56,7 +65,7 @@ public:
TrafficInfo() = default;
TrafficInfo(MwmSet::MwmId const & mwmId);
TrafficInfo(MwmSet::MwmId const & mwmId, int64_t const & currentDataVersion);
// Fetches the latest traffic data from the server and updates the coloring.
// Construct the url by passing an MwmId.
@ -68,6 +77,7 @@ public:
MwmSet::MwmId const & GetMwmId() const { return m_mwmId; }
Coloring const & GetColoring() const { return m_coloring; }
Availability GetAvailability() const { return m_availabilityStatus; }
static void SerializeTrafficData(Coloring const & coloring, vector<uint8_t> & result);
@ -77,5 +87,7 @@ private:
// The mapping from feature segments to speed groups (see speed_groups.hpp).
Coloring m_coloring;
MwmSet::MwmId m_mwmId;
Availability m_availabilityStatus = Availability::Unknown;
int64_t m_currentDataVersion = 0;
};
} // namespace traffic

View file

@ -50,21 +50,21 @@ UNIT_TEST(TrafficInfo_RemoteFile)
TestMwmSet mwmSet;
auto const & r =
mwmSet.Register(platform::LocalCountryFile::MakeForTesting("traffic_data_test"));
TrafficInfo trafficInfo(r.first);
TrafficInfo trafficInfo(r.first, r.first.GetInfo()->GetVersion());
TEST(trafficInfo.ReceiveTrafficData(), ());
}
{
TestMwmSet mwmSet;
auto const & r =
mwmSet.Register(platform::LocalCountryFile::MakeForTesting("traffic_data_test2"));
TrafficInfo trafficInfo(r.first);
TrafficInfo trafficInfo(r.first, r.first.GetInfo()->GetVersion());
TEST(!trafficInfo.ReceiveTrafficData(), ());
}
{
TestMwmSet mwmSet;
auto const & r =
mwmSet.Register(platform::LocalCountryFile::MakeForTesting("traffic_data_test", 101010));
TrafficInfo trafficInfo(r.first);
TrafficInfo trafficInfo(r.first, r.first.GetInfo()->GetVersion());
TEST(trafficInfo.ReceiveTrafficData(), ());
}
}