增加事件驱动和业务回滚处理方式

This commit is contained in:
冯佳
2026-03-04 08:50:04 +08:00
parent cc4c361df6
commit 47a9dff6ef
25 changed files with 2626 additions and 1780 deletions

View File

@ -11,6 +11,13 @@
#include <string.h>
#include "lwip/prot/ip4.h"
#include "sht40.h"
#include "tcp_server.h"
#include "event_queue.h"
#include "event_handler.h"
#include "event_trigger.h"
#include "transaction.h"
#include "state_manager.h"
#include "error_handler.h"
/* Utility macros */
#define MIN(a, b) ((a) < (b) ? (a) : (b))
@ -35,11 +42,13 @@ osal_sem_t sem_ip_ready = NULL;
#define STATIC_GW_ADDR2 1
#define STATIC_GW_ADDR3 1
/* TCP Server Configuration */
#define SERVER_IP_ADDR "192.168.1.116" /* PC IP Address */
#define SERVER_PORT 5588
/* Blink Thread */
/**
* @brief LED闪烁任务入口函数
* @param parameter 任务参数(本函数中未使用)
* @note 该函数初始化PA6引脚为输出模式并在循环中翻转该引脚状态实现LED闪烁效果
*/
static void blink_entry(void *parameter)
{
/* Initialize PA6 */
@ -50,16 +59,31 @@ static void blink_entry(void *parameter)
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* 初始化为关闭状态 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
while (1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_6);
// osal_log_i("System Running...");
osal_thread_mdelay(1000);
/* 检查数据上传成功标志 */
if (data_upload_success)
{
/* 亮LED */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
osal_thread_mdelay(500); /* 亮500ms */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
/* 重置标志 */
data_upload_success = 0;
}
osal_thread_mdelay(100);
}
}
/* Ethernet Input Thread */
/**
* @brief 以太网输入任务入口函数
* @param parameter 任务参数(本函数中未使用)
* @note 该函数在循环中调用ethernetif_input处理接收到的以太网数据包
*/
static void ethernet_input_entry(void *parameter)
{
while(1)
@ -69,11 +93,22 @@ static void ethernet_input_entry(void *parameter)
}
}
/* Ping Configuration */
/**
* @brief Ping配置参数
* @note 该结构体定义了Ping操作的相关参数包括ID、数据大小、接收超时时间
*/
#define PING_ID 0xAFAF
#define PING_DATA_SIZE 32
#define PING_RCV_TIMEO 5000 // 5 seconds
/**
* @brief 准备Ping回显包
* @param iecho 指向icmp_echo_hdr结构体的指针用于存储回显包
* @param len 回显包的总长度包括icmp_echo_hdr头和数据
* @param seq Ping包的序列号
* @note 该函数填充icmp_echo_hdr结构体设置类型为ICMP_ECHOID为PING_ID序列号为seq
* 并填充数据部分为0到(data_len-1)的连续字节
*/
static void ping_prepare_echo(struct icmp_echo_hdr *iecho, u16_t len, u16_t seq)
{
size_t i;
@ -92,6 +127,12 @@ static void ping_prepare_echo(struct icmp_echo_hdr *iecho, u16_t len, u16_t seq)
iecho->chksum = inet_chksum(iecho, len);
}
/**
* @brief 检查目标IP是否可达
* @param target_ip 目标IP地址字符串
* @return 如果目标IP可达返回1否则返回0
* @note 该函数通过发送ICMP Echo请求包到目标IP并等待响应来检查目标IP是否可达
*/
static int ping_check(const char *target_ip)
{
int s;
@ -170,374 +211,7 @@ exit:
return ret;
}
/* TCP Client Configuration */
#define TCP_CLIENT_AUTH_USERNAME "admin"
#define TCP_CLIENT_AUTH_PASSWORD "password123"
#define TCP_CLIENT_MAX_RECONNECT_DELAY 10000 // 最大重连延迟 10 秒
#define TCP_CLIENT_INIT_RECONNECT_DELAY 1000 // 初始重连延迟 1 秒
#define TCP_CLIENT_HEARTBEAT_INTERVAL 500 // 心跳间隔 500ms
#define TCP_CLIENT_AUTH_TIMEOUT 3000 // 认证超时 3 秒
#define TCP_CLIENT_CONNECT_TIMEOUT 5000 // 连接超时 5 秒
/* Helper function: Check if authentication response is successful */
static int is_auth_success(const char *response)
{
if (!response)
return 0;
// 移除末尾的换行符和空格
char *p = (char *)response;
while (*p && (*p == ' ' || *p == '\n' || *p == '\r')) p++;
char *end = p + strlen(p) - 1;
while (end > p && (*end == ' ' || *end == '\n' || *end == '\r')) end--;
*(end + 1) = '\0';
// 转换为小写进行比较
char lower_data[128];
for (int i = 0; p[i]; i++) {
lower_data[i] = tolower(p[i]);
}
lower_data[strlen(p)] = '\0';
osal_log_i("Processed auth response: '%s'", p);
osal_log_i("Lowercase auth response: '%s'", lower_data);
/* Check if authentication was successful */
if (strstr(lower_data, "ok") != NULL ||
strstr(lower_data, "success") != NULL ||
strstr(lower_data, "auth_success") != NULL)
{
return 1;
}
return 0;
}
/* Helper function: Create and configure socket */
static int create_and_configure_socket(void)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
osal_log_e("Socket creation error: %d", errno);
return -1;
}
/* Set non-blocking mode */
int flags = fcntl(sock, F_GETFL, 0);
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0)
{
osal_log_e("Failed to set non-blocking mode: %d", errno);
closesocket(sock);
return -1;
}
/* Set socket options for better performance */
int keepalive = 1;
int keepidle = 60;
int keepintvl = 10;
int keepcnt = 3;
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(keepintvl));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt));
return sock;
}
/* Helper function: Connect to server with timeout */
static int connect_to_server(int sock, struct sockaddr_in *server_addr)
{
int ret = connect(sock, (struct sockaddr *)server_addr, sizeof(struct sockaddr));
if (ret == -1 && errno != EINPROGRESS)
{
osal_log_e("Connect failed: %d", errno);
return -1;
}
/* Use select to wait for connection completion */
fd_set wset;
struct timeval tv;
FD_ZERO(&wset);
FD_SET(sock, &wset);
tv.tv_sec = TCP_CLIENT_CONNECT_TIMEOUT / 1000;
tv.tv_usec = (TCP_CLIENT_CONNECT_TIMEOUT % 1000) * 1000;
if (select(sock + 1, NULL, &wset, NULL, &tv) > 0)
{
int error = 0;
socklen_t len = sizeof(error);
getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len);
if (error != 0)
{
osal_log_e("Connect failed: %d", error);
return -1;
}
}
else
{
osal_log_e("Connect timeout");
return -1;
}
return 0;
}
/* Helper function: Send authentication and wait for response */
static int authenticate_server(int sock)
{
/* Send authentication information */
char auth_data[64];
snprintf(auth_data, sizeof(auth_data), "AUTH %s %s\n", TCP_CLIENT_AUTH_USERNAME, TCP_CLIENT_AUTH_PASSWORD);
if (send(sock, auth_data, strlen(auth_data), 0) < 0)
{
osal_log_e("Failed to send authentication: %d", errno);
return 0;
}
osal_log_i("Sent authentication: %s", auth_data);
/* Wait for authentication response */
char recv_data[128];
fd_set rset;
struct timeval tv;
FD_ZERO(&rset);
FD_SET(sock, &rset);
tv.tv_sec = TCP_CLIENT_AUTH_TIMEOUT / 1000;
tv.tv_usec = (TCP_CLIENT_AUTH_TIMEOUT % 1000) * 1000;
if (select(sock + 1, &rset, NULL, NULL, &tv) > 0)
{
int bytes_received = recv(sock, recv_data, sizeof(recv_data) - 1, 0);
if (bytes_received > 0)
{
recv_data[bytes_received] = '\0';
osal_log_i("Authentication response: '%s'", recv_data);
if (is_auth_success(recv_data))
{
osal_log_i("Authentication successful!");
return 1;
}
else
{
osal_log_e("Authentication failed: %s", recv_data);
return 0;
}
}
else if (bytes_received == 0)
{
osal_log_w("Connection closed by server during authentication");
return 0;
}
else
{
osal_log_e("Recv error during authentication: %d", errno);
return 0;
}
}
else
{
osal_log_w("No authentication response received");
return 0;
}
}
/* TCP Client Thread */
static void tcp_client_entry(void *parameter)
{
int sock = -1;
struct sockaddr_in server_addr;
char recv_data[128];
int bytes_received;
int reconnect_count = 0;
int reconnect_delay = TCP_CLIENT_INIT_RECONNECT_DELAY;
/* Wait for IP address ready */
if (osal_sem_take(sem_ip_ready, OSAL_WAIT_FOREVER) != OSAL_OK)
{
osal_log_e("Failed to take IP ready semaphore");
return;
}
osal_log_i("TCP Client Starting...");
/* Prepare server address */
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP_ADDR);
memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
while (1)
{
/* Check Link Status */
if (!netif_is_link_up(&gnetif)) {
osal_log_w("Link down, waiting for link up...");
osal_thread_mdelay(1000);
continue;
}
/* Create and configure socket */
sock = create_and_configure_socket();
if (sock < 0)
{
osal_log_e("Failed to create socket, reconnecting in %d ms...", reconnect_delay);
osal_thread_mdelay(reconnect_delay);
// 指数退避重连
reconnect_count++;
reconnect_delay = MIN(reconnect_delay * 2, TCP_CLIENT_MAX_RECONNECT_DELAY);
continue;
}
osal_log_i("Connecting to server %s:%d...", SERVER_IP_ADDR, SERVER_PORT);
/* Connect to server */
if (connect_to_server(sock, &server_addr) < 0)
{
closesocket(sock);
osal_log_e("Connection failed, reconnecting in %d ms...", reconnect_delay);
osal_thread_mdelay(reconnect_delay);
// 指数退避重连
reconnect_count++;
reconnect_delay = MIN(reconnect_delay * 2, TCP_CLIENT_MAX_RECONNECT_DELAY);
continue;
}
osal_log_i("Connected to server!");
/* Authenticate with server */
if (!authenticate_server(sock))
{
osal_log_e("Authentication failed, reconnecting in %d ms...", reconnect_delay);
closesocket(sock);
osal_thread_mdelay(reconnect_delay);
// 指数退避重连
reconnect_count++;
reconnect_delay = MIN(reconnect_delay * 2, TCP_CLIENT_MAX_RECONNECT_DELAY);
continue;
}
// 认证成功,重置重连计数器和延迟
reconnect_count = 0;
reconnect_delay = TCP_CLIENT_INIT_RECONNECT_DELAY;
int report_interval = 5000; // 5秒上报一次温湿度数据
osal_tick_t last_report_time = 0;
while (1)
{
/* Check Link Status */
if (!netif_is_link_up(&gnetif)) {
osal_log_w("Link lost during connection");
break;
}
/* Send heartbeat */
if (send(sock, "Heartbeat", 9, 0) < 0) {
if (errno != EWOULDBLOCK) {
osal_log_e("Send error: %d", errno);
break;
}
}
/* 定期读取并上报温湿度数据 */
osal_tick_t current_time = osal_tick_get();
if (current_time - last_report_time >= report_interval)
{
float temperature = 0.0f, humidity = 0.0f;
int result = sht40_read_temperature_humidity(&temperature, &humidity);
if (result == 0)
{
char sensor_data[64];
int temp_int = (int)(temperature * 100);
int hum_int = (int)(humidity * 100);
/* 处理负值温度显示 */
int temp_integer = temp_int / 100;
int temp_decimal = temp_int % 100;
if (temp_decimal < 0) temp_decimal = -temp_decimal;
int hum_integer = hum_int / 100;
int hum_decimal = hum_int % 100;
if (hum_decimal < 0) hum_decimal = -hum_decimal;
if (temp_integer >= 0)
{
snprintf(sensor_data, sizeof(sensor_data), "TEMP=+%d.%02d℃,HUM=%d.%02d%%RH\n",
temp_integer, temp_decimal, hum_integer, hum_decimal);
}
else
{
snprintf(sensor_data, sizeof(sensor_data), "TEMP=%d.%02d℃,HUM=%d.%02d%%RH\n",
temp_integer, temp_decimal, hum_integer, hum_decimal);
}
if (send(sock, sensor_data, strlen(sensor_data), 0) < 0)
{
if (errno != EWOULDBLOCK)
{
osal_log_e("Send sensor data failed: %d", errno);
break;
}
}
else
{
osal_log_i("Sent sensor data: %s", sensor_data);
}
}
else
{
osal_log_e("Failed to read sensor data, result: %d", result);
}
last_report_time = current_time;
}
/* Wait for data with timeout */
fd_set rset;
struct timeval tv;
FD_ZERO(&rset);
FD_SET(sock, &rset);
tv.tv_sec = TCP_CLIENT_HEARTBEAT_INTERVAL / 1000;
tv.tv_usec = (TCP_CLIENT_HEARTBEAT_INTERVAL % 1000) * 1000;
int n = select(sock + 1, &rset, NULL, NULL, &tv);
if (n > 0) {
bytes_received = recv(sock, recv_data, sizeof(recv_data) - 1, 0);
if (bytes_received > 0)
{
recv_data[bytes_received] = '\0';
osal_log_i("Received: %s", recv_data);
}
else if (bytes_received == 0)
{
osal_log_w("Connection closed by server");
break;
}
else
{
if (errno != EWOULDBLOCK) {
osal_log_e("Recv error: %d", errno);
break;
}
}
} else if (n < 0) {
osal_log_e("Select error: %d", errno);
break;
}
/* n == 0 is timeout, continue loop */
}
closesocket(sock);
osal_log_i("Connection closed, reconnecting in %d ms...", reconnect_delay);
osal_thread_mdelay(reconnect_delay);
// 指数退避重连
reconnect_count++;
reconnect_delay = MIN(reconnect_delay * 2, TCP_CLIENT_MAX_RECONNECT_DELAY);
}
}
/* Network Monitor Thread (DHCP & Fallback) */
static void network_monitor_entry(void *parameter)
@ -553,6 +227,8 @@ static void network_monitor_entry(void *parameter)
if (gnetif.ip_addr.addr != 0)
{
osal_log_i("DHCP Success!");
/* 触发网络连接事件 */
event_trigger(EVENT_TYPE_NETWORK_CONNECTED, EVENT_PRIORITY_NORMAL, NULL, 0);
break;
}
osal_thread_mdelay(100);
@ -574,6 +250,8 @@ static void network_monitor_entry(void *parameter)
netif_set_addr(&gnetif, &ipaddr, &netmask, &gw);
netif_set_up(&gnetif);
/* 触发网络连接事件 */
event_trigger(EVENT_TYPE_NETWORK_CONNECTED, EVENT_PRIORITY_NORMAL, NULL, 0);
}
osal_log_i("IP Address: %d.%d.%d.%d", ip4_addr1(&gnetif.ip_addr), ip4_addr2(&gnetif.ip_addr), ip4_addr3(&gnetif.ip_addr), ip4_addr4(&gnetif.ip_addr));
@ -591,6 +269,61 @@ static void network_monitor_entry(void *parameter)
}
}
/* 事件处理函数 */
static int sensor_data_handler(event_t *event, void *user_data)
{
osal_log_i("Handling sensor data event");
/* 这里可以添加传感器数据处理逻辑 */
return 0;
}
static int network_connected_handler(event_t *event, void *user_data)
{
osal_log_i("Handling network connected event");
state_manager_set_network_state(NETWORK_STATE_CONNECTED);
return 0;
}
static int network_disconnected_handler(event_t *event, void *user_data)
{
osal_log_i("Handling network disconnected event");
state_manager_set_network_state(NETWORK_STATE_DISCONNECTED);
return 0;
}
static int tcp_client_connected_handler(event_t *event, void *user_data)
{
osal_log_i("Handling TCP client connected event");
state_manager_set_tcp_state(TCP_STATE_CONNECTED);
return 0;
}
static int tcp_client_disconnected_handler(event_t *event, void *user_data)
{
osal_log_i("Handling TCP client disconnected event");
state_manager_set_tcp_state(TCP_STATE_LISTENING);
return 0;
}
static int timer_handler(event_t *event, void *user_data)
{
osal_log_i("Handling timer event");
/* 这里可以添加定时任务逻辑 */
return 0;
}
static int error_handler(event_t *event, void *user_data)
{
osal_log_i("Handling error event");
error_code_t error = ERROR_UNKNOWN;
if (event->data != NULL)
{
error = *(error_code_t *)event->data;
}
error_handler_process(error, user_data);
return 0;
}
int main(void)
{
osal_thread_t tid;
@ -601,6 +334,62 @@ int main(void)
rt_kprintf("Main: OSAL Log Level = %d\n", OSAL_LOG_LEVEL);
osal_log_i("Test osal_log_i from main");
/* Initialize event queue */
if (event_queue_init() != 0)
{
osal_log_e("Failed to initialize event queue");
return -1;
}
/* Initialize event handler */
if (event_handler_init() != 0)
{
osal_log_e("Failed to initialize event handler");
return -1;
}
/* Initialize transaction management */
if (transaction_init() != 0)
{
osal_log_e("Failed to initialize transaction management");
return -1;
}
/* Initialize state manager */
if (state_manager_init() != 0)
{
osal_log_e("Failed to initialize state manager");
return -1;
}
/* Initialize error handler */
if (error_handler_init() != 0)
{
osal_log_e("Failed to initialize error handler");
return -1;
}
/* Register event handlers */
event_handler_register(EVENT_TYPE_SENSOR_DATA, sensor_data_handler, NULL);
event_handler_register(EVENT_TYPE_NETWORK_CONNECTED, network_connected_handler, NULL);
event_handler_register(EVENT_TYPE_NETWORK_DISCONNECTED, network_disconnected_handler, NULL);
event_handler_register(EVENT_TYPE_TCP_CLIENT_CONNECTED, tcp_client_connected_handler, NULL);
event_handler_register(EVENT_TYPE_TCP_CLIENT_DISCONNECTED, tcp_client_disconnected_handler, NULL);
event_handler_register(EVENT_TYPE_TIMER, timer_handler, NULL);
event_handler_register(EVENT_TYPE_ERROR, error_handler, NULL);
/* Create event dispatch thread */
tid = osal_thread_create("event_dispatch", event_dispatch_thread, NULL, 1024, 8);
if (tid != NULL)
{
osal_thread_start(tid);
rt_kprintf("Thread 'event_dispatch' created.\n");
}
else
{
rt_kprintf("Failed to create thread 'event_dispatch'\n");
}
/* Initialize TCP/IP stack */
rt_kprintf("Initializing TCP/IP stack...\n");
tcpip_init(NULL, NULL);
@ -649,10 +438,13 @@ int main(void)
if (sht40_init() != 0)
{
osal_log_e("SHT40 sensor initialization failed");
error_code_t error = ERROR_SENSOR;
event_trigger(EVENT_TYPE_ERROR, EVENT_PRIORITY_HIGH, &error, sizeof(error));
}
else
{
osal_log_i("SHT40 sensor initialized successfully");
state_manager_set_sensor_state(SENSOR_STATE_READY);
/* Use heater once during initialization for self-calibration */
osal_log_i("Performing SHT40 self-calibration using heater...");
@ -663,19 +455,21 @@ int main(void)
else
{
osal_log_e("SHT40 self-calibration failed");
error_code_t error = ERROR_SENSOR;
event_trigger(EVENT_TYPE_ERROR, EVENT_PRIORITY_HIGH, &error, sizeof(error));
}
}
/* Create TCP Client Thread */
tid = osal_thread_create("tcp_client", tcp_client_entry, NULL, 2048, 15);
/* Create TCP Server Thread */
tid = osal_thread_create("tcp_server", tcp_server_entry, NULL, 2048, 15);
if (tid != NULL)
{
osal_thread_start(tid);
rt_kprintf("Thread 'tcp_client' created.\n");
rt_kprintf("Thread 'tcp_server' created.\n");
}
else
{
rt_kprintf("Failed to create thread 'tcp_client'\n");
rt_kprintf("Failed to create thread 'tcp_server'\n");
}
/* Create Blink/Status Thread */