public void HandleCStoreRequest(DicomCStoreRequest request, string calledAeTitle, uint associationId) { Guard.Against.Null(request, nameof(request)); if (!_aeTitleManagers.ContainsKey(calledAeTitle)) { throw new ArgumentException($"Called AE Title '{calledAeTitle}' is not configured"); } if (!_storageInfoProvider.HasSpaceAvailableToStore) { throw new InsufficientStorageAvailableException($"Insufficient storage available. Available storage space: {_storageInfoProvider.AvailableFreeSpace:D}"); } _logger.Log(LogLevel.Information, $"Preparing to save instance received for {calledAeTitle} in {_aeTitleManagers[calledAeTitle].Value.AeStorageRootFullPath}."); var instanceStorage = InstanceStorageInfo.CreateInstanceStorageInfo(request, _aeTitleManagers[calledAeTitle].Value.AeStorageRootFullPath, calledAeTitle, associationId); using (_logger.BeginScope("SOPInstanceUID={0}", instanceStorage.SopInstanceUid)) { _logger.Log(LogLevel.Information, "Patient ID: {PatientId}", instanceStorage.PatientId); _logger.Log(LogLevel.Information, "Study Instance UID: {StudyInstanceUid}", instanceStorage.StudyInstanceUid); _logger.Log(LogLevel.Information, "Series Instance UID: {SeriesInstanceUid}", instanceStorage.SeriesInstanceUid); _logger.Log(LogLevel.Information, "Storage File Path: {InstanceStorageFullPath}", instanceStorage.InstanceStorageFullPath); _aeTitleManagers[calledAeTitle].Value.Save(request, instanceStorage); _logger.Log(LogLevel.Debug, $"Instance saved with handler {instanceStorage.InstanceStorageFullPath}"); } }
public void CreateWithValidData() { var instance = InstanceStorageInfo.CreateInstanceStorageInfo( _request, _storageRoot, "MyAeTitle", _fileSystem); var expectedAaeStoragePath = Path.Combine(_storageRoot, "MyAeTitle"); var expectedPatientStoragePath = Path.Combine(expectedAaeStoragePath, "PID"); var expectedSeriesStoragePath = Path.Combine(expectedPatientStoragePath, Path.Combine(_studyInstanceUid, _seriesInstanceUid)); var expectedInstanceStoragePath = Path.Combine(expectedSeriesStoragePath, $"{_sopInstanceUid}.dcm"); Assert.Equal("1.2.3", instance.SopClassUid); Assert.Equal("PID", instance.PatientId); Assert.Equal(_studyInstanceUid, instance.StudyInstanceUid); Assert.Equal(_seriesInstanceUid, instance.SeriesInstanceUid); Assert.Equal(_sopInstanceUid, instance.SopInstanceUid); Assert.Equal("MyAeTitle", instance.CalledAeTitle); Assert.Equal(_storageRoot, instance.StorageRootPath); Assert.Equal(expectedAaeStoragePath, instance.AeStoragePath); Assert.Equal(expectedPatientStoragePath, instance.PatientStoragePath); Assert.Equal(expectedInstanceStoragePath, instance.InstanceStorageFullPath); Assert.True(_fileSystem.Directory.Exists(expectedSeriesStoragePath)); var logger = new Mock <ILogger>(); logger.Setup(p => p.Information("", "")); }
private async Task RetrieveInstances(IDicomWebClient dicomWebClient, string studyInstanceUid, RequestedSeries series, string storagePath, Dictionary <string, InstanceStorageInfo> retrievedInstance) { Guard.Against.NullOrWhiteSpace(studyInstanceUid, nameof(studyInstanceUid)); Guard.Against.Null(series, nameof(series)); Guard.Against.Null(storagePath, nameof(storagePath)); Guard.Against.Null(retrievedInstance, nameof(retrievedInstance)); foreach (var instance in series.Instances) { if (instance.IsRetrieved) { continue; } foreach (var sopInstanceUid in instance.SopInstanceUid) { _logger.Log(LogLevel.Information, $"Retrieving instance {sopInstanceUid}"); var file = await dicomWebClient.Wado.Retrieve(studyInstanceUid, series.SeriesInstanceUid, sopInstanceUid); var instanceStorageInfo = InstanceStorageInfo.CreateInstanceStorageInfo(file, storagePath, _fileSystem); if (retrievedInstance.ContainsKey(instanceStorageInfo.SopInstanceUid)) { _logger.Log(LogLevel.Warning, $"Instance '{instanceStorageInfo.SopInstanceUid}' already retrieved/stored."); continue; } SaveFile(file, instanceStorageInfo); retrievedInstance.Add(instanceStorageInfo.SopInstanceUid, instanceStorageInfo); } instance.IsRetrieved = true; } }
public void ShallSaveAndNotify() { _dicomToolkit.Setup(p => p.Save(It.IsAny <DicomFile>(), It.IsAny <string>())); _notificationService.Setup(p => p.NewInstanceStored(It.IsAny <InstanceStorageInfo>())); var config = new ClaraApplicationEntity(); config.AeTitle = "my-aet"; config.Processor = "Nvidia.Clara.DicomAdapter.Test.Unit.MockJobProcessor, Nvidia.Clara.Dicom.Test.Unit"; var handler = new ApplicationEntityHandler(_serviceProvider, config, _rootStoragePath, _cancellationTokenSource.Token, _fileSystem); var request = GenerateRequest(); var instance = InstanceStorageInfo.CreateInstanceStorageInfo(request, _rootStoragePath, config.AeTitle, _fileSystem); handler.Save(request, instance); _fileSystem.File.Create(instance.InstanceStorageFullPath); handler.Save(request, instance); _logger.VerifyLogging(LogLevel.Error, Times.Never()); _logger.VerifyLogging("Instance already exists, skipping.", LogLevel.Information, Times.Once()); _logger.VerifyLogging("Instance saved successfully.", LogLevel.Debug, Times.Once()); _logger.VerifyLogging("Instance stored and notified successfully.", LogLevel.Information, Times.Once()); _dicomToolkit.Verify(p => p.Save(It.IsAny <DicomFile>(), It.IsAny <string>()), Times.Exactly(1)); _notificationService.Verify(p => p.NewInstanceStored(instance), Times.Once()); }
private async Task SaveFiles(IAsyncEnumerable <DicomFile> files, string storagePath, Dictionary <string, InstanceStorageInfo> retrievedInstance) { Guard.Against.Null(files, nameof(files)); Guard.Against.Null(storagePath, nameof(storagePath)); Guard.Against.Null(retrievedInstance, nameof(retrievedInstance)); var total = 0; var saved = 0; await foreach (var file in files) { total++; var instance = InstanceStorageInfo.CreateInstanceStorageInfo(file, storagePath, _fileSystem); if (retrievedInstance.ContainsKey(instance.SopInstanceUid)) { _logger.Log(LogLevel.Warning, $"Instance '{instance.SopInstanceUid}' already retrieved/stored."); continue; } SaveFile(file, instance); retrievedInstance.Add(instance.SopInstanceUid, instance); saved++; } _logger.Log(LogLevel.Information, $"Saved {saved} out of {total} instances retrieved"); }
private void RestoreExistingInstances(InferenceRequest inferenceRequest, Dictionary <string, InstanceStorageInfo> retrievedInstances) { Guard.Against.Null(inferenceRequest, nameof(inferenceRequest)); Guard.Against.Null(retrievedInstances, nameof(retrievedInstances)); _logger.Log(LogLevel.Debug, $"Restoring previously retrieved DICOM instances from {inferenceRequest.StoragePath}"); foreach (var file in _fileSystem.Directory.EnumerateFiles(inferenceRequest.StoragePath, "*.dcm", System.IO.SearchOption.AllDirectories)) { if (_dicomToolkit.HasValidHeader(file)) { var dicomFile = _dicomToolkit.Open(file); var instance = InstanceStorageInfo.CreateInstanceStorageInfo(dicomFile, inferenceRequest.StoragePath, _fileSystem); if (retrievedInstances.ContainsKey(instance.SopInstanceUid)) { continue; } retrievedInstances.Add(instance.SopInstanceUid, instance); _logger.Log(LogLevel.Debug, $"Restored previously retrieved instance {instance.SopInstanceUid}"); } else { _logger.Log(LogLevel.Warning, $"Unable to restore previously retrieved instance from {file}; file does not contain valid DICOM header."); } } }
public void CreateWithValidData() { var expectedAeStoragePath = Path.Combine(_storageRoot, "MyAeTitle"); var expectedAeAssocationStoragePath = Path.Combine(expectedAeStoragePath, "1", "dcm"); var expectedPatientStoragePath = Path.Combine(expectedAeAssocationStoragePath, "PID"); var expectedSeriesStoragePath = Path.Combine(expectedPatientStoragePath, Path.Combine(_studyInstanceUid, _seriesInstanceUid)); var expectedInstanceStoragePath = Path.Combine(expectedSeriesStoragePath, $"{_sopInstanceUid}.dcm"); var instance = InstanceStorageInfo.CreateInstanceStorageInfo( _request, expectedAeStoragePath, "MyAeTitle", 1, _fileSystem); Assert.Equal("1.2.3", instance.SopClassUid); Assert.Equal("PID", instance.PatientId); Assert.Equal(_studyInstanceUid, instance.StudyInstanceUid); Assert.Equal(_seriesInstanceUid, instance.SeriesInstanceUid); Assert.Equal(_sopInstanceUid, instance.SopInstanceUid); Assert.Equal("MyAeTitle", instance.CalledAeTitle); Assert.Equal(expectedAeAssocationStoragePath, instance.AeStoragePath); Assert.Equal(expectedPatientStoragePath, instance.PatientStoragePath); Assert.Equal(expectedInstanceStoragePath, instance.InstanceStorageFullPath); Assert.True(_fileSystem.Directory.Exists(expectedSeriesStoragePath)); }
public void CreateWithNullDicomCStoreRequestShallThrow() { Assert.Throws <ArgumentNullException>(() => { InstanceStorageInfo.CreateInstanceStorageInfo( null, _storageRoot, "AETITLE"); }); }
public void CreateWithoutSourceAeTitleShallThrow() { Assert.Throws <ArgumentException>(() => { InstanceStorageInfo.CreateInstanceStorageInfo( _request, _storageRoot, " "); }); }
public void CreateWithoutStorageRootFullPathShallThrow() { Assert.Throws <ArgumentException>(() => { InstanceStorageInfo.CreateInstanceStorageInfo( _request, string.Empty, "AETITLE"); }); }
public static InstanceStorageInfo GenerateInstance( string storageRootFullPath, string calledAeTitle, uint associationId = 1, IFileSystem fileSystem = null) { fileSystem = fileSystem ?? new MockFileSystem(); var instance = InstanceStorageInfo.CreateInstanceStorageInfo( GenerateDicomCStoreRequest(), storageRootFullPath, calledAeTitle, associationId, fileSystem ?? new MockFileSystem()); fileSystem.File.Create(instance.InstanceStorageFullPath).Close(); return(instance); }
public async Task ShallDeleteFiles() { var files = new List <InstanceStorageInfo>() { InstanceStorageInfo.CreateInstanceStorageInfo(GenerateRequest(), "/test", "AET", _fileSystem), InstanceStorageInfo.CreateInstanceStorageInfo(GenerateRequest(), "/test", "AET", _fileSystem), InstanceStorageInfo.CreateInstanceStorageInfo(GenerateRequest(), "/test", "AET", _fileSystem) }; foreach (var file in files) { _fileSystem.File.Create(file.InstanceStorageFullPath); } var cancellationTokenSource = new CancellationTokenSource(); var stack = new Stack <InstanceStorageInfo>(files); _queue.Setup(p => p.Dequeue(It.IsAny <CancellationToken>())) .Returns(() => { if (stack.TryPop(out InstanceStorageInfo result)) { return(result); } cancellationTokenSource.Cancel(); return(null); }); await _service.StartAsync(cancellationTokenSource.Token); while (!cancellationTokenSource.IsCancellationRequested) { Thread.Sleep(100); } _queue.Verify(p => p.Dequeue(It.IsAny <CancellationToken>()), Times.AtLeast(3)); foreach (var file in files) { Assert.False(_fileSystem.File.Exists(file.InstanceStorageFullPath)); } _logger.VerifyLogging("Cancellation requested.", LogLevel.Information, Times.Once()); }
public void ShallRespectRetryPolicyOnFailures() { _dicomToolkit.Setup(p => p.Save(It.IsAny <DicomFile>(), It.IsAny <string>())).Throws <Exception>(); var config = new ClaraApplicationEntity(); config.AeTitle = "my-aet"; config.Processor = "Nvidia.Clara.DicomAdapter.Test.Unit.MockJobProcessor, Nvidia.Clara.Dicom.Test.Unit"; var handler = new ApplicationEntityHandler(_serviceProvider, config, _rootStoragePath, _cancellationTokenSource.Token, _fileSystem); var request = GenerateRequest(); var instance = InstanceStorageInfo.CreateInstanceStorageInfo(request, _rootStoragePath, config.AeTitle, _fileSystem); var exception = Assert.Throws <Exception>(() => { handler.Save(request, instance); }); _logger.VerifyLogging(LogLevel.Error, Times.Exactly(3)); _dicomToolkit.Verify(p => p.Save(It.IsAny <DicomFile>(), It.IsAny <string>()), Times.Exactly(4)); }
public void WorkflowTest() { var service = new InstanceStoredNotificationService(_logger.Object, _cleanupQueue.Object); var observer = new Mock <IObserver <InstanceStorageInfo> >(); observer.Setup(p => p.OnNext(It.IsAny <InstanceStorageInfo>())); var cancel = service.Subscribe(observer.Object); var request = GenerateRequest(); var instance = InstanceStorageInfo.CreateInstanceStorageInfo(request, "/storage", "AET", new MockFileSystem()); service.NewInstanceStored(instance); service.NewInstanceStored(instance); service.NewInstanceStored(instance); observer.Verify(p => p.OnNext(It.IsAny <InstanceStorageInfo>()), Times.Exactly(3)); cancel.Dispose(); observer.Reset(); service.NewInstanceStored(instance); observer.Verify(p => p.OnNext(It.IsAny <InstanceStorageInfo>()), Times.Never()); }
public void ShallQueueAndDequeueItems() { for (var i = 0; i < 10; i++) { _queue.QueueInstance(InstanceStorageInfo.CreateInstanceStorageInfo( GenerateRequest(), "/test", "AET", new MockFileSystem() )); } _cancellationTokenSource.CancelAfter(500); var items = new List <InstanceStorageInfo>(); for (var i = 0; i < 10; i++) { items.Add(_queue.Dequeue(_cancellationTokenSource.Token)); } Assert.Equal(10, items.Count); }
public void ShallIngoreInstancesWithConfiguredSopClassUids() { _dicomToolkit.Setup(p => p.Save(It.IsAny <DicomFile>(), It.IsAny <string>())); var config = new ClaraApplicationEntity(); config.AeTitle = "my-aet"; config.IgnoredSopClasses = new List <string>() { DicomUID.SecondaryCaptureImageStorage.UID }; config.Processor = "Nvidia.Clara.DicomAdapter.Test.Unit.MockJobProcessor, Nvidia.Clara.Dicom.Test.Unit"; var handler = new ApplicationEntityHandler(_serviceProvider, config, _rootStoragePath, _cancellationTokenSource.Token, _fileSystem); var request = GenerateRequest(); var instance = InstanceStorageInfo.CreateInstanceStorageInfo(request, _rootStoragePath, config.AeTitle, _fileSystem); handler.Save(request, instance); _logger.VerifyLogging($"Instance with SOP Class {DicomUID.SecondaryCaptureImageStorage.UID} ignored based on configured AET {config.AeTitle}", LogLevel.Warning, Times.Once()); _dicomToolkit.Verify(p => p.Save(It.IsAny <DicomFile>(), It.IsAny <string>()), Times.Never()); }
private void GenerateInstances() { var request = GenerateRequest(_patient1, _study1, _series1); var instance = InstanceStorageInfo.CreateInstanceStorageInfo(request, _fileSystem.Path.DirectorySeparatorChar.ToString(), _aeTitle, 1, _fileSystem); _instances.Add(instance); _fileSystem.File.CreateText(instance.InstanceStorageFullPath); request = GenerateRequest(_patient1, _study1, _series2); instance = InstanceStorageInfo.CreateInstanceStorageInfo(request, _fileSystem.Path.DirectorySeparatorChar.ToString(), _aeTitle, 1, _fileSystem); _instances.Add(instance); _fileSystem.File.CreateText(instance.InstanceStorageFullPath); request = GenerateRequest(_patient1, _study2, _series3); instance = InstanceStorageInfo.CreateInstanceStorageInfo(request, _fileSystem.Path.DirectorySeparatorChar.ToString(), _aeTitle, 1, _fileSystem); _instances.Add(instance); _fileSystem.File.CreateText(instance.InstanceStorageFullPath); request = GenerateRequest(_patient2, _study3, _series4); instance = InstanceStorageInfo.CreateInstanceStorageInfo(request, _fileSystem.Path.DirectorySeparatorChar.ToString(), _aeTitle, 1, _fileSystem); _instances.Add(instance); _fileSystem.File.CreateText(instance.InstanceStorageFullPath); }
public void HandleCStoreRequest(DicomCStoreRequest request, string calledAeTitle, uint associationId) { Guard.Against.Null(request, nameof(request)); if (!_aeTitleManagers.ContainsKey(calledAeTitle)) { throw new ArgumentException($"Called AE Title '{calledAeTitle}' is not configured"); } _logger.Log(LogLevel.Information, "Preparing to save instance from {callingAeTitle}.", calledAeTitle); var instanceStorage = InstanceStorageInfo.CreateInstanceStorageInfo(request, Configuration.Value.Storage.Temporary, calledAeTitle, associationId); using (_logger.BeginScope("SOPInstanceUID={0}", instanceStorage.SopInstanceUid)) { _logger.Log(LogLevel.Information, "Patient ID: {PatientId}", instanceStorage.PatientId); _logger.Log(LogLevel.Information, "Study Instance UID: {StudyInstanceUid}", instanceStorage.StudyInstanceUid); _logger.Log(LogLevel.Information, "Series Instance UID: {SeriesInstanceUid}", instanceStorage.SeriesInstanceUid); _logger.Log(LogLevel.Information, "Storage File Path: {InstanceStorageFullPath}", instanceStorage.InstanceStorageFullPath); _aeTitleManagers[calledAeTitle].Value.Save(request, instanceStorage); _logger.Log(LogLevel.Debug, "Instance saved with handler", instanceStorage.InstanceStorageFullPath); } }
public void NewInstanceStored_NoSupportedObservers() { var request = GenerateRequest(); var instance = InstanceStorageInfo.CreateInstanceStorageInfo(request, "/storage", "AET", new MockFileSystem()); _cleanupQueue.Setup(p => p.QueueInstance(It.IsAny <InstanceStorageInfo>())); var service = new InstanceStoredNotificationService(_logger.Object, _cleanupQueue.Object); var observer = new Mock <IObserver <InstanceStorageInfo> >(); observer.Setup(p => p.OnNext(It.IsAny <InstanceStorageInfo>())).Throws(new InstanceNotSupportedException(instance)); var cancel = service.Subscribe(observer.Object); service.NewInstanceStored(instance); observer.Verify(p => p.OnNext(It.IsAny <InstanceStorageInfo>()), Times.Once()); _cleanupQueue.Verify(p => p.QueueInstance(instance), Times.Once()); cancel.Dispose(); observer.Reset(); service.NewInstanceStored(instance); observer.Verify(p => p.OnNext(It.IsAny <InstanceStorageInfo>()), Times.Never()); }