Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
KWH40
fml40-reference-implementation
Commits
f906a51d
Commit
f906a51d
authored
Oct 02, 2020
by
Jiahang Chen
Browse files
ADD Notebooks for workshop
parent
b286ffd2
Pipeline
#338072
passed with stage
in 14 seconds
Changes
10
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
.gitignore
View file @
f906a51d
...
...
@@ -18,4 +18,208 @@ logs
credentials/*.json
credentials/*.txt
!./credentials/collection.txt.gpg
\ No newline at end of file
!./credentials/collection.txt.gpg
# Created by https://www.gitignore.io/api/python,pycharm+all
# Edit at https://www.gitignore.io/?templates=python,pycharm+all
### PyCharm+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### PyCharm+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
.idea/
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# End of https://www.gitignore.io/api/python,pycharm+all
# don't commit actual config file
config.py
# venv
venv/*
\ No newline at end of file
configs/config_dt.json
0 → 100644
View file @
f906a51d
{
"thingId"
:
"s3i:bea74f50-fa6c-4aba-b7f8-90153b401ffc"
,
"policyId"
:
"s3i:bea74f50-fa6c-4aba-b7f8-90153b401ffc"
,
"attributes"
:
{
"class"
:
"ml40::Thing"
,
"name"
:
"created DT Harvester for workshop"
,
"roles"
:
[
{
"class"
:
"fml40::Harvester"
}
],
"features"
:
[
{
"class"
:
"fml40::ProvidesProductionData"
},
{
"class"
:
"fml40::AcceptsFellingJobs"
},
{
"class"
:
"ml40::ManagesJobs"
}
]
}
}
\ No newline at end of file
dt_creation.py
0 → 100644
View file @
f906a51d
import
s3i
import
json
import
jwt
from
ml.tools
import
load_config
,
get_s3i_broker
,
get_receiver_callback_func
from
ml.dt_factory
import
create_dt_ref
from
ml.fml40.features.functionalities.accepts_felling_jobs
import
AcceptsFellingJobs
dt_creation_app_id
=
"s3i:a44bd6dc-c607-4574-9b16-cf8579819356"
dt_creation_app_secret
=
"970ebe57-7dfb-4090-8c38-4b44111d5288"
print
(
"KWH application to create a dt, please log in!"
)
#username = input('[S3I]: Please enter your username:').strip('," ')
#password = input('[S3I]: Please enter your password:')
username
=
"KWH-Team"
password
=
"V4yuJ31izh0GCH36954O"
print
(
"Your credentials are sent to S3I IdentityProvider."
)
s3i_identity_provider
=
s3i
.
IdentityProvider
(
grant_type
=
'password'
,
identity_provider_url
=
"https://idp.s3i.vswf.dev/"
,
realm
=
'KWH'
,
client_id
=
dt_creation_app_id
,
client_secret
=
dt_creation_app_secret
,
username
=
username
,
password
=
password
)
access_token
=
s3i_identity_provider
.
get_token
(
s3i
.
TokenType
.
ACCESS_TOKEN
)
''' decode the access token'''
parsed_username
=
jwt
.
decode
(
access_token
,
verify
=
False
)[
"preferred_username"
]
print
(
"Token received, "
+
parsed_username
+
" logged in."
)
### create identity of digital twin
dt_secret
=
"c49e4050-d194-4f08-90c5-e17fd5befba5"
dt_model
=
load_config
(
'configs/config_dt.json'
)
dt_ref
=
create_dt_ref
(
model
=
dt_model
,
grant_type
=
"password"
,
secret
=
dt_secret
,
username
=
username
,
password
=
password
,
is_broker
=
True
,
is_repo
=
False
)
dt_proxy
=
dt_ref
.
proxy
()
dt_proxy
.
run_forever
()
class
AcceptsFellingJobsImpl
(
AcceptsFellingJobs
):
def
acceptJob
(
self
,
job
):
print
(
"i have accepted a job {}"
.
format
(
job
))
return
True
dt_proxy
.
add_function_impl
(
AcceptsFellingJobsImpl
,
"AcceptsFellingJobs"
)
dt_creation_hmi.py
0 → 100644
View file @
f906a51d
import
s3i
import
jwt
import
uuid
username
=
"chen"
password
=
"8810515"
client_id
=
"s3i:b4f72a11-fb3d-47a3-b499-e7a88eaa5fe1"
client_secret
=
"84df6340-9d64-4ae6-9076-86c224ec484a"
s3i_identity_provider
=
s3i
.
IdentityProvider
(
grant_type
=
'password'
,
identity_provider_url
=
"https://idp.s3i.vswf.dev/"
,
realm
=
'KWH'
,
client_id
=
client_id
,
client_secret
=
client_secret
,
username
=
username
,
password
=
password
)
access_token
=
s3i_identity_provider
.
get_token
(
s3i
.
TokenType
.
ACCESS_TOKEN
)
''' decode the access token'''
parsed_username
=
jwt
.
decode
(
access_token
,
verify
=
False
)[
"preferred_username"
]
print
(
"Token received, "
+
parsed_username
+
" logged in."
)
s3i_dir
=
s3i
.
Directory
(
s3i_dir_url
=
"https://dir.s3i.vswf.dev/api/2/"
,
token
=
access_token
)
hmi_json
=
s3i_dir
.
queryThingIDBased
(
thingID
=
client_id
)
hmi_endpoints
=
hmi_json
[
"attributes"
].
get
(
"allEndpoints"
,
None
)
hmi_s3ib_ep
=
""
for
hmi_ep
in
hmi_endpoints
:
if
"s3ib"
in
hmi_ep
:
hmi_s3ib_ep
=
hmi_ep
serv_req
=
s3i
.
messages
.
ServiceRequest
()
serv_req
.
fillServiceRequest
(
senderUUID
=
client_id
,
receiverUUID
=
[
"s3i:bea74f50-fa6c-4aba-b7f8-90153b401ffc"
],
sender_endpoint
=
hmi_s3ib_ep
,
serviceType
=
"AcceptsFellingJob/acceptJob"
,
parameters
=
{
"job"
:
"fml40::FellingJob"
},
msgUUID
=
str
(
uuid
.
uuid4
())
)
my_dt_json
=
s3i_dir
.
queryThingIDBased
(
thingID
=
"s3i:bea74f50-fa6c-4aba-b7f8-90153b401ffc"
)
dt_endpoints
=
my_dt_json
[
"attributes"
].
get
(
"allEndpoints"
,
None
)
dt_ep
=
""
for
temp
in
dt_endpoints
:
if
"s3ib"
in
temp
:
dt_ep
=
temp
s3i_broker
=
s3i
.
Broker
(
auth_form
=
"Username/Password"
,
username
=
" "
,
password
=
access_token
,
host
=
"rabbitmq.s3i.vswf.dev"
)
s3i_broker
.
send
([
dt_ep
],
serv_req
.
msg
.
__str__
())
ml/__init__.py
0 → 100644
View file @
f906a51d
ml/dt_factory.py
View file @
f906a51d
""" Implements a factory for managing digital twins."""
import
sys
import
sys
,
json
from
s3i
import
IdentityProvider
from
s3i
import
TokenType
from
ml.tools
import
get_idp
...
...
@@ -13,7 +13,7 @@
from
customer_code_example.forwarder_komatsu
import
komatsu
from
customer_code_example.mini_tractor_driver
import
MiniTractorDriver
from
ml.ml40.roles.servi
ce
.service
import
Service
from
ml.ml40.roles.servi
ves
.service
import
Service
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,28 +47,29 @@
)
# TODO: Get rid of this global variable
# TODO: automatically get all classes in modul
DT_FACTORY
=
{}
DT_FACTORY
[
"Service"
]
=
Service
DT_FACTORY
[
"
ml40::
Thing"
]
=
Thing
DT_FACTORY
[
"
ml40::
ManagesJobs"
]
=
ManagesJobs
DT_FACTORY
[
"
ml40::
Location"
]
=
Location
DT_FACTORY
[
"
ml40::
Composite"
]
=
Composite
DT_FACTORY
[
"
ml40::
Shared"
]
=
Shared
DT_FACTORY
[
"
ml40::
Weight"
]
=
Weight
DT_FACTORY
[
"
ml40::
Moisture"
]
=
Moisture
DT_FACTORY
[
"
fml40::
AcceptsProximityAlert"
]
=
AcceptsProximityAlert
DT_FACTORY
[
"
fml40::
AcceptsFellingJobs"
]
=
AcceptsFellingJobs
DT_FACTORY
[
"
fml40::
ProvidesProductionData"
]
=
ProvidesProductionData
DT_FACTORY
[
"
fml40::
Harvests"
]
=
Harvests
DT_FACTORY
[
"
fml40::
Forwards"
]
=
Forwards
DT_FACTORY
[
"
fml40::
AcceptsForwardingJobs"
]
=
AcceptsForwardingJobs
DT_FACTORY
[
"
fml40::
ProvidesPassabilityInformation"
]
=
ProvidesPassabilityInformation
DT_FACTORY
[
"
fml40::
ProvidesMoisturePrediction"
]
=
ProvidesMoisturePrediction
DT_FACTORY
[
"
fml40::
MoisturePredictionReport"
]
=
MoisturePredictionReport
DT_FACTORY
[
"
fml40::
AcceptsPassabilityReport"
]
=
AcceptsPassabilityReport
DT_FACTORY
[
"Thing"
]
=
Thing
DT_FACTORY
[
"ManagesJobs"
]
=
ManagesJobs
DT_FACTORY
[
"Location"
]
=
Location
DT_FACTORY
[
"Composite"
]
=
Composite
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
def
get_dt_names
():
...
...
@@ -106,6 +107,8 @@ def create_dt_with_idp(config, id_p):
# d_t = DT_FACTORY.get(dt_type, ManagingActor)(id_p, config)
# return d_t
###
#TODO: was passiert, wenn es unter subfeature noch ein subsubfeature gibt?
def
build_sub_featrues
(
thing_ref
,
feature_proxy
,
json_feature
):
json_sub_features
=
json_feature
.
get
(
"subFeatures"
,
[])
...
...
@@ -126,6 +129,13 @@ def build_sub_featrues(thing_ref, feature_proxy, json_feature):
def
build
(
thing_ref
,
attributes
):
thing_proxy
=
thing_ref
.
proxy
()
roles
=
attributes
.
get
(
"roles"
,
[])
if
not
roles
:
APP_LOGGER
.
critical
(
"Roles: %s is missing"
%
roles
)
else
:
APP_LOGGER
.
debug
(
"Adding roles: %s"
%
roles
)
thing_proxy
.
roles
=
roles
json_features
=
attributes
.
get
(
"features"
,
[])
for
json_feature
in
json_features
:
class_name
=
json_feature
.
get
(
"class"
,
""
)
...
...
@@ -153,17 +163,22 @@ def create_dt_ref(model, grant_type, secret, username, password, is_broker, is_r
:rtype: tuple(Reference, str)
"""
# id_p = get_idp(grant_type, thing_id, secret, username, password)
attributes
=
model
.
get
(
"attributes"
,
None
)
if
attributes
is
None
:
prin
t
(
"Incomplete model: attributes missing!"
)
sys
.
exit
(
)
sys
.
exi
t
(
"Incomplete model: attributes missing!"
)
attributes
=
str
(
attributes
).
replace
(
"fml40::"
,
""
).
replace
(
"ml40::"
,
""
).
replace
(
"'"
,
'"'
)
attributes
=
json
.
loads
(
attributes
)
thing_type
=
attributes
.
get
(
"class"
,
""
)
if
thing_type
==
""
:
print
(
"Unknown type %s"
%
thing_type
)
sys
.
exit
()
roles
=
attributes
.
get
(
"roles"
,
None
)
if
roles
is
None
:
sys
.
exit
(
"Incomplete model: roles missing!"
)
thing_name
=
attributes
.
get
(
"name"
,
""
)
APP_LOGGER
.
debug
(
"Creating ditigtal twin %s"
%
thing_name
)
d_t
=
DT_FACTORY
.
get
(
thing_type
,
ManagingActor
)
...
...
@@ -174,7 +189,8 @@ def create_dt_ref(model, grant_type, secret, username, password, is_broker, is_r
username
=
username
,
password
=
password
,
is_broker
=
is_broker
,
is_repo
=
is_repo
,
is_repo
=
is_repo
)
build
(
thing_ref
,
attributes
)
return
thing_ref
ml/ml40/__init__.py
0 → 100644
View file @
f906a51d
ml/ml40/features/feature.py
View file @
f906a51d
from
ml.managed_actor
import
ManagedActor
from
ml.identifier
import
ID
import
types
class
Feature
(
ManagedActor
):
def
__init__
(
self
,
name
,
ref_managing_actor
):
...
...
ml/ml40/features/functionalities/functionality.py
View file @
f906a51d
...
...
@@ -16,7 +16,6 @@ def __init__(self, name, ref_managing_actor):
def
from_json
(
self
,
json_obj
):
super
().
from_json
(
json_obj
)
def
create_service_reply
(
self
,
json_body
,
results
):
self
.
service_reply
=
ServiceReply
()
thing_proxy
=
self
.
managing_actor
...
...
ml/thing.py
View file @
f906a51d
...
...
@@ -16,10 +16,21 @@
from
ml.tools
import
decode_message
from
ml.tools
import
load_config
from
ml.app_logger
import
APP_LOGGER
from
ml.fml40.features.functionalities.forwards
import
send_passability_request
from
ml.tools
import
create_request
class
bcolors
:
"""colors for the console log"""
HEADER
=
'
\033
[95m'
OKBLUE
=
'
\033
[94m'
OKGREEN
=
'
\033
[92m'
WARNING
=
'
\033
[93m'
FAIL
=
'
\033
[91m'
ENDC
=
'
\033
[0m'
BOLD
=
'
\033
[1m'
UNDERLINE
=
'
\033
[4m'
class
BaseVariable
(
object
):
IDP_URL
=
"https://idp.s3i.vswf.dev/"
IDP_REALM
=
"KWH"
...
...
@@ -37,15 +48,14 @@ def get_sensor_uuid(body_json):
class
Thing
(
ManagingActor
):
def
__init__
(
self
,
client_secret
,
model
:
dict
,
grant_type
:
str
,
is_broker
:
bool
,
is_repo
:
bool
,
username
=
None
,
password
=
None
,
endpoint
=
None
,
self
,
client_secret
,
model
:
dict
,
grant_type
:
str
,
is_broker
:
bool
,
is_repo
:
bool
,
username
=
None
,
password
=
None
,
):
super
(
Thing
,
self
).
__init__
()
self
.
__thing_id
=
model
.
get
(
"thingId"
,
""
)
...
...
@@ -54,32 +64,39 @@ def __init__(
self
.
__username
=
username
self
.
__password
=
password
self
.
__client_secret
=
client_secret
self
.
__all_endpoints
=
model
.
get
(
"allEndpoints"
,
""
)
self
.
__is_broker
=
is_broker
self
.
__is_repo
=
is_repo
self
.
__access_token
=
""
self
.
__endpoint
=
f
"s3ib://
{
self
.
__thing_id
}
"
self
.
__endpoint
=
""
self
.
__ws_connected
=
False
self
.
__observers
=
[]
self
.
_broker
=
None
self
.
_ws
=
None
self
.
roles
=
[]
self
.
features
=
{}
self
.
broker
=
None
self
.
ws
=
None
self
.
dir
=
None
attributes
=
model
.
get
(
"attributes"
,
None
)
self
.
__name
=
""
self
.
__class_name
=
""
self
.
__type_name
=
""
self
.
__represent
s
=
""
self
.
__
thing_struc
ture
=
{}
self
.
__default_hmi
=
""
self
.
role
s
=
[]
self
.
__
fea
ture
s
=
{}
if
attributes
:
self
.
__name
=
attributes
.
get
(
"name"
,
""
)
self
.
__class_name
=
attributes
.
get
(
"class"
,
""
)
self
.
__type_name
=
attributes
.
get
(
"type"
,
""
)
self
.
__represents
=
attributes
.
get
(
"represents"
,
""
)
self
.
__thing_structure
=
attributes
.
get
(
"thingStructure"
,
{})
self
.
__default_hmi
=
attributes
.
get
(
"default_hmi"
,
""
)
@
property
def
features
(
self
):
return
self
.
__features
@
features
.
setter
def
features
(
self
,
value
):
self
.
__features
=
value
def
send_message
(
self
,
receiver_ids
,
req
):
send_message
(
self
.
__access_token
,
receiver_ids
,
req
)
...
...
@@ -94,11 +111,13 @@ def send_service_message(self, receiver_ids, service_class_name, parameters):
parameters
,
)
"""
def on_receive(self, msg, decode=True):
# TODO: Invent a different mechanism for this. Works for now!
# APP_LOGGER.info(f"Receiving message: {message_type}")
body_str = decode_message(msg, decode)
body_json = json.loads(body_str)
print(body_json)
message_type = body_json.get("messageType", "")
if message_type == "serviceRequest":
self.on_service_request(body_json)
...
...
@@ -108,12 +127,7 @@ def on_receive(self, msg, decode=True):
self.on_process_get_value_reply(body_json)
elif message_type == "serviceReply":
self.on_process_service_reply(body_json)
def
on_process_service_reply
(
self
,
msg
):
pass
def
on_process_get_value_reply
(
self
,
body_json
):
self
.
handle_humidity_get_value_request
(
body_json
)
"""
@
property
def
class_name
(
self
):
...
...
@@ -139,26 +153,15 @@ def name(self):
def
thing_id
(
self
):
return
self
.
__thing_id
def
attach
(
self
,
observer
):
if
observer
not
in
self
.
__observers
:
self
.
__observers
.
append
(
observer
)
def
detach
(
self
,
observer
):
if
observer
in
self
.
_observers
:
self
.
__observers
.
remove
(
observer
)
def
notify
(
self
,
path
,
modifier
=
None
):
for
observer
in
self
.
__observers
:
if
modifier
!=
observer
:
observer
.
update
(
self
,
path
)
def
run_forever
(
self
):
print
(
"[S³I]: Launch {}{}{}"
.
format
(
bcolors
.
OKGREEN
,
self
.
name
,
bcolors
.
ENDC
))
self
.
__connect_with_idp
()
def
__on_key_pressed
(
self
,
key
):
pass
def
__connect_with_idp
(
self
):
print
(
bcolors
.
OKBLUE
+
"[S³I][IdP]"
+
bcolors
.
ENDC
+
": Connect with S3I-IdentityProvider"
)