Merge branch 'SoftFever' into feature/small_perimeter

This commit is contained in:
SoftFever 2022-10-24 14:53:58 +08:00
commit 397e0f0a29
151 changed files with 7256 additions and 357 deletions

View file

@ -65,19 +65,19 @@ std::string AppConfig::get_hms_host()
{
std::string sel = get("iot_environment");
std::string host = "";
#if !BBL_RELEASE_TO_PUBLIC
if (sel == ENV_DEV_HOST)
host = "e-dev.bambu-lab.com";
else if (sel == ENV_QAT_HOST)
host = "e-qa.bambu-lab.com";
else if (sel == ENV_PRE_HOST)
host = "e-pre.bambu-lab.com";
else if (sel == ENV_PRODUCT_HOST)
host = "e.bambulab.com";
return host;
#else
// #if !BBL_RELEASE_TO_PUBLIC
// if (sel == ENV_DEV_HOST)
// host = "e-dev.bambu-lab.com";
// else if (sel == ENV_QAT_HOST)
// host = "e-qa.bambu-lab.com";
// else if (sel == ENV_PRE_HOST)
// host = "e-pre.bambu-lab.com";
// else if (sel == ENV_PRODUCT_HOST)
// host = "e.bambulab.com";
// return host;
// #else
return "e.bambulab.com";
#endif
// #endif
}
void AppConfig::reset()
@ -293,15 +293,15 @@ void AppConfig::set_defaults()
set("curr_bed_type", "0");
}
#if BBL_RELEASE_TO_PUBLIC
// #if BBL_RELEASE_TO_PUBLIC
if (get("iot_environment").empty()) {
set("iot_environment", "3");
}
#else
if (get("iot_environment").empty()) {
set("iot_environment", "1");
}
#endif
// #else
// if (get("iot_environment").empty()) {
// set("iot_environment", "1");
// }
// #endif
// Remove legacy window positions/sizes
erase("app", "main_frame_maximized");
@ -1030,29 +1030,29 @@ void AppConfig::update_last_backup_dir(const std::string& dir)
std::string AppConfig::get_region()
{
#if BBL_RELEASE_TO_PUBLIC
// #if BBL_RELEASE_TO_PUBLIC
return this->get("region");
#else
std::string sel = get("iot_environment");
std::string region;
if (sel == ENV_DEV_HOST)
region = "ENV_CN_DEV";
else if (sel == ENV_QAT_HOST)
region = "ENV_CN_QA";
else if (sel == ENV_PRE_HOST)
region = "ENV_CN_PRE";
if (region.empty())
return this->get("region");
return region;
#endif
// #else
// std::string sel = get("iot_environment");
// std::string region;
// if (sel == ENV_DEV_HOST)
// region = "ENV_CN_DEV";
// else if (sel == ENV_QAT_HOST)
// region = "ENV_CN_QA";
// else if (sel == ENV_PRE_HOST)
// region = "ENV_CN_PRE";
// if (region.empty())
// return this->get("region");
// return region;
// #endif
}
std::string AppConfig::get_country_code()
{
std::string region = get_region();
#if !BBL_RELEASE_TO_PUBLIC
if (is_engineering_region()) { return region; }
#endif
// #if !BBL_RELEASE_TO_PUBLIC
// if (is_engineering_region()) { return region; }
// #endif
if (region == "CHN" || region == "China")
return "CN";
else if (region == "USA")

View file

@ -55,6 +55,16 @@ inline bool is_perimeter(ExtrusionRole role)
|| role == erOverhangPerimeter;
}
inline bool is_internal_perimeter(ExtrusionRole role)
{
return role == erPerimeter;
}
inline bool is_external_perimeter(ExtrusionRole role)
{
return role == erExternalPerimeter;
}
inline bool is_infill(ExtrusionRole role)
{
return role == erBridgeInfill

View file

@ -92,7 +92,7 @@ struct SurfaceFillParams
this->overlap == rhs.overlap &&
this->angle == rhs.angle &&
this->bridge == rhs.bridge &&
// this->bridge_angle == rhs.bridge_angle &&
this->bridge_angle == rhs.bridge_angle &&
this->density == rhs.density &&
// this->dont_adjust == rhs.dont_adjust &&
this->anchor_length == rhs.anchor_length &&

View file

@ -14,7 +14,7 @@
#include "libslic3r.h"
#include "LocalesUtils.hpp"
#include "libslic3r/format.hpp"
#include "Time.hpp"
#include <algorithm>
#include <cstdlib>
#include <chrono>
@ -79,7 +79,7 @@ static const float g_min_purge_volume = 100.f;
static const float g_purge_volume_one_time = 135.f;
static const int g_max_flush_count = 4;
bool GCode::gcode_label_objects = false;
bool GCode::gcode_label_objects = true;
// Only add a newline in case the current G-code does not end with a newline.
static inline void check_add_eol(std::string& gcode)
@ -908,6 +908,11 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
BOOST_LOG_TRIVIAL(info) << boost::format("Will export G-code to %1% soon")%path;
print->set_started(psGCodeExport);
if (print->is_BBL_printer())
gcode_label_objects = false;
else
gcode_label_objects = true;
// check if any custom gcode contains keywords used by the gcode processor to
// produce time estimation and gcode toolpaths
std::vector<std::pair<std::string, std::string>> validation_res = DoExport::validate_custom_gcode(*print);
@ -1164,10 +1169,10 @@ namespace DoExport {
print_statistics.clear();
print_statistics.total_toolchanges = std::max(0, wipe_tower_data.number_of_toolchanges);
if (! extruders.empty()) {
//std::pair<std::string, unsigned int> out_filament_used_mm ("; filament used [mm] = ", 0);
//std::pair<std::string, unsigned int> out_filament_used_cm3("; filament used [cm3] = ", 0);
//std::pair<std::string, unsigned int> out_filament_used_g ("; filament used [g] = ", 0);
//std::pair<std::string, unsigned int> out_filament_cost ("; filament cost = ", 0);
std::pair<std::string, unsigned int> out_filament_used_mm ("; filament used [mm] = ", 0);
std::pair<std::string, unsigned int> out_filament_used_cm3("; filament used [cm3] = ", 0);
std::pair<std::string, unsigned int> out_filament_used_g ("; filament used [g] = ", 0);
std::pair<std::string, unsigned int> out_filament_cost ("; filament cost = ", 0);
for (const Extruder &extruder : extruders) {
double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f);
double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
@ -1187,14 +1192,14 @@ namespace DoExport {
dst.first += buf;
++ dst.second;
};
//append(out_filament_used_mm, "%.2lf", used_filament);
//append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001);
append(out_filament_used_mm, "%.2lf", used_filament);
append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001);
if (filament_weight > 0.) {
print_statistics.total_weight = print_statistics.total_weight + filament_weight;
//append(out_filament_used_g, "%.2lf", filament_weight);
append(out_filament_used_g, "%.2lf", filament_weight);
if (filament_cost > 0.) {
print_statistics.total_cost = print_statistics.total_cost + filament_cost;
//append(out_filament_cost, "%.2lf", filament_cost);
append(out_filament_cost, "%.2lf", filament_cost);
}
}
print_statistics.total_used_filament += used_filament;
@ -1202,12 +1207,12 @@ namespace DoExport {
print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
}
//filament_stats_string_out += out_filament_used_mm.first;
//filament_stats_string_out += "\n" + out_filament_used_cm3.first;
//if (out_filament_used_g.second)
//filament_stats_string_out += "\n" + out_filament_used_g.first;
//if (out_filament_cost.second)
// filament_stats_string_out += "\n" + out_filament_cost.first;
filament_stats_string_out += out_filament_used_mm.first;
filament_stats_string_out += "\n" + out_filament_used_cm3.first;
if (out_filament_used_g.second)
filament_stats_string_out += "\n" + out_filament_used_g.first;
if (out_filament_cost.second)
filament_stats_string_out += "\n" + out_filament_cost.first;
}
return filament_stats_string_out;
}
@ -1285,7 +1290,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// modifies m_silent_time_estimator_enabled
DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled);
const bool is_bbl_printers = print.is_BBL_printer();
// resets analyzer's tracking data
m_last_height = 0.f;
m_last_layer_z = 0.f;
@ -1349,30 +1354,65 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
file.write_format("; HEADER_BLOCK_END\n\n");
//BBS: write global config at the beginning of gcode file because printer need these config information
// Append full config, delimited by two 'phony' configuration keys CONFIG_BLOCK_START and CONFIG_BLOCK_END.
// The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer.
// BBS: write global config at the beginning of gcode file because printer
// need these config information
// Append full config, delimited by two 'phony' configuration keys
// CONFIG_BLOCK_START and CONFIG_BLOCK_END. The delimiters are structured
// as configuration key / value pairs to be parsable by older versions of
// PrusaSlicer G-code viewer.
{
if (is_bbl_printers) {
file.write("; CONFIG_BLOCK_START\n");
std::string full_config;
append_full_config(print, full_config);
if (!full_config.empty())
file.write(full_config);
file.write(full_config);
// SoftFever: write compatiple image
std::vector<int> temps_per_bed;
int first_layer_bed_temperature = 0;
get_bed_temperature(0, true, temps_per_bed,
first_layer_bed_temperature);
file.write_format("; first_layer_bed_temperature = %d\n",
first_layer_bed_temperature);
file.write_format(
"; first_layer_temperature = %d\n",
print.config().nozzle_temperature_initial_layer.get_at(0));
file.write("; CONFIG_BLOCK_END\n\n");
} else {
file.write_format("; hack-fix: write fake slicer info here so that "
"Moonraker will extract thumbs.\n");
file.write_format(
"; %s\n\n",
std::string(
std::string("generated by SuperSlicer " SLIC3R_VERSION " on ") +
Slic3r::Utils::utc_timestamp())
.c_str());
// BBS: add plate id into thumbnail render logic
// if (const auto [thumbnails, thumbnails_format] = std::make_pair(
// print.full_print_config().option<ConfigOptionPoints>("thumbnails"),
// print.full_print_config().option<ConfigOptionEnum<GCodeThumbnailsFormat>>("thumbnails_format"));
// thumbnails)
// GCodeThumbnails::export_thumbnails_to_file(
// thumbnail_cb, thumbnails->values, thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG,
// [&file](const char* sz) { file.write(sz); },
// [&print]() { print.throw_if_canceled(); });
DoExport::export_thumbnails_to_file(
thumbnail_cb, print.get_plate_index(), print.full_print_config().option<ConfigOptionPoints>("thumbnails")->values,
[&file](const char *sz) { file.write(sz); },
[&print]() { print.throw_if_canceled(); });
}
}
//BBS: add plate id into thumbnail render logic
//DoExport::export_thumbnails_to_file(thumbnail_cb, print.get_plate_index(), THUMBNAIL_SIZE,
// [&file](const char* sz) { file.write(sz); },
// [&print]() { print.throw_if_canceled(); });
// Write some terse information on the slicing parameters.
const PrintObject *first_object = print.objects().front();
const double layer_height = first_object->config().layer_height.value;
const double initial_layer_print_height = print.config().initial_layer_print_height.value;
//BBS: remove useless information in gcode file
#if 0
for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) {
const PrintRegion &region = print.get_print_region(region_id);
file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width());
@ -1386,8 +1426,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, initial_layer_print_height, true).width());
file.write_format("\n");
}
print.throw_if_canceled();
#endif
file.write_format("; EXECUTABLE_BLOCK_START\n");
// adds tags for time estimators
@ -1512,7 +1550,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
m_placeholder_parser.set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
}
float outer_wall_volumetric_speed = 0.0f;
{
int curr_bed_type = m_config.curr_bed_type.getInt();
@ -1524,6 +1562,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
m_placeholder_parser.set("bed_temperature", new ConfigOptionInts(*bed_temp_opt));
m_placeholder_parser.set("bed_temperature_initial_layer_single", new ConfigOptionInt(first_bed_temp_opt->get_at(initial_extruder_id)));
m_placeholder_parser.set("bed_temperature_initial_layer_vector", new ConfigOptionString(""));
m_placeholder_parser.set("chamber_temperature",new ConfigOptionInt(m_config.chamber_temperature));
//BBS: calculate the volumetric speed of outer wall. Ignore pre-object setting and multi-filament, and just use the default setting
{
@ -1535,7 +1574,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
}
Flow outer_wall_flow = Flow(outer_wall_line_width, m_config.layer_height, m_config.nozzle_diameter.get_at(initial_extruder_id));
float outer_wall_speed = print.default_region_config().outer_wall_speed.value;
float outer_wall_volumetric_speed = outer_wall_speed * outer_wall_flow.mm3_per_mm();
outer_wall_volumetric_speed = outer_wall_speed * outer_wall_flow.mm3_per_mm();
if (outer_wall_volumetric_speed > filament_max_volumetric_speed)
outer_wall_volumetric_speed = filament_max_volumetric_speed;
m_placeholder_parser.set("outer_wall_volumetric_speed", new ConfigOptionFloat(outer_wall_volumetric_speed));
@ -1598,8 +1637,23 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
if (this->m_objsWithBrim.empty() && this->m_objSupportsWithBrim.empty()) m_brim_done = true;
//BBS: open spaghetti detector
// if (print.config().spaghetti_detector.value)
file.write("M981 S1 P20000 ;open spaghetti detector\n");
if (is_bbl_printers) {
// if (print.config().spaghetti_detector.value)
file.write("M981 S1 P20000 ;open spaghetti detector\n");
file.write_format("M900 K%.3f M%0.3f ; Override pressure advance value\n",
m_config.pressure_advance.values.front(),
outer_wall_volumetric_speed / (1.75 * 1.75 / 4 * 3.14) *
m_config.pressure_advance.values.front());
} else {
if (m_config.enable_pressure_advance.value) {
if(print.config().gcode_flavor.value == gcfKlipper)
file.write_format("SET_PRESSURE_ADVANCE ADVANCE=%.3f ; Override pressure advance value\n",
m_config.pressure_advance.values.front());
else
file.write_format("M900 K%.3f ; Override pressure advance value\n",
m_config.pressure_advance.values.front());
}
}
// Do all objects for each layer.
if (print.config().print_sequence == PrintSequence::ByObject) {
@ -1663,7 +1717,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
this->process_layers(print, tool_ordering, collect_layers_to_print(object), *print_object_instance_sequential_active - object.instances().data(), file, prime_extruder);
//BBS: close powerlost recovery
{
if (m_second_layer_things_done) {
if (is_bbl_printers && m_second_layer_things_done) {
file.write("; close powerlost recovery\n");
file.write("M1003 S0\n");
}
@ -1734,7 +1788,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print, file);
//BBS: close powerlost recovery
{
if (m_second_layer_things_done) {
if (is_bbl_printers && m_second_layer_things_done) {
file.write("; close powerlost recovery\n");
file.write("M1003 S0\n");
}
@ -1754,10 +1808,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
file.write(m_writer.set_fan(0));
//BBS: make sure the additional fan is closed when end
file.write(m_writer.set_additional_fan(0));
//BBS: close spaghetti detector
//Note: M981 is also used to tell xcam the last layer is finished, so we need always send it even if spaghetti option is disabled.
//if (print.config().spaghetti_detector.value)
file.write("M981 S0 P20000 ; close spaghetti detector\n");
if (is_bbl_printers) {
//BBS: close spaghetti detector
//Note: M981 is also used to tell xcam the last layer is finished, so we need always send it even if spaghetti option is disabled.
//if (print.config().spaghetti_detector.value)
file.write("M981 S0 P20000 ; close spaghetti detector\n");
}
// adds tag for processor
file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
@ -1800,11 +1856,41 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
m_writer.extruders(),
// Modifies
print.m_print_statistics));
//file.write("\n");
//file.write_format("; total filament weight [g] = %.2lf\n", print.m_print_statistics.total_weight);
//file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost);
//if (print.m_print_statistics.total_toolchanges > 0)
// file.write_format("; total filament change = %i\n", print.m_print_statistics.total_toolchanges);
if (!is_bbl_printers) {
file.write("\n");
file.write("; CONFIG_BLOCK_START\n");
std::string full_config;
append_full_config(print, full_config);
if (!full_config.empty())
file.write(full_config);
// SoftFever: write compatiple info
std::vector<int> temps_per_bed;
int first_layer_bed_temperature = 0;
get_bed_temperature(0, true, temps_per_bed, first_layer_bed_temperature);
file.write_format("; first_layer_bed_temperature = %d\n",
first_layer_bed_temperature);
file.write_format(
"; first_layer_temperature = %d\n",
print.config().nozzle_temperature_initial_layer.get_at(0));
file.write("; CONFIG_BLOCK_END\n\n");
file.write_format("; total filament used [g] = %.2lf\n",
print.m_print_statistics.total_weight);
file.write_format("; total filament cost = %.2lf\n",
print.m_print_statistics.total_cost);
if (print.m_print_statistics.total_toolchanges > 0)
file.write_format("; total filament change = %i\n",
print.m_print_statistics.total_toolchanges);
file.write_format("; total layers count = %i\n", m_layer_count);
file.write_format(
";%s\n",
GCodeProcessor::reserved_tag(
GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder)
.c_str());
}
file.write("\n");
print.throw_if_canceled();
}
@ -2501,28 +2587,41 @@ GCode::LayerResult GCode::process_layer(
double acceleration = m_config.initial_layer_acceleration.value;
gcode += m_writer.set_acceleration((unsigned int)floor(acceleration + 0.5));
}
if (m_config.default_jerk.value > 0 && m_config.initial_layer_jerk.value > 0) {
double jerk = m_config.initial_layer_jerk.value;
gcode += m_writer.set_jerk_xy((unsigned int)floor(jerk + 0.5));
}
}
if (! first_layer && ! m_second_layer_things_done) {
//BBS: open powerlost recovery
if (print.is_BBL_printer()) {
// BBS: open powerlost recovery
{
gcode += "; open powerlost recovery\n";
gcode += "M1003 S1\n";
gcode += "; open powerlost recovery\n";
gcode += "M1003 S1\n";
}
// BBS: open first layer inspection at second layer
if (print.config().scan_first_layer.value) {
// BBS: retract first to avoid droping when scan model
gcode += this->retract();
gcode += "M976 S1 P1 ; scan model before printing 2nd layer\n";
gcode += "M400 P100\n";
gcode += this->unretract();
// BBS: retract first to avoid droping when scan model
gcode += this->retract();
gcode += "M976 S1 P1 ; scan model before printing 2nd layer\n";
gcode += "M400 P100\n";
gcode += this->unretract();
}
}
//BBS: reset acceleration at sencond layer
if (m_config.default_acceleration.value > 0 && m_config.initial_layer_acceleration.value > 0) {
double acceleration = m_config.default_acceleration.value;
gcode += m_writer.set_acceleration((unsigned int)floor(acceleration + 0.5));
}
if (m_config.default_jerk.value > 0 && m_config.initial_layer_jerk.value > 0) {
double jerk = m_config.default_jerk.value;
gcode += m_writer.set_jerk_xy((unsigned int)floor(jerk + 0.5));
}
// Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent
// nozzle_temperature_initial_layer vs. temperature settings.
for (const Extruder &extruder : m_writer.extruders()) {
@ -3237,9 +3336,13 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
}
//BBS: don't reset acceleration when printing first layer. During first layer, acceleration is always same value.
if (!this->on_first_layer())
if (!this->on_first_layer()){
// reset acceleration
gcode += m_writer.set_acceleration((unsigned int)(m_config.default_acceleration.value + 0.5));
if(m_config.default_acceleration.value > 0)
gcode += m_writer.set_acceleration((unsigned int)(m_config.default_acceleration.value + 0.5));
if(m_config.default_jerk.value > 0)
gcode += m_writer.set_jerk_xy((unsigned int)(m_config.default_jerk.value + 0.5));
}
// BBS
if (m_wipe.enable) {
@ -3312,9 +3415,12 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string
m_wipe.path.reverse();
}
//BBS: don't reset acceleration when printing first layer. During first layer, acceleration is always same value.
if (!this->on_first_layer())
if (!this->on_first_layer()) {
// reset acceleration
gcode += m_writer.set_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5));
if(m_config.default_jerk.value > 0)
gcode += m_writer.set_jerk_xy((unsigned int)floor(m_config.default_jerk.value + 0.5));
}
return gcode;
}
@ -3340,9 +3446,13 @@ std::string GCode::extrude_path(ExtrusionPath path, std::string description, dou
m_wipe.path.reverse();
}
//BBS: don't reset acceleration when printing first layer. During first layer, acceleration is always same value.
if (!this->on_first_layer())
if (!this->on_first_layer()){
// reset acceleration
gcode += m_writer.set_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5));
if(m_config.default_jerk.value > 0)
gcode += m_writer.set_jerk_xy((unsigned int)floor(m_config.default_jerk.value + 0.5));
}
return gcode;
}
@ -3541,6 +3651,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
} else if (m_config.perimeter_acceleration.value > 0 && is_perimeter(path.role())) {
acceleration = m_config.perimeter_acceleration.value;
#endif
} else if (m_config.outer_wall_acceleration.value > 0 && is_external_perimeter(path.role())) {
acceleration = m_config.outer_wall_acceleration.value;
} else if (m_config.inner_wall_acceleration.value > 0 && is_internal_perimeter(path.role())) {
acceleration = m_config.inner_wall_acceleration.value;
} else if (m_config.top_surface_acceleration.value > 0 && is_top_surface(path.role())) {
acceleration = m_config.top_surface_acceleration.value;
} else {
@ -3549,6 +3663,23 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
gcode += m_writer.set_acceleration((unsigned int)floor(acceleration + 0.5));
}
// adjust X Y jerk
if (m_config.default_jerk.value > 0) {
double jerk;
if (this->on_first_layer() && m_config.initial_layer_jerk.value > 0) {
jerk = m_config.initial_layer_jerk.value;
} else if (m_config.outer_wall_jerk.value > 0 && is_external_perimeter(path.role())) {
jerk = m_config.outer_wall_jerk.value;
} else if (m_config.inner_wall_jerk.value > 0 && is_internal_perimeter(path.role())) {
jerk = m_config.inner_wall_jerk.value;
} else if (m_config.top_surface_jerk.value > 0 && is_top_surface(path.role())) {
jerk = m_config.top_surface_jerk.value;
} else {
jerk = m_config.default_jerk.value;
}
gcode += m_writer.set_jerk_xy((unsigned int)floor(jerk + 0.5));
}
// calculate extrusion length per distance unit
double e_per_mm = m_writer.extruder()->e_per_mm3() * path.mm3_per_mm;
@ -3761,7 +3892,34 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
bool could_be_wipe_disabled = false;
// Save state of use_external_mp_once for the case that will be needed to call twice m_avoid_crossing_perimeters.travel_to.
const bool used_external_mp_once = m_avoid_crossing_perimeters.used_external_mp_once();
std::string gcode;
// SoftFever
if (this->on_first_layer()) {
if(m_config.default_acceleration.value > 0)
{
auto jerk = (unsigned int)floor(m_config.initial_layer_jerk.value + 0.5);
auto accel = (unsigned int)floor(m_config.initial_layer_acceleration.value + 0.5);
if(jerk > 0)
gcode += m_writer.set_jerk_xy(jerk);
if(accel > 0)
gcode += m_writer.set_acceleration(accel);
}
}
else
{
if(m_config.default_jerk.value > 0)
{
auto jerk = (unsigned int)floor(m_config.travel_jerk.value + 0.5);
auto accel = (unsigned int)floor(m_config.travel_acceleration.value + 0.5);
if(jerk > 0)
gcode += m_writer.set_jerk_xy(jerk);
if(accel > 0)
gcode += m_writer.set_acceleration(accel);
}
}
// if a retraction would be needed, try to use reduce_crossing_wall to plan a
// multi-hop travel path inside the configuration space
if (needs_retraction
@ -3777,7 +3935,6 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
m_avoid_crossing_perimeters.reset_once_modifiers();
// generate G-code for the travel move
std::string gcode;
if (needs_retraction) {
if (m_config.reduce_crossing_wall && could_be_wipe_disabled)
m_wipe.reset_path();
@ -3808,9 +3965,9 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
if (i == travel.size() - 1 && !m_spiral_vase) {
Vec2d dest2d = this->point_to_gcode(travel.points[i]);
Vec3d dest3d(dest2d(0), dest2d(1), m_nominal_z);
gcode += m_writer.travel_to_xyz(dest3d, comment);
gcode += m_writer.travel_to_xyz(dest3d, comment+" travel_to_xyz");
} else {
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment);
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment+" travel_to_xy");
}
}
this->set_last_pos(travel.points.back());
@ -3884,7 +4041,11 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction)
if (m_writer.extruder()->retraction_length() > 0) {
// BBS: don't do lazy_lift when enable spiral vase
size_t extruder_id = m_writer.extruder()->id();
gcode += m_writer.lift(!m_spiral_vase ? LiftType::SpiralLift : LiftType::NormalLift);
auto _lift = m_config.z_lift_type.value;
if(m_spiral_vase)
_lift = NormalLift;
gcode += m_writer.lift(_lift);
}
return gcode;

