isar 1.18.0__tar.gz → 1.19.1__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 (206) hide show
  1. {isar-1.18.0 → isar-1.19.1}/.github/workflows/project_automations.yml +5 -5
  2. {isar-1.18.0 → isar-1.19.1}/.github/workflows/publish_isar_base_image.yml +4 -4
  3. {isar-1.18.0 → isar-1.19.1}/.github/workflows/pythonpackage.yml +3 -3
  4. {isar-1.18.0 → isar-1.19.1}/.github/workflows/pythonpublish.yml +2 -2
  5. isar-1.19.1/.github/workflows/stale.yml +32 -0
  6. {isar-1.18.0 → isar-1.19.1}/Dockerfile +2 -2
  7. {isar-1.18.0 → isar-1.19.1}/PKG-INFO +13 -2
  8. {isar-1.18.0 → isar-1.19.1}/README.md +11 -0
  9. {isar-1.18.0 → isar-1.19.1}/main.py +0 -12
  10. {isar-1.18.0 → isar-1.19.1}/setup.py +1 -1
  11. isar-1.19.1/src/isar/__init__.py +6 -0
  12. {isar-1.18.0 → isar-1.19.1}/src/isar/config/log.py +4 -3
  13. {isar-1.18.0 → isar-1.19.1}/src/isar/config/settings.py +17 -15
  14. {isar-1.18.0 → isar-1.19.1}/src/isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +2 -2
  15. {isar-1.18.0 → isar-1.19.1}/src/isar/services/service_connections/mqtt/robot_info_publisher.py +2 -2
  16. {isar-1.18.0 → isar-1.19.1}/src/isar/state_machine/state_machine.py +52 -14
  17. {isar-1.18.0 → isar-1.19.1}/src/isar/state_machine/states/__init__.py +1 -0
  18. isar-1.19.1/src/isar/state_machine/states/idle.py +85 -0
  19. {isar-1.18.0 → isar-1.19.1}/src/isar/state_machine/states/monitor.py +3 -5
  20. isar-1.19.1/src/isar/state_machine/states/offline.py +62 -0
  21. {isar-1.18.0 → isar-1.19.1}/src/isar/state_machine/states_enum.py +1 -0
  22. {isar-1.18.0 → isar-1.19.1}/src/isar/storage/uploader.py +8 -7
  23. {isar-1.18.0 → isar-1.19.1}/src/isar/storage/utilities.py +3 -3
  24. {isar-1.18.0 → isar-1.19.1}/src/isar.egg-info/PKG-INFO +13 -2
  25. {isar-1.18.0 → isar-1.19.1}/src/isar.egg-info/SOURCES.txt +1 -1
  26. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/telemetry/mqtt_client.py +5 -7
  27. {isar-1.18.0 → isar-1.19.1}/tests/integration/turtlebot/test_successful_mission.py +3 -3
  28. {isar-1.18.0 → isar-1.19.1}/tests/isar/state_machine/test_state_machine.py +28 -1
  29. {isar-1.18.0 → isar-1.19.1}/tests/mocks/robot_interface.py +12 -0
  30. isar-1.18.0/.github/workflows/stale.yml +0 -33
  31. isar-1.18.0/src/isar/__init__.py +0 -6
  32. isar-1.18.0/src/isar/services/service_connections/mqtt/robot_status_publisher.py +0 -119
  33. isar-1.18.0/src/isar/state_machine/states/idle.py +0 -40
  34. {isar-1.18.0 → isar-1.19.1}/.dockerignore +0 -0
  35. {isar-1.18.0 → isar-1.19.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  36. {isar-1.18.0 → isar-1.19.1}/.github/ISSUE_TEMPLATE/feature.md +0 -0
  37. {isar-1.18.0 → isar-1.19.1}/.github/ISSUE_TEMPLATE/improvement.md +0 -0
  38. {isar-1.18.0 → isar-1.19.1}/.github/release.yml +0 -0
  39. {isar-1.18.0 → isar-1.19.1}/.gitignore +0 -0
  40. {isar-1.18.0 → isar-1.19.1}/.pre-commit-config.yaml +0 -0
  41. {isar-1.18.0 → isar-1.19.1}/LICENSE +0 -0
  42. {isar-1.18.0 → isar-1.19.1}/SECURITY.md +0 -0
  43. {isar-1.18.0 → isar-1.19.1}/docker-compose-turtlebot.yml +0 -0
  44. {isar-1.18.0 → isar-1.19.1}/docker-compose.yml +0 -0
  45. {isar-1.18.0 → isar-1.19.1}/docs/Makefile +0 -0
  46. {isar-1.18.0 → isar-1.19.1}/docs/make.bat +0 -0
  47. {isar-1.18.0 → isar-1.19.1}/docs/rst_processing.py +0 -0
  48. {isar-1.18.0 → isar-1.19.1}/docs/source/conf.py +0 -0
  49. {isar-1.18.0 → isar-1.19.1}/docs/source/index.rst +0 -0
  50. {isar-1.18.0 → isar-1.19.1}/docs/source/readme_link.md +0 -0
  51. {isar-1.18.0 → isar-1.19.1}/docs/state_machine_diagram.png +0 -0
  52. {isar-1.18.0 → isar-1.19.1}/pyproject.toml +0 -0
  53. {isar-1.18.0 → isar-1.19.1}/radixconfig.yml +0 -0
  54. {isar-1.18.0 → isar-1.19.1}/setup.cfg +0 -0
  55. {isar-1.18.0 → isar-1.19.1}/src/isar/apis/__init__.py +0 -0
  56. {isar-1.18.0 → isar-1.19.1}/src/isar/apis/api.py +0 -0
  57. {isar-1.18.0 → isar-1.19.1}/src/isar/apis/models/__init__.py +0 -0
  58. {isar-1.18.0 → isar-1.19.1}/src/isar/apis/models/models.py +0 -0
  59. {isar-1.18.0 → isar-1.19.1}/src/isar/apis/models/start_mission_definition.py +0 -0
  60. {isar-1.18.0 → isar-1.19.1}/src/isar/apis/schedule/__init__.py +0 -0
  61. {isar-1.18.0 → isar-1.19.1}/src/isar/apis/schedule/scheduling_controller.py +0 -0
  62. {isar-1.18.0 → isar-1.19.1}/src/isar/apis/security/__init__.py +0 -0
  63. {isar-1.18.0 → isar-1.19.1}/src/isar/apis/security/authentication.py +0 -0
  64. {isar-1.18.0 → isar-1.19.1}/src/isar/config/__init__.py +0 -0
  65. {isar-1.18.0 → isar-1.19.1}/src/isar/config/certs/ca-cert.pem +0 -0
  66. {isar-1.18.0 → isar-1.19.1}/src/isar/config/configuration_error.py +0 -0
  67. {isar-1.18.0 → isar-1.19.1}/src/isar/config/keyvault/__init__.py +0 -0
  68. {isar-1.18.0 → isar-1.19.1}/src/isar/config/keyvault/keyvault_error.py +0 -0
  69. {isar-1.18.0 → isar-1.19.1}/src/isar/config/keyvault/keyvault_service.py +0 -0
  70. {isar-1.18.0 → isar-1.19.1}/src/isar/config/logging.conf +0 -0
  71. {isar-1.18.0 → isar-1.19.1}/src/isar/config/maps/JSP1_intermediate_deck.json +0 -0
  72. {isar-1.18.0 → isar-1.19.1}/src/isar/config/maps/JSP1_weather_deck.json +0 -0
  73. {isar-1.18.0 → isar-1.19.1}/src/isar/config/maps/default_map.json +0 -0
  74. {isar-1.18.0 → isar-1.19.1}/src/isar/config/maps/klab_b.json +0 -0
  75. {isar-1.18.0 → isar-1.19.1}/src/isar/config/maps/klab_compressor.json +0 -0
  76. {isar-1.18.0 → isar-1.19.1}/src/isar/config/maps/klab_turtlebot.json +0 -0
  77. {isar-1.18.0 → isar-1.19.1}/src/isar/config/maps/turtleworld.json +0 -0
  78. {isar-1.18.0 → isar-1.19.1}/src/isar/config/predefined_mission_definition/__init__.py +0 -0
  79. {isar-1.18.0 → isar-1.19.1}/src/isar/config/predefined_mission_definition/default_exr.json +0 -0
  80. {isar-1.18.0 → isar-1.19.1}/src/isar/config/predefined_mission_definition/default_mission.json +0 -0
  81. {isar-1.18.0 → isar-1.19.1}/src/isar/config/predefined_mission_definition/default_turtlebot.json +0 -0
  82. {isar-1.18.0 → isar-1.19.1}/src/isar/config/predefined_missions/__init__.py +0 -0
  83. {isar-1.18.0 → isar-1.19.1}/src/isar/config/predefined_missions/default.json +0 -0
  84. {isar-1.18.0 → isar-1.19.1}/src/isar/config/predefined_missions/default_turtlebot.json +0 -0
  85. {isar-1.18.0 → isar-1.19.1}/src/isar/config/predefined_poses/__init__.py +0 -0
  86. {isar-1.18.0 → isar-1.19.1}/src/isar/config/predefined_poses/predefined_poses.py +0 -0
  87. {isar-1.18.0 → isar-1.19.1}/src/isar/config/settings.env +0 -0
  88. {isar-1.18.0 → isar-1.19.1}/src/isar/mission_planner/__init__.py +0 -0
  89. {isar-1.18.0 → isar-1.19.1}/src/isar/mission_planner/local_planner.py +0 -0
  90. {isar-1.18.0 → isar-1.19.1}/src/isar/mission_planner/mission_planner_interface.py +0 -0
  91. {isar-1.18.0 → isar-1.19.1}/src/isar/mission_planner/sequential_task_selector.py +0 -0
  92. {isar-1.18.0 → isar-1.19.1}/src/isar/mission_planner/task_selector_interface.py +0 -0
  93. {isar-1.18.0 → isar-1.19.1}/src/isar/models/__init__.py +0 -0
  94. {isar-1.18.0 → isar-1.19.1}/src/isar/models/communication/__init__.py +0 -0
  95. {isar-1.18.0 → isar-1.19.1}/src/isar/models/communication/message.py +0 -0
  96. {isar-1.18.0 → isar-1.19.1}/src/isar/models/communication/queues/__init__.py +0 -0
  97. {isar-1.18.0 → isar-1.19.1}/src/isar/models/communication/queues/queue_io.py +0 -0
  98. {isar-1.18.0 → isar-1.19.1}/src/isar/models/communication/queues/queue_timeout_error.py +0 -0
  99. {isar-1.18.0 → isar-1.19.1}/src/isar/models/communication/queues/queues.py +0 -0
  100. {isar-1.18.0 → isar-1.19.1}/src/isar/models/communication/queues/status_queue.py +0 -0
  101. {isar-1.18.0 → isar-1.19.1}/src/isar/models/mission_metadata/__init__.py +0 -0
  102. {isar-1.18.0 → isar-1.19.1}/src/isar/modules.py +0 -0
  103. {isar-1.18.0 → isar-1.19.1}/src/isar/services/__init__.py +0 -0
  104. {isar-1.18.0 → isar-1.19.1}/src/isar/services/auth/__init__.py +0 -0
  105. {isar-1.18.0 → isar-1.19.1}/src/isar/services/auth/azure_credentials.py +0 -0
  106. {isar-1.18.0 → isar-1.19.1}/src/isar/services/readers/__init__.py +0 -0
  107. {isar-1.18.0 → isar-1.19.1}/src/isar/services/readers/base_reader.py +0 -0
  108. {isar-1.18.0 → isar-1.19.1}/src/isar/services/service_connections/__init__.py +0 -0
  109. {isar-1.18.0 → isar-1.19.1}/src/isar/services/service_connections/mqtt/__init__.py +0 -0
  110. {isar-1.18.0 → isar-1.19.1}/src/isar/services/service_connections/mqtt/mqtt_client.py +0 -0
  111. {isar-1.18.0 → isar-1.19.1}/src/isar/services/service_connections/request_handler.py +0 -0
  112. {isar-1.18.0 → isar-1.19.1}/src/isar/services/service_connections/stid/__init__.py +0 -0
  113. {isar-1.18.0 → isar-1.19.1}/src/isar/services/utilities/__init__.py +0 -0
  114. {isar-1.18.0 → isar-1.19.1}/src/isar/services/utilities/queue_utilities.py +0 -0
  115. {isar-1.18.0 → isar-1.19.1}/src/isar/services/utilities/scheduling_utilities.py +0 -0
  116. {isar-1.18.0 → isar-1.19.1}/src/isar/services/utilities/threaded_request.py +0 -0
  117. {isar-1.18.0 → isar-1.19.1}/src/isar/state_machine/__init__.py +0 -0
  118. {isar-1.18.0 → isar-1.19.1}/src/isar/state_machine/states/initialize.py +0 -0
  119. {isar-1.18.0 → isar-1.19.1}/src/isar/state_machine/states/initiate.py +0 -0
  120. {isar-1.18.0 → isar-1.19.1}/src/isar/state_machine/states/off.py +0 -0
  121. {isar-1.18.0 → isar-1.19.1}/src/isar/state_machine/states/paused.py +0 -0
  122. {isar-1.18.0 → isar-1.19.1}/src/isar/state_machine/states/stop.py +0 -0
  123. {isar-1.18.0 → isar-1.19.1}/src/isar/storage/__init__.py +0 -0
  124. {isar-1.18.0 → isar-1.19.1}/src/isar/storage/blob_storage.py +0 -0
  125. {isar-1.18.0 → isar-1.19.1}/src/isar/storage/local_storage.py +0 -0
  126. {isar-1.18.0 → isar-1.19.1}/src/isar/storage/slimm_storage.py +0 -0
  127. {isar-1.18.0 → isar-1.19.1}/src/isar/storage/storage_interface.py +0 -0
  128. {isar-1.18.0 → isar-1.19.1}/src/isar.egg-info/dependency_links.txt +0 -0
  129. {isar-1.18.0 → isar-1.19.1}/src/isar.egg-info/requires.txt +0 -0
  130. {isar-1.18.0 → isar-1.19.1}/src/isar.egg-info/top_level.txt +0 -0
  131. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/__init__.py +0 -0
  132. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/__init__.py +0 -0
  133. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/exceptions/__init__.py +0 -0
  134. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/exceptions/robot_exceptions.py +0 -0
  135. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/initialize/__init__.py +0 -0
  136. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/initialize/initialize_params.py +0 -0
  137. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/inspection/__init__.py +0 -0
  138. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/inspection/inspection.py +0 -0
  139. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/mission/__init__.py +0 -0
  140. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/mission/mission.py +0 -0
  141. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/mission/status.py +0 -0
  142. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/mission/step.py +0 -0
  143. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/mission/task.py +0 -0
  144. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/robots/__init__.py +0 -0
  145. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/models/robots/robot_model.py +0 -0
  146. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/robot_interface.py +0 -0
  147. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/telemetry/__init__.py +0 -0
  148. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/telemetry/payloads.py +0 -0
  149. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/test_robot_interface.py +0 -0
  150. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/utilities/__init__.py +0 -0
  151. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/utilities/json_service.py +0 -0
  152. {isar-1.18.0 → isar-1.19.1}/src/robot_interface/utilities/uuid_string_factory.py +0 -0
  153. {isar-1.18.0 → isar-1.19.1}/tests/__init__.py +0 -0
  154. {isar-1.18.0 → isar-1.19.1}/tests/conftest.py +0 -0
  155. {isar-1.18.0 → isar-1.19.1}/tests/integration/__init__.py +0 -0
  156. {isar-1.18.0 → isar-1.19.1}/tests/integration/turtlebot/__init__.py +0 -0
  157. {isar-1.18.0 → isar-1.19.1}/tests/integration/turtlebot/config/__init__.py +0 -0
  158. {isar-1.18.0 → isar-1.19.1}/tests/integration/turtlebot/config/maps/__init__.py +0 -0
  159. {isar-1.18.0 → isar-1.19.1}/tests/integration/turtlebot/config/maps/turtleworld.json +0 -0
  160. {isar-1.18.0 → isar-1.19.1}/tests/integration/turtlebot/config/missions/__init__.py +0 -0
  161. {isar-1.18.0 → isar-1.19.1}/tests/integration/turtlebot/config/missions/default.json +0 -0
  162. {isar-1.18.0 → isar-1.19.1}/tests/isar/__init__.py +0 -0
  163. {isar-1.18.0 → isar-1.19.1}/tests/isar/apis/__init__.py +0 -0
  164. {isar-1.18.0 → isar-1.19.1}/tests/isar/apis/scheduler/__init__.py +0 -0
  165. {isar-1.18.0 → isar-1.19.1}/tests/isar/apis/scheduler/test_scheduler_router.py +0 -0
  166. {isar-1.18.0 → isar-1.19.1}/tests/isar/apis/security/__init__.py +0 -0
  167. {isar-1.18.0 → isar-1.19.1}/tests/isar/apis/security/test_authentication.py +0 -0
  168. {isar-1.18.0 → isar-1.19.1}/tests/isar/mission/__init__.py +0 -0
  169. {isar-1.18.0 → isar-1.19.1}/tests/isar/mission/test_mission.py +0 -0
  170. {isar-1.18.0 → isar-1.19.1}/tests/isar/models/__init__.py +0 -0
  171. {isar-1.18.0 → isar-1.19.1}/tests/isar/models/communication/__init__.py +0 -0
  172. {isar-1.18.0 → isar-1.19.1}/tests/isar/models/communication/test_queues.py +0 -0
  173. {isar-1.18.0 → isar-1.19.1}/tests/isar/models/example_mission_definition.json +0 -0
  174. {isar-1.18.0 → isar-1.19.1}/tests/isar/models/test_start_mission_definition.py +0 -0
  175. {isar-1.18.0 → isar-1.19.1}/tests/isar/services/__init__.py +0 -0
  176. {isar-1.18.0 → isar-1.19.1}/tests/isar/services/readers/__init__.py +0 -0
  177. {isar-1.18.0 → isar-1.19.1}/tests/isar/services/readers/test_base_reader.py +0 -0
  178. {isar-1.18.0 → isar-1.19.1}/tests/isar/services/readers/test_mission_reader.py +0 -0
  179. {isar-1.18.0 → isar-1.19.1}/tests/isar/services/service_connections/__init__.py +0 -0
  180. {isar-1.18.0 → isar-1.19.1}/tests/isar/services/service_connections/echo/__init__.py +0 -0
  181. {isar-1.18.0 → isar-1.19.1}/tests/isar/services/service_connections/test_base_request_handler.py +0 -0
  182. {isar-1.18.0 → isar-1.19.1}/tests/isar/services/utilities/__init__.py +0 -0
  183. {isar-1.18.0 → isar-1.19.1}/tests/isar/services/utilities/test_queue_utilities.py +0 -0
  184. {isar-1.18.0 → isar-1.19.1}/tests/isar/services/utilities/test_scheduling_utilities.py +0 -0
  185. {isar-1.18.0 → isar-1.19.1}/tests/isar/state_machine/__init__.py +0 -0
  186. {isar-1.18.0 → isar-1.19.1}/tests/isar/state_machine/states/__init__.py +0 -0
  187. {isar-1.18.0 → isar-1.19.1}/tests/isar/state_machine/states/test_monitor.py +0 -0
  188. {isar-1.18.0 → isar-1.19.1}/tests/isar/storage/test_blob_storage.py +0 -0
  189. {isar-1.18.0 → isar-1.19.1}/tests/isar/storage/test_uploader.py +0 -0
  190. {isar-1.18.0 → isar-1.19.1}/tests/mocks/__init__.py +0 -0
  191. {isar-1.18.0 → isar-1.19.1}/tests/mocks/blob_storage.py +0 -0
  192. {isar-1.18.0 → isar-1.19.1}/tests/mocks/mission_definition.py +0 -0
  193. {isar-1.18.0 → isar-1.19.1}/tests/mocks/mqtt_client.py +0 -0
  194. {isar-1.18.0 → isar-1.19.1}/tests/mocks/pose.py +0 -0
  195. {isar-1.18.0 → isar-1.19.1}/tests/mocks/request.py +0 -0
  196. {isar-1.18.0 → isar-1.19.1}/tests/mocks/status.py +0 -0
  197. {isar-1.18.0 → isar-1.19.1}/tests/mocks/step.py +0 -0
  198. {isar-1.18.0 → isar-1.19.1}/tests/mocks/task.py +0 -0
  199. {isar-1.18.0 → isar-1.19.1}/tests/mocks/token.py +0 -0
  200. {isar-1.18.0 → isar-1.19.1}/tests/test_data/test_json_file.json +0 -0
  201. {isar-1.18.0 → isar-1.19.1}/tests/test_data/test_map_config/test_map_config.json +0 -0
  202. {isar-1.18.0 → isar-1.19.1}/tests/test_data/test_mission_not_working.json +0 -0
  203. {isar-1.18.0 → isar-1.19.1}/tests/test_data/test_mission_working.json +0 -0
  204. {isar-1.18.0 → isar-1.19.1}/tests/test_data/test_mission_working_no_tasks.json +0 -0
  205. {isar-1.18.0 → isar-1.19.1}/tests/test_data/test_thermal_image_mission.json +0 -0
  206. {isar-1.18.0 → isar-1.19.1}/tests/test_modules.py +0 -0
@@ -19,7 +19,7 @@ jobs:
19
19
  if: github.event_name == 'issues' && github.event.action == 'opened' || github.event.action == 'reopened'
20
20
  steps:
21
21
  - name: 'Move issue to "Todo"'
22
- uses: leonsteinhaeuser/project-beta-automations@v1.0.2
22
+ uses: leonsteinhaeuser/project-beta-automations@v2.1.0
23
23
  with:
24
24
  gh_token: ${{ secrets.MY_GITHUB_TOKEN }}
25
25
  organization: equinor
@@ -32,7 +32,7 @@ jobs:
32
32
  if: github.event_name == 'issues' && github.event.action == 'closed'
33
33
  steps:
34
34
  - name: 'Moved issue to "Done"'
35
- uses: leonsteinhaeuser/project-beta-automations@v1.0.2
35
+ uses: leonsteinhaeuser/project-beta-automations@v2.1.0
36
36
  with:
37
37
  gh_token: ${{ secrets.MY_GITHUB_TOKEN }}
38
38
  organization: equinor
@@ -45,7 +45,7 @@ jobs:
45
45
  if: github.event_name == 'pull_request' && github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'review_requested'
46
46
  steps:
47
47
  - name: 'Move PR to "In Progress"'
48
- uses: leonsteinhaeuser/project-beta-automations@v1.0.2
48
+ uses: leonsteinhaeuser/project-beta-automations@v2.1.0
49
49
  with:
50
50
  gh_token: ${{ secrets.MY_GITHUB_TOKEN }}
51
51
  organization: equinor
@@ -58,7 +58,7 @@ jobs:
58
58
  if: github.event_name == 'pull_request' && github.event.action == 'ready_for_review'
59
59
  steps:
60
60
  - name: 'Move PR to "Review"'
61
- uses: leonsteinhaeuser/project-beta-automations@v1.0.2
61
+ uses: leonsteinhaeuser/project-beta-automations@v2.1.0
62
62
  with:
63
63
  gh_token: ${{ secrets.MY_GITHUB_TOKEN }}
64
64
  organization: equinor
@@ -71,7 +71,7 @@ jobs:
71
71
  if: github.event_name == 'pull_request' && github.event.action == 'closed'
72
72
  steps:
73
73
  - name: 'Move PR to "Done"'
74
- uses: leonsteinhaeuser/project-beta-automations@v1.0.2
74
+ uses: leonsteinhaeuser/project-beta-automations@v2.1.0
75
75
  with:
76
76
  gh_token: ${{ secrets.MY_GITHUB_TOKEN }}
77
77
  organization: equinor
@@ -27,10 +27,10 @@ jobs:
27
27
 
28
28
  steps:
29
29
  - name: Checkout repository
30
- uses: actions/checkout@v3
30
+ uses: actions/checkout@v4
31
31
 
32
32
  - name: Log in to the Container registry
33
- uses: docker/login-action@v2
33
+ uses: docker/login-action@v3
34
34
  with:
35
35
  registry: ${{ env.REGISTRY }}
36
36
  username: ${{ github.actor }}
@@ -38,12 +38,12 @@ jobs:
38
38
 
39
39
  - name: Extract metadata (tags, labels) for Docker
40
40
  id: meta
41
- uses: docker/metadata-action@v4
41
+ uses: docker/metadata-action@v5
42
42
  with:
43
43
  images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
44
44
 
45
45
  - name: Build and push Docker image
46
- uses: docker/build-push-action@v4
46
+ uses: docker/build-push-action@v5
47
47
  with:
48
48
  context: .
49
49
  file: Dockerfile
@@ -14,13 +14,13 @@ jobs:
14
14
  strategy:
15
15
  fail-fast: false
16
16
  matrix:
17
- python-version: ["3.10", "3.11", "3.12"]
17
+ python-version: ["3.11", "3.12"]
18
18
 
19
19
  steps:
20
- - uses: actions/checkout@v3
20
+ - uses: actions/checkout@v4
21
21
 
22
22
  - name: Set up Python ${{ matrix.python-version }}
23
- uses: actions/setup-python@v3
23
+ uses: actions/setup-python@v5
24
24
  with:
25
25
  python-version: ${{ matrix.python-version }}
26
26
 
@@ -8,9 +8,9 @@ jobs:
8
8
  deploy:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
- - uses: actions/checkout@v3
11
+ - uses: actions/checkout@v4
12
12
  - name: Set up Python
13
- uses: actions/setup-python@v3
13
+ uses: actions/setup-python@v5
14
14
  with:
15
15
  python-version: "3.x"
16
16
  - name: Install dependencies
@@ -0,0 +1,32 @@
1
+ # This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
2
+ #
3
+ # You can adjust the behavior by modifying this file.
4
+ # For more information, see:
5
+ # https://github.com/actions/stale
6
+ name: Mark stale issues and pull requests
7
+
8
+ on:
9
+ workflow_dispatch:
10
+ schedule:
11
+ - cron: "35 8 * * *"
12
+
13
+ jobs:
14
+ stale:
15
+ runs-on: ubuntu-latest
16
+ permissions:
17
+ issues: write
18
+ pull-requests: write
19
+
20
+ steps:
21
+ - name: Close Stale Issues
22
+ uses: actions/stale@v9
23
+ with:
24
+ repo-token: ${{ secrets.STALE_ISSUE_TOKEN }}
25
+ stale-issue-message: "This issue has automatically been marked as stale as there has been no activity for 60 days."
26
+ stale-pr-message: "This pull request has automatically been marked as stale as there has been no activity for 30 days."
27
+ stale-issue-label: "stale"
28
+ stale-pr-label: "stale"
29
+ close-issue-message: "This issue has been closed automatically due to a lack of activity."
30
+ close-pr-message: "This pull request has been closed automatically due to a lack of activity."
31
+ days-before-pr-stale: 30
32
+ days-before-close: -1
@@ -1,4 +1,4 @@
1
- FROM python:3.10-slim AS builder
1
+ FROM python:3.12-slim AS builder
2
2
 
3
3
  WORKDIR /app
4
4
  RUN apt-get update
@@ -15,7 +15,7 @@ RUN pip install .
15
15
  # Install the base isar-robot package
16
16
  RUN pip install isar-robot
17
17
 
18
- FROM python:3.10-slim
18
+ FROM python:3.12-slim
19
19
  WORKDIR /app
20
20
  COPY --from=builder /opt/venv /opt/venv
21
21
  ENV PATH="/opt/venv/bin:$PATH"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: isar
3
- Version: 1.18.0
3
+ Version: 1.19.1
4
4
  Summary: Integration and Supervisory control of Autonomous Robots
5
5
  Home-page: https://github.com/equinor/isar
6
6
  Author: Equinor ASA
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python
12
12
  Classifier: Topic :: Scientific/Engineering
13
13
  Classifier: Topic :: Scientific/Engineering :: Physics
14
14
  Classifier: Topic :: Software Development :: Libraries
15
- Requires-Python: >=3.10
15
+ Requires-Python: >=3.11
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
18
  Requires-Dist: alitra>=1.1.3
@@ -415,6 +415,17 @@ ISAR_MQTT_PASSWORD
415
415
 
416
416
  If not specified the password will default to an empty string.
417
417
 
418
+ ## Running several ISAR instances locally
419
+
420
+ To run several ISAR instances in parallel locally:
421
+ 1. Generate a guid: https://www.guidgenerator.com/
422
+ 2. Open a new terminal in the isar folder
423
+ 3. Run the following command before running main.py:
424
+
425
+ ```
426
+ export ISAR_API_PORT=port_name_higher_than_1024 ISAR_ISAR_ID=guid ISAR_ROBOT_NAME=random_robot_name
427
+ ```
428
+
418
429
  # Contributions
419
430
 
420
431
  Equinor welcomes all kinds of contributions, including code, bug reports, issues, feature requests, and documentation
@@ -361,6 +361,17 @@ ISAR_MQTT_PASSWORD
361
361
 
362
362
  If not specified the password will default to an empty string.
363
363
 
364
+ ## Running several ISAR instances locally
365
+
366
+ To run several ISAR instances in parallel locally:
367
+ 1. Generate a guid: https://www.guidgenerator.com/
368
+ 2. Open a new terminal in the isar folder
369
+ 3. Run the following command before running main.py:
370
+
371
+ ```
372
+ export ISAR_API_PORT=port_name_higher_than_1024 ISAR_ISAR_ID=guid ISAR_ROBOT_NAME=random_robot_name
373
+ ```
374
+
364
375
  # Contributions
365
376
 
366
377
  Equinor welcomes all kinds of contributions, including code, bug reports, issues, feature requests, and documentation
@@ -19,9 +19,6 @@ from isar.services.service_connections.mqtt.robot_heartbeat_publisher import (
19
19
  from isar.services.service_connections.mqtt.robot_info_publisher import (
20
20
  RobotInfoPublisher,
21
21
  )
22
- from isar.services.service_connections.mqtt.robot_status_publisher import (
23
- RobotStatusPublisher,
24
- )
25
22
  from isar.state_machine.state_machine import StateMachine, main
26
23
  from isar.storage.uploader import Uploader
27
24
  from robot_interface.robot_interface import RobotInterface
@@ -56,15 +53,6 @@ if __name__ == "__main__":
56
53
  target=mqtt_client.run, name="ISAR MQTT Client", daemon=True
57
54
  )
58
55
  threads.append(mqtt_thread)
59
- robot_status_publisher: RobotStatusPublisher = RobotStatusPublisher(
60
- mqtt_queue=queues.mqtt_queue, robot=robot, state_machine=state_machine
61
- )
62
- robot_status_thread: Thread = Thread(
63
- target=robot_status_publisher.run,
64
- name="ISAR Robot Status Publisher",
65
- daemon=True,
66
- )
67
- threads.append(robot_status_thread)
68
56
 
69
57
  robot_info_publisher: RobotInfoPublisher = RobotInfoPublisher(
70
58
  mqtt_queue=queues.mqtt_queue
@@ -66,5 +66,5 @@ setup(
66
66
  "sphinx",
67
67
  ]
68
68
  },
69
- python_requires=">=3.10",
69
+ python_requires=">=3.11",
70
70
  )
