def test_cannot_observe(caplog): obs = Observatory() site_details = create_location_from_config() cameras = create_cameras_from_config() assert obs.can_observe is False time.sleep(0.5) # log sink time log_record = caplog.records[-1] assert log_record.message.endswith( "not present") and log_record.levelname == "WARNING" obs.scheduler = create_scheduler_from_config( observer=site_details['observer']) assert obs.can_observe is False time.sleep(0.5) # log sink time log_record = caplog.records[-1] assert log_record.message.endswith( "not present") and log_record.levelname == "WARNING" for cam_name, cam in cameras.items(): obs.add_camera(cam_name, cam) assert obs.can_observe is False log_record = caplog.records[-1] time.sleep(0.5) # log sink time assert log_record.message.endswith( "not present") and log_record.levelname == "WARNING"
def create_mount_simulator(mount_info=None, earth_location=None, *args, **kwargs): # Remove mount simulator current_simulators = get_config('simulator', default=[]) logger.warning(f'Current simulators: {current_simulators}') with suppress(ValueError): current_simulators.remove('mount') mount_config = mount_info or { 'model': 'Mount Simulator', 'driver': 'simulator', 'serial': { 'port': '/dev/FAKE' } } # Set mount device info to simulator set_config('mount', mount_config) earth_location = earth_location or create_location_from_config()['earth_location'] logger.debug(f"Loading mount driver: pocs.mount.{mount_config['driver']}") try: module = load_module(f"panoptes.pocs.mount.{mount_config['driver']}") except error.NotFound as e: raise error.MountNotFound(f'Error loading mount module: {e!r}') mount = module.Mount(earth_location, *args, **kwargs) logger.success(f"{mount_config['driver'].title()} mount created") return mount
def test_bad_scheduler_fields_file(config_host, config_port): set_config('scheduler.fields_file', 'foobar') site_details = create_location_from_config() with pytest.raises(error.NotFound): create_scheduler_from_config(observer=site_details['observer']) reset_conf(config_host, config_port)
def create_scheduler_from_config(observer=None, *args, **kwargs): """ Sets up the scheduler that will be used by the observatory """ logger = get_logger() scheduler_config = get_config('scheduler', default=None) logger.info(f'scheduler_config: {scheduler_config!r}') if scheduler_config is None or len(scheduler_config) == 0: logger.info("No scheduler in config") return None if not observer: logger.debug(f'No Observer provided, creating from config.') site_details = create_location_from_config() observer = site_details['observer'] scheduler_type = scheduler_config.get('type', 'dispatch') # Read the targets from the file fields_file = scheduler_config.get('fields_file', 'simple.yaml') fields_path = os.path.join(get_config('directories.targets'), fields_file) logger.debug(f'Creating scheduler: {fields_path}') if os.path.exists(fields_path): try: # Load the required module module = load_module(f'panoptes.pocs.scheduler.{scheduler_type}') obstruction_list = get_config('location.obstructions', default=[]) default_horizon = get_config('location.horizon', default=30 * u.degree) horizon_line = horizon_utils.Horizon( obstructions=obstruction_list, default_horizon=default_horizon.value) # Simple constraint for now constraints = [ Altitude(horizon=horizon_line), MoonAvoidance(), Duration(default_horizon, weight=5.) ] # Create the Scheduler instance scheduler = module.Scheduler(observer, fields_file=fields_path, constraints=constraints, *args, **kwargs) logger.debug("Scheduler created") except error.NotFound as e: raise error.NotFound(msg=e) else: raise error.NotFound( msg=f"Fields file does not exist: fields_file={fields_file!r}") return scheduler
def test_create_mount_with_earth_location(config_host, config_port): # Get location to pass manually. loc = create_location_from_config() # Set config to not have a location. set_config('location', None) set_config('simulator', hardware.get_all_names()) assert isinstance(create_mount_from_config(earth_location=loc['earth_location']), AbstractMount) is True reset_conf(config_host, config_port)
def __init__(self, cameras=None, scheduler=None, dome=None, mount=None, *args, **kwargs): """Main Observatory class Starts up the observatory. Reads config file, sets up location, dates and weather station. Adds cameras, scheduler, dome and mount. """ super().__init__(*args, **kwargs) self.scheduler = None self.dome = None self.mount = None self.logger.info('Initializing observatory') # Setup information about site location self.logger.info('Setting up location') site_details = create_location_from_config() self.location = site_details['location'] self.earth_location = site_details['earth_location'] self.observer = site_details['observer'] # Do some one-time calculations now = current_time() self._local_sun_pos = self.observer.altaz( now, target=get_sun(now)).alt # Re-calculated self._local_sunrise = self.observer.sun_rise_time(now) self._local_sunset = self.observer.sun_set_time(now) self._evening_astro_time = self.observer.twilight_evening_astronomical( now, which='next') self._morning_astro_time = self.observer.twilight_morning_astronomical( now, which='next') # Set up some of the hardware. self.set_mount(mount) self.cameras = OrderedDict() self._primary_camera = None if cameras: self.logger.info(f'Adding cameras to the observatory: {cameras}') for cam_name, camera in cameras.items(): self.add_camera(cam_name, camera) # TODO(jamessynge): Figure out serial port validation behavior here compared to that for # the mount. self.set_dome(dome) self.set_scheduler(scheduler) self.current_offset_info = None self._image_dir = self.get_config('directories.images') self.logger.success('Observatory initialized')
def observatory(mount, cameras, images_dir): """Return a valid Observatory instance with a specific config.""" site_details = create_location_from_config() scheduler = create_scheduler_from_config(observer=site_details['observer']) obs = Observatory(scheduler=scheduler) obs.set_mount(mount) for cam_name, cam in cameras.items(): obs.add_camera(cam_name, cam) return obs
def setup(self): location = create_location_from_config() # Can't supply full location, need earth_location with pytest.raises(AssertionError): mount = Mount(location) earth_location = location['earth_location'] mount = Mount(earth_location) assert mount is not None self.mount = mount with pytest.raises(AssertionError): assert self.mount.query('version') == 'V1.00' assert self.mount.is_initialized is False assert self.mount.initialize() is True
def test_set_scheduler(observatory, caplog): site_details = create_location_from_config() scheduler = create_scheduler_from_config(observer=site_details['observer']) assert observatory.current_observation is None observatory.set_scheduler(scheduler=None) assert observatory.scheduler is None observatory.set_scheduler(scheduler=scheduler) assert observatory.scheduler is not None err_msg = 'Scheduler is not an instance of .*BaseScheduler' with pytest.raises(TypeError, match=err_msg): observatory.set_scheduler('scheduler') err_msg = ".*missing 1 required positional argument.*" with pytest.raises(TypeError, match=err_msg): observatory.set_scheduler()
def site_details(): return create_location_from_config()
def create_mount_from_config(mount_info=None, earth_location=None, *args, **kwargs): """Create a mount instance based on the provided config. Creates an instance of the AbstractMount sub-class in the module specified in the config. Specifically, the class must be in a file called pocs/mount/<DRIVER_NAME>.py, and the class must be called Mount. Args: mount_info: Optional param which overrides the 'mount' entry in config if provided. Useful for testing. earth_location: `astropy.coordinates.EarthLocation` instance, representing the location of the mount on the Earth. If not specified, the config must include the observatory's location (Latitude, Longitude and Altitude above mean sea level). Useful for testing. *args: Other positional args will be passed to the concrete class specified in the config. **kwargs: Other keyword args will be passed to the concrete class specified in the config. Returns: An instance of the Mount class if the config (or mount_info) is complete. `None` if neither mount_info nor config['mount'] is provided. Raises: error.MountNotFound: Exception raised when mount cannot be created because of incorrect configuration. """ # If mount_info was not passed as a parameter, check config. if mount_info is None: logger.debug('No mount info provided, using values from config.') mount_info = get_config('mount', default=None) # If nothing in config, raise exception. if mount_info is None: raise error.MountNotFound('No mount information in config, cannot create.') # If earth_location was not passed as a parameter, check config. if earth_location is None: logger.debug('No location provided, using values from config.') # Get details from config. site_details = create_location_from_config() earth_location = site_details['earth_location'] driver = mount_info.get('driver') if not driver or not isinstance(driver, str): raise error.MountNotFound('Mount info in config is missing a driver name.') model = mount_info.get('model', driver) logger.debug(f'Mount: driver={driver} model={model}') # Check if we should be using a simulator use_simulator = 'mount' in get_config('simulator', default=[]) logger.debug(f'Mount is simulator: {use_simulator}') # Create simulator if requested if use_simulator or (driver == 'simulator'): logger.debug(f'Creating mount simulator') return create_mount_simulator(mount_info=mount_info, earth_location=earth_location) # See if we have a serial connection try: port = mount_info['serial']['port'] logger.info(f'Looking for {driver} on {port}.') if port is None or len(glob(port)) == 0: msg = f'Mount port ({port}) not available. Use simulator = mount for simulator.' raise error.MountNotFound(msg=msg) except KeyError: # See Issue 866 if model == 'bisque': logger.debug('Driver specifies a bisque type mount, no serial port needed.') else: msg = 'Mount port not specified in config file. Use simulator=mount for simulator.' raise error.MountNotFound(msg=msg) logger.debug(f'Loading mount driver: pocs.mount.{driver}') try: module = load_module(f'panoptes.pocs.mount.{driver}') except error.NotFound as e: raise error.MountNotFound(e) # Make the mount include site information mount = module.Mount(location=earth_location, *args, **kwargs) logger.success(f'{driver} mount created') return mount
def test_no_scheduler_in_config(config_host, config_port): set_config('scheduler', None) site_details = create_location_from_config() assert create_scheduler_from_config( observer=site_details['observer']) is None reset_conf(config_host, config_port)