--[[ @module link_info @summary 4G 链路信息采集: IMEI / 流量卡ICCID / IMSI / 手机号 / CSQ 信号 @date 2026.08.31 @modify 2026.09.10 全部 mobile API 走 safe() 包装 —— 修 link_info.lua:29 "attempt to call a nil value (field 'msisdn')" 导致的上电复位 @usage 4G 通道特有字段,随上行 JSON 注入(顶层 link 对象,平台可选解析,不破坏主协议结构)。 字段说明(接入《DLD960_IoT_MQTT协议》4G 通道适配章节时一并标注): - imei: 4G 模块 IMEI(mobile.imei()) - iccid: 流量卡 ICCID(mobile.iccid();卡商未写入返回 nil → 置空串) - imsi: IMSI(mobile.imsi();部分运营商/测试卡返回 nil) - msisdn: 手机号(Air780EPM 无 mobile.msisdn;官方 API 为 mobile.number(simid), 物联卡普遍无机号 → 通常空串) - csq: 信号强度 0-31(mobile.csq();越大越好,31=最强,99/255=无信号) - net: 网络制式(当前 4G 通道固定 "4G";后续若支持 5G 或以太网备份可扩展) ]] local link_info = {} --[[ 安全的 mobile API 调用 ⚠ LuatOS 各固件/型号的 API 面并不一致: 字段**不存在**时直接调用会抛 "attempt to call a nil value" 并让 Lua VM 直接退出(设备 15s 后复位), 而不是像返回 nil 那样温和。Air780EPM 实测无 mobile.msisdn (官方 API 是 mobile.number(simid),见 demo/mobile/mobile_test.lua)。 故本工程所有 mobile.xxx 调用一律经此函数: 字段不存在或调用异常 → nil, 绝不抛出。 ]] local function safe(fn, ...) if type(fn) ~= "function" then return nil end local ok, v = pcall(fn, ...) if ok then return v end return nil end link_info.safe = safe -- 供其他模块复用 -- 实时采集(CSQ 每次调用现读;若需降低开销,可改为定时缓存刷新) -- ⚠ 本函数保证永不抛异常(对外 API 全部走 safe), 调用方即使不 pcall 也无 VM 复位风险 function link_info.get() local csq = safe(mobile.csq) -- mobile.csq() 在某些固件返回 table(如 {csq=xx, ber=xx});此处兼容 if type(csq) == "table" then csq = csq.csq end if type(csq) ~= "number" then csq = 0 end return { imei = safe(mobile.imei) or "", iccid = safe(mobile.iccid) or "", imsi = safe(mobile.imsi) or "", msisdn = safe(mobile.number, 0) or "", csq = csq, net = "4G", } end return link_info