Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
KWH40
fml40-reference-implementation
Commits
0d09d73a
Commit
0d09d73a
authored
Nov 23, 2020
by
GromeTT
Committed by
GromeTT
Dec 02, 2020
Browse files
ENH: Added partial documentation to thing.py
parent
380f53f8
Changes
2
Hide whitespace changes
Inline
Side-by-side
docs/source/index.rst
View file @
0d09d73a
...
...
@@ -14,6 +14,7 @@ Table of Contents
:caption: Contents:
md/preliminaries.md
thing
dt_factory
tools
...
...
ml/thing.py
View file @
0d09d73a
...
...
@@ -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
(
...
...
@@ -244,7 +354,7 @@ def __on_broker_callback(self, ch, method, properties, body):
body
=
json
.
loads
(
body_str
)
except
ValueError
:
pass
elif
isinstance
(
body
,
int
):
return
elif
isinstance
(
body
,
str
):
...
...
@@ -359,7 +469,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
)
...
...
@@ -422,18 +534,9 @@ def on_service_request(self, body_json):
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
)
)
service_reply
.
fillServiceReply
(
senderUUID
=
self
.
thing_id
,
receiverUUID
=
body_json
.
get
(
"sender"
,
None
),
serviceType
=
body_json
.
get
(
"serviceType"
,
None
),
results
=
{
"error"
:
"invalid functionalities (serviceType) {}"
.
format
(
service_functionality
)},
replyingToUUID
=
body_json
.
get
(
"identifier"
,
None
),
msgUUID
=
"s3i:{}"
.
format
(
uuid
.
uuid4
())
)
else
:
# TODO: Call right functionality.
try
:
...
...
@@ -515,8 +618,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
):
...
...
@@ -535,8 +639,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
):
...
...
@@ -550,34 +655,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
...
...
@@ -593,7 +694,7 @@ def to_json(self):
"attributes"
:
{
"class"
:
"ml40::Thing"
,
"name"
:
self
.
name
,
}
}
,
}
if
self
.
roles
:
self
.
dt_json
[
"attributes"
][
"roles"
]
=
list
()
...
...
@@ -611,7 +712,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
())
...
...
@@ -620,25 +721,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
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment