feat(任务): 录制手势改成**纯录制回放**(真手指轨迹)+ 手机端 getevent 录制
用户反馈:录制出来的滑动又被套上滑动那套逻辑(方向/幅度/拟人重新生成),
"导致滑动还是不顺畅"。录制就该是录制——按录下的路径与时间原样重放。
- 新步骤 `gesture`「录制手势」:存完整轨迹点列 `[[x,y,t_ms],…]`,
回放时原样交给设备(`d.swipe_points(points, duration)`),不做任何加工。
与 `swipe` 是两套东西(滑动是参数化的,录制是点列)。
- core/gesture.py(新):
· 手机端录制 `PhoneRecorder`:`getevent` 读**真触屏**设备(自动挑 fts_ts 这类、
排除 uinput/vitural-sar 等合成节点),解析两套协议(BTN_TOUCH / ABS_MT_TRACKING_ID);
**合成的注入事件不会出现在真触屏节点上**(实测),所以录到的只有人手的动作;
· 点列清洗:按 ≥16ms 抽稀但**末点必留**(快划时不能把收尾丢了);
· 回放**一次调用**而不是逐点注入:实测这台设备单次触摸 RPC ≈190ms,
逐点回放 20 点要 3.8 秒——只能让设备自己插值,"时间"由点密度还原。
- 接口:POST /api/gesture/record/{start,stop}(需设备权限;重复开始返回 409)。
- 「录制手势」步骤卡片:录到就回填(手机上录 / 网页上录),显示点数/时长;
**滑动步骤上的「录制手势」按钮已移除**(按用户要求,录制不再走滑动逻辑)。
- 录制前自动唤醒设备:**息屏时 u2 抓 UI 树会从 3 秒退化到 65 秒**(实测),
截图也是黑的——这是本次排查最耗时的坑,已写进文档。
自测:假设备单测(点列/时长/抽稀/失败退回直线,10 项);
无头浏览器 + CDP 真模拟拖动 → 录到 14 点/605ms 并按实际轨迹回填;
手机录制接口起停/重复拦截;
真机回放:用录下的轨迹跑任务 → 步骤 `gesture ok`。
⚠️ 手机端"真手指轨迹"这一段需要人真的去划才能端到端验证(我没有手指)。
文档:TASK_DEV §3.2(两套东西对比 + 三个坑)、步骤表 21 种、API、README。
This commit is contained in:
+23
-2
@@ -28,6 +28,7 @@ worker 按 steps 顺序执行,支持 loop 步骤循环、停止信号、进度
|
||||
wait - 等待时长
|
||||
notify - 发自定义通知(标题/正文自己写,推送 webhook)
|
||||
stop_self - 停止本设备的任务(其它设备不受影响)
|
||||
gesture - 回放录制的手势轨迹(纯录制:路径与时长照录制,见 core/gesture.py)
|
||||
loop - 循环块(含 children 步骤列表 + max_iterations)
|
||||
group - 动作组(含 children 步骤列表,按序执行一次,可折叠复用)
|
||||
"""
|
||||
@@ -40,7 +41,7 @@ from core.device_worker import BaseWorker, _update_status
|
||||
from core.u2_helper import (ensure_app_running, wait_for_app_home, random_sleep,
|
||||
screen_is_on, current_package)
|
||||
from core.logger import get_logger
|
||||
from core import notifier, step_log, humanize, patrol
|
||||
from core import notifier, step_log, humanize, patrol, gesture
|
||||
|
||||
_log = get_logger("task.generic")
|
||||
|
||||
@@ -117,6 +118,8 @@ STEP_TYPES = [
|
||||
"params": {"title": "", "message": "", "level": "info"}},
|
||||
{"type": "stop_self", "label": "停止本设备", "icon": "⛔",
|
||||
"params": {"reason": ""}},
|
||||
{"type": "gesture", "label": "录制手势", "icon": "⏺",
|
||||
"params": {"points": [], "speed": 1.0}},
|
||||
]
|
||||
|
||||
|
||||
@@ -233,7 +236,11 @@ class GenericStepsWorker(BaseWorker):
|
||||
self._record_step(step, "skip", f"概率 {prob}% 未触发", 0)
|
||||
return
|
||||
self.set_action(f"执行: {label}")
|
||||
_log.info(f"[{self.serial}] 步骤: {label}({stype}) params={params}")
|
||||
if stype == "gesture": # 点列可能有几十个点,别整段甩进日志
|
||||
_log.info(f"[{self.serial}] 步骤: {label}({stype}) "
|
||||
f"{len(params.get('points') or [])} 个轨迹点")
|
||||
else:
|
||||
_log.info(f"[{self.serial}] 步骤: {label}({stype}) params={params}")
|
||||
|
||||
ret = None
|
||||
# 结果归类:异常 > 未命中(handler 返回 False)> 正常
|
||||
@@ -710,6 +717,20 @@ class GenericStepsWorker(BaseWorker):
|
||||
self.stop()
|
||||
return True
|
||||
|
||||
def _exec_gesture(self, d, params, depth=0):
|
||||
"""回放**录制**的手势(路径与时长照录制,不做任何再加工)。
|
||||
|
||||
与「滑动」步骤是两套东西:滑动每次重新生成(弧线/抖动/本设备手速),
|
||||
这里只把录下来的点列原样交给设备插值(原因见 core/gesture.py 模块头)。
|
||||
"""
|
||||
pts = params.get("points") or []
|
||||
if len(pts) < 2:
|
||||
_log.warning(f"[{self.serial}] gesture 步骤没有有效轨迹(points 为空),跳过")
|
||||
return None
|
||||
info = gesture.replay(d, pts, speed=params.get("speed", 1.0))
|
||||
_log.info(f"[{self.serial}] 录制回放:{info['points']} 个点 / {info['duration']}s")
|
||||
return True
|
||||
|
||||
def _exec_click(self, d, params, depth=0):
|
||||
sel_type = params.get("selector_type", "xpath")
|
||||
sel_val = params.get("selector_value", "")
|
||||
|
||||
Reference in New Issue
Block a user