The QuerySCPExtended example demonstrates how to use the DCF to implement a simple Query/Retrieve service class provider that extends the stock QRSCP,
overriding methods to handle the CFindRequest, CMoveRequest, CGetRequest, and CCancelRequest DIMSE messages.
Classes
Class | Description | |
---|---|---|
Options |
Command line options class for parsing user options for QuerySCPExtended.
| |
OverrideQRSCP |
The OverrideQRSCP class provides an alternate implementation of the QRSCP that overrides the QRSCP
methods directly, instead of using the callback techniques used in the QuerySCPCallback.
| |
Program |
Basic query/retrieve service class provider example which should be run with the QuerySCU.
This example creates a class that extends the QuerySCP class and overrides the CFindRq, CGetRq, CMoveRq, CCancelRq and CStoreRsp methods to handle the required Query/Retrieve service DIMSE messages. See the SampleVNA example for a more full-featured example of a Query/Retrieve Service implementation. When responding to a MOVE request from a Query SCU, this Query/Retrieve SCP will send instances to a Store SCP. A Store SCP may be run by using the StoreSCPExtended code. StoreSCPExtended -p 105 -i path-to-ImagesReceivedForQuerySCU-folder QuerySCPExtended -p 104 -i path-to-QuerySCPImages-folder -mp 105 QuerySCU -p 104 FIND "Stephen^Miner" QuerySCU -p 104 MOVE 1.2.123.4.454.62341.246.2 | |
ProgramExtendedQueryServer |
A class that demonstrates the extended style server for a Query/Retrieve SCP.
|
Examples
QuerySCPExtended Sample Code
public class Program { /// <summary> /// Main entry point for QuerySCPExtended. /// </summary> /// <param name="args">Program arguments.</param> [STAThread] public static void Main(string[] args) { try { AllowedPresentationContext cFindContext = new AllowedPresentationContext( Uids.StudyRootQueryRetrieveInformationModelFIND, new List<string> {Uids.TransferSyntax.ExplicitVRLittleEndian, Uids.TransferSyntax.ImplicitVRLittleEndian}); AllowedPresentationContext cMoveContext = new AllowedPresentationContext( Uids.StudyRootQueryRetrieveInformationModelMOVE, new List<string> {Uids.TransferSyntax.ExplicitVRLittleEndian, Uids.TransferSyntax.ImplicitVRLittleEndian}); AllowedPresentationContext cGetContext = new AllowedPresentationContext( Uids.StudyRootQueryRetrieveInformationModelGET, new List<string> {Uids.TransferSyntax.ExplicitVRLittleEndian, Uids.TransferSyntax.ImplicitVRLittleEndian}); AllowedPresentationContext mrStore = new AllowedPresentationContext( Uids.MRImageStorage, new List<string> {Uids.ILE}); Options options; if (!Options.TryParse(args, out options)) return; if (string.IsNullOrEmpty(options.ImageStorageDirectory)) options.ImageStorageDirectory = Path.Combine( Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) ?? Environment.CurrentDirectory, "QuerySCPImages"); ExtendedQueryServer queryServer = new ExtendedQueryServer( options.ImageStorageDirectory, options.MoveDestinationPort, new List<AllowedPresentationContext> {cFindContext, cMoveContext, cGetContext, mrStore}); AssociationManager mgr = new AssociationManager(); mgr.ServerTcpPort = options.Port; mgr.AssociationConfigPolicyMgr = queryServer; mgr.AddAssociationListener(queryServer); // AssociationManager.run() blocks, so start him listening for connections on a separate thread Thread t = new Thread(mgr.Run); t.Start(); if (!mgr.WaitForRunning(2000)) { throw new TimeoutException("AssociationManager did not start in an acceptable amount of time"); } Console.WriteLine("listening on {0}...", mgr.ServerTcpPort); Console.WriteLine("Requested instances will be matched in directory '{0}'", options.ImageStorageDirectory); Console.WriteLine("Requested instances will be sent to port {0}", options.MoveDestinationPort); } catch (Exception e) { Console.WriteLine("Error during execution: {0}", e); Environment.ExitCode = 1; } finally { if (Debugger.IsAttached) { Console.Write("Press any key to continue . . . "); Console.ReadKey(); } } } /// <summary> /// A class that demonstrates the extended style server for a Query/Retrieve SCP. /// </summary> /// <remarks> /// <para> /// The <see cref="IAssociationConfigPolicyManager"/> allows us to get a callback to set our /// session settings for the association. /// </para> /// <para> /// This class overrides the <see cref="AssociationListenerAdapter.BeginAssociation"/> method /// to install a QRSCP. /// </para> /// <para> /// Other <see cref="AssociationListenerAdapter"/> methods may be overridden to implement /// additional functionality. /// </para> /// </remarks> public class ExtendedQueryServer : AssociationListenerAdapter, IAssociationConfigPolicyManager { readonly IList<AllowedPresentationContext> _presentationContexts; private readonly string _imageStorageDirectory; private readonly int _moveDestinationPort; /// <summary> /// Constructs an ExtendedQueryServer that will service incoming associations. /// </summary> /// <param name="imageStorageDirectory">The directory in which to look for matching instance.</param> /// <param name="moveDestinationPort">The port on which matching C-MOVE instances are to be sent back; assumed to be on same address, "localhost".</param> /// <param name="allowedPresentationContexts">The list of presentation contexts that will be allowed</param> public ExtendedQueryServer(string imageStorageDirectory, int moveDestinationPort, IList<AllowedPresentationContext> allowedPresentationContexts) { _presentationContexts = allowedPresentationContexts; _imageStorageDirectory = imageStorageDirectory; _moveDestinationPort = moveDestinationPort; } #region IAssociationConfigPolicyManager Overrides /// <summary> /// Returns a DicomSessionSettings object to be used for the association. /// </summary> /// <param name="assoc">The AssociationAcceptor for the given association</param> /// <returns>The DicomSessionSettings for the given association.</returns> public DicomSessionSettings GetSessionSettings(AssociationAcceptor assoc) { return new DicomSessionSettings() { SessionName = String.Format("QRServer: {0}", assoc.DicomSocket.ConnectionID) }; } #endregion #region AssociationListener Overrides /// <summary> /// Creates the QRSCP to handle the association that caused this method to be called. /// </summary> /// <param name="assoc">The AssociationAcceptor for the given association</param> public override void BeginAssociation(AssociationAcceptor assoc) { assoc.RegisterServiceClassProvider(new OverrideQRSCP(_imageStorageDirectory, _moveDestinationPort, assoc, _presentationContexts)); } #endregion } }