irc-netcore-api/IRaCIS.Core.Application/Service/ImageAndDoc/DicomArchiveService.cs

450 lines
20 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using Dicom;
using Dicom.Imaging.Codec;
using EasyCaching.Core;
using IRaCIS.Core.Application.Contracts.Dicom;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Domain.Share;
using Microsoft.Extensions.Hosting;
using System.Text;
using IRaCIS.Core.Application.Contracts;
namespace IRaCIS.Core.Application.Services
{
public class DicomArchiveService : IDicomArchiveService
{
private readonly IRepository<DicomStudy> _studyRepository;
private readonly IRepository<DicomSeries> _seriesRepository;
private readonly IRepository<DicomInstance> _instanceRepository;
private readonly IEasyCachingProvider _provider;
private readonly DicomFileStoreHelper _dicomFileStoreHelper;
private static object lockCodeGenerate = new object();
public DicomArchiveService(IRepository<DicomStudy> studyRepository,
IRepository<DicomSeries> seriesRepository,
IRepository<DicomInstance> instanceRepository,
IHostEnvironment hostEnvironment,
DicomFileStoreHelper dicomFileStoreHelper,
IEasyCachingProvider provider)
{
_dicomFileStoreHelper = dicomFileStoreHelper;
_studyRepository = studyRepository;
_seriesRepository = seriesRepository;
_instanceRepository = instanceRepository;
_provider = provider;
}
public async Task<bool> DicomDBDataSaveChange()
{
var success = await _studyRepository.SaveChangesAsync();
return success;
}
public async Task<Guid> ArchiveDicomStreamAsync(Stream dicomStream,
DicomTrialSiteSubjectInfo addtionalInfo, List<string> seriesInstanceUidList, List<string> instanceUidList)
{
DicomFile dicomFile = await DicomFile.OpenAsync(dicomStream, Encoding.Default);
DicomDataset dataset = dicomFile.Dataset;
//如果数据库存在该instance 记录 那么就不处理
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
if (instanceUidList.Any(t => t == sopInstanceUid))
{
return IdentifierHelper.CreateGuid(studyInstanceUid, addtionalInfo.TrialId.ToString());
}
var anonymize_AddFixedFiledList = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize_AddFixedFiled).Value;
var anonymize_AddIRCInfoFiled = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize_AddIRCInfoFiled).Value;
var anonymize_FixedField = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize_FixedField).Value;
var anonymize_IRCInfoField = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize_IRCInfoField).Value;
foreach (var item in anonymize_AddFixedFiledList.Union(anonymize_FixedField))
{
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
dataset.AddOrUpdate(dicomTag, item.ReplaceValue);
}
foreach (var item in anonymize_AddIRCInfoFiled.Union(anonymize_IRCInfoField))
{
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
if (dicomTag == DicomTag.ClinicalTrialProtocolID)
{
dataset.AddOrUpdate(dicomTag, addtionalInfo.TrialCode);
}
if (dicomTag == DicomTag.ClinicalTrialSiteID)
{
dataset.AddOrUpdate(dicomTag, addtionalInfo.TrialSiteCode);
}
if (dicomTag == DicomTag.ClinicalTrialSubjectID)
{
dataset.AddOrUpdate(dicomTag, addtionalInfo.SubjectCode);
}
if (dicomTag == DicomTag.ClinicalTrialTimePointID)
{
dataset.AddOrUpdate(dicomTag, addtionalInfo.VisitNum.ToString());
}
if (dicomTag == DicomTag.PatientID)
{
dataset.AddOrUpdate(dicomTag, addtionalInfo.TrialCode+"_"+ addtionalInfo.SubjectCode);
}
}
#region 调整废弃
////按照配置文件 匿名化
//foreach (var anonymizeItem in SystemConfig.AnonymizeTagList)
//{
// if (anonymizeItem.Enable)
// {
// ushort group = Convert.ToUInt16(anonymizeItem.Group, 16);
// ushort element = Convert.ToUInt16(anonymizeItem.Element, 16);
// dataset.AddOrUpdate(new DicomTag(group, element), anonymizeItem.ReplaceValue);
// }
//}
//if (AppSettings.AddClinicalInfo) //是否需要写入临床信息
//{
// //Dicom 文件中写入临床信息
// dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolID, addtionalInfo.TrialCode); //Trial
// dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolName, addtionalInfo.TrialIndication); //indication
// dataset.AddOrUpdate(DicomTag.ClinicalTrialSponsorName, addtionalInfo.Sponsor);//sponsor
// dataset.AddOrUpdate(DicomTag.ClinicalTrialSiteID, addtionalInfo.SiteCode); //SiteId
// dataset.AddOrUpdate(DicomTag.ClinicalTrialSiteName, addtionalInfo.SiteName);//SiteName
// dataset.AddOrUpdate(DicomTag.ClinicalTrialSubjectID, addtionalInfo.SubjectCode + " " + addtionalInfo.SubjectSex);//SubjectId
// dataset.AddOrUpdate(DicomTag.ClinicalTrialTimePointID, addtionalInfo.VisitNum.ToString()); // TimePoint
// dataset.AddOrUpdate(DicomTag.ClinicalTrialTimePointDescription, addtionalInfo.VisitName + " " + addtionalInfo.SVUPDES);
//}
//DicomStudy dicomStudy = null;
//DicomSeries dicomSeries = null;
//DicomInstance dicomInstance = null;
//lock (lockTest)
//{
// dicomStudy = CreateDicomStudy(dataset, addtionalInfo, out bool isStudyNeedAdd);
// dicomSeries = CreateDicomSeries(dataset, dicomStudy, out bool isSeriesNeedAdd);
// dicomInstance = CreateDicomInstance(dataset, dicomStudy, dicomSeries);
// if (isStudyNeedAdd) _studyRepository.Add(dicomStudy);
// if (isSeriesNeedAdd) _seriesRepository.Add(dicomSeries);
// _instanceRepository.Add(dicomInstance);
//}
#endregion
DicomStudy dicomStudy = CreateDicomStudy(dataset, addtionalInfo, out bool isStudyNeedAdd);
DicomSeries dicomSeries = CreateDicomSeries(dataset, dicomStudy, out bool isSeriesNeedAdd);
DicomInstance dicomInstance = CreateDicomInstance(dataset, dicomStudy, dicomSeries);
if (isStudyNeedAdd) await _studyRepository.AddAsync(dicomStudy);
if (isSeriesNeedAdd) await _seriesRepository.AddAsync(dicomSeries);
await _instanceRepository.AddAsync(dicomInstance);
string filePath = _dicomFileStoreHelper.CreateInstanceFilePath(dicomStudy, dicomSeries.Id, dicomInstance.Id);
var samplesPerPixel = dataset.GetSingleValueOrDefault(DicomTag.SamplesPerPixel, string.Empty);
var photometricInterpretation = dataset.GetSingleValueOrDefault(DicomTag.PhotometricInterpretation, string.Empty);
if (samplesPerPixel == "1" && (photometricInterpretation.ToUpper() == "MONOCHROME2" || photometricInterpretation.ToUpper() == "MONOCHROME1"))//MONOCHROME2
{
if (dataset.InternalTransferSyntax.IsEncapsulated)
{
await dicomFile.SaveAsync(filePath);
}
else
{
await dicomFile.Clone(DicomTransferSyntax.JPEGLSLossless).SaveAsync(filePath);
}
}
else
{
if (dataset.InternalTransferSyntax.IsEncapsulated) await dicomFile.SaveAsync(filePath);
else await dicomFile.Clone(DicomTransferSyntax.RLELossless).SaveAsync(filePath); //RLELossless
}
return dicomInstance.StudyId;
}
private DicomStudy CreateDicomStudy(DicomDataset dataset, DicomTrialSiteSubjectInfo addtionalInfo, out bool isStudyNeedAdd)
{
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, addtionalInfo.TrialId.ToString());
// 每个线程都查询数据库最大的和缓存中最大的取最大值为基数生成Code
//var id = Thread.CurrentThread.ManagedThreadId.ToString("00");
//虽然每个文件都会进来,但是只要查询过,就会跟踪,不会再次查询数据库 这里线程并发会有问题得加锁不然生成Code 出错
DicomStudy dicomStudy = _studyRepository.ImageFind(studyId, typeof(DicomStudy));
if (dicomStudy != null)
{
isStudyNeedAdd = false;
return dicomStudy;
}
//_logger.LogWarning($"Thread {id} ,studyUid{studyInstanceUid} 生成StudyId{studyId}");
isStudyNeedAdd = true;
dicomStudy = new DicomStudy
{
Id = studyId,
StudyInstanceUid = studyInstanceUid,
/* StudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, DateTime.Now).TimeOfDay),*///dataset.GetDateTime(DicomTag.StudyDate, DicomTag.StudyTime),
StudyTime = dataset.GetSingleValue<DateTime?>(DicomTag.StudyDate)?.Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, TimeSpan.Zero)),
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty),
PatientId = 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),
SiteId = addtionalInfo.SiteId,
TrialId = addtionalInfo.TrialId,
SubjectId = addtionalInfo.SubjectId,
SubjectVisitId = addtionalInfo.SubjectVisitId,
//IsDoubleReview = addtionalInfo.IsDoubleReview,
SeriesCount = 0,
InstanceCount = 0
};
if (dicomStudy.PatientBirthDate.Length == 8)
{
dicomStudy.PatientBirthDate = $"{dicomStudy.PatientBirthDate[0]}{dicomStudy.PatientBirthDate[1]}{dicomStudy.PatientBirthDate[2]}{dicomStudy.PatientBirthDate[3]}-{dicomStudy.PatientBirthDate[4]}{dicomStudy.PatientBirthDate[5]}-{dicomStudy.PatientBirthDate[6]}{dicomStudy.PatientBirthDate[7]}";
}
lock (lockCodeGenerate)
{
//查询数据库获取最大的Code 没有记录则为0
var dbStudyCodeIntMax = _studyRepository.Where(s => s.TrialId == addtionalInfo.TrialId).Select(t => t.Code).DefaultIfEmpty().Max();
//获取缓存中的值 并发的时候,需要记录,已被占用的值 这样其他线程在此占用的最大的值上递增
var cacheMaxCodeInt = _provider.Get<int>($"{addtionalInfo.TrialId }_{ StaticData.StudyMaxCode}").Value;
int currentNextCodeInt = cacheMaxCodeInt > dbStudyCodeIntMax ? cacheMaxCodeInt + 1 : dbStudyCodeIntMax + 1;
dicomStudy.Code = currentNextCodeInt;
dicomStudy.StudyCode = "ST" + currentNextCodeInt.ToString("D5");
_provider.Set<int>($"{addtionalInfo.TrialId }_{ StaticData.StudyMaxCode}", dicomStudy.Code, TimeSpan.FromMinutes(30));
}
#region Setting Code old
//var studyCode = _studyRepository.Where(s => s.TrialId == addtionalInfo.TrialId).Select(t => t.StudyCode).OrderByDescending(c => c).FirstOrDefault();
//var cacheMaxCode = _provider.Get<string>($"{addtionalInfo.TrialId }_{ StaticData.StudyMaxCode}").Value;
//if (studyCode == null && string.IsNullOrEmpty(cacheMaxCode))
//{
// dicomStudy.StudyCode = "ST" + 1.ToString().PadLeft(5, '0');
// _logger.LogWarning($"Thread {id} DB{studyCode} 生成{ dicomStudy.StudyCode}");
//}
//else
//{
// int dbNum = 0;
// int cacheNum = 0;
// if (studyCode != null)
// {
// int.TryParse(studyCode.Substring(studyCode.Length - 5, 5), out dbNum);
// }
// if (!string.IsNullOrEmpty(cacheMaxCode))
// {
// int.TryParse(cacheMaxCode.Substring(cacheMaxCode.Length - 5, 5), out cacheNum);
// }
// dbNum = cacheNum > dbNum ? cacheNum : dbNum;
// dicomStudy.StudyCode = "ST" + (++dbNum).ToString().PadLeft(5, '0');
// _logger.LogWarning($" Thread {id} DB{studyCode} cache:{cacheNum} 生成{ dicomStudy.StudyCode}");
//}
//_provider.Set<string>($"{addtionalInfo.TrialId }_{ StaticData.StudyMaxCode}", dicomStudy.StudyCode, TimeSpan.FromMinutes(30));
#endregion
return dicomStudy;
}
private DicomSeries CreateDicomSeries(DicomDataset dataset, DicomStudy dicomStudy, out bool isSeriesNeedAdd)
{
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
Guid seriesId = IdentifierHelper.CreateGuid(dicomStudy.StudyInstanceUid, seriesInstanceUid, dicomStudy.TrialId.ToString());
//有几个序列会查询几次
DicomSeries dicomSeries = _seriesRepository.ImageFind(seriesId, typeof(DicomSeries));
if (dicomSeries != null)
{
isSeriesNeedAdd = false;
return dicomSeries;
}
else
{
//var id = Thread.CurrentThread.ManagedThreadId.ToString("00");
isSeriesNeedAdd = true;
dicomSeries = new DicomSeries
{
Id = seriesId,
StudyId = dicomStudy.Id,
StudyInstanceUid = dicomStudy.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 = dataset.GetSingleValue<DateTime?>(DicomTag.SeriesDate)?.Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, TimeSpan.Zero)),// dataset.GetDateTime(DicomTag.SeriesDate, DicomTag.SeriesTime),
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.ImagerPixelSpacing, string.Empty),
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
SiteId = dicomStudy.SiteId,
TrialId = dicomStudy.TrialId,
SubjectId = dicomStudy.SubjectId,
SubjectVisitId = dicomStudy.SubjectVisitId,
InstanceCount = 0
};
++dicomStudy.SeriesCount;
//_logger.LogWarning($"线程:{id},sericeId:{seriesId},count:{SeriesDic.Keys.Count}");
return dicomSeries;
}
}
private DicomInstance CreateDicomInstance(DicomDataset dataset, DicomStudy dicomStudy, DicomSeries dicomSeries)
{
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
Guid instanceId = IdentifierHelper.CreateGuid(dicomStudy.StudyInstanceUid, dicomSeries.SeriesInstanceUid, sopInstanceUid, dicomStudy.TrialId.ToString());
DicomInstance dicomInstance = new DicomInstance
{
Id = instanceId,
StudyId = dicomStudy.Id,
SeriesId = dicomSeries.Id,
SiteId = dicomStudy.SiteId,
TrialId = dicomStudy.TrialId,
SubjectId = dicomStudy.SubjectId,
SubjectVisitId = dicomStudy.SubjectVisitId,
StudyInstanceUid = dicomStudy.StudyInstanceUid,
SeriesInstanceUid = dicomSeries.SeriesInstanceUid,
SopInstanceUid = sopInstanceUid,
InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
InstanceTime = dataset.GetSingleValue<DateTime?>(DicomTag.ContentDate)?.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),
};
++dicomStudy.InstanceCount;
++dicomSeries.InstanceCount;
return dicomInstance;
}
}
}