@@ -0,0 +1,6 @@
1
+ from importlib.metadata import PackageNotFoundError, distribution
2
+
3
+ try:
4
+ __version__ = distribution(__name__).version
5
+ except PackageNotFoundError:
6
+ pass # package is not installed
@@ -1,6 +1,6 @@
1
- import importlib.resources as pkg_resources
2
1
  import logging
3
2
  import logging.config
3
+ from importlib.resources import as_file, files
4
4
 
5
5
  import yaml
6
6
  from opencensus.ext.azure.log_exporter import AzureLogHandler
@@ -14,8 +14,9 @@ from isar.config.settings import settings
14
14
 
15
15
  def setup_loggers(keyvault: Keyvault) -> None:
16
16
  log_levels: dict = settings.LOG_LEVELS
17
- with pkg_resources.path("isar.config", "logging.conf") as path:
18
- log_config = yaml.safe_load(open(path))
17
+ source = files("isar").joinpath("config").joinpath("logging.conf")
18
+ with as_file(source) as f:
19
+ log_config = yaml.safe_load(open(f))
19
20
 
20
21
  logging.config.dictConfig(log_config)
21
22
 
@@ -1,5 +1,5 @@
1
- import importlib.resources as pkg_resources
2
1
  import os
2
+ from importlib.resources import as_file, files
3
3
  from typing import Any, List, Optional
