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.
- isar/__init__.py +2 -5
- isar/apis/api.py +159 -66
- isar/apis/models/__init__.py +0 -1
- isar/apis/models/models.py +22 -12
- isar/apis/models/start_mission_definition.py +128 -123
- isar/apis/robot_control/robot_controller.py +41 -0
- isar/apis/schedule/scheduling_controller.py +135 -121
- isar/apis/security/authentication.py +5 -5
- isar/config/certs/ca-cert.pem +32 -32
- isar/config/keyvault/keyvault_service.py +1 -2
- isar/config/log.py +47 -39
- isar/config/logging.conf +16 -31
- isar/config/open_telemetry.py +102 -0
- isar/config/predefined_mission_definition/default_exr.json +49 -0
- isar/config/predefined_mission_definition/default_mission.json +1 -5
- isar/config/predefined_mission_definition/default_turtlebot.json +4 -11
- isar/config/predefined_missions/default.json +67 -87
- isar/config/predefined_missions/default_extra_capabilities.json +107 -0
- isar/config/settings.py +119 -142
- isar/eventhandlers/eventhandler.py +123 -0
- isar/mission_planner/local_planner.py +6 -20
- isar/mission_planner/mission_planner_interface.py +1 -1
- isar/models/events.py +184 -0
- isar/models/status.py +18 -0
- isar/modules.py +118 -205
- isar/robot/robot.py +377 -0
- isar/robot/robot_battery.py +60 -0
- isar/robot/robot_monitor_mission.py +357 -0
- isar/robot/robot_pause_mission.py +74 -0
- isar/robot/robot_resume_mission.py +67 -0
- isar/robot/robot_start_mission.py +66 -0
- isar/robot/robot_status.py +61 -0
- isar/robot/robot_stop_mission.py +68 -0
- isar/robot/robot_upload_inspection.py +75 -0
- isar/script.py +171 -0
- isar/services/service_connections/mqtt/mqtt_client.py +47 -11
- isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +32 -0
- isar/services/service_connections/mqtt/robot_info_publisher.py +4 -3
- isar/services/service_connections/persistent_memory.py +69 -0
- isar/services/utilities/mqtt_utilities.py +93 -0
- isar/services/utilities/robot_utilities.py +20 -0
- isar/services/utilities/scheduling_utilities.py +393 -65
- isar/state_machine/state_machine.py +227 -486
- isar/state_machine/states/__init__.py +0 -7
- isar/state_machine/states/await_next_mission.py +114 -0
- isar/state_machine/states/blocked_protective_stop.py +60 -0
- isar/state_machine/states/going_to_lockdown.py +95 -0
- isar/state_machine/states/going_to_recharging.py +92 -0
- isar/state_machine/states/home.py +115 -0
- isar/state_machine/states/intervention_needed.py +77 -0
- isar/state_machine/states/lockdown.py +38 -0
- isar/state_machine/states/maintenance.py +36 -0
- isar/state_machine/states/monitor.py +137 -166
- isar/state_machine/states/offline.py +60 -0
- isar/state_machine/states/paused.py +92 -23
- isar/state_machine/states/pausing.py +48 -0
- isar/state_machine/states/pausing_return_home.py +48 -0
- isar/state_machine/states/recharging.py +80 -0
- isar/state_machine/states/resuming.py +57 -0
- isar/state_machine/states/resuming_return_home.py +64 -0
- isar/state_machine/states/return_home_paused.py +109 -0
- isar/state_machine/states/returning_home.py +217 -0
- isar/state_machine/states/stopping.py +61 -0
- isar/state_machine/states/stopping_due_to_maintenance.py +61 -0
- isar/state_machine/states/stopping_go_to_lockdown.py +60 -0
- isar/state_machine/states/stopping_go_to_recharge.py +51 -0
- isar/state_machine/states/stopping_return_home.py +77 -0
- isar/state_machine/states/unknown_status.py +72 -0
- isar/state_machine/states_enum.py +22 -5
- isar/state_machine/transitions/mission.py +192 -0
- isar/state_machine/transitions/return_home.py +106 -0
- isar/state_machine/transitions/robot_status.py +80 -0
- isar/state_machine/utils/common_event_handlers.py +73 -0
- isar/storage/blob_storage.py +71 -45
- isar/storage/local_storage.py +28 -14
- isar/storage/storage_interface.py +28 -6
- isar/storage/uploader.py +184 -55
- isar/storage/utilities.py +35 -27
- isar-1.34.9.dist-info/METADATA +496 -0
- isar-1.34.9.dist-info/RECORD +135 -0
- {isar-1.15.0.dist-info → isar-1.34.9.dist-info}/WHEEL +1 -1
- isar-1.34.9.dist-info/entry_points.txt +3 -0
- robot_interface/models/exceptions/__init__.py +0 -7
- robot_interface/models/exceptions/robot_exceptions.py +274 -4
- robot_interface/models/initialize/__init__.py +0 -1
- robot_interface/models/inspection/__init__.py +0 -13
- robot_interface/models/inspection/inspection.py +43 -34
- robot_interface/models/mission/mission.py +18 -14
- robot_interface/models/mission/status.py +20 -25
- robot_interface/models/mission/task.py +156 -92
- robot_interface/models/robots/battery_state.py +6 -0
- robot_interface/models/robots/media.py +13 -0
- robot_interface/models/robots/robot_model.py +7 -7
- robot_interface/robot_interface.py +135 -66
- robot_interface/telemetry/mqtt_client.py +84 -12
- robot_interface/telemetry/payloads.py +111 -12
- robot_interface/utilities/json_service.py +7 -1
- isar/config/predefined_missions/default_turtlebot.json +0 -110
- isar/config/predefined_poses/__init__.py +0 -0
- isar/config/predefined_poses/predefined_poses.py +0 -616
- isar/config/settings.env +0 -26
- isar/mission_planner/sequential_task_selector.py +0 -23
- isar/mission_planner/task_selector_interface.py +0 -31
- isar/models/communication/__init__.py +0 -0
- isar/models/communication/message.py +0 -12
- isar/models/communication/queues/__init__.py +0 -4
- isar/models/communication/queues/queue_io.py +0 -12
- isar/models/communication/queues/queue_timeout_error.py +0 -2
- isar/models/communication/queues/queues.py +0 -19
- isar/models/communication/queues/status_queue.py +0 -20
- isar/models/mission_metadata/__init__.py +0 -0
- isar/services/readers/__init__.py +0 -0
- isar/services/readers/base_reader.py +0 -37
- isar/services/service_connections/mqtt/robot_status_publisher.py +0 -93
- isar/services/service_connections/stid/__init__.py +0 -0
- isar/services/service_connections/stid/stid_service.py +0 -45
- isar/services/utilities/queue_utilities.py +0 -39
- isar/state_machine/states/idle.py +0 -40
- isar/state_machine/states/initialize.py +0 -60
- isar/state_machine/states/initiate.py +0 -129
- isar/state_machine/states/off.py +0 -18
- isar/state_machine/states/stop.py +0 -78
- isar/storage/slimm_storage.py +0 -181
- isar-1.15.0.dist-info/METADATA +0 -417
- isar-1.15.0.dist-info/RECORD +0 -113
- robot_interface/models/initialize/initialize_params.py +0 -9
- robot_interface/models/mission/step.py +0 -211
- {isar-1.15.0.dist-info → isar-1.34.9.dist-info/licenses}/LICENSE +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
69
|
+
return datetime.now(timezone.utc) >= self._next_retry_time
|
|
39
70
|
|
|
40
71
|
def seconds_until_retry(self) -> int:
|
|
41
|
-
return max(
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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,
|
|
98
|
-
|
|
153
|
+
def _upload(self, item: BlobItem) -> StoragePaths:
|
|
154
|
+
inspection_paths: StoragePaths
|
|
99
155
|
try:
|
|
100
|
-
|
|
101
|
-
inspection=
|
|
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(
|
|
105
|
-
f"uploaded inspection {str(
|
|
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(
|
|
108
|
-
except StorageException:
|
|
109
|
-
if
|
|
110
|
-
|
|
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(
|
|
168
|
+
f"Storage handler: {type(item.storage_handler).__name__} "
|
|
113
169
|
f"failed to upload inspection: "
|
|
114
|
-
f"{str(
|
|
115
|
-
f"Retrying in {
|
|
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(
|
|
175
|
+
f"Storage handler: {type(item.storage_handler).__name__} "
|
|
121
176
|
f"exceeded max retries to upload inspection: "
|
|
122
|
-
f"{str(
|
|
177
|
+
f"{str(item.inspection.id)[:8]}. Aborting upload."
|
|
123
178
|
)
|
|
124
|
-
|
|
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
|
-
|
|
192
|
+
item for item in self._internal_upload_queue if should_upload(item)
|
|
129
193
|
]
|
|
130
194
|
for item in ready_items:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
137
|
-
self,
|
|
257
|
+
def _publish_inspection_result(
|
|
258
|
+
self,
|
|
259
|
+
inspection: InspectionBlob,
|
|
260
|
+
inspection_paths: StoragePaths[BlobStoragePath],
|
|
138
261
|
) -> None:
|
|
139
|
-
"""Publishes the
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
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
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Tuple
|
|
5
5
|
|
|
6
|
-
from
|
|
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
|
-
"
|
|
60
|
+
"data_files": [
|
|
46
61
|
{
|
|
47
62
|
"folder": f"/{get_foldername(mission=mission)}",
|
|
48
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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}"
|