diff --git a/app/main.cpp b/app/main.cpp index 81bb838..9f66cb6 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -10,6 +10,7 @@ #include "monitor_task.h" #include "work_task.h" #include "lvgl_task.h" +#include "debug_log.h" #include #include #include @@ -159,6 +160,8 @@ extern "C" int main(void) printf("\r\n===== System Ready =====\r\n"); printf("CPU: 168MHz, SDRAM: 32MB, Flash: 1MB\r\n\r\n"); + log_init(); + Lcd::instance().clear(Lcd::BLACK); printf("Initializing RT-Thread...\r\n"); diff --git a/app/tasks/debug_log.cpp b/app/tasks/debug_log.cpp new file mode 100644 index 0000000..54f0c78 --- /dev/null +++ b/app/tasks/debug_log.cpp @@ -0,0 +1,108 @@ +#include "debug_log.h" +#include +#include +#include +#include + +static log_entry_t log_buf[LOG_BUF_SIZE] __attribute__((section(".sdram"))); +static uint32_t write_idx = 0; +static uint32_t total_count = 0; +static bool initialized = false; + +void log_init(void) +{ + if (!initialized) + { + memset(log_buf, 0, sizeof(log_buf)); + write_idx = 0; + total_count = 0; + initialized = true; + LOG_INFO("Debug log system initialized, %d entries buffer", LOG_BUF_SIZE); + } +} + +void log_write(log_level_t level, const char *file, int line, const char *func, const char *fmt, ...) +{ + if (!initialized) return; + + uint32_t idx = write_idx % LOG_BUF_SIZE; + log_entry_t *entry = &log_buf[idx]; + + entry->tick = rt_tick_get(); + entry->level = level; + + va_list args; + va_start(args, fmt); + rt_snprintf(entry->message, LOG_MSG_MAX, "[%s:%d] ", file, line); + uint32_t prefix_len = strlen(entry->message); + rt_vsnprintf(entry->message + prefix_len, LOG_MSG_MAX - prefix_len, fmt, args); + va_end(args); + + write_idx++; + total_count++; + + const char *level_str = "I"; + if (level == LOG_LEVEL_WARN) level_str = "W"; + else if (level == LOG_LEVEL_ERROR) level_str = "E"; + + printf("[%lu][%s] %s\r\n", + (unsigned long)entry->tick, + level_str, + entry->message); +} + +void log_dump(void) +{ + printf("\r\n========== DEBUG LOG DUMP ==========\r\n"); + printf("Total entries: %lu, Buffer slots: %d\r\n", + (unsigned long)total_count, LOG_BUF_SIZE); + printf("====================================\r\n"); + + if (total_count == 0) + { + printf("(no log entries)\r\n"); + return; + } + + uint32_t start = 0; + uint32_t count = LOG_BUF_SIZE; + if (total_count < LOG_BUF_SIZE) + { + start = 0; + count = total_count; + } + else + { + start = write_idx % LOG_BUF_SIZE; + } + + for (uint32_t i = 0; i < count; i++) + { + uint32_t idx = (start + i) % LOG_BUF_SIZE; + const log_entry_t *entry = &log_buf[idx]; + if (entry->tick == 0 && entry->message[0] == '\0') + continue; + + const char *level_str = "INFO"; + if (entry->level == LOG_LEVEL_WARN) level_str = "WARN"; + else if (entry->level == LOG_LEVEL_ERROR) level_str = "ERROR"; + + printf("[%06lu][%s] %s\r\n", + (unsigned long)entry->tick, + level_str, + entry->message); + } + + printf("========== DUMP END ==========\r\n"); +} + +uint32_t log_count(void) +{ + return total_count; +} + +const log_entry_t *log_get_entry(uint32_t index) +{ + if (index >= LOG_BUF_SIZE) return NULL; + return &log_buf[index]; +} diff --git a/app/tasks/debug_log.h b/app/tasks/debug_log.h new file mode 100644 index 0000000..af271a1 --- /dev/null +++ b/app/tasks/debug_log.h @@ -0,0 +1,37 @@ +#ifndef __DEBUG_LOG_H__ +#define __DEBUG_LOG_H__ + +#include +#include + +#define LOG_BUF_SIZE 100 +#define LOG_MSG_MAX 100 + +typedef enum { + LOG_LEVEL_INFO = 0, + LOG_LEVEL_WARN = 1, + LOG_LEVEL_ERROR = 2, +} log_level_t; + +typedef struct { + uint32_t tick; + log_level_t level; + char message[LOG_MSG_MAX]; +} log_entry_t; + +void log_init(void); +void log_write(log_level_t level, const char *file, int line, const char *func, const char *fmt, ...); +void log_dump(void); +uint32_t log_count(void); +const log_entry_t *log_get_entry(uint32_t index); + +#define LOG_INFO(fmt, ...) \ + log_write(LOG_LEVEL_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__) + +#define LOG_WARN(fmt, ...) \ + log_write(LOG_LEVEL_WARN, __FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__) + +#define LOG_ERROR(fmt, ...) \ + log_write(LOG_LEVEL_ERROR, __FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__) + +#endif diff --git a/app/tasks/packer_ui.cpp b/app/tasks/packer_ui.cpp index deef56b..44a60be 100644 --- a/app/tasks/packer_ui.cpp +++ b/app/tasks/packer_ui.cpp @@ -1,6 +1,8 @@ #include "packer_ui.h" +#include "debug_log.h" #include "lvgl/lvgl.h" #include +#include LV_FONT_DECLARE(custom_cjk_16); LV_FONT_DECLARE(custom_cjk_20); @@ -8,241 +10,319 @@ LV_FONT_DECLARE(custom_cjk_30); LV_FONT_DECLARE(custom_cjk_40); static lv_obj_t *packer_scr = NULL; -static lv_obj_t *header = NULL; -static lv_obj_t *weight_panel = NULL; -static lv_obj_t *control_panel = NULL; -static lv_obj_t *start_btn = NULL; -static lv_obj_t *weight_label = NULL; -static lv_obj_t *count_label = NULL; -static lv_obj_t *status_label = NULL; -static lv_obj_t *set_weight_label = NULL; -static lv_obj_t *minus_btn = NULL; -static lv_obj_t *plus_btn = NULL; +static lv_obj_t *card_status = NULL; +static lv_obj_t *card_target = NULL; +static lv_obj_t *btn_minus = NULL; +static lv_obj_t *btn_plus = NULL; +static lv_obj_t *btn_start = NULL; +static lv_obj_t *btn_reset = NULL; +static lv_obj_t *label_target_val = NULL; +static lv_obj_t *label_current_weight = NULL; +static lv_obj_t *label_pack_count = NULL; +static lv_obj_t *label_status = NULL; +static lv_obj_t *status_dot = NULL; +static volatile bool is_running = false; +static volatile uint32_t target_weight = 190; +static volatile uint32_t current_weight = 0; +static volatile uint32_t pack_count = 0; -static lv_color_t COLOR_HEADER_BG = lv_color_hex(0x153962); -static lv_color_t COLOR_BG = lv_color_hex(0x0D1117); -static lv_color_t COLOR_CARD = lv_color_hex(0x161B22); -static lv_color_t COLOR_TEXT = lv_color_hex(0xFFFFFF); -static lv_color_t COLOR_GREEN = lv_color_hex(0x3FB950); -static lv_color_t COLOR_GREEN_BRIGHT = lv_color_hex(0x01EB9B); -static lv_color_t COLOR_RED = lv_color_hex(0xF76F4A); -static lv_color_t COLOR_BORDER = lv_color_hex(0x30363D); -static lv_color_t COLOR_BUTTON_DARK = lv_color_hex(0x2A2828); +static lv_color_t COLOR_BG; +static lv_color_t COLOR_CARD; +static lv_color_t COLOR_CARD_TARGET; +static lv_color_t COLOR_BTN_START; +static lv_color_t COLOR_BTN_DARK; +static lv_color_t COLOR_BTN_ADJ; +static lv_color_t COLOR_BORDER; +static lv_color_t COLOR_TEXT_MAIN; +static lv_color_t COLOR_TEXT_SEC; +static lv_color_t COLOR_GREEN; +static lv_color_t COLOR_ADJ_ICON; -static bool is_running = false; -static uint32_t target_weight = 200; -static uint32_t pack_count = 200; +static void start_cb(lv_event_t *e); +static void reset_cb(lv_event_t *e); +static void minus_cb(lv_event_t *e); +static void plus_cb(lv_event_t *e); +static void update_display(void); -static void start_stop_cb(lv_event_t *e); -static void weight_dec_cb(lv_event_t *e); -static void weight_inc_cb(lv_event_t *e); -static void update_status_display(); +static void set_colors(void) +{ + COLOR_BG = lv_color_hex(0x161b22); + COLOR_CARD = lv_color_hex(0x000000); + COLOR_CARD_TARGET = lv_color_hex(0x080a0e); + COLOR_BTN_START = lv_color_hex(0x06990c); + COLOR_BTN_DARK = lv_color_hex(0x000000); + COLOR_BTN_ADJ = lv_color_hex(0x1e3c28); + COLOR_BORDER = lv_color_hex(0x30363d); + COLOR_TEXT_MAIN = lv_color_hex(0xffffff); + COLOR_TEXT_SEC = lv_color_hex(0x9ab3a5); + COLOR_GREEN = lv_color_hex(0x06990c); + COLOR_ADJ_ICON = lv_color_hex(0xc4c4c4); +} + +static lv_obj_t *create_card(lv_obj_t *parent, int x, int y, int w, int h, lv_color_t bg, int radius) +{ + lv_obj_t *card = lv_obj_create(parent); + lv_obj_set_size(card, w, h); + lv_obj_set_pos(card, x, y); + lv_obj_set_style_bg_color(card, bg, 0); + lv_obj_set_style_radius(card, radius, 0); + lv_obj_set_style_border_width(card, 0, 0); + lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE); + return card; +} RetCode packer_ui_create(void) { + set_colors(); + packer_scr = lv_obj_create(NULL); lv_obj_set_style_bg_color(packer_scr, COLOR_BG, 0); lv_obj_set_size(packer_scr, 800, 480); lv_obj_clear_flag(packer_scr, LV_OBJ_FLAG_SCROLLABLE); - header = lv_obj_create(packer_scr); - lv_obj_set_size(header, 800, 60); - lv_obj_set_pos(header, 0, 0); - lv_obj_set_style_bg_color(header, COLOR_HEADER_BG, 0); - lv_obj_set_style_radius(header, 0, 0); - lv_obj_clear_flag(header, LV_OBJ_FLAG_SCROLLABLE); + card_status = create_card(packer_scr, 10, 10, 560, 330, COLOR_CARD, 20); - weight_panel = lv_obj_create(packer_scr); - lv_obj_set_size(weight_panel, 560, 400); - lv_obj_set_pos(weight_panel, 10, 70); - lv_obj_set_style_bg_color(weight_panel, COLOR_CARD, 0); - lv_obj_set_style_radius(weight_panel, 10, 0); - lv_obj_set_style_border_width(weight_panel, 2, 0); - lv_obj_set_style_border_color(weight_panel, COLOR_BORDER, 0); - lv_obj_clear_flag(weight_panel, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_t *w_title = lv_label_create(card_status); + lv_label_set_text(w_title, "当前重量"); + lv_obj_set_style_text_font(w_title, &custom_cjk_16, 0); + lv_obj_set_style_text_color(w_title, COLOR_TEXT_SEC, 0); + lv_obj_set_pos(w_title, 103, 14); - lv_obj_t *set_weight_title = lv_label_create(weight_panel); - lv_label_set_text(set_weight_title, "设置重量"); - lv_obj_set_style_text_font(set_weight_title, &custom_cjk_40, 0); - lv_obj_set_style_text_color(set_weight_title, COLOR_TEXT, 0); - lv_obj_set_pos(set_weight_title, 39, 101); + label_current_weight = lv_label_create(card_status); + lv_label_set_text(label_current_weight, "0.0"); + lv_obj_set_style_text_font(label_current_weight, &custom_cjk_40, 0); + lv_obj_set_style_text_color(label_current_weight, COLOR_TEXT_MAIN, 0); + lv_obj_set_pos(label_current_weight, 82, 64); - set_weight_label = lv_label_create(weight_panel); - char weight_str[20]; - snprintf(weight_str, sizeof(weight_str), "%lug", target_weight / 10); - lv_label_set_text(set_weight_label, weight_str); - lv_obj_set_style_text_font(set_weight_label, &custom_cjk_40, 0); - lv_obj_set_style_text_color(set_weight_label, COLOR_GREEN_BRIGHT, 0); - lv_obj_set_pos(set_weight_label, 239, 101); + lv_obj_t *w_unit = lv_label_create(card_status); + lv_label_set_text(w_unit, "克"); + lv_obj_set_style_text_font(w_unit, &custom_cjk_20, 0); + lv_obj_set_style_text_color(w_unit, COLOR_TEXT_SEC, 0); + lv_obj_set_pos(w_unit, 140, 60); - minus_btn = lv_obj_create(weight_panel); - lv_obj_set_size(minus_btn, 80, 80); - lv_obj_set_pos(minus_btn, 350, 89); - lv_obj_set_style_bg_color(minus_btn, COLOR_BUTTON_DARK, 0); - lv_obj_set_style_radius(minus_btn, 20, 0); - lv_obj_add_event_cb(minus_btn, weight_dec_cb, LV_EVENT_CLICKED, NULL); + lv_obj_t *w_sub = lv_label_create(card_status); + lv_label_set_text(w_sub, "称重斗实时值"); + lv_obj_set_style_text_font(w_sub, &custom_cjk_16, 0); + lv_obj_set_style_text_color(w_sub, lv_color_hex(0x6f9181), 0); + lv_obj_set_pos(w_sub, 25, 105); - lv_obj_t *minus_line = lv_obj_create(minus_btn); - lv_obj_set_size(minus_line, 40, 5); - lv_obj_set_style_bg_color(minus_line, COLOR_TEXT, 0); - lv_obj_align(minus_line, LV_ALIGN_CENTER, 0, 0); + lv_obj_t *div_line = lv_obj_create(card_status); + lv_obj_set_size(div_line, 500, 1); + lv_obj_set_pos(div_line, 25, 140); + lv_obj_set_style_bg_color(div_line, COLOR_BORDER, 0); + lv_obj_clear_flag(div_line, LV_OBJ_FLAG_SCROLLABLE); - plus_btn = lv_obj_create(weight_panel); - lv_obj_set_size(plus_btn, 80, 80); - lv_obj_set_pos(plus_btn, 470, 90); - lv_obj_set_style_bg_color(plus_btn, COLOR_BUTTON_DARK, 0); - lv_obj_set_style_radius(plus_btn, 20, 0); - lv_obj_add_event_cb(plus_btn, weight_inc_cb, LV_EVENT_CLICKED, NULL); + lv_obj_t *vertical_line = lv_obj_create(card_status); + lv_obj_set_size(vertical_line, 1, 287); + lv_obj_set_pos(vertical_line, 277, 22); + lv_obj_set_style_bg_color(vertical_line, COLOR_BORDER, 0); + lv_obj_clear_flag(vertical_line, LV_OBJ_FLAG_SCROLLABLE); - lv_obj_t *plus_h_line = lv_obj_create(plus_btn); - lv_obj_set_size(plus_h_line, 40, 5); - lv_obj_set_style_bg_color(plus_h_line, COLOR_TEXT, 0); - lv_obj_align(plus_h_line, LV_ALIGN_CENTER, 0, 0); + lv_obj_t *c_title = lv_label_create(card_status); + lv_label_set_text(c_title, "总包数"); + lv_obj_set_style_text_font(c_title, &custom_cjk_16, 0); + lv_obj_set_style_text_color(c_title, COLOR_TEXT_SEC, 0); + lv_obj_set_pos(c_title, 297, 22); - lv_obj_t *plus_v_line = lv_obj_create(plus_btn); - lv_obj_set_size(plus_v_line, 5, 40); - lv_obj_set_style_bg_color(plus_v_line, COLOR_TEXT, 0); - lv_obj_align(plus_v_line, LV_ALIGN_CENTER, 0, 0); + label_pack_count = lv_label_create(card_status); + lv_label_set_text(label_pack_count, "0"); + lv_obj_set_style_text_font(label_pack_count, &custom_cjk_40, 0); + lv_obj_set_style_text_color(label_pack_count, COLOR_TEXT_MAIN, 0); + lv_obj_set_pos(label_pack_count, 297, 50); - control_panel = lv_obj_create(packer_scr); - lv_obj_set_size(control_panel, 200, 400); - lv_obj_set_pos(control_panel, 590, 70); - lv_obj_set_style_bg_color(control_panel, COLOR_CARD, 0); - lv_obj_set_style_radius(control_panel, 10, 0); - lv_obj_set_style_border_width(control_panel, 2, 0); - lv_obj_set_style_border_color(control_panel, COLOR_BORDER, 0); - lv_obj_clear_flag(control_panel, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_t *c_unit = lv_label_create(card_status); + lv_label_set_text(c_unit, "包"); + lv_obj_set_style_text_font(c_unit, &custom_cjk_20, 0); + lv_obj_set_style_text_color(c_unit, COLOR_TEXT_SEC, 0); + lv_obj_set_pos(c_unit, 390, 60); - start_btn = lv_obj_create(control_panel); - lv_obj_set_size(start_btn, 159, 190); - lv_obj_set_pos(start_btn, 20, 20); - lv_obj_set_style_bg_color(start_btn, COLOR_GREEN, 0); - lv_obj_set_style_radius(start_btn, 20, 0); - lv_obj_add_event_cb(start_btn, start_stop_cb, LV_EVENT_CLICKED, NULL); + lv_obj_t *c_sub = lv_label_create(card_status); + lv_label_set_text(c_sub, "累计成品"); + lv_obj_set_style_text_font(c_sub, &custom_cjk_16, 0); + lv_obj_set_style_text_color(c_sub, lv_color_hex(0x6f9181), 0); + lv_obj_set_pos(c_sub, 297, 110); - lv_obj_t *btn_label = lv_label_create(start_btn); - lv_label_set_text(btn_label, "启动"); - lv_obj_set_style_text_font(btn_label, &custom_cjk_30, 0); - lv_obj_set_style_text_color(btn_label, COLOR_TEXT, 0); - lv_obj_align(btn_label, LV_ALIGN_TOP_MID, 0, 73); + lv_obj_t *status_bg = create_card(card_status, 318, 196, 224, 71, COLOR_CARD, 30); - lv_obj_t *btn_hint = lv_label_create(start_btn); - lv_label_set_text(btn_hint, "按此启动"); - lv_obj_set_style_text_font(btn_hint, &custom_cjk_16, 0); - lv_obj_set_style_text_color(btn_hint, COLOR_TEXT, 0); - lv_obj_align(btn_hint, LV_ALIGN_TOP_MID, 0, 160); + status_dot = lv_obj_create(status_bg); + lv_obj_set_size(status_dot, 34, 35); + lv_obj_set_style_radius(status_dot, 17, 0); + lv_obj_set_style_border_width(status_dot, 0, 0); + lv_obj_set_style_bg_color(status_dot, COLOR_GREEN, 0); + lv_obj_set_pos(status_dot, 22, 9); - lv_obj_t *weight_title = lv_label_create(control_panel); - lv_label_set_text(weight_title, "当前重量"); - lv_obj_set_style_text_font(weight_title, &custom_cjk_16, 0); - lv_obj_set_style_text_color(weight_title, COLOR_TEXT, 0); - lv_obj_set_pos(weight_title, 30, 218); + label_status = lv_label_create(status_bg); + lv_label_set_text(label_status, "已停止"); + lv_obj_set_style_text_font(label_status, &custom_cjk_30, 0); + lv_obj_set_style_text_color(label_status, COLOR_TEXT_SEC, 0); + lv_obj_align(label_status, LV_ALIGN_RIGHT_MID, -20, 0); - weight_label = lv_label_create(control_panel); - lv_label_set_text(weight_label, "0.0g"); - lv_obj_set_style_text_font(weight_label, &custom_cjk_20, 0); - lv_obj_set_style_text_color(weight_label, COLOR_GREEN, 0); - lv_obj_set_pos(weight_label, 30, 244); + btn_start = lv_btn_create(packer_scr); + lv_obj_set_size(btn_start, 210, 330); + lv_obj_set_pos(btn_start, 580, 10); + lv_obj_set_style_bg_color(btn_start, COLOR_BTN_START, 0); + lv_obj_set_style_radius(btn_start, 20, 0); + lv_obj_clear_flag(btn_start, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_add_event_cb(btn_start, start_cb, LV_EVENT_CLICKED, NULL); - lv_obj_t *count_title = lv_label_create(control_panel); - lv_label_set_text(count_title, "包装计数"); - lv_obj_set_style_text_font(count_title, &custom_cjk_16, 0); - lv_obj_set_style_text_color(count_title, COLOR_TEXT, 0); - lv_obj_set_pos(count_title, 30, 280); + lv_obj_t *start_lbl = lv_label_create(btn_start); + lv_label_set_text(start_lbl, "启动"); + lv_obj_set_style_text_font(start_lbl, &custom_cjk_30, 0); + lv_obj_set_style_text_color(start_lbl, COLOR_TEXT_MAIN, 0); + lv_obj_align(start_lbl, LV_ALIGN_CENTER, 0, 0); - count_label = lv_label_create(control_panel); - char count_str[20]; - snprintf(count_str, sizeof(count_str), "%lu", pack_count); - lv_label_set_text(count_label, count_str); - lv_obj_set_style_text_font(count_label, &custom_cjk_20, 0); - lv_obj_set_style_text_color(count_label, COLOR_GREEN, 0); - lv_obj_set_pos(count_label, 30, 300); + card_target = create_card(packer_scr, 10, 350, 560, 120, COLOR_CARD_TARGET, 20); - lv_obj_t *status_title = lv_label_create(control_panel); - lv_label_set_text(status_title, "运行状态"); - lv_obj_set_style_text_font(status_title, &custom_cjk_16, 0); - lv_obj_set_style_text_color(status_title, COLOR_TEXT, 0); - lv_obj_set_pos(status_title, 30, 335); + lv_obj_t *t_title = lv_label_create(card_target); + lv_label_set_text(t_title, "设计重量"); + lv_obj_set_style_text_font(t_title, &custom_cjk_16, 0); + lv_obj_set_style_text_color(t_title, COLOR_TEXT_SEC, 0); + lv_obj_set_pos(t_title, 25, 15); - status_label = lv_label_create(control_panel); - lv_label_set_text(status_label, "已停止"); - lv_obj_set_style_text_font(status_label, &custom_cjk_20, 0); - lv_obj_set_style_text_color(status_label, COLOR_RED, 0); - lv_obj_set_pos(status_label, 28, 361); + label_target_val = lv_label_create(card_target); + char tmp[20]; + snprintf(tmp, sizeof(tmp), "%lu.%lu", (unsigned long)(target_weight / 10), (unsigned long)(target_weight % 10)); + lv_label_set_text(label_target_val, tmp); + lv_obj_set_style_text_font(label_target_val, &custom_cjk_40, 0); + lv_obj_set_style_text_color(label_target_val, COLOR_TEXT_MAIN, 0); + lv_obj_set_pos(label_target_val, 25, 40); + + lv_obj_t *t_unit = lv_label_create(card_target); + lv_label_set_text(t_unit, "克/包"); + lv_obj_set_style_text_font(t_unit, &custom_cjk_16, 0); + lv_obj_set_style_text_color(t_unit, COLOR_TEXT_SEC, 0); + lv_obj_set_pos(t_unit, 130, 50); + + btn_minus = lv_btn_create(card_target); + lv_obj_set_size(btn_minus, 60, 60); + lv_obj_set_pos(btn_minus, 331, 30); + lv_obj_set_style_bg_color(btn_minus, COLOR_BTN_ADJ, 0); + lv_obj_set_style_radius(btn_minus, 30, 0); + lv_obj_clear_flag(btn_minus, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_add_event_cb(btn_minus, minus_cb, LV_EVENT_CLICKED, NULL); + + lv_obj_t *minus_lbl = lv_label_create(btn_minus); + lv_label_set_text(minus_lbl, "-"); + lv_obj_set_style_text_font(minus_lbl, &custom_cjk_30, 0); + lv_obj_set_style_text_color(minus_lbl, COLOR_TEXT_MAIN, 0); + lv_obj_align(minus_lbl, LV_ALIGN_CENTER, 0, 0); + + btn_plus = lv_btn_create(card_target); + lv_obj_set_size(btn_plus, 60, 60); + lv_obj_set_pos(btn_plus, 463, 30); + lv_obj_set_style_bg_color(btn_plus, COLOR_BTN_ADJ, 0); + lv_obj_set_style_radius(btn_plus, 30, 0); + lv_obj_clear_flag(btn_plus, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_add_event_cb(btn_plus, plus_cb, LV_EVENT_CLICKED, NULL); + + lv_obj_t *plus_lbl = lv_label_create(btn_plus); + lv_label_set_text(plus_lbl, "+"); + lv_obj_set_style_text_font(plus_lbl, &custom_cjk_30, 0); + lv_obj_set_style_text_color(plus_lbl, COLOR_TEXT_MAIN, 0); + lv_obj_align(plus_lbl, LV_ALIGN_CENTER, 0, 0); + + btn_reset = lv_btn_create(packer_scr); + lv_obj_set_size(btn_reset, 210, 120); + lv_obj_set_pos(btn_reset, 580, 350); + lv_obj_set_style_bg_color(btn_reset, COLOR_BTN_DARK, 0); + lv_obj_set_style_radius(btn_reset, 20, 0); + lv_obj_clear_flag(btn_reset, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_add_event_cb(btn_reset, reset_cb, LV_EVENT_CLICKED, NULL); + + lv_obj_t *reset_lbl = lv_label_create(btn_reset); + lv_label_set_text(reset_lbl, "复位"); + lv_obj_set_style_text_font(reset_lbl, &custom_cjk_30, 0); + lv_obj_set_style_text_color(reset_lbl, COLOR_TEXT_SEC, 0); + lv_obj_align(reset_lbl, LV_ALIGN_CENTER, 0, 0); lv_scr_load(packer_scr); + update_display(); return RET_OK; } -static void start_stop_cb(lv_event_t *e) +static void start_cb(lv_event_t *e) { (void)e; - is_running = !is_running; - update_status_display(); + is_running = true; + current_weight = 0; + LOG_INFO("Start button pressed, target=%lu.%lug", + (unsigned long)(target_weight / 10), (unsigned long)(target_weight % 10)); + update_display(); } -static void weight_dec_cb(lv_event_t *e) +static void reset_cb(lv_event_t *e) { (void)e; - if (is_running) return; - if (target_weight > 10) { + LOG_INFO("Reset button pressed, total packs=%lu", (unsigned long)pack_count); + is_running = false; + current_weight = 0; + pack_count = 0; + update_display(); +} + +static void minus_cb(lv_event_t *e) +{ + (void)e; + if (is_running) + return; + if (target_weight > 10) + { target_weight -= 10; - char weight_str[20]; - snprintf(weight_str, sizeof(weight_str), "%lug", target_weight / 10); - lv_label_set_text(set_weight_label, weight_str); + char tmp[20]; + snprintf(tmp, sizeof(tmp), "%lu.%lu", (unsigned long)(target_weight / 10), (unsigned long)(target_weight % 10)); + lv_label_set_text(label_target_val, tmp); + LOG_INFO("Target weight decreased to %s", tmp); } } -static void weight_inc_cb(lv_event_t *e) +static void plus_cb(lv_event_t *e) { (void)e; - if (is_running) return; - if (target_weight < 1000) { + if (is_running) + return; + if (target_weight < 2000) + { target_weight += 10; - char weight_str[20]; - snprintf(weight_str, sizeof(weight_str), "%lug", target_weight / 10); - lv_label_set_text(set_weight_label, weight_str); + char tmp[20]; + snprintf(tmp, sizeof(tmp), "%lu.%lu", (unsigned long)(target_weight / 10), (unsigned long)(target_weight % 10)); + lv_label_set_text(label_target_val, tmp); + LOG_INFO("Target weight increased to %s", tmp); } } -static void update_status_display() +static void update_display(void) { - if (is_running) { - lv_label_set_text(status_label, "运行中"); - lv_obj_set_style_text_color(status_label, COLOR_GREEN, 0); - lv_label_set_text(lv_obj_get_child(start_btn, 0), "停止"); - lv_obj_set_style_bg_color(start_btn, COLOR_RED, 0); - } else { - lv_label_set_text(status_label, "已停止"); - lv_obj_set_style_text_color(status_label, COLOR_RED, 0); - lv_label_set_text(lv_obj_get_child(start_btn, 0), "启动"); - lv_obj_set_style_bg_color(start_btn, COLOR_GREEN, 0); + char tmp[32]; + + snprintf(tmp, sizeof(tmp), "%lu.%lu", (unsigned long)(current_weight / 10), (unsigned long)(current_weight % 10)); + lv_label_set_text(label_current_weight, tmp); + + snprintf(tmp, sizeof(tmp), "%lu", (unsigned long)pack_count); + lv_label_set_text(label_pack_count, tmp); + + if (is_running) + { + lv_label_set_text(label_status, "运行中"); + lv_obj_set_style_text_color(label_status, COLOR_GREEN, 0); + lv_obj_set_style_bg_color(status_dot, COLOR_GREEN, 0); + } + else + { + lv_label_set_text(label_status, "已停止"); + lv_obj_set_style_text_color(label_status, COLOR_TEXT_SEC, 0); + lv_obj_set_style_bg_color(status_dot, COLOR_GREEN, 0); } } void packer_ui_update(void) { - if (is_running) { - static uint32_t current_weight = 0; - if (current_weight < target_weight) { - current_weight += 5; - } else { - current_weight = 0; - pack_count++; - char count_str[20]; - snprintf(count_str, sizeof(count_str), "%lu", pack_count); - lv_label_set_text(count_label, count_str); - } - - char weight_str[20]; - snprintf(weight_str, sizeof(weight_str), "%lu.%lug", current_weight / 10, current_weight % 10); - lv_label_set_text(weight_label, weight_str); - } + update_display(); } void packer_ui_destroy(void) { - if (packer_scr) { + if (packer_scr) + { lv_obj_del(packer_scr); packer_scr = NULL; } diff --git a/tools/capture_log.py b/tools/capture_log.py new file mode 100644 index 0000000..e7ba9de --- /dev/null +++ b/tools/capture_log.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +""" +Serial log capture tool for LSPi Project. +Captures device debug output and saves to timestamped log files. + +Usage: + python tools/capture_log.py # Auto-detect COM port + python tools/capture_log.py --port COM3 # Specify port + python tools/capture_log.py --port COM3 --baud 9600 --output logs/ +""" + +import serial +import serial.tools.list_ports +import datetime +import argparse +import os +import sys +import signal + +def find_device_port(): + ports = serial.tools.list_ports.comports() + for p in ports: + if p.vid is not None: + return p.device + if ports: + return ports[0].device + return None + +def main(): + parser = argparse.ArgumentParser(description="LSPi Serial Log Capture") + parser.add_argument("--port", "-p", default=None, help="Serial port (e.g. COM3)") + parser.add_argument("--baud", "-b", type=int, default=9600, help="Baud rate (default: 9600)") + parser.add_argument("--output", "-o", default="logs", help="Output directory (default: logs)") + args = parser.parse_args() + + port = args.port + if port is None: + port = find_device_port() + if port is None: + print("ERROR: No serial port found. Specify with --port") + sys.exit(1) + print(f"Auto-detected port: {port}") + + os.makedirs(args.output, exist_ok=True) + + timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + log_file = os.path.join(args.output, f"device_log_{timestamp}.txt") + latest_link = os.path.join(args.output, "latest.txt") + + print(f"Connecting to {port} @ {args.baud} baud...") + print(f"Saving to: {log_file}") + print("Press Ctrl+C to stop capture") + print("-" * 60) + + ser = serial.Serial(port, args.baud, timeout=0.1) + running = True + + def handle_exit(sig, frame): + nonlocal running + print("\nCapture stopped.") + running = False + + signal.signal(signal.SIGINT, handle_exit) + + with open(log_file, "w", encoding="utf-8") as f: + f.write(f"LSPi Device Log Capture\n") + f.write(f"Port: {port} @ {args.baud} baud\n") + f.write(f"Started: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") + f.write("=" * 60 + "\n") + + while running: + try: + line = ser.readline().decode("utf-8", errors="replace").rstrip() + if line: + now = datetime.datetime.now().strftime("%H:%M:%S.%f")[:-3] + output = f"[{now}] {line}" + print(output) + f.write(output + "\n") + f.flush() + except serial.SerialException: + print("ERROR: Connection lost") + break + + ser.close() + + if os.path.exists(latest_link): + os.remove(latest_link) + try: + os.symlink(os.path.basename(log_file), latest_link) + except: + pass + + print(f"\nLog saved to: {log_file}") + +if __name__ == "__main__": + main()