refactor: 完成R8裸机lwIP移植并更新文档
This commit is contained in:
+190
-345
@@ -1,431 +1,276 @@
|
||||
/**
|
||||
* @file tcp_client.c
|
||||
* @brief TCP Client module implementation for transparent transmission with UART3
|
||||
* @brief lwIP RAW TCP client for the UART3 bridge.
|
||||
*/
|
||||
|
||||
#include "tcp_client.h"
|
||||
#include "config.h"
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/sockets.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "stream_buffer.h"
|
||||
#include "main.h"
|
||||
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/tcp.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Private Variables
|
||||
*---------------------------------------------------------------------------*/
|
||||
typedef struct {
|
||||
struct tcp_pcb *pcb;
|
||||
uint8_t rx_ring[TCP_CLIENT_RX_BUFFER_SIZE];
|
||||
uint16_t rx_head;
|
||||
uint16_t rx_tail;
|
||||
uint32_t next_retry_ms;
|
||||
tcp_client_config_t config;
|
||||
tcp_client_status_t status;
|
||||
} tcp_client_ctx_t;
|
||||
|
||||
/* Client configuration */
|
||||
static tcp_client_config_t client_config = {
|
||||
.server_ip = {192, 168, 1, 100},
|
||||
.server_port = TCP_CLIENT_DEFAULT_PORT,
|
||||
.auto_reconnect = true,
|
||||
.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS
|
||||
};
|
||||
static tcp_client_ctx_t g_client;
|
||||
|
||||
/* Client status */
|
||||
static tcp_client_status_t client_status = {
|
||||
.state = TCP_CLIENT_STATE_IDLE,
|
||||
.rx_bytes = 0,
|
||||
.tx_bytes = 0,
|
||||
.reconnect_count = 0,
|
||||
.errors = 0
|
||||
};
|
||||
|
||||
/* Socket descriptor */
|
||||
static int client_socket = -1;
|
||||
|
||||
/* Stream buffers for UART integration */
|
||||
static StreamBufferHandle_t rx_stream = NULL; /* TCP RX -> UART TX */
|
||||
static StreamBufferHandle_t tx_stream = NULL; /* UART RX -> TCP TX */
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Private Functions
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
static int tcp_client_send_all(int sock, const uint8_t *data, uint16_t len)
|
||||
static uint16_t ring_free(uint16_t head, uint16_t tail, uint16_t size)
|
||||
{
|
||||
uint16_t total = 0;
|
||||
return (head >= tail) ? (uint16_t)(size - head + tail - 1u) : (uint16_t)(tail - head - 1u);
|
||||
}
|
||||
|
||||
while (total < len)
|
||||
{
|
||||
int sent = send(sock, data + total, len - total, 0);
|
||||
if (sent > 0)
|
||||
{
|
||||
total += (uint16_t)sent;
|
||||
static err_t tcp_client_on_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
|
||||
{
|
||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||
struct pbuf *q;
|
||||
|
||||
if (err != ERR_OK) {
|
||||
if (p != NULL) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
return err;
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
ctx->pcb = NULL;
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
for (q = p; q != NULL; q = q->next) {
|
||||
const uint8_t *src = (const uint8_t *)q->payload;
|
||||
for (uint16_t i = 0; i < q->len; ++i) {
|
||||
if (ring_free(ctx->rx_head, ctx->rx_tail, TCP_CLIENT_RX_BUFFER_SIZE) == 0u) {
|
||||
ctx->status.errors++;
|
||||
break;
|
||||
}
|
||||
ctx->rx_ring[ctx->rx_head] = src[i];
|
||||
ctx->rx_head = (uint16_t)((ctx->rx_head + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
|
||||
ctx->status.rx_bytes++;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)total;
|
||||
tcp_recved(pcb, p->tot_len);
|
||||
pbuf_free(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Internal connect function
|
||||
*/
|
||||
static int tcp_client_do_connect(void)
|
||||
static err_t tcp_client_on_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
|
||||
{
|
||||
struct sockaddr_in server_addr;
|
||||
int ret;
|
||||
|
||||
if (client_socket >= 0)
|
||||
{
|
||||
/* Already connected */
|
||||
return 0;
|
||||
}
|
||||
|
||||
client_status.state = TCP_CLIENT_STATE_CONNECTING;
|
||||
|
||||
/* Create socket */
|
||||
client_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (client_socket < 0)
|
||||
{
|
||||
client_status.state = TCP_CLIENT_STATE_ERROR;
|
||||
client_status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prepare server address */
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_port = htons(client_config.server_port);
|
||||
server_addr.sin_addr.s_addr = ((uint32_t)client_config.server_ip[0]) |
|
||||
((uint32_t)client_config.server_ip[1] << 8) |
|
||||
((uint32_t)client_config.server_ip[2] << 16) |
|
||||
((uint32_t)client_config.server_ip[3] << 24);
|
||||
|
||||
/* Connect to server */
|
||||
ret = connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr));
|
||||
if (ret < 0)
|
||||
{
|
||||
close(client_socket);
|
||||
client_socket = -1;
|
||||
client_status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
client_status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
client_status.state = TCP_CLIENT_STATE_CONNECTED;
|
||||
|
||||
return 0;
|
||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||
(void)pcb;
|
||||
ctx->status.tx_bytes += len;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Public Functions
|
||||
*---------------------------------------------------------------------------*/
|
||||
static void tcp_client_on_err(void *arg, err_t err)
|
||||
{
|
||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||
(void)err;
|
||||
ctx->pcb = NULL;
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
}
|
||||
|
||||
static err_t tcp_client_on_connected(void *arg, struct tcp_pcb *pcb, err_t err)
|
||||
{
|
||||
tcp_client_ctx_t *ctx = (tcp_client_ctx_t *)arg;
|
||||
|
||||
if (err != ERR_OK) {
|
||||
ctx->pcb = NULL;
|
||||
ctx->status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
ctx->status.errors++;
|
||||
ctx->next_retry_ms = HAL_GetTick() + ctx->config.reconnect_interval_ms;
|
||||
return err;
|
||||
}
|
||||
|
||||
ctx->pcb = pcb;
|
||||
ctx->status.state = TCP_CLIENT_STATE_CONNECTED;
|
||||
tcp_arg(pcb, ctx);
|
||||
tcp_recv(pcb, tcp_client_on_recv);
|
||||
tcp_sent(pcb, tcp_client_on_sent);
|
||||
tcp_err(pcb, tcp_client_on_err);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize TCP Client module
|
||||
*/
|
||||
int tcp_client_init(const tcp_client_config_t *config)
|
||||
{
|
||||
if (config != NULL)
|
||||
{
|
||||
memcpy(&client_config, config, sizeof(tcp_client_config_t));
|
||||
memset(&g_client, 0, sizeof(g_client));
|
||||
g_client.config.server_ip[0] = 192u;
|
||||
g_client.config.server_ip[1] = 168u;
|
||||
g_client.config.server_ip[2] = 1u;
|
||||
g_client.config.server_ip[3] = 100u;
|
||||
g_client.config.server_port = TCP_CLIENT_DEFAULT_PORT;
|
||||
g_client.config.auto_reconnect = true;
|
||||
g_client.config.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS;
|
||||
g_client.status.state = TCP_CLIENT_STATE_IDLE;
|
||||
|
||||
if (config != NULL) {
|
||||
g_client.config = *config;
|
||||
}
|
||||
|
||||
/* Create stream buffers */
|
||||
if (rx_stream == NULL)
|
||||
{
|
||||
rx_stream = xStreamBufferCreate(TCP_CLIENT_RX_BUFFER_SIZE, 1);
|
||||
if (rx_stream == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (tx_stream == NULL)
|
||||
{
|
||||
tx_stream = xStreamBufferCreate(TCP_CLIENT_TX_BUFFER_SIZE, 1);
|
||||
if (tx_stream == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
client_status.state = TCP_CLIENT_STATE_IDLE;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect to remote server
|
||||
*/
|
||||
int tcp_client_connect(void)
|
||||
{
|
||||
return tcp_client_do_connect();
|
||||
struct tcp_pcb *pcb;
|
||||
ip_addr_t remote_addr;
|
||||
err_t err;
|
||||
|
||||
if (g_client.pcb != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pcb = tcp_new_ip_type(IPADDR_TYPE_V4);
|
||||
if (pcb == NULL) {
|
||||
g_client.status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
IP_ADDR4(&remote_addr,
|
||||
g_client.config.server_ip[0],
|
||||
g_client.config.server_ip[1],
|
||||
g_client.config.server_ip[2],
|
||||
g_client.config.server_ip[3]);
|
||||
|
||||
g_client.status.state = TCP_CLIENT_STATE_CONNECTING;
|
||||
tcp_arg(pcb, &g_client);
|
||||
err = tcp_connect(pcb, &remote_addr, g_client.config.server_port, tcp_client_on_connected);
|
||||
if (err != ERR_OK) {
|
||||
tcp_abort(pcb);
|
||||
g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
g_client.status.errors++;
|
||||
g_client.next_retry_ms = HAL_GetTick() + g_client.config.reconnect_interval_ms;
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_client.pcb = pcb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnect from server
|
||||
*/
|
||||
int tcp_client_disconnect(void)
|
||||
{
|
||||
if (client_socket >= 0)
|
||||
{
|
||||
close(client_socket);
|
||||
client_socket = -1;
|
||||
if (g_client.pcb != NULL) {
|
||||
tcp_arg(g_client.pcb, NULL);
|
||||
tcp_recv(g_client.pcb, NULL);
|
||||
tcp_sent(g_client.pcb, NULL);
|
||||
tcp_err(g_client.pcb, NULL);
|
||||
tcp_abort(g_client.pcb);
|
||||
g_client.pcb = NULL;
|
||||
}
|
||||
|
||||
client_status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
|
||||
|
||||
g_client.status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send data to server
|
||||
*/
|
||||
int tcp_client_send(const uint8_t *data, uint16_t len)
|
||||
{
|
||||
int sent;
|
||||
|
||||
if (client_socket < 0)
|
||||
{
|
||||
err_t err;
|
||||
|
||||
if (g_client.pcb == NULL || data == NULL || len == 0u) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sent = tcp_client_send_all(client_socket, data, len);
|
||||
if (sent > 0)
|
||||
{
|
||||
client_status.tx_bytes += sent;
|
||||
|
||||
if (tcp_sndbuf(g_client.pcb) < len) {
|
||||
return 0;
|
||||
}
|
||||
else if (sent < 0)
|
||||
{
|
||||
/* Connection error */
|
||||
close(client_socket);
|
||||
client_socket = -1;
|
||||
client_status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
client_status.errors++;
|
||||
|
||||
err = tcp_write(g_client.pcb, data, len, TCP_WRITE_FLAG_COPY);
|
||||
if (err != ERR_OK) {
|
||||
g_client.status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sent;
|
||||
|
||||
err = tcp_output(g_client.pcb);
|
||||
if (err != ERR_OK) {
|
||||
g_client.status.errors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receive data from server
|
||||
*/
|
||||
int tcp_client_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms)
|
||||
{
|
||||
int received;
|
||||
struct timeval tv;
|
||||
|
||||
if (client_socket < 0)
|
||||
{
|
||||
uint16_t copied = 0u;
|
||||
(void)timeout_ms;
|
||||
|
||||
if (data == NULL || max_len == 0u) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set receive timeout */
|
||||
tv.tv_sec = timeout_ms / 1000;
|
||||
tv.tv_usec = (timeout_ms % 1000) * 1000;
|
||||
setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||
|
||||
received = recv(client_socket, data, max_len, 0);
|
||||
if (received > 0)
|
||||
{
|
||||
client_status.rx_bytes += received;
|
||||
|
||||
while (copied < max_len && g_client.rx_tail != g_client.rx_head) {
|
||||
data[copied++] = g_client.rx_ring[g_client.rx_tail];
|
||||
g_client.rx_tail = (uint16_t)((g_client.rx_tail + 1u) % TCP_CLIENT_RX_BUFFER_SIZE);
|
||||
}
|
||||
else if (received == 0)
|
||||
{
|
||||
/* Connection closed by server */
|
||||
close(client_socket);
|
||||
client_socket = -1;
|
||||
client_status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
return received;
|
||||
|
||||
return (int)copied;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if connected to server
|
||||
*/
|
||||
bool tcp_client_is_connected(void)
|
||||
{
|
||||
return (client_socket >= 0);
|
||||
return (g_client.pcb != NULL) && (g_client.status.state == TCP_CLIENT_STATE_CONNECTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update server configuration
|
||||
*/
|
||||
int tcp_client_set_server(const uint8_t *ip, uint16_t port)
|
||||
{
|
||||
if (ip == NULL)
|
||||
{
|
||||
if (ip == NULL || port == 0u) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Disconnect if connected */
|
||||
if (client_socket >= 0)
|
||||
{
|
||||
tcp_client_disconnect();
|
||||
}
|
||||
|
||||
memcpy(client_config.server_ip, ip, 4);
|
||||
client_config.server_port = port;
|
||||
|
||||
|
||||
memcpy(g_client.config.server_ip, ip, 4u);
|
||||
g_client.config.server_port = port;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get TCP Client status
|
||||
*/
|
||||
void tcp_client_get_status(tcp_client_status_t *status)
|
||||
{
|
||||
if (status != NULL)
|
||||
{
|
||||
memcpy(status, &client_status, sizeof(tcp_client_status_t));
|
||||
if (status != NULL) {
|
||||
*status = g_client.status;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get RX StreamBuffer handle
|
||||
*/
|
||||
void *tcp_client_get_rx_stream(void)
|
||||
{
|
||||
return rx_stream;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get TX StreamBuffer handle
|
||||
*/
|
||||
void *tcp_client_get_tx_stream(void)
|
||||
{
|
||||
return tx_stream;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TCP Client task
|
||||
*/
|
||||
void tcp_client_task(void *argument)
|
||||
{
|
||||
const device_config_t *cfg;
|
||||
tcp_client_config_t task_cfg;
|
||||
uint8_t rx_buffer[256];
|
||||
uint8_t tx_buffer[256];
|
||||
int received;
|
||||
size_t tx_len;
|
||||
fd_set read_fds;
|
||||
struct timeval tv;
|
||||
uint32_t reconnect_timer = 0;
|
||||
|
||||
(void)argument;
|
||||
|
||||
/* Initialize client */
|
||||
task_cfg.server_ip[0] = 192;
|
||||
task_cfg.server_ip[1] = 168;
|
||||
task_cfg.server_ip[2] = 1;
|
||||
task_cfg.server_ip[3] = 100;
|
||||
task_cfg.server_port = TCP_CLIENT_DEFAULT_PORT;
|
||||
task_cfg.auto_reconnect = true;
|
||||
task_cfg.reconnect_interval_ms = TCP_CLIENT_RECONNECT_DELAY_MS;
|
||||
}
|
||||
|
||||
cfg = config_get();
|
||||
if (cfg != NULL)
|
||||
{
|
||||
memcpy(task_cfg.server_ip, cfg->remote_ip, sizeof(task_cfg.server_ip));
|
||||
if (cfg->remote_port > 0)
|
||||
{
|
||||
task_cfg.server_port = cfg->remote_port;
|
||||
}
|
||||
if (cfg->reconnect_interval > 0)
|
||||
{
|
||||
task_cfg.reconnect_interval_ms = cfg->reconnect_interval;
|
||||
}
|
||||
void tcp_client_poll(void)
|
||||
{
|
||||
uint32_t now;
|
||||
|
||||
if (!g_client.config.auto_reconnect || g_client.pcb != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
tcp_client_init(&task_cfg);
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* Handle connection state */
|
||||
if (client_socket < 0)
|
||||
{
|
||||
/* Not connected - try to reconnect */
|
||||
if (client_config.auto_reconnect)
|
||||
{
|
||||
if (xTaskGetTickCount() - reconnect_timer >= pdMS_TO_TICKS(client_config.reconnect_interval_ms))
|
||||
{
|
||||
if (tcp_client_do_connect() == 0)
|
||||
{
|
||||
/* Connected successfully */
|
||||
client_status.reconnect_count++;
|
||||
}
|
||||
reconnect_timer = xTaskGetTickCount();
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait before retry */
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Handle data transfer if connected */
|
||||
|
||||
/* Check for data from TCP server */
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(client_socket, &read_fds);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10000; /* 10ms timeout */
|
||||
|
||||
if (select(client_socket + 1, &read_fds, NULL, NULL, &tv) > 0)
|
||||
{
|
||||
if (FD_ISSET(client_socket, &read_fds))
|
||||
{
|
||||
received = recv(client_socket, rx_buffer, sizeof(rx_buffer), 0);
|
||||
if (received > 0)
|
||||
{
|
||||
/* Forward to UART via stream buffer */
|
||||
xStreamBufferSend(rx_stream, rx_buffer, received, pdMS_TO_TICKS(10));
|
||||
client_status.rx_bytes += received;
|
||||
}
|
||||
else if (received == 0)
|
||||
{
|
||||
/* Connection closed */
|
||||
close(client_socket);
|
||||
client_socket = -1;
|
||||
client_status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
reconnect_timer = xTaskGetTickCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Error */
|
||||
close(client_socket);
|
||||
client_socket = -1;
|
||||
client_status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
client_status.errors++;
|
||||
reconnect_timer = xTaskGetTickCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for data from UART to send to TCP */
|
||||
tx_len = xStreamBufferReceive(tx_stream, tx_buffer, sizeof(tx_buffer), 0);
|
||||
if (tx_len > 0)
|
||||
{
|
||||
int sent = tcp_client_send_all(client_socket, tx_buffer, (uint16_t)tx_len);
|
||||
if (sent > 0)
|
||||
{
|
||||
client_status.tx_bytes += sent;
|
||||
}
|
||||
else if (sent < 0)
|
||||
{
|
||||
/* Send error */
|
||||
close(client_socket);
|
||||
client_socket = -1;
|
||||
client_status.state = TCP_CLIENT_STATE_DISCONNECTED;
|
||||
client_status.errors++;
|
||||
reconnect_timer = xTaskGetTickCount();
|
||||
}
|
||||
}
|
||||
|
||||
/* Small delay to prevent tight loop */
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
now = HAL_GetTick();
|
||||
if (now >= g_client.next_retry_ms) {
|
||||
g_client.status.reconnect_count++;
|
||||
g_client.next_retry_ms = now + g_client.config.reconnect_interval_ms;
|
||||
(void)tcp_client_connect();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user