isar 1.33.1__tar.gz → 1.33.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of isar might be problematic. Click here for more details.
- {isar-1.33.1 → isar-1.33.2}/PKG-INFO +1 -1
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/settings.py +5 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/services/service_connections/mqtt/mqtt_client.py +46 -10
- {isar-1.33.1 → isar-1.33.2}/src/isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +3 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/state_machine.py +5 -2
- isar-1.33.2/src/isar/storage/blob_storage.py +101 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/storage/local_storage.py +21 -11
- {isar-1.33.1 → isar-1.33.2}/src/isar/storage/storage_interface.py +27 -6
- {isar-1.33.1 → isar-1.33.2}/src/isar/storage/uploader.py +30 -13
- {isar-1.33.1 → isar-1.33.2}/src/isar.egg-info/PKG-INFO +1 -1
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/telemetry/mqtt_client.py +62 -6
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/telemetry/payloads.py +4 -2
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/utilities/json_service.py +6 -0
- isar-1.33.1/src/isar/storage/blob_storage.py +0 -79
- {isar-1.33.1 → isar-1.33.2}/.dockerignore +0 -0
- {isar-1.33.1 → isar-1.33.2}/.env.test +0 -0
- {isar-1.33.1 → isar-1.33.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {isar-1.33.1 → isar-1.33.2}/.github/ISSUE_TEMPLATE/feature.md +0 -0
- {isar-1.33.1 → isar-1.33.2}/.github/ISSUE_TEMPLATE/improvement.md +0 -0
- {isar-1.33.1 → isar-1.33.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {isar-1.33.1 → isar-1.33.2}/.github/release.yml +0 -0
- {isar-1.33.1 → isar-1.33.2}/.github/workflows/compile_requirements.yml +0 -0
- {isar-1.33.1 → isar-1.33.2}/.github/workflows/project_automations.yml +0 -0
- {isar-1.33.1 → isar-1.33.2}/.github/workflows/pythonpackage.yml +0 -0
- {isar-1.33.1 → isar-1.33.2}/.github/workflows/pythonpublish.yml +0 -0
- {isar-1.33.1 → isar-1.33.2}/.github/workflows/stale.yml +0 -0
- {isar-1.33.1 → isar-1.33.2}/.gitignore +0 -0
- {isar-1.33.1 → isar-1.33.2}/.pre-commit-config.yaml +0 -0
- {isar-1.33.1 → isar-1.33.2}/LICENSE +0 -0
- {isar-1.33.1 → isar-1.33.2}/README.md +0 -0
- {isar-1.33.1 → isar-1.33.2}/SECURITY.md +0 -0
- {isar-1.33.1 → isar-1.33.2}/docs/Makefile +0 -0
- {isar-1.33.1 → isar-1.33.2}/docs/full_state_machine_diagram.png +0 -0
- {isar-1.33.1 → isar-1.33.2}/docs/make.bat +0 -0
- {isar-1.33.1 → isar-1.33.2}/docs/mission_state_machine_diagram.png +0 -0
- {isar-1.33.1 → isar-1.33.2}/docs/robot_status_state_machine_diagram.png +0 -0
- {isar-1.33.1 → isar-1.33.2}/docs/rst_processing.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/docs/source/conf.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/docs/source/index.rst +0 -0
- {isar-1.33.1 → isar-1.33.2}/docs/source/readme_link.md +0 -0
- {isar-1.33.1 → isar-1.33.2}/docs/update_state_diagram.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/main.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/pyproject.toml +0 -0
- {isar-1.33.1 → isar-1.33.2}/radixconfig.yml +0 -0
- {isar-1.33.1 → isar-1.33.2}/requirements.txt +0 -0
- {isar-1.33.1 → isar-1.33.2}/setup.cfg +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/apis/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/apis/api.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/apis/models/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/apis/models/models.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/apis/models/start_mission_definition.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/apis/robot_control/robot_controller.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/apis/schedule/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/apis/schedule/scheduling_controller.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/apis/security/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/apis/security/authentication.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/certs/ca-cert.pem +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/configuration_error.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/keyvault/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/keyvault/keyvault_error.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/keyvault/keyvault_service.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/log.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/logging.conf +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/JSP1_intermediate_deck.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/JSP1_weather_deck.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/default_map.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/klab_b.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/klab_compressor.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/klab_turtlebot.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/turtleworld.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/open_telemetry.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_mission_definition/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_mission_definition/default_exr.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_mission_definition/default_mission.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_mission_definition/default_turtlebot.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_missions/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_missions/default.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_missions/default_turtlebot.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/eventhandlers/eventhandler.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/mission_planner/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/mission_planner/local_planner.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/mission_planner/mission_planner_interface.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/mission_planner/sequential_task_selector.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/mission_planner/task_selector_interface.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/models/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/models/events.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/modules.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/robot/robot.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/robot/robot_start_mission.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/robot/robot_status.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/robot/robot_stop_mission.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/robot/robot_task_status.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/script.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/services/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/services/auth/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/services/auth/azure_credentials.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/services/service_connections/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/services/service_connections/mqtt/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/services/service_connections/mqtt/robot_info_publisher.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/services/service_connections/request_handler.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/services/utilities/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/services/utilities/robot_utilities.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/services/utilities/scheduling_utilities.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/services/utilities/threaded_request.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/await_next_mission.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/blocked_protective_stop.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/home.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/intervention_needed.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/monitor.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/offline.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/paused.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/recharging.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/returning_home.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/robot_standing_still.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/stopping.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/unknown_status.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states_enum.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/fail_mission.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/finish_mission.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/pause.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/resume.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/return_home.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/robot_status.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/start_mission.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/stop.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/utils.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/mission.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/return_home.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/robot_status.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/utils/common_event_handlers.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/storage/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar/storage/utilities.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar.egg-info/SOURCES.txt +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar.egg-info/dependency_links.txt +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar.egg-info/entry_points.txt +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar.egg-info/requires.txt +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/isar.egg-info/top_level.txt +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/exceptions/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/exceptions/robot_exceptions.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/initialize/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/inspection/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/inspection/inspection.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/mission/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/mission/mission.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/mission/status.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/mission/task.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/robots/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/robots/battery_state.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/robots/media.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/robots/robot_model.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/robot_interface.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/telemetry/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/test_robot_interface.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/utilities/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/src/robot_interface/utilities/uuid_string_factory.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/conftest.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/integration/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/config/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/config/maps/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/config/maps/turtleworld.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/config/missions/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/config/missions/default.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/test_successful_mission.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/models/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/models/example_mission_definition.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/models/test_start_mission_definition.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/scheduler/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/scheduler/test_scheduler_router.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/security/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/security/test_authentication.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/mission/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/mission/test_mission.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/models/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/models/communication/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/models/communication/test_events.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/services/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/services/readers/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/services/readers/test_mission_reader.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/services/service_connections/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/services/service_connections/echo/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/services/service_connections/test_base_request_handler.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/services/utilities/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/services/utilities/test_queue_utilities.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/services/utilities/test_scheduling_utilities.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/state_machine/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/state_machine/states/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/state_machine/states/test_monitor.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/state_machine/test_state_machine.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/storage/test_blob_storage.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/isar/storage/test_uploader.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_data/test_map_config/test_map_config.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_data/test_mission_not_working.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_data/test_mission_working.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_data/test_mission_working_no_tasks.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_data/test_thermal_image_mission.json +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_double/__init__.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_double/blob_storage.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_double/mission_definition.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_double/pose.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_double/request.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_double/robot_interface.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_double/status.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_double/task.py +0 -0
- {isar-1.33.1 → isar-1.33.2}/tests/test_double/token.py +0 -0
|
@@ -244,6 +244,11 @@ class Settings(BaseSettings):
|
|
|
244
244
|
default="intervention_needed", validate_default=True
|
|
245
245
|
)
|
|
246
246
|
|
|
247
|
+
# List of MQTT Topics Expiry
|
|
248
|
+
MQTT_ROBOT_HEARTBEAT_EXPIRY: int = Field(default=5)
|
|
249
|
+
MQTT_TELEMETRY_EXPIRY: int = Field(default=10)
|
|
250
|
+
MQTT_MISSION_AND_TASK_EXPIRY: int = Field(default=86400)
|
|
251
|
+
|
|
247
252
|
# Logging
|
|
248
253
|
|
|
249
254
|
# Log handlers
|
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
|
+
import time
|
|
3
4
|
from queue import Empty, Queue
|
|
4
5
|
|
|
5
6
|
import backoff
|
|
6
7
|
from paho.mqtt import client as mqtt
|
|
7
8
|
from paho.mqtt.client import Client
|
|
9
|
+
from paho.mqtt.packettypes import PacketTypes
|
|
10
|
+
from paho.mqtt.properties import Properties
|
|
8
11
|
|
|
9
12
|
from isar.config.settings import settings
|
|
10
13
|
from robot_interface.telemetry.mqtt_client import MqttClientInterface
|
|
11
14
|
|
|
12
15
|
|
|
16
|
+
def props_expiry(seconds: int) -> Properties:
|
|
17
|
+
p = Properties(PacketTypes.PUBLISH)
|
|
18
|
+
p.MessageExpiryInterval = seconds
|
|
19
|
+
return p
|
|
20
|
+
|
|
21
|
+
|
|
13
22
|
def _on_success(data: dict) -> None:
|
|
14
23
|
logging.getLogger("mqtt_client").info("Connected to MQTT Broker")
|
|
15
24
|
logging.getLogger("mqtt_client").debug(
|
|
@@ -53,7 +62,9 @@ class MqttClient(MqttClientInterface):
|
|
|
53
62
|
|
|
54
63
|
self.port: int = settings.MQTT_PORT
|
|
55
64
|
|
|
56
|
-
self.client: Client = Client(
|
|
65
|
+
self.client: Client = Client(
|
|
66
|
+
protocol=mqtt.MQTTv5, callback_api_version=mqtt.CallbackAPIVersion.VERSION2
|
|
67
|
+
)
|
|
57
68
|
|
|
58
69
|
self.client.enable_logger(logger=self.logger)
|
|
59
70
|
|
|
@@ -74,20 +85,36 @@ class MqttClient(MqttClientInterface):
|
|
|
74
85
|
|
|
75
86
|
while True:
|
|
76
87
|
if not self.client.is_connected():
|
|
88
|
+
time.sleep(0) # avoid CPU spin
|
|
77
89
|
continue
|
|
78
90
|
try:
|
|
79
|
-
|
|
91
|
+
item = self.mqtt_queue.get(timeout=1)
|
|
92
|
+
if len(item) == 4:
|
|
93
|
+
topic, payload, qos, retain = item
|
|
94
|
+
properties = None
|
|
95
|
+
else:
|
|
96
|
+
topic, payload, qos, retain, properties = item
|
|
80
97
|
except Empty:
|
|
81
98
|
continue
|
|
82
99
|
|
|
83
|
-
self.publish(
|
|
100
|
+
self.publish(
|
|
101
|
+
topic=topic,
|
|
102
|
+
payload=payload,
|
|
103
|
+
qos=qos,
|
|
104
|
+
retain=retain,
|
|
105
|
+
properties=properties,
|
|
106
|
+
)
|
|
84
107
|
|
|
85
|
-
def on_connect(self, client, userdata, flags,
|
|
86
|
-
self.logger.info(
|
|
108
|
+
def on_connect(self, client, userdata, flags, reason_code, properties):
|
|
109
|
+
self.logger.info(
|
|
110
|
+
"Connection returned result: " + mqtt.connack_string(reason_code)
|
|
111
|
+
)
|
|
87
112
|
|
|
88
|
-
def on_disconnect(self, client, userdata,
|
|
89
|
-
if
|
|
90
|
-
self.logger.warning(
|
|
113
|
+
def on_disconnect(self, client, userdata, reason_code, properties):
|
|
114
|
+
if reason_code != 0:
|
|
115
|
+
self.logger.warning(
|
|
116
|
+
f"Unexpected disconnection from MQTT Broker, {reason_code}"
|
|
117
|
+
)
|
|
91
118
|
|
|
92
119
|
@backoff.on_exception(
|
|
93
120
|
backoff.expo,
|
|
@@ -102,6 +129,15 @@ class MqttClient(MqttClientInterface):
|
|
|
102
129
|
self.logger.info("Host: %s, Port: %s", host, port)
|
|
103
130
|
self.client.connect(host=host, port=port)
|
|
104
131
|
|
|
105
|
-
def publish(
|
|
132
|
+
def publish(
|
|
133
|
+
self,
|
|
134
|
+
topic: str,
|
|
135
|
+
payload: str,
|
|
136
|
+
qos: int = 0,
|
|
137
|
+
retain: bool = False,
|
|
138
|
+
properties=None,
|
|
139
|
+
):
|
|
106
140
|
self.logger.debug("Publishing message to topic: %s", topic)
|
|
107
|
-
self.client.publish(
|
|
141
|
+
self.client.publish(
|
|
142
|
+
topic=topic, payload=payload, qos=qos, retain=retain, properties=properties
|
|
143
|
+
)
|
{isar-1.33.1 → isar-1.33.2}/src/isar/services/service_connections/mqtt/robot_heartbeat_publisher.py
RENAMED
|
@@ -4,6 +4,7 @@ from datetime import datetime, timezone
|
|
|
4
4
|
from queue import Queue
|
|
5
5
|
|
|
6
6
|
from isar.config.settings import settings
|
|
7
|
+
from isar.services.service_connections.mqtt.mqtt_client import props_expiry
|
|
7
8
|
from robot_interface.telemetry.mqtt_client import MqttPublisher
|
|
8
9
|
from robot_interface.telemetry.payloads import RobotHeartbeatPayload
|
|
9
10
|
from robot_interface.utilities.json_service import EnhancedJSONEncoder
|
|
@@ -24,6 +25,8 @@ class RobotHeartbeatPublisher:
|
|
|
24
25
|
self.mqtt_publisher.publish(
|
|
25
26
|
topic=settings.TOPIC_ISAR_ROBOT_HEARTBEAT,
|
|
26
27
|
payload=json.dumps(payload, cls=EnhancedJSONEncoder),
|
|
28
|
+
retain=True,
|
|
29
|
+
properties=props_expiry(settings.MQTT_ROBOT_HEARTBEAT_EXPIRY),
|
|
27
30
|
)
|
|
28
31
|
|
|
29
32
|
time.sleep(settings.ROBOT_HEARTBEAT_PUBLISH_INTERVAL)
|
|
@@ -15,6 +15,7 @@ from isar.mission_planner.task_selector_interface import (
|
|
|
15
15
|
TaskSelectorStop,
|
|
16
16
|
)
|
|
17
17
|
from isar.models.events import Events, SharedState
|
|
18
|
+
from isar.services.service_connections.mqtt.mqtt_client import props_expiry
|
|
18
19
|
from isar.state_machine.states.await_next_mission import AwaitNextMission
|
|
19
20
|
from isar.state_machine.states.blocked_protective_stop import BlockedProtectiveStop
|
|
20
21
|
from isar.state_machine.states.home import Home
|
|
@@ -285,10 +286,11 @@ class StateMachine(object):
|
|
|
285
286
|
)
|
|
286
287
|
|
|
287
288
|
self.mqtt_publisher.publish(
|
|
288
|
-
topic=settings.TOPIC_ISAR_MISSION,
|
|
289
|
+
topic=settings.TOPIC_ISAR_MISSION + f"/{self.current_mission.id}",
|
|
289
290
|
payload=json.dumps(payload, cls=EnhancedJSONEncoder),
|
|
290
291
|
qos=1,
|
|
291
292
|
retain=True,
|
|
293
|
+
properties=props_expiry(settings.MQTT_MISSION_AND_TASK_EXPIRY),
|
|
292
294
|
)
|
|
293
295
|
|
|
294
296
|
def publish_task_status(self, task: TASKS) -> None:
|
|
@@ -316,10 +318,11 @@ class StateMachine(object):
|
|
|
316
318
|
)
|
|
317
319
|
|
|
318
320
|
self.mqtt_publisher.publish(
|
|
319
|
-
topic=settings.TOPIC_ISAR_TASK,
|
|
321
|
+
topic=settings.TOPIC_ISAR_TASK + f"/{task.id}",
|
|
320
322
|
payload=json.dumps(payload, cls=EnhancedJSONEncoder),
|
|
321
323
|
qos=1,
|
|
322
324
|
retain=True,
|
|
325
|
+
properties=props_expiry(settings.MQTT_MISSION_AND_TASK_EXPIRY),
|
|
323
326
|
)
|
|
324
327
|
|
|
325
328
|
def publish_intervention_needed(self, error_message: str) -> None:
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from azure.core.exceptions import ResourceExistsError
|
|
5
|
+
from azure.storage.blob import BlobServiceClient, ContainerClient
|
|
6
|
+
|
|
7
|
+
from isar.config.keyvault.keyvault_service import Keyvault
|
|
8
|
+
from isar.config.settings import settings
|
|
9
|
+
from isar.storage.storage_interface import (
|
|
10
|
+
BlobStoragePath,
|
|
11
|
+
StorageException,
|
|
12
|
+
StorageInterface,
|
|
13
|
+
StoragePaths,
|
|
14
|
+
)
|
|
15
|
+
from isar.storage.utilities import construct_metadata_file, construct_paths
|
|
16
|
+
from robot_interface.models.inspection.inspection import InspectionBlob
|
|
17
|
+
from robot_interface.models.mission.mission import Mission
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BlobStorage(StorageInterface):
|
|
21
|
+
def __init__(self, keyvault: Keyvault) -> None:
|
|
22
|
+
self.logger = logging.getLogger("uploader")
|
|
23
|
+
|
|
24
|
+
self.container_client_data = self._get_container_client(
|
|
25
|
+
keyvault, "AZURE-STORAGE-CONNECTION-STRING-DATA"
|
|
26
|
+
)
|
|
27
|
+
self.container_client_metadata = self._get_container_client(
|
|
28
|
+
keyvault, "AZURE-STORAGE-CONNECTION-STRING-METADATA"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def _get_container_client(self, keyvault: Keyvault, secret_name: str):
|
|
32
|
+
storage_connection_string = keyvault.get_secret(secret_name).value
|
|
33
|
+
|
|
34
|
+
if storage_connection_string is None:
|
|
35
|
+
raise RuntimeError(f"{secret_name} from keyvault is None")
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
blob_service_client = BlobServiceClient.from_connection_string(
|
|
39
|
+
storage_connection_string
|
|
40
|
+
)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
self.logger.error("Unable to retrieve blob service client. Error: %s", e)
|
|
43
|
+
raise e
|
|
44
|
+
|
|
45
|
+
container_client = blob_service_client.get_container_client(
|
|
46
|
+
settings.BLOB_CONTAINER
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if not container_client.exists():
|
|
50
|
+
raise RuntimeError(
|
|
51
|
+
"The configured blob container %s does not exist",
|
|
52
|
+
settings.BLOB_CONTAINER,
|
|
53
|
+
)
|
|
54
|
+
return container_client
|
|
55
|
+
|
|
56
|
+
def store(
|
|
57
|
+
self, inspection: InspectionBlob, mission: Mission
|
|
58
|
+
) -> StoragePaths[BlobStoragePath]:
|
|
59
|
+
if inspection.data is None:
|
|
60
|
+
raise StorageException("Nothing to store. The inspection data is empty")
|
|
61
|
+
|
|
62
|
+
data_filename, metadata_filename = construct_paths(
|
|
63
|
+
inspection=inspection, mission=mission
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
metadata_bytes: bytes = construct_metadata_file(
|
|
67
|
+
inspection=inspection, mission=mission, filename=data_filename.name
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
data_path = self._upload_file(
|
|
71
|
+
filename=data_filename,
|
|
72
|
+
data=inspection.data,
|
|
73
|
+
container_client=self.container_client_data,
|
|
74
|
+
)
|
|
75
|
+
metadata_path = self._upload_file(
|
|
76
|
+
filename=metadata_filename,
|
|
77
|
+
data=metadata_bytes,
|
|
78
|
+
container_client=self.container_client_metadata,
|
|
79
|
+
)
|
|
80
|
+
return StoragePaths(data_path=data_path, metadata_path=metadata_path)
|
|
81
|
+
|
|
82
|
+
def _upload_file(
|
|
83
|
+
self, filename: Path, data: bytes, container_client: ContainerClient
|
|
84
|
+
) -> BlobStoragePath:
|
|
85
|
+
blob_client = container_client.get_blob_client(filename.as_posix())
|
|
86
|
+
try:
|
|
87
|
+
blob_client.upload_blob(data=data)
|
|
88
|
+
except ResourceExistsError as e:
|
|
89
|
+
self.logger.error(
|
|
90
|
+
"Blob %s already exists in container. Error: %s", filename.as_posix(), e
|
|
91
|
+
)
|
|
92
|
+
raise StorageException from e
|
|
93
|
+
except Exception as e:
|
|
94
|
+
self.logger.error("An unexpected error occurred while uploading blob")
|
|
95
|
+
raise StorageException from e
|
|
96
|
+
|
|
97
|
+
return BlobStoragePath(
|
|
98
|
+
storage_account=settings.BLOB_STORAGE_ACCOUNT,
|
|
99
|
+
blob_container=settings.BLOB_CONTAINER,
|
|
100
|
+
blob_name=blob_client.blob_name,
|
|
101
|
+
)
|
|
@@ -2,7 +2,12 @@ import logging
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
4
|
from isar.config.settings import settings
|
|
5
|
-
from isar.storage.storage_interface import
|
|
5
|
+
from isar.storage.storage_interface import (
|
|
6
|
+
LocalStoragePath,
|
|
7
|
+
StorageException,
|
|
8
|
+
StorageInterface,
|
|
9
|
+
StoragePaths,
|
|
10
|
+
)
|
|
6
11
|
from isar.storage.utilities import construct_metadata_file, construct_paths
|
|
7
12
|
from robot_interface.models.inspection.inspection import InspectionBlob
|
|
8
13
|
from robot_interface.models.mission.mission import Mission
|
|
@@ -13,33 +18,35 @@ class LocalStorage(StorageInterface):
|
|
|
13
18
|
self.root_folder: Path = Path(settings.LOCAL_STORAGE_PATH)
|
|
14
19
|
self.logger = logging.getLogger("uploader")
|
|
15
20
|
|
|
16
|
-
def store(
|
|
21
|
+
def store(
|
|
22
|
+
self, inspection: InspectionBlob, mission: Mission
|
|
23
|
+
) -> StoragePaths[LocalStoragePath]:
|
|
17
24
|
if inspection.data is None:
|
|
18
25
|
raise StorageException("Nothing to store. The inspection data is empty")
|
|
19
26
|
|
|
20
|
-
|
|
27
|
+
local_filename, local_metadata_filename = construct_paths(
|
|
21
28
|
inspection=inspection, mission=mission
|
|
22
29
|
)
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
data_path: Path = self.root_folder.joinpath(local_filename)
|
|
32
|
+
metadata_path: Path = self.root_folder.joinpath(local_metadata_filename)
|
|
26
33
|
|
|
27
|
-
|
|
34
|
+
data_path.parent.mkdir(parents=True, exist_ok=True)
|
|
28
35
|
|
|
29
36
|
metadata_bytes: bytes = construct_metadata_file(
|
|
30
|
-
inspection=inspection, mission=mission, filename=
|
|
37
|
+
inspection=inspection, mission=mission, filename=local_filename.name
|
|
31
38
|
)
|
|
32
39
|
try:
|
|
33
40
|
with (
|
|
34
|
-
open(
|
|
35
|
-
open(
|
|
41
|
+
open(data_path, "wb") as file,
|
|
42
|
+
open(metadata_path, "wb") as metadata_file,
|
|
36
43
|
):
|
|
37
44
|
file.write(inspection.data)
|
|
38
45
|
metadata_file.write(metadata_bytes)
|
|
39
46
|
except IOError as e:
|
|
40
47
|
self.logger.warning(
|
|
41
48
|
f"Failed open/write for one of the following files: \n"
|
|
42
|
-
f"{
|
|
49
|
+
f"{data_path}\n{metadata_path}"
|
|
43
50
|
)
|
|
44
51
|
raise StorageException from e
|
|
45
52
|
except Exception as e:
|
|
@@ -47,4 +54,7 @@ class LocalStorage(StorageInterface):
|
|
|
47
54
|
"An unexpected error occurred while writing to local storage"
|
|
48
55
|
)
|
|
49
56
|
raise StorageException from e
|
|
50
|
-
return
|
|
57
|
+
return StoragePaths(
|
|
58
|
+
data_path=LocalStoragePath(file_path=data_path),
|
|
59
|
+
metadata_path=LocalStoragePath(file_path=metadata_path),
|
|
60
|
+
)
|
|
@@ -1,25 +1,46 @@
|
|
|
1
1
|
from abc import ABCMeta, abstractmethod
|
|
2
|
-
from
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Generic, TypeVar
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
3
6
|
|
|
4
7
|
from robot_interface.models.inspection.inspection import InspectionBlob
|
|
5
8
|
from robot_interface.models.mission.mission import Mission
|
|
6
9
|
|
|
7
10
|
|
|
11
|
+
class BlobStoragePath(BaseModel):
|
|
12
|
+
storage_account: str
|
|
13
|
+
blob_container: str
|
|
14
|
+
blob_name: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LocalStoragePath(BaseModel):
|
|
18
|
+
file_path: Path
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
TPath = TypeVar("TPath", BlobStoragePath, LocalStoragePath)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class StoragePaths(BaseModel, Generic[TPath]):
|
|
25
|
+
data_path: TPath
|
|
26
|
+
metadata_path: TPath
|
|
27
|
+
|
|
28
|
+
|
|
8
29
|
class StorageInterface(metaclass=ABCMeta):
|
|
9
30
|
@abstractmethod
|
|
10
|
-
def store(self, inspection: InspectionBlob, mission: Mission) ->
|
|
31
|
+
def store(self, inspection: InspectionBlob, mission: Mission) -> StoragePaths:
|
|
11
32
|
"""
|
|
12
33
|
Parameters
|
|
13
34
|
----------
|
|
14
|
-
mission : Mission
|
|
15
|
-
Mission the inspection is a part of.
|
|
16
35
|
inspection : InspectionBlob
|
|
17
36
|
The inspection object to be stored.
|
|
37
|
+
mission : Mission
|
|
38
|
+
Mission the inspection is a part of.
|
|
18
39
|
|
|
19
40
|
Returns
|
|
20
41
|
----------
|
|
21
|
-
|
|
22
|
-
|
|
42
|
+
StoragePaths
|
|
43
|
+
Paths to the data and metadata
|
|
23
44
|
|
|
24
45
|
Raises
|
|
25
46
|
----------
|
|
@@ -4,11 +4,17 @@ from dataclasses import dataclass
|
|
|
4
4
|
from datetime import datetime, timedelta, timezone
|
|
5
5
|
from queue import Empty, Queue
|
|
6
6
|
from threading import Event
|
|
7
|
-
from typing import List
|
|
7
|
+
from typing import List
|
|
8
8
|
|
|
9
9
|
from isar.config.settings import settings
|
|
10
10
|
from isar.models.events import Events
|
|
11
|
-
from isar.storage.storage_interface import
|
|
11
|
+
from isar.storage.storage_interface import (
|
|
12
|
+
BlobStoragePath,
|
|
13
|
+
LocalStoragePath,
|
|
14
|
+
StorageException,
|
|
15
|
+
StorageInterface,
|
|
16
|
+
StoragePaths,
|
|
17
|
+
)
|
|
12
18
|
from robot_interface.models.inspection.inspection import (
|
|
13
19
|
Inspection,
|
|
14
20
|
InspectionBlob,
|
|
@@ -133,10 +139,10 @@ class Uploader:
|
|
|
133
139
|
except Empty:
|
|
134
140
|
continue
|
|
135
141
|
|
|
136
|
-
def _upload(self, item: BlobItem) ->
|
|
137
|
-
|
|
142
|
+
def _upload(self, item: BlobItem) -> StoragePaths:
|
|
143
|
+
inspection_paths: StoragePaths
|
|
138
144
|
try:
|
|
139
|
-
|
|
145
|
+
inspection_paths = item.storage_handler.store(
|
|
140
146
|
inspection=item.inspection, mission=item.mission
|
|
141
147
|
)
|
|
142
148
|
self.logger.info(
|
|
@@ -144,7 +150,7 @@ class Uploader:
|
|
|
144
150
|
f"uploaded inspection {str(item.inspection.id)[:8]}"
|
|
145
151
|
)
|
|
146
152
|
self._internal_upload_queue.remove(item)
|
|
147
|
-
except StorageException:
|
|
153
|
+
except StorageException as e:
|
|
148
154
|
if item.get_retry_count() < self.max_retry_attempts:
|
|
149
155
|
item.increment_retry(self.max_wait_time)
|
|
150
156
|
self.logger.warning(
|
|
@@ -160,7 +166,8 @@ class Uploader:
|
|
|
160
166
|
f"{str(item.inspection.id)[:8]}. Aborting upload."
|
|
161
167
|
)
|
|
162
168
|
self._internal_upload_queue.remove(item)
|
|
163
|
-
|
|
169
|
+
raise e
|
|
170
|
+
return inspection_paths
|
|
164
171
|
|
|
165
172
|
def _process_upload_queue(self) -> None:
|
|
166
173
|
def should_upload(_item):
|
|
@@ -181,10 +188,17 @@ class Uploader:
|
|
|
181
188
|
)
|
|
182
189
|
self._internal_upload_queue.remove(item)
|
|
183
190
|
elif isinstance(item, BlobItem):
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
191
|
+
try:
|
|
192
|
+
inspection_paths = self._upload(item)
|
|
193
|
+
if isinstance(inspection_paths.data_path, LocalStoragePath):
|
|
194
|
+
self.logger.info("Skipping publishing when using local storage")
|
|
195
|
+
else:
|
|
196
|
+
self._publish_inspection_result(
|
|
197
|
+
inspection=item.inspection,
|
|
198
|
+
inspection_paths=inspection_paths,
|
|
199
|
+
)
|
|
200
|
+
except StorageException:
|
|
201
|
+
pass
|
|
188
202
|
else:
|
|
189
203
|
self.logger.warning(
|
|
190
204
|
f"Unable to process upload item as its type {type(item).__name__} is not supported"
|
|
@@ -223,7 +237,9 @@ class Uploader:
|
|
|
223
237
|
)
|
|
224
238
|
|
|
225
239
|
def _publish_inspection_result(
|
|
226
|
-
self,
|
|
240
|
+
self,
|
|
241
|
+
inspection: InspectionBlob,
|
|
242
|
+
inspection_paths: StoragePaths[BlobStoragePath],
|
|
227
243
|
) -> None:
|
|
228
244
|
"""Publishes the reference of the inspection result to the MQTT Broker
|
|
229
245
|
along with the analysis type
|
|
@@ -235,7 +251,8 @@ class Uploader:
|
|
|
235
251
|
isar_id=settings.ISAR_ID,
|
|
236
252
|
robot_name=settings.ROBOT_NAME,
|
|
237
253
|
inspection_id=inspection.id,
|
|
238
|
-
|
|
254
|
+
blob_storage_data_path=inspection_paths.data_path,
|
|
255
|
+
blob_storage_metadata_path=inspection_paths.metadata_path,
|
|
239
256
|
installation_code=settings.PLANT_SHORT_NAME,
|
|
240
257
|
tag_id=inspection.metadata.tag_id,
|
|
241
258
|
inspection_type=type(inspection).__name__,
|
|
@@ -5,6 +5,10 @@ from datetime import datetime, timezone
|
|
|
5
5
|
from queue import Queue
|
|
6
6
|
from typing import Callable, Tuple
|
|
7
7
|
|
|
8
|
+
from paho.mqtt.packettypes import PacketTypes
|
|
9
|
+
from paho.mqtt.properties import Properties
|
|
10
|
+
|
|
11
|
+
from isar.config.settings import settings
|
|
8
12
|
from robot_interface.models.exceptions.robot_exceptions import (
|
|
9
13
|
RobotTelemetryException,
|
|
10
14
|
RobotTelemetryNoUpdateException,
|
|
@@ -14,10 +18,21 @@ from robot_interface.telemetry.payloads import CloudHealthPayload
|
|
|
14
18
|
from robot_interface.utilities.json_service import EnhancedJSONEncoder
|
|
15
19
|
|
|
16
20
|
|
|
21
|
+
def props_expiry(seconds: int) -> Properties:
|
|
22
|
+
p = Properties(PacketTypes.PUBLISH)
|
|
23
|
+
p.MessageExpiryInterval = seconds
|
|
24
|
+
return p
|
|
25
|
+
|
|
26
|
+
|
|
17
27
|
class MqttClientInterface(metaclass=ABCMeta):
|
|
18
28
|
@abstractmethod
|
|
19
29
|
def publish(
|
|
20
|
-
self,
|
|
30
|
+
self,
|
|
31
|
+
topic: str,
|
|
32
|
+
payload: str,
|
|
33
|
+
qos: int = 0,
|
|
34
|
+
retain: bool = False,
|
|
35
|
+
properties: Properties = None,
|
|
21
36
|
) -> None:
|
|
22
37
|
"""
|
|
23
38
|
Parameters
|
|
@@ -42,9 +57,20 @@ class MqttPublisher(MqttClientInterface):
|
|
|
42
57
|
self.mqtt_queue: Queue = mqtt_queue
|
|
43
58
|
|
|
44
59
|
def publish(
|
|
45
|
-
self,
|
|
60
|
+
self,
|
|
61
|
+
topic: str,
|
|
62
|
+
payload: str,
|
|
63
|
+
qos: int = 0,
|
|
64
|
+
retain: bool = False,
|
|
65
|
+
properties: Properties = None,
|
|
46
66
|
) -> None:
|
|
47
|
-
queue_message: Tuple[str, str, int, bool] = (
|
|
67
|
+
queue_message: Tuple[str, str, int, bool, Properties] = (
|
|
68
|
+
topic,
|
|
69
|
+
payload,
|
|
70
|
+
qos,
|
|
71
|
+
retain,
|
|
72
|
+
properties,
|
|
73
|
+
)
|
|
48
74
|
self.mqtt_queue.put(queue_message)
|
|
49
75
|
|
|
50
76
|
|
|
@@ -57,6 +83,7 @@ class MqttTelemetryPublisher(MqttClientInterface):
|
|
|
57
83
|
interval: float,
|
|
58
84
|
qos: int = 0,
|
|
59
85
|
retain: bool = False,
|
|
86
|
+
properties: Properties = None,
|
|
60
87
|
) -> None:
|
|
61
88
|
self.mqtt_queue: Queue = mqtt_queue
|
|
62
89
|
self.telemetry_method: Callable = telemetry_method
|
|
@@ -64,9 +91,13 @@ class MqttTelemetryPublisher(MqttClientInterface):
|
|
|
64
91
|
self.interval: float = interval
|
|
65
92
|
self.qos: int = qos
|
|
66
93
|
self.retain: bool = retain
|
|
94
|
+
self.properties: Properties = properties
|
|
67
95
|
|
|
68
96
|
def run(self, isar_id: str, robot_name: str) -> None:
|
|
69
97
|
self.cloud_health_topic: str = f"isar/{isar_id}/cloud_health"
|
|
98
|
+
self.battery_topic: str = f"isar/{isar_id}/battery"
|
|
99
|
+
self.pose_topic: str = f"isar/{isar_id}/pose"
|
|
100
|
+
self.pressure_topic: str = f"isar/{isar_id}/pressure"
|
|
70
101
|
topic: str
|
|
71
102
|
payload: str
|
|
72
103
|
|
|
@@ -84,12 +115,37 @@ class MqttTelemetryPublisher(MqttClientInterface):
|
|
|
84
115
|
)
|
|
85
116
|
topic = self.cloud_health_topic
|
|
86
117
|
|
|
87
|
-
|
|
118
|
+
publish_properties = self.properties
|
|
119
|
+
|
|
120
|
+
if topic in (
|
|
121
|
+
self.battery_topic,
|
|
122
|
+
self.pose_topic,
|
|
123
|
+
self.pressure_topic,
|
|
124
|
+
):
|
|
125
|
+
publish_properties = props_expiry(settings.MQTT_TELEMETRY_EXPIRY)
|
|
88
126
|
|
|
127
|
+
self.publish(
|
|
128
|
+
topic=topic,
|
|
129
|
+
payload=payload,
|
|
130
|
+
qos=self.qos,
|
|
131
|
+
retain=self.retain,
|
|
132
|
+
properties=publish_properties,
|
|
133
|
+
)
|
|
89
134
|
time.sleep(self.interval)
|
|
90
135
|
|
|
91
136
|
def publish(
|
|
92
|
-
self,
|
|
137
|
+
self,
|
|
138
|
+
topic: str,
|
|
139
|
+
payload: str,
|
|
140
|
+
qos: int = 0,
|
|
141
|
+
retain: bool = False,
|
|
142
|
+
properties: Properties = None,
|
|
93
143
|
) -> None:
|
|
94
|
-
queue_message: Tuple[str, str, int, bool] = (
|
|
144
|
+
queue_message: Tuple[str, str, int, bool, Properties] = (
|
|
145
|
+
topic,
|
|
146
|
+
payload,
|
|
147
|
+
qos,
|
|
148
|
+
retain,
|
|
149
|
+
properties,
|
|
150
|
+
)
|
|
95
151
|
self.mqtt_queue.put(queue_message)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from datetime import datetime
|
|
3
|
-
from typing import List, Optional
|
|
3
|
+
from typing import List, Optional
|
|
4
4
|
|
|
5
5
|
from alitra import Pose
|
|
6
6
|
|
|
7
|
+
from isar.storage.storage_interface import BlobStoragePath
|
|
7
8
|
from robot_interface.models.exceptions.robot_exceptions import ErrorReason
|
|
8
9
|
from robot_interface.models.mission.status import MissionStatus, RobotStatus, TaskStatus
|
|
9
10
|
from robot_interface.models.mission.task import TaskTypes
|
|
@@ -119,7 +120,8 @@ class InspectionResultPayload:
|
|
|
119
120
|
isar_id: str
|
|
120
121
|
robot_name: str
|
|
121
122
|
inspection_id: str
|
|
122
|
-
|
|
123
|
+
blob_storage_data_path: BlobStoragePath
|
|
124
|
+
blob_storage_metadata_path: BlobStoragePath
|
|
123
125
|
installation_code: str
|
|
124
126
|
tag_id: Optional[str]
|
|
125
127
|
inspection_type: Optional[str]
|
|
@@ -7,6 +7,7 @@ from uuid import UUID
|
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
from alitra import Orientation
|
|
10
|
+
from pydantic import BaseModel
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class EnhancedJSONEncoder(json.JSONEncoder):
|
|
@@ -15,6 +16,11 @@ class EnhancedJSONEncoder(json.JSONEncoder):
|
|
|
15
16
|
"""
|
|
16
17
|
|
|
17
18
|
def default(self, o):
|
|
19
|
+
if isinstance(o, BaseModel):
|
|
20
|
+
dump = getattr(o, "model_dump", None)
|
|
21
|
+
if callable(dump):
|
|
22
|
+
return dump()
|
|
23
|
+
return o.__dict__
|
|
18
24
|
if is_dataclass(o):
|
|
19
25
|
return asdict(o) # type: ignore
|
|
20
26
|
if isinstance(o, UUID):
|