replaces SioServerWrapper with setup function

This commit is contained in:
Mose Müller 2023-12-19 12:57:30 +01:00
parent f64b5c35ab
commit c7d63f5139
2 changed files with 88 additions and 75 deletions

View File

@ -6,6 +6,7 @@ import socketio # type: ignore[import-untyped]
from pydase.data_service.data_service import process_callable_attribute from pydase.data_service.data_service import process_callable_attribute
from pydase.data_service.data_service_observer import DataServiceObserver from pydase.data_service.data_service_observer import DataServiceObserver
from pydase.data_service.state_manager import StateManager
from pydase.utils.helpers import get_object_attr_from_path_list from pydase.utils.helpers import get_object_attr_from_path_list
from pydase.utils.logging import SocketIOHandler from pydase.utils.logging import SocketIOHandler
from pydase.utils.serializer import dump from pydase.utils.serializer import dump
@ -89,88 +90,98 @@ class UpdateWebSettingsDict(TypedDict):
value: Any value: Any
class SioServerWrapper: def setup_sio_server(
def __init__( observer: DataServiceObserver,
self, enable_cors: bool,
observer: DataServiceObserver, loop: asyncio.AbstractEventLoop,
enable_cors: bool, ) -> socketio.AsyncServer:
loop: asyncio.AbstractEventLoop, """
Sets up and configures a Socket.IO asynchronous server.
Args:
observer (DataServiceObserver):
The observer managing state updates and communication.
enable_cors (bool):
Flag indicating whether CORS should be enabled for the server.
loop (asyncio.AbstractEventLoop):
The event loop in which the server will run.
Returns:
socketio.AsyncServer: The configured Socket.IO asynchronous server.
"""
state_manager = observer.state_manager
if enable_cors:
sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*")
else:
sio = socketio.AsyncServer(async_mode="asgi")
setup_sio_events(sio, state_manager)
setup_logging_handler(sio)
# Add notification callback to observer
def sio_callback(
full_access_path: str, value: Any, cached_value_dict: dict[str, Any]
) -> None: ) -> None:
self._loop = loop if cached_value_dict != {}:
self._enable_cors = enable_cors serialized_value = dump(value)
self._observer = observer if cached_value_dict["type"] != "method":
self._state_manager = self._observer.state_manager cached_value_dict["type"] = serialized_value["type"]
self._service = self._state_manager.service
self._setup_sio() cached_value_dict["value"] = serialized_value["value"]
self._setup_logging_handler()
def _setup_logging_handler(self) -> None: async def notify() -> None:
logger = logging.getLogger() try:
logger.addHandler(SocketIOHandler(self.sio)) await sio.emit(
"notify",
{
"data": {
"full_access_path": full_access_path,
"value": cached_value_dict,
}
},
)
except Exception as e:
logger.warning("Failed to send notification: %s", e)
def _setup_sio(self) -> None: loop.create_task(notify())
if self._enable_cors:
self.sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*")
else:
self.sio = socketio.AsyncServer(async_mode="asgi")
@self.sio.event observer.add_notification_callback(sio_callback)
def set_attribute(sid: str, data: UpdateDict) -> Any:
logger.debug("Received frontend update: %s", data)
path_list = [*data["parent_path"].split("."), data["name"]]
path_list.remove("DataService") # always at the start, does not do anything
path = ".".join(path_list)
return self._state_manager.set_service_attribute_value_by_path(
path=path, value=data["value"]
)
@self.sio.event return sio
def run_method(sid: str, data: RunMethodDict) -> Any:
logger.debug("Running method: %s", data)
path_list = [*data["parent_path"].split("."), data["name"]]
path_list.remove("DataService") # always at the start, does not do anything
method = get_object_attr_from_path_list(self._service, path_list)
return process_callable_attribute(method, data["kwargs"])
@self.sio.event
def web_settings(sid: str, data: UpdateWebSettingsDict) -> Any:
logger.debug("Received web settings update: %s", data)
path_list, config_option, value = (
data["access_path"].split("."),
data["config_option"],
data["value"],
)
path_list.pop(0) # remove first entry (specifies root object, not needed)
# write to web-settings.json file
self._add_notification_callback_to_observer() def setup_sio_events(sio: socketio.AsyncServer, state_manager: StateManager) -> None:
@sio.event
def set_attribute(sid: str, data: UpdateDict) -> Any:
logger.debug("Received frontend update: %s", data)
path_list = [*data["parent_path"].split("."), data["name"]]
path_list.remove("DataService") # always at the start, does not do anything
path = ".".join(path_list)
return state_manager.set_service_attribute_value_by_path(
path=path, value=data["value"]
)
def _add_notification_callback_to_observer(self) -> None: @sio.event
def sio_callback( def run_method(sid: str, data: RunMethodDict) -> Any:
full_access_path: str, value: Any, cached_value_dict: dict[str, Any] logger.debug("Running method: %s", data)
) -> None: path_list = [*data["parent_path"].split("."), data["name"]]
if cached_value_dict != {}: path_list.remove("DataService") # always at the start, does not do anything
serialized_value = dump(value) method = get_object_attr_from_path_list(state_manager.service, path_list)
if cached_value_dict["type"] != "method": return process_callable_attribute(method, data["kwargs"])
cached_value_dict["type"] = serialized_value["type"]
cached_value_dict["value"] = serialized_value["value"] @sio.event
def web_settings(sid: str, data: UpdateWebSettingsDict) -> Any:
logger.debug("Received web settings update: %s", data)
path_list, config_option, value = (
data["access_path"].split("."),
data["config_option"],
data["value"],
)
path_list.pop(0) # remove first entry (specifies root object, not needed)
async def notify() -> None:
try:
await self.sio.emit(
"notify",
{
"data": {
"full_access_path": full_access_path,
"value": cached_value_dict,
}
},
)
except Exception as e:
logger.warning("Failed to send notification: %s", e)
self._loop.create_task(notify()) def setup_logging_handler(sio: socketio.AsyncServer) -> None:
logger = logging.getLogger()
self._observer.add_notification_callback(sio_callback) logger.addHandler(SocketIOHandler(sio))

View File

@ -11,7 +11,9 @@ from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from pydase.data_service.data_service_observer import DataServiceObserver from pydase.data_service.data_service_observer import DataServiceObserver
from pydase.server.web_server.sio_server_wrapper import SioServerWrapper from pydase.server.web_server.sio_server import (
setup_sio_server,
)
from pydase.version import __version__ from pydase.version import __version__
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -77,7 +79,7 @@ class WebServer:
await self.web_server.serve() await self.web_server.serve()
def _setup_socketio(self) -> None: def _setup_socketio(self) -> None:
self._sio = SioServerWrapper(self.observer, self.enable_cors, self._loop).sio self._sio = setup_sio_server(self.observer, self.enable_cors, self._loop)
self.__sio_app = socketio.ASGIApp(self._sio) self.__sio_app = socketio.ASGIApp(self._sio)
def _setup_fastapi_app(self) -> None: def _setup_fastapi_app(self) -> None: