~aleteoryx/muditaos

f93fe176e7ec51753767cdd34630f0873b477d1d — Piotr Tański 4 years ago bdbda7f
[BH-776] Pre-wakeup handling skeleton

First draft of pre-wakeup handling mechanism.
M module-services/service-time/AlarmOperations.cpp => module-services/service-time/AlarmOperations.cpp +30 -17
@@ 11,6 11,7 @@
namespace alarms
{
    std::unique_ptr<IAlarmOperations> CommonAlarmOperationsFactory::create(
        [[maybe_unused]] sys::Service *service,
        std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo,
        IAlarmOperations::GetCurrentTime getCurrentTimeCallback) const
    {


@@ 223,8 224,12 @@ namespace alarms

    auto AlarmOperationsCommon::minuteUpdated(TimePoint now) -> void
    {
        auto isHandlingInProgress = !ongoingSingleEvents.empty();
        processEvents(now);
    }

    void AlarmOperationsCommon::processEvents(TimePoint now)
    {
        const auto isHandlingInProgress = !ongoingSingleEvents.empty();
        if (!nextSingleEvents.empty()) {
            processNextEventsQueue(now);
        }


@@ 246,28 251,36 @@ namespace alarms

    void AlarmOperationsCommon::switchAlarmExecution(const SingleEventRecord &singleAlarmEvent, bool newStateOn)
    {
        alarms::AlarmType alarmType = alarms::AlarmType::None;
        if (typeid(*(singleAlarmEvent.parent)) == typeid(AlarmEventRecord)) {
            alarmType = alarms::AlarmType::Clock;
        }

        auto alarmEventPtr = std::dynamic_pointer_cast<AlarmEventRecord>(singleAlarmEvent.parent);
        if (alarmEventPtr) {
            auto handler = alarmHandlerFactory.getHandler(alarmType);
            if (handler) {
                if (newStateOn) {
                    handler->handle(*alarmEventPtr);
                }
                else {
                    handler->handleOff(*alarmEventPtr);
                }
            }
        if (auto alarmEventPtr = std::dynamic_pointer_cast<AlarmEventRecord>(singleAlarmEvent.parent); alarmEventPtr) {
            handleAlarmEvent(alarmEventPtr, getAlarmEventType(singleAlarmEvent), newStateOn);
        }
        else {
            LOG_WARN("Parent type is not AlarmEventRecord!");
        }
    }

    alarms::AlarmType AlarmOperationsCommon::getAlarmEventType(const SingleEventRecord &event)
    {
        if (typeid(*(event.parent)) == typeid(AlarmEventRecord)) {
            return alarms::AlarmType::Clock;
        }
        return alarms::AlarmType::None;
    }

    void AlarmOperationsCommon::handleAlarmEvent(const std::shared_ptr<AlarmEventRecord> &event,
                                                 alarms::AlarmType alarmType,
                                                 bool newStateOn)
    {
        if (auto handler = alarmHandlerFactory.getHandler(alarmType); handler) {
            if (newStateOn) {
                handler->handle(*event);
            }
            else {
                handler->handleOff(*event);
            }
        }
    }

    auto AlarmOperationsCommon::processNextEventsQueue(const TimePoint now) -> void
    {
        if (nextSingleEvents.front()->startDate <= now) {

M module-services/service-time/AlarmOperations.hpp => module-services/service-time/AlarmOperations.hpp +14 -4
@@ 10,9 10,10 @@

#include <module-db/Interface/AlarmEventRecord.hpp>

#include <Service/Service.hpp>

namespace alarms
{

    class IAlarmOperations
    {
      public:


@@ 56,6 57,7 @@ namespace alarms
        virtual ~IAlarmOperationsFactory() noexcept = default;

        virtual std::unique_ptr<IAlarmOperations> create(
            sys::Service *service,
            std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo,
            IAlarmOperations::GetCurrentTime getCurrentTimeCallback) const = 0;
    };


@@ 63,8 65,8 @@ namespace alarms
    class AlarmOperationsCommon : public IAlarmOperations
    {
      public:
        explicit AlarmOperationsCommon(std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo,
                                       GetCurrentTime getCurrentTimeCallback);
        AlarmOperationsCommon(std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo,
                              GetCurrentTime getCurrentTimeCallback);

        void updateEventsCache(TimePoint now) override;



@@ 86,7 88,7 @@ namespace alarms
        void addAlarmExecutionHandler(const alarms::AlarmType type,
                                      const std::shared_ptr<alarms::AlarmHandler> handler) override;

      private:
      protected:
        std::unique_ptr<AbstractAlarmEventsRepository> alarmEventsRepo;
        AlarmHandlerFactory alarmHandlerFactory;



@@ 95,6 97,12 @@ namespace alarms
        std::vector<std::unique_ptr<SingleEventRecord>> ongoingSingleEvents;
        std::vector<std::unique_ptr<SnoozedAlarmEventRecord>> snoozedSingleEvents;

        alarms::AlarmType getAlarmEventType(const SingleEventRecord &event);
        void handleAlarmEvent(const std::shared_ptr<AlarmEventRecord> &event,
                              alarms::AlarmType alarmType,
                              bool newStateOn);

      private:
        GetCurrentTime getCurrentTimeCallback;

        // Max 100 alarms for one minute seems reasonable, next events will be dropped


@@ 113,6 121,7 @@ namespace alarms

        void checkAndUpdateCache(AlarmEventRecord record);
        void switchAlarmExecution(const SingleEventRecord &singleAlarmEvent, bool newStateOn);
        void processEvents(TimePoint now);
        void processNextEventsQueue(const TimePoint now);
        void processSnoozedEventsQueue(const TimePoint now);



@@ 123,6 132,7 @@ namespace alarms
    {
      public:
        std::unique_ptr<IAlarmOperations> create(
            sys::Service *service,
            std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo,
            IAlarmOperations::GetCurrentTime getCurrentTimeCallback) const override;
    };

M module-services/service-time/ServiceTime.cpp => module-services/service-time/ServiceTime.cpp +3 -4
@@ 35,15 35,14 @@ namespace stm
    constexpr auto automaticTimezoneRules = "UTC0";

    ServiceTime::ServiceTime(const alarms::IAlarmOperationsFactory &alarmOperationsFactory)
        : sys::Service(service::name::service_time, "", StackDepth)
        : sys::Service(service::name::service_time, "", StackDepth), timeManager{std::make_unique<TimeManager>(
                                                                         std::make_unique<RTCCommand>(this))}
    {
        LOG_INFO("[ServiceTime] Initializing");
        bus.channels.push_back(sys::BusChannel::ServiceDBNotifications);

        timeManager = std::make_unique<TimeManager>(std::make_unique<RTCCommand>(this));

        auto alarmEventsRepo = std::make_unique<alarms::AlarmEventsDBRepository>(this);
        auto alarmOperations = alarmOperationsFactory.create(std::move(alarmEventsRepo), TimePointNow);
        auto alarmOperations = alarmOperationsFactory.create(this, std::move(alarmEventsRepo), TimePointNow);
        alarmOperations->updateEventsCache(TimePointNow());
        alarmMessageHandler = std::make_unique<alarms::AlarmMessageHandler>(this, std::move(alarmOperations));
    }

M module-services/service-time/include/service-time/AlarmHandler.hpp => module-services/service-time/include/service-time/AlarmHandler.hpp +2 -0
@@ 12,6 12,8 @@ namespace alarms
        Clock,
        Calendar,
        EveningReminder,
        PreWakeUpChime,
        PreWakeUpFrontlight,
        None
    };


M module-services/service-time/tests/tests-AlarmOperations.cpp => module-services/service-time/tests/tests-AlarmOperations.cpp +1 -1
@@ 113,7 113,7 @@ class AlarmOperationsFixture : public ::testing::Test
  protected:
    auto getMockedAlarmOperations(std::unique_ptr<MockAlarmEventsRepository> &alarmRepo)
    {
        return std::make_unique<alarms::AlarmOperations>(std::move(alarmRepo), timeInjector);
        return std::make_unique<alarms::AlarmOperationsCommon>(std::move(alarmRepo), timeInjector);
    }
};


M products/BellHybrid/BellHybridMain.cpp => products/BellHybrid/BellHybridMain.cpp +1 -1
@@ 66,7 66,7 @@ int main()
    systemServices.emplace_back(sys::CreatorFor<ServiceDB>());
    systemServices.emplace_back(sys::CreatorFor<ServiceAudio>());
    systemServices.emplace_back(sys::CreatorFor<ServiceDesktop>());
    systemServices.emplace_back(sys::CreatorFor<stm::ServiceTime>(alarms::AlarmOperationsFactory{}));
    systemServices.emplace_back(sys::CreatorFor<stm::ServiceTime>());
    systemServices.emplace_back(sys::CreatorFor<service::eink::ServiceEink>());
    systemServices.emplace_back(sys::CreatorFor<service::gui::ServiceGUI>());


M products/BellHybrid/alarms/BellAlarmHandler.cpp => products/BellHybrid/alarms/BellAlarmHandler.cpp +29 -0
@@ 51,4 51,33 @@ namespace alarms
        return true;
    }

    PreWakeUpChimeHandler::PreWakeUpChimeHandler(sys::Service *service) : service{service}
    {}

    auto PreWakeUpChimeHandler::handle(const AlarmEventRecord &record) -> bool
    {
        LOG_INFO("PreWakeUpChimeHandler::handle");
        return true;
    }

    auto PreWakeUpChimeHandler::handleOff(const AlarmEventRecord &record) -> bool
    {
        LOG_INFO("PreWakeUpChimeHandler::handleOff");
        return true;
    }

    PreWakeUpFrontlightHandler::PreWakeUpFrontlightHandler(sys::Service *service) : service{service}
    {}

    auto PreWakeUpFrontlightHandler::handle(const AlarmEventRecord &record) -> bool
    {
        LOG_INFO("PreWakeUpFrontlightHandler::handle");
        return true;
    }

    auto PreWakeUpFrontlightHandler::handleOff(const AlarmEventRecord &record) -> bool
    {
        LOG_INFO("PreWakeUpFrontlightHandler::handleOff");
        return true;
    }
} // namespace alarms

M products/BellHybrid/alarms/include/BellAlarmHandler.hpp => products/BellHybrid/alarms/include/BellAlarmHandler.hpp +26 -0
@@ 29,4 29,30 @@ namespace alarms
        auto handle(const AlarmEventRecord &record) -> bool;
        auto handleOff(const AlarmEventRecord &record) -> bool;
    };

    class PreWakeUpChimeHandler : public AlarmHandler
    {
      public:
        explicit PreWakeUpChimeHandler(sys::Service *service);
        auto handle(const AlarmEventRecord &record) -> bool;
        auto handleOff(const AlarmEventRecord &record) -> bool;

        static constexpr auto name = "PreWakeUpChimeHandler";

      private:
        sys::Service *service{};
    };

    class PreWakeUpFrontlightHandler : public AlarmHandler
    {
      public:
        explicit PreWakeUpFrontlightHandler(sys::Service *service);
        auto handle(const AlarmEventRecord &record) -> bool;
        auto handleOff(const AlarmEventRecord &record) -> bool;

        static constexpr auto name = "PreWakeUpFrontlightHandler";

      private:
        sys::Service *service{};
    };
} // namespace alarms

M products/BellHybrid/services/time/AlarmOperations.cpp => products/BellHybrid/services/time/AlarmOperations.cpp +92 -1
@@ 3,12 3,103 @@

#include <time/AlarmOperations.hpp>

#include <BellAlarmHandler.hpp>

namespace alarms
{
    namespace
    {
        class MockedPreWakeUpSettingsProvider : public PreWakeUpSettingsProvider
        {
          public:
            auto getChimeSettings() -> Settings override
            {
                return {true, std::chrono::minutes{5}};
            }

            auto getFrontlightSettings() -> Settings override
            {
                return {true, std::chrono::minutes{5}};
            }
        };
    } // namespace

    std::unique_ptr<IAlarmOperations> AlarmOperationsFactory::create(
        sys::Service *service,
        std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo,
        IAlarmOperations::GetCurrentTime getCurrentTimeCallback) const
    {
        return std::make_unique<AlarmOperations>(std::move(alarmEventsRepo), getCurrentTimeCallback);
        auto settingsProvider = std::make_unique<MockedPreWakeUpSettingsProvider>();
        auto alarmOperations  = std::make_unique<AlarmOperations>(
            std::move(alarmEventsRepo), getCurrentTimeCallback, std::move(settingsProvider));
        alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::PreWakeUpChime,
                                                  std::make_shared<alarms::PreWakeUpChimeHandler>(service));
        alarmOperations->addAlarmExecutionHandler(alarms::AlarmType::PreWakeUpFrontlight,
                                                  std::make_shared<alarms::PreWakeUpFrontlightHandler>(service));
        return alarmOperations;
    }

    AlarmOperations::AlarmOperations(std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo,
                                     GetCurrentTime getCurrentTimeCallback,
                                     std::unique_ptr<PreWakeUpSettingsProvider> &&settingsProvider)
        : AlarmOperationsCommon{std::move(alarmEventsRepo), std::move(getCurrentTimeCallback)},
          settingsProvider{std::move(settingsProvider)}
    {}

    void AlarmOperations::minuteUpdated(TimePoint now)
    {
        AlarmOperationsCommon::minuteUpdated(now);
        processPreWakeUpEvents(now);
    }

    void AlarmOperations::processPreWakeUpEvents(TimePoint now)
    {
        if (nextSingleEvents.empty()) {
            return;
        }

        auto nextEvent = *(nextSingleEvents.front());
        if (getAlarmEventType(nextEvent) != alarms::AlarmType::Clock) {
            return;
        }

        PreWakeUpHandler handler{settingsProvider.get()};
        preWakeUp(nextEvent, handler.handle(now, nextEvent));
    }

    void AlarmOperations::preWakeUp(const SingleEventRecord &event, PreWakeUpHandler::Result handleResult)
    {
        if (!handleResult.timeForChime && !handleResult.timeForFrontlight) {
            return;
        }

        if (auto alarmEventPtr = std::dynamic_pointer_cast<AlarmEventRecord>(event.parent); alarmEventPtr) {
            if (handleResult.timeForChime) {
                handleAlarmEvent(alarmEventPtr, alarms::AlarmType::PreWakeUpChime, true);
            }
            if (handleResult.timeForFrontlight) {
                handleAlarmEvent(alarmEventPtr, alarms::AlarmType::PreWakeUpFrontlight, true);
            }
        }
    }

    PreWakeUpHandler::PreWakeUpHandler(PreWakeUpSettingsProvider *settingsProvider) : settingsProvider{settingsProvider}
    {}

    auto PreWakeUpHandler::handle(TimePoint now, const SingleEventRecord &event) -> Result
    {
        const auto chimeSettings       = settingsProvider->getChimeSettings();
        const auto frontlightSettings  = settingsProvider->getFrontlightSettings();
        const auto isTimeForChime      = isTimeForPreWakeUp(now, event, chimeSettings);
        const auto isTimeForFrontlight = isTimeForPreWakeUp(now, event, frontlightSettings);
        return {isTimeForChime, isTimeForFrontlight};
    }

    auto PreWakeUpHandler::isTimeForPreWakeUp(TimePoint now,
                                              const SingleEventRecord &event,
                                              PreWakeUpSettingsProvider::Settings settings) -> bool
    {
        const auto expectedAlarmStart = std::chrono::floor<std::chrono::minutes>(now) + settings.timeBeforeAlarm;
        return settings.enabled && std::chrono::floor<std::chrono::minutes>(event.startDate) == expectedAlarmStart;
    }
} // namespace alarms

M products/BellHybrid/services/time/CMakeLists.txt => products/BellHybrid/services/time/CMakeLists.txt +1 -0
@@ 18,6 18,7 @@ target_include_directories(bell-time
target_link_libraries(bell-time
    PRIVATE
        module-sys
        bell::alarms
    PUBLIC
        service-time
)

M products/BellHybrid/services/time/include/time/AlarmOperations.hpp => products/BellHybrid/services/time/include/time/AlarmOperations.hpp +46 -1
@@ 11,13 11,58 @@ namespace alarms
    {
      public:
        std::unique_ptr<IAlarmOperations> create(
            sys::Service *service,
            std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo,
            IAlarmOperations::GetCurrentTime getCurrentTimeCallback) const override;
    };

    class PreWakeUpSettingsProvider
    {
      public:
        struct Settings
        {
            bool enabled{false};
            std::chrono::minutes timeBeforeAlarm{std::chrono::minutes::zero()};
        };

        virtual ~PreWakeUpSettingsProvider() noexcept    = default;
        virtual auto getChimeSettings() -> Settings      = 0;
        virtual auto getFrontlightSettings() -> Settings = 0;
    };

    class PreWakeUpHandler
    {
      public:
        struct Result
        {
            bool timeForChime;
            bool timeForFrontlight;
        };

        explicit PreWakeUpHandler(PreWakeUpSettingsProvider *settingsProvider);
        auto handle(TimePoint now, const SingleEventRecord &event) -> Result;

      private:
        auto isTimeForPreWakeUp(TimePoint now,
                                const SingleEventRecord &event,
                                PreWakeUpSettingsProvider::Settings settings) -> bool;

        PreWakeUpSettingsProvider *settingsProvider;
    };

    class AlarmOperations : public AlarmOperationsCommon
    {
      public:
        using AlarmOperationsCommon::AlarmOperationsCommon;
        AlarmOperations(std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo,
                        GetCurrentTime getCurrentTimeCallback,
                        std::unique_ptr<PreWakeUpSettingsProvider> &&settingsProvider);

        void minuteUpdated(TimePoint now) override;

      private:
        void processPreWakeUpEvents(TimePoint now);
        void preWakeUp(const SingleEventRecord &event, PreWakeUpHandler::Result handleResult);

        std::unique_ptr<PreWakeUpSettingsProvider> settingsProvider;
    };
} // namespace alarms