Fix capture error handing

The error handling for capture needs to still send the aquired event
or it will lock forever. On getting the errors to work there
were some other minor issues in how the errors were called.
This commit is contained in:
Julian Stirling 2025-04-09 23:52:47 +01:00
parent 70e7a21a29
commit 83d2930d1f

View file

@ -152,14 +152,13 @@ ZipBlob = blob_type("application/zip")
class SmartScanThing(Thing): class SmartScanThing(Thing):
def __init__(self, path_to_openflexure_stitch: str): def __init__(self, path_to_openflexure_stitch: str):
self._script = path_to_openflexure_stitch self._stitching_script = path_to_openflexure_stitch
self._preview_stitch_popen = None self._preview_stitch_popen = None
self._preview_stitch_popen_lock = threading.Lock() self._preview_stitch_popen_lock = threading.Lock()
self._correlate_popen = None self._correlate_popen = None
self._correlate_popen_lock = threading.Lock() self._correlate_popen_lock = threading.Lock()
self._scan_lock = threading.Lock() self._scan_lock = threading.Lock()
# Variables set by the scan # Variables set by the scan
self._latest_scan_name = None self._latest_scan_name = None
self._cancel = None self._cancel = None
@ -217,9 +216,11 @@ class SmartScanThing(Thing):
# record starting position so we can return there # record starting position so we can return there
self._starting_position = self._stage.position self._starting_position = self._stage.position
self._run_scan(scan_name, overlap) self._run_scan(scan_name, overlap)
except: except Exception as e:
self._return_to_starting_position() self._return_to_starting_position()
self._perform_final_stitch(overlap) self._perform_final_stitch(overlap)
# Error must be raised so UI gives correct output
raise e
finally: finally:
# However the scan finnishes unset all variables and release lock # However the scan finnishes unset all variables and release lock
self._cancel = None self._cancel = None
@ -550,6 +551,7 @@ class SmartScanThing(Thing):
) )
capture_thread.start() capture_thread.start()
acquired.wait() # wait until the image is acquired acquired.wait() # wait until the image is acquired
positions.append(loc[:2]) positions.append(loc[:2])
names.append(name) names.append(name)
@ -637,6 +639,7 @@ class SmartScanThing(Thing):
download_zip=False, download_zip=False,
) )
self._logger.info("Waiting for background processes to finish...") self._logger.info("Waiting for background processes to finish...")
self.preview_stitch_wait() self.preview_stitch_wait()
self.correlate_wait() self.correlate_wait()
try: try:
@ -658,9 +661,12 @@ class SmartScanThing(Thing):
This will set the event `acquired` once the image has been acquired, so This will set the event `acquired` once the image has been acquired, so
that the stage may be moved while it's saved. that the stage may be moved while it's saved.
""" """
capture_start = time.time() try:
image, metadata = self.capture_image() capture_start = time.time()
acquired.set() image, metadata = self.capture_image()
finally:
# Ensure aquired is set even if capture fails or program will hang forever.
acquired.set()
acquisition_time = time.time() acquisition_time = time.time()
self.save_capture(jpeg_path, image, metadata) self.save_capture(jpeg_path, image, metadata)
save_time = time.time() save_time = time.time()
@ -681,9 +687,7 @@ class SmartScanThing(Thing):
metadata = self._metadata_getter() metadata = self._metadata_getter()
image = self._cam.capture_array()[..., :3] image = self._cam.capture_array()[..., :3]
except Exception as e: except Exception as e:
raise CaptureError( raise CaptureError("An error occurred while capturing") from e
"An error occurred while capturing: {}".format(e), exc_info=e
)
return image, metadata return image, metadata
def save_capture( def save_capture(
@ -709,10 +713,7 @@ class SmartScanThing(Thing):
except: except:
self._logger.warning(f"Failed to add metadata to {jpeg_path}") self._logger.warning(f"Failed to add metadata to {jpeg_path}")
except Exception as e: except Exception as e:
raise IOError( raise IOError(f"An error occurred while saving {jpeg_path}") from e
f"An error occurred while saving {jpeg_path}: {e}",
exc_info=e,
)
@thing_property @thing_property
def max_range(self) -> int: def max_range(self) -> int:
@ -905,12 +906,24 @@ class SmartScanThing(Thing):
return FileResponse(path) return FileResponse(path)
def preview_stitch_start(self, images_folder: str) -> None: def preview_stitch_start(self, images_folder: str) -> None:
"""Start stitching a preview of the scan in a subprocess""" """Start stitching a preview of the scan in a backgroun subprocess
This uses popen and returns immediatly
- self._preview_stitch_popen holds the popen for polling
- self._preview_stitch_popen_lock is a lock aquired while interacting
with Popen
"""
if self.preview_stitch_running(): if self.preview_stitch_running():
raise RuntimeError("Only one subprocess is allowed at a time") raise RuntimeError("Only one subprocess is allowed at a time")
with self._preview_stitch_popen_lock: with self._preview_stitch_popen_lock:
self._preview_stitch_popen = Popen( self._preview_stitch_popen = Popen(
[self._script, "--stitching_mode", "only_stage_stitch", images_folder] [
self._stitching_script,
"--stitching_mode",
"only_stage_stitch",
images_folder,
]
) )
def preview_stitch_running(self) -> bool: def preview_stitch_running(self) -> bool:
@ -934,7 +947,7 @@ class SmartScanThing(Thing):
with self._correlate_popen_lock: with self._correlate_popen_lock:
self._correlate_popen = Popen( self._correlate_popen = Popen(
[ [
self._script, self._stitching_script,
"--stitching_mode", "--stitching_mode",
"only_correlate", "only_correlate",
"--minimum_overlap", "--minimum_overlap",
@ -1012,7 +1025,7 @@ class SmartScanThing(Thing):
overlap = 0.1 overlap = 0.1
self.run_subprocess( self.run_subprocess(
[ [
self._script, self._stitching_script,
"--stitching_mode", "--stitching_mode",
"all", "all",
f"{tiff_arg}", f"{tiff_arg}",