~aleteoryx/muditaos

ref: d4d0e7ec3487e64649f764c2bbcf5c25cf3c204d muditaos/module-bluetooth/Bluetooth/WorkerController.cpp -rw-r--r-- 6.6 KiB
d4d0e7ec — Tomasz Rybarski [BH-1222] Alarm Bottom Message Translations 4 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "WorkerController.hpp"

#include "Device.hpp"
#include "interface/profiles/ProfileManager.hpp"

#include <log/log.hpp>
#define BOOST_SML_CFG_DISABLE_MIN_SIZE // GCC10 fix
#include <boost/sml.hpp>
#include <magic_enum.hpp>
#include <stdexcept>

namespace bluetooth
{
    namespace sml = boost::sml;

    namespace
    {
        struct TurnOn
        {};
        struct TurnOff
        {};
        struct ShutDown
        {};
        struct ProcessCommand
        {
            Command command;
        };

        class InitializationError : public std::runtime_error
        {
          public:
            using std::runtime_error::runtime_error;
        };

        class ProcessingError : public std::runtime_error
        {
          public:
            using std::runtime_error::runtime_error;
        };

        struct InitializationState
        {
            bool isInitDone = false;
        };

        struct Setup
        {
          public:
            auto operator()() const
            {
                auto isInit = [](InitializationState &data) { return data.isInitDone; };
                auto init   = [](std::shared_ptr<AbstractDriver> &driver) {
                    if (const auto status = driver->init(); status != Error::Success) {
                        throw InitializationError{"Unable to initialize a bluetooth driver."};
                    }
                };
                auto setup = [](DeviceRegistrationFunction &registerDevice, InitializationState &data) {
                    if (const auto status = registerDevice(); status != Error::Success) {
                        throw InitializationError{"Unable to initialize bluetooth"};
                    }
                    data.isInitDone = true;
                };
                auto startDriver = [](std::shared_ptr<AbstractDriver> &driver) {
                    if (const auto status = driver->run(); status != Error::Success) {
                        throw InitializationError{"Unable to run the bluetooth driver"};
                    }
                };

                using namespace sml;
                // clang-format off
                return make_transition_table(*"Setup"_s / startDriver = "StartingDriver"_s,
                                             "Setup"_s + on_entry<_> [ !isInit ] / ( init, setup ),
                                             "StartingDriver"_s = X);
                // clang-format on
            }
        };

        struct On
        {
            auto operator()() const
            {
                auto isInit        = [](InitializationState &data) { return data.isInitDone; };
                auto handleCommand = [](std::shared_ptr<AbstractCommandHandler> &processor,
                                        const ProcessCommand &processCommand) {
                    if (const auto status = processor->handle(processCommand.command); status != Error::Success) {
                        throw ProcessingError{"Failed to process command"};
                    }
                };

                using namespace sml;
                // clang-format off
                return make_transition_table(*"Idle"_s + event<ProcessCommand> [ isInit ] / handleCommand = "Processing"_s,
                                             "Processing"_s = "Idle"_s);
                // clang-format on
            }
        };

        class StateMachine
        {
          public:
            auto operator()() const
            {
                auto turnOff              = [](std::shared_ptr<AbstractDriver> &driver) { driver->stop(); };
                auto printInitError       = [](const InitializationError &error) { LOG_ERROR("%s", error.what()); };
                auto printProcessingError = [](const ProcessingError &error) { LOG_ERROR("%s", error.what()); };

                using namespace sml;
                // clang-format off
                return make_transition_table(*"Off"_s + event<TurnOn> = state<Setup>,
                                             state<Setup> = state<On>,
                                             state<Setup> + exception<InitializationError> / printInitError = "Off"_s,
                                             state<On> + event<TurnOff> / turnOff = "Off"_s,
                                             state<On> + exception<ProcessingError> / ( printProcessingError, turnOff ) = "Restart"_s,
                                             "Restart"_s = state<Setup>,
                                             "Off"_s + event<ShutDown> = X);
                // clang-format on
            }
        };
    } // namespace

    class StatefulController::Impl
    {
      public:
        Impl(std::shared_ptr<AbstractDriver> &&driver,
             std::shared_ptr<AbstractCommandHandler> &&handler,
             DeviceRegistrationFunction &&registerDevice);

        using SM = sml::sm<StateMachine>;
        SM sm;
    };

    StatefulController::Impl::Impl(std::shared_ptr<AbstractDriver> &&driver,
                                   std::shared_ptr<AbstractCommandHandler> &&handler,
                                   DeviceRegistrationFunction &&registerDevice)
        : sm{std::move(driver), std::move(handler), std::move(registerDevice), InitializationState{}}
    {}

    StatefulController::StatefulController(std::shared_ptr<AbstractDriver> &&driver,
                                           std::shared_ptr<AbstractCommandHandler> &&handler,
                                           DeviceRegistrationFunction &&registerDevice)
        : pimpl(std::make_unique<Impl>(std::move(driver), std::move(handler), std::move(registerDevice)))
    {}

    StatefulController::~StatefulController() noexcept = default;

    void StatefulController::turnOn()
    {
        pimpl->sm.process_event(TurnOn{});
    }

    void StatefulController::turnOff()
    {
        pimpl->sm.process_event(TurnOff{});
    }

    void StatefulController::shutdown()
    {
        if (isOn()) {
            turnOff();
        }
        pimpl->sm.process_event(ShutDown{});
    }

    auto StatefulController::isOn() const -> bool
    {
        using namespace sml;
        return !pimpl->sm.is("Off"_s) && !isTerminated();
    }

    auto StatefulController::isTerminated() const -> bool
    {
        using namespace sml;
        return pimpl->sm.is(X);
    }

    void StatefulController::processCommand(Command command)
    {
        LOG_INFO("Process command: %s", magic_enum::enum_name(command.getType()).data());
        pimpl->sm.process_event(ProcessCommand{command});
        command.destroy();
    }
} // namespace bluetooth