2018-05-04 16:04:43 +02:00
#include <numeric>
2018-05-21 14:29:53 +02:00
#include <algorithm>
2018-07-24 17:42:12 +02:00
#include <thread>
2018-07-27 11:55:11 +02:00
#include <condition_variable>
2018-07-24 17:42:12 +02:00
#include <stdexcept>
2018-05-04 16:04:43 +02:00
#include <boost/format.hpp>
2018-07-24 17:42:12 +02:00
#include <boost/asio.hpp>
2018-05-04 16:04:43 +02:00
#include <boost/filesystem/path.hpp>
2018-06-19 11:16:56 +02:00
#include <boost/filesystem/fstream.hpp>
2018-05-04 16:04:43 +02:00
#include <boost/log/trivial.hpp>
2018-07-27 11:55:11 +02:00
#include <boost/optional.hpp>
2018-05-04 16:04:43 +02:00
2018-07-24 17:42:12 +02:00
#include "libslic3r/Utils.hpp"
#include "avrdude/avrdude-slic3r.hpp"
#include "GUI.hpp"
2019-02-21 18:59:21 +01:00
#include "GUI_App.hpp"
2018-11-26 14:41:58 +01:00
#include "I18N.hpp"
2018-07-24 17:42:12 +02:00
#include "MsgDialog.hpp"
#include "../Utils/HexFile.hpp"
#include "../Utils/Serial.hpp"
// wx includes need to come after asio because of the WinSock.h problem
#include "FirmwareDialog.hpp"
2018-05-04 16:04:43 +02:00
#include <wx/app.h>
2018-05-16 15:51:14 +02:00
#include <wx/event.h>
2018-05-04 16:04:43 +02:00
#include <wx/sizer.h>
#include <wx/settings.h>
2018-06-05 11:55:23 +02:00
#include <wx/timer.h>
2018-05-04 16:04:43 +02:00
#include <wx/panel.h>
#include <wx/button.h>
#include <wx/filepicker.h>
#include <wx/textctrl.h>
#include <wx/stattext.h>
#include <wx/combobox.h>
2018-05-18 15:38:33 +02:00
#include <wx/gauge.h>
#include <wx/collpane.h>
2018-05-18 18:37:16 +02:00
#include <wx/msgdlg.h>
2018-07-27 11:55:11 +02:00
#include <wx/filefn.h>
2018-05-04 16:04:43 +02:00
namespace fs = boost :: filesystem ;
2018-07-24 17:42:12 +02:00
namespace asio = boost :: asio ;
using boost :: system :: error_code ;
2018-07-27 11:55:11 +02:00
using boost :: optional ;
2018-05-04 16:04:43 +02:00
namespace Slic3r {
2018-07-24 17:42:12 +02:00
using Utils :: HexFile ;
using Utils :: SerialPortInfo ;
using Utils :: Serial ;
2018-05-04 16:04:43 +02:00
2018-07-27 11:55:11 +02:00
// 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 ,
2019-03-27 16:49:44 +01:00
USB_PID_CW1_BOOT = 7 ,
USB_PID_CW1_APP = 8 ,
2018-07-27 11:55:11 +02:00
};
2018-05-18 17:35:24 +02:00
// This enum discriminates the kind of information in EVT_AVRDUDE,
// it's stored in the ExtraLong field of wxCommandEvent.
enum AvrdudeEvent
{
AE_MESSAGE ,
2018-06-05 11:55:23 +02:00
AE_PROGRESS ,
2018-07-27 11:55:11 +02:00
AE_STATUS ,
2018-05-18 17:35:24 +02:00
AE_EXIT ,
};
wxDECLARE_EVENT ( EVT_AVRDUDE , wxCommandEvent );
wxDEFINE_EVENT ( EVT_AVRDUDE , wxCommandEvent );
2018-07-27 11:55:11 +02:00
wxDECLARE_EVENT ( EVT_ASYNC_DIALOG , wxCommandEvent );
wxDEFINE_EVENT ( EVT_ASYNC_DIALOG , wxCommandEvent );
2019-03-27 16:49:44 +01:00
struct Avr109Pid
{
unsigned boot ;
unsigned app ;
Avr109Pid ( unsigned boot , unsigned app ) : boot ( boot ), app ( app ) {}
};
2018-05-18 17:35:24 +02:00
2018-05-04 16:04:43 +02:00
// Private
struct FirmwareDialog :: priv
{
2018-05-18 18:37:16 +02:00
enum AvrDudeComplete
{
2018-07-27 11:55:11 +02:00
AC_NONE ,
2018-05-18 18:37:16 +02:00
AC_SUCCESS ,
AC_FAILURE ,
2018-07-27 11:55:11 +02:00
AC_USER_CANCELLED ,
2018-05-18 18:37:16 +02:00
};
2018-05-18 17:35:24 +02:00
FirmwareDialog * q ; // PIMPL back pointer ("Q-Pointer")
2018-05-04 16:04:43 +02:00
2018-07-27 11:55:11 +02:00
// GUI elements
2018-05-04 16:04:43 +02:00
wxComboBox * port_picker ;
2018-07-27 11:55:11 +02:00
wxStaticText * port_autodetect ;
2018-05-04 16:04:43 +02:00
wxFilePickerCtrl * hex_picker ;
2018-05-18 15:38:33 +02:00
wxStaticText * txt_status ;
wxGauge * progressbar ;
2018-05-21 14:29:53 +02:00
wxCollapsiblePane * spoiler ;
2018-05-04 16:04:43 +02:00
wxTextCtrl * txt_stdout ;
2018-05-18 17:35:24 +02:00
wxButton * btn_rescan ;
2018-05-16 15:51:14 +02:00
wxButton * btn_close ;
2018-05-04 16:04:43 +02:00
wxButton * btn_flash ;
2018-05-18 18:37:16 +02:00
wxString btn_flash_label_ready ;
wxString btn_flash_label_flashing ;
2018-07-27 11:55:11 +02:00
wxString label_status_flashing ;
2018-05-04 16:04:43 +02:00
2018-06-05 11:55:23 +02:00
wxTimer timer_pulse ;
2018-07-27 11:55:11 +02:00
// 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 ;
2018-05-18 17:35:24 +02:00
// 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 ;
2018-05-18 15:38:33 +02:00
unsigned progress_tasks_done ;
2018-07-24 17:42:12 +02:00
unsigned progress_tasks_bar ;
2018-07-27 11:55:11 +02:00
bool user_cancelled ;
2018-07-24 17:42:12 +02:00
const bool extra_verbose ; // For debugging
2018-05-18 15:38:33 +02:00
2018-05-18 17:35:24 +02:00
priv ( FirmwareDialog * q ) :
q ( q ),
2018-05-18 18:37:16 +02:00
btn_flash_label_ready ( _ ( L ( "Flash!" ))),
btn_flash_label_flashing ( _ ( L ( "Cancel" ))),
2018-07-27 11:55:11 +02:00
label_status_flashing ( _ ( L ( "Flashing in progress. Please do not disconnect the printer!" ))),
2018-06-05 11:55:23 +02:00
timer_pulse ( q ),
2018-05-18 18:37:16 +02:00
progress_tasks_done ( 0 ),
2018-07-24 17:42:12 +02:00
progress_tasks_bar ( 0 ),
2018-07-27 11:55:11 +02:00
user_cancelled ( false ),
2018-07-24 17:42:12 +02:00
extra_verbose ( false )
2018-05-16 15:51:14 +02:00
{}
2018-05-04 16:04:43 +02:00
void find_serial_ports ();
2018-07-27 11:55:11 +02:00
void fit_no_shrink ();
void set_txt_status ( const wxString & label );
2018-07-24 17:42:12 +02:00
void flashing_start ( unsigned tasks );
2018-06-19 11:16:56 +02:00
void flashing_done ( AvrDudeComplete complete );
2018-07-27 11:55:11 +02:00
void enable_port_picker ( bool enable );
void load_hex_file ( const wxString & path );
void queue_status ( wxString message );
void queue_error ( const wxString & message );
2018-07-24 17:42:12 +02:00
2018-07-27 11:55:11 +02:00
bool ask_model_id_mismatch ( const std :: string & printer_model );
bool check_model_id ();
2019-03-27 16:49:44 +01:00
void avr109_wait_for_bootloader ( Avr109Pid usb_pid , unsigned retries );
void avr109_reboot ( const SerialPortInfo & port );
void avr109_lookup_port ( Avr109Pid usb_pid );
2018-07-27 11:55:11 +02:00
void prepare_common ();
void prepare_mk2 ();
void prepare_mk3 ();
2019-03-27 16:49:44 +01:00
void prepare_avr109 ( Avr109Pid usb_pid );
2018-05-04 16:04:43 +02:00
void perform_upload ();
2018-07-24 17:42:12 +02:00
2018-07-27 11:55:11 +02:00
void user_cancel ();
2018-05-18 17:35:24 +02:00
void on_avrdude ( const wxCommandEvent & evt );
2018-07-27 11:55:11 +02:00
void on_async_dialog ( const wxCommandEvent & evt );
2018-07-24 17:42:12 +02:00
void ensure_joined ();
2019-03-27 16:49:44 +01:00
static const char * avr109_dev_name ( Avr109Pid usb_pid );
2018-05-04 16:04:43 +02:00
};
void FirmwareDialog :: priv :: find_serial_ports ()
{
2018-05-21 16:14:20 +02:00
auto new_ports = Utils :: scan_serial_ports_extended ();
if ( new_ports != this -> ports ) {
this -> ports = new_ports ;
port_picker -> Clear ();
for ( const auto & port : this -> ports )
2018-07-25 16:54:02 +02:00
port_picker -> Append ( wxString :: FromUTF8 ( port . friendly_name . data ()));
2018-05-21 16:14:20 +02:00
if ( ports . size () > 0 ) {
int idx = port_picker -> GetValue (). IsEmpty () ? 0 : - 1 ;
for ( int i = 0 ; i < ( int ) this -> ports . size (); ++ i )
if ( this -> ports [ i ]. is_printer ) {
idx = i ;
break ;
}
if ( idx != - 1 )
port_picker -> SetSelection ( idx );
}
2018-05-04 16:04:43 +02:00
}
}
2018-07-27 11:55:11 +02:00
void FirmwareDialog :: priv :: fit_no_shrink ()
{
// Ensure content fits into window and window is not shrinked
2018-07-30 14:51:49 +02:00
const auto old_size = q -> GetSize ();
2018-07-27 11:55:11 +02:00
q -> Layout ();
q -> Fit ();
2018-07-30 14:51:49 +02:00
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 );
2018-07-27 11:55:11 +02:00
}
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 ();
}
2018-07-24 17:42:12 +02:00
void FirmwareDialog :: priv :: flashing_start ( unsigned tasks )
2018-05-16 15:51:14 +02:00
{
2018-07-27 11:55:11 +02:00
modal_response = wxID_NONE ;
2018-06-19 11:16:56 +02:00
txt_stdout -> Clear ();
2018-07-27 11:55:11 +02:00
set_txt_status ( label_status_flashing );
2018-10-01 15:09:31 +02:00
txt_status -> SetForegroundColour ( GUI :: wxGetApp (). get_label_clr_modified ());
2018-06-19 11:16:56 +02:00
port_picker -> Disable ();
btn_rescan -> Disable ();
hex_picker -> Disable ();
btn_close -> Disable ();
btn_flash -> SetLabel ( btn_flash_label_flashing );
2018-07-24 17:42:12 +02:00
progressbar -> SetRange ( 200 * tasks ); // See progress callback below
2018-06-19 11:16:56 +02:00
progressbar -> SetValue ( 0 );
progress_tasks_done = 0 ;
2018-07-24 17:42:12 +02:00
progress_tasks_bar = 0 ;
2018-07-27 11:55:11 +02:00
user_cancelled = false ;
2018-06-19 11:16:56 +02:00
timer_pulse . Start ( 50 );
}
2018-05-18 18:37:16 +02:00
2018-06-19 11:16:56 +02:00
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 ) {
2018-07-27 11:55:11 +02:00
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 ;
2018-06-19 11:16:56 +02:00
}
}
2018-07-27 11:55:11 +02:00
void FirmwareDialog :: priv :: enable_port_picker ( bool enable )
2018-06-19 11:16:56 +02:00
{
2018-07-27 11:55:11 +02:00
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 ());
2019-03-27 16:49:44 +01:00
const bool auto_lookup = hex_file . device == HexFile :: DEV_MM_CONTROL || hex_file . device == HexFile :: DEV_CW1 ;
enable_port_picker ( ! auto_lookup );
2018-07-27 11:55:11 +02:00
}
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 ;
2018-07-24 17:42:12 +02:00
}
2018-07-27 11:55:11 +02:00
}
2018-07-24 17:42:12 +02:00
2018-07-27 11:55:11 +02:00
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;
// }
2018-07-24 17:42:12 +02:00
2018-07-27 11:55:11 +02:00
// 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;
}
2019-03-27 16:49:44 +01:00
void FirmwareDialog :: priv :: avr109_wait_for_bootloader ( Avr109Pid usb_pid , unsigned retries )
2018-07-27 11:55:11 +02:00
{
2018-07-24 17:42:12 +02:00
enum {
2018-07-27 11:55:11 +02:00
SLEEP_MS = 500 ,
2018-07-24 17:42:12 +02:00
};
2018-07-27 11:55:11 +02:00
for ( unsigned i = 0 ; i < retries && ! user_cancelled ; i ++ ) {
std :: this_thread :: sleep_for ( std :: chrono :: milliseconds ( SLEEP_MS ));
2018-06-19 11:16:56 +02:00
2018-07-27 11:55:11 +02:00
auto ports = Utils :: scan_serial_ports_extended ();
ports . erase ( std :: remove_if ( ports . begin (), ports . end (), [ = ]( const SerialPortInfo & port ) {
2019-03-27 16:49:44 +01:00
return port . id_vendor != USB_VID_PRUSA || port . id_product != usb_pid . boot ;
2018-07-27 11:55:11 +02:00
}), ports . end ());
2018-07-24 17:42:12 +02:00
2018-07-27 11:55:11 +02:00
if ( ports . size () == 1 ) {
port = ports [ 0 ];
return ;
} else if ( ports . size () > 1 ) {
2019-03-27 16:49:44 +01:00
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Several VID/PID 0x2c99/%1% devices found" ) % usb_pid . boot ;
queue_error ( wxString :: Format (
_ ( L ( "Multiple %s devices found. Please only connect one at a time for flashing." )), avr109_dev_name ( usb_pid )));
2018-07-24 17:42:12 +02:00
return ;
2018-06-19 11:16:56 +02:00
}
2018-05-16 15:51:14 +02:00
}
}
2019-03-27 16:49:44 +01:00
void FirmwareDialog :: priv :: avr109_reboot ( const SerialPortInfo & port )
2018-07-27 11:55:11 +02:00
{
asio :: io_service io ;
Serial serial ( io , port . port , 1200 );
std :: this_thread :: sleep_for ( std :: chrono :: milliseconds ( 50 ));
}
2019-03-27 16:49:44 +01:00
void FirmwareDialog :: priv :: avr109_lookup_port ( Avr109Pid usb_pid )
2018-07-27 11:55:11 +02:00
{
2019-03-27 16:49:44 +01:00
const char * dev_name = avr109_dev_name ( usb_pid );
const wxString msg_not_found = wxString :: Format (
_ ( L ( "The %s device was not found. \n "
"If the device is connected, please press the Reset button next to the USB connector ..." )),
dev_name );
2018-09-17 15:12:13 +02:00
2019-03-27 16:49:44 +01:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Flashing %1%, looking for VID/PID 0x2c99/%2% or 0x2c99/%3% ..." )
% dev_name
% usb_pid . boot
% usb_pid . app ;
2018-07-27 11:55:11 +02:00
auto ports = Utils :: scan_serial_ports_extended ();
ports . erase ( std :: remove_if ( ports . begin (), ports . end (), [ = ]( const SerialPortInfo & port ) {
2018-09-17 15:12:13 +02:00
return port . id_vendor != USB_VID_PRUSA ||
2019-03-27 16:49:44 +01:00
port . id_product != usb_pid . boot &&
port . id_product != usb_pid . app ;
2018-07-27 11:55:11 +02:00
}), ports . end ());
if ( ports . size () == 0 ) {
2019-03-27 16:49:44 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Device not found, asking the user to press Reset and waiting for the device to show up ..." ;
queue_status ( msg_not_found );
avr109_wait_for_bootloader ( usb_pid , 30 );
2018-07-27 11:55:11 +02:00
} else if ( ports . size () > 1 ) {
2019-03-27 16:49:44 +01:00
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "Several VID/PID 0x2c99/%1% devices found" ) % usb_pid . boot ;
queue_error ( wxString :: Format ( _ ( L ( "Multiple %s devices found. Please only connect one at a time for flashing." )), dev_name ));
2018-07-27 11:55:11 +02:00
} else {
2019-03-27 16:49:44 +01:00
if ( ports [ 0 ]. id_product == usb_pid . app ) {
2018-07-27 11:55:11 +02:00
// The device needs to be rebooted into the bootloader mode
2019-03-27 16:49:44 +01:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Found VID/PID 0x2c99/%1% at `%2%`, rebooting the device ..." ) % usb_pid . app % ports [ 0 ]. port ;
avr109_reboot ( ports [ 0 ]);
avr109_wait_for_bootloader ( usb_pid , 10 );
2018-09-17 15:12:13 +02:00
if ( ! port ) {
// The device in bootloader mode was not found, inform the user and wait some more...
2019-03-27 16:49:44 +01:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "%1% device not found after reboot, asking the user to press Reset and waiting for the device to show up ..." ) % dev_name ;
queue_status ( msg_not_found );
avr109_wait_for_bootloader ( usb_pid , 30 );
2018-09-17 15:12:13 +02:00
}
2018-07-27 11:55:11 +02:00
} else {
port = ports [ 0 ];
}
}
}
void FirmwareDialog :: priv :: prepare_common ()
2018-05-04 16:04:43 +02:00
{
std :: vector < std :: string > args {{
2018-06-19 11:16:56 +02:00
extra_verbose ? "-vvvvv" : "-v" ,
2018-05-04 16:04:43 +02:00
"-p" , "atmega2560" ,
2018-06-14 15:03:16 +02:00
// 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.
2018-06-19 11:16:56 +02:00
"-c" , "wiring" ,
2018-07-27 11:55:11 +02:00
"-P" , port -> port ,
"-b" , "115200" , // TODO: Allow other rates? Ditto elsewhere.
2018-05-04 16:04:43 +02:00
"-D" ,
2018-07-27 11:55:11 +02:00
"-U" , ( boost :: format ( "flash:w:0:%1%:i" ) % hex_file . path . string ()). str (),
2018-05-04 16:04:43 +02:00
}};
2019-02-22 10:12:38 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Preparing arguments avrdude: "
2018-05-04 16:04:43 +02:00
<< std :: accumulate ( std :: next ( args . begin ()), args . end (), args [ 0 ], []( std :: string a , const std :: string & b ) {
return a + ' ' + b ;
});
2018-07-27 11:55:11 +02:00
avrdude -> push_args ( std :: move ( args ));
2018-07-24 17:42:12 +02:00
}
2018-06-19 11:16:56 +02:00
2018-07-27 11:55:11 +02:00
void FirmwareDialog :: priv :: prepare_mk2 ()
2018-07-24 17:42:12 +02:00
{
2018-07-27 11:55:11 +02:00
if ( ! port ) { return ; }
if ( ! check_model_id ()) {
avrdude -> cancel ();
return ;
}
prepare_common ();
2018-07-24 17:42:12 +02:00
}
2018-06-19 11:16:56 +02:00
2018-07-27 11:55:11 +02:00
void FirmwareDialog :: priv :: prepare_mk3 ()
2018-07-24 17:42:12 +02:00
{
2018-07-27 11:55:11 +02:00
if ( ! port ) { return ; }
if ( ! check_model_id ()) {
avrdude -> cancel ();
return ;
}
prepare_common ();
2018-07-24 17:42:12 +02:00
// 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:
2018-07-27 11:55:11 +02:00
std :: vector < std :: string > args {{
2018-07-24 17:42:12 +02:00
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" ,
2018-07-27 11:55:11 +02:00
"-P" , port -> port ,
2018-07-24 17:42:12 +02:00
"-b" , "115200" ,
"-D" ,
"-u" , // disable safe mode
2018-07-27 11:55:11 +02:00
"-U" , ( boost :: format ( "flash:w:1:%1%:i" ) % hex_file . path . string ()). str (),
2018-07-24 17:42:12 +02:00
}};
2019-02-22 10:12:38 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Preparing avrdude arguments for external flash flashing: "
2018-07-27 11:55:11 +02:00
<< std :: accumulate ( std :: next ( args . begin ()), args . end (), args [ 0 ], []( std :: string a , const std :: string & b ) {
2018-07-24 17:42:12 +02:00
return a + ' ' + b ;
});
2018-07-27 11:55:11 +02:00
avrdude -> push_args ( std :: move ( args ));
2018-07-24 17:42:12 +02:00
}
2019-03-27 16:49:44 +01:00
void FirmwareDialog :: priv :: prepare_avr109 ( Avr109Pid usb_pid )
2018-07-24 17:42:12 +02:00
{
2018-07-27 11:55:11 +02:00
port = boost :: none ;
2019-03-27 16:49:44 +01:00
avr109_lookup_port ( usb_pid );
2018-07-27 11:55:11 +02:00
if ( ! port ) {
2019-03-27 16:49:44 +01:00
queue_error ( wxString :: Format ( _ ( L ( "The %s device could not have been found" )), avr109_dev_name ( usb_pid )));
2018-07-27 11:55:11 +02:00
return ;
2018-06-19 11:16:56 +02:00
}
2018-07-24 17:42:12 +02:00
2019-03-27 16:49:44 +01:00
BOOST_LOG_TRIVIAL ( info ) << boost :: format ( "Found VID/PID 0x2c99/%1% at `%2%`, flashing ..." ) % usb_pid . boot % port -> port ;
2018-07-27 11:55:11 +02:00
queue_status ( label_status_flashing );
2018-07-24 17:42:12 +02:00
std :: vector < std :: string > args {{
extra_verbose ? "-vvvvv" : "-v" ,
"-p" , "atmega32u4" ,
"-c" , "avr109" ,
2018-07-27 11:55:11 +02:00
"-P" , port -> port ,
2018-07-24 17:42:12 +02:00
"-b" , "57600" ,
"-D" ,
2018-07-27 11:55:11 +02:00
"-U" , ( boost :: format ( "flash:w:0:%1%:i" ) % hex_file . path . string ()). str (),
2018-07-24 17:42:12 +02:00
}};
2019-02-22 10:12:38 +01:00
BOOST_LOG_TRIVIAL ( info ) << "Preparing avrdude arguments: "
2018-07-24 17:42:12 +02:00
<< std :: accumulate ( std :: next ( args . begin ()), args . end (), args [ 0 ], []( std :: string a , const std :: string & b ) {
return a + ' ' + b ;
});
2018-07-27 11:55:11 +02:00
avrdude -> push_args ( std :: move ( args ));
2018-07-24 17:42:12 +02:00
}
void FirmwareDialog :: priv :: perform_upload ()
{
auto filename = hex_picker -> GetPath ();
if ( filename . IsEmpty ()) { return ; }
2018-07-27 11:55:11 +02:00
load_hex_file ( filename ); // Might already be loaded, but we want to make sure it's fresh
2018-07-24 17:42:12 +02:00
2018-07-27 11:55:11 +02:00
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 ;
}
2018-07-25 16:54:02 +02:00
}
2018-07-24 17:42:12 +02:00
const bool extra_verbose = false ; // For debugging
2018-07-27 11:55:11 +02:00
flashing_start ( hex_file . device == HexFile :: DEV_MK3 ? 2 : 1 );
2018-07-24 17:42:12 +02:00
// Init the avrdude object
2019-02-26 13:19:49 +01:00
AvrDude avrdude ;
2018-07-24 17:42:12 +02:00
// 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 ;
2018-08-07 11:28:39 +02:00
avrdude
. on_run ([ this ]( AvrDude :: Ptr avrdude ) {
this -> avrdude = std :: move ( avrdude );
2018-07-24 17:42:12 +02:00
try {
2018-07-27 11:55:11 +02:00
switch ( this -> hex_file . device ) {
2018-07-24 17:42:12 +02:00
case HexFile :: DEV_MK3 :
2018-07-27 11:55:11 +02:00
this -> prepare_mk3 ();
2018-07-24 17:42:12 +02:00
break ;
case HexFile :: DEV_MM_CONTROL :
2019-03-27 16:49:44 +01:00
this -> prepare_avr109 ( Avr109Pid ( USB_PID_MMU_BOOT , USB_PID_MMU_APP ));
break ;
case HexFile :: DEV_CW1 :
this -> prepare_avr109 ( Avr109Pid ( USB_PID_CW1_BOOT , USB_PID_CW1_APP ));
2018-07-24 17:42:12 +02:00
break ;
default :
2018-07-27 11:55:11 +02:00
this -> prepare_mk2 ();
2018-07-24 17:42:12 +02:00
break ;
}
} catch ( const std :: exception & ex ) {
2019-03-27 16:49:44 +01:00
if ( port ) {
queue_error ( wxString :: Format ( _ ( L ( "Error accessing port at %s: %s" )), port -> port , ex . what ()));
} else {
queue_error ( wxString :: Format ( _ ( L ( "Error: %s" )), ex . what ()));
}
2018-07-24 17:42:12 +02:00
}
})
2018-06-19 11:16:56 +02:00
. on_message ( std :: move ([ q , extra_verbose ]( const char * msg , unsigned /* size */ ) {
if ( extra_verbose ) {
BOOST_LOG_TRIVIAL ( debug ) << "avrdude: " << msg ;
}
2018-05-18 17:35:24 +02:00
auto evt = new wxCommandEvent ( EVT_AVRDUDE , q -> GetId ());
2018-05-23 17:21:01 +02:00
auto wxmsg = wxString :: FromUTF8 ( msg );
2019-02-22 14:55:00 +01:00
#ifdef WIN32
// The string might be in local encoding
if ( wxmsg . IsEmpty () && * msg != '\0' ) {
wxmsg = wxString ( msg );
}
#endif
2018-05-18 17:35:24 +02:00
evt -> SetExtraLong ( AE_MESSAGE );
2018-05-23 17:21:01 +02:00
evt -> SetString ( std :: move ( wxmsg ));
2018-05-18 17:35:24 +02:00
wxQueueEvent ( q , evt );
}))
2018-05-21 15:24:24 +02:00
. on_progress ( std :: move ([ q ]( const char * /* task */ , unsigned progress ) {
auto evt = new wxCommandEvent ( EVT_AVRDUDE , q -> GetId ());
2018-06-05 11:55:23 +02:00
evt -> SetExtraLong ( AE_PROGRESS );
2018-05-18 17:35:24 +02:00
evt -> SetInt ( progress );
2018-05-21 15:24:24 +02:00
wxQueueEvent ( q , evt );
2018-05-18 17:35:24 +02:00
}))
2018-07-27 11:55:11 +02:00
. on_complete ( std :: move ([ this ]() {
auto evt = new wxCommandEvent ( EVT_AVRDUDE , this -> q -> GetId ());
2018-05-18 17:35:24 +02:00
evt -> SetExtraLong ( AE_EXIT );
2018-07-27 11:55:11 +02:00
evt -> SetInt ( this -> avrdude -> exit_code ());
wxQueueEvent ( this -> q , evt );
2018-05-18 17:35:24 +02:00
}))
. run ();
}
2018-05-04 16:04:43 +02:00
2018-07-27 11:55:11 +02:00
void FirmwareDialog :: priv :: user_cancel ()
2018-05-21 15:24:24 +02:00
{
if ( avrdude ) {
2018-07-27 11:55:11 +02:00
user_cancelled = true ;
2018-05-21 15:24:24 +02:00
avrdude -> cancel ();
}
}
2018-05-18 17:35:24 +02:00
void FirmwareDialog :: priv :: on_avrdude ( const wxCommandEvent & evt )
{
2018-05-18 18:37:16 +02:00
AvrDudeComplete complete_kind ;
switch ( evt . GetExtraLong ()) {
2018-05-18 17:35:24 +02:00
case AE_MESSAGE :
txt_stdout -> AppendText ( evt . GetString ());
break ;
2018-05-04 16:04:43 +02:00
2018-06-05 11:55:23 +02:00
case AE_PROGRESS :
2018-05-18 17:35:24 +02:00
// We try to track overall progress here.
2018-06-19 11:16:56 +02:00
// 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.
2018-05-18 17:35:24 +02:00
2018-06-05 11:55:23 +02:00
if ( progress_tasks_done > 0 ) {
2018-07-24 17:42:12 +02:00
progressbar -> SetValue ( progress_tasks_bar + evt . GetInt ());
2018-05-18 17:35:24 +02:00
}
if ( evt . GetInt () == 100 ) {
2018-06-05 11:55:23 +02:00
timer_pulse . Stop ();
2018-07-24 17:42:12 +02:00
if ( progress_tasks_done % 3 != 0 ) {
progress_tasks_bar += 100 ;
}
progress_tasks_done ++ ;
2018-05-18 17:35:24 +02:00
}
break ;
case AE_EXIT :
BOOST_LOG_TRIVIAL ( info ) << "avrdude exit code: " << evt . GetInt ();
2018-05-18 18:37:16 +02:00
2018-07-27 11:55:11 +02:00
// 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 ; }
2018-06-19 11:16:56 +02:00
flashing_done ( complete_kind );
2018-07-24 17:42:12 +02:00
ensure_joined ();
break ;
2018-05-18 17:35:24 +02:00
2018-07-27 11:55:11 +02:00
case AE_STATUS :
set_txt_status ( evt . GetString ());
2018-05-18 17:35:24 +02:00
break ;
default :
break ;
}
2018-05-04 16:04:43 +02:00
}
2018-07-27 11:55:11 +02:00
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 ();
}
2018-07-24 17:42:12 +02:00
void FirmwareDialog :: priv :: ensure_joined ()
{
// Make sure the background thread is collected and the AvrDude object reset
if ( avrdude ) { avrdude -> join (); }
avrdude . reset ();
}
2019-03-27 16:49:44 +01:00
const char * FirmwareDialog :: priv :: avr109_dev_name ( Avr109Pid usb_pid ) {
switch ( usb_pid . boot ) {
case USB_PID_MMU_BOOT :
return "Prusa MMU 2.0 Control" ;
break ;
case USB_PID_CW1_BOOT :
return "Prusa CurWa" ;
break ;
default : throw std :: runtime_error (( boost :: format ( "Invalid avr109 device USB PID: %1%" ) % usb_pid . boot ). str ());
}
}
2018-05-04 16:04:43 +02:00
// Public
FirmwareDialog :: FirmwareDialog ( wxWindow * parent ) :
2018-05-21 14:29:53 +02:00
wxDialog ( parent , wxID_ANY , _ ( L ( "Firmware flasher" )), wxDefaultPosition , wxDefaultSize , wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
2018-05-18 17:35:24 +02:00
p ( new priv ( this ))
2018-05-04 16:04:43 +02:00
{
enum {
DIALOG_MARGIN = 15 ,
SPACING = 10 ,
2019-02-21 18:59:21 +01:00
MIN_WIDTH = 50 ,
MIN_HEIGHT = 18 ,
MIN_HEIGHT_EXPANDED = 40 ,
2018-05-04 16:04:43 +02:00
};
2019-02-21 18:59:21 +01:00
const int em = GUI :: wxGetApp (). em_unit ();
int min_width = MIN_WIDTH * em ;
int min_height = MIN_HEIGHT * em ;
int min_height_expanded = MIN_HEIGHT_EXPANDED * em ;
2018-05-18 15:38:33 +02:00
wxFont status_font = wxSystemSettings :: GetFont ( wxSYS_DEFAULT_GUI_FONT );
status_font . MakeBold ();
2018-05-04 16:04:43 +02:00
wxFont mono_font ( wxFontInfo (). Family ( wxFONTFAMILY_TELETYPE ));
mono_font . MakeSmaller ();
2018-07-27 11:55:11 +02:00
// Create GUI components and layout
2018-05-04 16:04:43 +02:00
auto * panel = new wxPanel ( this );
wxBoxSizer * vsizer = new wxBoxSizer ( wxVERTICAL );
panel -> SetSizer ( vsizer );
2018-07-27 11:55:11 +02:00
auto * label_hex_picker = new wxStaticText ( panel , wxID_ANY , _ ( L ( "Firmware image:" )));
2019-02-25 12:06:30 +01:00
p -> hex_picker = new wxFilePickerCtrl ( panel , wxID_ANY , wxEmptyString , wxFileSelectorPromptStr ,
2018-09-17 15:12:13 +02:00
"Hex files (*.hex)|*.hex|All files|*.*" );
2018-07-27 11:55:11 +02:00
2018-05-18 15:38:33 +02:00
auto * label_port_picker = new wxStaticText ( panel , wxID_ANY , _ ( L ( "Serial port:" )));
2018-05-04 16:04:43 +02:00
p -> port_picker = new wxComboBox ( panel , wxID_ANY );
2018-07-27 11:55:11 +02:00
p -> port_autodetect = new wxStaticText ( panel , wxID_ANY , _ ( L ( "Autodetected" )));
2018-05-18 17:35:24 +02:00
p -> btn_rescan = new wxButton ( panel , wxID_ANY , _ ( L ( "Rescan" )));
2018-05-04 16:04:43 +02:00
auto * port_sizer = new wxBoxSizer ( wxHORIZONTAL );
port_sizer -> Add ( p -> port_picker , 1 , wxEXPAND | wxRIGHT , SPACING );
2018-05-18 17:35:24 +02:00
port_sizer -> Add ( p -> btn_rescan , 0 );
2018-07-27 11:55:11 +02:00
port_sizer -> Add ( p -> port_autodetect , 1 , wxEXPAND );
p -> enable_port_picker ( true );
2018-05-04 16:04:43 +02:00
2018-07-27 11:55:11 +02:00
auto * label_progress = new wxStaticText ( panel , wxID_ANY , _ ( L ( "Progress:" )));
p -> progressbar = new wxGauge ( panel , wxID_ANY , 1 , wxDefaultPosition , wxDefaultSize , wxGA_HORIZONTAL | wxGA_SMOOTH );
2018-05-04 16:04:43 +02:00
2018-05-18 15:38:33 +02:00
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 );
2018-05-04 16:04:43 +02:00
2018-05-18 15:38:33 +02:00
auto * grid = new wxFlexGridSizer ( 2 , SPACING , SPACING );
grid -> AddGrowableCol ( 1 );
grid -> Add ( label_hex_picker , 0 , wxALIGN_CENTER_VERTICAL );
grid -> Add ( p -> hex_picker , 0 , wxEXPAND );
2018-07-27 11:55:11 +02:00
grid -> Add ( label_port_picker , 0 , wxALIGN_CENTER_VERTICAL );
grid -> Add ( port_sizer , 0 , wxEXPAND );
2018-05-18 15:38:33 +02:00
grid -> Add ( label_progress , 0 , wxALIGN_CENTER_VERTICAL );
grid -> Add ( p -> progressbar , 1 , wxEXPAND | wxALIGN_CENTER_VERTICAL );
2018-07-27 11:55:11 +02:00
grid -> Add ( label_status , 0 , wxALIGN_CENTER_VERTICAL );
grid -> Add ( p -> txt_status , 0 , wxEXPAND );
2018-05-18 15:38:33 +02:00
vsizer -> Add ( grid , 0 , wxEXPAND | wxTOP | wxBOTTOM , SPACING );
2018-07-30 14:51:49 +02:00
p -> spoiler = new wxCollapsiblePane ( panel , wxID_ANY , _ ( L ( "Advanced: avrdude output log" )), wxDefaultPosition , wxDefaultSize , wxCP_DEFAULT_STYLE | wxCP_NO_TLW_RESIZE );
2018-05-21 14:29:53 +02:00
auto * spoiler_pane = p -> spoiler -> GetPane ();
2018-05-18 15:38:33 +02:00
auto * spoiler_sizer = new wxBoxSizer ( wxVERTICAL );
2018-05-21 14:29:53 +02:00
p -> txt_stdout = new wxTextCtrl ( spoiler_pane , wxID_ANY , wxEmptyString , wxDefaultPosition , wxDefaultSize , wxTE_MULTILINE | wxTE_READONLY );
2018-05-04 16:04:43 +02:00
p -> txt_stdout -> SetFont ( mono_font );
2018-05-18 15:38:33 +02:00
spoiler_sizer -> Add ( p -> txt_stdout , 1 , wxEXPAND );
spoiler_pane -> SetSizer ( spoiler_sizer );
2018-05-21 14:29:53 +02:00
// The doc says proportion need to be 0 for wxCollapsiblePane.
// Experience says it needs to be 1, otherwise things won't get sized properly.
vsizer -> Add ( p -> spoiler , 1 , wxEXPAND | wxBOTTOM , SPACING );
2018-05-04 16:04:43 +02:00
2019-02-28 13:04:37 +01:00
p -> btn_close = new wxButton ( panel , wxID_CLOSE , _ ( L ( "Close" ))); // Note: The label needs to be present, otherwise we get accelerator bugs on Mac
2018-05-18 18:37:16 +02:00
p -> btn_flash = new wxButton ( panel , wxID_ANY , p -> btn_flash_label_ready );
2018-07-27 11:55:11 +02:00
p -> btn_flash -> Disable ();
2018-05-04 16:04:43 +02:00
auto * bsizer = new wxBoxSizer ( wxHORIZONTAL );
2018-05-16 15:51:14 +02:00
bsizer -> Add ( p -> btn_close );
2018-05-04 16:04:43 +02:00
bsizer -> AddStretchSpacer ();
bsizer -> Add ( p -> btn_flash );
vsizer -> Add ( bsizer , 0 , wxEXPAND );
auto * topsizer = new wxBoxSizer ( wxVERTICAL );
topsizer -> Add ( panel , 1 , wxEXPAND | wxALL , DIALOG_MARGIN );
2019-02-21 18:59:21 +01:00
SetMinSize ( wxSize ( min_width , min_height ));
2018-05-21 14:29:53 +02:00
SetSizerAndFit ( topsizer );
const auto size = GetSize ();
2019-02-21 18:59:21 +01:00
SetSize ( std :: max ( size . GetWidth (), static_cast < int > ( min_width )), std :: max ( size . GetHeight (), static_cast < int > ( min_height )));
2018-05-21 14:29:53 +02:00
Layout ();
2018-05-04 16:04:43 +02:00
2019-01-15 10:24:58 +01:00
SetEscapeId ( wxID_CLOSE ); // To close the dialog using "Esc" button
2018-07-27 11:55:11 +02:00
// 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 ();
}
});
2019-02-21 18:59:21 +01:00
p -> spoiler -> Bind ( wxEVT_COLLAPSIBLEPANE_CHANGED , [ = ]( wxCollapsiblePaneEvent & evt ) {
2018-05-21 14:29:53 +02:00
if ( evt . GetCollapsed ()) {
2019-02-21 18:59:21 +01:00
this -> SetMinSize ( wxSize ( min_width , min_height ));
2018-07-30 14:51:49 +02:00
const auto new_height = this -> GetSize (). GetHeight () - this -> p -> txt_stdout -> GetSize (). GetHeight ();
this -> SetSize ( this -> GetSize (). GetWidth (), new_height );
2018-05-21 14:29:53 +02:00
} else {
2019-02-21 18:59:21 +01:00
this -> SetMinSize ( wxSize ( min_width , min_height_expanded ));
2018-05-21 14:29:53 +02:00
}
this -> Layout ();
2018-07-30 14:51:49 +02:00
this -> p -> fit_no_shrink ();
2018-05-21 14:29:53 +02:00
});
2018-05-04 16:04:43 +02:00
2018-05-16 15:51:14 +02:00
p -> btn_close -> Bind ( wxEVT_BUTTON , [ this ]( wxCommandEvent & ) { this -> Close (); });
2018-05-18 17:35:24 +02:00
p -> btn_rescan -> Bind ( wxEVT_BUTTON , [ this ]( wxCommandEvent & ) { this -> p -> find_serial_ports (); });
2018-05-18 18:37:16 +02:00
p -> btn_flash -> Bind ( wxEVT_BUTTON , [ this ]( wxCommandEvent & ) {
if ( this -> p -> avrdude ) {
// Flashing is in progress, ask the user if they're really sure about canceling it
wxMessageDialog dlg ( this ,
_ ( L ( "Are you sure you want to cancel firmware flashing? \n This could leave your printer in an unusable state!" )),
_ ( L ( "Confirmation" )),
wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION );
if ( dlg . ShowModal () == wxID_YES ) {
2018-07-30 14:51:49 +02:00
this -> p -> set_txt_status ( _ ( L ( "Cancelling..." )));
this -> p -> user_cancel ();
2018-05-18 18:37:16 +02:00
}
} else {
// Start a flashing task
this -> p -> perform_upload ();
}
});
2018-06-05 11:55:23 +02:00
Bind ( wxEVT_TIMER , [ this ]( wxTimerEvent & evt ) { this -> p -> progressbar -> Pulse (); });
2018-05-18 17:35:24 +02:00
Bind ( EVT_AVRDUDE , [ this ]( wxCommandEvent & evt ) { this -> p -> on_avrdude ( evt ); });
2018-07-27 11:55:11 +02:00
Bind ( EVT_ASYNC_DIALOG , [ this ]( wxCommandEvent & evt ) { this -> p -> on_async_dialog ( evt ); });
2018-05-04 16:04:43 +02:00
2018-05-16 15:51:14 +02:00
Bind ( wxEVT_CLOSE_WINDOW , [ this ]( wxCloseEvent & evt ) {
2018-05-18 17:35:24 +02:00
if ( this -> p -> avrdude ) {
2018-05-16 15:51:14 +02:00
evt . Veto ();
} else {
2019-01-15 10:24:58 +01:00
this -> EndModal ( wxID_CLOSE );
2018-05-16 15:51:14 +02:00
evt . Skip ();
}
});
2018-05-21 14:29:53 +02:00
p -> find_serial_ports ();
2018-05-04 16:04:43 +02:00
}
FirmwareDialog ::~ FirmwareDialog ()
{
// Needed bacuse of forward defs
}
void FirmwareDialog :: run ( wxWindow * parent )
{
FirmwareDialog dialog ( parent );
dialog . ShowModal ();
}
}