isar 1.31.0__py3-none-any.whl → 1.31.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of isar might be problematic. Click here for more details.
- isar/config/open_telemetry.py +2 -8
- isar/eventhandlers/eventhandler.py +93 -0
- isar/models/events.py +118 -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 +2 -1
- isar/services/utilities/scheduling_utilities.py +26 -24
- isar/state_machine/state_machine.py +79 -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/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/transitions/functions/robot_status.py +4 -5
- isar/state_machine/transitions/functions/stop.py +3 -12
- isar/state_machine/utils/common_event_handlers.py +166 -0
- isar/storage/uploader.py +1 -1
- {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/METADATA +1 -1
- {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/RECORD +32 -43
- 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.31.1.dist-info}/WHEEL +0 -0
- {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/entry_points.txt +0 -0
- {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/licenses/LICENSE +0 -0
- {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/top_level.txt +0 -0
isar/config/open_telemetry.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
-
from azure.identity import ManagedIdentityCredential
|
|
4
3
|
from azure.monitor.opentelemetry.exporter import (
|
|
5
4
|
AzureMonitorLogExporter,
|
|
6
5
|
AzureMonitorTraceExporter,
|
|
@@ -57,12 +56,7 @@ def get_azure_monitor_exporters() -> (
|
|
|
57
56
|
Else use Azure Managed Identity to create Azure Monitor Exporters.
|
|
58
57
|
"""
|
|
59
58
|
connection_string = settings.APPLICATIONINSIGHTS_CONNECTION_STRING
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
log_exporter = AzureMonitorLogExporter(connection_string=connection_string)
|
|
63
|
-
else:
|
|
64
|
-
credential = ManagedIdentityCredential()
|
|
65
|
-
trace_exporter = AzureMonitorTraceExporter(credential=credential)
|
|
66
|
-
log_exporter = AzureMonitorLogExporter(credential=credential)
|
|
59
|
+
trace_exporter = AzureMonitorTraceExporter(connection_string=connection_string)
|
|
60
|
+
log_exporter = AzureMonitorLogExporter(connection_string=connection_string)
|
|
67
61
|
|
|
68
62
|
return trace_exporter, log_exporter
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from threading import Event as ThreadEvent
|
|
6
|
+
from typing import TYPE_CHECKING, Callable, Generic, List, Optional, TypeVar
|
|
7
|
+
|
|
8
|
+
from transitions import State
|
|
9
|
+
|
|
10
|
+
from isar.config.settings import settings
|
|
11
|
+
from isar.models.events import Event
|
|
12
|
+
|
|
13
|
+
T = TypeVar("T")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class EventHandlerMapping(Generic[T]):
|
|
18
|
+
name: str
|
|
19
|
+
event: Event[T]
|
|
20
|
+
handler: Callable[[Event[T]], Optional[Callable]]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class TimeoutHandlerMapping:
|
|
25
|
+
name: str
|
|
26
|
+
timeout_in_seconds: float
|
|
27
|
+
handler: Callable[[], Optional[Callable]]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from isar.state_machine.state_machine import StateMachine
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class EventHandlerBase(State):
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
state_machine: "StateMachine",
|
|
38
|
+
state_name: str,
|
|
39
|
+
event_handler_mappings: List[EventHandlerMapping],
|
|
40
|
+
timers: List[TimeoutHandlerMapping] = [],
|
|
41
|
+
) -> None:
|
|
42
|
+
|
|
43
|
+
super().__init__(name=state_name, on_enter=self.start)
|
|
44
|
+
self.state_machine: "StateMachine" = state_machine
|
|
45
|
+
self.logger = logging.getLogger("state_machine")
|
|
46
|
+
self.events = state_machine.events
|
|
47
|
+
self.signal_state_machine_to_stop: ThreadEvent = (
|
|
48
|
+
state_machine.signal_state_machine_to_stop
|
|
49
|
+
)
|
|
50
|
+
self.event_handler_mappings = event_handler_mappings
|
|
51
|
+
self.state_name: str = state_name
|
|
52
|
+
self.timers = timers
|
|
53
|
+
|
|
54
|
+
def start(self) -> None:
|
|
55
|
+
self.state_machine.update_state()
|
|
56
|
+
self._run()
|
|
57
|
+
|
|
58
|
+
def stop(self) -> None:
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
def _run(self) -> None:
|
|
62
|
+
should_exit_state: bool = False
|
|
63
|
+
timers = deepcopy(self.timers)
|
|
64
|
+
entered_time = time.time()
|
|
65
|
+
while True:
|
|
66
|
+
if self.signal_state_machine_to_stop.is_set():
|
|
67
|
+
self.logger.info(
|
|
68
|
+
"Stopping state machine from %s state", self.state_name
|
|
69
|
+
)
|
|
70
|
+
break
|
|
71
|
+
|
|
72
|
+
for timer in timers:
|
|
73
|
+
if time.time() - entered_time > timer.timeout_in_seconds:
|
|
74
|
+
transition_func = timer.handler()
|
|
75
|
+
timers.remove(timer)
|
|
76
|
+
if transition_func is not None:
|
|
77
|
+
transition_func()
|
|
78
|
+
should_exit_state = True
|
|
79
|
+
break
|
|
80
|
+
|
|
81
|
+
if should_exit_state:
|
|
82
|
+
break
|
|
83
|
+
|
|
84
|
+
for handler_mapping in self.event_handler_mappings:
|
|
85
|
+
transition_func = handler_mapping.handler(handler_mapping.event)
|
|
86
|
+
if transition_func is not None:
|
|
87
|
+
transition_func()
|
|
88
|
+
should_exit_state = True
|
|
89
|
+
break
|
|
90
|
+
|
|
91
|
+
if should_exit_state:
|
|
92
|
+
break
|
|
93
|
+
time.sleep(settings.FSM_SLEEP_TIME)
|
isar/models/events.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from collections import deque
|
|
2
|
+
from queue import Empty, Queue
|
|
3
|
+
from typing import Generic, Optional, TypeVar
|
|
4
|
+
|
|
5
|
+
from transitions import State
|
|
6
|
+
|
|
7
|
+
from isar.apis.models.models import ControlMissionResponse
|
|
8
|
+
from isar.config.settings import settings
|
|
9
|
+
from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
|
|
10
|
+
from robot_interface.models.mission.mission import Mission
|
|
11
|
+
from robot_interface.models.mission.status import RobotStatus, TaskStatus
|
|
12
|
+
from robot_interface.models.mission.task import TASKS
|
|
13
|
+
|
|
14
|
+
T = TypeVar("T")
|
|
15
|
+
T1 = TypeVar("T1")
|
|
16
|
+
T2 = TypeVar("T2")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Event(Queue[T]):
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
super().__init__(maxsize=1)
|
|
22
|
+
|
|
23
|
+
def trigger_event(self, data: T) -> None:
|
|
24
|
+
self.put(data)
|
|
25
|
+
|
|
26
|
+
def consume_event(self, timeout: int = None) -> Optional[T]:
|
|
27
|
+
try:
|
|
28
|
+
return self.get(block=timeout is not None, timeout=timeout)
|
|
29
|
+
except Empty:
|
|
30
|
+
if timeout is not None:
|
|
31
|
+
raise EventTimeoutError
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
def clear_event(self) -> None:
|
|
35
|
+
while True:
|
|
36
|
+
try:
|
|
37
|
+
self.get(block=False)
|
|
38
|
+
except Empty:
|
|
39
|
+
break
|
|
40
|
+
|
|
41
|
+
def has_event(self) -> bool:
|
|
42
|
+
return (
|
|
43
|
+
self.qsize() != 0
|
|
44
|
+
) # Queue size is not reliable, but should be sufficient for this case
|
|
45
|
+
|
|
46
|
+
def check(self) -> Optional[T]:
|
|
47
|
+
if not self._qsize():
|
|
48
|
+
return None
|
|
49
|
+
with self.mutex:
|
|
50
|
+
queueList = list(self.queue)
|
|
51
|
+
return queueList.pop()
|
|
52
|
+
|
|
53
|
+
def update(self, item: T):
|
|
54
|
+
with self.mutex:
|
|
55
|
+
self.queue: deque[T] = deque()
|
|
56
|
+
self.queue.append(item)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Events:
|
|
60
|
+
def __init__(self) -> None:
|
|
61
|
+
self.api_requests: APIRequests = APIRequests()
|
|
62
|
+
self.state_machine_events: StateMachineEvents = StateMachineEvents()
|
|
63
|
+
self.robot_service_events: RobotServiceEvents = RobotServiceEvents()
|
|
64
|
+
|
|
65
|
+
self.upload_queue: Queue = Queue(maxsize=10)
|
|
66
|
+
|
|
67
|
+
if settings.MQTT_ENABLED:
|
|
68
|
+
self.mqtt_queue: Queue = Queue()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class APIEvent(Generic[T1, T2]):
|
|
72
|
+
"""
|
|
73
|
+
Creates input and output event. The events are defined such that the input is from
|
|
74
|
+
api to state machine while the output is from state machine to api.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def __init__(self):
|
|
78
|
+
self.input: Event[T1] = Event()
|
|
79
|
+
self.output: Event[T2] = Event()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class APIRequests:
|
|
83
|
+
def __init__(self) -> None:
|
|
84
|
+
self.start_mission: APIEvent[Mission, bool] = APIEvent()
|
|
85
|
+
self.stop_mission: APIEvent[str, ControlMissionResponse] = APIEvent()
|
|
86
|
+
self.pause_mission: APIEvent[bool, ControlMissionResponse] = APIEvent()
|
|
87
|
+
self.resume_mission: APIEvent[bool, ControlMissionResponse] = APIEvent()
|
|
88
|
+
self.return_home: APIEvent[bool, bool] = APIEvent()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class StateMachineEvents:
|
|
92
|
+
def __init__(self) -> None:
|
|
93
|
+
self.start_mission: Event[Mission] = Event()
|
|
94
|
+
self.stop_mission: Event[bool] = Event()
|
|
95
|
+
self.pause_mission: Event[bool] = Event()
|
|
96
|
+
self.task_status_request: Event[str] = Event()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class RobotServiceEvents:
|
|
100
|
+
def __init__(self) -> None:
|
|
101
|
+
self.task_status_updated: Event[TaskStatus] = Event()
|
|
102
|
+
self.task_status_failed: Event[ErrorMessage] = Event()
|
|
103
|
+
self.mission_started: Event[bool] = Event()
|
|
104
|
+
self.mission_failed: Event[ErrorMessage] = Event()
|
|
105
|
+
self.robot_status_changed: Event[bool] = Event()
|
|
106
|
+
self.mission_failed_to_stop: Event[ErrorMessage] = Event()
|
|
107
|
+
self.mission_successfully_stopped: Event[bool] = Event()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class SharedState:
|
|
111
|
+
def __init__(self) -> None:
|
|
112
|
+
self.state: Event[State] = Event()
|
|
113
|
+
self.robot_status: Event[RobotStatus] = Event()
|
|
114
|
+
self.state_machine_current_task: Event[TASKS] = Event()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class EventTimeoutError(Exception):
|
|
118
|
+
pass
|
isar/modules.py
CHANGED
|
@@ -12,7 +12,7 @@ from isar.config.settings import settings
|
|
|
12
12
|
from isar.mission_planner.local_planner import LocalPlanner
|
|
13
13
|
from isar.mission_planner.sequential_task_selector import SequentialTaskSelector
|
|
14
14
|
from isar.mission_planner.task_selector_interface import TaskSelectorInterface
|
|
15
|
-
from isar.models.
|
|
15
|
+
from isar.models.events import Events, SharedState
|
|
16
16
|
from isar.robot.robot import Robot
|
|
17
17
|
from isar.services.utilities.robot_utilities import RobotUtilities
|
|
18
18
|
from isar.services.utilities.scheduling_utilities import SchedulingUtilities
|
isar/robot/robot.py
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from
|
|
3
|
-
from threading import Event
|
|
2
|
+
from threading import Event as ThreadEvent
|
|
4
3
|
from typing import Optional
|
|
5
4
|
|
|
6
|
-
from isar.models.
|
|
5
|
+
from isar.models.events import (
|
|
6
|
+
Event,
|
|
7
7
|
Events,
|
|
8
8
|
RobotServiceEvents,
|
|
9
9
|
SharedState,
|
|
10
10
|
StateMachineEvents,
|
|
11
11
|
)
|
|
12
|
-
from isar.models.communication.queues.queue_utils import check_for_event, trigger_event
|
|
13
12
|
from isar.robot.robot_start_mission import RobotStartMissionThread
|
|
14
13
|
from isar.robot.robot_status import RobotStatusThread
|
|
15
14
|
from isar.robot.robot_stop_mission import RobotStopMissionThread
|
|
16
15
|
from isar.robot.robot_task_status import RobotTaskStatusThread
|
|
17
16
|
from robot_interface.models.exceptions.robot_exceptions import ErrorMessage, ErrorReason
|
|
17
|
+
from robot_interface.models.mission.mission import Mission
|
|
18
18
|
from robot_interface.robot_interface import RobotInterface
|
|
19
19
|
|
|
20
20
|
|
|
@@ -31,7 +31,7 @@ class Robot(object):
|
|
|
31
31
|
self.robot_status_thread: Optional[RobotStatusThread] = None
|
|
32
32
|
self.robot_task_status_thread: Optional[RobotTaskStatusThread] = None
|
|
33
33
|
self.stop_mission_thread: Optional[RobotStopMissionThread] = None
|
|
34
|
-
self.signal_thread_quitting:
|
|
34
|
+
self.signal_thread_quitting: ThreadEvent = ThreadEvent()
|
|
35
35
|
|
|
36
36
|
def stop(self) -> None:
|
|
37
37
|
self.signal_thread_quitting.set()
|
|
@@ -53,8 +53,8 @@ class Robot(object):
|
|
|
53
53
|
self.robot_task_status_thread = None
|
|
54
54
|
self.start_mission_thread = None
|
|
55
55
|
|
|
56
|
-
def
|
|
57
|
-
start_mission =
|
|
56
|
+
def _start_mission_event_handler(self, event: Event[Mission]) -> None:
|
|
57
|
+
start_mission = event.consume_event()
|
|
58
58
|
if start_mission is not None:
|
|
59
59
|
if (
|
|
60
60
|
self.start_mission_thread is not None
|
|
@@ -72,8 +72,8 @@ class Robot(object):
|
|
|
72
72
|
)
|
|
73
73
|
self.start_mission_thread.start()
|
|
74
74
|
|
|
75
|
-
def
|
|
76
|
-
task_id: str =
|
|
75
|
+
def _task_status_request_handler(self, event: Event[str]) -> None:
|
|
76
|
+
task_id: str = event.consume_event()
|
|
77
77
|
if task_id:
|
|
78
78
|
self.robot_task_status_thread = RobotTaskStatusThread(
|
|
79
79
|
self.robot_service_events,
|
|
@@ -83,8 +83,8 @@ class Robot(object):
|
|
|
83
83
|
)
|
|
84
84
|
self.robot_task_status_thread.start()
|
|
85
85
|
|
|
86
|
-
def
|
|
87
|
-
if
|
|
86
|
+
def _stop_mission_request_handler(self, event: Event[bool]) -> None:
|
|
87
|
+
if event.consume_event():
|
|
88
88
|
if (
|
|
89
89
|
self.stop_mission_thread is not None
|
|
90
90
|
and self.stop_mission_thread.is_alive()
|
|
@@ -102,8 +102,8 @@ class Robot(object):
|
|
|
102
102
|
error_reason=ErrorReason.RobotStillStartingMissionException,
|
|
103
103
|
error_description=error_description,
|
|
104
104
|
)
|
|
105
|
-
trigger_event(
|
|
106
|
-
|
|
105
|
+
self.robot_service_events.mission_failed_to_stop.trigger_event(
|
|
106
|
+
error_message
|
|
107
107
|
)
|
|
108
108
|
return
|
|
109
109
|
self.stop_mission_thread = RobotStopMissionThread(
|
|
@@ -118,14 +118,12 @@ class Robot(object):
|
|
|
118
118
|
self.robot_status_thread.start()
|
|
119
119
|
|
|
120
120
|
while not self.signal_thread_quitting.wait(0):
|
|
121
|
-
self.
|
|
122
|
-
self.state_machine_events.start_mission
|
|
123
|
-
)
|
|
121
|
+
self._start_mission_event_handler(self.state_machine_events.start_mission)
|
|
124
122
|
|
|
125
|
-
self.
|
|
123
|
+
self._task_status_request_handler(
|
|
126
124
|
self.state_machine_events.task_status_request
|
|
127
125
|
)
|
|
128
126
|
|
|
129
|
-
self.
|
|
127
|
+
self._stop_mission_request_handler(self.state_machine_events.stop_mission)
|
|
130
128
|
|
|
131
129
|
self.logger.info("Exiting robot service main thread")
|
|
@@ -2,11 +2,7 @@ import logging
|
|
|
2
2
|
from threading import Event, Thread
|
|
3
3
|
|
|
4
4
|
from isar.config.settings import settings
|
|
5
|
-
from isar.models.
|
|
6
|
-
from isar.models.communication.queues.queue_utils import (
|
|
7
|
-
trigger_event,
|
|
8
|
-
trigger_event_without_data,
|
|
9
|
-
)
|
|
5
|
+
from isar.models.events import RobotServiceEvents
|
|
10
6
|
from robot_interface.models.exceptions.robot_exceptions import (
|
|
11
7
|
ErrorMessage,
|
|
12
8
|
RobotException,
|
|
@@ -44,13 +40,13 @@ class RobotStartMissionThread(Thread):
|
|
|
44
40
|
self.logger.error(
|
|
45
41
|
f"Mission is infeasible and cannot be scheduled because: {e.error_description}"
|
|
46
42
|
)
|
|
47
|
-
trigger_event(
|
|
48
|
-
self.robot_service_events.mission_failed,
|
|
43
|
+
self.robot_service_events.mission_failed.trigger_event(
|
|
49
44
|
ErrorMessage(
|
|
50
45
|
error_reason=e.error_reason,
|
|
51
46
|
error_description=e.error_description,
|
|
52
|
-
)
|
|
47
|
+
)
|
|
53
48
|
)
|
|
49
|
+
|
|
54
50
|
break
|
|
55
51
|
except RobotException as e:
|
|
56
52
|
retries += 1
|
|
@@ -66,12 +62,11 @@ class RobotStartMissionThread(Thread):
|
|
|
66
62
|
f"{e.error_description}"
|
|
67
63
|
)
|
|
68
64
|
|
|
69
|
-
trigger_event(
|
|
70
|
-
self.robot_service_events.mission_failed,
|
|
65
|
+
self.robot_service_events.mission_failed.trigger_event(
|
|
71
66
|
ErrorMessage(
|
|
72
67
|
error_reason=e.error_reason,
|
|
73
68
|
error_description=e.error_description,
|
|
74
|
-
)
|
|
69
|
+
)
|
|
75
70
|
)
|
|
76
71
|
break
|
|
77
72
|
|
|
@@ -79,12 +74,11 @@ class RobotStartMissionThread(Thread):
|
|
|
79
74
|
|
|
80
75
|
started_mission = True
|
|
81
76
|
except RobotInfeasibleMissionException as e:
|
|
82
|
-
trigger_event(
|
|
83
|
-
self.robot_service_events.mission_failed,
|
|
77
|
+
self.robot_service_events.mission_failed.trigger_event(
|
|
84
78
|
ErrorMessage(
|
|
85
79
|
error_reason=e.error_reason, error_description=e.error_description
|
|
86
80
|
),
|
|
87
81
|
)
|
|
88
82
|
|
|
89
83
|
if started_mission:
|
|
90
|
-
|
|
84
|
+
self.robot_service_events.mission_started.trigger_event(True)
|
isar/robot/robot_status.py
CHANGED
|
@@ -3,8 +3,7 @@ import time
|
|
|
3
3
|
from threading import Event, Thread
|
|
4
4
|
|
|
5
5
|
from isar.config.settings import settings
|
|
6
|
-
from isar.models.
|
|
7
|
-
from isar.models.communication.queues.queue_utils import update_shared_state
|
|
6
|
+
from isar.models.events import SharedState
|
|
8
7
|
from robot_interface.models.exceptions.robot_exceptions import RobotException
|
|
9
8
|
from robot_interface.robot_interface import RobotInterface
|
|
10
9
|
|
|
@@ -49,7 +48,7 @@ class RobotStatusThread(Thread):
|
|
|
49
48
|
self.last_robot_status_poll_time = time.time()
|
|
50
49
|
|
|
51
50
|
robot_status = self.robot.robot_status()
|
|
52
|
-
|
|
51
|
+
self.shared_state.robot_status.update(robot_status)
|
|
53
52
|
except RobotException as e:
|
|
54
53
|
self.logger.error(f"Failed to retrieve robot status: {e}")
|
|
55
54
|
continue
|
isar/robot/robot_stop_mission.py
CHANGED
|
@@ -4,11 +4,7 @@ from threading import Event, Thread
|
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
6
|
from isar.config.settings import settings
|
|
7
|
-
from isar.models.
|
|
8
|
-
from isar.models.communication.queues.queue_utils import (
|
|
9
|
-
trigger_event,
|
|
10
|
-
trigger_event_without_data,
|
|
11
|
-
)
|
|
7
|
+
from isar.models.events import RobotServiceEvents
|
|
12
8
|
from robot_interface.models.exceptions.robot_exceptions import (
|
|
13
9
|
ErrorMessage,
|
|
14
10
|
RobotActionException,
|
|
@@ -51,9 +47,7 @@ class RobotStopMissionThread(Thread):
|
|
|
51
47
|
time.sleep(settings.FSM_SLEEP_TIME)
|
|
52
48
|
continue
|
|
53
49
|
|
|
54
|
-
|
|
55
|
-
self.robot_service_events.mission_successfully_stopped
|
|
56
|
-
)
|
|
50
|
+
self.robot_service_events.mission_successfully_stopped.trigger_event(True)
|
|
57
51
|
return
|
|
58
52
|
|
|
59
53
|
error_description = (
|
|
@@ -68,4 +62,4 @@ class RobotStopMissionThread(Thread):
|
|
|
68
62
|
error_description=error_description,
|
|
69
63
|
)
|
|
70
64
|
|
|
71
|
-
|
|
65
|
+
self.robot_service_events.mission_failed_to_stop.trigger_event(error_message)
|
isar/robot/robot_task_status.py
CHANGED
|
@@ -4,8 +4,7 @@ from threading import Event, Thread
|
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
6
|
from isar.config.settings import settings
|
|
7
|
-
from isar.models.
|
|
8
|
-
from isar.models.communication.queues.queue_utils import trigger_event
|
|
7
|
+
from isar.models.events import RobotServiceEvents
|
|
9
8
|
from isar.services.utilities.threaded_request import ThreadedRequest
|
|
10
9
|
from robot_interface.models.exceptions.robot_exceptions import (
|
|
11
10
|
ErrorMessage,
|
|
@@ -82,10 +81,7 @@ class RobotTaskStatusThread(Thread):
|
|
|
82
81
|
)
|
|
83
82
|
break
|
|
84
83
|
|
|
85
|
-
|
|
84
|
+
self.robot_service_events.task_status_updated.trigger_event(task_status)
|
|
86
85
|
return
|
|
87
86
|
|
|
88
|
-
trigger_event(
|
|
89
|
-
self.robot_service_events.task_status_failed,
|
|
90
|
-
failed_task_error,
|
|
91
|
-
)
|
|
87
|
+
self.robot_service_events.task_status_failed.trigger_event(failed_task_error)
|
isar/script.py
CHANGED
|
@@ -10,7 +10,7 @@ from isar.apis.api import API
|
|
|
10
10
|
from isar.config.log import setup_loggers
|
|
11
11
|
from isar.config.open_telemetry import setup_open_telemetry
|
|
12
12
|
from isar.config.settings import robot_settings, settings
|
|
13
|
-
from isar.models.
|
|
13
|
+
from isar.models.events import Events
|
|
14
14
|
from isar.modules import ApplicationContainer, get_injector
|
|
15
15
|
from isar.robot.robot import Robot
|
|
16
16
|
from isar.services.service_connections.mqtt.mqtt_client import MqttClient
|
|
@@ -69,6 +69,7 @@ def print_startup_info():
|
|
|
69
69
|
print_setting("Mission planner", settings.MISSION_PLANNER)
|
|
70
70
|
print_setting("Using local storage", settings.STORAGE_LOCAL_ENABLED)
|
|
71
71
|
print_setting("Using blob storage", settings.STORAGE_BLOB_ENABLED)
|
|
72
|
+
print_setting("Blob storage account", settings.BLOB_STORAGE_ACCOUNT)
|
|
72
73
|
print_setting("Using async inspection uploading", settings.UPLOAD_INSPECTIONS_ASYNC)
|
|
73
74
|
print_setting("Plant code", settings.PLANT_CODE)
|
|
74
75
|
print_setting("Plant name", settings.PLANT_NAME)
|
|
@@ -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,12 @@ class SchedulingUtilities:
|
|
|
286
290
|
self.logger.info("OK - Mission successfully stopped")
|
|
287
291
|
return stop_mission_response
|
|
288
292
|
|
|
289
|
-
def _send_command(self, input:
|
|
290
|
-
|
|
293
|
+
def _send_command(self, input: T1, api_event: APIEvent[T1, T2]) -> T2:
|
|
294
|
+
api_event.input.trigger_event(input)
|
|
291
295
|
try:
|
|
292
|
-
return
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
)
|
|
296
|
-
except QueueTimeoutError as e:
|
|
297
|
-
QueueUtilities.clear_queue(queueio.input)
|
|
296
|
+
return api_event.output.consume_event(timeout=self.queue_timeout)
|
|
297
|
+
except EventTimeoutError as e:
|
|
298
|
+
self.logger.error("Queue timed out")
|
|
299
|
+
api_event.input.clear_event()
|
|
298
300
|
self.logger.error("No output received for command to state machine")
|
|
299
301
|
raise e
|