Files
ESP32_Serial_Swiss_Army_Knife/src/session_console.c
T

468 lines
15 KiB
C

#include "session_console.h"
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "esp_console.h"
#include "esp_err.h"
#include "session_broker.h"
#define CONSOLE_HEX_MAX_BYTES 48
#define CONSOLE_READ_MAX_BYTES 512
static void print_usage(void)
{
printf("Usage:\n");
printf(" broker status|clients|counters|clear-counters\n");
printf(" broker connect <name>\n");
printf(" broker disconnect <client-id>\n");
printf(" broker request-writer <client-id>\n");
printf(" broker release-writer <client-id>\n");
printf(" broker force-writer <client-id|none>\n");
printf(" broker send-hex <client-id> <hex-bytes>\n");
printf(" broker read <client-id> [maximum-bytes]\n");
printf(" broker events <client-id>\n");
}
static bool parse_u32(const char *text, uint32_t minimum, uint32_t maximum, uint32_t *value)
{
if (text == NULL || *text == '\0') {
return false;
}
for (const char *character = text; *character != '\0'; ++character) {
if (*character < '0' || *character > '9') {
return false;
}
}
char *end = NULL;
errno = 0;
unsigned long parsed = strtoul(text, &end, 10);
if (errno != 0 || end == text || *end != '\0' ||
parsed < minimum || parsed > maximum) {
return false;
}
*value = (uint32_t)parsed;
return true;
}
static bool parse_client_id(const char *text, session_broker_client_id_t *client_id)
{
uint32_t value;
if (!parse_u32(text, 1, UINT32_MAX, &value)) {
return false;
}
*client_id = value;
return true;
}
static const char *client_type_name(session_broker_client_type_t type)
{
switch (type) {
case SESSION_BROKER_CLIENT_CONSOLE:
return "console";
case SESSION_BROKER_CLIENT_USB:
return "usb";
case SESSION_BROKER_CLIENT_WEB:
return "web";
case SESSION_BROKER_CLIENT_SSH:
return "ssh";
case SESSION_BROKER_CLIENT_INTERNAL:
return "internal";
default:
return "unknown";
}
}
static const char *event_type_name(session_broker_event_type_t type)
{
switch (type) {
case SESSION_BROKER_EVENT_CONNECT:
return "connect";
case SESSION_BROKER_EVENT_DISCONNECT:
return "disconnect";
case SESSION_BROKER_EVENT_WRITER_GRANTED:
return "writer-granted";
case SESSION_BROKER_EVENT_WRITER_RELEASED:
return "writer-released";
case SESSION_BROKER_EVENT_WRITER_REVOKED:
return "writer-revoked";
case SESSION_BROKER_EVENT_WRITER_DENIED:
return "writer-denied";
default:
return "unknown";
}
}
static int show_status(void)
{
session_broker_global_snapshot_t snapshot;
esp_err_t err = session_broker_get_global_snapshot(&snapshot);
if (err != ESP_OK) {
printf("Could not read broker status: %s\n", esp_err_to_name(err));
return 1;
}
printf("Broker clients=%" PRIu32 " writer=%lu latest-event=%" PRIu64 "\n",
snapshot.connected_clients,
(unsigned long)snapshot.writer_id,
snapshot.latest_event_sequence);
printf("UART-RX=%" PRIu64 " unobserved=%" PRIu64
" observer-queued=%" PRIu64 " observer-read=%" PRIu64
" observer-dropped=%" PRIu64 "\n",
snapshot.counters.uart_rx_bytes,
snapshot.counters.unobserved_rx_bytes,
snapshot.counters.output_queued_bytes,
snapshot.counters.output_read_bytes,
snapshot.counters.output_dropped_bytes);
return 0;
}
static int show_clients(void)
{
session_broker_client_snapshot_t clients[SESSION_BROKER_MAX_CLIENTS];
size_t count = session_broker_list_clients(clients, SESSION_BROKER_MAX_CLIENTS);
if (count == 0) {
printf("No broker clients connected.\n");
return 0;
}
printf("ID type writer pending events name\n");
for (size_t index = 0; index < count; ++index) {
const session_broker_client_snapshot_t *client = &clients[index];
printf("%-10lu %-9s %-6s %-7u %-6" PRIu32 " %s\n",
(unsigned long)client->id,
client_type_name(client->type),
client->is_writer ? "yes" : "no",
(unsigned int)client->output_bytes_pending,
client->events_pending,
client->name);
}
return 0;
}
static int show_counters(void)
{
session_broker_global_snapshot_t snapshot;
esp_err_t err = session_broker_get_global_snapshot(&snapshot);
if (err != ESP_OK) {
printf("Could not read broker counters: %s\n", esp_err_to_name(err));
return 1;
}
const session_broker_global_counters_t *counter = &snapshot.counters;
printf("RX: UART=%" PRIu64 " unobserved=%" PRIu64
" queued=%" PRIu64 " read=%" PRIu64 " dropped=%" PRIu64 "\n",
counter->uart_rx_bytes,
counter->unobserved_rx_bytes,
counter->output_queued_bytes,
counter->output_read_bytes,
counter->output_dropped_bytes);
printf("TX: accepted=%" PRIu64 " rejected=%" PRIu64 "\n",
counter->tx_accepted_bytes,
counter->tx_rejected_bytes);
printf("Sessions: connect=%" PRIu64 " disconnect=%" PRIu64
" writer-requests=%" PRIu64 " grants=%" PRIu64
" releases=%" PRIu64 " revocations=%" PRIu64
" denials=%" PRIu64 " changes=%" PRIu64 "\n",
counter->connections,
counter->disconnections,
counter->writer_requests,
counter->writer_grants,
counter->writer_releases,
counter->writer_revocations,
counter->writer_denials,
counter->writer_changes);
printf("Events: generated=%" PRIu64 " queued=%" PRIu64
" popped=%" PRIu64 " dropped=%" PRIu64 "\n",
counter->events_generated,
counter->events_queued,
counter->events_popped,
counter->event_drops);
session_broker_client_snapshot_t clients[SESSION_BROKER_MAX_CLIENTS];
size_t count = session_broker_list_clients(clients, SESSION_BROKER_MAX_CLIENTS);
printf("Active clients (since connect/clear; lost on disconnect; global totals retained):\n");
printf("Output bytes: HWM <= %u; clear seeds pending; read = handed to transport.\n",
(unsigned int)SESSION_BROKER_OUTPUT_SIZE);
printf("Global dropped also includes unread output discarded on disconnect.\n");
printf("ID type pending HWM UART queued read dropped\n");
for (size_t index = 0; index < count; ++index) {
const session_broker_client_snapshot_t *client = &clients[index];
printf("%-10lu %-9s %-7u %-7u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
(unsigned long)client->id,
client_type_name(client->type),
(unsigned int)client->output_bytes_pending,
(unsigned int)client->counters.output_high_water_bytes,
client->counters.uart_rx_bytes,
client->counters.output_queued_bytes,
client->counters.output_read_bytes,
client->counters.output_dropped_bytes);
}
return 0;
}
static int connect_client(const char *name)
{
session_broker_client_id_t client_id;
esp_err_t err = session_broker_connect(
SESSION_BROKER_CLIENT_CONSOLE,
name,
&client_id);
if (err != ESP_OK) {
printf("Could not connect broker client: %s\n", esp_err_to_name(err));
return 1;
}
printf("Connected console client '%s' with ID %lu.\n", name, (unsigned long)client_id);
return 0;
}
static int disconnect_client(const char *id_text)
{
session_broker_client_id_t client_id;
if (!parse_client_id(id_text, &client_id)) {
printf("Invalid client ID.\n");
return 1;
}
esp_err_t err = session_broker_disconnect(client_id);
if (err != ESP_OK) {
printf("Could not disconnect client %lu: %s\n", (unsigned long)client_id, esp_err_to_name(err));
return 1;
}
printf("Disconnected client %lu.\n", (unsigned long)client_id);
return 0;
}
static int change_writer(const char *operation, const char *id_text)
{
session_broker_client_id_t client_id;
if (!parse_client_id(id_text, &client_id)) {
printf("Invalid client ID.\n");
return 1;
}
esp_err_t err;
if (strcmp(operation, "request-writer") == 0) {
err = session_broker_request_writer(client_id);
} else {
err = session_broker_release_writer(client_id);
}
if (err != ESP_OK) {
printf("Writer operation failed for client %lu: %s\n",
(unsigned long)client_id, esp_err_to_name(err));
return 1;
}
printf("Writer is now %lu.\n", (unsigned long)session_broker_get_writer_id());
return 0;
}
static int force_writer(const char *id_text)
{
session_broker_client_id_t client_id = SESSION_BROKER_NO_CLIENT;
if (strcmp(id_text, "none") != 0 && !parse_client_id(id_text, &client_id)) {
printf("Writer must be a connected client ID or 'none'.\n");
return 1;
}
esp_err_t err = session_broker_force_writer(client_id);
if (err != ESP_OK) {
printf("Could not force writer: %s\n", esp_err_to_name(err));
return 1;
}
printf("Writer is now %lu.\n", (unsigned long)session_broker_get_writer_id());
return 0;
}
static int hexadecimal_value(char character)
{
if (character >= '0' && character <= '9') {
return character - '0';
}
if (character >= 'a' && character <= 'f') {
return character - 'a' + 10;
}
if (character >= 'A' && character <= 'F') {
return character - 'A' + 10;
}
return -1;
}
static int send_hexadecimal(const char *id_text, const char *text)
{
session_broker_client_id_t client_id;
if (!parse_client_id(id_text, &client_id)) {
printf("Invalid client ID.\n");
return 1;
}
uint8_t data[CONSOLE_HEX_MAX_BYTES];
size_t text_length = strlen(text);
if (text_length == 0 || (text_length % 2) != 0 || text_length > sizeof(data) * 2) {
printf("Provide 1..%d bytes as hexadecimal digits without separators.\n", CONSOLE_HEX_MAX_BYTES);
return 1;
}
size_t data_length = text_length / 2;
for (size_t index = 0; index < data_length; ++index) {
int high = hexadecimal_value(text[index * 2]);
int low = hexadecimal_value(text[index * 2 + 1]);
if (high < 0 || low < 0) {
size_t invalid_index = index * 2 + (high < 0 ? 0 : 1);
printf("Invalid hexadecimal digit at character %u.\n", (unsigned int)invalid_index);
return 1;
}
data[index] = (uint8_t)((high << 4) | low);
}
size_t accepted = 0;
esp_err_t err = session_broker_write(client_id, data, data_length, &accepted);
if (err != ESP_OK) {
printf("Client %lu could not write: %s\n", (unsigned long)client_id, esp_err_to_name(err));
return 1;
}
printf("Broker accepted %u of %u bytes from client %lu.\n",
(unsigned int)accepted,
(unsigned int)data_length,
(unsigned long)client_id);
return accepted == data_length ? 0 : 1;
}
static int read_hexadecimal(int argc, char **argv)
{
session_broker_client_id_t client_id;
if (!parse_client_id(argv[2], &client_id)) {
printf("Invalid client ID.\n");
return 1;
}
uint32_t maximum = CONSOLE_READ_MAX_BYTES;
if (argc == 4 && !parse_u32(argv[3], 1, CONSOLE_READ_MAX_BYTES, &maximum)) {
printf("Read size must be 1..%d bytes.\n", CONSOLE_READ_MAX_BYTES);
return 1;
}
uint8_t data[CONSOLE_READ_MAX_BYTES];
size_t received = 0;
esp_err_t err = session_broker_read(client_id, data, maximum, &received);
if (err != ESP_OK) {
printf("Could not read client %lu: %s\n", (unsigned long)client_id, esp_err_to_name(err));
return 1;
}
printf("Client %lu read %u byte%s",
(unsigned long)client_id,
(unsigned int)received,
received == 1 ? "" : "s");
if (received > 0) {
printf(": ");
for (size_t index = 0; index < received; ++index) {
printf("%02x", data[index]);
}
}
printf("\n");
return 0;
}
static int show_events(const char *id_text)
{
session_broker_client_id_t client_id;
if (!parse_client_id(id_text, &client_id)) {
printf("Invalid client ID.\n");
return 1;
}
session_broker_client_snapshot_t snapshot;
esp_err_t err = session_broker_get_client_snapshot(client_id, &snapshot);
if (err != ESP_OK) {
printf("Unknown client %lu: %s\n", (unsigned long)client_id, esp_err_to_name(err));
return 1;
}
unsigned int count = 0;
session_broker_event_t event;
while (true) {
err = session_broker_pop_event(client_id, &event);
if (err == ESP_ERR_TIMEOUT) {
break;
}
if (err != ESP_OK) {
printf("Could not pop events for client %lu: %s\n",
(unsigned long)client_id, esp_err_to_name(err));
return 1;
}
printf("#%" PRIu64 " %-16s subject=%lu writer=%lu\n",
event.sequence,
event_type_name(event.type),
(unsigned long)event.client_id,
(unsigned long)event.writer_id);
++count;
}
if (count == 0) {
printf("No pending events for client %lu.\n", (unsigned long)client_id);
}
return 0;
}
static int command_broker(int argc, char **argv)
{
if (argc == 1 || (argc == 2 && strcmp(argv[1], "status") == 0)) {
return show_status();
}
if (argc == 2 && strcmp(argv[1], "clients") == 0) {
return show_clients();
}
if (argc == 2 && strcmp(argv[1], "counters") == 0) {
return show_counters();
}
if (argc == 2 && strcmp(argv[1], "clear-counters") == 0) {
esp_err_t err = session_broker_clear_counters();
if (err != ESP_OK) {
printf("Could not clear broker counters: %s\n", esp_err_to_name(err));
return 1;
}
printf("Broker counters cleared.\n");
return 0;
}
if (argc == 3 && strcmp(argv[1], "connect") == 0) {
return connect_client(argv[2]);
}
if (argc == 3 && strcmp(argv[1], "disconnect") == 0) {
return disconnect_client(argv[2]);
}
if (argc == 3 &&
(strcmp(argv[1], "request-writer") == 0 ||
strcmp(argv[1], "release-writer") == 0)) {
return change_writer(argv[1], argv[2]);
}
if (argc == 3 && strcmp(argv[1], "force-writer") == 0) {
return force_writer(argv[2]);
}
if (argc == 4 && strcmp(argv[1], "send-hex") == 0) {
return send_hexadecimal(argv[2], argv[3]);
}
if ((argc == 3 || argc == 4) && strcmp(argv[1], "read") == 0) {
return read_hexadecimal(argc, argv);
}
if (argc == 3 && strcmp(argv[1], "events") == 0) {
return show_events(argv[2]);
}
print_usage();
return 1;
}
esp_err_t session_console_register_commands(void)
{
const esp_console_cmd_t command = {
.command = "broker",
.help = "Manage Phase 2 broker clients, writer ownership, events, and binary data",
.hint = NULL,
.func = &command_broker,
.argtable = NULL,
};
return esp_console_cmd_register(&command);
}