From 3506e290f095e83c0866a857e3e9e9ea0f00cec2 Mon Sep 17 00:00:00 2001 From: "C. Albrecht" Date: Thu, 20 Aug 2020 16:46:24 +0200 Subject: [PATCH 01/84] WIP --- .gitignore | 9 +- customer_code_example/mini_tractor_driver.py | 12 +- fml40s.py | 101 +++-- ml/app_logger.py | 33 ++ ml/authentication.py | 110 ++++++ ml/dt_factory.py | 180 +++++++++ ml/fml40/__init__.py | 0 ml/fml40/features/__init__.py | 0 ml/fml40/features/functionalities/__init__.py | 0 .../functionalities/accepts_felling_jobs.py | 21 ++ .../accepts_felling_support_jobs.py | 8 + .../accepts_forwarding_jobs.py | 21 ++ .../accepts_log_measurements.py | 8 + .../accepts_log_transportaition_jobs.py | 8 + .../accepts_moisture_measurement.py | 7 + .../functionalities/accepts_move_commands.py | 13 + .../accepts_passability_report.py | 16 + .../accepts_proximity_alert.py | 17 + .../accepts_single_tree_felling_jobs.py | 8 + .../classifies_tree_species.py | 6 + ml/fml40/features/functionalities/cuts.py | 6 + .../functionalities/determines_passability.py | 17 + .../functionalities/displays_health_alarms.py | 7 + .../evaluates_stand_attributes.py | 10 + ml/fml40/features/functionalities/fells.py | 16 + .../forest_planning_evaluation.py | 6 + ml/fml40/features/functionalities/forwards.py | 21 ++ .../generates_afforestation_suggestions.py | 8 + .../generates_felling_suggestions.py | 9 + ml/fml40/features/functionalities/grabs.py | 12 + ml/fml40/features/functionalities/harvests.py | 19 + .../features/functionalities/measure_wood.py | 10 + .../functionalities/monitor_health_status.py | 7 + .../provides_moisture_prediction.py | 20 + .../provides_passability_information.py | 15 + .../provides_production_data.py | 11 + .../functionalities/provides_tree_data.py | 13 + .../functionalities/provides_weather_data.py | 5 + .../functionalities/simulates_tree_growth.py | 5 + .../functionalities/supports_felling.py | 9 + .../functionalities/transports_logs.py | 14 + ml/fml40/features/properties/__init__.py | 0 .../features/properties/values/__init__.py | 0 .../properties/values/abstract_inventory.py | 5 + ml/fml40/features/properties/values/dbh.py | 8 + .../properties/values/documents/__init__.py | 0 .../values/documents/jobs/__init__.py | 0 .../values/documents/jobs/felling_job.py | 8 + .../documents/jobs/fellung_support_job.py | 8 + .../values/documents/jobs/forwarding_job.py | 7 + .../documents/jobs/log_transportation_job.py | 9 + .../documents/jobs/safety_felling_job.py | 8 + .../documents/jobs/single_tree_felling_job.py | 10 + .../documents/jobs/transportation_job.py | 8 + .../values/documents/reports/__init__.py | 0 .../reports/afforestation_suggestion.py | 9 + .../reports/felling_method_suggestion.py | 21 ++ .../reports/felling_support_suggestion.py | 9 + .../values/documents/reports/felling_tool.py | 6 + .../documents/reports/log_measurement.py | 9 + .../reports/log_transportation_report.py | 9 + .../values/documents/reports/map_data.py | 9 + .../reports/moisture_prediction_report.py | 13 + .../documents/reports/passability_report.py | 9 + .../documents/reports/production_data.py | 9 + .../reports/soil_moisture_measurement.py | 9 + .../properties/values/inventory_data.py | 15 + .../values/stem_segment_properties.py | 13 + ml/fml40/features/properties/values/tilt.py | 10 + .../features/properties/values/tree_data.py | 5 + .../features/properties/values/tree_type.py | 8 + ml/fml40/machines/forwarder.py | 52 +++ ml/fml40/machines/harvester.py | 32 ++ ml/fml40/roles/__init__.py | 3 + ml/fml40/roles/dts/__init__.py | 7 + .../roles/dts/handheld_devices/__init__.py | 2 + .../roles/dts/handheld_devices/brushcutter.py | 5 + .../roles/dts/handheld_devices/chainsaw.py | 5 + ml/fml40/roles/dts/machines/__init__.py | 7 + ml/fml40/roles/dts/machines/forest_machine.py | 5 + ml/fml40/roles/dts/machines/forwarder.py | 5 + ml/fml40/roles/dts/machines/harvester.py | 5 + ml/fml40/roles/dts/machines/log_truck.py | 4 + ml/fml40/roles/dts/machines/mini_tractor.py | 5 + ml/fml40/roles/dts/machines/skidder.py | 5 + ml/fml40/roles/dts/machines/wheel_loader.py | 5 + ml/fml40/roles/dts/parts/__init__.py | 5 + ml/fml40/roles/dts/parts/grabber.py | 5 + ml/fml40/roles/dts/parts/harvesting_head.py | 5 + ml/fml40/roles/dts/parts/log_loading_area.py | 5 + ml/fml40/roles/dts/parts/saw.py | 5 + ml/fml40/roles/dts/parts/winch.py | 5 + ml/fml40/roles/dts/persons/__init__.py | 4 + ml/fml40/roles/dts/persons/forest_owner.py | 5 + ml/fml40/roles/dts/persons/forest_worker.py | 5 + .../dts/persons/mini_tractor_operator.py | 5 + .../roles/dts/persons/skidder_operator.py | 5 + ml/fml40/roles/dts/sensors/__init__.py | 1 + ml/fml40/roles/dts/sensors/vitality_sensor.py | 5 + ml/fml40/roles/dts/sites/__init__.py | 3 + ml/fml40/roles/dts/sites/forest_enterprise.py | 5 + ml/fml40/roles/dts/sites/hauler.py | 5 + ml/fml40/roles/dts/sites/mill/__init__.py | 3 + ml/fml40/roles/dts/sites/mill/mill.py | 5 + ml/fml40/roles/dts/sites/mill/papermill.py | 5 + ml/fml40/roles/dts/sites/mill/sawmill.py | 5 + ml/fml40/roles/dts/ways/__init__.py | 0 ml/fml40/roles/hmis/__init__.py | 0 ml/fml40/roles/services/__init__.py | 0 ml/fml40_factory.py | 4 + ml/identifier.py | 19 + ml/managed_actor.py | 29 ++ ml/managing_actor.py | 69 ++++ ml/ml40/features/__init__.py | 0 ml/ml40/features/feature.py | 31 ++ ml/ml40/features/functionalities/__init__.py | 0 .../features/functionalities/accepts_jobs.py | 21 ++ .../functionalities/accepts_reports.py | 12 + .../features/functionalities/clears_jobs.py | 12 + .../features/functionalities/functionality.py | 37 ++ .../features/functionalities/manages_jobs.py | 20 + .../features/functionalities/plans_routes.py | 11 + .../functionalities/provides_map_data.py | 11 + .../provides_operational_data.py | 17 + ml/ml40/features/functionalities/renders.py | 10 + ml/ml40/features/properties/__init__.py | 0 ml/ml40/features/properties/association.py | 6 + ml/ml40/features/properties/composite.py | 15 + ml/ml40/features/properties/property.py | 5 + ml/ml40/features/properties/shared.py | 12 + .../features/properties/values/__init__.py | 0 ml/ml40/features/properties/values/address.py | 13 + .../features/properties/values/dimensions.py | 12 + .../properties/values/documents/__init__.py | 0 .../properties/values/documents/document.py | 7 + .../values/documents/jobs/__init__.py | 0 .../values/documents/jobs/generic_job.py | 8 + .../properties/values/documents/jobs/job.py | 9 + .../values/documents/jobs/job_list.py | 8 + .../values/documents/jobs/job_status.py | 7 + .../values/documents/reports/__init__.py | 0 .../values/documents/reports/report.py | 9 + .../properties/values/generic_property.py | 10 + .../properties/values/liquid_filling_level.py | 10 + .../features/properties/values/location.py | 15 + .../features/properties/values/moisture.py | 20 + .../properties/values/personal_name.py | 10 + .../properties/values/rotational_speed.py | 8 + ml/ml40/features/properties/values/route.py | 5 + .../features/properties/values/temperature.py | 23 ++ .../features/properties/values/time_slot.py | 10 + ml/ml40/features/properties/values/value.py | 22 ++ ml/ml40/features/properties/values/weight.py | 18 + ml/ml40/roles/__init__.py | 1 + ml/ml40/roles/dts/__init__.py | 8 + ml/ml40/roles/dts/dt.py | 7 + .../roles/dts/handheld_devices/__init__.py | 1 + .../dts/handheld_devices/handheld_device.py | 7 + ml/ml40/roles/dts/machines/__init__.py | 1 + ml/ml40/roles/dts/machines/machine.py | 6 + ml/ml40/roles/dts/parts/__init__.py | 5 + ml/ml40/roles/dts/parts/crane.py | 6 + ml/ml40/roles/dts/parts/engine.py | 5 + ml/ml40/roles/dts/parts/part.py | 5 + ml/ml40/roles/dts/parts/scale.py | 5 + ml/ml40/roles/dts/parts/tank.py | 5 + ml/ml40/roles/dts/persons/__init__.py | 2 + ml/ml40/roles/dts/persons/machine_operator.py | 5 + ml/ml40/roles/dts/persons/person.py | 5 + ml/ml40/roles/dts/sensors/__init__.py | 4 + ml/ml40/roles/dts/sensors/air_sensor.py | 5 + ml/ml40/roles/dts/sensors/sensor.py | 5 + ml/ml40/roles/dts/sensors/soil_sensor.py | 5 + ml/ml40/roles/dts/sites/__init__.py | 1 + ml/ml40/roles/dts/sites/site.py | 5 + ml/ml40/roles/dts/ways/__init__.py | 1 + ml/ml40/roles/dts/ways/way.py | 5 + ml/ml40/roles/hmis/__init__.py | 5 + ml/ml40/roles/hmis/app.py | 8 + ml/ml40/roles/hmis/dashboard.py | 8 + ml/ml40/roles/hmis/hmd.py | 8 + ml/ml40/roles/hmis/hmi.py | 7 + ml/ml40/roles/hmis/machine_ui.py | 7 + ml/ml40/roles/role.py | 3 + ml/ml40/roles/servives/__init__.py | 1 + ml/ml40/roles/servives/service.py | 6 + ml/thing.py | 283 ++++++++++++++ ml/tools.py | 348 ++++++++++++++++++ requirements.txt | 5 + scripts/edit_thing.py | 77 ++-- scripts/get_token.py | 89 +---- tests/__init__.py | 2 + tests/res/dummy_credentials.json | 4 + tests/res/malformatted.json | 1 + tests/test_digital_twins.py | 226 ++++++++++++ tests/test_get_token.py | 20 + 196 files changed, 2887 insertions(+), 181 deletions(-) create mode 100644 ml/app_logger.py create mode 100644 ml/authentication.py create mode 100644 ml/dt_factory.py create mode 100644 ml/fml40/__init__.py create mode 100644 ml/fml40/features/__init__.py create mode 100644 ml/fml40/features/functionalities/__init__.py create mode 100644 ml/fml40/features/functionalities/accepts_felling_jobs.py create mode 100644 ml/fml40/features/functionalities/accepts_felling_support_jobs.py create mode 100644 ml/fml40/features/functionalities/accepts_forwarding_jobs.py create mode 100644 ml/fml40/features/functionalities/accepts_log_measurements.py create mode 100644 ml/fml40/features/functionalities/accepts_log_transportaition_jobs.py create mode 100644 ml/fml40/features/functionalities/accepts_moisture_measurement.py create mode 100644 ml/fml40/features/functionalities/accepts_move_commands.py create mode 100644 ml/fml40/features/functionalities/accepts_passability_report.py create mode 100644 ml/fml40/features/functionalities/accepts_proximity_alert.py create mode 100644 ml/fml40/features/functionalities/accepts_single_tree_felling_jobs.py create mode 100644 ml/fml40/features/functionalities/classifies_tree_species.py create mode 100644 ml/fml40/features/functionalities/cuts.py create mode 100644 ml/fml40/features/functionalities/determines_passability.py create mode 100644 ml/fml40/features/functionalities/displays_health_alarms.py create mode 100644 ml/fml40/features/functionalities/evaluates_stand_attributes.py create mode 100644 ml/fml40/features/functionalities/fells.py create mode 100644 ml/fml40/features/functionalities/forest_planning_evaluation.py create mode 100644 ml/fml40/features/functionalities/forwards.py create mode 100644 ml/fml40/features/functionalities/generates_afforestation_suggestions.py create mode 100644 ml/fml40/features/functionalities/generates_felling_suggestions.py create mode 100644 ml/fml40/features/functionalities/grabs.py create mode 100644 ml/fml40/features/functionalities/harvests.py create mode 100644 ml/fml40/features/functionalities/measure_wood.py create mode 100644 ml/fml40/features/functionalities/monitor_health_status.py create mode 100644 ml/fml40/features/functionalities/provides_moisture_prediction.py create mode 100644 ml/fml40/features/functionalities/provides_passability_information.py create mode 100644 ml/fml40/features/functionalities/provides_production_data.py create mode 100644 ml/fml40/features/functionalities/provides_tree_data.py create mode 100644 ml/fml40/features/functionalities/provides_weather_data.py create mode 100644 ml/fml40/features/functionalities/simulates_tree_growth.py create mode 100644 ml/fml40/features/functionalities/supports_felling.py create mode 100644 ml/fml40/features/functionalities/transports_logs.py create mode 100644 ml/fml40/features/properties/__init__.py create mode 100644 ml/fml40/features/properties/values/__init__.py create mode 100644 ml/fml40/features/properties/values/abstract_inventory.py create mode 100644 ml/fml40/features/properties/values/dbh.py create mode 100644 ml/fml40/features/properties/values/documents/__init__.py create mode 100644 ml/fml40/features/properties/values/documents/jobs/__init__.py create mode 100644 ml/fml40/features/properties/values/documents/jobs/felling_job.py create mode 100644 ml/fml40/features/properties/values/documents/jobs/fellung_support_job.py create mode 100644 ml/fml40/features/properties/values/documents/jobs/forwarding_job.py create mode 100644 ml/fml40/features/properties/values/documents/jobs/log_transportation_job.py create mode 100644 ml/fml40/features/properties/values/documents/jobs/safety_felling_job.py create mode 100644 ml/fml40/features/properties/values/documents/jobs/single_tree_felling_job.py create mode 100644 ml/fml40/features/properties/values/documents/jobs/transportation_job.py create mode 100644 ml/fml40/features/properties/values/documents/reports/__init__.py create mode 100644 ml/fml40/features/properties/values/documents/reports/afforestation_suggestion.py create mode 100644 ml/fml40/features/properties/values/documents/reports/felling_method_suggestion.py create mode 100644 ml/fml40/features/properties/values/documents/reports/felling_support_suggestion.py create mode 100644 ml/fml40/features/properties/values/documents/reports/felling_tool.py create mode 100644 ml/fml40/features/properties/values/documents/reports/log_measurement.py create mode 100644 ml/fml40/features/properties/values/documents/reports/log_transportation_report.py create mode 100644 ml/fml40/features/properties/values/documents/reports/map_data.py create mode 100644 ml/fml40/features/properties/values/documents/reports/moisture_prediction_report.py create mode 100644 ml/fml40/features/properties/values/documents/reports/passability_report.py create mode 100644 ml/fml40/features/properties/values/documents/reports/production_data.py create mode 100644 ml/fml40/features/properties/values/documents/reports/soil_moisture_measurement.py create mode 100644 ml/fml40/features/properties/values/inventory_data.py create mode 100644 ml/fml40/features/properties/values/stem_segment_properties.py create mode 100644 ml/fml40/features/properties/values/tilt.py create mode 100644 ml/fml40/features/properties/values/tree_data.py create mode 100644 ml/fml40/features/properties/values/tree_type.py create mode 100644 ml/fml40/machines/forwarder.py create mode 100644 ml/fml40/machines/harvester.py create mode 100644 ml/fml40/roles/__init__.py create mode 100644 ml/fml40/roles/dts/__init__.py create mode 100644 ml/fml40/roles/dts/handheld_devices/__init__.py create mode 100644 ml/fml40/roles/dts/handheld_devices/brushcutter.py create mode 100644 ml/fml40/roles/dts/handheld_devices/chainsaw.py create mode 100644 ml/fml40/roles/dts/machines/__init__.py create mode 100644 ml/fml40/roles/dts/machines/forest_machine.py create mode 100644 ml/fml40/roles/dts/machines/forwarder.py create mode 100644 ml/fml40/roles/dts/machines/harvester.py create mode 100644 ml/fml40/roles/dts/machines/log_truck.py create mode 100644 ml/fml40/roles/dts/machines/mini_tractor.py create mode 100644 ml/fml40/roles/dts/machines/skidder.py create mode 100644 ml/fml40/roles/dts/machines/wheel_loader.py create mode 100644 ml/fml40/roles/dts/parts/__init__.py create mode 100644 ml/fml40/roles/dts/parts/grabber.py create mode 100644 ml/fml40/roles/dts/parts/harvesting_head.py create mode 100644 ml/fml40/roles/dts/parts/log_loading_area.py create mode 100644 ml/fml40/roles/dts/parts/saw.py create mode 100644 ml/fml40/roles/dts/parts/winch.py create mode 100644 ml/fml40/roles/dts/persons/__init__.py create mode 100644 ml/fml40/roles/dts/persons/forest_owner.py create mode 100644 ml/fml40/roles/dts/persons/forest_worker.py create mode 100644 ml/fml40/roles/dts/persons/mini_tractor_operator.py create mode 100644 ml/fml40/roles/dts/persons/skidder_operator.py create mode 100644 ml/fml40/roles/dts/sensors/__init__.py create mode 100644 ml/fml40/roles/dts/sensors/vitality_sensor.py create mode 100644 ml/fml40/roles/dts/sites/__init__.py create mode 100644 ml/fml40/roles/dts/sites/forest_enterprise.py create mode 100644 ml/fml40/roles/dts/sites/hauler.py create mode 100644 ml/fml40/roles/dts/sites/mill/__init__.py create mode 100644 ml/fml40/roles/dts/sites/mill/mill.py create mode 100644 ml/fml40/roles/dts/sites/mill/papermill.py create mode 100644 ml/fml40/roles/dts/sites/mill/sawmill.py create mode 100644 ml/fml40/roles/dts/ways/__init__.py create mode 100644 ml/fml40/roles/hmis/__init__.py create mode 100644 ml/fml40/roles/services/__init__.py create mode 100644 ml/fml40_factory.py create mode 100644 ml/identifier.py create mode 100644 ml/managed_actor.py create mode 100644 ml/managing_actor.py create mode 100644 ml/ml40/features/__init__.py create mode 100644 ml/ml40/features/feature.py create mode 100644 ml/ml40/features/functionalities/__init__.py create mode 100644 ml/ml40/features/functionalities/accepts_jobs.py create mode 100644 ml/ml40/features/functionalities/accepts_reports.py create mode 100644 ml/ml40/features/functionalities/clears_jobs.py create mode 100644 ml/ml40/features/functionalities/functionality.py create mode 100644 ml/ml40/features/functionalities/manages_jobs.py create mode 100644 ml/ml40/features/functionalities/plans_routes.py create mode 100644 ml/ml40/features/functionalities/provides_map_data.py create mode 100644 ml/ml40/features/functionalities/provides_operational_data.py create mode 100644 ml/ml40/features/functionalities/renders.py create mode 100644 ml/ml40/features/properties/__init__.py create mode 100644 ml/ml40/features/properties/association.py create mode 100644 ml/ml40/features/properties/composite.py create mode 100644 ml/ml40/features/properties/property.py create mode 100644 ml/ml40/features/properties/shared.py create mode 100644 ml/ml40/features/properties/values/__init__.py create mode 100644 ml/ml40/features/properties/values/address.py create mode 100644 ml/ml40/features/properties/values/dimensions.py create mode 100644 ml/ml40/features/properties/values/documents/__init__.py create mode 100644 ml/ml40/features/properties/values/documents/document.py create mode 100644 ml/ml40/features/properties/values/documents/jobs/__init__.py create mode 100644 ml/ml40/features/properties/values/documents/jobs/generic_job.py create mode 100644 ml/ml40/features/properties/values/documents/jobs/job.py create mode 100644 ml/ml40/features/properties/values/documents/jobs/job_list.py create mode 100644 ml/ml40/features/properties/values/documents/jobs/job_status.py create mode 100644 ml/ml40/features/properties/values/documents/reports/__init__.py create mode 100644 ml/ml40/features/properties/values/documents/reports/report.py create mode 100644 ml/ml40/features/properties/values/generic_property.py create mode 100644 ml/ml40/features/properties/values/liquid_filling_level.py create mode 100644 ml/ml40/features/properties/values/location.py create mode 100644 ml/ml40/features/properties/values/moisture.py create mode 100644 ml/ml40/features/properties/values/personal_name.py create mode 100644 ml/ml40/features/properties/values/rotational_speed.py create mode 100644 ml/ml40/features/properties/values/route.py create mode 100644 ml/ml40/features/properties/values/temperature.py create mode 100644 ml/ml40/features/properties/values/time_slot.py create mode 100644 ml/ml40/features/properties/values/value.py create mode 100644 ml/ml40/features/properties/values/weight.py create mode 100644 ml/ml40/roles/__init__.py create mode 100644 ml/ml40/roles/dts/__init__.py create mode 100644 ml/ml40/roles/dts/dt.py create mode 100644 ml/ml40/roles/dts/handheld_devices/__init__.py create mode 100644 ml/ml40/roles/dts/handheld_devices/handheld_device.py create mode 100644 ml/ml40/roles/dts/machines/__init__.py create mode 100644 ml/ml40/roles/dts/machines/machine.py create mode 100644 ml/ml40/roles/dts/parts/__init__.py create mode 100644 ml/ml40/roles/dts/parts/crane.py create mode 100644 ml/ml40/roles/dts/parts/engine.py create mode 100644 ml/ml40/roles/dts/parts/part.py create mode 100644 ml/ml40/roles/dts/parts/scale.py create mode 100644 ml/ml40/roles/dts/parts/tank.py create mode 100644 ml/ml40/roles/dts/persons/__init__.py create mode 100644 ml/ml40/roles/dts/persons/machine_operator.py create mode 100644 ml/ml40/roles/dts/persons/person.py create mode 100644 ml/ml40/roles/dts/sensors/__init__.py create mode 100644 ml/ml40/roles/dts/sensors/air_sensor.py create mode 100644 ml/ml40/roles/dts/sensors/sensor.py create mode 100644 ml/ml40/roles/dts/sensors/soil_sensor.py create mode 100644 ml/ml40/roles/dts/sites/__init__.py create mode 100644 ml/ml40/roles/dts/sites/site.py create mode 100644 ml/ml40/roles/dts/ways/__init__.py create mode 100644 ml/ml40/roles/dts/ways/way.py create mode 100644 ml/ml40/roles/hmis/__init__.py create mode 100644 ml/ml40/roles/hmis/app.py create mode 100644 ml/ml40/roles/hmis/dashboard.py create mode 100644 ml/ml40/roles/hmis/hmd.py create mode 100644 ml/ml40/roles/hmis/hmi.py create mode 100644 ml/ml40/roles/hmis/machine_ui.py create mode 100644 ml/ml40/roles/role.py create mode 100644 ml/ml40/roles/servives/__init__.py create mode 100644 ml/ml40/roles/servives/service.py create mode 100644 ml/thing.py create mode 100644 ml/tools.py create mode 100644 tests/__init__.py create mode 100644 tests/res/dummy_credentials.json create mode 100644 tests/res/malformatted.json create mode 100644 tests/test_digital_twins.py create mode 100644 tests/test_get_token.py diff --git a/.gitignore b/.gitignore index 8a90a36..15cca76 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +build +dist +*mypy_cache **/__pycache__/ *.py[cod] .vscode @@ -8,9 +11,11 @@ logs .python-version *.org *.sh -/configs/ !/configs/config_forest.json !/configs/config_forwarder_komatsu.json !/configs/config_harvester_john_deere.json !/configs/config_harvester_ponsse.json -scripts/credentials \ No newline at end of file + +credentials/*.json +credentials/*.txt +!./credentials/collection.txt.gpg \ No newline at end of file diff --git a/customer_code_example/mini_tractor_driver.py b/customer_code_example/mini_tractor_driver.py index 2ddab0e..9b4861f 100644 --- a/customer_code_example/mini_tractor_driver.py +++ b/customer_code_example/mini_tractor_driver.py @@ -1,14 +1,14 @@ """Implementation of the digital twin mini tractor driver""" import json from s3i import TokenType -from fml40.app_logger import APP_LOGGER -from fml40.tools import send_request -from fml40.tools import decode_message -from fml40.tools import verify_message -from fml40.worker import Worker +from ml.app_logger import APP_LOGGER +from ml.tools import send_request +from ml.tools import decode_message +from ml.tools import verify_message +from ml.thing import Thing -class MiniTractorDriver(Worker): +class MiniTractorDriver(Thing): def __init__(self, idp, config): APP_LOGGER.info("Hello I'm the mini tractor driver.") super().__init__(idp, config) diff --git a/fml40s.py b/fml40s.py index 5cd4701..70ca7fd 100755 --- a/fml40s.py +++ b/fml40s.py @@ -7,27 +7,27 @@ import argparse import json -from fml40.dt_factory import create_dt_ref -from fml40.dt_factory import get_dt_names -from fml40.tools import send_requests -from fml40.tools import get_requests -from fml40.tools import load_config -from fml40.tools import get_receiver_callback_func -from fml40.tools import get_s3i_broker -from fml40.tools import GRANT_TYPES -from fml40.functionalities.functionalities import get_functionality_names -from fml40.dt_factory import DT_FACTORY -from fml40.app_logger import setup_logger -from customer_code_example.harvester_john_deere import john_deere -from customer_code_example.harvester_ponsse import ponsse -from customer_code_example.forwarder_komatsu import komatsu -from customer_code_example.mini_tractor_driver import MiniTractorDriver - - -DT_FACTORY["JohnDeere"] = john_deere.JohnDeere -DT_FACTORY["Komatsu"] = komatsu.Komatsu -DT_FACTORY["Ponsse"] = ponsse.Ponsse -DT_FACTORY["MiniTractorDriver"] = MiniTractorDriver +import threading +import time +from s3i import TokenType +from s3i import IdentityProvider +from s3i import Broker +from ml.dt_factory import create_dt_ref +from ml.dt_factory import get_dt_names +from ml.tools import create_request +from ml.tools import get_requests +from ml.tools import load_config +from ml.tools import get_receiver_callback_func +from ml.tools import get_s3i_broker +from ml.tools import GRANT_TYPES +from ml.dt_factory import DT_FACTORY +from ml.app_logger import setup_logger +from ml.authentication import get_credentials_dispatch +from ml.authentication import configure_credentials_parser +from ml.tools import send_requests +from ml.tools import send_request +from ml.tools import send_message +from ml.fml40.features.functionalities.forwards import send_moisture_get_value_requests def create_argparser(): @@ -42,29 +42,25 @@ def create_argparser(): subparsers = parent_parser.add_subparsers(dest="command") launch_parser = subparsers.add_parser("launch") launch_parser.add_argument("dt_config_fp", nargs=1, type=str) - launch_parser.add_argument("-s", "--singleshot", action="store_true") - launch_parser.add_argument("-u", default="", help="Username") - launch_parser.add_argument("-p", default="", help="Password") - launch_parser.add_argument( - "-g", choices=GRANT_TYPES, default=GRANT_TYPES[0], help="grant type" - ) + launch_parser.add_argument("--f", + default=None, + help="Filepath to credentials in json format.") + configure_credentials_parser(launch_parser) list_parser = subparsers.add_parser("list") - list_parser.add_argument("class_type", nargs=1, choices=["functionalities", "dts"]) + list_parser.add_argument("class_type", + nargs=1, + choices=["functionalities", "dts"]) return parent_parser def print_digital_twins(dt_names): - print( - "The following digital twins are " - "available:\n\n\t{}".format("\n\t".join(dt_names)) - ) + print("The following digital twins are " + "available:\n\n\t{}".format("\n\t".join(dt_names))) def print_functionalities(func_names): - print( - "The following functionalities are available:" - "\n\n\t{}".format("\n\t".join(func_names)) - ) + print("The following functionalities are available:" + "\n\n\t{}".format("\n\t".join(func_names))) def main(): @@ -77,30 +73,19 @@ def main(): if args.class_type[0] == "dts": dt_names = get_dt_names() print_digital_twins(dt_names) - elif args.class_type[0] == "functionalities": - func_names = get_functionality_names() - print_functionalities(func_names) elif args.command == "launch": dt_config_fp = args.dt_config_fp[0] - config = load_config(dt_config_fp) - dt_name = config["type"] - sender_id = config["thingId"] - app_logger = setup_logger(dt_name) - app_logger.debug("Parsed config: %s", json.dumps(config, indent=2)) - dt_ref = create_dt_ref(config, args.g, args.u, args.p) - if dt_ref is None: - print(f"Thing called {dt_name} is not available.") - - if args.singleshot: - app_logger.debug("Running in singleshot mode") - requests = get_requests(config) - send_requests(dt_ref, requests) - dt_ref.stop() - else: - app_logger.debug("Running in persistent mode") - s3i_broker = get_s3i_broker(dt_ref) - callback_p = get_receiver_callback_func(dt_ref) - s3i_broker.receive("s3ib://{}".format(sender_id), callback_p) + model = load_config(dt_config_fp) + app_logger = setup_logger(__name__) + app_logger.debug("Parsed model: %s", json.dumps(model, indent=2)) + _, secret, username, password = get_credentials_dispatch( + args.g, args.f) + thing_ref = create_dt_ref(model, args.g, secret, username, password, + True, False) + thing_proxy = thing_ref.proxy() + thing_proxy.run_forever() + access_token = thing_proxy.access_token.get() + print(access_token) if __name__ == "__main__": diff --git a/ml/app_logger.py b/ml/app_logger.py new file mode 100644 index 0000000..4704e63 --- /dev/null +++ b/ml/app_logger.py @@ -0,0 +1,33 @@ +import logging +import os + +APP_LOGGER = logging.getLogger("app_logger") + + +def setup_logger(dt_name): + """Creates logger named app_logger. + + Logs are printed to stdout and saved to log files under '/logs'. + Each file is names 'dt_name'.log. + :param dt_name: Name of the digital twin. + :returns: Created logger + :rtype: Logger + + """ + if not os.path.exists("logs"): + os.mkdir("logs") + + app_logger = logging.getLogger('app_logger') + app_logger.setLevel(logging.DEBUG) + log_formatter = logging.Formatter( + "%(asctime)s - %(levelname)s - {}: %(message)s".format(dt_name) + ) + stream_handler = logging.StreamHandler() + stream_handler.setFormatter(log_formatter) + stream_handler.setLevel(logging.DEBUG) + file_handler = logging.FileHandler(filename="./logs/{}.log".format(dt_name)) + file_handler.setLevel(logging.DEBUG) + file_handler.setFormatter(log_formatter) + app_logger.addHandler(file_handler) + app_logger.addHandler(stream_handler) + return app_logger diff --git a/ml/authentication.py b/ml/authentication.py new file mode 100644 index 0000000..7d0b25d --- /dev/null +++ b/ml/authentication.py @@ -0,0 +1,110 @@ +from s3i import IdentityProvider, TokenType +import argparse +import json + +GRANT_TYPES = ("client_credentials", "password") + + +def configure_credentials_parser(parser): + parser.add_argument( + "--g", choices=GRANT_TYPES, default=GRANT_TYPES[0], help="grant type" + ) + parser.add_argument("--d", action="store_false", help="Prompt for client") + parser.add_argument("--s", action="store_true", help="Prompt for scope") + parser.add_argument("--c", default=None, help="Filepath to credentials") + + +def get_username_and_password(filepath=None): + username = None + password = None + if filepath is not None: + with open(filepath, "r") as json_f: + js_object = json.load(json_f) + username = js_object["name"] + password = js_object["password"] + else: + username = input("[S3I]: Please enter your username: ") + # TODO: Use getpass + password = input("[S3I]: Please enter your password: ") + return username, password + + +def get_client_id_and_secret(filepath=None): + client_id = None + client_secret = None + if filepath is not None: + with open(filepath, "r") as json_f: + js_object = json.load(json_f) + client_id = js_object.get("thingId", None) + if not client_id: + client_id = js_object.get("identifier") + + client_secret = js_object.get("client_secret") + if not client_secret: + client_secret = js_object.get("secret") + else: + client_id = input("[S3I]: Please enter client id: ") + # TODO: Use getpass + client_secret = input("[S3I]: Please enter client secret: ") + return client_id, client_secret + +def get_credentials_dispatch(grant_type, filepath=None): + username = "" + password = "" + secret = "" + client_id = "" + if grant_type == GRANT_TYPES[0]: + client_id, secret = get_client_id_and_secret(filepath) + else: + username, password = get_username_and_password(filepath) + return client_id, secret, username, password + +def authorize_client(default_client, with_scope): + client = "admin-cli" + client_secret = "" + scope = "" + if not default_client: + tmp = input( + "[S3I]: Please enter the thing (client) you want to " + "authorize (or leave blank to default to admin client): " + ) + if tmp != "": + client = tmp + client_secret = input( + "[S3I]: Please enter the thing's secret (client secret): " + ) + if with_scope: + scope = input( + "[S3I]: Please enter the client scope which is requested for the " + "access token and has been assigned to the client (or leave blank): " + ) + return client, client_secret, scope + + +def get_access_token(grant_type, client_id, client_secret, username, password, scope): + idp = IdentityProvider( + grant_type=grant_type, + client_id=client_id, + username=username, + password=password, + client_secret=client_secret, + realm="KWH", + identity_provider_url="https://idp.s3i.vswf.dev/", + ) + return idp.get_token(TokenType.ACCESS_TOKEN, scope=scope) + + +def get_access_token_dispatch(file_path, grant_type, ask_client, ask_scope): + access_token = None + if grant_type == GRANT_TYPES[1]: + username, password = get_username_and_password(file_path) + client_id, client_secret, scope = authorize_client(ask_client, ask_scope) + access_token = get_access_token( + grant_type, client_id, client_secret, username, password, scope + ) + else: + client_id, client_secret = get_client_id_and_secret(file_path) + access_token = get_access_token( + grant_type, client_id, client_secret, "", "", "" + ) + return access_token diff --git a/ml/dt_factory.py b/ml/dt_factory.py new file mode 100644 index 0000000..ef9f107 --- /dev/null +++ b/ml/dt_factory.py @@ -0,0 +1,180 @@ +""" Implements a factory for managing digital twins.""" + +import sys +from s3i import IdentityProvider +from s3i import TokenType +from ml.tools import get_idp +from ml.managing_actor import ManagingActor +from ml.tools import load_config +from ml.app_logger import APP_LOGGER +from ml.thing import Thing +from customer_code_example.harvester_john_deere import john_deere +from customer_code_example.harvester_ponsse import ponsse +from customer_code_example.forwarder_komatsu import komatsu +from customer_code_example.mini_tractor_driver import MiniTractorDriver + +from ml.ml40.roles.service.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 +from ml.ml40.features.properties.composite import Composite + +from ml.ml40.features.properties.values.weight import Weight +from ml.ml40.features.properties.values.moisture import Moisture +from ml.fml40.features.functionalities.accepts_proximity_alert import ( + AcceptsProximityAlert, +) +from ml.fml40.features.functionalities.accepts_felling_jobs import AcceptsFellingJobs +from ml.fml40.features.functionalities.harvests import Harvests +from ml.fml40.features.functionalities.forwards import Forwards +from ml.fml40.features.functionalities.provides_production_data import ( + ProvidesProductionData, +) +from ml.fml40.features.functionalities.accepts_forwarding_jobs import ( + AcceptsForwardingJobs, +) +from ml.fml40.features.functionalities.provides_passability_information import ( + ProvidesPassabilityInformation, +) +from ml.fml40.features.functionalities.provides_moisture_prediction import ( + ProvidesMoisturePrediction, +) +from ml.fml40.features.functionalities.accepts_passability_report import ( + AcceptsPassabilityReport, +) +from ml.fml40.features.properties.values.documents.reports.moisture_prediction_report import ( + MoisturePredictionReport, +) + + + +# TODO: Get rid of this global variable +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 + + +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_featrues(thing_ref, feature_proxy, json_feature): + json_sub_features = json_feature.get("subFeatures", []) + for json_sub_feature in json_sub_features: + class_name = json_sub_feature.get("class", "") + sub_feature = DT_FACTORY.get(class_name, None) + if not sub_feature: + APP_LOGGER.critical("Subfeature: %s is missing" % class_name) + else: + APP_LOGGER.debug("Adding subfeature: %s" % class_name) + sub_feature_ref = sub_feature.start("", thing_ref) + sub_feature_proxy = sub_feature_ref.proxy() + sub_feature_proxy.from_json(json_feature).get() + type_name = sub_feature_proxy.type_name.get() + sub_features = feature_proxy.subfeatures.get() + sub_features.append(sub_feature_ref) + + +def build(thing_ref, attributes): + thing_proxy = thing_ref.proxy() + json_features = attributes.get("features", []) + for json_feature in json_features: + class_name = json_feature.get("class", "") + feature = DT_FACTORY.get(class_name, None) + if not feature: + APP_LOGGER.critical("Feature: %s is missing" % class_name) + else: + APP_LOGGER.debug("Adding feature: %s" % class_name) + feature_ref = feature.start("", 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 + build_sub_featrues(thing_ref, feature_proxy, json_feature) + + +def create_dt_ref(model, grant_type, secret, username, password, is_broker, is_repo): + """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) + + """ + # id_p = get_idp(grant_type, thing_id, secret, username, password) + attributes = model.get("attributes", None) + if attributes is None: + print("Incomplete model: attributes missing!") + sys.exit() + + thing_type = attributes.get("class", "") + if thing_type == "": + print("Unknown type %s" % thing_type) + sys.exit() + + thing_name = attributes.get("name", "") + APP_LOGGER.debug("Creating ditigtal twin %s" % thing_name) + d_t = DT_FACTORY.get(thing_type, ManagingActor) + thing_ref = d_t.start( + model=model, + grant_type=grant_type, + client_secret=secret, + username=username, + password=password, + is_broker=is_broker, + is_repo=is_repo, + ) + build(thing_ref, attributes) + return thing_ref diff --git a/ml/fml40/__init__.py b/ml/fml40/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/fml40/features/__init__.py b/ml/fml40/features/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/fml40/features/functionalities/__init__.py b/ml/fml40/features/functionalities/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/fml40/features/functionalities/accepts_felling_jobs.py b/ml/fml40/features/functionalities/accepts_felling_jobs.py new file mode 100644 index 0000000..2e5b243 --- /dev/null +++ b/ml/fml40/features/functionalities/accepts_felling_jobs.py @@ -0,0 +1,21 @@ +from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs +from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob +from ml.app_logger import APP_LOGGER + + +class AcceptsFellingJobs(AcceptsJobs): + def __init__(self, name, ref_managing_actor): + super(AcceptsFellingJobs, self).__init__( + name=name, ref_managing_actor=ref_managing_actor + ) + + def acceptJob(self, job: FellingJob) -> bool: + APP_LOGGER.info("Checking if the job can be accepted.") + proxy_executer = self.managing_actor.proxy_functionalities.get()[ + self.proxy_name + ] + proxy_executer.executeJob(job) + return True + + def from_json(self, json_obj): + super().from_json(json_obj) diff --git a/ml/fml40/features/functionalities/accepts_felling_support_jobs.py b/ml/fml40/features/functionalities/accepts_felling_support_jobs.py new file mode 100644 index 0000000..f521484 --- /dev/null +++ b/ml/fml40/features/functionalities/accepts_felling_support_jobs.py @@ -0,0 +1,8 @@ +from modelling_language.ml40.feature.functionality.AcceptsJob import AcceptsJobs +from modelling_language.fml40.feature.property.value.document.job.FellingSupportJob import FellingSupportJob + + +class AcceptFellingSupportJobs(AcceptsJobs): + + def acceptJob(self, job: FellingSupportJob) -> bool: + pass diff --git a/ml/fml40/features/functionalities/accepts_forwarding_jobs.py b/ml/fml40/features/functionalities/accepts_forwarding_jobs.py new file mode 100644 index 0000000..7cd46e8 --- /dev/null +++ b/ml/fml40/features/functionalities/accepts_forwarding_jobs.py @@ -0,0 +1,21 @@ +from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs +from ml.fml40.features.properties.values.documents.jobs.forwarding_job import ( + ForwardingJob, +) +from ml.app_logger import APP_LOGGER + + +class AcceptsForwardingJobs(AcceptsJobs): + def __init__(self, name, ref_managing_actor): + super().__init__(ref_managing_actor=ref_managing_actor, name=name) + + def acceptJob(self, job: ForwardingJob) -> bool: + APP_LOGGER.info("I check if I (the Komatsu Forwarder) can accept the job.") + proxy_executer = self.managing_actor.proxy_functionalities.get().get( + "fml40::Forwards", None + ) + proxy_executer.executeJob(job) + return True + + def from_json(self, json_obj): + super().from_json(json_obj) diff --git a/ml/fml40/features/functionalities/accepts_log_measurements.py b/ml/fml40/features/functionalities/accepts_log_measurements.py new file mode 100644 index 0000000..3d2ff24 --- /dev/null +++ b/ml/fml40/features/functionalities/accepts_log_measurements.py @@ -0,0 +1,8 @@ +from modelling_language.ml40.feature.functionality import Functionality +from modelling_language.fml40.feature.property.value.document.report import LogMeasurement + + +class AcceptsLogMeasurements(Functionality): + + def acceptLogMeasurement(self, log_measurement: LogMeasurement) -> bool: + pass diff --git a/ml/fml40/features/functionalities/accepts_log_transportaition_jobs.py b/ml/fml40/features/functionalities/accepts_log_transportaition_jobs.py new file mode 100644 index 0000000..4c8791d --- /dev/null +++ b/ml/fml40/features/functionalities/accepts_log_transportaition_jobs.py @@ -0,0 +1,8 @@ +from modelling_language.ml40.feature.functionality import AcceptsJobs +from modelling_language.fml40.feature.property.value.document.job import LogTransportationJob + + +class AcceptsLogTransportationJobs(AcceptsJobs): + + def acceptJob(self, job: LogTransportationJob) -> bool: + pass diff --git a/ml/fml40/features/functionalities/accepts_moisture_measurement.py b/ml/fml40/features/functionalities/accepts_moisture_measurement.py new file mode 100644 index 0000000..6970b58 --- /dev/null +++ b/ml/fml40/features/functionalities/accepts_moisture_measurement.py @@ -0,0 +1,7 @@ +from modelling_language.ml40.feature.functionality import Functionality +from modelling_language.fml40.feature.property.value.document.report import SoilMoistureMeasurement + + +class AcceptsMoistureMeasurement(Functionality): + def acceptMoistureMeasurement(self, input: SoilMoistureMeasurement): + pass \ No newline at end of file diff --git a/ml/fml40/features/functionalities/accepts_move_commands.py b/ml/fml40/features/functionalities/accepts_move_commands.py new file mode 100644 index 0000000..c0e3081 --- /dev/null +++ b/ml/fml40/features/functionalities/accepts_move_commands.py @@ -0,0 +1,13 @@ +from modelling_language.ml40.feature.functionality import Functionality + + +class AcceptsMoveCommands(Functionality): + def __init__(self, name, ref_managing_actor): + super().__init__( + name=name, + ref_managing_actor=ref_managing_actor + ) + + def move(self, longitude: float, latitude: float): + print("move to longitude: {}, latitude : {}".format(longitude, latitude)) + return {"longitude": longitude, "latitude": latitude} diff --git a/ml/fml40/features/functionalities/accepts_passability_report.py b/ml/fml40/features/functionalities/accepts_passability_report.py new file mode 100644 index 0000000..b69dcdc --- /dev/null +++ b/ml/fml40/features/functionalities/accepts_passability_report.py @@ -0,0 +1,16 @@ +from ml.ml40.features.functionalities.functionality import Functionality +from ml.identifier import ID +from ml.fml40.features.properties.values.documents.reports.passability_report import ( + PassabilityReport, +) + + + +class AcceptsPassabilityReport(Functionality): + def __init__(self, name, ref_managing_actor): + super(AcceptsPassabilityReport, self).__init__( + name=name, ref_managing_actor=ref_managing_actor + ) + + def acceptsReport(self, report): + pass diff --git a/ml/fml40/features/functionalities/accepts_proximity_alert.py b/ml/fml40/features/functionalities/accepts_proximity_alert.py new file mode 100644 index 0000000..8c500ea --- /dev/null +++ b/ml/fml40/features/functionalities/accepts_proximity_alert.py @@ -0,0 +1,17 @@ +import json +from ml.ml40.features.functionalities.functionality import Functionality +from ml.identifier import ID + + +class AcceptsProximityAlert(Functionality): + def __init__(self, name, ref_managing_actor): + super().__init__(name=name, ref_managing_actor=ref_managing_actor) + + def proximityAlert(self, ids: list, distances: list): + print("Making Proximity Alert...") + + def from_json(self, json_obj): + super().from_json(json_obj) + + def class_name(self): + return self.__class__.name diff --git a/ml/fml40/features/functionalities/accepts_single_tree_felling_jobs.py b/ml/fml40/features/functionalities/accepts_single_tree_felling_jobs.py new file mode 100644 index 0000000..0b1ee14 --- /dev/null +++ b/ml/fml40/features/functionalities/accepts_single_tree_felling_jobs.py @@ -0,0 +1,8 @@ +from modelling_language.ml40.feature.functionality import AcceptsJobs +from modelling_language.fml40.feature.property.value.document.job import SingleTreeFellingJob + + +class AcceptsSingleTreeFellingJobs(AcceptsJobs): + + def acceptJob(self, job: SingleTreeFellingJob) -> bool: + pass diff --git a/ml/fml40/features/functionalities/classifies_tree_species.py b/ml/fml40/features/functionalities/classifies_tree_species.py new file mode 100644 index 0000000..4c37fb1 --- /dev/null +++ b/ml/fml40/features/functionalities/classifies_tree_species.py @@ -0,0 +1,6 @@ +from modelling_language.ml40.feature.functionality import Functionality + + +class ClassifiesTreeSpecies(Functionality): + def calculateTreeSpeciesClassification(self, tree: bytes) -> bytes: + pass diff --git a/ml/fml40/features/functionalities/cuts.py b/ml/fml40/features/functionalities/cuts.py new file mode 100644 index 0000000..d097042 --- /dev/null +++ b/ml/fml40/features/functionalities/cuts.py @@ -0,0 +1,6 @@ +from modelling_language.ml40.feature.functionality import Functionality + + +class Cuts(Functionality): + def cut(self): + pass diff --git a/ml/fml40/features/functionalities/determines_passability.py b/ml/fml40/features/functionalities/determines_passability.py new file mode 100644 index 0000000..f654185 --- /dev/null +++ b/ml/fml40/features/functionalities/determines_passability.py @@ -0,0 +1,17 @@ +from modelling_language.ml40.feature.functionality import Functionality +from datetime import date + + +class DeterminesPassability(Functionality): + + def isPassableNow(self, input: float) -> bool: + pass + + def maxPassableWeightNow(self) -> float: + pass + + def maxPassableWeightOnDate(self, date: date) -> float: + pass + + def willBePassable(self, input: float) -> date: + pass diff --git a/ml/fml40/features/functionalities/displays_health_alarms.py b/ml/fml40/features/functionalities/displays_health_alarms.py new file mode 100644 index 0000000..3cebd5f --- /dev/null +++ b/ml/fml40/features/functionalities/displays_health_alarms.py @@ -0,0 +1,7 @@ +from modelling_language.ml40.feature.functionality import Functionality + + +class DisplaysHealthAlarms(Functionality): + + def displayHealthAlarm(self): + pass diff --git a/ml/fml40/features/functionalities/evaluates_stand_attributes.py b/ml/fml40/features/functionalities/evaluates_stand_attributes.py new file mode 100644 index 0000000..a020e5a --- /dev/null +++ b/ml/fml40/features/functionalities/evaluates_stand_attributes.py @@ -0,0 +1,10 @@ +from modelling_language.ml40.feature.functionality import Functionality +from datetime import date + + +class EvaluatesStandAttributes(Functionality): + def calculateStandAttributes(self, input_a: bytes, input_b: date) -> str: + pass + + def calculateStock(self, input_a: bytes) -> float: + pass diff --git a/ml/fml40/features/functionalities/fells.py b/ml/fml40/features/functionalities/fells.py new file mode 100644 index 0000000..97e6299 --- /dev/null +++ b/ml/fml40/features/functionalities/fells.py @@ -0,0 +1,16 @@ +from modelling_language.ml40.feature.functionality import Functionality +from modelling_language.fml40.feature.property.value import TreeData +from modelling_language.fml40.feature.property.value.document.job import FellingJob + +class Fells(Functionality): + + def __init__(self, name, ref_managing_actor): + super().__init__(name=name, ref_managing_actor=ref_managing_actor) + + def executeFellingJob(self, job: FellingJob): + print("i am executing the felling job {}".format(job)) + pass + + def fell(self, tree_data: TreeData): + print("i am felling the tree {}".format(tree_data)) + pass diff --git a/ml/fml40/features/functionalities/forest_planning_evaluation.py b/ml/fml40/features/functionalities/forest_planning_evaluation.py new file mode 100644 index 0000000..a816504 --- /dev/null +++ b/ml/fml40/features/functionalities/forest_planning_evaluation.py @@ -0,0 +1,6 @@ +from modelling_language.ml40.feature.functionality import Functionality + + +class ForestPlanningEvaluation(Functionality): + def evaluateInventoryData(self): + pass diff --git a/ml/fml40/features/functionalities/forwards.py b/ml/fml40/features/functionalities/forwards.py new file mode 100644 index 0000000..ee4b92b --- /dev/null +++ b/ml/fml40/features/functionalities/forwards.py @@ -0,0 +1,21 @@ +import uuid +from ml.ml40.features.functionalities.functionality import Functionality +from ml.fml40.features.properties.values.documents.jobs.forwarding_job import ( + ForwardingJob, +) +from ml.app_logger import APP_LOGGER +from ml.tools import create_request +from s3i import Broker +from s3i import GetValueRequest + + + +class Forwards(Functionality): + def __init__(self, name, ref_managing_actor): + super(Forwards, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + + def executeJob(self, job): + pass + + def from_json(self, json_obj): + super().from_json(json_obj) diff --git a/ml/fml40/features/functionalities/generates_afforestation_suggestions.py b/ml/fml40/features/functionalities/generates_afforestation_suggestions.py new file mode 100644 index 0000000..011510b --- /dev/null +++ b/ml/fml40/features/functionalities/generates_afforestation_suggestions.py @@ -0,0 +1,8 @@ +from modelling_language.ml40.feature.functionality import Functionality +from modelling_language.fml40.feature.property.value.document.report import AfforestationSuggestion + + +class GeneratesAfforestationSuggestions(Functionality): + + def generateAfforestationSuggestion(self) -> AfforestationSuggestion: + pass \ No newline at end of file diff --git a/ml/fml40/features/functionalities/generates_felling_suggestions.py b/ml/fml40/features/functionalities/generates_felling_suggestions.py new file mode 100644 index 0000000..def2fd4 --- /dev/null +++ b/ml/fml40/features/functionalities/generates_felling_suggestions.py @@ -0,0 +1,9 @@ +from modelling_language.ml40.feature.functionality import Functionality +from modelling_language.identifier import ID +from modelling_language.fml40.feature.property.value.document.report import FellingSupportSuggestion + + +class GeneratesFellingSuggestions(Functionality): + + def generateFellingSuggestion(self, tree_Id: ID) -> FellingSupportSuggestion: + pass \ No newline at end of file diff --git a/ml/fml40/features/functionalities/grabs.py b/ml/fml40/features/functionalities/grabs.py new file mode 100644 index 0000000..508fbf4 --- /dev/null +++ b/ml/fml40/features/functionalities/grabs.py @@ -0,0 +1,12 @@ +from modelling_language.ml40.feature.functionality import Functionality + + +class Grabs(Functionality): + def __init__(self): + pass + + def close(self): + pass + + def open(self): + pass diff --git a/ml/fml40/features/functionalities/harvests.py b/ml/fml40/features/functionalities/harvests.py new file mode 100644 index 0000000..5d5b580 --- /dev/null +++ b/ml/fml40/features/functionalities/harvests.py @@ -0,0 +1,19 @@ +from ml.ml40.features.functionalities.functionality import Functionality +from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob +from ml.app_logger import APP_LOGGER + + +class Harvests(Functionality): + def __init__(self, name, ref_managing_actor): + super(Harvests, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + + def executeJob(self, job: FellingJob): + APP_LOGGER.info("I am harvesting right now. So much fun!") + # TODO: Error handling + proxy_manager = self.managing_actor.proxy_functionalities.get().get( + ["ml40::ManageJobs"], None + ) + proxy_manager.assignJob(self.sender_id, self.receiver_id, job) + + def from_json(self, json_obj): + super().from_json(json_obj) diff --git a/ml/fml40/features/functionalities/measure_wood.py b/ml/fml40/features/functionalities/measure_wood.py new file mode 100644 index 0000000..7a65a88 --- /dev/null +++ b/ml/fml40/features/functionalities/measure_wood.py @@ -0,0 +1,10 @@ +from modelling_language.ml40.feature.functionality import Functionality +from modelling_language.fml40.feature.property.value.document.report import LogMeasurement + + +class MeasuresWood(Functionality): + def __init__(self): + pass + + def measureLog(self) -> LogMeasurement: + pass diff --git a/ml/fml40/features/functionalities/monitor_health_status.py b/ml/fml40/features/functionalities/monitor_health_status.py new file mode 100644 index 0000000..1f39415 --- /dev/null +++ b/ml/fml40/features/functionalities/monitor_health_status.py @@ -0,0 +1,7 @@ +from modelling_language.ml40.feature.functionality import Functionality + + +class MonitorsHealthStatus(Functionality): + + def MonitorsHealthStatus(self): + pass diff --git a/ml/fml40/features/functionalities/provides_moisture_prediction.py b/ml/fml40/features/functionalities/provides_moisture_prediction.py new file mode 100644 index 0000000..2bcc8bb --- /dev/null +++ b/ml/fml40/features/functionalities/provides_moisture_prediction.py @@ -0,0 +1,20 @@ +from ml.ml40.features.functionalities.functionality import Functionality +from ml.identifier import ID +from ml.fml40.features.properties.values.documents.reports.passability_report import ( + PassabilityReport, +) +import datetime +import requests + + +class ProvidesMoisturePrediction(Functionality): + def __init__(self, name, ref_managing_actor): + super(ProvidesMoisturePrediction, self).__init__( + name=name, ref_managing_actor=ref_managing_actor + ) + + def predict(self): + pass + + def predictMoistures(self, location, moisture): + pass diff --git a/ml/fml40/features/functionalities/provides_passability_information.py b/ml/fml40/features/functionalities/provides_passability_information.py new file mode 100644 index 0000000..afeb30f --- /dev/null +++ b/ml/fml40/features/functionalities/provides_passability_information.py @@ -0,0 +1,15 @@ +from ml.ml40.features.functionalities.functionality import Functionality +from ml.identifier import ID +from ml.fml40.features.properties.values.documents.reports.passability_report import ( + PassabilityReport, +) + + +class ProvidesPassabilityInformation(Functionality): + def __init__(self, name, ref_managing_actor): + super(ProvidesPassabilityInformation, self).__init__( + name=name, ref_managing_actor=ref_managing_actor + ) + + def execute(self, json_body): + pass diff --git a/ml/fml40/features/functionalities/provides_production_data.py b/ml/fml40/features/functionalities/provides_production_data.py new file mode 100644 index 0000000..9914622 --- /dev/null +++ b/ml/fml40/features/functionalities/provides_production_data.py @@ -0,0 +1,11 @@ +from ml.ml40.features.functionalities.functionality import Functionality +from ml.fml40.features.properties.values.documents.reports.production_data import ProductionData + + +class ProvidesProductionData(Functionality): + def __init__(self, name, ref_managing_actor): + super(ProvidesProductionData, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + + def getProductionData(self) -> ProductionData: + return "Production Data is huge" diff --git a/ml/fml40/features/functionalities/provides_tree_data.py b/ml/fml40/features/functionalities/provides_tree_data.py new file mode 100644 index 0000000..5c6e29c --- /dev/null +++ b/ml/fml40/features/functionalities/provides_tree_data.py @@ -0,0 +1,13 @@ +from modelling_language.ml40.feature.functionality import Functionality +from modelling_language.ml40.feature.property.value import Location +from modelling_language.identifier import ID +from modelling_language.fml40.feature.property.value import TreeData + + +class ProvidesTreeData(Functionality): + + def getTreeData(self, Tree:ID) -> list: + pass + + def getTreesInDiameter(self, location: Location, diameter: float) -> list: + pass diff --git a/ml/fml40/features/functionalities/provides_weather_data.py b/ml/fml40/features/functionalities/provides_weather_data.py new file mode 100644 index 0000000..df6eddd --- /dev/null +++ b/ml/fml40/features/functionalities/provides_weather_data.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.feature.functionality import Functionality + + +class ProvidesWeatherData(Functionality): + pass diff --git a/ml/fml40/features/functionalities/simulates_tree_growth.py b/ml/fml40/features/functionalities/simulates_tree_growth.py new file mode 100644 index 0000000..9cd51f9 --- /dev/null +++ b/ml/fml40/features/functionalities/simulates_tree_growth.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.feature.functionality import Functionality + + +class SimulatesTreeGrowth(Functionality): + pass diff --git a/ml/fml40/features/functionalities/supports_felling.py b/ml/fml40/features/functionalities/supports_felling.py new file mode 100644 index 0000000..f713d7a --- /dev/null +++ b/ml/fml40/features/functionalities/supports_felling.py @@ -0,0 +1,9 @@ +from modelling_language.ml40.feature.functionality import Functionality +from modelling_language.fml40.feature.property.value.document.job import FellingJob +from modelling_language.fml40.feature.property.value.document.report import FellingSupportSuggestion + + +class SupportsFelling(Functionality): + + def supportFelling(self, job:FellingJob, suggestion: FellingSupportSuggestion): + pass diff --git a/ml/fml40/features/functionalities/transports_logs.py b/ml/fml40/features/functionalities/transports_logs.py new file mode 100644 index 0000000..17c3539 --- /dev/null +++ b/ml/fml40/features/functionalities/transports_logs.py @@ -0,0 +1,14 @@ +from modelling_language.ml40.feature.functionality import Functionality +from modelling_language.fml40.feature.property.value.document.job import LogTransportationJob +from modelling_language.fml40.feature.property.value.document.report import LogTransportationReport + + +class TransportsLogs(Functionality): + def __init__(self, name, ref_managing_actor): + super(TransportsLogs, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor) + + def transportLogs(self, job: LogTransportationJob) -> LogTransportationReport: + print("i am making Log transportation report for the job {}".format(job)) + return "the transportation report is huge" diff --git a/ml/fml40/features/properties/__init__.py b/ml/fml40/features/properties/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/fml40/features/properties/values/__init__.py b/ml/fml40/features/properties/values/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/fml40/features/properties/values/abstract_inventory.py b/ml/fml40/features/properties/values/abstract_inventory.py new file mode 100644 index 0000000..ddd6e19 --- /dev/null +++ b/ml/fml40/features/properties/values/abstract_inventory.py @@ -0,0 +1,5 @@ +from abc import ABC + + +class AbstractInventory(ABC): + pass diff --git a/ml/fml40/features/properties/values/dbh.py b/ml/fml40/features/properties/values/dbh.py new file mode 100644 index 0000000..a3b9411 --- /dev/null +++ b/ml/fml40/features/properties/values/dbh.py @@ -0,0 +1,8 @@ +from modelling_language.ml40.feature.property.value import Value + + +class DBH(Value): + def __init__(self, name, ref_managing_actor, dbh: float): + super(DBH, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.dbh = dbh \ No newline at end of file diff --git a/ml/fml40/features/properties/values/documents/__init__.py b/ml/fml40/features/properties/values/documents/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/fml40/features/properties/values/documents/jobs/__init__.py b/ml/fml40/features/properties/values/documents/jobs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/fml40/features/properties/values/documents/jobs/felling_job.py b/ml/fml40/features/properties/values/documents/jobs/felling_job.py new file mode 100644 index 0000000..ae21fec --- /dev/null +++ b/ml/fml40/features/properties/values/documents/jobs/felling_job.py @@ -0,0 +1,8 @@ +from ml.ml40.features.properties.values.documents.jobs.job import Job + + +class FellingJob(Job): + def __init__(self, name, ref_managing_actor): + super(FellingJob, self).__init__( + name=name, ref_managing_actor=ref_managing_actor + ) diff --git a/ml/fml40/features/properties/values/documents/jobs/fellung_support_job.py b/ml/fml40/features/properties/values/documents/jobs/fellung_support_job.py new file mode 100644 index 0000000..b42b1cb --- /dev/null +++ b/ml/fml40/features/properties/values/documents/jobs/fellung_support_job.py @@ -0,0 +1,8 @@ +from ml.ml40.features.properties.values.documents.jobs.job import Job + + +class FellingSupportJob(Job): + def __init__(self, name, ref_managing_actor): + super(FellingSupportJob, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor) diff --git a/ml/fml40/features/properties/values/documents/jobs/forwarding_job.py b/ml/fml40/features/properties/values/documents/jobs/forwarding_job.py new file mode 100644 index 0000000..14d1001 --- /dev/null +++ b/ml/fml40/features/properties/values/documents/jobs/forwarding_job.py @@ -0,0 +1,7 @@ +from ml.ml40.features.properties.values.documents.jobs.job import Job + + +class ForwardingJob(Job): + def __init__(self, name, ref_managing_actor): + super(ForwardingJob, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) diff --git a/ml/fml40/features/properties/values/documents/jobs/log_transportation_job.py b/ml/fml40/features/properties/values/documents/jobs/log_transportation_job.py new file mode 100644 index 0000000..c814feb --- /dev/null +++ b/ml/fml40/features/properties/values/documents/jobs/log_transportation_job.py @@ -0,0 +1,9 @@ +from ml.ml40.features.properties.values.documents.jobs.job import Job + + +class LogTransportationJob(Job): + def __init__(self, name, ref_managing_actor, woodPiles: list): + super(LogTransportationJob, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor) + self.woodPiles = woodPiles diff --git a/ml/fml40/features/properties/values/documents/jobs/safety_felling_job.py b/ml/fml40/features/properties/values/documents/jobs/safety_felling_job.py new file mode 100644 index 0000000..8320718 --- /dev/null +++ b/ml/fml40/features/properties/values/documents/jobs/safety_felling_job.py @@ -0,0 +1,8 @@ +from ml.ml40.features.properties.values.documents.jobs.job import Job + + +class SafetyFellingJob(Job): + def __init__(self, name, ref_managing_actor): + super(SafetyFellingJob, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor) diff --git a/ml/fml40/features/properties/values/documents/jobs/single_tree_felling_job.py b/ml/fml40/features/properties/values/documents/jobs/single_tree_felling_job.py new file mode 100644 index 0000000..e903703 --- /dev/null +++ b/ml/fml40/features/properties/values/documents/jobs/single_tree_felling_job.py @@ -0,0 +1,10 @@ +from ml.ml40.features.properties.values.documents.jobs.job import Job +from ml.identifier import ID + + +class SingleTreeFellingJob(Job): + def __init__(self, name, ref_managing_actor, tree: ID): + super(SingleTreeFellingJob, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor) + self.tree = tree diff --git a/ml/fml40/features/properties/values/documents/jobs/transportation_job.py b/ml/fml40/features/properties/values/documents/jobs/transportation_job.py new file mode 100644 index 0000000..30ef44c --- /dev/null +++ b/ml/fml40/features/properties/values/documents/jobs/transportation_job.py @@ -0,0 +1,8 @@ +from ml.ml40.features.properties.values.documents.jobs.job import Job + + +class TransportationJob(Job): + def __init__(self, name, ref_managing_actor): + super(TransportationJob, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor) diff --git a/ml/fml40/features/properties/values/documents/reports/__init__.py b/ml/fml40/features/properties/values/documents/reports/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/fml40/features/properties/values/documents/reports/afforestation_suggestion.py b/ml/fml40/features/properties/values/documents/reports/afforestation_suggestion.py new file mode 100644 index 0000000..35980dd --- /dev/null +++ b/ml/fml40/features/properties/values/documents/reports/afforestation_suggestion.py @@ -0,0 +1,9 @@ +from modelling_language.ml40.feature.property.value.document.report import Report + + +class AfforestationSuggestion(Report): + def __init__(self, name, ref_managing_actor): + super(AfforestationSuggestion, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor + ) diff --git a/ml/fml40/features/properties/values/documents/reports/felling_method_suggestion.py b/ml/fml40/features/properties/values/documents/reports/felling_method_suggestion.py new file mode 100644 index 0000000..94c3d00 --- /dev/null +++ b/ml/fml40/features/properties/values/documents/reports/felling_method_suggestion.py @@ -0,0 +1,21 @@ +from ml.ml40.features.properties.values.documents.reports.report import Report +from ml.fml40.features.properties.values.documents.reports.felling_tool import ( + FellingTool, +) +from ml.identifier import ID + + +class FellingMethodSuggestion(Report): + def __init__( + self, + name, + ref_managing_actor, + liftHeight: int, + tool: FellingTool, + torque: float, + tree: ID, + ): + self.liftHeight = liftHeight + self.tool = tool + self.torque = torque + self.tree = tree diff --git a/ml/fml40/features/properties/values/documents/reports/felling_support_suggestion.py b/ml/fml40/features/properties/values/documents/reports/felling_support_suggestion.py new file mode 100644 index 0000000..122f79b --- /dev/null +++ b/ml/fml40/features/properties/values/documents/reports/felling_support_suggestion.py @@ -0,0 +1,9 @@ +from modelling_language.ml40.feature.property.value.document.report import Report + + +class FellingSupportSuggestion(Report): + def __init__(self, name, ref_managing_actor): + super(FellingSupportSuggestion, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor + ) diff --git a/ml/fml40/features/properties/values/documents/reports/felling_tool.py b/ml/fml40/features/properties/values/documents/reports/felling_tool.py new file mode 100644 index 0000000..6797ded --- /dev/null +++ b/ml/fml40/features/properties/values/documents/reports/felling_tool.py @@ -0,0 +1,6 @@ +from enum import Enum + + +class FellingTool(Enum): + WINCH = 0 + WEDGE = 1 diff --git a/ml/fml40/features/properties/values/documents/reports/log_measurement.py b/ml/fml40/features/properties/values/documents/reports/log_measurement.py new file mode 100644 index 0000000..b2b0f83 --- /dev/null +++ b/ml/fml40/features/properties/values/documents/reports/log_measurement.py @@ -0,0 +1,9 @@ +from ml.ml40.features.properties.values.documents.reports.report import Report + + +class LogMeasurement(Report): + def __init__(self, name, ref_managing_actor): + super(LogMeasurement, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor + ) diff --git a/ml/fml40/features/properties/values/documents/reports/log_transportation_report.py b/ml/fml40/features/properties/values/documents/reports/log_transportation_report.py new file mode 100644 index 0000000..d4184cb --- /dev/null +++ b/ml/fml40/features/properties/values/documents/reports/log_transportation_report.py @@ -0,0 +1,9 @@ +from modelling_language.ml40.feature.property.value.document.report import Report + + +class LogTransportationReport(Report): + def __init__(self, name, ref_managing_actor): + super(LogTransportationReport, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor + ) diff --git a/ml/fml40/features/properties/values/documents/reports/map_data.py b/ml/fml40/features/properties/values/documents/reports/map_data.py new file mode 100644 index 0000000..2f92acd --- /dev/null +++ b/ml/fml40/features/properties/values/documents/reports/map_data.py @@ -0,0 +1,9 @@ +from modelling_language.ml40.feature.property.value.document.report import Report + + +class MapData(Report): + def __init__(self, name, ref_managing_actor): + super(MapData, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor + ) diff --git a/ml/fml40/features/properties/values/documents/reports/moisture_prediction_report.py b/ml/fml40/features/properties/values/documents/reports/moisture_prediction_report.py new file mode 100644 index 0000000..27d2a1e --- /dev/null +++ b/ml/fml40/features/properties/values/documents/reports/moisture_prediction_report.py @@ -0,0 +1,13 @@ +from ml.ml40.features.properties.values.documents.reports.report import Report + + +class MoisturePredictionReport(Report): + def __init__(self, name, ref_managing_actor): + super(MoisturePredictionReport, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor + ) + + + def to_json(self): + pass diff --git a/ml/fml40/features/properties/values/documents/reports/passability_report.py b/ml/fml40/features/properties/values/documents/reports/passability_report.py new file mode 100644 index 0000000..7acd760 --- /dev/null +++ b/ml/fml40/features/properties/values/documents/reports/passability_report.py @@ -0,0 +1,9 @@ +from ml.ml40.features.properties.values.documents.reports.report import Report + + +class PassabilityReport(Report): + def __init__(self, name, ref_managing_actor): + super(PassabilityReport, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor + ) diff --git a/ml/fml40/features/properties/values/documents/reports/production_data.py b/ml/fml40/features/properties/values/documents/reports/production_data.py new file mode 100644 index 0000000..0f836cc --- /dev/null +++ b/ml/fml40/features/properties/values/documents/reports/production_data.py @@ -0,0 +1,9 @@ +from ml.ml40.features.properties.values.documents.reports.report import Report + + +class ProductionData(Report): + def __init__(self, name, ref_managing_actor): + super(ProductionData, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor + ) diff --git a/ml/fml40/features/properties/values/documents/reports/soil_moisture_measurement.py b/ml/fml40/features/properties/values/documents/reports/soil_moisture_measurement.py new file mode 100644 index 0000000..03f6dc2 --- /dev/null +++ b/ml/fml40/features/properties/values/documents/reports/soil_moisture_measurement.py @@ -0,0 +1,9 @@ +from modelling_language.ml40.feature.property.value.document.report import Report + + +class SoilMoistureMeasurement(Report): + def __init__(self, name, ref_managing_actor): + super(SoilMoistureMeasurement, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor + ) diff --git a/ml/fml40/features/properties/values/inventory_data.py b/ml/fml40/features/properties/values/inventory_data.py new file mode 100644 index 0000000..836b37b --- /dev/null +++ b/ml/fml40/features/properties/values/inventory_data.py @@ -0,0 +1,15 @@ +from modelling_language.fml40.feature.property.value import AbstractInventory +from modelling_language.ml40.feature.property.value import Value + + +class InventoryData(Value): + def __init__(self): + self._data = [] + + @property + def data(self): + return self._data + + @data.setter + def data(self, value: AbstractInventory): + self._data.append(value) diff --git a/ml/fml40/features/properties/values/stem_segment_properties.py b/ml/fml40/features/properties/values/stem_segment_properties.py new file mode 100644 index 0000000..66be1ae --- /dev/null +++ b/ml/fml40/features/properties/values/stem_segment_properties.py @@ -0,0 +1,13 @@ +from modelling_language.ml40.feature.property.value import Value + + +class StemSegmentProperties(Value): + def __init__(self, name, ref_managing_actor, + diameter: float, length: float, quality: str, woodType: str): + super(StemSegmentProperties, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.diameter = diameter + self.length = length + self.quality = quality + self.woodType = woodType + diff --git a/ml/fml40/features/properties/values/tilt.py b/ml/fml40/features/properties/values/tilt.py new file mode 100644 index 0000000..56488ec --- /dev/null +++ b/ml/fml40/features/properties/values/tilt.py @@ -0,0 +1,10 @@ +from modelling_language.ml40.feature.property.value import Value + + +class Tilt(Value): + def __init__(self, name, ref_managing_actor, + direction: float, tilt: float): + super(Tilt, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.direction = direction + self.tilt = tilt diff --git a/ml/fml40/features/properties/values/tree_data.py b/ml/fml40/features/properties/values/tree_data.py new file mode 100644 index 0000000..1cd0b75 --- /dev/null +++ b/ml/fml40/features/properties/values/tree_data.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.feature.property.value import Value + + +class TreeData(Value): + pass diff --git a/ml/fml40/features/properties/values/tree_type.py b/ml/fml40/features/properties/values/tree_type.py new file mode 100644 index 0000000..57fc36c --- /dev/null +++ b/ml/fml40/features/properties/values/tree_type.py @@ -0,0 +1,8 @@ +from modelling_language.ml40.feature.property.value import Value + + +class TreeType(Value): + def __init__(self, name, ref_managing_ref, conifer: bool): + super(TreeType, self).__init__(name=name, + ref_managing_ref=ref_managing_ref) + self.conifer = conifer \ No newline at end of file diff --git a/ml/fml40/machines/forwarder.py b/ml/fml40/machines/forwarder.py new file mode 100644 index 0000000..89a079a --- /dev/null +++ b/ml/fml40/machines/forwarder.py @@ -0,0 +1,52 @@ +"""Implementation of a generic Forwarder""" +import json +from ml.thing import Thing +from ml.app_logger import APP_LOGGER +from ml.tools import decode_message +from ml.tools import verify_message + + +class Forwarder(Thing): + """Implements a generic Forwarder.""" + + def __init__( + self, + data_model, + is_broker, + is_repo, + grant_type, + client_secret, + username, + password, + ): + super().__init__( + client_secret=client_secret, + data_model=data_model, + is_broker=is_broker, + is_repo=is_repo, + grant_type=grant_type, + username=username, + password=password, + ) + self.jobs = dict() + + def on_receive(self, msg): + """This function is called when the asset administration shell gets something + + :param body: json object representing a s3i message. + + """ + body_str = decode_message(msg) + body_json = json.loads(body_str) + APP_LOGGER.info( + "Received {} of type {}".format( + body_json["messageType"], body_json["serviceType"] + ) + ) + if verify_message(body_json, "serviceRequest", "fml40::ForwardingJob"): + self.proxy_functionalities["AcceptsForwardingJobs"].acceptJob( + "This is a forwarding job" + ) + msg = """I'm the asset administration shell and I distributed your message + to one of my highly qualified services""" + return msg diff --git a/ml/fml40/machines/harvester.py b/ml/fml40/machines/harvester.py new file mode 100644 index 0000000..63bfd77 --- /dev/null +++ b/ml/fml40/machines/harvester.py @@ -0,0 +1,32 @@ +"""Generic implementation of a harvester.""" + +import json +from ml.thing import Thing +from ml.app_logger import APP_LOGGER +from ml.tools import decode_message + + +class Harvester(Thing): + """Implements a generic harvester.""" + + def __init__( + self, + data_model, + is_broker, + is_repo, + grant_type, + client_secret, + username, + password, + ): + super().__init__( + client_secret=client_secret, + data_model=data_model, + is_broker=is_broker, + is_repo=is_repo, + grant_type=grant_type, + username=username, + password=password, + ) + + self.jobs = dict() diff --git a/ml/fml40/roles/__init__.py b/ml/fml40/roles/__init__.py new file mode 100644 index 0000000..c49f2a8 --- /dev/null +++ b/ml/fml40/roles/__init__.py @@ -0,0 +1,3 @@ +from .DT import * +from .HMI import * +from .Service import * diff --git a/ml/fml40/roles/dts/__init__.py b/ml/fml40/roles/dts/__init__.py new file mode 100644 index 0000000..96d8bdf --- /dev/null +++ b/ml/fml40/roles/dts/__init__.py @@ -0,0 +1,7 @@ +from .HandheldDevice import * +from .Machine import * +from .Part import * +from .Person import * +from .Sensor import * +from .Site import * +from .Way import * \ No newline at end of file diff --git a/ml/fml40/roles/dts/handheld_devices/__init__.py b/ml/fml40/roles/dts/handheld_devices/__init__.py new file mode 100644 index 0000000..ef158aa --- /dev/null +++ b/ml/fml40/roles/dts/handheld_devices/__init__.py @@ -0,0 +1,2 @@ +from .Brushcutter import Brushcutter +from .Chainsaw import Chainsaw \ No newline at end of file diff --git a/ml/fml40/roles/dts/handheld_devices/brushcutter.py b/ml/fml40/roles/dts/handheld_devices/brushcutter.py new file mode 100644 index 0000000..75a6295 --- /dev/null +++ b/ml/fml40/roles/dts/handheld_devices/brushcutter.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.handheld_devices.handheld_device import HandheldDevice + + +class Brushcutter(HandheldDevice): + pass diff --git a/ml/fml40/roles/dts/handheld_devices/chainsaw.py b/ml/fml40/roles/dts/handheld_devices/chainsaw.py new file mode 100644 index 0000000..2ce3c4b --- /dev/null +++ b/ml/fml40/roles/dts/handheld_devices/chainsaw.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.handheld_devices.handheld_device import HandheldDevice + + +class Chainsaw(HandheldDevice): + pass diff --git a/ml/fml40/roles/dts/machines/__init__.py b/ml/fml40/roles/dts/machines/__init__.py new file mode 100644 index 0000000..462f964 --- /dev/null +++ b/ml/fml40/roles/dts/machines/__init__.py @@ -0,0 +1,7 @@ +from .ForestMachine import ForestMachine +from .Forwarder import Forwarder +from .Harvester import Harvester +from .LogTruck import LogTruck +from .MiniTractor import MiniTractor +from .Skidder import Skidder +from .WheelLoader import WheelLoader \ No newline at end of file diff --git a/ml/fml40/roles/dts/machines/forest_machine.py b/ml/fml40/roles/dts/machines/forest_machine.py new file mode 100644 index 0000000..07e5507 --- /dev/null +++ b/ml/fml40/roles/dts/machines/forest_machine.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.machines.machine import Machine + + +class ForestMachine(Machine): + pass diff --git a/ml/fml40/roles/dts/machines/forwarder.py b/ml/fml40/roles/dts/machines/forwarder.py new file mode 100644 index 0000000..17b656a --- /dev/null +++ b/ml/fml40/roles/dts/machines/forwarder.py @@ -0,0 +1,5 @@ +from ml.fml40.roles.dt.machines.forest_machine import ForestMachine + + +class Forwarder(ForestMachine): + pass diff --git a/ml/fml40/roles/dts/machines/harvester.py b/ml/fml40/roles/dts/machines/harvester.py new file mode 100644 index 0000000..eb6bc47 --- /dev/null +++ b/ml/fml40/roles/dts/machines/harvester.py @@ -0,0 +1,5 @@ +from ml.fml40.roles.dts.machines.forest_machine import ForestMachine + + +class Harvester(ForestMachine): + pass diff --git a/ml/fml40/roles/dts/machines/log_truck.py b/ml/fml40/roles/dts/machines/log_truck.py new file mode 100644 index 0000000..5eaf520 --- /dev/null +++ b/ml/fml40/roles/dts/machines/log_truck.py @@ -0,0 +1,4 @@ +from ml.fml40.roles.dts.machines.forest_machine import ForestMachine + +class LogTruck(ForestMachine): + pass diff --git a/ml/fml40/roles/dts/machines/mini_tractor.py b/ml/fml40/roles/dts/machines/mini_tractor.py new file mode 100644 index 0000000..46be356 --- /dev/null +++ b/ml/fml40/roles/dts/machines/mini_tractor.py @@ -0,0 +1,5 @@ +from ml.fml40.roles.dts.machines.forest_machine import ForestMachine + + +class MiniTractor(ForestMachine): + pass diff --git a/ml/fml40/roles/dts/machines/skidder.py b/ml/fml40/roles/dts/machines/skidder.py new file mode 100644 index 0000000..4c7997f --- /dev/null +++ b/ml/fml40/roles/dts/machines/skidder.py @@ -0,0 +1,5 @@ +from ml.fml40.roles.dts.machines.forest_machine import ForestMachine + + +class Skidder(ForestMachine): + pass diff --git a/ml/fml40/roles/dts/machines/wheel_loader.py b/ml/fml40/roles/dts/machines/wheel_loader.py new file mode 100644 index 0000000..b2a9203 --- /dev/null +++ b/ml/fml40/roles/dts/machines/wheel_loader.py @@ -0,0 +1,5 @@ +from ml.fml40.roles.dts.machine.forest_machine import ForestMachine + + +class WheelLoader(ForestMachine): + pass diff --git a/ml/fml40/roles/dts/parts/__init__.py b/ml/fml40/roles/dts/parts/__init__.py new file mode 100644 index 0000000..016d6ab --- /dev/null +++ b/ml/fml40/roles/dts/parts/__init__.py @@ -0,0 +1,5 @@ +from .Grabber import Grabber +from .HarvestingHead import HarvestingHead +from .LogLoadingArea import LogLoadingArea +from .Saw import Saw +from .Winch import Winch \ No newline at end of file diff --git a/ml/fml40/roles/dts/parts/grabber.py b/ml/fml40/roles/dts/parts/grabber.py new file mode 100644 index 0000000..0f6d236 --- /dev/null +++ b/ml/fml40/roles/dts/parts/grabber.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.parts.part import Part + + +class Grabber(Part): + pass diff --git a/ml/fml40/roles/dts/parts/harvesting_head.py b/ml/fml40/roles/dts/parts/harvesting_head.py new file mode 100644 index 0000000..8a1bdeb --- /dev/null +++ b/ml/fml40/roles/dts/parts/harvesting_head.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.parts.part import Part + + +class HarvestingHead(Part): + pass diff --git a/ml/fml40/roles/dts/parts/log_loading_area.py b/ml/fml40/roles/dts/parts/log_loading_area.py new file mode 100644 index 0000000..ca177f8 --- /dev/null +++ b/ml/fml40/roles/dts/parts/log_loading_area.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.parts.part import Part + + +class LogLoadingArea(Part): + pass diff --git a/ml/fml40/roles/dts/parts/saw.py b/ml/fml40/roles/dts/parts/saw.py new file mode 100644 index 0000000..31811ae --- /dev/null +++ b/ml/fml40/roles/dts/parts/saw.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.parts.part import Part + + +class Saw(Part): + pass diff --git a/ml/fml40/roles/dts/parts/winch.py b/ml/fml40/roles/dts/parts/winch.py new file mode 100644 index 0000000..229d1ea --- /dev/null +++ b/ml/fml40/roles/dts/parts/winch.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.parts.part import Part + + +class Winch(Part): + pass diff --git a/ml/fml40/roles/dts/persons/__init__.py b/ml/fml40/roles/dts/persons/__init__.py new file mode 100644 index 0000000..b440d42 --- /dev/null +++ b/ml/fml40/roles/dts/persons/__init__.py @@ -0,0 +1,4 @@ +from .ForestOwner import ForestOwner +from .ForestWorker import ForestWorker +from .MiniTractorOperator import MiniTractorOperator +from .SkidderOperator import SkidderOperator diff --git a/ml/fml40/roles/dts/persons/forest_owner.py b/ml/fml40/roles/dts/persons/forest_owner.py new file mode 100644 index 0000000..bc25b1c --- /dev/null +++ b/ml/fml40/roles/dts/persons/forest_owner.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.persons.person import Person + + +class ForestOwner(Person): + pass diff --git a/ml/fml40/roles/dts/persons/forest_worker.py b/ml/fml40/roles/dts/persons/forest_worker.py new file mode 100644 index 0000000..d84a219 --- /dev/null +++ b/ml/fml40/roles/dts/persons/forest_worker.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.persons.person import Person + + +class ForestWorker(Person): + pass diff --git a/ml/fml40/roles/dts/persons/mini_tractor_operator.py b/ml/fml40/roles/dts/persons/mini_tractor_operator.py new file mode 100644 index 0000000..79dc4ac --- /dev/null +++ b/ml/fml40/roles/dts/persons/mini_tractor_operator.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.persons.person import Person + + +class MiniTractorOperator(Person): + pass diff --git a/ml/fml40/roles/dts/persons/skidder_operator.py b/ml/fml40/roles/dts/persons/skidder_operator.py new file mode 100644 index 0000000..34587df --- /dev/null +++ b/ml/fml40/roles/dts/persons/skidder_operator.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.persons.person import Person + + +class SkidderOperator(Person): + pass diff --git a/ml/fml40/roles/dts/sensors/__init__.py b/ml/fml40/roles/dts/sensors/__init__.py new file mode 100644 index 0000000..0dfae59 --- /dev/null +++ b/ml/fml40/roles/dts/sensors/__init__.py @@ -0,0 +1 @@ +from .VitalitySensor import VilalitySensor \ No newline at end of file diff --git a/ml/fml40/roles/dts/sensors/vitality_sensor.py b/ml/fml40/roles/dts/sensors/vitality_sensor.py new file mode 100644 index 0000000..3cc661b --- /dev/null +++ b/ml/fml40/roles/dts/sensors/vitality_sensor.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.sensors.sensor import Sensor + + +class VilalitySensor(Sensor): + pass diff --git a/ml/fml40/roles/dts/sites/__init__.py b/ml/fml40/roles/dts/sites/__init__.py new file mode 100644 index 0000000..b53fe40 --- /dev/null +++ b/ml/fml40/roles/dts/sites/__init__.py @@ -0,0 +1,3 @@ +from .ForestEnterprise import ForestEnterprise +from .Hauler import Hauler +from .Mill import * diff --git a/ml/fml40/roles/dts/sites/forest_enterprise.py b/ml/fml40/roles/dts/sites/forest_enterprise.py new file mode 100644 index 0000000..4fecd47 --- /dev/null +++ b/ml/fml40/roles/dts/sites/forest_enterprise.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.sites.site import Site + + +class ForestEnterprise(Site): + pass diff --git a/ml/fml40/roles/dts/sites/hauler.py b/ml/fml40/roles/dts/sites/hauler.py new file mode 100644 index 0000000..009fa5d --- /dev/null +++ b/ml/fml40/roles/dts/sites/hauler.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.sites.site import Site + + +class Hauler(Site): + pass diff --git a/ml/fml40/roles/dts/sites/mill/__init__.py b/ml/fml40/roles/dts/sites/mill/__init__.py new file mode 100644 index 0000000..8d9ac19 --- /dev/null +++ b/ml/fml40/roles/dts/sites/mill/__init__.py @@ -0,0 +1,3 @@ +from .Mill import Mill +from .Sawmill import Sawmill +from .Papermill import Papermill diff --git a/ml/fml40/roles/dts/sites/mill/mill.py b/ml/fml40/roles/dts/sites/mill/mill.py new file mode 100644 index 0000000..8c297b1 --- /dev/null +++ b/ml/fml40/roles/dts/sites/mill/mill.py @@ -0,0 +1,5 @@ +from ml.ml40.roles.dts.sites.site import Site + + +class Mill(Site): + pass diff --git a/ml/fml40/roles/dts/sites/mill/papermill.py b/ml/fml40/roles/dts/sites/mill/papermill.py new file mode 100644 index 0000000..b5d34ad --- /dev/null +++ b/ml/fml40/roles/dts/sites/mill/papermill.py @@ -0,0 +1,5 @@ +from ml.fml40.roles.dts.sites.mill import Mill + + +class Papermill(Mill): + pass diff --git a/ml/fml40/roles/dts/sites/mill/sawmill.py b/ml/fml40/roles/dts/sites/mill/sawmill.py new file mode 100644 index 0000000..354ab76 --- /dev/null +++ b/ml/fml40/roles/dts/sites/mill/sawmill.py @@ -0,0 +1,5 @@ +from ml.fml40.roles.dts.sites.mill import Mill + + +class Sawmill(Mill): + pass diff --git a/ml/fml40/roles/dts/ways/__init__.py b/ml/fml40/roles/dts/ways/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/fml40/roles/hmis/__init__.py b/ml/fml40/roles/hmis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/fml40/roles/services/__init__.py b/ml/fml40/roles/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/fml40_factory.py b/ml/fml40_factory.py new file mode 100644 index 0000000..0bee2a4 --- /dev/null +++ b/ml/fml40_factory.py @@ -0,0 +1,4 @@ +from ml.dt_factory import DT_FACTORY +from ml.functionalities.functionalities import FUNCTIONALITY_FACTORY + +FML40_FACTORY = {**DT_FACTORY, ** FUNCTIONALITY_FACTORY} diff --git a/ml/identifier.py b/ml/identifier.py new file mode 100644 index 0000000..9a52068 --- /dev/null +++ b/ml/identifier.py @@ -0,0 +1,19 @@ +class ID(object): + """ + Define primitive variable type ID + """ + def __init__(self, identifier): + self.__identifier = identifier + + @property + def ID(self): + return self.__identifier + + @ID.setter + def ID(self, value): + """ + TODO specification ID e.g. s3i:uuid or gSFL:uuid? + :param value: identifier + :return: identifier + """ + self.__identifier = value diff --git a/ml/managed_actor.py b/ml/managed_actor.py new file mode 100644 index 0000000..442025c --- /dev/null +++ b/ml/managed_actor.py @@ -0,0 +1,29 @@ +"""Provides an encapsulation class for digital twins allowing to run +it in a dedicated thread.""" +import pykka +from s3i import TokenType +from ml.app_logger import APP_LOGGER + + +class ManagedActor(pykka.ThreadingActor): + """Provides an encapsulation class for digital twins allowing to run + it in a dedicated thread.""" + + def __init__(self, name, refManagingActor): + super().__init__() + self.managing_actor = refManagingActor.proxy() + self.managing_actor.register_managed_actor(name, self.actor_ref.proxy()) + + def on_receive(self, message): + """Prints a greatings formula and message to stdout. + + """ + print( + """Hello, I'm a Managed Actor of a digital twin with the name {}.\n + You send me a message: {}""".format( + self.__name, message + ) + ) + + def from_json(self, json_obj): + self.__name = json_obj.get("name", "") diff --git a/ml/managing_actor.py b/ml/managing_actor.py new file mode 100644 index 0000000..5e42801 --- /dev/null +++ b/ml/managing_actor.py @@ -0,0 +1,69 @@ +"""This is the administration shell of the forwarder. + It receives the service replys which are addressed to + the forwarder and schedules the further forwarder actions + when all necessary replys have arrived. + """ +import pykka +from s3i import TokenType +from s3i import Directory +from ml.app_logger import APP_LOGGER + + +class ManagingActor(pykka.ThreadingActor): + """This is the administration shell of the forwarder. + It receives the service replys which are addressed to + the forwarder and schedules the further forwarder actions + when all necessary replys have arrived. + """ + + def __init__(self): + super().__init__() + self.proxy_functionalities = {} + + def get_repr(self): + pass + # s3i_dir = Directory( + # s3i_dir_url="https://dir.s3i.vswf.dev/api/2/", token=self.access_token + # ) + # response = s3i_dir.queryThingIDBased(self.thing_id) + # return response + + def sync_down(self): + # TODO: Set attributes and values on top level + pass + + def on_receive(self, message): + """ Prints a greatings formula and message to stdout. + + :param message: Printable object + + """ + print("Hello, I'm a secretary\nYou send me a message %s" % message) + + def register_managed_actor(self, name, proxy): + """Adds functionaliy proxy under the name name. + + :param name: String identifying proxy + :param proxy: Functioanlity like object. + + """ + self.proxy_functionalities[name] = proxy + + def on_stop(self): + """Stops all functionality threads. + + """ + print("Stopping") + for key in self.proxy_functionalities: + pykka.ActorRegistry.get_by_class_name(key)[0].stop() + + def accept(self, functionality, parameters): + """FIXME! briefly describe function + + :param functionality: + :param parameters: + :returns: + :rtype: + + """ + self.proxy_functionalities[functionality].visit(self, parameters) diff --git a/ml/ml40/features/__init__.py b/ml/ml40/features/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/ml40/features/feature.py b/ml/ml40/features/feature.py new file mode 100644 index 0000000..47bd449 --- /dev/null +++ b/ml/ml40/features/feature.py @@ -0,0 +1,31 @@ +from ml.managed_actor import ManagedActor +from ml.identifier import ID + +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 + self.__subfeatures = [] + + @property + def type_name(self): + return self.__class_name + + @property + def name(self): + return self.__name + + @property + def subfeatures(self): + return self.__subfeatures + + @property + def identifier(self): + return self.__identifier + + 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", "")) diff --git a/ml/ml40/features/functionalities/__init__.py b/ml/ml40/features/functionalities/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/ml40/features/functionalities/accepts_jobs.py b/ml/ml40/features/functionalities/accepts_jobs.py new file mode 100644 index 0000000..4c99518 --- /dev/null +++ b/ml/ml40/features/functionalities/accepts_jobs.py @@ -0,0 +1,21 @@ +from ml.ml40.features.functionalities.functionality import Functionality +from ml.ml40.features.properties.values.documents.jobs.job import Job +from ml.ml40.features.properties.values.documents.jobs.job_status import JobStatus +from ml.identifier import ID + + +class AcceptsJobs(Functionality): + def __init__(self, name, ref_managing_actor): + super(AcceptsJobs, self).__init__( + name=name, ref_managing_actor=ref_managing_actor + ) + self.__jobs = dict() + + def acceptJob(self, job: Job) -> bool: + print("I am checking if i can accept job {}".format(job)) + + def queryJobStatus(self, identifier: ID) -> JobStatus: + pass + + def removeJob(self, identifier: ID) -> bool: + pass diff --git a/ml/ml40/features/functionalities/accepts_reports.py b/ml/ml40/features/functionalities/accepts_reports.py new file mode 100644 index 0000000..62e84c6 --- /dev/null +++ b/ml/ml40/features/functionalities/accepts_reports.py @@ -0,0 +1,12 @@ +from ml.ml40.features.functionalities import Functionality +from ml.ml40.features.properties.document.report import Report + + +class AcceptsReports(Functionality): + def __init__(self, name, ref_managing_actor): + super(AcceptsReports, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor) + + def acceptReport(self, report: Report): + pass diff --git a/ml/ml40/features/functionalities/clears_jobs.py b/ml/ml40/features/functionalities/clears_jobs.py new file mode 100644 index 0000000..0f60ca2 --- /dev/null +++ b/ml/ml40/features/functionalities/clears_jobs.py @@ -0,0 +1,12 @@ +from ml.ml40.features.functionality import Functionality +from ml.ml40.features.properties.value.document.job import Job + + +class ClearsJobs(Functionality): + def __init__(self, name, identifier, ref_managing_actor): + super(ClearsJobs, self).__init__( + name=name, identifier=identifier, ref_managing_actor=ref_managing_actor + ) + + def clear(self, job: Job) -> bool: + pass diff --git a/ml/ml40/features/functionalities/functionality.py b/ml/ml40/features/functionalities/functionality.py new file mode 100644 index 0000000..1135143 --- /dev/null +++ b/ml/ml40/features/functionalities/functionality.py @@ -0,0 +1,37 @@ +import uuid +from s3i import ServiceReply +from ml.ml40.features.feature import Feature + + +class Functionality(Feature): + """Genric implementation of a Functionaliy.""" + + def __init__(self, name, ref_managing_actor): + super(Functionality, self).__init__( + name=name, ref_managing_actor=ref_managing_actor + ) + # TODO: Make it private + self.service_reply = None + + 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 + sender_uuid = thing_proxy.thing_id.get() + msg_uuid = str(uuid.uuid4()) + receiver_uuids = [json_body.get("sender", "")] + service_type = json_body.get("serviceType", "") + receiver_uuids = json_body.get("replyingToMessage", "") + reply_to_msg_uuid = json_body.get("identifier", "") + self.service_reply.fillServiceReply( + sender_uuid, + receiver_uuids, + service_type, + results, + msg_uuid, + reply_to_msg_uuid, + ) + return self.service_reply diff --git a/ml/ml40/features/functionalities/manages_jobs.py b/ml/ml40/features/functionalities/manages_jobs.py new file mode 100644 index 0000000..f95019c --- /dev/null +++ b/ml/ml40/features/functionalities/manages_jobs.py @@ -0,0 +1,20 @@ +from ml.ml40.features.functionalities.functionality import Functionality +from ml.ml40.features.properties.values.documents.jobs.job import Job +from ml.ml40.features.properties.values.documents.reports.report import Report +from ml.identifier import ID + + +class ManagesJobs(Functionality): + def __init__(self, name, ref_managing_actor, *args, **kwargs): + super().__init__(name=name, ref_managing_actor=ref_managing_actor) + + def acceptReport(self, report: Report): + print("accepts Report ..") + pass + + def assignJob(self, job: Job, identifier: ID): + print("I assign the job {0} to Komatsu forwarder...".format(job)) + self.managing_actor.on_assign_forwarding_job(job) + + def from_json(self, json_obj): + super().from_json(json_obj) diff --git a/ml/ml40/features/functionalities/plans_routes.py b/ml/ml40/features/functionalities/plans_routes.py new file mode 100644 index 0000000..66cda73 --- /dev/null +++ b/ml/ml40/features/functionalities/plans_routes.py @@ -0,0 +1,11 @@ +from ml.ml40.features.functionalities import Functionality +from ml.ml40.features.properties import Route, Location + + +class PlansRoutes(Functionality): + def __init__(self, name, ref_managing_actor): + super(PlansRoutes, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + + def planRoute(self, start: Location, goal: Location) -> Route: + pass diff --git a/ml/ml40/features/functionalities/provides_map_data.py b/ml/ml40/features/functionalities/provides_map_data.py new file mode 100644 index 0000000..adf3ef9 --- /dev/null +++ b/ml/ml40/features/functionalities/provides_map_data.py @@ -0,0 +1,11 @@ +from ml.ml40.features.functionality import Functionality +from ml.fml40.features.properties.value.document.report import PassabilityReport + + +class ProvidesMapData(Functionality): + def __init__(self, name, ref_managing_actor): + super(ProvidesMapData, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + + def getMapData(self, map: int) -> PassabilityReport: + pass diff --git a/ml/ml40/features/functionalities/provides_operational_data.py b/ml/ml40/features/functionalities/provides_operational_data.py new file mode 100644 index 0000000..5f56bcc --- /dev/null +++ b/ml/ml40/features/functionalities/provides_operational_data.py @@ -0,0 +1,17 @@ +from ml.ml40.features.functionalities import Functionality +from ml.ml40.features.properties.value.document.report import Report + + +class ProvidesOperationalData(Functionality): + def __init__(self, name, ref_managing_actor): + super(ProvidesOperationalData, self).__init__( + name=name, ref_managing_actor=ref_managing_actor + ) + + def getOperationalData(self) -> Report: + print("make the operational data report...") + return "this is a simply report" + + + def from_json(self, json_obj): + super.from_json(json_obj) diff --git a/ml/ml40/features/functionalities/renders.py b/ml/ml40/features/functionalities/renders.py new file mode 100644 index 0000000..ac00fc1 --- /dev/null +++ b/ml/ml40/features/functionalities/renders.py @@ -0,0 +1,10 @@ +from ml.ml40.features.functionalies import Functionality +from ml.identifier import ID + + +class Renders(Functionality): + def __init__(self, name, ref_managing_actor): + super(Renders, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + + def create3DVideo(self, identifier: ID) -> bytes: + pass diff --git a/ml/ml40/features/properties/__init__.py b/ml/ml40/features/properties/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/ml40/features/properties/association.py b/ml/ml40/features/properties/association.py new file mode 100644 index 0000000..606e8ee --- /dev/null +++ b/ml/ml40/features/properties/association.py @@ -0,0 +1,6 @@ +from ml.ml40.features.properties.property import Property + + +class Association(Property): + def __init__(self, name, ref_managing_actor): + super(Association, self).__init__(name=name, ref_managing_actor=ref_managing_actor) diff --git a/ml/ml40/features/properties/composite.py b/ml/ml40/features/properties/composite.py new file mode 100644 index 0000000..f4d16d9 --- /dev/null +++ b/ml/ml40/features/properties/composite.py @@ -0,0 +1,15 @@ +from ml.ml40.features.properties.association import Association + + +class Composite(Association): + 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 + self.__targets = [] + + + def from_json(self, json_obj): + super().from_json(json_obj) + self.__targets = json_obj.get("targets", []) diff --git a/ml/ml40/features/properties/property.py b/ml/ml40/features/properties/property.py new file mode 100644 index 0000000..e6c556c --- /dev/null +++ b/ml/ml40/features/properties/property.py @@ -0,0 +1,5 @@ +from ml.ml40.features.feature import Feature + +class Property(Feature): + def __init__(self, name, ref_managing_actor): + super(Property, self).__init__(name=name, ref_managing_actor=ref_managing_actor) diff --git a/ml/ml40/features/properties/shared.py b/ml/ml40/features/properties/shared.py new file mode 100644 index 0000000..8bfb31f --- /dev/null +++ b/ml/ml40/features/properties/shared.py @@ -0,0 +1,12 @@ +from ml.ml40.features.properties.association import Association + + +class Shared(Association): + def __init__(self, name, ref_managing_actor): + super(Shared, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + # INFO: targets is a list of things + self.__targets = [] + + def from_json(self, json_obj): + super().from_json(json_obj) + self.__targets = json_obj.get("targets", []) diff --git a/ml/ml40/features/properties/values/__init__.py b/ml/ml40/features/properties/values/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/ml40/features/properties/values/address.py b/ml/ml40/features/properties/values/address.py new file mode 100644 index 0000000..192573e --- /dev/null +++ b/ml/ml40/features/properties/values/address.py @@ -0,0 +1,13 @@ +from ml.ml40.features.properties.values.value import Value + + +class Address(Value): + def __init__(self, name, ref_managing_actor, city: str, country: str, + street: str, streetnumber: str, zip_code: str): + super(Address, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.city = city + self.country = country + self.street = street + self.streetnumber = streetnumber + self.zip_code = zip_code diff --git a/ml/ml40/features/properties/values/dimensions.py b/ml/ml40/features/properties/values/dimensions.py new file mode 100644 index 0000000..1df6e15 --- /dev/null +++ b/ml/ml40/features/properties/values/dimensions.py @@ -0,0 +1,12 @@ +from ml.ml40.features.properties.values.value import Value + + +class Dimensions(Value): + def __init__(self, name, ref_managing_actor, + height: float, length: float, weight: float, width: float): + super(Dimensions, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.height = height + self.length = length + self.weight = weight + self.width = width diff --git a/ml/ml40/features/properties/values/documents/__init__.py b/ml/ml40/features/properties/values/documents/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/ml40/features/properties/values/documents/document.py b/ml/ml40/features/properties/values/documents/document.py new file mode 100644 index 0000000..abb8fed --- /dev/null +++ b/ml/ml40/features/properties/values/documents/document.py @@ -0,0 +1,7 @@ +from ml.ml40.features.properties.values.value import Value + + +class Document(Value): + def __init__(self, name, ref_managing_actor): + super(Document, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) diff --git a/ml/ml40/features/properties/values/documents/jobs/__init__.py b/ml/ml40/features/properties/values/documents/jobs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/ml40/features/properties/values/documents/jobs/generic_job.py b/ml/ml40/features/properties/values/documents/jobs/generic_job.py new file mode 100644 index 0000000..27ef2ed --- /dev/null +++ b/ml/ml40/features/properties/values/documents/jobs/generic_job.py @@ -0,0 +1,8 @@ +from ml.ml40.feature.properties.value.document.job import Job + + +class GenericJob(Job): + def __init__(self, name, ref_managing_actor, content: str): + super(GenericJob, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.content = content diff --git a/ml/ml40/features/properties/values/documents/jobs/job.py b/ml/ml40/features/properties/values/documents/jobs/job.py new file mode 100644 index 0000000..d084cb4 --- /dev/null +++ b/ml/ml40/features/properties/values/documents/jobs/job.py @@ -0,0 +1,9 @@ +from ml.ml40.features.properties.values.documents.document import Document +from ml.ml40.features.properties.values.documents.jobs.job_status import JobStatus + + +class Job(Document): + def __init__(self, name, ref_managing_actor, status: JobStatus): + super(Job, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.status = status diff --git a/ml/ml40/features/properties/values/documents/jobs/job_list.py b/ml/ml40/features/properties/values/documents/jobs/job_list.py new file mode 100644 index 0000000..2e6d648 --- /dev/null +++ b/ml/ml40/features/properties/values/documents/jobs/job_list.py @@ -0,0 +1,8 @@ +from ml.ml40.features.properties.values.documents.jobs.job import Job + + +class JobList(Job): + def __init__(self, name, ref_managing_actor): + super(JobList, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.jobs = list() diff --git a/ml/ml40/features/properties/values/documents/jobs/job_status.py b/ml/ml40/features/properties/values/documents/jobs/job_status.py new file mode 100644 index 0000000..dcb708a --- /dev/null +++ b/ml/ml40/features/properties/values/documents/jobs/job_status.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class JobStatus(Enum): + Pending = 0 + InProgress = 1 + Complete = 2 diff --git a/ml/ml40/features/properties/values/documents/reports/__init__.py b/ml/ml40/features/properties/values/documents/reports/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/ml40/features/properties/values/documents/reports/report.py b/ml/ml40/features/properties/values/documents/reports/report.py new file mode 100644 index 0000000..a86a4a7 --- /dev/null +++ b/ml/ml40/features/properties/values/documents/reports/report.py @@ -0,0 +1,9 @@ +from ml.ml40.features.properties.values.documents.document import Document + + +class Report(Document): + def __init__(self, name, ref_managing_actor): + super(Report, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor + ) diff --git a/ml/ml40/features/properties/values/generic_property.py b/ml/ml40/features/properties/values/generic_property.py new file mode 100644 index 0000000..6f7c7f6 --- /dev/null +++ b/ml/ml40/features/properties/values/generic_property.py @@ -0,0 +1,10 @@ +from ml.ml40.features.properties.values.value import Value + + +class GenericProperty(Value): + def __init__(self, name, ref_managing_actor, value: str): + super(GenericProperty, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.value = value + if not isinstance(value, str): + raise TypeError("except str type") diff --git a/ml/ml40/features/properties/values/liquid_filling_level.py b/ml/ml40/features/properties/values/liquid_filling_level.py new file mode 100644 index 0000000..3cd20de --- /dev/null +++ b/ml/ml40/features/properties/values/liquid_filling_level.py @@ -0,0 +1,10 @@ +from ml.ml40.features.properties.values.value import Value + + +class LiquidFillingLevel(Value): + def __init__(self, name, ref_managing_actor, + currentLevel: float, maxLevel: float): + super(LiquidFillingLevel, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.currentLevel = currentLevel + self.maxLevel = maxLevel diff --git a/ml/ml40/features/properties/values/location.py b/ml/ml40/features/properties/values/location.py new file mode 100644 index 0000000..7c1d8b2 --- /dev/null +++ b/ml/ml40/features/properties/values/location.py @@ -0,0 +1,15 @@ +from ml.ml40.features.properties.values.value import Value + + +class Location(Value): + def __init__(self, name, ref_managing_actor): + super(Location, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + self.__latitude = 0 + self.__longitude = 0 + self.__orientation = 0 + + def from_json(self, json_obj): + super().from_json(json_obj) + self.__latitude = json_obj.get("latitude", "") + self.__longitude = json_obj.get("longitude", "") + self.__orientation = json_obj.get("orientation", "") diff --git a/ml/ml40/features/properties/values/moisture.py b/ml/ml40/features/properties/values/moisture.py new file mode 100644 index 0000000..7a4f82a --- /dev/null +++ b/ml/ml40/features/properties/values/moisture.py @@ -0,0 +1,20 @@ +from ml.ml40.features.properties.values.value import Value + + +class Moisture(Value): + def __init__(self, name, ref_managing_actor): + super(Moisture, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.__humidity = 0 + + @property + def humidity(self): + return self.__humidity + + @humidity.setter + def humidity(self, value): + self.__humidity = value + + def from_json(self, json_obj): + super().from_json(json_obj) + self.__humidity = json_obj.get("humidity", 0) diff --git a/ml/ml40/features/properties/values/personal_name.py b/ml/ml40/features/properties/values/personal_name.py new file mode 100644 index 0000000..382d84d --- /dev/null +++ b/ml/ml40/features/properties/values/personal_name.py @@ -0,0 +1,10 @@ +from ml.ml40.features.properties.values.value import Value + + +class PersonalName(Value): + def __init__(self, name, ref_managing_actor, firstname: str, + lastname: str): + super(PersonalName, + self).__init__(name=name, ref_managing_actor=ref_managing_actor) + self.firstname = firstname + self.lastname = lastname diff --git a/ml/ml40/features/properties/values/rotational_speed.py b/ml/ml40/features/properties/values/rotational_speed.py new file mode 100644 index 0000000..46c4699 --- /dev/null +++ b/ml/ml40/features/properties/values/rotational_speed.py @@ -0,0 +1,8 @@ +from ml.ml40.features.properties.values.value import Value + + +class RotationalSpeed(Value): + def __init__(self, name, ref_managing_actor, rpm): + super(RotationalSpeed, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.rpm = rpm diff --git a/ml/ml40/features/properties/values/route.py b/ml/ml40/features/properties/values/route.py new file mode 100644 index 0000000..cc00094 --- /dev/null +++ b/ml/ml40/features/properties/values/route.py @@ -0,0 +1,5 @@ +from ml.ml40.features.properties.values.value import Value + + +class Route(Value): + pass diff --git a/ml/ml40/features/properties/values/temperature.py b/ml/ml40/features/properties/values/temperature.py new file mode 100644 index 0000000..539d980 --- /dev/null +++ b/ml/ml40/features/properties/values/temperature.py @@ -0,0 +1,23 @@ +from ml.ml40.features.properties.values.value import Value + + +class Temperature(Value): + def __init__(self, name, ref_managing_actor, temperature: float): + super(Temperature, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.__temperature = temperature + + @property + def temperature(self): + return self.__temperature + + @temperature.setter + def temperature(self, value): + if isinstance(value, float): + self.__temperature = value + else: + raise TypeError + + + + diff --git a/ml/ml40/features/properties/values/time_slot.py b/ml/ml40/features/properties/values/time_slot.py new file mode 100644 index 0000000..dd7e020 --- /dev/null +++ b/ml/ml40/features/properties/values/time_slot.py @@ -0,0 +1,10 @@ +from ml.ml40.features.properties.values.value import Value + + +class TimeSlot(Value): + def __init__(self, name, ref_managing_actor, + end: int, start: int): + super(TimeSlot, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.end = end + self.start = start diff --git a/ml/ml40/features/properties/values/value.py b/ml/ml40/features/properties/values/value.py new file mode 100644 index 0000000..6245e42 --- /dev/null +++ b/ml/ml40/features/properties/values/value.py @@ -0,0 +1,22 @@ +from ml.ml40.features.properties.property import Property + + +class Value(Property): + def __init__(self, name, ref_managing_actor): + super(Value, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + self._valid_to = "" + self._valid_from = "" + + @property + def valid_to(self): + return self._valid_to + + def set_valid_to(self, valid_to): + self._valid_to = valid_to + + @property + def valid_from(self): + return self._valid_from + + def set_valid_from(self, valid_from): + self._valid_from = valid_from diff --git a/ml/ml40/features/properties/values/weight.py b/ml/ml40/features/properties/values/weight.py new file mode 100644 index 0000000..2c6fdd3 --- /dev/null +++ b/ml/ml40/features/properties/values/weight.py @@ -0,0 +1,18 @@ +from ml.ml40.features.properties.values.value import Value + + +class Weight(Value): + def __init__(self, name, ref_managing_actor): + super(Weight, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + self._weight = 0 + + @property + def weight(self): + return self._weight + + def get_weight(self): + return self._weight + + def from_json(self, json_obj): + super().from_json(json_obj) + self._weight = json_obj.get("weight", 0) diff --git a/ml/ml40/roles/__init__.py b/ml/ml40/roles/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/ml/ml40/roles/__init__.py @@ -0,0 +1 @@ + diff --git a/ml/ml40/roles/dts/__init__.py b/ml/ml40/roles/dts/__init__.py new file mode 100644 index 0000000..d090894 --- /dev/null +++ b/ml/ml40/roles/dts/__init__.py @@ -0,0 +1,8 @@ +from .DT import DT +from .HandheldDevice import HandheldDevice +from .Person import Person, MachineOperator +from .Machine import Machine +from .Way import Way +from .Sensor import SoilSensor, Sensor, AirSensor +from .Site import Site +from .Part import Part, Crane, Engine, Scale, Tank diff --git a/ml/ml40/roles/dts/dt.py b/ml/ml40/roles/dts/dt.py new file mode 100644 index 0000000..3bfe234 --- /dev/null +++ b/ml/ml40/roles/dts/dt.py @@ -0,0 +1,7 @@ +from modelling_language.ml40.role.Role import Role + + +class DT(Role): + def __init__(self, name, ref_managing_actor): + super(DT, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) diff --git a/ml/ml40/roles/dts/handheld_devices/__init__.py b/ml/ml40/roles/dts/handheld_devices/__init__.py new file mode 100644 index 0000000..dcf572d --- /dev/null +++ b/ml/ml40/roles/dts/handheld_devices/__init__.py @@ -0,0 +1 @@ +from .HandheldDevice import HandheldDevice \ No newline at end of file diff --git a/ml/ml40/roles/dts/handheld_devices/handheld_device.py b/ml/ml40/roles/dts/handheld_devices/handheld_device.py new file mode 100644 index 0000000..fe67c13 --- /dev/null +++ b/ml/ml40/roles/dts/handheld_devices/handheld_device.py @@ -0,0 +1,7 @@ +from modelling_language.ml40.role.DT import DT + + +class HandheldDevice(DT): + pass + + diff --git a/ml/ml40/roles/dts/machines/__init__.py b/ml/ml40/roles/dts/machines/__init__.py new file mode 100644 index 0000000..fed41ed --- /dev/null +++ b/ml/ml40/roles/dts/machines/__init__.py @@ -0,0 +1 @@ +from .Machine import Machine \ No newline at end of file diff --git a/ml/ml40/roles/dts/machines/machine.py b/ml/ml40/roles/dts/machines/machine.py new file mode 100644 index 0000000..97eaf3e --- /dev/null +++ b/ml/ml40/roles/dts/machines/machine.py @@ -0,0 +1,6 @@ +from modelling_language.ml40.role.DT import DT + + +class Machine(DT): + def __init__(self): + pass diff --git a/ml/ml40/roles/dts/parts/__init__.py b/ml/ml40/roles/dts/parts/__init__.py new file mode 100644 index 0000000..121a68b --- /dev/null +++ b/ml/ml40/roles/dts/parts/__init__.py @@ -0,0 +1,5 @@ +from .Part import Part +from .Crane import Crane +from .Engine import Engine +from .Scale import Scale +from .Tank import Tank diff --git a/ml/ml40/roles/dts/parts/crane.py b/ml/ml40/roles/dts/parts/crane.py new file mode 100644 index 0000000..032bb89 --- /dev/null +++ b/ml/ml40/roles/dts/parts/crane.py @@ -0,0 +1,6 @@ +from modelling_language.ml40.role.DT.Part import Part + + +class Crane(Part): + pass + diff --git a/ml/ml40/roles/dts/parts/engine.py b/ml/ml40/roles/dts/parts/engine.py new file mode 100644 index 0000000..1ae3dd8 --- /dev/null +++ b/ml/ml40/roles/dts/parts/engine.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.role.DT.Part import Part + + +class Engine(Part): + pass diff --git a/ml/ml40/roles/dts/parts/part.py b/ml/ml40/roles/dts/parts/part.py new file mode 100644 index 0000000..bb71cf5 --- /dev/null +++ b/ml/ml40/roles/dts/parts/part.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.role.DT import DT + + +class Part(DT): + pass \ No newline at end of file diff --git a/ml/ml40/roles/dts/parts/scale.py b/ml/ml40/roles/dts/parts/scale.py new file mode 100644 index 0000000..769e185 --- /dev/null +++ b/ml/ml40/roles/dts/parts/scale.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.role.DT.Part import Part + + +class Scale(Part): + pass \ No newline at end of file diff --git a/ml/ml40/roles/dts/parts/tank.py b/ml/ml40/roles/dts/parts/tank.py new file mode 100644 index 0000000..cd6a314 --- /dev/null +++ b/ml/ml40/roles/dts/parts/tank.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.role.DT.Part import Part + + +class Tank(Part): + pass \ No newline at end of file diff --git a/ml/ml40/roles/dts/persons/__init__.py b/ml/ml40/roles/dts/persons/__init__.py new file mode 100644 index 0000000..b23ce9d --- /dev/null +++ b/ml/ml40/roles/dts/persons/__init__.py @@ -0,0 +1,2 @@ +from .Person import Person +from .MachineOperator import MachineOperator \ No newline at end of file diff --git a/ml/ml40/roles/dts/persons/machine_operator.py b/ml/ml40/roles/dts/persons/machine_operator.py new file mode 100644 index 0000000..892c71d --- /dev/null +++ b/ml/ml40/roles/dts/persons/machine_operator.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.role.DT.Person import Person + + +class MachineOperator(Person): + pass diff --git a/ml/ml40/roles/dts/persons/person.py b/ml/ml40/roles/dts/persons/person.py new file mode 100644 index 0000000..608ee6c --- /dev/null +++ b/ml/ml40/roles/dts/persons/person.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.role.DT import DT + + +class Person(DT): + pass \ No newline at end of file diff --git a/ml/ml40/roles/dts/sensors/__init__.py b/ml/ml40/roles/dts/sensors/__init__.py new file mode 100644 index 0000000..91a60e3 --- /dev/null +++ b/ml/ml40/roles/dts/sensors/__init__.py @@ -0,0 +1,4 @@ +from .Sensor import Sensor +from .AirSensor import AirSensor +from .SoilSensor import SoilSensor + diff --git a/ml/ml40/roles/dts/sensors/air_sensor.py b/ml/ml40/roles/dts/sensors/air_sensor.py new file mode 100644 index 0000000..6028165 --- /dev/null +++ b/ml/ml40/roles/dts/sensors/air_sensor.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.role.DT.Sensor import Sensor + + +class AirSensor(Sensor): + pass \ No newline at end of file diff --git a/ml/ml40/roles/dts/sensors/sensor.py b/ml/ml40/roles/dts/sensors/sensor.py new file mode 100644 index 0000000..1c6f981 --- /dev/null +++ b/ml/ml40/roles/dts/sensors/sensor.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.role.DT import DT + + +class Sensor(DT): + pass \ No newline at end of file diff --git a/ml/ml40/roles/dts/sensors/soil_sensor.py b/ml/ml40/roles/dts/sensors/soil_sensor.py new file mode 100644 index 0000000..61b7032 --- /dev/null +++ b/ml/ml40/roles/dts/sensors/soil_sensor.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.role.DT.Sensor import Sensor + + +class SoilSensor(Sensor): + pass \ No newline at end of file diff --git a/ml/ml40/roles/dts/sites/__init__.py b/ml/ml40/roles/dts/sites/__init__.py new file mode 100644 index 0000000..7a5c7f3 --- /dev/null +++ b/ml/ml40/roles/dts/sites/__init__.py @@ -0,0 +1 @@ +from .Site import Site \ No newline at end of file diff --git a/ml/ml40/roles/dts/sites/site.py b/ml/ml40/roles/dts/sites/site.py new file mode 100644 index 0000000..6073307 --- /dev/null +++ b/ml/ml40/roles/dts/sites/site.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.role.DT import DT + + +class Site(DT): + pass \ No newline at end of file diff --git a/ml/ml40/roles/dts/ways/__init__.py b/ml/ml40/roles/dts/ways/__init__.py new file mode 100644 index 0000000..30825fc --- /dev/null +++ b/ml/ml40/roles/dts/ways/__init__.py @@ -0,0 +1 @@ +from .Way import Way \ No newline at end of file diff --git a/ml/ml40/roles/dts/ways/way.py b/ml/ml40/roles/dts/ways/way.py new file mode 100644 index 0000000..2ee0df5 --- /dev/null +++ b/ml/ml40/roles/dts/ways/way.py @@ -0,0 +1,5 @@ +from modelling_language.ml40.role.DT import DT + + +class Way(DT): + pass diff --git a/ml/ml40/roles/hmis/__init__.py b/ml/ml40/roles/hmis/__init__.py new file mode 100644 index 0000000..7aeb0d0 --- /dev/null +++ b/ml/ml40/roles/hmis/__init__.py @@ -0,0 +1,5 @@ +from .HMI import HMI +from .App import App +from .Dashboard import Dashboard +from .HMD import HMD +from .MachineUI import MachineUI diff --git a/ml/ml40/roles/hmis/app.py b/ml/ml40/roles/hmis/app.py new file mode 100644 index 0000000..21fcac5 --- /dev/null +++ b/ml/ml40/roles/hmis/app.py @@ -0,0 +1,8 @@ +from modelling_language.ml40.role.HMI import HMI + + +class App(HMI): + def __init__(self, name, ref_managing_actor): + super(App, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor) diff --git a/ml/ml40/roles/hmis/dashboard.py b/ml/ml40/roles/hmis/dashboard.py new file mode 100644 index 0000000..9f21448 --- /dev/null +++ b/ml/ml40/roles/hmis/dashboard.py @@ -0,0 +1,8 @@ +from modelling_language.ml40.role.HMI import HMI + + +class Dashboard(HMI): + def __init__(self, name, ref_managing_actor): + super(Dashboard, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor) diff --git a/ml/ml40/roles/hmis/hmd.py b/ml/ml40/roles/hmis/hmd.py new file mode 100644 index 0000000..05065a6 --- /dev/null +++ b/ml/ml40/roles/hmis/hmd.py @@ -0,0 +1,8 @@ +from modelling_language.ml40.role.HMI import HMI + + +class HMD(HMI): + def __init__(self, name, ref_managing_actor): + super(HMD, self).__init__( + name=name, + ref_managing_actor=ref_managing_actor) diff --git a/ml/ml40/roles/hmis/hmi.py b/ml/ml40/roles/hmis/hmi.py new file mode 100644 index 0000000..12a5951 --- /dev/null +++ b/ml/ml40/roles/hmis/hmi.py @@ -0,0 +1,7 @@ +from modelling_language.ml40.role.Role import Role + + +class HMI(Role): + def __init__(self, name, ref_managing_actor): + super(HMI, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) diff --git a/ml/ml40/roles/hmis/machine_ui.py b/ml/ml40/roles/hmis/machine_ui.py new file mode 100644 index 0000000..664eb8e --- /dev/null +++ b/ml/ml40/roles/hmis/machine_ui.py @@ -0,0 +1,7 @@ +from modelling_language.ml40.role.HMI import HMI + + +class MachineUI(HMI): + def __init__(self, name, ref_managing_actor): + super(MachineUI, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) diff --git a/ml/ml40/roles/role.py b/ml/ml40/roles/role.py new file mode 100644 index 0000000..0d4b1ff --- /dev/null +++ b/ml/ml40/roles/role.py @@ -0,0 +1,3 @@ +class Role(): + def __init__(self, name, ref_managing_actor): + pass diff --git a/ml/ml40/roles/servives/__init__.py b/ml/ml40/roles/servives/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/ml/ml40/roles/servives/__init__.py @@ -0,0 +1 @@ + diff --git a/ml/ml40/roles/servives/service.py b/ml/ml40/roles/servives/service.py new file mode 100644 index 0000000..3833312 --- /dev/null +++ b/ml/ml40/roles/servives/service.py @@ -0,0 +1,6 @@ +from ml.ml40.roles.role import Role + + +class Service(Role): + def __init__(self, name, ref_managing_actor): + super(Service, self).__init__(name=name, ref_managing_actor=ref_managing_actor) diff --git a/ml/thing.py b/ml/thing.py new file mode 100644 index 0000000..a88b931 --- /dev/null +++ b/ml/thing.py @@ -0,0 +1,283 @@ +import ast +import threading +import time +import websocket +import json +import os +import uuid +from s3i import IdentityProvider, TokenType, Broker, GetValueReply +from s3i import Directory + +# from pynput import keyboard +from ml.managing_actor import ManagingActor +from ml.tools import BColors +from ml.tools import send_request +from ml.tools import send_message +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 BaseVariable(object): + IDP_URL = "https://idp.s3i.vswf.dev/" + IDP_REALM = "KWH" + BROKER_HOST = "rabbitmq.s3i.vswf.dev" + REPO_WWS_URL = "wss://ditto.s3i.vswf.dev/ws/2" + REPO_URL = "https://ditto.s3i.vswf.dev/api/2/" + 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, + client_secret, + model: dict, + grant_type: str, + is_broker: bool, + is_repo: bool, + username=None, + password=None, + endpoint=None, + ): + super(Thing, self).__init__() + self.__thing_id = model.get("thingId", "") + self.__policy_id = model.get("policyId", "") + self.__grant_type = grant_type + 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.__ws_connected = False + self.__observers = [] + self._broker = None + self._ws = None + self.roles = [] + self.features = {} + + attributes = model.get("attributes", None) + self.__name = "" + self.__class_name = "" + self.__type_name = "" + self.__represents = "" + self.__thing_structure = {} + self.__default_hmi = "" + 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", "") + + def send_message(self, receiver_ids, req): + send_message(self.__access_token, receiver_ids, req) + + def send_service_message(self, receiver_ids, service_class_name, parameters): + APP_LOGGER.info(f"Sending message {service_class_name}") + send_request( + self.__access_token, + self.thing_id, + receiver_ids, + service_class_name, + 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) + message_type = body_json.get("messageType", "") + if message_type == "serviceRequest": + self.on_service_request(body_json) + elif message_type == "getValueRequest": + self.on_get_value_request(body_json) + elif message_type == "getValueReply": + 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): + return self.__class_name + + @property + def client_secret(self): + return self.__client_secret + + @property + def grant_type(self): + return self.__grant_type + + @property + def access_token(self): + return self.__access_token + + @property + def name(self): + return self.__name + + @property + 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): + self.__connect_with_idp() + + def __on_key_pressed(self, key): + pass + + def __connect_with_idp(self): + idp = IdentityProvider( + grant_type=self.__grant_type, + client_id=self.__thing_id, + username=self.__username, + password=self.__password, + client_secret=self.__client_secret, + realm=BaseVariable.IDP_REALM, + identity_provider_url=BaseVariable.IDP_URL, + ) + + # This may take a while so fetch token directly. + idp.get_token(TokenType.ACCESS_TOKEN) + idp.run_forever(token_type=TokenType.ACCESS_TOKEN, on_new_token=self.__on_token) + + def __on_token(self, token): + self.__access_token = token + if self.__is_broker: + self.__connect_with_broker() + if self.__is_repo: + self.__connect_with_repo() + + def __connect_with_repo(self): + self._ws = websocket.WebSocketApp( + BaseVariable.REPO_WWS_URL, + header={"Authorization: Bearer {}".format(self.__access_token)}, + on_message=self.__on_new_websocket_message, + on_error=self.__on_new_websocket_error, + on_open=self.__on_websocket_connection_opened, + on_close=self.__on_websocket_connection_closed, + ) + + threading.Thread(target=self._ws.run_forever).start() + + @staticmethod + def __on_new_websocket_message(ws, msg): + pass + + @staticmethod + def __on_new_websocket_error(ws, error): + print(BColors.OKBLUE + "[S³I][Repo]" + BColors.ENDC + " : Websocekt error") + + def __on_websocket_connection_opened(self): + self.__ws_connected = True + self._ws.send("START-SEND-MESSAGES") + print( + BColors.OKBLUE + + "[S³I][Repo]" + + BColors.ENDC + + ": Websocket connection built" + ) + + def __on_websocket_connection_closed(self): + self.__ws_connected = False + print( + BColors.OKBLUE + + "[S³I][Repo]" + + BColors.ENDC + + ": Websocket connection closed" + ) + + def sync_with_repo(self, path, topic): + if not self.__ws_connected: + return None + msg = {"topic": topic, "path": path, "value": self.fml40_data_model[path]} + self._ws.send(json.dumps(msg)) + + def __connect_with_broker(self): + broker = Broker( + auth_form="Username/Password", + username=" ", + password=self.__access_token, + host=BaseVariable.BROKER_HOST, + ) + self._broker = broker + threading.Thread( + target=self._broker.receive, + args=(self.__endpoint, self.__on_broker_callback), + ).start() + + def __on_broker_callback(self, ch, method, properties, body): + self.on_receive(body) + + def on_get_value_request(self, msg): + get_value_reply = GetValueReply() + request_sender = msg.get("sender", "") + request_msg_id = msg.get("identifier") + request_sender_endpoint = msg.get("replyToEndpoint") + request_sender_endpoint = f"s3ib://{request_sender_endpoint}" + attribute_path = msg.get("attributePath") + reply_msg_uuid = "s3i:" + str(uuid.uuid4()) + # TODO: Get correct value. + value = None + + if attribute_path == "features/moisture": + moisture = self.features.get("ml40::Moisture", None) + moisture_proxy = moisture.proxy() + value = moisture_proxy.humidity.get() + + get_value_reply.fillGetValueReply( + senderUUID=self.__thing_id, + receiverUUID=[request_sender], + results=value, + msgUUID=reply_msg_uuid, + replyingToUUID=request_msg_id, + ) + msg_txt = get_value_reply.msg.__str__() + self._broker.send( + receiver_endpoints=[request_sender_endpoint], msg=msg_txt, + ) + + + def on_service_request(self, body_json): + service_type = body_json.get("serviceType") + func = self.features.get(service_type, None) + if func is None: + APP_LOGGER.critical( + "Functionality %s is missing in %s!" % (func, self.name) + ) + else: + pass + # TODO: Call right functionality. + # func_proxy = func.proxy() diff --git a/ml/tools.py b/ml/tools.py new file mode 100644 index 0000000..9eccfdc --- /dev/null +++ b/ml/tools.py @@ -0,0 +1,348 @@ +"""tools.py provivdes a collection of convenience functions.""" + +import datetime +import time +import uuid +import json +from functools import partial +from s3i import Directory +from s3i import Broker +from s3i import ServiceRequest +from s3i import TokenType +from s3i import IdentityProvider +from ml.app_logger import APP_LOGGER +from ml.authentication import GRANT_TYPES + +IDENTITY_PROVIDER_URL = "https://idp.s3i.vswf.dev/" + + +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" + + +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 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 + + +def send_message(access_token, receiver_ids, req): + 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", + ) + s3i_broker.send(receiver_endpoints, req.msg.__str__()) + + +# 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 + + """ + + 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 + + +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 + + """ + 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 load_config(config_filepath): + """Creates a json object from a json formatted file found at config_filepath. + + :param config_filepath: Path to json formatted file. + + """ + with open(config_filepath) as config_file: + config = json.load(config_file) + 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. + + """ + res = list(load_config(config) for config in config_filepaths) + return res + + +def id_to_endpoint(thing_id): + """Returns the endpoint for thing_id. + + :param thing_id: id + :returns: endpoint + :rtype: str + + """ + + 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)) + + """ + 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 diff --git a/requirements.txt b/requirements.txt index 13a0c35..af0299b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,9 @@ https://git.rwth-aachen.de/kwh40/s3i/-/jobs/artifacts/master/raw/public/s3i-0.4-py3-none-any.whl?job=wheel Pykka +websocket +pynput +opcua +requests +asyncio pylint==2.4.4 #https://git.rwth-aachen.de/kwh40/s3i/-/jobs/758151/artifacts/raw/public/s3i-0.4-py3-none-any.whl diff --git a/scripts/edit_thing.py b/scripts/edit_thing.py index ed9832d..38206f1 100644 --- a/scripts/edit_thing.py +++ b/scripts/edit_thing.py @@ -1,10 +1,14 @@ +import sys + +sys.path.append("./") import logging import sys import json import argparse from s3i import IdentityProvider, Config, Directory, TokenType -from get_token import get_access_token_dispatch -from get_token import GRANT_TYPES +from ml.authentication import GRANT_TYPES +from ml.authentication import get_access_token_dispatch +from ml.authentication import configure_credentials_parser config_s_url = "https://config.s3i.vswf.dev/" logger = logging.getLogger(__name__) @@ -93,8 +97,8 @@ def print_response(response): print(json.dumps(response, indent=4)) -def show_thing(token, thing_id): - s3i_dir = Directory(s3i_dir_url="https://dir.s3i.vswf.dev/api/2/", token=token) +def show_thing(token, thing_id, url): + s3i_dir = Directory(url, token=token) response = s3i_dir.queryThingIDBased(thing_id) print_response(response) @@ -113,51 +117,45 @@ def update_thing_default(s3i_dir, thing, person): s3i_dir.updateThingIDBased(thing[0], hmi_body) -def update_thing(thing_id, access_token, body): - s3i_dir = Directory( - s3i_dir_url="https://dir.s3i.vswf.dev/api/2/", token=access_token - ) +def update_thing(thing_id, access_token, body, url): + s3i_dir = Directory(s3i_dir_url=url, token=access_token) s3i_dir.updateThingIDBased(thing_id, body) response = s3i_dir.queryThingIDBased(thing_id) print_response(response) -def config_credentials_parser(parser): - parser.add_argument( - "--g", choices=GRANT_TYPES, default=GRANT_TYPES[0], help="grant type" +def configure_identifier_parser(parser): + # TODO: What about multiple ids?! + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument( + "--i", default=None, help="Id (in s3i format) of the thing to be printed out." + ) + group.add_argument( + "--f", default=None, help="Filepath to credentials in json format." ) - parser.add_argument("--d", action="store_false", help="Prompt for client") - parser.add_argument("--s", action="store_true", help="Prompt for scope") - parser.add_argument("--i", default=None, help="Filepath to credentials") def config_show_parser(parser): - parser.add_argument( - "thingId", help="Id (in s3i format) of the thing to be printed out." - ) - config_credentials_parser(parser) + configure_credentials_parser(parser) + configure_identifier_parser(parser) + parser.add_argument("--repo", action="store_true") def config_create_parser(parser): - config_credentials_parser(parser) + configure_credentials_parser(parser) def config_update_parser(parser): - config_credentials_parser(parser) - parser.add_argument( - "thingId", help="Id (in s3i format) of the thing to be altered." - ) + configure_credentials_parser(parser) + # configure_identifier_parser(parser) parser.add_argument( "config", help="Path to json formatted file that specifies the thing." ) def config_delete_parser(parser): - # TODO: What about multiple ids?! - parser.add_argument( - "thingId", help="Id (in s3i format) of the thing to be deleted." - ) - config_credentials_parser(parser) + configure_credentials_parser(parser) + configure_identifier_parser(parser) def create_arg_parser(): @@ -181,16 +179,33 @@ def create_arg_parser(): return parser +def get_identifier_from_file(filepath): + with open(filepath, "r", encoding="utf-8") as cred_file: + obj = json.load(cred_file) + thing_id = obj.get("identifier", None) + if not thing_id: + thing_id = obj.get("thingId") + return thing_id + + def main(): parser = create_arg_parser() args = parser.parse_args() - token = get_access_token_dispatch(args.i, args.g, args.d, args.s) + token = get_access_token_dispatch(args.c, args.g, args.d, args.s) + url = "https://dir.s3i.vswf.dev/api/2/" + if args.repo: + url = "https://ditto.s3i.vswf.dev/api/2/" if args.command == "show": - show_thing(token, args.thingId) + thing_id = None + if args.f: + thing_id = get_identifier_from_file(args.f) + if args.i: + thing_id = args.i + show_thing(token, thing_id, url) elif args.command == "update": with open(args.config, "r") as config_file: body = json.load(config_file) - update_thing(body["thingId"], token, body) + update_thing(body["thingId"], token, body, url) elif args.command == "create": create_thing(token, encrypted=False) elif args.command == "delete": diff --git a/scripts/get_token.py b/scripts/get_token.py index e3a4e26..50a1154 100644 --- a/scripts/get_token.py +++ b/scripts/get_token.py @@ -1,73 +1,10 @@ -from s3i import IdentityProvider, TokenType +import sys +sys.path.append("./") import argparse import json - -GRANT_TYPES = ("client_credentials", "password") - - -def get_username_and_password(filepath=None): - username = None - password = None - if filepath is not None: - with open(filepath, "r") as json_f: - js_object = json.load(json_f) - username = js_object["name"] - password = js_object["password"] - else: - username = input("[S3I]: Please enter your username: ") - # TODO: Use getpass - password = input("[S3I]: Please enter your password: ") - return username, password - - -def get_client_id_and_secret(filepath=None): - client_id = None - client_secret = None - if filepath is not None: - with open(filepath, "r") as json_f: - js_object = json.load(json_f) - client_id = js_object["thingId"] - client_secret = js_object["client_secret"] - else: - client_id = input("[S3I]: Please enter client id: ") - # TODO: Use getpass - client_secret = input("[S3I]: Please enter client secret: ") - return client_id, client_secret - - -def authorize_client(default_client, with_scope): - client = "admin-cli" - client_secret = "" - scope = "" - if not default_client: - tmp = input( - "[S3I]: Please enter the thing (client) you want to " - "authorize (or leave blank to default to admin client): " - ) - if tmp != "": - client = tmp - client_secret = input( - "[S3I]: Please enter the thing's secret (client secret): " - ) - if with_scope: - scope = input( - "[S3I]: Please enter the client scope which is requested for the " - "access token and has been assigned to the client (or leave blank): " - ) - return client, client_secret, scope - - -def get_access_token(grant_type, client_id, client_secret, username, password, scope): - idp = IdentityProvider( - grant_type=grant_type, - client_id=client_id, - username=username, - password=password, - client_secret=client_secret, - realm="KWH", - identity_provider_url="https://idp.s3i.vswf.dev/", - ) - return idp.get_token(TokenType.ACCESS_TOKEN, scope=scope) +# from s3i.identity_provider import IdentityProvider, TokenType +from ml.authentication import GRANT_TYPES +from ml.authentication import get_access_token_dispatch def create_parser(): @@ -81,22 +18,6 @@ def create_parser(): return parser -def get_access_token_dispatch(file_path, grant_type, ask_client, ask_scope): - access_token = None - if grant_type == GRANT_TYPES[1]: - username, password = get_username_and_password(file_path) - client_id, client_secret, scope = authorize_client(ask_client, ask_scope) - access_token = get_access_token( - grant_type, client_id, client_secret, username, password, scope - ) - else: - client_id, client_secret = get_client_id_and_secret(file_path) - access_token = get_access_token( - grant_type, client_id, client_secret, "", "", "" - ) - return access_token - - def main(): parser = create_parser() args = parser.parse_args() diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..428a99b --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,2 @@ +import sys +sys.path.append("./../../s3i") diff --git a/tests/res/dummy_credentials.json b/tests/res/dummy_credentials.json new file mode 100644 index 0000000..d1d28b9 --- /dev/null +++ b/tests/res/dummy_credentials.json @@ -0,0 +1,4 @@ +{ + "name" : "user1", + "password" : "eins" +} diff --git a/tests/res/malformatted.json b/tests/res/malformatted.json new file mode 100644 index 0000000..92f72ed --- /dev/null +++ b/tests/res/malformatted.json @@ -0,0 +1 @@ +blank diff --git a/tests/test_digital_twins.py b/tests/test_digital_twins.py new file mode 100644 index 0000000..90e06b5 --- /dev/null +++ b/tests/test_digital_twins.py @@ -0,0 +1,226 @@ +"""Digital twin testing""" +import unittest +import uuid +from s3i import ServiceRequest +from s3i import GetValueRequest +from ml.tools import GRANT_TYPES +from ml.tools import load_config +from ml.tools import get_requests +from ml.tools import send_requests +from ml.tools import get_s3i_broker +from ml.tools import get_receiver_callback_func +from ml.tools import create_request +from ml.app_logger import setup_logger +from ml.dt_factory import create_dt_ref +from ml.dt_factory import DT_FACTORY +from ml.authentication import get_client_id_and_secret +from customer_code_example.harvester_john_deere import john_deere +from customer_code_example.harvester_ponsse import ponsse +from customer_code_example.forwarder_komatsu import komatsu +from ml.fml40.features.functionalities.accepts_proximity_alert import ( + AcceptsProximityAlert, +) + +setup_logger("Testing") + +DT_FACTORY["JohnDeere"] = john_deere.JohnDeere +DT_FACTORY["Komatsu"] = komatsu.Komatsu +DT_FACTORY["Ponsse"] = ponsse.Ponsse +CONFIG_PATH_DEERE = "./configs/config_harvester_john_deere.json" +CONFIG_PATH_PONSSE = "./configs/config_harvester_ponsse.json" +CONFIG_PATH_KOMATSU = "./configs/config_forwarder_komatsu.json" + + +class TestDigitalTwins(unittest.TestCase): + """Implements tests for digital twins.""" + + def test_john_deere(self): + """Testing of the digital twin John Deere. + + """ + deere_config = load_config(CONFIG_PATH_DEERE) + _, client_secret = get_client_id_and_secret("./credentials/john_deere.json") + + grant_type = GRANT_TYPES[0] + deere_ref = create_dt_ref( + model=deere_config, + grant_type=grant_type, + secret=client_secret, + username="", + password="", + is_broker=True, + is_repo=False, + ) + deere_proxy = deere_ref.proxy() + self.assertEqual(deere_proxy.name.get(), "John Deere Harvester FBZ") + self.assertEqual( + deere_proxy.thing_id.get(), "s3i:c5d5cd58-8786-40b2-8079-5f2de443de36" + ) + + features = deere_proxy.features.get() + self.assertEqual(len(features), 8) + + results = features.get("fml40::AcceptsProximityAlert") + self.assertEqual(results.proxy().name.get(), "AcceptsProximityAlert 0") + + results = features.get("fml40::AcceptsFellingJobs") + self.assertEqual(results.proxy().name.get(), "AcceptsFellingJobs 1") + + results = features.get("fml40::Harvests") + self.assertEqual(results.proxy().name.get(), "Harvests 2") + + results = features.get("ml40::ManagesJobs") + self.assertEqual(results.proxy().name.get(), "ManagesJobs 3") + + results = features.get("fml40::ProvidesProductionData") + self.assertEqual(results.proxy().name.get(), "ProvidesProductionData 4") + + results = features.get("ml40::Location") + self.assertEqual(results.proxy().name.get(), "Location 5") + + results = features.get("ml40::Shared") + self.assertEqual(results.proxy().name.get(), "Shared 6") + + results = features.get("ml40::Composite") + self.assertEqual(results.proxy().name.get(), "Composite 7") + + def test_ponsse(self): + ponsse_config = load_config(CONFIG_PATH_PONSSE) + _, client_secret = get_client_id_and_secret("./credentials/ponsse.json") + + grant_type = GRANT_TYPES[0] + ponsse_ref = create_dt_ref( + model=ponsse_config, + grant_type=grant_type, + secret=client_secret, + username="", + password="", + is_broker=True, + is_repo=False, + ) + ponsse_proxy = ponsse_ref.proxy() + self.assertEqual(ponsse_proxy.name.get(), "Ponsse Harvester") + self.assertEqual( + ponsse_proxy.thing_id.get(), "s3i:5e83b933-331f-4278-b318-b7fdcb0e4872" + ) + + features = ponsse_proxy.features.get() + self.assertEqual(len(features), 7) + + results = features.get("fml40::AcceptsProximityAlert") + self.assertEqual(results.proxy().name.get(), "alert 0") + + results = features.get("fml40::AcceptsFellingJobs") + self.assertEqual(results.proxy().name.get(), "felling job 1") + + results = features.get("fml40::Harvests") + self.assertEqual(results.proxy().name.get(), "harvests 2") + + results = features.get("ml40::ManagesJobs") + self.assertEqual(results.proxy().name.get(), "manages 3") + + results = features.get("fml40::ProvidesProductionData") + self.assertEqual(results.proxy().name.get(), "production 4") + + results = features.get("ml40::Location") + self.assertEqual(results.proxy().name.get(), "location 5") + + results = features.get("ml40::Shared") + self.assertEqual(results.proxy().name.get(), "shared 6") + + def test_komatsu(self): + komatsu_config = load_config(CONFIG_PATH_KOMATSU) + _, client_secret = get_client_id_and_secret("./credentials/komatsu.json") + + grant_type = GRANT_TYPES[0] + komatsu_ref = create_dt_ref( + model=komatsu_config, + grant_type=grant_type, + secret=client_secret, + username="", + password="", + is_broker=True, + is_repo=False, + ) + komatsu_proxy = komatsu_ref.proxy() + self.assertEqual(komatsu_proxy.name.get(), "Komatsu Forwarder") + self.assertEqual( + komatsu_proxy.thing_id.get(), "s3i:3154edfa-5b04-4a28-b803-d6ec46135c19" + ) + + features = komatsu_proxy.features.get() + self.assertEqual(len(features), 6) + + results = features.get("fml40::AcceptsProximityAlert") + self.assertEqual(results.proxy().name.get(), "Proxi 1") + + results = features.get("fml40::AcceptsForwardingJobs") + self.assertEqual(results.proxy().name.get(), "Forwarding 2") + + results = features.get("fml40::Forwards") + self.assertEqual(results.proxy().name.get(), "Forwards 3") + + results = features.get("ml40::Location") + self.assertEqual(results.proxy().name.get(), "Location 4") + + results = features.get("ml40::Shared") + self.assertEqual(results.proxy().name.get(), "Shared 5") + + results = features.get("ml40::Composite") + self.assertEqual(results.proxy().name.get(), "Composite 6") + + def test_passability_service(self): + passability_config = load_config(CONFIG_PATH_PASSABILITY_SERVICE) + _, client_secret = get_client_id_and_secret( + "./credentials/passability_service.json" + ) + + grant_type = GRANT_TYPES[0] + passability_ref = create_dt_ref( + model=passability_config, + grant_type=grant_type, + secret=client_secret, + username="", + password="", + is_broker=True, + is_repo=False, + ) + passability_proxy = passability_ref.proxy() + self.assertEqual(passability_proxy.name.get(), "Passability Service") + self.assertEqual( + passability_proxy.thing_id.get(), "s3i:fcf78290-f462-4c9b-8fcb-e63603702983" + ) + + features = passability_proxy.features.get() + self.assertEqual(len(features), 1) + + result = features.get("fml40::ProvidesPassabilityInformation") + result_proxy = result.proxy() + self.assertEqual(result_proxy.name.get(), "Passability Information Provider") + + # Send passability request to passsability service + forwarder_ref = self.load_passability_forwarder() + forwarder_proxy = forwarder_ref.proxy() + parameters = {"load": 200, "moisture": 300} + req = create_request( + forwarder_proxy.thing_id.get(), + passability_proxy.thing_id.get(), + "fml40::ProvidesPassabilityInformation", + parameters, + ) + req_msg = req.msg.__str__() + passability_proxy.on_receive(req_msg, False) + features = passability_proxy.features.get() + self.assertEqual(len(features), 1) + result = features.get("fml40::ProvidesPassabilityInformation") + result_proxy = result.proxy() + reply = result_proxy.service_reply.get() + self.assertEqual(reply.msg.get("sender", ""), passability_proxy.thing_id.get()) + self.assertEqual(reply.msg.get("messageType", ""), "serviceReply") + self.assertEqual( + reply.msg.get("serviceType", ""), "fml40::ProvidesPassabilityInformation" + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_get_token.py b/tests/test_get_token.py new file mode 100644 index 0000000..db0b8ad --- /dev/null +++ b/tests/test_get_token.py @@ -0,0 +1,20 @@ +"""Testing get_token.py""" +from unittest import TestCase +from json.decoder import JSONDecodeError +from ml.authentication import get_username_and_password + + +class TestGetTokenScript(TestCase): + def test_get_user_name_and_password(self): + + self.assertRaises( + FileNotFoundError, get_username_and_password, "file_not_available.json" + ) + self.assertRaises( + JSONDecodeError, get_username_and_password, "tests/res/malformatted.json" + ) + username, password = get_username_and_password( + "tests/res/dummy_credentials.json" + ) + self.assertEqual(username, "user1") + self.assertEqual(password, "eins") -- GitLab From 3196fda3bccd96bc7956da65e55de47bfad2bb71 Mon Sep 17 00:00:00 2001 From: "C. Albrecht" Date: Wed, 26 Aug 2020 16:58:21 +0200 Subject: [PATCH 02/84] ENH: WIP Argparsing + naming --- ml/authentication.py | 144 ++++++++++++++------------ scripts/get_token.py | 23 +--- scripts/{edit_thing.py => s3i_api.py} | 107 +++++++++++-------- 3 files changed, 146 insertions(+), 128 deletions(-) rename scripts/{edit_thing.py => s3i_api.py} (74%) diff --git a/ml/authentication.py b/ml/authentication.py index 7d0b25d..9dd3f6c 100644 --- a/ml/authentication.py +++ b/ml/authentication.py @@ -5,16 +5,82 @@ GRANT_TYPES = ("client_credentials", "password") +def configure_client_parser(parser): + # Setup a parser for file input + subparsers = parser.add_subparsers(required=True, dest="client") + client_file_parser = subparsers.add_parser( + "cfi", help="Specify client and seceret via file input.") + client_file_parser.add_argument("cfp", help="Filepath") + + # Setup a parser for user input + client_ui_parser = subparsers.add_parser( + "cui", help="Specify client and secret via terminal.") + client_ui_parser.add_argument("-c", + default="admin-cli", + help="Client name: admin-cli is default") + client_ui_parser.add_argument( + "-cs", default="", help="Client secret: empty string is default") + return client_file_parser, client_ui_parser + + +def configure_user_parser(parser): + # Setup a parser for file input + subparsers = parser.add_subparsers(required=True, dest="user") + user_file_parser = subparsers.add_parser( + "ufi", help="Specifiy username and password via file input.") + user_file_parser.add_argument("ufp", help="Filepath to credentials.") + + # Setup a parser for user input + user_ui_parser = subparsers.add_parser( + "uui", help="Specify username and password via terminal.") + user_ui_parser.add_argument("-u", required=True, help="Username") + user_ui_parser.add_argument("-up", required=True, help="User password") + return user_file_parser, user_ui_parser + + def configure_credentials_parser(parser): - parser.add_argument( - "--g", choices=GRANT_TYPES, default=GRANT_TYPES[0], help="grant type" - ) - parser.add_argument("--d", action="store_false", help="Prompt for client") - parser.add_argument("--s", action="store_true", help="Prompt for scope") - parser.add_argument("--c", default=None, help="Filepath to credentials") + parser.add_argument("--g", + choices=GRANT_TYPES, + default=GRANT_TYPES[0], + help=f"Specify grant type ({GRANT_TYPES[0]} is default)") + parser.add_argument("--scope", + default="email", + help="Specifiy the scope (email is default)") + cfi_parser, cui_parser = configure_client_parser(parser) + cfi_ufi_parser, cfi_uui_parser = configure_user_parser(cfi_parser) + cui_ufi_parser, cui_uui_parser = configure_user_parser(cui_parser) + return parser -def get_username_and_password(filepath=None): + +def get_token_from_args(args): + grant_type = args.g + scope = args.scope + client = None + secret = None + username = None + password = None + + if args.client == "cfi": + filepath = args.cfp + client, secret = parse_client_id_and_secret(filepath) + else: + client = args.c + secret = args.cs + + if args.user == "ufi": + filepath = args.ufp + username, password = parse_username_and_password(filepath) + else: + username = args.u + password = args.up + + token = get_access_token(grant_type, client, secret, username, password, + scope) + return str(token) + + +def parse_username_and_password(filepath): username = None password = None if filepath is not None: @@ -22,14 +88,10 @@ def get_username_and_password(filepath=None): js_object = json.load(json_f) username = js_object["name"] password = js_object["password"] - else: - username = input("[S3I]: Please enter your username: ") - # TODO: Use getpass - password = input("[S3I]: Please enter your password: ") return username, password -def get_client_id_and_secret(filepath=None): +def parse_client_id_and_secret(filepath): client_id = None client_secret = None if filepath is not None: @@ -38,50 +100,14 @@ def get_client_id_and_secret(filepath=None): client_id = js_object.get("thingId", None) if not client_id: client_id = js_object.get("identifier") - client_secret = js_object.get("client_secret") if not client_secret: client_secret = js_object.get("secret") - else: - client_id = input("[S3I]: Please enter client id: ") - # TODO: Use getpass - client_secret = input("[S3I]: Please enter client secret: ") return client_id, client_secret -def get_credentials_dispatch(grant_type, filepath=None): - username = "" - password = "" - secret = "" - client_id = "" - if grant_type == GRANT_TYPES[0]: - client_id, secret = get_client_id_and_secret(filepath) - else: - username, password = get_username_and_password(filepath) - return client_id, secret, username, password - -def authorize_client(default_client, with_scope): - client = "admin-cli" - client_secret = "" - scope = "" - if not default_client: - tmp = input( - "[S3I]: Please enter the thing (client) you want to " - "authorize (or leave blank to default to admin client): " - ) - if tmp != "": - client = tmp - client_secret = input( - "[S3I]: Please enter the thing's secret (client secret): " - ) - if with_scope: - scope = input( - "[S3I]: Please enter the client scope which is requested for the " - "access token and has been assigned to the client (or leave blank): " - ) - return client, client_secret, scope - - -def get_access_token(grant_type, client_id, client_secret, username, password, scope): + +def get_access_token(grant_type, client_id, client_secret, username, password, + scope): idp = IdentityProvider( grant_type=grant_type, client_id=client_id, @@ -92,19 +118,3 @@ def get_access_token(grant_type, client_id, client_secret, username, password, s identity_provider_url="https://idp.s3i.vswf.dev/", ) return idp.get_token(TokenType.ACCESS_TOKEN, scope=scope) - - -def get_access_token_dispatch(file_path, grant_type, ask_client, ask_scope): - access_token = None - if grant_type == GRANT_TYPES[1]: - username, password = get_username_and_password(file_path) - client_id, client_secret, scope = authorize_client(ask_client, ask_scope) - access_token = get_access_token( - grant_type, client_id, client_secret, username, password, scope - ) - else: - client_id, client_secret = get_client_id_and_secret(file_path) - access_token = get_access_token( - grant_type, client_id, client_secret, "", "", "" - ) - return access_token diff --git a/scripts/get_token.py b/scripts/get_token.py index 50a1154..6d58978 100644 --- a/scripts/get_token.py +++ b/scripts/get_token.py @@ -2,28 +2,15 @@ sys.path.append("./") import argparse import json -# from s3i.identity_provider import IdentityProvider, TokenType -from ml.authentication import GRANT_TYPES -from ml.authentication import get_access_token_dispatch - - -def create_parser(): - parser = argparse.ArgumentParser() - parser.add_argument("--i", default=None, help="Filepath to credentials") - parser.add_argument("--d", action="store_false", help="Prompt for client") - parser.add_argument("--s", action="store_true", help="Prompt for scope") - parser.add_argument( - "--g", choices=GRANT_TYPES, default=GRANT_TYPES[0], help="grant type" - ) - return parser +from ml.authentication import configure_credentials_parser +from ml.authentication import get_token_from_args def main(): - parser = create_parser() + parser = argparse.ArgumentParser() + parser = configure_credentials_parser(parser) args = parser.parse_args() - access_token = get_access_token_dispatch(args.i, args.g, args.d, args.s) - print("[S3I]: Access token:\n ", access_token) - + token = get_token_from_args(args) if __name__ == "__main__": main() diff --git a/scripts/edit_thing.py b/scripts/s3i_api.py similarity index 74% rename from scripts/edit_thing.py rename to scripts/s3i_api.py index 38206f1..e362570 100644 --- a/scripts/edit_thing.py +++ b/scripts/s3i_api.py @@ -5,10 +5,11 @@ import sys import json import argparse +from s3i.validation import validate_s3i_uuid from s3i import IdentityProvider, Config, Directory, TokenType -from ml.authentication import GRANT_TYPES -from ml.authentication import get_access_token_dispatch +from s3i.repository import Repository from ml.authentication import configure_credentials_parser +from ml.authentication import get_token_from_args config_s_url = "https://config.s3i.vswf.dev/" logger = logging.getLogger(__name__) @@ -34,7 +35,8 @@ def person_from_config_response(new_person): def print_person(person): print("Created a person named: {}".format(person[0])) print("Created person has an identity (uuid): {}".format(person[3])) - print("Created DT for person has thing identity (uuid): {}".format(person[1])) + print("Created DT for person has thing identity (uuid): {}".format( + person[1])) print("Created DT for person has secret: {}".format(person[2])) @@ -56,21 +58,20 @@ def delete_thing(access_token, thing_id): def create_person(token, verbose=False): person_name = input("Please enter the selected username for new user: ") - person_password = input("Please enter the selected password for new user: ") + person_password = input( + "Please enter the selected password for new user: ") s3i_config = Config(server_url=config_s_url, token=token) - new_person = s3i_config.create_person( - username=person_name, password=person_password - ) + new_person = s3i_config.create_person(username=person_name, + password=person_password) if new_person.status_code == 400: logger.critical( "Person could not be created because some key is missing.\n" - "Shutting down." - ) + "Shutting down.") sys.exit("400") if new_person.status_code == 409: logger.critical( - "Person could not be create because due to a conflict.\n" "Shutting down." - ) + "Person could not be create because due to a conflict.\n" + "Shutting down.") sys.exit("409") person = person_from_config_response(new_person) @@ -82,7 +83,8 @@ def create_person(token, verbose=False): def create_thing(access_token, encrypted): - s3i_new_user_config = Config(config_s_url, token=access_token) + """Create a new thing.""" + s3i_new_user_config = Config(url, token=access_token) response = s3i_new_user_config.create_thing(encrypted=encrypted) res = response.json() if response.status_code == 201: @@ -97,9 +99,18 @@ def print_response(response): print(json.dumps(response, indent=4)) -def show_thing(token, thing_id, url): - s3i_dir = Directory(url, token=token) - response = s3i_dir.queryThingIDBased(thing_id) +def get_ditto_instance(token, use_repo): + """Returns an instance of class Repository if use_repo is True, + otherwise it is of type Directory.""" + if use_repo: + return Repository(token=token) + else: + return Directory(token=token) + + +def show_thing(token, thing_id, use_repo): + ditto = get_ditto_instance(token, use_repo) + response = ditto.queryThingIDBased(thing_id) print_response(response) @@ -124,21 +135,37 @@ def update_thing(thing_id, access_token, body, url): print_response(response) +def get_identifier_from_file(filepath): + with open(filepath, "r", encoding="utf-8") as cred_file: + obj = json.load(cred_file) + thing_id = obj.get("identifier", None) + if not thing_id: + thing_id = obj.get("thingId") + return thing_id + + def configure_identifier_parser(parser): - # TODO: What about multiple ids?! group = parser.add_mutually_exclusive_group(required=True) group.add_argument( - "--i", default=None, help="Id (in s3i format) of the thing to be printed out." - ) - group.add_argument( - "--f", default=None, help="Filepath to credentials in json format." - ) + "--i", + default=None, + help="Id (in s3i format) of the thing to be printed out.") + group.add_argument("--f", + default=None, + help="Filepath to credentials in json format.") + + +def config_ditto_parser(parser): + parser.add_argument( + "--repo", + action="store_true", + help="Executes operation on repository if set (directory otherwise).") def config_show_parser(parser): + config_ditto_parser(parser) configure_credentials_parser(parser) configure_identifier_parser(parser) - parser.add_argument("--repo", action="store_true") def config_create_parser(parser): @@ -147,10 +174,8 @@ def config_create_parser(parser): def config_update_parser(parser): configure_credentials_parser(parser) - # configure_identifier_parser(parser) parser.add_argument( - "config", help="Path to json formatted file that specifies the thing." - ) + "config", help="Path to json formatted file that specifies the thing.") def config_delete_parser(parser): @@ -163,15 +188,15 @@ def create_arg_parser(): description="This script allows creation, alteration and printing " "of things located in the s3i directory.\n" "Structure of credential file is as follows:\n" - '{ "name" : "...","password": "..." } ' - ) + '{ "name" : "...","password": "..." } ') subparsers = parser.add_subparsers(dest="command") show_parser = subparsers.add_parser( - "show", help="Prints json representation of a thing." - ) - create_parser = subparsers.add_parser("create") + "show", help="Prints json representation of a thing.") + + create_parser = subparsers.add_parser("create", help="Creates a thing") update_parser = subparsers.add_parser("update", help="Updates a thing") delete_parser = subparsers.add_parser("delete", help="Delete a thing") + config_show_parser(show_parser) config_create_parser(create_parser) config_update_parser(update_parser) @@ -179,39 +204,35 @@ def create_arg_parser(): return parser -def get_identifier_from_file(filepath): - with open(filepath, "r", encoding="utf-8") as cred_file: - obj = json.load(cred_file) - thing_id = obj.get("identifier", None) - if not thing_id: - thing_id = obj.get("thingId") - return thing_id - - def main(): + # TODO: Distinguish between Repository and Directory class, parser = create_arg_parser() args = parser.parse_args() - token = get_access_token_dispatch(args.c, args.g, args.d, args.s) + token = get_token_from_args(args) + url = "https://dir.s3i.vswf.dev/api/2/" if args.repo: url = "https://ditto.s3i.vswf.dev/api/2/" if args.command == "show": + use_repo = args.repo thing_id = None if args.f: thing_id = get_identifier_from_file(args.f) if args.i: thing_id = args.i - show_thing(token, thing_id, url) + if validate_s3i_uuid(thing_id): + show_thing(token, thing_id, use_repo) + else: + print("{} is not a valid s3i uuid.".format(thing_id)) elif args.command == "update": with open(args.config, "r") as config_file: body = json.load(config_file) update_thing(body["thingId"], token, body, url) elif args.command == "create": - create_thing(token, encrypted=False) + create_thing(url, token, encrypted=False) elif args.command == "delete": delete_thing(token, args.thingId) if __name__ == "__main__": - # """username/password for creating new user in S3I""" main() -- GitLab From 234340f8aa546358b6e06154733e0571eb487cbf Mon Sep 17 00:00:00 2001 From: "C. Albrecht" Date: Thu, 27 Aug 2020 13:41:22 +0200 Subject: [PATCH 03/84] FIX: Added print statement --- scripts/get_token.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/get_token.py b/scripts/get_token.py index 6d58978..d56e6fe 100644 --- a/scripts/get_token.py +++ b/scripts/get_token.py @@ -11,6 +11,8 @@ def main(): parser = configure_credentials_parser(parser) args = parser.parse_args() token = get_token_from_args(args) + print(token) + if __name__ == "__main__": main() -- GitLab From f7d2a2df63c2d7e6445e9c35f3c786c9f0c17440 Mon Sep 17 00:00:00 2001 From: "C. Albrecht" Date: Fri, 25 Sep 2020 15:05:49 +0200 Subject: [PATCH 04/84] FIX: PEP8 --- ml/authentication.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ml/authentication.py b/ml/authentication.py index 9dd3f6c..339d343 100644 --- a/ml/authentication.py +++ b/ml/authentication.py @@ -48,8 +48,8 @@ def configure_credentials_parser(parser): help="Specifiy the scope (email is default)") cfi_parser, cui_parser = configure_client_parser(parser) - cfi_ufi_parser, cfi_uui_parser = configure_user_parser(cfi_parser) - cui_ufi_parser, cui_uui_parser = configure_user_parser(cui_parser) + __cfi_ufi_parser, __cfi_uui_parser = configure_user_parser(cfi_parser) + __cui_ufi_parser, __cui_uui_parser = configure_user_parser(cui_parser) return parser -- GitLab From 39e32122446d82ef754723c2cd5d75d5e11c1374 Mon Sep 17 00:00:00 2001 From: "C. Albrecht" Date: Fri, 25 Sep 2020 15:06:10 +0200 Subject: [PATCH 05/84] FIX: Creation of things in repo/dir --- scripts/s3i_api.py | 75 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/scripts/s3i_api.py b/scripts/s3i_api.py index e362570..625e1d1 100644 --- a/scripts/s3i_api.py +++ b/scripts/s3i_api.py @@ -82,16 +82,31 @@ def create_person(token, verbose=False): return person -def create_thing(access_token, encrypted): - """Create a new thing.""" - s3i_new_user_config = Config(url, token=access_token) - response = s3i_new_user_config.create_thing(encrypted=encrypted) +def create_thing_in_repo(config, thing_id): + """Creates a copy of an existing thing in ditto repository.""" + res = config.create_cloud_copy(thing_id) + res = response.json() + status_code = res.status_code + if status_code == 201: + print("A copy has been successfully created.") + else: + print("An error occured: {}".format(status_code)) + + +def create_thing_in_directory(config, with_broker): + """Creates a new thing in ditto directory.""" + response = config.create_thing() res = response.json() if response.status_code == 201: thing_id = res["identifier"] secret = res["secret"] print(json.dumps(res, indent=2)) + if with_broker: + response = config.create_broker_queue(thing_id) + if response.status_code != 201: + print("An error occured: {}".format({response.status_code})) return thing_id, secret + print("An error occured: {}".format({response.status_code})) return None @@ -104,8 +119,7 @@ def get_ditto_instance(token, use_repo): otherwise it is of type Directory.""" if use_repo: return Repository(token=token) - else: - return Directory(token=token) + return Directory(token=token) def show_thing(token, thing_id, use_repo): @@ -169,13 +183,24 @@ def config_show_parser(parser): def config_create_parser(parser): - configure_credentials_parser(parser) + subparsers = parser.add_subparsers(required=True, dest="create_cmd") + dir_parser = subparsers.add_parser( + "dir", help="Creates a new thing in directory.") + dir_parser.add_argument("--broker", + action="store_true", + help="Creates a broker queue.") + repo_parser = subparsers.add_parser( + "repo", help="Creates a copy of an existing thing in repository.") + configure_identifier_parser(repo_parser) + configure_credentials_parser(dir_parser) + configure_credentials_parser(repo_parser) def config_update_parser(parser): - configure_credentials_parser(parser) + config_ditto_parser(parser) parser.add_argument( "config", help="Path to json formatted file that specifies the thing.") + configure_credentials_parser(parser) def config_delete_parser(parser): @@ -204,22 +229,30 @@ def create_arg_parser(): return parser +def get_thing_id_from_args(args): + thing_id = None + if args.f: + thing_id = get_identifier_from_file(args.f) + elif args.i: + thing_id = args.i + return thing_id + + +def get_url_from_args(args): + url = "https://dir.s3i.vswf.dev/api/2/" + if args.repo: + url = "https://ditto.s3i.vswf.dev/api/2/" + return url + def main(): # TODO: Distinguish between Repository and Directory class, parser = create_arg_parser() args = parser.parse_args() token = get_token_from_args(args) - - url = "https://dir.s3i.vswf.dev/api/2/" - if args.repo: - url = "https://ditto.s3i.vswf.dev/api/2/" if args.command == "show": + url = get_url_from_args(args) use_repo = args.repo - thing_id = None - if args.f: - thing_id = get_identifier_from_file(args.f) - if args.i: - thing_id = args.i + thing_id = get_thing_id_from_args(args) if validate_s3i_uuid(thing_id): show_thing(token, thing_id, use_repo) else: @@ -227,9 +260,15 @@ def main(): elif args.command == "update": with open(args.config, "r") as config_file: body = json.load(config_file) + url = get_url_from_args(args) update_thing(body["thingId"], token, body, url) elif args.command == "create": - create_thing(url, token, encrypted=False) + config = Config(token=token) + if args.create_cmd == "dir": + create_thing_in_directory(config, args.broker) + else: + thing_id = get_thing_id_from_args(args) + create_thing_in_repo(config, thing_id) elif args.command == "delete": delete_thing(token, args.thingId) -- GitLab From be3d879e7cfed2f88a935ca698b787dd6a6ceb3e Mon Sep 17 00:00:00 2001 From: "C. Albrecht" Date: Fri, 25 Sep 2020 16:14:15 +0200 Subject: [PATCH 06/84] NEW: added script thing_to_ditto_thing.py --- scripts/thing_to_ditto_thing.py | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 scripts/thing_to_ditto_thing.py diff --git a/scripts/thing_to_ditto_thing.py b/scripts/thing_to_ditto_thing.py new file mode 100644 index 0000000..abd8fa1 --- /dev/null +++ b/scripts/thing_to_ditto_thing.py @@ -0,0 +1,34 @@ +import json + +def add_feature(features, feature_id, feature_name): + short_id = feature_id[14:] + properties = {feature_name : ""} + features[short_id] = {"properties" : properties} + +def transform(obj, features): + for key in obj: + value = obj[key] + if isinstance(value, dict): + transform(value, features) + elif isinstance(value, list): + for ele in value: + if isinstance(ele, dict): + transform(ele, features) + elif isinstance(value, str): + if value.startswith("ditto-feature:"): + add_feature(features, value, key) + +def main(): + filename = "" + with open(filename, "r") as in_file: + thing = json.load(in_file) + features = thing.get("features", None) + if not features: + features = {} + thing["features"] = features + + transform(thing, features) + print(json.dumps(thing, indent=2)) + + +main() -- GitLab From 58490d92570b26200d19d1ceabccd858c20df9d0 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 29 Sep 2020 12:30:44 +0200 Subject: [PATCH 07/84] add wheel-file-creation in gitlab ci --- .gitlab-ci.yml | 20 ++++++++++++++++++++ setup.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 .gitlab-ci.yml create mode 100644 setup.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..bdb9705 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,20 @@ +image: python + +stages: + - build_wheel + + +wheel: + tags: + - wheel + stages: build_wheel + script: + - pip install --upgrade pip setuptools wheel + - python setup.py bdist_wheel + - mv dist/ public + + artifacts: + paths: + - public + only: + - dev_chen diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..7561e54 --- /dev/null +++ b/setup.py @@ -0,0 +1,29 @@ +import setuptools + +with open("README.md", "r") as fh: + long_description = fh.read() + +with open("requirements.txt", "r") as req: + requirements = req.read() + + +setuptools.setup( + name = "fml40 reference implementation", + version = "0.1", + author = "Kompetenzzentrum Wald und Holz 4.0", + author_email = "s3i@kwh40.de", + description = "fml40 reference implementation basic functions", + long_description=long_description, + long_description_content_type="text/markdown", + url = "https://www.kwh40.de/", + packages=setuptools.find_packages(), + install_requires= [ + "Pykka==2.0.2", + "pylint==2.4.4" + ], + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: LGPL", + "Operating System :: OS Independent", + ], +) -- GitLab From 078d385a9bed4a24b74817bed5ea2a1198a1abea Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 29 Sep 2020 12:35:41 +0200 Subject: [PATCH 08/84] fix gitlab ci --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bdb9705..e34eedf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,7 +7,7 @@ stages: wheel: tags: - wheel - stages: build_wheel + stage: build_wheel script: - pip install --upgrade pip setuptools wheel - python setup.py bdist_wheel -- GitLab From e6ca69ddd1588879e70b91edae17c78b40b096e5 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 29 Sep 2020 14:58:59 +0200 Subject: [PATCH 09/84] fix gitlab ci file and add wheel to readme --- .gitlab-ci.yml | 1 + README.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e34eedf..29ea747 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,4 +17,5 @@ wheel: paths: - public only: + - master - dev_chen diff --git a/README.md b/README.md index 609edaf..0a3de2b 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ ============================== Reference implementation of the forest modelling language 4.0 (fml 4.0) which is explained in a KWH 4.0 white paper (not public yet). +To use the fml40 reference implementation package in your own project you can install it using the latest [wheel]. +(https://git.rwth-aachen.de/kwh40/fml40-reference-implementation/-/jobs/artifacts/master/raw/public/fml40_reference_implementation-0.1-py3-none-any.whl?job=wheel) Features -------- -- GitLab From 6cf44b627cde3d9252d36101b6476e264f92ab34 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 29 Sep 2020 22:32:06 +0200 Subject: [PATCH 10/84] fix setup bug --- .gitlab-ci.yml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 29ea747..419728e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,4 +18,4 @@ wheel: - public only: - master - - dev_chen + - fix_bug_whell diff --git a/setup.py b/setup.py index 7561e54..c3a7a3a 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( - name = "fml40 reference implementation", + name = "fml40", version = "0.1", author = "Kompetenzzentrum Wald und Holz 4.0", author_email = "s3i@kwh40.de", -- GitLab From b70989448e201358ff8d4fa0777ae460b5baf7ed Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 29 Sep 2020 22:37:19 +0200 Subject: [PATCH 11/84] add init.py for fml40 --- fml40/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 fml40/__init__.py diff --git a/fml40/__init__.py b/fml40/__init__.py new file mode 100644 index 0000000..e69de29 -- GitLab From b770cb17316e3715a1a231d7ed269c62ed18404d Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 29 Sep 2020 23:00:39 +0200 Subject: [PATCH 12/84] fix package in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c3a7a3a..00d0a18 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ long_description=long_description, long_description_content_type="text/markdown", url = "https://www.kwh40.de/", - packages=setuptools.find_packages(), + packages=['fml40'], install_requires= [ "Pykka==2.0.2", "pylint==2.4.4" -- GitLab From 954a5474015561b4ce51130551e531a236d7e56d Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 29 Sep 2020 23:07:02 +0200 Subject: [PATCH 13/84] DEL dev branch from gitlab ci --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 419728e..2d5051a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,4 +18,3 @@ wheel: - public only: - master - - fix_bug_whell -- GitLab From d21f8aa8bfab9bcb0330f92a7662913b46315909 Mon Sep 17 00:00:00 2001 From: Jiahang Chen <13508-jiahang.chen@users.noreply.git.rwth-aachen.de> Date: Tue, 29 Sep 2020 23:18:39 +0200 Subject: [PATCH 14/84] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0a3de2b..6af66bb 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ ============================== Reference implementation of the forest modelling language 4.0 (fml 4.0) which is explained in a KWH 4.0 white paper (not public yet). -To use the fml40 reference implementation package in your own project you can install it using the latest [wheel]. -(https://git.rwth-aachen.de/kwh40/fml40-reference-implementation/-/jobs/artifacts/master/raw/public/fml40_reference_implementation-0.1-py3-none-any.whl?job=wheel) +To use the fml40 reference implementation package in your own project you can install it using the latest [wheel](https://git.rwth-aachen.de/kwh40/fml40-reference-implementation/-/jobs/artifacts/master/raw/public/fml40-0.1-py3-none-any.whl?job=wheel). +To install this wheel, go to the respective directory or switch to your designated virtual environment and install the .whl file, just run pip install https://git.rwth-aachen.de/kwh40/fml40-reference-implementation/-/jobs/artifacts/master/raw/public/fml40-0.1-py3-none-any.whl?job=wheel Features -------- -- GitLab From b13aa4d7ea56172bb1c5ef769c2daf017bbf4a2d Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Wed, 30 Sep 2020 13:54:40 +0200 Subject: [PATCH 15/84] rebase dev_albrecht and change gitlab ci --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2d5051a..29ea747 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,3 +18,4 @@ wheel: - public only: - master + - dev_chen -- GitLab From b286ffd2b718e3922cd8f485407e96cd0506b052 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Wed, 30 Sep 2020 13:56:39 +0200 Subject: [PATCH 16/84] ADD dev_chen to gitlabci --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2d5051a..29ea747 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,3 +18,4 @@ wheel: - public only: - master + - dev_chen -- GitLab From f906a51daef2590fa69c8b124c6ac91eedf97ab1 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Fri, 2 Oct 2020 21:49:53 +0200 Subject: [PATCH 17/84] ADD Notebooks for workshop --- .gitignore | 206 +++++++++++++++++- configs/config_dt.json | 24 ++ dt_creation.py | 51 +++++ dt_creation_hmi.py | 49 +++++ ml/__init__.py | 0 ml/dt_factory.py | 68 +++--- ml/ml40/__init__.py | 0 ml/ml40/features/feature.py | 2 + .../features/functionalities/functionality.py | 1 - ml/thing.py | 171 ++++++++++----- 10 files changed, 492 insertions(+), 80 deletions(-) create mode 100644 configs/config_dt.json create mode 100644 dt_creation.py create mode 100644 dt_creation_hmi.py create mode 100644 ml/__init__.py create mode 100644 ml/ml40/__init__.py diff --git a/.gitignore b/.gitignore index 15cca76..51d1c94 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/configs/config_dt.json b/configs/config_dt.json new file mode 100644 index 0000000..1887885 --- /dev/null +++ b/configs/config_dt.json @@ -0,0 +1,24 @@ +{ + "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 diff --git a/dt_creation.py b/dt_creation.py new file mode 100644 index 0000000..670f4d1 --- /dev/null +++ b/dt_creation.py @@ -0,0 +1,51 @@ +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") + + + diff --git a/dt_creation_hmi.py b/dt_creation_hmi.py new file mode 100644 index 0000000..18aa633 --- /dev/null +++ b/dt_creation_hmi.py @@ -0,0 +1,49 @@ +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__()) diff --git a/ml/__init__.py b/ml/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/dt_factory.py b/ml/dt_factory.py index ef9f107..02f0667 100644 --- a/ml/dt_factory.py +++ b/ml/dt_factory.py @@ -1,6 +1,6 @@ """ 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.service.service import Service +from ml.ml40.roles.servives.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: - print("Incomplete model: attributes missing!") - sys.exit() - + sys.exit("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 + diff --git a/ml/ml40/__init__.py b/ml/ml40/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/ml40/features/feature.py b/ml/ml40/features/feature.py index 47bd449..0cd95d8 100644 --- a/ml/ml40/features/feature.py +++ b/ml/ml40/features/feature.py @@ -1,5 +1,7 @@ from ml.managed_actor import ManagedActor from ml.identifier import ID +import types + class Feature(ManagedActor): def __init__(self, name, ref_managing_actor): diff --git a/ml/ml40/features/functionalities/functionality.py b/ml/ml40/features/functionalities/functionality.py index 1135143..fb97908 100644 --- a/ml/ml40/features/functionalities/functionality.py +++ b/ml/ml40/features/functionalities/functionality.py @@ -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 diff --git a/ml/thing.py b/ml/thing.py index a88b931..262455c 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -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.__represents = "" - self.__thing_structure = {} - self.__default_hmi = "" + self.roles = [] + self.__features = {} + 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") idp = IdentityProvider( grant_type=self.__grant_type, client_id=self.__thing_id, @@ -170,17 +173,33 @@ def __connect_with_idp(self): ) # This may take a while so fetch token directly. - idp.get_token(TokenType.ACCESS_TOKEN) idp.run_forever(token_type=TokenType.ACCESS_TOKEN, on_new_token=self.__on_token) def __on_token(self, token): self.__access_token = token + self.__connect_with_dir() if self.__is_broker: self.__connect_with_broker() if self.__is_repo: self.__connect_with_repo() + def __connect_with_dir(self): + print( + bcolors.OKBLUE + + "[S³I][Dir]" + + bcolors.ENDC + + ": Connect with S3I-Directory" + ) + self.dir = Directory(s3i_dir_url="https://dir.s3i.vswf.dev/api/2/", token=self.__access_token) + def __connect_with_repo(self): + print( + bcolors.OKBLUE + + "[S³I][Repo]" + + bcolors.ENDC + + ": Connect with S3I-Repository" + ) + self._ws = websocket.WebSocketApp( BaseVariable.REPO_WWS_URL, header={"Authorization: Bearer {}".format(self.__access_token)}, @@ -226,20 +245,47 @@ def sync_with_repo(self, path, topic): self._ws.send(json.dumps(msg)) def __connect_with_broker(self): - broker = Broker( + print(bcolors.OKBLUE + "[S³I][Broker]" + bcolors.ENDC + ": Connect with S3I-Broker") + self.broker = Broker( auth_form="Username/Password", username=" ", password=self.__access_token, host=BaseVariable.BROKER_HOST, ) - self._broker = broker + endpoints = self.dir.queryThingIDBased(thingID=self.thing_id)["attributes"]["allEndpoints"] + for ep in endpoints: + if "s3ib" in ep: + self.__endpoint = ep + threading.Thread( - target=self._broker.receive, + target=self.broker.receive, args=(self.__endpoint, self.__on_broker_callback), ).start() def __on_broker_callback(self, ch, method, properties, body): - self.on_receive(body) + body = ast.literal_eval(body.decode("utf-8")) + if isinstance(body, int): + print(body) + elif isinstance(body, str): + print(body) + else: + message_type = body.get("messageType") + if message_type == "userMessage": + self.on_user_message(body) + if message_type == "serviceRequest": + self.on_service_request(body) + elif message_type == "getValueRequest": + self.on_get_value_request(body) + elif message_type == "getValueReply": + self.on_get_value_reply(body) + elif message_type == "serviceReply": + self.on_service_reply(body) + else: + ### TODO send user message reply back + pass + + def on_user_message(self, msg): + print(msg) def on_get_value_request(self, msg): get_value_reply = GetValueReply() @@ -265,19 +311,40 @@ def on_get_value_request(self, msg): replyingToUUID=request_msg_id, ) msg_txt = get_value_reply.msg.__str__() - self._broker.send( + self.broker.send( receiver_endpoints=[request_sender_endpoint], msg=msg_txt, ) - def on_service_request(self, body_json): service_type = body_json.get("serviceType") - func = self.features.get(service_type, None) - if func is None: + service_functionality = service_type.split('/')[0] + service_functionality_obj = self.features.get(service_functionality, None) + if service_functionality_obj is None: APP_LOGGER.critical( - "Functionality %s is missing in %s!" % (func, self.name) + "Functionality %s is not one of the built-in functionalities in %s!" % (service_functionality, self.name) ) else: pass # TODO: Call right functionality. # func_proxy = func.proxy() + func_proxy = service_functionality_obj.proxy() + method = getattr(func_proxy, service_type.split('/')[1]) + method("test_job") + + def on_get_value_reply(self, msg): + pass + + def on_service_reply(self, msg): + pass + + def add_function_impl(self, obj, 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) + + -- GitLab From 0176df764c125956e6db2eaa23f18228330ab93e Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 5 Oct 2020 15:18:03 +0200 Subject: [PATCH 18/84] update ml package --- .../functionalities/accepts_felling_jobs.py | 23 ++- .../features/functionalities/accepts_jobs.py | 2 +- .../properties/values/documents/jobs/job.py | 3 +- ml/ml40/roles/hmis/hmi.py | 2 +- ml/thing.py | 183 ++++++++---------- ml/tools.py | 59 ++++-- 6 files changed, 148 insertions(+), 124 deletions(-) diff --git a/ml/fml40/features/functionalities/accepts_felling_jobs.py b/ml/fml40/features/functionalities/accepts_felling_jobs.py index 2e5b243..caaa0e9 100644 --- a/ml/fml40/features/functionalities/accepts_felling_jobs.py +++ b/ml/fml40/features/functionalities/accepts_felling_jobs.py @@ -8,14 +8,25 @@ def __init__(self, name, ref_managing_actor): super(AcceptsFellingJobs, self).__init__( name=name, ref_managing_actor=ref_managing_actor ) + self.job_list = [] def acceptJob(self, job: FellingJob) -> bool: - APP_LOGGER.info("Checking if the job can be accepted.") - proxy_executer = self.managing_actor.proxy_functionalities.get()[ - self.proxy_name - ] - proxy_executer.executeJob(job) - return True + APP_LOGGER.info("Checking if the felling job can be accepted.") + return self.add_to_job_list(job) + + def queryJobStatus(self, identifier): + pass + + def removeJob(self, identifier): + pass def from_json(self, json_obj): super().from_json(json_obj) + + def add_to_job_list(self, job): + if job not in self.job_list: + self.job_list.append(job) + return True + else: + APP_LOGGER.info("[S³I]: This felling job has been already added in job list") + return False \ No newline at end of file diff --git a/ml/ml40/features/functionalities/accepts_jobs.py b/ml/ml40/features/functionalities/accepts_jobs.py index 4c99518..56b3956 100644 --- a/ml/ml40/features/functionalities/accepts_jobs.py +++ b/ml/ml40/features/functionalities/accepts_jobs.py @@ -12,7 +12,7 @@ def __init__(self, name, ref_managing_actor): self.__jobs = dict() def acceptJob(self, job: Job) -> bool: - print("I am checking if i can accept job {}".format(job)) + pass def queryJobStatus(self, identifier: ID) -> JobStatus: pass diff --git a/ml/ml40/features/properties/values/documents/jobs/job.py b/ml/ml40/features/properties/values/documents/jobs/job.py index d084cb4..16db691 100644 --- a/ml/ml40/features/properties/values/documents/jobs/job.py +++ b/ml/ml40/features/properties/values/documents/jobs/job.py @@ -3,7 +3,6 @@ class Job(Document): - def __init__(self, name, ref_managing_actor, status: JobStatus): + def __init__(self, name, ref_managing_actor, status=JobStatus.Pending): super(Job, self).__init__(name=name, ref_managing_actor=ref_managing_actor) - self.status = status diff --git a/ml/ml40/roles/hmis/hmi.py b/ml/ml40/roles/hmis/hmi.py index 12a5951..8d4516f 100644 --- a/ml/ml40/roles/hmis/hmi.py +++ b/ml/ml40/roles/hmis/hmi.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.Role import Role +from ml.ml40.roles.role import Role class HMI(Role): diff --git a/ml/thing.py b/ml/thing.py index 262455c..5937186 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -1,34 +1,17 @@ import ast import threading -import time import websocket import json -import os import uuid -from s3i import IdentityProvider, TokenType, Broker, GetValueReply -from s3i import Directory +from s3i import IdentityProvider, TokenType, Broker, GetValueReply, Directory +from s3i.broker import BrokerREST +from s3i.messages import ServiceReply -# from pynput import keyboard from ml.managing_actor import ManagingActor from ml.tools import BColors -from ml.tools import send_request from ml.tools import send_message -from ml.tools import decode_message -from ml.tools import load_config +from ml.tools import find_broker_endpoint from ml.app_logger import APP_LOGGER -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): @@ -54,7 +37,8 @@ def __init__( grant_type: str, is_broker: bool, is_repo: bool, - username=None, + is_broker_rest=True, + username=None, password=None, ): super(Thing, self).__init__() @@ -66,6 +50,7 @@ def __init__( self.__client_secret = client_secret self.__is_broker = is_broker + self.__is_broker_rest = is_broker_rest self.__is_repo = is_repo self.__access_token = "" @@ -97,38 +82,6 @@ def features(self): def features(self, value): self.__features = value - - def send_message(self, receiver_ids, req): - send_message(self.__access_token, receiver_ids, req) - - def send_service_message(self, receiver_ids, service_class_name, parameters): - APP_LOGGER.info(f"Sending message {service_class_name}") - send_request( - self.__access_token, - self.thing_id, - receiver_ids, - service_class_name, - 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) - elif message_type == "getValueRequest": - self.on_get_value_request(body_json) - elif message_type == "getValueReply": - self.on_process_get_value_reply(body_json) - elif message_type == "serviceReply": - self.on_process_service_reply(body_json) - """ - @property def class_name(self): return self.__class_name @@ -154,14 +107,11 @@ def thing_id(self): return self.__thing_id def run_forever(self): - print("[S³I]: Launch {}{}{}".format(bcolors.OKGREEN, self.name, bcolors.ENDC)) + 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") + print(BColors.OKBLUE + "[S³I][IdP]" + BColors.ENDC + ": Connect with S3I-IdentityProvider") idp = IdentityProvider( grant_type=self.__grant_type, client_id=self.__thing_id, @@ -185,18 +135,18 @@ def __on_token(self, token): def __connect_with_dir(self): print( - bcolors.OKBLUE + BColors.OKBLUE + "[S³I][Dir]" - + bcolors.ENDC + + BColors.ENDC + ": Connect with S3I-Directory" ) self.dir = Directory(s3i_dir_url="https://dir.s3i.vswf.dev/api/2/", token=self.__access_token) def __connect_with_repo(self): print( - bcolors.OKBLUE + BColors.OKBLUE + "[S³I][Repo]" - + bcolors.ENDC + + BColors.ENDC + ": Connect with S3I-Repository" ) @@ -245,47 +195,65 @@ def sync_with_repo(self, path, topic): self._ws.send(json.dumps(msg)) def __connect_with_broker(self): - print(bcolors.OKBLUE + "[S³I][Broker]" + bcolors.ENDC + ": Connect with S3I-Broker") - self.broker = Broker( - auth_form="Username/Password", - username=" ", - password=self.__access_token, - host=BaseVariable.BROKER_HOST, - ) - endpoints = self.dir.queryThingIDBased(thingID=self.thing_id)["attributes"]["allEndpoints"] - for ep in endpoints: - if "s3ib" in ep: - self.__endpoint = ep + 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) + + def receive(): + while True: + msg_str = self.broker.receive_once(self.__endpoint) + if msg_str == "": + continue + else: + self.__on_broker_callback(ch=None, method=None, properties=None, + body=json.loads(msg_str)) - threading.Thread( - target=self.broker.receive, - args=(self.__endpoint, self.__on_broker_callback), - ).start() + threading.Thread( + target=receive, + ).start() + + else: + self.broker = Broker( + auth_form="Username/Password", + username=" ", + password=self.__access_token, + host=BaseVariable.BROKER_HOST, + ) + + threading.Thread( + target=self.broker.receive, + args=(self.__endpoint, self.__on_broker_callback), + ).start() def __on_broker_callback(self, ch, method, properties, body): - body = ast.literal_eval(body.decode("utf-8")) - if isinstance(body, int): + if isinstance(body, bytes): + body = ast.literal_eval(body.decode("utf-8")) + elif isinstance(body, int): print(body) + return elif isinstance(body, str): print(body) + return + + message_type = body.get("messageType") + if message_type == "userMessage": + self.on_user_message(body) + elif message_type == "serviceRequest": + self.on_service_request(body) + elif message_type == "getValueRequest": + self.on_get_value_request(body) + elif message_type == "getValueReply": + self.on_get_value_reply(body) + elif message_type == "serviceReply": + self.on_service_reply(body) else: - message_type = body.get("messageType") - if message_type == "userMessage": - self.on_user_message(body) - if message_type == "serviceRequest": - self.on_service_request(body) - elif message_type == "getValueRequest": - self.on_get_value_request(body) - elif message_type == "getValueReply": - self.on_get_value_reply(body) - elif message_type == "serviceReply": - self.on_service_reply(body) - else: - ### TODO send user message reply back - pass + ### TODO send user message reply back + pass def on_user_message(self, msg): - print(msg) + pass def on_get_value_request(self, msg): get_value_reply = GetValueReply() @@ -321,30 +289,39 @@ def on_service_request(self, body_json): service_functionality_obj = self.features.get(service_functionality, None) 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) ) else: pass # TODO: Call right functionality. - # func_proxy = func.proxy() func_proxy = service_functionality_obj.proxy() method = getattr(func_proxy, service_type.split('/')[1]) - method("test_job") + result = {"ok": method("test_job").get()} + + service_reply = ServiceReply() + service_reply.fillServiceReply( + senderUUID=self.thing_id, + receiverUUID=body_json.get("sender", None), + serviceType=body_json.get("serviceType", None), + results=result, + replyingToUUID=body_json.get("identifier", None), + msgUUID=str(uuid.uuid4()) + ) + send_message(self.broker, self.dir, service_reply.msg) def on_get_value_reply(self, msg): - pass + print(msg) def on_service_reply(self, msg): - pass + print(msg) def add_function_impl(self, obj, 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 + "Functionality %s is not one of the build-in functionalities" % feature_name ) else: self.__features[feature_name] = obj.start("", self.actor_ref) - - diff --git a/ml/tools.py b/ml/tools.py index 9eccfdc..5d9d298 100644 --- a/ml/tools.py +++ b/ml/tools.py @@ -4,6 +4,7 @@ import time import uuid import json +import os from functools import partial from s3i import Directory from s3i import Broker @@ -177,6 +178,22 @@ def send_request(access_token, sender_id, receiver_ids, service_type, parameters 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: @@ -218,17 +235,6 @@ def create_request(sender_id, receiver_ids, service_type, parameters={}): return req -def send_message(access_token, receiver_ids, req): - 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", - ) - s3i_broker.send(receiver_endpoints, req.msg.__str__()) - - # 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 @@ -290,6 +296,35 @@ def dir_name_to_default_endpoints(idp, name): return endpoint +def make_config_file(dt_id, name, roles, features=[]): + config_file = { + "thingId": dt_id, + "policyId": dt_id, + "attributes": { + "class": "ml40::Thing", + "name": name, + "roles": [ + { + "class": roles + } + ], + "features": [ + ] + } + } + for feature in features: + config_file["attributes"]["features"].append( + { + "class": feature + } + ) + cwd = os.getcwd() + path = os.path.join(cwd, "configs", "config_{}.json".format(name)) + with open(path, 'wb') as file: + file.write(json.dumps(config_file).encode('utf-8')) + return "config_{}.json".format(name) + + def load_config(config_filepath): """Creates a json object from a json formatted file found at config_filepath. @@ -346,3 +381,5 @@ def get_requests(config): receiver_uuids.append(target) requests.append((msg_type, receiver_uuids, parameters)) return requests + + -- GitLab From da94ac4337dbdcc527d047f05d9dba3be9910682 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 5 Oct 2020 15:49:23 +0200 Subject: [PATCH 19/84] change package name --- README.md | 4 ++-- setup.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6af66bb..866fc88 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ ============================== Reference implementation of the forest modelling language 4.0 (fml 4.0) which is explained in a KWH 4.0 white paper (not public yet). -To use the fml40 reference implementation package in your own project you can install it using the latest [wheel](https://git.rwth-aachen.de/kwh40/fml40-reference-implementation/-/jobs/artifacts/master/raw/public/fml40-0.1-py3-none-any.whl?job=wheel). -To install this wheel, go to the respective directory or switch to your designated virtual environment and install the .whl file, just run pip install https://git.rwth-aachen.de/kwh40/fml40-reference-implementation/-/jobs/artifacts/master/raw/public/fml40-0.1-py3-none-any.whl?job=wheel +To use the fml40 reference implementation package in your own project you can install it using the latest [wheel](https://git.rwth-aachen.de/kwh40/fml40-reference-implementation/-/jobs/artifacts/master/raw/public/ml-0.1-py3-none-any.whl?job=wheel). +To install this wheel, go to the respective directory or switch to your designated virtual environment and install the .whl file, just run pip install https://git.rwth-aachen.de/kwh40/fml40-reference-implementation/-/jobs/artifacts/master/raw/public/ml-0.1-py3-none-any.whl?job=wheel Features -------- diff --git a/setup.py b/setup.py index 00d0a18..0d819cd 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( - name = "fml40", + name = "ml", version = "0.1", author = "Kompetenzzentrum Wald und Holz 4.0", author_email = "s3i@kwh40.de", @@ -16,7 +16,7 @@ long_description=long_description, long_description_content_type="text/markdown", url = "https://www.kwh40.de/", - packages=['fml40'], + packages=['ml'], install_requires= [ "Pykka==2.0.2", "pylint==2.4.4" -- GitLab From 15fd924cde6f68ef29be2e7abd64bfd6650136df Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 5 Oct 2020 16:14:43 +0200 Subject: [PATCH 20/84] fix setup problem --- configs/config.json | 1 + configs/config_dt test.json | 1 + configs/config_my HMI.json | 1 + configs/config_test.json | 1 + dt_creation.py | 47 ++++++++++----- dt_creation_hmi.py | 115 +++++++++++++++++++++++++++--------- requirements.txt | 9 +-- setup.py | 7 +++ 8 files changed, 130 insertions(+), 52 deletions(-) create mode 100644 configs/config.json create mode 100644 configs/config_dt test.json create mode 100644 configs/config_my HMI.json create mode 100644 configs/config_test.json diff --git a/configs/config.json b/configs/config.json new file mode 100644 index 0000000..e59d1d4 --- /dev/null +++ b/configs/config.json @@ -0,0 +1 @@ +{"thingId": "s3i:feb857f1-3058-4ed4-8826-bbf182cbb16e", "policyId": "s3i:feb857f1-3058-4ed4-8826-bbf182cbb16e", "attributes": {"class": "ml40::Thing", "name": "dt test", "roles": [{"class": "fml40::Harvester"}], "features": [{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}]}} \ No newline at end of file diff --git a/configs/config_dt test.json b/configs/config_dt test.json new file mode 100644 index 0000000..231c191 --- /dev/null +++ b/configs/config_dt test.json @@ -0,0 +1 @@ +{"thingId": "s3i:b4a0ec9b-66be-4390-b786-9c70b1c017e6", "policyId": "s3i:b4a0ec9b-66be-4390-b786-9c70b1c017e6", "attributes": {"class": "ml40::Thing", "name": "dt test", "roles": [{"class": "fml40::Harvester"}], "features": [{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}]}} \ No newline at end of file diff --git a/configs/config_my HMI.json b/configs/config_my HMI.json new file mode 100644 index 0000000..60c72fa --- /dev/null +++ b/configs/config_my HMI.json @@ -0,0 +1 @@ +{"thingId": "s3i:b4f72a11-fb3d-47a3-b499-e7a88eaa5fe1", "policyId": "s3i:b4f72a11-fb3d-47a3-b499-e7a88eaa5fe1", "attributes": {"class": "ml40::Thing", "name": "my HMI", "roles": [{"class": "ml40::HMI"}], "features": []}} \ No newline at end of file diff --git a/configs/config_test.json b/configs/config_test.json new file mode 100644 index 0000000..491b14b --- /dev/null +++ b/configs/config_test.json @@ -0,0 +1 @@ +{"thingId": "s3i:de59a481-dd97-429f-a0ca-fab013b6bfbc", "policyId": "s3i:de59a481-dd97-429f-a0ca-fab013b6bfbc", "attributes": {"class": "ml40::Thing", "name": "test", "roles": [{"class": "fml40::Harvester"}], "features": [{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}]}} \ No newline at end of file diff --git a/dt_creation.py b/dt_creation.py index 670f4d1..8465927 100644 --- a/dt_creation.py +++ b/dt_creation.py @@ -1,19 +1,18 @@ import s3i import json import jwt -from ml.tools import load_config, get_s3i_broker, get_receiver_callback_func +import requests +from ml.tools import load_config, get_s3i_broker, get_receiver_callback_func, make_config_file 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" -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.") - +username = "chen" +password = "8810515" s3i_identity_provider = s3i.IdentityProvider(grant_type='password', identity_provider_url="https://idp.s3i.vswf.dev/", realm='KWH', @@ -26,13 +25,33 @@ ''' 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" +# TODO class for config api in master to merge +# create a thing identity for the dt +create_thing_resp = requests.post(url="https://config.s3i.vswf.dev/things/", + headers={"Authorization": "Bearer {}".format(access_token), + "Content-Type": "application/json"}, + data=json.dumps({})) +print(create_thing_resp.json()) +dt_id = create_thing_resp.json().get("identifier", None) +print(dt_id) +create_cloud_copy_resp = requests.post(url="https://config.s3i.vswf.dev/things/{}/repository".format(dt_id), + headers={"Authorization": "Bearer {}".format(access_token), + "Content-Type": "application/json"}) +create_endpoint_resp = requests.post(url="https://config.s3i.vswf.dev/things/{}/broker".format(dt_id), + headers={"Authorization": "Bearer {}".format(access_token), + "Content-Type": "application/json"}, + data=json.dumps({"encrypted": False}) + ) -dt_model = load_config('configs/config_dt.json') +dt_secret = create_thing_resp.json().get("secret", None) +#dt_secret = "c49e4050-d194-4f08-90c5-e17fd5befba5" +dt_name = "test" +config_file_name = make_config_file(dt_id=dt_id, name=dt_name, roles="fml40::Harvester", features=["fml40::ProvidesProductionData", + "fml40::AcceptsFellingJobs"]) +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, is_broker=True, is_repo=False) dt_proxy = dt_ref.proxy() @@ -40,12 +59,10 @@ class AcceptsFellingJobsImpl(AcceptsFellingJobs): - def acceptJob(self, job): - print("i have accepted a job {}".format(job)) - return True + def my_accept_job_func(self, job): + APP_LOGGER.info("I am checking if the felling job can be accepted.") + return self.add_to_job_list(job) dt_proxy.add_function_impl(AcceptsFellingJobsImpl, "AcceptsFellingJobs") - - diff --git a/dt_creation_hmi.py b/dt_creation_hmi.py index 18aa633..a8384db 100644 --- a/dt_creation_hmi.py +++ b/dt_creation_hmi.py @@ -1,49 +1,106 @@ import s3i import jwt import uuid +from ml.tools import find_broker_endpoint, make_config_file, load_config +from ml.dt_factory import create_dt_ref +from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob +import requests +import json, time 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_id = "s3i:b4f72a11-fb3d-47a3-b499-e7a88eaa5fe1" +hmi_secret = "84df6340-9d64-4ae6-9076-86c224ec484a" + +config_file_name = make_config_file(dt_id=hmi_id, name="my HMI", roles="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=True, is_repo=False) +hmi_proxy = hmi_ref.proxy() +hmi_proxy.run_forever() + + +s3i_dir = s3i.Directory(s3i_dir_url="https://dir.s3i.vswf.dev/api/2/", token=hmi_proxy.access_token.get()) +hmi_json = s3i_dir.queryThingIDBased(thingID=hmi_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 + +messageIds = list() + + serv_req = s3i.messages.ServiceRequest() +receiver = input("[S³I]: Please enter the id of your digital twin: ") +job = FellingJob("", hmi_ref) serv_req.fillServiceRequest( - senderUUID=client_id, receiverUUID=["s3i:bea74f50-fa6c-4aba-b7f8-90153b401ffc"], sender_endpoint=hmi_s3ib_ep, - serviceType="AcceptsFellingJob/acceptJob", + senderUUID=hmi_id, receiverUUID=[receiver], sender_endpoint=hmi_s3ib_ep, + serviceType="AcceptsFellingJobs/my_accept_job_func", 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 +receiver_endpoint = find_broker_endpoint(s3i_dir, thing_id=receiver) + +service_req_response = requests.post(url="https://broker.s3i.vswf.dev/{}".format(receiver_endpoint), + data=json.dumps(serv_req.msg), + headers={'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + hmi_proxy.access_token.get()} + ) +print(service_req_response.text) + +"""Store the message id into message storage""" +messageIds.append(serv_req.msg["identifier"]) + +time.sleep(1) +response = requests.get(url="https://broker.s3i.vswf.dev/{}".format(hmi_s3ib_ep), headers={'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + hmi_proxy.access_token.get()}) +print(response.text) + +""" +def receive(msg_type): + access_token = s3i_identity_provider.get_token(s3i.TokenType.ACCESS_TOKEN) + headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + access_token} + response = requests.get(url="https://broker.s3i.vswf.dev/{}".format(hmi_id), headers=headers) + msg_json = "" + value_json = "" + json_acceptable_string = response.text.replace("'", "\"") + if json_acceptable_string: + if check_message_encryption(response.text.strip('"')) == "pgp": + print_with_timestamp( + "You received a PGP message but this notebook can not decrypt PGP messages. Use the 03_inbox notebook to receive PGP messages.") + print_with_timestamp("PGP Message: " + response.text) + repeat(msg_type) + else: + msg_json = json.loads(json_acceptable_string) + if msg_json["replyingToMessage"] in messageIds and msg_json["messageType"] == msg_type: + if msg_type == "serviceReply": + value_json = msg_json["results"] + elif msg_type == "getValueReply": + value_json = msg_json["value"] + elif msg_type == "setValueReply": + value_json = msg_json["ok"] + messageIds.remove(msg_json["replyingToMessage"]) + else: + repeat(msg_type) + + else: + print_with_timestamp("The harvester did not respond yet.") + repeat(msg_type) + return msg_json, value_json + -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__()) +def repeat(msg_type): + decision = input("[S3I] Do you want to check for new messages again? [j/n]") + if decision in yes: + receive(msg_type) + elif decision in no: + print_with_timestamp( + "You do not want to check for more messages. If you want to check for new messages, just execute this cell again (Run button or SHIFT+RETURN)") + else: + print_with_timestamp( + "I could not understand your response. If you want to check for new messages, just execute this cell again (Run button or SHIFT+RETURN)") +""" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index af0299b..0637638 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,2 @@ -https://git.rwth-aachen.de/kwh40/s3i/-/jobs/artifacts/master/raw/public/s3i-0.4-py3-none-any.whl?job=wheel -Pykka -websocket -pynput -opcua -requests -asyncio -pylint==2.4.4 + #https://git.rwth-aachen.de/kwh40/s3i/-/jobs/758151/artifacts/raw/public/s3i-0.4-py3-none-any.whl diff --git a/setup.py b/setup.py index 0d819cd..0237c3e 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,13 @@ install_requires= [ "Pykka==2.0.2", "pylint==2.4.4" + "https://git.rwth-aachen.de/kwh40/s3i/-/jobs/artifacts/master/raw/public/s3i-0.4-py3-none-any.whl?job=wheel", + "websocket", + "pynput", + "opcua", + "requests", + "asyncio", + "pylint==2.4.4" ], classifiers=[ "Programming Language :: Python :: 3", -- GitLab From 1aad4383dc9442e38eed328f62643a9372a135e3 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 5 Oct 2020 16:16:24 +0200 Subject: [PATCH 21/84] repaire requirement txt --- requirements.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0637638..af0299b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,9 @@ - +https://git.rwth-aachen.de/kwh40/s3i/-/jobs/artifacts/master/raw/public/s3i-0.4-py3-none-any.whl?job=wheel +Pykka +websocket +pynput +opcua +requests +asyncio +pylint==2.4.4 #https://git.rwth-aachen.de/kwh40/s3i/-/jobs/758151/artifacts/raw/public/s3i-0.4-py3-none-any.whl -- GitLab From 5d3f934fb9a1c430e70f473dd4df1fbc892720af Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 5 Oct 2020 16:20:14 +0200 Subject: [PATCH 22/84] delete undependet lib --- ml/dt_factory.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ml/dt_factory.py b/ml/dt_factory.py index 02f0667..cfbdcab 100644 --- a/ml/dt_factory.py +++ b/ml/dt_factory.py @@ -8,10 +8,7 @@ from ml.tools import load_config from ml.app_logger import APP_LOGGER from ml.thing import Thing -from customer_code_example.harvester_john_deere import john_deere -from customer_code_example.harvester_ponsse import ponsse -from customer_code_example.forwarder_komatsu import komatsu -from customer_code_example.mini_tractor_driver import MiniTractorDriver + from ml.ml40.roles.servives.service import Service from ml.ml40.features.functionalities.manages_jobs import ManagesJobs -- GitLab From 0f400a67650e7f12954d824205b12d8a9d07858f Mon Sep 17 00:00:00 2001 From: Jiahang Chen <13508-jiahang.chen@users.noreply.git.rwth-aachen.de> Date: Mon, 5 Oct 2020 16:31:51 +0200 Subject: [PATCH 23/84] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0237c3e..3beafc1 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ long_description=long_description, long_description_content_type="text/markdown", url = "https://www.kwh40.de/", - packages=['ml'], + #packages=['ml'], install_requires= [ "Pykka==2.0.2", "pylint==2.4.4" -- GitLab From 17dbcd35e9cee9623d12e090267e8568c3e6aae3 Mon Sep 17 00:00:00 2001 From: Jiahang Chen <13508-jiahang.chen@users.noreply.git.rwth-aachen.de> Date: Mon, 5 Oct 2020 16:33:15 +0200 Subject: [PATCH 24/84] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3beafc1..ce642d4 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ long_description=long_description, long_description_content_type="text/markdown", url = "https://www.kwh40.de/", - #packages=['ml'], + packages=['ml', 'fml40', 'ml40'], install_requires= [ "Pykka==2.0.2", "pylint==2.4.4" -- GitLab From abc37445d1e9d0081df526495520f499924f9e85 Mon Sep 17 00:00:00 2001 From: Jiahang Chen <13508-jiahang.chen@users.noreply.git.rwth-aachen.de> Date: Mon, 5 Oct 2020 16:36:35 +0200 Subject: [PATCH 25/84] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ce642d4..121541b 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ long_description=long_description, long_description_content_type="text/markdown", url = "https://www.kwh40.de/", - packages=['ml', 'fml40', 'ml40'], + packages=['ml', 'ml.fml40', 'ml.ml40'], install_requires= [ "Pykka==2.0.2", "pylint==2.4.4" -- GitLab From db2cfcdc9d657145152a4bb2863c4f720c7db0b0 Mon Sep 17 00:00:00 2001 From: Jiahang Chen <13508-jiahang.chen@users.noreply.git.rwth-aachen.de> Date: Mon, 5 Oct 2020 16:45:58 +0200 Subject: [PATCH 26/84] add find_packages into setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 121541b..df00888 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ long_description=long_description, long_description_content_type="text/markdown", url = "https://www.kwh40.de/", - packages=['ml', 'ml.fml40', 'ml.ml40'], + packages=find_packages(), install_requires= [ "Pykka==2.0.2", "pylint==2.4.4" -- GitLab From 8b30289a99b640abae72087cb9128f935c625a12 Mon Sep 17 00:00:00 2001 From: Jiahang Chen <13508-jiahang.chen@users.noreply.git.rwth-aachen.de> Date: Mon, 5 Oct 2020 16:48:11 +0200 Subject: [PATCH 27/84] import find_packages --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index df00888..a5f7e79 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -import setuptools +import setuptools, find_packages with open("README.md", "r") as fh: long_description = fh.read() -- GitLab From 85c7343a33a15d02551fb6ff962c020a09e8608f Mon Sep 17 00:00:00 2001 From: Jiahang Chen <13508-jiahang.chen@users.noreply.git.rwth-aachen.de> Date: Mon, 5 Oct 2020 16:49:20 +0200 Subject: [PATCH 28/84] Update setup.py --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index a5f7e79..5ae42c8 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -import setuptools, find_packages +import setuptools with open("README.md", "r") as fh: long_description = fh.read() @@ -16,7 +16,7 @@ long_description=long_description, long_description_content_type="text/markdown", url = "https://www.kwh40.de/", - packages=find_packages(), + packages=setuptools.find_packages(), install_requires= [ "Pykka==2.0.2", "pylint==2.4.4" -- GitLab From 1168b98ed4dcdf8a7082fc5bed8356b0d85608d8 Mon Sep 17 00:00:00 2001 From: Jiahang Chen <13508-jiahang.chen@users.noreply.git.rwth-aachen.de> Date: Mon, 5 Oct 2020 16:56:00 +0200 Subject: [PATCH 29/84] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5ae42c8..b03a365 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ packages=setuptools.find_packages(), install_requires= [ "Pykka==2.0.2", - "pylint==2.4.4" + "pylint==2.4.4", "https://git.rwth-aachen.de/kwh40/s3i/-/jobs/artifacts/master/raw/public/s3i-0.4-py3-none-any.whl?job=wheel", "websocket", "pynput", -- GitLab From 8f1aabe318434c909501df578025c5676c071c56 Mon Sep 17 00:00:00 2001 From: Jiahang Chen <13508-jiahang.chen@users.noreply.git.rwth-aachen.de> Date: Mon, 5 Oct 2020 16:58:24 +0200 Subject: [PATCH 30/84] Update setup.py --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index b03a365..fb4c476 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,6 @@ "opcua", "requests", "asyncio", - "pylint==2.4.4" ], classifiers=[ "Programming Language :: Python :: 3", -- GitLab From bb0df68fb30953a815bc4d7926c68ef1117d15a8 Mon Sep 17 00:00:00 2001 From: Jiahang Chen <13508-jiahang.chen@users.noreply.git.rwth-aachen.de> Date: Mon, 5 Oct 2020 17:00:11 +0200 Subject: [PATCH 31/84] Update setup.py --- setup.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index fb4c476..7e2ca11 100644 --- a/setup.py +++ b/setup.py @@ -18,18 +18,18 @@ url = "https://www.kwh40.de/", packages=setuptools.find_packages(), install_requires= [ - "Pykka==2.0.2", - "pylint==2.4.4", + "Pykka", + "pylint", "https://git.rwth-aachen.de/kwh40/s3i/-/jobs/artifacts/master/raw/public/s3i-0.4-py3-none-any.whl?job=wheel", "websocket", "pynput", "opcua", "requests", - "asyncio", + "asyncio" ], classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: LGPL", "Operating System :: OS Independent", - ], + ] ) -- GitLab From 0067ef478082cebc5bd667be3c8fc3cf06e0df96 Mon Sep 17 00:00:00 2001 From: Jiahang Chen <13508-jiahang.chen@users.noreply.git.rwth-aachen.de> Date: Mon, 5 Oct 2020 17:07:20 +0200 Subject: [PATCH 32/84] Update setup.py --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 7e2ca11..6975678 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,6 @@ install_requires= [ "Pykka", "pylint", - "https://git.rwth-aachen.de/kwh40/s3i/-/jobs/artifacts/master/raw/public/s3i-0.4-py3-none-any.whl?job=wheel", "websocket", "pynput", "opcua", -- GitLab From a0a03a389512026fbffd4fb62ad4a7e82bdc8ca1 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 5 Oct 2020 23:57:24 +0200 Subject: [PATCH 33/84] add brokerrest to thing --- ml/dt_factory.py | 3 ++- ml/thing.py | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/ml/dt_factory.py b/ml/dt_factory.py index cfbdcab..aa7f79a 100644 --- a/ml/dt_factory.py +++ b/ml/dt_factory.py @@ -150,7 +150,7 @@ def build(thing_ref, attributes): build_sub_featrues(thing_ref, feature_proxy, json_feature) -def create_dt_ref(model, grant_type, secret, username, password, is_broker, is_repo): +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. @@ -185,6 +185,7 @@ def create_dt_ref(model, grant_type, secret, username, password, is_broker, is_r client_secret=secret, username=username, password=password, + is_broker_rest=is_broker_rest, is_broker=is_broker, is_repo=is_repo ) diff --git a/ml/thing.py b/ml/thing.py index 5937186..b8b6e46 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -3,13 +3,13 @@ import websocket import json import uuid -from s3i import IdentityProvider, TokenType, Broker, GetValueReply, Directory -from s3i.broker import BrokerREST +import requests +from s3i import IdentityProvider, TokenType, GetValueReply, Directory +from s3i.broker import Broker, BrokerREST, BrokerMetaClass from s3i.messages import ServiceReply from ml.managing_actor import ManagingActor from ml.tools import BColors -from ml.tools import send_message from ml.tools import find_broker_endpoint from ml.app_logger import APP_LOGGER @@ -214,6 +214,8 @@ def receive(): target=receive, ).start() + + else: self.broker = Broker( auth_form="Username/Password", @@ -279,7 +281,7 @@ def on_get_value_request(self, msg): replyingToUUID=request_msg_id, ) msg_txt = get_value_reply.msg.__str__() - self.broker.send( + send_resp = self.broker.send( receiver_endpoints=[request_sender_endpoint], msg=msg_txt, ) @@ -290,7 +292,7 @@ def on_service_request(self, body_json): 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) + service_functionality, self.name) ) else: pass @@ -306,9 +308,14 @@ def on_service_request(self, body_json): serviceType=body_json.get("serviceType", None), results=result, replyingToUUID=body_json.get("identifier", None), - msgUUID=str(uuid.uuid4()) + msgUUID="s3i:{}".format(uuid.uuid4()) ) - send_message(self.broker, self.dir, service_reply.msg) + receiver_eps = list() + for r in service_reply.msg.get("receivers", None): + receiver_ep = find_broker_endpoint(self.dir, r) + receiver_eps.append(receiver_ep) + + self.broker.send(receiver_endpoints=receiver_eps, msg=json.dumps(service_reply.msg)) def on_get_value_reply(self, msg): print(msg) -- GitLab From 2dbf12a2e9ce338b3571df93cb8bb79ebdf9645d Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 5 Oct 2020 23:58:16 +0200 Subject: [PATCH 34/84] delete string 's3i' aus dem logger info --- ml/fml40/features/functionalities/accepts_felling_jobs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ml/fml40/features/functionalities/accepts_felling_jobs.py b/ml/fml40/features/functionalities/accepts_felling_jobs.py index caaa0e9..fdf4405 100644 --- a/ml/fml40/features/functionalities/accepts_felling_jobs.py +++ b/ml/fml40/features/functionalities/accepts_felling_jobs.py @@ -28,5 +28,5 @@ def add_to_job_list(self, job): self.job_list.append(job) return True else: - APP_LOGGER.info("[S³I]: This felling job has been already added in job list") + APP_LOGGER.info("This felling job has been already added in job list") return False \ No newline at end of file -- GitLab From 4a4524ddcce639cc49f6a762767c2e72a7cf353a Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 6 Oct 2020 00:31:54 +0200 Subject: [PATCH 35/84] update two notebooks --- dt_creation.py | 6 ++-- dt_creation_hmi.py | 82 ++++------------------------------------------ 2 files changed, 10 insertions(+), 78 deletions(-) diff --git a/dt_creation.py b/dt_creation.py index 8465927..85b0133 100644 --- a/dt_creation.py +++ b/dt_creation.py @@ -33,7 +33,6 @@ headers={"Authorization": "Bearer {}".format(access_token), "Content-Type": "application/json"}, data=json.dumps({})) -print(create_thing_resp.json()) dt_id = create_thing_resp.json().get("identifier", None) print(dt_id) create_cloud_copy_resp = requests.post(url="https://config.s3i.vswf.dev/things/{}/repository".format(dt_id), @@ -48,11 +47,12 @@ dt_secret = create_thing_resp.json().get("secret", None) #dt_secret = "c49e4050-d194-4f08-90c5-e17fd5befba5" dt_name = "test" -config_file_name = make_config_file(dt_id=dt_id, name=dt_name, roles="fml40::Harvester", features=["fml40::ProvidesProductionData", - "fml40::AcceptsFellingJobs"]) +config_file_name = make_config_file(dt_id=dt_id, name=dt_name, roles="fml40::Harvester", + features=["fml40::ProvidesProductionData", "fml40::AcceptsFellingJobs"]) 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, + is_broker_rest=True, is_broker=True, is_repo=False) dt_proxy = dt_ref.proxy() dt_proxy.run_forever() diff --git a/dt_creation_hmi.py b/dt_creation_hmi.py index a8384db..0714044 100644 --- a/dt_creation_hmi.py +++ b/dt_creation_hmi.py @@ -4,7 +4,6 @@ from ml.tools import find_broker_endpoint, make_config_file, load_config from ml.dt_factory import create_dt_ref from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob - import requests import json, time @@ -16,91 +15,24 @@ config_file_name = make_config_file(dt_id=hmi_id, name="my HMI", roles="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=True, is_repo=False) + username=username, password=password, + is_broker_rest=True, is_broker=True, is_repo=False) hmi_proxy = hmi_ref.proxy() hmi_proxy.run_forever() -s3i_dir = s3i.Directory(s3i_dir_url="https://dir.s3i.vswf.dev/api/2/", token=hmi_proxy.access_token.get()) -hmi_json = s3i_dir.queryThingIDBased(thingID=hmi_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 - - -messageIds = list() - +hmi_endpoint = find_broker_endpoint(hmi_proxy.dir.get(), hmi_id) serv_req = s3i.messages.ServiceRequest() receiver = input("[S³I]: Please enter the id of your digital twin: ") job = FellingJob("", hmi_ref) +print(job.__dict__) serv_req.fillServiceRequest( - senderUUID=hmi_id, receiverUUID=[receiver], sender_endpoint=hmi_s3ib_ep, + senderUUID=hmi_id, receiverUUID=[receiver], sender_endpoint=hmi_endpoint, serviceType="AcceptsFellingJobs/my_accept_job_func", parameters={"job": "fml40::FellingJob"}, msgUUID=str(uuid.uuid4()) ) -receiver_endpoint = find_broker_endpoint(s3i_dir, thing_id=receiver) - -service_req_response = requests.post(url="https://broker.s3i.vswf.dev/{}".format(receiver_endpoint), - data=json.dumps(serv_req.msg), - headers={'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + hmi_proxy.access_token.get()} - ) -print(service_req_response.text) - -"""Store the message id into message storage""" -messageIds.append(serv_req.msg["identifier"]) - -time.sleep(1) -response = requests.get(url="https://broker.s3i.vswf.dev/{}".format(hmi_s3ib_ep), headers={'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + hmi_proxy.access_token.get()}) -print(response.text) - -""" -def receive(msg_type): - access_token = s3i_identity_provider.get_token(s3i.TokenType.ACCESS_TOKEN) - headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + access_token} - response = requests.get(url="https://broker.s3i.vswf.dev/{}".format(hmi_id), headers=headers) - msg_json = "" - value_json = "" - json_acceptable_string = response.text.replace("'", "\"") - if json_acceptable_string: - if check_message_encryption(response.text.strip('"')) == "pgp": - print_with_timestamp( - "You received a PGP message but this notebook can not decrypt PGP messages. Use the 03_inbox notebook to receive PGP messages.") - print_with_timestamp("PGP Message: " + response.text) - repeat(msg_type) - else: - msg_json = json.loads(json_acceptable_string) - if msg_json["replyingToMessage"] in messageIds and msg_json["messageType"] == msg_type: - if msg_type == "serviceReply": - value_json = msg_json["results"] - elif msg_type == "getValueReply": - value_json = msg_json["value"] - elif msg_type == "setValueReply": - value_json = msg_json["ok"] - messageIds.remove(msg_json["replyingToMessage"]) - else: - repeat(msg_type) - - else: - print_with_timestamp("The harvester did not respond yet.") - repeat(msg_type) - return msg_json, value_json - - -def repeat(msg_type): - decision = input("[S3I] Do you want to check for new messages again? [j/n]") - if decision in yes: - receive(msg_type) - elif decision in no: - print_with_timestamp( - "You do not want to check for more messages. If you want to check for new messages, just execute this cell again (Run button or SHIFT+RETURN)") - else: - print_with_timestamp( - "I could not understand your response. If you want to check for new messages, just execute this cell again (Run button or SHIFT+RETURN)") -""" \ No newline at end of file +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)) -- GitLab From 0d577bac3a3ae43fd629d56880417b19316cd9c0 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 6 Oct 2020 19:30:12 +0200 Subject: [PATCH 36/84] update fellingjob and req.txt --- configs/config_test.json | 2 +- dt_creation_hmi.py | 4 ++++ .../features/properties/values/documents/jobs/felling_job.py | 2 ++ ml/ml40/features/feature.py | 4 ++++ requirements.txt | 3 +-- 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/configs/config_test.json b/configs/config_test.json index 491b14b..9ea2877 100644 --- a/configs/config_test.json +++ b/configs/config_test.json @@ -1 +1 @@ -{"thingId": "s3i:de59a481-dd97-429f-a0ca-fab013b6bfbc", "policyId": "s3i:de59a481-dd97-429f-a0ca-fab013b6bfbc", "attributes": {"class": "ml40::Thing", "name": "test", "roles": [{"class": "fml40::Harvester"}], "features": [{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}]}} \ No newline at end of file +{"thingId": "s3i:af86ad33-63a4-427b-9914-3f199cd025c4", "policyId": "s3i:af86ad33-63a4-427b-9914-3f199cd025c4", "attributes": {"class": "ml40::Thing", "name": "test", "roles": [{"class": "fml40::Harvester"}], "features": [{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}]}} \ No newline at end of file diff --git a/dt_creation_hmi.py b/dt_creation_hmi.py index 0714044..c12f8d9 100644 --- a/dt_creation_hmi.py +++ b/dt_creation_hmi.py @@ -4,6 +4,7 @@ from ml.tools import find_broker_endpoint, make_config_file, load_config from ml.dt_factory import create_dt_ref from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob +from ml.identifier import ID import requests import json, time @@ -25,7 +26,10 @@ serv_req = s3i.messages.ServiceRequest() receiver = input("[S³I]: Please enter the id of your digital twin: ") +job_id = ID(identifier="s3i:{}".format(uuid.uuid4())) job = FellingJob("", hmi_ref) +job.identifier = job_id.ID + print(job.__dict__) serv_req.fillServiceRequest( senderUUID=hmi_id, receiverUUID=[receiver], sender_endpoint=hmi_endpoint, diff --git a/ml/fml40/features/properties/values/documents/jobs/felling_job.py b/ml/fml40/features/properties/values/documents/jobs/felling_job.py index ae21fec..f2588cc 100644 --- a/ml/fml40/features/properties/values/documents/jobs/felling_job.py +++ b/ml/fml40/features/properties/values/documents/jobs/felling_job.py @@ -6,3 +6,5 @@ def __init__(self, name, ref_managing_actor): super(FellingJob, self).__init__( name=name, ref_managing_actor=ref_managing_actor ) + + diff --git a/ml/ml40/features/feature.py b/ml/ml40/features/feature.py index 0cd95d8..1b36abc 100644 --- a/ml/ml40/features/feature.py +++ b/ml/ml40/features/feature.py @@ -27,6 +27,10 @@ def subfeatures(self): def identifier(self): return self.__identifier + @identifier.setter + def identifier(self, value): + self.__identifier = value + def from_json(self, json_obj): self.__name = json_obj.get("name", "") self.__class_name = json_obj.get("class", "") diff --git a/requirements.txt b/requirements.txt index af0299b..3152cdd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,8 @@ https://git.rwth-aachen.de/kwh40/s3i/-/jobs/artifacts/master/raw/public/s3i-0.4-py3-none-any.whl?job=wheel Pykka -websocket +websocket-client==0.57.0 pynput opcua requests asyncio pylint==2.4.4 -#https://git.rwth-aachen.de/kwh40/s3i/-/jobs/758151/artifacts/raw/public/s3i-0.4-py3-none-any.whl -- GitLab From 8eb857890a090ba6adb2dcdda387ae0c7669c1e5 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 6 Oct 2020 23:53:35 +0200 Subject: [PATCH 37/84] complete the acceptFellingJob functionality --- .../functionalities/accepts_felling_jobs.py | 11 ++++++++-- ml/identifier.py | 2 ++ ml/ml40/features/feature.py | 20 +++++++++++++++--- ml/ml40/features/properties/property.py | 3 +++ .../properties/values/documents/jobs/job.py | 21 ++++++++++++++++++- ml/ml40/features/properties/values/value.py | 14 ++++++++++++- ml/thing.py | 11 ++++++++-- 7 files changed, 73 insertions(+), 9 deletions(-) diff --git a/ml/fml40/features/functionalities/accepts_felling_jobs.py b/ml/fml40/features/functionalities/accepts_felling_jobs.py index fdf4405..11bbfce 100644 --- a/ml/fml40/features/functionalities/accepts_felling_jobs.py +++ b/ml/fml40/features/functionalities/accepts_felling_jobs.py @@ -15,10 +15,17 @@ def acceptJob(self, job: FellingJob) -> bool: return self.add_to_job_list(job) def queryJobStatus(self, identifier): - pass + for job in self.job_list: + if job.get("id") == identifier: + return {"identifier": identifier, "status": job.get("status")} + return {"identifier": identifier, "status": "NOT FOUND"} def removeJob(self, identifier): - pass + for job in self.job_list: + if job.get("id") == identifier: + self.job_list.remove(job) + return True + return False def from_json(self, json_obj): super().from_json(json_obj) diff --git a/ml/identifier.py b/ml/identifier.py index 9a52068..b843029 100644 --- a/ml/identifier.py +++ b/ml/identifier.py @@ -3,8 +3,10 @@ class ID(object): Define primitive variable type ID """ def __init__(self, identifier): + # TODO validate identifier self.__identifier = identifier + @property def ID(self): return self.__identifier diff --git a/ml/ml40/features/feature.py b/ml/ml40/features/feature.py index 0cd95d8..b438a15 100644 --- a/ml/ml40/features/feature.py +++ b/ml/ml40/features/feature.py @@ -1,6 +1,6 @@ from ml.managed_actor import ManagedActor from ml.identifier import ID -import types +import uuid class Feature(ManagedActor): @@ -8,7 +8,7 @@ def __init__(self, name, ref_managing_actor): super(Feature, self).__init__(name, ref_managing_actor) self.__name = name self.__class_name = "" - self.__identifier = ID + self.__identifier = ID(identifier="s3i:{}".format(uuid.uuid4())).ID self.__subfeatures = [] @property @@ -19,6 +19,10 @@ def type_name(self): def name(self): return self.__name + @name.setter + def name(self, value): + self.__name = value + @property def subfeatures(self): return self.__subfeatures @@ -27,7 +31,17 @@ def subfeatures(self): def identifier(self): return self.__identifier + @identifier.setter + def identifier(self, value): + self.__identifier = value + + def to_json(self): + return { + "class": self.name, + "id": self.identifier, + } + 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", "")) + self.__identifier = json_obj.get("identifier", "") diff --git a/ml/ml40/features/properties/property.py b/ml/ml40/features/properties/property.py index e6c556c..14cb65b 100644 --- a/ml/ml40/features/properties/property.py +++ b/ml/ml40/features/properties/property.py @@ -1,5 +1,8 @@ from ml.ml40.features.feature import Feature + class Property(Feature): def __init__(self, name, ref_managing_actor): super(Property, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + + diff --git a/ml/ml40/features/properties/values/documents/jobs/job.py b/ml/ml40/features/properties/values/documents/jobs/job.py index 16db691..50e401a 100644 --- a/ml/ml40/features/properties/values/documents/jobs/job.py +++ b/ml/ml40/features/properties/values/documents/jobs/job.py @@ -3,6 +3,25 @@ class Job(Document): - def __init__(self, name, ref_managing_actor, status=JobStatus.Pending): + def __init__(self, name, ref_managing_actor): super(Job, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + self.__status = JobStatus(0) + + @property + def status(self): + return self.__status + + @status.setter + def status(self, value): + self.__status = value + + def to_json(self): + temp_dict = super().to_json() + if self.status: + _dict = temp_dict.copy() + _dict.update({ + "status": self.status.name + }) + return _dict + return temp_dict diff --git a/ml/ml40/features/properties/values/value.py b/ml/ml40/features/properties/values/value.py index 6245e42..67d1a6b 100644 --- a/ml/ml40/features/properties/values/value.py +++ b/ml/ml40/features/properties/values/value.py @@ -1,5 +1,5 @@ from ml.ml40.features.properties.property import Property - +import copy class Value(Property): def __init__(self, name, ref_managing_actor): @@ -20,3 +20,15 @@ def valid_from(self): def set_valid_from(self, valid_from): self._valid_from = valid_from + + def to_json(self): + temp_dict = super().to_json() + if self.valid_from and self.valid_to: + _dict = temp_dict.copy() + _dict.update({ + "value_to": self.valid_to, + "value_from": self.valid_from + }) + return _dict + + return temp_dict diff --git a/ml/thing.py b/ml/thing.py index b8b6e46..c7fb9aa 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -66,11 +66,12 @@ 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", "") @@ -287,6 +288,7 @@ def on_get_value_request(self, msg): def on_service_request(self, body_json): service_type = body_json.get("serviceType") + parameters = body_json.get("parameters") service_functionality = service_type.split('/')[0] service_functionality_obj = self.features.get(service_functionality, None) if service_functionality_obj is None: @@ -299,7 +301,12 @@ def on_service_request(self, body_json): # TODO: Call right functionality. func_proxy = service_functionality_obj.proxy() method = getattr(func_proxy, service_type.split('/')[1]) - result = {"ok": method("test_job").get()} + + result = method(**parameters).get() + if isinstance(result, bool): + results = {"ok": result} + else: + results = result service_reply = ServiceReply() service_reply.fillServiceReply( -- GitLab From 180cdae567f5151901d58e8a2c0ba3885ffb25e3 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 6 Oct 2020 23:54:58 +0200 Subject: [PATCH 38/84] update --- configs/config_test.json | 2 +- dt_creation.py | 5 ++++- dt_creation_hmi.py | 11 +++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/configs/config_test.json b/configs/config_test.json index 491b14b..5b128f0 100644 --- a/configs/config_test.json +++ b/configs/config_test.json @@ -1 +1 @@ -{"thingId": "s3i:de59a481-dd97-429f-a0ca-fab013b6bfbc", "policyId": "s3i:de59a481-dd97-429f-a0ca-fab013b6bfbc", "attributes": {"class": "ml40::Thing", "name": "test", "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": "test", "roles": [{"class": "fml40::Harvester"}], "features": [{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}]}} \ No newline at end of file diff --git a/dt_creation.py b/dt_creation.py index 85b0133..11a6810 100644 --- a/dt_creation.py +++ b/dt_creation.py @@ -29,6 +29,7 @@ ### create identity of digital twin # TODO class for config api in master to merge # create a thing identity for the dt +""" create_thing_resp = requests.post(url="https://config.s3i.vswf.dev/things/", headers={"Authorization": "Bearer {}".format(access_token), "Content-Type": "application/json"}, @@ -45,7 +46,9 @@ ) dt_secret = create_thing_resp.json().get("secret", None) -#dt_secret = "c49e4050-d194-4f08-90c5-e17fd5befba5" +""" +dt_id = "s3i:b6d1cc6d-896c-40fe-9403-b5b7682b1d03" +dt_secret = "cd4d24d2-f702-4f51-b0cf-6b77a423b33a" dt_name = "test" config_file_name = make_config_file(dt_id=dt_id, name=dt_name, roles="fml40::Harvester", features=["fml40::ProvidesProductionData", "fml40::AcceptsFellingJobs"]) diff --git a/dt_creation_hmi.py b/dt_creation_hmi.py index 0714044..dd64dff 100644 --- a/dt_creation_hmi.py +++ b/dt_creation_hmi.py @@ -25,12 +25,15 @@ serv_req = s3i.messages.ServiceRequest() receiver = input("[S³I]: Please enter the id of your digital twin: ") -job = FellingJob("", hmi_ref) -print(job.__dict__) +job_ref = FellingJob.start("ml40::FellingJob", hmi_ref) +job_proxy = job_ref.proxy() +job_proxy.identifier = "s3i:d53e84fd-432c-4e19-9008-546f296c2d5c" +print(job_proxy.to_json().get()) + serv_req.fillServiceRequest( senderUUID=hmi_id, receiverUUID=[receiver], sender_endpoint=hmi_endpoint, - serviceType="AcceptsFellingJobs/my_accept_job_func", - parameters={"job": "fml40::FellingJob"}, + serviceType="AcceptsFellingJobs/queryJobStatus", + parameters={"identifier": job_proxy.identifier.get()}, msgUUID=str(uuid.uuid4()) ) -- GitLab From e89bbad6912b3a8e3afc0deb16a573a95dd65eb8 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Wed, 7 Oct 2020 11:17:45 +0200 Subject: [PATCH 39/84] update felling job --- configs/config.json | 1 - configs/config_dt test.json | 1 - configs/config_dt.json | 24 ------------------- configs/config_my_dt_harvester.json | 1 + configs/config_test.json | 5 ---- dt_creation.py | 5 ++-- dt_creation_hmi.py | 4 ++-- ml/dt_factory.py | 23 ++++++++++-------- .../functionalities/accepts_felling_jobs.py | 13 +++++++++- ml/thing.py | 10 +++++++- 10 files changed, 39 insertions(+), 48 deletions(-) delete mode 100644 configs/config.json delete mode 100644 configs/config_dt test.json delete mode 100644 configs/config_dt.json create mode 100644 configs/config_my_dt_harvester.json delete mode 100644 configs/config_test.json diff --git a/configs/config.json b/configs/config.json deleted file mode 100644 index e59d1d4..0000000 --- a/configs/config.json +++ /dev/null @@ -1 +0,0 @@ -{"thingId": "s3i:feb857f1-3058-4ed4-8826-bbf182cbb16e", "policyId": "s3i:feb857f1-3058-4ed4-8826-bbf182cbb16e", "attributes": {"class": "ml40::Thing", "name": "dt test", "roles": [{"class": "fml40::Harvester"}], "features": [{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}]}} \ No newline at end of file diff --git a/configs/config_dt test.json b/configs/config_dt test.json deleted file mode 100644 index 231c191..0000000 --- a/configs/config_dt test.json +++ /dev/null @@ -1 +0,0 @@ -{"thingId": "s3i:b4a0ec9b-66be-4390-b786-9c70b1c017e6", "policyId": "s3i:b4a0ec9b-66be-4390-b786-9c70b1c017e6", "attributes": {"class": "ml40::Thing", "name": "dt test", "roles": [{"class": "fml40::Harvester"}], "features": [{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}]}} \ No newline at end of file diff --git a/configs/config_dt.json b/configs/config_dt.json deleted file mode 100644 index 1887885..0000000 --- a/configs/config_dt.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "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 diff --git a/configs/config_my_dt_harvester.json b/configs/config_my_dt_harvester.json new file mode 100644 index 0000000..31a7774 --- /dev/null +++ b/configs/config_my_dt_harvester.json @@ -0,0 +1 @@ +{"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 diff --git a/configs/config_test.json b/configs/config_test.json deleted file mode 100644 index 9a9f1f9..0000000 --- a/configs/config_test.json +++ /dev/null @@ -1,5 +0,0 @@ -<<<<<<< HEAD -{"thingId": "s3i:b6d1cc6d-896c-40fe-9403-b5b7682b1d03", "policyId": "s3i:b6d1cc6d-896c-40fe-9403-b5b7682b1d03", "attributes": {"class": "ml40::Thing", "name": "test", "roles": [{"class": "fml40::Harvester"}], "features": [{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}]}} -======= -{"thingId": "s3i:af86ad33-63a4-427b-9914-3f199cd025c4", "policyId": "s3i:af86ad33-63a4-427b-9914-3f199cd025c4", "attributes": {"class": "ml40::Thing", "name": "test", "roles": [{"class": "fml40::Harvester"}], "features": [{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}]}} ->>>>>>> 0d577bac3a3ae43fd629d56880417b19316cd9c0 diff --git a/dt_creation.py b/dt_creation.py index 11a6810..9a3f329 100644 --- a/dt_creation.py +++ b/dt_creation.py @@ -49,7 +49,7 @@ """ dt_id = "s3i:b6d1cc6d-896c-40fe-9403-b5b7682b1d03" dt_secret = "cd4d24d2-f702-4f51-b0cf-6b77a423b33a" -dt_name = "test" +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"]) setup_logger(dt_name) @@ -63,9 +63,8 @@ class AcceptsFellingJobsImpl(AcceptsFellingJobs): def my_accept_job_func(self, job): - APP_LOGGER.info("I am checking if the felling job can be accepted.") + APP_LOGGER.info("Checking job with my impl function.") return self.add_to_job_list(job) dt_proxy.add_function_impl(AcceptsFellingJobsImpl, "AcceptsFellingJobs") - diff --git a/dt_creation_hmi.py b/dt_creation_hmi.py index 1242d59..f4f99e1 100644 --- a/dt_creation_hmi.py +++ b/dt_creation_hmi.py @@ -34,8 +34,8 @@ serv_req.fillServiceRequest( senderUUID=hmi_id, receiverUUID=[receiver], sender_endpoint=hmi_endpoint, - serviceType="AcceptsFellingJobs/queryJobStatus", - parameters={"identifier": job_proxy.identifier.get()}, + serviceType="AcceptsFellingJobs/acceptJob", + parameters={"job": job_proxy.to_json().get()}, msgUUID=str(uuid.uuid4()) ) diff --git a/ml/dt_factory.py b/ml/dt_factory.py index aa7f79a..d5c90e6 100644 --- a/ml/dt_factory.py +++ b/ml/dt_factory.py @@ -127,20 +127,23 @@ 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 + + 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) json_features = attributes.get("features", []) for json_feature in json_features: - class_name = json_feature.get("class", "") - feature = DT_FACTORY.get(class_name, None) + feature_name = json_feature.get("class", "") + feature = DT_FACTORY.get(feature_name, None) if not feature: - APP_LOGGER.critical("Feature: %s is missing" % class_name) + APP_LOGGER.critical("Feature: %s is missing" % feature_name) else: - APP_LOGGER.debug("Adding feature: %s" % class_name) + APP_LOGGER.debug("Adding feature: %s" % feature_name) feature_ref = feature.start("", thing_ref) feature_proxy = feature_ref.proxy() feature_proxy.from_json(json_feature).get() @@ -177,7 +180,7 @@ def create_dt_ref(model, grant_type, secret, username, password, is_broker_rest, sys.exit("Incomplete model: roles missing!") thing_name = attributes.get("name", "") - APP_LOGGER.debug("Creating ditigtal twin %s" % thing_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( model=model, diff --git a/ml/fml40/features/functionalities/accepts_felling_jobs.py b/ml/fml40/features/functionalities/accepts_felling_jobs.py index 11bbfce..230e208 100644 --- a/ml/fml40/features/functionalities/accepts_felling_jobs.py +++ b/ml/fml40/features/functionalities/accepts_felling_jobs.py @@ -1,4 +1,5 @@ from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs +from ml.ml40.features.properties.values.documents.jobs.job_status import JobStatus from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob from ml.app_logger import APP_LOGGER @@ -12,19 +13,27 @@ 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: + return False return self.add_to_job_list(job) def queryJobStatus(self, identifier): + APP_LOGGER.info("Checking the job status of job {}".format(identifier)) for job in self.job_list: if job.get("id") == identifier: + APP_LOGGER.info("Job {} is now in status {}".format(identifier, job.get("status"))) return {"identifier": identifier, "status": job.get("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.get("id") == 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 from_json(self, json_obj): @@ -32,8 +41,10 @@ def from_json(self, json_obj): def add_to_job_list(self, job): if job not in self.job_list: + job["status"] = JobStatus.InProgress.name self.job_list.append(job) + APP_LOGGER.info("Job accepted!") return True else: - APP_LOGGER.info("This felling job has been already added in job list") + APP_LOGGER.info("Job has been already accepted!") return False \ No newline at end of file diff --git a/ml/thing.py b/ml/thing.py index c7fb9aa..6d29980 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -83,6 +83,14 @@ def features(self): def features(self, value): self.__features = value + @property + def roles(self): + return self.__roles + + @roles.setter + def roles(self, value): + self.__roles = value + @property def class_name(self): return self.__class_name @@ -313,7 +321,7 @@ def on_service_request(self, body_json): senderUUID=self.thing_id, receiverUUID=body_json.get("sender", None), serviceType=body_json.get("serviceType", None), - results=result, + results=results, replyingToUUID=body_json.get("identifier", None), msgUUID="s3i:{}".format(uuid.uuid4()) ) -- GitLab From f9396be819928199047280761c250777fd3fbb26 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Wed, 7 Oct 2020 12:29:55 +0200 Subject: [PATCH 40/84] clear up all roles --- ml/fml40/roles/__init__.py | 3 --- ml/fml40/roles/dts/__init__.py | 7 ------- ml/fml40/roles/dts/handheld_devices/__init__.py | 2 -- ml/fml40/roles/dts/machines/__init__.py | 7 ------- ml/fml40/roles/dts/machines/forwarder.py | 2 +- ml/fml40/roles/dts/machines/log_truck.py | 1 + ml/fml40/roles/dts/machines/wheel_loader.py | 2 +- ml/fml40/roles/dts/parts/__init__.py | 5 ----- ml/fml40/roles/dts/persons/__init__.py | 4 ---- ml/fml40/roles/dts/sensors/__init__.py | 1 - ml/fml40/roles/dts/sites/__init__.py | 3 --- ml/fml40/roles/dts/sites/mill/__init__.py | 3 --- ml/fml40/roles/dts/sites/mill/papermill.py | 2 +- ml/fml40/roles/dts/sites/mill/sawmill.py | 2 +- ml/ml40/roles/dts/__init__.py | 9 +-------- ml/ml40/roles/dts/dt.py | 2 +- ml/ml40/roles/dts/handheld_devices/__init__.py | 1 - .../dts/handheld_devices/handheld_device.py | 2 +- ml/ml40/roles/dts/machines/__init__.py | 1 - ml/ml40/roles/dts/machines/machine.py | 2 +- ml/ml40/roles/dts/parts/__init__.py | 5 ----- ml/ml40/roles/dts/parts/crane.py | 2 +- ml/ml40/roles/dts/parts/engine.py | 2 +- ml/ml40/roles/dts/parts/part.py | 2 +- ml/ml40/roles/dts/parts/scale.py | 2 +- ml/ml40/roles/dts/parts/tank.py | 2 +- ml/ml40/roles/dts/persons/__init__.py | 2 -- ml/ml40/roles/dts/persons/machine_operator.py | 2 +- ml/ml40/roles/dts/persons/person.py | 2 +- ml/ml40/roles/dts/sensors/__init__.py | 4 ---- ml/ml40/roles/dts/sensors/air_sensor.py | 2 +- ml/ml40/roles/dts/sensors/sensor.py | 2 +- ml/ml40/roles/dts/sensors/soil_sensor.py | 2 +- ml/ml40/roles/dts/sites/__init__.py | 1 - ml/ml40/roles/dts/sites/site.py | 2 +- ml/ml40/roles/dts/ways/__init__.py | 1 - ml/ml40/roles/dts/ways/way.py | 2 +- ml/ml40/roles/hmis/__init__.py | 5 ----- ml/ml40/roles/hmis/app.py | 2 +- ml/ml40/roles/hmis/dashboard.py | 2 +- ml/ml40/roles/hmis/hmd.py | 2 +- ml/ml40/roles/hmis/machine_ui.py | 2 +- ml/ml40/roles/role.py | 17 +++++++++++++++-- 43 files changed, 40 insertions(+), 88 deletions(-) diff --git a/ml/fml40/roles/__init__.py b/ml/fml40/roles/__init__.py index c49f2a8..e69de29 100644 --- a/ml/fml40/roles/__init__.py +++ b/ml/fml40/roles/__init__.py @@ -1,3 +0,0 @@ -from .DT import * -from .HMI import * -from .Service import * diff --git a/ml/fml40/roles/dts/__init__.py b/ml/fml40/roles/dts/__init__.py index 96d8bdf..e69de29 100644 --- a/ml/fml40/roles/dts/__init__.py +++ b/ml/fml40/roles/dts/__init__.py @@ -1,7 +0,0 @@ -from .HandheldDevice import * -from .Machine import * -from .Part import * -from .Person import * -from .Sensor import * -from .Site import * -from .Way import * \ No newline at end of file diff --git a/ml/fml40/roles/dts/handheld_devices/__init__.py b/ml/fml40/roles/dts/handheld_devices/__init__.py index ef158aa..e69de29 100644 --- a/ml/fml40/roles/dts/handheld_devices/__init__.py +++ b/ml/fml40/roles/dts/handheld_devices/__init__.py @@ -1,2 +0,0 @@ -from .Brushcutter import Brushcutter -from .Chainsaw import Chainsaw \ No newline at end of file diff --git a/ml/fml40/roles/dts/machines/__init__.py b/ml/fml40/roles/dts/machines/__init__.py index 462f964..e69de29 100644 --- a/ml/fml40/roles/dts/machines/__init__.py +++ b/ml/fml40/roles/dts/machines/__init__.py @@ -1,7 +0,0 @@ -from .ForestMachine import ForestMachine -from .Forwarder import Forwarder -from .Harvester import Harvester -from .LogTruck import LogTruck -from .MiniTractor import MiniTractor -from .Skidder import Skidder -from .WheelLoader import WheelLoader \ No newline at end of file diff --git a/ml/fml40/roles/dts/machines/forwarder.py b/ml/fml40/roles/dts/machines/forwarder.py index 17b656a..f20b118 100644 --- a/ml/fml40/roles/dts/machines/forwarder.py +++ b/ml/fml40/roles/dts/machines/forwarder.py @@ -1,4 +1,4 @@ -from ml.fml40.roles.dt.machines.forest_machine import ForestMachine +from ml.fml40.roles.dts.machines.forest_machine import ForestMachine class Forwarder(ForestMachine): diff --git a/ml/fml40/roles/dts/machines/log_truck.py b/ml/fml40/roles/dts/machines/log_truck.py index 5eaf520..751dd1d 100644 --- a/ml/fml40/roles/dts/machines/log_truck.py +++ b/ml/fml40/roles/dts/machines/log_truck.py @@ -1,4 +1,5 @@ from ml.fml40.roles.dts.machines.forest_machine import ForestMachine + class LogTruck(ForestMachine): pass diff --git a/ml/fml40/roles/dts/machines/wheel_loader.py b/ml/fml40/roles/dts/machines/wheel_loader.py index b2a9203..fb1be89 100644 --- a/ml/fml40/roles/dts/machines/wheel_loader.py +++ b/ml/fml40/roles/dts/machines/wheel_loader.py @@ -1,4 +1,4 @@ -from ml.fml40.roles.dts.machine.forest_machine import ForestMachine +from ml.fml40.roles.dts.machines.forest_machine import ForestMachine class WheelLoader(ForestMachine): diff --git a/ml/fml40/roles/dts/parts/__init__.py b/ml/fml40/roles/dts/parts/__init__.py index 016d6ab..e69de29 100644 --- a/ml/fml40/roles/dts/parts/__init__.py +++ b/ml/fml40/roles/dts/parts/__init__.py @@ -1,5 +0,0 @@ -from .Grabber import Grabber -from .HarvestingHead import HarvestingHead -from .LogLoadingArea import LogLoadingArea -from .Saw import Saw -from .Winch import Winch \ No newline at end of file diff --git a/ml/fml40/roles/dts/persons/__init__.py b/ml/fml40/roles/dts/persons/__init__.py index b440d42..e69de29 100644 --- a/ml/fml40/roles/dts/persons/__init__.py +++ b/ml/fml40/roles/dts/persons/__init__.py @@ -1,4 +0,0 @@ -from .ForestOwner import ForestOwner -from .ForestWorker import ForestWorker -from .MiniTractorOperator import MiniTractorOperator -from .SkidderOperator import SkidderOperator diff --git a/ml/fml40/roles/dts/sensors/__init__.py b/ml/fml40/roles/dts/sensors/__init__.py index 0dfae59..e69de29 100644 --- a/ml/fml40/roles/dts/sensors/__init__.py +++ b/ml/fml40/roles/dts/sensors/__init__.py @@ -1 +0,0 @@ -from .VitalitySensor import VilalitySensor \ No newline at end of file diff --git a/ml/fml40/roles/dts/sites/__init__.py b/ml/fml40/roles/dts/sites/__init__.py index b53fe40..e69de29 100644 --- a/ml/fml40/roles/dts/sites/__init__.py +++ b/ml/fml40/roles/dts/sites/__init__.py @@ -1,3 +0,0 @@ -from .ForestEnterprise import ForestEnterprise -from .Hauler import Hauler -from .Mill import * diff --git a/ml/fml40/roles/dts/sites/mill/__init__.py b/ml/fml40/roles/dts/sites/mill/__init__.py index 8d9ac19..e69de29 100644 --- a/ml/fml40/roles/dts/sites/mill/__init__.py +++ b/ml/fml40/roles/dts/sites/mill/__init__.py @@ -1,3 +0,0 @@ -from .Mill import Mill -from .Sawmill import Sawmill -from .Papermill import Papermill diff --git a/ml/fml40/roles/dts/sites/mill/papermill.py b/ml/fml40/roles/dts/sites/mill/papermill.py index b5d34ad..dc4f4f2 100644 --- a/ml/fml40/roles/dts/sites/mill/papermill.py +++ b/ml/fml40/roles/dts/sites/mill/papermill.py @@ -1,4 +1,4 @@ -from ml.fml40.roles.dts.sites.mill import Mill +from ml.fml40.roles.dts.sites.mill.mill import Mill class Papermill(Mill): diff --git a/ml/fml40/roles/dts/sites/mill/sawmill.py b/ml/fml40/roles/dts/sites/mill/sawmill.py index 354ab76..fd03337 100644 --- a/ml/fml40/roles/dts/sites/mill/sawmill.py +++ b/ml/fml40/roles/dts/sites/mill/sawmill.py @@ -1,4 +1,4 @@ -from ml.fml40.roles.dts.sites.mill import Mill +from ml.fml40.roles.dts.sites.mill.mill import Mill class Sawmill(Mill): diff --git a/ml/ml40/roles/dts/__init__.py b/ml/ml40/roles/dts/__init__.py index d090894..8b13789 100644 --- a/ml/ml40/roles/dts/__init__.py +++ b/ml/ml40/roles/dts/__init__.py @@ -1,8 +1 @@ -from .DT import DT -from .HandheldDevice import HandheldDevice -from .Person import Person, MachineOperator -from .Machine import Machine -from .Way import Way -from .Sensor import SoilSensor, Sensor, AirSensor -from .Site import Site -from .Part import Part, Crane, Engine, Scale, Tank + diff --git a/ml/ml40/roles/dts/dt.py b/ml/ml40/roles/dts/dt.py index 3bfe234..601c9c5 100644 --- a/ml/ml40/roles/dts/dt.py +++ b/ml/ml40/roles/dts/dt.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.Role import Role +from ml.ml40.roles.role import Role class DT(Role): diff --git a/ml/ml40/roles/dts/handheld_devices/__init__.py b/ml/ml40/roles/dts/handheld_devices/__init__.py index dcf572d..e69de29 100644 --- a/ml/ml40/roles/dts/handheld_devices/__init__.py +++ b/ml/ml40/roles/dts/handheld_devices/__init__.py @@ -1 +0,0 @@ -from .HandheldDevice import HandheldDevice \ No newline at end of file diff --git a/ml/ml40/roles/dts/handheld_devices/handheld_device.py b/ml/ml40/roles/dts/handheld_devices/handheld_device.py index fe67c13..9e2b293 100644 --- a/ml/ml40/roles/dts/handheld_devices/handheld_device.py +++ b/ml/ml40/roles/dts/handheld_devices/handheld_device.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT import DT +from ml.ml40.roles.dts.dt import DT class HandheldDevice(DT): diff --git a/ml/ml40/roles/dts/machines/__init__.py b/ml/ml40/roles/dts/machines/__init__.py index fed41ed..e69de29 100644 --- a/ml/ml40/roles/dts/machines/__init__.py +++ b/ml/ml40/roles/dts/machines/__init__.py @@ -1 +0,0 @@ -from .Machine import Machine \ No newline at end of file diff --git a/ml/ml40/roles/dts/machines/machine.py b/ml/ml40/roles/dts/machines/machine.py index 97eaf3e..50fc16e 100644 --- a/ml/ml40/roles/dts/machines/machine.py +++ b/ml/ml40/roles/dts/machines/machine.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT import DT +from ml.ml40.roles.dts.dt import DT class Machine(DT): diff --git a/ml/ml40/roles/dts/parts/__init__.py b/ml/ml40/roles/dts/parts/__init__.py index 121a68b..e69de29 100644 --- a/ml/ml40/roles/dts/parts/__init__.py +++ b/ml/ml40/roles/dts/parts/__init__.py @@ -1,5 +0,0 @@ -from .Part import Part -from .Crane import Crane -from .Engine import Engine -from .Scale import Scale -from .Tank import Tank diff --git a/ml/ml40/roles/dts/parts/crane.py b/ml/ml40/roles/dts/parts/crane.py index 032bb89..5069bab 100644 --- a/ml/ml40/roles/dts/parts/crane.py +++ b/ml/ml40/roles/dts/parts/crane.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT.Part import Part +from ml.ml40.roles.dts.parts.part import Part class Crane(Part): diff --git a/ml/ml40/roles/dts/parts/engine.py b/ml/ml40/roles/dts/parts/engine.py index 1ae3dd8..0d4d25f 100644 --- a/ml/ml40/roles/dts/parts/engine.py +++ b/ml/ml40/roles/dts/parts/engine.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT.Part import Part +from ml.ml40.roles.dts.parts.part import Part class Engine(Part): diff --git a/ml/ml40/roles/dts/parts/part.py b/ml/ml40/roles/dts/parts/part.py index bb71cf5..d34bcb6 100644 --- a/ml/ml40/roles/dts/parts/part.py +++ b/ml/ml40/roles/dts/parts/part.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT import DT +from ml.ml40.roles.dts.dt import DT class Part(DT): diff --git a/ml/ml40/roles/dts/parts/scale.py b/ml/ml40/roles/dts/parts/scale.py index 769e185..fbcb8e1 100644 --- a/ml/ml40/roles/dts/parts/scale.py +++ b/ml/ml40/roles/dts/parts/scale.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT.Part import Part +from ml.ml40.roles.dts.parts.part import Part class Scale(Part): diff --git a/ml/ml40/roles/dts/parts/tank.py b/ml/ml40/roles/dts/parts/tank.py index cd6a314..ef74695 100644 --- a/ml/ml40/roles/dts/parts/tank.py +++ b/ml/ml40/roles/dts/parts/tank.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT.Part import Part +from ml.ml40.roles.dts.parts.part import Part class Tank(Part): diff --git a/ml/ml40/roles/dts/persons/__init__.py b/ml/ml40/roles/dts/persons/__init__.py index b23ce9d..e69de29 100644 --- a/ml/ml40/roles/dts/persons/__init__.py +++ b/ml/ml40/roles/dts/persons/__init__.py @@ -1,2 +0,0 @@ -from .Person import Person -from .MachineOperator import MachineOperator \ No newline at end of file diff --git a/ml/ml40/roles/dts/persons/machine_operator.py b/ml/ml40/roles/dts/persons/machine_operator.py index 892c71d..74d9d6a 100644 --- a/ml/ml40/roles/dts/persons/machine_operator.py +++ b/ml/ml40/roles/dts/persons/machine_operator.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT.Person import Person +from ml.ml40.roles.dts.persons.person import Person class MachineOperator(Person): diff --git a/ml/ml40/roles/dts/persons/person.py b/ml/ml40/roles/dts/persons/person.py index 608ee6c..7c9a86b 100644 --- a/ml/ml40/roles/dts/persons/person.py +++ b/ml/ml40/roles/dts/persons/person.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT import DT +from ml.ml40.roles.dts.dt import DT class Person(DT): diff --git a/ml/ml40/roles/dts/sensors/__init__.py b/ml/ml40/roles/dts/sensors/__init__.py index 91a60e3..e69de29 100644 --- a/ml/ml40/roles/dts/sensors/__init__.py +++ b/ml/ml40/roles/dts/sensors/__init__.py @@ -1,4 +0,0 @@ -from .Sensor import Sensor -from .AirSensor import AirSensor -from .SoilSensor import SoilSensor - diff --git a/ml/ml40/roles/dts/sensors/air_sensor.py b/ml/ml40/roles/dts/sensors/air_sensor.py index 6028165..d31cf62 100644 --- a/ml/ml40/roles/dts/sensors/air_sensor.py +++ b/ml/ml40/roles/dts/sensors/air_sensor.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT.Sensor import Sensor +from ml.ml40.roles.dts.sensors.sensor import Sensor class AirSensor(Sensor): diff --git a/ml/ml40/roles/dts/sensors/sensor.py b/ml/ml40/roles/dts/sensors/sensor.py index 1c6f981..e23c948 100644 --- a/ml/ml40/roles/dts/sensors/sensor.py +++ b/ml/ml40/roles/dts/sensors/sensor.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT import DT +from ml.ml40.roles.dts.dt import DT class Sensor(DT): diff --git a/ml/ml40/roles/dts/sensors/soil_sensor.py b/ml/ml40/roles/dts/sensors/soil_sensor.py index 61b7032..c2179fa 100644 --- a/ml/ml40/roles/dts/sensors/soil_sensor.py +++ b/ml/ml40/roles/dts/sensors/soil_sensor.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT.Sensor import Sensor +from ml.ml40.roles.dts.sensors.sensor import Sensor class SoilSensor(Sensor): diff --git a/ml/ml40/roles/dts/sites/__init__.py b/ml/ml40/roles/dts/sites/__init__.py index 7a5c7f3..e69de29 100644 --- a/ml/ml40/roles/dts/sites/__init__.py +++ b/ml/ml40/roles/dts/sites/__init__.py @@ -1 +0,0 @@ -from .Site import Site \ No newline at end of file diff --git a/ml/ml40/roles/dts/sites/site.py b/ml/ml40/roles/dts/sites/site.py index 6073307..450c9d9 100644 --- a/ml/ml40/roles/dts/sites/site.py +++ b/ml/ml40/roles/dts/sites/site.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT import DT +from ml.ml40.roles.dts.dt import DT class Site(DT): diff --git a/ml/ml40/roles/dts/ways/__init__.py b/ml/ml40/roles/dts/ways/__init__.py index 30825fc..e69de29 100644 --- a/ml/ml40/roles/dts/ways/__init__.py +++ b/ml/ml40/roles/dts/ways/__init__.py @@ -1 +0,0 @@ -from .Way import Way \ No newline at end of file diff --git a/ml/ml40/roles/dts/ways/way.py b/ml/ml40/roles/dts/ways/way.py index 2ee0df5..77168fe 100644 --- a/ml/ml40/roles/dts/ways/way.py +++ b/ml/ml40/roles/dts/ways/way.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.DT import DT +from ml.ml40.roles.dts.dt import DT class Way(DT): diff --git a/ml/ml40/roles/hmis/__init__.py b/ml/ml40/roles/hmis/__init__.py index 7aeb0d0..e69de29 100644 --- a/ml/ml40/roles/hmis/__init__.py +++ b/ml/ml40/roles/hmis/__init__.py @@ -1,5 +0,0 @@ -from .HMI import HMI -from .App import App -from .Dashboard import Dashboard -from .HMD import HMD -from .MachineUI import MachineUI diff --git a/ml/ml40/roles/hmis/app.py b/ml/ml40/roles/hmis/app.py index 21fcac5..d8e068b 100644 --- a/ml/ml40/roles/hmis/app.py +++ b/ml/ml40/roles/hmis/app.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.HMI import HMI +from ml.ml40.roles.hmis.hmi import HMI class App(HMI): diff --git a/ml/ml40/roles/hmis/dashboard.py b/ml/ml40/roles/hmis/dashboard.py index 9f21448..bee56fe 100644 --- a/ml/ml40/roles/hmis/dashboard.py +++ b/ml/ml40/roles/hmis/dashboard.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.HMI import HMI +from ml.ml40.roles.hmis.hmi import HMI class Dashboard(HMI): diff --git a/ml/ml40/roles/hmis/hmd.py b/ml/ml40/roles/hmis/hmd.py index 05065a6..7da563a 100644 --- a/ml/ml40/roles/hmis/hmd.py +++ b/ml/ml40/roles/hmis/hmd.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.HMI import HMI +from ml.ml40.roles.hmis.hmi import HMI class HMD(HMI): diff --git a/ml/ml40/roles/hmis/machine_ui.py b/ml/ml40/roles/hmis/machine_ui.py index 664eb8e..fea821f 100644 --- a/ml/ml40/roles/hmis/machine_ui.py +++ b/ml/ml40/roles/hmis/machine_ui.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.role.HMI import HMI +from ml.ml40.roles.hmis.hmi import HMI class MachineUI(HMI): diff --git a/ml/ml40/roles/role.py b/ml/ml40/roles/role.py index 0d4b1ff..ce4fcfb 100644 --- a/ml/ml40/roles/role.py +++ b/ml/ml40/roles/role.py @@ -1,3 +1,16 @@ -class Role(): +from ml.managed_actor import ManagedActor + + +class Role(ManagedActor): def __init__(self, name, ref_managing_actor): - pass + super(Role, self).__init__(name, ref_managing_actor) + self.__name = name + + @property + def name(self): + return self.__name + + @name.setter + def name(self, value): + self.__name = value + -- GitLab From a7e854153c5ef6f387ae63806ab092047f583cf4 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Wed, 7 Oct 2020 12:57:02 +0200 Subject: [PATCH 41/84] clear up values --- ml/fml40/features/properties/values/dbh.py | 12 +++++- .../documents/jobs/log_transportation_job.py | 10 ++++- .../reports/afforestation_suggestion.py | 2 +- .../reports/felling_support_suggestion.py | 2 +- .../reports/log_transportation_report.py | 2 +- .../reports/soil_moisture_measurement.py | 2 +- .../properties/values/inventory_data.py | 14 ++++--- .../values/stem_segment_properties.py | 41 ++++++++++++++++--- ml/fml40/features/properties/values/tilt.py | 22 ++++++++-- .../features/properties/values/tree_data.py | 6 ++- .../features/properties/values/tree_type.py | 16 ++++++-- .../values/documents/jobs/generic_job.py | 2 +- .../properties/values/documents/jobs/job.py | 4 +- .../values/documents/jobs/job_list.py | 10 ++++- ml/ml40/features/properties/values/value.py | 1 + 15 files changed, 115 insertions(+), 31 deletions(-) diff --git a/ml/fml40/features/properties/values/dbh.py b/ml/fml40/features/properties/values/dbh.py index a3b9411..f22c883 100644 --- a/ml/fml40/features/properties/values/dbh.py +++ b/ml/fml40/features/properties/values/dbh.py @@ -1,8 +1,16 @@ -from modelling_language.ml40.feature.property.value import Value +from ml.ml40.features.properties.values.value import Value class DBH(Value): def __init__(self, name, ref_managing_actor, dbh: float): super(DBH, self).__init__(name=name, ref_managing_actor=ref_managing_actor) - self.dbh = dbh \ No newline at end of file + self.__dbh = dbh + + @property + def dbh(self): + return self.__dbh + + @dbh.setter + def dbh(self, value): + self.__dbh = value \ No newline at end of file diff --git a/ml/fml40/features/properties/values/documents/jobs/log_transportation_job.py b/ml/fml40/features/properties/values/documents/jobs/log_transportation_job.py index c814feb..1406291 100644 --- a/ml/fml40/features/properties/values/documents/jobs/log_transportation_job.py +++ b/ml/fml40/features/properties/values/documents/jobs/log_transportation_job.py @@ -6,4 +6,12 @@ def __init__(self, name, ref_managing_actor, woodPiles: list): super(LogTransportationJob, self).__init__( name=name, ref_managing_actor=ref_managing_actor) - self.woodPiles = woodPiles + self.__woodPiles = woodPiles + + @property + def woodPiles(self): + return self.__woodPiles + + @woodPiles.setter + def woodPiles(self, value): + self.__woodPiles.append(value) diff --git a/ml/fml40/features/properties/values/documents/reports/afforestation_suggestion.py b/ml/fml40/features/properties/values/documents/reports/afforestation_suggestion.py index 35980dd..90419b9 100644 --- a/ml/fml40/features/properties/values/documents/reports/afforestation_suggestion.py +++ b/ml/fml40/features/properties/values/documents/reports/afforestation_suggestion.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.property.value.document.report import Report +from ml.ml40.features.properties.values.documents.reports.report import Report class AfforestationSuggestion(Report): diff --git a/ml/fml40/features/properties/values/documents/reports/felling_support_suggestion.py b/ml/fml40/features/properties/values/documents/reports/felling_support_suggestion.py index 122f79b..0f13119 100644 --- a/ml/fml40/features/properties/values/documents/reports/felling_support_suggestion.py +++ b/ml/fml40/features/properties/values/documents/reports/felling_support_suggestion.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.property.value.document.report import Report +from ml.ml40.features.properties.values.documents.reports.report import Report class FellingSupportSuggestion(Report): diff --git a/ml/fml40/features/properties/values/documents/reports/log_transportation_report.py b/ml/fml40/features/properties/values/documents/reports/log_transportation_report.py index d4184cb..fd3bd4a 100644 --- a/ml/fml40/features/properties/values/documents/reports/log_transportation_report.py +++ b/ml/fml40/features/properties/values/documents/reports/log_transportation_report.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.property.value.document.report import Report +from ml.ml40.features.properties.values.documents.reports.report import Report class LogTransportationReport(Report): diff --git a/ml/fml40/features/properties/values/documents/reports/soil_moisture_measurement.py b/ml/fml40/features/properties/values/documents/reports/soil_moisture_measurement.py index 03f6dc2..f0cd721 100644 --- a/ml/fml40/features/properties/values/documents/reports/soil_moisture_measurement.py +++ b/ml/fml40/features/properties/values/documents/reports/soil_moisture_measurement.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.property.value.document.report import Report +from ml.ml40.features.properties.values.documents.reports.report import Report class SoilMoistureMeasurement(Report): diff --git a/ml/fml40/features/properties/values/inventory_data.py b/ml/fml40/features/properties/values/inventory_data.py index 836b37b..1a251f0 100644 --- a/ml/fml40/features/properties/values/inventory_data.py +++ b/ml/fml40/features/properties/values/inventory_data.py @@ -1,15 +1,17 @@ -from modelling_language.fml40.feature.property.value import AbstractInventory -from modelling_language.ml40.feature.property.value import Value +from ml.fml40.features.properties.values.abstract_inventory import AbstractInventory +from ml.ml40.features.properties.values.value import Value class InventoryData(Value): - def __init__(self): - self._data = [] + def __init__(self, name, ref_managing_actor): + super(InventoryData, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.__data = [] @property def data(self): - return self._data + return self.__data @data.setter def data(self, value: AbstractInventory): - self._data.append(value) + self.__data.append(value) diff --git a/ml/fml40/features/properties/values/stem_segment_properties.py b/ml/fml40/features/properties/values/stem_segment_properties.py index 66be1ae..b535c14 100644 --- a/ml/fml40/features/properties/values/stem_segment_properties.py +++ b/ml/fml40/features/properties/values/stem_segment_properties.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.property.value import Value +from ml.ml40.features.properties.values.value import Value class StemSegmentProperties(Value): @@ -6,8 +6,39 @@ def __init__(self, name, ref_managing_actor, diameter: float, length: float, quality: str, woodType: str): super(StemSegmentProperties, self).__init__(name=name, ref_managing_actor=ref_managing_actor) - self.diameter = diameter - self.length = length - self.quality = quality - self.woodType = woodType + self.__diameter = diameter + self.__length = length + self.__quality = quality + self.__woodType = woodType + @property + def diameter(self): + return self.__diameter + + @diameter.setter + def diameter(self, value): + self.__diameter = value + + @property + def length(self): + return self.__length + + @length.setter + def length(self, value): + self.__length = value + + @property + def quality(self): + return self.__quality + + @quality.setter + def quality(self, value): + self.__quality = value + + @property + def woodType(self): + return self.__woodType + + @woodType.setter + def woodType(self, value): + self.__woodType = value diff --git a/ml/fml40/features/properties/values/tilt.py b/ml/fml40/features/properties/values/tilt.py index 56488ec..d9debff 100644 --- a/ml/fml40/features/properties/values/tilt.py +++ b/ml/fml40/features/properties/values/tilt.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.property.value import Value +from ml.ml40.features.properties.values.value import Value class Tilt(Value): @@ -6,5 +6,21 @@ def __init__(self, name, ref_managing_actor, direction: float, tilt: float): super(Tilt, self).__init__(name=name, ref_managing_actor=ref_managing_actor) - self.direction = direction - self.tilt = tilt + self.__direction = direction + self.__tilt = tilt + + @property + def direction(self): + return self.__direction + + @direction.setter + def direction(self, value): + self.__direction = value + + @property + def tilt(self): + return self.__tilt + + @tilt.setter + def tilt(self, value): + self.__tilt = value diff --git a/ml/fml40/features/properties/values/tree_data.py b/ml/fml40/features/properties/values/tree_data.py index 1cd0b75..a979591 100644 --- a/ml/fml40/features/properties/values/tree_data.py +++ b/ml/fml40/features/properties/values/tree_data.py @@ -1,5 +1,7 @@ -from modelling_language.ml40.feature.property.value import Value +from ml.ml40.features.properties.values.value import Value class TreeData(Value): - pass + def __init__(self, name, ref_managing_actor): + super(TreeData, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) diff --git a/ml/fml40/features/properties/values/tree_type.py b/ml/fml40/features/properties/values/tree_type.py index 57fc36c..d6cdaa4 100644 --- a/ml/fml40/features/properties/values/tree_type.py +++ b/ml/fml40/features/properties/values/tree_type.py @@ -1,8 +1,16 @@ -from modelling_language.ml40.feature.property.value import Value +from ml.ml40.features.properties.values.value import Value class TreeType(Value): - def __init__(self, name, ref_managing_ref, conifer: bool): + def __init__(self, name, ref_managing_actor, conifer: bool): super(TreeType, self).__init__(name=name, - ref_managing_ref=ref_managing_ref) - self.conifer = conifer \ No newline at end of file + ref_managing_actor=ref_managing_actor) + self.__conifer = conifer + + @property + def conifer(self): + return self.__conifer + + @conifer.setter + def conifer(self, value): + self.__conifer = value diff --git a/ml/ml40/features/properties/values/documents/jobs/generic_job.py b/ml/ml40/features/properties/values/documents/jobs/generic_job.py index 27ef2ed..ebfe435 100644 --- a/ml/ml40/features/properties/values/documents/jobs/generic_job.py +++ b/ml/ml40/features/properties/values/documents/jobs/generic_job.py @@ -1,4 +1,4 @@ -from ml.ml40.feature.properties.value.document.job import Job +from ml.ml40.features.properties.values.documents.jobs.job import Job class GenericJob(Job): diff --git a/ml/ml40/features/properties/values/documents/jobs/job.py b/ml/ml40/features/properties/values/documents/jobs/job.py index 50e401a..ef8b924 100644 --- a/ml/ml40/features/properties/values/documents/jobs/job.py +++ b/ml/ml40/features/properties/values/documents/jobs/job.py @@ -3,10 +3,10 @@ class Job(Document): - def __init__(self, name, ref_managing_actor): + def __init__(self, name, ref_managing_actor, job_status: JobStatus): super(Job, self).__init__(name=name, ref_managing_actor=ref_managing_actor) - self.__status = JobStatus(0) + self.__status = job_status @property def status(self): diff --git a/ml/ml40/features/properties/values/documents/jobs/job_list.py b/ml/ml40/features/properties/values/documents/jobs/job_list.py index 2e6d648..e168497 100644 --- a/ml/ml40/features/properties/values/documents/jobs/job_list.py +++ b/ml/ml40/features/properties/values/documents/jobs/job_list.py @@ -5,4 +5,12 @@ class JobList(Job): def __init__(self, name, ref_managing_actor): super(JobList, self).__init__(name=name, ref_managing_actor=ref_managing_actor) - self.jobs = list() + self.__jobs = list() + + @property + def jobs(self): + return self.__jobs + + @jobs.setter + def jobs(self, value): + self.__jobs.append(value) \ No newline at end of file diff --git a/ml/ml40/features/properties/values/value.py b/ml/ml40/features/properties/values/value.py index 67d1a6b..6330882 100644 --- a/ml/ml40/features/properties/values/value.py +++ b/ml/ml40/features/properties/values/value.py @@ -1,6 +1,7 @@ from ml.ml40.features.properties.property import Property import copy + class Value(Property): def __init__(self, name, ref_managing_actor): super(Value, self).__init__(name=name, ref_managing_actor=ref_managing_actor) -- GitLab From 7dbabfcdff457301334fd8e03d2b9bcd69405ccc Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Wed, 7 Oct 2020 19:25:09 +0200 Subject: [PATCH 42/84] add subfeatures --- configs/config_my_dt_harvester.json | 2 +- dt_creation.py | 23 +++++--- ml/dt_factory.py | 44 +++++++++------ .../functionalities/accepts_felling_jobs.py | 2 +- ml/fml40/roles/dts/machines/forest_machine.py | 6 ++- ml/fml40/roles/dts/machines/harvester.py | 4 +- ml/managed_actor.py | 37 +++++++++++-- ml/ml40/features/feature.py | 39 +++++--------- ml/ml40/features/properties/composite.py | 9 +++- .../properties/values/documents/jobs/job.py | 4 +- .../properties/values/rotational_speed.py | 12 ++++- ml/ml40/roles/dts/machines/machine.py | 7 ++- ml/thing.py | 18 ++----- ml/tools.py | 53 +++++++++++++++---- 14 files changed, 171 insertions(+), 89 deletions(-) diff --git a/configs/config_my_dt_harvester.json b/configs/config_my_dt_harvester.json index 31a7774..ab90702 100644 --- a/configs/config_my_dt_harvester.json +++ b/configs/config_my_dt_harvester.json @@ -1 +1 @@ -{"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 diff --git a/dt_creation.py b/dt_creation.py index 9a3f329..bce02ad 100644 --- a/dt_creation.py +++ b/dt_creation.py @@ -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()) diff --git a/ml/dt_factory.py b/ml/dt_factory.py index d5c90e6..59e6b63 100644 --- a/ml/dt_factory.py +++ b/ml/dt_factory.py @@ -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. diff --git a/ml/fml40/features/functionalities/accepts_felling_jobs.py b/ml/fml40/features/functionalities/accepts_felling_jobs.py index 230e208..859ef9a 100644 --- a/ml/fml40/features/functionalities/accepts_felling_jobs.py +++ b/ml/fml40/features/functionalities/accepts_felling_jobs.py @@ -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) diff --git a/ml/fml40/roles/dts/machines/forest_machine.py b/ml/fml40/roles/dts/machines/forest_machine.py index 07e5507..d4ae55c 100644 --- a/ml/fml40/roles/dts/machines/forest_machine.py +++ b/ml/fml40/roles/dts/machines/forest_machine.py @@ -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 diff --git a/ml/fml40/roles/dts/machines/harvester.py b/ml/fml40/roles/dts/machines/harvester.py index eb6bc47..af4e876 100644 --- a/ml/fml40/roles/dts/machines/harvester.py +++ b/ml/fml40/roles/dts/machines/harvester.py @@ -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) diff --git a/ml/managed_actor.py b/ml/managed_actor.py index 442025c..28bfe6a 100644 --- a/ml/managed_actor.py +++ b/ml/managed_actor.py @@ -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", "") diff --git a/ml/ml40/features/feature.py b/ml/ml40/features/feature.py index b438a15..39bde64 100644 --- a/ml/ml40/features/feature.py +++ b/ml/ml40/features/feature.py @@ -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", "") diff --git a/ml/ml40/features/properties/composite.py b/ml/ml40/features/properties/composite.py index f4d16d9..de278d8 100644 --- a/ml/ml40/features/properties/composite.py +++ b/ml/ml40/features/properties/composite.py @@ -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) diff --git a/ml/ml40/features/properties/values/documents/jobs/job.py b/ml/ml40/features/properties/values/documents/jobs/job.py index ef8b924..15553d0 100644 --- a/ml/ml40/features/properties/values/documents/jobs/job.py +++ b/ml/ml40/features/properties/values/documents/jobs/job.py @@ -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 diff --git a/ml/ml40/features/properties/values/rotational_speed.py b/ml/ml40/features/properties/values/rotational_speed.py index 46c4699..061c133 100644 --- a/ml/ml40/features/properties/values/rotational_speed.py +++ b/ml/ml40/features/properties/values/rotational_speed.py @@ -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 diff --git a/ml/ml40/roles/dts/machines/machine.py b/ml/ml40/roles/dts/machines/machine.py index 50fc16e..ee30508 100644 --- a/ml/ml40/roles/dts/machines/machine.py +++ b/ml/ml40/roles/dts/machines/machine.py @@ -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 + ) diff --git a/ml/thing.py b/ml/thing.py index 6d29980..4e61a25 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -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) diff --git a/ml/tools.py b/ml/tools.py index 5d9d298..8c0a76c 100644 --- a/ml/tools.py +++ b/ml/tools.py @@ -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 - - -- GitLab From 2f71a532840f23f8a628f59d5e83cf98d4672db7 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Thu, 8 Oct 2020 22:21:27 +0200 Subject: [PATCH 43/84] UPDATE some properties --- configs/config_my_dt_harvester.json | 2 +- dt_creation.py | 55 ++-- dt_creation_hmi.py | 56 +++- ml/dt_factory.py | 273 ++++++++++++++---- .../functionalities/accepts_felling_jobs.py | 20 +- .../accepts_felling_support_jobs.py | 5 +- .../accepts_log_measurements.py | 4 +- .../accepts_log_transportaition_jobs.py | 4 +- .../accepts_moisture_measurement.py | 4 +- .../functionalities/accepts_move_commands.py | 3 +- .../accepts_single_tree_felling_jobs.py | 4 +- .../classifies_tree_species.py | 2 +- ml/fml40/features/functionalities/cuts.py | 2 +- .../functionalities/determines_passability.py | 3 +- .../functionalities/displays_health_alarms.py | 2 +- .../evaluates_stand_attributes.py | 2 +- ml/fml40/features/functionalities/fells.py | 7 +- .../forest_planning_evaluation.py | 2 +- .../generates_afforestation_suggestions.py | 4 +- .../generates_felling_suggestions.py | 6 +- ml/fml40/features/functionalities/grabs.py | 2 +- .../features/functionalities/measure_wood.py | 4 +- .../functionalities/monitor_health_status.py | 2 +- .../functionalities/provides_tree_data.py | 8 +- .../functionalities/provides_weather_data.py | 2 +- .../functionalities/simulates_tree_growth.py | 2 +- .../functionalities/supports_felling.py | 6 +- .../functionalities/transports_logs.py | 6 +- .../features/properties/values/assortment.py | 27 ++ ml/fml40/features/properties/values/dbh.py | 15 +- .../values/documents/reports/map_data.py | 3 +- .../properties/values/harvested_volume.py | 27 ++ .../properties/values/harvesting_parameter.py | 27 ++ .../properties/values/thickness_class.py | 7 + .../features/properties/values/tree_type.py | 2 +- .../properties/values/wood_quality.py | 8 + ml/managed_actor.py | 17 +- ml/ml40/features/feature.py | 31 +- .../functionalities/accepts_reports.py | 4 +- .../features/functionalities/clears_jobs.py | 4 +- .../features/functionalities/plans_routes.py | 6 +- .../functionalities/provides_map_data.py | 5 +- .../provides_operational_data.py | 4 +- ml/ml40/features/functionalities/renders.py | 2 +- ml/ml40/features/properties/composite.py | 17 +- .../features/properties/values/location.py | 32 +- .../features/properties/values/temperature.py | 4 +- ml/thing.py | 50 +--- ml/tools.py | 67 ++--- 49 files changed, 582 insertions(+), 269 deletions(-) create mode 100644 ml/fml40/features/properties/values/assortment.py create mode 100644 ml/fml40/features/properties/values/harvested_volume.py create mode 100644 ml/fml40/features/properties/values/harvesting_parameter.py create mode 100644 ml/fml40/features/properties/values/thickness_class.py create mode 100644 ml/fml40/features/properties/values/wood_quality.py diff --git a/configs/config_my_dt_harvester.json b/configs/config_my_dt_harvester.json index ab90702..3c2bcae 100644 --- a/configs/config_my_dt_harvester.json +++ b/configs/config_my_dt_harvester.json @@ -1 +1 @@ -{"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 +{"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 diff --git a/dt_creation.py b/dt_creation.py index bce02ad..49083f7 100644 --- a/dt_creation.py +++ b/dt_creation.py @@ -2,7 +2,7 @@ import json import jwt import requests -from ml.tools import load_config, make_config_file, make_sub_thing +from ml.tools import load_config, make_thing_config, 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 @@ -27,38 +27,36 @@ 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 + """ -create_thing_resp = requests.post(url="https://config.s3i.vswf.dev/things/", - headers={"Authorization": "Bearer {}".format(access_token), - "Content-Type": "application/json"}, - data=json.dumps({})) -dt_id = create_thing_resp.json().get("identifier", None) +s3i_config = s3i.Config(access_token) +resp = s3i_config.create_thing() +dt_id = resp.json().get("identifier", None) +dt_secret = resp.json().get("secret", None) +s3i_config.create_broker_queue(thing_id=dt_id) print(dt_id) -create_cloud_copy_resp = requests.post(url="https://config.s3i.vswf.dev/things/{}/repository".format(dt_id), - headers={"Authorization": "Bearer {}".format(access_token), - "Content-Type": "application/json"}) -create_endpoint_resp = requests.post(url="https://config.s3i.vswf.dev/things/{}/broker".format(dt_id), - headers={"Authorization": "Bearer {}".format(access_token), - "Content-Type": "application/json"}, - data=json.dumps({"encrypted": False}) - ) - -dt_secret = create_thing_resp.json().get("secret", None) """ + dt_id = "s3i:b6d1cc6d-896c-40fe-9403-b5b7682b1d03" dt_secret = "cd4d24d2-f702-4f51-b0cf-6b77a423b33a" dt_name = "my_dt_harvester" -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_engine = make_sub_thing(name="my_engine", roles=[{"class": "ml40::Engine"}], + features=[ + {"class": "ml40::RotationalSpeed", + "rpm": 5} + ]) + +config_cran = make_sub_thing(name="my_bord_computer", roles=[{"class": "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]}]) +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]}]) 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, is_broker_rest=True, is_broker=True, is_repo=False) @@ -72,9 +70,12 @@ def my_accept_job_func(self, job): return self.add_to_job_list(job) -dt_proxy.add_function_impl(AcceptsFellingJobsImpl, "AcceptsFellingJobs") -print(dt_proxy.features.get()) -#print(dt_proxy.features.get()["Composite"].targets.get()) +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()) diff --git a/dt_creation_hmi.py b/dt_creation_hmi.py index f4f99e1..43d2a98 100644 --- a/dt_creation_hmi.py +++ b/dt_creation_hmi.py @@ -1,10 +1,12 @@ import s3i import jwt import uuid -from ml.tools import find_broker_endpoint, make_config_file, load_config -from ml.dt_factory import create_dt_ref +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.app_logger import setup_logger import requests import json, time @@ -12,8 +14,10 @@ password = "8810515" hmi_id = "s3i:b4f72a11-fb3d-47a3-b499-e7a88eaa5fe1" hmi_secret = "84df6340-9d64-4ae6-9076-86c224ec484a" +setup_logger("my HMI") -config_file_name = make_config_file(dt_id=hmi_id, name="my HMI", roles="ml40::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, @@ -25,19 +29,51 @@ hmi_endpoint = find_broker_endpoint(hmi_proxy.dir.get(), hmi_id) serv_req = s3i.messages.ServiceRequest() -receiver = input("[S³I]: Please enter the id of your digital twin: ") -job_ref = FellingJob.start("ml40::FellingJob", hmi_ref) -job_proxy = job_ref.proxy() -job_proxy.identifier = "s3i:d53e84fd-432c-4e19-9008-546f296c2d5c" -print(job_proxy.to_json().get()) +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: ") serv_req.fillServiceRequest( senderUUID=hmi_id, receiverUUID=[receiver], sender_endpoint=hmi_endpoint, - serviceType="AcceptsFellingJobs/acceptJob", - parameters={"job": job_proxy.to_json().get()}, + serviceType="fml40::AcceptsFellingJobs/removeJob", + parameters={"identifier": id}, + #parameters={"job": felling_job_proxy.to_json().get()}, msgUUID=str(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)) + + diff --git a/ml/dt_factory.py b/ml/dt_factory.py index 59e6b63..a31118b 100644 --- a/ml/dt_factory.py +++ b/ml/dt_factory.py @@ -1,58 +1,161 @@ """ Implements a factory for managing digital twins.""" import sys, json -from s3i import IdentityProvider -from s3i import TokenType -from ml.tools import get_idp + from ml.managing_actor import ManagingActor -from ml.tools import load_config from ml.app_logger import APP_LOGGER -from ml.thing import Thing +from ml.tools import remove_namespace +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 +from ml.ml40.roles.hmis.machine_ui import MachineUI +from ml.ml40.roles.hmis.hmd import HMD from ml.ml40.roles.hmis.hmi import HMI +from ml.ml40.roles.dts.handheld_devices.handheld_device import HandheldDevice +from ml.ml40.roles.dts.machines.machine import Machine +from ml.ml40.roles.dts.parts.crane import Crane +from ml.ml40.roles.dts.parts.part import Part +from ml.ml40.roles.dts.parts.engine import Engine +from ml.ml40.roles.dts.parts.scale import Scale +from ml.ml40.roles.dts.parts.tank import Tank +from ml.ml40.roles.dts.persons.machine_operator import MachineOperator +from ml.ml40.roles.dts.persons.person import Person +from ml.ml40.roles.dts.sensors.sensor import Sensor +from ml.ml40.roles.dts.sensors.air_sensor import AirSensor +from ml.ml40.roles.dts.sensors.soil_sensor import SoilSensor +from ml.ml40.roles.dts.sites.site import Site +from ml.ml40.roles.dts.ways.way import Way + +from ml.fml40.roles.dts.handheld_devices.brushcutter import Brushcutter +from ml.fml40.roles.dts.handheld_devices.chainsaw import Chainsaw +from ml.fml40.roles.dts.machines.forest_machine import ForestMachine +from ml.fml40.roles.dts.machines.forwarder import Forwarder 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 +from ml.fml40.roles.dts.machines.mini_tractor import MiniTractor +from ml.fml40.roles.dts.machines.skidder import Skidder +from ml.fml40.roles.dts.machines.wheel_loader import WheelLoader +from ml.fml40.roles.dts.parts.grabber import Grabber +from ml.fml40.roles.dts.parts.harvesting_head import HarvestingHead +from ml.fml40.roles.dts.parts.log_loading_area import LogLoadingArea +from ml.fml40.roles.dts.parts.saw import Saw +from ml.fml40.roles.dts.parts.winch import Winch +from ml.fml40.roles.dts.persons.forest_owner import ForestOwner +from ml.fml40.roles.dts.persons.forest_worker import ForestWorker +from ml.fml40.roles.dts.persons.mini_tractor_operator import MiniTractorOperator +from ml.fml40.roles.dts.persons.skidder_operator import SkidderOperator +from ml.fml40.roles.dts.sensors.vitality_sensor import VilalitySensor +from ml.fml40.roles.dts.sites.forest_enterprise import ForestEnterprise +from ml.fml40.roles.dts.sites.hauler import Hauler +from ml.fml40.roles.dts.sites.mill.mill import Mill +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.values.weight import Weight +from ml.ml40.features.properties.property import Property +from ml.ml40.features.properties.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 +from ml.ml40.features.properties.values.liquid_filling_level import LiquidFillingLevel +from ml.ml40.features.properties.values.location import Location from ml.ml40.features.properties.values.moisture import Moisture -from ml.fml40.features.functionalities.accepts_proximity_alert import ( - AcceptsProximityAlert, -) +from ml.ml40.features.properties.values.personal_name import PersonalName +from ml.ml40.features.properties.values.rotational_speed import RotationalSpeed +from ml.ml40.features.properties.values.route import Route +from ml.ml40.features.properties.values.temperature import Temperature +from ml.ml40.features.properties.values.time_slot import TimeSlot +from ml.ml40.features.properties.values.weight import Weight +from ml.ml40.features.properties.values.documents.jobs.generic_job import GenericJob +from ml.ml40.features.properties.values.documents.jobs.job import Job +from ml.ml40.features.properties.values.documents.jobs.job_list import JobList +from ml.ml40.features.properties.values.documents.jobs.job_status import JobStatus +from ml.ml40.features.properties.values.documents.reports.report import Report + +from ml.fml40.features.properties.values.abstract_inventory import AbstractInventory +from ml.fml40.features.properties.values.assortment import Assortment +from ml.fml40.features.properties.values.dbh import DBH +from ml.fml40.features.properties.values.harvesting_parameter import HarvestingParameters +from ml.fml40.features.properties.values.harvested_volume import HarvestedVolume +from ml.fml40.features.properties.values.inventory_data import InventoryData +from ml.fml40.features.properties.values.stem_segment_properties import StemSegmentProperties +from ml.fml40.features.properties.values.thickness_class import ThicknessClass +from ml.fml40.features.properties.values.tilt import Tilt +from ml.fml40.features.properties.values.tree_data import TreeData +from ml.fml40.features.properties.values.tree_type import TreeType +from ml.fml40.features.properties.values.wood_quality import WoodQuality + +from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob +from ml.fml40.features.properties.values.documents.jobs.fellung_support_job import FellingSupportJob +from ml.fml40.features.properties.values.documents.jobs.forwarding_job import ForwardingJob +from ml.fml40.features.properties.values.documents.jobs.log_transportation_job import LogTransportationJob +from ml.fml40.features.properties.values.documents.jobs.safety_felling_job import SafetyFellingJob +from ml.fml40.features.properties.values.documents.jobs.single_tree_felling_job import SingleTreeFellingJob +from ml.fml40.features.properties.values.documents.jobs.transportation_job import TransportationJob + +from ml.fml40.features.properties.values.documents.reports.afforestation_suggestion import AfforestationSuggestion +from ml.fml40.features.properties.values.documents.reports.felling_method_suggestion import FellingMethodSuggestion +from ml.fml40.features.properties.values.documents.reports.felling_support_suggestion import FellingSupportSuggestion +from ml.fml40.features.properties.values.documents.reports.felling_tool import FellingTool +from ml.fml40.features.properties.values.documents.reports.log_measurement import LogMeasurement +from ml.fml40.features.properties.values.documents.reports.log_transportation_report import LogTransportationReport +from ml.fml40.features.properties.values.documents.reports.map_data import MapData +from ml.fml40.features.properties.values.documents.reports.moisture_prediction_report import MoisturePredictionReport +from ml.fml40.features.properties.values.documents.reports.passability_report import PassabilityReport +from ml.fml40.features.properties.values.documents.reports.production_data import ProductionData +from ml.fml40.features.properties.values.documents.reports.soil_moisture_measurement import SoilMoistureMeasurement + +from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs +from ml.ml40.features.functionalities.accepts_reports import AcceptsReports +from ml.ml40.features.functionalities.clears_jobs import ClearsJobs +from ml.ml40.features.functionalities.functionality import Functionality +from ml.ml40.features.functionalities.manages_jobs import ManagesJobs +from ml.ml40.features.functionalities.plans_routes import PlansRoutes +from ml.ml40.features.functionalities.provides_map_data import ProvidesMapData +from ml.ml40.features.functionalities.provides_operational_data import ProvidesOperationalData +from ml.ml40.features.functionalities.renders import Renders + from ml.fml40.features.functionalities.accepts_felling_jobs import AcceptsFellingJobs -from ml.fml40.features.functionalities.harvests import Harvests +from ml.fml40.features.functionalities.accepts_felling_support_jobs import AcceptFellingSupportJobs +from ml.fml40.features.functionalities.accepts_log_measurements import AcceptsLogMeasurements +from ml.fml40.features.functionalities.accepts_log_transportaition_jobs import AcceptsLogTransportationJobs +from ml.fml40.features.functionalities.accepts_moisture_measurement import AcceptsMoistureMeasurement +from ml.fml40.features.functionalities.accepts_move_commands import AcceptsMoveCommands +from ml.fml40.features.functionalities.accepts_passability_report import AcceptsPassabilityReport +from ml.fml40.features.functionalities.accepts_proximity_alert import AcceptsProximityAlert +from ml.fml40.features.functionalities.accepts_single_tree_felling_jobs import AcceptsSingleTreeFellingJobs +from ml.fml40.features.functionalities.classifies_tree_species import ClassifiesTreeSpecies +from ml.fml40.features.functionalities.cuts import Cuts +from ml.fml40.features.functionalities.determines_passability import DeterminesPassability +from ml.fml40.features.functionalities.displays_health_alarms import DisplaysHealthAlarms +from ml.fml40.features.functionalities.evaluates_stand_attributes import EvaluatesStandAttributes +from ml.fml40.features.functionalities.fells import Fells +from ml.fml40.features.functionalities.forest_planning_evaluation import ForestPlanningEvaluation from ml.fml40.features.functionalities.forwards import Forwards -from ml.fml40.features.functionalities.provides_production_data import ( - ProvidesProductionData, -) -from ml.fml40.features.functionalities.accepts_forwarding_jobs import ( - AcceptsForwardingJobs, -) -from ml.fml40.features.functionalities.provides_passability_information import ( - ProvidesPassabilityInformation, -) -from ml.fml40.features.functionalities.provides_moisture_prediction import ( - ProvidesMoisturePrediction, -) -from ml.fml40.features.functionalities.accepts_passability_report import ( - AcceptsPassabilityReport, -) -from ml.fml40.features.properties.values.documents.reports.moisture_prediction_report import ( - MoisturePredictionReport, -) +from ml.fml40.features.functionalities.generates_afforestation_suggestions import GeneratesAfforestationSuggestions +from ml.fml40.features.functionalities.generates_felling_suggestions import GeneratesFellingSuggestions +from ml.fml40.features.functionalities.grabs import Grabs +from ml.fml40.features.functionalities.harvests import Harvests +from ml.fml40.features.functionalities.measure_wood import MeasuresWood +from ml.fml40.features.functionalities.monitor_health_status import MonitorsHealthStatus +from ml.fml40.features.functionalities.provides_moisture_prediction import ProvidesMoisturePrediction +from ml.fml40.features.functionalities.provides_passability_information import ProvidesPassabilityInformation +from ml.fml40.features.functionalities.provides_production_data import ProvidesProductionData +from ml.fml40.features.functionalities.provides_tree_data import ProvidesTreeData +from ml.fml40.features.functionalities.provides_weather_data import ProvidesWeatherData +from ml.fml40.features.functionalities.simulates_tree_growth import SimulatesTreeGrowth +from ml.fml40.features.functionalities.supports_felling import SupportsFelling +from ml.fml40.features.functionalities.transports_logs import TransportsLogs # TODO: Get rid of this global variable # TODO: automatically get all classes in modul DT_FACTORY = {} +""" DT_FACTORY["Thing"] = Thing DT_FACTORY["Composite"] = Composite @@ -77,7 +180,13 @@ DT_FACTORY["ProvidesMoisturePrediction"] = ProvidesMoisturePrediction DT_FACTORY["MoisturePredictionReport"] = MoisturePredictionReport DT_FACTORY["AcceptsPassabilityReport"] = AcceptsPassabilityReport +""" +import sys, inspect + +clsmembers = inspect.getmembers(sys.modules[__name__], inspect.isclass) +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. @@ -115,26 +224,45 @@ def create_dt_with_idp(config, id_p): # 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", []) + temp_dict = {} for json_sub_feature in json_sub_features: - class_name = json_sub_feature.get("class", "") - sub_feature = DT_FACTORY.get(class_name, None) - if not sub_feature: - APP_LOGGER.critical("Subfeature: %s is missing" % class_name) + #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) else: - APP_LOGGER.debug("Adding subfeature: %s" % class_name) - sub_feature_ref = sub_feature.start("", thing_ref) - sub_feature_proxy = sub_feature_ref.proxy() - sub_feature_proxy.from_json(json_feature).get() - type_name = sub_feature_proxy.type_name.get() - sub_features = feature_proxy.subfeatures.get() - sub_features.append(sub_feature_ref) + 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(): + if key == "targets": + build_sub_thing(feature_proxy, sub_feature) + elif key == "subFeatures": + build_sub_featrues(thing_ref, sub_feature_ref.proxy(), json_sub_feature) + + else: + setattr(sub_feature_ref.proxy(), key, json_sub_feature[key]) + + feature_proxy.subFeatures = temp_dict + + +def build_sub_thing(feature_proxy, 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) def build(thing_ref, 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) @@ -142,21 +270,31 @@ def build(thing_ref, attributes): json_features = attributes.get("features", []) for json_feature in json_features: feature_name = json_feature.get("class", "") - feature = DT_FACTORY.get(feature_name, None) + 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 - 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) + role = DT_FACTORY.get(remove_namespace(role_name), None) if role is None: APP_LOGGER.critical("Roles: %s is missing" % role_name) else: @@ -165,7 +303,32 @@ def __build_roles(thing_ref, thing_proxy, roles): 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): +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}) + + return feature_ref + + +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. @@ -179,10 +342,9 @@ def create_dt_ref(model, grant_type, secret, username, password, is_broker_rest, attributes = model.get("attributes", None) if attributes is None: sys.exit("Incomplete model: attributes missing!") - attributes = str(attributes).replace("fml40::", "").replace("ml40::", "").replace("'", '"') - attributes = json.loads(attributes) - thing_type = attributes.get("class", "") - + #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() @@ -206,4 +368,3 @@ def create_dt_ref(model, grant_type, secret, username, password, is_broker_rest, ) build(thing_ref, attributes) return thing_ref - diff --git a/ml/fml40/features/functionalities/accepts_felling_jobs.py b/ml/fml40/features/functionalities/accepts_felling_jobs.py index 859ef9a..d2f4c6b 100644 --- a/ml/fml40/features/functionalities/accepts_felling_jobs.py +++ b/ml/fml40/features/functionalities/accepts_felling_jobs.py @@ -20,7 +20,7 @@ def acceptJob(self, job: FellingJob) -> bool: def queryJobStatus(self, identifier): APP_LOGGER.info("Checking the job status of job {}".format(identifier)) for job in self.job_list: - if job.get("id") == identifier: + if job.get("identifier") == identifier: APP_LOGGER.info("Job {} is now in status {}".format(identifier, job.get("status"))) return {"identifier": identifier, "status": job.get("status")} APP_LOGGER.info("Job {} can not be queried".format(identifier)) @@ -29,7 +29,7 @@ def queryJobStatus(self, identifier): def removeJob(self, identifier): APP_LOGGER.info("Checking if i can remove the job {}".format(identifier)) for job in self.job_list: - if job.get("id") == identifier: + if job.get("identifier") == identifier: self.job_list.remove(job) APP_LOGGER.info("Job {} removed".format(identifier)) return True @@ -40,11 +40,17 @@ def from_json(self, json_obj): super().from_json(json_obj) def add_to_job_list(self, job): - if job not in self.job_list: + if self.job_list: + if job.get("identifier", None) not in str(self.job_list): + job["status"] = JobStatus.InProgress.name + self.job_list.append(job) + APP_LOGGER.info("Job accepted!") + return True + else: + APP_LOGGER.info("Job has been already accepted!") + return False + else: job["status"] = JobStatus.InProgress.name self.job_list.append(job) APP_LOGGER.info("Job accepted!") - return True - else: - APP_LOGGER.info("Job has been already accepted!") - return False \ No newline at end of file + return True \ No newline at end of file diff --git a/ml/fml40/features/functionalities/accepts_felling_support_jobs.py b/ml/fml40/features/functionalities/accepts_felling_support_jobs.py index f521484..b7be7c5 100644 --- a/ml/fml40/features/functionalities/accepts_felling_support_jobs.py +++ b/ml/fml40/features/functionalities/accepts_felling_support_jobs.py @@ -1,6 +1,5 @@ -from modelling_language.ml40.feature.functionality.AcceptsJob import AcceptsJobs -from modelling_language.fml40.feature.property.value.document.job.FellingSupportJob import FellingSupportJob - +from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs +from ml.fml40.features.properties.values.documents.jobs.fellung_support_job import FellingSupportJob class AcceptFellingSupportJobs(AcceptsJobs): diff --git a/ml/fml40/features/functionalities/accepts_log_measurements.py b/ml/fml40/features/functionalities/accepts_log_measurements.py index 3d2ff24..b1cc6bb 100644 --- a/ml/fml40/features/functionalities/accepts_log_measurements.py +++ b/ml/fml40/features/functionalities/accepts_log_measurements.py @@ -1,5 +1,5 @@ -from modelling_language.ml40.feature.functionality import Functionality -from modelling_language.fml40.feature.property.value.document.report import LogMeasurement +from ml.ml40.features.functionalities.functionality import Functionality +from ml.fml40.features.properties.values.documents.reports.log_measurement import LogMeasurement class AcceptsLogMeasurements(Functionality): diff --git a/ml/fml40/features/functionalities/accepts_log_transportaition_jobs.py b/ml/fml40/features/functionalities/accepts_log_transportaition_jobs.py index 4c8791d..3f7ea4b 100644 --- a/ml/fml40/features/functionalities/accepts_log_transportaition_jobs.py +++ b/ml/fml40/features/functionalities/accepts_log_transportaition_jobs.py @@ -1,5 +1,5 @@ -from modelling_language.ml40.feature.functionality import AcceptsJobs -from modelling_language.fml40.feature.property.value.document.job import LogTransportationJob +from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs +from ml.fml40.features.properties.values.documents.jobs.log_transportation_job import LogTransportationJob class AcceptsLogTransportationJobs(AcceptsJobs): diff --git a/ml/fml40/features/functionalities/accepts_moisture_measurement.py b/ml/fml40/features/functionalities/accepts_moisture_measurement.py index 6970b58..81db2c4 100644 --- a/ml/fml40/features/functionalities/accepts_moisture_measurement.py +++ b/ml/fml40/features/functionalities/accepts_moisture_measurement.py @@ -1,5 +1,5 @@ -from modelling_language.ml40.feature.functionality import Functionality -from modelling_language.fml40.feature.property.value.document.report import SoilMoistureMeasurement +from ml.ml40.features.functionalities.functionality import Functionality +from ml.fml40.features.properties.values.documents.reports.soil_moisture_measurement import SoilMoistureMeasurement class AcceptsMoistureMeasurement(Functionality): diff --git a/ml/fml40/features/functionalities/accepts_move_commands.py b/ml/fml40/features/functionalities/accepts_move_commands.py index c0e3081..c0869d1 100644 --- a/ml/fml40/features/functionalities/accepts_move_commands.py +++ b/ml/fml40/features/functionalities/accepts_move_commands.py @@ -1,5 +1,4 @@ -from modelling_language.ml40.feature.functionality import Functionality - +from ml.ml40.features.functionalities.functionality import Functionality class AcceptsMoveCommands(Functionality): def __init__(self, name, ref_managing_actor): diff --git a/ml/fml40/features/functionalities/accepts_single_tree_felling_jobs.py b/ml/fml40/features/functionalities/accepts_single_tree_felling_jobs.py index 0b1ee14..fe7edba 100644 --- a/ml/fml40/features/functionalities/accepts_single_tree_felling_jobs.py +++ b/ml/fml40/features/functionalities/accepts_single_tree_felling_jobs.py @@ -1,5 +1,5 @@ -from modelling_language.ml40.feature.functionality import AcceptsJobs -from modelling_language.fml40.feature.property.value.document.job import SingleTreeFellingJob +from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs +from ml.fml40.features.properties.values.documents.jobs.single_tree_felling_job import SingleTreeFellingJob class AcceptsSingleTreeFellingJobs(AcceptsJobs): diff --git a/ml/fml40/features/functionalities/classifies_tree_species.py b/ml/fml40/features/functionalities/classifies_tree_species.py index 4c37fb1..9660143 100644 --- a/ml/fml40/features/functionalities/classifies_tree_species.py +++ b/ml/fml40/features/functionalities/classifies_tree_species.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.functionality import Functionality +from ml.ml40.features.functionalities.functionality import Functionality class ClassifiesTreeSpecies(Functionality): diff --git a/ml/fml40/features/functionalities/cuts.py b/ml/fml40/features/functionalities/cuts.py index d097042..fd37581 100644 --- a/ml/fml40/features/functionalities/cuts.py +++ b/ml/fml40/features/functionalities/cuts.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.functionality import Functionality +from ml.ml40.features.functionalities.functionality import Functionality class Cuts(Functionality): diff --git a/ml/fml40/features/functionalities/determines_passability.py b/ml/fml40/features/functionalities/determines_passability.py index f654185..176fcb8 100644 --- a/ml/fml40/features/functionalities/determines_passability.py +++ b/ml/fml40/features/functionalities/determines_passability.py @@ -1,6 +1,5 @@ -from modelling_language.ml40.feature.functionality import Functionality from datetime import date - +from ml.ml40.features.functionalities.functionality import Functionality class DeterminesPassability(Functionality): diff --git a/ml/fml40/features/functionalities/displays_health_alarms.py b/ml/fml40/features/functionalities/displays_health_alarms.py index 3cebd5f..4566e6a 100644 --- a/ml/fml40/features/functionalities/displays_health_alarms.py +++ b/ml/fml40/features/functionalities/displays_health_alarms.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.functionality import Functionality +from ml.ml40.features.functionalities.functionality import Functionality class DisplaysHealthAlarms(Functionality): diff --git a/ml/fml40/features/functionalities/evaluates_stand_attributes.py b/ml/fml40/features/functionalities/evaluates_stand_attributes.py index a020e5a..fe42c37 100644 --- a/ml/fml40/features/functionalities/evaluates_stand_attributes.py +++ b/ml/fml40/features/functionalities/evaluates_stand_attributes.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.functionality import Functionality +from ml.ml40.features.functionalities.functionality import Functionality from datetime import date diff --git a/ml/fml40/features/functionalities/fells.py b/ml/fml40/features/functionalities/fells.py index 97e6299..1c6765d 100644 --- a/ml/fml40/features/functionalities/fells.py +++ b/ml/fml40/features/functionalities/fells.py @@ -1,6 +1,7 @@ -from modelling_language.ml40.feature.functionality import Functionality -from modelling_language.fml40.feature.property.value import TreeData -from modelling_language.fml40.feature.property.value.document.job import FellingJob +from ml.ml40.features.functionalities.functionality import Functionality +from ml.fml40.features.properties.values.tree_data import TreeData +from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob + class Fells(Functionality): diff --git a/ml/fml40/features/functionalities/forest_planning_evaluation.py b/ml/fml40/features/functionalities/forest_planning_evaluation.py index a816504..bb15a4e 100644 --- a/ml/fml40/features/functionalities/forest_planning_evaluation.py +++ b/ml/fml40/features/functionalities/forest_planning_evaluation.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.functionality import Functionality +from ml.ml40.features.functionalities.functionality import Functionality class ForestPlanningEvaluation(Functionality): diff --git a/ml/fml40/features/functionalities/generates_afforestation_suggestions.py b/ml/fml40/features/functionalities/generates_afforestation_suggestions.py index 011510b..5e09775 100644 --- a/ml/fml40/features/functionalities/generates_afforestation_suggestions.py +++ b/ml/fml40/features/functionalities/generates_afforestation_suggestions.py @@ -1,5 +1,5 @@ -from modelling_language.ml40.feature.functionality import Functionality -from modelling_language.fml40.feature.property.value.document.report import AfforestationSuggestion +from ml.ml40.features.functionalities.functionality import Functionality +from ml.fml40.features.properties.values.documents.reports.afforestation_suggestion import AfforestationSuggestion class GeneratesAfforestationSuggestions(Functionality): diff --git a/ml/fml40/features/functionalities/generates_felling_suggestions.py b/ml/fml40/features/functionalities/generates_felling_suggestions.py index def2fd4..2f42b9d 100644 --- a/ml/fml40/features/functionalities/generates_felling_suggestions.py +++ b/ml/fml40/features/functionalities/generates_felling_suggestions.py @@ -1,6 +1,6 @@ -from modelling_language.ml40.feature.functionality import Functionality -from modelling_language.identifier import ID -from modelling_language.fml40.feature.property.value.document.report import FellingSupportSuggestion +from ml.ml40.features.functionalities.functionality import Functionality +from ml.identifier import ID +from ml.fml40.features.properties.values.documents.reports.felling_support_suggestion import FellingSupportSuggestion class GeneratesFellingSuggestions(Functionality): diff --git a/ml/fml40/features/functionalities/grabs.py b/ml/fml40/features/functionalities/grabs.py index 508fbf4..a969706 100644 --- a/ml/fml40/features/functionalities/grabs.py +++ b/ml/fml40/features/functionalities/grabs.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.functionality import Functionality +from ml.ml40.features.functionalities.functionality import Functionality class Grabs(Functionality): diff --git a/ml/fml40/features/functionalities/measure_wood.py b/ml/fml40/features/functionalities/measure_wood.py index 7a65a88..630a7c1 100644 --- a/ml/fml40/features/functionalities/measure_wood.py +++ b/ml/fml40/features/functionalities/measure_wood.py @@ -1,5 +1,5 @@ -from modelling_language.ml40.feature.functionality import Functionality -from modelling_language.fml40.feature.property.value.document.report import LogMeasurement +from ml.ml40.features.functionalities.functionality import Functionality +from ml.fml40.features.properties.values.documents.reports.log_measurement import LogMeasurement class MeasuresWood(Functionality): diff --git a/ml/fml40/features/functionalities/monitor_health_status.py b/ml/fml40/features/functionalities/monitor_health_status.py index 1f39415..e0484e8 100644 --- a/ml/fml40/features/functionalities/monitor_health_status.py +++ b/ml/fml40/features/functionalities/monitor_health_status.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.functionality import Functionality +from ml.ml40.features.functionalities.functionality import Functionality class MonitorsHealthStatus(Functionality): diff --git a/ml/fml40/features/functionalities/provides_tree_data.py b/ml/fml40/features/functionalities/provides_tree_data.py index 5c6e29c..d7fcf8c 100644 --- a/ml/fml40/features/functionalities/provides_tree_data.py +++ b/ml/fml40/features/functionalities/provides_tree_data.py @@ -1,7 +1,7 @@ -from modelling_language.ml40.feature.functionality import Functionality -from modelling_language.ml40.feature.property.value import Location -from modelling_language.identifier import ID -from modelling_language.fml40.feature.property.value import TreeData +from ml.ml40.features.functionalities.functionality import Functionality +from ml.ml40.features.properties.values.location import Location +from ml.identifier import ID +from ml.fml40.features.properties.values.tree_data import TreeData class ProvidesTreeData(Functionality): diff --git a/ml/fml40/features/functionalities/provides_weather_data.py b/ml/fml40/features/functionalities/provides_weather_data.py index df6eddd..3c1774a 100644 --- a/ml/fml40/features/functionalities/provides_weather_data.py +++ b/ml/fml40/features/functionalities/provides_weather_data.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.functionality import Functionality +from ml.ml40.features.functionalities.functionality import Functionality class ProvidesWeatherData(Functionality): diff --git a/ml/fml40/features/functionalities/simulates_tree_growth.py b/ml/fml40/features/functionalities/simulates_tree_growth.py index 9cd51f9..e34edee 100644 --- a/ml/fml40/features/functionalities/simulates_tree_growth.py +++ b/ml/fml40/features/functionalities/simulates_tree_growth.py @@ -1,4 +1,4 @@ -from modelling_language.ml40.feature.functionality import Functionality +from ml.ml40.features.functionalities.functionality import Functionality class SimulatesTreeGrowth(Functionality): diff --git a/ml/fml40/features/functionalities/supports_felling.py b/ml/fml40/features/functionalities/supports_felling.py index f713d7a..2efde1e 100644 --- a/ml/fml40/features/functionalities/supports_felling.py +++ b/ml/fml40/features/functionalities/supports_felling.py @@ -1,6 +1,6 @@ -from modelling_language.ml40.feature.functionality import Functionality -from modelling_language.fml40.feature.property.value.document.job import FellingJob -from modelling_language.fml40.feature.property.value.document.report import FellingSupportSuggestion +from ml.ml40.features.functionalities.functionality import Functionality +from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob +from ml.fml40.features.properties.values.documents.reports.felling_support_suggestion import FellingSupportSuggestion class SupportsFelling(Functionality): diff --git a/ml/fml40/features/functionalities/transports_logs.py b/ml/fml40/features/functionalities/transports_logs.py index 17c3539..5004493 100644 --- a/ml/fml40/features/functionalities/transports_logs.py +++ b/ml/fml40/features/functionalities/transports_logs.py @@ -1,6 +1,6 @@ -from modelling_language.ml40.feature.functionality import Functionality -from modelling_language.fml40.feature.property.value.document.job import LogTransportationJob -from modelling_language.fml40.feature.property.value.document.report import LogTransportationReport +from ml.ml40.features.functionalities.functionality import Functionality +from ml.fml40.features.properties.values.documents.jobs.log_transportation_job import LogTransportationJob +from ml.fml40.features.properties.values.documents.reports.log_transportation_report import LogTransportationReport class TransportsLogs(Functionality): diff --git a/ml/fml40/features/properties/values/assortment.py b/ml/fml40/features/properties/values/assortment.py new file mode 100644 index 0000000..74ff5ef --- /dev/null +++ b/ml/fml40/features/properties/values/assortment.py @@ -0,0 +1,27 @@ +from ml.ml40.features.properties.values.value import Value + + +class Assortment(Value): + def __init__(self, name, ref_managing_actor, grade=""): + super(Assortment, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.__grade = grade + + @property + def grade(self): + return self.__grade + + @grade.setter + def grade(self, value): + self.__grade = value + + def to_json(self): + temp_dict = super().to_json() + if self.grade: + _dict = temp_dict.copy() + _dict.update({ + "grade": self.grade, + }) + return _dict + else: + return temp_dict diff --git a/ml/fml40/features/properties/values/dbh.py b/ml/fml40/features/properties/values/dbh.py index f22c883..8151ce9 100644 --- a/ml/fml40/features/properties/values/dbh.py +++ b/ml/fml40/features/properties/values/dbh.py @@ -2,7 +2,7 @@ class DBH(Value): - def __init__(self, name, ref_managing_actor, dbh: float): + def __init__(self, name, ref_managing_actor, dbh=0): super(DBH, self).__init__(name=name, ref_managing_actor=ref_managing_actor) self.__dbh = dbh @@ -13,4 +13,15 @@ def dbh(self): @dbh.setter def dbh(self, value): - self.__dbh = value \ No newline at end of file + self.__dbh = value + + def to_json(self): + temp_dict = super().to_json() + if self.dbh: + _dict = temp_dict.copy() + _dict.update({ + "dbh": self.dbh, + }) + return _dict + else: + return temp_dict \ No newline at end of file diff --git a/ml/fml40/features/properties/values/documents/reports/map_data.py b/ml/fml40/features/properties/values/documents/reports/map_data.py index 2f92acd..5e8094a 100644 --- a/ml/fml40/features/properties/values/documents/reports/map_data.py +++ b/ml/fml40/features/properties/values/documents/reports/map_data.py @@ -1,5 +1,4 @@ -from modelling_language.ml40.feature.property.value.document.report import Report - +from ml.ml40.features.properties.values.documents.reports.report import Report class MapData(Report): def __init__(self, name, ref_managing_actor): diff --git a/ml/fml40/features/properties/values/harvested_volume.py b/ml/fml40/features/properties/values/harvested_volume.py new file mode 100644 index 0000000..aabc29a --- /dev/null +++ b/ml/fml40/features/properties/values/harvested_volume.py @@ -0,0 +1,27 @@ +from ml.ml40.features.properties.values.value import Value + + +class HarvestedVolume(Value): + def __init__(self, name, ref_managing_actor, volume=0): + super(HarvestedVolume, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.__volume = volume + + @property + def volume(self): + return self.__volume + + @volume.setter + def volume(self, value): + self.__volume = value + + def to_json(self): + temp_dict = super().to_json() + if self.volume: + _dict = temp_dict.copy() + _dict.update({ + "volume": self.__volume, + }) + return _dict + else: + return temp_dict diff --git a/ml/fml40/features/properties/values/harvesting_parameter.py b/ml/fml40/features/properties/values/harvesting_parameter.py new file mode 100644 index 0000000..5cc67b4 --- /dev/null +++ b/ml/fml40/features/properties/values/harvesting_parameter.py @@ -0,0 +1,27 @@ +from ml.ml40.features.properties.values.value import Value + + +class HarvestingParameters(Value): + def __init__(self, name, ref_managing_actor, cuttingLengths=0): + super(HarvestingParameters, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + self.__cuttingLengths = cuttingLengths + + @property + def cuttingLengths(self): + return self.__cuttingLengths + + @cuttingLengths.setter + def cuttingLengths(self, value): + self.__cuttingLengths = value + + def to_json(self): + temp_dict = super().to_json() + if self.cuttingLengths: + _dict = temp_dict.copy() + _dict.update({ + "cuttingLengths": self.__cuttingLengths, + }) + return _dict + else: + return temp_dict diff --git a/ml/fml40/features/properties/values/thickness_class.py b/ml/fml40/features/properties/values/thickness_class.py new file mode 100644 index 0000000..f5660a4 --- /dev/null +++ b/ml/fml40/features/properties/values/thickness_class.py @@ -0,0 +1,7 @@ +from ml.ml40.features.properties.values.value import Value + + +class ThicknessClass(Value): + def __init__(self, name, ref_managing_actor): + super(ThicknessClass, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) diff --git a/ml/fml40/features/properties/values/tree_type.py b/ml/fml40/features/properties/values/tree_type.py index d6cdaa4..8c25c93 100644 --- a/ml/fml40/features/properties/values/tree_type.py +++ b/ml/fml40/features/properties/values/tree_type.py @@ -2,7 +2,7 @@ class TreeType(Value): - def __init__(self, name, ref_managing_actor, conifer: bool): + def __init__(self, name, ref_managing_actor, conifer=False): super(TreeType, self).__init__(name=name, ref_managing_actor=ref_managing_actor) self.__conifer = conifer diff --git a/ml/fml40/features/properties/values/wood_quality.py b/ml/fml40/features/properties/values/wood_quality.py new file mode 100644 index 0000000..208dac7 --- /dev/null +++ b/ml/fml40/features/properties/values/wood_quality.py @@ -0,0 +1,8 @@ +from ml.ml40.features.properties.values.value import Value + + +class WoodQuality(Value): + def __init__(self, name, ref_managing_actor): + super(WoodQuality, self).__init__(name=name, + ref_managing_actor=ref_managing_actor) + diff --git a/ml/managed_actor.py b/ml/managed_actor.py index 28bfe6a..9cffab2 100644 --- a/ml/managed_actor.py +++ b/ml/managed_actor.py @@ -16,6 +16,7 @@ def __init__(self, name, ref_managing_actor): self.managing_actor = ref_managing_actor.proxy() self.managing_actor.register_managed_actor(name, self.actor_ref.proxy()) self.__name = name + self.__class_name = name self.__identifier = ID(identifier="s3i:{}".format(uuid.uuid4())).ID @property @@ -26,6 +27,14 @@ def name(self): def name(self, value): self.__name = value + @property + def class_name(self): + return self.__class_name + + @class_name.setter + def class_name(self, value): + self.__class_name = value + @property def identifier(self): return self.__identifier @@ -36,10 +45,14 @@ def identifier(self, value): self.__identifier = value def to_json(self): - return { - "class": self.name, + json_out = { + "class": self.class_name, "identifier": self.identifier, } + if self.class_name != self.name: + json_out["name"] = self.name + + return json_out def from_json(self, json_obj): self.__name = json_obj.get("name", "") diff --git a/ml/ml40/features/feature.py b/ml/ml40/features/feature.py index 39bde64..092837a 100644 --- a/ml/ml40/features/feature.py +++ b/ml/ml40/features/feature.py @@ -1,32 +1,29 @@ from ml.managed_actor import ManagedActor -from ml.identifier import ID -import uuid class Feature(ManagedActor): - def __init__(self, name, ref_managing_actor): + def __init__(self, name, ref_managing_actor, subFeatures={}): super(Feature, self).__init__(name, ref_managing_actor) - self.__subfeatures = [] + self.__subFeatures = subFeatures @property - def subfeatures(self): - return self.__subfeatures + def subFeatures(self): + return self.__subFeatures - @subfeatures.setter - def subfeatures(self, value): - self.__subfeatures = value + @subFeatures.setter + def subFeatures(self, value): + self.__subFeatures = value def to_json(self): temp_dict = super().to_json() - if self.subfeatures: - _dict = temp_dict.copy() - _dict.update({ - "subfeatures": self.subfeatures, - }) - return _dict - else: - return temp_dict + #print(temp_dict) + if self.subFeatures: + temp_dict["subFeatures"] = [] + for key in self.subFeatures.keys(): + temp_dict["subFeatures"].append(self.subFeatures[key].proxy().to_json().get()) + return temp_dict def from_json(self, json_obj): self.__name = json_obj.get("name", "") self.__identifier = json_obj.get("identifier", "") + diff --git a/ml/ml40/features/functionalities/accepts_reports.py b/ml/ml40/features/functionalities/accepts_reports.py index 62e84c6..20d69fc 100644 --- a/ml/ml40/features/functionalities/accepts_reports.py +++ b/ml/ml40/features/functionalities/accepts_reports.py @@ -1,6 +1,6 @@ -from ml.ml40.features.functionalities import Functionality -from ml.ml40.features.properties.document.report import Report +from ml.ml40.features.functionalities.functionality import Functionality +from ml.ml40.features.properties.values.documents.reports.report import Report class AcceptsReports(Functionality): def __init__(self, name, ref_managing_actor): diff --git a/ml/ml40/features/functionalities/clears_jobs.py b/ml/ml40/features/functionalities/clears_jobs.py index 0f60ca2..34160ee 100644 --- a/ml/ml40/features/functionalities/clears_jobs.py +++ b/ml/ml40/features/functionalities/clears_jobs.py @@ -1,5 +1,5 @@ -from ml.ml40.features.functionality import Functionality -from ml.ml40.features.properties.value.document.job import Job +from ml.ml40.features.functionalities.functionality import Functionality +from ml.ml40.features.properties.values.documents.jobs.job import Job class ClearsJobs(Functionality): diff --git a/ml/ml40/features/functionalities/plans_routes.py b/ml/ml40/features/functionalities/plans_routes.py index 66cda73..354925a 100644 --- a/ml/ml40/features/functionalities/plans_routes.py +++ b/ml/ml40/features/functionalities/plans_routes.py @@ -1,6 +1,6 @@ -from ml.ml40.features.functionalities import Functionality -from ml.ml40.features.properties import Route, Location - +from ml.ml40.features.functionalities.functionality import Functionality +from ml.ml40.features.properties.values.route import Route +from ml.ml40.features.properties.values.location import Location class PlansRoutes(Functionality): def __init__(self, name, ref_managing_actor): diff --git a/ml/ml40/features/functionalities/provides_map_data.py b/ml/ml40/features/functionalities/provides_map_data.py index adf3ef9..c4a9038 100644 --- a/ml/ml40/features/functionalities/provides_map_data.py +++ b/ml/ml40/features/functionalities/provides_map_data.py @@ -1,6 +1,5 @@ -from ml.ml40.features.functionality import Functionality -from ml.fml40.features.properties.value.document.report import PassabilityReport - +from ml.ml40.features.functionalities.functionality import Functionality +from ml.fml40.features.properties.values.documents.reports.passability_report import PassabilityReport class ProvidesMapData(Functionality): def __init__(self, name, ref_managing_actor): diff --git a/ml/ml40/features/functionalities/provides_operational_data.py b/ml/ml40/features/functionalities/provides_operational_data.py index 5f56bcc..f98dd4b 100644 --- a/ml/ml40/features/functionalities/provides_operational_data.py +++ b/ml/ml40/features/functionalities/provides_operational_data.py @@ -1,5 +1,5 @@ -from ml.ml40.features.functionalities import Functionality -from ml.ml40.features.properties.value.document.report import Report +from ml.ml40.features.functionalities.functionality import Functionality +from ml.ml40.features.properties.values.documents.reports.report import Report class ProvidesOperationalData(Functionality): diff --git a/ml/ml40/features/functionalities/renders.py b/ml/ml40/features/functionalities/renders.py index ac00fc1..94d66c2 100644 --- a/ml/ml40/features/functionalities/renders.py +++ b/ml/ml40/features/functionalities/renders.py @@ -1,4 +1,4 @@ -from ml.ml40.features.functionalies import Functionality +from ml.ml40.features.functionalities.functionality import Functionality from ml.identifier import ID diff --git a/ml/ml40/features/properties/composite.py b/ml/ml40/features/properties/composite.py index de278d8..2a3a461 100644 --- a/ml/ml40/features/properties/composite.py +++ b/ml/ml40/features/properties/composite.py @@ -6,17 +6,26 @@ 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 subfeatures self.__targets = [] @property - def target(self): + def targets(self): return self.__targets - @target.setter - def target(self, value): + @targets.setter + def targets(self, value): self.__targets = value + def to_json(self): + temp_dict = super().to_json() + if self.targets: + + temp_dict["targets"] = [] + for target in self.targets: + temp_dict["targets"].append(target.proxy().model.get()["attributes"]) + + return temp_dict + def from_json(self, json_obj): super().from_json(json_obj) self.__targets = json_obj.get("targets", []) diff --git a/ml/ml40/features/properties/values/location.py b/ml/ml40/features/properties/values/location.py index 7c1d8b2..e611c5b 100644 --- a/ml/ml40/features/properties/values/location.py +++ b/ml/ml40/features/properties/values/location.py @@ -2,11 +2,35 @@ class Location(Value): - def __init__(self, name, ref_managing_actor): + def __init__(self, name, ref_managing_actor, longitude=0, latitude=0, orientation=0): super(Location, self).__init__(name=name, ref_managing_actor=ref_managing_actor) - self.__latitude = 0 - self.__longitude = 0 - self.__orientation = 0 + self.__latitude = latitude + self.__longitude = longitude + self.__orientation = orientation + + @property + def latitude(self): + return self.__latitude + + @latitude.setter + def latitude(self, value): + self.__latitude = value + + @property + def longitude(self): + return self.__longitude + + @longitude.setter + def longitude(self, value): + self.__longitude = value + + @property + def orientation(self): + return self.__orientation + + @orientation.setter + def orientation(self, value): + self.__orientation = value def from_json(self, json_obj): super().from_json(json_obj) diff --git a/ml/ml40/features/properties/values/temperature.py b/ml/ml40/features/properties/values/temperature.py index 539d980..b6a52c7 100644 --- a/ml/ml40/features/properties/values/temperature.py +++ b/ml/ml40/features/properties/values/temperature.py @@ -2,7 +2,7 @@ class Temperature(Value): - def __init__(self, name, ref_managing_actor, temperature: float): + def __init__(self, name, ref_managing_actor, temperature=0): super(Temperature, self).__init__(name=name, ref_managing_actor=ref_managing_actor) self.__temperature = temperature @@ -13,7 +13,7 @@ def temperature(self): @temperature.setter def temperature(self, value): - if isinstance(value, float): + if isinstance(value, (float, int)): self.__temperature = value else: raise TypeError diff --git a/ml/thing.py b/ml/thing.py index 4e61a25..7681d57 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -7,6 +7,7 @@ from s3i import IdentityProvider, TokenType, GetValueReply, Directory from s3i.broker import Broker, BrokerREST, BrokerMetaClass from s3i.messages import ServiceReply +from ml.tools import remove_namespace from ml.managing_actor import ManagingActor from ml.tools import BColors @@ -26,16 +27,17 @@ class BaseVariable(object): class Thing(ManagingActor): def __init__( self, - client_secret, model: dict, - grant_type: str, - is_broker: bool, - is_repo: bool, + client_secret="", + grant_type="password", + is_broker=False, + is_repo=False, is_broker_rest=True, username=None, password=None, ): super(Thing, self).__init__() + self.__model = model self.__thing_id = model.get("thingId", "") self.__policy_id = model.get("policyId", "") self.__grant_type = grant_type @@ -58,15 +60,15 @@ def __init__( attributes = model.get("attributes", None) self.__name = "" - self.__class_name = "" - self.__type_name = "" self.__roles = {} self.__features = {} if attributes: self.__name = attributes.get("name", "") - self.__class_name = attributes.get("class", "") - self.__type_name = attributes.get("type", "") + + @property + def model(self): + return self.__model @property def features(self): @@ -104,6 +106,10 @@ def name(self): def thing_id(self): return self.__thing_id + @property + def policy_id(self): + return self.__policy_id + def run_forever(self): print("[S³I]: Launch {}{}{}".format(BColors.OKGREEN, self.name, BColors.ENDC)) self.__connect_with_idp() @@ -256,32 +262,8 @@ def on_user_message(self, msg): pass def on_get_value_request(self, msg): - get_value_reply = GetValueReply() - request_sender = msg.get("sender", "") - request_msg_id = msg.get("identifier") - request_sender_endpoint = msg.get("replyToEndpoint") - request_sender_endpoint = f"s3ib://{request_sender_endpoint}" - attribute_path = msg.get("attributePath") - reply_msg_uuid = "s3i:" + str(uuid.uuid4()) - # TODO: Get correct value. - value = None - - if attribute_path == "features/moisture": - moisture = self.features.get("ml40::Moisture", None) - moisture_proxy = moisture.proxy() - value = moisture_proxy.humidity.get() - - get_value_reply.fillGetValueReply( - senderUUID=self.__thing_id, - receiverUUID=[request_sender], - results=value, - msgUUID=reply_msg_uuid, - replyingToUUID=request_msg_id, - ) - msg_txt = get_value_reply.msg.__str__() - send_resp = self.broker.send( - receiver_endpoints=[request_sender_endpoint], msg=msg_txt, - ) + # TODO implement get value request + pass def on_service_request(self, body_json): service_type = body_json.get("serviceType") diff --git a/ml/tools.py b/ml/tools.py index 8c0a76c..84dab0a 100644 --- a/ml/tools.py +++ b/ml/tools.py @@ -299,62 +299,39 @@ def make_sub_thing(name, roles, id="", features=[]): sub_thing = { "class": "ml40::Thing", "name": name, - "roles": [], - "features": [] + "roles": roles, + "features": 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=[]): +def make_feature_config(class_name, identifier="", name="", subFeatures=""): + config_json = { + "class": class_name + } + if identifier: + config_json["identifier"] = identifier + if name: + config_json["name"] = name + if subFeatures: + config_json["subFeatures"] = subFeatures + + return config_json + + +def make_thing_config(dt_id, name, roles, features=[]): config_file = { "thingId": dt_id, "policyId": dt_id, "attributes": { "class": "ml40::Thing", "name": name, - "roles": [ - ], - "features": [ - ] + "roles": roles, + "features": features, } } - for role in roles: - config_file["attributes"]["roles"].append( - { - "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: @@ -418,3 +395,7 @@ def get_requests(config): receiver_uuids.append(target) requests.append((msg_type, receiver_uuids, parameters)) return requests + + +def remove_namespace(input_str): + return input_str.replace("fml40::", "").replace("ml40::", "") -- GitLab From 94c8432ac81761956b98469ace7dc98ec79330c9 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Thu, 8 Oct 2020 23:31:10 +0200 Subject: [PATCH 44/84] update on_service_reply --- ml/thing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ml/thing.py b/ml/thing.py index 7681d57..b2d651d 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -307,7 +307,7 @@ def on_get_value_reply(self, msg): print(msg) def on_service_reply(self, msg): - print(msg) + print(BColors.OKBLUE + "[S³I]: You have received a service reply: " + BColors.ENDC + json.dumps(msg, indent=2)) def add_function_impl(self, impl_actor, feature_name): feature = self.__features.get(feature_name, None) -- GitLab From dfa66f33309d03c18ed2651c091c1ed8c363aad0 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Fri, 9 Oct 2020 08:07:37 +0200 Subject: [PATCH 45/84] add timer --- ml/thing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ml/thing.py b/ml/thing.py index b2d651d..4a8cdda 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -8,7 +8,7 @@ from s3i.broker import Broker, BrokerREST, BrokerMetaClass from s3i.messages import ServiceReply from ml.tools import remove_namespace - +import time from ml.managing_actor import ManagingActor from ml.tools import BColors from ml.tools import find_broker_endpoint @@ -128,6 +128,7 @@ def __connect_with_idp(self): # This may take a while so fetch token directly. idp.run_forever(token_type=TokenType.ACCESS_TOKEN, on_new_token=self.__on_token) + time.sleep(1) def __on_token(self, token): self.__access_token = token -- GitLab From f6e29bc296f1d43a48d2e2365dece2471379626d Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 26 Oct 2020 16:31:45 +0100 Subject: [PATCH 46/84] rewritten ml package --- configs/config_my_dt_harvester.json | 2 +- dt_creation.py | 107 +++++-- dt_creation_hmi.py | 109 +++---- ml/dt_factory.py | 258 ++++++----------- ml/feature.py | 58 ++++ .../functionalities/accepts_felling_jobs.py | 54 +--- .../accepts_felling_support_jobs.py | 5 + .../accepts_forwarding_jobs.py | 16 +- .../accepts_log_measurements.py | 4 + .../accepts_log_transportaition_jobs.py | 4 + .../accepts_moisture_measurement.py | 5 + .../functionalities/accepts_move_commands.py | 6 +- .../accepts_passability_report.py | 16 +- .../accepts_proximity_alert.py | 13 +- .../accepts_single_tree_felling_jobs.py | 8 +- .../classifies_tree_species.py | 5 + ml/fml40/features/functionalities/cuts.py | 5 + .../functionalities/determines_passability.py | 5 + .../functionalities/displays_health_alarms.py | 4 + .../evaluates_stand_attributes.py | 5 + ml/fml40/features/functionalities/fells.py | 7 +- .../forest_planning_evaluation.py | 5 + ml/fml40/features/functionalities/forwards.py | 16 +- .../generates_afforestation_suggestions.py | 4 + .../generates_felling_suggestions.py | 4 + ml/fml40/features/functionalities/grabs.py | 6 +- ml/fml40/features/functionalities/harvests.py | 16 +- .../features/functionalities/measure_wood.py | 6 +- .../functionalities/monitor_health_status.py | 4 + .../provides_moisture_prediction.py | 15 +- .../provides_passability_information.py | 13 +- .../provides_production_data.py | 7 +- .../functionalities/provides_tree_data.py | 4 + .../functionalities/provides_weather_data.py | 6 +- .../functionalities/simulates_tree_growth.py | 7 +- .../functionalities/supports_felling.py | 4 + .../functionalities/transports_logs.py | 6 +- .../properties/values/abstract_inventory.py | 5 +- .../features/properties/values/assortment.py | 24 +- ml/fml40/features/properties/values/dbh.py | 23 +- .../values/documents/jobs/felling_job.py | 10 +- .../properties/values/harvested_volume.py | 24 +- .../properties/values/harvesting_parameter.py | 23 +- .../properties/values/inventory_data.py | 20 +- .../values/stem_segment_properties.py | 34 ++- .../properties/values/thickness_class.py | 10 +- ml/fml40/features/properties/values/tilt.py | 23 +- .../features/properties/values/tree_data.py | 7 +- .../features/properties/values/tree_type.py | 16 +- .../properties/values/wood_quality.py | 7 +- ml/fml40/machines/forwarder.py | 52 ---- ml/fml40/machines/harvester.py | 32 --- .../roles/dts/handheld_devices/brushcutter.py | 5 +- .../roles/dts/handheld_devices/chainsaw.py | 5 +- ml/fml40/roles/dts/machines/forest_machine.py | 7 +- ml/fml40/roles/dts/machines/forwarder.py | 5 +- ml/fml40/roles/dts/machines/harvester.py | 7 +- ml/fml40/roles/dts/machines/log_truck.py | 5 +- ml/fml40/roles/dts/machines/mini_tractor.py | 5 +- ml/fml40/roles/dts/machines/skidder.py | 5 +- ml/fml40/roles/dts/machines/wheel_loader.py | 5 +- ml/fml40/roles/dts/parts/grabber.py | 5 +- ml/fml40/roles/dts/parts/harvesting_head.py | 5 +- ml/fml40/roles/dts/parts/log_loading_area.py | 5 +- ml/fml40/roles/dts/parts/saw.py | 5 +- ml/fml40/roles/dts/parts/winch.py | 5 +- ml/fml40/roles/dts/persons/forest_owner.py | 5 +- ml/fml40/roles/dts/persons/forest_worker.py | 5 +- .../dts/persons/mini_tractor_operator.py | 5 +- .../roles/dts/persons/skidder_operator.py | 5 +- ml/fml40/roles/dts/sensors/vitality_sensor.py | 5 +- ml/fml40/roles/dts/sites/forest_enterprise.py | 5 +- ml/fml40/roles/dts/sites/hauler.py | 5 +- ml/fml40/roles/dts/sites/mill/mill.py | 5 +- ml/fml40/roles/dts/sites/mill/papermill.py | 5 +- ml/fml40/roles/dts/sites/mill/sawmill.py | 5 +- ml/fml40_factory.py | 4 - ml/identifier.py | 31 +- ml/ml40/features/feature.py | 29 -- .../features/functionalities/accepts_jobs.py | 9 +- .../functionalities/accepts_reports.py | 10 +- .../features/functionalities/clears_jobs.py | 8 +- .../features/functionalities/functionality.py | 34 +-- .../features/functionalities/manages_jobs.py | 13 +- .../features/functionalities/plans_routes.py | 8 +- .../functionalities/provides_map_data.py | 8 +- .../provides_operational_data.py | 11 +- ml/ml40/features/functionalities/renders.py | 6 +- ml/ml40/features/properties/association.py | 6 - .../properties/associations/__init__.py | 0 .../properties/associations/association.py | 7 + .../properties/associations/composite.py | 27 ++ .../properties/associations/shared.py | 27 ++ ml/ml40/features/properties/composite.py | 31 -- ml/ml40/features/properties/property.py | 9 +- ml/ml40/features/properties/shared.py | 12 - ml/ml40/features/properties/values/address.py | 35 ++- .../features/properties/values/dimensions.py | 31 +- .../properties/values/documents/document.py | 7 +- .../values/documents/jobs/generic_job.py | 26 +- .../properties/values/documents/jobs/job.py | 24 +- .../values/documents/jobs/job_list.py | 16 +- .../properties/values/generic_property.py | 19 +- .../properties/values/liquid_filling_level.py | 23 +- .../features/properties/values/location.py | 28 +- .../features/properties/values/moisture.py | 18 +- .../properties/values/personal_name.py | 24 +- .../properties/values/rotational_speed.py | 17 +- ml/ml40/features/properties/values/route.py | 5 +- .../features/properties/values/temperature.py | 16 +- .../features/properties/values/time_slot.py | 21 +- ml/ml40/features/properties/values/value.py | 39 ++- ml/ml40/features/properties/values/weight.py | 26 +- ml/ml40/roles/dts/dt.py | 12 +- .../dts/handheld_devices/handheld_device.py | 5 +- ml/ml40/roles/dts/machines/machine.py | 8 +- ml/ml40/roles/dts/parts/crane.py | 5 +- ml/ml40/roles/dts/parts/engine.py | 5 +- ml/ml40/roles/dts/parts/part.py | 7 +- ml/ml40/roles/dts/parts/scale.py | 5 +- ml/ml40/roles/dts/parts/tank.py | 5 +- ml/ml40/roles/dts/persons/machine_operator.py | 5 +- ml/ml40/roles/dts/persons/person.py | 5 +- ml/ml40/roles/dts/sensors/air_sensor.py | 5 +- ml/ml40/roles/dts/sensors/sensor.py | 5 +- ml/ml40/roles/dts/sensors/soil_sensor.py | 5 +- ml/ml40/roles/dts/sites/site.py | 5 +- ml/ml40/roles/dts/ways/way.py | 5 +- ml/ml40/roles/hmis/app.py | 4 +- ml/ml40/roles/hmis/dashboard.py | 4 +- ml/ml40/roles/hmis/hmd.py | 4 +- ml/ml40/roles/hmis/hmi.py | 9 +- ml/ml40/roles/hmis/machine_ui.py | 7 +- ml/ml40/roles/role.py | 16 -- ml/ml40/roles/servives/service.py | 8 +- ml/role.py | 41 +++ ml/thing.py | 272 +++++++++++++++--- requirements.txt | 2 +- tests/test_benedict.py | 20 ++ tests/test_threading_futhur.py | 10 + 140 files changed, 1432 insertions(+), 970 deletions(-) create mode 100644 ml/feature.py delete mode 100644 ml/fml40/machines/forwarder.py delete mode 100644 ml/fml40/machines/harvester.py delete mode 100644 ml/fml40_factory.py delete mode 100644 ml/ml40/features/feature.py delete mode 100644 ml/ml40/features/properties/association.py create mode 100644 ml/ml40/features/properties/associations/__init__.py create mode 100644 ml/ml40/features/properties/associations/association.py create mode 100644 ml/ml40/features/properties/associations/composite.py create mode 100644 ml/ml40/features/properties/associations/shared.py delete mode 100644 ml/ml40/features/properties/composite.py delete mode 100644 ml/ml40/features/properties/shared.py delete mode 100644 ml/ml40/roles/role.py create mode 100644 ml/role.py create mode 100644 tests/test_benedict.py create mode 100644 tests/test_threading_futhur.py diff --git a/configs/config_my_dt_harvester.json b/configs/config_my_dt_harvester.json index 3c2bcae..48b2009 100644 --- a/configs/config_my_dt_harvester.json +++ b/configs/config_my_dt_harvester.json @@ -1 +1 @@ -{"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 diff --git a/dt_creation.py b/dt_creation.py index 49083f7..478d65c 100644 --- a/dt_creation.py +++ b/dt_creation.py @@ -1,11 +1,11 @@ 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) diff --git a/dt_creation_hmi.py b/dt_creation_hmi.py index 43d2a98..02d96ca 100644 --- a/dt_creation_hmi.py +++ b/dt_creation_hmi.py @@ -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)) diff --git a/ml/dt_factory.py b/ml/dt_factory.py index a31118b..4a6d779 100644 --- a/ml/dt_factory.py +++ b/ml/dt_factory.py @@ -1,13 +1,27 @@ """ 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, diff --git a/ml/feature.py b/ml/feature.py new file mode 100644 index 0000000..0fd8644 --- /dev/null +++ b/ml/feature.py @@ -0,0 +1,58 @@ +from ml.identifier import ID +import sys +from abc import ABC + + +class Feature(ABC): + def __init__(self, name="", identifier=""): + self.__name = name + if "ml/fml40" in sys.modules[self.__class__.__module__].__file__: + self.__class_name = "fml40::{}".format(self.__class__.__name__) + else: + self.__class_name = "ml40::{}".format(self.__class__.__name__) + self.__identifier = ID(identifier).identifier + self.__subFeatures = dict() + self.__json_out = dict() + + @property + def class_name(self): + return self.__class_name + + @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): + self.__identifier = ID(value).identifier + + + @property + def subFeatures(self): + return self.__subFeatures + + @subFeatures.setter + def subFeatures(self, value): + self.__subFeatures = value + + def to_json(self): + self.__json_out = { + "class": self.class_name, + "identifier": self.identifier, + } + if self.name: + self.__json_out["name"] = self.name + if self.subFeatures: + self.__json_out["subFeatures"] = list() + for key in self.subFeatures.keys(): + self.__json_out["subFeatures"].append(self.subFeatures[key].to_json()) + return self.__json_out + diff --git a/ml/fml40/features/functionalities/accepts_felling_jobs.py b/ml/fml40/features/functionalities/accepts_felling_jobs.py index d2f4c6b..976e196 100644 --- a/ml/fml40/features/functionalities/accepts_felling_jobs.py +++ b/ml/fml40/features/functionalities/accepts_felling_jobs.py @@ -1,56 +1,20 @@ from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs from ml.ml40.features.properties.values.documents.jobs.job_status import JobStatus from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob -from ml.app_logger import APP_LOGGER class AcceptsFellingJobs(AcceptsJobs): - def __init__(self, name, ref_managing_actor): - super(AcceptsFellingJobs, self).__init__( - name=name, ref_managing_actor=ref_managing_actor - ) - self.job_list = [] + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def acceptJob(self, job: FellingJob) -> bool: - APP_LOGGER.info("Checking if the felling job can be accepted.") - if job.get("identifier") is None or job.get("status") is None: - return False - return self.add_to_job_list(job) + pass - def queryJobStatus(self, identifier): - APP_LOGGER.info("Checking the job status of job {}".format(identifier)) - for job in self.job_list: - if job.get("identifier") == identifier: - APP_LOGGER.info("Job {} is now in status {}".format(identifier, job.get("status"))) - return {"identifier": identifier, "status": job.get("status")} - APP_LOGGER.info("Job {} can not be queried".format(identifier)) - return {"identifier": identifier, "status": "NOT FOUND"} + def queryJobStatus(self, identifier) -> JobStatus: + pass - def removeJob(self, identifier): - APP_LOGGER.info("Checking if i can remove the job {}".format(identifier)) - for job in self.job_list: - if job.get("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 removeJob(self, identifier) -> bool: + pass - def from_json(self, json_obj): - super().from_json(json_obj) - - def add_to_job_list(self, job): - if self.job_list: - if job.get("identifier", None) not in str(self.job_list): - job["status"] = JobStatus.InProgress.name - self.job_list.append(job) - APP_LOGGER.info("Job accepted!") - return True - else: - APP_LOGGER.info("Job has been already accepted!") - return False - else: - job["status"] = JobStatus.InProgress.name - self.job_list.append(job) - APP_LOGGER.info("Job accepted!") - return True \ No newline at end of file diff --git a/ml/fml40/features/functionalities/accepts_felling_support_jobs.py b/ml/fml40/features/functionalities/accepts_felling_support_jobs.py index b7be7c5..d0e065a 100644 --- a/ml/fml40/features/functionalities/accepts_felling_support_jobs.py +++ b/ml/fml40/features/functionalities/accepts_felling_support_jobs.py @@ -1,7 +1,12 @@ from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs from ml.fml40.features.properties.values.documents.jobs.fellung_support_job import FellingSupportJob + class AcceptFellingSupportJobs(AcceptsJobs): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def acceptJob(self, job: FellingSupportJob) -> bool: pass diff --git a/ml/fml40/features/functionalities/accepts_forwarding_jobs.py b/ml/fml40/features/functionalities/accepts_forwarding_jobs.py index 7cd46e8..49be368 100644 --- a/ml/fml40/features/functionalities/accepts_forwarding_jobs.py +++ b/ml/fml40/features/functionalities/accepts_forwarding_jobs.py @@ -6,16 +6,12 @@ class AcceptsForwardingJobs(AcceptsJobs): - def __init__(self, name, ref_managing_actor): - super().__init__(ref_managing_actor=ref_managing_actor, name=name) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def acceptJob(self, job: ForwardingJob) -> bool: - APP_LOGGER.info("I check if I (the Komatsu Forwarder) can accept the job.") - proxy_executer = self.managing_actor.proxy_functionalities.get().get( - "fml40::Forwards", None - ) - proxy_executer.executeJob(job) - return True + pass + - def from_json(self, json_obj): - super().from_json(json_obj) diff --git a/ml/fml40/features/functionalities/accepts_log_measurements.py b/ml/fml40/features/functionalities/accepts_log_measurements.py index b1cc6bb..10a5a14 100644 --- a/ml/fml40/features/functionalities/accepts_log_measurements.py +++ b/ml/fml40/features/functionalities/accepts_log_measurements.py @@ -3,6 +3,10 @@ class AcceptsLogMeasurements(Functionality): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def acceptLogMeasurement(self, log_measurement: LogMeasurement) -> bool: pass diff --git a/ml/fml40/features/functionalities/accepts_log_transportaition_jobs.py b/ml/fml40/features/functionalities/accepts_log_transportaition_jobs.py index 3f7ea4b..86e9960 100644 --- a/ml/fml40/features/functionalities/accepts_log_transportaition_jobs.py +++ b/ml/fml40/features/functionalities/accepts_log_transportaition_jobs.py @@ -3,6 +3,10 @@ class AcceptsLogTransportationJobs(AcceptsJobs): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def acceptJob(self, job: LogTransportationJob) -> bool: pass diff --git a/ml/fml40/features/functionalities/accepts_moisture_measurement.py b/ml/fml40/features/functionalities/accepts_moisture_measurement.py index 81db2c4..ade784f 100644 --- a/ml/fml40/features/functionalities/accepts_moisture_measurement.py +++ b/ml/fml40/features/functionalities/accepts_moisture_measurement.py @@ -3,5 +3,10 @@ class AcceptsMoistureMeasurement(Functionality): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + def acceptMoistureMeasurement(self, input: SoilMoistureMeasurement): pass \ No newline at end of file diff --git a/ml/fml40/features/functionalities/accepts_move_commands.py b/ml/fml40/features/functionalities/accepts_move_commands.py index c0869d1..7f100ef 100644 --- a/ml/fml40/features/functionalities/accepts_move_commands.py +++ b/ml/fml40/features/functionalities/accepts_move_commands.py @@ -1,11 +1,11 @@ from ml.ml40.features.functionalities.functionality import Functionality + class AcceptsMoveCommands(Functionality): - def __init__(self, name, ref_managing_actor): + def __init__(self, name="", identifier=""): super().__init__( name=name, - ref_managing_actor=ref_managing_actor - ) + identifier=identifier) def move(self, longitude: float, latitude: float): print("move to longitude: {}, latitude : {}".format(longitude, latitude)) diff --git a/ml/fml40/features/functionalities/accepts_passability_report.py b/ml/fml40/features/functionalities/accepts_passability_report.py index b69dcdc..abf676c 100644 --- a/ml/fml40/features/functionalities/accepts_passability_report.py +++ b/ml/fml40/features/functionalities/accepts_passability_report.py @@ -1,16 +1,12 @@ from ml.ml40.features.functionalities.functionality import Functionality -from ml.identifier import ID -from ml.fml40.features.properties.values.documents.reports.passability_report import ( - PassabilityReport, -) - +from ml.fml40.features.properties.values.documents.reports.passability_report import PassabilityReport class AcceptsPassabilityReport(Functionality): - def __init__(self, name, ref_managing_actor): - super(AcceptsPassabilityReport, self).__init__( - name=name, ref_managing_actor=ref_managing_actor - ) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) - def acceptsReport(self, report): + def acceptsReport(self, report:PassabilityReport): pass diff --git a/ml/fml40/features/functionalities/accepts_proximity_alert.py b/ml/fml40/features/functionalities/accepts_proximity_alert.py index 8c500ea..2a36f54 100644 --- a/ml/fml40/features/functionalities/accepts_proximity_alert.py +++ b/ml/fml40/features/functionalities/accepts_proximity_alert.py @@ -1,17 +1,12 @@ -import json from ml.ml40.features.functionalities.functionality import Functionality -from ml.identifier import ID class AcceptsProximityAlert(Functionality): - def __init__(self, name, ref_managing_actor): - super().__init__(name=name, ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def proximityAlert(self, ids: list, distances: list): print("Making Proximity Alert...") - def from_json(self, json_obj): - super().from_json(json_obj) - - def class_name(self): - return self.__class__.name diff --git a/ml/fml40/features/functionalities/accepts_single_tree_felling_jobs.py b/ml/fml40/features/functionalities/accepts_single_tree_felling_jobs.py index fe7edba..2928883 100644 --- a/ml/fml40/features/functionalities/accepts_single_tree_felling_jobs.py +++ b/ml/fml40/features/functionalities/accepts_single_tree_felling_jobs.py @@ -1,8 +1,8 @@ from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs -from ml.fml40.features.properties.values.documents.jobs.single_tree_felling_job import SingleTreeFellingJob class AcceptsSingleTreeFellingJobs(AcceptsJobs): - - def acceptJob(self, job: SingleTreeFellingJob) -> bool: - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/features/functionalities/classifies_tree_species.py b/ml/fml40/features/functionalities/classifies_tree_species.py index 9660143..2f3e30b 100644 --- a/ml/fml40/features/functionalities/classifies_tree_species.py +++ b/ml/fml40/features/functionalities/classifies_tree_species.py @@ -2,5 +2,10 @@ class ClassifiesTreeSpecies(Functionality): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + def calculateTreeSpeciesClassification(self, tree: bytes) -> bytes: pass diff --git a/ml/fml40/features/functionalities/cuts.py b/ml/fml40/features/functionalities/cuts.py index fd37581..edc1a0e 100644 --- a/ml/fml40/features/functionalities/cuts.py +++ b/ml/fml40/features/functionalities/cuts.py @@ -2,5 +2,10 @@ class Cuts(Functionality): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + def cut(self): pass diff --git a/ml/fml40/features/functionalities/determines_passability.py b/ml/fml40/features/functionalities/determines_passability.py index 176fcb8..db86ec0 100644 --- a/ml/fml40/features/functionalities/determines_passability.py +++ b/ml/fml40/features/functionalities/determines_passability.py @@ -1,7 +1,12 @@ from datetime import date from ml.ml40.features.functionalities.functionality import Functionality + class DeterminesPassability(Functionality): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def isPassableNow(self, input: float) -> bool: pass diff --git a/ml/fml40/features/functionalities/displays_health_alarms.py b/ml/fml40/features/functionalities/displays_health_alarms.py index 4566e6a..54d44d3 100644 --- a/ml/fml40/features/functionalities/displays_health_alarms.py +++ b/ml/fml40/features/functionalities/displays_health_alarms.py @@ -2,6 +2,10 @@ class DisplaysHealthAlarms(Functionality): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def displayHealthAlarm(self): pass diff --git a/ml/fml40/features/functionalities/evaluates_stand_attributes.py b/ml/fml40/features/functionalities/evaluates_stand_attributes.py index fe42c37..9b36196 100644 --- a/ml/fml40/features/functionalities/evaluates_stand_attributes.py +++ b/ml/fml40/features/functionalities/evaluates_stand_attributes.py @@ -3,6 +3,11 @@ class EvaluatesStandAttributes(Functionality): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + def calculateStandAttributes(self, input_a: bytes, input_b: date) -> str: pass diff --git a/ml/fml40/features/functionalities/fells.py b/ml/fml40/features/functionalities/fells.py index 1c6765d..b9efb50 100644 --- a/ml/fml40/features/functionalities/fells.py +++ b/ml/fml40/features/functionalities/fells.py @@ -4,9 +4,10 @@ class Fells(Functionality): - - def __init__(self, name, ref_managing_actor): - super().__init__(name=name, ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def executeFellingJob(self, job: FellingJob): print("i am executing the felling job {}".format(job)) diff --git a/ml/fml40/features/functionalities/forest_planning_evaluation.py b/ml/fml40/features/functionalities/forest_planning_evaluation.py index bb15a4e..e4e632f 100644 --- a/ml/fml40/features/functionalities/forest_planning_evaluation.py +++ b/ml/fml40/features/functionalities/forest_planning_evaluation.py @@ -2,5 +2,10 @@ class ForestPlanningEvaluation(Functionality): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + def evaluateInventoryData(self): pass diff --git a/ml/fml40/features/functionalities/forwards.py b/ml/fml40/features/functionalities/forwards.py index ee4b92b..37ec3ae 100644 --- a/ml/fml40/features/functionalities/forwards.py +++ b/ml/fml40/features/functionalities/forwards.py @@ -1,18 +1,12 @@ -import uuid from ml.ml40.features.functionalities.functionality import Functionality -from ml.fml40.features.properties.values.documents.jobs.forwarding_job import ( - ForwardingJob, -) -from ml.app_logger import APP_LOGGER -from ml.tools import create_request -from s3i import Broker -from s3i import GetValueRequest - +from ml.fml40.features.properties.values.documents.jobs.forwarding_job import ForwardingJob class Forwards(Functionality): - def __init__(self, name, ref_managing_actor): - super(Forwards, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def executeJob(self, job): pass diff --git a/ml/fml40/features/functionalities/generates_afforestation_suggestions.py b/ml/fml40/features/functionalities/generates_afforestation_suggestions.py index 5e09775..67cf505 100644 --- a/ml/fml40/features/functionalities/generates_afforestation_suggestions.py +++ b/ml/fml40/features/functionalities/generates_afforestation_suggestions.py @@ -3,6 +3,10 @@ class GeneratesAfforestationSuggestions(Functionality): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def generateAfforestationSuggestion(self) -> AfforestationSuggestion: pass \ No newline at end of file diff --git a/ml/fml40/features/functionalities/generates_felling_suggestions.py b/ml/fml40/features/functionalities/generates_felling_suggestions.py index 2f42b9d..be03b38 100644 --- a/ml/fml40/features/functionalities/generates_felling_suggestions.py +++ b/ml/fml40/features/functionalities/generates_felling_suggestions.py @@ -4,6 +4,10 @@ class GeneratesFellingSuggestions(Functionality): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def generateFellingSuggestion(self, tree_Id: ID) -> FellingSupportSuggestion: pass \ No newline at end of file diff --git a/ml/fml40/features/functionalities/grabs.py b/ml/fml40/features/functionalities/grabs.py index a969706..aab129f 100644 --- a/ml/fml40/features/functionalities/grabs.py +++ b/ml/fml40/features/functionalities/grabs.py @@ -2,8 +2,10 @@ class Grabs(Functionality): - def __init__(self): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def close(self): pass diff --git a/ml/fml40/features/functionalities/harvests.py b/ml/fml40/features/functionalities/harvests.py index 5d5b580..4005666 100644 --- a/ml/fml40/features/functionalities/harvests.py +++ b/ml/fml40/features/functionalities/harvests.py @@ -4,16 +4,12 @@ class Harvests(Functionality): - def __init__(self, name, ref_managing_actor): - super(Harvests, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def executeJob(self, job: FellingJob): - APP_LOGGER.info("I am harvesting right now. So much fun!") - # TODO: Error handling - proxy_manager = self.managing_actor.proxy_functionalities.get().get( - ["ml40::ManageJobs"], None - ) - proxy_manager.assignJob(self.sender_id, self.receiver_id, job) + pass + - def from_json(self, json_obj): - super().from_json(json_obj) diff --git a/ml/fml40/features/functionalities/measure_wood.py b/ml/fml40/features/functionalities/measure_wood.py index 630a7c1..38542fa 100644 --- a/ml/fml40/features/functionalities/measure_wood.py +++ b/ml/fml40/features/functionalities/measure_wood.py @@ -3,8 +3,10 @@ class MeasuresWood(Functionality): - def __init__(self): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def measureLog(self) -> LogMeasurement: pass diff --git a/ml/fml40/features/functionalities/monitor_health_status.py b/ml/fml40/features/functionalities/monitor_health_status.py index e0484e8..157723a 100644 --- a/ml/fml40/features/functionalities/monitor_health_status.py +++ b/ml/fml40/features/functionalities/monitor_health_status.py @@ -2,6 +2,10 @@ class MonitorsHealthStatus(Functionality): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def MonitorsHealthStatus(self): pass diff --git a/ml/fml40/features/functionalities/provides_moisture_prediction.py b/ml/fml40/features/functionalities/provides_moisture_prediction.py index 2bcc8bb..f3789db 100644 --- a/ml/fml40/features/functionalities/provides_moisture_prediction.py +++ b/ml/fml40/features/functionalities/provides_moisture_prediction.py @@ -1,17 +1,12 @@ from ml.ml40.features.functionalities.functionality import Functionality -from ml.identifier import ID -from ml.fml40.features.properties.values.documents.reports.passability_report import ( - PassabilityReport, -) -import datetime -import requests +from ml.fml40.features.properties.values.documents.reports.passability_report import PassabilityReport class ProvidesMoisturePrediction(Functionality): - def __init__(self, name, ref_managing_actor): - super(ProvidesMoisturePrediction, self).__init__( - name=name, ref_managing_actor=ref_managing_actor - ) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def predict(self): pass diff --git a/ml/fml40/features/functionalities/provides_passability_information.py b/ml/fml40/features/functionalities/provides_passability_information.py index afeb30f..33f054f 100644 --- a/ml/fml40/features/functionalities/provides_passability_information.py +++ b/ml/fml40/features/functionalities/provides_passability_information.py @@ -1,15 +1,12 @@ from ml.ml40.features.functionalities.functionality import Functionality -from ml.identifier import ID -from ml.fml40.features.properties.values.documents.reports.passability_report import ( - PassabilityReport, -) class ProvidesPassabilityInformation(Functionality): - def __init__(self, name, ref_managing_actor): - super(ProvidesPassabilityInformation, self).__init__( - name=name, ref_managing_actor=ref_managing_actor - ) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + def execute(self, json_body): pass diff --git a/ml/fml40/features/functionalities/provides_production_data.py b/ml/fml40/features/functionalities/provides_production_data.py index 9914622..aabb665 100644 --- a/ml/fml40/features/functionalities/provides_production_data.py +++ b/ml/fml40/features/functionalities/provides_production_data.py @@ -3,9 +3,10 @@ class ProvidesProductionData(Functionality): - def __init__(self, name, ref_managing_actor): - super(ProvidesProductionData, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super(ProvidesProductionData, self).__init__( + name=name, + identifier=identifier) def getProductionData(self) -> ProductionData: return "Production Data is huge" diff --git a/ml/fml40/features/functionalities/provides_tree_data.py b/ml/fml40/features/functionalities/provides_tree_data.py index d7fcf8c..c262b50 100644 --- a/ml/fml40/features/functionalities/provides_tree_data.py +++ b/ml/fml40/features/functionalities/provides_tree_data.py @@ -5,6 +5,10 @@ class ProvidesTreeData(Functionality): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def getTreeData(self, Tree:ID) -> list: pass diff --git a/ml/fml40/features/functionalities/provides_weather_data.py b/ml/fml40/features/functionalities/provides_weather_data.py index 3c1774a..ad00bea 100644 --- a/ml/fml40/features/functionalities/provides_weather_data.py +++ b/ml/fml40/features/functionalities/provides_weather_data.py @@ -2,4 +2,8 @@ class ProvidesWeatherData(Functionality): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + diff --git a/ml/fml40/features/functionalities/simulates_tree_growth.py b/ml/fml40/features/functionalities/simulates_tree_growth.py index e34edee..7fef971 100644 --- a/ml/fml40/features/functionalities/simulates_tree_growth.py +++ b/ml/fml40/features/functionalities/simulates_tree_growth.py @@ -2,4 +2,9 @@ class SimulatesTreeGrowth(Functionality): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + + diff --git a/ml/fml40/features/functionalities/supports_felling.py b/ml/fml40/features/functionalities/supports_felling.py index 2efde1e..da78717 100644 --- a/ml/fml40/features/functionalities/supports_felling.py +++ b/ml/fml40/features/functionalities/supports_felling.py @@ -4,6 +4,10 @@ class SupportsFelling(Functionality): + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def supportFelling(self, job:FellingJob, suggestion: FellingSupportSuggestion): pass diff --git a/ml/fml40/features/functionalities/transports_logs.py b/ml/fml40/features/functionalities/transports_logs.py index 5004493..ca8fd3a 100644 --- a/ml/fml40/features/functionalities/transports_logs.py +++ b/ml/fml40/features/functionalities/transports_logs.py @@ -4,10 +4,10 @@ class TransportsLogs(Functionality): - def __init__(self, name, ref_managing_actor): - super(TransportsLogs, self).__init__( + def __init__(self, name="", identifier=""): + super().__init__( name=name, - ref_managing_actor=ref_managing_actor) + identifier=identifier) def transportLogs(self, job: LogTransportationJob) -> LogTransportationReport: print("i am making Log transportation report for the job {}".format(job)) diff --git a/ml/fml40/features/properties/values/abstract_inventory.py b/ml/fml40/features/properties/values/abstract_inventory.py index ddd6e19..2421416 100644 --- a/ml/fml40/features/properties/values/abstract_inventory.py +++ b/ml/fml40/features/properties/values/abstract_inventory.py @@ -2,4 +2,7 @@ class AbstractInventory(ABC): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/features/properties/values/assortment.py b/ml/fml40/features/properties/values/assortment.py index 74ff5ef..e0798bd 100644 --- a/ml/fml40/features/properties/values/assortment.py +++ b/ml/fml40/features/properties/values/assortment.py @@ -2,10 +2,12 @@ class Assortment(Value): - def __init__(self, name, ref_managing_actor, grade=""): - super(Assortment, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.__grade = grade + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.__grade = None + self.__json_out = dict() @property def grade(self): @@ -16,12 +18,8 @@ def grade(self, value): self.__grade = value def to_json(self): - temp_dict = super().to_json() - if self.grade: - _dict = temp_dict.copy() - _dict.update({ - "grade": self.grade, - }) - return _dict - else: - return temp_dict + self.__json_out = super().to_json() + if self.grade is not None: + self.__json_out["grade"] = self.grade + + return self.__json_out diff --git a/ml/fml40/features/properties/values/dbh.py b/ml/fml40/features/properties/values/dbh.py index 8151ce9..f6b303d 100644 --- a/ml/fml40/features/properties/values/dbh.py +++ b/ml/fml40/features/properties/values/dbh.py @@ -2,10 +2,12 @@ class DBH(Value): - def __init__(self, name, ref_managing_actor, dbh=0): - super(DBH, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.__dbh = dbh + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.__dbh = None + self.__json_out = dict() @property def dbh(self): @@ -16,12 +18,7 @@ def dbh(self, value): self.__dbh = value def to_json(self): - temp_dict = super().to_json() - if self.dbh: - _dict = temp_dict.copy() - _dict.update({ - "dbh": self.dbh, - }) - return _dict - else: - return temp_dict \ No newline at end of file + self.__json_out = super().to_json() + if self.dbh is not None: + self.__json_out["dbh"] = self.dbh + return self.__json_out \ No newline at end of file diff --git a/ml/fml40/features/properties/values/documents/jobs/felling_job.py b/ml/fml40/features/properties/values/documents/jobs/felling_job.py index f2588cc..07603a7 100644 --- a/ml/fml40/features/properties/values/documents/jobs/felling_job.py +++ b/ml/fml40/features/properties/values/documents/jobs/felling_job.py @@ -2,9 +2,7 @@ class FellingJob(Job): - def __init__(self, name, ref_managing_actor): - super(FellingJob, self).__init__( - name=name, ref_managing_actor=ref_managing_actor - ) - - + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/features/properties/values/harvested_volume.py b/ml/fml40/features/properties/values/harvested_volume.py index aabc29a..3a3ea75 100644 --- a/ml/fml40/features/properties/values/harvested_volume.py +++ b/ml/fml40/features/properties/values/harvested_volume.py @@ -2,10 +2,13 @@ class HarvestedVolume(Value): - def __init__(self, name, ref_managing_actor, volume=0): - super(HarvestedVolume, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.__volume = volume + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + + self.__volume = None + self.__json_out = None @property def volume(self): @@ -16,12 +19,7 @@ def volume(self, value): self.__volume = value def to_json(self): - temp_dict = super().to_json() - if self.volume: - _dict = temp_dict.copy() - _dict.update({ - "volume": self.__volume, - }) - return _dict - else: - return temp_dict + self.__json_out = super().to_json() + if self.volume is not None: + self.__json_out["volume"] = self.volume + return self.__json_out diff --git a/ml/fml40/features/properties/values/harvesting_parameter.py b/ml/fml40/features/properties/values/harvesting_parameter.py index 5cc67b4..505f716 100644 --- a/ml/fml40/features/properties/values/harvesting_parameter.py +++ b/ml/fml40/features/properties/values/harvesting_parameter.py @@ -2,10 +2,12 @@ class HarvestingParameters(Value): - def __init__(self, name, ref_managing_actor, cuttingLengths=0): - super(HarvestingParameters, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.__cuttingLengths = cuttingLengths + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.__cuttingLengths = None + self.__json_out = dict() @property def cuttingLengths(self): @@ -16,12 +18,7 @@ def cuttingLengths(self, value): self.__cuttingLengths = value def to_json(self): - temp_dict = super().to_json() - if self.cuttingLengths: - _dict = temp_dict.copy() - _dict.update({ - "cuttingLengths": self.__cuttingLengths, - }) - return _dict - else: - return temp_dict + self.__json_out = super().to_json() + if self.cuttingLengths is not None: + self.__json_out["cuttingLengths"] = self.cuttingLengths + return self.__json_out diff --git a/ml/fml40/features/properties/values/inventory_data.py b/ml/fml40/features/properties/values/inventory_data.py index 1a251f0..e1fdae1 100644 --- a/ml/fml40/features/properties/values/inventory_data.py +++ b/ml/fml40/features/properties/values/inventory_data.py @@ -3,15 +3,25 @@ class InventoryData(Value): - def __init__(self, name, ref_managing_actor): - super(InventoryData, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.__data = [] + self.__json_out = dict() @property def data(self): return self.__data @data.setter - def data(self, value: AbstractInventory): - self.__data.append(value) + def data(self, value): + self.__data = value + + def to_json(self): + self.__json_out = super().to_json() + if self.data: + self.__json_out["data"] = self.data + return self.__json_out + diff --git a/ml/fml40/features/properties/values/stem_segment_properties.py b/ml/fml40/features/properties/values/stem_segment_properties.py index b535c14..2b474a7 100644 --- a/ml/fml40/features/properties/values/stem_segment_properties.py +++ b/ml/fml40/features/properties/values/stem_segment_properties.py @@ -2,14 +2,16 @@ class StemSegmentProperties(Value): - def __init__(self, name, ref_managing_actor, - diameter: float, length: float, quality: str, woodType: str): - super(StemSegmentProperties, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.__diameter = diameter - self.__length = length - self.__quality = quality - self.__woodType = woodType + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + + self.__diameter = None + self.__length = None + self.__quality = None + self.__woodType = None + self.__json_out = dict() @property def diameter(self): @@ -42,3 +44,19 @@ def woodType(self): @woodType.setter def woodType(self, value): self.__woodType = value + + def to_json(self): + self.__json_out = super().to_json() + if self.diameter is not None: + self.__json_out["diameter"] = self.diameter + + if self.length is not None: + self.__json_out["length"] = self.length + + if self.woodType is not None: + self.__json_out["woodType"] = self.woodType + + if self.quality is not None: + self.__json_out["quality"] = self.quality + + return self.__json_out diff --git a/ml/fml40/features/properties/values/thickness_class.py b/ml/fml40/features/properties/values/thickness_class.py index f5660a4..b89c879 100644 --- a/ml/fml40/features/properties/values/thickness_class.py +++ b/ml/fml40/features/properties/values/thickness_class.py @@ -2,6 +2,10 @@ class ThicknessClass(Value): - def __init__(self, name, ref_managing_actor): - super(ThicknessClass, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + + + diff --git a/ml/fml40/features/properties/values/tilt.py b/ml/fml40/features/properties/values/tilt.py index d9debff..6d2ae60 100644 --- a/ml/fml40/features/properties/values/tilt.py +++ b/ml/fml40/features/properties/values/tilt.py @@ -2,12 +2,13 @@ class Tilt(Value): - def __init__(self, name, ref_managing_actor, - direction: float, tilt: float): - super(Tilt, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.__direction = direction - self.__tilt = tilt + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.__direction = None + self.__tilt = None + self.__json_out = dict() @property def direction(self): @@ -24,3 +25,13 @@ def tilt(self): @tilt.setter def tilt(self, value): self.__tilt = value + + def to_json(self): + self.__json_out = super().to_json() + if self.direction is not None: + self.__json_out["direction"] = self.direction + + if self.tilt is not None: + self.__json_out["tilt"] = self.tilt + + return self.__json_out \ No newline at end of file diff --git a/ml/fml40/features/properties/values/tree_data.py b/ml/fml40/features/properties/values/tree_data.py index a979591..a6e4262 100644 --- a/ml/fml40/features/properties/values/tree_data.py +++ b/ml/fml40/features/properties/values/tree_data.py @@ -2,6 +2,7 @@ class TreeData(Value): - def __init__(self, name, ref_managing_actor): - super(TreeData, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/features/properties/values/tree_type.py b/ml/fml40/features/properties/values/tree_type.py index 8c25c93..9c6482a 100644 --- a/ml/fml40/features/properties/values/tree_type.py +++ b/ml/fml40/features/properties/values/tree_type.py @@ -2,10 +2,12 @@ class TreeType(Value): - def __init__(self, name, ref_managing_actor, conifer=False): - super(TreeType, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.__conifer = conifer + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.__conifer = None + self.__json_out = dict() @property def conifer(self): @@ -14,3 +16,9 @@ def conifer(self): @conifer.setter def conifer(self, value): self.__conifer = value + + def to_json(self): + self.__json_out = super().to_json() + if self.conifer is not None: + self.__json_out["conifer"] = self.conifer + return self.__json_out diff --git a/ml/fml40/features/properties/values/wood_quality.py b/ml/fml40/features/properties/values/wood_quality.py index 208dac7..fc73afd 100644 --- a/ml/fml40/features/properties/values/wood_quality.py +++ b/ml/fml40/features/properties/values/wood_quality.py @@ -2,7 +2,8 @@ class WoodQuality(Value): - def __init__(self, name, ref_managing_actor): - super(WoodQuality, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/machines/forwarder.py b/ml/fml40/machines/forwarder.py deleted file mode 100644 index 89a079a..0000000 --- a/ml/fml40/machines/forwarder.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Implementation of a generic Forwarder""" -import json -from ml.thing import Thing -from ml.app_logger import APP_LOGGER -from ml.tools import decode_message -from ml.tools import verify_message - - -class Forwarder(Thing): - """Implements a generic Forwarder.""" - - def __init__( - self, - data_model, - is_broker, - is_repo, - grant_type, - client_secret, - username, - password, - ): - super().__init__( - client_secret=client_secret, - data_model=data_model, - is_broker=is_broker, - is_repo=is_repo, - grant_type=grant_type, - username=username, - password=password, - ) - self.jobs = dict() - - def on_receive(self, msg): - """This function is called when the asset administration shell gets something - - :param body: json object representing a s3i message. - - """ - body_str = decode_message(msg) - body_json = json.loads(body_str) - APP_LOGGER.info( - "Received {} of type {}".format( - body_json["messageType"], body_json["serviceType"] - ) - ) - if verify_message(body_json, "serviceRequest", "fml40::ForwardingJob"): - self.proxy_functionalities["AcceptsForwardingJobs"].acceptJob( - "This is a forwarding job" - ) - msg = """I'm the asset administration shell and I distributed your message - to one of my highly qualified services""" - return msg diff --git a/ml/fml40/machines/harvester.py b/ml/fml40/machines/harvester.py deleted file mode 100644 index 63bfd77..0000000 --- a/ml/fml40/machines/harvester.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Generic implementation of a harvester.""" - -import json -from ml.thing import Thing -from ml.app_logger import APP_LOGGER -from ml.tools import decode_message - - -class Harvester(Thing): - """Implements a generic harvester.""" - - def __init__( - self, - data_model, - is_broker, - is_repo, - grant_type, - client_secret, - username, - password, - ): - super().__init__( - client_secret=client_secret, - data_model=data_model, - is_broker=is_broker, - is_repo=is_repo, - grant_type=grant_type, - username=username, - password=password, - ) - - self.jobs = dict() diff --git a/ml/fml40/roles/dts/handheld_devices/brushcutter.py b/ml/fml40/roles/dts/handheld_devices/brushcutter.py index 75a6295..fc5e3d2 100644 --- a/ml/fml40/roles/dts/handheld_devices/brushcutter.py +++ b/ml/fml40/roles/dts/handheld_devices/brushcutter.py @@ -2,4 +2,7 @@ class Brushcutter(HandheldDevice): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/handheld_devices/chainsaw.py b/ml/fml40/roles/dts/handheld_devices/chainsaw.py index 2ce3c4b..596e979 100644 --- a/ml/fml40/roles/dts/handheld_devices/chainsaw.py +++ b/ml/fml40/roles/dts/handheld_devices/chainsaw.py @@ -2,4 +2,7 @@ class Chainsaw(HandheldDevice): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/machines/forest_machine.py b/ml/fml40/roles/dts/machines/forest_machine.py index d4ae55c..a25e733 100644 --- a/ml/fml40/roles/dts/machines/forest_machine.py +++ b/ml/fml40/roles/dts/machines/forest_machine.py @@ -2,8 +2,7 @@ class ForestMachine(Machine): - def __init__(self, name, ref_managing_actor): - super(ForestMachine, self).__init__( + def __init__(self, name="", identifier=""): + super().__init__( name=name, - ref_managing_actor=ref_managing_actor - ) \ No newline at end of file + identifier=identifier) \ No newline at end of file diff --git a/ml/fml40/roles/dts/machines/forwarder.py b/ml/fml40/roles/dts/machines/forwarder.py index f20b118..e404fda 100644 --- a/ml/fml40/roles/dts/machines/forwarder.py +++ b/ml/fml40/roles/dts/machines/forwarder.py @@ -2,4 +2,7 @@ class Forwarder(ForestMachine): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) \ No newline at end of file diff --git a/ml/fml40/roles/dts/machines/harvester.py b/ml/fml40/roles/dts/machines/harvester.py index af4e876..74f17e8 100644 --- a/ml/fml40/roles/dts/machines/harvester.py +++ b/ml/fml40/roles/dts/machines/harvester.py @@ -2,6 +2,7 @@ class Harvester(ForestMachine): - def __init__(self, name, ref_managing_actor): - super(Harvester, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/machines/log_truck.py b/ml/fml40/roles/dts/machines/log_truck.py index 751dd1d..51e53fd 100644 --- a/ml/fml40/roles/dts/machines/log_truck.py +++ b/ml/fml40/roles/dts/machines/log_truck.py @@ -2,4 +2,7 @@ class LogTruck(ForestMachine): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/machines/mini_tractor.py b/ml/fml40/roles/dts/machines/mini_tractor.py index 46be356..c8fd951 100644 --- a/ml/fml40/roles/dts/machines/mini_tractor.py +++ b/ml/fml40/roles/dts/machines/mini_tractor.py @@ -2,4 +2,7 @@ class MiniTractor(ForestMachine): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) \ No newline at end of file diff --git a/ml/fml40/roles/dts/machines/skidder.py b/ml/fml40/roles/dts/machines/skidder.py index 4c7997f..308745b 100644 --- a/ml/fml40/roles/dts/machines/skidder.py +++ b/ml/fml40/roles/dts/machines/skidder.py @@ -2,4 +2,7 @@ class Skidder(ForestMachine): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/machines/wheel_loader.py b/ml/fml40/roles/dts/machines/wheel_loader.py index fb1be89..bfb01b7 100644 --- a/ml/fml40/roles/dts/machines/wheel_loader.py +++ b/ml/fml40/roles/dts/machines/wheel_loader.py @@ -2,4 +2,7 @@ class WheelLoader(ForestMachine): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/parts/grabber.py b/ml/fml40/roles/dts/parts/grabber.py index 0f6d236..5112fc3 100644 --- a/ml/fml40/roles/dts/parts/grabber.py +++ b/ml/fml40/roles/dts/parts/grabber.py @@ -2,4 +2,7 @@ class Grabber(Part): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/parts/harvesting_head.py b/ml/fml40/roles/dts/parts/harvesting_head.py index 8a1bdeb..7fd38e1 100644 --- a/ml/fml40/roles/dts/parts/harvesting_head.py +++ b/ml/fml40/roles/dts/parts/harvesting_head.py @@ -2,4 +2,7 @@ class HarvestingHead(Part): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/parts/log_loading_area.py b/ml/fml40/roles/dts/parts/log_loading_area.py index ca177f8..9e4a7e8 100644 --- a/ml/fml40/roles/dts/parts/log_loading_area.py +++ b/ml/fml40/roles/dts/parts/log_loading_area.py @@ -2,4 +2,7 @@ class LogLoadingArea(Part): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/parts/saw.py b/ml/fml40/roles/dts/parts/saw.py index 31811ae..7c5d853 100644 --- a/ml/fml40/roles/dts/parts/saw.py +++ b/ml/fml40/roles/dts/parts/saw.py @@ -2,4 +2,7 @@ class Saw(Part): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/parts/winch.py b/ml/fml40/roles/dts/parts/winch.py index 229d1ea..ea47c1f 100644 --- a/ml/fml40/roles/dts/parts/winch.py +++ b/ml/fml40/roles/dts/parts/winch.py @@ -2,4 +2,7 @@ class Winch(Part): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/persons/forest_owner.py b/ml/fml40/roles/dts/persons/forest_owner.py index bc25b1c..836bf78 100644 --- a/ml/fml40/roles/dts/persons/forest_owner.py +++ b/ml/fml40/roles/dts/persons/forest_owner.py @@ -2,4 +2,7 @@ class ForestOwner(Person): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/persons/forest_worker.py b/ml/fml40/roles/dts/persons/forest_worker.py index d84a219..53611bc 100644 --- a/ml/fml40/roles/dts/persons/forest_worker.py +++ b/ml/fml40/roles/dts/persons/forest_worker.py @@ -2,4 +2,7 @@ class ForestWorker(Person): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/persons/mini_tractor_operator.py b/ml/fml40/roles/dts/persons/mini_tractor_operator.py index 79dc4ac..1fc716e 100644 --- a/ml/fml40/roles/dts/persons/mini_tractor_operator.py +++ b/ml/fml40/roles/dts/persons/mini_tractor_operator.py @@ -2,4 +2,7 @@ class MiniTractorOperator(Person): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/persons/skidder_operator.py b/ml/fml40/roles/dts/persons/skidder_operator.py index 34587df..b6cde2a 100644 --- a/ml/fml40/roles/dts/persons/skidder_operator.py +++ b/ml/fml40/roles/dts/persons/skidder_operator.py @@ -2,4 +2,7 @@ class SkidderOperator(Person): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/sensors/vitality_sensor.py b/ml/fml40/roles/dts/sensors/vitality_sensor.py index 3cc661b..8244341 100644 --- a/ml/fml40/roles/dts/sensors/vitality_sensor.py +++ b/ml/fml40/roles/dts/sensors/vitality_sensor.py @@ -2,4 +2,7 @@ class VilalitySensor(Sensor): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/sites/forest_enterprise.py b/ml/fml40/roles/dts/sites/forest_enterprise.py index 4fecd47..e117eb1 100644 --- a/ml/fml40/roles/dts/sites/forest_enterprise.py +++ b/ml/fml40/roles/dts/sites/forest_enterprise.py @@ -2,4 +2,7 @@ class ForestEnterprise(Site): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/sites/hauler.py b/ml/fml40/roles/dts/sites/hauler.py index 009fa5d..8f7cba9 100644 --- a/ml/fml40/roles/dts/sites/hauler.py +++ b/ml/fml40/roles/dts/sites/hauler.py @@ -2,4 +2,7 @@ class Hauler(Site): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/sites/mill/mill.py b/ml/fml40/roles/dts/sites/mill/mill.py index 8c297b1..860cdfd 100644 --- a/ml/fml40/roles/dts/sites/mill/mill.py +++ b/ml/fml40/roles/dts/sites/mill/mill.py @@ -2,4 +2,7 @@ class Mill(Site): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/sites/mill/papermill.py b/ml/fml40/roles/dts/sites/mill/papermill.py index dc4f4f2..da9200c 100644 --- a/ml/fml40/roles/dts/sites/mill/papermill.py +++ b/ml/fml40/roles/dts/sites/mill/papermill.py @@ -2,4 +2,7 @@ class Papermill(Mill): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40/roles/dts/sites/mill/sawmill.py b/ml/fml40/roles/dts/sites/mill/sawmill.py index fd03337..0dc0402 100644 --- a/ml/fml40/roles/dts/sites/mill/sawmill.py +++ b/ml/fml40/roles/dts/sites/mill/sawmill.py @@ -2,4 +2,7 @@ class Sawmill(Mill): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/fml40_factory.py b/ml/fml40_factory.py deleted file mode 100644 index 0bee2a4..0000000 --- a/ml/fml40_factory.py +++ /dev/null @@ -1,4 +0,0 @@ -from ml.dt_factory import DT_FACTORY -from ml.functionalities.functionalities import FUNCTIONALITY_FACTORY - -FML40_FACTORY = {**DT_FACTORY, ** FUNCTIONALITY_FACTORY} diff --git a/ml/identifier.py b/ml/identifier.py index b843029..7ee4119 100644 --- a/ml/identifier.py +++ b/ml/identifier.py @@ -1,21 +1,26 @@ -class ID(object): +import uuid + + +class ID: """ Define primitive variable type ID """ - def __init__(self, identifier): - # TODO validate identifier - self.__identifier = identifier + def __init__(self, identifier=""): + # TODO validate identifier + if identifier != "" and self.is_valid_uuid(identifier.replace("s3i:", "")): + self.__identifier = identifier + else: + self.__identifier = "s3i:{}".format(uuid.uuid4()) @property - def ID(self): + def identifier(self): return self.__identifier - @ID.setter - def ID(self, value): - """ - TODO specification ID e.g. s3i:uuid or gSFL:uuid? - :param value: identifier - :return: identifier - """ - self.__identifier = value + @staticmethod + def is_valid_uuid(uuid_to_test, version=4): + try: + uuid_obj = uuid.UUID(uuid_to_test, version=version) + except ValueError: + return False + return str(uuid_obj) == uuid_to_test diff --git a/ml/ml40/features/feature.py b/ml/ml40/features/feature.py deleted file mode 100644 index 092837a..0000000 --- a/ml/ml40/features/feature.py +++ /dev/null @@ -1,29 +0,0 @@ -from ml.managed_actor import ManagedActor - - -class Feature(ManagedActor): - def __init__(self, name, ref_managing_actor, subFeatures={}): - super(Feature, self).__init__(name, ref_managing_actor) - self.__subFeatures = subFeatures - - @property - def subFeatures(self): - return self.__subFeatures - - @subFeatures.setter - def subFeatures(self, value): - self.__subFeatures = value - - def to_json(self): - temp_dict = super().to_json() - #print(temp_dict) - if self.subFeatures: - temp_dict["subFeatures"] = [] - for key in self.subFeatures.keys(): - temp_dict["subFeatures"].append(self.subFeatures[key].proxy().to_json().get()) - return temp_dict - - def from_json(self, json_obj): - self.__name = json_obj.get("name", "") - self.__identifier = json_obj.get("identifier", "") - diff --git a/ml/ml40/features/functionalities/accepts_jobs.py b/ml/ml40/features/functionalities/accepts_jobs.py index 56b3956..e0955a2 100644 --- a/ml/ml40/features/functionalities/accepts_jobs.py +++ b/ml/ml40/features/functionalities/accepts_jobs.py @@ -5,11 +5,10 @@ class AcceptsJobs(Functionality): - def __init__(self, name, ref_managing_actor): - super(AcceptsJobs, self).__init__( - name=name, ref_managing_actor=ref_managing_actor - ) - self.__jobs = dict() + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def acceptJob(self, job: Job) -> bool: pass diff --git a/ml/ml40/features/functionalities/accepts_reports.py b/ml/ml40/features/functionalities/accepts_reports.py index 20d69fc..f4dae27 100644 --- a/ml/ml40/features/functionalities/accepts_reports.py +++ b/ml/ml40/features/functionalities/accepts_reports.py @@ -1,12 +1,12 @@ - from ml.ml40.features.functionalities.functionality import Functionality from ml.ml40.features.properties.values.documents.reports.report import Report + class AcceptsReports(Functionality): - def __init__(self, name, ref_managing_actor): - super(AcceptsReports, self).__init__( - name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def acceptReport(self, report: Report): pass diff --git a/ml/ml40/features/functionalities/clears_jobs.py b/ml/ml40/features/functionalities/clears_jobs.py index 34160ee..54ce44b 100644 --- a/ml/ml40/features/functionalities/clears_jobs.py +++ b/ml/ml40/features/functionalities/clears_jobs.py @@ -3,10 +3,10 @@ class ClearsJobs(Functionality): - def __init__(self, name, identifier, ref_managing_actor): - super(ClearsJobs, self).__init__( - name=name, identifier=identifier, ref_managing_actor=ref_managing_actor - ) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def clear(self, job: Job) -> bool: pass diff --git a/ml/ml40/features/functionalities/functionality.py b/ml/ml40/features/functionalities/functionality.py index fb97908..cba654d 100644 --- a/ml/ml40/features/functionalities/functionality.py +++ b/ml/ml40/features/functionalities/functionality.py @@ -1,36 +1,12 @@ -import uuid -from s3i import ServiceReply -from ml.ml40.features.feature import Feature +from ml.feature import Feature class Functionality(Feature): """Genric implementation of a Functionaliy.""" - - def __init__(self, name, ref_managing_actor): + def __init__(self, name="", identifier=""): super(Functionality, self).__init__( - name=name, ref_managing_actor=ref_managing_actor - ) - # TODO: Make it private - self.service_reply = None + name=name, + identifier=identifier) + - 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 - sender_uuid = thing_proxy.thing_id.get() - msg_uuid = str(uuid.uuid4()) - receiver_uuids = [json_body.get("sender", "")] - service_type = json_body.get("serviceType", "") - receiver_uuids = json_body.get("replyingToMessage", "") - reply_to_msg_uuid = json_body.get("identifier", "") - self.service_reply.fillServiceReply( - sender_uuid, - receiver_uuids, - service_type, - results, - msg_uuid, - reply_to_msg_uuid, - ) - return self.service_reply diff --git a/ml/ml40/features/functionalities/manages_jobs.py b/ml/ml40/features/functionalities/manages_jobs.py index f95019c..9c389f0 100644 --- a/ml/ml40/features/functionalities/manages_jobs.py +++ b/ml/ml40/features/functionalities/manages_jobs.py @@ -5,16 +5,13 @@ class ManagesJobs(Functionality): - def __init__(self, name, ref_managing_actor, *args, **kwargs): - super().__init__(name=name, ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def acceptReport(self, report: Report): - print("accepts Report ..") pass def assignJob(self, job: Job, identifier: ID): - print("I assign the job {0} to Komatsu forwarder...".format(job)) - self.managing_actor.on_assign_forwarding_job(job) - - def from_json(self, json_obj): - super().from_json(json_obj) + pass diff --git a/ml/ml40/features/functionalities/plans_routes.py b/ml/ml40/features/functionalities/plans_routes.py index 354925a..3ff77eb 100644 --- a/ml/ml40/features/functionalities/plans_routes.py +++ b/ml/ml40/features/functionalities/plans_routes.py @@ -3,9 +3,11 @@ from ml.ml40.features.properties.values.location import Location class PlansRoutes(Functionality): - def __init__(self, name, ref_managing_actor): - super(PlansRoutes, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + def planRoute(self, start: Location, goal: Location) -> Route: pass diff --git a/ml/ml40/features/functionalities/provides_map_data.py b/ml/ml40/features/functionalities/provides_map_data.py index c4a9038..cf64334 100644 --- a/ml/ml40/features/functionalities/provides_map_data.py +++ b/ml/ml40/features/functionalities/provides_map_data.py @@ -1,10 +1,12 @@ from ml.ml40.features.functionalities.functionality import Functionality from ml.fml40.features.properties.values.documents.reports.passability_report import PassabilityReport + class ProvidesMapData(Functionality): - def __init__(self, name, ref_managing_actor): - super(ProvidesMapData, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def getMapData(self, map: int) -> PassabilityReport: pass diff --git a/ml/ml40/features/functionalities/provides_operational_data.py b/ml/ml40/features/functionalities/provides_operational_data.py index f98dd4b..576d1bf 100644 --- a/ml/ml40/features/functionalities/provides_operational_data.py +++ b/ml/ml40/features/functionalities/provides_operational_data.py @@ -3,15 +3,12 @@ class ProvidesOperationalData(Functionality): - def __init__(self, name, ref_managing_actor): - super(ProvidesOperationalData, self).__init__( - name=name, ref_managing_actor=ref_managing_actor - ) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def getOperationalData(self) -> Report: - print("make the operational data report...") return "this is a simply report" - def from_json(self, json_obj): - super.from_json(json_obj) diff --git a/ml/ml40/features/functionalities/renders.py b/ml/ml40/features/functionalities/renders.py index 94d66c2..92c0f9a 100644 --- a/ml/ml40/features/functionalities/renders.py +++ b/ml/ml40/features/functionalities/renders.py @@ -3,8 +3,10 @@ class Renders(Functionality): - def __init__(self, name, ref_managing_actor): - super(Renders, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) def create3DVideo(self, identifier: ID) -> bytes: pass diff --git a/ml/ml40/features/properties/association.py b/ml/ml40/features/properties/association.py deleted file mode 100644 index 606e8ee..0000000 --- a/ml/ml40/features/properties/association.py +++ /dev/null @@ -1,6 +0,0 @@ -from ml.ml40.features.properties.property import Property - - -class Association(Property): - def __init__(self, name, ref_managing_actor): - super(Association, self).__init__(name=name, ref_managing_actor=ref_managing_actor) diff --git a/ml/ml40/features/properties/associations/__init__.py b/ml/ml40/features/properties/associations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/ml40/features/properties/associations/association.py b/ml/ml40/features/properties/associations/association.py new file mode 100644 index 0000000..7ac5088 --- /dev/null +++ b/ml/ml40/features/properties/associations/association.py @@ -0,0 +1,7 @@ +from ml.ml40.features.properties.property import Property + + +class Association(Property): + def __init__(self, name="", identifier=""): + super(Association, self).__init__(name=name, + identifier=identifier) diff --git a/ml/ml40/features/properties/associations/composite.py b/ml/ml40/features/properties/associations/composite.py new file mode 100644 index 0000000..eb09f75 --- /dev/null +++ b/ml/ml40/features/properties/associations/composite.py @@ -0,0 +1,27 @@ +from ml.ml40.features.properties.associations.association import Association + + +class Composite(Association): + def __init__(self, name="", identifier=""): + super(Composite, self).__init__( + name=name, + identifier=identifier) + self.__targets = dict() + self.__json_out = dict() + + @property + def targets(self): + return self.__targets + + @targets.setter + def targets(self, value): + self.__targets = value + + def to_json(self): + self.__json_out = super().to_json() + if self.targets: + self.__json_out["targets"] = [] + for key in self.targets.keys(): + self.__json_out["targets"].append(self.targets[key].to_subthing_json()) + return self.__json_out + diff --git a/ml/ml40/features/properties/associations/shared.py b/ml/ml40/features/properties/associations/shared.py new file mode 100644 index 0000000..d94677d --- /dev/null +++ b/ml/ml40/features/properties/associations/shared.py @@ -0,0 +1,27 @@ +from ml.ml40.features.properties.associations.association import Association + + +class Shared(Association): + def __init__(self, name="", identifier=""): + super(Shared, self).__init__( + name=name, + identifier=identifier) + self.__targets = [] + self.__targets = dict() + self.__json_out = dict() + + @property + def targets(self): + return self.__targets + + @targets.setter + def targets(self, value): + self.__targets = value + + def to_json(self): + self.__json_out = super().to_json() + if self.targets: + self.__json_out["targets"] = [] + for key in self.targets.keys(): + self.__json_out["targets"].append(self.targets[key].to_subthing_json()) + return self.__json_out diff --git a/ml/ml40/features/properties/composite.py b/ml/ml40/features/properties/composite.py deleted file mode 100644 index 2a3a461..0000000 --- a/ml/ml40/features/properties/composite.py +++ /dev/null @@ -1,31 +0,0 @@ -from ml.ml40.features.properties.association import Association - - -class Composite(Association): - def __init__(self, name, ref_managing_actor): - super(Composite, self).__init__( - name=name, ref_managing_actor=ref_managing_actor - ) - self.__targets = [] - - @property - def targets(self): - return self.__targets - - @targets.setter - def targets(self, value): - self.__targets = value - - def to_json(self): - temp_dict = super().to_json() - if self.targets: - - temp_dict["targets"] = [] - for target in self.targets: - temp_dict["targets"].append(target.proxy().model.get()["attributes"]) - - return temp_dict - - def from_json(self, json_obj): - super().from_json(json_obj) - self.__targets = json_obj.get("targets", []) diff --git a/ml/ml40/features/properties/property.py b/ml/ml40/features/properties/property.py index 14cb65b..8c18282 100644 --- a/ml/ml40/features/properties/property.py +++ b/ml/ml40/features/properties/property.py @@ -1,8 +1,11 @@ -from ml.ml40.features.feature import Feature +from ml.feature import Feature class Property(Feature): - def __init__(self, name, ref_managing_actor): - super(Property, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super(Property, self).__init__( + name=name, + identifier=identifier) + diff --git a/ml/ml40/features/properties/shared.py b/ml/ml40/features/properties/shared.py deleted file mode 100644 index 8bfb31f..0000000 --- a/ml/ml40/features/properties/shared.py +++ /dev/null @@ -1,12 +0,0 @@ -from ml.ml40.features.properties.association import Association - - -class Shared(Association): - def __init__(self, name, ref_managing_actor): - super(Shared, self).__init__(name=name, ref_managing_actor=ref_managing_actor) - # INFO: targets is a list of things - self.__targets = [] - - def from_json(self, json_obj): - super().from_json(json_obj) - self.__targets = json_obj.get("targets", []) diff --git a/ml/ml40/features/properties/values/address.py b/ml/ml40/features/properties/values/address.py index 192573e..297feec 100644 --- a/ml/ml40/features/properties/values/address.py +++ b/ml/ml40/features/properties/values/address.py @@ -2,12 +2,29 @@ class Address(Value): - def __init__(self, name, ref_managing_actor, city: str, country: str, - street: str, streetnumber: str, zip_code: str): - super(Address, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.city = city - self.country = country - self.street = street - self.streetnumber = streetnumber - self.zip_code = zip_code + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + + self.city = None + self.country = None + self.street = None + self.streetnumber = None + self.zip = None + self.__json_out = dict() + + def to_json(self): + self.__json_out = super().to_json() + if self.city is not None: + self.__json_out["city"] = self.city + if self.country is not None: + self.__json_out["country"] = self.country + if self.street is not None: + self.__json_out["street"] = self.street + if self.streetnumber is not None: + self.__json_out["streetnumber"] = self.streetnumber + if self.zip is not None: + self.__json_out["zip"] = self.zip + + return self.__json_out diff --git a/ml/ml40/features/properties/values/dimensions.py b/ml/ml40/features/properties/values/dimensions.py index 1df6e15..774d938 100644 --- a/ml/ml40/features/properties/values/dimensions.py +++ b/ml/ml40/features/properties/values/dimensions.py @@ -2,11 +2,26 @@ class Dimensions(Value): - def __init__(self, name, ref_managing_actor, - height: float, length: float, weight: float, width: float): - super(Dimensions, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.height = height - self.length = length - self.weight = weight - self.width = width + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + + self.height = None + self.length = None + self.weight = None + self.width = None + self.__json_out = dict() + + def to_json(self): + self.__json_out = super().to_json() + if self.height is not None: + self.__json_out["height"] = self.height + if self.length is not None: + self.__json_out["length"] = self.length + if self.weight is not None: + self.__json_out["weight"] = self.weight + if self.width is not None: + self.__json_out["width"] = self.width + + return self.__json_out diff --git a/ml/ml40/features/properties/values/documents/document.py b/ml/ml40/features/properties/values/documents/document.py index abb8fed..60ad0bc 100644 --- a/ml/ml40/features/properties/values/documents/document.py +++ b/ml/ml40/features/properties/values/documents/document.py @@ -2,6 +2,7 @@ class Document(Value): - def __init__(self, name, ref_managing_actor): - super(Document, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) \ No newline at end of file diff --git a/ml/ml40/features/properties/values/documents/jobs/generic_job.py b/ml/ml40/features/properties/values/documents/jobs/generic_job.py index ebfe435..8652981 100644 --- a/ml/ml40/features/properties/values/documents/jobs/generic_job.py +++ b/ml/ml40/features/properties/values/documents/jobs/generic_job.py @@ -2,7 +2,25 @@ class GenericJob(Job): - def __init__(self, name, ref_managing_actor, content: str): - super(GenericJob, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.content = content + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.__content = None + self.__json_out = dict() + + + @property + def content(self): + return self.__content + + @content.setter + def content(self, value): + self.__content = value + + def to_json(self): + self.__json_out = super().to_json() + if self.content is not None: + self.__json_out["content"] = self.content + + return self.__json_out diff --git a/ml/ml40/features/properties/values/documents/jobs/job.py b/ml/ml40/features/properties/values/documents/jobs/job.py index 15553d0..3d47ca8 100644 --- a/ml/ml40/features/properties/values/documents/jobs/job.py +++ b/ml/ml40/features/properties/values/documents/jobs/job.py @@ -3,25 +3,23 @@ class Job(Document): - 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 + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.__status = None + self.__json_out = dict() @property def status(self): return self.__status @status.setter - def status(self, value): + def status(self, value: JobStatus): self.__status = value def to_json(self): - temp_dict = super().to_json() - if self.status: - _dict = temp_dict.copy() - _dict.update({ - "status": self.status - }) - return _dict - return temp_dict + self.__json_out = super().to_json() + if self.status is not None: + self.__json_out["status"] = self.status + return self.__json_out diff --git a/ml/ml40/features/properties/values/documents/jobs/job_list.py b/ml/ml40/features/properties/values/documents/jobs/job_list.py index e168497..202d339 100644 --- a/ml/ml40/features/properties/values/documents/jobs/job_list.py +++ b/ml/ml40/features/properties/values/documents/jobs/job_list.py @@ -2,10 +2,12 @@ class JobList(Job): - def __init__(self, name, ref_managing_actor): - super(JobList, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) self.__jobs = list() + self.__json_out = dict() @property def jobs(self): @@ -13,4 +15,10 @@ def jobs(self): @jobs.setter def jobs(self, value): - self.__jobs.append(value) \ No newline at end of file + self.__jobs.append(value) + + def to_json(self): + self.__json_out = super().to_json() + if self.jobs: + self.__json_out["jobs"] = self.jobs + return self.__json_out \ No newline at end of file diff --git a/ml/ml40/features/properties/values/generic_property.py b/ml/ml40/features/properties/values/generic_property.py index 6f7c7f6..23f8dcb 100644 --- a/ml/ml40/features/properties/values/generic_property.py +++ b/ml/ml40/features/properties/values/generic_property.py @@ -2,9 +2,16 @@ class GenericProperty(Value): - def __init__(self, name, ref_managing_actor, value: str): - super(GenericProperty, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.value = value - if not isinstance(value, str): - raise TypeError("except str type") + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.value = None + self.__json_out = dict() + + def to_json(self): + self.__json_out = super().to_json() + if self.value is not None: + self.__json_out["value"] = self.value + + return self.__json_out diff --git a/ml/ml40/features/properties/values/liquid_filling_level.py b/ml/ml40/features/properties/values/liquid_filling_level.py index 3cd20de..99b4c73 100644 --- a/ml/ml40/features/properties/values/liquid_filling_level.py +++ b/ml/ml40/features/properties/values/liquid_filling_level.py @@ -2,9 +2,20 @@ class LiquidFillingLevel(Value): - def __init__(self, name, ref_managing_actor, - currentLevel: float, maxLevel: float): - super(LiquidFillingLevel, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.currentLevel = currentLevel - self.maxLevel = maxLevel + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + + self.currentLevel = None + self.maxLevel = None + self.__json_out = dict() + + def to_json(self): + self.__json_out = super().to_json() + if self.currentLevel is not None: + self.__json_out["currentLevel"] = self.currentLevel + if self.maxLevel is not None: + self.__json_out["maxLevel"] = self.maxLevel + return self.__json_out + diff --git a/ml/ml40/features/properties/values/location.py b/ml/ml40/features/properties/values/location.py index e611c5b..624eab7 100644 --- a/ml/ml40/features/properties/values/location.py +++ b/ml/ml40/features/properties/values/location.py @@ -2,11 +2,14 @@ class Location(Value): - def __init__(self, name, ref_managing_actor, longitude=0, latitude=0, orientation=0): - super(Location, self).__init__(name=name, ref_managing_actor=ref_managing_actor) - self.__latitude = latitude - self.__longitude = longitude - self.__orientation = orientation + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.__latitude = None + self.__longitude = None + self.__orientation = None + self.__json_out = dict() @property def latitude(self): @@ -32,8 +35,13 @@ def orientation(self): def orientation(self, value): self.__orientation = value - def from_json(self, json_obj): - super().from_json(json_obj) - self.__latitude = json_obj.get("latitude", "") - self.__longitude = json_obj.get("longitude", "") - self.__orientation = json_obj.get("orientation", "") + def to_json(self): + self.__json_out = super().to_json() + if self.latitude is not None: + self.__json_out["latitude"] = self.latitude + if self.longitude is not None: + self.__json_out["longitude"] = self.longitude + if self.orientation is not None: + self.__json_out["orientation"] = self.orientation + + return self.to_json() \ No newline at end of file diff --git a/ml/ml40/features/properties/values/moisture.py b/ml/ml40/features/properties/values/moisture.py index 7a4f82a..e298ea2 100644 --- a/ml/ml40/features/properties/values/moisture.py +++ b/ml/ml40/features/properties/values/moisture.py @@ -2,10 +2,12 @@ class Moisture(Value): - def __init__(self, name, ref_managing_actor): - super(Moisture, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.__humidity = 0 + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.__humidity = None + self.__join_out = dict() @property def humidity(self): @@ -15,6 +17,8 @@ def humidity(self): def humidity(self, value): self.__humidity = value - def from_json(self, json_obj): - super().from_json(json_obj) - self.__humidity = json_obj.get("humidity", 0) + def to_json(self): + self.__join_out = super().to_json() + if self.humidity is not None: + self.__join_out["humidity"] = self.humidity + return self.__join_out diff --git a/ml/ml40/features/properties/values/personal_name.py b/ml/ml40/features/properties/values/personal_name.py index 382d84d..5bdb4b1 100644 --- a/ml/ml40/features/properties/values/personal_name.py +++ b/ml/ml40/features/properties/values/personal_name.py @@ -2,9 +2,21 @@ class PersonalName(Value): - def __init__(self, name, ref_managing_actor, firstname: str, - lastname: str): - super(PersonalName, - self).__init__(name=name, ref_managing_actor=ref_managing_actor) - self.firstname = firstname - self.lastname = lastname + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + + self.firstname = None + self.lastname = None + self.__json_out = dict() + + def to_json(self): + self.__json_out = super().to_json() + if self.firstname is not None: + self.__json_out["firstname"] = self.firstname + + if self.lastname is not None: + self.__json_out["lastname"] = self.lastname + + return self.__json_out \ No newline at end of file diff --git a/ml/ml40/features/properties/values/rotational_speed.py b/ml/ml40/features/properties/values/rotational_speed.py index 061c133..5ce0e80 100644 --- a/ml/ml40/features/properties/values/rotational_speed.py +++ b/ml/ml40/features/properties/values/rotational_speed.py @@ -2,10 +2,13 @@ class RotationalSpeed(Value): - def __init__(self, name, ref_managing_actor, rpm=0): - super(RotationalSpeed, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.__rpm = rpm + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + + self.__rpm = None + self.__json_out = dict() @property def rpm(self): @@ -14,3 +17,9 @@ def rpm(self): @rpm.setter def rpm(self, value): self.__rpm = value + + def to_json(self): + self.__json_out = super().to_json() + if self.rpm is not None: + self.__json_out["rpm"] = self.rpm + return self.__json_out diff --git a/ml/ml40/features/properties/values/route.py b/ml/ml40/features/properties/values/route.py index cc00094..a13920b 100644 --- a/ml/ml40/features/properties/values/route.py +++ b/ml/ml40/features/properties/values/route.py @@ -2,4 +2,7 @@ class Route(Value): - pass + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) diff --git a/ml/ml40/features/properties/values/temperature.py b/ml/ml40/features/properties/values/temperature.py index b6a52c7..56d6cbb 100644 --- a/ml/ml40/features/properties/values/temperature.py +++ b/ml/ml40/features/properties/values/temperature.py @@ -2,10 +2,12 @@ class Temperature(Value): - def __init__(self, name, ref_managing_actor, temperature=0): - super(Temperature, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.__temperature = temperature + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.__temperature = None + self.__json_out = dict() @property def temperature(self): @@ -18,6 +20,12 @@ def temperature(self, value): else: raise TypeError + def to_json(self): + self.__json_out = super().to_json() + if self.temperature is not None: + self.__json_out["temperature"] = self.temperature + return self.__json_out + diff --git a/ml/ml40/features/properties/values/time_slot.py b/ml/ml40/features/properties/values/time_slot.py index dd7e020..fcd16ae 100644 --- a/ml/ml40/features/properties/values/time_slot.py +++ b/ml/ml40/features/properties/values/time_slot.py @@ -2,9 +2,18 @@ class TimeSlot(Value): - def __init__(self, name, ref_managing_actor, - end: int, start: int): - super(TimeSlot, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) - self.end = end - self.start = start + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.end = None + self.start = None + self.__json_out = dict() + + def to_json(self): + self.__json_out = super().to_json() + if self.end is not None: + self.__json_out["end"] = self.end + if self.start is not None: + self.__json_out["start"] = self.start + return self.__json_out diff --git a/ml/ml40/features/properties/values/value.py b/ml/ml40/features/properties/values/value.py index 6330882..ed7a242 100644 --- a/ml/ml40/features/properties/values/value.py +++ b/ml/ml40/features/properties/values/value.py @@ -1,35 +1,34 @@ from ml.ml40.features.properties.property import Property -import copy class Value(Property): - def __init__(self, name, ref_managing_actor): - super(Value, self).__init__(name=name, ref_managing_actor=ref_managing_actor) - self._valid_to = "" - self._valid_from = "" + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + self.__valid_to = "" + self.__valid_from = "" + #TODO validate the datetype of valid from and valid to + self.__json_out = dict() @property def valid_to(self): - return self._valid_to + return self.__valid_to - def set_valid_to(self, valid_to): - self._valid_to = valid_to + @valid_to.setter + def valid_to(self, value): + self.__valid_to = value @property def valid_from(self): - return self._valid_from + return self.__valid_from - def set_valid_from(self, valid_from): - self._valid_from = valid_from + def valid_from(self, value): + self.__valid_from = value def to_json(self): - temp_dict = super().to_json() + self.__json_out = super().to_json() if self.valid_from and self.valid_to: - _dict = temp_dict.copy() - _dict.update({ - "value_to": self.valid_to, - "value_from": self.valid_from - }) - return _dict - - return temp_dict + self.__json_out["valid_from"] = self.valid_from + self.__json_out["valid_to"] = self.valid_to + return self.__json_out diff --git a/ml/ml40/features/properties/values/weight.py b/ml/ml40/features/properties/values/weight.py index 2c6fdd3..a51eed0 100644 --- a/ml/ml40/features/properties/values/weight.py +++ b/ml/ml40/features/properties/values/weight.py @@ -2,17 +2,25 @@ class Weight(Value): - def __init__(self, name, ref_managing_actor): - super(Weight, self).__init__(name=name, ref_managing_actor=ref_managing_actor) - self._weight = 0 + def __init__(self, name="", identifier=""): + super().__init__( + name=name, + identifier=identifier) + + self.__weight = None + self.__json_out = dict() @property def weight(self): - return self._weight + return self.__weight + + @weight.setter + def weight(self, value): + self.__weight = value - def get_weight(self): - return self._weight + def to_json(self): + self.__json_out = super().to_json() + if self.weight is not None: + self.__json_out["weight"] = self.weight + return self.__json_out - def from_json(self, json_obj): - super().from_json(json_obj) - self._weight = json_obj.get("weight", 0) diff --git a/ml/ml40/roles/dts/dt.py b/ml/ml40/roles/dts/dt.py index 601c9c5..f4d29f2 100644 --- a/ml/ml40/roles/dts/dt.py +++ b/ml/ml40/roles/dts/dt.py @@ -1,7 +1,11 @@ -from ml.ml40.roles.role import Role +from ml.role import Role class DT(Role): - def __init__(self, name, ref_managing_actor): - super(DT, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super(DT, self).__init__( + name=name, + identifier=identifier) + + + diff --git a/ml/ml40/roles/dts/handheld_devices/handheld_device.py b/ml/ml40/roles/dts/handheld_devices/handheld_device.py index 9e2b293..a5eadec 100644 --- a/ml/ml40/roles/dts/handheld_devices/handheld_device.py +++ b/ml/ml40/roles/dts/handheld_devices/handheld_device.py @@ -2,6 +2,9 @@ class HandheldDevice(DT): - pass + def __init__(self, name="", identifier=""): + super(HandheldDevice, self).__init__( + name=name, + identifier=identifier) diff --git a/ml/ml40/roles/dts/machines/machine.py b/ml/ml40/roles/dts/machines/machine.py index ee30508..9d3834d 100644 --- a/ml/ml40/roles/dts/machines/machine.py +++ b/ml/ml40/roles/dts/machines/machine.py @@ -2,8 +2,8 @@ class Machine(DT): - def __init__(self, name, ref_managing_actor): - super(Machine, self).__init__( + def __init__(self, name="", identifier=""): + super(DT, self).__init__( name=name, - ref_managing_actor=ref_managing_actor - ) + identifier=identifier) + diff --git a/ml/ml40/roles/dts/parts/crane.py b/ml/ml40/roles/dts/parts/crane.py index 5069bab..d4a48c5 100644 --- a/ml/ml40/roles/dts/parts/crane.py +++ b/ml/ml40/roles/dts/parts/crane.py @@ -2,5 +2,8 @@ class Crane(Part): - pass + def __init__(self, name="", identifier=""): + super(Crane, self).__init__( + name=name, + identifier=identifier) diff --git a/ml/ml40/roles/dts/parts/engine.py b/ml/ml40/roles/dts/parts/engine.py index 0d4d25f..a9522a7 100644 --- a/ml/ml40/roles/dts/parts/engine.py +++ b/ml/ml40/roles/dts/parts/engine.py @@ -2,4 +2,7 @@ class Engine(Part): - pass + def __init__(self, name="", identifier=""): + super(Engine, self).__init__( + name=name, + identifier=identifier) diff --git a/ml/ml40/roles/dts/parts/part.py b/ml/ml40/roles/dts/parts/part.py index d34bcb6..ec43c35 100644 --- a/ml/ml40/roles/dts/parts/part.py +++ b/ml/ml40/roles/dts/parts/part.py @@ -2,4 +2,9 @@ class Part(DT): - pass \ No newline at end of file + def __init__(self, name="", identifier=""): + super(Part, self).__init__( + name=name, + identifier=identifier) + + diff --git a/ml/ml40/roles/dts/parts/scale.py b/ml/ml40/roles/dts/parts/scale.py index fbcb8e1..c722a9d 100644 --- a/ml/ml40/roles/dts/parts/scale.py +++ b/ml/ml40/roles/dts/parts/scale.py @@ -2,4 +2,7 @@ class Scale(Part): - pass \ No newline at end of file + def __init__(self, name="", identifier=""): + super(Scale, self).__init__( + name=name, + identifier=identifier) \ No newline at end of file diff --git a/ml/ml40/roles/dts/parts/tank.py b/ml/ml40/roles/dts/parts/tank.py index ef74695..1a5a2f0 100644 --- a/ml/ml40/roles/dts/parts/tank.py +++ b/ml/ml40/roles/dts/parts/tank.py @@ -2,4 +2,7 @@ class Tank(Part): - pass \ No newline at end of file + def __init__(self, name="", identifier=""): + super(Tank, self).__init__( + name=name, + identifier=identifier) \ No newline at end of file diff --git a/ml/ml40/roles/dts/persons/machine_operator.py b/ml/ml40/roles/dts/persons/machine_operator.py index 74d9d6a..c334bb9 100644 --- a/ml/ml40/roles/dts/persons/machine_operator.py +++ b/ml/ml40/roles/dts/persons/machine_operator.py @@ -2,4 +2,7 @@ class MachineOperator(Person): - pass + def __init__(self, name="", identifier=""): + super(MachineOperator, self).__init__( + name=name, + identifier=identifier) diff --git a/ml/ml40/roles/dts/persons/person.py b/ml/ml40/roles/dts/persons/person.py index 7c9a86b..5f957ae 100644 --- a/ml/ml40/roles/dts/persons/person.py +++ b/ml/ml40/roles/dts/persons/person.py @@ -2,4 +2,7 @@ class Person(DT): - pass \ No newline at end of file + def __init__(self, name="", identifier=""): + super(Person, self).__init__( + name=name, + identifier=identifier) \ No newline at end of file diff --git a/ml/ml40/roles/dts/sensors/air_sensor.py b/ml/ml40/roles/dts/sensors/air_sensor.py index d31cf62..87b7635 100644 --- a/ml/ml40/roles/dts/sensors/air_sensor.py +++ b/ml/ml40/roles/dts/sensors/air_sensor.py @@ -2,4 +2,7 @@ class AirSensor(Sensor): - pass \ No newline at end of file + def __init__(self, name="", identifier=""): + super(AirSensor, self).__init__( + name=name, + identifier=identifier) \ No newline at end of file diff --git a/ml/ml40/roles/dts/sensors/sensor.py b/ml/ml40/roles/dts/sensors/sensor.py index e23c948..e941261 100644 --- a/ml/ml40/roles/dts/sensors/sensor.py +++ b/ml/ml40/roles/dts/sensors/sensor.py @@ -2,4 +2,7 @@ class Sensor(DT): - pass \ No newline at end of file + def __init__(self, name="", identifier=""): + super(Sensor, self).__init__( + name=name, + identifier=identifier) \ No newline at end of file diff --git a/ml/ml40/roles/dts/sensors/soil_sensor.py b/ml/ml40/roles/dts/sensors/soil_sensor.py index c2179fa..e72411b 100644 --- a/ml/ml40/roles/dts/sensors/soil_sensor.py +++ b/ml/ml40/roles/dts/sensors/soil_sensor.py @@ -2,4 +2,7 @@ class SoilSensor(Sensor): - pass \ No newline at end of file + def __init__(self, name="", identifier=""): + super(SoilSensor, self).__init__( + name=name, + identifier=identifier) \ No newline at end of file diff --git a/ml/ml40/roles/dts/sites/site.py b/ml/ml40/roles/dts/sites/site.py index 450c9d9..5242a2c 100644 --- a/ml/ml40/roles/dts/sites/site.py +++ b/ml/ml40/roles/dts/sites/site.py @@ -2,4 +2,7 @@ class Site(DT): - pass \ No newline at end of file + def __init__(self, name="", identifier=""): + super(Site, self).__init__( + name=name, + identifier=identifier) \ No newline at end of file diff --git a/ml/ml40/roles/dts/ways/way.py b/ml/ml40/roles/dts/ways/way.py index 77168fe..04db20a 100644 --- a/ml/ml40/roles/dts/ways/way.py +++ b/ml/ml40/roles/dts/ways/way.py @@ -2,4 +2,7 @@ class Way(DT): - pass + def __init__(self, name="", identifier=""): + super(Way, self).__init__( + name=name, + identifier=identifier) diff --git a/ml/ml40/roles/hmis/app.py b/ml/ml40/roles/hmis/app.py index d8e068b..e4b9b68 100644 --- a/ml/ml40/roles/hmis/app.py +++ b/ml/ml40/roles/hmis/app.py @@ -2,7 +2,7 @@ class App(HMI): - def __init__(self, name, ref_managing_actor): + def __init__(self, name="", identifier=""): super(App, self).__init__( name=name, - ref_managing_actor=ref_managing_actor) + identifier=identifier) \ No newline at end of file diff --git a/ml/ml40/roles/hmis/dashboard.py b/ml/ml40/roles/hmis/dashboard.py index bee56fe..e387e95 100644 --- a/ml/ml40/roles/hmis/dashboard.py +++ b/ml/ml40/roles/hmis/dashboard.py @@ -2,7 +2,7 @@ class Dashboard(HMI): - def __init__(self, name, ref_managing_actor): + def __init__(self, name="", identifier=""): super(Dashboard, self).__init__( name=name, - ref_managing_actor=ref_managing_actor) + identifier=identifier) \ No newline at end of file diff --git a/ml/ml40/roles/hmis/hmd.py b/ml/ml40/roles/hmis/hmd.py index 7da563a..7691e9f 100644 --- a/ml/ml40/roles/hmis/hmd.py +++ b/ml/ml40/roles/hmis/hmd.py @@ -2,7 +2,7 @@ class HMD(HMI): - def __init__(self, name, ref_managing_actor): + def __init__(self, name="", identifier=""): super(HMD, self).__init__( name=name, - ref_managing_actor=ref_managing_actor) + identifier=identifier) diff --git a/ml/ml40/roles/hmis/hmi.py b/ml/ml40/roles/hmis/hmi.py index 8d4516f..5885c3c 100644 --- a/ml/ml40/roles/hmis/hmi.py +++ b/ml/ml40/roles/hmis/hmi.py @@ -1,7 +1,8 @@ -from ml.ml40.roles.role import Role +from ml.role import Role class HMI(Role): - def __init__(self, name, ref_managing_actor): - super(HMI, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super(HMI, self).__init__( + name=name, + identifier=identifier) diff --git a/ml/ml40/roles/hmis/machine_ui.py b/ml/ml40/roles/hmis/machine_ui.py index fea821f..50616d8 100644 --- a/ml/ml40/roles/hmis/machine_ui.py +++ b/ml/ml40/roles/hmis/machine_ui.py @@ -2,6 +2,7 @@ class MachineUI(HMI): - def __init__(self, name, ref_managing_actor): - super(MachineUI, self).__init__(name=name, - ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super(MachineUI, self).__init__( + name=name, + identifier=identifier) \ No newline at end of file diff --git a/ml/ml40/roles/role.py b/ml/ml40/roles/role.py deleted file mode 100644 index ce4fcfb..0000000 --- a/ml/ml40/roles/role.py +++ /dev/null @@ -1,16 +0,0 @@ -from ml.managed_actor import ManagedActor - - -class Role(ManagedActor): - def __init__(self, name, ref_managing_actor): - super(Role, self).__init__(name, ref_managing_actor) - self.__name = name - - @property - def name(self): - return self.__name - - @name.setter - def name(self, value): - self.__name = value - diff --git a/ml/ml40/roles/servives/service.py b/ml/ml40/roles/servives/service.py index 3833312..09ed403 100644 --- a/ml/ml40/roles/servives/service.py +++ b/ml/ml40/roles/servives/service.py @@ -1,6 +1,8 @@ -from ml.ml40.roles.role import Role +from ml.role import Role class Service(Role): - def __init__(self, name, ref_managing_actor): - super(Service, self).__init__(name=name, ref_managing_actor=ref_managing_actor) + def __init__(self, name="", identifier=""): + super(Service, self).__init__( + name=name, + identifier=identifier) \ No newline at end of file diff --git a/ml/role.py b/ml/role.py new file mode 100644 index 0000000..6711001 --- /dev/null +++ b/ml/role.py @@ -0,0 +1,41 @@ +from ml.identifier import ID +from abc import ABC +import sys + +class Role(ABC): + def __init__(self, name="", identifier=""): + self.__identifier = ID(identifier).identifier + self.__name = name + if "ml/fml40" in sys.modules[self.__class__.__module__].__file__: + self.__class_name = "fml40::{}".format(self.__class__.__name__) + else: + self.__class_name = "ml40::{}".format(self.__class__.__name__) + + self.__json_out = dict() + + @property + def class_name(self): + return self.__class_name + + @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): + self.__identifier = ID(value).identifier + + def to_json(self): + self.__json_out = { + "class": self.__class_name, + "identifier": self.__identifier, + } + return self.__json_out diff --git a/ml/thing.py b/ml/thing.py index 4a8cdda..a4df064 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -3,16 +3,14 @@ import websocket import json import uuid -import requests -from s3i import IdentityProvider, TokenType, GetValueReply, Directory -from s3i.broker import Broker, BrokerREST, BrokerMetaClass +from s3i import IdentityProvider, TokenType, GetValueReply, Directory, Repository +from s3i.broker import Broker, BrokerREST from s3i.messages import ServiceReply -from ml.tools import remove_namespace -import time -from ml.managing_actor import ManagingActor 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): @@ -24,7 +22,7 @@ class BaseVariable(object): DIR_URL = "https://dir.s3i.vswf.dev/api/2/" -class Thing(ManagingActor): +class Thing: def __init__( self, model: dict, @@ -36,7 +34,6 @@ def __init__( username=None, password=None, ): - super(Thing, self).__init__() self.__model = model self.__thing_id = model.get("thingId", "") self.__policy_id = model.get("policyId", "") @@ -57,12 +54,16 @@ def __init__( self.broker = None self.ws = None self.dir = None + self.repo = None + + self.repo_json = dict() + self.dir_json = dict() attributes = model.get("attributes", None) self.__name = "" self.__roles = {} self.__features = {} - + self.__resGetValue = list() if attributes: self.__name = attributes.get("name", "") @@ -114,6 +115,34 @@ def run_forever(self): print("[S³I]: Launch {}{}{}".format(BColors.OKGREEN, self.name, BColors.ENDC)) self.__connect_with_idp() + threading.Thread(target=self.__dir_syn).start() + if self.__is_repo: + threading.Thread(target=self.__repo_syn).start() + + @staticmethod + def add_user_def(func): + threading.Thread(target=func).start() + + def __dir_syn(self): + while True: + time.sleep(0.1) + 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) + + def __repo_syn(self): + while self.__is_repo: + time.sleep(0.1) + old_repo_json = self.repo_json + self.to_repo_json() + if self.repo_json == old_repo_json: + continue + else: + self.repo.updateThingIDBased(thingID=self.thing_id, payload=self.repo_json) + def __connect_with_idp(self): print(BColors.OKBLUE + "[S³I][IdP]" + BColors.ENDC + ": Connect with S3I-IdentityProvider") idp = IdentityProvider( @@ -128,15 +157,14 @@ def __connect_with_idp(self): # This may take a while so fetch token directly. idp.run_forever(token_type=TokenType.ACCESS_TOKEN, on_new_token=self.__on_token) - time.sleep(1) def __on_token(self, token): self.__access_token = token self.__connect_with_dir() + self.__connect_with_repo() if self.__is_broker: self.__connect_with_broker() - if self.__is_repo: - self.__connect_with_repo() + def __connect_with_dir(self): print( @@ -145,7 +173,7 @@ def __connect_with_dir(self): + BColors.ENDC + ": Connect with S3I-Directory" ) - self.dir = Directory(s3i_dir_url="https://dir.s3i.vswf.dev/api/2/", token=self.__access_token) + self.dir = Directory(s3i_dir_url=BaseVariable.DIR_URL, token=self.__access_token) def __connect_with_repo(self): print( @@ -154,6 +182,7 @@ 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._ws = websocket.WebSocketApp( BaseVariable.REPO_WWS_URL, @@ -194,15 +223,15 @@ def __on_websocket_connection_closed(self): ) def sync_with_repo(self, path, topic): + #TODO if not self.__ws_connected: return None - msg = {"topic": topic, "path": path, "value": self.fml40_data_model[path]} + msg = {"topic": topic, "path": path, "value": self.repo_json[path]} self._ws.send(json.dumps(msg)) def __connect_with_broker(self): 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) @@ -214,13 +243,10 @@ def receive(): else: self.__on_broker_callback(ch=None, method=None, properties=None, body=json.loads(msg_str)) - threading.Thread( - target=receive, + target=receive ).start() - - else: self.broker = Broker( auth_form="Username/Password", @@ -238,10 +264,8 @@ def __on_broker_callback(self, ch, method, properties, body): if isinstance(body, bytes): body = ast.literal_eval(body.decode("utf-8")) elif isinstance(body, int): - print(body) return elif isinstance(body, str): - print(body) return message_type = body.get("messageType") @@ -260,40 +284,142 @@ def __on_broker_callback(self, ch, method, properties, body): pass def on_user_message(self, msg): - pass + print( + BColors.OKBLUE + + "[S³I][Broker]" + + BColors.ENDC + + ": You have received a user message" + + json.dumps(msg, indent=2) + ) def on_get_value_request(self, msg): - # TODO implement get value request - pass + get_value_reply = GetValueReply() + request_sender = msg.get("sender") + request_msg_id = msg.get("identifier") + request_sender_endpoint = msg.get("replyToEndpoint") + attribute_path = msg.get("attributePath") + reply_msg_uuid = "s3i:" + str(uuid.uuid4()) + try: + value = self._uriToData(attribute_path) + except KeyError: + value = "invalid attribute path" + + get_value_reply.fillGetValueReply( + senderUUID=self.thing_id, + receiverUUID=[request_sender], + results=json.dumps(value), + msgUUID=reply_msg_uuid, + replyingToUUID=request_msg_id, + ) + self.broker.send( + receiver_endpoints=[request_sender_endpoint], + msg=json.dumps(get_value_reply.msg), + ) + + def _uriToData(self, uri): + if uri == "": + return self.repo_json + else: + uri_list = uri.split("/") + if uri_list[0] == "features": + try: + return self.repo_json[uri] + except KeyError: + return "invalid attribute path" + + try: + self._getValue(self.repo_json, uri_list) + except: + return "invalid attribute path" + if self.__resGetValue.__len__() == 0: + return "invalid attribute path" + response = copy.deepcopy(self.__resGetValue) + self.__resGetValue.clear() + if response.__len__() == 1: + return response[0] + else: + return response + + def _getValue(self, source, uri_list): + value = source[uri_list[0]] + if uri_list.__len__() == 1: + # if is ditto-feature + if isinstance(value, str): + try: + stringValue_split = value.split(":") + if stringValue_split[0] == "ditto-feature": + value = self.repo_json["features"][stringValue_split[1]]["properties"][uri_list[0]] + except: + pass + self.__resGetValue.append(value) + return + if isinstance(value, dict): + del uri_list[0] + self._getValue(value, uri_list) + if isinstance(value, list): + if isinstance(value[0], (str, int, float, bool, list)): + return value + if isinstance(value[0], dict): + for item in value: + if item["class"] == "ml40::Thing": + for i in item["roles"]: + if self._findValue(i, uri_list[1]): + uri_list_1 = copy.deepcopy(uri_list) + del uri_list_1[:2] + self._getValue(item, uri_list_1) + else: + if self._findValue(item, uri_list[1]): + uri_list_1 = copy.deepcopy(uri_list) + del uri_list_1[:2] + if not uri_list_1: + self.__resGetValue.append(item) + return + else: + self._getValue(item, uri_list_1) + if isinstance(value, (str, int, float, bool)): + # if is ditto-feature + if isinstance(value, str): + try: + stringValue_split = value.split(":") + if stringValue_split[0] == "ditto-feature": + value = self.repo_json["features"][stringValue_split[1]][ + "properties" + ][uri_list[0]] + except: + pass + self.__resGetValue.append(value) + + def _findValue(self, json, value): + for item in json: + if json[item] == value: + # print("Parameter: ", json[item]) + return True + return False def on_service_request(self, body_json): service_type = body_json.get("serviceType") parameters = body_json.get("parameters") service_functionality = service_type.split('/')[0] - service_functionality_obj = self.features.get(service_functionality, None) + 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) ) else: - pass # TODO: Call right functionality. - func_proxy = service_functionality_obj.proxy() - method = getattr(func_proxy, service_type.split('/')[1]) + method = getattr(service_functionality_obj, service_type.split('/')[1]) + result = method(**parameters) - result = method(**parameters).get() if isinstance(result, bool): - results = {"ok": result} - else: - results = result + result = {"ok": result} service_reply = ServiceReply() service_reply.fillServiceReply( senderUUID=self.thing_id, receiverUUID=body_json.get("sender", None), serviceType=body_json.get("serviceType", None), - results=results, + results=result, replyingToUUID=body_json.get("identifier", None), msgUUID="s3i:{}".format(uuid.uuid4()) ) @@ -301,20 +427,78 @@ def on_service_request(self, body_json): for r in service_reply.msg.get("receivers", None): receiver_ep = find_broker_endpoint(self.dir, r) receiver_eps.append(receiver_ep) - self.broker.send(receiver_endpoints=receiver_eps, msg=json.dumps(service_reply.msg)) def on_get_value_reply(self, msg): - print(msg) + print( + BColors.OKBLUE + + "[S³I][Broker]" + + BColors.ENDC + + ": You have received a get value reply" + + json.dumps(msg, indent=2) + ) def on_service_reply(self, msg): - print(BColors.OKBLUE + "[S³I]: You have received a service reply: " + BColors.ENDC + json.dumps(msg, indent=2)) + print( + BColors.OKBLUE + + "[S³I][Broker]" + + BColors.ENDC + + ": You have received a service reply" + + json.dumps(msg, indent=2) + ) + + def to_dir_json(self): + self.dir_json = self.dir.queryThingIDBased(self.thing_id) + if self.thing_id is not None: + self.dir_json["thingId"] = self.thing_id + if self.policy_id is not None: + self.dir_json["policyId"] = self.policy_id + self.dir_json["attributes"]["class"] = "ml40::Thing" + + self.dir_json["attributes"]["name"] = self.name + if self.roles: + self.dir_json["attributes"]["roles"] = list() + if self.features: + self.dir_json["attributes"]["features"] = list() + + for key in self.roles.keys(): + self.dir_json["attributes"]["roles"].append(self.roles[key].to_json()) + for key in self.features.keys(): + self.dir_json["attributes"]["features"].append(self.features[key].to_json()) + + return self.dir_json + + def to_repo_json(self, path=None, value=None): + if path is None and value is None: + self.repo_json = { + "thingId": self.thing_id, + "policyId": self.policy_id, + "attributes": { + "class": "ml40::Thing", + "name": self.name, + } + } + if self.roles: + self.repo_json["attributes"]["roles"] = list() + if self.features: + self.repo_json["attributes"]["features"] = list() + for key in self.roles.keys(): + self.repo_json["attributes"]["roles"].append(self.roles[key].to_json()) + for key in self.features: + self.repo_json["attributes"]["features"].append(self.features[key].to_json()) + return self.repo_json + + def to_subthing_json(self): + json_out = { + "class": "ml40::Thing", + "name": self.name, + "roles": [], + "features": [] + } + for key in self.roles.keys(): + json_out["roles"].append(self.roles[key].to_json()) + for key in self.features.keys(): + json_out["features"].append(self.features[key].to_json()) + return json_out + - 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] = impl_actor.start(feature_name, self.actor_ref) diff --git a/requirements.txt b/requirements.txt index 3152cdd..daad432 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ https://git.rwth-aachen.de/kwh40/s3i/-/jobs/artifacts/master/raw/public/s3i-0.4-py3-none-any.whl?job=wheel -Pykka +Pykka==2.0.2 websocket-client==0.57.0 pynput opcua diff --git a/tests/test_benedict.py b/tests/test_benedict.py new file mode 100644 index 0000000..979b174 --- /dev/null +++ b/tests/test_benedict.py @@ -0,0 +1,20 @@ +from benedict import benedict + + +class MyDict(benedict): + def __init__(self, init_dict): + for k, v in init_dict.items(): + if isinstance(v, dict): + init_dict[k] = MyDict(v) + super().__init__(init_dict) + + def __setitem__(self, key, value): + print("dict set") + if isinstance(value, dict): + _value = MyDict(value) + else: + _value = value + + super().__setitem__(key, value) + + diff --git a/tests/test_threading_futhur.py b/tests/test_threading_futhur.py new file mode 100644 index 0000000..8fdbc9a --- /dev/null +++ b/tests/test_threading_futhur.py @@ -0,0 +1,10 @@ +import pykka +a = pykka.ThreadingFuture() +b = pykka.ThreadingFuture() +c = pykka.ThreadingFuture() +f = a.join(b, c) +a.set('def') +b.set(123) +c.set(False) +b.set(345) +print(f.get()) -- GitLab From 50cfaf4876cced88bcbb3f6052921865c38e020b Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 26 Oct 2020 21:37:47 +0100 Subject: [PATCH 47/84] fix bug due to access to expiring --- ml/thing.py | 59 +++++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/ml/thing.py b/ml/thing.py index a4df064..179bd39 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -125,23 +125,29 @@ def add_user_def(func): def __dir_syn(self): while True: - time.sleep(0.1) - old_dir_json = self.dir_json - self.to_dir_json() - if self.dir_json == old_dir_json: + try: + time.sleep(0.1) + 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 - else: - self.dir.updateThingIDBased(thingID=self.thing_id, payload=self.dir_json) def __repo_syn(self): while self.__is_repo: - time.sleep(0.1) - old_repo_json = self.repo_json - self.to_repo_json() - if self.repo_json == old_repo_json: + try: + time.sleep(0.1) + old_repo_json = self.repo_json + self.to_repo_json() + if self.repo_json == old_repo_json: + continue + else: + self.repo.updateThingIDBased(thingID=self.thing_id, payload=self.repo_json) + except: continue - else: - self.repo.updateThingIDBased(thingID=self.thing_id, payload=self.repo_json) def __connect_with_idp(self): print(BColors.OKBLUE + "[S³I][IdP]" + BColors.ENDC + ": Connect with S3I-IdentityProvider") @@ -165,7 +171,6 @@ def __on_token(self, token): if self.__is_broker: self.__connect_with_broker() - def __connect_with_dir(self): print( BColors.OKBLUE @@ -223,7 +228,7 @@ def __on_websocket_connection_closed(self): ) def sync_with_repo(self, path, topic): - #TODO + # TODO if not self.__ws_connected: return None msg = {"topic": topic, "path": path, "value": self.repo_json[path]} @@ -237,12 +242,17 @@ def __connect_with_broker(self): def receive(): while True: - msg_str = self.broker.receive_once(self.__endpoint) - if msg_str == "": + try: + time.sleep(0.1) + msg_str = self.broker.receive_once(self.__endpoint) + if msg_str == "": + continue + else: + self.__on_broker_callback(ch=None, method=None, properties=None, + body=json.loads(msg_str)) + except: continue - else: - self.__on_broker_callback(ch=None, method=None, properties=None, - body=json.loads(msg_str)) + threading.Thread( target=receive ).start() @@ -423,11 +433,8 @@ def on_service_request(self, body_json): replyingToUUID=body_json.get("identifier", None), msgUUID="s3i:{}".format(uuid.uuid4()) ) - receiver_eps = list() - for r in service_reply.msg.get("receivers", None): - receiver_ep = find_broker_endpoint(self.dir, r) - receiver_eps.append(receiver_ep) - self.broker.send(receiver_endpoints=receiver_eps, msg=json.dumps(service_reply.msg)) + self.broker.send(receiver_endpoints=[body_json.get("replyToEndpoint", None)], + msg=json.dumps(service_reply.msg)) def on_get_value_reply(self, msg): print( @@ -454,7 +461,7 @@ def to_dir_json(self): if self.policy_id is not None: self.dir_json["policyId"] = self.policy_id self.dir_json["attributes"]["class"] = "ml40::Thing" - + self.dir_json["attributes"]["name"] = self.name if self.roles: self.dir_json["attributes"]["roles"] = list() @@ -500,5 +507,3 @@ def to_subthing_json(self): for key in self.features.keys(): json_out["features"].append(self.features[key].to_json()) return json_out - - -- GitLab From 447146c59440f44b012fb333a1c91fa67593168f Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 26 Oct 2020 22:26:53 +0100 Subject: [PATCH 48/84] fix feature file path bug --- ml/feature.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ml/feature.py b/ml/feature.py index 0fd8644..ffc117e 100644 --- a/ml/feature.py +++ b/ml/feature.py @@ -6,7 +6,8 @@ class Feature(ABC): def __init__(self, name="", identifier=""): self.__name = name - if "ml/fml40" in sys.modules[self.__class__.__module__].__file__: + path = sys.modules[__name__].__file__ if __name__ == "__main__" else __file__ + if "ml/fml40" in path: self.__class_name = "fml40::{}".format(self.__class__.__name__) else: self.__class_name = "ml40::{}".format(self.__class__.__name__) -- GitLab From a2fbd1d1f9bec4909bb3547ee79caf377b5032c6 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 27 Oct 2020 13:04:24 +0100 Subject: [PATCH 49/84] delete websocket --- ml/thing.py | 48 +----------------------------------------------- 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/ml/thing.py b/ml/thing.py index 179bd39..b50ef99 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -1,6 +1,5 @@ import ast import threading -import websocket import json import uuid from s3i import IdentityProvider, TokenType, GetValueReply, Directory, Repository @@ -189,51 +188,6 @@ def __connect_with_repo(self): ) self.repo = Repository(s3i_repo_url=BaseVariable.REPO_URL, token=self.__access_token) - self._ws = websocket.WebSocketApp( - BaseVariable.REPO_WWS_URL, - header={"Authorization: Bearer {}".format(self.__access_token)}, - on_message=self.__on_new_websocket_message, - on_error=self.__on_new_websocket_error, - on_open=self.__on_websocket_connection_opened, - on_close=self.__on_websocket_connection_closed, - ) - - threading.Thread(target=self._ws.run_forever).start() - - @staticmethod - def __on_new_websocket_message(ws, msg): - pass - - @staticmethod - def __on_new_websocket_error(ws, error): - print(BColors.OKBLUE + "[S³I][Repo]" + BColors.ENDC + " : Websocekt error") - - def __on_websocket_connection_opened(self): - self.__ws_connected = True - self._ws.send("START-SEND-MESSAGES") - print( - BColors.OKBLUE - + "[S³I][Repo]" - + BColors.ENDC - + ": Websocket connection built" - ) - - def __on_websocket_connection_closed(self): - self.__ws_connected = False - print( - BColors.OKBLUE - + "[S³I][Repo]" - + BColors.ENDC - + ": Websocket connection closed" - ) - - def sync_with_repo(self, path, topic): - # TODO - if not self.__ws_connected: - return None - msg = {"topic": topic, "path": path, "value": self.repo_json[path]} - self._ws.send(json.dumps(msg)) - def __connect_with_broker(self): print(BColors.OKBLUE + "[S³I][Broker]" + BColors.ENDC + ": Connect with S3I-Broker") self.__endpoint = find_broker_endpoint(self.dir, thing_id=self.thing_id) @@ -317,7 +271,7 @@ def on_get_value_request(self, msg): get_value_reply.fillGetValueReply( senderUUID=self.thing_id, receiverUUID=[request_sender], - results=json.dumps(value), + results=str(value), msgUUID=reply_msg_uuid, replyingToUUID=request_msg_id, ) -- GitLab From 7aad6d259f52e552b0d2475112fccfecff0c143a Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 27 Oct 2020 13:09:02 +0100 Subject: [PATCH 50/84] update gitignore and delete cre --- .gitignore | 5 ++++- dt_creation.py | 18 +++++++++--------- dt_creation_hmi.py | 8 +++----- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 51d1c94..e21683d 100644 --- a/.gitignore +++ b/.gitignore @@ -222,4 +222,7 @@ dmypy.json config.py # venv -venv/* \ No newline at end of file +venv/* + +logs/* +config.py \ No newline at end of file diff --git a/dt_creation.py b/dt_creation.py index 478d65c..6b0999f 100644 --- a/dt_creation.py +++ b/dt_creation.py @@ -6,14 +6,15 @@ from ml.ml40.features.properties.values.documents.jobs.job_status import JobStatus from ml.app_logger import APP_LOGGER, setup_logger import time +from config import * -dt_creation_app_id = "s3i:a44bd6dc-c607-4574-9b16-cf8579819356" -dt_creation_app_secret = "970ebe57-7dfb-4090-8c38-4b44111d5288" +dt_creation_app_id = dt_creation_app_id +dt_creation_app_secret = dt_creation_app_secret # username = input('[S3I]: Please enter your username:').strip('," ') # password = input('[S3I]: Please enter your password:') -username = "chen" -password = "8810515" +username = username +password = password s3i_identity_provider = s3i.IdentityProvider(grant_type='password', identity_provider_url="https://idp.s3i.vswf.dev/", realm='KWH', @@ -38,8 +39,8 @@ print(dt_id) """ -dt_id = "s3i:b6d1cc6d-896c-40fe-9403-b5b7682b1d03" -dt_secret = "cd4d24d2-f702-4f51-b0cf-6b77a423b33a" +dt_id = dt_id +dt_secret = dt_secret dt_name = "my_dt_harvester" config_engine = make_sub_thing(name="my_engine", roles=[{"class": "ml40::Engine"}], features=[ @@ -57,12 +58,10 @@ 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, +dt = create_dt_ref(model=dt_model, grant_type="client_credentials", secret=dt_secret, is_broker_rest=True, is_broker=True, is_repo=True) -dt.run_forever() - class AcceptsFellingJobsImpl(AcceptsFellingJobs): def __init__(self, name="", identifier=""): @@ -131,3 +130,4 @@ def simulate_rpm(): add_function_impl_obj(dt, AcceptsFellingJobsImpl, "fml40::AcceptsFellingJobs") dt.add_user_def(func=simulate_rpm) +dt.run_forever() diff --git a/dt_creation_hmi.py b/dt_creation_hmi.py index 02d96ca..830dd48 100644 --- a/dt_creation_hmi.py +++ b/dt_creation_hmi.py @@ -6,11 +6,8 @@ from ml.app_logger import setup_logger import requests import json +from config import * -username = "chen" -password = "8810515" -hmi_id = "s3i:b4f72a11-fb3d-47a3-b499-e7a88eaa5fe1" -hmi_secret = "84df6340-9d64-4ae6-9076-86c224ec484a" setup_logger("my HMI") config_file_name = make_thing_config(dt_id=hmi_id, name="my HMI", roles=[{"class": "ml40::HMI"}], @@ -73,10 +70,11 @@ getv_req = s3i.GetValueRequest() rpm_path = "attributes/features/ml40::Composite/targets/ml40::Engine/features/ml40::RotationalSpeed/rpm" +#rpm_path = "" 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)) +resp = hmi.broker.send([receiver_endpoint], json.dumps(getv_req.msg)) -- GitLab From e761135592a53c4a894dd91050dece527cee9339 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 27 Oct 2020 13:32:15 +0100 Subject: [PATCH 51/84] change the type of value of the gervaluerequest to dict --- ml/thing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ml/thing.py b/ml/thing.py index b50ef99..aabf3ae 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -271,7 +271,7 @@ def on_get_value_request(self, msg): get_value_reply.fillGetValueReply( senderUUID=self.thing_id, receiverUUID=[request_sender], - results=str(value), + results=value, msgUUID=reply_msg_uuid, replyingToUUID=request_msg_id, ) -- GitLab From 56a9b121ab132565bae7c93ac1c1d321d293b5bd Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 27 Oct 2020 17:11:23 +0100 Subject: [PATCH 52/84] remove websocket client from the req --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index daad432..ed3d59e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ https://git.rwth-aachen.de/kwh40/s3i/-/jobs/artifacts/master/raw/public/s3i-0.4-py3-none-any.whl?job=wheel Pykka==2.0.2 -websocket-client==0.57.0 pynput opcua requests -- GitLab From 4dc617cfb64f9d6d8ddcbb8d06febefbcedd3d28 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 27 Oct 2020 17:12:08 +0100 Subject: [PATCH 53/84] fix the s3i component name --- ml/thing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ml/thing.py b/ml/thing.py index aabf3ae..6d64cd2 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -149,7 +149,7 @@ def __repo_syn(self): continue def __connect_with_idp(self): - print(BColors.OKBLUE + "[S³I][IdP]" + BColors.ENDC + ": Connect with S3I-IdentityProvider") + print(BColors.OKBLUE + "[S³I][IdP]" + BColors.ENDC + ": Connect with S3I IdentityProvider") idp = IdentityProvider( grant_type=self.__grant_type, client_id=self.__thing_id, @@ -175,7 +175,7 @@ def __connect_with_dir(self): BColors.OKBLUE + "[S³I][Dir]" + BColors.ENDC - + ": Connect with S3I-Directory" + + ": Connect with S3I Directory" ) self.dir = Directory(s3i_dir_url=BaseVariable.DIR_URL, token=self.__access_token) @@ -184,12 +184,12 @@ def __connect_with_repo(self): BColors.OKBLUE + "[S³I][Repo]" + BColors.ENDC - + ": Connect with S3I-Repository" + + ": Connect with S3I Repository" ) 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) -- GitLab From 359deef26bc4ed0a0c253d4b839aaaabb7201295 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 27 Oct 2020 22:51:14 +0100 Subject: [PATCH 54/84] update the print out --- ml/thing.py | 78 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/ml/thing.py b/ml/thing.py index 6d64cd2..f277c34 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -252,11 +252,18 @@ def on_user_message(self, msg): BColors.OKBLUE + "[S³I][Broker]" + BColors.ENDC - + ": You have received a user message" + + ": You have received a S³I-B UserMessage" + json.dumps(msg, indent=2) ) def on_get_value_request(self, msg): + print( + BColors.OKBLUE + + "[S³I][Broker]" + + BColors.ENDC + + ": You have received a S³I-B GetValueRequest" + + json.dumps(msg, indent=2) + ) get_value_reply = GetValueReply() request_sender = msg.get("sender") request_msg_id = msg.get("identifier") @@ -264,6 +271,13 @@ def on_get_value_request(self, msg): attribute_path = msg.get("attributePath") reply_msg_uuid = "s3i:" + str(uuid.uuid4()) try: + print( + BColors.OKBLUE + + "[S³I]" + + BColors.ENDC + + ": Search the attribute with path: " + + attribute_path + ) value = self._uriToData(attribute_path) except KeyError: value = "invalid attribute path" @@ -275,10 +289,18 @@ def on_get_value_request(self, msg): msgUUID=reply_msg_uuid, replyingToUUID=request_msg_id, ) - self.broker.send( + + res = self.broker.send( receiver_endpoints=[request_sender_endpoint], msg=json.dumps(get_value_reply.msg), ) + if res.status_code == 201: + print( + BColors.OKBLUE + + "[S³I][Broker]" + + BColors.ENDC + + ": Send a S³I-B GetValueReply back to the requester." + ) def _uriToData(self, uri): if uri == "": @@ -361,6 +383,13 @@ def _findValue(self, json, value): return False def on_service_request(self, body_json): + print( + BColors.OKBLUE + + "[S³I][Broker]" + + BColors.ENDC + + ": You have received a S³I-B ServiceRequest " + + json.dumps(body_json, indent=2) + ) service_type = body_json.get("serviceType") parameters = body_json.get("parameters") service_functionality = service_type.split('/')[0] @@ -372,6 +401,13 @@ def on_service_request(self, body_json): ) else: # TODO: Call right functionality. + print( + BColors.OKBLUE + + "[S³I][Broker]" + + BColors.ENDC + + ": Execute the function {0} of the class {1}.".format(service_type.split('/')[1], + service_type.split('/')[0]) + ) method = getattr(service_functionality_obj, service_type.split('/')[1]) result = method(**parameters) @@ -387,26 +423,58 @@ def on_service_request(self, body_json): replyingToUUID=body_json.get("identifier", None), msgUUID="s3i:{}".format(uuid.uuid4()) ) - self.broker.send(receiver_endpoints=[body_json.get("replyToEndpoint", None)], + + res = self.broker.send(receiver_endpoints=[body_json.get("replyToEndpoint", None)], msg=json.dumps(service_reply.msg)) + if res.status_code == 201: + print( + BColors.OKBLUE + + "[S³I][Broker]" + + BColors.ENDC + + ": Send a S³I-B ServiceReply back to the requester " + + json.dumps(body_json, indent=2) + ) def on_get_value_reply(self, msg): print( BColors.OKBLUE + "[S³I][Broker]" + BColors.ENDC - + ": You have received a get value reply" + + ": You have received a S³I-B GetValueReply" + json.dumps(msg, indent=2) ) + value = msg.get("value", None) + if isinstance(value, dict): + value = json.dumps(value, indent=2) + + print( + BColors.OKBLUE + + "[S³I][Broker]" + + BColors.ENDC + + ": The queried value is: {0}{1}{2}".format(BColors.OKGREEN, value, + BColors.ENDC) + ) + def on_service_reply(self, msg): print( BColors.OKBLUE + "[S³I][Broker]" + BColors.ENDC - + ": You have received a service reply" + + ": You have received a S³I-B ServiceReply" + json.dumps(msg, indent=2) ) + results = msg.get("results", None) + if isinstance(results, dict): + results = json.dumps(results, indent=2) + + print( + BColors.OKBLUE + + "[S³I][Broker]" + + BColors.ENDC + + ": The result is: {0}{1}{2}".format(BColors.OKGREEN, results, + BColors.ENDC) + ) def to_dir_json(self): self.dir_json = self.dir.queryThingIDBased(self.thing_id) -- GitLab From d96ffe7499a2e3b4ce492b67b9ae45772ecc21e2 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 27 Oct 2020 23:13:25 +0100 Subject: [PATCH 55/84] fix message print out --- ml/thing.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ml/thing.py b/ml/thing.py index f277c34..c22becf 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -425,14 +425,13 @@ def on_service_request(self, body_json): ) res = self.broker.send(receiver_endpoints=[body_json.get("replyToEndpoint", None)], - msg=json.dumps(service_reply.msg)) + msg=json.dumps(service_reply.msg)) if res.status_code == 201: print( BColors.OKBLUE + "[S³I][Broker]" + BColors.ENDC + ": Send a S³I-B ServiceReply back to the requester " - + json.dumps(body_json, indent=2) ) def on_get_value_reply(self, msg): -- GitLab From fcf0aa04cb26e6c2df0f91db8552cee357e9bfd8 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Wed, 28 Oct 2020 17:10:02 +0100 Subject: [PATCH 56/84] add sync for the original thing json model --- ml/thing.py | 67 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/ml/thing.py b/ml/thing.py index c22becf..53b2961 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -57,6 +57,7 @@ def __init__( self.repo_json = dict() self.dir_json = dict() + self.dt_json = dict() attributes = model.get("attributes", None) self.__name = "" @@ -114,6 +115,7 @@ def run_forever(self): print("[S³I]: Launch {}{}{}".format(BColors.OKGREEN, self.name, BColors.ENDC)) self.__connect_with_idp() + threading.Thread(target=self.__json_syn).start() threading.Thread(target=self.__dir_syn).start() if self.__is_repo: threading.Thread(target=self.__repo_syn).start() @@ -122,10 +124,18 @@ def run_forever(self): def add_user_def(func): threading.Thread(target=func).start() - def __dir_syn(self): + def __json_syn(self, freq=0.1): while True: try: - time.sleep(0.1) + time.sleep(freq) + self.to_json() + except: + continue + + def __dir_syn(self, freq=0.1): + while True: + try: + time.sleep(freq) old_dir_json = self.dir_json self.to_dir_json() if self.dir_json == old_dir_json: @@ -135,10 +145,10 @@ def __dir_syn(self): except: continue - def __repo_syn(self): + def __repo_syn(self, freq=0.1): while self.__is_repo: try: - time.sleep(0.1) + time.sleep(freq) old_repo_json = self.repo_json self.to_repo_json() if self.repo_json == old_repo_json: @@ -304,17 +314,17 @@ def on_get_value_request(self, msg): def _uriToData(self, uri): if uri == "": - return self.repo_json + return self.dt_json else: uri_list = uri.split("/") if uri_list[0] == "features": try: - return self.repo_json[uri] + return self.dt_json[uri] except KeyError: return "invalid attribute path" try: - self._getValue(self.repo_json, uri_list) + self._getValue(self.dt_json, uri_list) except: return "invalid attribute path" if self.__resGetValue.__len__() == 0: @@ -334,7 +344,7 @@ def _getValue(self, source, uri_list): try: stringValue_split = value.split(":") if stringValue_split[0] == "ditto-feature": - value = self.repo_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) @@ -368,7 +378,7 @@ def _getValue(self, source, uri_list): try: stringValue_split = value.split(":") if stringValue_split[0] == "ditto-feature": - value = self.repo_json["features"][stringValue_split[1]][ + value = self.dt_json["features"][stringValue_split[1]][ "properties" ][uri_list[0]] except: @@ -496,26 +506,29 @@ def to_dir_json(self): return self.dir_json - def to_repo_json(self, path=None, value=None): - if path is None and value is None: - self.repo_json = { - "thingId": self.thing_id, - "policyId": self.policy_id, - "attributes": { - "class": "ml40::Thing", - "name": self.name, - } - } - if self.roles: - self.repo_json["attributes"]["roles"] = list() - if self.features: - self.repo_json["attributes"]["features"] = list() - for key in self.roles.keys(): - self.repo_json["attributes"]["roles"].append(self.roles[key].to_json()) - for key in self.features: - self.repo_json["attributes"]["features"].append(self.features[key].to_json()) + def to_repo_json(self): + self.repo_json = self.dt_json return self.repo_json + def to_json(self): + self.dt_json = { + "thingId": self.thing_id, + "policyId": self.policy_id, + "attributes": { + "class": "ml40::Thing", + "name": self.name, + } + } + if self.roles: + self.dt_json["attributes"]["roles"] = list() + if self.features: + self.dt_json["attributes"]["features"] = list() + for key in self.roles.keys(): + self.dt_json["attributes"]["roles"].append(self.roles[key].to_json()) + for key in self.features: + self.dt_json["attributes"]["features"].append(self.features[key].to_json()) + return self.dt_json + def to_subthing_json(self): json_out = { "class": "ml40::Thing", -- GitLab From c2a89010ff1e5564879b200b1b6d7294415ccd7b Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Thu, 29 Oct 2020 09:58:30 +0100 Subject: [PATCH 57/84] DEL: unused thins --- .gitignore | 9 +- __init__.py | 0 configs/config_forest.json | 34 -- configs/config_forwarder_komatsu.json | 98 ----- configs/config_harvester_john_deere.json | 113 ------ configs/config_harvester_ponsse.json | 58 --- configs/config_my HMI.json | 1 - configs/config_my_dt_harvester.json | 1 - customer_code_example/__init__.py | 2 - .../forwarder_komatsu/__init__.py | 1 - .../forwarder_komatsu/komatsu.py | 17 - .../harvester_john_deere/__init__.py | 1 - .../harvester_john_deere/john_deere.py | 39 -- .../harvester_ponsse/__init__.py | 1 - .../harvester_ponsse/ponsse.py | 32 -- customer_code_example/mini_tractor_driver.py | 37 -- dt_creation.py => demo/dt_creation.py | 2 +- dt_creation_hmi.py => demo/dt_creation_hmi.py | 9 +- fml40/__init__.py | 0 fml40/app_logger.py | 33 -- fml40/dt_factory.py | 67 ---- fml40/functionalities/__init__.py | 0 fml40/functionalities/functionalities.py | 157 -------- fml40/machines/forwarder.py | 48 --- fml40/machines/harvester.py | 57 --- fml40/managed_actor.py | 31 -- fml40/managing_actor.py | 63 ---- fml40/tools.py | 357 ------------------ fml40/worker.py | 32 -- fml40s.py | 18 +- ml/managed_actor.py | 69 ---- ml/managing_actor.py | 69 ---- requirements.txt | 1 - scripts/get_token.py | 18 - scripts/s3i_api.py | 277 -------------- scripts/thing_to_ditto_thing.py | 34 -- setup.py | 12 +- tests/__init__.py | 2 - tests/res/dummy_credentials.json | 4 - tests/res/malformatted.json | 1 - tests/test_benedict.py | 20 - tests/test_digital_twins.py | 226 ----------- tests/test_get_token.py | 20 - tests/test_threading_futhur.py | 10 - 44 files changed, 21 insertions(+), 2060 deletions(-) delete mode 100644 __init__.py delete mode 100644 configs/config_forest.json delete mode 100644 configs/config_forwarder_komatsu.json delete mode 100644 configs/config_harvester_john_deere.json delete mode 100644 configs/config_harvester_ponsse.json delete mode 100644 configs/config_my HMI.json delete mode 100644 configs/config_my_dt_harvester.json delete mode 100644 customer_code_example/__init__.py delete mode 100644 customer_code_example/forwarder_komatsu/__init__.py delete mode 100644 customer_code_example/forwarder_komatsu/komatsu.py delete mode 100644 customer_code_example/harvester_john_deere/__init__.py delete mode 100644 customer_code_example/harvester_john_deere/john_deere.py delete mode 100644 customer_code_example/harvester_ponsse/__init__.py delete mode 100644 customer_code_example/harvester_ponsse/ponsse.py delete mode 100644 customer_code_example/mini_tractor_driver.py rename dt_creation.py => demo/dt_creation.py (99%) rename dt_creation_hmi.py => demo/dt_creation_hmi.py (90%) delete mode 100644 fml40/__init__.py delete mode 100644 fml40/app_logger.py delete mode 100644 fml40/dt_factory.py delete mode 100644 fml40/functionalities/__init__.py delete mode 100644 fml40/functionalities/functionalities.py delete mode 100644 fml40/machines/forwarder.py delete mode 100644 fml40/machines/harvester.py delete mode 100644 fml40/managed_actor.py delete mode 100644 fml40/managing_actor.py delete mode 100644 fml40/tools.py delete mode 100644 fml40/worker.py delete mode 100644 ml/managed_actor.py delete mode 100644 ml/managing_actor.py delete mode 100644 scripts/get_token.py delete mode 100644 scripts/s3i_api.py delete mode 100644 scripts/thing_to_ditto_thing.py delete mode 100644 tests/__init__.py delete mode 100644 tests/res/dummy_credentials.json delete mode 100644 tests/res/malformatted.json delete mode 100644 tests/test_benedict.py delete mode 100644 tests/test_digital_twins.py delete mode 100644 tests/test_get_token.py delete mode 100644 tests/test_threading_futhur.py diff --git a/.gitignore b/.gitignore index e21683d..d8a43d5 100644 --- a/.gitignore +++ b/.gitignore @@ -224,5 +224,12 @@ config.py # venv venv/* +# log info logs/* -config.py \ No newline at end of file + +# config +config.py + +# pylinc +.pylintrc +setup.cfg \ No newline at end of file diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/configs/config_forest.json b/configs/config_forest.json deleted file mode 100644 index b84d80f..0000000 --- a/configs/config_forest.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "client_secret": "d48971ab-2c22-47d5-a8dc-86079a2c4cb6", - "thingId": "s3i:a8b7da14-9e43-47f0-a876-cfd1ca38a9e1", - "policyID": "s3i:a8b7da14-9e43-47f0-a876-cfd1ca38a9e1", - "type": "Forest", - "attributes": { - "class": "ml40::Thing", - "name": "WZL-Demo Wald", - "roles": [ - { - "class": "fml40::Forest" - } - ], - "components": [ - { - "class": "fml40::InventoryData", - "data": { - "class": "fml::AbstractInventory" - } - }, - "s3i:...", - "s3i:..." - ] - }, - "requests": { - "msg_1": { - "msg_type": "fml40::FellingJob", - "receivers": [ - "s3i:c5d5cd58-8786-40b2-8079-5f2de443de36", - "s3i:5e83b933-331f-4278-b318-b7fdcb0e4872" - ] - } - } -} diff --git a/configs/config_forwarder_komatsu.json b/configs/config_forwarder_komatsu.json deleted file mode 100644 index 0657903..0000000 --- a/configs/config_forwarder_komatsu.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "client_secret": "432f0bfc-9a13-4551-a4cd-b94b054cdadf", - "thingId": "s3i:3154edfa-5b04-4a28-b803-d6ec46135c19", - "policyId": "s3i:3154edfa-5b04-4a28-b803-d6ec46135c19", - "type": "Komatsu", - "functionalities": [ - { - "type": "AcceptsForwardingJobs", - "proxy": "Forwards" - } - ], - "attributes": { - "class": "ml40::Thing", - "name": "Komatsu Forwarder", - "roles": [ - { - "class": "fml40::Forwarder" - } - ], - "features": [ - { - "class": "fml40::AcceptsProximityAlert" - }, - { - "class": "fml40::AcceptsForwardingJobs" - }, - { - "class": "fml40::Forwards" - }, - { - "class": "ml40::Location", - "longitude": 6.2492276, - "latitude": 50.808797 - }, - { - "class": "ml40::Shared", - "targets": [ - "s3i:..." - ] - }, - { - "class": "ml40::Composite", - "targets": [ - { - "class": "ml40::Thing", - "roles": [ - "ml40::Engine" - ], - "name": "Motor", - "identifier": "s3i:...", - "features": [ - { - "class": "ml40::RotationalSpeed", - "rpm": "3000" - } - ] - }, - { - "class": "ml40::Thing", - "roles": [ - "ml40::Crane" - ], - "name": "Kran", - "identifier": "s3i:...", - "features": [ - { - "class": "ml40::Composite", - "targets": [ - { - "class": "ml40::Thing", - "roles": [ - "fml40::ForwarderCrane" - ], - "name": "Forwarder Kran", - "identifier": "s3i:...", - "features": [ - { - "class": "fml40::Grabs" - } - ] - } - ] - } - ] - }, - { - "class": "ml40::Thing", - "roles": [ - "ml40::MachineUI" - ], - "name": "Bordcomputer", - "identifier": "s3i:..." - } - ] - } - ] - } -} diff --git a/configs/config_harvester_john_deere.json b/configs/config_harvester_john_deere.json deleted file mode 100644 index ccf43ca..0000000 --- a/configs/config_harvester_john_deere.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "client_secret": "fc86fe53-1fb5-4cb7-96d2-c05c7e548d69", - "thingId": "s3i:c5d5cd58-8786-40b2-8079-5f2de443de36", - "policyId": "s3i:c5d5cd58-8786-40b2-8079-5f2de443de36", - "type": "JohnDeere", - "functionalities": [ - { - "type": "AcceptsFellingJobs", - "proxy": "Harvests" - }, - { - "type": "Harvests", - "receiver": "s3i:3154edfa-5b04-4a28-b803-d6ec46135c19", - "proxy": "ManageJobs" - }, - { - "type": "ManageJobs", - "proxy" : "Forwards" - } - ], - "attributes": { - "class": "ml40::Thing", - "name": "John Deere Harvester FBZ", - "roles": [ - { - "class": "fml40::Harvester" - } - ], - "features": [ - { - "class": "fml40::AcceptsProximityAlert" - }, - { - "class": "fml40::AcceptsFellingJobs" - }, - { - "class": "fml40::Harvests" - }, - { - "class": "ml40::ManagesJobs" - }, - { - "class": "fml40::ProvidesProductionData" - }, - { - "class": "ml40::Location", - "longitude": 6.2492276, - "latitude": 50.808797 - }, - { - "class": "ml40::Shared", - "targets": [ - "s3i:..." - ] - }, - { - "class": "ml40::Composite", - "targets": [ - { - "class": "ml40::Thing", - "roles": [ - "ml40::Engine" - ], - "name": "Motor", - "identifier": "s3i:...", - "features": [ - { - "class": "ml40::RotationalSpeed", - "rpm": "3000" - } - ] - }, - { - "class": "ml40::Thing", - "roles": [ - "ml40::Crane" - ], - "name": "Kran", - "identifier": "s3i:...", - "features": [ - { - "class": "ml40::Composite", - "targets": [ - { - "class": "ml40::Thing", - "roles": [ - "fml40::HarvestingHead" - ], - "name": "Harvesterkopf", - "identifier": "s3i:...", - "features": [ - { - "class": "fml40::Grabs" - } - ] - } - ] - } - ] - }, - { - "class": "ml40::Thing", - "roles": [ - "ml40::MachineUI" - ], - "name": "Bordcomputer", - "identifier": "s3i:..." - } - ] - } - ] - } -} diff --git a/configs/config_harvester_ponsse.json b/configs/config_harvester_ponsse.json deleted file mode 100644 index 09026dd..0000000 --- a/configs/config_harvester_ponsse.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "client_secret": "9d34802b-3a2f-4558-8c63-f1af09a68c2d", - "thingId": "s3i:5e83b933-331f-4278-b318-b7fdcb0e4872", - "policyId": "s3i:5e83b933-331f-4278-b318-b7fdcb0e4872", - "type": "Ponsse", - "functionalities": [ - { - "type": "AcceptsFellingJobs", - "proxy": "Harvests" - }, - { - "type": "Harvests", - "receiver": "s3i:3154edfa-5b04-4a28-b803-d6ec46135c19", - "proxy": "ManageJobs" - }, - { - "type": "ManageJobs", - "proxy": "Forwards" - } - ], - "attributes": { - "class": "ml40::Thing", - "name": "Ponsse Harvester", - "roles": [ - { - "class": "fml40::Harvester" - } - ], - "features": [ - { - "class": "fml40::AcceptsProximityAlert" - }, - { - "class": "fml40::AcceptsFellingJobs" - }, - { - "class": "fml40::Harvests" - }, - { - "class": "ml40::ManagesJobs" - }, - { - "class": "fml40::ProvidesProductionData" - }, - { - "class": "ml40::Location", - "longitude": 6.2492276, - "latitude": 50.808797 - }, - { - "class": "ml40::Shared", - "targets": [ - "s3i:..." - ] - } - ] - } -} diff --git a/configs/config_my HMI.json b/configs/config_my HMI.json deleted file mode 100644 index 60c72fa..0000000 --- a/configs/config_my HMI.json +++ /dev/null @@ -1 +0,0 @@ -{"thingId": "s3i:b4f72a11-fb3d-47a3-b499-e7a88eaa5fe1", "policyId": "s3i:b4f72a11-fb3d-47a3-b499-e7a88eaa5fe1", "attributes": {"class": "ml40::Thing", "name": "my HMI", "roles": [{"class": "ml40::HMI"}], "features": []}} \ No newline at end of file diff --git a/configs/config_my_dt_harvester.json b/configs/config_my_dt_harvester.json deleted file mode 100644 index 48b2009..0000000 --- a/configs/config_my_dt_harvester.json +++ /dev/null @@ -1 +0,0 @@ -{"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 diff --git a/customer_code_example/__init__.py b/customer_code_example/__init__.py deleted file mode 100644 index e9b2215..0000000 --- a/customer_code_example/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -import sys -sys.path.append("./../") diff --git a/customer_code_example/forwarder_komatsu/__init__.py b/customer_code_example/forwarder_komatsu/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/customer_code_example/forwarder_komatsu/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/customer_code_example/forwarder_komatsu/komatsu.py b/customer_code_example/forwarder_komatsu/komatsu.py deleted file mode 100644 index 01fbc41..0000000 --- a/customer_code_example/forwarder_komatsu/komatsu.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Example implementation of a Komatsu forwarder.""" - -from fml40.machines.forwarder import Forwarder - - -class Komatsu(Forwarder): - """Example implementation of a Komatsu forwarder.""" - - def __init__(self, idp, config): - """Constructs the Komatsu forwarder. - - :param idp: IdentityProvider object used to ask for an access token. - :param config: A json-object describing the forwarder. - - - """ - super().__init__(idp, config) diff --git a/customer_code_example/harvester_john_deere/__init__.py b/customer_code_example/harvester_john_deere/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/customer_code_example/harvester_john_deere/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/customer_code_example/harvester_john_deere/john_deere.py b/customer_code_example/harvester_john_deere/john_deere.py deleted file mode 100644 index 1b9b946..0000000 --- a/customer_code_example/harvester_john_deere/john_deere.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Example implentation of a JohnDeere harvester.""" - -from s3i import TokenType -from fml40.machines.harvester import Harvester -from fml40.app_logger import APP_LOGGER -from fml40.tools import send_request - -# TODO: What information may the job argument contain? - - -class JohnDeere(Harvester): - """Example implentation of a JohnDeere harvester.""" - - def __init__(self, idp, config): - super().__init__(idp, config) - APP_LOGGER.info( - "Hello I'm the John Deere Harvester of " - "the FBZ. I can harvest if you send me a " - "felling job." - ) - - def send_forwarding_job(self, receiver_id, job): - """Sends a fml40::Forwardingjob message to receiver_id. - - :param sender_id: - :param receiver_id: s3i formatted id of the receiver. - :param job: - - """ - APP_LOGGER.info("Sending forwarding job.") - receiver_ids = [receiver_id] - access_token = self.idp.get_token(TokenType.ACCESS_TOKEN) - send_request( - access_token, - self.config["thingId"], - receiver_ids, - "fml40::ForwardingJob", - parameters=job, - ) diff --git a/customer_code_example/harvester_ponsse/__init__.py b/customer_code_example/harvester_ponsse/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/customer_code_example/harvester_ponsse/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/customer_code_example/harvester_ponsse/ponsse.py b/customer_code_example/harvester_ponsse/ponsse.py deleted file mode 100644 index d6d84bc..0000000 --- a/customer_code_example/harvester_ponsse/ponsse.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Example implentation of a Ponsse harvester.""" - -from s3i import TokenType -from fml40.machines.harvester import Harvester -from fml40.tools import send_request -from fml40.app_logger import APP_LOGGER - - -class Ponsse(Harvester): - """Example implentation of a Ponsse harvester.""" - - def __init__(self, idp, config): - super().__init__(idp, config) - - def send_forwarding_job(self, receiver_id, job): - """Send a fml40::Forwardingjob message to receiver_id. - - :param sender_id: - :param receiver_id: s3i formatted id of the receiver. - :param job: - - """ - APP_LOGGER.info("Sending forwarding job.") - receiver_ids = [receiver_id] - access_token = self.idp.get_token(TokenType.ACCESS_TOKEN) - send_request( - access_token, - self.config["thingId"], - receiver_ids, - "fml40::ForwardingJob", - parameters=job, - ) diff --git a/customer_code_example/mini_tractor_driver.py b/customer_code_example/mini_tractor_driver.py deleted file mode 100644 index 9b4861f..0000000 --- a/customer_code_example/mini_tractor_driver.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Implementation of the digital twin mini tractor driver""" -import json -from s3i import TokenType -from ml.app_logger import APP_LOGGER -from ml.tools import send_request -from ml.tools import decode_message -from ml.tools import verify_message -from ml.thing import Thing - - -class MiniTractorDriver(Thing): - def __init__(self, idp, config): - APP_LOGGER.info("Hello I'm the mini tractor driver.") - super().__init__(idp, config) - - def on_receive(self, msg): - body_str = decode_message(msg) - body_json = json.loads(body_str) - APP_LOGGER.debug(f"Received message: {body_str}") - if verify_message(body_json, "serviceRequest", "fml40::FellingJob"): - job = body_json.get("parameters", {}) - func = self.proxy_functionalities["AcceptsFellingJobs"] - func.acceptJob(job) - else: - APP_LOGGER.warning("Not handling message") - - def send_fellling_job(self, receiver_id, job): - receiver_ids = [receiver_id] - access_token = self.idp.get_token(TokenType.ACCESS_TOKEN) - send_request( - access_token, - self.config["thingId"], - receiver_ids, - "fml40::FellingJob", - parameters=job, - ) - APP_LOGGER.info("FellingJob send") diff --git a/dt_creation.py b/demo/dt_creation.py similarity index 99% rename from dt_creation.py rename to demo/dt_creation.py index 6b0999f..348d637 100644 --- a/dt_creation.py +++ b/demo/dt_creation.py @@ -60,7 +60,7 @@ dt = create_dt_ref(model=dt_model, grant_type="client_credentials", secret=dt_secret, is_broker_rest=True, - is_broker=True, is_repo=True) + is_broker=True, is_repo=False) class AcceptsFellingJobsImpl(AcceptsFellingJobs): diff --git a/dt_creation_hmi.py b/demo/dt_creation_hmi.py similarity index 90% rename from dt_creation_hmi.py rename to demo/dt_creation_hmi.py index 830dd48..74e4790 100644 --- a/dt_creation_hmi.py +++ b/demo/dt_creation_hmi.py @@ -61,15 +61,16 @@ felling_job = build_feature(feature=feature_config_json) serv_req.fillServiceRequest( senderUUID=hmi_id, receiverUUID=[receiver], sender_endpoint=hmi_endpoint, - serviceType="fml40::AcceptsFellingJobs/removeJob", - #serviceType="fml40::AcceptsFellingJobs/acceptJob", - parameters={"identifier": "s3i:d09214a7-46bb-4672-b4e0-2c0aa0e395a3"}, - #parameters={"job": felling_job.to_json()}, + #serviceType="fml40::AcceptsFellingJobs/removeJob", + serviceType="fml40::AcceptsFellingJobs/acceptJob", + #parameters={"identifier": "s3i:d09214a7-46bb-4672-b4e0-2c0aa0e395a3"}, + parameters={"job": felling_job.to_json()}, msgUUID="s3i:{}".format(uuid.uuid4()) ) getv_req = s3i.GetValueRequest() rpm_path = "attributes/features/ml40::Composite/targets/ml40::Engine/features/ml40::RotationalSpeed/rpm" +#rpm_path = "attributes/name" #rpm_path = "" getv_req.fillGetValueRequest( senderUUID=hmi_id, receiverUUID=[receiver], sender_endpoint=hmi_endpoint, diff --git a/fml40/__init__.py b/fml40/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/fml40/app_logger.py b/fml40/app_logger.py deleted file mode 100644 index 4704e63..0000000 --- a/fml40/app_logger.py +++ /dev/null @@ -1,33 +0,0 @@ -import logging -import os - -APP_LOGGER = logging.getLogger("app_logger") - - -def setup_logger(dt_name): - """Creates logger named app_logger. - - Logs are printed to stdout and saved to log files under '/logs'. - Each file is names 'dt_name'.log. - :param dt_name: Name of the digital twin. - :returns: Created logger - :rtype: Logger - - """ - if not os.path.exists("logs"): - os.mkdir("logs") - - app_logger = logging.getLogger('app_logger') - app_logger.setLevel(logging.DEBUG) - log_formatter = logging.Formatter( - "%(asctime)s - %(levelname)s - {}: %(message)s".format(dt_name) - ) - stream_handler = logging.StreamHandler() - stream_handler.setFormatter(log_formatter) - stream_handler.setLevel(logging.DEBUG) - file_handler = logging.FileHandler(filename="./logs/{}.log".format(dt_name)) - file_handler.setLevel(logging.DEBUG) - file_handler.setFormatter(log_formatter) - app_logger.addHandler(file_handler) - app_logger.addHandler(stream_handler) - return app_logger diff --git a/fml40/dt_factory.py b/fml40/dt_factory.py deleted file mode 100644 index d1a5a13..0000000 --- a/fml40/dt_factory.py +++ /dev/null @@ -1,67 +0,0 @@ -""" Implements a factory for managing digital twins.""" - -import sys -from s3i import IdentityProvider -from s3i import TokenType -from fml40.tools import get_idp -from fml40.managing_actor import ManagingActor -from fml40.tools import load_config -from fml40.app_logger import APP_LOGGER - -# TODO: Get rid of this global variable -DT_FACTORY = {} - - -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 create_dt_ref(config, grant_type, username, password): - """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) - - """ - - id_p = get_idp(config, grant_type, username, password) - dt_type = config["type"] - APP_LOGGER.debug("Creating ditigtal twin of type %s", dt_type) - d_t = DT_FACTORY.get(dt_type, ManagingActor) - dt_ref = d_t.start(id_p, config) - return dt_ref diff --git a/fml40/functionalities/__init__.py b/fml40/functionalities/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/fml40/functionalities/functionalities.py b/fml40/functionalities/functionalities.py deleted file mode 100644 index f4e35a8..0000000 --- a/fml40/functionalities/functionalities.py +++ /dev/null @@ -1,157 +0,0 @@ -"""Collection of functionalities.""" - - -from fml40.managed_actor import ManagedActor -from fml40.app_logger import APP_LOGGER - - -class Functionality(ManagedActor): - """Genric implementation of a Functionaliy.""" - - def __init__(self, idp, ref_managing_actor, name, proxy_name): - super().__init__(idp, ref_managing_actor, name, proxy_name) - - -class AcceptsJobs(Functionality): - """Defines an interface for functionalities accepting jobs. """ - - def __init__(self, idp, ref_managing_actor, name, proxy_name): - super().__init__(idp, ref_managing_actor, name, proxy_name) - self.jobs = list() - - def acceptJob(self, job): - """Accepts a job. - - :param job: - - """ - - pass - - def queryJob(self, job_id): - """Queries for the job identified by job_id. - - :param job_id: - :returns: - :rtype: - - """ - - pass - - def removeJob(self, job_id): - """Removes the job identified by job_id. - - :param job_id: - :returns: - :rtype: - - """ - - pass - - -class AcceptsFellingJobs(AcceptsJobs): - """Implementation of the functionality AcceptsFellingJobs.""" - - def __init__(self, idp, ref_managing_actor, name, proxy_name): - super().__init__(idp, ref_managing_actor, name, proxy_name) - - def acceptJob(self, job): - # Check if job can be accepted - # Find the correct functionality which should execute the job - APP_LOGGER.info("Checking if the job can be accepted.") - proxy_executer = self.managing_actor.proxy_functionalities.get()[ - self.proxy_name - ] - proxy_executer.executeJob(job) - - -class Harvests(ManagedActor): - """Implementation of the functionality Harvests.""" - - def __init__( - self, idp, ref_managing_actor, name, proxy_name, sender_id, receiver_id - ): - super().__init__(idp, ref_managing_actor, name, proxy_name) - self.receiver_id = receiver_id - self.sender_id = sender_id - - def executeJob(self, job): - """Exectutes the dedicated proxy with job. - - :param job: - - """ - - APP_LOGGER.info("I am harvesting right now. So much fun!") - proxy_manager = self.managing_actor.proxy_functionalities.get()[self.proxy_name] - proxy_manager.assignJob(self.sender_id, self.receiver_id, job) - - -class ManageJobs(ManagedActor): - """Implementation of the functionality ManageJobs.""" - - def __init__(self, idp, ref_managing_actor, name, proxy_name): - super().__init__(idp, ref_managing_actor, name, proxy_name) - - def assignJob(self, sender_id, receiver_id, job): - """Calls dedicated proxy with the given arguments. - - :param sender_id: id of the sending thing - :param receiver_id: id of the receiving thing - :param job: - - """ - APP_LOGGER.info("I assign the job {} to the Komatsu forwarder".format(job)) - # access_token = self.idp.get_token(TokenType.ACCESS_TOKEN) - self.managing_actor.send_forwarding_job(receiver_id, job) - print("Done") - - -class AcceptsForwardingJobs(AcceptsJobs): - """Implementation of the functionality AccepsForwardingJobs.""" - - def __init__(self, idp, ref_managing_actor, name, proxy_name): - super().__init__(idp, ref_managing_actor, name, proxy_name) - - def acceptJob(self, job): - """Calls dedicated proxy with job. - - :param job: - - """ - print("accepting job") - APP_LOGGER.info("I check if I (the Komatsu Forwarder) can accept the job.") - proxy_executer = self.managing_actor.proxy_functionalities.get()[ - self.proxy_name - ] - proxy_executer.executeJob(job) - - -class Forwards(ManagedActor): - """Implementation of the functionality Forwards.""" - - def __init__(self, idp, ref_managing_actor, name, proxy_name): - super().__init__(idp, ref_managing_actor, name, proxy_name) - - def executeJob(self, job): - """Printing message to stdout. - - :param job: Unused - - """ - APP_LOGGER.info("I am forwarding right now. So much fun!") - - -FUNCTIONALITY_FACTORY = { - "AcceptsFellingJobs": AcceptsFellingJobs, - "Harvests": Harvests, - "ManageJobs": ManageJobs, - "AcceptsForwardingJobs": AcceptsForwardingJobs, - "Forwards": Forwards, -} - - -def get_functionality_names(): - return FUNCTIONALITY_FACTORY.keys() diff --git a/fml40/machines/forwarder.py b/fml40/machines/forwarder.py deleted file mode 100644 index b6f3192..0000000 --- a/fml40/machines/forwarder.py +++ /dev/null @@ -1,48 +0,0 @@ -"""Implementation of a generic Forwarder""" -import json -from fml40.managing_actor import ManagingActor -from fml40.functionalities import functionalities -from fml40.app_logger import APP_LOGGER -from fml40.tools import decode_message -from fml40.tools import verify_message - - -class Forwarder(ManagingActor): - """Implements a generic Forwarder.""" - - def __init__(self, idp, config): - super().__init__(idp, config=config) - self.jobs = dict() - for func in config["functionalities"]: - functionality = functionalities.FUNCTIONALITY_FACTORY[func["type"]] - receiver_id = func.get("receiver", None) - APP_LOGGER.debug("Creating functionality: {}".format(func["type"])) - if receiver_id is None: - functionality.start( - self.idp, self.actor_ref, func["type"], func["proxy"] - ) - else: - functionality.start( - self.idp, self.actor_ref, func["type"], func["proxy"], receiver_id - ) - - def on_receive(self, msg): - """This function is called when the asset administration shell gets something - - :param body: json object representing a s3i message. - - """ - body_str = decode_message(msg) - body_json = json.loads(body_str) - APP_LOGGER.info( - "Received {} of type {}".format( - body_json["messageType"], body_json["serviceType"] - ) - ) - if verify_message(body_json, "serviceRequest", "fml40::ForwardingJob"): - self.proxy_functionalities["AcceptsForwardingJobs"].acceptJob( - "This is a forwarding job" - ) - msg = """I'm the asset administration shell and I distributed your message - to one of my highly qualified services""" - return msg diff --git a/fml40/machines/harvester.py b/fml40/machines/harvester.py deleted file mode 100644 index d411828..0000000 --- a/fml40/machines/harvester.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Generic implementation of a harvester.""" - -import json -from fml40.managing_actor import ManagingActor -from fml40.functionalities import functionalities -from fml40.app_logger import APP_LOGGER -from fml40.tools import decode_message - -class Harvester(ManagingActor): - """Implements a generic harvester.""" - - def __init__(self, idp, config): - super().__init__(idp, config=config) - self.jobs = dict() - sender_id = config["thingId"] - for func in config["functionalities"]: - functionality = functionalities.FUNCTIONALITY_FACTORY[func["type"]] - receiver_id = func.get("receiver", None) - APP_LOGGER.debug("Creating functionality: {}".format(func["type"])) - if receiver_id is None: - functionality.start(idp, self.actor_ref, func["type"], func["proxy"]) - else: - functionality.start( - idp, - self.actor_ref, - func["type"], - func["proxy"], - sender_id, - receiver_id, - ) - - def on_receive(self, msg): - """This function is called when the Message Distributer gets something - told. In this case, whenever the harvester receives a message. - """ - body_str = decode_message(msg) - body_json = json.loads(body_str) - APP_LOGGER.info( - "Received {} of type {}".format( - body_json["messageType"], body_json["serviceType"] - ) - ) - msg_type = "serviceRequest" - sv_type = "fml40::FellingJob" - if body_json["messageType"] == msg_type and body_json["serviceType"] == sv_type: - func_name = "AcceptsFellingJobs" - func = self.proxy_functionalities[func_name] - if func is None: - APP_LOGGER.cirtical( - "Functionality %s is missing in %s!" % (func_name, self.name) - ) - else: - func.acceptJob("This is a felling job") - - msg = """I\'m the Message Distribution Actor and I distributed your - message to one of my highly qualified actors""" - return msg diff --git a/fml40/managed_actor.py b/fml40/managed_actor.py deleted file mode 100644 index 944961e..0000000 --- a/fml40/managed_actor.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Provides an encapsulation class for digital twins allowing to run -it in a dedicated thread.""" -import pykka -from s3i import TokenType -from fml40.app_logger import APP_LOGGER - -class ManagedActor(pykka.ThreadingActor): - """Provides an encapsulation class for digital twins allowing to run - it in a dedicated thread.""" - - def __init__(self, idp, refManagingActor, name, proxy_name): - super().__init__() - self.idp = idp - self.name = name - self.managing_actor = refManagingActor.proxy() - self.managing_actor.register_managed_actor(name, self.actor_ref.proxy()) - self.proxy_name = proxy_name - APP_LOGGER.debug("Requesting access token") - self.access_token = idp.get_token(TokenType.ACCESS_TOKEN) - APP_LOGGER.debug("Received access token") - - def on_receive(self, message): - """Prints a greatings formula and message to stdout. - - """ - print( - """Hello, I'm a Managed Actor of a digital twin with the name {}.\n - You send me a message: {}""".format( - self.name, message - ) - ) diff --git a/fml40/managing_actor.py b/fml40/managing_actor.py deleted file mode 100644 index 08f0f2e..0000000 --- a/fml40/managing_actor.py +++ /dev/null @@ -1,63 +0,0 @@ -"""This is the administration shell of the forwarder. - It receives the service replys which are addressed to - the forwarder and schedules the further forwarder actions - when all necessary replys have arrived. - """ -import pykka -from s3i import TokenType -from fml40.app_logger import APP_LOGGER - -class ManagingActor(pykka.ThreadingActor): - """This is the administration shell of the forwarder. - It receives the service replys which are addressed to - the forwarder and schedules the further forwarder actions - when all necessary replys have arrived. - """ - - def __init__(self, idp, config): - super().__init__() - self.idp = idp - self.config = config - self.thing_id = config["thingId"] - self.name = config["type"] - self.proxy_functionalities = dict() - APP_LOGGER.debug("Requesting access token") - self.access_token = idp.get_token(TokenType.ACCESS_TOKEN) - APP_LOGGER.debug(self.access_token) - # APP_LOGGER.debug("Initialized managing actor") - - def on_receive(self, message): - """ Prints a greatings formula and message to stdout. - - :param message: Printable object - - """ - print("Hello, I'm a secretary\nYou send me a message %s" % message) - - def register_managed_actor(self, name, proxy): - """Adds functionaliy proxy under the name name. - - :param name: String identifying proxy - :param proxy: Functioanlity like object. - - """ - self.proxy_functionalities[name] = proxy - - def on_stop(self): - """Stops all functionality threads. - - """ - print("Stopping") - for key in self.proxy_functionalities: - pykka.ActorRegistry.get_by_class_name(key)[0].stop() - - def accept(self, functionality, parameters): - """FIXME! briefly describe function - - :param functionality: - :param parameters: - :returns: - :rtype: - - """ - self.proxy_functionalities[functionality].visit(self, parameters) diff --git a/fml40/tools.py b/fml40/tools.py deleted file mode 100644 index f00290f..0000000 --- a/fml40/tools.py +++ /dev/null @@ -1,357 +0,0 @@ -"""tools.py provivdes a collection of convenience functions.""" - -import datetime -import time -import uuid -import json -import importlib -from functools import partial -from s3i import Directory -from s3i import Broker -from s3i import ServiceReply -from s3i import ServiceRequest -from s3i import TokenType -from s3i import IdentityProvider -from fml40.app_logger import APP_LOGGER - - -IDENTITY_PROVIDER_URL = "https://idp.s3i.vswf.dev/" -GRANT_TYPES = ("client_credentials", "password") - - -class Bolors: - """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" - - -def get_idp(config, grant_type, 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) - client_id = config["thingId"] - secret = config.get("client_secret", "") - 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(dt_ref): - """Returns a s3i broker for dt_ref. - - :param dt_ref: ActorRef of a digital twin - :returns: a s3i broker - :rtype: s3i.Broker - - """ - - dt_proxy = dt_ref.proxy() - s3i_broker = Broker( - auth_form="Username/Password", - username="", - password=dt_proxy.access_token.get(), - 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 - dt_ref.tell(body) - - 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 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): - # convert bytes to str - body_str = ( - msg.decode("utf8") - .replace("'", '"') - .replace("True", "true") - .replace("False", "false") - ) - return body_str - - -def send_request(access_token, sender_id, receiver_ids, service_type, parameters=None): - """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: - - """ - # tmp = get_end_point(access_token, receiver_ids[0]) - # print(tmp) - receiver_endpoints = [("s3ib://{}".format(i)) for i in receiver_ids] - 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 - ) - s3i_broker = Broker( - auth_form="Username/Password", - username=" ", - password=access_token, - host="rabbitmq.s3i.vswf.dev", - ) - s3i_broker.send(receiver_endpoints, req.msg.__str__()) - - -def service_reply(idp, config, service_type, receiver_endpoints, results): - """ FIXME: This function is not used - - """ - config_data = importlib.import_module("config." + config) - sender_uuid = config_data.DATA["thingId"] - receiver_uuid = [item.split("//")[1] for item in receiver_endpoints] - msg_uuid = "s3i:" + str(uuid.uuid4()) - req = ServiceReply() - req.fillServiceReply( - sender_uuid, - receiver_uuid, - service_type, - results, - msgUUID=msg_uuid, - replyingToUUID=id, - ) - access_token = idp.get_token(TokenType.ACCESS_TOKEN) - s3i_broker = Broker( - auth_form="Username/Password", - username=" ", - password=access_token, - host="rabbitmq.s3i.vswf.dev", - ) - s3i_broker.send(receiver_endpoints, req.msg.__str__()) - APP_LOGGER.debug( - "Service Reply send: {} : {}".format( - req.msg["messageType"], req.msg["service_type"] - ) - ) - - -# 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 - - """ - - 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 - - -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 - - """ - s3i_directory = Directory( - s3i_dir_url="https://dir.s3i.vswf.dev/api/2/", - token=idp.get_token(TokenType.ACCESS_TOKEN), - ) - # TODO: How can this throw? - 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 load_config(config_filepath): - """Creates a json object from a json formatted file found at config_filepath. - - :param config_filepath: Path to json formatted file. - - """ - with open(config_filepath) as config_file: - config = json.load(config_file) - 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. - - """ - res = list(load_config(config) for config in config_filepaths) - return res - - -def id_to_endpoint(thing_id): - """Returns the endpoint for thing_id. - - :param thing_id: id - :returns: endpoint - :rtype: str - - """ - - 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)) - - """ - 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 diff --git a/fml40/worker.py b/fml40/worker.py deleted file mode 100644 index 9dff048..0000000 --- a/fml40/worker.py +++ /dev/null @@ -1,32 +0,0 @@ -from fml40.managing_actor import ManagingActor -from fml40.functionalities import functionalities -from fml40.app_logger import APP_LOGGER - - -class Worker(ManagingActor): - - def __init__(self, idp, config): - super().__init__(idp, config=config) - self.parse_functionalities() - - def parse_functionalities(self): - if self.config.get("functionalities", None) is None: - APP_LOGGER.debug("Digital twin has no functionalities setup") - return - APP_LOGGER.debug("Creating functionalities") - sender_id = self.config["thingId"] - for func in self.config["functionalities"]: - functionality = functionalities.FUNCTIONALITY_FACTORY[func["type"]] - receiver_id = func.get("receiver", None) - APP_LOGGER.debug("Creating functionality: {}".format(func["type"])) - if receiver_id is None: - functionality.start(self.idp, self.actor_ref, func["type"], func["proxy"]) - else: - functionality.start( - self.idp, - self.actor_ref, - func["type"], - func["proxy"], - sender_id, - receiver_id, - ) diff --git a/fml40s.py b/fml40s.py index 70ca7fd..d6c7972 100755 --- a/fml40s.py +++ b/fml40s.py @@ -7,27 +7,11 @@ import argparse import json -import threading -import time -from s3i import TokenType -from s3i import IdentityProvider -from s3i import Broker from ml.dt_factory import create_dt_ref -from ml.dt_factory import get_dt_names -from ml.tools import create_request -from ml.tools import get_requests from ml.tools import load_config -from ml.tools import get_receiver_callback_func -from ml.tools import get_s3i_broker -from ml.tools import GRANT_TYPES -from ml.dt_factory import DT_FACTORY from ml.app_logger import setup_logger -from ml.authentication import get_credentials_dispatch from ml.authentication import configure_credentials_parser -from ml.tools import send_requests -from ml.tools import send_request -from ml.tools import send_message -from ml.fml40.features.functionalities.forwards import send_moisture_get_value_requests + def create_argparser(): diff --git a/ml/managed_actor.py b/ml/managed_actor.py deleted file mode 100644 index 9cffab2..0000000 --- a/ml/managed_actor.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Provides an encapsulation class for digital twins allowing to run -it in a dedicated thread.""" -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, ref_managing_actor): - super().__init__() - self.managing_actor = ref_managing_actor.proxy() - self.managing_actor.register_managed_actor(name, self.actor_ref.proxy()) - self.__name = name - self.__class_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 class_name(self): - return self.__class_name - - @class_name.setter - def class_name(self, value): - self.__class_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): - json_out = { - "class": self.class_name, - "identifier": self.identifier, - } - if self.class_name != self.name: - json_out["name"] = self.name - - return json_out - - 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. - - """ - print( - """Hello, I'm a Managed Actor of a digital twin with the name {}.\n - You send me a message: {}""".format( - self.__name, message - ) - ) diff --git a/ml/managing_actor.py b/ml/managing_actor.py deleted file mode 100644 index 5e42801..0000000 --- a/ml/managing_actor.py +++ /dev/null @@ -1,69 +0,0 @@ -"""This is the administration shell of the forwarder. - It receives the service replys which are addressed to - the forwarder and schedules the further forwarder actions - when all necessary replys have arrived. - """ -import pykka -from s3i import TokenType -from s3i import Directory -from ml.app_logger import APP_LOGGER - - -class ManagingActor(pykka.ThreadingActor): - """This is the administration shell of the forwarder. - It receives the service replys which are addressed to - the forwarder and schedules the further forwarder actions - when all necessary replys have arrived. - """ - - def __init__(self): - super().__init__() - self.proxy_functionalities = {} - - def get_repr(self): - pass - # s3i_dir = Directory( - # s3i_dir_url="https://dir.s3i.vswf.dev/api/2/", token=self.access_token - # ) - # response = s3i_dir.queryThingIDBased(self.thing_id) - # return response - - def sync_down(self): - # TODO: Set attributes and values on top level - pass - - def on_receive(self, message): - """ Prints a greatings formula and message to stdout. - - :param message: Printable object - - """ - print("Hello, I'm a secretary\nYou send me a message %s" % message) - - def register_managed_actor(self, name, proxy): - """Adds functionaliy proxy under the name name. - - :param name: String identifying proxy - :param proxy: Functioanlity like object. - - """ - self.proxy_functionalities[name] = proxy - - def on_stop(self): - """Stops all functionality threads. - - """ - print("Stopping") - for key in self.proxy_functionalities: - pykka.ActorRegistry.get_by_class_name(key)[0].stop() - - def accept(self, functionality, parameters): - """FIXME! briefly describe function - - :param functionality: - :param parameters: - :returns: - :rtype: - - """ - self.proxy_functionalities[functionality].visit(self, parameters) diff --git a/requirements.txt b/requirements.txt index ed3d59e..f3b5107 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ https://git.rwth-aachen.de/kwh40/s3i/-/jobs/artifacts/master/raw/public/s3i-0.4-py3-none-any.whl?job=wheel -Pykka==2.0.2 pynput opcua requests diff --git a/scripts/get_token.py b/scripts/get_token.py deleted file mode 100644 index d56e6fe..0000000 --- a/scripts/get_token.py +++ /dev/null @@ -1,18 +0,0 @@ -import sys -sys.path.append("./") -import argparse -import json -from ml.authentication import configure_credentials_parser -from ml.authentication import get_token_from_args - - -def main(): - parser = argparse.ArgumentParser() - parser = configure_credentials_parser(parser) - args = parser.parse_args() - token = get_token_from_args(args) - print(token) - - -if __name__ == "__main__": - main() diff --git a/scripts/s3i_api.py b/scripts/s3i_api.py deleted file mode 100644 index 625e1d1..0000000 --- a/scripts/s3i_api.py +++ /dev/null @@ -1,277 +0,0 @@ -import sys - -sys.path.append("./") -import logging -import sys -import json -import argparse -from s3i.validation import validate_s3i_uuid -from s3i import IdentityProvider, Config, Directory, TokenType -from s3i.repository import Repository -from ml.authentication import configure_credentials_parser -from ml.authentication import get_token_from_args - -config_s_url = "https://config.s3i.vswf.dev/" -logger = logging.getLogger(__name__) - - -class Person: - def __init__(self, name="", thingId="", uuid="", secret=""): - self.name = name - self.thingId = thingId - self.uuid = uuid - self.secret = secret - self.password = "" - - -def person_from_config_response(new_person): - person_id = new_person.json()["thingIdentity"]["identifier"] - person_secret = new_person.json()["thingIdentity"]["secret"] - person_uuid = new_person.json()["personIdentity"]["identifier"] - r_val = Person("name", person_id, person_secret, person_uuid) - return r_val - - -def print_person(person): - print("Created a person named: {}".format(person[0])) - print("Created person has an identity (uuid): {}".format(person[3])) - print("Created DT for person has thing identity (uuid): {}".format( - person[1])) - print("Created DT for person has secret: {}".format(person[2])) - - -def delete_thing(access_token, thing_id): - config = Config(server_url=config_s_url, token=access_token) - response = config.delete_thing(thing_id) - if response.status_code == 204: - print("{thing_id} has been successfully deleted.") - elif response.status_code == 400: - print("{thing_id} does not exist.") - print(response.json()) - elif response.status_code == 401: - print("Access token is missing or invalid") - print(response.json()) - else: - print("Unexpected error") - print(response.json()) - - -def create_person(token, verbose=False): - person_name = input("Please enter the selected username for new user: ") - person_password = input( - "Please enter the selected password for new user: ") - s3i_config = Config(server_url=config_s_url, token=token) - new_person = s3i_config.create_person(username=person_name, - password=person_password) - if new_person.status_code == 400: - logger.critical( - "Person could not be created because some key is missing.\n" - "Shutting down.") - sys.exit("400") - if new_person.status_code == 409: - logger.critical( - "Person could not be create because due to a conflict.\n" - "Shutting down.") - sys.exit("409") - - person = person_from_config_response(new_person) - person.password = person_password - if verbose: - print_person(person) - - return person - - -def create_thing_in_repo(config, thing_id): - """Creates a copy of an existing thing in ditto repository.""" - res = config.create_cloud_copy(thing_id) - res = response.json() - status_code = res.status_code - if status_code == 201: - print("A copy has been successfully created.") - else: - print("An error occured: {}".format(status_code)) - - -def create_thing_in_directory(config, with_broker): - """Creates a new thing in ditto directory.""" - response = config.create_thing() - res = response.json() - if response.status_code == 201: - thing_id = res["identifier"] - secret = res["secret"] - print(json.dumps(res, indent=2)) - if with_broker: - response = config.create_broker_queue(thing_id) - if response.status_code != 201: - print("An error occured: {}".format({response.status_code})) - return thing_id, secret - print("An error occured: {}".format({response.status_code})) - return None - - -def print_response(response): - print(json.dumps(response, indent=4)) - - -def get_ditto_instance(token, use_repo): - """Returns an instance of class Repository if use_repo is True, - otherwise it is of type Directory.""" - if use_repo: - return Repository(token=token) - return Directory(token=token) - - -def show_thing(token, thing_id, use_repo): - ditto = get_ditto_instance(token, use_repo) - response = ditto.queryThingIDBased(thing_id) - print_response(response) - - -def update_person(s3i_dir, person, thing): - dt_person_body = s3i_dir.queryThingIDBased(person.thingId) - dt_person_body["attributes"]["defaultHMI"] = thing[0] - s3i_dir.updateThingIDBased(person.thingId, dt_person_body) - - -def update_thing_default(s3i_dir, thing, person): - # step 4.1: update the new HMI (add name, type...) - hmi_body = s3i_dir.queryThingIDBased(thing[0]) - hmi_body["attributes"]["name"] = "HMI of {}".format(person.name) - hmi_body["attributes"]["type"] = "hmi" - s3i_dir.updateThingIDBased(thing[0], hmi_body) - - -def update_thing(thing_id, access_token, body, url): - s3i_dir = Directory(s3i_dir_url=url, token=access_token) - s3i_dir.updateThingIDBased(thing_id, body) - response = s3i_dir.queryThingIDBased(thing_id) - print_response(response) - - -def get_identifier_from_file(filepath): - with open(filepath, "r", encoding="utf-8") as cred_file: - obj = json.load(cred_file) - thing_id = obj.get("identifier", None) - if not thing_id: - thing_id = obj.get("thingId") - return thing_id - - -def configure_identifier_parser(parser): - group = parser.add_mutually_exclusive_group(required=True) - group.add_argument( - "--i", - default=None, - help="Id (in s3i format) of the thing to be printed out.") - group.add_argument("--f", - default=None, - help="Filepath to credentials in json format.") - - -def config_ditto_parser(parser): - parser.add_argument( - "--repo", - action="store_true", - help="Executes operation on repository if set (directory otherwise).") - - -def config_show_parser(parser): - config_ditto_parser(parser) - configure_credentials_parser(parser) - configure_identifier_parser(parser) - - -def config_create_parser(parser): - subparsers = parser.add_subparsers(required=True, dest="create_cmd") - dir_parser = subparsers.add_parser( - "dir", help="Creates a new thing in directory.") - dir_parser.add_argument("--broker", - action="store_true", - help="Creates a broker queue.") - repo_parser = subparsers.add_parser( - "repo", help="Creates a copy of an existing thing in repository.") - configure_identifier_parser(repo_parser) - configure_credentials_parser(dir_parser) - configure_credentials_parser(repo_parser) - - -def config_update_parser(parser): - config_ditto_parser(parser) - parser.add_argument( - "config", help="Path to json formatted file that specifies the thing.") - configure_credentials_parser(parser) - - -def config_delete_parser(parser): - configure_credentials_parser(parser) - configure_identifier_parser(parser) - - -def create_arg_parser(): - parser = argparse.ArgumentParser( - description="This script allows creation, alteration and printing " - "of things located in the s3i directory.\n" - "Structure of credential file is as follows:\n" - '{ "name" : "...","password": "..." } ') - subparsers = parser.add_subparsers(dest="command") - show_parser = subparsers.add_parser( - "show", help="Prints json representation of a thing.") - - create_parser = subparsers.add_parser("create", help="Creates a thing") - update_parser = subparsers.add_parser("update", help="Updates a thing") - delete_parser = subparsers.add_parser("delete", help="Delete a thing") - - config_show_parser(show_parser) - config_create_parser(create_parser) - config_update_parser(update_parser) - config_delete_parser(delete_parser) - return parser - - -def get_thing_id_from_args(args): - thing_id = None - if args.f: - thing_id = get_identifier_from_file(args.f) - elif args.i: - thing_id = args.i - return thing_id - - -def get_url_from_args(args): - url = "https://dir.s3i.vswf.dev/api/2/" - if args.repo: - url = "https://ditto.s3i.vswf.dev/api/2/" - return url - -def main(): - # TODO: Distinguish between Repository and Directory class, - parser = create_arg_parser() - args = parser.parse_args() - token = get_token_from_args(args) - if args.command == "show": - url = get_url_from_args(args) - use_repo = args.repo - thing_id = get_thing_id_from_args(args) - if validate_s3i_uuid(thing_id): - show_thing(token, thing_id, use_repo) - else: - print("{} is not a valid s3i uuid.".format(thing_id)) - elif args.command == "update": - with open(args.config, "r") as config_file: - body = json.load(config_file) - url = get_url_from_args(args) - update_thing(body["thingId"], token, body, url) - elif args.command == "create": - config = Config(token=token) - if args.create_cmd == "dir": - create_thing_in_directory(config, args.broker) - else: - thing_id = get_thing_id_from_args(args) - create_thing_in_repo(config, thing_id) - elif args.command == "delete": - delete_thing(token, args.thingId) - - -if __name__ == "__main__": - main() diff --git a/scripts/thing_to_ditto_thing.py b/scripts/thing_to_ditto_thing.py deleted file mode 100644 index abd8fa1..0000000 --- a/scripts/thing_to_ditto_thing.py +++ /dev/null @@ -1,34 +0,0 @@ -import json - -def add_feature(features, feature_id, feature_name): - short_id = feature_id[14:] - properties = {feature_name : ""} - features[short_id] = {"properties" : properties} - -def transform(obj, features): - for key in obj: - value = obj[key] - if isinstance(value, dict): - transform(value, features) - elif isinstance(value, list): - for ele in value: - if isinstance(ele, dict): - transform(ele, features) - elif isinstance(value, str): - if value.startswith("ditto-feature:"): - add_feature(features, value, key) - -def main(): - filename = "" - with open(filename, "r") as in_file: - thing = json.load(in_file) - features = thing.get("features", None) - if not features: - features = {} - thing["features"] = features - - transform(thing, features) - print(json.dumps(thing, indent=2)) - - -main() diff --git a/setup.py b/setup.py index 6975678..74de479 100644 --- a/setup.py +++ b/setup.py @@ -8,16 +8,16 @@ setuptools.setup( - name = "ml", - version = "0.1", - author = "Kompetenzzentrum Wald und Holz 4.0", - author_email = "s3i@kwh40.de", - description = "fml40 reference implementation basic functions", + name="ml", + version="0.1", + author="Kompetenzzentrum Wald und Holz 4.0", + author_email="s3i@kwh40.de", + description="fml40 reference implementation basic functions", long_description=long_description, long_description_content_type="text/markdown", url = "https://www.kwh40.de/", packages=setuptools.find_packages(), - install_requires= [ + install_requires=[ "Pykka", "pylint", "websocket", diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 428a99b..0000000 --- a/tests/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -import sys -sys.path.append("./../../s3i") diff --git a/tests/res/dummy_credentials.json b/tests/res/dummy_credentials.json deleted file mode 100644 index d1d28b9..0000000 --- a/tests/res/dummy_credentials.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name" : "user1", - "password" : "eins" -} diff --git a/tests/res/malformatted.json b/tests/res/malformatted.json deleted file mode 100644 index 92f72ed..0000000 --- a/tests/res/malformatted.json +++ /dev/null @@ -1 +0,0 @@ -blank diff --git a/tests/test_benedict.py b/tests/test_benedict.py deleted file mode 100644 index 979b174..0000000 --- a/tests/test_benedict.py +++ /dev/null @@ -1,20 +0,0 @@ -from benedict import benedict - - -class MyDict(benedict): - def __init__(self, init_dict): - for k, v in init_dict.items(): - if isinstance(v, dict): - init_dict[k] = MyDict(v) - super().__init__(init_dict) - - def __setitem__(self, key, value): - print("dict set") - if isinstance(value, dict): - _value = MyDict(value) - else: - _value = value - - super().__setitem__(key, value) - - diff --git a/tests/test_digital_twins.py b/tests/test_digital_twins.py deleted file mode 100644 index 90e06b5..0000000 --- a/tests/test_digital_twins.py +++ /dev/null @@ -1,226 +0,0 @@ -"""Digital twin testing""" -import unittest -import uuid -from s3i import ServiceRequest -from s3i import GetValueRequest -from ml.tools import GRANT_TYPES -from ml.tools import load_config -from ml.tools import get_requests -from ml.tools import send_requests -from ml.tools import get_s3i_broker -from ml.tools import get_receiver_callback_func -from ml.tools import create_request -from ml.app_logger import setup_logger -from ml.dt_factory import create_dt_ref -from ml.dt_factory import DT_FACTORY -from ml.authentication import get_client_id_and_secret -from customer_code_example.harvester_john_deere import john_deere -from customer_code_example.harvester_ponsse import ponsse -from customer_code_example.forwarder_komatsu import komatsu -from ml.fml40.features.functionalities.accepts_proximity_alert import ( - AcceptsProximityAlert, -) - -setup_logger("Testing") - -DT_FACTORY["JohnDeere"] = john_deere.JohnDeere -DT_FACTORY["Komatsu"] = komatsu.Komatsu -DT_FACTORY["Ponsse"] = ponsse.Ponsse -CONFIG_PATH_DEERE = "./configs/config_harvester_john_deere.json" -CONFIG_PATH_PONSSE = "./configs/config_harvester_ponsse.json" -CONFIG_PATH_KOMATSU = "./configs/config_forwarder_komatsu.json" - - -class TestDigitalTwins(unittest.TestCase): - """Implements tests for digital twins.""" - - def test_john_deere(self): - """Testing of the digital twin John Deere. - - """ - deere_config = load_config(CONFIG_PATH_DEERE) - _, client_secret = get_client_id_and_secret("./credentials/john_deere.json") - - grant_type = GRANT_TYPES[0] - deere_ref = create_dt_ref( - model=deere_config, - grant_type=grant_type, - secret=client_secret, - username="", - password="", - is_broker=True, - is_repo=False, - ) - deere_proxy = deere_ref.proxy() - self.assertEqual(deere_proxy.name.get(), "John Deere Harvester FBZ") - self.assertEqual( - deere_proxy.thing_id.get(), "s3i:c5d5cd58-8786-40b2-8079-5f2de443de36" - ) - - features = deere_proxy.features.get() - self.assertEqual(len(features), 8) - - results = features.get("fml40::AcceptsProximityAlert") - self.assertEqual(results.proxy().name.get(), "AcceptsProximityAlert 0") - - results = features.get("fml40::AcceptsFellingJobs") - self.assertEqual(results.proxy().name.get(), "AcceptsFellingJobs 1") - - results = features.get("fml40::Harvests") - self.assertEqual(results.proxy().name.get(), "Harvests 2") - - results = features.get("ml40::ManagesJobs") - self.assertEqual(results.proxy().name.get(), "ManagesJobs 3") - - results = features.get("fml40::ProvidesProductionData") - self.assertEqual(results.proxy().name.get(), "ProvidesProductionData 4") - - results = features.get("ml40::Location") - self.assertEqual(results.proxy().name.get(), "Location 5") - - results = features.get("ml40::Shared") - self.assertEqual(results.proxy().name.get(), "Shared 6") - - results = features.get("ml40::Composite") - self.assertEqual(results.proxy().name.get(), "Composite 7") - - def test_ponsse(self): - ponsse_config = load_config(CONFIG_PATH_PONSSE) - _, client_secret = get_client_id_and_secret("./credentials/ponsse.json") - - grant_type = GRANT_TYPES[0] - ponsse_ref = create_dt_ref( - model=ponsse_config, - grant_type=grant_type, - secret=client_secret, - username="", - password="", - is_broker=True, - is_repo=False, - ) - ponsse_proxy = ponsse_ref.proxy() - self.assertEqual(ponsse_proxy.name.get(), "Ponsse Harvester") - self.assertEqual( - ponsse_proxy.thing_id.get(), "s3i:5e83b933-331f-4278-b318-b7fdcb0e4872" - ) - - features = ponsse_proxy.features.get() - self.assertEqual(len(features), 7) - - results = features.get("fml40::AcceptsProximityAlert") - self.assertEqual(results.proxy().name.get(), "alert 0") - - results = features.get("fml40::AcceptsFellingJobs") - self.assertEqual(results.proxy().name.get(), "felling job 1") - - results = features.get("fml40::Harvests") - self.assertEqual(results.proxy().name.get(), "harvests 2") - - results = features.get("ml40::ManagesJobs") - self.assertEqual(results.proxy().name.get(), "manages 3") - - results = features.get("fml40::ProvidesProductionData") - self.assertEqual(results.proxy().name.get(), "production 4") - - results = features.get("ml40::Location") - self.assertEqual(results.proxy().name.get(), "location 5") - - results = features.get("ml40::Shared") - self.assertEqual(results.proxy().name.get(), "shared 6") - - def test_komatsu(self): - komatsu_config = load_config(CONFIG_PATH_KOMATSU) - _, client_secret = get_client_id_and_secret("./credentials/komatsu.json") - - grant_type = GRANT_TYPES[0] - komatsu_ref = create_dt_ref( - model=komatsu_config, - grant_type=grant_type, - secret=client_secret, - username="", - password="", - is_broker=True, - is_repo=False, - ) - komatsu_proxy = komatsu_ref.proxy() - self.assertEqual(komatsu_proxy.name.get(), "Komatsu Forwarder") - self.assertEqual( - komatsu_proxy.thing_id.get(), "s3i:3154edfa-5b04-4a28-b803-d6ec46135c19" - ) - - features = komatsu_proxy.features.get() - self.assertEqual(len(features), 6) - - results = features.get("fml40::AcceptsProximityAlert") - self.assertEqual(results.proxy().name.get(), "Proxi 1") - - results = features.get("fml40::AcceptsForwardingJobs") - self.assertEqual(results.proxy().name.get(), "Forwarding 2") - - results = features.get("fml40::Forwards") - self.assertEqual(results.proxy().name.get(), "Forwards 3") - - results = features.get("ml40::Location") - self.assertEqual(results.proxy().name.get(), "Location 4") - - results = features.get("ml40::Shared") - self.assertEqual(results.proxy().name.get(), "Shared 5") - - results = features.get("ml40::Composite") - self.assertEqual(results.proxy().name.get(), "Composite 6") - - def test_passability_service(self): - passability_config = load_config(CONFIG_PATH_PASSABILITY_SERVICE) - _, client_secret = get_client_id_and_secret( - "./credentials/passability_service.json" - ) - - grant_type = GRANT_TYPES[0] - passability_ref = create_dt_ref( - model=passability_config, - grant_type=grant_type, - secret=client_secret, - username="", - password="", - is_broker=True, - is_repo=False, - ) - passability_proxy = passability_ref.proxy() - self.assertEqual(passability_proxy.name.get(), "Passability Service") - self.assertEqual( - passability_proxy.thing_id.get(), "s3i:fcf78290-f462-4c9b-8fcb-e63603702983" - ) - - features = passability_proxy.features.get() - self.assertEqual(len(features), 1) - - result = features.get("fml40::ProvidesPassabilityInformation") - result_proxy = result.proxy() - self.assertEqual(result_proxy.name.get(), "Passability Information Provider") - - # Send passability request to passsability service - forwarder_ref = self.load_passability_forwarder() - forwarder_proxy = forwarder_ref.proxy() - parameters = {"load": 200, "moisture": 300} - req = create_request( - forwarder_proxy.thing_id.get(), - passability_proxy.thing_id.get(), - "fml40::ProvidesPassabilityInformation", - parameters, - ) - req_msg = req.msg.__str__() - passability_proxy.on_receive(req_msg, False) - features = passability_proxy.features.get() - self.assertEqual(len(features), 1) - result = features.get("fml40::ProvidesPassabilityInformation") - result_proxy = result.proxy() - reply = result_proxy.service_reply.get() - self.assertEqual(reply.msg.get("sender", ""), passability_proxy.thing_id.get()) - self.assertEqual(reply.msg.get("messageType", ""), "serviceReply") - self.assertEqual( - reply.msg.get("serviceType", ""), "fml40::ProvidesPassabilityInformation" - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_get_token.py b/tests/test_get_token.py deleted file mode 100644 index db0b8ad..0000000 --- a/tests/test_get_token.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Testing get_token.py""" -from unittest import TestCase -from json.decoder import JSONDecodeError -from ml.authentication import get_username_and_password - - -class TestGetTokenScript(TestCase): - def test_get_user_name_and_password(self): - - self.assertRaises( - FileNotFoundError, get_username_and_password, "file_not_available.json" - ) - self.assertRaises( - JSONDecodeError, get_username_and_password, "tests/res/malformatted.json" - ) - username, password = get_username_and_password( - "tests/res/dummy_credentials.json" - ) - self.assertEqual(username, "user1") - self.assertEqual(password, "eins") diff --git a/tests/test_threading_futhur.py b/tests/test_threading_futhur.py deleted file mode 100644 index 8fdbc9a..0000000 --- a/tests/test_threading_futhur.py +++ /dev/null @@ -1,10 +0,0 @@ -import pykka -a = pykka.ThreadingFuture() -b = pykka.ThreadingFuture() -c = pykka.ThreadingFuture() -f = a.join(b, c) -a.set('def') -b.set(123) -c.set(False) -b.set(345) -print(f.get()) -- GitLab From fd21b78e3aae3c2e3f305d0b0f863aaeca8c5d28 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Thu, 29 Oct 2020 20:55:10 +0100 Subject: [PATCH 58/84] move the dt_creation and hmi to the folder demo --- demo/dt_creation.py | 2 +- demo/dt_creation_hmi.py | 2 +- path/__init__.py | 0 path/path.py | 0 tests/test_path.py | 0 5 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 path/__init__.py create mode 100644 path/path.py create mode 100644 tests/test_path.py diff --git a/demo/dt_creation.py b/demo/dt_creation.py index 348d637..ba168ab 100644 --- a/demo/dt_creation.py +++ b/demo/dt_creation.py @@ -56,7 +56,7 @@ {"class": "ml40::Composite", "targets": [config_engine, config_cran]}]) setup_logger(dt_name) -dt_model = load_config('configs/{}'.format(config_file_name)) +dt_model = load_config(config_file_name) dt = create_dt_ref(model=dt_model, grant_type="client_credentials", secret=dt_secret, is_broker_rest=True, diff --git a/demo/dt_creation_hmi.py b/demo/dt_creation_hmi.py index 74e4790..a5d98fd 100644 --- a/demo/dt_creation_hmi.py +++ b/demo/dt_creation_hmi.py @@ -12,7 +12,7 @@ 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_model = load_config(config_file_name) 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) diff --git a/path/__init__.py b/path/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/path/path.py b/path/path.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_path.py b/tests/test_path.py new file mode 100644 index 0000000..e69de29 -- GitLab From 51f9806a4253696dfcebd38607e80b8da49efe40 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Thu, 29 Oct 2020 20:55:31 +0100 Subject: [PATCH 59/84] rewritte the fml40 parse --- fml40s.py | 90 +++++++++++++++++++++++++++---------------------------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/fml40s.py b/fml40s.py index d6c7972..fae6023 100755 --- a/fml40s.py +++ b/fml40s.py @@ -6,45 +6,26 @@ """ import argparse -import json -from ml.dt_factory import create_dt_ref from ml.tools import load_config -from ml.app_logger import setup_logger -from ml.authentication import configure_credentials_parser - +import os +from path.path import PROJECT_ROOT +from ml.dt_factory import create_dt_ref def create_argparser(): - """Creates an agrparser. + parent_parser = argparse.ArgumentParser(prog="", description="FML40 argument parse") - :returns: Created argparser - :rtype: Argparser + sub_parsers = parent_parser.add_subparsers(dest="command") - """ - parent_parser = argparse.ArgumentParser(prog="", description="") - parent_parser.add_argument("--log", nargs=1, default="CRITICAL") - subparsers = parent_parser.add_subparsers(dest="command") - launch_parser = subparsers.add_parser("launch") - launch_parser.add_argument("dt_config_fp", nargs=1, type=str) - launch_parser.add_argument("--f", - default=None, - help="Filepath to credentials in json format.") - configure_credentials_parser(launch_parser) - list_parser = subparsers.add_parser("list") - list_parser.add_argument("class_type", - nargs=1, - choices=["functionalities", "dts"]) - return parent_parser + launch_parser = sub_parsers.add_parser("launch") + launch_parser.add_argument("dt_config", nargs=1, type=str, help="path of the configuration file for the digital twin") + list_parser = sub_parsers.add_parser("list") + list_parser.add_argument("ml", nargs=1, + choices=["dts"], # Todo list all ml and fml40 functionalities and properties + help="list the current digital twins configured") -def print_digital_twins(dt_names): - print("The following digital twins are " - "available:\n\n\t{}".format("\n\t".join(dt_names))) - - -def print_functionalities(func_names): - print("The following functionalities are available:" - "\n\n\t{}".format("\n\t".join(func_names))) + return parent_parser def main(): @@ -54,22 +35,39 @@ def main(): args = parser.parse_args() if args.command == "list": - if args.class_type[0] == "dts": - dt_names = get_dt_names() - print_digital_twins(dt_names) + if args.ml[0] == "dts": + g = os.walk(os.path.join(PROJECT_ROOT, "configs")) + dt_name = list() + for path, dir_list, file_list in g: + for file_name in file_list: + dt_name.append(load_config(file_name)["attributes"].get("name", None)) + print(dt_name) + elif args.command == "launch": - dt_config_fp = args.dt_config_fp[0] - model = load_config(dt_config_fp) - app_logger = setup_logger(__name__) - app_logger.debug("Parsed model: %s", json.dumps(model, indent=2)) - _, secret, username, password = get_credentials_dispatch( - args.g, args.f) - thing_ref = create_dt_ref(model, args.g, secret, username, password, - True, False) - thing_proxy = thing_ref.proxy() - thing_proxy.run_forever() - access_token = thing_proxy.access_token.get() - print(access_token) + dt_config = args.dt_config[0] + try: + model = load_config(dt_config) + except FileNotFoundError: + print("{} can not be found".format(dt_config)) + return + secret = input("Please enter the client secret of the digital twin with id {}: ".format(model["thingId"])) + is_broker = input("Does this DT has a S³I-B endpoint? (j/n) ") + if "j" == is_broker.lower(): + is_broker = True + elif "n" == is_broker.lower(): + is_broker = False + is_repo = input("Does this DT has a cloud copy in the S³I Repository? (j/n) ") + if "j" == is_repo.lower(): + is_repo = True + elif "n" == is_repo.lower(): + is_repo = False + + dt = create_dt_ref(model=model, grant_type="client_credentials", + secret=secret, + is_broker_rest=True, + is_broker=True, + is_repo=True) + dt.run_forever() if __name__ == "__main__": -- GitLab From 2fbbb5abec67dd073def427b688ddfbddfb958a9 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Thu, 29 Oct 2020 20:56:08 +0100 Subject: [PATCH 60/84] change the config data output root --- ml/tools.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ml/tools.py b/ml/tools.py index 84dab0a..561451a 100644 --- a/ml/tools.py +++ b/ml/tools.py @@ -13,6 +13,7 @@ from s3i import IdentityProvider from ml.app_logger import APP_LOGGER from ml.authentication import GRANT_TYPES +from path.path import PROJECT_ROOT IDENTITY_PROVIDER_URL = "https://idp.s3i.vswf.dev/" @@ -295,7 +296,7 @@ def dir_name_to_default_endpoints(idp, name): return endpoint -def make_sub_thing(name, roles, id="", features=[]): +def make_sub_thing(name, roles, features=[]): sub_thing = { "class": "ml40::Thing", "name": name, @@ -332,20 +333,20 @@ def make_thing_config(dt_id, name, roles, features=[]): } } - cwd = os.getcwd() - path = os.path.join(cwd, "configs", "config_{}.json".format(name)) + path = os.path.join(PROJECT_ROOT, "configs", "{}.json".format(name)) with open(path, 'wb') as file: file.write(json.dumps(config_file).encode('utf-8')) - return "config_{}.json".format(name) + return "{}.json".format(name) -def load_config(config_filepath): +def load_config(config_file_name): """Creates a json object from a json formatted file found at config_filepath. :param config_filepath: Path to json formatted file. """ - with open(config_filepath) as config_file: + config_file_path = os.path.join(PROJECT_ROOT, "configs", config_file_name) + with open(config_file_path) as config_file: config = json.load(config_file) return config -- GitLab From d5641de517e252841865359f4a2ee4f3f7fed72c Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Thu, 29 Oct 2020 20:56:19 +0100 Subject: [PATCH 61/84] add path --- path/path.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/path/path.py b/path/path.py index e69de29..f5aca10 100644 --- a/path/path.py +++ b/path/path.py @@ -0,0 +1,8 @@ +import os +import sys + + +path = sys.modules[__name__].__file__ if __name__ == "__main__" else __file__ + +PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(path), os.path.pardir)) + -- GitLab From 11ebd697793242398e76ed1fb7c7d157a5cff66d Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Thu, 29 Oct 2020 22:20:05 +0100 Subject: [PATCH 62/84] fix path problem --- configs/my_dt_harvester.json | 1 + ml/tools.py | 6 +++--- path/path.py | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 configs/my_dt_harvester.json diff --git a/configs/my_dt_harvester.json b/configs/my_dt_harvester.json new file mode 100644 index 0000000..48b2009 --- /dev/null +++ b/configs/my_dt_harvester.json @@ -0,0 +1 @@ +{"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 diff --git a/ml/tools.py b/ml/tools.py index 561451a..88e7222 100644 --- a/ml/tools.py +++ b/ml/tools.py @@ -13,7 +13,7 @@ from s3i import IdentityProvider from ml.app_logger import APP_LOGGER from ml.authentication import GRANT_TYPES -from path.path import PROJECT_ROOT +from path.path import get_root IDENTITY_PROVIDER_URL = "https://idp.s3i.vswf.dev/" @@ -333,7 +333,7 @@ def make_thing_config(dt_id, name, roles, features=[]): } } - path = os.path.join(PROJECT_ROOT, "configs", "{}.json".format(name)) + path = os.path.join(get_root(), "configs", "{}.json".format(name)) with open(path, 'wb') as file: file.write(json.dumps(config_file).encode('utf-8')) return "{}.json".format(name) @@ -345,7 +345,7 @@ def load_config(config_file_name): :param config_filepath: Path to json formatted file. """ - config_file_path = os.path.join(PROJECT_ROOT, "configs", config_file_name) + config_file_path = os.path.join(get_root(), "configs", config_file_name) with open(config_file_path) as config_file: config = json.load(config_file) return config diff --git a/path/path.py b/path/path.py index f5aca10..68d25b6 100644 --- a/path/path.py +++ b/path/path.py @@ -2,7 +2,7 @@ import sys -path = sys.modules[__name__].__file__ if __name__ == "__main__" else __file__ - -PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(path), os.path.pardir)) +def get_root(): + path = sys.modules[__name__].__file__ if __name__ == "__main__" else __file__ + return os.path.abspath(os.path.join(os.path.dirname(path), os.path.pardir)) -- GitLab From d14ad2b34e42dba42ddcff0874c4c9a6ff5fa122 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Thu, 29 Oct 2020 22:48:55 +0100 Subject: [PATCH 63/84] fix path problem for jupyter notebook --- ml/tools.py | 8 ++++---- path/path.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ml/tools.py b/ml/tools.py index 88e7222..dba6eb3 100644 --- a/ml/tools.py +++ b/ml/tools.py @@ -321,7 +321,7 @@ def make_feature_config(class_name, identifier="", name="", subFeatures=""): return config_json -def make_thing_config(dt_id, name, roles, features=[]): +def make_thing_config(dt_id, name, roles, features=[], root=__file__): config_file = { "thingId": dt_id, "policyId": dt_id, @@ -333,19 +333,19 @@ def make_thing_config(dt_id, name, roles, features=[]): } } - path = os.path.join(get_root(), "configs", "{}.json".format(name)) + path = os.path.join(get_root(root), "configs", "{}.json".format(name)) with open(path, 'wb') as file: file.write(json.dumps(config_file).encode('utf-8')) return "{}.json".format(name) -def load_config(config_file_name): +def load_config(config_file_name, root=__file__): """Creates a json object from a json formatted file found at config_filepath. :param config_filepath: Path to json formatted file. """ - config_file_path = os.path.join(get_root(), "configs", config_file_name) + config_file_path = os.path.join(get_root(root), "configs", config_file_name) with open(config_file_path) as config_file: config = json.load(config_file) return config diff --git a/path/path.py b/path/path.py index 68d25b6..65a3563 100644 --- a/path/path.py +++ b/path/path.py @@ -2,7 +2,7 @@ import sys -def get_root(): - path = sys.modules[__name__].__file__ if __name__ == "__main__" else __file__ +def get_root(file_path): + path = sys.modules[__name__].__file__ if __name__ == "__main__" else file_path return os.path.abspath(os.path.join(os.path.dirname(path), os.path.pardir)) -- GitLab From ea8c0652816356d9107406d269f6c8193f13c466 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Thu, 29 Oct 2020 23:17:17 +0100 Subject: [PATCH 64/84] remove path folder --- configs/my HMI.json | 1 + demo/dt_creation.py | 8 ++++++-- demo/dt_creation_hmi.py | 8 ++++++-- ml/tools.py | 5 ++--- path/__init__.py | 0 path/path.py | 8 -------- 6 files changed, 15 insertions(+), 15 deletions(-) create mode 100644 configs/my HMI.json delete mode 100644 path/__init__.py delete mode 100644 path/path.py diff --git a/configs/my HMI.json b/configs/my HMI.json new file mode 100644 index 0000000..ad5d9a8 --- /dev/null +++ b/configs/my HMI.json @@ -0,0 +1 @@ +{"thingId": "s3i:7f46a255-3245-439e-84f0-090f0b221965", "policyId": "s3i:7f46a255-3245-439e-84f0-090f0b221965", "attributes": {"class": "ml40::Thing", "name": "my HMI", "roles": [{"class": "ml40::HMI"}], "features": []}} \ No newline at end of file diff --git a/demo/dt_creation.py b/demo/dt_creation.py index ba168ab..8a3ac7f 100644 --- a/demo/dt_creation.py +++ b/demo/dt_creation.py @@ -7,6 +7,9 @@ from ml.app_logger import APP_LOGGER, setup_logger import time from config import * +import os + +root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) dt_creation_app_id = dt_creation_app_id dt_creation_app_secret = dt_creation_app_secret @@ -54,9 +57,10 @@ features=[{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}, {"class": "ml40::Composite", - "targets": [config_engine, config_cran]}]) + "targets": [config_engine, config_cran]}], + root=root_path) setup_logger(dt_name) -dt_model = load_config(config_file_name) +dt_model = load_config(config_file_name, root=root_path) dt = create_dt_ref(model=dt_model, grant_type="client_credentials", secret=dt_secret, is_broker_rest=True, diff --git a/demo/dt_creation_hmi.py b/demo/dt_creation_hmi.py index a5d98fd..e8fe5aa 100644 --- a/demo/dt_creation_hmi.py +++ b/demo/dt_creation_hmi.py @@ -7,12 +7,16 @@ import requests import json from config import * +import os + +root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) + 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(config_file_name) + root=root_path) +hmi_model = load_config(config_file_name, root=root_path) 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) diff --git a/ml/tools.py b/ml/tools.py index dba6eb3..ce97b19 100644 --- a/ml/tools.py +++ b/ml/tools.py @@ -13,7 +13,6 @@ from s3i import IdentityProvider from ml.app_logger import APP_LOGGER from ml.authentication import GRANT_TYPES -from path.path import get_root IDENTITY_PROVIDER_URL = "https://idp.s3i.vswf.dev/" @@ -333,7 +332,7 @@ def make_thing_config(dt_id, name, roles, features=[], root=__file__): } } - path = os.path.join(get_root(root), "configs", "{}.json".format(name)) + path = os.path.join(root, "configs", "{}.json".format(name)) with open(path, 'wb') as file: file.write(json.dumps(config_file).encode('utf-8')) return "{}.json".format(name) @@ -345,7 +344,7 @@ def load_config(config_file_name, root=__file__): :param config_filepath: Path to json formatted file. """ - config_file_path = os.path.join(get_root(root), "configs", config_file_name) + config_file_path = os.path.join(root, "configs", config_file_name) with open(config_file_path) as config_file: config = json.load(config_file) return config diff --git a/path/__init__.py b/path/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/path/path.py b/path/path.py deleted file mode 100644 index 65a3563..0000000 --- a/path/path.py +++ /dev/null @@ -1,8 +0,0 @@ -import os -import sys - - -def get_root(file_path): - path = sys.modules[__name__].__file__ if __name__ == "__main__" else file_path - return os.path.abspath(os.path.join(os.path.dirname(path), os.path.pardir)) - -- GitLab From 0b910996adf201cf637db76431dea2bb3d86b789 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Fri, 30 Oct 2020 13:12:20 +0100 Subject: [PATCH 65/84] fix location class bug --- ml/ml40/features/properties/values/location.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ml/ml40/features/properties/values/location.py b/ml/ml40/features/properties/values/location.py index 624eab7..f9d2ccd 100644 --- a/ml/ml40/features/properties/values/location.py +++ b/ml/ml40/features/properties/values/location.py @@ -44,4 +44,4 @@ def to_json(self): if self.orientation is not None: self.__json_out["orientation"] = self.orientation - return self.to_json() \ No newline at end of file + return self.__json_out \ No newline at end of file -- GitLab From 75c5607f4460cfd0c47c26df3d9a33407993278e Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Fri, 30 Oct 2020 13:12:41 +0100 Subject: [PATCH 66/84] fix build feature bug --- ml/dt_factory.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ml/dt_factory.py b/ml/dt_factory.py index 4a6d779..7aa1fc1 100644 --- a/ml/dt_factory.py +++ b/ml/dt_factory.py @@ -242,6 +242,8 @@ def build_feature(feature): APP_LOGGER.debug("Adding feature: %s" % feature_class_name) feature_instance = feature_obj() for key in feature.keys(): + if key == "class": + continue if key == "targets": build_sub_thing(feature_instance, feature) elif key == "subFeatures": -- GitLab From 8685b26f14d63c2e7023bcd1333101eb62110eb1 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Fri, 30 Oct 2020 13:13:24 +0100 Subject: [PATCH 67/84] fix to_json() bug --- ml/thing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ml/thing.py b/ml/thing.py index 53b2961..d07fe5f 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -525,7 +525,7 @@ def to_json(self): self.dt_json["attributes"]["features"] = list() for key in self.roles.keys(): self.dt_json["attributes"]["roles"].append(self.roles[key].to_json()) - for key in self.features: + for key in self.features.keys(): self.dt_json["attributes"]["features"].append(self.features[key].to_json()) return self.dt_json -- GitLab From 28e71f6b89e61bfe061879560e7c1d40ef5c8d17 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Fri, 30 Oct 2020 13:13:52 +0100 Subject: [PATCH 68/84] add location to configuration file --- demo/dt_creation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/demo/dt_creation.py b/demo/dt_creation.py index 8a3ac7f..8bffe5a 100644 --- a/demo/dt_creation.py +++ b/demo/dt_creation.py @@ -56,6 +56,7 @@ 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::Location", "longitude": 6.45435, "latitude": 52.543534}, {"class": "ml40::Composite", "targets": [config_engine, config_cran]}], root=root_path) -- GitLab From f2e7e1f1555882d7523ec840c55e824f5ad3df60 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 2 Nov 2020 10:35:01 +0100 Subject: [PATCH 69/84] clear the requirement --- requirements.txt | 6 +----- setup.py | 5 ----- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/requirements.txt b/requirements.txt index f3b5107..495264c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,2 @@ https://git.rwth-aachen.de/kwh40/s3i/-/jobs/artifacts/master/raw/public/s3i-0.4-py3-none-any.whl?job=wheel -pynput -opcua -requests -asyncio -pylint==2.4.4 +https://git.rwth-aachen.de/kwh40/fml40-reference-implementation/-/jobs/artifacts/dev_chen/raw/public/fml40-0.1-py3-none-any.whl?job=wheel \ No newline at end of file diff --git a/setup.py b/setup.py index 74de479..29bf98b 100644 --- a/setup.py +++ b/setup.py @@ -18,13 +18,8 @@ url = "https://www.kwh40.de/", packages=setuptools.find_packages(), install_requires=[ - "Pykka", "pylint", - "websocket", - "pynput", - "opcua", "requests", - "asyncio" ], classifiers=[ "Programming Language :: Python :: 3", -- GitLab From c20d6bcc3f3b8b21379520d8d39b511093c76f13 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 2 Nov 2020 11:53:51 +0100 Subject: [PATCH 70/84] Update the readme and req --- README.md | 88 +++++++++++++++++++++--------------------------- fml40s.py | 9 ++--- requirements.txt | 2 +- setup.py | 3 +- 4 files changed, 46 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 866fc88..58c6b8c 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ fml40-reference-implementation ============================== -Reference implementation of the forest modelling language 4.0 (fml 4.0) which is explained in a KWH 4.0 white paper (not public yet). +Reference implementation of the forest modelling language 4.0 (fml 4.0) which is explained in a [KWH 4.0 white paper](https://www.kwh40.de/wp-content/uploads/2020/03/KWH40-Standpunkt-fml40-Version-1.0.pdf). To use the fml40 reference implementation package in your own project you can install it using the latest [wheel](https://git.rwth-aachen.de/kwh40/fml40-reference-implementation/-/jobs/artifacts/master/raw/public/ml-0.1-py3-none-any.whl?job=wheel). To install this wheel, go to the respective directory or switch to your designated virtual environment and install the .whl file, just run pip install https://git.rwth-aachen.de/kwh40/fml40-reference-implementation/-/jobs/artifacts/master/raw/public/ml-0.1-py3-none-any.whl?job=wheel Features -------- -- launching digital twins in single shot or persistent mode via `fml40s.py` +- launching digital twins in persistent mode via `fml40s.py` - extending the capabilities with user defined digital twins Requirements @@ -19,65 +19,62 @@ Installation ------------ -Run `pip install -m requirements` +Run `pip install -m requirements` in your virtual environment. To create a virtual environment run `mkvirtualenv ` and `workon ` Usage ----- A digital twin can be launched in persistent mode by executing -``` example -python fml40s.py launch +``` +python fml40s.py launch ``` +all the config files must be located in the folder `configs`. +For more options call just run -If called with `--singleshot` option the digital twin receives all available messages, potentially sends some messages and shuts down afterwards. - -For more options call - -``` example +``` python fml40s.py -h ``` -Extending with user defined classes ------------------------------------ - -A new thing can be implemented by creating a python class which inherits from [ManagingActor](#managing-actor) or an existing [machine](#machines). In order to use the class, it has to be registered to `DT_FACTORY` within `fml40s.py`. Config files ------------ A valid configuration consists of exactly one json object. Mandatory fields of the json object are -- client\_secret, -- thingId and -- type +- thingId +- policyId and +- attributes MyDigitalTwin.json: ``` example { - "client_secret": "secret", "thingId": "s3i:id", - "type": "Komatsu", - "functionalities": [ - { - "type": "AcceptsForwardingJobs", - "proxy": "Forwards" - } -} -``` - -Optionally a list named `functionalities` can be defined. The objects contained by this list specify the [functionalities](#functionalities) a thing has. Currently a list entry has the following structure: + "policyId": "s3i:id", + "attributes": { + "class": "ml40::Thing", + "name": "my digital twin", + "roles": [ + { + "class": "fml40::Harvester" + } + ], + "features": [ + { + "class": "fml40::ProvidesProductionData" + }, + { + "class": "ml40::Location", + "longitude": 5.03424, + "latitude": 52.52345 + } + ] -``` example -{ - "type": "Harvests", - "receiver": "s3i:3154edfa-5b04-4a28-b803-d6ec46135c19", - "proxy": "ManageJobs" + } } ``` -In both cases `type` has to match the exact class name of the thing or functionality. The fields `proxy` and `receiver` are option. `proxy` can be used to specify a functionality that will be executed afterwards. If this functionality is going to send messages, the id of the receiving thing has to be declared in `receiver`. Structure --------- @@ -86,26 +83,19 @@ ### Configs This folder contains example configuration files in json format for some digital twins. -### Functionalities - -This directory includes the implementations of the functionalities as described in the fml40 white paper. - -### Machines - -Contains base classes for machines. The base classes are implemented as *Managing Actors* since these actors always manage different functionalities, which are *Managed Actors*. +### ml -### Managed Actor +This directory includes the implementations of the fml40 python reference implementation as described in the fml40 white paper. -A managed actor is an actor with reference to the [pykka](https://www.pykka.org/en/latest/) implementation. To instantiate a managed actor, an associated *Managing Actor* is needed. The managed actor registers at this managing actor. -### Managing Actor +### demo -A managing actor has a dict *proxyFunctionalities* that lists all actors/functionalities the managing actor manages. The dict assigns the names of the managed actor to their proxy reference (see also [pykka proxies](https://www.pykka.org/en/latest/api/proxies/)). Also, the managing actor stops all its managed actors listed in its dict before it is stopped itself. +Example of creating and launching a harvester. Additionally, a hmi is created to communicate with the harvester using S³I-B protocol. -### Tools +### logs -Implements service functions that can be used. Currently, also S³I interface functions like *sending a service request* are implemented here. These kind of functions should be outsourced in an *interfaces* directory soon. +is composed of the logging files of the created digital twins. -### Customer code example +### tests -Example implementation of a JohnDeere harvester, Ponsse harvester and Komatsu forwader. +contains all test scripts (currently in development) \ No newline at end of file diff --git a/fml40s.py b/fml40s.py index fae6023..15a8891 100755 --- a/fml40s.py +++ b/fml40s.py @@ -8,9 +8,10 @@ import argparse from ml.tools import load_config import os -from path.path import PROJECT_ROOT from ml.dt_factory import create_dt_ref +root_path = os.path.abspath("") + def create_argparser(): parent_parser = argparse.ArgumentParser(prog="", description="FML40 argument parse") @@ -36,17 +37,17 @@ def main(): if args.command == "list": if args.ml[0] == "dts": - g = os.walk(os.path.join(PROJECT_ROOT, "configs")) + g = os.walk(os.path.join(root_path, "configs")) dt_name = list() for path, dir_list, file_list in g: for file_name in file_list: - dt_name.append(load_config(file_name)["attributes"].get("name", None)) + dt_name.append(load_config(file_name, root=root_path)["attributes"].get("name", None)) print(dt_name) elif args.command == "launch": dt_config = args.dt_config[0] try: - model = load_config(dt_config) + model = load_config(dt_config, root=root_path) except FileNotFoundError: print("{} can not be found".format(dt_config)) return diff --git a/requirements.txt b/requirements.txt index 495264c..8e3c7a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ https://git.rwth-aachen.de/kwh40/s3i/-/jobs/artifacts/master/raw/public/s3i-0.4-py3-none-any.whl?job=wheel -https://git.rwth-aachen.de/kwh40/fml40-reference-implementation/-/jobs/artifacts/dev_chen/raw/public/fml40-0.1-py3-none-any.whl?job=wheel \ No newline at end of file +https://git.rwth-aachen.de/kwh40/fml40-reference-implementation/-/jobs/artifacts/dev_chen/raw/public/ml-0.1-py3-none-any.whl?job=wheel \ No newline at end of file diff --git a/setup.py b/setup.py index 29bf98b..858ddf6 100644 --- a/setup.py +++ b/setup.py @@ -18,8 +18,7 @@ url = "https://www.kwh40.de/", packages=setuptools.find_packages(), install_requires=[ - "pylint", - "requests", + "requests" ], classifiers=[ "Programming Language :: Python :: 3", -- GitLab From 9a57aa06b7d911d1e1860d2e8532aa4e1b656230 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Sat, 7 Nov 2020 17:02:14 +0100 Subject: [PATCH 71/84] fix dir json problem --- configs/my_dt_harvester.json | 1 - ml/thing.py | 73 ++++++++++++++++++++++++++++-------- 2 files changed, 58 insertions(+), 16 deletions(-) delete mode 100644 configs/my_dt_harvester.json diff --git a/configs/my_dt_harvester.json b/configs/my_dt_harvester.json deleted file mode 100644 index 48b2009..0000000 --- a/configs/my_dt_harvester.json +++ /dev/null @@ -1 +0,0 @@ -{"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 diff --git a/ml/thing.py b/ml/thing.py index d07fe5f..872609a 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -5,6 +5,7 @@ from s3i import IdentityProvider, TokenType, GetValueReply, Directory, 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 @@ -35,6 +36,8 @@ def __init__( ): self.__model = model self.__thing_id = model.get("thingId", "") + if not self.__thing_id: + self.__thing_id = ID().identifier self.__policy_id = model.get("policyId", "") self.__grant_type = grant_type self.__username = username @@ -134,7 +137,7 @@ def __json_syn(self, freq=0.1): def __dir_syn(self, freq=0.1): while True: - try: + #try: time.sleep(freq) old_dir_json = self.dir_json self.to_dir_json() @@ -142,8 +145,8 @@ def __dir_syn(self, freq=0.1): continue else: self.dir.updateThingIDBased(thingID=self.thing_id, payload=self.dir_json) - except: - continue + # except: + # continue def __repo_syn(self, freq=0.1): while self.__is_repo: @@ -464,7 +467,6 @@ def on_get_value_reply(self, msg): BColors.ENDC) ) - def on_service_reply(self, msg): print( BColors.OKBLUE @@ -491,19 +493,39 @@ def to_dir_json(self): self.dir_json["thingId"] = self.thing_id if self.policy_id is not None: self.dir_json["policyId"] = self.policy_id - self.dir_json["attributes"]["class"] = "ml40::Thing" - - self.dir_json["attributes"]["name"] = self.name - if self.roles: - self.dir_json["attributes"]["roles"] = list() - if self.features: - self.dir_json["attributes"]["features"] = list() - + if self.name is not None: + self.dir_json["attributes"]["name"] = self.name + 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"] + } + self.dir_json["attributes"]["dataModel"] = "fml40" + self.dir_json["attributes"]["thingStructure"] = { + "class": "ml40::Thing", + "links": [] + } for key in self.roles.keys(): - self.dir_json["attributes"]["roles"].append(self.roles[key].to_json()) - for key in self.features.keys(): - self.dir_json["attributes"]["features"].append(self.features[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_entry = { + "association": "features", + "target": { + "class": self.features[key].class_name, + } + } + # if the feature has targets, like ml40::Composite + 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() + feature_entry["target"]["links"].append(target_json) + self.dir_json["attributes"]["thingStructure"]["links"].append(feature_entry) return self.dir_json def to_repo_json(self): @@ -532,6 +554,7 @@ def to_json(self): def to_subthing_json(self): json_out = { "class": "ml40::Thing", + "identifier": self.thing_id, "name": self.name, "roles": [], "features": [] @@ -541,3 +564,23 @@ def to_subthing_json(self): for key in self.features.keys(): json_out["features"].append(self.features[key].to_json()) return json_out + + def to_subthing_dir_json(self): + 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() + } + json_out["links"].append(role_entry) + for key in self.features.keys(): + feature_entry = { + "association": "features", + "target": self.features[key].to_json() + } + json_out["links"].append(feature_entry) + return json_out -- GitLab From 76e64385557928af6c49136cb2ae03dfede55aa9 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Sat, 7 Nov 2020 17:02:28 +0100 Subject: [PATCH 72/84] fix config file path problem --- configs/my_dt_harvester.json | 1 + demo/dt_creation.py | 9 +++++---- ml/dt_factory.py | 5 +++-- ml/tools.py | 15 ++++++++------- 4 files changed, 17 insertions(+), 13 deletions(-) create mode 100644 configs/my_dt_harvester.json diff --git a/configs/my_dt_harvester.json b/configs/my_dt_harvester.json new file mode 100644 index 0000000..474135d --- /dev/null +++ b/configs/my_dt_harvester.json @@ -0,0 +1 @@ +{"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::Location", "longitude": 6.45435, "latitude": 52.543534}, {"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 diff --git a/demo/dt_creation.py b/demo/dt_creation.py index 8bffe5a..d1fb3ed 100644 --- a/demo/dt_creation.py +++ b/demo/dt_creation.py @@ -9,7 +9,6 @@ from config import * import os -root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) dt_creation_app_id = dt_creation_app_id dt_creation_app_secret = dt_creation_app_secret @@ -53,19 +52,21 @@ config_cran = make_sub_thing(name="my_bord_computer", roles=[{"class": "ml40::MachineUI"}]) +config_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, "configs")) + 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::Location", "longitude": 6.45435, "latitude": 52.543534}, {"class": "ml40::Composite", "targets": [config_engine, config_cran]}], - root=root_path) + config_path=config_path) setup_logger(dt_name) -dt_model = load_config(config_file_name, root=root_path) +dt_model = load_config(config_filepath=os.path.join(config_path, config_file_name)) dt = create_dt_ref(model=dt_model, grant_type="client_credentials", secret=dt_secret, is_broker_rest=True, - is_broker=True, is_repo=False) + is_broker=True, is_repo=True) class AcceptsFellingJobsImpl(AcceptsFellingJobs): diff --git a/ml/dt_factory.py b/ml/dt_factory.py index 7aa1fc1..07381cf 100644 --- a/ml/dt_factory.py +++ b/ml/dt_factory.py @@ -176,7 +176,6 @@ for member in clsmembers: DT_FACTORY[member[0]] = member[1] - def build_sub_features(feature_ins, feature): sub_features = feature.get("subFeatures", []) @@ -226,6 +225,7 @@ def build_role(role): 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) + role_instance = None else: APP_LOGGER.debug("Adding roles: %s" % role_class_name) role_instance = role_obj() @@ -238,6 +238,7 @@ def build_feature(feature): if feature_obj is None: APP_LOGGER.critical("Feature: %s is missing" % feature_class_name) + feature_instance = None else: APP_LOGGER.debug("Adding feature: %s" % feature_class_name) feature_instance = feature_obj() @@ -278,7 +279,7 @@ def create_dt_ref(model, grant_type="password", sys.exit("Incomplete model: roles missing!") thing_name = attributes.get("name", "") - APP_LOGGER.debug("Creating ditigtal twin {} with id {}".format(thing_name, model.get("thingId", ""))) + APP_LOGGER.debug("Build digital twin {} with id {}".format(thing_name, model.get("thingId", ""))) d_t = DT_FACTORY.get(thing_type) diff --git a/ml/tools.py b/ml/tools.py index ce97b19..95f742f 100644 --- a/ml/tools.py +++ b/ml/tools.py @@ -320,7 +320,10 @@ def make_feature_config(class_name, identifier="", name="", subFeatures=""): return config_json -def make_thing_config(dt_id, name, roles, features=[], root=__file__): +def make_thing_config(dt_id, name, roles, features=[], config_path=""): + if not config_path: + config_path = os.path.join("__file__", "configs") + config_file = { "thingId": dt_id, "policyId": dt_id, @@ -331,21 +334,19 @@ def make_thing_config(dt_id, name, roles, features=[], root=__file__): "features": features, } } - - path = os.path.join(root, "configs", "{}.json".format(name)) - with open(path, 'wb') as file: + file_path = os.path.join(config_path, "{}.json".format(name)) + with open(file_path, 'wb') as file: file.write(json.dumps(config_file).encode('utf-8')) return "{}.json".format(name) -def load_config(config_file_name, root=__file__): +def load_config(config_filepath): """Creates a json object from a json formatted file found at config_filepath. :param config_filepath: Path to json formatted file. """ - config_file_path = os.path.join(root, "configs", config_file_name) - with open(config_file_path) as config_file: + with open(config_filepath) as config_file: config = json.load(config_file) return config -- GitLab From 40a5b9feba3e44166c4db75e2a67885f80af3e6c Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Sat, 7 Nov 2020 20:48:10 +0100 Subject: [PATCH 73/84] fix dir json print out problem --- ml/thing.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ml/thing.py b/ml/thing.py index 872609a..03a4cea 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -515,9 +515,7 @@ def to_dir_json(self): for key in self.features.keys(): feature_entry = { "association": "features", - "target": { - "class": self.features[key].class_name, - } + "target": self.features[key].to_json() } # if the feature has targets, like ml40::Composite if hasattr(self.features[key], 'targets'): -- GitLab From 53ad9125c8b05785a6ef57363f10c1d8f89ed26f Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Sun, 8 Nov 2020 12:41:52 +0100 Subject: [PATCH 74/84] change dt to thing --- ml/dt_factory.py | 17 +++++++++-------- ml/tools.py | 6 +++--- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/ml/dt_factory.py b/ml/dt_factory.py index 07381cf..a5f1873 100644 --- a/ml/dt_factory.py +++ b/ml/dt_factory.py @@ -2,6 +2,7 @@ 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 @@ -165,7 +166,6 @@ from ml.fml40.features.functionalities.supports_felling import SupportsFelling from ml.fml40.features.functionalities.transports_logs import TransportsLogs - # TODO: Get rid of this global variable # TODO: automatically get all classes in modul DT_FACTORY = {} @@ -176,6 +176,7 @@ for member in clsmembers: DT_FACTORY[member[0]] = member[1] + def build_sub_features(feature_ins, feature): sub_features = feature.get("subFeatures", []) @@ -200,7 +201,7 @@ def build_sub_features(feature_ins, 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}) + sub_thing_ref = create_thing(model={"attributes": json_sub_thing}) sub_thing_name = json_sub_thing.get("name", None) feature_ins.targets[sub_thing_name] = sub_thing_ref @@ -225,7 +226,7 @@ def build_role(role): 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) - role_instance = None + role_instance = None else: APP_LOGGER.debug("Adding roles: %s" % role_class_name) role_instance = role_obj() @@ -265,9 +266,9 @@ def add_function_impl_obj(thing, impl_obj, 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): +def create_thing(model, grant_type="password", + secret="", username=None, password=None, + is_broker_rest=False, is_broker=False, is_repo=False): attributes = model.get("attributes", None) if attributes is None: @@ -281,9 +282,9 @@ def create_dt_ref(model, grant_type="password", thing_name = attributes.get("name", "") APP_LOGGER.debug("Build digital twin {} with id {}".format(thing_name, model.get("thingId", ""))) - d_t = DT_FACTORY.get(thing_type) + _thing = DT_FACTORY.get(thing_type) - thing_ref = d_t( + thing_ref = _thing( model=model, grant_type=grant_type, client_secret=secret, diff --git a/ml/tools.py b/ml/tools.py index 95f742f..eb5fb69 100644 --- a/ml/tools.py +++ b/ml/tools.py @@ -320,13 +320,13 @@ def make_feature_config(class_name, identifier="", name="", subFeatures=""): return config_json -def make_thing_config(dt_id, name, roles, features=[], config_path=""): +def make_thing_config(thing_id, name, roles, features=[], config_path=""): if not config_path: config_path = os.path.join("__file__", "configs") config_file = { - "thingId": dt_id, - "policyId": dt_id, + "thingId": thing_id, + "policyId": thing_id, "attributes": { "class": "ml40::Thing", "name": name, -- GitLab From 1f219f5fab5941d8b136cce145a99ee302ab0e64 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 9 Nov 2020 19:51:45 +0100 Subject: [PATCH 75/84] update init file --- ml/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ml/__init__.py b/ml/__init__.py index e69de29..132aff8 100644 --- a/ml/__init__.py +++ b/ml/__init__.py @@ -0,0 +1,3 @@ +from ml.dt_factory import create_thing, add_function_impl_obj, build, build_feature, build_role, build_sub_features, build_sub_thing +from ml.tools import make_thing_config, load_config, make_feature_config +from ml.app_logger import setup_logger, APP_LOGGER -- GitLab From 956400eebc450e7b68f4601a8944380b5eb35f50 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 9 Nov 2020 19:52:07 +0100 Subject: [PATCH 76/84] change debug to info --- ml/dt_factory.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ml/dt_factory.py b/ml/dt_factory.py index a5f1873..2f2d8dc 100644 --- a/ml/dt_factory.py +++ b/ml/dt_factory.py @@ -186,7 +186,7 @@ def build_sub_features(feature_ins, feature): if sub_f_obj is None: APP_LOGGER.critical("Subfeature: %s is missing" % sub_f_name) else: - APP_LOGGER.debug("Adding subfeature: %s" % sub_f_name) + APP_LOGGER.info("Adding subfeature: %s" % sub_f_name) sub_f_instance = sub_f_obj() for key in sub_f.keys(): if key == "targets": @@ -228,7 +228,7 @@ def build_role(role): APP_LOGGER.critical("Roles: %s is missing" % role_class_name) role_instance = None else: - APP_LOGGER.debug("Adding roles: %s" % role_class_name) + APP_LOGGER.info("Adding roles: %s" % role_class_name) role_instance = role_obj() return role_instance @@ -241,7 +241,7 @@ def build_feature(feature): APP_LOGGER.critical("Feature: %s is missing" % feature_class_name) feature_instance = None else: - APP_LOGGER.debug("Adding feature: %s" % feature_class_name) + APP_LOGGER.info("Adding feature: %s" % feature_class_name) feature_instance = feature_obj() for key in feature.keys(): if key == "class": @@ -262,7 +262,7 @@ def add_function_impl_obj(thing, impl_obj, feature_name): "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) + APP_LOGGER.info("Implementation object is added into the functionality %s" % feature_name) thing.features[feature_name] = impl_obj(feature_name) @@ -280,7 +280,7 @@ def create_thing(model, grant_type="password", sys.exit("Incomplete model: roles missing!") thing_name = attributes.get("name", "") - APP_LOGGER.debug("Build digital twin {} with id {}".format(thing_name, model.get("thingId", ""))) + APP_LOGGER.info("Build digital twin {} with id {}".format(thing_name, model.get("thingId", ""))) _thing = DT_FACTORY.get(thing_type) -- GitLab From 6c5ea285078570a38de52c6c422782b8666ab395 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 9 Nov 2020 19:58:05 +0100 Subject: [PATCH 77/84] add find_broker_endpoint --- ml/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ml/__init__.py b/ml/__init__.py index 132aff8..a1130f3 100644 --- a/ml/__init__.py +++ b/ml/__init__.py @@ -1,3 +1,3 @@ from ml.dt_factory import create_thing, add_function_impl_obj, build, build_feature, build_role, build_sub_features, build_sub_thing -from ml.tools import make_thing_config, load_config, make_feature_config +from ml.tools import make_thing_config, load_config, make_feature_config, find_broker_endpoint from ml.app_logger import setup_logger, APP_LOGGER -- GitLab From 4e1b3de9958529a0192f9ddfd578a0e9d6ab8b7a Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 9 Nov 2020 22:54:45 +0100 Subject: [PATCH 78/84] add make_sub_thing into the __init__ --- ml/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ml/__init__.py b/ml/__init__.py index a1130f3..9b0b150 100644 --- a/ml/__init__.py +++ b/ml/__init__.py @@ -1,3 +1,3 @@ from ml.dt_factory import create_thing, add_function_impl_obj, build, build_feature, build_role, build_sub_features, build_sub_thing -from ml.tools import make_thing_config, load_config, make_feature_config, find_broker_endpoint +from ml.tools import make_thing_config, make_sub_thing, load_config, make_feature_config, find_broker_endpoint from ml.app_logger import setup_logger, APP_LOGGER -- GitLab From aef0b95b2fcceb0f3ab22c68a93c8e1eb2662c1d Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 9 Nov 2020 23:39:55 +0100 Subject: [PATCH 79/84] fix double log problem --- ml/app_logger.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ml/app_logger.py b/ml/app_logger.py index 4704e63..223d3a5 100644 --- a/ml/app_logger.py +++ b/ml/app_logger.py @@ -17,8 +17,8 @@ def setup_logger(dt_name): if not os.path.exists("logs"): os.mkdir("logs") - app_logger = logging.getLogger('app_logger') - app_logger.setLevel(logging.DEBUG) + APP_LOGGER = logging.getLogger('app_logger') + APP_LOGGER.setLevel(logging.DEBUG) log_formatter = logging.Formatter( "%(asctime)s - %(levelname)s - {}: %(message)s".format(dt_name) ) @@ -28,6 +28,6 @@ def setup_logger(dt_name): file_handler = logging.FileHandler(filename="./logs/{}.log".format(dt_name)) file_handler.setLevel(logging.DEBUG) file_handler.setFormatter(log_formatter) - app_logger.addHandler(file_handler) - app_logger.addHandler(stream_handler) - return app_logger + APP_LOGGER.addHandler(file_handler) + APP_LOGGER.addHandler(stream_handler) + return APP_LOGGER -- GitLab From 8928dc2b97f04f477033892898e6515a60688c18 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 9 Nov 2020 23:41:53 +0100 Subject: [PATCH 80/84] delete double logger --- ml/app_logger.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ml/app_logger.py b/ml/app_logger.py index 223d3a5..594b242 100644 --- a/ml/app_logger.py +++ b/ml/app_logger.py @@ -17,7 +17,6 @@ def setup_logger(dt_name): if not os.path.exists("logs"): os.mkdir("logs") - APP_LOGGER = logging.getLogger('app_logger') APP_LOGGER.setLevel(logging.DEBUG) log_formatter = logging.Formatter( "%(asctime)s - %(levelname)s - {}: %(message)s".format(dt_name) -- GitLab From 164a39ffc14e4e357e1456879783fd127316fb01 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Mon, 9 Nov 2020 23:53:23 +0100 Subject: [PATCH 81/84] fix logger problem --- ml/app_logger.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ml/app_logger.py b/ml/app_logger.py index 594b242..ed0bcde 100644 --- a/ml/app_logger.py +++ b/ml/app_logger.py @@ -4,7 +4,7 @@ APP_LOGGER = logging.getLogger("app_logger") -def setup_logger(dt_name): +def setup_logger(dt_name, app_logger=APP_LOGGER): """Creates logger named app_logger. Logs are printed to stdout and saved to log files under '/logs'. @@ -17,16 +17,18 @@ def setup_logger(dt_name): if not os.path.exists("logs"): os.mkdir("logs") - APP_LOGGER.setLevel(logging.DEBUG) + app_logger.setLevel(logging.DEBUG) + log_formatter = logging.Formatter( "%(asctime)s - %(levelname)s - {}: %(message)s".format(dt_name) ) + stream_handler = logging.StreamHandler() stream_handler.setFormatter(log_formatter) - stream_handler.setLevel(logging.DEBUG) + # stream_handler.setLevel(logging.DEBUG) file_handler = logging.FileHandler(filename="./logs/{}.log".format(dt_name)) file_handler.setLevel(logging.DEBUG) file_handler.setFormatter(log_formatter) - APP_LOGGER.addHandler(file_handler) - APP_LOGGER.addHandler(stream_handler) - return APP_LOGGER + app_logger.addHandler(file_handler) + app_logger.addHandler(stream_handler) + return app_logger -- GitLab From 10322492e696e9c4b384dd2c49b65a9f224b9045 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 10 Nov 2020 20:36:52 +0100 Subject: [PATCH 82/84] add GPL3.0 --- LICENSE.txt | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..153d416 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file -- GitLab From 1036415a80ef1a8c1c88a1457bdf5033f4803088 Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 10 Nov 2020 20:37:06 +0100 Subject: [PATCH 83/84] fix dir json output --- ml/thing.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ml/thing.py b/ml/thing.py index 03a4cea..b065db8 100644 --- a/ml/thing.py +++ b/ml/thing.py @@ -513,9 +513,13 @@ def to_dir_json(self): 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": self.features[key].to_json() + "target": feature_target } # if the feature has targets, like ml40::Composite if hasattr(self.features[key], 'targets'): @@ -576,9 +580,13 @@ def to_subthing_dir_json(self): } 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": self.features[key].to_json() + "target": feature_target } json_out["links"].append(feature_entry) return json_out -- GitLab From d63592b5949a682ec90876be5e661f764168ca6a Mon Sep 17 00:00:00 2001 From: Jiahang Chen Date: Tue, 10 Nov 2020 20:39:34 +0100 Subject: [PATCH 84/84] cleanup --- .pylintrc | 2 -- configs/my_HMI.json | 1 + configs/my_dt_harvester_cred.json | 1 + demo/dt_creation.py | 35 +++++++++++++++++++++---------- demo/dt_creation_hmi.py | 25 +++++++++++++--------- setup.cfg | 19 ----------------- 6 files changed, 41 insertions(+), 42 deletions(-) delete mode 100644 .pylintrc create mode 100644 configs/my_HMI.json create mode 100644 configs/my_dt_harvester_cred.json delete mode 100644 setup.cfg diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 1983cc5..0000000 --- a/.pylintrc +++ /dev/null @@ -1,2 +0,0 @@ -[Master] -init-hook = 'import sys; sys.path.append("./"); sys.path.append("/home/r2d2m/.pyenv/versions/3.6.6/envs/fml40-venv-3.6.6/lib/python3.6/site-packages/")' \ No newline at end of file diff --git a/configs/my_HMI.json b/configs/my_HMI.json new file mode 100644 index 0000000..08f19fa --- /dev/null +++ b/configs/my_HMI.json @@ -0,0 +1 @@ +{"thingId": "s3i:7f46a255-3245-439e-84f0-090f0b221965", "policyId": "s3i:7f46a255-3245-439e-84f0-090f0b221965", "attributes": {"class": "ml40::Thing", "name": "my_HMI", "roles": [{"class": "ml40::HMI"}], "features": []}} \ No newline at end of file diff --git a/configs/my_dt_harvester_cred.json b/configs/my_dt_harvester_cred.json new file mode 100644 index 0000000..a39d2de --- /dev/null +++ b/configs/my_dt_harvester_cred.json @@ -0,0 +1 @@ +{"identifier": "s3i:b6d1cc6d-896c-40fe-9403-b5b7682b1d03", "secret": "cd4d24d2-f702-4f51-b0cf-6b77a423b33a"} \ No newline at end of file diff --git a/demo/dt_creation.py b/demo/dt_creation.py index d1fb3ed..956f61f 100644 --- a/demo/dt_creation.py +++ b/demo/dt_creation.py @@ -1,22 +1,24 @@ import s3i import jwt from ml.tools import load_config, make_thing_config, make_sub_thing -from ml.dt_factory import create_dt_ref, build_feature, add_function_impl_obj +from ml.dt_factory import create_thing, 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 from config import * import os - +import json +from config import * dt_creation_app_id = dt_creation_app_id dt_creation_app_secret = dt_creation_app_secret # username = input('[S3I]: Please enter your username:').strip('," ') # password = input('[S3I]: Please enter your password:') -username = username -password = password +cred = chen +username = cred["username"] +password = cred["password"] s3i_identity_provider = s3i.IdentityProvider(grant_type='password', identity_provider_url="https://idp.s3i.vswf.dev/", realm='KWH', @@ -30,7 +32,6 @@ parsed_username = jwt.decode(access_token, verify=False)["preferred_username"] ### create identity of digital twin - """ s3i_config = s3i.Config(access_token) resp = s3i_config.create_thing() @@ -38,33 +39,45 @@ 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) """ dt_id = dt_id dt_secret = dt_secret +dt_cred = { + "identifier": dt_id, + "secret": dt_secret +} dt_name = "my_dt_harvester" +config_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, "configs")) +cred_filepath = os.path.join(config_path, "{}_cred.json".format(dt_name)) +with open(cred_filepath, 'wb') as file: + file.write(json.dumps(dt_cred).encode('utf-8')) + config_engine = make_sub_thing(name="my_engine", roles=[{"class": "ml40::Engine"}], features=[ {"class": "ml40::RotationalSpeed", "rpm": 2001} ]) + config_cran = make_sub_thing(name="my_bord_computer", roles=[{"class": "ml40::MachineUI"}]) -config_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, "configs")) -config_file_name = make_thing_config(dt_id=dt_id, name=dt_name, roles=[{"class": "fml40::Harvester"}], +config_file_name = make_thing_config(thing_id=dt_id, name=dt_name, roles=[{"class": "fml40::Harvester"}], features=[{"class": "fml40::ProvidesProductionData"}, {"class": "fml40::AcceptsFellingJobs"}, {"class": "ml40::Location", "longitude": 6.45435, "latitude": 52.543534}, {"class": "ml40::Composite", "targets": [config_engine, config_cran]}], - config_path=config_path) + config_path=config_path) + setup_logger(dt_name) -dt_model = load_config(config_filepath=os.path.join(config_path, config_file_name)) +dt_model = load_config(config_filepath=os.path.join(config_path, "my_dt_harvester.json")) + +with open(cred_filepath) as file: + dt_cred = json.load(file) -dt = create_dt_ref(model=dt_model, grant_type="client_credentials", secret=dt_secret, +dt = create_thing(model=dt_model, grant_type="client_credentials", secret=dt_cred.get("secret"), is_broker_rest=True, is_broker=True, is_repo=True) diff --git a/demo/dt_creation_hmi.py b/demo/dt_creation_hmi.py index e8fe5aa..876d73c 100644 --- a/demo/dt_creation_hmi.py +++ b/demo/dt_creation_hmi.py @@ -2,22 +2,28 @@ import jwt import uuid from ml.tools import find_broker_endpoint, make_thing_config, load_config, make_feature_config -from ml.dt_factory import create_dt_ref, build_feature +from ml.dt_factory import create_thing, build_feature from ml.app_logger import setup_logger import requests import json -from config import * +#from config import * import os +from config import * root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) +config_path = os.path.join(root_path, "configs") - +cred = chen +hmi_id = cred["application_id"] +hmi_secret = cred["application_secret"] +username = cred["username"] +password = cred["password"] setup_logger("my HMI") -config_file_name = make_thing_config(dt_id=hmi_id, name="my HMI", roles=[{"class": "ml40::HMI"}], - root=root_path) -hmi_model = load_config(config_file_name, root=root_path) -hmi = create_dt_ref(model=hmi_model, grant_type="password", secret=hmi_secret, +config_file_name = make_thing_config(thing_id=hmi_id, name="my_HMI", roles=[{"class": "ml40::HMI"}], + config_path=config_path) +hmi_model = load_config(config_filepath=os.path.join(config_path, config_file_name)) +hmi = create_thing(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() @@ -27,8 +33,6 @@ """ Print out thing entry from directory and repository """ -print(hmi.dir.queryThingIDBased(thingID=receiver)) -print(hmi.repo.queryThingIDBased(thingID=receiver)) serv_req = s3i.messages.ServiceRequest() @@ -82,4 +86,5 @@ ) receiver_endpoint = find_broker_endpoint(hmi.dir, thing_id=receiver) -resp = hmi.broker.send([receiver_endpoint], json.dumps(getv_req.msg)) +resp = hmi.broker.send([receiver_endpoint], json.dumps(serv_req.msg)) +print(resp.text) \ No newline at end of file diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 755cab8..0000000 --- a/setup.cfg +++ /dev/null @@ -1,19 +0,0 @@ -[pylint] -max-line-length = 88 - -[pylint.Master] -init-hook = 'import sys; sys.path.append("./"); sys.path.append("/home/r2d2m/.pyenv/versions/3.6.6/envs/fml40-venv-3.6.6/lib/python3.6/site-packages/")' - -[pylint.messages_control] -disable = C0330, C0326 - -[flake8] -max-line-length = 88 -extend-ignore = E203, W503 - -[isort] -multi_line_output = 3 -include_trailing_comma = True -force_grid_wrap = 0 -use_parentheses = True -line_length = 88 \ No newline at end of file -- GitLab