DICOM Basics using .NET and C# - Understanding Worklists and MPPS

Introduction

This is part of my series of articles on the DICOM standard. In this tutorial, we'll explore two related DICOM services that are essential for radiology workflow: Modality Worklist (MWL) and Modality Performed Procedure Step (MPPS).

The Modality Worklist service allows imaging devices (modalities) to retrieve scheduled procedure information from a hospital information system, eliminating manual data entry errors. MPPS complements this by allowing modalities to report back the status of procedures as they are performed.

Prerequisites

Before you begin, ensure you have the following:

  • A .NET development environment (Visual Studio or Visual Studio Code)
  • The Fellow Oak DICOM library (fo-dicom) installed via NuGet
  • An MWL Server: Orthanc with Worklist plugin or DCM4CHEE
  • You can find all the code demonstrated in this tutorial on GitHub here

“The best way to predict the future is to create it.” ~ Peter Drucker

The Theory Behind Worklist and MPPS

The Modality Worklist exists to solve the Master Patient Index (MPI) problem at the point of imaging. Before MWL, technologists manually entered patient demographics at the modality console, leading to typos, misidentifications, and inconsistencies. Studies could become orphaned or misattributed. MWL provides a single source of truth: the scheduling system (RIS/HIS) that already verified patient identity during registration.

From an information architecture perspective, MWL implements data replication at the point of need. Rather than requiring modalities to have real-time access to patient databases (which would create tight coupling and availability concerns), MWL uses a pull-based query model. The modality queries only when needed, receiving a snapshot of relevant scheduled procedures. This loose coupling allows modalities to operate semi-independently while still receiving authoritative patient data.

The Scheduled Procedure Step Sequence reflects the reality that a single order can involve multiple imaging steps. A CT exam might include scout images, contrast phases, and delayed images. Each step has its own scheduling attributes while sharing patient and study context. This hierarchical model (Study → Requested Procedure → Scheduled Procedure Step) enables complex multi-step protocols while maintaining traceability to the original order.

MPPS addresses the bidirectional synchronization problem. While MWL pushes scheduling information to modalities, MPPS closes the loop by reporting what actually happened back to the information system. This is crucial because scheduled procedures don't always proceed as planned: exams may be cancelled, additional images may be acquired, or different protocols may be used. MPPS provides the performed reality to contrast with the scheduled intent.

The MPPS state model (IN PROGRESS → COMPLETED or DISCONTINUED) implements a finite state machine that captures the exam lifecycle. N-CREATE signals the start (state transition to IN PROGRESS), while N-SET can update attributes during the exam and signal completion. This state machine enables real-time tracking of exam status across the enterprise.

Understanding Modality Worklist

The Modality Worklist (MWL) is a DICOM service that allows modalities to query for scheduled procedures. Here's a typical workflow:

  1. Patient arrives for scheduled imaging exam
  2. Technologist queries the worklist from the modality
  3. Worklist returns matching scheduled procedures
  4. Technologist selects the correct procedure
  5. Patient demographics are automatically populated
  6. Imaging begins with correct patient and study information

MWL SOP Class and Key Attributes

Modality Worklist uses the C-FIND operation with a specific SOP Class:

SOP ClassUID
Modality Worklist Information Model - FIND1.2.840.10008.5.1.4.31

Key query attributes include:

Patient Level:

TagNameDescription
(0010,0010)Patient NamePatient’s full name
(0010,0020)Patient IDHospital patient identifier
(0010,0030)Patient Birth DateDate of birth
(0010,0040)Patient SexM, F, or O

Study/Procedure Level:

TagNameDescription
(0008,0050)Accession NumberUnique order identifier
(0020,000D)Study Instance UIDUnique study identifier
(0032,1060)Requested Procedure DescriptionDescription of the procedure
(0040,1001)Requested Procedure IDProcedure identifier

Scheduled Procedure Step Sequence (0040,0100):

TagNameDescription
(0008,0060)ModalityCT, MR, US, etc.
(0040,0001)Scheduled Station AE TitleTarget modality AE title
(0040,0002)Scheduled Procedure Step Start DateScheduled date
(0040,0003)Scheduled Procedure Step Start TimeScheduled time
(0040,0007)Scheduled Procedure Step DescriptionStep description

