Browse Source

graph bugfix

nodeinfo-routing-update
jeka 1 month ago
parent
commit
9113428e7d
  1. 2
      tools/etcpmon/build.sh
  2. 64
      tools/etcpmon/etcpmon_client.c
  3. 37
      tools/etcpmon/etcpmon_client.h
  4. 149
      tools/etcpmon/etcpmon_gui.c
  5. 53
      tools/etcpmon/etcpmon_gui.h

2
tools/etcpmon/build.sh

@ -29,7 +29,7 @@ LDFLAGS="-mwindows"
LIBS="-lws2_32 -lcomctl32 -luser32 -lgdi32" LIBS="-lws2_32 -lcomctl32 -luser32 -lgdi32"
TARGET="etcpmon.exe" TARGET="etcpmon.exe"
SRCS="etcpmon_main.c etcpmon_gui.c etcpmon_client.c" SRCS="etcpmon_main.c etcpmon_gui.c etcpmon_client.c etcpmon_graph.c"
OBJ_FILES="" OBJ_FILES=""
log "Compiler: $CC" log "Compiler: $CC"

64
tools/etcpmon/etcpmon_client.c

@ -14,7 +14,7 @@
#define LOG_FILENAME "etcpmon.log" #define LOG_FILENAME "etcpmon.log"
/* Get current timestamp in milliseconds (Unix epoch) */ /* Get current timestamp in milliseconds (Unix epoch) */
static uint64_t get_timestamp_ms(void) { uint64_t get_timestamp_ms(void) {
FILETIME ft; FILETIME ft;
GetSystemTimeAsFileTime(&ft); GetSystemTimeAsFileTime(&ft);
@ -46,6 +46,7 @@ void etcpmon_client_init(struct etcpmon_client* client) {
client->connected = 0; client->connected = 0;
client->fully_connected = 0; client->fully_connected = 0;
client->log_file = NULL; client->log_file = NULL;
memset(&client->history, 0, sizeof(client->history));
} }
void etcpmon_client_cleanup(struct etcpmon_client* client) { void etcpmon_client_cleanup(struct etcpmon_client* client) {
@ -367,6 +368,8 @@ static void handle_response(struct etcpmon_client* client, uint8_t msg_type,
client->on_metrics(client->last_metrics, client->last_links, client->on_metrics(client->last_metrics, client->last_links,
links_count, client->user_data); links_count, client->user_data);
} }
etcpmon_client_add_to_history(client, client->last_metrics);
} }
break; break;
} }
@ -579,3 +582,62 @@ const char* etcpmon_client_get_conn_name(struct etcpmon_client* client, uint64_t
} }
return NULL; return NULL;
} }
struct metrics_history* etcpmon_client_get_history(struct etcpmon_client* client) {
if (!client) return NULL;
return &client->history;
}
void etcpmon_client_add_to_history(struct etcpmon_client* client, struct etcpmon_rsp_metrics* metrics) {
if (!client || !metrics) return;
struct metrics_history* h = &client->history;
int idx = h->head;
if (client->log_file) {
fprintf(client->log_file, "add_to_history: rtt_last=%u rtt_avg10=%u rtt_avg100=%u jitter=%u\n",
metrics->etcp.rtt_last, metrics->etcp.rtt_avg_10, metrics->etcp.rtt_avg_100, metrics->etcp.jitter);
fflush(client->log_file);
}
float scale = 0.1f; // to ms (protocol: 0.1ms units)
h->values[GRAPH_METRIC_RTT_LAST][idx] = (float)metrics->etcp.rtt_last * scale;
h->values[GRAPH_METRIC_RTT_AVG10][idx] = (float)metrics->etcp.rtt_avg_10 * scale;
h->values[GRAPH_METRIC_RTT_AVG100][idx] = (float)metrics->etcp.rtt_avg_100 * scale;
h->values[GRAPH_METRIC_JITTER][idx] = (float)metrics->etcp.jitter * scale;
uint32_t retrans_delta = 0;
uint32_t acks_delta = 0;
uint64_t bytes_delta = 0;
if (h->count > 0) {
if (metrics->etcp.retrans_count >= h->last_retrans) {
retrans_delta = metrics->etcp.retrans_count - h->last_retrans;
}
if (metrics->etcp.ack_count >= h->last_acks) {
acks_delta = metrics->etcp.ack_count - h->last_acks;
}
if (metrics->etcp.bytes_sent_total >= h->last_bytes_sent) {
bytes_delta = metrics->etcp.bytes_sent_total - h->last_bytes_sent;
}
}
h->last_retrans = metrics->etcp.retrans_count;
h->last_acks = metrics->etcp.ack_count;
h->last_bytes_sent = metrics->etcp.bytes_sent_total;
h->values[GRAPH_METRIC_RETRANS][idx] = (float)retrans_delta;
h->values[GRAPH_METRIC_ACKS][idx] = (float)acks_delta;
h->values[GRAPH_METRIC_INFLIGHT][idx] = (float)metrics->etcp.unacked_bytes;
h->values[GRAPH_METRIC_BYTES_SENT][idx] = (float)bytes_delta;
h->head = (h->head + 1) % GRAPH_HISTORY_SIZE;
if (h->count < GRAPH_HISTORY_SIZE) {
h->count++;
}
if (client->log_file) {
fprintf(client->log_file, "history updated: count=%d head=%d\n", h->count, h->head);
fflush(client->log_file);
}
}

