#wip: WebSockets building.
This commit is contained in:
parent
45b2597ab0
commit
4a97794e2c
@ -63,10 +63,15 @@
|
|||||||
"http_server_port" : 18000,
|
"http_server_port" : 18000,
|
||||||
"AnP_HTTPServer_end" : null,
|
"AnP_HTTPServer_end" : null,
|
||||||
|
|
||||||
"AnP_WebSocketServer_start" : null,
|
"AnP_WebSocketsServerManager_start" : null,
|
||||||
"web_socket_server_host" : "",
|
"default_web_sockets_server" : {
|
||||||
"web_socket_server_port" : 18765,
|
"anp" : {
|
||||||
"AnP_WebSocketServer_end" : null,
|
"type" : "WebSocketServerDriver",
|
||||||
|
"host" : "localhost",
|
||||||
|
"port" : 18765
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AnP_WebSocketsServerManager_end" : null,
|
||||||
|
|
||||||
"AnP_TitlesManager_start" : null,
|
"AnP_TitlesManager_start" : null,
|
||||||
"default_titles_files" : [
|
"default_titles_files" : [
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import {Common} from "../Utils/Common.ecma.js";
|
import {Common} from "../Utils/Common.ecma.js";
|
||||||
|
import {Event} from "../Application/Event.ecma.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class WebSocketsDriver
|
* @class WebSocketsDriver
|
||||||
@ -12,6 +13,12 @@ import {Common} from "../Utils/Common.ecma.js";
|
|||||||
*/
|
*/
|
||||||
export const WebSocketsDriver = (function(){
|
export const WebSocketsDriver = (function(){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback continue_callback
|
||||||
|
* @param {boolean} ok
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @constructs WebSocketsDriver
|
* @constructs WebSocketsDriver
|
||||||
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
|
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
|
||||||
@ -23,19 +30,39 @@ export const WebSocketsDriver = (function(){
|
|||||||
|
|
||||||
/** @type {WebSocketsDriver} */
|
/** @type {WebSocketsDriver} */
|
||||||
const self = this;
|
const self = this;
|
||||||
let server = null,
|
/** @type {WebSocket|null} */
|
||||||
|
let client = null,
|
||||||
|
/** @type {string|null} */
|
||||||
url = null,
|
url = null,
|
||||||
|
/** @type {boolean} */
|
||||||
started = false;
|
started = false;
|
||||||
|
|
||||||
|
/** @type {Event} */
|
||||||
this.on_open = new Event();
|
this.on_open = new Event();
|
||||||
|
/** @type {Event} */
|
||||||
this.on_message = new Event();
|
this.on_message = new Event();
|
||||||
|
/** @type {Event} */
|
||||||
this.on_error = new Event();
|
this.on_error = new Event();
|
||||||
|
/** @type {Event} */
|
||||||
this.on_close = new Event();
|
this.on_close = new Event();
|
||||||
|
|
||||||
const constructor = () => {};
|
/**
|
||||||
|
* @returns {void}
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
const constructor = () => {
|
||||||
|
|
||||||
|
url = Common.get_string(inputs.url, "ws://localhost:8080");
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {?continue_callback} callback
|
||||||
|
* @returns {boolean}
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
this.start = (callback = null) => {
|
this.start = (callback = null) => {
|
||||||
|
|
||||||
|
/** @type {continue_callback} */
|
||||||
const end = ok => Common.execute(callback, ok);
|
const end = ok => Common.execute(callback, ok);
|
||||||
|
|
||||||
if(started){
|
if(started){
|
||||||
@ -44,27 +71,24 @@ export const WebSocketsDriver = (function(){
|
|||||||
};
|
};
|
||||||
started = true;
|
started = true;
|
||||||
|
|
||||||
server = new WebSocket(`ws://${host}:${port}`);
|
client = new WebSocket(url);
|
||||||
|
|
||||||
server.onopen = on_open;
|
client.onopen = on_open;
|
||||||
|
client.onmessage = on_message
|
||||||
server.onmessage = event => {
|
client.onerror = on_error;
|
||||||
self.on_message.trigger(event);
|
client.onclose = on_close;
|
||||||
};
|
|
||||||
|
|
||||||
server.onerror = event => {
|
|
||||||
self.on_error.trigger(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
server.onclose = event => {
|
|
||||||
self.on_close.trigger(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {?continue_callback} callback
|
||||||
|
* @returns {boolean}
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
this.close = (callback = null) => {
|
this.close = (callback = null) => {
|
||||||
|
|
||||||
|
/** @type {continue_callback} */
|
||||||
const end = ok => Common.execute(callback, ok);
|
const end = ok => Common.execute(callback, ok);
|
||||||
|
|
||||||
if(!started){
|
if(!started){
|
||||||
@ -73,34 +97,30 @@ export const WebSocketsDriver = (function(){
|
|||||||
};
|
};
|
||||||
started = false;
|
started = false;
|
||||||
|
|
||||||
server.close();
|
client.close();
|
||||||
server = null;
|
client = null;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const open = event => {
|
const on_open = event => {
|
||||||
console.log(["client", event]);
|
console.log(["client", event]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const new_message = event => {
|
const on_message = event => {
|
||||||
console.log(["message", event]);
|
console.log(["message", event]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const error = event => {
|
const on_error = event => {
|
||||||
console.log(["error", event]);
|
console.log(["error", event]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const close = event => {
|
const on_close = event => {
|
||||||
console.log(["close", event]);
|
console.log(["close", event]);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.send = (ids, message) => {
|
this.send = message => {
|
||||||
Common.get_array(ids).forEach(id => {});
|
client.send(message);
|
||||||
};
|
|
||||||
|
|
||||||
this.send_to_all = message => {
|
|
||||||
self.send(Object.keys(clients), message);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor();
|
constructor();
|
||||||
|
|||||||
26
Python/Abstracts/WebSocketsServerAbstract.py
Normal file
26
Python/Abstracts/WebSocketsServerAbstract.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Any, Self, Optional, Sequence
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from Application.Event import Event
|
||||||
|
|
||||||
|
class WebSocketsServerAbstract(ABC):
|
||||||
|
|
||||||
|
def __init__(self:Self, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
|
||||||
|
self.on_new_client:Event = Event()
|
||||||
|
self.on_message:Event = Event()
|
||||||
|
self.on_close:Event = Event()
|
||||||
|
self.on_error:Event = Event()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def start(self:Self) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def close(self:Self) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def close_client(self:Self, id:int) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def send(self:Self, data:Any|None, ids:Optional[int|Sequence[int]] = None) -> None:pass
|
||||||
@ -14,6 +14,7 @@ from Managers.ControllersManager import ControllersManager
|
|||||||
from Managers.DispatchesManager import DispatchesManager
|
from Managers.DispatchesManager import DispatchesManager
|
||||||
from Managers.IndexesManager import IndexesManager
|
from Managers.IndexesManager import IndexesManager
|
||||||
from Managers.RoutesManager import RoutesManager
|
from Managers.RoutesManager import RoutesManager
|
||||||
|
from Managers.WebSocketsServerManager import WebSocketsServerManager
|
||||||
from Drivers.HTTPDriver import HTTPDriver
|
from Drivers.HTTPDriver import HTTPDriver
|
||||||
from Utils.Common import Common
|
from Utils.Common import Common
|
||||||
from Utils.Patterns import RE
|
from Utils.Patterns import RE
|
||||||
@ -45,6 +46,7 @@ class AnP:
|
|||||||
self.dispatches:DispatchesManager = DispatchesManager(self)
|
self.dispatches:DispatchesManager = DispatchesManager(self)
|
||||||
self.indexes:IndexesManager = IndexesManager(self)
|
self.indexes:IndexesManager = IndexesManager(self)
|
||||||
self.routes:RoutesManager = RoutesManager(self)
|
self.routes:RoutesManager = RoutesManager(self)
|
||||||
|
self.web_sockets_servers:WebSocketsServerManager = WebSocketsServerManager(self)
|
||||||
self.http_server:HTTPDriver = HTTPDriver(self)
|
self.http_server:HTTPDriver = HTTPDriver(self)
|
||||||
|
|
||||||
def update(self:Self) -> None:
|
def update(self:Self) -> None:
|
||||||
@ -58,6 +60,7 @@ class AnP:
|
|||||||
self.dispatches.update()
|
self.dispatches.update()
|
||||||
self.indexes.update()
|
self.indexes.update()
|
||||||
self.routes.update()
|
self.routes.update()
|
||||||
|
self.web_sockets_servers.update()
|
||||||
self.http_server.update()
|
self.http_server.update()
|
||||||
|
|
||||||
def reset(self:Self) -> None:
|
def reset(self:Self) -> None:
|
||||||
@ -71,10 +74,12 @@ class AnP:
|
|||||||
self.dispatches.reset()
|
self.dispatches.reset()
|
||||||
self.indexes.reset()
|
self.indexes.reset()
|
||||||
self.routes.reset()
|
self.routes.reset()
|
||||||
|
self.web_sockets_servers.reset()
|
||||||
self.http_server.reset()
|
self.http_server.reset()
|
||||||
|
|
||||||
def close(self:Self) -> None:
|
def close(self:Self) -> None:
|
||||||
self.__working = False
|
self.__working = False
|
||||||
|
self.web_sockets_servers.close()
|
||||||
self.http_server.close()
|
self.http_server.close()
|
||||||
|
|
||||||
def working(self:Self) -> bool:
|
def working(self:Self) -> bool:
|
||||||
|
|||||||
@ -34,3 +34,6 @@ class Event:
|
|||||||
def remove(self:Self, i:int) -> None:
|
def remove(self:Self, i:int) -> None:
|
||||||
if i in self.__events:
|
if i in self.__events:
|
||||||
del self.__events[i]
|
del self.__events[i]
|
||||||
|
|
||||||
|
def clean(self:Self) -> None:
|
||||||
|
self.__events = {}
|
||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
from typing import Any, Optional, Self, Sequence
|
from typing import Any, Optional, Self, Sequence
|
||||||
from Interfaces.Application.AnPInterface import AnPInterface
|
from Interfaces.Application.AnPInterface import AnPInterface
|
||||||
|
from requests import post as Post
|
||||||
|
|
||||||
class OllamaDriver:
|
class OllamaDriver:
|
||||||
|
|
||||||
|
|||||||
@ -1,28 +1,37 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from threading import Thread
|
||||||
from typing import Any, Self, Sequence, Optional
|
from typing import Any, Self, Sequence, Optional
|
||||||
|
from Abstracts.WebSocketsServerAbstract import WebSocketsServerAbstract
|
||||||
|
from Abstracts.ModelAbstract import ModelAbstract
|
||||||
from websockets.sync.server import serve as server_serve
|
from websockets.sync.server import serve as server_serve
|
||||||
from websockets import ServerConnection as WebSocketServer, ClientConnection as WebSocketClient
|
from websockets import Server as WebSocketServer, ClientConnection as WebSocketClient
|
||||||
from Application.Event import Event
|
|
||||||
from Interfaces.Application.AnPInterface import AnPInterface
|
from Interfaces.Application.AnPInterface import AnPInterface
|
||||||
|
from Utils.Checks import Check
|
||||||
|
|
||||||
class WebSocketServerDriver:
|
class WebSocketServerDriver(WebSocketsServerAbstract, ModelAbstract):
|
||||||
|
|
||||||
def __init__(self:Self, anp:AnPInterface, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
|
def __init__(self:Self, anp:AnPInterface, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
|
||||||
|
|
||||||
self.anp:AnPInterface = anp
|
self.anp:AnPInterface = anp
|
||||||
self.on_new_client:Event = Event()
|
super().__init__(inputs)
|
||||||
self.on_message:Event = Event()
|
|
||||||
self.on_close:Event = Event()
|
|
||||||
self.on_error:Event = Event()
|
|
||||||
self.__server:WebSocketServer
|
self.__server:WebSocketServer
|
||||||
self.__clients:dict[str, WebSocketClient] = {}
|
self.__clients:dict[int, WebSocketClient] = {}
|
||||||
self.__host:str = anp.settings.get(("web_socket_server_host", "host"), inputs, "")
|
self.__host:str = anp.settings.get(("web_socket_server_host", "host"), inputs, "")
|
||||||
self.__port:int = anp.settings.get(("web_socket_server_port", "port"), inputs, 8765)
|
self.__port:int = anp.settings.get(("web_socket_server_port", "port"), inputs, 8765)
|
||||||
|
self.__client_i:int = 0
|
||||||
|
self.__thread:Thread = None
|
||||||
|
|
||||||
with server_serve(self.__handler, self.__host, self.__port) as self.__server:
|
anp.settings.get(("web_socket_server_autostart", "autostart"), inputs, True) and self.start()
|
||||||
self.__server.serve_forever()
|
|
||||||
|
def __run(self:Self) -> None:
|
||||||
|
self.__server = server_serve(self.__handler, self.__host, self.__port)
|
||||||
|
self.__server.serve_forever()
|
||||||
|
|
||||||
|
def start(self:Self) -> None:
|
||||||
|
self.__thread = Thread(target = self.__run)
|
||||||
|
self.__thread.start()
|
||||||
|
|
||||||
def close(self:Self) -> None:
|
def close(self:Self) -> None:
|
||||||
|
|
||||||
@ -31,14 +40,14 @@ class WebSocketServerDriver:
|
|||||||
for id in tuple(self.__clients.keys()):
|
for id in tuple(self.__clients.keys()):
|
||||||
self.close_client(id)
|
self.close_client(id)
|
||||||
|
|
||||||
self.__server.close()
|
self.__server.shutdown()
|
||||||
|
|
||||||
def close_client(self:Self, id:str, show_exception:bool = True) -> None:
|
def close_client(self:Self, id:int) -> None:
|
||||||
if id in self.__clients:
|
if id in self.__clients:
|
||||||
try:
|
try:
|
||||||
self.__clients[id].close()
|
self.__clients[id].close()
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
show_exception and self.anp.exception(exception, "web_socket_server_client_close_exception", {
|
self.anp.exception(exception, "web_socket_server_client_close_exception", {
|
||||||
"client": id,
|
"client": id,
|
||||||
"port": self.__port,
|
"port": self.__port,
|
||||||
"host": self.__host
|
"host": self.__host
|
||||||
@ -47,9 +56,12 @@ class WebSocketServerDriver:
|
|||||||
|
|
||||||
def __handler(self:Self, client:WebSocketClient) -> None:
|
def __handler(self:Self, client:WebSocketClient) -> None:
|
||||||
|
|
||||||
id:str = str(id(client))
|
id:int = self.__client_i
|
||||||
|
|
||||||
|
self.__client_i += 1
|
||||||
|
|
||||||
self.__clients[id] = client
|
self.__clients[id] = client
|
||||||
self.on_new_client.execute(client, id)
|
self.on_new_client.execute(id)
|
||||||
|
|
||||||
self.anp.print("info", "web_socket_server_client_connected", {
|
self.anp.print("info", "web_socket_server_client_connected", {
|
||||||
"client": id,
|
"client": id,
|
||||||
@ -64,19 +76,38 @@ class WebSocketServerDriver:
|
|||||||
message:str = client.recv()
|
message:str = client.recv()
|
||||||
if message is None:
|
if message is None:
|
||||||
break
|
break
|
||||||
self.on_message.execute(client, message)
|
self.on_message.execute(id, message)
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
self.anp.exception(exception, "web_socket_server_client_exception", {
|
self.anp.exception(exception, "web_socket_server_client_exception", {
|
||||||
"client": id,
|
"client": id,
|
||||||
"port": self.__port,
|
"port": self.__port,
|
||||||
"host": self.__host
|
"host": self.__host
|
||||||
})
|
})
|
||||||
self.on_error.execute(client, exception)
|
self.on_error.execute(id, exception)
|
||||||
finally:
|
finally:
|
||||||
self.close_client(id, False)
|
self.close_client(id)
|
||||||
self.on_close.execute(client)
|
self.on_close.execute(id)
|
||||||
self.anp.print("info", "web_socket_server_client_disconnected", {
|
self.anp.print("info", "web_socket_server_client_disconnected", {
|
||||||
"client": id,
|
"client": id,
|
||||||
"port": self.__port,
|
"port": self.__port,
|
||||||
"host": self.__host
|
"host": self.__host
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def send(self:Self, data:str, ids:int|Sequence[int]) -> bool:
|
||||||
|
|
||||||
|
success:bool = True
|
||||||
|
id:int
|
||||||
|
|
||||||
|
for id in ids if Check.is_array(ids) else [ids]:
|
||||||
|
if id in self.__clients:
|
||||||
|
try:
|
||||||
|
self.__clients[id].send(data)
|
||||||
|
except Exception as exception:
|
||||||
|
self.anp.exception(exception, "web_socket_server_client_send_exception", {
|
||||||
|
"client": id,
|
||||||
|
"port": self.__port,
|
||||||
|
"host": self.__host
|
||||||
|
})
|
||||||
|
success = False
|
||||||
|
|
||||||
|
return success
|
||||||
@ -12,6 +12,7 @@ from Interfaces.Managers.ControllersManagerInterface import ControllersManagerIn
|
|||||||
from Interfaces.Managers.DispatchesManagerInterface import DispatchesManagerInterface
|
from Interfaces.Managers.DispatchesManagerInterface import DispatchesManagerInterface
|
||||||
from Interfaces.Managers.IndexesManagerInterface import IndexesManagerInterface
|
from Interfaces.Managers.IndexesManagerInterface import IndexesManagerInterface
|
||||||
from Interfaces.Managers.RoutesManagerInterface import RoutesManagerInterface
|
from Interfaces.Managers.RoutesManagerInterface import RoutesManagerInterface
|
||||||
|
from Interfaces.Managers.WebSocketsServerManagerInterface import WebSocketsServerManagerInterface
|
||||||
|
|
||||||
class AnPInterface(ABC):
|
class AnPInterface(ABC):
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ class AnPInterface(ABC):
|
|||||||
self.dispatches:DispatchesManagerInterface = None
|
self.dispatches:DispatchesManagerInterface = None
|
||||||
self.indexes:IndexesManagerInterface = None
|
self.indexes:IndexesManagerInterface = None
|
||||||
self.routes:RoutesManagerInterface = None
|
self.routes:RoutesManagerInterface = None
|
||||||
|
self.web_sockets_servers:WebSocketsServerManagerInterface = None
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def update(self:Self) -> None:pass
|
def update(self:Self) -> None:pass
|
||||||
|
|||||||
@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Any, Self, Optional, Sequence
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from Abstracts.WebSocketsServerAbstract import WebSocketsServerAbstract
|
||||||
|
from Application.Event import Event
|
||||||
|
|
||||||
|
class WebSocketsServerManagerInterface(ABC):
|
||||||
|
|
||||||
|
def __init__(self:Self, anp:Any, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
|
||||||
|
self.on_new_client:Event = None
|
||||||
|
self.on_message:Event = None
|
||||||
|
self.on_close:Event = None
|
||||||
|
self.on_error:Event = None
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def update(self:Self) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def reset(self:Self) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def close(self:Self) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def remove(self:Self, names:str|Sequence[str]) -> None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get(self:Self, name:str) -> WebSocketsServerAbstract|None:pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def send(self:Self,
|
||||||
|
name:str,
|
||||||
|
controller:str,
|
||||||
|
method:str,
|
||||||
|
data:Optional[Any] = None,
|
||||||
|
clients:Optional[int|Sequence[int]] = None,
|
||||||
|
code:int = 200
|
||||||
|
) -> None:pass
|
||||||
@ -35,7 +35,10 @@ class ModelsManager:
|
|||||||
key:str
|
key:str
|
||||||
|
|
||||||
for key in Common.get_keys(keys):
|
for key in Common.get_keys(keys):
|
||||||
if key in self.__models and isinstance(self.__models[key], Type):
|
if key in self.__models and (
|
||||||
|
isinstance(self.__models[key], Type) or
|
||||||
|
issubclass(self.__models[key], Type)
|
||||||
|
):
|
||||||
return self.__models[key]
|
return self.__models[key]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -47,7 +50,7 @@ class ModelsManager:
|
|||||||
for key, model in subinputs.items():
|
for key, model in subinputs.items():
|
||||||
if Common.is_mark_key(key) and model is None:
|
if Common.is_mark_key(key) and model is None:
|
||||||
continue
|
continue
|
||||||
if isinstance(model, ModelAbstract) and (
|
if issubclass(model, ModelAbstract) and (
|
||||||
overwrite or key not in self.__models
|
overwrite or key not in self.__models
|
||||||
):
|
):
|
||||||
self.__models[key] = model
|
self.__models[key] = model
|
||||||
128
Python/Managers/WebSocketsServerManager.py
Normal file
128
Python/Managers/WebSocketsServerManager.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Any, Self, Optional, Sequence
|
||||||
|
from Interfaces.Application.AnPInterface import AnPInterface
|
||||||
|
from Abstracts.WebSocketsServerAbstract import WebSocketsServerAbstract
|
||||||
|
from Application.Event import Event
|
||||||
|
from Models.RequestModel import RequestModel
|
||||||
|
from Utils.Checks import Check
|
||||||
|
from Utils.Common import Common
|
||||||
|
|
||||||
|
class WebSocketsServerManager:
|
||||||
|
|
||||||
|
def __init__(self:Self, anp:AnPInterface, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
|
||||||
|
|
||||||
|
self.anp:AnPInterface = anp
|
||||||
|
self.__web_sockets:dict[str, WebSocketsServerAbstract] = {}
|
||||||
|
self.on_new_client:Event = Event()
|
||||||
|
self.on_message:Event = Event()
|
||||||
|
self.on_close:Event = Event()
|
||||||
|
self.on_error:Event = Event()
|
||||||
|
|
||||||
|
self.on_message.add(self.__receive)
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def update(self:Self) -> None:
|
||||||
|
|
||||||
|
key:str
|
||||||
|
|
||||||
|
for key in ("default_web_sockets_server_files", "web_sockets_server_files", "default_web_sockets_server", "web_sockets_server"):
|
||||||
|
self.add(self.anp.settings.get(key), True)
|
||||||
|
|
||||||
|
def reset(self:Self) -> None:
|
||||||
|
|
||||||
|
self.__web_sockets = {}
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def close(self:Self) -> None:
|
||||||
|
for web_socket in self.__web_sockets.values():
|
||||||
|
web_socket.close()
|
||||||
|
self.__web_sockets = {}
|
||||||
|
|
||||||
|
def __set(self:Self, name:str, web_socket:WebSocketsServerAbstract) -> None:
|
||||||
|
|
||||||
|
self.__web_sockets[name] = web_socket
|
||||||
|
|
||||||
|
web_socket.on_new_client.add(lambda id:self.on_new_client(web_socket, id, name))
|
||||||
|
web_socket.on_message.add(lambda id, message:self.on_message(web_socket, id, message, name))
|
||||||
|
web_socket.on_close.add(lambda id:self.on_close(web_socket, id, name))
|
||||||
|
web_socket.on_error.add(lambda id, exception:self.on_error(web_socket, id, exception, name))
|
||||||
|
|
||||||
|
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
|
||||||
|
|
||||||
|
subinputs:dict[str, Any|None]
|
||||||
|
|
||||||
|
for subinputs in Common.load_json(inputs, True):
|
||||||
|
|
||||||
|
key:str
|
||||||
|
value:Any|None
|
||||||
|
|
||||||
|
for key, value in subinputs.items():
|
||||||
|
if isinstance(value, WebSocketsServerAbstract):
|
||||||
|
if overwrite or key not in self.__web_sockets:
|
||||||
|
self.__set(key, value)
|
||||||
|
elif Check.is_dictionary(value):
|
||||||
|
if overwrite or key not in self.__web_sockets:
|
||||||
|
|
||||||
|
_type:str|None = Common.get_value("type", value)
|
||||||
|
|
||||||
|
if _type is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
Class:type[WebSocketsServerAbstract] = self.anp.models.get(WebSocketsServerAbstract, _type)
|
||||||
|
|
||||||
|
Class and issubclass(Class, WebSocketsServerAbstract) and self.__set(key, Class(self.anp, value))
|
||||||
|
|
||||||
|
def remove(self:Self, names:str|Sequence[str]) -> None:
|
||||||
|
for name in names if Check.is_array(names) else [names]:
|
||||||
|
if name in self.__web_sockets:
|
||||||
|
try:
|
||||||
|
self.__web_sockets[name].close()
|
||||||
|
del self.__web_sockets[name]
|
||||||
|
except Exception as exception:
|
||||||
|
self.anp.exception(exception, "web_socket_server_close_exception", {"name": name})
|
||||||
|
|
||||||
|
def get(self:Self, name:str) -> WebSocketsServerAbstract|None:
|
||||||
|
return self.__web_sockets.get(name)
|
||||||
|
|
||||||
|
def send(self:Self,
|
||||||
|
name:str,
|
||||||
|
controller:str,
|
||||||
|
method:str,
|
||||||
|
data:Optional[Any] = None,
|
||||||
|
clients:Optional[int|Sequence[int]] = None,
|
||||||
|
code:int = 200
|
||||||
|
) -> None:
|
||||||
|
if name in self.__web_sockets:
|
||||||
|
try:
|
||||||
|
self.__web_sockets[name].send(Common.data_encode({
|
||||||
|
"ok" : code >= 200 and code < 300,
|
||||||
|
"code" : code,
|
||||||
|
"controller" : controller,
|
||||||
|
"method" : method,
|
||||||
|
"data" : data
|
||||||
|
}), clients)
|
||||||
|
except Exception as exception:
|
||||||
|
self.anp.exception(exception, "web_socket_server_send_exception", {"name": name})
|
||||||
|
|
||||||
|
def __receive(self:Self, web_socket:WebSocketsServerAbstract, client:int, raw_data:str, name:str) -> None:
|
||||||
|
|
||||||
|
data:dict[str, Any|None] = Common.data_decode(raw_data)
|
||||||
|
|
||||||
|
if "controller" in data and "method" in data:
|
||||||
|
|
||||||
|
request:RequestModel = RequestModel()
|
||||||
|
|
||||||
|
request.data = data.get("data", None)
|
||||||
|
request.variables["web_socket"] = web_socket
|
||||||
|
request.variables["client_id"] = client
|
||||||
|
request.variables["web_socket_name"] = name
|
||||||
|
|
||||||
|
self.anp.controllers.execute(
|
||||||
|
data["controller"],
|
||||||
|
data["method"],
|
||||||
|
request
|
||||||
|
)
|
||||||
@ -21,6 +21,7 @@ class RequestModel:
|
|||||||
self.response_code:int = 0
|
self.response_code:int = 0
|
||||||
self.response_headers:dict[str, Any|None] = {}
|
self.response_headers:dict[str, Any|None] = {}
|
||||||
self.callback:Callable[[RequestModel, str|bytes|None], None]|None = None
|
self.callback:Callable[[RequestModel, str|bytes|None], None]|None = None
|
||||||
|
self.data:Any|None = None
|
||||||
|
|
||||||
def get(self:Self, key:str|Sequence[str], default:Optional[Any] = None) -> Any|None:
|
def get(self:Self, key:str|Sequence[str], default:Optional[Any] = None) -> Any|None:
|
||||||
return Common.get_value(key, (
|
return Common.get_value(key, (
|
||||||
|
|||||||
@ -8,6 +8,8 @@ from json import loads as json_decode
|
|||||||
from io import FileIO
|
from io import FileIO
|
||||||
from mimetypes import guess_type as get_mime_by_extension
|
from mimetypes import guess_type as get_mime_by_extension
|
||||||
from inspect import FrameInfo, stack as get_stack
|
from inspect import FrameInfo, stack as get_stack
|
||||||
|
from json import dumps as json_encode, loads as json_decode
|
||||||
|
from base64 import b64encode as base64_encode, b64decode as base64_decode
|
||||||
from Utils.Checks import Check
|
from Utils.Checks import Check
|
||||||
from Utils.Patterns import RE
|
from Utils.Patterns import RE
|
||||||
|
|
||||||
@ -272,3 +274,54 @@ class Common:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def is_file(path:str) -> bool:
|
def is_file(path:str) -> bool:
|
||||||
return is_file(path)
|
return is_file(path)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def json_encode(data:dict[str, Any|None]|Sequence[Any|None]) -> str|None:
|
||||||
|
try:
|
||||||
|
return json_encode(data)
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def json_decode(data:str) -> dict[str, Any|None]|Sequence[Any|None]|None:
|
||||||
|
try:
|
||||||
|
return json_decode(data)
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def base64_encode(data:bytes) -> str|None:
|
||||||
|
try:
|
||||||
|
return base64_encode(data).decode("utf-8")
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def base64_decode(data:str) -> bytes|None:
|
||||||
|
try:
|
||||||
|
return base64_decode(data.encode("utf-8"))
|
||||||
|
except Exception as exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def data_encode(cls:type[Self], data:Any|None) -> str|None:
|
||||||
|
if Check.is_json_data(data):
|
||||||
|
data = cls.json_encode(data)
|
||||||
|
elif not Check.is_string(data):
|
||||||
|
data = str(data)
|
||||||
|
return cls.base64_encode(data.encode("utf-8"))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def data_decode(cls:type[Self], data:str) -> Any|None:
|
||||||
|
if Check.is_string(data):
|
||||||
|
data = cls.base64_decode(data)
|
||||||
|
if Check.is_string(data):
|
||||||
|
try:
|
||||||
|
return cls.json_decode(data)
|
||||||
|
except Exception as exception:
|
||||||
|
return data
|
||||||
|
return None
|
||||||
@ -4,10 +4,12 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from Application.AnP import AnP
|
from Application.AnP import AnP
|
||||||
from Controllers.AIController import AIController
|
from Controllers.AIController import AIController
|
||||||
|
from Drivers.WebSocketServerDriver import WebSocketServerDriver
|
||||||
|
|
||||||
inputs:dict[str, dict[str, Any|None]] = {
|
inputs:dict[str, dict[str, Any|None]] = {
|
||||||
"default_models" : {
|
"default_models" : {
|
||||||
"AIController" : AIController
|
"AIController" : AIController,
|
||||||
|
"WebSocketServerDriver" : WebSocketServerDriver,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user