From 8967ee57ed90f984859d4fbdf9bae78c1683d23d Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 30 Nov 2021 08:43:39 +0100 Subject: [PATCH] Start Simulation for Duet Author: Martin Loidl * PrintHost can now return a set of possible actions to be done after a upload is finished * Added new Button for starting a simulation after upload * Duet Hosts are now able to start a simulation after upload instead of starting a print * removed now unneeded config key 'printhost_print' --- src/slic3r/GUI/Plater.cpp | 5 ++- src/slic3r/GUI/PrintHostDialogs.cpp | 67 ++++++++++++++++------------- src/slic3r/GUI/PrintHostDialogs.hpp | 15 +++---- src/slic3r/Utils/AstroBox.cpp | 4 +- src/slic3r/Utils/AstroBox.hpp | 2 +- src/slic3r/Utils/Duet.cpp | 24 ++++++++--- src/slic3r/Utils/Duet.hpp | 4 +- src/slic3r/Utils/FlashAir.hpp | 2 +- src/slic3r/Utils/MKS.cpp | 4 +- src/slic3r/Utils/MKS.hpp | 2 +- src/slic3r/Utils/OctoPrint.cpp | 4 +- src/slic3r/Utils/OctoPrint.hpp | 6 +-- src/slic3r/Utils/PrintHost.hpp | 14 ++++-- src/slic3r/Utils/Repetier.cpp | 12 +++--- src/slic3r/Utils/Repetier.hpp | 3 +- 15 files changed, 97 insertions(+), 71 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 628aaa4d2..cd354ce55 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6122,11 +6122,12 @@ void Plater::send_gcode() upload_job.printhost->get_groups(groups); } - PrintHostSendDialog dlg(default_output_file, upload_job.printhost->can_start_print(), groups); + PrintHostSendDialog dlg(default_output_file, upload_job.printhost->get_post_upload_actions(), groups); if (dlg.ShowModal() == wxID_OK) { upload_job.upload_data.upload_path = dlg.filename(); - upload_job.upload_data.start_print = dlg.start_print(); + upload_job.upload_data.post_action = dlg.post_action(); upload_job.upload_data.group = dlg.group(); + p->export_gcode(fs::path(), false, std::move(upload_job)); } } diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index da6840ef8..2cea580be 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -23,7 +23,6 @@ #include "GUI_App.hpp" #include "MsgDialog.hpp" #include "I18N.hpp" -#include "../Utils/PrintHost.hpp" #include "MainFrame.hpp" #include "libslic3r/AppConfig.hpp" #include "NotificationManager.hpp" @@ -35,13 +34,13 @@ namespace Slic3r { namespace GUI { static const char *CONFIG_KEY_PATH = "printhost_path"; -static const char *CONFIG_KEY_PRINT = "printhost_print"; static const char *CONFIG_KEY_GROUP = "printhost_group"; -PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_print, const wxArrayString &groups) +PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUploadActions post_actions, const wxArrayString &groups) : MsgDialog(static_cast(wxGetApp().mainframe), _L("Send G-Code to printer host"), _L("Upload to Printer Host with the following filename:")) , txt_filename(new wxTextCtrl(this, wxID_ANY)) , combo_groups(!groups.IsEmpty() ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, groups, wxCB_READONLY) : nullptr) + , post_upload_action(PrintHostPostUploadAction::None) { #ifdef __APPLE__ txt_filename->OSXDisableAllSmartSubstitutions(); @@ -77,38 +76,47 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr txt_filename->SetValue(recent_path); txt_filename->SetFocus(); - wxString suffix = recent_path.substr(recent_path.find_last_of('.')); + m_valid_suffix = recent_path.substr(recent_path.find_last_of('.')); + // .gcode suffix control + auto validate_path = [this](const wxString &path) -> bool { + if (! path.Lower().EndsWith(m_valid_suffix.Lower())) { + MessageDialog msg_wingow(this, wxString::Format(_L("Upload filename doesn't end with \"%s\". Do you wish to continue?"), m_valid_suffix), wxString(SLIC3R_APP_NAME), wxYES | wxNO); + if (msg_wingow.ShowModal() == wxID_NO) + return false; + } + return true; + }; - if (can_start_print) { + if (post_actions.has(PrintHostPostUploadAction::StartPrint)) { auto* btn_print = add_button(wxID_YES, false, _L("Upload and Print")); - btn_print->Bind(wxEVT_BUTTON, [this, suffix](wxCommandEvent&) { - wxString path = txt_filename->GetValue(); - // .gcode suffix control - if (!path.Lower().EndsWith(suffix.Lower())) - { - MessageDialog msg_wingow(this, wxString::Format(_L("Upload filename doesn't end with \"%s\". Do you wish to continue?"), suffix), wxString(SLIC3R_APP_NAME), wxYES | wxNO); - if (msg_wingow.ShowModal() == wxID_NO) - return; + btn_print->Bind(wxEVT_BUTTON, [this, validate_path](wxCommandEvent&) { + if (validate_path(txt_filename->GetValue())) { + post_upload_action = PrintHostPostUploadAction::StartPrint; + EndDialog(wxID_OK); } - start_print_selected = true; - EndDialog(wxID_OK); - }); + }); } + + if (post_actions.has(PrintHostPostUploadAction::StartSimulation)) { + auto* btn_print = add_button(wxID_YES, false, _L("Upload and Simulate")); + btn_print->Bind(wxEVT_BUTTON, [this, validate_path](wxCommandEvent&) { + if (validate_path(txt_filename->GetValue())) { + post_upload_action = PrintHostPostUploadAction::StartSimulation; + EndDialog(wxID_OK); + } + }); + } + add_button(wxID_CANCEL); if (auto* btn_ok = get_button(wxID_OK); btn_ok != NULL) { btn_ok->SetLabel(_L("Upload")); - btn_ok->Bind(wxEVT_BUTTON, [this, suffix](wxCommandEvent&) { - wxString path = txt_filename->GetValue(); - // .gcode suffix control - if (!path.Lower().EndsWith(suffix.Lower())) - { - MessageDialog msg_wingow(this, wxString::Format(_L("Upload filename doesn't end with \"%s\". Do you wish to continue?"), suffix), wxString(SLIC3R_APP_NAME), wxYES | wxNO); - if (msg_wingow.ShowModal() == wxID_NO) - return; + btn_ok->Bind(wxEVT_BUTTON, [this, validate_path](wxCommandEvent&) { + if (validate_path(txt_filename->GetValue())) { + post_upload_action = PrintHostPostUploadAction::None; + EndDialog(wxID_OK); } - EndDialog(wxID_OK); - }); + }); } finalize(); @@ -137,9 +145,9 @@ fs::path PrintHostSendDialog::filename() const return into_path(txt_filename->GetValue()); } -bool PrintHostSendDialog::start_print() const +PrintHostPostUploadAction PrintHostSendDialog::post_action() const { - return start_print_selected; + return post_upload_action; } std::string PrintHostSendDialog::group() const @@ -165,8 +173,7 @@ void PrintHostSendDialog::EndModal(int ret) AppConfig *app_config = wxGetApp().app_config; app_config->set("recent", CONFIG_KEY_PATH, into_u8(path)); - app_config->set("recent", CONFIG_KEY_PRINT, start_print() ? "1" : "0"); - + if (combo_groups != nullptr) { wxString group = combo_groups->GetValue(); app_config->set("recent", CONFIG_KEY_GROUP, into_u8(group)); diff --git a/src/slic3r/GUI/PrintHostDialogs.hpp b/src/slic3r/GUI/PrintHostDialogs.hpp index fa3505f70..ff3eb6012 100644 --- a/src/slic3r/GUI/PrintHostDialogs.hpp +++ b/src/slic3r/GUI/PrintHostDialogs.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_PrintHostSendDialog_hpp_ #define slic3r_PrintHostSendDialog_hpp_ +#include #include #include @@ -10,34 +11,32 @@ #include "GUI_Utils.hpp" #include "MsgDialog.hpp" +#include "../Utils/PrintHost.hpp" class wxButton; class wxTextCtrl; +class wxChoice; class wxComboBox; -class wxCheckBox; class wxDataViewListCtrl; - namespace Slic3r { -struct PrintHostJob; - namespace GUI { - class PrintHostSendDialog : public GUI::MsgDialog { public: - PrintHostSendDialog(const boost::filesystem::path &path, bool can_start_print, const wxArrayString& groups); + PrintHostSendDialog(const boost::filesystem::path &path, PrintHostPostUploadActions post_actions, const wxArrayString& groups); boost::filesystem::path filename() const; - bool start_print() const; + PrintHostPostUploadAction post_action() const; std::string group() const; virtual void EndModal(int ret) override; private: wxTextCtrl *txt_filename; wxComboBox *combo_groups; - bool start_print_selected { false }; + PrintHostPostUploadAction post_upload_action; + wxString m_valid_suffix; }; diff --git a/src/slic3r/Utils/AstroBox.cpp b/src/slic3r/Utils/AstroBox.cpp index 20560f1c1..8781549a2 100644 --- a/src/slic3r/Utils/AstroBox.cpp +++ b/src/slic3r/Utils/AstroBox.cpp @@ -115,11 +115,11 @@ bool AstroBox::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Error % url % upload_filename.string() % upload_parent_path.string() - % upload_data.start_print; + % (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false"); auto http = Http::post(std::move(url)); set_auth(http); - http.form_add("print", upload_data.start_print ? "true" : "false") + http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false") .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? .form_add_file("file", upload_data.source_path.string(), upload_filename.string()) .on_complete([&](std::string body, unsigned status) { diff --git a/src/slic3r/Utils/AstroBox.hpp b/src/slic3r/Utils/AstroBox.hpp index f24018b1b..ad656c494 100644 --- a/src/slic3r/Utils/AstroBox.hpp +++ b/src/slic3r/Utils/AstroBox.hpp @@ -26,7 +26,7 @@ public: bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; bool has_auto_discovery() const override { return true; } bool can_test() const override { return true; } - bool can_start_print() const override { return true; } + PrintHostPostUploadActions get_post_upload_actions() const { return PrintHostPostUploadAction::StartPrint; } std::string get_host() const override { return host; } protected: diff --git a/src/slic3r/Utils/Duet.cpp b/src/slic3r/Utils/Duet.cpp index ba93603a1..3293a3ff2 100644 --- a/src/slic3r/Utils/Duet.cpp +++ b/src/slic3r/Utils/Duet.cpp @@ -67,10 +67,10 @@ bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn e bool dsf = (connectionType == ConnectionType::dsf); auto upload_cmd = get_upload_url(upload_data.upload_path.string(), connectionType); - BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filepath: %2%, print: %3%, command: %4%") + BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filepath: %2%, post_action: %3%, command: %4%") % upload_data.source_path % upload_data.upload_path - % upload_data.start_print + % int(upload_data.post_action) % upload_cmd; auto http = (dsf ? Http::put(std::move(upload_cmd)) : Http::post(std::move(upload_cmd))); @@ -87,9 +87,15 @@ bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn e BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Request completed but error code was received: %1%") % err_code; error_fn(format_error(body, L("Unknown error occured"), 0)); res = false; - } else if (upload_data.start_print) { + } else if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) { wxString errormsg; - res = start_print(errormsg, upload_data.upload_path.string(), connectionType); + res = start_print(errormsg, upload_data.upload_path.string(), connectionType, false); + if (! res) { + error_fn(std::move(errormsg)); + } + } else if (upload_data.post_action == PrintHostPostUploadAction::StartSimulation) { + wxString errormsg; + res = start_print(errormsg, upload_data.upload_path.string(), connectionType, true); if (! res) { error_fn(std::move(errormsg)); } @@ -230,7 +236,7 @@ std::string Duet::timestamp_str() const return std::string(buffer); } -bool Duet::start_print(wxString &msg, const std::string &filename, ConnectionType connectionType) const +bool Duet::start_print(wxString &msg, const std::string &filename, ConnectionType connectionType, bool simulationMode) const { assert(connectionType != ConnectionType::error); @@ -240,14 +246,18 @@ bool Duet::start_print(wxString &msg, const std::string &filename, ConnectionTyp auto url = dsf ? (boost::format("%1%machine/code") % get_base_url()).str() - : (boost::format("%1%rr_gcode?gcode=M32%%20\"0:/gcodes/%2%\"") + : (boost::format(simulationMode + ? "%1%rr_gcode?gcode=M37%%20P\"0:/gcodes/%2%\"" + : "%1%rr_gcode?gcode=M32%%20\"0:/gcodes/%2%\"") % get_base_url() % Http::url_encode(filename)).str(); auto http = (dsf ? Http::post(std::move(url)) : Http::get(std::move(url))); if (dsf) { http.set_post_body( - (boost::format("M32 \"0:/gcodes/%1%\"") + (boost::format(simulationMode + ? "M37 P\"0:/gcodes/%1%\"" + : "M32 \"0:/gcodes/%1%\"") % filename).str() ); } diff --git a/src/slic3r/Utils/Duet.hpp b/src/slic3r/Utils/Duet.hpp index e5aec548b..ea007006c 100644 --- a/src/slic3r/Utils/Duet.hpp +++ b/src/slic3r/Utils/Duet.hpp @@ -25,7 +25,7 @@ public: bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; bool has_auto_discovery() const override { return false; } bool can_test() const override { return true; } - bool can_start_print() const override { return true; } + PrintHostPostUploadActions get_post_upload_actions() const { return PrintHostPostUploadAction::StartPrint | PrintHostPostUploadAction::StartSimulation; } std::string get_host() const override { return host; } private: @@ -39,7 +39,7 @@ private: std::string timestamp_str() const; ConnectionType connect(wxString &msg) const; void disconnect(ConnectionType connectionType) const; - bool start_print(wxString &msg, const std::string &filename, ConnectionType connectionType) const; + bool start_print(wxString &msg, const std::string &filename, ConnectionType connectionType, bool simulationMode) const; int get_err_code_from_body(const std::string &body) const; }; diff --git a/src/slic3r/Utils/FlashAir.hpp b/src/slic3r/Utils/FlashAir.hpp index 181405d46..b03a2c9e5 100644 --- a/src/slic3r/Utils/FlashAir.hpp +++ b/src/slic3r/Utils/FlashAir.hpp @@ -26,7 +26,7 @@ public: bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; bool has_auto_discovery() const override { return false; } bool can_test() const override { return true; } - bool can_start_print() const override { return false; } + PrintHostPostUploadActions get_post_upload_actions() const { return PrintHostPostUploadAction::StartPrint; } std::string get_host() const override { return host; } private: diff --git a/src/slic3r/Utils/MKS.cpp b/src/slic3r/Utils/MKS.cpp index 2188a8f68..80a79537d 100644 --- a/src/slic3r/Utils/MKS.cpp +++ b/src/slic3r/Utils/MKS.cpp @@ -70,7 +70,7 @@ bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn er BOOST_LOG_TRIVIAL(info) << boost::format("MKS: Uploading file %1%, filepath: %2%, print: %3%, command: %4%") % upload_data.source_path % upload_data.upload_path - % upload_data.start_print + % (upload_data.post_action == PrintHostPostUploadAction::StartPrint) % upload_cmd; auto http = Http::post(std::move(upload_cmd)); @@ -85,7 +85,7 @@ bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn er error_fn(format_error(body, L("Unknown error occured"), 0)); res = false; } - else if (upload_data.start_print) { + else if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) { wxString errormsg; res = start_print(errormsg, upload_data.upload_path.string()); if (!res) { diff --git a/src/slic3r/Utils/MKS.hpp b/src/slic3r/Utils/MKS.hpp index 7564b7f81..37021dc6d 100644 --- a/src/slic3r/Utils/MKS.hpp +++ b/src/slic3r/Utils/MKS.hpp @@ -25,7 +25,7 @@ public: bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; bool has_auto_discovery() const override { return false; } bool can_test() const override { return true; } - bool can_start_print() const override { return true; } + PrintHostPostUploadActions get_post_upload_actions() const { return PrintHostPostUploadAction::StartPrint; } std::string get_host() const override { return m_host; } private: diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 12db505b2..d9448b62e 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -137,11 +137,11 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro % url % upload_filename.string() % upload_parent_path.string() - % upload_data.start_print; + % (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false"); auto http = Http::post(std::move(url)); set_auth(http); - http.form_add("print", upload_data.start_print ? "true" : "false") + http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false") .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? .form_add_file("file", upload_data.source_path.string(), upload_filename.string()) .on_complete([&](std::string body, unsigned status) { diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 481b79731..14d53118a 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -28,7 +28,7 @@ public: bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; bool has_auto_discovery() const override { return true; } bool can_test() const override { return true; } - bool can_start_print() const override { return true; } + PrintHostPostUploadActions get_post_upload_actions() const { return PrintHostPostUploadAction::StartPrint; } std::string get_host() const override { return m_host; } const std::string& get_apikey() const { return m_apikey; } const std::string& get_cafile() const { return m_cafile; } @@ -57,7 +57,7 @@ public: wxString get_test_ok_msg() const override; wxString get_test_failed_msg(wxString &msg) const override; - bool can_start_print() const override { return false; } + PrintHostPostUploadActions get_post_upload_actions() const { return {}; } protected: bool validate_version_text(const boost::optional &version_text) const override; @@ -82,7 +82,7 @@ public: wxString get_test_ok_msg() const override; wxString get_test_failed_msg(wxString& msg) const override; - bool can_start_print() const override { return true; } + PrintHostPostUploadActions get_post_upload_actions() const { return PrintHostPostUploadAction::StartPrint; } protected: bool validate_version_text(const boost::optional& version_text) const override; diff --git a/src/slic3r/Utils/PrintHost.hpp b/src/slic3r/Utils/PrintHost.hpp index 35a870b29..dd22e60b7 100644 --- a/src/slic3r/Utils/PrintHost.hpp +++ b/src/slic3r/Utils/PrintHost.hpp @@ -2,12 +2,14 @@ #define slic3r_PrintHost_hpp_ #include +#include #include #include #include #include +#include #include "Http.hpp" class wxArrayString; @@ -16,6 +18,13 @@ namespace Slic3r { class DynamicPrintConfig; +enum class PrintHostPostUploadAction { + None, + StartPrint, + StartSimulation +}; +using PrintHostPostUploadActions = enum_bitmask; +ENABLE_ENUM_BITMASK_OPERATORS(PrintHostPostUploadAction); struct PrintHostUpload { @@ -24,10 +33,9 @@ struct PrintHostUpload std::string group; - bool start_print = false; + PrintHostPostUploadAction post_action { PrintHostPostUploadAction::None }; }; - class PrintHost { public: @@ -44,7 +52,7 @@ public: virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const = 0; virtual bool has_auto_discovery() const = 0; virtual bool can_test() const = 0; - virtual bool can_start_print() const = 0; + virtual PrintHostPostUploadActions get_post_upload_actions() const = 0; // A print host usually does not support multiple printers, with the exception of Repetier server. virtual bool supports_multiple_printers() const { return false; } virtual std::string get_host() const = 0; diff --git a/src/slic3r/Utils/Repetier.cpp b/src/slic3r/Utils/Repetier.cpp index 094d1baa2..0569d97fa 100644 --- a/src/slic3r/Utils/Repetier.cpp +++ b/src/slic3r/Utils/Repetier.cpp @@ -107,7 +107,9 @@ bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Error bool res = true; - auto url = upload_data.start_print?make_url((boost::format("printer/job/%1%") % port).str()):make_url((boost::format("printer/model/%1%") % port).str()); + auto url = upload_data.post_action == PrintHostPostUploadAction::StartPrint + ? make_url((boost::format("printer/job/%1%") % port).str()) + : make_url((boost::format("printer/model/%1%") % port).str()); BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%, group: %7%") % name @@ -115,17 +117,17 @@ bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Error % url % upload_filename.string() % upload_parent_path.string() - % upload_data.start_print + % (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false") % upload_data.group; auto http = Http::post(std::move(url)); set_auth(http); - + if (! upload_data.group.empty() && upload_data.group != _utf8(L("Default"))) { http.form_add("group", upload_data.group); } - - if(upload_data.start_print) { + + if(upload_data.post_action == PrintHostPostUploadAction::StartPrint) { http.form_add("name", upload_filename.string()); } diff --git a/src/slic3r/Utils/Repetier.hpp b/src/slic3r/Utils/Repetier.hpp index 057575417..8b95b019d 100644 --- a/src/slic3r/Utils/Repetier.hpp +++ b/src/slic3r/Utils/Repetier.hpp @@ -7,7 +7,6 @@ #include "PrintHost.hpp" - namespace Slic3r { class DynamicPrintConfig; @@ -27,7 +26,7 @@ public: bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override; bool has_auto_discovery() const override { return false; } bool can_test() const override { return true; } - bool can_start_print() const override { return true; } + PrintHostPostUploadActions get_post_upload_actions() const { return PrintHostPostUploadAction::StartPrint; } bool supports_multiple_printers() const override { return true; } std::string get_host() const override { return host; }