mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-28 19:31:22 +00:00
cleanup moonraker and snapmaker printer agents
This commit is contained in:
@@ -1793,8 +1793,6 @@ int MachineObject::command_ams_refresh_rfid2(int ams_id, int slot_id)
|
|||||||
int MachineObject::command_start_camera()
|
int MachineObject::command_start_camera()
|
||||||
{
|
{
|
||||||
if (!m_agent) return -1;
|
if (!m_agent) return -1;
|
||||||
// why: this fires from the camera view's renew timer, so a refusal must stay silent -
|
|
||||||
// show_unsupported_dlg() here would pop a dialog every ~5 min on every other printer.
|
|
||||||
return m_agent->command_start_camera(get_dev_id());
|
return m_agent->command_start_camera(get_dev_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2315,9 +2315,6 @@ void StatusPanel::update_camera_state(MachineObject* obj)
|
|||||||
const bool has_printer_webcam = !obj->webcam_stream_url.empty();
|
const bool has_printer_webcam = !obj->webcam_stream_url.empty();
|
||||||
if (has_printer_webcam) {
|
if (has_printer_webcam) {
|
||||||
if (m_printer_webcam_url != obj->webcam_stream_url) {
|
if (m_printer_webcam_url != obj->webcam_stream_url) {
|
||||||
// why: start timing belongs to the loaded camera.
|
|
||||||
// carrying it across printers suppresses the new camera's initial start.
|
|
||||||
m_camera_start_sent = std::chrono::steady_clock::time_point{};
|
|
||||||
m_custom_camera_view->LoadURL(obj->webcam_stream_url);
|
m_custom_camera_view->LoadURL(obj->webcam_stream_url);
|
||||||
m_custom_camera_view->Show();
|
m_custom_camera_view->Show();
|
||||||
m_media_ctrl->Hide();
|
m_media_ctrl->Hide();
|
||||||
@@ -2331,19 +2328,9 @@ void StatusPanel::update_camera_state(MachineObject* obj)
|
|||||||
m_media_ctrl->Hide();
|
m_media_ctrl->Hide();
|
||||||
m_media_play_ctrl->Hide();
|
m_media_play_ctrl->Hide();
|
||||||
}
|
}
|
||||||
// why: printers like the U1 capture only while asked and retire the capture task ~362 s
|
|
||||||
// after each start, so the open camera view has to renew ahead of that. 300 s matches
|
|
||||||
// Snapmaker's own client. Agents that do not need it refuse the call silently.
|
|
||||||
const auto now = std::chrono::steady_clock::now();
|
|
||||||
if (m_camera_start_sent == std::chrono::steady_clock::time_point{} ||
|
|
||||||
now - m_camera_start_sent >= std::chrono::seconds(300)) {
|
|
||||||
obj->command_start_camera();
|
|
||||||
m_camera_start_sent = now;
|
|
||||||
}
|
|
||||||
} else if (!m_printer_webcam_url.empty()) {
|
} else if (!m_printer_webcam_url.empty()) {
|
||||||
handle_camera_source_change();
|
handle_camera_source_change();
|
||||||
m_printer_webcam_url.clear();
|
m_printer_webcam_url.clear();
|
||||||
m_camera_start_sent = std::chrono::steady_clock::time_point{};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//sdcard
|
//sdcard
|
||||||
|
|||||||
@@ -666,8 +666,6 @@ protected:
|
|||||||
int m_last_extrusion = -1;
|
int m_last_extrusion = -1;
|
||||||
int m_last_vcamera = -1;
|
int m_last_vcamera = -1;
|
||||||
std::string m_printer_webcam_url;
|
std::string m_printer_webcam_url;
|
||||||
// note: zero = not started; see update_camera_state() for the renew interval.
|
|
||||||
std::chrono::steady_clock::time_point m_camera_start_sent{};
|
|
||||||
int m_model_mall_request_count = 0;
|
int m_model_mall_request_count = 0;
|
||||||
bool m_is_load_with_temp = false;
|
bool m_is_load_with_temp = false;
|
||||||
json m_rating_result;
|
json m_rating_result;
|
||||||
|
|||||||
@@ -93,9 +93,6 @@ public:
|
|||||||
{ return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; }
|
{ return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; }
|
||||||
virtual int command_ams_select_tray(std::string, std::string, int, bool)
|
virtual int command_ams_select_tray(std::string, std::string, int, bool)
|
||||||
{ return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; }
|
{ return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; }
|
||||||
// why: some printers emit camera frames only while explicitly asked, and retire the
|
|
||||||
// capture task on their own - the camera view starts it and renews it. Printers with an
|
|
||||||
// always-on stream need nothing here, hence the honest refusal by default.
|
|
||||||
virtual int command_start_camera(std::string)
|
virtual int command_start_camera(std::string)
|
||||||
{ return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; }
|
{ return ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED; }
|
||||||
|
|
||||||
|
|||||||
@@ -1558,9 +1558,6 @@ bool MoonrakerPrinterAgent::send_ws_rpc(const std::string& method, const nlohman
|
|||||||
request["id"] = next_jsonrpc_id++;
|
request["id"] = next_jsonrpc_id++;
|
||||||
const std::string body = request.dump();
|
const std::string body = request.dump();
|
||||||
|
|
||||||
// why: the configured address and Moonraker's own API port are both plausible websocket
|
|
||||||
// homes - the U1 answers on 7125 while its base_url points at port 80. Try what the user
|
|
||||||
// configured first, then the default API port.
|
|
||||||
std::vector<std::string> ports{endpoint.port};
|
std::vector<std::string> ports{endpoint.port};
|
||||||
if (endpoint.port != "7125") {
|
if (endpoint.port != "7125") {
|
||||||
ports.emplace_back("7125");
|
ports.emplace_back("7125");
|
||||||
@@ -1590,9 +1587,6 @@ bool MoonrakerPrinterAgent::send_ws_rpc(const std::string& method, const nlohman
|
|||||||
ws.text(true);
|
ws.text(true);
|
||||||
ws.write(net::buffer(body));
|
ws.write(net::buffer(body));
|
||||||
|
|
||||||
// why: some RPCs are answered on another transport entirely, so a reply may never
|
|
||||||
// come - read once with a short deadline purely to let the printer act on the
|
|
||||||
// request before we close, then drop the socket. A timeout here is the normal path.
|
|
||||||
ws.next_layer().expires_after(std::chrono::seconds(2));
|
ws.next_layer().expires_after(std::chrono::seconds(2));
|
||||||
beast::flat_buffer buffer;
|
beast::flat_buffer buffer;
|
||||||
beast::error_code read_ec;
|
beast::error_code read_ec;
|
||||||
@@ -1613,18 +1607,6 @@ bool MoonrakerPrinterAgent::send_ws_rpc(const std::string& method, const nlohman
|
|||||||
|
|
||||||
bool MoonrakerPrinterAgent::fetch_webcam_info(const std::string& base_url, const std::string& api_key, uint64_t generation)
|
bool MoonrakerPrinterAgent::fetch_webcam_info(const std::string& base_url, const std::string& api_key, uint64_t generation)
|
||||||
{
|
{
|
||||||
if (const std::string override_url = webcam_stream_override(base_url); !override_url.empty()) {
|
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(payload_mutex);
|
|
||||||
if (generation != connect_generation.load()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
webcam_stream_url = override_url;
|
|
||||||
}
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent: using printer-specific webcam URL " << override_url;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string stream_url;
|
std::string stream_url;
|
||||||
std::string webcam_name;
|
std::string webcam_name;
|
||||||
std::string error;
|
std::string error;
|
||||||
@@ -2155,6 +2137,8 @@ void MoonrakerPrinterAgent::run_status_stream(std::string dev_id, std::string ba
|
|||||||
|
|
||||||
// Read loop
|
// Read loop
|
||||||
while (!ws_stop.load()) {
|
while (!ws_stop.load()) {
|
||||||
|
on_status_loop_tick(dev_id);
|
||||||
|
|
||||||
ws.next_layer().expires_after(std::chrono::seconds(2));
|
ws.next_layer().expires_after(std::chrono::seconds(2));
|
||||||
beast::flat_buffer buffer;
|
beast::flat_buffer buffer;
|
||||||
beast::error_code ec;
|
beast::error_code ec;
|
||||||
@@ -2969,7 +2953,7 @@ std::string MoonrakerPrinterAgent::join_url(const std::string& base_url, const s
|
|||||||
|
|
||||||
// Sanitize filename to prevent path traversal attacks
|
// Sanitize filename to prevent path traversal attacks
|
||||||
// Extracts only the basename, removing any path components
|
// Extracts only the basename, removing any path components
|
||||||
std::string MoonrakerPrinterAgent::sanitize_filename(const std::string& filename) const
|
std::string MoonrakerPrinterAgent::sanitize_filename(const std::string& filename)
|
||||||
{
|
{
|
||||||
if (filename.empty()) {
|
if (filename.empty()) {
|
||||||
return "print.gcode";
|
return "print.gcode";
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ protected:
|
|||||||
// Helpers
|
// Helpers
|
||||||
bool is_numeric(const std::string& value);
|
bool is_numeric(const std::string& value);
|
||||||
std::string normalize_base_url(std::string host, const std::string& port);
|
std::string normalize_base_url(std::string host, const std::string& port);
|
||||||
std::string sanitize_filename(const std::string& filename) const;
|
std::string sanitize_filename(const std::string& filename);
|
||||||
std::string join_url(const std::string& base_url, const std::string& path) const;
|
std::string join_url(const std::string& base_url, const std::string& path) const;
|
||||||
|
|
||||||
// Trim whitespace and convert to uppercase
|
// Trim whitespace and convert to uppercase
|
||||||
@@ -142,13 +142,9 @@ protected:
|
|||||||
bool post_print_action(const std::string& action,
|
bool post_print_action(const std::string& action,
|
||||||
const std::string& base_url, const std::string& api_key) const;
|
const std::string& base_url, const std::string& api_key) const;
|
||||||
|
|
||||||
// Send one JSON-RPC call over a short-lived Moonraker websocket. Returns true when the
|
|
||||||
// request was written; it never waits for a reply.
|
|
||||||
bool send_ws_rpc(const std::string& method, const nlohmann::json& params);
|
bool send_ws_rpc(const std::string& method, const nlohmann::json& params);
|
||||||
|
|
||||||
// why: a printer with no /server/webcams/list entry can still name its stream directly;
|
virtual void on_status_loop_tick(const std::string& dev_id) {}
|
||||||
// returning empty (the default) keeps the normal Moonraker discovery path.
|
|
||||||
virtual std::string webcam_stream_override(const std::string& base_url) const { return {}; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int handle_request(const std::string& dev_id, const std::string& json_str);
|
int handle_request(const std::string& dev_id, const std::string& json_str);
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
#include "SnapmakerPrinterAgent.hpp"
|
#include "SnapmakerPrinterAgent.hpp"
|
||||||
#include "Http.hpp"
|
#include "Http.hpp"
|
||||||
#include "libslic3r/PresetBundle.hpp"
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
#include "libslic3r/Utils.hpp"
|
|
||||||
#include "slic3r/GUI/GUI_App.hpp"
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
|
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
#include <boost/nowide/fstream.hpp>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@@ -15,6 +13,13 @@ namespace Slic3r {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr const char* SNAPMAKER_AGENT_VERSION = "0.0.1";
|
constexpr const char* SNAPMAKER_AGENT_VERSION = "0.0.1";
|
||||||
|
constexpr int64_t CAMERA_REFRESH_INTERVAL_MS = 300'000;
|
||||||
|
|
||||||
|
int64_t now_ms()
|
||||||
|
{
|
||||||
|
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||||
|
}
|
||||||
|
|
||||||
// Safely access a parallel array by index, returning a fallback if out of bounds.
|
// Safely access a parallel array by index, returning a fallback if out of bounds.
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -71,67 +76,38 @@ std::string find_closest_color_preset_by_vendor_and_type(const PresetCollection&
|
|||||||
|
|
||||||
SnapmakerPrinterAgent::SnapmakerPrinterAgent(std::string log_dir) : MoonrakerPrinterAgent(std::move(log_dir)) {}
|
SnapmakerPrinterAgent::SnapmakerPrinterAgent(std::string log_dir) : MoonrakerPrinterAgent(std::move(log_dir)) {}
|
||||||
|
|
||||||
int SnapmakerPrinterAgent::command_start_camera(std::string dev_id)
|
void SnapmakerPrinterAgent::start_camera_monitor()
|
||||||
{
|
{
|
||||||
(void) dev_id;
|
|
||||||
// why: the printer executes this over the websocket but answers only over MQTT, and the
|
|
||||||
// call itself blocks on socket I/O - it fires from the camera view's renew timer on the UI
|
|
||||||
// thread, so run it detached rather than block the caller on a reply that never comes.
|
|
||||||
// note: interval is dead time in SECONDS on top of a ~0.455 s capture, so 0 is the 2.15 fps
|
|
||||||
// ceiling (1 measures 0.63 fps), and it cannot be changed while a capture task is running.
|
|
||||||
std::thread([this] {
|
std::thread([this] {
|
||||||
send_ws_rpc("camera.start_monitor",
|
send_ws_rpc("camera.start_monitor",
|
||||||
{{"domain", "lan"}, {"interval", 0}, {"expect_pw", false}});
|
{{"domain", "lan"}, {"interval", 0}, {"expect_pw", false}});
|
||||||
}).detach();
|
}).detach();
|
||||||
return BAMBU_NETWORK_SUCCESS;
|
m_camera_last_fire_ms.store(now_ms());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SnapmakerPrinterAgent::webcam_stream_override(const std::string& base_url) const
|
void SnapmakerPrinterAgent::on_status_loop_tick(const std::string& dev_id)
|
||||||
{
|
{
|
||||||
const std::string snapshot_url = join_url(base_url, "/server/files/camera/monitor.jpg");
|
(void) dev_id;
|
||||||
|
const int64_t last = m_camera_last_fire_ms.load();
|
||||||
// why: one wrapper file per printer - two U1s would otherwise overwrite each other's URL.
|
if (last == 0 || now_ms() - last >= CAMERA_REFRESH_INTERVAL_MS) {
|
||||||
const boost::filesystem::path page = boost::filesystem::path(data_dir()) / "cache" /
|
start_camera_monitor();
|
||||||
("snapmaker_camera_" + sanitize_filename(device_info.dev_ip) + ".html");
|
|
||||||
|
|
||||||
// why: the printer writes a still JPEG at ~2 fps, so the page polls it with a cache buster
|
|
||||||
// instead of consuming a stream. Chaining the next request off onload (never a bare
|
|
||||||
// setInterval) keeps requests from piling up when the printer is slow to answer.
|
|
||||||
const std::string html =
|
|
||||||
"<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Camera</title><style>"
|
|
||||||
"html,body{margin:0;height:100%;background:#000;overflow:hidden}"
|
|
||||||
"img{width:100%;height:100%;object-fit:contain;display:block}</style></head>"
|
|
||||||
"<body><img id=\"frame\" alt=\"\"><script>\n"
|
|
||||||
"var src=\"" + snapshot_url + "\";\n"
|
|
||||||
"var img=document.getElementById(\"frame\");\n"
|
|
||||||
"function next(){img.src=src+\"?_nocache=\"+Date.now()+\"_\"+Math.floor(Math.random()*10000);}\n"
|
|
||||||
"img.onload=function(){setTimeout(next,250);};\n"
|
|
||||||
"img.onerror=function(){setTimeout(next,1000);};\n"
|
|
||||||
"next();\n"
|
|
||||||
"</script></body></html>\n";
|
|
||||||
|
|
||||||
std::string write_error;
|
|
||||||
try {
|
|
||||||
boost::filesystem::create_directories(page.parent_path());
|
|
||||||
boost::nowide::ofstream out(page.string().c_str(), std::ios::binary | std::ios::trunc);
|
|
||||||
out << html;
|
|
||||||
out.close();
|
|
||||||
// note: an ofstream reports a failed write in its state, not by throwing.
|
|
||||||
if (!out) {
|
|
||||||
write_error = "write failed";
|
|
||||||
}
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
write_error = e.what();
|
|
||||||
}
|
|
||||||
if (!write_error.empty()) {
|
|
||||||
// why: no wrapper means no camera - a raw monitor.jpg URL would render one frozen frame
|
|
||||||
// and read as a broken feed, so fall back to showing nothing and say why in the log.
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "SnapmakerPrinterAgent: could not write camera page " << page.string()
|
|
||||||
<< ": " << write_error;
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return "file://" + page.generic_string();
|
int SnapmakerPrinterAgent::connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl)
|
||||||
|
{
|
||||||
|
const int rtn = MoonrakerPrinterAgent::connect_printer(dev_id, dev_ip, username, password, use_ssl);
|
||||||
|
if (rtn == BAMBU_NETWORK_SUCCESS) {
|
||||||
|
start_camera_monitor();
|
||||||
|
}
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SnapmakerPrinterAgent::command_start_camera(std::string dev_id)
|
||||||
|
{
|
||||||
|
(void) dev_id;
|
||||||
|
start_camera_monitor();
|
||||||
|
return BAMBU_NETWORK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
AgentInfo SnapmakerPrinterAgent::get_agent_info_static()
|
AgentInfo SnapmakerPrinterAgent::get_agent_info_static()
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include "MoonrakerPrinterAgent.hpp"
|
#include "MoonrakerPrinterAgent.hpp"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@@ -16,14 +18,17 @@ public:
|
|||||||
AgentInfo get_agent_info() override { return get_agent_info_static(); }
|
AgentInfo get_agent_info() override { return get_agent_info_static(); }
|
||||||
|
|
||||||
bool fetch_filament_info(std::string dev_id, FilamentSyncMode sync_mode = FilamentSyncMode::pull) override;
|
bool fetch_filament_info(std::string dev_id, FilamentSyncMode sync_mode = FilamentSyncMode::pull) override;
|
||||||
|
int connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl) override;
|
||||||
int command_start_camera(std::string dev_id) override;
|
int command_start_camera(std::string dev_id) override;
|
||||||
|
|
||||||
protected:
|
|
||||||
std::string webcam_stream_override(const std::string& base_url) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Combine filament_type + filament_sub_type into a unified type string
|
// Combine filament_type + filament_sub_type into a unified type string
|
||||||
static std::string combine_filament_type(const std::string& type, const std::string& sub_type);
|
static std::string combine_filament_type(const std::string& type, const std::string& sub_type);
|
||||||
|
|
||||||
|
void start_camera_monitor();
|
||||||
|
void on_status_loop_tick(const std::string& dev_id) override;
|
||||||
|
|
||||||
|
std::atomic<int64_t> m_camera_last_fire_ms{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|||||||
Reference in New Issue
Block a user