def _poll_exposure(self, readout_args): timer = CountdownTimer(duration=self._timeout) try: try: # See if the command has finished. while self._exposure_proc.poll() is None: # Sleep if not done yet. timer.sleep() except subprocess.TimeoutExpired: self.logger.warning( f'Timeout on exposure process for {self.name}') self._exposure_proc.kill() outs, errs = self._exposure_proc.communicate(timeout=10) if errs is not None and errs > '': self.logger.error(f'Camera exposure errors: {errs}') except (RuntimeError, error.PanError) as err: # Error returned by driver at some point while polling self.logger.error( 'Error while waiting for exposure on {}: {}'.format(self, err)) raise err else: # Camera type specific readout function self._readout(*readout_args) finally: self.logger.debug(f'Setting exposure event for {self.name}') self._exposure_proc = None self._is_exposing_event.clear( ) # Make sure this gets set regardless of readout errors
def test_countdown_timer_non_blocking(): timer = CountdownTimer(0) assert timer.is_non_blocking assert timer.time_left() == 0 for arg, expected_duration in [(2, 2.0), (0.5, 0.5), (1 * u.second, 1.0)]: timer = CountdownTimer(arg) assert timer.duration == expected_duration
def test_countdown_timer_bad_input(): with pytest.raises(ValueError): assert CountdownTimer('d') with pytest.raises(ValueError): assert CountdownTimer(current_time()) with pytest.raises(AssertionError): assert CountdownTimer(-1)
def slew_to_target(self, blocking=False, timeout=180): """ Slews to the currently assigned target coordinates. Slews the mount to the coordinates that have been assigned by `~set_target_coordinates`. If no coordinates have been set, do nothing and return `False`, otherwise return response from the mount. If `blocking=True` then wait for up to `timeout` seconds for the mount to reach the `is_tracking` state. If a timeout occurs, raise a `pocs.error.Timeout` exception. Args: blocking (bool, optional): If command should block while slewing to home, default False. timeout (int, optional): Maximum time spent slewing to home, default 180 seconds. Returns: bool: indicating success """ success = False if self.is_parked: self.logger.info("Mount is parked") elif not self.has_target: self.logger.info("Target Coordinates not set") else: self.logger.debug('Slewing to target') success = self.query('slew_to_target') self.logger.debug("Mount response: {}".format(success)) if success: if blocking: # Set up the timeout timer self.logger.debug( f'Setting slew timeout timer for {timeout} sec') timeout_timer = CountdownTimer(timeout) block_time = 3 # seconds while self.is_tracking is False: if timeout_timer.expired(): self.logger.warning( f'slew_to_target timout: {timeout} seconds') raise error.Timeout('Problem slewing to target') self.logger.debug( f'Slewing to target, sleeping for {block_time} seconds' ) timeout_timer.sleep(max_sleep=block_time) self.logger.debug(f'Done with slew_to_target block') else: self.logger.warning('Problem with slew_to_target') return success
def _readout(self, filename=None, header=None): self.logger.debug(f'Calling _readout for {self}') timer = CountdownTimer(duration=self.readout_time) # Get example FITS file from test data directory file_path = os.path.join(os.environ['POCS'], 'tests', 'data', 'unsolved.fits') fake_data = fits.getdata(file_path) if header.get('IMAGETYP') == 'Dark Frame': # Replace example data with a bunch of random numbers fake_data = np.random.randint(low=975, high=1026, size=fake_data.shape, dtype=fake_data.dtype) self.logger.debug(f'Writing filename={filename!r} for {self}') fits_utils.write_fits(fake_data, header, filename) # Sleep for the remainder of the readout time. timer.sleep()
def get_periodic_status(): while self.connected: status = self.status self.logger.trace(f'Periodic status call: {status!r}') self.db.insert_current('status', status) CountdownTimer( self.get_config('status_check_interval', default=60)).sleep()
def slew_to_home(self, blocking=False, timeout=180): """Slews the mount to the home position. Note: Home position and Park position are not the same thing Args: blocking (bool, optional): If command should block while slewing to home, default False. timeout (int, optional): Maximum time spent slewing to home, default 180 seconds. Returns: bool: indicating success Args: blocking (bool, optional): If command should block while slewing to home, default False. """ response = 0 # Set up the timeout timer timeout_timer = CountdownTimer(timeout) block_time = 3 # seconds if not self.is_parked: # Reset target coordinates self._target_coordinates = None # Start the slew response = self.query('slew_to_home') if response and blocking: while self.is_home is False: if timeout_timer.expired(): self.logger.warning( f'slew_to_home timout: {timeout} seconds') response = 0 break self.logger.debug( f'Slewing to home, sleeping for {block_time} seconds') timeout_timer.sleep(max_sleep=block_time) else: self.logger.info('Mount is parked') return response
def test_countdown_timer_sleep_log(caplog): count_time = 1 timer = CountdownTimer(count_time) # Default is a debug level timer.sleep() assert caplog.records[-1].levelname == 'DEBUG' assert caplog.records[-1].message.startswith('Sleeping for') timer.restart() timer.sleep(log_level='info') assert caplog.records[-1].levelname == 'INFO' assert caplog.records[-1].message.startswith('Sleeping for')
def test_countdown_timer_sleep(): count_time = 1 timer = CountdownTimer(count_time) assert timer.time_left() > 0 assert timer.expired() is False assert timer.is_non_blocking is False counter = 0. while timer.time_left() > 0.5: assert timer.sleep(max_sleep=0.1) counter += 0.1 # Wait for the remaining half second assert timer.sleep() is False assert counter == pytest.approx(0.5) assert timer.time_left() == 0 assert timer.expired() is True assert timer.sleep() is False
def _poll_exposure(self, readout_args, exposure_time, timeout=None, interval=0.01): """ Wait until camera is no longer exposing or the timeout is reached. If the timeout is reached, an `error.Timeout` is raised. """ if timeout is None: timer_duration = self._timeout + self._readout_time + exposure_time.to_value( u.second) else: timer_duration = timeout self.logger.debug( f"Polling exposure with timeout of {timer_duration} seconds.") timer = CountdownTimer(duration=timer_duration) try: while self.is_exposing: if timer.expired(): msg = f"Timeout (timer.duration={timer.duration!r}) waiting for exposure on" f" {self} to complete" raise error.Timeout(msg) time.sleep(interval) except Exception as err: # Error returned by driver at some point while polling self.logger.error( f'Error while waiting for exposure on {self}: {err!r}') self._exposure_error = repr(err) raise err else: # Camera type specific readout function try: self._readout(*readout_args) except Exception as err: self.logger.error(f"Error during readout on {self}: {err!r}") self._exposure_error = repr(err) raise err finally: # Make sure this gets set regardless of any errors self._is_exposing_event.clear()
def test_countdown_timer(): count_time = 1 timer = CountdownTimer(count_time) assert timer.time_left() > 0 assert timer.expired() is False assert timer.is_non_blocking is False counter = 0. while timer.time_left() > 0: time.sleep(0.1) counter += 0.1 assert counter == pytest.approx(1) assert timer.time_left() == 0 assert timer.expired() is True
def wait(self, delay=None): """ Send POCS to wait. Loops for `delay` number of seconds. If `delay` is more than 30.0 seconds, then check for status signals (which are updated every 60 seconds by default). Keyword Arguments: delay {float|None} -- Number of seconds to wait. If default `None`, look up value in config, otherwise 2.5 seconds. """ if delay is None: # pragma: no cover delay = self.get_config('wait_delay', default=2.5) sleep_timer = CountdownTimer(delay) self.logger.info(f'Starting a wait timer of {delay} seconds') while not sleep_timer.expired() and not self.interrupted: self.logger.debug( f'Wait timer: {sleep_timer.time_left():.02f} / {delay:.02f}') sleep_timer.sleep(max_sleep=30) is_expired = sleep_timer.expired() self.logger.debug(f'Leaving wait timer: expired={is_expired}') return is_expired
def _efw_poll(self, filterwheel_ID, position, move_event, timeout): """ Polls filter wheel until the current move is complete. Also monitors for errors while polling and checks position after the move is complete. Optionally sets a threading.Event to signal the end of the move. Has an optional timeout to raise an TimeoutError is the move takes longer than expected. Args: filterwheel_ID (int): integer ID of the filterwheel that is moving. position (int): position to move the filter wheel. Must be an integer >= 0. move_event (threading.Event, optional): Event to set once the move is complete timeout (u.Quantity, optional): maximum time to wait for the move to complete. Should be a Quantity with time units. If a numeric type without units is given seconds will be assumed. Raises: `panoptes.utils.error.PanError`: raised if the driver returns an error or if the final position is not as expected. `panoptes.utils.error.Timeout`: raised if the move does not end within the period of time specified by the timeout argument. """ if timeout is not None: timer = CountdownTimer(duration=timeout) try: # No status query function in the SDK. Only way to check on progress of move # is to keep issuing the same move command until we stop getting the MOVING # error code back. error_code = self._CDLL.EFWSetPosition( ctypes.c_int(filterwheel_ID), ctypes.c_int(position)) while error_code == ErrorCode.MOVING: if timeout is not None and timer.expired(): msg = "Timeout waiting for filterwheel {} to move to {}".format( filterwheel_ID, position) raise error.Timeout(msg) time.sleep(0.1) error_code = self._CDLL.EFWSetPosition( ctypes.c_int(filterwheel_ID), ctypes.c_int(position)) if error_code != ErrorCode.SUCCESS: # Got some sort of error while polling. msg = "Error while moving filterwheel {} to {}: {}".format( filterwheel_ID, position, ErrorCode(error_code).name) self.logger.error(msg) raise error.PanError(msg) final_position = self.get_position(filterwheel_ID) if final_position != position: msg = "Tried to move filterwheel {} to {}, but ended up at {}.".format( filterwheel_ID, position, final_position) self.logger.error(msg) raise error.PanError(msg) self.logger.debug( f"Filter wheel {filterwheel_ID} moved to {position}.") finally: # Regardless must always set the Event when the move has stopped. if move_event is not None: move_event.set()