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.
- isar/__init__.py +2 -5
- isar/apis/api.py +159 -66
- isar/apis/models/__init__.py +0 -1
- isar/apis/models/models.py +22 -12
- isar/apis/models/start_mission_definition.py +128 -123
- isar/apis/robot_control/robot_controller.py +41 -0
- isar/apis/schedule/scheduling_controller.py +135 -121
- isar/apis/security/authentication.py +5 -5
- isar/config/certs/ca-cert.pem +32 -32
- isar/config/keyvault/keyvault_service.py +1 -2
- isar/config/log.py +47 -39
- isar/config/logging.conf +16 -31
- isar/config/open_telemetry.py +102 -0
- isar/config/predefined_mission_definition/default_exr.json +49 -0
- isar/config/predefined_mission_definition/default_mission.json +1 -5
- isar/config/predefined_mission_definition/default_turtlebot.json +4 -11
- isar/config/predefined_missions/default.json +67 -87
- isar/config/predefined_missions/default_extra_capabilities.json +107 -0
- isar/config/settings.py +119 -142
- isar/eventhandlers/eventhandler.py +123 -0
- isar/mission_planner/local_planner.py +6 -20
- isar/mission_planner/mission_planner_interface.py +1 -1
- isar/models/events.py +184 -0
- isar/models/status.py +18 -0
- isar/modules.py +118 -205
- isar/robot/robot.py +377 -0
- isar/robot/robot_battery.py +60 -0
- isar/robot/robot_monitor_mission.py +357 -0
- isar/robot/robot_pause_mission.py +74 -0
- isar/robot/robot_resume_mission.py +67 -0
- isar/robot/robot_start_mission.py +66 -0
- isar/robot/robot_status.py +61 -0
- isar/robot/robot_stop_mission.py +68 -0
- isar/robot/robot_upload_inspection.py +75 -0
- isar/script.py +171 -0
- isar/services/service_connections/mqtt/mqtt_client.py +47 -11
- isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +32 -0
- isar/services/service_connections/mqtt/robot_info_publisher.py +4 -3
- isar/services/service_connections/persistent_memory.py +69 -0
- isar/services/utilities/mqtt_utilities.py +93 -0
- isar/services/utilities/robot_utilities.py +20 -0
- isar/services/utilities/scheduling_utilities.py +393 -65
- isar/state_machine/state_machine.py +227 -486
- isar/state_machine/states/__init__.py +0 -7
- isar/state_machine/states/await_next_mission.py +114 -0
- isar/state_machine/states/blocked_protective_stop.py +60 -0
- isar/state_machine/states/going_to_lockdown.py +95 -0
- isar/state_machine/states/going_to_recharging.py +92 -0
- isar/state_machine/states/home.py +115 -0
- isar/state_machine/states/intervention_needed.py +77 -0
- isar/state_machine/states/lockdown.py +38 -0
- isar/state_machine/states/maintenance.py +36 -0
- isar/state_machine/states/monitor.py +137 -166
- isar/state_machine/states/offline.py +60 -0
- isar/state_machine/states/paused.py +92 -23
- isar/state_machine/states/pausing.py +48 -0
- isar/state_machine/states/pausing_return_home.py +48 -0
- isar/state_machine/states/recharging.py +80 -0
- isar/state_machine/states/resuming.py +57 -0
- isar/state_machine/states/resuming_return_home.py +64 -0
- isar/state_machine/states/return_home_paused.py +109 -0
- isar/state_machine/states/returning_home.py +217 -0
- isar/state_machine/states/stopping.py +61 -0
- isar/state_machine/states/stopping_due_to_maintenance.py +61 -0
- isar/state_machine/states/stopping_go_to_lockdown.py +60 -0
- isar/state_machine/states/stopping_go_to_recharge.py +51 -0
- isar/state_machine/states/stopping_return_home.py +77 -0
- isar/state_machine/states/unknown_status.py +72 -0
- isar/state_machine/states_enum.py +22 -5
- isar/state_machine/transitions/mission.py +192 -0
- isar/state_machine/transitions/return_home.py +106 -0
- isar/state_machine/transitions/robot_status.py +80 -0
- isar/state_machine/utils/common_event_handlers.py +73 -0
- isar/storage/blob_storage.py +71 -45
- isar/storage/local_storage.py +28 -14
- isar/storage/storage_interface.py +28 -6
- isar/storage/uploader.py +184 -55
- isar/storage/utilities.py +35 -27
- isar-1.34.9.dist-info/METADATA +496 -0
- isar-1.34.9.dist-info/RECORD +135 -0
- {isar-1.15.0.dist-info → isar-1.34.9.dist-info}/WHEEL +1 -1
- isar-1.34.9.dist-info/entry_points.txt +3 -0
- robot_interface/models/exceptions/__init__.py +0 -7
- robot_interface/models/exceptions/robot_exceptions.py +274 -4
- robot_interface/models/initialize/__init__.py +0 -1
- robot_interface/models/inspection/__init__.py +0 -13
- robot_interface/models/inspection/inspection.py +43 -34
- robot_interface/models/mission/mission.py +18 -14
- robot_interface/models/mission/status.py +20 -25
- robot_interface/models/mission/task.py +156 -92
- robot_interface/models/robots/battery_state.py +6 -0
- robot_interface/models/robots/media.py +13 -0
- robot_interface/models/robots/robot_model.py +7 -7
- robot_interface/robot_interface.py +135 -66
- robot_interface/telemetry/mqtt_client.py +84 -12
- robot_interface/telemetry/payloads.py +111 -12
- robot_interface/utilities/json_service.py +7 -1
- isar/config/predefined_missions/default_turtlebot.json +0 -110
- isar/config/predefined_poses/__init__.py +0 -0
- isar/config/predefined_poses/predefined_poses.py +0 -616
- isar/config/settings.env +0 -26
- isar/mission_planner/sequential_task_selector.py +0 -23
- isar/mission_planner/task_selector_interface.py +0 -31
- isar/models/communication/__init__.py +0 -0
- isar/models/communication/message.py +0 -12
- isar/models/communication/queues/__init__.py +0 -4
- isar/models/communication/queues/queue_io.py +0 -12
- isar/models/communication/queues/queue_timeout_error.py +0 -2
- isar/models/communication/queues/queues.py +0 -19
- isar/models/communication/queues/status_queue.py +0 -20
- isar/models/mission_metadata/__init__.py +0 -0
- isar/services/readers/__init__.py +0 -0
- isar/services/readers/base_reader.py +0 -37
- isar/services/service_connections/mqtt/robot_status_publisher.py +0 -93
- isar/services/service_connections/stid/__init__.py +0 -0
- isar/services/service_connections/stid/stid_service.py +0 -45
- isar/services/utilities/queue_utilities.py +0 -39
- isar/state_machine/states/idle.py +0 -40
- isar/state_machine/states/initialize.py +0 -60
- isar/state_machine/states/initiate.py +0 -129
- isar/state_machine/states/off.py +0 -18
- isar/state_machine/states/stop.py +0 -78
- isar/storage/slimm_storage.py +0 -181
- isar-1.15.0.dist-info/METADATA +0 -417
- isar-1.15.0.dist-info/RECORD +0 -113
- robot_interface/models/initialize/initialize_params.py +0 -9
- robot_interface/models/mission/step.py +0 -211
- {isar-1.15.0.dist-info → isar-1.34.9.dist-info/licenses}/LICENSE +0 -0
- {isar-1.15.0.dist-info → isar-1.34.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
2
|
+
|
|
3
|
+
from isar.apis.models.models import LockdownResponse, MaintenanceResponse
|
|
4
|
+
from isar.config.settings import settings
|
|
5
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
6
|
+
from isar.models.events import Event
|
|
7
|
+
from robot_interface.models.mission.status import RobotStatus
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from isar.state_machine.state_machine import StateMachine
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Recharging(EventHandlerBase):
|
|
14
|
+
|
|
15
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
16
|
+
shared_state = state_machine.shared_state
|
|
17
|
+
events = state_machine.events
|
|
18
|
+
|
|
19
|
+
def robot_battery_level_updated_handler(event: Event[float]):
|
|
20
|
+
battery_level: float = event.check()
|
|
21
|
+
if battery_level < settings.ROBOT_BATTERY_RECHARGE_THRESHOLD:
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
return state_machine.robot_recharged # type: ignore
|
|
25
|
+
|
|
26
|
+
def robot_offline_handler(event: Event[RobotStatus]):
|
|
27
|
+
robot_status: Optional[RobotStatus] = event.check()
|
|
28
|
+
|
|
29
|
+
if robot_status is None:
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
if robot_status == RobotStatus.Offline:
|
|
33
|
+
return state_machine.robot_status_offline # type: ignore
|
|
34
|
+
|
|
35
|
+
def _send_to_lockdown_event_handler(event: Event[bool]):
|
|
36
|
+
should_lockdown: bool = event.consume_event()
|
|
37
|
+
if not should_lockdown:
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
events.api_requests.send_to_lockdown.response.trigger_event(
|
|
41
|
+
LockdownResponse(lockdown_started=True)
|
|
42
|
+
)
|
|
43
|
+
return state_machine.reached_lockdown # type: ignore
|
|
44
|
+
|
|
45
|
+
def _set_maintenance_mode_event_handler(event: Event[bool]):
|
|
46
|
+
should_set_maintenande_mode: bool = event.consume_event()
|
|
47
|
+
if should_set_maintenande_mode:
|
|
48
|
+
events.api_requests.set_maintenance_mode.response.trigger_event(
|
|
49
|
+
MaintenanceResponse(is_maintenance_mode=True)
|
|
50
|
+
)
|
|
51
|
+
return state_machine.set_maintenance_mode # type: ignore
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
55
|
+
EventHandlerMapping(
|
|
56
|
+
name="robot_battery_update_event",
|
|
57
|
+
event=shared_state.robot_battery_level,
|
|
58
|
+
handler=robot_battery_level_updated_handler,
|
|
59
|
+
),
|
|
60
|
+
EventHandlerMapping(
|
|
61
|
+
name="robot_offline_event",
|
|
62
|
+
event=shared_state.robot_status,
|
|
63
|
+
handler=robot_offline_handler,
|
|
64
|
+
),
|
|
65
|
+
EventHandlerMapping(
|
|
66
|
+
name="send_to_lockdown_event",
|
|
67
|
+
event=events.api_requests.send_to_lockdown.request,
|
|
68
|
+
handler=_send_to_lockdown_event_handler,
|
|
69
|
+
),
|
|
70
|
+
EventHandlerMapping(
|
|
71
|
+
name="set_maintenance_mode",
|
|
72
|
+
event=events.api_requests.set_maintenance_mode.request,
|
|
73
|
+
handler=_set_maintenance_mode_event_handler,
|
|
74
|
+
),
|
|
75
|
+
]
|
|
76
|
+
super().__init__(
|
|
77
|
+
state_name="recharging",
|
|
78
|
+
state_machine=state_machine,
|
|
79
|
+
event_handler_mappings=event_handlers,
|
|
80
|
+
)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Callable, List, Optional
|
|
2
|
+
|
|
3
|
+
from isar.apis.models.models import ControlMissionResponse
|
|
4
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
5
|
+
from isar.models.events import Event
|
|
6
|
+
from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from isar.state_machine.state_machine import StateMachine
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Resuming(EventHandlerBase):
|
|
13
|
+
|
|
14
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
15
|
+
events = state_machine.events
|
|
16
|
+
|
|
17
|
+
def _failed_resume_event_handler(
|
|
18
|
+
event: Event[ErrorMessage],
|
|
19
|
+
) -> Optional[Callable]:
|
|
20
|
+
error_message: Optional[ErrorMessage] = event.consume_event()
|
|
21
|
+
|
|
22
|
+
if error_message is None:
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
state_machine.events.api_requests.resume_mission.response.trigger_event(
|
|
26
|
+
ControlMissionResponse(success=False)
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
return state_machine.mission_resuming_failed # type: ignore
|
|
30
|
+
|
|
31
|
+
def _successful_resume_event_handler(event: Event[bool]) -> Optional[Callable]:
|
|
32
|
+
if not event.consume_event():
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
state_machine.events.api_requests.resume_mission.response.trigger_event(
|
|
36
|
+
ControlMissionResponse(success=True)
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return state_machine.mission_resumed # type:ignore
|
|
40
|
+
|
|
41
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
42
|
+
EventHandlerMapping(
|
|
43
|
+
name="failed_resume_event",
|
|
44
|
+
event=events.robot_service_events.mission_failed_to_resume,
|
|
45
|
+
handler=_failed_resume_event_handler,
|
|
46
|
+
),
|
|
47
|
+
EventHandlerMapping(
|
|
48
|
+
name="successful_resume_event",
|
|
49
|
+
event=events.robot_service_events.mission_successfully_resumed,
|
|
50
|
+
handler=_successful_resume_event_handler,
|
|
51
|
+
),
|
|
52
|
+
]
|
|
53
|
+
super().__init__(
|
|
54
|
+
state_name="resuming",
|
|
55
|
+
state_machine=state_machine,
|
|
56
|
+
event_handler_mappings=event_handlers,
|
|
57
|
+
)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Callable, List, Optional
|
|
2
|
+
|
|
3
|
+
from isar.apis.models.models import ControlMissionResponse
|
|
4
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
5
|
+
from isar.models.events import Event
|
|
6
|
+
from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from isar.state_machine.state_machine import StateMachine
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ResumingReturnHome(EventHandlerBase):
|
|
13
|
+
|
|
14
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
15
|
+
events = state_machine.events
|
|
16
|
+
|
|
17
|
+
def _failed_resume_event_handler(
|
|
18
|
+
event: Event[ErrorMessage],
|
|
19
|
+
) -> Optional[Callable]:
|
|
20
|
+
error_message: Optional[ErrorMessage] = event.consume_event()
|
|
21
|
+
|
|
22
|
+
if error_message is None:
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
state_machine.events.api_requests.resume_mission.response.trigger_event(
|
|
26
|
+
ControlMissionResponse(
|
|
27
|
+
success=False,
|
|
28
|
+
failure_reason=(
|
|
29
|
+
getattr(error_message, "error_reason", str(error_message))
|
|
30
|
+
if hasattr(error_message, "error_reason")
|
|
31
|
+
else str(error_message)
|
|
32
|
+
),
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return state_machine.return_home_mission_resuming_failed # type: ignore
|
|
37
|
+
|
|
38
|
+
def _successful_resume_event_handler(event: Event[bool]) -> Optional[Callable]:
|
|
39
|
+
if not event.consume_event():
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
state_machine.events.api_requests.resume_mission.response.trigger_event(
|
|
43
|
+
ControlMissionResponse(success=True)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return state_machine.return_home_mission_resumed # type: ignore
|
|
47
|
+
|
|
48
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
49
|
+
EventHandlerMapping(
|
|
50
|
+
name="failed_resume_event",
|
|
51
|
+
event=events.robot_service_events.mission_failed_to_resume,
|
|
52
|
+
handler=_failed_resume_event_handler,
|
|
53
|
+
),
|
|
54
|
+
EventHandlerMapping(
|
|
55
|
+
name="successful_resume_event",
|
|
56
|
+
event=events.robot_service_events.mission_successfully_resumed,
|
|
57
|
+
handler=_successful_resume_event_handler,
|
|
58
|
+
),
|
|
59
|
+
]
|
|
60
|
+
super().__init__(
|
|
61
|
+
state_name="resuming_return_home",
|
|
62
|
+
state_machine=state_machine,
|
|
63
|
+
event_handler_mappings=event_handlers,
|
|
64
|
+
)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Callable, List, Optional
|
|
2
|
+
|
|
3
|
+
from isar.apis.models.models import LockdownResponse, MissionStartResponse
|
|
4
|
+
from isar.config.settings import settings
|
|
5
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
6
|
+
from isar.models.events import Event
|
|
7
|
+
from robot_interface.models.mission.mission import Mission
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from isar.state_machine.state_machine import StateMachine
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ReturnHomePaused(EventHandlerBase):
|
|
14
|
+
|
|
15
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
16
|
+
events = state_machine.events
|
|
17
|
+
shared_state = state_machine.shared_state
|
|
18
|
+
|
|
19
|
+
def _robot_battery_level_updated_handler(
|
|
20
|
+
event: Event[float],
|
|
21
|
+
) -> Optional[Callable]:
|
|
22
|
+
battery_level: float = event.check()
|
|
23
|
+
|
|
24
|
+
if (
|
|
25
|
+
battery_level is None
|
|
26
|
+
or battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD
|
|
27
|
+
):
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
state_machine.events.state_machine_events.resume_mission.trigger_event(True)
|
|
31
|
+
return state_machine.resume # type: ignore
|
|
32
|
+
|
|
33
|
+
def _start_mission_event_handler(
|
|
34
|
+
event: Event[Mission],
|
|
35
|
+
) -> Optional[Callable]:
|
|
36
|
+
if not event.has_event():
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
if not state_machine.battery_level_is_above_mission_start_threshold():
|
|
40
|
+
state_machine.events.api_requests.start_mission.request.consume_event()
|
|
41
|
+
response = MissionStartResponse(
|
|
42
|
+
mission_id=None,
|
|
43
|
+
mission_started=False,
|
|
44
|
+
mission_not_started_reason="Robot battery too low",
|
|
45
|
+
)
|
|
46
|
+
state_machine.events.api_requests.start_mission.response.trigger_event(
|
|
47
|
+
response
|
|
48
|
+
)
|
|
49
|
+
return None
|
|
50
|
+
state_machine.events.state_machine_events.stop_mission.trigger_event(True)
|
|
51
|
+
return state_machine.stop_return_home # type: ignore
|
|
52
|
+
|
|
53
|
+
def _send_to_lockdown_event_handler(
|
|
54
|
+
event: Event[bool],
|
|
55
|
+
) -> Optional[Callable]:
|
|
56
|
+
should_lockdown: bool = event.consume_event()
|
|
57
|
+
if not should_lockdown:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
events.api_requests.send_to_lockdown.response.trigger_event(
|
|
61
|
+
LockdownResponse(lockdown_started=True)
|
|
62
|
+
)
|
|
63
|
+
state_machine.events.state_machine_events.resume_mission.trigger_event(True)
|
|
64
|
+
return state_machine.resume_lockdown # type: ignore
|
|
65
|
+
|
|
66
|
+
def _set_maintenance_mode_event_handler(event: Event[bool]):
|
|
67
|
+
should_set_maintenande_mode: bool = event.consume_event()
|
|
68
|
+
if should_set_maintenande_mode:
|
|
69
|
+
state_machine.logger.warning(
|
|
70
|
+
"Cancelling current mission due to robot going to maintenance mode"
|
|
71
|
+
)
|
|
72
|
+
state_machine.events.state_machine_events.stop_mission.trigger_event(
|
|
73
|
+
True
|
|
74
|
+
)
|
|
75
|
+
return state_machine.stop_due_to_maintenance # type: ignore
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
79
|
+
EventHandlerMapping(
|
|
80
|
+
name="resume_return_home_event",
|
|
81
|
+
event=events.api_requests.resume_mission.request,
|
|
82
|
+
handler=lambda event: state_machine.resume if event.consume_event() else None, # type: ignore
|
|
83
|
+
),
|
|
84
|
+
EventHandlerMapping(
|
|
85
|
+
name="robot_battery_update_event",
|
|
86
|
+
event=shared_state.robot_battery_level,
|
|
87
|
+
handler=_robot_battery_level_updated_handler,
|
|
88
|
+
),
|
|
89
|
+
EventHandlerMapping(
|
|
90
|
+
name="start_mission_event",
|
|
91
|
+
event=events.api_requests.start_mission.request,
|
|
92
|
+
handler=_start_mission_event_handler,
|
|
93
|
+
),
|
|
94
|
+
EventHandlerMapping(
|
|
95
|
+
name="send_to_lockdown_event",
|
|
96
|
+
event=events.api_requests.send_to_lockdown.request,
|
|
97
|
+
handler=_send_to_lockdown_event_handler,
|
|
98
|
+
),
|
|
99
|
+
EventHandlerMapping(
|
|
100
|
+
name="set_maintenance_mode",
|
|
101
|
+
event=events.api_requests.set_maintenance_mode.request,
|
|
102
|
+
handler=_set_maintenance_mode_event_handler,
|
|
103
|
+
),
|
|
104
|
+
]
|
|
105
|
+
super().__init__(
|
|
106
|
+
state_name="return_home_paused",
|
|
107
|
+
state_machine=state_machine,
|
|
108
|
+
event_handler_mappings=event_handlers,
|
|
109
|
+
)
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Callable, List, Optional
|
|
2
|
+
|
|
3
|
+
from isar.apis.models.models import (
|
|
4
|
+
ControlMissionResponse,
|
|
5
|
+
LockdownResponse,
|
|
6
|
+
MissionStartResponse,
|
|
7
|
+
)
|
|
8
|
+
from isar.config.settings import settings
|
|
9
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
10
|
+
from isar.models.events import Event
|
|
11
|
+
from isar.state_machine.utils.common_event_handlers import mission_started_event_handler
|
|
12
|
+
from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
|
|
13
|
+
from robot_interface.models.mission.mission import Mission
|
|
14
|
+
from robot_interface.models.mission.status import MissionStatus
|
|
15
|
+
from robot_interface.models.mission.task import ReturnToHome
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from isar.state_machine.state_machine import StateMachine
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ReturningHome(EventHandlerBase):
|
|
22
|
+
|
|
23
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
24
|
+
self.failed_return_home_attempts: int = 0
|
|
25
|
+
events = state_machine.events
|
|
26
|
+
shared_state = state_machine.shared_state
|
|
27
|
+
|
|
28
|
+
def _pause_mission_event_handler(event: Event[bool]) -> Optional[Callable]:
|
|
29
|
+
if not event.consume_event():
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
state_machine.events.api_requests.pause_mission.response.trigger_event(
|
|
33
|
+
ControlMissionResponse(success=True)
|
|
34
|
+
)
|
|
35
|
+
state_machine.events.state_machine_events.pause_mission.trigger_event(True)
|
|
36
|
+
return state_machine.pause_return_home # type: ignore
|
|
37
|
+
|
|
38
|
+
def _start_mission_event_handler(
|
|
39
|
+
event: Event[Mission],
|
|
40
|
+
) -> Optional[Callable]:
|
|
41
|
+
if not event.has_event():
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
if not state_machine.battery_level_is_above_mission_start_threshold():
|
|
45
|
+
state_machine.events.api_requests.start_mission.request.consume_event()
|
|
46
|
+
response = MissionStartResponse(
|
|
47
|
+
mission_id=None,
|
|
48
|
+
mission_started=False,
|
|
49
|
+
mission_not_started_reason="Robot battery too low",
|
|
50
|
+
)
|
|
51
|
+
state_machine.events.api_requests.start_mission.response.trigger_event(
|
|
52
|
+
response
|
|
53
|
+
)
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
state_machine.events.state_machine_events.stop_mission.trigger_event(True)
|
|
57
|
+
return state_machine.stop_return_home # type: ignore
|
|
58
|
+
|
|
59
|
+
def _mission_status_event_handler(
|
|
60
|
+
event: Event[MissionStatus],
|
|
61
|
+
) -> Optional[Callable]:
|
|
62
|
+
mission_status: Optional[MissionStatus] = event.consume_event()
|
|
63
|
+
|
|
64
|
+
if mission_status and mission_status not in [
|
|
65
|
+
MissionStatus.InProgress,
|
|
66
|
+
MissionStatus.NotStarted,
|
|
67
|
+
MissionStatus.Paused,
|
|
68
|
+
]:
|
|
69
|
+
if mission_status != MissionStatus.Successful:
|
|
70
|
+
self.failed_return_home_attempts += 1
|
|
71
|
+
if (
|
|
72
|
+
self.failed_return_home_attempts
|
|
73
|
+
>= settings.RETURN_HOME_RETRY_LIMIT
|
|
74
|
+
):
|
|
75
|
+
state_machine.logger.warning(
|
|
76
|
+
f"Failed to return home after {self.failed_return_home_attempts} attempts."
|
|
77
|
+
)
|
|
78
|
+
state_machine.publish_intervention_needed(
|
|
79
|
+
error_message=f"Return home failed after {self.failed_return_home_attempts} attempts."
|
|
80
|
+
)
|
|
81
|
+
state_machine.print_transitions()
|
|
82
|
+
return state_machine.return_home_failed # type: ignore
|
|
83
|
+
else:
|
|
84
|
+
state_machine.start_mission(
|
|
85
|
+
Mission(
|
|
86
|
+
tasks=[ReturnToHome()],
|
|
87
|
+
name="Return Home",
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
# This clears the current robot status value, so we don't read an outdated value
|
|
93
|
+
state_machine.events.robot_service_events.robot_status_changed.clear_event()
|
|
94
|
+
return state_machine.returned_home # type: ignore
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
def _send_to_lockdown_event_handler(
|
|
98
|
+
event: Event[bool],
|
|
99
|
+
) -> Optional[Callable]:
|
|
100
|
+
should_lockdown: bool = event.consume_event()
|
|
101
|
+
if not should_lockdown:
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
events.api_requests.send_to_lockdown.response.trigger_event(
|
|
105
|
+
LockdownResponse(lockdown_started=True)
|
|
106
|
+
)
|
|
107
|
+
return state_machine.go_to_lockdown # type: ignore
|
|
108
|
+
|
|
109
|
+
def _mission_failed_event_handler(
|
|
110
|
+
event: Event[Optional[ErrorMessage]],
|
|
111
|
+
) -> Optional[Callable]:
|
|
112
|
+
mission_failed: Optional[ErrorMessage] = event.consume_event()
|
|
113
|
+
if mission_failed is not None:
|
|
114
|
+
state_machine.logger.warning(
|
|
115
|
+
f"Failed to initiate return home because: "
|
|
116
|
+
f"{mission_failed.error_description}"
|
|
117
|
+
)
|
|
118
|
+
state_machine.publish_intervention_needed(
|
|
119
|
+
error_message="Return home failed to initiate."
|
|
120
|
+
)
|
|
121
|
+
state_machine.print_transitions()
|
|
122
|
+
return state_machine.return_home_failed # type: ignore
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
def _set_maintenance_mode_event_handler(event: Event[bool]):
|
|
126
|
+
should_set_maintenande_mode: bool = event.consume_event()
|
|
127
|
+
if should_set_maintenande_mode:
|
|
128
|
+
state_machine.logger.warning(
|
|
129
|
+
"Cancelling current mission due to robot going to maintenance mode"
|
|
130
|
+
)
|
|
131
|
+
state_machine.events.state_machine_events.stop_mission.trigger_event(
|
|
132
|
+
True
|
|
133
|
+
)
|
|
134
|
+
return state_machine.stop_due_to_maintenance # type: ignore
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
def _robot_already_home_event_handler(event: Event[bool]):
|
|
138
|
+
already_home: bool = event.consume_event()
|
|
139
|
+
if already_home:
|
|
140
|
+
state_machine.logger.info(
|
|
141
|
+
"Robot reported that it is already home. "
|
|
142
|
+
"Assuming return home mission successful without running."
|
|
143
|
+
)
|
|
144
|
+
return state_machine.returned_home # type: ignore
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
def _robot_battery_level_updated_handler(
|
|
148
|
+
event: Event[float],
|
|
149
|
+
) -> Optional[Callable]:
|
|
150
|
+
battery_level: float = event.check()
|
|
151
|
+
if (
|
|
152
|
+
battery_level is None
|
|
153
|
+
or battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD
|
|
154
|
+
):
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
return state_machine.go_to_recharging # type: ignore
|
|
158
|
+
|
|
159
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
160
|
+
EventHandlerMapping(
|
|
161
|
+
name="pause_mission_event",
|
|
162
|
+
event=events.api_requests.pause_mission.request,
|
|
163
|
+
handler=_pause_mission_event_handler,
|
|
164
|
+
),
|
|
165
|
+
EventHandlerMapping(
|
|
166
|
+
name="mission_started_event",
|
|
167
|
+
event=events.robot_service_events.mission_started,
|
|
168
|
+
handler=lambda event: mission_started_event_handler(
|
|
169
|
+
state_machine, event
|
|
170
|
+
),
|
|
171
|
+
),
|
|
172
|
+
EventHandlerMapping(
|
|
173
|
+
name="mission_failed_event",
|
|
174
|
+
event=events.robot_service_events.mission_failed,
|
|
175
|
+
handler=_mission_failed_event_handler,
|
|
176
|
+
),
|
|
177
|
+
EventHandlerMapping(
|
|
178
|
+
name="start_mission_event",
|
|
179
|
+
event=events.api_requests.start_mission.request,
|
|
180
|
+
handler=_start_mission_event_handler,
|
|
181
|
+
),
|
|
182
|
+
EventHandlerMapping(
|
|
183
|
+
name="mission_status_event",
|
|
184
|
+
event=events.robot_service_events.mission_status_updated,
|
|
185
|
+
handler=_mission_status_event_handler,
|
|
186
|
+
),
|
|
187
|
+
EventHandlerMapping(
|
|
188
|
+
name="robot_battery_update_event",
|
|
189
|
+
event=shared_state.robot_battery_level,
|
|
190
|
+
handler=_robot_battery_level_updated_handler,
|
|
191
|
+
),
|
|
192
|
+
EventHandlerMapping(
|
|
193
|
+
name="send_to_lockdown_event",
|
|
194
|
+
event=events.api_requests.send_to_lockdown.request,
|
|
195
|
+
handler=_send_to_lockdown_event_handler,
|
|
196
|
+
),
|
|
197
|
+
EventHandlerMapping(
|
|
198
|
+
name="set_maintenance_mode",
|
|
199
|
+
event=events.api_requests.set_maintenance_mode.request,
|
|
200
|
+
handler=_set_maintenance_mode_event_handler,
|
|
201
|
+
),
|
|
202
|
+
EventHandlerMapping(
|
|
203
|
+
name="robot_already_home",
|
|
204
|
+
event=events.robot_service_events.robot_already_home,
|
|
205
|
+
handler=_robot_already_home_event_handler,
|
|
206
|
+
),
|
|
207
|
+
]
|
|
208
|
+
super().__init__(
|
|
209
|
+
state_name="returning_home",
|
|
210
|
+
state_machine=state_machine,
|
|
211
|
+
event_handler_mappings=event_handlers,
|
|
212
|
+
on_transition=self._reset_failure_counter,
|
|
213
|
+
on_entry=self._reset_failure_counter,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
def _reset_failure_counter(self):
|
|
217
|
+
self.failed_return_home_attempts = 0
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Callable, List, Optional
|
|
2
|
+
|
|
3
|
+
from isar.apis.models.models import ControlMissionResponse
|
|
4
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
5
|
+
from isar.models.events import Event
|
|
6
|
+
from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from isar.state_machine.state_machine import StateMachine
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Stopping(EventHandlerBase):
|
|
13
|
+
|
|
14
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
15
|
+
events = state_machine.events
|
|
16
|
+
|
|
17
|
+
def _failed_stop_event_handler(
|
|
18
|
+
event: Event[ErrorMessage],
|
|
19
|
+
) -> Optional[Callable]:
|
|
20
|
+
error_message: Optional[ErrorMessage] = event.consume_event()
|
|
21
|
+
if error_message is None:
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
stopped_mission_response: ControlMissionResponse = ControlMissionResponse(
|
|
25
|
+
success=False, failure_reason="ISAR failed to stop mission"
|
|
26
|
+
)
|
|
27
|
+
state_machine.events.api_requests.stop_mission.response.trigger_event(
|
|
28
|
+
stopped_mission_response
|
|
29
|
+
)
|
|
30
|
+
return state_machine.mission_stopping_failed # type: ignore
|
|
31
|
+
|
|
32
|
+
def _successful_stop_event_handler(event: Event[bool]) -> Optional[Callable]:
|
|
33
|
+
if not event.consume_event():
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
state_machine.events.api_requests.stop_mission.response.trigger_event(
|
|
37
|
+
ControlMissionResponse(success=True)
|
|
38
|
+
)
|
|
39
|
+
state_machine.print_transitions()
|
|
40
|
+
if not state_machine.battery_level_is_above_mission_start_threshold():
|
|
41
|
+
state_machine.start_return_home_mission()
|
|
42
|
+
return state_machine.start_return_home_monitoring # type: ignore
|
|
43
|
+
return state_machine.mission_stopped # type: ignore
|
|
44
|
+
|
|
45
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
46
|
+
EventHandlerMapping(
|
|
47
|
+
name="failed_stop_event",
|
|
48
|
+
event=events.robot_service_events.mission_failed_to_stop,
|
|
49
|
+
handler=_failed_stop_event_handler,
|
|
50
|
+
),
|
|
51
|
+
EventHandlerMapping(
|
|
52
|
+
name="successful_stop_event",
|
|
53
|
+
event=events.robot_service_events.mission_successfully_stopped,
|
|
54
|
+
handler=_successful_stop_event_handler,
|
|
55
|
+
),
|
|
56
|
+
]
|
|
57
|
+
super().__init__(
|
|
58
|
+
state_name="stopping",
|
|
59
|
+
state_machine=state_machine,
|
|
60
|
+
event_handler_mappings=event_handlers,
|
|
61
|
+
)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Callable, List, Optional
|
|
2
|
+
|
|
3
|
+
from isar.apis.models.models import MaintenanceResponse
|
|
4
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
5
|
+
from isar.models.events import Event
|
|
6
|
+
from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from isar.state_machine.state_machine import StateMachine
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class StoppingDueToMaintenance(EventHandlerBase):
|
|
13
|
+
|
|
14
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
15
|
+
events = state_machine.events
|
|
16
|
+
|
|
17
|
+
def _failed_stop_event_handler(
|
|
18
|
+
event: Event[ErrorMessage],
|
|
19
|
+
) -> Optional[Callable]:
|
|
20
|
+
error_message: Optional[ErrorMessage] = event.consume_event()
|
|
21
|
+
if error_message is not None:
|
|
22
|
+
events.api_requests.set_maintenance_mode.response.trigger_event(
|
|
23
|
+
MaintenanceResponse(
|
|
24
|
+
is_maintenance_mode=False,
|
|
25
|
+
failure_reason="Failed to stop ongoing mission",
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
state_machine.logger.error(
|
|
29
|
+
f"Failed to stop mission in StoppingDueToMaintenance. Message: {error_message.error_description}"
|
|
30
|
+
)
|
|
31
|
+
return state_machine.mission_stopping_failed # type: ignore
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
def _successful_stop_event_handler(event: Event[bool]) -> Optional[Callable]:
|
|
35
|
+
if event.consume_event():
|
|
36
|
+
state_machine.publish_mission_aborted(
|
|
37
|
+
"Mission aborted, robot being sent to maintenance", True
|
|
38
|
+
)
|
|
39
|
+
events.api_requests.set_maintenance_mode.response.trigger_event(
|
|
40
|
+
MaintenanceResponse(is_maintenance_mode=True)
|
|
41
|
+
)
|
|
42
|
+
return state_machine.mission_stopped # type: ignore
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
46
|
+
EventHandlerMapping(
|
|
47
|
+
name="failed_stop_event",
|
|
48
|
+
event=events.robot_service_events.mission_failed_to_stop,
|
|
49
|
+
handler=_failed_stop_event_handler,
|
|
50
|
+
),
|
|
51
|
+
EventHandlerMapping(
|
|
52
|
+
name="successful_stop_event",
|
|
53
|
+
event=events.robot_service_events.mission_successfully_stopped,
|
|
54
|
+
handler=_successful_stop_event_handler,
|
|
55
|
+
),
|
|
56
|
+
]
|
|
57
|
+
super().__init__(
|
|
58
|
+
state_name="stopping_due_to_maintenance",
|
|
59
|
+
state_machine=state_machine,
|
|
60
|
+
event_handler_mappings=event_handlers,
|
|
61
|
+
)
|