mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 17:50:04 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			280 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include <scwx/qt/manager/position_manager.hpp>
 | |
| #include <scwx/qt/manager/settings_manager.hpp>
 | |
| #include <scwx/qt/manager/thread_manager.hpp>
 | |
| #include <scwx/qt/settings/general_settings.hpp>
 | |
| #include <scwx/qt/types/location_types.hpp>
 | |
| #include <scwx/common/geographic.hpp>
 | |
| #include <scwx/util/logger.hpp>
 | |
| 
 | |
| #include <mutex>
 | |
| #include <set>
 | |
| 
 | |
| #include <boost/uuid/random_generator.hpp>
 | |
| #include <QAbstractEventDispatcher>
 | |
| #include <QGeoPositionInfoSource>
 | |
| 
 | |
| namespace scwx
 | |
| {
 | |
| namespace qt
 | |
| {
 | |
| namespace manager
 | |
| {
 | |
| 
 | |
| static const std::string logPrefix_ = "scwx::qt::manager::position_manager";
 | |
| static const auto        logger_    = scwx::util::Logger::Create(logPrefix_);
 | |
| 
 | |
| class PositionManager::Impl
 | |
| {
 | |
| public:
 | |
|    explicit Impl(PositionManager* self) :
 | |
|        self_ {self}, trackingUuid_ {boost::uuids::random_generator()()}
 | |
|    {
 | |
|       auto& generalSettings = settings::GeneralSettings::Instance();
 | |
| 
 | |
|       gpsParent_->moveToThread(gpsThread_);
 | |
| 
 | |
|       logger_->debug(
 | |
|          "Available sources: {}",
 | |
|          QGeoPositionInfoSource::availableSources().join(", ").toStdString());
 | |
| 
 | |
|       CreatePositionSourceAsync();
 | |
| 
 | |
|       positioningPluginCallbackUuid_ =
 | |
|          generalSettings.positioning_plugin().RegisterValueChangedCallback(
 | |
|             [this](const std::string&)
 | |
|             { createPositionSourcePending_ = true; });
 | |
|       nmeaBaudRateCallbackUuid_ =
 | |
|          generalSettings.nmea_baud_rate().RegisterValueChangedCallback(
 | |
|             [this](const std::int64_t&)
 | |
|             { createPositionSourcePending_ = true; });
 | |
|       nmeaSourceCallbackUuid_ =
 | |
|          generalSettings.nmea_source().RegisterValueChangedCallback(
 | |
|             [this](const std::string&)
 | |
|             { createPositionSourcePending_ = true; });
 | |
| 
 | |
|       connect(&SettingsManager::Instance(),
 | |
|               &SettingsManager::SettingsSaved,
 | |
|               self_,
 | |
|               [this]()
 | |
|               {
 | |
|                  if (createPositionSourcePending_)
 | |
|                  {
 | |
|                     CreatePositionSourceAsync();
 | |
|                  }
 | |
|               });
 | |
|    }
 | |
|    ~Impl()
 | |
|    {
 | |
|       auto& generalSettings = settings::GeneralSettings::Instance();
 | |
| 
 | |
|       generalSettings.positioning_plugin().UnregisterValueChangedCallback(
 | |
|          positioningPluginCallbackUuid_);
 | |
|       generalSettings.nmea_baud_rate().UnregisterValueChangedCallback(
 | |
|          nmeaBaudRateCallbackUuid_);
 | |
|       generalSettings.nmea_source().UnregisterValueChangedCallback(
 | |
|          nmeaSourceCallbackUuid_);
 | |
| 
 | |
|       gpsParent_->deleteLater();
 | |
|    }
 | |
| 
 | |
|    void CreatePositionSource();
 | |
|    void CreatePositionSourceAsync();
 | |
|    void EnablePositionUpdates(boost::uuids::uuid uuid, bool enabled);
 | |
| 
 | |
|    PositionManager* self_;
 | |
|    QThread* gpsThread_ {ThreadManager::Instance().thread("position_manager")};
 | |
| 
 | |
|    boost::uuids::uuid trackingUuid_;
 | |
|    bool               trackingEnabled_ {false};
 | |
| 
 | |
|    std::set<boost::uuids::uuid> uuids_ {};
 | |
| 
 | |
|    std::mutex positionSourceMutex_ {};
 | |
| 
 | |
|    QObject*                gpsParent_ {new QObject};
 | |
|    QGeoPositionInfoSource* geoPositionInfoSource_ {};
 | |
|    QGeoPositionInfo        position_ {};
 | |
| 
 | |
|    types::PositioningPlugin lastPositioningPlugin_ {
 | |
|       types::PositioningPlugin::Unknown};
 | |
|    std::int64_t lastNmeaBaudRate_ {-1};
 | |
|    std::string  lastNmeaSource_ {"?"};
 | |
| 
 | |
|    boost::uuids::uuid positioningPluginCallbackUuid_ {};
 | |
|    boost::uuids::uuid nmeaBaudRateCallbackUuid_ {};
 | |
|    boost::uuids::uuid nmeaSourceCallbackUuid_ {};
 | |
| 
 | |
|    bool createPositionSourcePending_ {false};
 | |
| };
 | |
| 
 | |
| PositionManager::PositionManager() : p(std::make_unique<Impl>(this)) {}
 | |
| PositionManager::~PositionManager() = default;
 | |
| 
 | |
| QGeoPositionInfo PositionManager::position() const
 | |
| {
 | |
|    return p->position_;
 | |
| }
 | |
| 
 | |
| bool PositionManager::IsLocationTracked()
 | |
| {
 | |
|    return p->trackingEnabled_;
 | |
| }
 | |
| 
 | |
| void PositionManager::Impl::CreatePositionSourceAsync()
 | |
| {
 | |
|    QMetaObject::invokeMethod(QAbstractEventDispatcher::instance(gpsThread_),
 | |
|                              [this]() { CreatePositionSource(); });
 | |
| }
 | |
| 
 | |
| void PositionManager::Impl::CreatePositionSource()
 | |
| {
 | |
|    auto& generalSettings = settings::GeneralSettings::Instance();
 | |
| 
 | |
|    createPositionSourcePending_ = false;
 | |
| 
 | |
|    types::PositioningPlugin positioningPlugin = types::GetPositioningPlugin(
 | |
|       generalSettings.positioning_plugin().GetValue());
 | |
|    std::int64_t nmeaBaudRate = generalSettings.nmea_baud_rate().GetValue();
 | |
|    std::string  nmeaSource   = generalSettings.nmea_source().GetValue();
 | |
| 
 | |
|    if (positioningPlugin == lastPositioningPlugin_ &&
 | |
|        nmeaBaudRate == lastNmeaBaudRate_ && nmeaSource == lastNmeaSource_)
 | |
|    {
 | |
|       return;
 | |
|    }
 | |
| 
 | |
|    QGeoPositionInfoSource* positionSource = nullptr;
 | |
| 
 | |
|    // TODO: macOS requires permission
 | |
|    if (positioningPlugin == types::PositioningPlugin::Default)
 | |
|    {
 | |
|       positionSource = QGeoPositionInfoSource::createDefaultSource(gpsParent_);
 | |
|    }
 | |
|    else if (positioningPlugin == types::PositioningPlugin::Nmea)
 | |
|    {
 | |
|       QVariantMap params {};
 | |
|       params["nmea.source"]   = QString::fromStdString(nmeaSource);
 | |
|       params["nmea.baudrate"] = static_cast<int>(nmeaBaudRate);
 | |
| 
 | |
|       positionSource =
 | |
|          QGeoPositionInfoSource::createSource("nmea", params, gpsParent_);
 | |
|    }
 | |
| 
 | |
|    if (positionSource != nullptr)
 | |
|    {
 | |
|       logger_->debug("Using position source: {}",
 | |
|                      positionSource->sourceName().toStdString());
 | |
| 
 | |
|       QObject::connect(positionSource,
 | |
|                        &QGeoPositionInfoSource::positionUpdated,
 | |
|                        self_,
 | |
|                        [this](const QGeoPositionInfo& info)
 | |
|                        {
 | |
|                           auto coordinate = info.coordinate();
 | |
| 
 | |
|                           if (coordinate != position_.coordinate())
 | |
|                           {
 | |
|                              logger_->trace("Position updated: {}, {}",
 | |
|                                             coordinate.latitude(),
 | |
|                                             coordinate.longitude());
 | |
|                           }
 | |
| 
 | |
|                           position_ = info;
 | |
| 
 | |
|                           Q_EMIT self_->PositionUpdated(info);
 | |
|                        });
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|       logger_->error("Unable to create position source for plugin: {}",
 | |
|                      types::GetPositioningPluginName(positioningPlugin));
 | |
|       return;
 | |
|    }
 | |
| 
 | |
|    lastPositioningPlugin_ = positioningPlugin;
 | |
|    lastNmeaBaudRate_      = nmeaBaudRate;
 | |
|    lastNmeaSource_        = nmeaSource;
 | |
| 
 | |
|    std::unique_lock lock {positionSourceMutex_};
 | |
| 
 | |
|    if (geoPositionInfoSource_ != nullptr)
 | |
|    {
 | |
|       geoPositionInfoSource_->stopUpdates();
 | |
|       delete geoPositionInfoSource_;
 | |
|    }
 | |
| 
 | |
|    geoPositionInfoSource_ = positionSource;
 | |
| 
 | |
|    if (!uuids_.empty())
 | |
|    {
 | |
|       positionSource->startUpdates();
 | |
|    }
 | |
| }
 | |
| 
 | |
| void PositionManager::EnablePositionUpdates(boost::uuids::uuid uuid,
 | |
|                                             bool               enabled)
 | |
| {
 | |
|    QMetaObject::invokeMethod(QAbstractEventDispatcher::instance(p->gpsThread_),
 | |
|                              [=, this]()
 | |
|                              { p->EnablePositionUpdates(uuid, enabled); });
 | |
| }
 | |
| 
 | |
| void PositionManager::Impl::EnablePositionUpdates(boost::uuids::uuid uuid,
 | |
|                                                   bool               enabled)
 | |
| {
 | |
|    std::unique_lock lock {positionSourceMutex_};
 | |
| 
 | |
|    if (geoPositionInfoSource_ == nullptr)
 | |
|    {
 | |
|       return;
 | |
|    }
 | |
| 
 | |
|    if (enabled)
 | |
|    {
 | |
|       if (uuids_.empty())
 | |
|       {
 | |
|          geoPositionInfoSource_->startUpdates();
 | |
|       }
 | |
| 
 | |
|       uuids_.insert(uuid);
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|       uuids_.erase(uuid);
 | |
| 
 | |
|       if (uuids_.empty())
 | |
|       {
 | |
|          geoPositionInfoSource_->stopUpdates();
 | |
|       }
 | |
|    }
 | |
| }
 | |
| 
 | |
| void PositionManager::TrackLocation(bool trackingEnabled)
 | |
| {
 | |
|    p->trackingEnabled_ = trackingEnabled;
 | |
|    EnablePositionUpdates(p->trackingUuid_, trackingEnabled);
 | |
|    Q_EMIT LocationTrackingChanged(trackingEnabled);
 | |
| }
 | |
| 
 | |
| std::shared_ptr<PositionManager> PositionManager::Instance()
 | |
| {
 | |
|    static std::weak_ptr<PositionManager> positionManagerReference_ {};
 | |
|    static std::mutex                     instanceMutex_ {};
 | |
| 
 | |
|    std::unique_lock lock(instanceMutex_);
 | |
| 
 | |
|    std::shared_ptr<PositionManager> positionManager =
 | |
|       positionManagerReference_.lock();
 | |
| 
 | |
|    if (positionManager == nullptr)
 | |
|    {
 | |
|       positionManager           = std::make_shared<PositionManager>();
 | |
|       positionManagerReference_ = positionManager;
 | |
|    }
 | |
| 
 | |
|    return positionManager;
 | |
| }
 | |
| 
 | |
| } // namespace manager
 | |
| } // namespace qt
 | |
| } // namespace scwx
 | 
