Files
vd_960/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c
T
wangfq c515638032 fix: MQTT协议 V1.02 同步 — 字段名缩短 + 时间单位修正
- docs/DLD960_IoT_MQTT协议.md: 字段缩写 + 时间单位 5ms→50ms
- vd960DBN/iot_mqtt_srv.c: 6字段缩短对齐协议 (level/iscar/freq/diff/sens/cndtn)
- vd960DBN/tcp_json_srv.c: gap_ms5/passtime_ms → gap/passtime
- vd960DBN/loop_uart_proto.h: 注释 5ms→50ms
- DBNMQTTool/main.py: 模拟样本数据字段同步
2026-07-09 10:18:03 +08:00

1340 lines
54 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.
/**
******************************************************************************
* @file tcp_json_srv.c
* @author wangfq
* @version V1.0
* @date 2026-06-30
* @brief TCP JSON protocol server implementation
* DLD960 TCP JSON protocol (port 5960) — auth + 15 commands
******************************************************************************
*/
#include "CONFIG.h"
#include "tcp_json_srv.h"
#include "eth_driver.h"
#include "wchnet.h"
#include "net_srv.h"
#include "cmcng.h"
#include "loop_uart_proto.h"
#include "simple_json.h"
#include "storage.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
/*===========================================================================
* Global State
*===========================================================================*/
uint8_t g_json_socket_listen = 0xFF; // TCP socket ID — handles both listen + client data
TcpJsonAuthState g_json_auth_state = JSON_STATE_WAIT_AUTH;
uint32_t g_json_auth_timer = 0;
uint8_t g_json_pwd_retry = 0;
uint32_t g_json_lockout_timer = 0;
TcpJsonPending g_json_pending = {0, 0, 0, "", 0};
ReportConfig g_report_cfg = {0}; // 上电默认关闭上报
uint32_t g_json_last_comm_ts = 0; // 最后通信时间戳
uint32_t g_json_last_connect_ts = 0; // 最后连接时间戳
uint8_t g_json_restart_count = 0; // 自动重启计数
uint32_t g_json_restart_cooldown = 0; // 冷却期截止时间
/*===========================================================================
* Frame Receive Buffer (line-delimited JSON)
*===========================================================================*/
static uint8_t g_json_recv_buf[TCP_JSON_RECV_BUF_LEN];
static uint16_t g_json_recv_len = 0;
static uint8_t g_json_wchnet_buf[RECE_BUF_LEN]; // WCHNET internal recv buffer
/*===========================================================================
* Internal helpers
*===========================================================================*/
static char g_tmp_value[256]; // reusable buffer for simple_parse_json
static void reset_tmp(void) {
memset(g_tmp_value, 0, sizeof(g_tmp_value));
}
static void json_send_error(uint8_t socket, uint32_t msg_id, const char *cmd,
int code, const char *msg) {
char *out = (char *)malloc(256);
if (!out) return;
snprintf(out, 256,
"{\"msg_id\":%lu,\"cmd\":\"%s\",\"ts\":%lu,\"code\":%d,\"msg\":\"%s\"}\n",
msg_id, cmd ? cmd : "", (unsigned long)mstick(), code, msg);
uint32_t slen = strlen(out);
WCHNET_SocketSend(socket, (uint8_t *)out, &slen);
free(out);
}
static void json_send_ok(uint8_t socket, uint32_t msg_id, const char *cmd,
const char *data_json) {
char *out = (char *)malloc(TCP_JSON_MAX_FRAME);
if (!out) return;
if (data_json && strlen(data_json) > 0) {
snprintf(out, TCP_JSON_MAX_FRAME,
"{\"msg_id\":%lu,\"cmd\":\"%s\",\"ts\":%lu,\"code\":0,\"msg\":\"success\",\"data\":%s}\n",
msg_id, cmd, (unsigned long)mstick(), data_json);
} else {
snprintf(out, TCP_JSON_MAX_FRAME,
"{\"msg_id\":%lu,\"cmd\":\"%s\",\"ts\":%lu,\"code\":0,\"msg\":\"success\"}\n",
msg_id, cmd, (unsigned long)mstick());
}
uint32_t slen = strlen(out);
WCHNET_SocketSend(socket, (uint8_t *)out, &slen);
free(out);
}
/* JSON field extractors — parse msg_id, cmd, and data object from raw JSON */
static uint32_t json_get_msg_id(const char *json) {
reset_tmp();
simple_parse_json(json, "\"msg_id\"", g_tmp_value);
if (strlen(g_tmp_value) == 0) return 0;
return (uint32_t)strtoul(g_tmp_value, NULL, 10);
}
static char *json_get_data_str(const char *json) {
char *data = (char *)malloc(TCP_JSON_MAX_FRAME);
if (!data) return NULL;
memset(data, 0, TCP_JSON_MAX_FRAME);
simple_parse_json(json, "\"data\"", data);
if (strlen(data) == 0) {
free(data);
return NULL;
}
return data;
}
static void json_get_str_field(const char *json, const char *key, char *out, int out_len) {
reset_tmp();
simple_parse_json(json, key, g_tmp_value);
// strip surrounding quotes if present
char *src = g_tmp_value;
if (src[0] == '"') src++;
int len = strlen(src);
if (len > 0 && src[len - 1] == '"') len--;
int copy_len = (len < out_len - 1) ? len : out_len - 1;
memcpy(out, src, copy_len);
out[copy_len] = '\0';
}
static void json_get_cmd(const char *json, char *out, int out_len) {
json_get_str_field(json, "\"cmd\"", out, out_len);
}
static uint32_t json_get_uint_field(const char *json, const char *key) {
reset_tmp();
simple_parse_json(json, key, g_tmp_value);
if (strlen(g_tmp_value) == 0) return 0;
return (uint32_t)strtoul(g_tmp_value, NULL, 10);
}
static uint8_t json_get_bool_field(const char *json, const char *key) {
reset_tmp();
simple_parse_json(json, key, g_tmp_value);
if (strstr(g_tmp_value, "true")) return 1;
return 0;
}
/*===========================================================================
* Frame parser: extract line-delimited JSON frames from receive buffer
*===========================================================================*/
static int json_extract_frame(uint8_t *buf, uint16_t *len, char *frame_out, uint16_t frame_size) {
uint16_t i;
for (i = 0; i < *len; i++) {
if (buf[i] == '\n') {
uint16_t frame_len = i;
if (frame_len >= frame_size) frame_len = frame_size - 1;
memcpy(frame_out, buf, frame_len);
frame_out[frame_len] = '\0';
// Remove consumed frame from buffer
uint16_t remaining = *len - i - 1;
if (remaining > 0) {
memmove(buf, buf + i + 1, remaining);
}
*len = remaining;
return 1;
}
}
return 0; // no complete frame yet
}
/*===========================================================================
* Command Handlers
*===========================================================================*/
/* 4.1 pwd_verify */
static void handle_pwd_verify(uint8_t socket, uint32_t msg_id, const char *json) {
PRINT("JSON: pwd_verify handler entered\n");
char *data = json_get_data_str(json);
if (!data) {
PRINT("JSON: pwd_verify — data is NULL\n");
json_send_error(socket, msg_id, "pwd_verify", JSON_CODE_PARAM_ERR, "missing data");
return;
}
PRINT("JSON: pwd_verify — data=[%s]\n", data);
char password[16] = {0};
json_get_str_field(data, "\"password\"", password, sizeof(password));
free(data);
PRINT("JSON: pwd_verify — password=[%s] len=%d\n", password, strlen(password));
PRINT("JSON: pwd_verify — dev_pwd=[%c%c%c%c%c%c]\n",
g_dev_password[0], g_dev_password[1], g_dev_password[2],
g_dev_password[3], g_dev_password[4], g_dev_password[5]);
if (strlen(password) == 0) {
json_send_error(socket, msg_id, "pwd_verify", JSON_CODE_PARAM_ERR, "missing password");
return;
}
// Compare password
if (memcmp(password, g_dev_password, 6) == 0 && strlen(password) == 6) {
g_json_auth_state = JSON_STATE_AUTHED;
g_json_pwd_retry = 0;
g_json_auth_timer = 0;
json_send_ok(socket, msg_id, "pwd_verify", NULL);
PRINT("JSON: Auth success\n");
} else {
g_json_pwd_retry++;
if (g_json_pwd_retry >= TCP_JSON_MAX_PWD_RETRY) {
PRINT("JSON: Auth failed %d times → restart\n", g_json_pwd_retry);
// 先发错误再重启,让 TCP 栈有时间 flush
json_send_error(socket, msg_id, "pwd_verify", JSON_CODE_AUTH_FAIL,
"password incorrect, connection will restart");
Delay_Ms(100);
tcp_json_restart();
} else {
json_send_error(socket, msg_id, "pwd_verify", JSON_CODE_AUTH_FAIL, "password incorrect");
}
}
}
/* 4.2 dev_serial_set */
static void handle_dev_serial_set(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "dev_serial_set", JSON_CODE_PARAM_ERR, "missing data");
return;
}
char serial[32] = {0};
json_get_str_field(data, "\"dev_serial\"", serial, sizeof(serial));
free(data);
if (strlen(serial) != 12) {
json_send_error(socket, msg_id, "dev_serial_set", JSON_CODE_PARAM_ERR, "invalid serial length");
return;
}
// Convert hex string to bytes
uint8_t serial_bytes[6];
for (int i = 0; i < 6; i++) {
char hex[3] = {serial[i * 2], serial[i * 2 + 1], '\0'};
serial_bytes[i] = (uint8_t)strtoul(hex, NULL, 16);
}
alter_dev_serila(serial_bytes);
memcpy(g_dev_number, serial_bytes, 6);
sprintf(g_dev_number_str, "%02X%02X%02X%02X%02X%02X",
g_dev_number[0], g_dev_number[1], g_dev_number[2],
g_dev_number[3], g_dev_number[4], g_dev_number[5]);
json_send_ok(socket, msg_id, "dev_serial_set", NULL);
PRINT("JSON: dev_serial_set -> %s\n", g_dev_number_str);
}
/* 4.3 dev_info_query */
static void handle_dev_info_query(uint8_t socket, uint32_t msg_id, const char *json) {
char data_json[512];
snprintf(data_json, sizeof(data_json),
"{\"dev_serial\":\"%s\",\"hard_ver\":\"%s\",\"soft_ver\":\"%s\","
"\"model\":\"%s\",\"product_code\":\"960001\","
"\"sub_code\":{\"net\":%s,\"iot\":%s},"
"\"bus\":{\"bus1\":0,\"bus2\":0,\"bus3\":0,\"bus4\":0}}",
g_dev_number_str, HARDWARE_VER, FIRMWARE_VER,
PRODUCT_MODEL,
g_sub_code_enable.net_enable ? "true" : "false",
g_sub_code_enable.iot_enable ? "true" : "false");
json_send_ok(socket, msg_id, "dev_info_query", data_json);
}
/* 4.4 ssc_net_set */
static void handle_ssc_net_set(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "ssc_net_set", JSON_CODE_PARAM_ERR, "missing data");
return;
}
Local_Net_Cfg lcfg = local_net_cfg; // copy current
NET_CENTER_INFO ccfg = net_center_info;
char ip_str[16] = {0};
json_get_str_field(data, "\"dev_ip\"", ip_str, sizeof(ip_str));
if (strlen(ip_str) > 0) get_ipstr_to_array(ip_str, lcfg.lip);
char mask_str[16] = {0};
json_get_str_field(data, "\"subnet_mask\"", mask_str, sizeof(mask_str));
if (strlen(mask_str) > 0) get_ipstr_to_array(mask_str, lcfg.sub);
char gw_str[16] = {0};
json_get_str_field(data, "\"route_ip\"", gw_str, sizeof(gw_str));
if (strlen(gw_str) > 0) get_ipstr_to_array(gw_str, lcfg.gw);
char lssc_ip_str[16] = {0};
json_get_str_field(data, "\"lssc_ip\"", lssc_ip_str, sizeof(lssc_ip_str));
if (strlen(lssc_ip_str) > 0) get_ipstr_to_array(lssc_ip_str, ccfg.lssc_ip);
char dns_str[16] = {0};
json_get_str_field(data, "\"dns\"", dns_str, sizeof(dns_str));
if (strlen(dns_str) > 0) get_ipstr_to_array(dns_str, lcfg.dns);
uint32_t port = json_get_uint_field(data, "\"port\"");
if (port > 0 && port <= 65535) ccfg.tcp_port = (uint16_t)port;
free(data);
// Write to flash and update globals
write_net_config(&lcfg, &ccfg, &iot_net_info, &g_iot_topic);
local_net_cfg = lcfg;
net_center_info = ccfg;
json_send_ok(socket, msg_id, "ssc_net_set", NULL);
PRINT("JSON: ssc_net_set done\n");
}
/* 4.5 ssc_net_query */
static void handle_ssc_net_query(uint8_t socket, uint32_t msg_id, const char *json) {
char data_json[400];
snprintf(data_json, sizeof(data_json),
"{\"dev_ip\":\"%d.%d.%d.%d\",\"subnet_mask\":\"%d.%d.%d.%d\","
"\"route_ip\":\"%d.%d.%d.%d\",\"lssc_ip\":\"%d.%d.%d.%d\","
"\"dns\":\"%d.%d.%d.%d\",\"port\":%d}",
local_net_cfg.lip[0], local_net_cfg.lip[1], local_net_cfg.lip[2], local_net_cfg.lip[3],
local_net_cfg.sub[0], local_net_cfg.sub[1], local_net_cfg.sub[2], local_net_cfg.sub[3],
local_net_cfg.gw[0], local_net_cfg.gw[1], local_net_cfg.gw[2], local_net_cfg.gw[3],
net_center_info.lssc_ip[0], net_center_info.lssc_ip[1],
net_center_info.lssc_ip[2], net_center_info.lssc_ip[3],
local_net_cfg.dns[0], local_net_cfg.dns[1], local_net_cfg.dns[2], local_net_cfg.dns[3],
net_center_info.tcp_port);
json_send_ok(socket, msg_id, "ssc_net_query", data_json);
}
/* 4.6 iot_net_set */
static void handle_iot_net_set(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "iot_net_set", JSON_CODE_PARAM_ERR, "missing data");
return;
}
IOT_NET_INFO icfg = iot_net_info;
char host[64] = {0};
json_get_str_field(data, "\"host\"", host, sizeof(host));
if (strlen(host) > 0) strncpy((char *)icfg.remote_addr, host, 63);
uint32_t port = json_get_uint_field(data, "\"port\"");
if (port > 0 && port <= 65535) icfg.mqtt_port = (uint16_t)port;
char client_id[64] = {0};
json_get_str_field(data, "\"client_id\"", client_id, sizeof(client_id));
if (strlen(client_id) > 0) strncpy((char *)icfg.client_id, client_id, 63);
char username[64] = {0};
json_get_str_field(data, "\"username\"", username, sizeof(username));
if (strlen(username) > 0) strncpy((char *)icfg.username, username, 63);
char password[32] = {0};
json_get_str_field(data, "\"password\"", password, sizeof(password));
if (strlen(password) > 0) strncpy((char *)icfg.password, password, 31);
free(data);
write_net_config(&local_net_cfg, &net_center_info, &icfg, &g_iot_topic);
iot_net_info = icfg;
json_send_ok(socket, msg_id, "iot_net_set", NULL);
PRINT("JSON: iot_net_set done\n");
}
/* 4.7 iot_net_query */
static void handle_iot_net_query(uint8_t socket, uint32_t msg_id, const char *json) {
char data_json[400];
snprintf(data_json, sizeof(data_json),
"{\"host\":\"%s\",\"port\":%d,\"client_id\":\"%s\","
"\"username\":\"%s\",\"password\":\"%s\"}",
iot_net_info.remote_addr, iot_net_info.mqtt_port,
iot_net_info.client_id, iot_net_info.username, iot_net_info.password);
json_send_ok(socket, msg_id, "iot_net_query", data_json);
}
/* 4.8 iot_topic_set */
static void handle_iot_topic_set(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "iot_topic_set", JSON_CODE_PARAM_ERR, "missing data");
return;
}
IOT_Topic topic = g_iot_topic;
topic.clientid_enable = json_get_bool_field(data, "\"client_id_enable\"");
char topic_pub[64] = {0};
json_get_str_field(data, "\"topic_pub\"", topic_pub, sizeof(topic_pub));
if (strlen(topic_pub) > 0) strncpy((char *)topic.topic_pub, topic_pub, 63);
char topic_sub[64] = {0};
json_get_str_field(data, "\"topic_sub\"", topic_sub, sizeof(topic_sub));
if (strlen(topic_sub) > 0) strncpy((char *)topic.topic_sub, topic_sub, 63);
free(data);
write_net_config(&local_net_cfg, &net_center_info, &iot_net_info, &topic);
g_iot_topic = topic;
json_send_ok(socket, msg_id, "iot_topic_set", NULL);
PRINT("JSON: iot_topic_set done\n");
}
/* 4.9 iot_topic_query */
static void handle_iot_topic_query(uint8_t socket, uint32_t msg_id, const char *json) {
char data_json[300];
snprintf(data_json, sizeof(data_json),
"{\"client_id_enable\":%s,\"topic_pub\":\"%s\",\"topic_sub\":\"%s\"}",
g_iot_topic.clientid_enable ? "true" : "false",
g_iot_topic.topic_pub, g_iot_topic.topic_sub);
json_send_ok(socket, msg_id, "iot_topic_query", data_json);
}
/* 4.10 pwd_set */
static void handle_pwd_set(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "pwd_set", JSON_CODE_PARAM_ERR, "missing data");
return;
}
char old_pwd[16] = {0}, new_pwd[16] = {0};
json_get_str_field(data, "\"old_password\"", old_pwd, sizeof(old_pwd));
json_get_str_field(data, "\"new_password\"", new_pwd, sizeof(new_pwd));
free(data);
if (strlen(old_pwd) != 6 || strlen(new_pwd) != 6) {
json_send_error(socket, msg_id, "pwd_set", JSON_CODE_PARAM_ERR, "password must be 6 digits");
return;
}
if (memcmp(old_pwd, g_dev_password, 6) != 0) {
json_send_error(socket, msg_id, "pwd_set", JSON_CODE_AUTH_FAIL, "old password incorrect");
return;
}
memcpy(g_dev_password, new_pwd, 6);
set_ble_safe_pass((uint8_t *)new_pwd);
json_send_ok(socket, msg_id, "pwd_set", NULL);
PRINT("JSON: pwd_set done\n");
}
/* 4.11 factory_reset */
static void handle_factory_reset(uint8_t socket, uint32_t msg_id, const char *json) {
json_send_ok(socket, msg_id, "factory_reset", NULL);
PRINT("JSON: factory_reset — resetting...\n");
Delay_Ms(100);
factory_dev_info();
Delay_Ms(100);
NVIC_SystemReset();
}
/* 4.12 device_reset */
static void handle_device_reset(uint8_t socket, uint32_t msg_id, const char *json) {
// No response — device resets immediately
char *out = (char *)malloc(128);
if (out) {
snprintf(out, 128,
"{\"msg_id\":%lu,\"cmd\":\"device_reset\",\"ts\":%lu,\"code\":0,\"msg\":\"resetting\"}\n",
msg_id, (unsigned long)mstick());
uint32_t slen = strlen(out);
WCHNET_SocketSend(socket, (uint8_t *)out, &slen);
free(out);
}
Delay_Ms(100);
NVIC_SystemReset();
}
/* 4.13 loop_param_set — 设置地感线圈多路参数 (CMD 0x63) */
static void handle_loop_param_set(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "loop_param_set", JSON_CODE_PARAM_ERR, "missing data");
return;
}
LUP_ParamSet ps;
memset(&ps, 0, sizeof(ps));
ps.auto_mode = (uint8_t)json_get_uint_field(data, "\"auto_mode\"");
// Parse channels array
// Use a simple approach: parse top-level fields for each known channel key
uint32_t amount = json_get_uint_field(data, "\"amount\"");
if (amount == 0 || amount > LUP_COIL_COUNT) {
amount = LUP_COIL_COUNT;
}
ps.amount = (uint8_t)amount;
// Parse per-channel data
char ch_key[16];
uint8_t ch;
for (ch = 0; ch < ps.amount; ch++) {
snprintf(ch_key, sizeof(ch_key), "\"ch%d\"", ch + 1);
char ch_data[512] = {0};
reset_tmp();
simple_parse_json(data, ch_key, g_tmp_value);
if (strlen(g_tmp_value) == 0) continue;
strncpy(ch_data, g_tmp_value, sizeof(ch_data) - 1);
LUP_CoilParam *cp = &ps.params[ch];
// sensitivity: 低4位=灵敏度, 高4位=freq_level
uint32_t sens_val = json_get_uint_field(ch_data, "\"sensitivity\"");
uint32_t freq_lev = json_get_uint_field(ch_data, "\"freq_level\""); // 0~3
cp->sensitivity = ((uint8_t)(freq_lev & 0x03) << 4) | (uint8_t)(sens_val & 0x0F);
cp->loop_delay = (uint8_t)json_get_uint_field(ch_data, "\"loop_delay\"");
// output_mode: 低3位=输出方式, 高5位=SafeMode
uint32_t out_mode = json_get_uint_field(ch_data, "\"output_mode\"");
uint32_t safe_mode = json_get_uint_field(ch_data, "\"safe_mode\"");
cp->output_mode = ((uint8_t)(safe_mode & 0x1F) << 3) | (uint8_t)(out_mode & 0x07);
cp->exist_mode = (uint8_t)json_get_uint_field(ch_data, "\"exist_mode\"");
// direction_mode: 低3位=方向模式, 高4位=Function_Mode
uint32_t dir_mode = json_get_uint_field(ch_data, "\"direction_mode\"");
uint32_t func_mode = json_get_uint_field(ch_data, "\"function_mode\"");
cp->direction_mode = ((uint8_t)(func_mode & 0x0F) << 4) | (uint8_t)(dir_mode & 0x07);
}
free(data);
// Send to Loop MCU and defer response
lup_send_set_param(&ps);
// Save pending state for async response
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_param_set", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 500;
PRINT("JSON: loop_param_set sent (ch=%d)\n", ps.amount);
}
/* 4.14 loop_param_query — 读取地感线圈多路参数 (CMD 0x64) */
static void handle_loop_param_query(uint8_t socket, uint32_t msg_id, const char *json) {
// Send query to Loop MCU and defer response
lup_send_get_param();
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_param_query", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 500;
PRINT("JSON: loop_param_query sent\n");
}
/* 4.15 report_config — 配置主动上报参数 */
static void handle_report_config(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (data) {
reset_tmp();
// sensor_type (int)
simple_parse_json(data, "\"sensor_type\"", g_tmp_value);
if (strlen(g_tmp_value) > 0) {
g_report_cfg.sensor_type = (uint8_t)strtoul(g_tmp_value, NULL, 10);
}
// enable (bool)
memset(g_tmp_value, 0, sizeof(g_tmp_value));
simple_parse_json(data, "\"enable\"", g_tmp_value);
if (strlen(g_tmp_value) > 0) {
g_report_cfg.enable = (uint8_t)(strstr(g_tmp_value, "true") ? 1 : 0);
}
// once (bool)
memset(g_tmp_value, 0, sizeof(g_tmp_value));
simple_parse_json(data, "\"once\"", g_tmp_value);
if (strlen(g_tmp_value) > 0) {
g_report_cfg.once = (uint8_t)(strstr(g_tmp_value, "true") ? 1 : 0);
}
// env_eval (bool)
memset(g_tmp_value, 0, sizeof(g_tmp_value));
simple_parse_json(data, "\"env_eval\"", g_tmp_value);
if (strlen(g_tmp_value) > 0) {
g_report_cfg.env_eval = (uint8_t)(strstr(g_tmp_value, "true") ? 1 : 0);
}
// interval (int)
memset(g_tmp_value, 0, sizeof(g_tmp_value));
simple_parse_json(data, "\"interval\"", g_tmp_value);
if (strlen(g_tmp_value) > 0) {
g_report_cfg.interval = (uint16_t)strtoul(g_tmp_value, NULL, 10);
}
// ack_required (bool)
memset(g_tmp_value, 0, sizeof(g_tmp_value));
simple_parse_json(data, "\"ack_required\"", g_tmp_value);
if (strlen(g_tmp_value) > 0) {
g_report_cfg.ack_required = (uint8_t)(strstr(g_tmp_value, "true") ? 1 : 0);
}
// timeout (int)
memset(g_tmp_value, 0, sizeof(g_tmp_value));
simple_parse_json(data, "\"timeout\"", g_tmp_value);
if (strlen(g_tmp_value) > 0) {
g_report_cfg.timeout = (uint16_t)strtoul(g_tmp_value, NULL, 10);
}
// 兼容旧协议 active_report
memset(g_tmp_value, 0, sizeof(g_tmp_value));
simple_parse_json(data, "\"active_report\"", g_tmp_value);
if (strlen(g_tmp_value) > 0) {
g_report_cfg.enable = (uint8_t)(strstr(g_tmp_value, "true") ? 1 : 0);
}
free(data);
}
char data_json[256];
snprintf(data_json, sizeof(data_json),
"{\"sensor_type\":%d,\"enable\":%s,\"once\":%s,\"env_eval\":%s,"
"\"interval\":%u,\"ack_required\":%s,\"timeout\":%u}",
g_report_cfg.sensor_type,
g_report_cfg.enable ? "true" : "false",
g_report_cfg.once ? "true" : "false",
g_report_cfg.env_eval ? "true" : "false",
g_report_cfg.interval,
g_report_cfg.ack_required ? "true" : "false",
g_report_cfg.timeout);
json_send_ok(socket, msg_id, "report_config", data_json);
PRINT("JSON: report_config enable=%d interval=%u sensor=%d\\n",
g_report_cfg.enable, g_report_cfg.interval, g_report_cfg.sensor_type);
}
/* 4.16 loop_version_query — 获取地感MCU版本号 (CMD 0x4A) */
static void handle_loop_version_query(uint8_t socket, uint32_t msg_id, const char *json) {
lup_send_get_version();
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_version_query", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 300;
PRINT("JSON: loop_version_query sent\n");
}
/* 4.17 loop_reset — 复位地感MCU (CMD 0x6D, 无回复) */
static void handle_loop_reset(uint8_t socket, uint32_t msg_id, const char *json) {
lup_send_reset();
json_send_ok(socket, msg_id, "loop_reset", NULL);
PRINT("JSON: loop_reset sent (no response expected)\n");
}
/* 4.18 loop_factory_init — 地感MCU出厂初始化 (CMD 0x92) */
static void handle_loop_factory_init(uint8_t socket, uint32_t msg_id, const char *json) {
lup_send_factory_init();
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_factory_init", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 600;
PRINT("JSON: loop_factory_init sent\n");
}
/* 4.19 loop_sens_read — 读取线圈灵敏度列表 (CMD 0x8A Read) */
static void handle_loop_sens_read(uint8_t socket, uint32_t msg_id, const char *json) {
lup_send_sensitivity_read();
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_sens_read", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 300;
PRINT("JSON: loop_sens_read sent\n");
}
/* 4.20 loop_sens_write — 写入线圈灵敏度列表 (CMD 0x8A Write) */
static void handle_loop_sens_write(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "loop_sens_write", JSON_CODE_PARAM_ERR, "missing data");
return;
}
LUP_Sensitivity sens;
memset(&sens, 0, sizeof(sens));
sens.rw = LUP_SENS_RW_WRITE;
uint32_t amount = json_get_uint_field(data, "\"amount\"");
if (amount == 0 || amount > LUP_COIL_COUNT) amount = LUP_COIL_COUNT;
sens.amount = (uint8_t)amount;
char key[16];
uint8_t i;
for (i = 0; i < sens.amount; i++) {
snprintf(key, sizeof(key), "\"ch%d\"", i + 1);
char ch_data[256] = {0};
reset_tmp();
simple_parse_json(data, key, g_tmp_value);
if (strlen(g_tmp_value) == 0) continue;
strncpy(ch_data, g_tmp_value, sizeof(ch_data) - 1);
sens.sens_in[i] = (uint16_t)json_get_uint_field(ch_data, "\"sens_in\"");
sens.sens_out[i] = (uint16_t)json_get_uint_field(ch_data, "\"sens_out\"");
}
free(data);
uint8_t buf[LUP_MAX_PKG_LEN];
uint16_t len = lup_build_sensitivity_write(buf, &sens);
UART2_SendString(buf, len);
PRINT("LUP Tx:");
for (i = 0; i < len; i++) PRINT(" %02X", buf[i]);
PRINT("\n");
lup_cmd_begin(LUP_CMD_SENSITIVITY, 300);
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_sens_write", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 300;
PRINT("JSON: loop_sens_write sent (ch=%d)\n", sens.amount);
}
/*===========================================================================
* Command Dispatch Table
*===========================================================================*/
typedef struct {
const char *cmd;
void (*handler)(uint8_t socket, uint32_t msg_id, const char *json);
uint8_t need_auth; // 1 = requires auth, 0 = pre-auth ok
} JsonCmdEntry;
static const JsonCmdEntry g_cmd_table[] = {
{"pwd_verify", handle_pwd_verify, 0},
{"dev_serial_set", handle_dev_serial_set, 1},
{"dev_info_query", handle_dev_info_query, 1},
{"ssc_net_set", handle_ssc_net_set, 1},
{"ssc_net_query", handle_ssc_net_query, 1},
{"iot_net_set", handle_iot_net_set, 1},
{"iot_net_query", handle_iot_net_query, 1},
{"iot_topic_set", handle_iot_topic_set, 1},
{"iot_topic_query", handle_iot_topic_query, 1},
{"pwd_set", handle_pwd_set, 1},
{"factory_reset", handle_factory_reset, 1},
{"device_reset", handle_device_reset, 1},
{"loop_param_set", handle_loop_param_set, 1},
{"loop_param_query", handle_loop_param_query, 1},
{"loop_version_query", handle_loop_version_query, 1},
{"loop_reset", handle_loop_reset, 1},
{"loop_factory_init", handle_loop_factory_init, 1},
{"loop_sens_read", handle_loop_sens_read, 1},
{"loop_sens_write", handle_loop_sens_write, 1},
{"report_config", handle_report_config, 1},
};
#define JSON_CMD_COUNT (sizeof(g_cmd_table) / sizeof(g_cmd_table[0]))
/*===========================================================================
* Frame Processing
*===========================================================================*/
static void json_process_frame(uint8_t socket, const char *frame) {
if (strlen(frame) == 0) return;
uint32_t msg_id = json_get_msg_id(frame);
char cmd[32] = {0};
json_get_cmd(frame, cmd, sizeof(cmd));
PRINT("JSON dispatch: msg_id=%lu cmd=[%s] len=%d\n", msg_id, cmd, strlen(cmd));
if (strlen(cmd) == 0) {
PRINT("JSON: empty cmd, sending error\n");
json_send_error(socket, msg_id, "", JSON_CODE_PARAM_ERR, "missing cmd field");
return;
}
// Dispatch
int handled = 0;
for (int i = 0; i < JSON_CMD_COUNT; i++) {
if (strcmp(cmd, g_cmd_table[i].cmd) == 0) {
PRINT("JSON: matched cmd[%d]=%s, need_auth=%d, auth_state=%d\n",
i, g_cmd_table[i].cmd, g_cmd_table[i].need_auth, g_json_auth_state);
// Check auth
if (g_cmd_table[i].need_auth && g_json_auth_state != JSON_STATE_AUTHED) {
json_send_error(socket, msg_id, cmd, JSON_CODE_NOT_AUTHED, "not authenticated");
handled = 1;
break;
}
PRINT("JSON: calling handler for %s\n", cmd);
g_cmd_table[i].handler(socket, msg_id, frame);
handled = 1;
break;
}
}
if (!handled) {
json_send_error(socket, msg_id, cmd, JSON_CODE_UNSUPPORTED, "unsupported command");
PRINT("JSON: unsupported cmd: [%s]\n", cmd);
}
}
/*===========================================================================
* json_sensor_callback — 注册到 lup_process_frame0xC0 帧到达时直接推送
*===========================================================================*/
/* Forward declarations */
static int format_sensor_json(char *buf, uint16_t buf_size, const LUP_SensorReport *sr);
static int format_loop_param_json(char *buf, uint16_t buf_size, const LUP_ParamGet *pg);
static void json_sensor_callback(const uint8_t *pkg, uint16_t len)
{
PRINT("JSON: sensor_cb enter socket=%d auth=%d report=%d\n",
g_json_socket_listen, g_json_auth_state, g_report_cfg.enable);
// Check: socket active, authed, report enabled
if (g_json_socket_listen == 0xFF) { PRINT("JSON: sensor_cb skip: no socket\n"); return; }
if (g_json_auth_state != JSON_STATE_AUTHED) { PRINT("JSON: sensor_cb skip: not authed\n"); return; }
if (!g_report_cfg.enable) { PRINT("JSON: sensor_cb skip: report disabled\\n"); return; }
LUP_SensorReport sr;
memset(&sr, 0, sizeof(sr));
int ret = lup_parse_sensor_report(pkg, len, &sr);
if (ret != 0) {
PRINT("JSON: sensor callback parse failed (%d)\n", ret);
return;
}
char data_json[2048];
if (format_sensor_json(data_json, sizeof(data_json), &sr) == 0) {
char *out = (char *)malloc(TCP_JSON_MAX_FRAME);
if (out) {
snprintf(out, TCP_JSON_MAX_FRAME,
"{\"msg_id\":0,\"cmd\":\"sensor_report\",\"ts\":%lu,"
"\"code\":0,\"msg\":\"success\",\"data\":%s}\n",
(unsigned long)mstick(), data_json);
uint32_t slen = strlen(out);
// WCHNET TCP: listen socket=N, data socket=N+1
WCHNET_SocketSend(g_json_socket_listen + 1, (uint8_t *)out, &slen);
free(out);
}
}
}
/*===========================================================================
* Public API
*===========================================================================*/
void tcp_json_restart(void) {
PRINT("JSON: === restart (count=%d) ===\n", g_json_restart_count);
/*
* WCHNET 限制: listen socket 关闭后无法在同端口立即重建 (ERR_ISCONN).
* 因此只关数据 socket 断开客户端, listen socket 保持不变.
*/
if (g_json_socket_listen != 0xFF) {
WCHNET_SocketClose(g_json_socket_listen + 1, TCP_CLOSE_NORMAL);
}
/* 重置应用层状态 — listen socket 继续监听 */
g_json_auth_state = JSON_STATE_WAIT_AUTH;
g_json_pwd_retry = 0;
g_json_recv_len = 0;
g_json_last_comm_ts = 0;
g_json_last_connect_ts = 0;
g_json_auth_timer = mstick(); // 从此刻重新计时
g_json_lockout_timer = 0;
g_json_pending.active = 0;
}
#define TCP_JSON_SRV_INIT_MAX_RETRY 3
void tcp_json_srv_init(void) {
uint8_t ret;
SOCK_INF sock_inf;
uint8_t attempt;
for (attempt = 0; attempt < TCP_JSON_SRV_INIT_MAX_RETRY; attempt++) {
memset(&sock_inf, 0, sizeof(SOCK_INF));
sock_inf.SourPort = TCP_JSON_PORT;
sock_inf.ProtoType = PROTO_TYPE_TCP;
sock_inf.RecvBufLen = RECE_BUF_LEN;
ret = WCHNET_SocketCreat(&g_json_socket_listen, &sock_inf);
if (ret != WCHNET_ERR_SUCCESS) {
PRINT("JSON: SocketCreat attempt %d failed: 0x%02X\n", attempt + 1, ret);
Delay_Ms(100);
continue;
}
ret = WCHNET_SocketListen(g_json_socket_listen);
if (ret != WCHNET_ERR_SUCCESS) {
PRINT("JSON: SocketListen attempt %d failed: 0x%02X\n", attempt + 1, ret);
WCHNET_SocketClose(g_json_socket_listen, TCP_CLOSE_NORMAL);
g_json_socket_listen = 0xFF;
Delay_Ms(100);
continue;
}
// 注册传感器回调(幂等,重复调用无副作用)
lup_set_sensor_callback(json_sensor_callback);
g_json_auth_timer = mstick(); // 记录 listen 创建时刻
PRINT("JSON: TCP listen on port %d (socket %d)\n", TCP_JSON_PORT, g_json_socket_listen);
return;
}
PRINT("JSON: SocketCreat/Listen failed after %d retries\n", TCP_JSON_SRV_INIT_MAX_RETRY);
}
void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat) {
// === CONNECT: client connected — configure recv buffer, init auth state ===
if (intstat & SINT_STAT_CONNECT) {
WCHNET_ModifyRecvBuf(socketid, (uint32_t)g_json_wchnet_buf, RECE_BUF_LEN);
g_json_auth_state = JSON_STATE_WAIT_AUTH;
g_json_pwd_retry = 0;
g_json_auth_timer = mstick();
g_json_recv_len = 0;
g_json_last_connect_ts = mstick();
g_json_last_comm_ts = mstick();
g_json_restart_count = 0; // 有连接就清零重启计数
memset(g_json_recv_buf, 0, sizeof(g_json_recv_buf));
PRINT("JSON: Client connected on socket %d, recv buf configured\n", socketid);
}
// === RECV: read and process incoming data ===
if (intstat & SINT_STAT_RECV) {
uint32_t recv_len = WCHNET_SocketRecvLen(socketid, NULL);
if (recv_len > 0) {
uint16_t space = TCP_JSON_RECV_BUF_LEN - g_json_recv_len;
if (recv_len > space) recv_len = space;
uint32_t rd_len = recv_len;
uint8_t tmp_buf[RECE_BUF_LEN];
WCHNET_SocketRecv(socketid, tmp_buf, &rd_len);
g_json_last_comm_ts = mstick(); // 有数据交互
memcpy(g_json_recv_buf + g_json_recv_len, tmp_buf, (uint16_t)rd_len);
g_json_recv_len += (uint16_t)rd_len;
char frame[TCP_JSON_MAX_FRAME];
while (json_extract_frame(g_json_recv_buf, &g_json_recv_len, frame, sizeof(frame))) {
PRINT("JSON recv: %s\n", frame);
json_process_frame(socketid, frame);
if (g_json_auth_state == JSON_STATE_WAIT_AUTH) {
g_json_auth_timer = mstick();
}
}
if (g_json_recv_len >= TCP_JSON_MAX_FRAME) {
PRINT("JSON: frame overflow, discarding buffer\n");
g_json_recv_len = 0;
memset(g_json_recv_buf, 0, sizeof(g_json_recv_buf));
}
}
}
// === DISCONNECT: client disconnected, reset state ===
if (intstat & SINT_STAT_DISCONNECT) {
PRINT("JSON: Client disconnected (id=%d)\n", socketid);
g_json_auth_state = JSON_STATE_WAIT_AUTH;
g_json_pwd_retry = 0;
g_json_recv_len = 0;
}
// === TIMEOUT: connection timed out ===
if (intstat & SINT_STAT_TIM_OUT) {
PRINT("JSON: Client timeout (id=%d)\n", socketid);
g_json_auth_state = JSON_STATE_WAIT_AUTH;
g_json_pwd_retry = 0;
g_json_recv_len = 0;
}
}
/*===========================================================================
* Deferred Response Helpers
*===========================================================================*/
/*
* Format coil param query response as JSON
*/
static int format_loop_param_json(char *buf, uint16_t buf_size, const LUP_ParamGet *pg) {
const char *freq_level_names[] = {"high", "mid_high", "mid_low", "low"};
char *p = buf;
int remaining = buf_size;
int written;
uint8_t i;
written = snprintf(p, remaining,
"{\"auto_mode\":%s,\"amount\":%d,\"channels\":[",
pg->auto_mode ? "true" : "false", pg->amount);
if (written < 0 || written >= remaining) return -1;
p += written; remaining -= written;
for (i = 0; i < pg->amount; i++) {
const LUP_CoilParam *cp = &pg->params[i];
uint8_t freq_lev = (cp->sensitivity >> 4) & 0x03;
uint8_t sens = cp->sensitivity & 0x0F;
uint8_t out_mode = cp->output_mode & 0x07;
uint8_t safe_mod = (cp->output_mode >> 3) & 0x1F;
uint8_t dir_mode = cp->direction_mode & 0x07;
uint8_t func_mod = (cp->direction_mode >> 4) & 0x0F;
written = snprintf(p, remaining,
"%s{\"ch\":%d,\"sensitivity\":%d,\"freq_level\":\"%s\","
"\"loop_delay\":%d,\"output_mode\":%d,\"safe_mode\":%d,"
"\"exist_mode\":%d,\"direction_mode\":%d,\"function_mode\":%d,"
"\"freq_initial\":%lu,\"freq_current\":%lu,\"freq_diff\":%lu}",
(i > 0) ? "," : "",
i + 1, sens, freq_level_names[freq_lev],
cp->loop_delay, out_mode, safe_mod,
cp->exist_mode, dir_mode, func_mod,
pg->freq[i][0], pg->freq[i][1], pg->freq[i][2]);
if (written < 0 || written >= remaining) return -1;
p += written; remaining -= written;
}
written = snprintf(p, remaining, "]}");
if (written < 0 || written >= remaining) return -1;
return 0;
}
/*
* Format sensor report as JSON
*/
static int format_sensor_json(char *buf, uint16_t buf_size, const LUP_SensorReport *sr) {
const char *freq_level_names[] = {"high", "mid_high", "mid_low", "low"};
char *p = buf;
int remaining = buf_size;
int written;
uint8_t i;
written = snprintf(p, remaining,
"{\"sens_type\":\"multi_coil\",\"sub_amount\":%d,\"sub_seq\":%d,\"coils\":[",
sr->sub_amount, sr->sub_sequence);
if (written < 0 || written >= remaining) return -1;
p += written; remaining -= written;
for (i = 0; i < sr->coil_count; i++) {
const LUP_CoilSensor *cs = &sr->coils[i];
const char *misc_field;
uint32_t misc_val;
switch (cs->misc_type) {
case 0:
// car_state=0(无车)→通过时间, car_state=1(有车)→车间距
misc_field = cs->car_state ? "\"gap\"" : "\"passtime\"";
misc_val = cs->misc.passtime_ms;
break;
case 1: misc_field = "\"cut_amount\""; misc_val = cs->misc.cut_amount; break;
case 2: misc_field = "\"flow_amount\""; misc_val = cs->misc.flow_amount; break;
case 3: misc_field = "\"relay_count\""; misc_val = cs->misc.relay_count; break;
default: misc_field = "\"misc\""; misc_val = 0; break;
}
written = snprintf(p, remaining,
"%s{\"ch\":%d,\"freq_level\":\"%s\",\"direction\":%d,"
"\"freq_type\":\"%s\",\"sensitivity\":%d,"
"\"condition\":%d,\"loop_state\":\"%s\",\"car_state\":\"%s\","
"\"freq\":%lu,\"variation\":%d,%s:%lu}",
(i > 0) ? "," : "",
i + 1,
freq_level_names[cs->freq_level],
cs->direction,
cs->freq_type ? "current" : "initial",
cs->sensitivity,
cs->condition,
cs->loop_state ? "broken" : "normal",
cs->car_state ? "car" : "no_car",
cs->freq,
cs->variation,
misc_field, misc_val);
if (written < 0 || written >= remaining) return -1;
p += written; remaining -= written;
}
written = snprintf(p, remaining, "]}");
if (written < 0 || written >= remaining) return -1;
return 0;
}
/*
* Check for and handle pending Loop MCU deferred responses
*/
static void json_check_pending(void) {
if (!g_json_pending.active) return;
if (g_lup_cmd.state == LUP_STATE_RESPONSE_READY) {
if (strcmp(g_json_pending.cmd, "loop_param_set") == 0) {
uint8_t success = 0;
int ret = lup_parse_set_param_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &success);
if (ret == 0 && success) {
json_send_ok(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, NULL);
} else {
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
ret == 0 ? "Loop MCU returned failure" : "Parse error");
}
} else if (strcmp(g_json_pending.cmd, "loop_param_query") == 0) {
LUP_ParamGet pg;
memset(&pg, 0, sizeof(pg));
int ret = lup_parse_param_get(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &pg);
if (ret == 0) {
char data_json[2048];
if (format_loop_param_json(data_json, sizeof(data_json), &pg) == 0) {
json_send_ok(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, data_json);
} else {
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
"Response too large");
}
} else {
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
"Failed to parse Loop MCU response");
}
} else if (strcmp(g_json_pending.cmd, "loop_version_query") == 0) {
LUP_VersionInfo info;
memset(&info, 0, sizeof(info));
int ret = lup_parse_version(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &info);
if (ret == 0) {
snprintf(info.version_str, sizeof(info.version_str),
"V%d.%d.%d (HW:%d.%d.%d)",
info.soft_main, info.soft_sub, info.soft_ssub,
info.hard_main, info.hard_sub, info.hard_ssub);
char data_json[256];
snprintf(data_json, sizeof(data_json),
"{\"soft_ver\":\"%d.%d.%d\",\"hard_ver\":\"%d.%d.%d\","
"\"version_str\":\"%s\"}",
info.soft_main, info.soft_sub, info.soft_ssub,
info.hard_main, info.hard_sub, info.hard_ssub,
info.version_str);
json_send_ok(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, data_json);
} else {
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
"Failed to parse version");
}
} else if (strcmp(g_json_pending.cmd, "loop_factory_init") == 0) {
uint8_t success = 0;
int ret = lup_parse_factory_init_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &success);
if (ret == 0 && success) {
json_send_ok(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, NULL);
} else {
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
ret == 0 ? "Factory init failed" : "Parse error");
}
} else if (strcmp(g_json_pending.cmd, "loop_sens_read") == 0) {
LUP_Sensitivity sens;
memset(&sens, 0, sizeof(sens));
int ret = lup_parse_sensitivity_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &sens);
if (ret == 0) {
char data_json[1024];
char *p = data_json;
int rem = sizeof(data_json);
int w = snprintf(p, rem,
"{\"ret\":\"%s\",\"amount\":%d,\"channels\":[",
(sens.rw == 0x10) ? "read" : (sens.rw == 0x11) ? "write" : "?",
sens.amount);
p += w; rem -= w;
uint8_t i;
for (i = 0; i < sens.amount; i++) {
w = snprintf(p, rem,
"%s{\"ch\":%d,\"sens_in\":%d,\"sens_out\":%d}",
(i > 0) ? "," : "", i + 1,
sens.sens_in[i], sens.sens_out[i]);
p += w; rem -= w;
}
snprintf(p, rem, "]}");
json_send_ok(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, data_json);
} else {
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
"Failed to parse sensitivity");
}
} else if (strcmp(g_json_pending.cmd, "loop_sens_write") == 0) {
LUP_Sensitivity sens;
memset(&sens, 0, sizeof(sens));
int ret = lup_parse_sensitivity_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &sens);
if (ret == 0) {
char data_json[1024];
char *p = data_json;
int rem = sizeof(data_json);
int w = snprintf(p, rem,
"{\"ret\":\"%s\",\"amount\":%d,\"channels\":[",
(sens.rw == 0x10) ? "read" : (sens.rw == 0x11) ? "write" : "?",
sens.amount);
p += w; rem -= w;
uint8_t i;
for (i = 0; i < sens.amount; i++) {
w = snprintf(p, rem,
"%s{\"ch\":%d,\"sens_in\":%d,\"sens_out\":%d}",
(i > 0) ? "," : "", i + 1,
sens.sens_in[i], sens.sens_out[i]);
p += w; rem -= w;
}
snprintf(p, rem, "]}");
json_send_ok(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, data_json);
} else {
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, JSON_CODE_INTERNAL_ERR,
"Failed to parse sensitivity response");
}
}
lup_cmd_done();
g_json_pending.active = 0;
PRINT("JSON: deferred response sent for %s\n", g_json_pending.cmd);
}
else if (g_lup_cmd.state == LUP_STATE_TIMEOUT
|| mstick() > g_json_pending.deadline) {
// Timeout
json_send_error(g_json_pending.socket, g_json_pending.msg_id,
g_json_pending.cmd, JSON_CODE_BUSY,
"Loop MCU no response");
lup_cmd_done();
g_json_pending.active = 0;
PRINT("JSON: deferred response timeout for %s\n", g_json_pending.cmd);
}
}
/*===========================================================================
* tcp_json_poll (updated)
*===========================================================================*/
void tcp_json_poll(void) {
// Only poll if we have an active connection (socket configured)
if (g_json_socket_listen == 0xFF) return;
// === 自动重启冷却期检查 ===
if (g_json_restart_cooldown > 0) {
if (mstick() < g_json_restart_cooldown) {
// 冷却期内,不做任何操作
return;
}
g_json_restart_cooldown = 0;
g_json_restart_count = 0;
PRINT("JSON: restart cooldown expired, resetting counter\n");
}
// === 超时条件1: 5分钟无连接 ===
if (g_json_last_connect_ts == 0) {
// 从未连接过 — 用 auth_timer (listen 创建时设) 作为起点
if (g_json_auth_state == JSON_STATE_WAIT_AUTH
&& mstick() - g_json_auth_timer > TCP_JSON_IDLE_TIMEOUT_MS) {
PRINT("JSON: %dmin no connection → restart\n", TCP_JSON_IDLE_TIMEOUT_MS / 60000);
goto do_restart;
}
} else if (mstick() - g_json_last_connect_ts > TCP_JSON_IDLE_TIMEOUT_MS) {
PRINT("JSON: %dmin since last connect → restart\n", TCP_JSON_IDLE_TIMEOUT_MS / 60000);
goto do_restart;
}
// === 超时条件2: 5分钟无通信 (仅在有连接/已认证时检查) ===
if (g_json_last_comm_ts > 0
&& mstick() - g_json_last_comm_ts > TCP_JSON_IDLE_TIMEOUT_MS) {
PRINT("JSON: %dmin no communication → restart\n", TCP_JSON_IDLE_TIMEOUT_MS / 60000);
goto do_restart;
}
// Auth: 3次失败在 handle_pwd_verify 中直接 tcp_json_restart()
// 5分钟无通信/无连接由上方 do_restart 路径处理
// Lockout timeout check (existing)
if (g_json_auth_state == JSON_STATE_LOCKOUT) {
if (mstick() - g_json_lockout_timer > TCP_JSON_LOCKOUT_TIMEOUT_MS) {
PRINT("JSON: Lockout expired\n");
g_json_auth_state = JSON_STATE_WAIT_AUTH;
g_json_pwd_retry = 0;
}
}
// Check for pending Loop MCU deferred responses
json_check_pending();
return;
do_restart:
g_json_restart_count++;
g_json_restart_cooldown = 0;
if (g_json_restart_count > TCP_JSON_MAX_RESTART_COUNT) {
// 超过最大重启次数,进入冷却期
g_json_restart_cooldown = mstick() + TCP_JSON_RESTART_COOLDOWN_MS;
PRINT("JSON: max restart (%d) reached, cooldown %d min\n",
TCP_JSON_MAX_RESTART_COUNT, TCP_JSON_RESTART_COOLDOWN_MS / 60000);
} else {
tcp_json_restart();
}
}
/*===========================================================================
* tcp_json_push_sensor — Push sensor data (0xC0) to connected TCP client
*
* Called from main loop when uart_srv() has a sensor report frame in
* g_pkg_uart_2 that hasn't been consumed by BLE.
*===========================================================================*/
void tcp_json_push_sensor(void) {
// Check: socket active, authed, report enabled
if (g_json_socket_listen == 0xFF) return;
if (g_json_auth_state != JSON_STATE_AUTHED) return;
if (!g_report_cfg.enable) return; // 主动上报未开启
// Only proceed if g_pkg_uart_2 has data and it's a 0xC0 sensor report
if (g_pkg_uart_2.flag == 0) return;
if (g_pkg_uart_2.pkg[0] != 0x7F) return;
if (g_pkg_uart_2.pkg[3] != 0xC0) return;
LUP_SensorReport sr;
memset(&sr, 0, sizeof(sr));
int ret = lup_parse_sensor_report(g_pkg_uart_2.pkg, g_pkg_uart_2.offset, &sr);
if (ret != 0) {
PRINT("JSON: sensor parse failed (%d)\n", ret);
InitPkgUart(&g_pkg_uart_2);
return;
}
char data_json[2048];
if (format_sensor_json(data_json, sizeof(data_json), &sr) == 0) {
char *out = (char *)malloc(TCP_JSON_MAX_FRAME);
if (out) {
snprintf(out, TCP_JSON_MAX_FRAME,
"{\"msg_id\":0,\"cmd\":\"sensor_report\",\"ts\":%lu,"
"\"code\":0,\"msg\":\"success\",\"data\":%s}\n",
(unsigned long)mstick(), data_json);
uint32_t slen = strlen(out);
// WCHNET TCP: listen socket=N, data socket=N+1
WCHNET_SocketSend(g_json_socket_listen + 1, (uint8_t *)out, &slen);
free(out);
}
}
// Clear after processing
InitPkgUart(&g_pkg_uart_2);
}