Skip to content
Snippets Groups Projects
Commit ecd0e780 authored by Matthias Stefan Bodenbenner's avatar Matthias Stefan Bodenbenner
Browse files

merged master into develop

parent db812499
No related branches found
No related tags found
No related merge requests found
'''Provides the Component class being the structuring element of the UDI, i.e. the underlying SOIL Model.
Components are structural elements of a SOIL-model.
A component contains an arbitrary number of children elements such that the overall model has tree-like shape.
Children elements can be components, functions, parameters or measurements.
'''
# from __future__ import annotations # from __future__ import annotations
import json import json
import os import os
...@@ -20,7 +28,41 @@ logger = root_logger.get(__name__) ...@@ -20,7 +28,41 @@ logger = root_logger.get(__name__)
class Component(Element): class Component(Element):
def __init__(self, uuid: str, name: str, description: str, functions: List[Function], measurements: List[Measurement], def __init__(self, uuid: str, name: str, description: str, functions: List[Function], measurements: List[Measurement],
parameters: List[Parameter], components: List['Component'], implementation: Dict, ontology: str = None): parameters: List[Parameter], components: List['Component'], mapping: Dict, ontology: str = None):
"""
Args:
uuid: Locally unique identifier of the component. Must start with 'COM-'.
For the sake if simplicity, it is suggested to use the name and simply prepend 'COM-' to obtain the UUID:
name: Human readable name of the component.
description: Human readable description of the purpose of the component.
functions: List of all children functions.
measurements: List of all children measurements.
parameters: List of all children parameters.
components: List of all children components. Might contain dynamic-components.
mapping: Dictionary containing a mapping of the underlying device implementation to the HTTP-endpoints.
The mapping of a component looks as follows :
{
'getter': com_implementation.get,
'setter': com_implementation.set,
'MEA-Temperature': com_implementation.get_mea_temperature,
'PAR-State': {...},
'FUN-Reset: {...},
'COM-Part': {...},
}
If the component does not have dynamic children components, 'getter' and 'setter' are set to None.
For all children there is a key-value pair where the UUID of the child is the key and the mapping of the child is the value.
For the structure of the childrens' mappings please refer to the respective documentation.
ontology: Optional field containing the reference to a semantic definition of the components name or purpose.
Raises:
ValueError: The UUID does not start with 'COM'.
AmbiguousUUIDException: There are at least two children having the same UUID.
InvalidModelException: One of the lists containing the components' children is not a list or contains elements which are not of the correct type.
InvalidMappingException: If something is wrong with the provided mapping.
"""
Element.__init__(self, uuid, name, description, ontology) Element.__init__(self, uuid, name, description, ontology)
if uuid[:3] != 'COM': if uuid[:3] != 'COM':
raise Exception('{}: The UUID must start with COM!'.format(uuid)) raise Exception('{}: The UUID must start with COM!'.format(uuid))
...@@ -53,18 +95,35 @@ class Component(Element): ...@@ -53,18 +95,35 @@ class Component(Element):
self._implementation_remove = implementation['remove'] if 'remove' in implementation else None self._implementation_remove = implementation['remove'] if 'remove' in implementation else None
def __getitem__(self, item: Union[str, List[str]], method: int = HTTP_GET) -> Any: def __getitem__(self, item: Union[str, List[str]], method: int = HTTP_GET) -> Any:
attribute = False """Returns the value of the specified item.
if isinstance(item, str):
attribute = hasattr(self, item) Args:
if item == "functions": item: Either a string or a list of uuids. Possible values string
values are 'functions', 'measurements', 'parameters', 'components'
and 'children'. The returned value is the list of the specified
elements.
If a list of UUIDs is given, the __getitem__ method of the child of
which its UUID is equal to first UUID in the list is called with
item[1:0].
method:
Returns: Either a list of (specific) children or, if a list of UUIDs is
given, the method returns an Element.
Raises
"""
if item == 'functions':
return self._functions return self._functions
if item == "measurements": if item == 'measurements':
return self._measurements return self._measurements
if item == "parameters": if item == 'parameters':
return self._measurements return self._measurements
if item == "components": if item == 'components':
return self._components return self._components
if item == "children": if item == 'children':
ret = [] ret = []
everything = self._components + self._measurements + self._parameters + self._functions everything = self._components + self._measurements + self._parameters + self._functions
for o in everything: for o in everything:
...@@ -82,33 +141,33 @@ class Component(Element): ...@@ -82,33 +141,33 @@ class Component(Element):
if len(item) == 1: if len(item) == 1:
return o return o
else: else:
return o.__getitem__(item[1:], method) return child.__getitem__(item[1:], method)
raise Exception("{}: Given uuid {} is not the id of a child of the current component!".format(self.uuid, item)) raise ChildNotFoundException(f'{self.uuid}: Given uuid {item} is not the id of a child of the current component!')
return super().__getitem__(item, method) return super().__getitem__(item, method)
def __setitem__(self, key: str, value: Any): def __setitem__(self, key: str, value: Any):
if key == "functions": if key == 'functions':
if not isinstance(value, list): if not isinstance(value, list):
raise Exception('{}: Given functions are not a list!'.format(self.uuid)) raise Exception('{}: Given functions are not a list!'.format(self.uuid))
for f in value: for f in value:
if not isinstance(f, Function): if not isinstance(f, Function):
raise Exception('{}: Given function is not of type Function!'.format(self.uuid)) raise Exception('{}: Given function is not of type Function!'.format(self.uuid))
self._functions = value self._functions = value
elif key == "measurements": elif key == 'measurements':
if not isinstance(value, list): if not isinstance(value, list):
raise Exception('{}: Given measurements are not a list!'.format(self.uuid)) raise Exception('{}: Given measurements are not a list!'.format(self.uuid))
for v in value: for v in value:
if not isinstance(v, Measurement): if not isinstance(v, Measurement):
raise Exception('{}: Given measurement is not of type Variable!'.format(self.uuid)) raise Exception('{}: Given measurement is not of type Variable!'.format(self.uuid))
self._measurements = value self._measurements = value
elif key == "parameters": elif key == 'parameters':
if not isinstance(value, list): if not isinstance(value, list):
raise Exception('{}: Given parameters are not a list!'.format(self.uuid)) raise Exception('{}: Given parameters are not a list!'.format(self.uuid))
for v in value: for v in value:
if not isinstance(v, Parameter): if not isinstance(v, Parameter):
raise Exception('{}: Given parameter is not of type Parameter!'.format(self.uuid)) raise Exception('{}: Given parameter is not of type Parameter!'.format(self.uuid))
self._measurements = value self._measurements = value
elif key == "components": elif key == 'components':
if not isinstance(value, list): if not isinstance(value, list):
raise Exception('{}: Given components are not a list!'.format(self.uuid)) raise Exception('{}: Given components are not a list!'.format(self.uuid))
for o in value: for o in value:
...@@ -174,7 +233,7 @@ class Component(Element): ...@@ -174,7 +233,7 @@ class Component(Element):
component_list[idx]['returns'] = merge_measurements(function['returns'], component_list[idx]['returns']) component_list[idx]['returns'] = merge_measurements(function['returns'], component_list[idx]['returns'])
return component_list return component_list
# merge components, i.e. overwrite fields of "static" children dictionary with the "dynamic" fields of the parents dictionary # merge components, i.e. overwrite fields of 'static' children dictionary with the 'dynamic' fields of the parents dictionary
uuid = parent_dict['uuid'] uuid = parent_dict['uuid']
component_dict['uuid'] = uuid component_dict['uuid'] = uuid
if 'name' in parent_dict: if 'name' in parent_dict:
...@@ -228,7 +287,7 @@ class Component(Element): ...@@ -228,7 +287,7 @@ class Component(Element):
measurements = [] measurements = []
for var in dictionary['measurements']: for var in dictionary['measurements']:
if mapping is not None: if mapping is not None:
submapping = mapping[var["uuid"]] if var['uuid'] in mapping else None submapping = mapping[var['uuid']] if var['uuid'] in mapping else None
measurements += [Measurement.deserialize(var, submapping)] measurements += [Measurement.deserialize(var, submapping)]
else: else:
measurements += [Measurement.deserialize(var)] measurements += [Measurement.deserialize(var)]
...@@ -238,7 +297,7 @@ class Component(Element): ...@@ -238,7 +297,7 @@ class Component(Element):
parameters = [] parameters = []
for par in dictionary['parameters']: for par in dictionary['parameters']:
if mapping is not None: if mapping is not None:
submapping = mapping[par["uuid"]] if par['uuid'] in mapping else None submapping = mapping[par['uuid']] if par['uuid'] in mapping else None
parameters += [Parameter.deserialize(par, submapping)] parameters += [Parameter.deserialize(par, submapping)]
else: else:
parameters += [Parameter.deserialize(par)] parameters += [Parameter.deserialize(par)]
...@@ -248,7 +307,7 @@ class Component(Element): ...@@ -248,7 +307,7 @@ class Component(Element):
functions = [] functions = []
for func in dictionary['functions']: for func in dictionary['functions']:
if mapping is not None: if mapping is not None:
submapping = mapping[func["uuid"]] if func['uuid'] in mapping else None submapping = mapping[func['uuid']] if func['uuid'] in mapping else None
functions += [Function.deserialize(func, submapping)] functions += [Function.deserialize(func, submapping)]
else: else:
functions += [Function.deserialize(func)] functions += [Function.deserialize(func)]
...@@ -258,7 +317,7 @@ class Component(Element): ...@@ -258,7 +317,7 @@ class Component(Element):
components = [] components = []
for obj in dictionary['components']: for obj in dictionary['components']:
if mapping is not None: if mapping is not None:
submapping = mapping[obj["uuid"]] if obj['uuid'] in mapping else None submapping = mapping[obj['uuid']] if obj['uuid'] in mapping else None
components += [Component.deserialize(obj, submapping, filepath)] components += [Component.deserialize(obj, submapping, filepath)]
else: else:
components += [Component.deserialize(obj, filepath=filepath)] components += [Component.deserialize(obj, filepath=filepath)]
...@@ -272,12 +331,12 @@ class Component(Element): ...@@ -272,12 +331,12 @@ class Component(Element):
raise SerialisationException('{}: The component can not be deserialized. {}'.format(uuid, e)) raise SerialisationException('{}: The component can not be deserialized. {}'.format(uuid, e))
def write(self, filename: str): def write(self, filename: str):
if filename[-5:] != ".json": if filename[-5:] != '.json':
raise Exception('{} is not a json file!'.format(filename)) raise Exception('{} is not a json file!'.format(filename))
model_dict = self.serialize(['all']) model_dict = self.serialize(['all'])
f = open(filename, "w") f = open(filename, 'w')
f.write(json.dumps(model_dict)) f.write(json.dumps(model_dict))
f.close() f.close()
...@@ -289,7 +348,7 @@ class Component(Element): ...@@ -289,7 +348,7 @@ class Component(Element):
return return
# self._components.append(element) # self._components.append(element)
else: else:
raise Exception("Wrong type updating element on existing model!") raise Exception('Wrong type updating element on existing model!')
def add(self, uuid: str, class_name: str, json_file: str, *args, **kwargs): def add(self, uuid: str, class_name: str, json_file: str, *args, **kwargs):
if uuid[:3] == 'COM': if uuid[:3] == 'COM':
...@@ -325,7 +384,7 @@ class Component(Element): ...@@ -325,7 +384,7 @@ class Component(Element):
if isinstance(file, str): if isinstance(file, str):
if not os.path.isfile(file): if not os.path.isfile(file):
raise Exception('There is no file named {}!'.format(file)) raise Exception('There is no file named {}!'.format(file))
if file[-5:] != ".json": if file[-5:] != '.json':
raise Exception('{} is not a json file!'.format(file)) raise Exception('{} is not a json file!'.format(file))
with open(file, 'r') as f: with open(file, 'r') as f:
model_dict = json.load(f) model_dict = json.load(f)
......
...@@ -18,6 +18,11 @@ class DimensionException(DeviceException): ...@@ -18,6 +18,11 @@ class DimensionException(DeviceException):
def __init__(self, description): def __init__(self, description):
BasicException.__init__(self, description) BasicException.__init__(self, description)
class ChildNotFound(UserException):
def __init__(self, description):
UserException.__init__(self, description)
class ChildNotFoundException(DeviceException): class ChildNotFoundException(DeviceException):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment