From 3a564e713a4ba76d2470a540954cfd4d5f204ecd Mon Sep 17 00:00:00 2001 From: tcomlab Date: Tue, 14 Apr 2026 16:39:02 +0300 Subject: [PATCH] Release 2026-04-14 spool and filament workflow --- src/app.h | 6 + src/connectivity.cpp | 345 +++++++++++++++++++++++++++++++++++++++++-- src/main.cpp | 12 +- src/ui.cpp | 113 +++++++++++++- 4 files changed, 459 insertions(+), 17 deletions(-) diff --git a/src/app.h b/src/app.h index 97c1816..f1f44cc 100644 --- a/src/app.h +++ b/src/app.h @@ -30,6 +30,7 @@ constexpr uint32_t kWifiConnectTimeoutMs = 20000; constexpr uint32_t kSensorTimeoutMs = 1500; constexpr uint8_t kAverageSamples = 16; constexpr uint8_t kRawFilterWindow = 8; +constexpr uint8_t kBatchCodeMaxLength = 4; constexpr float kRawFilterAlpha = 0.20f; constexpr float kWeightStepHysteresisGrams = 0.75f; constexpr lv_coord_t kScreenMargin = 12; @@ -71,10 +72,12 @@ struct WifiData struct InventreeMatch { int id = 0; + int part_id = 0; float quantity = 0.0f; String location; String part_name; String batch; + String spool_weight; }; constexpr size_t kInventreeMaxMatches = 8; @@ -109,6 +112,8 @@ struct UiRefs lv_obj_t *weight_note_label = nullptr; lv_obj_t *weight_raw_value = nullptr; lv_obj_t *workflow_batch_ta = nullptr; + lv_obj_t *workflow_spool_weight_value = nullptr; + lv_obj_t *workflow_filament_weight_value = nullptr; lv_obj_t *workflow_stock_name_label = nullptr; lv_obj_t *workflow_stock_meta_label = nullptr; @@ -173,6 +178,7 @@ void saveInventreeConfig(); void loadInventreeConfig(); float getCurrentWeightGrams(); +bool getFilamentWeightGrams(int32_t &grams); void performTare(); void performCalibration(float reference_grams); void clearCalibration(); diff --git a/src/connectivity.cpp b/src/connectivity.cpp index 1b51d94..04f915b 100644 --- a/src/connectivity.cpp +++ b/src/connectivity.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -11,6 +12,13 @@ namespace { +bool performInventreeRequest(const char *method, + const String &path, + const String &request_body, + int &http_code, + String &response_body, + String &error_text); + String normalizeInventreeBaseUrl(const String &value) { String base_url = value; @@ -50,6 +58,165 @@ String urlEncode(const String &value) return encoded; } +void sanitizeBatchCode(String &value) +{ + value.trim(); + + if (value.length() > kBatchCodeMaxLength) + { + value.remove(kBatchCodeMaxLength); + } +} + +String formatSpoolWeightValue(const String &value_text) +{ + String value = value_text; + value.trim(); + return value; +} + +String normalizeParameterKey(const String &value) +{ + String normalized; + normalized.reserve(value.length()); + + for (size_t i = 0; i < value.length(); ++i) + { + const unsigned char ch = static_cast(value[i]); + if (isalnum(ch)) + { + normalized += static_cast(tolower(ch)); + } + } + + return normalized; +} + +bool isSpoolWeightParameterName(const String ¶meter_name) +{ + const String normalized_name = normalizeParameterKey(parameter_name); + return normalized_name == "spoolwegh" || normalized_name == "spoolweght" || normalized_name == "spoolweight"; +} + +String extractSpoolWeightFromPartDetail(JsonVariantConst part_detail) +{ + JsonVariantConst parameters_map = part_detail["parameters_map"]; + + if (parameters_map.isNull()) + { + return ""; + } + + const char *const spool_weight = + parameters_map["SpoolWegh"] | + parameters_map["SpoolWeght"] | + parameters_map["SpoolWeight"] | + parameters_map["spool_weight"] | + parameters_map["spoolwegh"] | + parameters_map["spoolweght"]; + + return spool_weight != nullptr ? formatSpoolWeightValue(String(spool_weight)) : String(); +} + +String formatInventreeParameterValue(JsonObjectConst parameter) +{ + String value = String(static_cast(parameter["data"] | "")); + value.trim(); + + if (value.isEmpty()) + { + const JsonVariantConst numeric_value = parameter["data_numeric"]; + if (!numeric_value.isNull()) + { + const float number = numeric_value.as(); + const bool whole_number = fabsf(number - roundf(number)) < 0.05f; + value = String(number, whole_number ? 0 : 1); + } + } + + String units = String(static_cast(parameter["template_detail"]["units"] | "")); + units.trim(); + + if (!value.isEmpty() && !units.isEmpty()) + { + String normalized_value = value; + normalized_value.toLowerCase(); + String normalized_units = units; + normalized_units.toLowerCase(); + + if (!normalized_value.endsWith(normalized_units)) + { + value += " "; + value += units; + } + } + + return value; +} + +String extractSpoolWeightFromParameters(JsonArrayConst parameters) +{ + for (JsonObjectConst parameter : parameters) + { + const String parameter_name = String(static_cast(parameter["template_detail"]["name"] | "")); + if (isSpoolWeightParameterName(parameter_name)) + { + return formatInventreeParameterValue(parameter); + } + } + + return ""; +} + +bool tryParseSpoolWeightGrams(int32_t &grams) +{ + grams = 0; + + if (inventree_data.selected_match < 0 || inventree_data.selected_match >= inventree_data.stored_matches) + { + return false; + } + + const String &spool_weight = inventree_data.matches[inventree_data.selected_match].spool_weight; + if (spool_weight.isEmpty()) + { + return false; + } + + bool has_digit = false; + for (size_t i = 0; i < spool_weight.length(); ++i) + { + if (isdigit(static_cast(spool_weight[i]))) + { + has_digit = true; + break; + } + } + + if (!has_digit) + { + return false; + } + + const float parsed_value = spool_weight.toFloat(); + grams = parsed_value <= 0.0f ? 0 : static_cast(lroundf(parsed_value)); + return true; +} + +bool tryGetMeasuredWeightGrams(int32_t &grams) +{ + grams = 0; + + if (!sensor_online || !calibration.has_offset || calibration.scale <= 0.0001f) + { + return false; + } + + const int32_t stable_grams = displayed_weight_valid ? displayed_weight_grams : static_cast(lroundf(getCurrentWeightGrams())); + grams = stable_grams < 0 ? 0 : stable_grams; + return true; +} + void syncInventreeInputs(bool persist) { inventree_data.base_url = ui.inventree_url_ta != nullptr @@ -77,7 +244,16 @@ void syncInventreeInputs(bool persist) inventree_data.batch = batch_input != nullptr ? String(lv_textarea_get_text(batch_input)) : inventree_data.batch; - inventree_data.batch.trim(); + sanitizeBatchCode(inventree_data.batch); + + if (batch_input != nullptr) + { + const String current_batch_text = String(lv_textarea_get_text(batch_input)); + if (current_batch_text != inventree_data.batch) + { + lv_textarea_set_text(batch_input, inventree_data.batch.c_str()); + } + } if (ui.workflow_batch_ta != nullptr && batch_input != ui.workflow_batch_ta) { @@ -144,6 +320,12 @@ void updateInventreeSelectionSummary() } } + if (!match.spool_weight.isEmpty()) + { + text += "\nSpool "; + text += match.spool_weight; + } + inventree_data.result_text = text; updateInventreeLabels(); } @@ -229,6 +411,61 @@ bool performInventreeHttpRequest(TClient &client, return http_code > 0; } +bool loadSpoolWeightForMatch(InventreeMatch &match, String &error_text) +{ + error_text = ""; + + if (!match.spool_weight.isEmpty() || match.part_id <= 0) + { + return true; + } + + int http_code = 0; + String response_body; + const String path = "/api/part/" + String(match.part_id) + "/?parameters=true"; + + if (!performInventreeRequest("GET", path, "", http_code, response_body, error_text)) + { + return false; + } + + if (http_code != 200) + { + error_text = response_body.length() > 0 ? response_body : "Unexpected part response."; + return false; + } + + JsonDocument filter; + filter["parameters_map"]["SpoolWegh"] = true; + filter["parameters_map"]["SpoolWeght"] = true; + filter["parameters_map"]["SpoolWeight"] = true; + filter["parameters_map"]["spool_weight"] = true; + filter["parameters_map"]["spoolwegh"] = true; + filter["parameters_map"]["spoolweght"] = true; + JsonArray parameters_filter = filter["parameters"].to(); + JsonObject parameter_filter = parameters_filter.add(); + parameter_filter["data"] = true; + parameter_filter["data_numeric"] = true; + parameter_filter["template_detail"]["name"] = true; + parameter_filter["template_detail"]["units"] = true; + + JsonDocument doc; + const DeserializationError json_error = deserializeJson(doc, response_body, DeserializationOption::Filter(filter)); + if (json_error) + { + error_text = json_error.c_str(); + return false; + } + + match.spool_weight = extractSpoolWeightFromPartDetail(doc.as()); + if (match.spool_weight.isEmpty()) + { + match.spool_weight = extractSpoolWeightFromParameters(doc["parameters"].as()); + } + + return true; +} + bool requestInventreeToken(String &error_text) { error_text = ""; @@ -378,18 +615,43 @@ bool getMeasuredWeightForInventree(String &quantity_text) { quantity_text = ""; - if (!sensor_online || !calibration.has_offset || calibration.scale <= 0.0001f) + int32_t filament_grams = 0; + if (!getFilamentWeightGrams(filament_grams)) { return false; } - const int32_t stable_grams = displayed_weight_valid ? displayed_weight_grams : static_cast(lroundf(getCurrentWeightGrams())); - quantity_text = String(stable_grams < 0 ? 0 : stable_grams); + quantity_text = String(filament_grams); return true; } } // namespace +bool getFilamentWeightGrams(int32_t &grams) +{ + grams = 0; + + int32_t measured_grams = 0; + if (!tryGetMeasuredWeightGrams(measured_grams)) + { + return false; + } + + int32_t spool_grams = 0; + if (!tryParseSpoolWeightGrams(spool_grams)) + { + return false; + } + + grams = measured_grams - spool_grams; + if (grams < 0) + { + grams = 0; + } + + return true; +} + void saveWifiConfig() { prefs.putString("wifi_ssid", wifi_data.ssid); @@ -424,7 +686,21 @@ void loadInventreeConfig() } inventree_data.token = prefs.getString("inv_tok", ""); - inventree_data.batch = prefs.getString("inv_batch", prefs.getString("inv_ipn", "")); + + if (prefs.isKey("inv_batch")) + { + inventree_data.batch = prefs.getString("inv_batch", ""); + } + else if (prefs.isKey("inv_ipn")) + { + inventree_data.batch = prefs.getString("inv_ipn", ""); + } + else + { + inventree_data.batch = ""; + } + + sanitizeBatchCode(inventree_data.batch); } bool inventreeConfigured() @@ -437,6 +713,11 @@ void clearInventreeMatches() inventree_data.total_matches = 0; inventree_data.stored_matches = 0; inventree_data.selected_match = -1; + + for (size_t i = 0; i < kInventreeMaxMatches; ++i) + { + inventree_data.matches[i] = InventreeMatch(); + } } void connectWifi(const String &ssid, const String &password, bool save_credentials) @@ -650,12 +931,19 @@ void findInventreeStockByBatch() JsonArray filter_results = filter["results"].to(); JsonObject filter_item = filter_results.add(); filter_item["pk"] = true; + filter_item["part"] = true; filter_item["batch"] = true; filter_item["quantity"] = true; filter_item["location"] = true; filter_item["location_detail"]["pathstring"] = true; filter_item["location_detail"]["name"] = true; filter_item["part_detail"]["name"] = true; + filter_item["part_detail"]["parameters_map"]["SpoolWegh"] = true; + filter_item["part_detail"]["parameters_map"]["SpoolWeght"] = true; + filter_item["part_detail"]["parameters_map"]["SpoolWeight"] = true; + filter_item["part_detail"]["parameters_map"]["spool_weight"] = true; + filter_item["part_detail"]["parameters_map"]["spoolwegh"] = true; + filter_item["part_detail"]["parameters_map"]["spoolweght"] = true; JsonDocument doc; const DeserializationError json_error = deserializeJson(doc, response, DeserializationOption::Filter(filter)); @@ -680,10 +968,13 @@ void findInventreeStockByBatch() } InventreeMatch &match = inventree_data.matches[inventree_data.stored_matches]; + match = InventreeMatch(); match.id = item["pk"] | 0; + match.part_id = item["part"] | 0; match.quantity = item["quantity"] | 0.0f; match.batch = String(static_cast(item["batch"] | "")); match.part_name = String(static_cast(item["part_detail"]["name"] | "")); + match.spool_weight = extractSpoolWeightFromPartDetail(item["part_detail"]); const char *pathstring = item["location_detail"]["pathstring"] | nullptr; const char *name = item["location_detail"]["name"] | nullptr; @@ -713,6 +1004,22 @@ void findInventreeStockByBatch() } inventree_data.selected_match = 0; + if (inventree_data.selected_match < inventree_data.stored_matches) + { + String spool_error; + if (!loadSpoolWeightForMatch(inventree_data.matches[inventree_data.selected_match], spool_error)) + { + Serial.printf("InvenTree spool weight fallback failed for part %d: %s\n", + inventree_data.matches[inventree_data.selected_match].part_id, + spool_error.c_str()); + } + else + { + Serial.printf("InvenTree spool weight for part %d: %s\n", + inventree_data.matches[inventree_data.selected_match].part_id, + inventree_data.matches[inventree_data.selected_match].spool_weight.c_str()); + } + } inventree_data.status_text = "Found " + String(inventree_data.total_matches) + " item(s)"; setInventreeNote(inventree_data.total_matches > inventree_data.stored_matches ? "More items exist than shown here. Use the first loaded results." @@ -738,6 +1045,20 @@ void selectInventreeMatch(int delta) inventree_data.selected_match = 0; } + String spool_error; + if (!loadSpoolWeightForMatch(inventree_data.matches[inventree_data.selected_match], spool_error)) + { + Serial.printf("InvenTree spool weight fallback failed for part %d: %s\n", + inventree_data.matches[inventree_data.selected_match].part_id, + spool_error.c_str()); + } + else + { + Serial.printf("InvenTree spool weight for part %d: %s\n", + inventree_data.matches[inventree_data.selected_match].part_id, + inventree_data.matches[inventree_data.selected_match].spool_weight.c_str()); + } + updateInventreeSelectionSummary(); } @@ -754,7 +1075,7 @@ void pushMeasuredWeightToInventree() String quantity_text; if (!getMeasuredWeightForInventree(quantity_text)) { - setInventreeNote("Weight is not ready. Calibrate HX711 in grams first."); + setInventreeNote("Filament Weight is not ready. Check calibration and Spool Weight."); return; } @@ -763,7 +1084,7 @@ void pushMeasuredWeightToInventree() JsonObject item = items.add(); item["pk"] = inventree_data.matches[inventree_data.selected_match].id; item["quantity"] = quantity_text; - body_doc["notes"] = "Updated from FilamentGauger sensor"; + body_doc["notes"] = "Updated net filament weight from FilamentGauger sensor"; String request_body; serializeJson(body_doc, request_body); @@ -776,7 +1097,7 @@ void pushMeasuredWeightToInventree() { inventree_data.status_text = "Update error"; inventree_data.result_text = error_text; - setInventreeNote("Could not submit measured weight to InvenTree."); + setInventreeNote("Could not submit Filament Weight to InvenTree."); updateInventreeLabels(); return; } @@ -785,17 +1106,17 @@ void pushMeasuredWeightToInventree() { inventree_data.status_text = "HTTP " + String(http_code); inventree_data.result_text = response.length() > 0 ? response : "Unexpected API response."; - setInventreeNote("Stock count update failed."); + setInventreeNote("Filament Weight update failed."); updateInventreeLabels(); return; } inventree_data.status_text = "Stock updated"; - inventree_data.result_text = "Sent " + quantity_text + " g to stock item ID " + + inventree_data.result_text = "Sent filament " + quantity_text + " g to stock item ID " + String(inventree_data.matches[inventree_data.selected_match].id); - setInventreeNote("Measured weight was written to InvenTree stock quantity."); + setInventreeNote("Filament Weight was written to InvenTree stock quantity."); updateInventreeLabels(); findInventreeStockByBatch(); - setInventreeNote("Measured weight was written to InvenTree stock quantity."); + setInventreeNote("Filament Weight was written to InvenTree stock quantity."); } diff --git a/src/main.cpp b/src/main.cpp index 934f1d2..247b9a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ namespace { String serial_line; +constexpr lv_display_rotation_t kDisplayRotation = LV_DISPLAY_ROTATION_180; void handleSerialCommand(const String &command) { @@ -112,11 +113,17 @@ void setup() hx711.begin(HX711_DOUT_PIN, HX711_SCK_PIN); + log_i("Initializing smart display..."); smartdisplay_init(); - auto *display = lv_disp_get_default(); - lv_display_set_rotation(display, LV_DISPLAY_ROTATION_180); + lv_display_t *display = lv_display_get_default(); + if (display != nullptr) + { + lv_display_set_rotation(display, kDisplayRotation); + } + log_i("Building UI..."); buildUi(); + log_i("UI ready."); lv_tick_inc(1); lv_timer_handler(); delay(20); @@ -134,6 +141,7 @@ void setup() next_ui_refresh_ms = millis(); log_i("HX711 pins: SCK=%d DOUT=%d", HX711_SCK_PIN, HX711_DOUT_PIN); + log_i("Setup complete."); } void loop() diff --git a/src/ui.cpp b/src/ui.cpp index 933820e..137c4a6 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -214,6 +214,7 @@ void openTextEditor(lv_obj_t *target, const char *title) setLabelTextIfChanged(ui.text_editor_title, title != nullptr ? title : "Edit"); lv_textarea_set_password_mode(ui.text_editor_ta, lv_textarea_get_password_mode(target)); lv_textarea_set_accepted_chars(ui.text_editor_ta, batch_input ? "0123456789" : nullptr); + lv_textarea_set_max_length(ui.text_editor_ta, batch_input ? kBatchCodeMaxLength : 0); lv_textarea_set_text(ui.text_editor_ta, lv_textarea_get_text(target)); lv_keyboard_set_textarea(ui.wifi_keyboard, ui.text_editor_ta); lv_keyboard_set_mode(ui.wifi_keyboard, batch_input ? LV_KEYBOARD_MODE_USER_1 : LV_KEYBOARD_MODE_TEXT_LOWER); @@ -610,8 +611,45 @@ lv_obj_t *createTextInput(lv_obj_t *parent, return textarea; } +lv_obj_t *createValueField(lv_obj_t *parent, + lv_coord_t x, + lv_coord_t y, + lv_coord_t width, + const char *value_text) +{ + lv_obj_t *field = lv_obj_create(parent); + lv_obj_set_size(field, width, 46); + lv_obj_set_pos(field, x, y); + lv_obj_remove_flag(field, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_set_style_radius(field, 14, 0); + lv_obj_set_style_bg_color(field, lv_color_black(), 0); + lv_obj_set_style_bg_opa(field, LV_OPA_COVER, 0); + lv_obj_set_style_border_color(field, lv_color_hex(0x1A1A1A), 0); + lv_obj_set_style_border_width(field, 1, 0); + lv_obj_set_style_shadow_width(field, 0, 0); + lv_obj_set_style_pad_hor(field, 14, 0); + lv_obj_set_style_pad_ver(field, 10, 0); + + lv_obj_t *value_label = lv_label_create(field); + lv_obj_set_width(value_label, width - 28); + lv_obj_set_style_text_color(value_label, lv_color_hex(0xF5FAFC), 0); + lv_obj_set_style_text_font(value_label, &lv_font_montserrat_18, 0); + lv_label_set_long_mode(value_label, LV_LABEL_LONG_WRAP); + lv_label_set_text(value_label, value_text != nullptr ? value_text : "--"); + lv_obj_align(value_label, LV_ALIGN_LEFT_MID, 0, 0); + + return value_label; +} + void buildWeightPage() { + constexpr lv_coord_t kBatchFieldWidth = 116; + constexpr lv_coord_t kFieldGap = 12; + constexpr lv_coord_t kSpoolFieldX = 24 + kBatchFieldWidth + kFieldGap; + constexpr lv_coord_t kSpoolFieldWidth = 210; + constexpr lv_coord_t kFilamentFieldX = kSpoolFieldX + kSpoolFieldWidth + kFieldGap; + constexpr lv_coord_t kFilamentFieldWidth = 210; + lv_obj_t *page = createPageContainer(ui.content_panel); ui.pages[pageIndex(PageId::Weight)] = page; createPageHeader(page, "Main", "Weigh filament spools by Batch Code and write grams to InvenTree"); @@ -655,13 +693,30 @@ void buildWeightPage() lv_obj_set_style_text_font(batch_label, &lv_font_montserrat_14, 0); lv_label_set_text(batch_label, "Batch Code"); - ui.workflow_batch_ta = createTextInput(page, 24, 234, kContentInnerWidth, "Main Batch Code", "Scan or type Batch Code", false); + ui.workflow_batch_ta = createTextInput(page, 24, 234, kBatchFieldWidth, "Main Batch Code", "Code", false); lv_textarea_set_accepted_chars(ui.workflow_batch_ta, "0123456789"); + lv_textarea_set_max_length(ui.workflow_batch_ta, kBatchCodeMaxLength); if (!inventree_data.batch.isEmpty()) { lv_textarea_set_text(ui.workflow_batch_ta, inventree_data.batch.c_str()); } + lv_obj_t *spool_weight_label = lv_label_create(page); + lv_obj_set_pos(spool_weight_label, kSpoolFieldX, 210); + lv_obj_set_style_text_color(spool_weight_label, lv_color_hex(0x7FA1B2), 0); + lv_obj_set_style_text_font(spool_weight_label, &lv_font_montserrat_14, 0); + lv_label_set_text(spool_weight_label, "Spool Weight"); + + ui.workflow_spool_weight_value = createValueField(page, kSpoolFieldX, 234, kSpoolFieldWidth, "--"); + + lv_obj_t *filament_weight_label = lv_label_create(page); + lv_obj_set_pos(filament_weight_label, kFilamentFieldX, 210); + lv_obj_set_style_text_color(filament_weight_label, lv_color_hex(0x7FA1B2), 0); + lv_obj_set_style_text_font(filament_weight_label, &lv_font_montserrat_14, 0); + lv_label_set_text(filament_weight_label, "Filament Weight"); + + ui.workflow_filament_weight_value = createValueField(page, kFilamentFieldX, 234, kFilamentFieldWidth, "--"); + lv_obj_t *stock_title = lv_label_create(page); lv_obj_set_pos(stock_title, 24, 290); lv_obj_set_style_text_color(stock_title, lv_color_hex(0x7FA1B2), 0); @@ -685,7 +740,7 @@ void buildWeightPage() ui.weight_note_label = lv_label_create(page); styleCenteredText(ui.weight_note_label, kContentInnerWidth, &lv_font_montserrat_14, lv_color_hex(0x8FA3AC)); lv_label_set_long_mode(ui.weight_note_label, LV_LABEL_LONG_WRAP); - lv_label_set_text(ui.weight_note_label, "Enter Batch Code, confirm input, then tap Write Weight."); + lv_label_set_text(ui.weight_note_label, "Enter Batch Code, confirm input, verify Spool and Filament Weight, then tap Write Weight."); alignTopCenter(ui.weight_note_label, 422); } @@ -800,6 +855,7 @@ void buildInventreePage() ui.inventree_batch_ta = createTextInput(page, kLeftColumnX, 276, kColumnWidth, "InvenTree Batch Code", "Batch Code", false); lv_textarea_set_accepted_chars(ui.inventree_batch_ta, "0123456789"); + lv_textarea_set_max_length(ui.inventree_batch_ta, kBatchCodeMaxLength); if (!inventree_data.batch.isEmpty()) { lv_textarea_set_text(ui.inventree_batch_ta, inventree_data.batch.c_str()); @@ -978,6 +1034,21 @@ void refreshUi() setLabelTextIfChanged(ui.mode_label, ""); setWeightStatus("RAW", lv_color_hex(0x1F5E7A)); } + + if (ui.workflow_filament_weight_value != nullptr) + { + int32_t filament_grams = 0; + if (getFilamentWeightGrams(filament_grams)) + { + char buffer[24]; + snprintf(buffer, sizeof(buffer), "%ld g", static_cast(filament_grams)); + setLabelTextIfChanged(ui.workflow_filament_weight_value, buffer); + } + else + { + setLabelTextIfChanged(ui.workflow_filament_weight_value, "--"); + } + } } void setActivePage(PageId page) @@ -1102,6 +1173,12 @@ void updateInventreeLabels() meta += match.location; } + if (!match.spool_weight.isEmpty()) + { + meta += " | Spool "; + meta += match.spool_weight; + } + setLabelTextIfChanged(ui.workflow_stock_name_label, title.c_str()); setLabelTextIfChanged(ui.workflow_stock_meta_label, meta.c_str()); } @@ -1118,6 +1195,36 @@ void updateInventreeLabels() "The device will load the stock item and prepare the current weight for writeback."); } } + + if (ui.workflow_spool_weight_value != nullptr) + { + const char *spool_weight_text = "--"; + if (inventree_data.selected_match >= 0 && inventree_data.selected_match < inventree_data.stored_matches) + { + const String &spool_weight = inventree_data.matches[inventree_data.selected_match].spool_weight; + if (!spool_weight.isEmpty()) + { + spool_weight_text = spool_weight.c_str(); + } + } + + setLabelTextIfChanged(ui.workflow_spool_weight_value, spool_weight_text); + } + + if (ui.workflow_filament_weight_value != nullptr) + { + int32_t filament_grams = 0; + if (getFilamentWeightGrams(filament_grams)) + { + char buffer[24]; + snprintf(buffer, sizeof(buffer), "%ld g", static_cast(filament_grams)); + setLabelTextIfChanged(ui.workflow_filament_weight_value, buffer); + } + else + { + setLabelTextIfChanged(ui.workflow_filament_weight_value, "--"); + } + } } void buildUi() @@ -1240,7 +1347,7 @@ void buildUi() layoutTextEditor(); lv_screen_load(ui.screen); - setWeightNote("Enter Batch Code, confirm input, then tap Write Weight."); + setWeightNote("Enter Batch Code, confirm input, verify Spool and Filament Weight, then tap Write Weight."); setCalibrationNote("Empty the platform, tap Tare, place the 100 g reference weight, then tap Cal."); if (wifi_data.ssid.isEmpty())