8.8 KiB
8.8 KiB
摘除 STF 迁移计划
背景:全舰队设备为 Tailscale IP:5555 直连(adb key 沿用 STF 的),单实例部署。 STF 当前仅提供:occupy/release 互斥、present+ready 健康信号、设备池清单(与 220 的 connect_devices.sh 双维护)、remoteConnect 桥接(IP:port 已禁用)、网页看屏。 任务本体跑 uiautomator2(atx-agent),与 STF agent(jp.co.cyberagent.stf)无关。
摘除收益:告别 agent 安装死锁(MIUI 弹窗)、STF 容器重启风险、occupy 冲突悬空; 调度直接基于 adb 真实现状。风险点:多实例互斥(当前单实例无此需求,见阶段 3)。
已确认的决策(2026-08-17)
- 设备清单放本地 SQLite(data/users.db 新增 devices 表),220 的 connect_devices.sh 退役(其"每 5 分钟补连"职责由平台定时器 + SSH 兜底接管,或直接依赖 adb 重试)
- USB 有线设备保留支持——当前舰队 0 台 USB(全 IP:5555),但能力要保留。
方案:220 的 adb 容器发布 5037 端口(改 docker 运行参数,唯一动 220 的地方),
平台通过 Tailscale 用
adb -H <220> -P 5037和adbutils.Adb(host=220)(u2 底层) 驱动远端 USB 设备;本机 USB 设备照旧走本地 adb。已验证 uiautomator2 3.7.0 底层 adbutils 支持远程 adb server - 网页远程看屏——引入 ws-scrcpy(Node 服务,WebRTC 低延迟流 + 触控), 与平台同机部署、内嵌管理员页面 iframe;本机 adb 即数据源,无需 STF。 备选:自建 minicap JPEG 流 + u2 注入(2-3 天),或保留 STF 容器只做看屏(不推荐,依赖没摘干净)
迁移难度与程度评估(2026-08-17)
代码量
| 项 | 量级 |
|---|---|
删除 core/stf_client.py 及引用 |
~250 行 |
新增 core/device_pool.py(清单+在线状态) |
~150 行 |
| 新增 devices 表 CRUD(复用现有 SQLite 基建) | ~120 行 |
修改 task_manager(resolve_serials/调度) |
~120 行 |
重写 device_worker.STFDevice(去 occupy/release) |
~80 行 |
修改 web_server 约 8 处调用点 |
~60 行 |
改造 stf_device_mgmt(SSH 脚本 → SQLite 管理) |
~100 行 |
| 前端 6 个文件文案/组件 | ~80 行 |
净效果:-600 行 / +400 行 / 改 ~300 行,集中在 task_manager 与 device_worker 两个核心文件。
分项难度(单人)
| 模块 | 难度 | 估算 | 关键点 |
|---|---|---|---|
| 设备池 SQLite + device_pool | 低 | 0.5-1 天 | 表结构 + CRUD + 管理页改数据源 |
| 调度替换 | 中 | 1-2 天 | 内存锁已有(_running[serial]),换数据源 + 对照验证 |
| web_server 调用点 | 低 | 0.5 天 | 8 处机械替换 |
| USB 支持 | 中 | 1 天 | 220 容器发端口 + adb_helper 支持 -H/-P + u2 远程 server;需一台真机验证 |
| 网页看屏(ws-scrcpy) | 中 | 0.5-1 天 | 独立 Node 服务 + 内嵌页 + 认证联动 |
| 界面清理 | 低 | 0.5 天 | 文案/按钮/列 |
| 回归验证 | 中 | 1 天 | 调度/抢占/重试/离线跳过/亮屏/剪贴板/终端/看屏 |
总计约 4-6 人日,分 4 个 commit 阶段,每阶段可独立回滚。
风险
- 中:USB 真机方案当前无设备可验,落地时需借一台 USB 设备
- 低:离线跳过判定语义变化(STF 状态 → adb 状态),个别边界行为可能微变,需对照
- 低:唯一动 220 的地方 = adb 容器发布 5037 端口(一次性)
- 低:ws-scrcpy 是新增 Node 依赖,需评估其维护性与内存占用(可先试用再定)
实施顺序(调整后)
- 阶段 0:SQLite 设备表 +
core/device_pool.py(纯新增,STF 照常跑) - 阶段 1:调度/生命周期替换(STF 保留运行对照验证)
- 阶段 2:USB 远程 adb server 打通 + ws-scrcpy 看屏部署
- 阶段 3:界面清理 + 220 停 STF 容器(
docker stop不删,可回滚)
阶段 0:新建设备池模块(纯新增,不改行为)
新建 core/device_pool.py + devices 表(data/users.db):
- devices 表:serial(主键)、name、enabled、note、created_at;管理页增删改
list_configured()— 读 SQLite devices 表(取代 SSH 读 220 脚本)list_online()— 本机adb devices中 state=device 的设备(实时)is_online(serial)— 在 list_online 中;可选adb -s get-state兜底(带超时)list_ready()— list_configured 与 list_online 的交集(取代 STF list_free_devices)- 全模块不 connect/不 kill-server/disconnect,遵守既有红线
- 迁移脚本:现有 8 台设备从 STF 池导入 devices 表
产出:core/device_pool.py + doc/ARCHITECTURE.md 设备章节初稿 + 数据迁移脚本。
阶段 1:替换调度与生命周期(STF 保留运行,可对照验证)
| 位置 | 现状 | 改为 |
|---|---|---|
task_manager.resolve_serials (145-183) |
stf.list_free_devices() / list_all_devices() |
device_pool.list_ready();preempt 模式 = 全部 list_online |
resolve_serials skip_offline 过滤 (166-183) |
STF present/ready 判定 | device_pool.is_online();跳过原因文案"离线/未连接" |
device_worker.STFDevice.acquire (45-70) |
stf.occupy() + 直连/桥接 |
去掉 occupy 与桥接分支(IP:port 直连保留,USB 序列号仅提示不支持);保留 adb_connect 重试 + 2s 等待 |
STFDevice.release (78-93) |
stf.release() |
置空(互斥由 _running[serial] 负责,本来就是内存锁) |
STFDevice._pick_free (72-76) |
list_free_devices |
device_pool.list_ready() |
_ForegroundScanner (234-386) |
list_all_devices / list_my_devices / remote_connect |
device_pool.list_online();桥接分支删除 |
web_server.api_device_screen_all (646) |
STF present 列表 | device_pool.list_online() |
web_server 设备列表 (924 / 1011 / 1079) |
STF 池合并 | SQLite devices 表 + 本地 adb(_merged_device_list 改数据源,保留 stf_not_ready 状态的等价物"未连接") |
web_server.api_release (740) / 启动清理 (1581) |
release_all_mine | 删除或改为清理本实例 _running 状态(重启本就清零) |
验证清单(阶段 1 完成后全跑一遍):
- 任务调度(serial/group/all 三种模式)+ 抢占/归还 + 重试 + 离线跳过
- 一键亮屏/息屏、剪贴板注入、应用版本查询、维护终端设备列表
- 前台 App 扫描
- 对照 STF 状态确认无行为差异
阶段 2:USB 打通 + 网页看屏(阶段 1 稳定运行 ≥ 3 天后)
- USB 远程 adb server:
- 220 侧一次性改动:adb 容器发布 5037 端口(
docker run -p 5037:5037或 iptables), Tailscale 可达 core/adb_helper._adb支持-H <220-tailscale-ip> -P 5037(仅 USB 序列号设备)- u2 连接:USB 设备走
adbutils.Adb(host=220_ip, port=5037)→u2.Device(dev)(已验证 u2 3.7.0 底层 adbutils 支持远程 adb server) - 本机 USB 设备照旧走本地 adb,零改动
- 需要一台真机 USB 设备验证
- 220 侧一次性改动:adb 容器发布 5037 端口(
- 网页看屏(ws-scrcpy):
- 平台机部署 ws-scrcpy(Node 服务,WebRTC 低延迟流 + 触控),本机 adb 为数据源
- 管理员页面新增「远程看屏」入口,iframe 内嵌;登录态复用(ws-scrcpy 自身鉴权 走 token 或仅内网/Tailscale 暴露)
- 备选方案(若 ws-scrcpy 试用不达标):自建 minicap JPEG 流 + u2 触控注入(2-3 天)
阶段 3:界面清理 + STF 停用(阶段 2 稳定后)
templates/admin/monitor.html:删「一键重启 STF 容器」区块 (460-468)、设备表 STF 占用列 (328); 「STF 设备管理」子分栏改名为「设备池管理」(增删改走 SQLite,SSH 部分退役)- 维护终端:删「卸载/检查 STF agent」按钮(agent 已无用);如需补「重启 u2 atx-agent」 (u2 断开重连会自动重推 atx-agent,一般无需手动)
- 文案清理:tools.js / apps.js / editor.js 中"STF 池"→"设备池",
api/adb/devices的 source 字段stf→pool - 删除
core/stf_client.py、STFClient全部引用;config.py中 STF_URL/STF_TOKEN 标注废弃 - 220 侧:停 STF 容器(
docker stop stf,不删除);connect_devices.sh cron 退役, 补连职责由平台侧定时器(对池内设备 adb connect 检查)接管 - 更新
doc/ARCHITECTURE.md/doc/DEPLOY.md相关章节
阶段 4(可选,未来多实例/多机时)
跨实例互斥:SQLite 行锁替代 occupy——
UPDATE device_locks SET owner=?, ts=? WHERE serial=? AND (owner IS NULL OR owner=?)
回滚方式
- 每个阶段一个 commit,问题可整体
git revert;STF 阶段 3 前一直运行,随时回切 - 阶段 3 的 STF 容器是
docker stop而非删除,回滚 =git revert+docker start stf - 每个阶段 commit 后在 dev 观察 ≥3 天再进下一阶段