~aleteoryx/muditaos

a5f621bc834dcfc15c776d4dd33ad86464ee2f64 — Lefucjusz 3 years ago 2664e8c
[MOS-487] Added call events state machine

Added tests and calls substate
bluetooth sm diagram created
All old bluetooth tests reviewed and updated
removed new - with queue it's not required
removed TODO
Co-authored-by: Bartosz Cichocki <sp2fet@gmail.com>
M module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.cpp => module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.cpp +0 -1
@@ 48,7 48,6 @@ void BluetoothSettingsModel::requestBondedDevices()

void BluetoothSettingsModel::requestScan()
{
    /// TODO send event Scan{}
    service->bus.sendUnicast(std::make_shared<BluetoothMessage>(BluetoothMessage::Request::Scan),
                             service::name::bluetooth);
}

M module-bluetooth/Bluetooth/BluetoothStateMachine.hpp => module-bluetooth/Bluetooth/BluetoothStateMachine.hpp +70 -41
@@ 73,7 73,6 @@ namespace bluetooth

    struct PostInit
    {
        // TODO shoundn't this be in driver?
        void operator()(DeviceRegistrationFunction registerDevice, InitializationState &data)
        {
            if (const auto status = registerDevice(); status != Error::Success) {


@@ 93,17 92,6 @@ namespace bluetooth
        }
    } static StartDriver;

    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 HandleOn
    {
        void operator()(std::shared_ptr<AbstractCommandHandler> handler)


@@ 140,9 128,9 @@ namespace bluetooth

    struct HandleUnpair
    {
        void operator()(std::shared_ptr<AbstractDriver> driver, const bt::evt::Unpair &event)
        void operator()(std::shared_ptr<AbstractCommandHandler> handler, const bt::evt::Unpair &event)
        {
            driver->unpair(event.device);
            handler->unpair(event.device);
        }
    } constexpr HandleUnpair;



@@ 303,39 291,79 @@ namespace bluetooth
        }
    } constexpr StopAudio;

    struct On
    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
        {
            auto isInit = [](InitializationState &data) { return data.isInitDone; };
            using namespace sml;
            // clang-format off
            return make_transition_table(
                *"CallSetup"_s + event<bt::evt::StartRinging> / StartRinging = "CallRinging"_s,
                "CallSetup"_s + event<bt::evt::StartRouting> / InitializeCall = "CallInitiated"_s,
                "CallSetup"_s + event<bt::evt::CallStarted> / CallStarted = "CallInitiated"_s,
                "CallSetup"_s + event<bt::evt::IncomingCallNumber> / (StartRinging, IncomingCall)  = "CallRinging"_s,

                "CallRinging"_s + event<bt::evt::StopRinging> / StopRinging = "CallDropped"_s,
                "CallRinging"_s + event<bt::evt::CallAnswered> / CallAnswered = "CallInProgress"_s,
                "CallRinging"_s + event<bt::evt::CallTerminated> / TerminateCall = "CallDropped"_s,

                "CallInitiated"_s + event<bt::evt::CallAnswered> / CallAnswered = "CallInProgress"_s,
               "CallInitiated"_s + event<bt::evt::StopRinging> / StopRinging = "CallDropped"_s,
               "CallInitiated"_s + event<bt::evt::CallTerminated> / TerminateCall = "CallDropped"_s,
                "CallInitiated"_s + event<bt::evt::IncomingCallNumber> / IncomingCall= "CallInitiated"_s,

                "CallInProgress"_s + event<bt::evt::CallTerminated> / TerminateCall = "CallDropped"_s,

                "CallDropped"_s = X

            );
        }
    };

    class Idle;
    struct On
    {
        auto operator()() 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(
                       *"Idle"_s + event<bt::evt::StartScan>[isInit] / HandleOn = "Idle"_s,
                       "Idle"_s + event<bt::evt::StopScan>[isInit] / HandleOff = "Idle"_s,
                       "Idle"_s + event<bt::evt::Pair>[isInit] / HandlePair = "Idle"_s,
                       "Idle"_s + event<bt::evt::Unpair>[isInit and Connected] / (HandleDisconnect, HandleUnpair) = "Idle"_s,
                       "Idle"_s + event<bt::evt::Unpair>[isInit] / (HandleUnpair, HandleDrop) = "Idle"_s,

                       "Idle"_s + event<bt::evt::VisibilityOn> / HandleSetVisibility = "Idle"_s,
                       "Idle"_s + event<bt::evt::VisibilityOff> / HandleUnsetVisibility = "Idle"_s,
                       "Idle"_s + event<bt::evt::ConnectAudio> / EstablishAudioConnection = "Idle"_s,

                       "Idle"_s + event<bt::evt::StartRinging> / StartRinging = "Idle"_s,
                       "Idle"_s + event<bt::evt::StopRinging> / StopRinging = "Idle"_s,
                       "Idle"_s + event<bt::evt::StartRouting> /InitializeCall = "Idle"_s,
                       "Idle"_s + event<bt::evt::CallAnswered> / CallAnswered = "Idle"_s,
                       "Idle"_s + event<bt::evt::CallTerminated> / TerminateCall = "Idle"_s,
                       "Idle"_s + event<bt::evt::CallStarted>/ CallStarted = "Idle"_s,
                       "Idle"_s + event<bt::evt::IncomingCallNumber> / IncomingCall= "Idle"_s,
                       "Idle"_s + event<bt::evt::SignalStrengthData> / SignalStrength = "Idle"_s,
                       "Idle"_s + event<bt::evt::OperatorNameData>/  SetOperatorName = "Idle"_s,
                       "Idle"_s + event<bt::evt::BatteryLevelData>/ SetBatteryLevel = "Idle"_s,
                       "Idle"_s + event<bt::evt::NetworkStatusData> / SetNetworkStatus = "Idle"_s,
                       "Idle"_s + event<bt::evt::StartStream>/ StartAudio = "Idle"_s,
                       "Idle"_s + event<bt::evt::StopStream>/ StopAudio = "Idle"_s
                       );
                        *state<Idle> + event<bt::evt::StartScan> / HandleOn = state<Idle>,
                        state<Idle> + event<bt::evt::StopScan> / HandleOff = state<Idle>,
                        state<Idle> + event<bt::evt::Pair> / HandlePair = state<Idle>,
                        state<Idle> + event<bt::evt::Unpair>[Connected] / (HandleDisconnect, HandleUnpair) = state<Idle>,
                        state<Idle> + event<bt::evt::Unpair>[not Connected] / (HandleUnpair, HandleDrop) = state<Idle>,

                        state<Idle> + event<bt::evt::VisibilityOn> / HandleSetVisibility = state<Idle>,
                        state<Idle> + event<bt::evt::VisibilityOff> / HandleUnsetVisibility = state<Idle>,
                        state<Idle> + event<bt::evt::ConnectAudio> / EstablishAudioConnection = state<Idle>,

                        state<Idle> + event<bt::evt::SignalStrengthData> / SignalStrength = state<Idle>,
                        state<Idle> + event<bt::evt::OperatorNameData>/  SetOperatorName = state<Idle>,
                        state<Idle> + event<bt::evt::BatteryLevelData>/ SetBatteryLevel = state<Idle>,
                        state<Idle> + event<bt::evt::NetworkStatusData> / SetNetworkStatus = state<Idle>,
                        state<Idle> + event<bt::evt::StartStream>/ StartAudio = state<Idle>,
                        state<Idle> + event<bt::evt::StopStream>/ StopAudio = state<Idle>,

                        state<Idle> + event<bt::evt::StartRouting> / forwardEvent= state<Call>,

                        state<Idle> + event<bt::evt::IncomingCallNumber>  / forwardEvent  = state<Call>,
                        state<Idle> + event<bt::evt::CallStarted> / forwardEvent= state<Call>,
                        state<Call> = state<Idle> // this one is needed to go out from Call substate properly!

                            );
            // clang-format on
        }
    };


