isar 1.26.2__py3-none-any.whl → 1.26.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of isar might be problematic. Click here for more details.

isar/apis/api.py CHANGED
@@ -5,10 +5,10 @@ from typing import List, Union
5
5
 
6
6
  import click
7
7
  import uvicorn
8
+ from dependency_injector.wiring import inject
8
9
  from fastapi import FastAPI, Request, Security
9
10
  from fastapi.middleware.cors import CORSMiddleware
10
11
  from fastapi.routing import APIRouter
11
- from injector import inject
12
12
  from opencensus.ext.azure.trace_exporter import AzureExporter
13
13
  from opencensus.trace.attributes_helper import COMMON_ATTRIBUTES
14
14
  from opencensus.trace.samplers import ProbabilitySampler
@@ -36,14 +36,14 @@ class API:
36
36
  authenticator: Authenticator,
37
37
  scheduling_controller: SchedulingController,
38
38
  robot_controller: RobotController,
39
- keyvault_client: Keyvault,
39
+ keyvault: Keyvault,
40
40
  port: int = settings.API_PORT,
41
41
  azure_ai_logging_enabled: bool = settings.LOG_HANDLER_APPLICATION_INSIGHTS_ENABLED,
42
42
  ) -> None:
43
43
  self.authenticator: Authenticator = authenticator
44
44
  self.scheduling_controller: SchedulingController = scheduling_controller
45
45
  self.robot_controller: RobotController = robot_controller
46
- self.keyvault_client: Keyvault = keyvault_client
46
+ self.keyvault: Keyvault = keyvault
47
47
  self.host: str = "0.0.0.0" # Locking uvicorn to use 0.0.0.0
48
48
  self.port: int = port
49
49
  self.azure_ai_logging_enabled: bool = azure_ai_logging_enabled
@@ -295,7 +295,7 @@ class API:
295
295
  def _add_request_logging_middleware(self, app: FastAPI) -> None:
296
296
  connection_string: str
297
297
  try:
