From 91b9b8aebfa634ee7a6f26284a5b782c72de0fb6 Mon Sep 17 00:00:00 2001 From: Enrico Turri <enricoturri@seznam.cz> Date: Wed, 23 May 2018 12:49:56 +0200 Subject: [PATCH 01/17] Fixed wrong layer height texture updates when using multiple objects --- lib/Slic3r/GUI/3DScene.pm | 21 +++++++++++++++++++-- xs/xsp/Print.xsp | 3 +++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 33c0e8d37..fca22bedd 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -360,6 +360,7 @@ sub _variable_layer_thickness_action { my ($self, $mouse_event, $do_modification) = @_; # A volume is selected. Test, whether hovering over a layer thickness bar. return if $self->{layer_height_edit_last_object_id} == -1; + if (defined($mouse_event)) { my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; $self->{layer_height_edit_last_z} = unscale($self->{print}->get_object($self->{layer_height_edit_last_object_id})->size->z) @@ -374,9 +375,20 @@ sub _variable_layer_thickness_action { $self->{layer_height_edit_strength}, $self->{layer_height_edit_band_width}, $self->{layer_height_edit_last_action}); - $self->volumes->[$self->{layer_height_edit_last_object_id}]->generate_layer_height_texture( + + # searches the id of the first volume of the selected object + my $volume_idx = 0; + for my $i (0..$self->{layer_height_edit_last_object_id} - 1) { + my $obj = $self->{print}->get_object($i); + for my $j (0..$obj->region_volumes_count - 1) { + $volume_idx += scalar @{$obj->get_region_volumes($j)}; + } + } + + $self->volumes->[$volume_idx]->generate_layer_height_texture( $self->{print}->get_object($self->{layer_height_edit_last_object_id}), 1); - $self->Refresh; + + $self->Refresh; # Automatic action on mouse down with the same coordinate. $self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS); } @@ -1623,6 +1635,11 @@ sub draw_active_object_annotations { $self->{layer_height_edit_shader}->set_uniform('z_cursor', $z_max * $z_cursor_relative); $self->{layer_height_edit_shader}->set_uniform('z_cursor_band_width', $self->{layer_height_edit_band_width}); glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); +#============================================================================================================================================= + print "texture id: "; + print $self->{layer_preview_z_texture_id}; + print "\n"; +#============================================================================================================================================= glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index ef9c5345f..702919514 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -52,6 +52,9 @@ _constant() int region_count() %code%{ RETVAL = THIS->print()->regions.size(); %}; + int region_volumes_count() + %code%{ RETVAL = THIS->region_volumes.size(); %}; + Ref<Print> print(); Ref<ModelObject> model_object(); Ref<StaticPrintConfig> config() From 157a34bcd935fd3b40f26f1e634fb8e06f545b3c Mon Sep 17 00:00:00 2001 From: Enrico Turri <enricoturri@seznam.cz> Date: Thu, 24 May 2018 09:57:12 +0200 Subject: [PATCH 02/17] AMF I/O - Automatic detection if open file is zip archive or xml format --- xs/src/libslic3r/Format/AMF.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp index 83b50ec9e..263363756 100644 --- a/xs/src/libslic3r/Format/AMF.cpp +++ b/xs/src/libslic3r/Format/AMF.cpp @@ -13,6 +13,9 @@ #include <boost/filesystem/operations.hpp> #include <boost/algorithm/string.hpp> +//############################################################################################################################################ +#include <boost/nowide/fstream.hpp> +//############################################################################################################################################ #include <miniz/miniz_zip.h> #if 0 @@ -666,10 +669,21 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model) // If bundle is not a null pointer, updates it if the amf file/archive contains config data bool load_amf(const char *path, PresetBundle* bundle, Model *model) { - if (boost::iends_with(path, ".zip.amf")) - return load_amf_archive(path, bundle, model); - else if (boost::iends_with(path, ".amf") || boost::iends_with(path, ".amf.xml")) + if (boost::iends_with(path, ".amf.xml")) + // backward compatibility with older slic3r output return load_amf_file(path, bundle, model); + else if (boost::iends_with(path, ".amf")) + { + boost::nowide::ifstream file(path, boost::nowide::ifstream::binary); + if (!file.good()) + return false; + + std::string zip_mask(2, '\0'); + file.read(const_cast<char*>(zip_mask.data()), 2); + file.close(); + + return (zip_mask == "PK") ? load_amf_archive(path, bundle, model) : load_amf_file(path, bundle, model); + } else return false; } From d5268fdc97adde08bbc5165cfbde4714efc583fa Mon Sep 17 00:00:00 2001 From: Enrico Turri <enricoturri@seznam.cz> Date: Mon, 28 May 2018 13:04:01 +0200 Subject: [PATCH 03/17] Removed unneeded debug output --- lib/Slic3r/GUI/3DScene.pm | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index fca22bedd..5fc1e0b18 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1635,11 +1635,6 @@ sub draw_active_object_annotations { $self->{layer_height_edit_shader}->set_uniform('z_cursor', $z_max * $z_cursor_relative); $self->{layer_height_edit_shader}->set_uniform('z_cursor_band_width', $self->{layer_height_edit_band_width}); glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); -#============================================================================================================================================= - print "texture id: "; - print $self->{layer_preview_z_texture_id}; - print "\n"; -#============================================================================================================================================= glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2, From e65fac5e8484b7737f90948d40dfd05a49a35969 Mon Sep 17 00:00:00 2001 From: bubnikv <bubnikv@gmail.com> Date: Mon, 4 Jun 2018 17:27:33 +0200 Subject: [PATCH 04/17] Added initial implementation of fixing 3MFs through the Netfabb API provided through the Windows 10 Universal Windows Platform API. --- xs/CMakeLists.txt | 2 + xs/src/slic3r/Utils/FixModelByWin10.cpp | 287 ++++++++++++++++++++++++ xs/src/slic3r/Utils/FixModelByWin10.hpp | 18 ++ 3 files changed, 307 insertions(+) create mode 100644 xs/src/slic3r/Utils/FixModelByWin10.cpp create mode 100644 xs/src/slic3r/Utils/FixModelByWin10.hpp diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 77747cd07..64f4b9274 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -232,6 +232,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/FirmwareDialog.hpp ${LIBDIR}/slic3r/Utils/Http.cpp ${LIBDIR}/slic3r/Utils/Http.hpp + ${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp + ${LIBDIR}/slic3r/Utils/FixModelByWin10.hpp ${LIBDIR}/slic3r/Utils/OctoPrint.cpp ${LIBDIR}/slic3r/Utils/OctoPrint.hpp ${LIBDIR}/slic3r/Utils/Bonjour.cpp diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/xs/src/slic3r/Utils/FixModelByWin10.cpp new file mode 100644 index 000000000..e4b1952a4 --- /dev/null +++ b/xs/src/slic3r/Utils/FixModelByWin10.cpp @@ -0,0 +1,287 @@ +#ifdef HAS_WIN10SDK + +#include "FixModelByWin10.hpp" + +#include <roapi.h> +#include <string> +#include <cstdint> +// for ComPtr +#include <wrl/client.h> +// from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/ +#include <winrt/robuffer.h> +#include <winrt/windows.storage.provider.h> +#include <winrt/windows.graphics.printing3d.h> + +extern "C"{ + // from rapi.h + typedef HRESULT (__stdcall* FunctionRoInitialize)(int); + typedef HRESULT (__stdcall* FunctionRoUninitialize)(); + typedef HRESULT (__stdcall* FunctionRoActivateInstance)(HSTRING activatableClassId, IInspectable **instance); + typedef HRESULT (__stdcall* FunctionRoGetActivationFactory)(HSTRING activatableClassId, REFIID iid, void **factory); + // from winstring.h + typedef HRESULT (__stdcall* FunctionWindowsCreateString)(LPCWSTR sourceString, UINT32 length, HSTRING *string); + typedef HRESULT (__stdcall* FunctionWindowsDelteString)(HSTRING string); +} + +HMODULE s_hRuntimeObjectLibrary = nullptr; +FunctionRoInitialize s_RoInitialize = nullptr; +FunctionRoUninitialize s_RoUninitialize = nullptr; +FunctionRoActivateInstance s_RoActivateInstance = nullptr; +FunctionRoGetActivationFactory s_RoGetActivationFactory = nullptr; +FunctionWindowsCreateString s_WindowsCreateString = nullptr; +FunctionWindowsDelteString s_WindowsDeleteString = nullptr; + +bool winrt_load_runtime_object_library() +{ + if (s_hRuntimeObjectLibrary == nullptr) + s_hRuntimeObjectLibrary = LoadLibrary(L"ComBase.dll"); + if (s_hRuntimeObjectLibrary != nullptr) { + s_RoInitialize = (FunctionRoInitialize) GetProcAddress(s_hRuntimeObjectLibrary, "RoInitialize"); + s_RoUninitialize = (FunctionRoUninitialize) GetProcAddress(s_hRuntimeObjectLibrary, "RoUninitialize"); + s_RoActivateInstance = (FunctionRoActivateInstance) GetProcAddress(s_hRuntimeObjectLibrary, "RoActivateInstance"); + s_RoGetActivationFactory = (FunctionRoGetActivationFactory) GetProcAddress(s_hRuntimeObjectLibrary, "RoGetActivationFactory"); + s_WindowsCreateString = (FunctionWindowsCreateString) GetProcAddress(s_hRuntimeObjectLibrary, "WindowsCreateString"); + s_WindowsDeleteString = (FunctionWindowsDelteString) GetProcAddress(s_hRuntimeObjectLibrary, "WindowsDeleteString"); + } + return s_RoInitialize && s_RoUninitialize && s_RoActivateInstance && s_WindowsCreateString && s_WindowsDeleteString; +} + +static HRESULT winrt_activate_instance(const std::wstring &class_name, IInspectable **pinst) +{ + HSTRING hClassName; + HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName); + if (S_OK != hr) + return hr; + hr = (*s_RoActivateInstance)(hClassName, pinst); + (*s_WindowsDeleteString)(hClassName); + return hr; +} + +template<typename TYPE> +static HRESULT winrt_activate_instance(const std::wstring &class_name, TYPE **pinst) +{ + IInspectable *pinspectable = nullptr; + HRESULT hr = winrt_activate_instance(class_name, &pinspectable); + if (S_OK != hr) + return hr; + hr = pinspectable->QueryInterface(__uuidof(TYPE), (void**)pinst); + pinspectable->Release(); + return hr; +} + +static HRESULT winrt_get_activation_factory(const std::wstring &class_name, REFIID iid, void **pinst) +{ + HSTRING hClassName; + HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName); + if (S_OK != hr) + return hr; + hr = (*s_RoGetActivationFactory)(hClassName, iid, pinst); + (*s_WindowsDeleteString)(hClassName); + return hr; +} + +template<typename TYPE> +static HRESULT winrt_get_activation_factory(const std::wstring &class_name, TYPE **pinst) +{ + return winrt_get_activation_factory(class_name, __uuidof(TYPE), reinterpret_cast<void**>(pinst)); +} + +template<typename T> +static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncAction, int blocking_tick_ms = 300) +{ + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo; + asyncAction.As(&asyncInfo); + AsyncStatus status; + // Ugly blocking loop until the RepairAsync call finishes. +//FIXME replace with a callback. +// https://social.msdn.microsoft.com/Forums/en-US/a5038fb4-b7b7-4504-969d-c102faa389fb/trying-to-block-an-async-operation-and-wait-for-a-particular-time?forum=vclanguage + for (;;) { + asyncInfo->get_Status(&status); + if (status != AsyncStatus::Started) + return status; + ::Sleep(blocking_tick_ms); + } +} + +static HRESULT winrt_open_file_stream( + const std::wstring &path, + ABI::Windows::Storage::FileAccessMode mode, + ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream) +{ + // Get the file factory. + Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFileStatics> fileFactory; + HRESULT hr = winrt_get_activation_factory(L"Windows.Storage.StorageFile", fileFactory.GetAddressOf()); + if (FAILED(hr)) return hr; + + // Open the file asynchronously. + HSTRING hstr_path; + hr = (*s_WindowsCreateString)(path.c_str(), path.size(), &hstr_path); + if (FAILED(hr)) return hr; + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFile*>> fileOpenAsync; + hr = fileFactory->GetFileFromPathAsync(hstr_path, fileOpenAsync.GetAddressOf()); + if (FAILED(hr)) return hr; + (*s_WindowsDeleteString)(hstr_path); + + // Wait until the file gets open, get the actual file. + AsyncStatus status = winrt_async_await(fileOpenAsync); + Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFile> storageFile; + if (status == AsyncStatus::Completed) { + hr = fileOpenAsync->GetResults(storageFile.GetAddressOf()); + } else { + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo; + hr = fileOpenAsync.As(&asyncInfo); + if (FAILED(hr)) return hr; + HRESULT err; + hr = asyncInfo->get_ErrorCode(&err); + return FAILED(hr) ? hr : err; + } + + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> fileStreamAsync; + hr = storageFile->OpenAsync(mode, fileStreamAsync.GetAddressOf()); + if (FAILED(hr)) return hr; + + status = winrt_async_await(fileStreamAsync); + if (status == AsyncStatus::Completed) { + hr = fileStreamAsync->GetResults(fileStream); + } else { + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo; + hr = fileStreamAsync.As(&asyncInfo); + if (FAILED(hr)) return hr; + HRESULT err; + hr = asyncInfo->get_ErrorCode(&err); + if (!FAILED(hr)) + hr = err; + } + return hr; +} + +bool is_windows10() +{ + HKEY hKey; + LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey); + if (lRes == ERROR_SUCCESS) { + WCHAR szBuffer[512]; + DWORD dwBufferSize = sizeof(szBuffer); + lRes = RegQueryValueExW(hKey, L"ProductName", 0, nullptr, (LPBYTE)szBuffer, &dwBufferSize); + if (lRes == ERROR_SUCCESS) + return wcsncmp(szBuffer, L"Windows 10", 10) == 0; + RegCloseKey(hKey); + } + return false; +} + +bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst) +{ + if (! is_windows10()) { + return false; + } + + if (! winrt_load_runtime_object_library()) { + printf("Failed to initialize the WinRT library. This should not happen on Windows 10. Exiting.\n"); + return -1; + } + + (*s_RoInitialize)(RO_INIT_MULTITHREADED); + { + Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> fileStream; + HRESULT hr = winrt_open_file_stream(L"D:\\3dprint\\bug.3mf", ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf()); + + Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3D3MFPackage> printing3d3mfpackage; + hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf()); + + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Graphics::Printing3D::Printing3DModel*>> modelAsync; + hr = printing3d3mfpackage->LoadModelFromPackageAsync(fileStream.Get(), modelAsync.GetAddressOf()); + + AsyncStatus status = winrt_async_await(modelAsync); + Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3DModel> model; + if (status == AsyncStatus::Completed) { + hr = modelAsync->GetResults(model.GetAddressOf()); + } else { + printf("Failed loading the input model. Exiting.\n"); + return -1; + } + + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IVector<ABI::Windows::Graphics::Printing3D::Printing3DMesh*>> meshes; + hr = model->get_Meshes(meshes.GetAddressOf()); + unsigned num_meshes = 0; + hr = meshes->get_Size(&num_meshes); + + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> repairAsync; + hr = model->RepairAsync(repairAsync.GetAddressOf()); + status = winrt_async_await(repairAsync); + if (status != AsyncStatus::Completed) { + printf("Mesh repair failed. Exiting.\n"); + return -1; + } + printf("Mesh repair finished successfully.\n"); + repairAsync->GetResults(); + + // Verify the number of meshes returned after the repair action. + meshes.Reset(); + hr = model->get_Meshes(meshes.GetAddressOf()); + hr = meshes->get_Size(&num_meshes); + + // Save model to this class' Printing3D3MFPackage. + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> saveToPackageAsync; + hr = printing3d3mfpackage->SaveModelToPackageAsync(model.Get(), saveToPackageAsync.GetAddressOf()); + status = winrt_async_await(saveToPackageAsync); + if (status != AsyncStatus::Completed) { + printf("Saving mesh into the 3MF container failed. Exiting.\n"); + return -1; + } + hr = saveToPackageAsync->GetResults(); + + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> generatorStreamAsync; + hr = printing3d3mfpackage->SaveAsync(generatorStreamAsync.GetAddressOf()); + status = winrt_async_await(generatorStreamAsync); + if (status != AsyncStatus::Completed) { + printf("Saving mesh into the 3MF container failed. Exiting.\n"); + return -1; + } + Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> generatorStream; + hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf()); + + // Go to the beginning of the stream. + generatorStream->Seek(0); + Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IInputStream> inputStream; + hr = generatorStream.As(&inputStream); + + // Get the buffer factory. + Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory; + hr = winrt_get_activation_factory(L"Windows.Storage.Streams.Buffer", bufferFactory.GetAddressOf()); + + // Open the destination file. + FILE *fout = ::fopen("D:\\3dprint\\bug-repaired.3mf", "wb"); + + Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer; + byte *buffer_ptr; + bufferFactory->Create(65536 * 2048, buffer.GetAddressOf()); + { + Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess; + buffer.As(&bufferByteAccess); + hr = bufferByteAccess->Buffer(&buffer_ptr); + } + uint32_t length; + hr = buffer->get_Length(&length); + + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperationWithProgress<ABI::Windows::Storage::Streams::IBuffer*, UINT32>> asyncRead; + for (;;) { + hr = inputStream->ReadAsync(buffer.Get(), 65536 * 2048, ABI::Windows::Storage::Streams::InputStreamOptions_ReadAhead, asyncRead.GetAddressOf()); + status = winrt_async_await(asyncRead); + if (status != AsyncStatus::Completed) { + printf("Saving mesh into the 3MF container failed. Exiting.\n"); + return -1; + } + hr = buffer->get_Length(&length); + if (length == 0) + break; + fwrite(buffer_ptr, length, 1, fout); + } + fclose(fout); + // Here all the COM objects will be released through the ComPtr destructors. + } + (*s_RoUninitialize)(); + return 0; +} + +#endif /* HAS_WIN10SDK */ diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/xs/src/slic3r/Utils/FixModelByWin10.hpp new file mode 100644 index 000000000..268b614ef --- /dev/null +++ b/xs/src/slic3r/Utils/FixModelByWin10.hpp @@ -0,0 +1,18 @@ +#ifndef slic3r_GUI_Utils_FixModelByWin10_hpp_ +#define slic3r_GUI_Utils_FixModelByWin10_hpp_ + +#include <string> + +#ifdef HAS_WIN10SDK + +extern bool is_windows10(); +extern bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst); + +#else /* HAS_WIN10SDK */ + +inline bool is_windows10() { return false; } +inline bool fix_model_by_win10_sdk(const std::string &, const std::string &) { return false; } + +#endif /* HAS_WIN10SDK * + +#endif /* slic3r_GUI_Utils_FixModelByWin10_hpp_ */ From d05d3cb652de8dfc156b62e940aaf8a7422dee20 Mon Sep 17 00:00:00 2001 From: bubnikv <bubnikv@gmail.com> Date: Mon, 4 Jun 2018 21:22:42 +0200 Subject: [PATCH 05/17] Initial working implementation of the "Fix by Netfabb" function. --- CMakeLists.txt | 16 ++++++ lib/Slic3r/GUI/Plater.pm | 33 ++++++++++++ xs/CMakeLists.txt | 7 +++ xs/src/slic3r/Utils/FixModelByWin10.cpp | 72 +++++++++++++++++++++---- xs/src/slic3r/Utils/FixModelByWin10.hpp | 12 ++++- xs/xsp/GUI.xsp | 7 +++ 6 files changed, 136 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e8b2a6faa..60d67553b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,22 @@ if(NOT DEFINED CMAKE_PREFIX_PATH) endif() endif() +# WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory. +# We pick it from environment if it is not defined in another way +if(WIN32) + if(NOT DEFINED WIN10SDK_PATH) + if(DEFINED ENV{WIN10SDK_PATH}) + set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}") + endif() + endif() + if(DEFINED WIN10SDK_PATH AND NOT EXISTS "${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h") + message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}") + message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found") + message("STL fixing by the Netfabb service will not be compiled") + unset(WIN10SDK_PATH) + endif() +endif() + add_subdirectory(xs) get_filename_component(PERL_BIN_PATH "${PERL_EXECUTABLE}" DIRECTORY) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d27be785c..40c2a1351 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1620,6 +1620,34 @@ sub export_object_stl { $self->statusbar->SetStatusText(L("STL file exported to ").$output_file); } +sub fix_through_netfabb { + my ($self) = @_; + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + my $model_object = $self->{model}->objects->[$obj_idx]; + my $model_fixed = Slic3r::Model->new; + Slic3r::GUI::fix_model_by_win10_sdk_gui($model_object, $self->{print}, $model_fixed); + + my @new_obj_idx = $self->load_model_objects(@{$model_fixed->objects}); + return if !@new_obj_idx; + + foreach my $new_obj_idx (@new_obj_idx) { + my $o = $self->{model}->objects->[$new_obj_idx]; + $o->clear_instances; + $o->add_instance($_) for @{$model_object->instances}; + #$o->invalidate_bounding_box; + + if ($o->volumes_count == $model_object->volumes_count) { + for my $i (0..($o->volumes_count-1)) { + $o->get_volume($i)->config->apply($model_object->get_volume($i)->config); + } + } + #FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid, + } + + $self->remove($obj_idx); +} + sub export_amf { my ($self) = @_; return if !@{$self->{objects}}; @@ -2178,6 +2206,11 @@ sub object_menu { $frame->_append_menu_item($menu, L("Export object as STL…"), L('Export this single object as STL file'), sub { $self->export_object_stl; }, undef, 'brick_go.png'); + if (Slic3r::GUI::is_windows10) { + $frame->_append_menu_item($menu, L("Fix STL through Netfabb"), L('Fix the model by sending it to a Netfabb cloud service through Windows 10 API'), sub { + $self->fix_through_netfabb; + }, undef, 'brick_go.png'); + } return $menu; } diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 64f4b9274..866374584 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -27,6 +27,13 @@ if(WIN32) # BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking. add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB) # -D_ITERATOR_DEBUG_LEVEL) + if(WIN10SDK_PATH) + message("Building with Win10 Netfabb STL fixing service support") + add_definitions(-DHAS_WIN10SDK) + include_directories("${WIN10SDK_PATH}/Include") + else() + message("Building without Win10 Netfabb STL fixing service support") + endif() endif() add_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE) diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/xs/src/slic3r/Utils/FixModelByWin10.cpp index e4b1952a4..27a79f84e 100644 --- a/xs/src/slic3r/Utils/FixModelByWin10.cpp +++ b/xs/src/slic3r/Utils/FixModelByWin10.cpp @@ -1,10 +1,15 @@ #ifdef HAS_WIN10SDK +#ifndef NOMINMAX +# define NOMINMAX +#endif + #include "FixModelByWin10.hpp" #include <roapi.h> #include <string> #include <cstdint> +#include <thread> // for ComPtr #include <wrl/client.h> // from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/ @@ -12,6 +17,18 @@ #include <winrt/windows.storage.provider.h> #include <winrt/windows.graphics.printing3d.h> +#include <boost/filesystem.hpp> +#include <boost/nowide/convert.hpp> +#include <boost/nowide/cstdio.hpp> + +#include "libslic3r/Model.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/Format/3mf.hpp" +#include "../GUI/GUI.hpp" +#include "../GUI/PresetBundle.hpp" + +#include <wx/progdlg.h> + extern "C"{ // from rapi.h typedef HRESULT (__stdcall* FunctionRoInitialize)(int); @@ -23,6 +40,8 @@ extern "C"{ typedef HRESULT (__stdcall* FunctionWindowsDelteString)(HSTRING string); } +namespace Slic3r { + HMODULE s_hRuntimeObjectLibrary = nullptr; FunctionRoInitialize s_RoInitialize = nullptr; FunctionRoUninitialize s_RoUninitialize = nullptr; @@ -178,13 +197,13 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path if (! winrt_load_runtime_object_library()) { printf("Failed to initialize the WinRT library. This should not happen on Windows 10. Exiting.\n"); - return -1; + return false; } - (*s_RoInitialize)(RO_INIT_MULTITHREADED); + HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED); { Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> fileStream; - HRESULT hr = winrt_open_file_stream(L"D:\\3dprint\\bug.3mf", ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf()); + hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf()); Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3D3MFPackage> printing3d3mfpackage; hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf()); @@ -198,7 +217,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path hr = modelAsync->GetResults(model.GetAddressOf()); } else { printf("Failed loading the input model. Exiting.\n"); - return -1; + return false; } Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IVector<ABI::Windows::Graphics::Printing3D::Printing3DMesh*>> meshes; @@ -211,7 +230,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path status = winrt_async_await(repairAsync); if (status != AsyncStatus::Completed) { printf("Mesh repair failed. Exiting.\n"); - return -1; + return false; } printf("Mesh repair finished successfully.\n"); repairAsync->GetResults(); @@ -227,7 +246,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path status = winrt_async_await(saveToPackageAsync); if (status != AsyncStatus::Completed) { printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return -1; + return false; } hr = saveToPackageAsync->GetResults(); @@ -236,7 +255,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path status = winrt_async_await(generatorStreamAsync); if (status != AsyncStatus::Completed) { printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return -1; + return false; } Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> generatorStream; hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf()); @@ -251,7 +270,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path hr = winrt_get_activation_factory(L"Windows.Storage.Streams.Buffer", bufferFactory.GetAddressOf()); // Open the destination file. - FILE *fout = ::fopen("D:\\3dprint\\bug-repaired.3mf", "wb"); + FILE *fout = boost::nowide::fopen(path_dst.c_str(), "wb"); Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer; byte *buffer_ptr; @@ -270,7 +289,7 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path status = winrt_async_await(asyncRead); if (status != AsyncStatus::Completed) { printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return -1; + return false; } hr = buffer->get_Length(&length); if (length == 0) @@ -281,7 +300,40 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path // Here all the COM objects will be released through the ComPtr destructors. } (*s_RoUninitialize)(); - return 0; + return true; } +void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result) +{ + enum { PROGRESS_RANGE = 1000 }; + wxProgressDialog progress_dialog( + _(L("Model fixing")), + _(L("Exporting model...")), + PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); + progress_dialog.Pulse(); + + // Executing the calculation in a background thread, so that the COM context could be created with its own threading model. + // (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context). + auto worker = std::thread([&model_object, &print, &result, &progress_dialog](){ + boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + path_src += ".3mf"; + Model model; + model.add_object(model_object); + bool res = Slic3r::store_3mf(path_src.string().c_str(), &model, const_cast<Print*>(&print), false); + model.clear_objects(); + model.clear_materials(); + + boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + path_dst += ".3mf"; + res = fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string()); + boost::filesystem::remove(path_src); + PresetBundle bundle; + res = Slic3r::load_3mf(path_dst.string().c_str(), &bundle, &result); + boost::filesystem::remove(path_dst); + }); + worker.join(); +} + +} // namespace Slic3r + #endif /* HAS_WIN10SDK */ diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/xs/src/slic3r/Utils/FixModelByWin10.hpp index 268b614ef..299c9b75b 100644 --- a/xs/src/slic3r/Utils/FixModelByWin10.hpp +++ b/xs/src/slic3r/Utils/FixModelByWin10.hpp @@ -3,16 +3,26 @@ #include <string> +namespace Slic3r { + +class Model; +class ModelObject; +class Print; + #ifdef HAS_WIN10SDK extern bool is_windows10(); extern bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst); +extern void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result); #else /* HAS_WIN10SDK */ inline bool is_windows10() { return false; } inline bool fix_model_by_win10_sdk(const std::string &, const std::string &) { return false; } +inline void fix_model_by_win10_sdk_gui(const ModelObject &, const Print &, Model &) {} -#endif /* HAS_WIN10SDK * +#endif /* HAS_WIN10SDK */ + +} // namespace Slic3r #endif /* slic3r_GUI_Utils_FixModelByWin10_hpp_ */ diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index 897e63693..1d860d284 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -4,6 +4,7 @@ #include <xsinit.h> #include "slic3r/GUI/GUI.hpp" #include "slic3r/Utils/ASCIIFolding.hpp" +#include "slic3r/Utils/FixModelByWin10.hpp" #include "slic3r/Utils/Serial.hpp" %} @@ -28,6 +29,9 @@ bool debugged() void break_to_debugger() %code{% Slic3r::GUI::break_to_debugger(); %}; +bool is_windows10() + %code{% RETVAL=Slic3r::is_windows10(); %}; + void set_wxapp(SV *ui) %code%{ Slic3r::GUI::set_wxapp((wxApp*)wxPli_sv_2_object(aTHX_ ui, "Wx::App")); %}; @@ -98,3 +102,6 @@ int get_export_option(SV *ui) void desktop_open_datadir_folder() %code%{ Slic3r::GUI::desktop_open_datadir_folder(); %}; + +void fix_model_by_win10_sdk_gui(ModelObject *model_object_src, Print *print, Model *model_dst) + %code%{ Slic3r::fix_model_by_win10_sdk_gui(*model_object_src, *print, *model_dst); %}; From 40bb0b6f55c5702da34b801eb23ee891a06c723d Mon Sep 17 00:00:00 2001 From: Enrico Turri <enricoturri@seznam.cz> Date: Tue, 5 Jun 2018 16:07:09 +0200 Subject: [PATCH 06/17] Fixed overflow in Polygon::area() --- xs/src/libslic3r/Polygon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/Polygon.cpp b/xs/src/libslic3r/Polygon.cpp index 27f9a2ca1..b5fd7e64f 100644 --- a/xs/src/libslic3r/Polygon.cpp +++ b/xs/src/libslic3r/Polygon.cpp @@ -103,7 +103,7 @@ double Polygon::area() const double a = 0.; for (size_t i = 0, j = n - 1; i < n; ++i) { - a += double(points[j].x + points[i].x) * double(points[i].y - points[j].y); + a += ((double)points[j].x + (double)points[i].x) * ((double)points[i].y - (double)points[j].y); j = i; } return 0.5 * a; From ce6a23ef3bc0e0caefdc96efc002a9a98339d50f Mon Sep 17 00:00:00 2001 From: bubnikv <bubnikv@gmail.com> Date: Wed, 6 Jun 2018 15:19:06 +0200 Subject: [PATCH 07/17] Repair by the netfabb service: Implemented progress dialog and cancelation. --- xs/src/slic3r/Utils/FixModelByWin10.cpp | 201 ++++++++++++++++-------- xs/src/slic3r/Utils/FixModelByWin10.hpp | 2 - 2 files changed, 132 insertions(+), 71 deletions(-) diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/xs/src/slic3r/Utils/FixModelByWin10.cpp index 27a79f84e..556035a5b 100644 --- a/xs/src/slic3r/Utils/FixModelByWin10.cpp +++ b/xs/src/slic3r/Utils/FixModelByWin10.cpp @@ -6,10 +6,19 @@ #include "FixModelByWin10.hpp" -#include <roapi.h> -#include <string> +#include <atomic> +#include <chrono> #include <cstdint> +#include <condition_variable> +#include <exception> +#include <string> #include <thread> + +#include <boost/filesystem.hpp> +#include <boost/nowide/convert.hpp> +#include <boost/nowide/cstdio.hpp> + +#include <roapi.h> // for ComPtr #include <wrl/client.h> // from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/ @@ -17,16 +26,13 @@ #include <winrt/windows.storage.provider.h> #include <winrt/windows.graphics.printing3d.h> -#include <boost/filesystem.hpp> -#include <boost/nowide/convert.hpp> -#include <boost/nowide/cstdio.hpp> - #include "libslic3r/Model.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/Format/3mf.hpp" #include "../GUI/GUI.hpp" #include "../GUI/PresetBundle.hpp" +#include <wx/msgdlg.h> #include <wx/progdlg.h> extern "C"{ @@ -105,8 +111,11 @@ static HRESULT winrt_get_activation_factory(const std::wstring &class_name, TYPE return winrt_get_activation_factory(class_name, __uuidof(TYPE), reinterpret_cast<void**>(pinst)); } +// To be called often to test whether to cancel the operation. +typedef std::function<void ()> ThrowOnCancelFn; + template<typename T> -static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncAction, int blocking_tick_ms = 300) +static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncAction, ThrowOnCancelFn throw_on_cancel, int blocking_tick_ms = 100) { Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo; asyncAction.As(&asyncInfo); @@ -118,6 +127,7 @@ static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncActio asyncInfo->get_Status(&status); if (status != AsyncStatus::Started) return status; + throw_on_cancel(); ::Sleep(blocking_tick_ms); } } @@ -125,7 +135,8 @@ static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncActio static HRESULT winrt_open_file_stream( const std::wstring &path, ABI::Windows::Storage::FileAccessMode mode, - ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream) + ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream, + ThrowOnCancelFn throw_on_cancel) { // Get the file factory. Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFileStatics> fileFactory; @@ -142,7 +153,7 @@ static HRESULT winrt_open_file_stream( (*s_WindowsDeleteString)(hstr_path); // Wait until the file gets open, get the actual file. - AsyncStatus status = winrt_async_await(fileOpenAsync); + AsyncStatus status = winrt_async_await(fileOpenAsync, throw_on_cancel); Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFile> storageFile; if (status == AsyncStatus::Completed) { hr = fileOpenAsync->GetResults(storageFile.GetAddressOf()); @@ -159,7 +170,7 @@ static HRESULT winrt_open_file_stream( hr = storageFile->OpenAsync(mode, fileStreamAsync.GetAddressOf()); if (FAILED(hr)) return hr; - status = winrt_async_await(fileStreamAsync); + status = winrt_async_await(fileStreamAsync, throw_on_cancel); if (status == AsyncStatus::Completed) { hr = fileStreamAsync->GetResults(fileStream); } else { @@ -189,21 +200,23 @@ bool is_windows10() return false; } -bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst) -{ - if (! is_windows10()) { - return false; - } +// Progress function, to be called regularly to update the progress. +typedef std::function<void (const char * /* message */, unsigned /* progress */)> ProgressFn; - if (! winrt_load_runtime_object_library()) { - printf("Failed to initialize the WinRT library. This should not happen on Windows 10. Exiting.\n"); - return false; - } +void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst, ProgressFn on_progress, ThrowOnCancelFn throw_on_cancel) +{ + if (! is_windows10()) + throw std::runtime_error("fix_model_by_win10_sdk called on non Windows 10 system"); + + if (! winrt_load_runtime_object_library()) + throw std::runtime_error("Failed to initialize the WinRT library."); HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED); { + on_progress(L("Exporting the source model"), 20); + Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> fileStream; - hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf()); + hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf(), throw_on_cancel); Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3D3MFPackage> printing3d3mfpackage; hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf()); @@ -211,30 +224,29 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Graphics::Printing3D::Printing3DModel*>> modelAsync; hr = printing3d3mfpackage->LoadModelFromPackageAsync(fileStream.Get(), modelAsync.GetAddressOf()); - AsyncStatus status = winrt_async_await(modelAsync); + AsyncStatus status = winrt_async_await(modelAsync, throw_on_cancel); Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3DModel> model; - if (status == AsyncStatus::Completed) { + if (status == AsyncStatus::Completed) hr = modelAsync->GetResults(model.GetAddressOf()); - } else { - printf("Failed loading the input model. Exiting.\n"); - return false; - } + else + throw std::runtime_error(L("Failed loading the input model.")); Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IVector<ABI::Windows::Graphics::Printing3D::Printing3DMesh*>> meshes; hr = model->get_Meshes(meshes.GetAddressOf()); unsigned num_meshes = 0; hr = meshes->get_Size(&num_meshes); + on_progress(L("Repairing the model by the Netfabb service"), 40); + Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> repairAsync; hr = model->RepairAsync(repairAsync.GetAddressOf()); - status = winrt_async_await(repairAsync); - if (status != AsyncStatus::Completed) { - printf("Mesh repair failed. Exiting.\n"); - return false; - } - printf("Mesh repair finished successfully.\n"); + status = winrt_async_await(repairAsync, throw_on_cancel); + if (status != AsyncStatus::Completed) + throw std::runtime_error(L("Mesh repair failed.")); repairAsync->GetResults(); + on_progress(L("Loading the repaired model"), 60); + // Verify the number of meshes returned after the repair action. meshes.Reset(); hr = model->get_Meshes(meshes.GetAddressOf()); @@ -243,20 +255,16 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path // Save model to this class' Printing3D3MFPackage. Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> saveToPackageAsync; hr = printing3d3mfpackage->SaveModelToPackageAsync(model.Get(), saveToPackageAsync.GetAddressOf()); - status = winrt_async_await(saveToPackageAsync); - if (status != AsyncStatus::Completed) { - printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return false; - } + status = winrt_async_await(saveToPackageAsync, throw_on_cancel); + if (status != AsyncStatus::Completed) + throw std::runtime_error(L("Saving mesh into the 3MF container failed.")); hr = saveToPackageAsync->GetResults(); Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> generatorStreamAsync; hr = printing3d3mfpackage->SaveAsync(generatorStreamAsync.GetAddressOf()); - status = winrt_async_await(generatorStreamAsync); - if (status != AsyncStatus::Completed) { - printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return false; - } + status = winrt_async_await(generatorStreamAsync, throw_on_cancel); + if (status != AsyncStatus::Completed) + throw std::runtime_error(L("Saving mesh into the 3MF container failed.")); Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> generatorStream; hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf()); @@ -286,11 +294,9 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperationWithProgress<ABI::Windows::Storage::Streams::IBuffer*, UINT32>> asyncRead; for (;;) { hr = inputStream->ReadAsync(buffer.Get(), 65536 * 2048, ABI::Windows::Storage::Streams::InputStreamOptions_ReadAhead, asyncRead.GetAddressOf()); - status = winrt_async_await(asyncRead); - if (status != AsyncStatus::Completed) { - printf("Saving mesh into the 3MF container failed. Exiting.\n"); - return false; - } + status = winrt_async_await(asyncRead, throw_on_cancel); + if (status != AsyncStatus::Completed) + throw std::runtime_error(L("Saving mesh into the 3MF container failed.")); hr = buffer->get_Length(&length); if (length == 0) break; @@ -300,38 +306,95 @@ bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path // Here all the COM objects will be released through the ComPtr destructors. } (*s_RoUninitialize)(); - return true; } +class RepairCanceledException : public std::exception { +public: + const char* what() const throw() { return "Model repair has been canceled"; } +}; + void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result) { - enum { PROGRESS_RANGE = 1000 }; + std::mutex mutex; + std::condition_variable condition; + std::unique_lock<std::mutex> lock(mutex); + struct Progress { + std::string message; + int percent = 0; + bool updated = false; + } progress; + std::atomic<bool> canceled = false; + std::atomic<bool> finished = false; + + // Open a progress dialog. wxProgressDialog progress_dialog( _(L("Model fixing")), _(L("Exporting model...")), - PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); - progress_dialog.Pulse(); - + 100, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); // Executing the calculation in a background thread, so that the COM context could be created with its own threading model. // (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context). - auto worker = std::thread([&model_object, &print, &result, &progress_dialog](){ - boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - path_src += ".3mf"; - Model model; - model.add_object(model_object); - bool res = Slic3r::store_3mf(path_src.string().c_str(), &model, const_cast<Print*>(&print), false); - model.clear_objects(); - model.clear_materials(); - - boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - path_dst += ".3mf"; - res = fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string()); - boost::filesystem::remove(path_src); - PresetBundle bundle; - res = Slic3r::load_3mf(path_dst.string().c_str(), &bundle, &result); - boost::filesystem::remove(path_dst); + bool success = false; + auto on_progress = [&mutex, &condition, &progress](const char *msg, unsigned prcnt) { + std::lock_guard<std::mutex> lk(mutex); + progress.message = msg; + progress.percent = prcnt; + progress.updated = true; + condition.notify_all(); + }; + auto worker_thread = boost::thread([&model_object, &print, &result, on_progress, &success, &canceled, &finished]() { + try { + on_progress(L("Exporting the source model"), 0); + boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + path_src += ".3mf"; + Model model; + model.add_object(model_object); + if (! Slic3r::store_3mf(path_src.string().c_str(), &model, const_cast<Print*>(&print), false)) { + boost::filesystem::remove(path_src); + throw std::runtime_error(L("Export of a temporary 3mf file failed")); + } + model.clear_objects(); + model.clear_materials(); + boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + path_dst += ".3mf"; + fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string(), on_progress, + [&canceled]() { if (canceled) throw RepairCanceledException(); }); + boost::filesystem::remove(path_src); + PresetBundle bundle; + on_progress(L("Loading the repaired model"), 80); + bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), &bundle, &result); + boost::filesystem::remove(path_dst); + if (! loaded) + throw std::runtime_error(L("Import of the repaired 3mf file failed")); + success = true; + finished = true; + on_progress(L("Model repair finished"), 100); + } catch (RepairCanceledException &ex) { + canceled = true; + finished = true; + on_progress(L("Model repair canceled"), 100); + } catch (std::exception &ex) { + success = false; + finished = true; + on_progress(ex.what(), 100); + } }); - worker.join(); + while (! finished) { + condition.wait_for(lock, std::chrono::milliseconds(500), [&progress]{ return progress.updated; }); + if (! progress_dialog.Update(progress.percent, _(progress.message))) + canceled = true; + progress.updated = false; + } + + if (canceled) { + // Nothing to show. + } else if (success) { + wxMessageDialog dlg(nullptr, _(L("Model repaired successfully")), _(L("Model Repair by the Netfabb service")), wxICON_INFORMATION | wxOK_DEFAULT); + dlg.ShowModal(); + } else { + wxMessageDialog dlg(nullptr, _(L("Model repair failed: \n")) + _(progress.message), _(L("Model Repair by the Netfabb service")), wxICON_ERROR | wxOK_DEFAULT); + dlg.ShowModal(); + } + worker_thread.join(); } } // namespace Slic3r diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/xs/src/slic3r/Utils/FixModelByWin10.hpp index 299c9b75b..c148a6970 100644 --- a/xs/src/slic3r/Utils/FixModelByWin10.hpp +++ b/xs/src/slic3r/Utils/FixModelByWin10.hpp @@ -12,13 +12,11 @@ class Print; #ifdef HAS_WIN10SDK extern bool is_windows10(); -extern bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst); extern void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result); #else /* HAS_WIN10SDK */ inline bool is_windows10() { return false; } -inline bool fix_model_by_win10_sdk(const std::string &, const std::string &) { return false; } inline void fix_model_by_win10_sdk_gui(const ModelObject &, const Print &, Model &) {} #endif /* HAS_WIN10SDK */ From 766d1d52a9c602c61f9bec66edb05162244e9c0b Mon Sep 17 00:00:00 2001 From: Enrico Turri <enricoturri@seznam.cz> Date: Thu, 7 Jun 2018 16:13:32 +0200 Subject: [PATCH 08/17] Fixed import of model rotated clockwise from 3mf --- xs/src/libslic3r/Format/3mf.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp index 0467962c3..2c32db1a6 100644 --- a/xs/src/libslic3r/Format/3mf.cpp +++ b/xs/src/libslic3r/Format/3mf.cpp @@ -1271,6 +1271,7 @@ namespace Slic3r { if ((std::abs(sx - sy) > 0.00001) || (std::abs(sx - sz) > 0.00001)) return; +#if 0 // use quaternions // rotations (extracted using quaternion) double inv_sx = 1.0 / sx; double inv_sy = 1.0 / sy; @@ -1331,6 +1332,25 @@ namespace Slic3r { if (angle_z < 0.0) angle_z += 2.0 * PI; } +#else // use eigen library + double inv_sx = 1.0 / sx; + double inv_sy = 1.0 / sy; + double inv_sz = 1.0 / sz; + + Eigen::Matrix3d m3x3; + m3x3 << (double)matrix(0, 0) * inv_sx, (double)matrix(0, 1) * inv_sy, (double)matrix(0, 2) * inv_sz, + (double)matrix(1, 0) * inv_sx, (double)matrix(1, 1) * inv_sy, (double)matrix(1, 2) * inv_sz, + (double)matrix(2, 0) * inv_sx, (double)matrix(2, 1) * inv_sy, (double)matrix(2, 2) * inv_sz; + + Eigen::AngleAxisd rotation; + rotation.fromRotationMatrix(m3x3); + + // invalid rotation axis, we currently handle only rotations around Z axis + if ((rotation.angle() != 0.0) && (rotation.axis() != Eigen::Vector3d::UnitZ()) && (rotation.axis() != -Eigen::Vector3d::UnitZ())) + return; + + double angle_z = (rotation.axis() == Eigen::Vector3d::UnitZ()) ? rotation.angle() : -rotation.angle(); +#endif instance.offset.x = offset_x; instance.offset.y = offset_y; From 7499a4dea438c0619cfaaa53dc713c0b2e52f809 Mon Sep 17 00:00:00 2001 From: bubnikv <bubnikv@gmail.com> Date: Tue, 19 Jun 2018 16:14:10 +0200 Subject: [PATCH 09/17] Disabled the UI gizmos, they are not yet ready for the prime time --- lib/Slic3r/GUI/Plater.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 0ac24664c..e15b8c34c 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -151,7 +151,7 @@ sub new { Slic3r::GUI::_3DScene::register_on_instance_moved_callback($self->{canvas3D}, $on_instances_moved); Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons); Slic3r::GUI::_3DScene::register_on_gizmo_scale_uniformly_callback($self->{canvas3D}, $on_gizmo_scale_uniformly); - Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1); +# Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1); Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1); From 478488972c7832c669f7ae7c47360555c95a7e7d Mon Sep 17 00:00:00 2001 From: Vojtech Kral <vojtech@kral.hk> Date: Tue, 19 Jun 2018 18:26:38 +0200 Subject: [PATCH 10/17] Updating bugfixes (#973) * ConfigWizard: Fix MM legacy profile detect * Remove Perl BedShapeDialog * PresetUpdater: Look for updates in resources as well * ConfigWizard: Startup condition based on printer profiles only rather than all profiles Previously wizard would not run if there was a leftover filament profile but no printer profiles * ConfigWizard: Fix button labels * ConfigWizard: Pick the very first printer variant by default --- lib/Slic3r/GUI.pm | 1 - lib/Slic3r/GUI/BedShapeDialog.pm | 316 -------------------------- xs/src/slic3r/GUI/ConfigWizard.cpp | 17 +- xs/src/slic3r/GUI/GUI.cpp | 2 +- xs/src/slic3r/Utils/PresetUpdater.cpp | 27 ++- 5 files changed, 28 insertions(+), 335 deletions(-) delete mode 100644 lib/Slic3r/GUI/BedShapeDialog.pm diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 52c482813..80130fefe 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -7,7 +7,6 @@ use File::Basename qw(basename); use FindBin; use List::Util qw(first); use Slic3r::GUI::2DBed; -use Slic3r::GUI::BedShapeDialog; use Slic3r::GUI::Controller; use Slic3r::GUI::Controller::ManualControlDialog; use Slic3r::GUI::Controller::PrinterPanel; diff --git a/lib/Slic3r/GUI/BedShapeDialog.pm b/lib/Slic3r/GUI/BedShapeDialog.pm deleted file mode 100644 index 70c8e0256..000000000 --- a/lib/Slic3r/GUI/BedShapeDialog.pm +++ /dev/null @@ -1,316 +0,0 @@ -# The bed shape dialog. -# The dialog opens from Print Settins tab -> Bed Shape: Set... - -package Slic3r::GUI::BedShapeDialog; -use strict; -use warnings; -use utf8; - -use List::Util qw(min max); -use Slic3r::Geometry qw(X Y unscale); -use Wx qw(:dialog :id :misc :sizer :choicebook wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_CLOSE); -use base 'Wx::Dialog'; - -sub new { - my $class = shift; - my ($parent, $default) = @_; - my $self = $class->SUPER::new($parent, -1, "Bed Shape", wxDefaultPosition, [350,700], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - - $self->{panel} = my $panel = Slic3r::GUI::BedShapePanel->new($self, $default); - - my $main_sizer = Wx::BoxSizer->new(wxVERTICAL); - $main_sizer->Add($panel, 1, wxEXPAND); - $main_sizer->Add($self->CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND); - - $self->SetSizer($main_sizer); - $self->SetMinSize($self->GetSize); - $main_sizer->SetSizeHints($self); - - # needed to actually free memory - EVT_CLOSE($self, sub { - $self->EndModal(wxID_OK); - $self->Destroy; - }); - - return $self; -} - -sub GetValue { - my ($self) = @_; - return $self->{panel}->GetValue; -} - -package Slic3r::GUI::BedShapePanel; - -use List::Util qw(min max sum first); -use Scalar::Util qw(looks_like_number); -use Slic3r::Geometry qw(PI X Y unscale scaled_epsilon); -use Wx qw(:font :id :misc :sizer :choicebook :filedialog :pen :brush wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_CLOSE EVT_CHOICEBOOK_PAGE_CHANGED EVT_BUTTON); -use base 'Wx::Panel'; - -use constant SHAPE_RECTANGULAR => 0; -use constant SHAPE_CIRCULAR => 1; -use constant SHAPE_CUSTOM => 2; - -sub new { - my $class = shift; - my ($parent, $default) = @_; - my $self = $class->SUPER::new($parent, -1); - - $self->on_change(undef); - - my $box = Wx::StaticBox->new($self, -1, "Shape"); - my $sbsizer = Wx::StaticBoxSizer->new($box, wxVERTICAL); - - # shape options - $self->{shape_options_book} = Wx::Choicebook->new($self, -1, wxDefaultPosition, [300,-1], wxCHB_TOP); - $sbsizer->Add($self->{shape_options_book}); - - $self->{optgroups} = []; - { - my $optgroup = $self->_init_shape_options_page('Rectangular'); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'rect_size', - type => 'point', - label => 'Size', - tooltip => 'Size in X and Y of the rectangular plate.', - default => [200,200], - )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'rect_origin', - type => 'point', - label => 'Origin', - tooltip => 'Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.', - default => [0,0], - )); - } - { - my $optgroup = $self->_init_shape_options_page('Circular'); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'diameter', - type => 'f', - label => 'Diameter', - tooltip => 'Diameter of the print bed. It is assumed that origin (0,0) is located in the center.', - sidetext => 'mm', - default => 200, - )); - } - { - my $optgroup = $self->_init_shape_options_page('Custom'); - $optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new( - full_width => 1, - widget => sub { - my ($parent) = @_; - - my $btn = Wx::Button->new($parent, -1, "Load shape from STL...", wxDefaultPosition, wxDefaultSize); - EVT_BUTTON($self, $btn, sub { $self->_load_stl }); - return $btn; - } - )); - } - - EVT_CHOICEBOOK_PAGE_CHANGED($self, -1, sub { - $self->_update_shape; - }); - - # right pane with preview canvas - my $canvas = $self->{canvas} = Slic3r::GUI::2DBed->new($self); - - # main sizer - my $top_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $top_sizer->Add($sbsizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10); - $top_sizer->Add($canvas, 1, wxEXPAND | wxALL, 10) if $canvas; - - $self->SetSizerAndFit($top_sizer); - - $self->_set_shape($default); - $self->_update_preview; - - return $self; -} - -sub on_change { - my ($self, $cb) = @_; - $self->{on_change} = $cb // sub {}; -} - -# Called from the constructor. -# Set the initial bed shape from a list of points. -# Deduce the bed shape type (rect, circle, custom) -# This routine shall be smart enough if the user messes up -# with the list of points in the ini file directly. -sub _set_shape { - my ($self, $points) = @_; - - # is this a rectangle? - if (@$points == 4) { - my $polygon = Slic3r::Polygon->new_scale(@$points); - my $lines = $polygon->lines; - if ($lines->[0]->parallel_to_line($lines->[2]) && $lines->[1]->parallel_to_line($lines->[3])) { - # okay, it's a rectangle - - # find origin - # the || 0 hack prevents "-0" which might confuse the user - my $x_min = min(map $_->[X], @$points) || 0; - my $x_max = max(map $_->[X], @$points) || 0; - my $y_min = min(map $_->[Y], @$points) || 0; - my $y_max = max(map $_->[Y], @$points) || 0; - my $origin = [-$x_min, -$y_min]; - - $self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR); - my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR]; - $optgroup->set_value('rect_size', [ $x_max-$x_min, $y_max-$y_min ]); - $optgroup->set_value('rect_origin', $origin); - $self->_update_shape; - return; - } - } - - # is this a circle? - { - # Analyze the array of points. Do they reside on a circle? - my $polygon = Slic3r::Polygon->new_scale(@$points); - my $center = $polygon->bounding_box->center; - my @vertex_distances = map $center->distance_to($_), @$polygon; - my $avg_dist = sum(@vertex_distances)/@vertex_distances; - if (!defined first { abs($_ - $avg_dist) > 10*scaled_epsilon } @vertex_distances) { - # all vertices are equidistant to center - $self->{shape_options_book}->SetSelection(SHAPE_CIRCULAR); - my $optgroup = $self->{optgroups}[SHAPE_CIRCULAR]; - $optgroup->set_value('diameter', sprintf("%.0f", unscale($avg_dist*2))); - $self->_update_shape; - return; - } - } - - if (@$points < 3) { - # Invalid polygon. Revert to default bed dimensions. - $self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR); - my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR]; - $optgroup->set_value('rect_size', [200, 200]); - $optgroup->set_value('rect_origin', [0, 0]); - $self->_update_shape; - return; - } - - # This is a custom bed shape, use the polygon provided. - $self->{shape_options_book}->SetSelection(SHAPE_CUSTOM); - # Copy the polygon to the canvas, make a copy of the array. - $self->{canvas}->bed_shape([@$points]); - $self->_update_shape; -} - -# Update the bed shape from the dialog fields. -sub _update_shape { - my ($self) = @_; - - my $page_idx = $self->{shape_options_book}->GetSelection; - if ($page_idx == SHAPE_RECTANGULAR) { - my $rect_size = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_size'); - my $rect_origin = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_origin'); - my ($x, $y) = @$rect_size; - return if !looks_like_number($x) || !looks_like_number($y); # empty strings or '-' or other things - return if !$x || !$y or $x == 0 or $y == 0; - my ($x0, $y0) = (0,0); - my ($x1, $y1) = ($x ,$y); - { - my ($dx, $dy) = @$rect_origin; - return if !looks_like_number($dx) || !looks_like_number($dy); # empty strings or '-' or other things - $x0 -= $dx; - $x1 -= $dx; - $y0 -= $dy; - $y1 -= $dy; - } - $self->{canvas}->bed_shape([ - [$x0,$y0], - [$x1,$y0], - [$x1,$y1], - [$x0,$y1], - ]); - } elsif ($page_idx == SHAPE_CIRCULAR) { - my $diameter = $self->{optgroups}[SHAPE_CIRCULAR]->get_value('diameter'); - return if !$diameter or $diameter == 0; - my $r = $diameter/2; - my $twopi = 2*PI; - my $edges = 60; - my $polygon = Slic3r::Polygon->new_scale( - map [ $r * cos $_, $r * sin $_ ], - map { $twopi/$edges*$_ } 1..$edges - ); - $self->{canvas}->bed_shape([ - map [ unscale($_->x), unscale($_->y) ], @$polygon #)) - ]); - } - - $self->{on_change}->(); - $self->_update_preview; -} - -sub _update_preview { - my ($self) = @_; - $self->{canvas}->Refresh if $self->{canvas}; - $self->Refresh; -} - -# Called from the constructor. -# Create a panel for a rectangular / circular / custom bed shape. -sub _init_shape_options_page { - my ($self, $title) = @_; - - my $panel = Wx::Panel->new($self->{shape_options_book}); - my $optgroup; - push @{$self->{optgroups}}, $optgroup = Slic3r::GUI::OptionsGroup->new( - parent => $panel, - title => 'Settings', - label_width => 100, - on_change => sub { - my ($opt_id) = @_; - #$self->{"_$opt_id"} = $optgroup->get_value($opt_id); - $self->_update_shape; - }, - ); - $panel->SetSizerAndFit($optgroup->sizer); - $self->{shape_options_book}->AddPage($panel, $title); - - return $optgroup; -} - -# Loads an stl file, projects it to the XY plane and calculates a polygon. -sub _load_stl { - my ($self) = @_; - - my $dialog = Wx::FileDialog->new($self, 'Choose a file to import bed shape from (STL/OBJ/AMF/PRUSA):', "", "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if ($dialog->ShowModal != wxID_OK) { - $dialog->Destroy; - return; - } - my $input_file = $dialog->GetPaths; - $dialog->Destroy; - - my $model = Slic3r::Model->read_from_file($input_file); - my $mesh = $model->mesh; - my $expolygons = $mesh->horizontal_projection; - - if (@$expolygons == 0) { - Slic3r::GUI::show_error($self, "The selected file contains no geometry."); - return; - } - if (@$expolygons > 1) { - Slic3r::GUI::show_error($self, "The selected file contains several disjoint areas. This is not supported."); - return; - } - - my $polygon = $expolygons->[0]->contour; - $self->{canvas}->bed_shape([ map [ unscale($_->x), unscale($_->y) ], @$polygon ]); - $self->_update_preview(); -} - -# Returns the resulting bed shape polygon. This value will be stored to the ini file. -sub GetValue { - my ($self) = @_; - return $self->{canvas}->bed_shape; -} - -1; diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/xs/src/slic3r/GUI/ConfigWizard.cpp index aed0c3534..ce06da853 100644 --- a/xs/src/slic3r/GUI/ConfigWizard.cpp +++ b/xs/src/slic3r/GUI/ConfigWizard.cpp @@ -113,6 +113,11 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, cons sizer->Add(all_none_sizer, 0, wxEXPAND); SetSizer(sizer); + + if (cboxes.size() > 0) { + cboxes[0]->SetValue(true); + on_checkbox(cboxes[0], true); + } } void PrinterPicker::select_all(bool select) @@ -598,10 +603,10 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt) static const std::unordered_map<std::string, std::pair<std::string, std::string>> legacy_preset_map {{ { "Original Prusa i3 MK2.ini", std::make_pair("MK2S", "0.4") }, - { "Original Prusa i3 MK2 MM Single Mode.ini", std::make_pair("MK2S", "0.4") }, - { "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") }, - { "Original Prusa i3 MK2 MultiMaterial.ini", std::make_pair("MK2S", "0.4") }, - { "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") }, + { "Original Prusa i3 MK2 MM Single Mode.ini", std::make_pair("MK2SMM", "0.4") }, + { "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, + { "Original Prusa i3 MK2 MultiMaterial.ini", std::make_pair("MK2SMM", "0.4") }, + { "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, { "Original Prusa i3 MK2 0.25 nozzle.ini", std::make_pair("MK2S", "0.25") }, { "Original Prusa i3 MK2 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") }, { "Original Prusa i3 MK3.ini", std::make_pair("MK3", "0.4") }, @@ -809,8 +814,8 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) : topsizer->AddSpacer(INDEX_MARGIN); topsizer->Add(p->hscroll, 1, wxEXPAND); - p->btn_prev = new wxButton(this, wxID_BACKWARD); - p->btn_next = new wxButton(this, wxID_FORWARD); + p->btn_prev = new wxButton(this, wxID_NONE, _(L("< &Back"))); + p->btn_next = new wxButton(this, wxID_NONE, _(L("&Next >"))); p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish"))); p->btn_cancel = new wxButton(this, wxID_CANCEL); p->btnsizer->AddStretchSpacer(); diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 974c554b6..e2f3925fc 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -423,7 +423,7 @@ bool check_unsaved_changes() bool config_wizard_startup(bool app_config_exists) { - if (! app_config_exists || g_PresetBundle->has_defauls_only()) { + if (! app_config_exists || g_PresetBundle->printers.size() <= 1) { config_wizard(ConfigWizard::RR_DATA_EMPTY); return true; } else if (g_AppConfig->legacy_datadir()) { diff --git a/xs/src/slic3r/Utils/PresetUpdater.cpp b/xs/src/slic3r/Utils/PresetUpdater.cpp index f34fc4c19..8159a75e2 100644 --- a/xs/src/slic3r/Utils/PresetUpdater.cpp +++ b/xs/src/slic3r/Utils/PresetUpdater.cpp @@ -259,7 +259,7 @@ void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors) con } const auto recommended = recommended_it->config_version; - BOOST_LOG_TRIVIAL(debug) << boost::format("New index for vendor: %1%: current version: %2%, recommended version: %3%") + BOOST_LOG_TRIVIAL(debug) << boost::format("Got index for vendor: %1%: current version: %2%, recommended version: %3%") % vendor.name % vendor.config_version.to_string() % recommended.to_string(); @@ -352,20 +352,25 @@ Updates PresetUpdater::priv::get_config_updates() const continue; } - auto path_in_cache = cache_path / (idx.vendor() + ".ini"); - if (! fs::exists(path_in_cache)) { - BOOST_LOG_TRIVIAL(warning) << "Index indicates update, but new bundle not found in cache: " << path_in_cache.string(); - continue; + auto path_src = cache_path / (idx.vendor() + ".ini"); + if (! fs::exists(path_src)) { + auto path_in_rsrc = rsrc_path / (idx.vendor() + ".ini"); + if (! fs::exists(path_in_rsrc)) { + BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update, but bundle found in neither cache nor resources") + % idx.vendor();; + continue; + } else { + path_src = std::move(path_in_rsrc); + } } - const auto cached_vp = VendorProfile::from_ini(path_in_cache, false); - if (cached_vp.config_version == recommended->config_version) { - updates.updates.emplace_back(std::move(path_in_cache), std::move(bundle_path), *recommended); + const auto new_vp = VendorProfile::from_ini(path_src, false); + if (new_vp.config_version == recommended->config_version) { + updates.updates.emplace_back(std::move(path_src), std::move(bundle_path), *recommended); } else { - BOOST_LOG_TRIVIAL(warning) << boost::format("Vendor: %1%: Index indicates update (%2%) but cached bundle has a different version: %3%") + BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources") % idx.vendor() - % recommended->config_version.to_string() - % cached_vp.config_version.to_string(); + % recommended->config_version.to_string(); } } } From 1602ddd56cca7a77d3d893c49df7efca926d108b Mon Sep 17 00:00:00 2001 From: Vojtech Kral <vojtech@kral.hk> Date: Tue, 22 May 2018 12:54:46 +0200 Subject: [PATCH 11/17] avrdude: Reduce retries to make timeout time more reasonable --- xs/src/avrdude/libavrdude.h | 2 +- xs/src/avrdude/stk500v2.c | 17 ++++++----------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/xs/src/avrdude/libavrdude.h b/xs/src/avrdude/libavrdude.h index e8197f9c2..238f59615 100644 --- a/xs/src/avrdude/libavrdude.h +++ b/xs/src/avrdude/libavrdude.h @@ -737,7 +737,7 @@ extern bool cancel_flag; #define RETURN_IF_CANCEL() \ do { \ if (cancel_flag) { \ - avrdude_message(MSG_INFO, "%s(): Cancelled, exiting...\n", __func__); \ + avrdude_message(MSG_INFO, "avrdude: %s(): Cancelled, exiting...\n", __func__); \ return -99; \ } \ } while (0) diff --git a/xs/src/avrdude/stk500v2.c b/xs/src/avrdude/stk500v2.c index d3acb639c..4d62640c0 100644 --- a/xs/src/avrdude/stk500v2.c +++ b/xs/src/avrdude/stk500v2.c @@ -79,7 +79,7 @@ #define SERIAL_TIMEOUT 2 // Retry count -#define RETRIES 5 +#define RETRIES 0 #if 0 #define DEBUG(...) avrdude_message(MSG_INFO, __VA_ARGS__) @@ -745,7 +745,7 @@ static int stk500v2_recv(PROGRAMMER * pgm, unsigned char *msg, size_t maxsize) { -static int stk500v2_getsync_internal(PROGRAMMER * pgm, int retries) { +int stk500v2_getsync(PROGRAMMER * pgm) { int tries = 0; unsigned char buf[1], resp[32]; int status; @@ -804,7 +804,7 @@ retry: progname, pgmname[PDATA(pgm)->pgmtype]); return 0; } else { - if (tries > retries) { + if (tries > RETRIES) { avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): can't communicate with device: resp=0x%02x\n", progname, resp[0]); return -6; @@ -814,7 +814,7 @@ retry: // or if we got a timeout } else if (status == -1) { - if (tries > retries) { + if (tries > RETRIES) { avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): timeout communicating with programmer\n", progname); return -1; @@ -823,7 +823,7 @@ retry: // or any other error } else { - if (tries > retries) { + if (tries > RETRIES) { avrdude_message(MSG_INFO, "%s: stk500v2_getsync(): error communicating with programmer: (%d)\n", progname,status); } else @@ -833,11 +833,6 @@ retry: return 0; } -int stk500v2_getsync(PROGRAMMER * pgm) { - // This is to avoid applying RETRIES exponentially - return stk500v2_getsync_internal(pgm, RETRIES); -} - static int stk500v2_command(PROGRAMMER * pgm, unsigned char * buf, size_t len, size_t maxlen) { int i; @@ -947,7 +942,7 @@ retry: } // otherwise try to sync up again - status = stk500v2_getsync_internal(pgm, 1); + status = stk500v2_getsync(pgm); if (status != 0) { if (tries > RETRIES) { avrdude_message(MSG_INFO, "%s: stk500v2_command(): failed miserably to execute command 0x%02x\n", From 2a07f3a0d58fc5785652bc3deee5e742dac16f05 Mon Sep 17 00:00:00 2001 From: Vojtech Kral <vojtech@kral.hk> Date: Wed, 23 May 2018 17:21:01 +0200 Subject: [PATCH 12/17] Firmware updater: Fix filename encoding on Windows --- xs/src/avrdude/fileio.c | 24 ++++++++++++++++++++++-- xs/src/slic3r/GUI/FirmwareDialog.cpp | 6 ++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/xs/src/avrdude/fileio.c b/xs/src/avrdude/fileio.c index f2d617823..2ed19bd15 100644 --- a/xs/src/avrdude/fileio.c +++ b/xs/src/avrdude/fileio.c @@ -45,6 +45,8 @@ #define MAX_LINE_LEN 256 /* max line length for ASCII format input files */ +#define MAX_MODE_LEN 32 // For fopen_utf8() + struct ihexrec { unsigned char reclen; @@ -100,6 +102,23 @@ static int fmt_autodetect(char * fname); +static FILE *fopen_utf8(const char *filename, const char *mode) +{ + // On Windows we need to convert the filename to UTF-16 +#if defined(WIN32NATIVE) + static wchar_t fname_buffer[PATH_MAX]; + static wchar_t mode_buffer[MAX_MODE_LEN]; + + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname_buffer, PATH_MAX) == 0) { return NULL; } + if (MultiByteToWideChar(CP_ACP, 0, mode, -1, mode_buffer, MAX_MODE_LEN) == 0) { return NULL; } + + return _wfopen(fname_buffer, mode_buffer); +#else + return fopen(filename, mode); +#endif +} + + char * fmtstr(FILEFMT format) { switch (format) { @@ -1368,10 +1387,11 @@ static int fmt_autodetect(char * fname) int first = 1; #if defined(WIN32NATIVE) - f = fopen(fname, "r"); + f = fopen_utf8(fname, "r"); #else f = fopen(fname, "rb"); #endif + if (f == NULL) { avrdude_message(MSG_INFO, "%s: error opening %s: %s\n", progname, fname, strerror(errno)); @@ -1533,7 +1553,7 @@ int fileio(int op, char * filename, FILEFMT format, if (format != FMT_IMM) { if (!using_stdio) { - f = fopen(fname, fio.mode); + f = fopen_utf8(fname, fio.mode); if (f == NULL) { avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n", progname, fio.iodesc, fname, strerror(errno)); diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp index 8ea9d2d6e..e57ec6326 100644 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp @@ -163,6 +163,7 @@ void FirmwareDialog::priv::perform_upload() flashing_status(true); + const auto filename_utf8 = filename.utf8_str(); std::vector<std::string> args {{ "-v", "-p", "atmega2560", @@ -170,7 +171,7 @@ void FirmwareDialog::priv::perform_upload() "-P", port, "-b", "115200", // XXX: is this ok to hardcode? "-D", - "-U", (boost::format("flash:w:%1%:i") % filename.ToStdString()).str() + "-U", (boost::format("flash:w:%1%:i") % filename_utf8.data()).str() }}; BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: " @@ -187,8 +188,9 @@ void FirmwareDialog::priv::perform_upload() .args(args) .on_message(std::move([q](const char *msg, unsigned /* size */) { auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); + auto wxmsg = wxString::FromUTF8(msg); evt->SetExtraLong(AE_MESSAGE); - evt->SetString(msg); + evt->SetString(std::move(wxmsg)); wxQueueEvent(q, evt); })) .on_progress(std::move([q](const char * /* task */, unsigned progress) { From 5414f7379d9ba0592f60a37a0c610a569787456c Mon Sep 17 00:00:00 2001 From: Vojtech Kral <vojtech@kral.hk> Date: Tue, 5 Jun 2018 11:55:23 +0200 Subject: [PATCH 13/17] FirmwareDialog: Fix progress display --- xs/src/avrdude/avrdude-slic3r.cpp | 23 ++++++++++++++++++----- xs/src/avrdude/avrdude-slic3r.hpp | 6 ++++++ xs/src/avrdude/ser_posix.c | 4 ++++ xs/src/slic3r/GUI/FirmwareDialog.cpp | 23 +++++++++++++++-------- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/xs/src/avrdude/avrdude-slic3r.cpp b/xs/src/avrdude/avrdude-slic3r.cpp index a859200fb..cf4380fdb 100644 --- a/xs/src/avrdude/avrdude-slic3r.cpp +++ b/xs/src/avrdude/avrdude-slic3r.cpp @@ -34,6 +34,7 @@ struct AvrDude::priv { std::string sys_config; std::vector<std::string> args; + RunFn run_fn; MessageFn message_fn; ProgressFn progress_fn; CompleteFn complete_fn; @@ -94,6 +95,12 @@ AvrDude& AvrDude::args(std::vector<std::string> args) return *this; } +AvrDude& AvrDude::on_run(RunFn fn) +{ + if (p) { p->run_fn = std::move(fn); } + return *this; +} + AvrDude& AvrDude::on_message(MessageFn fn) { if (p) { p->message_fn = std::move(fn); } @@ -123,11 +130,17 @@ AvrDude::Ptr AvrDude::run() if (self->p) { auto avrdude_thread = std::thread([self]() { - auto res = self->p->run(); - if (self->p->complete_fn) { - self->p->complete_fn(res); - } - }); + if (self->p->run_fn) { + self->p->run_fn(); + } + + auto res = self->p->run(); + + if (self->p->complete_fn) { + self->p->complete_fn(res); + } + }); + self->p->avrdude_thread = std::move(avrdude_thread); } diff --git a/xs/src/avrdude/avrdude-slic3r.hpp b/xs/src/avrdude/avrdude-slic3r.hpp index 8d881b094..29d96f72d 100644 --- a/xs/src/avrdude/avrdude-slic3r.hpp +++ b/xs/src/avrdude/avrdude-slic3r.hpp @@ -12,6 +12,7 @@ class AvrDude { public: typedef std::shared_ptr<AvrDude> Ptr; + typedef std::function<void()> RunFn; typedef std::function<void(const char * /* msg */, unsigned /* size */)> MessageFn; typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn; typedef std::function<void(int /* exit status */)> CompleteFn; @@ -29,6 +30,11 @@ public: // Set avrdude cli arguments AvrDude& args(std::vector<std::string> args); + // Set a callback to be called just after run() before avrdude is ran + // This can be used to perform any needed setup tasks from the background thread. + // This has no effect when using run_sync(). + AvrDude& on_run(RunFn fn); + // Set message output callback AvrDude& on_message(MessageFn fn); diff --git a/xs/src/avrdude/ser_posix.c b/xs/src/avrdude/ser_posix.c index 91b18e945..cb0fc0385 100644 --- a/xs/src/avrdude/ser_posix.c +++ b/xs/src/avrdude/ser_posix.c @@ -376,6 +376,10 @@ static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen FD_SET(fd->ifd, &rfds); nfds = select(fd->ifd + 1, &rfds, NULL, NULL, &to2); + // FIXME: The timeout has different behaviour on Linux vs other Unices + // On Linux, the timeout is modified by subtracting the time spent, + // on OS X (for example), it is not modified. + // POSIX recommends re-initializing it before selecting. if (nfds == 0) { avrdude_message(MSG_NOTICE2, "%s: ser_recv(): programmer is not responding\n", progname); diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp index e57ec6326..bbb00e445 100644 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp @@ -10,6 +10,7 @@ #include <wx/event.h> #include <wx/sizer.h> #include <wx/settings.h> +#include <wx/timer.h> #include <wx/panel.h> #include <wx/button.h> #include <wx/filepicker.h> @@ -36,7 +37,7 @@ namespace Slic3r { enum AvrdudeEvent { AE_MESSAGE, - AE_PRORGESS, + AE_PROGRESS, AE_EXIT, }; @@ -62,7 +63,6 @@ struct FirmwareDialog::priv std::vector<Utils::SerialPortInfo> ports; wxFilePickerCtrl *hex_picker; wxStaticText *txt_status; - wxStaticText *txt_progress; wxGauge *progressbar; wxCollapsiblePane *spoiler; wxTextCtrl *txt_stdout; @@ -72,6 +72,8 @@ struct FirmwareDialog::priv wxString btn_flash_label_ready; wxString btn_flash_label_flashing; + wxTimer timer_pulse; + // This is a shared pointer holding the background AvrDude task // also serves as a status indication (it is set _iff_ the background task is running, otherwise it is reset). AvrDude::Ptr avrdude; @@ -83,6 +85,7 @@ struct FirmwareDialog::priv q(q), btn_flash_label_ready(_(L("Flash!"))), btn_flash_label_flashing(_(L("Cancel"))), + timer_pulse(q), avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()), progress_tasks_done(0), cancelled(false) @@ -131,6 +134,7 @@ void FirmwareDialog::priv::flashing_status(bool value, AvrDudeComplete complete) progressbar->SetValue(0); progress_tasks_done = 0; cancelled = false; + timer_pulse.Start(50); } else { auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); port_picker->Enable(); @@ -186,6 +190,7 @@ void FirmwareDialog::priv::perform_upload() avrdude = AvrDude() .sys_config(avrdude_config) .args(args) + .on_run([]() { /* TODO: needed? */ }) .on_message(std::move([q](const char *msg, unsigned /* size */) { auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); auto wxmsg = wxString::FromUTF8(msg); @@ -195,7 +200,7 @@ void FirmwareDialog::priv::perform_upload() })) .on_progress(std::move([q](const char * /* task */, unsigned progress) { auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); - evt->SetExtraLong(AE_PRORGESS); + evt->SetExtraLong(AE_PROGRESS); evt->SetInt(progress); wxQueueEvent(q, evt); })) @@ -226,19 +231,19 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt) txt_stdout->AppendText(evt.GetString()); break; - case AE_PRORGESS: + case AE_PROGRESS: // We try to track overall progress here. // When uploading the firmware, avrdude first reads a littlebit of status data, // then performs write, then reading (verification). - // We Pulse() during the first read and combine progress of the latter two tasks. + // We ignore the first task (which just let's the timer_pulse work) + // and then display overall progress during the latter two tasks. - if (progress_tasks_done == 0) { - progressbar->Pulse(); - } else { + if (progress_tasks_done > 0) { progressbar->SetValue(progress_tasks_done - 100 + evt.GetInt()); } if (evt.GetInt() == 100) { + timer_pulse.Stop(); progress_tasks_done += 100; } @@ -376,6 +381,8 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) : } }); + Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->p->progressbar->Pulse(); }); + Bind(EVT_AVRDUDE, [this](wxCommandEvent &evt) { this->p->on_avrdude(evt); }); Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt) { From 7863412687376a5882aceb46085bfdaf0cadf2b0 Mon Sep 17 00:00:00 2001 From: bubnikv <bubnikv@gmail.com> Date: Thu, 14 Jun 2018 15:03:16 +0200 Subject: [PATCH 14/17] Firwmare updater for the Einsy external flash memory, to be used as a storage for localization strings. Hacked into the avrdude Arduino STK500 (not STK500v2) protocol. --- xs/CMakeLists.txt | 16 ++-- xs/src/avrdude/arduino.c | 51 +++++++++++ xs/src/avrdude/stk500.c | 125 +++++++++++++++++---------- xs/src/slic3r/GUI/FirmwareDialog.cpp | 12 ++- 4 files changed, 147 insertions(+), 57 deletions(-) diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 117af1959..66c1cdd6a 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -353,8 +353,6 @@ add_library(semver STATIC ) -add_subdirectory(src/avrdude) - # Generate the Slic3r Perl module (XS) typemap file. set(MyTypemap ${CMAKE_CURRENT_BINARY_DIR}/typemap) add_custom_command( @@ -517,12 +515,12 @@ if (WIN32 AND ";${PerlEmbed_CCFLAGS};" MATCHES ";[-/]Od;") message("Old CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}") message("Old CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELEASE}") message("Old CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS_RELEASE}") - set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG") - set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG") - set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG") - set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG") - set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG") + set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") + set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG /DWIN32") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG /DWIN32") + set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32") + set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG /DWIN32") endif() # The following line will add -fPIC on Linux to make the XS.so rellocable. add_definitions(${PerlEmbed_CCCDLFLAGS}) @@ -530,6 +528,8 @@ if (WIN32) target_link_libraries(XS ${PERL_LIBRARY}) endif() +add_subdirectory(src/avrdude) + ## REQUIRED packages # Find and configure boost diff --git a/xs/src/avrdude/arduino.c b/xs/src/avrdude/arduino.c index 566f56abd..886a43f0b 100644 --- a/xs/src/avrdude/arduino.c +++ b/xs/src/avrdude/arduino.c @@ -102,6 +102,57 @@ static int arduino_open(PROGRAMMER * pgm, char * port) */ stk500_drain(pgm, 0); +{ + //FIXME initialization sequence for programming the external FLASH. + const char entry_magic_send [] = "start\n"; + const char entry_magic_receive[] = "w25x20cl_enter\n"; + const char entry_magic_cfm [] = "w25x20cl_cfm\n"; + const char *entry_magic_ptr = entry_magic_send; + struct timeval tv; + double tstart, tnow; + char c; + gettimeofday(&tv, NULL); + tstart = tv.tv_sec; + while (*entry_magic_ptr != 0) { + if (serial_recv(&pgm->fd, &c, 1) < 0) + goto timedout; + printf("Received: %c (%d)\n", c, (int)c); + if (c != *entry_magic_ptr ++) { + avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname); + return -1; + } + gettimeofday(&tv, NULL); + tnow = tv.tv_sec; + if (tnow-tstart > 2.) { // wuff - signed/unsigned/overflow + timedout: + avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname); + return -1; + } + } + if (serial_send(&pgm->fd, entry_magic_receive, strlen(entry_magic_receive)) < 0) { + avrdude_message(MSG_INFO, "%s: stk500v2_send(): failed to send command to serial port\n",progname); + return -1; + } + + entry_magic_ptr = entry_magic_cfm; + while (*entry_magic_ptr != 0) { + if (serial_recv(&pgm->fd, &c, 1) < 0) + goto timedout2; + printf("Received: %c (%d)\n", c, (int)c); + if (c != *entry_magic_ptr++) { + avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname); + return -1; + } + gettimeofday(&tv, NULL); + tnow = tv.tv_sec; + if (tnow - tstart > 2.) { // wuff - signed/unsigned/overflow + timedout2: + avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname); + return -1; + } + } +} + if (stk500_getsync(pgm) < 0) return -1; diff --git a/xs/src/avrdude/stk500.c b/xs/src/avrdude/stk500.c index 5d2d3c1df..63deb228f 100644 --- a/xs/src/avrdude/stk500.c +++ b/xs/src/avrdude/stk500.c @@ -716,11 +716,14 @@ static int stk500_loadaddr(PROGRAMMER * pgm, AVRMEM * mem, unsigned int addr) } buf[0] = Cmnd_STK_LOAD_ADDRESS; - buf[1] = addr & 0xff; - buf[2] = (addr >> 8) & 0xff; - buf[3] = Sync_CRC_EOP; - - stk500_send(pgm, buf, 4); + // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter. + // Send the binary data by nibbles to avoid transmitting the ';' character. + buf[1] = addr & 0x0f; + buf[2] = addr & 0xf0; + buf[3] = (addr >> 8) & 0x0f; + buf[4] = (addr >> 8) & 0xf0; + buf[5] = Sync_CRC_EOP; + stk500_send(pgm, buf, 6); if (stk500_recv(pgm, buf, 1) < 0) return -1; @@ -765,7 +768,9 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, int block_size; int tries; unsigned int n; - unsigned int i; + unsigned int i, j; + unsigned int prusa3d_semicolon_workaround_round = 0; + bool has_semicolon = false; if (strcmp(m->desc, "flash") == 0) { memtype = 'F'; @@ -806,44 +811,64 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, tries++; stk500_loadaddr(pgm, m, addr/a_div); - /* build command block and avoid multiple send commands as it leads to a crash - of the silabs usb serial driver on mac os x */ - i = 0; - buf[i++] = Cmnd_STK_PROG_PAGE; - buf[i++] = (block_size >> 8) & 0xff; - buf[i++] = block_size & 0xff; - buf[i++] = memtype; - memcpy(&buf[i], &m->buf[addr], block_size); - i += block_size; - buf[i++] = Sync_CRC_EOP; - stk500_send( pgm, buf, i); - - if (stk500_recv(pgm, buf, 1) < 0) - return -1; - if (buf[0] == Resp_STK_NOSYNC) { - if (tries > 33) { - avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): can't get into sync\n", - progname); - return -3; + for (i = 0; i < n_bytes; ++ i) + if (m->buf[addr + i] == ';') { + has_semicolon = true; + break; + } + + for (prusa3d_semicolon_workaround_round = 0; prusa3d_semicolon_workaround_round < (has_semicolon ? 2 : 1); ++ prusa3d_semicolon_workaround_round) { + /* build command block and avoid multiple send commands as it leads to a crash + of the silabs usb serial driver on mac os x */ + i = 0; + buf[i++] = Cmnd_STK_PROG_PAGE; + // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter. + // Send the binary data by nibbles to avoid transmitting the ';' character. + buf[i++] = (block_size >> 8) & 0xf0; + buf[i++] = (block_size >> 8) & 0x0f; + buf[i++] = block_size & 0xf0; + buf[i++] = block_size & 0x0f; + buf[i++] = memtype; + if (has_semicolon) { + for (j = 0; j < block_size; ++i, ++ j) { + buf[i] = m->buf[addr + j]; + if (buf[i] == ';') + buf[i] |= (prusa3d_semicolon_workaround_round ? 0xf0 : 0x0f); + } + } else { + memcpy(&buf[i], &m->buf[addr], block_size); + i += block_size; + } + buf[i++] = Sync_CRC_EOP; + stk500_send( pgm, buf, i); + + if (stk500_recv(pgm, buf, 1) < 0) + return -1; + if (buf[0] == Resp_STK_NOSYNC) { + if (tries > 33) { + avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): can't get into sync\n", + progname); + return -3; + } + if (stk500_getsync(pgm) < 0) + return -1; + goto retry; + } + else if (buf[0] != Resp_STK_INSYNC) { + avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, " + "expect=0x%02x, resp=0x%02x\n", + progname, Resp_STK_INSYNC, buf[0]); + return -4; + } + + if (stk500_recv(pgm, buf, 1) < 0) + return -1; + if (buf[0] != Resp_STK_OK) { + avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, " + "expect=0x%02x, resp=0x%02x\n", + progname, Resp_STK_INSYNC, buf[0]); + return -5; } - if (stk500_getsync(pgm) < 0) - return -1; - goto retry; - } - else if (buf[0] != Resp_STK_INSYNC) { - avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, " - "expect=0x%02x, resp=0x%02x\n", - progname, Resp_STK_INSYNC, buf[0]); - return -4; - } - - if (stk500_recv(pgm, buf, 1) < 0) - return -1; - if (buf[0] != Resp_STK_OK) { - avrdude_message(MSG_INFO, "\n%s: stk500_paged_write(): (a) protocol error, " - "expect=0x%02x, resp=0x%02x\n", - progname, Resp_STK_INSYNC, buf[0]); - return -5; } } @@ -893,11 +918,15 @@ static int stk500_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, tries++; stk500_loadaddr(pgm, m, addr/a_div); buf[0] = Cmnd_STK_READ_PAGE; - buf[1] = (block_size >> 8) & 0xff; - buf[2] = block_size & 0xff; - buf[3] = memtype; - buf[4] = Sync_CRC_EOP; - stk500_send(pgm, buf, 5); + // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter. + // Send the binary data by nibbles to avoid transmitting the ';' character. + buf[1] = (block_size >> 8) & 0xf0; + buf[2] = (block_size >> 8) & 0x0f; + buf[3] = block_size & 0xf0; + buf[4] = block_size & 0x0f; + buf[5] = memtype; + buf[6] = Sync_CRC_EOP; + stk500_send(pgm, buf, 7); if (stk500_recv(pgm, buf, 1) < 0) return -1; diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp index bbb00e445..136b17af6 100644 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp @@ -171,11 +171,19 @@ void FirmwareDialog::priv::perform_upload() std::vector<std::string> args {{ "-v", "-p", "atmega2560", - "-c", "wiring", + // Using the "Wiring" mode to program Rambo or Einsy, using the STK500v2 protocol (not the STK500). + // The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip + // is flashed with a buggy firmware. +// "-c", "wiring", + // Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2). + // The Prusa's avrdude is patched again to never send semicolons inside the data packets. + "-c", "arduino", "-P", port, "-b", "115200", // XXX: is this ok to hardcode? "-D", + "-u", // disable safe mode "-U", (boost::format("flash:w:%1%:i") % filename_utf8.data()).str() +// "-v", "-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange }}; BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: " @@ -192,6 +200,8 @@ void FirmwareDialog::priv::perform_upload() .args(args) .on_run([]() { /* TODO: needed? */ }) .on_message(std::move([q](const char *msg, unsigned /* size */) { + // Debugging output to console, useful when avrdude is executed in a super verbose mode (with -v -v -v). + // printf("%s", msg); auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); auto wxmsg = wxString::FromUTF8(msg); evt->SetExtraLong(AE_MESSAGE); From 15f943938b622a3f32bbb8847809acb53782df2a Mon Sep 17 00:00:00 2001 From: Vojtech Kral <vojtech@kral.hk> Date: Mon, 18 Jun 2018 18:10:50 +0200 Subject: [PATCH 15/17] avrdude: add file offset to update operation spec, refactoring --- xs/src/avrdude/arduino.c | 95 ++++++++++++++-------------- xs/src/avrdude/fileio.c | 57 +++++++++++------ xs/src/avrdude/libavrdude.h | 5 +- xs/src/avrdude/main.c | 2 +- xs/src/avrdude/update.c | 38 ++++++++--- xs/src/slic3r/GUI/FirmwareDialog.cpp | 4 +- 6 files changed, 118 insertions(+), 83 deletions(-) diff --git a/xs/src/avrdude/arduino.c b/xs/src/avrdude/arduino.c index 886a43f0b..5e0693e94 100644 --- a/xs/src/avrdude/arduino.c +++ b/xs/src/avrdude/arduino.c @@ -80,6 +80,49 @@ static int arduino_read_sig_bytes(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m) return 3; } +static int prusa_init_external_flash(PROGRAMMER * pgm) +{ + // Note: send/receive as in _the firmare_ send & receives + const char entry_magic_send [] = "start\n"; + const char entry_magic_receive[] = "w25x20cl_enter\n"; + const char entry_magic_cfm [] = "w25x20cl_cfm\n"; + const size_t buffer_len = 32; // Should be large enough for the above messages + + int res; + size_t recv_size; + char *buffer = alloca(buffer_len); + + // 1. receive the "start" command + recv_size = sizeof(entry_magic_send) - 1; + res = serial_recv(&pgm->fd, buffer, recv_size); + if (res < 0) { + avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname); + return -1; + } else if (strncmp(buffer, entry_magic_send, recv_size) != 0) { + avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname); + return -1; + } + + // 2. Send the external flash programmer enter command + if (serial_send(&pgm->fd, entry_magic_receive, sizeof(entry_magic_receive) - 1) < 0) { + avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): Failed to send command to the printer\n",progname); + return -1; + } + + // 3. Receive the entry confirmation command + recv_size = sizeof(entry_magic_cfm) - 1; + res = serial_recv(&pgm->fd, buffer, recv_size); + if (res < 0) { + avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname); + return -1; + } else if (strncmp(buffer, entry_magic_cfm, recv_size) != 0) { + avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname); + return -1; + } + + return 0; +} + static int arduino_open(PROGRAMMER * pgm, char * port) { union pinfo pinfo; @@ -102,56 +145,10 @@ static int arduino_open(PROGRAMMER * pgm, char * port) */ stk500_drain(pgm, 0); -{ - //FIXME initialization sequence for programming the external FLASH. - const char entry_magic_send [] = "start\n"; - const char entry_magic_receive[] = "w25x20cl_enter\n"; - const char entry_magic_cfm [] = "w25x20cl_cfm\n"; - const char *entry_magic_ptr = entry_magic_send; - struct timeval tv; - double tstart, tnow; - char c; - gettimeofday(&tv, NULL); - tstart = tv.tv_sec; - while (*entry_magic_ptr != 0) { - if (serial_recv(&pgm->fd, &c, 1) < 0) - goto timedout; - printf("Received: %c (%d)\n", c, (int)c); - if (c != *entry_magic_ptr ++) { - avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname); - return -1; - } - gettimeofday(&tv, NULL); - tnow = tv.tv_sec; - if (tnow-tstart > 2.) { // wuff - signed/unsigned/overflow - timedout: - avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname); - return -1; - } + // Initialization sequence for programming the external FLASH on the Prusa MK3 + if (prusa_init_external_flash(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: arduino_open(): Failed to initialize MK3 external flash programming mode\n", progname); } - if (serial_send(&pgm->fd, entry_magic_receive, strlen(entry_magic_receive)) < 0) { - avrdude_message(MSG_INFO, "%s: stk500v2_send(): failed to send command to serial port\n",progname); - return -1; - } - - entry_magic_ptr = entry_magic_cfm; - while (*entry_magic_ptr != 0) { - if (serial_recv(&pgm->fd, &c, 1) < 0) - goto timedout2; - printf("Received: %c (%d)\n", c, (int)c); - if (c != *entry_magic_ptr++) { - avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname); - return -1; - } - gettimeofday(&tv, NULL); - tnow = tv.tv_sec; - if (tnow - tstart > 2.) { // wuff - signed/unsigned/overflow - timedout2: - avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname); - return -1; - } - } -} if (stk500_getsync(pgm) < 0) return -1; diff --git a/xs/src/avrdude/fileio.c b/xs/src/avrdude/fileio.c index 2ed19bd15..aa57f5587 100644 --- a/xs/src/avrdude/fileio.c +++ b/xs/src/avrdude/fileio.c @@ -45,7 +45,7 @@ #define MAX_LINE_LEN 256 /* max line length for ASCII format input files */ -#define MAX_MODE_LEN 32 // For fopen_utf8() +#define MAX_MODE_LEN 32 // For fopen_and_seek() struct ihexrec { @@ -98,12 +98,13 @@ static int fileio_num(struct fioparms * fio, char * filename, FILE * f, AVRMEM * mem, int size, FILEFMT fmt); -static int fmt_autodetect(char * fname); +static int fmt_autodetect(char * fname, size_t offset); -static FILE *fopen_utf8(const char *filename, const char *mode) +static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offset) { + FILE *file; // On Windows we need to convert the filename to UTF-16 #if defined(WIN32NATIVE) static wchar_t fname_buffer[PATH_MAX]; @@ -112,10 +113,24 @@ static FILE *fopen_utf8(const char *filename, const char *mode) if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname_buffer, PATH_MAX) == 0) { return NULL; } if (MultiByteToWideChar(CP_ACP, 0, mode, -1, mode_buffer, MAX_MODE_LEN) == 0) { return NULL; } - return _wfopen(fname_buffer, mode_buffer); + file = _wfopen(fname_buffer, mode_buffer); #else - return fopen(filename, mode); + file = fopen(filename, mode); #endif + + if (file != NULL) { + // Some systems allow seeking past the end of file, so we need check for that first and disallow + if (fseek(file, 0, SEEK_END) != 0 + || offset >= ftell(file) + || fseek(file, offset, SEEK_SET) != 0 + ) { + fclose(file); + file = NULL; + errno = EINVAL; + } + } + + return file; } @@ -1377,7 +1392,7 @@ int fileio_setparms(int op, struct fioparms * fp, -static int fmt_autodetect(char * fname) +static int fmt_autodetect(char * fname, size_t offset) { FILE * f; unsigned char buf[MAX_LINE_LEN]; @@ -1387,9 +1402,9 @@ static int fmt_autodetect(char * fname) int first = 1; #if defined(WIN32NATIVE) - f = fopen_utf8(fname, "r"); + f = fopen_and_seek(fname, "r", offset); #else - f = fopen(fname, "rb"); + f = fopen_and_seek(fname, "rb", offset); #endif if (f == NULL) { @@ -1465,7 +1480,7 @@ static int fmt_autodetect(char * fname) int fileio(int op, char * filename, FILEFMT format, - struct avrpart * p, char * memtype, int size) + struct avrpart * p, char * memtype, int size, size_t offset) { int rc; FILE * f; @@ -1497,15 +1512,17 @@ int fileio(int op, char * filename, FILEFMT format, using_stdio = 0; if (strcmp(filename, "-")==0) { - if (fio.op == FIO_READ) { - fname = "<stdin>"; - f = stdin; - } - else { - fname = "<stdout>"; - f = stdout; - } - using_stdio = 1; + return -1; + // Note: we don't want to read stdin or write to stdout as part of Slic3r + // if (fio.op == FIO_READ) { + // fname = "<stdin>"; + // f = stdin; + // } + // else { + // fname = "<stdout>"; + // f = stdout; + // } + // using_stdio = 1; } else { fname = filename; @@ -1522,7 +1539,7 @@ int fileio(int op, char * filename, FILEFMT format, return -1; } - format_detect = fmt_autodetect(fname); + format_detect = fmt_autodetect(fname, offset); if (format_detect < 0) { avrdude_message(MSG_INFO, "%s: can't determine file format for %s, specify explicitly\n", progname, fname); @@ -1553,7 +1570,7 @@ int fileio(int op, char * filename, FILEFMT format, if (format != FMT_IMM) { if (!using_stdio) { - f = fopen_utf8(fname, fio.mode); + f = fopen_and_seek(fname, fio.mode, offset); if (f == NULL) { avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n", progname, fio.iodesc, fname, strerror(errno)); diff --git a/xs/src/avrdude/libavrdude.h b/xs/src/avrdude/libavrdude.h index 238f59615..536f1a2f7 100644 --- a/xs/src/avrdude/libavrdude.h +++ b/xs/src/avrdude/libavrdude.h @@ -821,7 +821,7 @@ extern "C" { char * fmtstr(FILEFMT format); int fileio(int op, char * filename, FILEFMT format, - struct avrpart * p, char * memtype, int size); + struct avrpart * p, char * memtype, int size, size_t offset); #ifdef __cplusplus } @@ -870,6 +870,7 @@ enum updateflags { typedef struct update_t { char * memtype; int op; + size_t offset; char * filename; int format; } UPDATE; @@ -881,7 +882,7 @@ extern "C" { extern UPDATE * parse_op(char * s); extern UPDATE * dup_update(UPDATE * upd); extern UPDATE * new_update(int op, char * memtype, int filefmt, - char * filename); + char * filename, size_t offset); extern void free_update(UPDATE * upd); extern int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags flags); diff --git a/xs/src/avrdude/main.c b/xs/src/avrdude/main.c index 0550ceff1..91f2fc827 100644 --- a/xs/src/avrdude/main.c +++ b/xs/src/avrdude/main.c @@ -194,7 +194,7 @@ static void usage(void) " -F Override invalid signature check.\n" " -e Perform a chip erase.\n" " -O Perform RC oscillator calibration (see AVR053). \n" - " -U <memtype>:r|w|v:<filename>[:format]\n" + " -U <memtype>:r|w|v:<offset>:<filename>[:format]\n" " Memory operation specification.\n" " Multiple -U options are allowed, each request\n" " is performed in the order specified.\n" diff --git a/xs/src/avrdude/update.c b/xs/src/avrdude/update.c index a73461dfa..fa3372476 100644 --- a/xs/src/avrdude/update.c +++ b/xs/src/avrdude/update.c @@ -101,6 +101,25 @@ UPDATE * parse_op(char * s) p++; + // Extension: Parse file contents offset + size_t offset = 0; + + for (; *p != ':'; p++) { + if (*p >= '0' && *p <= '9') { + offset *= 10; + offset += *p - 0x30; + } else { + avrdude_message(MSG_INFO, "%s: invalid update specification: offset is not a number\n", progname); + free(upd->memtype); + free(upd); + return NULL; + } + } + + upd->offset = offset; + printf("parse_op: offset: %lu\n", offset); + p++; + /* * Now, parse the filename component. Instead of looking for the * leftmost possible colon delimiter, we look for the rightmost one. @@ -176,7 +195,7 @@ UPDATE * dup_update(UPDATE * upd) return u; } -UPDATE * new_update(int op, char * memtype, int filefmt, char * filename) +UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, size_t offset) { UPDATE * u; @@ -190,6 +209,7 @@ UPDATE * new_update(int op, char * memtype, int filefmt, char * filename) u->filename = strdup(filename); u->op = op; u->format = filefmt; + u->offset = offset; return u; } @@ -250,7 +270,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f progname, strcmp(upd->filename, "-")==0 ? "<stdout>" : upd->filename); } - rc = fileio(FIO_WRITE, upd->filename, upd->format, p, upd->memtype, size); + rc = fileio(FIO_WRITE, upd->filename, upd->format, p, upd->memtype, size, 0); if (rc < 0) { avrdude_message(MSG_INFO, "%s: write to file '%s' failed\n", progname, upd->filename); @@ -267,7 +287,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f progname, strcmp(upd->filename, "-")==0 ? "<stdin>" : upd->filename); } - rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1); + rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset); if (rc < 0) { avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n", progname, upd->filename); @@ -296,11 +316,11 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f report_progress(1,1,NULL); } else { - /* - * test mode, don't actually write to the chip, output the buffer - * to stdout in intel hex instead - */ - rc = fileio(FIO_WRITE, "-", FMT_IHEX, p, upd->memtype, size); + // /* + // * test mode, don't actually write to the chip, output the buffer + // * to stdout in intel hex instead + // */ + // rc = fileio(FIO_WRITE, "-", FMT_IHEX, p, upd->memtype, size, 0); } if (rc < 0) { @@ -332,7 +352,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f progname, mem->desc, upd->filename); } - rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1); + rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset); if (rc < 0) { avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n", progname, upd->filename); diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp index 136b17af6..f9aabacc0 100644 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp @@ -182,8 +182,8 @@ void FirmwareDialog::priv::perform_upload() "-b", "115200", // XXX: is this ok to hardcode? "-D", "-u", // disable safe mode - "-U", (boost::format("flash:w:%1%:i") % filename_utf8.data()).str() -// "-v", "-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange + "-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(), // FIXME + // "-vvvvv", //"-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange }}; BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: " From 635bb1e484e319b1d403faac39170cc4d0dfa6f7 Mon Sep 17 00:00:00 2001 From: Vojtech Kral <vojtech@kral.hk> Date: Tue, 19 Jun 2018 11:16:56 +0200 Subject: [PATCH 16/17] Firmware updater: Add support for l10n firmware images --- xs/src/avrdude/avrdude-slic3r.cpp | 35 ++++-- xs/src/avrdude/avrdude-slic3r.hpp | 19 +-- xs/src/avrdude/main.c | 2 +- xs/src/slic3r/GUI/FirmwareDialog.cpp | 178 ++++++++++++++++++--------- 4 files changed, 156 insertions(+), 78 deletions(-) diff --git a/xs/src/avrdude/avrdude-slic3r.cpp b/xs/src/avrdude/avrdude-slic3r.cpp index cf4380fdb..030353413 100644 --- a/xs/src/avrdude/avrdude-slic3r.cpp +++ b/xs/src/avrdude/avrdude-slic3r.cpp @@ -1,5 +1,6 @@ #include "avrdude-slic3r.hpp" +#include <deque> #include <thread> extern "C" { @@ -33,7 +34,8 @@ static void avrdude_progress_handler_closure(const char *task, unsigned progress struct AvrDude::priv { std::string sys_config; - std::vector<std::string> args; + std::deque<std::vector<std::string>> args; + size_t current_args_set = 0; RunFn run_fn; MessageFn message_fn; ProgressFn progress_fn; @@ -41,10 +43,13 @@ struct AvrDude::priv std::thread avrdude_thread; + priv(std::string &&sys_config) : sys_config(sys_config) {} + + int run_one(const std::vector<std::string> &args); int run(); }; -int AvrDude::priv::run() { +int AvrDude::priv::run_one(const std::vector<std::string> &args) { std::vector<char*> c_args {{ const_cast<char*>(PACKAGE_NAME) }}; for (const auto &arg : args) { c_args.push_back(const_cast<char*>(arg.data())); @@ -69,10 +74,22 @@ int AvrDude::priv::run() { return res; } +int AvrDude::priv::run() { + for (; args.size() > 0; current_args_set++) { + int res = run_one(args.front()); + args.pop_front(); + if (res != 0) { + return res; + } + } + + return 0; +} + // Public -AvrDude::AvrDude() : p(new priv()) {} +AvrDude::AvrDude(std::string sys_config) : p(new priv(std::move(sys_config))) {} AvrDude::AvrDude(AvrDude &&other) : p(std::move(other.p)) {} @@ -83,15 +100,9 @@ AvrDude::~AvrDude() } } -AvrDude& AvrDude::sys_config(std::string sys_config) +AvrDude& AvrDude::push_args(std::vector<std::string> args) { - if (p) { p->sys_config = std::move(sys_config); } - return *this; -} - -AvrDude& AvrDude::args(std::vector<std::string> args) -{ - if (p) { p->args = std::move(args); } + if (p) { p->args.push_back(std::move(args)); } return *this; } @@ -137,7 +148,7 @@ AvrDude::Ptr AvrDude::run() auto res = self->p->run(); if (self->p->complete_fn) { - self->p->complete_fn(res); + self->p->complete_fn(res, self->p->current_args_set); } }); diff --git a/xs/src/avrdude/avrdude-slic3r.hpp b/xs/src/avrdude/avrdude-slic3r.hpp index 29d96f72d..273aa2378 100644 --- a/xs/src/avrdude/avrdude-slic3r.hpp +++ b/xs/src/avrdude/avrdude-slic3r.hpp @@ -15,20 +15,20 @@ public: typedef std::function<void()> RunFn; typedef std::function<void(const char * /* msg */, unsigned /* size */)> MessageFn; typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn; - typedef std::function<void(int /* exit status */)> CompleteFn; + typedef std::function<void(int /* exit status */, size_t /* args_id */)> CompleteFn; - AvrDude(); + // Main c-tor, sys_config is the location of avrdude's main configuration file + AvrDude(std::string sys_config); AvrDude(AvrDude &&); AvrDude(const AvrDude &) = delete; AvrDude &operator=(AvrDude &&) = delete; AvrDude &operator=(const AvrDude &) = delete; ~AvrDude(); - // Set location of avrdude's main configuration file - AvrDude& sys_config(std::string sys_config); - - // Set avrdude cli arguments - AvrDude& args(std::vector<std::string> args); + // Push a set of avrdude cli arguments + // Each set makes one avrdude invocation - use this method multiple times to push + // more than one avrdude invocations. + AvrDude& push_args(std::vector<std::string> args); // Set a callback to be called just after run() before avrdude is ran // This can be used to perform any needed setup tasks from the background thread. @@ -42,7 +42,10 @@ public: // Progress is reported per each task (reading / writing) in percents. AvrDude& on_progress(ProgressFn fn); - // Called when avrdude's main function finishes + // Called when the last avrdude invocation finishes with the exit status of zero, + // or earlier, if one of the invocations return a non-zero status. + // The second argument contains the sequential id of the last avrdude invocation argument set. + // This has no effect when using run_sync(). AvrDude& on_complete(CompleteFn fn); int run_sync(); diff --git a/xs/src/avrdude/main.c b/xs/src/avrdude/main.c index 91f2fc827..d4c34fe44 100644 --- a/xs/src/avrdude/main.c +++ b/xs/src/avrdude/main.c @@ -374,7 +374,7 @@ static void list_parts(FILE * f, const char *prefix, LISTID avrparts) static int cleanup_main(int status) { - if (pgm_setup && pgm->teardown) { + if (pgm_setup && pgm != NULL && pgm->teardown) { pgm->teardown(pgm); } diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp index f9aabacc0..d74743055 100644 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp @@ -4,6 +4,7 @@ #include <algorithm> #include <boost/format.hpp> #include <boost/filesystem/path.hpp> +#include <boost/filesystem/fstream.hpp> #include <boost/log/trivial.hpp> #include <wx/app.h> @@ -92,7 +93,9 @@ struct FirmwareDialog::priv {} void find_serial_ports(); - void flashing_status(bool flashing, AvrDudeComplete complete = AC_NONE); + void flashing_start(bool flashing_l10n); + void flashing_done(AvrDudeComplete complete); + size_t hex_lang_offset(const wxString &path); void perform_upload(); void cancel(); void on_avrdude(const wxCommandEvent &evt); @@ -119,43 +122,76 @@ void FirmwareDialog::priv::find_serial_ports() } } -void FirmwareDialog::priv::flashing_status(bool value, AvrDudeComplete complete) +void FirmwareDialog::priv::flashing_start(bool flashing_l10n) { - if (value) { - txt_stdout->Clear(); - txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!"))); - txt_status->SetForegroundColour(GUI::get_label_clr_modified()); - port_picker->Disable(); - btn_rescan->Disable(); - hex_picker->Disable(); - btn_close->Disable(); - btn_flash->SetLabel(btn_flash_label_flashing); - progressbar->SetRange(200); // See progress callback below - progressbar->SetValue(0); - progress_tasks_done = 0; - cancelled = false; - timer_pulse.Start(50); - } else { - auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); - port_picker->Enable(); - btn_rescan->Enable(); - hex_picker->Enable(); - btn_close->Enable(); - btn_flash->SetLabel(btn_flash_label_ready); - txt_status->SetForegroundColour(text_color); - progressbar->SetValue(200); + txt_stdout->Clear(); + txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!"))); + txt_status->SetForegroundColour(GUI::get_label_clr_modified()); + port_picker->Disable(); + btn_rescan->Disable(); + hex_picker->Disable(); + btn_close->Disable(); + btn_flash->SetLabel(btn_flash_label_flashing); + progressbar->SetRange(flashing_l10n ? 500 : 200); // See progress callback below + progressbar->SetValue(0); + progress_tasks_done = 0; + cancelled = false; + timer_pulse.Start(50); +} - switch (complete) { - case AC_SUCCESS: txt_status->SetLabel(_(L("Flashing succeeded!"))); break; - case AC_FAILURE: txt_status->SetLabel(_(L("Flashing failed. Please see the avrdude log below."))); break; - case AC_CANCEL: txt_status->SetLabel(_(L("Flashing cancelled."))); break; +void FirmwareDialog::priv::flashing_done(AvrDudeComplete complete) +{ + auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + port_picker->Enable(); + btn_rescan->Enable(); + hex_picker->Enable(); + btn_close->Enable(); + btn_flash->SetLabel(btn_flash_label_ready); + txt_status->SetForegroundColour(text_color); + timer_pulse.Stop(); + progressbar->SetValue(progressbar->GetRange()); + + switch (complete) { + case AC_SUCCESS: txt_status->SetLabel(_(L("Flashing succeeded!"))); break; + case AC_FAILURE: txt_status->SetLabel(_(L("Flashing failed. Please see the avrdude log below."))); break; + case AC_CANCEL: txt_status->SetLabel(_(L("Flashing cancelled."))); break; + } +} + +size_t FirmwareDialog::priv::hex_lang_offset(const wxString &path) +{ + fs::ifstream file(fs::path(path.wx_str())); + if (! file.good()) { + return 0; + } + + static const char *hex_terminator = ":00000001FF\r"; + size_t res = 0; + std::string line; + while (getline(file, line, '\n').good()) { + // Account for LF vs CRLF + if (!line.empty() && line.back() != '\r') { + line.push_back('\r'); + } + + if (line == hex_terminator) { + if (res == 0) { + // This is the first terminator seen, save the position + res = file.tellg(); + } else { + // We've found another terminator, return the offset just after the first one + // which is the start of the second 'section'. + return res; + } } } + + return 0; } void FirmwareDialog::priv::perform_upload() { - auto filename = hex_picker->GetPath(); + auto filename = hex_picker->GetPath(); std::string port = port_picker->GetValue().ToStdString(); int selection = port_picker->GetSelection(); if (selection != -1) { @@ -165,25 +201,32 @@ void FirmwareDialog::priv::perform_upload() } if (filename.IsEmpty() || port.empty()) { return; } - flashing_status(true); - + const bool extra_verbose = false; // For debugging + const auto lang_offset = hex_lang_offset(filename); const auto filename_utf8 = filename.utf8_str(); + + flashing_start(lang_offset > 0); + + // It is ok here to use the q-pointer to the FirmwareDialog + // because the dialog ensures it doesn't exit before the background thread is done. + auto q = this->q; + + // Init the avrdude object + AvrDude avrdude(avrdude_config); + + // Build argument list(s) std::vector<std::string> args {{ - "-v", + extra_verbose ? "-vvvvv" : "-v", "-p", "atmega2560", // Using the "Wiring" mode to program Rambo or Einsy, using the STK500v2 protocol (not the STK500). // The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip // is flashed with a buggy firmware. -// "-c", "wiring", - // Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2). - // The Prusa's avrdude is patched again to never send semicolons inside the data packets. - "-c", "arduino", + "-c", "wiring", "-P", port, - "-b", "115200", // XXX: is this ok to hardcode? + "-b", "115200", // TODO: Allow other rates? Ditto below. "-D", - "-u", // disable safe mode - "-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(), // FIXME - // "-vvvvv", //"-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange + // XXX: Safe mode? + "-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(), }}; BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: " @@ -191,17 +234,38 @@ void FirmwareDialog::priv::perform_upload() return a + ' ' + b; }); - // It is ok here to use the q-pointer to the FirmwareDialog - // because the dialog ensures it doesn't exit before the background thread is done. - auto q = this->q; + avrdude.push_args(std::move(args)); + + if (lang_offset > 0) { + // The hex file also contains another section with l10n data to be flashed into the external flash on MK3 (Einsy) + // This is done via another avrdude invocation, here we build arg list for that: + std::vector<std::string> args_l10n {{ + extra_verbose ? "-vvvvv" : "-v", + "-p", "atmega2560", + // Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2). + // The Prusa's avrdude is patched again to never send semicolons inside the data packets. + "-c", "arduino", + "-P", port, + "-b", "115200", + "-D", + "-u", // disable safe mode + "-U", (boost::format("flash:w:%1%:%2%:i") % lang_offset % filename_utf8.data()).str(), + }}; + + BOOST_LOG_TRIVIAL(info) << "Invoking avrdude for external flash flashing, arguments: " + << std::accumulate(std::next(args_l10n.begin()), args_l10n.end(), args_l10n[0], [](std::string a, const std::string &b) { + return a + ' ' + b; + }); + + avrdude.push_args(std::move(args_l10n)); + } + + this->avrdude = avrdude + .on_message(std::move([q, extra_verbose](const char *msg, unsigned /* size */) { + if (extra_verbose) { + BOOST_LOG_TRIVIAL(debug) << "avrdude: " << msg; + } - avrdude = AvrDude() - .sys_config(avrdude_config) - .args(args) - .on_run([]() { /* TODO: needed? */ }) - .on_message(std::move([q](const char *msg, unsigned /* size */) { - // Debugging output to console, useful when avrdude is executed in a super verbose mode (with -v -v -v). - // printf("%s", msg); auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); auto wxmsg = wxString::FromUTF8(msg); evt->SetExtraLong(AE_MESSAGE); @@ -214,7 +278,7 @@ void FirmwareDialog::priv::perform_upload() evt->SetInt(progress); wxQueueEvent(q, evt); })) - .on_complete(std::move([q](int status) { + .on_complete(std::move([q](int status, size_t /* args_id */) { auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); evt->SetExtraLong(AE_EXIT); evt->SetInt(status); @@ -243,10 +307,10 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt) case AE_PROGRESS: // We try to track overall progress here. - // When uploading the firmware, avrdude first reads a littlebit of status data, - // then performs write, then reading (verification). - // We ignore the first task (which just let's the timer_pulse work) - // and then display overall progress during the latter two tasks. + // Avrdude performs 3 tasks per one memory operation ("-U" arg), + // first of which is reading of status data (very short). + // We use the timer_pulse during the very first task to indicate intialization + // and then display overall progress during the latter tasks. if (progress_tasks_done > 0) { progressbar->SetValue(progress_tasks_done - 100 + evt.GetInt()); @@ -263,7 +327,7 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt) BOOST_LOG_TRIVIAL(info) << "avrdude exit code: " << evt.GetInt(); complete_kind = cancelled ? AC_CANCEL : (evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE); - flashing_status(false, complete_kind); + flashing_done(complete_kind); // Make sure the background thread is collected and the AvrDude object reset if (avrdude) { avrdude->join(); } From 725b8524f2c922dcaa32b280d6553eaba81ae30f Mon Sep 17 00:00:00 2001 From: Vojtech Kral <vojtech@kral.hk> Date: Tue, 19 Jun 2018 15:45:30 +0200 Subject: [PATCH 17/17] avrdude: Fix error handling in arduino, fix various outputs --- xs/src/avrdude/arduino.c | 1 + xs/src/avrdude/avrpart.c | 10 +++++----- xs/src/avrdude/update.c | 1 - 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/xs/src/avrdude/arduino.c b/xs/src/avrdude/arduino.c index 5e0693e94..fc9f4571f 100644 --- a/xs/src/avrdude/arduino.c +++ b/xs/src/avrdude/arduino.c @@ -148,6 +148,7 @@ static int arduino_open(PROGRAMMER * pgm, char * port) // Initialization sequence for programming the external FLASH on the Prusa MK3 if (prusa_init_external_flash(pgm) < 0) { avrdude_message(MSG_INFO, "%s: arduino_open(): Failed to initialize MK3 external flash programming mode\n", progname); + return -1; } if (stk500_getsync(pgm) < 0) diff --git a/xs/src/avrdude/avrpart.c b/xs/src/avrdude/avrpart.c index 621a85b98..b04851ac1 100644 --- a/xs/src/avrdude/avrpart.c +++ b/xs/src/avrdude/avrpart.c @@ -378,7 +378,7 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, int type, char * optr; if (m == NULL) { - fprintf(f, + avrdude_message(MSG_INFO, "%s Block Poll Page Polled\n" "%sMemory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack\n" "%s----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------\n", @@ -386,13 +386,13 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, int type, } else { if (verbose > 2) { - fprintf(f, + avrdude_message(MSG_INFO, "%s Block Poll Page Polled\n" "%sMemory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack\n" "%s----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------\n", prefix, prefix, prefix); } - fprintf(f, + avrdude_message(MSG_INFO, "%s%-11s %4d %5d %5d %4d %-6s %6d %4d %6d %5d %5d 0x%02x 0x%02x\n", prefix, m->desc, m->mode, m->delay, m->blocksize, m->pollindex, m->paged ? "yes" : "no", @@ -415,7 +415,7 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, int type, optr = avr_op_str(i); else optr = " "; - fprintf(f, + avrdude_message(MSG_INFO, "%s %-11s %8d %8s %5d %5d\n", prefix, optr, j, bittype(m->op[i]->bit[j].type), @@ -620,7 +620,7 @@ void avr_display(FILE * f, AVRPART * p, const char * prefix, int verbose) LNODEID ln; AVRMEM * m; - fprintf(f, + avrdude_message(MSG_INFO, "%sAVR Part : %s\n" "%sChip Erase delay : %d us\n" "%sPAGEL : P%02X\n" diff --git a/xs/src/avrdude/update.c b/xs/src/avrdude/update.c index fa3372476..e9dd6e325 100644 --- a/xs/src/avrdude/update.c +++ b/xs/src/avrdude/update.c @@ -117,7 +117,6 @@ UPDATE * parse_op(char * s) } upd->offset = offset; - printf("parse_op: offset: %lu\n", offset); p++; /*