@@ 369,7 397,8 @@ namespace bluetooth
            using namespace sml;
            // clang-format off
                return make_transition_table(*"Off"_s + event<bt::evt::PowerOn> = state<Setup>,
                                             state<Setup> = state<On>,
                                             state<Setup>[isInit] = state<On>,
                                             state<Setup>[not isInit] = "Restart"_s,
                                             state<Setup> + exception<InitializationError> / printInitError = "Off"_s,

                                             state<On> + event<bt::evt::PowerOff> / TurnOff = "Off"_s,

M module-bluetooth/Bluetooth/BluetoothWorker.cpp => module-bluetooth/Bluetooth/BluetoothWorker.cpp +4 -5
@@ 158,14 158,13 @@ auto BluetoothWorker::handleCommand(QueueHandle_t queue) -> bool
    LOG_INFO("handle bluetooth command(s)");
    xQueueReceive(queue, nullptr, 0);
    while (not workerQueue->empty()) {
        auto cmd = workerQueue->peek();
        if (cmd == std::nullopt) {
        auto command = workerQueue->peek();
        if (command == std::nullopt) {
            LOG_ERROR("There was no data even with notification");
            break;
        }
        if ((*cmd).evt != nullptr) {
            controller->handle(*cmd->evt);
            delete cmd->evt;
        if ((*command).event != nullptr) {
            controller->handle(*command->event);
        }
    }
    return true;

M module-bluetooth/Bluetooth/WorkerController.cpp => module-bluetooth/Bluetooth/WorkerController.cpp +0 -1
@@ 67,7 67,6 @@ namespace bluetooth
    {
        pimpl->sm.process_event(evt);
    };
    // TODO split TurnOn and PowerOn?
    void StatefulController::handle(const bt::evt::PowerOn &evt)
    {
        pimpl->sm.process_event(evt);

M module-bluetooth/Bluetooth/command/Command.hpp => module-bluetooth/Bluetooth/command/Command.hpp +1 -1
@@ 10,7 10,7 @@ namespace bluetooth

    struct Command
    {
        bt::evt::Base *evt = nullptr;
        std::unique_ptr<bt::evt::Base> event;
    };

} // namespace bluetooth

A module-bluetooth/Bluetooth/doc/bluetooth_states.svg => module-bluetooth/Bluetooth/doc/bluetooth_states.svg +122 -0
@@ 0,0 1,122 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="1057px" preserveAspectRatio="none" style="width:5301px;height:1057px;background:#FFFFFF;" version="1.1" viewBox="0 0 5301 1057" width="5301px" zoomAndPan="magnify"><defs><filter height="300%" id="f1g6rh13jrsu0e" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><ellipse cx="929.61" cy="51" fill="#000000" filter="url(#f1g6rh13jrsu0e)" rx="10" ry="10" style="stroke:none;stroke-width:1.0;"/><g id="Off"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="50" x="258.61" y="419"/><line style="stroke:#A80036;stroke-width:1.5;" x1="258.61" x2="308.61" y1="448.0679" y2="448.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="21" x="273.11" y="438.9659">Off</text></g><g id="bluetooth::Setup"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="132" x="288.61" y="566"/><line style="stroke:#A80036;stroke-width:1.5;" x1="288.61" x2="420.61" y1="595.0679" y2="595.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="112" x="298.61" y="585.9659">bluetooth::Setup</text></g><g id="bluetooth"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="88.1003" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="85" x="975.11" y="7"/><line style="stroke:#A80036;stroke-width:1.5;" x1="975.11" x2="1060.11" y1="36.0679" y2="36.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="65" x="985.11" y="26.9659">bluetooth</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="43" x="980.11" y="53.896">:Setup {</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="27" x="980.11" y="70.2401">:On {</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="32" x="980.11" y="86.5842">:Call {</text></g><g id="Setup"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="120.7885" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="170" x="844.61" y="268"/><line style="stroke:#A80036;stroke-width:1.5;" x1="844.61" x2="1014.61" y1="297.0679" y2="297.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="39" x="910.11" y="287.9659">Setup</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="48" x="849.61" y="314.896">on_entry</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="105" x="852.61" y="331.2401">[!bluetooth::IsInit] /</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="147" x="852.61" y="347.5842">(bluetooth::InitDevicesList,</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="115" x="852.61" y="363.9283">bluetooth::InitDriver,</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="105" x="852.61" y="380.2725">bluetooth::PostInit)</text></g><ellipse cx="789.61" cy="1035" filter="url(#f1g6rh13jrsu0e)" rx="10" ry="10" style="stroke:#000000;stroke-width:1.0;fill:none;"/><ellipse cx="790.11" cy="1035.5" fill="#000000" rx="6" ry="6" style="stroke:none;stroke-width:1.0;"/><g id="bluetooth::On"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="113" x="242.11" y="713"/><line style="stroke:#A80036;stroke-width:1.5;" x1="242.11" x2="355.11" y1="742.0679" y2="742.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="93" x="252.11" y="732.9659">bluetooth::On</text></g><g id="bluetooth::Idle"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="119" x="2675.11" y="172"/><line style="stroke:#A80036;stroke-width:1.5;" x1="2675.11" x2="2794.11" y1="201.0679" y2="201.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="99" x="2685.11" y="191.9659">bluetooth::Idle</text></g><g id="bluetooth::Call"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="118" x="2969.61" y="419"/><line style="stroke:#A80036;stroke-width:1.5;" x1="2969.61" x2="3087.61" y1="448.0679" y2="448.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="98" x="2979.61" y="438.9659">bluetooth::Call</text></g><g id="CallSetup"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="84" x="1223.61" y="172"/><line style="stroke:#A80036;stroke-width:1.5;" x1="1223.61" x2="1307.61" y1="201.0679" y2="201.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="64" x="1233.61" y="191.9659">CallSetup</text></g><g id="CallRinging"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="98" x="1583.61" y="419"/><line style="stroke:#A80036;stroke-width:1.5;" x1="1583.61" x2="1681.61" y1="448.0679" y2="448.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="78" x="1593.61" y="438.9659">CallRinging</text></g><g id="CallInitiated"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="102" x="1214.61" y="419"/><line style="stroke:#A80036;stroke-width:1.5;" x1="1214.61" x2="1316.61" y1="448.0679" y2="448.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="82" x="1224.61" y="438.9659">CallInitiated</text></g><g id="CallDropped"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="104" x="1309.61" y="713"/><line style="stroke:#A80036;stroke-width:1.5;" x1="1309.61" x2="1413.61" y1="742.0679" y2="742.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="84" x="1319.61" y="732.9659">CallDropped</text></g><g id="CallInProgress"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="118" x="1473.61" y="566"/><line style="stroke:#A80036;stroke-width:1.5;" x1="1473.61" x2="1591.61" y1="595.0679" y2="595.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="98" x="1483.61" y="585.9659">CallInProgress</text></g><g id="Restart"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="68" x="769.61" y="878"/><line style="stroke:#A80036;stroke-width:1.5;" x1="769.61" x2="837.61" y1="907.0679" y2="907.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="48" x="779.61" y="897.9659">Restart</text></g><g id="ExceptionsHandling"><rect fill="#FEFECE" filter="url(#f1g6rh13jrsu0e)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="154" x="482.61" y="172"/><line style="stroke:#A80036;stroke-width:1.5;" x1="482.61" x2="636.61" y1="201.0679" y2="201.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="134" x="492.61" y="191.9659">ExceptionsHandling</text></g><!--MD5=[79cb27a3a0f2a433e2384f6da7fb5477]
link *start to Off--><path d="M919.67,52.77 C869.13,57 636.32,79.97 464.61,156 C385.58,191 350.14,193.91 305.61,268 C278.4,313.28 277.88,377 280.43,413.75 " fill="none" id="*start-to-Off" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="280.82,418.97,284.1139,409.6883,280.4343,413.9849,276.1377,410.3053,280.82,418.97" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[314e95ddc488a39884b195caa7bb14cb]
link Off to bluetooth::Setup--><path d="M258.38,455.8 C241.19,464.66 219.77,479.01 209.61,499 C202.16,513.66 200.23,522.49 209.61,536 C226.42,560.2 255.44,573.57 283.12,580.96 " fill="none" id="Off-to-bluetooth::Setup" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="288.27,582.27,280.5299,576.1797,283.4235,581.0405,278.5627,583.9341,288.27,582.27" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="100" x="210.61" y="522.897">bt::evt::PowerOn</text><!--MD5=[02550b1d1d6904a10fbe2329ae522ac1]
link bluetooth::Setup to Off--><path d="M345.19,565.66 C337.76,547.1 326.82,521.07 315.61,499 C311.33,490.56 306.26,481.62 301.44,473.5 " fill="none" id="bluetooth::Setup-to-Off" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="298.8,469.09,299.9622,478.87,301.3557,473.3875,306.8382,474.781,298.8,469.09" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="94" x="562.61" y="513.897">internal_event /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="547" x="337.61" y="531.603">bluetooth::StateMachine::operator()() const::&lt;lambda(const bluetooth::InitializationError&amp;)&gt;</text><!--MD5=[927f07f7a187749b6c90a9e4e71dac58]
link *start to Setup--><path d="M929.61,61.17 C929.61,92.06 929.61,194.25 929.61,262.69 " fill="none" id="*start-to-Setup" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="929.61,267.97,933.61,258.97,929.61,262.97,925.61,258.97,929.61,267.97" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[144674951cef53d126f749734b3a1416]
link Setup to *end--><path d="M929.61,389.29 C929.61,426 929.61,473.97 929.61,516.5 C929.61,516.5 929.61,516.5 929.61,904 C929.61,945.61 935.81,964.4 907.61,995 C880.3,1024.65 829.99,1031.8 804.81,1033.49 " fill="none" id="Setup-to-*end" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="799.54,1033.79,808.7541,1037.2685,804.5318,1033.5039,808.2964,1029.2816,799.54,1033.79" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="5" x="995.11" y="660.897">/</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="131" x="933.61" y="678.603">bluetooth::StartDriver</text><!--MD5=[910add9d8757630db0a8da4496a1123e]
link bluetooth::Setup to bluetooth::On--><path d="M345.24,616.27 C335.51,641.47 320.28,680.89 309.9,707.78 " fill="none" id="bluetooth::Setup-to-bluetooth::On" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="308.02,712.65,314.9956,705.6973,309.8226,707.9862,307.5336,702.8132,308.02,712.65" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="0" x="389.61" y="660.897"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="104" x="337.61" y="678.603">[bluetooth::IsInit]</text><!--MD5=[b5169dfd47ce128b9422cf957a36284e]
link *start to bluetooth::Idle--><path d="M932.53,60.73 C936.46,70.94 944.51,87.41 957.61,95 C1108.04,182.17 2367.27,194.13 2669.56,195.75 " fill="none" id="*start-to-bluetooth::Idle" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2674.81,195.78,2665.833,191.7286,2669.8101,195.7514,2665.7873,199.7285,2674.81,195.78" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.14,195.5 C2813.61,195.51 2829.11,196.01 2829.11,197 C2829.11,197.9 2816.38,198.39 2799.5,198.48 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.14,198.5,2803.1514,202.4743,2799.14,198.4857,2803.1285,194.4743,2794.14,198.5" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="109" x="2845.61" y="193.397">bt::evt::StartScan /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="127" x="2838.11" y="211.103">bluetooth::HandleOn</text><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.36,194.66 C2866.85,193.28 2971.11,194.06 2971.11,197 C2971.11,199.87 2871.68,200.68 2799.51,199.43 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle-1" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.36,199.34,2803.2901,203.4937,2799.3593,199.4257,2803.4272,195.4949,2794.36,199.34" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="108" x="2988.11" y="193.397">bt::evt::StopScan /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="127" x="2980.11" y="211.103">bluetooth::HandleOff</text><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.3,194.14 C2905.06,191.24 3113.11,192.19 3113.11,197 C3113.11,201.74 2911.51,202.73 2799.57,199.99 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle-2" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.3,199.86,2803.1942,204.09,2799.2983,199.9885,2803.3998,196.0927,2794.3,199.86" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="75" x="3149.11" y="193.397">bt::evt::Pair /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="132" x="3122.11" y="211.103">bluetooth::HandlePair</text><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.27,193.74 C2937.87,189.21 3260.11,190.3 3260.11,197 C3260.11,203.62 2945.38,204.76 2799.39,200.42 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle-3" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.27,200.26,2803.1521,204.5154,2799.268,200.4028,2803.3806,196.5187,2794.27,200.26" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="85" x="3316.11" y="175.897">bt::evt::Unpair</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="147" x="3286.61" y="193.603">[bluetooth::Connected] /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="182" x="3269.11" y="211.309">(bluetooth::HandleDisconnect,</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="154" x="3283.11" y="229.0151">bluetooth::HandleUnpair)</text><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.41,193.49 C2975.29,187.18 3457.11,188.35 3457.11,197 C3457.11,205.57 2984.19,206.79 2799.52,200.68 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle-4" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.41,200.51,2803.2799,204.7907,2799.4075,200.6671,2803.5312,196.7947,2794.41,200.51" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="85" x="3500.61" y="175.897">bt::evt::Unpair</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="151" x="3469.11" y="193.603">[!bluetooth::Connected] /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="157" x="3466.11" y="211.309">(bluetooth::HandleUnpair,</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="143" x="3473.11" y="229.0151">bluetooth::HandleDrop)</text><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.24,193.24 C3004.07,185.17 3629.11,186.42 3629.11,197 C3629.11,207.49 3014.4,208.81 2799.55,200.96 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle-5" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.24,200.76,2803.0729,205.1165,2799.236,200.9598,2803.3927,197.1229,2794.24,200.76" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="119" x="3665.61" y="193.397">bt::evt::VisibilityOn /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="177" x="3638.11" y="211.103">bluetooth::HandleSetVisibility</text><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.41,193.02 C3033.79,183.16 3821.11,184.48 3821.11,197 C3821.11,209.42 3045.28,210.82 2799.76,201.19 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle-6" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.41,200.98,2803.2429,205.3365,2799.406,201.1798,2803.5627,197.3429,2794.41,200.98" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="119" x="3866.11" y="193.397">bt::evt::VisibilityOff /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="194" x="3830.11" y="211.103">bluetooth::HandleUnsetVisibility</text><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.51,192.85 C3063.26,181.15 4030.11,182.53 4030.11,197 C4030.11,211.38 3075.49,212.84 2799.71,201.38 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle-7" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.51,201.15,2803.3305,205.5317,2799.5054,201.3641,2803.673,197.539,2794.51,201.15" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="137" x="4080.61" y="193.397">bt::evt::ConnectAudio /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="223" x="4039.11" y="211.103">bluetooth::EstablishAudioConnection</text><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.49,192.71 C3093.67,179.14 4268.11,180.56 4268.11,197 C4268.11,213.34 3107.39,214.85 2799.84,201.52 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle-8" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.49,201.29,2803.3105,205.6717,2799.4854,201.5041,2803.653,197.679,2794.49,201.29" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="169" x="4274.11" y="193.397">bt::evt::SignalStrengthData /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="155" x="4282.61" y="211.103">bluetooth::SignalStrength</text><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.35,192.54 C3115.85,177.13 4449.11,178.62 4449.11,197 C4449.11,215.29 3130.13,216.85 2799.62,201.7 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle-9" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.35,201.46,2803.1579,205.8668,2799.3448,201.6883,2803.5233,197.8752,2794.35,201.46" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="171" x="4459.11" y="193.397">bt::evt::OperatorNameData /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="176" x="4458.11" y="211.103">bluetooth::SetOperatorName</text><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.18,192.4 C3137.92,175.13 4640.11,176.66 4640.11,197 C4640.11,217.24 3152.56,218.86 2799.29,201.86 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle-10" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.18,201.6,2802.9753,206.0319,2799.1741,201.8426,2803.3635,198.0413,2794.18,201.6" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="155" x="4650.11" y="193.397">bt::evt::BatteryLevelData /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="160" x="4649.11" y="211.103">bluetooth::SetBatteryLevel</text><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.31,192.23 C3158.53,173.12 4815.11,174.71 4815.11,197 C4815.11,219.19 3173.86,220.86 2799.46,202.03 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle-11" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.31,201.77,2803.092,206.2283,2799.3034,202.0275,2803.5041,198.2389,2794.31,201.77" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="170" x="4825.11" y="193.397">bt::evt::NetworkStatusData /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="175" x="4824.11" y="211.103">bluetooth::SetNetworkStatus</text><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.31,192.1 C3179.46,171.12 5005.11,172.75 5005.11,197 C5005.11,221.14 3195.47,222.87 2799.47,202.18 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle-12" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.31,201.9,2803.0927,206.357,2799.3034,202.1568,2803.5036,198.3675,2794.31,201.9" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="124" x="5015.61" y="193.397">bt::evt::StartStream /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="130" x="5014.11" y="211.103">bluetooth::StartAudio</text><!--MD5=[e670b6c53f9238347594bdfdca62cd3d]
link bluetooth::Idle to bluetooth::Idle--><path d="M2794.42,191.93 C3196.27,169.12 5150.11,170.8 5150.11,197 C5150.11,223.09 3212.45,224.87 2799.51,202.35 " fill="none" id="bluetooth::Idle-to-bluetooth::Idle-13" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.42,202.07,2803.1892,206.5533,2799.4126,202.3418,2803.6241,198.5652,2794.42,202.07" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="123" x="5160.61" y="193.397">bt::evt::StopStream /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="129" x="5159.11" y="211.103">bluetooth::StopAudio</text><!--MD5=[aa9a1460239b2db4e78e8d448c407a44]
link bluetooth::Idle to bluetooth::Call--><path d="M2674.96,198.16 C2485.61,199.28 1907.38,207.64 1851.61,268 C1815.12,307.5 1814.47,350.11 1851.61,389 C1890.27,429.47 2723.28,440.21 2964.01,442.48 " fill="none" id="bluetooth::Idle-to-bluetooth::Call" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2969.24,442.52,2960.2746,438.443,2964.2402,442.4771,2960.206,446.4427,2969.24,442.52" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="129" x="2060.61" y="324.897">bt::evt::StartRouting /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="542" x="1855.61" y="342.603">bluetooth::On::operator()() const::&lt;lambda(const auto:25&amp;, auto:26&amp;, auto:27&amp;, auto:28&amp;)&gt;</text><!--MD5=[aa9a1460239b2db4e78e8d448c407a44]
link bluetooth::Idle to bluetooth::Call--><path d="M2675.07,203.49 C2597.27,212.03 2467.53,231.45 2438.61,268 C2405.25,310.18 2402.36,349.28 2438.61,389 C2473.54,427.27 2818.97,438.75 2964.5,441.9 " fill="none" id="bluetooth::Idle-to-bluetooth::Call-1" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2969.59,442.01,2960.684,437.8048,2964.5913,441.8954,2960.5007,445.8027,2969.59,442.01" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="127" x="2648.61" y="324.897">bt::evt::StartRinging /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="542" x="2442.61" y="342.603">bluetooth::On::operator()() const::&lt;lambda(const auto:25&amp;, auto:26&amp;, auto:27&amp;, auto:28&amp;)&gt;</text><!--MD5=[aa9a1460239b2db4e78e8d448c407a44]
link bluetooth::Idle to bluetooth::Call--><path d="M2794.27,207.13 C2860.9,218.53 2962.94,239.99 2989.61,268 C3009.26,288.63 3020.75,369.24 3025.71,413.5 " fill="none" id="bluetooth::Idle-to-bluetooth::Call-2" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="3026.28,418.71,3029.2794,409.329,3025.7372,413.7395,3021.3267,410.1974,3026.28,418.71" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="180" x="3206.11" y="324.897">bt::evt::IncomingCallNumber /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="542" x="3026.61" y="342.603">bluetooth::On::operator()() const::&lt;lambda(const auto:25&amp;, auto:26&amp;, auto:27&amp;, auto:28&amp;)&gt;</text><!--MD5=[aa9a1460239b2db4e78e8d448c407a44]
link bluetooth::Idle to bluetooth::Call--><path d="M2794.39,198.41 C2977.88,200.27 3523.81,210.63 3576.61,268 C3613.03,307.57 3612.73,349.15 3576.61,389 C3544.64,424.27 3230.66,437.35 3092.88,441.41 " fill="none" id="bluetooth::Idle-to-bluetooth::Call-3" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="3087.65,441.57,3096.7606,445.3113,3092.648,441.4272,3096.5321,437.3146,3087.65,441.57" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="117" x="3818.61" y="324.897">bt::evt::CallStarted /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="542" x="3607.61" y="342.603">bluetooth::On::operator()() const::&lt;lambda(const auto:25&amp;, auto:26&amp;, auto:27&amp;, auto:28&amp;)&gt;</text><!--MD5=[d17d82d2953a3aed516e17d5adfe1257]
link bluetooth::Call to bluetooth::Idle--><path d="M3087.93,442.41 C3315.43,439.85 4121.39,428.05 4158.61,389 C4195.72,350.08 4195.7,306.95 4158.61,268 C4111.18,218.18 3072.09,202.1 2799.57,198.73 " fill="none" id="bluetooth::Call-to-bluetooth::Idle" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="2794.44,198.67,2803.3819,202.7982,2799.4395,198.7414,2803.4962,194.799,2794.44,198.67" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[e8d1cdb01dc71a5f44bd57230ffde492]
link *start to CallSetup--><path d="M933.06,60.52 C937.4,70.21 945.75,85.88 957.61,95 C1036.49,155.62 1153.3,180.68 1218.05,190.39 " fill="none" id="*start-to-CallSetup" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1223.38,191.17,1215.0595,185.9003,1218.4334,190.4409,1213.8929,193.8148,1223.38,191.17" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[8c8fbb0723596f69e831b948650e3ae1]
link CallSetup to CallRinging--><path d="M1307.73,208.78 C1339.48,219 1381.82,237.44 1407.61,268 C1443.8,310.88 1403.72,349.54 1443.61,389 C1446.11,391.47 1523.47,413.07 1578.43,428.19 " fill="none" id="CallSetup-to-CallRinging" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1583.34,429.54,1575.7112,423.311,1578.5165,428.2232,1573.6043,431.0286,1583.34,429.54" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="127" x="1453.11" y="324.897">bt::evt::StartRinging /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="141" x="1447.61" y="342.603">bluetooth::StartRinging</text><!--MD5=[8c8fbb0723596f69e831b948650e3ae1]
link CallSetup to CallRinging--><path d="M1307.87,201.42 C1387.11,208.7 1553.65,228.4 1593.61,268 C1613.9,288.11 1625.15,369.34 1629.89,413.71 " fill="none" id="CallSetup-to-CallRinging-1" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1630.43,418.93,1633.4827,409.5662,1629.9155,413.9565,1625.5251,410.3894,1630.43,418.93" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="180" x="1627.61" y="315.897">bt::evt::IncomingCallNumber /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="148" x="1645.11" y="333.603">(bluetooth::StartRinging,</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="148" x="1645.11" y="351.309">bluetooth::IncomingCall)</text><!--MD5=[6c74df229f58e4dcaae25da6ed5d3d33]
link CallSetup to CallInitiated--><path d="M1264.32,222.29 C1263.66,235.75 1262.94,252.8 1262.61,268 C1261.46,321.77 1261.12,335.24 1262.61,389 C1262.83,396.93 1263.24,405.51 1263.67,413.44 " fill="none" id="CallSetup-to-CallInitiated" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1263.98,418.82,1267.4585,409.6059,1263.6939,413.8282,1259.4716,410.0636,1263.98,418.82" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="129" x="1268.61" y="324.897">bt::evt::StartRouting /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="136" x="1266.61" y="342.603">bluetooth::InitializeCall</text><!--MD5=[6c74df229f58e4dcaae25da6ed5d3d33]
link CallSetup to CallInitiated--><path d="M1223.56,205.37 C1188.76,213.92 1141.48,231.79 1118.61,268 C1089.9,313.47 1087.83,344.9 1118.61,389 C1139,418.2 1177.01,431.66 1209.27,437.84 " fill="none" id="CallSetup-to-CallInitiated-1" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1214.4,438.77,1206.2494,433.2412,1209.4788,437.8856,1204.8344,441.115,1214.4,438.77" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="117" x="1128.11" y="324.897">bt::evt::CallStarted /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="131" x="1122.61" y="342.603">bluetooth::CallStarted</text><!--MD5=[2cf9fde54e37716b1e7732b4be1dc61f]
link CallRinging to CallDropped--><path d="M1681.82,457.08 C1764.62,479.94 1918.07,534.63 1867.61,616 C1820.25,692.38 1540.79,723.13 1418.67,733.03 " fill="none" id="CallRinging-to-CallDropped" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1413.68,733.43,1422.9711,736.6974,1418.664,733.0301,1422.3313,728.7231,1413.68,733.43" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="126" x="1887.11" y="587.397">bt::evt::StopRinging /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="140" x="1881.61" y="605.103">bluetooth::StopRinging</text><!--MD5=[2cf9fde54e37716b1e7732b4be1dc61f]
link CallRinging to CallDropped--><path d="M1663.51,469.15 C1671.99,477.58 1680.09,487.77 1684.61,499 C1710.33,562.78 1657.43,658.57 1628.61,683 C1597.59,709.3 1487.7,724.64 1418.95,731.87 " fill="none" id="CallRinging-to-CallDropped-1" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1413.66,732.42,1423.0238,735.4727,1418.6335,731.9055,1422.2006,727.5151,1413.66,732.42" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="142" x="1694.11" y="587.397">bt::evt::CallTerminated /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="148" x="1692.61" y="605.103">bluetooth::TerminateCall</text><!--MD5=[34ddcf6c3e1e05c11b1b9c0794d185da]
link CallRinging to CallInProgress--><path d="M1583.42,458.34 C1563.06,466.56 1541.47,479.39 1529.61,499 C1518.55,517.3 1520.01,541.67 1523.86,560.58 " fill="none" id="CallRinging-to-CallInProgress" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1525,565.77,1526.9734,556.1209,1523.926,560.8867,1519.1601,557.8393,1525,565.77" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="132" x="1539.11" y="513.897">bt::evt::CallAnswered /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="146" x="1533.61" y="531.603">bluetooth::CallAnswered</text><!--MD5=[eebbe44de448acf8b9365784d2503838]
link CallInitiated to CallInProgress--><path d="M1285.64,469.06 C1303.82,489.49 1332.34,518.24 1362.61,536 C1372.19,541.62 1424.43,557.9 1468.5,571.13 " fill="none" id="CallInitiated-to-CallInProgress" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1473.56,572.64,1466.0749,566.2389,1468.7677,571.2137,1463.7929,573.9065,1473.56,572.64" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="132" x="1372.11" y="513.897">bt::evt::CallAnswered /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="146" x="1366.61" y="531.603">bluetooth::CallAnswered</text><!--MD5=[b525498bdf470e0e68f4395aa42457ac]
link CallInitiated to CallDropped--><path d="M1214.52,466.86 C1151.76,496.97 1058.17,554.53 1097.61,616 C1143.03,686.78 1241.35,716.7 1304.35,728.95 " fill="none" id="CallInitiated-to-CallDropped" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1309.58,729.94,1301.4792,724.3385,1304.6669,729.0117,1299.9938,732.1994,1309.58,729.94" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="126" x="1107.11" y="587.397">bt::evt::StopRinging /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="140" x="1101.61" y="605.103">bluetooth::StopRinging</text><!--MD5=[b525498bdf470e0e68f4395aa42457ac]
link CallInitiated to CallDropped--><path d="M1268.94,469 C1274.01,502.55 1284.97,565.05 1302.61,616 C1313.94,648.71 1331.91,684.08 1345.05,708.08 " fill="none" id="CallInitiated-to-CallDropped-1" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1347.61,712.72,1346.7739,702.9067,1345.1988,708.3398,1339.7656,706.7647,1347.61,712.72" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="142" x="1308.11" y="587.397">bt::evt::CallTerminated /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="148" x="1306.61" y="605.103">bluetooth::TerminateCall</text><!--MD5=[c670cd63c66a440aa8816f8a901db355]
link CallInitiated to CallInitiated--><path d="M1316.82,430.53 C1335.91,430.09 1351.61,434.58 1351.61,444 C1351.61,452.54 1338.71,457.03 1322.09,457.46 " fill="none" id="CallInitiated-to-CallInitiated" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1316.82,457.47,1325.82,461.47,1321.82,457.47,1325.82,453.47,1316.82,457.47" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="180" x="1357.61" y="440.397">bt::evt::IncomingCallNumber /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="144" x="1377.11" y="458.103">bluetooth::IncomingCall</text><!--MD5=[e5be4361d58891989e9bf4b9d8fa8f4d]
link CallInProgress to CallDropped--><path d="M1503.99,616.27 C1473.69,641.96 1425.95,682.45 1394.22,709.35 " fill="none" id="CallInProgress-to-CallDropped" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="1390.04,712.89,1399.4938,710.1285,1393.8564,709.6596,1394.3252,704.0223,1390.04,712.89" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="142" x="1473.11" y="660.897">bt::evt::CallTerminated /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="148" x="1471.61" y="678.603">bluetooth::TerminateCall</text><!--MD5=[48489f0be2ce80eb8bbe6138c2467942]
link CallDropped to *end--><path d="M1331.48,763.03 C1266.64,813.57 1107.41,931.42 955.61,995 C902.66,1017.18 834.67,1028.14 804.82,1032.15 " fill="none" id="CallDropped-to-*end" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="799.7,1032.81,809.1397,1035.6193,804.6585,1032.167,808.1108,1027.6858,799.7,1032.81" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[899b5b126b684b60d9937cf3105663d0]
link bluetooth::Setup to Restart--><path d="M403.47,616.07 C473.08,650.1 605.07,713.62 719.61,763 C752.96,777.38 774.1,763.74 795.61,793 C812.33,815.74 812.32,848.85 809.38,872.61 " fill="none" id="bluetooth::Setup-to-Restart" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="808.67,877.83,813.8418,869.4483,809.3411,872.8752,805.9142,868.3746,808.67,877.83" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="0" x="777.61" y="734.397"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="108" x="723.61" y="752.103">[!bluetooth::IsInit]</text><!--MD5=[f3e6401e50ee2df1da851e91ae759d9b]
link Restart to bluetooth::Setup--><path d="M818.39,877.98 C840.22,839.07 874.62,761.72 836.61,713 C786.44,648.67 547.32,613.35 425.83,599.29 " fill="none" id="Restart-to-bluetooth::Setup" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="420.84,598.71,429.3227,603.7143,425.8071,599.2826,430.2389,595.767,420.84,598.71" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[2d008fa89a59a9e0842c2ee994f890ff]
link bluetooth::On to Off--><path d="M244.1,712.99 C157.82,671.68 6,583.42 71.61,499 C93.8,470.46 200.21,454.5 253.32,448.2 " fill="none" id="bluetooth::On-to-Off" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="258.29,447.62,248.8838,444.7005,253.3243,448.205,249.8198,452.6456,258.29,447.62" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="108" x="99.11" y="587.397">bt::evt::PowerOff /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="112" x="98.61" y="605.103">bluetooth::TurnOff</text><!--MD5=[63092485ce6a3ef55ef86eba886f472c]
link bluetooth::On to Restart--><path d="M263.62,763.07 C254.38,771.38 245.61,781.52 240.61,793 C230.86,815.41 224.17,829.91 240.61,848 C275.75,886.67 641.61,898.36 763.99,901.21 " fill="none" id="bluetooth::On-to-Restart" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="769.29,901.33,760.3838,897.1254,764.2913,901.2157,760.2009,905.1233,769.29,901.33" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="94" x="469.11" y="807.897">internal_event /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="546" x="244.61" y="825.603">(bluetooth::StateMachine::operator()() const::&lt;lambda(const bluetooth::ProcessingError&amp;)&gt;,</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="116" x="459.61" y="843.309">bluetooth::TurnOff)</text><!--MD5=[266a3cac834d88eb95e29c2fd0e04373]
link bluetooth::On to *end--><path d="M250.86,763.13 C239.95,771.07 229.76,781 223.61,793 C212.47,814.76 209.28,828.2 223.61,848 C293.21,944.12 692.78,1017.36 774.87,1031.52 " fill="none" id="bluetooth::On-to-*end" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="779.81,1032.36,771.6131,1026.9001,774.8815,1031.5172,770.2645,1034.7857,779.81,1032.36" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="115" x="357.61" y="899.397">bt::evt::ShutDown /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="112" x="360.61" y="917.103">bluetooth::TurnOff</text><!--MD5=[1358184afceacdc65b62430905842065]
link Restart to *end--><path d="M794.3,928.09 C791.2,937.3 788.15,947.99 786.61,958 C784.12,974.25 785.98,978.57 786.61,995 C786.93,1003.22 787.62,1012.39 788.26,1019.77 " fill="none" id="Restart-to-*end" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="788.73,1025.03,791.9157,1015.7106,788.2864,1020.0497,783.9472,1016.4204,788.73,1025.03" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="115" x="787.61" y="972.897">bt::evt::ShutDown /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="112" x="790.61" y="990.603">bluetooth::TurnOff</text><!--MD5=[5376f9f495d2db7dc2fd7c9e57fb9170]
link *start to ExceptionsHandling--><path d="M920.47,55.56 C880.51,71.11 719.11,133.92 626.27,170.06 " fill="none" id="*start-to-ExceptionsHandling" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="621.35,171.97,631.1881,172.4302,626.0089,170.155,628.2841,164.9759,621.35,171.97" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[cbe7c9d7a51ace67092b018b465afc00]
link ExceptionsHandling to Off--><path d="M482.37,204.38 C434.97,212.22 376.34,229.6 338.61,268 C299.29,308.02 319.93,335.63 302.61,389 C299.97,397.14 296.96,405.92 294.12,413.98 " fill="none" id="ExceptionsHandling-to-Off" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="292.38,418.89,299.1383,411.7258,294.0381,414.1729,291.591,409.0728,292.38,418.89" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="94" x="380.61" y="324.897">internal_event /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="173" x="342.61" y="342.603">bluetooth::ExceptionHandler</text><!--MD5=[cbe7c9d7a51ace67092b018b465afc00]
link ExceptionsHandling to Off--><path d="M562.28,222.28 C565.27,262.69 564.86,343.75 520.61,389 C492.05,418.22 371.08,434.13 313.85,440.15 " fill="none" id="ExceptionsHandling-to-Off-1" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="308.85,440.67,318.2138,443.7227,313.8235,440.1555,317.3906,435.7651,308.85,440.67" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="94" x="605.61" y="324.897">internal_event /</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="173" x="567.61" y="342.603">bluetooth::ExceptionHandler</text><!--MD5=[98082356dd0c978256ddd0df15d48b0c]
link Off to *end--><path d="M258.53,451.7 C207.76,466.95 97.61,509.34 97.61,590 C97.61,590 97.61,590 97.61,904 C97.61,975.31 672.52,1024.63 774.31,1032.8 " fill="none" id="Off-to-*end" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="779.56,1033.22,770.9087,1028.5131,774.576,1032.8201,770.2689,1036.4874,779.56,1033.22" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="107" x="98.61" y="743.397">bt::evt::ShutDown</text><!--MD5=[9b110ebaad31f60d13af94aaff1cdba1]
@startuml

[*] - -> Off
Off - -> bluetooth::Setup : bt::evt::PowerOn
state bluetooth::Setup {
  [*] - -> Setup
  Setup : on_entry\n [!bluetooth::IsInit] /\n (bluetooth::InitDevicesList,\n bluetooth::InitDriver,\n bluetooth::PostInit)
  Setup - -> [*] : /\n bluetooth::StartDriver
}
bluetooth::Setup - -> bluetooth::On :\n [bluetooth::IsInit]
state bluetooth::On {
  [*] - -> bluetooth::Idle
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::StartScan /\n bluetooth::HandleOn
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::StopScan /\n bluetooth::HandleOff
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::Pair /\n bluetooth::HandlePair
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::Unpair\n [bluetooth::Connected] /\n (bluetooth::HandleDisconnect,\n bluetooth::HandleUnpair)
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::Unpair\n [!bluetooth::Connected] /\n (bluetooth::HandleUnpair,\n bluetooth::HandleDrop)
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::VisibilityOn /\n bluetooth::HandleSetVisibility
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::VisibilityOff /\n bluetooth::HandleUnsetVisibility
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::ConnectAudio /\n bluetooth::EstablishAudioConnection
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::SignalStrengthData /\n bluetooth::SignalStrength
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::OperatorNameData /\n bluetooth::SetOperatorName
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::BatteryLevelData /\n bluetooth::SetBatteryLevel
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::NetworkStatusData /\n bluetooth::SetNetworkStatus
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::StartStream /\n bluetooth::StartAudio
  bluetooth::Idle - -> bluetooth::Idle : bt::evt::StopStream /\n bluetooth::StopAudio
  bluetooth::Idle - -> bluetooth::Call : bt::evt::StartRouting /\n bluetooth::On::operator()() const::<lambda(const auto:25&, auto:26&, auto:27&, auto:28&)>
  state bluetooth::Call {
    [*] - -> CallSetup
    CallSetup - -> CallRinging : bt::evt::StartRinging /\n bluetooth::StartRinging
    CallSetup - -> CallInitiated : bt::evt::StartRouting /\n bluetooth::InitializeCall
    CallSetup - -> CallInitiated : bt::evt::CallStarted /\n bluetooth::CallStarted
    CallSetup - -> CallRinging : bt::evt::IncomingCallNumber /\n (bluetooth::StartRinging,\n bluetooth::IncomingCall)
    CallRinging - -> CallDropped : bt::evt::StopRinging /\n bluetooth::StopRinging
    CallRinging - -> CallInProgress : bt::evt::CallAnswered /\n bluetooth::CallAnswered
    CallRinging - -> CallDropped : bt::evt::CallTerminated /\n bluetooth::TerminateCall
    CallInitiated - -> CallInProgress : bt::evt::CallAnswered /\n bluetooth::CallAnswered
    CallInitiated - -> CallDropped : bt::evt::StopRinging /\n bluetooth::StopRinging
    CallInitiated - -> CallDropped : bt::evt::CallTerminated /\n bluetooth::TerminateCall
    CallInitiated - -> CallInitiated : bt::evt::IncomingCallNumber /\n bluetooth::IncomingCall
    CallInProgress - -> CallDropped : bt::evt::CallTerminated /\n bluetooth::TerminateCall
    CallDropped - -> [*]
  }
  bluetooth::Idle - -> bluetooth::Call : bt::evt::StartRinging /\n bluetooth::On::operator()() const::<lambda(const auto:25&, auto:26&, auto:27&, auto:28&)>
  bluetooth::Idle - -> bluetooth::Call : bt::evt::IncomingCallNumber /\n bluetooth::On::operator()() const::<lambda(const auto:25&, auto:26&, auto:27&, auto:28&)>
  bluetooth::Idle - -> bluetooth::Call : bt::evt::CallStarted /\n bluetooth::On::operator()() const::<lambda(const auto:25&, auto:26&, auto:27&, auto:28&)>
  bluetooth::Call - -> bluetooth::Idle
}
bluetooth::Setup - -> Restart :\n [!bluetooth::IsInit]
bluetooth::Setup - -> Off : internal_event /\n bluetooth::StateMachine::operator()() const::<lambda(const bluetooth::InitializationError&)>
bluetooth::On - -> Off : bt::evt::PowerOff /\n bluetooth::TurnOff
bluetooth::On - -> Restart : internal_event /\n (bluetooth::StateMachine::operator()() const::<lambda(const bluetooth::ProcessingError&)>,\n bluetooth::TurnOff)
bluetooth::On - -> [*] : bt::evt::ShutDown /\n bluetooth::TurnOff
Restart - -> bluetooth::Setup
Restart - -> [*] : bt::evt::ShutDown /\n bluetooth::TurnOff

[*] - -> ExceptionsHandling
ExceptionsHandling - -> Off : internal_event /\n bluetooth::ExceptionHandler
ExceptionsHandling - -> Off : internal_event /\n bluetooth::ExceptionHandler
Off - -> [*] : bt::evt::ShutDown

@enduml

PlantUML version 1.2021.9(Sun Jul 25 12:13:56 CEST 2021)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Default Encoding: UTF-8
Language: en
Country: US
--></g></svg>
\ No newline at end of file

M module-bluetooth/Bluetooth/doc/uml/CMakeLists.txt => module-bluetooth/Bluetooth/doc/uml/CMakeLists.txt +1 -1
@@ 1,4 1,4 @@
project(call_uml)
project(bluetooth_uml)
add_executable(${PROJECT_NAME} uml_printer.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE
        module-bluetooth

M module-bluetooth/Bluetooth/doc/uml/uml_printer.cpp => module-bluetooth/Bluetooth/doc/uml/uml_printer.cpp +2 -2
@@ 1,10 1,10 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <BluetoothMachine.hpp>
#include <BluetoothStateMachine.hpp>
#include <sml-utils/PlantUML.hpp>

int main()
{
    dump<bluetooth::SM>();
    dump<bluetooth::StateMachine>();
}

M module-bluetooth/Bluetooth/interface/BluetoothDriver.hpp => module-bluetooth/Bluetooth/interface/BluetoothDriver.hpp +0 -1
@@ 21,7 21,6 @@ namespace bluetooth
        [[nodiscard]] virtual auto run() -> Error::Code  = 0;
        [[nodiscard]] virtual auto stop() -> Error::Code = 0;
        [[nodiscard]] virtual auto scan() -> Error       = 0;
        // TODO make bool again
        virtual void stopScan()                                             = 0;
        virtual void setVisibility(bool visibility)                         = 0;
        virtual void pair(Devicei device, std::uint8_t protectionLevel = 0) = 0;

M module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.cpp => module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.cpp +0 -1
@@ 106,7 106,6 @@ namespace bluetooth
            LOG_INFO("BTstack up and running");
            bluetooth::KeyStorage::settings->setValue(bluetooth::Settings::State,
                                                      static_cast<int>(BluetoothStatus::State::On));
            // TODO inform SM and process ON?
            if (powerOnCallback) {
                powerOnCallback();
            }

M module-bluetooth/CMakeLists.txt => module-bluetooth/CMakeLists.txt +6 -0
@@ 34,6 34,12 @@ set(SOURCES
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/command/Command.cpp
        )

if (${PROJECT_TARGET} STREQUAL "TARGET_Linux")
    add_subdirectory(Bluetooth/doc/uml/)
endif()



include(lib/btstack.cmake)
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${BOARD_DIR_SOURCES})


M module-bluetooth/tests/tests-StatefulController.cpp => module-bluetooth/tests/tests-StatefulController.cpp +317 -199
@@ 8,63 8,46 @@

using namespace bluetooth;

auto driver()
{
    fakeit::Mock<AbstractDriver> mock;
    fakeit::When(Method(mock, init)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(mock, init)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(mock, run)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(mock, stop)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(mock, scan)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(mock, stopScan)).AlwaysReturn();
    fakeit::When(Method(mock, setVisibility)).AlwaysReturn();
    fakeit::When(Method(mock, pair)).AlwaysReturn();
    fakeit::When(Method(mock, unpair)).AlwaysReturn();
    fakeit::When(Method(mock, registerErrorCallback)).AlwaysReturn();
    fakeit::When(Method(mock, registerPowerOnCallback)).AlwaysReturn();
    return mock;
};

auto InitializerMock = []() { return Error::Success; };

auto handler()
{
    fakeit::Mock<AbstractCommandHandler> mock;
    fakeit::When(Method(mock, scan)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(mock, stopScan)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(mock, setVisibility)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(mock, establishAudioConnection)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(mock, disconnectAudioConnection)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(mock, pair)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(mock, unpair)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(mock, availableDevices)).AlwaysReturn(Error::Code::Success);
    return mock;
};

template <typename T> auto mock_to_shared(T *val)
{
    auto t = std::shared_ptr<T>(val, [](...) {});
    return t;
}

/// TODO nei wiem czemu to nie działą (double free)
template <typename T> class Mock
namespace mock
{
    T t;
    std::shared_ptr<std::remove_reference_t<decltype(t.get())>> shared_;
    template <typename T> auto mock_to_shared(T *val)
    {
        auto t = std::shared_ptr<T>(val, [](...) {});
        return t;
    }

  public:
    explicit Mock(T t) : t(t)
    {}
    auto driver()
    {
        fakeit::Mock<AbstractDriver> mock;
        fakeit::When(Method(mock, init)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, run)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, stop)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, scan)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, stopScan)).AlwaysReturn();
        fakeit::When(Method(mock, setVisibility)).AlwaysReturn();
        fakeit::When(Method(mock, pair)).AlwaysReturn();
        fakeit::When(Method(mock, unpair)).AlwaysReturn();
        fakeit::When(Method(mock, registerErrorCallback)).AlwaysReturn();
        fakeit::When(Method(mock, registerPowerOnCallback)).AlwaysReturn();
        return mock;
    };

    auto shared()
    auto handler()
    {
        return shared_;
    }
};
        fakeit::Mock<AbstractCommandHandler> mock;
        fakeit::When(Method(mock, scan)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, stopScan)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, setVisibility)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, establishAudioConnection)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, disconnectAudioConnection)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, pair)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, unpair)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, availableDevices)).AlwaysReturn(Error::Code::Success);
        return mock;
    };

namespace mock
{
    auto settings()
    {
        fakeit::Mock<bluetooth::SettingsHolder> mock;


@@ 102,161 85,296 @@ namespace mock
        return mock;
    };

    struct SM
    {
        decltype(driver()) d         = mock::driver();
        decltype(handler()) h        = mock::handler();
        decltype(mock::settings()) s = mock::settings();
        decltype(mock::profile()) p  = mock::profile();

        using Driver    = decltype(mock_to_shared(&d.get()));
        using Processor = decltype(mock_to_shared(&h.get()));
        using Settings  = decltype(mock_to_shared(&s.get()));
        using Devices   = decltype(mock::devices());
        using Profile   = decltype(mock_to_shared(&p.get()));

        Driver drive        = mock_to_shared(&d.get());
        Processor processor = mock_to_shared(&h.get());
        Settings sett       = mock_to_shared(&s.get());
        Devices devices     = mock::devices();
        Profile profile     = mock_to_shared(&p.get());

        StatefulController::Impl controller{drive, processor, InitializerMock, sett, devices, profile};

        SM()
        {}

        explicit SM(DeviceRegistrationFunction registerDevice)
            : controller{drive, processor, std::move(registerDevice), sett, devices, profile}
        {}

        auto &get()
        {
            return controller;
        }
    };

} // namespace mock

