346 lines
25 KiB
Markdown
346 lines
25 KiB
Markdown
# MAG160C 安卓统一 APP — 会话状态与心跳锚点
|
||
|
||
> 本文件是本任务("整合 4 个官方 APP 重写为 Android 16 现代化 APP")的心跳锚点。
|
||
> 每完成一个里程碑更新勾选;会话中断先读本文件 + `reverse_apk_features.md`,
|
||
> 再按"下一步"继续。约定:不弹询问窗,按计划自主推进,重大分歧点才停下来问。
|
||
|
||
## 任务目标
|
||
|
||
1. 逆向 C:\Project\MAG160C\app 4 个官方 APP(普通版/专业版/ThermoScope/demo)→ 已完成
|
||
2. 整合全部功能开发一款安卓 APP:适配 Android 16(API 36)、现代 UI、横竖屏、
|
||
无流氓文件夹、干爽简洁 → **进行中**
|
||
3. 背景问题:专业版在用户 Android 16 手机上花屏(根因见 reverse_apk_features.md §6)
|
||
|
||
## 已完成的里程碑
|
||
|
||
- [x] 工具链:jadx 1.5.1(C:\Tools\jadx)、Ghidra 12.1.2(C:\Tools)、IDA MCP 可用
|
||
- [x] 4 APK 解包 + jadx 全量反编译(%TEMP%\opencode\apkwork\jadx_*)
|
||
- [x] 普通版/专业版/ThermoScope/demo 功能清单 → docs/android_app/reverse_apk_features.md
|
||
- [x] IDA:pro libcoresdk.so GetOutputImage/copyBitmap 位图格式证实(ARGB_8888)
|
||
- [x] Ghidra:libcxsdk.so 导出 C++ API 清单(CFunctions 族,与 ARM64 coresdk 同源)
|
||
- [x] 花屏根因分析(32 位专属库 + targetSdk25 legacy 渲染栈)
|
||
|
||
## 关键事实速查(开发时直接用)
|
||
|
||
- USB:VID 0x833C PID 1,EP 0x03/0x82/0x81/0x84,帧 28B 头+38400B+尾,15fps
|
||
- 协议/渲染/温度算法全部已逆向:analysis/protocol_spec.md + csdk/(逐像素验证过的官方管线)
|
||
- csdk 的 mag160c_render 无平台依赖:USB 帧 → 320×240 RGB24 + 探针测温(毫度)
|
||
- DDT 标定文件:可从设备经 0x6BB6B66D/E 下载(官方 prepareProcessImage 同款),仓库有
|
||
build-artifacts/mag160c_official.ddt(1.8MB,SN 160043865)
|
||
- MDT 文件格式:reverse_apk_features.md §4(152B Tail + 5 段布局 + ROI 256B 记录)
|
||
- 温度:毫度 int;MDT 解码走 ConvertResponse2Temperature + T2E(Revise) + CorrectTemperature
|
||
|
||
## 用户已确认的范围(2026-09-06)
|
||
|
||
- 云功能:预留 Retrofit 接口模块,默认关闭
|
||
- 扫码打标 + 任务巡检(sqlite/xml) + 红外基准图叠加:v1 全部实现
|
||
- **网络互连(用户新增需求)**:主机手机插 USB 热像仪,局域网内另一台手机
|
||
远程实时预览——UDP 广播自动发现 + 手动添加内网 IP;自定义轻量协议
|
||
(控制通道 JSON + 图像流),不走厂商 33596/33597 协议
|
||
|
||
## 当前阶段
|
||
|
||
**阶段 2/8 完成:纯Kotlin核心管线移植并逐字节验证(2026-09-06)**
|
||
|
||
## 已完成里程碑(本任务)
|
||
|
||
- [x] 阶段1:轻量工具链定案——NDK/CMake/zig 全部卸载(-2.4GB),纯 Kotlin 方案:
|
||
渲染管线+温度算法+帧解析从 C 移植为 Kotlin,Gradle 8.14.3 + SDK 已有组件
|
||
- [x] 阶段2:项目骨架(android/,AGP 8.11.1 + Kotlin 2.2.0 + Compose BOM,
|
||
compileSdk/targetSdk 36,minSdk 26),最小APK可构建
|
||
- [x] 阶段2:表数据自动生成 analysis/gen_kotlin_tables.py → OfficialTables.kt
|
||
(palette256 ARGB / T2E 646 / T2E274 / E2T_ACC_Q10)
|
||
- [x] 阶段2:核心移植 FrameStream / RenderPipeline / TempMath(毫度测温)
|
||
- [x] 阶段2:PC端参考工具 analysis/render_offline.c(gcc 编译)+ 差分测试:
|
||
**Kotlin 输出与C参考逐字节一致**(60帧序列,首渲染帧32,NUC/LUT/灰度/输出全同)
|
||
- [x] 阶段2:FrameStream 分块重组测试 + 温度换算单调性测试全过
|
||
|
||
## 移植陷阱记录(教训)
|
||
|
||
- C 无符号32位回绕:乘积/减法必须 u32() 掩码(cdf*denom、0xffc0000-iv7*0x40000、
|
||
(v-win_lo)*S 等)
|
||
- lutRebuild 的 u12 基址是常量 iv7*0x10+0x10,**不是**链式 u21v;u21v 只用于曲线钳制比较
|
||
- Kotlin ByteArray 存 255 = -1:lut[bfmax] != 0xFF 判断必须 (b.toInt() and 0xFF)
|
||
- 暖机窗口内 FFC 状态机暂停(warm check 在 ffc_step 之前)——移植时保持顺序
|
||
|
||
## 用户反馈修复(2026-09-07)
|
||
|
||
- [x] **中文乱码根因**:部分源码曾被python以GBK重写(AppRoot.kt),编译出的字符串
|
||
mojibake。已写 fix_encoding.py 全量转UTF-8 + kotlin.daemon.jvmargs 锁UTF-8 +
|
||
dex字节级验证(classes5/9.dex 中"媒体库"UTF-8正确)
|
||
- [x] **权限弹窗**:Manifest 加 USB_DEVICE_ATTACHED intent-filter + device_filter
|
||
(插热像仪系统自动弹权限/启动);相册页运行时申请 READ_MEDIA_IMAGES
|
||
(模拟器实测弹窗正常)
|
||
- [x] **图标**:全部重绘为可验证几何图形(双弧圆/直线/矩形),横竖屏截图确认渲染正确
|
||
- [x] **专业版三大功能补齐**:多点测温(点击加Pt1..PtN、再点删除)、右侧色标条
|
||
(渐变+最高/最低温标注)、最高温追踪开关(追踪·开/关)
|
||
- [x] **演示模式**:无设备时用真实管线+DDT渲染合成帧(热块场景),全UI可探索;
|
||
demo温度用局部线性显示映射(真机温度走管线数学)
|
||
- [x] Android 16.1 模拟器(Medium_Phone AVD)实机验证:中文/图标/布局/横竖屏正常,
|
||
预览图在 analysis/preview/preview_*.png
|
||
|
||
## 用户反馈修复 第二轮(2026-09-07)
|
||
|
||
- [x] 竖屏时热像图旋转90°竖着显示(填满屏宽);横屏时保持传感器原生4:3方向
|
||
填满屏高——两个方向下显示区域都最大化(用户要求的官方行为)
|
||
- [x] 横屏:控制栏移到屏幕右侧(竖列),竖屏保持底部横排6项等分——切换时
|
||
不再跳位
|
||
- [x] 追踪/测温点标记改为大号"圆环+实心点"图标,字高随密度缩放(更大)
|
||
- [x] 色标条放大(宽20dp、高0.8视口)+更大刻度字号
|
||
- [x] ScreenOrientation=fullSensor:跟随传感器旋转(系统旋转锁定时也转,
|
||
与官方普通版一致)
|
||
- [x] **SurfaceView旋转存活**:AppRoot/LiveScreen改为稳定单分支布局(导航/控制
|
||
栏做成overlay),SurfaceView不再因横竖屏切换被销毁重建(此前横屏花屏空
|
||
白的根因)
|
||
- [x] 模拟器实测:竖屏/横屏截图确认全部生效(analysis/preview/)
|
||
|
||
## 用户反馈修复 第三轮(2026-09-07)
|
||
|
||
- [x] **布局重构**:控制项(FFC/变倍/追踪/调色板/拍照/录像/调色板名)移到**顶部横栏**,
|
||
横竖屏都显示;底栏导航(实时/相册/分析/设置)保持原位置(竖屏底部栏/横屏左侧栏)
|
||
- [x] **图像内容世界固定**:绘制角度 = (90 - 显示器rotation×90) mod 360,
|
||
四向旋转(竖屏/左横/右横/倒竖屏)图像内容都不随手机转,只转图标文字
|
||
(修复"向右横屏图像颠倒"问题)
|
||
- [x] **状态栏沉浸**:insetsController 隐藏状态栏(下滑临时显示),底栏导航仍可用
|
||
- [x] **inset感知渲染**:渲染器 viewport = 全屏减顶栏/底栏inset(AppRoot横屏时
|
||
导航栏宽度不再误算为底部inset)
|
||
- [x] 顶栏通过 onSizeChanged 上报 vm.uiTopPx;导航 overlay 高度经 UiInsets 单例
|
||
同步 vm.uiBottomPx
|
||
- [x] **(重要教训)python 裸写文件会用 GBK**:所有 python 编辑必须显式
|
||
encoding='utf-8';本次 LiveScreen.kt 双重乱码已重写恢复,dex 验证 CJK 正常
|
||
- [x] 模拟器验证:顶栏/左导航/图像/色标/标记布局正确,中文正常,状态栏隐藏生效
|
||
|
||
## 用户反馈修复 第四轮(2026-09-07,最终布局约定)
|
||
|
||
- [x] **顶栏/底栏位置固定**:顶栏=控制项(FFC/变倍/追踪/调色板/拍照/录像)恒在顶部;
|
||
底栏=导航恒在底部。横竖屏都不移动(**取消横屏左栏**),图标文字跟随屏幕方向(恒可读)
|
||
- [x] **图像区域永不旋转**:图像恒 90°CW 绘制为 3:4 竖向,填满顶栏~底栏之间可用区,
|
||
手机怎么转都不动;只转标记文字(文字恒屏幕水平,位置经 probeToScreen 固定90°映射)
|
||
- [x] **顶栏适配挖孔屏**:`WindowInsets.safeDrawing.only(Top)` 顶部安全区 padding
|
||
- [x] 去掉 display-rotation 依赖(uiRotation/drawRotationDeg 已删,tapImage/probeToScreen
|
||
用固定 90° 映射)
|
||
- [x] 完整交接文档:docs/android_app/HANDOFF_DEVELOPMENT.md(含给新模型的读取提示词)
|
||
|
||
## 用户反馈修复 第五轮(2026-09-07,布局约定最终定稿:构图钉死竖屏框架)
|
||
|
||
- [x] **用户澄清**:"顶栏底栏位置不变"指**绝对位置**——永远贴着手机竖屏的物理顶边
|
||
(挖孔侧)与物理底边;热像区域也**完全不动**。不是"横屏后显示区域跟着屏幕转":
|
||
那样屏显方向会和镜头实际方向对不上。
|
||
- [x] **根因与修复**:此前 fullSensor 下渲染"相对当前屏幕固定 90°CW",手机横过来时
|
||
屏幕本身转 90°,整个构图相对手机框架也转 90° → 与镜头方向脱节。修复 =
|
||
**Activity 锁定竖屏**(manifest `screenOrientation="portrait"`):屏幕相对手机框架
|
||
永不旋转,构图(顶栏/热像区/底栏)永远钉在竖屏框架上;手机怎么物理旋转都一样。
|
||
- [x] 顺带清理:GalleryScreen 去掉 isLandscape() 死逻辑(恒 3 列);
|
||
AppRoot/LiveScreen/LiveRenderer/UiInsets 注释同步更新。
|
||
- [x] 模拟器四方向实测(0/90/180/270°,`adb emu rotate`):**四个方向截图逐字节一致**
|
||
(MD5 相同)——构图绝对固定;演示画面/顶栏/底栏/色标/标记/中文均正常
|
||
(analysis/preview/preview_rot*_v5.png)。
|
||
- [x] 单元测试全过;APK 已更新 build-artifacts/mag160c-app-debug.apk。
|
||
- 注:不要再改回 fullSensor/sensor 横屏;横竖屏适配类需求一律以"竖屏构图恒定"为准。
|
||
|
||
## 用户反馈修复 第六轮(2026-09-07,横屏持机图标文字可读)
|
||
|
||
- [x] **用户反馈**:竖屏锁定后,横过来拿手机时顶栏/底栏里的图标和文字不旋转(侧着)。
|
||
约定补全:**构图钉死竖屏框架不变**(条栏绝对位置+热像区域不动),但条栏内容
|
||
(图标+文字)与 OSD 文字要按**物理持机朝向**补偿旋转,保持可读。
|
||
- [x] 新增 `ui/DeviceOrientation.kt`:加速度计→手机相对竖屏的顺时针物理转角
|
||
φ∈{0,90,180,270}(主轴判定+2.5m/s² 滞回;竖屏锁定下 Display.rotation 恒 0 不可用)。
|
||
- [x] 顶栏 7 控制项/底部导航 4 项:`graphicsLayer rotationZ=-φ` 原位预旋转(布局不动)。
|
||
- [x] 渲染器 OSD 文字(中心温/探针标注/色标最高最低数字)`canvas.rotate(-φ)` 绕锚点
|
||
旋转;标记圆点、色标条几何仍钉死在图像上。
|
||
- [x] 模拟器实测(`adb emu sensor set acceleration` 驱动 4 姿态):四姿态下热像区域
|
||
完全一致,图标/文字按姿态正确补偿(analysis/preview/preview_pose*_v6.png);
|
||
姿态复位 0° 后输出与第五轮逐字节一致(MD5 相同)。
|
||
- [x] 单元测试全过;APK 已更新。
|
||
- 注:对话框(调色板等)与其他页签内容未做补偿旋转(保持竖屏可读),如需再加。
|
||
|
||
## 用户反馈修复 第七轮(2026-09-07,真机朝向全反修复)
|
||
|
||
- [x] **用户反馈**:真机正持竖屏时屏幕内容倒立,其他方向也全反。
|
||
- [x] **根因**:第六轮把加速度计符号约定写反。Android 真机 TYPE_ACCELEROMETER
|
||
静止读数 = "加速度减重力",指向世界上方向(平放屏幕朝上 z=+9.81、竖屏正持
|
||
y=+9.81);模拟器虚拟传感器却是重力向量约定(正持 y=-9.81),故模拟器测试
|
||
"通过"而真机全反(所有姿态差 180°)。
|
||
- [x] **修复**:DeviceOrientation 映射改为 (0,+g)=0、(-g,0)=90、(0,-g)=180、(+g,0)=270;
|
||
代码注释明确标注"勿按模拟器默认值改回"。模拟器用反号值复测:姿态 0 恢复正常
|
||
正持显示,90/180/270 补偿几何不变(preview_fix_pose*_v7.png)。
|
||
- [x] 单元测试全过;APK 已更新。
|
||
|
||
## 用户反馈修复 第八轮(2026-09-07,相机式按键布局)
|
||
|
||
- [x] **顶栏精简为 4 个相机控制项**(左→右,带小字标签,随持机朝向补偿旋转):
|
||
FFC 校正 / 数码变倍(显示当前 1×/2×/4×,点击循环)/ 最高温追踪开关(追踪·开,
|
||
高亮+新 ic_target 准星图标)/ 调色板(显示当前调色板名,点击弹出选择)。
|
||
- [x] **底部新增相机快门区**(仅实时页,位于底导航上方,白/红色调):
|
||
相册快捷入口(跳转媒体库页签)/ 大圆形快门=拍照 / 录像-停止(红圈,录像中
|
||
红圈内容变红色方块)。快门区高度并入渲染器底部 inset(vm.uiBottomPx =
|
||
导航高+快门区高),测温点点击映射同步排除该区域。
|
||
- [x] 底部导航(实时/相册/分析/设置)不变;快门区内容同样按持机朝向补偿旋转。
|
||
- [x] 模拟器实测:0°/90° 姿态布局、旋转补偿、图像区域均正确
|
||
(preview_ui_v8.png / preview_ui90_v8.png);单元测试全过;APK 已更新。
|
||
|
||
## 用户反馈修复 第九轮(2026-09-07,真机热像顶端被顶栏遮挡)
|
||
|
||
- [x] **用户反馈**:真机上快门区把热像显示区顶满后,热像最顶端被顶栏盖住看不见。
|
||
- [x] **根因**:顶栏 `onSizeChanged` 挂在 `safeDrawing` 挖孔 inset 内侧,`uiTopPx`
|
||
不含挖孔安全区高度;模拟器无挖孔看不出来。快门区加入后剩余高度变小、热像
|
||
进入"高度受限"铺满视口状态,顶端误差直接表现为热像顶端插入顶栏背后。
|
||
- [x] **修复**:`onSizeChanged` 移到 `windowInsetsPadding` 之前(`uiTopPx` = 挖孔
|
||
安全区 + 顶栏全高,渲染器视口完整跳过顶栏);同时按要求缩小快门区
|
||
(相册/录像 42dp、快门 60dp、纵向 padding 6dp、间距 44dp)。
|
||
- [x] 模拟器复测布局正常,单元测试全过;APK 已更新。
|
||
|
||
## 用户反馈修复 第十轮(2026-09-09,撤下演示热像图)
|
||
|
||
- [x] **用户要求**:撤下用于测试的合成热像图(演示模式),准备接真机+热像仪实测显示。
|
||
- [x] LiveViewModel:`connect()` 无设备时不再启动演示渲染,改为 `status="no_device"`
|
||
(LiveScreen 既有占位文案"未检测到热像仪,请插入MAG160C",渲染器无帧即黑底);
|
||
删除 startDemo/buildDemoFrames/demoPipeline/demoNuc 与 refreshTemps 演示分支
|
||
(连带消除"演示循环与真机流同时写 latestFrame、演示温度覆盖真机温度"的隐患)。
|
||
勿把演示模式加回来。
|
||
- [x] 工具链在本机重装并重建 APK(2026-09-09):JDK Temurin 21 `C:\Tools\jdk-21`、
|
||
SDK `C:\Tools\android-sdk`(platform 36 / build-tools 36.0.0 / platform-tools,许可已接受)、
|
||
Gradle 用工程 wrapper(8.14.3);交接文档旧路径 C:\Tools\gradle-8.14.3 / Zulu /
|
||
AppData SDK 属另一台开发机已失效(HANDOFF §2/§5 已改为本机路径)。
|
||
assembleDebug+test 全过;dex 抽查中文 UTF-8 正常、无 startDemo 残留;
|
||
build-artifacts/mag160c-app-debug.apk 已更新(11.8MB),可直接装真机。
|
||
- 待办不变(真机 USB 实测温度标定、网络互连远程预览、离线MDT温度解码、
|
||
调色板精确提取、PIP/云模块)。
|
||
|
||
## 用户反馈修复 第十一轮(2026-09-09,真机黑屏:日志落盘 + 修复缺失 START)
|
||
|
||
- [x] **用户反馈**:真机插热像仪有 USB 弹窗,但实时画面黑屏;要求把 debug 日志
|
||
保存到热成像相册目录,运行后把文件发回分析。
|
||
- [x] **黑屏根因(代码审查定位)**:`IrSession.start` 移植时漏掉了硬件已验证的
|
||
启动序列(csdk `mag160c_ir.c`):读循环前必须 FFC(0)×2 → 300ms → START(73);
|
||
旧代码只发 66b/66c/66f 就进读循环,相机从未出流 → EP 0x81 静默 → 黑屏。
|
||
已按 C 参考补上(50ms → FFC(0)×2 → 300ms → START)。
|
||
- [x] **新增 `media/DebugLog.kt`**:每次 connect() 在 MediaStore 建一个
|
||
`debug_yyyyMMdd_HHmmss.log`(DCIM/MAG160C,text/plain,API29+ 无需权限,
|
||
相册/文件管理器可见),逐行 flush;同时镜像到 logcat(tag `MAG160C/*`);
|
||
MainActivity 装崩溃钩子(栈回写文件后再交前 handler)。
|
||
- [x] **全链路插桩**:UsbTransport(设备列表/权限结果/openDevice/claim/端点表)、
|
||
IrSession(命令交换 write/read/响应 magic+头16字节、首3次读头24字节、
|
||
前5帧 type/shutter、每2s 心跳 stats reads/frames/rendered/timeouts/fps/
|
||
renderState/ref、STOP)、LiveViewModel(connect/权限/首帧到达/5s 心跳)。
|
||
- [x] 状态文案补全:open_fail/no_endpoints/exception:* 在 LiveScreen 显示
|
||
具体提示(原先一律"连接中…",掩盖故障)。
|
||
- [x] 构建+单测全过;dex 抽查中文/日志器/启动序列字符串正常;APK 已更新
|
||
(11.87MB)。调试日志要点:若仍黑屏,看文件里 `stats: reads=?` ——
|
||
reads=0 → START 被无视(查 cmd 日志);frames>0 rendered=0 →
|
||
warm/FFC/ref 窗口(ref=false 持续 → 66c 响应异常);rendered>0 仍黑屏 →
|
||
UI/渲染层问题(hb: uiFrames=?)。
|
||
- 注:调试日志文件由 MediaStore Files(非 Images)写入,部分相册 app 不显示
|
||
text/plain,可用系统"文件"应用或 PC 复制 DCIM/MAG160C/debug_*.log。
|
||
|
||
## 用户反馈修复 第十二轮(2026-09-10,打开即闪退:日志落点修复)
|
||
|
||
- [x] **用户反馈**:第 11 轮 APK 装好后直接打开就闪退。
|
||
- [x] **根因**:`DebugLog.startFile` 用 `MediaStore.Files` 往 DCIM/MAG160C 插
|
||
text/plain 文件;scoped storage 规定 DCIM 只收图片/视频,非媒体文件被拒,
|
||
`insert()` 抛 IllegalArgumentException,而该调用在 `LaunchedEffect →
|
||
connect()` 协程里无 try/catch → 启动即崩。
|
||
- [x] **修复**:DebugLog 全链路 try/catch 永不抛异常;落点三级回退
|
||
DCIM/MAG160C → Download/MAG160C(非媒体允许目录)→ 应用私有外部目录;
|
||
文件头写实际落点 `sink=`;日志 4MB 封顶。`startFile` 提前到
|
||
MainActivity.onCreate(启动崩溃也有记录);`connect()`/权限回调整体
|
||
try/catch,失败置 `connect_fail`(statusText 显示"连接流程异常")。
|
||
- [x] 构建+单测全过,dex 抽查通过;APK 已更新(11.87MB)。
|
||
- 注:取日志时 DCIM/MAG160C 和 Download/MAG160C 都看一眼(文件头 sink= 注明
|
||
实际位置);若在前者失败会自动落后者。
|
||
|
||
## 用户反馈修复 第十三轮(2026-09-10,真机日志分析:端点静默 + 重枚举)
|
||
|
||
- [x] **用户回传 debug 日志**(vivo V2509A, Android16):相机识别/DDT/66b 响应全正常
|
||
(SN 160043865),但 66c/66f 响应超时、FFC/START 第一轮写失败(write=-1,
|
||
端点疑似 halt)、流端点 19 秒 0 字节;00:51:10 相机重新枚举(权限再次弹出)
|
||
——相机在会话中途疑似断电/复位;且新旧两个 Activity 的会话同时在抢同一设备
|
||
(claimInterface 互相夺走)。
|
||
- [x] **IrSession 加固**:流端点改 **UsbRequest 异步**(API30+,position=字节数;
|
||
低版本回退同步 bulkTransfer);任何 transfer 失败先 GET_STATUS 诊断 +
|
||
**CLEAR_FEATURE(HALT)** 再重试一次;66b/66c/66f 响应读取缩短为 400ms 且
|
||
失败不阻断(与 C 参考一致);**会话互斥**(companion active,新会话先停旧
|
||
会话);stop() 时序修复(STOP 在 close 之前发,由 streamLoop 收尾)。
|
||
- [x] **LiveViewModel**:注册 USB **DETACHED** 接收器(VID 匹配→停会话+no_device);
|
||
connect() 800ms 去抖;流 10 秒 0 数据 notify `no_stream_data`
|
||
(LiveScreen 屏显"已连接但无数据流")。
|
||
- [x] 构建+单测全过;APK 已更新(11.87MB)。
|
||
- ⚠️ **下一步排查(若仍无流)**:相机中途重枚举强烈怀疑 **OTG 供电不足**
|
||
(vivo 口限流 → 相机 MCU 帧处理起来后掉电复位)。请用户:① 换一根好点的
|
||
短线/带供电的 OTG 转接器再试;② 在同一台手机装官方普通版 MAG-Cx 对照
|
||
(若官方也掉,就是供电/兼容问题,与我们的代码无关);③ 新日志看
|
||
`usb detached` 与 `get_status halted=` 行。
|
||
|
||
## 用户反馈修复 第十四轮(2026-09-10,端点 halt 真凶确认:usbfs 超时遗留)
|
||
|
||
- [x] **用户回传第二轮日志 + 关键信息"官方软件正常"**:每次 clear_halt 都成功
|
||
(rc=0)但下一次读仍 -1 且 halted=1 → 不是相机 STALL,而是 **Linux/Android
|
||
usbfs 在 bulkTransfer 超时后把端点标记 halted,后续传输全部瞬间失败**。
|
||
流端点首次 500ms 读超时(相机 1~2s 才开始出流)→ 0x81 被标 halted →
|
||
之后 19 秒的读全是瞬间失败,帧全被错过。66c/66f 响应读不到同机理
|
||
(C 参考在 PC 上也忽略这些失败,完全吻合)。官方 App 正常 → 排除供电问题。
|
||
- [x] **修复(核心一行)**:流循环每次 -1 后**无条件 GET_STATUS + CLEAR_HALT**
|
||
(节流日志),端点永远保持可用;相机 1~2 秒启动延迟期间的若干次超时
|
||
不再杀死数据流。去掉异步 UsbRequest(usbfs 超时对 async 同样留 halt,
|
||
无收益);5 秒零数据自动重发一次 START 兜底;流内 FFC 响应读缩短为
|
||
400ms(不再阻塞渲染线程 2 秒)。
|
||
- [x] 第 13 轮的会话互斥/DETACHED 恢复/去抖已验证生效(日志见 "stopping
|
||
stale previous session")。
|
||
- [x] 构建+单测全过;APK 已更新(11.87MB)。
|
||
- 预期:本轮装上后,日志应出现 `first reads [0] n=...` 与递增的 frames/rendered,
|
||
画面出图。若 timeouts 持续上涨且 reads=0,再看 5s re-kick 与 halted 状态。
|
||
|
||
## 待办
|
||
|
||
- 真机USB实测(温度绝对值标定、FFC/录像/MDT保存端到端)——进行中:
|
||
第13轮加固 APK 待用户复测;若仍无流,按上方供电排查三步走
|
||
- 网络互连远程预览(用户需求:UDP自动发现+手动IP)
|
||
- 离线MDT温度解码(ConvertResponse2Temperature+标定参数,待真机文件对照)
|
||
- 厂商12调色板精确提取(需运行时抓取)
|
||
- 可见光PIP融合、云模块(预留结构)
|
||
|
||
## 里程碑日志
|
||
|
||
- 阶段3b 完成(2026-09-06 晚): 拍照 MDT 容器(jpg+info块+raw帧+备注)存入
|
||
MediaStore DCIM/MAG160C;MP4 录像 = Surface 编码 H.264 @320×240 15fps 2Mbps
|
||
- 阶段4 完成: 媒体库(MediaStore 扫描 + 内嵌 JPEG 缩略图 + MDT 尾部校验)
|
||
- 阶段5a 完成: 分析查看器(detectTransformGestures 缩放1-4x/平移、12 调色板
|
||
重渲染原始帧、自动窗口、备注编辑回写容器、温度探针近似)
|
||
- 阶段5b 完成: PDF 巡检报告(PdfDocument:标题/7×4信息表/热像图/结果/建议/测试员)
|
||
- 阶段6a 完成: 设置页(默认调色板/发射率/报警温度/关于,SharedPreferences 私有)
|
||
+ 任务巡检解析器 TaskParser(sqlite task/region/device/part/phase + XML title/target)
|
||
|
||
## 阶段3a 完成(2026-09-06 晚)
|
||
|
||
- [x] USB传输层 usb/UsbTransport.kt(VID 0x833C 枚举/权限/claim/EP查找)
|
||
- [x] 命令协议 usb/MagProtocol.kt(4B命令+FFC 8B;0x5BB5B55B 信息块解析)
|
||
- [x] 直播会话 usb/IrSession.kt(66b/66c/66f → FFC(0)x2 → 300ms → START(73),
|
||
读线程→FrameStream→RenderPipeline→帧回调;STOP(74);手动FFC)
|
||
- [x] 官方12调色板 core/Palettes.kt(铁虹=官方提取表,其余为标准曲线近似)
|
||
- [x] UI骨架:底部导航4区(实时/相册/分析/设置,横屏自动切侧栏 NavigationRail)
|
||
- [x] 实时画面 ui/live/:SurfaceView+软件Canvas渲染(letterbox+数码变倍+OSD
|
||
中心温/最高最低温标记),ViewModel驱动,慢速测温定时器
|
||
- [x] DDT标定文件打包进 assets/mag160c.ddt
|
||
- [x] 全APK assembleDebug 成功(11.7MB,无material-icons膨胀)
|
||
- [ ] 待实机验证USB流(需插设备)
|
||
|
||
## 开发计划(待确认后逐阶段执行)
|
||
|
||
- 阶段 1:环境(cmdline-tools/NDK r27+/gradle 8.x + 16KB 对齐配置 + AGP 8.x)
|
||
- 阶段 2:新仓库 app-android(Kotlin + Compose M3 + NDK 移植 csdk 渲染/温度 C 代码 + Kotlin USB 层)
|
||
- 阶段 3:实时画面(USB 流+官方管线+OSD+ROI+调色板+FFC+增强+PIP 可见光+拍照 MDT+MP4 录像)
|
||
- 阶段 4:媒体库(MediaStore,DCIM/MAG160C,无流氓文件夹)
|
||
- 阶段 5:MDT 离线分析(缩放/ROI/参数/PDF 报告/备注)
|
||
- 阶段 6:设置/任务巡检/可选云(Retrofit opt-in)
|
||
- 阶段 7:Android 16 专项适配验收(16KB 对齐/横竖屏/深色/预测性返回)
|
||
- 阶段 8:打包安装到用户手机实测
|
||
|
||
## 心跳约定
|
||
|
||
- 每完成一个阶段:更新本文件勾选 + git commit(不 push)。
|
||
- 中断恢复:读本文件 → 按"下一步"继续 → 环境检查命令:
|
||
```powershell
|
||
Test-Path C:\Users\ZXC\AppData\Local\Android\Sdk\ndk\<ver>
|
||
Get-Command java; Get-Command gradle
|
||
```
|