isar 1.33.5__tar.gz → 1.33.7__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.5 → isar-1.33.7}/PKG-INFO +1 -1
- isar-1.33.7/docs/full_state_machine_diagram.png +0 -0
- isar-1.33.7/docs/mission_state_machine_diagram.png +0 -0
- isar-1.33.7/docs/robot_status_state_machine_diagram.png +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/apis/api.py +34 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/apis/models/models.py +5 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/apis/schedule/scheduling_controller.py +34 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/settings.py +4 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/models/events.py +19 -1
- {isar-1.33.5 → isar-1.33.7}/src/isar/robot/robot.py +50 -1
- isar-1.33.5/src/isar/robot/robot_status.py → isar-1.33.7/src/isar/robot/robot_battery.py +17 -15
- isar-1.33.7/src/isar/robot/robot_pause_mission.py +63 -0
- isar-1.33.7/src/isar/robot/robot_status.py +71 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/robot/robot_stop_mission.py +1 -1
- {isar-1.33.5 → isar-1.33.7}/src/isar/services/utilities/scheduling_utilities.py +50 -4
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/state_machine.py +27 -7
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/await_next_mission.py +19 -1
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/blocked_protective_stop.py +9 -9
- isar-1.33.7/src/isar/state_machine/states/going_to_lockdown.py +80 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/home.py +27 -4
- isar-1.33.7/src/isar/state_machine/states/lockdown.py +37 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/monitor.py +16 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/offline.py +9 -10
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/paused.py +19 -1
- isar-1.33.7/src/isar/state_machine/states/pausing.py +74 -0
- isar-1.33.7/src/isar/state_machine/states/pausing_return_home.py +74 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/recharging.py +16 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/return_home_paused.py +17 -1
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/returning_home.py +18 -2
- isar-1.33.7/src/isar/state_machine/states/stopping_go_to_lockdown.py +79 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states_enum.py +5 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/transitions/functions/fail_mission.py +7 -0
- isar-1.33.7/src/isar/state_machine/transitions/functions/pause.py +31 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/transitions/functions/robot_status.py +15 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/transitions/mission.py +55 -10
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/transitions/return_home.py +62 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/utils/common_event_handlers.py +5 -2
- {isar-1.33.5 → isar-1.33.7}/src/isar.egg-info/PKG-INFO +1 -1
- {isar-1.33.5 → isar-1.33.7}/src/isar.egg-info/SOURCES.txt +7 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/mission/status.py +2 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/state_machine/test_state_machine.py +478 -4
- {isar-1.33.5 → isar-1.33.7}/tests/test_double/robot_interface.py +38 -1
- isar-1.33.5/docs/full_state_machine_diagram.png +0 -0
- isar-1.33.5/docs/mission_state_machine_diagram.png +0 -0
- isar-1.33.5/docs/robot_status_state_machine_diagram.png +0 -0
- isar-1.33.5/src/isar/state_machine/transitions/functions/pause.py +0 -91
- {isar-1.33.5 → isar-1.33.7}/.dockerignore +0 -0
- {isar-1.33.5 → isar-1.33.7}/.env.test +0 -0
- {isar-1.33.5 → isar-1.33.7}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {isar-1.33.5 → isar-1.33.7}/.github/ISSUE_TEMPLATE/feature.md +0 -0
- {isar-1.33.5 → isar-1.33.7}/.github/ISSUE_TEMPLATE/improvement.md +0 -0
- {isar-1.33.5 → isar-1.33.7}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {isar-1.33.5 → isar-1.33.7}/.github/release.yml +0 -0
- {isar-1.33.5 → isar-1.33.7}/.github/workflows/compile_requirements.yml +0 -0
- {isar-1.33.5 → isar-1.33.7}/.github/workflows/project_automations.yml +0 -0
- {isar-1.33.5 → isar-1.33.7}/.github/workflows/pythonpackage.yml +0 -0
- {isar-1.33.5 → isar-1.33.7}/.github/workflows/pythonpublish.yml +0 -0
- {isar-1.33.5 → isar-1.33.7}/.github/workflows/stale.yml +0 -0
- {isar-1.33.5 → isar-1.33.7}/.gitignore +0 -0
- {isar-1.33.5 → isar-1.33.7}/.pre-commit-config.yaml +0 -0
- {isar-1.33.5 → isar-1.33.7}/LICENSE +0 -0
- {isar-1.33.5 → isar-1.33.7}/README.md +0 -0
- {isar-1.33.5 → isar-1.33.7}/SECURITY.md +0 -0
- {isar-1.33.5 → isar-1.33.7}/docs/Makefile +0 -0
- {isar-1.33.5 → isar-1.33.7}/docs/make.bat +0 -0
- {isar-1.33.5 → isar-1.33.7}/docs/rst_processing.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/docs/source/conf.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/docs/source/index.rst +0 -0
- {isar-1.33.5 → isar-1.33.7}/docs/source/readme_link.md +0 -0
- {isar-1.33.5 → isar-1.33.7}/docs/update_state_diagram.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/main.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/pyproject.toml +0 -0
- {isar-1.33.5 → isar-1.33.7}/radixconfig.yml +0 -0
- {isar-1.33.5 → isar-1.33.7}/requirements.txt +0 -0
- {isar-1.33.5 → isar-1.33.7}/setup.cfg +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/apis/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/apis/models/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/apis/models/start_mission_definition.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/apis/robot_control/robot_controller.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/apis/schedule/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/apis/security/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/apis/security/authentication.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/certs/ca-cert.pem +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/configuration_error.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/keyvault/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/keyvault/keyvault_error.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/keyvault/keyvault_service.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/log.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/logging.conf +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/maps/JSP1_intermediate_deck.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/maps/JSP1_weather_deck.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/maps/default_map.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/maps/klab_b.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/maps/klab_compressor.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/maps/klab_turtlebot.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/maps/turtleworld.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/open_telemetry.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/predefined_mission_definition/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/predefined_mission_definition/default_exr.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/predefined_mission_definition/default_mission.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/predefined_mission_definition/default_turtlebot.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/predefined_missions/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/predefined_missions/default.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/config/predefined_missions/default_turtlebot.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/eventhandlers/eventhandler.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/mission_planner/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/mission_planner/local_planner.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/mission_planner/mission_planner_interface.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/mission_planner/sequential_task_selector.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/mission_planner/task_selector_interface.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/models/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/modules.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/robot/robot_start_mission.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/robot/robot_task_status.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/script.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/services/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/services/auth/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/services/auth/azure_credentials.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/services/service_connections/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/services/service_connections/mqtt/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/services/service_connections/mqtt/mqtt_client.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/services/service_connections/mqtt/robot_info_publisher.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/services/service_connections/request_handler.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/services/utilities/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/services/utilities/robot_utilities.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/services/utilities/threaded_request.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/intervention_needed.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/stopping.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/stopping_return_home.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/states/unknown_status.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/transitions/functions/finish_mission.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/transitions/functions/resume.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/transitions/functions/return_home.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/transitions/functions/start_mission.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/transitions/functions/stop.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/transitions/functions/utils.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/state_machine/transitions/robot_status.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/storage/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/storage/blob_storage.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/storage/local_storage.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/storage/storage_interface.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/storage/uploader.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar/storage/utilities.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar.egg-info/dependency_links.txt +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar.egg-info/entry_points.txt +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar.egg-info/requires.txt +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/isar.egg-info/top_level.txt +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/exceptions/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/exceptions/robot_exceptions.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/initialize/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/inspection/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/inspection/inspection.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/mission/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/mission/mission.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/mission/task.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/robots/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/robots/battery_state.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/robots/media.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/models/robots/robot_model.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/robot_interface.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/telemetry/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/telemetry/mqtt_client.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/telemetry/payloads.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/test_robot_interface.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/utilities/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/utilities/json_service.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/src/robot_interface/utilities/uuid_string_factory.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/conftest.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/integration/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/integration/turtlebot/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/integration/turtlebot/config/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/integration/turtlebot/config/maps/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/integration/turtlebot/config/maps/turtleworld.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/integration/turtlebot/config/missions/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/integration/turtlebot/config/missions/default.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/integration/turtlebot/test_successful_mission.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/apis/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/apis/models/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/apis/models/example_mission_definition.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/apis/models/test_start_mission_definition.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/apis/scheduler/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/apis/scheduler/test_scheduler_router.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/apis/security/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/apis/security/test_authentication.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/mission/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/mission/test_mission.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/models/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/models/communication/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/models/communication/test_events.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/services/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/services/readers/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/services/readers/test_mission_reader.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/services/service_connections/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/services/service_connections/echo/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/services/service_connections/test_base_request_handler.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/services/utilities/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/services/utilities/test_queue_utilities.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/services/utilities/test_scheduling_utilities.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/state_machine/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/state_machine/states/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/state_machine/states/test_monitor.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/storage/test_blob_storage.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/isar/storage/test_uploader.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_data/test_map_config/test_map_config.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_data/test_mission_not_working.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_data/test_mission_working.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_data/test_mission_working_no_tasks.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_data/test_thermal_image_mission.json +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_double/__init__.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_double/blob_storage.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_double/mission_definition.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_double/mqtt_client.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_double/pose.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_double/request.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_double/status.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_double/task.py +0 -0
- {isar-1.33.5 → isar-1.33.7}/tests/test_double/token.py +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -245,6 +245,40 @@ class API:
|
|
|
245
245
|
},
|
|
246
246
|
},
|
|
247
247
|
)
|
|
248
|
+
router.add_api_route(
|
|
249
|
+
path="/schedule/lockdown",
|
|
250
|
+
endpoint=self.scheduling_controller.lockdown,
|
|
251
|
+
methods=["POST"],
|
|
252
|
+
dependencies=[authentication_dependency],
|
|
253
|
+
summary="Send the robot to dock and ensure that it stays there",
|
|
254
|
+
responses={
|
|
255
|
+
HTTPStatus.OK.value: {"description": "Robot going to dock"},
|
|
256
|
+
HTTPStatus.CONFLICT.value: {
|
|
257
|
+
"description": "Conflict - Invalid command in the current state"
|
|
258
|
+
},
|
|
259
|
+
HTTPStatus.INTERNAL_SERVER_ERROR.value: {
|
|
260
|
+
"description": "Internal Server Error - Current state of state machine unknown"
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
)
|
|
264
|
+
router.add_api_route(
|
|
265
|
+
path="/schedule/release-lockdown",
|
|
266
|
+
endpoint=self.scheduling_controller.release_lockdown,
|
|
267
|
+
methods=["POST"],
|
|
268
|
+
dependencies=[authentication_dependency],
|
|
269
|
+
summary="Allow the robot to start missions again",
|
|
270
|
+
responses={
|
|
271
|
+
HTTPStatus.OK.value: {
|
|
272
|
+
"description": "Robot is able to receive missions again"
|
|
273
|
+
},
|
|
274
|
+
HTTPStatus.CONFLICT.value: {
|
|
275
|
+
"description": "Conflict - Invalid command in the current state"
|
|
276
|
+
},
|
|
277
|
+
HTTPStatus.INTERNAL_SERVER_ERROR.value: {
|
|
278
|
+
"description": "Internal Server Error - Current state of state machine unknown"
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
)
|
|
248
282
|
router.add_api_route(
|
|
249
283
|
path="/schedule/release-intervention-needed",
|
|
250
284
|
endpoint=self.scheduling_controller.release_intervention_needed,
|
|
@@ -32,6 +32,11 @@ class MissionStartResponse(BaseModel):
|
|
|
32
32
|
mission_not_started_reason: Optional[str] = None
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
class LockdownResponse(BaseModel):
|
|
36
|
+
lockdown_started: bool
|
|
37
|
+
failure_reason: Optional[str] = None
|
|
38
|
+
|
|
39
|
+
|
|
35
40
|
class RobotInfoResponse(BaseModel):
|
|
36
41
|
robot_package: str
|
|
37
42
|
isar_id: str
|
|
@@ -254,6 +254,40 @@ class SchedulingController:
|
|
|
254
254
|
self.scheduling_utilities.release_intervention_needed()
|
|
255
255
|
self.logger.info("Released intervention needed state successfully")
|
|
256
256
|
|
|
257
|
+
@tracer.start_as_current_span("lockdown")
|
|
258
|
+
def lockdown(self) -> None:
|
|
259
|
+
self.logger.info("Received request to lockdown robot")
|
|
260
|
+
|
|
261
|
+
state: States = self.scheduling_utilities.get_state()
|
|
262
|
+
|
|
263
|
+
if state == States.Lockdown:
|
|
264
|
+
error_message = "Conflict - Lockdown command received in lockdown state"
|
|
265
|
+
self.logger.warning(error_message)
|
|
266
|
+
raise HTTPException(
|
|
267
|
+
status_code=HTTPStatus.CONFLICT,
|
|
268
|
+
detail=error_message,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
self.scheduling_utilities.lock_down_robot()
|
|
272
|
+
self.logger.info("Lockdown started successfully")
|
|
273
|
+
|
|
274
|
+
@tracer.start_as_current_span("release_lockdown")
|
|
275
|
+
def release_lockdown(self) -> None:
|
|
276
|
+
self.logger.info("Received request to release robot lockdown")
|
|
277
|
+
|
|
278
|
+
state: States = self.scheduling_utilities.get_state()
|
|
279
|
+
|
|
280
|
+
if state != States.Lockdown:
|
|
281
|
+
error_message = f"Conflict - Release lockdown command received in invalid state - State: {state}"
|
|
282
|
+
self.logger.warning(error_message)
|
|
283
|
+
raise HTTPException(
|
|
284
|
+
status_code=HTTPStatus.CONFLICT,
|
|
285
|
+
detail=error_message,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
self.scheduling_utilities.release_robot_lockdown()
|
|
289
|
+
self.logger.info("Released lockdown successfully")
|
|
290
|
+
|
|
257
291
|
def _api_response(self, mission: Mission) -> StartMissionResponse:
|
|
258
292
|
return StartMissionResponse(
|
|
259
293
|
id=mission.id,
|
|
@@ -59,6 +59,9 @@ class Settings(BaseSettings):
|
|
|
59
59
|
# issues
|
|
60
60
|
REQUEST_STATUS_COMMUNICATION_RECONNECT_DELAY: float = Field(default=10)
|
|
61
61
|
|
|
62
|
+
# Time allowed to clear the robot status before giving up
|
|
63
|
+
CLEAR_ROBOT_STATUS_TIMEOUT: int = Field(default=3)
|
|
64
|
+
|
|
62
65
|
# Number of attempts for state transitions resume and pause if failed
|
|
63
66
|
STATE_TRANSITION_NUM_RETIRES: int = Field(default=10)
|
|
64
67
|
|
|
@@ -78,6 +81,7 @@ class Settings(BaseSettings):
|
|
|
78
81
|
ROBOT_STATUS_PUBLISH_INTERVAL: float = Field(default=1)
|
|
79
82
|
ROBOT_HEARTBEAT_PUBLISH_INTERVAL: float = Field(default=1)
|
|
80
83
|
ROBOT_INFO_PUBLISH_INTERVAL: float = Field(default=5)
|
|
84
|
+
ROBOT_API_BATTERY_POLL_INTERVAL: float = Field(default=5)
|
|
81
85
|
ROBOT_API_STATUS_POLL_INTERVAL: float = Field(default=5)
|
|
82
86
|
THREAD_CHECK_INTERVAL: float = Field(default=0.01)
|
|
83
87
|
|
|
@@ -4,7 +4,11 @@ from typing import Generic, Optional, TypeVar
|
|
|
4
4
|
|
|
5
5
|
from transitions import State
|
|
6
6
|
|
|
7
|
-
from isar.apis.models.models import
|
|
7
|
+
from isar.apis.models.models import (
|
|
8
|
+
ControlMissionResponse,
|
|
9
|
+
LockdownResponse,
|
|
10
|
+
MissionStartResponse,
|
|
11
|
+
)
|
|
8
12
|
from isar.config.settings import settings
|
|
9
13
|
from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
|
|
10
14
|
from robot_interface.models.mission.mission import Mission
|
|
@@ -105,6 +109,12 @@ class APIRequests:
|
|
|
105
109
|
self.release_intervention_needed: APIEvent[bool, bool] = APIEvent(
|
|
106
110
|
"release_intervention_needed"
|
|
107
111
|
)
|
|
112
|
+
self.send_to_lockdown: APIEvent[bool, LockdownResponse] = APIEvent(
|
|
113
|
+
"send_to_lockdown"
|
|
114
|
+
)
|
|
115
|
+
self.release_from_lockdown: APIEvent[bool, bool] = APIEvent(
|
|
116
|
+
"release_from_lockdown"
|
|
117
|
+
)
|
|
108
118
|
|
|
109
119
|
|
|
110
120
|
class StateMachineEvents:
|
|
@@ -113,6 +123,7 @@ class StateMachineEvents:
|
|
|
113
123
|
self.stop_mission: Event[bool] = Event("stop_mission")
|
|
114
124
|
self.pause_mission: Event[bool] = Event("pause_mission")
|
|
115
125
|
self.task_status_request: Event[str] = Event("task_status_request")
|
|
126
|
+
self.clear_robot_status: Event[bool] = Event("clear_robot_status")
|
|
116
127
|
|
|
117
128
|
|
|
118
129
|
class RobotServiceEvents:
|
|
@@ -122,12 +133,19 @@ class RobotServiceEvents:
|
|
|
122
133
|
self.mission_started: Event[bool] = Event("mission_started")
|
|
123
134
|
self.mission_failed: Event[ErrorMessage] = Event("mission_failed")
|
|
124
135
|
self.robot_status_changed: Event[bool] = Event("robot_status_changed")
|
|
136
|
+
self.robot_status_cleared: Event[bool] = Event("robot_status_cleared")
|
|
125
137
|
self.mission_failed_to_stop: Event[ErrorMessage] = Event(
|
|
126
138
|
"mission_failed_to_stop"
|
|
127
139
|
)
|
|
128
140
|
self.mission_successfully_stopped: Event[bool] = Event(
|
|
129
141
|
"mission_successfully_stopped"
|
|
130
142
|
)
|
|
143
|
+
self.mission_failed_to_pause: Event[ErrorMessage] = Event(
|
|
144
|
+
"mission_failed_to_pause"
|
|
145
|
+
)
|
|
146
|
+
self.mission_successfully_paused: Event[bool] = Event(
|
|
147
|
+
"mission_successfully_paused"
|
|
148
|
+
)
|
|
131
149
|
|
|
132
150
|
|
|
133
151
|
class SharedState:
|
|
@@ -9,6 +9,8 @@ from isar.models.events import (
|
|
|
9
9
|
SharedState,
|
|
10
10
|
StateMachineEvents,
|
|
11
11
|
)
|
|
12
|
+
from isar.robot.robot_battery import RobotBatteryThread
|
|
13
|
+
from isar.robot.robot_pause_mission import RobotPauseMissionThread
|
|
12
14
|
from isar.robot.robot_start_mission import RobotStartMissionThread
|
|
13
15
|
from isar.robot.robot_status import RobotStatusThread
|
|
14
16
|
from isar.robot.robot_stop_mission import RobotStopMissionThread
|
|
@@ -28,15 +30,22 @@ class Robot(object):
|
|
|
28
30
|
self.shared_state: SharedState = shared_state
|
|
29
31
|
self.robot: RobotInterface = robot
|
|
30
32
|
self.start_mission_thread: Optional[RobotStartMissionThread] = None
|
|
33
|
+
self.robot_battery_thread: Optional[RobotBatteryThread] = None
|
|
31
34
|
self.robot_status_thread: Optional[RobotStatusThread] = None
|
|
32
35
|
self.robot_task_status_thread: Optional[RobotTaskStatusThread] = None
|
|
33
36
|
self.stop_mission_thread: Optional[RobotStopMissionThread] = None
|
|
37
|
+
self.pause_mission_thread: Optional[RobotPauseMissionThread] = None
|
|
34
38
|
self.signal_thread_quitting: ThreadEvent = ThreadEvent()
|
|
35
39
|
|
|
36
40
|
def stop(self) -> None:
|
|
37
41
|
self.signal_thread_quitting.set()
|
|
38
42
|
if self.robot_status_thread is not None and self.robot_status_thread.is_alive():
|
|
39
43
|
self.robot_status_thread.join()
|
|
44
|
+
if (
|
|
45
|
+
self.robot_battery_thread is not None
|
|
46
|
+
and self.robot_battery_thread.is_alive()
|
|
47
|
+
):
|
|
48
|
+
self.robot_battery_thread.join()
|
|
40
49
|
if (
|
|
41
50
|
self.robot_task_status_thread is not None
|
|
42
51
|
and self.robot_task_status_thread.is_alive()
|
|
@@ -50,6 +59,7 @@ class Robot(object):
|
|
|
50
59
|
if self.stop_mission_thread is not None and self.stop_mission_thread.is_alive():
|
|
51
60
|
self.stop_mission_thread.join()
|
|
52
61
|
self.robot_status_thread = None
|
|
62
|
+
self.robot_battery_thread = None
|
|
53
63
|
self.robot_task_status_thread = None
|
|
54
64
|
self.start_mission_thread = None
|
|
55
65
|
|
|
@@ -111,12 +121,49 @@ class Robot(object):
|
|
|
111
121
|
)
|
|
112
122
|
self.stop_mission_thread.start()
|
|
113
123
|
|
|
124
|
+
def _pause_mission_request_handler(self, event: Event[bool]) -> None:
|
|
125
|
+
if event.consume_event():
|
|
126
|
+
if (
|
|
127
|
+
self.pause_mission_thread is not None
|
|
128
|
+
and self.pause_mission_thread.is_alive()
|
|
129
|
+
):
|
|
130
|
+
self.logger.warning(
|
|
131
|
+
"Received pause mission event while trying to pause a mission. Aborting pause attempt."
|
|
132
|
+
)
|
|
133
|
+
return
|
|
134
|
+
if (
|
|
135
|
+
self.start_mission_thread is not None
|
|
136
|
+
and self.start_mission_thread.is_alive()
|
|
137
|
+
):
|
|
138
|
+
error_description = "Received pause mission event while trying to start a mission. Aborting pause attempt."
|
|
139
|
+
error_message = ErrorMessage(
|
|
140
|
+
error_reason=ErrorReason.RobotStillStartingMissionException,
|
|
141
|
+
error_description=error_description,
|
|
142
|
+
)
|
|
143
|
+
self.robot_service_events.mission_failed_to_stop.trigger_event(
|
|
144
|
+
error_message
|
|
145
|
+
)
|
|
146
|
+
return
|
|
147
|
+
self.pause_mission_thread = RobotPauseMissionThread(
|
|
148
|
+
self.robot_service_events, self.robot, self.signal_thread_quitting
|
|
149
|
+
)
|
|
150
|
+
self.pause_mission_thread.start()
|
|
151
|
+
|
|
114
152
|
def run(self) -> None:
|
|
115
153
|
self.robot_status_thread = RobotStatusThread(
|
|
116
|
-
self.robot,
|
|
154
|
+
robot=self.robot,
|
|
155
|
+
signal_thread_quitting=self.signal_thread_quitting,
|
|
156
|
+
shared_state=self.shared_state,
|
|
157
|
+
state_machine_events=self.state_machine_events,
|
|
158
|
+
robot_service_events=self.robot_service_events,
|
|
117
159
|
)
|
|
118
160
|
self.robot_status_thread.start()
|
|
119
161
|
|
|
162
|
+
self.robot_battery_thread = RobotBatteryThread(
|
|
163
|
+
self.robot, self.signal_thread_quitting, self.shared_state
|
|
164
|
+
)
|
|
165
|
+
self.robot_battery_thread.start()
|
|
166
|
+
|
|
120
167
|
while not self.signal_thread_quitting.wait(0):
|
|
121
168
|
self._start_mission_event_handler(self.state_machine_events.start_mission)
|
|
122
169
|
|
|
@@ -124,6 +171,8 @@ class Robot(object):
|
|
|
124
171
|
self.state_machine_events.task_status_request
|
|
125
172
|
)
|
|
126
173
|
|
|
174
|
+
self._pause_mission_request_handler(self.state_machine_events.pause_mission)
|
|
175
|
+
|
|
127
176
|
self._stop_mission_request_handler(self.state_machine_events.stop_mission)
|
|
128
177
|
|
|
129
178
|
self.logger.info("Exiting robot service main thread")
|
|
@@ -8,7 +8,7 @@ from robot_interface.models.exceptions.robot_exceptions import RobotException
|
|
|
8
8
|
from robot_interface.robot_interface import RobotInterface
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class
|
|
11
|
+
class RobotBatteryThread(Thread):
|
|
12
12
|
def __init__(
|
|
13
13
|
self,
|
|
14
14
|
robot: RobotInterface,
|
|
@@ -19,20 +19,24 @@ class RobotStatusThread(Thread):
|
|
|
19
19
|
self.shared_state: SharedState = shared_state
|
|
20
20
|
self.robot: RobotInterface = robot
|
|
21
21
|
self.signal_thread_quitting: Event = signal_thread_quitting
|
|
22
|
-
self.
|
|
23
|
-
|
|
24
|
-
)
|
|
25
|
-
Thread.__init__(self, name="Robot status thread")
|
|
22
|
+
self.last_robot_battery_poll_time: float = time.time()
|
|
23
|
+
self.force_battery_poll_next_iteration: bool = True
|
|
24
|
+
Thread.__init__(self, name="Robot battery thread")
|
|
26
25
|
|
|
27
26
|
def stop(self) -> None:
|
|
28
27
|
return
|
|
29
28
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
def _is_ready_to_poll_for_battery(self) -> bool:
|
|
30
|
+
if self.force_battery_poll_next_iteration:
|
|
31
|
+
self.force_battery_poll_next_iteration = False
|
|
32
|
+
return True
|
|
33
|
+
|
|
34
|
+
time_since_last_robot_battery_poll = (
|
|
35
|
+
time.time() - self.last_robot_battery_poll_time
|
|
33
36
|
)
|
|
34
37
|
return (
|
|
35
|
-
|
|
38
|
+
time_since_last_robot_battery_poll
|
|
39
|
+
> settings.ROBOT_API_BATTERY_POLL_INTERVAL
|
|
36
40
|
)
|
|
37
41
|
|
|
38
42
|
def run(self):
|
|
@@ -42,17 +46,15 @@ class RobotStatusThread(Thread):
|
|
|
42
46
|
thread_check_interval = settings.THREAD_CHECK_INTERVAL
|
|
43
47
|
|
|
44
48
|
while not self.signal_thread_quitting.wait(thread_check_interval):
|
|
45
|
-
if not self.
|
|
49
|
+
if not self._is_ready_to_poll_for_battery():
|
|
46
50
|
continue
|
|
47
51
|
try:
|
|
48
|
-
self.
|
|
52
|
+
self.last_robot_battery_poll_time = time.time()
|
|
49
53
|
|
|
50
|
-
robot_status = self.robot.robot_status()
|
|
51
54
|
robot_battery_level = self.robot.get_battery_level()
|
|
52
55
|
|
|
53
|
-
self.shared_state.robot_status.update(robot_status)
|
|
54
56
|
self.shared_state.robot_battery_level.update(robot_battery_level)
|
|
55
57
|
except RobotException as e:
|
|
56
|
-
self.logger.error(f"Failed to retrieve robot
|
|
58
|
+
self.logger.error(f"Failed to retrieve robot battery level: {e}")
|
|
57
59
|
continue
|
|
58
|
-
self.logger.info("Exiting robot
|
|
60
|
+
self.logger.info("Exiting robot battery thread")
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
from threading import Event, Thread
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from isar.config.settings import settings
|
|
7
|
+
from isar.models.events import RobotServiceEvents
|
|
8
|
+
from robot_interface.models.exceptions.robot_exceptions import (
|
|
9
|
+
ErrorMessage,
|
|
10
|
+
RobotActionException,
|
|
11
|
+
RobotException,
|
|
12
|
+
)
|
|
13
|
+
from robot_interface.robot_interface import RobotInterface
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RobotPauseMissionThread(Thread):
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
robot_service_events: RobotServiceEvents,
|
|
20
|
+
robot: RobotInterface,
|
|
21
|
+
signal_thread_quitting: Event,
|
|
22
|
+
):
|
|
23
|
+
self.logger = logging.getLogger("robot")
|
|
24
|
+
self.robot_service_events: RobotServiceEvents = robot_service_events
|
|
25
|
+
self.robot: RobotInterface = robot
|
|
26
|
+
self.signal_thread_quitting: Event = signal_thread_quitting
|
|
27
|
+
Thread.__init__(self, name="Robot pause mission thread")
|
|
28
|
+
|
|
29
|
+
def run(self) -> None:
|
|
30
|
+
retries = 0
|
|
31
|
+
error: Optional[ErrorMessage] = None
|
|
32
|
+
while retries < settings.STATE_TRANSITION_NUM_RETIRES:
|
|
33
|
+
if self.signal_thread_quitting.wait(0):
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
self.robot.pause()
|
|
38
|
+
except (RobotActionException, RobotException) as e:
|
|
39
|
+
self.logger.warning(
|
|
40
|
+
f"\nFailed to pause robot because: {e.error_description}"
|
|
41
|
+
f"\nAttempting to pause the robot again"
|
|
42
|
+
)
|
|
43
|
+
retries += 1
|
|
44
|
+
error = ErrorMessage(
|
|
45
|
+
error_reason=e.error_reason, error_description=e.error_description
|
|
46
|
+
)
|
|
47
|
+
time.sleep(settings.FSM_SLEEP_TIME)
|
|
48
|
+
continue
|
|
49
|
+
self.robot_service_events.mission_successfully_paused.trigger_event(True)
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
error_description = (
|
|
53
|
+
f"\nFailed to pause the robot after {retries} attempts because: "
|
|
54
|
+
f"{error.error_description}"
|
|
55
|
+
f"\nBe aware that the robot may still be moving even though a pause has "
|
|
56
|
+
"been attempted"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
error_message = ErrorMessage(
|
|
60
|
+
error_reason=error.error_reason,
|
|
61
|
+
error_description=error_description,
|
|
62
|
+
)
|
|
63
|
+
self.robot_service_events.mission_failed_to_pause.trigger_event(error_message)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
from threading import Event, Thread
|
|
4
|
+
|
|
5
|
+
from isar.config.settings import settings
|
|
6
|
+
from isar.models.events import RobotServiceEvents, SharedState, StateMachineEvents
|
|
7
|
+
from robot_interface.models.exceptions.robot_exceptions import RobotException
|
|
8
|
+
from robot_interface.robot_interface import RobotInterface
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RobotStatusThread(Thread):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
robot: RobotInterface,
|
|
15
|
+
signal_thread_quitting: Event,
|
|
16
|
+
shared_state: SharedState,
|
|
17
|
+
robot_service_events: RobotServiceEvents,
|
|
18
|
+
state_machine_events: StateMachineEvents,
|
|
19
|
+
):
|
|
20
|
+
self.logger = logging.getLogger("robot")
|
|
21
|
+
self.shared_state: SharedState = shared_state
|
|
22
|
+
self.robot_service_events: RobotServiceEvents = robot_service_events
|
|
23
|
+
self.state_machine_events: StateMachineEvents = state_machine_events
|
|
24
|
+
self.robot: RobotInterface = robot
|
|
25
|
+
self.signal_thread_quitting: Event = signal_thread_quitting
|
|
26
|
+
self.robot_status_poll_interval: float = settings.ROBOT_API_STATUS_POLL_INTERVAL
|
|
27
|
+
self.last_robot_status_poll_time: float = time.time()
|
|
28
|
+
self.force_status_poll_next_iteration: bool = True
|
|
29
|
+
Thread.__init__(self, name="Robot status thread")
|
|
30
|
+
|
|
31
|
+
def stop(self) -> None:
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
def _is_ready_to_poll_for_status(self) -> bool:
|
|
35
|
+
if self.force_status_poll_next_iteration:
|
|
36
|
+
self.force_status_poll_next_iteration = False
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
time_since_last_robot_status_poll = (
|
|
40
|
+
time.time() - self.last_robot_status_poll_time
|
|
41
|
+
)
|
|
42
|
+
return time_since_last_robot_status_poll > self.robot_status_poll_interval
|
|
43
|
+
|
|
44
|
+
def run(self):
|
|
45
|
+
if self.signal_thread_quitting.is_set():
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
thread_check_interval = settings.THREAD_CHECK_INTERVAL
|
|
49
|
+
|
|
50
|
+
while not self.signal_thread_quitting.wait(thread_check_interval):
|
|
51
|
+
if self.state_machine_events.clear_robot_status.consume_event() is not None:
|
|
52
|
+
self.shared_state.robot_status.clear_event()
|
|
53
|
+
self.robot_service_events.robot_status_changed.clear_event()
|
|
54
|
+
self.robot_service_events.robot_status_cleared.trigger_event(True)
|
|
55
|
+
self.force_status_poll_next_iteration = True
|
|
56
|
+
|
|
57
|
+
if not self._is_ready_to_poll_for_status():
|
|
58
|
+
continue
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
self.last_robot_status_poll_time = time.time()
|
|
62
|
+
|
|
63
|
+
robot_status = self.robot.robot_status()
|
|
64
|
+
|
|
65
|
+
if robot_status is not self.shared_state.robot_status.check():
|
|
66
|
+
self.shared_state.robot_status.update(robot_status)
|
|
67
|
+
self.robot_service_events.robot_status_changed.trigger_event(True)
|
|
68
|
+
except RobotException as e:
|
|
69
|
+
self.logger.error(f"Failed to retrieve robot status: {e}")
|
|
70
|
+
continue
|
|
71
|
+
self.logger.info("Exiting robot status thread")
|
|
@@ -24,7 +24,7 @@ class RobotStopMissionThread(Thread):
|
|
|
24
24
|
self.robot_service_events: RobotServiceEvents = robot_service_events
|
|
25
25
|
self.robot: RobotInterface = robot
|
|
26
26
|
self.signal_thread_quitting: Event = signal_thread_quitting
|
|
27
|
-
Thread.__init__(self, name="Robot
|
|
27
|
+
Thread.__init__(self, name="Robot stop mission thread")
|
|
28
28
|
|
|
29
29
|
def run(self) -> None:
|
|
30
30
|
retries = 0
|
|
@@ -322,14 +322,60 @@ class SchedulingUtilities:
|
|
|
322
322
|
try:
|
|
323
323
|
self._send_command(True, self.api_events.release_intervention_needed)
|
|
324
324
|
self.logger.info("OK - Intervention needed state released")
|
|
325
|
+
except EventConflictError:
|
|
326
|
+
error_message = (
|
|
327
|
+
"Previous release intervention needed request is still being processed"
|
|
328
|
+
)
|
|
329
|
+
self.logger.warning(error_message)
|
|
330
|
+
raise HTTPException(status_code=HTTPStatus.CONFLICT, detail=error_message)
|
|
325
331
|
except EventTimeoutError:
|
|
332
|
+
error_message = "Cannot release intervention needed as it is not in intervention needed state"
|
|
333
|
+
self.logger.warning(error_message)
|
|
334
|
+
raise HTTPException(status_code=HTTPStatus.CONFLICT, detail=error_message)
|
|
335
|
+
|
|
336
|
+
def lock_down_robot(self) -> None:
|
|
337
|
+
"""Lock down robot
|
|
338
|
+
|
|
339
|
+
Raises
|
|
340
|
+
------
|
|
341
|
+
HTTPException 500 Internal Server Error
|
|
342
|
+
If the robot could not be locked down
|
|
343
|
+
"""
|
|
344
|
+
try:
|
|
345
|
+
self._send_command(True, self.api_events.send_to_lockdown)
|
|
346
|
+
self.logger.info("OK - Robot sent into lockdown")
|
|
347
|
+
except EventConflictError:
|
|
348
|
+
error_message = "Previous lockdown request is still being processed"
|
|
349
|
+
self.logger.warning(error_message)
|
|
350
|
+
raise HTTPException(status_code=HTTPStatus.CONFLICT, detail=error_message)
|
|
351
|
+
except EventTimeoutError:
|
|
352
|
+
error_message = "Cannot send robot to lockdown as it is already in lockdown"
|
|
353
|
+
self.logger.warning(error_message)
|
|
354
|
+
raise HTTPException(status_code=HTTPStatus.CONFLICT, detail=error_message)
|
|
355
|
+
|
|
356
|
+
def release_robot_lockdown(self) -> None:
|
|
357
|
+
"""Release robot from lockdown
|
|
358
|
+
|
|
359
|
+
Raises
|
|
360
|
+
------
|
|
361
|
+
HTTPException 500 Internal Server Error
|
|
362
|
+
If the robot could not be released from lockdown
|
|
363
|
+
"""
|
|
364
|
+
try:
|
|
365
|
+
self._send_command(True, self.api_events.release_from_lockdown)
|
|
366
|
+
self.logger.info("OK - Robot released form lockdown")
|
|
367
|
+
except EventConflictError:
|
|
326
368
|
error_message = (
|
|
327
|
-
"
|
|
369
|
+
"Previous release robot from lockdown request is still being processed"
|
|
328
370
|
)
|
|
329
|
-
self.logger.
|
|
330
|
-
raise HTTPException(
|
|
331
|
-
|
|
371
|
+
self.logger.warning(error_message)
|
|
372
|
+
raise HTTPException(status_code=HTTPStatus.CONFLICT, detail=error_message)
|
|
373
|
+
except EventTimeoutError:
|
|
374
|
+
error_message = (
|
|
375
|
+
"Cannot release robot from lockdown as it is not in lockdown"
|
|
332
376
|
)
|
|
377
|
+
self.logger.warning(error_message)
|
|
378
|
+
raise HTTPException(status_code=HTTPStatus.CONFLICT, detail=error_message)
|
|
333
379
|
|
|
334
380
|
def _send_command(self, input: T1, api_event: APIEvent[T1, T2]) -> T2:
|
|
335
381
|
if api_event.request.has_event():
|
|
@@ -18,15 +18,20 @@ from isar.models.events import Events, SharedState
|
|
|
18
18
|
from isar.services.service_connections.mqtt.mqtt_client import props_expiry
|
|
19
19
|
from isar.state_machine.states.await_next_mission import AwaitNextMission
|
|
20
20
|
from isar.state_machine.states.blocked_protective_stop import BlockedProtectiveStop
|
|
21
|
+
from isar.state_machine.states.going_to_lockdown import GoingToLockdown
|
|
21
22
|
from isar.state_machine.states.home import Home
|
|
22
23
|
from isar.state_machine.states.intervention_needed import InterventionNeeded
|
|
24
|
+
from isar.state_machine.states.lockdown import Lockdown
|
|
23
25
|
from isar.state_machine.states.monitor import Monitor
|
|
24
26
|
from isar.state_machine.states.offline import Offline
|
|
25
27
|
from isar.state_machine.states.paused import Paused
|
|
28
|
+
from isar.state_machine.states.pausing import Pausing
|
|
29
|
+
from isar.state_machine.states.pausing_return_home import PausingReturnHome
|
|
26
30
|
from isar.state_machine.states.recharging import Recharging
|
|
27
31
|
from isar.state_machine.states.return_home_paused import ReturnHomePaused
|
|
28
32
|
from isar.state_machine.states.returning_home import ReturningHome
|
|
29
33
|
from isar.state_machine.states.stopping import Stopping
|
|
34
|
+
from isar.state_machine.states.stopping_go_to_lockdown import StoppingGoToLockdown
|
|
30
35
|
from isar.state_machine.states.stopping_return_home import StoppingReturnHome
|
|
31
36
|
from isar.state_machine.states.unknown_status import UnknownStatus
|
|
32
37
|
from isar.state_machine.states_enum import States
|
|
@@ -102,8 +107,12 @@ class StateMachine(object):
|
|
|
102
107
|
self.returning_home_state: State = ReturningHome(self)
|
|
103
108
|
self.stopping_state: State = Stopping(self)
|
|
104
109
|
self.paused_state: State = Paused(self)
|
|
110
|
+
self.pausing_state: State = Pausing(self)
|
|
105
111
|
self.return_home_paused_state: State = ReturnHomePaused(self)
|
|
106
112
|
self.stopping_return_home_state: State = StoppingReturnHome(self)
|
|
113
|
+
self.pausing_return_home_state: State = PausingReturnHome(self)
|
|
114
|
+
self.stopping_go_to_lockdown_state: State = StoppingGoToLockdown(self)
|
|
115
|
+
self.going_to_lockdown_state: State = GoingToLockdown(self)
|
|
107
116
|
|
|
108
117
|
# States Waiting for mission
|
|
109
118
|
self.await_next_mission_state: State = AwaitNextMission(self)
|
|
@@ -114,6 +123,7 @@ class StateMachine(object):
|
|
|
114
123
|
self.offline_state: State = Offline(self)
|
|
115
124
|
self.blocked_protective_stopping_state: State = BlockedProtectiveStop(self)
|
|
116
125
|
self.recharging_state: State = Recharging(self)
|
|
126
|
+
self.lockdown_state: State = Lockdown(self)
|
|
117
127
|
|
|
118
128
|
# Error and special status states
|
|
119
129
|
self.unknown_status_state: State = UnknownStatus(self)
|
|
@@ -123,7 +133,9 @@ class StateMachine(object):
|
|
|
123
133
|
self.returning_home_state,
|
|
124
134
|
self.stopping_state,
|
|
125
135
|
self.stopping_return_home_state,
|
|
136
|
+
self.pausing_return_home_state,
|
|
126
137
|
self.paused_state,
|
|
138
|
+
self.pausing_state,
|
|
127
139
|
self.return_home_paused_state,
|
|
128
140
|
self.await_next_mission_state,
|
|
129
141
|
self.home_state,
|
|
@@ -132,6 +144,9 @@ class StateMachine(object):
|
|
|
132
144
|
self.unknown_status_state,
|
|
133
145
|
self.intervention_needed_state,
|
|
134
146
|
self.recharging_state,
|
|
147
|
+
self.stopping_go_to_lockdown_state,
|
|
148
|
+
self.going_to_lockdown_state,
|
|
149
|
+
self.lockdown_state,
|
|
135
150
|
]
|
|
136
151
|
|
|
137
152
|
self.machine = Machine(
|
|
@@ -288,13 +303,14 @@ class StateMachine(object):
|
|
|
288
303
|
timestamp=datetime.now(timezone.utc),
|
|
289
304
|
)
|
|
290
305
|
|
|
291
|
-
self.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
306
|
+
if self.current_mission:
|
|
307
|
+
self.mqtt_publisher.publish(
|
|
308
|
+
topic=settings.TOPIC_ISAR_MISSION + f"/{self.current_mission.id}",
|
|
309
|
+
payload=json.dumps(payload, cls=EnhancedJSONEncoder),
|
|
310
|
+
qos=1,
|
|
311
|
+
retain=True,
|
|
312
|
+
properties=props_expiry(settings.MQTT_MISSION_AND_TASK_EXPIRY),
|
|
313
|
+
)
|
|
298
314
|
|
|
299
315
|
def publish_task_status(self, task: TASKS) -> None:
|
|
300
316
|
"""Publishes the task status to the MQTT Broker"""
|
|
@@ -384,6 +400,10 @@ class StateMachine(object):
|
|
|
384
400
|
return RobotStatus.InterventionNeeded
|
|
385
401
|
elif self.current_state == States.Recharging:
|
|
386
402
|
return RobotStatus.Recharging
|
|
403
|
+
elif self.current_state == States.Lockdown:
|
|
404
|
+
return RobotStatus.Lockdown
|
|
405
|
+
elif self.current_state == States.GoingToLockdown:
|
|
406
|
+
return RobotStatus.GoingToLockdown
|
|
387
407
|
else:
|
|
388
408
|
return RobotStatus.Busy
|
|
389
409
|
|