|
1 |
| -/* |
2 |
| - * Copyright 2022-2023 MONAI Consortium |
3 |
| - * |
4 |
| - * Licensed under the Apache License, Version 2.0 (the "License"); |
5 |
| - * you may not use this file except in compliance with the License. |
6 |
| - * You may obtain a copy of the License at |
7 |
| - * |
8 |
| - * http://www.apache.org/licenses/LICENSE-2.0 |
9 |
| - * |
10 |
| - * Unless required by applicable law or agreed to in writing, software |
11 |
| - * distributed under the License is distributed on an "AS IS" BASIS, |
12 |
| - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 |
| - * See the License for the specific language governing permissions and |
14 |
| - * limitations under the License. |
15 |
| - */ |
16 |
| - |
17 |
| -using System; |
18 |
| -using System.Collections.Concurrent; |
19 |
| -using System.Collections.Generic; |
20 |
| -using System.IO.Abstractions; |
21 |
| -using System.Linq; |
| 1 | +using System; |
22 | 2 | using System.Net;
|
23 | 3 | using System.Net.Sockets;
|
24 | 4 | using System.Text;
|
25 | 5 | using System.Threading;
|
26 | 6 | using System.Threading.Tasks;
|
27 |
| -using Ardalis.GuardClauses; |
28 | 7 | using HL7.Dotnetcore;
|
29 |
| -using Microsoft.Extensions.DependencyInjection; |
30 |
| -using Microsoft.Extensions.Hosting; |
31 | 8 | using Microsoft.Extensions.Logging;
|
32 | 9 | using Microsoft.Extensions.Options;
|
33 |
| -using Monai.Deploy.InformaticsGateway.Api.PlugIns; |
34 |
| -using Monai.Deploy.InformaticsGateway.Api.Rest; |
35 |
| -using Monai.Deploy.InformaticsGateway.Api.Storage; |
36 |
| -using Monai.Deploy.InformaticsGateway.Common; |
| 10 | +using Monai.Deploy.InformaticsGateway.Api.Mllp; |
37 | 11 | using Monai.Deploy.InformaticsGateway.Configuration;
|
38 |
| -using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; |
39 | 12 | using Monai.Deploy.InformaticsGateway.Logging;
|
40 |
| -using Monai.Deploy.InformaticsGateway.Services.Common; |
41 |
| -using Monai.Deploy.InformaticsGateway.Services.Connectors; |
42 |
| -using Monai.Deploy.InformaticsGateway.Services.HealthLevel7; |
43 |
| -using Monai.Deploy.InformaticsGateway.Services.Storage; |
44 |
| -using Monai.Deploy.Messaging.Events; |
45 | 13 |
|
46 |
| -namespace Monai.Deploy.InformaticsGateway.Api.Mllp |
| 14 | +namespace Monai.Deploy.InformaticsGateway.Services.HealthLevel7 |
47 | 15 | {
|
48 |
| - internal sealed class MllpService : IMllpService, IHostedService, IDisposable, IMonaiService |
| 16 | + internal class MllpService : IMllpService |
49 | 17 | {
|
50 |
| - private const int SOCKET_OPERATION_CANCELLED = 125; |
51 |
| - private bool _disposedValue; |
52 |
| - private readonly ITcpListener _tcpListener; |
53 |
| - private readonly IMllpClientFactory _mllpClientFactory; |
54 |
| - private readonly IObjectUploadQueue _uploadQueue; |
55 |
| - private readonly IPayloadAssembler _payloadAssembler; |
56 |
| - private readonly IFileSystem _fileSystem; |
57 |
| - private readonly ILoggerFactory _logginFactory; |
58 |
| - private readonly ILogger<MllpService> _logger; |
59 |
| - private readonly IOptions<InformaticsGatewayConfiguration> _configuration; |
60 |
| - private readonly IStorageInfoProvider _storageInfoProvider; |
61 |
| - private readonly ConcurrentDictionary<Guid, IMllpClient> _activeTasks; |
62 |
| - private readonly IMllpExtract _mIIpExtract; |
63 |
| - private readonly IInputHL7DataPlugInEngine _inputHL7DataPlugInEngine; |
64 |
| - private readonly IHl7ApplicationConfigRepository _hl7ApplicationConfigRepository; |
65 |
| - private DateTime _lastConfigRead = new(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc); |
66 |
| - |
67 |
| - public int ActiveConnections |
68 |
| - { |
69 |
| - get |
70 |
| - { |
71 |
| - return _activeTasks.Count; |
72 |
| - } |
73 |
| - } |
74 |
| - |
75 |
| - public ServiceStatus Status { get; set; } = ServiceStatus.Unknown; |
76 |
| - |
77 |
| - public string ServiceName => "HL7 Service"; |
78 |
| - |
79 |
| - public MllpService(IServiceScopeFactory serviceScopeFactory, IOptions<InformaticsGatewayConfiguration> configuration) |
80 |
| - { |
81 |
| - ArgumentNullException.ThrowIfNull(serviceScopeFactory, nameof(serviceScopeFactory)); |
82 |
| - |
83 |
| - _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); |
84 |
| - |
85 |
| - var serviceScope = serviceScopeFactory.CreateScope(); |
86 |
| - _logginFactory = serviceScope.ServiceProvider.GetService<ILoggerFactory>() ?? throw new ServiceNotFoundException(nameof(ILoggerFactory)); |
87 |
| - _logger = _logginFactory.CreateLogger<MllpService>(); |
88 |
| - var tcpListenerFactory = serviceScope.ServiceProvider.GetService<ITcpListenerFactory>() ?? throw new ServiceNotFoundException(nameof(ITcpListenerFactory)); |
89 |
| - _tcpListener = tcpListenerFactory.CreateTcpListener(System.Net.IPAddress.Any, _configuration.Value.Hl7.Port); |
90 |
| - _mllpClientFactory = serviceScope.ServiceProvider.GetService<IMllpClientFactory>() ?? throw new ServiceNotFoundException(nameof(IMllpClientFactory)); |
91 |
| - _uploadQueue = serviceScope.ServiceProvider.GetService<IObjectUploadQueue>() ?? throw new ServiceNotFoundException(nameof(IObjectUploadQueue)); |
92 |
| - _payloadAssembler = serviceScope.ServiceProvider.GetService<IPayloadAssembler>() ?? throw new ServiceNotFoundException(nameof(IPayloadAssembler)); |
93 |
| - _fileSystem = serviceScope.ServiceProvider.GetService<IFileSystem>() ?? throw new ServiceNotFoundException(nameof(IFileSystem)); |
94 |
| - _storageInfoProvider = serviceScope.ServiceProvider.GetService<IStorageInfoProvider>() ?? throw new ServiceNotFoundException(nameof(IStorageInfoProvider)); |
95 |
| - _mIIpExtract = serviceScope.ServiceProvider.GetService<IMllpExtract>() ?? throw new ServiceNotFoundException(nameof(IMllpExtract)); |
96 |
| - _activeTasks = new ConcurrentDictionary<Guid, IMllpClient>(); |
97 |
| - _inputHL7DataPlugInEngine = serviceScope.ServiceProvider.GetService<IInputHL7DataPlugInEngine>() ?? throw new ServiceNotFoundException(nameof(IInputHL7DataPlugInEngine)); |
98 |
| - _hl7ApplicationConfigRepository = serviceScope.ServiceProvider.GetService<IHl7ApplicationConfigRepository>() ?? throw new ServiceNotFoundException(nameof(IHl7ApplicationConfigRepository)); |
99 |
| - } |
100 |
| - |
101 |
| - public Task StartAsync(CancellationToken cancellationToken) |
102 |
| - { |
103 |
| - var task = Task.Run(async () => |
104 |
| - { |
105 |
| - _tcpListener.Start(); |
106 |
| - await BackgroundProcessing(cancellationToken).ConfigureAwait(true); |
107 |
| - }, CancellationToken.None); |
108 |
| - |
109 |
| - Status = ServiceStatus.Running; |
110 |
| - _logger.ServiceRunning(ServiceName); |
111 |
| - _logger.Hl7ListeningOnPort(_configuration.Value.Hl7.Port); |
112 |
| - |
113 |
| - if (task.IsCompleted) |
114 |
| - return task; |
115 |
| - return Task.CompletedTask; |
116 |
| - } |
117 |
| - |
118 |
| - public Task StopAsync(CancellationToken cancellationToken) |
119 |
| - { |
120 |
| - _logger.ServiceStopping(ServiceName); |
121 |
| - _tcpListener.Stop(); |
122 |
| - Status = ServiceStatus.Stopped; |
123 |
| - return Task.CompletedTask; |
124 |
| - } |
125 |
| - |
126 |
| - private async Task BackgroundProcessing(CancellationToken cancellationToken) |
127 |
| - { |
128 |
| - while (!cancellationToken.IsCancellationRequested) |
129 |
| - { |
130 |
| - IMllpClient? mllpClient = null; |
131 |
| - try |
132 |
| - { |
133 |
| - WaitUntilAvailable(_configuration.Value.Hl7.MaximumNumberOfConnections); |
134 |
| - var client = await _tcpListener.AcceptTcpClientAsync(cancellationToken).ConfigureAwait(false); |
135 |
| - _logger.ClientConnected(); |
136 |
| - |
137 |
| - if (!_storageInfoProvider.HasSpaceAvailableToStore) |
138 |
| - { |
139 |
| - _logger.Hl7DisconnectedDueToLowStorageSpace(_storageInfoProvider.AvailableFreeSpace); |
140 |
| - client.Close(); |
141 |
| - await Task.Delay(5000, cancellationToken).ConfigureAwait(false); |
142 |
| - continue; |
143 |
| - } |
144 |
| - |
145 |
| - mllpClient = _mllpClientFactory.CreateClient(client, _configuration.Value.Hl7, _logginFactory.CreateLogger<MllpClient>()); |
146 |
| - _ = mllpClient.Start(OnDisconnect, cancellationToken); |
147 |
| - _activeTasks.TryAdd(mllpClient.ClientId, mllpClient); |
148 |
| - } |
149 |
| - catch (System.Net.Sockets.SocketException ex) |
150 |
| - { |
151 |
| - _logger.Hl7SocketException(ex.Message); |
152 |
| - |
153 |
| - if (mllpClient is not null) |
154 |
| - { |
155 |
| - mllpClient.Dispose(); |
156 |
| - _activeTasks.Remove(mllpClient.ClientId, out _); |
157 |
| - } |
158 |
| - |
159 |
| - if (ex.ErrorCode == SOCKET_OPERATION_CANCELLED) |
160 |
| - { |
161 |
| - break; |
162 |
| - } |
163 |
| - } |
164 |
| - catch (Exception ex) |
165 |
| - { |
166 |
| - _logger.ServiceInvalidOrCancelled(ServiceName, ex); |
167 |
| - } |
168 |
| - } |
169 |
| - Status = ServiceStatus.Cancelled; |
170 |
| - _logger.ServiceCancelled(ServiceName); |
171 |
| - } |
172 |
| - |
173 |
| - private async Task OnDisconnect(IMllpClient client, MllpClientResult result) |
174 |
| - { |
175 |
| - Guard.Against.Null(client, nameof(client)); |
176 |
| - Guard.Against.Null(result, nameof(result)); |
177 |
| - |
178 |
| - await ConfigurePlugInEngine().ConfigureAwait(false); |
179 |
| - |
180 |
| - try |
181 |
| - { |
182 |
| - foreach (var message in result.Messages) |
183 |
| - { |
184 |
| - var newMessage = message; |
185 |
| - var hl7Filemetadata = new Hl7FileStorageMetadata(client.ClientId.ToString(), DataService.HL7, client.ClientIp); |
186 |
| - var configItem = await _mIIpExtract.GetConfigItem(message).ConfigureAwait(false); |
187 |
| - if (configItem is not null) |
188 |
| - { |
189 |
| - await _inputHL7DataPlugInEngine.ExecutePlugInsAsync(message, hl7Filemetadata, configItem).ConfigureAwait(false); |
190 |
| - newMessage = await _mIIpExtract.ExtractInfo(hl7Filemetadata, message, configItem).ConfigureAwait(false); |
191 |
| - |
192 |
| - _logger.HL7MessageAfterPluginProcessing(newMessage.HL7Message, hl7Filemetadata.CorrelationId); |
193 |
| - } |
194 |
| - _logger.Hl7MessageReceieved(newMessage.HL7Message); |
195 |
| - await hl7Filemetadata.SetDataStream(newMessage.HL7Message, _configuration.Value.Storage.TemporaryDataStorage, _fileSystem, _configuration.Value.Storage.LocalTemporaryStoragePath).ConfigureAwait(false); |
196 |
| - var payloadId = await _payloadAssembler.Queue(client.ClientId.ToString(), hl7Filemetadata, new DataOrigin { DataService = DataService.HL7, Source = client.ClientIp, Destination = FileStorageMetadata.IpAddress() }).ConfigureAwait(false); |
197 |
| - hl7Filemetadata.PayloadId ??= payloadId.ToString(); |
198 |
| - _uploadQueue.Queue(hl7Filemetadata); |
199 |
| - } |
200 |
| - } |
201 |
| - catch (Exception ex) |
202 |
| - { |
203 |
| - _logger.ErrorHandlingHl7Results(ex); |
204 |
| - } |
205 |
| - finally |
206 |
| - { |
207 |
| - _activeTasks.Remove(client.ClientId, out _); |
208 |
| - _logger.Hl7ClientRemoved(client.ClientId); |
209 |
| - client.Dispose(); |
210 |
| - } |
211 |
| - } |
212 | 18 |
|
213 |
| - private async Task ConfigurePlugInEngine() |
214 |
| - { |
215 |
| - var configs = await _hl7ApplicationConfigRepository.GetAllAsync().ConfigureAwait(false); |
216 |
| - if (configs is not null && configs.Count is not 0 && configs.Max(c => c.LastModified) > _lastConfigRead) |
217 |
| - { |
218 |
| - var pluginAssemblies = new List<string>(); |
219 |
| - foreach (var config in configs.Where(p => p.PlugInAssemblies?.Count > 0)) |
220 |
| - { |
221 |
| - try |
222 |
| - { |
223 |
| - pluginAssemblies.AddRange(config.PlugInAssemblies.Where(p => string.IsNullOrWhiteSpace(p) is false && pluginAssemblies.Contains(p) is false)); |
224 |
| - } |
225 |
| - catch (Exception ex) |
226 |
| - { |
227 |
| - _logger.HL7PluginLoadingExceptions(ex); |
228 |
| - } |
229 |
| - } |
230 |
| - if (pluginAssemblies.Count is not 0) |
231 |
| - { |
232 |
| - _inputHL7DataPlugInEngine.Configure(pluginAssemblies); |
233 |
| - } |
234 |
| - } |
235 |
| - _lastConfigRead = DateTime.UtcNow; |
236 |
| - } |
237 |
| - |
238 |
| - private void WaitUntilAvailable(int maximumNumberOfConnections) |
239 |
| - { |
240 |
| - var count = 0; |
241 |
| - while (ActiveConnections >= maximumNumberOfConnections) |
242 |
| - { |
243 |
| - if (++count % 25 == 1) |
244 |
| - { |
245 |
| - _logger.MaxedOutHl7Connections(maximumNumberOfConnections); |
246 |
| - } |
247 |
| - Thread.Sleep(100); |
248 |
| - } |
249 |
| - } |
| 19 | + private readonly ILogger<MllpService> _logger; |
| 20 | + private readonly InformaticsGatewayConfiguration _configuration; |
250 | 21 |
|
251 |
| - private void Dispose(bool disposing) |
| 22 | + public MllpService(ILogger<MllpService> logger, IOptions<InformaticsGatewayConfiguration> configuration) |
252 | 23 | {
|
253 |
| - if (!_disposedValue) |
254 |
| - { |
255 |
| - if (disposing) |
256 |
| - { |
257 |
| - foreach (var client in _activeTasks.Values) |
258 |
| - { |
259 |
| - client.Dispose(); |
260 |
| - } |
261 |
| - } |
262 |
| - |
263 |
| - _disposedValue = true; |
264 |
| - } |
| 24 | + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); |
| 25 | + _configuration = configuration?.Value ?? throw new ArgumentNullException(nameof(configuration)); ; |
265 | 26 | }
|
266 | 27 |
|
267 | 28 | public async Task SendMllp(IPAddress address, int port, string hl7Message, CancellationToken cancellationToken)
|
@@ -312,7 +73,7 @@ private async Task WriteMessage(byte[] sendMessageByteBuffer, IPAddress address,
|
312 | 73 | private async Task EnsureAck(NetworkStream networkStream)
|
313 | 74 | {
|
314 | 75 | using var s_cts = new CancellationTokenSource();
|
315 |
| - s_cts.CancelAfter(_configuration.Value.Hl7.ClientTimeoutMilliseconds); |
| 76 | + s_cts.CancelAfter(_configuration.Hl7.ClientTimeoutMilliseconds); |
316 | 77 | var buffer = new byte[2048];
|
317 | 78 |
|
318 | 79 | // get the SentHl7Message
|
@@ -344,12 +105,5 @@ private async Task EnsureAck(NetworkStream networkStream)
|
344 | 105 | }
|
345 | 106 | throw new Hl7SendException("ACK message contains no ACK!");
|
346 | 107 | }
|
347 |
| - |
348 |
| - public void Dispose() |
349 |
| - { |
350 |
| - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method |
351 |
| - Dispose(disposing: true); |
352 |
| - GC.SuppressFinalize(this); |
353 |
| - } |
354 | 108 | }
|
355 | 109 | }
|
0 commit comments