diff --git a/include/serial_AT.hpp b/include/serial_AT.hpp index ddf8699..0b3fc48 100644 --- a/include/serial_AT.hpp +++ b/include/serial_AT.hpp @@ -5,6 +5,17 @@ // 全局 IMEI(读取成功后写入) extern std::string IMEI; +struct RadioInfo +{ + int rsrp = -999; // dBm + int rsrq = -999; // dB + float sinr = -999; // dB + std::string rat; // LTE / NR5G / etc. + std::string band; +}; + +extern RadioInfo g_radio_info; + // 初始化 AT 串口(启动线程) void init_serial_at(const std::string& device, int baudrate); diff --git a/src/serial_AT.cpp b/src/serial_AT.cpp index 470998c..a58b721 100644 --- a/src/serial_AT.cpp +++ b/src/serial_AT.cpp @@ -17,6 +17,9 @@ std::string IMEI; std::mutex imei_mutex; +RadioInfo g_radio_info; +static std::mutex radio_mutex; + // ================== 运行控制 ================== static std::atomic serial_at_running{false}; @@ -105,8 +108,13 @@ void stop_http_server() http_server.reset(); } -// 只保留一个任务:AT+GSN -static std::vector at_tasks = {{"AT+GSN", 3, 0, {}}}; +// IMEI 需要无限重试 +// 信号 AT 命令周期查询(5 秒一次) +// max_retries = -1 → 永不删除 +static std::vector at_tasks = { + {"AT+GSN", -1, 0, {}}, // 直到成功为止 + {"AT+QENG=\"servingcell\"", -1, 0, {}} // RSRP/RSRQ/SINR 查询(周期) +}; // ================== 发送线程 ================== static void serial_at_send_loop() @@ -130,12 +138,16 @@ static void serial_at_send_loop() { auto& task = *it; - if (task.sent_count >= task.max_retries) + // ============================ + // FIX: max_retries = -1 → 无限发送 + // ============================ + if (task.max_retries >= 0 && task.sent_count >= task.max_retries) { it = at_tasks.erase(it); continue; } + // 5 秒一次发送 if (task.last_sent.time_since_epoch().count() == 0 || std::chrono::duration_cast(now - task.last_sent).count() >= 5) { @@ -176,10 +188,63 @@ static void handle_serial_at_data(const std::string& data) LOG_INFO("[serial_at] IMEI = " + line); - std::lock_guard lock(at_tasks_mutex); - at_tasks.clear(); + { + std::lock_guard lock(at_tasks_mutex); + + // 保留非 IMEI 的任务 + at_tasks.erase( + std::remove_if(at_tasks.begin(), at_tasks.end(), [](const AtTask& t) { return t.cmd == "AT+GSN"; }), + at_tasks.end()); + } + return; } + + // ======================= + // QENG="servingcell" 解析 + // ======================= + if (line.rfind("+QENG:", 0) == 0 || line.find("servingcell") != std::string::npos) + { + std::vector tokens; + std::string tmp; + std::stringstream ss(line); + + while (std::getline(ss, tmp, ',')) + { + // 去除双引号和空白 + tmp.erase(std::remove(tmp.begin(), tmp.end(), '"'), tmp.end()); + tmp.erase(0, tmp.find_first_not_of(" \t")); + tmp.erase(tmp.find_last_not_of(" \t") + 1); + tokens.push_back(tmp); + } + + // LTE 格式固定至少 15~17 字段 + if (tokens.size() >= 15) + { + // 对号入座 + int pci = std::stoi(tokens[6]); // 物理小区编号 + int band = std::stoi(tokens[8]); // LTE band + int rsrp = std::stoi(tokens[11]); // dBm + int rsrq = std::stoi(tokens[12]); // dB + int sinr = std::stoi(tokens[14]); // dB + + { + std::lock_guard lk(radio_mutex); + g_radio_info.pci = pci; + g_radio_info.band = band; + g_radio_info.rsrp = rsrp; + g_radio_info.rsrq = rsrq; + g_radio_info.sinr = sinr; + g_radio_info.raw = line; // 保留原始字符串用于调试 + } + + LOG_INFO("[serial_at] QENG parsed: RSRP=" + std::to_string(rsrp) + " RSRQ=" + std::to_string(rsrq) + + " SINR=" + std::to_string(sinr) + " PCI=" + std::to_string(pci) + + " BAND=" + std::to_string(band)); + + continue; + } + } } }