#include "tcp_client.h" #include "FreeRTOS.h" #include "task.h" #include "queue.h" #include "lwip/api.h" #include "lwip/ip_addr.h" #include "lwip/tcp.h" #include "lwip/tcpip.h" #include "app_runtime.h" #include "config.h" #include "debug_log.h" #include "ethernetif.h" #include "route_msg.h" #define TCP_CLIENT_CONNECT_TIMEOUT_MS 500 #define TCP_CLIENT_RECONNECT_INTERVAL_MS 3000u #define TCP_CLIENT_STOP_POLL_MS 50u static BaseType_t tcp_client_stop_requested(void) { return (app_network_task_stop_requested() != pdFALSE) ? pdTRUE : pdFALSE; } static BaseType_t tcp_client_delay_with_stop(uint32_t delay_ms) { uint32_t remaining_ms = delay_ms; while (remaining_ms > 0u) { uint32_t slice_ms = (remaining_ms > TCP_CLIENT_STOP_POLL_MS) ? TCP_CLIENT_STOP_POLL_MS : remaining_ms; if (tcp_client_stop_requested() != pdFALSE) { return pdFALSE; } vTaskDelay(pdMS_TO_TICKS(slice_ms)); remaining_ms -= slice_ms; } return (tcp_client_stop_requested() == pdFALSE) ? pdTRUE : pdFALSE; } static void tcp_client_abort_and_delete(struct netconn *conn, uint8_t link_index) { struct tcp_pcb *pcb; if (conn == NULL) { return; } pcb = conn->pcb.tcp; if (pcb != NULL) { LOCK_TCPIP_CORE(); pcb = conn->pcb.tcp; if (pcb != NULL) { tcp_abort(pcb); conn->pcb.tcp = NULL; conn->state = NETCONN_NONE; debug_log_printf("[CLI] idx=%u abort-close\r\n", (unsigned int)link_index); } UNLOCK_TCPIP_CORE(); } netconn_delete(conn); } static err_t tcp_client_worker(struct netconn *conn, uint8_t link_index) { struct netbuf *buf; const device_config_t *cfg = config_get(); uint8_t uart_endpoint = config_uart_index_to_endpoint(cfg->links[link_index].uart); uint8_t src_endpoint = config_link_index_to_endpoint(link_index); err_t err; route_msg_t *tx_msg; route_send_result_t route_result; netconn_set_recvtimeout(conn, 10); for (;;) { if (tcp_client_stop_requested() != pdFALSE) { return ERR_CLSD; } err = netconn_recv(conn, &buf); if (err == ERR_OK) { do { void *data; uint16_t len; netbuf_data(buf, &data, &len); route_result = route_send(xTcpRxQueue, src_endpoint, uart_endpoint, (link_index == CONFIG_LINK_C1) ? ROUTE_CONN_C1 : ROUTE_CONN_C2, (const uint8_t *)data, len, pdMS_TO_TICKS(10)); if (route_result != ROUTE_SEND_OK) { debug_log_printf("[CLI] idx=%u rx-route-fail rc=%s len=%u\r\n", (unsigned int)link_index, route_send_result_to_str(route_result), (unsigned int)len); netbuf_delete(buf); return ERR_CLSD; } } while (netbuf_next(buf) >= 0); netbuf_delete(buf); } else if (err == ERR_TIMEOUT) { if (tcp_client_stop_requested() != pdFALSE) { return ERR_CLSD; } } else { return err; } while (xQueueReceive(xLinkTxQueues[link_index], &tx_msg, 0) == pdPASS) { if (tcp_client_stop_requested() != pdFALSE) { route_msg_free(tx_msg); return ERR_CLSD; } err = netconn_write(conn, tx_msg->data, tx_msg->len, NETCONN_COPY); route_msg_free(tx_msg); if (err != ERR_OK) { return err; } } } } static void tcp_client_task(uint8_t link_index) { const device_config_t *cfg; struct netconn *conn; ip_addr_t remote_ip; uint32_t delay_ms; err_t err; uint8_t first_connect_deferred; netconn_thread_init(); first_connect_deferred = (link_index == CONFIG_LINK_C1) ? 1u : 0u; for (;;) { if (tcp_client_stop_requested() != pdFALSE) { break; } while ((g_netif_ready == pdFALSE) || (ethernetif_link_is_up() == 0u)) { if (tcp_client_stop_requested() != pdFALSE) { goto exit_task; } vTaskDelay(pdMS_TO_TICKS(100)); } cfg = config_get(); if (cfg->links[link_index].enabled == 0u) { if (tcp_client_stop_requested() != pdFALSE) { break; } if (tcp_client_delay_with_stop(500u) == pdFALSE) { break; } continue; } delay_ms = TCP_CLIENT_RECONNECT_INTERVAL_MS; if (first_connect_deferred != 0u) { first_connect_deferred = 0u; debug_log_write("[CLI] C1 first-connect defer\r\n"); if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) { break; } continue; } conn = netconn_new(NETCONN_TCP); if (conn == NULL) { if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) { break; } continue; } if (cfg->links[link_index].local_port != 0u) { err = netconn_bind(conn, IP_ADDR_ANY, cfg->links[link_index].local_port); if (err != ERR_OK) { debug_log_printf("[CLI] idx=%u bind-fail err=%d lport=%u\r\n", (unsigned int)link_index, (int)err, (unsigned int)cfg->links[link_index].local_port); netconn_delete(conn); if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) { break; } continue; } } IP_ADDR4(&remote_ip, cfg->links[link_index].remote_ip[0], cfg->links[link_index].remote_ip[1], cfg->links[link_index].remote_ip[2], cfg->links[link_index].remote_ip[3]); netconn_set_recvtimeout(conn, TCP_CLIENT_CONNECT_TIMEOUT_MS); err = netconn_connect(conn, &remote_ip, cfg->links[link_index].remote_port); if (err == ERR_OK) { debug_log_printf("[CLI] idx=%u connect-ok\r\n", (unsigned int)link_index); (void)tcp_client_worker(conn, link_index); } else { if (err == ERR_TIMEOUT) { debug_log_printf("[CLI] idx=%u connect-timeout ms=%u rip=%u.%u.%u.%u rport=%u\r\n", (unsigned int)link_index, (unsigned int)TCP_CLIENT_CONNECT_TIMEOUT_MS, (unsigned int)cfg->links[link_index].remote_ip[0], (unsigned int)cfg->links[link_index].remote_ip[1], (unsigned int)cfg->links[link_index].remote_ip[2], (unsigned int)cfg->links[link_index].remote_ip[3], (unsigned int)cfg->links[link_index].remote_port); } else { debug_log_printf("[CLI] idx=%u connect-fail err=%d rip=%u.%u.%u.%u rport=%u\r\n", (unsigned int)link_index, (int)err, (unsigned int)cfg->links[link_index].remote_ip[0], (unsigned int)cfg->links[link_index].remote_ip[1], (unsigned int)cfg->links[link_index].remote_ip[2], (unsigned int)cfg->links[link_index].remote_ip[3], (unsigned int)cfg->links[link_index].remote_port); } } tcp_client_abort_and_delete(conn, link_index); if (tcp_client_stop_requested() != pdFALSE) { break; } if (tcp_client_delay_with_stop(delay_ms) == pdFALSE) { break; } } exit_task: netconn_thread_cleanup(); app_on_network_task_exit(xTaskGetCurrentTaskHandle()); vTaskDelete(NULL); } void TcpCliTask_C1(void *argument) { (void)argument; tcp_client_task(CONFIG_LINK_C1); } void TcpCliTask_C2(void *argument) { (void)argument; tcp_client_task(CONFIG_LINK_C2); }