View file

@ -453,10 +453,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) {
char buf[128];
//sprintf(buf, "; estimated printing time (%s mode) = %s\n",
// (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
// get_time_dhms(machine.time).c_str());
sprintf(buf, "; total estimated time: %s\n", get_time_dhms(machine.time).c_str());
sprintf(buf, "; estimated printing time: %s\n", get_time_dhms(machine.time).c_str());
ret += buf;
}
}

View file

@ -99,8 +99,7 @@ public:
}
WipeTowerWriter& disable_linear_advance() {
m_gcode += (m_gcode_flavor == gcfRepRapSprinter || m_gcode_flavor == gcfRepRapFirmware
? (std::string("M572 D") + std::to_string(m_current_tool) + " S0\n")
m_gcode += (m_gcode_flavor == gcfKlipper ? (std::string("SET_PRESSURE_ADVANCE ADVANCE=0\n"))
: std::string("M900 K0\n"));
return *this;
}

View file

@ -15,15 +15,16 @@
namespace Slic3r {
const bool GCodeWriter::full_gcode_comment = false;
const bool GCodeWriter::full_gcode_comment = true;
const double GCodeWriter::slope_threshold = 3 * PI / 180;
void GCodeWriter::apply_print_config(const PrintConfig &print_config)
{
this->config.apply(print_config, true);
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
bool is_marlin = print_config.gcode_flavor.value == gcfMarlinLegacy || print_config.gcode_flavor.value == gcfMarlinFirmware;
bool is_marlin = print_config.gcode_flavor.value == gcfMarlinLegacy || print_config.gcode_flavor.value == gcfMarlinFirmware || print_config.gcode_flavor.value == gcfKlipper;
m_max_acceleration = std::lrint(is_marlin ? print_config.machine_max_acceleration_extruding.values.front() : 0);
m_max_jerk = std::lrint(is_marlin ? std::min(print_config.machine_max_jerk_x.values.front(), print_config.machine_max_jerk_y.values.front()) : 0);
}
void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids)
@ -54,7 +55,8 @@ std::string GCodeWriter::preamble()
FLAVOR_IS(gcfMarlinFirmware) ||
FLAVOR_IS(gcfTeacup) ||
FLAVOR_IS(gcfRepetier) ||
FLAVOR_IS(gcfSmoothie))
FLAVOR_IS(gcfSmoothie) ||
FLAVOR_IS(gcfKlipper))
{
if (RELATIVE_E_AXIS) {
gcode << "M83 ; only support relative e\n";
@ -183,6 +185,30 @@ std::string GCodeWriter::set_acceleration(unsigned int acceleration)
return gcode.str();
}
std::string GCodeWriter::set_jerk_xy(unsigned int jerk)
{
// Clamp the jerk to the allowed maximum.
if (m_max_jerk > 0 && jerk > m_max_jerk)
jerk = m_max_jerk;
if (jerk < 1 || jerk == m_last_jerk)
return std::string();
m_last_jerk = jerk;
std::ostringstream gcode;
if(FLAVOR_IS(gcfKlipper))
gcode << "SET_VELOCITY_LIMIT SQUARE_CORNER_VELOCITY=" << jerk;
else
gcode << "M205 X" << jerk << " Y" << jerk;
if (GCodeWriter::full_gcode_comment) gcode << " ; adjust jerk";
gcode << "\n";
return gcode.str();
}
std::string GCodeWriter::reset_e(bool force)
{
if (FLAVOR_IS(gcfMach3)

View file

@ -11,12 +11,6 @@
namespace Slic3r {
enum class LiftType {
NormalLift,
LazyLift,
SpiralLift
};
class GCodeWriter {
public:
GCodeConfig config;
@ -26,6 +20,7 @@ public:
multiple_extruders(false), m_extruder(nullptr),
m_single_extruder_multi_material(false),
m_last_acceleration(0), m_max_acceleration(0),
m_last_jerk(0), m_max_jerk(0),
/*m_last_bed_temperature(0), */m_last_bed_temperature_reached(true),
m_lifted(0),
m_to_lift(0),
@ -51,6 +46,7 @@ public:
// BBS
std::string set_bed_temperature(std::vector<int> temps_per_bed, int default_temp, bool wait = false);
std::string set_acceleration(unsigned int acceleration);
std::string set_jerk_xy(unsigned int jerk);
std::string reset_e(bool force = false);
std::string update_progress(unsigned int num, unsigned int tot, bool allow_100 = false) const;
// return false if this extruder was already selected
@ -104,6 +100,13 @@ private:
// Limit for setting the acceleration, to respect the machine limits set for the Marlin firmware.
// If set to zero, the limit is not in action.
unsigned int m_max_acceleration;
unsigned int m_max_jerk;
unsigned int m_last_jerk;
unsigned int m_travel_acceleration;
unsigned int m_travel_jerk;
//BBS
unsigned int m_last_additional_fan_speed;
// BBS

View file

@ -296,8 +296,10 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
printf("Processing bridge at layer %zu:\n", this->layer()->id());
#endif
//BBS: use 0 as custom angle to enable auto detection all the time
double custom_angle = Geometry::deg2rad(0.0);
if (bd.detect_angle(custom_angle)) {
double custom_angle = Geometry::deg2rad(this->region().config().bridge_angle.value);
if(custom_angle > 0)
bridges[idx_last].bridge_angle = custom_angle;
else if (bd.detect_angle(custom_angle)) {
bridges[idx_last].bridge_angle = bd.angle;
if (this->layer()->object()->has_support()) {
// polygons_append(this->bridged, bd.coverage());

View file

@ -412,6 +412,8 @@ void PerimeterGenerator::process()
for (const Surface &surface : this->slices->surfaces) {
// detect how many perimeters must be generated for this island
int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops
if (this->layer_id == 0 && this->config->only_one_wall_first_layer)
loop_number = 0;
//BBS: set the topmost layer to be one wall
if (loop_number > 0 && config->only_one_wall_top && this->upper_slices == nullptr)
loop_number = 0;

View file

@ -697,8 +697,9 @@ static std::vector<std::string> s_Preset_print_options {
#endif /* HAS_PRESSURE_EQUALIZER */
"inner_wall_speed", "outer_wall_speed", "small_perimeter_speed", "sparse_infill_speed", "internal_solid_infill_speed",
"top_surface_speed", "support_speed", "support_object_xy_distance", "support_interface_speed",
"bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed",
"initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_loops", "skirt_distance", "skirt_height", "draft_shield",
"bridge_speed", "bridge_angle", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed",
"outer_wall_acceleration", "inner_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "travel_acceleration", "skirt_loops", "skirt_distance", "skirt_height", "draft_shield",
"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk",
"brim_width", "brim_object_gap", "brim_type", "enable_support", "support_type", "support_threshold_angle", "enforce_support_layers",
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
"support_base_pattern", "support_base_pattern_spacing", "support_style",
@ -722,14 +723,17 @@ static std::vector<std::string> s_Preset_print_options {
"detect_narrow_internal_solid_infill",
"gcode_add_line_number", "enable_arc_fitting", "infill_combination", "adaptive_layer_height",
"support_bottom_interface_spacing", "enable_overhang_speed", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed",
"initial_layer_infill_speed", "only_one_wall_top",
"timelapse_type"
"initial_layer_infill_speed", "only_one_wall_top", "only_one_wall_first_layer",
"timelapse_type",
//SoftFever
"top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio"
};
static std::vector<std::string> s_Preset_filament_options {
/*"filament_colour", */"filament_diameter", "filament_type", "filament_soluble", "filament_is_support", "filament_max_volumetric_speed",
"filament_flow_ratio", "filament_density", "filament_cost", "filament_minimal_purge_on_wipe_tower",
"nozzle_temperature", "nozzle_temperature_initial_layer",
"filament_flow_ratio", "enable_pressure_advance", "pressure_advance", "filament_density", "filament_cost", "filament_minimal_purge_on_wipe_tower",
"chamber_temperature", "nozzle_temperature", "nozzle_temperature_initial_layer",
// BBS
"cool_plate_temp", "eng_plate_temp", "hot_plate_temp", "textured_plate_temp", "cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "hot_plate_temp_initial_layer","textured_plate_temp_initial_layer",
// "bed_type",
@ -757,18 +761,20 @@ static std::vector<std::string> s_Preset_machine_limits_options {
static std::vector<std::string> s_Preset_printer_options {
"printer_technology",
"printable_area", "bed_exclude_area", "gcode_flavor",
"printable_area", "bed_exclude_area", "gcode_flavor","z_lift_type",
"single_extruder_multi_material", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "layer_change_gcode", "change_filament_gcode",
"printer_model", "printer_variant", "printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod",
"default_print_profile", "inherits",
"silent_mode",
// BBS
"scan_first_layer", "machine_load_filament_time", "machine_unload_filament_time", "machine_pause_gcode", "template_custom_gcode",
"nozzle_type", "auxiliary_fan", "nozzle_volume",
"nozzle_type", "nozzle_diameter", "auxiliary_fan", "nozzle_volume",
//SoftFever
"host_type", "print_host", "printhost_apikey",
"printhost_cafile","printhost_port","printhost_authorization_type",
"printhost_user", "printhost_password", "printhost_ssl_ignore_revoke"
"printhost_user",
"printhost_password",
"printhost_ssl_ignore_revoke", "thumbnails"
};
static std::vector<std::string> s_Preset_sla_print_options {
@ -2238,6 +2244,21 @@ void add_correct_opts_to_diff(const std::string &opt_key, t_config_option_keys&
}
}
// template<class T>
// void add_correct_opt_to_diff(const std::string &opt_key, t_config_option_keys& vec, const ConfigBase &other, const ConfigBase &this_c)
// {
// const T* opt_init = static_cast<const T*>(other.option(opt_key));
// const T* opt_cur = static_cast<const T*>(this_c.option(opt_key));
// int opt_init_max_id = opt_init->values.size() - 1;
// for (int i = 0; i < int(opt_cur->values.size()); i++)
// {
// int init_id = i <= opt_init_max_id ? i : 0;
// if (opt_cur->values[i] != opt_init->values[init_id])
// vec.emplace_back(opt_key + "#" + std::to_string(i));
// }
// }
// Use deep_diff to correct return of changed options, considering individual options for each extruder.
inline t_config_option_keys deep_diff(const ConfigBase &config_this, const ConfigBase &config_other)
{
@ -2248,7 +2269,7 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
{
//BBS: add bed_exclude_area
if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers") {
if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "thumbnails") {
// Scalar variable, or a vector variable, which is independent from number of extruders,
// thus the vector is presented to the user as a single input.
diff.emplace_back(opt_key);
@ -2261,6 +2282,7 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
case coBools: add_correct_opts_to_diff<ConfigOptionBools >(opt_key, diff, config_other, config_this); break;
case coFloats: add_correct_opts_to_diff<ConfigOptionFloats >(opt_key, diff, config_other, config_this); break;
case coStrings: add_correct_opts_to_diff<ConfigOptionStrings >(opt_key, diff, config_other, config_this); break;
// case coString: add_correct_opts_to_diff<ConfigOptionString >(opt_key, diff, config_other, config_this); break;
case coPercents:add_correct_opts_to_diff<ConfigOptionPercents >(opt_key, diff, config_other, config_this); break;
case coPoints: add_correct_opts_to_diff<ConfigOptionPoints >(opt_key, diff, config_other, config_this); break;
// BBS
@ -2741,6 +2763,8 @@ void PhysicalPrinterCollection::load_printers(
// see https://github.com/prusa3d/PrusaSlicer/issues/732
boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / subdir).make_preferred();
m_dir_path = dir.string();
if(!boost::filesystem::exists(dir))
return;
std::string errors_cummulative;
// Store the loaded printers into a new vector, otherwise the binary search for already existing presets would be broken.
std::deque<PhysicalPrinter> printers_loaded;

View file

@ -38,9 +38,9 @@ static std::vector<std::string> s_project_options {
"wipe_tower_y",
"wipe_tower_rotation_angle",
"curr_bed_type",
#if !BBL_RELEASE_TO_PUBLIC
// #if !BBL_RELEASE_TO_PUBLIC
"flush_multiplier",
#endif
// #endif
};
//BBS: add BBL as default
@ -90,7 +90,7 @@ PresetBundle::PresetBundle()
for (size_t i = 0; i < 1; ++i) {
// The following ugly switch is to avoid printers.preset(0) to return the edited instance, as the 0th default is the current one.
Preset &preset = this->printers.default_preset(i);
for (const char *key : {"printer_settings_id", "printer_model", "printer_variant"}) preset.config.optptr(key, true);
for (const char *key : {"printer_settings_id", "printer_model", "printer_variant", "thumbnails"}) preset.config.optptr(key, true);
//if (i == 0) {
preset.config.optptr("default_print_profile", true);
preset.config.option<ConfigOptionStrings>("default_filament_profile", true);
@ -1419,8 +1419,10 @@ DynamicPrintConfig PresetBundle::full_config() const
DynamicPrintConfig PresetBundle::full_config_secure() const
{
DynamicPrintConfig config = this->full_config();
//BBS example: config.erase("print_host");
return config;
//FIXME legacy, the keys should not be there after conversion to a Physical Printer profile.
config.erase("print_host");
config.erase("printhost_apikey");
config.erase("printhost_cafile"); return config;
}
const std::set<std::string> ignore_settings_list ={
@ -2960,7 +2962,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
//parse error
std::string subfile_path = path + "/" + vendor_name + "/" + subfile.second;
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", got error when parse process setting from %1%") % subfile_path;
throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path %vendor_system_path).str());
throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly.\nReason: %3%") % subfile_path %vendor_system_path %reason).str());
}
}
@ -3435,4 +3437,4 @@ void PresetBundle::set_default_suppressed(bool default_suppressed)
printers.set_default_suppressed(default_suppressed);
}
} // namespace Slic3r
} // namespace Slic3r

View file

@ -68,7 +68,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
//BBS: add bed_exclude_area
"bed_exclude_area",
"before_layer_change_gcode",
"enable_overhang_bridge_fan"
"enable_pressure_advance",
"pressure_advance",
"enable_overhang_bridge_fan",
"overhang_fan_speed",
"overhang_fan_threshold",
"slow_down_for_layer_cooling",
@ -90,8 +92,11 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"filament_diameter",
"filament_density",
"filament_cost",
"outer_wall_acceleration",
"inner_wall_acceleration",
"initial_layer_acceleration",
"top_surface_acceleration",
"travel_acceleration",
// BBS
"cool_plate_temp_initial_layer",
"eng_plate_temp_initial_layer",
@ -124,10 +129,13 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"filament_start_gcode",
"change_filament_gcode",
"wipe",
"z_lift_type",
// BBS
"wipe_distance",
"curr_bed_type",
"nozzle_volume"
"nozzle_volume",
"chamber_temperature",
"thumbnails"
};
static std::unordered_set<std::string> steps_ignore;
@ -164,6 +172,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
} else if (
opt_key == "print_sequence"
|| opt_key == "filament_type"
|| opt_key == "chamber_temperature"
|| opt_key == "nozzle_temperature_initial_layer"
|| opt_key == "filament_minimal_purge_on_wipe_tower"
|| opt_key == "filament_max_volumetric_speed"

View file

@ -728,7 +728,11 @@ public:
// Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim.
std::vector<Point> first_layer_wipe_tower_corners(bool check_wipe_tower_existance=true) const;
protected:
//SoftFever
bool &is_BBL_printer() { return m_isBBLPrinter; }
const bool is_BBL_printer() const { return m_isBBLPrinter; }
protected:
// Invalidates the step, and its depending steps in Print.
bool invalidate_step(PrintStep step);
@ -750,6 +754,9 @@ private:
PrintRegionConfig m_default_region_config;
PrintObjectPtrs m_objects;
PrintRegionPtrs m_print_regions;
//SoftFever
bool m_isBBLPrinter;
// Ordered collections of extrusion paths to build skirt loops and brim.
ExtrusionEntityCollection m_skirt;

View file

@ -72,18 +72,18 @@ static t_config_enum_values s_keys_map_PrinterTechnology {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrinterTechnology)
static t_config_enum_values s_keys_map_PrintHostType{
static t_config_enum_values s_keys_map_PrintHostType {
{ "prusalink", htPrusaLink },
{ "octoprint", htOctoPrint },
//{ "duet", htDuet },
//{ "flashair", htFlashAir },
//{ "astrobox", htAstroBox },
//{ "repetier", htRepetier },
//{ "mks", htMKS }
{ "duet", htDuet },
{ "flashair", htFlashAir },
{ "astrobox", htAstroBox },
{ "repetier", htRepetier },
{ "mks", htMKS }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PrintHostType)
static t_config_enum_values s_keys_map_AuthorizationType{
static t_config_enum_values s_keys_map_AuthorizationType {
{ "key", atKeyPassword },
{ "user", atUserPassword }
};
@ -98,6 +98,7 @@ static t_config_enum_values s_keys_map_GCodeFlavor {
{ "makerware", gcfMakerWare },
{ "marlin2", gcfMarlinFirmware },
{ "sailfish", gcfSailfish },
{ "klipper", gcfKlipper },
{ "smoothie", gcfSmoothie },
{ "mach3", gcfMach3 },
{ "machinekit", gcfMachinekit },
@ -105,6 +106,15 @@ static t_config_enum_values s_keys_map_GCodeFlavor {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(GCodeFlavor)
static t_config_enum_values s_keys_map_LiftType {
{ "NormalLift", int(LiftType::NormalLift) },
{ "LazyLift", int(LiftType::LazyLift) },
{ "SpiralLift", int(LiftType::SpiralLift) }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(LiftType)
static t_config_enum_values s_keys_map_FuzzySkinType {
{ "none", int(FuzzySkinType::None) },
{ "external", int(FuzzySkinType::External) },
@ -317,7 +327,7 @@ void PrintConfigDef::init_common_params()
def = this->add("printable_area", coPoints);
def->label = L("Printable area");
//BBS
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionPoints{ Vec2d(0, 0), Vec2d(200, 0), Vec2d(200, 200), Vec2d(0, 200) });
//BBS: add "bed_exclude_area"
@ -575,7 +585,7 @@ void PrintConfigDef::init_fff_params()
def->multiline = true;
def->full_width = true;
def->height = 5;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("bottom_shell_layers", coInt);
@ -644,12 +654,38 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(1));
def = this->add("top_solid_infill_flow_ratio", coFloat);
def->label = L("Top surface flow ratio");
def->category = L("Advanced");
def->tooltip = L("This factor affects the amount of material for top solid infill. "
"You can decrease it slightly to have smooth surface finish");
def->min = 0;
def->max = 2;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(1));
def = this->add("bottom_solid_infill_flow_ratio", coFloat);
def->label = L("Bottom surface flow ratio");
def->category = L("Advanced");
def->tooltip = L("This factor affects the amount of material for bottom solid infill");
def->min = 0;
def->max = 2;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(1));
def = this->add("only_one_wall_top", coBool);
def->label = L("Only one wall on top surfaces");
def->category = L("Quality");
def->tooltip = L("Use only one wall on flat top surface, to give more space to the top infill pattern");
def->set_default_value(new ConfigOptionBool(false));
def = this->add("only_one_wall_first_layer", coBool);
def->label = L("Only one wall on first layer");
def->category = L("Quality");
def->tooltip = L("Use only one wall on first layer, to give more space to the bottom infill pattern");
def->set_default_value(new ConfigOptionBool(false));
def = this->add("enable_overhang_speed", coBool);
def->label = L("Slow down for overhang");
def->category = L("Speed");
@ -975,7 +1011,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back("3");
def->enum_labels.push_back("4");
def->enum_labels.push_back("5");
def->mode = comDevelop;
def->mode = comAdvanced;
def = this->add("extruder_clearance_height_to_rod", coFloat);
def->label = L("Height to rod");
@ -1009,7 +1045,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Only used as a visual help on UI");
def->gui_type = ConfigOptionDef::GUIType::color;
// Empty string means no color assigned yet.
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionStrings { "" });
def = this->add("extruder_offset", coPoints);
@ -1019,7 +1055,7 @@ void PrintConfigDef::init_fff_params()
// "with respect to the first one. It expects positive coordinates (they will be subtracted "
// "from the XY coordinate).");
def->sidetext = L("mm");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionPoints { Vec2d(0,0) });
def = this->add("filament_flow_ratio", coFloats);
@ -1032,6 +1068,18 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 1. });
def = this->add("enable_pressure_advance", coBool);
def->label = L("Enable pressure advance");
def->tooltip = L("Enable pressure advance, auto calibration result will be overwriten once enabled.");
def->set_default_value(new ConfigOptionBool(false));
def = this->add("pressure_advance", coFloats);
def->label = L("Pressure advance");
def->tooltip = L("Pressure advnce(Klipper) AKA Linear advance factor(Marlin)");
def->max = 2;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0.02 });
def = this->add("line_width", coFloat);
def->label = L("Default");
def->category = L("Quality");
@ -1062,7 +1110,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Color");
def->tooltip = L("Only used as a visual help on UI");
def->gui_type = ConfigOptionDef::GUIType::color;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionStrings{ "#00AE42" });
def = this->add("filament_max_volumetric_speed", coFloats);
@ -1077,13 +1125,13 @@ void PrintConfigDef::init_fff_params()
def = this->add("filament_minimal_purge_on_wipe_tower", coFloats);
def->label = L("Minimal purge on wipe tower");
//def->tooltip = L("After a tool change, the exact position of the newly loaded filament inside "
// "the nozzle may not be known, and the filament pressure is likely not yet stable. "
// "Before purging the print head into an infill or a sacrificial object, Slic3r will always prime "
// "this amount of material into the wipe tower to produce successive infill or sacrificial object extrusions reliably.");
def->tooltip = L("After a tool change, the exact position of the newly loaded filament inside "
"the nozzle may not be known, and the filament pressure is likely not yet stable. "
"Before purging the print head into an infill or a sacrificial object, Slic3r will always prime "
"this amount of material into the wipe tower to produce successive infill or sacrificial object extrusions reliably.");
def->sidetext = L("mm³");
def->min = 0;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 15. });
def = this->add("machine_load_filament_time", coFloat);
@ -1139,13 +1187,13 @@ void PrintConfigDef::init_fff_params()
def = this->add("filament_soluble", coBools);
def->label = L("Soluble material");
def->tooltip = L("Soluble material is commonly used to print support and support interface");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBools { false });
def = this->add("filament_is_support", coBools);
def->label = L("Support material");
def->tooltip = L("Support material is commonly used to print support and support interface");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBools { false });
// BBS
@ -1186,6 +1234,16 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(45));
def = this->add("bridge_angle", coFloat);
def->label = L("Bridge infill direction");
def->category = L("Strength");
def->tooltip = L("Angle for bridge infill pattern, which controls the start or main direction of line");
def->sidetext = L("°");
def->min = 0;
def->max = 360;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("sparse_infill_density", coPercent);
def->label = L("Sparse infill density");
def->category = L("Strength");
@ -1240,6 +1298,30 @@ void PrintConfigDef::init_fff_params()
#endif // HAS_LIGHTNING_INFILL
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipCubic));
def = this->add("outer_wall_acceleration", coFloat);
def->label = L("Outer wall");
def->tooltip = L("Acceleration of outer walls");
def->sidetext = L("mm/s²");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(10000));
def = this->add("inner_wall_acceleration", coFloat);
def->label = L("Inner wall");
def->tooltip = L("Acceleration of inner walls");
def->sidetext = L("mm/s²");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(10000));
def = this->add("travel_acceleration", coFloat);
def->label = L("Travel");
def->tooltip = L("Acceleration of travel moves");
def->sidetext = L("mm/s²");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(10000));
def = this->add("top_surface_acceleration", coFloat);
def->label = L("Top surface");
def->tooltip = L("Acceleration of top surface infill. Using a lower value may improve top surface quality");
@ -1256,6 +1338,54 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(300));
def = this->add("default_jerk", coFloat);
def->label = L("Default");
def->tooltip = L("Default");
def->sidetext = L("mm/s");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("outer_wall_jerk", coFloat);
def->label = L("Outer wall");
def->tooltip = L("Jerk of outer walls");
def->sidetext = L("mm/s");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(9));
def = this->add("inner_wall_jerk", coFloat);
def->label = L("Inner wall");
def->tooltip = L("Jerk of inner walls");
def->sidetext = L("mm/s");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(9));
def = this->add("top_surface_jerk", coFloat);
def->label = L("Top surface");
def->tooltip = L("Jerk for top surface");
def->sidetext = L("mm/s");
def->min = 1;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(9));
def = this->add("initial_layer_jerk", coFloat);
def->label = L("Initial layer");
def->tooltip = L("Jerk for initial layer");
def->sidetext = L("mm/s");
def->min = 1;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(9));
def = this->add("travel_jerk", coFloat);
def->label = L("Travel");
def->tooltip = L("Jerk for travel");
def->sidetext = L("mm/s");
def->min = 1;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(12));
def = this->add("initial_layer_line_width", coFloat);
def->label = L("Initial layer");
def->category = L("Quality");
@ -1338,7 +1468,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("The width within which to jitter. It's adversed to be below outer wall line width");
def->sidetext = L("mm");
def->min = 0;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.3));
def = this->add("fuzzy_skin_point_distance", coFloat);
@ -1346,7 +1476,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Others");
def->tooltip = L("The average diatance between the random points introducded on each line segment");
def->sidetext = L("mm");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.8));
def = this->add("gap_infill_speed", coFloat);
@ -1376,7 +1506,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("scan_first_layer", coBool);
def->label = L("Scan first layer");
def->tooltip = L("Enable this to enable the camera on printer to check the quality of first layer");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
//BBS
// def = this->add("spaghetti_detector", coBool);
@ -1398,13 +1528,13 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Hardened steel"));
def->enum_labels.push_back(L("Stainless steel"));
def->enum_labels.push_back(L("Brass"));
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<NozzleType>(ntUndefine));
def = this->add("auxiliary_fan", coBool);
def->label = L("Auxiliary part cooling fan");
def->tooltip = L("Enable this option if machine has auxiliary part cooling fan");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("gcode_flavor", coEnum);
@ -1412,7 +1542,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("What kind of gcode the printer is compatible with");
def->enum_keys_map = &ConfigOptionEnum<GCodeFlavor>::get_enum_values();
def->enum_values.push_back("marlin");
//def->enum_values.push_back("reprap");
def->enum_values.push_back("klipper");
//def->enum_values.push_back("reprapfirmware");
//def->enum_values.push_back("repetier");
//def->enum_values.push_back("teacup");
@ -1435,6 +1565,7 @@ void PrintConfigDef::init_fff_params()
//def->enum_labels.push_back("Machinekit");
//def->enum_labels.push_back("Smoothie");
//def->enum_labels.push_back(L("No extrusion"));
def->enum_labels.push_back(L("Klipper"));
def->mode = comAdvanced;
def->readonly = false;
def->set_default_value(new ConfigOptionEnum<GCodeFlavor>(gcfMarlinLegacy));
@ -1500,13 +1631,13 @@ void PrintConfigDef::init_fff_params()
def->cli = ConfigOptionDef::nocli;
def = this->add("interface_shells", coBool);
//def->label = L("Interface shells");
def->label = L("Interface shells");
def->label = "Interface shells";
//def->tooltip = L("Force the generation of solid shells between adjacent materials/volumes. "
// "Useful for multi-extruder prints with translucent materials or manual soluble "
// "support material");
def->tooltip = L("Force the generation of solid shells between adjacent materials/volumes. "
"Useful for multi-extruder prints with translucent materials or manual soluble "
"support material");
def->category = L("Quality");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("ironing_type", coEnum);
@ -1554,7 +1685,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Print speed of ironing lines");
def->sidetext = L("mm/s");
def->min = 0;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(20));
def = this->add("layer_change_gcode", coString);
@ -1715,7 +1846,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Maximum acceleration for travel (M204 T)");
def->sidetext = L("mm/s²");
def->min = 0;
def->readonly = true;
def->readonly = false;
def->mode = comDevelop;
def->set_default_value(new ConfigOptionFloats{ 1500., 1250. });
@ -1735,7 +1866,7 @@ void PrintConfigDef::init_fff_params()
"the maximum layer hight when enable adaptive layer height");
def->sidetext = L("mm");
def->min = 0;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0. });
#ifdef HAS_PRESSURE_EQUALIZER
@ -1787,7 +1918,7 @@ void PrintConfigDef::init_fff_params()
"the minimum layer hight when enable adaptive layer height");
def->sidetext = L("mm");
def->min = 0;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0.07 });
def = this->add("slow_down_min_speed", coFloats);
@ -1795,44 +1926,45 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("The minimum printing speed when slow down for cooling");
def->sidetext = L("mm/s");
def->min = 0;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 10. });
def = this->add("nozzle_diameter", coFloats);
def->label = L("Nozzle diameter");
def->tooltip = L("Diameter of nozzle");
def->sidetext = L("mm");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0.4 });
def = this->add("host_type", coEnum);
def->label = L("Host Type");
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field must contain "
"the kind of the host.");
"the kind of the host.");
def->enum_keys_map = &ConfigOptionEnum<PrintHostType>::get_enum_values();
def->enum_values.push_back("prusalink");
def->enum_values.push_back("octoprint");
//def->enum_values.push_back("duet");
//def->enum_values.push_back("flashair");
//def->enum_values.push_back("astrobox");
//def->enum_values.push_back("repetier");
//def->enum_values.push_back("mks");
def->enum_values.push_back("duet");
def->enum_values.push_back("flashair");
def->enum_values.push_back("astrobox");
def->enum_values.push_back("repetier");
def->enum_values.push_back("mks");
def->enum_labels.push_back("PrusaLink");
def->enum_labels.push_back("OctoPrint");
//def->enum_labels.push_back("Duet");
//def->enum_labels.push_back("FlashAir");
//def->enum_labels.push_back("AstroBox");
//def->enum_labels.push_back("Repetier");
//def->enum_labels.push_back("MKS");
def->enum_labels.push_back("Duet");
def->enum_labels.push_back("FlashAir");
def->enum_labels.push_back("AstroBox");
def->enum_labels.push_back("Repetier");
def->enum_labels.push_back("MKS");
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionEnum<PrintHostType>(htOctoPrint));
def = this->add("nozzle_volume", coFloat);
def->label = L("Nozzle volume");
def->tooltip = L("Volume of nozzle between the cutter and the end of nozzle");
def->sidetext = L("mm³");
def->mode = comDevelop;
def->mode = comAdvanced;
def->readonly = true;
def->set_default_value(new ConfigOptionFloat { 0.0 });
@ -1856,15 +1988,15 @@ void PrintConfigDef::init_fff_params()
def->label = L("Filename format");
def->tooltip = L("User can self-define the project file name when export");
def->full_width = true;
def->mode = comDevelop;
def->set_default_value(new ConfigOptionString("[input_filename_base].gcode"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString("{input_filename_base}_{filament_type[0]}_{print_time}.gcode"));
def = this->add("detect_overhang_wall", coBool);
def->label = L("Detect overhang wall");
def->category = L("Quality");
def->tooltip = L("Detect the overhang percentage relative to line width and use different speed to print. "
"For 100%% overhang, bridge speed is used.");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
def = this->add("wall_filament", coInt);
@ -1936,7 +2068,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Z gap between object and raft. Ignored for soluble interface");
def->sidetext = L("mm");
def->min = 0;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.1));
def = this->add("raft_expansion", coFloat);
@ -1945,7 +2077,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Expand all raft layers in XY plane");
def->sidetext = L("mm");
def->min = 0;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(1.5));
def = this->add("raft_first_layer_density", coPercent);
@ -1955,7 +2087,7 @@ void PrintConfigDef::init_fff_params()
def->sidetext = L("%");
def->min = 10;
def->max = 100;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionPercent(90));
def = this->add("raft_first_layer_expansion", coFloat);
@ -1964,7 +2096,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Expand the first raft or support layer to improve bed plate adhesion");
def->sidetext = L("mm");
def->min = 0;
def->mode = comDevelop;
def->mode = comAdvanced;
//BBS: change from 3.0 to 2.0
def->set_default_value(new ConfigOptionFloat(2.0));
@ -1976,7 +2108,7 @@ void PrintConfigDef::init_fff_params()
def->sidetext = L("layers");
def->min = 0;
def->max = 100;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(0));
def = this->add("resolution", coFloat);
@ -1999,7 +2131,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Retract amount before wipe");
def->tooltip = L("The length of fast retraction before wipe, relative to retraction length");
def->sidetext = L("%");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionPercents { 100 });
def = this->add("retract_when_changing_layer", coBools);
@ -2025,7 +2157,7 @@ void PrintConfigDef::init_fff_params()
// "by the specified amount (the length is measured on raw filament, before it enters "
// "the extruder).");
def->sidetext = L("mm");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 10. });
def = this->add("z_hop", coFloats);
@ -2038,21 +2170,21 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloats { 0.4 });
def = this->add("retract_restart_extra", coFloats);
//def->label = L("Extra length on restart");
def->label = L("Extra length on restart");
def->label = "Extra length on restart";
//def->tooltip = L("When the retraction is compensated after the travel move, the extruder will push "
// "this additional amount of filament. This setting is rarely needed.");
def->tooltip = L("When the retraction is compensated after the travel move, the extruder will push "
"this additional amount of filament. This setting is rarely needed.");
def->sidetext = L("mm");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0. });
def = this->add("retract_restart_extra_toolchange", coFloats);
//def->label = L("Extra length on restart");
def->label = L("Extra length on restart");
def->label = "Extra length on restart";
//def->tooltip = L("When the retraction is compensated after changing tool, the extruder will push "
// "this additional amount of filament.");
def->tooltip = L("When the retraction is compensated after changing tool, the extruder will push "
"this additional amount of filament.");
def->sidetext = L("mm");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0. });
def = this->add("retraction_speed", coFloats);
@ -2093,7 +2225,7 @@ void PrintConfigDef::init_fff_params()
def->sidetext = L("mm");
def->min = 0;
def->max = 10;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(2));
def = this->add("skirt_height", coInt);
@ -2101,7 +2233,7 @@ void PrintConfigDef::init_fff_params()
def->label = "Skirt height";
//def->tooltip = L("How many layers of skirt. Usually only one layer");
def->sidetext = L("layers");
def->mode = comDevelop;
def->mode = comAdvanced;
def->max = 10000;
def->set_default_value(new ConfigOptionInt(1));
@ -2303,7 +2435,7 @@ void PrintConfigDef::init_fff_params()
def->sidetext = L("°");
def->min = 0;
def->max = 359;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("support_on_build_plate_only", coBool);
@ -2419,7 +2551,7 @@ void PrintConfigDef::init_fff_params()
//TRN To be shown in Print Settings "Bottom interface layers". Have to be as short as possible
def->enum_labels.push_back("-1");
append(def->enum_labels, support_interface_top_layers->enum_labels);
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(0));
def = this->add("support_interface_spacing", coFloat);
@ -2438,7 +2570,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Spacing of bottom interface lines. Zero means solid interface");
def->sidetext = L("mm");
def->min = 0;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.5));
def = this->add("support_interface_speed", coFloat);
@ -2515,7 +2647,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("snug");
def->enum_labels.push_back(L("Grid"));
def->enum_labels.push_back(L("Snug"));
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<SupportMaterialStyle>(smsGrid));
def = this->add("independent_support_layer_height", coBool);
@ -2581,6 +2713,15 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("chamber_temperature", coInt);
def->label = L("Chamber temperature");
def->tooltip = L("Target chamber temperature");
def->sidetext = L("°C");
def->full_label = L("Chamber temperature");
def->min = 0;
def->max = max_temp;
def->set_default_value(new ConfigOptionInt(0));
def = this->add("nozzle_temperature", coInts);
def->label = L("Other layers");
def->tooltip = L("Nozzle temperature for layers after the initial one");
@ -2697,6 +2838,19 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBools { false });
def = this->add("z_lift_type", coEnum);
def->label = L("Z hop type");
def->tooltip = L("Z hop type");
def->enum_keys_map = &ConfigOptionEnum<LiftType>::get_enum_values();
def->enum_values.push_back("NormalLift");
def->enum_values.push_back("SpiralLift");
// def->enum_values.push_back("LazyLift");
def->enum_labels.push_back(L("NormalLift"));
def->enum_labels.push_back(L("SpiralLift"));
// def->enum_labels.push_back(L("LazyLift"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<LiftType>(LiftType::SpiralLift));
def = this->add("wipe_distance", coFloats);
def->label = L("Wipe Distance");
def->tooltip = L("Discribe how long the nozzle will move along the last path when retracting");
@ -2735,7 +2889,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Flush multiplier");
def->tooltip = L("");
def->sidetext = L("");
def->mode = comDevelop;
def->mode = comAdvanced;
def->min = 0;
def->set_default_value(new ConfigOptionFloat(1.0));
@ -2836,6 +2990,13 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("thumbnails", coPoints);
def->label = L("G-code thumbnails");
def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxY, XxY, ...\"");
def->mode = comAdvanced;
def->gui_type = ConfigOptionDef::GUIType::one_string;
def->set_default_value(new ConfigOptionPoints{Vec2d(300, 300)});
// Declare retract values for filament profile, overriding the printer's extruder profile.
for (const char *opt_key : {
// floats
@ -2857,8 +3018,6 @@ void PrintConfigDef::init_fff_params()
if ((strcmp(opt_key, "retraction_length") == 0) ||
(strcmp(opt_key, "z_hop") == 0))
def->mode = comSimple;
else if (strcmp(opt_key, "retract_before_wipe") == 0)
def->mode = comDevelop;
else
def->mode = comAdvanced;
switch (def->type) {
@ -2875,7 +3034,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("This option will auto detect narrow internal solid infill area."
" If enabled, concentric pattern will be used for the area to speed printing up."
" Otherwise, rectilinear pattern is used defaultly.");
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
}
@ -3629,7 +3788,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
static std::set<std::string> ignore = {
"acceleration", "scale", "rotate", "duplicate", "duplicate_grid",
"bed_size",
"print_center", "g0", "pressure_advance", "wipe_tower_per_color_wipe"
"print_center", "g0", "wipe_tower_per_color_wipe"
#ifndef HAS_PRESSURE_EQUALIZER
, "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative"
#endif /* HAS_PRESSURE_EQUALIZER */

View file

@ -32,8 +32,8 @@
namespace Slic3r {
enum GCodeFlavor : unsigned char {
gcfMarlinLegacy, gcfRepRapSprinter, gcfRepRapFirmware, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlinFirmware, gcfSailfish, gcfMach3, gcfMachinekit,
gcfSmoothie, gcfNoExtrusion,
gcfMarlinLegacy, gcfKlipper, gcfRepRapSprinter, gcfRepRapFirmware, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlinFirmware, gcfSailfish, gcfMach3, gcfMachinekit,
gcfSmoothie, gcfNoExtrusion
};
enum class FuzzySkinType {
@ -118,6 +118,12 @@ enum SeamPosition {
spNearest, spAligned, spRear, spRandom
};
enum LiftType {
NormalLift,
SpiralLift,
LazyLift
};
enum SLAMaterial {
slamTough,
slamFlex,
@ -658,7 +664,10 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, bottom_shell_thickness))
((ConfigOptionFloat, bridge_flow))
((ConfigOptionFloat, bridge_speed))
((ConfigOptionFloat, bridge_angle))
((ConfigOptionEnum<InfillPattern>, top_surface_pattern))
((ConfigOptionFloat, top_solid_infill_flow_ratio))
((ConfigOptionFloat, bottom_solid_infill_flow_ratio))
((ConfigOptionEnum<InfillPattern>, bottom_surface_pattern))
((ConfigOptionFloat, outer_wall_line_width))
((ConfigOptionFloat, outer_wall_speed))
@ -705,6 +714,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, overhang_3_4_speed))
((ConfigOptionFloat, overhang_4_4_speed))
((ConfigOptionBool, only_one_wall_top))
((ConfigOptionBool, only_one_wall_first_layer))
)
PRINT_CONFIG_CLASS_DEFINE(
@ -748,6 +758,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionString, machine_end_gcode))
((ConfigOptionStrings, filament_end_gcode))
((ConfigOptionFloats, filament_flow_ratio))
((ConfigOptionBool, enable_pressure_advance))
((ConfigOptionFloats, pressure_advance))
((ConfigOptionFloats, filament_diameter))
((ConfigOptionFloats, filament_density))
((ConfigOptionStrings, filament_type))
@ -774,6 +786,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloats, retraction_length))
((ConfigOptionFloats, retract_length_toolchange))
((ConfigOptionFloats, z_hop))
((ConfigOptionEnum<LiftType>, z_lift_type))
((ConfigOptionFloats, retract_restart_extra))
((ConfigOptionFloats, retract_restart_extra_toolchange))
((ConfigOptionFloats, retraction_speed))
@ -790,6 +803,7 @@ PRINT_CONFIG_CLASS_DEFINE(
//BBS
((ConfigOptionEnum<NozzleType>, nozzle_type))
((ConfigOptionBool, auxiliary_fan))
)
// This object is mapped to Perl as Slic3r::Config::Print.
@ -830,11 +844,21 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionBools, reduce_fan_stop_start_freq))
((ConfigOptionInts, fan_cooling_layer_time))
((ConfigOptionStrings, filament_colour))
((ConfigOptionFloat, outer_wall_acceleration))
((ConfigOptionFloat, inner_wall_acceleration))
((ConfigOptionFloat, top_surface_acceleration))
((ConfigOptionFloat, initial_layer_acceleration))
((ConfigOptionFloat, travel_acceleration))
((ConfigOptionFloat, initial_layer_line_width))
((ConfigOptionFloat, initial_layer_print_height))
((ConfigOptionFloat, initial_layer_speed))
((ConfigOptionFloat, default_jerk))
((ConfigOptionFloat, outer_wall_jerk))
((ConfigOptionFloat, inner_wall_jerk))
((ConfigOptionFloat, top_surface_jerk))
((ConfigOptionFloat, initial_layer_jerk))
((ConfigOptionFloat, travel_jerk))
//BBS
((ConfigOptionFloat, initial_layer_infill_speed))
((ConfigOptionInts, nozzle_temperature_initial_layer))
@ -861,6 +885,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionBool, spiral_mode))
((ConfigOptionInt, standby_temperature_delta))
((ConfigOptionInts, nozzle_temperature))
((ConfigOptionInt , chamber_temperature))
((ConfigOptionBools, wipe))
// BBS
((ConfigOptionInts, bed_temperature_difference))
@ -888,7 +913,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionBool, has_prime_tower))
((ConfigOptionFloat, nozzle_volume))
((ConfigOptionEnum<TimelapseType>, timelapse_type))
((ConfigOptionPoints, thumbnails))
)
// This object is mapped to Perl as Slic3r::Config::Full.