TEST_CASE("Given StatefulController when turn on then turned on")
{
    auto d         = driver();
    auto drive     = mock_to_shared(&d.get());
    auto h         = handler();
    auto processor = mock_to_shared(&h.get());
    auto s         = mock::settings();
    auto sett      = mock_to_shared(&s.get());
    auto devs      = mock::devices();
    auto p         = mock::profile();
    auto profile   = mock_to_shared(&p.get());
    // auto profile = Mock(mock::profile());

    StatefulController::Impl controller(drive, processor, InitializerMock, sett, devs, profile);
    using namespace boost::sml;

    auto sm         = mock::SM();
    auto controller = sm.get();

    controller.sm.process_event(bt::evt::PowerOn{});
    REQUIRE(controller.sm.is(state<bluetooth::On>));

    controller.sm.process_event(bt::evt::PowerOff{});
    REQUIRE(controller.sm.is("Off"_s));
}

TEST_CASE("pair/unpair")
{
    using namespace boost::sml;

    auto sm         = mock::SM();
    auto controller = sm.get();
    auto device     = Devicei{"lol"};

    REQUIRE(controller.sm.process_event(bt::evt::PowerOn{}));
    REQUIRE(controller.sm.is(state<bluetooth::On>));

    REQUIRE(controller.sm.process_event(bt::evt::Pair{device}));
    fakeit::Verify(Method(sm.h, pair)).Exactly(1);

    REQUIRE(controller.sm.process_event(bt::evt::Unpair{device}));
    fakeit::Verify(Method(sm.h, unpair)).Exactly(1);
}

