141 строка
4.5 KiB
Python
Исполняемый файл
141 строка
4.5 KiB
Python
Исполняемый файл
import asyncio
|
|
import logging
|
|
|
|
from argparse import ArgumentParser
|
|
|
|
from api.dmrapi import start_api
|
|
from dmrtools import Dispatcher
|
|
from dmrtools.app import App
|
|
from dmrtools.asyncnetwork import AsyncDatagramServer
|
|
from dmrtools.auth import IPeerAuth
|
|
|
|
|
|
class DMRMaster:
|
|
def __init__(self) -> None:
|
|
self.interface: str = '0.0.0.0'
|
|
self.port: int = 62031
|
|
self.web_interface: str = '0.0.0.0'
|
|
self.web_port: int = 8000
|
|
self.dispatcher: Dispatcher|None = None
|
|
self.dg_server: AsyncDatagramServer|None = None
|
|
|
|
def setup(self) -> None:
|
|
ap = ArgumentParser(
|
|
prog="dmrmaster.py",
|
|
description='0DMRMaster Server by Alexander Mokrov (UR6LKW)')
|
|
|
|
ap.add_argument('-i', '--interface', type=str,
|
|
help='Interface to listen on. Defaults to 0.0.0.0.')
|
|
ap.add_argument('-p', '--port', type=int,
|
|
help='UDP port to listen on. Defaults to 62031.')
|
|
ap.add_argument('--web-interface', type=str,
|
|
help='Interface to run API on. Defaults to 0.0.0.0.')
|
|
ap.add_argument('--web-port', type=int,
|
|
help='TCP port to run API on. Defaults to 8000.')
|
|
ap.add_argument('-l', '--log-file', type=str, help='Log filename')
|
|
ap.add_argument('-d', '--ll-debug', action='store_true',
|
|
help='Log level (INFO/DEBUG)')
|
|
|
|
args = ap.parse_args()
|
|
# print(args)
|
|
|
|
if args.interface is not None:
|
|
self.interface = args.interface
|
|
if args.port is not None:
|
|
self.port = args.port
|
|
if args.web_interface is not None:
|
|
self.web_interface = args.web_interface
|
|
if args.web_port is not None:
|
|
self.web_port = args.web_port
|
|
|
|
log_level = logging.DEBUG if args.ll_debug else logging.INFO
|
|
self.setup_log(log_level, args.log_file)
|
|
|
|
def setup_log(self, log_level = logging.DEBUG, log_file = None) -> None:
|
|
handlers = [logging.StreamHandler()]
|
|
|
|
if log_file:
|
|
file_handler = logging.FileHandler(log_file)
|
|
handlers.append(file_handler) # type: ignore[arg-type]
|
|
|
|
logging.basicConfig(
|
|
level=log_level,
|
|
format='%(asctime)s [%(levelname)s] %(message)s',
|
|
datefmt='%Y-%m-%d %H:%M:%S',
|
|
handlers=handlers
|
|
)
|
|
|
|
def set_peer_auth(self, peer_auth: IPeerAuth) -> None:
|
|
if self.dispatcher is None:
|
|
logging.error("Can't set auth: no dispatcher")
|
|
return
|
|
logging.debug(f"Peer auth set to {peer_auth}")
|
|
self.dispatcher.peer_auth = peer_auth
|
|
|
|
def register_app(self, app: App) -> None:
|
|
if self.dispatcher is None:
|
|
logging.error("Can't register app: no dispatcher")
|
|
return
|
|
self.dispatcher.app_keeper.register(app)
|
|
|
|
def config(self) -> None:
|
|
"""
|
|
Local config
|
|
"""
|
|
pass
|
|
|
|
def run(self) -> None:
|
|
"""
|
|
Run, go async and handle KeyboardInterupt
|
|
"""
|
|
try:
|
|
asyncio.run(self.__async_run())
|
|
except KeyboardInterrupt:
|
|
logging.info("Interrupted by user (Ctrl+C). Exiting gracefully.")
|
|
|
|
async def __async_run(self) -> None:
|
|
await self.__start_udp()
|
|
|
|
if not isinstance(self.dg_server, AsyncDatagramServer):
|
|
logging.critical(f"Can't listen on {self.interface}:{self.port}")
|
|
return
|
|
|
|
self.dispatcher = Dispatcher(self.dg_server)
|
|
|
|
self.config() # local config for apps
|
|
|
|
logging.info(f"Starting API on {self.web_interface}:{self.web_port}")
|
|
|
|
try:
|
|
await start_api(self.web_interface, self.web_port, self.dispatcher)
|
|
# await asyncio.Future() # Run forever
|
|
finally:
|
|
self.stop()
|
|
|
|
async def __start_udp(self) -> None:
|
|
logging.info(
|
|
f"Starting server listening on {self.interface}:{self.port}")
|
|
|
|
loop = asyncio.get_running_loop()
|
|
transport, self.dg_server = await loop.create_datagram_endpoint(
|
|
lambda: AsyncDatagramServer(),
|
|
local_addr=(self.interface, self.port))
|
|
|
|
def stop(self) -> None:
|
|
if self.dispatcher is not None:
|
|
self.dispatcher.shutdown()
|
|
self.dispatcher = None
|
|
if self.dg_server is not None:
|
|
self.dg_server.close()
|
|
self.dg_server = None
|
|
|
|
|
|
def main():
|
|
master = DMRMaster();
|
|
master.setup()
|
|
master.run()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|