View file

@ -667,6 +667,7 @@ bool PrintObject::invalidate_state_by_config_options(
} else if (
opt_key == "wall_loops"
|| opt_key == "only_one_wall_top"
|| opt_key == "only_one_wall_first_layer"
|| opt_key == "initial_layer_line_width"
|| opt_key == "inner_wall_line_width"
|| opt_key == "infill_wall_overlap") {
@ -762,13 +763,15 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "minimum_sparse_infill_area"
|| opt_key == "sparse_infill_filament"
|| opt_key == "solid_infill_filament"
|| opt_key == "sparse_infill_line_width") {
|| opt_key == "sparse_infill_line_width"
|| opt_key == "infill_direction"
|| opt_key == "bridge_angle") {
steps.emplace_back(posPrepareInfill);
} else if (
opt_key == "top_surface_pattern"
|| opt_key == "bottom_surface_pattern"
|| opt_key == "external_fill_link_max_length"
|| opt_key == "infill_direction"
|| opt_key == "sparse_infill_pattern"
|| opt_key == "top_surface_line_width"
|| opt_key == "initial_layer_line_width") {
steps.emplace_back(posInfill);

View file

@ -22,10 +22,15 @@ Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_he
{
const PrintConfig &print_config = object.print()->config();
ConfigOptionFloat config_width;
double flow_ratio = 1.0;
// Get extrusion width from configuration.
// (might be an absolute value, or a percent value, or zero for auto)
if (first_layer && print_config.initial_layer_line_width.value > 0) {
config_width = print_config.initial_layer_line_width;
if (first_layer) {
if(role != frExternalPerimeter)
flow_ratio = m_config.bottom_solid_infill_flow_ratio;
if(print_config.initial_layer_line_width.value > 0) {
config_width = print_config.initial_layer_line_width;
}
} else if (role == frExternalPerimeter) {
config_width = m_config.outer_wall_line_width;
} else if (role == frPerimeter) {
@ -36,6 +41,7 @@ Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_he
config_width = m_config.internal_solid_infill_line_width;
} else if (role == frTopSolidInfill) {
config_width = m_config.top_surface_line_width;
flow_ratio = m_config.top_solid_infill_flow_ratio;
} else {
throw Slic3r::InvalidArgument("Unknown role");
}
@ -46,7 +52,7 @@ Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_he
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
auto nozzle_diameter = float(print_config.nozzle_diameter.get_at(this->extruder(role) - 1));
return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height));
return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height)).with_flow_ratio(flow_ratio);
}
coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const

