mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-05 17:17:42 +00:00
Pause/resume/stop, g-code sends, temps, and light ran synchronous HTTP on the UI thread, freezing the app up to 10s per click on slow or unreachable printers. Run them on a single agent-owned FIFO worker so g-code ordering is preserved, while command translation stays synchronous so unsupported- command dialogs still work. Add a pending-disabled state to the pause, resume, and abort buttons for Moonraker-family printers: the icon only flips once the WebSocket reports the real state, which also rules out double-click races.
245 lines
11 KiB
C++
245 lines
11 KiB
C++
#ifndef __MOONRAKER_PRINTER_AGENT_HPP__
|
|
#define __MOONRAKER_PRINTER_AGENT_HPP__
|
|
|
|
#include "IPrinterAgent.hpp"
|
|
#include "ICloudServiceAgent.hpp"
|
|
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <set>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <condition_variable>
|
|
#include <deque>
|
|
#include <functional>
|
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
namespace Slic3r {
|
|
|
|
bool moonraker_is_light_name(const std::string& name);
|
|
|
|
class MoonrakerPrinterAgent : public IPrinterAgent
|
|
{
|
|
public:
|
|
explicit MoonrakerPrinterAgent(std::string log_dir);
|
|
~MoonrakerPrinterAgent() override;
|
|
|
|
static AgentInfo get_agent_info_static();
|
|
AgentInfo get_agent_info() override { return get_agent_info_static(); }
|
|
|
|
// Cloud Agent Dependency
|
|
void set_cloud_agent(std::shared_ptr<ICloudServiceAgent> cloud) override;
|
|
|
|
// Communication
|
|
int send_message(std::string dev_id, std::string json_str, int qos, int flag) override;
|
|
int connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl) override;
|
|
int disconnect_printer() override;
|
|
int send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag) override;
|
|
|
|
// Certificates
|
|
int check_cert() override;
|
|
void install_device_cert(std::string dev_id, bool lan_only) override;
|
|
|
|
// Discovery
|
|
bool start_discovery(bool start, bool sending) override;
|
|
|
|
// Binding
|
|
int ping_bind(std::string ping_code) override;
|
|
int bind_detect(std::string dev_ip, std::string sec_link, detectResult& detect) override;
|
|
int bind(std::string dev_ip, std::string dev_id, std::string dev_model, std::string sec_link, std::string timezone, bool improved, OnUpdateStatusFn update_fn) override;
|
|
int unbind(std::string dev_id) override;
|
|
int request_bind_ticket(std::string* ticket) override;
|
|
int get_hms_snapshot(std::string dev_id, std::string file_name, std::function<void(std::string, int)> callback) override;
|
|
int set_server_callback(OnServerErrFn fn) override;
|
|
|
|
// Machine Selection
|
|
std::string get_user_selected_machine() override;
|
|
int set_user_selected_machine(std::string dev_id) override;
|
|
|
|
// Print Job Operations
|
|
int start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
|
|
int start_local_print_with_record(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
|
|
int start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
|
|
int start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) override;
|
|
int start_sdcard_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) override;
|
|
|
|
// Callbacks
|
|
int set_on_ssdp_msg_fn(OnMsgArrivedFn fn) override;
|
|
int set_on_printer_connected_fn(OnPrinterConnectedFn fn) override;
|
|
int set_on_subscribe_failure_fn(GetSubscribeFailureFn fn) override;
|
|
int set_on_message_fn(OnMessageFn fn) override;
|
|
int set_on_user_message_fn(OnMessageFn fn) override;
|
|
int set_on_local_connect_fn(OnLocalConnectedFn fn) override;
|
|
int set_on_local_message_fn(OnMessageFn fn) override;
|
|
int set_queue_on_main_fn(QueueOnMainFn fn) override;
|
|
|
|
// Pull-mode agent (on-demand filament sync)
|
|
FilamentSyncMode get_filament_sync_mode() const override { return FilamentSyncMode::pull; }
|
|
bool fetch_filament_info(std::string dev_id) override;
|
|
|
|
protected:
|
|
struct MoonrakerDeviceInfo
|
|
{
|
|
std::string dev_id;
|
|
std::string dev_ip;
|
|
std::string api_key;
|
|
std::string base_url;
|
|
std::string model_id;
|
|
std::string model_name;
|
|
std::string dev_name;
|
|
std::string version;
|
|
std::string klippy_state;
|
|
bool use_ssl = false;
|
|
} device_info;
|
|
|
|
// Tray data for AMS payload building
|
|
struct AmsTrayData {
|
|
int slot_index = 0; // 0-based slot index
|
|
bool has_filament = false;
|
|
std::string tray_type; // Material type (e.g., "PLA", "ASA")
|
|
std::string tray_color; // Raw color (#RRGGBB, 0xRRGGBB, or RRGGBBAA)
|
|
std::string tray_info_idx; // Setting ID (optional)
|
|
int bed_temp = 0; // Optional
|
|
int nozzle_temp = 0; // Optional
|
|
};
|
|
|
|
// Build ams JSON and call parser
|
|
void build_ams_payload(int ams_count, int max_lane_index, const std::vector<AmsTrayData>& trays);
|
|
|
|
// Methods that derived classes may need to override or access
|
|
virtual bool init_device_info(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl);
|
|
virtual bool fetch_device_info(const std::string& base_url, const std::string& api_key, MoonrakerDeviceInfo& info, std::string& error) const;
|
|
|
|
// State access for derived classes
|
|
mutable std::recursive_mutex state_mutex;
|
|
|
|
// Helpers
|
|
bool is_numeric(const std::string& value);
|
|
std::string normalize_base_url(std::string host, const std::string& port);
|
|
std::string sanitize_filename(const std::string& filename);
|
|
std::string join_url(const std::string& base_url, const std::string& path) const;
|
|
|
|
// Trim whitespace and convert to uppercase
|
|
static std::string trim_and_upper(const std::string& input);
|
|
|
|
// Map filament type to OrcaFilamentLibrary preset ID for AMS sync compatibility
|
|
static std::string map_filament_type_to_generic_id(const std::string& filament_type);
|
|
|
|
// Send a G-code script via Moonraker (/printer/gcode/script)
|
|
bool send_gcode(const std::string& dev_id, const std::string& gcode) const;
|
|
bool send_gcode(const std::string& dev_id, const std::string& gcode,
|
|
const std::string& base_url, const std::string& api_key) const;
|
|
bool post_print_action(const std::string& action) const;
|
|
bool post_print_action(const std::string& action,
|
|
const std::string& base_url, const std::string& api_key) const;
|
|
|
|
private:
|
|
int handle_request(const std::string& dev_id, const std::string& json_str);
|
|
int send_version_info(const std::string& dev_id);
|
|
int send_access_code(const std::string& dev_id);
|
|
|
|
bool fetch_object_list(const std::string& base_url, const std::string& api_key, std::set<std::string>& objects, std::string& error) const;
|
|
bool query_printer_status(const std::string& base_url, const std::string& api_key, nlohmann::json& status, std::string& error) const;
|
|
bool fetch_webcam_info(const std::string& base_url, const std::string& api_key, uint64_t generation);
|
|
|
|
void announce_printhost_device();
|
|
void dispatch_local_connect(int state, const std::string& dev_id, const std::string& msg);
|
|
void dispatch_printer_connected(const std::string& dev_id);
|
|
void dispatch_message(const std::string& dev_id, const std::string& payload);
|
|
void start_status_stream(const std::string& dev_id, const std::string& base_url, const std::string& api_key);
|
|
void stop_status_stream();
|
|
void run_status_stream(std::string dev_id, std::string base_url, std::string api_key);
|
|
void handle_ws_message(std::string dev_id, std::string payload, std::string base_url, std::string api_key);
|
|
void refresh_thumbnail_url(std::string base_url, std::string api_key);
|
|
void update_status_cache(const nlohmann::json& updates);
|
|
nlohmann::json build_print_payload_locked() const;
|
|
|
|
// Print control helpers
|
|
int pause_print(const std::string& dev_id);
|
|
int resume_print(const std::string& dev_id);
|
|
int cancel_print(const std::string& dev_id);
|
|
|
|
// File upload
|
|
bool upload_gcode(const std::string& local_path, const std::string& filename,
|
|
const std::string& base_url, const std::string& api_key,
|
|
OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn);
|
|
|
|
// Start a print of a previously uploaded G-code file (path relative to the
|
|
// Moonraker gcodes root).
|
|
bool start_print_file(const std::string& base_url, const std::string& api_key,
|
|
const std::string& filename, std::string& error_msg) const;
|
|
|
|
// Connection thread management
|
|
void perform_connection_async(const std::string& dev_id,
|
|
const std::string& base_url,
|
|
const std::string& api_key,
|
|
uint64_t generation);
|
|
|
|
// System-specific filament fetch methods
|
|
bool fetch_hh_filament_info(std::vector<AmsTrayData>& trays, int& max_lane_index);
|
|
bool fetch_moonraker_filament_data(std::vector<AmsTrayData>& trays, int& max_lane_index);
|
|
|
|
// JSON helper methods
|
|
static std::string safe_json_string(const nlohmann::json& obj, const char* key);
|
|
static int safe_json_int(const nlohmann::json& obj, const char* key);
|
|
static std::string safe_array_string(const nlohmann::json& arr, int idx);
|
|
static int safe_array_int(const nlohmann::json& arr, int idx);
|
|
static std::string normalize_color_value(const std::string& color);
|
|
|
|
std::string ssdp_announced_host;
|
|
std::string ssdp_announced_id;
|
|
std::shared_ptr<ICloudServiceAgent> m_cloud_agent;
|
|
std::string selected_machine;
|
|
|
|
OnMsgArrivedFn on_ssdp_msg_fn;
|
|
OnPrinterConnectedFn on_printer_connected_fn;
|
|
GetSubscribeFailureFn on_subscribe_failure_fn;
|
|
OnMessageFn on_message_fn;
|
|
OnMessageFn on_user_message_fn;
|
|
OnLocalConnectedFn on_local_connect_fn;
|
|
OnMessageFn on_local_message_fn;
|
|
QueueOnMainFn queue_on_main_fn;
|
|
OnServerErrFn on_server_err_fn;
|
|
|
|
mutable std::recursive_mutex payload_mutex;
|
|
nlohmann::json status_cache;
|
|
// note: guarded by payload_mutex; filled by refresh_thumbnail_url(), empty url = looked up, none found
|
|
std::string thumbnail_filename;
|
|
std::string thumbnail_url;
|
|
std::string webcam_stream_url;
|
|
unsigned thumbnail_lookup_attempts = 0;
|
|
|
|
std::atomic<int> next_jsonrpc_id{1};
|
|
std::set<std::string> available_objects; // Track for feature detection
|
|
bool assumed_light_on = false;
|
|
|
|
std::atomic<bool> ws_stop{false};
|
|
std::atomic<bool> ws_reconnect_requested{false}; // Flag to trigger reconnection
|
|
std::atomic<uint64_t> ws_last_emit_ms{0};
|
|
std::thread ws_thread;
|
|
|
|
// Throttling configuration for WebSocket updates
|
|
// Critical changes (state transitions) dispatch immediately; telemetry is throttled
|
|
static constexpr uint64_t STATUS_UPDATE_INTERVAL_MS = 1000; // 1 update/sec for telemetry
|
|
std::atomic<uint64_t> ws_last_dispatch_ms{0};
|
|
std::string last_print_state; // Track state for immediate dispatch on change
|
|
|
|
// Connection thread management
|
|
std::atomic<uint64_t> connect_generation{0};
|
|
std::thread connect_thread;
|
|
mutable std::recursive_mutex connect_mutex;
|
|
|
|
void enqueue_command(std::function<void()> fn);
|
|
void run_command_worker();
|
|
std::thread cmd_thread;
|
|
std::deque<std::function<void()>> cmd_queue;
|
|
std::mutex cmd_mutex;
|
|
std::condition_variable cmd_cv;
|
|
bool cmd_stop = false;
|
|
};
|
|
|
|
} // namespace Slic3r
|
|
|
|
#endif
|