Browse Source
Implemented: - etcp_send(conn, entry) - отправляет пакет в очередь normalizer - etcp_bind(id, callback) - подписка на пакеты с определенным ID - etcp_unbind(id) - отписка от пакетов - etcp_recv(queue, arg) - коллбэк для маршрутизации пакетов по ID - etcp_api_init/deinit - инициализация API Integration: - pn_init() теперь устанавливает etcp_recv как callback для pn->output - Добавлены etcp_api.c/h в src/Makefile.am - Добавлен etcp_api.o в тестовые зависимости API использует первый байт кодограммы (cmd) как ID для маршрутизации. ID=0 используется как default handler если нет специфичного binding.nodeinfo-routing-update
11 changed files with 282 additions and 7 deletions
@ -0,0 +1,179 @@
|
||||
/**
|
||||
* @file etcp_api.c |
||||
* @brief Реализация API для приёма-передачи пакетов через ETCP |
||||
*/ |
||||
|
||||
#include "etcp_api.h" |
||||
#include "etcp.h" |
||||
#include "pkt_normalizer.h" |
||||
#include "../lib/debug_config.h" |
||||
#include <string.h> |
||||
|
||||
#define DEBUG_CATEGORY_ETCP_API 1 |
||||
|
||||
#define ETCP_MAX_BINDINGS 256 /**< Максимальное количество bindings (по одному на каждый ID 0-255) */ |
||||
|
||||
/**
|
||||
* @brief Структура binding для ID |
||||
*/ |
||||
struct ETCP_BINDING { |
||||
etcp_recv_fn callback; /**< Коллбэк для обработки пакетов */ |
||||
uint8_t active; /**< Флаг активности binding */ |
||||
}; |
||||
|
||||
// Таблица bindings (индекс = ID пакета)
|
||||
static struct ETCP_BINDING g_bindings[ETCP_MAX_BINDINGS]; |
||||
static int g_api_initialized = 0; |
||||
|
||||
void etcp_api_init(void) { |
||||
if (g_api_initialized) { |
||||
return; |
||||
} |
||||
|
||||
memset(g_bindings, 0, sizeof(g_bindings)); |
||||
g_api_initialized = 1; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "ETCP API initialized"); |
||||
} |
||||
|
||||
void etcp_api_deinit(void) { |
||||
if (!g_api_initialized) { |
||||
return; |
||||
} |
||||
|
||||
memset(g_bindings, 0, sizeof(g_bindings)); |
||||
g_api_initialized = 0; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "ETCP API deinitialized"); |
||||
} |
||||
|
||||
int etcp_bind(uint8_t id, etcp_recv_fn callback) { |
||||
if (!g_api_initialized) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_bind: API not initialized"); |
||||
return -1; |
||||
} |
||||
|
||||
if (!callback) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_bind: NULL callback for id=%u", id); |
||||
return -1; |
||||
} |
||||
|
||||
if (g_bindings[id].active) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_bind: ID %u already bound, overwriting", id); |
||||
} |
||||
|
||||
g_bindings[id].callback = callback; |
||||
g_bindings[id].active = 1; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "etcp_bind: Bound ID %u to callback %p", id, (void*)callback); |
||||
return 0; |
||||
} |
||||
|
||||
int etcp_unbind(uint8_t id) { |
||||
if (!g_api_initialized) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: API not initialized"); |
||||
return -1; |
||||
} |
||||
|
||||
if (!g_bindings[id].active) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: ID %u not bound", id); |
||||
return -1; |
||||
} |
||||
|
||||
g_bindings[id].callback = NULL; |
||||
g_bindings[id].active = 0; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: Unbound ID %u", id); |
||||
return 0; |
||||
} |
||||
|
||||
int etcp_send(struct ETCP_CONN* conn, struct ll_entry* entry) { |
||||
if (!conn) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: NULL connection"); |
||||
return -1; |
||||
} |
||||
|
||||
if (!entry) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: NULL entry"); |
||||
return -1; |
||||
} |
||||
|
||||
if (!conn->normalizer) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: Connection has no normalizer"); |
||||
return -1; |
||||
} |
||||
|
||||
struct PKTNORM* pn = conn->normalizer; |
||||
|
||||
if (!pn->input) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: Normalizer has no input queue"); |
||||
return -1; |
||||
} |
||||
|
||||
// Помещаем entry в очередь input normalizer
|
||||
// queue_data_put забирает ownership entry
|
||||
int result = queue_data_put(pn->input, entry, 0); |
||||
|
||||
if (result != 0) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_send: queue_data_put failed (queue full?)"); |
||||
return -1; |
||||
} |
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_send: Packet queued for sending"); |
||||
return 0; |
||||
} |
||||
|
||||
void etcp_recv(struct ll_queue* queue, void* arg) { |
||||
struct ETCP_CONN* conn = (struct ETCP_CONN*)arg; |
||||
|
||||
if (!queue || !conn) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Invalid arguments (queue=%p, conn=%p)",
|
||||
(void*)queue, (void*)conn); |
||||
return; |
||||
} |
||||
|
||||
// Получаем элемент из очереди
|
||||
struct ll_entry* entry = queue_data_get(queue); |
||||
if (!entry) { |
||||
// Очередь пуста - это нормально, просто возобновляем коллбэк
|
||||
queue_resume_callback(queue); |
||||
return; |
||||
} |
||||
|
||||
// Проверяем что есть данные
|
||||
if (!entry->dgram || entry->len == 0) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Empty packet received"); |
||||
queue_entry_free(entry); |
||||
queue_dgram_free(entry); |
||||
queue_resume_callback(queue); |
||||
return; |
||||
} |
||||
|
||||
// Получаем ID пакета (первый байт кодограммы)
|
||||
uint8_t id = entry->dgram[0]; |
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Received packet with ID %u from conn %p",
|
||||
id, (void*)conn); |
||||
|
||||
// Ищем binding для этого ID
|
||||
if (id < ETCP_MAX_BINDINGS && g_bindings[id].active && g_bindings[id].callback) { |
||||
// Вызываем коллбэк
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Delivering packet ID %u to callback %p",
|
||||
id, (void*)g_bindings[id].callback); |
||||
g_bindings[id].callback(conn, entry); |
||||
} else { |
||||
// Нет binding для этого ID - по умолчанию считаем что это данные (ID=0)
|
||||
if (g_bindings[0].active && g_bindings[0].callback) { |
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_recv: No binding for ID %u, routing to default (ID=0)", id); |
||||
g_bindings[0].callback(conn, entry); |
||||
} else { |
||||
// Нет default binding - освобождаем пакет
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_recv: No binding for ID %u and no default handler, dropping packet", id); |
||||
queue_entry_free(entry); |
||||
queue_dgram_free(entry); |
||||
} |
||||
} |
||||
|
||||
// Возобновляем коллбэк для получения следующего пакета
|
||||
queue_resume_callback(queue); |
||||
} |
||||
@ -1,5 +1,95 @@
|
||||
/* api для приёма-передачи пакетов через etcp
|
||||
/**
|
||||
* @file etcp_api.h |
||||
* @brief API для приёма-передачи пакетов через ETCP |
||||
*
|
||||
* Основные функции: |
||||
* - etcp_send() - отправить пакет в очередь normalizer |
||||
* - etcp_bind() - подписаться на пакеты с определенным ID |
||||
* - etcp_recv() - коллбэк для сбора пакетов из всех подключений |
||||
*
|
||||
* Формат кодограмм: <cmd 1 byte> <data ... n bytes> |
||||
* cmd = 0 - пакет для передачи адресату |
||||
* cmd = 1 - элемент роутинг-таблицы |
||||
* cmd = 2 - запрос роутинг-таблицы |
||||
*/ |
||||
|
||||
1.
|
||||
#ifndef ETCP_API_H |
||||
#define ETCP_API_H |
||||
|
||||
*/ |
||||
#include <stdint.h> |
||||
#include "../lib/ll_queue.h" |
||||
|
||||
// Forward declarations
|
||||
struct ETCP_CONN; |
||||
|
||||
/**
|
||||
* @brief Тип коллбэка для приёма пакетов |
||||
* @param conn ETCP соединение от которого получен пакет |
||||
* @param entry Элемент очереди с данными пакета |
||||
*
|
||||
* @note Коллбэк должен освободить entry через queue_entry_free()
|
||||
* и dgram через queue_dgram_free() после обработки |
||||
*/ |
||||
typedef void (*etcp_recv_fn)(struct ETCP_CONN* conn, struct ll_entry* entry); |
||||
|
||||
/**
|
||||
* @brief Отправить пакет в очередь normalizer |
||||
*
|
||||
* @param conn ETCP соединение |
||||
* @param entry Элемент очереди с данными для отправки |
||||
* @return 0 при успехе, -1 при ошибке |
||||
*
|
||||
* @note Функция забирает ownership entry - вызывающий код не должен
|
||||
* освобождать entry после вызова |
||||
*/ |
||||
int etcp_send(struct ETCP_CONN* conn, struct ll_entry* entry); |
||||
|
||||
/**
|
||||
* @brief Подписаться на все пакеты с указанным ID |
||||
*
|
||||
* @param id Идентификатор пакета (первый байт кодограммы, cmd) |
||||
* @param callback Коллбэк для обработки пакетов с этим ID |
||||
* @return 0 при успехе, -1 при ошибке (например, нет свободных слотов) |
||||
*
|
||||
* @note ID=0 зарезервирован для обычных пакетов данных |
||||
* @note Можно зарегистрировать только один коллбэк на каждый ID |
||||
*/ |
||||
int etcp_bind(uint8_t id, etcp_recv_fn callback); |
||||
|
||||
/**
|
||||
* @brief Отписаться от пакетов с указанным ID |
||||
*
|
||||
* @param id Идентификатор пакета |
||||
* @return 0 при успехе, -1 если binding не найден |
||||
*/ |
||||
int etcp_unbind(uint8_t id); |
||||
|
||||
/**
|
||||
* @brief Коллбэк для очередей output ll_queue normalizer |
||||
*
|
||||
* Собирает пакеты из всех подключений и отправляет в bind->cbk |
||||
* по ID (первый байт кодограммы). |
||||
*
|
||||
* @param queue Очередь из которой получен пакет |
||||
* @param arg Аргумент - указатель на ETCP_CONN |
||||
*
|
||||
* @note Этот коллбэк устанавливается автоматически при создании |
||||
* ETCP подключения через pn_init() |
||||
*/ |
||||
void etcp_recv(struct ll_queue* queue, void* arg); |
||||
|
||||
/**
|
||||
* @brief Инициализация API |
||||
*
|
||||
* Должна быть вызвана один раз перед использованием API |
||||
*/ |
||||
void etcp_api_init(void); |
||||
|
||||
/**
|
||||
* @brief Деинициализация API |
||||
*
|
||||
* Очищает все bindings |
||||
*/ |
||||
void etcp_api_deinit(void); |
||||
|
||||
#endif // ETCP_API_H
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue