mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-04 06:11:06 +00:00 
			
		
		
		
	Enables building C3 examples in CI. Fixes related warnings/errors and disables examples that cannot run.
		
			
				
	
	
		
			230 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			230 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Keep Alive engine for wss server example
 | 
						|
 | 
						|
   This example code is in the Public Domain (or CC0 licensed, at your option.)
 | 
						|
 | 
						|
   Unless required by applicable law or agreed to in writing, this
 | 
						|
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 | 
						|
   CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
*/
 | 
						|
 | 
						|
#include <esp_log.h>
 | 
						|
#include <esp_system.h>
 | 
						|
#include "freertos/FreeRTOS.h"
 | 
						|
#include "freertos/queue.h"
 | 
						|
#include "freertos/task.h"
 | 
						|
#include "keep_alive.h"
 | 
						|
#include "esp_timer.h"
 | 
						|
 | 
						|
typedef enum {
 | 
						|
    NO_CLIENT = 0,
 | 
						|
    CLIENT_FD_ADD,
 | 
						|
    CLIENT_FD_REMOVE,
 | 
						|
    CLIENT_UPDATE,
 | 
						|
    CLIENT_ACTIVE,
 | 
						|
    STOP_TASK,
 | 
						|
} client_fd_action_type_t;
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    client_fd_action_type_t type;
 | 
						|
    int fd;
 | 
						|
    uint64_t last_seen;
 | 
						|
} client_fd_action_t;
 | 
						|
 | 
						|
typedef struct wss_keep_alive_storage {
 | 
						|
    size_t max_clients;
 | 
						|
    wss_check_client_alive_cb_t check_client_alive_cb;
 | 
						|
    wss_check_client_alive_cb_t client_not_alive_cb;
 | 
						|
    size_t keep_alive_period_ms;
 | 
						|
    size_t not_alive_after_ms;
 | 
						|
    void * user_ctx;
 | 
						|
    QueueHandle_t q;
 | 
						|
    client_fd_action_t clients[];
 | 
						|
} wss_keep_alive_storage_t;
 | 
						|
 | 
						|
typedef struct wss_keep_alive_storage* wss_keep_alive_t;
 | 
						|
 | 
						|
static const char *TAG = "wss_keep_alive";
 | 
						|
 | 
						|
static uint64_t _tick_get_ms(void)
 | 
						|
{
 | 
						|
    return esp_timer_get_time()/1000;
 | 
						|
}
 | 
						|
 | 
						|
// Goes over active clients to find out how long we could sleep before checking who's alive
 | 
						|
static uint64_t get_max_delay(wss_keep_alive_t h)
 | 
						|
{
 | 
						|
    int64_t check_after_ms = 30000; // max delay, no need to check anyone
 | 
						|
    for (int i=0; i<h->max_clients; ++i) {
 | 
						|
        if (h->clients[i].type == CLIENT_ACTIVE) {
 | 
						|
            uint64_t check_this_client_at = h->clients[i].last_seen + h->keep_alive_period_ms;
 | 
						|
            if (check_this_client_at < check_after_ms + _tick_get_ms()) {
 | 
						|
                check_after_ms = check_this_client_at - _tick_get_ms();
 | 
						|
                if (check_after_ms < 0) {
 | 
						|
                    check_after_ms = 1000; // min delay, some client(s) not responding already
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return check_after_ms;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static bool update_client(wss_keep_alive_t h, int sockfd, uint64_t timestamp)
 | 
						|
{
 | 
						|
    for (int i=0; i<h->max_clients; ++i) {
 | 
						|
        if (h->clients[i].type == CLIENT_ACTIVE && h->clients[i].fd == sockfd) {
 | 
						|
            h->clients[i].last_seen = timestamp;
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
static bool remove_client(wss_keep_alive_t h, int sockfd)
 | 
						|
{
 | 
						|
    for (int i=0; i<h->max_clients; ++i) {
 | 
						|
        if (h->clients[i].type == CLIENT_ACTIVE && h->clients[i].fd == sockfd) {
 | 
						|
            h->clients[i].type = NO_CLIENT;
 | 
						|
            h->clients[i].fd = -1;
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
static bool add_new_client(wss_keep_alive_t h,int sockfd)
 | 
						|
{
 | 
						|
    for (int i=0; i<h->max_clients; ++i) {
 | 
						|
        if (h->clients[i].type == NO_CLIENT) {
 | 
						|
            h->clients[i].type = CLIENT_ACTIVE;
 | 
						|
            h->clients[i].fd = sockfd;
 | 
						|
            h->clients[i].last_seen = _tick_get_ms();
 | 
						|
            return true; // success
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
static void keep_alive_task(void* arg)
 | 
						|
{
 | 
						|
    wss_keep_alive_storage_t *keep_alive_storage = arg;
 | 
						|
    bool run_task = true;
 | 
						|
    client_fd_action_t client_action;
 | 
						|
    while (run_task) {
 | 
						|
        if (xQueueReceive(keep_alive_storage->q, (void *) &client_action,
 | 
						|
                get_max_delay(keep_alive_storage) / portTICK_PERIOD_MS) == pdTRUE) {
 | 
						|
            switch (client_action.type) {
 | 
						|
                case CLIENT_FD_ADD:
 | 
						|
                    if (!add_new_client(keep_alive_storage, client_action.fd)) {
 | 
						|
                        ESP_LOGE(TAG, "Cannot add new client");
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                case CLIENT_FD_REMOVE:
 | 
						|
                    if (!remove_client(keep_alive_storage, client_action.fd)) {
 | 
						|
                        ESP_LOGE(TAG, "Cannot remove client fd:%d", client_action.fd);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                case CLIENT_UPDATE:
 | 
						|
                    if (!update_client(keep_alive_storage, client_action.fd, client_action.last_seen)) {
 | 
						|
                        ESP_LOGE(TAG, "Cannot find client fd:%d", client_action.fd);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                case STOP_TASK:
 | 
						|
                    run_task = false;
 | 
						|
                    break;
 | 
						|
                default:
 | 
						|
                    ESP_LOGE(TAG, "Unexpected client action");
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
                // timeout: check if PING message needed
 | 
						|
                for (int i=0; i<keep_alive_storage->max_clients; ++i) {
 | 
						|
                    if (keep_alive_storage->clients[i].type == CLIENT_ACTIVE) {
 | 
						|
                        if (keep_alive_storage->clients[i].last_seen + keep_alive_storage->keep_alive_period_ms <= _tick_get_ms()) {
 | 
						|
                            ESP_LOGD(TAG, "Haven't seen the client (fd=%d) for a while", keep_alive_storage->clients[i].fd);
 | 
						|
                            if (keep_alive_storage->clients[i].last_seen + keep_alive_storage->not_alive_after_ms <= _tick_get_ms()) {
 | 
						|
                                ESP_LOGE(TAG, "Client (fd=%d) not alive!",  keep_alive_storage->clients[i].fd);
 | 
						|
                                keep_alive_storage->client_not_alive_cb(keep_alive_storage, keep_alive_storage->clients[i].fd);
 | 
						|
                            } else {
 | 
						|
                                keep_alive_storage->check_client_alive_cb(keep_alive_storage, keep_alive_storage->clients[i].fd);
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
    }
 | 
						|
    vQueueDelete(keep_alive_storage->q);
 | 
						|
    free(keep_alive_storage);
 | 
						|
 | 
						|
    vTaskDelete(NULL);
 | 
						|
}
 | 
						|
 | 
						|
wss_keep_alive_t wss_keep_alive_start(wss_keep_alive_config_t *config)
 | 
						|
{
 | 
						|
    size_t queue_size = config->max_clients/2;
 | 
						|
    size_t client_list_size = config->max_clients + queue_size;
 | 
						|
    wss_keep_alive_t keep_alive_storage = calloc(1,
 | 
						|
            sizeof(wss_keep_alive_storage_t) + client_list_size* sizeof(client_fd_action_t));
 | 
						|
    if (keep_alive_storage == NULL) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
    keep_alive_storage->check_client_alive_cb = config->check_client_alive_cb;
 | 
						|
    keep_alive_storage->client_not_alive_cb = config->client_not_alive_cb;
 | 
						|
    keep_alive_storage->max_clients = config->max_clients;
 | 
						|
    keep_alive_storage->not_alive_after_ms = config->not_alive_after_ms;
 | 
						|
    keep_alive_storage->keep_alive_period_ms = config->keep_alive_period_ms;
 | 
						|
    keep_alive_storage->user_ctx = config->user_ctx;
 | 
						|
    keep_alive_storage->q =  xQueueCreate(queue_size, sizeof(client_fd_action_t));
 | 
						|
    if (xTaskCreate(keep_alive_task, "keep_alive_task", config->task_stack_size,
 | 
						|
                    keep_alive_storage, config->task_prio, NULL) != pdTRUE) {
 | 
						|
        wss_keep_alive_stop(keep_alive_storage);
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
    return keep_alive_storage;
 | 
						|
}
 | 
						|
 | 
						|
void wss_keep_alive_stop(wss_keep_alive_t h)
 | 
						|
{
 | 
						|
    client_fd_action_t stop = { .type = STOP_TASK };
 | 
						|
    xQueueSendToBack(h->q, &stop, 0);
 | 
						|
    // internal structs will be de-allocated in the task
 | 
						|
}
 | 
						|
 | 
						|
esp_err_t wss_keep_alive_add_client(wss_keep_alive_t h, int fd)
 | 
						|
{
 | 
						|
    client_fd_action_t client_fd_action = { .fd = fd, .type = CLIENT_FD_ADD};
 | 
						|
    if (xQueueSendToBack(h->q, &client_fd_action, 0) == pdTRUE) {
 | 
						|
        return ESP_OK;
 | 
						|
    }
 | 
						|
    return ESP_FAIL;
 | 
						|
}
 | 
						|
 | 
						|
esp_err_t wss_keep_alive_remove_client(wss_keep_alive_t h, int fd)
 | 
						|
{
 | 
						|
    client_fd_action_t client_fd_action = { .fd = fd, .type = CLIENT_FD_REMOVE};
 | 
						|
    if (xQueueSendToBack(h->q, &client_fd_action, 0) == pdTRUE) {
 | 
						|
        return ESP_OK;
 | 
						|
    }
 | 
						|
    return ESP_FAIL;
 | 
						|
}
 | 
						|
 | 
						|
esp_err_t wss_keep_alive_client_is_active(wss_keep_alive_t h, int fd)
 | 
						|
{
 | 
						|
    client_fd_action_t client_fd_action = { .fd = fd, .type = CLIENT_UPDATE,
 | 
						|
                                            .last_seen = _tick_get_ms()};
 | 
						|
    if (xQueueSendToBack(h->q, &client_fd_action, 0) == pdTRUE) {
 | 
						|
        return ESP_OK;
 | 
						|
    }
 | 
						|
    return ESP_FAIL;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
void wss_keep_alive_set_user_ctx(wss_keep_alive_t h, void *ctx)
 | 
						|
{
 | 
						|
    h->user_ctx = ctx;
 | 
						|
}
 | 
						|
 | 
						|
void* wss_keep_alive_get_user_ctx(wss_keep_alive_t h)
 | 
						|
{
 | 
						|
    return h->user_ctx;
 | 
						|
}
 |