dinh dang so kieu Vietnam
This commit is contained in:
@@ -64,6 +64,8 @@ long g_reasoning_tokens= 0;
|
||||
long g_cached_tokens = 0;
|
||||
long g_failed = 0;
|
||||
|
||||
long g_tokens30d = 0; // tổng token 30 ngày (range=30d)
|
||||
|
||||
AccountStat g_accounts[8];
|
||||
int g_accountCount = 0;
|
||||
ModelStat g_models[6];
|
||||
@@ -208,17 +210,47 @@ void tft_showFetchError() {
|
||||
tftUnlock();
|
||||
}
|
||||
|
||||
// ── Footer hiển thị IP để truy cập trang Config ──
|
||||
// ── Footer hiển thị token 30 ngày + IP để truy cập trang Config ──
|
||||
void drawFooter() {
|
||||
// Vẽ thanh footer ở 16px dưới cùng
|
||||
tft.drawFastHLine(0, SCREEN_HEIGHT-17, SCREEN_WIDTH, COLOR_HEADER);
|
||||
tft.setTextSize(1); tft.setTextColor(COLOR_CYAN);
|
||||
tft.setCursor(6, SCREEN_HEIGHT-13);
|
||||
int footerTop = SCREEN_HEIGHT - 32;
|
||||
tft.drawFastHLine(0, footerTop - 1, SCREEN_WIDTH, COLOR_HEADER);
|
||||
|
||||
// Dòng 1: tổng token 30 ngày
|
||||
char buf[40];
|
||||
tft.setTextSize(1); tft.setTextColor(COLOR_YELLOW);
|
||||
tft.setCursor(6, footerTop + 3);
|
||||
tft.print("30d Tokens: ");
|
||||
tft.setTextColor(COLOR_ORANGE);
|
||||
fmtNum(g_tokens30d, buf);
|
||||
tft.print(buf);
|
||||
|
||||
// Dòng 2: IP config
|
||||
tft.setTextColor(COLOR_CYAN);
|
||||
tft.setCursor(6, footerTop + 15);
|
||||
tft.print("Config: http://");
|
||||
tft.setTextColor(COLOR_ORANGE);
|
||||
tft.print(WiFi.localIP().toString());
|
||||
}
|
||||
|
||||
// ── Format số kiểu EU: dấu . phân cách hàng nghìn, không thập phân ──
|
||||
void fmtNum(long n, char* out) {
|
||||
char tmp[20];
|
||||
bool neg = n < 0;
|
||||
if (neg) n = -n;
|
||||
int dig = 0, i = 0;
|
||||
if (n == 0) { tmp[i++] = '0'; dig = 1; }
|
||||
while (n > 0) {
|
||||
if (dig > 0 && dig % 3 == 0) tmp[i++] = '.';
|
||||
tmp[i++] = '0' + (n % 10);
|
||||
n /= 10;
|
||||
dig++;
|
||||
}
|
||||
if (neg) tmp[i++] = '-';
|
||||
int j = 0;
|
||||
while (i > 0) out[j++] = tmp[--i];
|
||||
out[j] = '\0';
|
||||
}
|
||||
|
||||
// ── Vẽ 4 trang dữ liệu ───────────────────────────
|
||||
void drawPage0() {
|
||||
// Tổng quan
|
||||
@@ -230,13 +262,13 @@ void drawPage0() {
|
||||
tft.setTextSize(1); tft.setTextColor(COLOR_CYAN);
|
||||
tft.setCursor(8,48); tft.print("Requests");
|
||||
tft.setTextSize(4); tft.setTextColor(COLOR_GREEN);
|
||||
snprintf(buf,sizeof(buf),"%ld",g_requests);
|
||||
fmtNum(g_requests, buf);
|
||||
tft.setCursor(8,62); tft.print(buf);
|
||||
|
||||
tft.setTextSize(1); tft.setTextColor(COLOR_CYAN);
|
||||
tft.setCursor(8,108); tft.print("Failed");
|
||||
tft.setTextSize(2); tft.setTextColor(g_failed > 0 ? COLOR_RED : COLOR_TEXT);
|
||||
snprintf(buf,sizeof(buf),"%ld",g_failed);
|
||||
fmtNum(g_failed, buf);
|
||||
tft.setCursor(8,120); tft.print(buf);
|
||||
|
||||
// Cột phải: token breakdown
|
||||
@@ -246,32 +278,34 @@ void drawPage0() {
|
||||
tft.setTextSize(1); tft.setTextColor(COLOR_YELLOW);
|
||||
tft.setCursor(x,y); tft.print("Total Tokens:"); y+=14;
|
||||
tft.setTextSize(2); tft.setTextColor(COLOR_ORANGE);
|
||||
snprintf(buf,sizeof(buf),"%ld",g_total_tokens);
|
||||
fmtNum(g_total_tokens, buf);
|
||||
tft.setCursor(x,y); tft.print(buf); y+=24;
|
||||
|
||||
tft.setTextSize(1);
|
||||
tft.setTextColor(COLOR_CYAN); tft.setCursor(x,y); tft.print("Input : ");
|
||||
tft.setTextColor(COLOR_TEXT); snprintf(buf,sizeof(buf),"%ld",g_input_tokens); tft.println(buf); y+=14;
|
||||
tft.setTextColor(COLOR_TEXT); fmtNum(g_input_tokens, buf); tft.println(buf); y+=14;
|
||||
tft.setTextColor(COLOR_CYAN); tft.setCursor(x,y); tft.print("Output : ");
|
||||
tft.setTextColor(COLOR_TEXT); snprintf(buf,sizeof(buf),"%ld",g_output_tokens); tft.println(buf); y+=14;
|
||||
tft.setTextColor(COLOR_TEXT); fmtNum(g_output_tokens, buf); tft.println(buf); y+=14;
|
||||
tft.setTextColor(COLOR_CYAN); tft.setCursor(x,y); tft.print("Reasoning: ");
|
||||
tft.setTextColor(COLOR_TEXT); snprintf(buf,sizeof(buf),"%ld",g_reasoning_tokens); tft.println(buf); y+=14;
|
||||
tft.setTextColor(COLOR_TEXT); fmtNum(g_reasoning_tokens, buf); tft.println(buf); y+=14;
|
||||
tft.setTextColor(COLOR_CYAN); tft.setCursor(x,y); tft.print("Cached : ");
|
||||
tft.setTextColor(COLOR_TEXT); snprintf(buf,sizeof(buf),"%ld",g_cached_tokens); tft.println(buf);
|
||||
tft.setTextColor(COLOR_TEXT); fmtNum(g_cached_tokens, buf); tft.println(buf);
|
||||
}
|
||||
|
||||
void drawPage1() {
|
||||
// Accounts
|
||||
tft.fillScreen(COLOR_BG);
|
||||
drawHeader("Account", 1, TOTAL_PAGES);
|
||||
int y=48; char buf[48];
|
||||
int y=48; char buf[48], nb[16], tb[16];
|
||||
for (int i=0; i<g_accountCount && i<5; i++) {
|
||||
char sn[36]; strncpy(sn,g_accounts[i].name,35); sn[35]='\0';
|
||||
fmtNum(g_accounts[i].requests, nb);
|
||||
fmtNum(g_accounts[i].total_tokens, tb);
|
||||
tft.setTextSize(1); tft.setTextColor(COLOR_YELLOW);
|
||||
tft.setCursor(6,y); tft.print(sn);
|
||||
tft.setTextColor(COLOR_TEXT); tft.setCursor(6,y+12);
|
||||
snprintf(buf,sizeof(buf)," %d reqs %ld tokens fail:%d",
|
||||
g_accounts[i].requests, g_accounts[i].total_tokens, g_accounts[i].failed);
|
||||
snprintf(buf,sizeof(buf)," %s reqs %s tokens fail:%d",
|
||||
nb, tb, g_accounts[i].failed);
|
||||
tft.print(buf);
|
||||
tft.drawFastHLine(0,y+26,SCREEN_WIDTH,COLOR_HEADER);
|
||||
y+=30;
|
||||
@@ -285,13 +319,15 @@ void drawPage2() {
|
||||
// Models
|
||||
tft.fillScreen(COLOR_BG);
|
||||
drawHeader("Model", 2, TOTAL_PAGES);
|
||||
int y=48; char buf[48];
|
||||
int y=48; char buf[48], nb[16], tb[16];
|
||||
for (int i=0; i<g_modelCount && i<6; i++) {
|
||||
fmtNum(g_models[i].requests, nb);
|
||||
fmtNum(g_models[i].total_tokens, tb);
|
||||
tft.setTextSize(1); tft.setTextColor(COLOR_GREEN);
|
||||
tft.setCursor(6,y); tft.print(g_models[i].name);
|
||||
tft.setTextColor(COLOR_TEXT); tft.setCursor(6,y+12);
|
||||
snprintf(buf,sizeof(buf)," %d reqs %ld tokens fail:%d",
|
||||
g_models[i].requests, g_models[i].total_tokens, g_models[i].failed);
|
||||
snprintf(buf,sizeof(buf)," %s reqs %s tokens fail:%d",
|
||||
nb, tb, g_models[i].failed);
|
||||
tft.print(buf);
|
||||
tft.drawFastHLine(0,y+26,SCREEN_WIDTH,COLOR_HEADER);
|
||||
y+=30;
|
||||
@@ -305,17 +341,19 @@ void drawPage3() {
|
||||
// Hours (hoạt động theo giờ gần nhất)
|
||||
tft.fillScreen(COLOR_BG);
|
||||
drawHeader("Hourly", 3, TOTAL_PAGES);
|
||||
int y=48; char buf[48];
|
||||
int y=48; char buf[48], nb[16], tb[16];
|
||||
int start = g_hourCount > 6 ? g_hourCount - 6 : 0; // 6 giờ gần nhất
|
||||
for (int i=start; i<g_hourCount; i++) {
|
||||
fmtNum(g_hours[i].requests, nb);
|
||||
fmtNum(g_hours[i].total_tokens, tb);
|
||||
tft.setTextSize(1); tft.setTextColor(COLOR_CYAN);
|
||||
tft.setCursor(6,y); tft.print(g_hours[i].hour);
|
||||
tft.setTextColor(COLOR_TEXT);
|
||||
snprintf(buf,sizeof(buf)," %d reqs %ld tok", g_hours[i].requests, g_hours[i].total_tokens);
|
||||
snprintf(buf,sizeof(buf)," %s reqs %s tok", nb, tb);
|
||||
tft.setCursor(6,y+12); tft.print(buf);
|
||||
tft.drawFastHLine(0,y+26,SCREEN_WIDTH,COLOR_HEADER);
|
||||
y+=30;
|
||||
if (y > SCREEN_HEIGHT - 28) break;
|
||||
if (y > SCREEN_HEIGHT - 36) break;
|
||||
}
|
||||
if (g_hourCount == 0) {
|
||||
tft.setTextColor(COLOR_TEXT); tft.setCursor(6,y); tft.print("(khong co du lieu)");
|
||||
@@ -424,6 +462,47 @@ bool fetchAndParseData() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════
|
||||
// Fetch tổng token 30 ngày (range=30d)
|
||||
// ═════════════════════════════════════════════════
|
||||
void fetch30dTokens() {
|
||||
// Tạo URL với range=30d từ g_api_url hiện tại
|
||||
String url30 = String(g_api_url);
|
||||
int q = url30.indexOf('?');
|
||||
if (q >= 0) {
|
||||
// Có query string -> thay thế/thêm range=30d
|
||||
int r = url30.indexOf("range=");
|
||||
if (r >= 0) {
|
||||
int amp = url30.indexOf('&', r);
|
||||
if (amp >= 0) url30 = url30.substring(0, r) + "range=30d" + url30.substring(amp);
|
||||
else url30 = url30.substring(0, r) + "range=30d";
|
||||
} else {
|
||||
url30 += "&range=30d";
|
||||
}
|
||||
} else {
|
||||
url30 += "?range=30d";
|
||||
}
|
||||
|
||||
Serial.printf("[FETCH30] GET %s\n", url30.c_str());
|
||||
HTTPClient http;
|
||||
http.begin(url30);
|
||||
http.setTimeout(10000);
|
||||
int code = http.GET();
|
||||
if (code != 200) { http.end(); return; }
|
||||
|
||||
String payload = http.getString();
|
||||
http.end();
|
||||
|
||||
DynamicJsonDocument doc(4096);
|
||||
DeserializationError err = deserializeJson(doc, payload);
|
||||
if (err) {
|
||||
Serial.printf("[FETCH30] JSON error: %s\n", err.c_str());
|
||||
return;
|
||||
}
|
||||
g_tokens30d = doc["summary"]["total_tokens"] | 0L;
|
||||
Serial.printf("[FETCH30] 30d tokens = %ld\n", g_tokens30d);
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════
|
||||
// HTML
|
||||
// ═════════════════════════════════════════════════
|
||||
@@ -610,6 +689,9 @@ void fetch_task(void* pv) {
|
||||
} else {
|
||||
tft_showFetchError();
|
||||
}
|
||||
// Lấy tổng token 30 ngày
|
||||
fetch30dTokens();
|
||||
drawCurrentPage();
|
||||
}
|
||||
// Nếu có yêu cầu fetch ngay (sau khi đổi API URL), không delay 60s
|
||||
if (g_fetchNow) {
|
||||
|
||||
Reference in New Issue
Block a user