Merge remote-tracking branch 'origin/master' into feature_arrange_with_libnest2d
This commit is contained in:
commit
66d1eb9b9d
20 changed files with 512 additions and 260 deletions
|
@ -35,8 +35,9 @@ struct AvrDude::priv
|
|||
{
|
||||
std::string sys_config;
|
||||
std::deque<std::vector<std::string>> args;
|
||||
size_t current_args_set = 0;
|
||||
bool cancelled = false;
|
||||
int exit_code = 0;
|
||||
size_t current_args_set = 0;
|
||||
RunFn run_fn;
|
||||
MessageFn message_fn;
|
||||
ProgressFn progress_fn;
|
||||
|
@ -146,15 +147,15 @@ AvrDude::Ptr AvrDude::run()
|
|||
int res = -1;
|
||||
|
||||
if (self->p->run_fn) {
|
||||
self->p->run_fn(*self);
|
||||
self->p->run_fn();
|
||||
}
|
||||
|
||||
if (! self->p->cancelled) {
|
||||
res = self->p->run();
|
||||
self->p->exit_code = self->p->run();
|
||||
}
|
||||
|
||||
if (self->p->complete_fn) {
|
||||
self->p->complete_fn(res, self->p->current_args_set);
|
||||
self->p->complete_fn();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -179,5 +180,20 @@ void AvrDude::join()
|
|||
}
|
||||
}
|
||||
|
||||
bool AvrDude::cancelled()
|
||||
{
|
||||
return p ? p->cancelled : false;
|
||||
}
|
||||
|
||||
int AvrDude::exit_code()
|
||||
{
|
||||
return p ? p->exit_code : 0;
|
||||
}
|
||||
|
||||
size_t AvrDude::last_args_set()
|
||||
{
|
||||
return p ? p->current_args_set : 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -12,10 +12,10 @@ class AvrDude
|
|||
{
|
||||
public:
|
||||
typedef std::shared_ptr<AvrDude> Ptr;
|
||||
typedef std::function<void(AvrDude&)> RunFn;
|
||||
typedef std::function<void()> RunFn;
|
||||
typedef std::function<void(const char * /* msg */, unsigned /* size */)> MessageFn;
|
||||
typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn;
|
||||
typedef std::function<void(int /* exit status */, size_t /* args_id */)> CompleteFn;
|
||||
typedef std::function<void()> CompleteFn;
|
||||
|
||||
// Main c-tor, sys_config is the location of avrdude's main configuration file
|
||||
AvrDude(std::string sys_config);
|
||||
|
@ -54,6 +54,10 @@ public:
|
|||
|
||||
void cancel();
|
||||
void join();
|
||||
|
||||
bool cancelled(); // Whether avrdude run was cancelled
|
||||
int exit_code(); // The exit code of the last invocation
|
||||
size_t last_args_set(); // Index of the last argument set that was processsed
|
||||
private:
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
|
|
|
@ -608,15 +608,18 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Find tool ordering for all the objects at once, and the initial extruder ID.
|
||||
// If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
|
||||
tool_ordering = print.m_tool_ordering.empty() ?
|
||||
ToolOrdering(print, initial_extruder_id) :
|
||||
print.m_tool_ordering;
|
||||
initial_extruder_id = tool_ordering.first_extruder();
|
||||
has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
|
||||
initial_extruder_id = (has_wipe_tower && ! print.config.single_extruder_multi_material_priming) ?
|
||||
// The priming towers will be skipped.
|
||||
tool_ordering.all_extruders().back() :
|
||||
// Don't skip the priming towers.
|
||||
tool_ordering.first_extruder();
|
||||
}
|
||||
if (initial_extruder_id == (unsigned int)-1) {
|
||||
// Nothing to print!
|
||||
|
@ -644,6 +647,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
m_placeholder_parser.set("current_object_idx", 0);
|
||||
// For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided.
|
||||
m_placeholder_parser.set("has_wipe_tower", has_wipe_tower);
|
||||
m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config.single_extruder_multi_material_priming);
|
||||
std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config.start_gcode.value, initial_extruder_id);
|
||||
|
||||
// Set bed temperature if the start G-code does not contain any bed temp control G-codes.
|
||||
|
@ -724,8 +728,11 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
}
|
||||
}
|
||||
|
||||
// Set initial extruder only after custom start G-code.
|
||||
_write(file, this->set_extruder(initial_extruder_id));
|
||||
if (! (has_wipe_tower && print.config.single_extruder_multi_material_priming)) {
|
||||
// Set initial extruder only after custom start G-code.
|
||||
// Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
|
||||
_write(file, this->set_extruder(initial_extruder_id));
|
||||
}
|
||||
|
||||
// Do all objects for each layer.
|
||||
if (print.config.complete_objects.value) {
|
||||
|
@ -803,27 +810,29 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
if (has_wipe_tower && ! layers_to_print.empty()) {
|
||||
m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get()));
|
||||
_write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
|
||||
_write(file, m_wipe_tower->prime(*this));
|
||||
// Verify, whether the print overaps the priming extrusions.
|
||||
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
|
||||
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
|
||||
for (const PrintObject *print_object : printable_objects)
|
||||
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
|
||||
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
|
||||
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
|
||||
bbox_prime.offset(0.5f);
|
||||
// Beep for 500ms, tone 800Hz. Yet better, play some Morse.
|
||||
_write(file, this->retract());
|
||||
_write(file, "M300 S800 P500\n");
|
||||
if (bbox_prime.overlap(bbox_print)) {
|
||||
// Wait for the user to remove the priming extrusions, otherwise they would
|
||||
// get covered by the print.
|
||||
_write(file, "M1 Remove priming towers and click button.\n");
|
||||
}
|
||||
else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
_write(file, "M1 S10\n");
|
||||
if (print.config.single_extruder_multi_material_priming) {
|
||||
_write(file, m_wipe_tower->prime(*this));
|
||||
// Verify, whether the print overaps the priming extrusions.
|
||||
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
|
||||
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
|
||||
for (const PrintObject *print_object : printable_objects)
|
||||
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
|
||||
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
|
||||
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
|
||||
bbox_prime.offset(0.5f);
|
||||
// Beep for 500ms, tone 800Hz. Yet better, play some Morse.
|
||||
_write(file, this->retract());
|
||||
_write(file, "M300 S800 P500\n");
|
||||
if (bbox_prime.overlap(bbox_print)) {
|
||||
// Wait for the user to remove the priming extrusions, otherwise they would
|
||||
// get covered by the print.
|
||||
_write(file, "M1 Remove priming towers and click button.\n");
|
||||
}
|
||||
else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
_write(file, "M1 S10\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Extrude the layers.
|
||||
|
|
|
@ -771,9 +771,23 @@ void ModelObject::scale(const Pointf3 &versor)
|
|||
|
||||
void ModelObject::rotate(float angle, const Axis &axis)
|
||||
{
|
||||
float min_z = FLT_MAX;
|
||||
for (ModelVolume *v : this->volumes)
|
||||
{
|
||||
v->mesh.rotate(angle, axis);
|
||||
this->origin_translation = Pointf3(0,0,0);
|
||||
min_z = std::min(min_z, v->mesh.stl.stats.min.z);
|
||||
}
|
||||
|
||||
if (min_z != 0.0f)
|
||||
{
|
||||
// translate the object so that its minimum z lays on the bed
|
||||
for (ModelVolume *v : this->volumes)
|
||||
{
|
||||
v->mesh.translate(0.0f, 0.0f, -min_z);
|
||||
}
|
||||
}
|
||||
|
||||
this->origin_translation = Pointf3(0, 0, 0);
|
||||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
|
|
|
@ -155,6 +155,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||
"retract_restart_extra",
|
||||
"retract_restart_extra_toolchange",
|
||||
"retract_speed",
|
||||
"single_extruder_multi_material_priming",
|
||||
"slowdown_below_layer_time",
|
||||
"standby_temperature_delta",
|
||||
"start_gcode",
|
||||
|
@ -1183,10 +1184,6 @@ void Print::_make_wipe_tower()
|
|||
wipe_tower.tool_change((unsigned int)-1, false));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::string Print::output_filename()
|
||||
{
|
||||
this->placeholder_parser.update_timestamp();
|
||||
|
@ -1225,7 +1222,6 @@ void Print::set_status(int percent, const std::string &message)
|
|||
printf("Print::status %d => %s\n", percent, message.c_str());
|
||||
}
|
||||
|
||||
|
||||
// Returns extruder this eec should be printed with, according to PrintRegion config
|
||||
int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion)
|
||||
{
|
||||
|
@ -1233,5 +1229,4 @@ int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion
|
|||
std::max<int>(region.config.perimeter_extruder.value - 1, 0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1623,6 +1623,12 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->cli = "single-extruder-multi-material!";
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("single_extruder_multi_material_priming", coBool);
|
||||
def->label = L("Prime all printing extruders");
|
||||
def->tooltip = L("If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print.");
|
||||
def->cli = "single-extruder-multi-material-priming!";
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("support_material", coBool);
|
||||
def->label = L("Generate support material");
|
||||
def->category = L("Support material");
|
||||
|
|
|
@ -553,6 +553,7 @@ public:
|
|||
ConfigOptionString start_gcode;
|
||||
ConfigOptionStrings start_filament_gcode;
|
||||
ConfigOptionBool single_extruder_multi_material;
|
||||
ConfigOptionBool single_extruder_multi_material_priming;
|
||||
ConfigOptionString toolchange_gcode;
|
||||
ConfigOptionFloat travel_speed;
|
||||
ConfigOptionBool use_firmware_retraction;
|
||||
|
@ -612,6 +613,7 @@ protected:
|
|||
OPT_PTR(retract_restart_extra_toolchange);
|
||||
OPT_PTR(retract_speed);
|
||||
OPT_PTR(single_extruder_multi_material);
|
||||
OPT_PTR(single_extruder_multi_material_priming);
|
||||
OPT_PTR(start_gcode);
|
||||
OPT_PTR(start_filament_gcode);
|
||||
OPT_PTR(toolchange_gcode);
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include "Model.hpp"
|
||||
#include "boost/nowide/iostream.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
@ -146,21 +148,18 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points)
|
|||
if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) {
|
||||
// okay, it's a rectangle
|
||||
// find origin
|
||||
// the || 0 hack prevents "-0" which might confuse the user
|
||||
int x_min, x_max, y_min, y_max;
|
||||
x_max = x_min = points->values[0].x;
|
||||
coordf_t x_min, x_max, y_min, y_max;
|
||||
x_max = x_min = points->values[0].x;
|
||||
y_max = y_min = points->values[0].y;
|
||||
for (auto pt : points->values){
|
||||
if (x_min > pt.x) x_min = pt.x;
|
||||
if (x_max < pt.x) x_max = pt.x;
|
||||
if (y_min > pt.y) y_min = pt.y;
|
||||
if (y_max < pt.y) y_max = pt.y;
|
||||
}
|
||||
if (x_min < 0) x_min = 0;
|
||||
if (x_max < 0) x_max = 0;
|
||||
if (y_min < 0) y_min = 0;
|
||||
if (y_max < 0) y_max = 0;
|
||||
auto origin = new ConfigOptionPoints{ Pointf(-x_min, -y_min) };
|
||||
for (auto pt : points->values)
|
||||
{
|
||||
x_min = std::min(x_min, pt.x);
|
||||
x_max = std::max(x_max, pt.x);
|
||||
y_min = std::min(y_min, pt.y);
|
||||
y_max = std::max(y_max, pt.y);
|
||||
}
|
||||
|
||||
auto origin = new ConfigOptionPoints{ Pointf(-x_min, -y_min) };
|
||||
|
||||
m_shape_options_book->SetSelection(SHAPE_RECTANGULAR);
|
||||
auto optgroup = m_optgroups[SHAPE_RECTANGULAR];
|
||||
|
|
|
@ -95,9 +95,10 @@ namespace Slic3r { namespace GUI {
|
|||
wxString tooltip_text("");
|
||||
wxString tooltip = _(m_opt.tooltip);
|
||||
if (tooltip.length() > 0)
|
||||
tooltip_text = tooltip + "(" + _(L("default")) + ": " +
|
||||
(boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") +
|
||||
default_string + ")";
|
||||
tooltip_text = tooltip + "\n " + _(L("default value")) + "\t: " +
|
||||
(boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") + default_string +
|
||||
(boost::iends_with(m_opt_id, "_gcode") ? "" : "\n") + "\n " +
|
||||
_(L("variable name")) + "\t: " + m_opt_id;
|
||||
|
||||
return tooltip_text;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
#include <numeric>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
#include <stdexcept>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "avrdude/avrdude-slic3r.hpp"
|
||||
|
@ -32,11 +34,13 @@
|
|||
#include <wx/gauge.h>
|
||||
#include <wx/collpane.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/filefn.h>
|
||||
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace asio = boost::asio;
|
||||
using boost::system::error_code;
|
||||
using boost::optional;
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -46,19 +50,31 @@ using Utils::SerialPortInfo;
|
|||
using Utils::Serial;
|
||||
|
||||
|
||||
// USB IDs used to perform device lookup
|
||||
enum {
|
||||
USB_VID_PRUSA = 0x2c99,
|
||||
USB_PID_MK2 = 1,
|
||||
USB_PID_MK3 = 2,
|
||||
USB_PID_MMU_BOOT = 3,
|
||||
USB_PID_MMU_APP = 4,
|
||||
};
|
||||
|
||||
// This enum discriminates the kind of information in EVT_AVRDUDE,
|
||||
// it's stored in the ExtraLong field of wxCommandEvent.
|
||||
enum AvrdudeEvent
|
||||
{
|
||||
AE_MESSAGE,
|
||||
AE_PROGRESS,
|
||||
AE_STATUS,
|
||||
AE_EXIT,
|
||||
AE_ERROR,
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(EVT_AVRDUDE, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_AVRDUDE, wxCommandEvent);
|
||||
|
||||
wxDECLARE_EVENT(EVT_ASYNC_DIALOG, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_ASYNC_DIALOG, wxCommandEvent);
|
||||
|
||||
|
||||
// Private
|
||||
|
||||
|
@ -66,15 +82,17 @@ struct FirmwareDialog::priv
|
|||
{
|
||||
enum AvrDudeComplete
|
||||
{
|
||||
AC_NONE,
|
||||
AC_SUCCESS,
|
||||
AC_FAILURE,
|
||||
AC_CANCEL,
|
||||
AC_USER_CANCELLED,
|
||||
};
|
||||
|
||||
FirmwareDialog *q; // PIMPL back pointer ("Q-Pointer")
|
||||
|
||||
// GUI elements
|
||||
wxComboBox *port_picker;
|
||||
std::vector<SerialPortInfo> ports;
|
||||
wxStaticText *port_autodetect;
|
||||
wxFilePickerCtrl *hex_picker;
|
||||
wxStaticText *txt_status;
|
||||
wxGauge *progressbar;
|
||||
|
@ -85,43 +103,66 @@ struct FirmwareDialog::priv
|
|||
wxButton *btn_flash;
|
||||
wxString btn_flash_label_ready;
|
||||
wxString btn_flash_label_flashing;
|
||||
wxString label_status_flashing;
|
||||
|
||||
wxTimer timer_pulse;
|
||||
|
||||
// Async modal dialog during flashing
|
||||
std::mutex mutex;
|
||||
int modal_response;
|
||||
std::condition_variable response_cv;
|
||||
|
||||
// Data
|
||||
std::vector<SerialPortInfo> ports;
|
||||
optional<SerialPortInfo> port;
|
||||
HexFile hex_file;
|
||||
|
||||
// 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;
|
||||
unsigned progress_tasks_bar;
|
||||
bool cancelled;
|
||||
bool user_cancelled;
|
||||
const bool extra_verbose; // For debugging
|
||||
|
||||
priv(FirmwareDialog *q) :
|
||||
q(q),
|
||||
btn_flash_label_ready(_(L("Flash!"))),
|
||||
btn_flash_label_flashing(_(L("Cancel"))),
|
||||
label_status_flashing(_(L("Flashing in progress. Please do not disconnect the printer!"))),
|
||||
timer_pulse(q),
|
||||
avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()),
|
||||
progress_tasks_done(0),
|
||||
progress_tasks_bar(0),
|
||||
cancelled(false),
|
||||
user_cancelled(false),
|
||||
extra_verbose(false)
|
||||
{}
|
||||
|
||||
void find_serial_ports();
|
||||
void fit_no_shrink();
|
||||
void set_txt_status(const wxString &label);
|
||||
void flashing_start(unsigned tasks);
|
||||
void flashing_done(AvrDudeComplete complete);
|
||||
void check_model_id(const HexFile &metadata, const SerialPortInfo &port);
|
||||
void enable_port_picker(bool enable);
|
||||
void load_hex_file(const wxString &path);
|
||||
void queue_status(wxString message);
|
||||
void queue_error(const wxString &message);
|
||||
|
||||
void prepare_common(AvrDude &, const SerialPortInfo &port, const std::string &filename);
|
||||
void prepare_mk2(AvrDude &, const SerialPortInfo &port, const std::string &filename);
|
||||
void prepare_mk3(AvrDude &, const SerialPortInfo &port, const std::string &filename);
|
||||
void prepare_mm_control(AvrDude &, const SerialPortInfo &port, const std::string &filename);
|
||||
bool ask_model_id_mismatch(const std::string &printer_model);
|
||||
bool check_model_id();
|
||||
void wait_for_mmu_bootloader(unsigned retries);
|
||||
void mmu_reboot(const SerialPortInfo &port);
|
||||
void lookup_port_mmu();
|
||||
void prepare_common();
|
||||
void prepare_mk2();
|
||||
void prepare_mk3();
|
||||
void prepare_mm_control();
|
||||
void perform_upload();
|
||||
|
||||
void cancel();
|
||||
void user_cancel();
|
||||
void on_avrdude(const wxCommandEvent &evt);
|
||||
void on_async_dialog(const wxCommandEvent &evt);
|
||||
void ensure_joined();
|
||||
};
|
||||
|
||||
|
@ -146,10 +187,32 @@ void FirmwareDialog::priv::find_serial_ports()
|
|||
}
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::fit_no_shrink()
|
||||
{
|
||||
// Ensure content fits into window and window is not shrinked
|
||||
const auto old_size = q->GetSize();
|
||||
q->Layout();
|
||||
q->Fit();
|
||||
const auto new_size = q->GetSize();
|
||||
const auto new_width = std::max(old_size.GetWidth(), new_size.GetWidth());
|
||||
const auto new_height = std::max(old_size.GetHeight(), new_size.GetHeight());
|
||||
q->SetSize(new_width, new_height);
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::set_txt_status(const wxString &label)
|
||||
{
|
||||
const auto width = txt_status->GetSize().GetWidth();
|
||||
txt_status->SetLabel(label);
|
||||
txt_status->Wrap(width);
|
||||
|
||||
fit_no_shrink();
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::flashing_start(unsigned tasks)
|
||||
{
|
||||
modal_response = wxID_NONE;
|
||||
txt_stdout->Clear();
|
||||
txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!")));
|
||||
set_txt_status(label_status_flashing);
|
||||
txt_status->SetForegroundColour(GUI::get_label_clr_modified());
|
||||
port_picker->Disable();
|
||||
btn_rescan->Disable();
|
||||
|
@ -160,7 +223,7 @@ void FirmwareDialog::priv::flashing_start(unsigned tasks)
|
|||
progressbar->SetValue(0);
|
||||
progress_tasks_done = 0;
|
||||
progress_tasks_bar = 0;
|
||||
cancelled = false;
|
||||
user_cancelled = false;
|
||||
timer_pulse.Start(50);
|
||||
}
|
||||
|
||||
|
@ -177,54 +240,190 @@ void FirmwareDialog::priv::flashing_done(AvrDudeComplete complete)
|
|||
progressbar->SetValue(progressbar->GetRange());
|
||||
|
||||
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;
|
||||
case AC_SUCCESS: set_txt_status(_(L("Flashing succeeded!"))); break;
|
||||
case AC_FAILURE: set_txt_status(_(L("Flashing failed. Please see the avrdude log below."))); break;
|
||||
case AC_USER_CANCELLED: set_txt_status(_(L("Flashing cancelled."))); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::check_model_id(const HexFile &metadata, const SerialPortInfo &port)
|
||||
void FirmwareDialog::priv::enable_port_picker(bool enable)
|
||||
{
|
||||
if (metadata.model_id.empty()) {
|
||||
// No data to check against
|
||||
return;
|
||||
port_picker->Show(enable);
|
||||
btn_rescan->Show(enable);
|
||||
port_autodetect->Show(! enable);
|
||||
q->Layout();
|
||||
fit_no_shrink();
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::load_hex_file(const wxString &path)
|
||||
{
|
||||
hex_file = HexFile(path.wx_str());
|
||||
enable_port_picker(hex_file.device != HexFile::DEV_MM_CONTROL);
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::queue_status(wxString message)
|
||||
{
|
||||
auto evt = new wxCommandEvent(EVT_AVRDUDE, this->q->GetId());
|
||||
evt->SetExtraLong(AE_STATUS);
|
||||
evt->SetString(std::move(message));
|
||||
wxQueueEvent(this->q, evt);
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::queue_error(const wxString &message)
|
||||
{
|
||||
auto evt = new wxCommandEvent(EVT_AVRDUDE, this->q->GetId());
|
||||
evt->SetExtraLong(AE_STATUS);
|
||||
evt->SetString(wxString::Format(_(L("Flashing failed: %s")), message));
|
||||
|
||||
wxQueueEvent(this->q, evt); avrdude->cancel();
|
||||
}
|
||||
|
||||
bool FirmwareDialog::priv::ask_model_id_mismatch(const std::string &printer_model)
|
||||
{
|
||||
// model_id in the hex file doesn't match what the printer repoted.
|
||||
// Ask the user if it should be flashed anyway.
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
||||
auto evt = new wxCommandEvent(EVT_ASYNC_DIALOG, this->q->GetId());
|
||||
evt->SetString(wxString::Format(_(L(
|
||||
"This firmware hex file does not match the printer model.\n"
|
||||
"The hex file is intended for: %s\n"
|
||||
"Printer reported: %s\n\n"
|
||||
"Do you want to continue and flash this hex file anyway?\n"
|
||||
"Please only continue if you are sure this is the right thing to do.")),
|
||||
hex_file.model_id, printer_model
|
||||
));
|
||||
wxQueueEvent(this->q, evt);
|
||||
|
||||
response_cv.wait(lock, [this]() { return this->modal_response != wxID_NONE; });
|
||||
|
||||
if (modal_response == wxID_YES) {
|
||||
return true;
|
||||
} else {
|
||||
user_cancel();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
asio::io_service io;
|
||||
Serial serial(io, port.port, 115200);
|
||||
serial.printer_setup();
|
||||
bool FirmwareDialog::priv::check_model_id()
|
||||
{
|
||||
// XXX: The implementation in Serial doesn't currently work reliably enough to be used.
|
||||
// Therefore, regretably, so far the check cannot be used and we just return true here.
|
||||
// TODO: Rewrite Serial using more platform-native code.
|
||||
return true;
|
||||
|
||||
// if (hex_file.model_id.empty()) {
|
||||
// // No data to check against, assume it's ok
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// asio::io_service io;
|
||||
// Serial serial(io, port->port, 115200);
|
||||
// serial.printer_setup();
|
||||
|
||||
// enum {
|
||||
// TIMEOUT = 2000,
|
||||
// RETREIES = 5,
|
||||
// };
|
||||
|
||||
// if (! serial.printer_ready_wait(RETREIES, TIMEOUT)) {
|
||||
// queue_error(wxString::Format(_(L("Could not connect to the printer at %s")), port->port));
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// std::string line;
|
||||
// error_code ec;
|
||||
// serial.printer_write_line("PRUSA Rev");
|
||||
// while (serial.read_line(TIMEOUT, line, ec)) {
|
||||
// if (ec) {
|
||||
// queue_error(wxString::Format(_(L("Could not connect to the printer at %s")), port->port));
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// if (line == "ok") { continue; }
|
||||
|
||||
// if (line == hex_file.model_id) {
|
||||
// return true;
|
||||
// } else {
|
||||
// return ask_model_id_mismatch(line);
|
||||
// }
|
||||
|
||||
// line.clear();
|
||||
// }
|
||||
|
||||
// return false;
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::wait_for_mmu_bootloader(unsigned retries)
|
||||
{
|
||||
enum {
|
||||
TIMEOUT = 1000,
|
||||
RETREIES = 3,
|
||||
SLEEP_MS = 500,
|
||||
};
|
||||
|
||||
if (! serial.printer_ready_wait(RETREIES, TIMEOUT)) {
|
||||
throw wxString::Format(_(L("Could not connect to the printer at %s")), port.port);
|
||||
}
|
||||
for (unsigned i = 0; i < retries && !user_cancelled; i++) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_MS));
|
||||
|
||||
std::string line;
|
||||
error_code ec;
|
||||
serial.printer_write_line("PRUSA Rev");
|
||||
while (serial.read_line(TIMEOUT, line, ec)) {
|
||||
if (ec) { throw wxString::Format(_(L("Could not connect to the printer at %s")), port.port); }
|
||||
if (line == "ok") { continue; }
|
||||
auto ports = Utils::scan_serial_ports_extended();
|
||||
ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
|
||||
return port.id_vendor != USB_VID_PRUSA && port.id_product != USB_PID_MMU_BOOT;
|
||||
}), ports.end());
|
||||
|
||||
if (line == metadata.model_id) {
|
||||
if (ports.size() == 1) {
|
||||
port = ports[0];
|
||||
return;
|
||||
} else if (ports.size() > 1) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found";
|
||||
queue_error(_(L("Multiple Original Prusa i3 MMU 2.0 devices found. Please only connect one at a time for flashing.")));
|
||||
return;
|
||||
} else {
|
||||
throw wxString::Format(_(L(
|
||||
"The firmware hex file does not match the printer model.\n"
|
||||
"The hex file is intended for:\n %s\n"
|
||||
"Printer reports:\n %s"
|
||||
)), metadata.model_id, line);
|
||||
}
|
||||
|
||||
line.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::prepare_common(AvrDude &avrdude, const SerialPortInfo &port, const std::string &filename)
|
||||
void FirmwareDialog::priv::mmu_reboot(const SerialPortInfo &port)
|
||||
{
|
||||
asio::io_service io;
|
||||
Serial serial(io, port.port, 1200);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::lookup_port_mmu()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Flashing MMU 2.0, looking for VID/PID 0x2c99/3 or 0x2c99/4 ...";
|
||||
|
||||
auto ports = Utils::scan_serial_ports_extended();
|
||||
ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
|
||||
return port.id_vendor != USB_VID_PRUSA &&
|
||||
port.id_product != USB_PID_MMU_BOOT &&
|
||||
port.id_product != USB_PID_MMU_APP;
|
||||
}), ports.end());
|
||||
|
||||
if (ports.size() == 0) {
|
||||
BOOST_LOG_TRIVIAL(info) << "MMU 2.0 device not found, asking the user to press Reset and waiting for the device to show up ...";
|
||||
|
||||
queue_status(_(L(
|
||||
"The Multi Material Control device was not found.\n"
|
||||
"If the device is connected, please press the Reset button next to the USB connector ..."
|
||||
)));
|
||||
|
||||
wait_for_mmu_bootloader(30);
|
||||
} else if (ports.size() > 1) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found";
|
||||
queue_error(_(L("Multiple Original Prusa i3 MMU 2.0 devices found. Please only connect one at a time for flashing.")));
|
||||
} else {
|
||||
if (ports[0].id_product == USB_PID_MMU_APP) {
|
||||
// The device needs to be rebooted into the bootloader mode
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/4 at `%1%`, rebooting the device ...") % ports[0].port;
|
||||
mmu_reboot(ports[0]);
|
||||
wait_for_mmu_bootloader(10);
|
||||
} else {
|
||||
port = ports[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::prepare_common()
|
||||
{
|
||||
std::vector<std::string> args {{
|
||||
extra_verbose ? "-vvvvv" : "-v",
|
||||
|
@ -233,10 +432,10 @@ void FirmwareDialog::priv::prepare_common(AvrDude &avrdude, const SerialPortInfo
|
|||
// The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip
|
||||
// is flashed with a buggy firmware.
|
||||
"-c", "wiring",
|
||||
"-P", port.port,
|
||||
"-b", "115200", // TODO: Allow other rates? Ditto below.
|
||||
"-P", port->port,
|
||||
"-b", "115200", // TODO: Allow other rates? Ditto elsewhere.
|
||||
"-D",
|
||||
"-U", (boost::format("flash:w:0:%1%:i") % filename).str(),
|
||||
"-U", (boost::format("flash:w:0:%1%:i") % hex_file.path.string()).str(),
|
||||
}};
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
|
||||
|
@ -244,97 +443,75 @@ void FirmwareDialog::priv::prepare_common(AvrDude &avrdude, const SerialPortInfo
|
|||
return a + ' ' + b;
|
||||
});
|
||||
|
||||
avrdude.push_args(std::move(args));
|
||||
avrdude->push_args(std::move(args));
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::prepare_mk2(AvrDude &avrdude, const SerialPortInfo &port, const std::string &filename)
|
||||
void FirmwareDialog::priv::prepare_mk2()
|
||||
{
|
||||
prepare_common(avrdude, port, filename);
|
||||
if (! port) { return; }
|
||||
|
||||
if (! check_model_id()) {
|
||||
avrdude->cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
prepare_common();
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::prepare_mk3(AvrDude &avrdude, const SerialPortInfo &port, const std::string &filename)
|
||||
void FirmwareDialog::priv::prepare_mk3()
|
||||
{
|
||||
prepare_common(avrdude, port, filename);
|
||||
if (! port) { return; }
|
||||
|
||||
if (! check_model_id()) {
|
||||
avrdude->cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
prepare_common();
|
||||
|
||||
// The hex file also contains another section with l10n data to be flashed into the external flash on MK3 (Einsy)
|
||||
// This is done via another avrdude invocation, here we build arg list for that:
|
||||
std::vector<std::string> args_l10n {{
|
||||
std::vector<std::string> args {{
|
||||
extra_verbose ? "-vvvvv" : "-v",
|
||||
"-p", "atmega2560",
|
||||
// Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2).
|
||||
// The Prusa's avrdude is patched again to never send semicolons inside the data packets.
|
||||
"-c", "arduino",
|
||||
"-P", port.port,
|
||||
"-P", port->port,
|
||||
"-b", "115200",
|
||||
"-D",
|
||||
"-u", // disable safe mode
|
||||
"-U", (boost::format("flash:w:1:%1%:i") % filename).str(),
|
||||
"-U", (boost::format("flash:w:1:%1%:i") % hex_file.path.string()).str(),
|
||||
}};
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude for external flash flashing, arguments: "
|
||||
<< std::accumulate(std::next(args_l10n.begin()), args_l10n.end(), args_l10n[0], [](std::string a, const std::string &b) {
|
||||
<< std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, const std::string &b) {
|
||||
return a + ' ' + b;
|
||||
});
|
||||
|
||||
avrdude.push_args(std::move(args_l10n));
|
||||
avrdude->push_args(std::move(args));
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::prepare_mm_control(AvrDude &avrdude, const SerialPortInfo &port_in, const std::string &filename)
|
||||
void FirmwareDialog::priv::prepare_mm_control()
|
||||
{
|
||||
// Check if the port has the PID/VID of 0x2c99/3
|
||||
// If not, check if it is the MMU (0x2c99/4) and reboot the by opening @ 1200 bauds
|
||||
BOOST_LOG_TRIVIAL(info) << "Flashing MMU 2.0, looking for VID/PID 0x2c99/3 or 0x2c99/4 ...";
|
||||
SerialPortInfo port = port_in;
|
||||
if (! port.id_match(0x2c99, 3)) {
|
||||
if (! port.id_match(0x2c99, 4)) {
|
||||
// This is not a Prusa MMU 2.0 device
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Not a Prusa MMU 2.0 device: `%1%`") % port.port;
|
||||
throw wxString::Format(_(L("The device at `%s` is not am Original Prusa i3 MMU 2.0 device")), port.port);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/4 at `%1%`, rebooting the device ...") % port.port;
|
||||
|
||||
{
|
||||
asio::io_service io;
|
||||
Serial serial(io, port.port, 1200);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
|
||||
// Wait for the bootloader to show up
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
|
||||
// Look for the rebooted device
|
||||
BOOST_LOG_TRIVIAL(info) << "... looking for VID/PID 0x2c99/3 ...";
|
||||
auto new_ports = Utils::scan_serial_ports_extended();
|
||||
unsigned hits = 0;
|
||||
for (auto &&new_port : new_ports) {
|
||||
if (new_port.id_match(0x2c99, 3)) {
|
||||
hits++;
|
||||
port = std::move(new_port);
|
||||
}
|
||||
}
|
||||
|
||||
if (hits == 0) {
|
||||
BOOST_LOG_TRIVIAL(error) << "No VID/PID 0x2c99/3 device found after rebooting the MMU 2.0";
|
||||
throw wxString::Format(_(L("Failed to reboot the device at `%s` for programming")), port.port);
|
||||
} else if (hits > 1) {
|
||||
// We found multiple 0x2c99/3 devices, this is bad, because there's no way to find out
|
||||
// which one is the one user wants to flash.
|
||||
BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found after rebooting the MMU 2.0";
|
||||
throw wxString::Format(_(L("Multiple Original Prusa i3 MMU 2.0 devices found. Please only connect one at a time for flashing.")), port.port);
|
||||
}
|
||||
port = boost::none;
|
||||
lookup_port_mmu();
|
||||
if (! port) {
|
||||
queue_error(_(L("The device could not have been found")));
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/3 at `%1%`, flashing ...") % port.port;
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/3 at `%1%`, flashing ...") % port->port;
|
||||
queue_status(label_status_flashing);
|
||||
|
||||
std::vector<std::string> args {{
|
||||
extra_verbose ? "-vvvvv" : "-v",
|
||||
"-p", "atmega32u4",
|
||||
"-c", "avr109",
|
||||
"-P", port.port,
|
||||
"-P", port->port,
|
||||
"-b", "57600",
|
||||
"-D",
|
||||
"-U", (boost::format("flash:w:0:%1%:i") % filename).str(),
|
||||
"-U", (boost::format("flash:w:0:%1%:i") % hex_file.path.string()).str(),
|
||||
}};
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
|
||||
|
@ -342,7 +519,7 @@ void FirmwareDialog::priv::prepare_mm_control(AvrDude &avrdude, const SerialPort
|
|||
return a + ' ' + b;
|
||||
});
|
||||
|
||||
avrdude.push_args(std::move(args));
|
||||
avrdude->push_args(std::move(args));
|
||||
}
|
||||
|
||||
|
||||
|
@ -351,20 +528,21 @@ void FirmwareDialog::priv::perform_upload()
|
|||
auto filename = hex_picker->GetPath();
|
||||
if (filename.IsEmpty()) { return; }
|
||||
|
||||
int selection = port_picker->GetSelection();
|
||||
if (selection == wxNOT_FOUND) { return; }
|
||||
load_hex_file(filename); // Might already be loaded, but we want to make sure it's fresh
|
||||
|
||||
const SerialPortInfo &port = this->ports[selection];
|
||||
// Verify whether the combo box list selection equals to the combo box edit value.
|
||||
if (wxString::FromUTF8(this->ports[selection].friendly_name.data()) != port_picker->GetValue()) {
|
||||
return;
|
||||
int selection = port_picker->GetSelection();
|
||||
if (selection != wxNOT_FOUND) {
|
||||
port = this->ports[selection];
|
||||
|
||||
// Verify whether the combo box list selection equals to the combo box edit value.
|
||||
if (wxString::FromUTF8(port->friendly_name.data()) != port_picker->GetValue()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const bool extra_verbose = false; // For debugging
|
||||
HexFile metadata(filename.wx_str());
|
||||
const std::string filename_utf8(filename.utf8_str().data());
|
||||
|
||||
flashing_start(metadata.device == HexFile::DEV_MK3 ? 2 : 1);
|
||||
flashing_start(hex_file.device == HexFile::DEV_MK3 ? 2 : 1);
|
||||
|
||||
// Init the avrdude object
|
||||
AvrDude avrdude(avrdude_config);
|
||||
|
@ -374,36 +552,23 @@ void FirmwareDialog::priv::perform_upload()
|
|||
auto q = this->q;
|
||||
|
||||
this->avrdude = avrdude
|
||||
.on_run([=](AvrDude &avrdude) {
|
||||
auto queue_error = [&](wxString message) {
|
||||
avrdude.cancel();
|
||||
|
||||
auto evt = new wxCommandEvent(EVT_AVRDUDE, this->q->GetId());
|
||||
evt->SetExtraLong(AE_ERROR);
|
||||
evt->SetString(std::move(message));
|
||||
wxQueueEvent(this->q, evt);
|
||||
};
|
||||
|
||||
.on_run([this]() {
|
||||
try {
|
||||
switch (metadata.device) {
|
||||
switch (this->hex_file.device) {
|
||||
case HexFile::DEV_MK3:
|
||||
this->check_model_id(metadata, port);
|
||||
this->prepare_mk3(avrdude, port, filename_utf8);
|
||||
this->prepare_mk3();
|
||||
break;
|
||||
|
||||
case HexFile::DEV_MM_CONTROL:
|
||||
this->check_model_id(metadata, port);
|
||||
this->prepare_mm_control(avrdude, port, filename_utf8);
|
||||
this->prepare_mm_control();
|
||||
break;
|
||||
|
||||
default:
|
||||
this->prepare_mk2(avrdude, port, filename_utf8);
|
||||
this->prepare_mk2();
|
||||
break;
|
||||
}
|
||||
} catch (const wxString &message) {
|
||||
queue_error(message);
|
||||
} catch (const std::exception &ex) {
|
||||
queue_error(wxString::Format(_(L("Error accessing port at %s: %s")), port.port, ex.what()));
|
||||
queue_error(wxString::Format(_(L("Error accessing port at %s: %s")), port->port, ex.what()));
|
||||
}
|
||||
})
|
||||
.on_message(std::move([q, extra_verbose](const char *msg, unsigned /* size */) {
|
||||
|
@ -423,20 +588,19 @@ void FirmwareDialog::priv::perform_upload()
|
|||
evt->SetInt(progress);
|
||||
wxQueueEvent(q, evt);
|
||||
}))
|
||||
.on_complete(std::move([q](int status, size_t /* args_id */) {
|
||||
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
|
||||
.on_complete(std::move([this]() {
|
||||
auto evt = new wxCommandEvent(EVT_AVRDUDE, this->q->GetId());
|
||||
evt->SetExtraLong(AE_EXIT);
|
||||
evt->SetInt(status);
|
||||
wxQueueEvent(q, evt);
|
||||
evt->SetInt(this->avrdude->exit_code());
|
||||
wxQueueEvent(this->q, evt);
|
||||
}))
|
||||
.run();
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::cancel()
|
||||
void FirmwareDialog::priv::user_cancel()
|
||||
{
|
||||
if (avrdude) {
|
||||
cancelled = true;
|
||||
txt_status->SetLabel(_(L("Cancelling...")));
|
||||
user_cancelled = true;
|
||||
avrdude->cancel();
|
||||
}
|
||||
}
|
||||
|
@ -474,19 +638,17 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
|
|||
case AE_EXIT:
|
||||
BOOST_LOG_TRIVIAL(info) << "avrdude exit code: " << evt.GetInt();
|
||||
|
||||
complete_kind = cancelled ? AC_CANCEL : (evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE);
|
||||
// Figure out the exit state
|
||||
if (user_cancelled) { complete_kind = AC_USER_CANCELLED; }
|
||||
else if (avrdude->cancelled()) { complete_kind = AC_NONE; } // Ie. cancelled programatically
|
||||
else { complete_kind = evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE; }
|
||||
|
||||
flashing_done(complete_kind);
|
||||
ensure_joined();
|
||||
break;
|
||||
|
||||
case AE_ERROR:
|
||||
txt_stdout->AppendText(evt.GetString());
|
||||
flashing_done(AC_FAILURE);
|
||||
ensure_joined();
|
||||
{
|
||||
GUI::ErrorDialog dlg(this->q, evt.GetString());
|
||||
dlg.ShowModal();
|
||||
}
|
||||
case AE_STATUS:
|
||||
set_txt_status(evt.GetString());
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -494,6 +656,16 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
|
|||
}
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::on_async_dialog(const wxCommandEvent &evt)
|
||||
{
|
||||
wxMessageDialog dlg(this->q, evt.GetString(), wxMessageBoxCaptionStr, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
modal_response = dlg.ShowModal();
|
||||
}
|
||||
response_cv.notify_all();
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::ensure_joined()
|
||||
{
|
||||
// Make sure the background thread is collected and the AvrDude object reset
|
||||
|
@ -521,44 +693,50 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
|
|||
wxFont mono_font(wxFontInfo().Family(wxFONTFAMILY_TELETYPE));
|
||||
mono_font.MakeSmaller();
|
||||
|
||||
// Create GUI components and layout
|
||||
|
||||
auto *panel = new wxPanel(this);
|
||||
wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
panel->SetSizer(vsizer);
|
||||
|
||||
auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:")));
|
||||
p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY);
|
||||
|
||||
auto *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:")));
|
||||
p->port_picker = new wxComboBox(panel, wxID_ANY);
|
||||
p->port_autodetect = new wxStaticText(panel, wxID_ANY, _(L("Autodetected")));
|
||||
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);
|
||||
port_sizer->Add(p->port_autodetect, 1, wxEXPAND);
|
||||
p->enable_port_picker(true);
|
||||
|
||||
auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:")));
|
||||
p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY);
|
||||
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 *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_port_picker, 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(port_sizer, 0, wxEXPAND);
|
||||
|
||||
grid->Add(label_progress, 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(p->progressbar, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
grid->Add(label_status, 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(p->txt_status, 0, wxEXPAND);
|
||||
|
||||
vsizer->Add(grid, 0, wxEXPAND | wxTOP | wxBOTTOM, SPACING);
|
||||
|
||||
p->spoiler = new wxCollapsiblePane(panel, wxID_ANY, _(L("Advanced: avrdude output log")));
|
||||
p->spoiler = new wxCollapsiblePane(panel, wxID_ANY, _(L("Advanced: avrdude output log")), wxDefaultPosition, wxDefaultSize, wxCP_DEFAULT_STYLE | wxCP_NO_TLW_RESIZE);
|
||||
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);
|
||||
|
@ -571,6 +749,7 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
|
|||
|
||||
p->btn_close = new wxButton(panel, wxID_CLOSE);
|
||||
p->btn_flash = new wxButton(panel, wxID_ANY, p->btn_flash_label_ready);
|
||||
p->btn_flash->Disable();
|
||||
auto *bsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
bsizer->Add(p->btn_close);
|
||||
bsizer->AddStretchSpacer();
|
||||
|
@ -585,16 +764,26 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
|
|||
SetSize(std::max(size.GetWidth(), static_cast<int>(MIN_WIDTH)), std::max(size.GetHeight(), static_cast<int>(MIN_HEIGHT)));
|
||||
Layout();
|
||||
|
||||
// Bind events
|
||||
|
||||
p->hex_picker->Bind(wxEVT_FILEPICKER_CHANGED, [this](wxFileDirPickerEvent& evt) {
|
||||
if (wxFileExists(evt.GetPath())) {
|
||||
this->p->load_hex_file(evt.GetPath());
|
||||
this->p->btn_flash->Enable();
|
||||
}
|
||||
});
|
||||
|
||||
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));
|
||||
const auto new_height = this->GetSize().GetHeight() - this->p->txt_stdout->GetSize().GetHeight();
|
||||
this->SetSize(this->GetSize().GetWidth(), new_height);
|
||||
} else {
|
||||
this->SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT_EXPANDED));
|
||||
}
|
||||
|
||||
this->Fit();
|
||||
this->Layout();
|
||||
this->p->fit_no_shrink();
|
||||
});
|
||||
|
||||
p->btn_close->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { this->Close(); });
|
||||
|
@ -608,7 +797,8 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
|
|||
_(L("Confirmation")),
|
||||
wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
|
||||
if (dlg.ShowModal() == wxID_YES) {
|
||||
this->p->cancel();
|
||||
this->p->set_txt_status(_(L("Cancelling...")));
|
||||
this->p->user_cancel();
|
||||
}
|
||||
} else {
|
||||
// Start a flashing task
|
||||
|
@ -619,6 +809,7 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
|
|||
Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->p->progressbar->Pulse(); });
|
||||
|
||||
Bind(EVT_AVRDUDE, [this](wxCommandEvent &evt) { this->p->on_avrdude(evt); });
|
||||
Bind(EVT_ASYNC_DIALOG, [this](wxCommandEvent &evt) { this->p->on_async_dialog(evt); });
|
||||
|
||||
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt) {
|
||||
if (this->p->avrdude) {
|
||||
|
|
|
@ -1941,12 +1941,23 @@ void GLCanvas3D::set_model(Model* model)
|
|||
|
||||
void GLCanvas3D::set_bed_shape(const Pointfs& shape)
|
||||
{
|
||||
m_bed.set_shape(shape);
|
||||
bool new_shape = (shape != m_bed.get_shape());
|
||||
if (new_shape)
|
||||
m_bed.set_shape(shape);
|
||||
|
||||
// Set the origin and size for painting of the coordinate system axes.
|
||||
m_axes.origin = Pointf3(0.0, 0.0, (coordf_t)GROUND_Z);
|
||||
set_axes_length(0.3f * (float)m_bed.get_bounding_box().max_size());
|
||||
|
||||
if (new_shape)
|
||||
{
|
||||
// forces the selection of the proper camera target
|
||||
if (m_volumes.volumes.empty())
|
||||
zoom_to_bed();
|
||||
else
|
||||
zoom_to_volumes();
|
||||
}
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -303,8 +303,8 @@ const std::vector<std::string>& Preset::print_options()
|
|||
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
|
||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects",
|
||||
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
|
||||
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "compatible_printers",
|
||||
"compatible_printers_condition","inherits"
|
||||
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "single_extruder_multi_material_priming",
|
||||
"compatible_printers", "compatible_printers_condition","inherits"
|
||||
};
|
||||
return s_opts;
|
||||
}
|
||||
|
|
|
@ -919,6 +919,7 @@ void TabPrint::build()
|
|||
optgroup->append_single_option_line("wipe_tower_width");
|
||||
optgroup->append_single_option_line("wipe_tower_rotation_angle");
|
||||
optgroup->append_single_option_line("wipe_tower_bridging");
|
||||
optgroup->append_single_option_line("single_extruder_multi_material_priming");
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Advanced")));
|
||||
optgroup->append_single_option_line("interface_shells");
|
||||
|
|
|
@ -46,8 +46,7 @@ static size_t hex_num_sections(fs::ifstream &file)
|
|||
}
|
||||
|
||||
HexFile::HexFile(fs::path path) :
|
||||
path(std::move(path)),
|
||||
device(DEV_GENERIC)
|
||||
path(std::move(path))
|
||||
{
|
||||
fs::ifstream file(this->path);
|
||||
if (! file.good()) {
|
||||
|
|
|
@ -19,9 +19,10 @@ struct HexFile
|
|||
};
|
||||
|
||||
boost::filesystem::path path;
|
||||
DeviceKind device;
|
||||
DeviceKind device = DEV_GENERIC;
|
||||
std::string model_id;
|
||||
|
||||
HexFile() {}
|
||||
HexFile(boost::filesystem::path path);
|
||||
};
|
||||
|
||||
|
|
|
@ -373,7 +373,7 @@ void Serial::set_DTR(bool on)
|
|||
void Serial::reset_line_num()
|
||||
{
|
||||
// See https://github.com/MarlinFirmware/Marlin/wiki/M110
|
||||
printer_write_line("M110 N0", 0);
|
||||
write_string("M110 N0\n");
|
||||
m_line_num = 0;
|
||||
}
|
||||
|
||||
|
@ -390,9 +390,9 @@ bool Serial::read_line(unsigned timeout, std::string &line, error_code &ec)
|
|||
asio::async_read(*this, boost::asio::buffer(&c, 1), [&](const error_code &read_ec, size_t size) {
|
||||
if (ec || size == 0) {
|
||||
fail = true;
|
||||
ec = read_ec;
|
||||
ec = read_ec; // FIXME: only if operation not aborted
|
||||
}
|
||||
timer.cancel();
|
||||
timer.cancel(); // FIXME: ditto
|
||||
});
|
||||
|
||||
if (timeout > 0) {
|
||||
|
@ -444,6 +444,7 @@ bool Serial::printer_ready_wait(unsigned retries, unsigned timeout)
|
|||
}
|
||||
line.clear();
|
||||
}
|
||||
|
||||
line.clear();
|
||||
}
|
||||
|
||||
|
@ -469,7 +470,7 @@ void Serial::printer_reset()
|
|||
this->set_DTR(true);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
this->set_DTR(false);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
}
|
||||
|
||||
std::string Serial::printer_format_line(const std::string &line, unsigned line_num)
|
||||
|
|
|
@ -51,12 +51,13 @@ public:
|
|||
// Reads a line or times out, the timeout is in milliseconds
|
||||
bool read_line(unsigned timeout, std::string &line, boost::system::error_code &ec);
|
||||
|
||||
// Perform setup for communicating with a printer
|
||||
// Perform an initial setup for communicating with a printer
|
||||
void printer_setup();
|
||||
|
||||
// Write data from a string
|
||||
size_t write_string(const std::string &str);
|
||||
|
||||
|
||||
// Attempts to reset the line numer and waits until the printer says "ok"
|
||||
bool printer_ready_wait(unsigned retries, unsigned timeout);
|
||||
|
||||
// Write Marlin-formatted line, with a line number and a checksum
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue