harktech: parse K2 Plus CFS slot state values (1=selected, 2=loaded)

Root-cause + fix for the K2 Plus sparse-slot / wrong-slot-mapping bug
reported by multiple testers on Reddit and PR #13744 (DaviBe92, Psych0SW,
swilsonnc, TrainAss, Gullible-Price-4257).

DaviBe92 supplied a raw /box/getRealBoxesInfo payload from a K2 Plus
running printer FW 1.1.5.2 / CFS 1.2.2 that shows the K2 Plus uses
THREE state values for slots:

  state: 0 → empty
  state: 1 → loaded AND currently selected as the active spool
  state: 2 → loaded but not currently selected

K2 (base) and K2 Pro firmwares — confirmed against the maintainer's
test printer — only use 0/1. Our parser had assumed the 0/1 form and
filtered with `if (state != 1) continue`, dropping every state=2 slot.

Symptoms this explains:
* DaviBe92: 3 spools loaded, only 1 displayed (the state=1 slot).
* Psych0SW / swilsonnc: 2 of 4 slots returned, "2nd shows 3rd's data"
  — the parser dropped slots with state=2, leaving a sparse map that
  PresetBundle::sync_ams_list then packs into consecutive UI trays.
* TrainAss / Gullible-Price-4257: "No loaded slots detected" — likely
  the same root cause when zero slots happen to be state=1.

Fix: treat any non-zero state as loaded. Belt-and-braces: also skip
entries that are blanked-out (vendor and type both empty) regardless of
state, in case a future firmware uses yet another encoding for empty.

No change required for K2 / K2 Pro behaviour — they already only emit
state=0 (empty) or state=1 (loaded), and the new filter accepts both.
This commit is contained in:
grant0013
2026-05-20 12:11:20 +00:00
parent 3df71d86aa
commit c27339dac5

View File

@@ -197,14 +197,27 @@ bool CrealityPrintAgent::parse_cfs_response(const std::string& response,
continue;
for (const auto& mat : box["materials"]) {
if (mat.value("state", 0) != 1) continue; // empty slot
// CFS slot state encoding observed across K2 family firmwares:
// * K2 (base) / K2 Pro : 0 = empty, 1 = loaded.
// * K2 Plus (1.1.5.5/CFS 1.4.2 onwards): 0 = empty,
// 1 = loaded AND currently
// selected as the active
// spool for printing,
// 2 = loaded but not selected.
// We treat anything non-zero as loaded. Belt-and-braces: also skip
// entries that look blank (no vendor and no type) regardless of state.
const int s_state = mat.value("state", 0);
const std::string s_vendor = mat.value("vendor", std::string());
const std::string s_type = mat.value("type", std::string());
if (s_state == 0) continue; // explicitly empty
if (s_vendor.empty() && s_type.empty()) continue; // blank entry — likely empty under a different state encoding
CFSSlot s;
s.box_id = cfs_index;
s.slot_id = mat.value("id", 0);
s.vendor = mat.value("vendor", "");
s.vendor = s_vendor;
s.brand_name = mat.value("name", "");
s.filament_type = mat.value("type", "");
s.filament_type = s_type;
s.color_hex = mat.value("color", "#FFFFFF");
// Creality reports colour as "#0RRGGBB" (8 chars with a leading zero