Merge pull request #5729 from rokuz/hla-manager

Added Hyper Local Ads (HLA) manager
This commit is contained in:
mpimenov 2017-04-03 15:22:49 +04:00 committed by GitHub
commit 1e457847cd
20 changed files with 593 additions and 63 deletions

1
.gitignore vendored
View file

@ -16,6 +16,7 @@ data/*.mwm.osm2ft
data/*.mwm.routing
data/*.mwmmeta
data/[!W]*.mwm
data/local_ads*.dat
# Compiled Python
*.pyc

View file

@ -440,7 +440,13 @@ void FilesContainerW::Write(ModelReaderPtr reader, Tag const & tag)
void FilesContainerW::Write(vector<char> const & buffer, Tag const & tag)
{
if (!buffer.empty())
GetWriter(tag).Write(&buffer[0], buffer.size());
GetWriter(tag).Write(buffer.data(), buffer.size());
}
void FilesContainerW::Write(vector<uint8_t> const & buffer, Tag const & tag)
{
if (!buffer.empty())
GetWriter(tag).Write(buffer.data(), buffer.size());
}
void FilesContainerW::Finish()

View file

@ -238,6 +238,7 @@ public:
void Write(string const & fPath, Tag const & tag);
void Write(ModelReaderPtr reader, Tag const & tag);
void Write(vector<char> const & buffer, Tag const & tag);
void Write(vector<uint8_t> const & buffer, Tag const & tag);
void Finish();

View file

@ -114,9 +114,9 @@ void BackendRenderer::AcceptMessage(ref_ptr<Message> message)
{
ScreenBase screen;
bool have3dBuildings;
bool needRegenerateTraffic;
m_requestedTiles->GetParams(screen, have3dBuildings, needRegenerateTraffic);
m_readManager->UpdateCoverage(screen, have3dBuildings, needRegenerateTraffic, tiles, m_texMng);
bool forceRequest;
m_requestedTiles->GetParams(screen, have3dBuildings, forceRequest);
m_readManager->UpdateCoverage(screen, have3dBuildings, forceRequest, tiles, m_texMng);
m_updateCurrentCountryFn(screen.ClipRect().Center(), (*tiles.begin()).m_zoomLevel);
}
break;
@ -428,9 +428,9 @@ void BackendRenderer::AcceptMessage(ref_ptr<Message> message)
break;
}
case Message::SetCustomSymbols:
case Message::AddCustomSymbols:
{
ref_ptr<SetCustomSymbolsMessage> msg = message;
ref_ptr<AddCustomSymbolsMessage> msg = message;
CustomSymbols customSymbols = msg->AcceptSymbols();
std::vector<FeatureID> features;
for (auto const & symbol : customSymbols)
@ -442,6 +442,20 @@ void BackendRenderer::AcceptMessage(ref_ptr<Message> message)
break;
}
case Message::RemoveCustomSymbols:
{
ref_ptr<RemoveCustomSymbolsMessage> msg = message;
std::vector<FeatureID> leftoverIds;
if (msg->NeedRemoveAll())
m_readManager->RemoveAllCustomSymbols();
else
m_readManager->RemoveCustomSymbols(msg->GetMwmId(), leftoverIds);
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<UpdateCustomSymbolsMessage>(std::move(leftoverIds)),
MessagePriority::Normal);
break;
}
default:
ASSERT(false, ());
break;

View file

@ -580,10 +580,24 @@ void DrapeEngine::RunScenario(ScenarioManager::ScenarioData && scenarioData,
manager->RunScenario(move(scenarioData), onStartFn, onFinishFn);
}
void DrapeEngine::SetCustomSymbols(CustomSymbols && symbols)
void DrapeEngine::AddCustomSymbols(CustomSymbols && symbols)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<SetCustomSymbolsMessage>(std::move(symbols)),
make_unique_dp<AddCustomSymbolsMessage>(std::move(symbols)),
MessagePriority::Normal);
}
void DrapeEngine::RemoveCustomSymbols(MwmSet::MwmId const & mwmId)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<RemoveCustomSymbolsMessage>(mwmId),
MessagePriority::Normal);
}
void DrapeEngine::RemoveAllCustomSymbols()
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<RemoveCustomSymbolsMessage>(),
MessagePriority::Normal);
}

View file

@ -192,7 +192,9 @@ public:
ScenarioManager::ScenarioCallback const & onStartFn,
ScenarioManager::ScenarioCallback const & onFinishFn);
void SetCustomSymbols(CustomSymbols && symbols);
void AddCustomSymbols(CustomSymbols && symbols);
void RemoveCustomSymbols(MwmSet::MwmId const & mwmId);
void RemoveAllCustomSymbols();
private:
void AddUserEvent(drape_ptr<UserEvent> && e);

View file

@ -135,10 +135,10 @@ FrontendRenderer::FrontendRenderer(Params && params)
, m_requestedTiles(params.m_requestedTiles)
, m_maxGeneration(0)
, m_needRestoreSize(false)
, m_needRegenerateTraffic(false)
, m_trafficEnabled(params.m_trafficEnabled)
, m_overlaysTracker(new OverlaysTracker())
, m_overlaysShowStatsCallback(move(params.m_overlaysShowStatsCallback))
, m_forceUpdateScene(false)
#ifdef SCENARIO_ENABLE
, m_scenarioManager(new ScenarioManager(this))
#endif
@ -339,7 +339,7 @@ void FrontendRenderer::AcceptMessage(ref_ptr<Message> message)
ScreenBase const & screen = m_userEventStream.GetCurrentScreen();
CheckIsometryMinScale(screen);
UpdateDisplacementEnabled();
InvalidateRect(screen.ClipRect());
m_forceUpdateScene = true;
}
if (m_guiRenderer->HasWidget(gui::WIDGET_RULER))
@ -623,7 +623,7 @@ void FrontendRenderer::AcceptMessage(ref_ptr<Message> message)
{
m_enable3dBuildings = msg->Allow3dBuildings();
CheckIsometryMinScale(screen);
InvalidateRect(screen.ClipRect());
m_forceUpdateScene = true;
}
if (m_enablePerspectiveInNavigation != msg->AllowPerspective())
@ -731,7 +731,7 @@ void FrontendRenderer::AcceptMessage(ref_ptr<Message> message)
ref_ptr<EnableTrafficMessage> msg = message;
m_trafficEnabled = msg->IsTrafficEnabled();
if (msg->IsTrafficEnabled())
m_needRegenerateTraffic = true;
m_forceUpdateScene = true;
else
m_trafficRenderer->ClearGLDependentResources();
break;
@ -740,7 +740,7 @@ void FrontendRenderer::AcceptMessage(ref_ptr<Message> message)
case Message::RegenerateTraffic:
case Message::SetSimplifiedTrafficColors:
{
m_needRegenerateTraffic = true;
m_forceUpdateScene = true;
break;
}
@ -781,8 +781,7 @@ void FrontendRenderer::AcceptMessage(ref_ptr<Message> message)
{
ref_ptr<UpdateCustomSymbolsMessage> msg = message;
m_overlaysTracker->SetTrackedOverlaysFeatures(msg->AcceptSymbolsFeatures());
ScreenBase const & screen = m_userEventStream.GetCurrentScreen();
InvalidateRect(screen.ClipRect());
m_forceUpdateScene = true;
break;
}
@ -835,7 +834,7 @@ void FrontendRenderer::UpdateGLResources()
// Request new tiles.
ScreenBase screen = m_userEventStream.GetCurrentScreen();
m_lastReadedModelView = screen;
m_requestedTiles->Set(screen, m_isIsometry || screen.isPerspective(), m_needRegenerateTraffic, ResolveTileKeys(screen));
m_requestedTiles->Set(screen, m_isIsometry || screen.isPerspective(), m_forceUpdateScene, ResolveTileKeys(screen));
m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<UpdateReadManagerMessage>(),
MessagePriority::UberHighSingleton);
@ -890,8 +889,8 @@ void FrontendRenderer::InvalidateRect(m2::RectD const & gRect)
// Request new tiles.
m_lastReadedModelView = screen;
m_requestedTiles->Set(screen, m_isIsometry || screen.isPerspective(),
m_needRegenerateTraffic, ResolveTileKeys(screen));
m_requestedTiles->Set(screen, m_isIsometry || screen.isPerspective(), m_forceUpdateScene,
ResolveTileKeys(screen));
m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<UpdateReadManagerMessage>(),
MessagePriority::UberHighSingleton);
@ -1748,7 +1747,7 @@ void FrontendRenderer::Routine::Do()
isActiveFrame |= m_renderer.m_texMng->UpdateDynamicTextures();
m_renderer.RenderScene(modelView);
if (modelViewChanged || m_renderer.m_needRegenerateTraffic)
if (modelViewChanged || m_renderer.m_forceUpdateScene)
m_renderer.UpdateScene(modelView);
isActiveFrame |= InterpolationHolder::Instance().Advance(frameTime);
@ -1911,16 +1910,16 @@ void FrontendRenderer::UpdateScene(ScreenBase const & modelView)
for (RenderLayer & layer : m_layers)
layer.m_isDirty |= RemoveGroups(removePredicate, layer.m_renderGroups, make_ref(m_overlayTree));
if (m_needRegenerateTraffic || m_lastReadedModelView != modelView)
if (m_forceUpdateScene || m_lastReadedModelView != modelView)
{
EmitModelViewChanged(modelView);
m_lastReadedModelView = modelView;
m_requestedTiles->Set(modelView, m_isIsometry || modelView.isPerspective(), m_needRegenerateTraffic,
m_requestedTiles->Set(modelView, m_isIsometry || modelView.isPerspective(), m_forceUpdateScene,
ResolveTileKeys(modelView));
m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<UpdateReadManagerMessage>(),
MessagePriority::UberHighSingleton);
m_needRegenerateTraffic = false;
m_forceUpdateScene = false;
}
}

View file

@ -333,12 +333,13 @@ private:
bool m_needRestoreSize;
bool m_needRegenerateTraffic;
bool m_trafficEnabled;
drape_ptr<OverlaysTracker> m_overlaysTracker;
OverlaysShowStatsCallback m_overlaysShowStatsCallback;
bool m_forceUpdateScene;
drape_ptr<ScenarioManager> m_scenarioManager;
#ifdef DEBUG

View file

@ -75,7 +75,8 @@ public:
DrapeApiAddLines,
DrapeApiRemove,
DrapeApiFlush,
SetCustomSymbols,
AddCustomSymbols,
RemoveCustomSymbols,
UpdateCustomSymbols,
};

View file

@ -1151,14 +1151,14 @@ private:
TProperties m_properties;
};
class SetCustomSymbolsMessage : public Message
class AddCustomSymbolsMessage : public Message
{
public:
explicit SetCustomSymbolsMessage(CustomSymbols && symbols)
explicit AddCustomSymbolsMessage(CustomSymbols && symbols)
: m_symbols(std::move(symbols))
{}
Type GetType() const override { return Message::SetCustomSymbols; }
Type GetType() const override { return Message::AddCustomSymbols; }
CustomSymbols && AcceptSymbols() { return std::move(m_symbols); }
@ -1166,6 +1166,23 @@ private:
CustomSymbols m_symbols;
};
class RemoveCustomSymbolsMessage : public Message
{
public:
RemoveCustomSymbolsMessage() = default;
explicit RemoveCustomSymbolsMessage(MwmSet::MwmId const & mwmId)
: m_mwmId(mwmId), m_removeAll(false)
{}
Type GetType() const override { return Message::RemoveCustomSymbols; }
bool NeedRemoveAll() const { return m_removeAll; }
MwmSet::MwmId const & GetMwmId() const { return m_mwmId; }
private:
MwmSet::MwmId m_mwmId;
bool m_removeAll = true;
};
class UpdateCustomSymbolsMessage : public Message
{
public:

View file

@ -87,13 +87,13 @@ void ReadManager::OnTaskFinished(threads::IRoutine * task)
myPool.Return(t);
}
void ReadManager::UpdateCoverage(ScreenBase const & screen, bool have3dBuildings, bool needRegenerateTraffic,
void ReadManager::UpdateCoverage(ScreenBase const & screen, bool have3dBuildings, bool forceUpdate,
TTilesCollection const & tiles, ref_ptr<dp::TextureManager> texMng)
{
m_modeChanged |= (m_have3dBuildings != have3dBuildings);
m_have3dBuildings = have3dBuildings;
if (m_modeChanged || needRegenerateTraffic || MustDropAllTiles(screen))
if (m_modeChanged || forceUpdate || MustDropAllTiles(screen))
{
m_modeChanged = false;
@ -280,18 +280,36 @@ void ReadManager::SetTrafficEnabled(bool trafficEnabled)
}
}
void ReadManager::UpdateCustomSymbols(CustomSymbols && symbols)
void ReadManager::UpdateCustomSymbols(CustomSymbols const & symbols)
{
auto customSymbolsContext = std::make_shared<CustomSymbolsContext>(std::move(symbols));
CustomSymbols currentSymbols = m_customSymbolsContext ? m_customSymbolsContext->m_symbols :
CustomSymbols();
for (auto const & s : symbols)
currentSymbols[s.first] = s.second;
m_customSymbolsContext = std::make_shared<CustomSymbolsContext>(std::move(currentSymbols));
}
#if !defined(OMIM_OS_LINUX)
std::atomic_exchange(&m_customSymbolsContext, customSymbolsContext);
#else
void ReadManager::RemoveCustomSymbols(MwmSet::MwmId const & mwmId, std::vector<FeatureID> & leftoverIds)
{
if (!m_customSymbolsContext)
return;
CustomSymbols currentSymbols;
leftoverIds.reserve(m_customSymbolsContext->m_symbols.size());
for (auto const & s : m_customSymbolsContext->m_symbols)
{
lock_guard<mutex> guard(m_customSymbolsContextMutex);
std::swap(m_customSymbolsContext, customSymbolsContext);
if (s.first.m_mwmId != mwmId)
{
currentSymbols.insert(std::make_pair(s.first, s.second));
leftoverIds.push_back(s.first);
}
}
#endif
m_customSymbolsContext = std::make_shared<CustomSymbolsContext>(std::move(currentSymbols));
}
void ReadManager::RemoveAllCustomSymbols()
{
m_customSymbolsContext = std::make_shared<CustomSymbolsContext>(CustomSymbols());
}
} // namespace df

View file

@ -32,7 +32,7 @@ public:
ReadManager(ref_ptr<ThreadsCommutator> commutator, MapDataProvider & model,
bool allow3dBuildings, bool trafficEnabled);
void UpdateCoverage(ScreenBase const & screen, bool have3dBuildings, bool needRegenerateTraffic,
void UpdateCoverage(ScreenBase const & screen, bool have3dBuildings, bool forceUpdate,
TTilesCollection const & tiles, ref_ptr<dp::TextureManager> texMng);
void Invalidate(TTilesCollection const & keyStorage);
void InvalidateAll();
@ -43,7 +43,9 @@ public:
void SetTrafficEnabled(bool trafficEnabled);
void UpdateCustomSymbols(CustomSymbols && symbols);
void UpdateCustomSymbols(CustomSymbols const & symbols);
void RemoveCustomSymbols(MwmSet::MwmId const & mwmId, std::vector<FeatureID> & leftoverIds);
void RemoveAllCustomSymbols();
static uint32_t ReadCount();
@ -53,7 +55,6 @@ private:
void PushTaskBackForTileKey(TileKey const & tileKey, ref_ptr<dp::TextureManager> texMng);
private:
ref_ptr<ThreadsCommutator> m_commutator;
MapDataProvider & m_model;
@ -88,13 +89,6 @@ private:
CustomSymbolsContextPtr m_customSymbolsContext;
// TODO (@y): unfortunately on Debian Jessie libstdc++ does not
// support atomic_exchange for shared pointers, so mutex is used
// instead. Get rid of this as soon as new libstdc++ is released.
#if defined(OMIM_OS_LINUX)
mutex m_customSymbolsContextMutex;
#endif
void CancelTileInfo(shared_ptr<TileInfo> const & tileToCancel);
void ClearTileInfo(shared_ptr<TileInfo> const & tileToClear);
void IncreaseCounter(int value);

View file

@ -3,14 +3,14 @@
namespace df
{
void RequestedTiles::Set(ScreenBase const & screen, bool have3dBuildings,
bool needRegenerateTraffic, TTilesCollection && tiles)
void RequestedTiles::Set(ScreenBase const & screen, bool have3dBuildings, bool forceRequest,
TTilesCollection && tiles)
{
lock_guard<mutex> lock(m_mutex);
m_tiles = move(tiles);
m_screen = screen;
m_have3dBuildings = have3dBuildings;
m_needRegenerateTraffic = needRegenerateTraffic;
m_forceRequest = forceRequest;
}
TTilesCollection RequestedTiles::GetTiles()
@ -23,13 +23,12 @@ TTilesCollection RequestedTiles::GetTiles()
return tiles;
}
void RequestedTiles::GetParams(ScreenBase & screen, bool & have3dBuildings,
bool & needRegenerateTraffic)
void RequestedTiles::GetParams(ScreenBase & screen, bool & have3dBuildings, bool & forceRequest)
{
lock_guard<mutex> lock(m_mutex);
screen = m_screen;
have3dBuildings = m_have3dBuildings;
needRegenerateTraffic = m_needRegenerateTraffic;
forceRequest = m_forceRequest;
}
bool RequestedTiles::CheckTileKey(TileKey const & tileKey) const
@ -41,4 +40,4 @@ bool RequestedTiles::CheckTileKey(TileKey const & tileKey) const
return m_tiles.find(tileKey) != m_tiles.end();
}
} //namespace df
} // namespace df

View file

@ -13,18 +13,17 @@ class RequestedTiles
{
public:
RequestedTiles() = default;
void Set(ScreenBase const & screen, bool have3dBuildings,
bool needRegenerateTraffic, TTilesCollection && tiles);
void Set(ScreenBase const & screen, bool have3dBuildings, bool forceRequest,
TTilesCollection && tiles);
TTilesCollection GetTiles();
void GetParams(ScreenBase & screen, bool & have3dBuildings,
bool & needRegenerateTraffic);
void GetParams(ScreenBase & screen, bool & have3dBuildings, bool & forceRequest);
bool CheckTileKey(TileKey const & tileKey) const;
private:
TTilesCollection m_tiles;
ScreenBase m_screen;
bool m_have3dBuildings = false;
bool m_needRegenerateTraffic = false;
bool m_forceRequest = false;
mutable mutex m_mutex;
};

View file

@ -43,6 +43,8 @@ set(
gps_track.hpp
gps_tracker.cpp
gps_tracker.hpp
local_ads_manager.cpp
local_ads_manager.hpp
mwm_url.cpp
mwm_url.hpp
place_page_info.cpp

View file

@ -276,6 +276,11 @@ TrafficManager & Framework::GetTrafficManager()
return m_trafficManager;
}
LocalAdsManager & Framework::GetLocalAdsManager()
{
return m_localAdsManager;
}
bool Framework::IsTrackingReporterEnabled() const
{
if (m_currentRouterType != routing::RouterType::Vehicle)
@ -309,6 +314,7 @@ void Framework::OnViewportChanged(ScreenBase const & screen)
m_currentModelView = screen;
m_trafficManager.UpdateViewport(m_currentModelView);
m_localAdsManager.UpdateViewport(m_currentModelView);
if (m_viewportChanged != nullptr)
m_viewportChanged(screen);
@ -393,6 +399,7 @@ Framework::Framework()
, m_trafficManager(bind(&Framework::GetMwmsByRect, this, _1), kMaxTrafficCacheSizeBytes,
// Note. |m_routingSession| should be declared before |m_trafficManager|.
m_routingSession)
, m_localAdsManager(bind(&Framework::GetMwmsByRect, this, _1), bind(&Framework::GetMwmIdByName, this, _1))
, m_displacementModeManager([this](bool show) {
int const mode = show ? dp::displacement::kHotelMode : dp::displacement::kDefaultMode;
CallDrapeFunction(bind(&df::DrapeEngine::SetDisplacementMode, _1, mode));
@ -499,6 +506,7 @@ Framework::Framework()
Framework::~Framework()
{
m_trafficManager.Teardown();
m_localAdsManager.Teardown();
DestroyDrapeEngine();
m_model.SetOnMapDeregisteredCallback(nullptr);
}
@ -615,6 +623,7 @@ void Framework::OnCountryFileDownloaded(storage::TCountryId const & countryId, s
rect = id.GetInfo()->m_limitRect;
}
m_trafficManager.Invalidate();
m_localAdsManager.OnDownloadCountry(countryId);
InvalidateRect(rect);
m_searchEngine->ClearCaches();
}
@ -639,6 +648,7 @@ bool Framework::OnCountryFileDelete(storage::TCountryId const & countryId, stora
m_model.DeregisterMap(platform::CountryFile(countryId));
deferredDelete = true;
m_trafficManager.OnMwmDelete(mwmId);
m_localAdsManager.OnDeleteCountry(countryId);
}
InvalidateRect(rect);
@ -1785,6 +1795,7 @@ void Framework::CreateDrapeEngine(ref_ptr<dp::OGLContextFactory> contextFactory,
m_drapeApi.SetEngine(make_ref(m_drapeEngine));
m_trafficManager.SetDrapeEngine(make_ref(m_drapeEngine));
m_localAdsManager.SetDrapeEngine(make_ref(m_drapeEngine));
benchmark::RunGraphicsBenchmark(this);
}
@ -1821,6 +1832,7 @@ void Framework::DestroyDrapeEngine()
{
m_drapeApi.SetEngine(nullptr);
m_trafficManager.Teardown();
m_localAdsManager.Teardown();
GpsTracker::Instance().Disconnect();
m_drapeEngine.reset();
}
@ -3432,7 +3444,7 @@ void Framework::VisualizeRoadsInRect(m2::RectD const & rect)
}, kScale);
}
vector<MwmSet::MwmId> Framework::GetMwmsByRect(m2::RectD const & rect)
vector<MwmSet::MwmId> Framework::GetMwmsByRect(m2::RectD const & rect) const
{
vector<MwmSet::MwmId> result;
if (!m_infoGetter)
@ -3440,7 +3452,12 @@ vector<MwmSet::MwmId> Framework::GetMwmsByRect(m2::RectD const & rect)
auto countryIds = m_infoGetter->GetRegionsCountryIdByRect(rect);
for (auto const & countryId : countryIds)
result.push_back(m_model.GetIndex().GetMwmIdByCountryFile(platform::CountryFile(countryId)));
result.push_back(GetMwmIdByName(countryId));
return result;
}
MwmSet::MwmId Framework::GetMwmIdByName(std::string const & name) const
{
return m_model.GetIndex().GetMwmIdByCountryFile(platform::CountryFile(name));
}

View file

@ -6,6 +6,7 @@
#include "map/city_finder.hpp"
#include "map/displacement_mode_manager.hpp"
#include "map/feature_vec_model.hpp"
#include "map/local_ads_manager.hpp"
#include "map/mwm_url.hpp"
#include "map/place_page_info.hpp"
#include "map/track.hpp"
@ -176,6 +177,8 @@ protected:
TrafficManager m_trafficManager;
LocalAdsManager m_localAdsManager;
/// This function will be called by m_storage when latest local files
/// is downloaded.
void OnCountryFileDownloaded(storage::TCountryId const & countryId, storage::Storage::TLocalFilePtr const localFile);
@ -369,7 +372,8 @@ public:
/// Guarantees that listener is called in the main thread context.
void SetCurrentCountryChangedListener(TCurrentCountryChanged const & listener);
vector<MwmSet::MwmId> GetMwmsByRect(m2::RectD const & rect);
vector<MwmSet::MwmId> GetMwmsByRect(m2::RectD const & rect) const;
MwmSet::MwmId GetMwmIdByName(std::string const & name) const;
private:
struct TapEvent
@ -789,6 +793,8 @@ public:
TrafficManager & GetTrafficManager();
LocalAdsManager & GetLocalAdsManager();
bool LoadTrafficEnabled();
void SaveTrafficEnabled(bool trafficEnabled);

357
map/local_ads_manager.cpp Normal file
View file

@ -0,0 +1,357 @@
#include "map/local_ads_manager.hpp"
#include "drape_frontend/drape_engine.hpp"
#include "drape_frontend/visual_params.hpp"
#include "indexer/scales.hpp"
#include "platform/http_client.hpp"
#include "platform/platform.hpp"
#include "coding/file_container.hpp"
#include "coding/file_name_utils.hpp"
#include "base/bits.hpp"
namespace
{
std::string const kCampaignFile = "local_ads_campaigns.dat";
std::string const kExpirationFile = "local_ads_expiration.dat";
auto constexpr kWWanUpdateTimeout = std::chrono::hours(12);
std::vector<uint8_t> SerializeTimestamp(std::chrono::steady_clock::time_point ts)
{
auto hours = std::chrono::duration_cast<std::chrono::hours>(ts.time_since_epoch()).count();
uint64_t const encodedHours = bits::ZigZagEncode(static_cast<int64_t>(hours));
std::vector<uint8_t> result;
MemWriter<decltype(result)> writer(result);
writer.Write(&encodedHours, sizeof(encodedHours));
return result;
}
std::chrono::steady_clock::time_point DeserializeTimestamp(ModelReaderPtr const & reader)
{
ASSERT_EQUAL(reader.Size(), sizeof(long), ());
std::vector<uint8_t> bytes(reader.Size());
reader.Read(0, bytes.data(), bytes.size());
MemReaderWithExceptions memReader(bytes.data(), bytes.size());
ReaderSource<decltype(memReader)> src(memReader);
uint64_t hours = ReadPrimitiveFromSource<uint64_t>(src);
int64_t const decodedHours = bits::ZigZagDecode(hours);
return std::chrono::steady_clock::time_point(std::chrono::hours(decodedHours));
}
std::string GetPath(std::string const & fileName)
{
return my::JoinFoldersToPath(GetPlatform().WritableDir(), fileName);
}
} // namespace
LocalAdsManager::LocalAdsManager(GetMwmsByRectFn const & getMwmsByRectFn,
GetMwmIdByName const & getMwmIdByName)
: m_getMwmsByRectFn(getMwmsByRectFn)
, m_getMwmIdByNameFn(getMwmIdByName)
, m_thread(&LocalAdsManager::ThreadRoutine, this)
{
CHECK(m_getMwmsByRectFn != nullptr, ());
CHECK(m_getMwmIdByNameFn != nullptr, ());
}
LocalAdsManager::~LocalAdsManager()
{
#ifdef DEBUG
{
std::lock_guard<std::mutex> lock(m_mutex);
ASSERT(!m_isRunning, ());
}
#endif
}
void LocalAdsManager::Teardown()
{
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_isRunning)
return;
m_isRunning = false;
}
m_condition.notify_one();
m_thread.join();
}
void LocalAdsManager::SetDrapeEngine(ref_ptr<df::DrapeEngine> engine)
{
m_drapeEngine = engine;
{
std::lock_guard<std::mutex> lock(m_symbolsCacheMutex);
if (!m_symbolsCache.empty())
m_drapeEngine->AddCustomSymbols(std::move(m_symbolsCache));
}
}
void LocalAdsManager::UpdateViewport(ScreenBase const & screen)
{
auto connectionStatus = GetPlatform().ConnectionStatus();
if (connectionStatus == Platform::EConnectionType::CONNECTION_NONE ||
df::GetZoomLevel(screen.GetScale()) <= scales::GetUpperWorldScale())
{
return;
}
std::vector<std::string> requestedCampaigns;
auto mwms = m_getMwmsByRectFn(screen.ClipRect());
if (mwms.empty())
return;
// Request local ads campaigns.
{
std::lock_guard<std::mutex> lock(m_mutex);
for (auto const & mwm : mwms)
{
auto info = mwm.GetInfo();
if (!info)
continue;
std::string const & mwmName = info->GetCountryName();
auto campaignIt = m_campaigns.find(mwmName);
if (campaignIt == m_campaigns.end())
{
requestedCampaigns.push_back(mwmName);
continue;
}
// If a campaign has not been requested from server this session.
if (!campaignIt->second)
{
auto const it = m_expiration.find(mwmName);
bool needUpdateByTimeout = (connectionStatus == Platform::EConnectionType::CONNECTION_WIFI);
if (!needUpdateByTimeout && it != m_expiration.end())
{
auto const currentTime = std::chrono::steady_clock::now();
needUpdateByTimeout = currentTime > (it->second + kWWanUpdateTimeout);
}
if (needUpdateByTimeout || it == m_expiration.end())
requestedCampaigns.push_back(mwmName);
}
}
if (!requestedCampaigns.empty())
{
m_requestedCampaigns.reserve(m_requestedCampaigns.size() + requestedCampaigns.size());
for (auto const & campaign : requestedCampaigns)
m_requestedCampaigns.push_back(std::make_pair(m_getMwmIdByNameFn(campaign), RequestType::Download));
m_condition.notify_one();
}
}
}
void LocalAdsManager::ThreadRoutine()
{
std::string const campaignFile = GetPath(kCampaignFile);
std::string const expirationFile = GetPath(kExpirationFile);
// Read persistence data (expiration file must be read first).
ReadExpirationFile(expirationFile);
ReadCampaignFile(campaignFile);
std::vector<Request> campaignMwms;
while (WaitForRequest(campaignMwms))
{
try
{
FilesContainerW campaignsContainer(campaignFile, FileWriter::OP_WRITE_EXISTING);
FilesContainerW expirationContainer(expirationFile, FileWriter::OP_WRITE_EXISTING);
for (auto const & mwm : campaignMwms)
{
if (!mwm.first.IsAlive())
continue;
std::string const countryName = mwm.first.GetInfo()->GetCountryName();
if (mwm.second == RequestType::Download)
{
// Download campaign data from server.
std::vector<uint8_t> rawData = DownloadCampaign(mwm.first);
if (rawData.empty())
continue;
// Save data persistently.
campaignsContainer.Write(rawData, countryName);
auto ts = std::chrono::steady_clock::now();
expirationContainer.Write(SerializeTimestamp(ts), countryName);
// Update run-time data.
{
std::lock_guard<std::mutex> lock(m_mutex);
m_campaigns[countryName] = true;
m_expiration[countryName] = ts;
}
// Deserialize and send data to rendering.
auto symbols = DeserializeCampaign(std::move(rawData), ts);
if (symbols.empty())
DeleteSymbolsFromRendering(mwm.first);
else
SendSymbolsToRendering(std::move(symbols));
}
else if (mwm.second == RequestType::Delete)
{
campaignsContainer.DeleteSection(countryName);
expirationContainer.DeleteSection(countryName);
DeleteSymbolsFromRendering(mwm.first);
}
}
campaignsContainer.Finish();
expirationContainer.Finish();
}
catch (RootException const & ex)
{
LOG(LWARNING, (ex.Msg()));
}
campaignMwms.clear();
}
}
bool LocalAdsManager::WaitForRequest(std::vector<Request> & campaignMwms)
{
std::unique_lock<std::mutex> lock(m_mutex);
m_condition.wait(lock, [this] {return !m_isRunning || !m_requestedCampaigns.empty();});
if (!m_isRunning)
return false;
if (!m_requestedCampaigns.empty())
campaignMwms.swap(m_requestedCampaigns);
return true;
}
void LocalAdsManager::OnDownloadCountry(std::string const & countryName)
{
std::lock_guard<std::mutex> lock(m_mutex);
m_campaigns.erase(countryName);
m_expiration.erase(countryName);
}
void LocalAdsManager::OnDeleteCountry(std::string const & countryName)
{
std::lock_guard<std::mutex> lock(m_mutex);
m_campaigns.erase(countryName);
m_expiration.erase(countryName);
m_requestedCampaigns.push_back(std::make_pair(m_getMwmIdByNameFn(countryName), RequestType::Delete));
m_condition.notify_one();
}
string LocalAdsManager::MakeRemoteURL(MwmSet::MwmId const &mwmId) const
{
// TODO: build correct URL after server completion.
return "http://172.27.15.68/campaigns.data";
}
std::vector<uint8_t> LocalAdsManager::DownloadCampaign(MwmSet::MwmId const & mwmId) const
{
platform::HttpClient request(MakeRemoteURL(mwmId));
if (!request.RunHttpRequest() || request.ErrorCode() != 200)
return std::vector<uint8_t>();
string const & response = request.ServerResponse();
return std::vector<uint8_t>(response.cbegin(), response.cend());
}
df::CustomSymbols LocalAdsManager::DeserializeCampaign(std::vector<uint8_t> && rawData,
std::chrono::steady_clock::time_point timestamp)
{
df::CustomSymbols symbols;
// TODO: Deserialize campaign.
// TODO: Filter by timestamp.
// TEMP!
//auto mwmId = m_getMwmIdByNameFn("Russia_Moscow");
//symbols.insert(std::make_pair(FeatureID(mwmId, 371323), df::CustomSymbol("test-m", true)));
//symbols.insert(std::make_pair(FeatureID(mwmId, 371363), df::CustomSymbol("test-m", true)));
//symbols.insert(std::make_pair(FeatureID(mwmId, 373911), df::CustomSymbol("test-m", true)));
return symbols;
}
void LocalAdsManager::ReadExpirationFile(std::string const & expirationFile)
{
if (!GetPlatform().IsFileExistsByFullPath(expirationFile))
{
FilesContainerW f(expirationFile);
return;
}
try
{
std::lock_guard<std::mutex> lock(m_mutex);
FilesContainerR expirationContainer(expirationFile);
expirationContainer.ForEachTag([this, &expirationContainer](FilesContainerR::Tag const & tag)
{
m_expiration[tag] = DeserializeTimestamp(expirationContainer.GetReader(tag));
});
}
catch (Reader::Exception const & ex)
{
LOG(LWARNING, ("Error reading file:", expirationFile, ex.Msg()));
}
}
void LocalAdsManager::ReadCampaignFile(std::string const & campaignFile)
{
if (!GetPlatform().IsFileExistsByFullPath(campaignFile))
{
FilesContainerW f(campaignFile);
return;
}
try
{
std::lock_guard<std::mutex> lock(m_mutex);
df::CustomSymbols allSymbols;
FilesContainerR campaignContainer(campaignFile);
campaignContainer.ForEachTag([this, &campaignContainer, &allSymbols](FilesContainerR::Tag const & tag)
{
auto const & reader = campaignContainer.GetReader(tag);
std::vector<uint8_t> rawData(reader.Size());
reader.Read(0, rawData.data(), rawData.size());
m_campaigns[tag] = false;
ASSERT(m_expiration.find(tag) != m_expiration.end(), ());
auto ts = m_expiration[tag];
auto const symbols = DeserializeCampaign(std::move(rawData), ts);
allSymbols.insert(symbols.begin(), symbols.end());
});
SendSymbolsToRendering(std::move(allSymbols));
}
catch (Reader::Exception const & ex)
{
LOG(LWARNING, ("Error reading file:", campaignFile, ex.Msg()));
}
}
void LocalAdsManager::SendSymbolsToRendering(df::CustomSymbols && symbols)
{
if (symbols.empty())
return;
if (m_drapeEngine == nullptr)
{
std::lock_guard<std::mutex> lock(m_symbolsCacheMutex);
m_symbolsCache.insert(symbols.begin(), symbols.end());
return;
}
m_drapeEngine->AddCustomSymbols(std::move(symbols));
}
void LocalAdsManager::DeleteSymbolsFromRendering(MwmSet::MwmId const & mwmId)
{
if (m_drapeEngine != nullptr)
m_drapeEngine->RemoveCustomSymbols(mwmId);
}

80
map/local_ads_manager.hpp Normal file
View file

@ -0,0 +1,80 @@
#pragma once
#include "drape_frontend/custom_symbol.hpp"
#include "drape/pointers.hpp"
#include "geometry/rect2d.hpp"
#include "geometry/screenbase.hpp"
#include "indexer/index.hpp"
#include "indexer/mwm_set.hpp"
#include "base/thread.hpp"
#include <chrono>
#include <map>
#include <mutex>
#include <string>
#include <vector>
namespace df
{
class DrapeEngine;
} // namespace df
class LocalAdsManager final
{
public:
using GetMwmsByRectFn = function<std::vector<MwmSet::MwmId>(m2::RectD const &)>;
using GetMwmIdByName = function<MwmSet::MwmId(std::string const &)>;
LocalAdsManager(GetMwmsByRectFn const & getMwmsByRectFn, GetMwmIdByName const & getMwmIdByName);
LocalAdsManager(LocalAdsManager && /* localAdsManager */) = default;
~LocalAdsManager();
void Teardown();
void SetDrapeEngine(ref_ptr<df::DrapeEngine> engine);
void UpdateViewport(ScreenBase const & screen);
void OnDownloadCountry(std::string const & countryName);
void OnDeleteCountry(std::string const & countryName);
private:
enum class RequestType
{
Download,
Delete
};
using Request = std::pair<MwmSet::MwmId, RequestType>;
void ThreadRoutine();
bool WaitForRequest(std::vector<Request> & campaignMwms);
std::string MakeRemoteURL(MwmSet::MwmId const & mwmId) const;
std::vector<uint8_t> DownloadCampaign(MwmSet::MwmId const & mwmId) const;
df::CustomSymbols DeserializeCampaign(std::vector<uint8_t> && rawData,
std::chrono::steady_clock::time_point timestamp);
void SendSymbolsToRendering(df::CustomSymbols && symbols);
void DeleteSymbolsFromRendering(MwmSet::MwmId const & mwmId);
void ReadExpirationFile(std::string const & expirationFile);
void ReadCampaignFile(std::string const & campaignFile);
GetMwmsByRectFn m_getMwmsByRectFn;
GetMwmIdByName m_getMwmIdByNameFn;
ref_ptr<df::DrapeEngine> m_drapeEngine;
std::map<std::string, bool> m_campaigns;
std::map<std::string, std::chrono::steady_clock::time_point> m_expiration;
df::CustomSymbols m_symbolsCache;
std::mutex m_symbolsCacheMutex;
bool m_isRunning = true;
std::condition_variable m_condition;
std::vector<Request> m_requestedCampaigns;
std::mutex m_mutex;
threads::SimpleThread m_thread;
};

View file

@ -27,6 +27,7 @@ HEADERS += \
gps_track_filter.hpp \
gps_track_storage.hpp \
gps_tracker.hpp \
local_ads_manager.hpp \
mwm_url.hpp \
place_page_info.hpp \
reachable_by_taxi_checker.hpp \
@ -53,6 +54,7 @@ SOURCES += \
gps_track_filter.cpp \
gps_track_storage.cpp \
gps_tracker.cpp \
local_ads_manager.cpp \
mwm_url.cpp \
place_page_info.cpp \
reachable_by_taxi_checker.cpp \