From 373e85fbfc355f51f015d762a314f90fd35e8d63 Mon Sep 17 00:00:00 2001 From: Wilfred Tyler Gee Date: Sat, 11 May 2024 19:17:55 -1000 Subject: [PATCH] POCS status store (#1264) * POCS status * Store the status in the db, which sends to firestore. * Don't store the state info permanently. * Default all db insert_current to not storing permanently. This could be done better. * Format. * Fixing a number of units in the observatory status. * Fixing a number of units in the observatory status. * More formatting. * Change the statue and include the next state. * Add default directory Tested on `PAN007`. --- src/panoptes/pocs/base.py | 2 +- src/panoptes/pocs/core.py | 37 +++++++++++++++---------- src/panoptes/pocs/mount/ioptron/base.py | 12 ++++---- src/panoptes/pocs/mount/mount.py | 10 ++++--- src/panoptes/pocs/observatory.py | 22 ++++++++------- src/panoptes/pocs/sensor/power.py | 2 +- src/panoptes/pocs/sensor/remote.py | 4 +-- src/panoptes/pocs/sensor/weather.py | 2 +- src/panoptes/pocs/state/machine.py | 18 +++++++----- 9 files changed, 62 insertions(+), 47 deletions(-) diff --git a/src/panoptes/pocs/base.py b/src/panoptes/pocs/base.py index fb983fbd4..72c213168 100644 --- a/src/panoptes/pocs/base.py +++ b/src/panoptes/pocs/base.py @@ -24,7 +24,7 @@ def __init__(self, config_host=None, config_port=None, *args, **kwargs): self._config_host = config_host or os.getenv('PANOPTES_CONFIG_HOST', 'localhost') self._config_port = config_port or os.getenv('PANOPTES_CONFIG_PORT', 6563) - log_dir = self.get_config('directories.base') + '/../logs' + log_dir = self.get_config('directories.base', default='.') + '/../logs' cloud_logging_level = kwargs.get( 'cloud_logging_level', self.get_config('panoptes_network.cloud_logging_level', default=None) diff --git a/src/panoptes/pocs/core.py b/src/panoptes/pocs/core.py index 6ece0a44c..d6bdab41f 100644 --- a/src/panoptes/pocs/core.py +++ b/src/panoptes/pocs/core.py @@ -13,7 +13,6 @@ from panoptes.pocs.scheduler.observation.base import Observation from panoptes.pocs.state.machine import PanStateMachine from panoptes.pocs.utils import error -from panoptes.pocs.utils.logger import get_logger class POCS(PanStateMachine, PanBase): @@ -47,7 +46,8 @@ def __init__( observatory, state_machine_file=None, simulators=None, - *args, **kwargs): + *args, **kwargs + ): # Explicitly call the base class. PanBase.__init__(self, *args, **kwargs) @@ -158,19 +158,21 @@ def should_retry(self): return self._obs_run_retries >= 0 @property - def status(self): - status = dict() - + def status(self) -> dict: try: - status['state'] = self.state - status['system'] = { - 'free_space': str(self._free_space), + status = { + 'from_state': self.state, + 'to_state': self.next_state, + 'system': { + 'free_space': str(self._free_space), + }, + 'observatory': self.observatory.status } - status['observatory'] = self.observatory.status + self.db.insert_current('status', status, store_permanently=False) + return status except Exception as e: # pragma: no cover self.logger.warning(f"Can't get status: {e!r}") - - return status + return {} ################################################################################################ # Methods @@ -266,7 +268,8 @@ def reset_observing_run(self): def observe_target(self, observation: Optional[Observation] = None, - park_if_unsafe: bool = True): + park_if_unsafe: bool = True + ): """Observe something! 🔭🌠 Note: This is a long-running blocking method. @@ -375,7 +378,7 @@ def is_safe(self, no_warning=False, horizon='observe', ignore=None, park_if_not_ safe = all([v for k, v in is_safe_values.items() if k not in ignore]) # Insert safety reading - self.db.insert_current('safety', is_safe_values) + self.db.insert_current('safety', is_safe_values, store_permanently=False) if not safe: if no_warning is False: @@ -465,8 +468,12 @@ def is_weather_safe(self, stale=180): return is_safe - def has_free_space(self, directory=None, required_space=0.25 * u.gigabyte, - low_space_percent=1.5): + def has_free_space( + self, + directory=None, + required_space=0.25 * u.gigabyte, + low_space_percent=1.5 + ): """Does hard drive have disk space (>= 0.5 GB). Args: diff --git a/src/panoptes/pocs/mount/ioptron/base.py b/src/panoptes/pocs/mount/ioptron/base.py index bd00f8b3f..b001251d7 100644 --- a/src/panoptes/pocs/mount/ioptron/base.py +++ b/src/panoptes/pocs/mount/ioptron/base.py @@ -303,7 +303,7 @@ def _update_status(self): status_dict = status_match.groupdict() self._state = MountState(int(status_dict['state'])) - status['state'] = self.state + status['state'] = self.state.name status['parked_software'] = self.is_parked coords_unit = getattr(u, self._location_units) @@ -311,14 +311,14 @@ def _update_status(self): # Longitude adds +90° to avoid negative numbers, so subtract for original. status['latitude'] = Latitude((float(status_dict['latitude']) * coords_unit).to(u.degree) - (90 * u.degree)) - status['gps'] = MountGPS(int(status_dict['gps'])) - status['tracking'] = MountTrackingState(int(status_dict['tracking'])) + status['gps'] = MountGPS(int(status_dict['gps'])).name + status['tracking'] = MountTrackingState(int(status_dict['tracking'])).name self._movement_speed = MountMovementSpeed(int(status_dict['movement_speed'])) - status['movement_speed'] = self._movement_speed + status['movement_speed'] = self._movement_speed.name - status['time_source'] = MountTimeSource(int(status_dict['time_source'])) - status['hemisphere'] = MountHemisphere(int(status_dict['hemisphere'])) + status['time_source'] = MountTimeSource(int(status_dict['time_source'])).name + status['hemisphere'] = MountHemisphere(int(status_dict['hemisphere'])).name self._at_mount_park = self.state == MountState.PARKED self._is_home = self.state == MountState.AT_HOME diff --git a/src/panoptes/pocs/mount/mount.py b/src/panoptes/pocs/mount/mount.py index 33466f534..a01f3452a 100644 --- a/src/panoptes/pocs/mount/mount.py +++ b/src/panoptes/pocs/mount/mount.py @@ -5,6 +5,8 @@ from astropy import units as u from astropy.coordinates import EarthLocation from astropy.coordinates import SkyCoord +from panoptes.utils.utils import get_quantity_value + from panoptes.pocs.base import PanBase from panoptes.utils.serializers import from_yaml from panoptes.utils.time import current_time @@ -125,13 +127,13 @@ def status(self): current_coord = self.get_current_coordinates() if current_coord is not None: - status['current_ra'] = current_coord.ra - status['current_dec'] = current_coord.dec + status['current_ra'] = get_quantity_value(current_coord.ra, unit='degree') + status['current_dec'] = get_quantity_value(current_coord.dec, unit='degree') if self.has_target: target_coord = self.get_target_coordinates() - status['mount_target_ra'] = target_coord.ra - status['mount_target_dec'] = target_coord.dec + status['mount_target_ra'] = get_quantity_value(target_coord.ra, unit='degree') + status['mount_target_dec'] = get_quantity_value(target_coord.dec, unit='degree') except Exception as e: self.logger.debug(f'Problem getting mount status: {e!r}') diff --git a/src/panoptes/pocs/observatory.py b/src/panoptes/pocs/observatory.py index ca3f73eb5..7df728ca8 100644 --- a/src/panoptes/pocs/observatory.py +++ b/src/panoptes/pocs/observatory.py @@ -1,11 +1,11 @@ import os from collections import OrderedDict from contextlib import suppress -from datetime import datetime from multiprocessing import Process from pathlib import Path from typing import Dict, Optional +import numpy as np from astropy import units as u from astropy.coordinates import get_body from astropy.io.fits import setval @@ -13,6 +13,7 @@ from panoptes.utils import images as img_utils from panoptes.utils.images import fits as fits_utils from panoptes.utils.time import current_time, CountdownTimer, flatten_time +from panoptes.utils.utils import get_quantity_value import panoptes.pocs.camera.fli from panoptes.pocs.base import PanBase @@ -290,11 +291,13 @@ def status(self): if self.mount and self.mount.is_initialized: status['mount'] = self.mount.status current_coords = self.mount.get_current_coordinates() - status['mount']['current_ha'] = self.observer.target_hour_angle(now, current_coords) + status['mount']['current_ha'] = get_quantity_value( + self.observer.target_hour_angle(now, current_coords), unit='degree' + ) if self.mount.has_target: target_coords = self.mount.get_target_coordinates() target_ha = self.observer.target_hour_angle(now, target_coords) - status['mount']['mount_target_ha'] = target_ha + status['mount']['mount_target_ha'] = get_quantity_value(target_ha, unit='degree') except Exception as e: # pragma: no cover self.logger.warning(f"Can't get mount status: {e!r}") @@ -314,17 +317,16 @@ def status(self): try: status['observer'] = { - 'siderealtime': str(self.sidereal_time), + 'siderealtime': get_quantity_value(self.sidereal_time, unit='degree'), 'utctime': now, - 'localtime': datetime.now(), 'local_evening_astro_time': self._evening_astro_time, 'local_morning_astro_time': self._morning_astro_time, 'local_sun_set_time': self._local_sunset, 'local_sun_rise_time': self._local_sunrise, - 'local_sun_position': self._local_sun_pos, - 'local_moon_alt': self.observer.moon_altaz(now).alt, + 'local_sun_position': get_quantity_value(self._local_sun_pos, unit='degree'), + 'local_moon_alt': get_quantity_value(self.observer.moon_altaz(now).alt, unit='degree'), 'local_moon_illumination': self.observer.moon_illumination(now), - 'local_moon_phase': self.observer.moon_phase(now), + 'local_moon_phase': get_quantity_value(self.observer.moon_phase(now)) / np.pi, } except Exception as e: # pragma: no cover @@ -501,7 +503,7 @@ def process_observation( ): self.logger.debug(f"Adding current observation to db: {image_id}") metadata['status'] = 'complete' - self.db.insert_current('images', metadata) + self.db.insert_current('images', metadata, store_permanently=False) if make_pretty_images or self.get_config( 'observations.make_pretty_images', @@ -575,7 +577,7 @@ def analyze_recent(self): 'd_dec': self.current_offset_info.delta_dec.value, 'magnitude': self.current_offset_info.magnitude.value, 'unit': 'arcsec', - } + }, store_permanently=False ) except error.SolveError: diff --git a/src/panoptes/pocs/sensor/power.py b/src/panoptes/pocs/sensor/power.py index e25ba62c4..2f882fda1 100644 --- a/src/panoptes/pocs/sensor/power.py +++ b/src/panoptes/pocs/sensor/power.py @@ -242,7 +242,7 @@ def record(self, collection_name: str = None): recent_values = self.readings collection_name = collection_name or self.arduino_board_name - self.db.insert_current(collection_name, recent_values) + self.db.insert_current(collection_name, recent_values, store_permanently=False) return recent_values diff --git a/src/panoptes/pocs/sensor/remote.py b/src/panoptes/pocs/sensor/remote.py index 036eef6c5..c8d65e28d 100644 --- a/src/panoptes/pocs/sensor/remote.py +++ b/src/panoptes/pocs/sensor/remote.py @@ -55,11 +55,11 @@ def capture(self, store_result: bool = True) -> dict: sensor_data['date'] = current_time(flatten=True) if store_result and len(sensor_data) > 0: - self.db.insert_current(self.sensor_name, sensor_data) + self.db.insert_current(self.sensor_name, sensor_data, store_permanently=False) # Make a separate power entry if 'power' in sensor_data: - self.db.insert_current('power', sensor_data['power']) + self.db.insert_current('power', sensor_data['power'], store_permanently=False) self.logger.debug(f'Remote data: {sensor_data}') diff --git a/src/panoptes/pocs/sensor/weather.py b/src/panoptes/pocs/sensor/weather.py index e4a975f37..c98360df1 100644 --- a/src/panoptes/pocs/sensor/weather.py +++ b/src/panoptes/pocs/sensor/weather.py @@ -58,7 +58,7 @@ def record(self): """Record the rolling mean of the power readings in the database.""" recent_values = self.weather_station.get_reading() - self.db.insert_current(self.collection_name, recent_values) + self.db.insert_current(self.collection_name, recent_values, store_permanently=False) return recent_values diff --git a/src/panoptes/pocs/state/machine.py b/src/panoptes/pocs/state/machine.py index 61ad8f723..8f67d23a8 100644 --- a/src/panoptes/pocs/state/machine.py +++ b/src/panoptes/pocs/state/machine.py @@ -1,14 +1,13 @@ -import os from contextlib import suppress from pathlib import Path -from transitions.extensions.states import Tags as MachineState -from transitions import Machine from panoptes.utils import error -from panoptes.utils.utils import listify +from panoptes.utils.config.client import get_config from panoptes.utils.library import load_module from panoptes.utils.serializers import from_yaml -from panoptes.utils.config.client import get_config +from panoptes.utils.utils import listify +from transitions import Machine +from transitions.extensions.states import Tags as MachineState class PanStateMachine(Machine): @@ -85,7 +84,8 @@ def next_state(self, value): ################################################################################################ def run(self, exit_when_done=False, run_once=False, park_when_done=True, - initial_next_state='ready'): + initial_next_state='ready' + ): """Runs the state machine loop. This runs the state machine in a loop. Setting the machine property @@ -218,7 +218,11 @@ def goto_next_state(self): state_changed = transition_method() if state_changed: self.logger.success(f'Finished with {self.state} state') - self.db.insert_current('state', {"source": self.state, "dest": self.next_state}) + self.db.insert_current( + 'state', + {"source": self.state, "dest": self.next_state}, + store_permanently=False + ) return state_changed