isar 1.30.3__tar.gz → 1.30.5__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.30.3 → isar-1.30.5}/.gitignore +3 -0
- {isar-1.30.3 → isar-1.30.5}/PKG-INFO +1 -1
- {isar-1.30.3 → isar-1.30.5}/requirements.txt +1 -1
- {isar-1.30.3 → isar-1.30.5}/src/isar/apis/api.py +40 -3
- {isar-1.30.3 → isar-1.30.5}/src/isar/apis/models/models.py +4 -4
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/settings.py +2 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/modules.py +11 -10
- {isar-1.30.3 → isar-1.30.5}/src/isar/script.py +3 -1
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/utilities/scheduling_utilities.py +10 -1
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/generic_states/ongoing_mission.py +1 -1
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/state_machine.py +7 -10
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/states/stopping.py +2 -2
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/transitions/functions/start_mission.py +1 -1
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/transitions/functions/stop.py +44 -1
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/transitions/mission.py +14 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar.egg-info/PKG-INFO +1 -1
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/telemetry/payloads.py +6 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/apis/scheduler/test_scheduler_router.py +16 -2
- {isar-1.30.3 → isar-1.30.5}/tests/isar/state_machine/test_state_machine.py +112 -6
- {isar-1.30.3 → isar-1.30.5}/tests/test_double/mission_definition.py +10 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_double/task.py +3 -2
- {isar-1.30.3 → isar-1.30.5}/.dockerignore +0 -0
- {isar-1.30.3 → isar-1.30.5}/.env.test +0 -0
- {isar-1.30.3 → isar-1.30.5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {isar-1.30.3 → isar-1.30.5}/.github/ISSUE_TEMPLATE/feature.md +0 -0
- {isar-1.30.3 → isar-1.30.5}/.github/ISSUE_TEMPLATE/improvement.md +0 -0
- {isar-1.30.3 → isar-1.30.5}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {isar-1.30.3 → isar-1.30.5}/.github/release.yml +0 -0
- {isar-1.30.3 → isar-1.30.5}/.github/workflows/compile_requirements.yml +0 -0
- {isar-1.30.3 → isar-1.30.5}/.github/workflows/project_automations.yml +0 -0
- {isar-1.30.3 → isar-1.30.5}/.github/workflows/pythonpackage.yml +0 -0
- {isar-1.30.3 → isar-1.30.5}/.github/workflows/pythonpublish.yml +0 -0
- {isar-1.30.3 → isar-1.30.5}/.github/workflows/stale.yml +0 -0
- {isar-1.30.3 → isar-1.30.5}/.pre-commit-config.yaml +0 -0
- {isar-1.30.3 → isar-1.30.5}/LICENSE +0 -0
- {isar-1.30.3 → isar-1.30.5}/README.md +0 -0
- {isar-1.30.3 → isar-1.30.5}/SECURITY.md +0 -0
- {isar-1.30.3 → isar-1.30.5}/docs/Makefile +0 -0
- {isar-1.30.3 → isar-1.30.5}/docs/full_state_machine_diagram.png +0 -0
- {isar-1.30.3 → isar-1.30.5}/docs/make.bat +0 -0
- {isar-1.30.3 → isar-1.30.5}/docs/mission_state_machine_diagram.png +0 -0
- {isar-1.30.3 → isar-1.30.5}/docs/robot_status_state_machine_diagram.png +0 -0
- {isar-1.30.3 → isar-1.30.5}/docs/rst_processing.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/docs/source/conf.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/docs/source/index.rst +0 -0
- {isar-1.30.3 → isar-1.30.5}/docs/source/readme_link.md +0 -0
- {isar-1.30.3 → isar-1.30.5}/docs/update_state_diagram.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/main.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/pyproject.toml +0 -0
- {isar-1.30.3 → isar-1.30.5}/radixconfig.yml +0 -0
- {isar-1.30.3 → isar-1.30.5}/setup.cfg +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/apis/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/apis/models/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/apis/models/start_mission_definition.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/apis/robot_control/robot_controller.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/apis/schedule/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/apis/schedule/scheduling_controller.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/apis/security/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/apis/security/authentication.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/certs/ca-cert.pem +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/configuration_error.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/keyvault/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/keyvault/keyvault_error.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/keyvault/keyvault_service.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/log.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/logging.conf +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/maps/JSP1_intermediate_deck.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/maps/JSP1_weather_deck.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/maps/default_map.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/maps/klab_b.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/maps/klab_compressor.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/maps/klab_turtlebot.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/maps/turtleworld.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/predefined_mission_definition/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/predefined_mission_definition/default_exr.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/predefined_mission_definition/default_mission.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/predefined_mission_definition/default_turtlebot.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/predefined_missions/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/predefined_missions/default.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/config/predefined_missions/default_turtlebot.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/mission_planner/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/mission_planner/local_planner.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/mission_planner/mission_planner_interface.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/mission_planner/sequential_task_selector.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/mission_planner/task_selector_interface.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/models/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/models/communication/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/models/communication/message.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/models/communication/queues/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/models/communication/queues/events.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/models/communication/queues/queue_io.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/models/communication/queues/queue_timeout_error.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/models/communication/queues/queue_utils.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/models/communication/queues/status_queue.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/models/mission_metadata/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/robot/robot.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/robot/robot_start_mission.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/robot/robot_status.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/robot/robot_stop_mission.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/robot/robot_task_status.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/auth/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/auth/azure_credentials.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/service_connections/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/service_connections/mqtt/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/service_connections/mqtt/mqtt_client.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/service_connections/mqtt/robot_info_publisher.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/service_connections/request_handler.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/service_connections/stid/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/utilities/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/utilities/queue_utilities.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/utilities/robot_utilities.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/services/utilities/threaded_request.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/generic_states/idle.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/generic_states/robot_unavailable.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/states/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/states/await_next_mission.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/states/blocked_protective_stop.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/states/home.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/states/monitor.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/states/offline.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/states/paused.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/states/returning_home.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/states/robot_standing_still.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/states/unknown_status.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/states_enum.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/transitions/functions/fail_mission.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/transitions/functions/finish_mission.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/transitions/functions/pause.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/transitions/functions/resume.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/transitions/functions/return_home.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/transitions/functions/robot_status.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/transitions/functions/utils.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/transitions/return_home.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/state_machine/transitions/robot_status.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/storage/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/storage/blob_storage.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/storage/local_storage.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/storage/storage_interface.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/storage/uploader.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar/storage/utilities.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar.egg-info/SOURCES.txt +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar.egg-info/dependency_links.txt +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar.egg-info/entry_points.txt +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar.egg-info/requires.txt +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/isar.egg-info/top_level.txt +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/exceptions/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/exceptions/robot_exceptions.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/initialize/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/inspection/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/inspection/inspection.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/mission/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/mission/mission.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/mission/status.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/mission/task.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/robots/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/robots/battery_state.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/robots/media.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/models/robots/robot_model.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/robot_interface.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/telemetry/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/telemetry/mqtt_client.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/test_robot_interface.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/utilities/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/utilities/json_service.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/src/robot_interface/utilities/uuid_string_factory.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/conftest.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/integration/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/integration/turtlebot/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/integration/turtlebot/config/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/integration/turtlebot/config/maps/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/integration/turtlebot/config/maps/turtleworld.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/integration/turtlebot/config/missions/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/integration/turtlebot/config/missions/default.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/integration/turtlebot/test_successful_mission.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/apis/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/apis/models/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/apis/models/example_mission_definition.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/apis/models/test_start_mission_definition.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/apis/scheduler/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/apis/security/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/apis/security/test_authentication.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/mission/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/mission/test_mission.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/models/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/models/communication/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/models/communication/test_queues.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/services/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/services/readers/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/services/readers/test_mission_reader.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/services/service_connections/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/services/service_connections/echo/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/services/service_connections/test_base_request_handler.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/services/utilities/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/services/utilities/test_queue_utilities.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/services/utilities/test_scheduling_utilities.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/state_machine/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/state_machine/states/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/state_machine/states/test_monitor.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/storage/test_blob_storage.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/isar/storage/test_uploader.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_data/test_map_config/test_map_config.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_data/test_mission_not_working.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_data/test_mission_working.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_data/test_mission_working_no_tasks.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_data/test_thermal_image_mission.json +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_double/__init__.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_double/blob_storage.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_double/mqtt_client.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_double/pose.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_double/request.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_double/robot_interface.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_double/status.py +0 -0
- {isar-1.30.3 → isar-1.30.5}/tests/test_double/token.py +0 -0
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import logging
|
|
3
|
+
import time
|
|
4
|
+
from datetime import datetime, timezone
|
|
2
5
|
from http import HTTPStatus
|
|
3
6
|
from logging import Logger
|
|
4
7
|
from typing import List, Union
|
|
@@ -24,6 +27,9 @@ from isar.config.configuration_error import ConfigurationError
|
|
|
24
27
|
from isar.config.keyvault.keyvault_error import KeyvaultError
|
|
25
28
|
from isar.config.keyvault.keyvault_service import Keyvault
|
|
26
29
|
from isar.config.settings import settings
|
|
30
|
+
from robot_interface.telemetry.mqtt_client import MqttClientInterface
|
|
31
|
+
from robot_interface.telemetry.payloads import StartUpMessagePayload
|
|
32
|
+
from robot_interface.utilities.json_service import EnhancedJSONEncoder
|
|
27
33
|
|
|
28
34
|
HTTP_URL = COMMON_ATTRIBUTES["HTTP_URL"]
|
|
29
35
|
HTTP_STATUS_CODE = COMMON_ATTRIBUTES["HTTP_STATUS_CODE"]
|
|
@@ -37,6 +43,7 @@ class API:
|
|
|
37
43
|
scheduling_controller: SchedulingController,
|
|
38
44
|
robot_controller: RobotController,
|
|
39
45
|
keyvault: Keyvault,
|
|
46
|
+
mqtt_publisher: MqttClientInterface,
|
|
40
47
|
port: int = settings.API_PORT,
|
|
41
48
|
azure_ai_logging_enabled: bool = settings.LOG_HANDLER_APPLICATION_INSIGHTS_ENABLED,
|
|
42
49
|
) -> None:
|
|
@@ -47,22 +54,31 @@ class API:
|
|
|
47
54
|
self.host: str = "0.0.0.0" # Locking uvicorn to use 0.0.0.0
|
|
48
55
|
self.port: int = port
|
|
49
56
|
self.azure_ai_logging_enabled: bool = azure_ai_logging_enabled
|
|
57
|
+
self.mqtt_publisher: MqttClientInterface = mqtt_publisher
|
|
50
58
|
|
|
51
59
|
self.logger: Logger = logging.getLogger("api")
|
|
52
60
|
|
|
53
61
|
self.app: FastAPI = self._create_app()
|
|
62
|
+
self.server = self._setup_server()
|
|
54
63
|
|
|
55
64
|
def get_app(self) -> FastAPI:
|
|
56
65
|
return self.app
|
|
57
66
|
|
|
58
|
-
def
|
|
59
|
-
uvicorn.
|
|
67
|
+
def _setup_server(self) -> uvicorn.Server:
|
|
68
|
+
config = uvicorn.Config(
|
|
60
69
|
self.app,
|
|
61
70
|
port=self.port,
|
|
62
71
|
host=self.host,
|
|
63
72
|
reload=False,
|
|
64
73
|
log_config=None,
|
|
65
74
|
)
|
|
75
|
+
return uvicorn.Server(config)
|
|
76
|
+
|
|
77
|
+
def wait_for_api_server_ready(self) -> None:
|
|
78
|
+
while not self.server.started:
|
|
79
|
+
time.sleep(0.01)
|
|
80
|
+
self.logger.info("Uvicorn server has been started")
|
|
81
|
+
self._publish_startup_message()
|
|
66
82
|
|
|
67
83
|
def _create_app(self) -> FastAPI:
|
|
68
84
|
tags_metadata = [
|
|
@@ -73,7 +89,10 @@ class API:
|
|
|
73
89
|
]
|
|
74
90
|
app = FastAPI(
|
|
75
91
|
openapi_tags=tags_metadata,
|
|
76
|
-
on_startup=[
|
|
92
|
+
on_startup=[
|
|
93
|
+
self.authenticator.load_config,
|
|
94
|
+
self._log_startup_message,
|
|
95
|
+
],
|
|
77
96
|
swagger_ui_oauth2_redirect_url="/oauth2-redirect",
|
|
78
97
|
swagger_ui_init_oauth={
|
|
79
98
|
"usePkceWithAuthorizationCodeGrant": True,
|
|
@@ -349,3 +368,21 @@ class API:
|
|
|
349
368
|
)
|
|
350
369
|
|
|
351
370
|
return response
|
|
371
|
+
|
|
372
|
+
def _publish_startup_message(self) -> None:
|
|
373
|
+
if not self.mqtt_publisher:
|
|
374
|
+
return
|
|
375
|
+
|
|
376
|
+
payload: StartUpMessagePayload = StartUpMessagePayload(
|
|
377
|
+
isar_id=settings.ISAR_ID,
|
|
378
|
+
timestamp=datetime.now(timezone.utc),
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
self.logger.info("Publishing startup message to MQTT broker")
|
|
382
|
+
|
|
383
|
+
self.mqtt_publisher.publish(
|
|
384
|
+
topic=settings.TOPIC_ISAR_STARTUP,
|
|
385
|
+
payload=json.dumps(payload, cls=EnhancedJSONEncoder),
|
|
386
|
+
qos=1,
|
|
387
|
+
retain=True,
|
|
388
|
+
)
|
|
@@ -19,10 +19,10 @@ class StartMissionResponse(BaseModel):
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class ControlMissionResponse(BaseModel):
|
|
22
|
-
mission_id: str
|
|
23
|
-
mission_status: str
|
|
24
|
-
task_id: str
|
|
25
|
-
task_status: str
|
|
22
|
+
mission_id: Optional[str]
|
|
23
|
+
mission_status: Optional[str]
|
|
24
|
+
task_id: Optional[str]
|
|
25
|
+
task_status: Optional[str]
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class RobotInfoResponse(BaseModel):
|
|
@@ -211,6 +211,7 @@ class Settings(BaseSettings):
|
|
|
211
211
|
TOPIC_ISAR_ROBOT_HEARTBEAT: str = Field(
|
|
212
212
|
default="robot_heartbeat", validate_default=True
|
|
213
213
|
)
|
|
214
|
+
TOPIC_ISAR_STARTUP: str = Field(default="startup", validate_default=True)
|
|
214
215
|
|
|
215
216
|
# Logging
|
|
216
217
|
|
|
@@ -261,6 +262,7 @@ class Settings(BaseSettings):
|
|
|
261
262
|
"TOPIC_ISAR_ROBOT_HEARTBEAT",
|
|
262
263
|
"TOPIC_ISAR_INSPECTION_RESULT",
|
|
263
264
|
"TOPIC_ISAR_INSPECTION_VALUE",
|
|
265
|
+
"TOPIC_ISAR_STARTUP",
|
|
264
266
|
)
|
|
265
267
|
@classmethod
|
|
266
268
|
def prefix_isar_topics(cls, v: Any, info: ValidationInfo):
|
|
@@ -45,6 +45,16 @@ class ApplicationContainer(containers.DeclarativeContainer):
|
|
|
45
45
|
)
|
|
46
46
|
robot_utilities = providers.Singleton(RobotUtilities, robot=robot_interface)
|
|
47
47
|
|
|
48
|
+
# Mqtt client
|
|
49
|
+
mqtt_client = (
|
|
50
|
+
providers.Singleton(
|
|
51
|
+
MqttPublisher,
|
|
52
|
+
mqtt_queue=providers.Callable(events.provided.mqtt_queue),
|
|
53
|
+
)
|
|
54
|
+
if settings.MQTT_ENABLED
|
|
55
|
+
else None
|
|
56
|
+
)
|
|
57
|
+
|
|
48
58
|
# API and controllers
|
|
49
59
|
authenticator = providers.Singleton(Authenticator)
|
|
50
60
|
scheduling_utilities = providers.Singleton(
|
|
@@ -65,6 +75,7 @@ class ApplicationContainer(containers.DeclarativeContainer):
|
|
|
65
75
|
scheduling_controller=scheduling_controller,
|
|
66
76
|
robot_controller=robot_controller,
|
|
67
77
|
keyvault=keyvault,
|
|
78
|
+
mqtt_publisher=mqtt_client,
|
|
68
79
|
)
|
|
69
80
|
|
|
70
81
|
# Storage
|
|
@@ -77,16 +88,6 @@ class ApplicationContainer(containers.DeclarativeContainer):
|
|
|
77
88
|
storage_handlers_temp.append(blob_storage)
|
|
78
89
|
storage_handlers = providers.List(*storage_handlers_temp)
|
|
79
90
|
|
|
80
|
-
# Mqtt client
|
|
81
|
-
mqtt_client = (
|
|
82
|
-
providers.Singleton(
|
|
83
|
-
MqttPublisher,
|
|
84
|
-
mqtt_queue=providers.Callable(events.provided.mqtt_queue),
|
|
85
|
-
)
|
|
86
|
-
if settings.MQTT_ENABLED
|
|
87
|
-
else None
|
|
88
|
-
)
|
|
89
|
-
|
|
90
91
|
# State machine
|
|
91
92
|
task_selector = providers.Singleton(
|
|
92
93
|
SequentialTaskSelector
|
|
@@ -160,13 +160,15 @@ def start() -> None:
|
|
|
160
160
|
threads.extend(publishers)
|
|
161
161
|
|
|
162
162
|
api: API = injector.api()
|
|
163
|
-
api_thread: Thread = Thread(target=api.
|
|
163
|
+
api_thread: Thread = Thread(target=api.server.run, name="ISAR API", daemon=True)
|
|
164
164
|
threads.append(api_thread)
|
|
165
165
|
|
|
166
166
|
for thread in threads:
|
|
167
167
|
thread.start()
|
|
168
168
|
logger.info("Started thread: %s", thread.name)
|
|
169
169
|
|
|
170
|
+
api.wait_for_api_server_ready()
|
|
171
|
+
|
|
170
172
|
while True:
|
|
171
173
|
for thread in threads:
|
|
172
174
|
if not thread.is_alive():
|
|
@@ -22,6 +22,7 @@ from isar.models.communication.queues.queue_timeout_error import QueueTimeoutErr
|
|
|
22
22
|
from isar.services.utilities.queue_utilities import QueueUtilities
|
|
23
23
|
from isar.state_machine.states_enum import States
|
|
24
24
|
from robot_interface.models.mission.mission import Mission
|
|
25
|
+
from robot_interface.models.mission.status import MissionStatus
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
class SchedulingUtilities:
|
|
@@ -253,13 +254,21 @@ class SchedulingUtilities:
|
|
|
253
254
|
|
|
254
255
|
Raises
|
|
255
256
|
------
|
|
256
|
-
|
|
257
|
+
HTTPException 503 Service Unavailable
|
|
258
|
+
The request was understood, but attempting to stop the mission failed
|
|
259
|
+
HTTPException 408 Request timeout
|
|
257
260
|
If there is a timeout while communicating with the state machine
|
|
258
261
|
"""
|
|
259
262
|
try:
|
|
260
263
|
stop_mission_response: ControlMissionResponse = self._send_command(
|
|
261
264
|
True, self.api_events.stop_mission
|
|
262
265
|
)
|
|
266
|
+
if stop_mission_response.mission_status != MissionStatus.Cancelled.value:
|
|
267
|
+
error_message = "Failed to stop mission"
|
|
268
|
+
self.logger.error(error_message)
|
|
269
|
+
raise HTTPException(
|
|
270
|
+
status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail=error_message
|
|
271
|
+
)
|
|
263
272
|
except QueueTimeoutError:
|
|
264
273
|
error_message = "Internal Server Error - Failed to stop mission"
|
|
265
274
|
self.logger.error(error_message)
|
|
@@ -194,6 +194,7 @@ class StateMachine(object):
|
|
|
194
194
|
self.current_task = None
|
|
195
195
|
self.send_task_status()
|
|
196
196
|
self.current_mission = None
|
|
197
|
+
self.mission_ongoing = False
|
|
197
198
|
|
|
198
199
|
def start_mission(self, mission: Mission):
|
|
199
200
|
"""Starts a scheduled mission."""
|
|
@@ -316,17 +317,13 @@ class StateMachine(object):
|
|
|
316
317
|
self.logger.info("Mission overview:\n%s", log_statement)
|
|
317
318
|
|
|
318
319
|
def _make_control_mission_response(self) -> ControlMissionResponse:
|
|
319
|
-
if self.current_mission is None:
|
|
320
|
-
raise ValueError("No current mission is set")
|
|
321
|
-
|
|
322
|
-
if self.current_task is None:
|
|
323
|
-
raise ValueError("No current task is set")
|
|
324
|
-
|
|
325
320
|
return ControlMissionResponse(
|
|
326
|
-
mission_id=self.current_mission.id,
|
|
327
|
-
mission_status=
|
|
328
|
-
|
|
329
|
-
|
|
321
|
+
mission_id=self.current_mission.id if self.current_mission else None,
|
|
322
|
+
mission_status=(
|
|
323
|
+
self.current_mission.status if self.current_mission else None
|
|
324
|
+
),
|
|
325
|
+
task_id=self.current_task.id if self.current_task else None,
|
|
326
|
+
task_status=self.current_task.status if self.current_task else None,
|
|
330
327
|
)
|
|
331
328
|
|
|
332
329
|
def _queue_empty_response(self) -> None:
|
|
@@ -39,9 +39,9 @@ class Stopping(State):
|
|
|
39
39
|
if error_message is not None:
|
|
40
40
|
self.logger.warning(error_message.error_description)
|
|
41
41
|
if self.stopping_return_home_mission:
|
|
42
|
-
self.state_machine.
|
|
42
|
+
self.state_machine.return_home_mission_stopping_failed() # type: ignore
|
|
43
43
|
else:
|
|
44
|
-
self.state_machine.
|
|
44
|
+
self.state_machine.mission_stopping_failed() # type: ignore
|
|
45
45
|
return True
|
|
46
46
|
return False
|
|
47
47
|
|
|
@@ -18,7 +18,7 @@ def put_start_mission_on_queue(state_machine: "StateMachine") -> bool:
|
|
|
18
18
|
|
|
19
19
|
def initiate_mission(state_machine: "StateMachine") -> bool:
|
|
20
20
|
state_machine.logger.info(
|
|
21
|
-
"
|
|
21
|
+
"Initiating mission:\n"
|
|
22
22
|
f" Mission ID: {state_machine.current_mission.id}\n"
|
|
23
23
|
f" Mission Name: {state_machine.current_mission.name}\n"
|
|
24
24
|
f" Number of Tasks: {len(state_machine.current_mission.tasks)}"
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING
|
|
2
2
|
|
|
3
|
-
from isar.models.communication.queues.queue_utils import
|
|
3
|
+
from isar.models.communication.queues.queue_utils import (
|
|
4
|
+
check_for_event_without_consumption,
|
|
5
|
+
trigger_event_without_data,
|
|
6
|
+
)
|
|
4
7
|
|
|
5
8
|
if TYPE_CHECKING:
|
|
6
9
|
from isar.state_machine.state_machine import StateMachine
|
|
@@ -39,11 +42,51 @@ def stop_mission_cleanup(state_machine: "StateMachine") -> bool:
|
|
|
39
42
|
return True
|
|
40
43
|
|
|
41
44
|
|
|
45
|
+
def stop_mission_failed(state_machine: "StateMachine") -> bool:
|
|
46
|
+
stopped_mission_response: ControlMissionResponse = (
|
|
47
|
+
state_machine._make_control_mission_response()
|
|
48
|
+
)
|
|
49
|
+
state_machine.events.api_requests.stop_mission.output.put(stopped_mission_response)
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
|
|
42
53
|
def stop_return_home_mission_cleanup(state_machine: "StateMachine") -> bool:
|
|
43
54
|
if state_machine.current_mission is None:
|
|
44
55
|
state_machine._queue_empty_response()
|
|
45
56
|
state_machine.reset_state_machine()
|
|
46
57
|
return True
|
|
47
58
|
|
|
59
|
+
if not check_for_event_without_consumption(
|
|
60
|
+
state_machine.events.api_requests.start_mission.input
|
|
61
|
+
):
|
|
62
|
+
state_machine.current_mission.status = MissionStatus.Cancelled
|
|
63
|
+
|
|
64
|
+
for task in state_machine.current_mission.tasks:
|
|
65
|
+
if task.status in [
|
|
66
|
+
TaskStatus.NotStarted,
|
|
67
|
+
TaskStatus.InProgress,
|
|
68
|
+
TaskStatus.Paused,
|
|
69
|
+
]:
|
|
70
|
+
task.status = TaskStatus.Cancelled
|
|
71
|
+
|
|
72
|
+
stopped_mission_response: ControlMissionResponse = (
|
|
73
|
+
state_machine._make_control_mission_response()
|
|
74
|
+
)
|
|
75
|
+
state_machine.events.api_requests.stop_mission.output.put(
|
|
76
|
+
stopped_mission_response
|
|
77
|
+
)
|
|
78
|
+
|
|
48
79
|
state_machine._finalize()
|
|
49
80
|
return True
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def stop_return_home_mission_failed(state_machine: "StateMachine") -> bool:
|
|
84
|
+
if check_for_event_without_consumption(
|
|
85
|
+
state_machine.events.api_requests.start_mission.input
|
|
86
|
+
):
|
|
87
|
+
return True
|
|
88
|
+
stopped_mission_response: ControlMissionResponse = (
|
|
89
|
+
state_machine._make_control_mission_response()
|
|
90
|
+
)
|
|
91
|
+
state_machine.events.api_requests.stop_mission.output.put(stopped_mission_response)
|
|
92
|
+
return True
|
|
@@ -15,7 +15,9 @@ from isar.state_machine.transitions.functions.start_mission import (
|
|
|
15
15
|
)
|
|
16
16
|
from isar.state_machine.transitions.functions.stop import (
|
|
17
17
|
stop_mission_cleanup,
|
|
18
|
+
stop_mission_failed,
|
|
18
19
|
stop_return_home_mission_cleanup,
|
|
20
|
+
stop_return_home_mission_failed,
|
|
19
21
|
trigger_stop_mission_event,
|
|
20
22
|
)
|
|
21
23
|
from isar.state_machine.transitions.functions.utils import def_transition
|
|
@@ -56,6 +58,18 @@ def get_mission_transitions(state_machine: "StateMachine") -> List[dict]:
|
|
|
56
58
|
"dest": state_machine.await_next_mission_state,
|
|
57
59
|
"before": def_transition(state_machine, stop_mission_cleanup),
|
|
58
60
|
},
|
|
61
|
+
{
|
|
62
|
+
"trigger": "mission_stopping_failed",
|
|
63
|
+
"source": state_machine.stopping_state,
|
|
64
|
+
"dest": state_machine.monitor_state,
|
|
65
|
+
"before": def_transition(state_machine, stop_mission_failed),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"trigger": "return_home_mission_stopping_failed",
|
|
69
|
+
"source": state_machine.stopping_state,
|
|
70
|
+
"dest": state_machine.returning_home_state,
|
|
71
|
+
"before": def_transition(state_machine, stop_return_home_mission_failed),
|
|
72
|
+
},
|
|
59
73
|
{
|
|
60
74
|
"trigger": "return_home_mission_stopped",
|
|
61
75
|
"source": state_machine.stopping_state,
|
|
@@ -16,6 +16,7 @@ from isar.state_machine.states_enum import States
|
|
|
16
16
|
from tests.test_double.mission_definition import DummyMissionDefinition
|
|
17
17
|
|
|
18
18
|
dummy_mission = DummyMissionDefinition.default_mission
|
|
19
|
+
dummy_mission_stopped = DummyMissionDefinition.stopped_mission
|
|
19
20
|
|
|
20
21
|
mock_return_unknown_status = mock.Mock(return_value=States.UnknownStatus)
|
|
21
22
|
mock_return_robot_standing_still = mock.Mock(return_value=States.RobotStandingStill)
|
|
@@ -34,9 +35,18 @@ dummy_control_mission_response = ControlMissionResponse(
|
|
|
34
35
|
task_id=dummy_task.id,
|
|
35
36
|
task_status=dummy_task.status,
|
|
36
37
|
)
|
|
38
|
+
dummy_stopped_control_mission_response = ControlMissionResponse(
|
|
39
|
+
mission_id=dummy_mission_stopped.id,
|
|
40
|
+
mission_status=dummy_mission_stopped.status,
|
|
41
|
+
task_id=dummy_mission_stopped.tasks[0].id,
|
|
42
|
+
task_status=dummy_mission_stopped.tasks[0].status,
|
|
43
|
+
)
|
|
37
44
|
mock_return_control_mission_response = mock.Mock(
|
|
38
45
|
return_value=dummy_control_mission_response
|
|
39
46
|
)
|
|
47
|
+
mock_return_stopped_control_mission_response = mock.Mock(
|
|
48
|
+
return_value=dummy_stopped_control_mission_response
|
|
49
|
+
)
|
|
40
50
|
mock_queue_timeout_error = mock.Mock(side_effect=QueueTimeoutError)
|
|
41
51
|
mock_mission_planner_error = mock.Mock(side_effect=MissionPlannerError)
|
|
42
52
|
|
|
@@ -255,7 +265,9 @@ class TestStopMission:
|
|
|
255
265
|
|
|
256
266
|
@pytest.mark.parametrize("state", valid_states)
|
|
257
267
|
@mock.patch.object(
|
|
258
|
-
SchedulingUtilities,
|
|
268
|
+
SchedulingUtilities,
|
|
269
|
+
"_send_command",
|
|
270
|
+
mock_return_stopped_control_mission_response,
|
|
259
271
|
)
|
|
260
272
|
def test_stop_mission(
|
|
261
273
|
self, client: TestClient, state: States, mocker: MockerFixture
|
|
@@ -263,7 +275,9 @@ class TestStopMission:
|
|
|
263
275
|
mocker.patch.object(SchedulingUtilities, "get_state", return_value=state)
|
|
264
276
|
response = client.post(url=self.schedule_stop_mission_path)
|
|
265
277
|
assert response.status_code == HTTPStatus.OK
|
|
266
|
-
assert response.json() == jsonable_encoder(
|
|
278
|
+
assert response.json() == jsonable_encoder(
|
|
279
|
+
dummy_stopped_control_mission_response
|
|
280
|
+
)
|
|
267
281
|
|
|
268
282
|
@mock.patch.object(SchedulingUtilities, "get_state", mock_return_unknown_status)
|
|
269
283
|
@mock.patch.object(
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import time
|
|
2
2
|
from collections import deque
|
|
3
|
+
from http import HTTPStatus
|
|
3
4
|
from threading import Thread
|
|
4
5
|
from typing import List
|
|
5
6
|
|
|
6
7
|
import pytest
|
|
8
|
+
from fastapi import HTTPException
|
|
7
9
|
from pytest_mock import MockerFixture
|
|
8
10
|
|
|
11
|
+
from isar.models.communication.queues.queue_utils import (
|
|
12
|
+
check_for_event_without_consumption,
|
|
13
|
+
)
|
|
9
14
|
from isar.modules import ApplicationContainer
|
|
10
15
|
from isar.robot.robot import Robot
|
|
11
16
|
from isar.robot.robot_status import RobotStatusThread
|
|
@@ -122,7 +127,6 @@ def test_state_machine_failed_dependency(
|
|
|
122
127
|
robot_service_thread: RobotServiceThreadMock,
|
|
123
128
|
mocker,
|
|
124
129
|
) -> None:
|
|
125
|
-
|
|
126
130
|
state_machine_thread.state_machine.await_next_mission_state.return_home_delay = 0.1
|
|
127
131
|
|
|
128
132
|
mocker.patch.object(StubRobot, "task_status", return_value=TaskStatus.Failed)
|
|
@@ -250,7 +254,6 @@ def test_state_machine_with_successful_mission_stop(
|
|
|
250
254
|
uploader_thread: UploaderThreadMock,
|
|
251
255
|
mocker,
|
|
252
256
|
) -> None:
|
|
253
|
-
|
|
254
257
|
mocker.patch.object(StubRobot, "robot_status", return_value=RobotStatus.Home)
|
|
255
258
|
mocker.patch.object(StubRobot, "task_status", return_value=TaskStatus.InProgress)
|
|
256
259
|
|
|
@@ -303,19 +306,19 @@ def test_state_machine_with_unsuccessful_mission_stop(
|
|
|
303
306
|
|
|
304
307
|
state_machine_thread.state_machine.sleep_time = 0
|
|
305
308
|
|
|
306
|
-
state_machine_thread.state_machine.await_next_mission_state.return_home_delay = 10 # Return home delay is set to 10 seconds to avoid the state machine triggering return home within the test duration
|
|
307
309
|
state_machine_thread.start()
|
|
308
310
|
robot_service_thread.start()
|
|
309
311
|
|
|
310
312
|
scheduling_utilities.start_mission(mission=mission)
|
|
311
313
|
time.sleep(1)
|
|
312
|
-
|
|
313
|
-
|
|
314
|
+
with pytest.raises(HTTPException) as exception_details:
|
|
315
|
+
scheduling_utilities.stop_mission()
|
|
314
316
|
|
|
315
317
|
expected_log = (
|
|
316
318
|
"Be aware that the robot may still be "
|
|
317
319
|
"moving even though a stop has been attempted"
|
|
318
320
|
)
|
|
321
|
+
assert exception_details.value.status_code == HTTPStatus.SERVICE_UNAVAILABLE.value
|
|
319
322
|
assert expected_log in caplog.text
|
|
320
323
|
assert state_machine_thread.state_machine.transitions_list == deque(
|
|
321
324
|
[
|
|
@@ -323,11 +326,114 @@ def test_state_machine_with_unsuccessful_mission_stop(
|
|
|
323
326
|
States.RobotStandingStill,
|
|
324
327
|
States.Monitor,
|
|
325
328
|
States.Stopping,
|
|
326
|
-
States.
|
|
329
|
+
States.Monitor,
|
|
330
|
+
]
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def test_state_machine_with_unsuccessful_return_home_stop(
|
|
335
|
+
container: ApplicationContainer,
|
|
336
|
+
mocker: MockerFixture,
|
|
337
|
+
state_machine_thread: StateMachineThreadMock,
|
|
338
|
+
caplog: pytest.LogCaptureFixture,
|
|
339
|
+
robot_service_thread: RobotServiceThreadMock,
|
|
340
|
+
) -> None:
|
|
341
|
+
scheduling_utilities: SchedulingUtilities = container.scheduling_utilities()
|
|
342
|
+
mocker.patch.object(StubRobot, "task_status", return_value=TaskStatus.InProgress)
|
|
343
|
+
mocker.patch.object(
|
|
344
|
+
StubRobot, "stop", side_effect=_mock_robot_exception_with_message
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
state_machine_thread.state_machine.sleep_time = 0
|
|
348
|
+
|
|
349
|
+
state_machine_thread.start()
|
|
350
|
+
robot_service_thread.start()
|
|
351
|
+
|
|
352
|
+
scheduling_utilities.return_home()
|
|
353
|
+
time.sleep(1)
|
|
354
|
+
with pytest.raises(HTTPException) as exception_details:
|
|
355
|
+
scheduling_utilities.stop_mission()
|
|
356
|
+
|
|
357
|
+
expected_log = (
|
|
358
|
+
"Be aware that the robot may still be "
|
|
359
|
+
"moving even though a stop has been attempted"
|
|
360
|
+
)
|
|
361
|
+
assert exception_details.value.status_code == HTTPStatus.SERVICE_UNAVAILABLE.value
|
|
362
|
+
assert expected_log in caplog.text
|
|
363
|
+
assert state_machine_thread.state_machine.transitions_list == deque(
|
|
364
|
+
[
|
|
365
|
+
States.UnknownStatus,
|
|
366
|
+
States.RobotStandingStill,
|
|
367
|
+
States.ReturningHome,
|
|
368
|
+
States.Stopping,
|
|
369
|
+
States.ReturningHome,
|
|
370
|
+
]
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def test_state_machine_with_successful_return_home_stop(
|
|
375
|
+
container: ApplicationContainer,
|
|
376
|
+
mocker: MockerFixture,
|
|
377
|
+
state_machine_thread: StateMachineThreadMock,
|
|
378
|
+
robot_service_thread: RobotServiceThreadMock,
|
|
379
|
+
) -> None:
|
|
380
|
+
scheduling_utilities: SchedulingUtilities = container.scheduling_utilities()
|
|
381
|
+
mocker.patch.object(StubRobot, "task_status", return_value=TaskStatus.InProgress)
|
|
382
|
+
|
|
383
|
+
state_machine_thread.state_machine.sleep_time = 0
|
|
384
|
+
|
|
385
|
+
state_machine_thread.start()
|
|
386
|
+
robot_service_thread.start()
|
|
387
|
+
|
|
388
|
+
scheduling_utilities.return_home()
|
|
389
|
+
time.sleep(1)
|
|
390
|
+
scheduling_utilities.stop_mission()
|
|
391
|
+
|
|
392
|
+
assert state_machine_thread.state_machine.transitions_list == deque(
|
|
393
|
+
[
|
|
394
|
+
States.UnknownStatus,
|
|
395
|
+
States.RobotStandingStill,
|
|
396
|
+
States.ReturningHome,
|
|
397
|
+
States.Stopping,
|
|
398
|
+
States.RobotStandingStill,
|
|
327
399
|
]
|
|
328
400
|
)
|
|
329
401
|
|
|
330
402
|
|
|
403
|
+
def test_state_machine_with_mission_start_during_return_home_without_queueing_stop_response(
|
|
404
|
+
container: ApplicationContainer,
|
|
405
|
+
mocker: MockerFixture,
|
|
406
|
+
state_machine_thread: StateMachineThreadMock,
|
|
407
|
+
robot_service_thread: RobotServiceThreadMock,
|
|
408
|
+
) -> None:
|
|
409
|
+
mission: Mission = Mission(name="Dummy misson", tasks=[StubTask.take_image()])
|
|
410
|
+
scheduling_utilities: SchedulingUtilities = container.scheduling_utilities()
|
|
411
|
+
mocker.patch.object(StubRobot, "task_status", return_value=TaskStatus.InProgress)
|
|
412
|
+
|
|
413
|
+
state_machine_thread.state_machine.sleep_time = 0
|
|
414
|
+
|
|
415
|
+
state_machine_thread.start()
|
|
416
|
+
robot_service_thread.start()
|
|
417
|
+
|
|
418
|
+
scheduling_utilities.return_home()
|
|
419
|
+
time.sleep(1)
|
|
420
|
+
scheduling_utilities.start_mission(mission=mission)
|
|
421
|
+
|
|
422
|
+
assert state_machine_thread.state_machine.transitions_list == deque(
|
|
423
|
+
[
|
|
424
|
+
States.UnknownStatus,
|
|
425
|
+
States.RobotStandingStill,
|
|
426
|
+
States.ReturningHome,
|
|
427
|
+
States.Stopping,
|
|
428
|
+
States.RobotStandingStill,
|
|
429
|
+
States.Monitor,
|
|
430
|
+
]
|
|
431
|
+
)
|
|
432
|
+
assert not check_for_event_without_consumption(
|
|
433
|
+
state_machine_thread.state_machine.events.api_requests.start_mission.input
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
|
|
331
437
|
def test_state_machine_offline_to_robot_standing_still(
|
|
332
438
|
state_machine_thread, robot_service_thread, mocker
|
|
333
439
|
) -> None:
|
|
@@ -12,6 +12,7 @@ from isar.apis.models.start_mission_definition import (
|
|
|
12
12
|
StartMissionTaskDefinition,
|
|
13
13
|
)
|
|
14
14
|
from robot_interface.models.mission.mission import Mission
|
|
15
|
+
from robot_interface.models.mission.status import MissionStatus, TaskStatus
|
|
15
16
|
from tests.test_double.task import StubTask
|
|
16
17
|
|
|
17
18
|
|
|
@@ -32,6 +33,15 @@ class DummyMissionDefinition:
|
|
|
32
33
|
dummy_task_take_image,
|
|
33
34
|
],
|
|
34
35
|
)
|
|
36
|
+
dummy_task_take_image_cancelled = StubTask.take_image(status=TaskStatus.Cancelled)
|
|
37
|
+
stopped_mission = Mission(
|
|
38
|
+
id="default_mission",
|
|
39
|
+
name="Dummy misson",
|
|
40
|
+
tasks=[
|
|
41
|
+
dummy_task_take_image_cancelled,
|
|
42
|
+
],
|
|
43
|
+
status=MissionStatus.Cancelled,
|
|
44
|
+
)
|
|
35
45
|
dummy_start_mission_inspection_definition = StartMissionInspectionDefinition(
|
|
36
46
|
type=InspectionTypes.image,
|
|
37
47
|
inspection_target=dummy_input_target_position,
|