Commit 7dbabfcd authored by Jiahang Chen's avatar Jiahang Chen
Browse files

add subfeatures

parent a7e85415
Pipeline #340092 passed with stage
in 14 seconds
{"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"}]}}
\ 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"}]}, {"class": "ml40::Thing", "name": "my_bord_computer", "roles": [{"class": "ml40::MachineUI"}], "features": []}]}]}}
\ No newline at end of file
......@@ -2,15 +2,16 @@
import json
import jwt
import requests
from ml.tools import load_config, get_s3i_broker, get_receiver_callback_func, make_config_file
from ml.tools import load_config, make_config_file, make_sub_thing
from ml.dt_factory import create_dt_ref
from ml.fml40.features.functionalities.accepts_felling_jobs import AcceptsFellingJobs
from ml.app_logger import APP_LOGGER, setup_logger
dt_creation_app_id = "s3i:a44bd6dc-c607-4574-9b16-cf8579819356"
dt_creation_app_secret = "970ebe57-7dfb-4090-8c38-4b44111d5288"
#username = input('[S3I]: Please enter your username:').strip('," ')
#password = input('[S3I]: Please enter your password:')
# username = input('[S3I]: Please enter your username:').strip('," ')
# password = input('[S3I]: Please enter your password:')
username = "chen"
password = "8810515"
s3i_identity_provider = s3i.IdentityProvider(grant_type='password',
......@@ -25,7 +26,6 @@
''' decode the access token'''
parsed_username = jwt.decode(access_token, verify=False)["preferred_username"]
### create identity of digital twin
# TODO class for config api in master to merge
# create a thing identity for the dt
......@@ -50,8 +50,13 @@
dt_id = "s3i:b6d1cc6d-896c-40fe-9403-b5b7682b1d03"
dt_secret = "cd4d24d2-f702-4f51-b0cf-6b77a423b33a"
dt_name = "my_dt_harvester"
config_file_name = make_config_file(dt_id=dt_id, name=dt_name, roles="fml40::Harvester",
features=["fml40::ProvidesProductionData", "fml40::AcceptsFellingJobs"])
config_engine = make_sub_thing(name="my_engine", roles=["ml40::Engine"],
features=["ml40::RotationalSpeed"])
config_cran = make_sub_thing(name="my_bord_computer", roles=["ml40::MachineUI"])
config_file_name = make_config_file(dt_id=dt_id, name=dt_name, roles=["fml40::Harvester"],
features=["fml40::ProvidesProductionData", "fml40::AcceptsFellingJobs",
{"ml40::Composite": [config_engine, config_cran]}])
setup_logger(dt_name)
dt_model = load_config('configs/{}'.format(config_file_name))
dt_ref = create_dt_ref(model=dt_model, grant_type="password", secret=dt_secret, username=username, password=password,
......@@ -68,3 +73,9 @@ def my_accept_job_func(self, job):
dt_proxy.add_function_impl(AcceptsFellingJobsImpl, "AcceptsFellingJobs")
print(dt_proxy.features.get())
#print(dt_proxy.features.get()["Composite"].targets.get())
#print(dt_proxy.roles.get())
#print(dt_proxy.name.get())
#print(dt_proxy.proxy_functionalities.get())
......@@ -11,6 +11,12 @@
from ml.ml40.roles.servives.service import Service
from ml.ml40.roles.hmis.hmi import HMI
from ml.fml40.roles.dts.machines.harvester import Harvester
from ml.fml40.roles.dts.machines.log_truck import LogTruck
from ml.ml40.features.functionalities.manages_jobs import ManagesJobs
from ml.ml40.features.properties.values.location import Location
from ml.ml40.features.properties.shared import Shared
......@@ -47,12 +53,16 @@
# 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["Thing"] = Thing
DT_FACTORY["HMI"] = HMI
DT_FACTORY["Harvester"] = Harvester
DT_FACTORY["LogTruck"] = LogTruck
DT_FACTORY["ManagesJobs"] = ManagesJobs
DT_FACTORY["Location"] = Location
DT_FACTORY["Composite"] = Composite
DT_FACTORY["Shared"] = Shared
DT_FACTORY["Weight"] = Weight
DT_FACTORY["Moisture"] = Moisture
......@@ -127,32 +137,34 @@ def build_sub_featrues(thing_ref, feature_proxy, json_feature):
def build(thing_ref, attributes):
thing_proxy = thing_ref.proxy()
roles = attributes.get("roles", [])
for role in roles:
role_name = role.get("class", "")
if not role:
APP_LOGGER.critical("Roles: %s is missing" % role_name)
else:
APP_LOGGER.debug("Adding roles: %s" % role_name)
thing_proxy.roles.get().append(role_name)
__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 = DT_FACTORY.get(feature_name, None)
if not feature:
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("", thing_ref)
feature_ref = feature.start(feature_name, thing_ref)
feature_proxy = feature_ref.proxy()
feature_proxy.from_json(json_feature).get()
type_name = feature_proxy.type_name.get()
features = thing_proxy.features.get()
features[type_name] = feature_ref
thing_proxy.features.get()[feature_proxy.name.get()] = feature_ref
build_sub_featrues(thing_ref, feature_proxy, json_feature)
def __build_roles(thing_ref, thing_proxy, roles):
for role in roles:
role_name = role.get("class", "")
role = DT_FACTORY.get(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_dt_ref(model, grant_type, secret, username, password, is_broker_rest, is_broker, is_repo):
"""Creates a ditigal twin, runs it in an own thread and returns a
reference to it.
......
......@@ -13,7 +13,7 @@ def __init__(self, name, ref_managing_actor):
def acceptJob(self, job: FellingJob) -> bool:
APP_LOGGER.info("Checking if the felling job can be accepted.")
if job.get("id") is None or job.get("status") is None:
if job.get("identifier") is None or job.get("status") is None:
return False
return self.add_to_job_list(job)
......
......@@ -2,4 +2,8 @@
class ForestMachine(Machine):
pass
def __init__(self, name, ref_managing_actor):
super(ForestMachine, self).__init__(
name=name,
ref_managing_actor=ref_managing_actor
)
\ No newline at end of file
......@@ -2,4 +2,6 @@
class Harvester(ForestMachine):
pass
def __init__(self, name, ref_managing_actor):
super(Harvester, self).__init__(name=name,
ref_managing_actor=ref_managing_actor)
......@@ -3,16 +3,46 @@
import pykka
from s3i import TokenType
from ml.app_logger import APP_LOGGER
import uuid
from ml.identifier import ID
class ManagedActor(pykka.ThreadingActor):
"""Provides an encapsulation class for digital twins allowing to run
it in a dedicated thread."""
def __init__(self, name, refManagingActor):
def __init__(self, name, ref_managing_actor):
super().__init__()
self.managing_actor = refManagingActor.proxy()
self.managing_actor = ref_managing_actor.proxy()
self.managing_actor.register_managed_actor(name, self.actor_ref.proxy())
self.__name = name
self.__identifier = ID(identifier="s3i:{}".format(uuid.uuid4())).ID
@property
def name(self):
return self.__name
@name.setter
def name(self, value):
self.__name = value
@property
def identifier(self):
return self.__identifier
@identifier.setter
def identifier(self, value):
# TODO validate the id
self.__identifier = value
def to_json(self):
return {
"class": self.name,
"identifier": self.identifier,
}
def from_json(self, json_obj):
self.__name = json_obj.get("name", "")
def on_receive(self, message):
"""Prints a greatings formula and message to stdout.
......@@ -24,6 +54,3 @@ def on_receive(self, message):
self.__name, message
)
)
def from_json(self, json_obj):
self.__name = json_obj.get("name", "")
......@@ -6,42 +6,27 @@
class Feature(ManagedActor):
def __init__(self, name, ref_managing_actor):
super(Feature, self).__init__(name, ref_managing_actor)
self.__name = name
self.__class_name = ""
self.__identifier = ID(identifier="s3i:{}".format(uuid.uuid4())).ID
self.__subfeatures = []
@property
def type_name(self):
return self.__class_name
@property
def name(self):
return self.__name
@name.setter
def name(self, value):
self.__name = value
@property
def subfeatures(self):
return self.__subfeatures
@property
def identifier(self):
return self.__identifier
@identifier.setter
def identifier(self, value):
self.__identifier = value
@subfeatures.setter
def subfeatures(self, value):
self.__subfeatures = value
def to_json(self):
return {
"class": self.name,
"id": self.identifier,
}
temp_dict = super().to_json()
if self.subfeatures:
_dict = temp_dict.copy()
_dict.update({
"subfeatures": self.subfeatures,
})
return _dict
else:
return temp_dict
def from_json(self, json_obj):
self.__name = json_obj.get("name", "")
self.__class_name = json_obj.get("class", "")
self.__identifier = json_obj.get("identifier", "")
......@@ -6,9 +6,16 @@ def __init__(self, name, ref_managing_actor):
super(Composite, self).__init__(
name=name, ref_managing_actor=ref_managing_actor
)
# INFO: targets is a list of ids
# INFO: targets is a list of subfeatures
self.__targets = []
@property
def target(self):
return self.__targets
@target.setter
def target(self, value):
self.__targets = value
def from_json(self, json_obj):
super().from_json(json_obj)
......
......@@ -3,7 +3,7 @@
class Job(Document):
def __init__(self, name, ref_managing_actor, job_status: JobStatus):
def __init__(self, name, ref_managing_actor, job_status=JobStatus.Pending.name):
super(Job, self).__init__(name=name,
ref_managing_actor=ref_managing_actor)
self.__status = job_status
......@@ -21,7 +21,7 @@ def to_json(self):
if self.status:
_dict = temp_dict.copy()
_dict.update({
"status": self.status.name
"status": self.status
})
return _dict
return temp_dict
......@@ -2,7 +2,15 @@
class RotationalSpeed(Value):
def __init__(self, name, ref_managing_actor, rpm):
def __init__(self, name, ref_managing_actor, rpm=0):
super(RotationalSpeed, self).__init__(name=name,
ref_managing_actor=ref_managing_actor)
self.rpm = rpm
self.__rpm = rpm
@property
def rpm(self):
return self.__rpm
@rpm.setter
def rpm(self, value):
self.__rpm = value
......@@ -2,5 +2,8 @@
class Machine(DT):
def __init__(self):
pass
def __init__(self, name, ref_managing_actor):
super(Machine, self).__init__(
name=name,
ref_managing_actor=ref_managing_actor
)
......@@ -23,12 +23,6 @@ class BaseVariable(object):
DIR_URL = "https://dir.s3i.vswf.dev/api/2/"
def get_sensor_uuid(body_json):
parameters = body_json.get("parameters", {})
sensor_uuid = parameters.get("sensor_uuid", "")
return sensor_uuid
class Thing(ManagingActor):
def __init__(
self,
......@@ -66,12 +60,11 @@ def __init__(
self.__name = ""
self.__class_name = ""
self.__type_name = ""
self.__roles = []
self.__roles = {}
self.__features = {}
if attributes:
self.__name = attributes.get("name", "")
self.__roles = attributes.get("roles", "")
self.__class_name = attributes.get("class", "")
self.__type_name = attributes.get("type", "")
......@@ -91,10 +84,6 @@ def roles(self):
def roles(self, value):
self.__roles = value
@property
def class_name(self):
return self.__class_name
@property
def client_secret(self):
return self.__client_secret
......@@ -338,12 +327,11 @@ def on_get_value_reply(self, msg):
def on_service_reply(self, msg):
print(msg)
def add_function_impl(self, obj, feature_name):
def add_function_impl(self, impl_actor, feature_name):
feature = self.__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:
self.__features[feature_name] = obj.start("", self.actor_ref)
self.__features[feature_name] = impl_actor.start(feature_name, self.actor_ref)
......@@ -204,7 +204,6 @@ def verify_message(message, message_type, service_type):
def decode_message(msg, decode=True):
# convert bytes to str
tmp = msg
if decode:
......@@ -296,6 +295,32 @@ def dir_name_to_default_endpoints(idp, name):
return endpoint
def make_sub_thing(name, roles, id="", features=[]):
sub_thing = {
"class": "ml40::Thing",
"name": name,
"roles": [],
"features": []
}
if id:
sub_thing["id"] = id
for role in roles:
sub_thing["roles"].append(
{
"class": role
}
)
for feature in features:
sub_thing["features"].append(
{
"class": feature
}
)
return sub_thing
def make_config_file(dt_id, name, roles, features=[]):
config_file = {
"thingId": dt_id,
......@@ -304,20 +329,32 @@ def make_config_file(dt_id, name, roles, features=[]):
"class": "ml40::Thing",
"name": name,
"roles": [
{
"class": roles
}
],
"features": [
]
}
}
for feature in features:
config_file["attributes"]["features"].append(
for role in roles:
config_file["attributes"]["roles"].append(
{
"class": feature
"class": role
}
)
for feature in features:
if isinstance(feature, str):
config_file["attributes"]["features"].append(
{
"class": feature
}
)
elif isinstance(feature, dict):
for key, value in feature.items():
config_file["attributes"]["features"].append(
{
"class": key,
"targets": value
}
)
cwd = os.getcwd()
path = os.path.join(cwd, "configs", "config_{}.json".format(name))
with open(path, 'wb') as file:
......@@ -381,5 +418,3 @@ def get_requests(config):
receiver_uuids.append(target)
requests.append((msg_type, receiver_uuids, parameters))
return requests
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