isar 1.26.0__py3-none-any.whl → 1.26.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.
- isar/apis/models/start_mission_definition.py +0 -4
- isar/apis/schedule/scheduling_controller.py +2 -25
- isar/config/settings.py +1 -1
- isar/models/communication/queues/events.py +57 -0
- isar/models/communication/queues/queue_utils.py +18 -13
- isar/models/communication/queues/status_queue.py +7 -5
- isar/modules.py +26 -13
- isar/robot/robot.py +46 -13
- isar/robot/robot_start_mission.py +10 -10
- isar/robot/robot_status.py +7 -4
- isar/robot/robot_stop_mission.py +72 -0
- isar/robot/robot_task_status.py +6 -6
- isar/script.py +7 -7
- isar/services/utilities/scheduling_utilities.py +10 -8
- isar/state_machine/state_machine.py +19 -72
- isar/state_machine/states/blocked_protective_stop.py +4 -12
- isar/state_machine/states/idle.py +45 -25
- isar/state_machine/states/monitor.py +127 -90
- isar/state_machine/states/offline.py +5 -1
- isar/state_machine/states/paused.py +5 -2
- isar/state_machine/states/stop.py +29 -58
- isar/state_machine/transitions/pause.py +1 -1
- isar/state_machine/transitions/resume.py +1 -1
- isar/state_machine/transitions/start_mission.py +3 -3
- isar/state_machine/transitions/stop.py +9 -2
- isar/storage/uploader.py +5 -5
- {isar-1.26.0.dist-info → isar-1.26.1.dist-info}/METADATA +4 -17
- {isar-1.26.0.dist-info → isar-1.26.1.dist-info}/RECORD +32 -31
- {isar-1.26.0.dist-info → isar-1.26.1.dist-info}/WHEEL +1 -1
- isar/models/communication/queues/queues.py +0 -37
- {isar-1.26.0.dist-info → isar-1.26.1.dist-info}/entry_points.txt +0 -0
- {isar-1.26.0.dist-info → isar-1.26.1.dist-info/licenses}/LICENSE +0 -0
- {isar-1.26.0.dist-info → isar-1.26.1.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import time
|
|
3
3
|
from copy import deepcopy
|
|
4
|
-
from
|
|
4
|
+
from queue import Queue
|
|
5
|
+
from typing import TYPE_CHECKING, Optional, Tuple
|
|
5
6
|
|
|
6
7
|
from injector import inject
|
|
7
8
|
from transitions import State
|
|
8
9
|
|
|
9
10
|
from isar.config.settings import settings
|
|
11
|
+
from isar.models.communication.queues.queue_utils import check_for_event, trigger_event
|
|
10
12
|
from isar.services.utilities.threaded_request import ThreadedRequest
|
|
11
13
|
from robot_interface.models.exceptions.robot_exceptions import (
|
|
12
14
|
ErrorMessage,
|
|
@@ -29,6 +31,9 @@ class Monitor(State):
|
|
|
29
31
|
self.state_machine: "StateMachine" = state_machine
|
|
30
32
|
|
|
31
33
|
self.logger = logging.getLogger("state_machine")
|
|
34
|
+
self.events = self.state_machine.events
|
|
35
|
+
|
|
36
|
+
self.awaiting_task_status: bool = False
|
|
32
37
|
|
|
33
38
|
def start(self) -> None:
|
|
34
39
|
self.state_machine.update_state()
|
|
@@ -38,109 +43,132 @@ class Monitor(State):
|
|
|
38
43
|
self.state_machine.mission_ongoing = False
|
|
39
44
|
return
|
|
40
45
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
def _check_and_handle_stop_mission_event(self, event: Queue) -> bool:
|
|
47
|
+
if check_for_event(event):
|
|
48
|
+
self.state_machine.stop() # type: ignore
|
|
49
|
+
return True
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
def _check_and_handle_pause_mission_event(self, event: Queue) -> bool:
|
|
53
|
+
if check_for_event(event):
|
|
54
|
+
self.state_machine.pause() # type: ignore
|
|
55
|
+
return True
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
def _check_and_handle_mission_started_event(self, event: Queue) -> None:
|
|
59
|
+
if not self.state_machine.mission_ongoing:
|
|
60
|
+
if check_for_event(event):
|
|
61
|
+
self.state_machine.mission_ongoing = True
|
|
62
|
+
|
|
63
|
+
def _check_and_handle_mission_failed_event(self, event: Queue) -> bool:
|
|
64
|
+
mission_failed: Optional[ErrorMessage] = check_for_event(event)
|
|
65
|
+
if mission_failed is not None:
|
|
66
|
+
self.state_machine.logger.warning(
|
|
67
|
+
f"Failed to initiate mission "
|
|
68
|
+
f"{str(self.state_machine.current_mission.id)[:8]} because: "
|
|
69
|
+
f"{mission_failed.error_description}"
|
|
70
|
+
)
|
|
71
|
+
self.state_machine.current_mission.error_message = ErrorMessage(
|
|
72
|
+
error_reason=mission_failed.error_reason,
|
|
73
|
+
error_description=mission_failed.error_description,
|
|
74
|
+
)
|
|
48
75
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
76
|
+
self.state_machine.mission_failed_to_start() # type: ignore
|
|
77
|
+
return True
|
|
78
|
+
return False
|
|
52
79
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
80
|
+
def _check_and_handle_task_status_failed_event(self, event: Queue) -> bool:
|
|
81
|
+
task_failure: Optional[ErrorMessage] = check_for_event(event)
|
|
82
|
+
if task_failure is not None:
|
|
83
|
+
self.awaiting_task_status = False
|
|
84
|
+
self.state_machine.current_task.error_message = task_failure
|
|
85
|
+
self.logger.error(
|
|
86
|
+
f"Monitoring task {self.state_machine.current_task.id[:8]} failed "
|
|
87
|
+
f"because: {task_failure.error_description}"
|
|
88
|
+
)
|
|
89
|
+
return self._handle_new_task_status(TaskStatus.Failed)
|
|
90
|
+
elif not self.awaiting_task_status:
|
|
91
|
+
trigger_event(
|
|
92
|
+
self.events.state_machine_events.task_status_request,
|
|
93
|
+
self.state_machine.current_task.id,
|
|
94
|
+
)
|
|
95
|
+
self.awaiting_task_status = True
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
def _check_and_handle_task_status_event(self, event: Queue) -> bool:
|
|
99
|
+
status: Optional[TaskStatus] = check_for_event(event)
|
|
100
|
+
if status is not None:
|
|
101
|
+
self.awaiting_task_status = False
|
|
102
|
+
return self._handle_new_task_status(status)
|
|
103
|
+
elif not self.awaiting_task_status:
|
|
104
|
+
trigger_event(
|
|
105
|
+
self.events.state_machine_events.task_status_request,
|
|
106
|
+
self.state_machine.current_task.id,
|
|
107
|
+
)
|
|
108
|
+
self.awaiting_task_status = True
|
|
109
|
+
return False
|
|
71
110
|
|
|
72
|
-
|
|
73
|
-
|
|
111
|
+
def _handle_new_task_status(self, status: TaskStatus) -> bool:
|
|
112
|
+
if self.state_machine.current_task is None:
|
|
113
|
+
self.state_machine.iterate_current_task()
|
|
74
114
|
|
|
75
|
-
|
|
115
|
+
self.state_machine.current_task.status = status
|
|
76
116
|
|
|
77
|
-
|
|
78
|
-
|
|
117
|
+
if self._should_upload_inspections():
|
|
118
|
+
get_inspection_thread = ThreadedRequest(self._queue_inspections_for_upload)
|
|
119
|
+
get_inspection_thread.start_thread(
|
|
120
|
+
deepcopy(self.state_machine.current_mission),
|
|
121
|
+
deepcopy(self.state_machine.current_task),
|
|
122
|
+
name="State Machine Get Inspections",
|
|
79
123
|
)
|
|
80
|
-
if task_failure is not None:
|
|
81
|
-
self.state_machine.current_task.error_message = task_failure
|
|
82
|
-
self.logger.error(
|
|
83
|
-
f"Monitoring task {self.state_machine.current_task.id[:8]} failed "
|
|
84
|
-
f"because: {task_failure.error_description}"
|
|
85
|
-
)
|
|
86
|
-
status = TaskStatus.Failed
|
|
87
|
-
else:
|
|
88
|
-
status = self.state_machine.get_task_status_event()
|
|
89
|
-
|
|
90
|
-
if status is None:
|
|
91
|
-
if not awaiting_task_status:
|
|
92
|
-
self.state_machine.request_task_status(
|
|
93
|
-
self.state_machine.current_task
|
|
94
|
-
)
|
|
95
|
-
awaiting_task_status = True
|
|
96
|
-
time.sleep(settings.FSM_SLEEP_TIME)
|
|
97
|
-
continue
|
|
98
|
-
else:
|
|
99
|
-
awaiting_task_status = False
|
|
100
|
-
|
|
101
|
-
if not isinstance(status, TaskStatus):
|
|
102
|
-
self.logger.error(
|
|
103
|
-
f"Received an invalid status update {status} when monitoring mission. "
|
|
104
|
-
"Only TaskStatus is expected."
|
|
105
|
-
)
|
|
106
|
-
break
|
|
107
124
|
|
|
125
|
+
if self.state_machine.current_task.is_finished():
|
|
126
|
+
self._report_task_status(self.state_machine.current_task)
|
|
127
|
+
self.state_machine.publish_task_status(task=self.state_machine.current_task)
|
|
128
|
+
|
|
129
|
+
self.state_machine.iterate_current_task()
|
|
108
130
|
if self.state_machine.current_task is None:
|
|
109
|
-
self.state_machine.
|
|
131
|
+
self.state_machine.mission_finished() # type: ignore
|
|
132
|
+
return True
|
|
110
133
|
|
|
111
|
-
|
|
134
|
+
# Report and update next task
|
|
135
|
+
self.state_machine.current_task.update_task_status()
|
|
136
|
+
self.state_machine.publish_task_status(task=self.state_machine.current_task)
|
|
137
|
+
return False
|
|
112
138
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
139
|
+
def _run(self) -> None:
|
|
140
|
+
self.awaiting_task_status = False
|
|
141
|
+
while True:
|
|
142
|
+
if self._check_and_handle_stop_mission_event(
|
|
143
|
+
self.events.api_requests.stop_mission.input
|
|
116
144
|
):
|
|
117
|
-
|
|
118
|
-
self._queue_inspections_for_upload
|
|
119
|
-
)
|
|
120
|
-
get_inspection_thread.start_thread(
|
|
121
|
-
deepcopy(self.state_machine.current_mission),
|
|
122
|
-
deepcopy(self.state_machine.current_task),
|
|
123
|
-
name="State Machine Get Inspections",
|
|
124
|
-
)
|
|
145
|
+
break
|
|
125
146
|
|
|
126
|
-
if self.
|
|
127
|
-
self.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
)
|
|
147
|
+
if self._check_and_handle_pause_mission_event(
|
|
148
|
+
self.events.api_requests.pause_mission.input
|
|
149
|
+
):
|
|
150
|
+
break
|
|
131
151
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
break
|
|
152
|
+
self._check_and_handle_mission_started_event(
|
|
153
|
+
self.events.robot_service_events.mission_started
|
|
154
|
+
)
|
|
136
155
|
|
|
137
|
-
|
|
138
|
-
self.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
156
|
+
if self._check_and_handle_mission_failed_event(
|
|
157
|
+
self.events.robot_service_events.mission_failed
|
|
158
|
+
):
|
|
159
|
+
break
|
|
160
|
+
|
|
161
|
+
if self._check_and_handle_task_status_failed_event(
|
|
162
|
+
self.events.robot_service_events.task_status_failed
|
|
163
|
+
):
|
|
164
|
+
break
|
|
165
|
+
|
|
166
|
+
if self._check_and_handle_task_status_event(
|
|
167
|
+
self.events.robot_service_events.task_status_updated
|
|
168
|
+
):
|
|
169
|
+
break
|
|
142
170
|
|
|
143
|
-
|
|
171
|
+
time.sleep(settings.FSM_SLEEP_TIME)
|
|
144
172
|
|
|
145
173
|
def _queue_inspections_for_upload(
|
|
146
174
|
self, mission: Mission, current_task: InspectionTask
|
|
@@ -163,6 +191,12 @@ class Monitor(State):
|
|
|
163
191
|
)
|
|
164
192
|
return
|
|
165
193
|
|
|
194
|
+
except Exception as e:
|
|
195
|
+
self.logger.error(
|
|
196
|
+
f"Failed to retrieve inspections because of unexpected error: {e}"
|
|
197
|
+
)
|
|
198
|
+
return
|
|
199
|
+
|
|
166
200
|
if not inspection:
|
|
167
201
|
self.logger.warning(
|
|
168
202
|
f"No inspection result data retrieved for task {str(current_task.id)[:8]}"
|
|
@@ -174,7 +208,7 @@ class Monitor(State):
|
|
|
174
208
|
inspection,
|
|
175
209
|
mission,
|
|
176
210
|
)
|
|
177
|
-
self.state_machine.
|
|
211
|
+
self.state_machine.events.upload_queue.put(message)
|
|
178
212
|
self.logger.info(
|
|
179
213
|
f"Inspection result: {str(inspection.id)[:8]} queued for upload"
|
|
180
214
|
)
|
|
@@ -190,6 +224,9 @@ class Monitor(State):
|
|
|
190
224
|
)
|
|
191
225
|
|
|
192
226
|
def _should_upload_inspections(self) -> bool:
|
|
227
|
+
if settings.UPLOAD_INSPECTIONS_ASYNC:
|
|
228
|
+
return False
|
|
229
|
+
|
|
193
230
|
return (
|
|
194
231
|
self.state_machine.current_task.is_finished()
|
|
195
232
|
and self.state_machine.current_task.status == TaskStatus.Successful
|
|
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING
|
|
|
4
4
|
|
|
5
5
|
from transitions import State
|
|
6
6
|
|
|
7
|
+
from isar.models.communication.queues.queue_utils import check_shared_state
|
|
7
8
|
from robot_interface.models.mission.status import RobotStatus
|
|
8
9
|
|
|
9
10
|
if TYPE_CHECKING:
|
|
@@ -15,6 +16,7 @@ class Offline(State):
|
|
|
15
16
|
super().__init__(name="offline", on_enter=self.start, on_exit=self.stop)
|
|
16
17
|
self.state_machine: "StateMachine" = state_machine
|
|
17
18
|
self.logger = logging.getLogger("state_machine")
|
|
19
|
+
self.shared_state = self.state_machine.shared_state
|
|
18
20
|
|
|
19
21
|
def start(self) -> None:
|
|
20
22
|
self.state_machine.update_state()
|
|
@@ -25,7 +27,9 @@ class Offline(State):
|
|
|
25
27
|
|
|
26
28
|
def _run(self) -> None:
|
|
27
29
|
while True:
|
|
28
|
-
robot_status =
|
|
30
|
+
robot_status: RobotStatus = check_shared_state(
|
|
31
|
+
self.shared_state.robot_status
|
|
32
|
+
)
|
|
29
33
|
if robot_status == RobotStatus.BlockedProtectiveStop:
|
|
30
34
|
transition = self.state_machine.robot_protective_stop_engaged # type: ignore
|
|
31
35
|
break
|
|
@@ -4,6 +4,8 @@ from typing import TYPE_CHECKING, Callable
|
|
|
4
4
|
|
|
5
5
|
from transitions import State
|
|
6
6
|
|
|
7
|
+
from isar.models.communication.queues.queue_utils import check_for_event
|
|
8
|
+
|
|
7
9
|
if TYPE_CHECKING:
|
|
8
10
|
from isar.state_machine.state_machine import StateMachine
|
|
9
11
|
|
|
@@ -13,6 +15,7 @@ class Paused(State):
|
|
|
13
15
|
super().__init__(name="paused", on_enter=self.start)
|
|
14
16
|
self.state_machine: "StateMachine" = state_machine
|
|
15
17
|
self.logger = logging.getLogger("state_machine")
|
|
18
|
+
self.events = self.state_machine.events
|
|
16
19
|
|
|
17
20
|
def start(self) -> None:
|
|
18
21
|
self.state_machine.update_state()
|
|
@@ -21,11 +24,11 @@ class Paused(State):
|
|
|
21
24
|
def _run(self) -> None:
|
|
22
25
|
transition: Callable
|
|
23
26
|
while True:
|
|
24
|
-
if self.
|
|
27
|
+
if check_for_event(self.events.api_requests.pause_mission.input):
|
|
25
28
|
transition = self.state_machine.stop # type: ignore
|
|
26
29
|
break
|
|
27
30
|
|
|
28
|
-
if self.
|
|
31
|
+
if check_for_event(self.events.api_requests.resume_mission.input):
|
|
29
32
|
transition = self.state_machine.resume # type: ignore
|
|
30
33
|
break
|
|
31
34
|
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import time
|
|
3
|
-
from
|
|
3
|
+
from queue import Queue
|
|
4
|
+
from typing import TYPE_CHECKING, Optional
|
|
4
5
|
|
|
5
6
|
from transitions import State
|
|
6
7
|
|
|
7
|
-
from isar.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
)
|
|
11
|
-
from robot_interface.models.exceptions.robot_exceptions import (
|
|
12
|
-
ErrorMessage,
|
|
13
|
-
RobotActionException,
|
|
14
|
-
RobotException,
|
|
15
|
-
)
|
|
8
|
+
from isar.models.communication.queues.queue_utils import check_for_event
|
|
9
|
+
from isar.services.utilities.threaded_request import ThreadedRequest
|
|
10
|
+
from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
|
|
16
11
|
|
|
17
12
|
if TYPE_CHECKING:
|
|
18
13
|
from isar.state_machine.state_machine import StateMachine
|
|
@@ -23,6 +18,7 @@ class Stop(State):
|
|
|
23
18
|
super().__init__(name="stop", on_enter=self.start, on_exit=self.stop)
|
|
24
19
|
self.state_machine: "StateMachine" = state_machine
|
|
25
20
|
self.logger = logging.getLogger("state_machine")
|
|
21
|
+
self.events = self.state_machine.events
|
|
26
22
|
self.stop_thread: Optional[ThreadedRequest] = None
|
|
27
23
|
self._count_number_retries: int = 0
|
|
28
24
|
|
|
@@ -36,56 +32,31 @@ class Stop(State):
|
|
|
36
32
|
self.stop_thread = None
|
|
37
33
|
self._count_number_retries = 0
|
|
38
34
|
|
|
39
|
-
def
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
try:
|
|
47
|
-
self.stop_thread.get_output()
|
|
48
|
-
except ThreadedRequestNotFinishedError:
|
|
49
|
-
time.sleep(self.state_machine.sleep_time)
|
|
50
|
-
continue
|
|
51
|
-
|
|
52
|
-
except (RobotActionException, RobotException) as e:
|
|
53
|
-
if self.handle_stop_fail(
|
|
54
|
-
retry_limit=self.state_machine.stop_robot_attempts_limit,
|
|
55
|
-
error_message=ErrorMessage(
|
|
56
|
-
error_reason=e.error_reason,
|
|
57
|
-
error_description=e.error_description,
|
|
58
|
-
),
|
|
59
|
-
):
|
|
60
|
-
transition = self.state_machine.mission_stopped # type: ignore
|
|
61
|
-
break
|
|
62
|
-
|
|
63
|
-
self.logger.warning(
|
|
64
|
-
f"\nFailed to stop robot because: {e.error_description}"
|
|
65
|
-
f"\nAttempting to stop the robot again"
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
self.stop_thread = None
|
|
69
|
-
continue
|
|
70
|
-
|
|
71
|
-
transition = self.state_machine.mission_stopped # type: ignore
|
|
35
|
+
def _check_and_handle_failed_stop(self, event: Queue[ErrorMessage]) -> bool:
|
|
36
|
+
error_message: Optional[ErrorMessage] = check_for_event(event)
|
|
37
|
+
if error_message is not None:
|
|
38
|
+
self.logger.warning(error_message.error_description)
|
|
39
|
+
self.state_machine.mission_stopped() # type: ignore
|
|
40
|
+
return True
|
|
41
|
+
return False
|
|
72
42
|
|
|
73
|
-
|
|
43
|
+
def _check_and_handle_successful_stop(self, event: Queue[bool]) -> bool:
|
|
44
|
+
if check_for_event(event):
|
|
45
|
+
self.state_machine.mission_stopped() # type: ignore
|
|
46
|
+
return True
|
|
47
|
+
return False
|
|
74
48
|
|
|
75
|
-
|
|
49
|
+
def _run(self) -> None:
|
|
50
|
+
while True:
|
|
76
51
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
52
|
+
if self._check_and_handle_failed_stop(
|
|
53
|
+
self.events.robot_service_events.mission_failed_to_stop
|
|
54
|
+
):
|
|
55
|
+
break
|
|
81
56
|
|
|
82
|
-
self.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"been attempted"
|
|
87
|
-
)
|
|
57
|
+
if self._check_and_handle_successful_stop(
|
|
58
|
+
self.events.robot_service_events.mission_successfully_stopped
|
|
59
|
+
):
|
|
60
|
+
break
|
|
88
61
|
|
|
89
|
-
|
|
90
|
-
time.sleep(self.state_machine.sleep_time)
|
|
91
|
-
return False
|
|
62
|
+
time.sleep(self.state_machine.sleep_time)
|
|
@@ -15,7 +15,7 @@ def pause_mission(state_machine: "StateMachine") -> bool:
|
|
|
15
15
|
paused_mission_response: ControlMissionResponse = (
|
|
16
16
|
state_machine._make_control_mission_response()
|
|
17
17
|
)
|
|
18
|
-
state_machine.
|
|
18
|
+
state_machine.events.api_requests.pause_mission.output.put(paused_mission_response)
|
|
19
19
|
|
|
20
20
|
state_machine.publish_mission_status()
|
|
21
21
|
state_machine.publish_task_status(task=state_machine.current_task)
|
|
@@ -21,7 +21,7 @@ def resume_mission(state_machine: "StateMachine") -> bool:
|
|
|
21
21
|
resume_mission_response: ControlMissionResponse = (
|
|
22
22
|
state_machine._make_control_mission_response()
|
|
23
23
|
)
|
|
24
|
-
state_machine.
|
|
24
|
+
state_machine.events.api_requests.resume_mission.output.put(resume_mission_response)
|
|
25
25
|
|
|
26
26
|
state_machine.robot.resume()
|
|
27
27
|
return True
|
|
@@ -12,7 +12,7 @@ from robot_interface.models.mission.status import MissionStatus, TaskStatus
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def put_start_mission_on_queue(state_machine: "StateMachine") -> bool:
|
|
15
|
-
state_machine.
|
|
15
|
+
state_machine.events.api_requests.start_mission.output.put(True)
|
|
16
16
|
return True
|
|
17
17
|
|
|
18
18
|
|
|
@@ -62,12 +62,12 @@ def set_mission_to_in_progress(state_machine: "StateMachine") -> bool:
|
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
def trigger_start_mission_or_task_event(state_machine: "StateMachine") -> bool:
|
|
65
|
-
state_machine.
|
|
65
|
+
state_machine.events.state_machine_events.start_mission.put(
|
|
66
66
|
state_machine.current_mission
|
|
67
67
|
)
|
|
68
68
|
return True
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
def _initialization_failed(state_machine: "StateMachine") -> None:
|
|
72
|
-
state_machine.
|
|
72
|
+
state_machine.events.api_requests.start_mission.output.put(False)
|
|
73
73
|
state_machine._finalize()
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING
|
|
2
2
|
|
|
3
|
+
from isar.models.communication.queues.queue_utils import trigger_event_without_data
|
|
4
|
+
|
|
3
5
|
if TYPE_CHECKING:
|
|
4
6
|
from isar.state_machine.state_machine import StateMachine
|
|
5
7
|
|
|
@@ -7,7 +9,12 @@ from isar.apis.models.models import ControlMissionResponse
|
|
|
7
9
|
from robot_interface.models.mission.status import MissionStatus, TaskStatus
|
|
8
10
|
|
|
9
11
|
|
|
10
|
-
def
|
|
12
|
+
def trigger_stop_mission_event(state_machine: "StateMachine") -> bool:
|
|
13
|
+
trigger_event_without_data(state_machine.events.state_machine_events.stop_mission)
|
|
14
|
+
return True
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def stop_mission_cleanup(state_machine: "StateMachine") -> bool:
|
|
11
18
|
if state_machine.current_mission is None:
|
|
12
19
|
state_machine._queue_empty_response()
|
|
13
20
|
state_machine.reset_state_machine()
|
|
@@ -26,7 +33,7 @@ def stop_mission(state_machine: "StateMachine") -> bool:
|
|
|
26
33
|
stopped_mission_response: ControlMissionResponse = (
|
|
27
34
|
state_machine._make_control_mission_response()
|
|
28
35
|
)
|
|
29
|
-
state_machine.
|
|
36
|
+
state_machine.events.api_requests.stop_mission.output.put(stopped_mission_response)
|
|
30
37
|
|
|
31
38
|
state_machine.publish_task_status(task=state_machine.current_task)
|
|
32
39
|
state_machine._finalize()
|
isar/storage/uploader.py
CHANGED
|
@@ -8,7 +8,7 @@ from typing import List, Union
|
|
|
8
8
|
from injector import inject
|
|
9
9
|
|
|
10
10
|
from isar.config.settings import settings
|
|
11
|
-
from isar.models.communication.queues.
|
|
11
|
+
from isar.models.communication.queues.events import Events
|
|
12
12
|
from isar.storage.storage_interface import StorageException, StorageInterface
|
|
13
13
|
from robot_interface.models.inspection.inspection import Inspection
|
|
14
14
|
from robot_interface.models.mission.mission import Mission
|
|
@@ -48,7 +48,7 @@ class Uploader:
|
|
|
48
48
|
@inject
|
|
49
49
|
def __init__(
|
|
50
50
|
self,
|
|
51
|
-
|
|
51
|
+
events: Events,
|
|
52
52
|
storage_handlers: List[StorageInterface],
|
|
53
53
|
mqtt_publisher: MqttClientInterface,
|
|
54
54
|
max_wait_time: int = settings.UPLOAD_FAILURE_MAX_WAIT,
|
|
@@ -58,8 +58,8 @@ class Uploader:
|
|
|
58
58
|
|
|
59
59
|
Parameters
|
|
60
60
|
----------
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
events : Events
|
|
62
|
+
Events used for cross-thread communication.
|
|
63
63
|
storage_handlers : List[StorageInterface]
|
|
64
64
|
List of handlers for different upload options
|
|
65
65
|
max_wait_time : float
|
|
@@ -67,7 +67,7 @@ class Uploader:
|
|
|
67
67
|
max_retry_attempts : int
|
|
68
68
|
Maximum attempts to retry an upload when it fails
|
|
69
69
|
"""
|
|
70
|
-
self.upload_queue: Queue =
|
|
70
|
+
self.upload_queue: Queue = events.upload_queue
|
|
71
71
|
self.storage_handlers: List[StorageInterface] = storage_handlers
|
|
72
72
|
self.mqtt_publisher = mqtt_publisher
|
|
73
73
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: isar
|
|
3
|
-
Version: 1.26.
|
|
3
|
+
Version: 1.26.1
|
|
4
4
|
Summary: Integration and Supervisory control of Autonomous Robots
|
|
5
5
|
Author-email: Equinor ASA <fg_robots_dev@equinor.com>
|
|
6
6
|
License: Eclipse Public License version 2.0
|
|
@@ -95,6 +95,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
95
95
|
Classifier: Programming Language :: Python :: 3.10
|
|
96
96
|
Classifier: Programming Language :: Python :: 3.11
|
|
97
97
|
Classifier: Programming Language :: Python :: 3.12
|
|
98
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
98
99
|
Classifier: Topic :: Scientific/Engineering
|
|
99
100
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
100
101
|
Classifier: Topic :: Software Development :: Libraries
|
|
@@ -137,6 +138,7 @@ Requires-Dist: pytest-xdist; extra == "dev"
|
|
|
137
138
|
Requires-Dist: pytest; extra == "dev"
|
|
138
139
|
Requires-Dist: requests-mock; extra == "dev"
|
|
139
140
|
Requires-Dist: ruff; extra == "dev"
|
|
141
|
+
Dynamic: license-file
|
|
140
142
|
|
|
141
143
|
# ISAR
|
|
142
144
|
|
|
@@ -254,21 +256,6 @@ In [this](./src/isar/config/predefined_missions) folder there are predefined def
|
|
|
254
256
|
corresponding to `mission_id=1`. A new mission may be added by adding a new json-file with a mission description. Note,
|
|
255
257
|
the mission IDs must be unique.
|
|
256
258
|
|
|
257
|
-
### Running with docker-compose
|
|
258
|
-
|
|
259
|
-
ISAR may be started with an instance of the [isar-robot](https://github.com/equinor/isar-robot) package by
|
|
260
|
-
|
|
261
|
-
```shell
|
|
262
|
-
docker-compose up --build
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
Provided that the simulator from [isar-turtlebot](https://github.com/equinor/isar-turtlebot) is running ISAR may be
|
|
266
|
-
started with the turtlebot by
|
|
267
|
-
|
|
268
|
-
```shell
|
|
269
|
-
docker-compose -f docker-compose-turtlebot.yml up --build
|
|
270
|
-
```
|
|
271
|
-
|
|
272
259
|
### Configuration
|
|
273
260
|
|
|
274
261
|
The system consists of many configuration variables which may alter the functionality. As an example, it is possible to
|