From bfdf22ba641ac5dbfa9e836b08b303021402e954 Mon Sep 17 00:00:00 2001 From: Petr Polezhaev Date: Sun, 27 Sep 2026 10:53:28 +0300 Subject: [PATCH] =?UTF-8?q?core(M2):=20=D0=BC=D0=B0=D1=88=D0=B8=D0=BD?= =?UTF-8?q?=D0=B0=20=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8=D0=B9?= =?UTF-8?q?=20=D1=81=D0=B5=D1=81=D1=81=D0=B8=D0=B8=20Ayla=20LAN=20+=20mock?= =?UTF-8?q?-=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20+=20=D0=B8=D0=BD=D1=82?= =?UTF-8?q?=D0=B5=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D0=BE=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D0=B5=20=D1=81=D1=86=D0=B5=D0=BD=D0=B0=D1=80=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - session.{hpp,cpp}: state machine (idle/registering/online/recovering/ offline/key_error); httpd-обработчики key_exchange (200/426/412, re-key прозрачно), commands (одна команда, 206/200, envelope, глобальный seq_no), datapoint (unpack -> PropertyEvent / 401+тишина 50с для re-key-восстановления); сессионный поток: local_reg POST?dsn/PUT (local_ip_for), keep-alive, backoff x1.6->60с, 503->offline/NoSlot, activation-timeout->recovering, delete_session с ожиданием выдачи; очередь с coalescing + batch; телеметрия; колбэки из двух потоков с задокументированным контрактом; буферы datapoint-пути в Impl. - platform: local_ip_for (UDP-connect) posix+esp-idf; стек httpd 24576 (переполнение 16КБ поймано gdb на Release). - mock_ac.py: мок-модуль, stdlib-only чистый python AES-256 (свёрстан с pycryptodome); сценарии: 503, no-poll, rekey-every, stale-gap (эмуляция 'вернувшегося' приложения), fail-pushes (битая подпись), garbage-pushes (обрыв блока), break-outbound (исходящий десинк -> модуль ре-кает на local_reg, как probe1-3), push-every, fail-first-ke. - session_runner + test_session_mock.py: 9 сценариев через ctest, включая самосинхронизацию CBC и восстановление после исходящего десинка. - Прибор AP-WC1E: активация <=1с; re-key семантика ИСПРАВЛЕНА по живым тестам: re-key при зазоре local_reg >= ~44-50с (не по возрасту сессии!); при честном keep-alive 15с сессия стабильна без re-key; PROTOCOL/LEGACY/ PLAN обновлены; восстановление = тишина >порога + возврат. - CI: 7/7 x3 (gcc-Rel, gcc-ASan/UBSan, clang); ESP-IDF esp32 build complete. Ревью под-агентом: 2 круга (стек httpd, залипание состояний, dangling cfg, физика десинка) — APPROVED. --- CMakeLists.txt | 2 + core.37208 | Bin 0 -> 9375744 bytes docs/LEGACY_ANALYSIS.md | 15 +- docs/PLAN_CORE_LIBRARY.md | 12 +- docs/PROTOCOL.md | 33 +- src/ayla/httpd.cpp | 2 +- src/ayla/platform/esp-idf/platform.cpp | 29 + src/ayla/platform/platform.hpp | 3 + src/ayla/platform/posix/platform.cpp | 36 +- src/ayla/session.cpp | 909 +++++++++++++++++++++++++ src/ayla/session.hpp | 126 ++++ tests/CMakeLists.txt | 14 + tests/ayla/mock_ac.py | 420 ++++++++++++ tests/ayla/session_runner.cpp | 135 ++++ tests/ayla/test_session_mock.py | 295 ++++++++ 15 files changed, 2005 insertions(+), 26 deletions(-) create mode 100644 core.37208 create mode 100644 src/ayla/session.cpp create mode 100644 src/ayla/session.hpp create mode 100644 tests/ayla/mock_ac.py create mode 100644 tests/ayla/session_runner.cpp create mode 100644 tests/ayla/test_session_mock.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f7166a..f032a97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ if(ESP_PLATFORM) "src/ayla/httpc.cpp" "src/ayla/httpd.cpp" "src/ayla/log.cpp" + "src/ayla/session.cpp" "src/ayla/platform/esp-idf/platform.cpp" INCLUDE_DIRS "include" @@ -79,6 +80,7 @@ else() src/ayla/httpc.cpp src/ayla/httpd.cpp src/ayla/log.cpp + src/ayla/session.cpp src/ayla/platform/posix/platform.cpp ) target_include_directories(fgl-aircon diff --git a/core.37208 b/core.37208 new file mode 100644 index 0000000000000000000000000000000000000000..35e3499002e312f837d8b5791d8645b4b29ca187 GIT binary patch literal 9375744 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 zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5cp4a=s*Af0002U|Fsbk95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n? z4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj! z0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^` zz<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!K zaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB) z95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c z2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*= zfddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede z;J|?c2M!!KaNxj!0|yQqIB?*=fddB)95`^`z<~n?4jede;J|?c2M!!KaNxj!0|yQq zIB?*=fddDycQ5djRfWCy*Eu-}XEI@WW*Bf#s(WJ4U{M1_9j)4(Fkl4KfM~}@Yj{~y zgi+CAwPnu`Y(Q-TRXaXzZ$RuFwf0V}wp`n5!=o}%Yo}IwvG$Q#t+YN$d)w=XkNK~i zy%x?K7y+y8-}Za>gx^_vt+m(M-~Bi{CnpmE1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ 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
  • 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 literal 0 HcmV?d00001 diff --git a/docs/LEGACY_ANALYSIS.md b/docs/LEGACY_ANALYSIS.md index d8b13b6..b5143aa 100644 --- a/docs/LEGACY_ANALYSIS.md +++ b/docs/LEGACY_ANALYSIS.md @@ -30,13 +30,14 @@ ### 2.1. [ГЛАВНАЯ ПРИЧИНА «РАССИНХРОНИЗАЦИИ»] Keep-alive 1200 с вместо 10–15 с `notifier.py:_KEEP_ALIVE_INTERVAL = 1200.0`. APK: 10 с (или `lan.json:keepAlive/3`). -Проверено на приборе: **единственный механизм восстановления после расхождения -CBC-цепочек — принудительный re-key, который модуль делает при получении -`local_reg` для сессии старше ≈44 с. Ответы 400/401 модуль игнорирует.** -Следствие для legacy: любая потерянная пара запрос-ответ/обрыв соединения → -обе стороны «глохнут» на срок до 20 минут (до следующего local_reg). Наблюдаемый -симптом «перестаёт понимать кондиционер» с самопроизвольным восстановлением — -именно это. +Проверено на приборе: **модуль игнорирует 400/401 на свои POST; переkey +происходит при `local_reg` после зазора ≥ ~44–50 с от предыдущего** (при +keep-alive 10–15 с re-key вообще не происходит). Следствие для legacy: любая +потерянная пара запрос-ответ → обе стороны «глохнут» до следующего local_reg +(до 20 минут), который завершится re-key — наблюдаемый симптом «перестаёт +понимать кондиционер, потом сам чинится» — именно это. Корректная стратегия +для новой реализации: при ошибке расшифровки — пауза ~50 с, затем local_reg +(re-key гарантирован). Дополнительно: длинные паузы между local_reg держат сессию «полуживой» (модуль не видит keep-alive, но слот может удерживаться), и конфликт за diff --git a/docs/PLAN_CORE_LIBRARY.md b/docs/PLAN_CORE_LIBRARY.md index 60acc92..8a5d854 100644 --- a/docs/PLAN_CORE_LIBRARY.md +++ b/docs/PLAN_CORE_LIBRARY.md @@ -199,9 +199,13 @@ int32_t fgl_convert_from_input(fgl_template, fgl_prop, int32_t disp, ### 5.2. Keep-alive и re-key * Таймер `keepalive_ms` (default **15000**); по истечении — `PUT local_reg` c `notify=(очередь непуста)`. Каждый `commands.json` перезапускает таймер. -* Re-key (очередной local_reg при возрасте сессии ≥ ~44 с) — штатное - событие: перегенерация шифров/цепочек, сессия не пересоздаётся, начальная - синхронизация не повторяется; seq_no приложения продолжает глобальный счётчик. +* Re-key — событие по инициативе модуля (при зазоре local_reg ≥ ~44–50 с, + [ПРОВЕРЕНО НА ПРИБОРЕ]; при штатном keep-alive НЕ происходит): обработать + как обычный KE (перегенерация шифров/цепочек), сессию не пересоздавать, + начальную синхронизацию не повторять; seq_no приложения продолжает + глобальный счётчик. +* Восстановление при ошибке расшифровки: тишина > порога (50 с по умолчанию) + и возврат — модуль гарантированно ре-кает [ПРОВЕРЕНО НА ПРИБОРЕ]. * Анти-спам: ≤1 local_reg/с; notify=1 — один на пакет команд. ### 5.3. Очередь команд @@ -291,7 +295,7 @@ HA-превью (PLAN_HOME_ASSISTANT §4) и тестами. |---|------------|------------------| | M0 ✅ | Монорепо-каркас: CMake (корень) + IDF-подключение, платслой, лог, CI | Собирается linux+esp-idf; пустой httpd отвечает 404 | | M1 ✅ | `src/ayla`: crypto+envelope, мини-httpd/httpc, jsmn-вендор | Векторы зелёные; httpd-тесты; совместимость с probe_reference.py | -| M2 | `src/ayla`: сессия (установка/активация/keep-alive/re-key/слоты/503/delete) с mock-модулем | Все сценарии mock; на приборе: активация ≤5 с, re-key каждые 45–60 с | +| M2 ✅ | `src/ayla`: сессия (установка/активация/keep-alive/re-key/слоты/503/delete) с mock-модулем | Все сценарии mock; на приборе: активация ≤5 с; семантика re-key: при зазоре local_reg ≥ ~44–50 с (при честном keep-alive 15 с — 0 re-key за 100 с; при 50 с — 3 re-key) | | M3 | `src/aircon`: шаблоны, конверсии+override, публичный API, batch | `tests/aircon` зелёные; на приборе: чтение всех свойств, batch=1 notify | | M4 | fglctl-пример, `tools/fglair-discover` (в т.ч. `--format esphome-secrets`), README библиотеки (сборка IDF/POSIX, тесты) | 24 ч на приборе: 0 рассинхронов; README готов | | M5 | (Опция) `FglHub` N устройств; mDNS-резолвер как опция host-разрешения | Два устройства одновременно | diff --git a/docs/PROTOCOL.md b/docs/PROTOCOL.md index c2a229c..ef2a061 100644 --- a/docs/PROTOCOL.md +++ b/docs/PROTOCOL.md @@ -216,15 +216,21 @@ GET http://:<порт>/local_lan/commands.json один «пустой» опрос `commands.json` — это признак принятой сессии. 2. `local_reg` от endpoint'а с живой сессией **моложе ~40 с** → только keep-alive, без key exchange. -3. `local_reg` от endpoint'а с сессией **старше ~44 с** → модуль принудительно - инициирует новый key exchange (ротация сессионных ключей). Т.е. при штатном - keep-alive каждые 10–15 с ключи ротируются примерно каждые 45–60 с. - `time_1` модуля — тикающий счётчик с шагом ≈10 нс (аптайм); порог, - вероятно, 44 с в этих единицах либо просто 4.4e9 тиков. +3. `local_reg` при **зазоре ≥ ~44–50 с** от предыдущего local_reg → + модуль принудительно инициирует новый key exchange («вернувшееся» + приложение получает свежие ключи). При штатном keep-alive каждые 10–15 с + re-key НЕ происходит — сессия живёт сколь угодно долго (проверено: + 100 с при 15 с keep-alive — 0 re-key; 125 с при 50 с keep-alive — 3 re-key, + оба без потерь). `time_1` модуля — тикающий счётчик с шагом ≈10 нс (аптайм); + порог, вероятно, 4.4e9 тиков (~44 с) от последнего local_reg. 4. **Ответы 401/400 на POST модуля игнорируются**: сессия продолжает работать, - re-key не вызывается. Единственный механизм восстановления после расхождения - CBC-цепочек — принудительный re-key по `local_reg` (п. 3). Поэтому интервал - keep-alive = интервал потенциального «зависания» при десинхроне. + re-key не вызывается. Восстановление после расхождения CBC-цепочек — + намеренная «тишина» приложения на > порога из п. 3 с последующим + `local_reg`: модуль сочтёт приложение вернувшимся и ре-кает. Т.е. стратегия + самовосстановления: при ошибке расшифровки — пауза keep-alive ~50–60 с, + затем возобновить (проверено на приборе). Отдельный случай — бракованная + подпись при живой цепочке (сообщение расшифровано, подпись не сошлась): + цепочка НЕ расходится, следующий push восстанавливает работу без re-key. 5. `delete_session` освобождает слот немедленно; следующий `local_reg` того же endpoint'а создаёт новую сессию. 6. Наблюдавшийся (не воспроизведённый повторно) режим отказа: модуль отвечает @@ -316,11 +322,12 @@ data: {"id":"","ack_status":200,"ack_message":0,"dsn":"..."} * **Потеря CBC-цепочки** (§3.4): модуль не может расшифровать ответ приложения / приложение не может расшифровать push модуля. Ответы 401/400 на POST модуля - **игнорируются** — модуль продолжает слать в «сломанный» канал. Восстановление - происходит только когда очередной `local_reg` (по возрасту ≥ ~44 с или от - нового endpoint'а) вызовет новый key exchange. Следствие: **интервал - keep-alive = максимальное время «мёртвой» сессии при десинхроне** - (10–15 с — незаметно; 1200 с как в legacy-скрипте — 20 минут глухоты). + **игнорируются** — модуль продолжает слать в «сломанный» канал. Восстановление: + приложение замолкает на > ~44–50 с (порог «возврата» из п. 4.4.3) и шлёт + `local_reg` — модуль переkey'ается. Реализация ядра: при ошибке расшифровки + пауза keep-alive ~50 с, затем возобновление. В legacy-скрипте пауза получалась + «бесплатно» из-за keep-alive 1200 с: каждый цикл завершался re-key при + возврате — потому рассинхрон «сам чинился» через ~20 минут. * **Смена lanip_key** (`key_id` не совпал): теоретический путь по APK — 412 + `refreshLanConfig()` из облака. За 5 лет эксплуатации прибора ротации ключа не наблюдалось ни разу; ключ, по-видимому, зашит в модуль, облако лишь хранит diff --git a/src/ayla/httpd.cpp b/src/ayla/httpd.cpp index 1b6506f..d82641a 100644 --- a/src/ayla/httpd.cpp +++ b/src/ayla/httpd.cpp @@ -121,7 +121,7 @@ void parse_target(const char* full_target, HttpRequest* req) { } } -constexpr uint32_t kHttpdThreadStack = 8192; +constexpr uint32_t kHttpdThreadStack = 24576; // commands-путь: envelope+crypto ~10КБ поверх буферов запроса constexpr uint32_t kAcceptPollMs = 100; constexpr uint32_t kClientRxTimeoutMs = 30000; constexpr uint32_t kClientTxTimeoutMs = 10000; diff --git a/src/ayla/platform/esp-idf/platform.cpp b/src/ayla/platform/esp-idf/platform.cpp index bada591..b5e241c 100644 --- a/src/ayla/platform/esp-idf/platform.cpp +++ b/src/ayla/platform/esp-idf/platform.cpp @@ -159,6 +159,35 @@ int tcp_connect(const char* host, uint16_t port, uint32_t timeout_ms) { return fd; } +bool local_ip_for(const char* host, char* out, size_t out_cap) { + struct addrinfo hints {}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + struct addrinfo* list = nullptr; + if (lwip_getaddrinfo(host, "80", &hints, &list) != 0 || list == nullptr) { + return false; + } + int fd = lwip_socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + lwip_freeaddrinfo(list); + return false; + } + bool ok = lwip_connect(fd, list->ai_addr, list->ai_addrlen) == 0; + struct sockaddr_in local {}; + socklen_t slen = sizeof(local); + if (ok && lwip_getsockname(fd, reinterpret_cast(&local), + &slen) == 0) { + const char* s = inet_ntop(AF_INET, &local.sin_addr, out, + static_cast(out_cap)); + ok = s != nullptr; + } else { + ok = false; + } + lwip_freeaddrinfo(list); + lwip_close(fd); + return ok; +} + long tcp_send(int fd, const void* buf, size_t len) { const uint8_t* p = static_cast(buf); size_t done = 0; diff --git a/src/ayla/platform/platform.hpp b/src/ayla/platform/platform.hpp index cab4e7c..49d147a 100644 --- a/src/ayla/platform/platform.hpp +++ b/src/ayla/platform/platform.hpp @@ -43,6 +43,9 @@ uint16_t tcp_local_port(int fd); int tcp_accept(int listen_fd, uint32_t* peer_ip, uint16_t* peer_port); // Подключается к host:port (host — DNS-имя или dotted-quad) с таймаутом. int tcp_connect(const char* host, uint16_t port, uint32_t timeout_ms); +// Локальный IP-адрес (dotted) интерфейса, которым достигается host +// (без реального трафика: UDP connect). Для local_reg. +bool local_ip_for(const char* host, char* out, size_t out_cap); long tcp_send(int fd, const void* buf, size_t len); // >0 / -1 // Блокирующее чтение; 0 — EOF, -1 — ошибка/таймаут. long tcp_recv(int fd, void* buf, size_t len); diff --git a/src/ayla/platform/posix/platform.cpp b/src/ayla/platform/posix/platform.cpp index bca64ba..41107aa 100644 --- a/src/ayla/platform/posix/platform.cpp +++ b/src/ayla/platform/posix/platform.cpp @@ -60,7 +60,12 @@ bool thread_create(void (*fn)(void*), void* ctx, const char* name, pthread_t tid; pthread_attr_t attr; pthread_attr_init(&attr); - if (stack_bytes > 0) pthread_attr_setstacksize(&attr, stack_bytes); + if (stack_bytes > 0) { + if (pthread_attr_setstacksize(&attr, stack_bytes) != 0) { + // glibc отвергает < PTHREAD_STACK_MIN; остаётся дефолт (больше — не меньше) + // логируем только: ядро запрашивает >= PTHREAD_STACK_MIN. + } + } int rc = pthread_create(&tid, &attr, thread_trampoline, start); pthread_attr_destroy(&attr); if (rc != 0) { @@ -168,6 +173,35 @@ int tcp_connect(const char* host, uint16_t port, uint32_t timeout_ms) { return fd; } +bool local_ip_for(const char* host, char* out, size_t out_cap) { + struct addrinfo hints {}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + struct addrinfo* list = nullptr; + if (::getaddrinfo(host, "80", &hints, &list) != 0 || list == nullptr) { + return false; + } + int fd = ::socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + ::freeaddrinfo(list); + return false; + } + bool ok = ::connect(fd, list->ai_addr, list->ai_addrlen) == 0; + struct sockaddr_in local {}; + socklen_t slen = sizeof(local); + if (ok && ::getsockname(fd, reinterpret_cast(&local), + &slen) == 0) { + const char* s = inet_ntop(AF_INET, &local.sin_addr, out, + static_cast(out_cap)); + ok = s != nullptr; + } else { + ok = false; + } + ::freeaddrinfo(list); + ::close(fd); + return ok; +} + long tcp_send(int fd, const void* buf, size_t len) { const uint8_t* p = static_cast(buf); size_t done = 0; diff --git a/src/ayla/session.cpp b/src/ayla/session.cpp new file mode 100644 index 0000000..cfd2c3b --- /dev/null +++ b/src/ayla/session.cpp @@ -0,0 +1,909 @@ +#include "ayla/session.hpp" + +#include +#include +#include +#include +#include + +#include "ayla/envelope.hpp" +#include "ayla/httpc.hpp" +#include "ayla/json.hpp" +#include "ayla/log.hpp" +#include "ayla/platform/platform.hpp" + +namespace fgl::ayla { + +namespace { + +constexpr uint32_t kLoopTickMs = 20; +constexpr size_t kMaxName = 40; +constexpr size_t kMaxQueue = 32; + +struct Command { + uint8_t type; // 1=GET, 2=SET, 3=DELETE + char name[kMaxName]; + int64_t value; + char base_type[10]; + int cmd_id; +}; + +bool gen_random_token(char* out, size_t len) { + static const char kAlpha[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + uint8_t rnd[32]; + size_t need = len <= sizeof(rnd) ? len : sizeof(rnd); + if (!plat::random(rnd, need)) return false; + for (size_t i = 0; i < len; i++) { + out[i] = kAlpha[rnd[i % need] % 62]; + } + out[len] = '\0'; + return true; +} + +// "cmd_id=5&status=200" -> значения; отсутствующие остаются прежними. +void parse_query(const char* query, int* cmd_id, int* status) { + const char* p = query; + while (*p != '\0') { + const char* eq = strchr(p, '='); + const char* amp = strchr(p, '&'); + const char* end = (amp != nullptr) ? amp : p + strlen(p); + if (eq != nullptr && eq < end) { + size_t klen = static_cast(eq - p); + int v = atoi(eq + 1); + if (klen == 6 && strncmp(p, "cmd_id", 6) == 0) *cmd_id = v; + if (klen == 6 && strncmp(p, "status", 6) == 0) *status = v; + } + if (amp == nullptr) break; + p = amp + 1; + } +} + +} // namespace + +struct Session::Impl { + SessionConfig cfg{}; + SessionTimings timings{}; + SessionCallbacks cbs{}; + + char host[64] = {}; + char dsn[40] = {}; + char lanip_key[64] = {}; + + // ---- httpd-поток (используется только из httpd-потока) ---- + HttpServer httpd; + SessionCrypto crypto; + bool crypto_ready = false; + int64_t out_seq = 0; + char ke_random2[17] = {}; + // Рабочие буферы datapoint-пути (httpd однопоточен; вынесены из стека — + // экономия ~7КБ стека потока httpd). + char dp_body[kHttpdMaxBody + 1]; + char dp_enc_b64[kEnvelopeMaxB64]; + char dp_sign_b64[96]; + char dp_plain[kEnvelopeMaxPlain]; + char dp_data[kEnvelopeMaxPlain]; + + // ---- разделяемое ---- + std::mutex queue_mu; + Command queue[kMaxQueue] = {}; + uint8_t queue_len = 0; + int next_cmd_id = 1; + bool batch_open = false; + Command batch[kMaxQueue] = {}; + uint8_t batch_len = 0; + + std::atomic state{static_cast(SessionState::kIdle)}; + std::atomic last_error{static_cast(SessionError::kNone)}; + std::atomic ke_time_ms{0}; // время ответа на KE (0 — не было) + std::atomic last_local_reg_ms{0}; + std::atomic quiet_until_ms{0}; // пауза local_reg (восстановление) + std::atomic retry_at_ms{0}; + std::atomic had_poll_since_ke{false}; + std::atomic ever_active{false}; // был online хотя бы раз + std::atomic want_notify{false}; // после batch commit / перехода online + std::atomic delete_pending{false}; + std::atomic delete_served{false}; + std::atomic decrypt_failed{false}; + std::atomic running{false}; + + std::atomic rekeys{0}; + std::atomic pushes_ok{0}; + std::atomic pushes_bad{0}; + std::atomic cmds_served{0}; + + plat::ThreadId thread = nullptr; + uint8_t backoff_attempts = 0; + uint32_t backoff_ms = 0; + bool reg_ok = false; // последний local_reg принят (202/200) + + uint16_t listen_port_actual = 0; + + // ---------- helpers (вызывается из обоих потоков) ---------- + void set_state(SessionState st, SessionError err) { + uint8_t prev = state.exchange(static_cast(st), + std::memory_order_acq_rel); + last_error.store(static_cast(err), std::memory_order_release); + if (prev != static_cast(st) && cbs.on_state != nullptr) { + cbs.on_state(cbs.ctx, st, err); // без дублирования одинаковых состояний + } + } + + // ---------- очередь (mutex) ---------- + bool enqueue_locked(const Command& cmd) { + // coalescing: SET замещает незабранный SET того же свойства; + // GET-дубликат отбрасывается; DELETE — единственный. + if (cmd.type == 3) { + for (uint8_t i = 0; i < queue_len; i++) { + if (queue[i].type == 3) return true; + } + } else { + for (uint8_t i = 0; i < queue_len; i++) { + if (queue[i].type == cmd.type && + strncmp(queue[i].name, cmd.name, kMaxName) == 0) { + if (cmd.type == 2) { + queue[i].value = cmd.value; // замещаем + return true; + } + if (cmd.type == 1) return true; // дубликат GET + } + } + } + if (queue_len >= cfg.max_queue || queue_len >= kMaxQueue) return false; + queue[queue_len++] = cmd; + return true; + } + + bool submit(Command cmd) { + std::lock_guard lk(queue_mu); + bool ok; + if (batch_open && cmd.type != 3) { + if (batch_len >= kMaxQueue) return false; + // coalescing внутри batch + for (uint8_t i = 0; i < batch_len; i++) { + if (batch[i].type == cmd.type && + strncmp(batch[i].name, cmd.name, kMaxName) == 0) { + if (cmd.type == 2) { + batch[i].value = cmd.value; + return true; + } + if (cmd.type == 1) return true; + } + } + batch[batch_len++] = cmd; + ok = true; + } else { + ok = enqueue_locked(cmd); + } + return ok; + } + + // ---------- httpd-обработчики (httpd-поток) ---------- + static bool http_handler(const HttpRequest& req, HttpResponse& resp, + void* ctx); + + void handle_key_exchange(const HttpRequest& req, HttpResponse& resp); + void handle_commands(HttpResponse& resp); + void handle_datapoint(const HttpRequest& req, HttpResponse& resp); + + void build_get_payload(char* out, size_t out_cap, const Command& c, + int* seq_out); + void build_set_payload(char* out, size_t out_cap, const Command& c); + + // ---------- session-поток ---------- + static void session_thread_trampoline(void* ctx) { + static_cast(ctx)->session_loop(); + } + void session_loop(); + bool send_local_reg(bool notify, bool first); +}; + +// --------------------------------------------------------------------------- +// httpd +// --------------------------------------------------------------------------- + +bool Session::Impl::http_handler(const HttpRequest& req, HttpResponse& resp, + void* ctx) { + auto* impl = static_cast(ctx); + if (strcmp(req.method, "POST") == 0) { + if (strcmp(req.target, "/local_lan/key_exchange.json") == 0) { + impl->handle_key_exchange(req, resp); + return true; + } + if (strcmp(req.target, "/local_lan/property/datapoint.json") == 0) { + impl->handle_datapoint(req, resp); + return true; + } + if (strcmp(req.target, "/local_lan/property/datapoint/ack.json") == 0 || + strcmp(req.target, "/local_lan/node/property/datapoint.json") == 0 || + strcmp(req.target, "/local_lan/node/property/datapoint/ack.json") == 0) { + FGL_LOGD("session: ack/node datapoint (пусто ok)"); + resp.status = 200; + return true; + } + } else if (strcmp(req.method, "GET") == 0) { + if (strcmp(req.target, "/local_lan/commands.json") == 0) { + if (!impl->crypto_ready) { + resp.status = 401; + return true; + } + impl->handle_commands(resp); + return true; + } + } + resp.status = 404; + return true; +} + +void Session::Impl::handle_key_exchange(const HttpRequest& req, + HttpResponse& resp) { + // Тело: {"key_exchange":{"ver":1,"proto":1,"key_id":N,"random_1":..,"time_1":N,"sec":""}} + char body[kHttpdMaxBody + 1]; + size_t n = req.body_len < kHttpdMaxBody ? req.body_len : kHttpdMaxBody; + memcpy(body, req.body, n); + body[n] = '\0'; + + json::Doc doc; + if (!doc.parse(body)) { + resp.status = 400; + return; + } + int64_t ver = 0, proto = 0, key_id = 0, time_1 = 0; + char random_1[32] = {}, sec[8] = {}; + bool have_r1 = doc.get_string("random_1", random_1, sizeof(random_1)); + bool have_t1 = doc.get_int("time_1", &time_1); + bool have_sec = doc.get_string("sec", sec, sizeof(sec)); + bool have_ver = doc.get_int("ver", &ver); + bool have_proto = doc.get_int("proto", &proto); + bool have_kid = doc.get_int("key_id", &key_id); + if (!have_r1 || !have_t1 || !have_ver || !have_proto || !have_kid) { + FGL_LOGW("session: key_exchange неполный"); + resp.status = 400; + return; + } + if (ver != 1 || proto != 1 || (have_sec && sec[0] != '\0')) { + FGL_LOGW("session: key_exchange ver/proto/sec не поддержаны"); + set_state(SessionState::kKeyError, SessionError::kBadKeyExchange); + resp.status = 426; + return; + } + { + // sec длиннее буфера -> get_string=false, но поле есть: setup-режим + // не поддерживаем — тоже 426 (PROTOCOL §3.1). + const char* sec_start = nullptr; + size_t sec_len = 0; + jsmntype_t sec_type; + if (doc.find("sec", &sec_start, &sec_len, &sec_type) && sec_type == JSMN_STRING && + sec_len >= sizeof(sec)) { + set_state(SessionState::kKeyError, SessionError::kBadKeyExchange); + resp.status = 426; + return; + } + } + if (static_cast(key_id) != cfg.lanip_key_id) { + FGL_LOGE("session: key_id %lld != %u — ротация ключа?", + static_cast(key_id), + static_cast(cfg.lanip_key_id)); + set_state(SessionState::kKeyError, SessionError::kKeyMismatch); + resp.status = 412; + return; + } + + if (!gen_random_token(ke_random2, 16)) { + resp.status = 500; + return; + } + int64_t time_2 = static_cast(plat::now_ms()) * 1000000ll; + crypto_ready = crypto.init(lanip_key, random_1, ke_random2, time_1, + time_2); // цепочки сбрасываются тут же + if (!crypto_ready) { + resp.status = 500; + return; + } + had_poll_since_ke.store(false, std::memory_order_release); + ke_time_ms.store(plat::now_ms(), std::memory_order_release); + bool rekey = ever_active.load(std::memory_order_acquire); + rekeys.fetch_add(1, std::memory_order_relaxed); + if (!rekey && state.load(std::memory_order_acquire) != + static_cast(SessionState::kRegistering)) { + set_state(SessionState::kRegistering, SessionError::kNone); + } + FGL_LOGI("session: key exchange #%u (rekey=%d)", + static_cast(rekeys.load(std::memory_order_relaxed)), rekey); + + static thread_local char out[128]; + json::Writer w(out, sizeof(out)); + w.begin_object(); + w.key("random_2"); + w.string(ke_random2); + w.key("time_2"); + w.integer(time_2); + w.end_object(); + resp.status = 200; + resp.body = reinterpret_cast(out); + resp.body_len = strlen(out); + // буфер out живёт до конца ответа (send_response в handle_connection + // выполняется синхронно в том же кадре стека httpd-потока). +} + +void Session::Impl::build_get_payload(char* out, size_t out_cap, + const Command& c, int* seq_out) { + (void)seq_out; + json::Writer w(out, out_cap); + w.begin_object(); + w.key("cmds"); + w.begin_array(); + w.begin_object(); + w.key("cmd"); + w.begin_object(); + w.key("cmd_id"); + w.integer(c.cmd_id); + w.key("method"); + w.string("GET"); + w.key("resource"); + char res[80]; + snprintf(res, sizeof(res), "property.json?name=%s", c.name); + w.string(res); + w.key("data"); + w.string(""); + w.key("uri"); + w.string("/local_lan/property/datapoint.json"); + w.end_object(); + w.end_object(); + w.end_array(); + w.end_object(); +} + +void Session::Impl::build_set_payload(char* out, size_t out_cap, + const Command& c) { + json::Writer w(out, out_cap); + w.begin_object(); + w.key("properties"); + w.begin_array(); + w.begin_object(); + w.key("property"); + w.begin_object(); + w.key("base_type"); + w.string(c.base_type); + w.key("name"); + w.string(c.name); + w.key("value"); + if (strcmp(c.base_type, "boolean") == 0) { + w.boolean(c.value != 0); + } else { + w.integer(c.value); + } + w.key("id"); + char id[9]; + gen_random_token(id, 8); + w.string(id); + w.end_object(); + w.end_object(); + w.end_array(); + w.end_object(); +} + +void Session::Impl::handle_commands(HttpResponse& resp) { + Command head{}; + bool have = false; + uint8_t remaining = 0; + { + std::lock_guard lk(queue_mu); + if (queue_len > 0) { + head = queue[0]; + have = true; + queue_len--; + memmove(queue, queue + 1, queue_len * sizeof(Command)); + remaining = queue_len; + } + } + char payload[512]; + if (!have) { + payload[0] = '{'; + payload[1] = '}'; + payload[2] = '\0'; + } else if (head.type == 1) { + build_get_payload(payload, sizeof(payload), head, nullptr); + } else if (head.type == 2) { + build_set_payload(payload, sizeof(payload), head); + } else { // DELETE session + json::Writer w(payload, sizeof(payload)); + w.begin_object(); + w.key("cmds"); + w.begin_array(); + w.begin_object(); + w.key("cmd"); + w.begin_object(); + w.key("cmd_id"); + w.integer(0); + w.key("method"); + w.string("DELETE"); + w.key("resource"); + w.string("local_reg.json"); + w.key("data"); + w.string("delete_session"); + w.key("uri"); + w.string("/local_lan"); + w.end_object(); + w.end_object(); + w.end_array(); + w.end_object(); + delete_served.store(true, std::memory_order_release); + delete_pending.store(false, std::memory_order_release); + FGL_LOGI("session: delete_session выдан модулю"); + } + + static thread_local char envelope[kEnvelopeMaxB64]; + int64_t seq = out_seq++; + if (!envelope_pack(crypto.app, seq, payload, envelope, sizeof(envelope))) { + resp.status = 500; + return; + } + cmds_served.fetch_add(1, std::memory_order_relaxed); + had_poll_since_ke.store(true, std::memory_order_release); + uint8_t st8 = state.load(std::memory_order_acquire); + if (st8 == static_cast(SessionState::kRegistering) || + st8 == static_cast(SessionState::kRecovering) || + st8 == static_cast(SessionState::kOffline)) { + // Опрос команд = сессия жива (в т.ч. после re-key при recovering/offline). + ever_active.store(true, std::memory_order_release); + decrypt_failed.store(false, std::memory_order_release); + set_state(SessionState::kOnline, SessionError::kNone); + want_notify.store(true, std::memory_order_release); + } + resp.status = remaining > 0 ? 206 : 200; + resp.body = reinterpret_cast(envelope); + resp.body_len = strlen(envelope); +} + +void Session::Impl::handle_datapoint(const HttpRequest& req, + HttpResponse& resp) { + size_t n = req.body_len < kHttpdMaxBody ? req.body_len : kHttpdMaxBody; + memcpy(dp_body, req.body, n); + dp_body[n] = '\0'; + json::Doc wrap; + if (!wrap.parse(dp_body)) { + resp.status = 400; + return; + } + if (!wrap.get_string("enc", dp_enc_b64, sizeof(dp_enc_b64)) || + !wrap.get_string("sign", dp_sign_b64, sizeof(dp_sign_b64))) { + resp.status = 400; + return; + } + + int64_t seq_no = -1; + if (!envelope_unpack(crypto.dev, dp_enc_b64, dp_sign_b64, dp_plain, + sizeof(dp_plain), &seq_no)) { + pushes_bad.fetch_add(1, std::memory_order_relaxed); + FGL_LOGW("session: push не расшифрован/подпись (401); пауза и re-key"); + if (state.load(std::memory_order_acquire) == + static_cast(SessionState::kOnline)) { + decrypt_failed.store(true, std::memory_order_release); + set_state(SessionState::kRecovering, SessionError::kDecryptFailed); + // Стратегия восстановления (PROTOCOL §4.4): замолчать на > порога + // «возврата» (~44-50с) — следующий local_reg заставит модуль re-key. + quiet_until_ms.store(plat::now_ms() + timings.recovering_quiet_ms, + std::memory_order_release); + } + resp.status = 401; + return; + } + + // Восстановление после 401 с живой цепочкой (бракованная подпись): + // сообщение расшифровано — отменяем тишину и возвращаем online. + if (decrypt_failed.exchange(false, std::memory_order_acq_rel)) { + quiet_until_ms.store(0, std::memory_order_release); + set_state(SessionState::kOnline, SessionError::kNone); + FGL_LOGI("session: цепочка восстановлена (успешный push после 401)"); + } + + // {"seq_no":N,"data":{"name":..,"value":..}} + json::Doc top; + if (!top.parse(dp_plain)) { + pushes_ok.fetch_add(1, std::memory_order_relaxed); + resp.status = 200; + return; + } + const char* data_start = nullptr; + size_t data_len = 0; + jsmntype_t data_type; + PropertyEvent ev{}; + ev.seq_no = seq_no; + if (top.find("data", &data_start, &data_len, &data_type) && + data_type == JSMN_OBJECT && data_len < sizeof(dp_data)) { + memcpy(dp_data, data_start, data_len); + dp_data[data_len] = '\0'; + { + json::Doc data_doc; + if (data_doc.parse(dp_data)) { + char name[kMaxName]; + if (data_doc.get_string("name", name, sizeof(name))) { + snprintf(ev.name, sizeof(ev.name), "%s", name); + int64_t iv = 0; + bool bv = false; + const char* sv = nullptr; + size_t sv_len = 0; + jsmntype_t vt; + if (data_doc.get_int("value", &iv)) { + ev.is_int = true; + ev.int_value = iv; + } else if (data_doc.get_bool("value", &bv)) { + ev.is_bool = true; + ev.bool_value = bv; + } else if (data_doc.find("value", &sv, &sv_len, &vt) && + vt == JSMN_STRING && sv_len + 1 <= sizeof(ev.str_value)) { + memcpy(ev.str_value, sv, sv_len); + ev.str_value[sv_len] = '\0'; + } + int cmd_id = -1, status = 0; + parse_query(req.query, &cmd_id, &status); + ev.cmd_id = cmd_id; + ev.status = status; + if (cbs.on_property != nullptr) { + cbs.on_property(cbs.ctx, ev); + } + } + } + } + } + pushes_ok.fetch_add(1, std::memory_order_relaxed); + resp.status = 200; +} + +// --------------------------------------------------------------------------- +// session-поток: local_reg / keep-alive / backoff / таймауты +// --------------------------------------------------------------------------- + +bool Session::Impl::send_local_reg(bool notify, bool first) { + char path[96]; + if (first) { + snprintf(path, sizeof(path), "/local_reg.json?dsn=%s", dsn); + } else { + snprintf(path, sizeof(path), "/local_reg.json"); + } + char local_ip[24]; + if (!plat::local_ip_for(host, local_ip, sizeof(local_ip))) { + FGL_LOGW("session: local_ip_for(%s) failed", host); + return false; + } + char body[160]; + json::Writer w(body, sizeof(body)); + w.begin_object(); + w.key("local_reg"); + w.begin_object(); + w.key("ip"); + w.string(local_ip); + w.key("notify"); + w.boolean(notify); + w.key("port"); + w.integer(listen_port_actual); + w.key("uri"); + w.string("/local_lan"); + w.end_object(); + w.end_object(); + if (!w.ok()) return false; + + HttpcRequest req; + req.method = first ? "POST" : "PUT"; + req.host = host; + req.port = cfg.device_port; + req.path = path; + req.body = reinterpret_cast(body); + req.body_len = strlen(body); + req.timeout_ms = 5000; + HttpcResponse resp; + if (!httpc_perform(req, &resp)) { + return false; + } + last_local_reg_ms.store(plat::now_ms(), std::memory_order_release); + if (resp.status == 503) { + set_state(SessionState::kOffline, SessionError::kNoSlot); + retry_at_ms.store(plat::now_ms() + timings.no_slot_retry_ms, + std::memory_order_release); + FGL_LOGW("session: local_reg -> 503 (нет слотов)"); + backoff_attempts = 0; + backoff_ms = 0; + reg_ok = false; // сессия не активировалась — следующий local_reg POST?dsn + return true; // транспорт ок — это протокольный ответ + } + if (resp.status != 202 && resp.status != 200) { + FGL_LOGW("session: local_reg -> %d", resp.status); + return false; + } + if (state.load(std::memory_order_acquire) == + static_cast(SessionState::kIdle)) { + set_state(SessionState::kRegistering, SessionError::kNone); + } + backoff_attempts = 0; + backoff_ms = 0; + reg_ok = true; + return true; +} + +void Session::Impl::session_loop() { + bool first_reg = true; + while (running.load(std::memory_order_acquire)) { + uint64_t now = plat::now_ms(); + SessionState st = + static_cast(state.load(std::memory_order_acquire)); + + // kKeyError — устойчивая ошибка: конфиг менять вручную, не дёргаем модуль. + if (st == SessionState::kKeyError) { + plat::sleep_ms(kLoopTickMs); + continue; + } + + // Тишина (восстановление после «KE без poll»). + if (now < quiet_until_ms.load(std::memory_order_acquire)) { + plat::sleep_ms(kLoopTickMs); + continue; + } + + // Активация: KE отвечен, но опроса нет. + uint64_t ke_time = ke_time_ms.load(std::memory_order_acquire); + if (st == SessionState::kRegistering && ke_time != 0 && + !had_poll_since_ke.load(std::memory_order_acquire) && + now - ke_time > timings.activation_timeout_ms) { + FGL_LOGW("session: активация не наступила (KE без poll) — пауза %ums", + static_cast(timings.recovering_quiet_ms)); + ke_time_ms.store(0, std::memory_order_release); + quiet_until_ms.store(now + timings.recovering_quiet_ms, + std::memory_order_release); + set_state(SessionState::kRecovering, SessionError::kActivationTimeout); + continue; + } + + // Backoff / отложенный повтор. + if (now < retry_at_ms.load(std::memory_order_acquire)) { + plat::sleep_ms(kLoopTickMs); + continue; + } + + // local_reg: по keep-alive, по notify (batch/online) или первичный. + bool queue_nonempty; + { + std::lock_guard lk(queue_mu); + queue_nonempty = queue_len > 0; + } + bool notify = queue_nonempty || want_notify.exchange(false, + std::memory_order_acq_rel); + uint64_t last_reg = last_local_reg_ms.load(std::memory_order_acquire); + bool due = notify || last_reg == 0 || + now - last_reg >= timings.keepalive_ms; + if (!due) { + plat::sleep_ms(kLoopTickMs); + continue; + } + + bool first = first_reg || !reg_ok; + if (!send_local_reg(queue_nonempty, first)) { + // Транспортная ошибка: backoff. + backoff_ms = backoff_ms == 0 ? timings.backoff_base_ms + : (backoff_ms * 8) / 5; // x1.6 + if (backoff_ms > timings.backoff_max_ms) { + backoff_ms = timings.backoff_max_ms; + } + backoff_attempts++; + if (backoff_attempts >= timings.backoff_attempts) { + set_state(SessionState::kOffline, SessionError::kUnreachable); + retry_at_ms.store(now + timings.backoff_max_ms, + std::memory_order_release); + backoff_attempts = 0; + backoff_ms = 0; + } else { + retry_at_ms.store(now + backoff_ms, std::memory_order_release); + } + continue; + } + if (reg_ok) first_reg = false; + } +} + +// --------------------------------------------------------------------------- +// Session (публичный класс) +// --------------------------------------------------------------------------- + +Session* Session::create(const SessionConfig& cfg, const SessionCallbacks& cbs) { + if (cfg.host == nullptr || cfg.dsn == nullptr || cfg.lanip_key == nullptr) { + return nullptr; + } + if (strlen(cfg.lanip_key) >= sizeof(Impl::lanip_key) || + strlen(cfg.dsn) >= sizeof(Impl::dsn) || + strlen(cfg.host) >= sizeof(Impl::host)) { + return nullptr; // не помещается во внутренние копии + } + auto* impl = new (std::nothrow) Impl(); + if (impl == nullptr) return nullptr; + impl->cfg = cfg; + impl->cbs = cbs; + if (impl->cfg.max_queue == 0) impl->cfg.max_queue = 16; + if (impl->cfg.keepalive_ms == 0) impl->cfg.keepalive_ms = 15000; + impl->timings.keepalive_ms = impl->cfg.keepalive_ms; + snprintf(impl->host, sizeof(impl->host), "%s", cfg.host); + snprintf(impl->dsn, sizeof(impl->dsn), "%s", cfg.dsn); + snprintf(impl->lanip_key, sizeof(impl->lanip_key), "%s", cfg.lanip_key); + auto* s = new (std::nothrow) Session(cfg, cbs); + if (s == nullptr) { + delete impl; + return nullptr; + } + s->impl_ = impl; + return s; +} + +Session::Session(const SessionConfig&, const SessionCallbacks&) : impl_(nullptr) {} + +SessionError Session::last_error() const { + if (impl_ == nullptr) return SessionError::kNone; + return static_cast(impl_->last_error.load(std::memory_order_acquire)); +} + +SessionState Session::state() const { + if (impl_ == nullptr) return SessionState::kIdle; + return static_cast(impl_->state.load(std::memory_order_acquire)); +} + +uint16_t Session::listen_port() const { + return impl_ != nullptr ? impl_->listen_port_actual : 0; +} + +uint32_t Session::rekey_count() const { + return impl_ != nullptr ? impl_->rekeys.load(std::memory_order_relaxed) : 0; +} +uint32_t Session::pushes_ok() const { + return impl_ != nullptr ? impl_->pushes_ok.load(std::memory_order_relaxed) : 0; +} +uint32_t Session::pushes_bad() const { + return impl_ != nullptr ? impl_->pushes_bad.load(std::memory_order_relaxed) : 0; +} +uint32_t Session::commands_served() const { + return impl_ != nullptr ? impl_->cmds_served.load(std::memory_order_relaxed) : 0; +} +bool Session::had_activity() const { + return impl_ != nullptr && + impl_->had_poll_since_ke.load(std::memory_order_acquire); +} +Session::~Session() { + stop(); + delete impl_; +} + +bool Session::start() { + if (impl_ == nullptr || impl_->running.load(std::memory_order_acquire)) { + return false; + } + if (!impl_->httpd.start(impl_->cfg.listen_port, Impl::http_handler, impl_, + "fgl_session")) { + return false; + } + impl_->listen_port_actual = impl_->httpd.port(); + impl_->running.store(true, std::memory_order_release); + if (!plat::thread_create(Impl::session_thread_trampoline, impl_, + "fgl_sess", 8192, &impl_->thread)) { + impl_->running.store(false, std::memory_order_release); + impl_->httpd.stop(); + return false; + } + return true; +} + +void Session::stop() { + if (impl_ == nullptr || !impl_->running.load(std::memory_order_acquire)) { + return; + } + // Штатное завершение: DELETE-команда + notify local_reg. Сессионный поток + // ещё работает и доставит notify; ждём выдачи команды модулю. + if (impl_->state.load(std::memory_order_acquire) != + static_cast(SessionState::kKeyError)) { + delete_session(); + uint64_t deadline = plat::now_ms() + impl_->timings.delete_wait_ms; + while (plat::now_ms() < deadline && + !impl_->delete_served.load(std::memory_order_acquire)) { + plat::sleep_ms(10); + } + } + impl_->running.store(false, std::memory_order_release); + if (impl_->thread != nullptr) { + plat::thread_join(impl_->thread); + impl_->thread = nullptr; + } + impl_->httpd.stop(); + // Сброс для возможного рестарта. + impl_->delete_served.store(false, std::memory_order_release); + impl_->delete_pending.store(false, std::memory_order_release); + impl_->quiet_until_ms.store(0, std::memory_order_release); + impl_->retry_at_ms.store(0, std::memory_order_release); + impl_->ke_time_ms.store(0, std::memory_order_release); + impl_->set_state(SessionState::kIdle, SessionError::kNone); +} + +bool Session::get_property(const char* name) { + if (impl_ == nullptr || name == nullptr || strlen(name) >= kMaxName) { + return false; + } + Command cmd{}; + cmd.type = 1; + snprintf(cmd.name, sizeof(cmd.name), "%s", name); + { + std::lock_guard lk(impl_->queue_mu); + cmd.cmd_id = impl_->next_cmd_id++; + } + if (!impl_->submit(cmd)) return false; + impl_->want_notify.store(true, std::memory_order_release); + return true; +} + +bool Session::set_property(const char* name, int64_t value, + const char* base_type) { + if (impl_ == nullptr || name == nullptr || strlen(name) >= kMaxName) { + return false; + } + Command cmd{}; + cmd.type = 2; + snprintf(cmd.name, sizeof(cmd.name), "%s", name); + cmd.value = value; + snprintf(cmd.base_type, sizeof(cmd.base_type), "%s", base_type); + { + std::lock_guard lk(impl_->queue_mu); + cmd.cmd_id = impl_->next_cmd_id++; + } + if (!impl_->submit(cmd)) return false; + impl_->want_notify.store(true, std::memory_order_release); + return true; +} + +void Session::set_timings_for_test(const SessionTimings& t) { + if (impl_ == nullptr) return; + SessionTimings tmp = t; + if (tmp.keepalive_ms < 100) tmp.keepalive_ms = 100; // анти-спам + impl_->timings = tmp; +} + +bool Session::begin_batch() { + std::lock_guard lk(impl_->queue_mu); + if (impl_->batch_open) return false; + impl_->batch_open = true; + impl_->batch_len = 0; + return true; +} + +bool Session::commit_batch() { + { + std::lock_guard lk(impl_->queue_mu); + if (!impl_->batch_open) return false; + bool ok = true; + for (uint8_t i = 0; i < impl_->batch_len; i++) { + if (!impl_->enqueue_locked(impl_->batch[i])) ok = false; + } + impl_->batch_open = false; + impl_->batch_len = 0; + if (!ok) return false; + } + impl_->want_notify.store(true, std::memory_order_release); + return true; +} + +bool Session::abort_batch() { + std::lock_guard lk(impl_->queue_mu); + if (!impl_->batch_open) return false; + impl_->batch_open = false; + impl_->batch_len = 0; + return true; +} + +bool Session::delete_session() { + if (impl_ == nullptr) return false; + Command cmd{}; + cmd.type = 3; + snprintf(cmd.name, sizeof(cmd.name), "local_reg.json"); + { + std::lock_guard lk(impl_->queue_mu); + if (!impl_->enqueue_locked(cmd)) return false; + } + impl_->delete_pending.store(true, std::memory_order_release); + impl_->want_notify.store(true, std::memory_order_release); + return true; +} + +} // namespace fgl::ayla diff --git a/src/ayla/session.hpp b/src/ayla/session.hpp new file mode 100644 index 0000000..9565741 --- /dev/null +++ b/src/ayla/session.hpp @@ -0,0 +1,126 @@ +// Сессия Ayla LAN (сторона «приложения»). docs/PROTOCOL.md §4-6, §4.4. +// Потоки: httpd (входящие от модуля: key exchange/commands/datapoint) и +// session (исходящие local_reg, таймеры, backoff). Крипто-цепочки и выдача +// команд — только в httpd-потоке; session-поток читает очередь под mutex. +#pragma once + +#include +#include +#include + +#include "ayla/crypto.hpp" +#include "ayla/httpd.hpp" + +namespace fgl::ayla { + +enum class SessionState : uint8_t { + kIdle = 0, // создан, не запущен + kRegistering, // local_reg отправлен, ждём key exchange + kOnline, // сессия активна + kRecovering, // ожидание самолечения (re-key по keep-alive / активация) + kOffline, // модуль недоступен (backoff) или нет слотов + kKeyError, // lanip_key_id не совпал — требуется смена конфига +}; + +enum class SessionError : int { + kNone = 0, + kNoSlot = 1, // 503: оба слота модуля заняты + kUnreachable = 2, // transport/backoff + kKeyMismatch = 3, // key_id != lanip_key_id (state = kKeyError) + kBadKeyExchange = 4, // ver/proto/sec не поддержаны + kActivationTimeout = 5,// KE прошёл, опроса commands.json нет (>5 c) + kDecryptFailed = 6, // подпись/расшифровка push не сошлись (ждём re-key) +}; + +// Событие обновления свойства (push модуля). Значение — какой-то один тип. +struct PropertyEvent { + char name[40]; + bool is_int = false; + int64_t int_value = 0; + bool is_bool = false; + bool bool_value = false; + char str_value[64]; // используется, если !is_int && !is_bool + int cmd_id = -1; // из ?cmd_id=N (ответ на GET), иначе -1 + int status = 0; // из ?status=200 + int64_t seq_no = 0; +}; + +struct SessionConfig { + const char* host = nullptr; // DNS-имя или IP модуля + uint16_t device_port = 80; // порт local_reg модуля + const char* dsn = nullptr; // "AC000W00XXXXXXX" + const char* lanip_key = nullptr; // base64-строка как есть + uint32_t lanip_key_id = 0; + uint16_t listen_port = 10275; // 0 — любой свободный + uint32_t keepalive_ms = 15000; + uint8_t max_queue = 16; // лимит очереди команд +}; + +struct SessionCallbacks { + // КОНТРАКТ: колбэки приходят из потоков ядра (httpd и/или session), + // возможно перекрытие во времени; обязаны быть быстрыми и реентерабельными. + // Вызывать stop() из колбэка запрещено (deadlock на join). + void (*on_state)(void* ctx, SessionState st, SessionError err); + void (*on_property)(void* ctx, const PropertyEvent& ev); + void* ctx = nullptr; +}; + +// Тайминги поведения (PROTOCOL §4.3-4.4; проверено на приборе). +struct SessionTimings { + uint32_t keepalive_ms = 15000; // период local_reg + uint32_t activation_timeout_ms = 5000; // нет poll после KE + uint32_t recovering_quiet_ms = 50000; // пауза при десинке/«KE без poll» + // (> порога возврата модуля ~44-50с) + uint32_t no_slot_retry_ms = 60000; // повтор после 503 + uint32_t backoff_base_ms = 1000; // transport backoff, шаг x1.6 + uint32_t backoff_max_ms = 60000; + uint8_t backoff_attempts = 6; + uint32_t delete_wait_ms = 2000; +}; + +class Session { + public: + static Session* create(const SessionConfig& cfg, const SessionCallbacks& cbs); + ~Session(); + + Session(const Session&) = delete; + Session& operator=(const Session&) = delete; + + bool start(); + // Штатное завершение: DELETE-команда + local_reg notify, ожидание выдачи, + // остановка потоков. state -> kIdle. + void stop(); + + SessionState state() const; + SessionError last_error() const; + uint16_t listen_port() const; + // Телеметрия (диагностика). + uint32_t rekey_count() const; + uint32_t pushes_ok() const; + uint32_t pushes_bad() const; + uint32_t commands_served() const; + bool had_activity() const; + + // ---- Команды (потокобезопасны; кладутся в очередь с coalescing) ---- + // GET-команда: свойство придёт on_property (cmd_id совпадает). + bool get_property(const char* name); + // SET-команда (integer/boolean как int64). + bool set_property(const char* name, int64_t value, + const char* base_type = "integer"); + // Пакет: собрать несколько команд, один notify на commit. + bool begin_batch(); + bool commit_batch(); + bool abort_batch(); + // DELETE local_reg.json/delete_session (для stop() и ручного завершения). + bool delete_session(); + + // Тест-хук: тайминги. ТОЛЬКО до start() (после — читаются потоками ядра). + void set_timings_for_test(const SessionTimings& t); + + private: + Session(const SessionConfig& cfg, const SessionCallbacks& cbs); + struct Impl; + Impl* impl_; +}; + +} // namespace fgl::ayla diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 86c44f3..9088672 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,9 +20,23 @@ function(fgl_add_test name) add_test(NAME ${name} COMMAND test_${name}) endfunction() +# Раннер сессии — не тест, приложение для интеграционных сценариев +# (запускается tests/ayla/test_session_mock.py через ctest). +add_executable(session_runner ayla/session_runner.cpp) +target_compile_features(session_runner PRIVATE cxx_std_20) +target_link_libraries(session_runner PRIVATE fgl-aircon) +target_include_directories(session_runner PRIVATE "${CMAKE_SOURCE_DIR}/src") + fgl_add_test(ayla_platform ayla/test_platform.cpp) fgl_add_test(ayla_httpd ayla/test_httpd.cpp) fgl_add_test(ayla_crypto ayla/test_crypto.cpp) fgl_add_test(ayla_envelope ayla/test_envelope.cpp) fgl_add_test(ayla_json ayla/test_json.cpp) fgl_add_test(ayla_httpc ayla/test_httpc.cpp) + +# Интеграционные сценарии с mock-модулем (python, stdlib-only). +find_package(Python3 COMPONENTS Interpreter REQUIRED) +add_test(NAME ayla_session_mock + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/ayla/test_session_mock.py + $ ${CMAKE_CURRENT_SOURCE_DIR}/ayla/mock_ac.py) +set_tests_properties(ayla_session_mock PROPERTIES TIMEOUT 180) diff --git a/tests/ayla/mock_ac.py b/tests/ayla/mock_ac.py new file mode 100644 index 0000000..91586da --- /dev/null +++ b/tests/ayla/mock_ac.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python3 +"""Мок-модуль кондиционера (сторона устройства) для интеграционных тестов +сессии. stdlib-only: AES-256 реализован на чистом python (объёмы крошечные). + +Сценарные флаги: + --503 всегда отвечать 503 на local_reg (нет слотов) + --no-poll key exchange без опроса commands.json (зависание) + --rekey-every N ре-кей на каждый N-й local_reg (N=1 — каждый) + --stale-gap S ре-кей, если зазор между local_reg >= S секунд + (эмуляция «вернувшегося» приложения; по умолчанию 44) + --garbage-pushes N первые N push с отрезанным блоком шифротекста + (входящая цепочка расходится на 1 сообщение) + --break-outbound N N раз «не заметить» ответ commands.json (исходящий + десинк): затем подпись наших команд не сойдётся — + мок, как реальный модуль, ре-кает на следующем local_reg + --fail-pushes N первые N push'ей с испорченной подписью + --push-every S спонтанный push свойства tick каждые S секунд + --fail-first-ke первый key_exchange с ver=2 (ожидаем 426) +Вывод (stdout, строки): + REG notify=0|1 + KE + CMD [value] + PUSH + DELETE +""" +import argparse +import base64 +import hashlib +import hmac +import http.server +import json +import random +import socket +import string +import sys +import threading +import time + +# --------------------------------------------------------------------------- +# Чистый python AES-256 (encrypt/decrypt block), CBC поверх. +# --------------------------------------------------------------------------- +_SBOX = [ + 0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, + 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, + 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, + 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, + 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, + 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, + 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, + 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, + 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, + 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, + 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, + 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, + 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, + 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, + 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, + 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16] +_RCON = [0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36,0x6c,0xd8,0xab,0x4d] +_INV_SBOX = [0]*256 +for _i, _b in enumerate(_SBOX): + _INV_SBOX[_b] = _i + +def _xtime(a): + a <<= 1 + if a & 0x100: + a = (a ^ 0x1b) & 0xff + return a + +def _expand_key(key): # 32 байта -> 60 слов по 4 байта (flat список) + w = list(key) + for i in range(32, 240, 4): + t = w[i-4:i] + if i % 32 == 0: + t = t[1:] + t[:1] + t = [_SBOX[b] for b in t] + t[0] ^= _RCON[i//32 - 1] + elif i % 32 == 16: + t = [_SBOX[b] for b in t] + w += [w[i-32+j] ^ t[j] for j in range(4)] + return w + +def _aes_encrypt_block(w, block): + s = list(block) + def add_round_key(r): + for i in range(16): + s[i] ^= w[r*16 + i] + def sub_shift(): + # SubBytes + ShiftRows (строка r — байты r, r+4, r+8, r+12 — влево на r) + t = [_SBOX[b] for b in s] + out = [0]*16 + for r in range(4): + for c in range(4): + out[r + 4*c] = t[r + 4*((c + r) % 4)] + for i in range(16): + s[i] = out[i] + def mix(): + t = [0]*16 + for c in range(4): + col = s[c*4:c*4+4] + t[c*4+0] = _xtime(col[0]) ^ _xtime(col[1]) ^ col[1] ^ col[2] ^ col[3] + t[c*4+1] = col[0] ^ _xtime(col[1]) ^ _xtime(col[2]) ^ col[2] ^ col[3] + t[c*4+2] = col[0] ^ col[1] ^ _xtime(col[2]) ^ _xtime(col[3]) ^ col[3] + t[c*4+3] = _xtime(col[0]) ^ col[0] ^ col[1] ^ col[2] ^ _xtime(col[3]) + for i in range(16): + s[i] = t[i] + add_round_key(0) + for rnd in range(1, 14): + sub_shift(); mix(); add_round_key(rnd) + sub_shift(); add_round_key(14) + return bytes(s) + +def _aes_decrypt_block(w, block): + inv_sbox = _INV_SBOX + def inv_sub_shift(s): + t = [inv_sbox[b] for b in s] + out = [0]*16 + for r in range(4): + for c in range(4): + out[r + 4*c] = t[r + 4*((c - r) % 4)] # инверсия сдвига влево на r + return out + def inv_mix(s): + def mul(a, b): + p = 0 + for _ in range(8): + if b & 1: + p ^= a + hi = a & 0x80 + a = (a << 1) & 0xff + if hi: + a ^= 0x1b + b >>= 1 + return p + t = [0]*16 + for c in range(4): + col = s[c*4:c*4+4] + t[c*4+0] = mul(col[0],14) ^ mul(col[1],11) ^ mul(col[2],13) ^ mul(col[3],9) + t[c*4+1] = mul(col[0],9) ^ mul(col[1],14) ^ mul(col[2],11) ^ mul(col[3],13) + t[c*4+2] = mul(col[0],13) ^ mul(col[1],9) ^ mul(col[2],14) ^ mul(col[3],11) + t[c*4+3] = mul(col[0],11) ^ mul(col[1],13) ^ mul(col[2],9) ^ mul(col[3],14) + return t + s = list(block) + def add_round_key(r): + for i in range(16): + s[i] ^= w[r*16 + i] + add_round_key(14) + for rnd in range(13, 0, -1): + s = inv_sub_shift(s) # InvShiftRows + InvSubBytes (коммутируют) + add_round_key(rnd) + s = inv_mix(s) + s = inv_sub_shift(s) + add_round_key(0) + return bytes(s) + +class PyAes: + def __init__(self, key): + self.w = _expand_key(key) + def cbc_encrypt(self, iv, data): + out = b"" + prev = iv + for i in range(0, len(data), 16): + blk = data[i:i+16] + blk = bytes(a ^ b for a, b in zip(blk, prev)) + prev = _aes_encrypt_block(self.w, blk) + out += prev + return out, prev + def cbc_decrypt(self, iv, data): + out = b"" + prev = iv + for i in range(0, len(data), 16): + blk = data[i:i+16] + dec = _aes_decrypt_block(self.w, blk) + out += bytes(a ^ b for a, b in zip(dec, prev)) + prev = blk + return out, prev + +# --------------------------------------------------------------------------- +class MockCrypto: + """Ключи одной стороны мока: dev (исходящие push) и app (входящие команды).""" + def __init__(self, lanip_key, rnd1, rnd2, t1, t2): + k = lanip_key.encode() + b1, b2 = rnd1.encode(), rnd2.encode() + s1, s2 = str(t1).encode(), str(t2).encode() + def m(msg, suf): + msg = msg + bytes([suf]) + return hmac.digest(k, hmac.digest(k, msg, "sha256") + msg, "sha256") + A, D = b1 + b2 + s1 + s2, b2 + b1 + s2 + s1 + self.dev_sign, self.dev_aes = m(D, 0x30), PyAes(m(D, 0x31)) + self.app_sign, self.app_aes = m(A, 0x30), PyAes(m(A, 0x31)) + self.dev_iv, self.app_iv = m(D, 0x32)[:16], m(A, 0x32)[:16] + + def pack_push(self, seq, data_json): + plain = json.dumps({"seq_no": seq, "data": data_json}, + separators=(",", ":")).encode() + sign = base64.b64encode(hmac.digest(self.dev_sign, plain, "sha256")).decode() + n = ((len(plain) + 1 + 15) // 16) * 16 + ct, self.dev_iv = self.dev_aes.cbc_encrypt(self.dev_iv, plain.ljust(n, b"\x00")) + enc = base64.b64encode(ct).decode() + return json.dumps({"enc": enc, "sign": sign}, separators=(",", ":")) + + def unpack_command(self, body): + d = json.loads(body) + pt, self.app_iv = self.app_aes.cbc_decrypt( + self.app_iv, base64.b64decode(d["enc"])) + pt = pt.rstrip(b"\x00") + # Подпись проверяется ДО разбора JSON (мусор не парсим). + ok = base64.b64encode(hmac.digest(self.app_sign, pt, "sha256")).decode() == d["sign"] + if not ok: + return False, None + return True, json.loads(pt.decode()) + +# --------------------------------------------------------------------------- +class Mock: + def __init__(self, args): + self.args = args + self.lock = threading.Lock() + self.props = {"operation_mode": 6, "fan_speed": 4, "tick": 0} + self.crypto = None + self.app_addr = None # (ip, port) приложения + self.push_seq = 0 + self.reg_count = 0 + self.last_reg_time = None + self.fail_pushes = args.fail_pushes + self.garbage_pushes = args.garbage_pushes + self.miss_response = args.break_outbound + self.stop = threading.Event() + + # ---------- исходящие к приложению ---------- + def http_call(self, method, path, body=b"", timeout=5): + ip, port = self.app_addr + c = socket.create_connection((ip, port), timeout=timeout) + req = (f"{method} {path} HTTP/1.1\r\nHost: {ip}\r\n" + f"Content-Type: application/json\r\n" + f"Content-Length: {len(body)}\r\nConnection: close\r\n\r\n").encode() + body + c.sendall(req) + raw = b"" + while True: + chunk = c.recv(4096) + if not chunk: + break + raw += chunk + c.close() + head, _, resp_body = raw.partition(b"\r\n\r\n") + status = int(head.split(b" ")[1]) + return status, resp_body + + def do_key_exchange(self): + rnd1 = "".join(random.choice(string.ascii_letters + string.digits + "+/") + for _ in range(16)) + t1 = int(time.monotonic_ns() // 1000) + ver = 2 if (self.args.fail_first_ke and self.reg_count == 1) else 1 + body = json.dumps({"key_exchange": { + "ver": ver, "proto": 1, "key_id": self.args.key_id, + "random_1": rnd1, "time_1": t1, "sec": ""}}, + separators=(",", ":")).encode() + status, resp = self.http_call("POST", "/local_lan/key_exchange.json", body) + print(f"KE {rnd1} -> {status}", flush=True) + if status != 200: + return False + d = json.loads(resp) + self.crypto = MockCrypto(self.args.lanip_key, rnd1, d["random_2"], + t1, d["time_2"]) + return True + + def poll_commands(self): + """Опрашивает commands.json, пока 206; исполняет команды.""" + while True: + status, body = self.http_call("GET", "/local_lan/commands.json") + if status != 200 and status != 206: + print(f"POLL -> {status}", flush=True) + return + if not self.crypto: + return + if self.miss_response > 0: + # «Модуль не получил/не расшифровал ответ»: цепочка приложения + # ушла, у мока нет — исходящий десинк. + self.miss_response -= 1 + print("CMD skipped (outbound desync)", flush=True) + return + ok, payload = self.crypto.unpack_command(body) + if not ok: + print("CMD bad-sign", flush=True) + # Реальный модуль: на следующем local_reg — key exchange. + self.crypto = None + return + data = payload.get("data", {}) + cmds = data.get("cmds", []) + props = data.get("properties", []) + if cmds: + for c in cmds: + cmd = c.get("cmd", {}) + if cmd.get("method") == "DELETE": + print("DELETE", flush=True) + return + res = cmd.get("resource", "") + name = res.split("name=")[-1] + cid = cmd.get("cmd_id", -1) + print(f"CMD GET {name} cid={cid}", flush=True) + self.push_datapoint(name, cid=cid) + elif props: + for p in props: + pr = p.get("property", {}) + self.props[pr.get("name", "?")] = pr.get("value") + print(f"CMD SET {pr.get('name')}={pr.get('value')}", flush=True) + elif not data: + return + if status == 200: + return + + def push_datapoint(self, name, cid=-1, corrupt=False, garbage=False): + self.push_seq += 1 + body = self.crypto.pack_push(self.push_seq - 1, + {"name": name, "value": self.props.get(name, 0)}) + if garbage: + # РЕАЛЬНЫЙ десинк CBC: отрезать последний блок шифротекста — + # цепочка мока ушла на блок дальше, приложение отстанет. + d = json.loads(body) + ct = base64.b64decode(d["enc"]) + d["enc"] = base64.b64encode(ct[:-16]).decode() + body = json.dumps(d, separators=(",", ":")) + if corrupt: + d = json.loads(body) + s = d["sign"] + d["sign"] = ("A" if s[-2] != "A" else "B") + s[1:] + body = json.dumps(d, separators=(",", ":")) + path = "/local_lan/property/datapoint.json" + if cid >= 0: + path += f"?cmd_id={cid}&status=200" + status, _ = self.http_call("POST", path, body.encode()) + print(f"PUSH {name} -> {status}{' CORRUPT' if corrupt else ''}", flush=True) + + # ---------- входящие local_reg ---------- + def handle_local_reg(self, body): + self.reg_count += 1 + d = json.loads(body)["local_reg"] + notify = d.get("notify", 0) + self.app_addr_json = d + self.app_addr = (d["ip"], d["port"]) + print(f"REG {'first' if self.reg_count == 1 else 'put'} notify={notify}", + flush=True) + now = time.monotonic() + stale = (self.last_reg_time is not None and + now - self.last_reg_time >= self.args.stale_gap) + self.last_reg_time = now + if self.args.http503: + return 503 + need_ke = self.crypto is None or stale or ( + self.args.rekey_every and self.reg_count % self.args.rekey_every == 0) + if need_ke and not self.do_key_exchange(): + return 202 + if not self.args.no_poll: + self.poll_commands() + return 202 + + def spontaneous_loop(self): + while not self.stop.wait(self.args.push_every or 10): + if self.crypto and not self.args.no_poll: + with self.lock: + self.props["tick"] += 1 + corrupt = self.fail_pushes > 0 + if corrupt: + self.fail_pushes -= 1 + garbage = self.garbage_pushes > 0 + if garbage: + self.garbage_pushes -= 1 + self.push_datapoint("tick", corrupt=corrupt, garbage=garbage) + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--port", type=int, required=True) + ap.add_argument("--lanip-key", required=True) + ap.add_argument("--key-id", type=int, required=True) + ap.add_argument("--503", dest="http503", action="store_true") + ap.add_argument("--no-poll", action="store_true") + ap.add_argument("--rekey-every", type=int, default=0) + ap.add_argument("--fail-pushes", type=int, default=0) + ap.add_argument("--garbage-pushes", type=int, default=0) + ap.add_argument("--break-outbound", type=int, default=0) + ap.add_argument("--stale-gap", type=float, default=44.0) + ap.add_argument("--push-every", type=float, default=0) + ap.add_argument("--fail-first-ke", action="store_true") + args = ap.parse_args() + + mock = Mock(args) + lock = mock.lock + + class H(http.server.BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + def log_message(self, *a): + pass + def do_POST(self): + n = int(self.headers.get("Content-Length") or 0) + body = self.rfile.read(n) + if self.path.startswith("/local_reg.json"): + with lock: + code = mock.handle_local_reg(body) + self.send_response(code) + self.send_header("Content-Length", "0") + self.end_headers() + return + self.send_response(404) + self.send_header("Content-Length", "0") + self.end_headers() + def do_PUT(self): + self.do_POST() + + srv = http.server.ThreadingHTTPServer(("127.0.0.1", args.port), H) + threading.Thread(target=srv.serve_forever, daemon=True).start() + if args.push_every: + threading.Thread(target=mock.spontaneous_loop, daemon=True).start() + print("READY", flush=True) + try: + while True: + time.sleep(0.5) + except KeyboardInterrupt: + pass + +if __name__ == "__main__": + main() diff --git a/tests/ayla/session_runner.cpp b/tests/ayla/session_runner.cpp new file mode 100644 index 0000000..390c1fb --- /dev/null +++ b/tests/ayla/session_runner.cpp @@ -0,0 +1,135 @@ +// Тестовый раннер сессии: поднимает fgl::ayla::Session против mock_ac.py +// (или реального модуля) и печатает события строками в stdout: +// STATE — смена состояния +// PROP — push свойства +// DELETED — delete_session забран модулем +// STATS ... — телеметрия (перед выходом) +// Управление окружением: +// RUNNER_KEEPALIVE_MS, RUNNER_ACTIVATION_MS, RUNNER_QUIET_MS, +// RUNNER_NOSLOT_RETRY_MS, RUNNER_DELETE_WAIT_MS +// RUNNER_SET_NAME/VALUE/AT — одиночная SET через AT секунд после старта. +#include +#include +#include +#include +#include + +#include "ayla/session.hpp" + +using fgl::ayla::PropertyEvent; +using fgl::ayla::Session; +using fgl::ayla::SessionError; +using fgl::ayla::SessionState; + +static const char* state_name(SessionState st) { + switch (st) { + case SessionState::kIdle: return "idle"; + case SessionState::kRegistering: return "registering"; + case SessionState::kOnline: return "online"; + case SessionState::kRecovering: return "recovering"; + case SessionState::kOffline: return "offline"; + case SessionState::kKeyError: return "key_error"; + } + return "?"; +} + +static uint32_t env_u32(const char* name, uint32_t def) { + const char* v = getenv(name); + return v != nullptr ? static_cast(atoi(v)) : def; +} + +static void on_state(void*, SessionState st, SessionError err) { + printf("STATE %s %d\n", state_name(st), static_cast(err)); + fflush(stdout); +} + +static void on_property(void*, const PropertyEvent& ev) { + if (ev.is_int) { + printf("PROP %s %d %d i:%lld\n", ev.name, ev.cmd_id, ev.status, + static_cast(ev.int_value)); + } else if (ev.is_bool) { + printf("PROP %s %d %d b:%d\n", ev.name, ev.cmd_id, ev.status, + ev.bool_value ? 1 : 0); + } else { + printf("PROP %s %d %d s:%s\n", ev.name, ev.cmd_id, ev.status, ev.str_value); + } + fflush(stdout); +} + +int main(int argc, char** argv) { + if (argc < 7) { + fprintf(stderr, + "usage: %s " + " [prop ...]\n", + argv[0]); + return 2; + } + fgl::ayla::SessionConfig cfg{}; + cfg.host = argv[1]; + cfg.device_port = static_cast(atoi(argv[2])); + cfg.listen_port = static_cast(atoi(argv[3])); + cfg.dsn = argv[4]; + cfg.lanip_key = argv[5]; + cfg.lanip_key_id = static_cast(atoi(argv[6])); + int duration_sec = atoi(argv[7]); + + fgl::ayla::SessionCallbacks cbs{}; + cbs.on_state = on_state; + cbs.on_property = on_property; + + Session* s = Session::create(cfg, cbs); + if (s == nullptr) { + fprintf(stderr, "create failed\n"); + return 2; + } + fgl::ayla::SessionTimings timings{}; + timings.keepalive_ms = env_u32("RUNNER_KEEPALIVE_MS", timings.keepalive_ms); + timings.activation_timeout_ms = + env_u32("RUNNER_ACTIVATION_MS", timings.activation_timeout_ms); + timings.recovering_quiet_ms = + env_u32("RUNNER_QUIET_MS", timings.recovering_quiet_ms); + timings.no_slot_retry_ms = env_u32("RUNNER_NOSLOT_RETRY_MS", + timings.no_slot_retry_ms); + timings.delete_wait_ms = env_u32("RUNNER_DELETE_WAIT_MS", + timings.delete_wait_ms); + s->set_timings_for_test(timings); + + if (!s->start()) { + fprintf(stderr, "start failed\n"); + return 2; + } + + // Начальная синхронизация: пакет GET всех свойств. + if (argc > 8) { + s->begin_batch(); + for (int i = 8; i < argc; i++) { + s->get_property(argv[i]); + } + s->commit_batch(); + } + + const char* set_name = getenv("RUNNER_SET_NAME"); + const char* set_val = getenv("RUNNER_SET_VALUE"); + uint32_t set_at = env_u32("RUNNER_SET_AT", 0); + if (set_name != nullptr && set_val != nullptr && set_at > 0) { + std::this_thread::sleep_for(std::chrono::seconds(set_at)); + s->set_property(set_name, atoll(set_val)); + printf("SET_DONE %s=%s\n", set_name, set_val); + fflush(stdout); + } + + auto deadline = std::chrono::steady_clock::now() + + std::chrono::seconds(duration_sec); + while (std::chrono::steady_clock::now() < deadline) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + s->stop(); // внутри: delete_session + ожидание выдачи + printf("DELETED\n"); + printf("STATS rekeys=%u pushes_ok=%u pushes_bad=%u cmds=%u state=%s\n", + s->rekey_count(), s->pushes_ok(), s->pushes_bad(), + s->commands_served(), state_name(s->state())); + fflush(stdout); + delete s; + return 0; +} diff --git a/tests/ayla/test_session_mock.py b/tests/ayla/test_session_mock.py new file mode 100644 index 0000000..80c5b43 --- /dev/null +++ b/tests/ayla/test_session_mock.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python3 +"""Интеграционные сценарии сессии против mock-модуля (mock_ac.py). + +usage: test_session_mock.py +Сценарии: normal, rekey, fail_push, slot_503, no_poll, bad_ke, set_get. +""" +import os +import socket +import subprocess +import sys +import tempfile +import threading +import time + +RUNNER = sys.argv[1] +MOCK = sys.argv[2] + +# Синтетический ключ (НЕ боевой). +LANIP_KEY = "TW9ja0tleUFDbkdvMTIzNDU2Nzg5MDEyMw==" +KEY_ID = 64201 +DSN = "AC000W00MOCK0001" + + +def free_port(): + s = socket.socket() + s.bind(("127.0.0.1", 0)) + port = s.getsockname()[1] + s.close() + return port + + +class Proc: + """Процесс с построчным логом stdout.""" + + def __init__(self, argv, env=None): + self.lines = [] + self.lock = threading.Lock() + self.ev = threading.Event() + self.proc = subprocess.Popen( + argv, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + text=True) + threading.Thread(target=self._reader, daemon=True).start() + + def _reader(self): + for line in self.proc.stdout: + with self.lock: + self.lines.append(line.rstrip("\n")) + self.ev.set() + + def wait_line(self, prefix, timeout, exclude_prefix=None): + """Ждёт строку, начинающуюся с prefix (или содержащую, если prefix + начинается с '~').""" + contains = prefix.startswith("~") + needle = prefix[1:] if contains else prefix + deadline = time.time() + timeout + seen = 0 + while time.time() < deadline: + with self.lock: + for i, line in enumerate(self.lines): + if i < seen: + continue + hit = (needle in line) if contains else line.startswith(prefix) + if hit: + if exclude_prefix and line.startswith(exclude_prefix): + continue + return line + seen = i + 1 + self.ev.wait(0.2) + return None + + def all_lines(self): + with self.lock: + return list(self.lines) + + def stop(self, timeout=10): + self.proc.terminate() + try: + self.proc.wait(timeout) + except subprocess.TimeoutExpired: + self.proc.kill() + + +def run_mock(extra, port): + p = Proc([sys.executable, MOCK, "--port", str(port), "--lanip-key", LANIP_KEY, + "--key-id", str(KEY_ID)] + extra) + if p.wait_line("READY", 10) is None: + raise RuntimeError("mock не стартовал: " + "\n".join(p.all_lines())) + return p + + +def run_runner(mock_port, listen_port, props, duration, env_extra=None, + timeout=None): + env = dict(os.environ) + if env_extra: + env.update(env_extra) + argv = [RUNNER, "127.0.0.1", str(mock_port), str(listen_port), DSN, + LANIP_KEY, str(KEY_ID), str(duration)] + props + return Proc(argv, env=env), (timeout or duration + 25) + + +FAILURES = [] + + +def check(cond, what): + tag = "ok " if cond else "FAIL" + print(f" [{tag}] {what}") + if not cond: + FAILURES.append(what) + + +def scenario_normal(): + print("== scenario: normal (установка, батч GET, push, delete)") + mock_port, listen = free_port(), free_port() + mock = run_mock([], mock_port) + runner, timeout = run_runner(mock_port, listen, + ["operation_mode", "fan_speed"], 3) + line = runner.wait_line("STATE online", 10) + check(line is not None, "переход в online") + p1 = runner.wait_line("PROP operation_mode", 10) + p2 = runner.wait_line("PROP fan_speed", 10) + check(p1 is not None and "cid=" not in p1 and " i:6" in p1, f"push #1: {p1}") + check(p2 is not None and " i:4" in p2, f"push #2: {p2}") + check(runner.wait_line("DELETED", timeout) is not None, "delete_session") + check(runner.wait_line("STATS", 5) is not None, "STATS") + check("rekeys=1" in " ".join(runner.all_lines()), "ровно 1 key exchange") + check("CMD GET operation_mode" in "\n".join(mock.all_lines()), + "mock исполнил GET") + check("DELETE" in "\n".join(mock.all_lines()), "mock получил DELETE") + runner.stop() + mock.stop() + + +def scenario_rekey(): + print("== scenario: rekey по local_reg (chain непрерывна)") + mock_port, listen = free_port(), free_port() + mock = run_mock(["--rekey-every", "2", "--push-every", "1"], mock_port) + runner, timeout = run_runner(mock_port, listen, [], 6, + env_extra={"RUNNER_KEEPALIVE_MS": "1000"}) + check(runner.wait_line("STATE online", 10) is not None, "online") + t1 = runner.wait_line("PROP tick", 15) + t2 = runner.wait_line("PROP tick", 15) + check(t1 is not None and t2 is not None, "спонтанные push после re-key") + runner.wait_line("DELETED", timeout) + stats = "\n".join(runner.all_lines()) + check("rekeys=" in stats and "pushes_bad=0" in stats, + f"без потерянных push: {stats.splitlines()[-1] if stats else '?'}") + check("CMD bad-sign" not in "\n".join(mock.all_lines()), + "mock проверял подписи наших команд") + runner.stop() + mock.stop() + + +def scenario_fail_push(): + print("== scenario: push с битой подписью -> recovering -> online") + mock_port, listen = free_port(), free_port() + mock = run_mock(["--push-every", "1", "--fail-pushes", "1"], mock_port) + runner, timeout = run_runner(mock_port, listen, [], 8, + env_extra={"RUNNER_KEEPALIVE_MS": "1000"}) + check(runner.wait_line("STATE online", 10) is not None, "online") + rec = runner.wait_line("STATE recovering 6", 15) + check(rec is not None, f"recovering после битой подписи: {rec}") + # Следующий push расшифровывается (цепочка в синке) -> online. + check(runner.wait_line("STATE online 0", 15) is not None, + "online восстановлен следующим валидным push") + check(runner.wait_line("STATS", timeout) is not None, "STATS получены") + stats = "\n".join(runner.all_lines()) + stats_line = [l for l in stats.splitlines() if l.startswith("STATS")] + check("pushes_bad=1" in stats, f"учтён 1 битый push: {stats_line}") + runner.stop() + mock.stop() + + +def scenario_slot_503(): + print("== scenario: 503 — нет слотов") + mock_port, listen = free_port(), free_port() + mock = run_mock(["--503"], mock_port) + runner, timeout = run_runner(mock_port, listen, [], 4, + env_extra={"RUNNER_NOSLOT_RETRY_MS": "2000"}) + line = runner.wait_line("STATE offline 1", 10) + check(line is not None, "offline с ошибкой NoSlot") + regs = [l for l in mock.all_lines() if l.startswith("REG")] + check(len(regs) >= 1, "local_reg доходил до модуля") + runner.stop() + mock.stop() + + +def scenario_no_poll(): + print("== scenario: KE без poll — активация не наступила, тихая пауза") + mock_port, listen = free_port(), free_port() + mock = run_mock(["--no-poll"], mock_port) + runner, timeout = run_runner(mock_port, listen, [], 5, + env_extra={"RUNNER_ACTIVATION_MS": "1500", + "RUNNER_QUIET_MS": "3000", + "RUNNER_KEEPALIVE_MS": "800"}) + line = runner.wait_line("STATE recovering 5", 15) + check(line is not None, "recovering с ActivationTimeout") + time.sleep(1.0) + regs = [l for l in mock.all_lines() if l.startswith("REG")] + # Первый reg + возможно второй до таймаута активации; после — тишина. + check(len(regs) <= 3, f"нет спама local_reg (получено {len(regs)})") + runner.stop() + mock.stop() + + +def scenario_bad_ke(): + print("== scenario: key_exchange ver=2 -> key_error") + mock_port, listen = free_port(), free_port() + mock = run_mock(["--fail-first-ke"], mock_port) + runner, timeout = run_runner(mock_port, listen, [], 3) + line = runner.wait_line("STATE key_error 4", 10) + check(line is not None, "key_error с BadKeyExchange") + check(mock.wait_line("~-> 426", 10) is not None, "mock получил 426") + runner.stop() + mock.stop() + + +def scenario_chain_divergence_heals(): + print("== scenario: входящая цепочка расходится на 1 сообщение и сходится") + mock_port, listen = free_port(), free_port() + mock = run_mock(["--push-every", "1", "--garbage-pushes", "1"], mock_port) + runner, timeout = run_runner(mock_port, listen, [], 8, + env_extra={"RUNNER_KEEPALIVE_MS": "1000"}) + check(runner.wait_line("STATE online", 10) is not None, "online") + check(runner.wait_line("STATE recovering 6", 15) is not None, + "recovering после урезанного push") + # CBC-состояние = последний шифроблок: следующий полный push сходится. + check(runner.wait_line("STATE online 0", 15) is not None, + "online восстановлен следующим push (цепочка сошлась)") + stats = runner.wait_line("STATS", timeout) + # Урезанный push + один residual (первый блок следующего) = 2 потери, + # далее цепочка сошлась (состояние CBC = последний шифроблок). + check(stats is not None and "pushes_bad=2" in stats, + f"ровно 2 потери: {stats}") + runner.stop() + mock.stop() + + +def scenario_outbound_desync_rekey(): + print("== scenario: исходящий десинк -> модуль ре-кает на local_reg") + mock_port, listen = free_port(), free_port() + mock = run_mock(["--break-outbound", "1"], mock_port) + runner, timeout = run_runner(mock_port, listen, ["operation_mode"], 12, + env_extra={"RUNNER_KEEPALIVE_MS": "1000", + "RUNNER_SET_NAME": "fan_speed", + "RUNNER_SET_VALUE": "2", + "RUNNER_SET_AT": "6"}) + check(runner.wait_line("STATE online", 10) is not None, "online") + check(mock.wait_line("~CMD skipped", 10) is not None, + "мок пропустил ответ (десинк)") + check(mock.wait_line("~CMD bad-sign", 15) is not None, + "подпись следующей команды не сошлась") + # Реальный модуль ре-кает на следующем local_reg (как в probe1-3); + # после re-key цепочки свежие — SET доходит. + check(mock.wait_line("~CMD SET fan_speed=2", 20) is not None, + "SET доставлена после re-key") + check(len([l for l in mock.all_lines() if l.startswith("KE ")]) >= 2, + "второй key exchange был") + runner.stop() + mock.stop() + + +def scenario_set_get(): + print("== scenario: SET + подтверждение GET (эха нет)") + mock_port, listen = free_port(), free_port() + mock = run_mock([], mock_port) + env = {"RUNNER_SET_NAME": "fan_speed", "RUNNER_SET_VALUE": "3", + "RUNNER_SET_AT": "1"} + runner, timeout = run_runner(mock_port, listen, [], 5, env_extra=env) + line = runner.wait_line("SET_DONE fan_speed=3", 10) + check(line is not None, "SET отправлена") + check(mock.wait_line("CMD SET fan_speed=3", 10) is not None, + "mock применил SET") + runner.wait_line("DELETED", timeout) + runner.stop() + mock.stop() + + +def main(): + scenarios = [scenario_normal, scenario_rekey, scenario_fail_push, + scenario_slot_503, scenario_no_poll, scenario_bad_ke, + scenario_chain_divergence_heals, + scenario_outbound_desync_rekey, scenario_set_get] + for sc in scenarios: + sc() + print() + if FAILURES: + print("ПРОВАЛЕНО:", len(FAILURES)) + for f in FAILURES: + print(" -", f) + sys.exit(1) + print("ВСЕ СЦЕНАРИИ ПРОЙДЕНЫ") + + +if __name__ == "__main__": + main()