isar 1.10.14__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.

Files changed (47) hide show
  1. isar/apis/api.py +54 -4
  2. isar/apis/models/start_mission_definition.py +44 -35
  3. isar/apis/security/authentication.py +4 -5
  4. isar/config/keyvault/keyvault_service.py +38 -21
  5. isar/config/log.py +42 -13
  6. isar/config/predefined_mission_definition/__init__.py +0 -0
  7. isar/config/predefined_mission_definition/default_mission.json +98 -0
  8. isar/config/predefined_mission_definition/default_turtlebot.json +136 -0
  9. isar/config/predefined_missions/default.json +84 -84
  10. isar/config/settings.env +5 -0
  11. isar/config/settings.py +51 -10
  12. isar/mission_planner/echo_planner.py +1 -1
  13. isar/models/communication/queues/queues.py +1 -1
  14. isar/models/mission/status.py +5 -5
  15. isar/models/mission_metadata/mission_metadata.py +2 -0
  16. isar/modules.py +3 -2
  17. isar/services/service_connections/mqtt/mqtt_client.py +0 -18
  18. isar/services/service_connections/mqtt/robot_info_publisher.py +33 -0
  19. isar/services/service_connections/mqtt/robot_status_publisher.py +66 -0
  20. isar/services/service_connections/request_handler.py +1 -1
  21. isar/services/utilities/scheduling_utilities.py +5 -5
  22. isar/services/utilities/threaded_request.py +3 -3
  23. isar/state_machine/state_machine.py +13 -5
  24. isar/state_machine/states/idle.py +6 -6
  25. isar/state_machine/states/initialize.py +9 -8
  26. isar/state_machine/states/initiate_step.py +16 -16
  27. isar/state_machine/states/monitor.py +17 -11
  28. isar/state_machine/states/paused.py +6 -6
  29. isar/state_machine/states/stop_step.py +10 -10
  30. isar/state_machine/states_enum.py +0 -1
  31. isar/storage/local_storage.py +2 -2
  32. isar/storage/slimm_storage.py +107 -41
  33. isar/storage/uploader.py +4 -5
  34. isar/storage/utilities.py +1 -23
  35. {isar-1.10.14.dist-info → isar-1.12.0.dist-info}/LICENSE +0 -0
  36. {isar-1.10.14.dist-info → isar-1.12.0.dist-info}/METADATA +4 -1
  37. {isar-1.10.14.dist-info → isar-1.12.0.dist-info}/RECORD +47 -40
  38. {isar-1.10.14.dist-info → isar-1.12.0.dist-info}/WHEEL +0 -0
  39. {isar-1.10.14.dist-info → isar-1.12.0.dist-info}/top_level.txt +0 -0
  40. robot_interface/models/inspection/inspection.py +3 -22
  41. robot_interface/models/mission/status.py +6 -1
  42. robot_interface/models/mission/step.py +5 -32
  43. robot_interface/models/robots/__init__.py +0 -0
  44. robot_interface/models/robots/robot_model.py +13 -0
  45. robot_interface/robot_interface.py +17 -0
  46. robot_interface/telemetry/payloads.py +34 -0
  47. robot_interface/utilities/json_service.py +3 -0
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import time
3
- from typing import TYPE_CHECKING, Optional
3
+ from typing import Optional, TYPE_CHECKING
4
4
 
5
5
  from transitions import State
6
6
 
@@ -11,19 +11,19 @@ if TYPE_CHECKING:
11
11
 
12
12
 
13
13
  class Idle(State):
14
- def __init__(self, state_machine: "StateMachine"):
14
+ def __init__(self, state_machine: "StateMachine") -> None:
15
15
  super().__init__(name="idle", on_enter=self.start, on_exit=self.stop)
16
16
  self.state_machine: "StateMachine" = state_machine
17
17
  self.logger = logging.getLogger("state_machine")
18
18
 
19
- def start(self):
19
+ def start(self) -> None:
20
20
  self.state_machine.update_state()
21
21
  self._run()
22
22
 
23
- def stop(self):
23
+ def stop(self) -> None:
24
24
  pass
25
25
 
26
- def _run(self):
26
+ def _run(self) -> None:
27
27
  while True:
28
28
  start_mission: Optional[
29
29
  StartMissionMessage
@@ -33,7 +33,7 @@ class Idle(State):
33
33
  mission=start_mission.mission,
34
34
  initial_pose=start_mission.initial_pose,
35
35
  )
36
- transition = self.state_machine.mission_started
36
+ transition = self.state_machine.mission_started # type: ignore
37
37
  break
38
38
  time.sleep(self.state_machine.sleep_time)
39
39
 
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import time
3
- from typing import TYPE_CHECKING, Callable, Optional
3
+ from typing import Callable, Optional, TYPE_CHECKING
4
4
 
5
5
  from injector import inject
6
6
  from transitions import State
@@ -17,23 +17,23 @@ if TYPE_CHECKING:
17
17
 
18
18
  class Initialize(State):
19
19
  @inject
20
- def __init__(self, state_machine: "StateMachine"):
20
+ def __init__(self, state_machine: "StateMachine") -> None:
21
21
  super().__init__(name="initialize", on_enter=self.start, on_exit=self.stop)
22
22
  self.state_machine: "StateMachine" = state_machine
23
23
 
24
24
  self.logger = logging.getLogger("state_machine")
25
25
  self.initialize_thread: Optional[ThreadedRequest] = None
26
26
 
27
- def start(self):
27
+ def start(self) -> None:
28
28
  self.state_machine.update_state()
29
29
  self._run()
30
30
 
31
- def stop(self):
31
+ def stop(self) -> None:
32
32
  if self.initialize_thread:
33
33
  self.initialize_thread.wait_for_thread()
34
34
  self.initialize_thread = None
35
35
 
36
- def _run(self):
36
+ def _run(self) -> None:
37
37
  transition: Callable
38
38
  while True:
39
39
  if not self.initialize_thread:
@@ -41,7 +41,8 @@ class Initialize(State):
41
41
  self.state_machine.robot.initialize
42
42
  )
43
43
  self.initialize_thread.start_thread(
44
- self.state_machine.get_initialize_params()
44
+ self.state_machine.get_initialize_params(),
45
+ name="State Machine Initialize Robot",
45
46
  )
46
47
 
47
48
  try:
@@ -51,9 +52,9 @@ class Initialize(State):
51
52
  continue
52
53
  except RobotException as e:
53
54
  self.logger.error(f"Initialization of robot failed. Error: {e}")
54
- transition = self.state_machine.initialization_failed
55
+ transition = self.state_machine.initialization_failed # type: ignore
55
56
  break
56
57
 
57
- transition = self.state_machine.initialization_successful
58
+ transition = self.state_machine.initialization_successful # type: ignore
58
59
  break
59
60
  transition()
@@ -1,28 +1,25 @@
1
1
  import logging
2
2
  import time
3
- from typing import TYPE_CHECKING, Callable
3
+ from typing import Callable, Optional, TYPE_CHECKING
4
4
 
5
5
  from transitions import State
6
6
 
7
7
  from isar.config.settings import settings
8
- from isar.models.mission.status import MissionStatus
9
8
  from isar.services.utilities.threaded_request import (
10
9
  ThreadedRequest,
11
10
  ThreadedRequestNotFinishedError,
12
11
  )
13
- from isar.state_machine.states_enum import States
14
12
  from robot_interface.models.exceptions import (
15
13
  RobotException,
16
14
  RobotInfeasibleStepException,
17
15
  )
18
- from robot_interface.models.mission.status import StepStatus
19
16
 
20
17
  if TYPE_CHECKING:
21
18
  from isar.state_machine.state_machine import StateMachine
22
19
 
23
20
 
24
21
  class InitiateStep(State):
25
- def __init__(self, state_machine: "StateMachine"):
22
+ def __init__(self, state_machine: "StateMachine") -> None:
26
23
  super().__init__(name="initiate_step", on_enter=self.start, on_exit=self.stop)
27
24
  self.state_machine: "StateMachine" = state_machine
28
25
  self.initiate_step_failure_counter: int = 0
@@ -31,45 +28,48 @@ class InitiateStep(State):
31
28
  )
32
29
  self.logger = logging.getLogger("state_machine")
33
30
 
34
- self.initiate_step_thread = None
31
+ self.initiate_step_thread: Optional[ThreadedRequest] = None
35
32
 
36
- def start(self):
33
+ def start(self) -> None:
37
34
  self.state_machine.update_state()
