isar 1.15.0__py3-none-any.whl → 1.34.9__py3-none-any.whl

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.
Files changed (129) hide show
  1. isar/__init__.py +2 -5
  2. isar/apis/api.py +159 -66
  3. isar/apis/models/__init__.py +0 -1
  4. isar/apis/models/models.py +22 -12
  5. isar/apis/models/start_mission_definition.py +128 -123
  6. isar/apis/robot_control/robot_controller.py +41 -0
  7. isar/apis/schedule/scheduling_controller.py +135 -121
  8. isar/apis/security/authentication.py +5 -5
  9. isar/config/certs/ca-cert.pem +32 -32
  10. isar/config/keyvault/keyvault_service.py +1 -2
  11. isar/config/log.py +47 -39
  12. isar/config/logging.conf +16 -31
  13. isar/config/open_telemetry.py +102 -0
  14. isar/config/predefined_mission_definition/default_exr.json +49 -0
  15. isar/config/predefined_mission_definition/default_mission.json +1 -5
  16. isar/config/predefined_mission_definition/default_turtlebot.json +4 -11
  17. isar/config/predefined_missions/default.json +67 -87
  18. isar/config/predefined_missions/default_extra_capabilities.json +107 -0
  19. isar/config/settings.py +119 -142
  20. isar/eventhandlers/eventhandler.py +123 -0
  21. isar/mission_planner/local_planner.py +6 -20
  22. isar/mission_planner/mission_planner_interface.py +1 -1
  23. isar/models/events.py +184 -0
  24. isar/models/status.py +18 -0
  25. isar/modules.py +118 -205
  26. isar/robot/robot.py +377 -0
  27. isar/robot/robot_battery.py +60 -0
  28. isar/robot/robot_monitor_mission.py +357 -0
  29. isar/robot/robot_pause_mission.py +74 -0
  30. isar/robot/robot_resume_mission.py +67 -0
  31. isar/robot/robot_start_mission.py +66 -0
  32. isar/robot/robot_status.py +61 -0
  33. isar/robot/robot_stop_mission.py +68 -0
  34. isar/robot/robot_upload_inspection.py +75 -0
  35. isar/script.py +171 -0
  36. isar/services/service_connections/mqtt/mqtt_client.py +47 -11
  37. isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +32 -0
  38. isar/services/service_connections/mqtt/robot_info_publisher.py +4 -3
  39. isar/services/service_connections/persistent_memory.py +69 -0
  40. isar/services/utilities/mqtt_utilities.py +93 -0
  41. isar/services/utilities/robot_utilities.py +20 -0
  42. isar/services/utilities/scheduling_utilities.py +393 -65
  43. isar/state_machine/state_machine.py +227 -486
  44. isar/state_machine/states/__init__.py +0 -7
  45. isar/state_machine/states/await_next_mission.py +114 -0
  46. isar/state_machine/states/blocked_protective_stop.py +60 -0
  47. isar/state_machine/states/going_to_lockdown.py +95 -0
  48. isar/state_machine/states/going_to_recharging.py +92 -0
  49. isar/state_machine/states/home.py +115 -0
  50. isar/state_machine/states/intervention_needed.py +77 -0
  51. isar/state_machine/states/lockdown.py +38 -0
  52. isar/state_machine/states/maintenance.py +36 -0
  53. isar/state_machine/states/monitor.py +137 -166
  54. isar/state_machine/states/offline.py +60 -0
  55. isar/state_machine/states/paused.py +92 -23
  56. isar/state_machine/states/pausing.py +48 -0
  57. isar/state_machine/states/pausing_return_home.py +48 -0
  58. isar/state_machine/states/recharging.py +80 -0
  59. isar/state_machine/states/resuming.py +57 -0
  60. isar/state_machine/states/resuming_return_home.py +64 -0
  61. isar/state_machine/states/return_home_paused.py +109 -0
  62. isar/state_machine/states/returning_home.py +217 -0
  63. isar/state_machine/states/stopping.py +61 -0
  64. isar/state_machine/states/stopping_due_to_maintenance.py +61 -0
  65. isar/state_machine/states/stopping_go_to_lockdown.py +60 -0
  66. isar/state_machine/states/stopping_go_to_recharge.py +51 -0
  67. isar/state_machine/states/stopping_return_home.py +77 -0
  68. isar/state_machine/states/unknown_status.py +72 -0
  69. isar/state_machine/states_enum.py +22 -5
  70. isar/state_machine/transitions/mission.py +192 -0
  71. isar/state_machine/transitions/return_home.py +106 -0
  72. isar/state_machine/transitions/robot_status.py +80 -0
  73. isar/state_machine/utils/common_event_handlers.py +73 -0
  74. isar/storage/blob_storage.py +71 -45
  75. isar/storage/local_storage.py +28 -14
  76. isar/storage/storage_interface.py +28 -6
  77. isar/storage/uploader.py +184 -55
  78. isar/storage/utilities.py +35 -27
  79. isar-1.34.9.dist-info/METADATA +496 -0
  80. isar-1.34.9.dist-info/RECORD +135 -0
  81. {isar-1.15.0.dist-info → isar-1.34.9.dist-info}/WHEEL +1 -1
  82. isar-1.34.9.dist-info/entry_points.txt +3 -0
  83. robot_interface/models/exceptions/__init__.py +0 -7
  84. robot_interface/models/exceptions/robot_exceptions.py +274 -4
  85. robot_interface/models/initialize/__init__.py +0 -1
  86. robot_interface/models/inspection/__init__.py +0 -13
  87. robot_interface/models/inspection/inspection.py +43 -34
  88. robot_interface/models/mission/mission.py +18 -14
  89. robot_interface/models/mission/status.py +20 -25
  90. robot_interface/models/mission/task.py +156 -92
  91. robot_interface/models/robots/battery_state.py +6 -0
  92. robot_interface/models/robots/media.py +13 -0
  93. robot_interface/models/robots/robot_model.py +7 -7
  94. robot_interface/robot_interface.py +135 -66
  95. robot_interface/telemetry/mqtt_client.py +84 -12
  96. robot_interface/telemetry/payloads.py +111 -12
  97. robot_interface/utilities/json_service.py +7 -1
  98. isar/config/predefined_missions/default_turtlebot.json +0 -110
  99. isar/config/predefined_poses/__init__.py +0 -0
  100. isar/config/predefined_poses/predefined_poses.py +0 -616
  101. isar/config/settings.env +0 -26
  102. isar/mission_planner/sequential_task_selector.py +0 -23
  103. isar/mission_planner/task_selector_interface.py +0 -31
  104. isar/models/communication/__init__.py +0 -0
  105. isar/models/communication/message.py +0 -12
  106. isar/models/communication/queues/__init__.py +0 -4
  107. isar/models/communication/queues/queue_io.py +0 -12
  108. isar/models/communication/queues/queue_timeout_error.py +0 -2
  109. isar/models/communication/queues/queues.py +0 -19
  110. isar/models/communication/queues/status_queue.py +0 -20
  111. isar/models/mission_metadata/__init__.py +0 -0
  112. isar/services/readers/__init__.py +0 -0
  113. isar/services/readers/base_reader.py +0 -37
  114. isar/services/service_connections/mqtt/robot_status_publisher.py +0 -93
  115. isar/services/service_connections/stid/__init__.py +0 -0
  116. isar/services/service_connections/stid/stid_service.py +0 -45
  117. isar/services/utilities/queue_utilities.py +0 -39
  118. isar/state_machine/states/idle.py +0 -40
  119. isar/state_machine/states/initialize.py +0 -60
  120. isar/state_machine/states/initiate.py +0 -129
  121. isar/state_machine/states/off.py +0 -18
  122. isar/state_machine/states/stop.py +0 -78
  123. isar/storage/slimm_storage.py +0 -181
  124. isar-1.15.0.dist-info/METADATA +0 -417
  125. isar-1.15.0.dist-info/RECORD +0 -113
  126. robot_interface/models/initialize/initialize_params.py +0 -9
  127. robot_interface/models/mission/step.py +0 -211
  128. {isar-1.15.0.dist-info → isar-1.34.9.dist-info/licenses}/LICENSE +0 -0
  129. {isar-1.15.0.dist-info → isar-1.34.9.dist-info}/top_level.txt +0 -0
isar/storage/uploader.py CHANGED
@@ -1,33 +1,64 @@
1
1
  import json
2
2
  import logging
3
3
  from dataclasses import dataclass
4
- from datetime import datetime, timedelta
4
+ from datetime import datetime, timedelta, timezone
5
5
  from queue import Empty, Queue
6
+ from threading import Event
6
7
  from typing import List
7
8
 
8
- from injector import inject
9
-
10
9
  from isar.config.settings import settings
11
- from isar.models.communication.queues import Queues
10
+ from isar.models.events import Events
11
+ from isar.storage.storage_interface import (
12
+ BlobStoragePath,
13
+ LocalStoragePath,
14
+ StorageException,
15
+ StorageInterface,
16
+ StoragePaths,
17
+ )
18
+ from robot_interface.models.inspection.inspection import (
19
+ Inspection,
20
+ InspectionBlob,
21
+ InspectionValue,
22
+ )
12
23
  from robot_interface.models.mission.mission import Mission
13
- from isar.storage.storage_interface import StorageException, StorageInterface
14
- from robot_interface.models.inspection.inspection import Inspection
15
24
  from robot_interface.telemetry.mqtt_client import MqttClientInterface
25
+ from robot_interface.telemetry.payloads import (
26
+ InspectionResultPayload,
27
+ InspectionValuePayload,
28
+ )
16
29
  from robot_interface.utilities.json_service import EnhancedJSONEncoder
17
30
 
18
31
 
32
+ def has_empty_blob_storage_path(storage_paths: StoragePaths):
33
+ for path in (storage_paths.data_path, storage_paths.metadata_path):
34
+ for value in (path.storage_account, path.blob_container, path.blob_name):
35
+ if not (value and value.strip()):
36
+ return True
37
+ return False
38
+
39
+
19
40
  @dataclass
20
41
  class UploaderQueueItem:
21
42
  inspection: Inspection
22
43
  mission: Mission
44
+
45
+
46
+ @dataclass
47
+ class ValueItem(UploaderQueueItem):
48
+ inspection: InspectionValue
49
+
50
+
51
+ @dataclass
52
+ class BlobItem(UploaderQueueItem):
53
+ inspection: InspectionBlob
23
54
  storage_handler: StorageInterface
24
55
  _retry_count: int
25
- _next_retry_time: datetime = datetime.utcnow()
56
+ _next_retry_time: datetime = datetime.now(timezone.utc)
26
57
 
27
58
  def increment_retry(self, max_wait_time: int) -> None:
28
59
  self._retry_count += 1
29
60
  seconds_until_retry: int = min(2**self._retry_count, max_wait_time)
30
- self._next_retry_time = datetime.utcnow() + timedelta(
61
+ self._next_retry_time = datetime.now(timezone.utc) + timedelta(
31
62
  seconds=seconds_until_retry
32
63
  )
33
64
 
@@ -35,17 +66,18 @@ class UploaderQueueItem:
35
66
  return self._retry_count
36
67
 
37
68
  def is_ready_for_upload(self) -> bool:
38
- return datetime.utcnow() >= self._next_retry_time
69
+ return datetime.now(timezone.utc) >= self._next_retry_time
39
70
 
40
71
  def seconds_until_retry(self) -> int:
41
- return max(0, int((self._next_retry_time - datetime.utcnow()).total_seconds()))
72
+ return max(
73
+ 0, int((self._next_retry_time - datetime.now(timezone.utc)).total_seconds())
74
+ )
42
75
 
43
76
 
44
77
  class Uploader:
45
- @inject
46
78
  def __init__(
47
79
  self,
48
- queues: Queues,
80
+ events: Events,
49
81
  storage_handlers: List[StorageInterface],
50
82
  mqtt_publisher: MqttClientInterface,
51
83
  max_wait_time: int = settings.UPLOAD_FAILURE_MAX_WAIT,
@@ -55,8 +87,8 @@ class Uploader:
55
87
 
56
88
  Parameters
57
89
  ----------
58
- queues : Queues
59
- Queues used for cross-thread communication.
90
+ events : Events
91
+ Events used for cross-thread communication.
60
92
  storage_handlers : List[StorageInterface]
61
93
  List of handlers for different upload options
62
94
  max_wait_time : float
@@ -64,7 +96,7 @@ class Uploader:
64
96
  max_retry_attempts : int
65
97
  Maximum attempts to retry an upload when it fails
66
98
  """
67
- self.upload_queue: Queue = queues.upload_queue
99
+ self.upload_queue: Queue = events.upload_queue
68
100
  self.storage_handlers: List[StorageInterface] = storage_handlers
69
101
  self.mqtt_publisher = mqtt_publisher
70
102
 
@@ -72,11 +104,16 @@ class Uploader:
72
104
  self.max_retry_attempts = max_retry_attempts
73
105
  self._internal_upload_queue: List[UploaderQueueItem] = []
74
106
 
107
+ self.signal_thread_quitting: Event = Event()
108
+
75
109
  self.logger = logging.getLogger("uploader")
76
110
 
111
+ def stop(self) -> None:
112
+ self.signal_thread_quitting.set()
113
+
77
114
  def run(self) -> None:
78
115
  self.logger.info("Started uploader")
79
- while True:
116
+ while not self.signal_thread_quitting.wait(0):
80
117
  inspection: Inspection
81
118
  mission: Mission
82
119
  try:
@@ -85,72 +122,164 @@ class Uploader:
85
122
 
86
123
  inspection, mission = self.upload_queue.get(timeout=1)
87
124
 
88
- # If new item from thread queue, add one per handler to internal queue:
89
- for storage_handler in self.storage_handlers:
90
- new_item: UploaderQueueItem = UploaderQueueItem(
91
- inspection, mission, storage_handler, _retry_count=-1
125
+ if not mission:
126
+ self.logger.warning(
127
+ "Failed to upload missing mission from upload queue"
92
128
  )
129
+ continue
130
+
131
+ new_item: UploaderQueueItem
132
+ if isinstance(inspection, InspectionValue):
133
+ new_item = ValueItem(inspection, mission)
93
134
  self._internal_upload_queue.append(new_item)
135
+
136
+ elif isinstance(inspection, InspectionBlob):
137
+ # If new item from thread queue, add one per handler to internal queue:
138
+ for storage_handler in self.storage_handlers:
139
+ new_item = BlobItem(
140
+ inspection, mission, storage_handler, _retry_count=-1
141
+ )
142
+ self._internal_upload_queue.append(new_item)
143
+ else:
144
+ self.logger.warning(
145
+ f"Unable to add UploaderQueueItem as its type {type(inspection).__name__} is unsupported"
146
+ )
94
147
  except Empty:
95
148
  continue
149
+ except Exception as e:
150
+ self.logger.error(f"Unexpected error in uploader thread: {e}")
151
+ continue
96
152
 
97
- def _upload(self, upload_item: UploaderQueueItem) -> str:
98
- inspection_path = ""
153
+ def _upload(self, item: BlobItem) -> StoragePaths:
154
+ inspection_paths: StoragePaths
99
155
  try:
100
- inspection_path = upload_item.storage_handler.store(
101
- inspection=upload_item.inspection, mission=upload_item.mission
156
+ inspection_paths = item.storage_handler.store(
157
+ inspection=item.inspection, mission=item.mission
102
158
  )
103
159
  self.logger.info(
104
- f"Storage handler: {type(upload_item.storage_handler).__name__} "
105
- f"uploaded inspection {str(upload_item.inspection.id)[:8]}"
160
+ f"Storage handler: {type(item.storage_handler).__name__} "
161
+ f"uploaded inspection {str(item.inspection.id)[:8]}"
106
162
  )
107
- self._internal_upload_queue.remove(upload_item)
108
- except StorageException:
109
- if upload_item.get_retry_count() < self.max_retry_attempts:
110
- upload_item.increment_retry(self.max_wait_time)
163
+ self._internal_upload_queue.remove(item)
164
+ except StorageException as e:
165
+ if item.get_retry_count() < self.max_retry_attempts:
166
+ item.increment_retry(self.max_wait_time)
111
167
  self.logger.warning(
112
- f"Storage handler: {type(upload_item.storage_handler).__name__} "
168
+ f"Storage handler: {type(item.storage_handler).__name__} "
113
169
  f"failed to upload inspection: "
114
- f"{str(upload_item.inspection.id)[:8]}. "
115
- f"Retrying in {upload_item.seconds_until_retry()}s."
170
+ f"{str(item.inspection.id)[:8]}. "
171
+ f"Retrying in {item.seconds_until_retry()}s."
116
172
  )
117
173
  else:
118
- self._internal_upload_queue.remove(upload_item)
119
174
  self.logger.error(
120
- f"Storage handler: {type(upload_item.storage_handler).__name__} "
175
+ f"Storage handler: {type(item.storage_handler).__name__} "
121
176
  f"exceeded max retries to upload inspection: "
122
- f"{str(upload_item.inspection.id)[:8]}. Aborting upload."
177
+ f"{str(item.inspection.id)[:8]}. Aborting upload."
123
178
  )
124
- return inspection_path
179
+ self._internal_upload_queue.remove(item)
180
+ raise e
181
+ return inspection_paths
125
182
 
126
183
  def _process_upload_queue(self) -> None:
184
+ def should_upload(_item):
185
+ if isinstance(_item, ValueItem):
186
+ return True
187
+ if _item.is_ready_for_upload():
188
+ return True
189
+ return False
190
+
127
191
  ready_items: List[UploaderQueueItem] = [
128
- x for x in self._internal_upload_queue if x.is_ready_for_upload()
192
+ item for item in self._internal_upload_queue if should_upload(item)
129
193
  ]
130
194
  for item in ready_items:
131
- inspection_path = self._upload(item)
132
- self._publish_inspection_path(
133
- inspection=item.inspection, inspection_path=inspection_path
195
+ if isinstance(item, ValueItem):
196
+ self._publish_inspection_value(item.inspection)
197
+ self.logger.info(
198
+ f"Published value for inspection {str(item.inspection.id)[:8]}"
199
+ )
200
+ self._internal_upload_queue.remove(item)
201
+ elif isinstance(item, BlobItem):
202
+ try:
203
+ inspection_paths = self._upload(item)
204
+ if isinstance(inspection_paths.data_path, LocalStoragePath):
205
+ self.logger.info("Skipping publishing when using local storage")
206
+ elif isinstance(
207
+ inspection_paths.data_path, BlobStoragePath
208
+ ) and has_empty_blob_storage_path(inspection_paths):
209
+ self.logger.warning(
210
+ "Skipping publishing: Blob storage paths are empty for inspection %s",
211
+ str(item.inspection.id)[:8],
212
+ )
213
+ else:
214
+ self._publish_inspection_result(
215
+ inspection=item.inspection,
216
+ inspection_paths=inspection_paths,
217
+ )
218
+ except StorageException:
219
+ pass
220
+ else:
221
+ self.logger.warning(
222
+ f"Unable to process upload item as its type {type(item).__name__} is not supported"
223
+ )
224
+
225
+ def _publish_inspection_value(self, inspection: InspectionValue) -> None:
226
+ if not self.mqtt_publisher:
227
+ return
228
+
229
+ if not isinstance(inspection, InspectionValue):
230
+ logging.warning(
231
+ f"Excpected type InspectionValue but got {type(inspection).__name__} instead"
134
232
  )
233
+ return
234
+
235
+ payload: InspectionValuePayload = InspectionValuePayload(
236
+ isar_id=settings.ISAR_ID,
237
+ robot_name=settings.ROBOT_NAME,
238
+ inspection_id=inspection.id,
239
+ installation_code=settings.PLANT_SHORT_NAME,
240
+ tag_id=inspection.metadata.tag_id,
241
+ inspection_type=type(inspection).__name__,
242
+ inspection_description=inspection.metadata.inspection_description,
243
+ value=inspection.value,
244
+ unit=inspection.unit,
245
+ x=inspection.metadata.robot_pose.position.x,
246
+ y=inspection.metadata.robot_pose.position.y,
247
+ z=inspection.metadata.robot_pose.position.z,
248
+ timestamp=inspection.metadata.start_time,
249
+ )
250
+ self.mqtt_publisher.publish(
251
+ topic=settings.TOPIC_ISAR_INSPECTION_VALUE,
252
+ payload=json.dumps(payload, cls=EnhancedJSONEncoder),
253
+ qos=1,
254
+ retain=True,
255
+ )
135
256
 
136
- def _publish_inspection_path(
137
- self, inspection: Inspection, inspection_path: str
257
+ def _publish_inspection_result(
258
+ self,
259
+ inspection: InspectionBlob,
260
+ inspection_paths: StoragePaths[BlobStoragePath],
138
261
  ) -> None:
139
- """Publishes the image url to the MQTT Broker"""
262
+ """Publishes the reference of the inspection result to the MQTT Broker
263
+ along with the analysis type
264
+ """
140
265
  if not self.mqtt_publisher:
141
266
  return
142
- payload: str = json.dumps(
143
- {
144
- "isar_id": settings.ISAR_ID,
145
- "robot_name": settings.ROBOT_NAME,
146
- "step_id": inspection.id,
147
- "inspection_path": inspection_path,
148
- "timestamp": datetime.utcnow(),
149
- },
150
- cls=EnhancedJSONEncoder,
267
+
268
+ payload: InspectionResultPayload = InspectionResultPayload(
269
+ isar_id=settings.ISAR_ID,
270
+ robot_name=settings.ROBOT_NAME,
271
+ inspection_id=inspection.id,
272
+ blob_storage_data_path=inspection_paths.data_path,
273
+ blob_storage_metadata_path=inspection_paths.metadata_path,
274
+ installation_code=settings.PLANT_SHORT_NAME,
275
+ tag_id=inspection.metadata.tag_id,
276
+ inspection_type=type(inspection).__name__,
277
+ inspection_description=inspection.metadata.inspection_description,
278
+ timestamp=inspection.metadata.start_time,
151
279
  )
152
280
  self.mqtt_publisher.publish(
153
281
  topic=settings.TOPIC_ISAR_INSPECTION_RESULT,
154
- payload=payload,
155
- retain=False,
282
+ payload=json.dumps(payload, cls=EnhancedJSONEncoder),
283
+ qos=1,
284
+ retain=True,
156
285
  )
isar/storage/utilities.py CHANGED
@@ -1,15 +1,13 @@
1
1
  import json
2
- import time
2
+ from datetime import datetime, timezone
3
3
  from pathlib import Path
4
4
  from typing import Tuple
5
5
 
6
- from robot_interface.models.mission.mission import Mission
6
+ from isar.config.settings import settings
7
7
  from robot_interface.models.inspection.inspection import Inspection
8
+ from robot_interface.models.mission.mission import Mission
8
9
  from robot_interface.utilities.json_service import EnhancedJSONEncoder
9
10
 
10
- from isar.config.settings import settings
11
- from datetime import date, datetime
12
-
13
11
 
14
12
  def construct_paths(inspection: Inspection, mission: Mission) -> Tuple[Path, Path]:
15
13
  folder: Path = Path(get_foldername(mission=mission))
@@ -35,29 +33,34 @@ def construct_metadata_file(
35
33
  "plant_code": settings.PLANT_CODE,
36
34
  "media_orientation_reference_system": settings.MEDIA_ORIENTATION_REFERENCE_SYSTEM, # noqa: E501
37
35
  "additional_meta": {
36
+ "inspection_id": inspection.id,
38
37
  "mission_id": mission.id,
39
38
  "mission_name": mission.name,
39
+ "mission_date": datetime.now(timezone.utc).date(),
40
40
  "plant_name": settings.PLANT_NAME,
41
- "mission_date": datetime.utcnow().date(),
42
41
  "isar_id": settings.ISAR_ID,
43
42
  "robot_name": settings.ROBOT_NAME,
43
+ "inspection_description": inspection.metadata.inspection_description,
44
+ "tag": inspection.metadata.tag_id,
45
+ "robot_pose": {
46
+ "position": {
47
+ "x": inspection.metadata.robot_pose.position.x,
48
+ "y": inspection.metadata.robot_pose.position.y,
49
+ "z": inspection.metadata.robot_pose.position.z,
50
+ },
51
+ "orientation": inspection.metadata.robot_pose.orientation.to_quat_array(),
52
+ },
53
+ "target_position": {
54
+ "x": inspection.metadata.target_position.x,
55
+ "y": inspection.metadata.target_position.y,
56
+ "z": inspection.metadata.target_position.z,
57
+ },
58
+ "timestamp": inspection.metadata.start_time,
44
59
  },
45
- "data": [
60
+ "data_files": [
46
61
  {
47
62
  "folder": f"/{get_foldername(mission=mission)}",
48
- "files": [
49
- {
50
- "file_name": filename,
51
- "timestamp": inspection.metadata.start_time,
52
- "x": inspection.metadata.pose.position.x,
53
- "y": inspection.metadata.pose.position.y,
54
- "z": inspection.metadata.pose.position.z,
55
- "tag": inspection.metadata.tag_id,
56
- "additional_media_metadata": {
57
- "orientation": inspection.metadata.pose.orientation.to_quat_array() # noqa: E501
58
- },
59
- }
60
- ],
63
+ "file_name": filename,
61
64
  }
62
65
  ],
63
66
  }
@@ -65,14 +68,19 @@ def construct_metadata_file(
65
68
  return json.dumps(data, cls=EnhancedJSONEncoder, indent=4).encode()
66
69
 
67
70
 
68
- def get_filename(
69
- inspection: Inspection,
70
- ) -> str:
71
- inspection_type: str = type(inspection).__name__
71
+ def get_filename(inspection: Inspection) -> str:
72
+ utc_time: str = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
72
73
  tag: str = inspection.metadata.tag_id if inspection.metadata.tag_id else "no-tag"
73
- epoch_time: int = int(time.time())
74
- return f"{tag}__{inspection_type}__{epoch_time}"
74
+ inspection_type: str = type(inspection).__name__
75
+ inspection_description: str = (
76
+ inspection.metadata.inspection_description.replace(" ", "-")
77
+ if inspection.metadata.inspection_description
78
+ else "NA"
79
+ )
80
+ return f"{tag}__{inspection_type}__{inspection_description}__{utc_time}"
75
81
 
76
82
 
77
83
  def get_foldername(mission: Mission) -> str:
78
- return f"{datetime.utcnow().date()}__{settings.PLANT_SHORT_NAME}__{mission.name}__{mission.id}"
84
+ utc_date: str = datetime.now(timezone.utc).strftime("%Y-%m-%d")
85
+ mission_name: str = mission.name.replace(" ", "-")
86
+ return f"{utc_date}__{settings.PLANT_SHORT_NAME}__{mission_name}__{mission.id}"