357 lines
18 KiB
C#
357 lines
18 KiB
C#
using IRaCIS.Core.Domain.Share;
|
|
using System.Text;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using IRaCIS.Core.Infrastructure;
|
|
using Medallion.Threading;
|
|
using FellowOakDicom;
|
|
using FellowOakDicom.Imaging.Codec;
|
|
using System.Data;
|
|
using IRaCIS.Core.Domain.Models;
|
|
using FellowOakDicom.Network;
|
|
using IRaCIS.Core.SCP.Service;
|
|
using IRaCIS.Core.Infra.EFCore;
|
|
using MassTransit;
|
|
using System.Runtime.Intrinsics.X86;
|
|
using Serilog.Sinks.File;
|
|
|
|
namespace IRaCIS.Core.SCP.Service
|
|
{
|
|
public class DicomArchiveService : BaseService, IDicomArchiveService
|
|
{
|
|
private readonly IRepository<SCPPatient> _patientRepository;
|
|
private readonly IRepository<SCPStudy> _studyRepository;
|
|
private readonly IRepository<SCPSeries> _seriesRepository;
|
|
private readonly IRepository<SCPInstance> _instanceRepository;
|
|
private readonly IRepository<Dictionary> _dictionaryRepository;
|
|
private readonly IDistributedLockProvider _distributedLockProvider;
|
|
|
|
|
|
private List<Guid> _instanceIdList = new List<Guid>();
|
|
|
|
public DicomArchiveService(IRepository<SCPPatient> patientRepository, IRepository<SCPStudy> studyRepository,
|
|
IRepository<SCPSeries> seriesRepository,
|
|
IRepository<SCPInstance> instanceRepository,
|
|
IRepository<Dictionary> dictionaryRepository,
|
|
IDistributedLockProvider distributedLockProvider)
|
|
{
|
|
_distributedLockProvider = distributedLockProvider;
|
|
_studyRepository = studyRepository;
|
|
_patientRepository = patientRepository;
|
|
_seriesRepository = seriesRepository;
|
|
_instanceRepository = instanceRepository;
|
|
_dictionaryRepository = dictionaryRepository;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// 单个文件接收 归档
|
|
/// </summary>
|
|
/// <param name="dataset"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="NotImplementedException"></exception>
|
|
public async Task<Guid> ArchiveDicomFileAsync(DicomDataset dataset, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
|
|
{
|
|
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
|
|
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
|
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
|
|
|
|
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID,string.Empty);
|
|
|
|
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
|
|
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString());
|
|
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString());
|
|
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString());
|
|
|
|
var isStudyNeedAdd = false;
|
|
var isSeriesNeedAdd = false;
|
|
var isInstanceNeedAdd = false;
|
|
var isPatientNeedAdd = false;
|
|
|
|
//var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
|
|
|
//using (@lock.Acquire())
|
|
{
|
|
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId );
|
|
var findStudy = await _studyRepository.FirstOrDefaultAsync(t=>t.Id== studyId);
|
|
var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
|
var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == instanceId);
|
|
|
|
DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay);
|
|
|
|
//先传输了修改了患者编号的,又传输了没有修改患者编号的,导致后传输的没有修改患者编号的下面的检查为0
|
|
if (findPatient == null && findStudy==null)
|
|
{
|
|
isPatientNeedAdd = true;
|
|
|
|
|
|
findPatient = new SCPPatient()
|
|
{
|
|
Id = NewId.NextSequentialGuid(),
|
|
TrialId=trialId,
|
|
TrialSiteId=trialSiteId,
|
|
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
|
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
|
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
|
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
|
|
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
|
|
|
|
EarliestStudyTime = studyTime,
|
|
LatestStudyTime = studyTime,
|
|
LatestPushTime = DateTime.Now,
|
|
};
|
|
|
|
if (findPatient.PatientBirthDate.Length == 8)
|
|
{
|
|
var birthDateStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}-{findPatient.PatientBirthDate[4]}{findPatient.PatientBirthDate[5]}-{findPatient.PatientBirthDate[6]}{findPatient.PatientBirthDate[7]}";
|
|
|
|
var yearStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}";
|
|
|
|
int year = 0;
|
|
|
|
var canParse = int.TryParse(yearStr, out year);
|
|
|
|
if (canParse && year > 1900)
|
|
{
|
|
findPatient.PatientBirthDate = birthDateStr;
|
|
|
|
DateTime birthDate;
|
|
|
|
if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate,out birthDate))
|
|
{
|
|
var patientAge = studyTime.Value.Year - birthDate.Year;
|
|
// 如果生日还未到,年龄减去一岁
|
|
if (studyTime.Value < birthDate.AddYears(patientAge))
|
|
{
|
|
patientAge--;
|
|
}
|
|
|
|
findPatient.PatientAge = patientAge.ToString();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
findPatient.PatientBirthDate = string.Empty;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (studyTime < findPatient.EarliestStudyTime)
|
|
{
|
|
findPatient.EarliestStudyTime = studyTime;
|
|
}
|
|
if (studyTime > findPatient.LatestStudyTime)
|
|
{
|
|
findPatient.LatestStudyTime = studyTime;
|
|
}
|
|
|
|
findPatient.LatestPushTime = DateTime.Now;
|
|
}
|
|
|
|
if (findStudy == null)
|
|
{
|
|
isStudyNeedAdd = true;
|
|
findStudy = new SCPStudy
|
|
{
|
|
CalledAE = calledAE,
|
|
CallingAE = callingAE,
|
|
|
|
PatientId = findPatient.Id,
|
|
Id = studyId,
|
|
TrialId = trialId,
|
|
TrialSiteId = trialSiteId,
|
|
StudyInstanceUid = studyInstanceUid,
|
|
StudyTime = studyTime,
|
|
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
|
//ModalityForEdit = modalityForEdit,
|
|
Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
|
|
InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty),
|
|
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
|
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
|
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
|
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
|
|
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
|
|
|
|
StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty),
|
|
AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty),
|
|
|
|
//需要特殊处理
|
|
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
|
|
|
|
|
|
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
|
|
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
|
|
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
|
|
|
|
|
|
|
|
//IsDoubleReview = addtionalInfo.IsDoubleReview,
|
|
SeriesCount = 0,
|
|
InstanceCount = 0
|
|
};
|
|
|
|
|
|
if (findStudy.PatientBirthDate.Length == 8)
|
|
{
|
|
findStudy.PatientBirthDate = $"{findStudy.PatientBirthDate[0]}{findStudy.PatientBirthDate[1]}{findStudy.PatientBirthDate[2]}{findStudy.PatientBirthDate[3]}-{findStudy.PatientBirthDate[4]}{findStudy.PatientBirthDate[5]}-{findStudy.PatientBirthDate[6]}{findStudy.PatientBirthDate[7]}";
|
|
}
|
|
}
|
|
|
|
|
|
if (findSerice == null)
|
|
{
|
|
isSeriesNeedAdd = true;
|
|
|
|
findSerice = new SCPSeries
|
|
{
|
|
Id = seriesId,
|
|
StudyId = findStudy.Id,
|
|
|
|
StudyInstanceUid = findStudy.StudyInstanceUid,
|
|
SeriesInstanceUid = seriesInstanceUid,
|
|
SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1),
|
|
//SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay),
|
|
//SeriesTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.SeriesDate) + dataset.GetSingleValue<string>(DicomTag.SeriesTime), out DateTime dt) ? dt : null,
|
|
SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.SeriesDate).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.SeriesTime).TimeOfDay),
|
|
Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
|
Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
|
|
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
|
|
|
|
ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty),
|
|
ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty),
|
|
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
|
|
SequenceName = dataset.GetSingleValueOrDefault(DicomTag.SequenceName, string.Empty),
|
|
ProtocolName = dataset.GetSingleValueOrDefault(DicomTag.ProtocolName, string.Empty),
|
|
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
|
|
|
|
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
|
|
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
|
|
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
|
|
|
|
|
|
InstanceCount = 0
|
|
};
|
|
|
|
++findStudy.SeriesCount;
|
|
}
|
|
|
|
|
|
if (findInstance == null)
|
|
{
|
|
isInstanceNeedAdd = true;
|
|
findInstance = new SCPInstance
|
|
{
|
|
Id = instanceId,
|
|
StudyId = findStudy.Id,
|
|
SeriesId = findSerice.Id,
|
|
StudyInstanceUid = findStudy.StudyInstanceUid,
|
|
SeriesInstanceUid = findSerice.SeriesInstanceUid,
|
|
|
|
SopInstanceUid = sopInstanceUid,
|
|
InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
|
|
InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.ContentDate).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.ContentTime).TimeOfDay),
|
|
//InstanceTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.ContentDate) + dataset.GetSingleValue<string>(DicomTag.ContentTime), out DateTime dt) ? dt : null,
|
|
//InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate,(DateTime?)null)?.Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, TimeSpan.Zero)),
|
|
//dataset.GetSingleValueOrDefault(DicomTag.ContentDate,DateTime.Now);//, DicomTag.ContentTime)
|
|
CPIStatus = false,
|
|
ImageRows = dataset.GetSingleValueOrDefault(DicomTag.Rows, 0),
|
|
ImageColumns = dataset.GetSingleValueOrDefault(DicomTag.Columns, 0),
|
|
SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0),
|
|
|
|
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
|
|
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0),
|
|
PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty),
|
|
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
|
|
FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty),
|
|
WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty),
|
|
WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty),
|
|
|
|
Path = fileRelativePath,
|
|
|
|
FileSize= fileSize,
|
|
|
|
};
|
|
|
|
++findStudy.InstanceCount;
|
|
++findSerice.InstanceCount;
|
|
}
|
|
|
|
if (isPatientNeedAdd)
|
|
{
|
|
var ss = await _patientRepository.AddAsync(findPatient);
|
|
}
|
|
if (isStudyNeedAdd)
|
|
{
|
|
var dd = await _studyRepository.AddAsync(findStudy);
|
|
}
|
|
else
|
|
{
|
|
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == findStudy.Id, t => new SCPStudy() { IsUploadFinished = false });
|
|
}
|
|
|
|
if (isSeriesNeedAdd)
|
|
{
|
|
await _seriesRepository.AddAsync(findSerice);
|
|
}
|
|
if (isInstanceNeedAdd)
|
|
{
|
|
await _instanceRepository.AddAsync(findInstance);
|
|
}
|
|
else
|
|
{
|
|
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath,FileSize=fileSize });
|
|
}
|
|
|
|
await _studyRepository.SaveChangesAsync();
|
|
|
|
return findStudy.Id;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// 从DICOM文件中获取使用的字符集
|
|
private string GetEncodingVaulueFromDicomFile(DicomDataset dataset, DicomTag dicomTag)
|
|
{
|
|
|
|
// 获取DICOM文件的特定元素,通常用于指示使用的字符集
|
|
var charset = dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);
|
|
|
|
var dicomEncoding = DicomEncoding.GetEncoding(charset);
|
|
|
|
|
|
var dicomStringElement = dataset.GetDicomItem<DicomStringElement>(dicomTag);
|
|
|
|
var bytes = dicomStringElement.Buffer.Data;
|
|
|
|
|
|
return dicomEncoding.GetString(bytes);
|
|
|
|
|
|
//// 从DICOM文件中获取使用的字符集
|
|
//string filePath = "C:\\Users\\hang\\Documents\\WeChat Files\\wxid_r2imdzb7j3q922\\FileStorage\\File\\2024-05\\1.2.840.113619.2.80.169103990.5390.1271401378.4.dcm";
|
|
//DicomFile dicomFile = DicomFile.Open(filePath);
|
|
|
|
//// 获取DICOM文件的特定元素,通常用于指示使用的字符集
|
|
//var charset = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);
|
|
|
|
//var dicomEncoding = DicomEncoding.GetEncoding(charset);
|
|
|
|
//var value = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
|
|
|
|
//var dicomStringElement = dicomFile.Dataset.GetDicomItem<DicomStringElement>(DicomTag.PatientName);
|
|
|
|
//var bytes = dicomStringElement.Buffer.Data;
|
|
|
|
//var aa= dicomEncoding.GetString(bytes);
|
|
|
|
|
|
}
|
|
}
|
|
}
|