Commit b4d06c1f6b97ce9d8984e001cde3f5f159897ee8
1 parent
be09bdbc
Exists in
dev
Improving device configuration inheritance of an generic configuration (add capa…
…bilities inheritance), on hardware configuration page now display if device is an attached device
Showing
2 changed files
with
74 additions
and
26 deletions
Show diff stats
src/core/pyros_django/obsconfig/configpyros.py
... | ... | @@ -175,25 +175,26 @@ class ConfigPyros: |
175 | 175 | |
176 | 176 | def read_capability_of_device(self,capability:dict)->dict: |
177 | 177 | """ |
178 | - | |
178 | + Read capability of device and inherit attributes from generic component then overwrite attributes defined in device config | |
179 | 179 | |
180 | 180 | Args: |
181 | - dict ([type]): [description] | |
181 | + capability (dict): dictionary containing a capabilitiy (keys : component and attributes) | |
182 | 182 | |
183 | 183 | Returns: |
184 | - dict: [description] | |
184 | + dict: dictionary of capability inherited by generic component and overwritten his attributes by current attributes of capability | |
185 | 185 | """ |
186 | 186 | |
187 | 187 | component_attributes = self.read_generic_component_and_return_attributes(capability["component"]) |
188 | 188 | |
189 | 189 | attributes = {} |
190 | + # get all attributes of device's capability | |
190 | 191 | for attribute in capability["attributes"]: |
191 | 192 | attribute = attribute["attribute"] |
192 | 193 | |
193 | 194 | attributes[attribute.pop("key")] = attribute |
194 | 195 | |
196 | + # for each attributes of generic component attributes | |
195 | 197 | for attribute_name in attributes.keys(): |
196 | - # merging values from general component to specific values of component in config file | |
197 | 198 | if "is_Enum" in component_attributes[attribute_name].keys(): |
198 | 199 | # check if value of this attribute is in Enum of general component attribute definition |
199 | 200 | enum_values = component_attributes[attribute_name]["value"] |
... | ... | @@ -207,6 +208,7 @@ class ConfigPyros: |
207 | 208 | new_attributes = {**component_attributes[attribute_name],**attributes[attribute_name]} |
208 | 209 | component_attributes[attribute_name] = new_attributes |
209 | 210 | |
211 | + # return inherited and overwritten attributes of capability | |
210 | 212 | capability["attributes"] = component_attributes |
211 | 213 | return capability |
212 | 214 | |
... | ... | @@ -215,9 +217,9 @@ class ConfigPyros: |
215 | 217 | def get_devices_names_and_file(self)->dict: |
216 | 218 | """ |
217 | 219 | |
218 | - | |
220 | + Return a dictionary giving the device file name by the device name | |
219 | 221 | Returns: |
220 | - dict: [description] | |
222 | + dict: key is device name, value is file name | |
221 | 223 | """ |
222 | 224 | devices_names_and_files = {} |
223 | 225 | for device in self.obs_config["OBSERVATORY"]["DEVICES"]: |
... | ... | @@ -228,74 +230,116 @@ class ConfigPyros: |
228 | 230 | |
229 | 231 | def read_device_config_file(self,config_file_name:str,is_generic=False)->dict: |
230 | 232 | """ |
231 | - | |
233 | + Read the device config file, inherit generic device config if "generic" key is present in "DEVICE". | |
234 | + Associate capabilities of attached_devices if this device has attached_devices. | |
235 | + Inherit capabilities from generic component and overwritte attributes defined in the device config | |
232 | 236 | Args: |
233 | - config_file_name (str): [description] | |
237 | + config_file_name (str): file name to be read | |
238 | + is_generic (bool, optional): tells if we're reading a generic configuration (is_generic =True) or not (is_generic = False). Defaults to False. | |
234 | 239 | |
235 | 240 | Returns: |
236 | - dict: [description] | |
241 | + dict: formatted device configuration (attributes, capabilities...) | |
237 | 242 | """ |
238 | 243 | self.current_file = config_file_name |
239 | 244 | print(f"Reading {config_file_name}") |
240 | 245 | try: |
241 | 246 | with open(config_file_name, 'r') as stream: |
242 | 247 | config_file = yaml.safe_load(stream) |
248 | + # if we're reading a generic device configuration, the path to get the schema is different than usual | |
243 | 249 | if is_generic: |
244 | 250 | self.SCHEMA_PATH = os.path.join(os.path.abspath(os.path.dirname(config_file_name)),"../schemas/") |
245 | 251 | else: |
246 | 252 | self.SCHEMA_PATH = os.path.join(os.path.abspath(os.path.dirname(config_file_name)),"../../../config/schemas/") |
247 | 253 | |
254 | + # read and verify that the device configuration match the schema | |
248 | 255 | result = self.check_and_return_config(config_file_name,self.SCHEMA_PATH+config_file["schema"]) |
256 | + # if the configuration didn't match the schema or had an error when reading the file | |
249 | 257 | if result == None: |
250 | 258 | print("Error when reading and validating config file, please check the errors right above") |
251 | 259 | exit(-1) |
252 | 260 | else: |
261 | + # the configuration is valid | |
262 | + # storing DEVICE key in device (DEVICE can contains : attributes of device, capabilities, attached devices) | |
253 | 263 | device = result["DEVICE"] |
254 | - | |
264 | + generic_device_config = None | |
265 | + # if the device is associated to an generic configuration, we'll read that generic configuration to inherit his attributes | |
255 | 266 | if "generic" in device: |
267 | + # storing the whole current config | |
256 | 268 | current_config = result |
257 | - generic_device_config = self.read_device_config_file(self.GENERIC_DEVICES_PATH+device["generic"],True) | |
258 | - | |
269 | + # read and get the generic device config | |
270 | + generic_device_config = self.read_device_config_file(self.GENERIC_DEVICES_PATH+device["generic"],is_generic=True) | |
271 | + # merge whole device config but we need to merge capabilities differently after | |
259 | 272 | new_config = {**generic_device_config["DEVICE"],**current_config["DEVICE"]} |
260 | - | |
261 | 273 | result["DEVICE"] = new_config |
262 | 274 | |
275 | + # device has capabilities | |
263 | 276 | if "CAPABILITIES" in device: |
264 | 277 | capabilities = [] |
265 | - for capability in device["CAPABILITIES"]: | |
266 | - capability = capability["CAPABILITY"] | |
267 | - | |
268 | - capabilities.append(self.read_capability_of_device(capability)) | |
269 | - | |
270 | - | |
278 | + # if the device is associated to a generic device we need to associate his capabilities with the generic capabilities and overwrite them | |
279 | + if generic_device_config != None: | |
280 | + # we're making a copy of generic device config so we can remove items during loop | |
281 | + copy_generic_device_config = generic_device_config.copy() | |
282 | + # We have to extend capabilities of generic device configuration's capabilities | |
283 | + for capability in current_config["DEVICE"]["CAPABILITIES"]: | |
284 | + is_capability_in_generic_config = False | |
285 | + current_config_capability = capability["CAPABILITY"] | |
286 | + current_config_component = current_config_capability["component"] | |
287 | + current_config_capability = self.read_capability_of_device(current_config_capability) | |
288 | + # find if this component was defined in generic_device_config | |
289 | + for index,generic_config_capability in enumerate(generic_device_config["DEVICE"]["CAPABILITIES"]): | |
290 | + # if the current capability is the capability of the component we're looking for | |
291 | + if current_config_component == generic_config_capability["component"]: | |
292 | + is_capability_in_generic_config = True | |
293 | + # we're merging their attributes | |
294 | + merged_attributes = {**generic_config_capability["attributes"],**current_config_capability["attributes"]} | |
295 | + # removing this capability from generic device configuration | |
296 | + generic_device_config["DEVICE"]["CAPABILITIES"].pop(index) | |
297 | + capabilities.append({"component": current_config_component,"attributes":merged_attributes}) | |
298 | + break | |
299 | + if is_capability_in_generic_config == False: | |
300 | + # 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 | |
301 | + capabilities.append(current_config_capability) | |
302 | + # looping through generic device config's capabilities in order to add them to current device configuration | |
303 | + for generic_config_capability in generic_device_config["DEVICE"]["CAPABILITIES"]: | |
304 | + capabilities.append(generic_config_capability) | |
305 | + else: | |
306 | + # device not associated to a generic device configuration | |
307 | + for capability in device["CAPABILITIES"]: | |
308 | + capability = capability["CAPABILITY"] | |
309 | + capabilities.append(self.read_capability_of_device(capability)) | |
271 | 310 | |
272 | 311 | device["CAPABILITIES"] = capabilities |
312 | + # associate capabilities to final device configuration (stored in result variable) | |
313 | + result["DEVICE"]["CAPABILITIES"] = device["CAPABILITIES"] | |
273 | 314 | if "ATTACHED_DEVICES" in device.keys(): |
274 | - | |
315 | + # device has attached devices, we need to read their configuration in order to get their capabilities and add them to the current device | |
275 | 316 | devices_name_and_file = self.get_devices_names_and_file() |
276 | 317 | active_devices = self.get_active_devices() |
277 | 318 | for attached_device in device["ATTACHED_DEVICES"]: |
278 | 319 | is_attached_device_link_to_agent = False |
320 | + # 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 | |
279 | 321 | for active_device in active_devices: |
280 | 322 | if devices_name_and_file[active_device] == attached_device["file"]: |
323 | + # the attached device is an active device (so it's linked to an agent) | |
281 | 324 | is_attached_device_link_to_agent = True |
282 | 325 | break |
283 | - | |
284 | 326 | if self.CONFIG_PATH+attached_device["file"] != config_file_name and not is_attached_device_link_to_agent: |
327 | + # if the attached device isn't the device itself and not active | |
285 | 328 | |
329 | + # get configuration of attached device | |
286 | 330 | config_of_attached_device = self.read_device_config_file(self.CONFIG_PATH+attached_device["file"]) |
287 | - | |
288 | 331 | capabilities_of_attached_device = None |
289 | 332 | if "CAPABILITIES" in config_of_attached_device["DEVICE"].keys(): |
290 | 333 | capabilities_of_attached_device = config_of_attached_device["DEVICE"]["CAPABILITIES"] |
291 | 334 | if capabilities_of_attached_device != None: |
292 | 335 | # get name of device corresponding to the config file name |
293 | - 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] | |
294 | - | |
336 | + 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] | |
295 | 337 | attached_device_name = [device for device,file_name in devices_name_and_file.items() if file_name == attached_device["file"]][0] |
296 | 338 | |
339 | + # associate attached device to his 'parent' device (parent device is the currently read device) | |
297 | 340 | self.devices_links[attached_device_name] = parent_device_name |
298 | 341 | for capability in capabilities_of_attached_device: |
342 | + # add capabilities of attached device to current device | |
299 | 343 | result["DEVICE"]["CAPABILITIES"].append(capability) |
300 | 344 | return result |
301 | 345 | except yaml.YAMLError as exc: | ... | ... |
src/core/pyros_django/obsconfig/templates/obsconfig/obs_hardware_configuration.html
... | ... | @@ -21,8 +21,12 @@ |
21 | 21 | <tr> |
22 | 22 | |
23 | 23 | |
24 | - | |
25 | - <td> <a href="{% url 'device_details' device %}">{{ device }} </td> | |
24 | + | |
25 | + {% if devices_links|get_item:device in active_devices %} | |
26 | + <td> <a href="{% url 'device_details' device %}">{{ device }} (attached device)</td> | |
27 | + {% else %} | |
28 | + <td> <a href="{% url 'device_details' device %}">{{ device }} </td> | |
29 | + {% endif %} | |
26 | 30 | <td> {{ devices|get_item:device|get_item:"device_config"|get_item:"description" }} </td> |
27 | 31 | {% if device in active_devices %} |
28 | 32 | <td class="in_use"> Yes </td> | ... | ... |