isar 1.33.1__tar.gz → 1.33.2__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.33.1 → isar-1.33.2}/PKG-INFO +1 -1
  2. {isar-1.33.1 → isar-1.33.2}/src/isar/config/settings.py +5 -0
  3. {isar-1.33.1 → isar-1.33.2}/src/isar/services/service_connections/mqtt/mqtt_client.py +46 -10
  4. {isar-1.33.1 → isar-1.33.2}/src/isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +3 -0
  5. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/state_machine.py +5 -2
  6. isar-1.33.2/src/isar/storage/blob_storage.py +101 -0
  7. {isar-1.33.1 → isar-1.33.2}/src/isar/storage/local_storage.py +21 -11
  8. {isar-1.33.1 → isar-1.33.2}/src/isar/storage/storage_interface.py +27 -6
  9. {isar-1.33.1 → isar-1.33.2}/src/isar/storage/uploader.py +30 -13
  10. {isar-1.33.1 → isar-1.33.2}/src/isar.egg-info/PKG-INFO +1 -1
  11. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/telemetry/mqtt_client.py +62 -6
  12. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/telemetry/payloads.py +4 -2
  13. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/utilities/json_service.py +6 -0
  14. isar-1.33.1/src/isar/storage/blob_storage.py +0 -79
  15. {isar-1.33.1 → isar-1.33.2}/.dockerignore +0 -0
  16. {isar-1.33.1 → isar-1.33.2}/.env.test +0 -0
  17. {isar-1.33.1 → isar-1.33.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  18. {isar-1.33.1 → isar-1.33.2}/.github/ISSUE_TEMPLATE/feature.md +0 -0
  19. {isar-1.33.1 → isar-1.33.2}/.github/ISSUE_TEMPLATE/improvement.md +0 -0
  20. {isar-1.33.1 → isar-1.33.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  21. {isar-1.33.1 → isar-1.33.2}/.github/release.yml +0 -0
  22. {isar-1.33.1 → isar-1.33.2}/.github/workflows/compile_requirements.yml +0 -0
  23. {isar-1.33.1 → isar-1.33.2}/.github/workflows/project_automations.yml +0 -0
  24. {isar-1.33.1 → isar-1.33.2}/.github/workflows/pythonpackage.yml +0 -0
  25. {isar-1.33.1 → isar-1.33.2}/.github/workflows/pythonpublish.yml +0 -0
  26. {isar-1.33.1 → isar-1.33.2}/.github/workflows/stale.yml +0 -0
  27. {isar-1.33.1 → isar-1.33.2}/.gitignore +0 -0
  28. {isar-1.33.1 → isar-1.33.2}/.pre-commit-config.yaml +0 -0
  29. {isar-1.33.1 → isar-1.33.2}/LICENSE +0 -0
  30. {isar-1.33.1 → isar-1.33.2}/README.md +0 -0
  31. {isar-1.33.1 → isar-1.33.2}/SECURITY.md +0 -0
  32. {isar-1.33.1 → isar-1.33.2}/docs/Makefile +0 -0
  33. {isar-1.33.1 → isar-1.33.2}/docs/full_state_machine_diagram.png +0 -0
  34. {isar-1.33.1 → isar-1.33.2}/docs/make.bat +0 -0
  35. {isar-1.33.1 → isar-1.33.2}/docs/mission_state_machine_diagram.png +0 -0
  36. {isar-1.33.1 → isar-1.33.2}/docs/robot_status_state_machine_diagram.png +0 -0
  37. {isar-1.33.1 → isar-1.33.2}/docs/rst_processing.py +0 -0
  38. {isar-1.33.1 → isar-1.33.2}/docs/source/conf.py +0 -0
  39. {isar-1.33.1 → isar-1.33.2}/docs/source/index.rst +0 -0
  40. {isar-1.33.1 → isar-1.33.2}/docs/source/readme_link.md +0 -0
  41. {isar-1.33.1 → isar-1.33.2}/docs/update_state_diagram.py +0 -0
  42. {isar-1.33.1 → isar-1.33.2}/main.py +0 -0
  43. {isar-1.33.1 → isar-1.33.2}/pyproject.toml +0 -0
  44. {isar-1.33.1 → isar-1.33.2}/radixconfig.yml +0 -0
  45. {isar-1.33.1 → isar-1.33.2}/requirements.txt +0 -0
  46. {isar-1.33.1 → isar-1.33.2}/setup.cfg +0 -0
  47. {isar-1.33.1 → isar-1.33.2}/src/isar/__init__.py +0 -0
  48. {isar-1.33.1 → isar-1.33.2}/src/isar/apis/__init__.py +0 -0
  49. {isar-1.33.1 → isar-1.33.2}/src/isar/apis/api.py +0 -0
  50. {isar-1.33.1 → isar-1.33.2}/src/isar/apis/models/__init__.py +0 -0
  51. {isar-1.33.1 → isar-1.33.2}/src/isar/apis/models/models.py +0 -0
  52. {isar-1.33.1 → isar-1.33.2}/src/isar/apis/models/start_mission_definition.py +0 -0
  53. {isar-1.33.1 → isar-1.33.2}/src/isar/apis/robot_control/robot_controller.py +0 -0
  54. {isar-1.33.1 → isar-1.33.2}/src/isar/apis/schedule/__init__.py +0 -0
  55. {isar-1.33.1 → isar-1.33.2}/src/isar/apis/schedule/scheduling_controller.py +0 -0
  56. {isar-1.33.1 → isar-1.33.2}/src/isar/apis/security/__init__.py +0 -0
  57. {isar-1.33.1 → isar-1.33.2}/src/isar/apis/security/authentication.py +0 -0
  58. {isar-1.33.1 → isar-1.33.2}/src/isar/config/__init__.py +0 -0
  59. {isar-1.33.1 → isar-1.33.2}/src/isar/config/certs/ca-cert.pem +0 -0
  60. {isar-1.33.1 → isar-1.33.2}/src/isar/config/configuration_error.py +0 -0
  61. {isar-1.33.1 → isar-1.33.2}/src/isar/config/keyvault/__init__.py +0 -0
  62. {isar-1.33.1 → isar-1.33.2}/src/isar/config/keyvault/keyvault_error.py +0 -0
  63. {isar-1.33.1 → isar-1.33.2}/src/isar/config/keyvault/keyvault_service.py +0 -0
  64. {isar-1.33.1 → isar-1.33.2}/src/isar/config/log.py +0 -0
  65. {isar-1.33.1 → isar-1.33.2}/src/isar/config/logging.conf +0 -0
  66. {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/JSP1_intermediate_deck.json +0 -0
  67. {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/JSP1_weather_deck.json +0 -0
  68. {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/default_map.json +0 -0
  69. {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/klab_b.json +0 -0
  70. {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/klab_compressor.json +0 -0
  71. {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/klab_turtlebot.json +0 -0
  72. {isar-1.33.1 → isar-1.33.2}/src/isar/config/maps/turtleworld.json +0 -0
  73. {isar-1.33.1 → isar-1.33.2}/src/isar/config/open_telemetry.py +0 -0
  74. {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_mission_definition/__init__.py +0 -0
  75. {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_mission_definition/default_exr.json +0 -0
  76. {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_mission_definition/default_mission.json +0 -0
  77. {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_mission_definition/default_turtlebot.json +0 -0
  78. {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_missions/__init__.py +0 -0
  79. {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_missions/default.json +0 -0
  80. {isar-1.33.1 → isar-1.33.2}/src/isar/config/predefined_missions/default_turtlebot.json +0 -0
  81. {isar-1.33.1 → isar-1.33.2}/src/isar/eventhandlers/eventhandler.py +0 -0
  82. {isar-1.33.1 → isar-1.33.2}/src/isar/mission_planner/__init__.py +0 -0
  83. {isar-1.33.1 → isar-1.33.2}/src/isar/mission_planner/local_planner.py +0 -0
  84. {isar-1.33.1 → isar-1.33.2}/src/isar/mission_planner/mission_planner_interface.py +0 -0
  85. {isar-1.33.1 → isar-1.33.2}/src/isar/mission_planner/sequential_task_selector.py +0 -0
  86. {isar-1.33.1 → isar-1.33.2}/src/isar/mission_planner/task_selector_interface.py +0 -0
  87. {isar-1.33.1 → isar-1.33.2}/src/isar/models/__init__.py +0 -0
  88. {isar-1.33.1 → isar-1.33.2}/src/isar/models/events.py +0 -0
  89. {isar-1.33.1 → isar-1.33.2}/src/isar/modules.py +0 -0
  90. {isar-1.33.1 → isar-1.33.2}/src/isar/robot/robot.py +0 -0
  91. {isar-1.33.1 → isar-1.33.2}/src/isar/robot/robot_start_mission.py +0 -0
  92. {isar-1.33.1 → isar-1.33.2}/src/isar/robot/robot_status.py +0 -0
  93. {isar-1.33.1 → isar-1.33.2}/src/isar/robot/robot_stop_mission.py +0 -0
  94. {isar-1.33.1 → isar-1.33.2}/src/isar/robot/robot_task_status.py +0 -0
  95. {isar-1.33.1 → isar-1.33.2}/src/isar/script.py +0 -0
  96. {isar-1.33.1 → isar-1.33.2}/src/isar/services/__init__.py +0 -0
  97. {isar-1.33.1 → isar-1.33.2}/src/isar/services/auth/__init__.py +0 -0
  98. {isar-1.33.1 → isar-1.33.2}/src/isar/services/auth/azure_credentials.py +0 -0
  99. {isar-1.33.1 → isar-1.33.2}/src/isar/services/service_connections/__init__.py +0 -0
  100. {isar-1.33.1 → isar-1.33.2}/src/isar/services/service_connections/mqtt/__init__.py +0 -0
  101. {isar-1.33.1 → isar-1.33.2}/src/isar/services/service_connections/mqtt/robot_info_publisher.py +0 -0
  102. {isar-1.33.1 → isar-1.33.2}/src/isar/services/service_connections/request_handler.py +0 -0
  103. {isar-1.33.1 → isar-1.33.2}/src/isar/services/utilities/__init__.py +0 -0
  104. {isar-1.33.1 → isar-1.33.2}/src/isar/services/utilities/robot_utilities.py +0 -0
  105. {isar-1.33.1 → isar-1.33.2}/src/isar/services/utilities/scheduling_utilities.py +0 -0
  106. {isar-1.33.1 → isar-1.33.2}/src/isar/services/utilities/threaded_request.py +0 -0
  107. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/__init__.py +0 -0
  108. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/__init__.py +0 -0
  109. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/await_next_mission.py +0 -0
  110. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/blocked_protective_stop.py +0 -0
  111. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/home.py +0 -0
  112. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/intervention_needed.py +0 -0
  113. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/monitor.py +0 -0
  114. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/offline.py +0 -0
  115. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/paused.py +0 -0
  116. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/recharging.py +0 -0
  117. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/returning_home.py +0 -0
  118. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/robot_standing_still.py +0 -0
  119. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/stopping.py +0 -0
  120. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states/unknown_status.py +0 -0
  121. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/states_enum.py +0 -0
  122. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/fail_mission.py +0 -0
  123. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/finish_mission.py +0 -0
  124. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/pause.py +0 -0
  125. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/resume.py +0 -0
  126. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/return_home.py +0 -0
  127. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/robot_status.py +0 -0
  128. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/start_mission.py +0 -0
  129. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/stop.py +0 -0
  130. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/functions/utils.py +0 -0
  131. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/mission.py +0 -0
  132. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/return_home.py +0 -0
  133. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/transitions/robot_status.py +0 -0
  134. {isar-1.33.1 → isar-1.33.2}/src/isar/state_machine/utils/common_event_handlers.py +0 -0
  135. {isar-1.33.1 → isar-1.33.2}/src/isar/storage/__init__.py +0 -0
  136. {isar-1.33.1 → isar-1.33.2}/src/isar/storage/utilities.py +0 -0
  137. {isar-1.33.1 → isar-1.33.2}/src/isar.egg-info/SOURCES.txt +0 -0
  138. {isar-1.33.1 → isar-1.33.2}/src/isar.egg-info/dependency_links.txt +0 -0
  139. {isar-1.33.1 → isar-1.33.2}/src/isar.egg-info/entry_points.txt +0 -0
  140. {isar-1.33.1 → isar-1.33.2}/src/isar.egg-info/requires.txt +0 -0
  141. {isar-1.33.1 → isar-1.33.2}/src/isar.egg-info/top_level.txt +0 -0
  142. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/__init__.py +0 -0
  143. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/__init__.py +0 -0
  144. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/exceptions/__init__.py +0 -0
  145. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/exceptions/robot_exceptions.py +0 -0
  146. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/initialize/__init__.py +0 -0
  147. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/inspection/__init__.py +0 -0
  148. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/inspection/inspection.py +0 -0
  149. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/mission/__init__.py +0 -0
  150. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/mission/mission.py +0 -0
  151. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/mission/status.py +0 -0
  152. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/mission/task.py +0 -0
  153. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/robots/__init__.py +0 -0
  154. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/robots/battery_state.py +0 -0
  155. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/robots/media.py +0 -0
  156. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/models/robots/robot_model.py +0 -0
  157. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/robot_interface.py +0 -0
  158. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/telemetry/__init__.py +0 -0
  159. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/test_robot_interface.py +0 -0
  160. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/utilities/__init__.py +0 -0
  161. {isar-1.33.1 → isar-1.33.2}/src/robot_interface/utilities/uuid_string_factory.py +0 -0
  162. {isar-1.33.1 → isar-1.33.2}/tests/__init__.py +0 -0
  163. {isar-1.33.1 → isar-1.33.2}/tests/conftest.py +0 -0
  164. {isar-1.33.1 → isar-1.33.2}/tests/integration/__init__.py +0 -0
  165. {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/__init__.py +0 -0
  166. {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/config/__init__.py +0 -0
  167. {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/config/maps/__init__.py +0 -0
  168. {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/config/maps/turtleworld.json +0 -0
  169. {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/config/missions/__init__.py +0 -0
  170. {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/config/missions/default.json +0 -0
  171. {isar-1.33.1 → isar-1.33.2}/tests/integration/turtlebot/test_successful_mission.py +0 -0
  172. {isar-1.33.1 → isar-1.33.2}/tests/isar/__init__.py +0 -0
  173. {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/__init__.py +0 -0
  174. {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/models/__init__.py +0 -0
  175. {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/models/example_mission_definition.json +0 -0
  176. {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/models/test_start_mission_definition.py +0 -0
  177. {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/scheduler/__init__.py +0 -0
  178. {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/scheduler/test_scheduler_router.py +0 -0
  179. {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/security/__init__.py +0 -0
  180. {isar-1.33.1 → isar-1.33.2}/tests/isar/apis/security/test_authentication.py +0 -0
  181. {isar-1.33.1 → isar-1.33.2}/tests/isar/mission/__init__.py +0 -0
  182. {isar-1.33.1 → isar-1.33.2}/tests/isar/mission/test_mission.py +0 -0
  183. {isar-1.33.1 → isar-1.33.2}/tests/isar/models/__init__.py +0 -0
  184. {isar-1.33.1 → isar-1.33.2}/tests/isar/models/communication/__init__.py +0 -0
  185. {isar-1.33.1 → isar-1.33.2}/tests/isar/models/communication/test_events.py +0 -0
  186. {isar-1.33.1 → isar-1.33.2}/tests/isar/services/__init__.py +0 -0
  187. {isar-1.33.1 → isar-1.33.2}/tests/isar/services/readers/__init__.py +0 -0
  188. {isar-1.33.1 → isar-1.33.2}/tests/isar/services/readers/test_mission_reader.py +0 -0
  189. {isar-1.33.1 → isar-1.33.2}/tests/isar/services/service_connections/__init__.py +0 -0
  190. {isar-1.33.1 → isar-1.33.2}/tests/isar/services/service_connections/echo/__init__.py +0 -0
  191. {isar-1.33.1 → isar-1.33.2}/tests/isar/services/service_connections/test_base_request_handler.py +0 -0
  192. {isar-1.33.1 → isar-1.33.2}/tests/isar/services/utilities/__init__.py +0 -0
  193. {isar-1.33.1 → isar-1.33.2}/tests/isar/services/utilities/test_queue_utilities.py +0 -0
  194. {isar-1.33.1 → isar-1.33.2}/tests/isar/services/utilities/test_scheduling_utilities.py +0 -0
  195. {isar-1.33.1 → isar-1.33.2}/tests/isar/state_machine/__init__.py +0 -0
  196. {isar-1.33.1 → isar-1.33.2}/tests/isar/state_machine/states/__init__.py +0 -0
  197. {isar-1.33.1 → isar-1.33.2}/tests/isar/state_machine/states/test_monitor.py +0 -0
  198. {isar-1.33.1 → isar-1.33.2}/tests/isar/state_machine/test_state_machine.py +0 -0
  199. {isar-1.33.1 → isar-1.33.2}/tests/isar/storage/test_blob_storage.py +0 -0
  200. {isar-1.33.1 → isar-1.33.2}/tests/isar/storage/test_uploader.py +0 -0
  201. {isar-1.33.1 → isar-1.33.2}/tests/test_data/test_map_config/test_map_config.json +0 -0
  202. {isar-1.33.1 → isar-1.33.2}/tests/test_data/test_mission_not_working.json +0 -0
  203. {isar-1.33.1 → isar-1.33.2}/tests/test_data/test_mission_working.json +0 -0
  204. {isar-1.33.1 → isar-1.33.2}/tests/test_data/test_mission_working_no_tasks.json +0 -0
  205. {isar-1.33.1 → isar-1.33.2}/tests/test_data/test_thermal_image_mission.json +0 -0
  206. {isar-1.33.1 → isar-1.33.2}/tests/test_double/__init__.py +0 -0
  207. {isar-1.33.1 → isar-1.33.2}/tests/test_double/blob_storage.py +0 -0
  208. {isar-1.33.1 → isar-1.33.2}/tests/test_double/mission_definition.py +0 -0
  209. {isar-1.33.1 → isar-1.33.2}/tests/test_double/pose.py +0 -0
  210. {isar-1.33.1 → isar-1.33.2}/tests/test_double/request.py +0 -0
  211. {isar-1.33.1 → isar-1.33.2}/tests/test_double/robot_interface.py +0 -0
  212. {isar-1.33.1 → isar-1.33.2}/tests/test_double/status.py +0 -0
  213. {isar-1.33.1 → isar-1.33.2}/tests/test_double/task.py +0 -0
  214. {isar-1.33.1 → isar-1.33.2}/tests/test_double/token.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: isar
3
- Version: 1.33.1
3
+ Version: 1.33.2
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
@@ -244,6 +244,11 @@ class Settings(BaseSettings):
244
244
  default="intervention_needed", validate_default=True
245
245
  )
246
246
 
247
+ # List of MQTT Topics Expiry
248
+ MQTT_ROBOT_HEARTBEAT_EXPIRY: int = Field(default=5)
249
+ MQTT_TELEMETRY_EXPIRY: int = Field(default=10)
250
+ MQTT_MISSION_AND_TASK_EXPIRY: int = Field(default=86400)
251
+
247
252
  # Logging
248
253
 
249
254
  # Log handlers
@@ -1,15 +1,24 @@
1
1
  import logging
2
2
  import os
3
+ import time
3
4
  from queue import Empty, Queue
4
5
 
5
6
  import backoff
6
7
  from paho.mqtt import client as mqtt
7
8
  from paho.mqtt.client import Client
9
+ from paho.mqtt.packettypes import PacketTypes
10
+ from paho.mqtt.properties import Properties
8
11
 
9
12
  from isar.config.settings import settings
10
13
  from robot_interface.telemetry.mqtt_client import MqttClientInterface
11
14
 
12
15
 
16
+ def props_expiry(seconds: int) -> Properties:
17
+ p = Properties(PacketTypes.PUBLISH)
18
+ p.MessageExpiryInterval = seconds
19
+ return p
20
+
21
+
13
22
  def _on_success(data: dict) -> None:
14
23
  logging.getLogger("mqtt_client").info("Connected to MQTT Broker")
15
24
  logging.getLogger("mqtt_client").debug(
@@ -53,7 +62,9 @@ class MqttClient(MqttClientInterface):
53
62
 
54
63
  self.port: int = settings.MQTT_PORT
55
64
 
56
- self.client: Client = Client(mqtt.CallbackAPIVersion.VERSION1)
65
+ self.client: Client = Client(
66
+ protocol=mqtt.MQTTv5, callback_api_version=mqtt.CallbackAPIVersion.VERSION2
67
+ )
57
68
 
58
69
  self.client.enable_logger(logger=self.logger)
59
70
 
@@ -74,20 +85,36 @@ class MqttClient(MqttClientInterface):
74
85
 
75
86
  while True:
76
87
  if not self.client.is_connected():
88
+ time.sleep(0) # avoid CPU spin
77
89
  continue
78
90
  try:
79
- topic, payload, qos, retain = self.mqtt_queue.get(timeout=1)
91
+ item = self.mqtt_queue.get(timeout=1)
92
+ if len(item) == 4:
93
+ topic, payload, qos, retain = item
94
+ properties = None
95
+ else:
96
+ topic, payload, qos, retain, properties = item
80
97
  except Empty:
81
98
  continue
82
99
 
83
- self.publish(topic=topic, payload=payload, qos=qos, retain=retain)
100
+ self.publish(
101
+ topic=topic,
102
+ payload=payload,
103
+ qos=qos,
104
+ retain=retain,
105
+ properties=properties,
106
+ )
84
107
 
85
- def on_connect(self, client, userdata, flags, rc):
86
- self.logger.info("Connection returned result: " + mqtt.connack_string(rc))
108
+ def on_connect(self, client, userdata, flags, reason_code, properties):
109
+ self.logger.info(
110
+ "Connection returned result: " + mqtt.connack_string(reason_code)
111
+ )
87
112
 
88
- def on_disconnect(self, client, userdata, rc):
89
- if rc != 0:
90
- self.logger.warning("Unexpected disconnection from MQTT Broker")
113
+ def on_disconnect(self, client, userdata, reason_code, properties):
114
+ if reason_code != 0:
115
+ self.logger.warning(
116
+ f"Unexpected disconnection from MQTT Broker, {reason_code}"
117
+ )
91
118
 
92
119
  @backoff.on_exception(
93
120
  backoff.expo,
@@ -102,6 +129,15 @@ class MqttClient(MqttClientInterface):
102
129
  self.logger.info("Host: %s, Port: %s", host, port)
103
130
  self.client.connect(host=host, port=port)
104
131
 
105
- def publish(self, topic: str, payload: str, qos: int = 0, retain: bool = False):
132
+ def publish(
133
+ self,
134
+ topic: str,
135
+ payload: str,
136
+ qos: int = 0,
137
+ retain: bool = False,
138
+ properties=None,
139
+ ):
106
140
  self.logger.debug("Publishing message to topic: %s", topic)
107
- self.client.publish(topic=topic, payload=payload, qos=qos, retain=retain)
141
+ self.client.publish(
142
+ topic=topic, payload=payload, qos=qos, retain=retain, properties=properties
143
+ )
@@ -4,6 +4,7 @@ from datetime import datetime, timezone
4
4
  from queue import Queue
5
5
 
6
6
  from isar.config.settings import settings
7
+ from isar.services.service_connections.mqtt.mqtt_client import props_expiry
7
8
  from robot_interface.telemetry.mqtt_client import MqttPublisher
8
9
  from robot_interface.telemetry.payloads import RobotHeartbeatPayload
9
10
  from robot_interface.utilities.json_service import EnhancedJSONEncoder
@@ -24,6 +25,8 @@ class RobotHeartbeatPublisher:
24
25
  self.mqtt_publisher.publish(
25
26
  topic=settings.TOPIC_ISAR_ROBOT_HEARTBEAT,
26
27
  payload=json.dumps(payload, cls=EnhancedJSONEncoder),
28
+ retain=True,
29
+ properties=props_expiry(settings.MQTT_ROBOT_HEARTBEAT_EXPIRY),
27
30
  )
28
31
 
29
32
  time.sleep(settings.ROBOT_HEARTBEAT_PUBLISH_INTERVAL)
@@ -15,6 +15,7 @@ from isar.mission_planner.task_selector_interface import (
15
15
  TaskSelectorStop,
16
16
  )
17
17
  from isar.models.events import Events, SharedState
18
+ from isar.services.service_connections.mqtt.mqtt_client import props_expiry
18
19
  from isar.state_machine.states.await_next_mission import AwaitNextMission
19
20
  from isar.state_machine.states.blocked_protective_stop import BlockedProtectiveStop
20
21
  from isar.state_machine.states.home import Home
@@ -285,10 +286,11 @@ class StateMachine(object):
285
286
  )
286
287
 
287
288
  self.mqtt_publisher.publish(
288
- topic=settings.TOPIC_ISAR_MISSION,
289
+ topic=settings.TOPIC_ISAR_MISSION + f"/{self.current_mission.id}",
289
290
  payload=json.dumps(payload, cls=EnhancedJSONEncoder),
290
291
  qos=1,
291
292
  retain=True,
293
+ properties=props_expiry(settings.MQTT_MISSION_AND_TASK_EXPIRY),
292
294
  )
293
295
 
294
296
  def publish_task_status(self, task: TASKS) -> None:
@@ -316,10 +318,11 @@ class StateMachine(object):
316
318
  )
317
319
 
318
320
  self.mqtt_publisher.publish(
319
- topic=settings.TOPIC_ISAR_TASK,
321
+ topic=settings.TOPIC_ISAR_TASK + f"/{task.id}",
320
322
  payload=json.dumps(payload, cls=EnhancedJSONEncoder),
321
323
  qos=1,
322
324
  retain=True,
325
+ properties=props_expiry(settings.MQTT_MISSION_AND_TASK_EXPIRY),
323
326
  )
324
327
 
325
328
  def publish_intervention_needed(self, error_message: str) -> None:
@@ -0,0 +1,101 @@
1
+ import logging
2
+ from pathlib import Path
3
+
4
+ from azure.core.exceptions import ResourceExistsError
5
+ from azure.storage.blob import BlobServiceClient, ContainerClient
6
+
7
+ from isar.config.keyvault.keyvault_service import Keyvault
8
+ from isar.config.settings import settings
9
+ from isar.storage.storage_interface import (
10
+ BlobStoragePath,
11
+ StorageException,
12
+ StorageInterface,
13
+ StoragePaths,
14
+ )
15
+ from isar.storage.utilities import construct_metadata_file, construct_paths
16
+ from robot_interface.models.inspection.inspection import InspectionBlob
17
+ from robot_interface.models.mission.mission import Mission
18
+
19
+
20
+ class BlobStorage(StorageInterface):
21
+ def __init__(self, keyvault: Keyvault) -> None:
22
+ self.logger = logging.getLogger("uploader")
23
+
24
+ self.container_client_data = self._get_container_client(
25
+ keyvault, "AZURE-STORAGE-CONNECTION-STRING-DATA"
26
+ )
27
+ self.container_client_metadata = self._get_container_client(
28
+ keyvault, "AZURE-STORAGE-CONNECTION-STRING-METADATA"
29
+ )
30
+
31
+ def _get_container_client(self, keyvault: Keyvault, secret_name: str):
32
+ storage_connection_string = keyvault.get_secret(secret_name).value
33
+
34
+ if storage_connection_string is None:
35
+ raise RuntimeError(f"{secret_name} from keyvault is None")
36
+
37
+ try:
38
+ blob_service_client = BlobServiceClient.from_connection_string(
39
+ storage_connection_string
40
+ )
41
+ except Exception as e:
42
+ self.logger.error("Unable to retrieve blob service client. Error: %s", e)
43
+ raise e
44
+
45
+ container_client = blob_service_client.get_container_client(
46
+ settings.BLOB_CONTAINER
47
+ )
48
+
49
+ if not container_client.exists():
50
+ raise RuntimeError(
51
+ "The configured blob container %s does not exist",
52
+ settings.BLOB_CONTAINER,
53
+ )
54
+ return container_client
55
+
56
+ def store(
57
+ self, inspection: InspectionBlob, mission: Mission
58
+ ) -> StoragePaths[BlobStoragePath]:
59
+ if inspection.data is None:
60
+ raise StorageException("Nothing to store. The inspection data is empty")
61
+
62
+ data_filename, metadata_filename = construct_paths(
63
+ inspection=inspection, mission=mission
64
+ )
65
+
66
+ metadata_bytes: bytes = construct_metadata_file(
67
+ inspection=inspection, mission=mission, filename=data_filename.name
68
+ )
69
+
70
+ data_path = self._upload_file(
71
+ filename=data_filename,
72
+ data=inspection.data,
73
+ container_client=self.container_client_data,
74
+ )
75
+ metadata_path = self._upload_file(
76
+ filename=metadata_filename,
77
+ data=metadata_bytes,
78
+ container_client=self.container_client_metadata,
79
+ )
80
+ return StoragePaths(data_path=data_path, metadata_path=metadata_path)
81
+
82
+ def _upload_file(
83
+ self, filename: Path, data: bytes, container_client: ContainerClient
84
+ ) -> BlobStoragePath:
85
+ blob_client = container_client.get_blob_client(filename.as_posix())
86
+ try:
87
+ blob_client.upload_blob(data=data)
88
+ except ResourceExistsError as e:
89
+ self.logger.error(
90
+ "Blob %s already exists in container. Error: %s", filename.as_posix(), e
91
+ )
92
+ raise StorageException from e
93
+ except Exception as e:
94
+ self.logger.error("An unexpected error occurred while uploading blob")
95
+ raise StorageException from e
96
+
97
+ return BlobStoragePath(
98
+ storage_account=settings.BLOB_STORAGE_ACCOUNT,
99
+ blob_container=settings.BLOB_CONTAINER,
100
+ blob_name=blob_client.blob_name,
101
+ )
@@ -2,7 +2,12 @@ import logging
2
2
  from pathlib import Path
3
3
 
4
4
  from isar.config.settings import settings
5
- from isar.storage.storage_interface import StorageException, StorageInterface
5
+ from isar.storage.storage_interface import (
6
+ LocalStoragePath,
7
+ StorageException,
8
+ StorageInterface,
9
+ StoragePaths,
10
+ )
6
11
  from isar.storage.utilities import construct_metadata_file, construct_paths
7
12
  from robot_interface.models.inspection.inspection import InspectionBlob
8
13
  from robot_interface.models.mission.mission import Mission
@@ -13,33 +18,35 @@ class LocalStorage(StorageInterface):
13
18
  self.root_folder: Path = Path(settings.LOCAL_STORAGE_PATH)
14
19
  self.logger = logging.getLogger("uploader")
15
20
 
16
- def store(self, inspection: InspectionBlob, mission: Mission) -> str:
21
+ def store(
22
+ self, inspection: InspectionBlob, mission: Mission
23
+ ) -> StoragePaths[LocalStoragePath]:
17
24
  if inspection.data is None:
18
25
  raise StorageException("Nothing to store. The inspection data is empty")
19
26
 
20
- local_path, local_metadata_path = construct_paths(
27
+ local_filename, local_metadata_filename = construct_paths(
21
28
  inspection=inspection, mission=mission
22
29
  )
23
30
 
24
- absolute_path: Path = self.root_folder.joinpath(local_path)
25
- absolute_metadata_path: Path = self.root_folder.joinpath(local_metadata_path)
31
+ data_path: Path = self.root_folder.joinpath(local_filename)
32
+ metadata_path: Path = self.root_folder.joinpath(local_metadata_filename)
26
33
 
27
- absolute_path.parent.mkdir(parents=True, exist_ok=True)
34
+ data_path.parent.mkdir(parents=True, exist_ok=True)
28
35
 
29
36
  metadata_bytes: bytes = construct_metadata_file(
30
- inspection=inspection, mission=mission, filename=local_path.name
37
+ inspection=inspection, mission=mission, filename=local_filename.name
31
38
  )
32
39
  try:
33
40
  with (
34
- open(absolute_path, "wb") as file,
35
- open(absolute_metadata_path, "wb") as metadata_file,
41
+ open(data_path, "wb") as file,
42
+ open(metadata_path, "wb") as metadata_file,
36
43
  ):
37
44
  file.write(inspection.data)
38
45
  metadata_file.write(metadata_bytes)
39
46
  except IOError as e:
40
47
  self.logger.warning(
41
48
  f"Failed open/write for one of the following files: \n"
42
- f"{absolute_path}\n{absolute_metadata_path}"
49
+ f"{data_path}\n{metadata_path}"
43
50
  )
44
51
  raise StorageException from e
45
52
  except Exception as e:
@@ -47,4 +54,7 @@ class LocalStorage(StorageInterface):
47
54
  "An unexpected error occurred while writing to local storage"
48
55
  )
49
56
  raise StorageException from e
50
- return str(absolute_path)
57
+ return StoragePaths(
58
+ data_path=LocalStoragePath(file_path=data_path),
59
+ metadata_path=LocalStoragePath(file_path=metadata_path),
60
+ )
@@ -1,25 +1,46 @@
1
1
  from abc import ABCMeta, abstractmethod
2
- from typing import Union
2
+ from pathlib import Path
3
+ from typing import Generic, TypeVar
4
+
5
+ from pydantic import BaseModel
3
6
 
4
7
  from robot_interface.models.inspection.inspection import InspectionBlob
5
8
  from robot_interface.models.mission.mission import Mission
6
9
 
7
10
 
11
+ class BlobStoragePath(BaseModel):
12
+ storage_account: str
13
+ blob_container: str
14
+ blob_name: str
15
+
16
+
17
+ class LocalStoragePath(BaseModel):
18
+ file_path: Path
19
+
20
+
21
+ TPath = TypeVar("TPath", BlobStoragePath, LocalStoragePath)
22
+
23
+
24
+ class StoragePaths(BaseModel, Generic[TPath]):
25
+ data_path: TPath
26
+ metadata_path: TPath
27
+
28
+
8
29
  class StorageInterface(metaclass=ABCMeta):
9
30
  @abstractmethod
10
- def store(self, inspection: InspectionBlob, mission: Mission) -> Union[str, dict]:
31
+ def store(self, inspection: InspectionBlob, mission: Mission) -> StoragePaths:
11
32
  """
12
33
  Parameters
13
34
  ----------
14
- mission : Mission
15
- Mission the inspection is a part of.
16
35
  inspection : InspectionBlob
17
36
  The inspection object to be stored.
37
+ mission : Mission
38
+ Mission the inspection is a part of.
18
39
 
19
40
  Returns
20
41
  ----------
21
- String
22
- Path of the saved inspection
42
+ StoragePaths
43
+ Paths to the data and metadata
23
44
 
24
45
  Raises
25
46
  ----------
@@ -4,11 +4,17 @@ from dataclasses import dataclass
4
4
  from datetime import datetime, timedelta, timezone
5
5
  from queue import Empty, Queue
6
6
  from threading import Event
7
- from typing import List, Union
7
+ from typing import List
8
8
 
9
9
  from isar.config.settings import settings
10
10
  from isar.models.events import Events
11
- from isar.storage.storage_interface import StorageException, StorageInterface
11
+ from isar.storage.storage_interface import (
12
+ BlobStoragePath,
13
+ LocalStoragePath,
14
+ StorageException,
15
+ StorageInterface,
16
+ StoragePaths,
17
+ )
12
18
  from robot_interface.models.inspection.inspection import (
13
19
  Inspection,
14
20
  InspectionBlob,
@@ -133,10 +139,10 @@ class Uploader:
133
139
  except Empty:
134
140
  continue
135
141
 
136
- def _upload(self, item: BlobItem) -> Union[str, dict]:
137
- inspection_path: Union[str, dict] = ""
142
+ def _upload(self, item: BlobItem) -> StoragePaths:
143
+ inspection_paths: StoragePaths
138
144
  try:
139
- inspection_path = item.storage_handler.store(
145
+ inspection_paths = item.storage_handler.store(
140
146
  inspection=item.inspection, mission=item.mission
141
147
  )
142
148
  self.logger.info(
@@ -144,7 +150,7 @@ class Uploader:
144
150
  f"uploaded inspection {str(item.inspection.id)[:8]}"
145
151
  )
146
152
  self._internal_upload_queue.remove(item)
147
- except StorageException:
153
+ except StorageException as e:
148
154
  if item.get_retry_count() < self.max_retry_attempts:
149
155
  item.increment_retry(self.max_wait_time)
150
156
  self.logger.warning(
@@ -160,7 +166,8 @@ class Uploader:
160
166
  f"{str(item.inspection.id)[:8]}. Aborting upload."
161
167
  )
162
168
  self._internal_upload_queue.remove(item)
163
- return inspection_path
169
+ raise e
170
+ return inspection_paths
164
171
 
165
172
  def _process_upload_queue(self) -> None:
166
173
  def should_upload(_item):
@@ -181,10 +188,17 @@ class Uploader:
181
188
  )
182
189
  self._internal_upload_queue.remove(item)
183
190
  elif isinstance(item, BlobItem):
184
- inspection_path = self._upload(item)
185
- self._publish_inspection_result(
186
- inspection=item.inspection, inspection_path=inspection_path
187
- )
191
+ try:
192
+ inspection_paths = self._upload(item)
193
+ if isinstance(inspection_paths.data_path, LocalStoragePath):
194
+ self.logger.info("Skipping publishing when using local storage")
195
+ else:
196
+ self._publish_inspection_result(
197
+ inspection=item.inspection,
198
+ inspection_paths=inspection_paths,
199
+ )
200
+ except StorageException:
201
+ pass
188
202
  else:
189
203
  self.logger.warning(
190
204
  f"Unable to process upload item as its type {type(item).__name__} is not supported"
@@ -223,7 +237,9 @@ class Uploader:
223
237
  )
224
238
 
225
239
  def _publish_inspection_result(
226
- self, inspection: InspectionBlob, inspection_path: Union[str, dict]
240
+ self,
241
+ inspection: InspectionBlob,
242
+ inspection_paths: StoragePaths[BlobStoragePath],
227
243
  ) -> None:
228
244
  """Publishes the reference of the inspection result to the MQTT Broker
229
245
  along with the analysis type
@@ -235,7 +251,8 @@ class Uploader:
235
251
  isar_id=settings.ISAR_ID,
236
252
  robot_name=settings.ROBOT_NAME,
237
253
  inspection_id=inspection.id,
238
- inspection_path=inspection_path,
254
+ blob_storage_data_path=inspection_paths.data_path,
255
+ blob_storage_metadata_path=inspection_paths.metadata_path,
239
256
  installation_code=settings.PLANT_SHORT_NAME,
240
257
  tag_id=inspection.metadata.tag_id,
241
258
  inspection_type=type(inspection).__name__,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: isar
3
- Version: 1.33.1
3
+ Version: 1.33.2
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
@@ -5,6 +5,10 @@ from datetime import datetime, timezone
5
5
  from queue import Queue
6
6
  from typing import Callable, Tuple
7
7
 
8
+ from paho.mqtt.packettypes import PacketTypes
9
+ from paho.mqtt.properties import Properties
10
+
11
+ from isar.config.settings import settings
8
12
  from robot_interface.models.exceptions.robot_exceptions import (
9
13
  RobotTelemetryException,
10
14
  RobotTelemetryNoUpdateException,
@@ -14,10 +18,21 @@ from robot_interface.telemetry.payloads import CloudHealthPayload
14
18
  from robot_interface.utilities.json_service import EnhancedJSONEncoder
15
19
 
16
20
 
21
+ def props_expiry(seconds: int) -> Properties:
22
+ p = Properties(PacketTypes.PUBLISH)
23
+ p.MessageExpiryInterval = seconds
24
+ return p
25
+
26
+
17
27
  class MqttClientInterface(metaclass=ABCMeta):
18
28
  @abstractmethod
19
29
  def publish(
20
- self, topic: str, payload: str, qos: int = 0, retain: bool = False
30
+ self,
31
+ topic: str,
32
+ payload: str,
33
+ qos: int = 0,
34
+ retain: bool = False,
35
+ properties: Properties = None,
21
36
  ) -> None:
22
37
  """
23
38
  Parameters
@@ -42,9 +57,20 @@ class MqttPublisher(MqttClientInterface):
42
57
  self.mqtt_queue: Queue = mqtt_queue
43
58
 
44
59
  def publish(
45
- self, topic: str, payload: str, qos: int = 0, retain: bool = False
60
+ self,
61
+ topic: str,
62
+ payload: str,
63
+ qos: int = 0,
64
+ retain: bool = False,
65
+ properties: Properties = None,
46
66
  ) -> None:
47
- queue_message: Tuple[str, str, int, bool] = (topic, payload, qos, retain)
67
+ queue_message: Tuple[str, str, int, bool, Properties] = (
68
+ topic,
69
+ payload,
70
+ qos,
71
+ retain,
72
+ properties,
73
+ )
48
74
  self.mqtt_queue.put(queue_message)
49
75
 
50
76
 
@@ -57,6 +83,7 @@ class MqttTelemetryPublisher(MqttClientInterface):
57
83
  interval: float,
58
84
  qos: int = 0,
59
85
  retain: bool = False,
86
+ properties: Properties = None,
60
87
  ) -> None:
61
88
  self.mqtt_queue: Queue = mqtt_queue
62
89
  self.telemetry_method: Callable = telemetry_method
@@ -64,9 +91,13 @@ class MqttTelemetryPublisher(MqttClientInterface):
64
91
  self.interval: float = interval
65
92
  self.qos: int = qos
66
93
  self.retain: bool = retain
94
+ self.properties: Properties = properties
67
95
 
68
96
  def run(self, isar_id: str, robot_name: str) -> None:
69
97
  self.cloud_health_topic: str = f"isar/{isar_id}/cloud_health"
98
+ self.battery_topic: str = f"isar/{isar_id}/battery"
99
+ self.pose_topic: str = f"isar/{isar_id}/pose"
100
+ self.pressure_topic: str = f"isar/{isar_id}/pressure"
70
101
  topic: str
71
102
  payload: str
72
103
 
@@ -84,12 +115,37 @@ class MqttTelemetryPublisher(MqttClientInterface):
84
115
  )
85
116
  topic = self.cloud_health_topic
86
117
 
87
- self.publish(topic=topic, payload=payload, qos=self.qos, retain=self.retain)
118
+ publish_properties = self.properties
119
+
120
+ if topic in (
121
+ self.battery_topic,
122
+ self.pose_topic,
123
+ self.pressure_topic,
124
+ ):
125
+ publish_properties = props_expiry(settings.MQTT_TELEMETRY_EXPIRY)
88
126
 
127
+ self.publish(
128
+ topic=topic,
129
+ payload=payload,
130
+ qos=self.qos,
131
+ retain=self.retain,
132
+ properties=publish_properties,
133
+ )
89
134
  time.sleep(self.interval)
90
135
 
91
136
  def publish(
92
- self, topic: str, payload: str, qos: int = 0, retain: bool = False
137
+ self,
138
+ topic: str,
139
+ payload: str,
140
+ qos: int = 0,
141
+ retain: bool = False,
142
+ properties: Properties = None,
93
143
  ) -> None:
94
- queue_message: Tuple[str, str, int, bool] = (topic, payload, qos, retain)
144
+ queue_message: Tuple[str, str, int, bool, Properties] = (
145
+ topic,
146
+ payload,
147
+ qos,
148
+ retain,
149
+ properties,
150
+ )
95
151
  self.mqtt_queue.put(queue_message)
@@ -1,9 +1,10 @@
1
1
  from dataclasses import dataclass
2
2
  from datetime import datetime
3
- from typing import List, Optional, Union
3
+ from typing import List, Optional
4
4
 
5
5
  from alitra import Pose
6
6
 
7
+ from isar.storage.storage_interface import BlobStoragePath
7
8
  from robot_interface.models.exceptions.robot_exceptions import ErrorReason
8
9
  from robot_interface.models.mission.status import MissionStatus, RobotStatus, TaskStatus
9
10
  from robot_interface.models.mission.task import TaskTypes
@@ -119,7 +120,8 @@ class InspectionResultPayload:
119
120
  isar_id: str
120
121
  robot_name: str
121
122
  inspection_id: str
122
- inspection_path: Union[str, dict]
123
+ blob_storage_data_path: BlobStoragePath
124
+ blob_storage_metadata_path: BlobStoragePath
123
125
  installation_code: str
124
126
  tag_id: Optional[str]
125
127
  inspection_type: Optional[str]
@@ -7,6 +7,7 @@ from uuid import UUID
7
7
 
8
8
  import numpy as np
9
9
  from alitra import Orientation
10
+ from pydantic import BaseModel
10
11
 
11
12
 
12
13
  class EnhancedJSONEncoder(json.JSONEncoder):
@@ -15,6 +16,11 @@ class EnhancedJSONEncoder(json.JSONEncoder):
15
16
  """
16
17
 
17
18
  def default(self, o):
19
+ if isinstance(o, BaseModel):
20
+ dump = getattr(o, "model_dump", None)
21
+ if callable(dump):
22
+ return dump()
23
+ return o.__dict__
18
24
  if is_dataclass(o):
19
25
  return asdict(o) # type: ignore
20
26
  if isinstance(o, UUID):