Merge branch 'master' into time_estimate

This commit is contained in:
bubnikv 2018-08-03 23:04:44 +02:00
commit ac2b20b54b
60 changed files with 2959 additions and 1548 deletions

View file

@ -8,6 +8,7 @@
#include <unordered_map>
#include <slic3r/GUI/GUI.hpp>
#include <ModelArrange.hpp>
#include <slic3r/GUI/PresetBundle.hpp>
#include <Geometry.hpp>
@ -293,6 +294,8 @@ void AppController::arrange_model()
supports_asynch()? std::launch::async : std::launch::deferred,
[this]()
{
using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
unsigned count = 0;
for(auto obj : model_->objects) count += obj->instances.size();
@ -310,13 +313,25 @@ void AppController::arrange_model()
auto dist = print_ctl()->config().min_object_distance();
BoundingBoxf bb(print_ctl()->config().bed_shape.values);
// Create the arranger config
auto min_obj_distance = static_cast<Coord>(dist/SCALING_FACTOR);
auto& bedpoints = print_ctl()->config().bed_shape.values;
Polyline bed; bed.points.reserve(bedpoints.size());
for(auto& v : bedpoints)
bed.append(Point::new_scale(v.x, v.y));
if(pind) pind->update(0, _(L("Arranging objects...")));
try {
model_->arrange_objects(dist, &bb, [pind, count](unsigned rem){
if(pind) pind->update(count - rem, _(L("Arranging objects...")));
arr::arrange(*model_,
min_obj_distance,
bed,
arr::BOX,
false, // create many piles not just one pile
[pind, count](unsigned rem) {
if(pind)
pind->update(count - rem, _(L("Arranging objects...")));
});
} catch(std::exception& e) {
std::cerr << e.what() << std::endl;

View file

@ -644,20 +644,62 @@ std::vector<int> GLVolumeCollection::load_object(
return volumes_idx;
}
int GLVolumeCollection::load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs)
{
float color[4] = { 0.5f, 0.5f, 0.0f, 0.5f };
this->volumes.emplace_back(new GLVolume(color));
GLVolume &v = *this->volumes.back();
int GLVolumeCollection::load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width)
{
if (depth < 0.01f)
return int(this->volumes.size() - 1);
if (height == 0.0f)
height = 0.1f;
auto mesh = make_cube(width, depth, height);
mesh.translate(-width / 2.f, -depth / 2.f, 0.f);
Point origin_of_rotation(0.f, 0.f);
mesh.rotate(rotation_angle,&origin_of_rotation);
TriangleMesh mesh;
float color[4] = { 0.5f, 0.5f, 0.0f, 1.f };
// In case we don't know precise dimensions of the wipe tower yet, we'll draw the box with different color with one side jagged:
if (size_unknown) {
color[0] = 0.9f;
color[1] = 0.6f;
depth = std::max(depth, 10.f); // Too narrow tower would interfere with the teeth. The estimate is not precise anyway.
float min_width = 30.f;
// We'll now create the box with jagged edge. y-coordinates of the pre-generated model are shifted so that the front
// edge has y=0 and centerline of the back edge has y=depth:
Pointf3s points;
std::vector<Point3> facets;
float out_points_idx[][3] = {{0, -depth, 0}, {0, 0, 0}, {38.453, 0, 0}, {61.547, 0, 0}, {100, 0, 0}, {100, -depth, 0}, {55.7735, -10, 0}, {44.2265, 10, 0},
{38.453, 0, 1}, {0, 0, 1}, {0, -depth, 1}, {100, -depth, 1}, {100, 0, 1}, {61.547, 0, 1}, {55.7735, -10, 1}, {44.2265, 10, 1}};
int out_facets_idx[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 5, 0}, {3, 5, 6}, {6, 2, 7}, {6, 0, 2}, {8, 9, 10}, {11, 12, 13}, {10, 11, 14}, {14, 11, 13}, {15, 8, 14},
{8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8},
{0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11}};
for (int i=0;i<16;++i)
points.push_back(Pointf3(out_points_idx[i][0] / (100.f/min_width), out_points_idx[i][1] + depth, out_points_idx[i][2]));
for (int i=0;i<28;++i)
facets.push_back(Point3(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2]));
TriangleMesh tooth_mesh(points, facets);
// We have the mesh ready. It has one tooth and width of min_width. We will now append several of these together until we are close to
// the required width of the block. Than we can scale it precisely.
size_t n = std::max(1, int(width/min_width)); // How many shall be merged?
for (size_t i=0;i<n;++i) {
mesh.merge(tooth_mesh);
tooth_mesh.translate(min_width, 0.f, 0.f);
}
mesh.scale(Pointf3(width/(n*min_width), 1.f, height)); // Scaling to proper width
}
else
mesh = make_cube(width, depth, height);
// We'll make another mesh to show the brim (fixed layer height):
TriangleMesh brim_mesh = make_cube(width+2.f*brim_width, depth+2.f*brim_width, 0.2f);
brim_mesh.translate(-brim_width, -brim_width, 0.f);
mesh.merge(brim_mesh);
mesh.rotate(rotation_angle, &origin_of_rotation); // rotates the box according to the config rotation setting
this->volumes.emplace_back(new GLVolume(color));
GLVolume &v = *this->volumes.back();
if (use_VBOs)
v.indexed_vertex_array.load_mesh_full_shading(mesh);

View file

@ -401,7 +401,7 @@ public:
bool use_VBOs);
int load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width);
// Render the volumes by OpenGL.
void render_VBOs() const;

