@@ -24,7 +24,8 @@ public class Ism7Client
2424 private readonly string _host ;
2525 private readonly ConcurrentDictionary < Type , XmlSerializer > _serializers = new ConcurrentDictionary < Type , XmlSerializer > ( ) ;
2626 private readonly Ism7Config _config ;
27- private ResponseDispatcher _dispatcher = new ResponseDispatcher ( ) ;
27+ private readonly Pipe _pipe ;
28+ private readonly ResponseDispatcher _dispatcher = new ResponseDispatcher ( ) ;
2829 private int _nextBundleId = 0 ;
2930 private short _lastKeepAlive = 0 ;
3031 private Stream _sslStream ;
@@ -41,67 +42,18 @@ public Ism7Client(Func<Ism7Config, CancellationToken, Task> messageHandler, stri
4142 _messageHandler = messageHandler ;
4243 _host = host ;
4344 _config = new Ism7Config ( parameterPath , localizer ) ;
45+ _pipe = new Pipe ( ) ;
4446 }
4547
4648 public async Task RunAsync ( string password , CancellationToken cancellationToken )
4749 {
48- var retryDelay = TimeSpan . FromSeconds ( 1 ) ;
49- var maxRetryDelay = TimeSpan . FromSeconds ( 10 ) ;
50- while ( ! cancellationToken . IsCancellationRequested )
50+ using ( _sslStream = await ConnectAsync ( cancellationToken ) )
5151 {
52- Task fillPipeTask = Task . CompletedTask ;
53- Task readPipeTask = Task . CompletedTask ;
54- Task keepAliveTask = Task . CompletedTask ;
55- using var attemptCts = CancellationTokenSource . CreateLinkedTokenSource ( cancellationToken ) ;
56- try
57- {
58- _dispatcher = new ResponseDispatcher ( ) ;
59- _lastKeepAlive = 0 ;
60- _nextBundleId = 0 ;
61- _nextSequenceId = 1 ;
62- using ( _sslStream = await ConnectAsync ( attemptCts . Token ) )
63- {
64- var pipe = new Pipe ( ) ;
65- fillPipeTask = FillPipeAsync ( pipe . Writer , attemptCts . Token ) ;
66- readPipeTask = ReadPipeAsync ( pipe . Reader , attemptCts . Token ) ;
67- _ = Task . WhenAny ( fillPipeTask , readPipeTask )
68- . ContinueWith ( _ => attemptCts . Cancel ( ) , TaskScheduler . Default ) ;
69- await AuthenticateAsync ( password , attemptCts . Token ) ;
70- keepAliveTask = KeepAliveAsync ( attemptCts . Token ) ;
71- await Task . WhenAny ( fillPipeTask , readPipeTask , keepAliveTask ) ;
72- }
73- }
74- catch ( OperationCanceledException ) when ( cancellationToken . IsCancellationRequested )
75- {
76- throw ;
77- }
78- catch ( Exception ex ) when ( ex is SocketException || ex is IOException )
79- {
80- Console . WriteLine ( $ "connection lost ({ ex . GetType ( ) . Name } ): { ex . Message } ") ;
81- }
82- finally
83- {
84- attemptCts . Cancel ( ) ;
85- try
86- {
87- await Task . WhenAll ( fillPipeTask , readPipeTask , keepAliveTask ) ;
88- }
89- catch
90- {
91- // Swallow errors from background tasks during shutdown.
92- }
93- }
94-
95- if ( cancellationToken . IsCancellationRequested )
96- {
97- break ;
98- }
99-
100- Console . WriteLine ( $ "reconnecting in { retryDelay . TotalSeconds : 0} s...") ;
101- await Task . Delay ( retryDelay , cancellationToken ) ;
102- retryDelay = retryDelay < maxRetryDelay
103- ? TimeSpan . FromSeconds ( Math . Min ( maxRetryDelay . TotalSeconds , retryDelay . TotalSeconds * 2 ) )
104- : maxRetryDelay ;
52+ var fillPipeTask = FillPipeAsync ( _pipe . Writer , cancellationToken ) ;
53+ var readPipeTask = ReadPipeAsync ( _pipe . Reader , cancellationToken ) ;
54+ await AuthenticateAsync ( password , cancellationToken ) ;
55+ var keepAlive = KeepAliveAsync ( cancellationToken ) ;
56+ await Task . WhenAny ( fillPipeTask , readPipeTask , keepAlive ) ;
10557 }
10658 }
10759
@@ -202,11 +154,6 @@ private async Task FillPipeAsync(PipeWriter target, CancellationToken cancellati
202154 }
203155 catch ( Exception ex )
204156 {
205- if ( cancellationToken . IsCancellationRequested && ex is OperationCanceledException )
206- {
207- await target . CompleteAsync ( ) ;
208- return ;
209- }
210157 Console . WriteLine ( ex ) ;
211158 await target . CompleteAsync ( ex ) ;
212159 }
@@ -276,11 +223,6 @@ private async Task ReadPipeAsync(PipeReader source, CancellationToken cancellati
276223 }
277224 catch ( Exception ex )
278225 {
279- if ( cancellationToken . IsCancellationRequested && ex is OperationCanceledException )
280- {
281- await source . CompleteAsync ( ) ;
282- return ;
283- }
284226 Console . WriteLine ( ex ) ;
285227 await source . CompleteAsync ( ex ) ;
286228 }
@@ -343,9 +285,13 @@ private async Task LoadInitialValuesAsync(CancellationToken cancellationToken)
343285 foreach ( var ( bundleId , infoReads ) in bundles )
344286 {
345287 NextBundleId ( ) ;
346- var responseTask = WaitForResponseAsync (
288+ _dispatcher . SubscribeOnce (
347289 x => x . MessageType == PayloadType . TgrBundleResp && ( ( TelegramBundleResp ) x ) . BundleId == bundleId ,
348- cancellationToken ) ;
290+ ( r , c ) =>
291+ {
292+ semaphore . Release ( ) ;
293+ return OnInitialValuesAsync ( r , c ) ;
294+ } ) ;
349295 foreach ( var infoRead in infoReads )
350296 {
351297 infoRead . BusAddress = busAddress ;
@@ -361,8 +307,6 @@ await SendAsync(new TelegramBundleReq
361307 TelegramBundleType = TelegramBundleType . pull ,
362308 InfoReadTelegrams = infoReads
363309 } , cancellationToken ) ;
364- var response = ( TelegramBundleResp ) await responseTask ;
365- await OnInitialValuesAsync ( response , cancellationToken ) ;
366310 }
367311 }
368312 if ( OnInitializationFinishedAsync is not null )
@@ -390,26 +334,19 @@ private async Task OnInitialValuesAsync(IResponse response, CancellationToken ca
390334 }
391335 }
392336
393- private async Task AuthenticateAsync ( string password , CancellationToken cancellationToken )
337+ private ValueTask AuthenticateAsync ( string password , CancellationToken cancellationToken )
394338 {
395- var responseTask = WaitForResponseAsync ( x => x . MessageType == PayloadType . DirectLogonResp , cancellationToken ) ;
396- await SendAsync ( new LoginReq { Password = password } , cancellationToken ) ;
397- var resp = ( LoginResp ) await responseTask ;
398- if ( resp . State != LoginState . ok )
399- throw new InvalidDataException ( "invalid login state" ) ;
400- await LoadInitialValuesAsync ( cancellationToken ) ;
339+ _dispatcher . SubscribeOnce ( x => x . MessageType == PayloadType . DirectLogonResp ,
340+ OnAuthenticateAsync ) ;
341+ return SendAsync ( new LoginReq { Password = password } , cancellationToken ) ;
401342 }
402343
403- private async Task < IResponse > WaitForResponseAsync ( Predicate < IResponse > predicate , CancellationToken cancellationToken )
344+ private Task OnAuthenticateAsync ( IResponse response , CancellationToken cancellationToken )
404345 {
405- var tcs = new TaskCompletionSource < IResponse > ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
406- using var registration = cancellationToken . Register ( ( ) => tcs . TrySetCanceled ( cancellationToken ) ) ;
407- _dispatcher . SubscribeOnce ( predicate , ( response , ct ) =>
408- {
409- tcs . TrySetResult ( response ) ;
410- return Task . CompletedTask ;
411- } ) ;
412- return await tcs . Task ;
346+ var resp = ( LoginResp ) response ;
347+ if ( resp . State != LoginState . ok )
348+ throw new InvalidDataException ( "invalid login state" ) ;
349+ return LoadInitialValuesAsync ( cancellationToken ) ;
413350 }
414351
415352 private async Task KeepAliveAsync ( CancellationToken cancellationToken )
0 commit comments