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
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
from queue import Queue
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class QueueIO:
|
|
5
|
-
"""
|
|
6
|
-
Creates input and output queue. The queues are defined such that the input is from
|
|
7
|
-
api to state machine while the output is from state machine to api.
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
def __init__(self, input_size: int = 0, output_size: int = 0):
|
|
11
|
-
self.input: Queue = Queue(maxsize=input_size)
|
|
12
|
-
self.output: Queue = Queue(maxsize=output_size)
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
from queue import Queue
|
|
2
|
-
|
|
3
|
-
from isar.config.settings import settings
|
|
4
|
-
from isar.models.communication.queues.queue_io import QueueIO
|
|
5
|
-
from isar.models.communication.queues.status_queue import StatusQueue
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Queues:
|
|
9
|
-
def __init__(self) -> None:
|
|
10
|
-
self.start_mission: QueueIO = QueueIO(input_size=1, output_size=1)
|
|
11
|
-
self.stop_mission: QueueIO = QueueIO(input_size=1, output_size=1)
|
|
12
|
-
self.pause_mission: QueueIO = QueueIO(input_size=1, output_size=1)
|
|
13
|
-
self.resume_mission: QueueIO = QueueIO(input_size=1, output_size=1)
|
|
14
|
-
self.single_action: QueueIO = QueueIO(input_size=1, output_size=1)
|
|
15
|
-
self.upload_queue: Queue = Queue(maxsize=10)
|
|
16
|
-
self.state: StatusQueue = StatusQueue()
|
|
17
|
-
|
|
18
|
-
if settings.MQTT_ENABLED:
|
|
19
|
-
self.mqtt_queue: Queue = Queue()
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
from collections import deque
|
|
2
|
-
from queue import Empty, Queue
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class StatusQueue(Queue):
|
|
7
|
-
def __init__(self) -> None:
|
|
8
|
-
super().__init__()
|
|
9
|
-
|
|
10
|
-
def check(self) -> Any:
|
|
11
|
-
if not self._qsize():
|
|
12
|
-
raise Empty
|
|
13
|
-
with self.mutex:
|
|
14
|
-
l = list(self.queue)
|
|
15
|
-
return l.pop()
|
|
16
|
-
|
|
17
|
-
def update(self, item: Any):
|
|
18
|
-
with self.mutex:
|
|
19
|
-
self.queue = deque()
|
|
20
|
-
self.queue.append(item)
|
|
File without changes
|
|
File without changes
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import logging
|
|
3
|
-
from dataclasses import is_dataclass
|
|
4
|
-
from logging import Logger
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Any, Optional
|
|
7
|
-
|
|
8
|
-
from dacite import Config, from_dict
|
|
9
|
-
|
|
10
|
-
logger: Logger = logging.getLogger("api")
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class BaseReader:
|
|
14
|
-
@staticmethod
|
|
15
|
-
def read_json(location: Path) -> dict:
|
|
16
|
-
with open(location) as json_file:
|
|
17
|
-
return json.load(json_file)
|
|
18
|
-
|
|
19
|
-
@staticmethod
|
|
20
|
-
def dict_to_dataclass(
|
|
21
|
-
dataclass_dict: dict,
|
|
22
|
-
target_dataclass: Any,
|
|
23
|
-
cast_config: list = [],
|
|
24
|
-
strict_config: bool = False,
|
|
25
|
-
) -> Optional[Any]:
|
|
26
|
-
if not is_dataclass(target_dataclass):
|
|
27
|
-
raise BaseReaderError("{target_dataclass} is not a dataclass")
|
|
28
|
-
generated_dataclass = from_dict(
|
|
29
|
-
data_class=target_dataclass,
|
|
30
|
-
data=dataclass_dict,
|
|
31
|
-
config=Config(cast=cast_config, strict=strict_config),
|
|
32
|
-
)
|
|
33
|
-
return generated_dataclass
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class BaseReaderError(Exception):
|
|
37
|
-
pass
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import logging
|
|
3
|
-
import time
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from logging import Logger
|
|
6
|
-
from queue import Queue
|
|
7
|
-
from threading import Thread
|
|
8
|
-
|
|
9
|
-
from isar.config.settings import settings
|
|
10
|
-
from isar.state_machine.state_machine import StateMachine
|
|
11
|
-
from isar.state_machine.states_enum import States
|
|
12
|
-
from robot_interface.models.exceptions import RobotCommunicationException
|
|
13
|
-
from robot_interface.models.mission.status import RobotStatus
|
|
14
|
-
from robot_interface.robot_interface import RobotInterface
|
|
15
|
-
from robot_interface.telemetry.mqtt_client import MqttPublisher
|
|
16
|
-
from robot_interface.telemetry.payloads import RobotStatusPayload
|
|
17
|
-
from robot_interface.utilities.json_service import EnhancedJSONEncoder
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class RobotStatusPublisher:
|
|
21
|
-
def __init__(
|
|
22
|
-
self, mqtt_queue: Queue, robot: RobotInterface, state_machine: StateMachine
|
|
23
|
-
):
|
|
24
|
-
self.mqtt_publisher: MqttPublisher = MqttPublisher(mqtt_queue=mqtt_queue)
|
|
25
|
-
self.robot: RobotInterface = robot
|
|
26
|
-
self.state_machine: StateMachine = state_machine
|
|
27
|
-
|
|
28
|
-
def _get_combined_robot_status(
|
|
29
|
-
self, robot_status: RobotStatus, current_state: States
|
|
30
|
-
) -> RobotStatus:
|
|
31
|
-
if robot_status == RobotStatus.Offline:
|
|
32
|
-
return RobotStatus.Offline
|
|
33
|
-
elif current_state == States.Idle and robot_status == RobotStatus.Available:
|
|
34
|
-
return RobotStatus.Available
|
|
35
|
-
elif current_state != States.Idle or robot_status == RobotStatus.Busy:
|
|
36
|
-
return RobotStatus.Busy
|
|
37
|
-
return None
|
|
38
|
-
|
|
39
|
-
def run(self) -> None:
|
|
40
|
-
robot_status_monitor: RobotStatusMonitor = RobotStatusMonitor(robot=self.robot)
|
|
41
|
-
robot_status_thread: Thread = Thread(
|
|
42
|
-
target=robot_status_monitor.run,
|
|
43
|
-
name="Robot Status Monitor",
|
|
44
|
-
daemon=True,
|
|
45
|
-
)
|
|
46
|
-
robot_status_thread.start()
|
|
47
|
-
|
|
48
|
-
while True:
|
|
49
|
-
combined_status: RobotStatus = self._get_combined_robot_status(
|
|
50
|
-
robot_status=robot_status_monitor.robot_status,
|
|
51
|
-
current_state=self.state_machine.current_state,
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
payload: RobotStatusPayload = RobotStatusPayload(
|
|
55
|
-
isar_id=settings.ISAR_ID,
|
|
56
|
-
robot_name=settings.ROBOT_NAME,
|
|
57
|
-
robot_status=combined_status,
|
|
58
|
-
current_isar_state=self.state_machine.current_state,
|
|
59
|
-
current_mission_id=self.state_machine.current_mission.id
|
|
60
|
-
if self.state_machine.current_mission
|
|
61
|
-
else None,
|
|
62
|
-
current_task_id=self.state_machine.current_task.id
|
|
63
|
-
if self.state_machine.current_task
|
|
64
|
-
else None,
|
|
65
|
-
current_step_id=self.state_machine.current_step.id
|
|
66
|
-
if self.state_machine.current_step
|
|
67
|
-
else None,
|
|
68
|
-
timestamp=datetime.utcnow(),
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
self.mqtt_publisher.publish(
|
|
72
|
-
topic=settings.TOPIC_ISAR_ROBOT_STATUS,
|
|
73
|
-
payload=json.dumps(payload, cls=EnhancedJSONEncoder),
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
time.sleep(settings.ROBOT_STATUS_PUBLISH_INTERVAL)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
class RobotStatusMonitor:
|
|
80
|
-
def __init__(self, robot: RobotInterface):
|
|
81
|
-
self.robot: RobotInterface = robot
|
|
82
|
-
self.robot_status: RobotStatus = RobotStatus.Offline
|
|
83
|
-
self.logger: Logger = logging.getLogger("robot_status_monitor")
|
|
84
|
-
|
|
85
|
-
def run(self) -> None:
|
|
86
|
-
while True:
|
|
87
|
-
try:
|
|
88
|
-
self.robot_status = self.robot.robot_status()
|
|
89
|
-
except RobotCommunicationException:
|
|
90
|
-
self.logger.warning(
|
|
91
|
-
"Failed to get robot status due to a communication exception"
|
|
92
|
-
)
|
|
93
|
-
time.sleep(settings.ROBOT_API_STATUS_POLL_INTERVAL)
|
|
File without changes
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from typing import Optional
|
|
3
|
-
|
|
4
|
-
from alitra import Frame, Position
|
|
5
|
-
from azure.identity import DefaultAzureCredential
|
|
6
|
-
from injector import inject
|
|
7
|
-
from requests import Response
|
|
8
|
-
|
|
9
|
-
from isar.config.settings import settings
|
|
10
|
-
from isar.services.auth.azure_credentials import AzureCredentials
|
|
11
|
-
from isar.services.service_connections.request_handler import RequestHandler
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class StidService:
|
|
15
|
-
@inject
|
|
16
|
-
def __init__(self, request_handler: RequestHandler):
|
|
17
|
-
self.request_handler: RequestHandler = request_handler
|
|
18
|
-
self.credentials: DefaultAzureCredential = (
|
|
19
|
-
AzureCredentials.get_azure_credentials()
|
|
20
|
-
)
|
|
21
|
-
self.logger = logging.getLogger("api")
|
|
22
|
-
|
|
23
|
-
def tag_position(self, tag: str) -> Optional[Position]:
|
|
24
|
-
client_id: str = settings.STID_CLIENT_ID
|
|
25
|
-
scope: str = settings.STID_APP_SCOPE
|
|
26
|
-
request_scope: str = f"{client_id}/{scope}"
|
|
27
|
-
|
|
28
|
-
token: str = self.credentials.get_token(request_scope).token
|
|
29
|
-
|
|
30
|
-
stid_url: str = settings.STID_API_URL
|
|
31
|
-
plant_name: str = settings.STID_PLANT_NAME
|
|
32
|
-
request_url: str = f"{stid_url}/{plant_name}/tag"
|
|
33
|
-
|
|
34
|
-
response: Response = self.request_handler.get(
|
|
35
|
-
url=request_url,
|
|
36
|
-
params={"tagNo": tag},
|
|
37
|
-
headers={"Authorization": f"Bearer {token}"},
|
|
38
|
-
)
|
|
39
|
-
tag_metadata: dict = response.json()
|
|
40
|
-
|
|
41
|
-
x_coord: float = tag_metadata["xCoordinate"] / 1000
|
|
42
|
-
y_coord: float = tag_metadata["yCoordinate"] / 1000
|
|
43
|
-
z_coord: float = tag_metadata["zCoordinate"] / 1000
|
|
44
|
-
|
|
45
|
-
return Position(x=x_coord, y=y_coord, z=z_coord, frame=Frame("asset"))
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from queue import Empty, Queue
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
from isar.models.communication.queues.queue_timeout_error import QueueTimeoutError
|
|
6
|
-
|
|
7
|
-
logger = logging.getLogger("api")
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class QueueUtilities:
|
|
11
|
-
"""
|
|
12
|
-
Contains utility functions for handling queue communication between threads.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
@staticmethod
|
|
16
|
-
def check_queue(queue: Queue, queue_timeout: int = None) -> Any:
|
|
17
|
-
"""
|
|
18
|
-
Checks if there is a message on a queue. If a timeout is specified the function
|
|
19
|
-
will raise a QueueTimeoutError if there is no message within the timeout. If
|
|
20
|
-
there is no timeout specified this function will block.
|
|
21
|
-
:param queue: The queue to be checked for a message
|
|
22
|
-
:param queue_timeout: Timeout in seconds
|
|
23
|
-
:return: Message found on queue
|
|
24
|
-
:raises QueueTimeoutError
|
|
25
|
-
"""
|
|
26
|
-
try:
|
|
27
|
-
message: Any = queue.get(timeout=queue_timeout)
|
|
28
|
-
except Empty:
|
|
29
|
-
logger.error("Queue timed out")
|
|
30
|
-
raise QueueTimeoutError
|
|
31
|
-
return message
|
|
32
|
-
|
|
33
|
-
@staticmethod
|
|
34
|
-
def clear_queue(queue: Queue) -> None:
|
|
35
|
-
while True:
|
|
36
|
-
try:
|
|
37
|
-
queue.get(block=False)
|
|
38
|
-
except Empty:
|
|
39
|
-
break
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import time
|
|
3
|
-
from typing import Optional, TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
from transitions import State
|
|
6
|
-
|
|
7
|
-
from isar.models.communication.message import StartMissionMessage
|
|
8
|
-
|
|
9
|
-
if TYPE_CHECKING:
|
|
10
|
-
from isar.state_machine.state_machine import StateMachine
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class Idle(State):
|
|
14
|
-
def __init__(self, state_machine: "StateMachine") -> None:
|
|
15
|
-
super().__init__(name="idle", on_enter=self.start, on_exit=self.stop)
|
|
16
|
-
self.state_machine: "StateMachine" = state_machine
|
|
17
|
-
self.logger = logging.getLogger("state_machine")
|
|
18
|
-
|
|
19
|
-
def start(self) -> None:
|
|
20
|
-
self.state_machine.update_state()
|
|
21
|
-
self._run()
|
|
22
|
-
|
|
23
|
-
def stop(self) -> None:
|
|
24
|
-
pass
|
|
25
|
-
|
|
26
|
-
def _run(self) -> None:
|
|
27
|
-
while True:
|
|
28
|
-
start_mission: Optional[
|
|
29
|
-
StartMissionMessage
|
|
30
|
-
] = self.state_machine.should_start_mission()
|
|
31
|
-
if start_mission:
|
|
32
|
-
self.state_machine.start_mission(
|
|
33
|
-
mission=start_mission.mission,
|
|
34
|
-
initial_pose=start_mission.initial_pose,
|
|
35
|
-
)
|
|
36
|
-
transition = self.state_machine.mission_started # type: ignore
|
|
37
|
-
break
|
|
38
|
-
time.sleep(self.state_machine.sleep_time)
|
|
39
|
-
|
|
40
|
-
transition()
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import time
|
|
3
|
-
from typing import Callable, Optional, TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
from injector import inject
|
|
6
|
-
from transitions import State
|
|
7
|
-
|
|
8
|
-
from isar.services.utilities.threaded_request import (
|
|
9
|
-
ThreadedRequest,
|
|
10
|
-
ThreadedRequestNotFinishedError,
|
|
11
|
-
)
|
|
12
|
-
from robot_interface.models.exceptions import RobotException
|
|
13
|
-
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
from isar.state_machine.state_machine import StateMachine
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class Initialize(State):
|
|
19
|
-
@inject
|
|
20
|
-
def __init__(self, state_machine: "StateMachine") -> None:
|
|
21
|
-
super().__init__(name="initialize", on_enter=self.start, on_exit=self.stop)
|
|
22
|
-
self.state_machine: "StateMachine" = state_machine
|
|
23
|
-
|
|
24
|
-
self.logger = logging.getLogger("state_machine")
|
|
25
|
-
self.initialize_thread: Optional[ThreadedRequest] = None
|
|
26
|
-
|
|
27
|
-
def start(self) -> None:
|
|
28
|
-
self.state_machine.update_state()
|
|
29
|
-
self._run()
|
|
30
|
-
|
|
31
|
-
def stop(self) -> None:
|
|
32
|
-
if self.initialize_thread:
|
|
33
|
-
self.initialize_thread.wait_for_thread()
|
|
34
|
-
self.initialize_thread = None
|
|
35
|
-
|
|
36
|
-
def _run(self) -> None:
|
|
37
|
-
transition: Callable
|
|
38
|
-
while True:
|
|
39
|
-
if not self.initialize_thread:
|
|
40
|
-
self.initialize_thread = ThreadedRequest(
|
|
41
|
-
self.state_machine.robot.initialize
|
|
42
|
-
)
|
|
43
|
-
self.initialize_thread.start_thread(
|
|
44
|
-
self.state_machine.get_initialize_params(),
|
|
45
|
-
name="State Machine Initialize Robot",
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
try:
|
|
49
|
-
self.initialize_thread.get_output()
|
|
50
|
-
except ThreadedRequestNotFinishedError:
|
|
51
|
-
time.sleep(self.state_machine.sleep_time)
|
|
52
|
-
continue
|
|
53
|
-
except RobotException as e:
|
|
54
|
-
self.logger.error(f"Initialization of robot failed. Error: {e}")
|
|
55
|
-
transition = self.state_machine.initialization_failed # type: ignore
|
|
56
|
-
break
|
|
57
|
-
|
|
58
|
-
transition = self.state_machine.initialization_successful # type: ignore
|
|
59
|
-
break
|
|
60
|
-
transition()
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import time
|
|
3
|
-
from typing import Any, Callable, Optional, TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
from transitions import State
|
|
6
|
-
|
|
7
|
-
from isar.config.settings import settings
|
|
8
|
-
from isar.services.utilities.threaded_request import (
|
|
9
|
-
ThreadedRequest,
|
|
10
|
-
ThreadedRequestNotFinishedError,
|
|
11
|
-
)
|
|
12
|
-
from robot_interface.models.exceptions import (
|
|
13
|
-
RobotException,
|
|
14
|
-
RobotInfeasibleMissionException,
|
|
15
|
-
RobotInfeasibleStepException,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
if TYPE_CHECKING:
|
|
19
|
-
from isar.state_machine.state_machine import StateMachine
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class Initiate(State):
|
|
23
|
-
def __init__(self, state_machine: "StateMachine") -> None:
|
|
24
|
-
super().__init__(name="initiate", on_enter=self.start, on_exit=self.stop)
|
|
25
|
-
self.state_machine: "StateMachine" = state_machine
|
|
26
|
-
self.initiate_failure_counter: int = 0
|
|
27
|
-
self.initiate_failure_counter_limit: int = (
|
|
28
|
-
settings.INITIATE_FAILURE_COUNTER_LIMIT
|
|
29
|
-
)
|
|
30
|
-
self.logger = logging.getLogger("state_machine")
|
|
31
|
-
|
|
32
|
-
self.initiate_thread: Optional[ThreadedRequest] = None
|
|
33
|
-
|
|
34
|
-
def start(self) -> None:
|
|
35
|
-
self.state_machine.update_state()
|
|
36
|
-
self._run()
|
|
37
|
-
|
|
38
|
-
def stop(self) -> None:
|
|
39
|
-
self.initiate_failure_counter = 0
|
|
40
|
-
if self.initiate_thread:
|
|
41
|
-
self.initiate_thread.wait_for_thread()
|
|
42
|
-
self.initiate_thread = None
|
|
43
|
-
|
|
44
|
-
def _run(self) -> None:
|
|
45
|
-
transition: Callable
|
|
46
|
-
while True:
|
|
47
|
-
if self.state_machine.should_stop_mission():
|
|
48
|
-
transition = self.state_machine.stop # type: ignore
|
|
49
|
-
break
|
|
50
|
-
|
|
51
|
-
if self.state_machine.should_pause_mission():
|
|
52
|
-
transition = self.state_machine.pause # type: ignore
|
|
53
|
-
break
|
|
54
|
-
|
|
55
|
-
if not self.state_machine.current_task:
|
|
56
|
-
self.logger.info(
|
|
57
|
-
f"Completed mission: {self.state_machine.current_mission.id}"
|
|
58
|
-
)
|
|
59
|
-
transition = self.state_machine.mission_finished # type: ignore
|
|
60
|
-
break
|
|
61
|
-
|
|
62
|
-
if not self.initiate_thread:
|
|
63
|
-
if self.state_machine.stepwise_mission:
|
|
64
|
-
self._run_initiate_thread(
|
|
65
|
-
initiate_function=self.state_machine.robot.initiate_step,
|
|
66
|
-
function_argument=self.state_machine.current_step,
|
|
67
|
-
thread_name="State Machine Initiate Step",
|
|
68
|
-
)
|
|
69
|
-
else:
|
|
70
|
-
self._run_initiate_thread(
|
|
71
|
-
initiate_function=self.state_machine.robot.initiate_mission,
|
|
72
|
-
function_argument=self.state_machine.current_mission,
|
|
73
|
-
thread_name="State Machine Initiate Mission",
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
try:
|
|
77
|
-
self.initiate_thread.get_output()
|
|
78
|
-
transition = self.state_machine.initiated # type: ignore
|
|
79
|
-
break
|
|
80
|
-
except ThreadedRequestNotFinishedError:
|
|
81
|
-
time.sleep(self.state_machine.sleep_time)
|
|
82
|
-
continue
|
|
83
|
-
except RobotInfeasibleStepException:
|
|
84
|
-
self.logger.warning(
|
|
85
|
-
f"Failed to initiate "
|
|
86
|
-
f"{type(self.state_machine.current_step).__name__}"
|
|
87
|
-
f"Invalid step: {str(self.state_machine.current_step.id)[:8]}"
|
|
88
|
-
)
|
|
89
|
-
transition = self.state_machine.initiate_infeasible # type: ignore
|
|
90
|
-
break
|
|
91
|
-
|
|
92
|
-
except RobotInfeasibleMissionException:
|
|
93
|
-
self.logger.warning(
|
|
94
|
-
f"Failed to initiate mission "
|
|
95
|
-
f"{str(self.state_machine.current_mission.id)[:8]}"
|
|
96
|
-
)
|
|
97
|
-
transition = self.state_machine.initiate_infeasible # type: ignore
|
|
98
|
-
break
|
|
99
|
-
|
|
100
|
-
except RobotException as e:
|
|
101
|
-
self.initiate_thread = None
|
|
102
|
-
self.initiate_failure_counter += 1
|
|
103
|
-
self.logger.warning(
|
|
104
|
-
f"Initiating step failed #: {str(self.initiate_failure_counter)} "
|
|
105
|
-
f"times. \n{e}"
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
if self.initiate_failure_counter >= self.initiate_failure_counter_limit:
|
|
109
|
-
self.logger.error(
|
|
110
|
-
f"Mission will be cancelled as initiate failed after "
|
|
111
|
-
f"{self.initiate_failure_counter_limit} attempts. "
|
|
112
|
-
f"Cancelling mission."
|
|
113
|
-
)
|
|
114
|
-
transition = self.state_machine.initiate_failed # type: ignore
|
|
115
|
-
break
|
|
116
|
-
|
|
117
|
-
time.sleep(self.state_machine.sleep_time)
|
|
118
|
-
|
|
119
|
-
transition()
|
|
120
|
-
|
|
121
|
-
def _run_initiate_thread(
|
|
122
|
-
self, initiate_function: Callable, function_argument: Any, thread_name: str
|
|
123
|
-
) -> None:
|
|
124
|
-
self.initiate_thread = ThreadedRequest(request_func=initiate_function)
|
|
125
|
-
|
|
126
|
-
self.initiate_thread.start_thread(
|
|
127
|
-
function_argument,
|
|
128
|
-
name=thread_name,
|
|
129
|
-
)
|
isar/state_machine/states/off.py
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from typing import TYPE_CHECKING
|
|
3
|
-
|
|
4
|
-
from transitions import State
|
|
5
|
-
|
|
6
|
-
if TYPE_CHECKING:
|
|
7
|
-
from isar.state_machine.state_machine import StateMachine
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class Off(State):
|
|
11
|
-
def __init__(self, state_machine: "StateMachine"):
|
|
12
|
-
super().__init__(name="off", on_enter=self.start)
|
|
13
|
-
self.logger = logging.getLogger("state_machine")
|
|
14
|
-
self.state_machine: "StateMachine" = state_machine
|
|
15
|
-
|
|
16
|
-
def start(self):
|
|
17
|
-
self.state_machine.update_state()
|
|
18
|
-
self.logger.info(f"State: {self.state_machine.current_state}")
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import time
|
|
3
|
-
from typing import TYPE_CHECKING, Callable, Optional
|
|
4
|
-
|
|
5
|
-
from transitions import State
|
|
6
|
-
|
|
7
|
-
from isar.services.utilities.threaded_request import (
|
|
8
|
-
ThreadedRequest,
|
|
9
|
-
ThreadedRequestNotFinishedError,
|
|
10
|
-
)
|
|
11
|
-
from robot_interface.models.exceptions import RobotException
|
|
12
|
-
|
|
13
|
-
if TYPE_CHECKING:
|
|
14
|
-
from isar.state_machine.state_machine import StateMachine
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class Stop(State):
|
|
18
|
-
def __init__(self, state_machine: "StateMachine") -> None:
|
|
19
|
-
super().__init__(name="stop", on_enter=self.start, on_exit=self.stop)
|
|
20
|
-
self.state_machine: "StateMachine" = state_machine
|
|
21
|
-
self.logger = logging.getLogger("state_machine")
|
|
22
|
-
self.stop_thread: Optional[ThreadedRequest] = None
|
|
23
|
-
self._count_number_retries: int = 0
|
|
24
|
-
|
|
25
|
-
def start(self) -> None:
|
|
26
|
-
self.state_machine.update_state()
|
|
27
|
-
self._run()
|
|
28
|
-
|
|
29
|
-
def stop(self) -> None:
|
|
30
|
-
if self.stop_thread:
|
|
31
|
-
self.stop_thread.wait_for_thread()
|
|
32
|
-
self.stop_thread = None
|
|
33
|
-
self._count_number_retries = 0
|
|
34
|
-
|
|
35
|
-
def _run(self) -> None:
|
|
36
|
-
transition: Callable
|
|
37
|
-
while True:
|
|
38
|
-
if not self.stop_thread:
|
|
39
|
-
self.stop_thread = ThreadedRequest(self.state_machine.robot.stop)
|
|
40
|
-
self.stop_thread.start_thread(name="State Machine Stop Robot")
|
|
41
|
-
|
|
42
|
-
if self.state_machine.should_stop_mission():
|
|
43
|
-
self.state_machine.stopped = True
|
|
44
|
-
|
|
45
|
-
try:
|
|
46
|
-
self.stop_thread.get_output()
|
|
47
|
-
except ThreadedRequestNotFinishedError:
|
|
48
|
-
time.sleep(self.state_machine.sleep_time)
|
|
49
|
-
continue
|
|
50
|
-
|
|
51
|
-
except RobotException:
|
|
52
|
-
if self.handle_stop_fail(
|
|
53
|
-
retry_limit=self.state_machine.stop_robot_attempts_limit
|
|
54
|
-
):
|
|
55
|
-
transition = self.state_machine.mission_stopped # type: ignore
|
|
56
|
-
break
|
|
57
|
-
|
|
58
|
-
self.logger.warning("Failed to stop robot. Retrying.")
|
|
59
|
-
self.stop_thread = None
|
|
60
|
-
continue
|
|
61
|
-
if self.state_machine.stopped:
|
|
62
|
-
transition = self.state_machine.mission_stopped # type: ignore
|
|
63
|
-
else:
|
|
64
|
-
transition = self.state_machine.mission_paused # type: ignore
|
|
65
|
-
break
|
|
66
|
-
|
|
67
|
-
transition()
|
|
68
|
-
|
|
69
|
-
def handle_stop_fail(self, retry_limit: int) -> bool:
|
|
70
|
-
self._count_number_retries += 1
|
|
71
|
-
if self._count_number_retries > retry_limit:
|
|
72
|
-
self.logger.warning(
|
|
73
|
-
"Could not communicate request: Reached limit for stop attempts. "
|
|
74
|
-
"Cancelled mission and transitioned to idle."
|
|
75
|
-
)
|
|
76
|
-
return True
|
|
77
|
-
time.sleep(self.state_machine.sleep_time)
|
|
78
|
-
return False
|