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.
Files changed (129) hide show
  1. isar/__init__.py +2 -5
  2. isar/apis/api.py +159 -66
  3. isar/apis/models/__init__.py +0 -1
  4. isar/apis/models/models.py +22 -12
  5. isar/apis/models/start_mission_definition.py +128 -123
  6. isar/apis/robot_control/robot_controller.py +41 -0
  7. isar/apis/schedule/scheduling_controller.py +135 -121
  8. isar/apis/security/authentication.py +5 -5
  9. isar/config/certs/ca-cert.pem +32 -32
  10. isar/config/keyvault/keyvault_service.py +1 -2
  11. isar/config/log.py +47 -39
  12. isar/config/logging.conf +16 -31
  13. isar/config/open_telemetry.py +102 -0
  14. isar/config/predefined_mission_definition/default_exr.json +49 -0
  15. isar/config/predefined_mission_definition/default_mission.json +1 -5
  16. isar/config/predefined_mission_definition/default_turtlebot.json +4 -11
  17. isar/config/predefined_missions/default.json +67 -87
  18. isar/config/predefined_missions/default_extra_capabilities.json +107 -0
  19. isar/config/settings.py +119 -142
  20. isar/eventhandlers/eventhandler.py +123 -0
  21. isar/mission_planner/local_planner.py +6 -20
  22. isar/mission_planner/mission_planner_interface.py +1 -1
  23. isar/models/events.py +184 -0
  24. isar/models/status.py +18 -0
  25. isar/modules.py +118 -205
  26. isar/robot/robot.py +377 -0
  27. isar/robot/robot_battery.py +60 -0
  28. isar/robot/robot_monitor_mission.py +357 -0
  29. isar/robot/robot_pause_mission.py +74 -0
  30. isar/robot/robot_resume_mission.py +67 -0
  31. isar/robot/robot_start_mission.py +66 -0
  32. isar/robot/robot_status.py +61 -0
  33. isar/robot/robot_stop_mission.py +68 -0
  34. isar/robot/robot_upload_inspection.py +75 -0
  35. isar/script.py +171 -0
  36. isar/services/service_connections/mqtt/mqtt_client.py +47 -11
  37. isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +32 -0
  38. isar/services/service_connections/mqtt/robot_info_publisher.py +4 -3
  39. isar/services/service_connections/persistent_memory.py +69 -0
  40. isar/services/utilities/mqtt_utilities.py +93 -0
  41. isar/services/utilities/robot_utilities.py +20 -0
  42. isar/services/utilities/scheduling_utilities.py +393 -65
  43. isar/state_machine/state_machine.py +227 -486
  44. isar/state_machine/states/__init__.py +0 -7
  45. isar/state_machine/states/await_next_mission.py +114 -0
  46. isar/state_machine/states/blocked_protective_stop.py +60 -0
  47. isar/state_machine/states/going_to_lockdown.py +95 -0
  48. isar/state_machine/states/going_to_recharging.py +92 -0
  49. isar/state_machine/states/home.py +115 -0
  50. isar/state_machine/states/intervention_needed.py +77 -0
  51. isar/state_machine/states/lockdown.py +38 -0
  52. isar/state_machine/states/maintenance.py +36 -0
  53. isar/state_machine/states/monitor.py +137 -166
  54. isar/state_machine/states/offline.py +60 -0
  55. isar/state_machine/states/paused.py +92 -23
  56. isar/state_machine/states/pausing.py +48 -0
  57. isar/state_machine/states/pausing_return_home.py +48 -0
  58. isar/state_machine/states/recharging.py +80 -0
  59. isar/state_machine/states/resuming.py +57 -0
  60. isar/state_machine/states/resuming_return_home.py +64 -0
  61. isar/state_machine/states/return_home_paused.py +109 -0
  62. isar/state_machine/states/returning_home.py +217 -0
  63. isar/state_machine/states/stopping.py +61 -0
  64. isar/state_machine/states/stopping_due_to_maintenance.py +61 -0
  65. isar/state_machine/states/stopping_go_to_lockdown.py +60 -0
  66. isar/state_machine/states/stopping_go_to_recharge.py +51 -0
  67. isar/state_machine/states/stopping_return_home.py +77 -0
  68. isar/state_machine/states/unknown_status.py +72 -0
  69. isar/state_machine/states_enum.py +22 -5
  70. isar/state_machine/transitions/mission.py +192 -0
  71. isar/state_machine/transitions/return_home.py +106 -0
  72. isar/state_machine/transitions/robot_status.py +80 -0
  73. isar/state_machine/utils/common_event_handlers.py +73 -0
  74. isar/storage/blob_storage.py +71 -45
  75. isar/storage/local_storage.py +28 -14
  76. isar/storage/storage_interface.py +28 -6
  77. isar/storage/uploader.py +184 -55
  78. isar/storage/utilities.py +35 -27
  79. isar-1.34.9.dist-info/METADATA +496 -0
  80. isar-1.34.9.dist-info/RECORD +135 -0
  81. {isar-1.15.0.dist-info → isar-1.34.9.dist-info}/WHEEL +1 -1
  82. isar-1.34.9.dist-info/entry_points.txt +3 -0
  83. robot_interface/models/exceptions/__init__.py +0 -7
  84. robot_interface/models/exceptions/robot_exceptions.py +274 -4
  85. robot_interface/models/initialize/__init__.py +0 -1
  86. robot_interface/models/inspection/__init__.py +0 -13
  87. robot_interface/models/inspection/inspection.py +43 -34
  88. robot_interface/models/mission/mission.py +18 -14
  89. robot_interface/models/mission/status.py +20 -25
  90. robot_interface/models/mission/task.py +156 -92
  91. robot_interface/models/robots/battery_state.py +6 -0
  92. robot_interface/models/robots/media.py +13 -0
  93. robot_interface/models/robots/robot_model.py +7 -7
  94. robot_interface/robot_interface.py +135 -66
  95. robot_interface/telemetry/mqtt_client.py +84 -12
  96. robot_interface/telemetry/payloads.py +111 -12
  97. robot_interface/utilities/json_service.py +7 -1
  98. isar/config/predefined_missions/default_turtlebot.json +0 -110
  99. isar/config/predefined_poses/__init__.py +0 -0
  100. isar/config/predefined_poses/predefined_poses.py +0 -616
  101. isar/config/settings.env +0 -26
  102. isar/mission_planner/sequential_task_selector.py +0 -23
  103. isar/mission_planner/task_selector_interface.py +0 -31
  104. isar/models/communication/__init__.py +0 -0
  105. isar/models/communication/message.py +0 -12
  106. isar/models/communication/queues/__init__.py +0 -4
  107. isar/models/communication/queues/queue_io.py +0 -12
  108. isar/models/communication/queues/queue_timeout_error.py +0 -2
  109. isar/models/communication/queues/queues.py +0 -19
  110. isar/models/communication/queues/status_queue.py +0 -20
  111. isar/models/mission_metadata/__init__.py +0 -0
  112. isar/services/readers/__init__.py +0 -0
  113. isar/services/readers/base_reader.py +0 -37
  114. isar/services/service_connections/mqtt/robot_status_publisher.py +0 -93
  115. isar/services/service_connections/stid/__init__.py +0 -0
  116. isar/services/service_connections/stid/stid_service.py +0 -45
  117. isar/services/utilities/queue_utilities.py +0 -39
  118. isar/state_machine/states/idle.py +0 -40
  119. isar/state_machine/states/initialize.py +0 -60
  120. isar/state_machine/states/initiate.py +0 -129
  121. isar/state_machine/states/off.py +0 -18
  122. isar/state_machine/states/stop.py +0 -78
  123. isar/storage/slimm_storage.py +0 -181
  124. isar-1.15.0.dist-info/METADATA +0 -417
  125. isar-1.15.0.dist-info/RECORD +0 -113
  126. robot_interface/models/initialize/initialize_params.py +0 -9
  127. robot_interface/models/mission/step.py +0 -211
  128. {isar-1.15.0.dist-info → isar-1.34.9.dist-info/licenses}/LICENSE +0 -0
  129. {isar-1.15.0.dist-info → isar-1.34.9.dist-info}/top_level.txt +0 -0
@@ -1,12 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import Optional
3
-
4
- from alitra import Pose
5
-
6
- from robot_interface.models.mission.mission import Mission
7
-
8
-
9
- @dataclass
10
- class StartMissionMessage:
11
- mission: Mission
12
- initial_pose: Optional[Pose]
@@ -1,4 +0,0 @@
1
- from .queue_io import QueueIO
2
- from .queue_timeout_error import QueueTimeoutError
3
- from .queues import Queues
4
- from .status_queue import StatusQueue
@@ -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,2 +0,0 @@
1
- class QueueTimeoutError(Exception):
2
- pass
@@ -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
- )
@@ -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