lacteApp
C++17 service for Lacte hardware
Loading...
Searching...
No Matches
UiApp.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <atomic>
4#include <config_store.hpp>
5#include <functional>
6#include <memory>
7#include <string>
8#include <thread>
9#include <utility>
10
11#include "AppContext.h"
12#include "bus/UiBusClient.hpp"
13
14namespace lacte::ui {
15
16// Базовый класс UI, который работает в собственном потоке внутри процесса
17// приложения. Строит и владеет СОБСТВЕННЫМ клиентом (см. UiBusClient.hpp) на
18// ОДНОЙ общей шине, которую строит LacteApp (см. AppContext::bus) —
19// LacteApp всегда передаёт этому классу только AppContext&, но никогда уже
20// готовый клиент. Start() запускает его (порождает поток, который
21// присоединяется к шине и выполняет UI), а Stop() останавливает — либо
22// можно просто дать ему разрушиться (RAII: деструктор сам останавливает
23// поток и дожидается его завершения). Конкретный UI (консольный,
24// framebuffer) реализует только Exec(): подключается к бэкенду через
25// ConnectBus(), а затем вызывает методы напрямую на Hub()/Runtime()
26// (UiBusClient.hpp) — отдельного фасада клиента нет, поскольку собственные
27// методы UiBusClient уже говорят всё, что могло бы туда добавиться.
28// Приложение (LacteApp) владеет одним экземпляром на каждый режим --ui; UI
29// общается с бэкендом только через шину, поэтому остаётся самодостаточной,
30// переносимой единицей.
31class UiApp {
32 public:
33 // Хуки на стороне отрисовки, которыми управляет бэкенд, проталкивая
34 // команды: UI сам определяет, что означают "enable/disable" и "изменение
35 // aeration" для его виджетов. Любой из них может быть пустым (например,
36 // для UI, который только опрашивает состояние).
37 struct Callbacks {
38 std::function<void(bool enabled)> on_enabled;
39 std::function<void(int aeration)> on_aeration;
40 };
41
42 // `ctx` объединяет ОДНУ общую шину, которую строит LacteApp (см.
43 // LacteApp::RunImpl()), и собственный живой ConfigStore LacteApp (см.
44 // AppContext.h) — этот UI строит и владеет СОБСТВЕННЫМ клиентом на
45 // `ctx.bus` (см. UiBusClient::create()), а не получает уже готовый; сам
46 // `ctx` должен жить дольше этого UI. `address`/`name` — это
47 // адрес/интроспекционное имя собственного клиента этого UI (см.
48 // AppConfig::K_UI_BUS_ADDRESS/kUiBusNodeName). Конкретный UI читает из
49 // `ctx.config` любые нужные ему пути к устройствам/настройки (см.
50 // FramebufferUi::Exec()); UI, которому нечего оттуда читать, как
51 // ConsoleUi, просто никогда не вызывает Config().
52 explicit UiApp(const AppContext& ctx, const uint32_t address,
53 const std::string& name)
54 : m_bus_(bus::UiBusClient::create(ctx.bus, address, name, ctx.app_name)),
55 m_config_(ctx.config),
56 m_shared_bus_(ctx.bus) {}
57 virtual ~UiApp() { stop(); }
58
59 UiApp(const UiApp&) = delete;
60 auto operator=(const UiApp&) -> UiApp& = delete;
61
62 // Порождает поток UI и немедленно возвращает управление.
63 void start();
64
65 // Сигнализирует UI остановиться и дожидается завершения его потока.
66 // Идемпотентен; деструктор тоже его вызывает.
67 void stop();
68
69 // Вызывается (из собственного потока UI, сразу после возврата из Exec())
70 // всякий раз, когда UI прекращает работу ПО СОБСТВЕННОЙ ИНИЦИАТИВЕ —
71 // пользователь нажал Quit/'q', либо — в те времена, когда консольный
72 // режим ConsoleUi был основан на FTXUI, а не на сегодняшнем простом ANSI
73 // (см. файловый doc-комментарий ConsoleUi.cpp) — СОБСТВЕННАЯ обработка
74 // SIGINT в FTXUI молча завершала цикл экрана раньше, чем реальный
75 // обработчик сигналов этого приложения вообще успевал сработать.
76 // Позволяет владеющему приложению (LacteApp) трактовать "UI завершился"
77 // как "пожалуйста, останови всё приложение целиком" — раньше завершение
78 // потока UI было полностью невидимо для остальной части приложения;
79 // воспроизведено вживую (на том старом консольном UI на базе FTXUI):
80 // нажатие 'q' (или ровно одно нажатие Ctrl+C, пока работал цикл FTXUI)
81 // закрывало UI на экране, но оставляло процесс бэкенда бесконечно
82 // работать в фоне без интерфейса — второй Ctrl+C требовался только
83 // потому, что временная подмена обработчика сигналов в FTXUI к тому
84 // моменту уже автоматически восстановила настоящий обработчик
85 // приложения. По-прежнему нужен в общем виде и сегодня (любой UI —
86 // собственная 'q' простого ANSI-консольного UI, framebuffer-UI — всё
87 // ещё может завершиться по своей инициативе), хотя конкретное
88 // взаимодействие FTXUI/SIGINT, которое это мотивировало, к ConsoleUi
89 // больше не относится. Должен быть установлен ДО Start(), а не после —
90 // иначе поток UI в принципе может оказаться уже за пределами Exec() к
91 // тому моменту, когда вызывающий код доберётся до установки этого
92 // колбэка.
93 void set_on_exit(std::function<void()> on_exit) {
94 m_on_exit_ = std::move(on_exit);
95 }
96
97 protected:
98 virtual auto exec()
99 -> int = 0; // собственный цикл UI; выполняется в потоке UI //
100
101 // Строит runtime этого UI и подписывает его на собственный клиент (см.
102 // UiBusClient::subscribe_runtime()) — с этого момента Runtime() валиден.
103 // subscribe_runtime() захватывает только weak_ptr на runtime, поэтому
104 // этот UI (и его runtime) можно уничтожить в любом порядке относительно
105 // клиента, на который он подписан, без риска висячего колбэка.
106 void connect_bus(Callbacks callbacks);
107
108 [[nodiscard]] auto hub() const -> bus::UiBusClient& { return *m_bus_; }
109 [[nodiscard]] auto runtime() const -> bus::UiRuntime& { return *m_runtime_; }
110
111 [[nodiscard]] auto config() const -> insitech::config::ConfigStore<>& {
112 return m_config_;
113 }
114
115 // Сама ОДНА общая шина (а не собственный клиент этого UI на ней) —
116 // позволяет конкретному UI по запросу вывести отчёт интроспекции шины
117 // (см. `IHub::print_table_report()`), не имея собственного
118 // `AppContext&` (этот UI его никогда не хранит — почему сохраняются
119 // только `m_bus_`/`m_config_`, см. doc-комментарий этого конструктора).
120 [[nodiscard]] auto shared_bus() const -> insitech::bus::IHub& {
121 return m_shared_bus_;
122 }
123
124 // Флаг работы, который должен опрашивать цикл UI и который переключает
125 // Stop(). Передаётся циклам, чтобы UI мог и сам себя остановить
126 // (например, по клавише выхода), сбросив его.
127 auto run_flag() -> std::atomic_bool& { return m_run_flag_; }
128
129 private:
130 std::shared_ptr<bus::UiBusClient> m_bus_;
131 insitech::config::ConfigStore<>& m_config_;
132 insitech::bus::IHub& m_shared_bus_;
133 std::atomic_bool m_run_flag_{false};
134 std::shared_ptr<bus::UiRuntime> m_runtime_;
135 std::thread m_thread_;
136 std::function<void()> m_on_exit_;
137};
138
139} // namespace lacte::ui
UiBusClient: собственный клиент UI на шине lacte - создаётся и принадлежит самому UiApp (см.
void connect_bus(Callbacks callbacks)
auto hub() const -> bus::UiBusClient &
Definition UiApp.hpp:108
auto runtime() const -> bus::UiRuntime &
Definition UiApp.hpp:109
auto shared_bus() const -> insitech::bus::IHub &
Definition UiApp.hpp:120
auto config() const -> insitech::config::ConfigStore<> &
Definition UiApp.hpp:111
auto run_flag() -> std::atomic_bool &
Definition UiApp.hpp:127
virtual ~UiApp()
Definition UiApp.hpp:57
UiApp(const UiApp &)=delete
void set_on_exit(std::function< void()> on_exit)
Definition UiApp.hpp:93
virtual auto exec() -> int=0
auto operator=(const UiApp &) -> UiApp &=delete
UiApp(const AppContext &ctx, const uint32_t address, const std::string &name)
Definition UiApp.hpp:52
std::function< void(int aeration)> on_aeration
Definition UiApp.hpp:39
std::function< void(bool enabled)> on_enabled
Definition UiApp.hpp:38