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_cached_tokens = 0;
|
||||||
long g_failed = 0;
|
long g_failed = 0;
|
||||||
|
|
||||||
|
long g_tokens30d = 0; // tổng token 30 ngày (range=30d)
|
||||||
|
|
||||||
AccountStat g_accounts[8];
|
AccountStat g_accounts[8];
|
||||||
int g_accountCount = 0;
|
int g_accountCount = 0;
|
||||||
ModelStat g_models[6];
|
ModelStat g_models[6];
|
||||||
@@ -208,17 +210,47 @@ void tft_showFetchError() {
|
|||||||
tftUnlock();
|
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() {
|
void drawFooter() {
|
||||||
// Vẽ thanh footer ở 16px dưới cùng
|
int footerTop = SCREEN_HEIGHT - 32;
|
||||||
tft.drawFastHLine(0, SCREEN_HEIGHT-17, SCREEN_WIDTH, COLOR_HEADER);
|
tft.drawFastHLine(0, footerTop - 1, SCREEN_WIDTH, COLOR_HEADER);
|
||||||
tft.setTextSize(1); tft.setTextColor(COLOR_CYAN);
|
|
||||||
tft.setCursor(6, SCREEN_HEIGHT-13);
|
// 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.print("Config: http://");
|
||||||
tft.setTextColor(COLOR_ORANGE);
|
tft.setTextColor(COLOR_ORANGE);
|
||||||
tft.print(WiFi.localIP().toString());
|
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 ───────────────────────────
|
// ── Vẽ 4 trang dữ liệu ───────────────────────────
|
||||||
void drawPage0() {
|
void drawPage0() {
|
||||||
// Tổng quan
|
// Tổng quan
|
||||||
@@ -230,13 +262,13 @@ void drawPage0() {
|
|||||||
tft.setTextSize(1); tft.setTextColor(COLOR_CYAN);
|
tft.setTextSize(1); tft.setTextColor(COLOR_CYAN);
|
||||||
tft.setCursor(8,48); tft.print("Requests");
|
tft.setCursor(8,48); tft.print("Requests");
|
||||||
tft.setTextSize(4); tft.setTextColor(COLOR_GREEN);
|
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.setCursor(8,62); tft.print(buf);
|
||||||
|
|
||||||
tft.setTextSize(1); tft.setTextColor(COLOR_CYAN);
|
tft.setTextSize(1); tft.setTextColor(COLOR_CYAN);
|
||||||
tft.setCursor(8,108); tft.print("Failed");
|
tft.setCursor(8,108); tft.print("Failed");
|
||||||
tft.setTextSize(2); tft.setTextColor(g_failed > 0 ? COLOR_RED : COLOR_TEXT);
|
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);
|
tft.setCursor(8,120); tft.print(buf);
|
||||||
|
|
||||||
// Cột phải: token breakdown
|
// Cột phải: token breakdown
|
||||||
@@ -246,32 +278,34 @@ void drawPage0() {
|
|||||||
tft.setTextSize(1); tft.setTextColor(COLOR_YELLOW);
|
tft.setTextSize(1); tft.setTextColor(COLOR_YELLOW);
|
||||||
tft.setCursor(x,y); tft.print("Total Tokens:"); y+=14;
|
tft.setCursor(x,y); tft.print("Total Tokens:"); y+=14;
|
||||||
tft.setTextSize(2); tft.setTextColor(COLOR_ORANGE);
|
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.setCursor(x,y); tft.print(buf); y+=24;
|
||||||
|
|
||||||
tft.setTextSize(1);
|
tft.setTextSize(1);
|
||||||
tft.setTextColor(COLOR_CYAN); tft.setCursor(x,y); tft.print("Input : ");
|
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_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_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_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() {
|
void drawPage1() {
|
||||||
// Accounts
|
// Accounts
|
||||||
tft.fillScreen(COLOR_BG);
|
tft.fillScreen(COLOR_BG);
|
||||||
drawHeader("Account", 1, TOTAL_PAGES);
|
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++) {
|
for (int i=0; i<g_accountCount && i<5; i++) {
|
||||||
char sn[36]; strncpy(sn,g_accounts[i].name,35); sn[35]='\0';
|
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.setTextSize(1); tft.setTextColor(COLOR_YELLOW);
|
||||||
tft.setCursor(6,y); tft.print(sn);
|
tft.setCursor(6,y); tft.print(sn);
|
||||||
tft.setTextColor(COLOR_TEXT); tft.setCursor(6,y+12);
|
tft.setTextColor(COLOR_TEXT); tft.setCursor(6,y+12);
|
||||||
snprintf(buf,sizeof(buf)," %d reqs %ld tokens fail:%d",
|
snprintf(buf,sizeof(buf)," %s reqs %s tokens fail:%d",
|
||||||
g_accounts[i].requests, g_accounts[i].total_tokens, g_accounts[i].failed);
|
nb, tb, g_accounts[i].failed);
|
||||||
tft.print(buf);
|
tft.print(buf);
|
||||||
tft.drawFastHLine(0,y+26,SCREEN_WIDTH,COLOR_HEADER);
|
tft.drawFastHLine(0,y+26,SCREEN_WIDTH,COLOR_HEADER);
|
||||||
y+=30;
|
y+=30;
|
||||||
@@ -285,13 +319,15 @@ void drawPage2() {
|
|||||||
// Models
|
// Models
|
||||||
tft.fillScreen(COLOR_BG);
|
tft.fillScreen(COLOR_BG);
|
||||||
drawHeader("Model", 2, TOTAL_PAGES);
|
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++) {
|
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.setTextSize(1); tft.setTextColor(COLOR_GREEN);
|
||||||
tft.setCursor(6,y); tft.print(g_models[i].name);
|
tft.setCursor(6,y); tft.print(g_models[i].name);
|
||||||
tft.setTextColor(COLOR_TEXT); tft.setCursor(6,y+12);
|
tft.setTextColor(COLOR_TEXT); tft.setCursor(6,y+12);
|
||||||
snprintf(buf,sizeof(buf)," %d reqs %ld tokens fail:%d",
|
snprintf(buf,sizeof(buf)," %s reqs %s tokens fail:%d",
|
||||||
g_models[i].requests, g_models[i].total_tokens, g_models[i].failed);
|
nb, tb, g_models[i].failed);
|
||||||
tft.print(buf);
|
tft.print(buf);
|
||||||
tft.drawFastHLine(0,y+26,SCREEN_WIDTH,COLOR_HEADER);
|
tft.drawFastHLine(0,y+26,SCREEN_WIDTH,COLOR_HEADER);
|
||||||
y+=30;
|
y+=30;
|
||||||
@@ -305,17 +341,19 @@ void drawPage3() {
|
|||||||
// Hours (hoạt động theo giờ gần nhất)
|
// Hours (hoạt động theo giờ gần nhất)
|
||||||
tft.fillScreen(COLOR_BG);
|
tft.fillScreen(COLOR_BG);
|
||||||
drawHeader("Hourly", 3, TOTAL_PAGES);
|
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
|
int start = g_hourCount > 6 ? g_hourCount - 6 : 0; // 6 giờ gần nhất
|
||||||
for (int i=start; i<g_hourCount; i++) {
|
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.setTextSize(1); tft.setTextColor(COLOR_CYAN);
|
||||||
tft.setCursor(6,y); tft.print(g_hours[i].hour);
|
tft.setCursor(6,y); tft.print(g_hours[i].hour);
|
||||||
tft.setTextColor(COLOR_TEXT);
|
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.setCursor(6,y+12); tft.print(buf);
|
||||||
tft.drawFastHLine(0,y+26,SCREEN_WIDTH,COLOR_HEADER);
|
tft.drawFastHLine(0,y+26,SCREEN_WIDTH,COLOR_HEADER);
|
||||||
y+=30;
|
y+=30;
|
||||||
if (y > SCREEN_HEIGHT - 28) break;
|
if (y > SCREEN_HEIGHT - 36) break;
|
||||||
}
|
}
|
||||||
if (g_hourCount == 0) {
|
if (g_hourCount == 0) {
|
||||||
tft.setTextColor(COLOR_TEXT); tft.setCursor(6,y); tft.print("(khong co du lieu)");
|
tft.setTextColor(COLOR_TEXT); tft.setCursor(6,y); tft.print("(khong co du lieu)");
|
||||||
@@ -424,6 +462,47 @@ bool fetchAndParseData() {
|
|||||||
return true;
|
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
|
// HTML
|
||||||
// ═════════════════════════════════════════════════
|
// ═════════════════════════════════════════════════
|
||||||
@@ -606,10 +685,13 @@ void fetch_task(void* pv) {
|
|||||||
drawCurrentPage();
|
drawCurrentPage();
|
||||||
Serial.println("Đèn Blue");
|
Serial.println("Đèn Blue");
|
||||||
pixels.setPixelColor(0, pixels.Color(0, 0, 255)); // (Chỉ số LED, R, G, B)
|
pixels.setPixelColor(0, pixels.Color(0, 0, 255)); // (Chỉ số LED, R, G, B)
|
||||||
pixels.show(); // Cập nhật hiển thị lên đèn
|
pixels.show(); // Cập nhật hiển thị lên đèn
|
||||||
} else {
|
} else {
|
||||||
tft_showFetchError();
|
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
|
// Nếu có yêu cầu fetch ngay (sau khi đổi API URL), không delay 60s
|
||||||
if (g_fetchNow) {
|
if (g_fetchNow) {
|
||||||
|
|||||||
Reference in New Issue
Block a user