private void InsertInstance(DicomFile file, StudyXml stream, WorkQueueUid uid, string deleteFile, SopInstanceProcessorSopType sopType) { using (var processor = new ServerCommandProcessor("Processing WorkQueue DICOM file")) { EventsHelper.Fire(OnInsertingSop, this, new SopInsertingEventArgs {Processor = processor }); InsertInstanceCommand insertInstanceCommand = null; InsertStudyXmlCommand insertStudyXmlCommand = null; String patientsName = file.DataSet[DicomTags.PatientsName].GetString(0, String.Empty); _modality = file.DataSet[DicomTags.Modality].GetString(0, String.Empty); if (_context.UpdateCommands.Count > 0) { foreach (BaseImageLevelUpdateCommand command in _context.UpdateCommands) { command.File = file; processor.AddCommand(command); } } try { // Create a context for applying actions from the rules engine ServerActionContext context = new ServerActionContext(file, _context.StorageLocation.FilesystemKey, _context.Partition, _context.StorageLocation.Key); context.CommandProcessor = processor; _context.SopCompressionRulesEngine.Execute(context); String seriesUid = file.DataSet[DicomTags.SeriesInstanceUid].GetString(0, String.Empty); String sopUid = file.DataSet[DicomTags.SopInstanceUid].GetString(0, String.Empty); String finalDest = _context.StorageLocation.GetSopInstancePath(seriesUid, sopUid); if (_context.UpdateCommands.Count > 0) { processor.AddCommand(new SaveDicomFileCommand(_context.StorageLocation, file, file.Filename != finalDest)); } else if (file.Filename != finalDest || processor.CommandCount > 0) { // Have to be careful here about failure on exists vs. not failing on exists // because of the different use cases of the importer. // save the file in the study folder, or if its been compressed processor.AddCommand(new SaveDicomFileCommand(finalDest, file, file.Filename != finalDest)); } // Update the StudyStream object insertStudyXmlCommand = new InsertStudyXmlCommand(file, stream, _context.StorageLocation); processor.AddCommand(insertStudyXmlCommand); // Have the rules applied during the command processor, and add the objects. processor.AddCommand(new ApplySopRulesCommand(context,_context.SopProcessedRulesEngine)); // If specified, delete the file if (deleteFile != null) processor.AddCommand(new FileDeleteCommand(deleteFile, true)); // Insert into the database, but only if its not a duplicate so the counts don't get off insertInstanceCommand = new InsertInstanceCommand(file, _context.StorageLocation); processor.AddCommand(insertInstanceCommand); // Do a check if the StudyStatus value should be changed in the StorageLocation. This // should only occur if the object has been compressed in the previous steps. processor.AddCommand(new UpdateStudyStatusCommand(_context.StorageLocation, file)); if (uid!=null) processor.AddCommand(new DeleteWorkQueueUidCommand(uid)); // Do the actual processing if (!processor.Execute()) { Platform.Log(LogLevel.Error, "Failure processing command {0} for SOP: {1}", processor.Description, file.MediaStorageSopInstanceUid); Platform.Log(LogLevel.Error, "File that failed processing: {0}", file.Filename); throw new ApplicationException("Unexpected failure (" + processor.FailureReason + ") executing command for SOP: " + file.MediaStorageSopInstanceUid, processor.FailureException); } Platform.Log(ServerPlatform.InstanceLogLevel, "Processed SOP: {0} for Patient {1}", file.MediaStorageSopInstanceUid, patientsName); // Fire NewSopEventArgs or UpdateSopEventArgs Event // Know its a duplicate if we have to delete the duplicate object if (sopType == SopInstanceProcessorSopType.NewSop) EventManager.FireEvent(this, new NewSopEventArgs { File = file, ServerPartitionEntry = _context.Partition, WorkQueueUidEntry = uid, WorkQueueEntry = _context.WorkQueueEntry, FileLength = InstanceStats.FileSize }); else if (sopType == SopInstanceProcessorSopType.UpdatedSop) EventManager.FireEvent(this, new UpdateSopEventArgs {File = file,ServerPartitionEntry = _context.Partition,WorkQueueUidEntry = uid, WorkQueueEntry = _context.WorkQueueEntry, FileLength = InstanceStats.FileSize}); } catch (Exception e) { Platform.Log(LogLevel.Error, e, "Unexpected exception when {0}. Rolling back operation.", processor.Description); processor.Rollback(); if (sopType == SopInstanceProcessorSopType.NewSop) EventManager.FireEvent(this, new FailedNewSopEventArgs { File = file, ServerPartitionEntry = _context.Partition, WorkQueueUidEntry = uid, WorkQueueEntry = _context.WorkQueueEntry, FileLength = InstanceStats.FileSize, FailureMessage = e.Message }); else EventManager.FireEvent(this, new FailedUpdateSopEventArgs { File = file, ServerPartitionEntry = _context.Partition, WorkQueueUidEntry = uid, WorkQueueEntry = _context.WorkQueueEntry, FileLength = InstanceStats.FileSize, FailureMessage = e.Message }); throw new ApplicationException("Unexpected exception when processing file.", e); } finally { if (insertInstanceCommand != null && insertInstanceCommand.Statistics.IsSet) _instanceStats.InsertDBTime.Add(insertInstanceCommand.Statistics); if (insertStudyXmlCommand != null && insertStudyXmlCommand.Statistics.IsSet) _instanceStats.InsertStreamTime.Add(insertStudyXmlCommand.Statistics); } } }
/// <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; } }