2
2
# pylint: disable=missing-type-doc
3
3
import asyncio
4
4
import logging
5
- import platform
6
5
import ssl
7
6
import traceback
8
7
from binascii import b2a_hex
@@ -500,7 +499,6 @@ def __init__(
500
499
address = None ,
501
500
handler = None ,
502
501
allow_reuse_address = False ,
503
- allow_reuse_port = False ,
504
502
defer_start = False ,
505
503
backlog = 20 ,
506
504
** kwargs ,
@@ -519,8 +517,6 @@ def __init__(
519
517
receives connection create/teardown events
520
518
:param allow_reuse_address: Whether the server will allow the
521
519
reuse of an address.
522
- :param allow_reuse_port: Whether the server will allow the
523
- reuse of a port.
524
520
:param backlog: is the maximum number of queued connections
525
521
passed to listen(). Defaults to 20, increase if many
526
522
connections are being made and broken to your Modbus slave
@@ -556,7 +552,6 @@ def __init__(
556
552
self .server = None
557
553
self .factory_parms = {
558
554
"reuse_address" : allow_reuse_address ,
559
- "reuse_port" : allow_reuse_port ,
560
555
"backlog" : backlog ,
561
556
"start_serving" : not defer_start ,
562
557
}
@@ -621,7 +616,6 @@ def __init__( # pylint: disable=too-many-arguments
621
616
reqclicert = False ,
622
617
handler = None ,
623
618
allow_reuse_address = False ,
624
- allow_reuse_port = False ,
625
619
defer_start = False ,
626
620
backlog = 20 ,
627
621
** kwargs ,
@@ -646,8 +640,6 @@ def __init__( # pylint: disable=too-many-arguments
646
640
receives connection create/teardown events
647
641
:param allow_reuse_address: Whether the server will allow the
648
642
reuse of an address.
649
- :param allow_reuse_port: Whether the server will allow the
650
- reuse of a port.
651
643
:param backlog: is the maximum number of queued connections
652
644
passed to listen(). Defaults to 20, increase if many
653
645
connections are being made and broken to your Modbus slave
@@ -665,7 +657,6 @@ def __init__( # pylint: disable=too-many-arguments
665
657
address = address ,
666
658
handler = handler ,
667
659
allow_reuse_address = allow_reuse_address ,
668
- allow_reuse_port = allow_reuse_port ,
669
660
defer_start = defer_start ,
670
661
backlog = backlog ,
671
662
** kwargs ,
@@ -689,7 +680,6 @@ def __init__(
689
680
identity = None ,
690
681
address = None ,
691
682
handler = None ,
692
- allow_reuse_port = False ,
693
683
defer_start = False , # pylint: disable=unused-argument
694
684
backlog = 20 , # pylint: disable=unused-argument
695
685
** kwargs ,
@@ -731,12 +721,10 @@ def __init__(
731
721
self .protocol = None
732
722
self .endpoint = None
733
723
self .on_connection_terminated = None
734
- self .stop_serving = self .loop .create_future ()
735
724
# asyncio future that will be done once server has started
736
725
self .serving = self .loop .create_future ()
737
726
self .factory_parms = {
738
727
"local_addr" : self .address ,
739
- "reuse_port" : allow_reuse_port ,
740
728
"allow_broadcast" : True ,
741
729
}
742
730
@@ -749,9 +737,12 @@ async def serve_forever(self):
749
737
** self .factory_parms ,
750
738
)
751
739
except asyncio .exceptions .CancelledError :
752
- pass
740
+ raise
741
+ except Exception as exc :
742
+ txt = f"Server unexpected exception { exc } "
743
+ _logger .error (txt )
744
+ raise RuntimeError (exc ) from exc
753
745
self .serving .set_result (True )
754
- await self .stop_serving
755
746
else :
756
747
raise RuntimeError (
757
748
"Can't call serve_forever on an already running server object"
@@ -765,13 +756,10 @@ async def server_close(self):
765
756
"""Close server."""
766
757
if self .endpoint :
767
758
self .endpoint .running = False
768
- if not self .stop_serving .done ():
769
- self .stop_serving .set_result (True )
770
759
if self .endpoint is not None and self .endpoint .handler_task is not None :
771
760
self .endpoint .handler_task .cancel ()
772
761
if self .protocol is not None :
773
762
self .protocol .close ()
774
- # TBD await self.protocol.wait_closed()
775
763
self .protocol = None
776
764
777
765
@@ -812,6 +800,7 @@ def __init__(
812
800
:param response_manipulator: Callback method for
813
801
manipulating the response
814
802
"""
803
+ self .loop = kwargs .get ("loop" ) or asyncio .get_event_loop ()
815
804
self .bytesize = kwargs .get ("bytesize" , Defaults .Bytesize )
816
805
self .parity = kwargs .get ("parity" , Defaults .Parity )
817
806
self .baudrate = kwargs .get ("baudrate" , Defaults .Baudrate )
@@ -862,7 +851,7 @@ async def _connect(self):
862
851
return
863
852
try :
864
853
self .transport , self .protocol = await create_serial_connection (
865
- asyncio . get_event_loop () ,
854
+ self . loop ,
866
855
lambda : self .handler (self ),
867
856
self .device ,
868
857
baudrate = self .baudrate ,
@@ -887,44 +876,56 @@ def on_connection_lost(self):
887
876
self .transport .close ()
888
877
self .transport = None
889
878
self .protocol = None
890
-
891
- self ._check_reconnect ()
879
+ if self . server is None :
880
+ self ._check_reconnect ()
892
881
893
882
async def shutdown (self ):
894
883
"""Terminate server."""
895
884
if self .transport is not None :
896
- self .transport .close ()
897
- self .transport = None
898
- self .protocol = None
885
+ self .transport .abort ()
886
+ if self .server is not None :
887
+ self .server .close ()
888
+ await asyncio .wait_for (self .server .wait_closed (), 10 )
889
+ self .server = None
890
+ self .transport = None
891
+ self .protocol = None
899
892
900
893
def _check_reconnect (self ):
901
894
"""Check reconnect."""
902
895
txt = f"checking autoreconnect { self .auto_reconnect } { self .reconnecting_task } "
903
896
_logger .debug (txt )
904
897
if self .auto_reconnect and (self .reconnecting_task is None ):
905
898
_logger .debug ("Scheduling serial connection reconnect" )
906
- loop = asyncio .get_event_loop ()
907
- self .reconnecting_task = loop .create_task (self ._delayed_connect ())
899
+ self .reconnecting_task = self .loop .create_task (self ._delayed_connect ())
908
900
909
901
async def serve_forever (self ):
910
902
"""Start endless loop."""
903
+ if self .server :
904
+ raise RuntimeError (
905
+ "Can't call serve_forever on an already running server object"
906
+ )
911
907
if self .device .startswith ("socket:" ):
912
908
# Socket server means listen so start a socket server
913
- parts = self .device [7 :].split (":" )
914
- host_port = ("" , int (parts [1 ]))
915
- self .server = await asyncio . get_event_loop () .create_server (
909
+ parts = self .device [9 :].split (":" )
910
+ host_addr = (parts [ 0 ] , int (parts [1 ]))
911
+ self .server = await self . loop .create_server (
916
912
lambda : self .handler (self ),
917
- * host_port ,
913
+ * host_addr ,
918
914
reuse_address = True ,
919
- reuse_port = True ,
920
915
start_serving = True ,
921
916
backlog = 20 ,
922
917
)
923
- await self .server .serve_forever ()
918
+ try :
919
+ await self .server .serve_forever ()
920
+ except asyncio .exceptions .CancelledError :
921
+ raise
922
+ except Exception as exc : # pylint: disable=broad-except
923
+ txt = f"Server unexpected exception { exc } "
924
+ _logger .error (txt )
924
925
return
925
926
926
- while True :
927
- await asyncio .sleep (360 )
927
+ while self . server or self . transport or self . protocol :
928
+ await asyncio .sleep (10 )
928
929
929
930
930
931
# --------------------------------------------------------------------------- #
@@ -951,64 +952,50 @@ def __init__(self, server, custom_functions, register):
951
952
self .job_stop = asyncio .Event ()
952
953
self .job_is_stopped = asyncio .Event ()
953
954
self .task = None
955
+ self .loop = asyncio .get_event_loop ()
954
956
955
957
@classmethod
956
958
def get_server (cls ):
957
959
"""Get server at index."""
958
- return cls ._servers [- 1 ]
960
+ return cls ._servers [- 1 ] if cls . _servers else None
959
961
960
962
def _remove (self ):
961
963
"""Remove server from active list."""
962
964
server = self ._servers [- 1 ]
963
965
self ._servers .pop ()
964
966
del server
965
967
968
+ async def _run (self ):
969
+ """Help starting/stopping server."""
970
+ # self.task = asyncio.create_task(self.server.serve_forever())
971
+ # await self.job_stop.wait()
972
+ # await self.server.shutdown()
973
+ # await asyncio.sleep(0.1)
974
+ # self.task.cancel()
975
+ # await asyncio.sleep(0.1)
976
+ # try:
977
+ # await asyncio.wait_for(self.task, 10)
978
+ # except asyncio.CancelledError:
979
+ # pass
980
+ # self.job_is_stopped.set()
981
+
966
982
async def run (self ):
967
983
"""Help starting/stopping server."""
968
984
try :
969
- self .task = asyncio .create_task (self .server .serve_forever ())
970
- except Exception as exc : # pylint: disable=broad-except
971
- txt = f"Server caught exception: { exc } "
972
- _logger .error (txt )
973
- await self .job_stop .wait ()
974
- await self .server .shutdown ()
975
- await asyncio .sleep (0.1 )
976
- self .task .cancel ()
977
- await asyncio .sleep (0.1 )
978
- try :
979
- await asyncio .wait_for (self .task , 10 )
985
+ # await self._run()
986
+ await self .server .serve_forever ()
980
987
except asyncio .CancelledError :
981
988
pass
982
- if platform .system ().lower () == "windows" :
983
- owntask = asyncio .current_task ()
984
- for task in asyncio .all_tasks ():
985
- if task != owntask :
986
- task .cancel ()
987
- try :
988
- await asyncio .wait_for (task , 10 )
989
- except asyncio .CancelledError :
990
- pass
991
- self .job_is_stopped .set ()
992
-
993
- def request_stop (self ):
994
- """Request server stop."""
995
- self .job_stop .set ()
996
989
997
990
async def async_await_stop (self ):
998
991
"""Wait for server stop."""
999
- try :
1000
- await self .job_is_stopped .wait ()
1001
- except asyncio .exceptions .CancelledError :
1002
- pass
1003
- self ._remove ()
1004
-
1005
- def await_stop (self ):
1006
- """Wait for server stop."""
1007
- for i in range (30 ): # Loop for 3 seconds
1008
- sleep (0.1 ) # in steps of 100 milliseconds.
1009
- if self .job_is_stopped .is_set ():
1010
- break
1011
- self ._remove ()
992
+ await self .server .shutdown ()
993
+ # self.job_stop.set()
994
+ # try:
995
+ # await asyncio.wait_for(self.job_is_stopped.wait(), 60)
996
+ # except asyncio.exceptions.CancelledError:
997
+ # pass
998
+ # self._remove()
1012
999
1013
1000
1014
1001
async def StartAsyncTcpServer ( # pylint: disable=invalid-name,dangerous-default-value
@@ -1035,10 +1022,10 @@ async def StartAsyncTcpServer( # pylint: disable=invalid-name,dangerous-default
1035
1022
server = ModbusTcpServer (
1036
1023
context , kwargs .pop ("framer" , ModbusSocketFramer ), identity , address , ** kwargs
1037
1024
)
1038
- job = _serverList ( server , custom_functions , not defer_start )
1039
- if defer_start :
1040
- return server
1041
- await job . run ()
1025
+ if not defer_start :
1026
+ job = _serverList ( server , custom_functions , not defer_start )
1027
+ await job . run ()
1028
+ return server
1042
1029
1043
1030
1044
1031
async def StartAsyncTlsServer ( # pylint: disable=invalid-name,dangerous-default-value,too-many-arguments
@@ -1051,7 +1038,6 @@ async def StartAsyncTlsServer( # pylint: disable=invalid-name,dangerous-default
1051
1038
password = None ,
1052
1039
reqclicert = False ,
1053
1040
allow_reuse_address = False ,
1054
- allow_reuse_port = False ,
1055
1041
custom_functions = [],
1056
1042
defer_start = False ,
1057
1043
** kwargs ,
@@ -1068,7 +1054,6 @@ async def StartAsyncTlsServer( # pylint: disable=invalid-name,dangerous-default
1068
1054
:param reqclicert: Force the sever request client's certificate
1069
1055
:param allow_reuse_address: Whether the server will allow the reuse of an
1070
1056
address.
1071
- :param allow_reuse_port: Whether the server will allow the reuse of a port.
1072
1057
:param custom_functions: An optional list of custom function classes
1073
1058
supported by server instance.
1074
1059
:param defer_start: if set, the server object will be returned ready to start.
@@ -1088,13 +1073,12 @@ async def StartAsyncTlsServer( # pylint: disable=invalid-name,dangerous-default
1088
1073
password ,
1089
1074
reqclicert ,
1090
1075
allow_reuse_address = allow_reuse_address ,
1091
- allow_reuse_port = allow_reuse_port ,
1092
1076
** kwargs ,
1093
1077
)
1094
- job = _serverList ( server , custom_functions , not defer_start )
1095
- if defer_start :
1096
- return server
1097
- await job . run ()
1078
+ if not defer_start :
1079
+ job = _serverList ( server , custom_functions , not defer_start )
1080
+ await job . run ()
1081
+ return server
1098
1082
1099
1083
1100
1084
async def StartAsyncUdpServer ( # pylint: disable=invalid-name,dangerous-default-value
@@ -1120,10 +1104,10 @@ async def StartAsyncUdpServer( # pylint: disable=invalid-name,dangerous-default
1120
1104
server = ModbusUdpServer (
1121
1105
context , kwargs .pop ("framer" , ModbusSocketFramer ), identity , address , ** kwargs
1122
1106
)
1123
- job = _serverList ( server , custom_functions , not defer_start )
1124
- if defer_start :
1125
- return server
1126
- await job . run ()
1107
+ if not defer_start :
1108
+ job = _serverList ( server , custom_functions , not defer_start )
1109
+ await job . run ()
1110
+ return server
1127
1111
1128
1112
1129
1113
async def StartAsyncSerialServer ( # pylint: disable=invalid-name,dangerous-default-value
@@ -1147,11 +1131,10 @@ async def StartAsyncSerialServer( # pylint: disable=invalid-name,dangerous-defa
1147
1131
server = ModbusSerialServer (
1148
1132
context , kwargs .pop ("framer" , ModbusAsciiFramer ), identity = identity , ** kwargs
1149
1133
)
1150
- job = _serverList (server , custom_functions , not defer_start )
1151
- if defer_start :
1152
- return server
1153
- await server .start ()
1154
- await job .run ()
1134
+ if not defer_start :
1135
+ job = _serverList (server , custom_functions , not defer_start )
1136
+ await job .run ()
1137
+ return server
1155
1138
1156
1139
1157
1140
def StartSerialServer (** kwargs ): # pylint: disable=invalid-name
@@ -1176,13 +1159,18 @@ def StartUdpServer(**kwargs): # pylint: disable=invalid-name
1176
1159
1177
1160
async def ServerAsyncStop (): # pylint: disable=invalid-name
1178
1161
"""Terminate server."""
1179
- my_job = _serverList .get_server ()
1180
- my_job .request_stop ()
1181
- await my_job .async_await_stop ()
1162
+ if my_job := _serverList .get_server ():
1163
+ await my_job .async_await_stop ()
1164
+ await asyncio .sleep (0.1 )
1165
+ else :
1166
+ raise RuntimeError ("ServerAsyncStop called without server task active." )
1182
1167
1183
1168
1184
1169
def ServerStop (): # pylint: disable=invalid-name
1185
1170
"""Terminate server."""
1186
- my_job = _serverList .get_server ()
1187
- my_job .request_stop ()
1188
- my_job .await_stop ()
1171
+ if my_job := _serverList .get_server ():
1172
+ if my_job .loop .is_running ():
1173
+ asyncio .run_coroutine_threadsafe (my_job .async_await_stop (), my_job .loop )
1174
+ sleep (0.1 )
1175
+ else :
1176
+ raise RuntimeError ("ServerStop called without server task active." )
0 commit comments