def __init__(self, *args, **kwargs): super(DitheredObservation, self).__init__(min_nexp=1, exp_set_size=1, *args, **kwargs) # Set initial list to original values self._exp_time = listify(self.exp_time) self._field = listify(self.field) self.extra_config = kwargs
def command(self, cmd): """ Run gphoto2 command """ # Test to see if there is a running command already if self._proc and self._proc.poll(): raise error.InvalidCommand("Command already running") else: # Build the command. run_cmd = [self._gphoto2, '--port', self.port] run_cmd.extend(listify(cmd)) self.logger.debug("gphoto2 command: {}".format(run_cmd)) try: self._proc = subprocess.Popen(run_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=False) except OSError as e: raise error.InvalidCommand( "Can't send command to gphoto2. {} \t {}".format( e, run_cmd)) except ValueError as e: raise error.InvalidCommand( "Bad parameters to gphoto2. {} \t {}".format(e, run_cmd)) except Exception as e: raise error.PanError(e)
def command(self, cmd): """ Run gphoto2 command """ # Test to see if there is a running command already if self._proc and self._proc.poll(): raise error.InvalidCommand("Command already running") else: # Build the command. run_cmd = [self._gphoto2, '--port', self.port] run_cmd.extend(listify(cmd)) self.logger.debug("gphoto2 command: {}".format(run_cmd)) try: self._proc = subprocess.Popen( run_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=False ) except OSError as e: raise error.InvalidCommand( "Can't send command to gphoto2. {} \t {}".format( e, run_cmd)) except ValueError as e: raise error.InvalidCommand( "Bad parameters to gphoto2. {} \t {}".format(e, run_cmd)) except Exception as e: raise error.PanError(e)
def _load_transition(self, transition): self.logger.debug("Loading transition: {}".format(transition)) # Add `check_safety` as the first transition for all states conditions = listify(transition.get('conditions', [])) conditions.insert(0, 'check_safety') transition['conditions'] = conditions self.logger.debug("Returning transition: {}".format(transition)) return transition
def load_config(config_files=None, simulator=None, parse=True, ignore_local=False): """ Load configuation information """ # Default to the pocs.yaml file if config_files is None: config_files = ['pocs'] config_files = listify(config_files) config = dict() config_dir = '{}/conf_files'.format(os.getenv('POCS')) for f in config_files: if not f.endswith('.yaml'): f = '{}.yaml'.format(f) if not f.startswith('/'): path = os.path.join(config_dir, f) else: path = f try: _add_to_conf(config, path) except Exception as e: warn("Problem with config file {}, skipping. {}".format(path, e)) # Load local version of config if not ignore_local: local_version = os.path.join(config_dir, f.replace('.', '_local.')) if os.path.exists(local_version): try: _add_to_conf(config, local_version) except Exception: warn("Problem with local config file {}, skipping".format( local_version)) if simulator is not None: if 'all' in simulator: config['simulator'] = [ 'camera', 'mount', 'weather', 'night', 'dome' ] else: config['simulator'] = simulator if parse: config = parse_config(config) return config
def load_config(config_files=None, simulator=None, parse=True, ignore_local=False): """Load configuation information This function supports loading of a number of different files. If no options are passed to `config_files` then the default `$POCS/conf_files/pocs.yaml` will be loaded. See Notes for additional information. Notes: The `config_files` parameter supports a number of options: * `config_files` is a list and loaded in order, so the first entry will have any values overwritten by similarly named keys in the second entry. * Entries can be placed in the `$POCS/conf_files` folder and should be passed as just the file name, e.g. [`weather.yaml`, `email.yaml`] for loading `$POCS/conf_files/weather.yaml` and `$POCS/conf_files/email.yaml` * The `.yaml` extension will be added if not present, so list can be written as just ['weather', 'email']. * `config_files` can also be specified by an absolute path, which can exist anywhere on the filesystem. * Local versions of files can override built-in versions and are automatically loaded if placed in the `$POCS/conf_files` folder. The files have a `<>_local.yaml` name, where `<>` is the built-in file. So a `$POCS/conf_files/pocs_local.yaml` will override any setting in the default `pocs.yaml` file. * Local files can be ignored (mostly for testing purposes) with the `ignore_local` parameter. Args: config_files (list, optional): A list of files to load as config, see Notes for details of how to specify files. simulator (list, optional): A list of hardware items that should be used as a simulator. parse (bool, optional): If the config file should attempt to create objects such as dates, astropy units, etc. ignore_local (bool, optional): If local files should be ignore, see Notes for details. Returns: dict: A dictionary of config items """ # Default to the pocs.yaml file if config_files is None: config_files = ['pocs'] config_files = listify(config_files) config = dict() config_dir = '{}/conf_files'.format(os.getenv('POCS')) for f in config_files: if not f.endswith('.yaml'): f = '{}.yaml'.format(f) if not f.startswith('/'): path = os.path.join(config_dir, f) else: path = f try: _add_to_conf(config, path) except Exception as e: warn("Problem with config file {}, skipping. {}".format(path, e)) # Load local version of config if not ignore_local: local_version = os.path.join(config_dir, f.replace('.', '_local.')) if os.path.exists(local_version): try: _add_to_conf(config, local_version) except Exception: warn("Problem with local config file {}, skipping".format( local_version)) if simulator is not None: config['simulator'] = hardware.get_simulator_names(simulator=simulator) if parse: config = _parse_config(config) return config
def load_config(config_files=None, simulator=None, parse=True, ignore_local=False): """Load configuation information This function supports loading of a number of different files. If no options are passed to `config_files` then the default `$POCS/conf_files/pocs.yaml` will be loaded. See Notes for additional information. Notes: The `config_files` parameter supports a number of options: * `config_files` is a list and loaded in order, so the first entry will have any values overwritten by similarly named keys in the second entry. * Entries can be placed in the `$POCS/conf_files` folder and should be passed as just the file name, e.g. [`weather.yaml`, `email.yaml`] for loading `$POCS/conf_files/weather.yaml` and `$POCS/conf_files/email.yaml` * The `.yaml` extension will be added if not present, so list can be written as just ['weather', 'email']. * `config_files` can also be specified by an absolute path, which can exist anywhere on the filesystem. * Local versions of files can override built-in versions and are automatically loaded if placed in the `$POCS/conf_files` folder. The files have a `<>_local.yaml` name, where `<>` is the built-in file. So a `$POCS/conf_files/pocs_local.yaml` will override any setting in the default `pocs.yaml` file. * Local files can be ignored (mostly for testing purposes) with the `ignore_local` parameter. Args: config_files (list, optional): A list of files to load as config, see Notes for details of how to specify files. simulator (list, optional): A list of hardware items that should be used as a simulator. parse (bool, optional): If the config file should attempt to create objects such as dates, astropy units, etc. ignore_local (bool, optional): If local files should be ignore, see Notes for details. Returns: dict: A dictionary of config items """ # Default to the pocs.yaml file if config_files is None: config_files = ['pocs'] config_files = listify(config_files) config = dict() config_dir = '{}/conf_files'.format(os.getenv('POCS')) for f in config_files: if not f.endswith('.yaml'): f = '{}.yaml'.format(f) if not f.startswith('/'): path = os.path.join(config_dir, f) else: path = f try: _add_to_conf(config, path) except Exception as e: warn("Problem with config file {}, skipping. {}".format(path, e)) # Load local version of config if not ignore_local: local_version = os.path.join(config_dir, f.replace('.', '_local.')) if os.path.exists(local_version): try: _add_to_conf(config, local_version) except Exception: warn("Problem with local config file {}, skipping".format(local_version)) if simulator is not None: config['simulator'] = hardware.get_simulator_names(simulator=simulator) if parse: config = _parse_config(config) return config
def get_observation(self, time=None, show_all=False, reread_fields_file=False): """Get a valid observation Args: time (astropy.time.Time, optional): Time at which scheduler applies, defaults to time called show_all (bool, optional): Return all valid observations along with merit value, defaults to False to only get top value reread_fields_file (bool, optional): If targets file should be reread before getting observation, default False. Returns: tuple or list: A tuple (or list of tuples) with name and score of ranked observations """ if reread_fields_file: self.logger.debug("Rereading fields file") # The setter method on `fields_file` will force a reread self.fields_file = self.fields_file if time is None: time = current_time() valid_obs = {obs: 1.0 for obs in self.observations} best_obs = [] common_properties = { 'end_of_night': self.observer.tonight(time=time, horizon=-18 * u.degree)[-1], 'moon': get_moon(time, self.observer.location) } for constraint in listify(self.constraints): self.logger.debug("Checking Constraint: {}".format(constraint)) for obs_name, observation in self.observations.items(): if obs_name in valid_obs: self.logger.debug("\tObservation: {}".format(obs_name)) veto, score = constraint.get_score( time, self.observer, observation, **common_properties) self.logger.debug("\t\tScore: {}\tVeto: {}".format(score, veto)) if veto: self.logger.debug("\t\t{} vetoed by {}".format(obs_name, constraint)) del valid_obs[obs_name] continue valid_obs[obs_name] += score for obs_name, score in valid_obs.items(): valid_obs[obs_name] += self.observations[obs_name].priority if len(valid_obs) > 0: # Sort the list by highest score (reverse puts in correct order) best_obs = sorted(valid_obs.items(), key=lambda x: x[1])[::-1] top_obs = best_obs[0] # Check new best against current_observation if self.current_observation is not None \ and top_obs[0] != self.current_observation.name: # Favor the current observation if still available end_of_next_set = time + self.current_observation.set_duration if self.observation_available(self.current_observation, end_of_next_set): # If current is better or equal to top, use it if self.current_observation.merit >= top_obs[1]: best_obs.insert(0, self.current_observation) # Set the current self.current_observation = self.observations[top_obs[0]] self.current_observation.merit = top_obs[1] else: if self.current_observation is not None: # Favor the current observation if still available end_of_next_set = time + self.current_observation.set_duration if end_of_next_set < common_properties['end_of_night'] and \ self.observation_available(self.current_observation, end_of_next_set): self.logger.debug("Reusing {}".format(self.current_observation)) best_obs = [(self.current_observation.name, self.current_observation.merit)] else: self.logger.warning("No valid observations found") self.current_observation = None if not show_all and len(best_obs) > 0: best_obs = best_obs[0] return best_obs
def test_empty_listify(): assert listify(None) == []
def test_listify(): assert listify(12) == [12] assert listify([1, 2, 3]) == [1, 2, 3]
def get_observation(self, time=None, show_all=False, reread_fields_file=False): """Get a valid observation Args: time (astropy.time.Time, optional): Time at which scheduler applies, defaults to time called show_all (bool, optional): Return all valid observations along with merit value, defaults to False to only get top value reread_fields_file (bool, optional): If targets file should be reread before getting observation, default False. Returns: tuple or list: A tuple (or list of tuples) with name and score of ranked observations """ if reread_fields_file: self.logger.debug("Rereading fields file") # The setter method on `fields_file` will force a reread self.fields_file = self.fields_file if time is None: time = current_time() valid_obs = {obs: 1.0 for obs in self.observations} best_obs = [] common_properties = { 'end_of_night': self.observer.tonight(time=time, horizon=-18 * u.degree)[-1], 'moon': get_moon(time, self.observer.location) } for constraint in listify(self.constraints): self.logger.debug("Checking Constraint: {}".format(constraint)) for obs_name, observation in self.observations.items(): if obs_name in valid_obs: self.logger.debug("\tObservation: {}".format(obs_name)) veto, score = constraint.get_score(time, self.observer, observation, **common_properties) self.logger.debug("\t\tScore: {}\tVeto: {}".format( score, veto)) if veto: self.logger.debug("\t\t{} vetoed by {}".format( obs_name, constraint)) del valid_obs[obs_name] continue valid_obs[obs_name] += score for obs_name, score in valid_obs.items(): valid_obs[obs_name] += self.observations[obs_name].priority if len(valid_obs) > 0: # Sort the list by highest score (reverse puts in correct order) best_obs = sorted(valid_obs.items(), key=lambda x: x[1])[::-1] top_obs = best_obs[0] # Check new best against current_observation if self.current_observation is not None \ and top_obs[0] != self.current_observation.name: # Favor the current observation if still available end_of_next_set = time + self.current_observation.set_duration if self.observation_available(self.current_observation, end_of_next_set): # If current is better or equal to top, use it if self.current_observation.merit >= top_obs[1]: best_obs.insert(0, self.current_observation) # Set the current self.current_observation = self.observations[top_obs[0]] self.current_observation.merit = top_obs[1] else: if self.current_observation is not None: # Favor the current observation if still available end_of_next_set = time + self.current_observation.set_duration if end_of_next_set < common_properties['end_of_night'] and \ self.observation_available(self.current_observation, end_of_next_set): self.logger.debug("Reusing {}".format( self.current_observation)) best_obs = [(self.current_observation.name, self.current_observation.merit)] else: self.logger.warning("No valid observations found") self.current_observation = None if not show_all and len(best_obs) > 0: best_obs = best_obs[0] return best_obs
def field(self, values): assert all(isinstance(f, Field) for f in listify(values)), \ self.logger.error("All fields must be a valid Field instance") self._field = listify(values)
def exp_time(self, values): assert all(t > 0.0 for t in listify(values)), \ self.logger.error("Exposure times (exp_time) must be greater than 0") self._exp_time = listify(values)