The StoreSCP example demonstrates how to use DCF to implement the DICOM requirements of a store service class provider.
Classes
Class | Description | |
---|---|---|
Program |
Basic store service class provider example which should be run with the StoreSCU.
| |
ProgramCallbackStoreServer |
CallbackStoreServer handles C-STORE requests on the associations by registering a callback that StoreSCP will call when the C-STORE request is received.
| |
ProgramExtendedStoreServer |
ExtendedSCPStoreServer handles associations by creating a custom SCP that extends StoreSCP to handle the C-STORE requests on the association.
| |
ProgramMyExtendedStoreScp |
MyExtendedStoreScp extends StoreSCP and overrides the CStoreRq method, which is called anytime a C-STORE request DIMSE message is received.
|
Examples
StoreSCP Sample Code
public class Program { /// <summary> /// Main entry point for StoreSCP. /// </summary> [STAThread] public static void Main() { try { // create a CallbackStoreServer that listens for associations on port 104 CallbackStoreServer server104 = new CallbackStoreServer(104); server104.BeginListening(); Console.WriteLine("listening on {0}...", 104); // create a CallbackStoreServer that listens for associations on port 105. // only accept MR images that are ELE or ILE for this server IList<AllowedPresentationContext> allowed = new List<AllowedPresentationContext>(); allowed.Add(new AllowedPresentationContext(Uids.MRImageStorage, new[] { Uids.TransferSyntax.ExplicitVRLittleEndian, Uids.TransferSyntax.ImplicitVRLittleEndian })); CallbackStoreServer server105 = new CallbackStoreServer(105, allowed); server105.BeginListening(); Console.WriteLine("listening on {0}...", 105); // 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("Exception caught during execution: {0}", e); } } /// <summary> /// CallbackStoreServer handles C-STORE requests on the associations by registering a callback that StoreSCP will call when the C-STORE request is received. /// </summary> public class CallbackStoreServer : AssociationListenerAdapter, IAssociationConfigPolicyManager { readonly AssociationManager _manager; readonly IList<AllowedPresentationContext> _presentationContexts; /// <summary> /// Constructs an CallbackStoreServer 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, or null to use defaults.</param> public CallbackStoreServer(int port, IList<AllowedPresentationContext> allowedPresentationContexts = null) { _presentationContexts = allowedPresentationContexts; _manager = new AssociationManager(); _manager.ServerTcpPort = port; _manager.AssociationConfigPolicyMgr = this; _manager.AddAssociationListener(this); } /// <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(); } /// <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"); } } /// <summary> /// Creates a StoreSCP object to potentially handle the association that caused this 'beginAssociation' method to be called. /// </summary> /// <param name="assoc">The AssociationAcceptor for the given association</param> public override void BeginAssociation(AssociationAcceptor assoc) { assoc.RegisterServiceClassProvider(new Dicom.Store.StoreSCP(assoc, _presentationContexts, CStore)); } /// <summary> /// Writes some information from the incoming dataset to the console. /// </summary> /// <param name="acceptor">The AssociationAcceptor for the given association</param> /// <param name="request">The inbound CStoreRequest</param> /// <returns>A successful CStoreResponse</returns> private CStoreResponse CStore(AssociationAcceptor acceptor, CStoreRequest request) { CStoreResponse response = new CStoreResponse(request); string dir = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) ?? "."; string filename = Path.Combine(dir, request.Data.GetElementStringValue(Tags.SOPInstanceUID) + ".dcm"); Console.WriteLine("CallbackStoreServer: 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); } return response; } } /// <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> /// 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(); } /// <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"); } } /// <summary> /// Creates a MyExtendedStoreScp object to potentially handle the association that caused this 'beginAssociation' method to be called. /// </summary> /// <param name="assoc">The AssociationAcceptor for the given association</param> public override void BeginAssociation(AssociationAcceptor assoc) { assoc.RegisterServiceClassProvider(new MyExtendedStoreScp(assoc, _presentationContexts)); } } /// <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> /// <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 DimseMessage</param> /// <returns>A DimseMessage response</returns> public override CStoreResponse CStoreRq(CStoreRequest request) { CStoreResponse response = new CStoreResponse(request); 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); } return response; } } }