187 lines
6.7 KiB
C
187 lines
6.7 KiB
C
/**
|
||
* @file ota_view.c
|
||
* @brief OTA status view implementation
|
||
*/
|
||
|
||
#define TAG "ota_view"
|
||
|
||
#include "ota_view.h"
|
||
#include "lisa_ui.h"
|
||
#include "lisa_ui_llm_base.h"
|
||
#include "lisa_ui_fonts.h"
|
||
#include <stdio.h>
|
||
|
||
typedef struct {
|
||
lisa_ui_llm_base_t base;
|
||
lv_obj_t *status_label;
|
||
lv_obj_t *progress_label;
|
||
lv_obj_t *eta_label;
|
||
char status_text[48];
|
||
char progress_text[32];
|
||
char eta_text[32];
|
||
} lisa_ui_ota_view_t;
|
||
|
||
static void ota_view_constructor(const lv_obj_class_t *class_p, lv_obj_t *obj);
|
||
static void ota_view_destructor(const lv_obj_class_t *class_p, lv_obj_t *obj);
|
||
|
||
const lv_obj_class_t lisa_ui_ota_view_class = {
|
||
.base_class = &lisa_ui_llm_base_class,
|
||
.instance_size = sizeof(lisa_ui_ota_view_t),
|
||
.constructor_cb = ota_view_constructor,
|
||
.destructor_cb = ota_view_destructor,
|
||
};
|
||
|
||
static void ota_view_constructor(const lv_obj_class_t *class_p, lv_obj_t *obj)
|
||
{
|
||
LV_UNUSED(class_p);
|
||
|
||
lisa_ui_ota_view_t *view = (lisa_ui_ota_view_t *)obj;
|
||
|
||
lv_obj_t *bar = lisa_ui_llm_base_bar_get(obj);
|
||
if (bar) {
|
||
lv_obj_add_flag(bar, LV_OBJ_FLAG_HIDDEN);
|
||
}
|
||
|
||
lv_obj_t *container = lisa_ui_llm_base_container_get(obj);
|
||
if (!container) {
|
||
return;
|
||
}
|
||
|
||
view->status_label = lv_label_create(container);
|
||
lv_label_set_text(view->status_label, "正在检查更新…");
|
||
lv_obj_set_style_text_color(view->status_label, lv_color_white(), 0);
|
||
lv_obj_set_style_text_font(view->status_label, &lv_font_chinese_16, 0);
|
||
lv_obj_set_width(view->status_label, lv_pct(100));
|
||
lv_obj_set_style_text_align(view->status_label, LV_TEXT_ALIGN_CENTER, 0);
|
||
lv_obj_align(view->status_label, LV_ALIGN_CENTER, 0, -16);
|
||
|
||
view->progress_label = lv_label_create(container);
|
||
lv_label_set_text(view->progress_label, "");
|
||
lv_obj_set_style_text_color(view->progress_label, lv_color_white(), 0);
|
||
lv_obj_set_style_text_font(view->progress_label, &lv_font_chinese_16, 0);
|
||
lv_obj_set_style_text_opa(view->progress_label, LV_OPA_70, 0);
|
||
lv_obj_set_width(view->progress_label, lv_pct(100));
|
||
lv_obj_set_style_text_align(view->progress_label, LV_TEXT_ALIGN_CENTER, 0);
|
||
lv_obj_align(view->progress_label, LV_ALIGN_CENTER, 0, 8);
|
||
|
||
view->eta_label = lv_label_create(container);
|
||
lv_label_set_text(view->eta_label, "");
|
||
lv_obj_set_style_text_color(view->eta_label, lv_color_white(), 0);
|
||
lv_obj_set_style_text_font(view->eta_label, &lv_font_chinese_16, 0);
|
||
lv_obj_set_style_text_opa(view->eta_label, LV_OPA_50, 0);
|
||
lv_obj_set_width(view->eta_label, lv_pct(100));
|
||
lv_obj_set_style_text_align(view->eta_label, LV_TEXT_ALIGN_CENTER, 0);
|
||
lv_obj_align(view->eta_label, LV_ALIGN_BOTTOM_MID, 0, -12);
|
||
}
|
||
|
||
static void ota_view_destructor(const lv_obj_class_t *class_p, lv_obj_t *obj)
|
||
{
|
||
(void)class_p;
|
||
(void)obj;
|
||
}
|
||
|
||
lv_obj_t *lisa_ui_ota_view_create(lv_obj_t *parent)
|
||
{
|
||
lv_obj_t *obj = lv_obj_class_create_obj(&lisa_ui_ota_view_class, parent);
|
||
lv_obj_class_init_obj(obj);
|
||
return obj;
|
||
}
|
||
|
||
static const char *ota_target_name(ota_target_e target)
|
||
{
|
||
switch (target) {
|
||
case OTA_TARGET_WAKE_WORD:
|
||
return "唤醒词";
|
||
case OTA_TARGET_PROMPT_TONE:
|
||
return "提示音";
|
||
case OTA_TARGET_EMOJI:
|
||
return "表情";
|
||
default:
|
||
return "资源";
|
||
}
|
||
}
|
||
|
||
static void ota_view_format_size(char *buf, size_t buf_size, uint32_t bytes)
|
||
{
|
||
if (bytes >= 1024 * 1024) {
|
||
snprintf(buf, buf_size, "%.1fMB", (float)bytes / (1024.0f * 1024.0f));
|
||
} else {
|
||
snprintf(buf, buf_size, "%uKB", bytes / 1024);
|
||
}
|
||
}
|
||
|
||
void lisa_ui_ota_view_update(lv_obj_t *obj, const ota_state_t *state)
|
||
{
|
||
if (!obj || !lv_obj_has_class(obj, &lisa_ui_ota_view_class)) {
|
||
return;
|
||
}
|
||
|
||
lisa_ui_ota_view_t *view = (lisa_ui_ota_view_t *)obj;
|
||
switch (state->state) {
|
||
case OTA_STATE_UPDATING: {
|
||
// 第一行:N/M 正在更新XXX…
|
||
if (state->update_total > 0) {
|
||
snprintf(view->status_text, sizeof(view->status_text), "%u/%u 正在更新%s…",
|
||
state->update_index, state->update_total, ota_target_name(state->target));
|
||
} else {
|
||
snprintf(view->status_text, sizeof(view->status_text), "正在更新%s…",
|
||
ota_target_name(state->target));
|
||
}
|
||
lv_label_set_text_static(view->status_label, view->status_text);
|
||
|
||
// 第二行:已下载 / 总大小
|
||
if (state->bytes_total > 0) {
|
||
char processed_str[16], total_str[16];
|
||
ota_view_format_size(processed_str, sizeof(processed_str), state->bytes_processed);
|
||
ota_view_format_size(total_str, sizeof(total_str), state->bytes_total);
|
||
snprintf(view->progress_text, sizeof(view->progress_text), "%s / %s", processed_str, total_str);
|
||
lv_label_set_text_static(view->progress_label, view->progress_text);
|
||
} else {
|
||
lv_label_set_text_static(view->progress_label, "");
|
||
}
|
||
|
||
// 第三行:预计剩余时间
|
||
if (state->bytes_processed < state->bytes_total) {
|
||
if (state->bytes_processed > 0 && state->elapsed_ms > 0) {
|
||
uint32_t remaining_bytes = state->bytes_total - state->bytes_processed;
|
||
float speed = (float)state->bytes_processed / (float)state->elapsed_ms;
|
||
uint32_t eta_sec = (uint32_t)((float)remaining_bytes / speed / 1000.0f);
|
||
uint32_t eta_min = (eta_sec + 59) / 60;
|
||
|
||
if (eta_min <= 1) {
|
||
snprintf(view->eta_text, sizeof(view->eta_text), "预计不到1分钟");
|
||
} else {
|
||
snprintf(view->eta_text, sizeof(view->eta_text), "预计%u分钟", eta_min);
|
||
}
|
||
} else {
|
||
snprintf(view->eta_text, sizeof(view->eta_text), "预计约1分钟");
|
||
}
|
||
lv_label_set_text_static(view->eta_label, view->eta_text);
|
||
} else {
|
||
lv_label_set_text_static(view->eta_label, "");
|
||
}
|
||
break;
|
||
}
|
||
case OTA_STATE_SUCCESSED:
|
||
lv_label_set_text_static(view->status_label, "更新完毕");
|
||
lv_label_set_text_static(view->progress_label, "");
|
||
if (state->reboot == OTA_REBOOT_STRATEGY_AUTO) {
|
||
lv_label_set_text_static(view->eta_label, "正在重启…");
|
||
} else {
|
||
lv_label_set_text_static(view->eta_label, "请手动重启设备");
|
||
}
|
||
break;
|
||
case OTA_STATE_FAILED:
|
||
lv_label_set_text_static(view->status_label, "更新失败");
|
||
lv_label_set_text_static(view->progress_label, "");
|
||
if (state->reboot == OTA_REBOOT_STRATEGY_AUTO) {
|
||
lv_label_set_text_static(view->eta_label, "正在重启…");
|
||
} else {
|
||
lv_label_set_text_static(view->eta_label, "请手动重启设备");
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|