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
{