Files
vd_960/vd960DBN/tests/test_snap_to_json.c
wangfq f1c9358aad feat(vd960DBN): TCP/MQTT log_* 命令支持快照流 stream=snapshot (2026-08-18)
- snapshot.c/h: 新增 snap_rec_to_json() — SnapRec 64B → JSON (channels 对齐 0xC0, variation 3B 符号扩展, misc_type 全枚举)
- tcp_json_srv.c: handle_log_stat/query/clear 加 stream 解析 + snapshot 分支
- iot_mqtt_srv.c: log_stat/query/clear 加 stream 解析 + snapshot 分支
- 事件流 count=0 按上限处理 (与 BLE 对齐); 快照 QUERY count≤2, CLEAR ~45ms
- 新增 gcc 隔离单测 tests/test_snap_to_json.c 34 断言全过
- devlog V4.1
2026-08-18 09:01:22 +08:00

165 lines
7.3 KiB
C

#include <stdio.h>
#include <string.h>
#include <stdint.h>
/* mock SnapRec (与 snapshot.h 一致, 64B) */
typedef struct {
uint8_t magic;
uint8_t len;
uint8_t flags;
uint8_t rsvd;
uint32_t seq;
uint32_t ts_ms;
uint16_t boot_seq;
uint16_t rsvd2;
uint8_t coils[48];
} SnapRec;
#define SNAP_COILS_MAX 4
#define SNAP_COIL_BYTES 12
int snap_rec_to_json(const SnapRec *r, char *buf, int buf_len)
{
int coil_count = (r->len > (SNAP_COILS_MAX * SNAP_COIL_BYTES))
? SNAP_COILS_MAX : (r->len / SNAP_COIL_BYTES);
if (coil_count < 0) coil_count = 0;
int pos = snprintf(buf, buf_len,
"{\"seq\":%lu,\"boot_seq\":%u,\"ts_ms\":%lu,\"coil_count\":%u,\"channels\":[",
(unsigned long)r->seq, (unsigned)r->boot_seq,
(unsigned long)r->ts_ms, (unsigned)coil_count);
int c;
for (c = 0; c < coil_count; c++) {
const uint8_t *p = &r->coils[c * SNAP_COIL_BYTES];
uint8_t cfg = p[0];
uint8_t cond = p[1];
uint32_t freq = (uint32_t)p[2] | ((uint32_t)p[3] << 8) | ((uint32_t)p[4] << 16);
int32_t variation = (int32_t)((uint32_t)p[5] | ((uint32_t)p[6] << 8) | ((uint32_t)p[7] << 16));
uint32_t misc = (uint32_t)p[8] | ((uint32_t)p[9] << 8)
| ((uint32_t)p[10] << 16) | ((uint32_t)p[11] << 24);
uint8_t freq_level = (cfg >> 6) & 0x03;
uint8_t direction = (cfg >> 5) & 0x01;
uint8_t freq_type = (cfg >> 4) & 0x01;
uint8_t sensitivity = cfg & 0x0F;
uint8_t condition = (cond >> 4) & 0x0F;
uint8_t loop_state = (cond >> 3) & 0x01; /* 0=normal 1=cut */
uint8_t car_state = (cond >> 2) & 0x01; /* 0=nocar 1=car */
uint8_t misc_type = cond & 0x03;
const char *fl = "high";
const char *mt = "time";
if (variation & 0x800000) variation |= (int32_t)0xFF000000; /* 3B sign extend */
if (freq_level == 1) fl = "mid_high";
else if (freq_level == 2) fl = "mid_low";
else if (freq_level == 3) fl = "low";
if (misc_type == 1) mt = "cut_count";
else if (misc_type == 2) mt = "flow_count";
else if (misc_type == 3) mt = "relay_count";
if (c > 0) pos += snprintf(buf + pos, buf_len - pos, ",");
pos += snprintf(buf + pos, buf_len - pos,
"{\"ch\":%u,\"freq_level\":\"%s\",\"direction\":%u,\"freq_type\":%u,"
"\"sensitivity\":%u,\"condition\":%u,\"loop_ok\":%s,\"has_car\":%s,"
"\"misc_type\":\"%s\",\"freq\":%lu,\"variation\":%ld,\"misc\":%lu}",
(unsigned)(c + 1), fl, (unsigned)direction, (unsigned)freq_type,
(unsigned)sensitivity, (unsigned)condition,
loop_state ? "false" : "true", car_state ? "true" : "false",
mt, (unsigned long)freq, (long)variation, (unsigned long)misc);
}
pos += snprintf(buf + pos, buf_len - pos, "]}");
return pos;
}
#define CHECK(cond) do { if (!(cond)) { printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); fails++; } else { passes++; } } while (0)
static int passes = 0, fails = 0;
int main(void)
{
char buf[2048];
SnapRec r;
/* 用例1: 单线圈, 高频(00)/触发/实时频率/灵敏度2, 正常无车, 时间量
freq=0x010F2A=69418, variation=+7, misc=0 */
memset(&r, 0, sizeof(r));
r.magic = 0xA6; r.len = 12; r.seq = 12345; r.ts_ms = 456789; r.boot_seq = 2;
r.coils[0] = 0x12; /* freq_level=00 direction=0 freq_type=1 sens=0010 */
r.coils[1] = 0x00; /* condition=0 loop_state=0 car_state=0 misc_type=00 */
r.coils[2] = 0x2A; r.coils[3] = 0x0F; r.coils[4] = 0x01; /* 69418 */
r.coils[5] = 0x07; r.coils[6] = 0x00; r.coils[7] = 0x00; /* +7 */
r.coils[8] = 0x00; r.coils[9] = 0x00; r.coils[10] = 0x00; r.coils[11] = 0x00;
snap_rec_to_json(&r, buf, sizeof(buf));
printf("case1: %s\n", buf);
CHECK(strstr(buf, "\"seq\":12345") != NULL);
CHECK(strstr(buf, "\"boot_seq\":2") != NULL);
CHECK(strstr(buf, "\"ts_ms\":456789") != NULL);
CHECK(strstr(buf, "\"coil_count\":1") != NULL);
CHECK(strstr(buf, "\"ch\":1") != NULL);
CHECK(strstr(buf, "\"freq_level\":\"high\"") != NULL);
CHECK(strstr(buf, "\"direction\":0") != NULL);
CHECK(strstr(buf, "\"freq_type\":1") != NULL);
CHECK(strstr(buf, "\"sensitivity\":2") != NULL);
CHECK(strstr(buf, "\"condition\":0") != NULL);
CHECK(strstr(buf, "\"loop_ok\":true") != NULL);
CHECK(strstr(buf, "\"has_car\":false") != NULL);
CHECK(strstr(buf, "\"misc_type\":\"time\"") != NULL);
CHECK(strstr(buf, "\"freq\":69418") != NULL);
CHECK(strstr(buf, "\"variation\":7") != NULL);
CHECK(strstr(buf, "\"misc\":0") != NULL);
/* 用例2: 4线圈全量 + 负 variation + 低频 + relay_count + 有车/断开
ch1: 低频(11)/方向判别(1)/初始频率(0)/灵敏度0, condition=0xF, loop_state=1(断开), car_state=1(有车), misc_type=11(relay)
freq=0x00C383=50051, variation=-10 (F6 FF FF), misc=128 */
memset(&r, 0, sizeof(r));
r.magic = 0xA6; r.len = 48; r.seq = 999; r.ts_ms = 1234; r.boot_seq = 3;
r.coils[0] = 0xE8; /* 11 1 0 0000 */
r.coils[1] = 0xFF; /* 1111 1 1 11 */
r.coils[2] = 0x83; r.coils[3] = 0xC3; r.coils[4] = 0x00; /* 50051 */
r.coils[5] = 0xF6; r.coils[6] = 0xFF; r.coils[7] = 0xFF; /* -10 */
r.coils[8] = 0x80; r.coils[9] = 0x00; r.coils[10] = 0x00; r.coils[11] = 0x00; /* 128 */
/* ch2: 中高(01), 正常无车, cut_count */
r.coils[12] = 0x50; /* 01 0 1 0000 */
r.coils[13] = 0x01; /* 0000 0 0 01 */
r.coils[14] = 0x00; r.coils[15] = 0x02; r.coils[16] = 0x00; /* 512 */
r.coils[17] = 0x01; r.coils[18] = 0x00; r.coils[19] = 0x00; /* +1 */
/* ch3: 中低(10), flow_count */
r.coils[24] = 0xA0; /* 10 1 0 0000 */
r.coils[25] = 0x02; /* 0000 0 0 10 */
r.coils[26] = 0x00; r.coils[27] = 0x01; r.coils[28] = 0x00; /* 256 */
/* ch4: 高频, 空 */
snap_rec_to_json(&r, buf, sizeof(buf));
printf("case2: %s\n", buf);
CHECK(strstr(buf, "\"coil_count\":4") != NULL);
CHECK(strstr(buf, "\"freq_level\":\"low\"") != NULL);
CHECK(strstr(buf, "\"direction\":1") != NULL);
CHECK(strstr(buf, "\"freq_type\":0") != NULL);
CHECK(strstr(buf, "\"sensitivity\":0") != NULL);
CHECK(strstr(buf, "\"condition\":15") != NULL);
CHECK(strstr(buf, "\"loop_ok\":false") != NULL);
CHECK(strstr(buf, "\"has_car\":true") != NULL);
CHECK(strstr(buf, "\"misc_type\":\"relay_count\"") != NULL);
CHECK(strstr(buf, "\"freq\":50051") != NULL);
CHECK(strstr(buf, "\"variation\":-10") != NULL);
CHECK(strstr(buf, "\"misc\":128") != NULL);
CHECK(strstr(buf, "\"freq_level\":\"mid_high\"") != NULL);
CHECK(strstr(buf, "\"misc_type\":\"cut_count\"") != NULL);
CHECK(strstr(buf, "\"freq_level\":\"mid_low\"") != NULL);
CHECK(strstr(buf, "\"misc_type\":\"flow_count\"") != NULL);
/* 用例3: len=0 边界 (空记录) */
memset(&r, 0, sizeof(r));
r.magic = 0xA6; r.len = 0;
snap_rec_to_json(&r, buf, sizeof(buf));
printf("case3: %s\n", buf);
CHECK(strstr(buf, "\"coil_count\":0") != NULL);
CHECK(strstr(buf, "\"channels\":[]") != NULL);
printf("\nPASS=%d FAIL=%d\n", passes, fails);
return fails ? 1 : 0;
}