Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get media configs in API endpoint #651

Merged
merged 1 commit into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
andchiind marked this conversation as resolved.
Show resolved Hide resolved
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