4
4
 
5
5
  from dotenv import load_dotenv
@@ -14,11 +14,12 @@ from robot_interface.telemetry.payloads import VideoStream
14
14
  class Settings(BaseSettings):
15
15
  def __init__(self) -> None:
16
16
  try:
17
- with pkg_resources.path(f"isar.config", "settings.env") as path:
18
- env_file_path = path
17
+ source = files("isar").joinpath("config").joinpath("settings.env")
18
+ with as_file(source) as eml:
19
+ env_file = eml
19
20
  except ModuleNotFoundError:
20
- env_file_path = None
21
- super().__init__(_env_file=env_file_path)
21
+ env_file = None
22
+ super().__init__(_env_file=env_file)
22
23
 
23
24
  # Determines which robot package ISAR will attempt to import
24
25
  # Name must match with an installed python package in the local environment
@@ -229,14 +230,13 @@ class Settings(BaseSettings):
229
230
  DATA_CLASSIFICATION: str = Field(default="internal")
230
231
 
231
232
  # List of MQTT Topics
232
- TOPIC_ISAR_STATE: str = Field(default="state", validate_default=True)
233
+ TOPIC_ISAR_STATUS: str = Field(default="status", validate_default=True)
233
234
  TOPIC_ISAR_MISSION: str = Field(default="mission", validate_default=True)
