Source code for panoptes.pocs.mount
from contextlib import suppress
from glob import glob
from panoptes.utils import error
from panoptes.utils.config.client import get_config, set_config
from panoptes.utils.library import load_module
from panoptes.pocs.mount.mount import AbstractMount # noqa
from panoptes.pocs.utils.location import create_location_from_config
from panoptes.pocs.utils.logger import get_logger
logger = get_logger()
[docs]
def create_mount_from_config(mount_info=None, earth_location=None, *args, **kwargs) -> AbstractMount:
"""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
brand = mount_info.get("brand")
driver = mount_info.get("driver")
model = mount_info.get("model", driver)
if not driver or not isinstance(driver, str):
raise error.MountNotFound("Mount info in config is missing a driver name.")
logger.debug(f"Mount: {brand=} {driver=} {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 ("simulator" in driver):
logger.debug("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) and not port.startswith("socket"):
if port == "loop://":
logger.warning("Using loop:// for mount connection.")
else:
raise error.MountNotFound(msg=f"Mount {port=} not available.")
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=}")
try:
module = load_module(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
[docs]
def create_mount_simulator(mount_info=None, earth_location=None, db_type="memory", *args, **kwargs):
"""Create and return a simulator-backed Mount instance.
Updates the config to point the 'mount' section at the simulator driver and
constructs a Mount object from panoptes.pocs.mount.simulator using the
provided EarthLocation (or one derived from configuration).
Args:
mount_info (dict | None): Optional dict to write to the 'mount' config. If None,
uses a default simulator config pointing at panoptes.pocs.mount.simulator.
earth_location (astropy.coordinates.EarthLocation | None): Site location to pass to the
Mount constructor. If None, it is created from configuration via
create_location_from_config().
db_type (str): Database backend type for the PanBase used by the Mount (default 'memory').
*args: Additional positional args forwarded to the simulator Mount constructor.
**kwargs: Additional keyword args forwarded to the simulator Mount constructor.
Returns:
AbstractMount: A newly constructed simulator Mount instance.
"""
# 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": "panoptes.pocs.mount.simulator",
"serial": {"port": "/dev/FAKE"},
}
# Set mount device info to simulator
set_config("mount", mount_config)
earth_location = earth_location
if earth_location is None:
earth_location = create_location_from_config().earth_location
logger.debug(f"Loading mount driver: {mount_config['driver']}")
try:
module = load_module(f"{mount_config['driver']}")
except error.NotFound as e:
raise error.MountNotFound(f"Error loading mount module: {e!r}")
mount_obj = module.Mount(earth_location, db_type=db_type, *args, **kwargs)
logger.success(f"{mount_config['driver'].title()} mount created")
return mount_obj