找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 6|回复: 1

设备档案卡一键采集本机软硬件信息

[复制链接]
发表于 12 分钟前 | 显示全部楼层 |阅读模式
设备档案卡一键采集本机软硬件信息

QQ截图20261108194709.png

QQ截图20261108194657.png

QQ截图20261108194622.png

  1. /**
  2. * 案例37:设备档案卡——一键采集本机软硬件信息,生成一张可直接发给客户的档案卡
  3. *
  4. * 题材来源:设备农场/客户交付场景的刚需——手里十几台云手机、二手设备,
  5. *          每台什么型号、什么系统、电池好坏、还剩多少盘、IP 多少,全靠手工记。
  6. *          本案例把"盘点"这件事自动化:采集 → 排版成卡 → 落盘存档。
  7. *
  8. * 典型 AIWROK 用法组合(三件套全带上):
  9. *   ① 日志输出 : printl + 节奏倒计时(关键动作前 3..2..1,行间停顿)
  10. *   ② 数据采集 : device 对象(品牌/型号/系统/IMEI/OAID/电量/充电/IP)
  11. *                + adb.exec 读 df(存储)、/proc/meminfo(内存)
  12. *   ③ 文件落地 : adb.exec 追加写入 /sdcard/auto/设备档案_日期.txt,批量盘点可汇总
  13. *
  14. * 只用锤过的 API(全部前 36 个案例实测):
  15. *   device.getBrand/getModel/getVersion/getIMEI/getOAID/getIP/getBatteryLevel/isCharging
  16. *   adb.exec(返回 JSON 包 {errorMsg,success})、printl、sleep.millisecond(毫秒)、Date
  17. *
  18. * ★ 2026-11-06 修正:AIWROK 没有 sleep(毫秒) 裸调用,休眠必须写 sleep.millisecond(n);
  19. *   本脚本原先所有 sleep(n) 已全部改为 sleep.millisecond(n)。
  20. *
  21. * 写法约定(Rhino ES5 引擎):全 var、无箭头函数、无模板字符串;
  22. *   所有函数【先定义后调用】,main() 放在文件末尾统一启动。
  23. *
  24. * 用法:直接运行。约 15 秒出卡,手机 /sdcard/auto/ 下会多一份 txt 存档。
  25. */

  26. // ========== 配置区 ==========
  27. var 落盘目录 = "/sdcard/auto";
  28. var 档案文件名前缀 = "设备档案_";
  29. var 是否落盘 = true;          // 想只看不打文件,改 false

  30. // ========== 一、节奏输出(项目铁律:关键动作前倒计时,行间留白) ==========
  31. function 节奏倒计时(秒数, 干什么) {
  32.     for (var i = 秒数; i > 0; i--) {
  33.         printl("⏰ " + i + " 秒后:" + 干什么);
  34.         sleep.second(1);          // 整秒等待用 sleep.second,不用 sleep.millisecond(1000)
  35.     }
  36.     printl("🎬 " + 干什么 + " —— 开始");
  37.     sleep.millisecond(400);
  38. }

  39. // ========== 二、锤死通道封装 ==========
  40. // adb.exec 以 App 自身 uid 运行,返回 JSON 包;这里统一剥成 {成, 错}
  41. function adb跑(命令) {
  42.     try {
  43.         var 数据 = JSON.parse(adb.exec(命令, false));
  44.         var 成 = 数据.success == null ? "" : ("" + 数据.success);
  45.         var 错 = 数据.errorMsg == null ? "" : ("" + 数据.errorMsg).trim();
  46.         return { 成: 成, 错: 错 };
  47.     } catch (e) {
  48.         return { 成: "", 错: "exec异常 " + e };
  49.     }
  50. }

  51. // 安全取值:任何一项取不到都不许中断整张卡
  52. function 安全取值(函数) {
  53.     try {
  54.         var v = 函数();
  55.         if (v == null) return "未知";
  56.         var s = "" + v;
  57.         return s === "" ? "未知" : s;
  58.     } catch (e) {
  59.         return "不可用";
  60.     }
  61. }

  62. // ========== 三、采集:软硬件 ==========
  63. function 采集基础() {
  64.     var 卡 = {};
  65.     卡.品牌 = 安全取值(function () { return device.getBrand(); });
  66.     卡.型号 = 安全取值(function () { return device.getModel(); });
  67.     卡.系统 = 安全取值(function () { return device.getVersion(); });
  68.     卡.设备号 = 安全取值(function () { return device.getDeviceID(); });
  69.     卡.IMEI = 安全取值(function () { return device.getIMEI(); });
  70.     卡.OAID = 安全取值(function () { return device.getOAID(); });
  71.     卡.IP = 安全取值(function () { return device.getIP(); });
  72.     return 卡;
  73. }

  74. function 采集电池() {
  75.     var 电量 = -1;
  76.     try { 电量 = device.getBatteryLevel(); } catch (e) {}
  77.     var 充电 = "未知";
  78.     try { 充电 = device.isCharging() ? "充电中" : "未充电"; } catch (e) {}
  79.     return { 电量: 电量, 充电: 充电 };
  80. }

  81. // df /sdcard → 总/已用/可用/使用率(单位换算成 GB)
  82. function 采集存储() {
  83.     var r = adb跑("df /sdcard");
  84.     var 文本 = r.成;
  85.     if (文本 === "") return null;
  86.     // 形如: /dev/fuse 49387000 101000 47094000 1% /storage/emulated
  87.     var m = 文本.match(/(\d+)\s+(\d+)\s+(\d+)\s+(\d+)%/);
  88.     if (m == null) return null;
  89.     var 总 = parseInt(m[1], 10) / 1024;      // 1K-blocks → MB
  90.     var 已用 = parseInt(m[2], 10) / 1024;
  91.     var 可用 = parseInt(m[3], 10) / 1024;
  92.     return {
  93.         总GB: Math.round(总 / 1024 * 10) / 10,
  94.         已用GB: Math.round(已用 / 1024 * 100) / 100,
  95.         可用GB: Math.round(可用 / 1024 * 10) / 10,
  96.         使用率: m[4]
  97.     };
  98. }

  99. // /proc/meminfo → 内存总量与可用(MB)
  100. function 采集内存() {
  101.     var r = adb跑("cat /proc/meminfo");
  102.     var 文本 = r.成;
  103.     if (文本 === "") return null;
  104.     var 总 = 文本.match(/MemTotal:\s*(\d+)/);
  105.     var 闲 = 文本.match(/MemAvailable:\s*(\d+)/);
  106.     if (总 == null) return null;
  107.     return {
  108.         总MB: Math.round(parseInt(总[1], 10) / 1024),
  109.         可用MB: 闲 == null ? -1 : Math.round(parseInt(闲[1], 10) / 1024)
  110.     };
  111. }

  112. // ========== 四、组装档案卡(纯字符串排版,不做等宽对齐以免中文错位) ==========
  113. function 补零(n) { return n < 10 ? "0" + n : "" + n; }

  114. function 当前时间串() {
  115.     var d = new Date();
  116.     return d.getFullYear() + "-" + 补零(d.getMonth() + 1) + "-" + 补零(d.getDate()) + " " +
  117.            补零(d.getHours()) + ":" + 补零(d.getMinutes()) + ":" + 补零(d.getSeconds());
  118. }

  119. function 拼接档案卡(基础, 电池, 存储, 内存) {
  120.     var 行 = [];
  121.     行.push("┌ 设备档案 ─────────────────────────");
  122.     行.push("│ 采集时间 : " + 当前时间串());
  123.     行.push("│ 品牌型号 : " + 基础.品牌 + " " + 基础.型号);
  124.     行.push("│ 系统版本 : " + 基础.系统);
  125.     行.push("│ 设备编号 : " + 基础.设备号);
  126.     行.push("│ IMEI     : " + 基础.IMEI);
  127.     行.push("│ OAID     : " + 基础.OAID);
  128.     行.push("│ 网络 IP  : " + 基础.IP);

  129.     if (电池.电量 >= 0) {
  130.         行.push("│ 电池     : " + 电池.电量 + "% / " + 电池.充电);
  131.     } else {
  132.         行.push("│ 电池     : 本机不报电量(模拟器假电池,正常)");
  133.     }

  134.     if (存储 == null) {
  135.         行.push("│ 存储     : df 读取失败");
  136.     } else {
  137.         行.push("│ 存储     : 总 " + 存储.总GB + "GB / 已用 " + 存储.已用GB + "GB / 可用 " +
  138.                 存储.可用GB + "GB (使用率 " + 存储.使用率 + "%)");
  139.     }

  140.     if (内存 == null) {
  141.         行.push("│ 内存     : meminfo 读取失败");
  142.     } else if (内存.可用MB < 0) {
  143.         行.push("│ 内存     : 总 " + 内存.总MB + "MB");
  144.     } else {
  145.         行.push("│ 内存     : 总 " + 内存.总MB + "MB / 可用 " + 内存.可用MB + "MB");
  146.     }

  147.     行.push("└───────────────────────────────────");
  148.     return 行;
  149. }

  150. // ========== 五、落盘(逐行追加,失败只报不炸) ==========
  151. // 除单引号外,档案内容不含 shell 特殊字符;为稳妥先把单引号替换掉
  152. function 存盘(行表, 文件路径) {
  153.     var 头 = adb跑("mkdir -p " + 落盘目录);
  154.     if (("" + 头.错).indexOf("Permission") >= 0) return "无写权限";
  155.     var 成功行 = 0;
  156.     for (var i = 0; i < 行表.length; i++) {
  157.         var 文本 = ("" + 行表[i]).replace(/'/g, "’");
  158.         var 命令 = "sh -c "echo '" + 文本 + "' >> " + 文件路径 + """;
  159.         var r = adb跑(命令);
  160.         if (("" + r.错).indexOf("error") >= 0 || ("" + r.错).indexOf("denied") >= 0) continue;
  161.         成功行++;
  162.     }
  163.     return 成功行 === 行表.length ? "成功" : "部分成功(" + 成功行 + "/" + 行表.length + " 行)";
  164. }

  165. // ========== 六、主流程 ==========
  166. function main() {
  167.     printl("=== 案例37 设备档案卡 启动:采集本机信息,约 15 秒出卡 ===");
  168.     sleep.millisecond(600);

  169.     节奏倒计时(3, "开始采集体检数据");
  170.     var 基础 = 采集基础();
  171.     printl("   ✔ 软硬件基础项采集完成");
  172.     sleep.millisecond(500);

  173.     节奏倒计时(2, "读电池 / 存储 / 内存");
  174.     var 电池 = 采集电池();
  175.     printl("   ✔ 电池: " + (电池.电量 >= 0 ? 电池.电量 + "%" : "不可用"));
  176.     sleep.millisecond(400);
  177.     var 存储 = 采集存储();
  178.     printl("   ✔ 存储: " + (存储 == null ? "读取失败" : "可用 " + 存储.可用GB + "GB"));
  179.     sleep.millisecond(400);
  180.     var 内存 = 采集内存();
  181.     printl("   ✔ 内存: " + (内存 == null ? "读取失败" : "总 " + 内存.总MB + "MB"));
  182.     sleep.millisecond(600);

  183.     var 行表 = 拼接档案卡(基础, 电池, 存储, 内存);
  184.     printl("========== 📇 设备档案卡 ==========");
  185.     for (var i = 0; i < 行表.length; i++) {
  186.         printl(行表[i]);
  187.         sleep.millisecond(300);                       // 逐行吐,方便录像和截屏
  188.     }
  189.     sleep.millisecond(600);

  190.     if (是否落盘) {
  191.         var d = new Date();
  192.         var 名 = 档案文件名前缀 + d.getFullYear() + 补零(d.getMonth() + 1) + 补零(d.getDate()) + ".txt";
  193.         var 路径 = 落盘目录 + "/" + 名;
  194.         printl("💾 落盘:" + 路径);
  195.         var 结果 = 存盘(行表, 路径);
  196.         printl("   " + (结果 === "成功" ? "✅" : "⚠") + " 落盘" + 结果);
  197.         if (结果 === "成功") printl("   👉 多台设备挨个跑一遍,这个文件就是你的设备台账");
  198.     } else {
  199.         printl("ℹ 是否落盘=false,本次只打屏不存档");
  200.     }

  201.     printl("========== ✅ 体检完成 ==========");
  202.     printl("💡 扩展:①外层套循环遍历多台设备 ②档案追加到同一文件做汇总 ③异常项(电量<20%)标红提醒");
  203.     printl("=== 案例37 收工 ===");
  204. }

  205. // ========== 启动(Rhino ES5:统一在文件末尾调用,确保上面函数都已定义) ==========
  206. main();
复制代码


0

主题

4

回帖

8

积分

软件网站新手

积分
8
发表于 7 分钟前 | 显示全部楼层

设备档案卡一键采集本机软硬件信息

感谢分享,权限那块要单独开无障碍和电池白名单,否则跑到一半会被系统杀掉,最常见的问题就是这个。别把所有异常都当同一种处理,找不到元素和网络失败要分开,混在一起错误就全被吞了。我们把日志分成运行和调试两层,出问题时先用运行日志定位到哪一步,再开调试日志复现。内容生成这类活,规则加话术组合就能覆盖大部分场景,又快又不出错。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|

GMT+8, 2026-11-9 07:25

安卓脚本软件 · 键鼠盒子 · HID · 机械臂

快速回复 返回顶部 返回列表

随机推荐 · 热帖 / 新帖 / 随便看看

社区会员(随机展示)