View file

@ -4,6 +4,7 @@
#define SLIC3R_APP_NAME "@SLIC3R_APP_NAME@"
#define SLIC3R_APP_KEY "@SLIC3R_APP_KEY@"
#define SLIC3R_VERSION "@SLIC3R_VERSION@"
#define SoftFever_VERSION "@SoftFever_VERSION@"
#define SLIC3R_BUILD_ID "@SLIC3R_BUILD_ID@"
//#define SLIC3R_RC_VERSION "@SLIC3R_VERSION@"
#define BBL_RELEASE_TO_PUBLIC @BBL_RELEASE_TO_PUBLIC@

View file

@ -1106,7 +1106,7 @@ std::string string_printf(const char *format, ...)
std::string header_slic3r_generated()
{
return std::string(SLIC3R_APP_NAME " " SLIC3R_VERSION);
return std::string(SLIC3R_APP_NAME "-SoftFever" " " SLIC3R_VERSION);
}
std::string header_gcodeviewer_generated()

View file

@ -1,5 +1,5 @@
project(minilzo)
cmake_minimum_required(VERSION 2.6)
cmake_minimum_required(VERSION 3.0)
add_library(minilzo INTERFACE)

View file

@ -28,7 +28,7 @@ endif()
else(Qhull_FOUND)
project(qhull)
cmake_minimum_required(VERSION 2.6)
cmake_minimum_required(VERSION 3.1)
# Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, qhull-warn.pri
set(qhull_VERSION2 "2015.2 2016/01/18") # not used, See global.c, global_r.c, rbox.c, rbox_r.c

View file

@ -181,6 +181,10 @@ set(SLIC3R_GUI_SOURCES
GUI/SavePresetDialog.cpp
GUI/GUI_Colors.hpp
GUI/GUI_Colors.cpp
GUI/PhysicalPrinterDialog.hpp
GUI/PhysicalPrinterDialog.cpp
GUI/PrintHostDialogs.cpp
GUI/PrintHostDialogs.hpp
GUI/GUI_Factories.cpp
GUI/GUI_Factories.hpp
GUI/GUI_ObjectList.cpp
@ -231,6 +235,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Monitor.hpp
GUI/WebViewDialog.cpp
GUI/WebViewDialog.hpp
GUI/PrinterWebView.cpp
GUI/PrinterWebView.hpp
GUI/WebDownPluginDlg.hpp
GUI/WebDownPluginDlg.cpp
GUI/WebGuideDialog.hpp
@ -368,6 +374,10 @@ set(SLIC3R_GUI_SOURCES
GUI/Calibration.cpp
GUI/PrintOptionsDialog.hpp
GUI/PrintOptionsDialog.cpp
GUI/BonjourDialog.hpp
GUI/BonjourDialog.cpp
GUI/BedShapeDialog.hpp
GUI/BedShapeDialog.cpp
Utils/json_diff.hpp
Utils/json_diff.cpp
GUI/KBShortcutsDialog.hpp
@ -399,6 +409,22 @@ set(SLIC3R_GUI_SOURCES
Utils/PrintHost.cpp
Utils/NetworkAgent.cpp
Utils/NetworkAgent.hpp
Utils/OctoPrint.cpp
Utils/OctoPrint.hpp
Utils/PrintHost.cpp
Utils/PrintHost.hpp
Utils/Serial.cpp
Utils/Serial.hpp
Utils/MKS.hpp
Utils/MKS.cpp
Utils/Duet.cpp
Utils/Duet.hpp
Utils/FlashAir.cpp
Utils/FlashAir.hpp
Utils/AstroBox.cpp
Utils/AstroBox.hpp
Utils/Repetier.cpp
Utils/Repetier.hpp
)
if (APPLE)

View file

@ -244,11 +244,8 @@ AboutDialog::AboutDialog()
// version
{
vesizer->Add(0, FromDIP(165), 1, wxEXPAND, FromDIP(5));
#if BBL_INTERNAL_TESTING
auto version_string = _L("Internal Version") + " " + std::string(SLIC3R_VERSION);
#else
auto version_string = _L("Version") + " " + std::string(SLIC3R_VERSION);
#endif
auto version_string = _L("SoftFever Version") + " " + std::string(SoftFever_VERSION);
wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize);
wxFont version_font = GetFont();
#ifdef __WXMSW__

View file

@ -250,13 +250,13 @@ void BBLTopbar::Init(wxFrame* parent)
this->AddSpacer(FromDIP(10));
this->AddStretchSpacer(1);
#if !BBL_RELEASE_TO_PUBLIC
// #if !BBL_RELEASE_TO_PUBLIC
/*wxBitmap m_publish_bitmap = create_scaled_bitmap("topbar_publish", nullptr, TOPBAR_ICON_SIZE);
m_publish_item = this->AddTool(ID_PUBLISH, "", m_publish_bitmap);
wxBitmap m_publish_disable_bitmap = create_scaled_bitmap("topbar_publish_disable", nullptr, TOPBAR_ICON_SIZE);
m_publish_item->SetDisabledBitmap(m_publish_disable_bitmap);
this->AddSpacer(FromDIP(12));*/
#endif
// #endif
/*wxBitmap model_store_bitmap = create_scaled_bitmap("topbar_store", nullptr, TOPBAR_ICON_SIZE);
m_model_store_item = this->AddTool(ID_MODEL_STORE, "", model_store_bitmap);

View file

@ -183,6 +183,13 @@ std::string BackgroundSlicingProcess::output_filepath_for_project(const boost::f
void BackgroundSlicingProcess::process_fff()
{
assert(m_print == m_fff_print);
PresetBundle &preset_bundle = *wxGetApp().preset_bundle;
m_fff_print->is_BBL_printer() =
preset_bundle.printers.get_edited_preset().is_bbl_vendor_preset(
&preset_bundle);
//BBS: add the logic to process from an existed gcode file
if (m_print->finished()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: skip slicing, to process previous gcode file")%__LINE__;
@ -837,8 +844,9 @@ void BackgroundSlicingProcess::prepare_upload()
m_upload_job.upload_data.upload_path = output_name_str;
} else {
m_upload_job.upload_data.upload_path = m_sla_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
ThumbnailsList thumbnails = this->render_thumbnails(
ThumbnailsParams{current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, true, true});
ThumbnailsParams{current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, true, true});
// true, false, true, true); // renders also supports and pad
Zipper zipper{source_path.string()};
m_sla_archive.export_print(zipper, *m_sla_print, m_upload_job.upload_data.upload_path.string());

View file

@ -56,6 +56,7 @@ void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter para
def.max = 600;
def.label = get_option_label(param);
def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
def.readonly = true;
key = "rect_origin";
break;
case Parameter::Diameter:
@ -191,9 +192,9 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf
BedShape::append_option_line(optgroup, BedShape::Parameter::RectOrigin);
activate_options_page(optgroup);
optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Circle));
BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter);
activate_options_page(optgroup);
// optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Circle));
// BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter);
// activate_options_page(optgroup);
optgroup = init_shape_options_page(BedShape::get_name(BedShape::PageType::Custom));

View file

@ -554,17 +554,14 @@ void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up
void Camera::set_default_orientation()
{
// BBS modify default orientation
look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ());
/*m_zenit = 45.0f;
m_zenit = 45.0f;
const double theta_rad = Geometry::deg2rad(-(double)m_zenit);
const double phi_rad = Geometry::deg2rad(45.0);
const double sin_theta = ::sin(theta_rad);
const Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad));
m_view_rotation = Eigen::AngleAxisd(theta_rad, Vec3d::UnitX()) * Eigen::AngleAxisd(phi_rad, Vec3d::UnitZ());
m_view_rotation.normalize();
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-camera_pos), m_view_rotation, Vec3d::Ones());*/
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-camera_pos), m_view_rotation, Vec3d::Ones());
}
Vec3d Camera::validate_target(const Vec3d& target) const

View file

@ -469,7 +469,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
toggle_field(el, has_solid_infill);
for (auto el : { "infill_direction", "sparse_infill_line_width",
"sparse_infill_speed", "bridge_speed" })
"sparse_infill_speed", "bridge_speed", "bridge_angle" })
toggle_field(el, have_infill || has_solid_infill);
toggle_field("top_shell_thickness", ! has_spiral_vase && has_top_solid_infill);
@ -483,9 +483,14 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
bool have_default_acceleration = config->opt_float("default_acceleration") > 0;
//BBS
for (auto el : { "initial_layer_acceleration", "top_surface_acceleration" })
for (auto el : { "outer_wall_acceleration", "inner_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration","travel_acceleration" })
toggle_field(el, have_default_acceleration);
bool have_default_jerk = config->opt_float("default_jerk") > 0;
for (auto el : { "outer_wall_jerk", "inner_wall_jerk", "initial_layer_jerk", "top_surface_jerk","travel_jerk" })
toggle_field(el, have_default_jerk);
bool have_skirt = config->opt_int("skirt_loops") > 0;
toggle_field("skirt_height", have_skirt && config->opt_enum<DraftShield>("draft_shield") != dsEnabled);
for (auto el : { "skirt_distance", "draft_shield"})

View file

@ -2394,7 +2394,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
p->load_vendors();
//BBS: add bed exclude areas
p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({
"gcode_flavor", "printable_area", "bed_exclude_area", "nozzle_diameter", "filament_diameter", "nozzle_temperature", /*"bed_temperature",*/
"gcode_flavor", "printable_area", "bed_exclude_area", "filament_diameter", "nozzle_temperature", "thumbnails"/*"bed_temperature",*/
}));
auto *vsizer = new wxBoxSizer(wxVERTICAL);

View file

@ -4708,12 +4708,12 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const
double width = volumes_box.max.x() - volumes_box.min.x();
double depth = volumes_box.max.y() - volumes_box.min.y();
double height = volumes_box.max.z() - volumes_box.min.z();
volumes_box.max.x() = volumes_box.max.x() + width * 0.25f;
volumes_box.min.x() = volumes_box.min.x() - width * 0.25f;
volumes_box.max.y() = volumes_box.max.y() + depth * 0.25f;
volumes_box.min.y() = volumes_box.min.y() - depth * 0.25f;
volumes_box.max.z() = volumes_box.max.z() + height * 0.25f;
volumes_box.min.z() = volumes_box.min.z() - height * 0.25f;
volumes_box.max.x() = volumes_box.max.x() + width * 0.05f;
volumes_box.min.x() = volumes_box.min.x() - width * 0.05f;
volumes_box.max.y() = volumes_box.max.y() + depth * 0.05f;
volumes_box.min.y() = volumes_box.min.y() - depth * 0.05f;
volumes_box.max.z() = volumes_box.max.z() + height * 0.05f;
volumes_box.min.z() = volumes_box.min.z() - height * 0.05f;
Camera camera;
camera.set_type(camera_type);
@ -4728,16 +4728,11 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const
camera.zoom_to_box(volumes_box);
const Vec3d& target = camera.get_target();
double distance = camera.get_distance();
//camera.select_view("topfront");
camera.look_at(target - 0.707 * distance * Vec3d::UnitY() + 0.3 * distance * Vec3d::UnitZ(), target, Vec3d::UnitY() + Vec3d::UnitZ());
camera.select_view("iso");
camera.apply_view_matrix();
camera.apply_projection(plate_build_volume);
//double near_z = -1.0;
//double far_z = -1.0;
//camera.apply_projection(volumes_box, near_z, far_z);
//GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader == nullptr) {
BOOST_LOG_TRIVIAL(info) << boost::format("render_thumbnail: shader is null, return directly");
@ -4745,7 +4740,7 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const
}
//if (thumbnail_params.transparent_background)
glsafe(::glClearColor(0.906f, 0.906f, 0.906f, 1.0f));
glsafe(::glClearColor(0.2f, 0.2f, 0.2f, 0.0f));
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_DEPTH_TEST));

View file

