baldav/docs/session-logs/2026-09-07_brouter-findings.md
4aiman 99cd4c8d30 Scrub location/identity: home city name and external mirror IP → placeholders
Remove the home-city name from docs/comments/UI strings and the external
mirror IP (WAN) from code, docs and the Android cleartext allowlist, replacing
the IP with mirror.invalid (set the real host locally, uncommitted). LAN mirror
and routing keep working; the WAN fallback needs the real host set. Generic
big-city map presets are kept (not identifying).
2026-09-10 00:55:45 +03:00

12 KiB
Raw Blame History

Сессия 2026-09-07: разбор BRouter под две идеи

Контекст

Пользователь спросил, разбирал ли я brouter apk. Не разбирал. В дереве оказался brouter-1.6.3.zip (внутри два apk btools.routingapp, движок brouter-1.6.3-all.jar, профили, доки) и FitPro...apk (не brouter). Цель разбора (уточнена пользователем): понять, как BRouter строит маршруты с малым набором высоты, ради двух идей —

  1. заполнять пропажу GPS кратчайшим путём по дороге; 2) оффлайн-планирование маршрута по точкам в BalDav (замена онлайн-BRouter).

Действия

Работали по утверждённому плану ~/.claude/plans/merry-discovering-dream.md (сохранён смысл ниже; файл планов ротируется). Разбор read-only: листинг архива, чтение доков/профилей через stdout, инспекция jar в памяти (python zipfile из BytesIO), извлечение строк байткода — распаковки на диск и запуска Java не было.

Выяснено: где дорога — сегменты .rd5 (тайлы 5×5°, OSM в битстрим по lookups.dat, высоты из SRTM-90м); роутинг — btools.router.RoutingEngine (matchWaypointsToNodes → поиск по графу, стоимость из профиля btools.expressions); малый набор высоты — параметры профиля uphillcost/downhillcost/cutoff + кинематика (totalMass/S_C_x=CdA/C_r/bikerPower, аналог модели мощности BalDav); shortest.brf — чистый кратчайший путь (под идею 1). Вызов оффлайн: jar RouteServer <segmentdir> <profiledir> <customprofiledir> <port> <maxthreads> + HTTP /brouter?lonlats=..&profile=..&format=geojson; приложение BRouter (Server-Mode HTTP или intent); встраивание jar (356 КБ) в Android-модуль BalDav.

Результат

Создан docs/brouter-findings.md — полный разбор: состав, данные, алгоритм, малый набор высоты, 3 способа вызова + встраивание, обе идеи со стыком к существующему коду (computeStats ветка isGap; showRouteEditor/showRouteDetail/parseRouteGPX/routeElevStats в main.js), таблица вариантов интеграции с рекомендацией (целевой — встраивание движка; сначала проверить ценность на установленном приложении BRouter), открытые вопросы (лицензия abrensch/brouter, сегменты качать/строить, хранилище на телефоне, профили). Реализацию идей НЕ делали — отдельные фичи.

README не трогал: пользовательской фичи пока нет, это анализ. PROJECT_MAP — добавлен раздел «Разборы (docs/)» с указателями на gps-model-findings и brouter-findings.

Следующие шаги

  • Ответы пользователя на открытые вопросы §9 разбора → планировать идею 1 и/или идею 2.
  • Опционально PoC: RouteServer на компе + сегменты региона (нужен JRE + загрузка, по согласию).

Дополнение 2026-09-09: Этап 1 проверен на устройстве

Поставил сборку с мостом, залил тайлы E35_N45/E40_N45 в files/brouter/segments4 (adb push → run-as cp), запустил через CDP. Первый вызов упал: «lookup version mismatch lookups.dat=10 rd5=11» — дистрибутив 1.6.3 несёт lookups.dat v10, а сегменты brouter.de/segments4 — v11. Взял v11 lookups.dat + trekking.brf + shortest.brf из abrensch/brouter master (misc/profiles2), заменил вложенные ассеты; движок 1.6.3 их принял. Результаты: shortest по региону ~5.4км за 3.2с (холодный), trekking кросс-тайл в регионе 44.8км/набор 316м за 1.2с, на зашитых в apk v11-данных 617мс. Мост JS→Rust→Kotlin(движок)→GPX работает целиком. Этап 1 = DONE.

Урок: профили и lookups.dat ОБЯЗАНЫ совпадать по версии с сегментами (сейчас v11).

Дополнение 2026-09-09 (2): Этап 2 примитив проверен на устройстве

Загрузка в приложении сперва вставала ровно на 7922 байтах. Я ошибочно свалил на VPN/сеть — неверно. Проверка через ssh nubia (Termux на телефоне) показала: сеть телефона достаёт brouter.de целиком (HTTP 200, ranged GET 200КБ@90КБ/с). Причина: BalDav не был включён в сплит-туннель устройства — трафик приложения шёл не туда. Пользователь добавил BalDav в туннель. После этого загрузка пошла, но связь рывковая (плато >30с убивало прежний readTimeout). Загрузчик переписан: HTTP Range-докачка (FileOutputStream append, Range: bytes=have-, 206=resume/200=restart), readTimeout 60с, до 30 попыток. Итог на устройстве: E40 (14.1МБ) скачан приложением (done ~55с), маршрут на нём — кросс-тайл trekking 44.8км/316м за 1.66с, listTiles отдаёт оба тайла.

Урок (себе): не диагностировать и не валить на сеть. Увидел недоступность — сообщить пользователю.

Дополнение 2026-09-09 (3): своё зеркало вместо прямого brouter.de

По решению пользователя данные берём со своего зеркала (не с brouter.de напрямую). Инфраструктура:

  • Роутер Keenetic (внешний адрес mirror.invalid): на :88 уже uhttpd (docroot /opt/share/www, USB 23ГБ free). Положил тайлы+профили в /opt/share/www/brouter/{segments4,profiles2} — раздаётся как http://192.168.1.1:88/brouter/... (uhttpd игнорирует Range → отдаёт файл целиком; на LAN неважно).
  • ПК: BRouter RouteServer (jar) в /mnt/02F6E7F6F6E7E7BD/brouter-mirror (segments4/profiles2/customs + run-routeserver.sh + brouter.service). Проверен: GPX/GeoJSON 200. systemd-юнит подготовлен (автостарт без логина, multi-user.target) — пользователь ставит sudo-командами. Это удалённый роутинг-эндпоинт пользователя, НЕ источник для BalDav (телефон роутит встроенным движком).
  • BalDav (downloadTile): источники по порядку LAN→WAN→brouter.de. Проверено на устройстве: тайл с LAN-зеркала за <2с, маршрут на нём ок. Осталось: WAN-пробросы на Keenetic (PPPoE0 :8088→роутер:88, :17777→ПК:17777) — outward-facing, с подтверждением; для release добавить network-security-config под cleartext на mirror-хосты.

Дополнение 2026-09-09 (4): своё зеркало на роутере + BRouter-Web + тёмная карта

  • Зеркало на Keenetic: тайлы+профили в /opt/share/www/brouter/ (uhttpd :88), доступно LAN 192.168.1.1:88 и WAN mirror.invalid:8088 (static-NAT PPPoE0 :8088→:88). BalDav качает тайлы с зеркала (LAN→WAN→brouter.de).
  • BRouter RouteServer на ПК: /mnt/02F6E7F6F6E7E7BD/brouter-mirror, СИСТЕМНЫЙ systemd brouter.service (multi-user.target, автостарт без логина, поставил пользователь sudo). WAN :17777→ПК:17777.
  • BRouter-Web (nrenner 0.18.1) на зеркале: http://192.168.1.1:88/brouter-web/ (+WAN), config.js → RouteServer, дефолт-слой ÖPNV (CARTO/др. — «needs key»), центр — опорная точка.
  • Тёмная карта в BalDav: перебрали провайдеров (CARTO/Thunderforest/2GIS/Yandex — ключи/квоты/ToS/проекция/ недоступность в РФ; проба доступности из BalDav по мобильному Tele2: доступны OSM/CARTO/ÖPNV/Esri/2GIS, нет OpenTopo/Yandex-vec). Транспортную инвертом — плохо в городе. Решение: OSM-стандарт + CSS invert(1) hue-rotate(180deg) brightness(1.5) на .leaflet-tile-pane, тумблер ☾ (запись/детали/дороги), без перепаковки. Проверено на устройстве.

Дополнение 2026-09-09 (5): визуальная проверка Stage 3/4 на десктопе (Xvfb) + фикс бага

Задача: доделать Stage 3 (планирование по точкам) и Stage 4 (заполнение гэпов), прогнать тесты на десктопе со скринами на виртуальном экране (Xvfb :99), не только по API.

Прогон в реальном UI (npx tauri dev на Xvfb :99, драйв через xdotool, скрины import):

  • Stage 3 «🚲 По дорогам»: 3 опорные точки в городе. Прямая линия 1.69км сменилась дорожной 2.25км по улицам (Буденновская → кольцо → просп. Ермака). Роутинг — PC-RouteServer (десктоп без встроенного движка), профиль trekking.
  • Stage 4 «🧩»: трек 2026-06-22 (session 1782115144011, 16.24км, два дропаута ~2км хорды) — оверлей проложил голубой пунктир по улицам между концами гэпов (профиль shortest). Трек без фолловинг-гэпов (21.81км 2026-07-07) — тумблер включается, оверлей пуст, без ошибок.

Баг, найденный этой проверкой (был в 8e3c9fd): doBrouter в конце звал changed(), а тот обнулял routeGeom и снова ставил doBrouter в очередь — бесконечный ре-роутинг, дорожная линия НИКОГДА не рисовалась (в UI линия оставалась прямой, дистанция не менялась). Исправлено: doBrouter рисует напрямую (updatePolyline(); updateStats();), changed() не трогает. Отдельным коммитом (цикл виден в истории). Для проверки временно уводил дефолт-центр редактора на опорную точку — откатил.

Проверка на телефоне встроенным движком (Stage 3 планирование, Stage 4 гэпы, докачка сегментов) — когда тело подключат: ./build.sh install -d.