diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/tcp_json_srv.h b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/tcp_json_srv.h index 5b855f1..382fec3 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/tcp_json_srv.h +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/tcp_json_srv.h @@ -22,6 +22,9 @@ #define TCP_JSON_RECV_BUF_LEN (TCP_JSON_MAX_FRAME * 2) #define TCP_JSON_MAX_PWD_RETRY 3 // Max password retries before lockout #define TCP_JSON_LOCKOUT_TIMEOUT_MS 60000 // 60s lockout after max retries +#define TCP_JSON_IDLE_TIMEOUT_MS 300000 // 5min idle → restart (no connect / no comm) +#define TCP_JSON_MAX_RESTART_COUNT 3 // Max auto-restart count +#define TCP_JSON_RESTART_COOLDOWN_MS 600000 // 10min cooldown after max restarts /*=========================================================================== * Auth / State @@ -63,6 +66,10 @@ extern TcpJsonAuthState g_json_auth_state; extern uint32_t g_json_auth_timer; // ms timer for auth timeout extern uint8_t g_json_pwd_retry; // password retry counter extern TcpJsonPending g_json_pending; // Pending Loop MCU command state +extern uint32_t g_json_last_comm_ts; // Last communication timestamp (ms) +extern uint32_t g_json_last_connect_ts; // Last client connect timestamp (ms) +extern uint8_t g_json_restart_count; // Auto-restart counter +extern uint32_t g_json_restart_cooldown; // Cooldown deadline (ms) /*=========================================================================== * API @@ -71,5 +78,6 @@ void tcp_json_srv_init(void); // Create listen socket on port void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat); // Socket interrupt handler void tcp_json_poll(void); // Periodic poll (auth timeout, etc.) void tcp_json_push_sensor(void); // Push sensor data (0xC0) to TCP client +void tcp_json_restart(void); // Close + reopen socket (auto-restart) #endif /* __TCP_JSON_SRV_H__ */ diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c index 7483aa5..f7b64b5 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c @@ -32,6 +32,10 @@ uint8_t g_json_pwd_retry = 0; uint32_t g_json_lockout_timer = 0; TcpJsonPending g_json_pending = {0, 0, 0, "", 0}; uint8_t g_report_active = 0; // 0=不主动上报, 1=主动上报传感器数据 +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) @@ -196,11 +200,12 @@ static void handle_pwd_verify(uint8_t socket, uint32_t msg_id, const char *json) } else { g_json_pwd_retry++; if (g_json_pwd_retry >= TCP_JSON_MAX_PWD_RETRY) { - g_json_auth_state = JSON_STATE_LOCKOUT; - g_json_lockout_timer = mstick(); - PRINT("JSON: Auth locked out (%d retries)\n", g_json_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, locked out for 60s"); + "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"); } @@ -788,36 +793,68 @@ static void json_sensor_callback(const uint8_t *pkg, uint16_t len) * Public API *===========================================================================*/ +void tcp_json_restart(void) { + PRINT("JSON: === auto-restart (count=%d) ===\n", g_json_restart_count); + + /* 1. 关闭数据 socket 和监听 socket */ + if (g_json_socket_listen != 0xFF) { + WCHNET_SocketClose(g_json_socket_listen + 1, TCP_CLOSE_NORMAL); + WCHNET_SocketClose(g_json_socket_listen, TCP_CLOSE_NORMAL); + g_json_socket_listen = 0xFF; + } + + /* 2. 重置状态 */ + 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 = 0; + g_json_lockout_timer = 0; + g_json_pending.active = 0; + + /* 3. 重新创建 socket 并监听 */ + tcp_json_srv_init(); +} + +#define TCP_JSON_SRV_INIT_MAX_RETRY 3 + void tcp_json_srv_init(void) { uint8_t ret; SOCK_INF sock_inf; - static uint8_t _init_done = 0; + uint8_t attempt; - if (_init_done) return; // 防止 net_srv_init 反复调用 - _init_done = 1; + 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; - // 注册传感器回调 — 必须在 socket 操作之前, - // 避免 socket 失败导致 return 而漏掉注册 - lup_set_sensor_callback(json_sensor_callback); + 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; + } - 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_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; + } - ret = WCHNET_SocketCreat(&g_json_socket_listen, &sock_inf); - if (ret != WCHNET_ERR_SUCCESS) { - PRINT("JSON: SocketCreat failed: 0x%02X\n", ret); + // 注册传感器回调(幂等,重复调用无副作用) + 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; } - ret = WCHNET_SocketListen(g_json_socket_listen); - if (ret != WCHNET_ERR_SUCCESS) { - PRINT("JSON: SocketListen failed: 0x%02X\n", ret); - return; - } - - PRINT("JSON: TCP listen on port %d (socket %d)\n", TCP_JSON_PORT, g_json_socket_listen); + 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) { @@ -828,6 +865,9 @@ void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat) { 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); } @@ -841,6 +881,7 @@ void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat) { 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; @@ -1138,7 +1179,38 @@ void tcp_json_poll(void) { // Only poll if we have an active connection (socket configured) if (g_json_socket_listen == 0xFF) return; - // Auth timeout check + // === 自动重启冷却期检查 === + 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 timeout check (existing) if (g_json_auth_state == JSON_STATE_WAIT_AUTH) { if (mstick() - g_json_auth_timer > TCP_JSON_AUTH_TIMEOUT_MS) { PRINT("JSON: Auth timeout, closing connection\n"); @@ -1149,7 +1221,7 @@ void tcp_json_poll(void) { } } - // Lockout timeout check + // 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"); @@ -1160,6 +1232,19 @@ void tcp_json_poll(void) { // 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(); + } } /*===========================================================================