/// <summary>
        /// Process a <see cref="WorkQueue"/> item of type AutoRoute.
        /// </summary>
        protected override void ProcessItem(Model.WorkQueue item)
        {
            if (WorkQueueItem.ScheduledTime >= WorkQueueItem.ExpirationTime && !HasPendingItems)
            {
                Platform.Log(LogLevel.Debug, "Removing Idle {0} entry : {1}", item.WorkQueueTypeEnum, item.GetKey().Key);
                base.PostProcessing(item, WorkQueueProcessorStatus.Complete, WorkQueueProcessorDatabaseUpdate.None);
                return;
            }

            if (!HasPendingItems)
            {
                // nothing to process, change to idle state
                PostProcessing(item, WorkQueueProcessorStatus.Idle, WorkQueueProcessorDatabaseUpdate.None);
                return;
            }

            Platform.Log(LogLevel.Info,
						 "Moving study {0} for Patient {1} (PatientId:{2} A#:{3}) on Partition {4} to {5}...",
						 Study.StudyInstanceUid, Study.PatientsName, Study.PatientId, Study.AccessionNumber,
						 ServerPartition.Description, DestinationDevice.AeTitle);

            // Load remote device information from the database.
            Device device = DestinationDevice;
            if (device == null)
            {
                item.FailureDescription = String.Format("Unknown auto-route destination \"{0}\"", item.DeviceKey);
                Platform.Log(LogLevel.Error, item.FailureDescription);

                PostProcessingFailure(item, WorkQueueProcessorFailureType.Fatal); // Fatal Error
                return;
            }

            if (device.Dhcp && device.IpAddress.Length == 0)
            {
                item.FailureDescription = String.Format("Auto-route destination is a DHCP device with no known IP address: \"{0}\"", device.AeTitle);
                Platform.Log(LogLevel.Error,
                             item.FailureDescription);

                PostProcessingFailure(item, WorkQueueProcessorFailureType.Fatal); // Fatal error
                return;
            }

            
            // Now setup the StorageSCU component
            int sendCounter = 0;
			using (ImageServerStorageScu scu = new ImageServerStorageScu(ServerPartition, device))
			{
				using (ServerExecutionContext context = new ServerExecutionContext())
					// set the preferred syntax lists
					scu.LoadPreferredSyntaxes(context.ReadContext);

				// Load the Instances to Send into the SCU component
				scu.AddStorageInstanceList(InstanceList);

				// Set an event to be called when each image is transferred
				scu.ImageStoreCompleted += delegate(Object sender, StorageInstance instance)
				                           	{
				                           		if (instance.SendStatus.Status == DicomState.Success
				                           		    || instance.SendStatus.Status == DicomState.Warning
				                           		    || instance.SendStatus.Equals(DicomStatuses.SOPClassNotSupported))
				                           		{
				                           			sendCounter++;
				                           			OnInstanceSent(instance);
				                           		}

				                           		if (instance.SendStatus.Status == DicomState.Failure)
				                           		{
				                           			scu.FailureDescription = instance.SendStatus.Description;
				                           			if (false == String.IsNullOrEmpty(instance.ExtendedFailureDescription))
				                           			{
				                           				scu.FailureDescription = String.Format("{0} [{1}]", scu.FailureDescription,
				                           				                                       instance.ExtendedFailureDescription);
				                           			}
				                           		}


				                           		if (CancelPending && !(this is WebMoveStudyItemProcessor) && !scu.Canceled)
				                           		{
				                           			Platform.Log(LogLevel.Info, "Auto-route canceled due to shutdown for study: {0}",
				                           			             StorageLocation.StudyInstanceUid);
				                           			item.FailureDescription = "Operation was canceled due to server shutdown request.";
				                           			scu.Cancel();
				                           		}
				                           	};

				try
				{
					// Block until send is complete
					scu.Send();

					// Join for the thread to exit
					scu.Join();
				}
				catch (Exception ex)
				{
					Platform.Log(LogLevel.Error, ex, "Error occurs while sending images to {0} : {1}", device.AeTitle, ex.Message);
				}
				finally
				{
					if (scu.FailureDescription.Length > 0)
					{
						item.FailureDescription = scu.FailureDescription;
						scu.Status = ScuOperationStatus.Failed;
					}

					// Reset the WorkQueue entry status
					if ((InstanceList.Count > 0 && sendCounter != InstanceList.Count) // not all sop were sent
					    || scu.Status == ScuOperationStatus.Failed
					    || scu.Status == ScuOperationStatus.ConnectFailed)
					{
						PostProcessingFailure(item, WorkQueueProcessorFailureType.NonFatal); // failures occurred}
					}
					else
					{
						OnComplete();
					}
				}
			}
        }
예제 #2
0
        /// <summary>
        /// Main routine for processing C-MOVE-RQ messages.  Called by the <see cref="DicomScp{DicomScpParameters}"/> component.
        /// </summary>
        /// <param name="server"></param>
        /// <param name="association"></param>
        /// <param name="presentationId"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public override bool OnReceiveRequest(DicomServer server, ServerAssociationParameters association, byte presentationId, DicomMessage message)
        {
            bool   finalResponseSent = false;
            string errorComment;

            try
            {
                // Check for a Cancel message, and cancel the SCU.
                if (message.CommandField == DicomCommandField.CCancelRequest)
                {
                    if (_theScu != null)
                    {
                        _theScu.Cancel();
                    }
                    return(true);
                }

                // Get the level of the Move.
                String level = message.DataSet[DicomTags.QueryRetrieveLevel].GetString(0, "");

                // Trim the remote AE, see extra spaces at the end before which has caused problems
                string remoteAe = message.MoveDestination.Trim();

                // Open a DB Connection
                using (IReadContext read = _store.OpenReadContext())
                {
                    // Load remote device information fromt he database.
                    Device device = LoadRemoteHost(read, Partition, remoteAe);
                    if (device == null)
                    {
                        errorComment = string.Format(
                            "Unknown move destination \"{0}\", failing C-MOVE-RQ from {1} to {2}",
                            remoteAe, association.CallingAE, association.CalledAE);
                        Platform.Log(LogLevel.Error, errorComment);
                        server.SendCMoveResponse(presentationId, message.MessageId, new DicomMessage(),
                                                 DicomStatuses.QueryRetrieveMoveDestinationUnknown, errorComment);
                        finalResponseSent = true;
                        return(true);
                    }

                    // If the remote node is a DHCP node, use its IP address from the connection information, else
                    // use what is configured.  Always use the configured port.
                    if (device.Dhcp)
                    {
                        device.IpAddress = association.RemoteEndPoint.Address.ToString();
                    }

                    // Now setup the StorageSCU component
                    _theScu = new ImageServerStorageScu(Partition, device,
                                                        association.CallingAE, message.MessageId);


                    // Now create the list of SOPs to send
                    bool bOnline;

                    if (level.Equals("PATIENT"))
                    {
                        bOnline = GetSopListForPatient(read, message, out errorComment);
                    }
                    else if (level.Equals("STUDY"))
                    {
                        bOnline = GetSopListForStudy(message, out errorComment);
                    }
                    else if (level.Equals("SERIES"))
                    {
                        bOnline = GetSopListForSeries(read, message, out errorComment);
                    }
                    else if (level.Equals("IMAGE"))
                    {
                        bOnline = GetSopListForSop(message, out errorComment);
                    }
                    else
                    {
                        errorComment = string.Format("Unexpected Study Root Move Query/Retrieve level: {0}", level);
                        Platform.Log(LogLevel.Error, errorComment);

                        server.SendCMoveResponse(presentationId, message.MessageId, new DicomMessage(),
                                                 DicomStatuses.QueryRetrieveIdentifierDoesNotMatchSOPClass,
                                                 errorComment);
                        finalResponseSent = true;
                        return(true);
                    }

                    // Could not find an online/readable location for the requested objects to move.
                    // Note that if the C-MOVE-RQ included a list of study instance uids, and some
                    // were online and some offline, we don't fail now (ie, the check on the Count)
                    if (!bOnline && _theScu.StorageInstanceList.Count == 0)
                    {
                        Platform.Log(LogLevel.Error, "Unable to find online storage location for C-MOVE-RQ: {0}", errorComment);

                        server.SendCMoveResponse(presentationId, message.MessageId, new DicomMessage(),
                                                 DicomStatuses.QueryRetrieveUnableToPerformSuboperations,
                                                 string.IsNullOrEmpty(errorComment) ? string.Empty : errorComment);
                        finalResponseSent = true;
                        _theScu.Dispose();
                        _theScu = null;
                        return(true);
                    }

                    // No files were eligible for transfer, just send success and return
                    if (_theScu.StorageInstanceList.Count == 0)
                    {
                        server.SendCMoveResponse(presentationId, message.MessageId, new DicomMessage(),
                                                 DicomStatuses.Success,
                                                 0, 0, 0, 0);
                        finalResponseSent = true;
                        _theScu.Dispose();
                        _theScu = null;
                        return(true);
                    }

                    // set the preferred syntax lists
                    _theScu.LoadPreferredSyntaxes(read);

                    _theScu.ImageStoreCompleted += delegate(Object sender, StorageInstance instance)
                    {
                        var         scu = (StorageScu)sender;
                        var         msg = new DicomMessage();
                        DicomStatus status;

                        if (instance.SendStatus.Status == DicomState.Failure)
                        {
                            errorComment =
                                string.IsNullOrEmpty(instance.ExtendedFailureDescription)
                                                                    ? instance.SendStatus.ToString()
                                                                    : instance.ExtendedFailureDescription;
                        }

                        if (scu.RemainingSubOperations == 0)
                        {
                            foreach (StorageInstance sop in _theScu.StorageInstanceList)
                            {
                                if ((sop.SendStatus.Status != DicomState.Success) &&
                                    (sop.SendStatus.Status != DicomState.Warning))
                                {
                                    msg.DataSet[DicomTags.FailedSopInstanceUidList].AppendString(sop.SopInstanceUid);
                                }
                            }
                            if (scu.Status == ScuOperationStatus.Canceled)
                            {
                                status = DicomStatuses.Cancel;
                            }
                            else if (scu.Status == ScuOperationStatus.ConnectFailed)
                            {
                                status = DicomStatuses.QueryRetrieveMoveDestinationUnknown;
                            }
                            else if (scu.FailureSubOperations > 0)
                            {
                                status = DicomStatuses.QueryRetrieveSubOpsOneOrMoreFailures;
                            }
                            else if (!bOnline)
                            {
                                status = DicomStatuses.QueryRetrieveUnableToPerformSuboperations;
                            }
                            else
                            {
                                status = DicomStatuses.Success;
                            }
                        }
                        else
                        {
                            status = DicomStatuses.Pending;

                            if ((scu.RemainingSubOperations % 5) != 0)
                            {
                                return;
                            }
                            // Only send a RSP every 5 to reduce network load
                        }
                        server.SendCMoveResponse(presentationId, message.MessageId,
                                                 msg, status,
                                                 (ushort)scu.SuccessSubOperations,
                                                 (ushort)scu.RemainingSubOperations,
                                                 (ushort)scu.FailureSubOperations,
                                                 (ushort)scu.WarningSubOperations,
                                                 status == DicomStatuses.QueryRetrieveSubOpsOneOrMoreFailures
                                                                                         ? errorComment
                                                                                         : string.Empty);


                        if (scu.RemainingSubOperations == 0)
                        {
                            finalResponseSent = true;
                        }
                    };

                    _theScu.AssociationAccepted +=
                        (sender, parms) => AssociationAuditLogger.BeginInstancesTransferAuditLogger(
                            _theScu.StorageInstanceList,
                            parms);

                    _theScu.BeginSend(
                        delegate(IAsyncResult ar)
                    {
                        if (_theScu != null)
                        {
                            if (!finalResponseSent)
                            {
                                var msg = new DicomMessage();
                                server.SendCMoveResponse(presentationId, message.MessageId,
                                                         msg, DicomStatuses.QueryRetrieveSubOpsOneOrMoreFailures,
                                                         (ushort)_theScu.SuccessSubOperations,
                                                         0,
                                                         (ushort)(_theScu.FailureSubOperations + _theScu.RemainingSubOperations),
                                                         (ushort)_theScu.WarningSubOperations, errorComment);
                                finalResponseSent = true;
                            }

                            _theScu.EndSend(ar);
                            _theScu.Dispose();
                            _theScu = null;
                        }
                    },
                        _theScu);

                    return(true);
                } // end using()
            }
            catch (Exception e)
            {
                Platform.Log(LogLevel.Error, e, "Unexpected exception when processing C-MOVE-RQ");
                if (finalResponseSent == false)
                {
                    try
                    {
                        server.SendCMoveResponse(presentationId, message.MessageId, new DicomMessage(),
                                                 DicomStatuses.QueryRetrieveUnableToProcess, e.Message);
                        finalResponseSent = true;
                    }
                    catch (Exception x)
                    {
                        Platform.Log(LogLevel.Error, x,
                                     "Unable to send final C-MOVE-RSP message on association from {0} to {1}",
                                     association.CallingAE, association.CalledAE);
                        server.Abort();
                    }
                }
            }
            return(false);
        }