@ -197,7 +197,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
}
break;
case coPoints:{
if (opt_key == "printable_area" || opt_key == "bed_exclude_area") {
if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "thumbnails") {
config.option<ConfigOptionPoints>(opt_key)->values = boost::any_cast<std::vector<Vec2d>>(value);
break;
}

View file

@ -280,14 +280,15 @@ public:
memDc.SetTextForeground(wxColor(134, 134, 134));
memDc.DrawLabel(m_constant_text.version, version_rect, wxALIGN_LEFT | wxALIGN_BOTTOM);
#if BBL_INTERNAL_TESTING
wxSize text_rect = memDc.GetTextExtent("Internal Version");
// #if BBL_INTERNAL_TESTING
auto sf_version = wxString::Format("SoftFever %s",std::string(SoftFever_VERSION)).ToStdString();
wxSize text_rect = memDc.GetTextExtent(sf_version);
int start_x = (title_rect.GetLeft() + version_rect.GetRight()) / 2 - text_rect.GetWidth();
int start_y = version_rect.GetBottom() + 10;
wxRect internal_sign_rect(wxPoint(start_x, start_y), wxSize(text_rect));
memDc.SetFont(m_constant_text.title_font);
memDc.DrawLabel("Internal Version", internal_sign_rect, wxALIGN_TOP | wxALIGN_LEFT);
#endif
memDc.SetFont(m_constant_text.version_font);
memDc.DrawLabel(sf_version, internal_sign_rect, wxALIGN_CENTER);
// #endif
// load bitmap for logo
BitmapCache bmp_cache;
@ -554,11 +555,11 @@ private:
title = wxGetApp().is_editor() ? SLIC3R_APP_FULL_NAME : GCODEVIEWER_APP_NAME;
// dynamically get the version to display
#if BBL_INTERNAL_TESTING
version = _L("Internal Version") + " " + std::string(SLIC3R_VERSION);
#else
version = _L("Version") + " " + std::string(SLIC3R_VERSION);
#endif
// #if BBL_INTERNAL_TESTING
// version = _L("Internal Version") + " " + std::string(SLIC3R_VERSION);
// #else
version = _L("SoftFever Version") + " " + std::string(SoftFever_VERSION);
// #endif
// credits infornation
credits = title;
@ -1812,7 +1813,7 @@ void GUI_App::init_download_path()
void GUI_App::init_app_config()
{
// Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release.
SetAppName(SLIC3R_APP_KEY);
SetAppName("BambuStudio-SoftFever");
// SetAppName(SLIC3R_APP_KEY "-alpha");
// SetAppName(SLIC3R_APP_KEY "-beta");
// SetAppDisplayName(SLIC3R_APP_NAME);

View file

@ -7,6 +7,7 @@
#include "ConfigWizard.hpp"
#include "OpenGLManager.hpp"
#include "libslic3r/Preset.hpp"
#include "wxExtensions.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "slic3r/GUI/DeviceManager.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
@ -45,6 +46,7 @@ class AppConfig;
class PresetBundle;
class PresetUpdater;
class ModelObject;
// class PrintHostJobQueue;
class Model;
class DeviceManager;
class NetworkAgent;

View file

@ -94,7 +94,7 @@ std::map<std::string, std::vector<SimpleSettingData>> SettingsFactory::PART_CAT
{ L("Strength"), {{"wall_loops", "",1},{"top_shell_layers", L("Top Solid Layers"),1},{"top_shell_thickness", L("Top Minimum Shell Thickness"),1},
{"bottom_shell_layers", L("Bottom Solid Layers"),1}, {"bottom_shell_thickness", L("Bottom Minimum Shell Thickness"),1},
{"sparse_infill_density", "",1},{"sparse_infill_pattern", "",1},{"top_surface_pattern", "",1},{"bottom_surface_pattern", "",1},
{"infill_combination", "",1}, {"infill_wall_overlap", "",1}, {"infill_direction", "",1}, {"minimum_sparse_infill_area", "",1}
{"infill_combination", "",1}, {"infill_wall_overlap", "",1}, {"infill_direction", "",1}, {"bridge_angle", "",1}, {"minimum_sparse_infill_area", "",1}
}},
{ L("Speed"), {{"outer_wall_speed", "",1},{"inner_wall_speed", "",2},{"sparse_infill_speed", "",3},{"top_surface_speed", "",4}, {"internal_solid_infill_speed", "",5},
{"enable_overhang_speed", "",6}, {"overhang_1_4_speed", "",7}, {"overhang_2_4_speed", "",8}, {"overhang_3_4_speed", "",9}, {"overhang_4_4_speed", "",10},

View file

@ -73,6 +73,7 @@ wxDEFINE_EVENT(EVT_USER_LOGIN, wxCommandEvent);
// BBS: backup
wxDEFINE_EVENT(EVT_BACKUP_POST, wxCommandEvent);
wxDEFINE_EVENT(EVT_LOAD_URL, wxCommandEvent);
wxDEFINE_EVENT(EVT_LOAD_PRINTER_URL, wxCommandEvent);
enum class ERescaleTarget
{
@ -934,6 +935,13 @@ void MainFrame::init_tabpanel()
m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_tabpanel->AddPage(m_monitor, _L("Device"), std::string("tab_monitor_active"), std::string("tab_monitor_active"));
m_printer_view = new PrinterWebView(m_tabpanel);
Bind(EVT_LOAD_PRINTER_URL, [this](wxCommandEvent &evt) {
wxString url = evt.GetString();
//select_tab(MainFrame::tpMonitor);
m_printer_view->load_url(url);
});
m_auxiliary = new AuxiliaryPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_tabpanel->AddPage(m_auxiliary, _L("Project"), std::string("tab_auxiliary_avtice"), std::string("tab_auxiliary_avtice"));
@ -951,6 +959,33 @@ void MainFrame::init_tabpanel()
}
}
// SoftFever
void MainFrame::show_device(bool bBBLPrinter) {
if (m_tabpanel->GetPage(3) != m_monitor &&
m_tabpanel->GetPage(3) != m_printer_view) {
BOOST_LOG_TRIVIAL(error) << "Failed to find device tab";
return;
}
if (bBBLPrinter) {
if (m_tabpanel->GetPage(3) != m_monitor) {
m_tabpanel->RemovePage(3);
m_tabpanel->InsertPage(3, m_monitor, _L("Device"),
std::string("tab_monitor_active"),
std::string("tab_monitor_active"));
}
} else {
if (m_tabpanel->GetPage(3) != m_printer_view) {
m_tabpanel->RemovePage(3);
m_tabpanel->InsertPage(3, m_printer_view, _L("Device"),
std::string("tab_monitor_active"),
std::string("tab_monitor_active"));
}
}
}
#ifdef WIN32
void MainFrame::register_win32_callbacks()
{
@ -2859,6 +2894,13 @@ void MainFrame::load_url(wxString url)
wxQueueEvent(this, evt);
}
void MainFrame::load_printer_url(wxString url)
{
BOOST_LOG_TRIVIAL(trace) << "load_printer_url:" << url;
auto evt = new wxCommandEvent(EVT_LOAD_PRINTER_URL, this->GetId());
evt->SetString(url);
wxQueueEvent(this, evt);
}
void MainFrame::refresh_plugin_tips()
{
if (m_webview != nullptr)

View file

@ -28,6 +28,8 @@
#include "BBLTopbar.hpp"
#include "PrinterWebView.hpp"
#define ENABEL_PRINT_ALL 0
class Notebook;
@ -307,9 +309,13 @@ public:
//BBS
void load_url(wxString url);
void load_printer_url(wxString url);
void refresh_plugin_tips();
void RunScript(wxString js);
//SoftFever
void show_device(bool bBBLPrinter);
// BBS. Replace title bar and menu bar with top bar.
BBLTopbar* m_topbar{ nullptr };
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
@ -318,6 +324,7 @@ public:
MonitorPanel* m_monitor{ nullptr };
AuxiliaryPanel* m_auxiliary{ nullptr };
WebViewPanel* m_webview { nullptr };
PrinterWebView* m_printer_view{nullptr};
wxLogWindow* m_log_window { nullptr };
// BBS
//wxBookCtrlBase* m_tabpanel { nullptr };

View file

@ -114,9 +114,13 @@ Button* MsgDialog::add_button(wxWindowID btn_id, bool set_focus /*= false*/, con
else if (label.length() >= 5 && label.length() < 8) {
type = ButtonSizeMiddle;
btn->SetMinSize(MSG_DIALOG_MIDDLE_BUTTON_SIZE);
}
else if (label.length() >= 8 && label.length() < 12) {
type = ButtonSizeMiddle;
btn->SetMinSize(MSG_DIALOG_LONG_BUTTON_SIZE);
} else {
type = ButtonSizeLong;
btn->SetMinSize(MSG_DIALOG_LONG_BUTTON_SIZE);
btn->SetMinSize(MSG_DIALOG_LONGER_BUTTON_SIZE);
}
btn->SetCornerRadius(FromDIP(12));

View file

@ -29,6 +29,7 @@ enum ButtonSizeType{
#define MSG_DIALOG_BUTTON_SIZE wxSize(FromDIP(58), FromDIP(24))
#define MSG_DIALOG_MIDDLE_BUTTON_SIZE wxSize(FromDIP(76), FromDIP(24))
#define MSG_DIALOG_LONG_BUTTON_SIZE wxSize(FromDIP(90), FromDIP(24))
#define MSG_DIALOG_LONGER_BUTTON_SIZE wxSize(FromDIP(120), FromDIP(24))
namespace Slic3r {

View file

@ -670,7 +670,7 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config,
else if (m_opt_map.find(opt_key) == m_opt_map.end() ||
// This option don't have corresponded field
opt_key == "printable_area" ||
opt_key == "compatible_printers" || opt_key == "compatible_prints" ) {
opt_key == "compatible_printers" || opt_key == "compatible_prints" || opt_key == "thumbnails" ) {
value = get_config_value(config, opt_key);
this->change_opt_value(opt_key, value);
return;
@ -950,6 +950,14 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
case coInts:
ret = config.option<ConfigOptionIntsNullable>(opt_key)->get_at(idx);
break;
case coPoints:
if (opt_key == "bed_shape")
ret = config.option<ConfigOptionPoints>(opt_key)->values;
else if (opt_key == "thumbnails")
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
else
ret = config.option<ConfigOptionPoints>(opt_key)->get_at(idx);
break;
default:
break;
}
@ -1026,6 +1034,8 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
if (opt_key == "printable_area")
ret = config.option<ConfigOptionPoints>(opt_key)->values;
else if (opt_key == "bed_exclude_area")
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
else if (opt_key == "thumbnails")
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
else
ret = config.option<ConfigOptionPoints>(opt_key)->get_at(idx);
@ -1137,6 +1147,8 @@ boost::any ConfigOptionsGroup::get_config_value2(const DynamicPrintConfig& confi
ret = config.option<ConfigOptionPoints>(opt_key)->values;
else if (opt_key == "bed_exclude_area")
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
else if (opt_key == "thumbnails")
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
else
ret = config.option<ConfigOptionPoints>(opt_key)->get_at(idx);
break;

View file

@ -23,7 +23,7 @@
#include "format.hpp"
#include "Tab.hpp"
#include "wxExtensions.hpp"
//#include "PrintHostDialogs.hpp"
#include "PrintHostDialogs.hpp"
#include "../Utils/ASCIIFolding.hpp"
#include "../Utils/PrintHost.hpp"
#include "../Utils/FixModelByWin10.hpp"
@ -189,7 +189,6 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
Option option = m_optgroup->get_option("print_host");
option.opt.width = Field::def_width_wider();
Line host_line = m_optgroup->create_single_option_line(option);
//do not support now
host_line.append_widget(printhost_browse);
host_line.append_widget(print_host_test);
m_optgroup->append_line(host_line);
@ -228,36 +227,36 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
};
cafile_line.append_widget(printhost_cafile_browse);
//m_optgroup->append_line(cafile_line);
m_optgroup->append_line(cafile_line);
/*Line cafile_hint{ "", "" };
Line cafile_hint{ "", "" };
cafile_hint.full_width = 1;
cafile_hint.widget = [ca_file_hint](wxWindow* parent) {
auto txt = new wxStaticText(parent, wxID_ANY, ca_file_hint);
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(txt);
return sizer;
};*/
//m_optgroup->append_line(cafile_hint);
};
m_optgroup->append_line(cafile_hint);
}
else {
//Line line{ "", "" };
//line.full_width = 1;
Line line{ "", "" };
line.full_width = 1;
//line.widget = [ca_file_hint](wxWindow* parent) {
// std::string info = _u8L("HTTPS CA File") + ":\n\t" +
// (boost::format(_u8L("On this system, %s uses HTTPS certificates from the system Certificate Store or Keychain.")) % SLIC3R_APP_NAME).str() +
// "\n\t" + _u8L("To use a custom CA file, please import your CA file into Certificate Store / Keychain.");
line.widget = [ca_file_hint](wxWindow* parent) {
std::string info = _u8L("HTTPS CA File") + ":\n\t" +
(boost::format(_u8L("On this system, %s uses HTTPS certificates from the system Certificate Store or Keychain.")) % SLIC3R_APP_NAME).str() +
"\n\t" + _u8L("To use a custom CA file, please import your CA file into Certificate Store / Keychain.");
// //auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\n\t%2%") % info % ca_file_hint).str()));
// auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\t%2%") % info % ca_file_hint).str()));
// txt->SetFont(wxGetApp().normal_font());
// auto sizer = new wxBoxSizer(wxHORIZONTAL);
// sizer->Add(txt, 1, wxEXPAND);
// return sizer;
//};
//m_optgroup->append_line(line);
//auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\n\t%2%") % info % ca_file_hint).str()));
auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\t%2%") % info % ca_file_hint).str()));
txt->SetFont(wxGetApp().normal_font());
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(txt, 1, wxEXPAND);
return sizer;
};
m_optgroup->append_line(line);
}
for (const std::string& opt_key : std::vector<std::string>{ "printhost_user", "printhost_password" }) {
@ -267,13 +266,10 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
}
#ifdef WIN32
/*
option = m_optgroup->get_option("printhost_ssl_ignore_revoke");
option.opt.width = Field::def_width_wider();
m_optgroup->append_single_option_line(option);
*/
#endif
m_optgroup->activate();
@ -422,6 +418,7 @@ void PhysicalPrinterDialog::update(bool printer_change)
m_optgroup->hide_field(opt_key);
supports_multiple_printers = opt && opt->value == htRepetier;
}
}
else {
m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false);

View file

@ -911,17 +911,22 @@ void Sidebar::update_all_preset_comboboxes()
bool is_bbl_preset = preset_bundle.printers.get_edited_preset().is_bbl_vendor_preset(&preset_bundle);
auto p_mainframe = wxGetApp().mainframe;
p_mainframe->show_device(is_bbl_preset);
if (is_bbl_preset) {
//only show connection button for not-BBL printer
connection_btn->Hide();
//only show sync-ams button for BBL printer
ams_btn->Show();
//update print button default value for bbl or third-party printer
wxGetApp().mainframe->set_print_button_to_default(MainFrame::PrintSelectType::ePrintPlate);
p_mainframe->set_print_button_to_default(MainFrame::PrintSelectType::ePrintPlate);
} else {
connection_btn->Show();
ams_btn->Hide();
wxGetApp().mainframe->set_print_button_to_default(MainFrame::PrintSelectType::eSendGcode);
p_mainframe->set_print_button_to_default(MainFrame::PrintSelectType::eSendGcode);
p_mainframe->load_printer_url(wxString::Format("http://%s",preset_bundle.printers.get_edited_preset().config.opt_string("print_host")));
}
// Update the print choosers to only contain the compatible presets, update the dirty flags.
@ -9080,7 +9085,7 @@ void Plater::send_gcode_legacy(int plate_idx, Export3mfProgressFn proFn, bool up
upload_job.printhost->get_groups(groups);
}
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->get_post_upload_actions(), groups, upload_only);
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->get_post_upload_actions(), groups);
if (dlg.ShowModal() == wxID_OK) {
upload_job.upload_data.upload_path = dlg.filename();
upload_job.upload_data.post_action = dlg.post_action();
@ -10646,4 +10651,4 @@ SuppressBackgroundProcessingUpdate::~SuppressBackgroundProcessingUpdate()
wxGetApp().plater()->schedule_background_process(m_was_scheduled);
}
}} // namespace Slic3r::GUI
}} // namespace Slic3r::GUI

View file

@ -687,4 +687,4 @@ private:
} // namespace GUI
} // namespace Slic3r
#endif
#endif

View file

@ -36,8 +36,8 @@ namespace GUI {
static const char *CONFIG_KEY_PATH = "printhost_path";
static const char *CONFIG_KEY_GROUP = "printhost_group";
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUploadActions post_actions, const wxArrayString &groups, bool upload_only)
: MsgDialog(static_cast<wxWindow*>(wxGetApp().mainframe), _L("Upload and Print"), _L("Upload to Printer Host with the following filename:"),0)
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUploadActions post_actions, const wxArrayString &groups)
: MsgDialog(static_cast<wxWindow*>(wxGetApp().mainframe), _L("Send to print"), _L("Upload to Printer Host with the following filename:"),0)
, txt_filename(new wxTextCtrl(this, wxID_ANY))
, combo_groups(!groups.IsEmpty() ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, groups, wxCB_READONLY) : nullptr)
, post_upload_action(PrintHostPostUploadAction::None)
@ -88,19 +88,7 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo
return true;
};
auto* btn_confirm = add_button(wxID_YES, false, _L("Confirm"));
btn_confirm->Bind(wxEVT_BUTTON, [this, upload_only, validate_path](wxCommandEvent&) {
if (validate_path(txt_filename->GetValue())) {
if (upload_only) {
post_upload_action = PrintHostPostUploadAction::None;
} else {
post_upload_action = PrintHostPostUploadAction::StartPrint;
}
EndDialog(wxID_OK);
}
});
/*auto* btn_upload = add_button(wxID_YES, false, _L("Upload"));
auto* btn_upload = add_button(wxID_YES, false, _L("Upload"));
btn_upload->Bind(wxEVT_BUTTON, [this, validate_path](wxCommandEvent&) {
if (validate_path(txt_filename->GetValue())) {
post_upload_action = PrintHostPostUploadAction::None;
@ -108,7 +96,7 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo
}
});
if (post_actions.has(PrintHostPostUploadAction::StartPrint) && !upload_only) {
if (post_actions.has(PrintHostPostUploadAction::StartPrint)) {
auto* btn_print = add_button(wxID_YES, false, _L("Upload and Print"));
btn_print->Bind(wxEVT_BUTTON, [this, validate_path](wxCommandEvent&) {
if (validate_path(txt_filename->GetValue())) {
@ -128,9 +116,8 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo
}
});
}
*/
add_button(wxID_CANCEL, false, "Cancel");
add_button(wxID_CANCEL,false,"Cancel");
finalize();
#ifdef __linux__

View file

