Remove all cancel hooks from smart scan and stitching
This commit is contained in:
parent
150e69d235
commit
510a44dd17
2 changed files with 19 additions and 32 deletions
|
|
@ -6,6 +6,7 @@ CPU intensity of stitching causing scanning problems due to the Python Global
|
|||
Interpreter Lock (GIL). May be possible to shift to multiprocessing in the future.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import shlex
|
||||
import signal
|
||||
|
|
@ -167,14 +168,17 @@ class PreviewStitcher(BaseStitcher):
|
|||
return False
|
||||
return self._popen_obj.poll() is None
|
||||
|
||||
def wait(self, cancel: lt.deps.CancelHook) -> None:
|
||||
def wait(self) -> None:
|
||||
"""Wait for this preview stitch to return.
|
||||
|
||||
:raises InvocationCancelledError: if the action is cancelled.
|
||||
"""
|
||||
while self.running:
|
||||
try:
|
||||
cancel.sleep(0.1)
|
||||
# This should act exactly like sleep if not started in a LabThings
|
||||
# action thread. In an action thread it will raise
|
||||
# InvocationCancelledError if the action is cancelled.
|
||||
lt.cancellable_sleep(0.2)
|
||||
except lt.exceptions.InvocationCancelledError as e:
|
||||
with self._popen_lock:
|
||||
if self._popen_obj is not None:
|
||||
|
|
@ -189,7 +193,7 @@ class FinalStitcher(BaseStitcher):
|
|||
self,
|
||||
images_dir: str,
|
||||
*,
|
||||
logger: lt.deps.InvocationLogger,
|
||||
logger: logging.Logger,
|
||||
overlap: Optional[float] = None,
|
||||
correlation_resize: Optional[float] = None,
|
||||
stitch_tiff: bool = False,
|
||||
|
|
@ -200,7 +204,7 @@ class FinalStitcher(BaseStitcher):
|
|||
All args except images_dir are positional only.
|
||||
|
||||
:param images_dir: The images directory of the scan to stitch.
|
||||
:param logger: The invocation logger for this stitch process.
|
||||
:param logger: The logger from the Thing that created this stitcher.
|
||||
:param overlap: The scan overlap, if not known enter None. A value will be
|
||||
chosen from the scan_data_dict, or set to a default value if no value is
|
||||
available in the scan_data.
|
||||
|
|
@ -281,10 +285,7 @@ class FinalStitcher(BaseStitcher):
|
|||
)
|
||||
return overlap, correlation_resize
|
||||
|
||||
def run(
|
||||
self,
|
||||
cancel: lt.deps.CancelHook,
|
||||
) -> None:
|
||||
def run(self) -> None:
|
||||
"""Run the final stitch logging any output.
|
||||
|
||||
:raises ChildProcessError: if exit code is not zero
|
||||
|
|
@ -305,7 +306,7 @@ class FinalStitcher(BaseStitcher):
|
|||
# Stop opening pipe blocking writing to it
|
||||
os.set_blocking(process.stdout.fileno(), False)
|
||||
|
||||
self._log_ongoing(process, cancel=cancel)
|
||||
self._log_ongoing(process)
|
||||
|
||||
if process.poll() == 0:
|
||||
self.logger.info("Stitching complete")
|
||||
|
|
@ -319,11 +320,7 @@ class FinalStitcher(BaseStitcher):
|
|||
f"Subprocess {STITCHING_CMD} errored with exit code {process.poll()}."
|
||||
)
|
||||
|
||||
def _log_ongoing(
|
||||
self,
|
||||
process: subprocess.Popen,
|
||||
cancel: lt.deps.CancelHook,
|
||||
) -> None:
|
||||
def _log_ongoing(self, process: subprocess.Popen) -> None:
|
||||
"""Log the ongoing process unless it is cancelled."""
|
||||
# Poll returns None while running, will return the error code when finished
|
||||
while process.poll() is None:
|
||||
|
|
@ -331,9 +328,10 @@ class FinalStitcher(BaseStitcher):
|
|||
# Once buffer is clear sleep for 0.2s before trying again.
|
||||
|
||||
try:
|
||||
# Note that using cancel.sleep allows the InvocationCancelledError to
|
||||
# be thrown
|
||||
cancel.sleep(0.2)
|
||||
# This should act exactly like sleep if not started in a LabThings
|
||||
# action thread. In an action thread it will raise
|
||||
# InvocationCancelledError if the action is cancelled.
|
||||
lt.cancellable_sleep(0.2)
|
||||
except lt.exceptions.InvocationCancelledError as e:
|
||||
self.logger.info("Stitching cancelled by user")
|
||||
process.kill()
|
||||
|
|
|
|||
|
|
@ -126,7 +126,6 @@ class SmartScanThing(lt.Thing):
|
|||
# any method that calls these should be decorated with
|
||||
# @_scan_running
|
||||
self._scan_logger: Optional[lt.deps.InvocationLogger] = None
|
||||
self._cancel: Optional[lt.deps.CancelHook] = None
|
||||
self._autofocus: Optional[AutofocusDep] = None
|
||||
self._stage: Optional[StageDep] = None
|
||||
self._cam: Optional[CameraClient] = None
|
||||
|
|
@ -139,7 +138,6 @@ class SmartScanThing(lt.Thing):
|
|||
@lt.action
|
||||
def sample_scan(
|
||||
self,
|
||||
cancel: lt.deps.CancelHook,
|
||||
logger: lt.deps.InvocationLogger,
|
||||
autofocus: AutofocusDep,
|
||||
stage: StageDep,
|
||||
|
|
@ -158,7 +156,6 @@ class SmartScanThing(lt.Thing):
|
|||
raise RuntimeError("Trying to run scan while scan is already running!")
|
||||
|
||||
# Set private variables for this scan
|
||||
self._cancel = cancel
|
||||
self._scan_logger = logger
|
||||
self._autofocus = autofocus
|
||||
self._stage = stage
|
||||
|
|
@ -188,7 +185,6 @@ class SmartScanThing(lt.Thing):
|
|||
raise e
|
||||
finally:
|
||||
# However the scan finishes, unset all variables and release lock
|
||||
self._cancel = None
|
||||
self._scan_logger = None
|
||||
self._autofocus = None
|
||||
self._stage = None
|
||||
|
|
@ -390,8 +386,6 @@ class SmartScanThing(lt.Thing):
|
|||
self._save_final_scan_data(scan_result="success")
|
||||
|
||||
except lt.exceptions.InvocationCancelledError:
|
||||
# Reset the cancel event so it can be thrown again
|
||||
self._cancel.clear()
|
||||
self._scan_logger.info("Stopping scan because it was cancelled.")
|
||||
self._save_final_scan_data(scan_result="cancelled by user")
|
||||
except Exception as e:
|
||||
|
|
@ -513,13 +507,12 @@ class SmartScanThing(lt.Thing):
|
|||
self._scan_logger.info("Waiting for background processes to finish...")
|
||||
|
||||
if self._preview_stitcher is not None:
|
||||
self._preview_stitcher.wait(self._cancel)
|
||||
self._preview_stitcher.wait()
|
||||
|
||||
if self._scan_data.stitch_automatically:
|
||||
self._scan_logger.info("Stitching final image (may take some time)...")
|
||||
self.stitch_scan(
|
||||
logger=self._scan_logger,
|
||||
cancel=self._cancel,
|
||||
scan_name=self._ongoing_scan.name,
|
||||
correlation_resize=self._scan_data.correlation_resize,
|
||||
overlap=self._scan_data.overlap,
|
||||
|
|
@ -731,7 +724,6 @@ class SmartScanThing(lt.Thing):
|
|||
def stitch_scan(
|
||||
self,
|
||||
logger: lt.deps.InvocationLogger,
|
||||
cancel: lt.deps.CancelHook,
|
||||
scan_name: str,
|
||||
correlation_resize: Optional[float] = None,
|
||||
overlap: Optional[float] = None,
|
||||
|
|
@ -753,8 +745,7 @@ class SmartScanThing(lt.Thing):
|
|||
scan_data_dict=scan_data_dict,
|
||||
)
|
||||
try:
|
||||
# start the final stitch, providing the cancel hook to allow aborting
|
||||
final_stitcher.run(cancel)
|
||||
final_stitcher.run()
|
||||
except lt.exceptions.InvocationCancelledError:
|
||||
# Sleep for 1 second just to allow invocation logs to pass to user.
|
||||
time.sleep(1)
|
||||
|
|
@ -774,9 +765,7 @@ class SmartScanThing(lt.Thing):
|
|||
return ZipBlob.from_file(zip_fname)
|
||||
|
||||
@lt.action
|
||||
def stitch_all_scans(
|
||||
self, logger: lt.deps.InvocationLogger, cancel: lt.deps.CancelHook
|
||||
) -> None:
|
||||
def stitch_all_scans(self, logger: lt.deps.InvocationLogger) -> None:
|
||||
"""Check the list of scans, and stitch any that don't have a DZI associated with it.
|
||||
|
||||
:raises RuntimeError: if the microscope is currently running a scan
|
||||
|
|
@ -786,4 +775,4 @@ class SmartScanThing(lt.Thing):
|
|||
raise RuntimeError("Can't stitch previous scans while a scan is ongoing")
|
||||
for scan in self._get_all_scan_info():
|
||||
if scan.dzi is None:
|
||||
self.stitch_scan(logger=logger, cancel=cancel, scan_name=scan.name)
|
||||
self.stitch_scan(logger=logger, scan_name=scan.name)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue