Skip to content

Commit

Permalink
Get media configs in API endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
andchiind committed Dec 3, 2024
1 parent 6bd48b1 commit 0c37b44
Show file tree
Hide file tree
Showing 12 changed files with 138 additions and 27 deletions.
22 changes: 21 additions & 1 deletion src/isar/apis/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from pydantic import AnyHttpUrl

from isar.apis.models.models import ControlMissionResponse, StartMissionResponse
from isar.apis.robot_control.robot_controller import RobotController
from isar.apis.schedule.scheduling_controller import SchedulingController
from isar.apis.security.authentication import Authenticator
from isar.config.configuration_error import ConfigurationError
Expand All @@ -34,12 +35,14 @@ def __init__(
self,
authenticator: Authenticator,
scheduling_controller: SchedulingController,
robot_controller: RobotController,
keyvault_client: Keyvault,
port: int = settings.API_PORT,
azure_ai_logging_enabled: bool = settings.LOG_HANDLER_APPLICATION_INSIGHTS_ENABLED,
) -> None:
self.authenticator: Authenticator = authenticator
self.scheduling_controller: SchedulingController = scheduling_controller
self.robot_controller: RobotController = robot_controller
self.keyvault_client: Keyvault = keyvault_client
self.host: str = "0.0.0.0" # Locking uvicorn to use 0.0.0.0
self.port: int = port
Expand Down Expand Up @@ -98,6 +101,8 @@ def _create_app(self) -> FastAPI:

app.include_router(router=self._create_info_router())

app.include_router(router=self._create_media_control_router())

return app

def _create_scheduler_router(self) -> APIRouter:
Expand Down Expand Up @@ -277,14 +282,29 @@ def _create_info_router(self) -> APIRouter:

router.add_api_route(
path="/info/robot-settings",
endpoint=self.scheduling_controller.get_info,
endpoint=self.robot_controller.get_info,
methods=["GET"],
dependencies=[authentication_dependency],
summary="Information about the robot-settings",
)

return router

def _create_media_control_router(self) -> APIRouter:
router: APIRouter = APIRouter(tags=["Media"])

authentication_dependency: Security = Security(self.authenticator.get_scheme())

router.add_api_route(
path="/media/media-stream-config",
endpoint=self.robot_controller.generate_media_config,
methods=["GET"],
dependencies=[authentication_dependency],
summary="Generates a media stream connection config",
)

return router

def _log_startup_message(self) -> None:
address_format = "%s://%s:%d/docs"
message = f"Uvicorn running on {address_format} (Press CTRL+C to quit)"
Expand Down
9 changes: 9 additions & 0 deletions src/isar/apis/models/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from dataclasses import dataclass
from typing import List, Optional

from alitra import Frame, Orientation, Pose, Position
from isar.apis.models.media_connection_type import MediaConnectionType
from pydantic import BaseModel, Field

from robot_interface.models.mission.task import TaskTypes
Expand Down Expand Up @@ -33,6 +35,13 @@ class RobotInfoResponse(BaseModel):
plant_short_name: str


@dataclass
class MediaConfig:
url: str
token: str
media_connection_type: MediaConnectionType


class InputOrientation(BaseModel):
x: float
y: float
Expand Down
35 changes: 35 additions & 0 deletions src/isar/apis/robot_control/robot_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import logging
from typing import List

from injector import inject

from isar.apis.models.models import (
RobotInfoResponse,
TaskResponse,
)
from isar.config.settings import robot_settings, settings
from isar.services.utilities.robot_utilities import RobotUtilities
from robot_interface.models.mission.task import Task


class RobotController:
@inject
def __init__(
self,
robot_utilities: RobotUtilities,
):
self.robot_utilities: RobotUtilities = robot_utilities
self.logger = logging.getLogger("api")

def generate_media_config(self):
return self.robot_utilities.generate_media_config()

def get_info(self):
return RobotInfoResponse(
robot_package=settings.ROBOT_PACKAGE,
isar_id=settings.ISAR_ID,
robot_name=settings.ROBOT_NAME,
robot_capabilities=robot_settings.CAPABILITIES,
robot_map_name=settings.DEFAULT_MAP,
plant_short_name=settings.PLANT_SHORT_NAME,
)
10 changes: 0 additions & 10 deletions src/isar/apis/schedule/scheduling_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,16 +295,6 @@ def start_move_arm_mission(
)
return self._api_response(mission)

def get_info(self):
return RobotInfoResponse(
robot_package=settings.ROBOT_PACKAGE,
isar_id=settings.ISAR_ID,
robot_name=settings.ROBOT_NAME,
robot_capabilities=robot_settings.CAPABILITIES,
robot_map_name=settings.DEFAULT_MAP,
plant_short_name=settings.PLANT_SHORT_NAME,
)

def _api_response(self, mission: Mission) -> StartMissionResponse:
return StartMissionResponse(
id=mission.id,
Expand Down
25 changes: 22 additions & 3 deletions src/isar/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from isar.apis.api import API
from isar.apis.schedule.scheduling_controller import SchedulingController
from isar.apis.robot_control.robot_controller import RobotController
from isar.apis.security.authentication import Authenticator
from isar.config.keyvault.keyvault_service import Keyvault
from isar.config.settings import settings
Expand All @@ -18,6 +19,7 @@
from isar.models.communication.queues.queues import Queues
from isar.services.service_connections.request_handler import RequestHandler
from isar.services.utilities.scheduling_utilities import SchedulingUtilities
from isar.services.utilities.robot_utilities import RobotUtilities
from isar.state_machine.state_machine import StateMachine
from isar.storage.blob_storage import BlobStorage
from isar.storage.local_storage import LocalStorage
Expand All @@ -35,9 +37,10 @@ def provide_api(
self,
authenticator: Authenticator,
scheduling_controller: SchedulingController,
robot_controller: RobotController,
keyvault: Keyvault,
) -> API:
return API(authenticator, scheduling_controller, keyvault)
return API(authenticator, scheduling_controller, robot_controller, keyvault)

@provider
@singleton
Expand All @@ -47,6 +50,14 @@ def provide_scheduling_controller(
) -> SchedulingController:
return SchedulingController(scheduling_utilities)

@provider
@singleton
def provide_robot_controller(
self,
robot_utilities: RobotUtilities,
) -> RobotController:
return RobotController(robot_utilities)


class AuthenticationModule(Module):
@provider
Expand Down Expand Up @@ -142,7 +153,7 @@ def provide_uploader(
)


class UtilitiesModule(Module):
class SchedulingUtilitiesModule(Module):
@provider
@singleton
def provide_scheduling_utilities(
Expand All @@ -151,6 +162,13 @@ def provide_scheduling_utilities(
return SchedulingUtilities(queues, mission_planner)


class RobotUtilitiesModule(Module):
@provider
@singleton
def provide_robot_utilities(self, robot: RobotInterface) -> RobotUtilities:
return RobotUtilities(robot)


class ServiceModule(Module):
@provider
@singleton
Expand Down Expand Up @@ -193,7 +211,8 @@ def provide_task_selector(self) -> TaskSelectorInterface:
"storage_blob": (BlobStorageModule, settings.STORAGE_BLOB_ENABLED),
"storage_slimm": (SlimmStorageModule, settings.STORAGE_SLIMM_ENABLED),
"mqtt": (MqttModule, "required"),
"utilities": (UtilitiesModule, "required"),
"utilities": (SchedulingUtilitiesModule, "required"),
"robot_utilities": (RobotUtilitiesModule, "required"),
}


Expand Down
23 changes: 23 additions & 0 deletions src/isar/services/utilities/robot_utilities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import logging

from injector import inject

from isar.apis.models.models import MediaConfig
from robot_interface.robot_interface import RobotInterface


class RobotUtilities:
"""
Contains utility functions for getting robot information from the API.
"""

@inject
def __init__(
self,
robot: RobotInterface,
):
self.robot: RobotInterface = robot
self.logger = logging.getLogger("api")

def generate_media_config(self) -> MediaConfig:
return self.robot.generate_media_config()
14 changes: 14 additions & 0 deletions src/robot_interface/robot_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from threading import Thread
from typing import Callable, List

from isar.apis.models.models import MediaConfig
from robot_interface.models.initialize import InitializeParams
from robot_interface.models.inspection.inspection import Inspection
from robot_interface.models.mission.mission import Mission
Expand Down Expand Up @@ -224,6 +225,19 @@ def initialize(self, params: InitializeParams) -> None:
"""
raise NotImplementedError

@abstractmethod
def generate_media_config(self) -> MediaConfig:
"""
Generate a JSON containing the url and token needed to establish a media stream
connection to a robot.
Returns
-------
MediaConfig
An object containing the connection information for a media stream connection
"""
raise NotImplementedError

@abstractmethod
def get_telemetry_publishers(
self, queue: Queue, isar_id: str, robot_name: str
Expand Down
8 changes: 0 additions & 8 deletions src/robot_interface/telemetry/payloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from transitions import State

from robot_interface.models.mission.status import RobotStatus
from robot_interface.telemetry.media_connection_type import MediaConnectionType


@dataclass
Expand Down Expand Up @@ -56,13 +55,6 @@ class VideoStream:
type: str


@dataclass
class MediaConfig(TelemetryPayload):
url: str
token: str
media_connection_type: MediaConnectionType


@dataclass
class RobotStatusPayload:
isar_id: str
Expand Down
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
SequentialTaskSelectorModule,
ServiceModule,
StateMachineModule,
UtilitiesModule,
SchedulingUtilitiesModule,
)
from isar.services.service_connections.request_handler import RequestHandler
from isar.services.utilities.scheduling_utilities import SchedulingUtilities
Expand Down Expand Up @@ -47,7 +47,7 @@ def injector():
ServiceModule,
StateMachineModule,
SequentialTaskSelectorModule,
UtilitiesModule,
SchedulingUtilitiesModule,
]
)

Expand All @@ -66,7 +66,7 @@ def injector_auth():
RequestHandlerModule,
ServiceModule,
StateMachineModule,
UtilitiesModule,
SchedulingUtilitiesModule,
]
)

Expand Down
4 changes: 2 additions & 2 deletions tests/integration/turtlebot/test_successful_mission.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
RobotModule,
ServiceModule,
StateMachineModule,
UtilitiesModule,
SchedulingUtilitiesModule,
)
from isar.services.readers.base_reader import BaseReader
from isar.state_machine.states_enum import States
Expand Down Expand Up @@ -57,7 +57,7 @@ def injector_turtlebot():
StateMachineModule,
LocalPlannerModule,
LocalStorageModule,
UtilitiesModule,
SchedulingUtilitiesModule,
MockMqttModule,
]
)
Expand Down
9 changes: 9 additions & 0 deletions tests/mocks/robot_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

from alitra import Frame, Orientation, Pose, Position

from isar.apis.models.media_connection_type import MediaConnectionType
from isar.apis.models.models import MediaConfig
from robot_interface.models.initialize import InitializeParams
from robot_interface.models.inspection.inspection import (
Image,
Expand Down Expand Up @@ -62,6 +64,13 @@ def get_inspection(self, task: InspectionTask) -> Inspection:
image.data = b"Some binary image data"
return image

def generate_media_config(self) -> MediaConfig:
return MediaConfig(
url="mockURL",
token="mockToken",
media_connection_type=MediaConnectionType.LiveKit,
)

def register_inspection_callback(
self, callback_function: Callable[[Inspection, Mission], None]
) -> None:
Expand Down

0 comments on commit 0c37b44

Please sign in to comment.