isar 1.30.4__py3-none-any.whl → 1.31.0__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.

Files changed (36) hide show
  1. isar/apis/api.py +7 -51
  2. isar/apis/models/models.py +5 -4
  3. isar/apis/models/start_mission_definition.py +4 -0
  4. isar/apis/robot_control/robot_controller.py +0 -2
  5. isar/apis/schedule/scheduling_controller.py +12 -4
  6. isar/config/log.py +8 -29
  7. isar/config/open_telemetry.py +68 -0
  8. isar/config/settings.py +12 -0
  9. isar/mission_planner/local_planner.py +0 -3
  10. isar/models/communication/queues/events.py +1 -1
  11. isar/models/communication/queues/queue_utils.py +2 -2
  12. isar/robot/robot.py +0 -3
  13. isar/script.py +2 -0
  14. isar/services/utilities/robot_utilities.py +0 -3
  15. isar/services/utilities/scheduling_utilities.py +28 -11
  16. isar/state_machine/generic_states/idle.py +1 -1
  17. isar/state_machine/generic_states/ongoing_mission.py +22 -7
  18. isar/state_machine/state_machine.py +7 -12
  19. isar/state_machine/states/monitor.py +0 -2
  20. isar/state_machine/states/stopping.py +2 -2
  21. isar/state_machine/states/unknown_status.py +1 -1
  22. isar/state_machine/transitions/functions/pause.py +39 -10
  23. isar/state_machine/transitions/functions/resume.py +44 -15
  24. isar/state_machine/transitions/functions/stop.py +44 -1
  25. isar/state_machine/transitions/mission.py +26 -2
  26. isar/state_machine/transitions/return_home.py +1 -1
  27. isar/storage/blob_storage.py +0 -2
  28. isar/storage/uploader.py +0 -3
  29. {isar-1.30.4.dist-info → isar-1.31.0.dist-info}/METADATA +7 -4
  30. {isar-1.30.4.dist-info → isar-1.31.0.dist-info}/RECORD +36 -35
  31. robot_interface/models/mission/task.py +0 -3
  32. robot_interface/robot_interface.py +1 -4
  33. {isar-1.30.4.dist-info → isar-1.31.0.dist-info}/WHEEL +0 -0
  34. {isar-1.30.4.dist-info → isar-1.31.0.dist-info}/entry_points.txt +0 -0
  35. {isar-1.30.4.dist-info → isar-1.31.0.dist-info}/licenses/LICENSE +0 -0
  36. {isar-1.30.4.dist-info → isar-1.31.0.dist-info}/top_level.txt +0 -0
isar/apis/api.py CHANGED
@@ -8,35 +8,23 @@ from typing import List, Union
8
8
 
9
9
  import click
10
10
  import uvicorn
11
- from dependency_injector.wiring import inject
12
- from fastapi import FastAPI, Request, Security
11
+ from fastapi import FastAPI, Security
13
12
  from fastapi.middleware.cors import CORSMiddleware
14
13
  from fastapi.routing import APIRouter
15
- from opencensus.ext.azure.trace_exporter import AzureExporter
16
- from opencensus.trace.attributes_helper import COMMON_ATTRIBUTES
17
- from opencensus.trace.samplers import ProbabilitySampler
18
- from opencensus.trace.span import SpanKind
19
- from opencensus.trace.tracer import Tracer
20
14
  from pydantic import AnyHttpUrl
21
15
 
22
16
  from isar.apis.models.models import ControlMissionResponse, StartMissionResponse
23
17
  from isar.apis.robot_control.robot_controller import RobotController
24
18
  from isar.apis.schedule.scheduling_controller import SchedulingController
25
19
  from isar.apis.security.authentication import Authenticator
26
- from isar.config.configuration_error import ConfigurationError
27
- from isar.config.keyvault.keyvault_error import KeyvaultError
28
20
  from isar.config.keyvault.keyvault_service import Keyvault
29
21
  from isar.config.settings import settings
30
22
  from robot_interface.telemetry.mqtt_client import MqttClientInterface
31
23
  from robot_interface.telemetry.payloads import StartUpMessagePayload
32
24
  from robot_interface.utilities.json_service import EnhancedJSONEncoder
33
25
 
34
- HTTP_URL = COMMON_ATTRIBUTES["HTTP_URL"]
35
- HTTP_STATUS_CODE = COMMON_ATTRIBUTES["HTTP_STATUS_CODE"]
36
-
37
26
 
38
27
  class API:
39
- @inject
40
28
  def __init__(
41
29
  self,
42
30
  authenticator: Authenticator,
@@ -45,7 +33,6 @@ class API:
45
33
  keyvault: Keyvault,
46
34
  mqtt_publisher: MqttClientInterface,
47
35
  port: int = settings.API_PORT,
48
- azure_ai_logging_enabled: bool = settings.LOG_HANDLER_APPLICATION_INSIGHTS_ENABLED,
49
36
  ) -> None:
50
37
  self.authenticator: Authenticator = authenticator
51
38
  self.scheduling_controller: SchedulingController = scheduling_controller
@@ -53,7 +40,6 @@ class API:
53
40
  self.keyvault: Keyvault = keyvault
54
41
  self.host: str = "0.0.0.0" # Locking uvicorn to use 0.0.0.0
55
42
  self.port: int = port
56
- self.azure_ai_logging_enabled: bool = azure_ai_logging_enabled
57
43
  self.mqtt_publisher: MqttClientInterface = mqtt_publisher
58
44
 
59
45
  self.logger: Logger = logging.getLogger("api")
@@ -113,9 +99,6 @@ class API:
113
99
  allow_headers=["*"],
114
100
  )
115
101
 
116
- if self.azure_ai_logging_enabled:
117
- self._add_request_logging_middleware(app)
118
-
119
102
  app.include_router(router=self._create_scheduler_router())
120
103
 
121
104
  app.include_router(router=self._create_info_router())
@@ -210,9 +193,15 @@ class API:
210
193
  "description": "Mission succesfully stopped",
211
194
  "model": ControlMissionResponse,
212
195
  },
196
+ HTTPStatus.UNPROCESSABLE_ENTITY.value: {
197
+ "description": "Invalid body - The JSON is incorrect",
198
+ },
213
199
  HTTPStatus.CONFLICT.value: {
214
200
  "description": "Conflict - Invalid command in the current state",
215
201
  },
202
+ HTTPStatus.BAD_REQUEST.value: {
203
+ "description": "Bad request - Robot does not have the capabilities to perform the mission",
204
+ },
216
205
  HTTPStatus.INTERNAL_SERVER_ERROR.value: {
217
206
  "description": "Internal Server Error - Current state of state machine unknown",
218
207
  },
@@ -336,39 +325,6 @@ class API:
336
325
  extra={"color_message": color_message},
337
326
  )
338
327
 
339
- def _add_request_logging_middleware(self, app: FastAPI) -> None:
340
- connection_string: str
341
- try:
342
- connection_string = self.keyvault.get_secret(
343
- "application-insights-connection-string"
344
- ).value
345
- except KeyvaultError:
346
- message: str = (
347
- "Missing connection string for Application Insights in key vault. "
348
- )
349
- self.logger.critical(message)
350
- raise ConfigurationError(message)
351
-
352
- @app.middleware("http")
353
- async def middlewareOpencensus(request: Request, call_next):
354
- tracer = Tracer(
355
- exporter=AzureExporter(connection_string=connection_string),
356
- sampler=ProbabilitySampler(1.0),
357
- )
358
- with tracer.span("main") as span:
359
- span.span_kind = SpanKind.SERVER
360
-
361
- response = await call_next(request)
362
-
363
- tracer.add_attribute_to_current_span(
364
- attribute_key=HTTP_STATUS_CODE, attribute_value=response.status_code
365
- )
366
- tracer.add_attribute_to_current_span(
367
- attribute_key=HTTP_URL, attribute_value=str(request.url)
368
- )
369
-
370
- return response
371
-
372
328
  def _publish_startup_message(self) -> None:
373
329
  if not self.mqtt_publisher:
374
330
  return
@@ -19,10 +19,11 @@ class StartMissionResponse(BaseModel):
19
19
 
20
20
 
21
21
  class ControlMissionResponse(BaseModel):
22
- mission_id: str
23
- mission_status: str
24
- task_id: str
25
- task_status: str
22
+ mission_id: Optional[str]
23
+ mission_status: Optional[str]
24
+ mission_not_found: Optional[bool] = False
25
+ task_id: Optional[str]
26
+ task_status: Optional[str]
26
27
 
27
28
 
28
29
  class RobotInfoResponse(BaseModel):
