Пример #1
0
        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("", ""));
        }
Пример #3
0
        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());
        }
Пример #5
0
        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");
        }
Пример #6
0
        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.");
                }
            }
        }
Пример #7
0
        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);
        }
Пример #12
0
        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));
        }
Пример #14
0
        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());
        }
Пример #17
0
        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);
            }
        }
Пример #19
0
        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());
        }