2024-12-20 06:44:50 +00:00
# include "BackgroundSlicingProcess.hpp"
# include "GUI_App.hpp"
# include "GUI.hpp"
# include "MainFrame.hpp"
# include "format.hpp"
# include <wx/app.h>
# include <wx/panel.h>
# include <wx/stdpaths.h>
// For zipped archive creation
# include <wx/stdstream.h>
# include <wx/wfstream.h>
# include <wx/zipstrm.h>
# include <miniz.h>
// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx.
# include "libslic3r/Print.hpp"
# include "libslic3r/SLAPrint.hpp"
# include "libslic3r/Utils.hpp"
# include "libslic3r/GCode/PostProcessor.hpp"
# include "libslic3r/Format/SL1.hpp"
# include "libslic3r/Thread.hpp"
# include "libslic3r/libslic3r.h"
# include <cassert>
# include <stdexcept>
# include <cctype>
# include <boost/format/format_fwd.hpp>
# include <boost/filesystem/operations.hpp>
# include <boost/log/trivial.hpp>
# include <boost/nowide/cstdio.hpp>
# include "I18N.hpp"
//#include "RemovableDriveManager.hpp"
# include "slic3r/GUI/Plater.hpp"
namespace Slic3r {
bool SlicingProcessCompletedEvent : : critical_error ( ) const
{
try {
this - > rethrow_exception ( ) ;
} catch ( const Slic3r : : SlicingError & ) {
// Exception derived from SlicingError is non-critical.
return false ;
} catch ( const Slic3r : : SlicingErrors & ) {
return false ;
} catch ( . . . ) { }
return true ;
}
bool SlicingProcessCompletedEvent : : invalidate_plater ( ) const
{
if ( critical_error ( ) )
{
try {
this - > rethrow_exception ( ) ;
}
catch ( const Slic3r : : ExportError & ) {
// Exception thrown by copying file does not ivalidate plater
return false ;
}
catch ( . . . ) {
}
return true ;
}
return false ;
}
std : : pair < std : : string , std : : vector < size_t > > SlicingProcessCompletedEvent : : format_error_message ( ) const
{
std : : string error ;
size_t monospace = 0 ;
try {
this - > rethrow_exception ( ) ;
} catch ( const std : : bad_alloc & ex ) {
wxString errmsg = GUI : : from_u8 ( boost : : format ( _utf8 ( L ( " An error occurred. Maybe memory of system is not enough or it's a bug "
" of the program " ) ) ) . str ( ) ) ;
error = std : : string ( errmsg . ToUTF8 ( ) ) + " \n " + std : : string ( ex . what ( ) ) ;
} catch ( const HardCrash & ex ) {
error = GUI : : format ( " A fatal error occurred: \" %1% \" " , ex . what ( ) ) + " \n " +
_u8L ( " Please save project and restart the program. " ) ;
} catch ( PlaceholderParserError & ex ) {
error = ex . what ( ) ;
monospace = 1 ;
} catch ( SlicingError & ex ) {
error = ex . what ( ) ;
monospace = ex . objectId ( ) ;
} catch ( SlicingErrors & exs ) {
std : : vector < size_t > ids ;
for ( auto & ex : exs . errors_ ) {
error = ex . what ( ) ;
monospace = ex . objectId ( ) ;
ids . push_back ( monospace ) ;
}
return std : : make_pair ( std : : move ( error ) , ids ) ;
} catch ( std : : exception & ex ) {
error = ex . what ( ) ;
} catch ( . . . ) {
error = " Unknown C++ exception. " ;
}
return std : : make_pair ( std : : move ( error ) , std : : vector < size_t > { monospace } ) ;
}
BackgroundSlicingProcess : : BackgroundSlicingProcess ( )
{
//BBS: move this logic to part plate
#if 0
boost : : filesystem : : path temp_path ( wxStandardPaths : : Get ( ) . GetTempDir ( ) . utf8_str ( ) . data ( ) ) ;
temp_path / = ( boost : : format ( " .%1%.gcode " ) % get_current_pid ( ) ) . str ( ) ;
m_temp_output_path = temp_path . string ( ) ;
# endif
}
BackgroundSlicingProcess : : ~ BackgroundSlicingProcess ( )
{
this - > stop ( ) ;
this - > join_background_thread ( ) ;
//BBS: move this logic to part plate
//boost::nowide::remove(m_temp_output_path.c_str());
}
//BBS: switch the print in background slicing process
bool BackgroundSlicingProcess : : switch_print_preprocess ( )
{
bool result = true ;
/*switch (m_printer_tech) {
case ptFFF : m_print = m_fff_print ; break ;
case ptSLA : m_print = m_sla_print ; break ;
default : assert ( false ) ; break ;
} */
return result ;
}
//BBS: judge whether can switch the print
bool BackgroundSlicingProcess : : can_switch_print ( )
{
bool result = true ;
if ( m_state = = STATE_RUNNING )
{
//currently it is on slicing, judge whether the slice result is valid or not
//if (m_current_plate->is_slice_result_valid())
{
result = false ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : slicing plate's plate_id %1%, on slicing, can not switch print " ) % m_current_plate - > get_index ( ) ;
}
}
return result ;
}
//BBS: select the printer technology
bool BackgroundSlicingProcess : : select_technology ( PrinterTechnology tech )
{
bool changed = false ;
if ( m_printer_tech ! = tech ) {
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : change the printer technology from %1% to %2% " ) % m_printer_tech % tech ;
m_printer_tech = tech ;
if ( m_print ! = nullptr )
this - > reset ( ) ;
changed = true ;
}
switch ( tech ) {
case ptFFF : m_print = m_fff_print ; break ;
case ptSLA : m_print = m_sla_print ; break ;
default : assert ( false ) ; break ;
}
assert ( m_print ! = nullptr ) ;
return changed ;
}
PrinterTechnology BackgroundSlicingProcess : : current_printer_technology ( ) const
{
//BBS: as the m_printer is changed frequently when switch plates, use m_printer_tech directly
return m_printer_tech ;
//return m_print->technology();
}
std : : string BackgroundSlicingProcess : : output_filepath_for_project ( const boost : : filesystem : : path & project_path )
{
assert ( m_print ! = nullptr ) ;
if ( project_path . empty ( ) )
return m_print - > output_filepath ( " " ) ;
return m_print - > output_filepath ( project_path . parent_path ( ) . string ( ) , project_path . stem ( ) . string ( ) ) ;
}
// This function may one day be merged into the Print, but historically the print was separated
// from the G-code generator.
2025-06-03 01:08:41 +00:00
//此功能可能有一天会合并到打印中, 但历史上打印与G代码生成器是分开的。
2024-12-20 06:44:50 +00:00
void BackgroundSlicingProcess : : process_fff ( )
{
assert ( m_print = = m_fff_print ) ;
PresetBundle & preset_bundle = * wxGetApp ( ) . preset_bundle ;
m_fff_print - > set_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
2025-06-03 01:08:41 +00:00
//BBS: 从已有的gcode文件中添加逻辑以进行处理
2024-12-20 06:44:50 +00:00
if ( m_print - > finished ( ) ) {
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " %1%: skip slicing, to process previous gcode file " ) % __LINE__ ;
m_fff_print - > set_status ( 80 , _utf8 ( L ( " Processing G-Code from Previous file... " ) ) ) ;
wxCommandEvent evt ( m_event_slicing_completed_id ) ;
// Post the Slicing Finished message for the G-code viewer to update.
// Passing the timestamp
2025-06-03 01:08:41 +00:00
//发布“切片完成”消息, 以便G代码查看器进行更新。
//传递时间戳
2024-12-20 06:44:50 +00:00
evt . SetInt ( ( int ) ( m_fff_print - > step_state_with_timestamp ( PrintStep : : psSlicingFinished ) . timestamp ) ) ;
wxQueueEvent ( GUI : : wxGetApp ( ) . mainframe - > m_plater , evt . Clone ( ) ) ;
m_temp_output_path = this - > get_current_plate ( ) - > get_tmp_gcode_path ( ) ;
if ( ! m_export_path . empty ( ) ) {
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " %1%: export gcode from %2% directly to %3% " ) % __LINE__ % m_temp_output_path % m_export_path ;
}
else {
m_fff_print - > export_gcode_from_previous_file ( m_temp_output_path , m_gcode_result , [ this ] ( const ThumbnailsParams & params ) { return this - > render_thumbnails ( params ) ; } ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " %1%: export_gcode_from_previous_file from %2% finished " ) % __LINE__ % m_temp_output_path ;
}
}
else {
//BBS: reset the gcode before reload_print in slicing_completed event processing
//FIX the gcode rename failed issue
2025-06-03 01:08:41 +00:00
//BBS:在slicing_colleged事件处理中reload_print之前重置gcode
//修复gcode重命名失败的问题
2024-12-20 06:44:50 +00:00
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " %1%: will start slicing, reset gcode_result %2% firstly " ) % __LINE__ % m_gcode_result ;
m_gcode_result - > reset ( ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " %1%: gcode_result reseted, will start print::process " ) % __LINE__ ;
m_print - > process ( ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " %1%: after print::process, send slicing complete event to gui... " ) % __LINE__ ;
wxCommandEvent evt ( m_event_slicing_completed_id ) ;
// Post the Slicing Finished message for the G-code viewer to update.
// Passing the timestamp
2025-06-03 01:08:41 +00:00
//发布“切片完成”消息, 以便G代码查看器进行更新。
//传递时间戳
2024-12-20 06:44:50 +00:00
evt . SetInt ( ( int ) ( m_fff_print - > step_state_with_timestamp ( PrintStep : : psSlicingFinished ) . timestamp ) ) ;
wxQueueEvent ( GUI : : wxGetApp ( ) . mainframe - > m_plater , evt . Clone ( ) ) ;
//BBS: add plate index into render params
2025-06-03 01:08:41 +00:00
//BBS: 在渲染参数中添加板块索引
2024-12-20 06:44:50 +00:00
m_temp_output_path = this - > get_current_plate ( ) - > get_tmp_gcode_path ( ) ;
m_fff_print - > export_gcode ( m_temp_output_path , m_gcode_result , [ this ] ( const ThumbnailsParams & params ) { return this - > render_thumbnails ( params ) ; } ) ;
finalize_gcode ( ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : export gcode finished " ) ;
}
if ( this - > set_step_started ( bspsGCodeFinalize ) ) {
if ( ! m_export_path . empty ( ) ) {
wxQueueEvent ( GUI : : wxGetApp ( ) . mainframe - > m_plater , new wxCommandEvent ( m_event_export_began_id ) ) ;
export_gcode ( ) ;
} else if ( ! m_upload_job . empty ( ) ) {
wxQueueEvent ( GUI : : wxGetApp ( ) . mainframe - > m_plater , new wxCommandEvent ( m_event_export_began_id ) ) ;
prepare_upload ( ) ;
} else {
m_print - > set_status ( 100 , _utf8 ( L ( " Slicing complete " ) ) ) ;
}
this - > set_step_done ( bspsGCodeFinalize ) ;
}
}
static void write_thumbnail ( Zipper & zipper , const ThumbnailData & data )
{
size_t png_size = 0 ;
void * png_data = tdefl_write_image_to_png_file_in_memory_ex ( ( const void * ) data . pixels . data ( ) , data . width , data . height , 4 , & png_size , MZ_DEFAULT_LEVEL , 1 ) ;
if ( png_data ! = nullptr )
{
zipper . add_entry ( " thumbnail/thumbnail " + std : : to_string ( data . width ) + " x " + std : : to_string ( data . height ) + " .png " , ( const std : : uint8_t * ) png_data , png_size ) ;
mz_free ( png_data ) ;
}
}
void BackgroundSlicingProcess : : process_sla ( )
{
assert ( m_print = = m_sla_print ) ;
m_print - > process ( ) ;
if ( this - > set_step_started ( bspsGCodeFinalize ) ) {
if ( ! m_export_path . empty ( ) ) {
wxQueueEvent ( GUI : : wxGetApp ( ) . mainframe - > m_plater , new wxCommandEvent ( m_event_export_began_id ) ) ;
const std : : string export_path = m_sla_print - > print_statistics ( ) . finalize_output_path ( m_export_path ) ;
//BBS: add plate id for thumbnail generation
ThumbnailsList thumbnails = this - > render_thumbnails (
ThumbnailsParams { current_print ( ) - > full_print_config ( ) . option < ConfigOptionPoints > ( " thumbnail_size " ) - > values , true , true , true , true , 0 } ) ;
Zipper zipper ( export_path ) ;
m_sla_archive . export_print ( zipper , * m_sla_print ) ; // true, false, true, true); // renders also supports and pad
for ( const ThumbnailData & data : thumbnails )
if ( data . is_valid ( ) )
write_thumbnail ( zipper , data ) ;
zipper . finalize ( ) ;
//m_print->set_status(100, (boost::format(_utf8(L("Masked SLA file exported to %1%"))) % export_path).str());
m_print - > set_status ( 100 , ( boost : : format ( _utf8 ( " Masked SLA file exported to %1% " ) ) % export_path ) . str ( ) ) ;
} else {
//m_print->set_status(100, _utf8(L("Slicing complete")));
m_print - > set_status ( 100 , _utf8 ( " Slicing complete " ) ) ;
}
this - > set_step_done ( bspsGCodeFinalize ) ;
}
}
void BackgroundSlicingProcess : : thread_proc ( )
{
//BBS: thread name
set_current_thread_name ( " bbl_BgSlcPcs " ) ;
name_tbb_thread_pool_threads_set_locale ( ) ;
assert ( m_print ! = nullptr ) ;
assert ( m_print = = m_fff_print | | m_print = = m_sla_print ) ;
std : : unique_lock < std : : mutex > lck ( m_mutex ) ;
// Let the caller know we are ready to run the background processing task.
m_state = STATE_IDLE ;
lck . unlock ( ) ;
m_condition . notify_one ( ) ;
for ( ; ; ) {
//BBS: sometimes the state has already been set in the start function
//assert(m_state == STATE_IDLE || m_state == STATE_CANCELED || m_state == STATE_FINISHED || m_state == STATE_STARTED);
// Wait until a new task is ready to be executed, or this thread should be finished.
lck . lock ( ) ;
m_condition . wait ( lck , [ this ] ( ) { return m_state = = STATE_STARTED | | m_state = = STATE_EXIT ; } ) ;
if ( m_state = = STATE_EXIT )
// Exiting this thread.
break ;
// Process the background slicing task.
m_state = STATE_RUNNING ;
//BBS: internal cancel
m_internal_cancelled = false ;
lck . unlock ( ) ;
std : : exception_ptr exception ;
# ifdef _WIN32
this - > call_process_seh_throw ( exception ) ;
# else
this - > call_process ( exception ) ;
# endif
m_print - > finalize ( ) ;
lck . lock ( ) ;
m_state = m_print - > canceled ( ) ? STATE_CANCELED : STATE_FINISHED ;
BOOST_LOG_TRIVIAL ( debug ) < < __FUNCTION__ < < boost : : format ( " : process finished, state %1%, print cancel_status %2% " ) % m_state % m_print - > cancel_status ( ) ;
if ( m_print - > cancel_status ( ) ! = Print : : CANCELED_INTERNAL ) {
// Only post the canceled event, if canceled by user.
// Don't post the canceled event, if canceled from Print::apply().
SlicingProcessCompletedEvent evt ( m_event_finished_id , 0 ,
( m_state = = STATE_CANCELED ) ? SlicingProcessCompletedEvent : : Cancelled :
exception ? SlicingProcessCompletedEvent : : Error : SlicingProcessCompletedEvent : : Finished , exception ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : send SlicingProcessCompletedEvent to main, status %1% " ) % evt . status ( ) ;
wxQueueEvent ( GUI : : wxGetApp ( ) . mainframe - > m_plater , evt . Clone ( ) ) ;
}
else {
//BBS: internal cancel
m_internal_cancelled = true ;
}
m_print - > restart ( ) ;
lck . unlock ( ) ;
// Let the UI thread wake up if it is waiting for the background task to finish.
m_condition . notify_one ( ) ;
// Let the UI thread see the result.
}
m_state = STATE_EXITED ;
lck . unlock ( ) ;
// End of the background processing thread. The UI thread should join m_thread now.
}
# ifdef _WIN32
// Only these SEH exceptions will be catched and turned into Slic3r::HardCrash C++ exceptions.
static bool is_win32_seh_harware_exception ( unsigned long ex ) throw ( ) {
return
ex = = STATUS_ACCESS_VIOLATION | |
ex = = STATUS_DATATYPE_MISALIGNMENT | |
ex = = STATUS_FLOAT_DIVIDE_BY_ZERO | |
ex = = STATUS_FLOAT_OVERFLOW | |
ex = = STATUS_FLOAT_UNDERFLOW | |
# ifdef STATUS_FLOATING_RESEVERED_OPERAND
ex = = STATUS_FLOATING_RESEVERED_OPERAND | |
# endif // STATUS_FLOATING_RESEVERED_OPERAND
ex = = STATUS_ILLEGAL_INSTRUCTION | |
ex = = STATUS_PRIVILEGED_INSTRUCTION | |
ex = = STATUS_INTEGER_DIVIDE_BY_ZERO | |
ex = = STATUS_INTEGER_OVERFLOW | |
ex = = STATUS_STACK_OVERFLOW ;
}
// Rethrow some SEH exceptions as Slic3r::HardCrash C++ exceptions.
static void rethrow_seh_exception ( unsigned long win32_seh_catched )
{
if ( win32_seh_catched ) {
// Rethrow SEH exception as Slicer::HardCrash.
if ( win32_seh_catched = = STATUS_ACCESS_VIOLATION | | win32_seh_catched = = STATUS_DATATYPE_MISALIGNMENT )
throw Slic3r : : HardCrash ( _u8L ( " Access violation " ) ) ;
if ( win32_seh_catched = = STATUS_ILLEGAL_INSTRUCTION | | win32_seh_catched = = STATUS_PRIVILEGED_INSTRUCTION )
throw Slic3r : : HardCrash ( _u8L ( " Illegal instruction " ) ) ;
if ( win32_seh_catched = = STATUS_FLOAT_DIVIDE_BY_ZERO | | win32_seh_catched = = STATUS_INTEGER_DIVIDE_BY_ZERO )
throw Slic3r : : HardCrash ( _u8L ( " Divide by zero " ) ) ;
if ( win32_seh_catched = = STATUS_FLOAT_OVERFLOW | | win32_seh_catched = = STATUS_INTEGER_OVERFLOW )
throw Slic3r : : HardCrash ( _u8L ( " Overflow " ) ) ;
if ( win32_seh_catched = = STATUS_FLOAT_UNDERFLOW )
throw Slic3r : : HardCrash ( _u8L ( " Underflow " ) ) ;
# ifdef STATUS_FLOATING_RESEVERED_OPERAND
if ( win32_seh_catched = = STATUS_FLOATING_RESEVERED_OPERAND )
throw Slic3r : : HardCrash ( _u8L ( " Floating reserved operand " ) ) ;
# endif // STATUS_FLOATING_RESEVERED_OPERAND
if ( win32_seh_catched = = STATUS_STACK_OVERFLOW )
throw Slic3r : : HardCrash ( _u8L ( " Stack overflow " ) ) ;
}
}
// Wrapper for Win32 structured exceptions. Win32 structured exception blocks and C++ exception blocks cannot be mixed in the same function.
unsigned long BackgroundSlicingProcess : : call_process_seh ( std : : exception_ptr & ex ) throw ( )
{
unsigned long win32_seh_catched = 0 ;
__try {
this - > call_process ( ex ) ;
} __except ( is_win32_seh_harware_exception ( GetExceptionCode ( ) ) ) {
win32_seh_catched = GetExceptionCode ( ) ;
}
return win32_seh_catched ;
}
void BackgroundSlicingProcess : : call_process_seh_throw ( std : : exception_ptr & ex ) throw ( )
{
unsigned long win32_seh_catched = this - > call_process_seh ( ex ) ;
if ( win32_seh_catched ) {
// Rethrow SEH exception as Slicer::HardCrash.
try {
rethrow_seh_exception ( win32_seh_catched ) ;
} catch ( . . . ) {
ex = std : : current_exception ( ) ;
}
}
}
# endif // _WIN32
void BackgroundSlicingProcess : : call_process ( std : : exception_ptr & ex ) throw ( )
{
try {
assert ( m_print ! = nullptr ) ;
switch ( m_print - > technology ( ) ) {
case ptFFF : this - > process_fff ( ) ; break ;
case ptSLA : this - > process_sla ( ) ; break ;
default : m_print - > process ( ) ; break ;
}
} catch ( CanceledException & /* ex */ ) {
// Canceled, this is all right.
assert ( m_print - > canceled ( ) ) ;
ex = std : : current_exception ( ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " :got cancelled exception " < < std : : endl ;
} catch ( . . . ) {
ex = std : : current_exception ( ) ;
BOOST_LOG_TRIVIAL ( error ) < < __FUNCTION__ < < " :got other exception " < < std : : endl ;
}
}
# ifdef _WIN32
unsigned long BackgroundSlicingProcess : : thread_proc_safe_seh ( ) throw ( )
{
unsigned long win32_seh_catched = 0 ;
__try {
this - > thread_proc_safe ( ) ;
} __except ( is_win32_seh_harware_exception ( GetExceptionCode ( ) ) ) {
win32_seh_catched = GetExceptionCode ( ) ;
}
return win32_seh_catched ;
}
void BackgroundSlicingProcess : : thread_proc_safe_seh_throw ( ) throw ( )
{
unsigned long win32_seh_catched = this - > thread_proc_safe_seh ( ) ;
if ( win32_seh_catched ) {
// Rethrow SEH exception as Slicer::HardCrash.
try {
rethrow_seh_exception ( win32_seh_catched ) ;
} catch ( . . . ) {
wxTheApp - > OnUnhandledException ( ) ;
}
}
}
# endif // _WIN32
void BackgroundSlicingProcess : : thread_proc_safe ( ) throw ( )
{
try {
this - > thread_proc ( ) ;
} catch ( . . . ) {
wxTheApp - > OnUnhandledException ( ) ;
}
}
void BackgroundSlicingProcess : : join_background_thread ( )
{
std : : unique_lock < std : : mutex > lck ( m_mutex ) ;
if ( m_state = = STATE_INITIAL ) {
// Worker thread has not been started yet.
assert ( ! m_thread . joinable ( ) ) ;
} else {
assert ( m_state = = STATE_IDLE ) ;
assert ( m_thread . joinable ( ) ) ;
// Notify the worker thread to exit.
m_state = STATE_EXIT ;
lck . unlock ( ) ;
m_condition . notify_one ( ) ;
// Wait until the worker thread exits.
m_thread . join ( ) ;
}
}
bool BackgroundSlicingProcess : : start ( )
{
if ( m_print - > empty ( ) ) {
if ( ! m_current_plate | | ! m_current_plate - > is_slice_result_valid ( ) )
// The print is empty (no object in Model, or all objects are out of the print bed).
return false ;
}
std : : unique_lock < std : : mutex > lck ( m_mutex ) ;
if ( m_state = = STATE_INITIAL ) {
// The worker thread is not running yet. Start it.
assert ( ! m_thread . joinable ( ) ) ;
m_thread = create_thread ( [ this ] {
# ifdef _WIN32
this - > thread_proc_safe_seh_throw ( ) ;
# else // _WIN32
this - > thread_proc_safe ( ) ;
# endif // _WIN32
} ) ;
// Wait until the worker thread is ready to execute the background processing task.
m_condition . wait ( lck , [ this ] ( ) { return m_state = = STATE_IDLE ; } ) ;
}
assert ( m_state = = STATE_IDLE | | this - > running ( ) ) ;
if ( this - > running ( ) )
// The background processing thread is already running.
return false ;
if ( ! this - > idle ( ) )
throw Slic3r : : RuntimeError ( " Cannot start a background task, the worker thread is not idle. " ) ;
m_state = STATE_STARTED ;
m_print - > set_cancel_callback ( [ this ] ( ) { this - > stop_internal ( ) ; } ) ;
lck . unlock ( ) ;
m_condition . notify_one ( ) ;
return true ;
}
// To be called on the UI thread.
bool BackgroundSlicingProcess : : stop ( )
{
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " , enter " < < std : : endl ;
// m_print->state_mutex() shall NOT be held. Unfortunately there is no interface to test for it.
std : : unique_lock < std : : mutex > lck ( m_mutex ) ;
if ( m_state = = STATE_INITIAL ) {
// m_export_path.clear();
return false ;
}
// assert(this->running());
if ( m_state = = STATE_STARTED | | m_state = = STATE_RUNNING ) {
// Cancel any task planned by the background thread on UI thread.
cancel_ui_task ( m_ui_task ) ;
m_print - > cancel ( ) ;
// Wait until the background processing stops by being canceled.
m_condition . wait ( lck , [ this ] ( ) { return m_state = = STATE_CANCELED ; } ) ;
// In the "Canceled" state. Reset the state to "Idle".
m_state = STATE_IDLE ;
m_print - > set_cancel_callback ( [ ] ( ) { } ) ;
} else if ( m_state = = STATE_FINISHED | | m_state = = STATE_CANCELED ) {
// In the "Finished" or "Canceled" state. Reset the state to "Idle".
m_state = STATE_IDLE ;
m_print - > set_cancel_callback ( [ ] ( ) { } ) ;
}
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " , exit " < < std : : endl ;
// m_export_path.clear();
return true ;
}
bool BackgroundSlicingProcess : : reset ( )
{
bool stopped = this - > stop ( ) ;
this - > reset_export ( ) ;
//BBS: don't clear print for print is not owned by background slicing process anymore
//do it in the part_plate
//m_print->clear();
this - > invalidate_all_steps ( ) ;
return stopped ;
}
// To be called by Print::apply() on the UI thread through the Print::m_cancel_callback to stop the background
// processing before changing any data of running or finalized milestones.
// This function shall not trigger any UI update through the wxWidgets event.
void BackgroundSlicingProcess : : stop_internal ( )
{
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " , enter " < < std : : endl ;
// m_print->state_mutex() shall be held. Unfortunately there is no interface to test for it.
if ( m_state = = STATE_IDLE )
// The worker thread is waiting on m_mutex/m_condition for wake up. The following lock of the mutex would block.
return ;
std : : unique_lock < std : : mutex > lck ( m_mutex ) ;
assert ( m_state = = STATE_STARTED | | m_state = = STATE_RUNNING | | m_state = = STATE_FINISHED | | m_state = = STATE_CANCELED ) ;
if ( m_state = = STATE_STARTED | | m_state = = STATE_RUNNING ) {
// Cancel any task planned by the background thread on UI thread.
cancel_ui_task ( m_ui_task ) ;
// At this point of time the worker thread may be blocking on m_print->state_mutex().
// Set the print state to canceled before unlocking the state_mutex(), so when the worker thread wakes up,
// it throws the CanceledException().
m_print - > cancel_internal ( ) ;
// Allow the worker thread to wake up if blocking on a milestone.
m_print - > state_mutex ( ) . unlock ( ) ;
// Wait until the background processing stops by being canceled.
m_condition . wait ( lck , [ this ] ( ) { return m_state = = STATE_CANCELED ; } ) ;
// Lock it back to be in a consistent state.
m_print - > state_mutex ( ) . lock ( ) ;
}
// In the "Canceled" state. Reset the state to "Idle".
m_state = STATE_IDLE ;
m_print - > set_cancel_callback ( [ ] ( ) { } ) ;
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < " , exit " < < std : : endl ;
}
// Execute task from background thread on the UI thread. Returns true if processed, false if cancelled.
bool BackgroundSlicingProcess : : execute_ui_task ( std : : function < void ( ) > task )
{
bool running = false ;
if ( m_mutex . try_lock ( ) ) {
// Cancellation is either not in process, or already canceled and waiting for us to finish.
// There must be no UI task planned.
assert ( ! m_ui_task ) ;
if ( ! m_print - > canceled ( ) ) {
running = true ;
m_ui_task = std : : make_shared < UITask > ( ) ;
}
m_mutex . unlock ( ) ;
} else {
// Cancellation is in process.
}
bool result = false ;
if ( running ) {
std : : shared_ptr < UITask > ctx = m_ui_task ;
GUI : : wxGetApp ( ) . mainframe - > m_plater - > CallAfter ( [ task , ctx ] ( ) {
// Running on the UI thread, thus ctx->state does not need to be guarded with mutex against ::cancel_ui_task().
assert ( ctx - > state = = UITask : : Planned | | ctx - > state = = UITask : : Canceled ) ;
if ( ctx - > state = = UITask : : Planned ) {
task ( ) ;
std : : unique_lock < std : : mutex > lck ( ctx - > mutex ) ;
ctx - > state = UITask : : Finished ;
}
// Wake up the worker thread from the UI thread.
ctx - > condition . notify_all ( ) ;
} ) ;
{
std : : unique_lock < std : : mutex > lock ( ctx - > mutex ) ;
ctx - > condition . wait ( lock , [ & ctx ] { return ctx - > state = = UITask : : Finished | | ctx - > state = = UITask : : Canceled ; } ) ;
}
result = ctx - > state = = UITask : : Finished ;
m_ui_task . reset ( ) ;
}
return result ;
}
// To be called on the UI thread from ::stop() and ::stop_internal().
void BackgroundSlicingProcess : : cancel_ui_task ( std : : shared_ptr < UITask > task )
{
if ( task ) {
std : : unique_lock < std : : mutex > lck ( task - > mutex ) ;
task - > state = UITask : : Canceled ;
lck . unlock ( ) ;
task - > condition . notify_all ( ) ;
}
}
bool BackgroundSlicingProcess : : empty ( ) const
{
assert ( m_print ! = nullptr ) ;
return m_print - > empty ( ) ;
}
StringObjectException BackgroundSlicingProcess : : validate ( StringObjectException * warning , Polygons * collison_polygons , std : : vector < std : : pair < Polygon , float > > * height_polygons )
{
assert ( m_print ! = nullptr ) ;
return m_print - > validate ( warning , collison_polygons , height_polygons ) ;
}
// Apply config over the print. Returns false, if the new config values caused any of the already
// processed steps to be invalidated, therefore the task will need to be restarted.
Print : : ApplyStatus BackgroundSlicingProcess : : apply ( const Model & model , const DynamicPrintConfig & config )
{
assert ( m_print ! = nullptr ) ;
assert ( config . opt_enum < PrinterTechnology > ( " printer_technology " ) = = m_print - > technology ( ) ) ;
// TODO: add partplate config
DynamicPrintConfig new_config = config ;
new_config . apply ( * m_current_plate - > config ( ) ) ;
Print : : ApplyStatus invalidated = m_print - > apply ( model , new_config ) ;
if ( ( invalidated & PrintBase : : APPLY_STATUS_INVALIDATED ) ! = 0 & & m_print - > technology ( ) = = ptFFF & &
! m_fff_print - > is_step_done ( psGCodeExport ) ) {
// Some FFF status was invalidated, and the G-code was not exported yet.
// Let the G-code preview UI know that the final G-code preview is not valid.
// In addition, this early memory deallocation reduces memory footprint.
BOOST_LOG_TRIVIAL ( info ) < < __FUNCTION__ < < boost : : format ( " : invalide gcode result %1%, will reset soon " ) % m_gcode_result ;
if ( m_gcode_result ! = nullptr )
m_gcode_result - > reset ( ) ;
}
return invalidated ;
}
void BackgroundSlicingProcess : : set_task ( const PrintBase : : TaskParams & params )
{
assert ( m_print ! = nullptr ) ;
m_print - > set_task ( params ) ;
}
// Set the output path of the G-code.
void BackgroundSlicingProcess : : schedule_export ( const std : : string & path , bool export_path_on_removable_media )
{
assert ( m_export_path . empty ( ) ) ;
if ( ! m_export_path . empty ( ) )
return ;
// Guard against entering the export step before changing the export path.
std : : scoped_lock < std : : mutex > lock ( m_print - > state_mutex ( ) ) ;
this - > invalidate_step ( bspsGCodeFinalize ) ;
m_export_path = path ;
m_export_path_on_removable_media = export_path_on_removable_media ;
}
void BackgroundSlicingProcess : : schedule_upload ( Slic3r : : PrintHostJob upload_job )
{
assert ( m_export_path . empty ( ) ) ;
if ( ! m_export_path . empty ( ) )
return ;
// Guard against entering the export step before changing the export path.
std : : scoped_lock < std : : mutex > lock ( m_print - > state_mutex ( ) ) ;
this - > invalidate_step ( bspsGCodeFinalize ) ;
m_export_path . clear ( ) ;
m_upload_job = std : : move ( upload_job ) ;
}
void BackgroundSlicingProcess : : reset_export ( )
{
assert ( ! this - > running ( ) ) ;
if ( ! this - > running ( ) ) {
m_export_path . clear ( ) ;
m_export_path_on_removable_media = false ;
// invalidate_step expects the mutex to be locked.
std : : scoped_lock < std : : mutex > lock ( m_print - > state_mutex ( ) ) ;
this - > invalidate_step ( bspsGCodeFinalize ) ;
}
}
bool BackgroundSlicingProcess : : set_step_started ( BackgroundSlicingProcessStep step )
{
return m_step_state . set_started ( step , m_print - > state_mutex ( ) , [ this ] ( ) { this - > throw_if_canceled ( ) ; } ) ;
}
void BackgroundSlicingProcess : : set_step_done ( BackgroundSlicingProcessStep step )
{
m_step_state . set_done ( step , m_print - > state_mutex ( ) , [ this ] ( ) { this - > throw_if_canceled ( ) ; } ) ;
}
bool BackgroundSlicingProcess : : is_step_done ( BackgroundSlicingProcessStep step ) const
{
return m_step_state . is_done ( step , m_print - > state_mutex ( ) ) ;
}
bool BackgroundSlicingProcess : : invalidate_step ( BackgroundSlicingProcessStep step )
{
bool invalidated = m_step_state . invalidate ( step , [ this ] ( ) { this - > stop_internal ( ) ; } ) ;
return invalidated ;
}
bool BackgroundSlicingProcess : : invalidate_all_steps ( )
{
return m_step_state . invalidate_all ( [ this ] ( ) { this - > stop_internal ( ) ; } ) ;
}
//Call post-processing script for the last step during slicing
void BackgroundSlicingProcess : : finalize_gcode ( )
{
m_print - > set_status ( 95 , _utf8 ( L ( " Running post-processing scripts " ) ) ) ;
run_post_process_scripts ( m_temp_output_path , false , " File " , m_temp_output_path , m_fff_print - > full_print_config ( ) ) ;
m_print - > set_status ( 100 , _utf8 ( L ( " Successfully executed post-processing script " ) ) ) ;
}
// G-code is generated in m_temp_output_path.
// Optionally run a post-processing script on a copy of m_temp_output_path.
// Copy the final G-code to target location (possibly a SD card, if it is a removable media, then verify that the file was written without an error).
void BackgroundSlicingProcess : : export_gcode ( )
{
// Perform the final post-processing of the export path by applying the print statistics over the file name.
std : : string export_path = m_fff_print - > print_statistics ( ) . finalize_output_path ( m_export_path ) ;
std : : string output_path = m_temp_output_path ;
//FIXME localize the messages
std : : string error_message ;
int copy_ret_val = CopyFileResult : : SUCCESS ;
try
{
copy_ret_val = copy_file ( output_path , export_path , error_message , m_export_path_on_removable_media ) ;
}
catch ( . . . )
{
throw Slic3r : : ExportError ( _utf8 ( L ( " Unknown error when export G-code. " ) ) ) ;
}
switch ( copy_ret_val ) {
case CopyFileResult : : SUCCESS : break ; // no error
case CopyFileResult : : FAIL_COPY_FILE :
//throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?\nError message: %1%"))) % error_message).str());
//break;
case CopyFileResult : : FAIL_FILES_DIFFERENT :
//throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str());
//break;
case CopyFileResult : : FAIL_RENAMING :
//throw Slic3r::ExportError((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str());
//break;
case CopyFileResult : : FAIL_CHECK_ORIGIN_NOT_OPENED :
//throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % output_path % export_path).str());
//break;
case CopyFileResult : : FAIL_CHECK_TARGET_NOT_OPENED :
//throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str());
//break;
default :
BOOST_LOG_TRIVIAL ( error ) < < " Fail code( " < < ( int ) copy_ret_val < < " ) when copy " < < output_path < < " to " < < export_path < < " . " ;
throw Slic3r : : ExportError ( ( boost : : format ( _utf8 ( L ( " Failed to save gcode file. \n Error message: %1%. \n Source file %2%. " ) ) ) % error_message % output_path ) . str ( ) ) ;
//throw Slic3r::ExportError(_utf8(L("Unknown error when export G-code.")));
break ;
}
// BBS
auto evt = new wxCommandEvent ( m_event_export_finished_id , GUI : : wxGetApp ( ) . mainframe - > m_plater - > GetId ( ) ) ;
wxString output_gcode_str = wxString : : FromUTF8 ( export_path . c_str ( ) , export_path . length ( ) ) ;
evt - > SetString ( output_gcode_str ) ;
wxQueueEvent ( GUI : : wxGetApp ( ) . mainframe - > m_plater , evt ) ;
// BBS: to be checked. Whether use export_path or output_path.
gcode_add_line_number ( export_path , m_fff_print - > full_print_config ( ) ) ;
}
// A print host upload job has been scheduled, enqueue it to the printhost job queue
void BackgroundSlicingProcess : : prepare_upload ( )
{
// Generate a unique temp path to which the gcode/zip file is copied/exported
boost : : filesystem : : path source_path = boost : : filesystem : : temp_directory_path ( )
/ boost : : filesystem : : unique_path ( " . " SLIC3R_APP_KEY " .upload.%%%%-%%%%-%%%%-%%%% " ) ;
if ( m_print = = m_fff_print ) {
m_print - > set_status ( 95 , _utf8 ( L ( " Running post-processing scripts " ) ) ) ;
std : : string error_message ;
if ( copy_file ( m_temp_output_path , source_path . string ( ) , error_message ) ! = SUCCESS )
throw Slic3r : : RuntimeError ( _utf8 ( L ( " Copying of the temporary G-code to the output G-code failed " ) ) ) ;
m_upload_job . upload_data . upload_path = m_fff_print - > print_statistics ( ) . finalize_output_path ( m_upload_job . upload_data . upload_path . string ( ) ) ;
// Make a copy of the source path, as run_post_process_scripts() is allowed to change it when making a copy of the source file
// (not here, but when the final target is a file).
std : : string source_path_str = source_path . string ( ) ;
std : : string output_name_str = m_upload_job . upload_data . upload_path . string ( ) ;
if ( run_post_process_scripts ( source_path_str , false , m_upload_job . printhost - > get_name ( ) , output_name_str , m_fff_print - > full_print_config ( ) ) )
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 > ( " thumbnail_size " ) - > 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 ( ) ) ;
for ( const ThumbnailData & data : thumbnails )
if ( data . is_valid ( ) )
write_thumbnail ( zipper , data ) ;
zipper . finalize ( ) ;
}
m_print - > set_status ( 100 , ( boost : : format ( _utf8 ( L ( " Scheduling upload to `%1%`. See Window -> Print Host Upload Queue " ) ) ) % m_upload_job . printhost - > get_host ( ) ) . str ( ) ) ;
m_upload_job . upload_data . source_path = std : : move ( source_path ) ;
GUI : : wxGetApp ( ) . printhost_job_queue ( ) . enqueue ( std : : move ( m_upload_job ) ) ;
}
// Executed by the background thread, to start a task on the UI thread.
ThumbnailsList BackgroundSlicingProcess : : render_thumbnails ( const ThumbnailsParams & params )
{
ThumbnailsList thumbnails ;
if ( m_thumbnail_cb )
this - > execute_ui_task ( [ this , & params , & thumbnails ] ( ) { thumbnails = m_thumbnail_cb ( params ) ; } ) ;
return thumbnails ;
}
} ; // namespace Slic3r