From c06ce3b58ccf6c3638d8feba157c9b1170734dec Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 31 Dec 2014 19:10:46 +0100 Subject: [PATCH] Initial work for a controller GUI --- lib/Slic3r/GUI.pm | 10 ++ lib/Slic3r/GUI/Controller/Frame.pm | 33 +++++ lib/Slic3r/GUI/Controller/PrinterPanel.pm | 142 ++++++++++++++++++++++ lib/Slic3r/GUI/MainFrame.pm | 4 + xs/src/libslic3r/GCodeSender.cpp | 83 ++++++++++++- xs/src/libslic3r/GCodeSender.hpp | 5 +- xs/xsp/GCodeSender.xsp | 5 +- 7 files changed, 276 insertions(+), 6 deletions(-) create mode 100644 lib/Slic3r/GUI/Controller/Frame.pm create mode 100644 lib/Slic3r/GUI/Controller/PrinterPanel.pm diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index e08e6362d..c0ba9fd8c 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -9,6 +9,8 @@ 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::PrinterPanel; use Slic3r::GUI::MainFrame; use Slic3r::GUI::Notifier; use Slic3r::GUI::Plater; @@ -292,4 +294,12 @@ 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}; +} + 1; diff --git a/lib/Slic3r/GUI/Controller/Frame.pm b/lib/Slic3r/GUI/Controller/Frame.pm new file mode 100644 index 000000000..a709b615c --- /dev/null +++ b/lib/Slic3r/GUI/Controller/Frame.pm @@ -0,0 +1,33 @@ +package Slic3r::GUI::Controller::Frame; +use strict; +use warnings; +use utf8; + +use Wx qw(:frame :id :misc :sizer); +use Wx::Event qw(EVT_CLOSE); +use base 'Wx::Frame'; + +sub new { + my ($class) = @_; + my $self = $class->SUPER::new(undef, -1, "Controller", wxDefaultPosition, [500,350], wxDEFAULT_FRAME_STYLE); + + $self->{sizer} = my $sizer = Wx::BoxSizer->new(wxVERTICAL); + $sizer->Add(Slic3r::GUI::Controller::PrinterPanel->new($self), 1, wxEXPAND); + + $self->SetSizer($sizer); + $self->SetMinSize($self->GetSize); + $sizer->SetSizeHints($self); + $self->Layout; + + EVT_CLOSE($self, sub { + my (undef, $event) = @_; + + # ... + + $event->Skip; + }); + + return $self; +} + +1; diff --git a/lib/Slic3r/GUI/Controller/PrinterPanel.pm b/lib/Slic3r/GUI/Controller/PrinterPanel.pm new file mode 100644 index 000000000..fcbbf16de --- /dev/null +++ b/lib/Slic3r/GUI/Controller/PrinterPanel.pm @@ -0,0 +1,142 @@ +package Slic3r::GUI::Controller::PrinterPanel; +use strict; +use warnings; +use utf8; + +use Wx qw(:panel :id :misc :sizer :button :bitmap); +use Wx::Event qw(EVT_BUTTON); +use base qw(Wx::Panel Class::Accessor); + +__PACKAGE__->mk_accessors(qw(sender)); + +sub new { + my ($class, $parent) = @_; + my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize); + + $self->{sizer} = my $sizer = Wx::StaticBoxSizer->new(Wx::StaticBox->new($self, -1, "Printer"), wxVERTICAL); + + # connection info + { + my $conn_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + $sizer->Add($conn_sizer, 0, wxEXPAND); + { + my $text = Wx::StaticText->new($self, -1, "Port:", wxDefaultPosition, wxDefaultSize); + $conn_sizer->Add($text, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); + } + { + $self->{serial_port_combobox} = Wx::ComboBox->new($self, -1, "", wxDefaultPosition, wxDefaultSize, []); + $self->scan_serial_ports; + $conn_sizer->Add($self->{serial_port_combobox}, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 1); + } + { + $self->{btn_rescan_serial} = my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG), + wxDefaultPosition, wxDefaultSize, &Wx::wxBORDER_NONE); + $conn_sizer->Add($btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); + EVT_BUTTON($self, $btn, sub { $self->scan_serial_ports }); + } + { + my $text = Wx::StaticText->new($self, -1, "Speed:", wxDefaultPosition, wxDefaultSize); + $conn_sizer->Add($text, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); + } + { + $self->{serial_speed_combobox} = Wx::ComboBox->new($self, -1, "250000", wxDefaultPosition, wxDefaultSize, + ["115200", "250000"]); + $conn_sizer->Add($self->{serial_speed_combobox}, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); + } + } + + # buttons + { + my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + $sizer->Add($buttons_sizer, 0, wxEXPAND); + { + $self->{btn_connect} = my $btn = Wx::Button->new($self, -1, "Connect", wxDefaultPosition, wxDefaultSize); + $buttons_sizer->Add($btn, 0, wxRIGHT, 5); + EVT_BUTTON($self, $btn, \&connect); + } + { + $self->{btn_disconnect} = my $btn = Wx::Button->new($self, -1, "Disconnect", wxDefaultPosition, wxDefaultSize); + $buttons_sizer->Add($btn, 0, wxRIGHT, 5); + EVT_BUTTON($self, $btn, \&disconnect); + } + } + + # status + $self->{status_text} = Wx::StaticText->new($self, -1, "Not connected", wxDefaultPosition, wxDefaultSize); + $sizer->Add($self->{status_text}, 0, wxEXPAND); + + $self->SetSizer($sizer); + $self->SetMinSize($self->GetSize); + $sizer->SetSizeHints($self); + + $self->_update_connection_controls; + + return $self; +} + +sub _update_connection_controls { + my ($self) = @_; + + if ($self->sender && $self->sender->is_connected) { + $self->{btn_connect}->Hide; + $self->{btn_disconnect}->Show; + $self->{serial_port_combobox}->Disable; + $self->{serial_speed_combobox}->Disable; + $self->{btn_rescan_serial}->Disable; + } else { + $self->{btn_connect}->Show; + $self->{btn_disconnect}->Hide; + $self->{serial_port_combobox}->Enable; + $self->{serial_speed_combobox}->Enable; + $self->{btn_rescan_serial}->Enable; + } +} + +sub set_status { + my ($self, $status) = @_; + $self->{status_text}->SetLabel($status); + $self->{status_text}->Refresh; +} + +sub connect { + my ($self) = @_; + + return if $self->sender && $self->sender->is_connected; + + $self->set_status("Connecting..."); + $self->sender(Slic3r::GCode::Sender->new); + my $res = $self->sender->connect( + $self->{serial_port_combobox}->GetValue, + $self->{serial_speed_combobox}->GetValue, + ); + if (!$res) { + $self->set_status("Connection failed"); + } + 1 until $self->sender->is_connected; + $self->set_status("Connected"); + $self->_update_connection_controls; +} + +sub disconnect { + my ($self) = @_; + + return if !$self->sender || !$self->sender->is_connected; + + $self->sender->disconnect; + $self->set_status("Not connected"); + $self->_update_connection_controls; +} + +sub scan_serial_ports { + my ($self) = @_; + + $self->{serial_port_combobox}->Clear; + + # TODO: Windows ports + + # UNIX and OS X + $self->{serial_port_combobox}->Append($_) + for glob '/dev/{ttyUSB,ttyACM,tty.,cu.,rfcomm}*'; +} + +1; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 4cd4db706..d23542e55 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -228,6 +228,10 @@ sub _init_menubar { $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", 'Show the printer controller', sub { + wxTheApp->show_printer_controller; + }); } # Help menu diff --git a/xs/src/libslic3r/GCodeSender.cpp b/xs/src/libslic3r/GCodeSender.cpp index f5398a699..8ed233c5f 100644 --- a/xs/src/libslic3r/GCodeSender.cpp +++ b/xs/src/libslic3r/GCodeSender.cpp @@ -7,22 +7,53 @@ #include #include +#if __APPLE__ +#include +#include +#include +#endif +#ifdef __linux +#include +#include +#include +#endif + namespace Slic3r { namespace asio = boost::asio; -GCodeSender::GCodeSender(std::string devname, unsigned int baud_rate) +GCodeSender::GCodeSender() : io(), serial(io), can_send(false), sent(0), error(false), connected(false) +{} + +GCodeSender::~GCodeSender() { - this->serial.open(devname); - this->serial.set_option(asio::serial_port_base::baud_rate(baud_rate)); + this->disconnect(); +} + +bool +GCodeSender::connect(std::string devname, unsigned int baud_rate) +{ + + try { + this->serial.open(devname); + } catch (boost::system::system_error &e) { + this->error = true; + return false; + } + this->serial.set_option(asio::serial_port_base::parity(asio::serial_port_base::parity::odd)); this->serial.set_option(asio::serial_port_base::character_size(asio::serial_port_base::character_size(8))); this->serial.set_option(asio::serial_port_base::flow_control(asio::serial_port_base::flow_control::none)); this->serial.set_option(asio::serial_port_base::stop_bits(asio::serial_port_base::stop_bits::one)); + this->set_baud_rate(baud_rate); + this->serial.close(); this->serial.open(devname); this->serial.set_option(asio::serial_port_base::parity(asio::serial_port_base::parity::none)); + + // set baud rate again because set_option overwrote it + this->set_baud_rate(baud_rate); this->open = true; // this gives some work to the io_service before it is started @@ -32,6 +63,52 @@ GCodeSender::GCodeSender(std::string devname, unsigned int baud_rate) // start reading in the background thread boost::thread t(boost::bind(&asio::io_service::run, &this->io)); this->background_thread.swap(t); + + return true; +} + +void +GCodeSender::set_baud_rate(unsigned int baud_rate) +{ + try { + // This does not support speeds > 115200 + this->serial.set_option(asio::serial_port_base::baud_rate(baud_rate)); + } catch (boost::system::system_error &e) { + boost::asio::serial_port::native_handle_type handle = this->serial.native_handle(); + +#if __APPLE__ + termios ios; + ::tcgetattr(handle, &ios); + ::cfsetspeed(&ios, baud_rate); + speed_t newSpeed = baud_rate; + ioctl(handle, IOSSIOSPEED, &newSpeed); + ::tcsetattr(handle, TCSANOW, &ios); +#endif +#ifdef __linux + termios ios; + ::tcgetattr(handle, &ios); + ::cfsetispeed(&ios, B38400); + ::cfsetospeed(&ios, B38400); + ::tcflush(handle, TCIFLUSH); + ::tcsetattr(handle, TCSANOW, &ios); + + struct serial_struct ss; + ioctl(handle, TIOCGSERIAL, &ss); + ss.flags = (ss.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST; + ss.custom_divisor = (ss.baud_base + (baud_rate / 2)) / baud_rate; + //cout << "bbase " << ss.baud_base << " div " << ss.custom_divisor; + long closestSpeed = ss.baud_base / ss.custom_divisor; + //cout << " Closest speed " << closestSpeed << endl; + ss.reserved_char[0] = 0; + if (closestSpeed < baud * 98 / 100 || closestSpeed > baud_rate * 102 / 100) { + throw std::exception("Failed to set baud rate"); + } + + ioctl(handle, TIOCSSERIAL, &ss); +#else + //throw invalid_argument ("OS does not currently support custom bauds"); +#endif + } } void diff --git a/xs/src/libslic3r/GCodeSender.hpp b/xs/src/libslic3r/GCodeSender.hpp index ef68849fb..17c4f0485 100644 --- a/xs/src/libslic3r/GCodeSender.hpp +++ b/xs/src/libslic3r/GCodeSender.hpp @@ -16,7 +16,9 @@ namespace asio = boost::asio; class GCodeSender : private boost::noncopyable { public: - GCodeSender(std::string devname, unsigned int baud_rate); + GCodeSender(); + ~GCodeSender(); + bool connect(std::string devname, unsigned int baud_rate); void send(const std::vector &lines); void send(const std::string &s); void disconnect(); @@ -39,6 +41,7 @@ class GCodeSender : private boost::noncopyable { bool can_send; size_t sent; + void set_baud_rate(unsigned int baud_rate); void set_error_status(bool e); void do_close(); void do_read(); diff --git a/xs/xsp/GCodeSender.xsp b/xs/xsp/GCodeSender.xsp index 150c8c64b..b0ff3da22 100644 --- a/xs/xsp/GCodeSender.xsp +++ b/xs/xsp/GCodeSender.xsp @@ -8,12 +8,13 @@ %} %name{Slic3r::GCode::Sender} class GCodeSender { - GCodeSender(std::string port, unsigned int baud_rate); + GCodeSender(); ~GCodeSender(); + bool connect(std::string port, unsigned int baud_rate); + void disconnect(); bool is_connected() const; int queue_size() const; - void disconnect(); void send(std::string s); };