diff --git a/src/core/pyros_django/obsconfig/configpyros.py b/src/core/pyros_django/obsconfig/configpyros.py index e6bdc4c..3b0434b 100644 --- a/src/core/pyros_django/obsconfig/configpyros.py +++ b/src/core/pyros_django/obsconfig/configpyros.py @@ -175,25 +175,26 @@ class ConfigPyros: def read_capability_of_device(self,capability:dict)->dict: """ - + Read capability of device and inherit attributes from generic component then overwrite attributes defined in device config Args: - dict ([type]): [description] + capability (dict): dictionary containing a capabilitiy (keys : component and attributes) Returns: - dict: [description] + dict: dictionary of capability inherited by generic component and overwritten his attributes by current attributes of capability """ component_attributes = self.read_generic_component_and_return_attributes(capability["component"]) attributes = {} + # get all attributes of device's capability for attribute in capability["attributes"]: attribute = attribute["attribute"] attributes[attribute.pop("key")] = attribute + # for each attributes of generic component attributes for attribute_name in attributes.keys(): - # merging values from general component to specific values of component in config file if "is_Enum" in component_attributes[attribute_name].keys(): # check if value of this attribute is in Enum of general component attribute definition enum_values = component_attributes[attribute_name]["value"] @@ -207,6 +208,7 @@ class ConfigPyros: new_attributes = {**component_attributes[attribute_name],**attributes[attribute_name]} component_attributes[attribute_name] = new_attributes + # return inherited and overwritten attributes of capability capability["attributes"] = component_attributes return capability @@ -215,9 +217,9 @@ class ConfigPyros: def get_devices_names_and_file(self)->dict: """ - + Return a dictionary giving the device file name by the device name Returns: - dict: [description] + dict: key is device name, value is file name """ devices_names_and_files = {} for device in self.obs_config["OBSERVATORY"]["DEVICES"]: @@ -228,74 +230,116 @@ class ConfigPyros: def read_device_config_file(self,config_file_name:str,is_generic=False)->dict: """ - + Read the device config file, inherit generic device config if "generic" key is present in "DEVICE". + Associate capabilities of attached_devices if this device has attached_devices. + Inherit capabilities from generic component and overwritte attributes defined in the device config Args: - config_file_name (str): [description] + config_file_name (str): file name to be read + is_generic (bool, optional): tells if we're reading a generic configuration (is_generic =True) or not (is_generic = False). Defaults to False. Returns: - dict: [description] + dict: formatted device configuration (attributes, capabilities...) """ self.current_file = config_file_name print(f"Reading {config_file_name}") try: with open(config_file_name, 'r') as stream: config_file = yaml.safe_load(stream) + # if we're reading a generic device configuration, the path to get the schema is different than usual if is_generic: self.SCHEMA_PATH = os.path.join(os.path.abspath(os.path.dirname(config_file_name)),"../schemas/") else: self.SCHEMA_PATH = os.path.join(os.path.abspath(os.path.dirname(config_file_name)),"../../../config/schemas/") + # read and verify that the device configuration match the schema result = self.check_and_return_config(config_file_name,self.SCHEMA_PATH+config_file["schema"]) + # if the configuration didn't match the schema or had an error when reading the file if result == None: print("Error when reading and validating config file, please check the errors right above") exit(-1) else: + # the configuration is valid + # storing DEVICE key in device (DEVICE can contains : attributes of device, capabilities, attached devices) device = result["DEVICE"] - + generic_device_config = None + # if the device is associated to an generic configuration, we'll read that generic configuration to inherit his attributes if "generic" in device: + # storing the whole current config current_config = result - generic_device_config = self.read_device_config_file(self.GENERIC_DEVICES_PATH+device["generic"],True) - + # read and get the generic device config + generic_device_config = self.read_device_config_file(self.GENERIC_DEVICES_PATH+device["generic"],is_generic=True) + # merge whole device config but we need to merge capabilities differently after new_config = {**generic_device_config["DEVICE"],**current_config["DEVICE"]} - result["DEVICE"] = new_config + # device has capabilities if "CAPABILITIES" in device: capabilities = [] - for capability in device["CAPABILITIES"]: - capability = capability["CAPABILITY"] - - capabilities.append(self.read_capability_of_device(capability)) - - + # if the device is associated to a generic device we need to associate his capabilities with the generic capabilities and overwrite them + if generic_device_config != None: + # we're making a copy of generic device config so we can remove items during loop + copy_generic_device_config = generic_device_config.copy() + # We have to extend capabilities of generic device configuration's capabilities + for capability in current_config["DEVICE"]["CAPABILITIES"]: + is_capability_in_generic_config = False + current_config_capability = capability["CAPABILITY"] + current_config_component = current_config_capability["component"] + current_config_capability = self.read_capability_of_device(current_config_capability) + # find if this component was defined in generic_device_config + for index,generic_config_capability in enumerate(generic_device_config["DEVICE"]["CAPABILITIES"]): + # if the current capability is the capability of the component we're looking for + if current_config_component == generic_config_capability["component"]: + is_capability_in_generic_config = True + # we're merging their attributes + merged_attributes = {**generic_config_capability["attributes"],**current_config_capability["attributes"]} + # removing this capability from generic device configuration + generic_device_config["DEVICE"]["CAPABILITIES"].pop(index) + capabilities.append({"component": current_config_component,"attributes":merged_attributes}) + break + if is_capability_in_generic_config == False: + # the component defined in the current_config isn't defined in generic config (should not happen but we'll deal with that case anyway) : we're simply adding this capability + capabilities.append(current_config_capability) + # looping through generic device config's capabilities in order to add them to current device configuration + for generic_config_capability in generic_device_config["DEVICE"]["CAPABILITIES"]: + capabilities.append(generic_config_capability) + else: + # device not associated to a generic device configuration + for capability in device["CAPABILITIES"]: + capability = capability["CAPABILITY"] + capabilities.append(self.read_capability_of_device(capability)) device["CAPABILITIES"] = capabilities + # associate capabilities to final device configuration (stored in result variable) + result["DEVICE"]["CAPABILITIES"] = device["CAPABILITIES"] if "ATTACHED_DEVICES" in device.keys(): - + # device has attached devices, we need to read their configuration in order to get their capabilities and add them to the current device devices_name_and_file = self.get_devices_names_and_file() active_devices = self.get_active_devices() for attached_device in device["ATTACHED_DEVICES"]: is_attached_device_link_to_agent = False + # we're looking for if the attached device is associated to an agent (i.e. the device is considered as 'active'). However an attached_device shoudn't be active for active_device in active_devices: if devices_name_and_file[active_device] == attached_device["file"]: + # the attached device is an active device (so it's linked to an agent) is_attached_device_link_to_agent = True break - if self.CONFIG_PATH+attached_device["file"] != config_file_name and not is_attached_device_link_to_agent: + # if the attached device isn't the device itself and not active + # get configuration of attached device config_of_attached_device = self.read_device_config_file(self.CONFIG_PATH+attached_device["file"]) - capabilities_of_attached_device = None if "CAPABILITIES" in config_of_attached_device["DEVICE"].keys(): capabilities_of_attached_device = config_of_attached_device["DEVICE"]["CAPABILITIES"] if capabilities_of_attached_device != None: # get name of device corresponding to the config file name - parent_device_name = [device for device,file_name in devices_name_and_file.items() if file_name == config_file_name[len(self.CONFIG_PATH):] ][0] - + parent_device_name = [device for device,file_name in devices_name_and_file.items() if file_name == config_file_name[len(self.CONFIG_PATH):]][0] attached_device_name = [device for device,file_name in devices_name_and_file.items() if file_name == attached_device["file"]][0] + # associate attached device to his 'parent' device (parent device is the currently read device) self.devices_links[attached_device_name] = parent_device_name for capability in capabilities_of_attached_device: + # add capabilities of attached device to current device result["DEVICE"]["CAPABILITIES"].append(capability) return result except yaml.YAMLError as exc: diff --git a/src/core/pyros_django/obsconfig/templates/obsconfig/obs_hardware_configuration.html b/src/core/pyros_django/obsconfig/templates/obsconfig/obs_hardware_configuration.html index 408e12f..e49b3e4 100644 --- a/src/core/pyros_django/obsconfig/templates/obsconfig/obs_hardware_configuration.html +++ b/src/core/pyros_django/obsconfig/templates/obsconfig/obs_hardware_configuration.html @@ -21,8 +21,12 @@