如何使用
简介:
本示例展示如何通过 Tasks 接口,完整管理一次自动化任务的生命周期。
脚本依次完成任务创建(tasksCreate)、任务入库确认(tasksGet),并通过轮询方式持续查询任务状态,直到任务执行结束(RUNNING → STOPPED_*),最终输出任务执行结果与关键信息。
示例内置标准化的步骤日志(开始 / 结束 / 耗时统计),并在失败场景下自动采集前台应用、Activity、电量、设备信息及截图留证,便于问题定位与结果审计。
适用于 自动化测试调度、批量任务执行、平台集成与执行结果监控 等场景,可直接复制运行。
运行前:
sleep(10000);
print('-tasks-test-');sigmaLoad("D:/scripts/task_create_submit_poll.js");
注意:
源代码
/**
* ============================================================
* [TASK] Create -> Submit -> Get results
* ============================================================
* 目标:
* 1) 创建任务 tasksCreate(...)
* 2) 确认任务已入库 tasksGet(...)
* 3) 轮询 tasksGet(...) 等待任务结束(RUNNING -> STOPPED_*)
* 4) 打印结果(status / errorMessage / execDevice / sigmaTestStatus 等)
*
* 输出要求:
* - 每一步打印「开始/结束/耗时」
* - 失败:写上下文(前台包名+activity+电量+设备信息)+ 截图 + 写日志文件(若 fs 可用)
*
* 关键注意:
* - tasksCreate() 会校验脚本路径必须存在,否则 "Script path not found"
* - tasks 的“执行器”不一定马上运行;如果 planTime 在未来会保持 SCHEDULED(-6)
* - 本示例用轮询 tasksGet 获取最终 status(-2/-3/-5 等)
*/
// ===================== 引入与注入 =====================
// var tcConst = global.tcConst || {};
// var sigmaConst = global.sigmaConst || {};
var { getDevices } = require("sigma/device");
require("sigma/app");
require("sigma/input");
require("sigma/image");
// tasks 模块
var tasks = require("sigma/tasks");
// ===================== 配置区 =====================
var LOG_DIR = "D:/tc_logs";
var IMG_TYPE = (sigmaConst.IMG_JPG || tcConst.IMG_JPG || sigmaConst.IMG_PNG || tcConst.IMG_PNG);
// 任务名必须唯一(建议带时间戳)
function pad2(n) { return (n < 10 ? "0" : "") + n; }
function ts() {
var d = new Date();
return (
d.getFullYear() +
pad2(d.getMonth() + 1) +
pad2(d.getDate()) + "_" +
pad2(d.getHours()) +
pad2(d.getMinutes()) +
pad2(d.getSeconds())
);
}
var TASK_NAME = "demo_task_" + ts();
// 脚本必须存在、且扩展名必须 .js/.tst/.scp
// 例:var SCRIPT_PATH = "E:/file/test.js";
var SCRIPT_PATH = "E:/file/test.js";
// 执行次数(iteration/iterCount)>=1
var ITERATION = 1;
// 执行时间策略:
// 1) 立刻创建(now):time = undefined, repeat = undefined
// 2) 指定一次性时间:time = "YYYY-MM-DD HH:mm[:ss]"
// 3) 周期:time="HH:mm[:ss]" + repeat=[0..6]
var TIME_MODE = "now"; // "now" | "oneshot" | "weekly"
// oneshot 示例(未来时间)
// var ONE_SHOT_TIME = "2026-01-14 23:50";
// weekly 示例:每周一三五 08:30
// var WEEKLY_TIME = "08:30";
// var WEEKLY_REPEAT = [1,3,5];
// 轮询等待最大时长(毫秒)
var WAIT_TIMEOUT_MS = 3 * 60 * 1000; // 3 分钟
var POLL_INTERVAL_MS = 1500;
// ===================== 小工具 =====================
function nowMs() { return Date.now(); }
function sleep(ms) {
if (typeof global.sleep === "function") return global.sleep(ms);
var t = Date.now();
while (Date.now() - t < ms) {}
}
function safeFilePart(s) { return String(s).replace(/[^\w\-.]+/g, "_"); }
function devName(d) { return d.name || (d.getName && d.getName()) || d.SN || "UnknownDevice"; }
function safeGet(fn, fallback) { try { return fn(); } catch (e) { return fallback; } }
// fs(有则写日志/锁)
var fs = null;
try { fs = require("fs"); } catch (e) { fs = null; }
function ensureDir(path) {
if (!fs) return false;
try { if (!fs.existsSync(path)) fs.mkdirSync(path, { recursive: true }); return true; }
catch (e) { return false; }
}
function appendFile(path, text) {
if (!fs) return false;
try { fs.appendFileSync(path, text, { encoding: "utf8" }); return true; }
catch (e) { return false; }
}
// ===================== 日志 ctx =====================
function makeCtx() {
ensureDir(LOG_DIR);
var logFile = LOG_DIR + "/TASK__" + safeFilePart(TASK_NAME) + "__" + ts() + ".log";
var canWrite = ensureDir(LOG_DIR) && !!fs;
return {
logFile: logFile,
canWrite: canWrite,
log: function (line) {
var msg = "[TASK] " + line;
print(msg);
if (this.canWrite) appendFile(this.logFile, msg + "\n");
}
};
}
// ===================== stepRun:开始/结束/耗时 =====================
function stepRun(ctx, stepName, fn) {
var start = nowMs();
ctx.log(">> START " + stepName);
try {
var ret = fn();
ctx.log("<< END " + stepName + " (cost " + (nowMs() - start) + " ms)");
return { ok: true, ret: ret };
} catch (e) {
ctx.log("<< FAIL " + stepName + " (cost " + (nowMs() - start) + " ms) err=" + String(e && e.message ? e.message : e));
return { ok: false, err: String(e && e.message ? e.message : e) };
}
}
// ===================== 失败上下文 + 截图 =====================
function writeFailContext(ctx, d, stepName, errText) {
ctx.log("---- FAIL CONTEXT (" + devName(d) + ") BEGIN ----");
ctx.log("step=" + stepName);
ctx.log("error=" + (errText || ""));
ctx.log("foregroundApp=" + (safeGet(function () { return d.getForegroundApp(); }, "") || ""));
ctx.log("activity=" + (safeGet(function () { return d.getActivity(); }, "") || ""));
ctx.log("battery=" + (safeGet(function () { return d.battery; }, "") || ""));
ctx.log("manufacturer=" + safeGet(function () { return d.manufacturer; }, ""));
ctx.log("model=" + safeGet(function () { return d.model; }, ""));
ctx.log("SN=" + safeGet(function () { return d.SN; }, ""));
ctx.log("IP=" + safeGet(function () { return d.IP; }, ""));
ctx.log("DPI=" + safeGet(function () { return d.DPI; }, ""));
ctx.log("resolution=" + safeGet(function () { return d.width; }, "") + "x" + safeGet(function () { return d.height; }, ""));
ctx.log("androidVersionRelease=" + safeGet(function () { return d.androidVersionRelease; }, ""));
ctx.log("androidVersionSdkInt=" + safeGet(function () { return d.androidVersionSdkInt; }, ""));
ctx.log("---- FAIL CONTEXT (" + devName(d) + ") END ----");
}
function screenshotOne(ctx, d, tag) {
var file = LOG_DIR + "/" + safeFilePart(devName(d)) + "__" + safeFilePart(tag) + "__" + ts() + ".jpg";
var ret = d.screenshot(file, IMG_TYPE);
if (ret === 0) ctx.log("!! Screenshot saved: " + file);
else ctx.log("!! Screenshot failed (" + devName(d) + "): " + lastError());
}
// ===================== task status helper =====================
function statusToText(code) {
var S = tasks.STATUS || {};
// 常见值(文档里)
if (code === 0) return "FILE_NOT_FOUND(0)";
if (code === S.RUNNING || code === -1) return "RUNNING(-1)";
if (code === S.STOPPED_NORMAL || code === -2) return "STOPPED_NORMAL(-2)";
if (code === S.STOPPED_ERROR || code === -3) return "STOPPED_ERROR(-3)";
if (code === S.PAUSED || code === -4) return "PAUSED(-4)";
if (code === S.USER_TERMINATED || code === -5) return "USER_TERMINATED(-5)";
if (code === S.SCHEDULED || code === -6) return "SCHEDULED(-6)";
return String(code);
}
function isTerminalStatus(code) {
return code === -2 || code === -3 || code === -5 || code === 0;
}
// ===================== main =====================
function main() {
var ctx = makeCtx();
// 可选:拿到设备,主要用于失败时上下文/截图
var devices = getDevices();
var list = [];
if (devices && devices.length) devices.forEach(function (d) { list.push(d); });
ctx.log("Selected devices: " + list.length);
// Step0:准备脚本路径存在性提示
stepRun(ctx, "Step0 Check script path string", function () {
ctx.log("TASK_NAME=" + TASK_NAME);
ctx.log("SCRIPT_PATH=" + SCRIPT_PATH);
ctx.log("ITERATION=" + ITERATION);
ctx.log("TIME_MODE=" + TIME_MODE);
});
// Step1:Create task
var s1 = stepRun(ctx, "Step1 tasksCreate()", function () {
var ret;
if (TIME_MODE === "now") {
// 立即(time/repeat 省略)
ret = tasks.tasksCreate(TASK_NAME, SCRIPT_PATH, ITERATION);
} else if (TIME_MODE === "oneshot") {
// 一次性定时(自行把 ONE_SHOT_TIME 配出来)
ret = tasks.tasksCreate(TASK_NAME, SCRIPT_PATH, ITERATION, ONE_SHOT_TIME);
} else if (TIME_MODE === "weekly") {
// 周期(自行把 WEEKLY_TIME/WEEKLY_REPEAT 配出来)
ret = tasks.tasksCreate(TASK_NAME, SCRIPT_PATH, ITERATION, WEEKLY_TIME, WEEKLY_REPEAT);
} else {
throw new Error("Unknown TIME_MODE: " + TIME_MODE);
}
if (ret !== true && typeof ret !== "number") {
// tasksCreate 文档:成功 true;失败 false(配合 lastError)
throw new Error("tasksCreate FAIL: " + lastError());
}
ctx.log("tasksCreate OK, ret=" + ret);
return ret;
});
if (!s1.ok) {
ctx.log("[CREATE] Task create FAIL: " + TASK_NAME);
ctx.log("[CREATE] err=" + s1.err);
// 失败时:对每台设备补一份上下文+截图
for (var i = 0; i < list.length; i++) {
writeFailContext(ctx, list[i], "tasksCreate", s1.err);
screenshotOne(ctx, list[i], "FAIL__tasksCreate");
}
// 额外:打印最近任务帮助排查
var s1b = stepRun(ctx, "Step1b tasksList(recent 5)", function () {
var recent = tasks.tasksList({ limit: 5, orderBy: "updatedAt DESC" });
ctx.log("recent tasks:\n" + JSON.stringify(recent, null, 2));
});
return;
}
// Step2:Get task record (submit/confirm)
var taskRow = null;
var s2 = stepRun(ctx, "Step2 tasksGet(taskName)", function () {
var t = tasks.tasksGet(TASK_NAME);
if (!t) throw new Error("tasksGet returned null (not found)");
taskRow = t;
// tasksGet 可能返回 object 或 [object](不同实现),这里统一一下
if (Array.isArray(taskRow)) taskRow = taskRow[0];
ctx.log("task snapshot:\n" + JSON.stringify(taskRow, null, 2));
return taskRow;
});
if (!s2.ok) return;
// Step3:Poll until terminal
var s3 = stepRun(ctx, "Step3 Poll task until terminal or timeout", function () {
var deadline = nowMs() + WAIT_TIMEOUT_MS;
var lastStatus = null;
while (nowMs() < deadline) {
var t = tasks.tasksGet(TASK_NAME);
if (Array.isArray(t)) t = t[0];
if (!t) throw new Error("tasksGet null during polling");
var st = t.status;
if (st !== lastStatus) {
ctx.log("poll: status=" + statusToText(st) + " startTime=" + t.startTime + " endTime=" + t.endTime);
lastStatus = st;
}
if (isTerminalStatus(st)) {
taskRow = t;
return t;
}
sleep(POLL_INTERVAL_MS);
}
// 超时:可能仍是 SCHEDULED/RUNNING
throw new Error("Timeout waiting task finish. lastStatus=" + statusToText(lastStatus));
});
// Step4:Print result summary
stepRun(ctx, "Step4 Print result summary", function () {
var t = taskRow;
if (Array.isArray(t)) t = t[0];
ctx.log("===== RESULT =====");
ctx.log("taskName=" + t.taskName);
ctx.log("taskID=" + t.taskID);
ctx.log("status=" + statusToText(t.status));
ctx.log("errorMessage=" + (t.errorMessage || ""));
ctx.log("execDevice=" + (t.execDevice || "-"));
ctx.log("startTime=" + t.startTime);
ctx.log("endTime=" + t.endTime);
ctx.log("sigmaTestStatus=" + (t.sigmaTestStatus || t.sigma_test_status || ""));
// 若失败:补一份设备上下文+截图
if (t.status === -3 || t.status === 0) {
ctx.log("Task ended with error, collecting device evidence...");
for (var i = 0; i < list.length; i++) {
writeFailContext(ctx, list[i], "taskResult(" + statusToText(t.status) + ")", t.errorMessage || "");
screenshotOne(ctx, list[i], "FAIL__taskResult");
}
}
});
// Step5:Optional - show recent list
stepRun(ctx, "Step5 tasksList(recent 5)", function () {
var recent = tasks.tasksList({ limit: 5, orderBy: "updatedAt DESC" });
ctx.log("recent tasks:\n" + JSON.stringify(recent, null, 2));
});
ctx.log("DONE.");
}
main();