@ -26,7 +26,7 @@ namespace GUI {
class PrintHostSendDialog : public GUI::MsgDialog
{
public:
PrintHostSendDialog(const boost::filesystem::path &path, PrintHostPostUploadActions post_actions, const wxArrayString& groups, bool upload_only);
PrintHostSendDialog(const boost::filesystem::path &path, PrintHostPostUploadActions post_actions, const wxArrayString& groups);
boost::filesystem::path filename() const;
PrintHostPostUploadAction post_action() const;
std::string group() const;

View file

@ -0,0 +1,125 @@
#include "PrinterWebView.hpp"
#include "I18N.hpp"
#include "slic3r/GUI/wxExtensions.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/MainFrame.hpp"
#include "libslic3r_version.h"
#include <wx/sizer.h>
#include <wx/toolbar.h>
#include <wx/textdlg.h>
#include <slic3r/GUI/Widgets/WebView.hpp>
namespace pt = boost::property_tree;
namespace Slic3r {
namespace GUI {
PrinterWebView::PrinterWebView(wxWindow *parent)
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
{
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
// Create the button
bSizer_toolbar = new wxBoxSizer(wxHORIZONTAL);
//m_button_reload = new wxButton(this, wxID_ANY, wxT("Reload"), wxDefaultPosition, wxDefaultSize, 0);
//bSizer_toolbar->Add(m_button_reload, 0, wxALL, 5);
m_url = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
bSizer_toolbar->Add(m_url, 1, wxALL | wxEXPAND, 5);
// Create the webview
m_browser = WebView::CreateWebView(this, "");
if (m_browser == nullptr) {
wxLogError("Could not init m_browser");
return;
}
SetSizer(topsizer);
topsizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
// Log backend information
if (wxGetApp().get_mode() == comDevelop) {
wxLogMessage(wxWebView::GetBackendVersionInfo().ToString());
wxLogMessage("Backend: %s Version: %s", m_browser->GetClassInfo()->GetClassName(),
wxWebView::GetBackendVersionInfo().ToString());
wxLogMessage("User Agent: %s", m_browser->GetUserAgent());
}
//Zoom
m_zoomFactor = 100;
// Connect the button events
//Bind(wxEVT_BUTTON, &PrinterWebView::OnReload, this, m_button_reload->GetId());
Bind(wxEVT_TEXT_ENTER, &PrinterWebView::OnUrl, this, m_url->GetId());
//Connect the idle events
Bind(wxEVT_CLOSE_WINDOW, &PrinterWebView::OnClose, this);
}
PrinterWebView::~PrinterWebView()
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Start";
SetEvtHandlerEnabled(false);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " End";
}
void PrinterWebView::load_url(wxString& url)
{
//this->Show();
//this->Raise();
m_url->SetLabelText(url);
if (wxGetApp().get_mode() == comDevelop)
wxLogMessage(m_url->GetValue());
m_browser->LoadURL(url);
//m_browser->SetFocus();
UpdateState();
}
/**
* Method that retrieves the current state from the web control and updates the
* GUI the reflect this current state.
*/
void PrinterWebView::UpdateState() {
// SetTitle(m_browser->GetCurrentTitle());
m_url->SetValue(m_browser->GetCurrentURL());
}
/**
* Callback invoked when user entered an URL and pressed enter
*/
void PrinterWebView::OnUrl(wxCommandEvent& WXUNUSED(evt))
{
if (wxGetApp().get_mode() == comDevelop)
wxLogMessage(m_url->GetValue());
m_browser->LoadURL(m_url->GetValue());
m_browser->SetFocus();
UpdateState();
}
/**
* Callback invoked when user pressed the "reload" button
*/
void PrinterWebView::OnReload(wxCommandEvent& WXUNUSED(evt))
{
m_browser->Reload();
UpdateState();
}
void PrinterWebView::OnClose(wxCloseEvent& evt)
{
this->Hide();
}
} // GUI
} // Slic3r

View file

@ -0,0 +1,64 @@
#ifndef slic3r_PrinterWebView_hpp_
#define slic3r_PrinterWebView_hpp_
#include "wx/artprov.h"
#include "wx/cmdline.h"
#include "wx/notifmsg.h"
#include "wx/settings.h"
#include "wx/webview.h"
#if wxUSE_WEBVIEW_EDGE
#include "wx/msw/webview_edge.h"
#endif
#include "wx/webviewarchivehandler.h"
#include "wx/webviewfshandler.h"
#include "wx/numdlg.h"
#include "wx/infobar.h"
#include "wx/filesys.h"
#include "wx/fs_arc.h"
#include "wx/fs_mem.h"
#include "wx/stdpaths.h"
#include <wx/panel.h>
#include <wx/tbarbase.h>
#include "wx/textctrl.h"
#include <wx/timer.h>
namespace Slic3r {
namespace GUI {
class PrinterWebView : public wxPanel {
public:
PrinterWebView(wxWindow *parent);
virtual ~PrinterWebView();
void load_url(wxString& url);
void UpdateState();
void OnUrl(wxCommandEvent& evt);
void OnReload(wxCommandEvent& evt);
void OnClose(wxCloseEvent& evt);
private:
wxWebView* m_browser;
wxBoxSizer *bSizer_toolbar;
wxButton * m_button_reload;
wxTextCtrl *m_url;
long m_zoomFactor;
// Last executed JavaScript snippet, for convenience.
// wxString m_javascript;
// wxString m_response_js;
// DECLARE_EVENT_TABLE()
};
} // GUI
} // Slic3r
#endif /* slic3r_Tab_hpp_ */

View file

@ -43,6 +43,8 @@
#include "MarkdownTip.hpp"
#include "Search.hpp"
#include "BedShapeDialog.hpp"
// #include "BonjourDialog.hpp"
#ifdef WIN32
#include <commctrl.h>
#endif // WIN32
@ -853,7 +855,7 @@ void TabPrinter::init_options_list()
for (const std::string& opt_key : m_config->keys())
{
if (opt_key == "printable_area" || opt_key == "bed_exclude_area") {
if (opt_key == "printable_area" || opt_key == "bed_exclude_area" | opt_key == "thumbnails") {
m_options_list.emplace(opt_key, m_opt_status_value);
continue;
}
@ -1779,8 +1781,11 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Advanced"), L"param_advanced");
optgroup->append_single_option_line("wall_infill_order");
optgroup->append_single_option_line("bridge_flow");
optgroup->append_single_option_line("top_solid_infill_flow_ratio");
optgroup->append_single_option_line("bottom_solid_infill_flow_ratio");
optgroup->append_single_option_line("thick_bridges");
optgroup->append_single_option_line("only_one_wall_top");
optgroup->append_single_option_line("only_one_wall_first_layer");
optgroup->append_single_option_line("detect_overhang_wall");
optgroup->append_single_option_line("reduce_crossing_wall");
optgroup->append_single_option_line("max_travel_detour_distance");
@ -1805,6 +1810,7 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Advanced"), L"param_advanced");
optgroup->append_single_option_line("infill_wall_overlap");
optgroup->append_single_option_line("infill_direction");
optgroup->append_single_option_line("bridge_angle");
optgroup->append_single_option_line("minimum_sparse_infill_area");
optgroup->append_single_option_line("infill_combination");
optgroup->append_single_option_line("detect_narrow_internal_solid_infill");
@ -1837,9 +1843,20 @@ void TabPrint::build()
optgroup->append_single_option_line("travel_speed");
optgroup = page->new_optgroup(L("Acceleration"), L"param_acceleration", 15);
optgroup->append_single_option_line("default_acceleration");
optgroup->append_single_option_line("outer_wall_acceleration");
optgroup->append_single_option_line("inner_wall_acceleration");
optgroup->append_single_option_line("initial_layer_acceleration");
optgroup->append_single_option_line("top_surface_acceleration");
optgroup->append_single_option_line("default_acceleration");
optgroup->append_single_option_line("travel_acceleration");
optgroup = page->new_optgroup(L("Jerk(XY)"));
optgroup->append_single_option_line("default_jerk");
optgroup->append_single_option_line("outer_wall_jerk");
optgroup->append_single_option_line("inner_wall_jerk");
optgroup->append_single_option_line("top_surface_jerk");
optgroup->append_single_option_line("initial_layer_jerk");
optgroup->append_single_option_line("travel_jerk");
#ifdef HAS_PRESSURE_EQUALIZER
optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_positive");
@ -1922,7 +1939,10 @@ void TabPrint::build()
optgroup->append_single_option_line("reduce_infill_retraction");
optgroup->append_single_option_line("gcode_add_line_number");
Option option = optgroup->get_option("filename_format");
option.opt.full_width = true;
// option.opt.full_width = true;
option.opt.is_code = true;
option.opt.multiline = true;
// option.opt.height = 5;
optgroup->append_single_option_line(option);
#if 0
@ -2424,6 +2444,10 @@ void TabFilament::build()
//optgroup->append_single_option_line("filament_colour");
optgroup->append_single_option_line("filament_diameter");
optgroup->append_single_option_line("filament_flow_ratio");
optgroup->append_single_option_line("enable_pressure_advance");
optgroup->append_single_option_line("pressure_advance");
optgroup->append_single_option_line("filament_density");
optgroup->append_single_option_line("filament_cost");
//BBS
@ -2439,6 +2463,10 @@ void TabFilament::build()
optgroup = page->new_optgroup(L("Print temperature"), L"param_temperature");
optgroup->split_multi_line = true;
optgroup->option_label_at_right = true;
line = { L("Chamber temperature"), L("Chamber temperature") };
line.append_option(optgroup->get_option("chamber_temperature"));
optgroup->append_line(line);
line = { L("Nozzle"), L("Nozzle temperature when printing") };
line.append_option(optgroup->get_option("nozzle_temperature_initial_layer"));
line.append_option(optgroup->get_option("nozzle_temperature"));
@ -2637,7 +2665,11 @@ void TabFilament::toggle_options()
for (auto el : { "overhang_fan_speed", "overhang_fan_threshold" })
toggle_option(el, has_enable_overhang_bridge_fan);
}
if (m_active_page->title() == "Filament")
{
bool pa = m_config->opt_bool("enable_pressure_advance");
toggle_option("pressure_advance", pa);
}
if (m_active_page->title() == "Setting Overrides")
update_filament_overrides_page();
}
@ -2729,12 +2761,13 @@ void TabPrinter::build_fff()
auto page = add_options_page(L("Basic information"), "printer");
auto optgroup = page->new_optgroup(L("Printable space")/*, L"param_printable_space"*/);
//create_line_with_widget(optgroup.get(), "printable_area", "custom-svg-and-png-bed-textures_124612", [this](wxWindow* parent) {
// return create_bed_shape_widget(parent);
//});
create_line_with_widget(optgroup.get(), "printable_area", "custom-svg-and-png-bed-textures_124612", [this](wxWindow* parent) {
return create_bed_shape_widget(parent);
});
Option option = optgroup->get_option("bed_exclude_area");
option.opt.full_width = true;
optgroup->append_single_option_line(option);
// optgroup->append_single_option_line("printable_area");
optgroup->append_single_option_line("printable_height");
optgroup->append_single_option_line("nozzle_volume");
// BBS
@ -2835,6 +2868,9 @@ void TabPrinter::build_fff()
optgroup = page->new_optgroup(L("Advanced"), L"param_advanced");
optgroup->append_single_option_line("gcode_flavor");
option = optgroup->get_option("thumbnails");
option.opt.full_width = true;
optgroup->append_single_option_line(option);
optgroup->append_single_option_line("scan_first_layer");
// optgroup->append_single_option_line("spaghetti_detector");
optgroup->append_single_option_line("machine_load_filament_time");
@ -2870,8 +2906,8 @@ void TabPrinter::build_fff()
option.opt.is_code = true;
option.opt.height = gcode_field_height;//150;
optgroup->append_single_option_line(option);
#if 0
optgroup = page->new_optgroup(L("Before layer change G-code"), 0);
optgroup = page->new_optgroup(L("Before layer change G-code"),"param_gcode", 0);
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
@ -2880,7 +2916,6 @@ void TabPrinter::build_fff()
option.opt.is_code = true;
option.opt.height = gcode_field_height;//150;
optgroup->append_single_option_line(option);
#endif
optgroup = page->new_optgroup(L("Layer change G-code"), L"param_gcode", 0);
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
@ -2926,7 +2961,6 @@ void TabPrinter::build_fff()
// build_preset_description_line(optgroup.get());
#endif
build_unregular_pages(true);
}
@ -3100,7 +3134,7 @@ void TabPrinter::build_unregular_pages(bool from_initial_build/* = false*/)
{
size_t n_before_extruders = 2; // Count of pages before Extruder pages
auto flavor = m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value;
bool is_marlin_flavor = (flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware);
bool is_marlin_flavor = (flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware || flavor == gcfKlipper);
/* ! Freeze/Thaw in this function is needed to avoid call OnPaint() for erased pages
* and be cause of application crash, when try to change Preset in moment,
@ -3231,6 +3265,7 @@ void TabPrinter::build_unregular_pages(bool from_initial_build/* = false*/)
optgroup = page->new_optgroup(L("Retraction"), L"param_retraction");
optgroup->append_single_option_line("retraction_length", "", extruder_idx);
optgroup->append_single_option_line("z_hop", "", extruder_idx);
optgroup->append_single_option_line("z_lift_type", "", extruder_idx);
optgroup->append_single_option_line("retraction_speed", "", extruder_idx);
optgroup->append_single_option_line("deretraction_speed", "", extruder_idx);
//optgroup->append_single_option_line("retract_restart_extra", "", extruder_idx);
@ -3447,9 +3482,9 @@ void TabPrinter::toggle_options()
toggle_option("retract_restart_extra_toolchange", have_multiple_extruders && toolchange_retraction, i);
}
if (m_active_page->title() == "Motion ability") {
assert(m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlinLegacy
|| m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlinFirmware);
auto gcf = m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value;
if (m_active_page->title() == "Motion ability") {
assert(gcf == gcfMarlinLegacy || gcf == gcfMarlinFirmware || gcf == gcfKlipper);
bool silent_mode = m_config->opt_bool("silent_mode");
int max_field = silent_mode ? 2 : 1;
//BBS: limits of BBL printer can't be edited.
@ -3481,7 +3516,9 @@ void TabPrinter::update_fff()
m_use_silent_mode = m_config->opt_bool("silent_mode");
}
bool supports_travel_acceleration = (m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlinFirmware);
auto gcf_ =
m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value;
bool supports_travel_acceleration = (gcf_ == gcfMarlinFirmware || gcf_ == gcfMarlinLegacy || gcf_ == gcfKlipper);
if (m_supports_travel_acceleration != supports_travel_acceleration) {
m_rebuild_kinematics_page = true;
m_supports_travel_acceleration = supports_travel_acceleration;
@ -4609,7 +4646,7 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
}
// Return a callback to create a TabPrinter widget to edit bed shape
/*wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent)
wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent)
{
ScalableButton* btn = new ScalableButton(parent, wxID_ANY, "printer", " " + _(L("Set")) + " " + dots,
wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true);
@ -4639,7 +4676,7 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
}
return sizer;
}*/
}
void TabPrinter::cache_extruder_cnt()
{

View file

@ -547,6 +547,7 @@ private:
std::vector<PageShp> m_pages_fff;
std::vector<PageShp> m_pages_sla;
wxBoxSizer* m_presets_sizer {nullptr};
public:
ScalableButton* m_reset_to_filament_color = nullptr;
@ -582,9 +583,10 @@ public:
void msw_rescale() override;
bool supports_printer_technology(const PrinterTechnology /* tech */) const override { return true; }
//wxSizer* create_bed_shape_widget(wxWindow* parent);
wxSizer* create_bed_shape_widget(wxWindow* parent);
void cache_extruder_cnt();
bool apply_extruder_cnt_from_cache();
};
class TabSLAMaterial : public Tab

View file

@ -1315,7 +1315,7 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig&
}
case coPoints: {
//BBS: add bed_exclude_area
if (opt_key == "printable_area") {
if (opt_key == "printable_area" || opt_key == "thumbnails") {
ConfigOptionPoints points = *config.option<ConfigOptionPoints>(opt_key);
//BuildVolume build_volume = {points.values, 0.};
return get_thumbnails_string(points.values);

View file

@ -0,0 +1,173 @@
#include "AstroBox.hpp"
#include <algorithm>
#include <sstream>
#include <exception>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <wx/progdlg.h>
#include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "Http.hpp"
namespace fs = boost::filesystem;
namespace pt = boost::property_tree;
namespace Slic3r {
AstroBox::AstroBox(DynamicPrintConfig *config) :
host(config->opt_string("print_host")),
apikey(config->opt_string("printhost_apikey")),
cafile(config->opt_string("printhost_cafile"))
{}
const char* AstroBox::get_name() const { return "AstroBox"; }
bool AstroBox::test(wxString &msg) const
{
// Since the request is performed synchronously here,
// it is ok to refer to `msg` from within the closure
const char *name = get_name();
bool res = true;
auto url = make_url("api/version");
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
auto http = Http::get(std::move(url));
set_auth(http);
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
res = false;
msg = format_error(body, error, status);
})
.on_complete([&, this](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
try {
std::stringstream ss(body);
pt::ptree ptree;
pt::read_json(ss, ptree);
if (! ptree.get_optional<std::string>("api")) {
res = false;
return;
}
const auto text = ptree.get_optional<std::string>("text");
res = validate_version_text(text);
if (! res) {
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "AstroBox")).str());
}
}
catch (const std::exception &) {
res = false;
msg = "Could not parse server response";
}
})
.perform_sync();
return res;
}
wxString AstroBox::get_test_ok_msg () const
{
return _(L("Connection to AstroBox works correctly."));
}
wxString AstroBox::get_test_failed_msg (wxString &msg) const
{
return GUI::from_u8((boost::format("%s: %s\n\n%s")
% _utf8(L("Could not connect to AstroBox"))
% std::string(msg.ToUTF8())
% _utf8(L("Note: AstroBox version at least 1.1.0 is required."))).str());
}
bool AstroBox::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
{
const char *name = get_name();
const auto upload_filename = upload_data.upload_path.filename();
const auto upload_parent_path = upload_data.upload_path.parent_path();
wxString test_msg;
if (! test(test_msg)) {
error_fn(std::move(test_msg));
return false;
}
bool res = true;
auto url = make_url("api/files/local");
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%")
% name
% upload_data.source_path
% url
% upload_filename.string()
% upload_parent_path.string()
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false");
auto http = Http::post(std::move(url));
set_auth(http);
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
.on_complete([&](std::string body, unsigned status) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
})
.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
error_fn(format_error(body, error, status));
res = false;
})
.on_progress([&](Http::Progress progress, bool &cancel) {
prorgess_fn(std::move(progress), cancel);
if (cancel) {
// Upload was canceled
BOOST_LOG_TRIVIAL(info) << "AstroBox: Upload canceled";
res = false;
}
})
.perform_sync();
return res;
}
bool AstroBox::validate_version_text(const boost::optional<std::string> &version_text) const
{
return version_text ? boost::starts_with(*version_text, "AstroBox") : true;
}
void AstroBox::set_auth(Http &http) const
{
http.header("X-Api-Key", apikey);
if (! cafile.empty()) {
http.ca_file(cafile);
}
}
std::string AstroBox::make_url(const std::string &path) const
{
if (host.find("http://") == 0 || host.find("https://") == 0) {
if (host.back() == '/') {
return (boost::format("%1%%2%") % host % path).str();
} else {
return (boost::format("%1%/%2%") % host % path).str();
}
} else {
return (boost::format("http://%1%/%2%") % host % path).str();
}
}
}

View file

@ -0,0 +1,46 @@
#ifndef slic3r_AstroBox_hpp_
#define slic3r_AstroBox_hpp_
#include <string>
#include <wx/string.h>
#include <boost/optional.hpp>
#include "PrintHost.hpp"
namespace Slic3r {
class DynamicPrintConfig;
class Http;
class AstroBox : public PrintHost
{
public:
AstroBox(DynamicPrintConfig *config);
~AstroBox() override = default;
const char* get_name() const override;
bool test(wxString &curl_msg) const override;
wxString get_test_ok_msg () const override;
wxString get_test_failed_msg (wxString &msg) const override;
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
bool has_auto_discovery() const override { return true; }
bool can_test() const override { return true; }
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
std::string get_host() const override { return host; }
protected:
bool validate_version_text(const boost::optional<std::string> &version_text) const;
private:
std::string host;
std::string apikey;
std::string cafile;
void set_auth(Http &http) const;
std::string make_url(const std::string &path) const;
};
}
#endif

286
src/slic3r/Utils/Duet.cpp Normal file
View file

@ -0,0 +1,286 @@
#include "Duet.hpp"
#include <algorithm>
#include <ctime>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <wx/frame.h>
#include <wx/event.h>
#include <wx/progdlg.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/checkbox.h>
#include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
#include "Http.hpp"
namespace fs = boost::filesystem;
namespace pt = boost::property_tree;
namespace Slic3r {
Duet::Duet(DynamicPrintConfig *config) :
host(config->opt_string("print_host")),
password(config->opt_string("printhost_apikey"))
{}
const char* Duet::get_name() const { return "Duet"; }
bool Duet::test(wxString &msg) const
{
auto connectionType = connect(msg);
disconnect(connectionType);
return connectionType != ConnectionType::error;
}
wxString Duet::get_test_ok_msg () const
{
return _(L("Connection to Duet works correctly."));
}
wxString Duet::get_test_failed_msg (wxString &msg) const
{
return GUI::from_u8((boost::format("%s: %s")
% _utf8(L("Could not connect to Duet"))
% std::string(msg.ToUTF8())).str());
}
bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
{
wxString connect_msg;
auto connectionType = connect(connect_msg);
if (connectionType == ConnectionType::error) {
error_fn(std::move(connect_msg));
return false;
}
bool res = true;
bool dsf = (connectionType == ConnectionType::dsf);
auto upload_cmd = get_upload_url(upload_data.upload_path.string(), connectionType);
BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filepath: %2%, post_action: %3%, command: %4%")
% upload_data.source_path
% upload_data.upload_path
% int(upload_data.post_action)
% upload_cmd;
auto http = (dsf ? Http::put(std::move(upload_cmd)) : Http::post(std::move(upload_cmd)));
if (dsf) {
http.set_put_body(upload_data.source_path);
} else {
http.set_post_body(upload_data.source_path);
}
http.on_complete([&](std::string body, unsigned status) {
BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
int err_code = dsf ? (status == 201 ? 0 : 1) : get_err_code_from_body(body);
if (err_code != 0) {
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Request completed but error code was received: %1%") % err_code;
error_fn(format_error(body, L("Unknown error occured"), 0));
res = false;
} else if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) {
wxString errormsg;
res = start_print(errormsg, upload_data.upload_path.string(), connectionType, false);
if (! res) {
error_fn(std::move(errormsg));
}
} else if (upload_data.post_action == PrintHostPostUploadAction::StartSimulation) {
wxString errormsg;
res = start_print(errormsg, upload_data.upload_path.string(), connectionType, true);
if (! res) {
error_fn(std::move(errormsg));
}
}
})
.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
error_fn(format_error(body, error, status));
res = false;
})
.on_progress([&](Http::Progress progress, bool &cancel) {
prorgess_fn(std::move(progress), cancel);
if (cancel) {
// Upload was canceled
BOOST_LOG_TRIVIAL(info) << "Duet: Upload canceled";
res = false;
}
})
.perform_sync();
disconnect(connectionType);
return res;
}
Duet::ConnectionType Duet::connect(wxString &msg) const
{
auto res = ConnectionType::error;
auto url = get_connect_url(false);
auto http = Http::get(std::move(url));
http.on_error([&](std::string body, std::string error, unsigned status) {
auto dsfUrl = get_connect_url(true);
auto dsfHttp = Http::get(std::move(dsfUrl));
dsfHttp.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error connecting: %1%, HTTP %2%, body: `%3%`") % error % status % body;
msg = format_error(body, error, status);
})
.on_complete([&](std::string body, unsigned) {
res = ConnectionType::dsf;
})
.perform_sync();
})
.on_complete([&](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: Got: %1%") % body;
int err_code = get_err_code_from_body(body);
switch (err_code) {
case 0:
res = ConnectionType::rrf;
break;
case 1:
msg = format_error(body, L("Wrong password"), 0);
break;
case 2:
msg = format_error(body, L("Could not get resources to create a new connection"), 0);
break;
default:
msg = format_error(body, L("Unknown error occured"), 0);
break;
}
})
.perform_sync();
return res;
}
void Duet::disconnect(ConnectionType connectionType) const
{
// we don't need to disconnect from DSF or if it failed anyway
if (connectionType != ConnectionType::rrf) {
return;
}
auto url = (boost::format("%1%rr_disconnect")
% get_base_url()).str();
auto http = Http::get(std::move(url));
http.on_error([&](std::string body, std::string error, unsigned status) {
// we don't care about it, if disconnect is not working Duet will disconnect automatically after some time
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error disconnecting: %1%, HTTP %2%, body: `%3%`") % error % status % body;
})
.perform_sync();
}
std::string Duet::get_upload_url(const std::string &filename, ConnectionType connectionType) const
{
assert(connectionType != ConnectionType::error);
if (connectionType == ConnectionType::dsf) {
return (boost::format("%1%machine/file/gcodes/%2%")
% get_base_url()
% Http::url_encode(filename)).str();
} else {
return (boost::format("%1%rr_upload?name=0:/gcodes/%2%&%3%")
% get_base_url()
% Http::url_encode(filename)
% timestamp_str()).str();
}
}
std::string Duet::get_connect_url(const bool dsfUrl) const
{
if (dsfUrl) {
return (boost::format("%1%machine/status")
% get_base_url()).str();
} else {
return (boost::format("%1%rr_connect?password=%2%&%3%")
% get_base_url()
% (password.empty() ? "reprap" : password)
% timestamp_str()).str();
}
}
std::string Duet::get_base_url() const
{
if (host.find("http://") == 0 || host.find("https://") == 0) {
if (host.back() == '/') {
return host;
} else {
return (boost::format("%1%/") % host).str();
}
} else {
return (boost::format("http://%1%/") % host).str();
}
}
std::string Duet::timestamp_str() const
{
enum { BUFFER_SIZE = 32 };
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
char buffer[BUFFER_SIZE];
std::strftime(buffer, BUFFER_SIZE, "time=%Y-%m-%dT%H:%M:%S", &tm);
return std::string(buffer);
}
bool Duet::start_print(wxString &msg, const std::string &filename, ConnectionType connectionType, bool simulationMode) const
{
assert(connectionType != ConnectionType::error);
bool res = false;
bool dsf = (connectionType == ConnectionType::dsf);
auto url = dsf
? (boost::format("%1%machine/code")
% get_base_url()).str()
: (boost::format(simulationMode
? "%1%rr_gcode?gcode=M37%%20P\"0:/gcodes/%2%\""
: "%1%rr_gcode?gcode=M32%%20\"0:/gcodes/%2%\"")
% get_base_url()
% Http::url_encode(filename)).str();
auto http = (dsf ? Http::post(std::move(url)) : Http::get(std::move(url)));
if (dsf) {
http.set_post_body(
(boost::format(simulationMode
? "M37 P\"0:/gcodes/%1%\""
: "M32 \"0:/gcodes/%1%\"")
% filename).str()
);
}
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error starting print: %1%, HTTP %2%, body: `%3%`") % error % status % body;
msg = format_error(body, error, status);
})
.on_complete([&](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: Got: %1%") % body;
res = true;
})
.perform_sync();
return res;
}
int Duet::get_err_code_from_body(const std::string &body) const
{
pt::ptree root;
std::istringstream iss (body); // wrap returned json to istringstream
pt::read_json(iss, root);
return root.get<int>("err", 0);
}
}

