isar 1.26.2__tar.gz → 1.26.3__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.

Files changed (212) hide show
  1. {isar-1.26.2 → isar-1.26.3}/PKG-INFO +2 -2
  2. {isar-1.26.2 → isar-1.26.3}/pyproject.toml +1 -1
  3. {isar-1.26.2 → isar-1.26.3}/requirements.txt +21 -16
  4. {isar-1.26.2 → isar-1.26.3}/src/isar/apis/api.py +4 -4
  5. {isar-1.26.2 → isar-1.26.3}/src/isar/apis/robot_control/robot_controller.py +1 -1
  6. {isar-1.26.2 → isar-1.26.3}/src/isar/apis/schedule/scheduling_controller.py +1 -1
  7. {isar-1.26.2 → isar-1.26.3}/src/isar/apis/security/authentication.py +2 -2
  8. {isar-1.26.2 → isar-1.26.3}/src/isar/config/log.py +2 -1
  9. {isar-1.26.2 → isar-1.26.3}/src/isar/mission_planner/local_planner.py +1 -1
  10. isar-1.26.3/src/isar/modules.py +138 -0
  11. {isar-1.26.2 → isar-1.26.3}/src/isar/robot/robot.py +3 -7
  12. {isar-1.26.2 → isar-1.26.3}/src/isar/robot/robot_start_mission.py +0 -1
  13. {isar-1.26.2 → isar-1.26.3}/src/isar/robot/robot_status.py +4 -5
  14. {isar-1.26.2 → isar-1.26.3}/src/isar/robot/robot_stop_mission.py +0 -1
  15. {isar-1.26.2 → isar-1.26.3}/src/isar/robot/robot_task_status.py +0 -1
  16. {isar-1.26.2 → isar-1.26.3}/src/isar/script.py +13 -16
  17. {isar-1.26.2 → isar-1.26.3}/src/isar/services/utilities/robot_utilities.py +1 -1
  18. {isar-1.26.2 → isar-1.26.3}/src/isar/services/utilities/scheduling_utilities.py +1 -1
  19. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/state_machine.py +8 -1
  20. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/states/blocked_protective_stop.py +9 -1
  21. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/states/idle.py +7 -0
  22. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/states/monitor.py +12 -1
  23. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/states/offline.py +7 -0
  24. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/states/paused.py +7 -0
  25. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/states/stop.py +6 -0
  26. {isar-1.26.2 → isar-1.26.3}/src/isar/storage/blob_storage.py +1 -1
  27. {isar-1.26.2 → isar-1.26.3}/src/isar/storage/slimm_storage.py +1 -1
  28. {isar-1.26.2 → isar-1.26.3}/src/isar/storage/uploader.py +8 -2
  29. {isar-1.26.2 → isar-1.26.3}/src/isar.egg-info/PKG-INFO +2 -2
  30. {isar-1.26.2 → isar-1.26.3}/src/isar.egg-info/SOURCES.txt +0 -1
  31. {isar-1.26.2 → isar-1.26.3}/src/isar.egg-info/requires.txt +1 -1
  32. isar-1.26.3/tests/conftest.py +194 -0
  33. {isar-1.26.2 → isar-1.26.3}/tests/integration/turtlebot/test_successful_mission.py +0 -70
  34. {isar-1.26.2 → isar-1.26.3}/tests/isar/state_machine/test_state_machine.py +81 -99
  35. {isar-1.26.2 → isar-1.26.3}/tests/isar/storage/test_uploader.py +28 -31
  36. isar-1.26.2/src/isar/modules.py +0 -247
  37. isar-1.26.2/tests/conftest.py +0 -150
  38. isar-1.26.2/tests/test_modules.py +0 -46
  39. {isar-1.26.2 → isar-1.26.3}/.dockerignore +0 -0
  40. {isar-1.26.2 → isar-1.26.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  41. {isar-1.26.2 → isar-1.26.3}/.github/ISSUE_TEMPLATE/feature.md +0 -0
  42. {isar-1.26.2 → isar-1.26.3}/.github/ISSUE_TEMPLATE/improvement.md +0 -0
  43. {isar-1.26.2 → isar-1.26.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  44. {isar-1.26.2 → isar-1.26.3}/.github/release.yml +0 -0
  45. {isar-1.26.2 → isar-1.26.3}/.github/workflows/compile_requirements.yml +0 -0
  46. {isar-1.26.2 → isar-1.26.3}/.github/workflows/project_automations.yml +0 -0
  47. {isar-1.26.2 → isar-1.26.3}/.github/workflows/pythonpackage.yml +0 -0
  48. {isar-1.26.2 → isar-1.26.3}/.github/workflows/pythonpublish.yml +0 -0
  49. {isar-1.26.2 → isar-1.26.3}/.github/workflows/stale.yml +0 -0
  50. {isar-1.26.2 → isar-1.26.3}/.gitignore +0 -0
  51. {isar-1.26.2 → isar-1.26.3}/.pre-commit-config.yaml +0 -0
  52. {isar-1.26.2 → isar-1.26.3}/LICENSE +0 -0
  53. {isar-1.26.2 → isar-1.26.3}/README.md +0 -0
  54. {isar-1.26.2 → isar-1.26.3}/SECURITY.md +0 -0
  55. {isar-1.26.2 → isar-1.26.3}/docs/Makefile +0 -0
  56. {isar-1.26.2 → isar-1.26.3}/docs/make.bat +0 -0
  57. {isar-1.26.2 → isar-1.26.3}/docs/rst_processing.py +0 -0
  58. {isar-1.26.2 → isar-1.26.3}/docs/source/conf.py +0 -0
  59. {isar-1.26.2 → isar-1.26.3}/docs/source/index.rst +0 -0
  60. {isar-1.26.2 → isar-1.26.3}/docs/source/readme_link.md +0 -0
  61. {isar-1.26.2 → isar-1.26.3}/docs/state_machine_diagram.png +0 -0
  62. {isar-1.26.2 → isar-1.26.3}/main.py +0 -0
  63. {isar-1.26.2 → isar-1.26.3}/radixconfig.yml +0 -0
  64. {isar-1.26.2 → isar-1.26.3}/setup.cfg +0 -0
  65. {isar-1.26.2 → isar-1.26.3}/src/isar/__init__.py +0 -0
  66. {isar-1.26.2 → isar-1.26.3}/src/isar/apis/__init__.py +0 -0
  67. {isar-1.26.2 → isar-1.26.3}/src/isar/apis/models/__init__.py +0 -0
  68. {isar-1.26.2 → isar-1.26.3}/src/isar/apis/models/models.py +0 -0
  69. {isar-1.26.2 → isar-1.26.3}/src/isar/apis/models/start_mission_definition.py +0 -0
  70. {isar-1.26.2 → isar-1.26.3}/src/isar/apis/schedule/__init__.py +0 -0
  71. {isar-1.26.2 → isar-1.26.3}/src/isar/apis/security/__init__.py +0 -0
  72. {isar-1.26.2 → isar-1.26.3}/src/isar/config/__init__.py +0 -0
  73. {isar-1.26.2 → isar-1.26.3}/src/isar/config/certs/ca-cert.pem +0 -0
  74. {isar-1.26.2 → isar-1.26.3}/src/isar/config/configuration_error.py +0 -0
  75. {isar-1.26.2 → isar-1.26.3}/src/isar/config/keyvault/__init__.py +0 -0
  76. {isar-1.26.2 → isar-1.26.3}/src/isar/config/keyvault/keyvault_error.py +0 -0
  77. {isar-1.26.2 → isar-1.26.3}/src/isar/config/keyvault/keyvault_service.py +0 -0
  78. {isar-1.26.2 → isar-1.26.3}/src/isar/config/logging.conf +0 -0
  79. {isar-1.26.2 → isar-1.26.3}/src/isar/config/maps/JSP1_intermediate_deck.json +0 -0
  80. {isar-1.26.2 → isar-1.26.3}/src/isar/config/maps/JSP1_weather_deck.json +0 -0
  81. {isar-1.26.2 → isar-1.26.3}/src/isar/config/maps/default_map.json +0 -0
  82. {isar-1.26.2 → isar-1.26.3}/src/isar/config/maps/klab_b.json +0 -0
  83. {isar-1.26.2 → isar-1.26.3}/src/isar/config/maps/klab_compressor.json +0 -0
  84. {isar-1.26.2 → isar-1.26.3}/src/isar/config/maps/klab_turtlebot.json +0 -0
  85. {isar-1.26.2 → isar-1.26.3}/src/isar/config/maps/turtleworld.json +0 -0
  86. {isar-1.26.2 → isar-1.26.3}/src/isar/config/predefined_mission_definition/__init__.py +0 -0
  87. {isar-1.26.2 → isar-1.26.3}/src/isar/config/predefined_mission_definition/default_exr.json +0 -0
  88. {isar-1.26.2 → isar-1.26.3}/src/isar/config/predefined_mission_definition/default_mission.json +0 -0
  89. {isar-1.26.2 → isar-1.26.3}/src/isar/config/predefined_mission_definition/default_turtlebot.json +0 -0
  90. {isar-1.26.2 → isar-1.26.3}/src/isar/config/predefined_missions/__init__.py +0 -0
  91. {isar-1.26.2 → isar-1.26.3}/src/isar/config/predefined_missions/default.json +0 -0
  92. {isar-1.26.2 → isar-1.26.3}/src/isar/config/predefined_missions/default_turtlebot.json +0 -0
  93. {isar-1.26.2 → isar-1.26.3}/src/isar/config/settings.env +0 -0
  94. {isar-1.26.2 → isar-1.26.3}/src/isar/config/settings.py +0 -0
  95. {isar-1.26.2 → isar-1.26.3}/src/isar/mission_planner/__init__.py +0 -0
  96. {isar-1.26.2 → isar-1.26.3}/src/isar/mission_planner/mission_planner_interface.py +0 -0
  97. {isar-1.26.2 → isar-1.26.3}/src/isar/mission_planner/sequential_task_selector.py +0 -0
  98. {isar-1.26.2 → isar-1.26.3}/src/isar/mission_planner/task_selector_interface.py +0 -0
  99. {isar-1.26.2 → isar-1.26.3}/src/isar/models/__init__.py +0 -0
  100. {isar-1.26.2 → isar-1.26.3}/src/isar/models/communication/__init__.py +0 -0
  101. {isar-1.26.2 → isar-1.26.3}/src/isar/models/communication/message.py +0 -0
  102. {isar-1.26.2 → isar-1.26.3}/src/isar/models/communication/queues/__init__.py +0 -0
  103. {isar-1.26.2 → isar-1.26.3}/src/isar/models/communication/queues/events.py +0 -0
  104. {isar-1.26.2 → isar-1.26.3}/src/isar/models/communication/queues/queue_io.py +0 -0
  105. {isar-1.26.2 → isar-1.26.3}/src/isar/models/communication/queues/queue_timeout_error.py +0 -0
  106. {isar-1.26.2 → isar-1.26.3}/src/isar/models/communication/queues/queue_utils.py +0 -0
  107. {isar-1.26.2 → isar-1.26.3}/src/isar/models/communication/queues/status_queue.py +0 -0
  108. {isar-1.26.2 → isar-1.26.3}/src/isar/models/mission_metadata/__init__.py +0 -0
  109. {isar-1.26.2 → isar-1.26.3}/src/isar/services/__init__.py +0 -0
  110. {isar-1.26.2 → isar-1.26.3}/src/isar/services/auth/__init__.py +0 -0
  111. {isar-1.26.2 → isar-1.26.3}/src/isar/services/auth/azure_credentials.py +0 -0
  112. {isar-1.26.2 → isar-1.26.3}/src/isar/services/service_connections/__init__.py +0 -0
  113. {isar-1.26.2 → isar-1.26.3}/src/isar/services/service_connections/mqtt/__init__.py +0 -0
  114. {isar-1.26.2 → isar-1.26.3}/src/isar/services/service_connections/mqtt/mqtt_client.py +0 -0
  115. {isar-1.26.2 → isar-1.26.3}/src/isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +0 -0
  116. {isar-1.26.2 → isar-1.26.3}/src/isar/services/service_connections/mqtt/robot_info_publisher.py +0 -0
  117. {isar-1.26.2 → isar-1.26.3}/src/isar/services/service_connections/request_handler.py +0 -0
  118. {isar-1.26.2 → isar-1.26.3}/src/isar/services/service_connections/stid/__init__.py +0 -0
  119. {isar-1.26.2 → isar-1.26.3}/src/isar/services/utilities/__init__.py +0 -0
  120. {isar-1.26.2 → isar-1.26.3}/src/isar/services/utilities/queue_utilities.py +0 -0
  121. {isar-1.26.2 → isar-1.26.3}/src/isar/services/utilities/threaded_request.py +0 -0
  122. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/__init__.py +0 -0
  123. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/states/__init__.py +0 -0
  124. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/states/off.py +0 -0
  125. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/states_enum.py +0 -0
  126. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/transitions/fail_mission.py +0 -0
  127. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/transitions/finish_mission.py +0 -0
  128. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/transitions/pause.py +0 -0
  129. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/transitions/resume.py +0 -0
  130. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/transitions/start_mission.py +0 -0
  131. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/transitions/stop.py +0 -0
  132. {isar-1.26.2 → isar-1.26.3}/src/isar/state_machine/transitions/utils.py +0 -0
  133. {isar-1.26.2 → isar-1.26.3}/src/isar/storage/__init__.py +0 -0
  134. {isar-1.26.2 → isar-1.26.3}/src/isar/storage/local_storage.py +0 -0
  135. {isar-1.26.2 → isar-1.26.3}/src/isar/storage/storage_interface.py +0 -0
  136. {isar-1.26.2 → isar-1.26.3}/src/isar/storage/utilities.py +0 -0
  137. {isar-1.26.2 → isar-1.26.3}/src/isar.egg-info/dependency_links.txt +0 -0
  138. {isar-1.26.2 → isar-1.26.3}/src/isar.egg-info/entry_points.txt +0 -0
  139. {isar-1.26.2 → isar-1.26.3}/src/isar.egg-info/top_level.txt +0 -0
  140. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/__init__.py +0 -0
  141. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/__init__.py +0 -0
  142. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/exceptions/__init__.py +0 -0
  143. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/exceptions/robot_exceptions.py +0 -0
  144. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/initialize/__init__.py +0 -0
  145. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/inspection/__init__.py +0 -0
  146. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/inspection/inspection.py +0 -0
  147. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/mission/__init__.py +0 -0
  148. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/mission/mission.py +0 -0
  149. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/mission/status.py +0 -0
  150. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/mission/task.py +0 -0
  151. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/robots/__init__.py +0 -0
  152. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/robots/battery_state.py +0 -0
  153. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/robots/media.py +0 -0
  154. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/models/robots/robot_model.py +0 -0
  155. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/robot_interface.py +0 -0
  156. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/telemetry/__init__.py +0 -0
  157. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/telemetry/mqtt_client.py +0 -0
  158. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/telemetry/payloads.py +0 -0
  159. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/test_robot_interface.py +0 -0
  160. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/utilities/__init__.py +0 -0
  161. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/utilities/json_service.py +0 -0
  162. {isar-1.26.2 → isar-1.26.3}/src/robot_interface/utilities/uuid_string_factory.py +0 -0
  163. {isar-1.26.2 → isar-1.26.3}/tests/__init__.py +0 -0
  164. {isar-1.26.2 → isar-1.26.3}/tests/integration/__init__.py +0 -0
  165. {isar-1.26.2 → isar-1.26.3}/tests/integration/turtlebot/__init__.py +0 -0
  166. {isar-1.26.2 → isar-1.26.3}/tests/integration/turtlebot/config/__init__.py +0 -0
  167. {isar-1.26.2 → isar-1.26.3}/tests/integration/turtlebot/config/maps/__init__.py +0 -0
  168. {isar-1.26.2 → isar-1.26.3}/tests/integration/turtlebot/config/maps/turtleworld.json +0 -0
  169. {isar-1.26.2 → isar-1.26.3}/tests/integration/turtlebot/config/missions/__init__.py +0 -0
  170. {isar-1.26.2 → isar-1.26.3}/tests/integration/turtlebot/config/missions/default.json +0 -0
  171. {isar-1.26.2 → isar-1.26.3}/tests/isar/__init__.py +0 -0
  172. {isar-1.26.2 → isar-1.26.3}/tests/isar/apis/__init__.py +0 -0
  173. {isar-1.26.2 → isar-1.26.3}/tests/isar/apis/models/__init__.py +0 -0
  174. {isar-1.26.2 → isar-1.26.3}/tests/isar/apis/models/example_mission_definition.json +0 -0
  175. {isar-1.26.2 → isar-1.26.3}/tests/isar/apis/models/test_start_mission_definition.py +0 -0
  176. {isar-1.26.2 → isar-1.26.3}/tests/isar/apis/scheduler/__init__.py +0 -0
  177. {isar-1.26.2 → isar-1.26.3}/tests/isar/apis/scheduler/test_scheduler_router.py +0 -0
  178. {isar-1.26.2 → isar-1.26.3}/tests/isar/apis/security/__init__.py +0 -0
  179. {isar-1.26.2 → isar-1.26.3}/tests/isar/apis/security/test_authentication.py +0 -0
  180. {isar-1.26.2 → isar-1.26.3}/tests/isar/mission/__init__.py +0 -0
  181. {isar-1.26.2 → isar-1.26.3}/tests/isar/mission/test_mission.py +0 -0
  182. {isar-1.26.2 → isar-1.26.3}/tests/isar/models/__init__.py +0 -0
  183. {isar-1.26.2 → isar-1.26.3}/tests/isar/models/communication/__init__.py +0 -0
  184. {isar-1.26.2 → isar-1.26.3}/tests/isar/models/communication/test_queues.py +0 -0
  185. {isar-1.26.2 → isar-1.26.3}/tests/isar/services/__init__.py +0 -0
  186. {isar-1.26.2 → isar-1.26.3}/tests/isar/services/readers/__init__.py +0 -0
  187. {isar-1.26.2 → isar-1.26.3}/tests/isar/services/readers/test_mission_reader.py +0 -0
  188. {isar-1.26.2 → isar-1.26.3}/tests/isar/services/service_connections/__init__.py +0 -0
  189. {isar-1.26.2 → isar-1.26.3}/tests/isar/services/service_connections/echo/__init__.py +0 -0
  190. {isar-1.26.2 → isar-1.26.3}/tests/isar/services/service_connections/test_base_request_handler.py +0 -0
  191. {isar-1.26.2 → isar-1.26.3}/tests/isar/services/utilities/__init__.py +0 -0
  192. {isar-1.26.2 → isar-1.26.3}/tests/isar/services/utilities/test_queue_utilities.py +0 -0
  193. {isar-1.26.2 → isar-1.26.3}/tests/isar/services/utilities/test_scheduling_utilities.py +0 -0
  194. {isar-1.26.2 → isar-1.26.3}/tests/isar/state_machine/__init__.py +0 -0
  195. {isar-1.26.2 → isar-1.26.3}/tests/isar/state_machine/states/__init__.py +0 -0
  196. {isar-1.26.2 → isar-1.26.3}/tests/isar/state_machine/states/test_monitor.py +0 -0
  197. {isar-1.26.2 → isar-1.26.3}/tests/isar/storage/test_blob_storage.py +0 -0
  198. {isar-1.26.2 → isar-1.26.3}/tests/mocks/__init__.py +0 -0
  199. {isar-1.26.2 → isar-1.26.3}/tests/mocks/blob_storage.py +0 -0
  200. {isar-1.26.2 → isar-1.26.3}/tests/mocks/mission_definition.py +0 -0
  201. {isar-1.26.2 → isar-1.26.3}/tests/mocks/mqtt_client.py +0 -0
  202. {isar-1.26.2 → isar-1.26.3}/tests/mocks/pose.py +0 -0
  203. {isar-1.26.2 → isar-1.26.3}/tests/mocks/request.py +0 -0
  204. {isar-1.26.2 → isar-1.26.3}/tests/mocks/robot_interface.py +0 -0
  205. {isar-1.26.2 → isar-1.26.3}/tests/mocks/status.py +0 -0
  206. {isar-1.26.2 → isar-1.26.3}/tests/mocks/task.py +0 -0
  207. {isar-1.26.2 → isar-1.26.3}/tests/mocks/token.py +0 -0
  208. {isar-1.26.2 → isar-1.26.3}/tests/test_data/test_map_config/test_map_config.json +0 -0
  209. {isar-1.26.2 → isar-1.26.3}/tests/test_data/test_mission_not_working.json +0 -0
  210. {isar-1.26.2 → isar-1.26.3}/tests/test_data/test_mission_working.json +0 -0
  211. {isar-1.26.2 → isar-1.26.3}/tests/test_data/test_mission_working_no_tasks.json +0 -0
  212. {isar-1.26.2 → isar-1.26.3}/tests/test_data/test_thermal_image_mission.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: isar
3
- Version: 1.26.2
3
+ Version: 1.26.3
4
4
  Summary: Integration and Supervisory control of Autonomous Robots
5
5
  Author-email: Equinor ASA <fg_robots_dev@equinor.com>
6
6
  License: Eclipse Public License version 2.0
@@ -111,7 +111,7 @@ Requires-Dist: click
111
111
  Requires-Dist: dacite
112
112
  Requires-Dist: fastapi-azure-auth
113
113
  Requires-Dist: fastapi
114
- Requires-Dist: injector
114
+ Requires-Dist: dependency-injector
115
115
  Requires-Dist: numpy
116
116
  Requires-Dist: opencensus-ext-azure
117
117
  Requires-Dist: opencensus-ext-logging
@@ -34,7 +34,7 @@ dependencies = [
34
34
  "dacite",
35
35
  "fastapi-azure-auth",
36
36
  "fastapi",
37
- "injector",
37
+ "dependency-injector",
38
38
  "numpy",
39
39
  "opencensus-ext-azure",
40
40
  "opencensus-ext-logging",
@@ -12,7 +12,7 @@ anyio==4.9.0
12
12
  # via
13
13
  # httpx
14
14
  # starlette
15
- azure-core==1.32.0
15
+ azure-core==1.33.0
16
16
  # via
17
17
  # azure-identity
18
18
  # azure-keyvault-secrets
@@ -24,7 +24,7 @@ azure-identity==1.21.0
24
24
  # opencensus-ext-azure
25
25
  azure-keyvault-secrets==4.9.0
26
26
  # via isar (pyproject.toml)
27
- azure-storage-blob==12.25.0
27
+ azure-storage-blob==12.25.1
28
28
  # via isar (pyproject.toml)
29
29
  backoff==2.2.1
30
30
  # via isar (pyproject.toml)
@@ -62,15 +62,15 @@ fastapi-azure-auth==5.1.1
62
62
  # via isar (pyproject.toml)
63
63
  google-api-core==2.24.2
64
64
  # via opencensus
65
- google-auth==2.38.0
65
+ google-auth==2.39.0
66
66
  # via google-api-core
67
- googleapis-common-protos==1.69.2
67
+ googleapis-common-protos==1.70.0
68
68
  # via google-api-core
69
69
  h11==0.14.0
70
70
  # via
71
71
  # httpcore
72
72
  # uvicorn
73
- httpcore==1.0.7
73
+ httpcore==1.0.8
74
74
  # via httpx
75
75
  httpx==0.28.1
76
76
  # via fastapi-azure-auth
@@ -91,7 +91,7 @@ msal==1.32.0
91
91
  # msal-extensions
92
92
  msal-extensions==1.3.1
93
93
  # via azure-identity
94
- numpy==2.2.4
94
+ numpy==2.2.5
95
95
  # via
96
96
  # alitra
97
97
  # isar (pyproject.toml)
@@ -113,7 +113,7 @@ paho-mqtt==2.1.0
113
113
  # via isar (pyproject.toml)
114
114
  proto-plus==1.26.1
115
115
  # via google-api-core
116
- protobuf==6.30.1
116
+ protobuf==6.30.2
117
117
  # via
118
118
  # google-api-core
119
119
  # googleapis-common-protos
@@ -124,18 +124,18 @@ pyasn1==0.6.1
124
124
  # via
125
125
  # pyasn1-modules
126
126
  # rsa
127
- pyasn1-modules==0.4.1
127
+ pyasn1-modules==0.4.2
128
128
  # via google-auth
129
129
  pycparser==2.22
130
130
  # via cffi
131
- pydantic==2.10.6
131
+ pydantic==2.11.3
132
132
  # via
133
133
  # fastapi
134
134
  # isar (pyproject.toml)
135
135
  # pydantic-settings
136
- pydantic-core==2.27.2
136
+ pydantic-core==2.33.1
137
137
  # via pydantic
138
- pydantic-settings==2.8.1
138
+ pydantic-settings==2.9.1
139
139
  # via isar (pyproject.toml)
140
140
  pyjwt[crypto]==2.10.1
141
141
  # via
@@ -159,7 +159,7 @@ requests==2.32.3
159
159
  # requests-toolbelt
160
160
  requests-toolbelt==1.0.0
161
161
  # via isar (pyproject.toml)
162
- rsa==4.9
162
+ rsa==4.9.1
163
163
  # via google-auth
164
164
  scipy==1.15.2
165
165
  # via alitra
@@ -170,11 +170,11 @@ six==1.17.0
170
170
  # transitions
171
171
  sniffio==1.3.1
172
172
  # via anyio
173
- starlette==0.46.1
173
+ starlette==0.46.2
174
174
  # via fastapi
175
175
  transitions==0.9.2
176
176
  # via isar (pyproject.toml)
177
- typing-extensions==4.12.2
177
+ typing-extensions==4.13.2
178
178
  # via
179
179
  # azure-core
180
180
  # azure-identity
@@ -183,9 +183,14 @@ typing-extensions==4.12.2
183
183
  # fastapi
184
184
  # pydantic
185
185
  # pydantic-core
186
- urllib3==2.3.0
186
+ # typing-inspection
187
+ typing-inspection==0.4.0
188
+ # via
189
+ # pydantic
190
+ # pydantic-settings
191
+ urllib3==2.4.0
187
192
  # via requests
188
- uvicorn==0.34.0
193
+ uvicorn==0.34.2
189
194
  # via isar (pyproject.toml)
190
195
  wrapt==1.17.2
191
196
  # via opencensus-ext-requests
@@ -5,10 +5,10 @@ from typing import List, Union
5
5
 
6
6
  import click
7
7
  import uvicorn
8
+ from dependency_injector.wiring import inject
8
9
  from fastapi import FastAPI, Request, Security
9
10
  from fastapi.middleware.cors import CORSMiddleware
10
11
  from fastapi.routing import APIRouter
11
- from injector import inject
12
12
  from opencensus.ext.azure.trace_exporter import AzureExporter
13
13
  from opencensus.trace.attributes_helper import COMMON_ATTRIBUTES
14
14
  from opencensus.trace.samplers import ProbabilitySampler
@@ -36,14 +36,14 @@ class API:
36
36
  authenticator: Authenticator,
37
37
  scheduling_controller: SchedulingController,
38
38
  robot_controller: RobotController,
39
- keyvault_client: Keyvault,
39
+ keyvault: Keyvault,
40
40
  port: int = settings.API_PORT,
41
41
  azure_ai_logging_enabled: bool = settings.LOG_HANDLER_APPLICATION_INSIGHTS_ENABLED,
42
42
  ) -> None:
43
43
  self.authenticator: Authenticator = authenticator
44
44
  self.scheduling_controller: SchedulingController = scheduling_controller
45
45
  self.robot_controller: RobotController = robot_controller
46
- self.keyvault_client: Keyvault = keyvault_client
46
+ self.keyvault: Keyvault = keyvault
47
47
  self.host: str = "0.0.0.0" # Locking uvicorn to use 0.0.0.0
48
48
  self.port: int = port
49
49
  self.azure_ai_logging_enabled: bool = azure_ai_logging_enabled
@@ -295,7 +295,7 @@ class API:
295
295
  def _add_request_logging_middleware(self, app: FastAPI) -> None:
296
296
  connection_string: str
297
297
  try:
298
- connection_string = self.keyvault_client.get_secret(
298
+ connection_string = self.keyvault.get_secret(
299
299
  "application-insights-connection-string"
300
300
  ).value
301
301
  except KeyvaultError:
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
 
3
+ from dependency_injector.wiring import inject
3
4
  from fastapi import HTTPException
4
- from injector import inject
5
5
 
6
6
  from isar.apis.models.models import RobotInfoResponse
7
7
  from isar.config.settings import robot_settings, settings
@@ -1,8 +1,8 @@
1
1
  import logging
2
2
  from http import HTTPStatus
3
3
 
4
+ from dependency_injector.wiring import inject
4
5
  from fastapi import Body, HTTPException, Path
5
- from injector import inject
6
6
 
7
7
  from isar.apis.models.models import (
8
8
  ControlMissionResponse,
@@ -3,7 +3,7 @@ import logging
3
3
  from fastapi import Depends
4
4
  from fastapi.security.base import SecurityBase
5
5
  from fastapi_azure_auth import SingleTenantAzureAuthorizationCodeBearer
6
- from fastapi_azure_auth.exceptions import InvalidAuth
6
+ from fastapi_azure_auth.exceptions import InvalidAuthHttp
7
7
  from fastapi_azure_auth.user import User
8
8
  from pydantic import BaseModel
9
9
 
@@ -35,7 +35,7 @@ async def validate_has_role(user: User = Depends(azure_scheme)) -> None:
35
35
  Raises a 403 authorization error if not.
36
36
  """
37
37
  if settings.REQUIRED_ROLE not in user.roles:
38
- raise InvalidAuth(
38
+ raise InvalidAuthHttp(
39
39
  "Current user does not possess the required role for this endpoint"
40
40
  )
41
41
 
@@ -56,7 +56,8 @@ def configure_azure_handler(log_config: dict, keyvault: Keyvault) -> logging.Han
56
56
  ).value
57
57
  except KeyvaultError:
58
58
  message: str = (
59
- f"CRITICAL ERROR: Missing connection string for Application Insights in key vault '{keyvault.name}'."
59
+ "CRITICAL ERROR: Missing connection string for"
60
+ f" Application Insights in key vault '{keyvault.name}'."
60
61
  )
61
62
  print(f"\n{message} \n")
62
63
  raise ConfigurationError(message)
@@ -2,7 +2,7 @@ import json
2
2
  import logging
3
3
  from pathlib import Path
4
4
 
5
- from injector import inject
5
+ from dependency_injector.wiring import inject
6
6
 
7
7
  from isar.config.settings import settings
8
8
  from isar.mission_planner.mission_planner_interface import (
@@ -0,0 +1,138 @@
1
+ import os
2
+ from importlib import import_module
3
+
4
+ from dependency_injector import containers, providers
5
+
6
+ from isar.apis.api import API
7
+ from isar.apis.robot_control.robot_controller import RobotController
8
+ from isar.apis.schedule.scheduling_controller import SchedulingController
9
+ from isar.apis.security.authentication import Authenticator
10
+ from isar.config.keyvault.keyvault_service import Keyvault
11
+ from isar.config.settings import settings
12
+ from isar.mission_planner.local_planner import LocalPlanner
13
+ from isar.mission_planner.sequential_task_selector import SequentialTaskSelector
14
+ from isar.mission_planner.task_selector_interface import TaskSelectorInterface
15
+ from isar.models.communication.queues.events import Events, SharedState
16
+ from isar.robot.robot import Robot
17
+ from isar.services.service_connections.request_handler import RequestHandler
18
+ from isar.services.utilities.robot_utilities import RobotUtilities
19
+ from isar.services.utilities.scheduling_utilities import SchedulingUtilities
20
+ from isar.state_machine.state_machine import StateMachine
21
+ from isar.storage.blob_storage import BlobStorage
22
+ from isar.storage.local_storage import LocalStorage
23
+ from isar.storage.slimm_storage import SlimmStorage
24
+ from isar.storage.uploader import Uploader
25
+ from robot_interface.telemetry.mqtt_client import MqttPublisher
26
+
27
+
28
+ class ApplicationContainer(containers.DeclarativeContainer):
29
+ config = providers.Configuration(pydantic_settings=[settings])
30
+
31
+ # Core services
32
+ keyvault = providers.Singleton(
33
+ Keyvault,
34
+ keyvault_name=settings.KEYVAULT_NAME,
35
+ client_id=settings.AZURE_CLIENT_ID,
36
+ client_secret=os.environ.get("AZURE_CLIENT_SECRET"),
37
+ tenant_id=settings.AZURE_TENANT_ID,
38
+ )
39
+
40
+ # Events and shared state
41
+ events = providers.Singleton(Events)
42
+ shared_state = providers.Singleton(SharedState)
43
+
44
+ # Robot-related services
45
+ robot_interface = providers.Singleton(
46
+ lambda: import_module(f"{settings.ROBOT_PACKAGE}.robotinterface").Robot()
47
+ )
48
+ robot_utilities = providers.Singleton(RobotUtilities, robot=robot_interface)
49
+
50
+ # API and controllers
51
+ authenticator = providers.Singleton(Authenticator)
52
+ scheduling_utilities = providers.Singleton(
53
+ SchedulingUtilities,
54
+ events=events,
55
+ shared_state=shared_state,
56
+ mission_planner=providers.Singleton(LocalPlanner),
57
+ )
58
+ scheduling_controller = providers.Singleton(
59
+ SchedulingController, scheduling_utilities=scheduling_utilities
60
+ )
61
+ robot_controller = providers.Singleton(
62
+ RobotController, robot_utilities=robot_utilities
63
+ )
64
+ api = providers.Singleton(
65
+ API,
66
+ authenticator=authenticator,
67
+ scheduling_controller=scheduling_controller,
68
+ robot_controller=robot_controller,
69
+ keyvault=keyvault,
70
+ )
71
+
72
+ # Storage
73
+ local_storage = providers.Singleton(LocalStorage)
74
+ blob_storage = providers.Singleton(BlobStorage, keyvault=keyvault)
75
+ slimm_storage = providers.Singleton(
76
+ SlimmStorage, request_handler=providers.Singleton(RequestHandler)
77
+ )
78
+ storage_handlers = providers.List(local_storage, blob_storage, slimm_storage)
79
+
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
+ # State machine
91
+ task_selector = providers.Singleton(
92
+ SequentialTaskSelector
93
+ if settings.TASK_SELECTOR == "sequential"
94
+ else TaskSelectorInterface
95
+ )
96
+
97
+ state_machine = providers.Singleton(
98
+ StateMachine,
99
+ events=events,
100
+ shared_state=shared_state,
101
+ robot=robot_interface,
102
+ mqtt_publisher=mqtt_client,
103
+ task_selector=task_selector,
104
+ )
105
+
106
+ # Robot
107
+ robot = providers.Singleton(
108
+ Robot, events=events, robot=robot_interface, shared_state=shared_state
109
+ )
110
+
111
+ # Uploader
112
+ uploader = providers.Singleton(
113
+ Uploader,
114
+ events=events,
115
+ storage_handlers=storage_handlers,
116
+ mqtt_publisher=mqtt_client,
117
+ )
118
+
119
+
120
+ def get_injector() -> ApplicationContainer:
121
+ container = ApplicationContainer()
122
+ container.init_resources()
123
+ container.wire(modules=[__name__])
124
+ container.config.from_dict(
125
+ {
126
+ "KEYVAULT_NAME": settings.KEYVAULT_NAME,
127
+ "MQTT_ENABLED": settings.MQTT_ENABLED,
128
+ "TASK_SELECTOR": settings.TASK_SELECTOR,
129
+ }
130
+ )
131
+
132
+ print("Loaded the following module configurations:")
133
+ for provider_name, provider in container.providers.items():
134
+ provider_repr = repr(provider)
135
+ simplified_provider = provider_repr.split(".")[-1].split(">")[0]
136
+ print(f" {provider_name:<20}: {simplified_provider}")
137
+
138
+ return container
@@ -3,7 +3,7 @@ from queue import Queue
3
3
  from threading import Event
4
4
  from typing import Optional
5
5
 
6
- from injector import inject
6
+ from dependency_injector.wiring import inject
7
7
 
8
8
  from isar.models.communication.queues.events import (
9
9
  Events,
@@ -20,11 +20,10 @@ from robot_interface.robot_interface import RobotInterface
20
20
 
21
21
 
22
22
  class Robot(object):
23
-
24
23
  @inject
25
24
  def __init__(
26
25
  self, events: Events, robot: RobotInterface, shared_state: SharedState
27
- ):
26
+ ) -> None:
28
27
  self.logger = logging.getLogger("robot")
29
28
  self.state_machine_events: StateMachineEvents = events.state_machine_events
30
29
  self.robot_service_events: RobotServiceEvents = events.robot_service_events
@@ -107,10 +106,7 @@ class Robot(object):
107
106
  )
108
107
  self.robot_status_thread.start()
109
108
 
110
- while True:
111
- if self.signal_thread_quitting.is_set():
112
- break
113
-
109
+ while not self.signal_thread_quitting.wait(0):
114
110
  self._check_and_handle_start_mission(
115
111
  self.state_machine_events.start_mission
116
112
  )
@@ -17,7 +17,6 @@ from robot_interface.robot_interface import RobotInterface
17
17
 
18
18
 
19
19
  class RobotStartMissionThread(Thread):
20
-
21
20
  def __init__(
22
21
  self,
23
22
  robot_service_events: RobotServiceEvents,
@@ -9,7 +9,6 @@ from robot_interface.robot_interface import RobotInterface
9
9
 
10
10
 
11
11
  class RobotStatusThread(Thread):
12
-
13
12
  def __init__(
14
13
  self,
15
14
  robot: RobotInterface,
@@ -21,7 +20,7 @@ class RobotStatusThread(Thread):
21
20
  self.robot: RobotInterface = robot
22
21
  self.signal_thread_quitting: Event = signal_thread_quitting
23
22
  self.last_robot_status_poll_time: float = time.time()
24
- Thread.__init__(self, name="Robot status thread", daemon=True)
23
+ Thread.__init__(self, name="Robot status thread")
25
24
 
26
25
  def stop(self) -> None:
27
26
  return
@@ -35,10 +34,10 @@ class RobotStatusThread(Thread):
35
34
  )
36
35
 
37
36
  def run(self):
38
- while True:
39
- if self.signal_thread_quitting.is_set():
40
- break
37
+ if self.signal_thread_quitting.is_set():
38
+ return
41
39
 
40
+ while not self.signal_thread_quitting.wait(0.001):
42
41
  if not self._is_ready_to_poll_for_status():
43
42
  continue
44
43
 
@@ -18,7 +18,6 @@ from robot_interface.robot_interface import RobotInterface
18
18
 
19
19
 
20
20
  class RobotStopMissionThread(Thread):
21
-
22
21
  def __init__(
23
22
  self,
24
23
  robot_service_events: RobotServiceEvents,
@@ -19,7 +19,6 @@ from robot_interface.robot_interface import RobotInterface
19
19
 
20
20
 
21
21
  class RobotTaskStatusThread(Thread):
22
-
23
22
  def __init__(
24
23
  self,
25
24
  robot_service_events: RobotServiceEvents,
@@ -5,15 +5,12 @@ from logging import Logger
5
5
  from threading import Thread
6
6
  from typing import Any, List, Tuple
7
7
 
8
- from injector import Injector
9
-
10
8
  import isar
11
9
  from isar.apis.api import API
12
- from isar.config.keyvault.keyvault_service import Keyvault
13
10
  from isar.config.log import setup_loggers
14
11
  from isar.config.settings import robot_settings, settings
15
12
  from isar.models.communication.queues.events import Events
16
- from isar.modules import get_injector
13
+ from isar.modules import ApplicationContainer, get_injector
17
14
  from isar.robot.robot import Robot
18
15
  from isar.services.service_connections.mqtt.mqtt_client import MqttClient
19
16
  from isar.services.service_connections.mqtt.robot_heartbeat_publisher import (
@@ -83,19 +80,19 @@ def print_startup_info():
83
80
 
84
81
 
85
82
  def start() -> None:
86
- injector: Injector = get_injector()
83
+ injector: ApplicationContainer = get_injector()
87
84
 
88
- keyvault_client = injector.get(Keyvault)
89
- setup_loggers(keyvault=keyvault_client)
85
+ keyvault = injector.keyvault()
86
+ setup_loggers(keyvault=keyvault)
90
87
  logger: Logger = logging.getLogger("main")
91
88
 
92
89
  print_startup_info()
93
90
 
94
- state_machine: StateMachine = injector.get(StateMachine)
95
- uploader: Uploader = injector.get(Uploader)
96
- robot: RobotInterface = injector.get(RobotInterface)
97
- events: Events = injector.get(Events)
98
- robot_service: Robot = injector.get(Robot)
91
+ state_machine: StateMachine = injector.state_machine()
92
+ uploader: Uploader = injector.uploader()
93
+ robot_interface: RobotInterface = injector.robot_interface()
94
+ events: Events = injector.events()
95
+ robot: Robot = injector.robot()
99
96
 
100
97
  threads: List[Thread] = []
101
98
 
@@ -110,7 +107,7 @@ def start() -> None:
110
107
  threads.append(uploader_thread)
111
108
 
112
109
  robot_service_thread: Thread = Thread(
113
- target=robot_service.run, name="Robot service", daemon=True
110
+ target=robot.run, name="Robot service", daemon=True
114
111
  )
115
112
  threads.append(robot_service_thread)
116
113
 
@@ -123,7 +120,7 @@ def start() -> None:
123
120
  )
124
121
  state_machine.events.upload_queue.put(message)
125
122
 
126
- robot.register_inspection_callback(inspections_callback)
123
+ robot_interface.register_inspection_callback(inspections_callback)
127
124
 
128
125
  if settings.MQTT_ENABLED:
129
126
  mqtt_client: MqttClient = MqttClient(mqtt_queue=events.mqtt_queue)
@@ -154,7 +151,7 @@ def start() -> None:
154
151
  )
155
152
  threads.append(robot_heartbeat_thread)
156
153
 
157
- publishers: List[Thread] = robot.get_telemetry_publishers(
154
+ publishers: List[Thread] = robot_interface.get_telemetry_publishers(
158
155
  queue=events.mqtt_queue,
159
156
  robot_name=settings.ROBOT_NAME,
160
157
  isar_id=settings.ISAR_ID,
@@ -163,7 +160,7 @@ def start() -> None:
163
160
  if publishers:
164
161
  threads.extend(publishers)
165
162
 
166
- api: API = injector.get(API)
163
+ api: API = injector.api()
167
164
  api_thread: Thread = Thread(target=api.run_app, name="ISAR API", daemon=True)
168
165
  threads.append(api_thread)
169
166
 
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
 
3
- from injector import inject
3
+ from dependency_injector.wiring import inject
4
4
 
5
5
  from robot_interface.models.robots.media import MediaConfig
6
6
  from robot_interface.robot_interface import RobotInterface
@@ -4,8 +4,8 @@ from http import HTTPStatus
4
4
  from queue import Empty
5
5
  from typing import Any, List
6
6
 
7
+ from dependency_injector.wiring import inject
7
8
  from fastapi import HTTPException
8
- from injector import inject
9
9
  from requests import HTTPError
10
10
 
11
11
  from isar.apis.models.models import ControlMissionResponse
@@ -2,9 +2,10 @@ import json
2
2
  import logging
3
3
  from collections import deque
4
4
  from datetime import datetime, timezone
5
+ from threading import Event
5
6
  from typing import Deque, List, Optional
6
7
 
7
- from injector import inject
8
+ from dependency_injector.wiring import inject
8
9
  from transitions import Machine
9
10
  from transitions.core import State
10
11
 
@@ -97,6 +98,8 @@ class StateMachine(object):
97
98
  self.mqtt_publisher: Optional[MqttClientInterface] = mqtt_publisher
98
99
  self.task_selector: TaskSelectorInterface = task_selector
99
100
 
101
+ self.signal_state_machine_to_stop: Event = Event()
102
+
100
103
  # List of states
101
104
  self.stop_state: State = Stop(self)
102
105
  self.paused_state: State = Paused(self)
@@ -237,6 +240,10 @@ class StateMachine(object):
237
240
  """Starts the state machine. Transitions into idle state."""
238
241
  self.to_idle() # type: ignore
239
242
 
243
+ def terminate(self):
244
+ self.logger.info("Stopping state machine")
245
+ self.signal_state_machine_to_stop.set()
246
+
240
247
  def iterate_current_task(self):
241
248
  if self.current_task.is_finished():
242
249
  try:
@@ -21,6 +21,7 @@ class BlockedProtectiveStop(State):
21
21
  self.logger = logging.getLogger("state_machine")
22
22
  self.robot_status_thread: Optional[ThreadedRequest] = None
23
23
  self.shared_state = self.state_machine.shared_state
24
+ self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
24
25
 
25
26
  def start(self) -> None:
26
27
  self.state_machine.update_state()
@@ -31,8 +32,15 @@ class BlockedProtectiveStop(State):
31
32
 
32
33
  def _run(self) -> None:
33
34
  while True:
34
- robot_status = check_shared_state(self.shared_state.robot_status)
35
+ if self.signal_state_machine_to_stop.is_set():
36
+ self.logger.info(
37
+ "Stopping state machine from %s state", self.__class__.__name__
38
+ )
39
+ break
35
40
 
41
+ robot_status: RobotStatus = check_shared_state(
42
+ self.shared_state.robot_status
43
+ )
36
44
  if robot_status == RobotStatus.Offline:
37
45
  transition = self.state_machine.robot_turned_offline # type: ignore
38
46
  break
@@ -25,6 +25,7 @@ class Idle(State):
25
25
  self.last_robot_status_poll_time: float = time.time()
26
26
  self.events = self.state_machine.events
27
27
  self.shared_state = self.state_machine.shared_state
28
+ self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
28
29
 
29
30
  def start(self) -> None:
30
31
  self.state_machine.update_state()
@@ -63,6 +64,12 @@ class Idle(State):
63
64
 
64
65
  def _run(self) -> None:
65
66
  while True:
67
+ if self.signal_state_machine_to_stop.is_set():
68
+ self.logger.info(
69
+ "Stopping state machine from %s state", self.__class__.__name__
70
+ )
71
+ break
72
+
66
73
  if self._check_and_handle_stop_mission_event(
67
74
  self.events.api_requests.stop_mission.input
68
75
  ):