Commit aadf674c authored by GromeTT's avatar GromeTT
Browse files

ENH: Added partial documentation to thing.py

parent d8c97a7f
......@@ -14,6 +14,7 @@ Table of Contents
:caption: Contents:
md/preliminaries.md
thing
dt_factory
tools
......
......@@ -2,18 +2,21 @@
import threading
import json
import uuid
from s3i import IdentityProvider, TokenType, GetValueReply, Directory, Repository
import time
import copy
from s3i import IdentityProvider, TokenType, GetValueReply, Directory
from s3i import Repository
from s3i.broker import Broker, BrokerREST
from s3i.messages import ServiceReply
from ml.identifier import ID
from ml.tools import BColors
from ml.tools import find_broker_endpoint
from ml.app_logger import APP_LOGGER
import time
import copy
class BaseVariable(object):
class BaseVariable:
"""The BaseVariable class holds various urls to s3i services."""
IDP_URL = "https://idp.s3i.vswf.dev/"
IDP_REALM = "KWH"
BROKER_HOST = "rabbitmq.s3i.vswf.dev"
......@@ -23,17 +26,21 @@ class BaseVariable(object):
class Thing:
"""The thing class represents a customizable runtime environment for
operating digital twins complying the forest modeling language (fml40)."""
def __init__(
self,
model: dict,
client_secret="",
grant_type="password",
is_broker=False,
is_repo=False,
is_broker_rest=True,
username=None,
password=None,
self,
model: dict,
client_secret="",
grant_type="password",
is_broker=False,
is_repo=False,
is_broker_rest=True,
username=None,
password=None,
):
self.__model = model
self.__thing_id = model.get("thingId", "")
if not self.__thing_id:
......@@ -49,7 +56,6 @@ def __init__(
self.__is_repo = is_repo
self.__access_token = ""
self.__endpoint = ""
self.__ws_connected = False
......@@ -72,10 +78,24 @@ def __init__(
@property
def model(self):
"""Returns the specification from which this thing has been constructed from.
:returns: Representation of a ml40 compliant thing.
:rtype: dict
"""
return self.__model
@property
def features(self):
"""Returns
:returns:
:rtype:
"""
return self.__features
@features.setter
......@@ -84,37 +104,93 @@ def features(self, value):
@property
def roles(self):
"""Returns the things roles.
:returns: ml40 roles
:rtype: dict
"""
return self.__roles
@roles.setter
def roles(self, value):
"""Adds a new role.
:param value: The new role
:type value: ml.role
"""
self.__roles = value
@property
def client_secret(self):
"""Returns the client secret.
:returns: Client secret
:rtype: str
"""
return self.__client_secret
@property
def grant_type(self):
"""Returns the method used to obtain java web tokens from the
identity provider.
:returns: authentication method
:rtype: str
"""
return self.__grant_type
@property
def access_token(self):
"""Returns the current java web token or an empty string.
:returns: java web token
:rtype: str
"""
return self.__access_token
@property
def name(self):
"""Returns the name of this thing.
:returns: name
:rtype: str
"""
return self.__name
@property
def thing_id(self):
"""Returns the identifier of this thing.
:returns: identifier
:rtype: str
"""
return self.__thing_id
@property
def policy_id(self):
"""Returns the identifier of this thing's policy.
:returns: identifier
:rtype: str
"""
return self.__policy_id
def run_forever(self):
"""Starts the thing in permanent mode.
"""
# TODO: Use logger instead!
print("[S³I]: Launch {}{}{}".format(BColors.OKGREEN, self.name, BColors.ENDC))
self.__connect_with_idp()
......@@ -128,6 +204,7 @@ def add_user_def(func):
threading.Thread(target=func).start()
def __json_syn(self, freq=0.1):
# ???: Does this do anything useful?
while True:
try:
time.sleep(freq)
......@@ -136,19 +213,29 @@ def __json_syn(self, freq=0.1):
continue
def __dir_syn(self, freq=0.1):
"""Applies local changes to the directory entry.
:param freq: Frequency of the update.
"""
while True:
#try:
time.sleep(freq)
old_dir_json = self.dir_json
self.to_dir_json()
if self.dir_json == old_dir_json:
continue
else:
self.dir.updateThingIDBased(thingID=self.thing_id, payload=self.dir_json)
# except:
# continue
# try:
time.sleep(freq)
old_dir_json = self.dir_json
self.to_dir_json()
if self.dir_json == old_dir_json:
continue
else:
self.dir.updateThingIDBased(
thingID=self.thing_id, payload=self.dir_json
)
# except:
# continue
def __repo_syn(self, freq=0.1):
"""Applies local changes to the repository entry.
:param freq: Frequency of the update.
"""
while self.__is_repo:
try:
time.sleep(freq)
......@@ -157,12 +244,24 @@ def __repo_syn(self, freq=0.1):
if self.repo_json == old_repo_json:
continue
else:
self.repo.updateThingIDBased(thingID=self.thing_id, payload=self.repo_json)
self.repo.updateThingIDBased(
thingID=self.thing_id, payload=self.repo_json
)
except:
continue
def __connect_with_idp(self):
print(BColors.OKBLUE + "[S³I][IdP]" + BColors.ENDC + ": Connect with S3I IdentityProvider")
"""
"""
# FIXME: Use logger!
print(
BColors.OKBLUE
+ "[S³I][IdP]"
+ BColors.ENDC
+ ": Connect with S3I IdentityProvider"
)
idp = IdentityProvider(
grant_type=self.__grant_type,
client_id=self.__thing_id,
......@@ -190,7 +289,9 @@ def __connect_with_dir(self):
+ BColors.ENDC
+ ": Connect with S3I Directory"
)
self.dir = Directory(s3i_dir_url=BaseVariable.DIR_URL, token=self.__access_token)
self.dir = Directory(
s3i_dir_url=BaseVariable.DIR_URL, token=self.__access_token
)
def __connect_with_repo(self):
print(
......@@ -199,10 +300,17 @@ def __connect_with_repo(self):
+ BColors.ENDC
+ ": Connect with S3I Repository"
)
self.repo = Repository(s3i_repo_url=BaseVariable.REPO_URL, token=self.__access_token)
self.repo = Repository(
s3i_repo_url=BaseVariable.REPO_URL, token=self.__access_token
)
def __connect_with_broker(self):
print(BColors.OKBLUE + "[S³I][Broker]" + BColors.ENDC + ": Connect with S3I Broker")
print(
BColors.OKBLUE
+ "[S³I][Broker]"
+ BColors.ENDC
+ ": Connect with S3I Broker"
)
self.__endpoint = find_broker_endpoint(self.dir, thing_id=self.thing_id)
if self.__is_broker_rest:
self.broker = BrokerREST(token=self.access_token)
......@@ -215,14 +323,16 @@ def receive():
if msg_str == "":
continue
else:
self.__on_broker_callback(ch=None, method=None, properties=None,
body=json.loads(msg_str))
self.__on_broker_callback(
ch=None,
method=None,
properties=None,
body=json.loads(msg_str),
)
except:
continue
threading.Thread(
target=receive
).start()
threading.Thread(target=receive).start()
else:
self.broker = Broker(
......@@ -347,7 +457,9 @@ def _getValue(self, source, uri_list):
try:
stringValue_split = value.split(":")
if stringValue_split[0] == "ditto-feature":
value = self.dt_json["features"][stringValue_split[1]]["properties"][uri_list[0]]
value = self.dt_json["features"][stringValue_split[1]][
"properties"
][uri_list[0]]
except:
pass
self.__resGetValue.append(value)
......@@ -405,12 +517,12 @@ def on_service_request(self, body_json):
)
service_type = body_json.get("serviceType")
parameters = body_json.get("parameters")
service_functionality = service_type.split('/')[0]
service_functionality = service_type.split("/")[0]
service_functionality_obj = self.features.get(service_functionality)
if service_functionality_obj is None:
APP_LOGGER.critical(
"Functionality %s is not one of the built-in functionalities in %s!" % (
service_functionality, self.name)
"Functionality %s is not one of the built-in functionalities in %s!"
% (service_functionality, self.name)
)
else:
# TODO: Call right functionality.
......@@ -418,10 +530,11 @@ def on_service_request(self, body_json):
BColors.OKBLUE
+ "[S³I][Broker]"
+ BColors.ENDC
+ ": Execute the function {0} of the class {1}.".format(service_type.split('/')[1],
service_type.split('/')[0])
+ ": Execute the function {0} of the class {1}.".format(
service_type.split("/")[1], service_type.split("/")[0]
)
)
method = getattr(service_functionality_obj, service_type.split('/')[1])
method = getattr(service_functionality_obj, service_type.split("/")[1])
result = method(**parameters)
if isinstance(result, bool):
......@@ -434,11 +547,13 @@ def on_service_request(self, body_json):
serviceType=body_json.get("serviceType", None),
results=result,
replyingToUUID=body_json.get("identifier", None),
msgUUID="s3i:{}".format(uuid.uuid4())
msgUUID="s3i:{}".format(uuid.uuid4()),
)
res = self.broker.send(receiver_endpoints=[body_json.get("replyToEndpoint", None)],
msg=json.dumps(service_reply.msg))
res = self.broker.send(
receiver_endpoints=[body_json.get("replyToEndpoint", None)],
msg=json.dumps(service_reply.msg),
)
if res.status_code == 201:
print(
BColors.OKBLUE
......@@ -463,8 +578,9 @@ def on_get_value_reply(self, msg):
BColors.OKBLUE
+ "[S³I][Broker]"
+ BColors.ENDC
+ ": The queried value is: {0}{1}{2}".format(BColors.OKGREEN, value,
BColors.ENDC)
+ ": The queried value is: {0}{1}{2}".format(
BColors.OKGREEN, value, BColors.ENDC
)
)
def on_service_reply(self, msg):
......@@ -483,8 +599,9 @@ def on_service_reply(self, msg):
BColors.OKBLUE
+ "[S³I][Broker]"
+ BColors.ENDC
+ ": The result is: {0}{1}{2}".format(BColors.OKGREEN, results,
BColors.ENDC)
+ ": The result is: {0}{1}{2}".format(
BColors.OKGREEN, results, BColors.ENDC
)
)
def to_dir_json(self):
......@@ -498,34 +615,30 @@ def to_dir_json(self):
if self.features.get("ml40::Location") is not None:
self.dir_json["attributes"]["location"] = {
"longitude": self.features.get("ml40::Location").to_json()["longitude"],
"latitude": self.features.get("ml40::Location").to_json()["latitude"]
"latitude": self.features.get("ml40::Location").to_json()["latitude"],
}
self.dir_json["attributes"]["dataModel"] = "fml40"
self.dir_json["attributes"]["thingStructure"] = {
"class": "ml40::Thing",
"links": []
"links": [],
}
for key in self.roles.keys():
role_entry = {
"association": "roles",
"target": self.roles[key].to_json()
}
role_entry = {"association": "roles", "target": self.roles[key].to_json()}
self.dir_json["attributes"]["thingStructure"]["links"].append(role_entry)
for key in self.features.keys():
feature_target = {
"class": self.features[key].to_json()["class"],
"identifier": self.features[key].to_json()["identifier"]
}
feature_entry = {
"association": "features",
"target": feature_target
"identifier": self.features[key].to_json()["identifier"],
}
feature_entry = {"association": "features", "target": feature_target}
# if the feature has targets, like ml40::Composite
if hasattr(self.features[key], 'targets'):
if hasattr(self.features[key], "targets"):
feature_entry["target"]["links"] = list()
for target in self.features[key].targets.keys():
target_json = self.features[key].targets[target].to_subthing_dir_json()
target_json = (
self.features[key].targets[target].to_subthing_dir_json()
)
feature_entry["target"]["links"].append(target_json)
self.dir_json["attributes"]["thingStructure"]["links"].append(feature_entry)
return self.dir_json
......@@ -541,7 +654,7 @@ def to_json(self):
"attributes": {
"class": "ml40::Thing",
"name": self.name,
}
},
}
if self.roles:
self.dt_json["attributes"]["roles"] = list()
......@@ -559,7 +672,7 @@ def to_subthing_json(self):
"identifier": self.thing_id,
"name": self.name,
"roles": [],
"features": []
"features": [],
}
for key in self.roles.keys():
json_out["roles"].append(self.roles[key].to_json())
......@@ -568,25 +681,15 @@ def to_subthing_json(self):
return json_out
def to_subthing_dir_json(self):
json_out = {
"class": "ml40::Thing",
"identifier": self.thing_id,
"links": []
}
json_out = {"class": "ml40::Thing", "identifier": self.thing_id, "links": []}
for key in self.roles.keys():
role_entry = {
"association": "roles",
"target":self.roles[key].to_json()
}
role_entry = {"association": "roles", "target": self.roles[key].to_json()}
json_out["links"].append(role_entry)
for key in self.features.keys():
feature_target = {
"class": self.features[key].to_json()["class"],
"identifier": self.features[key].to_json()["identifier"]
}
feature_entry = {
"association": "features",
"target": feature_target
"identifier": self.features[key].to_json()["identifier"],
}
feature_entry = {"association": "features", "target": feature_target}
json_out["links"].append(feature_entry)
return json_out
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment