From fd92cb32ba74a41726e7b8ba4fcc353b209871a4 Mon Sep 17 00:00:00 2001 From: Aaron Chong Date: Tue, 18 Jul 2023 21:56:09 +0800 Subject: [PATCH] Feature/beacons backend (#726) * Beacon state database model created Signed-off-by: Aaron Chong * Beacons backend Signed-off-by: Aaron Chong * Added route to app, fixed return value as tuple Signed-off-by: Aaron Chong * Small fix in alerts on_next call Signed-off-by: Aaron Chong * Set any newly saved beacon state to next event for subscription Signed-off-by: Aaron Chong * Fix typo on subs call and raise HTTP exception when state is not found Signed-off-by: Aaron Chong * Lint Signed-off-by: Aaron Chong * Fix parameter name Signed-off-by: Aaron Chong --------- Signed-off-by: Aaron Chong --- packages/api-server/api_server/app.py | 3 ++ .../models/tortoise_models/__init__.py | 1 + .../models/tortoise_models/beacons.py | 14 +++++ .../api-server/api_server/rmf_io/__init__.py | 1 + .../api-server/api_server/rmf_io/events.py | 8 +++ .../api-server/api_server/routes/__init__.py | 1 + .../api-server/api_server/routes/beacons.py | 54 +++++++++++++++++++ .../api-server/api_server/routes/internal.py | 6 ++- 8 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 packages/api-server/api_server/models/tortoise_models/beacons.py create mode 100644 packages/api-server/api_server/routes/beacons.py diff --git a/packages/api-server/api_server/app.py b/packages/api-server/api_server/app.py index 740c58867..74275e86f 100644 --- a/packages/api-server/api_server/app.py +++ b/packages/api-server/api_server/app.py @@ -78,6 +78,9 @@ async def on_sio_connect(sid: str, _environ: dict, auth: Optional[dict] = None): app.include_router( routes.alerts_router, prefix="/alerts", dependencies=[Depends(user_dep)] ) +app.include_router( + routes.beacons_router, prefix="/beacons", dependencies=[Depends(user_dep)] +) app.include_router( routes.building_map_router, prefix="/building_map", dependencies=[Depends(user_dep)] ) diff --git a/packages/api-server/api_server/models/tortoise_models/__init__.py b/packages/api-server/api_server/models/tortoise_models/__init__.py index 183d1433a..8b1faae37 100644 --- a/packages/api-server/api_server/models/tortoise_models/__init__.py +++ b/packages/api-server/api_server/models/tortoise_models/__init__.py @@ -1,5 +1,6 @@ from .alerts import * from .authorization import * +from .beacons import * from .building_map import BuildingMap from .dispenser_state import DispenserState from .door_state import DoorState diff --git a/packages/api-server/api_server/models/tortoise_models/beacons.py b/packages/api-server/api_server/models/tortoise_models/beacons.py new file mode 100644 index 000000000..2dee2728d --- /dev/null +++ b/packages/api-server/api_server/models/tortoise_models/beacons.py @@ -0,0 +1,14 @@ +from tortoise.contrib.pydantic.creator import pydantic_model_creator +from tortoise.fields import BooleanField, CharField +from tortoise.models import Model + + +class BeaconState(Model): + id = CharField(255, pk=True) + online = BooleanField(index=True) + category = CharField(255, null=True, index=True) + activated = BooleanField(index=True) + level = CharField(255, null=True, index=True) + + +BeaconStatePydantic = pydantic_model_creator(BeaconState) diff --git a/packages/api-server/api_server/rmf_io/__init__.py b/packages/api-server/api_server/rmf_io/__init__.py index 0d3e3a2e8..ed166520e 100644 --- a/packages/api-server/api_server/rmf_io/__init__.py +++ b/packages/api-server/api_server/rmf_io/__init__.py @@ -3,6 +3,7 @@ RmfEvents, TaskEvents, alert_events, + beacon_events, fleet_events, rmf_events, task_events, diff --git a/packages/api-server/api_server/rmf_io/events.py b/packages/api-server/api_server/rmf_io/events.py index fbe2cf6ae..7b54087a5 100644 --- a/packages/api-server/api_server/rmf_io/events.py +++ b/packages/api-server/api_server/rmf_io/events.py @@ -45,3 +45,11 @@ def __init__(self): alert_events = AlertEvents() + + +class BeaconEvents: + def __init__(self): + self.beacons = Subject() # Beacons + + +beacon_events = BeaconEvents() diff --git a/packages/api-server/api_server/routes/__init__.py b/packages/api-server/api_server/routes/__init__.py index 202d3b8bd..df03f6763 100644 --- a/packages/api-server/api_server/routes/__init__.py +++ b/packages/api-server/api_server/routes/__init__.py @@ -1,5 +1,6 @@ from .admin import router as admin_router from .alerts import router as alerts_router +from .beacons import router as beacons_router from .building_map import router as building_map_router from .dispensers import router as dispensers_router from .doors import router as doors_router diff --git a/packages/api-server/api_server/routes/beacons.py b/packages/api-server/api_server/routes/beacons.py new file mode 100644 index 000000000..ada6bb552 --- /dev/null +++ b/packages/api-server/api_server/routes/beacons.py @@ -0,0 +1,54 @@ +from typing import List + +from fastapi import HTTPException +from rx import operators as rxops + +from api_server.fast_io import FastIORouter, SubscriptionRequest +from api_server.models import tortoise_models as ttm +from api_server.rmf_io import beacon_events + +router = FastIORouter(tags=["Beacons"]) + + +@router.sub("", response_model=ttm.BeaconStatePydantic) +async def sub_beacons(_req: SubscriptionRequest): + return beacon_events.beacons.pipe(rxops.filter(lambda x: x is not None)) + + +@router.get("", response_model=List[ttm.BeaconStatePydantic]) +async def get_beacons(): + beacons = await ttm.BeaconState.all() + return [await ttm.BeaconStatePydantic.from_tortoise_orm(a) for a in beacons] + + +@router.get("/{beacon_id}", response_model=ttm.BeaconStatePydantic) +async def get_beacon(beacon_id: str): + beacon_state = await ttm.BeaconState.get_or_none(id=beacon_id) + if beacon_state is None: + raise HTTPException(404, f"Beacon with ID {beacon_id} not found") + beacon_state_pydantic = await ttm.BeaconStatePydantic.from_tortoise_orm( + beacon_state + ) + return beacon_state_pydantic + + +@router.post("", status_code=201, response_model=ttm.BeaconStatePydantic) +async def save_beacon_state( + beacon_id: str, online: bool, category: str, activated: bool, level: str +): + beacon_state, _ = await ttm.BeaconState.update_or_create( + { + "online": online, + "category": category, + "activated": activated, + "level": level, + }, + id=beacon_id, + ) + if beacon_state is None: + raise HTTPException(404, f"Could not save beacon state with ID {beacon_id}") + beacon_state_pydantic = await ttm.BeaconStatePydantic.from_tortoise_orm( + beacon_state + ) + beacon_events.beacons.on_next(beacon_state_pydantic) + return beacon_state_pydantic diff --git a/packages/api-server/api_server/routes/internal.py b/packages/api-server/api_server/routes/internal.py index 77d253a27..1493b8697 100644 --- a/packages/api-server/api_server/routes/internal.py +++ b/packages/api-server/api_server/routes/internal.py @@ -59,7 +59,8 @@ async def process_msg(msg: Dict[str, Any], fleet_repo: FleetRepository) -> None: if task_state.status == mdl.Status.completed: alert = await alert_repo.create_alert(task_state.booking.id, "task") - alert_events.alerts.on_next(alert) + if alert is not None: + alert_events.alerts.on_next(alert) elif payload_type == "task_log_update": task_log = mdl.TaskEventLog(**msg["data"]) @@ -68,7 +69,8 @@ async def process_msg(msg: Dict[str, Any], fleet_repo: FleetRepository) -> None: if task_log_has_error(task_log): alert = await alert_repo.create_alert(task_log.task_id, "task") - alert_events.alerts.on_next(alert) + if alert is not None: + alert_events.alerts.on_next(alert) elif payload_type == "fleet_state_update": fleet_state = mdl.FleetState(**msg["data"])