isar 1.31.0__py3-none-any.whl → 1.31.1__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 (46) hide show
  1. isar/config/open_telemetry.py +2 -8
  2. isar/eventhandlers/eventhandler.py +93 -0
  3. isar/models/events.py +118 -0
  4. isar/modules.py +1 -1
  5. isar/robot/robot.py +16 -18
  6. isar/robot/robot_start_mission.py +8 -14
  7. isar/robot/robot_status.py +2 -3
  8. isar/robot/robot_stop_mission.py +3 -9
  9. isar/robot/robot_task_status.py +3 -7
  10. isar/script.py +2 -1
  11. isar/services/utilities/scheduling_utilities.py +26 -24
  12. isar/state_machine/state_machine.py +79 -9
  13. isar/state_machine/states/await_next_mission.py +46 -11
  14. isar/state_machine/states/blocked_protective_stop.py +24 -15
  15. isar/state_machine/states/home.py +40 -9
  16. isar/state_machine/states/monitor.py +83 -12
  17. isar/state_machine/states/offline.py +25 -13
  18. isar/state_machine/states/paused.py +24 -38
  19. isar/state_machine/states/returning_home.py +75 -14
  20. isar/state_machine/states/robot_standing_still.py +41 -11
  21. isar/state_machine/states/stopping.py +52 -67
  22. isar/state_machine/states/unknown_status.py +37 -64
  23. isar/state_machine/transitions/functions/robot_status.py +4 -5
  24. isar/state_machine/transitions/functions/stop.py +3 -12
  25. isar/state_machine/utils/common_event_handlers.py +166 -0
  26. isar/storage/uploader.py +1 -1
  27. {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/METADATA +1 -1
  28. {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/RECORD +32 -43
  29. isar/models/communication/__init__.py +0 -0
  30. isar/models/communication/message.py +0 -8
  31. isar/models/communication/queues/__init__.py +0 -0
  32. isar/models/communication/queues/events.py +0 -58
  33. isar/models/communication/queues/queue_io.py +0 -12
  34. isar/models/communication/queues/queue_timeout_error.py +0 -2
  35. isar/models/communication/queues/queue_utils.py +0 -38
  36. isar/models/communication/queues/status_queue.py +0 -22
  37. isar/models/mission_metadata/__init__.py +0 -0
  38. isar/services/service_connections/stid/__init__.py +0 -0
  39. isar/services/utilities/queue_utilities.py +0 -39
  40. isar/state_machine/generic_states/idle.py +0 -133
  41. isar/state_machine/generic_states/ongoing_mission.py +0 -309
  42. isar/state_machine/generic_states/robot_unavailable.py +0 -61
  43. {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/WHEEL +0 -0
  44. {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/entry_points.txt +0 -0
  45. {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/licenses/LICENSE +0 -0
  46. {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/top_level.txt +0 -0
@@ -1,309 +0,0 @@
1
- import logging
2
- import time
3
- from copy import deepcopy
4
- from enum import Enum
5
- from queue import Queue
6
- from threading import Event
7
- from typing import TYPE_CHECKING, Optional, Tuple
8
-
9
- from isar.apis.models.models import ControlMissionResponse
10
- from isar.config.settings import settings
11
- from isar.models.communication.message import StartMissionMessage
12
- from isar.models.communication.queues.queue_utils import (
13
- check_for_event,
14
- check_for_event_without_consumption,
15
- trigger_event,
16
- )
17
- from isar.services.utilities.threaded_request import ThreadedRequest
18
- from robot_interface.models.exceptions.robot_exceptions import (
19
- ErrorMessage,
20
- ErrorReason,
21
- RobotException,
22
- RobotRetrieveInspectionException,
23
- )
24
- from robot_interface.models.inspection.inspection import Inspection
25
- from robot_interface.models.mission.mission import Mission
26
- from robot_interface.models.mission.status import TaskStatus
27
- from robot_interface.models.mission.task import InspectionTask, Task
28
-
29
-
30
- class OngoingMissionStates(str, Enum):
31
- Monitor = "monitor"
32
- ReturningHome = "returningHome"
33
-
34
-
35
- if TYPE_CHECKING:
36
- from isar.state_machine.state_machine import StateMachine
37
-
38
-
39
- class OngoingMission:
40
- def __init__(
41
- self,
42
- state_machine: "StateMachine",
43
- state: OngoingMissionStates,
44
- ) -> None:
45
- self.state_machine: "StateMachine" = state_machine
46
- self.logger = logging.getLogger("state_machine")
47
- self.events = state_machine.events
48
- self.awaiting_task_status: bool = False
49
- self.signal_state_machine_to_stop: Event = (
50
- state_machine.signal_state_machine_to_stop
51
- )
52
- self.state: OngoingMissionStates = state
53
-
54
- def start(self) -> None:
55
- self.state_machine.update_state()
56
- self._run()
57
-
58
- def stop(self) -> None:
59
- return
60
-
61
- def _check_and_handle_stop_mission_event(self, event: Queue) -> bool:
62
- mission_id: str = check_for_event(event)
63
- if mission_id is not None:
64
- if self.state_machine.current_mission.id == mission_id or mission_id == "":
65
- self.state_machine.stop() # type: ignore
66
- return True
67
- else:
68
- self.events.api_requests.stop_mission.output.put(
69
- ControlMissionResponse(
70
- mission_id=mission_id,
71
- mission_status=self.state_machine.current_mission.status,
72
- mission_not_found=True,
73
- task_id=self.state_machine.current_task.id,
74
- task_status=self.state_machine.current_task.status,
75
- )
76
- )
77
- return False
78
-
79
- def _check_and_handle_pause_mission_event(self, event: Queue) -> bool:
80
- if check_for_event(event):
81
- self.state_machine.pause() # type: ignore
82
- return True
83
- return False
84
-
85
- def _check_and_handle_mission_started_event(self, event: Queue) -> bool:
86
- if check_for_event(event):
87
- self.state_machine.mission_ongoing = True
88
- return True
89
- return False
90
-
91
- def _check_and_handle_mission_failed_event(self, event: Queue) -> bool:
92
- mission_failed: Optional[ErrorMessage] = check_for_event(event)
93
- if mission_failed is not None:
94
- self.state_machine.logger.warning(
95
- f"Failed to initiate mission "
96
- f"{str(self.state_machine.current_mission.id)[:8]} because: "
97
- f"{mission_failed.error_description}"
98
- )
99
- self.state_machine.current_mission.error_message = ErrorMessage(
100
- error_reason=mission_failed.error_reason,
101
- error_description=mission_failed.error_description,
102
- )
103
- self.state_machine.mission_failed_to_start() # type: ignore
104
- return True
105
- return False
106
-
107
- def _check_and_handle_task_status_failed_event(self, event: Queue) -> bool:
108
- if not self.state_machine.mission_ongoing:
109
- return False
110
-
111
- task_failure: Optional[ErrorMessage] = check_for_event(event)
112
- if task_failure is not None:
113
- self.awaiting_task_status = False
114
- self.state_machine.current_task.error_message = task_failure
115
- self.logger.error(
116
- f"Monitoring task {self.state_machine.current_task.id[:8]} failed "
117
- f"because: {task_failure.error_description}"
118
- )
119
- return self._handle_new_task_status(TaskStatus.Failed)
120
- elif not self.awaiting_task_status:
121
- trigger_event(
122
- self.events.state_machine_events.task_status_request,
123
- self.state_machine.current_task.id,
124
- )
125
- self.awaiting_task_status = True
126
- return False
127
-
128
- def _check_and_handle_task_status_event(self, event: Queue) -> bool:
129
- if not self.state_machine.mission_ongoing:
130
- return False
131
-
132
- status: Optional[TaskStatus] = check_for_event(event)
133
- if status is not None:
134
- self.awaiting_task_status = False
135
- return self._handle_new_task_status(status)
136
- elif not self.awaiting_task_status:
137
- trigger_event(
138
- self.events.state_machine_events.task_status_request,
139
- self.state_machine.current_task.id,
140
- )
141
- self.awaiting_task_status = True
142
- return False
143
-
144
- def _handle_new_task_status(self, status: TaskStatus) -> bool:
145
- if self.state_machine.current_task is None:
146
- self.state_machine.iterate_current_task()
147
-
148
- self.state_machine.current_task.status = status
149
-
150
- if self.state_machine.current_task.is_finished():
151
- self._report_task_status(self.state_machine.current_task)
152
- self.state_machine.publish_task_status(task=self.state_machine.current_task)
153
-
154
- if self.state == OngoingMissionStates.ReturningHome:
155
- if status != TaskStatus.Successful:
156
- self.state_machine.current_mission.error_message = ErrorMessage(
157
- error_reason=ErrorReason.RobotActionException,
158
- error_description="Return home failed.",
159
- )
160
- self.state_machine.return_home_failed() # type: ignore
161
- return True
162
- self.state_machine.returned_home() # type: ignore
163
- return True
164
-
165
- if self._should_upload_inspections():
166
- get_inspection_thread = ThreadedRequest(
167
- self._queue_inspections_for_upload
168
- )
169
- get_inspection_thread.start_thread(
170
- deepcopy(self.state_machine.current_mission),
171
- deepcopy(self.state_machine.current_task),
172
- name="State Machine Get Inspections",
173
- )
174
-
175
- self.state_machine.iterate_current_task()
176
- if self.state_machine.current_task is None:
177
- self.state_machine.mission_finished() # type: ignore
178
- return True
179
-
180
- return False
181
-
182
- def _check_and_handle_start_mission_event(
183
- self, event: Queue[StartMissionMessage]
184
- ) -> bool:
185
- if check_for_event_without_consumption(event):
186
- self.state_machine.stop() # type: ignore
187
- return True
188
-
189
- return False
190
-
191
- def _run(self) -> None:
192
- self.awaiting_task_status = False
193
- while True:
194
- if self.signal_state_machine_to_stop.is_set():
195
- self.logger.info(
196
- "Stopping state machine from %s state", self.state.name
197
- )
198
- break
199
-
200
- if self._check_and_handle_stop_mission_event(
201
- self.events.api_requests.stop_mission.input
202
- ):
203
- break
204
-
205
- if (
206
- self.state == OngoingMissionStates.Monitor
207
- and self._check_and_handle_pause_mission_event(
208
- self.events.api_requests.pause_mission.input
209
- )
210
- ):
211
- break
212
-
213
- self._check_and_handle_mission_started_event(
214
- self.events.robot_service_events.mission_started
215
- )
216
-
217
- if self._check_and_handle_mission_failed_event(
218
- self.events.robot_service_events.mission_failed
219
- ):
220
- break
221
-
222
- if (
223
- self.state == OngoingMissionStates.ReturningHome
224
- and self._check_and_handle_start_mission_event(
225
- self.events.api_requests.start_mission.input
226
- )
227
- ):
228
- break
229
-
230
- if self._check_and_handle_task_status_failed_event(
231
- self.events.robot_service_events.task_status_failed
232
- ):
233
- break
234
-
235
- if self._check_and_handle_task_status_event(
236
- self.events.robot_service_events.task_status_updated
237
- ):
238
- break
239
-
240
- time.sleep(settings.FSM_SLEEP_TIME)
241
-
242
- def _queue_inspections_for_upload(
243
- self, mission: Mission, current_task: InspectionTask
244
- ) -> None:
245
- try:
246
- inspection: Inspection = self.state_machine.robot.get_inspection(
247
- task=current_task
248
- )
249
- if current_task.inspection_id != inspection.id:
250
- self.logger.warning(
251
- f"The inspection_id of task ({current_task.inspection_id}) "
252
- f"and result ({inspection.id}) is not matching. "
253
- f"This may lead to confusions when accessing the inspection later"
254
- )
255
-
256
- except (RobotRetrieveInspectionException, RobotException) as e:
257
- self._set_error_message(e)
258
- self.logger.error(
259
- f"Failed to retrieve inspections because: {e.error_description}"
260
- )
261
- return
262
-
263
- except Exception as e:
264
- self.logger.error(
265
- f"Failed to retrieve inspections because of unexpected error: {e}"
266
- )
267
- return
268
-
269
- if not inspection:
270
- self.logger.warning(
271
- f"No inspection result data retrieved for task {str(current_task.id)[:8]}"
272
- )
273
-
274
- inspection.metadata.tag_id = current_task.tag_id
275
-
276
- message: Tuple[Inspection, Mission] = (
277
- inspection,
278
- mission,
279
- )
280
- self.state_machine.events.upload_queue.put(message)
281
- self.logger.info(
282
- f"Inspection result: {str(inspection.id)[:8]} queued for upload"
283
- )
284
-
285
- def _report_task_status(self, task: Task) -> None:
286
- if task.status == TaskStatus.Failed:
287
- self.logger.warning(
288
- f"Task: {str(task.id)[:8]} was reported as failed by the robot"
289
- )
290
- elif task.status == TaskStatus.Successful:
291
- self.logger.info(
292
- f"{type(task).__name__} task: {str(task.id)[:8]} completed"
293
- )
294
-
295
- def _should_upload_inspections(self) -> bool:
296
- if settings.UPLOAD_INSPECTIONS_ASYNC:
297
- return False
298
-
299
- return (
300
- self.state_machine.current_task.is_finished()
301
- and self.state_machine.current_task.status == TaskStatus.Successful
302
- and isinstance(self.state_machine.current_task, InspectionTask)
303
- )
304
-
305
- def _set_error_message(self, e: RobotException) -> None:
306
- error_message: ErrorMessage = ErrorMessage(
307
- error_reason=e.error_reason, error_description=e.error_description
308
- )
309
- self.state_machine.current_task.error_message = error_message
@@ -1,61 +0,0 @@
1
- import logging
2
- import time
3
- from enum import Enum
4
- from typing import TYPE_CHECKING
5
-
6
- from isar.models.communication.queues.queue_utils import check_shared_state
7
- from robot_interface.models.mission.status import RobotStatus
8
-
9
-
10
- class RobotUnavailableStates(str, Enum):
11
- BlockedProtectiveStop = "blockedProtectiveStop"
12
- Offline = "offline"
13
-
14
-
15
- if TYPE_CHECKING:
16
- from isar.state_machine.state_machine import StateMachine
17
-
18
-
19
- class RobotUnavailable:
20
- def __init__(
21
- self,
22
- state_machine: "StateMachine",
23
- state: RobotUnavailableStates,
24
- ) -> None:
25
- self.state_machine: "StateMachine" = state_machine
26
- self.logger = logging.getLogger("state_machine")
27
- self.shared_state = self.state_machine.shared_state
28
- self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
29
- self.state: RobotUnavailableStates = state
30
-
31
- def start(self) -> None:
32
- self.state_machine.update_state()
33
- self._run()
34
-
35
- def stop(self) -> None:
36
- return
37
-
38
- def _run(self) -> None:
39
- while True:
40
- if self.signal_state_machine_to_stop.is_set():
41
- self.logger.info(
42
- "Stopping state machine from %s state", self.state.name
43
- )
44
- break
45
-
46
- robot_status: RobotStatus = check_shared_state(
47
- self.shared_state.robot_status
48
- )
49
-
50
- expected_status = (
51
- RobotStatus.BlockedProtectiveStop
52
- if self.state == RobotUnavailableStates.BlockedProtectiveStop
53
- else RobotStatus.Offline
54
- )
55
- if robot_status != expected_status:
56
- transition = self.state_machine.robot_status_changed # type: ignore
57
- break
58
-
59
- time.sleep(self.state_machine.sleep_time)
60
-
61
- transition()
File without changes