mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-23 00:31:11 -06:00
Merge branch 'master' of https://github.com/prusa3d/Slic3r into opengl_to_cpp
This commit is contained in:
commit
59af3fb866
434 changed files with 185728 additions and 109 deletions
402
xs/src/slic3r/GUI/FirmwareDialog.cpp
Normal file
402
xs/src/slic3r/GUI/FirmwareDialog.cpp
Normal file
|
@ -0,0 +1,402 @@
|
|||
#include "FirmwareDialog.hpp"
|
||||
|
||||
#include <numeric>
|
||||
#include <algorithm>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/app.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/filepicker.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/combobox.h>
|
||||
#include <wx/gauge.h>
|
||||
#include <wx/collpane.h>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "avrdude/avrdude-slic3r.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "../Utils/Serial.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
// This enum discriminates the kind of information in EVT_AVRDUDE,
|
||||
// it's stored in the ExtraLong field of wxCommandEvent.
|
||||
enum AvrdudeEvent
|
||||
{
|
||||
AE_MESSAGE,
|
||||
AE_PRORGESS,
|
||||
AE_EXIT,
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(EVT_AVRDUDE, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_AVRDUDE, wxCommandEvent);
|
||||
|
||||
|
||||
// Private
|
||||
|
||||
struct FirmwareDialog::priv
|
||||
{
|
||||
enum AvrDudeComplete
|
||||
{
|
||||
AC_NONE,
|
||||
AC_SUCCESS,
|
||||
AC_FAILURE,
|
||||
AC_CANCEL,
|
||||
};
|
||||
|
||||
FirmwareDialog *q; // PIMPL back pointer ("Q-Pointer")
|
||||
|
||||
wxComboBox *port_picker;
|
||||
std::vector<Utils::SerialPortInfo> ports;
|
||||
wxFilePickerCtrl *hex_picker;
|
||||
wxStaticText *txt_status;
|
||||
wxStaticText *txt_progress;
|
||||
wxGauge *progressbar;
|
||||
wxCollapsiblePane *spoiler;
|
||||
wxTextCtrl *txt_stdout;
|
||||
wxButton *btn_rescan;
|
||||
wxButton *btn_close;
|
||||
wxButton *btn_flash;
|
||||
wxString btn_flash_label_ready;
|
||||
wxString btn_flash_label_flashing;
|
||||
|
||||
// This is a shared pointer holding the background AvrDude task
|
||||
// also serves as a status indication (it is set _iff_ the background task is running, otherwise it is reset).
|
||||
AvrDude::Ptr avrdude;
|
||||
std::string avrdude_config;
|
||||
unsigned progress_tasks_done;
|
||||
bool cancelled;
|
||||
|
||||
priv(FirmwareDialog *q) :
|
||||
q(q),
|
||||
btn_flash_label_ready(_(L("Flash!"))),
|
||||
btn_flash_label_flashing(_(L("Cancel"))),
|
||||
avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()),
|
||||
progress_tasks_done(0),
|
||||
cancelled(false)
|
||||
{}
|
||||
|
||||
void find_serial_ports();
|
||||
void flashing_status(bool flashing, AvrDudeComplete complete = AC_NONE);
|
||||
void perform_upload();
|
||||
void cancel();
|
||||
void on_avrdude(const wxCommandEvent &evt);
|
||||
};
|
||||
|
||||
void FirmwareDialog::priv::find_serial_ports()
|
||||
{
|
||||
auto new_ports = Utils::scan_serial_ports_extended();
|
||||
if (new_ports != this->ports) {
|
||||
this->ports = new_ports;
|
||||
port_picker->Clear();
|
||||
for (const auto &port : this->ports)
|
||||
port_picker->Append(port.friendly_name);
|
||||
if (ports.size() > 0) {
|
||||
int idx = port_picker->GetValue().IsEmpty() ? 0 : -1;
|
||||
for (int i = 0; i < (int)this->ports.size(); ++ i)
|
||||
if (this->ports[i].is_printer) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
if (idx != -1)
|
||||
port_picker->SetSelection(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::flashing_status(bool value, AvrDudeComplete complete)
|
||||
{
|
||||
if (value) {
|
||||
txt_stdout->Clear();
|
||||
txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!")));
|
||||
txt_status->SetForegroundColour(GUI::get_label_clr_modified());
|
||||
port_picker->Disable();
|
||||
btn_rescan->Disable();
|
||||
hex_picker->Disable();
|
||||
btn_close->Disable();
|
||||
btn_flash->SetLabel(btn_flash_label_flashing);
|
||||
progressbar->SetRange(200); // See progress callback below
|
||||
progressbar->SetValue(0);
|
||||
progress_tasks_done = 0;
|
||||
cancelled = false;
|
||||
} else {
|
||||
auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
port_picker->Enable();
|
||||
btn_rescan->Enable();
|
||||
hex_picker->Enable();
|
||||
btn_close->Enable();
|
||||
btn_flash->SetLabel(btn_flash_label_ready);
|
||||
txt_status->SetForegroundColour(text_color);
|
||||
progressbar->SetValue(200);
|
||||
|
||||
switch (complete) {
|
||||
case AC_SUCCESS: txt_status->SetLabel(_(L("Flashing succeeded!"))); break;
|
||||
case AC_FAILURE: txt_status->SetLabel(_(L("Flashing failed. Please see the avrdude log below."))); break;
|
||||
case AC_CANCEL: txt_status->SetLabel(_(L("Flashing cancelled."))); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::perform_upload()
|
||||
{
|
||||
auto filename = hex_picker->GetPath();
|
||||
std::string port = port_picker->GetValue().ToStdString();
|
||||
int selection = port_picker->GetSelection();
|
||||
if (selection != -1) {
|
||||
// Verify whether the combo box list selection equals to the combo box edit value.
|
||||
if (this->ports[selection].friendly_name == port)
|
||||
port = this->ports[selection].port;
|
||||
}
|
||||
if (filename.IsEmpty() || port.empty()) { return; }
|
||||
|
||||
flashing_status(true);
|
||||
|
||||
std::vector<std::string> args {{
|
||||
"-v",
|
||||
"-p", "atmega2560",
|
||||
"-c", "wiring",
|
||||
"-P", port,
|
||||
"-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;
|
||||
});
|
||||
|
||||
// It is ok here to use the q-pointer to the FirmwareDialog
|
||||
// because the dialog ensures it doesn't exit before the background thread is done.
|
||||
auto q = this->q;
|
||||
|
||||
avrdude = AvrDude()
|
||||
.sys_config(avrdude_config)
|
||||
.args(args)
|
||||
.on_message(std::move([q](const char *msg, unsigned /* size */) {
|
||||
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
|
||||
evt->SetExtraLong(AE_MESSAGE);
|
||||
evt->SetString(msg);
|
||||
wxQueueEvent(q, evt);
|
||||
}))
|
||||
.on_progress(std::move([q](const char * /* task */, unsigned progress) {
|
||||
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
|
||||
evt->SetExtraLong(AE_PRORGESS);
|
||||
evt->SetInt(progress);
|
||||
wxQueueEvent(q, evt);
|
||||
}))
|
||||
.on_complete(std::move([q](int status) {
|
||||
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
|
||||
evt->SetExtraLong(AE_EXIT);
|
||||
evt->SetInt(status);
|
||||
wxQueueEvent(q, evt);
|
||||
}))
|
||||
.run();
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::cancel()
|
||||
{
|
||||
if (avrdude) {
|
||||
cancelled = true;
|
||||
txt_status->SetLabel(_(L("Cancelling...")));
|
||||
avrdude->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
|
||||
{
|
||||
AvrDudeComplete complete_kind;
|
||||
|
||||
switch (evt.GetExtraLong()) {
|
||||
case AE_MESSAGE:
|
||||
txt_stdout->AppendText(evt.GetString());
|
||||
break;
|
||||
|
||||
case AE_PRORGESS:
|
||||
// We try to track overall progress here.
|
||||
// When uploading the firmware, avrdude first reads a littlebit of status data,
|
||||
// then performs write, then reading (verification).
|
||||
// We Pulse() during the first read and combine progress of the latter two tasks.
|
||||
|
||||
if (progress_tasks_done == 0) {
|
||||
progressbar->Pulse();
|
||||
} else {
|
||||
progressbar->SetValue(progress_tasks_done - 100 + evt.GetInt());
|
||||
}
|
||||
|
||||
if (evt.GetInt() == 100) {
|
||||
progress_tasks_done += 100;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AE_EXIT:
|
||||
BOOST_LOG_TRIVIAL(info) << "avrdude exit code: " << evt.GetInt();
|
||||
|
||||
complete_kind = cancelled ? AC_CANCEL : (evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE);
|
||||
flashing_status(false, complete_kind);
|
||||
|
||||
// Make sure the background thread is collected and the AvrDude object reset
|
||||
if (avrdude) { avrdude->join(); }
|
||||
avrdude.reset();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Public
|
||||
|
||||
FirmwareDialog::FirmwareDialog(wxWindow *parent) :
|
||||
wxDialog(parent, wxID_ANY, _(L("Firmware flasher")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
||||
p(new priv(this))
|
||||
{
|
||||
enum {
|
||||
DIALOG_MARGIN = 15,
|
||||
SPACING = 10,
|
||||
MIN_WIDTH = 600,
|
||||
MIN_HEIGHT = 200,
|
||||
MIN_HEIGHT_EXPANDED = 500,
|
||||
};
|
||||
|
||||
wxFont status_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
status_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 *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:")));
|
||||
p->port_picker = new wxComboBox(panel, wxID_ANY);
|
||||
p->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(p->btn_rescan, 0);
|
||||
|
||||
auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:")));
|
||||
p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY);
|
||||
|
||||
auto *label_status = new wxStaticText(panel, wxID_ANY, _(L("Status:")));
|
||||
p->txt_status = new wxStaticText(panel, wxID_ANY, _(L("Ready")));
|
||||
p->txt_status->SetFont(status_font);
|
||||
|
||||
auto *label_progress = new wxStaticText(panel, wxID_ANY, _(L("Progress:")));
|
||||
p->progressbar = new wxGauge(panel, wxID_ANY, 1, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL | wxGA_SMOOTH);
|
||||
|
||||
auto *grid = new wxFlexGridSizer(2, SPACING, SPACING);
|
||||
grid->AddGrowableCol(1);
|
||||
grid->Add(label_port_picker, 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(port_sizer, 0, wxEXPAND);
|
||||
|
||||
grid->Add(label_hex_picker, 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(p->hex_picker, 0, wxEXPAND);
|
||||
|
||||
grid->Add(label_status, 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(p->txt_status, 0, wxEXPAND);
|
||||
|
||||
grid->Add(label_progress, 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(p->progressbar, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
vsizer->Add(grid, 0, wxEXPAND | wxTOP | wxBOTTOM, SPACING);
|
||||
|
||||
p->spoiler = new wxCollapsiblePane(panel, wxID_ANY, _(L("Advanced: avrdude output log")));
|
||||
auto *spoiler_pane = p->spoiler->GetPane();
|
||||
auto *spoiler_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
p->txt_stdout = new wxTextCtrl(spoiler_pane, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY);
|
||||
p->txt_stdout->SetFont(mono_font);
|
||||
spoiler_sizer->Add(p->txt_stdout, 1, wxEXPAND);
|
||||
spoiler_pane->SetSizer(spoiler_sizer);
|
||||
// The doc says proportion need to be 0 for wxCollapsiblePane.
|
||||
// Experience says it needs to be 1, otherwise things won't get sized properly.
|
||||
vsizer->Add(p->spoiler, 1, wxEXPAND | wxBOTTOM, SPACING);
|
||||
|
||||
p->btn_close = new wxButton(panel, wxID_CLOSE);
|
||||
p->btn_flash = new wxButton(panel, wxID_ANY, p->btn_flash_label_ready);
|
||||
auto *bsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
bsizer->Add(p->btn_close);
|
||||
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);
|
||||
SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT));
|
||||
SetSizerAndFit(topsizer);
|
||||
const auto size = GetSize();
|
||||
SetSize(std::max(size.GetWidth(), static_cast<int>(MIN_WIDTH)), std::max(size.GetHeight(), static_cast<int>(MIN_HEIGHT)));
|
||||
Layout();
|
||||
|
||||
p->spoiler->Bind(wxEVT_COLLAPSIBLEPANE_CHANGED, [this](wxCollapsiblePaneEvent &evt) {
|
||||
// Dialog size gets screwed up by wxCollapsiblePane, we need to fix it here
|
||||
if (evt.GetCollapsed()) {
|
||||
this->SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT));
|
||||
} else {
|
||||
this->SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT_EXPANDED));
|
||||
}
|
||||
|
||||
this->Fit();
|
||||
this->Layout();
|
||||
});
|
||||
|
||||
p->btn_close->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { this->Close(); });
|
||||
p->btn_rescan->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { this->p->find_serial_ports(); });
|
||||
|
||||
p->btn_flash->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) {
|
||||
if (this->p->avrdude) {
|
||||
// Flashing is in progress, ask the user if they're really sure about canceling it
|
||||
wxMessageDialog dlg(this,
|
||||
_(L("Are you sure you want to cancel firmware flashing?\nThis could leave your printer in an unusable state!")),
|
||||
_(L("Confirmation")),
|
||||
wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
|
||||
if (dlg.ShowModal() == wxID_YES) {
|
||||
this->p->cancel();
|
||||
}
|
||||
} else {
|
||||
// Start a flashing task
|
||||
this->p->perform_upload();
|
||||
}
|
||||
});
|
||||
|
||||
Bind(EVT_AVRDUDE, [this](wxCommandEvent &evt) { this->p->on_avrdude(evt); });
|
||||
|
||||
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt) {
|
||||
if (this->p->avrdude) {
|
||||
evt.Veto();
|
||||
} else {
|
||||
evt.Skip();
|
||||
}
|
||||
});
|
||||
|
||||
p->find_serial_ports();
|
||||
}
|
||||
|
||||
FirmwareDialog::~FirmwareDialog()
|
||||
{
|
||||
// Needed bacuse of forward defs
|
||||
}
|
||||
|
||||
void FirmwareDialog::run(wxWindow *parent)
|
||||
{
|
||||
FirmwareDialog dialog(parent);
|
||||
dialog.ShowModal();
|
||||
}
|
||||
|
||||
|
||||
}
|
31
xs/src/slic3r/GUI/FirmwareDialog.hpp
Normal file
31
xs/src/slic3r/GUI/FirmwareDialog.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef slic3r_FirmwareDialog_hpp_
|
||||
#define slic3r_FirmwareDialog_hpp_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <wx/dialog.h>
|
||||
|
||||
|
||||
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<priv> p;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -4,11 +4,8 @@
|
|||
#include <assert.h>
|
||||
#include <cmath>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#if __APPLE__
|
||||
|
@ -25,7 +22,6 @@
|
|||
#undef max
|
||||
#endif
|
||||
#include "boost/nowide/convert.hpp"
|
||||
#pragma comment(lib, "user32.lib")
|
||||
#endif
|
||||
|
||||
#include <wx/app.h>
|
||||
|
@ -55,10 +51,12 @@
|
|||
#include "Preferences.hpp"
|
||||
#include "PresetBundle.hpp"
|
||||
#include "UpdateDialogs.hpp"
|
||||
#include "FirmwareDialog.hpp"
|
||||
|
||||
#include "../Utils/PresetUpdater.hpp"
|
||||
#include "../Config/Snapshot.hpp"
|
||||
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
#if __APPLE__
|
||||
|
@ -86,83 +84,6 @@ void enable_screensaver()
|
|||
#endif
|
||||
}
|
||||
|
||||
std::vector<std::string> scan_serial_ports()
|
||||
{
|
||||
std::vector<std::string> out;
|
||||
#ifdef _WIN32
|
||||
// 1) Open the registry key SERIALCOM.
|
||||
HKEY hKey;
|
||||
LONG lRes = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_READ, &hKey);
|
||||
assert(lRes == ERROR_SUCCESS);
|
||||
if (lRes == ERROR_SUCCESS) {
|
||||
// 2) Get number of values of SERIALCOM key.
|
||||
DWORD cValues; // number of values for key
|
||||
{
|
||||
TCHAR achKey[255]; // buffer for subkey name
|
||||
DWORD cbName; // size of name string
|
||||
TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name
|
||||
DWORD cchClassName = MAX_PATH; // size of class string
|
||||
DWORD cSubKeys=0; // number of subkeys
|
||||
DWORD cbMaxSubKey; // longest subkey size
|
||||
DWORD cchMaxClass; // longest class string
|
||||
DWORD cchMaxValue; // longest value name
|
||||
DWORD cbMaxValueData; // longest value data
|
||||
DWORD cbSecurityDescriptor; // size of security descriptor
|
||||
FILETIME ftLastWriteTime; // last write time
|
||||
// Get the class name and the value count.
|
||||
lRes = RegQueryInfoKey(
|
||||
hKey, // key handle
|
||||
achClass, // buffer for class name
|
||||
&cchClassName, // size of class string
|
||||
NULL, // reserved
|
||||
&cSubKeys, // number of subkeys
|
||||
&cbMaxSubKey, // longest subkey size
|
||||
&cchMaxClass, // longest class string
|
||||
&cValues, // number of values for this key
|
||||
&cchMaxValue, // longest value name
|
||||
&cbMaxValueData, // longest value data
|
||||
&cbSecurityDescriptor, // security descriptor
|
||||
&ftLastWriteTime); // last write time
|
||||
assert(lRes == ERROR_SUCCESS);
|
||||
}
|
||||
// 3) Read the SERIALCOM values.
|
||||
{
|
||||
DWORD dwIndex = 0;
|
||||
for (int i = 0; i < cValues; ++ i, ++ dwIndex) {
|
||||
wchar_t valueName[2048];
|
||||
DWORD valNameLen = 2048;
|
||||
DWORD dataType;
|
||||
wchar_t data[2048];
|
||||
DWORD dataSize = 4096;
|
||||
lRes = ::RegEnumValueW(hKey, dwIndex, valueName, &valNameLen, nullptr, &dataType, (BYTE*)&data, &dataSize);
|
||||
if (lRes == ERROR_SUCCESS && dataType == REG_SZ && valueName[0] != 0)
|
||||
out.emplace_back(boost::nowide::narrow(data));
|
||||
}
|
||||
}
|
||||
::RegCloseKey(hKey);
|
||||
}
|
||||
#else
|
||||
// UNIX and OS X
|
||||
std::initializer_list<const char*> prefixes { "ttyUSB" , "ttyACM", "tty.", "cu.", "rfcomm" };
|
||||
for (auto &dir_entry : boost::filesystem::directory_iterator(boost::filesystem::path("/dev"))) {
|
||||
std::string name = dir_entry.path().filename().string();
|
||||
for (const char *prefix : prefixes) {
|
||||
if (boost::starts_with(name, prefix)) {
|
||||
out.emplace_back(dir_entry.path().string());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
out.erase(std::remove_if(out.begin(), out.end(),
|
||||
[](const std::string &key){
|
||||
return boost::starts_with(key, "Bluetooth") || boost::starts_with(key, "FireFly");
|
||||
}),
|
||||
out.end());
|
||||
return out;
|
||||
}
|
||||
|
||||
bool debugged()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
@ -383,6 +304,7 @@ enum ConfigMenuIDs {
|
|||
ConfigMenuUpdate,
|
||||
ConfigMenuPreferences,
|
||||
ConfigMenuLanguage,
|
||||
ConfigMenuFlashFirmware,
|
||||
ConfigMenuCnt,
|
||||
};
|
||||
|
||||
|
@ -396,11 +318,15 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
|
|||
local_menu->Append(config_id_base + ConfigMenuWizard, ConfigWizard::name() + "\u2026", config_wizard_tooltip);
|
||||
local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("Configuration Snapshots"))+"\u2026", _(L("Inspect / activate configuration snapshots")));
|
||||
local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration Snapshot")), _(L("Capture a configuration snapshot")));
|
||||
local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates")));
|
||||
// local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates")));
|
||||
local_menu->AppendSeparator();
|
||||
local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("Preferences"))+"\u2026\tCtrl+,", _(L("Application preferences")));
|
||||
local_menu->AppendSeparator();
|
||||
local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language")));
|
||||
local_menu->AppendSeparator();
|
||||
local_menu->Append(config_id_base + ConfigMenuFlashFirmware, _(L("Flash printer firmware")), _(L("Upload a firmware image into an Arduino based printer")));
|
||||
// TODO: for when we're able to flash dictionaries
|
||||
// local_menu->Append(config_id_base + FirmwareMenuDict, _(L("Flash language file")), _(L("Upload a language dictionary file into a Prusa printer")));
|
||||
|
||||
local_menu->Bind(wxEVT_MENU, [config_id_base, event_language_change, event_preferences_changed](wxEvent &event){
|
||||
switch (event.GetId() - config_id_base) {
|
||||
case ConfigMenuWizard:
|
||||
|
@ -455,12 +381,22 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
|
|||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
case ConfigMenuFlashFirmware:
|
||||
FirmwareDialog::run(g_wxMainFrame);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
menu->Append(local_menu, _(L("&Configuration")));
|
||||
}
|
||||
|
||||
void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change)
|
||||
{
|
||||
add_config_menu(menu, event_language_change, 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.
|
||||
bool check_unsaved_changes()
|
||||
|
@ -992,4 +928,35 @@ void about()
|
|||
dlg.Destroy();
|
||||
}
|
||||
|
||||
void desktop_open_datadir_folder()
|
||||
{
|
||||
// Execute command to open a file explorer, platform dependent.
|
||||
std::string cmd =
|
||||
#ifdef _WIN32
|
||||
"explorer "
|
||||
#elif __APPLE__
|
||||
"open "
|
||||
#else
|
||||
"xdg-open "
|
||||
#endif
|
||||
;
|
||||
// Escape the path, platform dependent.
|
||||
std::string path = data_dir();
|
||||
#ifdef _WIN32
|
||||
// Enclose the path into double quotes on Windows. A quote character is forbidden in file names,
|
||||
// therefore it does not need to be escaped.
|
||||
cmd += '"';
|
||||
cmd += path;
|
||||
cmd += '"';
|
||||
#else
|
||||
// Enclose the path into single quotes on Unix / OSX. All single quote characters need to be escaped
|
||||
// inside a file name.
|
||||
cmd += '\'';
|
||||
boost::replace_all(path, "'", "\\'");
|
||||
cmd += path;
|
||||
cmd += '\'';
|
||||
#endif
|
||||
::wxExecute(wxString::FromUTF8(cmd.c_str()), wxEXEC_ASYNC, nullptr);
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
@ -68,7 +68,6 @@ inline t_file_wild_card& get_file_wild_card() {
|
|||
|
||||
void disable_screensaver();
|
||||
void enable_screensaver();
|
||||
std::vector<std::string> scan_serial_ports();
|
||||
bool debugged();
|
||||
void break_to_debugger();
|
||||
|
||||
|
@ -91,7 +90,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.
|
||||
|
@ -159,7 +158,9 @@ void add_export_option(wxFileDialog* dlg, const std::string& format);
|
|||
int get_export_option(wxFileDialog* dlg);
|
||||
|
||||
// Display an About dialog
|
||||
void about();
|
||||
extern void about();
|
||||
// Ask the destop to open the datadir using the default file explorer.
|
||||
extern void desktop_open_datadir_folder();
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "slic3r/Utils/Http.hpp"
|
||||
#include "slic3r/Utils/OctoPrint.hpp"
|
||||
#include "slic3r/Utils/Serial.hpp"
|
||||
#include "BonjourDialog.hpp"
|
||||
#include "WipeTowerDialog.hpp"
|
||||
#include "ButtonsDescription.hpp"
|
||||
|
@ -1693,7 +1694,7 @@ void TabPrinter::build()
|
|||
void TabPrinter::update_serial_ports(){
|
||||
Field *field = get_field("serial_port");
|
||||
Choice *choice = static_cast<Choice *>(field);
|
||||
choice->set_values(scan_serial_ports());
|
||||
choice->set_values(Utils::scan_serial_ports());
|
||||
}
|
||||
|
||||
void TabPrinter::extruders_count_changed(size_t extruders_count){
|
||||
|
@ -1709,7 +1710,8 @@ void TabPrinter::build_extruder_pages(){
|
|||
size_t n_before_extruders = 2; // Count of pages before Extruder pages
|
||||
size_t n_after_single_extruder_MM = 2; // Count of pages after single_extruder_multi_material page
|
||||
|
||||
if (m_extruders_count_old == m_extruders_count || m_extruders_count <= 2)
|
||||
if (m_extruders_count_old == m_extruders_count ||
|
||||
(m_has_single_extruder_MM_page && m_extruders_count == 1))
|
||||
{
|
||||
// if we have a single extruder MM setup, add a page with configuration options:
|
||||
for (int i = 0; i < m_pages.size(); ++i) // first make sure it's not there already
|
||||
|
@ -1717,16 +1719,19 @@ void TabPrinter::build_extruder_pages(){
|
|||
m_pages.erase(m_pages.begin() + i);
|
||||
break;
|
||||
}
|
||||
if (m_extruders_count > 1 && m_config->opt_bool("single_extruder_multi_material")) {
|
||||
// create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves
|
||||
auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png", true);
|
||||
auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters")));
|
||||
optgroup->append_single_option_line("cooling_tube_retraction");
|
||||
optgroup->append_single_option_line("cooling_tube_length");
|
||||
optgroup->append_single_option_line("parking_pos_retraction");
|
||||
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
|
||||
}
|
||||
m_has_single_extruder_MM_page = false;
|
||||
}
|
||||
if (m_extruders_count > 1 && m_config->opt_bool("single_extruder_multi_material") && !m_has_single_extruder_MM_page) {
|
||||
// create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves
|
||||
auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png", true);
|
||||
auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters")));
|
||||
optgroup->append_single_option_line("cooling_tube_retraction");
|
||||
optgroup->append_single_option_line("cooling_tube_length");
|
||||
optgroup->append_single_option_line("parking_pos_retraction");
|
||||
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
|
||||
m_has_single_extruder_MM_page = true;
|
||||
}
|
||||
|
||||
|
||||
for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx){
|
||||
//# build page
|
||||
|
|
|
@ -312,6 +312,7 @@ public:
|
|||
//Slic3r::GUI::Tab::Printer;
|
||||
class TabPrinter : public Tab
|
||||
{
|
||||
bool m_has_single_extruder_MM_page = false;
|
||||
public:
|
||||
wxButton* m_serial_test_btn;
|
||||
wxButton* m_octoprint_host_test_btn;
|
||||
|
|
193
xs/src/slic3r/Utils/Serial.cpp
Normal file
193
xs/src/slic3r/Utils/Serial.cpp
Normal file
|
@ -0,0 +1,193 @@
|
|||
#include "Serial.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#if _WIN32
|
||||
#include <Windows.h>
|
||||
#include <Setupapi.h>
|
||||
#include <initguid.h>
|
||||
#include <devguid.h>
|
||||
// Undefine min/max macros incompatible with the standard library
|
||||
// For example, std::numeric_limits<std::streamsize>::max()
|
||||
// produces some weird errors
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
#include "boost/nowide/convert.hpp"
|
||||
#pragma comment(lib, "user32.lib")
|
||||
#elif __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreFoundation/CFString.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/serial/IOSerialKeys.h>
|
||||
#include <IOKit/serial/ioss.h>
|
||||
#include <sys/syslimits.h>
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
namespace Utils {
|
||||
|
||||
static bool looks_like_printer(const std::string &friendly_name)
|
||||
{
|
||||
return friendly_name.find("Original Prusa") != std::string::npos;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static std::string get_tty_friendly_name(const std::string &path, const std::string &name)
|
||||
{
|
||||
const auto sysfs_product = (boost::format("/sys/class/tty/%1%/device/../product") % name).str();
|
||||
std::ifstream file(sysfs_product);
|
||||
std::string product;
|
||||
|
||||
std::getline(file, product);
|
||||
return file.good() ? (boost::format("%1% (%2%)") % product % path).str() : path;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<SerialPortInfo> scan_serial_ports_extended()
|
||||
{
|
||||
std::vector<SerialPortInfo> output;
|
||||
|
||||
#ifdef _WIN32
|
||||
SP_DEVINFO_DATA devInfoData = { 0 };
|
||||
devInfoData.cbSize = sizeof(devInfoData);
|
||||
// Get the tree containing the info for the ports.
|
||||
HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS, 0, nullptr, DIGCF_PRESENT);
|
||||
if (hDeviceInfo != INVALID_HANDLE_VALUE) {
|
||||
// Iterate over all the devices in the tree.
|
||||
for (int nDevice = 0; SetupDiEnumDeviceInfo(hDeviceInfo, nDevice, &devInfoData); ++ nDevice) {
|
||||
SerialPortInfo port_info;
|
||||
// Get the registry key which stores the ports settings.
|
||||
HKEY hDeviceKey = SetupDiOpenDevRegKey(hDeviceInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
|
||||
if (hDeviceKey) {
|
||||
// Read in the name of the port.
|
||||
wchar_t pszPortName[4096];
|
||||
DWORD dwSize = sizeof(pszPortName);
|
||||
DWORD dwType = 0;
|
||||
if (RegQueryValueEx(hDeviceKey, L"PortName", NULL, &dwType, (LPBYTE)pszPortName, &dwSize) == ERROR_SUCCESS)
|
||||
port_info.port = boost::nowide::narrow(pszPortName);
|
||||
RegCloseKey(hDeviceKey);
|
||||
if (port_info.port.empty())
|
||||
continue;
|
||||
}
|
||||
// Find the size required to hold the device info.
|
||||
DWORD regDataType;
|
||||
DWORD reqSize = 0;
|
||||
SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, nullptr, nullptr, 0, &reqSize);
|
||||
std::vector<wchar_t> hardware_id(reqSize > 1 ? reqSize : 1);
|
||||
// Now store it in a buffer.
|
||||
if (! SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, ®DataType, (BYTE*)hardware_id.data(), reqSize, nullptr))
|
||||
continue;
|
||||
port_info.hardware_id = boost::nowide::narrow(hardware_id.data());
|
||||
// Find the size required to hold the friendly name.
|
||||
reqSize = 0;
|
||||
SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, nullptr, 0, &reqSize);
|
||||
std::vector<wchar_t> friendly_name;
|
||||
friendly_name.reserve(reqSize > 1 ? reqSize : 1);
|
||||
// Now store it in a buffer.
|
||||
if (! SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, (BYTE*)friendly_name.data(), reqSize, nullptr)) {
|
||||
port_info.friendly_name = port_info.port;
|
||||
} else {
|
||||
port_info.friendly_name = boost::nowide::narrow(friendly_name.data());
|
||||
port_info.is_printer = looks_like_printer(port_info.friendly_name);
|
||||
}
|
||||
output.emplace_back(std::move(port_info));
|
||||
}
|
||||
}
|
||||
#elif __APPLE__
|
||||
// inspired by https://sigrok.org/wiki/Libserialport
|
||||
CFMutableDictionaryRef classes = IOServiceMatching(kIOSerialBSDServiceValue);
|
||||
if (classes != 0) {
|
||||
io_iterator_t iter;
|
||||
if (IOServiceGetMatchingServices(kIOMasterPortDefault, classes, &iter) == KERN_SUCCESS) {
|
||||
io_object_t port;
|
||||
while ((port = IOIteratorNext(iter)) != 0) {
|
||||
CFTypeRef cf_property = IORegistryEntryCreateCFProperty(port, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
|
||||
if (cf_property) {
|
||||
char path[PATH_MAX];
|
||||
Boolean result = CFStringGetCString((CFStringRef)cf_property, path, sizeof(path), kCFStringEncodingUTF8);
|
||||
CFRelease(cf_property);
|
||||
if (result) {
|
||||
SerialPortInfo port_info;
|
||||
port_info.port = path;
|
||||
if ((cf_property = IORegistryEntrySearchCFProperty(port, kIOServicePlane,
|
||||
CFSTR("USB Interface Name"), kCFAllocatorDefault,
|
||||
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
|
||||
(cf_property = IORegistryEntrySearchCFProperty(port, kIOServicePlane,
|
||||
CFSTR("USB Product Name"), kCFAllocatorDefault,
|
||||
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
|
||||
(cf_property = IORegistryEntrySearchCFProperty(port, kIOServicePlane,
|
||||
CFSTR("Product Name"), kCFAllocatorDefault,
|
||||
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
|
||||
(cf_property = IORegistryEntryCreateCFProperty(port,
|
||||
CFSTR(kIOTTYDeviceKey), kCFAllocatorDefault, 0))) {
|
||||
// Description limited to 127 char, anything longer would not be user friendly anyway.
|
||||
char description[128];
|
||||
if (CFStringGetCString((CFStringRef)cf_property, description, sizeof(description), kCFStringEncodingUTF8)) {
|
||||
port_info.friendly_name = std::string(description) + " (" + port_info.port + ")";
|
||||
port_info.is_printer = looks_like_printer(port_info.friendly_name);
|
||||
}
|
||||
CFRelease(cf_property);
|
||||
}
|
||||
if (port_info.friendly_name.empty())
|
||||
port_info.friendly_name = port_info.port;
|
||||
output.emplace_back(std::move(port_info));
|
||||
}
|
||||
}
|
||||
IOObjectRelease(port);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
// UNIX / Linux
|
||||
std::initializer_list<const char*> prefixes { "ttyUSB" , "ttyACM", "tty.", "cu.", "rfcomm" };
|
||||
for (auto &dir_entry : boost::filesystem::directory_iterator(boost::filesystem::path("/dev"))) {
|
||||
std::string name = dir_entry.path().filename().string();
|
||||
for (const char *prefix : prefixes) {
|
||||
if (boost::starts_with(name, prefix)) {
|
||||
const auto path = dir_entry.path().string();
|
||||
SerialPortInfo spi;
|
||||
spi.port = path;
|
||||
spi.hardware_id = path;
|
||||
#ifdef __linux__
|
||||
spi.friendly_name = get_tty_friendly_name(path, name);
|
||||
#else
|
||||
spi.friendly_name = path;
|
||||
#endif
|
||||
output.emplace_back(std::move(spi));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
output.erase(std::remove_if(output.begin(), output.end(),
|
||||
[](const SerialPortInfo &info) {
|
||||
return boost::starts_with(info.port, "Bluetooth") || boost::starts_with(info.port, "FireFly");
|
||||
}),
|
||||
output.end());
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<std::string> scan_serial_ports()
|
||||
{
|
||||
std::vector<SerialPortInfo> ports = scan_serial_ports_extended();
|
||||
std::vector<std::string> output;
|
||||
output.reserve(ports.size());
|
||||
for (const SerialPortInfo &spi : ports)
|
||||
output.emplace_back(std::move(spi.port));
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
} // namespace Slic3r
|
32
xs/src/slic3r/Utils/Serial.hpp
Normal file
32
xs/src/slic3r/Utils/Serial.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#ifndef slic3r_GUI_Utils_Serial_hpp_
|
||||
#define slic3r_GUI_Utils_Serial_hpp_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace Utils {
|
||||
|
||||
struct SerialPortInfo {
|
||||
std::string port;
|
||||
std::string hardware_id;
|
||||
std::string friendly_name;
|
||||
bool is_printer = false;
|
||||
};
|
||||
|
||||
inline bool operator==(const SerialPortInfo &sp1, const SerialPortInfo &sp2)
|
||||
{
|
||||
return sp1.port == sp2.port &&
|
||||
sp1.hardware_id == sp2.hardware_id &&
|
||||
sp1.is_printer == sp2.is_printer;
|
||||
}
|
||||
|
||||
extern std::vector<std::string> scan_serial_ports();
|
||||
extern std::vector<SerialPortInfo> scan_serial_ports_extended();
|
||||
|
||||
} // Utils
|
||||
} // Slic3r
|
||||
|
||||
#endif /* slic3r_GUI_Utils_Serial_hpp_ */
|
Loading…
Add table
Add a link
Reference in a new issue