LAUREL BRIDGE

LaurelBridge.DCFExamples.MWLSCU Namespace

DICOM Connectivity Framework V3.4
The MWLSCU example demonstrates how to use DCF to implement a modality worklist service class user.
Classes

  ClassDescription
Public classProgram
Basic worklist service class user example which should be run with MWLSCP.
Public classProgramMWLQueryListener
This class wraps the worklist SCU and handles receiving worklist query responses. Various properties on the scu object allow setting timeouts for various conditions.
Remarks

Supported OS Platforms:

  • Windows - .Net Framework 4.7.2 64-bit and 32-bit
  • Windows - .Net Core 2.1 64-bit and 32-bit
  • Linux - .Net Core 2.1 64-bit

Examples

MWLSCU Sample Code
public class Program
{
    /// <summary>
    /// The main entry point for MWLSCU.
    /// </summary>
    [STAThread]
    public static void Main()
    {
        MWLQueryListener listener = new MWLQueryListener();
        listener.ExecuteQuery(BuildQuery());

        if (System.Diagnostics.Debugger.IsAttached)
        {
            Console.Write("Press any key to continue . . . ");
            Console.ReadKey();
        }
    }

    /// <summary>
    /// Build the QRIdentifier for this query.
    /// </summary>
    /// <remarks>
    /// We take the simple approach in this example of specifying an ISO_IR 100 character set for the
    /// encoding of our query request, which is probably fine for 90% of the Western languages.  By a strict
    /// reading of the spec, the character set should only be specified if we actually require it in our
    /// query.  Note that the SCP is free to ignore and/or not support our character set.
    /// <para>
    /// If you need to encode additional and/or multi-byte character sets, you may use the EncodingUtils.AnalyzeEncodings
    /// method in the CharacterSets namespace.
    /// </para>
    /// </remarks>
    /// <returns>The QRIdentifier dataset.</returns>
    private static QRIdentifier BuildQuery()
    {
        QRIdentifier query = new QRIdentifier();
        //Fields to query on.
        query.PatientsName = "*STEPHEN";

        //Fields to return.
        query.PatientId = "";
        query.PatientsSex = "";
        query.Modality = "";
        query.StudyDate = "";

        // Specify that this request contains ISO_IR 100 encoded data
        query.DataSet.Insert(Tags.SpecificCharacterSet, EncodingUtils.GetCharacterSetName(CharacterSetID.ISO_IR_100));

        return query;
    }

    /// <summary>
    /// This class wraps the worklist SCU and handles receiving worklist query responses.
    /// Various properties on the scu object allow setting timeouts for various conditions.
    /// </summary>
    public class MWLQueryListener : IQueryListener
    {
        private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

        private readonly DCF.Dicom.Worklist.MWLSCU _scu;

        /// <summary>
        /// Constructor creates the MWLSCU, and configures timeouts.
        /// </summary>
        public MWLQueryListener()
        {
            AssociationInfo ainfo = new AssociationInfo();
            ainfo.CallingTitle = "SCU";
            ainfo.CalledTitle = "SCP";
            ainfo.CalledPresentationAddress = "localhost:10104";

            // Add the requested presentation context
            RequestedPresentationContext ctx = new RequestedPresentationContext(
                1,
                Uids.ModalityWorklistInformationModelFIND,
                Uids.ELE, Uids.ILE);

            ainfo.AddRequestedPresentationContext(ctx);

            // Make the SCU here
            _scu = new DCF.Dicom.Worklist.MWLSCU(ainfo, new DicomSessionSettings() { IsDebugEnabled = true, DebugFlags = DF.User8 });

            _scu.MaxReturnedResults = 100;
            //scu_.QueryTimeoutSeconds = configuration_.QueryTimeoutSeconds;
            _scu.QueryTimeoutSeconds = -1; // We only use the progress timer, not the absolute timer
            _scu.SendDimseTimeoutSeconds = 30;
            _scu.ReceiveDimseTimeoutSeconds = 30;
            _scu.ProgressTimeoutSeconds = 30;
        }

        /// <summary>
        /// Execute a c-find request using the dataset in the QRIdentifier.
        /// </summary>
        /// <param name="query">the query dataset</param>
        public void ExecuteQuery(QRIdentifier query)
        {
            try
            {
                Logger.DebugFormat(DF.User8, "Query Data set is: {0}{1}", Environment.NewLine, query.DataSet);

                _scu.RequestAssociation();
                _scu.CFind(query.DataSet, this, true);
                _scu.ReleaseAssociation();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error executing query: {0}{1}{1}Please verify the Query SCP is up and running.", ex, Environment.NewLine);
            }
        }

        #region QueryListener Members
        /// <summary>
        /// This method is called at the completion of the query.
        /// </summary>
        /// <param name="status">The DIMSE status of the final response.</param>
        public void QueryComplete(int status)
        {
            Logger.InfoFormat("Received queryComplete event status = {0}", status);

            switch (status)
            {
                default:
                    {
                        Console.WriteLine("Operation status({0})", status);
                        break;
                    }
                case QueryListenerStatus.QUERY_LISTENER_SUCCESS:
                    {
                        Console.WriteLine("Operation finished with no errors.");
                        break;
                    }
                case QueryListenerStatus.QUERY_LISTENER_CANCELED:
                    {
                        Console.WriteLine("Operation was Canceled successfully");
                        break;
                    }
                case QueryListenerStatus.QUERY_LISTENER_WARNING:
                    {
                        Console.WriteLine("Warning: some or all subops failed for C-MOVE");
                        break;
                    }
                case QueryListenerStatus.QUERY_LISTENER_ERROR:
                    {
                        Console.WriteLine("Failure.");
                        break;
                    }
                case QueryListenerStatus.QUERY_LISTENER_INTERNAL_ERROR:
                    {
                        Console.WriteLine("Internal Error. Association may still be connected. Aborting");
                        if (_scu.Connected) { _scu.AbortAssociation(); }
                        break;
                    }
                case QueryListenerStatus.QUERY_LISTENER_TIMEOUT:
                    {
                        Console.WriteLine("Operation timed out");
                        if (_scu.Connected) { _scu.AbortAssociation(); }
                        break;
                    }
                case QueryListenerStatus.QUERY_LISTENER_SUCCESS_MAX_RETURNED_RESULTS_REACHED:
                    {
                        Console.WriteLine("Success, Max Returned Results Reached");
                        if (_scu.Connected) { _scu.AbortAssociation(); }
                        break;
                    }
                case QueryListenerStatus.QUERY_LISTENER_DIMSE_RECEIVE_TIMEOUT:
                    {
                        Console.WriteLine("Operation DIMSE receive timed out");
                        if (_scu.Connected) { _scu.AbortAssociation(); }
                        break;
                    }
            }
        }

        /// <summary>
        /// This method is called for all incoming DIMSE response messages.
        /// </summary>
        /// <param name="rsp">The DIMSE response messages.</param>
        public void QueryEvent(DimseMessage rsp)
        {
            // This method is called for both pending and final responses, so filter out 
            // the final response (it doesn't have a data dataset, only a command dataset).
            if (rsp.DataSetType != 0x101)
                Console.WriteLine("Received response:{0}{1}", Environment.NewLine, rsp.Data);
        }
        #endregion
    }
}