234
235
  TOPIC_ISAR_TASK: str = Field(default="task", validate_default=True)
235
236
  TOPIC_ISAR_STEP: str = Field(default="step", validate_default=True)
236
237
  TOPIC_ISAR_INSPECTION_RESULT: str = Field(
237
238
  default="inspection_result", validate_default=True
238
239
  )
239
- TOPIC_ISAR_ROBOT_STATUS: str = Field(default="robot_status", validate_default=True)
240
240
  TOPIC_ISAR_ROBOT_INFO: str = Field(default="robot_info", validate_default=True)
241
241
  TOPIC_ISAR_ROBOT_HEARTBEAT: str = Field(
242
242
  default="robot_heartbeat", validate_default=True
@@ -284,11 +284,10 @@ class Settings(BaseSettings):
284
284
  }
285
285
 
286
286
  @field_validator(
287
- "TOPIC_ISAR_STATE",
287
+ "TOPIC_ISAR_STATUS",
288
288
  "TOPIC_ISAR_MISSION",
289
289
  "TOPIC_ISAR_TASK",
290
290
  "TOPIC_ISAR_STEP",
291
- "TOPIC_ISAR_ROBOT_STATUS",
292
291
  "TOPIC_ISAR_ROBOT_INFO",
293
292
  "TOPIC_ISAR_ROBOT_HEARTBEAT",
294
293
  "TOPIC_ISAR_INSPECTION_RESULT",
@@ -312,13 +311,16 @@ settings = Settings()
312
311
  class RobotSettings(BaseSettings):
313
312
  def __init__(self) -> None:
314
313
  try:
315
- with pkg_resources.path(
316
- f"{settings.ROBOT_PACKAGE}.config", "settings.env"
317
- ) as path:
318
- env_file_path = path
314
+ source = (
315
+ files(f"{settings.ROBOT_PACKAGE}")
316
+ .joinpath("config")
317
+ .joinpath("settings.env")
318
+ )
319
+ with as_file(source) as eml:
320
+ env_file = eml
319
321
  except ModuleNotFoundError:
320
- env_file_path = None
321
- super().__init__(_env_file=env_file_path)
322
+ env_file = None
323
+ super().__init__(_env_file=env_file)
322
324
 
323
325
  # ISAR steps the robot is capable of performing
324
326
  # This should be set in the robot package settings.env file
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  import time
3
- from datetime import datetime
3
+ from datetime import UTC, datetime
4
4
  from queue import Queue
5
5
 
6
6
  from isar.config.settings import settings
@@ -18,7 +18,7 @@ class RobotHeartbeatPublisher:
18
18
  payload: RobotHeartbeatPayload = RobotHeartbeatPayload(
19
19
  isar_id=settings.ISAR_ID,
20
20
  robot_name=settings.ROBOT_NAME,
21
- timestamp=datetime.utcnow(),
21
+ timestamp=datetime.now(UTC),
22
22
  )
23
23
 
24
24
  self.mqtt_publisher.publish(
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  import time
3
- from datetime import datetime
3
+ from datetime import UTC, datetime
4
4
  from queue import Queue
5
5
 
6
6
  from isar.config.settings import robot_settings, settings
@@ -25,7 +25,7 @@ class RobotInfoPublisher:
25
25
  host=settings.API_HOST_VIEWED_EXTERNALLY,
26
26
  port=settings.API_PORT,
27
27
  capabilities=robot_settings.CAPABILITIES,
28
- timestamp=datetime.utcnow(),
28
+ timestamp=datetime.now(UTC),
29
29
  )
30
30
 
31
31
  self.mqtt_publisher.publish(
@@ -2,7 +2,7 @@ import json
2
2
  import logging
3
3
  import queue
4
4
  from collections import deque
5
- from datetime import datetime
5
+ from datetime import UTC, datetime
6
6
  from typing import Deque, List, Optional
7
7
 
8
8
  from alitra import Pose
@@ -24,6 +24,7 @@ from isar.state_machine.states import (
24
24
  Initiate,
25
25
  Monitor,
26
26
  Off,
27
+ Offline,
27
28
  Paused,
28
29
  Stop,
29
30
  )
@@ -31,7 +32,12 @@ from isar.state_machine.states_enum import States
31
32
  from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
32
33
  from robot_interface.models.initialize.initialize_params import InitializeParams
33
34
  from robot_interface.models.mission.mission import Mission
34
- from robot_interface.models.mission.status import MissionStatus, StepStatus, TaskStatus
35
+ from robot_interface.models.mission.status import (
36
+ MissionStatus,
37
+ RobotStatus,
38
+ StepStatus,
39
+ TaskStatus,
40
+ )
35
41
  from robot_interface.models.mission.step import Step
36
42
  from robot_interface.models.mission.task import Task
37
43
  from robot_interface.robot_interface import RobotInterface
@@ -88,6 +94,7 @@ class StateMachine(object):
88
94
  self.monitor_state: State = Monitor(self)
89
95
  self.initiate_state: State = Initiate(self)
90
96
  self.off_state: State = Off(self)
97
+ self.offline_state: State = Offline(self)
91
98
 
92
99
  self.states: List[State] = [
93
100
  self.off_state,
@@ -97,6 +104,7 @@ class StateMachine(object):
97
104
  self.monitor_state,
98
105
  self.stop_state,
99
106
  self.paused_state,
107
+ self.offline_state,
100
108
  ]
101
109
 
102
110
  self.machine = Machine(self, states=self.states, initial="off", queued=True)
@@ -194,6 +202,18 @@ class StateMachine(object):
194
202
  "dest": self.idle_state,
195
203
  "before": self._mission_stopped,
196
204
  },
205
+ {
206
+ "trigger": "robot_turned_offline",
207
+ "source": [self.idle_state],
208
+ "dest": self.offline_state,
209
+ "before": self._offline,
210
+ },
211
+ {
212
+ "trigger": "robot_turned_online",
213
+ "source": self.offline_state,
214
+ "dest": self.idle_state,
215
+ "before": self._online,
216
+ },
197
217
  ]
