// device liquid crystal display menu // // Date: 2024-01-31 // // Keyboard layout: Four buttons // // Enter - to access current selection // // Up - to select previous item // // Down - to select next item // // Stop - to stop scanning right now #pragma once #include #include #include #include #include #include #include #include #define MENU_ID_RETURN -1 // ID of menu item that return to parent #define LINK_DEFINE(x, y) x##y #define MEMNU_CMD_HANDLER_PARAM dev_menu* menu, int id #define MENU_CMD_HANDLER_RET bool #define MENU_CMD_CALLBACK std::function class dev_menu : public refer { dev_menu* parent_ = nullptr; typedef struct _menu_item { std::string text; bool leaf; union { int id; // valid on leaf dev_menu *child; // valid on !leaf }; }MITEM; std::vector items_; int cur_ = 0; int sel_ = -1; bool check_item_ = false; bool need_ret_parent_ = true; int find_item(const char* text); public: dev_menu(bool check_item = false, bool need_ret_parent = true); protected: virtual ~dev_menu(); void set_parent(dev_menu* parent); public: bool add_menu(const char* text, int id); bool add_menu(const char* text, dev_menu* submenu); bool remove_menu(const char* text); void set_need_return_parent(bool need); bool move_to(bool next); // true - move to next, false - move to previous. if at end position of move direction, return false bool select(const char* txt); void reset_pos(void); // Function: access current menu // // Parameter: id - to receive ID of current menu item if leaf, fill '-1' if a submenu // // Return: current menu, user should call 'release' after use dev_menu* enter(int* id); // Function: get all menus text // // Parameter: text - to receive the menu text // // sel - to receive current setting index // // Return: current menu index int get_menu_text(std::vector& text, int& sel); }; class KeyMonitor; class Lcd; class ui_mgr : public refer { dev_menu* root_ = nullptr; dev_menu* cur_ = nullptr; volatile bool menu_mode_ = false; // whether LCD is displaying menu volatile bool run_ = true; volatile bool ready_enable_ = true; bool scanning_ = false; int paper_total_ = 0; int paper_cnt_ = 0; SIZE font_size_ = {16, 16}; POINT hold_pos_ = {0, 0}; std::map handler_; std::unique_ptr lcd_; std::unique_ptr keyboard_; class permanent_data : public refer { uint64_t history_cnt_ = 0; uint32_t roller_cnt_ = 0; uint32_t adden_ = 0; uint32_t speed_ = 0; std::string root_; const std::string his_name_ = "history-count"; const std::string rol_name_ = "roller-count"; #pragma pack(push) #pragma pack(1) typedef struct _speed_time { uint64_t ms; uint32_t speed; }SPEEDTM, *LPSPEEDTM; typedef struct _times { uint64_t history; uint64_t roller; }TIMES; #pragma pack(pop) std::map speed_times_; public: permanent_data() { root_ = utils::get_local_data_path() + PATH_SEPARATOR + "record"; utils::create_folder(root_.c_str()); root_ += PATH_SEPARATOR; char buf[1024] = {0}, *ptr = buf; FILE *src = fopen((root_ + his_name_).c_str(), "rb"); int len = 0; LPSPEEDTM pspt = nullptr; if(src) { len = fread(buf, 1, sizeof(buf), src); fclose(src); history_cnt_ = *(uint64_t*)buf; len -= sizeof(history_cnt_); ptr = buf + sizeof(history_cnt_); pspt = (LPSPEEDTM)ptr; for(int i = 0; i < len / sizeof(*pspt); ++i) { TIMES t = {0}; t.history = pspt[i].ms; speed_times_[pspt[i].speed] = t; } } src = fopen((root_ + rol_name_).c_str(), "rb"); if(src) { memset(buf, 0, sizeof(buf)); len = fread(buf, 1, sizeof(buf), src); fclose(src); roller_cnt_ = *(uint32_t*)buf; len -= sizeof(roller_cnt_); ptr = buf + sizeof(roller_cnt_); pspt = (LPSPEEDTM)ptr; for(int i = 0; i < len / sizeof(*pspt); ++i) { if(speed_times_.count(pspt[i].speed)) { speed_times_[pspt[i].speed].roller = pspt[i].ms; } else { TIMES t = {0}; t.roller = pspt[i].ms; speed_times_[pspt[i].speed] = t; } } } } protected: virtual ~permanent_data() { save(); } public: uint64_t history_count(void) { return history_cnt_; } uint32_t roller_count(void) { return roller_cnt_; } uint32_t increase_count(uint32_t run_ms, uint64_t* hiscnt = nullptr) { ++adden_; ++roller_cnt_; ++history_cnt_; speed_times_[speed_].history += run_ms; speed_times_[speed_].roller += run_ms; if(hiscnt) *hiscnt = history_cnt_; return roller_cnt_; } uint32_t clear_roller_count(void) { roller_cnt_ = 0; for(auto& v: speed_times_) v.second.roller = 0; return roller_cnt_; } void set_speed(uint32_t speed) { speed_ = speed; if(speed_times_.count(speed) == 0) { TIMES t = {0}; speed_times_[speed] = t; } } void save(void) { FILE *dst = fopen((root_ + his_name_).c_str(), "wb"); if(dst) { fwrite(&history_cnt_, 1, sizeof(history_cnt_), dst); for(auto& v: speed_times_) { SPEEDTM stm = {0}; stm.speed = v.first; stm.ms = v.second.history; fwrite(&stm, 1, sizeof(stm), dst); } fclose(dst); } dst = fopen((root_ + rol_name_).c_str(), "wb"); if(dst) { fwrite(&roller_cnt_, 1, sizeof(roller_cnt_), dst); for(auto& v: speed_times_) { SPEEDTM stm = {0}; stm.speed = v.first; stm.ms = v.second.roller; fwrite(&stm, 1, sizeof(stm), dst); } fclose(dst); } } }; refer_guard perm_data_; bool do_menu_command(int cmd); // return whether should hold UI ? void init(void); void clear(void); void refresh_lcd(bool cur_at_top); void move_to(bool next); void enter(void); typedef struct _disp_data { uint8_t x; uint8_t y; uint8_t cnt; // clear width when ptr[0] == nullptr uint8_t mask; // clear height when ptr[0] == nullptr uint8_t *ptr[16]; }DISPDATA; MUTEX ready_lck_; chronograph ready_watch_; DISPDATA ready_; safe_fifo disp_data_; safe_thread disp_thrd_; void thread_display(void); void display_ready(void); void set_ready_status_enabled(bool enable); void reset_ready_watch(void); int get_ready_watch_ms(void); public: ui_mgr(); protected: virtual ~ui_mgr(); public: void key_event(int key); };