Files
banban/ql-application/threadx/interface/custom_app/net_diag.c
T

230 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* On-demand field diagnostics. No PDP reconfiguration or recovery actions. */
#include <stdio.h>
#include <string.h>
#include "app_inc.h"
#include "hal_api.h"
#include "ql_nw.h"
#include "ql_data_call.h"
#include "ql_func.h"
#include "net_diag_probe.h"
#include "net_diag.h"
extern uint8_t get_sleep_mode(void);
extern int get_mqtt_connect_status(void);
extern unsigned char get_xiaoice_connected_status(void);
extern int fota_start;
extern u8 power_down_status;
static volatile int enabled;
static ql_task_t sample_task, probe_task;
static int sinr_unavailable;
static const char *targets[] = {"223.5.5.5", "banban.api.talkingq.com"};
static const char *labels[] = {"参照DNS服务", "业务WSS入口"};
static const unsigned short ports[] = {53, 443};
static unsigned long attempts[2], received[2], streak[2], errors[2];
static const char *registration_name(int state)
{
switch (state) {
case 0: return "未注册";
case 1: return "已注册本地网络";
case 2: return "正在搜索网络";
case 3: return "注册被拒绝";
case 5: return "已注册漫游网络";
case -1: return "读取失败";
default: return "未知状态";
}
}
static unsigned long now_ms(void)
{
return (unsigned long)hal_os_up_time_ms();
}
void net_diag_event(const char *event, int code)
{
if (enabled)
printf("[现场诊断][%lu ms][事件] %s;状态码=%d\r\n", now_ms(), event, code);
}
static int paused(void)
{
return fota_start == 1 || power_down_status != 0 || get_sleep_mode();
}
int net_diag_is_enabled(void) { return enabled; }
int net_diag_can_probe(void) { return enabled && !paused(); }
static void sample_worker(void *arg)
{
int previous_reg = -1, previous_pdp = -1, previous_pause = -1;
unsigned long down_since = 0, next_cell = 0;
int down = 0, had_cell = 0;
unsigned int cell_id = 0, pci = 0;
static QL_NW_CELL_INFO_T cells;
(void)arg;
for (;;) {
if (enabled) {
int hold = paused();
if (hold != previous_pause) {
net_diag_event(hold ? "休眠/升级/关机中,暂停采样与新探测" : "诊断采样运行中", hold);
previous_pause = hold;
}
if (!hold) {
QL_NW_SIGNAL_STRENGTH_INFO_T signal;
QL_NW_REG_STATUS_INFO_T reg;
struct ql_data_call_info data;
int sr, rr, dr, registered, pdp;
memset(&signal, 0, sizeof(signal));
memset(&reg, 0, sizeof(reg));
memset(&data, 0, sizeof(data));
sr = ql_nw_get_signal_strength(&signal);
rr = ql_nw_get_reg_status(&reg);
dr = ql_get_data_call_info(1, 0, &data);
registered = rr == QL_NW_SUCCESS ? (int)reg.data_reg.state : -1;
pdp = dr == 0 ? data.v4.state : -1;
printf("[现场诊断][%lu ms][连接] 注册=%s(%d) 数据连接=%s MQTT=%s WSS=%s\r\n",
now_ms(), registration_name(registered), registered,
pdp == 1 ? "已连接" : (pdp == -1 ? "读取失败" : "未连接"),
get_mqtt_connect_status() == 1 ? "已连接" : "未连接",
get_xiaoice_connected_status() ? "已连接" : "未连接");
if (sr == QL_NW_SUCCESS && rr == QL_NW_SUCCESS &&
(reg.data_reg.rat == QL_NW_ACCESS_TECH_E_UTRAN ||
reg.data_reg.rat == QL_NW_ACCESS_TECH_E_UTRAN_CA)) {
char cqi[32];
if (signal.LTE_SignalStrength.cqi >= 0 && signal.LTE_SignalStrength.cqi <= 15)
snprintf(cqi, sizeof(cqi), "%d", signal.LTE_SignalStrength.cqi);
else snprintf(cqi, sizeof(cqi), "不可用(raw=%d)", signal.LTE_SignalStrength.cqi);
printf("[现场诊断][%lu ms][信号原始值] RSSI=%d RSRP=%d RSRQ=%d CQI=%s;单位/无效值以模组为准\r\n",
now_ms(), signal.LTE_SignalStrength.rssi, signal.LTE_SignalStrength.rsrp,
signal.LTE_SignalStrength.rsrq, cqi);
} else {
printf("[现场诊断][%lu ms][信号] 暂不可判读;接口返回=%d 注册=%d\r\n", now_ms(), sr, registered);
}
if (registered != previous_reg || pdp != previous_pdp) {
net_diag_event("注册或数据连接状态变化(详见连接行)", registered);
previous_reg = registered; previous_pdp = pdp;
}
/* Unknown readings never become a fabricated disconnect/recovery. */
if (rr == QL_NW_SUCCESS && dr == 0) {
int online = (registered == 1 || registered == 5) && pdp == 1;
if (!online && !down) { down = 1; down_since = now_ms(); net_diag_event("网络不可用,开始计时", 0); }
if (online && down) {
printf("[现场诊断][%lu ms][恢复] 本次网络不可用观测时长=%lu ms(含采样误差)\r\n", now_ms(), now_ms() - down_since);
down = 0;
}
}
if ((unsigned long)(now_ms() - next_cell) >= 10000UL || next_cell == 0) {
int sinr, result;
next_cell = now_ms();
if (!sinr_unavailable) {
if (!m_get_api_ptr || !m_get_api_ptr("ql_nw_get_sinr")) {
sinr_unavailable = 1;
net_diag_event("当前内核未导出SINR接口;本次开机跳过该项", 0);
} else {
result = ql_nw_get_sinr(&sinr);
if (result == QL_NW_SUCCESS && sinr != 255)
printf("[现场诊断][%lu ms][信噪比原始值] SINR=%d;编码以模组为准\r\n", now_ms(), sinr);
else if (result == 255 || (result == QL_NW_SUCCESS && sinr == 255)) {
sinr_unavailable = 1;
net_diag_event("SINR接口/数值不可用;本次开机跳过该项", result);
} else net_diag_event("SINR暂时读取失败", result);
}
}
memset(&cells, 0, sizeof(cells));
result = ql_nw_get_cell_info(&cells);
if (result == QL_NW_SUCCESS && cells.lte_info_valid) {
int i;
for (i = 0; i < cells.lte_info_num && i < QL_NW_CELL_MAX_NUM; ++i) {
QL_MCM_NW_LTE_CELL_INFO_T *c = &cells.lte_info[i];
if (c->flag != 0 || c->cid == 0) continue;
printf("[现场诊断][%lu ms][驻留小区] CID=%lu PCI=%lu EARFCN=%lu MCC原始编码=0x%lX MNC原始编码=0x%lX%s\r\n",
now_ms(), (unsigned long)c->cid, (unsigned long)c->pci,
(unsigned long)c->earfcn, (unsigned long)c->mcc, (unsigned long)c->mnc,
had_cell && (cell_id != c->cid || pci != c->pci) ? ";驻留小区发生变化" : "");
cell_id = c->cid; pci = c->pci; had_cell = 1;
break;
}
if (i == cells.lte_info_num || i == QL_NW_CELL_MAX_NUM)
net_diag_event("无有效LTE驻留小区记录", 0);
} else net_diag_event("LTE小区信息不可用", result);
}
}
}
ql_rtos_task_sleep_ms(2000);
}
}
static void probe_once(int index)
{
net_probe_result result;
printf("[现场诊断][%lu ms][探测开始] %s %s:%u;解析后TCP连接,连接超时3000ms\r\n",
now_ms(), labels[index], targets[index], (unsigned int)ports[index]);
net_diag_probe(targets[index], ports[index], &result);
if (result.outcome == PROBE_CANCELLED) {
net_diag_event("状态变化,已取消本轮新连接探测", 0); return;
}
if (result.outcome == PROBE_OK || result.outcome == PROBE_CONNECT || result.outcome == PROBE_TIMEOUT) {
++attempts[index];
if (result.outcome == PROBE_OK) {
++received[index]; streak[index] = 0;
printf("[现场诊断][%lu ms][探测成功] %s;IP=%s 解析=%lu ms TCP建连=%lu ms(非AI响应时间)\r\n",
now_ms(), labels[index], result.address, result.dns_ms, result.tcp_ms);
} else {
++streak[index];
printf("[现场诊断][%lu ms][探测失败] %s;阶段=%s IP=%s 解析=%lu ms TCP等待=%lu ms 错误=%d 连续失败=%lu\r\n",
now_ms(), labels[index], result.outcome == PROBE_TIMEOUT ? "TCP超时" : "TCP拒绝/连接错误",
result.address, result.dns_ms, result.tcp_ms, result.error, streak[index]);
}
} else {
++errors[index];
printf("[现场诊断][%lu ms][探测执行异常] 目标=%s 阶段=%s 错误=%d 解析返回=%d 解析=%lu ms;不计入TCP失败率\r\n",
now_ms(), labels[index], result.outcome == PROBE_DNS ? "域名解析" : "本地套接字/数据连接",
result.error, result.dns_result, result.dns_ms);
}
printf("[现场诊断][%lu ms][累计统计] %s;TCP有效尝试=%lu 成功=%lu 失败=%lu 失败率=%lu%% 执行异常=%lu;非业务丢包率\r\n",
now_ms(), labels[index], attempts[index], received[index], attempts[index] - received[index],
attempts[index] ? (attempts[index] - received[index]) * 100UL / attempts[index] : 0, errors[index]);
}
static void probe_worker(void *arg)
{
(void)arg;
for (;;) {
if (enabled && !paused()) {
int i;
for (i = 0; i < 2; ++i) {
struct ql_data_call_info data;
memset(&data, 0, sizeof(data));
if (paused()) break;
if (ql_get_data_call_info(1, 0, &data) != 0 || data.v4.state != 1) {
net_diag_event("数据连接未就绪,跳过主动探测", 0); break;
}
probe_once(i);
}
}
ql_rtos_task_sleep_ms(10000);
}
}
/* Called only by the serialized key worker, never from the GPIO interrupt. */
void net_diag_start(void)
{
int ret;
if (enabled) { net_diag_event("主动模式已开启,继续采集;重启退出", 1); return; }
if (!sample_task) {
ret = ql_rtos_task_create(&sample_task, 4096, 120, "net_sample", sample_worker, NULL);
if (ret != 0) { sample_task = NULL; printf("[现场诊断][错误] 采样任务创建失败=%d,请短按重试\r\n", ret); return; }
}
if (!probe_task) {
ret = ql_rtos_task_create(&probe_task, 4096, 120, "net_probe", probe_worker, NULL);
if (ret != 0) { probe_task = NULL; printf("[现场诊断][错误] 探测任务创建失败=%d,请短按重试\r\n", ret); return; }
}
printf("\r\n[现场诊断] ===== 主动模式开启:重启退出 =====\r\n");
printf("[现场诊断] 时间=开机毫秒;UART与USB CDC同步,UTF-8;下一轮录音开始记录分段耗时\r\n");
printf("[现场诊断] 信号/连接约2秒一条;小区约10秒一次;两节点依次探测后间隔10秒\r\n");
printf("[现场诊断] 主动探测改用DNS/TCP,不依赖可选Ping接口;3000ms连接超时不含DNS解析耗时\r\n");
printf("[现场诊断] 主动流量可能改变空闲态、耗电及故障表现;休眠/升级/关机时暂停新探测\r\n");
enabled = 1;
}