Commit 170dae2e authored by Jiahang Chen's avatar Jiahang Chen
Browse files

add documentation

parent bd2bee5d
Pipeline #359800 passed with stages
in 35 seconds
......@@ -3,25 +3,6 @@
from ml.app_logger import APP_LOGGER
from ml.tools import remove_namespace
"""
from ml.thing import Thing
from ml.ml40.features.properties.associations.composite import Composite
from ml.fml40.roles.dts.machines.harvester import Harvester
from ml.fml40.features.functionalities.accepts_felling_jobs import AcceptsFellingJobs
from ml.ml40.features.properties.values.rotational_speed import RotationalSpeed
from ml.ml40.roles.dts.parts.engine import Engine
from ml.fml40.features.functionalities.provides_production_data import ProvidesProductionData
from ml.ml40.roles.hmis.machine_ui import MachineUI
from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob
from ml.ml40.roles.hmis.hmi import HMI
from ml.fml40.features.properties.values.assortment import Assortment
from ml.fml40.features.properties.values.thickness_class import ThicknessClass
from ml.fml40.features.properties.values.wood_quality import WoodQuality
from ml.fml40.features.properties.values.harvesting_parameter import HarvestingParameters
from ml.fml40.features.properties.values.tree_type import TreeType
from ml.fml40.features.properties.values.harvested_volume import HarvestedVolume
"""
from ml.thing import Thing
from ml.ml40.roles.servives.service import Service
from ml.ml40.roles.hmis.app import App
......@@ -178,8 +159,16 @@
def build_sub_features(feature_ins, feature):
sub_features = feature.get("subFeatures", [])
"""
build the ml40/fml40 sub features
:param feature_ins: ml40/fml40 feature instance
:type feature_ins: object
:param feature: ml40/fml40 feature
:type feature: dict
"""
sub_features = feature.get("subFeatures", [])
for sub_f in sub_features:
sub_f_name = sub_f.get("class", "")
sub_f_obj = DT_FACTORY.get(remove_namespace(sub_f_name), None)
......@@ -198,8 +187,17 @@ def build_sub_features(feature_ins, feature):
feature_ins.subFeatures[sub_f_name] = sub_f_instance
def build_sub_thing(feature_ins, json_feature):
json_sub_things = json_feature.get("targets", [])
def build_sub_thing(feature_ins, feature):
"""
build fml40 sub thing to ml40/fml40 feature
:param feature_ins: ml40/fml40 feature instance
:type feature_ins: object
:param feature: ml40/fml40 feature
:type feature: dict
"""
json_sub_things = feature.get("targets", [])
for json_sub_thing in json_sub_things:
sub_thing_ref = create_thing(model={"attributes": json_sub_thing})
sub_thing_name = json_sub_thing.get("name", None)
......@@ -207,6 +205,15 @@ def build_sub_thing(feature_ins, json_feature):
def build(thing, attributes):
"""
build ml40 thing
:param thing: ml40 thing instance
:type thing: object
:param attributes: attributes in ml40 thing json
:type attributes: dict
"""
if not isinstance(attributes, dict):
APP_LOGGER.critical("Attributes is no valid json")
return
......@@ -222,6 +229,13 @@ def build(thing, attributes):
def build_role(role):
"""
build ml40/fml40 role in a ml40 thing instance
:param role: ml40/fml40 role
:type role: dict
"""
role_class_name = role.get("class", "")
role_obj = DT_FACTORY.get(remove_namespace(role_class_name), None)
if role_obj is None:
......@@ -234,6 +248,13 @@ def build_role(role):
def build_feature(feature):
"""
build ml40/fml40 feature in a ml40 thing instance
:param feature: ml40/fml40 feature
:type feature: dict
"""
feature_class_name = feature.get("class", "")
feature_obj = DT_FACTORY.get(remove_namespace(feature_class_name), None)
......@@ -256,6 +277,18 @@ def build_feature(feature):
def add_function_impl_obj(thing, impl_obj, feature_name):
"""
add user-specific implemented object to a thing instance
:param thing: ml40 thing instance
:type thing: object
:param impl_obj: ml40/fml40 feature instance
:type impl_obj: object
:param feature_name: class name of a ml40/fml40 feature
:type feature_name: str
"""
feature = thing.features.get(feature_name, None)
if feature is None:
APP_LOGGER.critical(
......@@ -269,6 +302,30 @@ def add_function_impl_obj(thing, impl_obj, feature_name):
def create_thing(model, grant_type="password",
secret="", username=None, password=None,
is_broker_rest=False, is_broker=False, is_repo=False):
"""
create a ml40 thing instance
:param model: JSON of a ml40::thing model
:type model: dict
:param grant_type: grant type of OAuth2.0, which can be password or client_credentials
:type grant_type: str
:param secret: secret of a thing
:type secret: str
:param username: username, if the grant_type is set as password, the username is required
:type username: str
:param password: password, if the grant_type is set as password, the password is required
:type password: str
:param is_broker: whether broker interface is enabled in the ml40::thing instance
:type is_broker: bool
:param is_broker_rest: whether the broker interface uses the HTTP REST
:type is_broker_rest: bool
:param is_repo: whether the repository interface is enabled in the ml40::thing instance
:type is_repo: bool
:returns: ml40::thing instance
:rtype: object
"""
attributes = model.get("attributes", None)
if attributes is None:
......
......@@ -30,272 +30,21 @@ class BColors:
UNDERLINE = "\033[4m"
def get_idp(config, grant_type, client_id, secret, username, password):
"""Returns an s3i.IdentiyProvider object
:param config: json object representing a thing configuration
:returns: a s3i.IdentiyProvider object
:rtype:s3i.IdentiyProvider
"""
APP_LOGGER.debug("Using grant type %s", grant_type)
APP_LOGGER.debug("Creating identity provider")
if grant_type == GRANT_TYPES[0]:
id_p = IdentityProvider(
grant_type="client_credentials",
identity_provider_url=IDENTITY_PROVIDER_URL,
realm="KWH",
client_id=client_id,
client_secret=secret,
)
else:
id_p = IdentityProvider(
grant_type=grant_type,
identity_provider_url=IDENTITY_PROVIDER_URL,
realm="KWH",
client_id=client_id,
client_secret=secret,
username=username,
password=password,
)
return id_p
def get_s3i_broker(thing_ref):
"""Returns a s3i broker for dt_ref.
:param dt_ref: ActorRef of a digital twin
:returns: a s3i broker
:rtype: s3i.Broker
"""
thing_proxy = thing_ref.proxy()
access_token = thing_proxy.name.get()
s3i_broker = Broker(
auth_form="Username/Password",
username="",
password=access_token,
host="rabbitmq.s3i.vswf.dev",
)
return s3i_broker
def get_receiver_callback_func(dt_ref):
"""Returns a function that calls tell() on dt_ref. All arguments
passed to the returned function are bundled before calling tell().
:param dt_ref: ActorRef of a digital twin
:returns: function
"""
def callback(dt_ref, channel, method, properties, body):
"""Handler for receiving messages.
:param ch:
:param method:
:param properties:
:param body: received message
"""
# body["ch"] = channel
# body["method"] = method
# body["properties"] = properties
print("test")
dt_ref.tell(body)
print("test1")
tmp = partial(callback, dt_ref)
return tmp
def print_ts(msg, end="\n"):
"""
Prints the given message in the s3i style
:param msg: message to print
:type msg: string
"""
global console_no_new_line
trail = ""
if end == "\r":
console_no_new_line = True
elif console_no_new_line:
console_no_new_line = False
trail = "\n"
t_stamp = datetime.datetime.fromtimestamp(time.time()).strftime("%H:%M:%S.%f")[:-3]
prompt_msg = "{}[S³I][{}] {}{}".format(trail, t_stamp, msg, end)
print(prompt_msg)
def input_ts(msg):
"""Asks for user input and returns it."""
t_stamp = datetime.datetime.fromtimestamp(time.time()).strftime("%H:%M:%S.%f")[:-3]
prompt_msg = "[S³I][{}] {}".format(t_stamp, msg)
return input(prompt_msg)
def send_requests(dt_ref, requests):
"""Sends all given requests.
:param access_token: valid access token
:param sender_id: id of the sender
:param requests: iterable of tuples(receiver_ids,
service_type). Where receiver_ids represents an iterable object of
ids.
"""
dt_proxy = dt_ref.proxy()
access_token = dt_proxy.access_token.get()
sender_id = dt_proxy.thing_id.get()
for request in requests:
send_request(access_token, sender_id, request[1], request[0], request[2])
def send_request(access_token, sender_id, receiver_ids, service_type, parameters={}):
"""Sends a message of type service_type from sender_id to receiver_id
with the given content specified by parameters.
:param access_token: valid access token
:param sender_id: id of the sender
:param receiver_ids:
:param service_type:
:param parameters:
:returns:
:rtype:
"""
receiver_endpoints = [("s3ib://{}".format(i)) for i in receiver_ids]
s3i_broker = Broker(
auth_form="Username/Password",
username=" ",
password=access_token,
host="rabbitmq.s3i.vswf.dev",
)
req = create_request(sender_id, receiver_ids, service_type, parameters)
msg_str = req.msg.__str__()
s3i_broker.send(receiver_endpoints, req.msg.__str__())
APP_LOGGER.debug(f"Message send: {msg_str}")
def send_message(broker_obj, dir_obj, msg: dict):
if isinstance(msg, dict):
receivers = msg.get("receivers", None)
for r in receivers:
broker_obj.send([find_broker_endpoint(dir_obj, thing_id=r)], json.dumps(msg))
def find_broker_endpoint(dir_obj, thing_id):
thing_json = dir_obj.queryThingIDBased(thing_id)
all_endpoints = thing_json["attributes"].get("allEndpoints", None)
if all_endpoints:
for ep in all_endpoints:
if "s3ib" in ep:
return ep
def verify_message(message, message_type, service_type):
# TODO: Exception handling
if message["messageType"] != message_type:
return False
if message["serviceType"] != service_type:
return False
return True
def decode_message(msg, decode=True):
# convert bytes to str
tmp = msg
if decode:
tmp = msg.decode("utf8")
body_str = tmp.replace("'", '"').replace("True", "true").replace("False", "false")
return body_str
def get_end_point(access_token, thing_id):
s3i_directory = Directory(
s3i_dir_url="https://dir.s3i.vswf.dev/api/2/", token=access_token
)
# TDOO: How can this throw?
try:
return s3i_directory.queryAttributeBased("thingId", thing_id)
except Exception:
return None
def create_request(sender_id, receiver_ids, service_type, parameters={}):
"""Returns a service request"""
msg_uuid = "s3i:" + str(uuid.uuid4())
sender_uuid = id_to_endpoint(sender_id)
req = ServiceRequest()
req.fillServiceRequest(
sender_id, receiver_ids, sender_uuid, service_type, parameters, msgUUID=msg_uuid
)
return req
# TODO: Pass access token instead of idp
def dir_search_with_name(idp, name):
"""Queries the s3i directory for the id corresponding to name and
returns it.
:param idp: s3i.ServiceProvider object
:param name: name of the thing
:returns: id of the thing
:rtype: str
def make_sub_thing(name, roles, features=[]):
"""
create a JSON for a fml40 sub thing.
s3i_directory = Directory(
s3i_dir_url="https://dir.s3i.vswf.dev/api/2/",
token=idp.get_token(TokenType.ACCESS_TOKEN),
)
# TDOO: How can this throw?
try:
return s3i_directory.queryAttributeBased("name", name)[0]["thingId"]
except Exception:
return None
:param name: name of the sub thing
:type name: str
:param roles: fml40 roles of the sub thing
:type roles: str
:param features: fml40 features of the sub thing
:type features: list
def dir_id_to_defualt_endpoints(idp, thing_id):
"""Queries the s3i directory for the endpoint of the thing identifyied
by id and returns it.
:param idp: s3i.ServiceProvider object
:param id: id of the thing
:returns: endpoint
:rtype: str
:returns: JSON of the sub thing
:rtype: dict
"""
s3i_directory = Directory(
s3i_dir_url="https://dir.s3i.vswf.dev/api/2/",
token=idp.get_token(TokenType.ACCESS_TOKEN),
)
try:
endpoint = s3i_directory.queryThingIDBased(
thing_id + "/attributes/defaultEndpoints"
)
except Exception:
endpoint = []
return endpoint
def dir_name_to_default_endpoints(idp, name):
"""Queries the s3i directory for the endpoint of the thing identifyied
by name.
:param idp: s3i.ServiceProvider object
:param name: name of the thing
:returns: endpoint
:rtype: str
"""
thing_id = dir_search_with_name(idp, name)
endpoint = dir_id_to_defualt_endpoints(idp, thing_id)
return endpoint
def make_sub_thing(name, roles, features=[]):
sub_thing = {
"class": "ml40::Thing",
"name": name,
......@@ -307,6 +56,22 @@ def make_sub_thing(name, roles, features=[]):
def make_feature_config(class_name, identifier="", name="", subFeatures=""):
"""
Create a JSON for a fml40 feature
:param class_name: class name of the fml40 feature
:type class_name: str
:param identifier: local id of the fml40 feature
:type identifier: str
:param name: name of the fml40 feature
:type name: str
:param subFeatures: sub features
:type subFeatures: dict
:returns: JSON of the fml40 feature
:rtype: dict
"""
config_json = {
"class": class_name
}
......@@ -321,6 +86,25 @@ def make_feature_config(class_name, identifier="", name="", subFeatures=""):
def make_thing_config(thing_id, name, roles, features=[], config_path=""):
"""
Create a configuration file (JSON) for a fml40 thing.
:param thing_id: identifier of the thing
:type thing_id: str
:param name: name of the thing
:type name: str
:param roles: fml40 roles of the thing
:type roles: list
:param features: fml40 features of the thing
:type features: list
:param config_path: path where the configuration file should be located
:type config_path: str
:returns: name of the configuration file of the thing
:rtype: str
"""
if not config_path:
config_path = os.path.join("__file__", "configs")
......@@ -341,9 +125,10 @@ def make_thing_config(thing_id, name, roles, features=[], config_path=""):
def load_config(config_filepath):
"""Creates a json object from a json formatted file found at config_filepath.
"""load a json object from a json formatted file found at config_filepath.
:param config_filepath: Path to json formatted file.
:type config_filepath: str
"""
with open(config_filepath) as config_file:
......@@ -351,52 +136,31 @@ def load_config(config_filepath):
return config
def load_configs(config_filepaths):
"""Creates json objects from json formatted files specified by the
entries of config_filepaths.
:param config_filepath: Iterable of paths to json formatted files.
def find_broker_endpoint(dir_obj, thing_id):
"""
res = list(load_config(config) for config in config_filepaths)
return res
find the S3I-B endpoint of a thing
def id_to_endpoint(thing_id):
"""Returns the endpoint for thing_id.
:param thing_id: id
:returns: endpoint
:rtype: str
:param dir_obj: S³I Directory Object
:type dir_obj: object
:param thing_id: identifier of the searched thing
"""
thing_json = dir_obj.queryThingIDBased(thing_id)
all_endpoints = thing_json["attributes"].get("allEndpoints", None)
if all_endpoints:
for ep in all_endpoints:
if "s3ib" in ep:
return ep
if thing_id.startswith("s3ib//"):
return id
return "s3ib://{}".format(thing_id)
def get_requests(config):
"""Parses config and returns a list of requests.
Requests are tuples (service_type, receiver_uuid, parameters)
:param config: json object representing the configuration of a
thing
:returns: list of requests
:rtype: list(tuple(service_type, receiver_uuid, parameters))
def remove_namespace(input_str):
"""
requests = []
for txt in config["requests"]:
msg = config["requests"][txt]
msg_type = msg["msg_type"]
parameters = msg.get("parameters", {})
receiver_uuids = []
for target in msg["receivers"]:
receiver_uuids.append(target)
requests.append((msg_type, receiver_uuids, parameters))
return requests
remove the namespace like ml40 or fml40
:param input_str: input with namespace
:type input_str: str
:returns: output without namespace
:rtype: str
def remove_namespace(input_str):
"""
return input_str.replace("fml40::", "").replace("ml40::", "")
Supports Markdown
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