Skip to content

Commit

Permalink
Merge branch 'deploy/hammer' into hammer/fix-delivery-desc
Browse files Browse the repository at this point in the history
Signed-off-by: Aaron Chong <aaronchongth@gmail.com>
  • Loading branch information
aaronchongth committed Oct 4, 2023
2 parents 7045716 + 4931d1f commit 16d38b0
Show file tree
Hide file tree
Showing 43 changed files with 4,351 additions and 416 deletions.
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
packages/api-server/api_server/static/

5 changes: 5 additions & 0 deletions packages/api-client/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
ApiServerModelsTortoiseModelsAlertsAlertLeaf,
ApiServerModelsTortoiseModelsBeaconsBeaconStateLeaf as BeaconState,
BuildingMap,
ApiServerModelsTortoiseModelsDeliveryAlertsDeliveryAlertLeaf as DeliveryAlert,
DispenserHealth,
DispenserState,
DoorHealth,
Expand Down Expand Up @@ -124,6 +125,10 @@ export class SioClient {
subscribeAlerts(listener: Listener<Alert>): Subscription {
return this.subscribe<Alert>(`/alerts`, listener);
}

subscribeDeliveryAlerts(listener: Listener<DeliveryAlert>): Subscription {
return this.subscribe<DeliveryAlert>(`/delivery_alerts`, listener);
}
}

export * from './openapi';
682 changes: 678 additions & 4 deletions packages/api-client/lib/openapi/api.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/api-client/lib/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import { version as rmfModelVer } from 'rmf-models';

export const version = {
rmfModels: rmfModelVer,
rmfServer: 'f24685f669162c7f8462a112c033f3e65c1ae744',
rmfServer: '6ca8484298a18d0b71041295c6c7880555971461',
openapiGenerator: '6.2.1',
};
219 changes: 218 additions & 1 deletion packages/api-client/schema/index.ts

Large diffs are not rendered by default.

65 changes: 55 additions & 10 deletions packages/api-server/api_server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
import schedule
from fastapi import Depends
from fastapi.middleware.cors import CORSMiddleware
from fastapi.openapi.docs import (
get_redoc_html,
get_swagger_ui_html,
get_swagger_ui_oauth2_redirect_html,
)
from fastapi.staticfiles import StaticFiles
from tortoise import Tortoise

Expand All @@ -27,7 +32,7 @@
User,
)
from .models import tortoise_models as ttm
from .repositories import StaticFilesRepository, TaskRepository
from .repositories import TaskRepository
from .rmf_io import HealthWatchdog, RmfBookKeeper, rmf_events
from .types import is_coroutine

Expand All @@ -46,7 +51,12 @@ async def on_sio_connect(sid: str, _environ: dict, auth: Optional[dict] = None):
return False


app = FastIO(title="RMF API Server", socketio_connect=on_sio_connect)
app = FastIO(
title="RMF API Server",
socketio_connect=on_sio_connect,
docs_url=None,
redoc_url=None,
)


app.add_middleware(
Expand All @@ -56,22 +66,25 @@ async def on_sio_connect(sid: str, _environ: dict, auth: Optional[dict] = None):
allow_methods=["*"],
allow_headers=["*"],
)
os.makedirs(app_config.static_directory, exist_ok=True)

app.mount(
"/static",
StaticFiles(directory=app_config.static_directory),
StaticFiles(
directory=os.path.join(os.path.dirname(os.path.abspath(__file__)), "static")
),
name="static",
)

os.makedirs(app_config.cache_directory, exist_ok=True)
app.mount(
"/cache",
StaticFiles(directory=app_config.cache_directory),
name="cache",
)

# will be called in reverse order on app shutdown
shutdown_cbs: List[Union[Coroutine[Any, Any, Any], Callable[[], None]]] = []

static_files_repo = StaticFilesRepository(
f"{app_config.public_url.geturl()}/static",
app_config.static_directory,
logger.getChild("static_files"),
)

rmf_bookkeeper = RmfBookKeeper(rmf_events, logger=logger.getChild("BookKeeper"))

app.include_router(routes.main_router)
Expand All @@ -84,6 +97,11 @@ async def on_sio_connect(sid: str, _environ: dict, auth: Optional[dict] = None):
app.include_router(
routes.building_map_router, prefix="/building_map", dependencies=[Depends(user_dep)]
)
app.include_router(
routes.delivery_alerts_router,
prefix="/delivery_alerts",
dependencies=[Depends(user_dep)],
)
app.include_router(
routes.doors_router, prefix="/doors", dependencies=[Depends(user_dep)]
)
Expand Down Expand Up @@ -202,6 +220,33 @@ async def on_shutdown():
logger.info("shutdown app")


