diff options
author | FormerLurker <hochgebe@gmail.com> | 2020-10-17 02:34:25 +0300 |
---|---|---|
committer | FormerLurker <hochgebe@gmail.com> | 2020-10-17 02:34:25 +0300 |
commit | 4fd38897fd66c245991a4066c7bb3db373087e70 (patch) | |
tree | 30a91cd0ded86278929869ec265c939fc839e18a | |
parent | 04958af691abfebc8314b300b4d8676f074439a7 (diff) |
Add additional statistics. Fix windows c++ build for python 2.7 compilers.
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | ArcWelder/arc_welder.cpp | 109 | ||||
-rw-r--r-- | ArcWelder/arc_welder.h | 298 | ||||
-rw-r--r-- | ArcWelder/segmented_arc.cpp | 5 | ||||
-rw-r--r-- | ArcWelder/segmented_arc.h | 1 | ||||
-rw-r--r-- | ArcWelder/segmented_shape.cpp | 18 | ||||
-rw-r--r-- | ArcWelder/segmented_shape.h | 2 | ||||
-rw-r--r-- | ArcWelder/unwritten_command.h | 11 | ||||
-rw-r--r-- | ArcWelderConsole/ArcWelderConsole.cpp | 2 | ||||
-rw-r--r-- | ArcWelderTest/ArcWelderTest.cpp | 21 | ||||
-rw-r--r-- | ArcWelderTest/ArcWelderTest.h | 1 | ||||
-rw-r--r-- | GcodeProcessorLib/GcodeProcessorLib.vcxproj | 2 | ||||
-rw-r--r-- | GcodeProcessorLib/gcode_parser.cpp | 40 | ||||
-rw-r--r-- | GcodeProcessorLib/gcode_parser.h | 2 | ||||
-rw-r--r-- | GcodeProcessorLib/logger.cpp | 63 | ||||
-rw-r--r-- | GcodeProcessorLib/logger.h | 9 | ||||
-rw-r--r-- | GcodeProcessorLib/utilities.cpp | 65 | ||||
-rw-r--r-- | GcodeProcessorLib/utilities.h | 7 | ||||
-rw-r--r-- | PyArcWelder/py_arc_welder.cpp | 78 | ||||
-rw-r--r-- | PyArcWelder/py_arc_welder.h | 2 | ||||
-rw-r--r-- | PyArcWelder/py_arc_welder_extension.cpp | 8 |
21 files changed, 613 insertions, 135 deletions
@@ -26,7 +26,9 @@ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ - +*.tlog +*.idb +Remote_Pi/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot diff --git a/ArcWelder/arc_welder.cpp b/ArcWelder/arc_welder.cpp index 2d3eac5..c283e26 100644 --- a/ArcWelder/arc_welder.cpp +++ b/ArcWelder/arc_welder.cpp @@ -22,6 +22,9 @@ // You can contact the author at the following email address: // FormerLurker@pm.me //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if _MSC_VER > 1200 +#define _CRT_SECURE_NO_DEPRECATE +#endif #include "arc_welder.h" #include <vector> @@ -31,7 +34,9 @@ #include <fstream> #include <iomanip> #include <sstream> -arc_welder::arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, double max_radius, gcode_position_args args) : current_arc_(DEFAULT_MIN_SEGMENTS, gcode_position_args_.position_buffer_size - 5, resolution_mm, max_radius) + + +arc_welder::arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, double max_radius, bool g90_g91_influences_extruder, int buffer_size, progress_callback callback) : current_arc_(DEFAULT_MIN_SEGMENTS, buffer_size - 5, resolution_mm, max_radius), segment_statistics_(segment_statistic_lengths, segment_statistic_lengths_count, log) { p_logger_ = log; debug_logging_enabled_ = false; @@ -40,12 +45,12 @@ arc_welder::arc_welder(std::string source_path, std::string target_path, logger verbose_logging_enabled_ = false; logger_type_ = 0; - progress_callback_ = NULL; + progress_callback_ = callback; verbose_output_ = false; source_path_ = source_path; target_path_ = target_path; resolution_mm_ = resolution_mm; - gcode_position_args_ = args; + gcode_position_args_ = get_args_(g90_g91_influences_extruder, buffer_size); notification_period_seconds = 1; lines_processed_ = 0; gcodes_processed_ = 0; @@ -66,19 +71,8 @@ arc_welder::arc_welder(std::string source_path, std::string target_path, logger } // We don't care about the printer settings, except for g91 influences extruder. - p_source_position_ = new gcode_position(gcode_position_args_); -} - -arc_welder::arc_welder(std::string source_path, std::string target_path, logger* log, double resolution_mm, double max_radius, bool g90_g91_influences_extruder, int buffer_size) - : arc_welder(source_path, target_path, log, resolution_mm, max_radius, arc_welder::get_args_(g90_g91_influences_extruder, buffer_size)) -{ -} - -arc_welder::arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, double max_radius, bool g90_g91_influences_extruder, int buffer_size, progress_callback callback) - : arc_welder(source_path, target_path, log, resolution_mm, max_radius, arc_welder::get_args_(g90_g91_influences_extruder, buffer_size)) -{ - progress_callback_ = callback; + p_source_position_ = new gcode_position(gcode_position_args_); } gcode_position_args arc_welder::get_args_(bool g90_g91_influences_extruder, int buffer_size) @@ -236,7 +230,7 @@ arc_welder_results results; stream << "Parsing: " << line; p_logger_->log(logger_type_, VERBOSE, stream.str()); } - parser_.try_parse_gcode(line.c_str(), cmd); + parser_.try_parse_gcode(line.c_str(), cmd, true); bool has_gcode = false; if (cmd.gcode.length() > 0) { @@ -251,7 +245,7 @@ arc_welder_results results; // Always process the command through the printer, even if no command is found // This is important so that comments can be analyzed //std::cout << "stabilization::process_file - updating position..."; - process_gcode(cmd, false); + process_gcode(cmd, false, false); // Only continue to process if we've found a command and either a progress_callback_ is supplied, or debug loggin is enabled. if (has_gcode && (progress_callback_ != NULL || info_logging_enabled_)) @@ -271,19 +265,20 @@ arc_welder_results results; if (current_arc_.is_shape() && waiting_for_arc_) { p_logger_->log(logger_type_, DEBUG, "The target file opened successfully."); - process_gcode(cmd, true); + process_gcode(cmd, true, false); } p_logger_->log(logger_type_, DEBUG, "Writing all unwritten gcodes to the target file."); write_unwritten_gcodes_to_file(); + p_logger_->log(logger_type_, DEBUG, "Fetching the final progress struct."); - p_logger_->log(logger_type_, DEBUG, "Processing complete, closing source and target file."); arc_welder_progress final_progress = get_progress_(static_cast<long>(file_size_), static_cast<double>(start_clock)); if (progress_callback_ != NULL || info_logging_enabled_) { // Sending final progress update message + p_logger_->log(logger_type_, VERBOSE, "Sending final progress update message."); on_progress_(final_progress); } - + p_logger_->log(logger_type_, DEBUG, "Processing complete, closing source and target file."); output_file_.close(); gcodeFile.close(); const clock_t end_clock = clock(); @@ -291,6 +286,8 @@ arc_welder_results results; results.success = continue_processing; results.cancelled = !continue_processing; results.progress = final_progress; + p_logger_->log(logger_type_, DEBUG, "Returning processing results."); + return results; } @@ -329,20 +326,20 @@ arc_welder_progress arc_welder::get_progress_(long source_file_position, double progress.compression_percent = (1.0 - (static_cast<float>(progress.target_file_size) / static_cast<float>(source_file_position))) * 100.0f; } + progress.segment_statistics = segment_statistics_; return progress; } -int arc_welder::process_gcode(parsed_command cmd, bool is_end) +int arc_welder::process_gcode(parsed_command cmd, bool is_end, bool is_reprocess) { // Update the position for the source gcode file p_source_position_->update(cmd, lines_processed_, gcodes_processed_, -1); - 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); + point p(p_cur_pos->get_gcode_x(), p_cur_pos->get_gcode_y(), p_cur_pos->get_gcode_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; @@ -351,12 +348,28 @@ int arc_welder::process_gcode(parsed_command cmd, bool is_end) bool arc_added = false; bool clear_shapes = false; + // Update the source file statistics + if (p_cur_pos->has_xy_position_changed && (extruder_current.is_extruding || extruder_current.is_retracting) && !is_reprocess) + { + double movement_length_mm = utilities::get_cartesian_distance(p_pre_pos->x, p_pre_pos->y, p_cur_pos->x, p_cur_pos->y); + if (movement_length_mm > 0) + { + segment_statistics_.update(movement_length_mm, true); + } + } + // 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. if ( !is_end && cmd.is_known_command && !cmd.is_empty && ( (cmd.command == "G0" || cmd.command == "G1") && utilities::is_equal(p_cur_pos->z, p_pre_pos->z) && + utilities::is_equal(p_cur_pos->x_offset, p_pre_pos->x_offset) && + utilities::is_equal(p_cur_pos->y_offset, p_pre_pos->y_offset) && + utilities::is_equal(p_cur_pos->z_offset, p_pre_pos->z_offset) && + utilities::is_equal(p_cur_pos->x_firmware_offset, p_pre_pos->x_firmware_offset) && + utilities::is_equal(p_cur_pos->y_firmware_offset, p_pre_pos->y_firmware_offset) && + utilities::is_equal(p_cur_pos->z_firmware_offset, p_pre_pos->z_firmware_offset) && !p_cur_pos->is_relative && ( !waiting_for_arc_ || @@ -378,7 +391,7 @@ 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, previous_extruder.e_relative); + point previous_p(p_pre_pos->get_gcode_x(), p_pre_pos->get_gcode_y(), p_pre_pos->get_gcode_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); @@ -488,6 +501,7 @@ int arc_welder::process_gcode(parsed_command cmd, bool is_end) } } } + if (!arc_added) { if (current_arc_.get_num_segments() < current_arc_.get_min_segments()) { @@ -507,9 +521,10 @@ int arc_welder::process_gcode(parsed_command cmd, bool is_end) if (current_arc_.is_shape()) { - // increment our statistics + // update our statistics points_compressed_ += current_arc_.get_num_segments()-1; - arcs_created_++; + arcs_created_++; // increment the number of generated arcs + //std::cout << "Arc shape found.\n"; // Get the comment now, before we remove the previous comments std::string comment = get_comment_for_arc(); @@ -548,13 +563,21 @@ int arc_welder::process_gcode(parsed_command cmd, bool is_end) if (debug_logging_enabled_) { - p_logger_->log(logger_type_, DEBUG, "Arc created with " + std::to_string(current_arc_.get_num_segments()) + " segments: " + gcode); + char buffer[20]; + std::string message = "Arc created with "; + sprintf(buffer, "%d", current_arc_.get_num_segments()); + message += buffer; + message += " segments: "; + message += gcode; + p_logger_->log(logger_type_, DEBUG, message); } // Get and alter the current position so we can add it to the unwritten commands list parsed_command arc_command = parser_.parse_gcode(gcode.c_str()); + double arc_extrusion_length = current_arc_.get_shape_length(); + unwritten_commands_.push_back( - unwritten_command(arc_command, p_cur_pos->is_extruder_relative) + unwritten_command(arc_command, p_cur_pos->is_extruder_relative, arc_extrusion_length) ); // write all unwritten commands (if we don't do this we'll mess up absolute e by adding an offset to the arc) @@ -569,7 +592,7 @@ int arc_welder::process_gcode(parsed_command cmd, bool is_end) // Reprocess this line if (!is_end) { - return process_gcode(cmd, false); + return process_gcode(cmd, false, true); } else { @@ -597,17 +620,21 @@ int arc_welder::process_gcode(parsed_command cmd, bool is_end) } } - if (clear_shapes) - { - waiting_for_arc_ = false; - current_arc_.clear(); - // The current command is unwritten, add it. - unwritten_commands_.push_back(unwritten_command(p_source_position_->get_current_position_ptr())); - } - else if (waiting_for_arc_ || !arc_added) + + + if (waiting_for_arc_ || !arc_added) { + position* cur_pos = p_source_position_->get_current_position_ptr(); + extruder& cur_extruder = cur_pos->get_current_extruder(); - unwritten_commands_.push_back(unwritten_command(p_source_position_->get_current_position_ptr())); + double length = 0; + if (p_cur_pos->has_xy_position_changed && (cur_extruder.is_extruding || cur_extruder.is_retracting)) + { + position* prev_pos = p_source_position_->get_previous_position_ptr(); + length = utilities::get_cartesian_distance(cur_pos->x, cur_pos->y, prev_pos->x, prev_pos->y); + } + + unwritten_commands_.push_back(unwritten_command(cur_pos, length)); } if (!waiting_for_arc_) @@ -648,7 +675,7 @@ std::string arc_welder::create_g92_e(double absolute_e) int arc_welder::write_gcode_to_file(std::string gcode) { - output_file_ << utilities::trim(gcode) << "\n"; + output_file_ << gcode << "\n"; return 1; } @@ -662,6 +689,10 @@ int arc_welder::write_unwritten_gcodes_to_file() { // The the current unwritten position and remove it from the list unwritten_command p = unwritten_commands_.pop_front(); + if (p.extrusion_length > 0) + { + segment_statistics_.update(p.extrusion_length, false); + } write_gcode_to_file(p.command.to_string()); } diff --git a/ArcWelder/arc_welder.h b/ArcWelder/arc_welder.h index 3e501af..d21c2c8 100644 --- a/ArcWelder/arc_welder.h +++ b/ArcWelder/arc_welder.h @@ -36,11 +36,284 @@ #include "array_list.h" #include "unwritten_command.h" #include "logger.h" +#include <cmath> + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + #define DEFAULT_G90_G91_INFLUENCES_EXTREUDER false +static const int segment_statistic_lengths_count = 12; +const double segment_statistic_lengths[] = { 0.002f, 0.005f, 0.01f, 0.05f, 0.1f, 0.5f, 1.0f, 5.0f, 10.0f, 20.0f, 50.0f, 100.0f }; + +struct segment_statistic { + segment_statistic(double min_length_mm, double max_length_mm) + { + count = 0; + min_mm = min_length_mm; + max_mm = max_length_mm; + } + + double min_mm; + double max_mm; + int count; +}; + +struct source_target_segment_statistics { + + + source_target_segment_statistics(const double segment_tracking_lengths[], const int num_lengths, logger* p_logger = NULL) { + total_length_source = 0; + total_length_target = 0; + total_count_source = 0; + total_count_target = 0; + max_width = 0; + max_precision = 3; + num_segment_tracking_lengths = num_lengths; + double current_min = 0; + for (int index = 0; index < num_lengths; index++) + { + double current_max = segment_tracking_lengths[index]; + source_segments.push_back(segment_statistic(current_min, segment_tracking_lengths[index])); + target_segments.push_back(segment_statistic(current_min, segment_tracking_lengths[index])); + current_min = current_max; + } + source_segments.push_back(segment_statistic(current_min, -1.0f)); + target_segments.push_back(segment_statistic(current_min, -1.0f)); + max_width = utilities::get_num_digits(current_min); + p_logger_ = p_logger_; + logger_type_ = 0; + } + + std::vector<segment_statistic> source_segments; + std::vector<segment_statistic> target_segments; + double total_length_source; + double total_length_target; + int max_width; + int max_precision; + int total_count_source; + int total_count_target; + int num_segment_tracking_lengths; + + void update(double length, bool is_source) + { + if (length <= 0) + return; + + std::vector<segment_statistic>* stats; + if (is_source) + { + total_count_source++; + total_length_source += length; + stats = &source_segments; + } + else + { + total_count_target++; + total_length_target += length; + stats = &target_segments; + } + for (int index = 0; index < (*stats).size(); index++) + { + segment_statistic& stat = (*stats)[index]; + if (stat.min_mm <= length && stat.max_mm > length || (index + 1) == (*stats).size()) + { + stat.count++; + break; + } + } + } + + std::string str() const { + + //if (p_logger_ != NULL) p_logger_->log(logger_type_, VERBOSE, "Building Segment Statistics."); + + std::stringstream output_stream; + std::stringstream format_stream; + const int min_column_size = 8; + int mm_col_size = max_width + max_precision + 2; // Adding 2 for the mm + int min_max_label_col_size = 4; + int percent_col_size = 9; + int totals_row_label_size = 22; + int count_col_size; + + // Calculate the count column size + int max_count = 0; + //if (p_logger_ != NULL) p_logger_->log(logger_type_, VERBOSE, "Calculating Column Size."); + + for (int index = 0; index < source_segments.size(); index++) + { + int source_count = source_segments[index].count; + int target_count = target_segments[index].count; + if (max_count < source_count) + { + max_count = source_count; + } + if (max_count < target_count) + { + max_count = target_count; + } + } + // Get the number of digits in the max count + count_col_size = utilities::get_num_digits(max_count); + // enforce the minimum of 6 + if (count_col_size < min_column_size) + { + count_col_size = min_column_size; + } + + if (max_precision > 0) + { + // We need an extra space in our column for the decimal. + mm_col_size++; + } + + // enforce the min column size + if (mm_col_size < min_column_size) + { + mm_col_size = min_column_size; + } + // Get the table width + int table_width = mm_col_size + min_max_label_col_size + mm_col_size + count_col_size + count_col_size + percent_col_size; + // Add a separator for the statistics + //output_stream << std::setw(table_width) << std::setfill('-') << "-" << "\n" << std::setfill(' ') ; + // Output the column headers + // Center the min and max column. + output_stream << utilities::center("Min", mm_col_size); + output_stream << std::setw(min_max_label_col_size) << ""; + output_stream << utilities::center("Max", mm_col_size); + // right align the source, target and change columns + output_stream << std::setw(count_col_size) << std::right << "Source"; + output_stream << std::setw(count_col_size) << std::right << "Target"; + output_stream << std::setw(percent_col_size) << std::right << "Change"; + output_stream << "\n"; + output_stream << std::setw(table_width) << std::setfill('-') << "" << std::setfill(' ') << "\n"; + output_stream << std::fixed << std::setprecision(max_precision); + for (int index = 0; index < source_segments.size(); index++) { + //extract the necessary variables from the source and target segments + double min_mm = source_segments[index].min_mm; + double max_mm = source_segments[index].max_mm; + int source_count = source_segments[index].count; + int target_count = target_segments[index].count; + // Calculate the percent change and create the string + // Construct the percent_change_string + std::string percent_change_string = utilities::get_percent_change_string(source_count, target_count, 1); + + // Create the strings to hold the column values + std::string min_mm_string; + std::string max_mm_string; + std::string source_count_string; + std::string target_count_string; + + // Clear the format stream and construct the min_mm_string + format_stream.str(std::string()); + format_stream << std::fixed << std::setprecision(max_precision) << min_mm << "mm"; + min_mm_string = format_stream.str(); + // Clear the format stream and construct the max_mm_string + format_stream.str(std::string()); + format_stream << std::fixed << std::setprecision(max_precision) << max_mm << "mm"; + max_mm_string = format_stream.str(); + // Clear the format stream and construct the source_count_string + format_stream.str(std::string()); + format_stream << std::fixed << std::setprecision(0) << source_count; + source_count_string = format_stream.str(); + // Clear the format stream and construct the target_count_string + format_stream.str(std::string()); + format_stream << std::fixed << std::setprecision(0) << target_count; + target_count_string = format_stream.str(); + // The min and max columns and the label need to be handled differently if this is the last item + if (index == source_segments.size() - 1) + { + // If we are on the last setment item, the 'min' value is the max, and there is no end + // The is because the last item contains the count of all items above the max length provided + // in the constructor + + // The 'min' column is empty here + output_stream << std::setw(mm_col_size) << std::internal << ""; + // Add the min/max label + output_stream << std::setw(min_max_label_col_size) << " >= "; + // Add the min mm string + output_stream << std::setw(mm_col_size) << std::internal << min_mm_string; + } + else + { + //if (p_logger_ != NULL) p_logger_->log(logger_type_, VERBOSE, "Adding row text."); + + // add the 'min' column + output_stream << std::setw(mm_col_size) << std::internal << min_mm_string; + // Add the min/max label + output_stream << std::setw(min_max_label_col_size) << " to "; + // Add the 'max' column + output_stream << std::setw(mm_col_size) << std::internal << max_mm_string; + } + // Add the source count + output_stream << std::setw(count_col_size) << source_count_string; + // Add the target count + output_stream << std::setw(count_col_size) << target_count_string; + // Add the percent change string + output_stream << std::setw(percent_col_size) << percent_change_string; + // End the line + output_stream << "\n"; + } + // Add the total rows separator + output_stream << std::setw(table_width) << std::setfill('-') << "" << std::setfill(' ') << "\n"; + // Add the total rows; + if (utilities::is_equal(total_length_source, total_length_target, 0.001)) + { + std::string total_distance_string; + format_stream.str(std::string()); + format_stream << std::fixed << std::setprecision(max_precision) << total_length_source << "mm"; + total_distance_string = format_stream.str(); + output_stream << std::setw(totals_row_label_size) << std::right << "Total distance:"; + output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_distance_string << "\n" << std::setfill(' '); + } + else + { + // We need to output two different distances (this probably should never happen) + // Format the total source distance string + std::string total_source_distance_string; + format_stream.str(std::string()); + format_stream << std::fixed << std::setprecision(max_precision) << total_length_source << "mm"; + total_source_distance_string = format_stream.str(); + // Add the total source distance row + output_stream << std::setw(totals_row_label_size) << std::right << "Total distance source:"; + output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_source_distance_string << "\n" << std::setfill(' '); + + // Format the total target distance string + std::string total_target_distance_string; + format_stream.str(std::string()); + format_stream << std::fixed << std::setprecision(max_precision) << total_length_target << "mm"; + total_target_distance_string = format_stream.str(); + // Add the total target distance row + output_stream << std::setw(totals_row_label_size) << std::right << "Total distance target:"; + output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_target_distance_string << "\n" << std::setfill(' '); + } + + // Add the total count rows + // Add the source count + output_stream << std::setprecision(0) << std::setw(totals_row_label_size) << std::right << "Total count source:"; + output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_count_source << "\n" << std::setfill(' '); + // Add the target count + output_stream << std::setw(totals_row_label_size) << std::right << "Total count target:"; + output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_count_target << "\n" << std::setfill(' '); + // Add the total percent change row + std::string total_percent_change_string = utilities::get_percent_change_string(total_count_source, total_count_target, 1); + output_stream << std::setw(totals_row_label_size) << std::right << "Total percent change:"; + output_stream << std::setw(table_width - totals_row_label_size) << std::setfill('.') << std::right << total_percent_change_string << "\n" << std::setfill(' '); + std::string output_string = output_stream.str(); + return output_string; + } + +private: + logger* p_logger_; + int logger_type_; +}; + +// Struct to hold the progress, statistics, and return values struct arc_welder_progress { - arc_welder_progress() { + arc_welder_progress() : segment_statistics(segment_statistic_lengths, segment_statistic_lengths_count, NULL) { percent_complete = 0.0; seconds_elapsed = 0.0; seconds_remaining = 0.0; @@ -53,6 +326,7 @@ struct arc_welder_progress { target_file_size = 0; compression_ratio = 0; compression_percent = 0; + } double percent_complete; double seconds_elapsed; @@ -66,11 +340,12 @@ struct arc_welder_progress { long source_file_position; long source_file_size; long target_file_size; + source_target_segment_statistics segment_statistics; std::string str() const { std::stringstream stream; stream << std::fixed << std::setprecision(2); - + stream << percent_complete << "% complete in " << seconds_elapsed << " seconds with " << seconds_remaining << " seconds remaining."; stream << " Gcodes Processed: " << gcodes_processed; stream << ", Current Line: " << lines_processed; @@ -80,8 +355,12 @@ struct arc_welder_progress { stream << ", Size Reduction: " << compression_percent << "% "; return stream.str(); } + std::string detail_str() const { + std::stringstream stream; + stream << "\n" << "Extrusion/Retraction Counts" << "\n" << segment_statistics.str() << "\n"; + return stream.str(); + } }; - // define the progress callback type typedef bool(*progress_callback)(arc_welder_progress); @@ -101,9 +380,7 @@ struct arc_welder_results { class arc_welder { public: - arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, double max_radius_mm, gcode_position_args args); - arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, double max_radius_mm, bool g90_g91_influences_extruder, int buffer_size); - arc_welder(std::string source_path, std::string target_path, logger * log, double resolution_mm, double max_radius_mm, bool g90_g91_influences_extruder, int buffer_size, progress_callback callback); + arc_welder(std::string source_path, std::string target_path, logger* log, double resolution_mm, double max_radius, bool g90_g91_influences_extruder, int buffer_size, progress_callback callback = NULL); void set_logger_type(int logger_type); virtual ~arc_welder(); arc_welder_results process(); @@ -111,12 +388,12 @@ public: protected: virtual bool on_progress_(const arc_welder_progress& progress); private: - arc_welder_progress get_progress_(long source_file_position, double start_clock); + arc_welder_progress get_progress_(long source_file_position, double start_clock); void add_arcwelder_comment_to_target(); void reset(); static gcode_position_args get_args_(bool g90_g91_influences_extruder, int buffer_size); progress_callback progress_callback_; - int process_gcode(parsed_command cmd, bool is_end); + int process_gcode(parsed_command cmd, bool is_end, bool is_reprocess); int write_gcode_to_file(std::string gcode); std::string get_arc_gcode_relative(double f, const std::string comment); std::string get_arc_gcode_absolute(double e, double f, const std::string comment); @@ -134,6 +411,7 @@ private: int last_gcode_line_written_; int points_compressed_; int arcs_created_; + source_target_segment_statistics segment_statistics_; long get_file_size(const std::string& file_path); double get_time_elapsed(double start_clock, double end_clock); double get_next_update_time() const; @@ -141,9 +419,9 @@ private: array_list<unwritten_command> unwritten_commands_; segmented_arc current_arc_; std::ofstream output_file_; - + // We don't care about the printer settings, except for g91 influences extruder. - gcode_position * p_source_position_; + gcode_position* p_source_position_; double previous_feedrate_; bool previous_is_extruder_relative_; gcode_parser parser_; diff --git a/ArcWelder/segmented_arc.cpp b/ArcWelder/segmented_arc.cpp index 220e740..297304b 100644 --- a/ArcWelder/segmented_arc.cpp +++ b/ArcWelder/segmented_arc.cpp @@ -31,11 +31,12 @@ #include <stdio.h> #include <cmath> -segmented_arc::segmented_arc() : segmented_arc(DEFAULT_MIN_SEGMENTS, DEFAULT_MAX_SEGMENTS, DEFAULT_RESOLUTION_MM, DEFAULT_MAX_RADIUS_MM) +segmented_arc::segmented_arc() : segmented_shape(DEFAULT_MIN_SEGMENTS, DEFAULT_MAX_SEGMENTS, DEFAULT_RESOLUTION_MM) { + max_radius_mm_ = DEFAULT_MAX_RADIUS_MM; } -segmented_arc::segmented_arc(int min_segments, int max_segments, double resolution_mm, double max_radius_mm) : segmented_shape(min_segments, max_segments, resolution_mm) +segmented_arc::segmented_arc(int min_segments = DEFAULT_MIN_SEGMENTS, int max_segments = DEFAULT_MAX_SEGMENTS, double resolution_mm = DEFAULT_RESOLUTION_MM, double max_radius_mm = DEFAULT_MAX_RADIUS_MM) : segmented_shape(min_segments, max_segments, resolution_mm) { if (max_radius_mm > DEFAULT_MAX_RADIUS_MM) max_radius_mm_ = DEFAULT_MAX_RADIUS_MM; else max_radius_mm_ = max_radius_mm; diff --git a/ArcWelder/segmented_arc.h b/ArcWelder/segmented_arc.h index 9a412ad..04dbeea 100644 --- a/ArcWelder/segmented_arc.h +++ b/ArcWelder/segmented_arc.h @@ -54,7 +54,6 @@ private: bool try_get_arc_(const circle& c, arc& target_arc); std::string get_shape_gcode_(bool has_e, double e, double f) const; circle arc_circle_; - int test_count_ = 0; double max_radius_mm_; }; diff --git a/ArcWelder/segmented_shape.cpp b/ArcWelder/segmented_shape.cpp index 312a7dc..cd5d808 100644 --- a/ArcWelder/segmented_shape.cpp +++ b/ArcWelder/segmented_shape.cpp @@ -227,18 +227,18 @@ point circle::get_closest_point(const point& p) const #pragma region Arc Functions bool arc::try_create_arc(const circle& c, const point& start_point, const point& mid_point, const point& end_point, double approximate_length, double resolution, arc& target_arc) { - point p1 = c.get_closest_point(start_point); - point p2 = c.get_closest_point(mid_point); - point p3 = c.get_closest_point(end_point); + //point p1 = c.get_closest_point(start_point); + //point p2 = c.get_closest_point(mid_point); + //point p3 = c.get_closest_point(end_point); /*// Get the radians between p1 and p2 (short angle) double p1_p2_rad = c.get_radians(p1, p2); double p2_p3_rad = c.get_radians(p2, p3); double p3_p1_rad = c.get_radians(p3, p1); */ - double polar_start_theta = c.get_polar_radians(p1); - double polar_mid_theta = c.get_polar_radians(p2); - double polar_end_theta = c.get_polar_radians(p3); + double polar_start_theta = c.get_polar_radians(start_point); + double polar_mid_theta = c.get_polar_radians(mid_point); + double polar_end_theta = c.get_polar_radians(end_point); // variable to hold radians double angle_radians = 0; @@ -306,11 +306,7 @@ bool arc::try_create_arc(const circle& c, const array_list<point>& points, doubl } #pragma endregion -segmented_shape::segmented_shape() : segmented_shape(DEFAULT_MIN_SEGMENTS, DEFAULT_MAX_SEGMENTS, DEFAULT_RESOLUTION_MM ) -{ -} - -segmented_shape::segmented_shape(int min_segments, int max_segments, double resolution_mm) : points_(max_segments) +segmented_shape::segmented_shape(int min_segments = DEFAULT_MIN_SEGMENTS, int max_segments = DEFAULT_MAX_SEGMENTS, double resolution_mm = DEFAULT_RESOLUTION_MM) : points_(max_segments) { max_segments_ = max_segments; diff --git a/ArcWelder/segmented_shape.h b/ArcWelder/segmented_shape.h index 9ca480b..957ceab 100644 --- a/ArcWelder/segmented_shape.h +++ b/ArcWelder/segmented_shape.h @@ -174,7 +174,7 @@ double distance_from_segment(segment s, point p); class segmented_shape { public: - segmented_shape(); + segmented_shape(int min_segments, int max_segments, double resolution_mm); segmented_shape& operator=(const segmented_shape& pos); virtual ~segmented_shape(); diff --git a/ArcWelder/unwritten_command.h b/ArcWelder/unwritten_command.h index 5591dbe..5c6f151 100644 --- a/ArcWelder/unwritten_command.h +++ b/ArcWelder/unwritten_command.h @@ -31,20 +31,25 @@ struct unwritten_command is_extruder_relative = false; e_relative = 0; offset_e = 0; + extrusion_length = 0; } - unwritten_command(parsed_command &cmd, bool is_relative) { - is_relative = false; + unwritten_command(parsed_command &cmd, bool is_relative, double command_length) { + is_extruder_relative = is_relative; command = cmd; + extrusion_length = command_length; } - unwritten_command(position* p) { + unwritten_command(position* p, double command_length) { + e_relative = p->get_current_extruder().e_relative; offset_e = p->get_current_extruder().get_offset_e(); is_extruder_relative = p->is_extruder_relative; command = p->command; + extrusion_length = command_length; } bool is_extruder_relative; double e_relative; double offset_e; + double extrusion_length; parsed_command command; std::string to_string(bool rewrite, std::string additional_comment) diff --git a/ArcWelderConsole/ArcWelderConsole.cpp b/ArcWelderConsole/ArcWelderConsole.cpp index ae31e10..b9cad50 100644 --- a/ArcWelderConsole/ArcWelderConsole.cpp +++ b/ArcWelderConsole/ArcWelderConsole.cpp @@ -112,7 +112,7 @@ int main(int argc, char* argv[]) log_level_string = log_level_arg.getValue(); log_level_value = -1; - for (unsigned int log_name_index = 0; log_name_index < log_level_names.size(); log_name_index++) + for (unsigned int log_name_index = 0; log_name_index < log_level_names_size; log_name_index++) { if (log_level_string == log_level_names[log_name_index]) { diff --git a/ArcWelderTest/ArcWelderTest.cpp b/ArcWelderTest/ArcWelderTest.cpp index 8601ba6..b330d98 100644 --- a/ArcWelderTest/ArcWelderTest.cpp +++ b/ArcWelderTest/ArcWelderTest.cpp @@ -52,6 +52,7 @@ int run_tests(int argc, char* argv[]) { std::cout << "Processing test run " << index + 1 << " of " << num_runs << ".\r\n"; TestAntiStutter(ANTI_STUTTER_TEST); + //TestParsingCase(); //TestDoubleToString(); //TestInverseProcessor(); //TestCircularBuffer(); @@ -240,10 +241,10 @@ static void TestAntiStutter(std::string filePath) //logger_levels.push_back(log_levels::DEBUG); logger_levels.push_back(log_levels::INFO); logger* p_logger = new logger(logger_names, logger_levels); - p_logger->set_log_level(INFO); + p_logger->set_log_level(VERBOSE); //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(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(BENCHY_DIFFICULT, "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\test_output.gcode", p_logger, max_resolution, max_radius_mm, false, 50, on_progress); + arc_welder arc_welder_obj(BENCHY_L1_DIFFICULT, "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\test_output.gcode", p_logger, max_resolution, max_radius_mm, false, 50, on_progress); //BENCHY_LAYER_1GCODE //SMALL_TEST //FACE_SHIELD @@ -261,8 +262,9 @@ static void TestAntiStutter(std::string filePath) //DIFFICULT_CURVES //ISSUE_PRICKLYPEAR_LAYER_0_114 //BARBARIAN - // Benchy_L1_Difficult - arc_welder_obj.process(); + // BENCHY_L1_DIFFICULT + arc_welder_results results = arc_welder_obj.process(); + p_logger->log(0, INFO, results.progress.detail_str()); p_logger->log(0, INFO, "Processing Complete."); delete p_logger; } @@ -288,3 +290,14 @@ void TestDoubleToString() } } + +static void TestParsingCase() +{ + gcode_parser parser; + //parsed_command command = parser.parse_gcode(" G0 X1 y2 ; test", true); + parsed_command command2 = parser.parse_gcode(" M73 P0 R93", true); + //parsed_command command2 = parser.parse_gcode("M204 P2000 R1500 T2000 ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2", true); + parsed_command command3 = parser.parse_gcode("G0 X1 y2; test", true); + + +}
\ No newline at end of file diff --git a/ArcWelderTest/ArcWelderTest.h b/ArcWelderTest/ArcWelderTest.h index 940c4aa..4ce0ccd 100644 --- a/ArcWelderTest/ArcWelderTest.h +++ b/ArcWelderTest/ArcWelderTest.h @@ -46,6 +46,7 @@ static gcode_position_args get_5_extruder_position_args(); static void TestAntiStutter(std::string filePath); static bool on_progress(arc_welder_progress progress); static void TestDoubleToString(); +static void TestParsingCase(); static std::string ANTI_STUTTER_TEST = "C:\\Users\\Brad\\Documents\\3DPrinter\\AntiStutter\\5x5_cylinder_2000Fn_0.2mm_PLA_MK2.5MMU2_4m.gcode"; diff --git a/GcodeProcessorLib/GcodeProcessorLib.vcxproj b/GcodeProcessorLib/GcodeProcessorLib.vcxproj index ec6b6a7..0457545 100644 --- a/GcodeProcessorLib/GcodeProcessorLib.vcxproj +++ b/GcodeProcessorLib/GcodeProcessorLib.vcxproj @@ -129,7 +129,7 @@ <ClCompile> <WarningLevel>Level3</WarningLevel> <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ConformanceMode>true</ConformanceMode> </ClCompile> <Link> diff --git a/GcodeProcessorLib/gcode_parser.cpp b/GcodeProcessorLib/gcode_parser.cpp index a4e7d0a..b12b4e9 100644 --- a/GcodeProcessorLib/gcode_parser.cpp +++ b/GcodeProcessorLib/gcode_parser.cpp @@ -104,14 +104,25 @@ gcode_parser::~gcode_parser() parsed_command gcode_parser::parse_gcode(const char * gcode) { + parsed_command p_cmd; + try_parse_gcode(gcode, p_cmd, true); + return p_cmd; +} +parsed_command gcode_parser::parse_gcode(const char* gcode, bool preserve_format) +{ parsed_command p_cmd; - try_parse_gcode(gcode, p_cmd); + try_parse_gcode(gcode, p_cmd, preserve_format); return p_cmd; } + +bool gcode_parser::try_parse_gcode(const char* gcode, parsed_command& command) +{ + return try_parse_gcode(gcode, command, true) ; +} // Superfast gcode parser - v2 -bool gcode_parser::try_parse_gcode(const char * gcode, parsed_command & command) +bool gcode_parser::try_parse_gcode(const char * gcode, parsed_command & command, bool preserve_format) { // Create a command char * p_gcode = const_cast<char *>(gcode); @@ -130,6 +141,9 @@ bool gcode_parser::try_parse_gcode(const char * gcode, parsed_command & command) command.is_empty = false; break; } + else if (preserve_format) { + command.gcode.push_back(c); + } p_gcode++; } command.command = ""; @@ -138,6 +152,9 @@ bool gcode_parser::try_parse_gcode(const char * gcode, parsed_command & command) command.is_empty = false; bool has_seen_character = false; + + bool is_text_only_parameter = text_only_functions_.find(command.command) != text_only_functions_.end(); + while (true) { char cur_char = *p_gcode; @@ -145,23 +162,25 @@ bool gcode_parser::try_parse_gcode(const char * gcode, parsed_command & command) break; else if (cur_char > 32 || (cur_char == ' ' && has_seen_character)) { - if (cur_char >= 'a' && cur_char <= 'z') + if (!preserve_format && !is_text_only_parameter && (cur_char >= 'a' && cur_char <= 'z')) command.gcode.push_back(cur_char - 32); else command.gcode.push_back(cur_char); has_seen_character = true; } + else if (preserve_format) + { + command.gcode.push_back(cur_char); + } p_gcode++; } - command.gcode = utilities::rtrim(command.gcode); + if (!preserve_format){ + command.gcode = utilities::rtrim(command.gcode); + } - if (command.is_known_command) + if (command.is_known_command && parsable_commands_.find(command.command) != parsable_commands_.end()) { - if (parsable_commands_.find(command.command) == parsable_commands_.end()) - { - return true; - } if (command.command.length() > 0 && command.command == "@OCTOLAPSE") { @@ -188,7 +207,7 @@ bool gcode_parser::try_parse_gcode(const char * gcode, parsed_command & command) } else if ( - text_only_functions_.find(command.command) != text_only_functions_.end() || + is_text_only_parameter || ( command.command.length() > 0 && command.command[0] == '@' ) @@ -232,6 +251,7 @@ bool gcode_parser::try_parse_gcode(const char * gcode, parsed_command & command) } } } + try_extract_comment(&p_gcode, &(command.comment)); diff --git a/GcodeProcessorLib/gcode_parser.h b/GcodeProcessorLib/gcode_parser.h index 50cb01b..714dfe4 100644 --- a/GcodeProcessorLib/gcode_parser.h +++ b/GcodeProcessorLib/gcode_parser.h @@ -35,7 +35,9 @@ public: gcode_parser(); ~gcode_parser(); bool try_parse_gcode(const char * gcode, parsed_command & command); + bool try_parse_gcode(const char* gcode, parsed_command& command, bool preserve_format); parsed_command parse_gcode(const char * gcode); + parsed_command parse_gcode(const char* gcode, bool preserve_format); private: gcode_parser(const gcode_parser &source); // Variables and lookups diff --git a/GcodeProcessorLib/logger.cpp b/GcodeProcessorLib/logger.cpp index 0118e52..8511777 100644 --- a/GcodeProcessorLib/logger.cpp +++ b/GcodeProcessorLib/logger.cpp @@ -19,7 +19,9 @@ // You can contact the author at the following email address: // FormerLurker@pm.me //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - +#if _MSC_VER > 1200 +#define _CRT_SECURE_NO_DEPRECATE +#endif #include "logger.h" logger::logger(std::vector<std::string> names, std::vector<int> levels) { // set to true by default, but can be changed by inheritance to support mandatory innitialization (for python or other integrations) @@ -89,20 +91,8 @@ void logger::create_log_message(const int logger_type, const int log_level, cons // example message // 2020-04-20 21:36:59,414 - arc_welder.__init__ - INFO - MESSAGE_GOES_HERE - // Create the time string in YYYY-MM-DD HH:MM:SS format - std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); - std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000; - const time_t now_time = std::chrono::system_clock::to_time_t(now); - struct tm tstruct; - char buf[25]; - tstruct = *localtime(&now_time); - // DOESN'T WORK WITH ALL COMPILERS... - //localtime_s(&tstruct, &now_time); - strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S.", &tstruct); - output = buf; - std::string s_miliseconds = std::to_string(ms.count()); - // Add the milliseconds, padded with 0s, to the output - output.append(std::string(3 - s_miliseconds.length(), '0') + s_miliseconds); + // Create the time string in YYYY-MM-DD HH:MM:SS.ms format + logger::get_timestamp(output); // Add a spacer output.append(" - "); // Add the logger name @@ -148,3 +138,46 @@ void logger::log(const int logger_type, const int log_level, const std::string& std::cout.flush(); } + +void logger::get_timestamp(std::string ×tamp) +{ + std::time_t rawtime; + std::tm* timeinfo; + char buffer[80]; + + std::time(&rawtime); + timeinfo = std::localtime(&rawtime); + std::strftime(buffer, 80, "%Y-%m-%d %H:%M:%S.", timeinfo); + + timestamp = buffer; + clock_t t = std::clock(); + int ms = static_cast<int>((t / CLOCKS_PER_MS)) % 1000; + + std::string s_miliseconds; + sprintf(buffer, "%d", ms) ;// std::to_string(ms); + s_miliseconds = buffer; + timestamp.append(std::string(3 - s_miliseconds.length(), '0') + s_miliseconds); + +} + +/* + +Severity Code Description Project File Line Suppression State + + + +std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000; + const time_t now_time = std::chrono::system_clock::to_time_t(now); + struct tm tstruct; + char buf[25]; + tstruct = *localtime(&now_time); + // DOESN'T WORK WITH ALL COMPILERS... + //localtime_s(&tstruct, &now_time); + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S.", &tstruct); + output = buf; + std::string s_miliseconds = std::to_string(ms.count()); + // Add the milliseconds, padded with 0s, to the output + output.append(std::string(3 - s_miliseconds.length(), '0') + s_miliseconds); + +*/
\ No newline at end of file diff --git a/GcodeProcessorLib/logger.h b/GcodeProcessorLib/logger.h index c681674..9db2df1 100644 --- a/GcodeProcessorLib/logger.h +++ b/GcodeProcessorLib/logger.h @@ -25,12 +25,16 @@ #include <vector> #include <cstdarg> #include <stdio.h> -#include <chrono> +#include <ctime> +//#include <chrono> #include <array> #define LOG_LEVEL_COUNT 7 +#define CLOCKS_PER_MS (CLOCKS_PER_SEC / 1000.0) enum log_levels { NOSET, VERBOSE, DEBUG, INFO, WARNING , ERROR, CRITICAL}; -const std::array<std::string, 7> log_level_names = { {"NOSET", "VERBOSE", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"} }; +//const std::array<std::string, 7> log_level_names = { {"NOSET", "VERBOSE", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"} }; +static const int log_level_names_size = 7; +static const char* log_level_names[] = {"NOSET", "VERBOSE", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}; const static int log_level_values[LOG_LEVEL_COUNT] = { 0, 5, 10, 20, 30, 40, 50}; class logger @@ -59,6 +63,7 @@ private: std::string* logger_names_; int * logger_levels_; int num_loggers_; + static void get_timestamp(std::string ×tamp); }; diff --git a/GcodeProcessorLib/utilities.cpp b/GcodeProcessorLib/utilities.cpp index 5e90768..0af96a9 100644 --- a/GcodeProcessorLib/utilities.cpp +++ b/GcodeProcessorLib/utilities.cpp @@ -23,6 +23,7 @@ #include <cmath> #include <sstream> #include <iostream> +#include <iomanip> // Had to increase the zero tolerance because prusa slicer doesn't always retract enough while wiping. const double ZERO_TOLERANCE = 0.000005; @@ -123,6 +124,13 @@ std::string utilities::to_string(double value) return os.str(); } +std::string utilities::to_string(int value) +{ + std::ostringstream os; + os << value; + return os.str(); +} + char * utilities::to_string(double value, unsigned short precision, char * str) { char reversed_int[20]; @@ -219,3 +227,60 @@ std::istream& utilities::safe_get_line(std::istream& is, std::string& t) } } } + +std::string utilities::center(std::string input, int width) +{ + int input_width = input.length(); + int difference = width - input_width; + if (difference < 1) + { + return input; + } + int left_padding = difference /2; + int right_padding = width - left_padding - input_width; + return std::string(left_padding, ' ') + input + std::string(right_padding, ' '); +} + +std::string utilities::get_percent_change_string(int v1, int v2, int precision) +{ + std::stringstream format_stream; + format_stream.str(std::string()); + std::string percent_change_string; + if (v1 == 0) + { + if (v2 > 0) + { + format_stream << "INF"; + } + else + { + format_stream << std::fixed << std::setprecision(1) << 0.0 << "%"; + } + } + else + { + double percent_change = (((double)v2 - (double)v1) / (double)v1) * 100.0; + format_stream << std::fixed << std::setprecision(precision) << percent_change << "%"; + } + return format_stream.str(); +} + +int utilities::get_num_digits(int x) +{ + x = abs(x); + return (x < 10 ? 1 : + (x < 100 ? 2 : + (x < 1000 ? 3 : + (x < 10000 ? 4 : + (x < 100000 ? 5 : + (x < 1000000 ? 6 : + (x < 10000000 ? 7 : + (x < 100000000 ? 8 : + (x < 1000000000 ? 9 : + 10))))))))); +} + +int utilities::get_num_digits(double x) +{ + return get_num_digits((int) x); +} diff --git a/GcodeProcessorLib/utilities.h b/GcodeProcessorLib/utilities.h index db858ad..a6ddbd2 100644 --- a/GcodeProcessorLib/utilities.h +++ b/GcodeProcessorLib/utilities.h @@ -44,12 +44,17 @@ public: static double get_cartesian_distance(double x1, double y1, double x2, double y2); static double get_cartesian_distance(double x1, double y1, double z1, double x2, double y2, double z2); static std::string to_string(double value); + static std::string to_string(int value); static char* to_string(double value, unsigned short precision, char* str); static std::string ltrim(const std::string& s); 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); - + static std::string center(std::string input, int width); + static std::string get_percent_change_string(int v1, int v2, int precision); + + static int get_num_digits(int x); + static int get_num_digits(double x); protected: static const std::string WHITESPACE_; private: diff --git a/PyArcWelder/py_arc_welder.cpp b/PyArcWelder/py_arc_welder.cpp index 302aa95..0eacf39 100644 --- a/PyArcWelder/py_arc_welder.cpp +++ b/PyArcWelder/py_arc_welder.cpp @@ -22,35 +22,55 @@ //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include "py_arc_welder.h" - -PyObject* py_arc_welder::build_py_progress(arc_welder_progress progress) +PyObject* py_arc_welder::build_py_progress(const arc_welder_progress& progress) { - PyObject* py_progress = Py_BuildValue("{s:d,s:d,s:d,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:f,s:f}", - 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_position", - progress.source_file_position, - u8"source_file_size", - progress.source_file_size, - u8"target_file_size", - progress.target_file_size, - u8"compression_ratio", - progress.compression_ratio, - u8"compression_percent", - progress.compression_percent + std::string segment_statistics = progress.segment_statistics.str(); + PyObject* pyMessage = gcode_arc_converter::PyUnicode_SafeFromString(segment_statistics); + if (pyMessage == NULL) + return NULL; + PyObject* py_progress = Py_BuildValue("{s:d,s:d,s:d,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:f,s:f,s:f,s:f,s:i,s:i}", + "percent_complete", + progress.percent_complete, //1 + "seconds_elapsed", + progress.seconds_elapsed, //2 + "seconds_remaining", + progress.seconds_remaining, //3 + "gcodes_processed", + progress.gcodes_processed, //4 + "lines_processed", + progress.lines_processed, //5 + "points_compressed", + progress.points_compressed, //6 + "arcs_created", + progress.arcs_created, //7 + "source_file_position", + progress.source_file_position, //8 + "source_file_size", + progress.source_file_size, //9 + "target_file_size", + progress.target_file_size, //10 + "compression_ratio", + progress.compression_ratio, //11 + "compression_percent", + progress.compression_percent, //12 + "source_file_total_length", + progress.segment_statistics.total_length_source, //13 + "target_file_total_length", + progress.segment_statistics.total_length_target, //14 + "source_file_total_count", + progress.segment_statistics.total_count_source, //15 + "target_file_total_count", + progress.segment_statistics.total_length_target //16 ); + + if (py_progress == NULL) + { + return NULL; + } + // Due to a CRAZY issue, I have to add this item after building the py_progress object, + // else it crashes in python 2.7. Looking forward to retiring this backwards + // compatible code... + PyDict_SetItemString(py_progress, "segment_statistics_text", pyMessage); return py_progress; } @@ -58,12 +78,14 @@ bool py_arc_welder::on_progress_(const arc_welder_progress& progress) { PyObject* py_dict = py_arc_welder::build_py_progress(progress); if (py_dict == NULL) + { return false; + } PyObject* func_args = Py_BuildValue("(O)", py_dict); if (func_args == NULL) { Py_DECREF(py_dict); - return true; + return false; // This was returning true, I think it was a typo. Making a note just in case. } PyGILState_STATE gstate = PyGILState_Ensure(); diff --git a/PyArcWelder/py_arc_welder.h b/PyArcWelder/py_arc_welder.h index ef56715..1b70980 100644 --- a/PyArcWelder/py_arc_welder.h +++ b/PyArcWelder/py_arc_welder.h @@ -41,7 +41,7 @@ public: virtual ~py_arc_welder() { } - static PyObject* build_py_progress(arc_welder_progress progress); + static PyObject* build_py_progress(const arc_welder_progress& progress); protected: virtual bool on_progress_(const arc_welder_progress& progress); private: diff --git a/PyArcWelder/py_arc_welder_extension.cpp b/PyArcWelder/py_arc_welder_extension.cpp index 07f9dfc..ea24f59 100644 --- a/PyArcWelder/py_arc_welder_extension.cpp +++ b/PyArcWelder/py_arc_welder_extension.cpp @@ -202,13 +202,13 @@ extern "C" PyObject* p_results = Py_BuildValue( "{s:i,s:i,s:s,s:O}", - u8"success", + "success", results.success, - u8"cancelled", + "cancelled", results.cancelled, - u8"message", + "message", results.message.c_str(), - u8"progress", + "progress", p_progress ); return p_results; |