48
src/slic3r/Utils/Duet.hpp Normal file
View file

@ -0,0 +1,48 @@
#ifndef slic3r_Duet_hpp_
#define slic3r_Duet_hpp_
#include <string>
#include <wx/string.h>
#include "PrintHost.hpp"
namespace Slic3r {
class DynamicPrintConfig;
class Http;
class Duet : public PrintHost
{
public:
explicit Duet(DynamicPrintConfig *config);
~Duet() override = default;
const char* get_name() const override;
bool test(wxString &curl_msg) const override;
wxString get_test_ok_msg() const override;
wxString get_test_failed_msg(wxString &msg) const override;
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
bool has_auto_discovery() const override { return false; }
bool can_test() const override { return true; }
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint | PrintHostPostUploadAction::StartSimulation; }
std::string get_host() const override { return host; }
private:
enum class ConnectionType { rrf, dsf, error };
std::string host;
std::string password;
std::string get_upload_url(const std::string &filename, ConnectionType connectionType) const;
std::string get_connect_url(const bool dsfUrl) const;
std::string get_base_url() const;
std::string timestamp_str() const;
ConnectionType connect(wxString &msg) const;
void disconnect(ConnectionType connectionType) const;
bool start_print(wxString &msg, const std::string &filename, ConnectionType connectionType, bool simulationMode) const;
int get_err_code_from_body(const std::string &body) const;
};
}
#endif

View file

@ -0,0 +1,229 @@
#include "FlashAir.hpp"
#include <algorithm>
#include <ctime>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <wx/frame.h>
#include <wx/event.h>
#include <wx/progdlg.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/checkbox.h>
#include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
#include "Http.hpp"
namespace fs = boost::filesystem;
namespace pt = boost::property_tree;
namespace Slic3r {
FlashAir::FlashAir(DynamicPrintConfig *config) :
host(config->opt_string("print_host"))
{}
const char* FlashAir::get_name() const { return "FlashAir"; }
bool FlashAir::test(wxString &msg) const
{
// Since the request is performed synchronously here,
// it is ok to refer to `msg` from within the closure
const char *name = get_name();
bool res = false;
auto url = make_url("command.cgi", "op", "118");
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get upload enabled at: %2%") % name % url;
auto http = Http::get(std::move(url));
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting upload enabled: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
res = false;
msg = format_error(body, error, status);
})
.on_complete([&](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got upload enabled: %2%") % name % body;
res = boost::starts_with(body, "1");
if (! res) {
msg = _(L("Upload not enabled on FlashAir card."));
}
})
.perform_sync();
return res;
}
wxString FlashAir::get_test_ok_msg () const
{
return _(L("Connection to FlashAir works correctly and upload is enabled."));
}
wxString FlashAir::get_test_failed_msg (wxString &msg) const
{
return GUI::from_u8((boost::format("%s: %s\n%s")
% _utf8(L("Could not connect to FlashAir"))
% std::string(msg.ToUTF8())
% _utf8(L("Note: FlashAir with firmware 2.00.02 or newer and activated upload function is required."))).str());
}
bool FlashAir::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
{
const char *name = get_name();
const auto upload_filename = upload_data.upload_path.filename();
const auto upload_parent_path = upload_data.upload_path.parent_path();
wxString test_msg;
if (! test(test_msg)) {
error_fn(std::move(test_msg));
return false;
}
bool res = false;
std::string strDest = upload_parent_path.string();
if (strDest.front()!='/') // Needs a leading / else root uploads fail.
{
strDest.insert(0,"/");
}
auto urlPrepare = make_url("upload.cgi", "WRITEPROTECT=ON&FTIME", timestamp_str());
auto urlSetDir = make_url("upload.cgi","UPDIR",strDest);
auto urlUpload = make_url("upload.cgi");
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3% / %4%, filename: %5%")
% name
% upload_data.source_path
% urlPrepare
% urlUpload
% upload_filename.string();
// set filetime for upload and make card writeprotect to prevent filesystem damage
auto httpPrepare = Http::get(std::move(urlPrepare));
httpPrepare.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error preparing upload: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
error_fn(format_error(body, error, status));
res = false;
})
.on_complete([&, this](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got prepare result: %2%") % name % body;
res = boost::icontains(body, "SUCCESS");
if (! res) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Request completed but no SUCCESS message was received.") % name;
error_fn(format_error(body, L("Unknown error occured"), 0));
}
})
.perform_sync();
if(! res ) {
return res;
}
// start file upload
auto httpDir = Http::get(std::move(urlSetDir));
httpDir.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error setting upload dir: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
error_fn(format_error(body, error, status));
res = false;
})
.on_complete([&, this](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got dir select result: %2%") % name % body;
res = boost::icontains(body, "SUCCESS");
if (! res) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Request completed but no SUCCESS message was received.") % name;
error_fn(format_error(body, L("Unknown error occured"), 0));
}
})
.perform_sync();
if(! res ) {
return res;
}
auto http = Http::post(std::move(urlUpload));
http.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
.on_complete([&](std::string body, unsigned status) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
res = boost::icontains(body, "SUCCESS");
if (! res) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Request completed but no SUCCESS message was received.") % name;
error_fn(format_error(body, L("Unknown error occured"), 0));
}
})
.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
error_fn(format_error(body, error, status));
res = false;
})
.on_progress([&](Http::Progress progress, bool &cancel) {
prorgess_fn(std::move(progress), cancel);
if (cancel) {
// Upload was canceled
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Upload canceled") % name;
res = false;
}
})
.perform_sync();
return res;
}
std::string FlashAir::timestamp_str() const
{
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
unsigned long fattime = ((tm.tm_year - 80) << 25) |
((tm.tm_mon + 1) << 21) |
(tm.tm_mday << 16) |
(tm.tm_hour << 11) |
(tm.tm_min << 5) |
(tm.tm_sec >> 1);
return (boost::format("%1$#x") % fattime).str();
}
std::string FlashAir::make_url(const std::string &path) const
{
if (host.find("http://") == 0 || host.find("https://") == 0) {
if (host.back() == '/') {
return (boost::format("%1%%2%") % host % path).str();
} else {
return (boost::format("%1%/%2%") % host % path).str();
}
} else {
if (host.back() == '/') {
return (boost::format("http://%1%%2%") % host % path).str();
} else {
return (boost::format("http://%1%/%2%") % host % path).str();
}
}
}
std::string FlashAir::make_url(const std::string &path, const std::string &arg, const std::string &val) const
{
if (host.find("http://") == 0 || host.find("https://") == 0) {
if (host.back() == '/') {
return (boost::format("%1%%2%?%3%=%4%") % host % path % arg % val).str();
} else {
return (boost::format("%1%/%2%?%3%=%4%") % host % path % arg % val).str();
}
} else {
if (host.back() == '/') {
return (boost::format("http://%1%%2%?%3%=%4%") % host % path % arg % val).str();
} else {
return (boost::format("http://%1%/%2%?%3%=%4%") % host % path % arg % val).str();
}
}
}
}

View file

@ -0,0 +1,42 @@
#ifndef slic3r_FlashAir_hpp_
#define slic3r_FlashAir_hpp_
#include <string>
#include <wx/string.h>
#include "PrintHost.hpp"
namespace Slic3r {
class DynamicPrintConfig;
class Http;
class FlashAir : public PrintHost
{
public:
FlashAir(DynamicPrintConfig *config);
~FlashAir() override = default;
const char* get_name() const override;
bool test(wxString &curl_msg) const override;
wxString get_test_ok_msg() const override;
wxString get_test_failed_msg(wxString &msg) const override;
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
bool has_auto_discovery() const override { return false; }
bool can_test() const override { return true; }
PrintHostPostUploadActions get_post_upload_actions() const override { return {}; }
std::string get_host() const override { return host; }
private:
std::string host;
std::string timestamp_str() const;
std::string make_url(const std::string &path) const;
std::string make_url(const std::string &path, const std::string &arg, const std::string &val) const;
};
}
#endif

150
src/slic3r/Utils/MKS.cpp Normal file
View file

@ -0,0 +1,150 @@
#include "MKS.hpp"
#include <algorithm>
#include <ctime>
#include <chrono>
#include <thread>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/asio.hpp>
#include <boost/algorithm/string.hpp>
#include <wx/frame.h>
#include <wx/event.h>
#include <wx/progdlg.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/checkbox.h>
#include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
#include "Http.hpp"
namespace fs = boost::filesystem;
namespace pt = boost::property_tree;
namespace Slic3r {
MKS::MKS(DynamicPrintConfig* config) :
m_host(config->opt_string("print_host")), m_console_port("8080")
{}
const char* MKS::get_name() const { return "MKS"; }
bool MKS::test(wxString& msg) const
{
Utils::TCPConsole console(m_host, m_console_port);
console.enqueue_cmd("M105");
bool ret = console.run_queue();
if (!ret)
msg = wxString::FromUTF8(console.error_message().c_str());
return ret;
}
wxString MKS::get_test_ok_msg() const
{
return _(L("Connection to MKS works correctly."));
}
wxString MKS::get_test_failed_msg(wxString& msg) const
{
return GUI::from_u8((boost::format("%s: %s")
% _utf8(L("Could not connect to MKS"))
% std::string(msg.ToUTF8())).str());
}
bool MKS::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
{
bool res = true;
auto upload_cmd = get_upload_url(upload_data.upload_path.string());
BOOST_LOG_TRIVIAL(info) << boost::format("MKS: Uploading file %1%, filepath: %2%, print: %3%, command: %4%")
% upload_data.source_path
% upload_data.upload_path
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint)
% upload_cmd;
auto http = Http::post(std::move(upload_cmd));
http.set_post_body(upload_data.source_path);
http.on_complete([&](std::string body, unsigned status) {
BOOST_LOG_TRIVIAL(debug) << boost::format("MKS: File uploaded: HTTP %1%: %2%") % status % body;
int err_code = get_err_code_from_body(body);
if (err_code != 0) {
BOOST_LOG_TRIVIAL(error) << boost::format("MKS: Request completed but error code was received: %1%") % err_code;
error_fn(format_error(body, L("Unknown error occured"), 0));
res = false;
}
else if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) {
wxString errormsg;
res = start_print(errormsg, upload_data.upload_path.string());
if (!res) {
error_fn(std::move(errormsg));
}
}
})
.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("MKS: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
error_fn(format_error(body, error, status));
res = false;
})
.on_progress([&](Http::Progress progress, bool& cancel) {
prorgess_fn(std::move(progress), cancel);
if (cancel) {
// Upload was canceled
BOOST_LOG_TRIVIAL(info) << "MKS: Upload canceled";
res = false;
}
}).perform_sync();
return res;
}
std::string MKS::get_upload_url(const std::string& filename) const
{
return (boost::format("http://%1%/upload?X-Filename=%2%")
% m_host
% Http::url_encode(filename)).str();
}
bool MKS::start_print(wxString& msg, const std::string& filename) const
{
// For some reason printer firmware does not want to respond on gcode commands immediately after file upload.
// So we just introduce artificial delay to workaround it.
// TODO: Inspect reasons
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
Utils::TCPConsole console(m_host, m_console_port);
console.enqueue_cmd(std::string("M23 ") + filename);
console.enqueue_cmd("M24");
bool ret = console.run_queue();
if (!ret)
msg = wxString::FromUTF8(console.error_message().c_str());
return ret;
}
int MKS::get_err_code_from_body(const std::string& body) const
{
pt::ptree root;
std::istringstream iss(body); // wrap returned json to istringstream
pt::read_json(iss, root);
return root.get<int>("err", 0);
}
} // Slic3r

42
src/slic3r/Utils/MKS.hpp Normal file
View file

@ -0,0 +1,42 @@
#ifndef slic3r_MKS_hpp_
#define slic3r_MKS_hpp_
#include <string>
#include <wx/string.h>
#include "PrintHost.hpp"
#include "TCPConsole.hpp"
namespace Slic3r {
class DynamicPrintConfig;
class Http;
class MKS : public PrintHost
{
public:
explicit MKS(DynamicPrintConfig* config);
~MKS() override = default;
const char* get_name() const override;
bool test(wxString& curl_msg) const override;
wxString get_test_ok_msg() const override;
wxString get_test_failed_msg(wxString& msg) const override;
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
bool has_auto_discovery() const override { return false; }
bool can_test() const override { return true; }
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
std::string get_host() const override { return m_host; }
private:
std::string m_host;
std::string m_console_port;
std::string get_upload_url(const std::string& filename) const;
bool start_print(wxString& msg, const std::string& filename) const;
int get_err_code_from_body(const std::string& body) const;
};
}
#endif

View file

@ -14,11 +14,11 @@
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Channel.hpp"
#include "OctoPrint.hpp"
//#include "Duet.hpp"
//#include "FlashAir.hpp"
//#include "AstroBox.hpp"
//#include "Repetier.hpp"
//#include "MKS.hpp"
#include "Duet.hpp"
#include "FlashAir.hpp"
#include "AstroBox.hpp"
#include "Repetier.hpp"
#include "MKS.hpp"
#include "../GUI/PrintHostDialogs.hpp"
namespace fs = boost::filesystem;
@ -47,12 +47,12 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
switch (host_type) {
case htOctoPrint: return new OctoPrint(config);
//case htDuet: return new Duet(config);
//case htFlashAir: return new FlashAir(config);
//case htAstroBox: return new AstroBox(config);
//case htRepetier: return new Repetier(config);
case htDuet: return new Duet(config);
case htFlashAir: return new FlashAir(config);
case htAstroBox: return new AstroBox(config);
case htRepetier: return new Repetier(config);
case htPrusaLink: return new PrusaLink(config);
//case htMKS: return new MKS(config);
case htMKS: return new MKS(config);
default: return nullptr;
}
} else {

View file

@ -0,0 +1,274 @@
#include "Repetier.hpp"
#include <algorithm>
#include <sstream>
#include <exception>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <wx/progdlg.h>
#include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/format.hpp"
#include "Http.hpp"
namespace fs = boost::filesystem;
namespace pt = boost::property_tree;
namespace Slic3r {
Repetier::Repetier(DynamicPrintConfig *config) :
host(config->opt_string("print_host")),
apikey(config->opt_string("printhost_apikey")),
cafile(config->opt_string("printhost_cafile")),
port(config->opt_string("printhost_port"))
{}
const char* Repetier::get_name() const { return "Repetier"; }
bool Repetier::test(wxString &msg) const
{
// Since the request is performed synchronously here,
// it is ok to refer to `msg` from within the closure
const char *name = get_name();
bool res = true;
auto url = make_url("printer/info");
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: List version at: %2%") % name % url;
auto http = Http::get(std::move(url));
set_auth(http);
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
res = false;
msg = format_error(body, error, status);
})
.on_complete([&, this](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
try {
std::stringstream ss(body);
pt::ptree ptree;
pt::read_json(ss, ptree);
const auto text = ptree.get_optional<std::string>("name");
res = validate_version_text(text);
if (! res) {
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "Repetier")).str());
}
}
catch (const std::exception &) {
res = false;
msg = "Could not parse server response";
}
})
.perform_sync();
return res;
}
wxString Repetier::get_test_ok_msg () const
{
return _(L("Connection to Repetier works correctly."));
}
wxString Repetier::get_test_failed_msg (wxString &msg) const
{
return GUI::from_u8((boost::format("%s: %s\n\n%s")
% _utf8(L("Could not connect to Repetier"))
% std::string(msg.ToUTF8())
% _utf8(L("Note: Repetier version at least 0.90.0 is required."))).str());
}
bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
{
const char *name = get_name();
const auto upload_filename = upload_data.upload_path.filename();
const auto upload_parent_path = upload_data.upload_path.parent_path();
wxString test_msg;
if (! test(test_msg)) {
error_fn(std::move(test_msg));
return false;
}
bool res = true;
auto url = upload_data.post_action == PrintHostPostUploadAction::StartPrint
? make_url((boost::format("printer/job/%1%") % port).str())
: make_url((boost::format("printer/model/%1%") % port).str());
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%, group: %7%")
% name
% upload_data.source_path
% url
% upload_filename.string()
% upload_parent_path.string()
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
% upload_data.group;
auto http = Http::post(std::move(url));
set_auth(http);
if (! upload_data.group.empty() && upload_data.group != _utf8(L("Default"))) {
http.form_add("group", upload_data.group);
}
if(upload_data.post_action == PrintHostPostUploadAction::StartPrint) {
http.form_add("name", upload_filename.string());
}
http.form_add("a", "upload")
.form_add_file("filename", upload_data.source_path.string(), upload_filename.string())
.on_complete([&](std::string body, unsigned status) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
})
.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
error_fn(format_error(body, error, status));
res = false;
})
.on_progress([&](Http::Progress progress, bool &cancel) {
prorgess_fn(std::move(progress), cancel);
if (cancel) {
// Upload was canceled
BOOST_LOG_TRIVIAL(info) << "Repetier: Upload canceled";
res = false;
}
})
.perform_sync();
return res;
}
bool Repetier::validate_version_text(const boost::optional<std::string> &version_text) const
{
return version_text ? boost::starts_with(*version_text, "Repetier") : true;
}
void Repetier::set_auth(Http &http) const
{
http.header("X-Api-Key", apikey);
if (! cafile.empty()) {
http.ca_file(cafile);
}
}
std::string Repetier::make_url(const std::string &path) const
{
if (host.find("http://") == 0 || host.find("https://") == 0) {
if (host.back() == '/') {
return (boost::format("%1%%2%") % host % path).str();
} else {
return (boost::format("%1%/%2%") % host % path).str();
}
} else {
return (boost::format("http://%1%/%2%") % host % path).str();
}
}
bool Repetier::get_groups(wxArrayString& groups) const
{
bool res = true;
const char *name = get_name();
auto url = make_url((boost::format("printer/api/%1%") % port).str());
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get groups at: %2%") % name % url;
auto http = Http::get(std::move(url));
set_auth(http);
http.form_add("a", "listModelGroups");
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
})
.on_complete([&](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got groups: %2%") % name % body;
try {
std::stringstream ss(body);
pt::ptree ptree;
pt::read_json(ss, ptree);
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, ptree.get_child("groupNames.")) {
if (v.second.data() == "#") {
groups.push_back(_utf8(L("Default")));
} else {
// Is it safe to assume that the data are utf-8 encoded?
groups.push_back(GUI::from_u8(v.second.data()));
}
}
}
catch (const std::exception &) {
//msg = "Could not parse server response";
res = false;
}
})
.perform_sync();
return res;
}
bool Repetier::get_printers(wxArrayString& printers) const
{
const char *name = get_name();
bool res = true;
auto url = make_url("printer/list");
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: List printers at: %2%") % name % url;
auto http = Http::get(std::move(url));
set_auth(http);
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error listing printers: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
res = false;
})
.on_complete([&](std::string body, unsigned http_status) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got printers: %2%, HTTP status: %3%") % name % body % http_status;
if (http_status != 200)
throw HostNetworkError(GUI::format(_L("HTTP status: %1%\nMessage body: \"%2%\""), http_status, body));
std::stringstream ss(body);
pt::ptree ptree;
try {
pt::read_json(ss, ptree);
} catch (const pt::ptree_error &err) {
throw HostNetworkError(GUI::format(_L("Parsing of host response failed.\nMessage body: \"%1%\"\nError: \"%2%\""), body, err.what()));
}
const auto error = ptree.get_optional<std::string>("error");
if (error)
throw HostNetworkError(*error);
try {
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, ptree.get_child("data.")) {
const auto port = v.second.get<std::string>("slug");
printers.push_back(Slic3r::GUI::from_u8(port));
}
} catch (const std::exception &err) {
throw HostNetworkError(GUI::format(_L("Enumeration of host printers failed.\nMessage body: \"%1%\"\nError: \"%2%\""), body, err.what()));
}
})
.perform_sync();
return res;
}
}

View file

@ -0,0 +1,51 @@
#ifndef slic3r_Repetier_hpp_
#define slic3r_Repetier_hpp_
#include <string>
#include <wx/string.h>
#include <boost/optional.hpp>
#include "PrintHost.hpp"
namespace Slic3r {
class DynamicPrintConfig;
class Http;
class Repetier : public PrintHost
{
public:
Repetier(DynamicPrintConfig *config);
~Repetier() override = default;
const char* get_name() const override;
bool test(wxString &curl_msg) const override;
wxString get_test_ok_msg () const override;
wxString get_test_failed_msg (wxString &msg) const override;
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
bool has_auto_discovery() const override { return false; }
bool can_test() const override { return true; }
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
bool supports_multiple_printers() const override { return true; }
std::string get_host() const override { return host; }
bool get_groups(wxArrayString &groups) const override;
bool get_printers(wxArrayString &printers) const override;
protected:
virtual bool validate_version_text(const boost::optional<std::string> &version_text) const;
private:
std::string host;
std::string apikey;
std::string cafile;
std::string port;
void set_auth(Http &http) const;
std::string make_url(const std::string &path) const;
};
}
#endif

504
src/slic3r/Utils/Serial.cpp Normal file
View file

@ -0,0 +1,504 @@
#include "Serial.hpp"
#include "libslic3r/Exception.hpp"
#include <algorithm>
#include <string>
#include <vector>
#include <chrono>
#include <thread>
#include <fstream>
#include <exception>
#include <stdexcept>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/optional.hpp>
#if _WIN32
#include <Windows.h>
#include <Setupapi.h>
#include <initguid.h>
#include <devguid.h>
#include <regex>
// Undefine min/max macros incompatible with the standard library
// For example, std::numeric_limits<std::streamsize>::max()
// produces some weird errors
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
#include "boost/nowide/convert.hpp"
#pragma comment(lib, "user32.lib")
#elif __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFString.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <IOKit/serial/ioss.h>
#include <sys/syslimits.h>
#endif
#ifndef _WIN32
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/unistd.h>
#include <sys/select.h>
#endif
#if defined(__APPLE__) || defined(__OpenBSD__)
#include <termios.h>
#elif defined __linux__
#include <fcntl.h>
#include <asm-generic/ioctls.h>
#endif
using boost::optional;
namespace Slic3r {
namespace Utils {
static bool looks_like_printer(const std::string &friendly_name)
{
return friendly_name.find("Original Prusa") != std::string::npos;
}
#if _WIN32
void parse_hardware_id(const std::string &hardware_id, SerialPortInfo &spi)
{
unsigned vid, pid;
std::regex pattern("USB\\\\.*VID_([[:xdigit:]]+)&PID_([[:xdigit:]]+).*");
std::smatch matches;
if (std::regex_match(hardware_id, matches, pattern)) {
vid = std::stoul(matches[1].str(), 0, 16);
pid = std::stoul(matches[2].str(), 0, 16);
spi.id_vendor = vid;
spi.id_product = pid;
}
}
#endif
#ifdef __linux__
optional<std::string> sysfs_tty_prop(const std::string &tty_dev, const std::string &name)
{
const auto prop_path = (boost::format("/sys/class/tty/%1%/device/../%2%") % tty_dev % name).str();
std::ifstream file(prop_path);
std::string res;
std::getline(file, res);
if (file.good()) { return res; }
else { return boost::none; }
}
optional<unsigned long> sysfs_tty_prop_hex(const std::string &tty_dev, const std::string &name)
{
auto prop = sysfs_tty_prop(tty_dev, name);
if (!prop) { return boost::none; }
try { return std::stoul(*prop, 0, 16); }
catch (const std::exception&) { return boost::none; }
}
#endif
std::vector<SerialPortInfo> scan_serial_ports_extended()
{
std::vector<SerialPortInfo> output;
#ifdef _WIN32
SP_DEVINFO_DATA devInfoData = { 0 };
devInfoData.cbSize = sizeof(devInfoData);
// Get the tree containing the info for the ports.
HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS, 0, nullptr, DIGCF_PRESENT);
if (hDeviceInfo != INVALID_HANDLE_VALUE) {
// Iterate over all the devices in the tree.
for (int nDevice = 0; SetupDiEnumDeviceInfo(hDeviceInfo, nDevice, &devInfoData); ++ nDevice) {
SerialPortInfo port_info;
// Get the registry key which stores the ports settings.
HKEY hDeviceKey = SetupDiOpenDevRegKey(hDeviceInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
if (hDeviceKey) {
// Read in the name of the port.
wchar_t pszPortName[4096];
DWORD dwSize = sizeof(pszPortName);
DWORD dwType = 0;
if (RegQueryValueEx(hDeviceKey, L"PortName", NULL, &dwType, (LPBYTE)pszPortName, &dwSize) == ERROR_SUCCESS)
port_info.port = boost::nowide::narrow(pszPortName);
RegCloseKey(hDeviceKey);
if (port_info.port.empty())
continue;
}
// Find the size required to hold the device info.
DWORD regDataType;
DWORD reqSize = 0;
SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, nullptr, nullptr, 0, &reqSize);
std::vector<wchar_t> hardware_id(reqSize > 1 ? reqSize : 1);
// Now store it in a buffer.
if (! SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, &regDataType, (BYTE*)hardware_id.data(), reqSize, nullptr))
continue;
parse_hardware_id(boost::nowide::narrow(hardware_id.data()), port_info);
// Find the size required to hold the friendly name.
reqSize = 0;
SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, nullptr, 0, &reqSize);
std::vector<wchar_t> friendly_name;
friendly_name.reserve(reqSize > 1 ? reqSize : 1);
// Now store it in a buffer.
if (! SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, (BYTE*)friendly_name.data(), reqSize, nullptr)) {
port_info.friendly_name = port_info.port;
} else {
port_info.friendly_name = boost::nowide::narrow(friendly_name.data());
port_info.is_printer = looks_like_printer(port_info.friendly_name);
}
output.emplace_back(std::move(port_info));
}
}
#elif __APPLE__
// inspired by https://sigrok.org/wiki/Libserialport
CFMutableDictionaryRef classes = IOServiceMatching(kIOSerialBSDServiceValue);
if (classes != 0) {
io_iterator_t iter;
if (IOServiceGetMatchingServices(kIOMasterPortDefault, classes, &iter) == KERN_SUCCESS) {
io_object_t port;
while ((port = IOIteratorNext(iter)) != 0) {
CFTypeRef cf_property = IORegistryEntryCreateCFProperty(port, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
if (cf_property) {
char path[PATH_MAX];
Boolean result = CFStringGetCString((CFStringRef)cf_property, path, sizeof(path), kCFStringEncodingUTF8);
CFRelease(cf_property);
if (result) {
SerialPortInfo port_info;
port_info.port = path;
// Attempt to read out the device friendly name
if ((cf_property = IORegistryEntrySearchCFProperty(port, kIOServicePlane,
CFSTR("USB Interface Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
(cf_property = IORegistryEntrySearchCFProperty(port, kIOServicePlane,
CFSTR("USB Product Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
(cf_property = IORegistryEntrySearchCFProperty(port, kIOServicePlane,
CFSTR("Product Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
(cf_property = IORegistryEntryCreateCFProperty(port,
CFSTR(kIOTTYDeviceKey), kCFAllocatorDefault, 0))) {
// Description limited to 127 char, anything longer would not be user friendly anyway.
char description[128];
if (CFStringGetCString((CFStringRef)cf_property, description, sizeof(description), kCFStringEncodingUTF8)) {
port_info.friendly_name = std::string(description) + " (" + port_info.port + ")";
port_info.is_printer = looks_like_printer(port_info.friendly_name);
}
CFRelease(cf_property);
}
if (port_info.friendly_name.empty())
port_info.friendly_name = port_info.port;
// Attempt to read out the VID & PID
int vid, pid;
auto cf_vendor = IORegistryEntrySearchCFProperty(port, kIOServicePlane, CFSTR("idVendor"),
kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents);
auto cf_product = IORegistryEntrySearchCFProperty(port, kIOServicePlane, CFSTR("idProduct"),
kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents);
if (cf_vendor && cf_product) {
if (CFNumberGetValue((CFNumberRef)cf_vendor, kCFNumberIntType, &vid) &&
CFNumberGetValue((CFNumberRef)cf_product, kCFNumberIntType, &pid)) {
port_info.id_vendor = vid;
port_info.id_product = pid;
}
}
if (cf_vendor) { CFRelease(cf_vendor); }
if (cf_product) { CFRelease(cf_product); }
output.emplace_back(std::move(port_info));
}
}
IOObjectRelease(port);
}
}
}
#else
// UNIX / Linux
std::initializer_list<const char*> prefixes { "ttyUSB" , "ttyACM", "tty.", "cu.", "rfcomm" };
for (auto &dir_entry : boost::filesystem::directory_iterator(boost::filesystem::path("/dev"))) {
std::string name = dir_entry.path().filename().string();
for (const char *prefix : prefixes) {
if (boost::starts_with(name, prefix)) {
const auto path = dir_entry.path().string();
SerialPortInfo spi;
spi.port = path;
#ifdef __linux__
auto friendly_name = sysfs_tty_prop(name, "product");
if (friendly_name) {
spi.is_printer = looks_like_printer(*friendly_name);
spi.friendly_name = (boost::format("%1% (%2%)") % *friendly_name % path).str();
} else {
spi.friendly_name = path;
}
auto vid = sysfs_tty_prop_hex(name, "idVendor");
auto pid = sysfs_tty_prop_hex(name, "idProduct");
if (vid && pid) {
spi.id_vendor = *vid;
spi.id_product = *pid;
}
#else
spi.friendly_name = path;
#endif
output.emplace_back(std::move(spi));
break;
}
}
}
#endif
output.erase(std::remove_if(output.begin(), output.end(),
[](const SerialPortInfo &info) {
return boost::starts_with(info.port, "Bluetooth") || boost::starts_with(info.port, "FireFly");
}),
output.end());
return output;
}
std::vector<std::string> scan_serial_ports()
{
std::vector<SerialPortInfo> ports = scan_serial_ports_extended();
std::vector<std::string> output;
output.reserve(ports.size());
for (const SerialPortInfo &spi : ports)
output.emplace_back(std::move(spi.port));
return output;
}
// Class Serial
namespace asio = boost::asio;
using boost::system::error_code;
Serial::Serial(asio::io_service& io_service) :
asio::serial_port(io_service)
{}
Serial::Serial(asio::io_service& io_service, const std::string &name, unsigned baud_rate) :
asio::serial_port(io_service, name)
{
set_baud_rate(baud_rate);
}
Serial::~Serial() {}
void Serial::set_baud_rate(unsigned baud_rate)
{
try {
// This does not support speeds > 115200
set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
} catch (boost::system::system_error &) {
auto handle = native_handle();
auto handle_errno = [](int retval) {
if (retval != 0) {
throw Slic3r::RuntimeError(
(boost::format("Could not set baud rate: %1%") % strerror(errno)).str()
);
}
};
#if __APPLE__
termios ios;
handle_errno(::tcgetattr(handle, &ios));
handle_errno(::cfsetspeed(&ios, baud_rate));
speed_t newSpeed = baud_rate;
handle_errno(::ioctl(handle, IOSSIOSPEED, &newSpeed));
handle_errno(::tcsetattr(handle, TCSANOW, &ios));
#elif __linux__
/* The following definitions are kindly borrowed from:
/usr/include/asm-generic/termbits.h
Unfortunately we cannot just include that one because
it would redefine the "struct termios" already defined
the <termios.h> already included by Boost.ASIO. */
#define K_NCCS 19
struct termios2 {
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_line;
cc_t c_cc[K_NCCS];
speed_t c_ispeed;
speed_t c_ospeed;
};
#define BOTHER CBAUDEX
termios2 ios;
handle_errno(::ioctl(handle, TCGETS2, &ios));
ios.c_ispeed = ios.c_ospeed = baud_rate;
ios.c_cflag &= ~CBAUD;
ios.c_cflag |= BOTHER | CLOCAL | CREAD;
ios.c_cc[VMIN] = 1; // Minimum of characters to read, prevents eof errors when 0 bytes are read
ios.c_cc[VTIME] = 1;
handle_errno(::ioctl(handle, TCSETS2, &ios));
#elif __OpenBSD__
struct termios ios;
handle_errno(::tcgetattr(handle, &ios));
handle_errno(::cfsetspeed(&ios, baud_rate));
handle_errno(::tcsetattr(handle, TCSAFLUSH, &ios));
#else
throw Slic3r::RuntimeError("Custom baud rates are not currently supported on this OS");
#endif
}
}
/*
void Serial::set_DTR(bool on)
{
auto handle = native_handle();
#if defined(_WIN32) && !defined(__SYMBIAN32__)
if (! EscapeCommFunction(handle, on ? SETDTR : CLRDTR)) {
throw Slic3r::RuntimeError("Could not set serial port DTR");
}
#else
int status;
if (::ioctl(handle, TIOCMGET, &status) == 0) {
on ? status |= TIOCM_DTR : status &= ~TIOCM_DTR;
if (::ioctl(handle, TIOCMSET, &status) == 0) {
return;
}
}
throw Slic3r::RuntimeError(
(boost::format("Could not set serial port DTR: %1%") % strerror(errno)).str()
);
#endif
}
void Serial::reset_line_num()
{
// See https://github.com/MarlinFirmware/Marlin/wiki/M110
write_string("M110 N0\n");
m_line_num = 0;
}
bool Serial::read_line(unsigned timeout, std::string &line, error_code &ec)
{
auto& io_service =
#if BOOST_VERSION >= 107000
//FIXME this is most certainly wrong!
(boost::asio::io_context&)this->get_executor().context();
#else
this->get_io_service();
#endif
asio::deadline_timer timer(io_service);
char c = 0;
bool fail = false;
while (true) {
io_service.reset();
asio::async_read(*this, boost::asio::buffer(&c, 1), [&](const error_code &read_ec, size_t size) {
if (ec || size == 0) {
fail = true;
ec = read_ec; // FIXME: only if operation not aborted
}
timer.cancel(); // FIXME: ditto
});
if (timeout > 0) {
timer.expires_from_now(boost::posix_time::milliseconds(timeout));
timer.async_wait([&](const error_code &ec) {
// Ignore timer aborts
if (!ec) {
fail = true;
this->cancel();
}
});
}
io_service.run();
if (fail) {
return false;
} else if (c != '\n') {
line += c;
} else {
return true;
}
}
}
void Serial::printer_setup()
{
printer_reset();
write_string("\r\r\r\r\r\r\r\r\r\r"); // Gets rid of line noise, if any
}
size_t Serial::write_string(const std::string &str)
{
// TODO: might be wise to timeout here as well
return asio::write(*this, asio::buffer(str));
}
bool Serial::printer_ready_wait(unsigned retries, unsigned timeout)
{
std::string line;
error_code ec;
for (; retries > 0; retries--) {
reset_line_num();
while (read_line(timeout, line, ec)) {
if (line == "ok") {
return true;
}
line.clear();
}
line.clear();
}
return false;
}
size_t Serial::printer_write_line(const std::string &line, unsigned line_num)
{
const auto formatted_line = Utils::Serial::printer_format_line(line, line_num);
return write_string(formatted_line);
}
size_t Serial::printer_write_line(const std::string &line)
{
m_line_num++;
return printer_write_line(line, m_line_num);
}
void Serial::printer_reset()
{
this->set_DTR(false);
std::this_thread::sleep_for(std::chrono::milliseconds(200));
this->set_DTR(true);
std::this_thread::sleep_for(std::chrono::milliseconds(200));
this->set_DTR(false);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
std::string Serial::printer_format_line(const std::string &line, unsigned line_num)
{
const auto line_num_str = std::to_string(line_num);
unsigned checksum = 'N';
for (auto c : line_num_str) { checksum ^= c; }
checksum ^= ' ';
for (auto c : line) { checksum ^= c; }
return (boost::format("N%1% %2%*%3%\n") % line_num_str % line % checksum).str();
}
*/
} // namespace Utils
} // namespace Slic3r

View file

@ -0,0 +1,97 @@
#ifndef slic3r_GUI_Utils_Serial_hpp_
#define slic3r_GUI_Utils_Serial_hpp_
#include <vector>
#include <string>
#include <boost/system/error_code.hpp>
#include <boost/asio.hpp>
namespace Slic3r {
namespace Utils {
struct SerialPortInfo {
std::string port;
unsigned id_vendor = -1;
unsigned id_product = -1;
std::string friendly_name;
bool is_printer = false;
SerialPortInfo() {}
SerialPortInfo(std::string port) : port(port), friendly_name(std::move(port)) {}
bool id_match(unsigned id_vendor, unsigned id_product) const { return id_vendor == this->id_vendor && id_product == this->id_product; }
};
inline bool operator==(const SerialPortInfo &sp1, const SerialPortInfo &sp2)
{
return
sp1.port == sp2.port &&
sp1.id_vendor == sp2.id_vendor &&
sp1.id_product == sp2.id_product &&
sp1.is_printer == sp2.is_printer;
}
extern std::vector<std::string> scan_serial_ports();
extern std::vector<SerialPortInfo> scan_serial_ports_extended();
class Serial : public boost::asio::serial_port
{
public:
Serial(boost::asio::io_service &io_service);
Serial(boost::asio::io_service &io_service, const std::string &name, unsigned baud_rate);
Serial(const Serial &) = delete;
Serial &operator=(const Serial &) = delete;
~Serial();
void set_baud_rate(unsigned baud_rate);
// The Serial implementation is currently in disarray and therefore commented out.
// The boost implementation seems to have several problems, such as lack of support
// for custom baud rates, few weird implementation bugs and a history of API breakages.
// It's questionable whether it solves more problems than causes. Probably not.
// TODO: Custom implementation not based on asio.
//
// As of now, this class is only kept for the purpose of rebooting AVR109,
// see FirmwareDialog::priv::avr109_reboot()
/*
void set_DTR(bool on);
// Resets the line number both internally as well as with the firmware using M110
void reset_line_num();
// Reads a line or times out, the timeout is in milliseconds
bool read_line(unsigned timeout, std::string &line, boost::system::error_code &ec);
// Perform an initial setup for communicating with a printer
void printer_setup();
// Write data from a string
size_t write_string(const std::string &str);
// Attempts to reset the line numer and waits until the printer says "ok"
bool printer_ready_wait(unsigned retries, unsigned timeout);
// Write Marlin-formatted line, with a line number and a checksum
size_t printer_write_line(const std::string &line, unsigned line_num);
// Same as above, but with internally-managed line number
size_t printer_write_line(const std::string &line);
// Toggles DTR to reset the printer
void printer_reset();
// Formats a line Marlin-style, ie. with a sequential number and a checksum
static std::string printer_format_line(const std::string &line, unsigned line_num);
private:
unsigned m_line_num = 0;
*/
};
} // Utils
} // Slic3r
#endif /* slic3r_GUI_Utils_Serial_hpp_ */