HIR 对接第三方初步测试完毕
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
064e3836ca
commit
aed778bbc6
|
|
@ -27,6 +27,7 @@ using FellowOakDicom.Imaging.Codec;
|
||||||
using FellowOakDicom.IO.Buffer;
|
using FellowOakDicom.IO.Buffer;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using FellowOakDicom.Network.Client;
|
using FellowOakDicom.Network.Client;
|
||||||
|
using MassTransit.Futures.Contracts;
|
||||||
|
|
||||||
namespace IRaCIS.Core.SCP.Service
|
namespace IRaCIS.Core.SCP.Service
|
||||||
{
|
{
|
||||||
|
|
@ -35,14 +36,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
{
|
{
|
||||||
public bool IsSupportThirdService { get; set; }
|
public bool IsSupportThirdService { get; set; }
|
||||||
|
|
||||||
public string ThirdSearchPacsAE { get; set; }
|
public List<ThirdDestinationAE> ThirdDestinationAEList { get; set; }
|
||||||
|
|
||||||
public string ThirdCallningAE { get; set; }
|
|
||||||
|
|
||||||
public string ThirdIP { get; set; }
|
|
||||||
|
|
||||||
public int THirdPort { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public List<string> CalledAEList { get; set; }
|
public List<string> CalledAEList { get; set; }
|
||||||
|
|
@ -50,6 +44,15 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
public string ServerPort { get; set; }
|
public string ServerPort { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ThirdDestinationAE
|
||||||
|
{
|
||||||
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string IP { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -120,9 +123,6 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
|
|
||||||
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
|
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
|
||||||
|
|
||||||
//_serviceProvider = (IServiceProvider)this.UserState;
|
|
||||||
|
|
||||||
//var tt = _injectServiceProvider;
|
|
||||||
|
|
||||||
var option = _serviceProvider.GetService<IOptionsMonitor<DicomSCPServiceOption>>().CurrentValue;
|
var option = _serviceProvider.GetService<IOptionsMonitor<DicomSCPServiceOption>>().CurrentValue;
|
||||||
|
|
||||||
|
|
@ -175,23 +175,27 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
|
|
||||||
public async Task OnReceiveAssociationReleaseRequestAsync()
|
public async Task OnReceiveAssociationReleaseRequestAsync()
|
||||||
{
|
{
|
||||||
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
if (_isCurrentThirdForward == false)
|
||||||
|
|
||||||
|
|
||||||
var @lock = _distributedLockProvider.CreateLock($"{_upload.CallingAE}");
|
|
||||||
|
|
||||||
using (await @lock.AcquireAsync())
|
|
||||||
{
|
{
|
||||||
|
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
||||||
|
|
||||||
await DataMaintenanceAsaync();
|
|
||||||
|
|
||||||
await AddUploadLogAsync();
|
var @lock = _distributedLockProvider.CreateLock($"{_upload.CallingAE}");
|
||||||
|
|
||||||
_releasedNormally = true;
|
using (await @lock.AcquireAsync())
|
||||||
|
{
|
||||||
|
|
||||||
Log.Logger.Information($"进入释放连接请求 {_releasedNormally}");
|
await DataMaintenanceAsaync();
|
||||||
|
|
||||||
|
await AddUploadLogAsync();
|
||||||
|
|
||||||
|
_releasedNormally = true;
|
||||||
|
|
||||||
|
Log.Logger.Information($"进入释放连接请求 {_releasedNormally}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
await SendAssociationReleaseResponseAsync();
|
await SendAssociationReleaseResponseAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -273,26 +277,28 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
|
|
||||||
public async void OnConnectionClosed(Exception exception)
|
public async void OnConnectionClosed(Exception exception)
|
||||||
{
|
{
|
||||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
if (_isCurrentThirdForward == false)
|
||||||
|
|
||||||
|
|
||||||
if (exception == null || _releasedNormally == false)
|
|
||||||
{
|
{
|
||||||
//客户端断网,恢复后,也是没有异常的,估计是超时走了关闭
|
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||||
|
|
||||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = true });
|
|
||||||
//记录日志
|
if (exception == null || _releasedNormally == false)
|
||||||
await AddUploadLogAsync();
|
{
|
||||||
|
//客户端断网,恢复后,也是没有异常的,估计是超时走了关闭
|
||||||
|
|
||||||
|
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = true });
|
||||||
|
//记录日志
|
||||||
|
await AddUploadLogAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//将检查设置为传输结束
|
||||||
|
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = false });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
//将检查设置为传输结束
|
|
||||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = false });
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
|
||||||
|
|
||||||
|
|
||||||
Log.Logger.Warning($"连接关闭 {_releasedNormally} {exception?.Message} {exception?.InnerException?.Message}");
|
Log.Logger.Warning($"连接关闭 {_releasedNormally} {exception?.Message} {exception?.InnerException?.Message}");
|
||||||
}
|
}
|
||||||
|
|
@ -319,46 +325,53 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
|
|
||||||
#region 判断是否转发第三方影像
|
#region 判断是否转发第三方影像
|
||||||
|
|
||||||
var cmoveInfo = _cmoveStudyRepository.Where(t => t.StudyInstanceUIDList.Any(c => c == studyInstanceUid)).OrderByDescending(t => t.CreateTime).FirstOrDefault();
|
if (DicomSCPServiceConfig.IsSupportThirdService)
|
||||||
|
|
||||||
//确定是第三方请求
|
|
||||||
if (cmoveInfo != null && cmoveInfo.CallingAE == DicomSCPServiceConfig.ThirdCallningAE)
|
|
||||||
{
|
{
|
||||||
_isCurrentThirdForward = true;
|
var cmoveInfo = _cmoveStudyRepository.Where(t => t.StudyInstanceUIDList.Any(c => c == studyInstanceUid)).OrderByDescending(t => t.CreateTime).FirstOrDefault();
|
||||||
|
|
||||||
var _dicomAERepository = _serviceProvider.GetService<IRepository<DicomAE>>();
|
//确定是第三方请求
|
||||||
var hirServer = await _dicomAERepository.FirstOrDefaultAsync(t => t.PacsTypeEnum == PacsType.HIRServer);
|
if (cmoveInfo != null && DicomSCPServiceConfig.ThirdDestinationAEList.Any(t => t.Name == cmoveInfo.DestinationAE))
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
// 克隆 Dataset 避免共享引用
|
_isCurrentThirdForward = true;
|
||||||
var dsCopy = request.Dataset?.Clone();
|
|
||||||
|
|
||||||
var forwardRequest = new DicomCStoreRequest(dsCopy);
|
var _dicomAERepository = _serviceProvider.GetService<IRepository<DicomAE>>();
|
||||||
|
var hirServer = await _dicomAERepository.FirstOrDefaultAsync(t => t.PacsTypeEnum == PacsType.HIRServer);
|
||||||
|
|
||||||
|
try
|
||||||
// 创建客户端连接到目标 PACS
|
|
||||||
var client = DicomClientFactory.Create(DicomSCPServiceConfig.ThirdIP, DicomSCPServiceConfig.THirdPort, false, DicomSCPServiceConfig.CalledAEList.First(), cmoveInfo.DestinationAE);
|
|
||||||
|
|
||||||
// 可以加入 OnResponseReceived 来处理目标 PACS 返回状态
|
|
||||||
forwardRequest.OnResponseReceived += (rq, rp) =>
|
|
||||||
{
|
{
|
||||||
Log.Logger.Information($"Forwarded C-STORE Response: {rq.SOPInstanceUID} {rp.Status}");
|
|
||||||
};
|
|
||||||
|
|
||||||
await client.AddRequestAsync(forwardRequest);
|
var findDestination = DicomSCPServiceConfig.ThirdDestinationAEList.FirstOrDefault(t => t.Name == cmoveInfo.DestinationAE);
|
||||||
await client.SendAsync();
|
|
||||||
|
var forwardRequest = new DicomCStoreRequest(request.File.Clone());
|
||||||
|
|
||||||
|
|
||||||
|
// 创建客户端连接到目标 PACS
|
||||||
|
var client = DicomClientFactory.Create(findDestination.IP, findDestination.Port, false, DicomSCPServiceConfig.CalledAEList.First(), cmoveInfo.DestinationAE);
|
||||||
|
|
||||||
|
// 可以加入 OnResponseReceived 来处理目标 PACS 返回状态
|
||||||
|
forwardRequest.OnResponseReceived += (rq, rp) =>
|
||||||
|
{
|
||||||
|
Log.Logger.Information($"Forwarded C-STORE Response: {rq.SOPInstanceUID} {rp.Status}");
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
await client.AddRequestAsync(forwardRequest);
|
||||||
|
await client.SendAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Logger.Error("Error forwarding C-STORE: " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Logger.Error("Error forwarding C-STORE: " + ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return new DicomCStoreResponse(request, DicomStatus.Success);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
//确保来了影像集合存在
|
//确保来了影像集合存在
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,26 @@
|
||||||
},
|
},
|
||||||
"DicomSCPServiceConfig": {
|
"DicomSCPServiceConfig": {
|
||||||
"IsSupportThirdService": true,
|
"IsSupportThirdService": true,
|
||||||
"ThirdSearchPacsAE": "ThirdCalledPacsAE",
|
|
||||||
"ThirdCallningAE": "ThirdCallningAE",
|
"ThirdDestinationAEList": [
|
||||||
"ThirdIP": "192.168.3.15",
|
{
|
||||||
"THirdPort": 112,
|
"Name": "LYAE",
|
||||||
|
"IP": "192.168.3.194",
|
||||||
|
"Port": "6000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "HIRLAE",
|
||||||
|
"IP": "106.14.89.110",
|
||||||
|
"Port": "6000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"CalledAEList": [
|
"CalledAEList": [
|
||||||
"HIRAE"
|
"HIRAE",
|
||||||
|
"STORESCP"
|
||||||
],
|
],
|
||||||
"ServerPort": 11112
|
"ServerPort": 11115
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -199,23 +199,26 @@ namespace IRaCIS.Core.API.HostService
|
||||||
|
|
||||||
|
|
||||||
// 异步发送到真实 PACS
|
// 异步发送到真实 PACS
|
||||||
_ = Task.Run(async () =>
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
try
|
var client = DicomClientFactory.Create(find.IP, find.Port, false, hirClient.CalledAE, find.CalledAE);
|
||||||
{
|
await client.AddRequestAsync(forward);
|
||||||
var client = DicomClientFactory.Create(find.IP, find.Port, false, hirClient.CalledAE, find.CalledAE);
|
await client.SendAsync();
|
||||||
await client.AddRequestAsync(forward);
|
}
|
||||||
await client.SendAsync();
|
catch (Exception ex)
|
||||||
}
|
{
|
||||||
catch (Exception ex)
|
Console.WriteLine("Error forwarding C-FIND: " + ex.Message);
|
||||||
{
|
}
|
||||||
Console.WriteLine("Error forwarding C-FIND: " + ex.Message);
|
finally
|
||||||
}
|
{
|
||||||
finally
|
channel.Writer.Complete();
|
||||||
{
|
}
|
||||||
channel.Writer.Complete();
|
|
||||||
}
|
//_ = Task.Run(async () =>
|
||||||
});
|
//{
|
||||||
|
|
||||||
|
//});
|
||||||
|
|
||||||
// 异步 yield 返回给上游
|
// 异步 yield 返回给上游
|
||||||
await foreach (var resp in channel.Reader.ReadAllAsync())
|
await foreach (var resp in channel.Reader.ReadAllAsync())
|
||||||
|
|
@ -263,6 +266,7 @@ namespace IRaCIS.Core.API.HostService
|
||||||
|
|
||||||
var channel = Channel.CreateUnbounded<DicomCMoveResponse>();
|
var channel = Channel.CreateUnbounded<DicomCMoveResponse>();
|
||||||
var clonedDataset = request.Dataset?.Clone() ?? new DicomDataset();
|
var clonedDataset = request.Dataset?.Clone() ?? new DicomDataset();
|
||||||
|
|
||||||
var forward = new DicomCMoveRequest(hirServer.CalledAE, studyInstanceUid)
|
var forward = new DicomCMoveRequest(hirServer.CalledAE, studyInstanceUid)
|
||||||
{
|
{
|
||||||
Dataset = clonedDataset
|
Dataset = clonedDataset
|
||||||
|
|
@ -281,6 +285,9 @@ namespace IRaCIS.Core.API.HostService
|
||||||
Completed = rp.Completed,
|
Completed = rp.Completed,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Logger.LogInformation($"Completed:{rp.Completed}");
|
||||||
|
|
||||||
channel.Writer.TryWrite(proxyResp);
|
channel.Writer.TryWrite(proxyResp);
|
||||||
|
|
||||||
if (!rp.Status.Equals(DicomStatus.Pending))
|
if (!rp.Status.Equals(DicomStatus.Pending))
|
||||||
|
|
@ -290,23 +297,25 @@ namespace IRaCIS.Core.API.HostService
|
||||||
};
|
};
|
||||||
|
|
||||||
// 异步发送到真实 PACS
|
// 异步发送到真实 PACS
|
||||||
_ = Task.Run(async () =>
|
try
|
||||||
{
|
{
|
||||||
try
|
var client = DicomClientFactory.Create(find.IP, find.Port, false, hirClient.CalledAE, find.CalledAE);
|
||||||
{
|
await client.AddRequestAsync(forward);
|
||||||
var client = DicomClientFactory.Create(find.IP, find.Port, false, hirClient.CalledAE, find.CalledAE);
|
await client.SendAsync();
|
||||||
await client.AddRequestAsync(forward);
|
}
|
||||||
await client.SendAsync();
|
catch (Exception ex)
|
||||||
}
|
{
|
||||||
catch (Exception ex)
|
Console.WriteLine("Error forwarding C-MOVE: " + ex.Message);
|
||||||
{
|
}
|
||||||
Console.WriteLine("Error forwarding C-MOVE: " + ex.Message);
|
finally
|
||||||
}
|
{
|
||||||
finally
|
channel.Writer.Complete();
|
||||||
{
|
}
|
||||||
channel.Writer.Complete();
|
|
||||||
}
|
//_ = Task.Run(async () =>
|
||||||
});
|
//{
|
||||||
|
|
||||||
|
//});
|
||||||
|
|
||||||
// 异步 yield 回上游
|
// 异步 yield 回上游
|
||||||
await foreach (var resp in channel.Reader.ReadAllAsync())
|
await foreach (var resp in channel.Reader.ReadAllAsync())
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,8 @@ builder.Services.AddControllers(options =>
|
||||||
})
|
})
|
||||||
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
||||||
|
|
||||||
|
builder.Services.AddOptions().Configure<DicomSCPServiceOption>(_configuration.GetSection("DicomSCPServiceConfig"));
|
||||||
|
|
||||||
// Panda动态WebApi + UnifiedApiResultFilter + 省掉控制器代码
|
// Panda动态WebApi + UnifiedApiResultFilter + 省掉控制器代码
|
||||||
builder.Services.AddDynamicWebApiSetup();
|
builder.Services.AddDynamicWebApiSetup();
|
||||||
//MinimalAPI
|
//MinimalAPI
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,8 @@
|
||||||
},
|
},
|
||||||
"DicomSCPServiceConfig": {
|
"DicomSCPServiceConfig": {
|
||||||
"IsSupportThirdService": true,
|
"IsSupportThirdService": true,
|
||||||
"ThirdSearchPacsAE": "ThirdCalledPacsAE",
|
"ThirdSearchPacsAE": "XCPACS",
|
||||||
"ThirdCallningAE": "ThirdCallningAE",
|
"ThirdCallningAE": "LYAE",
|
||||||
"CalledAEList": [
|
"CalledAEList": [
|
||||||
"HIRSCUAE",
|
"HIRSCUAE",
|
||||||
"HIRSCPAE"
|
"HIRSCPAE"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue