diff --git a/.gitignore b/.gitignore index d99206f..830564c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ build/ *.o *.a /.idea +debug/ .target.cfg diff --git a/debug.sh b/debug.sh new file mode 100755 index 0000000..5914e93 --- /dev/null +++ b/debug.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd -P)" + +if [ "$TARGET" = "rpi" ]; then + export CROSS_COMPILE=armv8-rpi3-linux-gnueabihf- +else + export CROSS_COMPILE=mipsel-buildroot-linux-musl- +fi + +cd $CURRENT_DIR + +found=true +if [ ! -f debug/guppyscreen ]; then + echo "ERROR: Missing guppyscreen" + found=false +fi + +if [ ! -f debug/guppyscreen.debug ]; then + echo "ERROR: Missing guppyscreen.debug" + found=false +fi + +if [ ! -f debug/guppyscreen.core ]; then + echo "ERROR: Missing guppyscreen.core" + found=false +fi + +if [ "$found" != "true" ]; then + exit 1 +else + readelf -wk debug/guppyscreen > /dev/null + if [ $? -ne 0 ]; then + exit 1 + fi +fi + +docker run -ti -v $PWD:$PWD pellcorp/guppydev /bin/bash -c "cd $PWD && ${CROSS_COMPILE}gdb debug/guppyscreen -c debug/guppyscreen.core" diff --git a/grumpyscreen.cfg b/grumpyscreen.cfg index 791335a..ea92da6 100644 --- a/grumpyscreen.cfg +++ b/grumpyscreen.cfg @@ -22,7 +22,7 @@ port: 7125 [commands] factory_reset_cmd: /etc/init.d/S58factoryreset reset -guppy_restart_cmd: sudo systemctl restart grumpyscreen +guppy_restart_cmd: /etc/init.d/S99guppyscreen restart # this is actually only used when running with helper script guppy_update_cmd: switch_to_stock_cmd: /usr/data/pellcorp/k1/switch-to-stock.sh diff --git a/release.sh b/release.sh index ab4b97e..5ce2fe6 100755 --- a/release.sh +++ b/release.sh @@ -25,6 +25,7 @@ elif [ "$ASSET_NAME" = "guppyscreen-rpi" ]; then sed -i 's/display_rotate: 3/display_rotate: 0/g' $RELEASES_DIR/grumpyscreen.cfg # rpi does not have factory reset sed -i 's:/etc/init.d/S58factoryreset::g' $RELEASES_DIR/grumpyscreen.cfg + sed -i 's:/etc/init.d/S99guppyscreen restart:sudo systemctl restart grumpyscreen:g' $RELEASES_DIR/grumpyscreen.cfg # rpi does not have switch to stock sed -i 's:/usr/data/pellcorp/k1/switch-to-stock.sh::g' $RELEASES_DIR/grumpyscreen.cfg # for now no support command for rpi either diff --git a/src/console_panel.cpp b/src/console_panel.cpp index a6c7dc5..1f3ef7e 100644 --- a/src/console_panel.cpp +++ b/src/console_panel.cpp @@ -49,41 +49,46 @@ lv_obj_t *ConsolePanel::get_container() { } // thanks Chad -void ta_add_text_limit_lines(lv_obj_t * ta, const std::string &line) -{ - // Always append a newline at the end - std::string msg = line + "\n"; - lv_textarea_add_text(ta, msg.c_str()); - - // Get the full text after appending - const char * full = lv_textarea_get_text(ta); - - // Count lines - int line_count = 0; - const char *p = full; - while (*p) { - if(*p == '\n') line_count++; - p++; +void ta_add_text_limit_lines(lv_obj_t * ta, const std::string &line) { + // Always append a newline at the end + std::string msg = line + "\n"; + lv_textarea_add_text(ta, msg.c_str()); + + // Get the full text after appending + const char * full = lv_textarea_get_text(ta); + + // Count lines + int line_count = 0; + const char *p = full; + while (*p) { + if(*p == '\n') { + line_count++; } - if (p != full && *(p-1) != '\n') line_count++; + p++; + } - // Trim if too many lines - if (line_count > 100) { - int drop = line_count - 100; + if (p != full && *(p-1) != '\n') { + line_count++; + } - // Find pointer to first line we want to keep - const char * keep = full; - while(drop > 0 && *keep) { - if(*keep == '\n') drop--; - keep++; - } + // Trim if too many lines + if (line_count > 100) { + int drop = line_count - 100; - // Replace textarea with trimmed content - lv_textarea_set_text(ta, keep); + // Find pointer to first line we want to keep + const char * keep = full; + while(drop > 0 && *keep) { + if(*keep == '\n') drop--; + keep++; } - // Auto-scroll to bottom - lv_textarea_set_cursor_pos(ta, LV_TEXTAREA_CURSOR_LAST); + size_t keep_len = strlen(keep); + std::string trimmed_text(keep, keep_len); + lv_textarea_set_text(ta, trimmed_text.c_str()); + } + + // Auto-scroll to bottom + lv_textarea_set_cursor_pos(ta, LV_TEXTAREA_CURSOR_LAST); } void ConsolePanel::handle_macro_response(json &j) { diff --git a/src/print_panel.cpp b/src/print_panel.cpp index 9b80784..cff1680 100644 --- a/src/print_panel.cpp +++ b/src/print_panel.cpp @@ -17,6 +17,7 @@ PrintPanel::PrintPanel(KWebSocketClient &websocket, std::mutex &lock, PrintStatu : NotifyConsumer(lock) , ws(websocket) , files_cont(lv_obj_create(lv_scr_act())) + , spinner(lv_spinner_create(files_cont, 1000, 60)) , left_cont(lv_obj_create(files_cont)) , file_table(lv_table_create(left_cont)) , file_view(lv_obj_create(files_cont)) @@ -38,6 +39,10 @@ PrintPanel::PrintPanel(KWebSocketClient &websocket, std::mutex &lock, PrintStatu lv_obj_set_flex_flow(files_cont, LV_FLEX_FLOW_ROW); lv_obj_set_style_pad_all(files_cont, 0, 0); + lv_obj_add_flag(spinner, LV_OBJ_FLAG_FLOATING); + lv_obj_align(spinner, LV_ALIGN_CENTER, 0, 0); + lv_obj_move_foreground(spinner); + // left side cont lv_obj_set_size(left_cont, LV_PCT(50), LV_PCT(100)); lv_obj_clear_flag(left_cont, LV_OBJ_FLAG_SCROLLABLE); @@ -107,6 +112,9 @@ void PrintPanel::consume(json &j) { } void PrintPanel::subscribe() { + lv_obj_clear_flag(file_table, LV_OBJ_FLAG_CLICKABLE); + lv_obj_clear_flag(spinner, LV_OBJ_FLAG_HIDDEN); + ws.send_jsonrpc("server.files.list", R"({"root":"gcodes"})"_json, [this](json &d) { std::lock_guard lock(lv_lock); std::string cur_path = cur_dir->full_path; @@ -119,10 +127,16 @@ void PrintPanel::subscribe() { root.add_path(KUtils::split(f["path"], '/'), f["path"], f["modified"].template get()); } } + Tree *dir = root.find_path(KUtils::split(cur_path, '/')); // need to simplify this using the directory endpoint cur_dir = dir; + this->populate_files(d); + + // Re-enable the table interaction ONLY after the data is stable + lv_obj_add_flag(file_table, LV_OBJ_FLAG_CLICKABLE); + lv_obj_add_flag(spinner, LV_OBJ_FLAG_HIDDEN); }); } @@ -139,7 +153,7 @@ void PrintPanel::foreground() { status_btn.enable(); print_btn.disable(); } - + lv_obj_move_foreground(files_cont); subscribe(); } @@ -240,22 +254,50 @@ void PrintPanel::show_file_detail(Tree *f) { if (f->contains_metadata()) { file_panel.refresh_view(f->metadata, f->full_path); } else { - LOG_TRACE("getting metadata for {}", f->name); + std::string filename = f->full_path; + std::string dir_path = cur_dir->full_path; + LOG_TRACE("getting metadata for {}/{}", dir_path, filename); + ws.send_jsonrpc("server.files.metadata", json::parse(R"({"filename":")" + f->full_path + R"("})"), - [f, this](json &d) { this->handle_metadata(f, d); }); + [this, filename, dir_path](json &d) { + this->handle_metadata(filename, dir_path, d); + }); } } } -void PrintPanel::handle_metadata(Tree *f, json &j) { - LOG_TRACE("handling metadata callback"); - if (f->is_leaf()) { +void PrintPanel::handle_metadata(const std::string& filename, const std::string& dir_path, json &j) { + LOG_TRACE("handling metadata for {}/{}", dir_path, filename); + + std::vector dir_segments; + if (!dir_path.empty()) { + dir_segments = KUtils::split(dir_path, '/'); + } + + // 1. Find the Parent Directory Node + Tree *parent_dir = &root; // Start search at root + + if (!dir_segments.empty()) { + // Find the directory node based on the captured path segments + parent_dir = root.find_path(dir_segments); + } + + Tree *f = nullptr; + if (parent_dir != nullptr) { + // 2. Find the File Node as a child of the parent directory + // We are looking for the file using the filename string. + f = parent_dir->get_child(filename.c_str()); + } + + if (f != nullptr && f->is_leaf()) { if (j.contains("result")) { std::lock_guard lock(lv_lock); f->set_metadata(j); file_panel.refresh_view(f->metadata, f->full_path); } + } else { + LOG_TRACE("is not a leaf {}/{}", dir_path, filename); } } diff --git a/src/print_panel.h b/src/print_panel.h index 7908fe3..9f41edc 100644 --- a/src/print_panel.h +++ b/src/print_panel.h @@ -19,7 +19,7 @@ class PrintPanel : public NotifyConsumer { void subscribe(); void foreground(); void handle_callback(lv_event_t *event); - void handle_metadata(Tree *, json & data); + void handle_metadata(const std::string& filename, const std::string& dir_path, json & data); void handle_back_btn(lv_event_t *event); void handle_print_callback(lv_event_t *event); void handle_status_btn(lv_event_t *event); @@ -51,9 +51,8 @@ class PrintPanel : public NotifyConsumer { KWebSocketClient &ws; lv_obj_t *files_cont; - + lv_obj_t *spinner; lv_obj_t *left_cont; - lv_obj_t *file_table; lv_obj_t *file_view; ButtonContainer status_btn;