Example #1
0
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"
Example #2
0
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
Example #3
0
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)
Example #4
0
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
Example #5
0
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)
Example #6
0
    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')
Example #7
0
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
Example #8
0
    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
Example #9
0
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()
Example #10
0
def site_details():
    return create_location_from_config()
Example #11
0
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
Example #12
0
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)