/// <summary> /// Process the duplicate with the supplied <see cref="DuplicateProcessingEnum"/> /// </summary> /// <param name="context">The processing context</param> /// <param name="file">The file</param> /// <param name="data">The data</param> /// <param name="duplicate">How the processor should handle the duplicate</param> public static void ProcessStoredDuplicate(SopInstanceProcessorContext context, DicomFile file, StudyProcessWorkQueueData data, DuplicateProcessingEnum duplicate) { SaveDuplicate(context, file); var uidData = new WorkQueueUidData { Extension = ServerPlatform.DuplicateFileExtension, GroupId = context.Group, DuplicateProcessing = duplicate }; if (context.Request != null) { uidData.OperationToken = context.Request.OperationToken; } context.CommandProcessor.AddCommand( new UpdateWorkQueueCommand(file, context.StudyLocation, true, data, uidData, context.Request)); }
/// <summary> /// Process the duplicate with the supplied <see cref="DuplicateProcessingEnum"/> /// </summary> /// <param name="context">The processing context</param> /// <param name="message">A subset of the message stored in <paramref name="sourceFilename"/></param> /// <param name="sourceFilename">The location of the filename that is a duplicate</param> /// <param name="data">The data</param> /// <param name="duplicate">How the processor should handle the duplicate</param> public static void ProcessStoredDuplicateFile(SopInstanceProcessorContext context, string sourceFilename, DicomMessageBase message, StudyProcessWorkQueueData data, DuplicateProcessingEnum duplicate) { SaveDuplicateFile(context, message.DataSet[DicomTags.SopInstanceUid].ToString(), sourceFilename); var uidData = new WorkQueueUidData { Extension = ServerPlatform.DuplicateFileExtension, GroupId = context.Group, DuplicateProcessing = duplicate }; if (context.Request != null) { uidData.OperationToken = context.Request.OperationToken; } context.CommandProcessor.AddCommand( new UpdateWorkQueueCommand(message, context.StudyLocation, true, data, uidData, context.Request)); }
private DicomProcessingResult HandleDuplicateFile(string sopInstanceUid, StudyStorageLocation studyLocation, ServerCommandProcessor commandProcessor, DicomMessageBase message, string sourceFilename, StudyProcessWorkQueueData data) { 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); } var sopProcessingContext = new SopInstanceProcessorContext(commandProcessor, studyLocation, _context.ContextID, _context.Request) { DuplicateProcessing = _context.DuplicateProcessing }; DicomProcessingResult result = DuplicateSopProcessorHelper.Process(sopProcessingContext, message, data, sourceFilename); return(result); }
private void HandleNonDuplicateFile(string seriesInstanceUid, string sopInstanceUid, StudyStorageLocation studyLocation, ServerCommandProcessor commandProcessor, DicomMessageBase message, string sourcePath, string path, bool dupImage, StudyProcessWorkQueueData data) { commandProcessor.AddCommand(new CreateDirectoryCommand(path)); path = Path.Combine(path, studyLocation.PartitionFolder); commandProcessor.AddCommand(new CreateDirectoryCommand(path)); path = Path.Combine(path, studyLocation.StudyFolder); commandProcessor.AddCommand(new CreateDirectoryCommand(path)); path = Path.Combine(path, studyLocation.StudyInstanceUid); commandProcessor.AddCommand(new CreateDirectoryCommand(path)); path = Path.Combine(path, seriesInstanceUid); commandProcessor.AddCommand(new CreateDirectoryCommand(path)); path = Path.Combine(path, sopInstanceUid); path += ServerPlatform.DicomFileExtension; commandProcessor.AddCommand(new RenameFileCommand(sourcePath, path, true)); WorkQueueUidData uidData = null; if (_context.Request != null && !string.IsNullOrEmpty(_context.Request.OperationToken)) { uidData = new WorkQueueUidData { OperationToken = _context.Request.OperationToken }; } commandProcessor.AddCommand( new UpdateWorkQueueCommand(message, studyLocation, dupImage, data, uidData, _context.Request)); #region SPECIAL CODE FOR TESTING if (Diagnostics.Settings.SimulateFileCorruption) { commandProcessor.AddCommand(new CorruptDicomFileCommand(path)); } #endregion }
/// <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; String seriesInstanceUid; String sopInstanceUid; String accessionNumber; String patientsName; LoadMessageUids(message, out studyInstanceUid, out seriesInstanceUid, out sopInstanceUid, out accessionNumber, out patientsName); 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; 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); } } var data = new StudyProcessWorkQueueData { ReceivingAeTitle = _context.AlternateAe == null ? _context.Partition.AeTitle : _context.AlternateAe.AeTitle }; if (File.Exists(finalDest)) { result = HandleDuplicate(sopInstanceUid, studyLocation, commandProcessor, file, data); if (!result.Successful) { return(result); } } else { HandleNonDuplicate(seriesInstanceUid, sopInstanceUid, studyLocation, commandProcessor, file, path, false, data); } 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); }
/// <summary> /// Create Duplicate SIQ Entry /// </summary> /// <param name="file"></param> /// <param name="location"></param> /// <param name="sourcePath"></param> /// <param name="queue"></param> /// <param name="uid"></param> /// <param name="data"></param> public static void CreateDuplicateSIQEntry(DicomFile file, StudyStorageLocation location, string sourcePath, WorkQueue queue, WorkQueueUid uid, StudyProcessWorkQueueData data) { Platform.Log(LogLevel.Info, "Creating Work Queue Entry for duplicate..."); String uidGroup = queue.GroupID ?? queue.GetKey().Key.ToString(); using (var commandProcessor = new ServerCommandProcessor("Insert Work Queue entry for duplicate")) { commandProcessor.AddCommand(new FileDeleteCommand(sourcePath, true)); var sopProcessingContext = new SopInstanceProcessorContext(commandProcessor, location, uidGroup); DicomProcessingResult result = Process(sopProcessingContext, file, data); if (!result.Successful) { FailUid(uid, true); return; } commandProcessor.AddCommand(new DeleteWorkQueueUidCommand(uid)); if (!commandProcessor.Execute()) { Platform.Log(LogLevel.Error, "Unexpected error when creating duplicate study integrity queue entry: {0}", commandProcessor.FailureReason); FailUid(uid, true); } } }
/// <summary> /// Inserts the duplicate DICOM file into the <see cref="WorkQueue"/> for processing (if applicable). /// </summary> /// <param name="context">The processing context.</param> /// <param name="file">The duplicate DICOM file being processed.</param> /// <param name="data">Extra data to insert for the WorkQueue item.</param> /// <param name="sourceFilename">Optional source filename already saved to disk to import.</param> /// <returns>A <see cref="DicomProcessingResult"/> that contains the result of the processing.</returns> /// <remarks> /// This method inserts a <see cref="CommandBase"/> into <paramref name="context.CommandProcessor"/>. /// The outcome of the operation depends on the <see cref="DuplicateSopPolicyEnum"/> of the <see cref="ServerPartition"/>. /// If it is set to <see cref="DuplicateSopPolicyEnum.CompareDuplicates"/>, the duplicate file will be /// inserted into the <see cref="WorkQueue"/> for processing. /// </remarks> public static DicomProcessingResult Process(SopInstanceProcessorContext context, DicomMessageBase file, StudyProcessWorkQueueData data, string sourceFilename = null) { Platform.CheckForNullReference(file, "file"); Platform.CheckForNullReference(context, "context"); Platform.CheckMemberIsSet(context.Group, "parameters.Group"); Platform.CheckMemberIsSet(context.CommandProcessor, "parameters.CommandProcessor"); Platform.CheckMemberIsSet(context.StudyLocation, "parameters.StudyLocation"); if (string.IsNullOrEmpty(sourceFilename)) { Platform.CheckForNullReference(file as DicomFile, "file"); } var result = new DicomProcessingResult { DicomStatus = DicomStatuses.Success, Successful = true, StudyInstanceUid = file.DataSet[DicomTags.StudyInstanceUid].GetString(0, string.Empty), SeriesInstanceUid = file.DataSet[DicomTags.SeriesInstanceUid].GetString(0, string.Empty), SopInstanceUid = file.DataSet[DicomTags.SopInstanceUid].GetString(0, string.Empty), SopClassUid = file.DataSet[DicomTags.SopClassUid].GetString(0, string.Empty), AccessionNumber = file.DataSet[DicomTags.AccessionNumber].GetString(0, string.Empty) }; string failureMessage; if (context.DuplicateProcessing.HasValue && context.DuplicateProcessing.Value.Equals(DuplicateProcessingEnum.Reject)) { failureMessage = String.Format("Duplicate SOP Instance received, rejecting {0}", result.SopInstanceUid); Platform.Log(LogLevel.Info, failureMessage); result.SetError(DicomStatuses.DuplicateSOPInstance, failureMessage); return(result); } if (SopClassIsReport(result.SopClassUid) && context.StudyLocation.ServerPartition.AcceptLatestReport) { Platform.Log(LogLevel.Info, "Duplicate Report received, overwriting {0}", result.SopInstanceUid); if (string.IsNullOrEmpty(sourceFilename)) { ProcessStoredDuplicate(context, file as DicomFile, data, DuplicateProcessingEnum.OverwriteReport); } else { ProcessStoredDuplicateFile(context, sourceFilename, file, data, DuplicateProcessingEnum.OverwriteReport); } return(result); } if (DuplicatePolicy.IsParitionDuplicatePolicyOverridden(context.StudyLocation)) { // Note: this is a special case where we need to temporarily override the duplicate policy for a particular study // so that SIQ entry can be processed (#10569). This should only happen once in a blue moon. Platform.Log(LogLevel.Warn, "Duplicate instance received for study {0} on Partition {1}. Duplicate policy overridden in app config. Will overwrite {2}", result.StudyInstanceUid, context.StudyLocation.ServerPartition.AeTitle, result.SopInstanceUid); if (string.IsNullOrEmpty(sourceFilename)) { ProcessStoredDuplicate(context, file as DicomFile, data, DuplicateProcessingEnum.OverwriteSop); } else { ProcessStoredDuplicateFile(context, sourceFilename, file, data, DuplicateProcessingEnum.OverwriteSop); } return(result); } if (context.DuplicateProcessing.HasValue) { Platform.Log(LogLevel.Info, context.DuplicateProcessing.Value.Equals(DuplicateProcessingEnum.Compare) ? "Duplicate SOP Instance received, comparing {0}" : "Duplicate SOP Instance received, overwriting {0}", result.SopInstanceUid); if (string.IsNullOrEmpty(sourceFilename)) { ProcessStoredDuplicate(context, file as DicomFile, data, context.DuplicateProcessing.Value); } else { ProcessStoredDuplicateFile(context, sourceFilename, file, data, context.DuplicateProcessing.Value); } return(result); } if (context.StudyLocation.ServerPartition.DuplicateSopPolicyEnum.Equals(DuplicateSopPolicyEnum.AcceptLatest)) { Platform.Log(LogLevel.Info, "Duplicate SOP Instance received, overwriting {0}", result.SopInstanceUid); if (string.IsNullOrEmpty(sourceFilename)) { ProcessStoredDuplicate(context, file as DicomFile, data, DuplicateProcessingEnum.OverwriteSopAndUpdateDatabase); } else { ProcessStoredDuplicateFile(context, sourceFilename, file, data, DuplicateProcessingEnum.OverwriteSopAndUpdateDatabase); } return(result); } if (context.StudyLocation.ServerPartition.DuplicateSopPolicyEnum.Equals(DuplicateSopPolicyEnum.SendSuccess)) { Platform.Log(LogLevel.Info, "Duplicate SOP Instance received, sending success response {0}", result.SopInstanceUid); return(result); } if (context.StudyLocation.ServerPartition.DuplicateSopPolicyEnum.Equals(DuplicateSopPolicyEnum.RejectDuplicates)) { failureMessage = String.Format("Duplicate SOP Instance received, rejecting {0}", result.SopInstanceUid); Platform.Log(LogLevel.Info, failureMessage); result.SetError(DicomStatuses.DuplicateSOPInstance, failureMessage); return(result); } if (context.StudyLocation.ServerPartition.DuplicateSopPolicyEnum.Equals(DuplicateSopPolicyEnum.CompareDuplicates)) { if (string.IsNullOrEmpty(sourceFilename)) { ProcessStoredDuplicate(context, file as DicomFile, data, DuplicateProcessingEnum.Compare); } else { ProcessStoredDuplicateFile(context, sourceFilename, file, data, DuplicateProcessingEnum.Compare); } } else { failureMessage = String.Format("Duplicate SOP Instance received. Unsupported duplicate policy {0}.", context.StudyLocation.ServerPartition.DuplicateSopPolicyEnum); result.SetError(DicomStatuses.DuplicateSOPInstance, failureMessage); return(result); } return(result); }