~aleteoryx/muditaos

a0c529580f966fb8aa6c9cdb02eae89f536e5f74 — Mateusz Piesta 4 years ago d863c4c
[BH-658] Alarm model update

Updated alarm model.
Added state controller logger
M products/BellHybrid/apps/application-bell-main/ApplicationBellMain.cpp => products/BellHybrid/apps/application-bell-main/ApplicationBellMain.cpp +7 -4
@@ 10,6 10,7 @@
#include "windows/BellMainMenuWindow.hpp"

#include <common/models/AlarmModel.hpp>
#include <service-db/DBNotificationMessage.hpp>
#include <windows/Dialog.hpp>

namespace app


@@ 21,6 22,7 @@ namespace app
                                             StartInBackground startInBackground)
        : Application(name, parent, mode, bluetoothMode, startInBackground)
    {
        bus.channels.push_back(sys::BusChannel::ServiceDBNotifications);
        addActionReceiver(manager::actions::ShowAlarm, [this](auto &&data) {
            switchWindow(gui::name::window::main_window, std::move(data));
            return actionHandled();


@@ 67,13 69,14 @@ namespace app
        if (respMessage != nullptr && respMessage->retCode == sys::ReturnCodes::Success) {
            return retMsg;
        }
        if (resp != nullptr) {
            if (auto command = callbackStorage->getCallback(resp); command->execute()) {
                refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
        auto msg = dynamic_cast<db::NotificationMessage *>(msgl);
        if (msg != nullptr) {
            for (auto &[name, window] : windowsStack.windows) {
                window->onDatabaseMessage(msg);
            }
            return sys::msgHandled();
        }
        return std::make_shared<sys::ResponseMessage>();
        return handleAsyncResponse(resp);
    }

    void ApplicationBellMain::showPopup(gui::popup::ID id, const gui::PopupRequestParams *params)

M products/BellHybrid/apps/application-bell-main/CMakeLists.txt => products/BellHybrid/apps/application-bell-main/CMakeLists.txt +1 -0
@@ 21,6 21,7 @@ target_sources(application-bell-main

        presenters/HomeScreenPresenter.hpp
        presenters/StateController.hpp
        presenters/StateControllerLogger.hpp

    PUBLIC
        include/application-bell-main/ApplicationBellMain.hpp

M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp => products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp +7 -0
@@ 10,6 10,7 @@
#include <module-sys/Timers/SystemTimer.hpp>
#include <module-sys/Timers/TimerFactory.hpp>
#include <time/time_constants.hpp>
#include <service-db/DBNotificationMessage.hpp>

namespace app::home_screen
{


@@ 77,4 78,10 @@ namespace app::home_screen
    {
        app->refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
    }
    void HomeScreenPresenter::onDatabaseMessage(db::NotificationMessage *msg)
    {
        if (msg->interface == db::Interface::Name::AlarmEvents && msg->type == db::Query::Type::Update) {
            alarmModel->update();
        }
    }
} // namespace app::home_screen

M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.hpp => products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.hpp +7 -0
@@ 25,6 25,11 @@ namespace gui
    class AppWindow;
}

namespace db
{
    class NotificationMessage;
}

namespace app::home_screen
{
    class AbstractTimeModel;


@@ 69,6 74,7 @@ namespace app::home_screen
        virtual void handleUpdateTimeEvent()                                        = 0;
        virtual bool handleInputEvent(const gui::InputEvent &inputEvent)            = 0;
        virtual void onBeforeShow()                                                 = 0;
        virtual void onDatabaseMessage(db::NotificationMessage *msg)                = 0;
        virtual void refreshWindow()                                                = 0;
        virtual void spawnTimer(std::chrono::milliseconds timeout = defaultTimeout) = 0;
        virtual void detachTimer()                                                  = 0;


@@ 95,6 101,7 @@ namespace app::home_screen
        void handleUpdateTimeEvent() override;
        bool handleInputEvent(const gui::InputEvent &inputEvent) override;
        void onBeforeShow() override;
        void onDatabaseMessage(db::NotificationMessage *msg) override;
        void refreshWindow() override;

        void spawnTimer(std::chrono::milliseconds timeout) override;

M products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp => products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp +53 -31
@@ 11,6 11,13 @@
#include <time/time_conversion.hpp>
#include <time/time_constants.hpp>

/// Uncomment to print state machine debug logs
/// #define DEBUG_STATE_MACHINE 1U

#ifdef DEBUG_STATE_MACHINE
#include "StateControllerLogger.hpp"
#endif

namespace app::home_screen
{
    namespace sml = boost::sml;


@@ 42,20 49,22 @@ namespace app::home_screen
                            std::to_string(duration.getMinutes()) + " min");
            };

            auto setDefaultAlarmTime = [](AbstractView &view, AbstractTimeModel &timeModel) {
                constexpr auto defaultAlarmTimeHour = 7U;
                constexpr auto defaultAlarmTimeMin  = 0U;
                const auto now                      = timeModel.getCurrentTime();
                const auto newTime                  = std::localtime(&now);
                newTime->tm_hour                    = defaultAlarmTimeHour;
                newTime->tm_min                     = defaultAlarmTimeMin;
                auto alarmTime                      = std::mktime(newTime);

                if (alarmTime < now) {
                    alarmTime += utils::time::secondsInDay;
                }
                view.setAlarmTime(alarmTime);
            };
            auto setDefaultAlarmTime =
                [](AbstractView &view, AbstractAlarmModel &alarmModel, AbstractTimeModel &timeModel) {
                    constexpr auto defaultAlarmTimeHour = 7U;
                    constexpr auto defaultAlarmTimeMin  = 0U;
                    const auto now                      = timeModel.getCurrentTime();
                    const auto newTime                  = std::localtime(&now);
                    newTime->tm_hour                    = defaultAlarmTimeHour;
                    newTime->tm_min                     = defaultAlarmTimeMin;
                    auto alarmTime                      = std::mktime(newTime);

                    if (alarmTime < now) {
                        alarmTime += utils::time::secondsInDay;
                    }
                    view.setAlarmTime(alarmTime);
                    alarmModel.setAlarmTime(alarmTime);
                };

            auto isAlarmActive = [](AbstractAlarmModel &alarmModel) -> bool { return alarmModel.isActive(); };



@@ 85,14 94,16 @@ namespace app::home_screen

        namespace Deactivated
        {
            auto entry =
                [](AbstractView &view, AbstractTemperatureModel &temperatureModel, AbstractTimeModel &timeModel) {
                    Helpers::setDefaultAlarmTime(view, timeModel);
                    view.setAlarmEdit(false);
                    view.setAlarmActive(false);
                    view.setAlarmVisible(false);
                    view.setTemperature(temperatureModel.getTemperature());
                };
            auto entry = [](AbstractView &view,
                            AbstractTemperatureModel &temperatureModel,
                            AbstractAlarmModel &alarmModel,
                            AbstractTimeModel &timeModel) {
                Helpers::setDefaultAlarmTime(view, alarmModel, timeModel);
                view.setAlarmEdit(false);
                view.setAlarmActive(false);
                view.setAlarmVisible(false);
                view.setTemperature(temperatureModel.getTemperature());
            };
        } // namespace Deactivated

        namespace DeactivatedWait


@@ 114,10 125,7 @@ namespace app::home_screen
                view.setAlarmTimeVisible(true);
                view.setAlarmVisible(true);
            };
            auto exit = [](AbstractView &view, AbstractAlarmModel &alarmModel) {
                view.setAlarmEdit(false);
                alarmModel.setAlarmTime(view.getAlarmTime());
            };
            auto exit = [](AbstractView &view) { view.setAlarmEdit(false); };

            auto processRotateLeft = [](AbstractView &view, AbstractPresenter &presenter) {
                presenter.spawnTimer();


@@ 150,6 158,7 @@ namespace app::home_screen
                            AbstractPresenter &presenter,
                            AbstractAlarmModel &alarmModel,
                            AbstractTimeModel &timeModel) {
                alarmModel.setAlarmTime(view.getAlarmTime());
                alarmModel.activate(true);
                presenter.spawnTimer();
                view.setBottomDescription(


@@ 164,10 173,10 @@ namespace app::home_screen
        {
            auto entry =
                [](AbstractView &view, AbstractAlarmModel &alarmModel, AbstractTemperatureModel &temperatureModel) {
                    view.setAlarmTime(alarmModel.getAlarmTime());
                    view.setTemperature(temperatureModel.getTemperature());
                    view.setAlarmActive(true);
                    view.setAlarmVisible(true);
                    view.setAlarmTime(alarmModel.getAlarmTime());
                };
        } // namespace Activated



@@ 223,9 232,9 @@ namespace app::home_screen
            {
                using namespace sml;
                // clang-format off
                return make_transition_table(*"Deactivated"_s + event<Events::LightPress>/ Helpers::switchToMenu = "Deactivated"_s,
                                             "Deactivated"_s + sml::on_entry<_> / Deactivated::entry,
                return make_transition_table(*"Deactivated"_s + sml::on_entry<_> / Deactivated::entry,
                                             "Deactivated"_s [Helpers::isAlarmActive] = "Activated"_s,
                                             "Deactivated"_s + event<Events::LightPress>/ Helpers::switchToMenu = "Deactivated"_s,
                                             "Deactivated"_s + event<Events::RotateRightPress> / Helpers::makeAlarmEditable = "DeactivatedEdit"_s,
                                             "Deactivated"_s + event<Events::DeepUpPress> / Helpers::showAlarmTime = "ActivatedWait"_s,
                                             "Deactivated"_s + event<Events::TimeUpdate> / Helpers::updateTemperature,


@@ 255,6 264,7 @@ namespace app::home_screen
                                             "ActivatedWait"_s + event<Events::LightPress>/ Helpers::switchToMenu = "Activated"_s,

                                             "Activated"_s + sml::on_entry<_> / Activated::entry,
                                             "Activated"_s [not Helpers::isAlarmActive] = "Deactivated"_s,
                                             "Activated"_s + event<Events::LightPress>/ Helpers::switchToMenu = "Activated"_s,
                                             "Activated"_s + event<Events::RotateRightPress> / Helpers::makeAlarmEditable = "ActivatedEdit"_s,
                                             "Activated"_s + event<Events::TimeUpdate> / Helpers::updateTemperature,


@@ 305,10 315,22 @@ namespace app::home_screen
             AbstractTemperatureModel &temperatureModel,
             AbstractAlarmModel &alarmModel,
             AbstractTimeModel &timeModel)
            : sm{view, presenter, temperatureModel, alarmModel, timeModel}
            : sm{
#ifdef DEBUG_STATE_MACHINE
                  smLogger,
#endif
                  view,
                  presenter,
                  temperatureModel,
                  alarmModel,
                  timeModel}
        {}

#ifdef DEBUG_STATE_MACHINE
        using SM = sml::sm<StateMachine, sml::logger<Logger>>;
        Logger smLogger;
#else
        using SM = sml::sm<StateMachine>;
#endif
        SM sm;
    };


A products/BellHybrid/apps/application-bell-main/presenters/StateControllerLogger.hpp => products/BellHybrid/apps/application-bell-main/presenters/StateControllerLogger.hpp +39 -0
@@ 0,0 1,39 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <boost/sml.hpp>
#include <log/log.hpp>

struct Logger
{
    template <class SM, class TEvent> void log_process_event(const TEvent &)
    {
        LOG_DEBUG(
            "[%s][process_event] %s\n", boost::sml::aux::get_type_name<SM>(), boost::sml::aux::get_type_name<TEvent>());
    }

    template <class SM, class TGuard, class TEvent> void log_guard(const TGuard &, const TEvent &, bool result)
    {
        LOG_DEBUG("[%s][guard] %s %s %s\n",
                  boost::sml::aux::get_type_name<SM>(),
                  boost::sml::aux::get_type_name<TGuard>(),
                  boost::sml::aux::get_type_name<TEvent>(),
                  (result ? "[OK]" : "[Reject]"));
    }

    template <class SM, class TAction, class TEvent> void log_action(const TAction &, const TEvent &)
    {
        LOG_DEBUG("[%s][action] %s %s\n",
                  boost::sml::aux::get_type_name<SM>(),
                  boost::sml::aux::get_type_name<TAction>(),
                  boost::sml::aux::get_type_name<TEvent>());
    }

    template <class SM, class TSrcState, class TDstState>
    void log_state_change(const TSrcState &src, const TDstState &dst)
    {
        LOG_DEBUG("[%s][transition] %s -> %s\n", boost::sml::aux::get_type_name<SM>(), src.c_str(), dst.c_str());
    }
};

M products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.cpp => products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.cpp +10 -0
@@ 11,6 11,7 @@
#include <gui/widgets/TextFixedSize.hpp>
#include <gui/widgets/Style.hpp>
#include <i18n/i18n.hpp>
#include <service-db/DBNotificationMessage.hpp>
#include <service-time/api/TimeSettingsApi.hpp>
#include <time/time_constants.hpp>
#include <widgets/AlarmSetSpinner.hpp>


@@ 245,4 246,13 @@ namespace gui
        handleMinuteDecrease(*newTime);
        alarm->setTime(std::mktime(newTime));
    }
    bool BellHomeScreenWindow::onDatabaseMessage(sys::Message *msg)
    {
        auto *msgNotification = dynamic_cast<db::NotificationMessage *>(msg);
        if (msgNotification != nullptr) {
            presenter->onDatabaseMessage(msgNotification);
            return true;
        }
        return false;
    }
} // namespace gui

M products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.hpp => products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.hpp +1 -0
@@ 26,6 26,7 @@ namespace gui
        bool updateTime() override;
        bool onInput(const InputEvent &inputEvent) override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        bool onDatabaseMessage(sys::Message *msg) override;

        void setAlarmTriggered() override;
        void setAlarmSnoozed() override;

M products/BellHybrid/apps/common/include/common/models/AbstractAlarmModel.hpp => products/BellHybrid/apps/common/include/common/models/AbstractAlarmModel.hpp +2 -0
@@ 16,6 16,8 @@ namespace app
        virtual void setAlarmTime(time_t time) = 0;
        virtual time_t getAlarmTime() const    = 0;
        virtual void activate(bool value)      = 0;
        /// Command model to update its internal data
        virtual void update() = 0;
    };

} // namespace app

M products/BellHybrid/apps/common/include/common/models/AlarmModel.hpp => products/BellHybrid/apps/common/include/common/models/AlarmModel.hpp +9 -1
@@ 26,12 26,20 @@ namespace app
        void setAlarmTime(time_t time) override;
        time_t getAlarmTime() const override;
        void activate(bool value) override;
        void update() override;

      private:
        void requestAlarm();
        enum class State
        {
            Invalid,
            InitInProgress,
            Valid
        };

        void updateAlarm(const AlarmEventRecord &alarm);
        Application *app{};
        AlarmEventRecord cachedRecord;
        State state{State::Invalid};

        std::function<bool(sys::ResponseMessage *)> responseCallback;
    };

M products/BellHybrid/apps/common/src/AlarmModel.cpp => products/BellHybrid/apps/common/src/AlarmModel.cpp +18 -5
@@ 17,14 17,27 @@ namespace app
        responseCallback = [this](const auto response) -> bool {
            const auto resp = dynamic_cast<alarms::AlarmGetResponseMessage *>(response);
            if (resp) {
                cachedRecord = resp->alarm;
                if (resp->alarm.isValid()) {
                    cachedRecord = resp->alarm;
                    state        = State::Valid;
                }
                else if (state == State::InitInProgress) {
                    /// We received invalid response from DB during model initialization. It means alarm record does not
                    /// exist, hence we need to create one.
                    auto request = AsyncRequest::createFromMessage(
                        std::make_unique<alarms::AlarmAddRequestMessage>(cachedRecord), service::name::service_time);
                    request->execute(this->app, this, responseCallback);

                    state = State::Valid;
                    this->update();
                }
            }
            return true;
        };

        requestAlarm();
        state = State::InitInProgress;
        update();
    }
    void AlarmModel::requestAlarm()
    void AlarmModel::update()
    {
        auto request = AsyncRequest::createFromMessage(std::make_unique<alarms::AlarmGetRequestMessage>(),
                                                       service::name::service_time);


@@ 51,7 64,7 @@ namespace app
                                                       service::name::service_time);
        request->execute(app, this, responseCallback);

        requestAlarm();
        cachedRecord = alarm;
    }
    bool AlarmModel::isActive() const
    {