From 89c27b8fca2b7b6aaeb4304becbbacb96cf941da Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 27 Oct 2026 14:00:49 +0100 Subject: [PATCH] Clarify control Action replies and graph display contracts --- tools/etcpmon/etcpmon_graph.h | 16 +++++++++--- tools/etcpmon/etcpmon_gui.h | 45 ++++++++++++++++++++------------ tools/etcpmon/etcpmon_protocol.h | 36 ++++++++++++++----------- 3 files changed, 63 insertions(+), 34 deletions(-) diff --git a/tools/etcpmon/etcpmon_graph.h b/tools/etcpmon/etcpmon_graph.h index 4b280409..ea7ae349 100644 --- a/tools/etcpmon/etcpmon_graph.h +++ b/tools/etcpmon/etcpmon_graph.h @@ -1,5 +1,8 @@ /* - * etcpmon_graph.h - ETCP Monitor Graph Definitions + * Отрисовка восьми BBR-каналов из кольцевой истории etcpmon_client в GUI-потоке. + * GraphWndProc получает etcpmon_app через lpCreateParams при CreateWindowEx; + * история и GDI-перья принадлежат app/client, модуль их не освобождает. + * Один пиксель X соответствует сэмплу; временная шкала неравномерна при задержке ответов. */ #ifndef ETCPMON_GRAPH_H @@ -27,14 +30,21 @@ extern "C" { struct etcpmon_app; struct metrics_history; -/* Graph colors for each metric */ +/* Цвета/короткие имена в порядке GRAPH_METRIC_* из etcpmon_client.h. */ extern const COLORREF graph_colors[GRAPH_METRICS_COUNT]; extern const char* graph_metric_names[GRAPH_METRICS_COUNT]; -/* Graph functions */ +/* WinAPI wndproc: WM_PAINT рисует историю, движение мыши выбирает сэмпл, уход выключает курсор. */ LRESULT CALLBACK GraphWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +/* Нарисовать app в HDC hdc внутри client rect rc через временный off-screen bitmap. + * Менее двух сэмплов — Waiting for data; масштабы видимой части обновляются не чаще 200 мс. */ void DrawGraph(HDC hdc, RECT* rc, struct etcpmon_app* app); +/* Записать индексы отмеченных каналов в selected[GRAPH_METRICS_COUNT], их число — в *count. + * Если галочек нет, выбирает первые четыре канала; для NULL app число равно нулю. */ void GetSelectedChannels(struct etcpmon_app* app, int* selected, int* count); +/* Обновить поля значений app по физическому индексу cursor_sample в кольце; неверный индекс даёт "-". + * Типы/единицы описаны в metrics_history; байты форматируются в B/KiB/MiB, скорости — B/s/KiB/s/MiB/s. + * Сравнивает с текущим текстом контрола, не хранит отдельного кеша и не добавляет сэмплы. */ void UpdateChannelValues(struct etcpmon_app* app, int cursor_sample); #ifdef __cplusplus diff --git a/tools/etcpmon/etcpmon_gui.h b/tools/etcpmon/etcpmon_gui.h index 1a9df3c0..fbfa1e86 100644 --- a/tools/etcpmon/etcpmon_gui.h +++ b/tools/etcpmon/etcpmon_gui.h @@ -1,5 +1,9 @@ /* - * etcpmon_gui.h - ETCP Monitor GUI Header + * WinAPI GUI etcpmon: init(app, instance) → create_window → run → cleanup. + * Caller владеет app; все вызовы, Winsock-клиент и callbacks работают в одном GUI-потоке. + * init регистрирует классы окон и запускает Winsock; create_window выделяет client, создаёт + * контролы/таймер 10 мс и сразу начинает connect к 192.168.40.1:9091. run показывает окно. + * GUI опрашивает списки раз в секунду; метрики автоматически запрашивает client раз в 100 мс. */ #ifndef ETCPMON_GUI_H @@ -213,7 +217,8 @@ extern "C" { #define IDC_CH_VALUE_7 527 #define IDC_CH_CHECK_7 537 -/* Main application structure */ +/* Состояние одного приложения; HWND контролов принадлежат окнам, перья и client — app. + * Порядок hChannel* совпадает с GRAPH_METRIC_*; интерфейс не предназначен для нескольких app одновременно. */ struct etcpmon_app { HINSTANCE hInstance; HWND hWndMain; @@ -375,53 +380,61 @@ struct etcpmon_app { HWND hDebugCatRadio[IDC_DEBUG_MAX_CAT][6]; /* [row][level] радио-кнопки */ int debug_cat_count; - /* Client state */ + /* Выделенный create_window клиент; cleanup освобождает его и завершает Winsock. */ struct etcpmon_client* client; - int need_initial_request; /* 1 = нужно запросить список при первом таймере */ + int need_initial_request; /* 1 = после готовности TCP запросить списки и debug config */ uint64_t last_selected_peer_id; /* чтобы не спамить SELECT_CONN каждый тик */ /* Timer */ UINT_PTR updateTimer; - DWORD last_poll_time; - int isConnected; + DWORD last_poll_time; /* GetTickCount последнего опроса списков; период 1000 мс */ + int isConnected; /* Включает CONNECTING; блокирует редактирование адреса */ }; -/* Initialize application */ +/* Обнулить caller-owned app, зарегистрировать классы с hInstance, запустить Winsock. + * 0 = успех, -1 = ошибка; основное окно и TCP-клиент ещё не созданы. */ int etcpmon_gui_init(struct etcpmon_app* app, HINSTANCE hInstance); -/* Create main window */ +/* Создать главное окно, контролы, client и таймер; начать connect к адресу по умолчанию. + * Возвращает HWND (также app->hWndMain) или NULL при ошибке создания окна. Показывает окно run. */ HWND etcpmon_gui_create_window(struct etcpmon_app* app); -/* Run message loop */ +/* Показать app->hWndMain и обрабатывать сообщения/сеть до WM_QUIT. + * Возвращает wParam сообщения выхода, -1 для отсутствующего app/окна. */ int etcpmon_gui_run(struct etcpmon_app* app); -/* Cleanup */ +/* После завершения run освободить tooltip, перья, client и Winsock; сам app не освобождает. */ void etcpmon_gui_cleanup(struct etcpmon_app* app); -/* Update connection status display */ +/* Скопировать text в status bar app; состояние соединения не меняет. */ void etcpmon_gui_set_status(struct etcpmon_app* app, const char* text); -/* Update connection list */ +/* Показать list[count], сохранить выбор по last_selected_peer_id или выбрать первую запись. + * list заимствован на время вызова, count=0 очищает список; SELECT здесь не отправляется. */ void etcpmon_gui_update_conn_list(struct etcpmon_app* app, struct etcpmon_conn_info* list, uint8_t count); -/* Update socket list */ +/* Показать list[count] локальных сокетов; пустые name/ip пропускаются. Данные заимствованы на время вызова. */ void etcpmon_gui_update_socket_list(struct etcpmon_app* app, struct etcpmon_socket_info* list, uint8_t count); -/* Update metrics display */ +/* Показать metrics и links[links_count], перерисовать график из уже обновлённой истории client. + * Входные снимки заимствованы на время вызова; новый сэмпл в историю не добавляет. */ void etcpmon_gui_update_metrics(struct etcpmon_app* app, struct etcpmon_rsp_metrics* metrics, struct etcpmon_link_metrics* links, uint8_t links_count); -/* Clear metrics display */ +/* Очистить поля/линки/график и освободить кеши метрик client, обнулить его историю. + * Транспорт, selected_peer_id и pending не меняет; не вызывать с используемыми далее указателями на кеш. + * Повторный вывод прежних величин после очистки обновляет сами контролы, отдельного кеша текста нет. */ void etcpmon_gui_clear_metrics(struct etcpmon_app* app); -/* Update graph with new metrics */ +/* Запросить перерисовку графика app и обновить значения последнего сэмпла при неактивном курсоре. + * metrics служит проверкой ненулевого аргумента; данные берутся из истории client, сэмпл не добавляется. */ void etcpmon_gui_update_graph(struct etcpmon_app* app, struct etcpmon_rsp_metrics* metrics); diff --git a/tools/etcpmon/etcpmon_protocol.h b/tools/etcpmon/etcpmon_protocol.h index 7c9abd10..fd106713 100644 --- a/tools/etcpmon/etcpmon_protocol.h +++ b/tools/etcpmon/etcpmon_protocol.h @@ -1,10 +1,14 @@ /* - * etcpmon_protocol.h - ETCP Monitor Protocol Definitions - * - * Binary protocol for monitoring ETCP connections - * Format: [2 bytes: msg_size] [1 byte: msg_type] [payload...] - * - * Period: 100ms (10 updates per second) + * Общий формат TCP control_server для etcpmon и других локальных клиентов управления. + * Кадр: [size:uint16][type:uint8][seq_id:uint8][payload]; size включает 4-байтовый заголовок, + * допустимо 4..32768 байт. Структуры packed(1), числа копируются в порядке байтов хоста + * без htons/ntohs; взаимодействующие сборки должны иметь одинаковый layout и endianness. + * VERSION — константа исходников, по сети не передаётся; согласования версии нет. + * TCP не сохраняет границы кадров. Метрики выдаются по запросу; 100 мс — интервал клиента. + * seq=0 не связывается с pending; ответ на запрос копирует его seq (кроме отдельных NODE_INFO). + * SELECT_CONN/SET_DEBUG_CONFIG/DISCONNECT не подтверждаются; ACTION даёт ACTION_RESULT либо ERROR. + * Подписка на узлы: NODE_INFO с seq=0, затем NODE_INFO_END с seq запроса; обновления также с seq=0. + * Встроенного шифрования/аутентификации нет; сервер допускает IPv4 по [control] allow. */ #ifndef ETCPMON_PROTOCOL_H @@ -92,12 +96,12 @@ struct etcpmon_msg_header { /* CMD_SELECT_CONN: Select connection to monitor */ struct etcpmon_cmd_select { - uint64_t peer_node_id; /* peer_node_id of connection to monitor */ + uint64_t peer_node_id; /* ID выбранного соединения; 0 снимает выбор, SELECT не подтверждается */ }; /* CMD_ACTION: Execute action command */ struct etcpmon_cmd_action { - char action[32]; /* Null-terminated action string */ + char action[32]; /* Ровно 32 байта с NUL; полное имя "nat"/"nodes", неизвестное имя даёт ERROR */ }; /* CMD_GET_METRICS: No payload required (uses previously selected connection) */ @@ -231,7 +235,7 @@ struct etcpmon_link_metrics { uint32_t bandwidth; /* Configured bandwidth (Kbps) */ uint32_t nat_changes_count; /* NAT address changes */ uint32_t rtt_last; /* Last RTT (0.1ms units) */ - uint32_t rtt_avg10; /* avg RTT (0.1ms units) */ + uint32_t rtt_avg10; /* Сейчас BBR min_rtt_us / 100 (0.1 мс), 0 без BBR; не среднее 10 RTT */ uint32_t tt_last; /* Last transmit time (0.1ms units) */ /* Timer flags (1 = active, 0 = NULL) */ @@ -293,9 +297,9 @@ struct etcpmon_tun_metrics { /* Router congestion metrics — aggregated over all router_conns */ struct etcpmon_router_metrics { uint32_t total_conns; - uint32_t total_inflight; - uint32_t total_send_q; - uint32_t total_recv_q; + uint32_t total_inflight; /* Сумма числа записей inflight_q, не байтов */ + uint32_t total_send_q; /* Сумма числа записей send_q */ + uint32_t total_recv_q; /* Сумма числа записей recv_q */ uint32_t pkts_sent; uint32_t pkts_send_err; uint32_t pkts_rcvd; @@ -322,7 +326,7 @@ struct etcpmon_rsp_metrics { struct etcpmon_rsp_debug_config { uint8_t global_level; /* Global debug level (0=NONE..5=TRACE) */ uint8_t category_count; /* Number of categories */ - /* Followed by: char names_csv[]; null-terminated CSV: "UASYNC,LL_QUEUE,...\0" */ + /* Followed by: char names_csv[]; NUL-terminated CSV категорий 1..N из debug_config */ /* Followed by: uint8_t levels[category_count]; per-category levels */ }; @@ -378,7 +382,8 @@ struct etcpmon_rsp_error { * Utility Functions (optional, for both client and server) * ============================================================================ */ -/* Build message header */ +/* Заполнить hdr из размера payload, типа и seq. Границы не проверяет: + * вызывающий код обязан обеспечить payload_size <= ETCPMON_MAX_MSG_SIZE - sizeof(*hdr). */ static inline void etcpmon_build_header(struct etcpmon_msg_header* hdr, uint16_t payload_size, uint8_t msg_type, @@ -388,7 +393,8 @@ static inline void etcpmon_build_header(struct etcpmon_msg_header* hdr, hdr->seq_id = seq_id; } -/* Validate message header */ +/* Проверить только диапазон hdr->size; 0 = допустим, -1 = ошибка. + * hdr должен указывать на полный заголовок; наличие payload/тип/seq эта функция не проверяет. */ static inline int etcpmon_validate_header(const struct etcpmon_msg_header* hdr) { if (hdr->size < sizeof(struct etcpmon_msg_header) || hdr->size > ETCPMON_MAX_MSG_SIZE) {