@@ -58,6 +58,10 @@ class StartMissionDefinition(BaseModel):
58
58
  start_pose: Optional[InputPose] = None
59
59
 
60
60
 
61
+ class StopMissionDefinition(BaseModel):
62
+ mission_id: Optional[str] = None
63
+
64
+
61
65
  def to_isar_mission(
62
66
  start_mission_definition: StartMissionDefinition,
63
67
  ) -> Mission:
@@ -1,6 +1,5 @@
1
1
  import logging
2
2
 
3
- from dependency_injector.wiring import inject
4
3
  from fastapi import HTTPException
5
4
 
6
5
  from isar.apis.models.models import RobotInfoResponse
@@ -10,7 +9,6 @@ from robot_interface.models.robots.media import MediaConfig
10
9
 
11
10
 
12
11
  class RobotController:
13
- @inject
14
12
  def __init__(
15
13
  self,
16
14
  robot_utilities: RobotUtilities,
@@ -2,7 +2,6 @@ import logging
2
2
  from http import HTTPStatus
3
3
  from threading import Lock
4
4
 
5
- from dependency_injector.wiring import inject
6
5
  from fastapi import Body, HTTPException, Path
7
6
 
8
7
  from isar.apis.models.models import (
@@ -12,6 +11,7 @@ from isar.apis.models.models import (
12
11
  )
13
12
  from isar.apis.models.start_mission_definition import (
14
13
  StartMissionDefinition,
14
+ StopMissionDefinition,
15
15
  to_isar_mission,
16
16
  )
17
17
  from isar.config.settings import robot_settings, settings
@@ -23,7 +23,6 @@ from robot_interface.models.mission.task import TASKS, InspectionTask, MoveArm
23
23
 
24
24
 
25
25
  class SchedulingController:
26
- @inject
27
26
  def __init__(
28
27
  self,
29
28
  scheduling_utilities: SchedulingUtilities,
@@ -179,7 +178,16 @@ class SchedulingController:
179
178
  )
180
179
  return resume_mission_response
181
180
 
182
- def stop_mission(self) -> ControlMissionResponse:
181
+ def stop_mission(
182
+ self,
183
+ mission_id: StopMissionDefinition = Body(
184
+ default=None,
185
+ embed=True,
186
+ title="Mission ID to stop",
187
+ description="The mission ID of the mission being stopped, in json format",
188
+ ),
189
+ ) -> ControlMissionResponse:
190
+
183
191
  self.logger.info("Received request to stop current mission")
184
192
 
185
193
  state: States = self.scheduling_utilities.get_state()
@@ -198,7 +206,7 @@ class SchedulingController:
198
206
  raise HTTPException(status_code=HTTPStatus.CONFLICT, detail=error_message)
199
207
 
200
208
  stop_mission_response: ControlMissionResponse = (
201
- self.scheduling_utilities.stop_mission()
209
+ self.scheduling_utilities.stop_mission(mission_id.mission_id)
202
210
  )
203
211
  return stop_mission_response
204
212
 
isar/config/log.py CHANGED
@@ -3,30 +3,21 @@ import logging.config
3
3
  from importlib.resources import as_file, files
4
4
 
5
5
  import yaml
6
- from opencensus.ext.azure.log_exporter import AzureLogHandler
7
6
  from uvicorn.logging import ColourizedFormatter
8
7
 
9
- from isar.config.configuration_error import ConfigurationError
10
- from isar.config.keyvault.keyvault_error import KeyvaultError
11
8
  from isar.config.keyvault.keyvault_service import Keyvault
12
9
  from isar.config.settings import settings
13
10
 
14
11
 
15
12
  def setup_loggers(keyvault: Keyvault) -> None:
16
13
  log_levels: dict = settings.LOG_LEVELS
17
- source = files("isar").joinpath("config").joinpath("logging.conf")
18
- with as_file(source) as f:
19
- log_config = yaml.safe_load(open(f))
14
+ log_config = load_log_config()
20
15
 
21
16
  logging.config.dictConfig(log_config)
22
17
 
23
18
  handlers = []
24
19
  if settings.LOG_HANDLER_LOCAL_ENABLED:
25
20
  handlers.append(configure_console_handler(log_config=log_config))
26
- if settings.LOG_HANDLER_APPLICATION_INSIGHTS_ENABLED:
27
- handlers.append(
28
- configure_azure_handler(log_config=log_config, keyvault=keyvault)
29
- )
30
21
 
31
22
  for log_handler in handlers:
32
23
  for loggers in log_config["loggers"].keys():
@@ -35,6 +26,13 @@ def setup_loggers(keyvault: Keyvault) -> None:
35
26
  logging.getLogger().addHandler(log_handler)
36
27
 
37
28
 
29
+ def load_log_config():
30
+ source = files("isar").joinpath("config").joinpath("logging.conf")
31
+ with as_file(source) as f:
32
+ log_config = yaml.safe_load(open(f))
33
+ return log_config
34
+
35
+
38
36
  def configure_console_handler(log_config: dict) -> logging.Handler:
39
37
  handler = logging.StreamHandler()
40
38
  handler.setLevel(log_config["root"]["level"])
@@ -46,22 +44,3 @@ def configure_console_handler(log_config: dict) -> logging.Handler:
46
44
  )
47
45
  )
48
46
  return handler
49
-
50
-
51
- def configure_azure_handler(log_config: dict, keyvault: Keyvault) -> logging.Handler:
52
- connection_string: str
53
- try:
54
- connection_string = keyvault.get_secret(
55
- "application-insights-connection-string"
56
- ).value
57
- except KeyvaultError:
58
- message: str = (
59
- "CRITICAL ERROR: Missing connection string for"
60
- f" Application Insights in key vault '{keyvault.name}'."
61
- )
62
- print(f"\n{message} \n")
63
- raise ConfigurationError(message)
64
-
65
- handler = AzureLogHandler(connection_string=connection_string)
66
- handler.setLevel(log_config["root"]["level"])
67
- return handler
@@ -0,0 +1,68 @@
1
+ import logging
2
+
3
+ from azure.identity import ManagedIdentityCredential
4
+ from azure.monitor.opentelemetry.exporter import (
5
+ AzureMonitorLogExporter,
6
+ AzureMonitorTraceExporter,
7
+ )
8
+ from fastapi import FastAPI
9
+ from opentelemetry import trace
10
+ from opentelemetry._logs import set_logger_provider
11
+ from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
12
+ from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
13
+ from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
14
+ from opentelemetry.sdk.resources import SERVICE_NAME, Resource
15
+ from opentelemetry.sdk.trace import TracerProvider
16
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
17
+
18
+ from isar.config.log import load_log_config
19
+ from isar.config.settings import settings
20
+
21
+
22
+ def setup_open_telemetry(app: FastAPI) -> None:
23
+ if not settings.LOG_HANDLER_APPLICATION_INSIGHTS_ENABLED:
24
+ return
25
+ trace_exporter, log_exporter = get_azure_monitor_exporters()
26
+
27
+ service_name = settings.OPEN_TELEMETRY_SERVICE_NAME
28
+ resource = Resource.create({SERVICE_NAME: service_name})
29
+
30
+ tracer_provider = TracerProvider(resource=resource)
31
+ tracer_provider.add_span_processor(BatchSpanProcessor(trace_exporter))
32
+ trace.set_tracer_provider(tracer_provider)
33
+
34
+ log_provider = LoggerProvider(resource=resource)
35
+ set_logger_provider(log_provider)
36
+ log_provider.add_log_record_processor(BatchLogRecordProcessor(log_exporter))
37
+
38
+ handler = LoggingHandler(logger_provider=log_provider)
39
+ attach_loggers_for_open_telemetry(handler)
40
+
41
+ FastAPIInstrumentor.instrument_app(app, tracer_provider=tracer_provider)
42
+
43
+
44
+ def attach_loggers_for_open_telemetry(handler: LoggingHandler):
45
+ log_config = load_log_config()
46
+
47
+ for logger_name in log_config["loggers"].keys():
48
+ logger = logging.getLogger(logger_name)
49
+ logger.addHandler(handler)
50
+
51
+
52
+ def get_azure_monitor_exporters() -> (
53
+ tuple[AzureMonitorTraceExporter, AzureMonitorLogExporter]
54
+ ):
55
+ """
56
+ If connection string is defined in environment variables, then use it to create Azure Monitor Exporters.
57
+ Else use Azure Managed Identity to create Azure Monitor Exporters.
58
+ """
59
+ connection_string = settings.APPLICATIONINSIGHTS_CONNECTION_STRING
60
+ if connection_string:
61
+ trace_exporter = AzureMonitorTraceExporter(connection_string=connection_string)
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)
67
+
68
+ return trace_exporter, log_exporter
isar/config/settings.py CHANGED
@@ -12,6 +12,12 @@ from robot_interface.telemetry.payloads import DocumentInfo
12
12
 
