isar 1.11.0__py3-none-any.whl → 1.12.0__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.
Potentially problematic release.
This version of isar might be problematic. Click here for more details.
- isar/apis/api.py +54 -1
- isar/apis/models/start_mission_definition.py +40 -31
- isar/apis/security/authentication.py +2 -2
- isar/config/keyvault/keyvault_service.py +36 -21
- isar/config/log.py +42 -13
- isar/config/predefined_mission_definition/__init__.py +0 -0
- isar/config/predefined_mission_definition/default_mission.json +98 -0
- isar/config/predefined_mission_definition/default_turtlebot.json +136 -0
- isar/config/predefined_missions/default.json +84 -84
- isar/config/settings.env +5 -0
- isar/config/settings.py +15 -3
- isar/mission_planner/echo_planner.py +1 -1
- isar/models/mission/status.py +5 -5
- isar/models/mission_metadata/mission_metadata.py +2 -0
- isar/modules.py +3 -2
- isar/services/service_connections/request_handler.py +1 -1
- isar/state_machine/state_machine.py +9 -1
- isar/storage/slimm_storage.py +108 -44
- isar/storage/utilities.py +1 -23
- {isar-1.11.0.dist-info → isar-1.12.0.dist-info}/LICENSE +0 -0
- {isar-1.11.0.dist-info → isar-1.12.0.dist-info}/METADATA +4 -1
- {isar-1.11.0.dist-info → isar-1.12.0.dist-info}/RECORD +26 -23
- {isar-1.11.0.dist-info → isar-1.12.0.dist-info}/WHEEL +0 -0
- {isar-1.11.0.dist-info → isar-1.12.0.dist-info}/top_level.txt +0 -0
- robot_interface/models/inspection/inspection.py +3 -22
- robot_interface/models/mission/step.py +4 -30
|
@@ -1,92 +1,92 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
{
|
|
5
|
-
"steps": [
|
|
2
|
+
"id": 1,
|
|
3
|
+
"tasks": [
|
|
6
4
|
{
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
5
|
+
"steps": [
|
|
6
|
+
{
|
|
7
|
+
"type": "drive_to_pose",
|
|
8
|
+
"pose": {
|
|
9
|
+
"position": {
|
|
10
|
+
"x": -2,
|
|
11
|
+
"y": -2,
|
|
12
|
+
"z": 0,
|
|
13
|
+
"frame": "asset"
|
|
14
|
+
},
|
|
15
|
+
"orientation": {
|
|
16
|
+
"x": 0,
|
|
17
|
+
"y": 0,
|
|
18
|
+
"z": 0.4794255,
|
|
19
|
+
"w": 0.8775826,
|
|
20
|
+
"frame": "asset"
|
|
21
|
+
},
|
|
22
|
+
"frame": "asset"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"type": "take_image",
|
|
27
|
+
"target": {
|
|
28
|
+
"x": 2,
|
|
29
|
+
"y": 2,
|
|
30
|
+
"z": 0,
|
|
31
|
+
"frame": "robot"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
]
|
|
24
35
|
},
|
|
25
36
|
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
37
|
+
"steps": [
|
|
38
|
+
{
|
|
39
|
+
"type": "drive_to_pose",
|
|
40
|
+
"pose": {
|
|
41
|
+
"position": {
|
|
42
|
+
"x": -2,
|
|
43
|
+
"y": 2,
|
|
44
|
+
"z": 0,
|
|
45
|
+
"frame": "asset"
|
|
46
|
+
},
|
|
47
|
+
"orientation": {
|
|
48
|
+
"x": 0,
|
|
49
|
+
"y": 0,
|
|
50
|
+
"z": 0.4794255,
|
|
51
|
+
"w": 0.8775826,
|
|
52
|
+
"frame": "asset"
|
|
53
|
+
},
|
|
54
|
+
"frame": "asset"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"type": "take_thermal_image",
|
|
59
|
+
"target": {
|
|
60
|
+
"x": 2,
|
|
61
|
+
"y": 2,
|
|
62
|
+
"z": 0,
|
|
63
|
+
"frame": "robot"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
]
|
|
56
67
|
},
|
|
57
68
|
{
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
"orientation": {
|
|
80
|
-
"x": 0,
|
|
81
|
-
"y": 0,
|
|
82
|
-
"z": 0.4794255,
|
|
83
|
-
"w": 0.8775826,
|
|
84
|
-
"frame": "asset"
|
|
85
|
-
},
|
|
86
|
-
"frame": "asset"
|
|
87
|
-
}
|
|
69
|
+
"steps": [
|
|
70
|
+
{
|
|
71
|
+
"type": "drive_to_pose",
|
|
72
|
+
"pose": {
|
|
73
|
+
"position": {
|
|
74
|
+
"x": 2,
|
|
75
|
+
"y": 2,
|
|
76
|
+
"z": 0,
|
|
77
|
+
"frame": "asset"
|
|
78
|
+
},
|
|
79
|
+
"orientation": {
|
|
80
|
+
"x": 0,
|
|
81
|
+
"y": 0,
|
|
82
|
+
"z": 0.4794255,
|
|
83
|
+
"w": 0.8775826,
|
|
84
|
+
"frame": "asset"
|
|
85
|
+
},
|
|
86
|
+
"frame": "asset"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
]
|
|
88
90
|
}
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
]
|
|
91
|
+
]
|
|
92
92
|
}
|
isar/config/settings.env
CHANGED
|
@@ -6,6 +6,9 @@ ISAR_STORAGE_LOCAL_ENABLED = true
|
|
|
6
6
|
ISAR_STORAGE_BLOB_ENABLED = false
|
|
7
7
|
ISAR_STORAGE_SLIMM_ENABLED = false
|
|
8
8
|
|
|
9
|
+
ISAR_LOG_HANDLER_LOCAL_ENABLED = true
|
|
10
|
+
ISAR_LOG_HANDLER_APPLICATION_INSIGHTS_ENABLED = false
|
|
11
|
+
|
|
9
12
|
ISAR_MQTT_ENABLED = true
|
|
10
13
|
ISAR_MQTT_SSL_ENABLED = true
|
|
11
14
|
|
|
@@ -19,3 +22,5 @@ ISAR_API_HOST_VIEWED_EXTERNALLY = 0.0.0.0
|
|
|
19
22
|
ISAR_MQTT_USERNAME = isar
|
|
20
23
|
ISAR_MQTT_HOST = localhost
|
|
21
24
|
ISAR_MQTT_PORT = 1883
|
|
25
|
+
|
|
26
|
+
ISAR_KEYVAULT_NAME = IsarDevKv
|
isar/config/settings.py
CHANGED
|
@@ -119,7 +119,7 @@ class Settings(BaseSettings):
|
|
|
119
119
|
MQTT_PORT: int = Field(default=1883)
|
|
120
120
|
|
|
121
121
|
# Keyvault name
|
|
122
|
-
|
|
122
|
+
KEYVAULT_NAME: str = Field(default="IsarDevKv")
|
|
123
123
|
|
|
124
124
|
# URL to storage account for Azure Blob Storage
|
|
125
125
|
BLOB_STORAGE_ACCOUNT_URL: str = Field(
|
|
@@ -151,14 +151,14 @@ class Settings(BaseSettings):
|
|
|
151
151
|
ECHO_API_URL: str = Field(default="https://echohubapi.equinor.com/api")
|
|
152
152
|
|
|
153
153
|
# Client ID for SLIMM App Registration
|
|
154
|
-
SLIMM_CLIENT_ID: str = Field(default="
|
|
154
|
+
SLIMM_CLIENT_ID: str = Field(default="c630ca4d-d8d6-45ab-8cc6-68a363d0de9e")
|
|
155
155
|
|
|
156
156
|
# Scope for access to SLIMM Ingestion API
|
|
157
157
|
SLIMM_APP_SCOPE: str = Field(default=".default")
|
|
158
158
|
|
|
159
159
|
# URL for SLIMM endpoint
|
|
160
160
|
SLIMM_API_URL: str = Field(
|
|
161
|
-
default="https://
|
|
161
|
+
default="https://scinspectioningestapitest.azurewebsites.net/Ingest"
|
|
162
162
|
)
|
|
163
163
|
|
|
164
164
|
# Whether the results should be copied directly into the SLIMM datalake or only the
|
|
@@ -236,6 +236,18 @@ class Settings(BaseSettings):
|
|
|
236
236
|
TOPIC_ISAR_ROBOT_STATUS: str = Field(default="robot_status")
|
|
237
237
|
TOPIC_ISAR_ROBOT_INFO: str = Field(default="robot_info")
|
|
238
238
|
|
|
239
|
+
# Logging
|
|
240
|
+
|
|
241
|
+
# Log handlers
|
|
242
|
+
# Determines which log handlers are used by ISAR
|
|
243
|
+
# Multiple log handlers can be chosen
|
|
244
|
+
# Each handler will be called when logging
|
|
245
|
+
# Selecting a different log handler than local may require certain access rights:
|
|
246
|
+
# - The Azure AI logger requires the 'APPLICATIONINSIGHTS_CONNECTION_STRING' to be set as an environment variable.
|
|
247
|
+
LOG_HANDLER_LOCAL_ENABLED: bool = Field(default=True)
|
|
248
|
+
LOG_HANDLER_APPLICATION_INSIGHTS_ENABLED: bool = Field(default=False)
|
|
249
|
+
|
|
250
|
+
# Log levels
|
|
239
251
|
API_LOG_LEVEL: str = Field(default="INFO")
|
|
240
252
|
MAIN_LOG_LEVEL: str = Field(default="INFO")
|
|
241
253
|
MQTT_LOG_LEVEL: str = Field(default="INFO")
|
isar/models/mission/status.py
CHANGED
|
@@ -3,19 +3,19 @@ from enum import Enum
|
|
|
3
3
|
|
|
4
4
|
class MissionStatus(str, Enum):
|
|
5
5
|
NotStarted: str = "not_started"
|
|
6
|
-
Started: str = "started"
|
|
7
6
|
InProgress: str = "in_progress"
|
|
7
|
+
Paused: str = "paused"
|
|
8
8
|
Failed: str = "failed"
|
|
9
9
|
Cancelled: str = "cancelled"
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
Successful: str = "successful"
|
|
11
|
+
PartiallySuccessful: str = "partially_successful"
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class TaskStatus(str, Enum):
|
|
15
15
|
NotStarted: str = "not_started"
|
|
16
16
|
InProgress: str = "in_progress"
|
|
17
|
-
|
|
17
|
+
Paused: str = "paused"
|
|
18
18
|
Failed: str = "failed"
|
|
19
19
|
Cancelled: str = "cancelled"
|
|
20
20
|
Successful: str = "successful"
|
|
21
|
-
|
|
21
|
+
PartiallySuccessful: str = "partially_successful"
|
|
@@ -17,8 +17,10 @@ class MissionMetadata:
|
|
|
17
17
|
source_url: Optional[str] = None
|
|
18
18
|
plant_code: str = settings.PLANT_CODE
|
|
19
19
|
plant_name: str = settings.PLANT_NAME
|
|
20
|
+
plant_short_name: str = settings.PLANT_SHORT_NAME
|
|
20
21
|
media_orientation_reference_system: str = (
|
|
21
22
|
settings.MEDIA_ORIENTATION_REFERENCE_SYSTEM
|
|
22
23
|
)
|
|
23
24
|
robot_id: str = settings.ROBOT_ID
|
|
25
|
+
robot_model: str = settings.ROBOT_TYPE
|
|
24
26
|
mission_date: date = datetime.utcnow().date()
|
isar/modules.py
CHANGED
|
@@ -36,8 +36,9 @@ class APIModule(Module):
|
|
|
36
36
|
self,
|
|
37
37
|
authenticator: Authenticator,
|
|
38
38
|
scheduling_controller: SchedulingController,
|
|
39
|
+
keyvault: Keyvault,
|
|
39
40
|
) -> API:
|
|
40
|
-
return API(authenticator, scheduling_controller)
|
|
41
|
+
return API(authenticator, scheduling_controller, keyvault)
|
|
41
42
|
|
|
42
43
|
@provider
|
|
43
44
|
@singleton
|
|
@@ -153,7 +154,7 @@ class ServiceModule(Module):
|
|
|
153
154
|
@provider
|
|
154
155
|
@singleton
|
|
155
156
|
def provide_keyvault(self) -> Keyvault:
|
|
156
|
-
return Keyvault(keyvault_name=settings.
|
|
157
|
+
return Keyvault(keyvault_name=settings.KEYVAULT_NAME)
|
|
157
158
|
|
|
158
159
|
@provider
|
|
159
160
|
@singleton
|
|
@@ -51,7 +51,7 @@ class RequestHandler:
|
|
|
51
51
|
response.raise_for_status()
|
|
52
52
|
except HTTPError:
|
|
53
53
|
self.logger.exception(
|
|
54
|
-
f"Http error. Http status code= {response.status_code}"
|
|
54
|
+
f"Http error. Http status code= {response.status_code}, Content: {response.content}"
|
|
55
55
|
)
|
|
56
56
|
raise
|
|
57
57
|
return response
|
|
@@ -262,7 +262,15 @@ class StateMachine(object):
|
|
|
262
262
|
self.update_current_step()
|
|
263
263
|
|
|
264
264
|
def _mission_finished(self) -> None:
|
|
265
|
-
|
|
265
|
+
fail_statuses: List[TaskStatus] = [
|
|
266
|
+
TaskStatus.Cancelled,
|
|
267
|
+
TaskStatus.Failed,
|
|
268
|
+
TaskStatus.PartiallySuccessful,
|
|
269
|
+
]
|
|
270
|
+
if any(task.status in fail_statuses for task in self.current_mission.tasks):
|
|
271
|
+
self.current_mission.status = MissionStatus.PartiallySuccessful
|
|
272
|
+
else:
|
|
273
|
+
self.current_mission.status = MissionStatus.Successful
|
|
266
274
|
self._finalize()
|
|
267
275
|
|
|
268
276
|
def _mission_started(self) -> None:
|
isar/storage/slimm_storage.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import logging
|
|
2
3
|
|
|
3
4
|
from azure.identity import DefaultAzureCredential
|
|
@@ -10,8 +11,8 @@ from isar.models.mission_metadata.mission_metadata import MissionMetadata
|
|
|
10
11
|
from isar.services.auth.azure_credentials import AzureCredentials
|
|
11
12
|
from isar.services.service_connections.request_handler import RequestHandler
|
|
12
13
|
from isar.storage.storage_interface import StorageException, StorageInterface
|
|
13
|
-
from isar.storage.utilities import get_filename
|
|
14
|
-
from robot_interface.models.inspection.inspection import Inspection
|
|
14
|
+
from isar.storage.utilities import get_filename
|
|
15
|
+
from robot_interface.models.inspection.inspection import Inspection, ThermalVideo, Video
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
class SlimmStorage(StorageInterface):
|
|
@@ -30,41 +31,61 @@ class SlimmStorage(StorageInterface):
|
|
|
30
31
|
|
|
31
32
|
self.url: str = settings.SLIMM_API_URL
|
|
32
33
|
|
|
33
|
-
def store(self, inspection: Inspection, metadata: MissionMetadata)
|
|
34
|
-
token: str = self.credentials.get_token(self.request_scope).token
|
|
35
|
-
|
|
36
|
-
request_url: str = f"{self.url}/UploadSingleFile"
|
|
37
|
-
|
|
38
|
-
inspection_type: str = get_inspection_type(inspection=inspection)
|
|
34
|
+
def store(self, inspection: Inspection, metadata: MissionMetadata):
|
|
39
35
|
filename: str = get_filename(
|
|
40
36
|
mission_id=metadata.mission_id,
|
|
41
|
-
inspection_type=
|
|
37
|
+
inspection_type=type(inspection).__name__,
|
|
42
38
|
inspection_id=inspection.id,
|
|
43
39
|
)
|
|
44
|
-
|
|
40
|
+
filename = f"{filename}.{inspection.metadata.file_type}"
|
|
41
|
+
if type(inspection) in [Video, ThermalVideo]:
|
|
42
|
+
self._store_video(filename, inspection, metadata)
|
|
43
|
+
else:
|
|
44
|
+
self._store_image(filename, inspection, metadata)
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
def _store_image(
|
|
47
|
+
self, filename: str, inspection: Inspection, metadata: MissionMetadata
|
|
48
|
+
):
|
|
49
|
+
multiform_body: MultipartEncoder = self._construct_multiform_request_image(
|
|
50
|
+
filename=filename, inspection=inspection, metadata=metadata
|
|
51
|
+
)
|
|
52
|
+
request_url: str = f"{self.url}/UploadSingleImage"
|
|
53
|
+
self._ingest(
|
|
54
|
+
inspection=inspection,
|
|
55
|
+
multiform_body=multiform_body,
|
|
56
|
+
request_url=request_url,
|
|
48
57
|
)
|
|
58
|
+
return
|
|
49
59
|
|
|
60
|
+
def _store_video(
|
|
61
|
+
self, filename: str, inspection: Inspection, metadata: MissionMetadata
|
|
62
|
+
):
|
|
63
|
+
multiform_body: MultipartEncoder = self._construct_multiform_request_video(
|
|
64
|
+
filename=filename, inspection=inspection, metadata=metadata
|
|
65
|
+
)
|
|
66
|
+
request_url = f"{self.url}/UploadSingleVideo"
|
|
50
67
|
self._ingest(
|
|
51
68
|
inspection=inspection,
|
|
52
69
|
multiform_body=multiform_body,
|
|
53
70
|
request_url=request_url,
|
|
54
|
-
token=token,
|
|
55
71
|
)
|
|
72
|
+
return
|
|
56
73
|
|
|
57
|
-
def _ingest(
|
|
74
|
+
def _ingest(
|
|
75
|
+
self, inspection: Inspection, multiform_body: MultipartEncoder, request_url: str
|
|
76
|
+
):
|
|
77
|
+
token: str = self.credentials.get_token(self.request_scope).token
|
|
58
78
|
try:
|
|
59
|
-
self.request_handler.post(
|
|
79
|
+
response = self.request_handler.post(
|
|
60
80
|
url=request_url,
|
|
61
|
-
params={"DataType": "still"},
|
|
62
81
|
data=multiform_body,
|
|
63
82
|
headers={
|
|
64
83
|
"Authorization": f"Bearer {token}",
|
|
65
84
|
"Content-Type": multiform_body.content_type,
|
|
66
85
|
},
|
|
67
86
|
)
|
|
87
|
+
guid = json.loads(response.text)["guid"]
|
|
88
|
+
self.logger.info(f"SLIMM upload GUID: {guid}")
|
|
68
89
|
except (RequestException, HTTPError) as e:
|
|
69
90
|
self.logger.warning(
|
|
70
91
|
f"Failed to upload inspection: {inspection.id} to SLIMM due to a "
|
|
@@ -73,50 +94,93 @@ class SlimmStorage(StorageInterface):
|
|
|
73
94
|
raise StorageException from e
|
|
74
95
|
|
|
75
96
|
@staticmethod
|
|
76
|
-
def
|
|
77
|
-
filename, inspection, metadata
|
|
78
|
-
)
|
|
97
|
+
def _construct_multiform_request_image(
|
|
98
|
+
filename: str, inspection: Inspection, metadata: MissionMetadata
|
|
99
|
+
):
|
|
79
100
|
array_of_orientation = (
|
|
80
101
|
inspection.metadata.time_indexed_pose.pose.orientation.to_quat_array().tolist()
|
|
81
102
|
)
|
|
82
103
|
multiform_body: MultipartEncoder = MultipartEncoder(
|
|
83
104
|
fields={
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
105
|
+
"PlantFacilitySAPCode": metadata.plant_code,
|
|
106
|
+
"InstCode": metadata.plant_short_name,
|
|
107
|
+
"InternalClassification": metadata.data_classification,
|
|
108
|
+
"IsoCountryCode": "NO",
|
|
109
|
+
"Geodetic.CoordinateReferenceSystemCode": metadata.coordinate_reference_system, # noqa: E501
|
|
110
|
+
"Geodetic.VerticalCoordinateReferenceSystemCode": metadata.vertical_reference_system, # noqa: E501
|
|
111
|
+
"Geodetic.OrientationReferenceSystem": metadata.media_orientation_reference_system, # noqa: E501
|
|
112
|
+
"SensorCarrier.SensorCarrierId": metadata.robot_id,
|
|
113
|
+
"SensorCarrier.ModelName": metadata.robot_model,
|
|
114
|
+
"Mission.MissionId": metadata.mission_id,
|
|
115
|
+
"Mission.Client": "Equinor",
|
|
116
|
+
"ImageMetadata.Timestamp": inspection.metadata.start_time.isoformat(), # noqa: E501
|
|
117
|
+
"ImageMetadata.X": str(
|
|
96
118
|
inspection.metadata.time_indexed_pose.pose.position.x
|
|
97
119
|
),
|
|
98
|
-
"
|
|
120
|
+
"ImageMetadata.Y": str(
|
|
99
121
|
inspection.metadata.time_indexed_pose.pose.position.y
|
|
100
122
|
),
|
|
101
|
-
"
|
|
123
|
+
"ImageMetadata.Y": str(
|
|
102
124
|
inspection.metadata.time_indexed_pose.pose.position.z
|
|
103
125
|
),
|
|
104
|
-
"
|
|
105
|
-
|
|
106
|
-
),
|
|
107
|
-
"
|
|
108
|
-
|
|
126
|
+
"ImageMetadata.CameraOrientation1": str(array_of_orientation[0]),
|
|
127
|
+
"ImageMetadata.CameraOrientation2": str(array_of_orientation[1]),
|
|
128
|
+
"ImageMetadata.CameraOrientation3": str(array_of_orientation[2]),
|
|
129
|
+
"ImageMetadata.CameraOrientation4": str(array_of_orientation[3]),
|
|
130
|
+
"ImageMetadata.AnalysisMethods": str(inspection.metadata.analysis),
|
|
131
|
+
"ImageMetadata.Description": str(inspection.metadata.additional),
|
|
132
|
+
"ImageMetadata.FunctionalLocation": inspection.metadata.tag_id # noqa: E501
|
|
133
|
+
if inspection.metadata.tag_id
|
|
134
|
+
else "NA",
|
|
135
|
+
"Filename": filename,
|
|
136
|
+
"AttachedFile": (filename, inspection.data),
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
return multiform_body
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def _construct_multiform_request_video(
|
|
143
|
+
filename: str,
|
|
144
|
+
inspection: Inspection,
|
|
145
|
+
metadata: MissionMetadata,
|
|
146
|
+
):
|
|
147
|
+
array_of_orientation = (
|
|
148
|
+
inspection.metadata.time_indexed_pose.pose.orientation.to_quat_array().tolist()
|
|
149
|
+
)
|
|
150
|
+
multiform_body: MultipartEncoder = MultipartEncoder(
|
|
151
|
+
fields={
|
|
152
|
+
"PlantFacilitySAPCode": metadata.plant_code,
|
|
153
|
+
"InstCode": metadata.plant_short_name,
|
|
154
|
+
"InternalClassification": metadata.data_classification,
|
|
155
|
+
"IsoCountryCode": "NO",
|
|
156
|
+
"Geodetic.CoordinateReferenceSystemCode": metadata.coordinate_reference_system, # noqa: E501
|
|
157
|
+
"Geodetic.VerticalCoordinateReferenceSystemCode": metadata.vertical_reference_system, # noqa: E501
|
|
158
|
+
"Geodetic.OrientationReferenceSystem": metadata.media_orientation_reference_system, # noqa: E501
|
|
159
|
+
"SensorCarrier.SensorCarrierId": metadata.robot_id,
|
|
160
|
+
"SensorCarrier.ModelName": metadata.robot_model,
|
|
161
|
+
"Mission.MissionId": metadata.mission_id,
|
|
162
|
+
"Mission.Client": "Equinor",
|
|
163
|
+
"VideoMetadata.Timestamp": inspection.metadata.start_time.isoformat(), # noqa: E501
|
|
164
|
+
"VideoMetadata.Duration": str(inspection.metadata.duration), # type: ignore
|
|
165
|
+
"VideoMetadata.X": str(
|
|
166
|
+
inspection.metadata.time_indexed_pose.pose.position.x
|
|
109
167
|
),
|
|
110
|
-
"
|
|
111
|
-
|
|
168
|
+
"VideoMetadata.Y": str(
|
|
169
|
+
inspection.metadata.time_indexed_pose.pose.position.y
|
|
112
170
|
),
|
|
113
|
-
"
|
|
114
|
-
|
|
171
|
+
"VideoMetadata.Y": str(
|
|
172
|
+
inspection.metadata.time_indexed_pose.pose.position.z
|
|
115
173
|
),
|
|
116
|
-
"
|
|
174
|
+
"VideoMetadata.CameraOrientation1": str(array_of_orientation[0]),
|
|
175
|
+
"VideoMetadata.CameraOrientation2": str(array_of_orientation[1]),
|
|
176
|
+
"VideoMetadata.CameraOrientation3": str(array_of_orientation[2]),
|
|
177
|
+
"VideoMetadata.CameraOrientation4": str(array_of_orientation[3]),
|
|
178
|
+
"VideoMetadata.AnalysisMethods": str(inspection.metadata.analysis),
|
|
179
|
+
"VideoMetadata.Description": str(inspection.metadata.additional),
|
|
180
|
+
"VideoMetadata.FunctionalLocation": inspection.metadata.tag_id # noqa: E501
|
|
117
181
|
if inspection.metadata.tag_id
|
|
118
182
|
else "NA",
|
|
119
|
-
"
|
|
183
|
+
"Filename": filename,
|
|
120
184
|
"AttachedFile": (filename, inspection.data),
|
|
121
185
|
}
|
|
122
186
|
)
|
isar/storage/utilities.py
CHANGED
|
@@ -14,12 +14,10 @@ from robot_interface.utilities.json_service import EnhancedJSONEncoder
|
|
|
14
14
|
def construct_local_paths(
|
|
15
15
|
inspection: Inspection, metadata: MissionMetadata
|
|
16
16
|
) -> Tuple[Path, Path]:
|
|
17
|
-
inspection_type: str = get_inspection_type(inspection=inspection)
|
|
18
|
-
|
|
19
17
|
folder: Path = Path(str(metadata.mission_id))
|
|
20
18
|
filename: str = get_filename(
|
|
21
19
|
mission_id=metadata.mission_id,
|
|
22
|
-
inspection_type=
|
|
20
|
+
inspection_type=type(inspection).__name__,
|
|
23
21
|
inspection_id=inspection.id,
|
|
24
22
|
)
|
|
25
23
|
|
|
@@ -77,23 +75,3 @@ def get_filename(
|
|
|
77
75
|
inspection_id: UUID,
|
|
78
76
|
) -> str:
|
|
79
77
|
return f"{mission_id}_{inspection_type}_{inspection_id}"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def get_inspection_type(inspection: Inspection) -> str:
|
|
83
|
-
if isinstance(inspection, Image):
|
|
84
|
-
return "image"
|
|
85
|
-
elif isinstance(inspection, ThermalImage):
|
|
86
|
-
return "thermal"
|
|
87
|
-
elif isinstance(inspection, Video):
|
|
88
|
-
return "video"
|
|
89
|
-
elif isinstance(inspection, ThermalVideo):
|
|
90
|
-
return "thermal_video"
|
|
91
|
-
else:
|
|
92
|
-
logging.getLogger("uploader").warning(
|
|
93
|
-
f"Failed to upload inspection with ID: {inspection.id}\n "
|
|
94
|
-
f"This was due to an inspection type ({type(inspection)}) "
|
|
95
|
-
f"which is not supported by the uploader"
|
|
96
|
-
)
|
|
97
|
-
raise StorageException(
|
|
98
|
-
f"Inspection type not supported by the uploader. Got {type(inspection)}"
|
|
99
|
-
)
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: isar
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.12.0
|
|
4
4
|
Summary: Integration and Supervisory control of Autonomous Robots
|
|
5
5
|
Home-page: https://github.com/equinor/isar
|
|
6
6
|
Author: Equinor ASA
|
|
@@ -25,6 +25,9 @@ Requires-Dist: dacite
|
|
|
25
25
|
Requires-Dist: fastapi-azure-auth
|
|
26
26
|
Requires-Dist: fastapi
|
|
27
27
|
Requires-Dist: injector
|
|
28
|
+
Requires-Dist: opencensus-ext-logging
|
|
29
|
+
Requires-Dist: opencensus-ext-requests
|
|
30
|
+
Requires-Dist: opencensus-ext-azure
|
|
28
31
|
Requires-Dist: numpy
|
|
29
32
|
Requires-Dist: paho-mqtt
|
|
30
33
|
Requires-Dist: pydantic
|