@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
openapi_url = app.openapi_url if app.openapi_url is not None else "/openapi.json"
return get_swagger_ui_html(
openapi_url=openapi_url,
title=app.title + " - Swagger UI",
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
swagger_js_url="/static/swagger-ui-bundle.js",
swagger_css_url="/static/swagger-ui.css",
)


@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
async def swagger_ui_redirect():
return get_swagger_ui_oauth2_redirect_html()


@app.get("/redoc", include_in_schema=False)
async def redoc_html():
openapi_url = app.openapi_url if app.openapi_url is not None else "/openapi.json"
return get_redoc_html(
openapi_url=openapi_url,
title=app.title + " - ReDoc",
redoc_js_url="/static/redoc.standalone.js",
)


async def _spin_scheduler():
while True:
schedule.run_pending()
Expand Down
2 changes: 1 addition & 1 deletion packages/api-server/api_server/app_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class AppConfig:
port: int
db_url: str
public_url: urllib.parse.ParseResult
static_directory: str
cache_directory: str
log_level: str
builtin_admin: str
jwt_public_key: Optional[str]
Expand Down
2 changes: 1 addition & 1 deletion packages/api-server/api_server/default_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# When being a proxy, this must be the url that rmf-server is mounted on.
# E.g. https://example.com/rmf/api/v1
"public_url": "http://localhost:8000",
"static_directory": "static", # The directory where static files should be stored.
"cache_directory": "run/cache", # The directory where cached files should be stored.
"log_level": "WARNING", # https://docs.python.org/3.8/library/logging.html#levels
# a user that is automatically given admin privileges, note that this does not guarantee that the user exists in the identity provider.
"builtin_admin": "admin",
Expand Down
2 changes: 2 additions & 0 deletions packages/api-server/api_server/fast_io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ def __init__(
**kwargs,
):
super().__init__(*args, **kwargs)
if self.swagger_ui_oauth2_redirect_url is None:
self.swagger_ui_oauth2_redirect_url = "docs/oauth2-redirect"
self.sio = socketio.AsyncServer(
async_mode="asgi", cors_allowed_origins=[], serializer=FastIOPacket
)
Expand Down
16 changes: 8 additions & 8 deletions packages/api-server/api_server/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,18 @@

from .logger import logger as base_logger
from .models import BuildingMap, DispenserState, DoorState, IngestorState, LiftState
from .repositories import StaticFilesRepository, static_files_repo
from .repositories import CachedFilesRepository, cached_files_repo
from .rmf_io import rmf_events
from .ros import ros_node


def process_building_map(
rmf_building_map: RmfBuildingMap,
static_files: StaticFilesRepository,
cached_files: CachedFilesRepository,
) -> BuildingMap:
"""
1. Converts a `BuildingMap` message to an ordered dict.
2. Saves the images into `{static_directory}/{map_name}/`.
2. Saves the images into `{cache_directory}/{map_name}/`.
3. Change the `AffineImage` `data` field to the url of the image.
"""
processed_map = message_to_ordereddict(rmf_building_map)
Expand All @@ -53,15 +53,15 @@ def process_building_map(
sha1_hash.update(image.data)
fingerprint = base64.b32encode(sha1_hash.digest()).lower().decode()
relpath = f"{rmf_building_map.name}/{level.name}-{image.name}.{fingerprint}.{image.encoding}" # pylint: disable=line-too-long
urlpath = static_files.add_file(image.data, relpath)
urlpath = cached_files.add_file(image.data, relpath)
processed_map["levels"][i]["images"][j]["data"] = urlpath
return BuildingMap(**processed_map)


class RmfGateway:
def __init__(
self,
static_files: StaticFilesRepository,
cached_files: CachedFilesRepository,
*,
logger: Optional[logging.Logger] = None,
):
Expand All @@ -74,7 +74,7 @@ def __init__(
self._submit_task_srv = ros_node().create_client(RmfSubmitTask, "submit_task")
self._cancel_task_srv = ros_node().create_client(RmfCancelTask, "cancel_task")

self.static_files = static_files
self.cached_files = cached_files
self.logger = logger or base_logger.getChild(self.__class__.__name__)
self._subscriptions: List[Subscription] = []

Expand Down Expand Up @@ -135,7 +135,7 @@ def convert_lift_state(lift_state: RmfLiftState):
RmfBuildingMap,
"map",
lambda msg: rmf_events.building_map.on_next(
process_building_map(msg, self.static_files)
process_building_map(msg, self.cached_files)
),
rclpy.qos.QoSProfile(
history=rclpy.qos.HistoryPolicy.KEEP_ALL,
Expand Down Expand Up @@ -191,5 +191,5 @@ def startup():
Must be called after the ros node is created and before spinning the it.
"""
global _rmf_gateway
_rmf_gateway = RmfGateway(static_files_repo)
_rmf_gateway = RmfGateway(cached_files_repo)
return _rmf_gateway
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .authorization import *
from .beacons import *
from .building_map import BuildingMap
from .delivery_alerts import *
from .dispenser_state import DispenserState
from .door_state import DoorState
from .fleets import FleetLog, FleetLogLog, FleetLogRobots, FleetLogRobotsLog, FleetState
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from enum import Enum

from tortoise.contrib.pydantic.creator import pydantic_model_creator
from tortoise.fields import CharEnumField, CharField
from tortoise.models import Model


class DeliveryAlert(Model):
"""
Custom alerts for custom delivery tasks
"""

class Category(str, Enum):
Missing = "missing"
Wrong = "wrong"

class Tier(str, Enum):
Warning = "warning"
Error = "error"

class Action(str, Enum):
Waiting = "waiting"
Cancel = "cancelled"
Override = "override"
Resume = "resume"

id = CharField(255, pk=True)
category = CharEnumField(Category, index=True)
tier = CharEnumField(Tier, index=True)
task_id = CharField(255, index=True, null=True)
action = CharEnumField(Action, index=True)
message = CharField(255, null=True)


DeliveryAlertPydantic = pydantic_model_creator(DeliveryAlert)
2 changes: 1 addition & 1 deletion packages/api-server/api_server/repositories/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .alerts import AlertRepository, alert_repo_dep
from .cached_files import CachedFilesRepository, cached_files_repo
from .fleets import FleetRepository, fleet_repo_dep
from .rmf import RmfRepository, rmf_repo_dep
from .static_files import StaticFilesRepository, static_files_repo
from .tasks import TaskRepository, task_repo_dep
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@
from api_server.logger import logger as _logger


class StaticFilesRepository:
class CachedFilesRepository:
def __init__(
self,
base_url: str,
directory: str,
logger: Optional[logging.Logger] = None,
):
"""
:param base_url: base url that static files are served from. When running behind a proxy,
:param base_url: base url that cached files are served from. When running behind a proxy,
this should be the url of the proxy.
:param directory: location to write the files to.
This should be the same directory that a static files server is serving from.
This should be the same directory that the cached files server is serving from.
"""
self.base_url = base_url
self.directory = directory
Expand All @@ -40,8 +40,9 @@ def add_file(self, data: bytes, path: str) -> str:
return urlpath


static_files_repo = StaticFilesRepository(
f"{app_config.public_url.geturl()}/static",
app_config.static_directory,
_logger.getChild("static_files"),
os.makedirs(app_config.cache_directory, exist_ok=True)
cached_files_repo = CachedFilesRepository(
f"{app_config.public_url.geturl()}/cache",
app_config.cache_directory,
_logger.getChild("cached_files"),
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@
import unittest
from os.path import dirname

from .static_files import StaticFilesRepository
from .cached_files import CachedFilesRepository


class TestStaticFilesRepository(unittest.TestCase):
class TestCachedFilesRepository(unittest.TestCase):
def setUp(self):
self.artifact_dir = f"{dirname(dirname(dirname(__file__)))}/test_artifacts"
self.repo = StaticFilesRepository("/static", self.artifact_dir)
self.repo = CachedFilesRepository("/cache", self.artifact_dir)

def test_add_file(self):
"""
test file is saved in the correct location and it returns the correct url
"""
target_path = "TestStaticFilesRepository/test_add_file.txt"
target_path = "TestCachedFilesRepository/test_add_file.txt"
url_path = self.repo.add_file(b"hello", target_path)
self.assertEqual(url_path, f"/static/{target_path}")
self.assertEqual(url_path, f"/cache/{target_path}")
saved_path = f"{self.artifact_dir}/{target_path}"
self.assertTrue(os.path.exists(saved_path))
with open(saved_path, "br") as f:
Expand Down
1 change: 1 addition & 0 deletions packages/api-server/api_server/rmf_io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
TaskEvents,
alert_events,
beacon_events,
delivery_alert_events,
fleet_events,
rmf_events,
task_events,
Expand Down
8 changes: 8 additions & 0 deletions packages/api-server/api_server/rmf_io/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,11 @@ def __init__(self):


beacon_events = BeaconEvents()


class DeliveryAlertEvents:
def __init__(self):
self.delivery_alerts = Subject() # DeliveryAlert


delivery_alert_events = DeliveryAlertEvents()
1 change: 1 addition & 0 deletions packages/api-server/api_server/routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .alerts import router as alerts_router
from .beacons import router as beacons_router
from .building_map import router as building_map_router
from .delivery_alerts import router as delivery_alerts_router
from .dispensers import router as dispensers_router
from .doors import router as doors_router
from .fleets import router as fleets_router
Expand Down
Loading

0 comments on commit 16d38b0

Please sign in to comment.