lacteApp
C++17 service for Lacte hardware
Loading...
Searching...
No Matches
LacteApp.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <memory>
4#include <optional>
5#include <runtime/runtime.hpp>
6#include <string>
7#include <utility>
8
9#include "AppContext.h"
10#include "AppDb.hpp"
11#include "AppRuntime.hpp"
12#include "BoardModel.h"
13#include "ConsoleApp.hpp"
14#include "Logger.hpp"
15#include "SystemState.hpp"
17#include "bus/UiBusClient.hpp"
18#include "cm_protocol.hpp"
19#include "libraries/interfaces/UartLinux.hpp"
20#include "protocols/lacte/VirtualBoard.hpp"
21#include "sqlite_bus_adapter.hpp"
22#include "ui/UiApp.hpp"
23
24// Обращённая к шине сторона m_storage_ (см. собственный файловый
25// doc-комментарий AppDb.hpp о том, почему сам ThreadSafeDB/AppDb вообще
26// ничего не знает о шине) - здесь список Meta прописан явно, вместо того
27// чтобы переиспользовать его из AppDb::Base, поскольку SqliteBusAdapter<
28// Meta...> - это совершенно другой шаблон (нет способа повторно применить
29// вариативный пакет, извлечённый из одного шаблона класса, как список
30// аргументов другого, без механики более сложной, чем оправдывают четыре
31// повторяющихся имени типа).
32using AppDbBusAdapter = insitech::database::SqliteBusAdapter<
37
38// Конкретная инстанциация Runtime<RuntimeFields>, которой владеет это
39// приложение (см. собственный accessor runtime() ConsoleApp ниже, который
40// разрешается ровно в этот тип, поскольку LacteApp указывает RuntimeFields
41// как собственный аргумент шаблона ConsoleApp'а RuntimeFieldsTuple - см.
42// собственный doc-комментарий @tparam в ConsoleApp.hpp). Самому LacteApp
43// никогда не нужно явно прописывать этот алиас - он просто вызывает
44// runtime(). Он существует исключительно для кода, которому нужно назвать/
45// сконструировать такой объект напрямую, не повторяя полную инстанциацию
46// шаблона - а именно для LacteApp/tests/BoardModelTest.cpp, который
47// подключает собственный LacteRuntime к BoardModel::OnUpdate() так же, как
48// это делает конструктор этого приложения (см. LacteApp.cpp).
49using LacteRuntime = insitech::runtime::Runtime<RuntimeFields>;
50
51// Оболочка приложения — тонкий связующий слой, а не место, где живёт
52// продуктовая логика. Вся её работа сводится к следующему:
53// * разобрать командную строку (см. опции m_app в конструкторе) и
54// превратить флаги в решения: виртуальная плата или реальная, какую
55// прошивку заливать, какой UI поднимать (--ui), тестовые режимы;
56// * владеть ресурсами уровня процесса и передавать их частям, которые
57// выполняют работу — она настраивает BoardModel (интерфейсы/виртуальная
58// плата) и подключает шинный транспорт UI/БД, затем запускает цикл
59// обновления;
60// * служить точкой, через которую тестовый набор управляет приложением
61// (он конструирует LacteApp с заранее заданными аргументами, путём к БД
62// и входным потоком — см. LacteApp/tests);
63// * самостоятельно реализовывать `ModelBusHandler` (health/param/snapshot/
64// rfid - см. `bus/ModelBusClient.hpp`), напрямую из результата опроса
65// BoardModel плюс собственного `runtime()` (см. базовый класс
66// ConsoleApp) - раньше это было задачей BoardModel; теперь BoardModel
67// вообще ничего не знает о шине (см. собственный файловый
68// doc-комментарий BoardModel.h), только о самой плате.
69// Шинный транспорт (см. собственный AppContext в
70// Libraries/app_control/AppContext.h - multicast-интерфейс открывается и
71// подключается к шине на этапе сборки контекста, ещё до первого вызова
72// Instance() - самому LacteApp здесь делать нечего) живёт в insitech::bus
73// (AppContext::bus - см. ConsoleApp::m_context_ - строится тем, кто вызывает
74// Instance()), доменный протокол - в cm_protocol.
75class LacteApp final
76 : public insitech::ConsoleApp<LacteApp, insitech::DefaultTerminationReasons,
77 RuntimeFields>,
79 public:
80 // Раньше host_rx_buffer/host_tx_buffer жили здесь и передавались в
81 // BoardModel как аргументы шаблона - теперь это собственные статические
82 // члены BoardModel (см. BoardModel::host_rx_buffer/host_tx_buffer в
83 // BoardModel.h), поскольку экземпляр всегда только один. VirtualBoard
84 // теперь так же владеет собственными буферами rx/tx (см.
85 // protocols/lacte/VirtualBoard.hpp) - здесь тоже нечего объявлять или
86 // передавать.
87 proto::interface::UartLinuxInterface m_uart_interface;
88 // HMI UART - совершенно отдельная физическая плата (управления
89 // кофемашиной), не lacte-плата (см. m_uart_interface выше) - открывается
90 // в run_impl(), только когда реально присутствует устройство (см.
91 // lacte::ui::hmi_interface_available()), напрямую в этом приложении, а
92 // не в каком-либо конкретном UI - см. собственный doc-комментарий
93 // m_system_state_/m_hmi_protocol_ ниже о том, почему.
94 proto::interface::UartLinuxInterface m_hmi_uart_interface;
95 // Один буфер на приём/передачу HMI-протокола (см. m_hmi_protocol_ ниже) -
96 // inline static член класса, а не namespace-scope переменная в .cpp (как
97 // было у hmi_tx_buffer в старой версии FramebufferUi.cpp), по той же
98 // причине, что и у BoardModel::host_rx_buffer/host_tx_buffer
99 // (BoardModel.h) - нужен адрес с внешней связностью для нетипового
100 // шаблонного параметра BackBoardProtocol, а экземпляр LacteApp на
101 // процесс всегда ровно один.
102 // NOLINTBEGIN(modernize-avoid-c-arrays)
103 static constexpr std::size_t K_HMI_TX_BUFFER_SIZE = 300;
104 inline static uint8_t hmi_tx_buffer[K_HMI_TX_BUFFER_SIZE]{};
105 // NOLINTEND(modernize-avoid-c-arrays)
108 // Совмещённое состояние системы (online lacte-платы + HMI-сигнал двери,
109 // см. собственный doc-комментарий SystemState.hpp) - отдельный объект, а
110 // не логика внутри какого-либо конкретного UI: ни один UI не должен
111 // ничего знать о HMI-протоколе вообще, только получать команду
112 // enable/disable по шине (см. run_impl(), обработчик m_hmi_protocol_ -
113 // публикует через m_ui_notifier_->set_ui_enabled(), тем же каналом
114 // UiCommand, которым уже пользуется отключение UI при обрыве связи с
115 // платой, см. runtime().on<BoardOnlineStatus>() в конструкторе). Оба UI
116 // (framebuffer и консольный) видят текущий system_state и через
117 // BoardSnapshot (см. BuildSnapshot()), для отображения/диагностики.
118 // SetDoorSignalLostForTests() ниже позволяет тестам задавать
119 // HMI-половину напрямую, без дублёра HMI-протокола - см. собственный
120 // doc-комментарий SystemState.hpp о том, почему это вообще возможно
121 // (чистая таблица истинности из двух bool).
123 // СОБСТВЕННЫЙ именованный Client этого приложения - каждый модуль
124 // логирует через такой клиент (см. собственное правило
125 // ConsoleApp/insitech::Logger), а не напрямую через m_context_.logger,
126 // чтобы сообщения самого LacteApp тоже были видны как события шины
127 // log.entry/log.line, помеченные тегом "[<app_name>]", так же как у
128 // BoardModel ("[model]"). Объявлен ПЕРЕД m_board_model_ (инициализируется
129 // первым в списке инициализации конструктора) исключительно по
130 // соглашению - реальной зависимости между ними нет.
131 std::shared_ptr<insitech::LoggerBusClient> m_client_;
132 // Самим кэшем/диспетчеризацией платы runtime владеет базовый класс
133 // ConsoleApp выше (его собственный m_runtime_, доступный через runtime()
134 // - см. собственный doc-комментарий @tparam RuntimeFieldsTuple в
135 // ConsoleApp.hpp; LacteApp указывает RuntimeFields как этот аргумент
136 // шаблона, поэтому сам никогда не объявляет соответствующий член, не
137 // разрешает путь кэша и не регистрирует callback сброса) - это
138 // приложение также регистрирует на нём обработчики записи в БД для
139 // отдельных полей напрямую (см. конструктор, заменяющий старый
140 // DbRowRouter, работавший через шину), а также callback OnUpdate() на
141 // m_board_model_ ниже, который проталкивает собственные показания
142 // BoardModel в этот runtime (см. собственный файловый doc-комментарий
143 // BoardModel.h о том, почему сам BoardModel никогда не трогает runtime()).
145 // Виртуальная плата — тестовая/девелоперская замена реальной платы; её
146 // дело, как и дело BoardModel, — это просто набор интерфейсов (см.
147 // AttachVirtualBoard). Сам BoardModel ничего не знает о VirtualBoard.
148 std::unique_ptr<proto::lacte::VirtualBoard> m_virtual_board;
149
152
154 // Сам ConsoleApp::instance() унаследован от AppShell (см. собственный
155 // файловый doc-комментарий ConsoleApp.hpp) - именно код AppShell реально
156 // конструирует LacteApp, поэтому ему это тоже нужно.
157 friend insitech::AppShell<LacteApp, insitech::DefaultTerminationReasons,
159
160 // Только для тестов: LacteApp::Instance() — это синглтон на весь процесс,
161 // а CLI11 не сбрасывает привязанный к опции член обратно в значение по
162 // умолчанию, если последующий вызов App::parse() просто не указывает эту
163 // опцию (член сохраняет то значение, которое установил ПРЕДЫДУЩИЙ вызов
164 // Run() - см. комментарий к virtual_args в LacteApp/tests/
165 // LacteAppTest.cpp). ResetTestModeForTests() позволяет тестовому набору
166 // напрямую очищать m_args_.test_mode между телами TEST_F, вместо борьбы
167 // с CLI11 за невыразимое значение --test "явно пустая строка".
168 void reset_test_mode_for_tests() { m_args_.test_mode.clear(); }
169
170 // Только для тестов, та же логика, что и у ResetTestModeForTests() выше:
171 // Instance() учитывает свой аргумент конструктора bin_path только при
172 // самом первом вызове за весь процесс — каждый последующий вызов (какой
173 // бы TEST_F ни оказался там первым) просто возвращает уже построенный
174 // синглтон, молча игнорируя другой bin_path, переданный в более поздний
175 // вызов Instance(). Тест, которому нужен конкретный bin_path и который
176 // не может положиться на разбор --bin-path (например, если он ни разу
177 // не перепарсивает аргументы CLI), устанавливает его напрямую здесь,
178 // вместо борьбы с синглтоном через конструктор.
179 void set_bin_path_for_tests(std::string path) {
180 m_args_.bin_path = std::move(path);
181 }
182
183 auto get_bin_path() const -> std::string { return m_args_.bin_path; }
184
185 // Только для тестов, та же логика, что и у SetBinPathForTests() выше:
186 // теперь run_impl() разрешает db_path напрямую из m_context_.config
187 // (kAppDir/kDbName) - единственного источника истины, а не аргумента,
188 // переданного вызывающим кодом. Тесту, которому нужен собственный файл
189 // БД на каждый TEST_F (конфигурация общего g_test_app_context фиксирована
190 // на весь бинарник), проще установить его напрямую здесь, чем
191 // пересобирать целиком config/AppContext ради переопределения одного
192 // поля.
193 void set_db_path_for_tests(std::string path) {
194 m_db_path_override_ = std::move(path);
195 }
196
197 // Только для тестов: задаёт HMI-половину SystemState напрямую, минуя
198 // реальный HMI UART/BackBoardProtocol - см. собственный doc-комментарий
199 // SystemStateResolver в SystemState.hpp о том, почему это вообще
200 // корректно (чистая таблица истинности из двух bool, ей всё равно,
201 // откуда пришёл каждый из них). Идёт через тот же ApplyDoorSignal(), что
202 // и реальный HMI-колбэк (run_impl()) - тест упражняет и пересчёт
203 // m_system_state_, и публикацию UiCommand через m_ui_notifier_, а не
204 // только первое.
205 void set_door_signal_lost_for_tests(const bool lost) {
206 apply_door_signal(lost);
207 }
208
209 auto run_impl(std::istream& input = std::cin) -> int;
210
211 void stop() {
212 m_running = false;
213 m_cv.notify_all();
214 }
215
220 [[nodiscard]] auto get_bus() const -> lacte::bus::ModelBusClient* {
221 return m_bus_.get();
222 }
223
224 private:
225 // Создаёт виртуальную плату (тестовую/девелоперскую замену реального
226 // оборудования) и подключает её интерфейсы к BoardModel — BoardModel
227 // видит только интерфейсы и никогда не узнаёт о существовании
228 // виртуальной платы. Используется только в режиме --virtual.
229 void attach_virtual_board();
230
231 // Строит и владеет СОБСТВЕННЫМИ клиентами этого приложения на `ctx.bus`
232 // — LacteApp сам строит только ОДИН общий узел шины (см.
233 // AppContext::bus); каждый участник (раньше это делал BoardModel, теперь
234 // UI, БД) строит собственного клиента (или клиентов) поверх него и сам
235 // подключает на нём свой протокол, вместо того чтобы получать уже
236 // готовые. Немедленно обслуживает темы запросов шины через
237 // ServeModel(*this) - теперь именно LacteApp реализует ModelBusHandler
238 // (см. doc-комментарий класса). Строит ДВУХ клиентов: `m_bus_`
239 // (ModelBusClient, для ServeModel()) и `m_ui_notifier_` (UiBusClient,
240 // исключительно для отправки UiCommand/BoardSnapshotEvent - это знание о
241 // протоколе UI, поэтому оно живёт в UiBusClient, а не здесь - см.
242 // файловый doc-комментарий UiBusClient.hpp).
243 void connect_bus(const AppContext& ctx, uint32_t address,
244 const std::string& name, uint32_t ui_notifier_address);
245
246 // Пересчитывает m_system_state_ из нового HMI-сигнала двери и, если уже
247 // подключена шина (m_ui_notifier_), публикует итог как UiCommand - см.
248 // собственный doc-комментарий .cpp-определения о том, почему это общая
249 // точка входа для реального HMI-колбэка И SetDoorSignalLostForTests().
250 void apply_door_signal(bool door_signal_lost);
251
252 // Шинный клиент, на котором это приложение обслуживает запросы
253 // ModelBusHandler, построенный и принадлежащий самому приложению (см.
254 // ConnectBus()) - реализует контракт ModelBusHandler, обслуживается
255 // через m_bus_->ServeModel(*this).
256 std::shared_ptr<lacte::bus::ModelBusClient> m_bus_;
257
258 // Второй клиент, исключительно для отправки UiCommand/BoardSnapshotEvent
259 // в UI (например, DisableUi() при изменении состояния платы - см.
260 // собственную регистрацию конструктора runtime().on<BoardOnlineStatus>())
261 // - это UiBusClient, а не ModelBusClient, поскольку это знание о
262 // протоколе UI (см. файловый doc-комментарий UiBusClient.hpp о причине).
263 // Нужен собственный, отличный от других, адрес шины - один клиент не
264 // может быть зарегистрирован на двух адресах сразу.
265 //
266 // m_ui_notifier_mx_ защищает КАЖДОЕ обращение к m_ui_notifier_ (чтение
267 // И запись, включая обнуление в teardown run_impl()) - без этого
268 // `if (m_ui_notifier_) { m_ui_notifier_->...(); }` в apply_door_signal()
269 // и в runtime().on<BoardOnlineStatus>() ниже гонится с main-потоком:
270 // apply_door_signal() вызывается с ФОНОВОГО потока m_hmi_protocol_ (см.
271 // ProtocolEndpoint::m_deque_thread в protolib - живёт всю жизнь
272 // LacteApp, никогда не останавливается на время teardown одного
273 // run_impl()), а on<BoardOnlineStatus> - с потока BoardModel::Worker().
274 // Оба могут сработать РОВНО в момент, когда run_impl() делает
275 // `m_ui_notifier_ = nullptr;` - проверка "не nullptr" сама по себе не
276 // синхронизирована с этим присваиванием, значит между проверкой и
277 // вызовом `->...()` объект может быть уже разрушен. Воспроизведено
278 // вживую на реальной плате: один и тот же паттерн ("после lacte model
279 // stopped") давал то SIGSEGV, то std::terminate() без исключения в
280 // полёте (мусорный vtable через уже освобождённую память) - разные
281 // симптомы одной и той же гонки, не два разных бага.
282 mutable std::mutex m_ui_notifier_mx_;
283 std::shared_ptr<lacte::bus::UiBusClient> m_ui_notifier_;
284
285 // Гейт teardown: run_impl() выставляет его в true ПЕРВЫМ действием
286 // своего teardown-блока, до единого reset()/nullptr - m_ui_.reset(),
287 // m_board_model_.stop() и всё, что идёт следом, ещё даже не начинались.
288 // Каждый обработчик, вызываемый с чужого потока и трогающий что-либо из
289 // teardown (m_storage_ в runtime().on<InfoPacketType>()/
290 // <BoardErrorDiff>()/on_unhandled(), m_ui_notifier_ в
291 // apply_door_signal()/on<BoardOnlineStatus>() - см. их собственные
292 // doc-комментарии) проверяет этот флаг ПЕРВОЙ строкой и молча
293 // выходит, если teardown уже начался - вместо того чтобы у каждого
294 // такого поля по отдельности заводить свой mutex (m_ui_notifier_mx_
295 // выше - именно такая точечная защита, оставлена как есть: сама по
296 // себе она уже полная гарантия для ЭТОГО одного поля, но не
297 // масштабируется на каждое новое поле, которое когда-либо понадобится
298 // читать из BoardModel::Worker()/m_hmi_protocol_'а). Не устраняет гонку
299 // формально (проверка флага и использование объекта - всё ещё два
300 // отдельных шага), но сокращает окно с "весь teardown" до "несколько
301 // инструкций между этим store() и первым reset()" - на практике этого
302 // достаточно, поскольку любой обработчик, уже начавший выполняться до
303 // set(true), либо успевает завершиться в этом крошечном окне, либо
304 // (для m_ui_notifier_ конкретно) всё равно защищён m_ui_notifier_mx_
305 // выше. Сбрасывается обратно в false в начале run_impl() - тот же
306 // экземпляр LacteApp может пережить несколько вызовов run_impl() (см.
307 // собственный комментарий об этом в teardown-блоке run_impl()).
308 std::atomic_bool m_tearing_down_{false};
309
310 // Контракт ModelBusHandler (см. bus/ModelBusClient.hpp) - это приложение
311 // реализует его напрямую из состояния m_board_model_ (health/param) и
312 // собственного runtime() (snapshot через BuildSnapshot(), rfid через
313 // GetRfidData() ниже) - раньше это было задачей BoardModel; теперь
314 // BoardModel вообще ничего не знает о шине.
315 auto health() -> bool override {
316 return m_board_model_.is_running() && m_board_model_.is_online();
317 }
318
319 auto get_param(const std::string& param) -> std::optional<int> override {
320 if (param == lacte::bus::K_PARAM_AERATION) {
321 return m_board_model_.get_aeration();
322 }
323 return std::nullopt;
324 }
325
326 auto set_param(const std::string& param, const int value) -> bool override {
327 if (param != lacte::bus::K_PARAM_AERATION) {
328 return false;
329 }
330 return m_board_model_.set_aeration(static_cast<uint8_t>(value));
331 }
332
333 // Отвечает на шинный запрос снапшота напрямую из runtime() - само
334 // отображение см. в определении .cpp. Без отдельной косвенности
335 // безаргументного BuildSnapshot() - это был единственный её вызывающий,
336 // помимо GetRfidData() ниже, которая уже напрямую берёт перегрузку с
337 // BoardData.
338 auto get_snapshot() -> lacte::bus::BoardSnapshot override {
339 return build_snapshot(runtime().current());
340 }
341
342 // Отвечает на шинный запрос данных RFID напрямую из runtime() - само
343 // отображение см. в определении .cpp (собственный doc-комментарий
344 // RfidData объясняет, почему это отдельно от BuildSnapshot()/
345 // GetSnapshot()). Без отдельной косвенности BuildRfidData() - это был
346 // единственный её вызывающий.
347 auto get_rfid_data() -> lacte::bus::RfidData override;
348
349 // Отображает показание board_state (см. собственный doc-комментарий
350 // BoardData) на проводной тип снапшота (перенесено из BoardModel - см.
351 // собственный файловый doc-комментарий BoardModel.h). Чистое
352 // отображение, без собственного ввода-вывода - оба вызывающих
353 // (GetSnapshot() выше и GetRfidData() ниже, которой нужен тот же самый
354 // фильтр по error_flags) сами получают runtime().current() и передают
355 // его сюда, так что вызывающий, у которого уже есть показание, никогда
356 // не вызывает второй, независимый вызов runtime().current() мгновением
357 // позже (два отдельных чтения, гоняющихся с любым параллельным Set() -
358 // именно этот баг раньше здесь и был).
359 auto build_snapshot(const BoardData& board_state) const
360 -> lacte::bus::BoardSnapshot;
361
362 // Каждое поле, которое становится аргументом командной строки,
363 // привязанным через CLI11 (см. вызовы m_app.add_option()/add_flag() в
364 // конструкторе), сгруппировано в одну структуру исключительно для того,
365 // чтобы собственный список членов LacteApp не превратился в восемь
366 // отдельных разрозненных полей - CLI11 привязывается напрямую к каждому
367 // полю здесь, точно так же, как к самостоятельному члену.
368 struct Args {
369 std::string test_mode;
370 std::string flash_path;
371 std::string device_name;
372 std::chrono::milliseconds check_update_interval{};
373 std::string bin_path;
374 bool virtual_mode{false};
375 // Режим UI, выбираемый через --ui: "fb" (framebuffer, по умолчанию),
376 // "console" или "no". Устанавливается из m_context_.config (к этому
377 // моменту уже загруженного — см. комментарий в конструкторе) в теле
378 // конструктора, до того как CLI11 регистрирует опцию.
379 std::string ui_mode;
380 // Минимальная серьёзность, записываемая в лог (0=INFO, 1=WARNING,
381 // 2=ERROR) - по умолчанию берётся из log_min_level конфигурации (см.
382 // AppConfig.hpp), --log-level переопределяет её. Применяется через
383 // m_context_.logger.set_level() в самом начале run_impl(), как только
384 // CLI11 реально разобрал argv (собственный конструктор AppContext уже
385 // применил значение по умолчанию, взятое только из конфигурации, ранее
386 // - до того как какая-либо CLI-опция могла бы его переопределить - см.
387 // AppContext.h).
388 int64_t log_min_level{};
389 };
390
391 Args m_args_;
392
393 // Переопределение только для тестов для разрешения db_path в run_impl()
394 // - см. собственный doc-комментарий SetDbPathForTests(). Пустая строка
395 // (значение по умолчанию) означает "использовать вместо этого kAppDir/
396 // kDbName из m_context_.config", точно как в реальном запуске.
397 std::string m_db_path_override_;
398
399 // Хранилище данных платы — напрямую сгенерированный кодогенератором
400 // AppDb (см. AppDb.hpp). shared_ptr (а не простой член), сконструирован
401 // через конструктор без шины и затем подключён к m_shared_bus_ через
402 // собственный ConnectExternalClient() у m_storage_bus_ (см. run_impl())
403 // - тот подписывается через weak_ptr на этот объект, а не через
404 // "голый" `this` (см. doc-комментарий
405 // SqliteBusAdapter::ConnectExternalClient()), так что порядок
406 // разрушения между m_storage_ и его клиентом не нужно проверять вручную:
407 // сообщение, пришедшее после того как m_storage_ уже уничтожен, просто
408 // ничего не делает. Собственные обработчики on<Field>()/on_unhandled()
409 // у m_runtime_ (регистрируются в run_impl(), сразу после построения
410 // этого объекта) пишут строки, на которые подписана эта БД, в рамках
411 // процесса - без кругового обхода через шину (раньше это делал
412 // DbRowRouter).
413 std::shared_ptr<AppDb> m_storage_;
414 // Обращённая к шине сторона m_storage_ (см. AppDb.hpp - сам AppDb/
415 // ThreadSafeDB вообще ничего не знает о шине) - optional (а не простой
416 // член), поскольку может быть сконструирован только тогда, когда
417 // m_storage_ уже существует, оба строятся вместе в run_impl(). Объявлен
418 // после m_storage_, чтобы разрушаться первым при остановке (прекратить
419 // использовать клиента раньше, чем уничтожается то, на что он указывает
420 // - не требование корректности, учитывая подписку через weak_ptr выше,
421 // просто более разумный порядок).
422 std::optional<AppDbBusAdapter> m_storage_bus_;
423
424 // UI, если он был запущен (--ui и присутствуют его устройства). Работает
425 // в собственном потоке и управляется по RAII — его сброс (при остановке)
426 // останавливает и присоединяет (join) этот поток.
427 std::unique_ptr<lacte::ui::UiApp> m_ui_;
428
429 // std::ostream &cout_;
430 std::mutex m_stop_mx_;
431 std::mutex m_gui_mx_;
432 // std::condition_variable stop_cv_;
456 explicit LacteApp(AppContext& ctx, const std::string& bin_path = "");
457
458 ~LacteApp() override = default;
459
460 void user_signal_handler(int signum) override;
461
462 void try_to_update();
463
464 // Заполняет/обновляет справочную таблицу "dictionary" в m_storage_
465 // (названия статусов + описания ошибок платы), чтобы она всегда была
466 // синхронизирована с текущей сборкой, без каких-либо ручных шагов над БД
467 // — общий механизм сравнения/перезаписи, который для этого используется,
468 // см. в ThreadSafeDB::sync_table() в Libraries/sqlite/sqlite.hpp.
469 // Вызывается один раз, сразу после конструирования m_storage_ (см.
470 // run_impl).
471 void ensure_dictionary_up_to_date() const;
472
473 void ping_test(std::istream& cin, std::ostream& cout);
474};
Схема БД приложения (модели + сгенерированные meta-описания полей/имени таблицы) и AppDb — тонкая обё...
Собственные типы runtime-полей платы и кортеж RuntimeFields, называющий весь набор - и ничего больше.
std::tuple< BoardOnlineStatus, proto::lacte::InfoPacketType, proto::lacte::BoardErrorDiff, proto::lacte::VersionPacketType, proto::lacte::UIDPacketType, proto::lacte::RFIDNumberType, proto::lacte::MagicWord, proto::lacte::LacteId, proto::lacte::LacteSn, proto::lacte::ProductVolume, proto::lacte::ProdDate, proto::lacte::ProductShelfLife, proto::lacte::UsageTime, proto::lacte::McuUid, proto::lacte::MachineSn, proto::lacte::ActivationTime, proto::lacte::MilkCounter, proto::lacte::TimeCounter, SoftVersion, proto::lacte::AerationParam > RuntimeFields
BoardModel - связь с самой платой lacte, и больше ничего: проводной протокол (m_host_proto),...
typename detail::BoardReadingOf< RuntimeFields >::type BoardData
Definition BoardModel.h:69
insitech::database::SqliteBusAdapter< insitech::database::meta::StatMeta, insitech::database::meta::ServiceInfoMeta, insitech::database::meta::ErrorsMeta, insitech::database::meta::DictionaryMeta > AppDbBusAdapter
Definition LacteApp.hpp:32
insitech::runtime::Runtime< RuntimeFields > LacteRuntime
Definition LacteApp.hpp:49
ModelBusClient: собственный клиент Model на шине lacte - создаётся и принадлежит самому Model (см.
Совмещённое состояние системы из ДВУХ независимых каналов связи: online-статуса платы lacte (proto::l...
UiBusClient: собственный клиент UI на шине lacte - создаётся и принадлежит самому UiApp (см.
proto::interface::UartLinuxInterface m_hmi_uart_interface
Definition LacteApp.hpp:94
proto::back_board_proto::HeadFlags last_flags_
Definition LacteApp.hpp:151
BoardModel m_board_model_
Definition LacteApp.hpp:144
void set_db_path_for_tests(std::string path)
Definition LacteApp.hpp:193
void stop()
Definition LacteApp.hpp:211
proto::interface::UartLinuxInterface m_uart_interface
Definition LacteApp.hpp:87
std::shared_ptr< insitech::LoggerBusClient > m_client_
Definition LacteApp.hpp:131
void reset_test_mode_for_tests()
Definition LacteApp.hpp:168
void set_bin_path_for_tests(std::string path)
Definition LacteApp.hpp:179
auto run_impl(std::istream &input=std::cin) -> int
static uint8_t hmi_tx_buffer[K_HMI_TX_BUFFER_SIZE]
Definition LacteApp.hpp:104
static constexpr std::size_t K_HMI_TX_BUFFER_SIZE
Definition LacteApp.hpp:103
std::unique_ptr< proto::lacte::VirtualBoard > m_virtual_board
Definition LacteApp.hpp:148
friend ConsoleApp
Definition LacteApp.hpp:153
auto get_bus() const -> lacte::bus::ModelBusClient *
Шинный клиент, на котором это приложение обслуживает запросы ModelBusHandler, если таковой есть - тот...
Definition LacteApp.hpp:220
proto::back_board_proto::HeadPacketTypes last_packet_
Definition LacteApp.hpp:150
void set_door_signal_lost_for_tests(const bool lost)
Definition LacteApp.hpp:205
proto::back_board_proto::BackBoardProtocol< hmi_tx_buffer, hmi_tx_buffer > m_hmi_protocol_
Definition LacteApp.hpp:107
lacte::SystemStateResolver m_system_state_
Definition LacteApp.hpp:122
auto get_bin_path() const -> std::string
Definition LacteApp.hpp:183
Хранит последний известный HMI-сигнал двери (единственная величина, которую сегодня физически некому ...
Серверный контракт: реализуйте эти методы, и ModelBusClient::ServeModel() будет их обслуживать.
virtual auto health() -> bool=0
Здоров ли бэкенд/диспенсер.
virtual auto get_rfid_data() -> RfidData=0
Формирует сырые данные текущей приложенной RFID-карты (см.
virtual auto get_snapshot() -> BoardSnapshot=0
Формирует текущий снапшот устройства.
Протокольная конечная точка (endpoint) для связи с задней платой (back board) поверх кадров головной ...
constexpr auto K_PARAM_AERATION
Имя параметра бэкенда (и ключ заголовка снапшота) для уровня аэрации.
Definition Envelopes.hpp:43