Step 1 of 3: Creating the MWL Query

Let's implement a Modality Worklist query using fo-dicom:

using System;
using System.Diagnostics;
using FellowOakDicom;
using System.Threading;
using System.Threading.Tasks;
using FellowOakDicom.Network;
using System.Threading;
using System.Threading.Tasks;
using FellowOakDicom.Network.Client;

namespace ModalityWorklistQuery
{
    public class Program
    {
        private static readonly string DicomServerHost = "localhost";
        private static readonly int DicomServerPort = 4242;
        private static readonly string RemoteAeTitle = "ORTHANC";
        private static readonly string LocalAeTitle = "FODICOM_MWL";
        private static readonly bool UseTls = false;

        public static async Task Main(string[] args)
        {
            try
            {
                LogToDebugConsole("=== Modality Worklist Query Tutorial ===");
                LogToDebugConsole($"  MWL Server:  {DicomServerHost}:{DicomServerPort}");
                LogToDebugConsole($"  Remote AE:   {RemoteAeTitle}");
                LogToDebugConsole($"  Local AE:    {LocalAeTitle}");

                // Create and send MWL query
                var client = CreateMwlClient();
                LogToDebugConsole("Sending Modality Worklist query...");
                await client.SendAsync(CancellationToken.None);

                LogToDebugConsole("Modality Worklist query completed.");
            }
            catch (Exception e)
            {
                LogToDebugConsole($"Error: {e.Message}");
            }
        }

        private static void LogToDebugConsole(string message)
        {
            Debug.WriteLine(message);
        }
    }
}

Step 2 of 3: Building the Query Request

Now let's build the C-FIND request with appropriate return keys:

private static DicomClient CreateMwlClient()
{
    var client = DicomClientFactory.Create(DicomServerHost, DicomServerPort, UseTls, LocalAeTitle, RemoteAeTitle);

    // Create MWL C-FIND request
    var request = DicomCFindRequest.CreateWorklistQuery();

    // Patient Level Return Keys
    request.Dataset.AddOrUpdate(DicomTag.PatientName, "");
    request.Dataset.AddOrUpdate(DicomTag.PatientID, "");
    request.Dataset.AddOrUpdate(DicomTag.PatientBirthDate, "");
    request.Dataset.AddOrUpdate(DicomTag.PatientSex, "");

    // Study/Procedure Level Return Keys
    request.Dataset.AddOrUpdate(DicomTag.AccessionNumber, "");
    request.Dataset.AddOrUpdate(DicomTag.StudyInstanceUID, "");
    request.Dataset.AddOrUpdate(DicomTag.RequestedProcedureDescription, "");
    request.Dataset.AddOrUpdate(DicomTag.RequestedProcedureID, "");

    // Scheduled Procedure Step Sequence (required for MWL)
    var scheduledProcedureStep = new DicomDataset();
    scheduledProcedureStep.Add(DicomTag.Modality, "");
    scheduledProcedureStep.Add(DicomTag.ScheduledStationAETitle, "");
    scheduledProcedureStep.Add(DicomTag.ScheduledProcedureStepStartDate, "");
    scheduledProcedureStep.Add(DicomTag.ScheduledProcedureStepStartTime, "");
    scheduledProcedureStep.Add(DicomTag.ScheduledProcedureStepDescription, "");
    scheduledProcedureStep.Add(DicomTag.ScheduledProcedureStepID, "");

    request.Dataset.AddOrUpdate(
        new DicomSequence(DicomTag.ScheduledProcedureStepSequence, scheduledProcedureStep));

    // Optional: Filter by today's date
    // scheduledProcedureStep.AddOrUpdate(DicomTag.ScheduledProcedureStepStartDate,
    //     DateTime.Today.ToString("yyyyMMdd"));

    // Optional: Filter by modality
    // scheduledProcedureStep.AddOrUpdate(DicomTag.Modality, "CT");

    // Attach response handler
    request.OnResponseReceived += OnMwlResponseReceived;

    await client.AddRequestAsync(request);

    // Add association event handlers
    client.AssociationAccepted += (s, e) =>
        LogToDebugConsole($"Association accepted by: {e.Association.RemoteHost}");
    client.AssociationRejected += (s, e) =>
        LogToDebugConsole($"Association rejected: {e.Reason}");
    client.AssociationReleased += (s, e) =>
        LogToDebugConsole("Association released.");

    return client;
}

