Add Transport-Neutral Serial Session Broker

This commit is contained in:
2026-08-23 00:01:50 +02:00
parent 535c27350d
commit 2524f43fac
11 changed files with 1407 additions and 102 deletions
+165
View File
@@ -0,0 +1,165 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SESSION_BROKER_MAX_CLIENTS 8U
#define SESSION_BROKER_CLIENT_NAME_MAX 23U
#define SESSION_BROKER_OUTPUT_SIZE 4096U
#define SESSION_BROKER_EVENT_QUEUE_LENGTH 16U
typedef uint32_t session_broker_client_id_t;
/* ID zero is reserved for "no client" and, specifically, "no writer". */
#define SESSION_BROKER_NO_CLIENT ((session_broker_client_id_t)0U)
typedef enum {
SESSION_BROKER_CLIENT_CONSOLE = 0,
SESSION_BROKER_CLIENT_USB,
SESSION_BROKER_CLIENT_WEB,
SESSION_BROKER_CLIENT_SSH,
SESSION_BROKER_CLIENT_INTERNAL,
} session_broker_client_type_t;
typedef enum {
SESSION_BROKER_EVENT_CONNECT = 0,
SESSION_BROKER_EVENT_DISCONNECT,
SESSION_BROKER_EVENT_WRITER_GRANTED,
SESSION_BROKER_EVENT_WRITER_RELEASED,
SESSION_BROKER_EVENT_WRITER_REVOKED,
SESSION_BROKER_EVENT_WRITER_DENIED,
} session_broker_event_type_t;
/*
* Events contain no pointers, so transports can encode them without lifetime
* concerns. sequence is broker-global and strictly increases per event.
* client_id identifies the subject; writer_id is the writer after the event.
*/
typedef struct {
uint64_t sequence;
session_broker_event_type_t type;
session_broker_client_id_t client_id;
session_broker_client_id_t writer_id;
} session_broker_event_t;
typedef struct {
/* UART bytes considered for delivery while this client was connected. */
uint64_t uart_rx_bytes;
uint64_t output_queued_bytes;
uint64_t output_read_bytes;
uint64_t output_dropped_bytes;
uint64_t tx_accepted_bytes;
uint64_t tx_rejected_bytes;
uint64_t connections;
uint64_t writer_requests;
uint64_t writer_grants;
uint64_t writer_releases;
uint64_t writer_revocations;
uint64_t writer_denials;
uint64_t writer_changes;
uint64_t events_queued;
uint64_t events_popped;
uint64_t event_drops;
} session_broker_client_counters_t;
typedef struct {
uint64_t uart_rx_bytes;
/* UART RX drained when there were no connected observers. */
uint64_t unobserved_rx_bytes;
/* Queue/read/drop totals count one copy per client observer. */
uint64_t output_queued_bytes;
uint64_t output_read_bytes;
uint64_t output_dropped_bytes;
uint64_t tx_accepted_bytes;
uint64_t tx_rejected_bytes;
uint64_t connections;
uint64_t disconnections;
uint64_t writer_requests;
uint64_t writer_grants;
uint64_t writer_releases;
uint64_t writer_revocations;
uint64_t writer_denials;
uint64_t writer_changes;
uint64_t events_generated;
uint64_t events_queued;
uint64_t events_popped;
uint64_t event_drops;
} session_broker_global_counters_t;
typedef struct {
session_broker_client_id_t id;
session_broker_client_type_t type;
char name[SESSION_BROKER_CLIENT_NAME_MAX + 1U];
bool is_writer;
size_t output_bytes_pending;
uint32_t events_pending;
session_broker_client_counters_t counters;
} session_broker_client_snapshot_t;
typedef struct {
session_broker_client_id_t writer_id;
uint32_t connected_clients;
uint64_t latest_event_sequence;
session_broker_global_counters_t counters;
} session_broker_global_snapshot_t;
/*
* Allocates all eight output streams and event queues, then starts the
* permanent broker task. The serial service must already be initialized
* before the first client connects.
*/
esp_err_t session_broker_init(void);
esp_err_t session_broker_connect(session_broker_client_type_t type,
const char *name,
session_broker_client_id_t *client_id);
esp_err_t session_broker_disconnect(session_broker_client_id_t client_id);
/*
* There is exactly one writer lease. Requests are granted only while idle;
* a competing request emits WRITER_DENIED. Force with ID zero revokes the
* current writer without assigning a replacement.
*/
esp_err_t session_broker_request_writer(session_broker_client_id_t client_id);
esp_err_t session_broker_release_writer(session_broker_client_id_t client_id);
esp_err_t session_broker_force_writer(session_broker_client_id_t client_id);
session_broker_client_id_t session_broker_get_writer_id(void);
/*
* Binary-transparent, zero-time data operations. write is accepted only from
* the current writer. read has one logical caller per connected transport.
* Partial transfers are normal and are reported through the output count.
*/
esp_err_t session_broker_write(session_broker_client_id_t client_id,
const uint8_t *data,
size_t size,
size_t *accepted);
esp_err_t session_broker_read(session_broker_client_id_t client_id,
uint8_t *data,
size_t size,
size_t *received);
esp_err_t session_broker_pop_event(session_broker_client_id_t client_id,
session_broker_event_t *event);
esp_err_t session_broker_get_client_snapshot(session_broker_client_id_t client_id,
session_broker_client_snapshot_t *snapshot);
esp_err_t session_broker_get_global_snapshot(session_broker_global_snapshot_t *snapshot);
/* Returns the number copied. Pass NULL with capacity zero to count clients. */
size_t session_broker_list_clients(session_broker_client_snapshot_t *clients,
size_t capacity);
/* Counter clearing does not reset client IDs, queued data, or event sequence. */
esp_err_t session_broker_clear_counters(void);
esp_err_t session_broker_clear_client_counters(session_broker_client_id_t client_id);
#ifdef __cplusplus
}
#endif