more factorization of the GraphCreator classes

This commit is contained in:
Félix Boisselier
2024-04-14 20:39:11 +02:00
parent 43ac2911a2
commit 656f6d0d9e

View File

@@ -119,7 +119,7 @@ class Config:
help='Number of results to keep in the result folder after each run of the script', help='Number of results to keep in the result folder after each run of the script',
) )
parser.add_argument('--dpi', type=int, default=150, dest='dpi', help='DPI of the output PNG files') parser.add_argument('--dpi', type=int, default=150, dest='dpi', help='DPI of the output PNG files')
parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + Config.get_git_version()) parser.add_argument('-v', '--version', action='version', version=f'Shake&Tune {Config.get_git_version()}')
return parser.parse_args() return parser.parse_args()
@@ -200,6 +200,52 @@ class GraphCreator(abc.ABC):
self._graph_date = datetime.now().strftime('%Y%m%d_%H%M%S') self._graph_date = datetime.now().strftime('%Y%m%d_%H%M%S')
self._version = Config.get_git_version() self._version = Config.get_git_version()
self._type = None
self._folder = None
def _setup_folder(self, graph_type):
self._type = graph_type
self._folder = Config.get_results_folder(graph_type)
def _move_and_prepare_files(self, glob_pattern, min_files_required, custom_name_func=None):
globbed_files = glob.glob(glob_pattern)
if not globbed_files:
raise FileNotFoundError(f'no CSV files found in the /tmp folder to create the {self._type} graphs!')
if len(globbed_files) < min_files_required:
raise FileNotFoundError(f'{min_files_required} CSV files are needed to create the {self._type} graphs!')
if min_files_required is None:
min_files_required = len(globbed_files)
lognames = []
for filename in sorted(globbed_files, key=os.path.getmtime, reverse=True)[:min_files_required]:
FileManager.wait_file_ready(filename)
custom_name = custom_name_func(filename) if custom_name_func else os.path.basename(filename)
new_file = os.path.join(self._folder, f'{self._type}_{self._graph_date}_{custom_name}.csv')
shutil.move(filename, new_file)
os.sync()
FileManager.wait_file_ready(new_file)
lognames.append(new_file)
return lognames
def _save_figure_and_cleanup(self, fig, lognames, axis_label=None):
axis_suffix = f'_{axis_label}' if axis_label else ''
png_filename = os.path.join(self._folder, f'{self._type}_{self._graph_date}{axis_suffix}.png')
fig.savefig(png_filename, dpi=self._dpi)
if self._keep_csv:
self._archive_files(lognames)
else:
self._remove_files(lognames)
def _archive_files(self, _):
return
def _remove_files(self, lognames):
for csv in lognames:
if os.path.exists(csv):
os.remove(csv)
@abc.abstractmethod @abc.abstractmethod
def create_graph(self): def create_graph(self):
pass pass
@@ -209,40 +255,16 @@ class BeltsGraphCreator(GraphCreator):
def __init__(self, keep_csv=False, dpi=150): def __init__(self, keep_csv=False, dpi=150):
super().__init__(keep_csv, dpi) super().__init__(keep_csv, dpi)
self._type = 'belts' self._setup_folder('belts')
self._folder = Config.get_results_folder(self._type)
def create_graph(self): def create_graph(self):
globbed_files = glob.glob('/tmp/raw_data_axis*.csv') lognames = self._move_and_prepare_files(
if not globbed_files: glob_pattern='/tmp/raw_data_axis*.csv',
raise FileNotFoundError('no CSV files found in the /tmp folder to create the belt comparison graphs!') min_files_required=2,
if len(globbed_files) < 2: custom_name_func=lambda f: os.path.basename(f).split('_')[3].split('.')[0].upper(),
raise FileNotFoundError('two CSV files are needed to create the belt comparison graphs!') )
lognames = []
for filename in sorted(globbed_files, key=os.path.getmtime, reverse=True)[:2]:
# Wait for the file handler to be released by Klipper
FileManager.wait_file_ready(filename)
# Cleanup of the filename and moving it in the result folder
belt = os.path.basename(filename).split('_')[3].split('.')[0].upper()
new_file = os.path.join(self._folder, f'{self._type}_{self._graph_date}_{belt}.csv')
shutil.move(filename, new_file)
lognames.append(new_file)
# Check if the file is ready to be read
os.sync()
FileManager.wait_file_ready(new_file)
fig = belts_calibration(lognames, Config.KLIPPER_FOLDER, self._version) fig = belts_calibration(lognames, Config.KLIPPER_FOLDER, self._version)
png_filename = os.path.join(self._folder, f'{self._type}_{self._graph_date}.png') self._save_figure_and_cleanup(fig, lognames)
fig.savefig(png_filename, dpi=self._dpi)
# Remove the CSV files if the user don't want to keep them
if not self._keep_csv:
for csv in lognames:
if os.path.exists(csv):
os.remove(csv)
class ShaperGraphCreator(GraphCreator): class ShaperGraphCreator(GraphCreator):
@@ -252,43 +274,18 @@ class ShaperGraphCreator(GraphCreator):
self._max_smoothing = max_smoothing self._max_smoothing = max_smoothing
self._scv = scv self._scv = scv
self._type = 'shaper' self._setup_folder('shaper')
self._folder = Config.get_results_folder(self._type)
def create_graph(self): def create_graph(self):
globbed_files = glob.glob('/tmp/raw_data*.csv') lognames = self._move_and_prepare_files(
if not globbed_files: glob_pattern='/tmp/raw_data*.csv',
raise FileNotFoundError('no CSV files found in the /tmp folder to create the input shaper graphs!') min_files_required=1,
custom_name_func=lambda f: os.path.basename(f).split('_')[3].split('.')[0].upper(),
# Find the CSV files with the latest timestamp and wait for it to be released by Klipper
filename = sorted(globbed_files, key=os.path.getmtime, reverse=True)[0]
FileManager.wait_file_ready(filename)
# Cleanup of the filename and moving it in the result folder
axis = os.path.basename(filename).split('_')[3].split('.')[0].upper()
new_file = os.path.join(self._folder, f'{self._type}_{self._graph_date}_{axis}.csv')
shutil.move(filename, new_file)
# Check if the file is ready to be read
os.sync()
FileManager.wait_file_ready(new_file)
fig = shaper_calibration(
[new_file],
Config.KLIPPER_FOLDER,
max_smoothing=self._max_smoothing,
scv=self._scv,
st_version=self._version,
) )
png_filename = os.path.join(self._folder, f'{self._type}_{self._graph_date}_{axis}.png') fig = shaper_calibration(
fig.savefig(png_filename, dpi=self._dpi) lognames, Config.KLIPPER_FOLDER, max_smoothing=self._max_smoothing, scv=self._scv, st_version=self._version
)
# Remove the CSV files if the user don't want to keep them self._save_figure_and_cleanup(fig, lognames, lognames[0].split('_')[-1].split('.')[0])
if not self._keep_csv:
if os.path.exists(new_file):
os.remove(new_file)
return axis
class VibrationsGraphCreator(GraphCreator): class VibrationsGraphCreator(GraphCreator):
@@ -299,46 +296,22 @@ class VibrationsGraphCreator(GraphCreator):
self._accel = accel self._accel = accel
self._chip_name = chip_name self._chip_name = chip_name
self._type = 'vibrations' self._setup_folder('vibrations')
self._folder = Config.get_results_folder(self._type)
def create_graph(self): def create_graph(self):
globbed_files = glob.glob(f'/tmp/{self._chip_name}-*.csv') lognames = self._move_and_prepare_files(
if not globbed_files: glob_pattern=f'/tmp/{self._chip_name}-*.csv',
raise FileNotFoundError('no CSV files found in the /tmp folder to create the vibrations graphs!') min_files_required=None,
if len(globbed_files) < 3: custom_name_func=lambda f: os.path.basename(f).replace(self._chip_name, self._type),
raise FileNotFoundError('at least 3 CSV files are needed to create the vibrations graphs!') )
lognames = []
for filename in globbed_files:
# Wait for the file handler to be released by Klipper
FileManager.wait_file_ready(filename)
# Cleanup of the filename and moving it in the result folder
cleanfilename = os.path.basename(filename).replace(self._chip_name, f'{self._type}_{self._graph_date}')
new_file = os.path.join(self._folder, cleanfilename)
shutil.move(filename, new_file)
lognames.append(new_file)
# Sync filesystem to avoid problems as there is a lot of file copied
os.sync()
time.sleep(5)
fig = vibrations_profile(lognames, Config.KLIPPER_FOLDER, self._kinematics, self._accel, self._version) fig = vibrations_profile(lognames, Config.KLIPPER_FOLDER, self._kinematics, self._accel, self._version)
png_filename = os.path.join(self._folder, f'{self._type}_{self._graph_date}.png') self._save_figure_and_cleanup(fig, lognames)
fig.savefig(png_filename, dpi=self._dpi)
# Archive all the csv files in a tarball in case the user want to keep them def _archive_files(self, lognames):
if self._keep_csv:
with tarfile.open(os.path.join(self._folder, f'{self._type}_{self._graph_date}.tar.gz'), 'w:gz') as tar: with tarfile.open(os.path.join(self._folder, f'{self._type}_{self._graph_date}.tar.gz'), 'w:gz') as tar:
for csv_file in lognames: for csv_file in lognames:
tar.add(csv_file, arcname=os.path.basename(csv_file), recursive=False) tar.add(csv_file, arcname=os.path.basename(csv_file), recursive=False)
# Remove the remaining CSV files not needed anymore (tarball is safe if it was created)
for csv in lognames:
if os.path.exists(csv):
os.remove(csv)
class AxesMapFinder: class AxesMapFinder:
def __init__(self, accel, chip_name): def __init__(self, accel, chip_name):
@@ -366,11 +339,11 @@ class AxesMapFinder:
def main(): def main():
print_with_c_locale(f'Shake&Tune version: {Config.get_git_version()}')
options = Config.parse_arguments() options = Config.parse_arguments()
FileManager.ensure_folders_exist() FileManager.ensure_folders_exist()
print_with_c_locale(f'Shake&Tune version: {Config.get_git_version()}')
graph_creator = None graph_creator = None
if options.type == 'belts': if options.type == 'belts':
graph_creator = BeltsGraphCreator(options.keep_csv, options.dpi) graph_creator = BeltsGraphCreator(options.keep_csv, options.dpi)