- 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
165 lines
7.3 KiB
C
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;
|
|
}
|