diff --git a/CMakeLists.txt b/CMakeLists.txt index 2287e8f92d..1bd6d1a6c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ set(engine_SRCS # Except main.cpp. src/communication/ArcusCommunication.cpp src/communication/ArcusCommunicationPrivate.cpp src/communication/CommandLine.cpp + src/communication/EmscriptenCommunication.cpp src/communication/Listener.cpp src/infill/ImageBasedDensityProvider.cpp @@ -270,7 +271,28 @@ endif () if (CMAKE_CXX_PLATFORM_ID STREQUAL "emscripten") message(STATUS "Building for Emscripten") - target_link_options(_CuraEngine PUBLIC -Wno-unused-command-line-argument -sINVOKE_RUN=0 -sEXPORT_NAME=CuraEngine -sEXPORTED_RUNTIME_METHODS=[callMain,FS] -sFORCE_FILESYSTEM=1 -sALLOW_MEMORY_GROWTH=1 -sEXPORT_ES6=1 -sMODULARIZE=1 -sSINGLE_FILE=1 -sENVIRONMENT=worker -sERROR_ON_UNDEFINED_SYMBOLS=0 -lembind --embind-emit-tsd CuraEngine.d.ts) + target_link_options(_CuraEngine + PUBLIC + "SHELL:-sINVOKE_RUN=0" + "SHELL:-sEXPORT_NAME=CuraEngine" + "SHELL:-sEXPORTED_RUNTIME_METHODS=[callMain,FS]" + "SHELL:-sFORCE_FILESYSTEM=1" + "SHELL:-sALLOW_MEMORY_GROWTH=1" + "SHELL:-sEXPORT_ES6=1" + "SHELL:-sMODULARIZE=1" + "SHELL:-sSINGLE_FILE=1" + "SHELL:-sENVIRONMENT=web" + "SHELL:-sERROR_ON_UNDEFINED_SYMBOLS=0" + "SHELL:-sWASM_BIGINT=1" + "SHELL:-sSTACK_SIZE=196608" + $<$:SHELL:-sASSERTIONS=2> + $<$:SHELL:-sSAFE_HEAP=1> + $<$:SHELL:-sSTACK_OVERFLOW_CHECK=2> + $<$:SHELL:-g3> + $<$:SHELL:-gsource-map> + "SHELL:-lembind" + "SHELL:--embind-emit-tsd CuraEngine.d.ts" + ) endif () target_link_libraries(CuraEngine PRIVATE diff --git a/include/Application.h b/include/Application.h index 205766f62f..15a9b94bb2 100644 --- a/include/Application.h +++ b/include/Application.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "utils/NoCopy.h" @@ -38,14 +39,14 @@ class Application : NoCopy * can assume that it is safe to access this without checking whether it is * initialised. */ - Communication* communication_ = nullptr; + std::shared_ptr communication_; /* * \brief The slice that is currently ongoing. * * If no slice has started yet, this will be a nullptr. */ - Slice* current_slice_ = nullptr; + std::shared_ptr current_slice_; /*! * \brief ThreadPool with lifetime tied to Application diff --git a/include/communication/CommandLine.h b/include/communication/CommandLine.h index 55c742871a..15286e6049 100644 --- a/include/communication/CommandLine.h +++ b/include/communication/CommandLine.h @@ -27,6 +27,8 @@ using container_setting_map = std::unordered_map; class CommandLine : public Communication { public: + CommandLine() = default; + /* * \brief Construct a new communicator that interprets the command line to * start a slice. @@ -155,18 +157,15 @@ class CommandLine : public Communication */ void sliceNext() override; -private: -#ifdef __EMSCRIPTEN__ - std::string progressHandler; -#endif - - std::vector search_directories_; - +protected: /* * \brief The command line arguments that the application was called with. */ std::vector arguments_; +private: + std::vector search_directories_; + /* * The last progress update that we output to stdcerr. */ diff --git a/include/communication/EmscriptenCommunication.h b/include/communication/EmscriptenCommunication.h new file mode 100644 index 0000000000..5144b96dc6 --- /dev/null +++ b/include/communication/EmscriptenCommunication.h @@ -0,0 +1,65 @@ +// Copyright (c) 2024 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifndef EMSCRIPTENCOMMUNICATION_H +#define EMSCRIPTENCOMMUNICATION_H +#ifdef __EMSCRIPTEN__ + +#include "communication/CommandLine.h" + +namespace cura +{ + +/** + * \class EmscriptenCommunication + * \brief A class for handling communication in an Emscripten environment. + * + * This class extends the CommandLine class and provides specific implementations + * for sending progress and handling slice information in an Emscripten environment. + */ +class EmscriptenCommunication : public CommandLine +{ +private: + std::string progress_handler_; ///< Handler for progress messages. + std::string gcode_header_handler_; ///< Handler for getting the GCode handler. + std::string slice_info_handler_; ///< Handler for slice information messages. + + /** + * \brief Creates a message containing slice information. + * \return A string containing the slice information message. + */ + [[nodiscard]] static std::string createSliceInfoMessage(); + +public: + /** + * \brief Constructor for EmscriptenCommunication. + * \param arguments A vector of strings containing the command line arguments. + */ + EmscriptenCommunication(const std::vector& arguments); + + /** + * \brief Sends the progress of the current operation. + * \param progress A double representing the progress percentage. + */ + void sendProgress(double progress) const override; + + /** + * \brief Sends GcodeHeader + */ + void sendGCodePrefix(const std::string& prefix) const override; + + /** + * \brief Initiates the slicing of the next item. + */ + void sliceNext() override; + + bool isSequential() const override + { + return false; + } +}; + +} // namespace cura + +#endif // __EMSCRIPTEN__ +#endif // EMSCRIPTENCOMMUNICATION_H diff --git a/include/utils/string.h b/include/utils/string.h index 7468077279..f21bd66524 100644 --- a/include/utils/string.h +++ b/include/utils/string.h @@ -9,6 +9,9 @@ #include #include // ostringstream +#include +#include +#include #include namespace cura @@ -27,6 +30,30 @@ static inline int stringcasecompare(const char* a, const char* b) return *a - *b; } +// Convert string to base64 string. +// This function is useful to forward string through javascript even if they contain any special strings +// +[[maybe_unused]] static std::string convertTobase64(const std::string& input) +{ + using namespace boost::archive::iterators; + // prepare the stream to hold the encoded data + std::stringstream output; + + // encode data + typedef base64_from_binary> base64_enc; + std::copy(base64_enc(input.begin()), base64_enc(input.end()), ostream_iterator(output)); + + // Retrieve the encoded string + std::string output_encoded = output.str(); + + // ensure padding if needed + size_t num = (3 - input.length() % 3) % 3; + for (size_t i = 0; i < num; i++) + { + output_encoded.push_back('='); + } + return output_encoded; +} /*! * Efficient conversion of micron integer type to millimeter string. * diff --git a/src/Application.cpp b/src/Application.cpp index a2a271f617..319c55e544 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -18,8 +18,10 @@ #include #include +#include "Slice.h" #include "communication/ArcusCommunication.h" //To connect via Arcus to the front-end. #include "communication/CommandLine.h" //To use the command line to slice stuff. +#include "communication/EmscriptenCommunication.h" // To use Emscripten to slice stuff. #include "progress/Progress.h" #include "utils/ThreadPool.h" #include "utils/string.h" //For stringcasecompare. @@ -45,7 +47,6 @@ Application::Application() Application::~Application() { - delete communication_; delete thread_pool_; } @@ -100,7 +101,7 @@ void Application::connect() } } - ArcusCommunication* arcus_communication = new ArcusCommunication(); + auto arcus_communication = std::make_shared(); arcus_communication->connect(ip, port); communication_ = arcus_communication; } @@ -214,8 +215,11 @@ void Application::slice() { arguments.emplace_back(argv_[argument_index]); } - - communication_ = new CommandLine(arguments); +#ifdef __EMSCRIPTEN__ + communication_ = std::make_shared(arguments); +#else + communication_ = std::make_shared(arguments); +#endif } void Application::run(const size_t argc, char** argv) diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index ce654d76ec..cd46b5e850 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -1982,7 +1982,7 @@ void LayerPlan::processFanSpeedAndMinimalLayerTime(Point2LL starting_position) void LayerPlan::writeGCode(GCodeExport& gcode) { - Communication* communication = Application::getInstance().communication_; + auto communication = Application::getInstance().communication_; communication->setLayerForSend(layer_nr_); communication->sendCurrentPosition(gcode.getPositionXY()); gcode.setLayerNr(layer_nr_); @@ -2544,7 +2544,7 @@ bool LayerPlan::writePathWithCoasting( Point2LL prev_pt = gcode.getPositionXY(); { // write normal extrude path: - Communication* communication = Application::getInstance().communication_; + auto communication = Application::getInstance().communication_; for (size_t point_idx = 0; point_idx <= point_idx_before_start; point_idx++) { auto [_, time] = extruder_plan.getPointToPointTime(prev_pt, path.points[point_idx], path); diff --git a/src/communication/ArcusCommunication.cpp b/src/communication/ArcusCommunication.cpp index 0f0fd4e6d3..27e0e19b51 100644 --- a/src/communication/ArcusCommunication.cpp +++ b/src/communication/ArcusCommunication.cpp @@ -520,7 +520,7 @@ void ArcusCommunication::sliceNext() // Handle the main Slice message. const cura::proto::Slice* slice_message = dynamic_cast(message.get()); // See if the message is of the message type Slice. Returns nullptr otherwise. - if (! slice_message) + if (slice_message == nullptr) { return; } @@ -553,15 +553,15 @@ void ArcusCommunication::sliceNext() } #endif // ENABLE_PLUGINS - Slice slice(slice_message->object_lists().size()); - Application::getInstance().current_slice_ = &slice; + auto slice = std::make_shared(slice_message->object_lists().size()); + Application::getInstance().current_slice_ = slice; private_data->readGlobalSettingsMessage(slice_message->global_settings()); private_data->readExtruderSettingsMessage(slice_message->extruders()); // Broadcast the settings to the plugins slots::instance().broadcast(*slice_message); - const size_t extruder_count = slice.scene.extruders.size(); + const size_t extruder_count = slice->scene.extruders.size(); // For each setting, register what extruder it should be obtained from (if this is limited to an extruder). for (const cura::proto::SettingExtruder& setting_extruder : slice_message->limit_to_extruder()) @@ -572,8 +572,8 @@ void ArcusCommunication::sliceNext() // If it's -1 it should be ignored as per the spec. Let's also ignore it if it's beyond range. continue; } - ExtruderTrain& extruder = slice.scene.extruders[setting_extruder.extruder()]; - slice.scene.limit_to_extruder.emplace(setting_extruder.name(), &extruder); + ExtruderTrain& extruder = slice->scene.extruders[setting_extruder.extruder()]; + slice->scene.limit_to_extruder.emplace(setting_extruder.name(), &extruder); } // Load all mesh groups, meshes and their settings. @@ -584,9 +584,9 @@ void ArcusCommunication::sliceNext() } spdlog::debug("Done reading Slice message."); - if (! slice.scene.mesh_groups.empty()) + if (! slice->scene.mesh_groups.empty()) { - slice.compute(); + slice->compute(); FffProcessor::getInstance()->finalize(); flushGCode(); sendPrintTimeMaterialEstimates(); diff --git a/src/communication/ArcusCommunicationPrivate.cpp b/src/communication/ArcusCommunicationPrivate.cpp index 81101dc9b2..44621918ca 100644 --- a/src/communication/ArcusCommunicationPrivate.cpp +++ b/src/communication/ArcusCommunicationPrivate.cpp @@ -47,7 +47,7 @@ std::shared_ptr ArcusCommunication::Private::getOptimized void ArcusCommunication::Private::readGlobalSettingsMessage(const proto::SettingList& global_settings_message) { - Slice* slice = Application::getInstance().current_slice_; + auto slice = Application::getInstance().current_slice_; for (const cura::proto::Setting& setting_message : global_settings_message.settings()) { slice->scene.settings.add(setting_message.name(), setting_message.value()); @@ -57,7 +57,7 @@ void ArcusCommunication::Private::readGlobalSettingsMessage(const proto::Setting void ArcusCommunication::Private::readExtruderSettingsMessage(const google::protobuf::RepeatedPtrField& extruder_messages) { // Make sure we have enough extruders added currently. - Slice* slice = Application::getInstance().current_slice_; + auto slice = Application::getInstance().current_slice_; const size_t extruder_count = slice->scene.settings.get("machine_extruder_count"); for (size_t extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++) { diff --git a/src/communication/CommandLine.cpp b/src/communication/CommandLine.cpp index 12e04adc13..80db95519e 100644 --- a/src/communication/CommandLine.cpp +++ b/src/communication/CommandLine.cpp @@ -32,10 +32,6 @@ #include "utils/format/filesystem_path.h" #include "utils/views/split_paths.h" -#ifdef __EMSCRIPTEN__ -#include -#endif - namespace cura { @@ -124,13 +120,6 @@ void CommandLine::sendProgress(double progress) const { return; } - // TODO: Do we want to print a progress bar? We'd need a better solution to not have that progress bar be ruined by any logging. -#ifdef __EMSCRIPTEN__ - // Call progress handler with progress - char js[100]; - std::sprintf(js, "globalThis[\"%s\"](%f)", progressHandler.c_str(), progress); - emscripten_run_script(js); -#endif } void CommandLine::sliceNext() @@ -146,16 +135,16 @@ void CommandLine::sliceNext() num_mesh_groups++; } } - Slice slice(num_mesh_groups); - Application::getInstance().current_slice_ = &slice; + Application::getInstance().current_slice_ = std::make_shared(num_mesh_groups); + auto slice = Application::getInstance().current_slice_; size_t mesh_group_index = 0; - Settings* last_settings = &slice.scene.settings; + Settings* last_settings = &slice->scene.settings; - slice.scene.extruders.reserve(arguments_.size() >> 1); // Allocate enough memory to prevent moves. - slice.scene.extruders.emplace_back(0, &slice.scene.settings); // Always have one extruder. - ExtruderTrain* last_extruder = slice.scene.extruders.data(); + slice->scene.extruders.reserve(arguments_.size() >> 1); // Allocate enough memory to prevent moves. + slice->scene.extruders.emplace_back(0, &slice->scene.settings); // Always have one extruder. + ExtruderTrain* last_extruder = slice->scene.extruders.data(); bool force_read_parent = false; bool force_read_nondefault = false; @@ -175,7 +164,7 @@ void CommandLine::sliceNext() mesh_group_index++; FffProcessor::getInstance()->time_keeper.restart(); - last_settings = &slice.scene.mesh_groups[mesh_group_index].settings; + last_settings = &slice->scene.mesh_groups[mesh_group_index].settings; } catch (...) { @@ -203,15 +192,12 @@ void CommandLine::sliceNext() force_read_parent = false; force_read_nondefault = false; } -#ifdef __EMSCRIPTEN__ - else if (argument.find("--progress") == 0) + else if (argument.starts_with("--progress_cb") || argument.starts_with("--slice_info_cb") || argument.starts_with("--gcode_header_cb")) { - // Store progress handler name + // Unused in command line slicing, but used in EmscriptenCommunication. argument_index++; argument = arguments_[argument_index]; - progressHandler = argument; } -#endif else { spdlog::error("Unknown option: {}", argument); @@ -266,12 +252,12 @@ void CommandLine::sliceNext() } // If this was the global stack, create extruders for the machine_extruder_count setting. - if (last_settings == &slice.scene.settings) + if (last_settings == &slice->scene.settings) { - const auto extruder_count = slice.scene.settings.get("machine_extruder_count"); - while (slice.scene.extruders.size() < extruder_count) + const auto extruder_count = slice->scene.settings.get("machine_extruder_count"); + while (slice->scene.extruders.size() < extruder_count) { - slice.scene.extruders.emplace_back(slice.scene.extruders.size(), &slice.scene.settings); + slice->scene.extruders.emplace_back(slice->scene.extruders.size(), &slice->scene.settings); } } // If this was an extruder stack, make sure that the extruder_nr setting is correct. @@ -284,13 +270,13 @@ void CommandLine::sliceNext() case 'e': { size_t extruder_nr = stoul(argument.substr(2)); - while (slice.scene.extruders.size() <= extruder_nr) // Make sure we have enough extruders up to the extruder_nr that the user wanted. + while (slice->scene.extruders.size() <= extruder_nr) // Make sure we have enough extruders up to the extruder_nr that the user wanted. { - slice.scene.extruders.emplace_back(extruder_nr, &slice.scene.settings); + slice->scene.extruders.emplace_back(extruder_nr, &slice->scene.settings); } - last_settings = &slice.scene.extruders[extruder_nr].settings_; + last_settings = &slice->scene.extruders[extruder_nr].settings_; last_settings->add("extruder_nr", argument.substr(2)); - last_extruder = &slice.scene.extruders[extruder_nr]; + last_extruder = &slice->scene.extruders[extruder_nr]; break; } case 'l': @@ -305,14 +291,14 @@ void CommandLine::sliceNext() const auto transformation = last_settings->get("mesh_rotation_matrix"); // The transformation applied to the model when loaded. - if (! loadMeshIntoMeshGroup(&slice.scene.mesh_groups[mesh_group_index], argument.c_str(), transformation, last_extruder->settings_)) + if (! loadMeshIntoMeshGroup(&slice->scene.mesh_groups[mesh_group_index], argument.c_str(), transformation, last_extruder->settings_)) { spdlog::error("Failed to load model: {}. (error number {})", argument, errno); exit(1); } else { - last_settings = &slice.scene.mesh_groups[mesh_group_index].meshes.back().settings_; + last_settings = &slice->scene.mesh_groups[mesh_group_index].meshes.back().settings_; } break; } @@ -334,7 +320,7 @@ void CommandLine::sliceNext() } case 'g': { - last_settings = &slice.scene.mesh_groups[mesh_group_index].settings; + last_settings = &slice->scene.mesh_groups[mesh_group_index].settings; break; } /* ... falls through ... */ @@ -432,49 +418,45 @@ void CommandLine::sliceNext() for (const auto& [setting_key, setting_value] : global_settings) { - slice.scene.settings.add(setting_key, setting_value); + slice->scene.settings.add(setting_key, setting_value); } for (const auto& [key, values] : extruder_settings) { const auto extruder_nr = std::stoi(key.substr(extruder_identifier.size())); - while (slice.scene.extruders.size() <= static_cast(extruder_nr)) + while (slice->scene.extruders.size() <= static_cast(extruder_nr)) { - slice.scene.extruders.emplace_back(extruder_nr, &slice.scene.settings); + slice->scene.extruders.emplace_back(extruder_nr, &slice->scene.settings); } for (const auto& [setting_key, setting_value] : values) { - slice.scene.extruders[extruder_nr].settings_.add(setting_key, setting_value); + slice->scene.extruders[extruder_nr].settings_.add(setting_key, setting_value); } } for (const auto& [key, values] : model_settings) { const auto& model_name = key; - - cura::MeshGroup mesh_group; for (const auto& [setting_key, setting_value] : values) { - mesh_group.settings.add(setting_key, setting_value); + slice->scene.mesh_groups[mesh_group_index].settings.add(setting_key, setting_value); } - const auto transformation = mesh_group.settings.get("mesh_rotation_matrix"); - const auto extruder_nr = mesh_group.settings.get("extruder_nr"); + const auto transformation = slice->scene.mesh_groups[mesh_group_index].settings.get("mesh_rotation_matrix"); + const auto extruder_nr = slice->scene.mesh_groups[mesh_group_index].settings.get("extruder_nr"); - if (! loadMeshIntoMeshGroup(&mesh_group, model_name.c_str(), transformation, slice.scene.extruders[extruder_nr].settings_)) + if (! loadMeshIntoMeshGroup(&slice->scene.mesh_groups[mesh_group_index], model_name.c_str(), transformation, slice->scene.extruders[extruder_nr].settings_)) { spdlog::error("Failed to load model: {}. (error number {})", model_name, errno); exit(1); } - - slice.scene.mesh_groups.push_back(std::move(mesh_group)); } for (const auto& [key, value] : limit_to_extruder) { const auto extruder_nr = std::stoi(value.substr(extruder_identifier.size())); if (extruder_nr >= 0) { - slice.scene.limit_to_extruder[key] = &slice.scene.extruders[extruder_nr]; + slice->scene.limit_to_extruder[key] = &slice->scene.extruders[extruder_nr]; } } @@ -505,11 +487,11 @@ void CommandLine::sliceNext() try { #endif // DEBUG - slice.scene.mesh_groups[mesh_group_index].finalize(); + slice->scene.mesh_groups[mesh_group_index].finalize(); spdlog::info("Loaded from disk in {:3}s\n", FffProcessor::getInstance()->time_keeper.restart()); // Start slicing. - slice.compute(); + slice->compute(); #ifndef DEBUG } catch (...) diff --git a/src/communication/EmscriptenCommunication.cpp b/src/communication/EmscriptenCommunication.cpp new file mode 100644 index 0000000000..8155290ee9 --- /dev/null +++ b/src/communication/EmscriptenCommunication.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2024 UltiMaker +// CuraEngine is released under the terms of the AGPLv3 or higher + +#ifdef __EMSCRIPTEN__ +#include "communication/EmscriptenCommunication.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Application.h" +#include "FffProcessor.h" +#include "Slice.h" +#include "utils/string.h" + +namespace cura +{ + +EmscriptenCommunication::EmscriptenCommunication(const std::vector& arguments) + : CommandLine(arguments) +{ + spdlog::info("Emscripten communication initialized"); + if (auto progress_flag = ranges::find(arguments_, "--progress_cb"); progress_flag != arguments_.end()) + { + progress_handler_ = *ranges::next(progress_flag); + } + if (auto slice_info_flag = ranges::find(arguments_, "--slice_info_cb"); slice_info_flag != arguments_.end()) + { + slice_info_handler_ = *ranges::next(slice_info_flag); + } + if (auto gcode_header_flag = ranges::find(arguments_, "--gcode_header_cb"); gcode_header_flag != arguments_.end()) + { + gcode_header_handler_ = *ranges::next(gcode_header_flag); + } +} + +void EmscriptenCommunication::sendGCodePrefix(const std::string& prefix) const +{ + emscripten_run_script(fmt::format("globalThis[\"{}\"](\"{}\")", gcode_header_handler_, convertTobase64(prefix)).c_str()); +} + +void EmscriptenCommunication::sendProgress(double progress) const +{ + emscripten_run_script(fmt::format("globalThis[\"{}\"]({})", progress_handler_, progress).c_str()); +} + +std::string EmscriptenCommunication::createSliceInfoMessage() +{ + // Construct a string with rapidjson containing the slice information + rapidjson::Document doc; + auto& allocator = doc.GetAllocator(); + doc.SetObject(); + + // Set the slice UUID + rapidjson::Value slice_uuid("slice_uuid", allocator); + rapidjson::Value uuid(Application::getInstance().instance_uuid_.c_str(), allocator); + doc.AddMember(slice_uuid, uuid, allocator); + + // Set the time estimates + rapidjson::Value time_estimates_json(rapidjson::kObjectType); + auto time_estimates = FffProcessor::getInstance()->getTotalPrintTimePerFeature(); + for (const auto& [feature, duration_idx] : std::vector>{ { "infill", PrintFeatureType::Infill }, + { "skin", PrintFeatureType::Skin }, + { "support", PrintFeatureType::Support }, + { "inner_wall", PrintFeatureType::InnerWall }, + { "move_combing", PrintFeatureType::MoveCombing }, + { "move_retraction", PrintFeatureType::MoveRetraction }, + { "outer_wall", PrintFeatureType::OuterWall }, + { "prime_tower", PrintFeatureType::PrimeTower }, + { "skirt_brim", PrintFeatureType::SkirtBrim }, + { "support_infill", PrintFeatureType::SupportInfill }, + { "support_interface", PrintFeatureType::SupportInterface } }) + { + rapidjson::Value feature_time(feature.c_str(), allocator); + rapidjson::Value feature_duration(time_estimates[static_cast(duration_idx)]); + time_estimates_json.AddMember(feature_time, feature_duration, allocator); + } + doc.AddMember("time_estimates", time_estimates_json, allocator); + + // Set the material estimates + rapidjson::Value material_estimates_json(rapidjson::kObjectType); + const Scene& scene = Application::getInstance().current_slice_->scene; + + for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice_->scene.extruders.size(); extruder_nr++) + { + const double value = FffProcessor::getInstance()->getTotalFilamentUsed(static_cast(extruder_nr)); + spdlog::info("Extruder {} used {} [mm] of filament", extruder_nr, value); + rapidjson::Value extruder_id(fmt::format("{}", extruder_nr).c_str(), allocator); + rapidjson::Value extruder_material_estimate(value); + material_estimates_json.AddMember(extruder_id, extruder_material_estimate, allocator); + } + doc.AddMember("material_estimates", material_estimates_json, allocator); + + // Set CuraEngine information + rapidjson::Value slicer_info_json(rapidjson::kObjectType); + rapidjson::Value slicer_version(CURA_ENGINE_VERSION, allocator); + doc.AddMember("slicer_info", slicer_info_json, allocator); + + // Serialize the JSON document to a string + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + doc.Accept(writer); + return buffer.GetString(); +} + +void EmscriptenCommunication::sliceNext() +{ + CommandLine::sliceNext(); + auto slice_info = createSliceInfoMessage(); + emscripten_run_script(fmt::format("globalThis[\"{}\"]({})", slice_info_handler_, slice_info).c_str()); +}; + +} // namespace cura + +#endif // __EMSCRIPTEN__ diff --git a/tests/FffGcodeWriterTest.cpp b/tests/FffGcodeWriterTest.cpp index 87b20cb64e..a3c52d6509 100644 --- a/tests/FffGcodeWriterTest.cpp +++ b/tests/FffGcodeWriterTest.cpp @@ -54,12 +54,12 @@ class FffGcodeWriterTest : public testing::Test inner_square.back().emplace_back(MM2INT(60), MM2INT(60)); inner_square.back().emplace_back(MM2INT(10), MM2INT(60)); - Application::getInstance().communication_ = new MockCommunication(); + Application::getInstance().communication_ = std::make_shared(); } SliceDataStorage* setUpStorage() { - Application::getInstance().current_slice_ = new Slice(1); + Application::getInstance().current_slice_ = std::make_shared(1); // Define all settings in the mesh group. The extruder train and model settings will fall back on that then. settings = &Application::getInstance().current_slice_->scene.settings; @@ -100,7 +100,7 @@ TEST_F(FffGcodeWriterTest, SurfaceGetsExtraInfillLinesUnderIt) { // SETUP SliceDataStorage* storage = setUpStorage(); - + // Set the fan speed layer time settings (since the LayerPlan constructor copies these). FanSpeedLayerTimeSettings fan_settings; fan_settings.cool_min_layer_time = settings->get("cool_min_layer_time"); @@ -110,7 +110,7 @@ TEST_F(FffGcodeWriterTest, SurfaceGetsExtraInfillLinesUnderIt) fan_settings.cool_fan_speed_max = settings->get("cool_fan_speed_max"); fan_settings.cool_min_speed = settings->get("cool_min_speed"); fan_settings.cool_fan_full_layer = settings->get("cool_fan_full_layer"); - + Mesh mesh(*settings); LayerPlan gcode_layer(*storage, 100, 10000, 100, 0, {fan_settings}, 20, 10, 5000 ); @@ -165,7 +165,7 @@ TEST_F(FffGcodeWriterTest, SurfaceGetsExtraInfillLinesUnderIt) } return false; }; - + // Check the results for (auto poly:inner_square) for (auto point:poly) diff --git a/tests/GCodeExportTest.cpp b/tests/GCodeExportTest.cpp index e2d7ef15e0..aa5d0620b0 100644 --- a/tests/GCodeExportTest.cpp +++ b/tests/GCodeExportTest.cpp @@ -38,7 +38,7 @@ class GCodeExportTest : public testing::Test * Mock away the communication channel where layer data is output by this * class. */ - MockCommunication* mock_communication; + std::shared_ptr mock_communication; void SetUp() override { @@ -68,15 +68,13 @@ class GCodeExportTest : public testing::Test gcode.machine_name_ = "Your favourite 3D printer"; // Set up a scene so that we may request settings. - Application::getInstance().current_slice_ = new Slice(1); - mock_communication = new MockCommunication(); + Application::getInstance().current_slice_ = std::make_shared(1); + mock_communication = std::make_shared(); Application::getInstance().communication_ = mock_communication; } void TearDown() override { - delete Application::getInstance().current_slice_; - delete Application::getInstance().communication_; Application::getInstance().communication_ = nullptr; } }; @@ -224,12 +222,7 @@ class GriffinHeaderTest : public testing::TestWithParam gcode.machine_name_ = "Your favourite 3D printer"; // Set up a scene so that we may request settings. - Application::getInstance().current_slice_ = new Slice(0); - } - - void TearDown() override - { - delete Application::getInstance().current_slice_; + Application::getInstance().current_slice_ = std::make_shared(0); } }; // NOLINTEND(misc-non-private-member-variables-in-classes) diff --git a/tests/LayerPlanTest.cpp b/tests/LayerPlanTest.cpp index da6a783644..112f3d457f 100644 --- a/tests/LayerPlanTest.cpp +++ b/tests/LayerPlanTest.cpp @@ -80,7 +80,7 @@ class LayerPlanTest : public testing::Test SliceDataStorage* setUpStorage() { constexpr size_t num_mesh_groups = 1; - Application::getInstance().current_slice_ = new Slice(num_mesh_groups); + Application::getInstance().current_slice_ = std::make_shared(num_mesh_groups); // Define all settings in the mesh group. The extruder train and model settings will fall back on that then. settings = &Application::getInstance().current_slice_->scene.current_mesh_group->settings; @@ -228,7 +228,6 @@ class LayerPlanTest : public testing::Test void TearDown() override { delete storage; - delete Application::getInstance().current_slice_; } }; diff --git a/tests/arcus/ArcusCommunicationPrivateTest.cpp b/tests/arcus/ArcusCommunicationPrivateTest.cpp index 12652958ca..40b651250a 100644 --- a/tests/arcus/ArcusCommunicationPrivateTest.cpp +++ b/tests/arcus/ArcusCommunicationPrivateTest.cpp @@ -34,15 +34,13 @@ class ArcusCommunicationPrivateTest : public testing::Test { instance = new ArcusCommunication::Private(); instance->socket = new MockSocket(); - Application::getInstance().current_slice_ = new Slice(GK_TEST_NUM_MESH_GROUPS); + Application::getInstance().current_slice_ = std::make_shared(GK_TEST_NUM_MESH_GROUPS); } void TearDown() override { delete instance->socket; delete instance; - - delete Application::getInstance().current_slice_; } /* diff --git a/tests/arcus/ArcusCommunicationTest.cpp b/tests/arcus/ArcusCommunicationTest.cpp index a05b88d98d..3c0eb20d37 100644 --- a/tests/arcus/ArcusCommunicationTest.cpp +++ b/tests/arcus/ArcusCommunicationTest.cpp @@ -14,6 +14,7 @@ #include "geometry/Shape.h" #include "settings/types/LayerIndex.h" #include "utils/Coord_t.h" +#include "utils/string.h" // NOLINTBEGIN(*-magic-numbers) namespace cura @@ -123,15 +124,16 @@ TEST_F(ArcusCommunicationTest, HasSlice) TEST_F(ArcusCommunicationTest, SendGCodePrefix) { - const std::string& prefix = "bladibla"; + const std::string prefix = ";bladiblhjvouyvu\n;iuboua"; + const std::string& encoded_prefix = convertTobase64(prefix); - ac->sendGCodePrefix(prefix); + ac->sendGCodePrefix(encoded_prefix); ac->flushGCode(); EXPECT_GT(socket->sent_messages.size(), 0); bool found_prefix = false; for (const auto& message : socket->sent_messages) { - if (message->DebugString().find(prefix) != std::string::npos) + if (message->DebugString().find(encoded_prefix) != std::string::npos) { found_prefix = true; break; diff --git a/tests/integration/SlicePhaseTest.cpp b/tests/integration/SlicePhaseTest.cpp index cfb9557024..763873bcd8 100644 --- a/tests/integration/SlicePhaseTest.cpp +++ b/tests/integration/SlicePhaseTest.cpp @@ -31,7 +31,7 @@ class SlicePhaseTest : public testing::Test Application::getInstance().startThreadPool(); // Set up a scene so that we may request settings. - Application::getInstance().current_slice_ = new Slice(1); + Application::getInstance().current_slice_ = std::make_shared(1); // And a few settings that we want to default. Scene& scene = Application::getInstance().current_slice_->scene; diff --git a/tests/settings/SettingsTest.cpp b/tests/settings/SettingsTest.cpp index 0c6c98f458..59a60a3528 100644 --- a/tests/settings/SettingsTest.cpp +++ b/tests/settings/SettingsTest.cpp @@ -87,8 +87,8 @@ class Slice; // Forward declaration to save some time compiling. TEST_F(SettingsTest, AddSettingExtruderTrain) { // Add a slice with some extruder trains. - std::shared_ptr current_slice = std::make_shared(0); - Application::getInstance().current_slice_ = current_slice.get(); + auto current_slice = std::make_shared(0); + Application::getInstance().current_slice_ = current_slice; current_slice->scene.extruders.emplace_back(0, nullptr); current_slice->scene.extruders.emplace_back(1, nullptr); current_slice->scene.extruders.emplace_back(2, nullptr); @@ -223,7 +223,7 @@ TEST_F(SettingsTest, OverwriteSetting) TEST_F(SettingsTest, Inheritance) { std::shared_ptr current_slice = std::make_shared(0); - Application::getInstance().current_slice_ = current_slice.get(); + Application::getInstance().current_slice_ = current_slice; const std::string value = "To be frank, I'd have to change my name."; Settings parent; @@ -240,7 +240,7 @@ TEST_F(SettingsTest, Inheritance) TEST_F(SettingsTest, LimitToExtruder) { std::shared_ptr current_slice = std::make_shared(0); - Application::getInstance().current_slice_ = current_slice.get(); + Application::getInstance().current_slice_ = current_slice; current_slice->scene.extruders.emplace_back(0, nullptr); current_slice->scene.extruders.emplace_back(1, nullptr); current_slice->scene.extruders.emplace_back(2, nullptr);