38
35
  self._run()
39
36
 
40
- def stop(self):
37
+ def stop(self) -> None:
41
38
  self.initiate_step_failure_counter = 0
42
39
  if self.initiate_step_thread:
43
40
  self.initiate_step_thread.wait_for_thread()
44
41
  self.initiate_step_thread = None
45
42
 
46
- def _run(self):
43
+ def _run(self) -> None:
47
44
  transition: Callable
48
45
  while True:
49
46
  if self.state_machine.should_stop_mission():
50
- transition = self.state_machine.stop
47
+ transition = self.state_machine.stop # type: ignore
51
48
  break
52
49
 
53
50
  if self.state_machine.should_pause_mission():
54
- transition = self.state_machine.pause
51
+ transition = self.state_machine.pause # type: ignore
55
52
  break
56
53
 
57
54
  if not self.state_machine.current_task:
58
55
  self.logger.info(
59
56
  f"Completed mission: {self.state_machine.current_mission.id}"
60
57
  )
61
- transition = self.state_machine.mission_finished
58
+ transition = self.state_machine.mission_finished # type: ignore
62
59
  break
63
60
 
64
61
  if not self.initiate_step_thread:
65
62
  self.initiate_step_thread = ThreadedRequest(
66
63
  self.state_machine.robot.initiate_step
67
64
  )
68
- self.initiate_step_thread.start_thread(self.state_machine.current_step)
65
+ self.initiate_step_thread.start_thread(
66
+ self.state_machine.current_step,
67
+ name="State Machine Initiate Step",
68
+ )
69
69
 
70
70
  try:
71
71
  self.initiate_step_thread.get_output()
72
- transition = self.state_machine.step_initiated
72
+ transition = self.state_machine.step_initiated # type: ignore
73
73
  break
74
74
  except ThreadedRequestNotFinishedError:
75
75
  time.sleep(self.state_machine.sleep_time)
@@ -80,7 +80,7 @@ class InitiateStep(State):
80
80
  f"{type(self.state_machine.current_step).__name__}"
81
81
  f"Invalid step: {str(self.state_machine.current_step.id)[:8]}"
82
82
  )
83
- transition = self.state_machine.step_infeasible
83
+ transition = self.state_machine.step_infeasible # type: ignore
84
84
  break
85
85
  except RobotException as e:
86
86
  self.initiate_step_thread = None
@@ -100,7 +100,7 @@ class InitiateStep(State):
100
100
  f"{self.initiate_step_failure_counter_limit} attempts. "
101
101
  f"Cancelling mission."
102
102
  )
103
- transition = self.state_machine.initiate_step_failed
103
+ transition = self.state_machine.initiate_step_failed # type: ignore
104
104
  break
105
105
 
106
106
  time.sleep(self.state_machine.sleep_time)
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  import time
3
3
  from copy import deepcopy
4
- from typing import TYPE_CHECKING, Callable, Optional, Sequence, Tuple
4
+ from typing import Callable, Optional, Sequence, TYPE_CHECKING, Tuple
5
5
 
6
6
  from injector import inject
7
7
  from transitions import State
@@ -21,38 +21,40 @@ if TYPE_CHECKING:
21
21
 
22
22
  class Monitor(State):
23
23
  @inject
24
- def __init__(self, state_machine: "StateMachine"):
24
+ def __init__(self, state_machine: "StateMachine") -> None:
25
25
  super().__init__(name="monitor", on_enter=self.start, on_exit=self.stop)
26
26
  self.state_machine: "StateMachine" = state_machine
27
27
 
28
28
  self.logger = logging.getLogger("state_machine")
29
29
  self.step_status_thread: Optional[ThreadedRequest] = None
30
30
 
31
- def start(self):
31
+ def start(self) -> None:
32
32
  self.state_machine.update_state()
33
33
  self._run()
34
34
 
35
- def stop(self):
35
+ def stop(self) -> None:
36
36
  if self.step_status_thread:
37
37
  self.step_status_thread.wait_for_thread()
38
38
  self.step_status_thread = None
39
39
 
40
- def _run(self):
40
+ def _run(self) -> None:
41
41
  transition: Callable
42
42
  while True:
43
43
  if self.state_machine.should_stop_mission():
