diff --git a/src/slic3r/GUI/DeviceCore/CMakeLists.txt b/src/slic3r/GUI/DeviceCore/CMakeLists.txt index e06cbe0abe..0277c591bd 100644 --- a/src/slic3r/GUI/DeviceCore/CMakeLists.txt +++ b/src/slic3r/GUI/DeviceCore/CMakeLists.txt @@ -60,6 +60,19 @@ list(APPEND SLIC3R_GUI_SOURCES GUI/DeviceCore/DevUtil.cpp GUI/DeviceCore/DevUtilBackend.h GUI/DeviceCore/DevUtilBackend.cpp + GUI/DeviceCore/DevAxis.h + GUI/DeviceCore/DevAxis.cpp + GUI/DeviceCore/DevAxisCtrl.cpp + GUI/DeviceCore/DevCalib.h + GUI/DeviceCore/DevCalib.cpp + GUI/DeviceCore/DevChamber.h + GUI/DeviceCore/DevChamber.cpp + GUI/DeviceCore/DevChamberCtrl.cpp + GUI/DeviceCore/DevStatus.h + GUI/DeviceCore/DevStatus.cpp + GUI/DeviceCore/DevUpgrade.h + GUI/DeviceCore/DevUpgrade.cpp + GUI/DeviceCore/DevUpgradeCtrl.cpp ) set(SLIC3R_GUI_SOURCES ${SLIC3R_GUI_SOURCES} PARENT_SCOPE) \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevAxis.cpp b/src/slic3r/GUI/DeviceCore/DevAxis.cpp new file mode 100644 index 0000000000..201e3afb00 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevAxis.cpp @@ -0,0 +1,25 @@ +#include "DevAxis.h" +#include "DevUtil.h" + +#include "slic3r/GUI/DeviceManager.hpp" + +namespace Slic3r +{ + +void DevAxis::ParseAxis(const json& print_json) +{ + DevJsonValParser::ParseVal(print_json, "home_flag", m_home_flag); + + if (print_json.contains("fun")) { + const std::string& fun = print_json["fun"].get(); + m_is_support_mqtt_homing = DevUtil::get_flag_bits(fun, 32); + m_is_support_mqtt_axis_ctrl = DevUtil::get_flag_bits(fun, 38); + } +} + +bool DevAxis::IsArchCoreXY() const +{ + return m_owner->get_printer_arch() == PrinterArch::ARCH_CORE_XY; +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevAxis.h b/src/slic3r/GUI/DeviceCore/DevAxis.h new file mode 100644 index 0000000000..a109ff8267 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevAxis.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include "slic3r/Utils/json_diff.hpp" + +namespace Slic3r { + +class MachineObject; + +class DevAxis +{ +public: + static std::shared_ptr Create(MachineObject *obj) { return std::shared_ptr( new DevAxis(obj)); } + virtual ~DevAxis() = default; + +public: + bool IsAxisAtHomeX() const { return m_home_flag == 0 ? true : (m_home_flag & 1) == 1; } + bool IsAxisAtHomeY() const { return m_home_flag == 0 ? true : ((m_home_flag >> 1) & 1) == 1; } + bool IsAxisAtHomeZ() const { return m_home_flag == 0 ? true : ((m_home_flag >> 2) & 1) == 1; } + + bool IsArchCoreXY() const; + +public: + void ParseAxis(const json &print_json); + + int Ctrl_GoHome(); + int Ctrl_Axis(std::string axis, double unit = 1.0f, double input_val = 1.0f, int speed = 3000); // xyz e + +protected: + DevAxis(MachineObject *obj) noexcept : m_owner(obj) {} + +private: + MachineObject *m_owner = nullptr; + + int m_home_flag = 0; // bit0:X, bit1:Y, bit2:Z + bool m_is_support_mqtt_axis_ctrl = false; + bool m_is_support_mqtt_homing = false; +}; + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevAxisCtrl.cpp b/src/slic3r/GUI/DeviceCore/DevAxisCtrl.cpp new file mode 100644 index 0000000000..6a975b3eb5 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevAxisCtrl.cpp @@ -0,0 +1,60 @@ +#include "DevAxis.h" + +#include "slic3r/GUI/DeviceManager.hpp" +#include "slic3r/Utils/NetworkAgent.hpp" + +namespace Slic3r +{ + +int DevAxis::Ctrl_GoHome() +{ + if (m_is_support_mqtt_homing) { + json j; + j["print"]["command"] = "back_to_center"; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + return m_owner->publish_json(j); + } + + // gcode command + return m_owner->is_in_printing() ? m_owner->publish_gcode("G28 X\n") : m_owner->publish_gcode("G28 \n"); +} + +int DevAxis::Ctrl_Axis(std::string axis, double unit, double input_val, int speed) +{ + if (m_is_support_mqtt_axis_ctrl) { + int dir = input_val > 0 ? 1 : -1; + if (!IsArchCoreXY()) { + if (axis.compare("Y") == 0 || axis.compare("Z") == 0) { + dir = -dir; + } + } + json j; + j["print"]["command"] = "xyz_ctrl"; + j["print"]["axis"] = axis; + j["print"]["dir"] = dir; + j["print"]["mode"] = (std::abs(input_val) >= 10) ? 1 : 0; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + return m_owner->publish_json(j); + } + + double value = input_val; + if (!IsArchCoreXY()) { + if (axis.compare("Y") == 0 || axis.compare("Z") == 0) { + value = -1.0 * input_val; + } + } + + char cmd[256]; + if (axis.compare("X") == 0 || axis.compare("Y") == 0 || axis.compare("Z") == 0) { + sprintf(cmd, "M211 S \nM211 X1 Y1 Z1\nM1002 push_ref_mode\nG91 \nG1 %s%0.1f F%d\nM1002 pop_ref_mode\nM211 R\n", axis.c_str(), value * unit, speed); + } else if (axis.compare("E") == 0) { + sprintf(cmd, "M83 \nG0 %s%0.1f F%d\n", axis.c_str(), value * unit, speed); + } else { + return -1; + } + + // Orca: strip analytics telemetry (matches MachineObject::command_axis_control) + return m_owner->publish_gcode(cmd); +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevCalib.cpp b/src/slic3r/GUI/DeviceCore/DevCalib.cpp new file mode 100644 index 0000000000..fdbcdc9728 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevCalib.cpp @@ -0,0 +1,327 @@ +#include +#include "slic3r/GUI/GUI_App.hpp" + +#include "slic3r/GUI/UserNotification.hpp" +#include "libslic3r/PrintConfig.hpp" + +#include +#include "fast_float/fast_float.h" + +#include "DevCalib.h" +#include "DevDefs.h" +#include "DevFilaSystem.h" +#include "DevConfig.h" + +namespace Slic3r { + +static float string_to_float(const std::string &str_value) +{ + float value = 0.0; + fast_float::from_chars(str_value.c_str(), str_value.c_str() + str_value.size(), value); + return value; +} + +static NozzleVolumeType convert_to_nozzle_type(const std::string &str) +{ + if (str.size() < 8) { + assert(false && "invalid nozzle info"); + return NozzleVolumeType::nvtStandard; + } + + if (str[1] == 'S') + return NozzleVolumeType::nvtStandard; + else if (str[1] == 'H') + return NozzleVolumeType::nvtHighFlow; + else if (str[1] == 'U') + return NozzleVolumeType::nvtTPUHighFlow; + // Orca: no nvtE3DHighFlow in Orca's NozzleVolumeType; map 'B' to Standard + else + return NozzleVolumeType::nvtStandard; +} + +static float get_number_flexible(const json& j, const std::string& key, float def = 0.0f) +{ + if (!j.contains(key)) return def; + + const auto& v = j[key]; + + if (v.is_number_float()) return v.get(); + if (v.is_number_integer()) return static_cast(v.get()); + if (v.is_string()) return string_to_float(v.get()); + + return def; +} + +void from_json(const json& j, PACalibResult& cali) { + cali.extruder_id = j.value("extruder_id", 0); + cali.nozzle_volume_type = convert_to_nozzle_type(j.value("nozzle_id", "HS00-0.4")); + cali.tray_id = j.value("tray_id",0); + cali.ams_id = j.value("ams_id",0); + cali.slot_id = j.value("slot_id",0); + cali.cali_idx = j.value("cali_idx",-1); + cali.nozzle_pos_id = j.value("nozzle_pos",-1); + cali.nozzle_diameter = get_number_flexible(j, "nozzle_diameter", 0.4f); + cali.nozzle_sn = j.value("nozzle_sn",""); + cali.filament_id = j.value("filament_id",""); + cali.setting_id = j.value("setting_id",""); + cali.name = j.value("name",""); + cali.k_value = get_number_flexible(j, "k_value", 0.0f); + cali.n_coef = get_number_flexible(j, "n_coef", 0.0f); + cali.confidence = j.value("confidence", 0); +} + +void from_json(const json& j, FlowRatioCalibResult& cali) +{ + cali.tray_id = j.value("tray_id", 0); + cali.nozzle_diameter = string_to_float(j.value("nozzle_diameter", "")); + cali.filament_id = j.value("filament_id", ""); + cali.setting_id = j.value("setting_id", ""); + cali.flow_ratio = string_to_float(j.value("flow_ratio", "")); + cali.confidence = j.value("confidence", 0); +} + +void DevCalib::ParseCalibVersion(const json& j, DevCalib* system) +{ + if(system) system->m_calib_version = j.value("cali_version", -1); +} + +bool DevCalib::IsVersionExpired() const +{ + if (m_last_calib_version.has_value()) + return m_last_calib_version.value() != m_calib_version; + else + return true; +} + +void DevCalib::ParseSupportNewAutoCalib(int flag, DevCalib* system) +{ + if(system) system->m_support_new_auto_cali = flag; +} + +void DevCalib::RequestPAResult() +{ + m_pa_results_status = CalibStatus::REQUEST; +} + +void DevCalib::ResetPAResult() +{ + m_pa_calib_results.clear(); + m_pa_results_status = CalibStatus::IDLE; +} + +int DevCalib::RequestPAHistory(const PACalibExtruderInfo &calib_info) +{ + m_pa_calib_tab.clear(); + m_pa_table_status = CalibStatus::REQUEST; + + return GetOwner()->command_get_pa_calibration_tab(calib_info); +} + +void DevCalib::ResetPAHistory() +{ + m_pa_calib_tab.clear(); + m_pa_table_status = CalibStatus::IDLE; +} + +void DevCalib::RequestFlowRateResult() +{ + m_flow_results_status = CalibStatus::REQUEST; +} + +void DevCalib::ResetFlowRateResult() +{ + m_flow_ratio_results.clear(); + m_flow_results_status = CalibStatus::IDLE; +} + +void calib_fail_message(MachineObject* obj, std::string cali_mode, std::string reason){ + wxString info; + if (reason == "invalid nozzle_diameter" || reason == "nozzle_diameter is not supported") { + info = _L("This calibration does not support the currently selected nozzle diameter"); + } else if (reason == "invalid handle_flowrate_cali param") { + info = _L("Current flowrate cali param is invalid"); + } else if (reason == "nozzle_diameter is not matched") { + info = _L("Selected diameter and machine diameter do not match"); + } else if (reason == "generate auto filament cali gcode failure") { + info = _L("Failed to generate cali gcode"); + } else { + info = wxString(reason); + } + + GUI::wxGetApp().push_notification(obj, info, _L("Calibration error"), UserNotificationStyle::UNS_WARNING_CONFIRM); + BOOST_LOG_TRIVIAL(info) << cali_mode << " result fail, reason = " << reason; +} + +void DevCalib::ExtrusionCalibSetParse(const json & jj){ + int tray_id = jj.value("tray_id", -1); + + auto tray_ams_slot_map = GetOwner()->GetFilaSystem()->GetTrayIndexMap(); + int ams_id = tray_ams_slot_map.find(tray_id) != tray_ams_slot_map.end() ? tray_ams_slot_map[tray_id].first : -1; + int slot_id = tray_ams_slot_map.find(tray_id) != tray_ams_slot_map.end() ? tray_ams_slot_map[tray_id].second : -1; + + if(tray_id == VIRTUAL_TRAY_MAIN_ID) { + GetOwner()->vt_slot[MAIN_EXTRUDER_ID].k = jj.value("k_value", GetOwner()->vt_slot[MAIN_EXTRUDER_ID].k); + GetOwner()->vt_slot[MAIN_EXTRUDER_ID].n = jj.value("n_value", GetOwner()->vt_slot[MAIN_EXTRUDER_ID].n); + }else{ + auto tray_item = GetOwner()->GetFilaSystem()->GetAmsTray(std::to_string(ams_id), std::to_string(slot_id)); + if (tray_item) { + tray_item->k = jj.value("k_value", tray_item->k); + tray_item->n = jj.value("n_coef", tray_item->n); + } + } + + GetOwner()->extrusion_cali_set_tray_id = tray_id; + GetOwner()->extrusion_cali_set_hold_start = std::chrono::system_clock::now(); +} + +/* calib select ack parse */ +void DevCalib::ExtrusionCalibSelectParse(const json &jj){ + try{ + int tray_id = jj.value("tray_id", -1); + + auto tray_ams_slot_map = GetOwner()->GetFilaSystem()->GetTrayIndexMap(); + int default_ams_id = tray_ams_slot_map.find(tray_id) != tray_ams_slot_map.end() ? tray_ams_slot_map[tray_id].first : -1; + int default_slot_id = tray_ams_slot_map.find(tray_id) != tray_ams_slot_map.end() ? tray_ams_slot_map[tray_id].second : -1; + + int ams_id = jj.value("ams_id", default_ams_id); + int slot_id = jj.value("slot_id", default_slot_id); + + BOOST_LOG_TRIVIAL(trace) << "extrusion_cali_sel: illegal ams_id = " << ams_id << "slot_id = " << slot_id; + + std::vector &vt_slot = GetOwner()->vt_slot; + if (ams_id == VIRTUAL_TRAY_MAIN_ID && vt_slot.size() > 0) { + vt_slot[MAIN_EXTRUDER_ID].cali_idx = jj.value("cali_idx", vt_slot[MAIN_EXTRUDER_ID].cali_idx); + vt_slot[MAIN_EXTRUDER_ID].set_hold_count(); + } else if (ams_id == VIRTUAL_TRAY_DEPUTY_ID && vt_slot.size() > 1) { + vt_slot[DEPUTY_EXTRUDER_ID].cali_idx = jj.value("cali_idx", vt_slot[DEPUTY_EXTRUDER_ID].cali_idx); + vt_slot[DEPUTY_EXTRUDER_ID].set_hold_count(); + } else { + auto tray_item = GetOwner()->GetFilaSystem()->GetAmsTray(std::to_string(ams_id), std::to_string(slot_id)); + if (tray_item) { + tray_item->cali_idx = jj.value("cali_idx", tray_item->cali_idx); + tray_item->set_hold_count(); + } + } + } catch(...){ + + } +} + +void DevCalib::ExtrusionCalibGetTableParse(const json &jj){ + if (GetPAHistoryStatus() == CalibStatus::REQUEST ) { + m_pa_table_status = CalibStatus::WAITING; + + /* request success */ + if (!(jj.contains("result") && jj.contains("reason") && jj["result"].get() == "fail")) { + SyncCalibVersion(); + m_pa_table_status = CalibStatus::FINISHED; + } + + try{ + json filaments_json; + if(jj.contains("filaments")) + { + /* fill item->nozzle_diameter with command->nozzle_diameter */ + filaments_json = jj["filaments"]; + for (auto &f : filaments_json) { + if (!f.contains("nozzle_diameter") && jj.contains("nozzle_diameter")) { + f["nozzle_diameter"] = jj["nozzle_diameter"]; + } + } + } + + m_pa_calib_tab = filaments_json.get>(); + + /* filter invalid pa_calib_tab */ + m_pa_calib_tab.erase(std::remove_if(m_pa_calib_tab.begin(), m_pa_calib_tab.end(), [](auto &res) { return res.k_value < 0.0f || res.k_value > 10.0f; }), m_pa_calib_tab.end()); + + if (m_pa_calib_tab.empty()) { BOOST_LOG_TRIVIAL(info) << "empty pa calib history"; } + }catch(...){ + m_pa_calib_tab.clear(); + BOOST_LOG_TRIVIAL(error) << "pa calib history missing fields, current json:\n "<< jj.dump(); + } + // notify cali history to update + } +} + +void DevCalib::ExtrusionCalibGetResultParse(const json &jj) +{ + m_pa_results_status = CalibStatus::WAITING; + + if (!(jj.contains("result") && jj.contains("reason") && jj["result"].get() == "fail" && jj.contains("err_code"))) { + m_pa_results_status = CalibStatus::FINISHED; + } + + try { + json filaments_json; + if(jj.contains("filaments")) + { + /* fill item->nozzle_diameter with command->nozzle_diameter */ + filaments_json = jj["filaments"]; + for (auto &f : filaments_json) { + if (!f.contains("nozzle_diameter") && jj.contains("nozzle_diameter") ) { + f["nozzle_diameter"] = jj["nozzle_diameter"]; + } + + if (IsSupportNewAutoCali()) { + auto ams_id = f.value("ams_id", 0); + auto slot_id = f.value("slot_id", 0); + if(f.contains("tray_id")){ + // Orca: no GetTrayIdByAmsSlotId in Orca DevFilaSystem; standard AMS tray formula + f["tray_id"] = ams_id * 4 + slot_id; + } + } + } + } + + m_pa_calib_results = filaments_json.get>(); + + m_pa_calib_results.erase(std::remove_if(m_pa_calib_results.begin(), m_pa_calib_results.end(), [](auto &res) { return res.k_value < 0.0f || res.k_value > 10.0f; }), m_pa_calib_results.end()); + + if (m_pa_calib_results.empty()) { BOOST_LOG_TRIVIAL(info) << "empty pa calib result"; } + } catch (...) { + m_pa_calib_results.clear(); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "pa calibration results missing fileds, current json: \n"<>(); + } catch (...) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "flow ratio calibration results missing fileds, current json:\n"<() == "extrusion_cali" || jj["command"].get() == "flowrate_cali") { + if (jj.contains("result")) { + if (jj["result"].get() == "success") { + } else if (jj["result"].get() == "fail") { + std::string cali_mode = jj["command"].get(); + std::string reason = jj["reason"].get(); + calib_fail_message(system->GetOwner(), cali_mode, reason); + } + } + } else if (jj["command"].get() == "extrusion_cali_set") { + system->ExtrusionCalibSetParse(jj); + } else if (jj["command"].get() == "extrusion_cali_sel") { + system->ExtrusionCalibSelectParse(jj); + } else if (jj["command"].get() == "extrusion_cali_get") { + system->ExtrusionCalibGetTableParse(jj); + } else if (jj["command"].get() == "extrusion_cali_get_result") { + system->ExtrusionCalibGetResultParse(jj); + } else if (jj["command"].get() == "flowrate_get_result" && !key_field_only) { + system->FlowrateGetResultParse(jj); + } +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevCalib.h b/src/slic3r/GUI/DeviceCore/DevCalib.h new file mode 100644 index 0000000000..d5894c1c9d --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevCalib.h @@ -0,0 +1,127 @@ +#pragma once +#include +#include "slic3r/Utils/json_diff.hpp" + + +#include "DevDefs.h" +#include "libslic3r/calib.hpp" // Orca: lowercase filename (Linux case-sensitive) + + +namespace Slic3r { + +class MachineObject; + + +enum class CalibStatus{ + IDLE = 0, + REQUEST, + WAITING, + FINISHED, +}; + +enum class ManualPaCaliMethod { + PA_LINE = 0, + PA_PATTERN, +}; + +class DevCalib +{ +protected: + bool m_support_new_auto_cali{false}; + int m_calib_version {-1}; + std::optional m_last_calib_version; + +public: + DevCalib(MachineObject *obj) : m_owner(obj){}; + MachineObject* GetOwner() const {return m_owner; }; + + void RequestPAResult(); + CalibStatus GetPAResultStatus() const {return m_pa_results_status;} + bool IsPAResultReady() const { return m_pa_results_status == CalibStatus::FINISHED;} + void ResetPAResult(); + + /* calib history */ + int RequestPAHistory(const PACalibExtruderInfo &calib_info); + CalibStatus GetPAHistoryStatus() const {return m_pa_table_status;} + bool IsPAHistoryReady() const { return m_pa_table_status == CalibStatus::FINISHED;} + void ResetPAHistory(); + + void RequestFlowRateResult(); + CalibStatus GetFlowRateResultStatus() const {return m_flow_results_status;} + bool IsFlowRateReady() const { return m_flow_results_status == CalibStatus::FINISHED;} + void ResetFlowRateResult(); + + int GetCalibVersion() const {return m_calib_version;} + void SyncCalibVersion() { if (IsVersionInited()) { m_last_calib_version = m_calib_version; } } + void ResetCalibVersion() {m_last_calib_version.reset();} + bool IsVersionExpired() const; + bool IsVersionInited() const { return m_calib_version > -1;} + + bool IsSupportNewAutoCali() const {return m_support_new_auto_cali;} + +public: + void SetStashCalibFinished(bool finished) {m_calib_finished = finished;} + bool GetStashCalibFinished() { return m_calib_finished;} + + void SetStashFlowRatio(float ratio) { m_flow_ratio = ratio;} + float GetStashFlowRatio() const {return m_flow_ratio;} + + FlowRatioCalibrationType GetFlowRatioCalibType() {return m_flow_ratio_calibration_type;} + void SetFlowRatioCalibType(const FlowRatioCalibrationType &type) { m_flow_ratio_calibration_type = type; } + + ManualPaCaliMethod GetManualPaCalibMethod() {return m_manual_pa_cali_method;} + void SetManualPaCalibMethod(const ManualPaCaliMethod& method) { m_manual_pa_cali_method = method;} + + NozzleDiameterType GetSelectedNozzleDiameter() {return m_selected_nozzle_diameter;} + void SetSelectedNozzleDiameter(const NozzleDiameterType& diameter) { m_selected_nozzle_diameter = diameter;} + + std::vector GetSelectedCalibPreset() {return m_selected_calib_preset;} + void ResetSelectedCalibPreset() { m_selected_calib_preset.clear();} + void SetSelectedCalibPreset(const std::vector& preset) { m_selected_calib_preset = preset;} + + std::vector GetPAHistory() const {return m_pa_calib_tab; } + std::vector GetPAResult() const {return m_pa_calib_results; } + std::vector GetFlowRatioResult() const {return m_flow_ratio_results; } + +protected: + void ExtrusionCalibSetParse(const json &jj); + void ExtrusionCalibSelectParse(const json &jj); + void ExtrusionCalibGetTableParse(const json &jj); + void ExtrusionCalibGetResultParse(const json &jj); + void FlowrateGetResultParse(const json &jj); + +private: + MachineObject* m_owner{nullptr}; + + std::vector m_pa_calib_tab; + std::vector m_pa_calib_results; + std::vector m_flow_ratio_results; + + CalibStatus m_pa_results_status{CalibStatus::IDLE}; + CalibStatus m_pa_table_status{CalibStatus::IDLE}; + CalibStatus m_flow_results_status{CalibStatus::IDLE}; + + FlowRatioCalibrationType m_flow_ratio_calibration_type{FlowRatioCalibrationType::COMPLETE_CALIBRATION}; + ManualPaCaliMethod m_manual_pa_cali_method{ManualPaCaliMethod::PA_LINE}; + + // calibration page selected info + // 1: record when start calibration in preset page + // 2: reset when start calibration in start page + // 3: save tray_id, filament_id, setting_id, and name, nozzle_dia + // std::vector selected_cali_preset; + NozzleDiameterType m_selected_nozzle_diameter{NozzleDiameterType::NONE_DIAMETER_TYPE}; + std::vector m_selected_calib_preset; + + // stash calibrating info + float m_flow_ratio { 0.0 }; + bool m_calib_finished{false}; + +public: + static void ParseCalibVersion(const json& j, DevCalib* system); + + static void ParseSupportNewAutoCalib(int flag, DevCalib* system); + + static void ParseV1_0(const json& print_json, DevCalib* system, bool key_field_only); +}; + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevChamber.cpp b/src/slic3r/GUI/DeviceCore/DevChamber.cpp new file mode 100644 index 0000000000..a0edef51c5 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevChamber.cpp @@ -0,0 +1,50 @@ +#include "DevChamber.h" + +#include "DevConfig.h" +#include "DevUtil.h" + +#include "slic3r/GUI/DeviceManager.hpp" + +namespace Slic3r { + +bool DevChamber::HasChamber() const { return m_owner->GetConfig()->HasChamber(); } + +bool DevChamber::SupportChamberTempDisplay() const { return m_owner->GetConfig()->SupportChamberTempDisplay();} + +bool DevChamber::SupportChamberEdit() const { return m_owner->GetConfig()->SupportChamberEdit(); } + +int DevChamber::GetChamberTempEditMin() const { return m_owner->GetConfig()->GetChamberTempEditMin(); } + +int DevChamber::GetChamberTempEditMax() const { return m_owner->GetConfig()->GetChamberTempEditMax(); } + +int DevChamber::GetChamberTempSwitchHeat() const { return m_owner->GetConfig()->GetChamberTempSwitchHeat(); } + +void DevChamber::ParseChamber(const json &print_json) +{ + ParseChamberV1_0(print_json); + ParseChamberV2_0(print_json); +} + +void DevChamber::ParseChamberV1_0(const json &print_json) +{ + DevJsonValParser::ParseVal(print_json, "chamber_temper", m_temp); + DevJsonValParser::ParseVal(print_json, "ctt", m_temp_target); +} + +void DevChamber::ParseChamberV2_0(const json &print_json) +{ + if (print_json.contains("device")) { + const json &device_jj = print_json["device"]; + if (device_jj.contains("ctc")) { + const json &ctc = device_jj["ctc"]; + int state = DevUtil::get_flag_bits(ctc["state"].get(), 0, 4); + if (ctc.contains("info")) { + const json &info = ctc["info"]; + m_temp = DevUtil::get_flag_bits(info["temp"].get(), 0, 16); + m_temp_target = DevUtil::get_flag_bits(info["temp"].get(), 16, 16); + } + } + } +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevChamber.h b/src/slic3r/GUI/DeviceCore/DevChamber.h new file mode 100644 index 0000000000..29acacff9c --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevChamber.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include "slic3r/Utils/json_diff.hpp" + +namespace Slic3r { + +class MachineObject; + +class DevChamber +{ +public: + static std::shared_ptr Create(MachineObject *obj) { return std::shared_ptr( new DevChamber(obj)); } + +public: // getter + bool HasChamber() const; + bool SupportChamberTempDisplay() const; + bool SupportChamberEdit() const; + int GetChamberTempEditMin() const; + int GetChamberTempEditMax() const; + int GetChamberTempSwitchHeat() const; + + float GetChamberTemp() const { return m_temp; }; + float GetChamberTempTarget() const { return m_temp_target; }; + +public: + // setter + void ParseChamber(const json &print_json); + + void ParseChamberV1_0(const json& print_json); + void ParseChamberV2_0(const json& print_json); + + // control + int CtrlSetChamberTemp(int temp); + +protected: + DevChamber(MachineObject *obj) : m_owner(obj) {} + +private: + float m_temp = 0.0f; + float m_temp_target = 0.0f; + MachineObject* m_owner = nullptr; +}; + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevChamberCtrl.cpp b/src/slic3r/GUI/DeviceCore/DevChamberCtrl.cpp new file mode 100644 index 0000000000..651806fd5f --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevChamberCtrl.cpp @@ -0,0 +1,16 @@ +#include "DevChamber.h" +#include "slic3r/GUI/DeviceManager.hpp" + +namespace Slic3r { + +int DevChamber::CtrlSetChamberTemp(int temp) +{ + json j; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["print"]["command"] = "set_ctt"; + j["print"]["ctt_val"] = temp; + + return m_owner->publish_json(j, 1); +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevStatus.cpp b/src/slic3r/GUI/DeviceCore/DevStatus.cpp new file mode 100644 index 0000000000..26d2bc4ceb --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevStatus.cpp @@ -0,0 +1,33 @@ +#include "DevStatus.h" +#include "slic3r/GUI/DeviceManager.hpp" +#include + +namespace Slic3r { + +void DevStatus::ParseStatus(const nlohmann::json& print_jj) +{ + try { + if (print_jj.contains("job")) { + const nlohmann::json& job_jj = print_jj.at("job"); + if (job_jj.contains("job_state")) { + auto new_state = (DevJobState)job_jj.at("job_state").get(); + if (m_job_state != new_state) { + m_job_state = new_state; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": job_state-> " << (int)new_state; + } + + if (m_job_state == DevJobState::JobStateFinishing) { + m_job_state = DevJobState::JobStateFinish; + } + } + } + } catch (const std::exception& e) { +#if BBL_RELEASE_TO_PUBLIC + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": get exception"; +#else + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": get exception=" << e.what(); +#endif + } +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevStatus.h b/src/slic3r/GUI/DeviceCore/DevStatus.h new file mode 100644 index 0000000000..45f76d2239 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevStatus.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include +#include "slic3r/Utils/json_diff.hpp" + +#include "DevDefs.h" + +namespace Slic3r { + +enum DevJobState +{ + JobStateIdle = 0, + JobStateSlicing = 1, + JobStatePrepare = 2, + JobStateStarting = 3, + JobStateRunning = 4, + JobStatePause = 5, + JobStatePausing = 6, + JobStateResuming = 7, + JobStateFinish = 8, + JobStateFailed = 9, + JobStateFinishing = 10, + JobStateStoppping = 11, +}; + +class MachineObject; +class DevStatus +{ +public: + DevStatus(MachineObject* owner) : m_owner(owner) {}; + +public: + std::optional GetJobState() const { return m_job_state; } + + // Parse + void ParseStatus(const nlohmann::json& print_jj); + +private: + MachineObject *m_owner = nullptr; + std::optional m_job_state; // could be nullopt for some old firmware +}; + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevUpgrade.cpp b/src/slic3r/GUI/DeviceCore/DevUpgrade.cpp new file mode 100644 index 0000000000..fc9ba0061d --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevUpgrade.cpp @@ -0,0 +1,130 @@ +#include "DevUpgrade.h" +#include "DevUtil.h" + +#include "slic3r/GUI/DeviceManager.hpp" + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/I18N.hpp" + +namespace Slic3r { + +int DevUpgrade::GetUpgradeProgressInt() const +{ + try { + return std::stoi(m_upgrade_progress); + } catch (const std::exception &e) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << e.what(); + } + + return 0; +} + +void DevUpgrade::ParseUpgrade_V1_0(const json &print_jj) +{ + if (print_jj.contains("upgrade_state")) { + const auto &upgrade_jj = print_jj["upgrade_state"]; + + // status and steps + DevJsonValParser::ParseVal(upgrade_jj, "status", m_upgrade_status); + DevJsonValParser::ParseVal(upgrade_jj, "progress", m_upgrade_progress); + DevJsonValParser::ParseVal(upgrade_jj, "message", m_upgrade_message); + DevJsonValParser::ParseVal(upgrade_jj, "module", m_upgrade_module); + DevJsonValParser::ParseVal(upgrade_jj, "err_code", m_upgrade_err_code); + + // version requested + DevJsonValParser::ParseVal(upgrade_jj, "new_version_state", m_upgrade_new_version_state); + DevJsonValParser::ParseVal(upgrade_jj, "consistency_request", m_upgrade_consistency_request); + DevJsonValParser::ParseVal(upgrade_jj, "force_upgrade", m_upgrade_force_upgrade); + + // version info + DevJsonValParser::ParseVal(upgrade_jj, "ota_new_version_number", m_ota_new_version_number); + + // version list + if (upgrade_jj.contains("new_ver_list")) { + m_new_ver_list.clear(); + for (auto ver_item = upgrade_jj["new_ver_list"].begin(); ver_item != upgrade_jj["new_ver_list"].end(); ver_item++) { + DevFirmwareVersionInfo ver_info; + DevJsonValParser::ParseVal(*ver_item, "name", ver_info.name); + DevJsonValParser::ParseVal(*ver_item, "cur_ver", ver_info.sw_ver); + DevJsonValParser::ParseVal(*ver_item, "new_ver", ver_info.sw_new_ver); + m_new_ver_list[ver_info.name] = ver_info; + if (ver_info.name == "ota" && m_ota_new_version_number.empty()) { m_ota_new_version_number = ver_info.sw_new_ver; } + } + } else { + m_new_ver_list.clear(); + } + + // try to parse display state + ParseUpgradeDisplayState(upgrade_jj); + } + + if (print_jj.contains("cfg")) { + const std::string &cfg = print_jj["cfg"].get(); + if (!cfg.empty()) { m_upgrade_force_upgrade = DevUtil::get_flag_bits(cfg, 2); } + } +} + +void DevUpgrade::ParseUpgradeDisplayState(const json &upgrade_state_jj) +{ + if (upgrade_state_jj.contains("dis_state")) { + if ((int) m_upgrade_display_state != upgrade_state_jj["dis_state"].get()) { + DevJsonValParser::ParseVal(upgrade_state_jj, "dis_state", m_upgrade_display_state); + + // update the version after upgrade finished + if (m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingFinished) { + Slic3r::GUI::wxGetApp().CallAfter([this] { + m_owner->command_get_version(); + }); + } + + // lan mode printer, hide upgrade avaliable state + if (m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingAvaliable && + m_owner->is_lan_mode_printer()) { + m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingUnavaliable; + } + } + } else { + // BBS compatibility with old version + if (m_upgrade_status == "DOWNLOADING" || + m_upgrade_status == "FLASHING" || + m_upgrade_status == "UPGRADE_REQUEST" || + m_upgrade_status == "PRE_FLASH_START" || + m_upgrade_status == "PRE_FLASH_SUCCESS") { + m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingInProgress; + } else if (m_upgrade_status == "UPGRADE_SUCCESS" || + m_upgrade_status == "DOWNLOAD_FAIL" || + m_upgrade_status == "FLASH_FAIL" || + m_upgrade_status == "PRE_FLASH_FAIL" || + m_upgrade_status == "UPGRADE_FAIL") { + m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingFinished; + } else { + if (HasNewVersion()) { + m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingAvaliable; + } else { + m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingUnavaliable; + } + } + } +} + +#define UpgradeNoError 0 +#define UpgradeDownloadFailed -1 +#define UpgradeVerfifyFailed -2 +#define UpgradeFlashFailed -3 +#define UpgradePrinting -4 + +wxString DevUpgrade::GetUpgradeErrCodeStr() const +{ + switch (m_upgrade_err_code) { + case UpgradeNoError: return _L("Update successful."); + case UpgradeDownloadFailed: return _L("Downloading failed."); + case UpgradeVerfifyFailed: return _L("Verification failed."); + case UpgradeFlashFailed: return _L("Update failed."); + case UpgradePrinting: return _L("Update failed."); + default: return _L("Update failed."); + } + + return ""; +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevUpgrade.h b/src/slic3r/GUI/DeviceCore/DevUpgrade.h new file mode 100644 index 0000000000..0290d5f7d4 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevUpgrade.h @@ -0,0 +1,108 @@ +#pragma once +#include +#include "slic3r/Utils/json_diff.hpp" + +#include "DevDefs.h" +#include "DevFirmware.h" + +namespace Slic3r { + +class MachineObject; +class DevUpgrade +{ +public: + static std::shared_ptr Create(MachineObject *owner) { return std::shared_ptr(new DevUpgrade(owner)); } + +public: + // upgrade status + bool IsUpgrading() const { return m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingInProgress; } + bool IsUpgradeAvaliable() const { return m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingAvaliable; } + DevFirmwareUpgradeState GetUpgradeState() const { return m_upgrade_display_state; } + std::string GetUpgradeStatusStr() const { return m_upgrade_status; } + + // upgrade request + bool IsUpgradeConsistencyRequest() const { return m_upgrade_consistency_request; } + bool IsUpgradeForceUpgrade() const { return m_upgrade_force_upgrade; } + + bool HasNewVersion() const { return m_upgrade_new_version_state == 1; } + std::string GetOtaNewVersion() const { return m_ota_new_version_number; } + std::map GetNewVersionList() const { return m_new_ver_list; } + + // upgrading + std::string GetUpgradeModuleStr() const { return m_upgrade_module; } + std::string GetUpgradeProgressStr() const { return m_upgrade_progress; } + int GetUpgradeProgressInt() const; + std::string GetUpgradeMessageStr() const { return m_upgrade_message; } + wxString GetUpgradeErrCodeStr() const; + +public: + // ctrls + int CtrlUpgradeConfirm(); + int CtrlUpgradeConsistencyConfirm(); + int CtrlUpgradeFirmware(FirmwareInfo info); + int CtrlUpgradeModule(std::string url, std::string module_type, std::string version); + + // parser + void ParseUpgrade_V1_0(const json &print_jj); + void ParseUpgradeDisplayState(const json &upgrade_state_jj); + +protected: + DevUpgrade(MachineObject *owner) : m_owner(owner) {} + +private: + MachineObject *m_owner = nullptr; + + int m_upgrade_new_version_state = 0; // 0: invalid version, 1: new version available, 2: no new version + bool m_upgrade_consistency_request = false; + bool m_upgrade_force_upgrade = false; + + std::string m_ota_new_version_number; + std::map m_new_ver_list; // some protcols have version list + DevFirmwareUpgradeState m_upgrade_display_state = DevFirmwareUpgradeState::DC; + + int m_upgrade_err_code; + std::string m_upgrade_status; + std::string m_upgrade_progress; + std::string m_upgrade_message; + std::string m_upgrade_module; +}; + +#if 0 /*TODO*/ +class DevUpgradeGetVersionInfo +{ +public: + DevUpgradeGetVersionInfo(MachineObject* obj) : m_owner(obj) {} ; + +public: + bool IsEmpty() const { m_module_version_map.empty();} + + DevFirmwareVersionInfo GetAirPumpVersionInfo() const { return m_air_pump_version_info; } + DevFirmwareVersionInfo GetLaserVersionInfo() const { return m_laser_version_info; } + DevFirmwareVersionInfo GetCuttingModuleVersionInfo() const { return m_cutting_module_version_info; } + DevFirmwareVersionInfo GetExtinguishVersionInfo() const { return m_extinguish_version_info; } + DevFirmwareVersionInfo GetRotaryVersionInfo() const { return m_rotary_version_info; } + std::map GetModuleVersionInfoMap() const { return m_module_version_map; } + +public: + int CtrlGetVersion(bool with_retry = true); + + void ParseGetVersion(const json &print_jj); + +private: + MachineObject *m_owner; + + // some version info + DevFirmwareVersionInfo m_air_pump_version_info; + DevFirmwareVersionInfo m_laser_version_info; + DevFirmwareVersionInfo m_cutting_module_version_info; + DevFirmwareVersionInfo m_extinguish_version_info; + DevFirmwareVersionInfo m_rotary_version_info; + std::map m_module_version_map; + + // ctrl retry + int m_retry_count = 0; + int m_max_retry = 3; +}; +#endif + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevUpgradeCtrl.cpp b/src/slic3r/GUI/DeviceCore/DevUpgradeCtrl.cpp new file mode 100644 index 0000000000..7fa2d2b5cf --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevUpgradeCtrl.cpp @@ -0,0 +1,56 @@ +#include "DevUpgrade.h" +#include "DevUtil.h" + +#include "slic3r/GUI/DeviceManager.hpp" + +namespace Slic3r { + +int DevUpgrade::CtrlUpgradeConfirm() +{ + json j; + j["upgrade"]["command"] = "upgrade_confirm"; + j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["upgrade"]["src_id"] = 1; // 1 for slicer + return m_owner->publish_json(j); +} + +int DevUpgrade::CtrlUpgradeConsistencyConfirm() +{ + json j; + j["upgrade"]["command"] = "consistency_confirm"; + j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["upgrade"]["src_id"] = 1; // 1 for slicer + return m_owner->publish_json(j); +} + +int DevUpgrade::CtrlUpgradeFirmware(FirmwareInfo info) +{ + std::string version = info.version; + std::string dst_url = info.url; + std::string module_name = info.module_type; + + json j; + j["upgrade"]["command"] = "start"; + j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["upgrade"]["url"] = info.url; + j["upgrade"]["module"] = info.module_type; + j["upgrade"]["version"] = info.version; + j["upgrade"]["src_id"] = 1; + + return m_owner->publish_json(j); +} + +int DevUpgrade::CtrlUpgradeModule(std::string url, std::string module_type, std::string version) +{ + json j; + j["upgrade"]["command"] = "start"; + j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["upgrade"]["url"] = url; + j["upgrade"]["module"] = module_type; + j["upgrade"]["version"] = version; + j["upgrade"]["src_id"] = 1; + + return m_owner->publish_json(j); +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 4651269306..2942b9cf27 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -44,6 +44,12 @@ #include "DeviceCore/DevManager.h" #include "DeviceCore/DevUtil.h" +// Orca: adopt DeviceCore split — axis/calib/chamber/status/upgrade modules +#include "DeviceCore/DevAxis.h" +#include "DeviceCore/DevChamber.h" +#include "DeviceCore/DevStatus.h" +#include "DeviceCore/DevUpgrade.h" + #define CALI_DEBUG #define MINUTE_30 1800000 //ms @@ -578,6 +584,13 @@ MachineObject::MachineObject(DeviceManager* manager, NetworkAgent* agent, std::s m_print_options = new DevPrintOptions(this); m_nozzle_mapping_ptr = std::make_shared(this); + + // Orca: adopt DeviceCore split — axis/calib/chamber/status/upgrade modules + m_axis = DevAxis::Create(this); + m_chamber = DevChamber::Create(this); + m_upgrade = DevUpgrade::Create(this); + m_status = new DevStatus(this); + m_calib = new DevCalib(this); } } @@ -630,6 +643,13 @@ MachineObject::~MachineObject() delete m_print_options; m_print_options = nullptr; + + // Orca: adopt DeviceCore split + delete m_calib; + m_calib = nullptr; + + delete m_status; + m_status = nullptr; } } @@ -3003,6 +3023,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ //supported function m_config->ParseConfig(jj); + m_status->ParseStatus(jj); // Orca: adopt DeviceCore split — populate DevStatus module if (jj.contains("support_build_plate_marker_detect")) { if (jj["support_build_plate_marker_detect"].is_boolean()) { @@ -3427,6 +3448,11 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ if (!key_field_only) { /* temperature */ + // Orca: adopt DeviceCore split — populate DevAxis/DevChamber modules + // alongside the inline handling (side-effect-free; inline stays authoritative) + m_axis->ParseAxis(jj); + m_chamber->ParseChamber(jj); + DevBed::ParseV1_0(jj,m_bed); if (jj.contains("frame_temper")) { diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index b4d5e15571..272d3e8377 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -22,6 +22,7 @@ #include "DeviceCore/DevDefs.h" #include "DeviceCore/DevConfigUtil.h" #include "DeviceCore/DevFirmware.h" +#include "DeviceCore/DevCalib.h" // Orca: adopt DeviceCore split (defines DevCalib, CalibStatus, ManualPaCaliMethod) #include "DeviceErrorDialog.hpp" #include @@ -63,11 +64,7 @@ class DeviceErrorDialog; // Previous definitions } class NetworkAgent; -enum ManualPaCaliMethod { - PA_LINE = 0, - PA_PATTERN, -}; - +// Orca: ManualPaCaliMethod now provided by DeviceCore/DevCalib.h (enum class) #define UpgradeNoError 0 #define UpgradeDownloadFailed -1 @@ -78,7 +75,9 @@ enum ManualPaCaliMethod { // Previous definitions class DevAms; class DevAmsTray; +class DevAxis; // Orca: adopt DeviceCore split class DevBed; +class DevChamber; // Orca: adopt DeviceCore split class DevConfig; class DevCtrl; class DevExtensionTool; @@ -92,7 +91,9 @@ class DevLamp; class DevNozzleSystem; class DevNozzleMappingCtrl; class DeviceManager; +class DevStatus; // Orca: adopt DeviceCore split class DevStorage; +class DevUpgrade; // Orca: adopt DeviceCore split struct DevPrintTaskRatingInfo; @@ -124,6 +125,15 @@ private: DevBed * m_bed; DevStorage* m_storage; + /* Orca: adopt DeviceCore split — axis/calib/chamber/status/upgrade modules. + Provide the reference-shape surface for later resync clusters; MachineObject's inline + handling of these concerns stays authoritative during the transition (removed in cluster 8). */ + std::shared_ptr m_axis; + std::shared_ptr m_chamber; + DevCalib* m_calib{ nullptr }; + DevStatus* m_status{ nullptr }; + std::shared_ptr m_upgrade; + /*Ctrl*/ DevCtrl* m_ctrl; @@ -146,6 +156,14 @@ public: ~MachineObject(); void set_agent(NetworkAgent* agent) { m_agent = agent; } + NetworkAgent* get_agent() const { return m_agent; } // Orca: needed by DeviceCore modules (DevAxisCtrl) + + // Orca: adopt DeviceCore split — reference-shape accessors for the new modules + std::shared_ptr GetAxis() const { return m_axis; } + std::shared_ptr GetChamber() const { return m_chamber; } + DevCalib* GetCalib() const { return m_calib; } + DevStatus* GetStatus() const { return m_status; } + std::weak_ptr GetUpgrade() const { return m_upgrade; } public: enum ActiveState {