198
218
  )
199
219
 
@@ -239,6 +259,12 @@ class StateMachine(object):
239
259
  def _off(self) -> None:
240
260
  return
241
261
 
262
+ def _offline(self) -> None:
263
+ return
264
+
265
+ def _online(self) -> None:
266
+ return
267
+
242
268
  def _resume(self) -> None:
243
269
  self.logger.info(f"Resuming mission: {self.current_mission.id}")
244
270
  self.current_mission.status = MissionStatus.InProgress
@@ -417,7 +443,7 @@ class StateMachine(object):
417
443
  self.send_state_status()
418
444
  self._log_state_transition(self.current_state)
419
445
  self.logger.info(f"State: {self.current_state}")
420
- self.publish_state()
446
+ self.publish_status()
421
447
 
422
448
  def reset_state_machine(self) -> None:
423
449
  self.logger.info("Resetting state machine")
@@ -482,7 +508,7 @@ class StateMachine(object):
482
508
  "error_description": (
483
509
  error_message.error_description if error_message else None
484
510
  ),
485
- "timestamp": datetime.utcnow(),
511
+ "timestamp": datetime.now(UTC),
486
512
  },
487
513
  cls=EnhancedJSONEncoder,
488
514
  )
@@ -490,7 +516,8 @@ class StateMachine(object):
490
516
  self.mqtt_publisher.publish(
491
517
  topic=settings.TOPIC_ISAR_MISSION,
492
518
  payload=payload,
493
- retain=False,
519
+ qos=1,
520
+ retain=True,
494
521
  )
495
522
 
496
523
  def publish_task_status(self, task: Task) -> None:
@@ -514,7 +541,7 @@ class StateMachine(object):
514
541
  "error_description": (
515
542
  error_message.error_description if error_message else None
516
543
  ),
517
- "timestamp": datetime.utcnow(),
544
+ "timestamp": datetime.now(UTC),
518
545
  },
519
546
  cls=EnhancedJSONEncoder,
520
547
  )
@@ -522,7 +549,8 @@ class StateMachine(object):
522
549
  self.mqtt_publisher.publish(
523
550
  topic=settings.TOPIC_ISAR_TASK,
524
551
  payload=payload,
525
- retain=False,
552
+ qos=1,
553
+ retain=True,
526
554
  )
527
555
 
528
556
  def publish_step_status(self, step: Step) -> None:
@@ -548,7 +576,7 @@ class StateMachine(object):
548
576
  "error_description": (
549
577
  error_message.error_description if error_message else None
550
578
  ),
551
- "timestamp": datetime.utcnow(),
579
+ "timestamp": datetime.now(UTC),
552
580
  },
553
581
  cls=EnhancedJSONEncoder,
554
582
  )
@@ -556,28 +584,38 @@ class StateMachine(object):
556
584
  self.mqtt_publisher.publish(
557
585
  topic=settings.TOPIC_ISAR_STEP,
558
586
  payload=payload,
559
- retain=False,
587
+ qos=1,
588
+ retain=True,
560
589
  )
561
590
 
562
- def publish_state(self) -> None:
591
+ def publish_status(self) -> None:
563
592
  if not self.mqtt_publisher:
564
593
  return
565
594
  payload: str = json.dumps(
566
595
  {
567
596
  "isar_id": settings.ISAR_ID,
568
597
  "robot_name": settings.ROBOT_NAME,
569
- "state": self.current_state,
570
- "timestamp": datetime.utcnow(),
598
+ "status": self._current_status(),
599
+ "timestamp": datetime.now(UTC),
571
600
  },
572
601
  cls=EnhancedJSONEncoder,
573
602
  )
574
603
 
575
604
  self.mqtt_publisher.publish(
576
- topic=settings.TOPIC_ISAR_STATE,
605
+ topic=settings.TOPIC_ISAR_STATUS,
577
606
  payload=payload,
578
- retain=False,
607
+ qos=1,
608
+ retain=True,
579
609
  )
580
610
 
611
+ def _current_status(self) -> RobotStatus:
612
+ if self.current_state == States.Idle:
613
+ return RobotStatus.Available
614
+ elif self.current_state == States.Offline:
615
+ return RobotStatus.Offline
616
+ else:
617
+ return RobotStatus.Busy
618
+
581
619
  def _log_state_transition(self, next_state):
