Browse Source

Refresh etcpmon connections and preserve selection by peer ID

master
evgeny 1 week ago
parent
commit
24906283fe
  1. 2
      tools/etcpmon/etcpmon_client.c
  2. 6
      tools/etcpmon/etcpmon_doc.md
  3. 117
      tools/etcpmon/etcpmon_gui.c

2
tools/etcpmon/etcpmon_client.c

@ -927,6 +927,7 @@ int etcpmon_client_process(struct etcpmon_client* client) {
} }
handle_response_with_callback(client, hdr->type, payload, payload_size); handle_response_with_callback(client, hdr->type, payload, payload_size);
if (!client->connected) return -1;
processed++; processed++;
/* Удаляем обработанное сообщение */ /* Удаляем обработанное сообщение */
@ -934,7 +935,6 @@ int etcpmon_client_process(struct etcpmon_client* client) {
memmove(client->recv_buffer, client->recv_buffer + total_size, memmove(client->recv_buffer, client->recv_buffer + total_size,
client->recv_len - total_size); client->recv_len - total_size);
} }
if (!client->connected) return -1;
client->recv_len -= total_size; client->recv_len -= total_size;
} }

6
tools/etcpmon/etcpmon_doc.md

@ -4,6 +4,12 @@
Windows GUI монитор для ETCP соединений uTun в реальном времени (100ms refresh). Подключается по TCP к control_server внутри uTun и отображает метрики соединений, линков, TUN-интерфейса, роутера и дебаг-уровней. Позволяет менять debug levels на лету через GUI. Windows GUI монитор для ETCP соединений uTun в реальном времени (100ms refresh). Подключается по TCP к control_server внутри uTun и отображает метрики соединений, линков, TUN-интерфейса, роутера и дебаг-уровней. Позволяет менять debug levels на лету через GUI.
Connections и локальные сокеты опрашиваются раз в секунду, даже при пустом списке соединений.
Выбор сохраняется по ID узла при изменении порядка и имён. При удалении выбранного соединения
выбирается первое доступное; пустой список или отключение очищает метрики и график.
Одновременно ожидается не более одного ответа на автоматический запрос метрик. Таймаут ответа
на запрос с seq_id — 5 секунд; причина отключения записывается в `etcpmon.log`.
## 2. Как пользоваться ## 2. Как пользоваться
### Типовой сценарий ### Типовой сценарий

117
tools/etcpmon/etcpmon_gui.c

