diff options
author | mpimenov <mpimenov@users.noreply.github.com> | 2017-04-03 14:22:49 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-03 14:22:49 +0300 |
commit | 1e457847cd1499d3d2e347fb46ab859e8a7aa2e3 (patch) | |
tree | 033cc7f929079029aac5efe3fd09644f86602827 | |
parent | b0014ce5e73f7919aab1bd6f1a3fad000b2cbfec (diff) | |
parent | 3890a4c172d40470eee8995b01773caf2eab959d (diff) |
Merge pull request #5729 from rokuz/hla-managerbeta-725
Added Hyper Local Ads (HLA) manager
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | coding/file_container.cpp | 8 | ||||
-rw-r--r-- | coding/file_container.hpp | 1 | ||||
-rw-r--r-- | drape_frontend/backend_renderer.cpp | 24 | ||||
-rw-r--r-- | drape_frontend/drape_engine.cpp | 18 | ||||
-rw-r--r-- | drape_frontend/drape_engine.hpp | 4 | ||||
-rwxr-xr-x | drape_frontend/frontend_renderer.cpp | 27 | ||||
-rwxr-xr-x | drape_frontend/frontend_renderer.hpp | 3 | ||||
-rw-r--r-- | drape_frontend/message.hpp | 3 | ||||
-rw-r--r-- | drape_frontend/message_subclasses.hpp | 23 | ||||
-rwxr-xr-x | drape_frontend/read_manager.cpp | 38 | ||||
-rwxr-xr-x | drape_frontend/read_manager.hpp | 14 | ||||
-rwxr-xr-x | drape_frontend/requested_tiles.cpp | 13 | ||||
-rwxr-xr-x | drape_frontend/requested_tiles.hpp | 9 | ||||
-rw-r--r-- | map/CMakeLists.txt | 2 | ||||
-rw-r--r-- | map/framework.cpp | 21 | ||||
-rw-r--r-- | map/framework.hpp | 8 | ||||
-rw-r--r-- | map/local_ads_manager.cpp | 357 | ||||
-rw-r--r-- | map/local_ads_manager.hpp | 80 | ||||
-rw-r--r-- | map/map.pro | 2 |
20 files changed, 593 insertions, 63 deletions
diff --git a/.gitignore b/.gitignore index d8f2b9c31a..bc975bd19f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ data/*.mwm.osm2ft data/*.mwm.routing data/*.mwmmeta data/[!W]*.mwm +data/local_ads*.dat # Compiled Python *.pyc diff --git a/coding/file_container.cpp b/coding/file_container.cpp index 2598fa299a..69d4a698f7 100644 --- a/coding/file_container.cpp +++ b/coding/file_container.cpp @@ -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() diff --git a/coding/file_container.hpp b/coding/file_container.hpp index 01e3fd8c0e..1307e8e8cd 100644 --- a/coding/file_container.hpp +++ b/coding/file_container.hpp @@ -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(); diff --git a/drape_frontend/backend_renderer.cpp b/drape_frontend/backend_renderer.cpp index 51d8415a1c..67a9e84e91 100644 --- a/drape_frontend/backend_renderer.cpp +++ b/drape_frontend/backend_renderer.cpp @@ -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; diff --git a/drape_frontend/drape_engine.cpp b/drape_frontend/drape_engine.cpp index 8b02efd2b1..e359f731e8 100644 --- a/drape_frontend/drape_engine.cpp +++ b/drape_frontend/drape_engine.cpp @@ -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); } diff --git a/drape_frontend/drape_engine.hpp b/drape_frontend/drape_engine.hpp index 6e5075866b..f7e346f93b 100644 --- a/drape_frontend/drape_engine.hpp +++ b/drape_frontend/drape_engine.hpp @@ -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); diff --git a/drape_frontend/frontend_renderer.cpp b/drape_frontend/frontend_renderer.cpp index 4fb766c64f..7da63bf1f7 100755 --- a/drape_frontend/frontend_renderer.cpp +++ b/drape_frontend/frontend_renderer.cpp @@ -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; } } diff --git a/drape_frontend/frontend_renderer.hpp b/drape_frontend/frontend_renderer.hpp index 7da2399699..6f6666108f 100755 --- a/drape_frontend/frontend_renderer.hpp +++ b/drape_frontend/frontend_renderer.hpp @@ -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 diff --git a/drape_frontend/message.hpp b/drape_frontend/message.hpp index 7c615e1674..69ee6c4172 100644 --- a/drape_frontend/message.hpp +++ b/drape_frontend/message.hpp @@ -75,7 +75,8 @@ public: DrapeApiAddLines, DrapeApiRemove, DrapeApiFlush, - SetCustomSymbols, + AddCustomSymbols, + RemoveCustomSymbols, UpdateCustomSymbols, }; diff --git a/drape_frontend/message_subclasses.hpp b/drape_frontend/message_subclasses.hpp index 1052847afd..ec2388fb1e 100644 --- a/drape_frontend/message_subclasses.hpp +++ b/drape_frontend/message_subclasses.hpp @@ -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: diff --git a/drape_frontend/read_manager.cpp b/drape_frontend/read_manager.cpp index 3f4205edb6..7d500ff7c7 100755 --- a/drape_frontend/read_manager.cpp +++ b/drape_frontend/read_manager.cpp @@ -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 diff --git a/drape_frontend/read_manager.hpp b/drape_frontend/read_manager.hpp index e4d3903d8c..e8ced849fe 100755 --- a/drape_frontend/read_manager.hpp +++ b/drape_frontend/read_manager.hpp @@ -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); diff --git a/drape_frontend/requested_tiles.cpp b/drape_frontend/requested_tiles.cpp index 64c6932862..4624dc9b56 100755 --- a/drape_frontend/requested_tiles.cpp +++ b/drape_frontend/requested_tiles.cpp @@ -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 diff --git a/drape_frontend/requested_tiles.hpp b/drape_frontend/requested_tiles.hpp index a5945bee8d..90f6a830f7 100755 --- a/drape_frontend/requested_tiles.hpp +++ b/drape_frontend/requested_tiles.hpp @@ -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; }; diff --git a/map/CMakeLists.txt b/map/CMakeLists.txt index c62a9dccf9..198302b6f1 100644 --- a/map/CMakeLists.txt +++ b/map/CMakeLists.txt @@ -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 diff --git a/map/framework.cpp b/map/framework.cpp index 1e86acd3db..ead18ca55f 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -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)); +} diff --git a/map/framework.hpp b/map/framework.hpp index bda5373a16..d0b2c6ec1f 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -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); diff --git a/map/local_ads_manager.cpp b/map/local_ads_manager.cpp new file mode 100644 index 0000000000..1a0ce69540 --- /dev/null +++ b/map/local_ads_manager.cpp @@ -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); +} diff --git a/map/local_ads_manager.hpp b/map/local_ads_manager.hpp new file mode 100644 index 0000000000..72b64d3115 --- /dev/null +++ b/map/local_ads_manager.hpp @@ -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; +}; diff --git a/map/map.pro b/map/map.pro index 0d6f9f60fb..6cf25d1408 100644 --- a/map/map.pro +++ b/map/map.pro @@ -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 \ |