/// <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> /// 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)); }
/// <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 static void SaveDuplicateFile(SopInstanceProcessorContext context, string sopInstanceUid, string sourceFilename) { String path = Path.Combine(context.StudyLocation.FilesystemPath, context.StudyLocation.PartitionFolder); context.CommandProcessor.AddCommand(new CreateDirectoryCommand(path)); path = Path.Combine(path, ServerPlatform.ReconcileStorageFolder); context.CommandProcessor.AddCommand(new CreateDirectoryCommand(path)); path = Path.Combine(path, context.Group /* the AE title + timestamp */); context.CommandProcessor.AddCommand(new CreateDirectoryCommand(path)); path = Path.Combine(path, context.StudyLocation.StudyInstanceUid); context.CommandProcessor.AddCommand(new CreateDirectoryCommand(path)); path = Path.Combine(path, sopInstanceUid); path += "." + ServerPlatform.DuplicateFileExtension; context.CommandProcessor.AddCommand(new RenameFileCommand(sourceFilename, path, true)); Platform.Log(ServerPlatform.InstanceLogLevel, "Duplicate ==> {0}", path); }
private static void SaveDuplicate(SopInstanceProcessorContext context, DicomFile file) { String sopUid = file.DataSet[DicomTags.SopInstanceUid].ToString(); String path = Path.Combine(context.StudyLocation.FilesystemPath, context.StudyLocation.PartitionFolder); context.CommandProcessor.AddCommand(new CreateDirectoryCommand(path)); path = Path.Combine(path, ServerPlatform.ReconcileStorageFolder); context.CommandProcessor.AddCommand(new CreateDirectoryCommand(path)); path = Path.Combine(path, context.Group /* the AE title + timestamp */); context.CommandProcessor.AddCommand(new CreateDirectoryCommand(path)); path = Path.Combine(path, context.StudyLocation.StudyInstanceUid); context.CommandProcessor.AddCommand(new CreateDirectoryCommand(path)); path = Path.Combine(path, sopUid); path += "." + ServerPlatform.DuplicateFileExtension; context.CommandProcessor.AddCommand(new SaveDicomFileCommand(path, file, true)); Platform.Log(ServerPlatform.InstanceLogLevel, "Duplicate ==> {0}", path); }
/// <summary> /// Schedules a reconciliation for the specified <see cref="DicomFile"/> /// </summary> /// <param name="context"></param> /// <param name="file"></param> /// <param name="uid"></param> private static void ScheduleReconcile(SopInstanceProcessorContext context, DicomFile file, WorkQueueUid uid) { ImageReconciler reconciler = new ImageReconciler(context); reconciler.ScheduleReconcile(file, StudyIntegrityReasonEnum.InconsistentData, uid); }
/// <summary> /// Process a specific DICOM file related to a <see cref="WorkQueue"/> request. /// </summary> /// <remarks> /// <para> /// On success and if <see cref="uid"/> is set, the <see cref="WorkQueueUid"/> field is deleted. /// </para> /// </remarks> /// <param name="stream">The <see cref="StudyXml"/> file to update with information from the file.</param> /// <param name="group">A group the sop is associated with.</param> /// <param name="file">The file to process.</param> /// <param name="compare">Flag to compare the demographics of <see cref="file"/> with the demographics in the database</param> /// <param name="retry">Flag telling if the item should be retried on failure. Note that if the item is a duplicate, the WorkQueueUid item is not failed. </param> /// <param name="uid">An optional WorkQueueUid associated with the entry, that will be deleted upon success or failed on failure.</param> /// <param name="deleteFile">An option file to delete as part of the process</param> /// <param name="sopType">Flag telling if the SOP is a new or updated SOP</param> /// <exception cref="Exception"/> /// <exception cref="DicomDataException"/> public ProcessingResult ProcessFile(string group, DicomFile file, StudyXml stream, bool compare, bool retry, WorkQueueUid uid, string deleteFile, SopInstanceProcessorSopType sopType) { Platform.CheckForNullReference(file, "file"); try { CheckDataLength(file); _instanceStats.ProcessTime.Start(); ProcessingResult result = new ProcessingResult { Status = ProcessingStatus.Success }; using (ServerCommandProcessor processor = new ServerCommandProcessor("Process File")) { SopInstanceProcessorContext processingContext = new SopInstanceProcessorContext(processor, _context.StorageLocation, group); if (EnforceNameRules) { _patientNameRules.Apply(file); } if (compare && ShouldReconcile(_context.StorageLocation, file)) { ScheduleReconcile(processingContext, file, uid); result.Status = ProcessingStatus.Reconciled; } else { InsertInstance(file, stream, uid, deleteFile,sopType); result.Status = ProcessingStatus.Success; } } _instanceStats.ProcessTime.End(); if (_context.SopProcessedRulesEngine.Statistics.LoadTime.IsSet) _instanceStats.SopRulesLoadTime.Add(_context.SopProcessedRulesEngine.Statistics.LoadTime); if (_context.SopProcessedRulesEngine.Statistics.ExecutionTime.IsSet) _instanceStats.SopEngineExecutionTime.Add(_context.SopProcessedRulesEngine.Statistics.ExecutionTime); _context.SopProcessedRulesEngine.Statistics.Reset(); //TODO: Should throw exception if result is failed? return result; } catch (Exception e) { // If its a duplicate, ignore the exception, and just throw it if (deleteFile != null && (e is InstanceAlreadyExistsException || e.InnerException is InstanceAlreadyExistsException)) throw; if (uid != null) FailUid(uid, retry); throw; } }
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; }
/// <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)) { Platform.Log(LogLevel.Warn, "Duplicate instance received for study {0} on Partition {1}. Duplicate policy overridden. 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; }
/// <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)) { Platform.Log(LogLevel.Warn, "Duplicate instance received for study {0} on Partition {1}. Duplicate policy overridden. 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); }
/// <summary> /// Creates an instance of <see cref="ImageReconciler"/> /// </summary> /// <param name="context"></param> public ImageReconciler(SopInstanceProcessorContext context) { _context = context; }