View file

@ -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];

View file

@ -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;
}

View file

@ -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) {

View file

@ -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;
}
@ -2303,7 +2314,12 @@ void GLCanvas3D::reload_scene(bool force)
float w = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_width"))->value;
float a = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_rotation_angle"))->value;
m_volumes.load_wipe_tower_preview(1000, x, y, w, 15.0f * (float)(extruders_count - 1), (float)height, a, m_use_VBOs && m_initialized);
float depth = m_print->get_wipe_tower_depth();
if (!m_print->state.is_done(psWipeTower))
depth = (900.f/w) * (float)(extruders_count - 1) ;
m_volumes.load_wipe_tower_preview(1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !m_print->state.is_done(psWipeTower),
m_print->config.nozzle_diameter.values[0] * 1.25f * 4.5f);
}
}
@ -3372,7 +3388,7 @@ void GLCanvas3D::_camera_tranform() const
::glMatrixMode(GL_MODELVIEW);
::glLoadIdentity();
::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch
::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch
::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f); // yaw
Pointf3 neg_target = m_camera.target.negative();
@ -4052,6 +4068,8 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
{
const Print *print;
const std::vector<float> *tool_colors;
WipeTower::xy wipe_tower_pos;
float wipe_tower_angle;
// Number of vertices (each vertex is 6x4=24 bytes long)
static const size_t alloc_size_max() { return 131072; } // 3.15MB
@ -4079,11 +4097,14 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
ctxt.print = m_print;
ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
if (m_print->m_wipe_tower_priming)
if (m_print->m_wipe_tower_priming && m_print->config.single_extruder_multi_material_priming)
ctxt.priming.emplace_back(*m_print->m_wipe_tower_priming.get());
if (m_print->m_wipe_tower_final_purge)
ctxt.final.emplace_back(*m_print->m_wipe_tower_final_purge.get());
ctxt.wipe_tower_angle = ctxt.print->config.wipe_tower_rotation_angle.value/180.f * M_PI;
ctxt.wipe_tower_pos = WipeTower::xy(ctxt.print->config.wipe_tower_x.value, ctxt.print->config.wipe_tower_y.value);
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
//FIXME Improve the heuristics for a grain size.
@ -4141,12 +4162,25 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
lines.reserve(n_lines);
widths.reserve(n_lines);
heights.assign(n_lines, extrusions.layer_height);
WipeTower::Extrusion e_prev = extrusions.extrusions[i-1];
if (!extrusions.priming) { // wipe tower extrusions describe the wipe tower at the origin with no rotation
e_prev.pos.rotate(ctxt.wipe_tower_angle);
e_prev.pos.translate(ctxt.wipe_tower_pos);
}
for (; i < j; ++i) {
const WipeTower::Extrusion &e = extrusions.extrusions[i];
WipeTower::Extrusion e = extrusions.extrusions[i];
assert(e.width > 0.f);
const WipeTower::Extrusion &e_prev = *(&e - 1);
if (!extrusions.priming) {
e.pos.rotate(ctxt.wipe_tower_angle);
e.pos.translate(ctxt.wipe_tower_pos);
}
lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
widths.emplace_back(e.width);
e_prev = e;
}
_3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z,
*vols[ctxt.volume_idx(e.tool, 0)]);
@ -4705,8 +4739,11 @@ void GLCanvas3D::_load_shells()
const PrintConfig& config = m_print->config;
unsigned int extruders_count = config.nozzle_diameter.size();
if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) {
const float width_per_extruder = 15.0f; // a simple workaround after wipe_tower_per_color_wipe got obsolete
m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, m_use_VBOs && m_initialized);
float depth = m_print->get_wipe_tower_depth();
if (!m_print->state.is_done(psWipeTower))
depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1) ;
m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
m_use_VBOs && m_initialized, !m_print->state.is_done(psWipeTower), m_print->config.nozzle_diameter.values[0] * 1.25f * 4.5f);
}
}
@ -4812,7 +4849,7 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
if (m_model == nullptr)
return;
std::set<std::string> done; // prevent moving instances twice
std::set<std::string> done; // prevent moving instances twice
bool object_moved = false;
Pointf3 wipe_tower_origin(0.0, 0.0, 0.0);
for (int volume_idx : volume_idxs)
@ -4821,7 +4858,7 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
int obj_idx = volume->object_idx();
int instance_idx = volume->instance_idx();
// prevent moving instances twice
// prevent moving instances twice
char done_id[64];
::sprintf(done_id, "%d_%d", obj_idx, instance_idx);
if (done.find(done_id) != done.end())