44
- transition = self.state_machine.stop
44
+ transition = self.state_machine.stop # type: ignore
45
45
  break
46
46
 
47
47
  if self.state_machine.should_pause_mission():
48
- transition = self.state_machine.pause
48
+ transition = self.state_machine.pause # type: ignore
49
49
  break
50
50
 
51
51
  if not self.step_status_thread:
52
52
  self.step_status_thread = ThreadedRequest(
53
53
  self.state_machine.robot.step_status
54
54
  )
55
- self.step_status_thread.start_thread()
55
+ self.step_status_thread.start_thread(
56
+ name="State Machine Monitor Current Step"
57
+ )
56
58
 
57
59
  try:
58
60
  step_status: StepStatus = self.step_status_thread.get_output()
@@ -65,8 +67,12 @@ class Monitor(State):
65
67
  self.state_machine.current_step.status = step_status
66
68
 
67
69
  if self._step_finished(step=self.state_machine.current_step):
68
- self._process_finished_step(step=self.state_machine.current_step)
69
- transition = self.state_machine.step_finished
70
+ get_inspections_thread = ThreadedRequest(self._process_finished_step)
71
+ get_inspections_thread.start_thread(
72
+ self.state_machine.current_step,
73
+ name="State Machine Get Inspections",
74
+ )
75
+ transition = self.state_machine.step_finished # type: ignore
70
76
  break
71
77
 
72
78
  self.step_status_thread = None
@@ -74,7 +80,7 @@ class Monitor(State):
74
80
 
75
81
  transition()
76
82
 
77
- def _queue_inspections_for_upload(self, current_step: InspectionStep):
83
+ def _queue_inspections_for_upload(self, current_step: InspectionStep) -> None:
78
84
  try:
