diff options
-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 | 18 | ||||
-rw-r--r-- | drape_frontend/drape_engine.cpp | 18 | ||||
-rw-r--r-- | drape_frontend/drape_engine.hpp | 4 | ||||
-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 | 34 | ||||
-rwxr-xr-x | drape_frontend/read_manager.hpp | 12 | ||||
-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/hla_manager.cpp | 350 | ||||
-rw-r--r-- | map/hla_manager.hpp | 81 | ||||
-rw-r--r-- | map/map.pro | 2 |
16 files changed, 556 insertions, 30 deletions
diff --git a/.gitignore b/.gitignore index d8f2b9c31a..bbd9c04cff 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ data/*.mwm.osm2ft data/*.mwm.routing data/*.mwmmeta data/[!W]*.mwm +data/hla*.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..3c5d02f877 100644 --- a/drape_frontend/backend_renderer.cpp +++ b/drape_frontend/backend_renderer.cpp @@ -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/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..a944a675a6 100755 --- a/drape_frontend/read_manager.cpp +++ b/drape_frontend/read_manager.cpp @@ -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..6660e72a23 100755 --- a/drape_frontend/read_manager.hpp +++ b/drape_frontend/read_manager.hpp @@ -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/map/CMakeLists.txt b/map/CMakeLists.txt index c62a9dccf9..cd8d7d8509 100644 --- a/map/CMakeLists.txt +++ b/map/CMakeLists.txt @@ -43,6 +43,8 @@ set( gps_track.hpp gps_tracker.cpp gps_tracker.hpp + hla_manager.cpp + hla_manager.hpp mwm_url.cpp mwm_url.hpp place_page_info.cpp diff --git a/map/framework.cpp b/map/framework.cpp index 1e86acd3db..49100905dd 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -276,6 +276,11 @@ TrafficManager & Framework::GetTrafficManager() return m_trafficManager; } +HLAManager & Framework::GetHLAManager() +{ + return m_hlaManager; +} + 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_hlaManager.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_hlaManager(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_hlaManager.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_hlaManager.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_hlaManager.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_hlaManager.SetDrapeEngine(make_ref(m_drapeEngine)); benchmark::RunGraphicsBenchmark(this); } @@ -1821,6 +1832,7 @@ void Framework::DestroyDrapeEngine() { m_drapeApi.SetEngine(nullptr); m_trafficManager.Teardown(); + m_hlaManager.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..8db53b50db 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/hla_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; + HLAManager m_hlaManager; + /// 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(); + HLAManager & GetHLAManager(); + bool LoadTrafficEnabled(); void SaveTrafficEnabled(bool trafficEnabled); diff --git a/map/hla_manager.cpp b/map/hla_manager.cpp new file mode 100644 index 0000000000..bd97566e90 --- /dev/null +++ b/map/hla_manager.cpp @@ -0,0 +1,350 @@ +#include "map/hla_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" + +namespace +{ +std::string const kCampaignFile = "hla_campaigns.dat"; +std::string const kExpirationFile = "hla_expiration.dat"; +auto constexpr kWWanUpdateTimeout = std::chrono::hours(12); + +std::vector<uint8_t> SerializeTimestamp(std::chrono::steady_clock::time_point ts) +{ + long hours = std::chrono::duration_cast<std::chrono::hours>(ts.time_since_epoch()).count(); + + std::vector<uint8_t> result; + MemWriter<decltype(result)> writer(result); + writer.Write(&hours, sizeof(hours)); + + 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); + long hours = ReadPrimitiveFromSource<long>(src); + + return std::chrono::steady_clock::time_point(std::chrono::hours(hours)); +} + +std::string GetPath(std::string const & fileName) +{ + return my::JoinFoldersToPath(GetPlatform().WritableDir(), fileName); +} +} // namespace + +HLAManager::HLAManager(GetMwmsByRectFn const & getMwmsByRectFn, + GetMwmIdByName const & getMwmIdByName) + : m_getMwmsByRectFn(getMwmsByRectFn) + , m_getMwmIdByNameFn(getMwmIdByName) + , m_thread(&HLAManager::ThreadRoutine, this) +{ + CHECK(m_getMwmsByRectFn != nullptr, ()); + CHECK(m_getMwmIdByNameFn != nullptr, ()); +} + +HLAManager::~HLAManager() +{ +#ifdef DEBUG + { + std::lock_guard<std::mutex> lock(m_mutex); + ASSERT(!m_isRunning, ()); + } +#endif +} + +void HLAManager::Teardown() +{ + { + std::lock_guard<std::mutex> lock(m_mutex); + if (!m_isRunning) + return; + m_isRunning = false; + } + m_condition.notify_one(); + m_thread.join(); +} + +void HLAManager::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 HLAManager::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 HLA 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 HLAManager::ThreadRoutine() +{ + std::string const campaignFile = GetPath(kCampaignFile); + std::string const expirationFile = GetPath(kExpirationFile); + + // Read persistence data (expiration file must be read the 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 HLAManager::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 HLAManager::OnDownloadCountry(std::string const & countryName) +{ + std::lock_guard<std::mutex> lock(m_mutex); + m_campaigns.erase(countryName); + m_expiration.erase(countryName); +} + +void HLAManager::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 HLAManager::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> HLAManager::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 HLAManager::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 HLAManager::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 HLAManager::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 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 HLAManager::SendSymbolsToRendering(df::CustomSymbols && symbols) +{ + 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 HLAManager::DeleteSymbolsFromRendering(MwmSet::MwmId const & mwmId) +{ + if (m_drapeEngine != nullptr) + m_drapeEngine->RemoveCustomSymbols(mwmId); +} diff --git a/map/hla_manager.hpp b/map/hla_manager.hpp new file mode 100644 index 0000000000..fd4acd006b --- /dev/null +++ b/map/hla_manager.hpp @@ -0,0 +1,81 @@ +#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 + +// Hyper Local Ads (HLA) manager. +class HLAManager final +{ +public: + using GetMwmsByRectFn = function<std::vector<MwmSet::MwmId>(m2::RectD const &)>; + using GetMwmIdByName = function<MwmSet::MwmId(std::string const &)>; + + HLAManager(GetMwmsByRectFn const & getMwmsByRectFn, GetMwmIdByName const & getMwmIdByName); + HLAManager(HLAManager && /* hlaManager */) = default; + ~HLAManager(); + + 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..d77cf3f23f 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 \ + hla_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 \ + hla_manager.cpp \ mwm_url.cpp \ place_page_info.cpp \ reachable_by_taxi_checker.cpp \ |