13
13
 
14
14
  class Settings(BaseSettings):
15
+ # Name of the OpenTelemetry service
16
+ OPEN_TELEMETRY_SERVICE_NAME: str = Field(default="isar")
17
+ # Connection string for Azure Application Insights
18
+ # This is optional and it will use managed identity if not set
19
+ APPLICATIONINSIGHTS_CONNECTION_STRING: str = Field(default="")
20
+
15
21
  # Determines which robot package ISAR will attempt to import
16
22
  # Name must match with an installed python package in the local environment
17
23
  ROBOT_PACKAGE: str = Field(default="isar_robot")
@@ -52,6 +58,12 @@ class Settings(BaseSettings):
52
58
  # issues
53
59
  REQUEST_STATUS_COMMUNICATION_RECONNECT_DELAY: float = Field(default=10)
54
60
 
61
+ # Number of attempts for state transitions resume and pause if failed
62
+ STATE_TRANSITION_NUM_RETIRES: int = Field(default=10)
63
+
64
+ # Interval between attempt of state transition
65
+ STATE_TRANSITION_RETRY_INTERVAL_SEC: float = Field(default=1)
66
+
55
67
  # Number of attempts to stop the robot before giving up
56
68
  STOP_ROBOT_ATTEMPTS_LIMIT: int = Field(default=3)
57
69
 
@@ -2,8 +2,6 @@ import json
2
2
  import logging
3
3
  from pathlib import Path
4
4
 
5
- from dependency_injector.wiring import inject
6
-
7
5
  from isar.config.settings import settings
8
6
  from isar.mission_planner.mission_planner_interface import (
9
7
  MissionNotFoundError,
@@ -16,7 +14,6 @@ logger = logging.getLogger("api")
16
14
 
17
15
 
18
16
  class LocalPlanner(MissionPlannerInterface):
19
- @inject
20
17
  def __init__(self):
21
18
  self.predefined_mission_folder = Path(settings.PREDEFINED_MISSIONS_FOLDER)
22
19
 
@@ -35,7 +35,7 @@ class APIRequests:
35
35
  class StateMachineEvents:
36
36
  def __init__(self) -> None:
37
37
  self.start_mission: Queue[Mission] = Queue(maxsize=1)
38
- self.stop_mission: Queue[bool] = Queue(maxsize=1)
38
+ self.stop_mission: Queue[str] = Queue(maxsize=1)
39
39
  self.pause_mission: Queue[bool] = Queue(maxsize=1)
40
40
  self.task_status_request: Queue[str] = Queue(maxsize=1)
41
41
 
@@ -1,12 +1,12 @@
1
1
  from queue import Empty, Queue
2
- from typing import Optional, TypeVar
2
+ from typing import Any, Optional, TypeVar
3
3
 
4
4
  from isar.models.communication.queues.status_queue import StatusQueue
5
5
 
6
6
  T = TypeVar("T")
7
7
 
8
8
 
9
- def trigger_event_without_data(queue: Queue[bool]) -> None:
9
+ def trigger_event_without_data(queue: Queue[Any]) -> None:
10
10
  queue.put(True)
11
11
 
12
12
 
isar/robot/robot.py CHANGED
@@ -3,8 +3,6 @@ from queue import Queue
3
3
  from threading import Event
4
4
  from typing import Optional
5
5
 
6
- from dependency_injector.wiring import inject
7
-
8
6
  from isar.models.communication.queues.events import (
9
7
  Events,
10
8
  RobotServiceEvents,
@@ -21,7 +19,6 @@ from robot_interface.robot_interface import RobotInterface
21
19
 
22
20
 
23
21
  class Robot(object):
24
- @inject
25
22
  def __init__(
26
23
  self, events: Events, robot: RobotInterface, shared_state: SharedState
27
24
  ) -> None:
isar/script.py CHANGED
@@ -8,6 +8,7 @@ from typing import Any, List, Tuple
8
8
  import isar
9
9
  from isar.apis.api import API
10
10
  from isar.config.log import setup_loggers
11
+ from isar.config.open_telemetry import setup_open_telemetry
11
12
  from isar.config.settings import robot_settings, settings
12
13
  from isar.models.communication.queues.events import Events
13
14
  from isar.modules import ApplicationContainer, get_injector
@@ -83,6 +84,7 @@ def start() -> None:
83
84
 
84
85
  keyvault = injector.keyvault()
85
86
  setup_loggers(keyvault=keyvault)
87
+ setup_open_telemetry(app=injector.api().app)
86
88
  logger: Logger = logging.getLogger("main")
87
89
 
88
90
  print_startup_info()
@@ -1,7 +1,5 @@
1
1
  import logging
2
2
 
3
- from dependency_injector.wiring import inject
4
-
5
3
  from robot_interface.models.robots.media import MediaConfig
6
4
  from robot_interface.robot_interface import RobotInterface
7
5
 
@@ -11,7 +9,6 @@ class RobotUtilities:
11
9
  Contains utility functions for getting robot information from the API.
12
10
  """
13
11
 
14
- @inject
15
12
  def __init__(
16
13
  self,
17
14
  robot: RobotInterface,
@@ -4,7 +4,6 @@ from http import HTTPStatus
4
4
  from queue import Empty
5
5
  from typing import Any, List
6
6
 
7
- from dependency_injector.wiring import inject
8
7
  from fastapi import HTTPException
9
8
  from requests import HTTPError
10
9
 
@@ -22,6 +21,7 @@ from isar.models.communication.queues.queue_timeout_error import QueueTimeoutErr
22
21
  from isar.services.utilities.queue_utilities import QueueUtilities
23
22
  from isar.state_machine.states_enum import States
24
23
  from robot_interface.models.mission.mission import Mission
24
+ from robot_interface.models.mission.status import MissionStatus
25
25
 
26
26
 
27
27
  class SchedulingUtilities:
@@ -30,7 +30,6 @@ class SchedulingUtilities:
30
30
  required thread communication through queues to the state machine.
31
31
  """
32
32
 
33
- @inject
34
33
  def __init__(
35
34
  self,
36
35
  events: Events,
@@ -219,15 +218,15 @@ class SchedulingUtilities:
219
218
  If there is a timeout while communicating with the state machine
220
219
  """
221
220
  try:
222
- return self._send_command(True, self.api_events.pause_mission)
221
+ response = self._send_command(True, self.api_events.pause_mission)
222
+ self.logger.info("OK - Mission successfully paused")
223
+ return response
223
224
  except QueueTimeoutError:
224
225
  error_message = "Internal Server Error - Failed to pause mission"
225
226
  self.logger.error(error_message)
226
227
  raise HTTPException(
227
228
  status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=error_message
228
229
  )
229
- finally:
230
- self.logger.info("OK - Mission successfully paused")
231
230
 
232
231
  def resume_mission(self) -> ControlMissionResponse:
233
232
  """Resume mission
@@ -238,28 +237,46 @@ class SchedulingUtilities:
238
237
  If there is a timeout while communicating with the state machine
239
238
  """
240
239
  try:
241
- return self._send_command(True, self.api_events.resume_mission)
240
+ response = self._send_command(True, self.api_events.resume_mission)
241
+ self.logger.info("OK - Mission successfully resumed")
242
+ return response
242
243
  except QueueTimeoutError:
243
244
  error_message = "Internal Server Error - Failed to resume mission"
244
245
  self.logger.error(error_message)
245
246
  raise HTTPException(
246
247
  status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=error_message
247
248
  )
248
- finally:
249
- self.logger.info("OK - Mission successfully resumed")
250
249
 
251
- def stop_mission(self) -> ControlMissionResponse:
250
+ def stop_mission(self, mission_id: str = "") -> ControlMissionResponse:
252
251
  """Stop mission
253
252
 
254
253
  Raises
255
254
  ------
256
- HTTTPException 408 Request timeout
255
+ HTTPException 404 Not Found
256
+ If the mission_id was not known to Isar
257
+ HTTPException 503 Service Unavailable
258
+ The request was understood, but attempting to stop the mission failed
259
+ HTTPException 408 Request timeout
257
260
  If there is a timeout while communicating with the state machine
258
261
  """
259
262
  try:
260
263
  stop_mission_response: ControlMissionResponse = self._send_command(
261
- True, self.api_events.stop_mission
264
+ mission_id, self.api_events.stop_mission
262
265
  )
266
+
267
+ if stop_mission_response.mission_not_found:
268
+ error_message = f"Mission ID {stop_mission_response.mission_id} is not currently running"
269
+ self.logger.error(error_message)
270
+ raise HTTPException(
271
+ status_code=HTTPStatus.NOT_FOUND, detail=error_message
272
+ )
273
+
274
+ if stop_mission_response.mission_status != MissionStatus.Cancelled.value:
275
+ error_message = "Failed to stop mission"
276
+ self.logger.error(error_message)
277
+ raise HTTPException(
278
+ status_code=HTTPStatus.CONFLICT, detail=error_message
279
+ )
263
280
  except QueueTimeoutError:
264
281
  error_message = "Internal Server Error - Failed to stop mission"
265
282
  self.logger.error(error_message)
@@ -86,7 +86,7 @@ class Idle:
86
86
  return False
87
87
 
88
88
  def _check_and_handle_stop_mission_event(self, event: Queue) -> bool:
89
- if check_for_event(event):
89
+ if check_for_event(event) is not None:
90
90
  self.state_machine.stop() # type: ignore
91
91
  return True
92
92
  return False
@@ -6,6 +6,7 @@ from queue import Queue
6
6
  from threading import Event
7
7
  from typing import TYPE_CHECKING, Optional, Tuple
8
8
 
9
+ from isar.apis.models.models import ControlMissionResponse
9
10
  from isar.config.settings import settings
10
11
  from isar.models.communication.message import StartMissionMessage
11
12
  from isar.models.communication.queues.queue_utils import (
@@ -16,6 +17,7 @@ from isar.models.communication.queues.queue_utils import (
16
17
  from isar.services.utilities.threaded_request import ThreadedRequest
17
18
  from robot_interface.models.exceptions.robot_exceptions import (
18
19
  ErrorMessage,
20
+ ErrorReason,
19
21
  RobotException,
20
22
  RobotRetrieveInspectionException,
21
23
  )
@@ -54,12 +56,24 @@ class OngoingMission:
54
56
  self._run()
55
57
 
56
58
  def stop(self) -> None:
57
- self.state_machine.mission_ongoing = False
59
+ return
58
60
 
59
61
  def _check_and_handle_stop_mission_event(self, event: Queue) -> bool:
60
- if check_for_event(event):
61
- self.state_machine.stop() # type: ignore
62
- return True
62
+ mission_id: str = check_for_event(event)
63
+ if mission_id is not None:
64
+ if self.state_machine.current_mission.id == mission_id or mission_id == "":
65
+ self.state_machine.stop() # type: ignore
66
+ return True
67
+ else:
68
+ self.events.api_requests.stop_mission.output.put(
69
+ ControlMissionResponse(
70
+ mission_id=mission_id,
71
+ mission_status=self.state_machine.current_mission.status,
72
+ mission_not_found=True,
73
+ task_id=self.state_machine.current_task.id,
74
+ task_status=self.state_machine.current_task.status,
75
+ )
76
+ )
63
77
  return False
64
78
 
65
79
  def _check_and_handle_pause_mission_event(self, event: Queue) -> bool:
@@ -139,6 +153,10 @@ class OngoingMission:
139
153
 
140
154
  if self.state == OngoingMissionStates.ReturningHome:
141
155
  if status != TaskStatus.Successful:
156
+ self.state_machine.current_mission.error_message = ErrorMessage(
157
+ error_reason=ErrorReason.RobotActionException,
158
+ error_description="Return home failed.",
159
+ )
142
160
  self.state_machine.return_home_failed() # type: ignore
143
161
  return True
144
162
  self.state_machine.returned_home() # type: ignore
@@ -159,9 +177,6 @@ class OngoingMission:
159
177
  self.state_machine.mission_finished() # type: ignore
160
178
  return True
161
179
 
162
- # Report and update next task
163
- self.state_machine.current_task.update_task_status()
164
- self.state_machine.publish_task_status(task=self.state_machine.current_task)
165
180
  return False
166
181
 
167
182
  def _check_and_handle_start_mission_event(
@@ -5,7 +5,6 @@ from datetime import datetime, timezone
5
5
  from threading import Event
6
6
  from typing import Deque, List, Optional
7
7
 
8
- from dependency_injector.wiring import inject
9
8
  from transitions import Machine
10
9
  from transitions.core import State
11
10
 
@@ -48,7 +47,6 @@ from robot_interface.utilities.json_service import EnhancedJSONEncoder
48
47
  class StateMachine(object):
49
48
  """Handles state transitions for supervisory robot control."""
50
49
 
51
- @inject
52
50
  def __init__(
53
51
  self,
54
52
  events: Events,
@@ -194,6 +192,7 @@ class StateMachine(object):
194
192
  self.current_task = None
195
193
  self.send_task_status()
196
194
  self.current_mission = None
195
+ self.mission_ongoing = False
197
196
 
198
197
  def start_mission(self, mission: Mission):
199
198
  """Starts a scheduled mission."""
@@ -316,17 +315,13 @@ class StateMachine(object):
316
315
  self.logger.info("Mission overview:\n%s", log_statement)
317
316
 
318
317
  def _make_control_mission_response(self) -> ControlMissionResponse:
319
- if self.current_mission is None:
320
- raise ValueError("No current mission is set")
321
-
322
- if self.current_task is None:
323
- raise ValueError("No current task is set")
324
-
325
318
  return ControlMissionResponse(
326
- mission_id=self.current_mission.id,
327
- mission_status=self.current_mission.status,
328
- task_id=self.current_task.id,
329
- task_status=self.current_task.status,
319
+ mission_id=self.current_mission.id if self.current_mission else None,
320
+ mission_status=(
321
+ self.current_mission.status if self.current_mission else None
322
+ ),
323
+ task_id=self.current_task.id if self.current_task else None,
324
+ task_status=self.current_task.status if self.current_task else None,
330
325
  )
331
326
 
332
327
  def _queue_empty_response(self) -> None:
@@ -1,6 +1,5 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
- from dependency_injector.wiring import inject
4
3
  from transitions import State
5
4
 
6
5
  from isar.state_machine.generic_states.ongoing_mission import (
@@ -13,7 +12,6 @@ if TYPE_CHECKING:
13
12
 
14
13
 
15
14
  class Monitor(State, OngoingMission):
16
- @inject
17
15
  def __init__(self, state_machine: "StateMachine") -> None:
18
16
  State.__init__(self, name="monitor", on_enter=self.start, on_exit=self.stop)
19
17
 
@@ -39,9 +39,9 @@ class Stopping(State):
39
39
  if error_message is not None:
40
40
  self.logger.warning(error_message.error_description)
41
41
  if self.stopping_return_home_mission:
42
- self.state_machine.return_home_mission_stopped() # type: ignore
42
+ self.state_machine.return_home_mission_stopping_failed() # type: ignore
43
43
  else:
44
- self.state_machine.mission_stopped() # type: ignore
44
+ self.state_machine.mission_stopping_failed() # type: ignore
45
45
  return True
46
46
  return False
47
47
 
@@ -33,7 +33,7 @@ class UnknownStatus(State):
33
33
  return
34
34
 
35
35
  def _check_and_handle_stop_mission_event(self, event: Queue) -> bool:
36
- if check_for_event(event):
36
+ if check_for_event(event) is not None:
37
37
  self.state_machine.stop() # type: ignore
38
38
  return True
39
39
  return False
@@ -3,22 +3,51 @@ from typing import TYPE_CHECKING
3
3
  if TYPE_CHECKING:
4
4
  from isar.state_machine.state_machine import StateMachine
5
5
 
6
+ import time
7
+
6
8
  from isar.apis.models.models import ControlMissionResponse
9
+ from isar.config.settings import settings
10
+ from robot_interface.models.exceptions.robot_exceptions import (
11
+ RobotActionException,
12
+ RobotException,
13
+ )
7
14
  from robot_interface.models.mission.status import MissionStatus, TaskStatus
8
15
 
9
16
 
10
17
  def pause_mission(state_machine: "StateMachine") -> bool:
11
18
  state_machine.logger.info("Pausing mission: %s", state_machine.current_mission.id)
12
- state_machine.current_mission.status = MissionStatus.Paused
13
- state_machine.current_task.status = TaskStatus.Paused
14
19
 
15
- paused_mission_response: ControlMissionResponse = (
16
- state_machine._make_control_mission_response()
17
- )
18
- state_machine.events.api_requests.pause_mission.output.put(paused_mission_response)
20
+ max_retries = settings.STATE_TRANSITION_NUM_RETIRES
21
+ retry_interval = settings.STATE_TRANSITION_RETRY_INTERVAL_SEC
22
+
23
+ for attempt in range(max_retries):
24
+ try:
25
+ state_machine.robot.pause()
26
+ state_machine.current_mission.status = MissionStatus.Paused
27
+ state_machine.current_task.status = TaskStatus.Paused
28
+
29
+ paused_mission_response: ControlMissionResponse = (
30
+ state_machine._make_control_mission_response()
31
+ )
32
+ state_machine.events.api_requests.pause_mission.output.put(
33
+ paused_mission_response
34
+ )
35
+
36
+ state_machine.publish_mission_status()
37
+ state_machine.publish_task_status(task=state_machine.current_task)
19
38
 
20
- state_machine.publish_mission_status()
21
- state_machine.publish_task_status(task=state_machine.current_task)
39
+ state_machine.logger.info("Mission paused successfully.")
40
+ return True
41
+ except RobotActionException as e:
42
+ state_machine.logger.warning(
43
+ f"Attempt {attempt + 1} to pause mission failed: {e.error_description}"
44
+ )
45
+ time.sleep(retry_interval)
46
+ except RobotException as e:
47
+ state_machine.logger.warning(
48
+ f"Attempt {attempt + 1} to pause mission raised a RobotException: {e.error_description}"
49
+ )
50
+ time.sleep(retry_interval)
22
51
 
23
- state_machine.robot.pause()
24
- return True
52
+ state_machine.logger.error("Failed to pause mission after multiple attempts.")
53
+ return False
@@ -3,25 +3,54 @@ from typing import TYPE_CHECKING
3
3
  if TYPE_CHECKING:
4
4
  from isar.state_machine.state_machine import StateMachine
5
5
 
6
+ import time
7
+
6
8
  from isar.apis.models.models import ControlMissionResponse
9
+ from isar.config.settings import settings
10
+ from robot_interface.models.exceptions.robot_exceptions import (
11
+ RobotActionException,
12
+ RobotException,
13
+ )
7
14
  from robot_interface.models.mission.status import MissionStatus, TaskStatus
8
15
 
9
16
 
10
17
  def resume_mission(state_machine: "StateMachine") -> bool:
11
18
  state_machine.logger.info("Resuming mission: %s", state_machine.current_mission.id)
12
- state_machine.current_mission.status = MissionStatus.InProgress
13
- state_machine.current_mission.error_message = None
14
- state_machine.current_task.status = TaskStatus.InProgress
15
-
16
- state_machine.mission_ongoing = True
17
-
18
- state_machine.publish_mission_status()
19
- state_machine.publish_task_status(task=state_machine.current_task)
20
-
21
- resume_mission_response: ControlMissionResponse = (
22
- state_machine._make_control_mission_response()
23
- )
24
- state_machine.events.api_requests.resume_mission.output.put(resume_mission_response)
25
19
 
26
- state_machine.robot.resume()
27
- return True
20
+ max_retries = settings.STATE_TRANSITION_NUM_RETIRES
21
+ retry_interval = settings.STATE_TRANSITION_RETRY_INTERVAL_SEC
22
+
23
+ for attempt in range(max_retries):
24
+ try:
25
+ state_machine.robot.resume()
26
+ state_machine.current_mission.status = MissionStatus.InProgress
27
+ state_machine.current_mission.error_message = None
28
+ state_machine.current_task.status = TaskStatus.InProgress
29
+
30
+ state_machine.mission_ongoing = True
31
+
32
+ state_machine.publish_mission_status()
33
+ state_machine.publish_task_status(task=state_machine.current_task)
34
+
35
+ resume_mission_response: ControlMissionResponse = (
36
+ state_machine._make_control_mission_response()
37
+ )
38
+ state_machine.events.api_requests.resume_mission.output.put(
39
+ resume_mission_response
40
+ )
41
+
42
+ state_machine.logger.info("Mission resumed successfully.")
43
+ return True
44
+ except RobotActionException as e:
45
+ state_machine.logger.warning(
46
+ f"Attempt {attempt + 1} to resume mission failed: {e.error_description}"
47
+ )
48
+ time.sleep(retry_interval)
49
+ except RobotException as e:
50
+ state_machine.logger.warning(
51
+ f"Attempt {attempt + 1} to resume mission raised a RobotException: {e.error_description}"
52
+ )
53
+ time.sleep(retry_interval)
54
+
55
+ state_machine.logger.error("Failed to resume mission after multiple attempts.")
56
+ return False
@@ -1,6 +1,9 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
- from isar.models.communication.queues.queue_utils import trigger_event_without_data
3
+ from isar.models.communication.queues.queue_utils import (
4
+ check_for_event_without_consumption,
5
+ trigger_event_without_data,
6
+ )
4
7
 
5
8
  if TYPE_CHECKING:
6
9
  from isar.state_machine.state_machine import StateMachine
@@ -39,11 +42,51 @@ def stop_mission_cleanup(state_machine: "StateMachine") -> bool:
39
42
  return True
40
43
 
41
44
 
45
+ def stop_mission_failed(state_machine: "StateMachine") -> bool:
46
+ stopped_mission_response: ControlMissionResponse = (
47
+ state_machine._make_control_mission_response()
48
+ )
49
+ state_machine.events.api_requests.stop_mission.output.put(stopped_mission_response)
50
+ return True
51
+
52
+
42
53
  def stop_return_home_mission_cleanup(state_machine: "StateMachine") -> bool:
43
54
  if state_machine.current_mission is None:
44
55
  state_machine._queue_empty_response()
45
56
  state_machine.reset_state_machine()
46
57
  return True
47
58
 
59
+ if not check_for_event_without_consumption(
60
+ state_machine.events.api_requests.start_mission.input
61
+ ):
62
+ state_machine.current_mission.status = MissionStatus.Cancelled
63
+
64
+ for task in state_machine.current_mission.tasks:
65
+ if task.status in [
66
+ TaskStatus.NotStarted,
67
+ TaskStatus.InProgress,
68
+ TaskStatus.Paused,
69
+ ]:
70
+ task.status = TaskStatus.Cancelled
71
+
72
+ stopped_mission_response: ControlMissionResponse = (
73
+ state_machine._make_control_mission_response()
74
+ )
75
+ state_machine.events.api_requests.stop_mission.output.put(
76
+ stopped_mission_response
77
+ )
78
+
48
79
  state_machine._finalize()
49
80
  return True
81
+
82
+
83
+ def stop_return_home_mission_failed(state_machine: "StateMachine") -> bool:
84
+ if check_for_event_without_consumption(
85
+ state_machine.events.api_requests.start_mission.input
86
+ ):
87
+ return True
88
+ stopped_mission_response: ControlMissionResponse = (
89
+ state_machine._make_control_mission_response()
90
+ )
91
+ state_machine.events.api_requests.stop_mission.output.put(stopped_mission_response)
92
+ return True
@@ -15,7 +15,9 @@ from isar.state_machine.transitions.functions.start_mission import (
15
15
  )
16
16
  from isar.state_machine.transitions.functions.stop import (
17
17
  stop_mission_cleanup,
18
+ stop_mission_failed,
18
19
  stop_return_home_mission_cleanup,
20
+ stop_return_home_mission_failed,
19
21
  trigger_stop_mission_event,
20
22
  )
21
23
  from isar.state_machine.transitions.functions.utils import def_transition
@@ -30,13 +32,23 @@ def get_mission_transitions(state_machine: "StateMachine") -> List[dict]:
30
32
  "trigger": "pause",
31
33
  "source": state_machine.monitor_state,
32
34
  "dest": state_machine.paused_state,
33
- "before": def_transition(state_machine, pause_mission),
35
+ "conditions": def_transition(state_machine, pause_mission),
36
+ },
37
+ {
38
+ "trigger": "pause",
39
+ "source": state_machine.monitor_state,
40
+ "dest": state_machine.monitor_state,
34
41
  },
35
42
  {
36
43
  "trigger": "resume",
37
44
  "source": state_machine.paused_state,
38
45
  "dest": state_machine.monitor_state,
39
- "before": def_transition(state_machine, resume_mission),
46
+ "conditions": def_transition(state_machine, resume_mission),
47
+ },
48
+ {
49
+ "trigger": "resume",
50
+ "source": state_machine.paused_state,
51
+ "dest": state_machine.paused_state,
40
52
  },
41
53
  {
42
54
  "trigger": "stop",
@@ -56,6 +68,18 @@ def get_mission_transitions(state_machine: "StateMachine") -> List[dict]:
56
68
  "dest": state_machine.await_next_mission_state,
57
69
  "before": def_transition(state_machine, stop_mission_cleanup),
58
70
  },
71
+ {
72
+ "trigger": "mission_stopping_failed",
73
+ "source": state_machine.stopping_state,
74
+ "dest": state_machine.monitor_state,
75
+ "before": def_transition(state_machine, stop_mission_failed),
76
+ },
77
+ {
78
+ "trigger": "return_home_mission_stopping_failed",
79
+ "source": state_machine.stopping_state,
80
+ "dest": state_machine.returning_home_state,
81
+ "before": def_transition(state_machine, stop_return_home_mission_failed),
82
+ },
59
83
  {
60
84
  "trigger": "return_home_mission_stopped",
61
85
  "source": state_machine.stopping_state,
@@ -63,7 +63,7 @@ def get_return_home_transitions(state_machine: "StateMachine") -> List[dict]:
63
63
  "trigger": "return_home_failed",
64
64
  "source": state_machine.returning_home_state,
65
65
  "dest": state_machine.robot_standing_still_state,
66
- "before": def_transition(state_machine, return_home_finished),
66
+ "before": def_transition(state_machine, report_failed_mission_and_finalize),
67
67
  },
68
68
  ]
69
69
  return return_home_transitions
@@ -4,7 +4,6 @@ from typing import Union
4
4
 
5
5
  from azure.core.exceptions import ResourceExistsError
6
6
  from azure.storage.blob import BlobClient, BlobServiceClient, ContainerClient
7
- from dependency_injector.wiring import inject
8
7
 
9
8
  from isar.config.keyvault.keyvault_service import Keyvault
10
9
  from isar.config.settings import settings
@@ -15,7 +14,6 @@ from robot_interface.models.mission.mission import Mission
15
14
 
16
15
 
17
16
  class BlobStorage(StorageInterface):
18
- @inject
19
17
  def __init__(
20
18
  self, keyvault: Keyvault, container_name: str = settings.BLOB_CONTAINER
21
19
  ) -> None:
isar/storage/uploader.py CHANGED
@@ -6,8 +6,6 @@ from queue import Empty, Queue
6
6
  from threading import Event
7
7
  from typing import List, Union
8
8
 
9
- from dependency_injector.wiring import inject
10
-
11
9
  from isar.config.settings import settings
12
10
  from isar.models.communication.queues.events import Events
13
11
  from isar.storage.storage_interface import StorageException, StorageInterface
@@ -63,7 +61,6 @@ class BlobItem(UploaderQueueItem):
63
61
 
64
62
 
65
63
  class Uploader:
66
- @inject
67
64
  def __init__(
68
65
  self,
69
66
  events: Events,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: isar
3
- Version: 1.30.4
3
+ Version: 1.31.0
4
4
  Summary: Integration and Supervisory control of Autonomous Robots
5
5
  Author-email: Equinor ASA <fg_robots_dev@equinor.com>
6
6
  License: Eclipse Public License version 2.0
@@ -113,9 +113,6 @@ Requires-Dist: fastapi-azure-auth
113
113
  Requires-Dist: fastapi
114
114
  Requires-Dist: dependency-injector
115
115
  Requires-Dist: numpy
116
- Requires-Dist: opencensus-ext-azure
117
- Requires-Dist: opencensus-ext-logging
118
- Requires-Dist: opencensus-ext-requests
119
116
  Requires-Dist: paho-mqtt
120
117
  Requires-Dist: pydantic_settings
121
118
  Requires-Dist: pydantic
@@ -126,6 +123,12 @@ Requires-Dist: requests-toolbelt
126
123
  Requires-Dist: requests
127
124
  Requires-Dist: transitions
128
125
  Requires-Dist: uvicorn
126
+ Requires-Dist: opentelemetry-api
127
+ Requires-Dist: opentelemetry-sdk
128
+ Requires-Dist: opentelemetry-exporter-otlp
129
+ Requires-Dist: opentelemetry-instrumentation-fastapi
130
+ Requires-Dist: azure-monitor-opentelemetry
131
+ Requires-Dist: azure-monitor-opentelemetry-exporter>=1.0.0b38
129
132
  Provides-Extra: dev
130
133
  Requires-Dist: black; extra == "dev"
131
134
  Requires-Dist: isort; extra == "dev"
@@ -1,21 +1,22 @@
1
1
  isar/__init__.py,sha256=cH8p8bVveu3FUL6kBhldcSlLaoHgD82Kd0-SwSNfGXw,87
2
2
  isar/modules.py,sha256=QBB1pge1i17HVMLA5n-qd9K3APCrX9bFF2vlfjheOhU,4778
3
- isar/script.py,sha256=0oUUfytKDOsWrPv4iWemTWl5L3Ug66mQsbvqGqQrVWk,6050
3
+ isar/script.py,sha256=OC0O3y5HO5OAstg7g-tLKgF7mulKfglkpztDRk9PvT0,6159
4
4
  isar/apis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- isar/apis/api.py,sha256=2hggLxcdsHqTYDAfVUMwMIBgpuB-0ba2HWMscHkRGzA,15072
5
+ isar/apis/api.py,sha256=B0fVZPKtnvrxzk48xoazGPYsJVjjBH4IL6glDJ3PGWI,13328
6
6
  isar/apis/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- isar/apis/models/models.py,sha256=HzLaWhjAv0uJRBWipIgYg_F75eaQ5jl9Pi4UnYbDJ-M,1749
8
- isar/apis/models/start_mission_definition.py,sha256=S87i_vrgXTY1nTMTiXMJAnx_q5uVxuiRNcrQrAYWKeo,6152
9
- isar/apis/robot_control/robot_controller.py,sha256=7EVukShJDhYez12wYeGWZXH-BrfQ4Wu2so0-fSWN2Zo,1277
7
+ isar/apis/models/models.py,sha256=GMOss2C8lBeRFV7E37mLwSOM6RhiyLQLcLBRzm_51d4,1835
8
+ isar/apis/models/start_mission_definition.py,sha256=m9M5dZCmvRM1b4N8xYGquCSplSr4GDk376s2QoVL8F4,6231
9
+ isar/apis/robot_control/robot_controller.py,sha256=0EHjMqOBdfJlMrCItGPz5X-4X2kc-2XlNnUU2LOcLfc,1219
10
10
  isar/apis/schedule/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- isar/apis/schedule/scheduling_controller.py,sha256=ggoWjYKxgMwJ5trs8gsU8SGj6CQlmb7Ee1SjRg7hSTw,9671
11
+ isar/apis/schedule/scheduling_controller.py,sha256=yQM2HMgelBnbkzPR_tLHOY2EbThUcSO5jMA6Xtp5FQA,9915
12
12
  isar/apis/security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  isar/apis/security/authentication.py,sha256=Se2puhe2TUBmfio2RLma52-VSLRhqvWgu0Cd1bhdwMo,2000
14
14
  isar/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  isar/config/configuration_error.py,sha256=rO6WOhafX6xvVib8WxV-eY483Z0PpN-9PxGsq5ATfKc,46
16
- isar/config/log.py,sha256=SzEWbzXU1DpN7YONIRT8k0zBOGm_qVkXlJuuZtb8STc,2300
16
+ isar/config/log.py,sha256=_zeHrsI4XqoHc1Um50PLHFjlwGiClM_M5CzcXoqwZuY,1380
17
17
  isar/config/logging.conf,sha256=mYO1xf27gAopEMHhGzY7-mwyfN16rwRLkPNMvy3zn2g,1127
18
- isar/config/settings.py,sha256=dn9fEKX7GpzD9Y7q0TjQxW9Xf0iY-pCl3NV7bkxH1E8,12487
18
+ isar/config/open_telemetry.py,sha256=7hO6lR4lgK3IRZVh0a4shxIuamyt9paLM6d_FwXw5OY,2661
19
+ isar/config/settings.py,sha256=-fBxs0TUGmoCX4nF1Rwg0YMZ-WQ6qsqFZpWHxqwOrlk,13029
19
20
  isar/config/certs/ca-cert.pem,sha256=qoNljfad_qcMxhXJIUMLd7nT-Qwf_d4dYSdoOFEOE8I,2179
20
21
  isar/config/keyvault/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
22
  isar/config/keyvault/keyvault_error.py,sha256=zvPCsZLjboxsxthYkxpRERCTFxYV8R5WmACewAUQLwk,41
@@ -35,7 +36,7 @@ isar/config/predefined_missions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
35
36
  isar/config/predefined_missions/default.json,sha256=NWo9y5noPmpjlNUxLnZK95Sz7DIEaUR-ISYlw3MP8i0,1251
36
37
  isar/config/predefined_missions/default_turtlebot.json,sha256=8Vk1_0P0BBsG0vwh4vwIYINiiWioErHZ0Ppjq3ctaPM,2143
37
38
  isar/mission_planner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
- isar/mission_planner/local_planner.py,sha256=6JxAR_V2JB-pVzZ8xajRa1tlKO3d3x-5oe-5NodgkvU,2579
39
+ isar/mission_planner/local_planner.py,sha256=Mkg3vvUBF1jImfQnaFvXLNpKVadR21X4mwDd_wHqJ2w,2520
39
40
  isar/mission_planner/mission_planner_interface.py,sha256=UgpPIM4FbrWOD7fGY3Ul64k3uYb8wo0FwSWGewYoVbc,485
40
41
  isar/mission_planner/sequential_task_selector.py,sha256=66agRPHuJnEa1vArPyty4muTasAZ50XPjjrSaTdY_Cc,643
41
42
  isar/mission_planner/task_selector_interface.py,sha256=pnLeaGPIuyXThcflZ_A7YL2b2xQjFT88hAZidkMomxU,707
@@ -43,13 +44,13 @@ isar/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
44
  isar/models/communication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
45
  isar/models/communication/message.py,sha256=ge2EdUXRnYkiEu5TIAYJFQET_5w-N8MRgc2Y31vetno,155
45
46
  isar/models/communication/queues/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
- isar/models/communication/queues/events.py,sha256=o_oMos8Md6aTv91ymRNY0fNDWwj5QxWcBm6rxHQz5TY,2413
47
+ isar/models/communication/queues/events.py,sha256=mZt1ZarqmyDW_y42w7aD1Iv-m7HLwrpUyOh3pM4w79Q,2412
47
48
  isar/models/communication/queues/queue_io.py,sha256=AnHWUCkZ0tunkxKKeBarq-OUkRM97IaMfA-a1pmf1cQ,394
48
49
  isar/models/communication/queues/queue_timeout_error.py,sha256=rF8TlNF7RHS_ueTZ5mp7aFkhLY1j0dcwMwH-Ba6lVpE,45
49
- isar/models/communication/queues/queue_utils.py,sha256=wg6bIR3NS35Ek9hnGR5eN8z6i0jd5tOwM-8l30h3uiA,868
50
+ isar/models/communication/queues/queue_utils.py,sha256=3kw7LxMVxCUM7JF7DVQgxbiUzMBMtVa8nwfpr7xDSGc,872
50
51
  isar/models/communication/queues/status_queue.py,sha256=on8kvlNsG6MJjEVsyqtBswIpmOdOggQiKr7F5x0T3jw,514
51
52
  isar/models/mission_metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
- isar/robot/robot.py,sha256=h-Z_1xCACekHvaj-vPJAhdzixSlcW4zMpe9__ui0zv8,5490
53
+ isar/robot/robot.py,sha256=MmdrJzC4U_dGK3NLA6vgGNi0awNQwFvsPDsr9030fso,5431
53
54
  isar/robot/robot_start_mission.py,sha256=vQKsKnrWQuY70jEK-qeeaZYpXxQH4vElorczYZheXTQ,3423
54
55
  isar/robot/robot_status.py,sha256=FpkTIAA-O5vKuyU7fnWn2YXtbstq5qbk9XSxbn_0MXU,2015
55
56
  isar/robot/robot_stop_mission.py,sha256=jUyfemvbyigxrlIp9aKPn-PvarhagJEgajQPS_LgJ7g,2442
@@ -66,47 +67,47 @@ isar/services/service_connections/mqtt/robot_info_publisher.py,sha256=AxokGk51hR
66
67
  isar/services/service_connections/stid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
68
  isar/services/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
69
  isar/services/utilities/queue_utilities.py,sha256=Pw3hehSwkXJNeDv-bDVDfs58VOwtt3i5hpiJ2ZpphuQ,1225
69
- isar/services/utilities/robot_utilities.py,sha256=J3miRs0t74HNBoMIzVMceMCibp5DozP9Ai1_i7B3kWI,573
70
- isar/services/utilities/scheduling_utilities.py,sha256=SVmpAuU7101FVUx6x4tcMDrxYaWBPhgOkntFxX_d6Zc,10179
70
+ isar/services/utilities/robot_utilities.py,sha256=4zCigsLXfqDC8POHchktSq81zr1_pTaRve_LQsVr6Mk,514
71
+ isar/services/utilities/scheduling_utilities.py,sha256=eXeTEe4JmP2mgVd77DC5SLGTPjpG6O9IhN330dAyh-4,11122
71
72
  isar/services/utilities/threaded_request.py,sha256=py4G-_RjnIdHljmKFAcQ6ddqMmp-ZYV39Ece-dqRqjs,1874
72
73
  isar/state_machine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
- isar/state_machine/state_machine.py,sha256=onPjxGqDGSSvjHxCosKxIyiTw0X01FLwJX9rJArqfXg,13152
74
+ isar/state_machine/state_machine.py,sha256=6rksxhG4OLoZYfRSB5wfpqHXQUperbRjqwnY-6ue8Gk,13099
74
75
  isar/state_machine/states_enum.py,sha256=Z3dO6k6JmQAZ1lzcR58oVCxoCky_-ZS9MaL3RSaNbZ4,434
75
- isar/state_machine/generic_states/idle.py,sha256=T_LSadQ8Eu9egYPgL43WKDYv27MwFu-dqIxDazVE_KU,4359
76
- isar/state_machine/generic_states/ongoing_mission.py,sha256=NTYQIddTG0Nsq_ZjcWWBSe9KsQp_7zQcKH_0HYW18SQ,10850
76
+ isar/state_machine/generic_states/idle.py,sha256=uXVHtKeX3jPZC7iVmic1sKD6nLtSOVynrHNiUCdTDcw,4371
77
+ isar/state_machine/generic_states/ongoing_mission.py,sha256=ns6mbaSJvjM7JAbl1txPA9VdjxTv4iWKGbdYahtTtGc,11571
77
78
  isar/state_machine/generic_states/robot_unavailable.py,sha256=pHmed1uRPwCWAR4uBJmdnxi9WX8veTbGG9ephmROhm0,1851
78
79
  isar/state_machine/states/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
80
  isar/state_machine/states/await_next_mission.py,sha256=U0oGi-O05S-ZvdhpQRuirrY3ZBbblURo5U95PsscMlQ,573
80
81
  isar/state_machine/states/blocked_protective_stop.py,sha256=KDux2lm5kwEEarnDt9FG4MFrWbYUzs1_DX5hu2ILIlI,675
81
82
  isar/state_machine/states/home.py,sha256=2v7BFQn4QZ0npOrXpWLWCoHD5IpSyAJr-vQCS3MQWJs,514
82
- isar/state_machine/states/monitor.py,sha256=1PMirQaaQa_V-DEmbv-4dqlrB5q_XTg1Q1Gt--F1Bgw,655
83
+ isar/state_machine/states/monitor.py,sha256=BVrfZb-EWIdNYTC75F8HCi_OOtjBYbphrwki98p1WdE,597
83
84
  isar/state_machine/states/offline.py,sha256=vZnBMVDnehZ-0gPsHNVRpZtb8OBBFkeEnq2olo_Ep6M,609
84
85
  isar/state_machine/states/paused.py,sha256=IgnWBM6WXj3wjFZMARAPWIpzWGyUSW35DVBvNHuiNP8,1460
85
86
  isar/state_machine/states/returning_home.py,sha256=BBQo3PoNP-THlvJa5gQr0fNBhInh_Ox1L2OeH8evUq8,638
86
87
  isar/state_machine/states/robot_standing_still.py,sha256=RbOZSiQz72tWAJID5ksgzcb4lND6OniiG35IE1wMUHk,579
87
- isar/state_machine/states/stopping.py,sha256=Y44kEw-xB9_63eP6utAMY0RegrIz_1nL5cSI3cfaXEg,2838
88
- isar/state_machine/states/unknown_status.py,sha256=tOwfHW_4bKKkUTpKD7qc5gIwGT_fTXaCNxXcogGw-wo,2416
89
- isar/state_machine/transitions/mission.py,sha256=Sb_D9i1orV_I_eCDI0c8ZvhCU89peUqTfHI5-G36d08,4905
90
- isar/state_machine/transitions/return_home.py,sha256=LeOMnckUUQYzBYRidD2ge6M0KdxGDTgo-ioXR7_9r8A,2714
88
+ isar/state_machine/states/stopping.py,sha256=yJE7KT1aGo_qd1VTETfg1rTrlM-hKKQYKQp7xu4py_Y,2854
89
+ isar/state_machine/states/unknown_status.py,sha256=qfIoL5f7Hj-aZKq5tCM1FctkuEI6SwgpQuixzKx11Zk,2428
90
+ isar/state_machine/transitions/mission.py,sha256=Lsiee_hSn5wd7KWpmvIdaG-49f6HGdkCdiXAo5L0ciE,5803
91
+ isar/state_machine/transitions/return_home.py,sha256=6aBpKpozT48RJ__w13gv-r7dSz-fEOCzkAuKq5Hwm6k,2728
91
92
  isar/state_machine/transitions/robot_status.py,sha256=c1ceyWRGCtx-KTDtxHXRD7oPbt8TQ2ej24A0wyim8vc,2720
92
93
  isar/state_machine/transitions/functions/fail_mission.py,sha256=_6HqBMALDinFZ4yh5GMpeqqgV5tw5i8OVMj5UDdqesg,495
93
94
  isar/state_machine/transitions/functions/finish_mission.py,sha256=TRQrk7HdllmAkwsp25HRZAFAk46Y1hLx3jmkIAKrHDI,1442
94
- isar/state_machine/transitions/functions/pause.py,sha256=aoDkq2nV6wBY0YQX3KbjvBR1ojP2o_SCRT_gfz0BulI,889
95
- isar/state_machine/transitions/functions/resume.py,sha256=9KQjH_6YBGyxFhb7G5dgDe3WH0xHawhEIw6yTVEm9os,998
95
+ isar/state_machine/transitions/functions/pause.py,sha256=oaIFd4aZnbeaHb-EGQE9ozJctskqXKsRKkNc68M8sp0,1975
96
+ isar/state_machine/transitions/functions/resume.py,sha256=HgAEn4jQOPkVPWWZCAh7dqnIerdF8nGcgFcZ1KsCNSQ,2104
96
97
  isar/state_machine/transitions/functions/return_home.py,sha256=UlniwYvpz74hxqgN0TyVv3LCmiMsqsosKEtEGLqkNj0,1139
97
98
  isar/state_machine/transitions/functions/robot_status.py,sha256=xhKZ5u_X8uDvnhvGnAIABuKaPXeVqFjkgj4H2Om-j_A,1013
98
99
  isar/state_machine/transitions/functions/start_mission.py,sha256=ricRfhLH1_lNpqWxneMZcm7ps2YfY6sQGHkiT0Glf6M,2564
99
- isar/state_machine/transitions/functions/stop.py,sha256=aIj3EPnpgNLdsJwOK1ehhI1TpenQa9JjBxZH0Nm6dLg,1649
100
+ isar/state_machine/transitions/functions/stop.py,sha256=JK4hjGc3QtgSzvCWeOr1YG61SH1QOhmVNjw_jOnahQg,3123
100
101
  isar/state_machine/transitions/functions/utils.py,sha256=Wa72Ocq4QT1E6qkpEJZQ3h5o33pGvx7Tlkt2JZ2Grbk,314
101
102
  isar/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
- isar/storage/blob_storage.py,sha256=Qci6bO508nlTHKPuPtVU5QcvGA4T7mv8cFrKWRcfw4g,3226
103
+ isar/storage/blob_storage.py,sha256=D67y3Z939iXoRxs8npOuKOhgs2o6dYz2ivTm5IXXhJE,3168
103
104
  isar/storage/local_storage.py,sha256=Bnmoi5gyN8r-oRh0aHrOdGqaH3JqRScFKMRXYojW5kY,1855
104
105
  isar/storage/storage_interface.py,sha256=DYDry4I7aZpDHJhsBF6s8zrgokFAc7fdKJKfA8AvL7o,828
105
- isar/storage/uploader.py,sha256=HAC3ssuPQCQ1xH4aTQfHIaq-ZoEzKwONWmsAOpNXOzw,9523
106
+ isar/storage/uploader.py,sha256=1F4M34aVnD828WV62muTIE04vj3D52lrTOOh8r6SgwA,9464
106
107
  isar/storage/utilities.py,sha256=oLH0Rp7UtrQQdilfITnmXO1Z0ExdeDhBImYHid55vBA,3449
107
- isar-1.30.4.dist-info/licenses/LICENSE,sha256=3fc2-ebLwHWwzfQbulGNRdcNob3SBQeCfEVUDYxsuqw,14058
108
+ isar-1.31.0.dist-info/licenses/LICENSE,sha256=3fc2-ebLwHWwzfQbulGNRdcNob3SBQeCfEVUDYxsuqw,14058
108
109
  robot_interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
109
- robot_interface/robot_interface.py,sha256=Miout0IRX7QniUNu_upyNzkB8KDUWin7GGCqIeMh83E,8195
110
+ robot_interface/robot_interface.py,sha256=-jCAKkZ2eiyzUyHVQmOzw4hMgLWR7pE8MHj-WZo85ZY,7978
110
111
  robot_interface/test_robot_interface.py,sha256=FV1urn7SbsMyWBIcTKjsBwAG4IsXeZ6pLHE0mA9EGGs,692
111
112
  robot_interface/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
112
113
  robot_interface/models/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -117,7 +118,7 @@ robot_interface/models/inspection/inspection.py,sha256=2T8czQcNt9J1M96tKGQA6P3s5
117
118
  robot_interface/models/mission/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
118
119
  robot_interface/models/mission/mission.py,sha256=MQ9p5cuclLXexaZu9bkDh5-aN99eunvYC0vP-Z_kUwI,960
119
120
  robot_interface/models/mission/status.py,sha256=UOCARLfLxLFXJEjfIH7aXYXO7xajOKBJsxz-Wd6gZQ4,740
120
- robot_interface/models/mission/task.py,sha256=7g4Xu-2yn3A7JVqrqg-XHHIX1uwYuIrRB00GrOchjio,4820
121
+ robot_interface/models/mission/task.py,sha256=YzaqJ_KIIm-3S2Y2-BG4Pdkc8sjFMzMx5jj8FtXSmFg,4744
121
122
  robot_interface/models/robots/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
123
  robot_interface/models/robots/battery_state.py,sha256=ktOtJ8ltdK0k_i7BoqYfhc5dbOzIG6Oo-uWC67fCWio,98
123
124
  robot_interface/models/robots/media.py,sha256=8A-CuuubfngzPprs6zWB9hSaqe3jzgsE8rcCzRX2Uto,227
@@ -128,8 +129,8 @@ robot_interface/telemetry/payloads.py,sha256=PpvmV7XeGgfhc_aRUYFOdwBTwV2x8TwTBIN
128
129
  robot_interface/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
130
  robot_interface/utilities/json_service.py,sha256=qkzVkb60Gi_pto-b5n1vNzCrQze2yqgIJqSLNLYj1Fg,1034
130
131
  robot_interface/utilities/uuid_string_factory.py,sha256=_NQIbBQ56w0qqO0MUDP6aPpHbxW7ATRhK8HnQiBSLkc,76
131
- isar-1.30.4.dist-info/METADATA,sha256=SaISJES198lknEfIqlqwrZ1CZe4A0vzi_mKYuu1fZrU,31063
132
- isar-1.30.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
133
- isar-1.30.4.dist-info/entry_points.txt,sha256=TFam7uNNw7J0iiDYzsH2gfG0u1eV1wh3JTw_HkhgKLk,49
134
- isar-1.30.4.dist-info/top_level.txt,sha256=UwIML2RtuQKCyJJkatcSnyp6-ldDjboB9k9JgKipO-U,21
135
- isar-1.30.4.dist-info/RECORD,,
132
+ isar-1.31.0.dist-info/METADATA,sha256=RZo3tvmuEPeEdu1CQeqgKAQyWysrlJExNPMVn8zjqPs,31217
133
+ isar-1.31.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
134
+ isar-1.31.0.dist-info/entry_points.txt,sha256=TFam7uNNw7J0iiDYzsH2gfG0u1eV1wh3JTw_HkhgKLk,49
135
+ isar-1.31.0.dist-info/top_level.txt,sha256=UwIML2RtuQKCyJJkatcSnyp6-ldDjboB9k9JgKipO-U,21
136
+ isar-1.31.0.dist-info/RECORD,,
@@ -50,9 +50,6 @@ class Task(BaseModel):
50
50
  return True
51
51
  return False
52
52
 
53
- def update_task_status(self) -> TaskStatus:
54
- return self.status
55
-
56
53
 
57
54
  class InspectionTask(Task):
58
55
  """
@@ -221,10 +221,7 @@ class RobotInterface(metaclass=ABCMeta):
221
221
  @abstractmethod
222
222
  def robot_status(self) -> RobotStatus:
223
223
  """
224
- Method which returns an enum indicating if the robot package is able to reach
225
- the interface which is used to communicate with the robot. This is further used
226
- by ISAR to indicate whether the ISAR instance is fully functional and may be
227
- used by other systems.
224
+ Method which returns an enum indicating the status of the robot.
228
225
 
229
226
  Returns
230
227
  -------
File without changes