feat: add button-activated field network diagnostics
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
# 现场网络诊断固件
|
||||
|
||||
GPIO84 原报警/留言按键改为诊断键。释放时按住超过 50 ms、少于 3 秒,开启主动模式。超过或等于 3 秒不执行动作。重复短按只打印已开启,不创建重复任务;重启恢复关闭。该按键不再报警、留言、播放提示音或停止音频。
|
||||
|
||||
设备处于定时休眠、OTA 或关机流程时不响应开启。开启之后遇到这些状态,暂停采样和新探测,已有探测可能继续到返回。没有新增常亮、常唤醒或自动重启操作;主动网络流量本身仍可能影响空闲态和耗电。
|
||||
|
||||
## 查看日志
|
||||
|
||||
使用原有固件日志串口,终端设置 UTF-8,保存完整串口输出。搜索 `[现场诊断]` 可筛出诊断行。时间为开机毫秒(目标平台计数会回绕),不是北京时间。日志不写 Flash、不上传服务器;断网时电脑仍可留存串口日志。
|
||||
|
||||
- 每约 2 秒:注册状态、PDP 数据连接、MQTT、WSS,及 LTE 信号原始字段。
|
||||
- 每约 10 秒:SINR 原始值、LTE 驻留小区 CID/PCI/EARFCN 和 MCC/MNC。标记小区变化。
|
||||
- 主动探测:依次 Ping `223.5.5.5` 和当前业务域名 `banban.api.talkingq.com`,每目标 1 个 32 字节载荷,ICMP 超时 2000 ms;整轮完成后等待 10 秒。域名解析和 SDK 调度可能增加耗时,非严格周期。
|
||||
- 即时事件:数据连接回调、当前 WSS 的建立/断开;注册/PDP 的采样变化、不可用和恢复。
|
||||
- 每次探测后:本次开机的已发送、收到、未回应、未回应率和执行异常数量。超时连续次数遇到收到回应时清零。未获得有效发包统计的 SDK 错误单独计数。
|
||||
|
||||
示例(演示数据,并非现场测量):
|
||||
|
||||
```text
|
||||
[现场诊断][12000 ms][连接] 注册=已注册本地网络(1) 数据连接=已连接 MQTT=已连接 WSS=已连接
|
||||
[现场诊断][12000 ms][信号原始值] RSSI=-71 RSRP=-97 RSRQ=-12 CQI=8;单位/无效值以模组为准
|
||||
[现场诊断][12500 ms][探测成功] 参照节点 223.5.5.5;往返=123 ms
|
||||
[现场诊断][12500 ms][累计统计] 参照节点;已发送=20 收到=19 未回应=1 未回应率=5% 执行异常=0(本次开机累计)
|
||||
```
|
||||
|
||||
## 判断边界
|
||||
|
||||
信号值以 SDK 返回的原始字段记录。必须结合模组实际单位、无效值和无线制式确认后解释,暂不直接分档。注册成功不代表有互联网;MQTT/WSS 状态是业务维护的状态,断网瞬间可能滞后。当前按 LTE 项目设计,其他制式不能直接使用 LTE 字段判读。
|
||||
|
||||
参照节点属于外部公共服务,可能限速或禁 ICMP;业务域名对应入口也可能不回答 Ping。两者均不是 AI 服务健康检查。ICMP 未回应率不等于语音业务丢包率;域名探测结果中的 RTT 是 ICMP 往返时间,不包括 DNS 或 AI 处理时间。
|
||||
|
||||
网络不可用计时依据注册状态和实际 PDP 查询;查询失败不虚构掉线/恢复。时间包含采样误差和暂停期间,不能当作精确无线断线时间。小区变化表示驻留记录改变,不等同于已确认发生连接态切换。
|
||||
|
||||
本次实现侧重网络现场日志,尚未新增每轮对话的 VAD/上传/服务端/首音分段计时,也未新增 DNS/TCP/TLS 独立探测。若网络记录正常但回复仍慢,需结合已有 WSS/TLS/音频日志和服务端记录继续定位。
|
||||
|
||||
## 验证与上板
|
||||
|
||||
运行 `python3 tools/test_net_diag.py`,以 SDK 头文件和模拟模组接口编译运行真实诊断源码,验证任务创建失败重试、重复开启、成功/无回应/执行异常统计、网络恢复计时和休眠暂停;并检查按键分支没有原报警/留言/音频操作。
|
||||
|
||||
macOS 测试仅在临时副本中移除 SDK 与宿主不兼容的 32 位 size_t 定义,不改 SDK 文件。该测试不代表 ARM 完整固件编译或硬件验收。正式构建沿用项目 Windows ARM Compiler 5 构建流程。
|
||||
|
||||
上板后核对:UTF-8 中文是否正确;50 ms 以下抖动、正常短按、重复短按、3 秒以上长按;断网仍可开启诊断;两目标成功和不回应;恢复网络;对话时不因按键中断声音;OTA/定时休眠暂停;重启关闭。核实实际模组信号单位、Ping 回调和内存余量。
|
||||
@@ -66,7 +66,8 @@ SRC_FILES+= \
|
||||
lib/libvad/vad_sp.c \
|
||||
mp3_player.c \
|
||||
http_aofei.c \
|
||||
net_mgt.c \
|
||||
net_mgt.c \
|
||||
net_diag.c \
|
||||
ws_talkingq.c \
|
||||
talkingq_wss.c \
|
||||
talkingq_root_ca.c \
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
#include "ql_rtos.h"
|
||||
#include "ql_gpio.h"
|
||||
#include "ql_uart.h"
|
||||
#include "app_inc.h"
|
||||
#include "app_inc.h"
|
||||
#include "net_diag.h"
|
||||
|
||||
#define KEY_PIN_WAKEUP_MIC GPIO_PIN_NO_53
|
||||
#define KEY_PIN_ALARM GPIO_PIN_NO_84
|
||||
@@ -121,7 +122,7 @@ static void key_thread_entry(void *parameter)
|
||||
unsigned char level = 0;
|
||||
unsigned char volume = 0;
|
||||
unsigned char pre_key_press_mic_counter = 0;
|
||||
unsigned char pre_key_press_alarm_counter = 0;
|
||||
unsigned char pre_key_press_alarm_counter = 0;
|
||||
PIN_LEVEL_E value;
|
||||
|
||||
while (1)
|
||||
@@ -209,30 +210,20 @@ static void key_thread_entry(void *parameter)
|
||||
if(value!=PIN_LEVEL_LOW){//up
|
||||
key_press_alarm_status = 0;
|
||||
|
||||
if(ql_rtos_get_systicks() - key_press_alarm_time > KEY_LONG_PRESS_3S){
|
||||
build_alarm();
|
||||
}
|
||||
else if(ql_rtos_get_systicks() - key_press_alarm_time > KEY_WAIT_TIME){
|
||||
if(!vad_audio_is_recording()&&key_start_leave==0&&get_mqtt_connect_status()==1){
|
||||
key_start_leave = 1;
|
||||
build_leave_to_parent();
|
||||
}else{
|
||||
key_start_leave = 0;
|
||||
app_play_tips_file(2);
|
||||
}
|
||||
}
|
||||
|
||||
/* GPIO84 is diagnostic-only; long holds have no action. */
|
||||
{
|
||||
u32 held = (u32)(ql_rtos_get_systicks() - key_press_alarm_time);
|
||||
if (held > KEY_WAIT_TIME && held < KEY_LONG_PRESS_3S)
|
||||
net_diag_start();
|
||||
}
|
||||
|
||||
key_press_alarm_time = ql_rtos_get_systicks();
|
||||
key_press_alarm_counter = 0;
|
||||
pre_key_press_mic_counter = 0;
|
||||
pre_key_press_alarm_counter = 0;
|
||||
}else{ //一直按着
|
||||
if(key_press_alarm_status==0){
|
||||
key_press_alarm_status = 1;//表示已经按下了
|
||||
//先停掉音频播放线程
|
||||
if(key_start_leave==0&&!vad_audio_is_recording()){
|
||||
stop_player_func();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#ifndef BANBAN_NET_DIAG_H
|
||||
#define BANBAN_NET_DIAG_H
|
||||
void net_diag_start(void);
|
||||
void net_diag_event(const char *event, int code);
|
||||
#endif
|
||||
@@ -0,0 +1,221 @@
|
||||
/* 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_ping.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 ql_ping_stat_t ping_result;
|
||||
static int ping_seen, ping_error;
|
||||
static const char *targets[] = {"223.5.5.5", "banban.api.talkingq.com"};
|
||||
static const char *labels[] = {"参照节点", "业务域名"};
|
||||
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();
|
||||
}
|
||||
|
||||
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(®, 0, sizeof(reg));
|
||||
memset(&data, 0, sizeof(data));
|
||||
sr = ql_nw_get_signal_strength(&signal);
|
||||
rr = ql_nw_get_reg_status(®);
|
||||
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 && (registered == 1 || registered == 5)) {
|
||||
printf("[现场诊断][%lu ms][信号原始值] RSSI=%d RSRP=%d RSRQ=%d CQI=%d;单位/无效值以模组为准\r\n",
|
||||
now_ms(), signal.LTE_SignalStrength.rssi, signal.LTE_SignalStrength.rsrp,
|
||||
signal.LTE_SignalStrength.rsrq, signal.LTE_SignalStrength.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();
|
||||
result = ql_nw_get_sinr(&sinr);
|
||||
if (result == QL_NW_SUCCESS)
|
||||
printf("[现场诊断][%lu ms][信噪比原始值] SINR=%d;编码以模组为准\r\n", now_ms(), sinr);
|
||||
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=%lu MNC=%lu%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 ping_callback(ql_ping_state_e state, int err, ql_ping_stat_t *stat)
|
||||
{
|
||||
(void)state;
|
||||
if (err) ping_error = err;
|
||||
if (stat) { ping_result = *stat; ping_seen = 1; }
|
||||
}
|
||||
|
||||
static void probe_once(int index)
|
||||
{
|
||||
ql_ping_cfg_t cfg;
|
||||
ql_ping_ctx_t *ctx;
|
||||
int ret;
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.pdpCid = 1; cfg.host = (char *)targets[index];
|
||||
cfg.timeout = 2000; cfg.dataSize = 32; cfg.pingNum = 1;
|
||||
ctx = ql_ping_init(cfg, ping_callback);
|
||||
if (!ctx) { ++errors[index]; net_diag_event("探测初始化失败,下轮重试", index); return; }
|
||||
memset(&ping_result, 0, sizeof(ping_result));
|
||||
ping_seen = 0; ping_error = 0;
|
||||
/* SDK declares ql_ping synchronous: callbacks complete before return. */
|
||||
ret = ql_ping(ctx);
|
||||
ql_ping_deinit(ctx);
|
||||
if (ping_seen && ping_result.sentNum) {
|
||||
attempts[index] += ping_result.sentNum;
|
||||
received[index] += ping_result.rcvdNum;
|
||||
if (ping_result.rcvdNum) {
|
||||
streak[index] = 0;
|
||||
printf("[现场诊断][%lu ms][探测成功] %s %s;往返=%lu ms\r\n",
|
||||
now_ms(), labels[index], targets[index], (unsigned long)ping_result.rtt);
|
||||
} else {
|
||||
++streak[index];
|
||||
printf("[现场诊断][%lu ms][探测未回应] %s;连续=%lu 次;可能超时或节点禁Ping,不能单独判定断网\r\n",
|
||||
now_ms(), labels[index], streak[index]);
|
||||
}
|
||||
} else {
|
||||
++errors[index];
|
||||
printf("[现场诊断][%lu ms][探测执行异常] %s;返回=%d 回调错误=%d;无有效发包统计,不计入未回应率\r\n",
|
||||
now_ms(), labels[index], ret, ping_error);
|
||||
}
|
||||
printf("[现场诊断][%lu ms][累计统计] %s;已发送=%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"
|
||||
"[现场诊断] 时间=开机毫秒;串口请选择UTF-8并保存完整日志\r\n"
|
||||
"[现场诊断] 信号/连接约2秒一条;小区约10秒一次;两节点依次探测后间隔10秒\r\n"
|
||||
"[现场诊断] 每节点32字节,ICMP超时2000ms;业务域名探测可能另有解析耗时,不等于WSS/AI处理耗时\r\n"
|
||||
"[现场诊断] 主动流量可能改变空闲态、耗电及故障表现;休眠/升级/关机时暂停新探测\r\n");
|
||||
enabled = 1;
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "ql_ping.h"
|
||||
#include "ql_nw.h"
|
||||
#include "net_diag.h"
|
||||
#include "ql_atcmd.h"
|
||||
#include "http_downfile.h"
|
||||
#include "ota_post_update.h"
|
||||
@@ -96,7 +97,8 @@ void app_data_recall(void)
|
||||
}
|
||||
|
||||
static void ql_data_call_cb(int profile_idx, int nw_status)
|
||||
{
|
||||
{
|
||||
net_diag_event("数据连接回调", nw_status);
|
||||
printf("[APP]ql_data_call_cb: profile(%d) status(%d)\r\n", profile_idx, nw_status);
|
||||
if(nw_status==0){
|
||||
ql_rtos_semaphore_release(net_mgt_datacall_sem);
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
#include <librws.h>
|
||||
#include "cJSON.h"
|
||||
#include "app_inc.h"
|
||||
#include "talkingq_wss.h"
|
||||
#include "talkingq_wss.h"
|
||||
#include "net_diag.h"
|
||||
#include "ql_rtos.h"
|
||||
#include "ql_application.h"
|
||||
#include "ql_audio.h"
|
||||
@@ -222,6 +223,7 @@ static void onopen(rws_socket socket)
|
||||
if (socket != app->socket || !app->init) return;
|
||||
wss_connect_counter=0;
|
||||
|
||||
net_diag_event("WSS连接建立(尚不代表鉴权或AI回复完成)", 0);
|
||||
LOG_D("websocket connected. ");
|
||||
if(ws_connect_sem){
|
||||
ql_rtos_semaphore_release(ws_connect_sem);
|
||||
@@ -245,6 +247,7 @@ static void onclose(rws_socket socket)
|
||||
}
|
||||
|
||||
if (socket != app->socket) return;
|
||||
net_diag_event("WSS连接断开", error_code);
|
||||
xiaoice_wss_connect_status = 0;
|
||||
app->socket = NULL;
|
||||
/* The worker owns and releases this socket after the callback. */
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
"""Run the real diagnostic code with mocked modem APIs (no network traffic)."""
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
base = root / 'ql-application/threadx/interface/custom_app'
|
||||
sdk = root / 'ql-application/threadx/common/include'
|
||||
harness = r'''
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
#include "net_diag.c"
|
||||
static int creates, fail_create, mode, hold, pdp = 1;
|
||||
static unsigned long clock_ms;
|
||||
static int sample_steps, step_limit = 1;
|
||||
static jmp_buf loop_exit;
|
||||
static ql_ping_ctx_t mock_ctx;
|
||||
static ql_ping_cb_t callback;
|
||||
uint64_t hal_os_up_time_ms(void) { return clock_ms; }
|
||||
uint8_t get_sleep_mode(void) { return hold; }
|
||||
int get_mqtt_connect_status(void) { return 1; }
|
||||
unsigned char get_xiaoice_connected_status(void) { return 1; }
|
||||
int fota_start;
|
||||
u8 power_down_status;
|
||||
QlOSStatus ql_rtos_task_create(ql_task_t *task, u32 stack, u8 priority, char *name,
|
||||
void (*entry)(void *), void *arg, ...) {
|
||||
(void)name; (void)entry; (void)arg;
|
||||
assert(stack == 4096 && priority == 120);
|
||||
++creates;
|
||||
if (fail_create == creates) return -9;
|
||||
*task = (void *)1; return 0;
|
||||
}
|
||||
void ql_rtos_task_sleep_ms(u32 ms) {
|
||||
clock_ms += ms;
|
||||
if (step_limit > 1) {
|
||||
++sample_steps;
|
||||
if (sample_steps == 1) pdp = 0;
|
||||
if (sample_steps == 2) pdp = 1;
|
||||
if (sample_steps < step_limit) return;
|
||||
}
|
||||
longjmp(loop_exit, 1);
|
||||
}
|
||||
QL_NW_ERROR_CODE ql_nw_get_signal_strength(QL_NW_SIGNAL_STRENGTH_INFO_T *s) {
|
||||
s->LTE_SignalStrength.rsrp = -97; return QL_NW_SUCCESS;
|
||||
}
|
||||
QL_NW_ERROR_CODE ql_nw_get_reg_status(QL_NW_REG_STATUS_INFO_T *r) {
|
||||
r->data_reg.state = 1; return QL_NW_SUCCESS;
|
||||
}
|
||||
int ql_get_data_call_info(int profile, int version, struct ql_data_call_info *d) {
|
||||
assert(profile == 1 && version == 0); d->v4.state = pdp; return 0;
|
||||
}
|
||||
QL_NW_ERROR_CODE ql_nw_get_sinr(int *s) { *s = 13; return QL_NW_SUCCESS; }
|
||||
QL_NW_ERROR_CODE ql_nw_get_cell_info(QL_NW_CELL_INFO_T *c) {
|
||||
c->lte_info_valid = 1; c->lte_info_num = 1;
|
||||
c->lte_info[0].cid = 123; c->lte_info[0].pci = 42; return QL_NW_SUCCESS;
|
||||
}
|
||||
ql_ping_ctx_t *ql_ping_init(ql_ping_cfg_t cfg, ql_ping_cb_t cb) {
|
||||
assert(cfg.timeout == 2000 && cfg.pingNum == 1 && cfg.dataSize == 32 && cfg.pdpCid == 1);
|
||||
callback = cb; return mode == 3 ? NULL : &mock_ctx;
|
||||
}
|
||||
int ql_ping(ql_ping_ctx_t *ctx) {
|
||||
ql_ping_stat_t s;
|
||||
assert(ctx == &mock_ctx); memset(&s, 0, sizeof(s));
|
||||
if (mode == 2) return -2;
|
||||
s.sentNum = 1; s.rcvdNum = mode == 0; s.rtt = 123;
|
||||
callback(QL_PING_FINISHED, mode == 1 ? -1 : 0, &s); return 0;
|
||||
}
|
||||
void ql_ping_deinit(ql_ping_ctx_t *ctx) { assert(ctx == &mock_ctx); }
|
||||
int main(void) {
|
||||
fail_create = 2;
|
||||
net_diag_start(); assert(!enabled && sample_task && !probe_task);
|
||||
fail_create = 0;
|
||||
net_diag_start(); assert(enabled && creates == 3);
|
||||
net_diag_start(); assert(creates == 3);
|
||||
mode = 0; probe_once(0); assert(attempts[0] == 1 && received[0] == 1 && !streak[0]);
|
||||
mode = 1; probe_once(0); assert(attempts[0] == 2 && received[0] == 1 && streak[0] == 1);
|
||||
mode = 2; probe_once(0); assert(attempts[0] == 2 && errors[0] == 1);
|
||||
mode = 3; probe_once(0); assert(attempts[0] == 2 && errors[0] == 2);
|
||||
mode = 0; probe_once(0); assert(!streak[0]);
|
||||
step_limit = 3;
|
||||
if (!setjmp(loop_exit)) sample_worker(NULL);
|
||||
step_limit = 1;
|
||||
hold = 1;
|
||||
if (!setjmp(loop_exit)) probe_worker(NULL);
|
||||
assert(attempts[1] == 0);
|
||||
puts("PASS: creation failure/retry, idempotent activation, probe counters, sampling, sleep pause");
|
||||
return 0;
|
||||
}
|
||||
'''
|
||||
with tempfile.TemporaryDirectory(prefix='banban-netdiag-') as directory:
|
||||
tmp = Path(directory)
|
||||
for name in ['ql_type.h', 'ql_rtos.h', 'ql_nw.h', 'ql_data_call.h', 'ql_ping.h']:
|
||||
content = (sdk / name).read_bytes()
|
||||
if name == 'ql_type.h':
|
||||
# The ARM SDK defines 32-bit size_t unconditionally. Host tests use libc's.
|
||||
content, count = re.subn(rb'typedef unsigned int\s+size_t;', b'/* host size_t */', content)
|
||||
assert count == 1
|
||||
(tmp / name).write_bytes(content)
|
||||
(tmp / 'test.c').write_text(harness)
|
||||
command = [shutil.which('clang') or 'cc', '-std=c99', '-Werror=implicit-function-declaration',
|
||||
'-Wno-typedef-redefinition']
|
||||
for include in [tmp, base, base / 'inc', base / 'lib/hal', sdk, sdk / 'lwipv4v6']:
|
||||
command.extend(['-I', str(include)])
|
||||
subprocess.run(command + [str(tmp / 'test.c'), '-o', str(tmp / 'test')], check=True)
|
||||
result = subprocess.run([str(tmp / 'test')], check=True, capture_output=True, text=True)
|
||||
print(result.stdout, end='')
|
||||
assert '本次网络不可用观测时长=2000 ms' in result.stdout
|
||||
assert '未回应率=50%' in result.stdout
|
||||
|
||||
key = (base / 'hal_key.c').read_text()
|
||||
alarm = key[key.index('if(key_press_alarm_counter!=0'):key.index('ql_rtos_task_sleep_ms(10);')]
|
||||
assert 'net_diag_start();' in alarm
|
||||
for forbidden in ['build_alarm(', 'build_leave_to_parent(', 'stop_player_func(', 'app_play_tips_file(']:
|
||||
assert forbidden not in alarm, forbidden
|
||||
print('PASS: diagnostic key has no former alarm/message/audio actions')
|
||||
Reference in New Issue
Block a user