Skip to content

Commit

Permalink
offload the finish measurement and get sample in a separate thread
Browse files Browse the repository at this point in the history
  • Loading branch information
Frix-x committed Sep 15, 2024
1 parent fe78456 commit 82c848f
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 7 deletions.
3 changes: 3 additions & 0 deletions shaketune/commands/axes_map_calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,21 @@ 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], speed)
toolhead.dwell(0.5)
accelerometer.stop_recording()
accelerometer.wait_for_samples(printer.get_reactor())
toolhead.dwell(0.5)
accelerometer.start_recording(measurements_manager, name='axesmap_Y', append_time=True)
toolhead.dwell(0.5)
toolhead.move([mid_x + SEGMENT_LENGTH / 2, mid_y + SEGMENT_LENGTH / 2, z_height, E], speed)
toolhead.dwell(0.5)
accelerometer.stop_recording()
accelerometer.wait_for_samples(printer.get_reactor())
toolhead.dwell(0.5)
accelerometer.start_recording(measurements_manager, name='axesmap_Z', append_time=True)
toolhead.dwell(0.5)
toolhead.move([mid_x + SEGMENT_LENGTH / 2, mid_y + SEGMENT_LENGTH / 2, z_height + SEGMENT_LENGTH, E], speed)
toolhead.dwell(0.5)
accelerometer.stop_recording()
accelerometer.wait_for_samples(printer.get_reactor())
toolhead.dwell(0.5)

# Re-enable the input shaper if it was active
Expand Down
1 change: 1 addition & 0 deletions shaketune/commands/axes_shaper_calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def axes_shaper_calibration(gcmd, config, st_process: ShakeTuneProcess) -> None:
accelerometer.start_recording(measurements_manager, name=config['label'], append_time=True)
vibrate_axis(toolhead, gcode, config['direction'], min_freq, max_freq, hz_per_sec, accel_per_hz)
accelerometer.stop_recording()
accelerometer.wait_for_samples(printer.get_reactor())
toolhead.dwell(0.5)
toolhead.wait_moves()

Expand Down
1 change: 1 addition & 0 deletions shaketune/commands/compare_belts_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ def compare_belts_responses(gcmd, config, st_process: ShakeTuneProcess) -> None:
accelerometer.start_recording(measurements_manager, name=config['label'], append_time=True)
vibrate_axis(toolhead, gcode, config['direction'], min_freq, max_freq, hz_per_sec, accel_per_hz)
accelerometer.stop_recording()
accelerometer.wait_for_samples(printer.get_reactor())
toolhead.dwell(0.5)
toolhead.wait_moves()

Expand Down
1 change: 1 addition & 0 deletions shaketune/commands/create_vibrations_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ def create_vibrations_profile(gcmd, config, st_process: ShakeTuneProcess) -> Non
toolhead.move([mid_x + dX, mid_y + dY, z_height, E], curr_speed)
toolhead.move([mid_x - dX, mid_y - dY, z_height, E], curr_speed)
accelerometer.stop_recording()
accelerometer.wait_for_samples(printer.get_reactor())

toolhead.dwell(0.3)
toolhead.wait_moves()
Expand Down
1 change: 1 addition & 0 deletions shaketune/commands/excitate_axis_at_freq.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def excitate_axis_at_freq(gcmd, config, st_process: ShakeTuneProcess) -> None:
# If the user wanted to create a graph, we stop the recording and generate it
if create_graph:
accelerometer.stop_recording()
accelerometer.wait_for_samples(printer.get_reactor())
toolhead.dwell(0.5)

creator = st_process.get_graph_creator()
Expand Down
47 changes: 40 additions & 7 deletions shaketune/helpers/accelerometer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import time
from multiprocessing import Process
from pathlib import Path
from threading import Thread
from typing import List, Tuple, TypedDict

import numpy as np
Expand Down Expand Up @@ -140,7 +141,7 @@ def wait_for_file_writes(self, k_reactor, timeout: int = 20):

if not complete:
raise TimeoutError(
'Shake&Tune was unable to write the accelerometer data into the archive file. '
'Shake&Tune was unable to write the accelerometer data into the .STDATA file. '
'This might be due to a slow SD card or a busy or full filesystem.'
)

Expand All @@ -152,6 +153,7 @@ def __init__(self, klipper_accelerometer):
self._k_accelerometer = klipper_accelerometer
self._bg_client = None
self._measurements_manager: MeasurementsManager = None
self._get_samples_thread = None

@staticmethod
def find_axis_accelerometer(printer, axis: str = 'xy'):
Expand Down Expand Up @@ -188,11 +190,42 @@ def stop_recording(self) -> MeasurementsManager:

bg_client = self._bg_client
self._bg_client = None
bg_client.finish_measurements()

samples = bg_client.samples or bg_client.get_samples()
self._measurements_manager.append_samples_to_last_measurement(samples)
m_manager = self._measurements_manager
self._measurements_manager = None
# Start a thread to finish the measurements and get the samples without blocking the main
# Klipper code. Since these operations are I/O-bound, the GIL should not cause significant
# issues and a thread is even recommended over a Process since bg_client may not be pickleable.
self._get_samples_thread = Thread(target=self._finish_and_get_samples, args=(bg_client,))
self._get_samples_thread.daemon = True
self._get_samples_thread.start()

return m_manager
return self._measurements_manager

def _finish_and_get_samples(self, bg_client):
try:
bg_client.finish_measurements()
samples = bg_client.samples or bg_client.get_samples()
self._measurements_manager.append_samples_to_last_measurement(samples)
except Exception as e:
ConsoleOutput.print(f'Error during accelerometer data retrieval: {e}')

def wait_for_samples(self, k_reactor, timeout: int = 20):
if self._get_samples_thread is None:
return # No samples are being retrieved

eventtime = k_reactor.monotonic()
endtime = eventtime + timeout
complete = False

while eventtime < endtime:
eventtime = k_reactor.pause(eventtime + 0.05)
if not self._get_samples_thread.is_alive():
complete = True
break

if not complete:
raise TimeoutError(
'Shake&Tune was unable to retrieve accelerometer data in time. '
'This might be due to slow hardware or a busy system.'
)

self._get_samples_thread = None

0 comments on commit 82c848f

Please sign in to comment.