From 0daf2f90297dafe3b68597e0821eedba01246e8f Mon Sep 17 00:00:00 2001 From: Shuai Zhang Date: Tue, 10 Dec 2019 21:51:51 +0800 Subject: [PATCH] Finished PlcHostedService. --- .../PlcClient.cs | 7 +- .../PlcClientApi.cs | 2 +- .../PlcClientSending.cs | 8 +- .../PlcServer.cs | 4 +- .../PlcHostedService.cs | 89 ++++++++++++++++++- codelab/ProtocolLab/MainWindow.xaml.cs | 1 - 6 files changed, 99 insertions(+), 12 deletions(-) diff --git a/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcClient.cs b/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcClient.cs index d39634ea..f57ddbf1 100644 --- a/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcClient.cs +++ b/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcClient.cs @@ -37,7 +37,6 @@ public partial class PlcClient : IDisposable new ConcurrentDictionary(); private int sequenceNumberGenerator = 0; - private bool closed = false; private bool disposedValue = false; public PlcClient(ILogger logger, TcpClient tcpClient) @@ -82,7 +81,7 @@ public void Dispose() GC.SuppressFinalize(this); } - public void Close() + public async void Close() { if (!this.closingCancellationTokenSource.IsCancellationRequested) { @@ -90,6 +89,10 @@ public void Close() this.requestContextSendingBufferBlock.Complete(); this.tcpClient.Close(); this.OnClosed?.Invoke(this, EventArgs.Empty); + + await this.sendingBackgroundTask.ConfigureAwait(false); + await this.receivingBackgroundTask.ConfigureAwait(false); + await this.deadlineBackgroundTask.ConfigureAwait(false); } } diff --git a/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcClientApi.cs b/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcClientApi.cs index c1d95520..a6ea6b1f 100644 --- a/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcClientApi.cs +++ b/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcClientApi.cs @@ -165,7 +165,7 @@ public async Task UpdateSwitchAsync(UpdateSwitchRequest request, DateTim private Task InvokeAsync(PlcFrame request, DateTime? deadline) { - if (this.closed) + if (this.closingCancellationTokenSource.IsCancellationRequested) { return Task.FromException(new RpcException(Status.DefaultCancelled)); } diff --git a/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcClientSending.cs b/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcClientSending.cs index 91c99819..b6cfd8ed 100644 --- a/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcClientSending.cs +++ b/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcClientSending.cs @@ -22,10 +22,10 @@ private async void SendingBackgroundTaskEntryPoint() .OutputAvailableAsync() .ConfigureAwait(false)) { - DateTime now = DateTime.Now; + DateTime utcNow = DateTime.UtcNow; while (this.requestContextSendingBufferBlock.TryReceive(out PlcRequestContext requestContext)) { - this.ProcessRequest(now, requestContext); + this.ProcessRequest(utcNow, requestContext); } } } @@ -36,9 +36,9 @@ private async void SendingBackgroundTaskEntryPoint() } } - private void ProcessRequest(DateTime now, PlcRequestContext requestContext) + private void ProcessRequest(DateTime utcNow, PlcRequestContext requestContext) { - if (requestContext.Deadline < now) + if (requestContext.Deadline?.ToUniversalTime() < utcNow) { requestContext.TaskCompletionSource.SetException(new RpcException( new Status(StatusCode.DeadlineExceeded, string.Empty))); diff --git a/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcServer.cs b/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcServer.cs index b4974031..733719b3 100644 --- a/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcServer.cs +++ b/GeothermalResearchInstitute/GeothermalResearchInstitute.PlcV2/PlcServer.cs @@ -36,7 +36,9 @@ public void Stop() public async Task AcceptAsync() { - TcpClient tcpClient = await this.tcpListener.AcceptTcpClientAsync().ConfigureAwait(false); + TcpClient tcpClient = await this.tcpListener + .AcceptTcpClientAsync() + .ConfigureAwait(false); return new PlcClient(this.loggerFactory.CreateLogger(), tcpClient); } } diff --git a/GeothermalResearchInstitute/GeothermalResearchInstitute.ServerConsole/PlcHostedService.cs b/GeothermalResearchInstitute/GeothermalResearchInstitute.ServerConsole/PlcHostedService.cs index a42c0e29..2c73e960 100644 --- a/GeothermalResearchInstitute/GeothermalResearchInstitute.ServerConsole/PlcHostedService.cs +++ b/GeothermalResearchInstitute/GeothermalResearchInstitute.ServerConsole/PlcHostedService.cs @@ -3,16 +3,24 @@ // Licensed under the GPLv3 license. See LICENSE file in the project root for full license information. // +using System; using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using GeothermalResearchInstitute.PlcV2; +using GeothermalResearchInstitute.v2; using Google.Protobuf; +using Grpc.Core; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace GeothermalResearchInstitute.ServerConsole { + [SuppressMessage( + "Design", + "CA1001:具有可释放字段的类型应该是可释放的", + Justification = "Disposed in StopAsync, ensured by framework.")] public class PlcHostedService : IHostedService { private readonly ILogger logger; @@ -21,6 +29,9 @@ public class PlcHostedService : IHostedService private readonly ConcurrentDictionary plcDictionary = new ConcurrentDictionary(); + private CancellationTokenSource cancellationTokenSource; + private Task backgroundTask; + public PlcHostedService(ILogger logger, PlcServer plcServer) { this.logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); @@ -37,14 +48,86 @@ public Task StartAsync(CancellationToken cancellationToken) this.plcServer.Start(); this.logger.LogInformation("PLC server is listening on {0}", this.plcServer.LocalEndPoint); - // TODO: Run background task + this.plcDictionary.Clear(); + this.cancellationTokenSource = new CancellationTokenSource(); + this.backgroundTask = Task.Factory.StartNew( + this.BackgroundTaskEntryPoint, + this.cancellationTokenSource.Token, + TaskCreationOptions.LongRunning | TaskCreationOptions.DenyChildAttach, + TaskScheduler.Default); + return Task.CompletedTask; } - public Task StopAsync(CancellationToken cancellationToken) + public async Task StopAsync(CancellationToken cancellationToken) { + this.cancellationTokenSource.Cancel(); + await this.backgroundTask.ConfigureAwait(false); + + foreach (ByteString id in this.plcDictionary.Keys) + { + if (this.plcDictionary.TryRemove(id, out PlcClient client)) + { + client.Close(); + client.Dispose(); + } + } + this.plcServer.Stop(); - return Task.CompletedTask; + + this.cancellationTokenSource.Dispose(); + this.cancellationTokenSource = null; + this.backgroundTask.Dispose(); + this.backgroundTask = null; + } + + private async void BackgroundTaskEntryPoint() + { + while (!this.cancellationTokenSource.IsCancellationRequested) + { + PlcClient client = await this.plcServer.AcceptAsync().ConfigureAwait(false); + + ConnectResponse response; + try + { + response = await client + .ConnectAsync(new ConnectRequest(), DateTime.UtcNow.AddSeconds(10)) + .ConfigureAwait(false); + } + catch (RpcException e) + { + this.logger.LogWarning( + e, + "Failed to send ConnectRequest to newly PLC {0}", + client.RemoteEndPoint); + continue; + } + + if (this.plcDictionary.TryAdd(response.Id, client)) + { + this.logger.LogInformation( + "Client(MAC={0}, EndPoint={1}) connected.", + BitConverter.ToString(response.Id.ToByteArray()), + client.RemoteEndPoint); + client.OnClosed += (sender, args) => + { + this.logger.LogInformation( + "Client(MAC={0}, EndPoint={1}) disconnected.", + BitConverter.ToString(response.Id.ToByteArray()), + client.RemoteEndPoint); + this.plcDictionary.TryRemove(response.Id, out PlcClient _); + }; + } + else + { + this.logger.LogWarning( + "Failed to add the client(MAC={0}, EndPoint={1}) into dictionary.", + BitConverter.ToString(response.Id.ToByteArray()), + client.RemoteEndPoint); + client.Close(); + client.Dispose(); + } + } } } } diff --git a/codelab/ProtocolLab/MainWindow.xaml.cs b/codelab/ProtocolLab/MainWindow.xaml.cs index 473084c1..fab40c9d 100644 --- a/codelab/ProtocolLab/MainWindow.xaml.cs +++ b/codelab/ProtocolLab/MainWindow.xaml.cs @@ -4,7 +4,6 @@ // using System; -using System.Buffers.Binary; using System.Globalization; using System.IO; using System.Linq;