Show top CPU core frequencies

This commit is contained in:
Codex 2026-06-23 21:19:55 +08:00
parent 3906daef09
commit c2af385cbc
4 changed files with 270 additions and 39 deletions

View File

@ -42,8 +42,19 @@ static void set_disk_text(ui_metrics_t *metrics, const char *disk_text)
snprintf(metrics->disk_text, sizeof(metrics->disk_text), "%s", disk_text); snprintf(metrics->disk_text, sizeof(metrics->disk_text), "%s", disk_text);
} }
static void set_cpu_core_text(ui_metrics_t *metrics, const char *core_text)
{
if (core_text == NULL || core_text[0] == '\0') {
metrics->cpu_core_text[0] = '\0';
return;
}
snprintf(metrics->cpu_core_text, sizeof(metrics->cpu_core_text), "%s", core_text);
}
static void set_optional_defaults(ui_metrics_t *metrics) static void set_optional_defaults(ui_metrics_t *metrics)
{ {
metrics->cpu_core_text[0] = '\0';
metrics->codex_5h_percent = -1; metrics->codex_5h_percent = -1;
metrics->codex_week_percent = -1; metrics->codex_week_percent = -1;
metrics->codex_5h_reset_mins = -1; metrics->codex_5h_reset_mins = -1;
@ -491,6 +502,105 @@ static bool parse_sys6_line(const char *line, ui_metrics_t *metrics)
return true; return true;
} }
static bool parse_sys7_line(const char *line, ui_metrics_t *metrics)
{
char buffer[640];
snprintf(buffer, sizeof(buffer), "%s", line);
char *cursor = buffer;
char *tag = strsep(&cursor, ",");
if (tag == NULL || strcmp(tag, "SYS7") != 0) {
return false;
}
int cpu = 0;
int cpu_mhz = 0;
int mem = 0;
int mem_used = 0;
int mem_total = 0;
int disk_count = 0;
int disk_percent = 0;
int disk_read = 0;
int disk_write = 0;
int rx = 0;
int tx = 0;
int codex_5h = -1;
int codex_week = -1;
int codex_5h_reset = -1;
int codex_week_reset = -1;
int codex_credits = -1;
int codex_today_tokens_m10 = -1;
int zhipu_status = 0;
int zhipu_5h = -1;
int zhipu_week = -1;
int zhipu_5h_reset = -1;
int zhipu_week_reset = -1;
if (!parse_int_token(&cursor, &cpu) ||
!parse_int_token(&cursor, &cpu_mhz) ||
!parse_int_token(&cursor, &mem) ||
!parse_int_token(&cursor, &mem_used) ||
!parse_int_token(&cursor, &mem_total) ||
!parse_int_token(&cursor, &disk_count) ||
!parse_int_token(&cursor, &disk_percent) ||
!parse_int_token(&cursor, &disk_read) ||
!parse_int_token(&cursor, &disk_write) ||
!parse_int_token(&cursor, &rx) ||
!parse_int_token(&cursor, &tx) ||
!parse_int_token(&cursor, &codex_5h) ||
!parse_int_token(&cursor, &codex_week) ||
!parse_int_token(&cursor, &codex_5h_reset) ||
!parse_int_token(&cursor, &codex_week_reset) ||
!parse_int_token(&cursor, &codex_credits) ||
!parse_int_token(&cursor, &codex_today_tokens_m10)) {
return false;
}
char *daily_text = strsep(&cursor, ",");
if (daily_text == NULL ||
!parse_int_token(&cursor, &zhipu_status) ||
!parse_int_token(&cursor, &zhipu_5h) ||
!parse_int_token(&cursor, &zhipu_week) ||
!parse_int_token(&cursor, &zhipu_5h_reset) ||
!parse_int_token(&cursor, &zhipu_week_reset)) {
return false;
}
char *core_text = strsep(&cursor, ",");
if (core_text == NULL) {
return false;
}
metrics->cpu_percent = clamp_percent(cpu);
metrics->cpu_mhz = cpu_mhz;
metrics->memory_percent = clamp_percent(mem);
metrics->memory_used_mb = mem_used;
metrics->memory_total_mb = mem_total;
metrics->disk_count = disk_count;
metrics->disk_percent = clamp_percent(disk_percent);
metrics->disk_read_kbps = disk_read;
metrics->disk_write_kbps = disk_write;
metrics->rx_kbps = rx;
metrics->tx_kbps = tx;
metrics->codex_5h_percent = (codex_5h >= 0) ? clamp_percent(codex_5h) : -1;
metrics->codex_week_percent = (codex_week >= 0) ? clamp_percent(codex_week) : -1;
metrics->codex_5h_reset_mins = codex_5h_reset;
metrics->codex_week_reset_mins = codex_week_reset;
metrics->codex_reset_credits = codex_credits;
metrics->codex_today_tokens_m10 = codex_today_tokens_m10;
parse_codex_daily_bars(metrics, daily_text);
metrics->zhipu_status = zhipu_status;
metrics->zhipu_5h_percent = (zhipu_5h >= 0) ? clamp_percent(zhipu_5h) : -1;
metrics->zhipu_week_percent = (zhipu_week >= 0) ? clamp_percent(zhipu_week) : -1;
metrics->zhipu_5h_reset_mins = zhipu_5h_reset;
metrics->zhipu_week_reset_mins = zhipu_week_reset;
metrics->zhipu_quota_percent = metrics->zhipu_5h_percent;
metrics->zhipu_latency_ms = -1;
set_cpu_core_text(metrics, core_text);
set_disk_text(metrics, cursor);
return true;
}
static bool parse_legacy_sys_line(const char *line, ui_metrics_t *metrics) static bool parse_legacy_sys_line(const char *line, ui_metrics_t *metrics)
{ {
int cpu = 0; int cpu = 0;
@ -527,6 +637,10 @@ bool metrics_parse_line(const char *line, ui_metrics_t *metrics)
memset(metrics, 0, sizeof(*metrics)); memset(metrics, 0, sizeof(*metrics));
set_optional_defaults(metrics); set_optional_defaults(metrics);
if (strncmp(line, "SYS7,", 5) == 0) {
return parse_sys7_line(line, metrics);
}
if (strncmp(line, "SYS6,", 5) == 0) { if (strncmp(line, "SYS6,", 5) == 0) {
return parse_sys6_line(line, metrics); return parse_sys6_line(line, metrics);
} }

View File

@ -10,12 +10,13 @@
#define CPU_HISTORY_POINTS 64 #define CPU_HISTORY_POINTS 64
#define CPU_CHART_W 184 #define CPU_CHART_W 184
#define CPU_CHART_H 78 #define CPU_CHART_H 78
#define CPU_TOP_CORE_COUNT 4
#define HALO_LABEL_COUNT 8 #define HALO_LABEL_COUNT 8
#define DISK_ROW_COUNT 4 #define DISK_ROW_COUNT 4
#define CODEX_DAILY_BAR_W 8 #define CODEX_DAILY_BAR_W 8
#define CODEX_DAILY_BAR_GAP 5 #define CODEX_DAILY_BAR_GAP 5
#define CODEX_DAILY_BAR_BASE_Y 292 #define CODEX_DAILY_BAR_BASE_Y 292
#define CODEX_DAILY_BAR_MAX_H 26 #define CODEX_DAILY_BAR_MAX_H 46
typedef enum { typedef enum {
CONN_USB = 0, CONN_USB = 0,
@ -29,6 +30,7 @@ static lv_obj_t *s_conn_label;
static lv_obj_t *s_env_label; static lv_obj_t *s_env_label;
static lv_obj_t *s_cpu_value_label; static lv_obj_t *s_cpu_value_label;
static lv_obj_t *s_cpu_freq_label; static lv_obj_t *s_cpu_freq_label;
static lv_obj_t *s_cpu_core_labels[CPU_TOP_CORE_COUNT];
static lv_obj_t *s_cpu_value_halo[HALO_LABEL_COUNT]; static lv_obj_t *s_cpu_value_halo[HALO_LABEL_COUNT];
static lv_obj_t *s_cpu_freq_halo[HALO_LABEL_COUNT]; static lv_obj_t *s_cpu_freq_halo[HALO_LABEL_COUNT];
static lv_obj_t *s_cpu_line; static lv_obj_t *s_cpu_line;
@ -49,6 +51,7 @@ static lv_obj_t *s_codex_5h_value_label;
static lv_obj_t *s_codex_week_value_label; static lv_obj_t *s_codex_week_value_label;
static lv_obj_t *s_codex_5h_reset_label; static lv_obj_t *s_codex_5h_reset_label;
static lv_obj_t *s_codex_week_reset_label; static lv_obj_t *s_codex_week_reset_label;
static lv_obj_t *s_codex_today_label;
static lv_obj_t *s_codex_5h_bar; static lv_obj_t *s_codex_5h_bar;
static lv_obj_t *s_codex_week_bar; static lv_obj_t *s_codex_week_bar;
static lv_obj_t *s_codex_daily_bars[UI_CODEX_DAILY_BAR_COUNT]; static lv_obj_t *s_codex_daily_bars[UI_CODEX_DAILY_BAR_COUNT];
@ -276,11 +279,24 @@ static void format_percent_compact(int percent, char *out, size_t out_size)
} }
} }
static void format_tokens_m10(int tokens_m10, char *out, size_t out_size)
{
if (tokens_m10 < 0) {
snprintf(out, out_size, "--");
} else if (tokens_m10 >= 10000) {
snprintf(out, out_size, "%d.%dB", tokens_m10 / 10000, (tokens_m10 % 10000) / 1000);
} else if (tokens_m10 >= 1000) {
snprintf(out, out_size, "%dM", (tokens_m10 + 5) / 10);
} else {
snprintf(out, out_size, "%d.%dM", tokens_m10 / 10, tokens_m10 % 10);
}
}
static const char *zhipu_status_text(int status) static const char *zhipu_status_text(int status)
{ {
switch (status) { switch (status) {
case 1: case 1:
return "KEY"; return "KEY OK";
case 2: case 2:
return "ERR"; return "ERR";
default: default:
@ -448,6 +464,9 @@ static void update_codex_panel(const ui_metrics_t *metrics)
lv_label_set_text(s_codex_5h_reset_label, reset_5h); lv_label_set_text(s_codex_5h_reset_label, reset_5h);
lv_label_set_text(s_codex_week_reset_label, reset_week); lv_label_set_text(s_codex_week_reset_label, reset_week);
format_tokens_m10(metrics->codex_today_tokens_m10, value, sizeof(value));
lv_label_set_text(s_codex_today_label, value);
for (int i = 0; i < UI_CODEX_DAILY_BAR_COUNT; i++) { for (int i = 0; i < UI_CODEX_DAILY_BAR_COUNT; i++) {
lv_obj_t *bar = s_codex_daily_bars[i]; lv_obj_t *bar = s_codex_daily_bars[i];
if (bar == NULL) { if (bar == NULL) {
@ -475,7 +494,7 @@ static void update_zhipu_panel(const ui_metrics_t *metrics)
char reset[12]; char reset[12];
char text[24]; char text[24];
snprintf(text, sizeof(text), "STAT %s", zhipu_status_text(metrics->zhipu_status)); snprintf(text, sizeof(text), "%s", zhipu_status_text(metrics->zhipu_status));
lv_label_set_text(s_zhipu_status_label, text); lv_label_set_text(s_zhipu_status_label, text);
format_percent_compact(metrics->zhipu_5h_percent, value, sizeof(value)); format_percent_compact(metrics->zhipu_5h_percent, value, sizeof(value));
@ -491,6 +510,31 @@ static void update_zhipu_panel(const ui_metrics_t *metrics)
lv_label_set_text(s_zhipu_week_reset_label, reset); lv_label_set_text(s_zhipu_week_reset_label, reset);
} }
static void update_cpu_core_labels(const char *core_text)
{
char buffer[UI_MAX_CPU_CORE_TEXT];
int row = 0;
snprintf(buffer, sizeof(buffer), "%s", core_text != NULL ? core_text : "");
char *cursor = buffer;
char *token = strtok(cursor, " ");
while (token != NULL && row < CPU_TOP_CORE_COUNT) {
for (char *p = token; *p != '\0'; p++) {
if (*p == ':') {
*p = ' ';
}
}
lv_label_set_text(s_cpu_core_labels[row], token);
lv_obj_clear_flag(s_cpu_core_labels[row], LV_OBJ_FLAG_HIDDEN);
row++;
token = strtok(NULL, " ");
}
for (int i = row; i < CPU_TOP_CORE_COUNT; i++) {
lv_obj_add_flag(s_cpu_core_labels[i], LV_OBJ_FLAG_HIDDEN);
}
}
static void update_net_line(const ui_metrics_t *metrics) static void update_net_line(const ui_metrics_t *metrics)
{ {
char rx[12]; char rx[12];
@ -557,6 +601,15 @@ void ui_screen_create(void)
lv_obj_set_width(s_cpu_freq_label, 128); lv_obj_set_width(s_cpu_freq_label, 128);
lv_obj_align(s_cpu_freq_label, LV_ALIGN_TOP_LEFT, 8, 72); lv_obj_align(s_cpu_freq_label, LV_ALIGN_TOP_LEFT, 8, 72);
for (int i = 0; i < CPU_TOP_CORE_COUNT; i++) {
s_cpu_core_labels[i] = make_label(screen, "--", &lv_font_montserrat_14);
lv_label_set_long_mode(s_cpu_core_labels[i], LV_LABEL_LONG_CLIP);
lv_obj_set_width(s_cpu_core_labels[i], 58);
lv_obj_set_style_text_align(s_cpu_core_labels[i], LV_TEXT_ALIGN_RIGHT, 0);
lv_obj_align(s_cpu_core_labels[i], LV_ALIGN_TOP_LEFT, 138, 28 + i * 16);
lv_obj_add_flag(s_cpu_core_labels[i], LV_OBJ_FLAG_HIDDEN);
}
lv_obj_t *memory_title = make_label(screen, "MEM", &lv_font_montserrat_14); lv_obj_t *memory_title = make_label(screen, "MEM", &lv_font_montserrat_14);
lv_obj_align(memory_title, LV_ALIGN_TOP_LEFT, 208, 8); lv_obj_align(memory_title, LV_ALIGN_TOP_LEFT, 208, 8);
make_label_halo(screen, s_memory_value_halo, "--%", &lv_font_montserrat_20, 50, make_label_halo(screen, s_memory_value_halo, "--%", &lv_font_montserrat_20, 50,
@ -607,32 +660,37 @@ void ui_screen_create(void)
lv_obj_t *codex_title = make_label(screen, "CODEX", &lv_font_montserrat_14); lv_obj_t *codex_title = make_label(screen, "CODEX", &lv_font_montserrat_14);
lv_obj_align(codex_title, LV_ALIGN_TOP_LEFT, 8, 184); lv_obj_align(codex_title, LV_ALIGN_TOP_LEFT, 8, 184);
s_codex_today_label = make_label(screen, "--", &lv_font_montserrat_14);
lv_label_set_long_mode(s_codex_today_label, LV_LABEL_LONG_CLIP);
lv_obj_set_width(s_codex_today_label, 70);
lv_obj_set_style_text_align(s_codex_today_label, LV_TEXT_ALIGN_RIGHT, 0);
lv_obj_align(s_codex_today_label, LV_ALIGN_TOP_LEFT, 126, 184);
s_codex_5h_label = make_label(screen, "5H", &lv_font_montserrat_14); s_codex_5h_label = make_label(screen, "5H", &lv_font_montserrat_14);
lv_obj_set_width(s_codex_5h_label, 28); lv_obj_set_width(s_codex_5h_label, 28);
lv_obj_align(s_codex_5h_label, LV_ALIGN_TOP_LEFT, 8, 198); lv_obj_align(s_codex_5h_label, LV_ALIGN_TOP_LEFT, 8, 204);
s_codex_5h_bar = make_bar(screen, 78, 8); s_codex_5h_bar = make_bar(screen, 78, 8);
lv_obj_align(s_codex_5h_bar, LV_ALIGN_TOP_LEFT, 36, 202); lv_obj_align(s_codex_5h_bar, LV_ALIGN_TOP_LEFT, 36, 208);
s_codex_5h_value_label = make_label(screen, "--", &lv_font_montserrat_14); s_codex_5h_value_label = make_label(screen, "--", &lv_font_montserrat_14);
lv_obj_set_width(s_codex_5h_value_label, 38); lv_obj_set_width(s_codex_5h_value_label, 38);
lv_obj_align(s_codex_5h_value_label, LV_ALIGN_TOP_LEFT, 118, 198); lv_obj_align(s_codex_5h_value_label, LV_ALIGN_TOP_LEFT, 118, 204);
s_codex_5h_reset_label = make_label(screen, "--", &lv_font_montserrat_14); s_codex_5h_reset_label = make_label(screen, "--", &lv_font_montserrat_14);
lv_label_set_long_mode(s_codex_5h_reset_label, LV_LABEL_LONG_CLIP); lv_label_set_long_mode(s_codex_5h_reset_label, LV_LABEL_LONG_CLIP);
lv_obj_set_width(s_codex_5h_reset_label, 38); lv_obj_set_width(s_codex_5h_reset_label, 38);
lv_obj_align(s_codex_5h_reset_label, LV_ALIGN_TOP_LEFT, 158, 198); lv_obj_align(s_codex_5h_reset_label, LV_ALIGN_TOP_LEFT, 158, 204);
s_codex_week_label = make_label(screen, "WK", &lv_font_montserrat_14); s_codex_week_label = make_label(screen, "WK", &lv_font_montserrat_14);
lv_obj_set_width(s_codex_week_label, 28); lv_obj_set_width(s_codex_week_label, 28);
lv_obj_align(s_codex_week_label, LV_ALIGN_TOP_LEFT, 8, 216); lv_obj_align(s_codex_week_label, LV_ALIGN_TOP_LEFT, 8, 224);
s_codex_week_bar = make_bar(screen, 78, 8); s_codex_week_bar = make_bar(screen, 78, 8);
lv_obj_align(s_codex_week_bar, LV_ALIGN_TOP_LEFT, 36, 220); lv_obj_align(s_codex_week_bar, LV_ALIGN_TOP_LEFT, 36, 228);
s_codex_week_value_label = make_label(screen, "--", &lv_font_montserrat_14); s_codex_week_value_label = make_label(screen, "--", &lv_font_montserrat_14);
lv_obj_set_width(s_codex_week_value_label, 38); lv_obj_set_width(s_codex_week_value_label, 38);
lv_obj_align(s_codex_week_value_label, LV_ALIGN_TOP_LEFT, 118, 216); lv_obj_align(s_codex_week_value_label, LV_ALIGN_TOP_LEFT, 118, 224);
s_codex_week_reset_label = make_label(screen, "--", &lv_font_montserrat_14); s_codex_week_reset_label = make_label(screen, "--", &lv_font_montserrat_14);
lv_label_set_long_mode(s_codex_week_reset_label, LV_LABEL_LONG_CLIP); lv_label_set_long_mode(s_codex_week_reset_label, LV_LABEL_LONG_CLIP);
lv_obj_set_width(s_codex_week_reset_label, 38); lv_obj_set_width(s_codex_week_reset_label, 38);
lv_obj_align(s_codex_week_reset_label, LV_ALIGN_TOP_LEFT, 158, 216); lv_obj_align(s_codex_week_reset_label, LV_ALIGN_TOP_LEFT, 158, 224);
make_rule(screen, 8, CODEX_DAILY_BAR_BASE_Y, 188, 1); make_rule(screen, 8, CODEX_DAILY_BAR_BASE_Y, 188, 1);
for (int i = 0; i < UI_CODEX_DAILY_BAR_COUNT; i++) { for (int i = 0; i < UI_CODEX_DAILY_BAR_COUNT; i++) {
@ -652,36 +710,36 @@ void ui_screen_create(void)
lv_obj_t *zhipu_title = make_label(screen, "ZHIPU", &lv_font_montserrat_14); lv_obj_t *zhipu_title = make_label(screen, "ZHIPU", &lv_font_montserrat_14);
lv_obj_align(zhipu_title, LV_ALIGN_TOP_LEFT, 208, 184); lv_obj_align(zhipu_title, LV_ALIGN_TOP_LEFT, 208, 184);
s_zhipu_status_label = make_label(screen, "STAT WAIT", &lv_font_montserrat_14); s_zhipu_status_label = make_label(screen, "WAIT", &lv_font_montserrat_14);
lv_obj_set_width(s_zhipu_status_label, 96); lv_obj_set_width(s_zhipu_status_label, 78);
lv_obj_set_style_text_align(s_zhipu_status_label, LV_TEXT_ALIGN_RIGHT, 0); lv_obj_set_style_text_align(s_zhipu_status_label, LV_TEXT_ALIGN_RIGHT, 0);
lv_obj_align(s_zhipu_status_label, LV_ALIGN_TOP_RIGHT, -8, 184); lv_obj_align(s_zhipu_status_label, LV_ALIGN_TOP_RIGHT, -8, 184);
s_zhipu_5h_label = make_label(screen, "5H", &lv_font_montserrat_14); s_zhipu_5h_label = make_label(screen, "5H", &lv_font_montserrat_14);
lv_obj_set_width(s_zhipu_5h_label, 28); lv_obj_set_width(s_zhipu_5h_label, 28);
lv_obj_align(s_zhipu_5h_label, LV_ALIGN_TOP_LEFT, 208, 198); lv_obj_align(s_zhipu_5h_label, LV_ALIGN_TOP_LEFT, 208, 204);
s_zhipu_5h_bar = make_bar(screen, 78, 8); s_zhipu_5h_bar = make_bar(screen, 78, 8);
lv_obj_align(s_zhipu_5h_bar, LV_ALIGN_TOP_LEFT, 236, 202); lv_obj_align(s_zhipu_5h_bar, LV_ALIGN_TOP_LEFT, 236, 208);
s_zhipu_5h_value_label = make_label(screen, "--", &lv_font_montserrat_14); s_zhipu_5h_value_label = make_label(screen, "--", &lv_font_montserrat_14);
lv_obj_set_width(s_zhipu_5h_value_label, 38); lv_obj_set_width(s_zhipu_5h_value_label, 38);
lv_obj_align(s_zhipu_5h_value_label, LV_ALIGN_TOP_LEFT, 318, 198); lv_obj_align(s_zhipu_5h_value_label, LV_ALIGN_TOP_LEFT, 318, 204);
s_zhipu_5h_reset_label = make_label(screen, "--", &lv_font_montserrat_14); s_zhipu_5h_reset_label = make_label(screen, "--", &lv_font_montserrat_14);
lv_label_set_long_mode(s_zhipu_5h_reset_label, LV_LABEL_LONG_CLIP); lv_label_set_long_mode(s_zhipu_5h_reset_label, LV_LABEL_LONG_CLIP);
lv_obj_set_width(s_zhipu_5h_reset_label, 38); lv_obj_set_width(s_zhipu_5h_reset_label, 38);
lv_obj_align(s_zhipu_5h_reset_label, LV_ALIGN_TOP_LEFT, 358, 198); lv_obj_align(s_zhipu_5h_reset_label, LV_ALIGN_TOP_LEFT, 358, 204);
s_zhipu_week_label = make_label(screen, "WK", &lv_font_montserrat_14); s_zhipu_week_label = make_label(screen, "WK", &lv_font_montserrat_14);
lv_obj_set_width(s_zhipu_week_label, 28); lv_obj_set_width(s_zhipu_week_label, 28);
lv_obj_align(s_zhipu_week_label, LV_ALIGN_TOP_LEFT, 208, 216); lv_obj_align(s_zhipu_week_label, LV_ALIGN_TOP_LEFT, 208, 224);
s_zhipu_week_bar = make_bar(screen, 78, 8); s_zhipu_week_bar = make_bar(screen, 78, 8);
lv_obj_align(s_zhipu_week_bar, LV_ALIGN_TOP_LEFT, 236, 220); lv_obj_align(s_zhipu_week_bar, LV_ALIGN_TOP_LEFT, 236, 228);
s_zhipu_week_value_label = make_label(screen, "--", &lv_font_montserrat_14); s_zhipu_week_value_label = make_label(screen, "--", &lv_font_montserrat_14);
lv_obj_set_width(s_zhipu_week_value_label, 38); lv_obj_set_width(s_zhipu_week_value_label, 38);
lv_obj_align(s_zhipu_week_value_label, LV_ALIGN_TOP_LEFT, 318, 216); lv_obj_align(s_zhipu_week_value_label, LV_ALIGN_TOP_LEFT, 318, 224);
s_zhipu_week_reset_label = make_label(screen, "--", &lv_font_montserrat_14); s_zhipu_week_reset_label = make_label(screen, "--", &lv_font_montserrat_14);
lv_label_set_long_mode(s_zhipu_week_reset_label, LV_LABEL_LONG_CLIP); lv_label_set_long_mode(s_zhipu_week_reset_label, LV_LABEL_LONG_CLIP);
lv_obj_set_width(s_zhipu_week_reset_label, 38); lv_obj_set_width(s_zhipu_week_reset_label, 38);
lv_obj_align(s_zhipu_week_reset_label, LV_ALIGN_TOP_LEFT, 358, 216); lv_obj_align(s_zhipu_week_reset_label, LV_ALIGN_TOP_LEFT, 358, 224);
} }
void ui_screen_tick(void) void ui_screen_tick(void)
@ -781,6 +839,7 @@ void ui_screen_update_metrics_from_source(const ui_metrics_t *metrics, const cha
snprintf(text, sizeof(text), "--GHz"); snprintf(text, sizeof(text), "--GHz");
} }
set_label_with_halo(s_cpu_freq_label, s_cpu_freq_halo, text); set_label_with_halo(s_cpu_freq_label, s_cpu_freq_halo, text);
update_cpu_core_labels(metrics->cpu_core_text);
set_cpu_curve(metrics->cpu_percent); set_cpu_curve(metrics->cpu_percent);
snprintf(text, sizeof(text), "%d%%", clamp_percent(metrics->memory_percent)); snprintf(text, sizeof(text), "%d%%", clamp_percent(metrics->memory_percent));

View File

@ -5,6 +5,7 @@ extern "C" {
#endif #endif
#define UI_MAX_DISK_TEXT 128 #define UI_MAX_DISK_TEXT 128
#define UI_MAX_CPU_CORE_TEXT 48
#define UI_CODEX_DAILY_BAR_COUNT 14 #define UI_CODEX_DAILY_BAR_COUNT 14
typedef struct { typedef struct {
@ -34,6 +35,7 @@ typedef struct {
int zhipu_week_percent; int zhipu_week_percent;
int zhipu_5h_reset_mins; int zhipu_5h_reset_mins;
int zhipu_week_reset_mins; int zhipu_week_reset_mins;
char cpu_core_text[UI_MAX_CPU_CORE_TEXT];
char disk_text[UI_MAX_DISK_TEXT]; char disk_text[UI_MAX_DISK_TEXT];
} ui_metrics_t; } ui_metrics_t;

View File

@ -122,7 +122,9 @@ class CpuFrequencyReader:
def __init__(self) -> None: def __init__(self) -> None:
self.pdh = None self.pdh = None
self.query = ctypes.c_void_p() self.query = ctypes.c_void_p()
self.counter = ctypes.c_void_p() self.total_counter = ctypes.c_void_p()
self.core_counters: list[tuple[int, ctypes.c_void_p]] = []
self.base_mhz = self._base_mhz()
self.available = False self.available = False
if sys.platform != "win32": if sys.platform != "win32":
@ -159,10 +161,20 @@ class CpuFrequencyReader:
return return
if add_counter( if add_counter(
self.query, self.query,
r"\Processor Information(_Total)\Actual Frequency", r"\Processor Information(_Total)\% Processor Performance",
None, None,
ctypes.byref(self.counter), ctypes.byref(self.total_counter),
) != 0: ) != 0:
self.total_counter = ctypes.c_void_p()
logical_count = psutil.cpu_count(logical=True) or 0
for index in range(logical_count):
counter = ctypes.c_void_p()
path = rf"\Processor Information(0,{index})\% Processor Performance"
if add_counter(self.query, path, None, ctypes.byref(counter)) == 0:
self.core_counters.append((index, counter))
if not self.total_counter and not self.core_counters:
return return
self.pdh.PdhCollectQueryData(self.query) self.pdh.PdhCollectQueryData(self.query)
@ -170,22 +182,66 @@ class CpuFrequencyReader:
except OSError: except OSError:
self.available = False self.available = False
def read_mhz(self) -> int: @staticmethod
if self.available and self.pdh is not None: def _base_mhz() -> int:
value = _PdhCounterValue() freq = psutil.cpu_freq()
counter_type = wintypes.DWORD() if freq and freq.max:
return int(round(freq.max))
if freq and freq.current:
return int(round(freq.current))
return 0
@staticmethod
def _format_core_mhz(index: int, mhz: int) -> str:
ghz10 = max(0, int(round(mhz / 100)))
return f"{index:02d}:{ghz10 // 10}.{ghz10 % 10}"
def _read_counter(self, counter: ctypes.c_void_p) -> float:
if self.pdh is None or not counter:
return -1.0
value = _PdhCounterValue()
counter_type = wintypes.DWORD()
rc = self.pdh.PdhGetFormattedCounterValue(
counter,
0x00000200,
ctypes.byref(counter_type),
ctypes.byref(value),
)
if rc != 0 or value.status != 0 or value.double_value < 0:
return -1.0
return value.double_value
def _perf_to_mhz(self, performance_percent: float) -> int:
if self.base_mhz <= 0 or performance_percent < 0:
return 0
return int(round(self.base_mhz * performance_percent / 100))
def read(self) -> tuple[int, str]:
if self.available and self.pdh is not None and self.base_mhz > 0:
if self.pdh.PdhCollectQueryData(self.query) == 0: if self.pdh.PdhCollectQueryData(self.query) == 0:
rc = self.pdh.PdhGetFormattedCounterValue( total_mhz = self._perf_to_mhz(self._read_counter(self.total_counter))
self.counter, core_values: list[tuple[int, int]] = []
0x00000200, for index, counter in self.core_counters:
ctypes.byref(counter_type), mhz = self._perf_to_mhz(self._read_counter(counter))
ctypes.byref(value), if mhz > 0:
core_values.append((index, mhz))
core_values.sort(key=lambda item: (-item[1], item[0]))
top_text = " ".join(
self._format_core_mhz(index, mhz)
for index, mhz in core_values[:4]
) )
if rc == 0 and value.status == 0 and value.double_value > 0: if total_mhz <= 0 and core_values:
return int(round(value.double_value)) total_mhz = core_values[0][1]
if total_mhz > 0:
return total_mhz, top_text
freq = psutil.cpu_freq() freq = psutil.cpu_freq()
return int(round(freq.current)) if freq and freq.current else 0 return (int(round(freq.current)) if freq and freq.current else 0), ""
def read_mhz(self) -> int:
return self.read()[0]
class LogicalDiskRateReader: class LogicalDiskRateReader:
@ -695,7 +751,7 @@ class MetricsSampler:
disk_rates = self.disk_rates.read_rates() disk_rates = self.disk_rates.read_rates()
cpu = self.cpu_percent.read_percent() cpu = self.cpu_percent.read_percent()
cpu_mhz = self.cpu_frequency.read_mhz() cpu_mhz, cpu_core_text = self.cpu_frequency.read()
mem_info = psutil.virtual_memory() mem_info = psutil.virtual_memory()
mem_percent = clamp_percent(mem_info.percent) mem_percent = clamp_percent(mem_info.percent)
@ -708,12 +764,12 @@ class MetricsSampler:
zhipu_status, zhipu_5h, zhipu_week, zhipu_5h_reset, zhipu_week_reset = self.zhipu.read() zhipu_status, zhipu_5h, zhipu_week, zhipu_5h_reset, zhipu_week_reset = self.zhipu.read()
return ( return (
f"SYS6,{cpu},{cpu_mhz},{mem_percent},{mem_used_mb},{mem_total_mb}," f"SYS7,{cpu},{cpu_mhz},{mem_percent},{mem_used_mb},{mem_total_mb},"
f"{disk_count},{disk_percent},{disk_read_kbps},{disk_write_kbps}," f"{disk_count},{disk_percent},{disk_read_kbps},{disk_write_kbps},"
f"{rx_kbps},{tx_kbps}," f"{rx_kbps},{tx_kbps},"
f"{codex_5h},{codex_week},{codex_5h_reset},{codex_week_reset},{codex_credits},{codex_today_tokens_m10}," f"{codex_5h},{codex_week},{codex_5h_reset},{codex_week_reset},{codex_credits},{codex_today_tokens_m10},"
f"{codex_daily_bars}," f"{codex_daily_bars},"
f"{zhipu_status},{zhipu_5h},{zhipu_week},{zhipu_5h_reset},{zhipu_week_reset},{disk_text}" f"{zhipu_status},{zhipu_5h},{zhipu_week},{zhipu_5h_reset},{zhipu_week_reset},{cpu_core_text},{disk_text}"
) )