55import logging
66import traceback
77import ssl
8+ from time import sleep
89
910import serial
1011from serial_asyncio import create_serial_connection
3233# --------------------------------------------------------------------------- #
3334# Allow access to server object, to e.g. make a shutdown
3435# --------------------------------------------------------------------------- #
35- ServerObject = None # pylint: disable=invalid-name
36+ _server_stopped = None # pylint: disable=invalid-name
37+ _server_stop = None # pylint: disable=invalid-name
3638
3739
3840def sslctx_provider (
@@ -551,11 +553,15 @@ async def serve_forever(self):
551553 try :
552554 await self .server .serve_forever ()
553555 except asyncio .exceptions .CancelledError :
554- pass
556+ raise
557+ except Exception as exc : # pylint: disable=broad-except
558+ txt = f"Server unexpected exception { exc } "
559+ _logger .error (txt )
555560 else :
556561 raise RuntimeError (
557562 "Can't call serve_forever on an already running server object"
558563 )
564+ _logger .info ("Server graceful shutdown." )
559565
560566 async def shutdown (self ):
561567 """Shutdown server."""
@@ -892,6 +898,32 @@ async def serve_forever(self):
892898# Creation Factories
893899# --------------------------------------------------------------------------- #
894900
901+ async def _helper_run_server (server , custom_functions ):
902+ """Help starting/stopping server."""
903+ global _server_stopped , _server_stop # pylint: disable=global-statement,invalid-name
904+
905+ for func in custom_functions :
906+ server .decoder .register (func )
907+ _server_stopped = asyncio .Event ()
908+ _server_stop = asyncio .Event ()
909+ try :
910+ server_task = asyncio .create_task (server .serve_forever ())
911+ except Exception as exc : # pylint: disable=broad-except
912+ txt = f"Server caught exception: { exc } "
913+ _logger .error (txt )
914+ await _server_stop .wait ()
915+ await server .shutdown ()
916+ server_task .cancel ()
917+ owntask = asyncio .current_task ()
918+ for task in asyncio .all_tasks ():
919+ if task != owntask :
920+ task .cancel ()
921+ try :
922+ await task
923+ except asyncio .CancelledError :
924+ pass
925+ _server_stopped .set ()
926+
895927
896928async def StartAsyncTcpServer ( # pylint: disable=invalid-name,dangerous-default-value
897929 context = None ,
@@ -914,17 +946,18 @@ async def StartAsyncTcpServer( # pylint: disable=invalid-name,dangerous-default
914946 :param kwargs: The rest
915947 :return: an initialized but inactive server object coroutine
916948 """
917- global ServerObject # pylint: disable=global-statement
918-
919949 framer = kwargs .pop ("framer" , ModbusSocketFramer )
920- ServerObject = ModbusTcpServer (context , framer , identity , address , ** kwargs )
921-
922- for func in custom_functions :
923- ServerObject .decoder .register (func ) # pragma: no cover
950+ server = ModbusTcpServer (
951+ context ,
952+ framer ,
953+ identity ,
954+ address ,
955+ ** kwargs
956+ )
924957
925958 if defer_start :
926- return ServerObject
927- await ServerObject . serve_forever ( )
959+ return server
960+ await _helper_run_server ( server , custom_functions )
928961
929962
930963async def StartAsyncTlsServer ( # pylint: disable=invalid-name,dangerous-default-value,too-many-arguments
@@ -963,10 +996,8 @@ async def StartAsyncTlsServer( # pylint: disable=invalid-name,dangerous-default
963996 :param kwargs: The rest
964997 :return: an initialized but inactive server object coroutine
965998 """
966- global ServerObject # pylint: disable=global-statement
967-
968999 framer = kwargs .pop ("framer" , ModbusTlsFramer )
969- ServerObject = ModbusTlsServer (
1000+ server = ModbusTlsServer (
9701001 context ,
9711002 framer ,
9721003 identity ,
@@ -980,13 +1011,9 @@ async def StartAsyncTlsServer( # pylint: disable=invalid-name,dangerous-default
9801011 allow_reuse_port = allow_reuse_port ,
9811012 ** kwargs ,
9821013 )
983-
984- for func in custom_functions :
985- ServerObject .decoder .register (func ) # pragma: no cover
986-
9871014 if defer_start :
988- return ServerObject
989- await ServerObject . serve_forever ( )
1015+ return server
1016+ await _helper_run_server ( server , custom_functions )
9901017
9911018
9921019async def StartAsyncUdpServer ( # pylint: disable=invalid-name,dangerous-default-value
@@ -1009,17 +1036,17 @@ async def StartAsyncUdpServer( # pylint: disable=invalid-name,dangerous-default
10091036 up without the ability to shut it off
10101037 :param kwargs:
10111038 """
1012- global ServerObject # pylint: disable=global-statement
1013-
10141039 framer = kwargs .pop ("framer" , ModbusSocketFramer )
1015- ServerObject = ModbusUdpServer (context , framer , identity , address , ** kwargs )
1016-
1017- for func in custom_functions :
1018- ServerObject .decoder .register (func ) # pragma: no cover
1019-
1040+ server = ModbusUdpServer (
1041+ context ,
1042+ framer ,
1043+ identity ,
1044+ address ,
1045+ ** kwargs
1046+ )
10201047 if defer_start :
1021- return ServerObject
1022- await ServerObject . serve_forever ( )
1048+ return server
1049+ await _helper_run_server ( server , custom_functions )
10231050
10241051
10251052async def StartAsyncSerialServer ( # pylint: disable=invalid-name,dangerous-default-value
@@ -1040,17 +1067,17 @@ async def StartAsyncSerialServer( # pylint: disable=invalid-name,dangerous-defa
10401067 up without the ability to shut it off
10411068 :param kwargs: The rest
10421069 """
1043- global ServerObject # pylint: disable=global-statement
1044-
10451070 framer = kwargs .pop ("framer" , ModbusAsciiFramer )
1046- ServerObject = ModbusSerialServer (context , framer , identity = identity , ** kwargs )
1047- for func in custom_functions :
1048- ServerObject .decoder .register (func )
1049-
1071+ server = ModbusSerialServer (
1072+ context ,
1073+ framer ,
1074+ identity = identity ,
1075+ ** kwargs
1076+ )
10501077 if defer_start :
1051- return ServerObject
1052- await ServerObject .start ()
1053- await ServerObject . serve_forever ( )
1078+ return server
1079+ await server .start ()
1080+ await _helper_run_server ( server , custom_functions )
10541081
10551082
10561083def StartSerialServer (** kwargs ): # pylint: disable=invalid-name
@@ -1075,13 +1102,18 @@ def StartUdpServer(**kwargs): # pylint: disable=invalid-name
10751102
10761103async def ServerAsyncStop (): # pylint: disable=invalid-name
10771104 """Terminate server."""
1078- global ServerObject # pylint: disable=global-statement, invalid-name
1105+ global _server_stopped , _server_stop # pylint: disable=invalid-name,global-variable-not-assigned
10791106
1080- if ServerObject :
1081- await ServerObject .shutdown ()
1082- ServerObject = None
1107+ _server_stop .set ()
1108+ try :
1109+ await _server_stopped .wait ()
1110+ except asyncio .exceptions .CancelledError :
1111+ pass
10831112
10841113
10851114def ServerStop (): # pylint: disable=invalid-name
10861115 """Terminate server."""
1087- asyncio .run (ServerAsyncStop ())
1116+ global _server_stopped , _server_stop # pylint: disable=invalid-name,global-variable-not-assigned
1117+
1118+ _server_stop .set ()
1119+ sleep (10 )
0 commit comments