diff --git a/src/http/server.py b/src/http/server.py
index d0046f92a08629054d499dca7d4403c5ba1104b3..06ae4098e9c100241f9e8d4b4a0339ad2627c49a 100644
--- a/src/http/server.py
+++ b/src/http/server.py
@@ -130,7 +130,7 @@ class HTTPServer(object):
semantic = False
if query is not None and 'format' in query and query['format'] in ['json', 'xml', 'turtle']:
dataformat = query['format']
- if query is not None and 'semantic' in query and query['semantic'] in ['data', 'shape']:
+ if query is not None and 'semantic' in query and query['semantic'] in ['data', 'metadata', 'profile']:
semantic = True
if dataformat == 'json':
@@ -175,7 +175,7 @@ class HTTPServer(object):
# determine whether a semantic answer is expected
semantic = None
- if request.query is not None and 'semantic' in request.query and request.query['semantic'] in ['data', 'shape']:
+ if request.query is not None and 'semantic' in request.query and request.query['semantic'] in ['data', 'metadata', 'profile']:
semantic = request.query['semantic']
# retrieving the element
diff --git a/src/soil/component.py b/src/soil/component.py
index 4bd9e499c2222f07543248b1c960f1fd2d4db714..77fdcdf08ad3e22d772870b6ce2dcd1253557318 100644
--- a/src/soil/component.py
+++ b/src/soil/component.py
@@ -9,7 +9,6 @@ Children elements can be components, functions, parameters or measurements.
# from __future__ import annotations
import json
import os
-import pprint
import sys
from typing import List, Any, Union, Dict
@@ -32,7 +31,7 @@ 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: Any, ontology: str = None,
- shape: str = None):
+ profile: str = None):
"""
Args:
@@ -46,7 +45,7 @@ class Component(Element):
components: List of all children components. Might contain dynamic-components.
implementation: The class of the sensor layer implementing this component.
ontology: Optional field containing the reference to a semantic definition of the components name or purpose.
- shape: Optional field containing the name of the shape defining the restrictions of this component using semantic web technologies.
+ profile: Optional field containing the name of the shape defining the restrictions of this component using semantic web technologies.
Raises:
ValueError: The UUID does not start with 'COM'.
@@ -54,7 +53,7 @@ class Component(Element):
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, shape)
+ Element.__init__(self, uuid, name, description, ontology, profile)
if uuid[:3] != 'COM':
raise Exception('{}: The UUID must start with COM!'.format(uuid))
if not isinstance(functions, list):
@@ -189,7 +188,7 @@ class Component(Element):
keys = [] if keys is None else keys
if not keys: # list is empty
- keys = ['uuid', 'name', 'description', 'children', 'ontology', 'shape']
+ keys = ['uuid', 'name', 'description', 'children', 'ontology', 'profile']
if 'all' in keys: # serialize complete tree recursively (overrides all other keys)
dictionary = super().serialize([], legacy_mode)
@@ -287,10 +286,9 @@ class Component(Element):
'{}: An component of the component can not be deserialized. {}'.format(uuid, e))
try:
ontology = dictionary['ontology'] if 'ontology' in dictionary else None
- shape = dictionary['shape'] if 'shape' in dictionary else None
+ profile = dictionary['profile'] if 'profile' in dictionary else None
return Component(dictionary['uuid'], dictionary['name'], dictionary['description'], functions, measurements,
- parameters, components, implementation,
- ontology, shape)
+ parameters, components, implementation, ontology, profile)
except Exception as e:
raise SerialisationException('{}: The component can not be deserialized. {}'.format(uuid, e))
@@ -378,17 +376,20 @@ class Component(Element):
else:
raise Exception('Given file is not a name of a json-file nor a json-like dictionary.')
- def load_semantics(self, folderpath: str) -> None:
- super().load_semantics(folderpath)
+ def load_semantics(self, profiles_path: str, metadata_path: str, parent_name: str) -> None:
+ super().load_semantics(profiles_path, metadata_path, parent_name)
for child in self.children:
- child.load_semantics(folderpath)
+ child.load_semantics(profiles_path, metadata_path, f"{parent_name}{self.uuid[4:].capitalize()}")
def serialize_semantics(self, kind: str) -> rdflib.Graph:
- if kind == 'shape':
- result = self._semantic_definition
+ if self._metadata_profile is None or self._metadata is None:
+ raise SerialisationException('No semantic information have been provided during initialization.')
+
+ if kind == 'profile':
+ result = self._metadata_profile
+ elif kind == 'metadata':
+ result = self._metadata
else:
- assert kind == 'data'
- # TODO implement
- result = None
+ raise DeviceException('The provided kind of semantic information cannot be returned.')
return result
diff --git a/src/soil/element.py b/src/soil/element.py
index b7772729283bf3ff51e6a0f501da68bb6ee70d4d..9d2d646e0166f8c3ed5986d19764d37451f9abb3 100644
--- a/src/soil/element.py
+++ b/src/soil/element.py
@@ -17,24 +17,26 @@ class Element(ABC):
"""
UUID_PATTERN = re.compile(BASE_UUID_PATTERN)
- def __init__(self, uuid: str, name: str, description: str, ontology: str = None, shape: str = None):
+ def __init__(self, uuid: str, name: str, description: str, ontology: str = None, profile: str = None):
if not isinstance(name, str) or name == '':
raise Exception('{}: Name is no string or the empty string!'.format(uuid))
if not isinstance(description, str) or description == '':
raise Exception('{}: Description is no string or the empty string!'.format(uuid))
if ontology is not None and not isinstance(ontology, str):
raise Exception('{}: Onthology is no string!'.format(uuid))
- if shape is not None and not isinstance(shape, str):
+ if profile is not None and not isinstance(profile, str):
raise Exception('{}: Shape is no string!'.format(uuid))
if not isinstance(uuid, str) or not Element.UUID_PATTERN.match(uuid):
raise Exception('Cannot use uuid {}. Wrong format!'.format(uuid))
else:
- self._uuid = uuid
- self._name = name
- self._description = description
- self._ontology = ontology
- self._shape = shape
- self._semantic_definition = None
+ self._uuid: str = uuid
+ self._name: str = name
+ self._description: str = description
+ self._ontology: str = ontology
+ self._profilename: str = profile
+ self._metadata_profile: rdflib.Graph = None
+ self._metadata: rdflib.Graph = None
+ self._semantic_name: str = None
@property
def uuid(self):
@@ -49,8 +51,8 @@ class Element(ABC):
return self._description
if item == "ontology":
return self._ontology
- if item == "shape":
- return self._shape
+ if item == "profile":
+ return self._profilename
raise KeyError("{}: Key error. No attribute is named '{}'".format(self.uuid, item))
def __setitem__(self, key: str, value: Any):
@@ -66,10 +68,10 @@ class Element(ABC):
if value is not None and not isinstance(value, str):
raise Exception('{}: Ontology is no string!'.format(self.uuid))
self._ontology = value
- elif key == "shape":
+ elif key == "profile":
if value is not None and not isinstance(value, str):
- raise Exception('{}: Shape is no string!'.format(self.uuid))
- self._shape = value
+ raise Exception('{}: Profile is no string!'.format(self.uuid))
+ self._profilename = value
else:
raise KeyError(
"{}: Key error. No attribute is named '{}' or it should not be changed".format(self.uuid, key))
@@ -82,7 +84,7 @@ class Element(ABC):
res['name'] = self._name
res['description'] = self._description
res['ontology'] = self._ontology
- res['shape'] = self._shape
+ res['profile'] = self._profilename
return res
@staticmethod
@@ -90,14 +92,20 @@ class Element(ABC):
def deserialize(dictionary: Dict):
...
- def load_semantics(self, folderpath: str) -> None:
- if self._shape is None:
+ def load_semantics(self, profiles_path: str, metadata_path: str, parent_name: str) -> None:
+ if self._profilename is None:
raise SerialisationException("Can not load semantic definition, shape attribute is not defined!")
- shape_file = os.path.join(folderpath, f"{self._shape}.shacl.ttl")
+ # load shapes
+ shape_filename = os.path.join(profiles_path, f"{self._profilename}.shacl.ttl")
+ self._metadata_profile = rdflib.Graph()
+ self._metadata_profile.parse(shape_filename)
- self._semantic_definition = rdflib.Graph()
- self._semantic_definition.parse(shape_file)
+ # load metadata
+ self._semantic_name = f'{parent_name}{self.uuid[4:].capitalize()}'
+ metadata_filename = os.path.join(metadata_path, f"{self._semantic_name}.ttl")
+ self._metadata = rdflib.Graph()
+ self._metadata.parse(metadata_filename)
@abstractmethod
def serialize_semantics(self, kind: str) -> rdflib.Graph:
diff --git a/src/soil/figure.py b/src/soil/figure.py
index 4fcdda44ca53d0b24ada4fd0614c4584351b983a..d94c2b3c89ebc718df5625a5f05c27af2a4b94d8 100644
--- a/src/soil/figure.py
+++ b/src/soil/figure.py
@@ -1,4 +1,5 @@
import asyncio
+import copy
import datetime
import inspect
import time
@@ -6,9 +7,11 @@ from abc import ABC
from typing import Any, List, Callable, Union
import nest_asyncio
+import rdflib
import strict_rfc3339 as rfc3339
from .datatype import Datatype
+from .semantics import Namespaces
nest_asyncio.apply()
@@ -39,8 +42,8 @@ def serialize_time(time):
class Figure(Element, ABC):
def __init__(self, uuid: str, name: str, description: str, datatype: Datatype, dimension: List[int], range: List,
- value: Any, getter: Callable, ontology: str = None, shape: str = None):
- Element.__init__(self, uuid, name, description, ontology, shape)
+ value: Any, getter: Callable, ontology: str = None, profile: str = None):
+ Element.__init__(self, uuid, name, description, ontology, profile)
# if type(datatype) is not str:
# raise Exception('{}: Datatype must be passed as string.'.format(uuid))
Figure.check_all(datatype, dimension, range, value)
@@ -285,6 +288,21 @@ class Figure(Element, ABC):
except RangeException as e:
raise e
+ @staticmethod
+ def serialize_value(data_graph: rdflib.Graph, value: Any) -> rdflib.term.Identifier:
+ if isinstance(value, list):
+ blank_node = rdflib.BNode()
+ data_graph.add((blank_node, Namespaces.rdf.rest, Namespaces.rdf.nil))
+ data_graph.add((blank_node, Namespaces.rdf.first, Figure.serialize_value(data_graph, value[len(value) - 1])))
+ for entry in reversed(value[:-1]):
+ new_blank_node = rdflib.BNode()
+ data_graph.add((new_blank_node, Namespaces.rdf.rest, blank_node))
+ data_graph.add((new_blank_node, Namespaces.rdf.first, Figure.serialize_value(data_graph, entry)))
+ blank_node = new_blank_node
+ return blank_node
+ else:
+ return rdflib.Literal(value)
+
@staticmethod
def is_scalar(value):
return not isinstance(value, list)
diff --git a/src/soil/function.py b/src/soil/function.py
index 59c60bf93f8c69511b11a8382ef571bfc9c94543..04131ff79633093e1d05d7f1e177e0f1b610e581 100644
--- a/src/soil/function.py
+++ b/src/soil/function.py
@@ -18,8 +18,8 @@ logger = root_logger.get(__name__)
class Function(Element):
def __init__(self, uuid: str, name: str, description: str, arguments: List[Figure], returns: List[Figure],
- implementation: Callable, ontology: str = None, shape: str = None):
- Element.__init__(self, uuid, name, description, ontology, shape)
+ implementation: Callable, ontology: str = None, profile: str = None):
+ Element.__init__(self, uuid, name, description, ontology, profile)
if uuid[:3] != 'FUN':
raise Exception('{}: The UUID must start with FUN!'.format(uuid))
if not isinstance(arguments, list):
@@ -173,12 +173,12 @@ class Function(Element):
raise SerialisationException('{}: A return of the function can not be deserialized. {}'.format(uuid, e))
try:
ontology = dictionary['ontology'] if 'ontology' in dictionary else None
- shape = dictionary['shape'] if 'shape' in dictionary else None
- return Function(dictionary['uuid'], dictionary['name'], dictionary['description'], arguments, returns, implementation, ontology, shape)
+ profile = dictionary['profile'] if 'profile' in dictionary else None
+ return Function(dictionary['uuid'], dictionary['name'], dictionary['description'], arguments, returns, implementation, ontology, profile)
except Exception as e:
raise SerialisationException('{}: The function can not be deserialized. {}'.format(uuid, e))
- def load_semantics(self, folderpath: str) -> None:
+ def load_semantics(self, profiles_path: str, metadata_path: str, parent_name: str) -> None:
# This method does nothing intentionally, as we do not have any semantic definition for function
pass
diff --git a/src/soil/measurement.py b/src/soil/measurement.py
index c096c51f0a51c6e17cd59c95a6d0ec0043c44d12..3dee1c151c1e805d002eb57366e8dd96037e1372 100644
--- a/src/soil/measurement.py
+++ b/src/soil/measurement.py
@@ -1,23 +1,26 @@
+import datetime
import warnings
-from typing import Dict, Callable, List
+from typing import Dict, Callable, List, Any
import rdflib
from deprecated import deprecated
+from rdflib import BNode
from .datatype import Datatype
from .figure import Figure
from ..utils import root_logger
from ..utils.constants import HTTP_GET
-from ..utils.error import SerialisationException
+from ..utils.error import SerialisationException, DeviceException
+from .semantics import Semantics, Namespaces
logger = root_logger.get(__name__)
class Measurement(Figure):
- def __init__(self, uuid: str, name: str, description: str, datatype: Datatype, dimension: List[int], range: List,
- getter: Callable, unit: str, label=None, ontology: str = None, shape: str = None):
- Figure.__init__(self, uuid, name, description, datatype, dimension, range, None, getter, ontology, shape)
+ def __init__(self, uuid: str, name: str, description: str, datatype: Datatype, dimension: List[int], range: List,
+ getter: Callable, unit: str, label=None, ontology: str = None, profile: str = None):
+ Figure.__init__(self, uuid, name, description, datatype, dimension, range, None, getter, ontology, profile)
if uuid[:3] != 'MEA':
raise Exception('{}: The UUID must start with MEA!'.format(uuid))
self._unit = unit
@@ -110,7 +113,7 @@ class Measurement(Figure):
# list is empty provide all attributes of the default-serialization
if not keys:
keys = ['uuid', 'name', 'description', 'datatype', 'value', 'dimension', 'range', 'timestamp', 'label',
- 'covariance', 'unit'] #, 'ontology']
+ 'covariance', 'unit'] # , 'ontology']
if 'value' in keys and 'timestamp' not in keys:
keys += ['timestamp']
dictionary = {}
@@ -163,12 +166,55 @@ class Measurement(Figure):
raise SerialisationException('{}: The measurement can not be deserialized. Unit is missing!'.format(uuid))
try:
ontology = dictionary['ontology'] if 'ontology' in dictionary else None
- shape = dictionary['shape'] if 'shape' in dictionary else None
+ profile = dictionary['profile'] if 'profile' in dictionary else None
return Measurement(dictionary['uuid'], dictionary['name'], dictionary['description'],
Datatype.from_string(dictionary['datatype']), dictionary['dimension'],
- dictionary['range'], implementation, dictionary['unit'], None, ontology, shape)
+ dictionary['range'], implementation, dictionary['unit'], None, ontology, profile)
except Exception as e:
raise SerialisationException('{}: The measurement can not be deserialized. {}'.format(uuid, e))
def serialize_semantics(self, kind: str) -> rdflib.Graph:
- return self._semantic_definition
\ No newline at end of file
+ if kind == 'profile':
+ result = self._metadata_profile
+ elif kind == 'metadata':
+ result = self._metadata
+ elif kind == 'data':
+ data_graph = rdflib.Graph()
+ data_graph.bind('sosa', Namespaces.sosa)
+ data_graph.bind(Semantics.prefix, Semantics.namespace)
+ data_graph.bind('soil', Namespaces.soil)
+ data_graph.bind('qudt', Namespaces.qudt)
+ data_graph.bind('unit', Namespaces.unit)
+ observation_subject = Semantics.namespace[f'{self._semantic_name}Observation']
+
+ sensor_triples = list(self._metadata.triples((None, Namespaces.sosa.isObservedBy, None)))
+ assert len(sensor_triples) == 1
+
+ # create observation node
+ data_graph.add((observation_subject, Namespaces.rdf.type, rdflib.URIRef(Namespaces.sosa.Observation)))
+ data_graph.add((observation_subject, Namespaces.schema.name, rdflib.Literal(f'{self._name} Observation')))
+ data_graph.add((observation_subject, Namespaces.sosa.observedProperty, Semantics.namespace[self._semantic_name]))
+ data_graph.add((observation_subject, Namespaces.sosa.hasResult, Semantics.namespace[f'{self._semantic_name}Measurement']))
+ data_graph.add((observation_subject, Namespaces.sosa.madeBySensor, sensor_triples[0][2]))
+
+ # create result node
+ unit_triples = list(self._metadata.triples((None, Namespaces.qudt.applicableUnit, None)))
+ assert len(unit_triples) == 1
+
+ measurement_subject = Semantics.namespace[f'{self._semantic_name}Measurement']
+ data_graph.add((measurement_subject, Namespaces.rdf.type, rdflib.URIRef(Namespaces.sosa.Result)))
+ data_graph.add((measurement_subject, Namespaces.rdf.type, rdflib.URIRef(Namespaces.soil.Measurement)))
+ data_graph.add((measurement_subject, Namespaces.sosa.isResultOf, observation_subject))
+ data_graph.add((measurement_subject, Namespaces.qudt.unit, unit_triples[0][2]))
+
+ rdf_value = Figure.serialize_value(data_graph, self.__getitem__('value', 0))
+
+ data_graph.add((measurement_subject, Namespaces.qudt.value, rdf_value))
+ data_graph.add((measurement_subject, Namespaces.schema.dateCreated, rdflib.Literal(datetime.datetime.now().astimezone())))
+
+ # TODO add uncertainty
+
+ result = data_graph
+ else:
+ raise DeviceException('The provided kind of semantic information cannot be returned.')
+ return result
diff --git a/src/soil/parameter.py b/src/soil/parameter.py
index b9e1579af24ab2d472d8c71bd2ebe93b29620a1e..d4f86fef7f9f9659a3e9e75b52c1555a63178d63 100644
--- a/src/soil/parameter.py
+++ b/src/soil/parameter.py
@@ -1,4 +1,5 @@
import asyncio
+import copy
import inspect
from typing import Dict, Callable, Any, List
@@ -7,6 +8,7 @@ import rdflib
from .datatype import Datatype
from .error import ReadOnlyException
from .figure import Figure
+from .semantics import Semantics, Namespaces
from ..utils import root_logger
from ..utils.constants import HTTP_GET
from ..utils.error import DeviceException, SerialisationException
@@ -17,8 +19,9 @@ logger = root_logger.get(__name__)
class Parameter(Figure):
def __init__(self, uuid: str, name: str, description: str, datatype: Datatype, dimension: List[int], range: List,
- value: Any, getter: Callable = None, setter: Callable = None, ontology: str = None, shape: str = None):
- Figure.__init__(self, uuid, name, description, datatype, dimension, range, value, getter, ontology, shape)
+ value: Any, getter: Callable = None, setter: Callable = None, ontology: str = None,
+ profile: str = None):
+ Figure.__init__(self, uuid, name, description, datatype, dimension, range, value, getter, ontology, profile)
if uuid[:3] not in ['PAR', 'ARG', 'RET']:
raise Exception('{}: The UUID must start with PAR, ARG or RET!'.format(uuid))
if setter is not None and not callable(setter):
@@ -119,10 +122,10 @@ class Parameter(Figure):
getter = implementation['getter'] if implementation is not None else None
setter = implementation['setter'] if implementation is not None else None
ontology = dictionary['ontology'] if 'ontology' in dictionary else None
- shape = dictionary['shape'] if 'shape' in dictionary else None
+ profile = dictionary['profile'] if 'profile' in dictionary else None
return Parameter(dictionary['uuid'], dictionary['name'], dictionary['description'],
Datatype.from_string(dictionary['datatype']), dictionary['dimension'],
- dictionary['range'], dictionary['value'], getter, setter, ontology, shape)
+ dictionary['range'], dictionary['value'], getter, setter, ontology, profile)
except Exception as e:
raise SerialisationException('{}: The variable can not be deserialized. {}'.format(uuid, e))
@@ -134,4 +137,19 @@ class Parameter(Figure):
raise ReadOnlyException(self._uuid, self._name)
def serialize_semantics(self, kind: str) -> rdflib.Graph:
- return self._semantic_definition
+ if kind == 'profile':
+ result = self._metadata_profile
+ elif kind == 'metadata':
+ result = copy.deepcopy(self._metadata)
+
+ triples = list(result.triples((None, Namespaces.qudt['value'], None)))
+ if len(triples) > 0:
+ assert (len(triples) == 1)
+ result.remove(triples[0])
+
+ rdf_value = Figure.serialize_value(result, self.__getitem__('value', 0))
+ result.add((Semantics.namespace[self._semantic_name], Namespaces.qudt['value'], rdf_value))
+ return result
+ else:
+ raise DeviceException('The provided kind of semantic information cannot be returned.')
+ return result
diff --git a/src/soil/semantics.py b/src/soil/semantics.py
new file mode 100644
index 0000000000000000000000000000000000000000..1f3baeb74b67e98fb09e97739655e2432447931b
--- /dev/null
+++ b/src/soil/semantics.py
@@ -0,0 +1,28 @@
+import rdflib
+
+
+class Semantics(object):
+
+ prefix: str = None
+ url: str = None
+ namespace: rdflib.Namespace = None
+
+ def __init__(self, config: dict[str, str]):
+ Semantics.prefix = config['prefix']
+ Semantics.url = config['url']
+ Semantics.namespace = rdflib.Namespace(config['url'])
+
+
+class Namespaces(object):
+
+ m4i = rdflib.Namespace('http://w3id.org/nfdi4ing/metadata4ing#')
+ quantitykind = rdflib.Namespace('http://qudt.org/vocab/quantitykind/')
+ qudt = rdflib.Namespace('http://qudt.org/schema/qudt/')
+ rdf = rdflib.namespace.RDF
+ schema = rdflib.Namespace('https://schema.org/')
+ si = rdflib.Namespace('https://ptb.de/si/')
+ soil = rdflib.Namespace('https://purl.org/fair-sensor-services/soil#')
+ sosa = rdflib.namespace.SOSA
+ ssn = rdflib.namespace.SSN
+ ssn_system = rdflib.Namespace('http://www.w3.org/ns/ssn/systems/')
+ unit = rdflib.Namespace('http://qudt.org/vocab/unit/')
diff --git a/src/soil/stream.py b/src/soil/stream.py
index 65ab197e49c6ff28f7a637a5224da0ab4c9ae33f..2cba19d9de418745a2c43136a34ecf09daa610a5 100644
--- a/src/soil/stream.py
+++ b/src/soil/stream.py
@@ -3,6 +3,7 @@ import json
from abc import ABC, abstractmethod
from typing import List, Callable, Any, Union, Dict
+import rdflib
from wzl.mqtt.client import MQTTPublisher
from . import figure
@@ -96,6 +97,16 @@ class Job(ABC):
return {}
return metadata
+ def _retrieve_semantic_metadata(self, model: 'Component' = None):
+ if model is None:
+ return rdflib.Graph()
+ try:
+ uuids = self.topic.split('/')
+ metadata = model.__getitem__(uuids).serialize_semantics([], 'data')
+ except Exception:
+ return rdflib.Graph()
+ return metadata
+
def data(self, model: Dict = None) -> Dict:
try:
data = self._retrieve_metadata(model)
@@ -106,6 +117,28 @@ class Job(ABC):
except Exception as e:
raise JobError()
+ def semantic_data(self, model: Dict = None) -> Dict:
+ try:
+ data = self._retrieve_semantic_metadata(model)
+ # TODO set mqtt topic as identifier
+
+ triples = list(self._metadata.triples((None, rdflib.URIRef("http://schema.org/name"), None)))
+ assert (len(triples) == 1)
+ triple = triples[0]
+
+ subject = triple[0]
+ predicate = rdflib.URIRef("http://qudt.org/schema/qudt/value")
+ object = rdflib.Literal(self.value)
+
+ # TODO creating the Measurement
+
+ # data.add(())
+ # data['value'] = self.value
+ # data['timestamp'] = figure.serialize_time(datetime.datetime.now())
+ return data
+ except Exception as e:
+ raise JobError()
+
class FixedJob(Job):
diff --git a/test/data/Sensor.json b/test/data/Sensor.json
deleted file mode 100644
index bea9071e5c3588a591125a1437b56f6781c24501..0000000000000000000000000000000000000000
--- a/test/data/Sensor.json
+++ /dev/null
@@ -1,231 +0,0 @@
-{
- "objects": [],
- "functions": [
- {
- "arguments": [],
- "returns": [],
- "uuid": "FUN-Refresh",
- "name": "Trigger Refresh",
- "description": "Manually triggers refresh of the sensors"
- },
- {
- "arguments": [
- {
- "constant": false,
- "range": [
- 1,
- null
- ],
- "datatype": "int",
- "dimension": [],
- "value": 1,
- "uuid": "PAR-Interval",
- "name": "Interval (s)",
- "description": "Interval in seconds over which to take RMS."
- }
- ],
- "returns": [
- {
- "constant": false,
- "range": [
- null,
- null
- ],
- "datatype": "int",
- "dimension": [],
- "value": 0,
- "uuid": "PAR-Count",
- "name": "Count",
- "description": "Number of values used for RMS estimation"
- },
- {
- "constant": false,
- "range": [
- null,
- null
- ],
- "datatype": "double",
- "dimension": [],
- "value": 0,
- "uuid": "PAR-RMS",
- "name": "RMS (*C)",
- "description": "Estimated RMS form measurement sequence"
- },
- {
- "constant": false,
- "range": [
- null,
- null
- ],
- "datatype": "double",
- "dimension": [],
- "value": 0,
- "uuid": "PAR-Mean",
- "name": "Mean (*C)",
- "description": "Mean of RMS estimation sequence (*C)"
- }
- ],
- "uuid": "FUN-MeasureRMS",
- "name": "Measure RMS",
- "description": "Measures the RMS over a given period of time for this sensor."
- },
- {
- "arguments": [
- {
- "constant": false,
- "range": [
- 0,
- 100
- ],
- "datatype": "int",
- "dimension": [],
- "value": 1,
- "uuid": "PAR-Interval",
- "name": "Refresh Interval (s)",
- "description": "Current refresh interval in seconds."
- },
- {
- "constant": false,
- "range": [
- null,
- 20
- ],
- "datatype": "string",
- "dimension": [],
- "value": "outside",
- "uuid": "PAR-Location",
- "name": "Location",
- "description": "Description of the location of the sensor."
- }
- ],
- "returns": [],
- "uuid": "FUN-Reset",
- "name": "Reset",
- "description": "Resets various values of the sensor."
- }
- ],
- "variables": [
- {
- "unit": "CEL",
- "range": [
- null,
- null
- ],
- "datatype": "double",
- "dimension": [],
- "value": 0,
- "uuid": "VAR-Temperature",
- "name": "Temperature (*C)",
- "description": "Most recently measured temperature in degree celsius"
- },
- {
- "unit": "A97",
- "range": [
- null,
- null
- ],
- "datatype": "double",
- "dimension": [],
- "value": 0,
- "uuid": "VAR-Pressure",
- "name": "Pressure (hPa)",
- "description": "Most recently measured pressure in hPa"
- },
- {
- "unit": "",
- "range": [
- 0,
- 100
- ],
- "datatype": "int",
- "dimension": [],
- "value": 0,
- "uuid": "VAR-Battery",
- "name": "Batterylevel (Percent)",
- "description": "Estimated battery level in percent"
- },
- {
- "unit": "",
- "range": [
- 0,
- 100
- ],
- "datatype": "int",
- "dimension": [],
- "value": 0,
- "uuid": "VAR-Signal",
- "name": "Signal strength",
- "description": "Signal strength in percent"
- },
- {
- "unit": "",
- "range": [
- 0,
- 100
- ],
- "datatype": "int",
- "dimension": [],
- "value": 50,
- "uuid": "VAR-Humidity",
- "name": "Humidity (Percent)",
- "description": "Gives the current humidity in percent."
- }
- ],
- "parameters": [
- {
- "constant": true,
- "range": [
- null,
- null
- ],
- "datatype": "string",
- "dimension": [],
- "value": "AC-DE-48-00-00-80",
- "uuid": "PAR-Mac",
- "name": "MAC-Adress",
- "description": "Bluetooth MAC-Address of the sensor "
- },
- {
- "constant": false,
- "range": [
- 0,
- 100
- ],
- "datatype": "int",
- "dimension": [],
- "value": 0,
- "uuid": "PAR-Interval",
- "name": "Refresh Interval (s)",
- "description": "Current refresh interval in seconds"
- },
- {
- "constant": false,
- "range": [
- null,
- 20
- ],
- "datatype": "string",
- "dimension": [],
- "value": "undefined",
- "uuid": "PAR-Location",
- "name": "Location",
- "description": "Description of the location of the sensor"
- },
- {
- "constant": true,
- "range": [
- "False",
- "True"
- ],
- "datatype": "bool",
- "dimension": [],
- "value": true,
- "uuid": "PAR-Contacted",
- "name": "Contacted",
- "description": "Flag whether the temperature is measured contacted (true) or ambient (false)"
- }
- ],
- "uuid": "OBJ-Sensor",
- "name": "Sensor",
- "description": "An individual Sensor of the distributed system."
-}
\ No newline at end of file