2019-05-07 19:51:14 +02:00
#include "FlashAir.hpp"
#include <algorithm>
#include <ctime>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
2019-11-04 14:28:18 +01:00
#include <boost/algorithm/string/predicate.hpp>
2019-05-07 19:51:14 +02:00
#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 ([ & , this ]( 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
{
2019-05-07 20:30:28 +02:00
return wxString :: Format ( "%s: %s" , _ ( L ( "Could not connect to FlashAir" )), msg , _ ( L ( "Note: FlashAir with firmware 2.00.02 or newer and activated upload function is required." )));
2019-05-07 19:51:14 +02:00
}
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 ;
2020-01-10 21:17:59 -05:00
std :: string strDest = upload_parent_path . string ();
if ( strDest . front () != '/' ) // Needs a leading / else root uploads fail.
{
strDest . insert ( 0 , "/" );
}
2019-05-07 19:51:14 +02:00
auto urlPrepare = make_url ( "upload.cgi" , "WRITEPROTECT=ON&FTIME" , timestamp_str ());
2020-01-10 21:17:59 -05:00
auto urlSetDir = make_url ( "upload.cgi" , "UPDIR" , strDest );
2019-05-07 19:51:14 +02:00
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 ) {
2020-01-10 21:17:59 -05:00
BOOST_LOG_TRIVIAL ( error ) << boost :: format ( "%1%: Error preparing upload: %2%, HTTP %3%, body: `%4%`" ) % name % error % status % body ;
2019-05-07 19:51:14 +02:00
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
2020-01-10 21:17:59 -05:00
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 ;
}
2019-05-07 19:51:14 +02:00
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 );
const char * name = get_name ();
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 ();
}
}
}
}