| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- /**
- * 假引擎:模拟「长时间运行 + 能被叫停 + 会跳心跳」,**完全不碰 TikTok**。
- *
- * 用途:验证界面侧的运行态交互(启动握手、状态条、进度、两段停止、心跳判活)。
- * 用真引擎验证这些时,它会把 TikTok 拉到前台,我就没法同时操作界面上的按钮了 ——
- * 而这个假引擎不切应用,界面始终留在前台,正好把 UI 交互测干净。
- *
- * 协议与真引擎完全一致:读 --config/--state/--stop-flag/--run-id,
- * 写 run-state.json(带 v / engine.capabilities)与 engine-beat.json。
- *
- * ★ 心跳不能省:界面在 v2 档位下是用心跳判活的。这个假引擎要是不跳,
- * 跑起来 15 秒就会被界面判成"引擎已退出" —— 最常用的联调手段反而成了误报源。
- */
- function argOf(argv, key) {
- for (var i = 0; argv && argv[i] !== undefined; i += 1) {
- if (String(argv[i]) === key) { return String(argv[i + 1]); }
- }
- return "";
- }
- var argv = engines.myEngine().execArgv;
- var STATE = argOf(argv, "--state");
- var STOP = argOf(argv, "--stop-flag");
- var RUN_ID = argOf(argv, "--run-id");
- /* 心跳文件跟 run-state 放同一目录(真引擎也是这么约定的),
- * 这样不必额外传一个 --beat 参数,命令行协议保持 5 个参数不变。 */
- var slash = STATE.lastIndexOf("/");
- var BEAT = (slash >= 0 ? STATE.substring(0, slash) : "") + "/engine-beat.json";
- var seq = 0;
- var seenAt = 0;
- function report(payload) {
- payload.v = 2;
- payload.runId = RUN_ID;
- payload.engine = payload.engine || { task: "fake", capabilities: { heartbeat: true } };
- payload.updatedAt = Date.now();
- files.write(STATE, JSON.stringify(payload));
- }
- /* 心跳必须是**独立线程**:主流程会 sleep(300) 轮询,
- * 若把心跳写在同一循环里,一旦某一步卡住心跳就跟着停 —— 那就失去意义了。
- * (真引擎侧也是同样的理由,见 tiktok-auto/src/bridge.js 的 heartbeatStart) */
- var beating = true;
- threads.start(function () {
- while (beating) {
- seq += 1;
- try {
- files.write(BEAT, JSON.stringify({
- v: 2, runId: RUN_ID, seq: seq, beatAt: Date.now(), stopSeenAt: seenAt
- }));
- } catch (e) { }
- sleep(2000);
- }
- });
- report({ status: "starting", phase: "假引擎启动中", done: 0, total: 3 });
- sleep(600);
- report({ status: "running", phase: "假引擎运行中", done: 0, total: 3 });
- var began = Date.now();
- var tick = 0;
- var done = 0;
- while (Date.now() - began < 60000) {
- tick += 1;
- /* 每 3 秒推进一格进度:用来验证状态条会跟着引擎上报刷新 */
- if (tick % 10 === 0 && done < 3) {
- done += 1;
- report({ status: "running", phase: "已处理 " + done + "/3", done: done, total: 3 });
- }
- var content = "";
- try {
- if (files.exists(STOP)) {
- content = String(files.read(STOP)).replace(/\s/g, "");
- }
- } catch (e) { }
- if (content !== "" && content === RUN_ID) {
- /* 先承认收到(sticky 的 stopSeenAt,界面靠它把"正在通知引擎"换成"正在收尾"),
- * 再走一段"收尾中"的过场 —— 真实引擎也是这样,不会立刻消失。 */
- seenAt = Date.now();
- report({ status: "stopping", phase: "正在收尾", message: "收到停止请求(假引擎)" });
- sleep(1200);
- beating = false;
- report({ status: "stopped", phase: "", message: "已停止(假引擎)" });
- exit();
- }
- sleep(300);
- }
- beating = false;
- report({ status: "finished", phase: "", done: 3, total: 3, message: "跑完了(假引擎)" });
|