mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 10:51:22 +00:00
feat: Filament Track Switch (H2-series O2L-FTS) support
The Filament Track Switch (H2-series accessory, product code O2L-FTS) feeds every AMS to both extruders through a two-track switch. Port full support across the device layer, project config, and GUI. Device / config: - Model the switch-aware AMS binding (the set of extruders an AMS can feed and which input track A/B feeds it), switch readiness, the O2L-FTS firmware module, and the fun2 capability bit for checking a slice against installed hardware. - Register has_filament_switcher and enable_filament_dynamic_map as project config that persists with the project and restores from a saved 3mf, and force both back to false on every project/printer/CLI load path. Live device sync is the only thing that sets them true. GUI: - Sidebar sync activates the switch from live device state, attributes each AMS to the extruder its input track feeds, shows a floating status icon (ready / not-calibrated), and surfaces a one-time tip / not-calibrated warning. - Send dialog gains a non-blocking slice-vs-hardware mismatch warning and a blocking error when a slice needs dynamic nozzle mapping but the switch is missing or not set up. - AMS load/unload guards, AMS-view routing glyph + un-calibrated banner + hidden external-spool road, and mapping-popup external-spool lockout. - Filament pickers collapse the per-extruder split into a single deduplicated "AMS filaments" group with a smart-assign toggle when the switch is ready. - Firmware-upgrade panel lists the O2L-FTS accessory and its version. - Device-provided filament-change steps (ams.cfs) drive the change-step display when firmware sends them, including the three switch steps. - "Load current filament" asks which extruder to feed via a FeedDirectionDialog when the switch is calibrated. Inert without the accessory: every path is gated on the switch being installed (MQTT aux bit 29, default off) or ready, both project flags default false, and the per-extruder AMS attribution is byte-identical, so AMS state, the send/load UI, and sliced g-code are unchanged for every printer that does not report a Filament Track Switch.
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
#include <wx/hyperlink.h>
|
||||
#include <wx/animate.h>
|
||||
#include <wx/dynarray.h>
|
||||
#include <optional>
|
||||
|
||||
|
||||
#define AMS_CONTROL_BRAND_COLOUR wxColour(0, 150, 136)
|
||||
@@ -400,6 +401,26 @@ private:
|
||||
};
|
||||
|
||||
|
||||
// Orca: routing glyph shown on the AMS control when a Filament Track Switch is installed.
|
||||
class SwitcherImage: public wxWindow
|
||||
{
|
||||
public:
|
||||
void setShowState(bool show_state) { m_show_state = show_state; };
|
||||
// void msw_rescale();
|
||||
void paintEvent(wxPaintEvent &evt);
|
||||
|
||||
void render(wxDC &dc);
|
||||
bool m_show_state = {false};
|
||||
wxColour m_colour;
|
||||
ScalableBitmap m_switcher;
|
||||
string m_file_name;
|
||||
// bool m_ams_loading{ false };
|
||||
void doRender(wxDC &dc);
|
||||
SwitcherImage(wxWindow *parent, wxWindowID id, string file_name, const wxSize& size, const wxPoint &pos = wxDefaultPosition);
|
||||
~SwitcherImage();
|
||||
};
|
||||
|
||||
|
||||
class AMSextruder : public wxWindow
|
||||
{
|
||||
private:
|
||||
@@ -771,6 +792,9 @@ public:
|
||||
void PlayRridLoading(wxString canid);
|
||||
void StopRridLoading(wxString canid);
|
||||
void msw_rescale();
|
||||
// Orca: hide/show the road segment below the item; used to drop the external-spool road
|
||||
// when a Filament Track Switch is installed. Returns true if the visibility actually changed.
|
||||
bool ShowRoad(bool show);
|
||||
void show_sn_value(bool show);
|
||||
void SetAmsStepExtra(wxString canid, AMSPassRoadType type, AMSPassRoadSTEP step);
|
||||
void SetAmsStep(std::string amsid, std::string canid, AMSPassRoadType type, AMSPassRoadSTEP step);
|
||||
@@ -849,6 +873,111 @@ wxDECLARE_EVENT(EVT_AMS_UNSELETED_AMS, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_VAMS_ON_FILAMENT_EDIT, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_AMS_SWITCH, SimpleEvent);
|
||||
|
||||
enum class DevExtruderState {
|
||||
FILLED_LOAD,
|
||||
FILLED_UNLOAD,
|
||||
EMPTY_LOAD,
|
||||
EMPTY_UNLOAD
|
||||
};
|
||||
|
||||
class DevExtruderImage : public wxWindow
|
||||
{
|
||||
ScalableBitmap *m_left_extruder_active_filled;
|
||||
ScalableBitmap *m_left_extruder_active_empty;
|
||||
ScalableBitmap *m_left_extruder_unactive_filled;
|
||||
ScalableBitmap *m_left_extruder_unactive_empty;
|
||||
ScalableBitmap *m_right_extruder_active_filled;
|
||||
ScalableBitmap *m_right_extruder_active_empty;
|
||||
ScalableBitmap *m_right_extruder_unactive_filled;
|
||||
ScalableBitmap *m_right_extruder_unactive_empty;
|
||||
|
||||
ScalableBitmap *m_extruder_single_nozzle_empty_load;
|
||||
ScalableBitmap *m_extruder_single_nozzle_empty_unload;
|
||||
ScalableBitmap *m_extruder_single_nozzle_filled_load;
|
||||
ScalableBitmap *m_extruder_single_nozzle_filled_unload;
|
||||
|
||||
DevExtruderState m_left_ext_state = {DevExtruderState::EMPTY_LOAD};
|
||||
DevExtruderState m_right_ext_state = {DevExtruderState::EMPTY_LOAD};
|
||||
DevExtruderState m_single_ext_state = {DevExtruderState::EMPTY_LOAD};
|
||||
|
||||
public:
|
||||
DevExtruderImage(wxWindow *parent, wxWindowID id,
|
||||
int extruder_num,
|
||||
const wxPoint &pos = wxDefaultPosition,
|
||||
const wxSize &size = wxDefaultSize);
|
||||
~DevExtruderImage()
|
||||
{
|
||||
|
||||
}
|
||||
void update(DevExtruderState single_state)
|
||||
{
|
||||
m_single_ext_state = single_state;
|
||||
}
|
||||
void update(DevExtruderState left_state, DevExtruderState right_state)
|
||||
{
|
||||
m_left_ext_state = left_state;
|
||||
m_right_ext_state = right_state;
|
||||
}
|
||||
|
||||
void msw_rescale();
|
||||
void setExtruderCount(int extruder_num)
|
||||
{
|
||||
m_extruder_num = extruder_num;
|
||||
}
|
||||
void setExtruderUsed(const std::string& loc)
|
||||
{
|
||||
if (current_extruder_loc == loc) { return; }
|
||||
current_extruder_loc = loc;
|
||||
Refresh();
|
||||
}
|
||||
private:
|
||||
void paintEvent(wxPaintEvent &evt)
|
||||
{
|
||||
wxPaintDC dc(this);
|
||||
render(dc);
|
||||
}
|
||||
void render(wxDC &dc);
|
||||
void doRender(wxDC &dc);
|
||||
int m_extruder_num = 1;
|
||||
std::string current_extruder_loc = "";
|
||||
|
||||
};
|
||||
|
||||
// Filament Track Switch: when the switch is installed and calibrated a filament can be routed to
|
||||
// either extruder, so this dialog lets the user pick. GetExtruderID() returns the chosen extruder
|
||||
// (1 = deputy/left, 0 = main/right) or nullopt if none was picked; that value is passed to
|
||||
// MachineObject::command_ams_change_filament. Only constructed on the FTS-ready path.
|
||||
class FeedDirectionDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
FeedDirectionDialog(wxWindow* parent, const int extruderNum, const std::string& printer_type = "");
|
||||
|
||||
std::optional<int> GetExtruderID();
|
||||
|
||||
void SetExtruderMapping(MachineObject* obj,
|
||||
const std::string& currAmsId,
|
||||
const std::string& currSlotId,
|
||||
const std::vector<std::pair<std::string, std::string>>& extruderSlots);
|
||||
|
||||
private:
|
||||
static wxString calcTrayName(MachineObject* obj, const std::string& amsID, const std::string& slotID);
|
||||
|
||||
int m_extruder_num{};
|
||||
std::string m_printer_type;
|
||||
wxString m_filament_id{};
|
||||
wxRadioButton* m_radioHelper{nullptr};
|
||||
wxRadioButton* m_leftRadio{nullptr};
|
||||
wxRadioButton* m_rightRadio{nullptr};
|
||||
wxRadioButton* m_lastChecked{nullptr};
|
||||
DevExtruderImage* m_extruderImage{nullptr};
|
||||
Button* m_confirmBtn{nullptr};
|
||||
std::optional<int> m_load_extruder_id = std::nullopt;
|
||||
|
||||
void OnConfirm(wxCommandEvent& event);
|
||||
void OnRadioClicked(wxCommandEvent& evt);
|
||||
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif // !slic3r_GUI_amscontrol_hpp_
|
||||
|
||||
Reference in New Issue
Block a user