Step 3 of 3: Handling the Response

Process the MWL response to display scheduled procedures:

private static void OnMwlResponseReceived(DicomCFindRequest request, DicomCFindResponse response)
{
    if (response.Status == DicomStatus.Pending)
    {
        LogToDebugConsole("--- Scheduled Procedure Found ---");
        LogToDebugConsole($"  Patient Name: {response.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, "")}");
        LogToDebugConsole($"  Patient ID:   {response.Dataset.GetSingleValueOrDefault(DicomTag.PatientID, "")}");
        LogToDebugConsole($"  Accession #:  {response.Dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, "")}");
        LogToDebugConsole($"  Procedure:    {response.Dataset.GetSingleValueOrDefault(DicomTag.RequestedProcedureDescription, "")}");

        // Extract Scheduled Procedure Step information
        var sps = response.Dataset.GetSequence(DicomTag.ScheduledProcedureStepSequence);
        if (sps != null && sps.Items.Count > 0)
        {
            var spsItem = sps.Items[0];
            LogToDebugConsole($"  Modality:     {spsItem.GetSingleValueOrDefault(DicomTag.Modality, "")}");
            LogToDebugConsole($"  Scheduled:    {spsItem.GetSingleValueOrDefault(DicomTag.ScheduledProcedureStepStartDate, "")} {spsItem.GetSingleValueOrDefault(DicomTag.ScheduledProcedureStepStartTime, "")}");
        }
        LogToDebugConsole("");
    }

    if (response.Status == DicomStatus.Success)
    {
        LogToDebugConsole("--- MWL Query Complete ---");
    }
}

Sample output from a worklist query:

=== Modality Worklist Query Tutorial ===
  MWL Server:  localhost:4242
  Remote AE:   ORTHANC
  Local AE:    FODICOM_MWL
Sending Modality Worklist query...
Association accepted by: localhost

--- Scheduled Procedure Found ---
  Patient Name: Smith^John
  Patient ID:   PAT001
  Accession #:  ACC12345
  Procedure:    CT Chest with Contrast
  Modality:     CT
  Scheduled:    20250121 093000

--- Scheduled Procedure Found ---
  Patient Name: Johnson^Mary
  Patient ID:   PAT002
  Accession #:  ACC12346
  Procedure:    CT Abdomen/Pelvis
  Modality:     CT
  Scheduled:    20250121 100000

--- MWL Query Complete ---
Association released.
Modality Worklist query completed.

Understanding MPPS (Modality Performed Procedure Step)

MPPS allows modalities to report procedure status back to the information system. It uses N-CREATE and N-SET operations:

OperationWhen Used
N-CREATE (IN PROGRESS)When procedure begins
N-SET (COMPLETED)When procedure completes successfully
N-SET (DISCONTINUED)When procedure is stopped/cancelled

The MPPS SOP Class is:

SOP ClassUID
Modality Performed Procedure Step1.2.840.10008.3.1.2.3.3

Orthanc Worklist Configuration

To test MWL with Orthanc, enable the Worklist plugin and create .wl files:

{
  "Plugins": ["libOrthancWorklistsPlugin.so"],
  "WorklistsDatabase": "/path/to/worklists"
}

Create worklist files (.wl) containing DICOM datasets with scheduled procedure information.

Best Practices

  • Always filter queries by date to reduce result set size
  • Filter by modality type when querying from a specific device
  • Implement caching for frequently accessed worklist data
  • Handle network timeouts and implement retry logic
  • Log all worklist interactions for troubleshooting

Conclusion

Modality Worklist and MPPS are essential DICOM services for integrating imaging modalities with hospital information systems. MWL eliminates manual data entry at the modality, reducing errors and improving workflow efficiency. MPPS provides real-time procedure status updates back to the scheduling system.

Together, these services form the foundation of the IHE Scheduled Workflow profile, which is widely implemented in healthcare facilities worldwide. Understanding and implementing these services is crucial for anyone developing medical imaging integration solutions.

Please check out the next tutorial in this series where we cover DICOM Structured Reports.