@ -22,7 +22,8 @@
#define WINDOW_WIDTH 1000 #define WINDOW_WIDTH 1000
#define WINDOW_HEIGHT 1400 #define WINDOW_HEIGHT 1400
#define WINDOW_CLIENT_W 1250 #define WINDOW_CLIENT_W 1250
#define UPDATE_INTERVAL 10 /* 50ms → 20 samples per second */ #define UPDATE_INTERVAL 10
#define LIST_UPDATE_INTERVAL 1000
/* Global app pointer for callbacks */ /* Global app pointer for callbacks */
static struct etcpmon_app* g_app = NULL; static struct etcpmon_app* g_app = NULL;
/* Forward declarations */ /* Forward declarations */
@ -1121,8 +1122,8 @@ static void OnConnect(struct etcpmon_app* app) {
if (etcpmon_client_connect(app->client, addr, port) == 0) { if (etcpmon_client_connect(app->client, addr, port) == 0) {
app->isConnected = 1; app->isConnected = 1;
app->need_initial_request = 1;
UpdateUIState(app); UpdateUIState(app);
etcpmon_gui_set_status(app, "Connected");
} else { } else {
etcpmon_gui_set_status(app, "Connection failed"); etcpmon_gui_set_status(app, "Connection failed");
} }
@ -1131,11 +1132,7 @@ static void OnDisconnect(struct etcpmon_app* app) {
if (!app || !app->client) return; if (!app || !app->client) return;
etcpmon_client_disconnect(app->client); etcpmon_client_disconnect(app->client);
app->isConnected = 0; on_disconnected(app);
app->need_initial_request = 1;
app->last_selected_peer_id = 0;
UpdateUIState(app);
etcpmon_gui_set_status(app, "Disconnected");
} }
static void OnConnectionSelect(struct etcpmon_app* app) { static void OnConnectionSelect(struct etcpmon_app* app) {
@ -1144,14 +1141,19 @@ static void OnConnectionSelect(struct etcpmon_app* app) {
int idx = (int)SendMessage(app->hListConnections, LB_GETCURSEL, 0, 0); int idx = (int)SendMessage(app->hListConnections, LB_GETCURSEL, 0, 0);
if (idx == LB_ERR) return; if (idx == LB_ERR) return;
uint64_t peer_id = (uint64_t)SendMessage(app->hListConnections, LB_GETITEMDATA, idx, 0); LRESULT list_idx = SendMessage(app->hListConnections, LB_GETITEMDATA, idx, 0);
if (list_idx < 0 || list_idx >= app->client->conn_count) return;
uint64_t peer_id = app->client->conn_list[list_idx].peer_node_id;
if (peer_id == 0) return; if (peer_id == 0) return;
/* Только если действительно изменилось — иначе спамим сервер */ /* Только если действительно изменилось — иначе спамим сервер */
if (peer_id != app->last_selected_peer_id) { if (peer_id != app->last_selected_peer_id) {
if (etcpmon_client_select_connection(app->client, peer_id) != 0) {
OnDisconnect(app);
etcpmon_gui_set_status(app, "Failed to select connection");
return;
}
app->last_selected_peer_id = peer_id; app->last_selected_peer_id = peer_id;
etcpmon_client_select_connection(app->client, peer_id);
etcpmon_gui_clear_metrics(app); /* очищаем старые цифры */ etcpmon_gui_clear_metrics(app); /* очищаем старые цифры */
etcpmon_gui_set_status(app, "Connection selected"); etcpmon_gui_set_status(app, "Connection selected");
@ -1264,9 +1266,7 @@ static void RefreshDebugUI(struct etcpmon_app* app) {
static void OnTimer(struct etcpmon_app* app) { static void OnTimer(struct etcpmon_app* app) {
if (!app || !app->client) return; if (!app || !app->client) return;
if (!app->client->connected) return;
// fprintf(app->client->log_file, "Timer... \n");
// fflush(app->client->log_file);
int processed = etcpmon_client_process(app->client); int processed = etcpmon_client_process(app->client);
if (processed < 0) { if (processed < 0) {
@ -1274,10 +1274,17 @@ static void OnTimer(struct etcpmon_app* app) {
return; return;
} }
if (app->need_initial_request && app->isConnected) { if (!app->client->fully_connected) return;
etcpmon_client_request_list(app->client);
etcpmon_client_request_socket_list(app->client); DWORD now = GetTickCount();
etcpmon_client_request_debug_config(app->client); if (app->need_initial_request || now - app->last_poll_time >= LIST_UPDATE_INTERVAL) {
if (etcpmon_client_request_list(app->client) != 0 || etcpmon_client_request_socket_list(app->client) != 0 ||
(app->need_initial_request && etcpmon_client_request_debug_config(app->client) != 0)) {
OnDisconnect(app);
etcpmon_gui_set_status(app, "Failed to request monitor data");
return;
}
app->last_poll_time = now;
app->need_initial_request = 0; app->need_initial_request = 0;
} }
@ -1324,6 +1331,12 @@ void etcpmon_gui_update_graph(struct etcpmon_app* app, struct etcpmon_rsp_metric
static void on_disconnected(void* user_data) { static void on_disconnected(void* user_data) {
struct etcpmon_app* app = (struct etcpmon_app*)user_data; struct etcpmon_app* app = (struct etcpmon_app*)user_data;
app->isConnected = 0; app->isConnected = 0;
app->need_initial_request = 1;
app->last_selected_peer_id = 0;
app->last_poll_time = 0;
SendMessage(app->hListConnections, LB_RESETCONTENT, 0, 0);
SendMessage(app->hListSockets, LB_RESETCONTENT, 0, 0);
etcpmon_gui_clear_metrics(app);
UpdateUIState(app); UpdateUIState(app);
etcpmon_gui_set_status(app, "Disconnected"); etcpmon_gui_set_status(app, "Disconnected");
} }
@ -1333,17 +1346,16 @@ static void on_conn_list(struct etcpmon_conn_info* list, uint8_t count, void* us
etcpmon_gui_update_conn_list(app, list, count); etcpmon_gui_update_conn_list(app, list, count);
/* Автоматически выбираем первое соединение (только один раз) */ if (count > 0) {
if (count > 0 && app->last_selected_peer_id == 0) { OnConnectionSelect(app);
uint64_t first_id = list[0].peer_node_id; } else if (app->last_selected_peer_id != 0) {
app->last_selected_peer_id = first_id; if (etcpmon_client_select_connection(app->client, 0) != 0) {
OnDisconnect(app);
etcpmon_client_select_connection(app->client, first_id); return;
}
/* Выделяем первую строку в списке */ app->last_selected_peer_id = 0;
SendMessage(app->hListConnections, LB_SETCURSEL, 0, 0); etcpmon_gui_clear_metrics(app);
etcpmon_gui_set_status(app, "No connections");
etcpmon_gui_set_status(app, "Auto-selected first connection");
} }
} }
@ -1358,7 +1370,6 @@ static void on_metrics(struct etcpmon_rsp_metrics* metrics,
struct etcpmon_app* app = (struct etcpmon_app*)user_data; struct etcpmon_app* app = (struct etcpmon_app*)user_data;
if (app) { if (app) {
etcpmon_gui_update_metrics(app, metrics, links, links_count); etcpmon_gui_update_metrics(app, metrics, links, links_count);
etcpmon_client_add_to_history(app->client, metrics);
if (!app->graph_cursor_active) { if (!app->graph_cursor_active) {
struct metrics_history* h = etcpmon_client_get_history(app->client); struct metrics_history* h = etcpmon_client_get_history(app->client);
if (h && h->count > 0) { if (h && h->count > 0) {
@ -1522,7 +1533,24 @@ void etcpmon_gui_update_conn_list(struct etcpmon_app* app,
uint8_t count) { uint8_t count) {
if (!app || !app->hListConnections) return; if (!app || !app->hListConnections) return;
/* Clear existing items */ int selected = count ? 0 : -1;
int changed = SendMessage(app->hListConnections, LB_GETCOUNT, 0, 0) != count;
for (uint8_t i = 0; i < count; i++) {
if (list[i].peer_node_id == app->last_selected_peer_id) selected = i;
char display[64], previous[64];
snprintf(display, sizeof(display), "%s (%016llX)", list[i].name, (unsigned long long)list[i].peer_node_id);
if (!changed && (SendMessage(app->hListConnections, LB_GETTEXTLEN, i, 0) >= (LRESULT)sizeof(previous) ||
SendMessageA(app->hListConnections, LB_GETTEXT, i, (LPARAM)previous) == LB_ERR || strcmp(display, previous) != 0)) {
changed = 1;
}
}
if (!changed) {
SendMessage(app->hListConnections, LB_SETCURSEL, selected, 0);
return;
}
int top = (int)SendMessage(app->hListConnections, LB_GETTOPINDEX, 0, 0);
SendMessage(app->hListConnections, WM_SETREDRAW, FALSE, 0);
SendMessage(app->hListConnections, LB_RESETCONTENT, 0, 0); SendMessage(app->hListConnections, LB_RESETCONTENT, 0, 0);
/* Add new items */ /* Add new items */
@ -1532,10 +1560,22 @@ void etcpmon_gui_update_conn_list(struct etcpmon_app* app,
list[i].name, (unsigned long long)list[i].peer_node_id); list[i].name, (unsigned long long)list[i].peer_node_id);
int idx = (int)SendMessageA(app->hListConnections, LB_ADDSTRING, 0, (LPARAM)display); int idx = (int)SendMessageA(app->hListConnections, LB_ADDSTRING, 0, (LPARAM)display);
if (idx != LB_ERR) { if (idx != LB_ERR && idx != LB_ERRSPACE) {
SendMessage(app->hListConnections, LB_SETITEMDATA, idx, (LPARAM)list[i].peer_node_id); /* Индекс сохраняет полный 64-битный ID и в 32-битной Windows. */
SendMessage(app->hListConnections, LB_SETITEMDATA, idx, i);
} else if (app->client && app->client->log_file) {
fprintf(app->client->log_file, "[ERROR] Failed to add connection row: index=%u result=%d\n", i, idx);
} }
} }
SendMessage(app->hListConnections, LB_SETCURSEL, selected, 0);
if (top >= 0 && top < count) SendMessage(app->hListConnections, LB_SETTOPINDEX, top, 0);
SendMessage(app->hListConnections, WM_SETREDRAW, TRUE, 0);
InvalidateRect(app->hListConnections, NULL, TRUE);
if (app->client && app->client->log_file) {
fprintf(app->client->log_file, "[LOG] Connections updated: count=%u selected_row=%d previous_peer=%016llX\n",
count, selected, (unsigned long long)app->last_selected_peer_id);
fflush(app->client->log_file);
}
} }
static const char* config_type_to_string(uint8_t config_type) { static const char* config_type_to_string(uint8_t config_type) {
@ -1565,7 +1605,7 @@ static const char* nat_type_to_string(uint8_t nat_type) {
void etcpmon_gui_update_socket_list(struct etcpmon_app* app, void etcpmon_gui_update_socket_list(struct etcpmon_app* app,
struct etcpmon_socket_info* list, struct etcpmon_socket_info* list,
uint8_t count) { uint8_t count) {
if (!app || !app->hListSockets || !list || count == 0 || count > 32) return; if (!app || !app->hListSockets) return;
SendMessage(app->hListSockets, LB_RESETCONTENT, 0, 0); SendMessage(app->hListSockets, LB_RESETCONTENT, 0, 0);
@ -1917,4 +1957,15 @@ void etcpmon_gui_clear_metrics(struct etcpmon_app* app) {
if (app->hListLinks) { if (app->hListLinks) {
SendMessage(app->hListLinks, LB_RESETCONTENT, 0, 0); SendMessage(app->hListLinks, LB_RESETCONTENT, 0, 0);
} }
if (app->client) {
memset(&app->client->history, 0, sizeof(app->client->history));
free(app->client->last_metrics);
app->client->last_metrics = NULL;
free(app->client->last_links);
app->client->last_links = NULL;
app->client->last_links_count = 0;
}
app->graph_cursor_active = 0;
for (int i = 0; i < GRAPH_METRICS_COUNT; i++) SetWindowTextA(app->hChannelValue[i], "");
if (app->hGraphWnd) InvalidateRect(app->hGraphWnd, NULL, TRUE);
} }

Loading…
Cancel
Save