|
|
网络巡检员云手机断网预警
- /**
- * 案例38(录像版):网络巡检员——云手机断网预警 + 延迟台账
- *
- * 题材来源:设备农场运维刚需。云手机网络一抖,刷视频任务全断流、掉线不自知,
- * 第二天客户问"为什么昨晚任务没跑满"——没台账说不清。本案例把
- * "网络到底好不好"变成可量化:丢包率、平均延迟、DNS 能不能解析,
- * 每轮巡检打屏 + 落盘 CSV,长期跑就是网络质量台账。
- *
- * 录像友好增强(用户要求):
- * ① 开场打"网络环境卡":本机IP/网关/DNS1/DNS2 全放出来;
- * ② 每个 ping 目标逐包打一行(time=xx ms),满屏滚动有节奏;
- * ③ 收工给每目标画 ASCII 延迟条(█ 长度=延迟),报告有画面感。
- *
- * 全走锤死通道(零新API):
- * ping 探测 → adb.exec("ping -c N -W 2 地址")(案例30锤死:JSON包{errorMsg,success})
- * 本机 IP → device.getIP()(案例37锤死)
- * 网关 → cat /proc/net/route 默认路由,小端十六进制转 IP
- * DNS → getprop net.dns1 / net.dns2
- * DNS 验证 → 直接用域名当 ping 目标:解析不了 ping 就报错,天然测 DNS
- * 落盘 CSV → adb.exec sh -c echo 追加(案例37锤死写法)
- * 等待 → sleep.second(整秒)/ sleep.millisecond(毫秒),禁止裸 sleep()
- *
- * 写法合规:Rhino ES5,全 var、字符串拼接、函数先定义后调用、main() 末尾启动;
- * 节奏输出:每轮巡检前 3..2..1 倒计时,日志行间留白,方便盯屏录像。
- */
- // ========== 配置区 ==========
- var 巡检目标表 = [
- { 名: "阿里DNS", 址: "223.5.5.5" },
- { 名: "DNSPod", 址: "119.29.29.29" },
- { 名: "百度(测DNS)", 址: "www.baidu.com" }
- ];
- var 巡检轮数 = 3; // 演示 3 轮,常驻改大
- var 每轮ping次数 = 4; // 每个目标 ping 4 包(逐包打屏)
- var 轮间隔秒 = 5; // 两轮之间歇 5 秒
- var 台账目录 = "/sdcard/auto";
- // ========== adb 通道(案例30锤死) ==========
- function adb跑(命令) {
- try {
- var 数据 = JSON.parse(adb.exec(命令, false));
- var 成 = 数据.success == null ? "" : ("" + 数据.success);
- var 错 = 数据.errorMsg == null ? "" : ("" + 数据.errorMsg).trim();
- return { 成: 成, 错: 错 };
- } catch (e) {
- return { 成: "", 错: "exec异常 " + e };
- }
- }
- // ========== 节奏倒计时(整秒一律 sleep.second) ==========
- function 节奏倒计时(秒数, 干什么) {
- for (var i = 秒数; i > 0; i--) {
- printl("⏰ " + i + " 秒后:" + 干什么);
- sleep.second(1);
- }
- printl("🎬 " + 干什么 + " —— 开始");
- sleep.millisecond(400);
- }
- // ========== 网络环境卡:本机IP / 网关 / DNS ==========
- function 取网关() {
- var r = adb跑("cat /proc/net/route");
- var 行表 = ("" + r.成).split("\n");
- for (var i = 0; i < 行表.length; i++) {
- var 列 = ("" + 行表[i]).trim().split(/\s+/);
- if (列.length < 4) continue;
- if (列[1] === "00000000") { // 目的=0.0.0.0 即默认路由
- var gw = 列[2];
- if (gw === "00000000") continue;
- // 小端十六进制 → 点分IP
- var 段 = [];
- for (var j = 3; j >= 0; j--) 段.push(parseInt(gw.substr(j * 2, 2), 16));
- return 段.join(".");
- }
- }
- return "未知";
- }
- function 取DNS() {
- var d1 = adb跑("getprop net.dns1");
- var d2 = adb跑("getprop net.dns2");
- var 一 = ("" + d1.成).trim();
- var 二 = ("" + d2.成).trim();
- if (一 === "") 一 = "未知";
- if (二 === "") 二 = "无";
- return { 主: 一, 备: 二 };
- }
- function 打网络环境卡(本机IP, 网关, dns) {
- printl("┌──────── 网络环境卡 ────────");
- printl("│ 本机 IP : " + 本机IP);
- printl("│ 默认网关: " + 网关);
- printl("│ DNS 主 : " + dns.主);
- printl("│ DNS 备 : " + dns.备);
- var 目标串 = [];
- for (var i = 0; i < 巡检目标表.length; i++) 目标串.push(巡检目标表[i].名 + "(" + 巡检目标表[i].址 + ")");
- printl("│ 探测目标: " + 目标串.join(" | "));
- printl("│ 探测强度: " + 巡检轮数 + " 轮 × " + 每轮ping次数 + " 包/目标");
- printl("└──────────────────────────────");
- sleep.millisecond(600);
- }
- // ========== ASCII 延迟条(报告有画面感) ==========
- function 画条(ms) {
- if (ms < 0) return "?";
- var 格 = Math.max(1, Math.round(ms / 10));
- if (格 > 40) 格 = 40;
- var 条 = "";
- for (var i = 0; i < 格; i++) 条 += "█";
- return 条 + " " + ms + "ms";
- }
- // ========== ping 一个目标并解析(含逐包时间列表) ==========
- function ping一次(地址) {
- var r = adb跑("ping -c " + 每轮ping次数 + " -W 2 " + 地址);
- var 文本 = r.成;
- if (文本 === "") return { 死: true, 因: r.错.slice(0, 60), 包: [] };
- var 丢包 = 文本.match(/(\d+(?:\.\d+)?)% packet loss/);
- if (丢包 == null) {
- var 解析挂 = ("" + 文本 + r.错).match(/bad address|unresolved|Name or service/i);
- return { 死: true, 因: 解析挂 == null ? "没匹配到统计行" : "DNS解析失败", 包: [] };
- }
- var 延迟 = 文本.match(/=\s*([\d.]+)\/([\d.]+)\/([\d.]+)/);
- // 逐包:64 bytes from x: icmp_seq=1 ttl=.. time=12.3 ms
- var 包 = [];
- var re = /time=([\d.]+)\s*ms/g;
- var m;
- while ((m = re.exec(文本)) != null) 包.push(parseFloat(m[1]));
- return {
- 死: false,
- 丢包率: parseFloat(丢包[1]),
- 平均ms: 延迟 == null ? -1 : parseFloat(延迟[2]),
- 最大ms: 延迟 == null ? -1 : parseFloat(延迟[3]),
- 包: 包
- };
- }
- // ========== 单目标评级 ==========
- function 评级(结果) {
- if (结果.死) return "❌不通";
- if (结果.丢包率 > 0) return "⚠丢包" + 结果.丢包率 + "%";
- if (结果.平均ms >= 300) return "🐢慢(" + 结果.平均ms + "ms)";
- return "✅" + 结果.平均ms + "ms";
- }
- // ========== 台账落盘(CSV,案例37锤死写法) ==========
- function 补零(n) { return n < 10 ? "0" + n : "" + n; }
- function 台账路径() {
- var d = new Date();
- return 台账目录 + "/网络巡检_" + d.getFullYear() + 补零(d.getMonth() + 1) + 补零(d.getDate()) + ".csv";
- }
- function 写台账(行文本, 路径) {
- var 安全 = ("" + 行文本).replace(/'/g, "’");
- adb跑("sh -c "echo '" + 安全 + "' >> " + 路径 + """);
- }
- // ========== 主流程 ==========
- function main() {
- printl("=== 案例38 网络巡检员 启动:" + 巡检轮数 + " 轮 × " + 巡检目标表.length + " 目标 ===");
- sleep.millisecond(500);
- // 开场:网络环境卡(IP/网关/DNS 全放出来)
- 节奏倒计时(3, "打网络环境卡");
- var 本机IP = "未知";
- try { 本机IP = device.getIP(); } catch (e) {}
- var 网关 = 取网关();
- var dns = 取DNS();
- 打网络环境卡(本机IP, 网关, dns);
- var 路径 = 台账路径();
- 写台账("时间,目标,丢包率%,平均ms,最大ms,评级", 路径);
- printl("📒 台账文件: " + 路径);
- sleep.millisecond(600);
- var 累计 = [];
- var i0;
- for (i0 = 0; i0 < 巡检目标表.length; i0++)
- 累计.push({ 不通: 0, 丢包总: 0, 延迟总: 0, 有效: 0 });
- var d0 = new Date();
- var 表头 = d0.getFullYear() + "-" + 补零(d0.getMonth() + 1) + "-" + 补零(d0.getDate());
- for (var 轮 = 1; 轮 <= 巡检轮数; 轮++) {
- 节奏倒计时(3, "第 " + 轮 + "/" + 巡检轮数 + " 轮全网探测");
- for (var i = 0; i < 巡检目标表.length; i++) {
- var 目标 = 巡检目标表[i];
- printl(" ▶ 探测 " + 目标.名 + " [" + 目标.址 + "]");
- sleep.millisecond(300);
- var r = ping一次(目标.址);
- if (r.死) {
- var 时0 = new Date();
- printl(" ❌ " + r.因 + " (" + 补零(时0.getHours()) + ":" + 补零(时0.getMinutes()) + ":" + 补零(时0.getSeconds()) + ")");
- 累计[i].不通++;
- var 时A = new Date();
- 写台账(表头 + " " + 补零(时A.getHours()) + ":" + 补零(时A.getMinutes()) + ":" + 补零(时A.getSeconds()) +
- "," + 目标.名 + ",100,-,-," + 目标.址 + "不通(" + r.因 + ")", 路径);
- } else {
- // 逐包打屏:录像滚动好看
- for (var p = 0; p < r.包.length; p++) {
- printl(" 第" + (p + 1) + "包 → " + 画条(r.包[p]));
- sleep.millisecond(300);
- }
- var 评 = 评级(r);
- var 时 = new Date();
- var 时刻 = 补零(时.getHours()) + ":" + 补零(时.getMinutes()) + ":" + 补零(时.getSeconds());
- printl(" 📊 小计: 丢包 " + r.丢包率 + "% | 评级 " + 评);
- sleep.millisecond(400);
- 累计[i].有效++;
- 累计[i].丢包总 += r.丢包率;
- if (r.平均ms >= 0) 累计[i].延迟总 += r.平均ms;
- 写台账(表头 + " " + 时刻 + "," + 目标.名 + "," + r.丢包率 + "," +
- r.平均ms + "," + r.最大ms + "," + 评, 路径);
- }
- }
- if (轮 < 巡检轮数) {
- printl(" 💤 歇 " + 轮间隔秒 + " 秒进下一轮");
- sleep.second(轮间隔秒);
- }
- }
- // ========== 巡检报告(带延迟条) ==========
- printl("========== 📋 巡检报告(" + 巡检轮数 + " 轮均值) ==========");
- var 全通 = true;
- for (var j = 0; j < 巡检目标表.length; j++) {
- var c = 累计[j];
- var 名 = 巡检目标表[j].名;
- if (c.有效 === 0) {
- printl(" " + 名 + " : ❌ " + 巡检轮数 + " 轮全不通(DNS挂了/断网/被墙)");
- 全通 = false;
- continue;
- }
- var 平均丢 = Math.round(c.丢包总 / 巡检轮数 * 10) / 10;
- var 平均延迟 = Math.round(c.延迟总 / c.有效 * 10) / 10;
- var 结论 = 平均丢 > 0 ? "⚠ 有丢包" : (平均延迟 >= 300 ? "🐢 偏慢" : "✅ 健康");
- if (平均丢 > 0 || 平均延迟 >= 300) 全通 = false;
- printl(" " + 名 + " : 丢包 " + 平均丢 + "% | 不通 " + c.不通 + "/" + 巡检轮数 + " 轮 → " + 结论);
- printl(" 延迟 " + 画条(平均延迟));
- }
- printl(" 总体: " + (全通 ? "✅ 网络健康,任务放心挂" : "⚠ 网络有隐患,建议错峰/换线路/查DNS"));
- printl("📒 台账已存: " + 路径 + "(多台设备同名文件各跑各的,收回来就是农场网络报表)");
- printl("💡 报警接法:评级出现 ❌ 时,adb.exec 里 curl 一条 HTTP 到你的服务器;常驻=外层套 while+sleep.second(60)");
- printl("=== 案例38 收工 ===");
- }
- // ========== 启动(Rhino ES5:函数全部定义完才调用) ==========
- main();
复制代码
|
|