private DicomProcessingResult HandleDuplicate(string sopInstanceUid, StudyStorageLocation studyLocation, ServerCommandProcessor commandProcessor, DicomFile file) { Study study = studyLocation.Study ?? studyLocation.LoadStudy(ServerExecutionContext.Current.PersistenceContext); if (study != null) { Platform.Log(LogLevel.Info, "Received duplicate SOP {0} (A#:{1} StudyUid:{2} Patient: {3} ID:{4})", sopInstanceUid, study.AccessionNumber, study.StudyInstanceUid, study.PatientsName, study.PatientId); } else { Platform.Log(LogLevel.Info, "Received duplicate SOP {0} (StudyUid:{1}). Existing files haven't been processed.", sopInstanceUid, studyLocation.StudyInstanceUid); } SopProcessingContext sopProcessingContext = new SopProcessingContext(commandProcessor, studyLocation, _context.ContextID); DicomProcessingResult result = DuplicateSopProcessorHelper.Process(sopProcessingContext, file); return(result); }
private AutoReconcilerResult ProcessImageAsIs(DicomFile file, StudyHistory lastHistory) { StudyStorage destinationStudy = StudyStorage.Load(lastHistory.DestStudyStorageKey); StudyStorageLocation destStudy; AutoReconcilerResult preProcessingResult = new AutoReconcilerResult(StudyReconcileAction.ProcessAsIs); //Load the destination. An exception will be thrown if any issues are encountered. FilesystemMonitor.Instance.GetWritableStudyStorageLocation(destinationStudy.ServerPartitionKey, destinationStudy.StudyInstanceUid, StudyRestore.True, StudyCache.True, out destStudy); bool belongsToAnotherStudy = !destStudy.Equals(StorageLocation); EnsureStudyCanBeUpdated(destStudy); if (belongsToAnotherStudy) { preProcessingResult.Changes = new List <UpdateItem> { new UpdateItem(DicomTags.StudyInstanceUid, file.DataSet[DicomTags.StudyInstanceUid].ToString(), destStudy.StudyInstanceUid) }; file.DataSet[DicomTags.StudyInstanceUid].SetStringValue(destStudy.StudyInstanceUid); SopInstanceImporterContext importContext = new SopInstanceImporterContext( _contextID, file.SourceApplicationEntityTitle, destStudy.ServerPartition); SopInstanceImporter importer = new SopInstanceImporter(importContext); DicomProcessingResult result = importer.Import(file); if (!result.Successful) { throw new ApplicationException("Unable to import image to destination study"); } } return(preProcessingResult); }
private AutoReconcilerResult MergeImage(StudyReconcileAction action, DicomFile file, StudyHistory lastHistory) { string originalSeriesUid = file.DataSet[DicomTags.SeriesInstanceUid].ToString(); string originalSopUid = file.DataSet[DicomTags.SopInstanceUid].ToString(); AutoReconcilerResult preProcessingResult = null; StudyStorageLocation destStudy; UidMapper uidMapper = null; if (lastHistory.DestStudyStorageKey != null) { StudyStorage destinationStudy = StudyStorage.Load(lastHistory.DestStudyStorageKey); //Load the destination. An exception will be thrown if any issues are encountered. FilesystemMonitor.Instance.GetWritableStudyStorageLocation(destinationStudy.ServerPartitionKey, destinationStudy.StudyInstanceUid, StudyRestore.True, StudyCache.True, out destStudy); EnsureStudyCanBeUpdated(destStudy); bool belongsToAnotherStudy = !destStudy.Equals(StorageLocation); ImageUpdateCommandBuilder commandBuilder = new ImageUpdateCommandBuilder(); IList <BaseImageLevelUpdateCommand> commands = commandBuilder.BuildCommands <StudyMatchingMap>(destStudy); if (belongsToAnotherStudy) { Platform.Log(LogLevel.Info, "AUTO-RECONCILE: Move SOP {0} to Study {1}, A#: {2}, Patient {3}", originalSopUid, destStudy.StudyInstanceUid, destStudy.Study.AccessionNumber, destStudy.Study.PatientsName); // Load the Uid Map, either from cache or from disk if (!_uidMapCache.TryGetValue(destStudy.Key, out uidMapper)) { UidMapXml mapXml = new UidMapXml(); mapXml.Load(destStudy); uidMapper = new UidMapper(mapXml); _uidMapCache.Add(destStudy.Key, uidMapper); } try { commands.Add(GetUidMappingCommand(StorageLocation, destStudy, uidMapper, originalSopUid, originalSeriesUid)); } catch (InstanceAlreadyExistsException ex) { Platform.Log(LogLevel.Info, "An instance already exists with the SOP Instance Uid {0}", ex.SopInstanceUid); preProcessingResult = new AutoReconcilerResult(StudyReconcileAction.Discard) { DiscardImage = true }; return(preProcessingResult); } } preProcessingResult = new AutoReconcilerResult(action) { Changes = GetUpdateList(file, commands) }; UpdateImage(file, commands); // First, must update the map if (uidMapper != null && uidMapper.Dirty) { UpdateUidMap(destStudy, uidMapper); } if (belongsToAnotherStudy) { SopInstanceImporterContext importContext = new SopInstanceImporterContext(_contextID, file.SourceApplicationEntityTitle, destStudy.ServerPartition); SopInstanceImporter importer = new SopInstanceImporter(importContext); DicomProcessingResult result = importer.Import(file); if (!result.Successful) { throw new ApplicationException(result.ErrorMessage); } } } return(preProcessingResult); }
/// <summary> /// Imports the specified <see cref="DicomMessageBase"/> object into the system. /// The object will be inserted into the <see cref="WorkQueue"/> for processing and /// if it's a duplicate, proper checks will be done and depending on the policy, it will be /// ignored, rejected or inserted into the <see cref="StudyIntegrityQueue"/> for manual intervention. /// </summary> /// <param name="message">The DICOM object to be imported.</param> /// <returns>An instance of <see cref="DicomProcessingResult"/> that describes the result of the processing.</returns> /// <exception cref="DicomDataException">Thrown when the DICOM object contains invalid data</exception> public DicomProcessingResult Import(DicomMessageBase message) { Platform.CheckForNullReference(message, "message"); String studyInstanceUid = message.DataSet[DicomTags.StudyInstanceUid].GetString(0, string.Empty); String seriesInstanceUid = message.DataSet[DicomTags.SeriesInstanceUid].GetString(0, string.Empty); String sopInstanceUid = message.DataSet[DicomTags.SopInstanceUid].GetString(0, string.Empty); String accessionNumber = message.DataSet[DicomTags.AccessionNumber].GetString(0, string.Empty); String patientsName = message.DataSet[DicomTags.PatientsName].GetString(0, string.Empty); DicomFile file = null; // Scrub the name for invalid characters. string newName = XmlUtils.XmlCharacterScrub(patientsName); if (!newName.Equals(patientsName)) { message.DataSet[DicomTags.PatientsName].SetStringValue(newName); } var result = new DicomProcessingResult { Successful = true, StudyInstanceUid = studyInstanceUid, SeriesInstanceUid = seriesInstanceUid, SopInstanceUid = sopInstanceUid, AccessionNumber = accessionNumber }; try { Validate(message); } catch (DicomDataException e) { result.SetError(DicomStatuses.ProcessingFailure, e.Message); return(result); } // Use the command processor for rollback capabilities. using (var commandProcessor = new ServerCommandProcessor(String.Format("Processing Sop Instance {0}", sopInstanceUid))) { try { string failureMessage; StudyStorageLocation studyLocation = GetWritableOnlineStorage(message); // GetWritableOnlineStorage should throw an exception if the study location cannot be found. Platform.CheckForNullReference(studyLocation, "studyLocation"); if (!studyLocation.QueueStudyStateEnum.Equals(QueueStudyStateEnum.Idle) && (!studyLocation.QueueStudyStateEnum.Equals(QueueStudyStateEnum.ProcessingScheduled))) { failureMessage = String.Format("Study {0} on partition {1} is being processed: {2}, can't accept new images.", studyLocation.StudyInstanceUid, _context.Partition.Description, studyLocation.QueueStudyStateEnum.Description); result.SetError(DicomStatuses.StorageStorageOutOfResources, failureMessage); return(result); } if (studyLocation.StudyStatusEnum.Equals(StudyStatusEnum.OnlineLossy)) { if (studyLocation.IsLatestArchiveLossless) { result.DicomStatus = DicomStatuses.StorageStorageOutOfResources; failureMessage = String.Format("Study {0} on partition {1} can't accept new images due to lossy compression of the study. Restoring study.", studyLocation.StudyInstanceUid, _context.Partition.Description); Platform.Log(LogLevel.Error, failureMessage); if (ServerHelper.InsertRestoreRequest(studyLocation) == null) { Platform.Log(LogLevel.Warn, "Unable to insert Restore Request for Study"); } result.SetError(DicomStatuses.StorageStorageOutOfResources, failureMessage); result.RestoreRequested = true; return(result); } } String path = studyLocation.FilesystemPath; const string extension = null; String finalDest = studyLocation.GetSopInstancePath(seriesInstanceUid, sopInstanceUid); file = ConvertToDicomFile(message, finalDest, _context.SourceAE); if (HasUnprocessedCopy(studyLocation.Key, seriesInstanceUid, sopInstanceUid)) { var accept = false; // This is a special case: #10569 // Allow user to revive an orphaned study by reprocessing the files found in the filesystem if (File.Exists(finalDest)) { accept = DuplicatePolicy.IsParitionDuplicatePolicyOverridden(studyLocation); } if (!accept) { failureMessage = string.Format("Another copy of the SOP Instance was received but has not been processed: {0}", sopInstanceUid); result.SetError(DicomStatuses.DuplicateSOPInstance, failureMessage); return(result); } } if (File.Exists(finalDest)) { result = HandleDuplicate(sopInstanceUid, studyLocation, commandProcessor, file); if (!result.Successful) { return(result); } } else { HandleNonDuplicate(seriesInstanceUid, sopInstanceUid, studyLocation, commandProcessor, file, path, false, extension); } if (commandProcessor.Execute()) { result.DicomStatus = DicomStatuses.Success; } else { failureMessage = String.Format("Failure processing message: {0}. Sending failure status.", commandProcessor.FailureReason); result.SetError(DicomStatuses.ProcessingFailure, failureMessage); // processor already rolled back return(result); } } catch (NoWritableFilesystemException) { String failureMessage = String.Format("Unable to process image, no writable filesystem found for Study UID {0}.", sopInstanceUid); commandProcessor.Rollback(); result.SetError(DicomStatuses.StorageStorageOutOfResources, failureMessage); } catch (StudyIsNearlineException e) { String failureMessage = e.RestoreRequested ? String.Format("{0}. Restore has been requested.", e.Message) : e.Message; Platform.Log(LogLevel.Error, failureMessage); commandProcessor.Rollback(); result.SetError(DicomStatuses.ProcessingFailure, failureMessage); } catch (FilesystemNotWritableException) { commandProcessor.Rollback(); string folder; if (!FilesystemMonitor.Instance.GetWriteableIncomingFolder(_context.Partition, out folder)) { String failureMessage = String.Format("Unable to process image, study storage location is missing or not writeable: {0}.", sopInstanceUid); result.SetError(DicomStatuses.StorageStorageOutOfResources, failureMessage); return(result); } if (file == null) { file = ConvertToDicomFile(message, string.Empty, _context.SourceAE); } if (!SaveToFolder(folder, sopInstanceUid, studyInstanceUid, file)) { String failureMessage = String.Format("Study storage location not writeable and no writeable incoming folder: {0}.", sopInstanceUid); result.SetError(DicomStatuses.StorageStorageOutOfResources, failureMessage); return(result); } Platform.Log(LogLevel.Info, "Saved existing SOP without writeable storage location to {0} folder: {1}", FilesystemMonitor.ImportDirectorySuffix, sopInstanceUid); result.DicomStatus = DicomStatuses.Success; return(result); } catch (Exception e) { Platform.Log(LogLevel.Error, e, "Unexpected exception when {0}. Rolling back operation.", commandProcessor.Description); commandProcessor.Rollback(); result.SetError(result.DicomStatus ?? DicomStatuses.ProcessingFailure, e.Message); } } return(result); }