37
tools/etcpmon/etcpmon_client.h

@ -16,6 +16,32 @@
extern "C" { extern "C" {
#endif #endif
/* Graph history size (samples) */
#define GRAPH_HISTORY_SIZE 860
/* Graph metrics types */
typedef enum {
GRAPH_METRIC_RTT_LAST = 0,
GRAPH_METRIC_RTT_AVG10,
GRAPH_METRIC_RTT_AVG100,
GRAPH_METRIC_JITTER,
GRAPH_METRIC_RETRANS,
GRAPH_METRIC_ACKS,
GRAPH_METRIC_INFLIGHT,
GRAPH_METRIC_BYTES_SENT,
GRAPH_METRIC_COUNT
} graph_metric_type_t;
/* Metrics history buffer */
struct metrics_history {
float values[GRAPH_METRIC_COUNT][GRAPH_HISTORY_SIZE];
int head;
int count;
uint32_t last_retrans;
uint32_t last_acks;
uint64_t last_bytes_sent;
};
/* Client state */ /* Client state */
struct etcpmon_client { struct etcpmon_client {
SOCKET sock; SOCKET sock;
@ -44,6 +70,9 @@ struct etcpmon_client {
/* Log file */ /* Log file */
FILE* log_file; FILE* log_file;
/* Metrics history for graph */
struct metrics_history history;
/* Callbacks */ /* Callbacks */
void (*on_connected)(void* user_data); void (*on_connected)(void* user_data);
void (*on_disconnected)(void* user_data); void (*on_disconnected)(void* user_data);
@ -108,6 +137,14 @@ void etcpmon_client_set_callbacks(struct etcpmon_client* client,
/* Get connection name by peer_id */ /* Get connection name by peer_id */
const char* etcpmon_client_get_conn_name(struct etcpmon_client* client, uint64_t peer_id); const char* etcpmon_client_get_conn_name(struct etcpmon_client* client, uint64_t peer_id);
/* Get metrics history */
struct metrics_history* etcpmon_client_get_history(struct etcpmon_client* client);
/* Add metrics to history */
void etcpmon_client_add_to_history(struct etcpmon_client* client, struct etcpmon_rsp_metrics* metrics);
uint64_t get_timestamp_ms(void); // Add this declaration
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

149
tools/etcpmon/etcpmon_gui.c

@ -16,6 +16,7 @@
#include "etcpmon_client.h" #include "etcpmon_client.h"
#include "etcpmon_protocol.h" #include "etcpmon_protocol.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
//#pragma comment(lib, "comctl32.lib") //#pragma comment(lib, "comctl32.lib")
@ -23,7 +24,7 @@
//#pragma comment(lib, "gdi32.lib") //#pragma comment(lib, "gdi32.lib")
#define WINDOW_WIDTH 900 #define WINDOW_WIDTH 900
#define WINDOW_HEIGHT 700 #define WINDOW_HEIGHT 900
#define UPDATE_INTERVAL 100 /* 100ms */ #define UPDATE_INTERVAL 100 /* 100ms */
/* Global app pointer for callbacks */ /* Global app pointer for callbacks */
@ -83,10 +84,25 @@ int etcpmon_gui_init(struct etcpmon_app* app, HINSTANCE hInstance) {
wcex.lpszClassName = ETCPMON_WINDOW_CLASS; wcex.lpszClassName = ETCPMON_WINDOW_CLASS;
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION); wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassExA(&wcex)) { if (!RegisterClassExA(&wcex)) {
return -1; return -1;
} }
/* Register Graph window class */
WNDCLASSEXA wcexGraph;
memset(&wcexGraph, 0, sizeof(wcexGraph));
wcexGraph.cbSize = sizeof(wcexGraph);
wcexGraph.style = CS_HREDRAW | CS_VREDRAW;
wcexGraph.lpfnWndProc = GraphWndProc;
wcexGraph.hInstance = hInstance;
wcexGraph.hIcon = NULL;
wcexGraph.hCursor = LoadCursor(NULL, IDC_CROSS);
wcexGraph.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcexGraph.lpszClassName = "ETCPMonGraph";
wcexGraph.hIconSm = NULL;
RegisterClassExA(&wcexGraph);
/* Initialize Winsock */ /* Initialize Winsock */
WSADATA wsaData; WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
@ -104,7 +120,7 @@ HWND etcpmon_gui_create_window(struct etcpmon_app* app) {
0, 0,
ETCPMON_WINDOW_CLASS, ETCPMON_WINDOW_CLASS,
"ETCP Monitor", "ETCP Monitor",
WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT,
NULL, NULL, app->hInstance, app NULL, NULL, app->hInstance, app
@ -192,8 +208,55 @@ static void CreateControls(struct etcpmon_app* app) {
WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL | LBS_NOINTEGRALHEIGHT, WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL | LBS_NOINTEGRALHEIGHT,
x, y + 20, 300, 120, hWnd, (HMENU)IDC_LIST_CONNECTIONS, hInst, NULL); x, y + 20, 300, 120, hWnd, (HMENU)IDC_LIST_CONNECTIONS, hInst, NULL);
/* Graph area - between Connections and ETCP Metrics */
y += 140;
// app->hGraphWnd = CreateWindowA("STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
// 10, y, GRAPH_WIDTH, GRAPH_HEIGHT, hWnd, (HMENU)IDC_GRAPH, hInst, app);
app->hGraphWnd = CreateWindowExA(
WS_EX_CLIENTEDGE,
"ETCPMonGraph", "",
WS_CHILD | WS_VISIBLE | WS_BORDER,
10, y, GRAPH_WIDTH, GRAPH_HEIGHT,
hWnd, (HMENU)IDC_GRAPH, hInst, app);
/* Channel list - grid blocks below graph */
y += GRAPH_HEIGHT + 5;
const char* channel_short_names[] = {
"RTT-L", "RTT10", "RTT100", "Jitter",
"Retrns", "ACKs", "Inflght", "Bytes/s"
};
int block_w = 200;
int block_h = 50;
int cols = 4;
int spacing = 5;
for (int i = 0; i < GRAPH_METRICS_COUNT; i++) {
int col = i % cols;
int row = i / cols;
int bx = 10 + col * (block_w + spacing);
int by = y + row * (block_h + spacing);
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);
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);
}
int channel_grid_h = ((GRAPH_METRICS_COUNT + cols - 1) / cols) * (block_h + spacing) - spacing;
y += channel_grid_h;
/* ETCP Metrics group */ /* ETCP Metrics group */
y += 150; y = 485;
CreateWindowExA(0, "BUTTON", "ETCP Metrics", CreateWindowExA(0, "BUTTON", "ETCP Metrics",
WS_CHILD | WS_VISIBLE | BS_GROUPBOX, WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
x, y, 430, 220, hWnd, (HMENU)IDC_STATIC, hInst, NULL); x, y, 430, 220, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
@ -265,8 +328,8 @@ static void CreateControls(struct etcpmon_app* app) {
WS_CHILD | WS_VISIBLE | ES_READONLY, WS_CHILD | WS_VISIBLE | ES_READONLY,
mx + 85, my, 100, 20, hWnd, (HMENU)IDC_EDIT_ETCP_LINKS, hInst, NULL); mx + 85, my, 100, 20, hWnd, (HMENU)IDC_EDIT_ETCP_LINKS, hInst, NULL);
/* TUN Metrics group */ /* TUN Metrics group - right of ETCP Metrics */
x = 460; y = 240; x = 460; y = 485;
CreateWindowExA(0, "BUTTON", "TUN Metrics", CreateWindowExA(0, "BUTTON", "TUN Metrics",
WS_CHILD | WS_VISIBLE | BS_GROUPBOX, WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
x, y, 430, 140, hWnd, (HMENU)IDC_STATIC, hInst, NULL); x, y, 430, 140, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
@ -312,14 +375,14 @@ static void CreateControls(struct etcpmon_app* app) {
mx + 285, my, 100, 20, hWnd, (HMENU)IDC_EDIT_TUN_WRITE_ERRS, hInst, NULL); mx + 285, my, 100, 20, hWnd, (HMENU)IDC_EDIT_TUN_WRITE_ERRS, hInst, NULL);
/* Links list */ /* Links list */
y = 470; y = 715;
CreateWindowExA(0, "STATIC", "Links:", CreateWindowExA(0, "STATIC", "Links:",
WS_CHILD | WS_VISIBLE, WS_CHILD | WS_VISIBLE,
10, y, 100, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL); 10, y, 100, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL);
app->hListLinks = CreateWindowExA(WS_EX_CLIENTEDGE, "LISTBOX", "", app->hListLinks = CreateWindowExA(WS_EX_CLIENTEDGE, "LISTBOX", "",
WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL | LBS_NOINTEGRALHEIGHT, WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL | LBS_NOINTEGRALHEIGHT,
10, y + 20, 880, 120, hWnd, (HMENU)IDC_LIST_LINKS, hInst, NULL); 10, y + 20, 880, 100, hWnd, (HMENU)IDC_LIST_LINKS, hInst, NULL);
} }
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
@ -476,6 +539,21 @@ static void on_connected(void* user_data) {
etcpmon_client_request_list(app->client); etcpmon_client_request_list(app->client);
} }
void etcpmon_gui_update_graph(struct etcpmon_app* app, struct etcpmon_rsp_metrics* metrics) {
if (!app || !metrics) return;
if (app->hGraphWnd) {
InvalidateRect(app->hGraphWnd, NULL, FALSE);
}
if (app->client) {
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);
}
}
}
static void on_disconnected(void* user_data) { static void on_disconnected(void* user_data) {
@ -508,7 +586,10 @@ static void on_metrics(struct etcpmon_rsp_metrics* metrics,
struct etcpmon_link_metrics* links, struct etcpmon_link_metrics* links,
uint8_t links_count, void* user_data) { uint8_t links_count, void* user_data) {
struct etcpmon_app* app = (struct etcpmon_app*)user_data; struct etcpmon_app* app = (struct etcpmon_app*)user_data;
etcpmon_gui_update_metrics(app, metrics, links, links_count); if (app) {
etcpmon_gui_update_metrics(app, metrics, links, links_count);
etcpmon_client_add_to_history(app->client, metrics);
}
} }
static void on_error(const char* msg, void* user_data) { static void on_error(const char* msg, void* user_data) {
@ -615,25 +696,32 @@ void etcpmon_gui_update_metrics(struct etcpmon_app* app,
metrics->tun.write_errors); metrics->tun.write_errors);
/* Update links list */ /* Update links list */
SendMessage(app->hListLinks, LB_RESETCONTENT, 0, 0); if (app->hListLinks) {
for (uint8_t i = 0; i < links_count; i++) { SendMessage(app->hListLinks, LB_RESETCONTENT, 0, 0);
char display[256]; for (uint8_t i = 0; i < links_count; i++) {
snprintf(display, sizeof(display), char display[256];
"Link %u: Status=%s, EncErrors=%u, DecErrors=%u, " snprintf(display, sizeof(display),
"SendErr=%u, RecvErr=%u, BytesEnc=%llu, BytesDec=%llu, " "Link %u: Status=%s, EncErrors=%u, DecErrors=%u, "
"BW=%u Kbps, NATChg=%u", "SendErr=%u, RecvErr=%u, BytesEnc=%llu, BytesDec=%llu, "
links[i].local_link_id, "BW=%u Kbps, NATChg=%u",
links[i].status ? "UP" : "DOWN", links[i].local_link_id,
links[i].encrypt_errors, links[i].status ? "UP" : "DOWN",
links[i].decrypt_errors, links[i].encrypt_errors,
links[i].send_errors, links[i].decrypt_errors,
links[i].recv_errors, links[i].send_errors,
(unsigned long long)links[i].total_encrypted, links[i].recv_errors,
(unsigned long long)links[i].total_decrypted, (unsigned long long)links[i].total_encrypted,
links[i].bandwidth, (unsigned long long)links[i].total_decrypted,
links[i].nat_changes_count); links[i].bandwidth,
links[i].nat_changes_count);
SendMessageA(app->hListLinks, LB_ADDSTRING, 0, (LPARAM)display);
SendMessageA(app->hListLinks, LB_ADDSTRING, 0, (LPARAM)display);
}
}
if (app->hGraphWnd) {
InvalidateRect(app->hGraphWnd, NULL, FALSE);
UpdateWindow(app->hGraphWnd); // Force immediate paint
} }
} }
@ -658,6 +746,9 @@ void etcpmon_gui_clear_metrics(struct etcpmon_app* app) {
SetDlgItemTextA(app->hWndMain, IDC_EDIT_TUN_READ_ERRS, ""); SetDlgItemTextA(app->hWndMain, IDC_EDIT_TUN_READ_ERRS, "");
SetDlgItemTextA(app->hWndMain, IDC_EDIT_TUN_WRITE_ERRS, ""); SetDlgItemTextA(app->hWndMain, IDC_EDIT_TUN_WRITE_ERRS, "");
SendMessage(app->hListLinks, LB_RESETCONTENT, 0, 0); if (app->hListLinks) {
SendMessage(app->hListLinks, LB_RESETCONTENT, 0, 0);
}
SendMessage(app->hListConnections, LB_RESETCONTENT, 0, 0); SendMessage(app->hListConnections, LB_RESETCONTENT, 0, 0);
} }

