Compare commits
1 Commits
release-20
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a564e713a |
@@ -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();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <HTTPClient.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -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<unsigned char>(value[i]);
|
||||
if (isalnum(ch))
|
||||
{
|
||||
normalized += static_cast<char>(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<const char *>(parameter["data"] | ""));
|
||||
value.trim();
|
||||
|
||||
if (value.isEmpty())
|
||||
{
|
||||
const JsonVariantConst numeric_value = parameter["data_numeric"];
|
||||
if (!numeric_value.isNull())
|
||||
{
|
||||
const float number = numeric_value.as<float>();
|
||||
const bool whole_number = fabsf(number - roundf(number)) < 0.05f;
|
||||
value = String(number, whole_number ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
String units = String(static_cast<const char *>(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<const char *>(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<unsigned char>(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<int32_t>(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<int32_t>(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<JsonArray>();
|
||||
JsonObject parameter_filter = parameters_filter.add<JsonObject>();
|
||||
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<JsonVariantConst>());
|
||||
if (match.spool_weight.isEmpty())
|
||||
{
|
||||
match.spool_weight = extractSpoolWeightFromParameters(doc["parameters"].as<JsonArrayConst>());
|
||||
}
|
||||
|
||||
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<int32_t>(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<JsonArray>();
|
||||
JsonObject filter_item = filter_results.add<JsonObject>();
|
||||
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<const char *>(item["batch"] | ""));
|
||||
match.part_name = String(static_cast<const char *>(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<JsonObject>();
|
||||
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.");
|
||||
}
|
||||
|
||||
12
src/main.cpp
12
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()
|
||||
|
||||
113
src/ui.cpp
113
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<long>(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<long>(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())
|
||||
|
||||
Reference in New Issue
Block a user