diff --git a/README.md b/README.md index b76b583..85f6489 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,12 @@ Follow these steps to install Shake&Tune on your printer: # printer.cfg file. If you want to see the macros in the webui, set this to True. # timeout: 600 # The maximum time in seconds to let Shake&Tune process the data and generate the graphs. + # measurements_chunk_size: 15 + # The number of measurements to keep in RAM before writing them to disk. This is useful + # to avoid running out of memory when processing large datasets like for the vibrations + # measurements. If you get Timer Too Close errors, try reducing this value (minimum is 2). + # dpi: 300 + # The resolution of the generated graphs. This usually doesn't need to be changed. ``` Don't forget to check out **[Shake&Tune documentation here](./docs/README.md)**. diff --git a/shaketune/commands/axes_map_calibration.py b/shaketune/commands/axes_map_calibration.py index 95c4939..604fa12 100644 --- a/shaketune/commands/axes_map_calibration.py +++ b/shaketune/commands/axes_map_calibration.py @@ -69,7 +69,7 @@ def axes_map_calibration(gcmd, config, st_process: ShakeTuneProcess) -> None: toolhead.move([mid_x - SEGMENT_LENGTH / 2, mid_y - SEGMENT_LENGTH / 2, z_height, E], feedrate_travel) toolhead.dwell(0.5) - measurements_manager = MeasurementsManager() + measurements_manager = MeasurementsManager(st_process.get_st_config().chunk_size) # Start the measurements and do the movements (+X, +Y and then +Z) accelerometer.start_recording(measurements_manager, name='axesmap_X', append_time=True) diff --git a/shaketune/commands/axes_shaper_calibration.py b/shaketune/commands/axes_shaper_calibration.py index 5aaf807..a4615cf 100644 --- a/shaketune/commands/axes_shaper_calibration.py +++ b/shaketune/commands/axes_shaper_calibration.py @@ -95,7 +95,7 @@ def axes_shaper_calibration(gcmd, config, st_process: ShakeTuneProcess) -> None: a for a in AXIS_CONFIG if a['axis'] == axis_input or (axis_input == 'all' and a['axis'] in ('x', 'y')) ] for config in filtered_config: - measurements_manager = MeasurementsManager() + measurements_manager = MeasurementsManager(st_process.get_st_config().chunk_size) # First we need to find the accelerometer chip suited for the axis accel_chip = Accelerometer.find_axis_accelerometer(printer, config['axis']) diff --git a/shaketune/commands/compare_belts_responses.py b/shaketune/commands/compare_belts_responses.py index 664649e..d700c23 100644 --- a/shaketune/commands/compare_belts_responses.py +++ b/shaketune/commands/compare_belts_responses.py @@ -103,7 +103,7 @@ def compare_belts_responses(gcmd, config, st_process: ShakeTuneProcess) -> None: else: input_shaper = None - measurements_manager = MeasurementsManager() + measurements_manager = MeasurementsManager(st_process.get_st_config().chunk_size) # Run the test for each axis for config in filtered_config: diff --git a/shaketune/commands/create_vibrations_profile.py b/shaketune/commands/create_vibrations_profile.py index ccd1e84..61ea653 100644 --- a/shaketune/commands/create_vibrations_profile.py +++ b/shaketune/commands/create_vibrations_profile.py @@ -81,7 +81,7 @@ def create_vibrations_profile(gcmd, config, st_process: ShakeTuneProcess) -> Non toolhead.move([mid_x - 15, mid_y - 15, z_height, E], feedrate_travel) toolhead.dwell(0.5) - measurements_manager = MeasurementsManager() + measurements_manager = MeasurementsManager(st_process.get_st_config().chunk_size) nb_speed_samples = int((max_speed - MIN_SPEED) / speed_increment + 1) for curr_angle in main_angles: diff --git a/shaketune/commands/excitate_axis_at_freq.py b/shaketune/commands/excitate_axis_at_freq.py index f6d6d45..c6f544f 100644 --- a/shaketune/commands/excitate_axis_at_freq.py +++ b/shaketune/commands/excitate_axis_at_freq.py @@ -42,7 +42,7 @@ def excitate_axis_at_freq(gcmd, config, st_process: ShakeTuneProcess) -> None: if k_accelerometer is None: raise gcmd.error(f'Accelerometer chip [{accel_chip}] was not found!') accelerometer = Accelerometer(k_accelerometer, printer.get_reactor()) - measurements_manager = MeasurementsManager() + measurements_manager = MeasurementsManager(st_process.get_st_config().chunk_size) ConsoleOutput.print(f'Excitating {axis.upper()} axis at {freq}Hz for {duration} seconds') diff --git a/shaketune/graph_creators/axes_map_graph_creator.py b/shaketune/graph_creators/axes_map_graph_creator.py index 9bb54a8..bf6072f 100644 --- a/shaketune/graph_creators/axes_map_graph_creator.py +++ b/shaketune/graph_creators/axes_map_graph_creator.py @@ -491,7 +491,7 @@ def main(): if options.output is None: opts.error('You must specify an output file.png to use the script (option -o)') - measurements_manager = MeasurementsManager() + measurements_manager = MeasurementsManager(10) if args[0].endswith('.csv'): measurements_manager.load_from_csvs(args) elif args[0].endswith('.stdata'): diff --git a/shaketune/graph_creators/belts_graph_creator.py b/shaketune/graph_creators/belts_graph_creator.py index 807e065..216764c 100644 --- a/shaketune/graph_creators/belts_graph_creator.py +++ b/shaketune/graph_creators/belts_graph_creator.py @@ -599,7 +599,7 @@ def main(): if options.output is None: opts.error('You must specify an output file.png to use the script (option -o)') - measurements_manager = MeasurementsManager() + measurements_manager = MeasurementsManager(10) if args[0].endswith('.csv'): measurements_manager.load_from_csvs(args) elif args[0].endswith('.stdata'): diff --git a/shaketune/graph_creators/shaper_graph_creator.py b/shaketune/graph_creators/shaper_graph_creator.py index 90c2749..0523f1b 100644 --- a/shaketune/graph_creators/shaper_graph_creator.py +++ b/shaketune/graph_creators/shaper_graph_creator.py @@ -722,7 +722,7 @@ def main(): if options.max_smoothing is not None and options.max_smoothing < 0.05: opts.error('Too small max_smoothing specified (must be at least 0.05)') - measurements_manager = MeasurementsManager() + measurements_manager = MeasurementsManager(10) if args[0].endswith('.csv'): measurements_manager.load_from_csvs(args) elif args[0].endswith('.stdata'): diff --git a/shaketune/graph_creators/static_graph_creator.py b/shaketune/graph_creators/static_graph_creator.py index 0fed522..dedd3e7 100644 --- a/shaketune/graph_creators/static_graph_creator.py +++ b/shaketune/graph_creators/static_graph_creator.py @@ -208,7 +208,7 @@ def main(): if options.output is None: opts.error('You must specify an output file.png to use the script (option -o)') - measurements_manager = MeasurementsManager() + measurements_manager = MeasurementsManager(10) if args[0].endswith('.csv'): measurements_manager.load_from_csvs(args) elif args[0].endswith('.stdata'): diff --git a/shaketune/graph_creators/vibrations_graph_creator.py b/shaketune/graph_creators/vibrations_graph_creator.py index 0c2ba6b..65df941 100644 --- a/shaketune/graph_creators/vibrations_graph_creator.py +++ b/shaketune/graph_creators/vibrations_graph_creator.py @@ -911,7 +911,7 @@ def main(): if options.kinematics not in {'cartesian', 'corexy', 'corexz'}: opts.error('Only cartesian, corexy and corexz kinematics are supported by this tool at the moment!') - measurements_manager = MeasurementsManager() + measurements_manager = MeasurementsManager(10) if args[0].endswith('.csv'): measurements_manager.load_from_csvs(args) elif args[0].endswith('.stdata'): diff --git a/shaketune/helpers/accelerometer.py b/shaketune/helpers/accelerometer.py index dd1fd3f..e6df50e 100644 --- a/shaketune/helpers/accelerometer.py +++ b/shaketune/helpers/accelerometer.py @@ -27,8 +27,6 @@ Sample = Tuple[float, float, float, float] SamplesList = List[Sample] -CHUNK_SIZE = 15 # Maximum number of measurements to keep in memory at once - class Measurement(TypedDict): name: str @@ -36,7 +34,8 @@ class Measurement(TypedDict): class MeasurementsManager: - def __init__(self): + def __init__(self, chunk_size: int): + self._chunk_size = chunk_size self.measurements: List[Measurement] = [] self._uuid = str(uuid.uuid4())[:8] self._temp_dir = Path(f'/tmp/shaketune_{self._uuid}') @@ -59,7 +58,7 @@ def append_samples_to_last_measurement(self, additional_samples: SamplesList): def add_measurement(self, name: str, samples: SamplesList = None): samples = samples if samples is not None else [] self.measurements.append({'name': name, 'samples': samples}) - if len(self.measurements) > CHUNK_SIZE: + if len(self.measurements) > self._chunk_size: self._save_chunk() def _save_chunk(self): diff --git a/shaketune/shaketune.py b/shaketune/shaketune.py index a8e60a9..f913515 100644 --- a/shaketune/shaketune.py +++ b/shaketune/shaketune.py @@ -36,6 +36,7 @@ DEFAULT_DPI = 150 DEFAULT_TIMEOUT = 600 DEFAULT_SHOW_MACROS = True +DEFAULT_MEASUREMENTS_CHUNK_SIZE = 15 # Maximum number of measurements to keep in memory at once ST_COMMANDS = { 'EXCITATE_AXIS_AT_FREQ': ( 'Maintain a specified excitation frequency for a period ' @@ -80,7 +81,8 @@ def _initialize_config(self, config) -> None: keep_n_results = config.getint('number_of_results_to_keep', default=DEFAULT_NUMBER_OF_RESULTS, minval=0) keep_raw_data = config.getboolean('keep_raw_data', default=DEFAULT_KEEP_RAW_DATA) dpi = config.getint('dpi', default=DEFAULT_DPI, minval=100, maxval=500) - self._st_config = ShakeTuneConfig(result_folder_path, keep_n_results, keep_raw_data, dpi) + m_chunk_size = config.getint('measurements_chunk_size', default=DEFAULT_MEASUREMENTS_CHUNK_SIZE, minval=2) + self._st_config = ShakeTuneConfig(result_folder_path, keep_n_results, keep_raw_data, m_chunk_size, dpi) self.timeout = config.getfloat('timeout', DEFAULT_TIMEOUT, above=0.0) self._show_macros = config.getboolean('show_macros_in_webui', default=DEFAULT_SHOW_MACROS) diff --git a/shaketune/shaketune_config.py b/shaketune/shaketune_config.py index d10cfd1..80020b8 100644 --- a/shaketune/shaketune_config.py +++ b/shaketune/shaketune_config.py @@ -30,12 +30,14 @@ def __init__( result_folder: Path = RESULTS_BASE_FOLDER, keep_n_results: int = 3, keep_raw_data: bool = False, + chunk_size: int = 15, dpi: int = 150, ) -> None: self._result_folder = result_folder self.keep_n_results = keep_n_results self.keep_raw_data = keep_raw_data + self.chunk_size = chunk_size self.dpi = dpi self.klipper_folder = KLIPPER_FOLDER diff --git a/shaketune/shaketune_process.py b/shaketune/shaketune_process.py index 2fd4aa1..b645175 100644 --- a/shaketune/shaketune_process.py +++ b/shaketune/shaketune_process.py @@ -30,6 +30,9 @@ def __init__(self, st_config: ShakeTuneConfig, reactor, graph_creator, timeout: def get_graph_creator(self): return self.graph_creator + def get_st_config(self): + return self._config + def run(self, measurements_manager: MeasurementsManager) -> None: # Start the target function in a new process (a thread is known to cause issues with Klipper and CANbus due to the GIL) self._process = Process(