53
tools/etcpmon/etcpmon_gui.h

@ -20,6 +20,9 @@ struct etcpmon_conn_info;
struct etcpmon_rsp_metrics; struct etcpmon_rsp_metrics;
struct etcpmon_link_metrics; struct etcpmon_link_metrics;
/* Graph constants */
#include "etcpmon_graph.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -59,6 +62,37 @@ extern "C" {
/* Link list control ID */ /* Link list control ID */
#define IDC_LIST_LINKS 400 #define IDC_LIST_LINKS 400
/* Graph control IDs */
#define IDC_GRAPH 500
#define IDC_STATIC_GRAPH 501
#define IDC_LIST_CHANNELS 502
/* Channel block IDs - 8 channels, 4 columns x 2 rows */
#define IDC_CH_NAME_0 510
#define IDC_CH_VALUE_0 520
#define IDC_CH_CHECK_0 530
#define IDC_CH_NAME_1 511
#define IDC_CH_VALUE_1 521
#define IDC_CH_CHECK_1 531
#define IDC_CH_NAME_2 512
#define IDC_CH_VALUE_2 522
#define IDC_CH_CHECK_2 532
#define IDC_CH_NAME_3 513
#define IDC_CH_VALUE_3 523
#define IDC_CH_CHECK_3 533
#define IDC_CH_NAME_4 514
#define IDC_CH_VALUE_4 524
#define IDC_CH_CHECK_4 534
#define IDC_CH_NAME_5 515
#define IDC_CH_VALUE_5 525
#define IDC_CH_CHECK_5 535
#define IDC_CH_NAME_6 516
#define IDC_CH_VALUE_6 526
#define IDC_CH_CHECK_6 536
#define IDC_CH_NAME_7 517
#define IDC_CH_VALUE_7 527
#define IDC_CH_CHECK_7 537
/* Main application structure */ /* Main application structure */
struct etcpmon_app { struct etcpmon_app {
HINSTANCE hInstance; HINSTANCE hInstance;
@ -95,6 +129,15 @@ struct etcpmon_app {
/* Links list */ /* Links list */
HWND hListLinks; HWND hListLinks;
/* Graph controls (managed by graph module) */
HWND hGraphWnd;
HWND hChannelName[GRAPH_METRICS_COUNT];
HWND hChannelValue[GRAPH_METRICS_COUNT];
HWND hChannelCheck[GRAPH_METRICS_COUNT];
int graph_cursor_x;
int graph_cursor_y;
int graph_cursor_active;
/* Client state - pointer to actual client structure */ /* Client state - pointer to actual client structure */
struct etcpmon_client* client; struct etcpmon_client* client;
@ -125,13 +168,17 @@ void etcpmon_gui_update_conn_list(struct etcpmon_app* app,
/* Update metrics display */ /* Update metrics display */
void etcpmon_gui_update_metrics(struct etcpmon_app* app, void etcpmon_gui_update_metrics(struct etcpmon_app* app,
struct etcpmon_rsp_metrics* metrics, struct etcpmon_rsp_metrics* metrics,
struct etcpmon_link_metrics* links, struct etcpmon_link_metrics* links,
uint8_t links_count); uint8_t links_count);
/* Clear metrics display */ /* Clear metrics display */
void etcpmon_gui_clear_metrics(struct etcpmon_app* app); void etcpmon_gui_clear_metrics(struct etcpmon_app* app);
/* Update graph with new metrics */
void etcpmon_gui_update_graph(struct etcpmon_app* app,
struct etcpmon_rsp_metrics* metrics);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

Loading…
Cancel
Save