TEST_CASE("start/stop scan")
{
    using namespace boost::sml;
    auto sm         = mock::SM();
    auto controller = sm.get();

    REQUIRE(controller.sm.process_event(bt::evt::PowerOn{}));
    REQUIRE(controller.sm.is(state<bluetooth::On>));

    REQUIRE(controller.sm.process_event(bt::evt::StartScan{}));
    fakeit::Verify(Method(sm.h, scan)).Exactly(1);

    REQUIRE(controller.sm.process_event(bt::evt::StopScan{}));
    fakeit::Verify(Method(sm.h, stopScan)).Exactly(1);
}

TEST_CASE("call test - incoming call")
{
    using namespace boost::sml;
    auto sm         = mock::SM();
    auto controller = sm.get();

    REQUIRE(controller.sm.process_event(bt::evt::PowerOn{}));
    REQUIRE(controller.sm.is(state<bluetooth::On>));
    auto number = utils::PhoneNumber::View{};

    SECTION("dropped call")
    {
        REQUIRE(controller.sm.process_event(bt::evt::IncomingCallNumber{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, startRinging)).Exactly(1);
        fakeit::Verify(Method(sm.p, setIncomingCallNumber)).Exactly(1);

        REQUIRE(controller.sm.process_event(bt::evt::CallTerminated{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, terminateCall)).Exactly(1);
    }

    SECTION("answered call")
    {
        REQUIRE(controller.sm.process_event(bt::evt::IncomingCallNumber{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, startRinging)).Exactly(1);
        fakeit::Verify(Method(sm.p, setIncomingCallNumber)).Exactly(1);

        REQUIRE(controller.sm.process_event(bt::evt::CallAnswered{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallInProgress"_s));
        fakeit::Verify(Method(sm.p, callAnswered)).Exactly(1);

        REQUIRE(controller.sm.process_event(bt::evt::CallTerminated{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, terminateCall)).Exactly(1);
    }

    SECTION("missed call")
    {
        REQUIRE(controller.sm.process_event(bt::evt::IncomingCallNumber{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, startRinging)).Exactly(1);
        fakeit::Verify(Method(sm.p, setIncomingCallNumber)).Exactly(1);

        REQUIRE(controller.sm.process_event(bt::evt::StopRinging{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, stopRinging)).Exactly(1);
    }
    SECTION("double missed call")
    {
        LOG_INFO("Double missed call");
        REQUIRE(controller.sm.process_event(bt::evt::IncomingCallNumber{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, startRinging)).Exactly(1);
        fakeit::Verify(Method(sm.p, setIncomingCallNumber)).Exactly(1);

        REQUIRE(controller.sm.process_event(bt::evt::StopRinging{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, stopRinging)).Exactly(1);

        REQUIRE(controller.sm.process_event(bt::evt::IncomingCallNumber{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, startRinging)).Exactly(2);
        fakeit::Verify(Method(sm.p, setIncomingCallNumber)).Exactly(2);

        REQUIRE(controller.sm.process_event(bt::evt::StopRinging{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, stopRinging)).Exactly(2);
    }
}

TEST_CASE("call test - outgoing call")
{
    using namespace boost::sml;
    auto sm         = mock::SM();
    auto controller = sm.get();

    REQUIRE(controller.sm.process_event(bt::evt::PowerOn{}));
    REQUIRE(controller.sm.is(state<bluetooth::On>));
    auto number = utils::PhoneNumber::View{};

    SECTION("dropped call")
    {
        REQUIRE(controller.sm.process_event(bt::evt::CallStarted{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallInitiated"_s));
        fakeit::Verify(Method(sm.p, callStarted)).Exactly(1);

        REQUIRE(controller.sm.process_event(bt::evt::CallTerminated{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, terminateCall)).Exactly(1);
    }

    SECTION("answered call")
    {
        REQUIRE(controller.sm.process_event(bt::evt::CallStarted{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallInitiated"_s));
        fakeit::Verify(Method(sm.p, callStarted)).Exactly(1);

        REQUIRE(controller.sm.process_event(bt::evt::CallAnswered{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallInProgress"_s));
        fakeit::Verify(Method(sm.p, callAnswered)).Exactly(1);

        REQUIRE(controller.sm.process_event(bt::evt::CallTerminated{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, terminateCall)).Exactly(1);
    }

    SECTION("miss-clicked call")
    {
        REQUIRE(controller.sm.process_event(bt::evt::CallStarted{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallInitiated"_s));
        fakeit::Verify(Method(sm.p, callStarted)).Exactly(1);

        REQUIRE(controller.sm.process_event(bt::evt::CallTerminated{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, terminateCall)).Exactly(1);
    }
}

TEST_CASE("Given StatefulController when error during device registration then turned off")
{
    using namespace boost::sml;
    auto sm         = mock::SM([]() { return bluetooth::Error::Code::SystemError; });
    auto controller = sm.get();

    controller.sm.process_event(bt::evt::PowerOn{});
    REQUIRE(!controller.sm.is(state<bluetooth::On>));
}

TEST_CASE("Given StatefulController when error during driver init then turned off")
{
    using namespace boost::sml;
    auto sm         = mock::SM();
    auto controller = sm.get();

    fakeit::When(Method(sm.d, init)).AlwaysReturn(Error::Code::SystemError);

    controller.sm.process_event(bt::evt::PowerOn{});
    REQUIRE(!controller.sm.is(state<bluetooth::On>));
}

TEST_CASE("Given StatefulController when restart then don't init twice")
{
    using namespace boost::sml;
    auto sm         = mock::SM();
    auto controller = sm.get();

    controller.sm.process_event(bt::evt::PowerOn{});
    REQUIRE(controller.sm.is(state<bluetooth::On>));
    fakeit::Verify(Method(sm.d, init)).Exactly(1);
    controller.sm.process_event(bt::evt::PowerOff{});
    // TODO assert driver initialized
    // TODO test for some loaded devices with some other mockup
    // TODO move machine to Impl and assert for next states
    // using namespace boost::sml; REQUIRE(controller->impl.is("Shutdown"_s));
    REQUIRE(controller.sm.is("Off"_s));
    fakeit::Verify(Method(sm.d, init)).Exactly(1);
    fakeit::Verify(Method(sm.p, deInit)).Exactly(1);
    controller.sm.process_event(bt::evt::PowerOn{});
    fakeit::Verify(Method(sm.d, init)).Exactly(1);
    REQUIRE(controller.sm.is(state<bluetooth::On>));
}

// TEST_CASE("pair")
// {
//     auto d = driver();
//     auto drive =  mock_to_shared(&d.get());
//     auto h = handler();
//     auto processor = mock_to_shared(&h.get());
//     auto s = mock::settings();
//     auto sett = mock_to_shared(&s.get());
//     auto devs = mock::devices();
//     auto profile = Mock(mock::profile());
//
//     StatefulController controller(drive, processor, InitializerMock, sett, devs, profile.shared());
//     controller.handle(bt::evt::PowerOn{});
//     controller.handle(bt::evt::Pair{Devicei{"lol"}});
//     // TODO test - no pair device in settings
// }
//
// TEST_CASE("unpair")
// {
//     auto d = driver();
//     auto drive =  mock_to_shared(&d.get());
//     auto h = handler();
//     auto processor = mock_to_shared(&h.get());
//     auto s = mock::settings();
//     auto sett = mock_to_shared(&s.get());
//     auto devs = mock::devices();
//     auto profile = Mock(mock::profile());
//     StatefulController controller(drive, processor, InitializerMock, sett, devs, profile.shared());
//
//     controller.handle(bt::evt::PowerOn{});
//     // TODO here nothing happens
//     controller.handle(bt::evt::Unpair{Devicei{"lol"}});
//     // TODO here - device added
//     controller.handle(bt::evt::Pair{Devicei{"lol"}});
//     // TODO here device removed
//     controller.handle(bt::evt::Pair{Devicei{"lol"}});
// }
//
//
//
// // TEST_CASE("Given StatefulController when error during device registration then turned off")
// // {
// //     auto h = handler();
// //     auto processor = mock_to_shared(&h);
// //
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, []() { return Error::SystemError; }};
// //     controller.handle(bt::evt::PowerOn{});
// // }
// //
// // TEST_CASE("Given StatefulController when error during driver init then turned off")
// // {
// //     driver->initReturnCode = Error::SystemError;
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOn{});
// // }
// //
// // TEST_CASE("Given StatefulController when error during driver run then turned off")
// // {
// //     driver->runReturnCode = Error::SystemError;
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOn{});
// // }
// //
// // TEST_CASE("Given StatefulController when restart then don't init twice")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{driver, processor, InitializerMock};
// //
// //     controller.handle(bt::evt::PowerOn{});
// //     controller.handle(bt::evt::PowerOff{});
// //     driver->initReturnCode = Error::SystemError;
// //     controller.handle(bt::evt::PowerOn{});
// // }
// //
// // TEST_CASE("Given StatefulController when turn off in off state then turned off")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOff{});
// // }
// //
// // TEST_CASE("Given StatefulController when turn off in on state then turned off")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOn{});
// //     controller.handle(bt::evt::PowerOff{});
// // }
// //
// // TEST_CASE("Given StatefulController when shutdown in off state then terminated")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOff{});
// //     REQUIRE(controller.isTerminated());
// // }
// //
// // TEST_CASE("Given StatefulController when shutdown in on state then terminated")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOn{});
// //     controller.handle(bt::evt::PowerOff{});
// //     REQUIRE(controller.isTerminated());
// // }
// //
// // TEST_CASE("Given StatefulController when process command successfully then turned on")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOn{});
// // }
// //
// // // TODO
// // TEST_CASE("Given StatefulController when processing command failed then restarted and turned on")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //
// //     controller.handle(bt::evt::PowerOn{});
// // }
TEST_CASE("Given StatefulController when turn off in off state then turned off")
{
    auto sm         = mock::SM();
    auto controller = sm.get();
    // there is no Off -> Off transition so it won't be handled
    REQUIRE(not controller.sm.process_event(bt::evt::PowerOff{}));
}

TEST_CASE("Given StatefulController when shutdown in off state then terminated")
{
    auto sm         = mock::SM();
    auto controller = sm.get();
    controller.sm.process_event(bt::evt::ShutDown{});
    REQUIRE(controller.sm.is(boost::sml::X));
}

TEST_CASE("Given StatefulController when shutdown in on state then terminated")
{
    auto sm         = mock::SM();
    auto controller = sm.get();
    // there is no Off -> Off transition so it won't be handled
    REQUIRE(controller.sm.process_event(bt::evt::PowerOn{}));
    controller.sm.process_event(bt::evt::PowerOff{});
    controller.sm.process_event(bt::evt::ShutDown{});
    REQUIRE(controller.sm.is(boost::sml::X));
}

TEST_CASE("Given StatefulController when processing command failed then restarted and turned on")
{
    using namespace boost::sml;
    auto sm         = mock::SM();
    auto controller = sm.get();

    fakeit::When(Method(sm.d, init)).AlwaysReturn(Error::Code::SystemError);
    controller.sm.process_event(bt::evt::PowerOn{});
    REQUIRE(!controller.sm.is(state<bluetooth::On>));

    fakeit::When(Method(sm.d, init)).AlwaysReturn(Error::Code::Success);
    controller.sm.process_event(bt::evt::PowerOn{});
    REQUIRE(controller.sm.is(state<bluetooth::On>));
}

M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +37 -35
@@ 139,8 139,8 @@ sys::ReturnCodes ServiceBluetooth::DeinitHandler()

void ServiceBluetooth::ProcessCloseReason(sys::CloseReason closeReason)
{
    sendWorkerCommand(new bt::evt::DisconnectAudio());
    sendWorkerCommand(new bt::evt::PowerOff());
    sendWorkerCommand(std::make_unique<bt::evt::DisconnectAudio>());
    sendWorkerCommand(std::make_unique<bt::evt::PowerOff>());
}

sys::MessagePointer ServiceBluetooth::DataReceivedHandler([[maybe_unused]] sys::DataMessage *msg,


@@ 193,7 193,7 @@ auto ServiceBluetooth::handle(message::bluetooth::SetStatus *msg) -> std::shared
    case BluetoothStatus::State::On:

        cpuSentinel->HoldMinimumFrequency(bsp::CpuFrequencyMHz::Level_3);
        sendWorkerCommand(new bt::evt::PowerOn());
        sendWorkerCommand(std::make_unique<bt::evt::PowerOn>());
        bus.sendMulticast(
            std::make_shared<sys::bluetooth::BluetoothModeChanged>(sys::bluetooth::BluetoothMode::Enabled),
            sys::BusChannel::BluetoothModeChanges);


@@ 213,10 213,10 @@ auto ServiceBluetooth::handle(message::bluetooth::SetStatus *msg) -> std::shared
        break;
    }
    if (newBtStatus.visibility) {
        sendWorkerCommand(new bt::evt::VisibilityOn());
        sendWorkerCommand(std::make_unique<bt::evt::VisibilityOn>());
    }
    else {
        sendWorkerCommand(new bt::evt::VisibilityOff());
        sendWorkerCommand(std::make_unique<bt::evt::VisibilityOff>());
    }
    return sys::MessageNone{};
}


@@ 226,7 226,7 @@ auto ServiceBluetooth::handle(BluetoothPairMessage *msg) -> std::shared_ptr<sys:
    auto device = msg->getDevice();
    bluetoothDevicesModel->removeDevice(device);

    sendWorkerCommand(new bt::evt::Pair(device));
    sendWorkerCommand(std::make_unique<bt::evt::Pair>(device));

    device.deviceState = DeviceState::Pairing;
    bluetoothDevicesModel->insertDevice(device);


@@ 255,16 255,16 @@ auto ServiceBluetooth::handle(BluetoothPairResultMessage *msg) -> std::shared_pt
    return sys::MessageNone{};
}

void ServiceBluetooth::sendWorkerCommand(bt::evt::Base *command)
void ServiceBluetooth::sendWorkerCommand(std::unique_ptr<bt::evt::Base> command)
{
    if (workerQueue != nullptr) {
        workerQueue->push(bluetooth::Command{command});
        workerQueue->push(bluetooth::Command{std::move(command)});
    }
}

auto ServiceBluetooth::handle(message::bluetooth::Unpair *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(new bt::evt::Unpair(msg->getDevice()));
    sendWorkerCommand(std::make_unique<bt::evt::Unpair>(msg->getDevice()));
    bluetoothDevicesModel->removeDevice(msg->getDevice());
    return sys::MessageNone{};
}


@@ 285,10 285,12 @@ auto ServiceBluetooth::handle(message::bluetooth::SetDeviceName *msg) -> std::sh
    auto newName = msg->getName();
    bluetooth::set_name(newName);
    settingsHolder->setValue(bluetooth::Settings::DeviceName, newName);
    sendWorkerCommand(new bt::evt::PowerOff());
    sendWorkerCommand(std::make_unique<bt::evt::PowerOff>());

    btRestartTimer = sys::TimerFactory::createSingleShotTimer(
        this, "btRestartTimer", btRestartDelay, [this](sys::Timer &_) { sendWorkerCommand(new bt::evt::PowerOn()); });
    btRestartTimer =
        sys::TimerFactory::createSingleShotTimer(this, "btRestartTimer", btRestartDelay, [this](sys::Timer &_) {
            sendWorkerCommand(std::make_unique<bt::evt::PowerOn>());
        });
    btRestartTimer.start();

    return sys::MessageNone{};


@@ 297,7 299,7 @@ auto ServiceBluetooth::handle(message::bluetooth::SetDeviceName *msg) -> std::sh
auto ServiceBluetooth::handle(message::bluetooth::Connect *msg) -> std::shared_ptr<sys::Message>
{
    auto device = msg->getDevice();
    sendWorkerCommand(new bt::evt::ConnectAudio(device));
    sendWorkerCommand(std::make_unique<bt::evt::ConnectAudio>(device));
    bluetoothDevicesModel->setInternalDeviceState(device, DeviceState::Connecting);
    bluetoothDevicesModel->syncDevicesWithApp();
    return sys::MessageNone{};


@@ 347,7 349,7 @@ auto ServiceBluetooth::handle(message::bluetooth::ConnectResult *msg) -> std::sh

auto ServiceBluetooth::handle([[maybe_unused]] message::bluetooth::Disconnect *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(new bt::evt::DisconnectAudio());
    sendWorkerCommand(std::make_unique<bt::evt::DisconnectAudio>());
    return sys::MessageNone{};
}



@@ 406,32 408,32 @@ auto ServiceBluetooth::handle(BluetoothMessage *msg) -> std::shared_ptr<sys::Mes

    switch (msg->req) {
    case BluetoothMessage::Scan:
        sendWorkerCommand(new bt::evt::StartScan());
        sendWorkerCommand(std::make_unique<bt::evt::StartScan>());
        break;
    case BluetoothMessage::StopScan:
        sendWorkerCommand(new bt::evt::StopScan());
        sendWorkerCommand(std::make_unique<bt::evt::StopScan>());
        break;
    case BluetoothMessage::getDevicesAvailable:
        sendWorkerCommand(new bt::evt::GetDevicesAvailable());
        sendWorkerCommand(std::make_unique<bt::evt::GetDevicesAvailable>());
        break;
    case BluetoothMessage::Visible: {
        auto visibility =
            not std::visit(bluetooth::BoolVisitor(), settingsHolder->getValue(bluetooth::Settings::Visibility));
        if (visibility) {
            sendWorkerCommand(new bt::evt::VisibilityOn());
            sendWorkerCommand(std::make_unique<bt::evt::VisibilityOn>());
        }
        else {
            sendWorkerCommand(new bt::evt::VisibilityOff());
            sendWorkerCommand(std::make_unique<bt::evt::VisibilityOff>());
        }
    } break;
    case BluetoothMessage::Play:
        sendWorkerCommand(new bt::evt::StartStream());
        sendWorkerCommand(std::make_unique<bt::evt::StartStream>());
        break;
    case BluetoothMessage::Disconnect:
        sendWorkerCommand(new bt::evt::DisconnectAudio());
        sendWorkerCommand(std::make_unique<bt::evt::DisconnectAudio>());
        break;
    case BluetoothMessage::Stop:
        sendWorkerCommand(new bt::evt::StopStream());
        sendWorkerCommand(std::make_unique<bt::evt::StopStream>());
        break;
    default:
        break;


@@ 442,14 444,14 @@ auto ServiceBluetooth::handle(BluetoothMessage *msg) -> std::shared_ptr<sys::Mes

auto ServiceBluetooth::handle(BluetoothAddrMessage *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(new bt::evt::ConnectAudio(msg->device));
    sendWorkerCommand(std::make_unique<bt::evt::ConnectAudio>(msg->device));
    return std::make_shared<sys::ResponseMessage>();
}

auto ServiceBluetooth::handle(sdesktop::developerMode::DeveloperModeRequest *msg) -> std::shared_ptr<sys::Message>
{
    if (typeid(*msg->event) == typeid(sdesktop::bluetooth::GetAvailableDevicesEvent)) {
        sendWorkerCommand(new bt::evt::GetDevicesAvailable());
        sendWorkerCommand(std::make_unique<bt::evt::GetDevicesAvailable>());
    }
    return sys::MessageNone{};
}


@@ 476,7 478,7 @@ auto ServiceBluetooth::handle(message::bluetooth::HFPVolume *msg) -> std::shared

auto ServiceBluetooth::handle(message::bluetooth::StartAudioRouting *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(new bt::evt::StartRouting());
    sendWorkerCommand(std::make_unique<bt::evt::StartRouting>());
    return std::make_shared<sys::ResponseMessage>();
}



@@ 488,7 490,7 @@ auto ServiceBluetooth::handle(CellularCallerIdMessage *msg) -> std::shared_ptr<s

    if (btOn) {
        LOG_DEBUG("Sending to profile!");
        sendWorkerCommand(new bt::evt::IncomingCallNumber(number));
        sendWorkerCommand(std::make_unique<bt::evt::IncomingCallNumber>(number));
    }

    return sys::MessageNone{};


@@ 496,7 498,7 @@ auto ServiceBluetooth::handle(CellularCallerIdMessage *msg) -> std::shared_ptr<s

auto ServiceBluetooth::handle(CellularCallActiveNotification *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(new bt::evt::CallAnswered());
    sendWorkerCommand(std::make_unique<bt::evt::CallAnswered>());
    return std::make_shared<sys::ResponseMessage>();
}



@@ 504,7 506,7 @@ auto ServiceBluetooth::handle(CellularSignalStrengthUpdateNotification *msg) -> 
{
    auto signalStrength = Store::GSM::get()->getSignalStrength();
    LOG_DEBUG("Bluetooth: RSSI %d/5", static_cast<int>(signalStrength.rssiBar));
    sendWorkerCommand(new bt::evt::SignalStrengthData(signalStrength));
    sendWorkerCommand(std::make_unique<bt::evt::SignalStrengthData>(signalStrength));
    return std::make_shared<sys::ResponseMessage>();
}



@@ 512,7 514,7 @@ auto ServiceBluetooth::handle(CellularCurrentOperatorNameNotification *msg) -> s
{
    auto opName = msg->getCurrentOperatorName();
    LOG_DEBUG("Bluetooth: Operator name: %s", opName.c_str());
    sendWorkerCommand(new bt::evt::OperatorNameData(opName));
    sendWorkerCommand(std::make_unique<bt::evt::OperatorNameData>(opName));
    return std::make_shared<sys::ResponseMessage>();
}



@@ 540,7 542,7 @@ void ServiceBluetooth::resetTimeoutTimer()

void ServiceBluetooth::handleTurnOff()
{
    sendWorkerCommand(new bt::evt::PowerOff());
    sendWorkerCommand(std::make_unique<bt::evt::PowerOff>());
    // NOTE: This should be in bluetooth state machine
    cpuSentinel->ReleaseMinimumFrequency();
    bus.sendMulticast(std::make_shared<sys::bluetooth::BluetoothModeChanged>(sys::bluetooth::BluetoothMode::Disabled),


@@ 561,31 563,31 @@ auto ServiceBluetooth::handle(sevm::BatteryStatusChangeMessage *msg) -> std::sha
{
    auto batteryLevel = Store::Battery::get().level;
    LOG_DEBUG("Bluetooth: Battery level %d", batteryLevel);
    sendWorkerCommand(new bt::evt::BatteryLevelData(batteryLevel));
    sendWorkerCommand(std::make_unique<bt::evt::BatteryLevelData>(batteryLevel));
    return sys::MessageNone{};
}
auto ServiceBluetooth::handle(cellular::CallEndedNotification *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(new bt::evt::CallTerminated());
    sendWorkerCommand(std::make_unique<bt::evt::CallTerminated>());
    return sys::MessageNone{};
}
auto ServiceBluetooth::handle(CellularNetworkStatusUpdateNotification *msg) -> std::shared_ptr<sys::Message>
{
    auto status = Store::GSM::get()->getNetwork().status;
    LOG_DEBUG("Bluetooth: Network status %s", magic_enum::enum_name(status).data());
    sendWorkerCommand(new bt::evt::NetworkStatusData(status));
    sendWorkerCommand(std::make_unique<bt::evt::NetworkStatusData>(status));
    return sys::MessageNone{};
}
auto ServiceBluetooth::handle(cellular::CallStartedNotification *msg) -> std::shared_ptr<sys::Message>
{
    if (!msg->isCallIncoming()) {
        auto evt = new bt::evt::CallStarted(msg->getNumber());
        auto evt = std::make_unique<bt::evt::CallStarted>(msg->getNumber());
        sendWorkerCommand(std::move(evt));
    }
    return sys::MessageNone{};
}
auto ServiceBluetooth::handle(CellularIncominCallMessage *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(new bt::evt::StartRinging());
    sendWorkerCommand(std::make_unique<bt::evt::StartRinging>());
    return sys::MessageNone{};
}

M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp => module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp +1 -1
@@ 90,7 90,7 @@ class ServiceBluetooth : public sys::Service
    void ProcessCloseReason(sys::CloseReason closeReason) override;
    virtual sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override;

    void sendWorkerCommand(bt::evt::Base *command);
    void sendWorkerCommand(std::unique_ptr<bt::evt::Base> command);

    void handleTurnOff();


M module-sys/Service/include/Service/Mailbox.hpp => module-sys/Service/include/Service/Mailbox.hpp +2 -2
@@ 47,9 47,9 @@ template <typename T, typename Base = cpp_freertos::Thread *, typename Lock = Se
        if (queue_.empty()) {
            return {};
        }
        auto el = queue_.front();
        auto el = std::move(queue_.front());
        queue_.pop_front();
        return {el};
        return std::optional<T>(std::move(el));
    }

    T pop(uint32_t timeout = portMAX_DELAY)

M module-utils/log/Logger.cpp => module-utils/log/Logger.cpp +1 -1
@@ 33,7 33,7 @@ namespace Log
            {"ServiceAntenna", logger_level::LOGERROR},
            {"ServiceAudio", logger_level::LOGINFO},
            {"ServiceBluetooth", logger_level::LOGINFO},
            {"ServiceBluetooth_w1", logger_level::LOGDEBUG},
            {"ServiceBluetooth_w1", logger_level::LOGINFO},
            {"ServiceFota", logger_level::LOGINFO},
            {"ServiceEink", logger_level::LOGINFO},
            {"ServiceDB", logger_level::LOGINFO},