77 lines
10 KiB
Markdown
77 lines
10 KiB
Markdown
# MapLibreParticleRNTestHost
|
||
|
||
这是 MapLibreParticleLab 内的独立 React Native 开发宿主,不是 NavSea 业务应用。它只有一个 `MLRNMapView` 与 WeatherRuntime 控制页,用于证明低频控制面:React 的 `reactTag` 经薄 Android 模块定位该 View,再定位该 View 的 `MapLibreMap` 与 map-owned `WeatherRuntime`。
|
||
|
||
## STEP-079 V4 QBIN Golden Fixture
|
||
|
||
`assets/qbin-golden/` 固化了 WeatherV2 的 8 个未压缩 QBIN V2 fixture:Wind、Wave、Current、Rain 各 T0/T1。它们从冻结来源 `/root/sourceserver/weatherV2/_artifacts/MapLibre_QBIN_V2_单元测试数据_v1/` 复制而来;运行时不依赖该来源、HTTP、CDN、NAS 或 gzip。每个 asset 经 `expo-asset` materialize 为本地 `file://` URI,再仅通过 `FieldRuntime.submitEncodedField(...)` 提交。
|
||
|
||
`src/qbinGoldenFixtures.ts` 保留 profile contract、稳定 field identity、validTime、size、SHA-256 和真实 binding。Wind/Current 的 ColorBinding 是 U `VELOCITY_COMPONENT`(semantic 1/group 1/unit 1),不是伪造 `SCALAR`;Wave 是 height `SCALAR` 与 direction particle;Rain 的 particle 明确为 N/A。页面保留全部 V3 控制,并新增独立 V4 dataset/load、T0/T1 结果、binding、seek、PLAY/PAUSE、正速度和 clear 控制。midpoint 只报告 command accepted,RN API 未暴露 mix,不能据此宣称观测到 0.5。
|
||
|
||
开发机可用以下只读校验核验 source 与 bundled 文件的 bytes/SHA-256(不属于运行时依赖):
|
||
|
||
```bash
|
||
python3 integration/react-native/MapLibreParticleRNTestHost/scripts/verify-qbin-golden-fixtures.py
|
||
```
|
||
|
||
## 当前 V3-08 版本与首次 prebuild 边界
|
||
|
||
- 当前验收目标是 RN/TS `13.4.1-particle.3` 配合 Native `cc.navsea.maplibre:android-sdk-particle-opengl:13.4.1-particle.2`。`.2` 是 STEP-069 v2 multi-plane 新 artifact;不得复用或覆盖 `.1`。
|
||
- 本地 legacy `@maplibre-particle/weather-runtime-bridge` 已移除;`@navsea/maplibre-particle-rn@13.4.1-particle.3` 是唯一的 `WeatherRuntimeBridge` 提供者。
|
||
- 必须先由用户手工构建并 Maven Local publish Native `.2`,再对 Test Host 执行 clean prebuild、build、install 与启动。当前仅完成坐标准备,AAR 尚未构建、发布或验收。
|
||
- Metro 当前 binary asset extension 为 `.f32`、`.u8` 与 `.qbin`。
|
||
|
||
- package manager 为 npm(`packageManager: npm@10.9.4`),锁文件为 `package-lock.json`;当前环境的 `npm ci --dry-run --ignore-scripts` 无须 `--legacy-peer-deps` 即可解析。
|
||
- package 明确固定 React Native `0.85.2`、React `19.2.3`、Expo `56.0.4`、`expo-file-system` `56.0.11`、TypeScript `5.9.3`、MLRN `11.3.6`;`app.json` 明确请求 New Architecture。Hermes 的最终启用状态必须在首次 Android prebuild 后检查生成工程。
|
||
- AGP `8.5.0`、Gradle `8.14.3`、Kotlin `2.1.20`、NDK `27.1.12297006`、JDK 17、minSdk 24、compile/targetSdk 36 是本 Test Host 的**人工验收配置目标**,不是尚未生成 Android 工程的已验证结果。首次 prebuild 后必须以实际 Gradle wrapper、AGP/Kotlin plugin、SDK/NDK 与 Hermes/New Architecture 配置为准核对。
|
||
|
||
## 历史:STEP-061/062 Custom SDK 来源(手工执行)
|
||
|
||
Gate C 封存环境负责生成 custom AAR;Test Host 只负责 Maven Local handoff。Expo plugin 对唯一 custom 坐标 `org.maplibre.gl:android-sdk-opengl` 排除 Maven Central,而将 Maven Local 排在首位。因此未先发布本仓库 custom SDK 时 dependency resolution 必须失败,不能悄悄使用官方 SDK;MLRN 的独立 turf/annotation/scalebar 依赖不被这个 SDK-only 排除误伤。
|
||
|
||
```bash
|
||
cd /mnt/sda1/MapLibreParticleLab/platform/android
|
||
# 仅当 checkout 已变化,或下方已核对 AAR 的 SHA-256 不一致时才需要:
|
||
RENDERER=opengl BUILDTYPE=Release make apackage
|
||
# STEP-061 必须先按当前 checkout 重建,且只能发布新的 `13.4.1-step061` 坐标:
|
||
./gradlew :MapLibreAndroid:publishOpenglreleasePublicationToMavenLocal --console=plain
|
||
cd /mnt/sda1/MapLibreParticleLab/integration/react-native/MapLibreParticleRNTestHost
|
||
npm ci
|
||
npx expo prebuild --platform android --clean
|
||
npx expo run:android
|
||
```
|
||
|
||
上述 Android 命令必须在 Gate C 封存 Linux 环境由用户手工执行;本 Step 的 Codex 没有执行。STEP-060C 的 Maven Local publication 已于 2026-09-07 人工通过:`signOpenglreleasePublication SKIPPED`,没有 `no configured signatory`,发布 AAR、POM、module metadata 均存在,且没有 `.asc`。当前 Gate C build output 与 Maven Local AAR 的共同 SHA-256 是 `2e73e25cfd31bb181f75ba2d84974afb949640f72de46bc0234d4579abe3ba38`,含 `WeatherRuntime`/`MapLibreMap` classes 和四 ABI。`47646fb…` 只是 publication 前中间 AAR SHA,不再是当前 artifact。后续只有 checkout 或 AAR 变化时,才重新 build/publish 并核对 provenance;不得以 `-x sign` 绕过签名,Maven Central 正式 publication 仍要求签名。2026-08-29 的旧 AAR(SHA-256 `5c67…f85e5c`)只可作 Gate C 历史证据,不能用于 STEP-060。
|
||
|
||
## Bridge 与生命周期
|
||
|
||
`WeatherRuntimeBridge` 每个命令通过 `UIManagerHelper.getUIManager(...FABRIC).resolveView(reactTag)` 解析 View,并只接受 `MLRNMapView`。它直接读取公开的 `MLRNMapView.mapLibreMap`,不复制 MLRN 的私有 `mViews`,不保存 current map,也没有第二套 timeline/playback state。`MapLibreMap` 持有每个 map 的唯一 `WeatherRuntime`:style replacement、View detach 和 React unmount 都会 dispose 旧 facade;style ready 后 `attach` 创建新的 facade。
|
||
|
||
Style JSON 明确创建 background、host-field `RasterArraySource`、`WeatherColorLayer` 与 `RasterParticleLayer`。Bridge 仅在 source/layers 都存在且 style 已加载时 attach;它不会创建私有 Style registry。错误为结构化 reject:`MAP_VIEW_NOT_FOUND`、`MAP_NOT_READY`、`STYLE_NOT_READY`、`WEATHER_OBJECTS_NOT_READY`、`RUNTIME_DISPOSED`、`INVALID_ARGUMENT` 或 Native `REJECTED`。
|
||
|
||
## Phase A / B 操作
|
||
|
||
页面 style ready 后显示 reactTag/runtime;Phase A 按钮为 `getState`、enable、WeatherColor visible、particle visible、FPS 30。Phase B 还通过 bridge 暴露 epoch-ms forecast seek、PLAY/PAUSE、速度、stops、独立 particle/trail RGBA、粒子视觉参数和 WeatherColor opacity。JS 只校验小参数,并不保存另一套 runtime 状态;snapshot 来自 Native。
|
||
|
||
## STEP-061 Binary U/V Field transport
|
||
|
||
正式 Binary ingress 为 `metadataJson + payloadFileUri/path`。Test Host 使用 Expo FileSystem `File.write(Uint8Array)` 将 raw bytes 写入 app cache;TurboModule 只接收 metadata 和本地 `file://` URI(也接受绝对本地路径),Kotlin 对可读性与 64 MiB 上限 fail-closed 后读取原始 `byte[]`,并且只调用既有 `WeatherRuntime.submitWindField(metadataJson, byte[])`。没有 `number[]`/`Array<number>`、payload JSON、base64、hex 或网络下载层。
|
||
|
||
`Submit binary WIND T0/T1` 固定生产 4×3 的两帧 `WIND/GLOBAL` canonical field:north-west origin、row-major、`interleaved-uv`、`float32-le`,并以 `DataView.setFloat32(..., true)` 显式写入 U=eastward、V=northward。初始状态与每轮开始均为 `PENDING`,绝不预先显示 READY。按钮先通过既有 `clearWindFields()` 清空 Source-owned Timeline,再依序提交一个声明字节数与文件实际字节数不一致的 invalid probe、T0、T1,并严格断言 Core 结果依次为 `REJECTED_ADAPTER`、`ACCEPTED_PENDING_INITIAL_WINDOW`、`INITIAL_WINDOW_READY`,最后 Native `WeatherRuntime.state` 必须为 `READY`;任意不符均显示 `ERROR`。
|
||
|
||
界面严格区分 `Core evidence: INITIAL_WINDOW_READY + runtime READY` 与 producer 已提交的 T0/T1 generation/time identity;后者不是 Native/Core runtime window。MIX 明确为 `Core-owned / not exposed`。Android 最终 T0/T1 真值由既有 logcat `HOST_FIELD_SNAPSHOT_READY` 与 `HOST_FIELD_INITIAL_WINDOW_READY` 验证,本 Step 不新增 Core getter。
|
||
|
||
启动入口是 `App.tsx`。`app.json` 已固定 `orientation: landscape`;下一次 Android prebuild/build 应生成 `MainActivity android:screenOrientation="landscape"`。Android build、安装及设备操作必须由用户手工执行,详见 `docs/60_测试/STEP-060_RNTestHost_Android人工编译与验收_v1.md`。
|
||
|
||
## STEP-062 真实 WeatherV2 综合地图
|
||
|
||
Test Host 固定消费已验收的 `org.maplibre.gl:android-sdk-opengl:13.4.1-step061`,本阶段不构建或发布 AAR。页面 style 顺序是 `background → GSI raster → WeatherColorLayer → RasterParticleLayer`;GSI 使用国土地理院标准地图 `https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png`(256、maxzoom 18、含 attribution),相机初始聚焦日本及周边 WeatherV2 bounds。
|
||
|
||
受控 assets `assets/weather/ecmwf-hres-wind-20260807/` 原样复制 Android Validation 的 `dataset.json` 与四个 `metadata.json`/`field.f32`。`expo-asset` 是明确直接依赖,`metro.config.js` 当前增加 `.f32` 与 `.u8` binary asset extension。点击 `LOAD REAL WEATHER T0-T3` 会先 clear,然后通过 `Asset.fromModule(...).downloadAsync()` 取得四个 Expo cache `file://` URI,按 producer validTime 提交;JS 只传小型 metadata JSON 和文件引用,不 materialize 3.01 MiB payload 为数组、JSON、base64 或 hex。
|
||
|
||
硬断言依次为 T0=`ACCEPTED_PENDING_INITIAL_WINDOW`、T1=`INITIAL_WINDOW_READY`、T2/T3=`ACCEPTED_NEXT_SNAPSHOT`,并在最后要求 Native state=`READY`;任何偏差均显示 `ERROR`。界面提供 PLAY/PAUSE、HALF/NORMAL/DOUBLE、四个 epoch-ms forecast seek、AUTO/30/60 FPS、两层可见性、冻结 WeatherColor stops、独立 particle/trail 色彩,以及粒子/轨迹视觉参数。Expo/系统管理 asset cache;本页不自建 `.f32` 临时文件。
|
||
|
||
## Windows 安装与 Metro reverse
|
||
|
||
Test Host 的 Metro 固定在 8091,但 Debug APK 的 RN dev-server 探测仍从设备 `localhost:8081` 发起。手工安装前以受控脚本 `scripts/05-install-and-run.ps1` 保留两条 reverse:`tcp:8091 -> tcp:8091` 与 `tcp:8081 -> tcp:8091`。脚本固定设备 `192.168.1.146`、package `org.maplibre.particle.rntesthost`、activity `.MainActivity`,并在安装前完成 reverse。
|