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
@@ -1,167 +1,172 @@
1
1
  import time
2
2
  from enum import Enum
3
- from typing import Any, Dict, List, Optional, Union
3
+ from typing import List, Optional
4
4
 
5
- from alitra import Position
6
5
  from pydantic import BaseModel, Field
7
6
 
8
7
  from isar.apis.models.models import InputPose, InputPosition
9
8
  from isar.config.settings import settings
10
9
  from isar.mission_planner.mission_planner_interface import MissionPlannerError
11
10
  from robot_interface.models.mission.mission import Mission
12
- from robot_interface.models.mission.step import (
13
- STEPS,
14
- DriveToPose,
11
+ from robot_interface.models.mission.task import (
12
+ TASKS,
15
13
  RecordAudio,
14
+ ReturnToHome,
15
+ TakeCO2Measurement,
16
16
  TakeImage,
17
17
  TakeThermalImage,
18
18
  TakeThermalVideo,
19
19
  TakeVideo,
20
+ ZoomDescription,
20
21
  )
21
- from robot_interface.models.mission.task import Task
22
+ from robot_interface.utilities.uuid_string_factory import uuid4_string
22
23
 
23
24
 
24
25
  class InspectionTypes(str, Enum):
25
- image: str = "Image"
26
- thermal_image: str = "ThermalImage"
27
- video: str = "Video"
28
- thermal_video: str = "ThermalVideo"
29
- audio: str = "Audio"
26
+ image = "Image"
27
+ thermal_image = "ThermalImage"
28
+ video = "Video"
29
+ thermal_video = "ThermalVideo"
30
+ audio = "Audio"
31
+ co2_measurement = "CO2Measurement"
32
+
33
+
34
+ class TaskType(str, Enum):
35
+ Inspection = "inspection"
36
+ ReturnToHome = "return_to_home"
30
37
 
31
38
 
32
39
  class StartMissionInspectionDefinition(BaseModel):
33
40
  type: InspectionTypes = Field(default=InspectionTypes.image)
34
41
  inspection_target: InputPosition
35
- analysis_types: Optional[str]
36
- duration: Optional[float]
37
- metadata: Optional[dict]
38
- id: Optional[str]
42
+ inspection_description: Optional[str] = None
43
+ duration: Optional[float] = None
39
44
 
40
45
 
41
46
  class StartMissionTaskDefinition(BaseModel):
47
+ id: Optional[str] = None
48
+ type: TaskType = Field(default=TaskType.Inspection)
42
49
  pose: InputPose
43
- inspections: List[StartMissionInspectionDefinition]
44
- tag: Optional[str]
45
- id: Optional[str]
50
+ inspection: Optional[StartMissionInspectionDefinition] = None
51
+ tag: Optional[str] = None
52
+ zoom: Optional[ZoomDescription] = None
46
53
 
47
54
 
48
55
  class StartMissionDefinition(BaseModel):
56
+ id: Optional[str] = None
49
57
  tasks: List[StartMissionTaskDefinition]
50
- id: Optional[str]
51
- name: Optional[str]
52
-
53
-
54
- def to_isar_mission(mission_definition: StartMissionDefinition) -> Mission:
55
- isar_tasks: List[Task] = []
56
- all_inspection_steps: List[STEPS] = []
57
- duplicate_ids: List[str] = []
58
-
59
- for task in mission_definition.tasks:
60
- try:
61
- tag_id: Optional[str] = task.tag
62
- drive_step: DriveToPose = DriveToPose(pose=task.pose.to_alitra_pose())
63
- inspection_steps: List[STEPS] = [
64
- create_inspection_step(
65
- inspection_type=inspection.type,
66
- duration=inspection.duration,
67
- target=inspection.inspection_target.to_alitra_position(),
68
- tag_id=tag_id,
69
- analysis=inspection.analysis_types,
70
- metadata=inspection.metadata,
71
- id=inspection.id,
72
- )
73
- for inspection in task.inspections
74
- ]
75
- except ValueError as e:
76
- raise MissionPlannerError(f"Failed to create task: {str(e)}")
77
-
78
- duplicate_ids = get_duplicate_ids(items=inspection_steps)
79
- if len(duplicate_ids) > 0:
80
- raise MissionPlannerError(
81
- f"Failed to create task: Duplicate step IDs are not allowed ({duplicate_ids})"
82
- )
83
- all_inspection_steps.extend(inspection_steps)
84
-
85
- isar_task: Task = Task(steps=[drive_step, *inspection_steps], tag_id=tag_id)
86
- if task.id:
87
- isar_task.id = task.id
88
- isar_tasks.append(isar_task)
58
+ name: Optional[str] = None
59
+ start_pose: Optional[InputPose] = None
60
+
61
+
62
+ class StopMissionDefinition(BaseModel):
63
+ mission_id: Optional[str] = None
64
+
65
+
66
+ def to_isar_mission(
67
+ start_mission_definition: StartMissionDefinition,
68
+ ) -> Mission:
69
+ isar_tasks: List[TASKS] = []
70
+
71
+ for task_definition in start_mission_definition.tasks:
72
+ task: TASKS = to_isar_task(task_definition)
73
+ isar_tasks.append(task)
89
74
 
90
75
  if not isar_tasks:
91
76
  raise MissionPlannerError("Mission does not contain any valid tasks")
92
77
 
93
- duplicate_ids = get_duplicate_ids(items=isar_tasks)
94
- if len(duplicate_ids) > 0:
95
- raise MissionPlannerError(
96
- f"Failed to create mission: Duplicate task IDs are not allowed ({duplicate_ids})"
97
- )
78
+ isar_mission_name: str = (
79
+ start_mission_definition.name
80
+ if start_mission_definition.name
81
+ else _build_mission_name()
82
+ )
83
+
84
+ start_pose = None
85
+ if start_mission_definition.start_pose:
86
+ start_pose = start_mission_definition.start_pose.to_alitra_pose()
87
+
88
+ id = start_mission_definition.id if start_mission_definition.id else uuid4_string()
89
+
90
+ return Mission(
91
+ id=id,
92
+ tasks=isar_tasks,
93
+ name=isar_mission_name,
94
+ start_pose=start_pose,
95
+ )
98
96
 
99
- duplicate_ids = get_duplicate_ids(items=all_inspection_steps)
100
- if len(duplicate_ids) > 0:
97
+
98
+ def to_isar_task(task_definition: StartMissionTaskDefinition) -> TASKS:
99
+ if task_definition.type == TaskType.Inspection:
100
+ return to_inspection_task(task_definition)
101
+ elif task_definition.type == TaskType.ReturnToHome:
102
+ return ReturnToHome()
103
+ else:
101
104
  raise MissionPlannerError(
102
- f"Failed to create task: Duplicate step IDs are not allowed ({duplicate_ids})"
105
+ f"Failed to create task: '{task_definition.type}' is not a valid"
103
106
  )
104
107
 
105
- isar_mission: Mission = Mission(tasks=isar_tasks)
106
108
 
107
- if mission_definition.name:
108
- isar_mission.name = mission_definition.name
109
- else:
110
- isar_mission.name = _build_mission_name()
111
-
112
- if mission_definition.id:
113
- isar_mission.id = mission_definition.id
114
-
115
- return isar_mission
116
-
117
-
118
- def create_inspection_step(
119
- inspection_type: InspectionTypes,
120
- duration: float,
121
- target: Position,
122
- analysis: Optional[str],
123
- tag_id: Optional[str],
124
- metadata: Optional[dict],
125
- id: Optional[str],
126
- ) -> STEPS:
127
- inspection_step_dict: Dict[str, Any] = {
128
- InspectionTypes.image.value: TakeImage(target=target),
129
- InspectionTypes.video.value: TakeVideo(target=target, duration=duration),
130
- InspectionTypes.thermal_image.value: TakeThermalImage(target=target),
131
- InspectionTypes.thermal_video.value: TakeThermalVideo(
132
- target=target, duration=duration
133
- ),
134
- InspectionTypes.audio.value: RecordAudio(target=target, duration=duration),
135
- }
136
-
137
- if inspection_type not in inspection_step_dict:
138
- raise ValueError(f"Inspection type '{inspection_type}' not supported")
109
+ def to_inspection_task(task_definition: StartMissionTaskDefinition) -> TASKS:
110
+ inspection_definition = task_definition.inspection
111
+
112
+ if inspection_definition.type == InspectionTypes.image:
113
+ return TakeImage(
114
+ id=task_definition.id if task_definition.id else uuid4_string(),
115
+ robot_pose=task_definition.pose.to_alitra_pose(),
116
+ tag_id=task_definition.tag,
117
+ inspection_description=task_definition.inspection.inspection_description,
118
+ target=task_definition.inspection.inspection_target.to_alitra_position(),
119
+ zoom=task_definition.zoom,
120
+ )
121
+ elif inspection_definition.type == InspectionTypes.video:
122
+ return TakeVideo(
123
+ id=task_definition.id if task_definition.id else uuid4_string(),
124
+ robot_pose=task_definition.pose.to_alitra_pose(),
125
+ tag_id=task_definition.tag,
126
+ inspection_description=task_definition.inspection.inspection_description,
127
+ target=task_definition.inspection.inspection_target.to_alitra_position(),
128
+ duration=inspection_definition.duration,
129
+ zoom=task_definition.zoom,
130
+ )
131
+ elif inspection_definition.type == InspectionTypes.thermal_image:
132
+ return TakeThermalImage(
133
+ id=task_definition.id if task_definition.id else uuid4_string(),
134
+ robot_pose=task_definition.pose.to_alitra_pose(),
135
+ tag_id=task_definition.tag,
136
+ inspection_description=task_definition.inspection.inspection_description,
137
+ target=task_definition.inspection.inspection_target.to_alitra_position(),
138
+ zoom=task_definition.zoom,
139
+ )
140
+ elif inspection_definition.type == InspectionTypes.thermal_video:
141
+ return TakeThermalVideo(
142
+ id=task_definition.id if task_definition.id else uuid4_string(),
143
+ robot_pose=task_definition.pose.to_alitra_pose(),
144
+ tag_id=task_definition.tag,
145
+ inspection_description=task_definition.inspection.inspection_description,
146
+ target=task_definition.inspection.inspection_target.to_alitra_position(),
147
+ duration=inspection_definition.duration,
148
+ zoom=task_definition.zoom,
149
+ )
150
+ elif inspection_definition.type == InspectionTypes.audio:
151
+ return RecordAudio(
152
+ id=task_definition.id if task_definition.id else uuid4_string(),
153
+ robot_pose=task_definition.pose.to_alitra_pose(),
154
+ tag_id=task_definition.tag,
155
+ inspection_description=task_definition.inspection.inspection_description,
156
+ target=task_definition.inspection.inspection_target.to_alitra_position(),
157
+ duration=inspection_definition.duration,
158
+ )
159
+ elif inspection_definition.type == InspectionTypes.co2_measurement:
160
+ return TakeCO2Measurement(
161
+ id=task_definition.id if task_definition.id else uuid4_string(),
162
+ robot_pose=task_definition.pose.to_alitra_pose(),
163
+ tag_id=task_definition.tag,
164
+ inspection_description=task_definition.inspection.inspection_description,
165
+ )
139
166
  else:
140
- inspection_step = inspection_step_dict[inspection_type]
141
-
142
- if tag_id:
143
- inspection_step.tag_id = tag_id
144
- if analysis:
145
- inspection_step.analysis = analysis
146
- if metadata:
147
- inspection_step.metadata = metadata
148
- if id:
149
- inspection_step.id = id
150
-
151
- return inspection_step
152
-
153
-
154
- def get_duplicate_ids(items: Union[List[Task], List[STEPS]]) -> List[str]:
155
- unique_ids: List[str] = []
156
- duplicate_ids: List[str] = []
157
- for item in items:
158
- id: str = item.id
159
- if id not in unique_ids:
160
- unique_ids.append(id)
161
- else:
162
- duplicate_ids.append(id)
163
-
164
- return duplicate_ids
167
+ raise ValueError(
168
+ f"Inspection type '{inspection_definition.type}' not supported"
169
+ )
165
170
 
166
171
 
167
172
  def _build_mission_name() -> str:
@@ -0,0 +1,41 @@
1
+ import logging
2
+
3
+ from fastapi import HTTPException
4
+ from opentelemetry import trace
5
+
6
+ from isar.apis.models.models import RobotInfoResponse
7
+ from isar.config.settings import robot_settings, settings
8
+ from isar.services.utilities.robot_utilities import RobotUtilities
9
+ from robot_interface.models.robots.media import MediaConfig
10
+
11
+ tracer = trace.get_tracer(__name__)
12
+
13
+
14
+ class RobotController:
15
+ def __init__(
16
+ self,
17
+ robot_utilities: RobotUtilities,
18
+ ):
19
+ self.robot_utilities: RobotUtilities = robot_utilities
20
+ self.logger = logging.getLogger("api")
21
+
22
+ @tracer.start_as_current_span("generate_media_config")
23
+ def generate_media_config(self) -> MediaConfig:
24
+ media_config: MediaConfig = self.robot_utilities.generate_media_config()
25
+ if media_config is None:
26
+ raise HTTPException(
27
+ status_code=204,
28
+ detail="Robot has no media config",
29
+ )
30
+ return media_config
31
+
32
+ @tracer.start_as_current_span("get_info")
33
+ def get_info(self) -> RobotInfoResponse:
34
+ return RobotInfoResponse(
35
+ robot_package=settings.ROBOT_PACKAGE,
36
+ isar_id=settings.ISAR_ID,
37
+ robot_name=settings.ROBOT_NAME,
38
+ robot_capabilities=robot_settings.CAPABILITIES,
39
+ robot_map_name=settings.DEFAULT_MAP,
40
+ plant_short_name=settings.PLANT_SHORT_NAME,
41
+ )