#include "session_console.h" #include #include #include #include #include #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 \n"); printf(" broker disconnect \n"); printf(" broker request-writer \n"); printf(" broker release-writer \n"); printf(" broker force-writer \n"); printf(" broker send-hex \n"); printf(" broker read [maximum-bytes]\n"); printf(" broker events \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); 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); }