isar 1.25.2__tar.gz → 1.25.4__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 (214) hide show
  1. isar-1.25.4/.github/PULL_REQUEST_TEMPLATE.md +11 -0
  2. {isar-1.25.2 → isar-1.25.4}/.github/workflows/pythonpackage.yml +9 -2
  3. {isar-1.25.2 → isar-1.25.4}/PKG-INFO +3 -5
  4. {isar-1.25.2 → isar-1.25.4}/pyproject.toml +1 -3
  5. {isar-1.25.2 → isar-1.25.4}/requirements.txt +16 -16
  6. {isar-1.25.2 → isar-1.25.4}/src/isar/apis/models/start_mission_definition.py +9 -9
  7. {isar-1.25.2 → isar-1.25.4}/src/isar/apis/robot_control/robot_controller.py +1 -3
  8. {isar-1.25.2 → isar-1.25.4}/src/isar/apis/schedule/scheduling_controller.py +2 -2
  9. {isar-1.25.2 → isar-1.25.4}/src/isar/mission_planner/sequential_task_selector.py +1 -1
  10. {isar-1.25.2 → isar-1.25.4}/src/isar/mission_planner/task_selector_interface.py +1 -1
  11. {isar-1.25.2 → isar-1.25.4}/src/isar/models/communication/queues/status_queue.py +2 -2
  12. {isar-1.25.2 → isar-1.25.4}/src/isar/modules.py +6 -5
  13. {isar-1.25.2 → isar-1.25.4}/src/isar/script.py +1 -1
  14. {isar-1.25.2 → isar-1.25.4}/src/isar/services/utilities/scheduling_utilities.py +6 -2
  15. {isar-1.25.2 → isar-1.25.4}/src/isar/state_machine/state_machine.py +68 -49
  16. isar-1.25.4/src/isar/state_machine/states/blocked_protective_stop.py +65 -0
  17. {isar-1.25.2 → isar-1.25.4}/src/isar/state_machine/states/idle.py +21 -10
  18. {isar-1.25.2 → isar-1.25.4}/src/isar/state_machine/states/monitor.py +2 -1
  19. {isar-1.25.2 → isar-1.25.4}/src/isar/state_machine/states_enum.py +1 -0
  20. {isar-1.25.2 → isar-1.25.4}/src/isar/storage/blob_storage.py +1 -1
  21. {isar-1.25.2 → isar-1.25.4}/src/isar/storage/uploader.py +12 -13
  22. {isar-1.25.2 → isar-1.25.4}/src/isar.egg-info/PKG-INFO +3 -5
  23. {isar-1.25.2 → isar-1.25.4}/src/isar.egg-info/SOURCES.txt +2 -0
  24. {isar-1.25.2 → isar-1.25.4}/src/isar.egg-info/requires.txt +1 -3
  25. {isar-1.25.2 → isar-1.25.4}/src/robot_interface/models/exceptions/robot_exceptions.py +20 -22
  26. isar-1.25.4/src/robot_interface/models/mission/status.py +29 -0
  27. {isar-1.25.2 → isar-1.25.4}/src/robot_interface/models/mission/task.py +1 -1
  28. isar-1.25.4/src/robot_interface/models/robots/battery_state.py +6 -0
  29. {isar-1.25.2 → isar-1.25.4}/src/robot_interface/models/robots/media.py +1 -1
  30. isar-1.25.4/src/robot_interface/models/robots/robot_model.py +13 -0
  31. {isar-1.25.2 → isar-1.25.4}/src/robot_interface/robot_interface.py +1 -1
  32. {isar-1.25.2 → isar-1.25.4}/src/robot_interface/telemetry/payloads.py +40 -9
  33. {isar-1.25.2 → isar-1.25.4}/tests/conftest.py +3 -1
  34. isar-1.25.4/tests/integration/turtlebot/config/missions/default.json +89 -0
  35. {isar-1.25.2 → isar-1.25.4}/tests/isar/models/communication/test_queues.py +1 -1
  36. isar-1.25.4/tests/isar/services/service_connections/echo/__init__.py +0 -0
  37. isar-1.25.4/tests/isar/services/utilities/__init__.py +0 -0
  38. {isar-1.25.2 → isar-1.25.4}/tests/isar/services/utilities/test_scheduling_utilities.py +2 -1
  39. isar-1.25.4/tests/isar/state_machine/__init__.py +0 -0
  40. isar-1.25.4/tests/isar/state_machine/states/__init__.py +0 -0
  41. {isar-1.25.2 → isar-1.25.4}/tests/isar/state_machine/test_state_machine.py +44 -7
  42. isar-1.25.4/tests/mocks/__init__.py +0 -0
  43. {isar-1.25.2 → isar-1.25.4}/tests/mocks/robot_interface.py +13 -1
  44. isar-1.25.4/tests/test_data/test_mission_not_working.json +45 -0
  45. isar-1.25.2/src/isar/apis/models/__init__.py +0 -1
  46. isar-1.25.2/src/isar/models/communication/queues/__init__.py +0 -4
  47. isar-1.25.2/src/isar/state_machine/states/__init__.py +0 -8
  48. isar-1.25.2/src/robot_interface/models/initialize/__init__.py +0 -1
  49. isar-1.25.2/src/robot_interface/models/inspection/__init__.py +0 -13
  50. isar-1.25.2/src/robot_interface/models/mission/status.py +0 -28
  51. isar-1.25.2/src/robot_interface/models/robots/battery_state.py +0 -6
  52. isar-1.25.2/src/robot_interface/models/robots/robot_model.py +0 -13
  53. isar-1.25.2/tests/integration/turtlebot/config/missions/default.json +0 -104
  54. isar-1.25.2/tests/test_data/test_mission_not_working.json +0 -61
  55. {isar-1.25.2 → isar-1.25.4}/.dockerignore +0 -0
  56. {isar-1.25.2 → isar-1.25.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  57. {isar-1.25.2 → isar-1.25.4}/.github/ISSUE_TEMPLATE/feature.md +0 -0
  58. {isar-1.25.2 → isar-1.25.4}/.github/ISSUE_TEMPLATE/improvement.md +0 -0
  59. {isar-1.25.2 → isar-1.25.4}/.github/release.yml +0 -0
  60. {isar-1.25.2 → isar-1.25.4}/.github/workflows/compile_requirements.yml +0 -0
  61. {isar-1.25.2 → isar-1.25.4}/.github/workflows/project_automations.yml +0 -0
  62. {isar-1.25.2 → isar-1.25.4}/.github/workflows/publish_isar_base_image.yml +0 -0
  63. {isar-1.25.2 → isar-1.25.4}/.github/workflows/pythonpublish.yml +0 -0
  64. {isar-1.25.2 → isar-1.25.4}/.github/workflows/stale.yml +0 -0
  65. {isar-1.25.2 → isar-1.25.4}/.gitignore +0 -0
  66. {isar-1.25.2 → isar-1.25.4}/.pre-commit-config.yaml +0 -0
  67. {isar-1.25.2 → isar-1.25.4}/Dockerfile +0 -0
  68. {isar-1.25.2 → isar-1.25.4}/LICENSE +0 -0
  69. {isar-1.25.2 → isar-1.25.4}/README.md +0 -0
  70. {isar-1.25.2 → isar-1.25.4}/SECURITY.md +0 -0
  71. {isar-1.25.2 → isar-1.25.4}/docker-compose-turtlebot.yml +0 -0
  72. {isar-1.25.2 → isar-1.25.4}/docker-compose.yml +0 -0
  73. {isar-1.25.2 → isar-1.25.4}/docs/Makefile +0 -0
  74. {isar-1.25.2 → isar-1.25.4}/docs/make.bat +0 -0
  75. {isar-1.25.2 → isar-1.25.4}/docs/rst_processing.py +0 -0
  76. {isar-1.25.2 → isar-1.25.4}/docs/source/conf.py +0 -0
  77. {isar-1.25.2 → isar-1.25.4}/docs/source/index.rst +0 -0
  78. {isar-1.25.2 → isar-1.25.4}/docs/source/readme_link.md +0 -0
  79. {isar-1.25.2 → isar-1.25.4}/docs/state_machine_diagram.png +0 -0
  80. {isar-1.25.2 → isar-1.25.4}/main.py +0 -0
  81. {isar-1.25.2 → isar-1.25.4}/radixconfig.yml +0 -0
  82. {isar-1.25.2 → isar-1.25.4}/setup.cfg +0 -0
  83. {isar-1.25.2 → isar-1.25.4}/src/isar/__init__.py +0 -0
  84. {isar-1.25.2 → isar-1.25.4}/src/isar/apis/__init__.py +0 -0
  85. {isar-1.25.2 → isar-1.25.4}/src/isar/apis/api.py +0 -0
  86. {isar-1.25.2/src/isar/apis/schedule → isar-1.25.4/src/isar/apis/models}/__init__.py +0 -0
  87. {isar-1.25.2 → isar-1.25.4}/src/isar/apis/models/models.py +0 -0
  88. {isar-1.25.2/src/isar/apis/security → isar-1.25.4/src/isar/apis/schedule}/__init__.py +0 -0
  89. {isar-1.25.2/src/isar/config → isar-1.25.4/src/isar/apis/security}/__init__.py +0 -0
  90. {isar-1.25.2 → isar-1.25.4}/src/isar/apis/security/authentication.py +0 -0
  91. {isar-1.25.2/src/isar/config/keyvault → isar-1.25.4/src/isar/config}/__init__.py +0 -0
  92. {isar-1.25.2 → isar-1.25.4}/src/isar/config/certs/ca-cert.pem +0 -0
  93. {isar-1.25.2 → isar-1.25.4}/src/isar/config/configuration_error.py +0 -0
  94. {isar-1.25.2/src/isar/config/predefined_mission_definition → isar-1.25.4/src/isar/config/keyvault}/__init__.py +0 -0
  95. {isar-1.25.2 → isar-1.25.4}/src/isar/config/keyvault/keyvault_error.py +0 -0
  96. {isar-1.25.2 → isar-1.25.4}/src/isar/config/keyvault/keyvault_service.py +0 -0
  97. {isar-1.25.2 → isar-1.25.4}/src/isar/config/log.py +0 -0
  98. {isar-1.25.2 → isar-1.25.4}/src/isar/config/logging.conf +0 -0
  99. {isar-1.25.2 → isar-1.25.4}/src/isar/config/maps/JSP1_intermediate_deck.json +0 -0
  100. {isar-1.25.2 → isar-1.25.4}/src/isar/config/maps/JSP1_weather_deck.json +0 -0
  101. {isar-1.25.2 → isar-1.25.4}/src/isar/config/maps/default_map.json +0 -0
  102. {isar-1.25.2 → isar-1.25.4}/src/isar/config/maps/klab_b.json +0 -0
  103. {isar-1.25.2 → isar-1.25.4}/src/isar/config/maps/klab_compressor.json +0 -0
  104. {isar-1.25.2 → isar-1.25.4}/src/isar/config/maps/klab_turtlebot.json +0 -0
  105. {isar-1.25.2 → isar-1.25.4}/src/isar/config/maps/turtleworld.json +0 -0
  106. {isar-1.25.2/src/isar/config/predefined_missions → isar-1.25.4/src/isar/config/predefined_mission_definition}/__init__.py +0 -0
  107. {isar-1.25.2 → isar-1.25.4}/src/isar/config/predefined_mission_definition/default_exr.json +0 -0
  108. {isar-1.25.2 → isar-1.25.4}/src/isar/config/predefined_mission_definition/default_mission.json +0 -0
  109. {isar-1.25.2 → isar-1.25.4}/src/isar/config/predefined_mission_definition/default_turtlebot.json +0 -0
  110. {isar-1.25.2/src/isar/mission_planner → isar-1.25.4/src/isar/config/predefined_missions}/__init__.py +0 -0
  111. {isar-1.25.2 → isar-1.25.4}/src/isar/config/predefined_missions/default.json +0 -0
  112. {isar-1.25.2 → isar-1.25.4}/src/isar/config/predefined_missions/default_turtlebot.json +0 -0
  113. {isar-1.25.2 → isar-1.25.4}/src/isar/config/settings.env +0 -0
  114. {isar-1.25.2 → isar-1.25.4}/src/isar/config/settings.py +0 -0
  115. {isar-1.25.2/src/isar/models → isar-1.25.4/src/isar/mission_planner}/__init__.py +0 -0
  116. {isar-1.25.2 → isar-1.25.4}/src/isar/mission_planner/local_planner.py +0 -0
  117. {isar-1.25.2 → isar-1.25.4}/src/isar/mission_planner/mission_planner_interface.py +0 -0
  118. {isar-1.25.2/src/isar/models/communication → isar-1.25.4/src/isar/models}/__init__.py +0 -0
  119. {isar-1.25.2/src/isar/models/mission_metadata → isar-1.25.4/src/isar/models/communication}/__init__.py +0 -0
  120. {isar-1.25.2 → isar-1.25.4}/src/isar/models/communication/message.py +0 -0
  121. {isar-1.25.2/src/isar/services → isar-1.25.4/src/isar/models/communication/queues}/__init__.py +0 -0
  122. {isar-1.25.2 → isar-1.25.4}/src/isar/models/communication/queues/queue_io.py +0 -0
  123. {isar-1.25.2 → isar-1.25.4}/src/isar/models/communication/queues/queue_timeout_error.py +0 -0
  124. {isar-1.25.2 → isar-1.25.4}/src/isar/models/communication/queues/queues.py +0 -0
  125. {isar-1.25.2/src/isar/services/auth → isar-1.25.4/src/isar/models/mission_metadata}/__init__.py +0 -0
  126. {isar-1.25.2/src/isar/services/service_connections → isar-1.25.4/src/isar/services}/__init__.py +0 -0
  127. {isar-1.25.2/src/isar/services/service_connections/mqtt → isar-1.25.4/src/isar/services/auth}/__init__.py +0 -0
  128. {isar-1.25.2 → isar-1.25.4}/src/isar/services/auth/azure_credentials.py +0 -0
  129. {isar-1.25.2/src/isar/services/service_connections/stid → isar-1.25.4/src/isar/services/service_connections}/__init__.py +0 -0
  130. {isar-1.25.2/src/isar/services/utilities → isar-1.25.4/src/isar/services/service_connections/mqtt}/__init__.py +0 -0
  131. {isar-1.25.2 → isar-1.25.4}/src/isar/services/service_connections/mqtt/mqtt_client.py +0 -0
  132. {isar-1.25.2 → isar-1.25.4}/src/isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +0 -0
  133. {isar-1.25.2 → isar-1.25.4}/src/isar/services/service_connections/mqtt/robot_info_publisher.py +0 -0
  134. {isar-1.25.2 → isar-1.25.4}/src/isar/services/service_connections/request_handler.py +0 -0
  135. {isar-1.25.2/src/isar/state_machine → isar-1.25.4/src/isar/services/service_connections/stid}/__init__.py +0 -0
  136. {isar-1.25.2/src/isar/storage → isar-1.25.4/src/isar/services/utilities}/__init__.py +0 -0
  137. {isar-1.25.2 → isar-1.25.4}/src/isar/services/utilities/queue_utilities.py +0 -0
  138. {isar-1.25.2 → isar-1.25.4}/src/isar/services/utilities/robot_utilities.py +0 -0
  139. {isar-1.25.2 → isar-1.25.4}/src/isar/services/utilities/threaded_request.py +0 -0
  140. {isar-1.25.2/src/robot_interface → isar-1.25.4/src/isar/state_machine}/__init__.py +0 -0
  141. {isar-1.25.2/src/robot_interface/models → isar-1.25.4/src/isar/state_machine/states}/__init__.py +0 -0
  142. {isar-1.25.2 → isar-1.25.4}/src/isar/state_machine/states/initialize.py +0 -0
  143. {isar-1.25.2 → isar-1.25.4}/src/isar/state_machine/states/initiate.py +0 -0
  144. {isar-1.25.2 → isar-1.25.4}/src/isar/state_machine/states/off.py +0 -0
  145. {isar-1.25.2 → isar-1.25.4}/src/isar/state_machine/states/offline.py +0 -0
  146. {isar-1.25.2 → isar-1.25.4}/src/isar/state_machine/states/paused.py +0 -0
  147. {isar-1.25.2 → isar-1.25.4}/src/isar/state_machine/states/stop.py +0 -0
  148. {isar-1.25.2/src/robot_interface/models/exceptions → isar-1.25.4/src/isar/storage}/__init__.py +0 -0
  149. {isar-1.25.2 → isar-1.25.4}/src/isar/storage/local_storage.py +0 -0
  150. {isar-1.25.2 → isar-1.25.4}/src/isar/storage/slimm_storage.py +0 -0
  151. {isar-1.25.2 → isar-1.25.4}/src/isar/storage/storage_interface.py +0 -0
  152. {isar-1.25.2 → isar-1.25.4}/src/isar/storage/utilities.py +0 -0
  153. {isar-1.25.2 → isar-1.25.4}/src/isar.egg-info/dependency_links.txt +0 -0
  154. {isar-1.25.2 → isar-1.25.4}/src/isar.egg-info/entry_points.txt +0 -0
  155. {isar-1.25.2 → isar-1.25.4}/src/isar.egg-info/top_level.txt +0 -0
  156. {isar-1.25.2/src/robot_interface/models/mission → isar-1.25.4/src/robot_interface}/__init__.py +0 -0
  157. {isar-1.25.2/src/robot_interface/models/robots → isar-1.25.4/src/robot_interface/models}/__init__.py +0 -0
  158. {isar-1.25.2/src/robot_interface/telemetry → isar-1.25.4/src/robot_interface/models/exceptions}/__init__.py +0 -0
  159. {isar-1.25.2/src/robot_interface/utilities → isar-1.25.4/src/robot_interface/models/initialize}/__init__.py +0 -0
  160. {isar-1.25.2 → isar-1.25.4}/src/robot_interface/models/initialize/initialize_params.py +0 -0
  161. {isar-1.25.2/tests → isar-1.25.4/src/robot_interface/models/inspection}/__init__.py +0 -0
  162. {isar-1.25.2 → isar-1.25.4}/src/robot_interface/models/inspection/inspection.py +0 -0
  163. {isar-1.25.2/tests/integration → isar-1.25.4/src/robot_interface/models/mission}/__init__.py +0 -0
  164. {isar-1.25.2 → isar-1.25.4}/src/robot_interface/models/mission/mission.py +0 -0
  165. {isar-1.25.2/tests/integration/turtlebot → isar-1.25.4/src/robot_interface/models/robots}/__init__.py +0 -0
  166. {isar-1.25.2/tests/integration/turtlebot/config → isar-1.25.4/src/robot_interface/telemetry}/__init__.py +0 -0
  167. {isar-1.25.2 → isar-1.25.4}/src/robot_interface/telemetry/mqtt_client.py +0 -0
  168. {isar-1.25.2 → isar-1.25.4}/src/robot_interface/test_robot_interface.py +0 -0
  169. {isar-1.25.2/tests/integration/turtlebot/config/maps → isar-1.25.4/src/robot_interface/utilities}/__init__.py +0 -0
  170. {isar-1.25.2 → isar-1.25.4}/src/robot_interface/utilities/json_service.py +0 -0
  171. {isar-1.25.2 → isar-1.25.4}/src/robot_interface/utilities/uuid_string_factory.py +0 -0
  172. {isar-1.25.2/tests/integration/turtlebot/config/missions → isar-1.25.4/tests}/__init__.py +0 -0
  173. {isar-1.25.2/tests/isar → isar-1.25.4/tests/integration}/__init__.py +0 -0
  174. {isar-1.25.2/tests/isar/apis → isar-1.25.4/tests/integration/turtlebot}/__init__.py +0 -0
  175. {isar-1.25.2/tests/isar/apis/models → isar-1.25.4/tests/integration/turtlebot/config}/__init__.py +0 -0
  176. {isar-1.25.2/tests/isar/apis/scheduler → isar-1.25.4/tests/integration/turtlebot/config/maps}/__init__.py +0 -0
  177. {isar-1.25.2 → isar-1.25.4}/tests/integration/turtlebot/config/maps/turtleworld.json +0 -0
  178. {isar-1.25.2/tests/isar/apis/security → isar-1.25.4/tests/integration/turtlebot/config/missions}/__init__.py +0 -0
  179. {isar-1.25.2 → isar-1.25.4}/tests/integration/turtlebot/test_successful_mission.py +0 -0
  180. {isar-1.25.2/tests/isar/mission → isar-1.25.4/tests/isar}/__init__.py +0 -0
  181. {isar-1.25.2/tests/isar/models → isar-1.25.4/tests/isar/apis}/__init__.py +0 -0
  182. {isar-1.25.2/tests/isar/models/communication → isar-1.25.4/tests/isar/apis/models}/__init__.py +0 -0
  183. {isar-1.25.2 → isar-1.25.4}/tests/isar/apis/models/example_mission_definition.json +0 -0
  184. {isar-1.25.2 → isar-1.25.4}/tests/isar/apis/models/test_start_mission_definition.py +0 -0
  185. {isar-1.25.2/tests/isar/services → isar-1.25.4/tests/isar/apis/scheduler}/__init__.py +0 -0
  186. {isar-1.25.2 → isar-1.25.4}/tests/isar/apis/scheduler/test_scheduler_router.py +0 -0
  187. {isar-1.25.2/tests/isar/services/readers → isar-1.25.4/tests/isar/apis/security}/__init__.py +0 -0
  188. {isar-1.25.2 → isar-1.25.4}/tests/isar/apis/security/test_authentication.py +0 -0
  189. {isar-1.25.2/tests/isar/services/service_connections → isar-1.25.4/tests/isar/mission}/__init__.py +0 -0
  190. {isar-1.25.2 → isar-1.25.4}/tests/isar/mission/test_mission.py +0 -0
  191. {isar-1.25.2/tests/isar/services/service_connections/echo → isar-1.25.4/tests/isar/models}/__init__.py +0 -0
  192. {isar-1.25.2/tests/isar/services/utilities → isar-1.25.4/tests/isar/models/communication}/__init__.py +0 -0
  193. {isar-1.25.2/tests/isar/state_machine → isar-1.25.4/tests/isar/services}/__init__.py +0 -0
  194. {isar-1.25.2/tests/isar/state_machine/states → isar-1.25.4/tests/isar/services/readers}/__init__.py +0 -0
  195. {isar-1.25.2 → isar-1.25.4}/tests/isar/services/readers/test_mission_reader.py +0 -0
  196. {isar-1.25.2/tests/mocks → isar-1.25.4/tests/isar/services/service_connections}/__init__.py +0 -0
  197. {isar-1.25.2 → isar-1.25.4}/tests/isar/services/service_connections/test_base_request_handler.py +0 -0
  198. {isar-1.25.2 → isar-1.25.4}/tests/isar/services/utilities/test_queue_utilities.py +0 -0
  199. {isar-1.25.2 → isar-1.25.4}/tests/isar/state_machine/states/test_monitor.py +0 -0
  200. {isar-1.25.2 → isar-1.25.4}/tests/isar/storage/test_blob_storage.py +0 -0
  201. {isar-1.25.2 → isar-1.25.4}/tests/isar/storage/test_uploader.py +0 -0
  202. {isar-1.25.2 → isar-1.25.4}/tests/mocks/blob_storage.py +0 -0
  203. {isar-1.25.2 → isar-1.25.4}/tests/mocks/mission_definition.py +0 -0
  204. {isar-1.25.2 → isar-1.25.4}/tests/mocks/mqtt_client.py +0 -0
  205. {isar-1.25.2 → isar-1.25.4}/tests/mocks/pose.py +0 -0
  206. {isar-1.25.2 → isar-1.25.4}/tests/mocks/request.py +0 -0
  207. {isar-1.25.2 → isar-1.25.4}/tests/mocks/status.py +0 -0
  208. {isar-1.25.2 → isar-1.25.4}/tests/mocks/task.py +0 -0
  209. {isar-1.25.2 → isar-1.25.4}/tests/mocks/token.py +0 -0
  210. {isar-1.25.2 → isar-1.25.4}/tests/test_data/test_map_config/test_map_config.json +0 -0
  211. {isar-1.25.2 → isar-1.25.4}/tests/test_data/test_mission_working.json +0 -0
  212. {isar-1.25.2 → isar-1.25.4}/tests/test_data/test_mission_working_no_tasks.json +0 -0
  213. {isar-1.25.2 → isar-1.25.4}/tests/test_data/test_thermal_image_mission.json +0 -0
  214. {isar-1.25.2 → isar-1.25.4}/tests/test_modules.py +0 -0
@@ -0,0 +1,11 @@
1
+ ## Ready for review checklist:
2
+ - [ ] A self-review has been performed
3
+ - [ ] All commits run individually
4
+ - [ ] Temporary changes have been removed, like logging, TODO, etc.
5
+ - [ ] The PR has been tested locally
6
+ - [ ] A test has been written
7
+ - [ ] This change doesn't need a new test
8
+ - [ ] Relevant issues are linked
9
+ - [ ] Remaining work is documented in issues
10
+ - [ ] There is no remaining work from this PR that requires new issues
11
+ - [ ] The changes do not introduce dead code as unused imports, functions etc.
@@ -44,7 +44,14 @@ jobs:
44
44
  env:
45
45
  ENVIRONMENT: local
46
46
 
47
- - name: Lint
47
+ - name: Run black
48
48
  run: |
49
49
  black --check .
50
- mypy .
50
+
51
+ - name: Run mypy
52
+ run: |
53
+ mypy --disable-error-code=import-untyped .
54
+
55
+ - name: Run ruff
56
+ run: |
57
+ ruff check .
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: isar
3
- Version: 1.25.2
3
+ Version: 1.25.4
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
@@ -127,9 +127,7 @@ Requires-Dist: transitions
127
127
  Requires-Dist: uvicorn
128
128
  Provides-Extra: dev
129
129
  Requires-Dist: black; extra == "dev"
130
- Requires-Dist: flake8; extra == "dev"
131
130
  Requires-Dist: mypy; extra == "dev"
132
- Requires-Dist: myst-parser; extra == "dev"
133
131
  Requires-Dist: pip-tools; extra == "dev"
134
132
  Requires-Dist: pre-commit; extra == "dev"
135
133
  Requires-Dist: pytest-dotenv; extra == "dev"
@@ -137,7 +135,7 @@ Requires-Dist: pytest-mock; extra == "dev"
137
135
  Requires-Dist: pytest-xdist; extra == "dev"
138
136
  Requires-Dist: pytest; extra == "dev"
139
137
  Requires-Dist: requests-mock; extra == "dev"
140
- Requires-Dist: sphinx; extra == "dev"
138
+ Requires-Dist: ruff; extra == "dev"
141
139
 
142
140
  # ISAR
143
141
 
@@ -60,9 +60,7 @@ repository = "https://github.com/equinor/isar.git"
60
60
  [project.optional-dependencies]
61
61
  dev = [
62
62
  "black",
63
- "flake8",
64
63
  "mypy",
65
- "myst-parser",
66
64
  "pip-tools",
67
65
  "pre-commit",
68
66
  "pytest-dotenv",
@@ -70,7 +68,7 @@ dev = [
70
68
  "pytest-xdist",
71
69
  "pytest",
72
70
  "requests-mock",
73
- "sphinx",
71
+ "ruff",
74
72
  ]
75
73
 
76
74
  [tool.setuptools_scm]
@@ -8,7 +8,7 @@ alitra==1.1.3
8
8
  # via isar (pyproject.toml)
9
9
  annotated-types==0.7.0
10
10
  # via pydantic
11
- anyio==4.7.0
11
+ anyio==4.8.0
12
12
  # via
13
13
  # httpx
14
14
  # starlette
@@ -30,16 +30,16 @@ backoff==2.2.1
30
30
  # via isar (pyproject.toml)
31
31
  cachetools==5.5.0
32
32
  # via google-auth
33
- certifi==2024.8.30
33
+ certifi==2024.12.14
34
34
  # via
35
35
  # httpcore
36
36
  # httpx
37
37
  # requests
38
38
  cffi==1.17.1
39
39
  # via cryptography
40
- charset-normalizer==3.4.0
40
+ charset-normalizer==3.4.1
41
41
  # via requests
42
- click==8.1.7
42
+ click==8.1.8
43
43
  # via
44
44
  # isar (pyproject.toml)
45
45
  # uvicorn
@@ -62,7 +62,7 @@ fastapi-azure-auth==5.0.1
62
62
  # via isar (pyproject.toml)
63
63
  google-api-core==2.24.0
64
64
  # via opencensus
65
- google-auth==2.36.0
65
+ google-auth==2.37.0
66
66
  # via google-api-core
67
67
  googleapis-common-protos==1.66.0
68
68
  # via google-api-core
@@ -91,7 +91,7 @@ msal==1.31.1
91
91
  # msal-extensions
92
92
  msal-extensions==1.2.0
93
93
  # via azure-identity
94
- numpy==2.2.0
94
+ numpy==2.2.1
95
95
  # via
96
96
  # alitra
97
97
  # isar (pyproject.toml)
@@ -103,7 +103,7 @@ opencensus==0.11.4
103
103
  # opencensus-ext-requests
104
104
  opencensus-context==0.1.3
105
105
  # via opencensus
106
- opencensus-ext-azure==1.1.13
106
+ opencensus-ext-azure==1.1.14
107
107
  # via isar (pyproject.toml)
108
108
  opencensus-ext-logging==0.1.1
109
109
  # via isar (pyproject.toml)
@@ -115,12 +115,12 @@ portalocker==2.10.1
115
115
  # via msal-extensions
116
116
  proto-plus==1.25.0
117
117
  # via google-api-core
118
- protobuf==5.29.1
118
+ protobuf==5.29.3
119
119
  # via
120
120
  # google-api-core
121
121
  # googleapis-common-protos
122
122
  # proto-plus
123
- psutil==6.1.0
123
+ psutil==6.1.1
124
124
  # via opencensus-ext-azure
125
125
  pyasn1==0.6.1
126
126
  # via
@@ -130,14 +130,14 @@ pyasn1-modules==0.4.1
130
130
  # via google-auth
131
131
  pycparser==2.22
132
132
  # via cffi
133
- pydantic==2.10.3
133
+ pydantic==2.10.5
134
134
  # via
135
135
  # fastapi
136
136
  # isar (pyproject.toml)
137
137
  # pydantic-settings
138
- pydantic-core==2.27.1
138
+ pydantic-core==2.27.2
139
139
  # via pydantic
140
- pydantic-settings==2.6.1
140
+ pydantic-settings==2.7.1
141
141
  # via isar (pyproject.toml)
142
142
  pyjwt[crypto]==2.10.1
143
143
  # via
@@ -163,7 +163,7 @@ requests-toolbelt==1.0.0
163
163
  # via isar (pyproject.toml)
164
164
  rsa==4.9
165
165
  # via google-auth
166
- scipy==1.14.1
166
+ scipy==1.15.1
167
167
  # via alitra
168
168
  six==1.17.0
169
169
  # via
@@ -186,9 +186,9 @@ typing-extensions==4.12.2
186
186
  # fastapi
187
187
  # pydantic
188
188
  # pydantic-core
189
- urllib3==2.2.3
189
+ urllib3==2.3.0
190
190
  # via requests
191
- uvicorn==0.32.1
191
+ uvicorn==0.34.0
192
192
  # via isar (pyproject.toml)
193
- wrapt==1.17.0
193
+ wrapt==1.17.1
194
194
  # via opencensus-ext-requests
@@ -23,18 +23,18 @@ from robot_interface.models.mission.task import (
23
23
 
24
24
 
25
25
  class InspectionTypes(str, Enum):
26
- image: str = "Image"
27
- thermal_image: str = "ThermalImage"
28
- video: str = "Video"
29
- thermal_video: str = "ThermalVideo"
30
- audio: str = "Audio"
26
+ image = "Image"
27
+ thermal_image = "ThermalImage"
28
+ video = "Video"
29
+ thermal_video = "ThermalVideo"
30
+ audio = "Audio"
31
31
 
32
32
 
33
33
  class TaskType(str, Enum):
34
- Inspection: str = "inspection"
35
- Localization: str = "localization"
36
- ReturnToHome: str = "return_to_home"
37
- Dock: str = "dock"
34
+ Inspection = "inspection"
35
+ Localization = "localization"
36
+ ReturnToHome = "return_to_home"
37
+ Dock = "dock"
38
38
 
39
39
 
40
40
  class StartMissionInspectionDefinition(BaseModel):
@@ -2,9 +2,7 @@ import logging
2
2
 
3
3
  from injector import inject
4
4
 
5
- from isar.apis.models.models import (
6
- RobotInfoResponse,
7
- )
5
+ from isar.apis.models.models import RobotInfoResponse
8
6
  from isar.config.settings import robot_settings, settings
9
7
  from isar.services.utilities.robot_utilities import RobotUtilities
10
8
 
@@ -6,11 +6,11 @@ from alitra import Pose
6
6
  from fastapi import Body, HTTPException, Path
7
7
  from injector import inject
8
8
 
9
- from isar.apis.models import InputPose, StartMissionResponse
10
9
  from isar.apis.models.models import (
11
10
  ControlMissionResponse,
12
- RobotInfoResponse,
13
11
  TaskResponse,
12
+ InputPose,
13
+ StartMissionResponse,
14
14
  )
15
15
  from isar.apis.models.start_mission_definition import (
16
16
  StartMissionDefinition,
@@ -4,7 +4,7 @@ from isar.mission_planner.task_selector_interface import (
4
4
  TaskSelectorInterface,
5
5
  TaskSelectorStop,
6
6
  )
7
- from robot_interface.models.mission.task import TASKS, Task
7
+ from robot_interface.models.mission.task import TASKS
8
8
 
9
9
 
10
10
  class SequentialTaskSelector(TaskSelectorInterface):
@@ -1,7 +1,7 @@
1
1
  from abc import ABCMeta, abstractmethod
2
2
  from typing import List
3
3
 
4
- from robot_interface.models.mission.task import TASKS, Task
4
+ from robot_interface.models.mission.task import TASKS
5
5
 
6
6
 
7
7
  class TaskSelectorInterface(metaclass=ABCMeta):
@@ -11,8 +11,8 @@ class StatusQueue(Queue):
11
11
  if not self._qsize():
12
12
  raise Empty
13
13
  with self.mutex:
14
- l = list(self.queue)
15
- return l.pop()
14
+ queueList = list(self.queue)
15
+ return queueList.pop()
16
16
 
17
17
  def update(self, item: Any):
18
18
  with self.mutex:
@@ -7,8 +7,8 @@ from typing import Dict, List, Tuple, Union
7
7
  from injector import Injector, Module, multiprovider, provider, singleton
8
8
 
9
9
  from isar.apis.api import API
10
- from isar.apis.schedule.scheduling_controller import SchedulingController
11
10
  from isar.apis.robot_control.robot_controller import RobotController
11
+ from isar.apis.schedule.scheduling_controller import SchedulingController
12
12
  from isar.apis.security.authentication import Authenticator
13
13
  from isar.config.keyvault.keyvault_service import Keyvault
14
14
  from isar.config.settings import settings
@@ -18,8 +18,8 @@ from isar.mission_planner.sequential_task_selector import SequentialTaskSelector
18
18
  from isar.mission_planner.task_selector_interface import TaskSelectorInterface
19
19
  from isar.models.communication.queues.queues import Queues
20
20
  from isar.services.service_connections.request_handler import RequestHandler
21
- from isar.services.utilities.scheduling_utilities import SchedulingUtilities
22
21
  from isar.services.utilities.robot_utilities import RobotUtilities
22
+ from isar.services.utilities.scheduling_utilities import SchedulingUtilities
23
23
  from isar.state_machine.state_machine import StateMachine
24
24
  from isar.storage.blob_storage import BlobStorage
25
25
  from isar.storage.local_storage import LocalStorage
@@ -70,9 +70,10 @@ class RobotModule(Module):
70
70
  @provider
71
71
  @singleton
72
72
  def provide_robot_interface(self) -> RobotInterface:
73
- robot_package_name: str = settings.ROBOT_PACKAGE
74
- robot: ModuleType = import_module(robot_package_name)
75
- return robot.robotinterface.Robot() # type: ignore
73
+ robot_interface: ModuleType = import_module(
74
+ f"{settings.ROBOT_PACKAGE}.robotinterface"
75
+ )
76
+ return robot_interface.Robot() # type: ignore
76
77
 
77
78
 
78
79
  class QueuesModule(Module):
@@ -66,7 +66,7 @@ def print_startup_info():
66
66
  print_setting(fillchar="-")
67
67
  print_setting("Robot package", settings.ROBOT_PACKAGE)
68
68
  print_setting("Robot name", settings.ROBOT_NAME)
69
- print_setting("Run mission stepwise", settings.RUN_MISSION_BY_TASK)
69
+ print_setting("Run mission taskwise", settings.RUN_MISSION_BY_TASK)
70
70
  print_setting("Running on port", settings.API_PORT)
71
71
  print_setting("Mission planner", settings.MISSION_PLANNER)
72
72
  print_setting("Using local storage", settings.STORAGE_LOCAL_ENABLED)
@@ -17,7 +17,11 @@ from isar.mission_planner.mission_planner_interface import (
17
17
  MissionPlannerInterface,
18
18
  )
19
19
  from isar.models.communication.message import StartMissionMessage
20
- from isar.models.communication.queues import QueueIO, Queues, QueueTimeoutError
20
+ from isar.models.communication.queues.queue_io import QueueIO
21
+ from isar.models.communication.queues.queues import Queues
22
+ from isar.models.communication.queues.queue_timeout_error import (
23
+ QueueTimeoutError,
24
+ )
21
25
  from isar.services.utilities.queue_utilities import QueueUtilities
22
26
  from isar.state_machine.states_enum import States
23
27
  from robot_interface.models.mission.mission import Mission
@@ -101,7 +105,7 @@ class SchedulingUtilities:
101
105
  is_capable: bool = True
102
106
  missing_capabilities: Set[str] = set()
103
107
  for task in mission.tasks:
104
- if not task.type in robot_capabilities:
108
+ if task.type not in robot_capabilities:
105
109
  is_capable = False
106
110
  missing_capabilities.add(task.type)
107
111
 
@@ -18,16 +18,15 @@ from isar.mission_planner.task_selector_interface import (
18
18
  )
19
19
  from isar.models.communication.message import StartMissionMessage
20
20
  from isar.models.communication.queues.queues import Queues
21
- from isar.state_machine.states import (
22
- Idle,
23
- Initialize,
24
- Initiate,
25
- Monitor,
26
- Off,
27
- Offline,
28
- Paused,
29
- Stop,
30
- )
21
+ from isar.state_machine.states.idle import Idle
22
+ from isar.state_machine.states.initialize import Initialize
23
+ from isar.state_machine.states.initiate import Initiate
24
+ from isar.state_machine.states.monitor import Monitor
25
+ from isar.state_machine.states.off import Off
26
+ from isar.state_machine.states.offline import Offline
27
+ from isar.state_machine.states.blocked_protective_stop import BlockedProtectiveStop
28
+ from isar.state_machine.states.paused import Paused
29
+ from isar.state_machine.states.stop import Stop
31
30
  from isar.state_machine.states_enum import States
32
31
  from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
33
32
  from robot_interface.models.initialize.initialize_params import InitializeParams
@@ -36,6 +35,11 @@ from robot_interface.models.mission.status import MissionStatus, RobotStatus, Ta
36
35
  from robot_interface.models.mission.task import TASKS
37
36
  from robot_interface.robot_interface import RobotInterface
38
37
  from robot_interface.telemetry.mqtt_client import MqttClientInterface
38
+ from robot_interface.telemetry.payloads import (
39
+ RobotStatusPayload,
40
+ MissionPayload,
41
+ TaskPayload,
42
+ )
39
43
  from robot_interface.utilities.json_service import EnhancedJSONEncoder
40
44
 
41
45
 
@@ -89,6 +93,7 @@ class StateMachine(object):
89
93
  self.initiate_state: State = Initiate(self)
90
94
  self.off_state: State = Off(self)
91
95
  self.offline_state: State = Offline(self)
96
+ self.blocked_protective_stop: State = BlockedProtectiveStop(self)
92
97
 
93
98
  self.states: List[State] = [
94
99
  self.off_state,
@@ -99,6 +104,7 @@ class StateMachine(object):
99
104
  self.stop_state,
100
105
  self.paused_state,
101
106
  self.offline_state,
107
+ self.blocked_protective_stop,
102
108
  ]
103
109
 
104
110
  self.machine = Machine(self, states=self.states, initial="off", queued=True)
@@ -224,6 +230,18 @@ class StateMachine(object):
224
230
  "dest": self.idle_state,
225
231
  "before": self._online,
226
232
  },
233
+ {
234
+ "trigger": "robot_protective_stop_engaged",
235
+ "source": [self.idle_state],
236
+ "dest": self.blocked_protective_stop,
237
+ "before": self._protective_stop_engaged,
238
+ },
239
+ {
240
+ "trigger": "robot_protective_stop_disengaged",
241
+ "source": self.blocked_protective_stop,
242
+ "dest": self.idle_state,
243
+ "before": self._protective_stop_disengaged,
244
+ },
227
245
  ]
228
246
  )
229
247
 
@@ -272,6 +290,12 @@ class StateMachine(object):
272
290
  def _online(self) -> None:
273
291
  return
274
292
 
293
+ def _protective_stop_engaged(self) -> None:
294
+ return
295
+
296
+ def _protective_stop_disengaged(self) -> None:
297
+ return
298
+
275
299
  def _resume(self) -> None:
276
300
  self.logger.info(f"Resuming mission: {self.current_mission.id}")
277
301
  self.current_mission.status = MissionStatus.InProgress
@@ -484,24 +508,22 @@ class StateMachine(object):
484
508
  if self.current_mission:
485
509
  if self.current_mission.error_message:
486
510
  error_message = self.current_mission.error_message
487
- payload: str = json.dumps(
488
- {
489
- "isar_id": settings.ISAR_ID,
490
- "robot_name": settings.ROBOT_NAME,
491
- "mission_id": self.current_mission.id if self.current_mission else None,
492
- "status": self.current_mission.status if self.current_mission else None,
493
- "error_reason": error_message.error_reason if error_message else None,
494
- "error_description": (
495
- error_message.error_description if error_message else None
496
- ),
497
- "timestamp": datetime.now(timezone.utc),
498
- },
499
- cls=EnhancedJSONEncoder,
511
+
512
+ payload: MissionPayload = MissionPayload(
513
+ isar_id=settings.ISAR_ID,
514
+ robot_name=settings.ROBOT_NAME,
515
+ mission_id=self.current_mission.id if self.current_mission else None,
516
+ status=self.current_mission.status if self.current_mission else None,
517
+ error_reason=error_message.error_reason if error_message else None,
518
+ error_description=(
519
+ error_message.error_description if error_message else None
520
+ ),
521
+ timestamp=datetime.now(timezone.utc),
500
522
  )
501
523
 
502
524
  self.mqtt_publisher.publish(
503
525
  topic=settings.TOPIC_ISAR_MISSION,
504
- payload=payload,
526
+ payload=json.dumps(payload, cls=EnhancedJSONEncoder),
505
527
  qos=1,
506
528
  retain=True,
507
529
  )
@@ -516,26 +538,23 @@ class StateMachine(object):
516
538
  if task.error_message:
517
539
  error_message = task.error_message
518
540
 
519
- payload: str = json.dumps(
520
- {
521
- "isar_id": settings.ISAR_ID,
522
- "robot_name": settings.ROBOT_NAME,
523
- "mission_id": self.current_mission.id if self.current_mission else None,
524
- "task_id": task.id if task else None,
525
- "status": task.status if task else None,
526
- "task_type": task.type,
527
- "error_reason": error_message.error_reason if error_message else None,
528
- "error_description": (
529
- error_message.error_description if error_message else None
530
- ),
531
- "timestamp": datetime.now(timezone.utc),
532
- },
533
- cls=EnhancedJSONEncoder,
541
+ payload: TaskPayload = TaskPayload(
542
+ isar_id=settings.ISAR_ID,
543
+ robot_name=settings.ROBOT_NAME,
544
+ mission_id=self.current_mission.id if self.current_mission else None,
545
+ task_id=task.id if task else None,
546
+ status=task.status if task else None,
547
+ task_type=task.type if task else None,
548
+ error_reason=error_message.error_reason if error_message else None,
549
+ error_description=(
550
+ error_message.error_description if error_message else None
551
+ ),
552
+ timestamp=datetime.now(timezone.utc),
534
553
  )
535
554
 
536
555
  self.mqtt_publisher.publish(
537
556
  topic=settings.TOPIC_ISAR_TASK,
538
- payload=payload,
557
+ payload=json.dumps(payload, cls=EnhancedJSONEncoder),
539
558
  qos=1,
540
559
  retain=True,
541
560
  )
@@ -543,19 +562,17 @@ class StateMachine(object):
543
562
  def publish_status(self) -> None:
544
563
  if not self.mqtt_publisher:
545
564
  return
546
- payload: str = json.dumps(
547
- {
548
- "isar_id": settings.ISAR_ID,
549
- "robot_name": settings.ROBOT_NAME,
550
- "status": self._current_status(),
551
- "timestamp": datetime.now(timezone.utc),
552
- },
553
- cls=EnhancedJSONEncoder,
565
+
566
+ payload: RobotStatusPayload = RobotStatusPayload(
567
+ isar_id=settings.ISAR_ID,
568
+ robot_name=settings.ROBOT_NAME,
569
+ status=self._current_status(),
570
+ timestamp=datetime.now(timezone.utc),
554
571
  )
555
572
 
556
573
  self.mqtt_publisher.publish(
557
574
  topic=settings.TOPIC_ISAR_STATUS,
558
- payload=payload,
575
+ payload=json.dumps(payload, cls=EnhancedJSONEncoder),
559
576
  qos=1,
560
577
  retain=True,
561
578
  )
@@ -565,6 +582,8 @@ class StateMachine(object):
565
582
  return RobotStatus.Available
566
583
  elif self.current_state == States.Offline:
567
584
  return RobotStatus.Offline
585
+ elif self.current_state == States.BlockedProtectiveStop:
586
+ return RobotStatus.BlockedProtectiveStop
568
587
  else:
569
588
  return RobotStatus.Busy
570
589
 
@@ -0,0 +1,65 @@
1
+ import logging
2
+ import time
3
+ from typing import TYPE_CHECKING, Optional
4
+
5
+ from transitions import State
6
+
7
+ from isar.config.settings import settings
8
+ from isar.services.utilities.threaded_request import (
9
+ ThreadedRequest,
10
+ ThreadedRequestNotFinishedError,
11
+ )
12
+ from robot_interface.models.exceptions.robot_exceptions import RobotException
13
+ from robot_interface.models.mission.status import RobotStatus
14
+
15
+ if TYPE_CHECKING:
16
+ from isar.state_machine.state_machine import StateMachine
17
+
18
+
19
+ class BlockedProtectiveStop(State):
20
+ def __init__(self, state_machine: "StateMachine") -> None:
21
+ super().__init__(
22
+ name="blocked_protective_stop", on_enter=self.start, on_exit=self.stop
23
+ )
24
+ self.state_machine: "StateMachine" = state_machine
25
+ self.logger = logging.getLogger("state_machine")
26
+ self.robot_status_thread: Optional[ThreadedRequest] = None
27
+
28
+ def start(self) -> None:
29
+ self.state_machine.update_state()
30
+ self._run()
31
+
32
+ def stop(self) -> None:
33
+ if self.robot_status_thread:
34
+ self.robot_status_thread.wait_for_thread()
35
+ self.robot_status_thread = None
36
+
37
+ def _run(self) -> None:
38
+ while True:
39
+ if not self.robot_status_thread:
40
+ self.robot_status_thread = ThreadedRequest(
41
+ request_func=self.state_machine.robot.robot_status
42
+ )
43
+ self.robot_status_thread.start_thread(
44
+ name="State Machine BlockedProtectiveStop Get Robot Status"
45
+ )
46
+
47
+ try:
48
+ robot_status: RobotStatus = self.robot_status_thread.get_output()
49
+ except ThreadedRequestNotFinishedError:
50
+ time.sleep(self.state_machine.sleep_time)
51
+ continue
52
+
53
+ except RobotException as e:
54
+ self.logger.error(
55
+ f"Failed to get robot status because: {e.error_description}"
56
+ )
57
+
58
+ if robot_status != RobotStatus.BlockedProtectiveStop:
59
+ transition = self.state_machine.robot_protective_stop_disengaged # type: ignore
60
+ break
61
+
62
+ self.robot_status_thread = None
63
+ time.sleep(settings.ROBOT_API_STATUS_POLL_INTERVAL)
64
+
65
+ transition()
@@ -24,6 +24,7 @@ class Idle(State):
24
24
  self.logger = logging.getLogger("state_machine")
25
25
  self.robot_status_thread: Optional[ThreadedRequest] = None
26
26
  self.last_robot_status_poll_time: float = time.time()
27
+ self.status_checked_at_least_once: bool = False
27
28
 
28
29
  def start(self) -> None:
29
30
  self.state_machine.update_state()
@@ -33,8 +34,12 @@ class Idle(State):
33
34
  if self.robot_status_thread:
34
35
  self.robot_status_thread.wait_for_thread()
35
36
  self.robot_status_thread = None
37
+ self.status_checked_at_least_once = False
36
38
 
37
39
  def _is_ready_to_poll_for_status(self) -> bool:
40
+ if not self.status_checked_at_least_once:
41
+ return True
42
+
38
43
  time_since_last_robot_status_poll = (
39
44
  time.time() - self.last_robot_status_poll_time
40
45
  )
@@ -47,17 +52,19 @@ class Idle(State):
47
52
  if self.state_machine.should_stop_mission():
48
53
  transition = self.state_machine.stop # type: ignore
49
54
  break
50
- start_mission: Optional[StartMissionMessage] = (
51
- self.state_machine.should_start_mission()
52
- )
53
- if start_mission:
54
- self.state_machine.start_mission(
55
- mission=start_mission.mission,
56
- initial_pose=start_mission.initial_pose,
55
+
56
+ if self.status_checked_at_least_once:
57
+ start_mission: Optional[StartMissionMessage] = (
58
+ self.state_machine.should_start_mission()
57
59
  )
58
- transition = self.state_machine.mission_started # type: ignore
59
- break
60
- time.sleep(self.state_machine.sleep_time)
60
+ if start_mission:
61
+ self.state_machine.start_mission(
62
+ mission=start_mission.mission,
63
+ initial_pose=start_mission.initial_pose,
64
+ )
65
+ transition = self.state_machine.mission_started # type: ignore
66
+ break
67
+ time.sleep(self.state_machine.sleep_time)
61
68
 
62
69
  if not self._is_ready_to_poll_for_status():
63
70
  continue
@@ -72,6 +79,7 @@ class Idle(State):
72
79
 
73
80
  try:
74
81
  robot_status: RobotStatus = self.robot_status_thread.get_output()
82
+ self.status_checked_at_least_once = True
75
83
  except ThreadedRequestNotFinishedError:
76
84
  time.sleep(self.state_machine.sleep_time)
77
85
  continue
@@ -86,6 +94,9 @@ class Idle(State):
86
94
  if robot_status == RobotStatus.Offline:
87
95
  transition = self.state_machine.robot_turned_offline # type: ignore
88
96
  break
97
+ elif robot_status == RobotStatus.BlockedProtectiveStop:
98
+ transition = self.state_machine.robot_protective_stop_engaged # type: ignore
99
+ break
89
100
 
90
101
  self.robot_status_thread = None
91
102