diff --git a/python/lsst/ts/observatory/control/base_calsys.py b/python/lsst/ts/observatory/control/base_calsys.py index f22a5127..d293eb06 100644 --- a/python/lsst/ts/observatory/control/base_calsys.py +++ b/python/lsst/ts/observatory/control/base_calsys.py @@ -26,6 +26,7 @@ import logging import typing +import jsonschema import yaml from lsst.ts import salobj from lsst.ts.xml.enums.Electrometer import UnitToRead @@ -166,19 +167,35 @@ def load_calibration_config_file(self, filename: str | None = None) -> None: the class name. However, it is possible to provide an override with the full file name. + It performs a schema validation if the configuration + schema file is present. The name of the schema file is + the class name with a "_schema" suffix and may or may + not be present. When the validation is performed, an + exception is raised if it fails. + Parameters ---------- filename : `str`, optional Alternative file name with the calibration configuration file. + + Raises + ------ + RuntimeError: + If configuration fails validation. + """ + base_name = type(self).__name__.lower() + data_path = ( - (get_data_path() / f"{type(self).__name__.lower()}.yaml").as_posix() + (get_data_path() / f"{base_name}.yaml").as_posix() if filename is None else filename ) + schema_path = (get_data_path() / f"{base_name}_schema.yaml").as_posix() + if len(self.calibration_config) > 0: self.log.warning( "Calibration configuration already loaded." @@ -188,6 +205,25 @@ def load_calibration_config_file(self, filename: str | None = None) -> None: with open(data_path, "r") as f: self.calibration_config = yaml.safe_load(f) + config_schema = None + try: + with open(schema_path, "r") as f: + config_schema = yaml.safe_load(f) + except FileNotFoundError: + self.log.warning( + f"Configuration schema file {schema_path} not found. " + f"No schema validation is performed for {data_path}." + ) + + if config_schema: + for item in self.calibration_config: + try: + jsonschema.validate( + instance=self.calibration_config[item], schema=config_schema + ) + except jsonschema.ValidationError as validation_error: + raise RuntimeError(validation_error.message) + def get_calibration_configuration(self, name: str) -> dict[str, typing.Any]: """Returns the configuration attributes given a configuration name. diff --git a/python/lsst/ts/observatory/control/data/atcalsys_schema.yaml b/python/lsst/ts/observatory/control/data/atcalsys_schema.yaml new file mode 100644 index 00000000..5215abd2 --- /dev/null +++ b/python/lsst/ts/observatory/control/data/atcalsys_schema.yaml @@ -0,0 +1,59 @@ +$id: https://github.com/lsst-ts/ts_observatory_control/blob/develop/python/lsst/ts/observatory/control/data/atcalsys_schema.yaml +$schema: http://json-schema.org/draft-07/schema# +title: Configuration file validation schema of Auxiliary Telescope Calibration System +type: object +properties: + calib_type: + type: string + use_camera: + type: boolean + atspec_filter: + type: string + atspec_grating: + type: string + wavelength: + type: number + wavelength_width: + type: number + wavelength_resolution: + type: number + monochromator_grating: + type: string + exit_slit: + type: number + entrance_slit: + type: number + electrometer_integration_time: + type: number + electrometer_mode: + type: string + electrometer_range: + type: number + use_fiberspectrograph: + type: boolean + use_electrometer: + type: boolean + exposure_times: + type: array + items: + type: number + + electrometer_exposure_time: + type: number + fiber_spectrum_exposure_time: + type: number +required: +- calib_type +- use_camera +- atspec_filter +- atspec_grating +- wavelength +- monochromator_grating +- exit_slit +- entrance_slit +- electrometer_integration_time +- electrometer_mode +- electrometer_range +- use_fiberspectrograph +- use_electrometer +- exposure_times