Commit f6e29bc2 authored by Jiahang Chen's avatar Jiahang Chen
Browse files

rewritten ml package

parent dfa66f33
Pipeline #348603 passed with stage
{"thingId": "s3i:b6d1cc6d-896c-40fe-9403-b5b7682b1d03", "policyId": "s3i:b6d1cc6d-896c-40fe-9403-b5b7682b1d03", "attributes": {"class": "ml40::Thing", "name": "my_dt_harvester", "roles": [{"class": "fml40::Harvester"}], "features": [{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}, {"class": "ml40::Composite", "targets": [{"class": "ml40::Thing", "name": "my_engine", "roles": [{"class": "ml40::Engine"}], "features": [{"class": "ml40::RotationalSpeed", "rpm": 5}]}, {"class": "ml40::Thing", "name": "my_bord_computer", "roles": [{"class": "ml40::MachineUI"}], "features": []}]}]}}
\ No newline at end of file
{"thingId": "s3i:b6d1cc6d-896c-40fe-9403-b5b7682b1d03", "policyId": "s3i:b6d1cc6d-896c-40fe-9403-b5b7682b1d03", "attributes": {"class": "ml40::Thing", "name": "my_dt_harvester", "roles": [{"class": "fml40::Harvester"}], "features": [{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}, {"class": "ml40::Composite", "targets": [{"class": "ml40::Thing", "name": "my_engine", "roles": [{"class": "ml40::Engine"}], "features": [{"class": "ml40::RotationalSpeed", "rpm": 2001}]}, {"class": "ml40::Thing", "name": "my_bord_computer", "roles": [{"class": "ml40::MachineUI"}], "features": []}]}]}}
\ No newline at end of file
import s3i
import json
import jwt
import requests
from ml.tools import load_config, make_thing_config, make_sub_thing
from ml.dt_factory import create_dt_ref
from ml.dt_factory import create_dt_ref, build_feature, add_function_impl_obj
from ml.fml40.features.functionalities.accepts_felling_jobs import AcceptsFellingJobs
from ml.ml40.features.properties.values.documents.jobs.job_status import JobStatus
from ml.app_logger import APP_LOGGER, setup_logger
import time
dt_creation_app_id = "s3i:a44bd6dc-c607-4574-9b16-cf8579819356"
dt_creation_app_secret = "970ebe57-7dfb-4090-8c38-4b44111d5288"
......@@ -34,6 +34,7 @@
dt_id = resp.json().get("identifier", None)
dt_secret = resp.json().get("secret", None)
s3i_config.create_broker_queue(thing_id=dt_id)
res = s3i_config.create_cloud_copy(thing_id=dt_id)
print(dt_id)
"""
......@@ -43,40 +44,90 @@
config_engine = make_sub_thing(name="my_engine", roles=[{"class": "ml40::Engine"}],
features=[
{"class": "ml40::RotationalSpeed",
"rpm": 5}
"rpm": 2001}
])
config_cran = make_sub_thing(name="my_bord_computer", roles=[{"class": "ml40::MachineUI"}])
config_file_name = make_thing_config(dt_id=dt_id, name=dt_name, roles=[{"class": "fml40::Harvester"}],
features=[{"class": "fml40::ProvidesProductionData"},
{"class": "fml40::AcceptsFellingJobs"},
{"class": "ml40::Composite",
"targets": [config_engine, config_cran]}])
features=[{"class": "fml40::ProvidesProductionData"},
{"class": "fml40::AcceptsFellingJobs"},
{"class": "ml40::Composite",
"targets": [config_engine, config_cran]}])
setup_logger(dt_name)
dt_model = load_config('configs/{}'.format(config_file_name))
dt = create_dt_ref(model=dt_model, grant_type="password", secret=dt_secret, username=username, password=password,
is_broker_rest=True,
is_broker=True, is_repo=True)
dt_ref = create_dt_ref(model=dt_model, grant_type="password", secret=dt_secret, username=username, password=password,
is_broker_rest=True,
is_broker=True, is_repo=False)
dt_proxy = dt_ref.proxy()
dt_proxy.run_forever()
dt.run_forever()
class AcceptsFellingJobsImpl(AcceptsFellingJobs):
def my_accept_job_func(self, job):
APP_LOGGER.info("Checking job with my impl function.")
return self.add_to_job_list(job)
dt_proxy.add_function_impl(AcceptsFellingJobsImpl, "fml40::AcceptsFellingJobs")
#print(dt_proxy.features.get())
print(dt_proxy.features.get()["ml40::Composite"].proxy().to_json().get())
#print(dt_proxy.features.get()["Composite"].proxy().targets.get()[0].proxy().features.get())
#print(dt_proxy.features.get()["Composite"].proxy().targets.get()[0].proxy().features.get()["RotationalSpeed"].proxy().rpm.get())
#print(dt_proxy.roles.get())
#print(dt_proxy.name.get())
#print(dt_proxy.proxy_functionalities.get())
def __init__(self, name="", identifier=""):
super(AcceptsFellingJobs, self).__init__(
name=name,
identifier=identifier)
self.job_list = []
def acceptJob(self, job):
APP_LOGGER.info("Checking if the felling job can be accepted.")
if isinstance(job, dict):
try:
felling_job = build_feature(feature=job)
for job in self.job_list:
if job.identifier == felling_job.identifier:
APP_LOGGER.info("Job with ID {} has been rejected, because this job was already accepted".format(
felling_job.identifier))
return False
felling_job.status = JobStatus.InProgress.name
self.job_list.append(felling_job)
APP_LOGGER.info("Job with ID {} has been accepted".format(felling_job.identifier))
return True
except:
APP_LOGGER.info("Job with ID {} has been rejected".format(felling_job.identifier))
return False
def queryJobStatus(self, identifier):
APP_LOGGER.info("Checking the job status of job {}".format(identifier))
for job in self.job_list:
if job.identifier == identifier:
APP_LOGGER.info("Job {} is now in status {}".format(identifier, job.status))
return {"identifier": identifier, "status": job.status}
APP_LOGGER.info("Job {} can not be queried".format(identifier))
return {"identifier": identifier, "status": "NOT FOUND"}
def removeJob(self, identifier):
APP_LOGGER.info("Checking if i can remove the job {}".format(identifier))
for job in self.job_list:
if job.identifier == identifier:
self.job_list.remove(job)
APP_LOGGER.info("Job {} removed".format(identifier))
return True
APP_LOGGER.info("Job {} can not be found".format(identifier))
return False
def simulate_rpm():
my_engine = dt.features["ml40::Composite"].targets["my_engine"]
tank = "up"
while True:
if tank == "down":
__new_rpm = my_engine.features["ml40::RotationalSpeed"].rpm - 10
if __new_rpm < 2000:
tank = "up"
elif tank == "up":
__new_rpm = my_engine.features["ml40::RotationalSpeed"].rpm + 10
if __new_rpm > 2500:
tank = "down"
my_engine.features["ml40::RotationalSpeed"].rpm = __new_rpm
time.sleep(1)
add_function_impl_obj(dt, AcceptsFellingJobsImpl, "fml40::AcceptsFellingJobs")
dt.add_user_def(func=simulate_rpm)
......@@ -2,13 +2,10 @@
import jwt
import uuid
from ml.tools import find_broker_endpoint, make_thing_config, load_config, make_feature_config
from ml.identifier import ID
from ml.dt_factory import create_dt_ref, create_feature_ref
from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob
from ml.identifier import ID
from ml.dt_factory import create_dt_ref, build_feature
from ml.app_logger import setup_logger
import requests
import json, time
import json
username = "chen"
password = "8810515"
......@@ -17,63 +14,69 @@
setup_logger("my HMI")
config_file_name = make_thing_config(dt_id=hmi_id, name="my HMI", roles=[{"class": "ml40::HMI"}],
)
)
hmi_model = load_config('configs/{}'.format(config_file_name))
hmi_ref = create_dt_ref(model=hmi_model, grant_type="password", secret=hmi_secret,
username=username, password=password,
is_broker_rest=True, is_broker=True, is_repo=False)
hmi_proxy = hmi_ref.proxy()
hmi_proxy.run_forever()
hmi = create_dt_ref(model=hmi_model, grant_type="password", secret=hmi_secret,
username=username, password=password,
is_broker_rest=True, is_broker=True, is_repo=False)
hmi.run_forever()
hmi_endpoint = find_broker_endpoint(hmi.dir, hmi_id)
receiver = input("[S³I]: Please enter the id of your digital twin: ")
"""
Print out thing entry from directory and repository
"""
print(hmi.dir.queryThingIDBased(thingID=receiver))
print(hmi.repo.queryThingIDBased(thingID=receiver))
hmi_endpoint = find_broker_endpoint(hmi_proxy.dir.get(), hmi_id)
serv_req = s3i.messages.ServiceRequest()
subFeatures = [{
"class": "fml40::Assortment",
"grade": "fl",
"name": "Stammholz Abschnitte",
"subFeatures": [
{
"class": "fml40::ThicknessClass",
"name": ">"
},
{
"class": "fml40::WoodQuality",
"name": "B-C"
},
{
"class": "fml40::HarvestingParameters",
"cuttingLengths": 20
},
{
"class": "fml40::TreeType",
"name": "Spruce",
"conifer": True
},
{
"class": "fml40::HarvestedVolume",
"volume": 140
}
]
}]
id = ID(identifier="s3i:721b1947-2807-4bc3-a6d3-fe9e97de90d0").ID
feature_config_json = make_feature_config(class_name="fml40::FellingJob", identifier=id, subFeatures=subFeatures)
felling_job_ref = create_feature_ref(model=feature_config_json, thing_ref=hmi_ref)
felling_job_proxy = felling_job_ref.proxy()
print(felling_job_proxy.to_json().get())
receiver = input("[S³I]: Please enter the id of your digital twin: ")
"class": "fml40::Assortment",
"grade": "fl",
"name": "Stammholz Abschnitte",
"subFeatures": [
{
"class": "fml40::ThicknessClass",
"name": ">"
},
{
"class": "fml40::WoodQuality",
"name": "B-C"
},
{
"class": "fml40::HarvestingParameters",
"cuttingLengths": 20
},
{
"class": "fml40::TreeType",
"name": "Spruce",
"conifer": True
},
{
"class": "fml40::HarvestedVolume",
"volume": 140
}
]
}]
feature_config_json = make_feature_config(class_name="fml40::FellingJob", subFeatures=subFeatures,
name="my_felling_job")
felling_job = build_feature(feature=feature_config_json)
serv_req.fillServiceRequest(
senderUUID=hmi_id, receiverUUID=[receiver], sender_endpoint=hmi_endpoint,
serviceType="fml40::AcceptsFellingJobs/removeJob",
parameters={"identifier": id},
#parameters={"job": felling_job_proxy.to_json().get()},
msgUUID=str(uuid.uuid4())
#serviceType="fml40::AcceptsFellingJobs/acceptJob",
parameters={"identifier": "s3i:d09214a7-46bb-4672-b4e0-2c0aa0e395a3"},
#parameters={"job": felling_job.to_json()},
msgUUID="s3i:{}".format(uuid.uuid4())
)
receiver_endpoint = find_broker_endpoint(hmi_proxy.dir.get(), thing_id=receiver)
resp = hmi_proxy.broker.get().send([receiver_endpoint], json.dumps(serv_req.msg))
getv_req = s3i.GetValueRequest()
rpm_path = "attributes/features/ml40::Composite/targets/ml40::Engine/features/ml40::RotationalSpeed/rpm"
getv_req.fillGetValueRequest(
senderUUID=hmi_id, receiverUUID=[receiver], sender_endpoint=hmi_endpoint,
attributePath=rpm_path, msgUUID="s3i:{}".format(uuid.uuid4())
)
receiver_endpoint = find_broker_endpoint(hmi.dir, thing_id=receiver)
resp = hmi.broker.send([receiver_endpoint], json.dumps(serv_req.msg))
""" Implements a factory for managing digital twins."""
import sys, json
from ml.managing_actor import ManagingActor
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
from ml.ml40.roles.hmis.dashboard import Dashboard
......@@ -54,10 +68,10 @@
from ml.fml40.roles.dts.sites.mill.papermill import Papermill
from ml.fml40.roles.dts.sites.mill.sawmill import Sawmill
from ml.ml40.features.properties.association import Association
from ml.ml40.features.properties.composite import Composite
from ml.ml40.features.properties.associations.association import Association
from ml.ml40.features.properties.associations.composite import Composite
from ml.ml40.features.properties.property import Property
from ml.ml40.features.properties.shared import Shared
from ml.ml40.features.properties.associations.shared import Shared
from ml.ml40.features.properties.values.address import Address
from ml.ml40.features.properties.values.dimensions import Dimensions
from ml.ml40.features.properties.values.generic_property import GenericProperty
......@@ -155,32 +169,6 @@
# TODO: Get rid of this global variable
# TODO: automatically get all classes in modul
DT_FACTORY = {}
"""
DT_FACTORY["Thing"] = Thing
DT_FACTORY["Composite"] = Composite
DT_FACTORY["Service"] = Service
DT_FACTORY["HMI"] = HMI
DT_FACTORY["Harvester"] = Harvester
DT_FACTORY["LogTruck"] = LogTruck
DT_FACTORY["ManagesJobs"] = ManagesJobs
DT_FACTORY["Location"] = Location
DT_FACTORY["Shared"] = Shared
DT_FACTORY["Weight"] = Weight
DT_FACTORY["Moisture"] = Moisture
DT_FACTORY["AcceptsProximityAlert"] = AcceptsProximityAlert
DT_FACTORY["AcceptsFellingJobs"] = AcceptsFellingJobs
DT_FACTORY["ProvidesProductionData"] = ProvidesProductionData
DT_FACTORY["Harvests"] = Harvests
DT_FACTORY["Forwards"] = Forwards
DT_FACTORY["AcceptsForwardingJobs"] = AcceptsForwardingJobs
DT_FACTORY["ProvidesPassabilityInformation"] = ProvidesPassabilityInformation
DT_FACTORY["ProvidesMoisturePrediction"] = ProvidesMoisturePrediction
DT_FACTORY["MoisturePredictionReport"] = MoisturePredictionReport
DT_FACTORY["AcceptsPassabilityReport"] = AcceptsPassabilityReport
"""
import sys, inspect
......@@ -188,166 +176,100 @@
for member in clsmembers:
DT_FACTORY[member[0]] = member[1]
def get_dt_names():
"""Returns a list containg the names of all registered digital twins.
"""
dt_names = list(map(lambda x: x, DT_FACTORY.keys()))
return dt_names
def create_dt_with_idp(config, id_p):
"""Creates and returns a ditigal twin.
:param config: A json-object describing the digital twin.
:param i_d_p: A s3i.IdentityProvider object
:returns: Tuple (digital twin, access token)
:rtype: tuple(config[type], str)
"""
dt_type = config["type"]
d_t = DT_FACTORY.get(dt_type, ManagingActor)
return d_t
# def create_dt(config):
# """Creates and returns a ditigal twin and returns .
# :param config: A json-object describing the digital twin.
# :returns: Tuple (digital twin, access token)
# :rtype: tuple(config[type], str)
# """
# id_p = get_idp(config)
# dt_type = config["type"]
# d_t = DT_FACTORY.get(dt_type, ManagingActor)(id_p, config)
# return d_t
def build_sub_features(feature_ins, feature):
sub_features = feature.get("subFeatures", [])
###
def build_sub_featrues(thing_ref, feature_proxy, json_feature):
json_sub_features = json_feature.get("subFeatures", [])
temp_dict = {}
for json_sub_feature in json_sub_features:
#str_sub_feature = json.dumps(json_sub_feature).replace("fml40::", "").replace("ml40::", "").replace("'", '"')
#json_sub_feature = json.loads(str_sub_feature)
sub_feature_name = json_sub_feature.get("class", "")
sub_feature = DT_FACTORY.get(remove_namespace(sub_feature_name), None)
if sub_feature is None:
APP_LOGGER.critical("Subfeature: %s is missing" % sub_feature_name)
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)
if sub_f_obj is None:
APP_LOGGER.critical("Subfeature: %s is missing" % sub_f_name)
else:
APP_LOGGER.debug("Adding subfeature: %s" % sub_feature_name)
sub_feature_ref = sub_feature.start(sub_feature_name, thing_ref)
temp_dict[sub_feature_name] = sub_feature_ref
for key in json_sub_feature.keys():
APP_LOGGER.debug("Adding subfeature: %s" % sub_f_name)
sub_f_instance = sub_f_obj()
for key in sub_f.keys():
if key == "targets":
build_sub_thing(feature_proxy, sub_feature)
build_sub_thing(sub_f_instance, sub_f)
elif key == "subFeatures":
build_sub_featrues(thing_ref, sub_feature_ref.proxy(), json_sub_feature)
build_sub_features(sub_f_instance, sub_f)
else:
setattr(sub_feature_ref.proxy(), key, json_sub_feature[key])
feature_proxy.subFeatures = temp_dict
setattr(sub_f_instance, key, sub_f[key])
feature_ins.subFeatures[sub_f_name] = sub_f_instance
def build_sub_thing(feature_proxy, json_feature):
def build_sub_thing(feature_ins, json_feature):
json_sub_things = json_feature.get("targets", [])
for json_sub_thing in json_sub_things:
sub_thing_ref = create_dt_ref(model={"attributes": json_sub_thing})
feature_proxy.targets.get().append(sub_thing_ref)
sub_thing_name = json_sub_thing.get("name", None)
feature_ins.targets[sub_thing_name] = sub_thing_ref
def build(thing_ref, attributes):
def build(thing, attributes):
if not isinstance(attributes, dict):
APP_LOGGER.critical("Attributes is no valid json")
return
thing_proxy = thing_ref.proxy()
roles = attributes.get("roles", [])
__build_roles(thing_ref, thing_proxy, roles)
json_features = attributes.get("features", [])
for json_feature in json_features:
feature_name = json_feature.get("class", "")
feature_pars = json_feature
feature = DT_FACTORY.get(remove_namespace(feature_name), None)
if feature is None:
APP_LOGGER.critical("Feature: %s is missing" % feature_name)
else:
APP_LOGGER.debug("Adding feature: %s" % feature_name)
feature_ref = feature.start(feature_name, thing_ref)
feature_proxy = feature_ref.proxy()
for key in feature_pars.keys():
if key == "targets":
build_sub_thing(feature_proxy, json_feature)
elif key == "subFeatures":
build_sub_featrues(thing_ref, feature_proxy, json_feature)
else:
setattr(feature_proxy, key, feature_pars[key])
thing_proxy.features.get()[feature_proxy.name.get()] = feature_ref
def __build_roles(thing_ref, thing_proxy, roles):
for role in roles:
role_name = role.get("class", "")
role = DT_FACTORY.get(remove_namespace(role_name), None)
if role is None:
APP_LOGGER.critical("Roles: %s is missing" % role_name)
else:
APP_LOGGER.debug("Adding roles: %s" % role_name)
role_ref = role.start(role_name, thing_ref)
thing_proxy.roles.get()[role_name] = role_ref
def create_feature_ref(model, thing_ref):
if not isinstance(model, dict):
APP_LOGGER.error("The input feature model is no valid json")
feature_class = model.get("class", None)
if feature_class is None:
APP_LOGGER.error("Incomplete model: class missing")
feature = DT_FACTORY.get(feature_class.replace("fml40::", "").replace("ml40::", ""), "")
feature_ref = feature.start(feature_class, thing_ref)
feature_proxy = feature_ref.proxy()
feature_id = model.get("identifier", None)
print(feature_id)
if feature_id is not None:
feature_proxy.identifier = feature_id
feature_sub_features = model.get("subFeatures", None)
if feature_sub_features is not None:
build_sub_featrues(thing_ref, feature_proxy, {"subFeatures": feature_sub_features})
role_instance = build_role(role)
thing.roles[role.get("class")] = role_instance
return feature_ref
json_features = attributes.get("features", [])
for feature in json_features:
feature_ins = build_feature(feature=feature)
thing.features[feature.get("class")] = feature_ins
def build_role(role):
role_class_name = role.get("class", "")
role_obj = DT_FACTORY.get(remove_namespace(role_class_name), None)
if role_obj is None:
APP_LOGGER.critical("Roles: %s is missing" % role_class_name)
else:
APP_LOGGER.debug("Adding roles: %s" % role_class_name)
role_instance = role_obj()
return role_instance
def build_feature(feature):
feature_class_name = feature.get("class", "")
feature_obj = DT_FACTORY.get(remove_namespace(feature_class_name), None)
if feature_obj is None:
APP_LOGGER.critical("Feature: %s is missing" % feature_class_name)
else:
APP_LOGGER.debug("Adding feature: %s" % feature_class_name)
feature_instance = feature_obj()
for key in feature.keys():
if key == "targets":
build_sub_thing(feature_instance, feature)
elif key == "subFeatures":
build_sub_features(feature_instance, feature)
else:
setattr(feature_instance, key, feature[key])
return feature_instance
def add_function_impl_obj(thing, impl_obj, feature_name):
feature = thing.features.get(feature_name, None)
if feature is None:
APP_LOGGER.critical(
"Functionality %s is not one of the build-in functionalities" % feature_name
)
else:
APP_LOGGER.debug("Implementation object is added into the functionality %s" % feature_name)
thing.features[feature_name] = impl_obj(feature_name)
def create_dt_ref(model, grant_type="password",
secret="", username=None, password=None,
is_broker_rest=False, is_broker=False, is_repo=False):
"""Creates a ditigal twin, runs it in an own thread and returns a
reference to it.
:param config: A json-object describing the digital twin.
:param i_d_p: A s3i.IdentityProvider object
:returns: Tuple (digital twin, access token)
:rtype: tuple(Reference, str)
"""
attributes = model.get("attributes", None)
if attributes is None:
sys.exit("Incomplete model: attributes missing!")
#attributes = json.dumps(attributes).replace("fml40::", "").replace("ml40::", "").replace("'", '"')
#attributes = json.loads(attributes)
thing_type = remove_namespace(attributes.get("class", ""))
if thing_type == "":
print("Unknown type %s" % thing_type)
sys.exit()
roles = attributes.get("roles", None)
if roles is None:
......@@ -355,8 +277,10 @@ def create_dt_ref(model, grant_type="password",
thing_name = attributes.get("name", "")
APP_LOGGER.debug("Creating ditigtal twin {} with id {}".format(thing_name, model.get("thingId", "")))
d_t = DT_FACTORY.get(thing_type, ManagingActor)
thing_ref = d_t.start(
d_t = DT_FACTORY.get(thing_type)
thing_ref = d_t(
model=model,
grant_type=grant_type,
client_secret=secret,
......
from ml.identifier import ID
import sys
from abc import ABC