298
- connection_string = self.keyvault_client.get_secret(
298
+ connection_string = self.keyvault.get_secret(
299
299
  "application-insights-connection-string"
300
300
  ).value
301
301
  except KeyvaultError:
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
 
3
+ from dependency_injector.wiring import inject
3
4
  from fastapi import HTTPException
4
- from injector import inject
5
5
 
6
6
  from isar.apis.models.models import RobotInfoResponse
7
7
  from isar.config.settings import robot_settings, settings
@@ -1,8 +1,8 @@
1
1
  import logging
2
2
  from http import HTTPStatus
3
3
 
4
+ from dependency_injector.wiring import inject
4
5
  from fastapi import Body, HTTPException, Path
5
- from injector import inject
6
6
 
7
7
  from isar.apis.models.models import (
8
8
  ControlMissionResponse,
@@ -3,7 +3,7 @@ import logging
3
3
  from fastapi import Depends
4
4
  from fastapi.security.base import SecurityBase
5
5
  from fastapi_azure_auth import SingleTenantAzureAuthorizationCodeBearer
6
- from fastapi_azure_auth.exceptions import InvalidAuth
6
+ from fastapi_azure_auth.exceptions import InvalidAuthHttp
7
7
  from fastapi_azure_auth.user import User
8
8
  from pydantic import BaseModel
9
9
 
@@ -35,7 +35,7 @@ async def validate_has_role(user: User = Depends(azure_scheme)) -> None:
35
35
  Raises a 403 authorization error if not.
36
36
  """
37
37
  if settings.REQUIRED_ROLE not in user.roles:
38
- raise InvalidAuth(
38
+ raise InvalidAuthHttp(
39
39
  "Current user does not possess the required role for this endpoint"
40
40
  )
41
41
 
isar/config/log.py CHANGED
@@ -56,7 +56,8 @@ def configure_azure_handler(log_config: dict, keyvault: Keyvault) -> logging.Han
56
56
  ).value
57
57
  except KeyvaultError:
58
58
  message: str = (
59
- f"CRITICAL ERROR: Missing connection string for Application Insights in key vault '{keyvault.name}'."
59
+ "CRITICAL ERROR: Missing connection string for"
60
+ f" Application Insights in key vault '{keyvault.name}'."
60
61
  )
61
62
  print(f"\n{message} \n")
62
63
  raise ConfigurationError(message)
@@ -2,7 +2,7 @@ import json
2
2
  import logging
3
3
  from pathlib import Path
4
4
 
5
- from injector import inject
5
+ from dependency_injector.wiring import inject
6
6
 
7
7
  from isar.config.settings import settings
8
8
  from isar.mission_planner.mission_planner_interface import (
isar/modules.py CHANGED
@@ -1,10 +1,7 @@
1
- import logging
1
+ import os
2
2
  from importlib import import_module
3
- from logging import Logger
4
- from types import ModuleType
5
- from typing import Dict, List, Tuple, Union
6
3
 
7
- from injector import Injector, Module, multiprovider, provider, singleton
4
+ from dependency_injector import containers, providers
8
5
 
9
6
  from isar.apis.api import API
10
7
  from isar.apis.robot_control.robot_controller import RobotController
@@ -13,10 +10,10 @@ from isar.apis.security.authentication import Authenticator
13
10
  from isar.config.keyvault.keyvault_service import Keyvault
14
11
  from isar.config.settings import settings
15
12
  from isar.mission_planner.local_planner import LocalPlanner
16
- from isar.mission_planner.mission_planner_interface import MissionPlannerInterface
17
13
  from isar.mission_planner.sequential_task_selector import SequentialTaskSelector
18
14
  from isar.mission_planner.task_selector_interface import TaskSelectorInterface
19
15
  from isar.models.communication.queues.events import Events, SharedState
16
+ from isar.robot.robot import Robot
20
17
  from isar.services.service_connections.request_handler import RequestHandler
21
18
  from isar.services.utilities.robot_utilities import RobotUtilities
22
19
  from isar.services.utilities.scheduling_utilities import SchedulingUtilities
@@ -24,224 +21,118 @@ from isar.state_machine.state_machine import StateMachine
24
21
  from isar.storage.blob_storage import BlobStorage
25
22
  from isar.storage.local_storage import LocalStorage
26
23
  from isar.storage.slimm_storage import SlimmStorage
27
- from isar.storage.storage_interface import StorageInterface
28
24
  from isar.storage.uploader import Uploader
29
- from robot_interface.robot_interface import RobotInterface
30
- from robot_interface.telemetry.mqtt_client import MqttClientInterface, MqttPublisher
31
-
32
-
33
- class APIModule(Module):
34
- @provider
35
- @singleton
36
- def provide_api(
37
- self,
38
- authenticator: Authenticator,
39
- scheduling_controller: SchedulingController,
40
- robot_controller: RobotController,
41
- keyvault: Keyvault,
42
- ) -> API:
43
- return API(authenticator, scheduling_controller, robot_controller, keyvault)
44
-
45
- @provider
46
- @singleton
47
- def provide_scheduling_controller(
48
- self,
49
- scheduling_utilities: SchedulingUtilities,
50
- ) -> SchedulingController:
51
- return SchedulingController(scheduling_utilities)
52
-
53
- @provider
54
- @singleton
55
- def provide_robot_controller(
56
- self,
57
- robot_utilities: RobotUtilities,
58
- ) -> RobotController:
59
- return RobotController(robot_utilities)
60
-
61
-
62
- class AuthenticationModule(Module):
63
- @provider
64
- @singleton
65
- def provide_authenticator(self) -> Authenticator:
66
- return Authenticator()
67
-
68
-
69
- class RobotModule(Module):
70
- @provider
71
- @singleton
72
- def provide_robot_interface(self) -> RobotInterface:
73
- robot_interface: ModuleType = import_module(
74
- f"{settings.ROBOT_PACKAGE}.robotinterface"
25
+ from robot_interface.telemetry.mqtt_client import MqttPublisher
26
+
27
+
28
+ class ApplicationContainer(containers.DeclarativeContainer):
29
+ config = providers.Configuration(pydantic_settings=[settings])
30
+
31
+ # Core services
32
+ keyvault = providers.Singleton(
33
+ Keyvault,
34
+ keyvault_name=settings.KEYVAULT_NAME,
35
+ client_id=settings.AZURE_CLIENT_ID,
36
+ client_secret=os.environ.get("AZURE_CLIENT_SECRET"),
37
+ tenant_id=settings.AZURE_TENANT_ID,
38
+ )
39
+
40
+ # Events and shared state
41
+ events = providers.Singleton(Events)
42
+ shared_state = providers.Singleton(SharedState)
43
+
44
+ # Robot-related services
45
+ robot_interface = providers.Singleton(
46
+ lambda: import_module(f"{settings.ROBOT_PACKAGE}.robotinterface").Robot()
47
+ )
48
+ robot_utilities = providers.Singleton(RobotUtilities, robot=robot_interface)
49
+
50
+ # API and controllers
51
+ authenticator = providers.Singleton(Authenticator)
52
+ scheduling_utilities = providers.Singleton(
53
+ SchedulingUtilities,
54
+ events=events,
55
+ shared_state=shared_state,
56
+ mission_planner=providers.Singleton(LocalPlanner),
57
+ )
58
+ scheduling_controller = providers.Singleton(
59
+ SchedulingController, scheduling_utilities=scheduling_utilities
60
+ )
61
+ robot_controller = providers.Singleton(
62
+ RobotController, robot_utilities=robot_utilities
63
+ )
64
+ api = providers.Singleton(
65
+ API,
66
+ authenticator=authenticator,
67
+ scheduling_controller=scheduling_controller,
68
+ robot_controller=robot_controller,
69
+ keyvault=keyvault,
70
+ )
71
+
72
+ # Storage
73
+ local_storage = providers.Singleton(LocalStorage)
74
+ blob_storage = providers.Singleton(BlobStorage, keyvault=keyvault)
75
+ slimm_storage = providers.Singleton(
76
+ SlimmStorage, request_handler=providers.Singleton(RequestHandler)
77
+ )
78
+ storage_handlers = providers.List(local_storage, blob_storage, slimm_storage)
79
+
80
+ # Mqtt client
81
+ mqtt_client = (
82
+ providers.Singleton(
83
+ MqttPublisher,
84
+ mqtt_queue=providers.Callable(events.provided.mqtt_queue),
75
85
  )
76
- return robot_interface.Robot() # type: ignore
77
-
78
-
79
- class EventsModule(Module):
80
- @provider
81
- @singleton
82
- def provide_events(self) -> Events:
83
- return Events()
84
-
85
-
86
- class SharedStateModule(Module):
87
- @provider
88
- @singleton
89
- def provide_shared_state(self) -> SharedState:
90
- return SharedState()
91
-
92
-
93
- class RequestHandlerModule(Module):
94
- @provider
95
- @singleton
96
- def provide_request_handler(self) -> RequestHandler:
97
- return RequestHandler()
98
-
99
-
100
- class BlobStorageModule(Module):
101
- @multiprovider
102
- @singleton
103
- def provide_blob_storage(self, keyvault: Keyvault) -> List[StorageInterface]:
104
- return [BlobStorage(keyvault)]
105
-
106
-
107
- class LocalStorageModule(Module):
108
- @multiprovider
109
- @singleton
110
- def provide_local_storage(self) -> List[StorageInterface]:
111
- return [LocalStorage()]
112
-
113
-
114
- class SlimmStorageModule(Module):
115
- @multiprovider
116
- @singleton
117
- def provide_slimm_storage(
118
- self, request_handler: RequestHandler
119
- ) -> List[StorageInterface]:
120
- return [SlimmStorage(request_handler=request_handler)]
121
-
122
-
123
- class LocalPlannerModule(Module):
124
- @provider
125
- @singleton
126
- def provide_local_planner(self) -> MissionPlannerInterface:
127
- return LocalPlanner()
128
-
129
-
130
- class StateMachineModule(Module):
131
- @provider
132
- @singleton
133
- def provide_state_machine(
134
- self,
135
- events: Events,
136
- shared_state: SharedState,
137
- robot: RobotInterface,
138
- mqtt_client: MqttClientInterface,
139
- task_selector: TaskSelectorInterface,
140
- ) -> StateMachine:
141
- return StateMachine(
142
- events=events,
143
- shared_state=shared_state,
144
- robot=robot,
145
- mqtt_publisher=mqtt_client,
146
- task_selector=task_selector,
147
- )
148
-
149
-
150
- class UploaderModule(Module):
151
- @provider
152
- @singleton
153
- def provide_uploader(
154
- self,
155
- events: Events,
156
- storage_handlers: List[StorageInterface],
157
- mqtt_client: MqttClientInterface,
158
- ) -> Uploader:
159
- return Uploader(
160
- events=events,
161
- storage_handlers=storage_handlers,
162
- mqtt_publisher=mqtt_client,
163
- )
164
-
165
-
166
- class SchedulingUtilitiesModule(Module):
167
- @provider
168
- @singleton
169
- def provide_scheduling_utilities(
170
- self,
171
- events: Events,
172
- shared_state: SharedState,
173
- mission_planner: MissionPlannerInterface,
174
- ) -> SchedulingUtilities:
175
- return SchedulingUtilities(events, shared_state, mission_planner)
176
-
177
-
178
- class RobotUtilitiesModule(Module):
179
- @provider
180
- @singleton
181
- def provide_robot_utilities(self, robot: RobotInterface) -> RobotUtilities:
182
- return RobotUtilities(robot)
183
-
184
-
185
- class ServiceModule(Module):
186
- @provider
187
- @singleton
188
- def provide_keyvault(self) -> Keyvault:
189
- return Keyvault(keyvault_name=settings.KEYVAULT_NAME)
190
-
191
-
192
- class MqttModule(Module):
193
- @provider
194
- @singleton
195
- def provide_mqtt_client(self, events: Events) -> MqttClientInterface:
196
- if settings.MQTT_ENABLED:
197
- return MqttPublisher(mqtt_queue=events.mqtt_queue)
198
- return None
199
-
200
-
201
- class SequentialTaskSelectorModule(Module):
202
- @provider
203
- @singleton
204
- def provide_task_selector(self) -> TaskSelectorInterface:
205
- return SequentialTaskSelector()
206
-
207
-
208
- modules: Dict[str, Tuple[Module, Union[str, bool]]] = {
209
- "api": (APIModule, "required"),
210
- "authentication": (AuthenticationModule, "required"),
211
- "events": (EventsModule, "required"),
212
- "shared_state": (SharedStateModule, "required"),
213
- "request_handler": (RequestHandlerModule, "required"),
214
- "robot_package": (RobotModule, settings.ROBOT_PACKAGE),
215
- "isar_id": (RobotModule, settings.ISAR_ID),
216
- "robot_name": (RobotModule, settings.ROBOT_NAME),
217
- "mission_planner": (LocalPlannerModule, settings.MISSION_PLANNER),
218
- "task_selector": (
219
- {"sequential": SequentialTaskSelectorModule}[settings.TASK_SELECTOR],
220
- settings.TASK_SELECTOR,
221
- ),
222
- "service": (ServiceModule, "required"),
223
- "state_machine": (StateMachineModule, "required"),
224
- "storage_local": (LocalStorageModule, settings.STORAGE_LOCAL_ENABLED),
225
- "storage_blob": (BlobStorageModule, settings.STORAGE_BLOB_ENABLED),
226
- "storage_slimm": (SlimmStorageModule, settings.STORAGE_SLIMM_ENABLED),
227
- "mqtt": (MqttModule, "required"),
228
- "utilities": (SchedulingUtilitiesModule, "required"),
229
- "robot_utilities": (RobotUtilitiesModule, "required"),
230
- }
231
-
232
-
233
- def get_injector() -> Injector:
234
- injector_modules: List[Module] = []
235
- module_overview: str = ""
236
-
237
- for category, (module, config_option) in modules.items():
238
- if config_option:
239
- injector_modules.append(module)
240
- module_overview += (
241
- f"\n {category:<15} : {config_option:<20} ({module.__name__})"
242
- )
243
-
244
- logger: Logger = logging.getLogger("modules")
245
- logger.info("Loaded the following module configurations: %s", module_overview)
246
-
247
- return Injector(injector_modules)
86
+ if settings.MQTT_ENABLED
87
+ else None
88
+ )
89
+
90
+ # State machine
91
+ task_selector = providers.Singleton(
92
+ SequentialTaskSelector
93
+ if settings.TASK_SELECTOR == "sequential"
94
+ else TaskSelectorInterface
95
+ )
96
+
97
+ state_machine = providers.Singleton(
98
+ StateMachine,
99
+ events=events,
100
+ shared_state=shared_state,
101
+ robot=robot_interface,
102
+ mqtt_publisher=mqtt_client,
103
+ task_selector=task_selector,
104
+ )
105
+
106
+ # Robot
107
+ robot = providers.Singleton(
108
+ Robot, events=events, robot=robot_interface, shared_state=shared_state
109
+ )
110
+
111
+ # Uploader
112
+ uploader = providers.Singleton(
113
+ Uploader,
114
+ events=events,
115
+ storage_handlers=storage_handlers,
116
+ mqtt_publisher=mqtt_client,
117
+ )
118
+
119
+
120
+ def get_injector() -> ApplicationContainer:
121
+ container = ApplicationContainer()
122
+ container.init_resources()
123
+ container.wire(modules=[__name__])
124
+ container.config.from_dict(
125
+ {
126
+ "KEYVAULT_NAME": settings.KEYVAULT_NAME,
127
+ "MQTT_ENABLED": settings.MQTT_ENABLED,
128
+ "TASK_SELECTOR": settings.TASK_SELECTOR,
129
+ }
130
+ )
131
+
132
+ print("Loaded the following module configurations:")
133
+ for provider_name, provider in container.providers.items():
134
+ provider_repr = repr(provider)
135
+ simplified_provider = provider_repr.split(".")[-1].split(">")[0]
136
+ print(f" {provider_name:<20}: {simplified_provider}")
137
+
138
+ return container
isar/robot/robot.py CHANGED
@@ -3,7 +3,7 @@ from queue import Queue
3
3
  from threading import Event
4
4
  from typing import Optional
5
5
 
6
- from injector import inject
6
+ from dependency_injector.wiring import inject
7
7
 
8
8
  from isar.models.communication.queues.events import (
9
9
  Events,
@@ -20,11 +20,10 @@ from robot_interface.robot_interface import RobotInterface
20
20
 
21
21
 
22
22
  class Robot(object):
23
-
24
23
  @inject
25
24
  def __init__(
26
25
  self, events: Events, robot: RobotInterface, shared_state: SharedState
27
- ):
26
+ ) -> None:
28
27
  self.logger = logging.getLogger("robot")
29
28
  self.state_machine_events: StateMachineEvents = events.state_machine_events
30
29
  self.robot_service_events: RobotServiceEvents = events.robot_service_events
@@ -107,10 +106,7 @@ class Robot(object):
107
106
  )
108
107
  self.robot_status_thread.start()
109
108
 
110
- while True:
111
- if self.signal_thread_quitting.is_set():
112
- break
113
-
109
+ while not self.signal_thread_quitting.wait(0):
114
110
  self._check_and_handle_start_mission(
115
111
  self.state_machine_events.start_mission
116
112
  )
@@ -17,7 +17,6 @@ from robot_interface.robot_interface import RobotInterface
17
17
 
18
18
 
19
19
  class RobotStartMissionThread(Thread):
20
-
21
20
  def __init__(
22
21
  self,
23
22
  robot_service_events: RobotServiceEvents,
@@ -9,7 +9,6 @@ from robot_interface.robot_interface import RobotInterface
9
9
 
10
10
 
11
11
  class RobotStatusThread(Thread):
12
-
13
12
  def __init__(
14
13
  self,
15
14
  robot: RobotInterface,
@@ -21,7 +20,7 @@ class RobotStatusThread(Thread):
21
20
  self.robot: RobotInterface = robot
22
21
  self.signal_thread_quitting: Event = signal_thread_quitting
23
22
  self.last_robot_status_poll_time: float = time.time()
24
- Thread.__init__(self, name="Robot status thread", daemon=True)
23
+ Thread.__init__(self, name="Robot status thread")
25
24
 
26
25
  def stop(self) -> None:
27
26
  return
@@ -35,10 +34,10 @@ class RobotStatusThread(Thread):
35
34
  )
36
35
 
37
36
  def run(self):
38
- while True:
39
- if self.signal_thread_quitting.is_set():
40
- break
37
+ if self.signal_thread_quitting.is_set():
38
+ return
41
39
 
40
+ while not self.signal_thread_quitting.wait(0.001):
42
41
  if not self._is_ready_to_poll_for_status():
43
42
  continue
44
43
 
@@ -18,7 +18,6 @@ from robot_interface.robot_interface import RobotInterface
18
18
 
19
19
 
20
20
  class RobotStopMissionThread(Thread):
21
-
22
21
  def __init__(
23
22
  self,
24
23
  robot_service_events: RobotServiceEvents,
@@ -19,7 +19,6 @@ from robot_interface.robot_interface import RobotInterface
19
19
 
20
20
 
21
21
  class RobotTaskStatusThread(Thread):
22
-
23
22
  def __init__(
24
23
  self,
25
24
  robot_service_events: RobotServiceEvents,
isar/script.py CHANGED
@@ -5,15 +5,12 @@ from logging import Logger
5
5
  from threading import Thread
6
6
  from typing import Any, List, Tuple
7
7
 
8
- from injector import Injector
9
-
10
8
  import isar
11
9
  from isar.apis.api import API
12
- from isar.config.keyvault.keyvault_service import Keyvault
13
10
  from isar.config.log import setup_loggers
14
11
  from isar.config.settings import robot_settings, settings
15
12
  from isar.models.communication.queues.events import Events
16
- from isar.modules import get_injector
13
+ from isar.modules import ApplicationContainer, get_injector
17
14
  from isar.robot.robot import Robot
18
15
  from isar.services.service_connections.mqtt.mqtt_client import MqttClient
19
16
  from isar.services.service_connections.mqtt.robot_heartbeat_publisher import (
@@ -83,19 +80,19 @@ def print_startup_info():
83
80
 
84
81
 
85
82
  def start() -> None:
86
- injector: Injector = get_injector()
83
+ injector: ApplicationContainer = get_injector()
87
84
 
88
- keyvault_client = injector.get(Keyvault)
89
- setup_loggers(keyvault=keyvault_client)
85
+ keyvault = injector.keyvault()
86
+ setup_loggers(keyvault=keyvault)
90
87
  logger: Logger = logging.getLogger("main")
91
88
 
92
89
  print_startup_info()
93
90
 
94
- state_machine: StateMachine = injector.get(StateMachine)
95
- uploader: Uploader = injector.get(Uploader)
96
- robot: RobotInterface = injector.get(RobotInterface)
97
- events: Events = injector.get(Events)
98
- robot_service: Robot = injector.get(Robot)
91
+ state_machine: StateMachine = injector.state_machine()
92
+ uploader: Uploader = injector.uploader()
93
+ robot_interface: RobotInterface = injector.robot_interface()
94
+ events: Events = injector.events()
95
+ robot: Robot = injector.robot()
99
96
 
100
97
  threads: List[Thread] = []
101
98
 
@@ -110,7 +107,7 @@ def start() -> None:
110
107
  threads.append(uploader_thread)
111
108
 
112
109
  robot_service_thread: Thread = Thread(
113
- target=robot_service.run, name="Robot service", daemon=True
110
+ target=robot.run, name="Robot service", daemon=True
114
111
  )
115
112
  threads.append(robot_service_thread)
116
113
 
@@ -123,7 +120,7 @@ def start() -> None:
123
120
  )
124
121
  state_machine.events.upload_queue.put(message)
125
122
 
126
- robot.register_inspection_callback(inspections_callback)
123
+ robot_interface.register_inspection_callback(inspections_callback)
127
124
 
128
125
  if settings.MQTT_ENABLED:
129
126
  mqtt_client: MqttClient = MqttClient(mqtt_queue=events.mqtt_queue)
@@ -154,7 +151,7 @@ def start() -> None:
154
151
  )
155
152
  threads.append(robot_heartbeat_thread)
156
153
 
157
- publishers: List[Thread] = robot.get_telemetry_publishers(
154
+ publishers: List[Thread] = robot_interface.get_telemetry_publishers(
158
155
  queue=events.mqtt_queue,
159
156
  robot_name=settings.ROBOT_NAME,
160
157
  isar_id=settings.ISAR_ID,
@@ -163,7 +160,7 @@ def start() -> None:
163
160
  if publishers:
164
161
  threads.extend(publishers)
165
162
 
166
- api: API = injector.get(API)
163
+ api: API = injector.api()
167
164
  api_thread: Thread = Thread(target=api.run_app, name="ISAR API", daemon=True)
168
165
  threads.append(api_thread)
169
166
 
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
 
3
- from injector import inject
3
+ from dependency_injector.wiring import inject
4
4
 
5
5
  from robot_interface.models.robots.media import MediaConfig
6
6
  from robot_interface.robot_interface import RobotInterface
@@ -4,8 +4,8 @@ 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
7
8
  from fastapi import HTTPException
8
- from injector import inject
9
9
  from requests import HTTPError
10
10
 
11
11
  from isar.apis.models.models import ControlMissionResponse
@@ -2,9 +2,10 @@ import json
2
2
  import logging
3
3
  from collections import deque
4
4
  from datetime import datetime, timezone
5
+ from threading import Event
5
6
  from typing import Deque, List, Optional
6
7
 
7
- from injector import inject
8
+ from dependency_injector.wiring import inject
8
9
  from transitions import Machine
9
10
  from transitions.core import State
10
11
 
@@ -97,6 +98,8 @@ class StateMachine(object):
97
98
  self.mqtt_publisher: Optional[MqttClientInterface] = mqtt_publisher
98
99
  self.task_selector: TaskSelectorInterface = task_selector
99
100
 
101
+ self.signal_state_machine_to_stop: Event = Event()
102
+
100
103
  # List of states
101
104
  self.stop_state: State = Stop(self)
102
105
  self.paused_state: State = Paused(self)
@@ -237,6 +240,10 @@ class StateMachine(object):
237
240
  """Starts the state machine. Transitions into idle state."""
238
241
  self.to_idle() # type: ignore
239
242
 
243
+ def terminate(self):
244
+ self.logger.info("Stopping state machine")
245
+ self.signal_state_machine_to_stop.set()
246
+
240
247
  def iterate_current_task(self):
241
248
  if self.current_task.is_finished():
242
249
  try:
@@ -21,6 +21,7 @@ class BlockedProtectiveStop(State):
21
21
  self.logger = logging.getLogger("state_machine")
22
22
  self.robot_status_thread: Optional[ThreadedRequest] = None
23
23
  self.shared_state = self.state_machine.shared_state
24
+ self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
24
25
 
25
26
  def start(self) -> None:
26
27
  self.state_machine.update_state()
@@ -31,8 +32,15 @@ class BlockedProtectiveStop(State):
31
32
 
32
33
  def _run(self) -> None:
33
34
  while True:
34
- robot_status = check_shared_state(self.shared_state.robot_status)
35
+ if self.signal_state_machine_to_stop.is_set():
36
+ self.logger.info(
37
+ "Stopping state machine from %s state", self.__class__.__name__
38
+ )
39
+ break
35
40
 
41
+ robot_status: RobotStatus = check_shared_state(
42
+ self.shared_state.robot_status
43
+ )
36
44
  if robot_status == RobotStatus.Offline:
37
45
  transition = self.state_machine.robot_turned_offline # type: ignore
38
46
  break
@@ -25,6 +25,7 @@ class Idle(State):
25
25
  self.last_robot_status_poll_time: float = time.time()
26
26
  self.events = self.state_machine.events
27
27
  self.shared_state = self.state_machine.shared_state
28
+ self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
28
29
 
29
30
  def start(self) -> None:
30
31
  self.state_machine.update_state()
@@ -63,6 +64,12 @@ class Idle(State):
63
64
 
64
65
  def _run(self) -> None:
65
66
  while True:
67
+ if self.signal_state_machine_to_stop.is_set():
68
+ self.logger.info(
69
+ "Stopping state machine from %s state", self.__class__.__name__
70
+ )
71
+ break
72
+
66
73
  if self._check_and_handle_stop_mission_event(
67
74
  self.events.api_requests.stop_mission.input
68
75
  ):
@@ -2,9 +2,10 @@ import logging
2
2
  import time
3
3
  from copy import deepcopy
4
4
  from queue import Queue
5
+ from threading import Event
5
6
  from typing import TYPE_CHECKING, Optional, Tuple
6
7
 
7
- from injector import inject
8
+ from dependency_injector.wiring import inject
8
9
  from transitions import State
9
10
 
10
11
  from isar.config.settings import settings
@@ -35,6 +36,10 @@ class Monitor(State):
35
36
 
36
37
  self.awaiting_task_status: bool = False
37
38
 
39
+ self.signal_state_machine_to_stop: Event = (
40
+ state_machine.signal_state_machine_to_stop
41
+ )
42
+
38
43
  def start(self) -> None:
39
44
  self.state_machine.update_state()
40
45
  self._run()
@@ -139,6 +144,12 @@ class Monitor(State):
139
144
  def _run(self) -> None:
140
145
  self.awaiting_task_status = False
141
146
  while True:
147
+ if self.signal_state_machine_to_stop.is_set():
148
+ self.logger.info(
149
+ "Stopping state machine from %s state", self.__class__.__name__
150
+ )
151
+ break
152
+
142
153
  if self._check_and_handle_stop_mission_event(
143
154
  self.events.api_requests.stop_mission.input
144
155
  ):
@@ -17,6 +17,7 @@ class Offline(State):
17
17
  self.state_machine: "StateMachine" = state_machine
18
18
  self.logger = logging.getLogger("state_machine")
19
19
  self.shared_state = self.state_machine.shared_state
20
+ self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
20
21
 
21
22
  def start(self) -> None:
22
23
  self.state_machine.update_state()
@@ -27,6 +28,12 @@ class Offline(State):
27
28
 
28
29
  def _run(self) -> None:
29
30
  while True:
31
+ if self.signal_state_machine_to_stop.is_set():
32
+ self.logger.info(
33
+ "Stopping state machine from %s state", self.__class__.__name__
34
+ )
35
+ break
36
+
30
37
  robot_status: RobotStatus = check_shared_state(
31
38
  self.shared_state.robot_status
32
39
  )
@@ -16,6 +16,7 @@ class Paused(State):
16
16
  self.state_machine: "StateMachine" = state_machine
17
17
  self.logger = logging.getLogger("state_machine")
18
18
  self.events = self.state_machine.events
19
+ self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
19
20
 
20
21
  def start(self) -> None:
21
22
  self.state_machine.update_state()
@@ -24,6 +25,12 @@ class Paused(State):
24
25
  def _run(self) -> None:
25
26
  transition: Callable
26
27
  while True:
28
+ if self.signal_state_machine_to_stop.is_set():
29
+ self.logger.info(
30
+ "Stopping state machine from %s state", self.__class__.__name__
31
+ )
32
+ break
33
+
27
34
  if check_for_event(self.events.api_requests.pause_mission.input):
28
35
  transition = self.state_machine.stop # type: ignore
29
36
  break
@@ -21,6 +21,7 @@ class Stop(State):
21
21
  self.events = self.state_machine.events
22
22
  self.stop_thread: Optional[ThreadedRequest] = None
23
23
  self._count_number_retries: int = 0
24
+ self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
24
25
 
25
26
  def start(self) -> None:
26
27
  self.state_machine.update_state()
@@ -48,6 +49,11 @@ class Stop(State):
48
49
 
49
50
  def _run(self) -> None:
50
51
  while True:
52
+ if self.signal_state_machine_to_stop.is_set():
53
+ self.logger.info(
54
+ "Stopping state machine from %s state", self.__class__.__name__
55
+ )
56
+ break
51
57
 
52
58
  if self._check_and_handle_failed_stop(
53
59
  self.events.robot_service_events.mission_failed_to_stop
@@ -4,7 +4,7 @@ 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 injector import inject
7
+ from dependency_injector.wiring import inject
8
8
 
9
9
  from isar.config.keyvault.keyvault_service import Keyvault
10
10
  from isar.config.settings import settings
@@ -2,7 +2,7 @@ import json
2
2
  import logging
3
3
 
4
4
  from azure.identity import DefaultAzureCredential
5
- from injector import inject
5
+ from dependency_injector.wiring import inject
6
6
  from requests import HTTPError, RequestException
7
7
  from requests_toolbelt import MultipartEncoder
8
8
 
isar/storage/uploader.py CHANGED
@@ -3,9 +3,10 @@ import logging
3
3
  from dataclasses import dataclass
4
4
  from datetime import datetime, timedelta, timezone
5
5
  from queue import Empty, Queue
6
+ from threading import Event
6
7
  from typing import List, Union
7
8
 
8
- from injector import inject
9
+ from dependency_injector.wiring import inject
9
10
 
10
11
  from isar.config.settings import settings
11
12
  from isar.models.communication.queues.events import Events
@@ -75,11 +76,16 @@ class Uploader:
75
76
  self.max_retry_attempts = max_retry_attempts
76
77
  self._internal_upload_queue: List[UploaderQueueItem] = []
77
78
 
79
+ self.signal_thread_quitting: Event = Event()
80
+
78
81
  self.logger = logging.getLogger("uploader")
79
82
 
83
+ def stop(self) -> None:
84
+ self.signal_thread_quitting.set()
85
+
80
86
  def run(self) -> None:
81
87
  self.logger.info("Started uploader")
82
- while True:
88
+ while not self.signal_thread_quitting.wait(0):
83
89
  inspection: Inspection
84
90
  mission: Mission
85
91
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: isar
3
- Version: 1.26.2
3
+ Version: 1.26.3
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
@@ -111,7 +111,7 @@ Requires-Dist: click
111
111
  Requires-Dist: dacite
112
112
  Requires-Dist: fastapi-azure-auth
113
113
  Requires-Dist: fastapi
114
- Requires-Dist: injector
114
+ Requires-Dist: dependency-injector
115
115
  Requires-Dist: numpy
116
116
  Requires-Dist: opencensus-ext-azure
117
117
  Requires-Dist: opencensus-ext-logging
@@ -1,19 +1,19 @@
1
1
  isar/__init__.py,sha256=cH8p8bVveu3FUL6kBhldcSlLaoHgD82Kd0-SwSNfGXw,87
2
- isar/modules.py,sha256=PCHSJ8JSt4WeR_8UBPnu_OFHNNUr8ETidC-WfSRcMpo,7707
3
- isar/script.py,sha256=sOZINSPjv6JHfPfHLKunl5BpMfoz83r7ZpwuWclA_mg,6158
2
+ isar/modules.py,sha256=shiuwpl3LCAZvl7a-8vE8L0ZxD1EW8wiZtvHbPOhMHk,4793
3
+ isar/script.py,sha256=VOI4nSEObDj53cSAFfzVBFDMwbu20uOsUZLZYyDZfBE,6083
4
4
  isar/apis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- isar/apis/api.py,sha256=ARf7-GsjTSqWj9UOSiEJ1YnSF-fH7Tr0-mkkfL10MZ4,12620
5
+ isar/apis/api.py,sha256=K0fYrLUo7dA1oD5oSkYinFPp3s4_DDnIJnY8BL3H1vQ,12610
6
6
  isar/apis/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  isar/apis/models/models.py,sha256=HzLaWhjAv0uJRBWipIgYg_F75eaQ5jl9Pi4UnYbDJ-M,1749
8
8
  isar/apis/models/start_mission_definition.py,sha256=rGpdb51LIrGRE5RjBONWflumow4ev8oTbDTx5Ph2_Pc,6210
9
- isar/apis/robot_control/robot_controller.py,sha256=D8LVMU8TRLohn1nlw2NQXjOxYmzjsOwjm_kr6f-h2VY,1259
9
+ isar/apis/robot_control/robot_controller.py,sha256=7EVukShJDhYez12wYeGWZXH-BrfQ4Wu2so0-fSWN2Zo,1277
10
10
  isar/apis/schedule/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- isar/apis/schedule/scheduling_controller.py,sha256=ky3Wk-WSLgtQz9b9cvt7as4uMJmEJOVJD2NxRxN5-9Q,7802
11
+ isar/apis/schedule/scheduling_controller.py,sha256=zEeRo9ep5QHkWl-xbXmBxnRtH3FZfLgkEQ1TajOvIsM,7820
12
12
  isar/apis/security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- isar/apis/security/authentication.py,sha256=sBdknpvE_3qSd6L-Saq6Oza12vpAmCQiyyAS2rWgHtQ,1992
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=zHFLmGWQRn8TrcsxUS6KHpJt2JE86kYazU7b-bkcN9o,2285
16
+ isar/config/log.py,sha256=SzEWbzXU1DpN7YONIRT8k0zBOGm_qVkXlJuuZtb8STc,2300
17
17
  isar/config/logging.conf,sha256=mYO1xf27gAopEMHhGzY7-mwyfN16rwRLkPNMvy3zn2g,1127
18
18
  isar/config/settings.env,sha256=cLIlcXTM8x0N-6XjXmC0Qclx5dfDC6myqa25tvVwmRw,500
19
19
  isar/config/settings.py,sha256=cFKINb2VRBrLLCzSK0zmRny2fu7S3sGKbDCBv28DxHI,12859
@@ -36,7 +36,7 @@ isar/config/predefined_missions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
36
36
  isar/config/predefined_missions/default.json,sha256=EDMfPQi2D_517jlfeU2BBFySkbvhYG7uEK6cbiK7DRM,1464
37
37
  isar/config/predefined_missions/default_turtlebot.json,sha256=8Vk1_0P0BBsG0vwh4vwIYINiiWioErHZ0Ppjq3ctaPM,2143
38
38
  isar/mission_planner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- isar/mission_planner/local_planner.py,sha256=45zVP3hzUHdZ5Ty4KWRL85_AUWiTmBhTtlsAruUDMD4,2561
39
+ isar/mission_planner/local_planner.py,sha256=6JxAR_V2JB-pVzZ8xajRa1tlKO3d3x-5oe-5NodgkvU,2579
40
40
  isar/mission_planner/mission_planner_interface.py,sha256=UgpPIM4FbrWOD7fGY3Ul64k3uYb8wo0FwSWGewYoVbc,485
41
41
  isar/mission_planner/sequential_task_selector.py,sha256=66agRPHuJnEa1vArPyty4muTasAZ50XPjjrSaTdY_Cc,643
42
42
  isar/mission_planner/task_selector_interface.py,sha256=pnLeaGPIuyXThcflZ_A7YL2b2xQjFT88hAZidkMomxU,707
@@ -50,11 +50,11 @@ isar/models/communication/queues/queue_timeout_error.py,sha256=rF8TlNF7RHS_ueTZ5
50
50
  isar/models/communication/queues/queue_utils.py,sha256=BGydIndLOFbj_Hyd2RiAlwdm7rWrem9BzoAnXH-ulxo,684
51
51
  isar/models/communication/queues/status_queue.py,sha256=on8kvlNsG6MJjEVsyqtBswIpmOdOggQiKr7F5x0T3jw,514
52
52
  isar/models/mission_metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
- isar/robot/robot.py,sha256=t7spWRnZwMVkohs6trrnZjTCxarOH1cPU8iy9vaYTiM,4699
54
- isar/robot/robot_start_mission.py,sha256=pppndYWPmg-GxU69RglB-4L4MlgXdLcsd0S5gFtfM3o,2767
55
- isar/robot/robot_status.py,sha256=l7tS-Rl-Xj0f8TgdCuZrhvi4Ubckw92u1VfBOoAFbUM,1605
56
- isar/robot/robot_stop_mission.py,sha256=V7hSfjeoprqkCwbLoDEHeIELPLRR0HvYH7Uc-_Wa8dM,2443
57
- isar/robot/robot_task_status.py,sha256=iSSfxn8uu40-qouT0sgPwIcB8h-2Bm3m3_tlXTuLoKk,3306
53
+ isar/robot/robot.py,sha256=RNuoE828lBOUBo9b2i5JiWhNsw7RqKpqEX4MlBPdLjI,4683
54
+ isar/robot/robot_start_mission.py,sha256=0S5mzOENLtRvW75THr_PTlSDycI3NVMOptJuL9TV0v8,2766
55
+ isar/robot/robot_status.py,sha256=3ysyLc639spTP4vuwruHirNTws0pct_pEtvPEaNtfwE,1623
56
+ isar/robot/robot_stop_mission.py,sha256=jUyfemvbyigxrlIp9aKPn-PvarhagJEgajQPS_LgJ7g,2442
57
+ isar/robot/robot_task_status.py,sha256=Rfmw5DUMZWsdjuLyPOegTbfkyAkNN3TW9EcHSoZ7fpo,3305
58
58
  isar/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
59
  isar/services/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
60
  isar/services/auth/azure_credentials.py,sha256=9PlwGe5FrPRbW2dp0go7LMp8_l_FRvL8xOXotXwzRDo,364
@@ -67,20 +67,20 @@ isar/services/service_connections/mqtt/robot_info_publisher.py,sha256=AxokGk51hR
67
67
  isar/services/service_connections/stid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
68
  isar/services/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
69
  isar/services/utilities/queue_utilities.py,sha256=Pw3hehSwkXJNeDv-bDVDfs58VOwtt3i5hpiJ2ZpphuQ,1225
70
- isar/services/utilities/robot_utilities.py,sha256=4-ob4kcIiRN_GXFDBMwBadfbwpYqKEkyzyC40wzvmko,555
71
- isar/services/utilities/scheduling_utilities.py,sha256=WaQXEK_cmPM9KNP3aBZGVnZCi97kR-M_rJWyWIGWZa4,8238
70
+ isar/services/utilities/robot_utilities.py,sha256=J3miRs0t74HNBoMIzVMceMCibp5DozP9Ai1_i7B3kWI,573
71
+ isar/services/utilities/scheduling_utilities.py,sha256=yYwNUCIasjVkH2-LVmHEdRCzUOOB3qr9xfVJdSpqlKY,8256
72
72
  isar/services/utilities/threaded_request.py,sha256=py4G-_RjnIdHljmKFAcQ6ddqMmp-ZYV39Ece-dqRqjs,1874
73
73
  isar/state_machine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
- isar/state_machine/state_machine.py,sha256=1ZomEjZelMDFHalZpK8e3Xex2Am_FsqwPFUTrfLeX6o,15248
74
+ isar/state_machine/state_machine.py,sha256=0A2VKmstY50hgC1gV-OdxmzCIGzkMZHOsdTAztn0gl0,15479
75
75
  isar/state_machine/states_enum.py,sha256=uqa3VD2Ig0PR5xG7s4-q5OLWiGdvEn-KYClv3KpRJ68,276
76
76
  isar/state_machine/states/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
- isar/state_machine/states/blocked_protective_stop.py,sha256=_InOhfCwCshrdUOquDy-js0cMrk8ddgPsHbHMLJzlNU,1540
78
- isar/state_machine/states/idle.py,sha256=0E9vFMXVJ7u2det3aN17KaNIBosA0zHr5mHIZqPL7dU,2771
79
- isar/state_machine/states/monitor.py,sha256=v-gPPjxmhNDpA2IKa7X1rm_xZC84pqHmgfSqCodz8PY,9153
77
+ isar/state_machine/states/blocked_protective_stop.py,sha256=LVguaE3fPkZwBBkxp1xpdFCeKajMqzQTZTsHwxQRitQ,1887
78
+ isar/state_machine/states/idle.py,sha256=ybi2q2Rg4vaLMg0CRZ8pauc5rYpIrE282lYkV6A-B_0,3076
79
+ isar/state_machine/states/monitor.py,sha256=hLhS-Eq1iTcxtAbhwVaJbcIOhWy8KMhhEmh88TWIePA,9536
80
80
  isar/state_machine/states/off.py,sha256=4d2rMaXK19Xi9eE8PrRLXrDv_hri_B1B0mYsk5L5RHo,545
81
- isar/state_machine/states/offline.py,sha256=-4PgEm52rnOXqhFYy0phXfPaNq99xkeBm_ylla-jPeU,1380
82
- isar/state_machine/states/paused.py,sha256=r0jXxlTIwsVw8eR3vNRPMjz-rzKepyaCrzquKLd0E9o,1156
83
- isar/state_machine/states/stop.py,sha256=bGtukno8n_f6nHqW6Zow4e21TYA72Vvmhj8coUFBLYM,2147
81
+ isar/state_machine/states/offline.py,sha256=ysXES4o3FLt6GMZ5v7SzxMDqsBMnePMOYBBhBk1Akfk,1685
82
+ isar/state_machine/states/paused.py,sha256=NgEl48GjLvnqGfPipYZZ6Yj8vdTb-fFLAK2tUu8i9j8,1461
83
+ isar/state_machine/states/stop.py,sha256=lF3uppwt_Qj66xWlRBeq26U1sUKo_DEu45V3cT-vmm4,2451
84
84
  isar/state_machine/transitions/fail_mission.py,sha256=_6HqBMALDinFZ4yh5GMpeqqgV5tw5i8OVMj5UDdqesg,495
85
85
  isar/state_machine/transitions/finish_mission.py,sha256=TRQrk7HdllmAkwsp25HRZAFAk46Y1hLx3jmkIAKrHDI,1442
86
86
  isar/state_machine/transitions/pause.py,sha256=aoDkq2nV6wBY0YQX3KbjvBR1ojP2o_SCRT_gfz0BulI,889
@@ -89,13 +89,13 @@ isar/state_machine/transitions/start_mission.py,sha256=BJdJvk0O5kq2DZkRHroVtkBzi
89
89
  isar/state_machine/transitions/stop.py,sha256=NzPvr6C1tmDqji5XJf5vw9up_Ow7ApuhjAzDVlJLZPw,1368
90
90
  isar/state_machine/transitions/utils.py,sha256=Wa72Ocq4QT1E6qkpEJZQ3h5o33pGvx7Tlkt2JZ2Grbk,314
91
91
  isar/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
92
- isar/storage/blob_storage.py,sha256=o4pdrG9P-rFAlmKg9Vuj2NckmIDgBvhKhpv1yOWH-F8,3208
92
+ isar/storage/blob_storage.py,sha256=Qci6bO508nlTHKPuPtVU5QcvGA4T7mv8cFrKWRcfw4g,3226
93
93
  isar/storage/local_storage.py,sha256=Bnmoi5gyN8r-oRh0aHrOdGqaH3JqRScFKMRXYojW5kY,1855
94
- isar/storage/slimm_storage.py,sha256=945p97i1d974lLRl3bo9XYD8awc0x2FxiM6vnGA9T3w,9032
94
+ isar/storage/slimm_storage.py,sha256=DJVvTce4cb8m0_b_r6yog41UuxO_9VKb1hYCZUTEhR0,9050
95
95
  isar/storage/storage_interface.py,sha256=DYDry4I7aZpDHJhsBF6s8zrgokFAc7fdKJKfA8AvL7o,828
96
- isar/storage/uploader.py,sha256=mJ-ax_6SvNpM2fG8WdyOUVKSB_J_nedJlCTJ_Xg2E4c,6543
96
+ isar/storage/uploader.py,sha256=M7FEr0iLsrSil8SqP7vmB3hswIOA2HTrnakj6aOQbvg,6749
97
97
  isar/storage/utilities.py,sha256=fitsdQ1ox5gr9fk9VuSk_iTBiEAIS8NZAnHabUZORh0,3173
98
- isar-1.26.2.dist-info/licenses/LICENSE,sha256=3fc2-ebLwHWwzfQbulGNRdcNob3SBQeCfEVUDYxsuqw,14058
98
+ isar-1.26.3.dist-info/licenses/LICENSE,sha256=3fc2-ebLwHWwzfQbulGNRdcNob3SBQeCfEVUDYxsuqw,14058
99
99
  robot_interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
100
  robot_interface/robot_interface.py,sha256=Bkk-joyIzRHxv8iZt7FLPFnlE8chlJad9vgjwc-fDkw,8786
101
101
  robot_interface/test_robot_interface.py,sha256=FV1urn7SbsMyWBIcTKjsBwAG4IsXeZ6pLHE0mA9EGGs,692
@@ -119,8 +119,8 @@ robot_interface/telemetry/payloads.py,sha256=m55aFrIczI-PDwQXvsFA6M7YNCTy83w7Ff-
119
119
  robot_interface/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
120
120
  robot_interface/utilities/json_service.py,sha256=qkzVkb60Gi_pto-b5n1vNzCrQze2yqgIJqSLNLYj1Fg,1034
121
121
  robot_interface/utilities/uuid_string_factory.py,sha256=_NQIbBQ56w0qqO0MUDP6aPpHbxW7ATRhK8HnQiBSLkc,76
122
- isar-1.26.2.dist-info/METADATA,sha256=tYUxPEqRTzHxD7YpOR76LGsjXy5UfILPCvFJOIXKTQ8,30252
123
- isar-1.26.2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
124
- isar-1.26.2.dist-info/entry_points.txt,sha256=TFam7uNNw7J0iiDYzsH2gfG0u1eV1wh3JTw_HkhgKLk,49
125
- isar-1.26.2.dist-info/top_level.txt,sha256=UwIML2RtuQKCyJJkatcSnyp6-ldDjboB9k9JgKipO-U,21
126
- isar-1.26.2.dist-info/RECORD,,
122
+ isar-1.26.3.dist-info/METADATA,sha256=WCaFk8atXbeG3TfFJCLLXAIw9wB6Lr1aX2sQgfKxESw,30263
123
+ isar-1.26.3.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
124
+ isar-1.26.3.dist-info/entry_points.txt,sha256=TFam7uNNw7J0iiDYzsH2gfG0u1eV1wh3JTw_HkhgKLk,49
125
+ isar-1.26.3.dist-info/top_level.txt,sha256=UwIML2RtuQKCyJJkatcSnyp6-ldDjboB9k9JgKipO-U,21
126
+ isar-1.26.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (79.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5