mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	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