Handle log, temperatures. Move controller to main tabpanel. More things

This commit is contained in:
Alessandro Ranellucci 2015-01-04 23:18:23 +01:00
parent 3ab4d4b094
commit 9af43bee52
8 changed files with 204 additions and 58 deletions

View File

@ -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) = @_;

View File

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

View File

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

View File

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

View File

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

View File

@ -158,6 +158,34 @@ GCodeSender::resume_queue()
this->send();
}
// purge log and return its contents
std::vector<std::string>
GCodeSender::purge_log()
{
boost::lock_guard<boost::mutex> l(this->log_mutex);
std::vector<std::string> 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<boost::mutex> l(this->log_mutex);
return this->T;
}
std::string
GCodeSender::getB() const
{
boost::lock_guard<boost::mutex> 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<char>(&this->read_buffer)),
std::istreambuf_iterator<char>());
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<size_t>(line.substr(0, line.find_first_not_of("0123456789")));
size_t toresend = boost::lexical_cast<size_t>(line.substr(0, line.find_first_not_of("0123456789")));
if (toresend == this->sent) {
{
boost::lock_guard<boost::mutex> 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<boost::mutex> 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<boost::mutex> 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();

View File

@ -27,6 +27,9 @@ class GCodeSender : private boost::noncopyable {
size_t queue_size() const;
void pause_queue();
void resume_queue();
std::vector<std::string> 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<std::string> 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);

View File

@ -18,6 +18,9 @@
void send(std::string s, bool priority = false);
void pause_queue();
void resume_queue();
std::vector<std::string> purge_log();
std::string getT();
std::string getB();
};
#endif