isar 1.34.9__py3-none-any.whl → 1.34.13__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/apis/api.py +0 -23
- isar/apis/models/start_mission_definition.py +6 -3
- isar/apis/schedule/scheduling_controller.py +3 -29
- isar/config/keyvault/keyvault_service.py +3 -1
- isar/config/settings.py +0 -8
- isar/models/status.py +4 -0
- isar/modules.py +0 -2
- isar/robot/robot.py +8 -2
- isar/script.py +1 -1
- isar/services/utilities/scheduling_utilities.py +0 -42
- isar/state_machine/state_machine.py +24 -2
- isar/state_machine/states/maintenance.py +8 -1
- isar/state_machine/states/stopping.py +8 -0
- isar/state_machine/states/stopping_paused_mission.py +36 -0
- isar/state_machine/states/stopping_paused_return_home.py +59 -0
- isar/state_machine/states/stopping_return_home.py +24 -42
- isar/state_machine/states/unknown_status.py +2 -0
- isar/state_machine/states_enum.py +2 -0
- isar/state_machine/transitions/mission.py +37 -4
- isar/state_machine/transitions/return_home.py +2 -0
- isar/state_machine/transitions/robot_status.py +7 -0
- isar/state_machine/utils/common_event_handlers.py +65 -0
- {isar-1.34.9.dist-info → isar-1.34.13.dist-info}/METADATA +49 -16
- {isar-1.34.9.dist-info → isar-1.34.13.dist-info}/RECORD +29 -44
- robot_interface/telemetry/payloads.py +1 -1
- isar/config/configuration_error.py +0 -2
- isar/config/keyvault/keyvault_error.py +0 -2
- isar/config/predefined_mission_definition/__init__.py +0 -0
- isar/config/predefined_mission_definition/default_exr.json +0 -49
- isar/config/predefined_mission_definition/default_mission.json +0 -87
- isar/config/predefined_mission_definition/default_turtlebot.json +0 -117
- isar/config/predefined_missions/__init__.py +0 -0
- isar/config/predefined_missions/default.json +0 -72
- isar/config/predefined_missions/default_extra_capabilities.json +0 -107
- isar/mission_planner/__init__.py +0 -0
- isar/mission_planner/local_planner.py +0 -68
- isar/mission_planner/mission_planner_interface.py +0 -26
- isar/services/auth/__init__.py +0 -0
- isar/services/auth/azure_credentials.py +0 -14
- isar/services/service_connections/request_handler.py +0 -153
- isar/services/utilities/threaded_request.py +0 -68
- robot_interface/models/initialize/__init__.py +0 -0
- {isar-1.34.9.dist-info → isar-1.34.13.dist-info}/WHEEL +0 -0
- {isar-1.34.9.dist-info → isar-1.34.13.dist-info}/entry_points.txt +0 -0
- {isar-1.34.9.dist-info → isar-1.34.13.dist-info}/licenses/LICENSE +0 -0
- {isar-1.34.9.dist-info → isar-1.34.13.dist-info}/top_level.txt +0 -0
isar/apis/api.py
CHANGED
|
@@ -112,29 +112,6 @@ class API:
|
|
|
112
112
|
|
|
113
113
|
authentication_dependency: Security = Security(self.authenticator.get_scheme())
|
|
114
114
|
|
|
115
|
-
router.add_api_route(
|
|
116
|
-
path="/schedule/start-mission/{id}",
|
|
117
|
-
endpoint=self.scheduling_controller.start_mission_by_id,
|
|
118
|
-
methods=["POST"],
|
|
119
|
-
deprecated=True,
|
|
120
|
-
dependencies=[authentication_dependency],
|
|
121
|
-
summary="Start a mission with id='id' from the current mission planner",
|
|
122
|
-
responses={
|
|
123
|
-
HTTPStatus.OK.value: {
|
|
124
|
-
"description": "Mission succesfully started",
|
|
125
|
-
"model": StartMissionResponse,
|
|
126
|
-
},
|
|
127
|
-
HTTPStatus.NOT_FOUND.value: {
|
|
128
|
-
"description": "Not found - Mission not found",
|
|
129
|
-
},
|
|
130
|
-
HTTPStatus.CONFLICT.value: {
|
|
131
|
-
"description": "Conflict - Invalid command in the current state",
|
|
132
|
-
},
|
|
133
|
-
HTTPStatus.INTERNAL_SERVER_ERROR.value: {
|
|
134
|
-
"description": "Internal Server Error - Current state of state machine unknown",
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
)
|
|
138
115
|
router.add_api_route(
|
|
139
116
|
path="/schedule/start-mission",
|
|
140
117
|
endpoint=self.scheduling_controller.start_mission,
|
|
@@ -6,7 +6,6 @@ from pydantic import BaseModel, Field
|
|
|
6
6
|
|
|
7
7
|
from isar.apis.models.models import InputPose, InputPosition
|
|
8
8
|
from isar.config.settings import settings
|
|
9
|
-
from isar.mission_planner.mission_planner_interface import MissionPlannerError
|
|
10
9
|
from robot_interface.models.mission.mission import Mission
|
|
11
10
|
from robot_interface.models.mission.task import (
|
|
12
11
|
TASKS,
|
|
@@ -63,6 +62,10 @@ class StopMissionDefinition(BaseModel):
|
|
|
63
62
|
mission_id: Optional[str] = None
|
|
64
63
|
|
|
65
64
|
|
|
65
|
+
class MissionFormatError(Exception):
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
|
|
66
69
|
def to_isar_mission(
|
|
67
70
|
start_mission_definition: StartMissionDefinition,
|
|
68
71
|
) -> Mission:
|
|
@@ -73,7 +76,7 @@ def to_isar_mission(
|
|
|
73
76
|
isar_tasks.append(task)
|
|
74
77
|
|
|
75
78
|
if not isar_tasks:
|
|
76
|
-
raise
|
|
79
|
+
raise MissionFormatError("Mission does not contain any valid tasks")
|
|
77
80
|
|
|
78
81
|
isar_mission_name: str = (
|
|
79
82
|
start_mission_definition.name
|
|
@@ -101,7 +104,7 @@ def to_isar_task(task_definition: StartMissionTaskDefinition) -> TASKS:
|
|
|
101
104
|
elif task_definition.type == TaskType.ReturnToHome:
|
|
102
105
|
return ReturnToHome()
|
|
103
106
|
else:
|
|
104
|
-
raise
|
|
107
|
+
raise MissionFormatError(
|
|
105
108
|
f"Failed to create task: '{task_definition.type}' is not a valid"
|
|
106
109
|
)
|
|
107
110
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from http import HTTPStatus
|
|
3
3
|
|
|
4
|
-
from fastapi import Body, HTTPException
|
|
4
|
+
from fastapi import Body, HTTPException
|
|
5
5
|
from opentelemetry import trace
|
|
6
6
|
|
|
7
7
|
from isar.apis.models.models import (
|
|
@@ -10,12 +10,12 @@ from isar.apis.models.models import (
|
|
|
10
10
|
TaskResponse,
|
|
11
11
|
)
|
|
12
12
|
from isar.apis.models.start_mission_definition import (
|
|
13
|
+
MissionFormatError,
|
|
13
14
|
StartMissionDefinition,
|
|
14
15
|
StopMissionDefinition,
|
|
15
16
|
to_isar_mission,
|
|
16
17
|
)
|
|
17
18
|
from isar.config.settings import robot_settings
|
|
18
|
-
from isar.mission_planner.mission_planner_interface import MissionPlannerError
|
|
19
19
|
from isar.services.utilities.scheduling_utilities import SchedulingUtilities
|
|
20
20
|
from isar.state_machine.states_enum import States
|
|
21
21
|
from robot_interface.models.mission.mission import Mission
|
|
@@ -32,32 +32,6 @@ class SchedulingController:
|
|
|
32
32
|
self.scheduling_utilities: SchedulingUtilities = scheduling_utilities
|
|
33
33
|
self.logger = logging.getLogger("api")
|
|
34
34
|
|
|
35
|
-
@tracer.start_as_current_span("start_mission_by_id")
|
|
36
|
-
def start_mission_by_id(
|
|
37
|
-
self,
|
|
38
|
-
mission_id: str = Path(
|
|
39
|
-
alias="id",
|
|
40
|
-
title="Mission ID",
|
|
41
|
-
description="ID-number for predefined mission",
|
|
42
|
-
),
|
|
43
|
-
) -> StartMissionResponse:
|
|
44
|
-
self.logger.info("Received request to start mission with id %s", mission_id)
|
|
45
|
-
|
|
46
|
-
state: States = self.scheduling_utilities.get_state()
|
|
47
|
-
self.scheduling_utilities.verify_state_machine_ready_to_receive_mission(state)
|
|
48
|
-
|
|
49
|
-
mission: Mission = self.scheduling_utilities.get_mission(mission_id)
|
|
50
|
-
|
|
51
|
-
self.scheduling_utilities.verify_robot_capable_of_mission(
|
|
52
|
-
mission=mission, robot_capabilities=robot_settings.CAPABILITIES
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
self.logger.info("Starting mission with ISAR Mission ID: '%s'", mission.id)
|
|
56
|
-
|
|
57
|
-
self.scheduling_utilities.start_mission(mission=mission)
|
|
58
|
-
|
|
59
|
-
return self._api_response(mission)
|
|
60
|
-
|
|
61
35
|
@tracer.start_as_current_span("start_mission")
|
|
62
36
|
def start_mission(
|
|
63
37
|
self,
|
|
@@ -87,7 +61,7 @@ class SchedulingController:
|
|
|
87
61
|
mission: Mission = to_isar_mission(
|
|
88
62
|
start_mission_definition=mission_definition
|
|
89
63
|
)
|
|
90
|
-
except
|
|
64
|
+
except MissionFormatError as e:
|
|
91
65
|
error_message = f"Bad Request - Cannot create ISAR mission: {e}"
|
|
92
66
|
self.logger.warning(error_message)
|
|
93
67
|
raise HTTPException(
|
|
@@ -9,7 +9,9 @@ from azure.core.exceptions import (
|
|
|
9
9
|
from azure.identity import ClientSecretCredential, DefaultAzureCredential
|
|
10
10
|
from azure.keyvault.secrets import KeyVaultSecret, SecretClient
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
class KeyvaultError(Exception):
|
|
14
|
+
pass
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
class Keyvault:
|
isar/config/settings.py
CHANGED
|
@@ -6,7 +6,6 @@ from dotenv import load_dotenv
|
|
|
6
6
|
from pydantic import Field, ValidationInfo, field_validator
|
|
7
7
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
8
8
|
|
|
9
|
-
from isar.config import predefined_missions
|
|
10
9
|
from robot_interface.models.robots.robot_model import RobotModel
|
|
11
10
|
from robot_interface.telemetry.payloads import DocumentInfo
|
|
12
11
|
|
|
@@ -36,10 +35,6 @@ class Settings(BaseSettings):
|
|
|
36
35
|
# The sleep is used to throttle the system on every iteration in the loop
|
|
37
36
|
FSM_SLEEP_TIME: float = Field(default=0.1)
|
|
38
37
|
|
|
39
|
-
# Location of JSON files containing predefined missions for the Local Planner to use
|
|
40
|
-
path: str = os.path.dirname(predefined_missions.__file__)
|
|
41
|
-
PREDEFINED_MISSIONS_FOLDER: str = Field(default=path + "/")
|
|
42
|
-
|
|
43
38
|
# Name of default map transformation
|
|
44
39
|
DEFAULT_MAP: str = Field(default="default_map")
|
|
45
40
|
|
|
@@ -93,9 +88,6 @@ class Settings(BaseSettings):
|
|
|
93
88
|
# Sets how many times the robot should try to return home if a return home fails
|
|
94
89
|
RETURN_HOME_RETRY_LIMIT: int = Field(default=5)
|
|
95
90
|
|
|
96
|
-
# Determines which mission planner module is used by ISAR
|
|
97
|
-
MISSION_PLANNER: str = Field(default="local")
|
|
98
|
-
|
|
99
91
|
# Determines which storage modules are used by ISAR
|
|
100
92
|
# Multiple storage modules can be chosen
|
|
101
93
|
# Each module will be called when storing results from inspections
|
isar/models/status.py
CHANGED
|
@@ -16,3 +16,7 @@ class IsarStatus(Enum):
|
|
|
16
16
|
GoingToLockdown = "goingtolockdown"
|
|
17
17
|
GoingToRecharging = "goingtorecharging"
|
|
18
18
|
Maintenance = "maintenance"
|
|
19
|
+
Pausing = "pausing"
|
|
20
|
+
PausingReturnHome = "pausingreturnhome"
|
|
21
|
+
Stopping = "stopping"
|
|
22
|
+
StoppingReturnHome = "stoppingreturnhome"
|
isar/modules.py
CHANGED
|
@@ -9,7 +9,6 @@ from isar.apis.schedule.scheduling_controller import SchedulingController
|
|
|
9
9
|
from isar.apis.security.authentication import Authenticator
|
|
10
10
|
from isar.config.keyvault.keyvault_service import Keyvault
|
|
11
11
|
from isar.config.settings import settings
|
|
12
|
-
from isar.mission_planner.local_planner import LocalPlanner
|
|
13
12
|
from isar.models.events import Events, SharedState
|
|
14
13
|
from isar.robot.robot import Robot
|
|
15
14
|
from isar.services.utilities.robot_utilities import RobotUtilities
|
|
@@ -59,7 +58,6 @@ class ApplicationContainer(containers.DeclarativeContainer):
|
|
|
59
58
|
SchedulingUtilities,
|
|
60
59
|
events=events,
|
|
61
60
|
shared_state=shared_state,
|
|
62
|
-
mission_planner=providers.Singleton(LocalPlanner),
|
|
63
61
|
)
|
|
64
62
|
scheduling_controller = providers.Singleton(
|
|
65
63
|
SchedulingController, scheduling_utilities=scheduling_utilities
|
isar/robot/robot.py
CHANGED
|
@@ -142,17 +142,23 @@ class Robot(object):
|
|
|
142
142
|
):
|
|
143
143
|
self.stop_mission_thread.join()
|
|
144
144
|
error_message = self.stop_mission_thread.error_message
|
|
145
|
-
self.stop_mission_thread = None
|
|
146
145
|
|
|
147
146
|
if error_message:
|
|
148
147
|
self.robot_service_events.mission_failed_to_stop.trigger_event(
|
|
149
148
|
error_message
|
|
150
149
|
)
|
|
150
|
+
self.stop_mission_thread = None
|
|
151
151
|
else:
|
|
152
|
-
self.signal_mission_stopped.set()
|
|
153
152
|
if self.monitor_mission_thread is not None:
|
|
153
|
+
if self.monitor_mission_thread.is_alive():
|
|
154
|
+
self.signal_mission_stopped.set()
|
|
155
|
+
return
|
|
154
156
|
self.monitor_mission_thread.join()
|
|
155
157
|
self.monitor_mission_thread = None
|
|
158
|
+
|
|
159
|
+
self.stop_mission_thread = None
|
|
160
|
+
# The mission status will already be reported on MQTT, the state machine does not need the event
|
|
161
|
+
self.robot_service_events.mission_status_updated.clear_event()
|
|
156
162
|
self.robot_service_events.mission_successfully_stopped.trigger_event(
|
|
157
163
|
True
|
|
158
164
|
)
|
isar/script.py
CHANGED
|
@@ -58,10 +58,10 @@ def print_startup_info():
|
|
|
58
58
|
)
|
|
59
59
|
|
|
60
60
|
print_setting("ISAR settings")
|
|
61
|
+
print_setting("ISAR ID", settings.ISAR_ID)
|
|
61
62
|
print_setting("Robot package", settings.ROBOT_PACKAGE)
|
|
62
63
|
print_setting("Robot name", settings.ROBOT_NAME)
|
|
63
64
|
print_setting("Running on port", settings.API_PORT)
|
|
64
|
-
print_setting("Mission planner", settings.MISSION_PLANNER)
|
|
65
65
|
print_setting("Using local storage", settings.STORAGE_LOCAL_ENABLED)
|
|
66
66
|
print_setting("Using blob storage", settings.STORAGE_BLOB_ENABLED)
|
|
67
67
|
print_setting("Blob storage account", settings.BLOB_STORAGE_ACCOUNT)
|
|
@@ -4,15 +4,9 @@ from http import HTTPStatus
|
|
|
4
4
|
from typing import List, TypeVar
|
|
5
5
|
|
|
6
6
|
from fastapi import HTTPException
|
|
7
|
-
from requests import HTTPError
|
|
8
7
|
|
|
9
8
|
from isar.apis.models.models import ControlMissionResponse, MaintenanceResponse
|
|
10
9
|
from isar.config.settings import settings
|
|
11
|
-
from isar.mission_planner.mission_planner_interface import (
|
|
12
|
-
MissionNotFoundError,
|
|
13
|
-
MissionPlannerError,
|
|
14
|
-
MissionPlannerInterface,
|
|
15
|
-
)
|
|
16
10
|
from isar.models.events import (
|
|
17
11
|
APIEvent,
|
|
18
12
|
APIRequests,
|
|
@@ -41,12 +35,10 @@ class SchedulingUtilities:
|
|
|
41
35
|
self,
|
|
42
36
|
events: Events,
|
|
43
37
|
shared_state: SharedState,
|
|
44
|
-
mission_planner: MissionPlannerInterface,
|
|
45
38
|
queue_timeout: int = settings.QUEUE_TIMEOUT,
|
|
46
39
|
):
|
|
47
40
|
self.api_events: APIRequests = events.api_requests
|
|
48
41
|
self.shared_state: SharedState = shared_state
|
|
49
|
-
self.mission_planner: MissionPlannerInterface = mission_planner
|
|
50
42
|
self.queue_timeout: int = queue_timeout
|
|
51
43
|
self.logger = logging.getLogger("api")
|
|
52
44
|
|
|
@@ -69,40 +61,6 @@ class SchedulingUtilities:
|
|
|
69
61
|
)
|
|
70
62
|
return current_state
|
|
71
63
|
|
|
72
|
-
def get_mission(self, mission_id: str) -> Mission:
|
|
73
|
-
"""Get the mission with mission_id from the current mission planner
|
|
74
|
-
|
|
75
|
-
Raises
|
|
76
|
-
------
|
|
77
|
-
HTTPException 404 Not Found
|
|
78
|
-
If requested mission with mission_id is not found
|
|
79
|
-
HTTPException 500 Internal Server Error
|
|
80
|
-
If for some reason the mission can not be returned
|
|
81
|
-
"""
|
|
82
|
-
try:
|
|
83
|
-
return self.mission_planner.get_mission(mission_id)
|
|
84
|
-
except HTTPError as e:
|
|
85
|
-
self.logger.error(e)
|
|
86
|
-
raise HTTPException(status_code=e.response.status_code)
|
|
87
|
-
except MissionNotFoundError as e:
|
|
88
|
-
self.logger.error(e)
|
|
89
|
-
raise HTTPException(
|
|
90
|
-
status_code=HTTPStatus.NOT_FOUND,
|
|
91
|
-
detail=f"Mission with id '{mission_id}' not found",
|
|
92
|
-
)
|
|
93
|
-
except MissionPlannerError as e:
|
|
94
|
-
self.logger.error(e)
|
|
95
|
-
raise HTTPException(
|
|
96
|
-
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
97
|
-
detail="Could not plan mission",
|
|
98
|
-
)
|
|
99
|
-
except Exception as e:
|
|
100
|
-
self.logger.error(e)
|
|
101
|
-
raise HTTPException(
|
|
102
|
-
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
103
|
-
detail="Could not return mission",
|
|
104
|
-
)
|
|
105
|
-
|
|
106
64
|
def verify_robot_capable_of_mission(
|
|
107
65
|
self, mission: Mission, robot_capabilities: List[str]
|
|
108
66
|
) -> bool:
|
|
@@ -41,6 +41,10 @@ from isar.state_machine.states.stopping_due_to_maintenance import (
|
|
|
41
41
|
)
|
|
42
42
|
from isar.state_machine.states.stopping_go_to_lockdown import StoppingGoToLockdown
|
|
43
43
|
from isar.state_machine.states.stopping_go_to_recharge import StoppingGoToRecharge
|
|
44
|
+
from isar.state_machine.states.stopping_paused_mission import StoppingPausedMission
|
|
45
|
+
from isar.state_machine.states.stopping_paused_return_home import (
|
|
46
|
+
StoppingPausedReturnHome,
|
|
47
|
+
)
|
|
44
48
|
from isar.state_machine.states.stopping_return_home import StoppingReturnHome
|
|
45
49
|
from isar.state_machine.states.unknown_status import UnknownStatus
|
|
46
50
|
from isar.state_machine.states_enum import States
|
|
@@ -106,6 +110,8 @@ class StateMachine(object):
|
|
|
106
110
|
self.going_to_lockdown_state: State = GoingToLockdown(self)
|
|
107
111
|
self.going_to_recharging_state: State = GoingToRecharging(self)
|
|
108
112
|
self.stopping_due_to_maintenance_state: State = StoppingDueToMaintenance(self)
|
|
113
|
+
self.stopping_paused_mission_state: State = StoppingPausedMission(self)
|
|
114
|
+
self.stopping_paused_return_home_state: State = StoppingPausedReturnHome(self)
|
|
109
115
|
|
|
110
116
|
# States Waiting for mission
|
|
111
117
|
self.await_next_mission_state: State = AwaitNextMission(self)
|
|
@@ -147,6 +153,8 @@ class StateMachine(object):
|
|
|
147
153
|
self.stopping_go_to_recharge_state,
|
|
148
154
|
self.stopping_due_to_maintenance_state,
|
|
149
155
|
self.maintenance_state,
|
|
156
|
+
self.stopping_paused_mission_state,
|
|
157
|
+
self.stopping_paused_return_home_state,
|
|
150
158
|
]
|
|
151
159
|
|
|
152
160
|
if settings.PERSISTENT_STORAGE_CONNECTION_STRING == "":
|
|
@@ -237,9 +245,8 @@ class StateMachine(object):
|
|
|
237
245
|
|
|
238
246
|
if self.shared_state.mission_id.check() is None:
|
|
239
247
|
self.logger.warning(
|
|
240
|
-
"
|
|
248
|
+
"Publishing mission aborted message with no ongoing mission."
|
|
241
249
|
)
|
|
242
|
-
return
|
|
243
250
|
|
|
244
251
|
payload: MissionAbortedPayload = MissionAbortedPayload(
|
|
245
252
|
isar_id=settings.ISAR_ID,
|
|
@@ -309,6 +316,21 @@ class StateMachine(object):
|
|
|
309
316
|
return IsarStatus.GoingToRecharging
|
|
310
317
|
elif self.current_state == States.Maintenance:
|
|
311
318
|
return IsarStatus.Maintenance
|
|
319
|
+
elif self.current_state == States.Pausing:
|
|
320
|
+
return IsarStatus.Pausing
|
|
321
|
+
elif self.current_state == States.PausingReturnHome:
|
|
322
|
+
return IsarStatus.PausingReturnHome
|
|
323
|
+
elif self.current_state in [
|
|
324
|
+
States.Stopping,
|
|
325
|
+
States.StoppingDueToMaintenance,
|
|
326
|
+
States.StoppingGoToLockdown,
|
|
327
|
+
States.StoppingGoToRecharge,
|
|
328
|
+
States.StoppingPausedMission,
|
|
329
|
+
States.StoppingPausedReturnHome,
|
|
330
|
+
]:
|
|
331
|
+
return IsarStatus.Stopping
|
|
332
|
+
elif self.current_state == States.StoppingReturnHome:
|
|
333
|
+
return IsarStatus.StoppingReturnHome
|
|
312
334
|
else:
|
|
313
335
|
return IsarStatus.Busy
|
|
314
336
|
|
|
@@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, List
|
|
|
2
2
|
|
|
3
3
|
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
4
4
|
from isar.models.events import Event
|
|
5
|
+
from robot_interface.models.mission.status import RobotStatus
|
|
5
6
|
|
|
6
7
|
if TYPE_CHECKING:
|
|
7
8
|
from isar.state_machine.state_machine import StateMachine
|
|
@@ -18,7 +19,13 @@ class Maintenance(EventHandlerBase):
|
|
|
18
19
|
events.api_requests.release_from_maintenance_mode.response.trigger_event(
|
|
19
20
|
True
|
|
20
21
|
)
|
|
21
|
-
|
|
22
|
+
|
|
23
|
+
robot_status = state_machine.shared_state.robot_status.check()
|
|
24
|
+
if robot_status == RobotStatus.Home:
|
|
25
|
+
return state_machine.goto_home # type: ignore
|
|
26
|
+
else:
|
|
27
|
+
return state_machine.goto_intervention_needed # type: ignore
|
|
28
|
+
|
|
22
29
|
return None
|
|
23
30
|
|
|
24
31
|
event_handlers: List[EventHandlerMapping] = [
|
|
@@ -36,6 +36,14 @@ class Stopping(EventHandlerBase):
|
|
|
36
36
|
state_machine.events.api_requests.stop_mission.response.trigger_event(
|
|
37
37
|
ControlMissionResponse(success=True)
|
|
38
38
|
)
|
|
39
|
+
|
|
40
|
+
if state_machine.shared_state.mission_id.check() is None:
|
|
41
|
+
reason: str = (
|
|
42
|
+
"Robot was busy and mission stopped but no ongoing mission found in shared state."
|
|
43
|
+
)
|
|
44
|
+
state_machine.logger.warning(reason)
|
|
45
|
+
state_machine.publish_mission_aborted(reason, False)
|
|
46
|
+
|
|
39
47
|
state_machine.print_transitions()
|
|
40
48
|
if not state_machine.battery_level_is_above_mission_start_threshold():
|
|
41
49
|
state_machine.start_return_home_mission()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, List
|
|
2
|
+
|
|
3
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
4
|
+
from isar.state_machine.utils.common_event_handlers import (
|
|
5
|
+
failed_stop_event_handler,
|
|
6
|
+
successful_stop_event_handler,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from isar.state_machine.state_machine import StateMachine
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class StoppingPausedMission(EventHandlerBase):
|
|
14
|
+
|
|
15
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
16
|
+
events = state_machine.events
|
|
17
|
+
|
|
18
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
19
|
+
EventHandlerMapping(
|
|
20
|
+
name="failed_stop_event",
|
|
21
|
+
event=events.robot_service_events.mission_failed_to_stop,
|
|
22
|
+
handler=lambda event: failed_stop_event_handler(state_machine, event),
|
|
23
|
+
),
|
|
24
|
+
EventHandlerMapping(
|
|
25
|
+
name="successful_stop_event",
|
|
26
|
+
event=events.robot_service_events.mission_successfully_stopped,
|
|
27
|
+
handler=lambda event: successful_stop_event_handler(
|
|
28
|
+
state_machine, event
|
|
29
|
+
),
|
|
30
|
+
),
|
|
31
|
+
]
|
|
32
|
+
super().__init__(
|
|
33
|
+
state_name="stopping_paused_mission",
|
|
34
|
+
state_machine=state_machine,
|
|
35
|
+
event_handler_mappings=event_handlers,
|
|
36
|
+
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
2
|
+
|
|
3
|
+
from isar.apis.models.models import MissionStartResponse
|
|
4
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
5
|
+
from isar.state_machine.utils.common_event_handlers import (
|
|
6
|
+
failed_stop_return_home_event_handler,
|
|
7
|
+
successful_stop_return_home_event_handler,
|
|
8
|
+
)
|
|
9
|
+
from robot_interface.models.mission.mission import Mission
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from isar.state_machine.state_machine import StateMachine
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class StoppingPausedReturnHome(EventHandlerBase):
|
|
16
|
+
|
|
17
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
18
|
+
events = state_machine.events
|
|
19
|
+
self.mission: Optional[Mission] = None
|
|
20
|
+
|
|
21
|
+
def _respond_to_start_mission_request():
|
|
22
|
+
self.mission = (
|
|
23
|
+
state_machine.events.api_requests.start_mission.request.consume_event()
|
|
24
|
+
)
|
|
25
|
+
if not self.mission:
|
|
26
|
+
state_machine.logger.error(
|
|
27
|
+
"Reached stopping paused return home without a mission request"
|
|
28
|
+
)
|
|
29
|
+
else:
|
|
30
|
+
response = MissionStartResponse(
|
|
31
|
+
mission_id=self.mission.id,
|
|
32
|
+
mission_started=True,
|
|
33
|
+
)
|
|
34
|
+
state_machine.events.api_requests.start_mission.response.trigger_event(
|
|
35
|
+
response
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
39
|
+
EventHandlerMapping(
|
|
40
|
+
name="failed_stop_event",
|
|
41
|
+
event=events.robot_service_events.mission_failed_to_stop,
|
|
42
|
+
handler=lambda event: failed_stop_return_home_event_handler(
|
|
43
|
+
state_machine, event
|
|
44
|
+
),
|
|
45
|
+
),
|
|
46
|
+
EventHandlerMapping(
|
|
47
|
+
name="successful_stop_event",
|
|
48
|
+
event=events.robot_service_events.mission_successfully_stopped,
|
|
49
|
+
handler=lambda event: successful_stop_return_home_event_handler(
|
|
50
|
+
state_machine, event, self.mission
|
|
51
|
+
),
|
|
52
|
+
),
|
|
53
|
+
]
|
|
54
|
+
super().__init__(
|
|
55
|
+
state_name="stopping_paused_return_home",
|
|
56
|
+
state_machine=state_machine,
|
|
57
|
+
event_handler_mappings=event_handlers,
|
|
58
|
+
on_entry=_respond_to_start_mission_request,
|
|
59
|
+
)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
from typing import TYPE_CHECKING, Callable, List, Optional
|
|
1
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
3
2
|
|
|
4
3
|
from isar.apis.models.models import MissionStartResponse
|
|
5
4
|
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
6
|
-
from isar.
|
|
7
|
-
|
|
5
|
+
from isar.state_machine.utils.common_event_handlers import (
|
|
6
|
+
failed_stop_return_home_event_handler,
|
|
7
|
+
successful_stop_return_home_event_handler,
|
|
8
|
+
)
|
|
8
9
|
from robot_interface.models.mission.mission import Mission
|
|
9
10
|
|
|
10
11
|
if TYPE_CHECKING:
|
|
@@ -14,64 +15,45 @@ if TYPE_CHECKING:
|
|
|
14
15
|
class StoppingReturnHome(EventHandlerBase):
|
|
15
16
|
|
|
16
17
|
def __init__(self, state_machine: "StateMachine"):
|
|
17
|
-
logger = logging.getLogger("state_machine")
|
|
18
18
|
events = state_machine.events
|
|
19
|
+
self.mission: Optional[Mission] = None
|
|
19
20
|
|
|
20
|
-
def
|
|
21
|
-
|
|
22
|
-
) -> Optional[Callable]:
|
|
23
|
-
error_message: Optional[ErrorMessage] = event.consume_event()
|
|
24
|
-
if error_message is None:
|
|
25
|
-
return None
|
|
26
|
-
|
|
27
|
-
logger.warning(error_message.error_description)
|
|
28
|
-
mission: Mission = (
|
|
21
|
+
def _respond_to_start_mission_request():
|
|
22
|
+
self.mission = (
|
|
29
23
|
state_machine.events.api_requests.start_mission.request.consume_event()
|
|
30
24
|
)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
if not self.mission:
|
|
26
|
+
state_machine.logger.error(
|
|
27
|
+
"Reached stopping return home without a mission request"
|
|
28
|
+
)
|
|
29
|
+
else:
|
|
30
|
+
response = MissionStartResponse(
|
|
31
|
+
mission_id=self.mission.id,
|
|
32
|
+
mission_started=True,
|
|
36
33
|
)
|
|
37
|
-
)
|
|
38
|
-
return state_machine.return_home_mission_stopping_failed # type: ignore
|
|
39
|
-
|
|
40
|
-
def _successful_stop_event_handler(event: Event[bool]) -> Optional[Callable]:
|
|
41
|
-
if not event.consume_event():
|
|
42
|
-
return None
|
|
43
|
-
|
|
44
|
-
mission: Mission = (
|
|
45
|
-
state_machine.events.api_requests.start_mission.request.consume_event()
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
if mission:
|
|
49
|
-
state_machine.start_mission(mission=mission)
|
|
50
34
|
state_machine.events.api_requests.start_mission.response.trigger_event(
|
|
51
|
-
|
|
35
|
+
response
|
|
52
36
|
)
|
|
53
|
-
return state_machine.start_mission_monitoring # type: ignore
|
|
54
|
-
|
|
55
|
-
state_machine.logger.error(
|
|
56
|
-
"Stopped return home without a new mission to start"
|
|
57
|
-
)
|
|
58
|
-
state_machine.start_return_home_mission()
|
|
59
|
-
return state_machine.start_return_home_monitoring # type: ignore
|
|
60
37
|
|
|
61
38
|
event_handlers: List[EventHandlerMapping] = [
|
|
62
39
|
EventHandlerMapping(
|
|
63
40
|
name="failed_stop_event",
|
|
64
41
|
event=events.robot_service_events.mission_failed_to_stop,
|
|
65
|
-
handler=
|
|
42
|
+
handler=lambda event: failed_stop_return_home_event_handler(
|
|
43
|
+
state_machine, event
|
|
44
|
+
),
|
|
66
45
|
),
|
|
67
46
|
EventHandlerMapping(
|
|
68
47
|
name="successful_stop_event",
|
|
69
48
|
event=events.robot_service_events.mission_successfully_stopped,
|
|
70
|
-
handler=
|
|
49
|
+
handler=lambda event: successful_stop_return_home_event_handler(
|
|
50
|
+
state_machine, event, self.mission
|
|
51
|
+
),
|
|
71
52
|
),
|
|
72
53
|
]
|
|
73
54
|
super().__init__(
|
|
74
55
|
state_name="stopping_return_home",
|
|
75
56
|
state_machine=state_machine,
|
|
76
57
|
event_handler_mappings=event_handlers,
|
|
58
|
+
on_entry=_respond_to_start_mission_request,
|
|
77
59
|
)
|
|
@@ -41,6 +41,8 @@ class UnknownStatus(EventHandlerBase):
|
|
|
41
41
|
return state_machine.robot_status_offline # type: ignore
|
|
42
42
|
elif robot_status == RobotStatus.BlockedProtectiveStop:
|
|
43
43
|
return state_machine.robot_status_blocked_protective_stop # type: ignore
|
|
44
|
+
elif robot_status == RobotStatus.Busy:
|
|
45
|
+
return state_machine.robot_status_busy # type: ignore
|
|
44
46
|
return None
|
|
45
47
|
|
|
46
48
|
def _reset_status_check():
|
|
@@ -26,6 +26,8 @@ class States(str, Enum):
|
|
|
26
26
|
StoppingGoToRecharge = "stopping_go_to_recharge"
|
|
27
27
|
Maintenance = "maintenance"
|
|
28
28
|
StoppingDueToMaintenance = "stopping_due_to_maintenance"
|
|
29
|
+
StoppingPausedMission = "stopping_paused_mission"
|
|
30
|
+
StoppingPausedReturnHome = "stopping_paused_return_home"
|
|
29
31
|
|
|
30
32
|
def __repr__(self):
|
|
31
33
|
return self.value
|