Firmware updater: Add support for l10n firmware images
This commit is contained in:
parent
15f943938b
commit
635bb1e484
4 changed files with 156 additions and 78 deletions
|
@ -1,5 +1,6 @@
|
|||
#include "avrdude-slic3r.hpp"
|
||||
|
||||
#include <deque>
|
||||
#include <thread>
|
||||
|
||||
extern "C" {
|
||||
|
@ -33,7 +34,8 @@ static void avrdude_progress_handler_closure(const char *task, unsigned progress
|
|||
struct AvrDude::priv
|
||||
{
|
||||
std::string sys_config;
|
||||
std::vector<std::string> args;
|
||||
std::deque<std::vector<std::string>> args;
|
||||
size_t current_args_set = 0;
|
||||
RunFn run_fn;
|
||||
MessageFn message_fn;
|
||||
ProgressFn progress_fn;
|
||||
|
@ -41,10 +43,13 @@ struct AvrDude::priv
|
|||
|
||||
std::thread avrdude_thread;
|
||||
|
||||
priv(std::string &&sys_config) : sys_config(sys_config) {}
|
||||
|
||||
int run_one(const std::vector<std::string> &args);
|
||||
int run();
|
||||
};
|
||||
|
||||
int AvrDude::priv::run() {
|
||||
int AvrDude::priv::run_one(const std::vector<std::string> &args) {
|
||||
std::vector<char*> c_args {{ const_cast<char*>(PACKAGE_NAME) }};
|
||||
for (const auto &arg : args) {
|
||||
c_args.push_back(const_cast<char*>(arg.data()));
|
||||
|
@ -69,10 +74,22 @@ int AvrDude::priv::run() {
|
|||
return res;
|
||||
}
|
||||
|
||||
int AvrDude::priv::run() {
|
||||
for (; args.size() > 0; current_args_set++) {
|
||||
int res = run_one(args.front());
|
||||
args.pop_front();
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Public
|
||||
|
||||
AvrDude::AvrDude() : p(new priv()) {}
|
||||
AvrDude::AvrDude(std::string sys_config) : p(new priv(std::move(sys_config))) {}
|
||||
|
||||
AvrDude::AvrDude(AvrDude &&other) : p(std::move(other.p)) {}
|
||||
|
||||
|
@ -83,15 +100,9 @@ AvrDude::~AvrDude()
|
|||
}
|
||||
}
|
||||
|
||||
AvrDude& AvrDude::sys_config(std::string sys_config)
|
||||
AvrDude& AvrDude::push_args(std::vector<std::string> args)
|
||||
{
|
||||
if (p) { p->sys_config = std::move(sys_config); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
AvrDude& AvrDude::args(std::vector<std::string> args)
|
||||
{
|
||||
if (p) { p->args = std::move(args); }
|
||||
if (p) { p->args.push_back(std::move(args)); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -137,7 +148,7 @@ AvrDude::Ptr AvrDude::run()
|
|||
auto res = self->p->run();
|
||||
|
||||
if (self->p->complete_fn) {
|
||||
self->p->complete_fn(res);
|
||||
self->p->complete_fn(res, self->p->current_args_set);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -15,20 +15,20 @@ public:
|
|||
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 */)> CompleteFn;
|
||||
typedef std::function<void(int /* exit status */, size_t /* args_id */)> CompleteFn;
|
||||
|
||||
AvrDude();
|
||||
// Main c-tor, sys_config is the location of avrdude's main configuration file
|
||||
AvrDude(std::string sys_config);
|
||||
AvrDude(AvrDude &&);
|
||||
AvrDude(const AvrDude &) = delete;
|
||||
AvrDude &operator=(AvrDude &&) = delete;
|
||||
AvrDude &operator=(const AvrDude &) = delete;
|
||||
~AvrDude();
|
||||
|
||||
// Set location of avrdude's main configuration file
|
||||
AvrDude& sys_config(std::string sys_config);
|
||||
|
||||
// Set avrdude cli arguments
|
||||
AvrDude& args(std::vector<std::string> args);
|
||||
// Push a set of avrdude cli arguments
|
||||
// Each set makes one avrdude invocation - use this method multiple times to push
|
||||
// more than one avrdude invocations.
|
||||
AvrDude& push_args(std::vector<std::string> args);
|
||||
|
||||
// Set a callback to be called just after run() before avrdude is ran
|
||||
// This can be used to perform any needed setup tasks from the background thread.
|
||||
|
@ -42,7 +42,10 @@ public:
|
|||
// Progress is reported per each task (reading / writing) in percents.
|
||||
AvrDude& on_progress(ProgressFn fn);
|
||||
|
||||
// Called when avrdude's main function finishes
|
||||
// Called when the last avrdude invocation finishes with the exit status of zero,
|
||||
// or earlier, if one of the invocations return a non-zero status.
|
||||
// The second argument contains the sequential id of the last avrdude invocation argument set.
|
||||
// This has no effect when using run_sync().
|
||||
AvrDude& on_complete(CompleteFn fn);
|
||||
|
||||
int run_sync();
|
||||
|
|
|
@ -374,7 +374,7 @@ static void list_parts(FILE * f, const char *prefix, LISTID avrparts)
|
|||
|
||||
static int cleanup_main(int status)
|
||||
{
|
||||
if (pgm_setup && pgm->teardown) {
|
||||
if (pgm_setup && pgm != NULL && pgm->teardown) {
|
||||
pgm->teardown(pgm);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <algorithm>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/app.h>
|
||||
|
@ -92,7 +93,9 @@ struct FirmwareDialog::priv
|
|||
{}
|
||||
|
||||
void find_serial_ports();
|
||||
void flashing_status(bool flashing, AvrDudeComplete complete = AC_NONE);
|
||||
void flashing_start(bool flashing_l10n);
|
||||
void flashing_done(AvrDudeComplete complete);
|
||||
size_t hex_lang_offset(const wxString &path);
|
||||
void perform_upload();
|
||||
void cancel();
|
||||
void on_avrdude(const wxCommandEvent &evt);
|
||||
|
@ -119,43 +122,76 @@ void FirmwareDialog::priv::find_serial_ports()
|
|||
}
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::flashing_status(bool value, AvrDudeComplete complete)
|
||||
void FirmwareDialog::priv::flashing_start(bool flashing_l10n)
|
||||
{
|
||||
if (value) {
|
||||
txt_stdout->Clear();
|
||||
txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!")));
|
||||
txt_status->SetForegroundColour(GUI::get_label_clr_modified());
|
||||
port_picker->Disable();
|
||||
btn_rescan->Disable();
|
||||
hex_picker->Disable();
|
||||
btn_close->Disable();
|
||||
btn_flash->SetLabel(btn_flash_label_flashing);
|
||||
progressbar->SetRange(200); // See progress callback below
|
||||
progressbar->SetValue(0);
|
||||
progress_tasks_done = 0;
|
||||
cancelled = false;
|
||||
timer_pulse.Start(50);
|
||||
} else {
|
||||
auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
port_picker->Enable();
|
||||
btn_rescan->Enable();
|
||||
hex_picker->Enable();
|
||||
btn_close->Enable();
|
||||
btn_flash->SetLabel(btn_flash_label_ready);
|
||||
txt_status->SetForegroundColour(text_color);
|
||||
progressbar->SetValue(200);
|
||||
txt_stdout->Clear();
|
||||
txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!")));
|
||||
txt_status->SetForegroundColour(GUI::get_label_clr_modified());
|
||||
port_picker->Disable();
|
||||
btn_rescan->Disable();
|
||||
hex_picker->Disable();
|
||||
btn_close->Disable();
|
||||
btn_flash->SetLabel(btn_flash_label_flashing);
|
||||
progressbar->SetRange(flashing_l10n ? 500 : 200); // See progress callback below
|
||||
progressbar->SetValue(0);
|
||||
progress_tasks_done = 0;
|
||||
cancelled = false;
|
||||
timer_pulse.Start(50);
|
||||
}
|
||||
|
||||
switch (complete) {
|
||||
case AC_SUCCESS: txt_status->SetLabel(_(L("Flashing succeeded!"))); break;
|
||||
case AC_FAILURE: txt_status->SetLabel(_(L("Flashing failed. Please see the avrdude log below."))); break;
|
||||
case AC_CANCEL: txt_status->SetLabel(_(L("Flashing cancelled."))); break;
|
||||
void FirmwareDialog::priv::flashing_done(AvrDudeComplete complete)
|
||||
{
|
||||
auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
port_picker->Enable();
|
||||
btn_rescan->Enable();
|
||||
hex_picker->Enable();
|
||||
btn_close->Enable();
|
||||
btn_flash->SetLabel(btn_flash_label_ready);
|
||||
txt_status->SetForegroundColour(text_color);
|
||||
timer_pulse.Stop();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
size_t FirmwareDialog::priv::hex_lang_offset(const wxString &path)
|
||||
{
|
||||
fs::ifstream file(fs::path(path.wx_str()));
|
||||
if (! file.good()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *hex_terminator = ":00000001FF\r";
|
||||
size_t res = 0;
|
||||
std::string line;
|
||||
while (getline(file, line, '\n').good()) {
|
||||
// Account for LF vs CRLF
|
||||
if (!line.empty() && line.back() != '\r') {
|
||||
line.push_back('\r');
|
||||
}
|
||||
|
||||
if (line == hex_terminator) {
|
||||
if (res == 0) {
|
||||
// This is the first terminator seen, save the position
|
||||
res = file.tellg();
|
||||
} else {
|
||||
// We've found another terminator, return the offset just after the first one
|
||||
// which is the start of the second 'section'.
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FirmwareDialog::priv::perform_upload()
|
||||
{
|
||||
auto filename = hex_picker->GetPath();
|
||||
auto filename = hex_picker->GetPath();
|
||||
std::string port = port_picker->GetValue().ToStdString();
|
||||
int selection = port_picker->GetSelection();
|
||||
if (selection != -1) {
|
||||
|
@ -165,25 +201,32 @@ void FirmwareDialog::priv::perform_upload()
|
|||
}
|
||||
if (filename.IsEmpty() || port.empty()) { return; }
|
||||
|
||||
flashing_status(true);
|
||||
|
||||
const bool extra_verbose = false; // For debugging
|
||||
const auto lang_offset = hex_lang_offset(filename);
|
||||
const auto filename_utf8 = filename.utf8_str();
|
||||
|
||||
flashing_start(lang_offset > 0);
|
||||
|
||||
// It is ok here to use the q-pointer to the FirmwareDialog
|
||||
// because the dialog ensures it doesn't exit before the background thread is done.
|
||||
auto q = this->q;
|
||||
|
||||
// Init the avrdude object
|
||||
AvrDude avrdude(avrdude_config);
|
||||
|
||||
// Build argument list(s)
|
||||
std::vector<std::string> args {{
|
||||
"-v",
|
||||
extra_verbose ? "-vvvvv" : "-v",
|
||||
"-p", "atmega2560",
|
||||
// Using the "Wiring" mode to program Rambo or Einsy, using the STK500v2 protocol (not the STK500).
|
||||
// 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",
|
||||
// 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",
|
||||
"-c", "wiring",
|
||||
"-P", port,
|
||||
"-b", "115200", // XXX: is this ok to hardcode?
|
||||
"-b", "115200", // TODO: Allow other rates? Ditto below.
|
||||
"-D",
|
||||
"-u", // disable safe mode
|
||||
"-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(), // FIXME
|
||||
// "-vvvvv", //"-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange
|
||||
// XXX: Safe mode?
|
||||
"-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(),
|
||||
}};
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
|
||||
|
@ -191,17 +234,38 @@ void FirmwareDialog::priv::perform_upload()
|
|||
return a + ' ' + b;
|
||||
});
|
||||
|
||||
// It is ok here to use the q-pointer to the FirmwareDialog
|
||||
// because the dialog ensures it doesn't exit before the background thread is done.
|
||||
auto q = this->q;
|
||||
avrdude.push_args(std::move(args));
|
||||
|
||||
if (lang_offset > 0) {
|
||||
// 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 {{
|
||||
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,
|
||||
"-b", "115200",
|
||||
"-D",
|
||||
"-u", // disable safe mode
|
||||
"-U", (boost::format("flash:w:%1%:%2%:i") % lang_offset % filename_utf8.data()).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) {
|
||||
return a + ' ' + b;
|
||||
});
|
||||
|
||||
avrdude.push_args(std::move(args_l10n));
|
||||
}
|
||||
|
||||
this->avrdude = avrdude
|
||||
.on_message(std::move([q, extra_verbose](const char *msg, unsigned /* size */) {
|
||||
if (extra_verbose) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "avrdude: " << msg;
|
||||
}
|
||||
|
||||
avrdude = AvrDude()
|
||||
.sys_config(avrdude_config)
|
||||
.args(args)
|
||||
.on_run([]() { /* TODO: needed? */ })
|
||||
.on_message(std::move([q](const char *msg, unsigned /* size */) {
|
||||
// Debugging output to console, useful when avrdude is executed in a super verbose mode (with -v -v -v).
|
||||
// printf("%s", msg);
|
||||
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
|
||||
auto wxmsg = wxString::FromUTF8(msg);
|
||||
evt->SetExtraLong(AE_MESSAGE);
|
||||
|
@ -214,7 +278,7 @@ void FirmwareDialog::priv::perform_upload()
|
|||
evt->SetInt(progress);
|
||||
wxQueueEvent(q, evt);
|
||||
}))
|
||||
.on_complete(std::move([q](int status) {
|
||||
.on_complete(std::move([q](int status, size_t /* args_id */) {
|
||||
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
|
||||
evt->SetExtraLong(AE_EXIT);
|
||||
evt->SetInt(status);
|
||||
|
@ -243,10 +307,10 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
|
|||
|
||||
case AE_PROGRESS:
|
||||
// We try to track overall progress here.
|
||||
// When uploading the firmware, avrdude first reads a littlebit of status data,
|
||||
// then performs write, then reading (verification).
|
||||
// We ignore the first task (which just let's the timer_pulse work)
|
||||
// and then display overall progress during the latter two tasks.
|
||||
// Avrdude performs 3 tasks per one memory operation ("-U" arg),
|
||||
// first of which is reading of status data (very short).
|
||||
// We use the timer_pulse during the very first task to indicate intialization
|
||||
// and then display overall progress during the latter tasks.
|
||||
|
||||
if (progress_tasks_done > 0) {
|
||||
progressbar->SetValue(progress_tasks_done - 100 + evt.GetInt());
|
||||
|
@ -263,7 +327,7 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
|
|||
BOOST_LOG_TRIVIAL(info) << "avrdude exit code: " << evt.GetInt();
|
||||
|
||||
complete_kind = cancelled ? AC_CANCEL : (evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE);
|
||||
flashing_status(false, complete_kind);
|
||||
flashing_done(complete_kind);
|
||||
|
||||
// Make sure the background thread is collected and the AvrDude object reset
|
||||
if (avrdude) { avrdude->join(); }
|
||||
|
|
Loading…
Add table
Reference in a new issue