View file

@ -903,6 +903,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
std::vector<float> extruders = dlg.get_extruders();
(config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(),matrix.end());
(config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(),extruders.end());
g_on_request_update_callback.call();
}
}));
return sizer;
@ -919,6 +920,10 @@ ConfigOptionsGroup* get_optgroup()
return m_optgroup.get();
}
void register_on_request_update_callback(void* callback) {
if (callback != nullptr)
g_on_request_update_callback.register_callback(callback);
}
wxButton* get_wiping_dialog_button()
{

View file

@ -4,6 +4,7 @@
#include <string>
#include <vector>
#include "Config.hpp"
#include "../../libslic3r/Utils.hpp"
#include <wx/intl.h>
#include <wx/string.h>
@ -171,6 +172,9 @@ wxString from_u8(const std::string &str);
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer);
static PerlCallback g_on_request_update_callback;
void register_on_request_update_callback(void* callback);
ConfigOptionsGroup* get_optgroup();
wxButton* get_wiping_dialog_button();

View file

@ -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;
}
@ -314,11 +314,12 @@ const std::vector<std::string>& Preset::filament_options()
static std::vector<std::string> s_opts {
"filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
"extrusion_multiplier", "filament_density", "filament_cost",
"filament_loading_speed", "filament_load_time", "filament_unloading_speed", "filament_unload_time", "filament_toolchange_delay",
"filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "temperature",
"first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed",
"bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
"start_filament_gcode", "end_filament_gcode","compatible_printers", "compatible_printers_condition", "inherits"
"filament_loading_speed", "filament_load_time", "filament_unloading_speed", "filament_unload_time", "filament_toolchange_delay",
"filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters",
"filament_minimal_purge_on_wipe_tower", "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature",
"fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time",
"slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", "end_filament_gcode","compatible_printers", "compatible_printers_condition",
"inherits"
};
return s_opts;
}

View file

@ -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");
@ -1297,6 +1298,7 @@ void TabFilament::build()
optgroup->append_single_option_line("filament_cooling_moves");
optgroup->append_single_option_line("filament_cooling_initial_speed");
optgroup->append_single_option_line("filament_cooling_final_speed");
optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower");
line = { _(L("Ramming")), "" };
line.widget = [this](wxWindow* parent){

View file

@ -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()) {

View file

@ -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);
};

View file

@ -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)

View file

@ -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