79
85
  inspections: Sequence[
80
86
  Inspection
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import time
3
- from typing import TYPE_CHECKING, Callable
3
+ from typing import Callable, TYPE_CHECKING
4
4
 
5
5
  from transitions import State
6
6
 
@@ -9,24 +9,24 @@ if TYPE_CHECKING:
9
9
 
10
10
 
11
11
  class Paused(State):
12
- def __init__(self, state_machine: "StateMachine"):
12
+ def __init__(self, state_machine: "StateMachine") -> None:
13
13
  super().__init__(name="paused", on_enter=self.start)
14
14
  self.state_machine: "StateMachine" = state_machine
15
15
  self.logger = logging.getLogger("state_machine")
16
16
 
17
- def start(self):
17
+ def start(self) -> None:
18
18
  self.state_machine.update_state()
19
19
  self._run()
20
20
 
21
- def _run(self):
21
+ def _run(self) -> None:
22
22
  transition: Callable
23
23
  while True:
24
24
  if self.state_machine.should_stop_mission():
25
- transition = self.state_machine.mission_stopped
25
+ transition = self.state_machine.mission_stopped # type: ignore
26
26
  break
27
27
 
28
28
  if self.state_machine.should_resume_mission():
29
- transition = self.state_machine.resume
29
+ transition = self.state_machine.resume # type: ignore
30
30
  break
31
31
 
32
32
  time.sleep(self.state_machine.sleep_time)
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import time
3
- from typing import Callable, TYPE_CHECKING
3
+ from typing import Callable, Optional, TYPE_CHECKING
4
4
 
5
5
  from transitions import State
6
6
 
@@ -15,29 +15,29 @@ if TYPE_CHECKING:
15
15
 
16
16
 
17
17
  class StopStep(State):
18
- def __init__(self, state_machine: "StateMachine"):
18
+ def __init__(self, state_machine: "StateMachine") -> None:
19
19
  super().__init__(name="stop_step", on_enter=self.start, on_exit=self.stop)
20
20
  self.state_machine: "StateMachine" = state_machine
21
21
  self.logger = logging.getLogger("state_machine")
22
- self.stop_thread = None
22
+ self.stop_thread: Optional[ThreadedRequest] = None
23
23
  self._count_number_retries: int = 0
24
24
 
25
- def start(self):
25
+ def start(self) -> None:
26
26
  self.state_machine.update_state()
27
27
  self._run()
28
28
 
29
- def stop(self):
29
+ def stop(self) -> None:
30
30
  if self.stop_thread:
31
31
  self.stop_thread.wait_for_thread()
32
32
  self.stop_thread = None
33
33
  self._count_number_retries = 0
34
34
 
35
- def _run(self):
35
+ def _run(self) -> None:
36
36
  transition: Callable
37
37
  while True:
38
38
  if not self.stop_thread:
39
39
  self.stop_thread = ThreadedRequest(self.state_machine.robot.stop)
40
- self.stop_thread.start_thread()
40
+ self.stop_thread.start_thread(name="State Machine Stop Robot")
41
41
 
42
42
  if self.state_machine.should_stop_mission():
43
43
  self.state_machine.stopped = True
@@ -52,16 +52,16 @@ class StopStep(State):
52
52
  if self.handle_stop_fail(
53
53
  retry_limit=self.state_machine.stop_robot_attempts_limit
54
54
  ):
55
- transition = self.state_machine.mission_stopped
55
+ transition = self.state_machine.mission_stopped # type: ignore
56
56
  break
57
57
 
58
58
  self.logger.warning("Failed to stop robot. Retrying.")
59
59
  self.stop_thread = None
60
60
  continue
61
61
  if self.state_machine.stopped:
62
- transition = self.state_machine.mission_stopped
62
+ transition = self.state_machine.mission_stopped # type: ignore
63
63
  else:
64
- transition = self.state_machine.mission_paused
64
+ transition = self.state_machine.mission_paused # type: ignore
65
65
  break
66
66
 
67
67
  transition()
@@ -2,7 +2,6 @@ from enum import Enum
2
2
 
3
3
 
4
4
  class States(str, Enum):
5
-
6
5
  Off = "off"
7
6
  Idle = "idle"
8
7
  InitiateStep = "initiate_step"
@@ -9,11 +9,11 @@ from robot_interface.models.inspection.inspection import Inspection
9
9
 
10
10
 
11
11
  class LocalStorage(StorageInterface):
12
- def __init__(self):
12
+ def __init__(self) -> None:
13
13
  self.root_folder: Path = Path(settings.LOCAL_STORAGE_PATH)
14
14
  self.logger = logging.getLogger("uploader")
15
15
 
16
- def store(self, inspection: Inspection, metadata: MissionMetadata):
16
+ def store(self, inspection: Inspection, metadata: MissionMetadata) -> None:
17
17
  local_path, local_metadata_path = construct_local_paths(
18
18
  inspection=inspection, metadata=metadata
19
19
  )
@@ -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, get_inspection_type
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):
@@ -31,40 +32,60 @@ class SlimmStorage(StorageInterface):
31
32
  self.url: str = settings.SLIMM_API_URL
32
33
 
33
34
  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)
39
35
  filename: str = get_filename(
40
36
  mission_id=metadata.mission_id,
41
- inspection_type=inspection_type,
37
+ inspection_type=type(inspection).__name__,
42
38
  inspection_id=inspection.id,
43
39
  )
44
- filename_with_ending: str = f"{filename}.{inspection.metadata.file_type}"
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
- multiform_body: MultipartEncoder = self._construct_multiform_request(
47
- filename=filename_with_ending, inspection=inspection, metadata=metadata
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(self, inspection, multiform_body, request_url, token):
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,48 +94,93 @@ class SlimmStorage(StorageInterface):
73
94
  raise StorageException from e
74
95
 
75
96
  @staticmethod
76
- def _construct_multiform_request(filename, inspection, metadata):
97
+ def _construct_multiform_request_image(
98
+ filename: str, inspection: Inspection, metadata: MissionMetadata
99
+ ):
77
100
  array_of_orientation = (
78
101
  inspection.metadata.time_indexed_pose.pose.orientation.to_quat_array().tolist()
79
102
  )
80
103
  multiform_body: MultipartEncoder = MultipartEncoder(
81
104
  fields={
82
- "SchemaMetadata.Mission.MissionId": metadata.mission_id,
83
- "SchemaMetadata.Mission.StartDate": metadata.mission_date.isoformat(),
84
- "SchemaMetadata.Mission.EndDate": metadata.mission_date.isoformat(),
85
- "SchemaMetadata.Geodetic.CoordinateReferenceSystemCode": metadata.coordinate_reference_system, # noqa: E501
86
- "SchemaMetadata.Geodetic.VerticalCoordinateReferenceSystemCode": metadata.vertical_reference_system, # noqa: E501
87
- "SchemaMetadata.Geodetic.OrientationReferenceSystem": metadata.media_orientation_reference_system, # noqa: E501
88
- "SchemaMetadata.SensorCarrier.Id": metadata.robot_id,
89
- "SchemaMetadata.InternalClassification": metadata.data_classification,
90
- "SchemaMetadata.PlantFacilitySAPCode": metadata.plant_code,
91
- "SchemaMetadata.Mission.Client": "Equinor",
92
- "SchemaMetadata.IsoCountryCode": "NO",
93
- "AttachedFileMetadata.X": str(
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(
94
118
  inspection.metadata.time_indexed_pose.pose.position.x
95
119
  ),
96
- "AttachedFileMetadata.Y": str(
120
+ "ImageMetadata.Y": str(
97
121
  inspection.metadata.time_indexed_pose.pose.position.y
98
122
  ),
99
- "AttachedFileMetadata.Z": str(
123
+ "ImageMetadata.Y": str(
100
124
  inspection.metadata.time_indexed_pose.pose.position.z
101
125
  ),
102
- "AttachedFileMetadata.CameraOrientation[0]": str(
103
- array_of_orientation[0]
104
- ),
105
- "AttachedFileMetadata.CameraOrientation[1]": str(
106
- array_of_orientation[1]
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
107
167
  ),
108
- "AttachedFileMetadata.CameraOrientation[2]": str(
109
- array_of_orientation[2]
168
+ "VideoMetadata.Y": str(
169
+ inspection.metadata.time_indexed_pose.pose.position.y
110
170
  ),
111
- "AttachedFileMetadata.CameraOrientation[3]": str(
112
- array_of_orientation[3]
171
+ "VideoMetadata.Y": str(
172
+ inspection.metadata.time_indexed_pose.pose.position.z
113
173
  ),
114
- "AttachedFileMetadata.FunctionalLocation": inspection.metadata.tag_id # noqa: E501
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
115
181
  if inspection.metadata.tag_id
116
182
  else "NA",
117
- "AttachedFileMetadata.Timestamp": inspection.metadata.start_time.isoformat(), # noqa: E501
183
+ "Filename": filename,
118
184
  "AttachedFile": (filename, inspection.data),
119
185
  }
120
186
  )
isar/storage/uploader.py CHANGED
@@ -4,11 +4,10 @@ from datetime import datetime, timedelta
4
4
  from queue import Empty, Queue
5
5
  from typing import List
6
6
 
7
- from robot_interface.models.inspection.inspection import Inspection
8
-
9
7
  from isar.config.settings import settings
10
8
  from isar.models.mission_metadata.mission_metadata import MissionMetadata
11
9
  from isar.storage.storage_interface import StorageException, StorageInterface
10
+ from robot_interface.models.inspection.inspection import Inspection
12
11
 
13
12
 
14
13
  @dataclass
@@ -19,7 +18,7 @@ class UploaderQueueItem:
19
18
  _retry_count: int
20
19
  _next_retry_time: datetime = datetime.utcnow()
21
20
 
22
- def increment_retry(self, max_wait_time: int):
21
+ def increment_retry(self, max_wait_time: int) -> None:
23
22
  self._retry_count += 1
24
23
  seconds_until_retry: int = min(2**self._retry_count, max_wait_time)
25
24
  self._next_retry_time = datetime.utcnow() + timedelta(
@@ -85,7 +84,7 @@ class Uploader:
85
84
  except Empty:
86
85
  continue
87
86
 
88
- def _upload(self, upload_item: UploaderQueueItem):
87
+ def _upload(self, upload_item: UploaderQueueItem) -> None:
89
88
  try:
90
89
  upload_item.storage_handler.store(
91
90
  inspection=upload_item.inspection, metadata=upload_item.mission_metadata
@@ -112,7 +111,7 @@ class Uploader:
112
111
  f"{str(upload_item.inspection.id)[:8]}. Aborting upload."
113
112
  )
114
113
 
115
- def _process_upload_queue(self):
114
+ def _process_upload_queue(self) -> None:
116
115
  ready_items: List[UploaderQueueItem] = [
117
116
  x for x in self._internal_upload_queue if x.is_ready_for_upload()
118
117
  ]