LAUREL BRIDGE

LaurelBridge.DCF.Examples.StoreSCPExtended Namespace

DICOM Connectivity Framework V3.4
The StoreSCPExtended example demonstrates how to use the DCF to implement a simple store SCP by extending the stock StoreSCP that receives datasets and writes them to disk.
Classes

  ClassDescription
Public classProgram
Basic store service class provider example which should be run with the StoreSCU.
Public classProgramExtendedStoreServer
ExtendedSCPStoreServer handles associations by creating a custom SCP that extends StoreSCP to handle the C-STORE requests on the association.
Public classProgramMyExtendedStoreScp
MyExtendedStoreScp extends StoreSCP and overrides the CStoreRq method, which is called anytime a C-STORE request DIMSE message is received.
Examples

StoreSCPExtended Sample Code
public class Program
{
    /// <summary>
    /// Main entry point for StoreSCPExtended.
    /// </summary>
    /// <param name="args">Program arguments.</param>
    [STAThread]
    public static void Main(string[] args)
    {
        try
        {
            // create an ExtendedSCPStoreServer that listens for associations on port 106
            ExtendedStoreServer server106 = new ExtendedStoreServer(106);
            server106.BeginListening();
            Console.WriteLine("listening on {0}...", 106);
        }
        catch (Exception e)
        {
            Console.WriteLine("Error during execution: {0}", e);
            Environment.ExitCode = 1;
        }
    }

    /// <summary>
    /// ExtendedSCPStoreServer handles associations by creating a custom SCP that extends StoreSCP to handle the C-STORE requests on the association.
    /// </summary>
    public class ExtendedStoreServer : AssociationListenerAdapter, IAssociationConfigPolicyManager
    {
        private readonly AssociationManager _manager;
        private readonly IList<AllowedPresentationContext> _presentationContexts;

        /// <summary>
        /// Constructs an ExtendedSCPStoreServer that listens on the specified port.
        /// </summary>
        /// <param name="port">The port number where we will listen for association requests</param>
        /// <param name="allowedPresentationContexts">The list of presentation contexts that will be allowed</param>
        public ExtendedStoreServer(int port, IList<AllowedPresentationContext> allowedPresentationContexts = null)
        {
            _presentationContexts = allowedPresentationContexts;
            _manager = new AssociationManager();
            _manager.ServerTcpPort = port;
            _manager.AssociationConfigPolicyMgr = this;
            _manager.AddAssociationListener(this);
        }

        /// <summary>
        /// Start listening for associations via the AssociationManager.
        /// </summary>
        public void BeginListening()
        {
            Thread t = new Thread(_manager.Run);
            t.Start();
            if (!_manager.WaitForRunning(2000))
            {
                throw new TimeoutException("AssociationManager did not start in an acceptable amount of time");
            }
        }

        #region IAssociationConfigPolicyManager implementation
        /// <summary>
        /// Returns a DicomSessionSettings object to be used for the association.
        /// </summary>
        /// <param name="assoc">The AssociationAcceptor for the given association</param>
        /// <returns>A default DicomSessionSettings object</returns>
        public DicomSessionSettings GetSessionSettings(AssociationAcceptor assoc)
        {
            return new DicomSessionSettings();
        }
        #endregion

        #region AssociationListenerAdapter overrides
        /// <summary>
        /// Override of BeginAssociation which is called during association negotiation.  Register an extended StoreSCP object to
        /// handle store requests for our allowed presentation contexts.
        /// </summary>
        /// <param name="assoc">The AssociationAcceptor for the given association</param>
        public override void BeginAssociation(AssociationAcceptor assoc)
        {
            assoc.RegisterServiceClassProvider(new MyExtendedStoreScp(assoc, _presentationContexts));
        }
        #endregion
    }

    /// <summary>
    /// MyExtendedStoreScp extends StoreSCP and overrides the CStoreRq method, which is called anytime a C-STORE request DIMSE message is received.
    /// </summary>
    public class MyExtendedStoreScp : Dicom.Store.StoreSCP
    {
        /// <summary>
        /// Create a store handler with the given acceptor and presentation contexts.
        /// </summary>
        /// <remarks>
        /// See <see cref="LaurelBridge.DCF.Dicom.Store.StoreSCP"/> for a description of where the default presentation contexts are retrieved.
        /// </remarks>
        /// <param name="acceptor">The association acceptor.</param>
        /// <param name="allowedPresentationContexts">The allowed presentation contexts which my be null to specify the default storage classes.</param>
        public MyExtendedStoreScp(AssociationAcceptor acceptor, IList<AllowedPresentationContext> allowedPresentationContexts = null)
            : base(acceptor, allowedPresentationContexts, null)
        {
        }

        /// <summary>
        /// Writes some information from the incoming dataset to the console.
        /// </summary>
        /// <param name="request">The inbound CStoreRequest DIMSE message</param>
        /// <returns>A successful CStoreResponse</returns>
        public override CStoreResponse CStoreRq(CStoreRequest request)
        {
            CStoreResponse response = new CStoreResponse(request);

            try
            {
                string dir = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) ?? ".";
                string filename = Path.Combine(dir, request.Data.GetElementStringValue(Tags.SOPInstanceUID) + ".dcm");
                Console.WriteLine(
                    "MyExtendedStoreScp: patient's name is {0} from {1} to port {2} with transfer syntax {3}. Writing to {4}{5}",
                    request.Data.GetElementStringValue(Tags.PatientName),
                    Acceptor.AssociationInfo.CallingTitle,
                    Acceptor.AssociationInfo.CalledPresentationAddress,
                    Acceptor.AssociationInfo.GetAcceptedPresentationContext(request.ContextId).TransferSyntaxUid,
                    filename,
                    Environment.NewLine);

                // write the dataset out as a chapter 10 file to the same location as this example's executable
                using (DicomFileOutput dfo = new DicomFileOutput(filename,
                    Acceptor.AssociationInfo.GetAcceptedPresentationContext(request.ContextId).TransferSyntaxUid))
                {
                    // persist the AE titles from the network connection in the ch10 file
                    dfo.GetFileMetaInformation().SendingAETitle = Acceptor.AssociationInfo.CallingTitle;
                    dfo.GetFileMetaInformation().ReceivingAETitle = Acceptor.AssociationInfo.CalledTitle;

                    dfo.WriteDataSet(request.Data);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("CStore Failed: {0}", e);
                response.Status = DimseStatus.PROCESSING_FAILURE;
                response.ErrorComment = "Failed while trying to store. Error is: " + e.Message;
            }

            return response;
        }
    }
}