From 9af43bee52bf9df52b32bcc7245dcb8205589d4d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 4 Jan 2015 23:18:23 +0100 Subject: [PATCH] Handle log, temperatures. Move controller to main tabpanel. More things --- lib/Slic3r/GUI.pm | 10 +- .../{Controller/Frame.pm => Controller.pm} | 10 +- lib/Slic3r/GUI/Controller/PrinterPanel.pm | 135 +++++++++++++++--- lib/Slic3r/GUI/MainFrame.pm | 26 ++-- lib/Slic3r/GUI/Plater.pm | 10 +- xs/src/libslic3r/GCodeSender.cpp | 60 +++++++- xs/src/libslic3r/GCodeSender.hpp | 8 ++ xs/xsp/GCodeSender.xsp | 3 + 8 files changed, 204 insertions(+), 58 deletions(-) rename lib/Slic3r/GUI/{Controller/Frame.pm => Controller.pm} (92%) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index f7bad0367..bb32281dc 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -9,7 +9,7 @@ use Slic3r::GUI::AboutDialog; use Slic3r::GUI::BedShapeDialog; use Slic3r::GUI::BonjourBrowser; use Slic3r::GUI::ConfigWizard; -use Slic3r::GUI::Controller::Frame; +use Slic3r::GUI::Controller; use Slic3r::GUI::Controller::PrinterPanel; use Slic3r::GUI::MainFrame; use Slic3r::GUI::Notifier; @@ -295,14 +295,6 @@ sub CallAfter { push @cb, $cb; } -sub show_printer_controller { - my ($self) = @_; - - $self->{controller_frame} //= Slic3r::GUI::Controller::Frame->new; - $self->{controller_frame}->Show; - return $self->{controller_frame}; -} - sub scan_serial_ports { my ($self) = @_; diff --git a/lib/Slic3r/GUI/Controller/Frame.pm b/lib/Slic3r/GUI/Controller.pm similarity index 92% rename from lib/Slic3r/GUI/Controller/Frame.pm rename to lib/Slic3r/GUI/Controller.pm index 131d5ea79..329efba89 100644 --- a/lib/Slic3r/GUI/Controller/Frame.pm +++ b/lib/Slic3r/GUI/Controller.pm @@ -1,17 +1,17 @@ -package Slic3r::GUI::Controller::Frame; +package Slic3r::GUI::Controller; use strict; use warnings; use utf8; use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button); use Wx::Event qw(EVT_CLOSE EVT_LEFT_DOWN EVT_MENU); -use base 'Wx::Frame'; +use base 'Wx::ScrolledWindow'; sub new { - my ($class) = @_; - my $self = $class->SUPER::new(undef, -1, "Controller", wxDefaultPosition, [600,350], - wxDEFAULT_FRAME_STYLE | wxFRAME_EX_METAL); + my ($class, $parent) = @_; + my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [600,350]); + $self->SetScrollbars(0, 1, 0, 1); $self->{sizer} = my $sizer = Wx::BoxSizer->new(wxVERTICAL); { diff --git a/lib/Slic3r/GUI/Controller/PrinterPanel.pm b/lib/Slic3r/GUI/Controller/PrinterPanel.pm index c6f155b07..1b7b74a5d 100644 --- a/lib/Slic3r/GUI/Controller/PrinterPanel.pm +++ b/lib/Slic3r/GUI/Controller/PrinterPanel.pm @@ -3,15 +3,17 @@ use strict; use warnings; use utf8; -use Wx qw(wxTheApp :panel :id :misc :sizer :button :bitmap :window :gauge :timer); +use Wx qw(wxTheApp :panel :id :misc :sizer :button :bitmap :window :gauge :timer + :textctrl :font :systemsettings); use Wx::Event qw(EVT_BUTTON EVT_MOUSEWHEEL EVT_TIMER); use base qw(Wx::Panel Class::Accessor); __PACKAGE__->mk_accessors(qw(printer_name config sender jobs - printing print_status_timer)); + printing status_timer temp_timer)); use constant CONNECTION_TIMEOUT => 3; # seconds -use constant PRINT_STATUS_TIMER_INTERVAL => 1000; # milliseconds +use constant STATUS_TIMER_INTERVAL => 1000; # milliseconds +use constant TEMP_TIMER_INTERVAL => 5000; # milliseconds sub new { my ($class, $parent, $printer_name, $config) = @_; @@ -21,20 +23,51 @@ sub new { $self->config($config); $self->jobs([]); + # set up the timer that polls for updates { my $timer_id = &Wx::NewId(); - $self->print_status_timer(Wx::Timer->new($self, $timer_id)); + $self->status_timer(Wx::Timer->new($self, $timer_id)); EVT_TIMER($self, $timer_id, sub { my ($self, $event) = @_; - return if !$self->printing; - my $queue_size = $self->sender->queue_size; - $self->{gauge}->SetValue($self->{gauge}->GetRange - $queue_size); - if ($queue_size == 0) { - $self->print_completed; - return; + if ($self->printing) { + my $queue_size = $self->sender->queue_size; + $self->{gauge}->SetValue($self->{gauge}->GetRange - $queue_size); + if ($queue_size == 0) { + $self->print_completed; + } } - # TODO: get temperature messages + $self->{log_textctrl}->AppendText("$_\n") for @{$self->sender->purge_log}; + { + my $temp = $self->sender->getT; + if ($temp eq '') { + $self->{temp_panel}->Hide; + } else { + if (!$self->{temp_panel}->IsShown) { + $self->{temp_panel}->Show; + $self->Layout; + } + $self->{temp_text}->SetLabel($temp . "°C"); + + $temp = $self->sender->getB; + if ($temp eq '') { + $self->{bed_temp_text}->SetLabel('n.a.'); + } else { + $self->{bed_temp_text}->SetLabel($temp . "°C"); + } + } + } + }); + } + + # set up the timer that sends temperature requests + # (responses are handled by status_timer) + { + my $timer_id = &Wx::NewId(); + $self->temp_timer(Wx::Timer->new($self, $timer_id)); + EVT_TIMER($self, $timer_id, sub { + my ($self, $event) = @_; + $self->sender->send("M105", 1); # send it through priority queue }); } @@ -44,7 +77,7 @@ sub new { # printer name { - my $text = Wx::StaticText->new($box, -1, $self->printer_name, wxDefaultPosition, [250,-1]); + my $text = Wx::StaticText->new($box, -1, $self->printer_name, wxDefaultPosition, [220,-1]); my $font = $text->GetFont; $font->SetPointSize(20); $text->SetFont($font); @@ -105,7 +138,7 @@ sub new { # buttons { - $self->{btn_connect} = my $btn = Wx::Button->new($box, -1, "Connect", wxDefaultPosition, [-1, 40]); + $self->{btn_connect} = my $btn = Wx::Button->new($box, -1, "Connect to printer", wxDefaultPosition, [-1, 40]); my $font = $btn->GetFont; $font->SetPointSize($font->GetPointSize + 2); $btn->SetFont($font); @@ -124,8 +157,40 @@ sub new { } # status - $self->{status_text} = Wx::StaticText->new($box, -1, "", wxDefaultPosition, [250,-1]); - $left_sizer->Add($self->{status_text}, 0, wxEXPAND | wxTOP, 15); + $self->{status_text} = Wx::StaticText->new($box, -1, "", wxDefaultPosition, [200,-1]); + $left_sizer->Add($self->{status_text}, 1, wxEXPAND | wxTOP, 15); + + # temperature + { + my $temp_panel = $self->{temp_panel} = Wx::Panel->new($box, -1); + my $temp_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + + my $temp_font = Wx::Font->new($Slic3r::GUI::small_font); + $temp_font->SetWeight(wxFONTWEIGHT_BOLD); + { + my $text = Wx::StaticText->new($temp_panel, -1, "Temperature:", wxDefaultPosition, wxDefaultSize); + $text->SetFont($Slic3r::GUI::small_font); + $temp_sizer->Add($text, 0, wxALIGN_CENTER_VERTICAL); + + $self->{temp_text} = Wx::StaticText->new($temp_panel, -1, "", wxDefaultPosition, wxDefaultSize); + $self->{temp_text}->SetFont($temp_font); + $self->{temp_text}->SetForegroundColour(Wx::wxRED); + $temp_sizer->Add($self->{temp_text}, 1, wxALIGN_CENTER_VERTICAL); + } + { + my $text = Wx::StaticText->new($temp_panel, -1, "Bed:", wxDefaultPosition, wxDefaultSize); + $text->SetFont($Slic3r::GUI::small_font); + $temp_sizer->Add($text, 0, wxALIGN_CENTER_VERTICAL); + + $self->{bed_temp_text} = Wx::StaticText->new($temp_panel, -1, "", wxDefaultPosition, wxDefaultSize); + $self->{bed_temp_text}->SetFont($temp_font); + $self->{bed_temp_text}->SetForegroundColour(Wx::wxRED); + $temp_sizer->Add($self->{bed_temp_text}, 1, wxALIGN_CENTER_VERTICAL); + } + $temp_panel->SetSizer($temp_sizer); + $temp_panel->Hide; + $left_sizer->Add($temp_panel, 0, wxEXPAND | wxTOP, 4); + } # print jobs panel my $print_jobs_sizer = Wx::BoxSizer->new(wxVERTICAL); @@ -141,14 +206,27 @@ sub new { $print_jobs_sizer->Add($self->{jobs_panel}, 1, wxEXPAND, 0); } + my $log_sizer = Wx::BoxSizer->new(wxVERTICAL); + { + my $text = Wx::StaticText->new($box, -1, "Log:", wxDefaultPosition, wxDefaultSize); + $text->SetFont($Slic3r::GUI::small_font); + $log_sizer->Add($text, 0, wxEXPAND, 0); + + my $log = $self->{log_textctrl} = Wx::TextCtrl->new($box, -1, "", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxBORDER_SUNKEN); + $log->SetBackgroundColour($box->GetBackgroundColour); + $log->SetFont($Slic3r::GUI::small_font); + $log->SetEditable(0); + $log_sizer->Add($self->{log_textctrl}, 1, wxEXPAND, 0); + } + $sizer->Add($left_sizer, 0, wxEXPAND | wxALL, 0); - $sizer->Add($print_jobs_sizer, 1, wxEXPAND | wxALL, 0); + $sizer->Add($print_jobs_sizer, 2, wxEXPAND | wxALL, 0); + $sizer->Add($log_sizer, 1, wxEXPAND | wxLEFT, 15); $self->SetSizer($sizer); $self->SetMinSize($self->GetSize); $self->_update_connection_controls; - $self->set_status('Printer is offline. Click the Connect button.'); return $self; } @@ -183,7 +261,7 @@ sub _update_connection_controls { sub set_status { my ($self, $status) = @_; $self->{status_text}->SetLabel($status); - $self->{status_text}->Wrap($self->{status_text}->GetSize->GetWidth - 30); + $self->{status_text}->Wrap($self->{status_text}->GetSize->GetWidth); $self->{status_text}->Refresh; $self->Layout; } @@ -209,6 +287,11 @@ sub connect { } if ($self->sender->is_connected) { $self->set_status("Printer is online. You can now start printing from the queue on the right."); + $self->status_timer->Start(STATUS_TIMER_INTERVAL, wxTIMER_CONTINUOUS); + $self->temp_timer->Start(TEMP_TIMER_INTERVAL, wxTIMER_CONTINUOUS); + + # request temperature now, without waiting for the timer + $self->sender->send("M105", 1); } else { $self->set_status("Connection failed. Check serial port and speed."); } @@ -219,14 +302,16 @@ sub connect { sub disconnect { my ($self) = @_; - $self->print_status_timer->Stop; + $self->status_timer->Stop; + $self->temp_timer->Stop; return if !$self->is_connected; $self->printing->printing(0) if $self->printing; $self->printing(undef); $self->{gauge}->Hide; + $self->{temp_panel}->Hide; $self->sender->disconnect; - $self->set_status("Not connected"); + $self->set_status(""); $self->_update_connection_controls; $self->reload_jobs; } @@ -280,8 +365,11 @@ sub print_job { $self->{gauge}->Show; $self->Layout; - $self->print_status_timer->Start(PRINT_STATUS_TIMER_INTERVAL, wxTIMER_CONTINUOUS); $self->set_status('Printing...'); + { + my @time = localtime(time); + $self->{log_textctrl}->AppendText(sprintf "=====\nPrint started at %02d:%02d:%02d\n", @time[2,1,0]); + } } sub print_completed { @@ -294,9 +382,12 @@ sub print_completed { $self->_update_connection_controls; $self->{gauge}->Hide; $self->Layout; - $self->print_status_timer->Stop; $self->set_status('Print completed.'); + { + my @time = localtime(time); + $self->{log_textctrl}->AppendText(sprintf "Print completed at %02d:%02d:%02d\n", @time[2,1,0]); + } # reorder jobs @{$self->jobs} = sort { $a->printed <=> $b->printed } @{$self->jobs}; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 1ea66887f..91f2bf859 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -86,6 +86,7 @@ sub _init_tabpanel { if (!$self->{no_plater}) { $panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), "Plater"); + $panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller"); } $self->{options_tabs} = {}; @@ -215,22 +216,25 @@ sub _init_menubar { # Window menu my $windowMenu = Wx::Menu->new; { - my $tab_count = $self->{no_plater} ? 3 : 4; - $self->_append_menu_item($windowMenu, "Select &Plater Tab\tCtrl+1", 'Show the plater', sub { - $self->select_tab(0); - }) unless $self->{no_plater}; + my $tab_offset = 0; + if (!$self->{no_plater}) { + $self->_append_menu_item($windowMenu, "Select &Plater Tab\tCtrl+1", 'Show the plater', sub { + $self->select_tab(0); + }); + $self->_append_menu_item($windowMenu, "Select &Controller Tab\tCtrl+T", 'Show the printer controller', sub { + $self->select_tab(1); + }); + $windowMenu->AppendSeparator(); + $tab_offset += 2; + } $self->_append_menu_item($windowMenu, "Select P&rint Settings Tab\tCtrl+2", 'Show the print settings', sub { - $self->select_tab($tab_count-3); + $self->select_tab($tab_offset+0); }); $self->_append_menu_item($windowMenu, "Select &Filament Settings Tab\tCtrl+3", 'Show the filament settings', sub { - $self->select_tab($tab_count-2); + $self->select_tab($tab_offset+1); }); $self->_append_menu_item($windowMenu, "Select Print&er Settings Tab\tCtrl+4", 'Show the printer settings', sub { - $self->select_tab($tab_count-1); - }); - $windowMenu->AppendSeparator(); - $self->_append_menu_item($windowMenu, "Printer &Controller\tCtrl+T", 'Show the printer controller', sub { - wxTheApp->show_printer_controller; + $self->select_tab($tab_offset+2); }); } diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 95a09976a..c5af0dc10 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1078,7 +1078,7 @@ sub on_export_completed { my $do_print = 0; if ($result) { if ($self->{print_file}) { - $message = "Adding file to print queue..."; + $message = "File added to print queue"; $do_print = 1; } elsif ($self->{send_gcode_file}) { $message = "Sending G-code file to the OctoPrint server..."; @@ -1108,7 +1108,7 @@ sub do_print { my $printer_tab = $self->GetFrame->{options_tabs}{printer}; my $printer_name = $printer_tab->get_current_preset->name; - my $controller = wxTheApp->show_printer_controller; + my $controller = $self->GetFrame->{controller}; my $printer_panel = $controller->add_printer($printer_name, $printer_tab->config); my $filament_stats = $self->{print}->filament_stats; @@ -1116,10 +1116,7 @@ sub do_print { $filament_stats = { map { $filament_names[$_] => $filament_stats->{$_} } keys %$filament_stats }; $printer_panel->load_print_job($self->{print_file}, $filament_stats); - $controller->Iconize(0); # restore the window if minimized - $controller->SetFocus(); # focus on my window - $controller->Raise(); # bring window to front - $controller->Show(1); # show the window + $self->GetFrame->select_tab(1); } sub send_gcode { @@ -1426,6 +1423,7 @@ sub object_list_changed { if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) { $self->{btn_export_gcode}->Disable; + $self->{btn_print}->Disable; $self->{btn_send_gcode}->Disable; } diff --git a/xs/src/libslic3r/GCodeSender.cpp b/xs/src/libslic3r/GCodeSender.cpp index 0fdb5c2bc..e0a6873e9 100644 --- a/xs/src/libslic3r/GCodeSender.cpp +++ b/xs/src/libslic3r/GCodeSender.cpp @@ -158,6 +158,34 @@ GCodeSender::resume_queue() this->send(); } +// purge log and return its contents +std::vector +GCodeSender::purge_log() +{ + boost::lock_guard l(this->log_mutex); + std::vector retval; + retval.reserve(this->log.size()); + while (!this->log.empty()) { + retval.push_back(this->log.front()); + this->log.pop(); + } + return retval; +} + +std::string +GCodeSender::getT() const +{ + boost::lock_guard l(this->log_mutex); + return this->T; +} + +std::string +GCodeSender::getB() const +{ + boost::lock_guard l(this->log_mutex); + return this->B; +} + void GCodeSender::do_close() { @@ -214,8 +242,10 @@ GCodeSender::on_read(const boost::system::error_code& error, } // copy the read buffer into string - std::string line((std::istreambuf_iterator(&this->read_buffer)), - std::istreambuf_iterator()); + std::istream is(&this->read_buffer); + std::string line; + std::getline(is, line); + // note that line might contain \r at its end // parse incoming line if (!this->connected @@ -236,10 +266,8 @@ GCodeSender::on_read(const boost::system::error_code& error, } else if (boost::istarts_with(line, "resend") // Marlin uses "Resend: " || boost::istarts_with(line, "rs")) { // extract the first number from line - using boost::lexical_cast; - using boost::bad_lexical_cast; boost::algorithm::trim_left_if(line, !boost::algorithm::is_digit()); - size_t toresend = lexical_cast(line.substr(0, line.find_first_not_of("0123456789"))); + size_t toresend = boost::lexical_cast(line.substr(0, line.find_first_not_of("0123456789"))); if (toresend == this->sent) { { boost::lock_guard l(this->queue_mutex); @@ -250,6 +278,28 @@ GCodeSender::on_read(const boost::system::error_code& error, } else { printf("Cannot resend %lu (last was %lu)\n", toresend, this->sent); } + } else if (boost::starts_with(line, "wait")) { + // ignore + } else { + // push any other line into the log + boost::lock_guard l(this->log_mutex); + this->log.push(line); + } + + // parse temperature info + { + size_t pos = line.find("T:"); + if (pos != std::string::npos && line.size() > pos + 2) { + // we got temperature info + boost::lock_guard l(this->log_mutex); + this->T = line.substr(pos+2, line.find_first_not_of("0123456789.", pos+2) - (pos+2)); + + pos = line.find("B:"); + if (pos != std::string::npos && line.size() > pos + 2) { + // we got bed temperature info + this->B = line.substr(pos+2, line.find_first_not_of("0123456789.", pos+2) - (pos+2)); + } + } } this->do_read(); diff --git a/xs/src/libslic3r/GCodeSender.hpp b/xs/src/libslic3r/GCodeSender.hpp index a97cabe9e..a09e7e5fe 100644 --- a/xs/src/libslic3r/GCodeSender.hpp +++ b/xs/src/libslic3r/GCodeSender.hpp @@ -27,6 +27,9 @@ class GCodeSender : private boost::noncopyable { size_t queue_size() const; void pause_queue(); void resume_queue(); + std::vector purge_log(); + std::string getT() const; + std::string getB() const; private: asio::io_service io; @@ -46,6 +49,11 @@ class GCodeSender : private boost::noncopyable { size_t sent; std::string last_sent; + // this mutex guards log, T, B + mutable boost::mutex log_mutex; + std::queue log; + std::string T, B; + void set_baud_rate(unsigned int baud_rate); void set_error_status(bool e); void do_send(const std::string &line); diff --git a/xs/xsp/GCodeSender.xsp b/xs/xsp/GCodeSender.xsp index e72a2e8e5..dc33284a8 100644 --- a/xs/xsp/GCodeSender.xsp +++ b/xs/xsp/GCodeSender.xsp @@ -18,6 +18,9 @@ void send(std::string s, bool priority = false); void pause_queue(); void resume_queue(); + std::vector purge_log(); + std::string getT(); + std::string getB(); }; #endif