From af360d7097d3c946b712fc62f0af4592d0e8921f Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Fri, 4 May 2018 16:04:43 +0200 Subject: [PATCH] Firmware updater GUI --- lib/Slic3r/GUI/MainFrame.pm | 4 +- xs/CMakeLists.txt | 2 + xs/src/avrdude/avrdude-slic3r.cpp | 21 ++- xs/src/avrdude/avrdude-slic3r.hpp | 4 + xs/src/slic3r/GUI/FirmwareDialog.cpp | 197 +++++++++++++++++++++++++++ xs/src/slic3r/GUI/FirmwareDialog.hpp | 31 +++++ xs/src/slic3r/GUI/GUI.cpp | 60 ++++---- xs/src/slic3r/GUI/GUI.hpp | 2 +- xs/xsp/GUI.xsp | 4 +- 9 files changed, 291 insertions(+), 34 deletions(-) create mode 100644 xs/src/slic3r/GUI/FirmwareDialog.cpp create mode 100644 xs/src/slic3r/GUI/FirmwareDialog.hpp diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index a8de4a135..dbec97584 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -352,8 +352,8 @@ sub _init_menubar { $menubar->Append($self->{object_menu}, L("&Object")) if $self->{object_menu}; $menubar->Append($windowMenu, L("&Window")); $menubar->Append($self->{viewMenu}, L("&View")) if $self->{viewMenu}; - # Add a configuration menu. - Slic3r::GUI::add_config_menu($menubar, $self->{preferences_event}, $self->{lang_ch_event}); + # Add additional menus from C++ + Slic3r::GUI::add_menus($menubar, $self->{preferences_event}, $self->{lang_ch_event}); $menubar->Append($helpMenu, L("&Help")); $self->SetMenuBar($menubar); } diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index a55e7d571..8b07562ba 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -226,6 +226,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/MsgDialog.hpp ${LIBDIR}/slic3r/GUI/UpdateDialogs.cpp ${LIBDIR}/slic3r/GUI/UpdateDialogs.hpp + ${LIBDIR}/slic3r/GUI/FirmwareDialog.cpp + ${LIBDIR}/slic3r/GUI/FirmwareDialog.hpp ${LIBDIR}/slic3r/Utils/Http.cpp ${LIBDIR}/slic3r/Utils/Http.hpp ${LIBDIR}/slic3r/Utils/OctoPrint.cpp diff --git a/xs/src/avrdude/avrdude-slic3r.cpp b/xs/src/avrdude/avrdude-slic3r.cpp index 9f9093aa2..d47469e84 100644 --- a/xs/src/avrdude/avrdude-slic3r.cpp +++ b/xs/src/avrdude/avrdude-slic3r.cpp @@ -5,30 +5,39 @@ extern "C" { #include "avrdude.h" } + namespace Slic3r { namespace AvrDude { -static void avrdude_message_handler_ostream(const char *msg, unsigned size, void *user_p) + +// Used by our custom code in avrdude to receive messages that avrdude normally outputs on stdout (see avrdude_message()) +static void avrdude_message_handler_closure(const char *msg, unsigned size, void *user_p) { - (void)size; - std::ostream &os = *reinterpret_cast(user_p); - os << msg; + auto *message_fn = reinterpret_cast(user_p); + (*message_fn)(msg, size); } -int main(std::vector args, std::string sys_config, std::ostream &os) +int main(std::vector args, std::string sys_config, MessageFn message_fn) { std::vector c_args {{ const_cast(PACKAGE_NAME) }}; for (const auto &arg : args) { c_args.push_back(const_cast(arg.data())); } - ::avrdude_message_handler_set(avrdude_message_handler_ostream, reinterpret_cast(&os)); + ::avrdude_message_handler_set(avrdude_message_handler_closure, reinterpret_cast(&message_fn)); const auto res = ::avrdude_main(static_cast(c_args.size()), c_args.data(), sys_config.c_str()); ::avrdude_message_handler_set(nullptr, nullptr); return res; } +int main(std::vector args, std::string sys_config, std::ostream &os) +{ + return main(std::move(args), std::move(sys_config), std::move([&os](const char *msg, unsigned /* size */) { + os << msg; + })); +} + } diff --git a/xs/src/avrdude/avrdude-slic3r.hpp b/xs/src/avrdude/avrdude-slic3r.hpp index d26a0edda..b6143b508 100644 --- a/xs/src/avrdude/avrdude-slic3r.hpp +++ b/xs/src/avrdude/avrdude-slic3r.hpp @@ -4,10 +4,14 @@ #include #include #include +#include namespace Slic3r { namespace AvrDude { + typedef std::function MessageFn; + + int main(std::vector args, std::string sys_config, MessageFn message_fn); int main(std::vector args, std::string sys_config, std::ostream &os); } diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp new file mode 100644 index 000000000..501348686 --- /dev/null +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp @@ -0,0 +1,197 @@ +#include "FirmwareDialog.hpp" + +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libslic3r/Utils.hpp" +#include "avrdude/avrdude-slic3r.hpp" +#include "GUI.hpp" + +namespace fs = boost::filesystem; + + +namespace Slic3r { + + +// Private + +struct FirmwareDialog::priv +{ + std::string avrdude_config; + + wxComboBox *port_picker; + wxFilePickerCtrl *hex_picker; + wxStaticText *status; + wxTextCtrl *txt_stdout; + wxButton *btn_flash; + + priv() : avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()) {} + + void find_serial_ports(); + void perform_upload(); +}; + +void FirmwareDialog::priv::find_serial_ports() +{ + auto ports = GUI::scan_serial_ports(); + + port_picker->Clear(); + for (const auto &port : ports) { port_picker->Append(port); } + + if (ports.size() > 0 && port_picker->GetValue().IsEmpty()) { + port_picker->SetSelection(0); + } +} + +void FirmwareDialog::priv::perform_upload() +{ + auto filename = hex_picker->GetPath(); + auto port = port_picker->GetValue(); + if (filename.IsEmpty() || port.IsEmpty()) { return; } + + // Note: we're not using wxTextCtrl's ability to act as a std::ostream + // because its imeplementation doesn't handle conversion from local charset + // which mangles error messages from perror(). + + auto message_fn = [this](const char *msg, unsigned /* size */) { + // TODO: also log into boost? (Problematic with progress bars.) + this->txt_stdout->AppendText(wxString(msg)); + wxTheApp->Yield(); + }; + + txt_stdout->SetValue(wxEmptyString); + status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!"))); + auto status_color_orig = status->GetForegroundColour(); + status->SetForegroundColour(GUI::get_label_clr_modified()); + btn_flash->Disable(); + + std::vector args {{ + "-v", + "-p", "atmega2560", + "-c", "wiring", + "-P", port.ToStdString(), + "-b", "115200", // XXX: is this ok to hardcode? + "-D", + "-U", (boost::format("flash:w:%1%:i") % filename.ToStdString()).str() + }}; + + BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: " + << std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, const std::string &b) { + return a + ' ' + b; + }); + + auto res = AvrDude::main(std::move(args), avrdude_config, std::move(message_fn)); + + BOOST_LOG_TRIVIAL(info) << "avrdude exit code: " << res; + + btn_flash->Enable(); + + status->SetForegroundColour(status_color_orig); + status->SetLabel( + res == 0 ? _(L("Flashing succeeded!")) : _(L("Flashing failed. Please see the avrdude log below.")) + ); +} + + +// Public + +FirmwareDialog::FirmwareDialog(wxWindow *parent) : + wxDialog(parent, wxID_ANY, _(L("Firmware flasher"))), + // p(new priv(this)) + p(new priv()) +{ + enum { + DIALOG_MARGIN = 15, + SPACING = 10, + }; + + wxFont bold_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + bold_font.MakeBold(); + wxFont mono_font(wxFontInfo().Family(wxFONTFAMILY_TELETYPE)); + mono_font.MakeSmaller(); + + auto *panel = new wxPanel(this); + wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL); + panel->SetSizer(vsizer); + + auto *txt_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:"))); + p->port_picker = new wxComboBox(panel, wxID_ANY); + auto *btn_rescan = new wxButton(panel, wxID_ANY, _(L("Rescan"))); + auto *port_sizer = new wxBoxSizer(wxHORIZONTAL); + port_sizer->Add(p->port_picker, 1, wxEXPAND | wxRIGHT, SPACING); + port_sizer->Add(btn_rescan, 0); + + auto *txt_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:"))); + p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY); + + auto *txt_status = new wxStaticText(panel, wxID_ANY, _(L("Status:"))); + p->status = new wxStaticText(panel, wxID_ANY, _(L("Ready"))); + p->status->SetFont(bold_font); + + auto *sizer_pickers = new wxFlexGridSizer(2, SPACING, SPACING); + sizer_pickers->AddGrowableCol(1); + sizer_pickers->Add(txt_port_picker, 0, wxALIGN_CENTER_VERTICAL); + sizer_pickers->Add(port_sizer, 0, wxEXPAND); + sizer_pickers->Add(txt_hex_picker, 0, wxALIGN_CENTER_VERTICAL); + sizer_pickers->Add(p->hex_picker, 0, wxEXPAND); + sizer_pickers->Add(txt_status, 0, wxALIGN_CENTER_VERTICAL); + sizer_pickers->Add(p->status, 0, wxEXPAND); + vsizer->Add(sizer_pickers, 0, wxEXPAND | wxBOTTOM, SPACING); + + p->txt_stdout = new wxTextCtrl(panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY); + p->txt_stdout->SetFont(mono_font); + vsizer->Add(p->txt_stdout, 1, wxEXPAND | wxBOTTOM, SPACING); + + p->btn_flash = new wxButton(panel, wxID_ANY, _(L("Flash!"))); + auto *bsizer = new wxBoxSizer(wxHORIZONTAL); + bsizer->AddStretchSpacer(); + bsizer->Add(p->btn_flash); + vsizer->Add(bsizer, 0, wxEXPAND); + + auto *topsizer = new wxBoxSizer(wxVERTICAL); + topsizer->Add(panel, 1, wxEXPAND | wxALL, DIALOG_MARGIN); + SetSizerAndFit(topsizer); + SetMinSize(wxSize(400, 400)); + SetSize(wxSize(800, 800)); + + p->find_serial_ports(); + + p->btn_flash->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { this->p->perform_upload(); }); + btn_rescan->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { this->p->find_serial_ports(); }); + + // Bind(EVT_BONJOUR_REPLY, &FirmwareDialog::on_reply, this); + + // Bind(EVT_BONJOUR_COMPLETE, [this](wxCommandEvent &) { + // this->timer_state = 0; + // }); + + // Bind(wxEVT_TIMER, &FirmwareDialog::on_timer, this); +} + +FirmwareDialog::~FirmwareDialog() +{ + // Needed bacuse of forward defs +} + +void FirmwareDialog::run(wxWindow *parent) +{ + FirmwareDialog dialog(parent); + dialog.ShowModal(); +} + + +} diff --git a/xs/src/slic3r/GUI/FirmwareDialog.hpp b/xs/src/slic3r/GUI/FirmwareDialog.hpp new file mode 100644 index 000000000..ad048bf5d --- /dev/null +++ b/xs/src/slic3r/GUI/FirmwareDialog.hpp @@ -0,0 +1,31 @@ +#ifndef slic3r_FirmwareDialog_hpp_ +#define slic3r_FirmwareDialog_hpp_ + +#include + +#include + + +namespace Slic3r { + + +class FirmwareDialog: public wxDialog +{ +public: + FirmwareDialog(wxWindow *parent); + FirmwareDialog(FirmwareDialog &&) = delete; + FirmwareDialog(const FirmwareDialog &) = delete; + FirmwareDialog &operator=(FirmwareDialog &&) = delete; + FirmwareDialog &operator=(const FirmwareDialog &) = delete; + ~FirmwareDialog(); + + static void run(wxWindow *parent); +private: + struct priv; + std::unique_ptr p; +}; + + +} + +#endif diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 7427c5953..5db2c4884 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -1,7 +1,6 @@ #include "GUI.hpp" #include "WipeTowerDialog.hpp" -#include // XXX #include #include @@ -56,11 +55,11 @@ #include "Preferences.hpp" #include "PresetBundle.hpp" #include "UpdateDialogs.hpp" +#include "FirmwareDialog.hpp" #include "../Utils/PresetUpdater.hpp" #include "../Config/Snapshot.hpp" -#include "avrdude/avrdude-slic3r.hpp" // XXX: TMP! namespace Slic3r { namespace GUI { @@ -254,27 +253,6 @@ void set_app_config(AppConfig *app_config) void set_preset_bundle(PresetBundle *preset_bundle) { g_PresetBundle = preset_bundle; - - auto res = AvrDude::main({{ - // "-h", - "-v", - "-p", - "atmega2560", - "-c", - "wiring", - "-P", - "COM1", - "-b115200", - "-D", - "-U", - "flash:w:c:\\local\\prusa3d_fw-3.2.0-RC2.534-1_75mm_MK3-EINSy10a-E3Dv6full.hex:i", - }}, - "c:\\local\\Slic3r\\resources\\avrdude\\avrdude.conf", std::cerr - ); // XXX: tmp - - std::cerr << "AvrDude::main: " << res << std::endl; - - exit(0); } void set_preset_updater(PresetUpdater *updater) @@ -485,6 +463,42 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l menu->Append(local_menu, _(L("&Configuration"))); } +enum FirmwareMenuIDs { + FirmwareMenuFlash, + FirmwareMenuDict, + FirmwareMenuCnt, +}; + +void add_firmware_menu(wxMenuBar *top_menu) +{ + auto *menu = new wxMenu(); + wxWindowID id_base = wxWindow::NewControlId(FirmwareMenuCnt); + + menu->Append(id_base + FirmwareMenuFlash, _(L("Flash printer firmware")), _(L("Upload a firmware image into a Prusa printer"))); + menu->Append(id_base + FirmwareMenuDict, _(L("Flash language file")), _(L("Upload a language dictionary file into a Prusa printer"))); + + menu->Bind(wxEVT_MENU, [id_base](wxEvent &event) { + switch (event.GetId() - id_base) { + case FirmwareMenuFlash: + FirmwareDialog::run(g_wxMainFrame); + break; + case FirmwareMenuDict: + // TODO + break; + default: + break; + } + }); + + top_menu->Append(menu, _(L("Fir&mware"))); +} + +void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change) +{ + add_config_menu(menu, event_language_change, event_language_change); + add_firmware_menu(menu); +} + // This is called when closing the application, when loading a config file or when starting the config wizard // to notify the user whether he is aware that some preset changes will be lost. bool check_unsaved_changes() diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 3752b5a7a..56a43b62d 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -91,7 +91,7 @@ unsigned get_colour_approx_luma(const wxColour &colour); void set_label_clr_modified(const wxColour& clr); void set_label_clr_sys(const wxColour& clr); -extern void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change); +extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change); // This is called when closing the application, when loading a config file or when starting the config wizard // to notify the user whether he is aware that some preset changes will be lost. diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index aa95bd647..2eb24ec7f 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -36,8 +36,8 @@ void set_main_frame(SV *ui) void set_tab_panel(SV *ui) %code%{ Slic3r::GUI::set_tab_panel((wxNotebook*)wxPli_sv_2_object(aTHX_ ui, "Wx::Notebook")); %}; -void add_config_menu(SV *ui, int event_preferences_changed, int event_language_change) - %code%{ Slic3r::GUI::add_config_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_preferences_changed, event_language_change); %}; +void add_menus(SV *ui, int event_preferences_changed, int event_language_change) + %code%{ Slic3r::GUI::add_menus((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_preferences_changed, event_language_change); %}; void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed) %code%{ Slic3r::GUI::create_preset_tabs(no_controller, event_value_change, event_presets_changed); %};