isar 1.31.0__py3-none-any.whl → 1.32.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 +18 -0
- isar/apis/schedule/scheduling_controller.py +16 -0
- isar/config/log.py +1 -2
- isar/config/logging.conf +9 -30
- isar/config/open_telemetry.py +2 -8
- isar/config/settings.py +4 -0
- isar/eventhandlers/eventhandler.py +93 -0
- isar/models/events.py +119 -0
- isar/modules.py +1 -1
- isar/robot/robot.py +16 -18
- isar/robot/robot_start_mission.py +8 -14
- isar/robot/robot_status.py +2 -3
- isar/robot/robot_stop_mission.py +3 -9
- isar/robot/robot_task_status.py +3 -7
- isar/script.py +9 -17
- isar/services/utilities/scheduling_utilities.py +45 -23
- isar/state_machine/state_machine.py +104 -9
- isar/state_machine/states/await_next_mission.py +46 -11
- isar/state_machine/states/blocked_protective_stop.py +24 -15
- isar/state_machine/states/home.py +40 -9
- isar/state_machine/states/intervention_needed.py +43 -0
- isar/state_machine/states/monitor.py +83 -12
- isar/state_machine/states/offline.py +25 -13
- isar/state_machine/states/paused.py +24 -38
- isar/state_machine/states/returning_home.py +75 -14
- isar/state_machine/states/robot_standing_still.py +41 -11
- isar/state_machine/states/stopping.py +52 -67
- isar/state_machine/states/unknown_status.py +37 -64
- isar/state_machine/states_enum.py +1 -0
- isar/state_machine/transitions/functions/fail_mission.py +7 -0
- isar/state_machine/transitions/functions/robot_status.py +4 -5
- isar/state_machine/transitions/functions/stop.py +3 -12
- isar/state_machine/transitions/mission.py +0 -6
- isar/state_machine/transitions/return_home.py +14 -2
- isar/state_machine/utils/common_event_handlers.py +166 -0
- isar/storage/uploader.py +1 -1
- {isar-1.31.0.dist-info → isar-1.32.0.dist-info}/METADATA +1 -1
- {isar-1.31.0.dist-info → isar-1.32.0.dist-info}/RECORD +44 -54
- robot_interface/models/mission/status.py +1 -0
- robot_interface/telemetry/payloads.py +8 -0
- isar/models/communication/__init__.py +0 -0
- isar/models/communication/message.py +0 -8
- isar/models/communication/queues/__init__.py +0 -0
- isar/models/communication/queues/events.py +0 -58
- isar/models/communication/queues/queue_io.py +0 -12
- isar/models/communication/queues/queue_timeout_error.py +0 -2
- isar/models/communication/queues/queue_utils.py +0 -38
- isar/models/communication/queues/status_queue.py +0 -22
- isar/models/mission_metadata/__init__.py +0 -0
- isar/services/service_connections/stid/__init__.py +0 -0
- isar/services/utilities/queue_utilities.py +0 -39
- isar/state_machine/generic_states/idle.py +0 -133
- isar/state_machine/generic_states/ongoing_mission.py +0 -309
- isar/state_machine/generic_states/robot_unavailable.py +0 -61
- {isar-1.31.0.dist-info → isar-1.32.0.dist-info}/WHEEL +0 -0
- {isar-1.31.0.dist-info → isar-1.32.0.dist-info}/entry_points.txt +0 -0
- {isar-1.31.0.dist-info → isar-1.32.0.dist-info}/licenses/LICENSE +0 -0
- {isar-1.31.0.dist-info → isar-1.32.0.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from copy import deepcopy
|
|
3
3
|
from http import HTTPStatus
|
|
4
|
-
from
|
|
5
|
-
from typing import Any, List
|
|
4
|
+
from typing import List, TypeVar
|
|
6
5
|
|
|
7
6
|
from fastapi import HTTPException
|
|
8
7
|
from requests import HTTPError
|
|
@@ -14,15 +13,20 @@ from isar.mission_planner.mission_planner_interface import (
|
|
|
14
13
|
MissionPlannerError,
|
|
15
14
|
MissionPlannerInterface,
|
|
16
15
|
)
|
|
17
|
-
from isar.models.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
from isar.models.events import (
|
|
17
|
+
APIEvent,
|
|
18
|
+
APIRequests,
|
|
19
|
+
Events,
|
|
20
|
+
EventTimeoutError,
|
|
21
|
+
SharedState,
|
|
22
|
+
)
|
|
22
23
|
from isar.state_machine.states_enum import States
|
|
23
24
|
from robot_interface.models.mission.mission import Mission
|
|
24
25
|
from robot_interface.models.mission.status import MissionStatus
|
|
25
26
|
|
|
27
|
+
T1 = TypeVar("T1")
|
|
28
|
+
T2 = TypeVar("T2")
|
|
29
|
+
|
|
26
30
|
|
|
27
31
|
class SchedulingUtilities:
|
|
28
32
|
"""
|
|
@@ -51,9 +55,8 @@ class SchedulingUtilities:
|
|
|
51
55
|
HTTPException 500 Internal Server Error
|
|
52
56
|
If the current state is not available on the queue
|
|
53
57
|
"""
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
except Empty:
|
|
58
|
+
current_state = self.shared_state.state.check()
|
|
59
|
+
if current_state is None:
|
|
57
60
|
error_message: str = (
|
|
58
61
|
"Internal Server Error - Current state of the state machine is unknown"
|
|
59
62
|
)
|
|
@@ -61,6 +64,7 @@ class SchedulingUtilities:
|
|
|
61
64
|
raise HTTPException(
|
|
62
65
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=error_message
|
|
63
66
|
)
|
|
67
|
+
return current_state
|
|
64
68
|
|
|
65
69
|
def get_mission(self, mission_id: str) -> Mission:
|
|
66
70
|
"""Get the mission with mission_id from the current mission planner
|
|
@@ -173,10 +177,10 @@ class SchedulingUtilities:
|
|
|
173
177
|
"""
|
|
174
178
|
try:
|
|
175
179
|
self._send_command(
|
|
176
|
-
|
|
180
|
+
deepcopy(mission),
|
|
177
181
|
self.api_events.start_mission,
|
|
178
182
|
)
|
|
179
|
-
except
|
|
183
|
+
except EventTimeoutError:
|
|
180
184
|
error_message = "Internal Server Error - Failed to start mission in ISAR"
|
|
181
185
|
self.logger.error(error_message)
|
|
182
186
|
raise HTTPException(
|
|
@@ -199,7 +203,7 @@ class SchedulingUtilities:
|
|
|
199
203
|
True,
|
|
200
204
|
self.api_events.return_home,
|
|
201
205
|
)
|
|
202
|
-
except
|
|
206
|
+
except EventTimeoutError:
|
|
203
207
|
error_message = (
|
|
204
208
|
"Internal Server Error - Failed to start return home mission in ISAR"
|
|
205
209
|
)
|
|
@@ -221,7 +225,7 @@ class SchedulingUtilities:
|
|
|
221
225
|
response = self._send_command(True, self.api_events.pause_mission)
|
|
222
226
|
self.logger.info("OK - Mission successfully paused")
|
|
223
227
|
return response
|
|
224
|
-
except
|
|
228
|
+
except EventTimeoutError:
|
|
225
229
|
error_message = "Internal Server Error - Failed to pause mission"
|
|
226
230
|
self.logger.error(error_message)
|
|
227
231
|
raise HTTPException(
|
|
@@ -240,7 +244,7 @@ class SchedulingUtilities:
|
|
|
240
244
|
response = self._send_command(True, self.api_events.resume_mission)
|
|
241
245
|
self.logger.info("OK - Mission successfully resumed")
|
|
242
246
|
return response
|
|
243
|
-
except
|
|
247
|
+
except EventTimeoutError:
|
|
244
248
|
error_message = "Internal Server Error - Failed to resume mission"
|
|
245
249
|
self.logger.error(error_message)
|
|
246
250
|
raise HTTPException(
|
|
@@ -277,7 +281,7 @@ class SchedulingUtilities:
|
|
|
277
281
|
raise HTTPException(
|
|
278
282
|
status_code=HTTPStatus.CONFLICT, detail=error_message
|
|
279
283
|
)
|
|
280
|
-
except
|
|
284
|
+
except EventTimeoutError:
|
|
281
285
|
error_message = "Internal Server Error - Failed to stop mission"
|
|
282
286
|
self.logger.error(error_message)
|
|
283
287
|
raise HTTPException(
|
|
@@ -286,14 +290,32 @@ class SchedulingUtilities:
|
|
|
286
290
|
self.logger.info("OK - Mission successfully stopped")
|
|
287
291
|
return stop_mission_response
|
|
288
292
|
|
|
289
|
-
def
|
|
290
|
-
|
|
293
|
+
def release_intervention_needed(self) -> None:
|
|
294
|
+
"""Release intervention needed state
|
|
295
|
+
|
|
296
|
+
Raises
|
|
297
|
+
------
|
|
298
|
+
HTTPException 500 Internal Server Error
|
|
299
|
+
If the intervention needed state could not be released
|
|
300
|
+
"""
|
|
291
301
|
try:
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
302
|
+
self._send_command(True, self.api_events.release_intervention_needed)
|
|
303
|
+
self.logger.info("OK - Intervention needed state released")
|
|
304
|
+
except EventTimeoutError:
|
|
305
|
+
error_message = (
|
|
306
|
+
"Internal Server Error - Failed to release intervention needed state"
|
|
295
307
|
)
|
|
296
|
-
|
|
297
|
-
|
|
308
|
+
self.logger.error(error_message)
|
|
309
|
+
raise HTTPException(
|
|
310
|
+
status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=error_message
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
def _send_command(self, input: T1, api_event: APIEvent[T1, T2]) -> T2:
|
|
314
|
+
api_event.input.trigger_event(input)
|
|
315
|
+
try:
|
|
316
|
+
return api_event.output.consume_event(timeout=self.queue_timeout)
|
|
317
|
+
except EventTimeoutError as e:
|
|
318
|
+
self.logger.error("Queue timed out")
|
|
319
|
+
api_event.input.clear_event()
|
|
298
320
|
self.logger.error("No output received for command to state machine")
|
|
299
321
|
raise e
|
|
@@ -3,7 +3,7 @@ import logging
|
|
|
3
3
|
from collections import deque
|
|
4
4
|
from datetime import datetime, timezone
|
|
5
5
|
from threading import Event
|
|
6
|
-
from typing import Deque, List, Optional
|
|
6
|
+
from typing import Deque, List, Optional, Tuple
|
|
7
7
|
|
|
8
8
|
from transitions import Machine
|
|
9
9
|
from transitions.core import State
|
|
@@ -14,11 +14,11 @@ from isar.mission_planner.task_selector_interface import (
|
|
|
14
14
|
TaskSelectorInterface,
|
|
15
15
|
TaskSelectorStop,
|
|
16
16
|
)
|
|
17
|
-
from isar.models.
|
|
18
|
-
from isar.models.communication.queues.queue_utils import update_shared_state
|
|
17
|
+
from isar.models.events import Events, SharedState
|
|
19
18
|
from isar.state_machine.states.await_next_mission import AwaitNextMission
|
|
20
19
|
from isar.state_machine.states.blocked_protective_stop import BlockedProtectiveStop
|
|
21
20
|
from isar.state_machine.states.home import Home
|
|
21
|
+
from isar.state_machine.states.intervention_needed import InterventionNeeded
|
|
22
22
|
from isar.state_machine.states.monitor import Monitor
|
|
23
23
|
from isar.state_machine.states.offline import Offline
|
|
24
24
|
from isar.state_machine.states.paused import Paused
|
|
@@ -30,13 +30,19 @@ from isar.state_machine.states_enum import States
|
|
|
30
30
|
from isar.state_machine.transitions.mission import get_mission_transitions
|
|
31
31
|
from isar.state_machine.transitions.return_home import get_return_home_transitions
|
|
32
32
|
from isar.state_machine.transitions.robot_status import get_robot_status_transitions
|
|
33
|
-
from robot_interface.models.exceptions.robot_exceptions import
|
|
33
|
+
from robot_interface.models.exceptions.robot_exceptions import (
|
|
34
|
+
ErrorMessage,
|
|
35
|
+
RobotException,
|
|
36
|
+
RobotRetrieveInspectionException,
|
|
37
|
+
)
|
|
38
|
+
from robot_interface.models.inspection.inspection import Inspection
|
|
34
39
|
from robot_interface.models.mission.mission import Mission
|
|
35
40
|
from robot_interface.models.mission.status import RobotStatus, TaskStatus
|
|
36
|
-
from robot_interface.models.mission.task import TASKS
|
|
41
|
+
from robot_interface.models.mission.task import TASKS, InspectionTask, Task
|
|
37
42
|
from robot_interface.robot_interface import RobotInterface
|
|
38
43
|
from robot_interface.telemetry.mqtt_client import MqttClientInterface
|
|
39
44
|
from robot_interface.telemetry.payloads import (
|
|
45
|
+
InterventionNeededPayload,
|
|
40
46
|
MissionPayload,
|
|
41
47
|
RobotStatusPayload,
|
|
42
48
|
TaskPayload,
|
|
@@ -97,6 +103,7 @@ class StateMachine(object):
|
|
|
97
103
|
self.await_next_mission_state: State = AwaitNextMission(self)
|
|
98
104
|
self.home_state: State = Home(self)
|
|
99
105
|
self.robot_standing_still_state: State = RobotStandingStill(self)
|
|
106
|
+
self.intervention_needed_state: State = InterventionNeeded(self)
|
|
100
107
|
|
|
101
108
|
# Status states
|
|
102
109
|
self.offline_state: State = Offline(self)
|
|
@@ -116,6 +123,7 @@ class StateMachine(object):
|
|
|
116
123
|
self.offline_state,
|
|
117
124
|
self.blocked_protective_stopping_state,
|
|
118
125
|
self.unknown_status_state,
|
|
126
|
+
self.intervention_needed_state,
|
|
119
127
|
]
|
|
120
128
|
|
|
121
129
|
self.machine = Machine(
|
|
@@ -140,6 +148,8 @@ class StateMachine(object):
|
|
|
140
148
|
|
|
141
149
|
self.current_state: State = States(self.state) # type: ignore
|
|
142
150
|
|
|
151
|
+
self.awaiting_task_status: bool = False
|
|
152
|
+
|
|
143
153
|
self.transitions_log_length: int = transitions_log_length
|
|
144
154
|
self.transitions_list: Deque[States] = deque([], self.transitions_log_length)
|
|
145
155
|
|
|
@@ -182,7 +192,7 @@ class StateMachine(object):
|
|
|
182
192
|
def update_state(self):
|
|
183
193
|
"""Updates the current state of the state machine."""
|
|
184
194
|
self.current_state = States(self.state) # type: ignore
|
|
185
|
-
|
|
195
|
+
self.shared_state.state.update(self.current_state)
|
|
186
196
|
self._log_state_transition(self.current_state)
|
|
187
197
|
self.logger.info("State: %s", self.current_state)
|
|
188
198
|
self.publish_status()
|
|
@@ -201,9 +211,21 @@ class StateMachine(object):
|
|
|
201
211
|
self.task_selector.initialize(tasks=self.current_mission.tasks)
|
|
202
212
|
|
|
203
213
|
def send_task_status(self):
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
214
|
+
self.shared_state.state_machine_current_task.update(self.current_task)
|
|
215
|
+
|
|
216
|
+
def report_task_status(self, task: Task) -> None:
|
|
217
|
+
if task.status == TaskStatus.Failed:
|
|
218
|
+
self.logger.warning(
|
|
219
|
+
f"Task: {str(task.id)[:8]} was reported as failed by the robot"
|
|
220
|
+
)
|
|
221
|
+
elif task.status == TaskStatus.Successful:
|
|
222
|
+
self.logger.info(
|
|
223
|
+
f"{type(task).__name__} task: {str(task.id)[:8]} completed"
|
|
224
|
+
)
|
|
225
|
+
else:
|
|
226
|
+
self.logger.info(
|
|
227
|
+
f"Task: {str(task.id)[:8]} was reported as task.status by the robot"
|
|
228
|
+
)
|
|
207
229
|
|
|
208
230
|
def publish_mission_status(self) -> None:
|
|
209
231
|
if not self.mqtt_publisher:
|
|
@@ -264,6 +286,25 @@ class StateMachine(object):
|
|
|
264
286
|
retain=True,
|
|
265
287
|
)
|
|
266
288
|
|
|
289
|
+
def publish_intervention_needed(self, error_message: str) -> None:
|
|
290
|
+
"""Publishes the intervention needed message to the MQTT Broker"""
|
|
291
|
+
if not self.mqtt_publisher:
|
|
292
|
+
return
|
|
293
|
+
|
|
294
|
+
payload: InterventionNeededPayload = InterventionNeededPayload(
|
|
295
|
+
isar_id=settings.ISAR_ID,
|
|
296
|
+
robot_name=settings.ROBOT_NAME,
|
|
297
|
+
reason=error_message,
|
|
298
|
+
timestamp=datetime.now(timezone.utc),
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
self.mqtt_publisher.publish(
|
|
302
|
+
topic=settings.TOPIC_ISAR_INTERVENTION_NEEDED,
|
|
303
|
+
payload=json.dumps(payload, cls=EnhancedJSONEncoder),
|
|
304
|
+
qos=1,
|
|
305
|
+
retain=True,
|
|
306
|
+
)
|
|
307
|
+
|
|
267
308
|
def publish_status(self) -> None:
|
|
268
309
|
if not self.mqtt_publisher:
|
|
269
310
|
return
|
|
@@ -296,6 +337,8 @@ class StateMachine(object):
|
|
|
296
337
|
return RobotStatus.Offline
|
|
297
338
|
elif self.current_state == States.BlockedProtectiveStop:
|
|
298
339
|
return RobotStatus.BlockedProtectiveStop
|
|
340
|
+
elif self.current_state == States.InterventionNeeded:
|
|
341
|
+
return RobotStatus.InterventionNeeded
|
|
299
342
|
else:
|
|
300
343
|
return RobotStatus.Busy
|
|
301
344
|
|
|
@@ -334,6 +377,58 @@ class StateMachine(object):
|
|
|
334
377
|
)
|
|
335
378
|
)
|
|
336
379
|
|
|
380
|
+
def should_upload_inspections(self) -> bool:
|
|
381
|
+
if settings.UPLOAD_INSPECTIONS_ASYNC:
|
|
382
|
+
return False
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
self.current_task.is_finished()
|
|
386
|
+
and self.current_task.status == TaskStatus.Successful
|
|
387
|
+
and isinstance(self.current_task, InspectionTask)
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
def queue_inspections_for_upload(
|
|
391
|
+
self, mission: Mission, current_task: InspectionTask, logger: logging.Logger
|
|
392
|
+
) -> None:
|
|
393
|
+
try:
|
|
394
|
+
inspection: Inspection = self.robot.get_inspection(task=current_task)
|
|
395
|
+
if current_task.inspection_id != inspection.id:
|
|
396
|
+
logger.warning(
|
|
397
|
+
f"The inspection_id of task ({current_task.inspection_id}) "
|
|
398
|
+
f"and result ({inspection.id}) is not matching. "
|
|
399
|
+
f"This may lead to confusions when accessing the inspection later"
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
except (RobotRetrieveInspectionException, RobotException) as e:
|
|
403
|
+
error_message: ErrorMessage = ErrorMessage(
|
|
404
|
+
error_reason=e.error_reason, error_description=e.error_description
|
|
405
|
+
)
|
|
406
|
+
self.current_task.error_message = error_message
|
|
407
|
+
logger.error(
|
|
408
|
+
f"Failed to retrieve inspections because: {e.error_description}"
|
|
409
|
+
)
|
|
410
|
+
return
|
|
411
|
+
|
|
412
|
+
except Exception as e:
|
|
413
|
+
logger.error(
|
|
414
|
+
f"Failed to retrieve inspections because of unexpected error: {e}"
|
|
415
|
+
)
|
|
416
|
+
return
|
|
417
|
+
|
|
418
|
+
if not inspection:
|
|
419
|
+
logger.warning(
|
|
420
|
+
f"No inspection result data retrieved for task {str(current_task.id)[:8]}"
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
inspection.metadata.tag_id = current_task.tag_id
|
|
424
|
+
|
|
425
|
+
message: Tuple[Inspection, Mission] = (
|
|
426
|
+
inspection,
|
|
427
|
+
mission,
|
|
428
|
+
)
|
|
429
|
+
self.events.upload_queue.put(message)
|
|
430
|
+
logger.info(f"Inspection result: {str(inspection.id)[:8]} queued for upload")
|
|
431
|
+
|
|
337
432
|
|
|
338
433
|
def main(state_machine: StateMachine):
|
|
339
434
|
"""Starts a state machine instance."""
|
|
@@ -1,20 +1,55 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING
|
|
1
|
+
from typing import TYPE_CHECKING, List
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from isar.config.settings import settings
|
|
4
|
+
from isar.eventhandlers.eventhandler import (
|
|
5
|
+
EventHandlerBase,
|
|
6
|
+
EventHandlerMapping,
|
|
7
|
+
TimeoutHandlerMapping,
|
|
8
|
+
)
|
|
9
|
+
from isar.state_machine.utils.common_event_handlers import (
|
|
10
|
+
return_home_event_handler,
|
|
11
|
+
start_mission_event_handler,
|
|
12
|
+
stop_mission_event_handler,
|
|
13
|
+
)
|
|
4
14
|
|
|
5
15
|
if TYPE_CHECKING:
|
|
6
16
|
from isar.state_machine.state_machine import StateMachine
|
|
7
17
|
|
|
8
|
-
from isar.state_machine.generic_states.idle import Idle, IdleStates
|
|
9
18
|
|
|
19
|
+
class AwaitNextMission(EventHandlerBase):
|
|
10
20
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
22
|
+
events = state_machine.events
|
|
23
|
+
|
|
24
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
25
|
+
EventHandlerMapping(
|
|
26
|
+
name="start_mission_event",
|
|
27
|
+
event=events.api_requests.start_mission.input,
|
|
28
|
+
handler=lambda event: start_mission_event_handler(state_machine, event),
|
|
29
|
+
),
|
|
30
|
+
EventHandlerMapping(
|
|
31
|
+
name="return_home_event",
|
|
32
|
+
event=events.api_requests.return_home.input,
|
|
33
|
+
handler=lambda event: return_home_event_handler(state_machine, event),
|
|
34
|
+
),
|
|
35
|
+
EventHandlerMapping(
|
|
36
|
+
name="stop_mission_event",
|
|
37
|
+
event=events.api_requests.return_home.input,
|
|
38
|
+
handler=lambda event: stop_mission_event_handler(state_machine, event),
|
|
39
|
+
),
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
timers: List[TimeoutHandlerMapping] = [
|
|
43
|
+
TimeoutHandlerMapping(
|
|
44
|
+
name="should_return_home_timer",
|
|
45
|
+
timeout_in_seconds=settings.RETURN_HOME_DELAY,
|
|
46
|
+
handler=lambda: state_machine.request_return_home, # type: ignore
|
|
47
|
+
)
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
super().__init__(
|
|
51
|
+
state_name="await_next_mission",
|
|
18
52
|
state_machine=state_machine,
|
|
19
|
-
|
|
53
|
+
event_handler_mappings=event_handlers,
|
|
54
|
+
timers=timers,
|
|
20
55
|
)
|
|
@@ -1,24 +1,33 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING
|
|
1
|
+
from typing import TYPE_CHECKING, List
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from
|
|
6
|
-
RobotUnavailable,
|
|
7
|
-
RobotUnavailableStates,
|
|
8
|
-
)
|
|
3
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
4
|
+
from isar.models.events import Event
|
|
5
|
+
from robot_interface.models.mission.status import RobotStatus
|
|
9
6
|
|
|
10
7
|
if TYPE_CHECKING:
|
|
11
8
|
from isar.state_machine.state_machine import StateMachine
|
|
12
9
|
|
|
13
10
|
|
|
14
|
-
class BlockedProtectiveStop(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
class BlockedProtectiveStop(EventHandlerBase):
|
|
12
|
+
|
|
13
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
14
|
+
shared_state = state_machine.shared_state
|
|
15
|
+
|
|
16
|
+
def _robot_status_event_handler(event: Event[RobotStatus]):
|
|
17
|
+
robot_status: RobotStatus = event.check()
|
|
18
|
+
if robot_status != RobotStatus.BlockedProtectiveStop:
|
|
19
|
+
return state_machine.robot_status_changed # type: ignore
|
|
20
|
+
return None
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
23
|
+
EventHandlerMapping(
|
|
24
|
+
name="robot_status_event",
|
|
25
|
+
event=shared_state.robot_status,
|
|
26
|
+
handler=_robot_status_event_handler,
|
|
27
|
+
),
|
|
28
|
+
]
|
|
29
|
+
super().__init__(
|
|
30
|
+
state_name="blocked_protective_stop",
|
|
22
31
|
state_machine=state_machine,
|
|
23
|
-
|
|
32
|
+
event_handler_mappings=event_handlers,
|
|
24
33
|
)
|
|
@@ -1,19 +1,50 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING
|
|
1
|
+
from typing import TYPE_CHECKING, List
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
4
|
+
from isar.state_machine.utils.common_event_handlers import (
|
|
5
|
+
return_home_event_handler,
|
|
6
|
+
robot_status_event_handler,
|
|
7
|
+
start_mission_event_handler,
|
|
8
|
+
stop_mission_event_handler,
|
|
9
|
+
)
|
|
10
|
+
from robot_interface.models.mission.status import RobotStatus
|
|
4
11
|
|
|
5
12
|
if TYPE_CHECKING:
|
|
6
13
|
from isar.state_machine.state_machine import StateMachine
|
|
7
14
|
|
|
8
|
-
from isar.state_machine.generic_states.idle import Idle, IdleStates
|
|
9
15
|
|
|
16
|
+
class Home(EventHandlerBase):
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
19
|
+
events = state_machine.events
|
|
20
|
+
shared_state = state_machine.shared_state
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
23
|
+
EventHandlerMapping(
|
|
24
|
+
name="start_mission_event",
|
|
25
|
+
event=events.api_requests.start_mission.input,
|
|
26
|
+
handler=lambda event: start_mission_event_handler(state_machine, event),
|
|
27
|
+
),
|
|
28
|
+
EventHandlerMapping(
|
|
29
|
+
name="return_home_event",
|
|
30
|
+
event=events.api_requests.return_home.input,
|
|
31
|
+
handler=lambda event: return_home_event_handler(state_machine, event),
|
|
32
|
+
),
|
|
33
|
+
EventHandlerMapping(
|
|
34
|
+
name="stop_mission_event",
|
|
35
|
+
event=events.api_requests.return_home.input,
|
|
36
|
+
handler=lambda event: stop_mission_event_handler(state_machine, event),
|
|
37
|
+
),
|
|
38
|
+
EventHandlerMapping(
|
|
39
|
+
name="robot_status_event",
|
|
40
|
+
event=shared_state.robot_status,
|
|
41
|
+
handler=lambda event: robot_status_event_handler(
|
|
42
|
+
state_machine, RobotStatus.Home, event
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
]
|
|
46
|
+
super().__init__(
|
|
47
|
+
state_name="home",
|
|
17
48
|
state_machine=state_machine,
|
|
18
|
-
|
|
49
|
+
event_handler_mappings=event_handlers,
|
|
19
50
|
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
3
|
+
|
|
4
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
5
|
+
from isar.models.events import Event
|
|
6
|
+
from isar.state_machine.utils.common_event_handlers import return_home_event_handler
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from isar.state_machine.state_machine import StateMachine
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class InterventionNeeded(EventHandlerBase):
|
|
13
|
+
|
|
14
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
15
|
+
events = state_machine.events
|
|
16
|
+
|
|
17
|
+
def release_intervention_needed_handler(
|
|
18
|
+
event: Event[bool],
|
|
19
|
+
) -> Optional[Callable]:
|
|
20
|
+
if event.consume_event():
|
|
21
|
+
state_machine.events.api_requests.release_intervention_needed.output.trigger_event(
|
|
22
|
+
True
|
|
23
|
+
)
|
|
24
|
+
return state_machine.release_intervention_needed # type: ignore
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
28
|
+
EventHandlerMapping(
|
|
29
|
+
name="return_home_event",
|
|
30
|
+
event=events.api_requests.return_home.input,
|
|
31
|
+
handler=lambda event: return_home_event_handler(state_machine, event),
|
|
32
|
+
),
|
|
33
|
+
EventHandlerMapping(
|
|
34
|
+
name="release_intervention_needed_event",
|
|
35
|
+
event=events.api_requests.release_intervention_needed.input,
|
|
36
|
+
handler=release_intervention_needed_handler,
|
|
37
|
+
),
|
|
38
|
+
]
|
|
39
|
+
super().__init__(
|
|
40
|
+
state_name="intervention_needed",
|
|
41
|
+
state_machine=state_machine,
|
|
42
|
+
event_handler_mappings=event_handlers,
|
|
43
|
+
)
|
|
@@ -1,22 +1,93 @@
|
|
|
1
|
-
|
|
1
|
+
import logging
|
|
2
|
+
from copy import deepcopy
|
|
3
|
+
from typing import TYPE_CHECKING, Callable, List, Optional
|
|
2
4
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from isar.
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
6
|
+
from isar.models.events import Event
|
|
7
|
+
from isar.services.utilities.threaded_request import ThreadedRequest
|
|
8
|
+
from isar.state_machine.utils.common_event_handlers import (
|
|
9
|
+
mission_failed_event_handler,
|
|
10
|
+
mission_started_event_handler,
|
|
11
|
+
stop_mission_event_handler,
|
|
12
|
+
task_status_event_handler,
|
|
13
|
+
task_status_failed_event_handler,
|
|
8
14
|
)
|
|
15
|
+
from robot_interface.models.mission.status import TaskStatus
|
|
9
16
|
|
|
10
17
|
if TYPE_CHECKING:
|
|
11
18
|
from isar.state_machine.state_machine import StateMachine
|
|
12
19
|
|
|
13
20
|
|
|
14
|
-
class Monitor(
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
class Monitor(EventHandlerBase):
|
|
22
|
+
|
|
23
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
24
|
+
logger = logging.getLogger("state_machine")
|
|
25
|
+
events = state_machine.events
|
|
26
|
+
|
|
27
|
+
def _pause_mission_event_handler(event: Event[bool]) -> Optional[Callable]:
|
|
28
|
+
if event.consume_event():
|
|
29
|
+
return state_machine.pause # type: ignore
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
def _handle_task_completed(task_status: TaskStatus):
|
|
33
|
+
if state_machine.should_upload_inspections():
|
|
34
|
+
get_inspection_thread = ThreadedRequest(
|
|
35
|
+
state_machine.queue_inspections_for_upload
|
|
36
|
+
)
|
|
37
|
+
get_inspection_thread.start_thread(
|
|
38
|
+
deepcopy(state_machine.current_mission),
|
|
39
|
+
deepcopy(state_machine.current_task),
|
|
40
|
+
logger,
|
|
41
|
+
name="State Machine Get Inspections",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
state_machine.iterate_current_task()
|
|
45
|
+
if state_machine.current_task is None:
|
|
46
|
+
return state_machine.mission_finished # type: ignore
|
|
47
|
+
return None
|
|
17
48
|
|
|
18
|
-
|
|
19
|
-
|
|
49
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
50
|
+
EventHandlerMapping(
|
|
51
|
+
name="stop_mission_event",
|
|
52
|
+
event=events.api_requests.stop_mission.input,
|
|
53
|
+
handler=lambda event: stop_mission_event_handler(state_machine, event),
|
|
54
|
+
),
|
|
55
|
+
EventHandlerMapping(
|
|
56
|
+
name="pause_mission_event",
|
|
57
|
+
event=events.api_requests.pause_mission.input,
|
|
58
|
+
handler=_pause_mission_event_handler,
|
|
59
|
+
),
|
|
60
|
+
EventHandlerMapping(
|
|
61
|
+
name="mission_started_event",
|
|
62
|
+
event=events.robot_service_events.mission_started,
|
|
63
|
+
handler=lambda event: mission_started_event_handler(
|
|
64
|
+
state_machine, event
|
|
65
|
+
),
|
|
66
|
+
),
|
|
67
|
+
EventHandlerMapping(
|
|
68
|
+
name="mission_failed_event",
|
|
69
|
+
event=events.robot_service_events.mission_failed,
|
|
70
|
+
handler=lambda event: mission_failed_event_handler(
|
|
71
|
+
state_machine, event
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
EventHandlerMapping(
|
|
75
|
+
name="task_status_failed_event",
|
|
76
|
+
event=events.robot_service_events.task_status_failed,
|
|
77
|
+
handler=lambda event: task_status_failed_event_handler(
|
|
78
|
+
state_machine, _handle_task_completed, event
|
|
79
|
+
),
|
|
80
|
+
),
|
|
81
|
+
EventHandlerMapping(
|
|
82
|
+
name="task_status_event",
|
|
83
|
+
event=events.robot_service_events.task_status_updated,
|
|
84
|
+
handler=lambda event: task_status_event_handler(
|
|
85
|
+
state_machine, _handle_task_completed, event
|
|
86
|
+
),
|
|
87
|
+
),
|
|
88
|
+
]
|
|
89
|
+
super().__init__(
|
|
90
|
+
state_name="monitor",
|
|
20
91
|
state_machine=state_machine,
|
|
21
|
-
|
|
92
|
+
event_handler_mappings=event_handlers,
|
|
22
93
|
)
|