Browse Source

gui + small fixes

nodeinfo-routing-update
jeka 1 month ago
parent
commit
cd169b106a
  1. 1
      AGENTS.md
  2. 4
      src/control_server.c
  3. 82
      src/route_bgp.c
  4. 31
      tools/etcpmon/Makefile.mingw
  5. 68
      tools/etcpmon/build.sh
  6. 13
      tools/etcpmon/etcpmon_client.c
  7. 3
      tools/etcpmon/etcpmon_client.h
  8. 186
      tools/etcpmon/etcpmon_gui.c
  9. 2
      tools/etcpmon/etcpmon_gui.h

1
AGENTS.md

@ -12,6 +12,7 @@ This file contains essential information for AI coding agents working in the uTu
## 🔧 Build Commands
### Full Build
win (msys2 ucrt64): build.bat
```bash
./autogen.sh # Generate configure script (if needed)
./configure # Configure build

4
src/control_server.c

@ -419,6 +419,10 @@ static void client_read_callback(socket_t fd, void* arg) {
if (received == SOCKET_ERROR) {
int err = WSAGetLastError();
if (err != WSAEWOULDBLOCK) {
if (err == 10054) {
client->connected = 0;
return;
}
if (server && server->log_file) {
fprintf(server->log_file, "%llu: [ERROR] Client recv error: %d\n",
(unsigned long long)get_timestamp_ms(), err);

82
src/route_bgp.c

@ -22,6 +22,12 @@
// Размер пакета (без заголовка ETCP)
#define BGP_PACKET_SIZE (sizeof(struct ROUTE_BGP_PACKET))
static inline void format_network(uint32_t network, char* out, size_t out_size)
{
uint32_t addr = htonl(network);
inet_ntop(AF_INET, &addr, out, out_size);
}
// Forward declarations
static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
const struct ROUTE_ENTRY* route);
@ -64,11 +70,20 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
return;
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received BGP packet: subcmd=%d, network=%08x/%d, node_id=%016llx",
pkt->subcmd, ntohl(pkt->network), pkt->prefix_length & 0x3F, (unsigned long long)pkt->node_id);
char network_str[INET_ADDRSTRLEN];
format_network(ntohl(pkt->network), network_str, sizeof(network_str));
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Received BGP packet: subcmd=%d, network=%s/%d, node_id=%016llx",
pkt->subcmd, network_str, pkt->prefix_length & 0x3F, (unsigned long long)pkt->node_id);
switch (pkt->subcmd) {
case ROUTE_SUBCMD_ENTRY: {
// Игнорируем маршруты от себя
if (pkt->node_id == instance->node_id) {
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Ignoring own route: %s/%d", network_str, pkt->prefix_length & 0x3F);
break;
}
// Создаем запись маршрута
struct ROUTE_ENTRY route;
memset(&route, 0, sizeof(route));
@ -97,32 +112,44 @@ static void route_bgp_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry*
}
// Вставляем в таблицу
char network_str[INET_ADDRSTRLEN];
format_network(route.network, network_str, sizeof(network_str));
if (route_table_insert(instance->rt, &route)) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Added learned route: %08x/%d via node %016llx (hops=%d)",
route.network, route.prefix_length,
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Added learned route: %s/%d via node %016llx (hops=%d)",
network_str, route.prefix_length,
(unsigned long long)route.destination_node_id,
route.metrics.hop_count);
} else {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Failed to insert route: %08x/%d",
route.network, route.prefix_length);
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Failed to insert route: %s/%d",
network_str, route.prefix_length);
}
break;
}
case ROUTE_SUBCMD_WITHDRAW: {
// Игнорируем withdraw от себя
if (pkt->node_id == instance->node_id) {
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Ignoring own withdraw: %s/%d", network_str, pkt->prefix_length & 0x3F);
break;
}
// Удаление маршрута (withdrawal)
uint32_t network = ntohl(pkt->network);
uint8_t prefix_length = pkt->prefix_length & 0x3F;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processing route withdrawal: %08x/%d from conn %p",
network, prefix_length, (void*)from_conn);
char network_str[INET_ADDRSTRLEN];
format_network(network, network_str, sizeof(network_str));
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Processing route withdrawal: %s/%d from conn %p",
network_str, prefix_length, (void*)from_conn);
if (route_table_delete_entry(instance->rt, network, prefix_length, from_conn)) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Route withdrawn successfully: %08x/%d",
network, prefix_length);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Route withdrawn successfully: %s/%d",
network_str, prefix_length);
} else {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Route to withdraw not found: %08x/%d",
network, prefix_length);
DEBUG_WARN(DEBUG_CATEGORY_BGP, "Route to withdraw not found: %s/%d",
network_str, prefix_length);
}
break;
}
@ -154,6 +181,11 @@ static void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* con
return;
}
// Не отправляем withdraw автору маршрута
if (route->destination_node_id == conn->peer_node_id) {
return;
}
// Создаем ETCP пакет
struct ll_entry* entry = ll_alloc_lldgram(sizeof(struct ROUTE_BGP_PACKET));
if (!entry) {
@ -175,8 +207,11 @@ static void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* con
entry->len = sizeof(struct ROUTE_BGP_PACKET);
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Sending withdraw: %08x/%d to conn %p",
route->network, route->prefix_length, (void*)conn);
char network_str[INET_ADDRSTRLEN];
format_network(route->network, network_str, sizeof(network_str));
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Sending withdraw: %s/%d to conn %p",
network_str, route->prefix_length, (void*)conn);
// Отправляем через ETCP
if (etcp_send(conn, entry) != 0) {
@ -238,8 +273,11 @@ static void route_bgp_on_route_change(struct ROUTE_TABLE* table,
// Не рассылаем маршруты через которые мы их получили (чтобы избежать петель)
// Это делается в цикле ниже
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Route change: action=%d, network=%08x/%d, type=%d",
action, entry->network, entry->prefix_length, entry->type);
char network_str[INET_ADDRSTRLEN];
format_network(entry->network, network_str, sizeof(network_str));
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Route change: action=%d, network=%s/%d, type=%d",
action, network_str, entry->prefix_length, entry->type);
// Перебираем все соединения в senders_list
struct ll_entry* item_entry = bgp->senders_list->head;
@ -272,6 +310,11 @@ static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
return;
}
// Не отправляем маршрут автору этого маршрута
if (route->destination_node_id == conn->peer_node_id) {
return;
}
// Пропускаем неактивные маршруты
if (!(route->flags & ROUTE_FLAG_ACTIVE)) {
return;
@ -313,8 +356,11 @@ static void route_bgp_send_route(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn,
entry->len = sizeof(struct ROUTE_BGP_PACKET);
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Sending route: %08x/%d to conn %p",
route->network, route->prefix_length & 0x3F, (void*)conn);
char network_str[INET_ADDRSTRLEN];
format_network(route->network, network_str, sizeof(network_str));
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Sending route: %s/%d to conn %p",
network_str, route->prefix_length & 0x3F, (void*)conn);
// Отправляем через ETCP
if (etcp_send(conn, entry) != 0) {

31
tools/etcpmon/Makefile.mingw

@ -1,31 +0,0 @@
# ETCP Monitor Makefile for MinGW/MSYS2
# Build: make -f Makefile.mingw
# Clean: make -f Makefile.mingw clean
CC = gcc
CFLAGS = -Wall -O2 -DWIN32_LEAN_AND_MEAN
LDFLAGS = -mwindows
LIBS = -lws2_32 -lcomctl32 -luser32 -lgdi32
TARGET = etcpmon.exe
SRCS = etcpmon_main.c etcpmon_gui.c etcpmon_client.c
OBJS = $(SRCS:.c=.o)
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
@echo "Build complete: $(TARGET)"
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f $(OBJS) $(TARGET)
@echo "Clean complete"
# Run the application
run: $(TARGET)
./$(TARGET)

68
tools/etcpmon/build.sh

@ -24,76 +24,40 @@ log ""
# Compiler settings
CC=${CC:-gcc}
CFLAGS="-Wall -O2 -DWIN32_LEAN_AND_MEAN"
LDFLAGS="-mwindows"
LIBS="-lws2_32 -lcomctl32 -luser32 -lgdi32"
TARGET="etcpmon.exe"
SRCS="etcpmon_main.c etcpmon_gui.c etcpmon_client.c etcpmon_graph.c"
OBJ_FILES=""
log "Compiler: $CC"
log "Target: $TARGET"
log "Sources: $SRCS"
log "Target: etcpmon.exe"
log ""
# Clean previous build
log "[1/5] Cleaning previous build..."
rm -f *.o $TARGET
# Build using Makefile
log "[1/3] Building..."
make CC="$CC" 2>&1 | tee -a "$LOG_FILE"
# Compile each source file
log ""
log "[2/5] Compiling source files..."
for src in $SRCS; do
obj="${src%.c}.o"
OBJ_FILES="$OBJ_FILES $obj"
log " CC $src -> $obj"
$CC $CFLAGS -c -o "$obj" "$src" 2>&1 | tee -a "$LOG_FILE" || {
log "[ERROR] Failed to compile $src"
exit 1
}
done
BUILD_RESULT=${PIPESTATUS[0]}
# Link
if [ $BUILD_RESULT -ne 0 ]; then
log ""
log "[3/5] Linking..."
log " LD $OBJ_FILES -> $TARGET"
$CC $LDFLAGS -o "$TARGET" $OBJ_FILES $LIBS 2>&1 | tee -a "$LOG_FILE" || {
log "[ERROR] Failed to link $TARGET"
log "================================"
log "Build FAILED!"
log "================================"
exit 1
}
# Strip debug symbols (optional, for smaller size)
log ""
log "[4/5] Stripping debug symbols..."
strip "$TARGET" 2>/dev/null | tee -a "$LOG_FILE" || true
fi
# Verify build
log ""
log "[5/5] Verifying build..."
if [ -f "$TARGET" ]; then
FILE_SIZE=$(du -h "$TARGET" | cut -f1)
log " OK: $TARGET created ($FILE_SIZE)"
# Check dependencies
log ""
log "Checking dependencies..."
if command -v objdump >/dev/null 2>&1; then
objdump -p "$TARGET" | grep "DLL Name:" | head -10 | tee -a "$LOG_FILE" || true
fi
log "[2/3] Verifying build..."
if [ -f "etcpmon.exe" ]; then
FILE_SIZE=$(du -h "etcpmon.exe" | cut -f1)
log " OK: etcpmon.exe created ($FILE_SIZE)"
log ""
log "================================"
log "Build SUCCESS!"
log "================================"
log ""
log "Executable: $TARGET"
log "Executable: etcpmon.exe"
log "Size: $FILE_SIZE"
log "Location: $(pwd)/$TARGET"
log ""
log "To run:"
log " cd $(pwd)"
log " ./$TARGET"
log "Location: $(pwd)/etcpmon.exe"
log ""
else
log ""

13
tools/etcpmon/etcpmon_client.c

@ -3,6 +3,7 @@
*/
#include "etcpmon_client.h"
#include "etcpmon_graph.h"
#include <stdio.h>
#include <string.h>
#include <ws2tcpip.h>
@ -47,6 +48,11 @@ void etcpmon_client_init(struct etcpmon_client* client) {
client->fully_connected = 0;
client->log_file = NULL;
memset(&client->history, 0, sizeof(client->history));
for (int i = 0; i < GRAPH_METRICS_COUNT; i++) {
client->history.min_val[i] = 1e9f;
client->history.max_val[i] = -1e9f;
}
client->history.last_minmax_update = 0; // ← добавь
}
void etcpmon_client_cleanup(struct etcpmon_client* client) {
@ -397,8 +403,6 @@ static void handle_response(struct etcpmon_client* client, uint8_t msg_type,
int etcpmon_client_process(struct etcpmon_client* client) {
if (!client || client->sock == INVALID_SOCKET) return -1;
fprintf(client->log_file, "%llu: [LOG] Client_process\n", (unsigned long long)get_timestamp_ms()); fflush(client->log_file);
// Проверка завершения non-blocking connect
if (client->connected && !client->fully_connected) {
int error = 0;
@ -593,6 +597,11 @@ void etcpmon_client_add_to_history(struct etcpmon_client* client, struct etcpmon
struct metrics_history* h = &client->history;
int idx = h->head;
// for (int m = 0; m < GRAPH_METRICS_COUNT; m++) {
// float v = h->values[m][idx];
// if (v < h->min_val[m]) h->min_val[m] = v;
// if (v > h->max_val[m]) h->max_val[m] = v;
// }
if (client->log_file) {
fprintf(client->log_file, "add_to_history: rtt_last=%u rtt_avg10=%u rtt_avg100=%u jitter=%u\n",

3
tools/etcpmon/etcpmon_client.h

@ -35,6 +35,9 @@ typedef enum {
/* Metrics history buffer */
struct metrics_history {
float values[GRAPH_METRIC_COUNT][GRAPH_HISTORY_SIZE];
float min_val[GRAPH_METRIC_COUNT];
float max_val[GRAPH_METRIC_COUNT];
DWORD last_minmax_update;
int head;
int count;
uint32_t last_retrans;

186
tools/etcpmon/etcpmon_gui.c

@ -25,7 +25,7 @@
#define WINDOW_WIDTH 900
#define WINDOW_HEIGHT 900
#define UPDATE_INTERVAL 100 /* 100ms */
#define UPDATE_INTERVAL 10 /* 50ms → 20 samples per second */
/* Global app pointer for callbacks */
static struct etcpmon_app* g_app = NULL;
@ -49,14 +49,6 @@ static void on_metrics(struct etcpmon_rsp_metrics* metrics,
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;
@ -140,6 +132,7 @@ HWND etcpmon_gui_create_window(struct etcpmon_app* app) {
/* Create all controls */
CreateControls(app);
SetTimer(app->hWndMain, IDC_TIMER_UPDATE, UPDATE_INTERVAL, NULL);
/* Set default values */
SetDlgItemTextA(app->hWndMain, IDC_EDIT_ADDR, "127.0.0.1");
@ -155,6 +148,10 @@ HWND etcpmon_gui_create_window(struct etcpmon_app* app) {
app);
}
for (int i = 0; i < GRAPH_METRICS_COUNT; i++) {
app->hGraphPens[i] = CreatePen(PS_SOLID, 2, graph_colors[i]);
}
UpdateUIState(app);
return app->hWndMain;
@ -229,7 +226,7 @@ static void CreateControls(struct etcpmon_app* app) {
};
int block_w = 200;
int block_h = 50;
int block_h = 28;
int cols = 4;
int spacing = 5;
@ -239,17 +236,17 @@ static void CreateControls(struct etcpmon_app* app) {
int bx = 10 + col * (block_w + spacing);
int by = y + row * (block_h + spacing);
app->hChannelCheck[i] = CreateWindowExA(0, "BUTTON", "",
WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BST_CHECKED,
bx, by + 4, 16, 14, hWnd, (HMENU)(UINT_PTR)(IDC_CH_CHECK_0 + i), hInst, NULL);
app->hChannelName[i] = CreateWindowExA(0, "STATIC", channel_short_names[i],
WS_CHILD | WS_VISIBLE,
bx, by, block_w, 16, hWnd, (HMENU)(UINT_PTR)(IDC_CH_NAME_0 + i), hInst, NULL);
bx + 20, by + 4, 60, 16, hWnd, (HMENU)(UINT_PTR)(IDC_CH_NAME_0 + i), hInst, NULL);
app->hChannelValue[i] = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", "",
WS_CHILD | WS_VISIBLE | ES_READONLY,
bx, by + 16, block_w, 18, hWnd, (HMENU)(UINT_PTR)(IDC_CH_VALUE_0 + i), hInst, NULL);
app->hChannelCheck[i] = CreateWindowExA(0, "BUTTON", "",
WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BST_CHECKED,
bx, by + 34, 16, 14, hWnd, (HMENU)(UINT_PTR)(IDC_CH_CHECK_0 + i), hInst, NULL);
bx + 85, by + 2, 110, 20, hWnd, (HMENU)(UINT_PTR)(IDC_CH_VALUE_0 + i), hInst, NULL);
}
int channel_grid_h = ((GRAPH_METRICS_COUNT + cols - 1) / cols) * (block_h + spacing) - spacing;
@ -415,18 +412,19 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l
}
break;
case WM_TIMER:
if (app && wParam == IDC_TIMER_UPDATE) {
OnTimer(app);
}
break;
case WM_SIZE:
if (app && app->hWndStatus) {
SendMessage(app->hWndStatus, WM_SIZE, 0, 0);
}
break;
case WM_TIMER:
if (wParam == IDC_TIMER_UPDATE && app) {
OnTimer(app);
return 0;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
@ -453,8 +451,6 @@ static void OnConnect(struct etcpmon_app* app) {
etcpmon_gui_set_status(app, "Connecting...");
if (etcpmon_client_connect(app->client, addr, port) == 0) {
/* Start update timer */
app->updateTimer = SetTimer(app->hWndMain, IDC_TIMER_UPDATE, UPDATE_INTERVAL, NULL);
app->isConnected = 1;
UpdateUIState(app);
etcpmon_gui_set_status(app, "Connected");
@ -466,11 +462,6 @@ static void OnConnect(struct etcpmon_app* app) {
static void OnDisconnect(struct etcpmon_app* app) {
if (!app || !app->client) return;
if (app->updateTimer) {
KillTimer(app->hWndMain, app->updateTimer);
app->updateTimer = 0;
}
etcpmon_client_disconnect(app->client);
app->isConnected = 0;
UpdateUIState(app);
@ -494,21 +485,54 @@ static void OnConnectionSelect(struct etcpmon_app* app) {
static void OnTimer(struct etcpmon_app* app) {
if (!app || !app->client) return;
/* Process any incoming data */
// int result =
/* 1. Сеть */
etcpmon_client_process(app->client);
/* If newly connected, request connection list */
/* 2. Первый запрос списка после подключения */
static int first_connect = 1;
if (first_connect && app->isConnected) {
etcpmon_client_request_list(app->client);
first_connect = 0;
}
/* Request metrics periodically if a connection is selected */
/* 3. Запрос метрик */
if (app->client->selected_peer_id != 0) {
etcpmon_client_request_metrics(app->client);
}
/* 4. График — 20 раз в секунду */
if (app->hGraphWnd) {
InvalidateRect(app->hGraphWnd, NULL, FALSE);
UpdateWindow(app->hGraphWnd); /* форсируем немедленную перерисовку */
}
/* 5. Цифры под графиком — обновляем если курсор не активен */
if (!app->graph_cursor_active) {
struct metrics_history* h = etcpmon_client_get_history(app->client);
if (h && h->count > 0) {
int last_idx = (h->head - 1 + GRAPH_HISTORY_SIZE) % GRAPH_HISTORY_SIZE;
UpdateChannelValues(app, last_idx);
}
} else {
RECT rc;
GetClientRect(app->hGraphWnd, &rc);
struct metrics_history* h = etcpmon_client_get_history(app->client);
if (h && h->count > 2) {
int w = rc.right - rc.left;
int pad = 5;
int gl = pad + 45;
int gr = w - pad;
int vis = (h->count < gr - gl) ? h->count : (gr - gl);
int oldest = (h->head - vis + GRAPH_HISTORY_SIZE) % GRAPH_HISTORY_SIZE;
int startx = gr - vis;
int x = app->graph_cursor_x;
if (x >= startx && x < startx + vis) {
int rel = x - startx;
int idx = (oldest + rel) % GRAPH_HISTORY_SIZE;
UpdateChannelValues(app, idx);
}
}
}
}
static void UpdateUIState(struct etcpmon_app* app) {
@ -589,6 +613,14 @@ static void on_metrics(struct etcpmon_rsp_metrics* metrics,
if (app) {
etcpmon_gui_update_metrics(app, metrics, links, links_count);
etcpmon_client_add_to_history(app->client, metrics);
if (!app->graph_cursor_active) {
struct metrics_history* h = etcpmon_client_get_history(app->client);
if (h && h->count > 0) {
int last_idx = (h->head - 1 + GRAPH_HISTORY_SIZE) % GRAPH_HISTORY_SIZE;
UpdateChannelValues(app, last_idx);
}
}
}
}
@ -608,15 +640,14 @@ int etcpmon_gui_run(struct etcpmon_app* app) {
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
return (int)msg.wParam;
}
void etcpmon_gui_cleanup(struct etcpmon_app* app) {
if (!app) return;
if (app->updateTimer) {
KillTimer(app->hWndMain, app->updateTimer);
for (int i = 0; i < GRAPH_METRICS_COUNT; i++) {
if (app->hGraphPens[i]) DeleteObject(app->hGraphPens[i]);
}
if (app->client) {
@ -655,47 +686,55 @@ void etcpmon_gui_update_conn_list(struct etcpmon_app* app,
}
}
/* Обновляет EDIT только если текст действительно изменился → убирает моргание */
static BOOL UpdateEditIfChanged(HWND hDlg, int nIDDlgItem, const char* fmt, ...)
{
char new_text[128];
va_list args;
va_start(args, fmt);
vsnprintf(new_text, sizeof(new_text), fmt, args);
va_end(args);
char old_text[128] = {0};
GetDlgItemTextA(hDlg, nIDDlgItem, old_text, sizeof(old_text));
if (strcmp(old_text, new_text) == 0)
return FALSE; /* ничего не изменилось — не трогаем */
SetDlgItemTextA(hDlg, nIDDlgItem, new_text);
InvalidateRect(GetDlgItem(hDlg, nIDDlgItem), NULL, FALSE);
return TRUE;
}
void etcpmon_gui_update_metrics(struct etcpmon_app* app,
struct etcpmon_rsp_metrics* metrics,
struct etcpmon_link_metrics* links,
uint8_t links_count) {
uint8_t links_count)
{
if (!app || !metrics) return;
/* Update ETCP metrics */
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_RTT_LAST, "%u us",
metrics->etcp.rtt_last * 100);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_RTT_AVG10, "%u us",
metrics->etcp.rtt_avg_10 * 100);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_RTT_AVG100, "%u us",
metrics->etcp.rtt_avg_100 * 100);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_JITTER, "%u us",
metrics->etcp.jitter * 100);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_BYTES_SENT, "%llu",
(unsigned long long)metrics->etcp.bytes_sent_total);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_RETRANS, "%u",
metrics->etcp.retrans_count);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_ACKS, "%u",
metrics->etcp.ack_count);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_INFLIGHT, "%u bytes",
metrics->etcp.unacked_bytes);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_ETCP_LINKS, "%u",
metrics->etcp.links_count);
/* Update TUN metrics */
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_TUN_READ_BYTES, "%llu",
(unsigned long long)metrics->tun.bytes_read);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_TUN_WRITE_BYTES, "%llu",
(unsigned long long)metrics->tun.bytes_written);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_TUN_READ_PKTS, "%u",
metrics->tun.packets_read);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_TUN_WRITE_PKTS, "%u",
metrics->tun.packets_written);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_TUN_READ_ERRS, "%u",
metrics->tun.read_errors);
SetDlgItemTextFmt(app->hWndMain, IDC_EDIT_TUN_WRITE_ERRS, "%u",
metrics->tun.write_errors);
/* Update links list */
HWND hMain = app->hWndMain;
/* ETCP Metrics — обновляем ТОЛЬКО при изменении */
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_RTT_LAST, "%u us", metrics->etcp.rtt_last * 100);
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_RTT_AVG10, "%u us", metrics->etcp.rtt_avg_10 * 100);
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_RTT_AVG100, "%u us", metrics->etcp.rtt_avg_100 * 100);
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_JITTER, "%u us", metrics->etcp.jitter * 100);
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_BYTES_SENT, "%llu", (unsigned long long)metrics->etcp.bytes_sent_total);
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_RETRANS, "%u", metrics->etcp.retrans_count);
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_ACKS, "%u", metrics->etcp.ack_count);
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_INFLIGHT, "%u bytes", metrics->etcp.unacked_bytes);
UpdateEditIfChanged(hMain, IDC_EDIT_ETCP_LINKS, "%u", metrics->etcp.links_count);
/* TUN Metrics */
UpdateEditIfChanged(hMain, IDC_EDIT_TUN_READ_BYTES, "%llu", (unsigned long long)metrics->tun.bytes_read);
UpdateEditIfChanged(hMain, IDC_EDIT_TUN_WRITE_BYTES, "%llu", (unsigned long long)metrics->tun.bytes_written);
UpdateEditIfChanged(hMain, IDC_EDIT_TUN_READ_PKTS, "%u", metrics->tun.packets_read);
UpdateEditIfChanged(hMain, IDC_EDIT_TUN_WRITE_PKTS, "%u", metrics->tun.packets_written);
UpdateEditIfChanged(hMain, IDC_EDIT_TUN_READ_ERRS, "%u", metrics->tun.read_errors);
UpdateEditIfChanged(hMain, IDC_EDIT_TUN_WRITE_ERRS, "%u", metrics->tun.write_errors);
/* Links list (перестраивается всегда — он маленький, моргание почти незаметно) */
if (app->hListLinks) {
SendMessage(app->hListLinks, LB_RESETCONTENT, 0, 0);
for (uint8_t i = 0; i < links_count; i++) {
@ -717,11 +756,12 @@ void etcpmon_gui_update_metrics(struct etcpmon_app* app,
SendMessageA(app->hListLinks, LB_ADDSTRING, 0, (LPARAM)display);
}
InvalidateRect(app->hListLinks, NULL, FALSE);
}
/* График */
if (app->hGraphWnd) {
InvalidateRect(app->hGraphWnd, NULL, FALSE);
UpdateWindow(app->hGraphWnd); // Force immediate paint
}
}

2
tools/etcpmon/etcpmon_gui.h

@ -99,6 +99,7 @@ struct etcpmon_app {
HWND hWndMain;
HWND hWndStatus;
HPEN hGraphPens[GRAPH_METRICS_COUNT];
/* Connection controls */
HWND hEditAddr;
HWND hEditPort;
@ -143,6 +144,7 @@ struct etcpmon_app {
/* Timer ID */
UINT_PTR updateTimer;
DWORD last_poll_time;
int isConnected;
};

Loading…
Cancel
Save