Integration with Octoprint. #1826

This commit is contained in:
Alessandro Ranellucci 2014-12-28 01:30:05 +01:00
parent eba19aaba4
commit efe7d5f857
6 changed files with 104 additions and 10 deletions

View File

@ -27,6 +27,7 @@ my %prereqs = qw(
); );
my %recommends = qw( my %recommends = qw(
Class::XSAccessor 0 Class::XSAccessor 0
LWP::UserAgent 0
XML::SAX::ExpatXS 0 XML::SAX::ExpatXS 0
); );

View File

@ -27,6 +27,7 @@ use Slic3r::GUI::SimpleTab;
use Slic3r::GUI::Tab; use Slic3r::GUI::Tab;
our $have_OpenGL = eval "use Slic3r::GUI::PreviewCanvas; 1"; our $have_OpenGL = eval "use Slic3r::GUI::PreviewCanvas; 1";
our $have_LWP = eval "use LWP::UserAgent; 1";
use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow
:filedialog); :filedialog);
@ -228,7 +229,7 @@ sub have_version_check {
my ($self) = @_; my ($self) = @_;
# return an explicit 0 # return an explicit 0
return ($Slic3r::have_threads && $Slic3r::build && eval "use LWP::UserAgent; 1") || 0; return ($Slic3r::have_threads && $Slic3r::build && $have_LWP) || 0;
} }
sub check_version { sub check_version {

View File

@ -48,6 +48,7 @@ sub new {
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
$self->{config} = Slic3r::Config->new_from_defaults(qw( $self->{config} = Slic3r::Config->new_from_defaults(qw(
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width
octoprint_host octoprint_apikey
)); ));
$self->{model} = Slic3r::Model->new; $self->{model} = Slic3r::Model->new;
$self->{print} = Slic3r::Print->new; $self->{print} = Slic3r::Print->new;
@ -181,9 +182,11 @@ sub new {
# right pane buttons # right pane buttons
$self->{btn_export_gcode} = Wx::Button->new($self, -1, "Export G-code…", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_export_gcode} = Wx::Button->new($self, -1, "Export G-code…", wxDefaultPosition, [-1, 30], wxBU_LEFT);
$self->{btn_send_gcode} = Wx::Button->new($self, -1, "Send to printer", wxDefaultPosition, [-1, 30], wxBU_LEFT);
$self->{btn_export_stl} = Wx::Button->new($self, -1, "Export STL…", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_export_stl} = Wx::Button->new($self, -1, "Export STL…", wxDefaultPosition, [-1, 30], wxBU_LEFT);
#$self->{btn_export_gcode}->SetFont($Slic3r::GUI::small_font); #$self->{btn_export_gcode}->SetFont($Slic3r::GUI::small_font);
#$self->{btn_export_stl}->SetFont($Slic3r::GUI::small_font); #$self->{btn_export_stl}->SetFont($Slic3r::GUI::small_font);
$self->{btn_send_gcode}->Hide;
if ($Slic3r::GUI::have_button_icons) { if ($Slic3r::GUI::have_button_icons) {
my %icons = qw( my %icons = qw(
@ -192,6 +195,7 @@ sub new {
reset cross.png reset cross.png
arrange bricks.png arrange bricks.png
export_gcode cog_go.png export_gcode cog_go.png
send_gcode cog_go.png
export_stl brick_go.png export_stl brick_go.png
increase add.png increase add.png
@ -213,6 +217,10 @@ sub new {
$self->export_gcode; $self->export_gcode;
Slic3r::thread_cleanup(); Slic3r::thread_cleanup();
}); });
EVT_BUTTON($self, $self->{btn_send_gcode}, sub {
$self->{send_gcode_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir());
Slic3r::thread_cleanup();
});
#EVT_BUTTON($self, $self->{btn_export_stl}, \&export_stl); #EVT_BUTTON($self, $self->{btn_export_stl}, \&export_stl);
if ($self->{htoolbar}) { if ($self->{htoolbar}) {
@ -355,6 +363,7 @@ sub new {
my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL); my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
$buttons_sizer->AddStretchSpacer(1); $buttons_sizer->AddStretchSpacer(1);
$buttons_sizer->Add($self->{btn_export_stl}, 0, wxALIGN_RIGHT, 0); $buttons_sizer->Add($self->{btn_export_stl}, 0, wxALIGN_RIGHT, 0);
$buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT, 0);
$buttons_sizer->Add($self->{btn_export_gcode}, 0, wxALIGN_RIGHT, 0); $buttons_sizer->Add($self->{btn_export_gcode}, 0, wxALIGN_RIGHT, 0);
my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); my $right_sizer = Wx::BoxSizer->new(wxVERTICAL);
@ -944,7 +953,7 @@ sub resume_background_process {
} }
sub export_gcode { sub export_gcode {
my $self = shift; my ($self, $output_file) = @_;
return if !@{$self->{objects}}; return if !@{$self->{objects}};
@ -976,14 +985,14 @@ sub export_gcode {
} }
# select output file # select output file
$self->{export_gcode_output_file} = $main::opt{output}; if ($output_file) {
{ $self->{export_gcode_output_file} = $self->{print}->expanded_output_filepath($output_file);
my $default_output_file = $self->{print}->expanded_output_filepath($self->{export_gcode_output_file}); } else {
my $default_output_file = $self->{print}->expanded_output_filepath($main::opt{output});
my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', wxTheApp->output_path(dirname($default_output_file)), my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', wxTheApp->output_path(dirname($default_output_file)),
basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE); basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE);
if ($dlg->ShowModal != wxID_OK) { if ($dlg->ShowModal != wxID_OK) {
$dlg->Destroy; $dlg->Destroy;
$self->{export_gcode_output_file} = undef;
return; return;
} }
$Slic3r::GUI::Settings->{_}{last_output_path} = dirname($dlg->GetPath); $Slic3r::GUI::Settings->{_}{last_output_path} = dirname($dlg->GetPath);
@ -1011,6 +1020,8 @@ sub export_gcode {
my $result = !Slic3r::GUI::catch_error($self); my $result = !Slic3r::GUI::catch_error($self);
$self->on_export_completed($result); $self->on_export_completed($result);
} }
return $self->{export_gcode_output_file};
} }
# This gets called only if we have threads. # This gets called only if we have threads.
@ -1072,14 +1083,49 @@ sub on_export_completed {
$self->{export_thread} = undef; $self->{export_thread} = undef;
my $message; my $message;
my $send_gcode = 0;
if ($result) { if ($result) {
$message = "G-code file exported to " . $self->{export_gcode_output_file}; if ($self->{send_gcode_file}) {
$message = "Sending G-code file to the Octoprint server...";
$send_gcode = 1;
} else {
$message = "G-code file exported to " . $self->{export_gcode_output_file};
}
} else { } else {
$message = "Export failed"; $message = "Export failed";
} }
$self->{export_gcode_output_file} = undef; $self->{export_gcode_output_file} = undef;
$self->statusbar->SetStatusText($message); $self->statusbar->SetStatusText($message);
wxTheApp->notify($message); wxTheApp->notify($message);
$self->send_gcode if $send_gcode;
$self->{send_gcode_file} = undef;
}
sub send_gcode {
my ($self) = @_;
$self->statusbar->StartBusy;
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
my $res = $ua->post(
"http://" . $self->{config}->octoprint_host . "/api/files/local",
Content_Type => 'form-data',
'X-Api-Key' => $self->{config}->octoprint_apikey,
Content => [
fn => [$self->{send_gcode_file}],
],
);
$self->statusbar->StopBusy;
if ($res->is_success) {
$self->statusbar->SetStatusText("G-code file successfully uploaded to the Octoprint server");
} else {
$self->statusbar->SetStatusText("Error while uploading to the Octoprint server: " . $res->status_line);
}
} }
sub export_stl { sub export_stl {
@ -1208,6 +1254,13 @@ sub on_config_change {
$self->{canvas}->update_bed_size; $self->{canvas}->update_bed_size;
$self->{canvas3D}->update_bed_size if $self->{canvas3D}; $self->{canvas3D}->update_bed_size if $self->{canvas3D};
$self->update; $self->update;
} elsif ($opt_key eq 'octoprint_host') {
if ($config->get('octoprint_host')) {
$self->{btn_send_gcode}->Show;
} else {
$self->{btn_send_gcode}->Hide;
}
$self->Layout;
} }
} }
@ -1313,7 +1366,7 @@ sub object_list_changed {
my $have_objects = @{$self->{objects}} ? 1 : 0; my $have_objects = @{$self->{objects}} ? 1 : 0;
my $method = $have_objects ? 'Enable' : 'Disable'; my $method = $have_objects ? 'Enable' : 'Disable';
$self->{"btn_$_"}->$method $self->{"btn_$_"}->$method
for grep $self->{"btn_$_"}, qw(reset arrange export_gcode export_stl); for grep $self->{"btn_$_"}, qw(reset arrange export_gcode export_stl send_gcode);
if ($self->{htoolbar}) { if ($self->{htoolbar}) {
$self->{htoolbar}->EnableTool($_, $have_objects) $self->{htoolbar}->EnableTool($_, $have_objects)

View File

@ -923,6 +923,7 @@ sub build {
$self->init_config_options(qw( $self->init_config_options(qw(
bed_shape z_offset bed_shape z_offset
gcode_flavor use_relative_e_distances gcode_flavor use_relative_e_distances
octoprint_host octoprint_apikey
use_firmware_retraction pressure_advance vibration_limit use_firmware_retraction pressure_advance vibration_limit
start_gcode end_gcode layer_gcode toolchange_gcode start_gcode end_gcode layer_gcode toolchange_gcode
nozzle_diameter extruder_offset nozzle_diameter extruder_offset
@ -996,6 +997,11 @@ sub build {
} }
}); });
} }
{
my $optgroup = $page->new_optgroup('Octoprint upload');
$optgroup->append_single_option_line('octoprint_host');
$optgroup->append_single_option_line('octoprint_apikey');
}
{ {
my $optgroup = $page->new_optgroup('Advanced'); my $optgroup = $page->new_optgroup('Advanced');
$optgroup->append_single_option_line('use_firmware_retraction'); $optgroup->append_single_option_line('use_firmware_retraction');
@ -1129,6 +1135,8 @@ sub _update {
my $config = $self->{config}; my $config = $self->{config};
$self->get_field('octoprint_apikey')->toggle($config->get('octoprint_host'));
my $have_multiple_extruders = $self->{extruders_count} > 1; my $have_multiple_extruders = $self->{extruders_count} > 1;
$self->get_field('toolchange_gcode')->toggle($have_multiple_extruders); $self->get_field('toolchange_gcode')->toggle($have_multiple_extruders);
@ -1319,8 +1327,8 @@ sub config {
} }
# apply preset values on top of defaults # apply preset values on top of defaults
my $config = Slic3r::Config->new_from_defaults(@$keys);
my $external_config = Slic3r::Config->load($self->file); my $external_config = Slic3r::Config->load($self->file);
my $config = Slic3r::Config->new;
$config->set($_, $external_config->get($_)) $config->set($_, $external_config->get($_))
for grep $external_config->has($_), @$keys; for grep $external_config->has($_), @$keys;

View File

@ -498,6 +498,16 @@ PrintConfigDef::build_def() {
Options["nozzle_diameter"].sidetext = "mm"; Options["nozzle_diameter"].sidetext = "mm";
Options["nozzle_diameter"].cli = "nozzle-diameter=f@"; Options["nozzle_diameter"].cli = "nozzle-diameter=f@";
Options["octoprint_apikey"].type = coString;
Options["octoprint_apikey"].label = "API Key";
Options["octoprint_apikey"].tooltip = "Slic3r can upload G-code files to Octoprint. This field should contain the API Key required for authentication.";
Options["octoprint_apikey"].cli = "octoprint-apikey=s";
Options["octoprint_host"].type = coString;
Options["octoprint_host"].label = "Host or IP";
Options["octoprint_host"].tooltip = "Slic3r can upload G-code files to Octoprint. This field should contain the hostname or IP address of the Octoprint instance.";
Options["octoprint_host"].cli = "octoprint-host=s";
Options["only_retract_when_crossing_perimeters"].type = coBool; Options["only_retract_when_crossing_perimeters"].type = coBool;
Options["only_retract_when_crossing_perimeters"].label = "Only retract when crossing perimeters"; Options["only_retract_when_crossing_perimeters"].label = "Only retract when crossing perimeters";
Options["only_retract_when_crossing_perimeters"].tooltip = "Disables retraction when the travel path does not exceed the upper layer's perimeters (and thus any ooze will be probably invisible)."; Options["only_retract_when_crossing_perimeters"].tooltip = "Disables retraction when the travel path does not exceed the upper layer's perimeters (and thus any ooze will be probably invisible).";

View File

@ -582,7 +582,27 @@ class PrintConfig : public GCodeConfig
}; };
}; };
class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig class HostConfig : public virtual StaticPrintConfig
{
public:
ConfigOptionString octoprint_host;
ConfigOptionString octoprint_apikey;
HostConfig() : StaticPrintConfig() {
this->octoprint_host.value = "";
this->octoprint_apikey.value = "";
};
ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
if (opt_key == "octoprint_host") return &this->octoprint_host;
if (opt_key == "octoprint_apikey") return &this->octoprint_apikey;
return NULL;
};
};
class FullPrintConfig
: public PrintObjectConfig, public PrintRegionConfig, public PrintConfig, public HostConfig
{ {
public: public:
ConfigOption* option(const t_config_option_key opt_key, bool create = false) { ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
@ -590,6 +610,7 @@ class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, publ
if ((opt = PrintObjectConfig::option(opt_key, create)) != NULL) return opt; if ((opt = PrintObjectConfig::option(opt_key, create)) != NULL) return opt;
if ((opt = PrintRegionConfig::option(opt_key, create)) != NULL) return opt; if ((opt = PrintRegionConfig::option(opt_key, create)) != NULL) return opt;
if ((opt = PrintConfig::option(opt_key, create)) != NULL) return opt; if ((opt = PrintConfig::option(opt_key, create)) != NULL) return opt;
if ((opt = HostConfig::option(opt_key, create)) != NULL) return opt;
return NULL; return NULL;
}; };
}; };