拉取影像
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
959fa1c40c
commit
39e7218b52
|
@ -18251,5 +18251,22 @@
|
|||
<param name="inQuery"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Application.Services.PatientService.GetCFindStudyList(IRaCIS.Application.Contracts.SCUQuery,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomAE},Microsoft.Extensions.Logging.ILogger{IRaCIS.Application.Services.PatientService})">
|
||||
<summary>
|
||||
获取 检查列表
|
||||
</summary>
|
||||
<param name="inQuery"></param>
|
||||
<param name="_dicomAEReposiotry"></param>
|
||||
<param name="_logger"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Application.Services.PatientService.CmoveStudyList(IRaCIS.Application.Contracts.SCUCmoveCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomAE})">
|
||||
<summary>
|
||||
拉取影像
|
||||
</summary>
|
||||
<param name="inCommand"></param>
|
||||
<param name="_dicomAEReposiotry"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
|
|
|
@ -48,6 +48,8 @@ namespace IRaCIS.Core.Application.ViewModel
|
|||
public int Port { get; set; }
|
||||
public string Modality { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
public PacsType PacsTypeEnum { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Application.Contracts
|
||||
{
|
||||
|
||||
public class SCUBasicInfo
|
||||
{
|
||||
public string? PatientID { get; set; }
|
||||
|
||||
public string? PatientName { get; set; }
|
||||
|
||||
public string? PatientSex { get; set; }
|
||||
|
||||
public string PatientBirthDate { get; set; }
|
||||
|
||||
public string? StudyID { get; set; }
|
||||
|
||||
public string? ModalitiesInStudy { get; set; }
|
||||
|
||||
|
||||
|
||||
public string? StudyDate { get; set; }
|
||||
|
||||
public string? StudyTime { get; set; }
|
||||
|
||||
public string? BodyPartExamined { get; set; }
|
||||
|
||||
public string? StudyDescription { get; set; }
|
||||
|
||||
public string? StudyInstanceUID { get; set; }
|
||||
}
|
||||
public class SCUQuery : SCUBasicInfo
|
||||
{
|
||||
[NotDefault]
|
||||
public Guid PacsDicomAEId { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class SCUCmoveCommand
|
||||
{
|
||||
[NotDefault]
|
||||
public Guid PacsDicomAEId { get; set; }
|
||||
|
||||
public List<string> StudyIDList { get; set; }
|
||||
}
|
||||
|
||||
public class SCUStudyView : SCUBasicInfo
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -42,6 +42,10 @@ using IDistributedLockProvider = Medallion.Threading.IDistributedLockProvider;
|
|||
using DocumentFormat.OpenXml.InkML;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using IRaCIS.Core.Infrastructure.Encryption;
|
||||
using FellowOakDicom.Network.Client;
|
||||
using FellowOakDicom.Network;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
|
||||
namespace IRaCIS.Application.Services
|
||||
{
|
||||
|
@ -2694,6 +2698,8 @@ namespace IRaCIS.Application.Services
|
|||
#endregion
|
||||
|
||||
|
||||
#region 影像下载
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取下载的访视检查信息
|
||||
|
@ -2791,6 +2797,8 @@ namespace IRaCIS.Application.Services
|
|||
return ResponseOutput.Ok(pageList);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// scp 影像推送记录表
|
||||
|
@ -2817,7 +2825,154 @@ namespace IRaCIS.Application.Services
|
|||
}
|
||||
|
||||
|
||||
#region 作为SCU C-find c-move 操作
|
||||
|
||||
#region 基础方法
|
||||
|
||||
public static DicomCFindRequest CreateStudyRequest(SCUQuery inQuery)
|
||||
{
|
||||
|
||||
var request = new DicomCFindRequest(DicomQueryRetrieveLevel.Study);
|
||||
|
||||
// always add the encoding
|
||||
request.Dataset.AddOrUpdate(new DicomTag(0x8, 0x5), "ISO_IR 100");
|
||||
|
||||
// add the dicom tags with empty values that should be included in the result of the QR Server
|
||||
//病例号
|
||||
request.Dataset.AddOrUpdate(DicomTag.PatientID, inQuery.PatientID.IsNullOrEmpty() ? "" : inQuery.PatientID);
|
||||
|
||||
request.Dataset.AddOrUpdate(DicomTag.PatientName, inQuery.PatientName.IsNullOrEmpty() ? "" : inQuery.PatientName);
|
||||
request.Dataset.AddOrUpdate(DicomTag.PatientSex, inQuery.PatientSex.IsNullOrEmpty() ? "" : inQuery.PatientSex);
|
||||
request.Dataset.AddOrUpdate(DicomTag.PatientBirthDate, inQuery.PatientBirthDate.IsNullOrEmpty() ? "" : inQuery.PatientBirthDate);
|
||||
//检查号 设备类型
|
||||
request.Dataset.AddOrUpdate(DicomTag.StudyID, inQuery.StudyID.IsNullOrEmpty() ? "" : inQuery.StudyID);
|
||||
|
||||
request.Dataset.AddOrUpdate(DicomTag.ModalitiesInStudy, inQuery.ModalitiesInStudy.IsNullOrEmpty() ? "" : inQuery.ModalitiesInStudy);
|
||||
|
||||
request.Dataset.AddOrUpdate(DicomTag.StudyDate, inQuery.StudyDate.IsNullOrEmpty() ? "" : inQuery.StudyDate);
|
||||
request.Dataset.AddOrUpdate(DicomTag.StudyTime, inQuery.StudyTime.IsNullOrEmpty() ? "" : inQuery.StudyTime);
|
||||
|
||||
request.Dataset.AddOrUpdate(DicomTag.BodyPartExamined, inQuery.BodyPartExamined.IsNullOrEmpty() ? "" : inQuery.BodyPartExamined);
|
||||
request.Dataset.AddOrUpdate(DicomTag.StudyDescription, inQuery.StudyDescription.IsNullOrEmpty() ? "" : inQuery.StudyDescription);
|
||||
|
||||
request.Dataset.AddOrUpdate(DicomTag.StudyInstanceUID, inQuery.StudyInstanceUID.IsNullOrEmpty() ? "" : inQuery.StudyInstanceUID);
|
||||
|
||||
// add the dicom tags that contain the filter criterias
|
||||
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
public static void DebugStudyResponse(DicomCFindResponse response, ILogger _logger)
|
||||
{
|
||||
if (response.Status == DicomStatus.Pending)
|
||||
{
|
||||
|
||||
// print the results
|
||||
_logger.LogDebug($"Patient {response.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty)}, {(response.Dataset.TryGetString(DicomTag.ModalitiesInStudy, out var dummy) ? dummy : string.Empty)}-Study from {response.Dataset.GetSingleValueOrDefault(DicomTag.StudyDate, new DateTime())} with UID {response.Dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty)} ");
|
||||
}
|
||||
if (response.Status == DicomStatus.Success)
|
||||
{
|
||||
Console.WriteLine(response.Status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 获取 检查列表
|
||||
/// </summary>
|
||||
/// <param name="inQuery"></param>
|
||||
/// <param name="_dicomAEReposiotry"></param>
|
||||
/// <param name="_logger"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<List<SCUStudyView>> GetCFindStudyList(SCUQuery inQuery,
|
||||
[FromServices] IRepository<DicomAE> _dicomAEReposiotry,
|
||||
[FromServices] ILogger<PatientService> _logger)
|
||||
{
|
||||
|
||||
var result = new List<SCUStudyView>();
|
||||
|
||||
var find = await _dicomAEReposiotry.FirstOrDefaultAsync(t => t.Id == inQuery.PacsDicomAEId);
|
||||
|
||||
if (find != null)
|
||||
{
|
||||
|
||||
var client = DicomClientFactory.Create(find.IP, find.Port, false, "HIRSCUAE", find.CalledAE);
|
||||
|
||||
client.NegotiateAsyncOps();
|
||||
|
||||
var request = CreateStudyRequest(inQuery);
|
||||
|
||||
request.OnResponseReceived += (req, response) =>
|
||||
{
|
||||
//DebugStudyResponse(response, _logger);
|
||||
|
||||
result.Add(new SCUStudyView()
|
||||
{
|
||||
PatientID = response.Dataset?.GetSingleValue<string>(DicomTag.PatientID),
|
||||
PatientName = response.Dataset?.GetSingleValue<string>(DicomTag.PatientName),
|
||||
PatientSex = response.Dataset?.GetSingleValue<string>(DicomTag.PatientSex),
|
||||
PatientBirthDate = response.Dataset?.GetSingleValue<string>(DicomTag.PatientBirthDate),
|
||||
StudyID = response.Dataset?.GetSingleValue<string>(DicomTag.StudyID),
|
||||
ModalitiesInStudy = response.Dataset?.GetSingleValue<string>(DicomTag.ModalitiesInStudy),
|
||||
StudyDate = response.Dataset?.GetSingleValue<string>(DicomTag.StudyDate),
|
||||
StudyTime = response.Dataset?.GetSingleValue<string>(DicomTag.StudyTime),
|
||||
BodyPartExamined = response.Dataset?.GetSingleValue<string>(DicomTag.BodyPartExamined),
|
||||
StudyDescription = response.Dataset?.GetSingleValue<string>(DicomTag.StudyDescription),
|
||||
StudyInstanceUID = response.Dataset?.GetSingleValue<string>(DicomTag.StudyInstanceUID)
|
||||
});
|
||||
|
||||
};
|
||||
await client.AddRequestAsync(request);
|
||||
await client.SendAsync();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 拉取影像
|
||||
/// </summary>
|
||||
/// <param name="inCommand"></param>
|
||||
/// <param name="_dicomAEReposiotry"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IResponseOutput> CmoveStudyList(SCUCmoveCommand inCommand, [FromServices] IRepository<DicomAE> _dicomAEReposiotry)
|
||||
{
|
||||
var result = new List<SCUStudyView>();
|
||||
|
||||
var find = await _dicomAEReposiotry.FirstOrDefaultAsync(t => t.Id == inCommand.PacsDicomAEId);
|
||||
|
||||
var hirServer = await _dicomAEReposiotry.FirstOrDefaultAsync(t => t.PacsTypeEnum == PacsType.HIRServer);
|
||||
|
||||
if (hirServer == null)
|
||||
{
|
||||
return ResponseOutput.NotOk("未找到服务端AE配置,请核查后再拉取");
|
||||
}
|
||||
|
||||
if (find != null)
|
||||
{
|
||||
|
||||
var client = DicomClientFactory.Create(find.IP, find.Port, false, "HIRSCUAE", find.CalledAE);
|
||||
client.NegotiateAsyncOps();
|
||||
|
||||
var cmoveRequestList = new List<DicomCMoveRequest>();
|
||||
|
||||
foreach (var item in inCommand.StudyIDList)
|
||||
{
|
||||
cmoveRequestList.Add(new DicomCMoveRequest(hirServer.CalledAE, item));
|
||||
}
|
||||
|
||||
await client.AddRequestsAsync(cmoveRequestList);
|
||||
await client.SendAsync();
|
||||
}
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,18 @@ namespace IRaCIS.Core.Domain.Models
|
|||
|
||||
public bool IsTestOK { get; set; }
|
||||
|
||||
}
|
||||
public PacsType PacsTypeEnum { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
public enum PacsType
|
||||
{
|
||||
HIRServer = 0,
|
||||
|
||||
//暂时不需要
|
||||
HIRClient = 1,
|
||||
|
||||
PacsServer = 2,
|
||||
}
|
||||
}
|
||||
|
|
18651
IRaCIS.Core.Infra.EFCore/Migrations/20241212081432_DicomAEAddField.Designer.cs
generated
Normal file
18651
IRaCIS.Core.Infra.EFCore/Migrations/20241212081432_DicomAEAddField.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,51 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace IRaCIS.Core.Infra.EFCore.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class DicomAEAddField : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "PatientId",
|
||||
table: "DicomStudy",
|
||||
type: "nvarchar(400)",
|
||||
maxLength: 400,
|
||||
nullable: false,
|
||||
comment: "废弃了,用PatientIdStr",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(400)",
|
||||
oldMaxLength: 400);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "PacsTypeEnum",
|
||||
table: "DicomAE",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PacsTypeEnum",
|
||||
table: "DicomAE");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "PatientId",
|
||||
table: "DicomStudy",
|
||||
type: "nvarchar(400)",
|
||||
maxLength: 400,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(400)",
|
||||
oldMaxLength: 400,
|
||||
oldComment: "废弃了,用PatientIdStr");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -812,6 +812,9 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
|
|||
.HasMaxLength(400)
|
||||
.HasColumnType("nvarchar(400)");
|
||||
|
||||
b.Property<int>("PacsTypeEnum")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Port")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -1207,7 +1210,8 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
|
|||
b.Property<string>("PatientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(400)
|
||||
.HasColumnType("nvarchar(400)");
|
||||
.HasColumnType("nvarchar(400)")
|
||||
.HasComment("废弃了,用PatientIdStr");
|
||||
|
||||
b.Property<string>("PatientIdStr")
|
||||
.IsRequired()
|
||||
|
|
Loading…
Reference in New Issue