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 - запрос роутинг-таблицы |
||||||
|
*/ |
||||||
|
|
||||||
|
#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); |
||||||
|
|
||||||
1.
|
/**
|
||||||
|
* @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