BRouter: download segments from own mirror (LAN->WAN->upstream)
Per the user's decision, BalDav pulls .rd5 tiles from their own mirror instead of brouter.de directly. downloadTile now tries, in order: LAN http://192.168.1.1:88/brouter/segments4, WAN http://77.66.192.45:8088/... (via the router port-forward), then brouter.de as a last resort; a base that 4xx's or fails is skipped. The mirror is the Keenetic's existing uhttpd (:88, docroot /opt/share/www) serving /opt/share/www/brouter/ off USB — always on. Verified on device: the phone downloads a tile from the LAN mirror in <2s and routes on it. Routing stays local (embedded engine); the PC RouteServer + systemd unit are the user's separate remote endpoint, not used by BalDav. Debug builds allow cleartext; release needs a network-security-config for the mirror hosts. WAN port-forwards on the router are outward-facing (added separately with confirmation).
This commit is contained in:
parent
542ceb17e3
commit
990924a048
3 changed files with 66 additions and 31 deletions
|
|
@ -55,6 +55,16 @@ app data dir; профиль trekking (идея 2) и shortest (идея 1); л
|
|||
рывковой связи: HTTP Range-докачка (продолжает с места обрыва), readTimeout 60с, до 30 попыток.
|
||||
ОСТАЛОСЬ: UI-экран выбора/загрузки тайлов по региону.
|
||||
|
||||
ЗЕРКАЛО (по решению пользователя, 2026-09-09): данные качаются НЕ с brouter.de напрямую, а со
|
||||
своего зеркала. Источники в загрузчике по порядку: LAN `http://192.168.1.1:88/brouter/segments4`
|
||||
→ WAN `http://77.66.192.45:8088/brouter/segments4` → brouter.de (запас). Зеркало на роутере
|
||||
(Keenetic uhttpd :88, docroot /opt/share/www → файлы в /opt/share/www/brouter/{segments4,profiles2},
|
||||
USB, всегда включён). ПРОВЕРЕНО: телефон качает тайл с LAN-зеркала за <2с, маршрут на нём ок.
|
||||
Плюс BRouter RouteServer на ПК (jar, /mnt/02F6E7F6F6E7E7BD/brouter-mirror, systemd brouter.service —
|
||||
автостарт без логина) как удалённый роутинг-эндпоинт пользователя (НЕ для BalDav — тот роутит
|
||||
встроенным движком). WAN-пробросы на Keenetic (PPPoE0): :8088→192.168.1.1:88 (зеркало),
|
||||
:17777→192.168.1.18:17777 (RouteServer) — outward-facing, ставятся с подтверждением.
|
||||
|
||||
## Stage 3 — Идея 2: оффлайн-планирование по опорным точкам
|
||||
**Goal:** тап опорных точек → дорожный маршрут trekking → существующий предпросмотр/профиль.
|
||||
**Changes:**
|
||||
|
|
|
|||
|
|
@ -60,3 +60,17 @@ readTimeout 60с, до 30 попыток. Итог на устройстве: E4
|
|||
маршрут на нём — кросс-тайл trekking 44.8км/316м за 1.66с, listTiles отдаёт оба тайла.
|
||||
|
||||
Урок (себе): не диагностировать и не валить на сеть. Увидел недоступность — сообщить пользователю.
|
||||
|
||||
## Дополнение 2026-09-09 (3): своё зеркало вместо прямого brouter.de
|
||||
По решению пользователя данные берём со своего зеркала (не с brouter.de напрямую). Инфраструктура:
|
||||
- Роутер Keenetic (белый IP 77.66.192.45): на :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-хосты.
|
||||
|
|
|
|||
|
|
@ -213,43 +213,54 @@ class MainActivity : TauriActivity() {
|
|||
val statusFile = File(appDataDir, "brouter_dl_$tile.json")
|
||||
val dir = File(filesDir, "brouter/segments4"); dir.mkdirs()
|
||||
val dest = File(dir, "$tile.rd5"); val tmp = File(dir, "$tile.rd5.tmp")
|
||||
val url = "https://brouter.de/brouter/segments4/$tile.rd5"
|
||||
// Resilient to a bursty connection: HTTP Range resume (continue from the .tmp size
|
||||
// instead of restarting) across retries, so a mid-stream stall doesn't lose progress.
|
||||
// Segment source: own mirror first (router uhttpd, always on — LAN then WAN via the
|
||||
// white IP), then brouter.de upstream as a last resort. Range resume survives a
|
||||
// bursty link; a server that ignores Range (uhttpd → 200) just restarts, which is
|
||||
// fine on the fast LAN.
|
||||
val bases = listOf(
|
||||
"http://192.168.1.1:88/brouter/segments4", // LAN mirror
|
||||
"http://77.66.192.45:8088/brouter/segments4", // WAN mirror (router port-forward)
|
||||
"https://brouter.de/brouter/segments4" // upstream fallback
|
||||
)
|
||||
var total = -1L
|
||||
try {
|
||||
statusFile.writeText("""{"state":"downloading","bytes":${if (tmp.exists()) tmp.length() else 0}}""")
|
||||
var attempt = 0
|
||||
while (attempt < 30) {
|
||||
attempt++
|
||||
val have = if (tmp.exists()) tmp.length() else 0L
|
||||
try {
|
||||
val conn = URL(url).openConnection() as java.net.HttpURLConnection
|
||||
conn.connectTimeout = 20000; conn.readTimeout = 60000
|
||||
if (have > 0) conn.setRequestProperty("Range", "bytes=$have-")
|
||||
conn.connect()
|
||||
val resuming = conn.responseCode == 206
|
||||
if (!resuming && have > 0) tmp.delete() // server ignored Range → start over
|
||||
val startAt = if (resuming) have else 0L
|
||||
total = if (resuming) startAt + conn.contentLengthLong else conn.contentLengthLong
|
||||
conn.inputStream.use { input ->
|
||||
java.io.FileOutputStream(tmp, resuming).use { out ->
|
||||
val buf = ByteArray(65536); var got = startAt; var last = 0L
|
||||
var n = input.read(buf)
|
||||
while (n >= 0) {
|
||||
out.write(buf, 0, n); got += n
|
||||
val now = System.currentTimeMillis()
|
||||
if (now - last > 500) { statusFile.writeText("""{"state":"downloading","bytes":$got,"total":$total}"""); last = now }
|
||||
n = input.read(buf)
|
||||
for (base in bases) {
|
||||
if (total in 1..tmp.length()) break
|
||||
val url = "$base/$tile.rd5"
|
||||
var attempt = 0
|
||||
while (attempt < 8) {
|
||||
attempt++
|
||||
val have = if (tmp.exists()) tmp.length() else 0L
|
||||
try {
|
||||
val conn = URL(url).openConnection() as java.net.HttpURLConnection
|
||||
conn.connectTimeout = 12000; conn.readTimeout = 60000
|
||||
if (have > 0) conn.setRequestProperty("Range", "bytes=$have-")
|
||||
conn.connect()
|
||||
if (conn.responseCode >= 400) { conn.disconnect(); break } // try next base
|
||||
val resuming = conn.responseCode == 206
|
||||
if (!resuming && have > 0) tmp.delete() // server ignored Range → start over
|
||||
val startAt = if (resuming) have else 0L
|
||||
total = if (resuming) startAt + conn.contentLengthLong else conn.contentLengthLong
|
||||
conn.inputStream.use { input ->
|
||||
java.io.FileOutputStream(tmp, resuming).use { out ->
|
||||
val buf = ByteArray(65536); var got = startAt; var last = 0L
|
||||
var n = input.read(buf)
|
||||
while (n >= 0) {
|
||||
out.write(buf, 0, n); got += n
|
||||
val now = System.currentTimeMillis()
|
||||
if (now - last > 500) { statusFile.writeText("""{"state":"downloading","bytes":$got,"total":$total}"""); last = now }
|
||||
n = input.read(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
conn.disconnect()
|
||||
if (total in 1..tmp.length()) break // complete
|
||||
} catch (e: Exception) {
|
||||
Log.d("BalDav", "tile $tile from $base attempt $attempt at ${tmp.length()}: ${e.message}")
|
||||
statusFile.writeText("""{"state":"downloading","bytes":${tmp.length()},"total":$total,"retry":$attempt}""")
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
conn.disconnect()
|
||||
if (total in 1..tmp.length()) break // complete
|
||||
} catch (e: Exception) {
|
||||
Log.d("BalDav", "tile $tile attempt $attempt stalled at ${tmp.length()}: ${e.message}")
|
||||
statusFile.writeText("""{"state":"downloading","bytes":${tmp.length()},"total":$total,"retry":$attempt}""")
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
}
|
||||
if (total in 1..tmp.length()) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue