mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-04-28 11:40:02 +02:00
feat: adding first version of webapi server
This commit is contained in:
parent
617450c2bf
commit
fa9086baa8
@ -1,3 +1,4 @@
|
|||||||
from .server import Server
|
from .server import Server
|
||||||
|
from .web_server import WebAPI
|
||||||
|
|
||||||
__all__ = ["Server"]
|
__all__ = ["Server"]
|
||||||
|
@ -18,6 +18,8 @@ from uvicorn.server import HANDLED_SIGNALS
|
|||||||
from pyDataInterface import DataService
|
from pyDataInterface import DataService
|
||||||
from pyDataInterface.version import __version__
|
from pyDataInterface.version import __version__
|
||||||
|
|
||||||
|
from .web_server import WebAPI
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import tiqi_rpc
|
import tiqi_rpc
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -123,31 +125,47 @@ class Server:
|
|||||||
host=self._host,
|
host=self._host,
|
||||||
port=self._rpc_port,
|
port=self._rpc_port,
|
||||||
)
|
)
|
||||||
tiqi_rpc_server.install_signal_handlers = lambda: None
|
tiqi_rpc_server.install_signal_handlers = lambda: None # type: ignore
|
||||||
future_or_task = self._loop.create_task(tiqi_rpc_server.serve())
|
future_or_task = self._loop.create_task(tiqi_rpc_server.serve())
|
||||||
self.servers["tiqi-rpc"] = future_or_task
|
self.servers["tiqi-rpc"] = future_or_task
|
||||||
if self._enable_web:
|
if self._enable_web:
|
||||||
# async def print_client_color() -> None:
|
self._wapi: WebAPI = WebAPI(
|
||||||
# while True:
|
*self._args,
|
||||||
# print(self._service.name)
|
service=self._service,
|
||||||
# await asyncio.sleep(1)
|
info=self._info,
|
||||||
|
**self._kwargs,
|
||||||
|
)
|
||||||
|
web_server = uvicorn.Server(
|
||||||
|
uvicorn.Config(
|
||||||
|
self._wapi.fastapi_app, host=self._host, port=self._web_port
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# future_or_task = self._loop.create_task(print_client_color())
|
def sio_callback(parent_path: str, name: str, value: Any) -> None:
|
||||||
# self._wapi: FastAPI = web_api(
|
async def notify() -> None:
|
||||||
# data_model=self._data_model,
|
try:
|
||||||
# info=self._info,
|
await self._wapi.sio.emit(
|
||||||
# *self._args,
|
"notify",
|
||||||
# **self._kwargs,
|
{
|
||||||
# )
|
"data": {
|
||||||
# web_server = uvicorn.Server(
|
"parent_path": parent_path,
|
||||||
# uvicorn.Config(self._wapi, host=self._host, port=self._web_port)
|
"name": name,
|
||||||
# )
|
"value": value,
|
||||||
# # overwrite uvicorn's signal handlers, otherwise it will bogart SIGINT and
|
}
|
||||||
# # SIGTERM, which makes it impossible to escape out of
|
},
|
||||||
# web_server.install_signal_handlers = lambda: None
|
)
|
||||||
# future_or_task = self._loop.create_task(web_server.serve())
|
except Exception as e:
|
||||||
# self.servers["web"] = future_or_task
|
logger.warning(f"Failed to send notification: {e}")
|
||||||
pass
|
|
||||||
|
self._loop.create_task(notify())
|
||||||
|
|
||||||
|
self._service.add_notification_callback(sio_callback)
|
||||||
|
|
||||||
|
# overwrite uvicorn's signal handlers, otherwise it will bogart SIGINT and
|
||||||
|
# SIGTERM, which makes it impossible to escape out of
|
||||||
|
web_server.install_signal_handlers = lambda: None # type: ignore
|
||||||
|
future_or_task = self._loop.create_task(web_server.serve())
|
||||||
|
self.servers["web"] = future_or_task
|
||||||
|
|
||||||
async def main_loop(self) -> None:
|
async def main_loop(self) -> None:
|
||||||
while not self.should_exit:
|
while not self.should_exit:
|
||||||
@ -156,14 +174,14 @@ class Server:
|
|||||||
async def shutdown(self) -> None:
|
async def shutdown(self) -> None:
|
||||||
logger.info("Shutting down")
|
logger.info("Shutting down")
|
||||||
|
|
||||||
await self._cancel_servers()
|
await self.__cancel_servers()
|
||||||
await self._cancel_tasks()
|
await self.__cancel_tasks()
|
||||||
|
|
||||||
if self._enable_rpc:
|
if self._enable_rpc:
|
||||||
logger.debug("Closing rpyc server.")
|
logger.debug("Closing rpyc server.")
|
||||||
self._rpc_server.close()
|
self._rpc_server.close()
|
||||||
|
|
||||||
async def _cancel_servers(self) -> None:
|
async def __cancel_servers(self) -> None:
|
||||||
for server_name, task in self.servers.items():
|
for server_name, task in self.servers.items():
|
||||||
task.cancel()
|
task.cancel()
|
||||||
try:
|
try:
|
||||||
@ -173,7 +191,7 @@ class Server:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Unexpected exception: {e}.")
|
logger.warning(f"Unexpected exception: {e}.")
|
||||||
|
|
||||||
async def _cancel_tasks(self) -> None:
|
async def __cancel_tasks(self) -> None:
|
||||||
for task in asyncio.all_tasks(self._loop):
|
for task in asyncio.all_tasks(self._loop):
|
||||||
task.cancel()
|
task.cancel()
|
||||||
try:
|
try:
|
||||||
@ -217,7 +235,7 @@ class Server:
|
|||||||
if self._enable_web:
|
if self._enable_web:
|
||||||
|
|
||||||
async def emit_exception() -> None:
|
async def emit_exception() -> None:
|
||||||
await self._wapi._sio.emit(
|
await self._wapi.sio.emit(
|
||||||
"notify",
|
"notify",
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
|
106
src/pyDataInterface/server/web_server.py
Normal file
106
src/pyDataInterface/server/web_server.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import socketio
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
|
from pyDataInterface import DataService
|
||||||
|
from pyDataInterface.config import OperationMode
|
||||||
|
from pyDataInterface.version import __version__
|
||||||
|
|
||||||
|
|
||||||
|
class WebAPI:
|
||||||
|
__sio_app: socketio.ASGIApp
|
||||||
|
__fastapi_app: FastAPI
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
service: DataService,
|
||||||
|
frontend: str | Path | None = None,
|
||||||
|
css: str | Path | None = None,
|
||||||
|
enable_CORS: bool = True,
|
||||||
|
info: dict[str, Any] = {},
|
||||||
|
*args: Any,
|
||||||
|
**kwargs: Any,
|
||||||
|
):
|
||||||
|
self.service = service
|
||||||
|
self.frontend = frontend
|
||||||
|
self.css = css
|
||||||
|
self.enable_CORS = enable_CORS
|
||||||
|
self.info = info
|
||||||
|
self.args = args
|
||||||
|
self.kwargs = kwargs
|
||||||
|
|
||||||
|
self.setup_socketio()
|
||||||
|
self.setup_fastapi_app()
|
||||||
|
|
||||||
|
def setup_socketio(self) -> None:
|
||||||
|
# the socketio ASGI app, to notify clients when params update
|
||||||
|
if self.enable_CORS:
|
||||||
|
self.__sio = socketio.AsyncServer(
|
||||||
|
async_mode="asgi", cors_allowed_origins="*"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.__sio = socketio.AsyncServer(async_mode="asgi")
|
||||||
|
self.__sio_app = socketio.ASGIApp(self.__sio)
|
||||||
|
|
||||||
|
def setup_fastapi_app(self) -> None: # noqa: CFQ004
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
if self.enable_CORS:
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
app.mount("/ws", self.__sio_app)
|
||||||
|
|
||||||
|
# @app.get("/version", include_in_schema=False)
|
||||||
|
@app.get("/version")
|
||||||
|
def version() -> str:
|
||||||
|
return __version__
|
||||||
|
|
||||||
|
@app.get("/name")
|
||||||
|
def name() -> str:
|
||||||
|
return self.service.get_service_name()
|
||||||
|
|
||||||
|
@app.get("/info")
|
||||||
|
def info() -> dict[str, Any]:
|
||||||
|
return self.info
|
||||||
|
|
||||||
|
@app.get("/service-properties")
|
||||||
|
def service_properties() -> dict[str, Any]:
|
||||||
|
return self.service.serialize()
|
||||||
|
|
||||||
|
if OperationMode().environment == "production":
|
||||||
|
app.mount(
|
||||||
|
"/",
|
||||||
|
StaticFiles(
|
||||||
|
directory=Path(__file__).parent.parent.parent.parent
|
||||||
|
/ "frontend"
|
||||||
|
/ "build",
|
||||||
|
html=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.__fastapi_app = app
|
||||||
|
|
||||||
|
def add_endpoint(self, name: str) -> None:
|
||||||
|
# your endpoint creation code
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_custom_openapi(self) -> None:
|
||||||
|
# your custom openapi generation code
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sio(self) -> socketio.AsyncServer:
|
||||||
|
return self.__sio
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fastapi_app(self) -> FastAPI:
|
||||||
|
return self.__fastapi_app
|
Loading…
x
Reference in New Issue
Block a user