/// <summary> /// Do the actual verification if an association is acceptable. /// </summary> /// <remarks> /// This method primarily checks the remote AE title to see if it is a valid device that can /// connect to the partition. /// </remarks> /// <param name="context">Generic parameter passed in, is a DicomScpParameters instance.</param> /// <param name="assocParms">The association parameters.</param> /// <param name="result">Output parameter with the DicomRejectResult for rejecting the association.</param> /// <param name="reason">Output parameter with the DicomRejectReason for rejecting the association.</param> /// <returns>true if the association should be accepted, false if it should be rejected.</returns> public static bool Verify(DicomScpContext context, ServerAssociationParameters assocParms, out DicomRejectResult result, out DicomRejectReason reason) { bool isNew; Device device = DeviceManager.LookupDevice(context.Partition, assocParms, out isNew); if (device == null) { if (context.Partition.AcceptAnyDevice) { reason = DicomRejectReason.NoReasonGiven; result = DicomRejectResult.Permanent; return true; } reason = DicomRejectReason.CallingAENotRecognized; result = DicomRejectResult.Permanent; return false; } if (device.Enabled == false) { Platform.Log(LogLevel.Error, "Rejecting association from {0} to {1}. Device is disabled.", assocParms.CallingAE, assocParms.CalledAE); reason = DicomRejectReason.CallingAENotRecognized; result = DicomRejectResult.Permanent; return false; } reason = DicomRejectReason.NoReasonGiven; result = DicomRejectResult.Permanent; return true; }
public override bool OnReceiveRequest(DicomServer server, ServerAssociationParameters association, byte presentationID, DicomMessage message) { server.SendCEchoResponse(presentationID, message.MessageId, DicomStatuses.Success); return true; }
public override bool OnReceiveRequest(DicomServer server, ServerAssociationParameters association, byte presentationID, DicomMessage message) { try { var import = new SopInstanceImporter(new SopInstanceImporterContext( "", association.CallingAE, Partition)); import.Import(message); server.SendCStoreResponse(presentationID, message.MessageId, message.AffectedSopInstanceUid, DicomStatuses.Success); } catch (DicomDataException ex) { Platform.Log(LogLevel.Error, ex); return false; // caller will abort the association } catch (Exception ex) { Platform.Log(LogLevel.Error, ex); return false; // caller will abort the association } return true; }
public static bool Listen(ServerAssociationParameters parameters, StartAssociation acceptor) { lock (_syncLock) { Listener theListener; if (_listeners.TryGetValue(parameters.LocalEndPoint, out theListener)) { ListenerInfo info = new ListenerInfo(); info.StartDelegate = acceptor; info.Parameters = parameters; if (theListener._applications.ContainsKey(parameters.CalledAE)) { Platform.Log(LogLevel.Error, "Already listening with AE {0} on {1}", parameters.CalledAE, parameters.LocalEndPoint.ToString()); return false; } theListener._applications.Add(parameters.CalledAE, info); Platform.Log(LogLevel.Info, "Starting to listen with AE {0} on existing port {1}", parameters.CalledAE, parameters.LocalEndPoint.ToString()); } else { theListener = new Listener(parameters, acceptor); if (!theListener.StartListening()) { Platform.Log(LogLevel.Error, "Unexpected error starting to listen on {0}", parameters.LocalEndPoint.ToString()); return false; } _listeners[parameters.LocalEndPoint] = theListener; theListener.StartThread(); Platform.Log(LogLevel.Info, "Starting to listen with AE {0} on port {1}", parameters.CalledAE, parameters.LocalEndPoint.ToString()); } return true; } }
public override bool OnReceiveRequest(DicomServer server, ServerAssociationParameters association, byte presentationID, DicomMessage message) { if (message.CommandField == DicomCommandField.CCancelRequest) { Platform.Log(LogLevel.Info, "Received Worklist-CANCEL-RQ message."); _cancelReceived = true; return true; } if (message.AffectedSopClassUid.Equals(SopClass.ModalityWorklistInformationModelFindUid)) { // We use the ThreadPool to process the thread requests. This is so that we return back // to the main message loop, and continue to look for cancel request messages coming // in. There's a small chance this may cause delays in responding to query requests if // the .NET Thread pool fills up. ThreadPool.QueueUserWorkItem(delegate { try { OnReceiveMWLQuery(server, presentationID, message); } catch (Exception x) { Platform.Log(LogLevel.Error, x, "Unexpected exception in OnReceiveStudyLevelQuery."); } }); return true; } // no supported message type, send a failure status server.SendCFindResponse(presentationID, message.MessageId, new DicomMessage(), DicomStatuses.QueryRetrieveIdentifierDoesNotMatchSOPClass); return true; }
public override bool OnReceiveRequest(DicomServer server, ServerAssociationParameters association, byte presentationID, DicomMessage message) { bool finalResponseSent = false; string errorComment; try { // check for a Cancel Message, and cance the scu if (message.CommandField == DicomCommandField.CCancelRequest) { if (_theScu != null) { _theScu.Cancel(); } return true; } string level = message.DataSet[DicomTags.QueryRetrieveLevel].GetString(0, string.Empty); string remoteAe = message.MoveDestination.Trim(); // load remote device for move information using (var ctx = new PacsContext()) { var device = (from d in ctx.Devices where d.ServerPartitionPK == Partition.Id && d.AeTitle == remoteAe select d).FirstOrDefault(); 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.Hostname = association.RemoteEndPoint.Address.ToString(); } // now setup the storage scu component _theScu = new PacsStorageScu(Partition, device, association.CallingAE, message.MessageId); bool bOnline; if (level.Equals("PATIENT")) { bOnline = GetSopListForInstance(ctx, message, out errorComment); } else if (level.Equals("STUDY")) { bOnline = GetSopListForStudy(ctx, message, out errorComment); } else if (level.Equals("SERIES")) { bOnline = GetSopListForSeries(ctx, message, out errorComment); } else if (level.Equals("IMAGE")) { bOnline = GetSopListForInstance(ctx, 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; } _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.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; } } 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; }
public override bool OnReceiveRequest(DicomServer server, ServerAssociationParameters association, byte presentationId, DicomMessage message) { string level = message.DataSet[DicomTags.QueryRetrieveLevel].GetString(0, string.Empty); if (message.CommandField == DicomCommandField.CCancelRequest) { Platform.Log(LogLevel.Info, "Received C-FIND-CANCEL-RQ message."); _cancelReceived = true; return true; } if (message.AffectedSopClassUid.Equals(SopClass.StudyRootQueryRetrieveInformationModelFindUid)) { switch (level) { case "STUDY": // We use the ThreadPool to process the thread requests. This is so that we return back // to the main message loop, and continue to look for cancel request messages coming // in. There's a small chance this may cause delays in responding to query requests if // the .NET Thread pool fills up. ThreadPool.QueueUserWorkItem(delegate { try { OnReceiveStudyLevelQuery(server, presentationId, message); } catch (Exception x) { Platform.Log(LogLevel.Error, x, "Unexpected exception in OnReceiveStudyLevelQuery."); } }); return true; case "SERIES": ThreadPool.QueueUserWorkItem(delegate { try { OnReceiveSeriesLevelQuery(server, presentationId, message); } catch (Exception x) { Platform.Log(LogLevel.Error, x, "Unexpected exception in OnReceiveSeriesLevelQuery."); } }); return true; case "IMAGE": ThreadPool.QueueUserWorkItem(delegate { try { OnReceiveImageLevelQuery(server, presentationId, message); } catch (Exception x) { Platform.Log(LogLevel.Error, x, "Unexpected exception in OnReceiveImageLevelQuery."); } }); return true; default: { Platform.Log(LogLevel.Error, "Unexpected Study Root Query/Retrieve level: {0}", level); server.SendCFindResponse(presentationId, message.MessageId, new DicomMessage(), DicomStatuses.QueryRetrieveIdentifierDoesNotMatchSOPClass); return true; } } } if (message.AffectedSopClassUid.Equals(SopClass.PatientRootQueryRetrieveInformationModelFindUid)) { switch (level) { case "PATIENT": ThreadPool.QueueUserWorkItem(delegate { try { OnReceivePatientQuery(server, presentationId, message); } catch (Exception x) { Platform.Log(LogLevel.Error, x, "Unexpected exception in OnReceivePatientQuery."); } }); return true; case "STUDY": ThreadPool.QueueUserWorkItem(delegate { try { OnReceiveStudyLevelQuery(server, presentationId, message); } catch (Exception x) { Platform.Log(LogLevel.Error, x, "Unexpected exception in OnReceiveStudyLevelQuery."); } }); return true; case "SERIES": ThreadPool.QueueUserWorkItem(delegate { try { OnReceiveSeriesLevelQuery(server, presentationId, message); } catch (Exception x) { Platform.Log(LogLevel.Error, x, "Unexpected exception in OnReceiveSeriesLevelQuery."); } }); return true; case "IMAGE": ThreadPool.QueueUserWorkItem(delegate { try { OnReceiveImageLevelQuery(server, presentationId, message); } catch (Exception x) { Platform.Log(LogLevel.Error, x, "Unexpected exception in OnReceiveImageLevelQuery."); } }); return true; default: { Platform.Log(LogLevel.Error, "Unexpected Patient Root Query/Retrieve level: {0}", level); server.SendCFindResponse(presentationId, message.MessageId, new DicomMessage(), DicomStatuses.QueryRetrieveIdentifierDoesNotMatchSOPClass); return true; } } } // no supported message type, send a failure status server.SendCFindResponse(presentationId, message.MessageId, new DicomMessage(), DicomStatuses.QueryRetrieveIdentifierDoesNotMatchSOPClass); return true; }
public override bool OnReceiveRequest(DicomServer server, ServerAssociationParameters association, byte presentationID, DicomMessage message) { #region MPPS NCreate Request if (message.CommandField == DicomCommandField.NCreateRequest) { ModalityPerformedProcedureStepIod mppsIod = new ModalityPerformedProcedureStepIod(message.DataSet); bool conform = CheckNCreateDataSetConformance(server, association, presentationID, mppsIod, true); if (!conform) { server.SendNCreateResponse(presentationID, message.MessageId, new DicomMessage(), DicomStatuses.InvalidAttributeValue); Platform.Log(LogLevel.Error, "Sending Invalid Attributes Response."); return true; } // wrong status if (mppsIod.PerformedProcedureStepInformation.PerformedProcedureStepStatus != PerformedProcedureStepStatus.InProgress) { server.SendNCreateResponse(presentationID, message.MessageId, new DicomMessage(), DicomStatuses.InvalidAttributeValue); Platform.Log(LogLevel.Error, "Received N-Create Request with bad status."); return true; } if (!ProcessNCreateRequest(server, presentationID, message)) { server.SendNCreateResponse(presentationID, message.MessageId, new DicomMessage(), DicomStatuses.ProcessingFailure); return true; } server.SendNCreateResponse(presentationID, message.MessageId, new DicomMessage(), DicomStatuses.Success); } #endregion #region MPPS NSet Request if (message.CommandField == DicomCommandField.NSetRequest) { ModalityPerformedProcedureStepIod mppsIod = new ModalityPerformedProcedureStepIod(message.DataSet); string studyInstanceUID = mppsIod. PerformedProcedureStepRelationship. DicomAttributeProvider[DicomTags.StudyInstanceUid]. GetString(0, ""); // Get Cached ModalityPerformedProcedureStepIod cachedMppsIod = new ModalityPerformedProcedureStepIod(); bool conform = CheckNSetDataSetConformance(server, association, presentationID, cachedMppsIod, mppsIod, true); if (!conform) { server.SendNCreateResponse(presentationID, message.MessageId, new DicomMessage(), DicomStatuses.InvalidAttributeValue); Platform.Log(LogLevel.Error, "Sending Failure Response."); return true; } bool success = true; if (mppsIod.PerformedProcedureStepInformation.PerformedProcedureStepStatus == PerformedProcedureStepStatus.Completed) { success = ProcessNSetRequestForCompleted(server, presentationID, message); } if (mppsIod.PerformedProcedureStepInformation.PerformedProcedureStepStatus == PerformedProcedureStepStatus.Discontinued) { success = ProcessNSetRequestForDiscontinued(server, presentationID, message); } server.SendNSetResponse(presentationID, message.MessageId, new DicomMessage(), success ? DicomStatuses.Success : DicomStatuses.ProcessingFailure); } #endregion // no supported message type, send a failure status server.SendCFindResponse(presentationID, message.MessageId, new DicomMessage(), DicomStatuses.QueryRetrieveIdentifierDoesNotMatchSOPClass); return true; }
internal Listener(ServerAssociationParameters parameters, StartAssociation acceptor) { ListenerInfo info = new ListenerInfo(); info.Parameters = parameters; info.StartDelegate = acceptor; _applications.Add(parameters.CalledAE, info); _ipEndPoint = parameters.LocalEndPoint; }
public static bool StopListening(ServerAssociationParameters parameters) { lock (_syncLock) { Listener theListener; if (_listeners.TryGetValue(parameters.LocalEndPoint, out theListener)) { if (theListener._applications.ContainsKey(parameters.CalledAE)) { theListener._applications.Remove(parameters.CalledAE); if (theListener._applications.Count == 0) { // Cleanup the listener _listeners.Remove(parameters.LocalEndPoint); theListener.StopThread(); theListener.Dispose(); } Platform.Log(LogLevel.Info, "Stopping listening with AE {0} on {1}", parameters.CalledAE, parameters.LocalEndPoint.ToString()); } else { Platform.Log(LogLevel.Error, "Unable to stop listening on AE {0}, assembly was not listening with this AE.", parameters.CalledAE); return false; } } else { Platform.Log(LogLevel.Error, "Unable to stop listening, assembly was not listening on end point {0}.", parameters.LocalEndPoint.ToString()); return false; } return true; } }
/// <summary> /// Stop listening for incoming associations. /// </summary> /// <remarks> /// <para> /// Note that <see cref="StartListening"/> can be called multiple times with different association /// parameters. /// </para> /// </remarks> /// <param name="parameters">The parameters to stop listening on.</param> public static void StopListening(ServerAssociationParameters parameters) { Listener.StopListening(parameters); }
/// <summary> /// Start listening for incoming associations. /// </summary> /// <remarks> /// <para> /// Note that StartListening can be called multiple times with different association parameters. /// </para> /// </remarks> /// <param name="parameters">The parameters to use when listening for associations.</param> /// <param name="acceptor">A delegate to be called to return a class instance that implements /// the <see cref="IDicomServerHandler"/> interface to handle an incoming association.</param> /// <returns><i>true</i> on success, <i>false</i> on failure</returns> public static bool StartListening(ServerAssociationParameters parameters, StartAssociation acceptor) { return Listener.Listen(parameters, acceptor); }
/// <summary> /// Method called when receiving an association request. /// </summary> /// <param name="association"></param> protected override void OnReceiveAssociateRequest(ServerAssociationParameters association) { ListenerInfo info; if (!_appList.TryGetValue(association.CalledAE, out info)) { Platform.Log(LogLevel.Error, "Rejecting association from {0}: Invalid Called AE Title ({1}).", association.CallingAE, association.CalledAE); SendAssociateReject(DicomRejectResult.Permanent, DicomRejectSource.ServiceProviderACSE, DicomRejectReason.CalledAENotRecognized); return; } // Populate the AssociationParameters properly association.ReadTimeout = info.Parameters.ReadTimeout; association.ReceiveBufferSize = info.Parameters.ReceiveBufferSize; association.WriteTimeout = info.Parameters.WriteTimeout; association.SendBufferSize = info.Parameters.SendBufferSize; association.RemoteEndPoint = _socket.RemoteEndPoint as IPEndPoint; association.LocalEndPoint = _socket.LocalEndPoint as IPEndPoint; // Setup Socketoptions based on the user's settings SetSocketOptions(association); // Select the presentation contexts bool anyValidContexts = NegotiateAssociation(association, info.Parameters); if (!anyValidContexts) { Platform.Log(LogLevel.Error, "Rejecting association from {0}: No valid presentation contexts.", association.CallingAE); SendAssociateReject(DicomRejectResult.Permanent, DicomRejectSource.ServiceProviderACSE, DicomRejectReason.NoReasonGiven); return; } _appList = null; try { _handler = info.StartDelegate(this, association); _handler.OnReceiveAssociateRequest(this, association); } catch (Exception e) { OnUserException(e, "Unexpected exception on OnReceiveAssociateRequest or StartDelegate"); } }
private static bool NegotiateAssociation(AssociationParameters cp, ServerAssociationParameters sp) { foreach (DicomPresContext clientContext in cp.GetPresentationContexts()) { TransferSyntax selectedSyntax = null; foreach (DicomPresContext serverContext in sp.GetPresentationContexts()) { if (clientContext.AbstractSyntax.Uid.Equals(serverContext.AbstractSyntax.Uid)) { foreach (TransferSyntax ts in serverContext.GetTransfers()) { if (clientContext.HasTransfer(ts)) { selectedSyntax = ts; break; } } } if (selectedSyntax != null) break; } if (selectedSyntax != null) { clientContext.ClearTransfers(); clientContext.AddTransfer(selectedSyntax); clientContext.SetResult(DicomPresContextResult.Accept); } else { // No contexts accepted, set if abstract or transfer syntax reject clientContext.SetResult(0 == sp.FindAbstractSyntax(clientContext.AbstractSyntax) ? DicomPresContextResult.RejectAbstractSyntaxNotSupported : DicomPresContextResult.RejectTransferSyntaxesNotSupported); } } bool anyValidContexts = false; foreach (DicomPresContext clientContext in cp.GetPresentationContexts()) { if (clientContext.Result == DicomPresContextResult.Accept) { anyValidContexts = true; break; } } if (anyValidContexts == false) { return false; } return true; }
private void SetSocketOptions(ServerAssociationParameters parameters) { _socket.ReceiveBufferSize = parameters.ReceiveBufferSize; _socket.SendBufferSize = parameters.SendBufferSize; _socket.ReceiveTimeout = parameters.ReadTimeout; _socket.SendTimeout = parameters.WriteTimeout; _socket.LingerState = new LingerOption(false, 0); // Nagle option _socket.NoDelay = parameters.DisableNagle; }
public override bool OnReceiveRequest(Dicom.Network.DicomServer server, ServerAssociationParameters association, byte presentationID, DicomMessage message) { throw new System.NotImplementedException(); }
protected virtual void OnReceiveAssociateRequest(ServerAssociationParameters association) { throw new Exception("The method or operation is not implemented."); }
private bool CheckNSetDataSetConformance(DicomServer server, ServerAssociationParameters association, byte presentationID, ModalityPerformedProcedureStepIod cachedMppsIod, ModalityPerformedProcedureStepIod receivedMppsIod, bool logFirstAnomalyOnly) { throw new NotImplementedException(); }