// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "WorkerController.hpp" #include "Device.hpp" #include "interface/profiles/ProfileManager.hpp" #include #define BOOST_SML_CFG_DISABLE_MIN_SIZE // GCC10 fix #include #include #include namespace bluetooth { namespace sml = boost::sml; namespace { struct TurnOn {}; struct TurnOff {}; struct ShutDown {}; struct ProcessCommand { Command command; }; class InitializationError : public std::runtime_error { public: using std::runtime_error::runtime_error; }; class ProcessingError : public std::runtime_error { public: using std::runtime_error::runtime_error; }; struct InitializationState { bool isInitDone = false; }; struct Setup { public: auto operator()() const { auto isInit = [](InitializationState &data) { return data.isInitDone; }; auto init = [](std::shared_ptr &driver) { if (const auto status = driver->init(); status != Error::Success) { throw InitializationError{"Unable to initialize a bluetooth driver."}; } }; auto setup = [](DeviceRegistrationFunction ®isterDevice, InitializationState &data) { if (const auto status = registerDevice(); status != Error::Success) { throw InitializationError{"Unable to initialize bluetooth"}; } data.isInitDone = true; }; auto startDriver = [](std::shared_ptr &driver) { if (const auto status = driver->run(); status != Error::Success) { throw InitializationError{"Unable to run the bluetooth driver"}; } }; using namespace sml; // clang-format off return make_transition_table(*"Setup"_s / startDriver = "StartingDriver"_s, "Setup"_s + on_entry<_> [ !isInit ] / ( init, setup ), "StartingDriver"_s = X); // clang-format on } }; struct On { auto operator()() const { auto isInit = [](InitializationState &data) { return data.isInitDone; }; auto handleCommand = [](std::shared_ptr &processor, const ProcessCommand &processCommand) { if (const auto status = processor->handle(processCommand.command); status != Error::Success) { throw ProcessingError{"Failed to process command"}; } }; using namespace sml; // clang-format off return make_transition_table(*"Idle"_s + event [ isInit ] / handleCommand = "Processing"_s, "Processing"_s = "Idle"_s); // clang-format on } }; class StateMachine { public: auto operator()() const { auto turnOff = [](std::shared_ptr &driver) { driver->stop(); }; auto printInitError = [](const InitializationError &error) { LOG_ERROR("%s", error.what()); }; auto printProcessingError = [](const ProcessingError &error) { LOG_ERROR("%s", error.what()); }; using namespace sml; // clang-format off return make_transition_table(*"Off"_s + event = state, state = state, state + exception / printInitError = "Off"_s, state + event / turnOff = "Off"_s, state + exception / ( printProcessingError, turnOff ) = "Restart"_s, "Restart"_s = state, "Off"_s + event = X); // clang-format on } }; } // namespace class StatefulController::Impl { public: Impl(std::shared_ptr &&driver, std::shared_ptr &&handler, DeviceRegistrationFunction &®isterDevice); using SM = sml::sm; SM sm; }; StatefulController::Impl::Impl(std::shared_ptr &&driver, std::shared_ptr &&handler, DeviceRegistrationFunction &®isterDevice) : sm{std::move(driver), std::move(handler), std::move(registerDevice), InitializationState{}} {} StatefulController::StatefulController(std::shared_ptr &&driver, std::shared_ptr &&handler, DeviceRegistrationFunction &®isterDevice) : pimpl(std::make_unique(std::move(driver), std::move(handler), std::move(registerDevice))) {} StatefulController::~StatefulController() noexcept = default; void StatefulController::turnOn() { pimpl->sm.process_event(TurnOn{}); } void StatefulController::turnOff() { pimpl->sm.process_event(TurnOff{}); } void StatefulController::shutdown() { if (isOn()) { turnOff(); } pimpl->sm.process_event(ShutDown{}); } auto StatefulController::isOn() const -> bool { using namespace sml; return !pimpl->sm.is("Off"_s) && !isTerminated(); } auto StatefulController::isTerminated() const -> bool { using namespace sml; return pimpl->sm.is(X); } void StatefulController::processCommand(Command command) { LOG_INFO("Process command: %s", magic_enum::enum_name(command.getType()).data()); pimpl->sm.process_event(ProcessCommand{command}); } } // namespace bluetooth