582
620
  """Logs all state transitions that are not self-transitions."""
583
621
  self.transitions_list.append(next_state)
@@ -3,5 +3,6 @@ from .initialize import Initialize
3
3
  from .initiate import Initiate
4
4
  from .monitor import Monitor
5
5
  from .off import Off
6
+ from .offline import Offline
6
7
  from .paused import Paused
7
8
  from .stop import Stop
@@ -0,0 +1,85 @@
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.models.communication.message import StartMissionMessage
9
+ from isar.services.utilities.threaded_request import (
10
+ ThreadedRequest,
11
+ ThreadedRequestNotFinishedError,
12
+ )
13
+ from robot_interface.models.exceptions.robot_exceptions import RobotException
14
+ from robot_interface.models.mission.status import RobotStatus
15
+
16
+ if TYPE_CHECKING:
17
+ from isar.state_machine.state_machine import StateMachine
18
+
19
+
20
+ class Idle(State):
21
+ def __init__(self, state_machine: "StateMachine") -> None:
22
+ super().__init__(name="idle", on_enter=self.start, on_exit=self.stop)
23
+ self.state_machine: "StateMachine" = state_machine
24
+ self.logger = logging.getLogger("state_machine")
25
+ self.robot_status_thread: Optional[ThreadedRequest] = None
26
+ self.last_robot_status_poll_time: float = time.time()
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
+ start_mission: Optional[StartMissionMessage] = (
40
+ self.state_machine.should_start_mission()
41
+ )
42
+ if start_mission:
43
+ self.state_machine.start_mission(
44
+ mission=start_mission.mission,
45
+ initial_pose=start_mission.initial_pose,
46
+ )
47
+ transition = self.state_machine.mission_started # type: ignore
48
+ break
49
+ time.sleep(self.state_machine.sleep_time)
50
+
51
+ time_from_last_robot_status_poll = (
52
+ time.time() - self.last_robot_status_poll_time
53
+ )
54
+ if (
55
+ time_from_last_robot_status_poll
56
+ < settings.ROBOT_API_STATUS_POLL_INTERVAL
57
+ ):
58
+ continue
59
+
60
+ if not self.robot_status_thread:
61
+ self.robot_status_thread = ThreadedRequest(
62
+ request_func=self.state_machine.robot.robot_status
63
+ )
64
+ self.robot_status_thread.start_thread(
65
+ name="State Machine Offline Get Robot Status"
66
+ )
67
+
68
+ try:
69
+ robot_status: RobotStatus = self.robot_status_thread.get_output()
70
+ except ThreadedRequestNotFinishedError:
71
+ time.sleep(self.state_machine.sleep_time)
72
+ continue
73
+
74
+ except (RobotException,) as e:
75
+ self.logger.error(
76
+ f"Failed to get robot status because: {e.error_description}"
77
+ )
78
+
79
+ self.last_robot_status_poll_time = time.time()
80
+
81
+ if robot_status == RobotStatus.Offline:
82
+ transition = self.state_machine.robot_turned_offline # type: ignore
83
+ break
84
+
85
+ transition()