Browse Source

monitor to new protocol

nodeinfo-routing-update
jeka 3 weeks ago
parent
commit
92a797ab66
  1. 37
      src/control_server.c
  2. 2
      src/etcp.c
  3. 212
      tools/etcpmon/etcpmon_client.c
  4. 29
      tools/etcpmon/etcpmon_client.h
  5. 106
      tools/etcpmon/etcpmon_gui.c
  6. 3
      tools/etcpmon/etcpmon_gui.h
  7. 8
      tools/etcpmon/etcpmon_protocol.h

37
src/control_server.c

@ -86,9 +86,9 @@ static void client_except_callback(socket_t fd, void* arg);
static void handle_client_data(struct control_server* server, struct control_client* client);
static void close_client(struct control_server* server, struct control_client* client);
static void send_conn_list(struct control_server* server, struct control_client* client);
static void send_metrics(struct control_server* server, struct control_client* client);
static void send_error(struct control_client* client, uint8_t error_code, const char* msg);
static void send_conn_list(struct control_server* server, struct control_client* client, uint8_t seq_id);
static void send_metrics(struct control_server* server, struct control_client* client, uint8_t seq_id);
static void send_error(struct control_client* client, uint8_t error_code, const char* msg, uint8_t seq_id);
static struct ETCP_CONN* find_connection_by_peer_id(struct UTUN_INSTANCE* instance, uint64_t peer_id);
@ -565,15 +565,17 @@ static void handle_client_data(struct control_server* server, struct control_cli
uint8_t* payload = client->recv_buffer + sizeof(struct etcpmon_msg_header);
uint16_t payload_size = hdr->size - sizeof(struct etcpmon_msg_header);
uint8_t req_seq = hdr->seq_id;
if (server->log_file) {
fprintf(server->log_file, "%llu: [LOG] Received command type=0x%02X size=%d\n",
(unsigned long long)get_timestamp_ms(), hdr->type, hdr->size);
fprintf(server->log_file, "%llu: [LOG] Received command type=0x%02X seq=%d size=%d\n",
(unsigned long long)get_timestamp_ms(), hdr->type, req_seq, hdr->size);
fflush(server->log_file);
}
switch (hdr->type) {
case ETCPMON_CMD_LIST_CONN:
send_conn_list(server, client);
send_conn_list(server, client, req_seq);
break;
case ETCPMON_CMD_SELECT_CONN:
@ -593,9 +595,9 @@ static void handle_client_data(struct control_server* server, struct control_cli
case ETCPMON_CMD_GET_METRICS:
if (client->selected_peer_id == 0) {
send_error(client, ETCPMON_ERR_NO_CONN_SELECTED,
"No connection selected");
"No connection selected", req_seq);
} else {
send_metrics(server, client);
send_metrics(server, client, req_seq);
}
break;
@ -615,7 +617,7 @@ static void handle_client_data(struct control_server* server, struct control_cli
fflush(server->log_file);
}
DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "Unknown command from client: 0x%02X", hdr->type);
send_error(client, ETCPMON_ERR_INVALID_CMD, "Unknown command");
send_error(client, ETCPMON_ERR_INVALID_CMD, "Unknown command", req_seq);
break;
}
@ -633,7 +635,7 @@ static void handle_client_data(struct control_server* server, struct control_cli
* Response Builders
* ============================================================================ */
static void send_conn_list(struct control_server* server, struct control_client* client) {
static void send_conn_list(struct control_server* server, struct control_client* client, uint8_t seq_id) {
struct UTUN_INSTANCE* instance = server->instance;
/* Count connections */
@ -655,7 +657,8 @@ static void send_conn_list(struct control_server* server, struct control_client*
struct etcpmon_msg_header* hdr = (struct etcpmon_msg_header*)buffer;
etcpmon_build_header(hdr, sizeof(struct etcpmon_rsp_conn_list) +
count * sizeof(struct etcpmon_conn_info),
ETCPMON_RSP_CONN_LIST);
ETCPMON_RSP_CONN_LIST,
seq_id);
struct etcpmon_rsp_conn_list* rsp = (struct etcpmon_rsp_conn_list*)(buffer + sizeof(*hdr));
rsp->count = count;
@ -686,13 +689,13 @@ static void send_conn_list(struct control_server* server, struct control_client*
u_free(buffer);
}
static void send_metrics(struct control_server* server, struct control_client* client) {
static void send_metrics(struct control_server* server, struct control_client* client, uint8_t seq_id) {
struct UTUN_INSTANCE* instance = server->instance;
/* Find selected connection */
struct ETCP_CONN* conn = find_connection_by_peer_id(instance, client->selected_peer_id);
if (!conn) {
send_error(client, ETCPMON_ERR_INVALID_CONN, "Connection not found");
send_error(client, ETCPMON_ERR_INVALID_CONN, "Connection not found", seq_id);
return;
}
@ -715,7 +718,8 @@ static void send_metrics(struct control_server* server, struct control_client* c
struct etcpmon_msg_header* hdr = (struct etcpmon_msg_header*)buffer;
etcpmon_build_header(hdr, sizeof(struct etcpmon_rsp_metrics) +
links_count * sizeof(struct etcpmon_link_metrics),
ETCPMON_RSP_METRICS);
ETCPMON_RSP_METRICS,
seq_id);
struct etcpmon_rsp_metrics* rsp = (struct etcpmon_rsp_metrics*)(buffer + sizeof(*hdr));
@ -885,7 +889,7 @@ static void send_metrics(struct control_server* server, struct control_client* c
u_free(buffer);
}
static void send_error(struct control_client* client, uint8_t error_code, const char* msg) {
static void send_error(struct control_client* client, uint8_t error_code, const char* msg, uint8_t seq_id) {
struct control_server* server = client->server;
uint16_t msg_len = (uint16_t)strlen(msg);
uint16_t rsp_size = ETCPMON_ERROR_SIZE(msg_len);
@ -895,7 +899,8 @@ static void send_error(struct control_client* client, uint8_t error_code, const
struct etcpmon_msg_header* hdr = (struct etcpmon_msg_header*)buffer;
etcpmon_build_header(hdr, sizeof(struct etcpmon_rsp_error) + msg_len + 1,
ETCPMON_RSP_ERROR);
ETCPMON_RSP_ERROR,
seq_id);
struct etcpmon_rsp_error* rsp = (struct etcpmon_rsp_error*)(buffer + sizeof(*hdr));
rsp->error_code = error_code;

2
src/etcp.c

@ -531,7 +531,7 @@ static void ack_timeout_check(struct ETCP_CONN* etcp) {
// Remove from wait_ack
pkt=(struct INFLIGHT_PACKET*)queue_data_get(etcp->input_wait_ack);
if (!pkt) break;
// Increment counters
pkt->send_count++;
pkt->retrans_req_count++; // Optional, if used for retrans request logic

212
tools/etcpmon/etcpmon_client.c

@ -47,12 +47,16 @@ void etcpmon_client_init(struct etcpmon_client* client) {
client->connected = 0;
client->fully_connected = 0;
client->log_file = NULL;
client->next_seq_id = 1; /* Start from 1, 0 = broadcast */
memset(&client->history, 0, sizeof(client->history));
for (int i = 0; i < GRAPH_METRICS_COUNT; i++) {
for (int i = 0; i < GRAPH_METRIC_COUNT; i++) {
client->history.min_val[i] = 1e9f;
client->history.max_val[i] = -1e9f;
}
client->history.last_minmax_update = 0; // ← добавь
client->history.last_minmax_update = 0;
/* Новый таймер авто-запроса метрик */
client->last_metrics_request_ms = 0;
}
void etcpmon_client_cleanup(struct etcpmon_client* client) {
@ -60,6 +64,16 @@ void etcpmon_client_cleanup(struct etcpmon_client* client) {
etcpmon_client_disconnect(client);
/* Free pending requests */
struct etcpmon_pending_request* req = client->pending_head;
while (req) {
struct etcpmon_pending_request* next = req->next;
free(req);
req = next;
}
client->pending_head = NULL;
client->pending_tail = NULL;
/* Close log file */
if (client->log_file) {
fprintf(client->log_file, "%llu: [LOG] Log closed\n",
@ -183,7 +197,7 @@ void etcpmon_client_disconnect(struct etcpmon_client* client) {
/* Send disconnect command */
if (client->connected) {
struct etcpmon_msg_header hdr;
etcpmon_build_header(&hdr, 0, ETCPMON_CMD_DISCONNECT);
etcpmon_build_header(&hdr, 0, ETCPMON_CMD_DISCONNECT, 0);
if (client->log_file) {
log_hex_data(client->log_file, "TX", (const uint8_t*)&hdr, sizeof(hdr));
@ -218,11 +232,11 @@ int etcpmon_client_request_list(struct etcpmon_client* client) {
if (!client || client->sock == INVALID_SOCKET) return -1;
struct etcpmon_msg_header hdr;
etcpmon_build_header(&hdr, 0, ETCPMON_CMD_LIST_CONN);
etcpmon_build_header(&hdr, 0, ETCPMON_CMD_LIST_CONN, 0); /* seq=0 = legacy режим */
if (client->log_file) {
log_hex_data(client->log_file, "TX", (const uint8_t*)&hdr, sizeof(hdr));
fprintf(client->log_file, "%llu: [LOG] CMD_LIST_CONN\n",
fprintf(client->log_file, "%llu: [LOG] CMD_LIST_CONN (seq=0)\n",
(unsigned long long)get_timestamp_ms());
fflush(client->log_file);
}
@ -243,11 +257,13 @@ int etcpmon_client_request_list(struct etcpmon_client* client) {
int etcpmon_client_select_connection(struct etcpmon_client* client, uint64_t peer_node_id) {
if (!client || client->sock == INVALID_SOCKET) return -1;
/* SELECT is special - it's not a request/response, it just sets state
* So we send with seq_id=0 (broadcast) */
uint8_t buffer[sizeof(struct etcpmon_msg_header) + sizeof(struct etcpmon_cmd_select)];
struct etcpmon_msg_header* hdr = (struct etcpmon_msg_header*)buffer;
struct etcpmon_cmd_select* cmd = (struct etcpmon_cmd_select*)(buffer + sizeof(*hdr));
etcpmon_build_header(hdr, sizeof(*cmd), ETCPMON_CMD_SELECT_CONN);
etcpmon_build_header(hdr, sizeof(*cmd), ETCPMON_CMD_SELECT_CONN, 0); /* seq_id=0 for SELECT */
cmd->peer_node_id = peer_node_id;
if (client->log_file) {
@ -275,7 +291,7 @@ int etcpmon_client_request_metrics(struct etcpmon_client* client) {
if (!client || client->sock == INVALID_SOCKET) return -1;
struct etcpmon_msg_header hdr;
etcpmon_build_header(&hdr, 0, ETCPMON_CMD_GET_METRICS);
etcpmon_build_header(&hdr, 0, ETCPMON_CMD_GET_METRICS, 0);
if (client->log_file) {
log_hex_data(client->log_file, "TX", (const uint8_t*)&hdr, sizeof(hdr));
@ -297,6 +313,82 @@ int etcpmon_client_request_metrics(struct etcpmon_client* client) {
return 0;
}
/* Send request with callback - queued version */
int etcpmon_client_send_request(struct etcpmon_client* client,
uint8_t type,
const void* payload, uint16_t payload_size,
void (*callback)(void* arg, uint8_t msg_type, const uint8_t* payload, uint16_t payload_size),
void* arg) {
if (!client || client->sock == INVALID_SOCKET || !callback) return -1;
/* Allocate pending request */
struct etcpmon_pending_request* req = (struct etcpmon_pending_request*)malloc(sizeof(*req));
if (!req) {
if (client->log_file) {
fprintf(client->log_file, "%llu: [ERROR] Failed to allocate pending request\n",
(unsigned long long)get_timestamp_ms());
fflush(client->log_file);
}
return -1;
}
/* Assign sequence ID */
req->seq_id = client->next_seq_id++;
if (client->next_seq_id == 0) client->next_seq_id = 1; /* Skip 0 (broadcast) */
req->callback = callback;
req->arg = arg;
req->next = NULL;
/* Add to queue */
if (client->pending_tail) {
client->pending_tail->next = req;
} else {
client->pending_head = req;
}
client->pending_tail = req;
/* Build message with seq_id */
uint8_t buffer[sizeof(struct etcpmon_msg_header) + ETCPMON_MAX_MSG_SIZE];
if (payload_size > ETCPMON_MAX_MSG_SIZE) {
free(req);
return -1;
}
struct etcpmon_msg_header* hdr = (struct etcpmon_msg_header*)buffer;
etcpmon_build_header(hdr, payload_size, type, req->seq_id);
if (payload && payload_size > 0) {
memcpy(buffer + sizeof(*hdr), payload, payload_size);
}
uint16_t total_size = sizeof(*hdr) + payload_size;
if (client->log_file) {
log_hex_data(client->log_file, "TX", buffer, total_size);
fprintf(client->log_file, "%llu: [LOG] CMD 0x%02X seq=%d queued\n",
(unsigned long long)get_timestamp_ms(), type, req->seq_id);
fflush(client->log_file);
}
int sent = send(client->sock, (const char*)buffer, total_size, 0);
if (sent != total_size) {
/* Remove from queue on error */
if (client->pending_head == req) {
client->pending_head = req->next;
if (!client->pending_head) client->pending_tail = NULL;
}
free(req);
if (client->log_file) {
fprintf(client->log_file, "%llu: [ERROR] Failed to send request: %d\n",
(unsigned long long)get_timestamp_ms(), WSAGetLastError());
fflush(client->log_file);
}
return -1;
}
return 0;
}
static void handle_response(struct etcpmon_client* client, uint8_t msg_type,
const uint8_t* payload, uint16_t payload_size) {
switch (msg_type) {
@ -400,6 +492,80 @@ static void handle_response(struct etcpmon_client* client, uint8_t msg_type,
}
}
/* Find pending request by seq_id */
static struct etcpmon_pending_request* find_pending_request(struct etcpmon_client* client, uint8_t seq_id) {
struct etcpmon_pending_request* req = client->pending_head;
while (req) {
if (req->seq_id == seq_id) {
return req;
}
req = req->next;
}
return NULL;
}
/* Remove pending request from queue */
static void remove_pending_request(struct etcpmon_client* client, struct etcpmon_pending_request* req) {
struct etcpmon_pending_request** curr = &client->pending_head;
while (*curr) {
if (*curr == req) {
*curr = req->next;
if (client->pending_tail == req) {
client->pending_tail = *curr;
}
free(req);
return;
}
curr = &(*curr)->next;
}
}
/* Handle response - find matching request and call callback */
static void handle_response_with_callback(struct etcpmon_client* client, uint8_t msg_type,
const uint8_t* payload, uint16_t payload_size) {
uint8_t seq_id = client->last_response_seq_id;
/* Find pending request */
struct etcpmon_pending_request* req = find_pending_request(client, seq_id);
if (req) {
/* Call the callback */
if (req->callback) {
req->callback(req->arg, msg_type, payload, payload_size);
}
/* Remove from queue */
remove_pending_request(client, req);
if (client->log_file) {
fprintf(client->log_file, "%llu: [LOG] Response handled, seq=%d removed from queue\n",
(unsigned long long)get_timestamp_ms(), seq_id);
fflush(client->log_file);
}
} else {
/* No pending request - maybe broadcast/unsolicited or stale */
if (client->log_file) {
fprintf(client->log_file, "%llu: [LOG] Response seq=%d - no pending request (broadcast?)\n",
(unsigned long long)get_timestamp_ms(), seq_id);
fflush(client->log_file);
}
}
/* === НОВОЕ: ВСЕГДА вызываем legacy обработчики (on_conn_list / on_metrics / on_error) ===
* Это позволяет использовать и старый код, и новый асинхронный send_request одновременно */
if (seq_id == 0 || msg_type == ETCPMON_RSP_CONN_LIST ||
msg_type == ETCPMON_RSP_METRICS || msg_type == ETCPMON_RSP_ERROR) {
handle_response(client, msg_type, payload, payload_size);
}
}
static void metrics_auto_callback(void* arg, uint8_t msg_type, const uint8_t* payload, uint16_t payload_size) {
(void)arg;
(void)msg_type;
(void)payload;
(void)payload_size;
/* Ничего не делаем — legacy on_metrics вызывается автоматически ниже */
}
int etcpmon_client_process(struct etcpmon_client* client) {
if (!client || client->sock == INVALID_SOCKET) return -1;
@ -419,6 +585,7 @@ int etcpmon_client_process(struct etcpmon_client* client) {
if (client->on_connected) {
client->on_connected(client->user_data); // Здесь вызов request_list
}
return 0;// выходим, не лезем в recv/send в этом же тике
// Продолжаем к recv, т.к. подключены
} else if (error == WSAEINPROGRESS || error == WSAEALREADY) {
// Подключение ещё в процессе, ничего не делаем, ждём следующий вызов process
@ -461,6 +628,27 @@ int etcpmon_client_process(struct etcpmon_client* client) {
}
}
/* === Авто-перезапрос метрик каждые 100 мс (новый асинхронный способ) === */
if (client->fully_connected && client->selected_peer_id != 0 && client->sock != INVALID_SOCKET) {
uint64_t now = get_timestamp_ms();
if (now - client->last_metrics_request_ms >= ETCPMON_UPDATE_INTERVAL_MS) {
/* Используем новый асинхронный путь с коллбэком */
etcpmon_client_send_request(client,
ETCPMON_CMD_GET_METRICS,
NULL, 0,
metrics_auto_callback,
NULL);
client->last_metrics_request_ms = now;
if (client->log_file) {
fprintf(client->log_file, "%llu: [LOG] Auto-request METRICS (async, seq=%d)\n",
(unsigned long long)now, client->next_seq_id - 1);
fflush(client->log_file);
}
}
}
// Если подключение в процессе, мы уже вышли выше. Здесь recv только для установленного соединения
uint8_t* buf = client->recv_buffer + client->recv_len;
int buf_space = ETCPMON_MAX_MSG_SIZE - client->recv_len;
@ -537,13 +725,17 @@ int etcpmon_client_process(struct etcpmon_client* client) {
uint8_t* payload = client->recv_buffer + sizeof(*hdr);
uint16_t payload_size = hdr->size - sizeof(*hdr);
/* Store seq_id for matching with pending requests */
client->last_response_seq_id = hdr->seq_id;
if (client->log_file) {
fprintf(client->log_file, "%llu: [LOG] Received message type=0x%02X size=%d\n",
(unsigned long long)get_timestamp_ms(), hdr->type, hdr->size);
fprintf(client->log_file, "%llu: [LOG] Received message type=0x%02X size=%d seq=%d\n",
(unsigned long long)get_timestamp_ms(), hdr->type, hdr->size, hdr->seq_id);
fflush(client->log_file);
}
handle_response(client, hdr->type, payload, payload_size);
/* Use callback-based handler */
handle_response_with_callback(client, hdr->type, payload, payload_size);
processed++;
/* Remove processed message */

29
tools/etcpmon/etcpmon_client.h

@ -50,6 +50,7 @@ struct etcpmon_client {
SOCKET sock;
int connected;
int fully_connected;
/* Receive buffer */
uint8_t recv_buffer[ETCPMON_MAX_MSG_SIZE];
uint16_t recv_len;
@ -76,6 +77,14 @@ struct etcpmon_client {
/* Metrics history for graph */
struct metrics_history history;
/* Pending requests queue with callbacks */
struct etcpmon_pending_request* pending_head;
struct etcpmon_pending_request* pending_tail;
uint8_t next_seq_id;
/* Last received response seq_id (for matching) */
uint8_t last_response_seq_id;
/* Callbacks */
void (*on_connected)(void* user_data);
void (*on_disconnected)(void* user_data);
@ -86,6 +95,15 @@ struct etcpmon_client {
void (*on_error)(const char* msg, void* user_data);
void* user_data;
uint64_t last_metrics_request_ms;
};
/* Pending request with callback */
struct etcpmon_pending_request {
uint8_t seq_id; /* Sequence ID */
void (*callback)(void* arg, uint8_t msg_type, const uint8_t* payload, uint16_t payload_size);
void* arg; /* User argument for callback */
struct etcpmon_pending_request* next;
};
/* Initialize client structure */
@ -120,6 +138,17 @@ int etcpmon_client_select_connection(struct etcpmon_client* client, uint64_t pee
*/
int etcpmon_client_request_metrics(struct etcpmon_client* client);
/* Send request with callback
* callback will be called when response is received
* arg will be passed to callback
* Returns 0 on success (queued), -1 on error
*/
int etcpmon_client_send_request(struct etcpmon_client* client,
uint8_t type,
const void* payload, uint16_t payload_size,
void (*callback)(void* arg, uint8_t msg_type, const uint8_t* payload, uint16_t payload_size),
void* arg);
/* Process incoming data (non-blocking)
* Should be called periodically (e.g., from window message loop)
* Returns 1 if data was processed, 0 if no data, -1 on error

106
tools/etcpmon/etcpmon_gui.c

@ -15,6 +15,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
//#pragma comment(lib, "comctl32.lib")
//#pragma comment(lib, "user32.lib")
//#pragma comment(lib, "gdi32.lib")
@ -39,15 +40,7 @@ static void on_metrics(struct etcpmon_rsp_metrics* metrics,
struct etcpmon_link_metrics* links,
uint8_t links_count, void* user_data);
static void on_error(const char* msg, void* user_data);
/* Helper functions */
static void SetDlgItemTextFmt(HWND hDlg, int nIDDlgItem, const char* fmt, ...) {
char buf[256];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
SetDlgItemTextA(hDlg, nIDDlgItem, buf);
}
int etcpmon_gui_init(struct etcpmon_app* app, HINSTANCE hInstance) {
if (!app) return -1;
@ -61,6 +54,11 @@ int etcpmon_gui_init(struct etcpmon_app* app, HINSTANCE hInstance) {
iccex.dwICC = ICC_LISTVIEW_CLASSES | ICC_STANDARD_CLASSES;
InitCommonControlsEx(&iccex);
/* === НОВОЕ === */
app->need_initial_request = 1;
app->last_selected_peer_id = 0;
app->isConnected = 0;
/* Register window class */
WNDCLASSEXA wcex;
memset(&wcex, 0, sizeof(wcex));
@ -652,7 +650,7 @@ static void CreateControls(struct etcpmon_app* app) {
for (int i = 0; i < 8; i++) {
app->hEditDebug[i] = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER,
q_col1 + 85 + i * 65, qy - 2, 60, 18, hWnd, (HMENU)(IDC_EDIT_DEBUG_0 + i), hInst, NULL);
q_col1 + 85 + i * 65, qy - 2, 60, 18, hWnd, (HMENU)((UINT_PTR)(IDC_EDIT_DEBUG_0 + i)), hInst, NULL);
}
}
@ -739,38 +737,46 @@ static void OnDisconnect(struct etcpmon_app* app) {
etcpmon_gui_clear_metrics(app);
etcpmon_gui_set_status(app, "Disconnected");
}
static void OnConnectionSelect(struct etcpmon_app* app) {
if (!app || !app->client) return;
if (!app || !app->hListConnections || !app->client) return;
int sel = (int)SendMessage(app->hListConnections, LB_GETCURSEL, 0, 0);
if (sel == LB_ERR) return;
int idx = (int)SendMessage(app->hListConnections, LB_GETCURSEL, 0, 0);
if (idx == LB_ERR) return;
/* Get peer_id from item data */
uint64_t peer_id = (uint64_t)SendMessage(app->hListConnections, LB_GETITEMDATA, sel, 0);
uint64_t peer_id = (uint64_t)SendMessage(app->hListConnections, LB_GETITEMDATA, idx, 0);
if (peer_id == 0) return;
/* Только если действительно изменилось — иначе спамим сервер */
if (peer_id != app->last_selected_peer_id) {
app->last_selected_peer_id = peer_id;
/* Request metrics for this connection */
etcpmon_client_select_connection(app->client, peer_id);
etcpmon_gui_clear_metrics(app); /* очищаем старые цифры */
etcpmon_gui_set_status(app, "Connection selected");
}
}
static void OnTimer(struct etcpmon_app* app) {
if (!app || !app->client) return;
/* 1. Сеть */
etcpmon_client_process(app->client);
/* 2. Первый запрос списка после подключения */
static int first_connect = 1;
if (first_connect && app->isConnected) {
etcpmon_client_request_list(app->client);
first_connect = 0;
}
/* 3. Запрос метрик */
if (app->client->selected_peer_id != 0) {
etcpmon_client_request_metrics(app->client);
// fprintf(app->client->log_file, "Timer... \n");
// fflush(app->client->log_file);
int processed = etcpmon_client_process(app->client);
if (processed < 0) {
OnDisconnect(app); // если связь оборвалась
return;
}
/* 4. График — 20 раз в секунду */
if (app->hGraphWnd) {
InvalidateRect(app->hGraphWnd, NULL, FALSE);
UpdateWindow(app->hGraphWnd); /* форсируем немедленную перерисовку */
if (app->need_initial_request && app->isConnected) {
etcpmon_client_request_list(app->client);
app->need_initial_request = 0;
}
}
static void UpdateUIState(struct etcpmon_app* app) {
if (!app) return;
@ -779,18 +785,16 @@ static void UpdateUIState(struct etcpmon_app* app) {
EnableWindow(app->hBtnConnect, !app->isConnected);
EnableWindow(app->hBtnDisconnect, app->isConnected);
}
/* Client callbacks */
static void on_connected(void* user_data) {
struct etcpmon_app* app = (struct etcpmon_app*)user_data;
app->isConnected = 1;
UpdateUIState(app);
etcpmon_gui_set_status(app, "Connected");
if (app->client->log_file) {
fprintf(app->client->log_file, "[DEBUG] on_connected called\n");
fflush(app->client->log_file);
}
etcpmon_client_request_list(app->client);
etcpmon_gui_set_status(app, "Connected to server");
}
void etcpmon_gui_update_graph(struct etcpmon_app* app, struct etcpmon_rsp_metrics* metrics) {
if (!app || !metrics) return;
@ -812,16 +816,26 @@ static void on_disconnected(void* user_data) {
UpdateUIState(app);
etcpmon_gui_set_status(app, "Disconnected");
}
static void on_conn_list(struct etcpmon_conn_info* list, uint8_t count, void* user_data) {
struct etcpmon_app* app = (struct etcpmon_app*)user_data;
etcpmon_gui_update_conn_list(app, list, count);
etcpmon_gui_set_status(app, "Connection list updated");
/* Automatically select the first connection if available */
if (count > 0 && app->hListConnections) {
/* Автоматически выбираем первое соединение (только один раз) */
if (count > 0 && app->last_selected_peer_id == 0) {
uint64_t first_id = list[0].peer_node_id;
app->last_selected_peer_id = first_id;
etcpmon_client_select_connection(app->client, first_id);
/* Выделяем первую строку в списке */
SendMessage(app->hListConnections, LB_SETCURSEL, 0, 0);
OnConnectionSelect(app);
etcpmon_gui_set_status(app, "Auto-selected first connection");
}
}
static void on_metrics(struct etcpmon_rsp_metrics* metrics,
struct etcpmon_link_metrics* links,
uint8_t links_count, void* user_data) {
@ -838,6 +852,7 @@ static void on_metrics(struct etcpmon_rsp_metrics* metrics,
}
}
}
static void on_error(const char* msg, void* user_data) {
struct etcpmon_app* app = (struct etcpmon_app*)user_data;
etcpmon_gui_set_status(app, msg);
@ -1040,11 +1055,14 @@ void etcpmon_gui_update_metrics(struct etcpmon_app* app,
}
InvalidateRect(app->hListLinks, NULL, FALSE);
}
/* График */
/* График — принудительно перерисовываем */
if (app->hGraphWnd) {
InvalidateRect(app->hGraphWnd, NULL, FALSE);
InvalidateRect(app->hGraphWnd, NULL, TRUE); /* TRUE = стираем фон */
UpdateWindow(app->hGraphWnd); /* сразу отрисовать */
}
}
void etcpmon_gui_clear_metrics(struct etcpmon_app* app) {
if (!app) return;
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ETCP_NAME, "");

3
tools/etcpmon/etcpmon_gui.h

@ -290,6 +290,9 @@ struct etcpmon_app {
/* Client state */
struct etcpmon_client* client;
int need_initial_request; /* 1 = нужно запросить список при первом таймере */
uint64_t last_selected_peer_id; /* чтобы не спамить SELECT_CONN каждый тик */
/* Timer */
UINT_PTR updateTimer;
DWORD last_poll_time;

8
tools/etcpmon/etcpmon_protocol.h

@ -52,13 +52,15 @@ extern "C" {
/* ============================================================================
* Message Header
* ============================================================================ */
* ============================================================================
* seq_id: 0 = broadcast/unsolicited, 1-255 = request/response pair */
#pragma pack(push, 1)
struct etcpmon_msg_header {
uint16_t size; /* Total message size including header */
uint8_t type; /* Message type (command or response) */
uint8_t seq_id; /* Sequence ID: request pairs use same ID */
};
/* ============================================================================
@ -254,9 +256,11 @@ struct etcpmon_rsp_error {
/* Build message header */
static inline void etcpmon_build_header(struct etcpmon_msg_header* hdr,
uint16_t payload_size,
uint8_t msg_type) {
uint8_t msg_type,
uint8_t seq_id) {
hdr->size = sizeof(struct etcpmon_msg_header) + payload_size;
hdr->type = msg_type;
hdr->seq_id = seq_id;
}
/* Validate message header */

Loading…
Cancel
Save