예제 #3
0
        /// <summary>
        /// Main routine for processing C-MOVE-RQ messages.  Called by the <see cref="DicomScp{DicomScpParameters}"/> component.
        /// </summary>
        /// <param name="server"></param>
        /// <param name="association"></param>
        /// <param name="presentationId"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public override bool OnReceiveRequest(DicomServer server, ServerAssociationParameters association, byte presentationId, DicomMessage message)
        {
            bool finalResponseSent = false;
            string errorComment;

            try
            {
                // Check for a Cancel message, and cancel the SCU.
                if (message.CommandField == DicomCommandField.CCancelRequest)
                {
                    if (_theScu != null)
                    {
                        _theScu.Cancel();
                    }
                    return true;
                }

                // Get the level of the Move.
                String level = message.DataSet[DicomTags.QueryRetrieveLevel].GetString(0, "");

                // Trim the remote AE, see extra spaces at the end before which has caused problems
                string remoteAe = message.MoveDestination.Trim();

                // Open a DB Connection
                using (IReadContext read = _store.OpenReadContext())
                {
                    // Load remote device information fromt he database.
                    Device device = LoadRemoteHost(read, Partition, remoteAe);
                    if (device == null)
                    {
                        errorComment = string.Format(
                                     "Unknown move destination \"{0}\", failing C-MOVE-RQ from {1} to {2}",
                                     remoteAe, association.CallingAE, association.CalledAE);
                        Platform.Log(LogLevel.Error, errorComment);
                        server.SendCMoveResponse(presentationId, message.MessageId, new DicomMessage(),
                                                 DicomStatuses.QueryRetrieveMoveDestinationUnknown, errorComment);
                        finalResponseSent = true;
                        return true;
                    }

                    // If the remote node is a DHCP node, use its IP address from the connection information, else
                    // use what is configured.  Always use the configured port.
                    if (device.Dhcp)
                        device.IpAddress = association.RemoteEndPoint.Address.ToString();

                    // Now setup the StorageSCU component
                    _theScu = new ImageServerStorageScu(Partition, device,
                                             association.CallingAE, message.MessageId);


                    // Now create the list of SOPs to send
                    bool bOnline;
                   
                    if (level.Equals("PATIENT"))
                    {
                        bOnline = GetSopListForPatient(read, message, out errorComment);
                    }
                    else if (level.Equals("STUDY"))
                    {
                        bOnline = GetSopListForStudy(message, out errorComment);
                    }
                    else if (level.Equals("SERIES"))
                    {
                        bOnline = GetSopListForSeries(read, message, out errorComment);
                    }
                    else if (level.Equals("IMAGE"))
                    {
                        bOnline = GetSopListForSop(message, out errorComment);
                    }
                    else
                    {
                        errorComment = string.Format("Unexpected Study Root Move Query/Retrieve level: {0}", level);
                        Platform.Log(LogLevel.Error, errorComment);

                        server.SendCMoveResponse(presentationId, message.MessageId, new DicomMessage(),
                                                 DicomStatuses.QueryRetrieveIdentifierDoesNotMatchSOPClass,
                                                 errorComment);
                        finalResponseSent = true;
                        return true;
                    }

                    // Could not find an online/readable location for the requested objects to move.
					// Note that if the C-MOVE-RQ included a list of study instance uids, and some 
					// were online and some offline, we don't fail now (ie, the check on the Count)
					if (!bOnline && _theScu.StorageInstanceList.Count == 0)
                    {
                        Platform.Log(LogLevel.Error, "Unable to find online storage location for C-MOVE-RQ: {0}", errorComment);

                        server.SendCMoveResponse(presentationId, message.MessageId, new DicomMessage(),
                                                 DicomStatuses.QueryRetrieveUnableToPerformSuboperations,
                                                 string.IsNullOrEmpty(errorComment) ? string.Empty : errorComment);
                        finalResponseSent = true;
                    	_theScu.Dispose();
                        _theScu = null;
                        return true;
                    }

                    // No files were eligible for transfer, just send success and return
                    if (_theScu.StorageInstanceList.Count == 0)
                    {
                        server.SendCMoveResponse(presentationId, message.MessageId, new DicomMessage(),
                                                 DicomStatuses.Success,
                                                 0, 0, 0, 0);
                        finalResponseSent = true;
						_theScu.Dispose();
						_theScu = null;
                        return true;
                    }

                    // set the preferred syntax lists
                    _theScu.LoadPreferredSyntaxes(read);

                	_theScu.ImageStoreCompleted += (sender, e) =>
                	                               	{
                	                               		var scu = (StorageScu) sender;
                	                               		var msg = new DicomMessage();
                	                               		var instance = e.StorageInstance;
                	                               		DicomStatus status;

                                                        if (instance.SendStatus.Status == DicomState.Failure)
                                                        {
                                                            errorComment =
                                                                string.IsNullOrEmpty(instance.ExtendedFailureDescription)
                                                                    ? instance.SendStatus.ToString()
                                                                    : instance.ExtendedFailureDescription;
                                                        }

                	                               		if (scu.RemainingSubOperations == 0)
                	                               		{
                	                               			foreach (StorageInstance sop in _theScu.StorageInstanceList)
                	                               			{
                	                               				if ((sop.SendStatus.Status != DicomState.Success)
                	                               				    && (sop.SendStatus.Status != DicomState.Warning))
                	                               					msg.DataSet[DicomTags.FailedSopInstanceUidList].AppendString(sop.SopInstanceUid);
                	                               			}
                	                               			if (scu.Status == ScuOperationStatus.Canceled)
                	                               				status = DicomStatuses.Cancel;
                	                               			else if (scu.Status == ScuOperationStatus.ConnectFailed)
                	                               				status = DicomStatuses.QueryRetrieveMoveDestinationUnknown;
                	                               			else if (scu.FailureSubOperations > 0)
                	                               				status = DicomStatuses.QueryRetrieveSubOpsOneOrMoreFailures;
															else if (!bOnline)
																status = DicomStatuses.QueryRetrieveUnableToPerformSuboperations;
                	                               			else
                	                               				status = DicomStatuses.Success;
                	                               		}
                	                               		else
                	                               		{
                	                               			status = DicomStatuses.Pending;

                	                               			if ((scu.RemainingSubOperations%5) != 0)
                	                               				return;
                	                               			// Only send a RSP every 5 to reduce network load
                	                               		}
                	                               	    server.SendCMoveResponse(presentationId, message.MessageId,
                	                               	                             msg, status,
                	                               	                             (ushort) scu.SuccessSubOperations,
                	                               	                             (ushort) scu.RemainingSubOperations,
                	                               	                             (ushort) scu.FailureSubOperations,
                	                               	                             (ushort) scu.WarningSubOperations,
                                                                                 status == DicomStatuses.QueryRetrieveSubOpsOneOrMoreFailures
                	                               	                                 ? errorComment
                	                               	                                 : string.Empty);
                	                               	    
                	                               	    
                	                               		if (scu.RemainingSubOperations == 0)
                	                               			finalResponseSent = true;
                	                               	};

                    _theScu.AssociationAccepted +=
                        (sender, parms) => AssociationAuditLogger.BeginInstancesTransferAuditLogger(
                            _theScu.StorageInstanceList,
                            parms);
					
                    _theScu.BeginSend(
                        delegate(IAsyncResult ar)
                        	{
								if (_theScu != null)
								{
                                    if (!finalResponseSent)
                                    {
                                        var msg = new DicomMessage();
                                        server.SendCMoveResponse(presentationId, message.MessageId,
                                                                 msg, DicomStatuses.QueryRetrieveSubOpsOneOrMoreFailures,
                                                                 (ushort) _theScu.SuccessSubOperations,
                                                                 0,
                                                                 (ushort) (_theScu.FailureSubOperations + _theScu.RemainingSubOperations),
                                                                 (ushort) _theScu.WarningSubOperations, errorComment);
                                        finalResponseSent = true;
                                    }

									_theScu.EndSend(ar);
									_theScu.Dispose();
									_theScu = null;
								}
                        	},
                        _theScu);

                    return true;
                } // end using()
            } 
            catch (Exception e)
            {
                Platform.Log(LogLevel.Error,e,"Unexpected exception when processing C-MOVE-RQ");
                if (finalResponseSent == false)
                {
                    try
                    {
                        server.SendCMoveResponse(presentationId, message.MessageId, new DicomMessage(),
                                                 DicomStatuses.QueryRetrieveUnableToProcess, e.Message);
                        finalResponseSent = true;
                    }
                    catch (Exception x)
                    {
                        Platform.Log(LogLevel.Error, x,
                                     "Unable to send final C-MOVE-RSP message on association from {0} to {1}",
                                     association.CallingAE, association.CalledAE);
                        server.Abort();
                    }
                }
            }
            return false;
        }