diff --git a/README.md b/README.md
index 80a46bc115ef997b8b5e5353db152b2c4558a6b0..08d4f4756adca67d016c1582d91d20c2879112b2 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
# Python Unified Device Interface
-Current stable version: 5.2.6
+Current stable version: 5.2.7
+
## Installation
1. Install the *WZL-Utilities* dependency via pip
@@ -53,6 +54,9 @@ Setup your server as follows:
2. If you are not using MQTT or HTTP leave out the respective lines.
## Recent changes
+5.2.7
+- improved error output for developers
+
5.2.6
- bug fix
- fixed serialization to RFC3339 time string
@@ -171,4 +175,4 @@ Setup your server as follows:
1.4.0:
- added optional ontology field to all elements
-- bug fixes
\ No newline at end of file
+- bug fixes
diff --git a/requirements.txt b/requirements.txt
index 2d3ba7d83f2fa31bf34728818813e7f23166b3dd..7805da466d99b356bf3ce368aa9a70debc402bfd 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,16 +1,8 @@
-aiohttp @ file:///D:/bld/aiohttp_1605734736785/work
+aiohttp
async-timeout==3.0.1
-attrs @ file:///home/conda/feedstock_root/build_artifacts/attrs_1605083924122/work
-certifi==2020.11.8
-chardet @ file:///D:/bld/chardet_1602255574834/work
docstring-parser==0.7.3
-idna @ file:///home/conda/feedstock_root/build_artifacts/idna_1593328102638/work
-multidict @ file:///D:/bld/multidict_1602413330828/work
-nest-asyncio @ file:///home/conda/feedstock_root/build_artifacts/nest-asyncio_1605195931949/work
-paho-mqtt==1.5.1
+nest-asyncio
strict-rfc3339==0.7
-typing-extensions @ file:///home/conda/feedstock_root/build_artifacts/typing_extensions_1602702424206/work
-wincertstore==0.2
wzl-mqtt==1.3.1
wzl-utilities==1.2.2
-yarl @ file:///D:/bld/yarl_1605429655746/work
+
diff --git a/setup.py b/setup.py
index 4fcf85cc10d606ce8891d616376756aa63832518..09ba42cd6eb16b7834120517a4514b4c00d601fb 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,7 @@
from setuptools import setup, find_packages
setup(name='wzl-udi',
- version='5.2.6',
+ version='5.2.7',
url='',
author='Matthias Bodenbenner',
author_email='m.bodenbenner@wzl.rwth-aachen.de',
diff --git a/src/http/server.py b/src/http/server.py
index 5468b66d3acd49f274073d0a95f260d8d1cae8e2..225e6684920d4153d242359910fadff5eeca1537 100644
--- a/src/http/server.py
+++ b/src/http/server.py
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
+import traceback
+
import functools
from aiohttp import web
@@ -89,6 +91,7 @@ class HTTPServer(object):
status = 200
logger.info('Response: {}'.format(response))
except (DeviceException, ServerException, UserException) as e:
+ logger.error(traceback.format_exc())
response = {'error': str(e)}
status = 500
logger.error('Response: {}'.format(response))
@@ -108,6 +111,7 @@ class HTTPServer(object):
status = 200
logger.info('Response: {}'.format(response))
except (DeviceException, ServerException, UserException) as e:
+ logger.error(traceback.format_exc())
response = {'error': str(e)}
status = 500
logger.error('Response: {}'.format(response))
@@ -131,10 +135,12 @@ class HTTPServer(object):
status = 200
logger.info('Response: {}'.format(response))
except ChildNotFoundException as e:
+ logger.error(traceback.format_exc())
response = {'error': str(e)}
status = 404
logger.error('Response: {}'.format(response))
except (DeviceException, ServerException, UserException) as e:
+ logger.error(traceback.format_exc())
response = {'error': str(e)}
status = 500
logger.error('Response: {}'.format(response))
@@ -168,10 +174,12 @@ class HTTPServer(object):
status = 200
logger.info('Response: {}'.format(response))
except ReadOnlyException as e:
+ logger.error(traceback.format_exc())
response = {'error': str(e)}
status = 403
logger.error('Response: {}'.format(response))
except InvokationException as e:
+ logger.error(traceback.format_exc())
response = {'error': str(e)}
status = 500
logger.error('Response: {}'.format(response))
@@ -196,6 +204,7 @@ class HTTPServer(object):
status = 200
logger.info('Response: {}'.format(response))
except (DeviceException, ServerException, UserException) as e:
+ logger.error(traceback.format_exc())
response = {'error': str(e)}
status = 500
logger.error('Response: {}'.format(response))
diff --git a/src/soil/component.py b/src/soil/component.py
deleted file mode 100644
index ab830e744d290bea80c532f746f3a2f3ef66a5b3..0000000000000000000000000000000000000000
--- a/src/soil/component.py
+++ /dev/null
@@ -1,336 +0,0 @@
-# from __future__ import annotations
-import json
-import os
-import sys
-from typing import List, Any, Union, Dict
-from wzl.utilities import root_logger
-
-from . import docstring_parser
-from .element import Element
-from .error import ChildNotFoundException
-from .function import Function
-from .measurement import Measurement
-from .parameter import Parameter
-from ..utils.constants import HTTP_GET
-from ..utils.error import SerialisationException, DeviceException, UserException
-
-logger = root_logger.get(__name__)
-
-
-class Component(Element):
-
- 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):
- Element.__init__(self, uuid, name, description, ontology)
- if uuid[:3] != 'COM':
- raise Exception('{}: The UUID must start with COM!'.format(uuid))
- if not isinstance(functions, list):
- raise Exception('{}: Given functions are not a list!'.format(uuid))
- for f in functions:
- if not isinstance(f, Function):
- raise Exception('{}: Given function is not of type Function!'.format(uuid))
- if not isinstance(measurements, list):
- raise Exception('{}: Given measurements are not a list!'.format(uuid))
- for v in measurements:
- if not isinstance(v, Measurement):
- raise Exception('{}: Given measurement is not of type Variables!'.format(uuid))
- if not isinstance(parameters, list):
- raise Exception('{}: Given measurements are not a list!'.format(uuid))
- for p in parameters:
- if not isinstance(p, Parameter):
- raise Exception('{}: Given measurement is not of type Variables!'.format(uuid))
- if not isinstance(components, list):
- raise Exception('{}: Given components are not a list!'.format(uuid))
- for o in components:
- if not isinstance(o, Component):
- raise Exception('{}: Given component is not of type Components!'.format(uuid))
-
- self._functions = functions
- self._measurements = measurements
- self._components = components
- self._parameters = parameters
- self._implementation_add = implementation['add'] if 'add' 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:
- attribute = False
- if isinstance(item, str):
- attribute = hasattr(self, item)
- if item == "functions":
- return self._functions
- if item == "measurements":
- return self._measurements
- if item == "parameters":
- return self._measurements
- if item == "components":
- return self._components
- if item == "children":
- ret = []
- everything = self._components + self._measurements + self._parameters + self._functions
- for o in everything:
- ret += [o.uuid]
- return ret
- # if the item is a list, the list contains the uuid of the descendants
- if isinstance(item, list):
- if len(item) > 0 and super().__getitem__('uuid', method) == item[0]:
- item = item[1:]
- if len(item) == 0:
- return self
- everything = self._components + self._measurements + self._parameters + self._functions
- for o in everything:
- if o.uuid == item[0]:
- if len(item) == 1:
- return o
- else:
- return o.__getitem__(item[1:], method)
- raise Exception("{}: Given uuid {} is not the id of a child of the current component!".format(self.uuid, item))
- return super().__getitem__(item, method)
-
- def __setitem__(self, key: str, value: Any):
- if key == "functions":
- if not isinstance(value, list):
- raise Exception('{}: Given functions are not a list!'.format(self.uuid))
- for f in value:
- if not isinstance(f, Function):
- raise Exception('{}: Given function is not of type Function!'.format(self.uuid))
- self._functions = value
- elif key == "measurements":
- if not isinstance(value, list):
- raise Exception('{}: Given measurements are not a list!'.format(self.uuid))
- for v in value:
- if not isinstance(v, Measurement):
- raise Exception('{}: Given measurement is not of type Variable!'.format(self.uuid))
- self._measurements = value
- elif key == "parameters":
- if not isinstance(value, list):
- raise Exception('{}: Given parameters are not a list!'.format(self.uuid))
- for v in value:
- if not isinstance(v, Parameter):
- raise Exception('{}: Given parameter is not of type Parameter!'.format(self.uuid))
- self._measurements = value
- elif key == "components":
- if not isinstance(value, list):
- raise Exception('{}: Given components are not a list!'.format(self.uuid))
- for o in value:
- if not isinstance(o, Component):
- raise Exception('{}: Given component is not of type Component!'.format(self.uuid))
- self._components = value
- else:
- super().__setitem__(key, value)
-
- def serialize(self, keys: List[Any], method: int = HTTP_GET) -> Dict[str, Any]:
- if not keys: # list is empty
- keys = ['uuid', 'name', 'description', 'children', 'ontology']
-
- if 'all' in keys: # serialize complete tree recursively (overrides all other keys)
- dictionary = super().serialize([])
- dictionary['measurements'] = list(map(lambda x: x.serialize([]), self._measurements))
- dictionary['functions'] = list(map(lambda x: x.serialize(['all']), self._functions))
- dictionary['components'] = list(map(lambda x: x.serialize(['all']), self._components))
- dictionary['parameters'] = list(map(lambda x: x.serialize(['all']), self._parameters))
- return dictionary
-
- dictionary = super().serialize(keys, method)
- if 'children' in keys:
- everything = self._components + self._measurements + self._parameters + self._functions
- dictionary['children'] = list(map(lambda x: x.serialize(['name', 'uuid']), everything))
- # if 'update' in keys:
- # # TODO implement (update all children)
- # pass
- # # if self._implementation is not None:
- # # dict['update'] = self._implementation()
- # else:
- # dict['children'] = list(map(lambda x: x.serialize(['value', 'uuid']), self._measurements))
- return dictionary
-
- @staticmethod
- def merge_dictionaries(parent_dict, component_dict):
- def merge_measurements(parent_list, component_list):
- for measurement in parent_list:
- if 'uuid' not in measurement:
- raise Exception('UUID {} not given for measurement to be overwritten.'.format(measurement['uuid']))
- idx = [i for i, v in enumerate(component_list) if v['uuid'] == measurement['uuid']]
- if len(idx) != 1:
- raise Exception('Mismatching UUID: {}.'.format(measurement['uuid']))
- idx = idx[0]
- component_list[idx].update(measurement)
- return component_list
-
- def merge_functions(parent_list, component_list):
- for function in parent_list:
- if 'uuid' not in function:
- raise Exception('UUID {} not given for function to be overwritten.'.format(function['uuid']))
- idx = [i for i, v in enumerate(component_list) if v['uuid'] == function['uuid']]
- if len(idx) != 1:
- raise Exception('Mismatching UUID: {}.'.format(function['uuid']))
- idx = idx[0]
- if 'name' in function:
- component_list[idx]['name'] = function['name']
- if 'description' in function:
- component_list[idx]['description'] = function['description']
- if 'arguments' in function:
- component_list[idx]['arguments'] = merge_measurements(function['arguments'], component_list[idx]['arguments'])
- if 'returns' in function:
- component_list[idx]['returns'] = merge_measurements(function['returns'], component_list[idx]['returns'])
- return component_list
-
- # merge components, i.e. overwrite fields of "static" children dictionary with the "dynamic" fields of the parents dictionary
- uuid = parent_dict['uuid']
- component_dict['uuid'] = uuid
- if 'name' in parent_dict:
- component_dict['name'] = parent_dict['name']
- if 'description' in parent_dict:
- component_dict['description'] = parent_dict['description']
- if 'measurements' in parent_dict:
- component_dict['measurements'] = merge_measurements(parent_dict['measurements'], component_dict['measurements'])
- if 'parameters' in parent_dict:
- component_dict['paramters'] = merge_measurements(parent_dict['parameters'], component_dict['parameters'])
- if 'functions' in parent_dict:
- component_dict['functions'] = merge_functions(parent_dict['functions'], component_dict['functions'])
- if 'components' in parent_dict:
- for obj in parent_dict['components']:
- index = [i for i, o in enumerate(component_dict['components']) if o['uuid'] == obj['uuid']]
- if len(index) != 1:
- raise Exception('Mismatching UUID: {}.'.format(obj['uuid']))
- index = index[0]
- component_dict['components'][index] = Component.merge_dictionaries(obj, component_dict['components'][index])
- return component_dict
-
- @staticmethod
- def deserialize(dictionary, mapping=None, filepath=''):
- if 'uuid' not in dictionary:
- raise SerialisationException('The component can not be deserialized. UUID is missing!')
- uuid = dictionary['uuid']
- if uuid[:3] != 'COM':
- raise SerialisationException(
- 'The component can not be deserialized. The UUID must start with COM, but actually starts with {}!'.format(uuid[:3]))
- if 'file' in dictionary:
- try:
- with open(os.path.normpath(os.path.join(filepath, dictionary['file']))) as file:
- component_dict = json.load(file)
- dictionary = Component.merge_dictionaries(dictionary, component_dict)
- except Exception as e:
- raise SerialisationException('{}: The component can not be deserialized. Provided JSON-file can not be parsed! {}'.format(uuid, e))
- if 'name' not in dictionary:
- raise SerialisationException('{}: The component can not be deserialized. Name is missing!'.format(uuid))
- if 'description' not in dictionary:
- raise SerialisationException('{}: The component can not be deserialized. Description is missing!'.format(uuid))
- if 'measurements' not in dictionary:
- raise SerialisationException('{}: The component can not be deserialized. List of measurements is missing!'.format(uuid))
- if 'parameters' not in dictionary:
- raise SerialisationException('{}: The component can not be deserialized. List of parameters is missing!'.format(uuid))
- if 'functions' not in dictionary:
- raise SerialisationException('{}: The component can not be deserialized. List of functions is missing!'.format(uuid))
- if 'components' not in dictionary:
- raise SerialisationException('{}: The component can not be deserialized. List of components is missing!'.format(uuid))
-
- try:
- measurements = []
- for var in dictionary['measurements']:
- if mapping is not None:
- submapping = mapping[var["uuid"]] if var['uuid'] in mapping else None
- measurements += [Measurement.deserialize(var, submapping)]
- else:
- measurements += [Measurement.deserialize(var)]
- except Exception as e:
- raise SerialisationException('{}: A measurement of the component can not be deserialized. {}'.format(uuid, e))
- try:
- parameters = []
- for par in dictionary['parameters']:
- if mapping is not None:
- submapping = mapping[par["uuid"]] if par['uuid'] in mapping else None
- parameters += [Parameter.deserialize(par, submapping)]
- else:
- parameters += [Parameter.deserialize(par)]
- except Exception as e:
- raise SerialisationException('{}: A parameter of the component can not be deserialized. {}'.format(uuid, e))
- try:
- functions = []
- for func in dictionary['functions']:
- if mapping is not None:
- submapping = mapping[func["uuid"]] if func['uuid'] in mapping else None
- functions += [Function.deserialize(func, submapping)]
- else:
- functions += [Function.deserialize(func)]
- except Exception as e:
- raise SerialisationException('{}: A function of the component can not be deserialized. {}'.format(uuid, e))
- try:
- components = []
- for obj in dictionary['components']:
- if mapping is not None:
- submapping = mapping[obj["uuid"]] if obj['uuid'] in mapping else None
- components += [Component.deserialize(obj, submapping, filepath)]
- else:
- components += [Component.deserialize(obj, filepath=filepath)]
- except Exception as e:
- raise SerialisationException('{}: An component of the component can not be deserialized. {}'.format(uuid, e))
- try:
- ontology = dictionary['ontology'] if 'ontology' in dictionary else None
- return Component(dictionary['uuid'], dictionary['name'], dictionary['description'], functions, measurements, parameters, components, mapping,
- ontology)
- except Exception as e:
- raise SerialisationException('{}: The component can not be deserialized. {}'.format(uuid, e))
-
- def write(self, filename: str):
- if filename[-5:] != ".json":
- raise Exception('{} is not a json file!'.format(filename))
-
- model_dict = self.serialize(['all'])
-
- f = open(filename, "w")
- f.write(json.dumps(model_dict))
- f.close()
-
- def update(self, element: Union['Component', Function, Measurement, Parameter]):
- if isinstance(element, Component):
- for i, o in enumerate(self._components):
- if o.uuid == element.uuid:
- self._components[i] = element
- return
- # self._components.append(element)
- else:
- raise Exception("Wrong type updating element on existing model!")
-
- def add(self, uuid: str, class_name: str, json_file: str, *args, **kwargs):
- if uuid[:3] == 'COM':
- if uuid not in [o.uuid for o in self._components]:
- try:
- __import__(class_name)
- implementation = getattr(sys.modules[class_name], class_name)(*args, **kwargs)
- mapping = docstring_parser.parse_docstrings_for_soil(implementation)
- self._components += [Component.load(json_file, mapping)]
- if self._implementation_add is not None:
- self._implementation_add(implementation)
- except Exception as e:
- raise DeviceException('Can not add component with UUID {}. {}'.format(uuid, e), predecessor=e)
- else:
- raise UserException('Component has already a child with UUID {}.'.format(uuid))
- else:
- raise UserException('UUID {} is not of the UUID of an component.'.format(uuid))
-
- def remove(self, uuid: str, *args, **kwargs):
- for o in self._components:
- if o.uuid == uuid:
- if self._implementation_remove is not None:
- try:
- self._implementation_remove(*args, **kwargs)
- except Exception as e:
- raise DeviceException(str(e), predecessor=e)
- self._components.remove(o)
- return
- raise ChildNotFoundException('{}: Child {} not found!'.format(self.uuid, uuid))
-
- @staticmethod
- def load(file: Union[str, dict], implementation: Any) -> 'Component':
- if isinstance(file, str):
- if not os.path.isfile(file):
- raise Exception('There is no file named {}!'.format(file))
- if file[-5:] != ".json":
- raise Exception('{} is not a json file!'.format(file))
- with open(file, 'r') as f:
- model_dict = json.load(f)
- return Component.deserialize(model_dict, implementation, os.path.dirname(file))
- elif isinstance(file, dict):
- return Component.deserialize(file, implementation)
- else:
- raise Exception('Given file is not a name of a json-file nor a json-like dictionary.')
diff --git a/src/soil/docstring_parser.py b/src/soil/docstring_parser.py
index b776036427483789379559e2a5c3880e357145d3..39c48aa6a5a13ddc33f0db02adf8b51ccffc613d 100644
--- a/src/soil/docstring_parser.py
+++ b/src/soil/docstring_parser.py
@@ -60,7 +60,6 @@ def parse_docstrings_for_mqtt(implementation, parent_url="", *args, **kwargs):
child_schedule = parse_children(parse_docstrings_for_mqtt, parent_url, implementation)
for key in child_schedule:
schedule += [*child_schedule[key]]
- print(schedule)
# parse functions and setters/getters of variables and parameters
object_dict = implementation.__class__.__dict__
diff --git a/src/soil/error.py b/src/soil/error.py
index 26f9f30ff5dc2901336110f9d0a5c7386f9da13c..df67dad4921f8a40a05ea92148c9ce7ebb1d8735 100644
--- a/src/soil/error.py
+++ b/src/soil/error.py
@@ -4,25 +4,43 @@ from ..utils.error import BasicException, DeviceException, UserException
class TypeException(DeviceException):
def __init__(self, description):
- BasicException.__init__(self, description)
+ DeviceException.__init__(self, description)
class RangeException(DeviceException):
def __init__(self, description):
- BasicException.__init__(self, description)
+ DeviceException.__init__(self, description)
class DimensionException(DeviceException):
def __init__(self, description):
- BasicException.__init__(self, description)
+ DeviceException.__init__(self, description)
+
+
+class InvalidModelException(UserException):
+
+ def __init__(self, description):
+ UserException.__init__(self, description)
+
+
+class InvalidMappingException(UserException):
+
+ def __init__(self, description):
+ UserException.__init__(self, description)
class ChildNotFoundException(DeviceException):
def __init__(self, description):
- BasicException.__init__(self, description)
+ DeviceException.__init__(self, description)
+
+
+class AmbiguousUUIDException(UserException):
+
+ def __init__(self, description):
+ UserException.__init__(self, description)
class ReadOnlyException(UserException):
@@ -30,18 +48,18 @@ class ReadOnlyException(UserException):
def __init__(self, uuid, name, description=None):
if description is None:
description = 'The parameter "{}" with UUID "{}" is a read-only parameter.'.format(name, uuid)
- BasicException.__init__(self, description)
+ UserException.__init__(self, description)
class NotImplementedException(UserException):
def __init__(self, uuid, name, description=None):
if description is None:
description = 'The function "{}" with UUID "{}" is not implemented.'.format(name, uuid)
- BasicException.__init__(self, description)
+ UserException.__init__(self, description)
class InvokationException(DeviceException):
def __init__(self, uuid, name, description=None):
if description is None:
description = 'Error when invoking "{}" with UUID "{}".'.format(name, uuid)
- BasicException.__init__(self, description)
\ No newline at end of file
+ DeviceException.__init__(self, description)
diff --git a/src/soil/measurement.py b/src/soil/measurement.py
deleted file mode 100644
index c38f79210e81eced6e27f6b63d673011e04af087..0000000000000000000000000000000000000000
--- a/src/soil/measurement.py
+++ /dev/null
@@ -1,141 +0,0 @@
-from deprecated import deprecated
-from typing import Dict
-from wzl.utilities import root_logger
-
-from ..utils.constants import HTTP_GET
-from ..utils.error import SerialisationException
-from .figure import Figure
-
-logger = root_logger.get(__name__)
-
-
-class Measurement(Figure):
-
- def __init__(self, uuid, name, description, datatype, dimension, range, getter, unit, nonce=None, ontology: str = None):
- Figure.__init__(self, uuid, name, description, datatype, dimension, range, None, getter, ontology)
- if uuid[:3] != 'MEA':
- raise Exception('{}: The UUID must start with MEA!'.format(uuid))
- self._unit = unit
- self._covariance = None # TODO init
- self._uncertainty = None # TODO init
- self._timestamp = None
- self._nonce = nonce
-
- @property
- def unit(self):
- return self._unit
-
- @property
- def covariance(self):
- return self._covariance
-
- @property
- def uncertainty(self):
- return self._uncertainty
-
- @property
- def timestamp(self):
- return self._timestamp
-
- @property
- def nonce(self):
- return self._nonce
-
- def __getitem__(self, item: str, method=HTTP_GET):
- """
- Getter-Method.
- According to the given key the method returns the value of the corresponding attribute.
- :param item: name of the attribute. Provided as string without leading underscores.
- :param method: ???
- :return: the value of the attribute indicated by 'item'.
- """
- if item == "unit":
- return self._unit
- if item == 'nonce':
- return self._nonce
- if item == 'covariance':
- return self._covariance
- if item == 'uncertainty':
- return self._uncertainty
- if item == 'timestamp':
- return self._timestamp
- if item == []:
- return self
- return super().__getitem__(item, method)
-
- def __setitem__(self, key: str, value):
- """
- Setter - Method
- If key is "value" datatype, dimension and range is checked for correctness.
- :param key: sets the value of the attribute with name 'item' to the provided value.
- :param value: value to be set
- """
- if key in ['value', 'timestamp', 'covariance', 'uncertainty']:
- raise KeyError('The {} attribute of a measurement can not be set manually!'.format(key))
- elif key == "nonce":
- self._nonce = self._nonce
- elif key == 'unit':
- self._unit = self._unit
- else:
- super().__setitem__(key, value)
-
- def serialize(self, keys: [str], method=HTTP_GET):
- """
- Serializes an object of type Measurement into a JSON-like dictionary.
- :param keys: All attributes given in the "keys" array are serialized.
- :param method: ???
- :return: a dictionary having all "keys" as keys and the values of the corresponding attributes as value.
- """
- # list is empty provide all attributes of the default-serialization
- if not keys:
- keys = ['uuid', 'name', 'description', 'datatype', 'value', 'dimension', 'range', 'timestamp', 'nonce', 'covariance', 'uncertainty',
- 'unit', 'ontology']
- # TODO temporary workaround, forces the server to always return the timestamp if the value is queried
- if 'value' in keys and 'timestamp' not in keys:
- keys += ['timestamp']
- dictionary = {}
- # get all attribute values
- for key in keys:
- value = self.__getitem__(key, method)
- # in case of timestamp convert into RFC3339 string
- if key == 'timestamp' or (key == 'value' and self._datatype == 'time'):
- value = value.isoformat() + 'Z' if value is not None else ""
- dictionary[key] = value
- return dictionary
-
- @staticmethod
- def deserialize(dictionary: Dict, implementation=None):
- """
- Takes a JSON-like dictionary, parses it, performs a complete correctness check and returns an object of type Figure with the
- values provided in the dictionary, if dictionary is a valid serialization of a Figure.
- :param dictionary: serialized measurement
- :param implementation: implementation wrapper object,
- :return: an object of type Figure
- """
- # check if all required attributes are present
- if 'uuid' not in dictionary:
- raise SerialisationException('The measurement can not be deserialized. UUID is missing!')
- uuid = dictionary['uuid']
- if uuid[:3] != 'MEA':
- raise SerialisationException(
- 'The Measurement can not be deserialized. The UUID must start with MEA, but actually starts with {}!'.format(uuid[:3]))
- if 'name' not in dictionary:
- raise SerialisationException('{}: The measurement can not be deserialized. Name is missing!'.format(uuid))
- if 'description' not in dictionary:
- raise SerialisationException('{}: The measurement can not be deserialized. Description is missing!'.format(uuid))
- if 'datatype' not in dictionary:
- raise SerialisationException('{}: The measurement can not be deserialized. Datatype is missing!'.format(uuid))
- if 'dimension' not in dictionary:
- raise SerialisationException('{}: The measurement can not be deserialized. Dimension is missing!'.format(uuid))
- if 'value' not in dictionary:
- raise SerialisationException('{}: The measurement can not be deserialized. Value is missing!'.format(uuid))
- if 'range' not in dictionary:
- raise SerialisationException('{}: The measurement can not be deserialized. Range is missing!'.format(uuid))
- if 'unit' not in dictionary:
- raise SerialisationException('{}: The measurement can not be deserialized. Unit is missing!'.format(uuid))
- try:
- ontology = dictionary['ontology'] if 'ontology' in dictionary else None
- return Measurement(dictionary['uuid'], dictionary['name'], dictionary['description'], dictionary['datatype'], dictionary['dimension'],
- dictionary['range'], implementation, dictionary['unit'], ontology)
- except Exception as e:
- raise SerialisationException('{}: The measurement can not be deserialized. {}'.format(uuid, e))