diff options
author | FormerLurker <hochgebe@gmail.com> | 2020-05-02 18:06:40 +0300 |
---|---|---|
committer | FormerLurker <hochgebe@gmail.com> | 2020-05-02 18:06:40 +0300 |
commit | 8d7b572cc7fa7faf795db88a29340b6380090f8f (patch) | |
tree | 5c5b2fb251af89e0c9038be84236c6df85d098fb | |
parent | 4fcf89d4995921b89b579d06052df11b66e4879f (diff) |
Add enhanced progress and complete statistics for arc welder. Add ARC_SEGMENTS_PER_SEC to inverse processor.
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | ArcWelder/arc_welder.cpp | 78 | ||||
-rw-r--r-- | ArcWelder/arc_welder.h | 60 | ||||
-rw-r--r-- | ArcWelderConsole/ArcWelderConsole.cpp | 10 | ||||
-rw-r--r-- | ArcWelderConsole/ArcWelderConsole.h | 3 | ||||
-rw-r--r-- | ArcWelderInverseProcessor/ArcWelderInverseProcessor.cpp | 2 | ||||
-rw-r--r-- | ArcWelderInverseProcessor/ArcWelderInverseProcessor.h | 3 | ||||
-rw-r--r-- | ArcWelderInverseProcessor/inverse_processor.cpp | 46 | ||||
-rw-r--r-- | ArcWelderInverseProcessor/inverse_processor.h | 17 | ||||
-rw-r--r-- | ArcWelderLib.sln (renamed from ArcWelder.sln) | 0 | ||||
-rw-r--r-- | ArcWelderTest/ArcWelderTest.cpp | 7 | ||||
-rw-r--r-- | ArcWelderTest/ArcWelderTest.h | 3 | ||||
-rw-r--r-- | GcodeProcessorLib/utilities.h | 4 | ||||
-rw-r--r-- | PyArcWelder/PyArcWelder.vcxproj | 14 | ||||
-rw-r--r-- | PyArcWelder/py_arc_welder.cpp | 69 | ||||
-rw-r--r-- | PyArcWelder/py_arc_welder.h | 3 | ||||
-rw-r--r-- | PyArcWelder/py_arc_welder_extension.cpp | 63 | ||||
-rw-r--r-- | PyArcWelder/py_arc_welder_extension.h | 4 | ||||
-rw-r--r-- | PyArcWelder/py_logger.cpp | 14 |
19 files changed, 289 insertions, 115 deletions
@@ -337,4 +337,6 @@ ASALocalRun/ .localhistory/ # BeatPulse healthcheck temp database -healthchecksdb
\ No newline at end of file +healthchecksdb +/DeployCodeToPlugin.bat +/DeployCodeToPlugin_Exclude.txt diff --git a/ArcWelder/arc_welder.cpp b/ArcWelder/arc_welder.cpp index 47a61f2..458b8e5 100644 --- a/ArcWelder/arc_welder.cpp +++ b/ArcWelder/arc_welder.cpp @@ -173,8 +173,10 @@ double arc_welder::get_time_elapsed(double start_clock, double end_clock) return static_cast<double>(end_clock - start_clock) / CLOCKS_PER_SEC; } -void arc_welder::process() +arc_welder_results arc_welder::process() { + arc_welder_results results; + verbose_logging_enabled_ = p_logger_->is_log_level_enabled(logger_type_, VERBOSE); debug_logging_enabled_ = p_logger_->is_log_level_enabled(logger_type_, DEBUG); info_logging_enabled_ = p_logger_->is_log_level_enabled(logger_type_, INFO); @@ -239,14 +241,20 @@ void arc_welder::process() { if ((lines_processed_ % read_lines_before_clock_check) == 0 && next_update_time < clock()) { - long file_position = static_cast<long>(gcodeFile.tellg()); + arc_welder_progress progress; + progress.gcodes_processed = gcodes_processed_; + progress.lines_processed = lines_processed_; + progress.points_compressed = points_compressed_; + progress.arcs_created = arcs_created_; + progress.target_file_size = static_cast<long>(output_file_.tellp()); + progress.source_file_size = static_cast<long>(gcodeFile.tellg()); // ToDo: tellg does not do what I think it does, but why? - long bytesRemaining = file_size_ - file_position; - double percentProgress = static_cast<double>(file_position) / static_cast<double>(file_size_) * 100.0; - double secondsElapsed = get_time_elapsed(start_clock, clock()); - double bytesPerSecond = static_cast<double>(file_position) / secondsElapsed; - double secondsToComplete = bytesRemaining / bytesPerSecond; - continue_processing = on_progress_(percentProgress, secondsElapsed, secondsToComplete, gcodes_processed_, lines_processed_, points_compressed_, arcs_created_); + long bytesRemaining = file_size_ - progress.source_file_size; + progress.percent_complete = static_cast<double>(progress.source_file_size) / static_cast<double>(file_size_) * 100.0; + progress.seconds_elapsed = get_time_elapsed(start_clock, clock()); + double bytesPerSecond = static_cast<double>(progress.source_file_size) / progress.seconds_elapsed; + progress.seconds_remaining = bytesRemaining / bytesPerSecond; + continue_processing = on_progress_(progress); next_update_time = get_next_update_time(); } } @@ -259,34 +267,48 @@ void arc_welder::process() write_unwritten_gcodes_to_file(); output_file_.close(); + results.success = continue_processing; + results.cancelled = !continue_processing; + results.progress.target_file_size = get_file_size(target_path_); + } else { - p_logger_->log_exception(logger_type_, "Unable to open the output file for writing."); + results.success = false; + results.message = "Unable to open the target file for writing."; + p_logger_->log_exception(logger_type_, results.message); } gcodeFile.close(); } else { - p_logger_->log_exception(logger_type_, "Unable to open the gcode file for processing."); + results.success = false; + results.message = "Unable to open the input file for processing."; + p_logger_->log_exception(logger_type_, results.message); } + const clock_t end_clock = clock(); const double total_seconds = static_cast<double>(end_clock - start_clock) / CLOCKS_PER_SEC; - on_progress_(100, total_seconds, 0, gcodes_processed_, lines_processed_, points_compressed_, arcs_created_); + results.progress.seconds_elapsed = total_seconds; + results.progress.gcodes_processed = gcodes_processed_; + results.progress.lines_processed = lines_processed_; + results.progress.points_compressed = points_compressed_; + results.progress.arcs_created = arcs_created_; + results.progress.source_file_size = file_size_; + return results; } -bool arc_welder::on_progress_(double percentComplete, double seconds_elapsed, double estimatedSecondsRemaining, int gcodesProcessed, int linesProcessed, int points_compressed, int arcs_created) +bool arc_welder::on_progress_(arc_welder_progress progress) { if (progress_callback_ != NULL) { - return progress_callback_(percentComplete, seconds_elapsed, estimatedSecondsRemaining, gcodesProcessed, linesProcessed, points_compressed, arcs_created); + return progress_callback_(progress); } std::stringstream stream; if (debug_logging_enabled_) { - stream << percentComplete << "% complete in " << seconds_elapsed << " seconds with " << estimatedSecondsRemaining << " seconds remaining. Gcodes Processed:" << gcodesProcessed << ", Current Line:" << linesProcessed << ", Points Compressed:" << points_compressed << ", ArcsCreated:" << arcs_created; - p_logger_->log(logger_type_, DEBUG, stream.str()); + p_logger_->log(logger_type_, DEBUG, progress.str()); } return true; @@ -299,6 +321,9 @@ int arc_welder::process_gcode(parsed_command cmd, bool is_end) position* p_cur_pos = p_source_position_->get_current_position_ptr(); position* p_pre_pos = p_source_position_->get_previous_position_ptr(); + extruder extruder_current = p_cur_pos->get_current_extruder(); + extruder previous_extruder = p_pre_pos->get_current_extruder(); + point p(p_cur_pos->x, p_cur_pos->y, p_cur_pos->z, extruder_current.e_relative); //std::cout << lines_processed_ << " - " << cmd.gcode << ", CurrentEAbsolute: " << cur_extruder.e <<", ExtrusionLength: " << cur_extruder.extrusion_length << ", Retraction Length: " << cur_extruder.retraction_length << ", IsExtruding: " << cur_extruder.is_extruding << ", IsRetracting: " << cur_extruder.is_retracting << ".\n"; int lines_written = 0; @@ -307,8 +332,7 @@ int arc_welder::process_gcode(parsed_command cmd, bool is_end) bool arc_added = false; bool clear_shapes = false; - extruder extruder_current = p_cur_pos->get_current_extruder(); - point p(p_cur_pos->x, p_cur_pos->y, p_cur_pos->z, extruder_current.e_relative); + // We need to make sure the printer is using absolute xyz, is extruding, and the extruder axis mode is the same as that of the previous position // TODO: Handle relative XYZ axis. This is possible, but maybe not so important. @@ -319,8 +343,8 @@ int arc_welder::process_gcode(parsed_command cmd, bool is_end) !p_cur_pos->is_relative && ( !waiting_for_arc_ || - (p_pre_pos->get_current_extruder().is_extruding && extruder_current.is_extruding) || - (p_pre_pos->get_current_extruder().is_retracting && extruder_current.is_retracting) + (previous_extruder.is_extruding && extruder_current.is_extruding) || + (previous_extruder.is_retracting && extruder_current.is_retracting) ) && p_cur_pos->is_extruder_relative == p_pre_pos->is_extruder_relative && (!waiting_for_arc_ || p_pre_pos->f == p_cur_pos->f) && @@ -336,13 +360,13 @@ int arc_welder::process_gcode(parsed_command cmd, bool is_end) } write_unwritten_gcodes_to_file(); // add the previous point as the starting point for the current arc - point previous_p(p_pre_pos->x, p_pre_pos->y, p_pre_pos->z, p_pre_pos->get_current_extruder().e_relative); + point previous_p(p_pre_pos->x, p_pre_pos->y, p_pre_pos->z, previous_extruder.e_relative); // Don't add any extrusion, or you will over extrude! //std::cout << "Trying to add first point (" << p.x << "," << p.y << "," << p.z << ")..."; current_arc_.try_add_point(previous_p, 0); } - double e_relative = p_cur_pos->get_current_extruder().e_relative; + double e_relative = extruder_current.e_relative; int num_points = current_arc_.get_num_segments(); arc_added = current_arc_.try_add_point(p, e_relative); if (arc_added) @@ -391,15 +415,15 @@ int arc_welder::process_gcode(parsed_command cmd, bool is_end) } else if ( waiting_for_arc_ && !( - (p_pre_pos->get_current_extruder().is_extruding && extruder_current.is_extruding) || - (p_pre_pos->get_current_extruder().is_retracting && extruder_current.is_retracting) + (previous_extruder.is_extruding && extruder_current.is_extruding) || + (previous_extruder.is_retracting && extruder_current.is_retracting) ) ) { std::string message = "Extruding or retracting state changed, cannot add point to current arc: " + cmd.gcode; if (verbose_logging_enabled_) { - extruder previous_extruder = p_pre_pos->get_current_extruder(); + message.append( " - Verbose Info\n\tCurrent Position Info - Absolute E:" + utilities::to_string(extruder_current.e) + ", Offset E:" + utilities::to_string(extruder_current.get_offset_e()) + @@ -477,20 +501,19 @@ int arc_welder::process_gcode(parsed_command cmd, bool is_end) { unwritten_commands_.pop_back(); } - // get the current absolute e coordinate of the previous position (the current position is not included in - // the arc) so we can make any adjustments that are necessary. + // get the feedrate for the previous position double current_f = p_pre_pos->f; // IMPORTANT NOTE: p_cur_pos and p_pre_pos will NOT be usable beyond this point. p_pre_pos = NULL; p_cur_pos = NULL; // Undo the previous updates that will be turned into the arc, including the current position + // (so not num_segments - 1, but num_segments) for (int index = 0; index < current_arc_.get_num_segments(); index++) { undo_commands_.push_back(p_source_position_->get_current_position_ptr()->command); p_source_position_->undo_update(); } - //position * p_undo_positions = p_source_position_->undo_update(current_arc_.get_num_segments()); // Set the current feedrate if it is different, else set to 0 to indicate that no feedrate should be included if(p_source_position_->get_current_position_ptr()->f == current_f) @@ -558,6 +581,7 @@ int arc_welder::process_gcode(parsed_command cmd, bool is_end) parsed_command cmd = undo_commands_.pop_back(); p_source_position_->update(undo_commands_[index], lines_processed_, gcodes_processed_, -1); } + // Clear the undo commands (there should be one left) undo_commands_.clear(); // Now clear the arc and flag the processor as not waiting for an arc waiting_for_arc_ = false; diff --git a/ArcWelder/arc_welder.h b/ArcWelder/arc_welder.h index 5e7db48..c64c7bd 100644 --- a/ArcWelder/arc_welder.h +++ b/ArcWelder/arc_welder.h @@ -36,8 +36,60 @@ #include "array_list.h" #include "unwritten_command.h" #include "logger.h" -// define anti stutter class -typedef bool(*progress_callback)(double percentComplete, double seconds_elapsed, double estimatedSecondsRemaining, int gcodesProcessed, int linesProcessed, int points_compressed, int arcs_created); + +struct arc_welder_progress { + arc_welder_progress() { + percent_complete = 0.0; + seconds_elapsed = 0.0; + seconds_remaining = 0.0; + gcodes_processed = 0; + lines_processed = 0; + points_compressed = 0; + arcs_created = 0; + source_file_size = 0; + target_file_size = 0; + } + double percent_complete; + double seconds_elapsed; + double seconds_remaining; + int gcodes_processed; + int lines_processed; + int points_compressed; + int arcs_created; + long source_file_size; + long target_file_size; + + std::string str() { + std::stringstream stream; + stream << std::fixed << std::setprecision(2); + double compression_ratio = 0; + if (target_file_size > 0) + compression_ratio = (static_cast<float>(source_file_size) / static_cast<float>(target_file_size) * 100.0f); + stream << percent_complete << "% complete in " << seconds_elapsed << " seconds with " << seconds_remaining << " seconds remaining."; + stream << " Gcodes Processed: " << gcodes_processed; + stream << ", Current Line: " << lines_processed; + stream << ", Points Compressed: " << points_compressed; + stream << ", ArcsCreated: " << arcs_created; + stream << ", Compression: " << compression_ratio << "% "; + return stream.str(); + } +}; + +// define the progress callback type +typedef bool(*progress_callback)(arc_welder_progress); + +struct arc_welder_results { + arc_welder_results() : progress() + { + success = false; + cancelled = false; + message = ""; + } + bool success; + bool cancelled; + std::string message; + arc_welder_progress progress; +}; class arc_welder { @@ -47,10 +99,10 @@ public: arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, bool g90_g91_influences_extruder, int buffer_size, progress_callback callback); void set_logger_type(int logger_type); virtual ~arc_welder(); - void process(); + arc_welder_results process(); double notification_period_seconds; protected: - virtual bool on_progress_(double percentComplete, double seconds_elapsed, double estimatedSecondsRemaining, int gcodesProcessed, int linesProcessed, int points_compressed, int arcs_created); + virtual bool on_progress_(arc_welder_progress progress); private: void reset(); static gcode_position_args get_args_(bool g90_g91_influences_extruder, int buffer_size); diff --git a/ArcWelderConsole/ArcWelderConsole.cpp b/ArcWelderConsole/ArcWelderConsole.cpp index ebc39b5..c96d4e7 100644 --- a/ArcWelderConsole/ArcWelderConsole.cpp +++ b/ArcWelderConsole/ArcWelderConsole.cpp @@ -26,7 +26,6 @@ #include <iostream> #include <sstream> #include <iomanip> -#include "arc_welder.h" #include "gcode_position.h" int main(int argc, char* argv[]) { @@ -224,13 +223,8 @@ int main(int argc, char* argv[]) return 0; } -static bool on_progress(double percent_complete, double seconds_elapsed, double seconds_remaining, int gcodes_processed, int current_line, int points_compressed, int arcs_created) +static bool on_progress(arc_welder_progress progress) { - std::cout << std::fixed << std::setprecision(1); - if (percent_complete < 100) - std::cout << percent_complete << "% complete in " << seconds_elapsed << " seconds with " << seconds_remaining << " seconds remaining. Current Line: " << current_line << ", Points Compressed:" << points_compressed << ", ArcsCreated:" << arcs_created << "\r\n"; - else if (percent_complete >= 100) - std::cout << "Processing Completed in " << seconds_elapsed << " seconds.\n\tPoints Compressed:" << points_compressed << "\n\tArcsCreated:" << arcs_created << "\n"; - std::cout.unsetf(std::ios::floatfield); + std::cout << progress.str(); return true; }
\ No newline at end of file diff --git a/ArcWelderConsole/ArcWelderConsole.h b/ArcWelderConsole/ArcWelderConsole.h index 35ad744..d7ac5b9 100644 --- a/ArcWelderConsole/ArcWelderConsole.h +++ b/ArcWelderConsole/ArcWelderConsole.h @@ -23,4 +23,5 @@ // FormerLurker@pm.me //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma once -static bool on_progress(double percent_complete, double seconds_elapsed, double seconds_remaining, int gcodes_processed, int current_line, int points_compressed, int arcs_created);
\ No newline at end of file +#include "arc_welder.h" +static bool on_progress(arc_welder_progress progress);
\ No newline at end of file diff --git a/ArcWelderInverseProcessor/ArcWelderInverseProcessor.cpp b/ArcWelderInverseProcessor/ArcWelderInverseProcessor.cpp index 4d1e2fa..38da205 100644 --- a/ArcWelderInverseProcessor/ArcWelderInverseProcessor.cpp +++ b/ArcWelderInverseProcessor/ArcWelderInverseProcessor.cpp @@ -32,7 +32,7 @@ int main(int argc, char* argv[]) { std::string info = "Arc Welder: Inverse Processor v0.1.rc1.dev0\nConverts G2/G3 commands to G1/G2 commands.\nCopyright(C) 2020 - Brad Hochgesang\n"; std::cout << info; - TestInverseProcessor(ARC_GYROID_BENCHY_DIFFICULT, "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\test_output.gcode"); + TestInverseProcessor(SIX_SPEED_TEST, "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\test_output.gcode"); } static void TestInverseProcessor(std::string source_path, std::string target_path) diff --git a/ArcWelderInverseProcessor/ArcWelderInverseProcessor.h b/ArcWelderInverseProcessor/ArcWelderInverseProcessor.h index d17f2dd..5647357 100644 --- a/ArcWelderInverseProcessor/ArcWelderInverseProcessor.h +++ b/ArcWelderInverseProcessor/ArcWelderInverseProcessor.h @@ -46,6 +46,9 @@ static std::string SUPER_HUGE_TEST = "C:\\Users\\Brad\\Documents\\3DPrinter\\Ant static std::string TORTURE_TEST = "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\stereographic_projection_0.2mm_PLA_MK2.5MMU2_2h49m.gcode"; static std::string ORCHID_POD = "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\Pla_OrchidPot.gcode"; static std::string ARC_GYROID_BENCHY_DIFFICULT = "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\AS_BenchyArc_Difficult.gcode"; +static std::string SIX_SPEED_TEST = "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\SixSpeedTest_AS.gcode"; + + // Issues static std::string ISSUE_MIMUPREFERIDA = "C:\\Users\\Brad\\Documents\\AntiStutter\\Issues\\MIMUPREFERIDA\\TESTSTUTTER.gcode"; static std::string ISSUE_PRICKLYPEAR = "C:\\Users\\Brad\\Documents\\AntiStutter\\Issues\\PricklyPear\\Barbarian.gcode"; diff --git a/ArcWelderInverseProcessor/inverse_processor.cpp b/ArcWelderInverseProcessor/inverse_processor.cpp index 57144b8..02991f4 100644 --- a/ArcWelderInverseProcessor/inverse_processor.cpp +++ b/ArcWelderInverseProcessor/inverse_processor.cpp @@ -68,15 +68,18 @@ inverse_processor::inverse_processor(std::string source_path, std::string target // 20200417 - FormerLurker - Declare two globals and pre-calculate some values that will reduce the // amount of trig funcitons we need to call while printing. For the price of having two globals we // save one trig calc per G2/G3 for both MIN_ARC_SEGMENTS and MIN_MM_PER_ARC_SEGMENT. This is a good trade IMHO. + + #ifdef MIN_ARC_SEGMENTS // Determines the radius at which the transition from using MM_PER_ARC_SEGMENT to MIN_ARC_SEGMENTS - /*const float*/arc_max_radius_threshold = MM_PER_ARC_SEGMENT / (2.0F * sin(M_PI / MIN_ARC_SEGMENTS)); + arc_max_radius_threshold = MM_PER_ARC_SEGMENT / (2.0F * sin(M_PI / MIN_ARC_SEGMENTS)); #endif +/* #if defined(MIN_ARC_SEGMENTS) && defined(MIN_MM_PER_ARC_SEGMENT) // Determines the radius at which the transition from using MIN_ARC_SEGMENTS to MIN_MM_PER_ARC_SEGMENT. - /*const float*/arc_min_radius_threshold = MIN_MM_PER_ARC_SEGMENT / (2.0F * sin(M_PI / MIN_ARC_SEGMENTS)); + arc_min_radius_threshold = MIN_MM_PER_ARC_SEGMENT / (2.0F * sin(M_PI / MIN_ARC_SEGMENTS)); #endif - +*/ } gcode_position_args inverse_processor::get_args_(bool g90_g91_influences_extruder, int buffer_size) @@ -202,7 +205,7 @@ void inverse_processor::process() } float radius = hypot(offset[X_AXIS], offset[Y_AXIS]); // Compute arc radius for mc_arc uint8_t isclockwise = cmd.command == "G2" ? 1 : 0; - output_file << mc_arc(position, target, offset, X_AXIS, Y_AXIS, Z_AXIS, static_cast<float>(p_pre_pos->f), radius, isclockwise, 0, p_cur_pos->is_extruder_relative) << "\n"; + output_file << mc_arc(position, target, offset, X_AXIS, Y_AXIS, Z_AXIS, static_cast<float>(p_cur_pos->f), radius, isclockwise, 0, p_cur_pos->is_extruder_relative) << "\n"; } else { @@ -256,7 +259,7 @@ std::string inverse_processor::mc_arc(float* position, float* target, float* off float rt_axis0 = target[axis_0] - center_axis0; float rt_axis1 = target[axis_1] - center_axis1; // 20200419 - Add a variable that will be used to hold the arc segment length - float mm_per_arc_segment; + float mm_per_arc_segment = MM_PER_ARC_SEGMENT; // CCW angle between position and target from circle center. Only one atan2() trig computation required. float angular_travel_total = atan2(r_axis0 * rt_axis1 - r_axis1 * rt_axis0, r_axis0 * rt_axis0 + r_axis1 * rt_axis1); @@ -265,18 +268,29 @@ std::string inverse_processor::mc_arc(float* position, float* target, float* off #ifdef MIN_ARC_SEGMENTS // 20200417 - FormerLurker - Implement MIN_ARC_SEGMENTS if it is defined - from Marlin 2.0 implementation // Do this before converting the angular travel for clockwise rotation + if (radius < arc_max_radius_threshold) mm_per_arc_segment = radius * ((2.0f * M_PI) / MIN_ARC_SEGMENTS); +#endif + +#ifdef ARC_SEGMENTS_PER_SEC + // 20200417 - FormerLurker - Implement MIN_ARC_SEGMENTS if it is defined - from Marlin 2.0 implementation + float mm_per_arc_segment_sec = (feed_rate / 60.0f) * (1.0f / ARC_SEGMENTS_PER_SEC); + if (mm_per_arc_segment_sec < mm_per_arc_segment) + mm_per_arc_segment = mm_per_arc_segment_sec; +#endif + #ifdef MIN_MM_PER_ARC_SEGMENT -// 20200417 - FormerLurker - Implement MIN_MM_PER_ARC_SEGMENT if it is defined -// This prevents a very high number of segments from being generated for curves of a short radius - if (radius < arc_min_radius_threshold) mm_per_arc_segment = MIN_MM_PER_ARC_SEGMENT; - else + // 20200417 - FormerLurker - Implement MIN_MM_PER_ARC_SEGMENT if it is defined + // This prevents a very high number of segments from being generated for curves of a short radius + if (mm_per_arc_segment < MIN_MM_PER_ARC_SEGMENT) mm_per_arc_segment = MIN_MM_PER_ARC_SEGMENT; #endif - if (radius < arc_max_radius_threshold) mm_per_arc_segment = radius * ((2.0f * M_PI) / MIN_ARC_SEGMENTS); - else mm_per_arc_segment = MM_PER_ARC_SEGMENT; -#else - // 20200418 - FormerLurker - Use the standard segment length - mm_per_arc_segment = MM_PER_ARC_SEGMENT; + +#if defined(MIN_MM_PER_ARC_SEGMENT) || defined(ARC_SEGMENTS_PER_SEC) || defined(MIN_ARC_SEGMENTS) + if (mm_per_arc_segment > MM_PER_ARC_SEGMENT) + mm_per_arc_segment = MM_PER_ARC_SEGMENT; #endif + + + // Adjust the angular travel if the direction is clockwise if (isclockwise) { angular_travel_total -= 2 * M_PI; } //20141002:full circle for G03 did not work, e.g. G03 X80 Y80 I20 J0 F2000 is giving an Angle of zero so head is not moving @@ -293,9 +307,9 @@ std::string inverse_processor::mc_arc(float* position, float* target, float* off if (millimeters_of_travel_arc < 0.001) { return ""; } // Calculate the total travel per segment // Calculate the number of arc segments - uint16_t segments = static_cast<uint16_t>(floor(millimeters_of_travel_arc / mm_per_arc_segment)); + uint16_t segments = static_cast<uint16_t>(ceil(millimeters_of_travel_arc / mm_per_arc_segment)); // Ensure at least one segment - if (segments < 1) segments = 1; + //if (segments < 1) segments = 1; // Calculate theta per segments and linear (z) travel per segment float theta_per_segment = angular_travel_total / segments; diff --git a/ArcWelderInverseProcessor/inverse_processor.h b/ArcWelderInverseProcessor/inverse_processor.h index 761e186..b84b92a 100644 --- a/ArcWelderInverseProcessor/inverse_processor.h +++ b/ArcWelderInverseProcessor/inverse_processor.h @@ -34,13 +34,15 @@ typedef signed char int8_t; #define M_PI 3.14159265358979323846f // pi enum AxisEnum { X_AXIS = 0, Y_AXIS= 1, Z_AXIS = 2, E_AXIS = 3, X_HEAD = 4, Y_HEAD = 5 }; // Arc interpretation settings: -#define MM_PER_ARC_SEGMENT 1.0f // The maximum length of an arc segment if a full circle of the same radius has more than MIN_ARC_SEGMENTS (if defined) -#define N_ARC_CORRECTION 25 // The number of interpolated segments that will be generated without a floating point correction -// 20200417 - FormerLurker - Add Additional Arc Config Values -#define MIN_ARC_SEGMENTS 32 // The minimum segments in a full circle. If not defined, MM_PER_ARC_SEMGMENT is enforced always -#define MIN_MM_PER_ARC_SEGMENT 0.25f // the minimum length of an interpolated segment. Must be smaller than MM_PER_ARC_SEGMENT if defined. +#define MM_PER_ARC_SEGMENT 2.0f // REQUIRED - The enforced maximum length of an arc segment +#define MIN_MM_PER_ARC_SEGMENT 0.2f /* OPTIONAL - the enforced minimum length of an interpolated segment. Must be smaller than + MM_PER_ARC_SEGMENT if defined. Only has an effect if MIN_ARC_SEGMENTS or ARC_SEGMENTS_PER_SEC is defined */ + // If both MIN_ARC_SEGMENTS and ARC_SEGMENTS_PER_SEC is defined, the minimum calculated segment length is used. +#define MIN_ARC_SEGMENTS 20 // OPTIONAL - The enforced minimum segments in a full circle of the same radius. +#define ARC_SEGMENTS_PER_SEC 40 // OPTIONAL - Use feedrate to choose segment length. +#define N_ARC_CORRECTION 25 // OPTIONAL - The number of interpolated segments that will be generated without a floating point correction //#define ARC_EXTRUSION_CORRECTION // If defined, we should apply correction to the extrusion length based on the - // difference in true arc length. The correctly is extremely small, and may not be worth the cpu cycles + // difference in true arc length. The correctly is extremely small, and may not be worth the cpu cycles class inverse_processor { public: @@ -54,8 +56,9 @@ private: std::string source_path_; std::string target_path_; gcode_position* p_source_position_; + float arc_max_radius_threshold; - float arc_min_radius_threshold; + //float arc_min_radius_threshold; float total_e_adjustment; int trig_calc_count = 0; }; diff --git a/ArcWelder.sln b/ArcWelderLib.sln index 9bfbb73..9bfbb73 100644 --- a/ArcWelder.sln +++ b/ArcWelderLib.sln diff --git a/ArcWelderTest/ArcWelderTest.cpp b/ArcWelderTest/ArcWelderTest.cpp index a082bc3..4988988 100644 --- a/ArcWelderTest/ArcWelderTest.cpp +++ b/ArcWelderTest/ArcWelderTest.cpp @@ -239,7 +239,8 @@ static void TestAntiStutter(std::string filePath) logger* p_logger = new logger(logger_names, logger_levels); p_logger->set_log_level(INFO); //arc_welder arc_welder_obj(BENCHY_0_5_MM_NO_WIPE, "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\test_output.gcode", p_logger, max_resolution, false, 50, static_cast<progress_callback>(on_progress)); - arc_welder arc_welder_obj(ISSUE_PRICKLYPEAR, "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\test_output.gcode", p_logger, max_resolution, false, 50); + arc_welder arc_welder_obj(SIX_SPEED_TEST, "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\test_output.gcode", p_logger, max_resolution, false, 50, on_progress); + arc_welder arc_welder_obj(SIX_SPEED_TEST, "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\SixSpeedTest_AS.gcode", p_logger, max_resolution, false, 50, on_progress); //BENCHY_LAYER_1GCODE //SMALL_TEST //FACE_SHIELD @@ -261,8 +262,8 @@ static void TestAntiStutter(std::string filePath) delete p_logger; } -static bool on_progress(double percentComplete, double secondsElapsed, double estimatedSecondsRemaining, int gcodes_processed, int currentLine, int points_compressed, int arcs_created) +static bool on_progress(arc_welder_progress progress) { - std::cout << percentComplete << "% complete in " << secondsElapsed << " seconds with " << estimatedSecondsRemaining << " seconds remaining. Current Line: " << currentLine << ", Points Compressed:" << points_compressed << ", ArcsCreated:" << arcs_created << "\r\n"; + std::cout << progress.str() << "\r\n"; return true; } diff --git a/ArcWelderTest/ArcWelderTest.h b/ArcWelderTest/ArcWelderTest.h index 4cda18a..9b5c3ea 100644 --- a/ArcWelderTest/ArcWelderTest.h +++ b/ArcWelderTest/ArcWelderTest.h @@ -44,7 +44,7 @@ static gcode_position_args get_single_extruder_position_args(); static gcode_position_args get_5_shared_extruder_position_args(); static gcode_position_args get_5_extruder_position_args(); static void TestAntiStutter(std::string filePath); -static bool on_progress(double percentComplete, double secondsElapsed, double estimatedSecondsRemaining, int gcodes_processed, int currentLine, int points_compressed, int arcs_created); +static bool on_progress(arc_welder_progress progress); static std::string ANTI_STUTTER_TEST = "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\5x5_cylinder_2000Fn_0.2mm_PLA_MK2.5MMU2_4m.gcode"; @@ -66,6 +66,7 @@ static std::string SUPER_HUGE_TEST = "C:\\Users\\Brad\\Documents\\3DPrinter\\Ant static std::string TORTURE_TEST = "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\stereographic_projection_0.2mm_PLA_MK2.5MMU2_2h49m.gcode"; static std::string ORCHID_POD = "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\Pla_OrchidPot.gcode"; static std::string ARC_GYROID_BENCHY_DIFFICULT = "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\AS_BenchyArc_Difficult.gcode"; +static std::string SIX_SPEED_TEST = "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\6_speed_test.gcode"; // Issues static std::string ISSUE_MIMUPREFERIDA = "C:\\Users\\Brad\\Documents\\AntiStutter\\Issues\\MIMUPREFERIDA\\TESTSTUTTER.gcode"; static std::string ISSUE_PRICKLYPEAR = "C:\\Users\\Brad\\Documents\\AntiStutter\\Issues\\PricklyPear\\Barbarian.gcode"; diff --git a/GcodeProcessorLib/utilities.h b/GcodeProcessorLib/utilities.h index 2a79d80..a109a98 100644 --- a/GcodeProcessorLib/utilities.h +++ b/GcodeProcessorLib/utilities.h @@ -22,7 +22,6 @@ #pragma once #include <string> - class utilities{ public: static bool is_zero(double x); @@ -32,7 +31,7 @@ public: static bool greater_than_or_equal(double x, double y); static bool less_than(double x, double y); static bool less_than_or_equal(double x, double y); - + // custom tolerance functions static bool is_zero(double x, double tolerance); static bool is_equal(double x, double y, double tolerance); @@ -49,6 +48,7 @@ public: static std::string rtrim(const std::string& s); static std::string trim(const std::string& s); static std::istream& safe_get_line(std::istream& is, std::string& t); + protected: static const std::string WHITESPACE_; private: diff --git a/PyArcWelder/PyArcWelder.vcxproj b/PyArcWelder/PyArcWelder.vcxproj index a17c4c6..1657d94 100644 --- a/PyArcWelder/PyArcWelder.vcxproj +++ b/PyArcWelder/PyArcWelder.vcxproj @@ -97,6 +97,10 @@ <GenerateDebugInformation>true</GenerateDebugInformation> <AdditionalLibraryDirectories>C:\Python27\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> @@ -110,6 +114,10 @@ <GenerateDebugInformation>true</GenerateDebugInformation> <AdditionalLibraryDirectories>C:\Python27\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> </Link> + <PostBuildEvent> + <Command> + </Command> + </PostBuildEvent> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ClCompile> @@ -127,6 +135,9 @@ <GenerateDebugInformation>true</GenerateDebugInformation> <AdditionalLibraryDirectories>C:\Python27\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> </Link> + <PostBuildEvent> + <Command>$(SolutionDir)\DeployCodeToPlugin.bat $(SolutionDir) C:\Users\Brad\source\repos\ArcWelderPlugin\</Command> + </PostBuildEvent> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> @@ -144,6 +155,9 @@ <GenerateDebugInformation>true</GenerateDebugInformation> <AdditionalLibraryDirectories>C:\Python27\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> </Link> + <PostBuildEvent> + <Command>$(SolutionDir)\DeployCodeToPlugin.bat $(SolutionDir) C:\Users\Brad\source\repos\ArcWelderPlugin\</Command> + </PostBuildEvent> </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="python_helpers.h" /> diff --git a/PyArcWelder/py_arc_welder.cpp b/PyArcWelder/py_arc_welder.cpp index 5768b40..0410c68 100644 --- a/PyArcWelder/py_arc_welder.cpp +++ b/PyArcWelder/py_arc_welder.cpp @@ -23,26 +23,65 @@ #include "py_arc_welder.h" -bool py_arc_welder::on_progress_(double percent_complete, double seconds_elapsed, double estimated_seconds_remaining, int gcodes_processed, int current_line, int points_compressed, int arcs_created) +PyObject* py_arc_welder::build_py_progress(arc_welder_progress progress) { - PyObject* funcArgs = Py_BuildValue("(d,d,d,i,i,i,i)", percent_complete, seconds_elapsed, estimated_seconds_remaining, gcodes_processed, current_line, points_compressed, arcs_created); - if (funcArgs == NULL) + PyObject* py_progress = Py_BuildValue("{s:d,s:d,s:d,s:i,s:i,s:i,s:i,s:i,s:i}", + u8"percent_complete", + progress.percent_complete, + u8"seconds_elapsed", + progress.seconds_elapsed, + u8"seconds_remaining", + progress.seconds_remaining, + u8"gcodes_processed", + progress.gcodes_processed, + u8"lines_processed", + progress.lines_processed, + u8"points_compressed", + progress.points_compressed, + u8"arcs_created", + progress.arcs_created, + u8"source_file_size", + progress.source_file_size, + u8"target_file_size", + progress.target_file_size + ); + return py_progress; +} + +bool py_arc_welder::on_progress_(arc_welder_progress progress) +{ + PyObject* py_dict = py_arc_welder::build_py_progress(progress); + if (py_dict == NULL) + return NULL; + PyObject* func_args = Py_BuildValue("(O)", py_dict); + if (func_args == NULL) { - return false; + Py_DECREF(py_dict); + return true; } - + PyGILState_STATE gstate = PyGILState_Ensure(); - PyObject* pContinueProcessing = PyObject_CallObject(py_progress_callback_, funcArgs); - Py_DECREF(funcArgs); - bool continue_processing = PyLong_AsLong(pContinueProcessing) > 0; - Py_DECREF(pContinueProcessing); - PyGILState_Release(gstate); - - if (pContinueProcessing == NULL || pContinueProcessing == Py_None) + PyObject* pContinueProcessing = PyObject_CallObject(py_progress_callback_, func_args); + Py_DECREF(func_args); + Py_DECREF(py_dict); + bool continue_processing; + if (pContinueProcessing == NULL) { - // no return value was supply, assume true - - return true; + // no return value was supply, assume true, but without decrefing pContinueProcessing + continue_processing = true; } + else + { + if (pContinueProcessing == Py_None) + { + continue_processing = true; + } + else + { + continue_processing = PyLong_AsLong(pContinueProcessing) > 0; + } + Py_DECREF(pContinueProcessing); + } + PyGILState_Release(gstate); return continue_processing; } diff --git a/PyArcWelder/py_arc_welder.h b/PyArcWelder/py_arc_welder.h index 64bfd71..b116791 100644 --- a/PyArcWelder/py_arc_welder.h +++ b/PyArcWelder/py_arc_welder.h @@ -41,8 +41,9 @@ public: virtual ~py_arc_welder() { } + static PyObject* build_py_progress(arc_welder_progress progress); protected: - virtual bool on_progress_(double percent_complete, double seconds_elapsed, double estimated_seconds_remaining, int gcodes_processed, int current_line, int points_compressed, int arcs_created); + virtual bool on_progress_(arc_welder_progress progress); private: PyObject* py_progress_callback_; }; diff --git a/PyArcWelder/py_arc_welder_extension.cpp b/PyArcWelder/py_arc_welder_extension.cpp index eaf9923..89410b9 100644 --- a/PyArcWelder/py_arc_welder_extension.cpp +++ b/PyArcWelder/py_arc_welder_extension.cpp @@ -39,17 +39,18 @@ int main(int argc, char* argv[]) } // Add a built-in module, before Py_Initialize - PyImport_AppendInittab("PyArcWelder", PyInit_PyGcodeArcConverter); + PyImport_AppendInittab("PyArcWelder", PyInit_PyArcWelder); // Pass argv[0] to the Python interpreter Py_SetProgramName(program); // Initialize the Python interpreter. Required. Py_Initialize(); - std::cout << "Initializing threads..."; + // We are not using threads, do not enable. + /*std::cout << "Initializing threads..."; if (!PyEval_ThreadsInitialized()) { PyEval_InitThreads(); - } + }*/ // Optionally import the module; alternatively, import can be deferred until the embedded script imports it. PyImport_ImportModule("PyArcWelder"); PyMem_RawFree(program); @@ -62,10 +63,13 @@ int main(int argc, char* argv[]) { Py_SetProgramName(argv[0]); Py_Initialize(); + // We are not using threads, do not enable. + /* std::cout << "Initializing threads..."; if (!PyEval_ThreadsInitialized()) { PyEval_InitThreads(); } - initPyGcodeArcConverter(); + */ + initPyArcWelder(); return 0; } @@ -82,61 +86,61 @@ static struct module_state _state; #endif // Python 2 module method definition -static PyMethodDef PyGcodeArcConverterMethods[] = { +static PyMethodDef PyArcWelderMethods[] = { { "ConvertFile", (PyCFunction)ConvertFile, METH_VARARGS ,"Converts segmented curve approximations to actual G2/G3 arcs within the supplied resolution." }, { NULL, NULL, 0, NULL } }; // Python 3 module method definition #if PY_MAJOR_VERSION >= 3 -static int PyGcodeArcConverter_traverse(PyObject* m, visitproc visit, void* arg) { +static int PyArcWelder_traverse(PyObject* m, visitproc visit, void* arg) { Py_VISIT(GETSTATE(m)->error); return 0; } -static int PyGcodeArcConverter_clear(PyObject* m) { +static int PyArcWelder_clear(PyObject* m) { Py_CLEAR(GETSTATE(m)->error); return 0; } static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, - "PyGcodeArcConverter", + "PyArcWelder", NULL, sizeof(struct module_state), - PyGcodeArcConverterMethods, + PyArcWelderMethods, NULL, - PyGcodeArcConverter_traverse, - PyGcodeArcConverter_clear, + PyArcWelder_traverse, + PyArcWelder_clear, NULL }; #define INITERROR return NULL PyMODINIT_FUNC -PyInit_PyGcodeArcConverter(void) +PyInit_PyArcWelder(void) #else #define INITERROR return -extern "C" void initPyGcodeArcConverter(void) +extern "C" void initPyArcWelder(void) #endif { - std::cout << "Initializing PyGcodeArcConverter V0.1.0rc1.dev0 - Copyright (C) 2019 Brad Hochgesang..."; + std::cout << "Initializing PyArcWelder V0.1.0rc1.dev0 - Copyright (C) 2019 Brad Hochgesang."; #if PY_MAJOR_VERSION >= 3 - std::cout << "Python 3+ Detected..."; + std::cout << " Python 3+ Detected..."; PyObject* module = PyModule_Create(&moduledef); #else - std::cout << "Python 2 Detected..."; - PyObject* module = Py_InitModule("PyGcodeArcConverter", PyGcodeArcConverterMethods); + std::cout << " Python 2 Detected..."; + PyObject* module = Py_InitModule("PyArcWelder", PyArcWelderMethods); #endif if (module == NULL) INITERROR; struct module_state* st = GETSTATE(module); - st->error = PyErr_NewException((char*)"PyGcodeArcConverter.Error", NULL, NULL); + st->error = PyErr_NewException((char*)"PyArcWelder.Error", NULL, NULL); if (st->error == NULL) { Py_DECREF(module); INITERROR; @@ -150,7 +154,7 @@ extern "C" void initPyGcodeArcConverter(void) std::string message = "PyArcWelder V0.1.0rc1.dev0 imported - Copyright (C) 2019 Brad Hochgesang..."; p_py_logger->log(GCODE_CONVERSION, INFO, message); p_py_logger->set_log_level_by_value(DEBUG); - std::cout << "complete\r\n"; + std::cout << " Initialization Complete\r\n"; #if PY_MAJOR_VERSION >= 3 return module; @@ -191,12 +195,27 @@ extern "C" p_py_logger->log(GCODE_CONVERSION, INFO, message); py_arc_welder arc_welder_obj(args.source_file_path, args.target_file_path, p_py_logger, args.resolution_mm, args.g90_g91_influences_extruder, 50, py_progress_callback); - arc_welder_obj.process(); + arc_welder_results results = arc_welder_obj.process(); message = "py_gcode_arc_converter.ConvertFile - Arc Conversion Complete."; p_py_logger->log(GCODE_CONVERSION, INFO, message); Py_XDECREF(py_progress_callback); - // For now just return py_none - return PyTuple_Pack(1, Py_None); + // return the arguments + PyObject* p_progress = py_arc_welder::build_py_progress(results.progress); + if (p_progress == NULL) + p_progress = Py_None; + + PyObject* p_results = Py_BuildValue( + "{s:i,s:i,s:s,s:O}", + u8"success", + results.success, + u8"cancelled", + results.cancelled, + u8"message", + results.message, + u8"progress", + p_progress + ); + return p_results; } } diff --git a/PyArcWelder/py_arc_welder_extension.h b/PyArcWelder/py_arc_welder_extension.h index 7a3786d..47b7215 100644 --- a/PyArcWelder/py_arc_welder_extension.h +++ b/PyArcWelder/py_arc_welder_extension.h @@ -33,9 +33,9 @@ extern "C" { #if PY_MAJOR_VERSION >= 3 - PyMODINIT_FUNC PyInit_PyGcodeArcConverter(void); + PyMODINIT_FUNC PyInit_PyArcWelder(void); #else - extern "C" void initPyGcodeArcConverter(void); + extern "C" void initPyArcWelder(void); #endif static PyObject* ConvertFile(PyObject* self, PyObject* args); } diff --git a/PyArcWelder/py_logger.cpp b/PyArcWelder/py_logger.cpp index d05e059..1020331 100644 --- a/PyArcWelder/py_logger.cpp +++ b/PyArcWelder/py_logger.cpp @@ -60,7 +60,6 @@ void py_logger::initialize_loggers() // Create a logging configurator PyGILState_STATE gstate = PyGILState_Ensure(); - std::cout << "Creating arguments for LoggingConfigurator creation.\r\n"; PyObject* funcArgs = Py_BuildValue("(s,s,s)", "arc_welder", "arc_welder.", "octoprint_arc_welder."); if (funcArgs == NULL) { @@ -68,7 +67,7 @@ void py_logger::initialize_loggers() PyErr_SetString(PyExc_ImportError, "Could not create LoggingConfigurator arguments."); return; } - std::cout << "Creating LoggingConfigurator..."; + py_logging_configurator = PyObject_CallObject(py_logging_configurator_name, funcArgs); std::cout << "Complete.\r\n"; Py_DECREF(funcArgs); @@ -98,8 +97,6 @@ void py_logger::initialize_loggers() py_critical_function_name = gcode_arc_converter::PyString_SafeFromString("critical"); py_get_effective_level_function_name = gcode_arc_converter::PyString_SafeFromString("getEffectiveLevel"); loggers_created_ = true; - std::cout << "Logger created successfully.\r\n"; - } void py_logger::set_internal_log_levels(bool check_real_time) @@ -199,6 +196,11 @@ void py_logger::log(const int logger_type, const int log_level, const std::strin case CRITICAL: pyFunctionName = py_critical_function_name; break; + default: + std::cout << "An unknown log level of '" << log_level << " 'was supplied for the message: " << message.c_str() << "\r\n"; + PyErr_Format(PyExc_ValueError, + "An unknown log level was supplied for the message %s.", message.c_str()); + return; } } PyObject* pyMessage = gcode_arc_converter::PyUnicode_SafeFromString(message); @@ -220,15 +222,19 @@ void py_logger::log(const int logger_type, const int log_level, const std::strin { std::cout << "Logging.arc_welder_log - null was returned from the specified logger.\r\n"; PyErr_SetString(PyExc_ValueError, "Logging.arc_welder_log - null was returned from the specified logger."); + return; } else { std::cout << "Logging.arc_welder_log - null was returned from the specified logger and an error was detected.\r\n"; + std::cout << "\tLog Level: " << log_level <<", Logger Type: " << logger_type << ", Message: " << message.c_str() << "\r\n"; + // I'm not sure what else to do here since I can't log the error. I will print it // so that it shows up in the console, but I can't log it, and there is no way to // return an error. PyErr_Print(); PyErr_Clear(); + return; } } else |