// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #pragma once #define BOOST_SML_CFG_DISABLE_MIN_SIZE // GCC10 fix #include #include #include "command/event/Events.hpp" #include "service-bluetooth/SettingsHolder.hpp" #include "interface/profiles/ProfileManager.hpp" #include "WorkerController.hpp" #include "Device.hpp" #include #include namespace bluetooth { namespace sml = boost::sml; 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 InitDriver { void operator()(std::shared_ptr driver) { if (driver == nullptr) { throw std::runtime_error("shouldn't happen"); } // printf("driver: 0x%X %d\n", driver.get(), int(driver.use_count())); if (const auto status = driver->init(); status != Result::Code::Success) { throw InitializationError{"Unable to initialize a bluetooth driver."}; } } } constexpr InitDriver; struct InitDevicesList { void operator()(const std::shared_ptr settings, const std::shared_ptr> pairedDevices) { auto bondedDevicesStr = std::visit(bluetooth::StringVisitor(), settings->getValue(bluetooth::Settings::BondedDevices)); pairedDevices->clear(); auto devices = SettingsSerializer::fromString(bondedDevicesStr); pairedDevices->assign(devices.begin(), devices.end()); LOG_INFO("Loaded: %d devices", int(pairedDevices->size())); } } constexpr InitDevicesList; struct IsInit { bool operator()(InitializationState &data) { return data.isInitDone; }; } constexpr isInit; struct PostInit { void operator()(DeviceRegistrationFunction registerDevice, InitializationState &data) { if (const auto status = registerDevice(); status != Result::Code::Success) { throw InitializationError{"Unable to initialize bluetooth"}; } data.isInitDone = true; } } constexpr PostInit; struct StartDriver { void operator()(std::shared_ptr driver) { if (const auto status = driver->run(); status != Result::Code::Success) { throw InitializationError{"Unable to run the bluetooth driver"}; } } } static StartDriver; struct HandleOn { void operator()(std::shared_ptr handler) { handler->scan(); } } constexpr HandleOn; struct HandleOff { void operator()(std::shared_ptr handler) { handler->stopScan(); } } constexpr HandleOff; struct HandlePair { void operator()(std::shared_ptr handler, const bluetooth::event::Pair &event) { handler->pair(event.device); } } constexpr HandlePair; struct Connected { bool operator()(std::shared_ptr settings, bluetooth::event::Unpair evt) { auto deviceAddr = std::visit(bluetooth::StringVisitor(), settings->getValue(bluetooth::Settings::ConnectedDevice)); return static_cast(deviceAddr == bd_addr_to_str(evt.device.address)); } } constexpr Connected; struct HandleUnpair { void operator()(std::shared_ptr handler, const bluetooth::event::Unpair &event) { handler->unpair(event.device); } } constexpr HandleUnpair; struct HandleDrop { void operator()(const bluetooth::event::Unpair &event, std::shared_ptr settings, std::shared_ptr> pairedDevices) { auto position = std::find_if(pairedDevices->begin(), pairedDevices->end(), [&](const Devicei &device) { return !bd_addr_cmp(event.device.address, device.address); }); if (position != pairedDevices->end()) { pairedDevices->erase(position); settings->setValue(bluetooth::Settings::BondedDevices, SettingsSerializer::toString(*pairedDevices)); LOG_INFO("Device removed from paired devices list"); } } } constexpr HandleDrop; struct HandleConnect { void operator()(std::shared_ptr handler, bluetooth::event::Connect evt) { handler->connect(evt.device); } } constexpr HandleConnect; struct HandleDisconnect { void operator()(std::shared_ptr handler) { handler->disconnect(); } } constexpr HandleDisconnect; struct HandleSetVisibility { void operator()(std::shared_ptr handler) { handler->setVisibility(true); } } constexpr HandleSetVisibility; struct HandleUnsetVisibility { void operator()(std::shared_ptr handler) { handler->setVisibility(false); } } constexpr HandleUnsetVisibility; struct IncomingCallStarted { void operator()(std::shared_ptr profileManager) { profileManager->incomingCallStarted(); } } constexpr IncomingCallStarted; struct IncomingCallNumber { void operator()(std::shared_ptr profileManager, bluetooth::event::IncomingCallNumber evt) { profileManager->setIncomingCallNumber(evt.number); } } constexpr IncomingCallNumber; struct OutgoingCallStarted { void operator()(std::shared_ptr profileManager, bluetooth::event::OutgoingCallStarted evt) { profileManager->outgoingCallStarted(evt.number); } } constexpr OutgoingCallStarted; struct IncomingCallAnswered { void operator()(std::shared_ptr profileManager) { profileManager->incomingCallAnswered(); } } constexpr IncomingCallAnswered; struct OutgoingCallAnswered { void operator()(std::shared_ptr profileManager) { profileManager->outgoingCallAnswered(); } } constexpr OutgoingCallAnswered; struct CallTerminated { void operator()(std::shared_ptr profileManager) { profileManager->callTerminated(); } } constexpr CallTerminated; struct CallMissed { void operator()(std::shared_ptr profileManager) { profileManager->callMissed(); } } constexpr CallMissed; struct SignalStrength { void operator()(std::shared_ptr profileManager, bluetooth::event::SignalStrengthData evt) { profileManager->setSignalStrengthData(evt.strength); } } constexpr SignalStrength; struct SetOperatorName { void operator()(std::shared_ptr profileManager, bluetooth::event::OperatorNameData evt) { profileManager->setOperatorNameData(bluetooth::OperatorName(evt.name)); } } constexpr SetOperatorName; struct SetBatteryLevel { void operator()(std::shared_ptr profileManager, bluetooth::event::BatteryLevelData evt) { profileManager->setBatteryLevelData(evt.level); } } constexpr SetBatteryLevel; struct SetNetworkStatus { void operator()(std::shared_ptr profileManager, bluetooth::event::NetworkStatusData evt) { profileManager->setNetworkStatusData(evt.status); } } constexpr SetNetworkStatus; struct StartAudio { void operator()(std::shared_ptr profileManager) { profileManager->start(); } } constexpr StartAudio; struct StopAudio { void operator()(std::shared_ptr profileManager) { profileManager->stop(); } } constexpr StopAudio; struct Setup { public: auto operator()() const { using namespace sml; return make_transition_table(*"Setup"_s + on_entry<_>[!isInit] / (InitDevicesList, InitDriver, PostInit), "Setup"_s / StartDriver = X); } }; struct Call { auto operator()() const { using namespace sml; // clang-format off return make_transition_table( *"CallSetup"_s + sml::event / IncomingCallStarted = "CallRinging"_s, "CallSetup"_s + sml::event / (IncomingCallStarted, IncomingCallNumber) = "CallRinging"_s, "CallSetup"_s + sml::event / OutgoingCallStarted = "CallInitiated"_s, "CallRinging"_s + sml::event / IncomingCallNumber = "CallRinging"_s, "CallRinging"_s + sml::event / IncomingCallAnswered = "CallInProgress"_s, "CallRinging"_s + sml::event / CallTerminated = "CallEnded"_s, "CallRinging"_s + sml::event / CallMissed = "CallEnded"_s, "CallInitiated"_s + sml::event / OutgoingCallAnswered = "CallInProgress"_s, "CallInitiated"_s + sml::event / CallTerminated = "CallEnded"_s, "CallInProgress"_s + sml::event / CallTerminated = "CallEnded"_s, "CallEnded"_s = X ); // clang-format on } }; class Idle; struct On { auto operator()() const { const auto forwardEvent = [](const auto &ev, auto &sm, auto &deps, auto &subs) { sm.process_event(ev, deps, subs); }; using namespace sml; // clang-format off return make_transition_table( *state + sml::event / HandleOn = state, state + sml::event / HandleOff = state, state + sml::event / HandlePair = state, state + sml::event[Connected] / (HandleDisconnect, HandleUnpair, HandleDrop) = state, state + sml::event[not Connected] / (HandleUnpair, HandleDrop) = state, state + sml::event / HandleSetVisibility = state, state + sml::event / HandleUnsetVisibility = state, state + sml::event / HandleConnect = state, state + sml::event / HandleDisconnect = state, state + sml::event / SignalStrength = state, state + sml::event/ SetOperatorName = state, state + sml::event/ SetBatteryLevel = state, state + sml::event / SetNetworkStatus = state, state + sml::event/ StartAudio = state, state + sml::event/ StopAudio = state, state + sml::event / forwardEvent = state, state + sml::event / forwardEvent = state, state + sml::event / forwardEvent = state, state = state // this one is needed to go out from Call substate properly! ); // clang-format on } }; struct ExceptionHandler { void operator()(const std::runtime_error &err) { LOG_FATAL("EXCEPTION %s", err.what()); } } constexpr ExceptionHandler; struct TurnOff { void operator()(std::shared_ptr driver, std::shared_ptr profileManager) { profileManager->deInit(); driver->stop(); }; } constexpr TurnOff; class StateMachine { public: auto operator()() const { 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 + sml::event = state, state[isInit] = state, state[not isInit] = "Restart"_s, state + exception / printInitError = "Off"_s, state + sml::event / TurnOff = "Off"_s, state + exception / ( printProcessingError, TurnOff ) = "Restart"_s, state + sml::event / TurnOff = X, "Restart"_s = state, "Restart"_s + sml::event /TurnOff = X, *("ExceptionsHandling"_s) + exception / ExceptionHandler = "Off"_s, "ExceptionsHandling"_s + exception / ExceptionHandler = "Off"_s, "Off"_s + sml::event = X); // clang-format on } }; class StatefulController::Impl { public: Impl() = delete; Impl(std::shared_ptr driver, std::shared_ptr handler, DeviceRegistrationFunction registerDevice, std::shared_ptr settings, std::shared_ptr> pairedDevices, std::shared_ptr profileManager); using SM = sml::sm>; SM sm; }; StatefulController::Impl::Impl(std::shared_ptr driver, std::shared_ptr handler, DeviceRegistrationFunction registerDevice, std::shared_ptr settings, std::shared_ptr> pairedDevices, std::shared_ptr profileManager) : sm{Logger{}, driver, handler, registerDevice, InitializationState{}, settings, pairedDevices, profileManager} {} } // namespace bluetooth