Wednesday, June 28, 2023

Modality Performed Procedure Step

Introduction


After the post on Modality Worklist, I felt that it wouldn’t be a complete without explanation on Modality Performed Procedure Step. MWL without MPPS is like a task list without checkboxes, and after all, striking a checkbox on a completed task is great fun. Talking of which, I once red this article about productivity and task lists and since then I’m using a circular checkbox on my paper to do notes because it’s 4 times faster. Instead of 4 lines you only need one. Think of it.

IHE Comes to Rescue


Though the DICOM standard states that it doesn’t go into the details of the implementation and what should be the implications of MPPS on workflow it is very clear from reading the details of the standard that an MPPS is the checkmark of MWL. The gap is closed by IHE radiology technical framework that does a great job and details exactly what should be the workflow and how the implementation should look like. If you are not familiar with IHE, I strongly recommend navigating to their web site and start digging. Getting familiar with the IHE Technical Frameworks can save a lot of expensive software architect hours and more important, save you from implementing things wrong. The IHE TF is high quality software specification document that you can use almost as is for your healthcare IT software projects.


Anyway, if you don’t have time to dig inside the long documents of IHE and DICOM and HL7, here’s a short data and program flow summary:

  1. The modality makes a MWL Query. Each result is a requested procedure object with one or more Scheduled Procedure Steps (SPS).
  2. The user picks one SPS to perform.  
  3. The modality creates a new Modality Performed Procedure Step (MPPS) that references the Study, the requested procedure, and the SPS.  This is done using the N-CREATE command. 
  4. There’s a state machine for MPPS with three states:
    1. In Progress (A dot at the center of the circular checkbox)
    2. Completed (A dash on the checkbox)
    3. Discontinued (Back to the beginning)
  5. After the images acquisition is done the modality sends an updated status for the MPPS using N-SET command. The N-SET must include a performed series sequence with at least one series in it, even if the procedure was aborted (in which case the series will have no images).
  6. At this point the Scheduler should dash the checkbox to mark the task as completed (or discontinued).
  7. Though usually you would have a 1-to-1 relationship between a scheduled procedure and a performed procedure, the DICOM data model has a n-to-m relationship between SPS and MPPS. The connection is made by the MPPS that references the SPS that it was performed for.

The DIMSE-N Protocol


Unlike all the other command that we’ve discussed so far in this tutorial namely C-ECHO, C-STORE, C-FIND and C-MOVE that are DIMSE-C commands, MPPS uses the normalized, DIMSE-N protocol commands N-CREATE and N-SET to create and update the Modality Performed Procedure Step normalized  information entity. We’ve discussed the normalized data model (aka DICOM Model of the Real World) briefly in chapter 4 when discussing DICOM Objects and stating that image objects are composites of modules from different information entities.
Like, in MWL before, here’s where MPPS fits into the DICOM Data Model:
A Study is comprised of one or more Modality Performed Procedure Steps.
A Modality Performed Procedure Step includes one or more Series.
(Series in turn contains one or more composite objects such as images).
The complete data model can be found at the beginning of chapter 3 of the DICOM standard.
What’s important to remember out of this ERD is that the MPPS is a child of the Study and parent of Series. Note that Series is still a child of a study; it is just that it can have a MPPS parent as well.
 If we look at workflow the sequencing is:
1. An order generates a Study (thru HL7 usually).
2. The Modality gets the Study Instance UID (by making a MWL query) and creates a MPPS.
3. The Modality creates new Series and updates the MPPS.
Practically, the Study Instance UID is created by the PACS/RIS and the Series Instance UID by the modality.

Programming a MPPS Client

The code for the following MPPS client can be found in MODALIZER-SDK Example projects. Try it out.

Let’s do some coding now and see how it all combines together. There are already two examples of MPPS in the DICOM example applications of MODALIZER-SDK, one in C++ and one in C# that you can download. These examples are for unsolicited study because the MPPS is created with no referenced SPS. In this post I want to continue the example of last post on MWL and add MPPS to it so we will start with the MWL client example.
As a start we’ll add three buttons to create complete and discontinue the MPPS. We will create a MPPS for the selected SPS in the lower grid the user will do the following:
1. Fill the MWL Query filters
2. Click MWL Query Button
3. Select an item from the upper grid holding the requested procedure
4. Select an item from the lower grid holding the SPS
5. Click the MPPS Start button. This will add a row to the MPPS grid at the bottom
6. Select a row from the MPPS grid
7. Click either Complete or Abort
Here’s a screenshot of the new MWL  test application after adding MPPS.



The major changes are at the lower part of the form. The start button, the new grid with the MPPS that we already sent and the two buttons to Complete or Abort the selected MPPS.
This is example has relatively a lot of code but most of it has nothing to do with DICOM. This code is rather related to the data grids and their associated data set and handling all the UI of the application. Because of that, I’ve took out all the MPPS code into a separate class (oddly named MPPS) and we’re going to go over this class now. It is really very simple and it has only two public methods, one for the N-CREATe and one for the N-SET.
In the N-CREATE we create an object with the information from the RP and SPS and then send it using a N-CREATE command. If all goes well we get back from the Server the SOP Instance UID of the newly created MPPS instance.
Here’s the code with lots of greens. This method create the object we are going to send with the minimum set of elements that the standard requires:

private DCXOBJ BuildNCreateObject()
        {
            DCXOBJ ssas = new DCXOBJ();
            DCXELM e = new DCXELM();
            DCXUID uid = new DCXUID();

            // Scheduled Step Attributes Sequence
            // This element hold the list of ID's that identify the SPS and RP that we
            // created this MPPS for

            // Get the STUDT INSTANCE UID from the RP we got from the MWL
            // or create a new one if not found
            e = TryGetElement(rp, DICOM_TAGS_ENUM.studyInstanceUID);
            if (e != null)
                ssas.insertElement(e);
            else
            {
                // Create a new UID
                e.Init((int)DICOM_TAGS_ENUM.studyInstanceUID);
                e.Value = uid.CreateUID(UID_TYPE.UID_TYPE_STUDY);
                ssas.insertElement(e);
            }

            e.Init((int)DICOM_TAGS_ENUM.ReferencedStudySequence);
            ssas.insertElement(e); /// Type 2 (0 length is OK)

            // Get the accession number from the RP. It should always be there
            e = TryGetElement(rp, DICOM_TAGS_ENUM.AccessionNumber);
            if (e != null)
                ssas.insertElement(e);

            // Get the RP ID and add it
            e = TryGetElement(rp, DICOM_TAGS_ENUM.RequestedProcedureID);
            if (e != null)
                ssas.insertElement(e);

            // Get the RP description
            e = TryGetElement(rp, DICOM_TAGS_ENUM.RequestedProcedureDescription);
            if (e != null)
                ssas.insertElement(e);

            // Get the SPS ID from the SPS object we got from the MWL
            e = TryGetElement(sps, DICOM_TAGS_ENUM.ScheduledProcedureStepID);
            if (e != null)
                ssas.insertElement(e);

            // SPS description
            e = TryGetElement(sps, DICOM_TAGS_ENUM.ScheduledProcedureStepDescription);
            if (e != null)
                ssas.insertElement(e);

            // If we have codes, not only text description
            e.Init((int)DICOM_TAGS_ENUM.ScheduledProtocolCodeSequence);
            ssas.insertElement(e); /// Type 2 (0 length is OK)

            // Add the Scheduled Step item to a sequence
            DCXOBJIterator sq = new DCXOBJIterator();
            sq.Insert(ssas);

            e.Init((int)DICOM_TAGS_ENUM.ScheduledStepAttributesSequence);
            e.Value = sq;

            ///
            /// Performed Procedure Step Object
            ///

            DCXOBJ pps = new DCXOBJ();

            // Add the Scheduled Step sequence to the MPPS object
            pps.insertElement(e);

            // Add Patient name
            e = TryGetElement(rp, DICOM_TAGS_ENUM.PatientsName);
            if (e != null)
                pps.insertElement(e);

            // Add Patient ID
            e = TryGetElement(rp, DICOM_TAGS_ENUM.patientID);
            if (e != null)
                pps.insertElement(e);

            // Add birth date null
            e.Init((int)DICOM_TAGS_ENUM.PatientsBirthDate);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            // Add sex null
            e.Init((int)DICOM_TAGS_ENUM.PatientsSex);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            // Referenced Patient Seq.
            e.Init((int)DICOM_TAGS_ENUM.ReferencedPatientSequence);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            // MPPS ID has not logic on it. It can be anything
            // The SCU have to create it but it doesn't have to be unique and the SCP
            // should not relay on its uniqueness
            // Here we use a timestamp
            e.Init((int)DICOM_TAGS_ENUM.PerformedProcedureStepID);
            e.Value = DateTime.Now.ToString("yyyymmddhhmmssttt");
            pps.insertElement(e);

            // Performed station AE title - this identify the modality that
            // did the work
            e.Init((int)DICOM_TAGS_ENUM.PerformedStationAETitle);
            e.Value = aeTitle;
            pps.insertElement(e);

            // A logical name of the station
            e.Init((int)DICOM_TAGS_ENUM.PerformedStationName);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            // The location
            e.Init((int)DICOM_TAGS_ENUM.PerformedLocation);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            // Start date and time - let's use 'Now'
            e.Init((int)DICOM_TAGS_ENUM.PerformedProcedureStepStartDate);
            e.Value = DateTime.Now;
            pps.insertElement(e);

            e.Init((int)DICOM_TAGS_ENUM.PerformedProcedureStepStartTime);
            e.Value = DateTime.Now;
            pps.insertElement(e);

            // This is important! The initial state is "IN PROGRESS"
            e.Init((int)DICOM_TAGS_ENUM.PerformedProcedureStepStatus);
            e.Value = "IN PROGRESS";
            pps.insertElement(e);
           
            // Description, we can set it later as well in the N-SET
            e.Init((int)DICOM_TAGS_ENUM.PerformedProcedureStepDescription);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            // Some more type 2 elements ...

            e.Init((int)DICOM_TAGS_ENUM.PerformedProcedureTypeDescription);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            e.Init((int)DICOM_TAGS_ENUM.ProcedureCodeSequence);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            e.Init((int)DICOM_TAGS_ENUM.PerformedProcedureStepEndDate);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            e.Init((int)DICOM_TAGS_ENUM.PerformedProcedureStepEndTime);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            // Modality - it's a type 1
            e.Init((int)DICOM_TAGS_ENUM.Modality);
            if (modality != null && modality.Length > 0)
                e.Value = modality;
            else
                e.Value = "OT";
            pps.insertElement(e);

            // More type 2 elements
            e.Init((int)DICOM_TAGS_ENUM.StudyID);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            e.Init((int)DICOM_TAGS_ENUM.PerformedProtocolCodeSequence);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            e.Init((int)DICOM_TAGS_ENUM.PerformedSeriesSequence);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            return pps;
        }

After creating the object we can send it using DCXREQ.MPPS_Create and get the SOP Instance UID back. Note the standard also allows the SCU to create this UID. IHE set  this as the SCP responsibility for MPPS.


public void Create(NetConnectionInfo connInfo)
        {
            DCXOBJ createObj = BuildNCreateObject();
            ///
            /// Send the N-CREATE command
            ///
            DCXREQ req = new DCXREQ();
            this.SOPInstanceUID =
                req.MPPS_Create(
                connInfo.CallingAETitle,
                connInfo.CalledETitle,
                connInfo.Host,
                connInfo.Port,
                createObj);
        }


After this is done, we add an item to the MPPS grid that also safe keep the MPPS instance for us in this application.
The next phase is done after the acquisition is done.



private DCXOBJ BuildNSetObject(bool completed)
        {
            DCXOBJ pps = new DCXOBJ();
            DCXELM e = new DCXELM();

            ///
            /// Performed Procedure Step
            ///

            // These are the elements we can update in the N-SET

            // Set the status to "COMPLETED" or "DISCONTINUED"
            e.Init((int)DICOM_TAGS_ENUM.PerformedProcedureStepStatus);
            e.Value = completed ? "COMPLETED" : "DISCONTINUED";
            pps.insertElement(e);

            // End date and time
            e.Init((int)DICOM_TAGS_ENUM.PerformedProcedureStepEndDate);
            e.Value = DateTime.Now;
            pps.insertElement(e);

            e.Init((int)DICOM_TAGS_ENUM.PerformedProcedureStepEndTime);
            e.Value = DateTime.Now;
            pps.insertElement(e);

            // More type 2 elements that we are allowed to change in the N-SET
            e.Init((int)DICOM_TAGS_ENUM.PerformedProcedureStepDescription);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            e.Init((int)DICOM_TAGS_ENUM.PerformedProcedureTypeDescription);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            e.Init((int)DICOM_TAGS_ENUM.ProcedureCodeSequence);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            e.Init((int)DICOM_TAGS_ENUM.PerformedProtocolCodeSequence);
            pps.insertElement(e); /// Type 2 (0 length is OK)

            ///
            /// Performed Series Sequence
            ///  - Must have at least one item even if discontinued!
            ///
            DCXOBJ series_item = new DCXOBJ();
            e.Init((int)DICOM_TAGS_ENUM.PerformingPhysiciansName);
            series_item.insertElement(e);

            e.Init((int)DICOM_TAGS_ENUM.ProtocolName);
            e.Value = "SOME PROTOCOL";
            series_item.insertElement(e);

            e.Init((int)DICOM_TAGS_ENUM.OperatorsName);
            series_item.insertElement(e);

            e.Init((int)DICOM_TAGS_ENUM.seriesInstanceUID);
            e.Value = "1.2.3.4.5.6";
            series_item.insertElement(e);

            e.Init((int)DICOM_TAGS_ENUM.SeriesDescription);
            series_item.insertElement(e);

            e.Init((int)DICOM_TAGS_ENUM.RetrieveAETitle);
            series_item.insertElement(e);

            e.Init((int)DICOM_TAGS_ENUM.ReferencedImageSequence);
            series_item.insertElement(e);

               e.Init((int)DICOM_TAGS_ENUM.ReferencedNonImageCompositeSOPInstanceSequence);
            series_item.insertElement(e);

            DCXOBJIterator series_sq = new DCXOBJIterator();
            series_sq.Insert(series_item);
            e.Init((int)DICOM_TAGS_ENUM.PerformedSeriesSequence);
            e.Value = series_sq;
            pps.insertElement(e);

            return pps;
        }

To send the N-SET we have to keep the SOP Instance UID from the N-CREATE and use it. The SOP Instance UID of the MPPS is not part of the object. It is send using the Affected SOP Instance UID of the N-SET command. In the N-CREATE it is returned from the SCP in the N-CREATE response in the same manner.

public void Set(bool completed, NetConnectionInfo connInfo)
        {
            DCXOBJ ppsSet = BuildNSetObject(completed);
            ///
            /// Send the N-SET
            ///
            DCXREQ req = new DCXREQ();
            req.MPPS_Set(
                connInfo.CallingAETitle,
                connInfo.CalledETitle,
                connInfo.Host,
                connInfo.Port,
                ppsSet,
                SOPInstanceUID);
            State = completed ? PPSState.COMPLETED : PPSState.DICOUNTINUED;
        }
This reminds me, I didn't mention the SOP Class UID of the MPPS N-SET and N-CREATE command. Can you guess what it is? Surprise! It’s the MWL SOP Class UID: 1.2.840.10008.3.1.2.3.3
That’s it for today. 

As always, comments and questions are most welcome

33 comments:

  1. HI Roni,
    I was trying to run your MPPS source code. But I am getting an error saying "Unable to find manifest signing certificate in the certificate store.". Can you please explain what should be done to resolve this??

    ReplyDelete
    Replies
    1. Hi
      Just remove the certificate file from the project or disable the signing option in the build settings.
      Roni

      Delete
    2. Friend hello, blogger source link failure, could you send me a copy of it? Thank you very much

      Delete
    3. Friend hello, blogger source link failure, could you send me a copy of it? Thank you very much

      Delete
  2. Hi Roni,
    the DICOM standard (Part 04, Annex F) states that the SCU shall set the status of the Scheduled Procedure Step to "IN PROGRESS" when it starts working on it. How can I do that? It doesn't seem to work with req.MPPS_Set(...) since I don't get the SOPInstanceUID of the Scheduled Procedure Step.

    ReplyDelete
  3. Hi Roni,
    I converted the C sharp code you posted for MWL and MPPS. Even the code compiled correctly except for the line in "MWL QUERY" button,

    req.OnQueryResponseRecieved += new IDCXREQEvents_OnQueryResponseRecievedEventHandler(OnQueryResponseRecievedAction);

    stating + operator is not defined for IDCXREQEvents_OnQueryResponseRecievedEventHandler(AddressOf OnQueryResponseRecievedAction)

    So I just removed the + operator and compiled the code.

    Now when I run the SCU and run my code, I am getting an exception stating
    "QUERY Failed: 'OnQueryResponseRecieved' on type 'DCXREQClass'not found."

    I am unable to find and resolve the issue.Can you please state what could be the issue and what can be done to solve it.

    The same code in C sharp which you posted is performing MWL Query correctly.


    ReplyDelete
    Replies
    1. Please download the sample applications from techzone.roniza.com/products/downloads and look at the C++ examples of Q/R SCU and Storage SCP.
      There's a class there called sink that implements the connection points that you should have something like.

      Delete
  4. I have a query about listening ports of MWL SCP and MPPS SCP.
    Should those two SCPs work on same port?
    Or it is fine if those two listen on two different ports?
    I did some research and found that most of the SCUs allow configuring those two SCPs separately. But I am not certain if ALL the SCUs follow same practice.
    Please advice.

    ReplyDelete
  5. Hi,

    I installed the DSRSVC server and the DicomDBPlugin.
    I was trying to run your MPPS application with this server, on port 104.
    It's ok for mwl Query but not for mpps. I have this error :
    "SOP Class not negociated, not proposed ot not accepted. SOP CLASS UID is : 1.2.840.10008.3.1.2.3.3"

    Can you help me please ?

    (callingAE and calledAE are on the same post, host = localhost)

    ReplyDelete
    Replies
    1. please email service@roniza.com for help and support

      Delete
  6. Hi,
    really a helpful article. i appreciate your effort.
    i have been working on modality worklist SCP and have implemented it. Now i want to delete worklist entry from modality which i sent to it. Is there a way through MPPS SCP or any other.

    ReplyDelete
    Replies
    1. Friend hello, blogger source link failure, could you send me a copy of it? Thank you very much

      Delete
  7. Hi Roni,
    really helpful article. I appreciate your effort.
    i am working on modality worklist SCP and have implemented it. Now i want to delete worklist entry which i sent to modality. Is there a way to do it witth MPPS SCP or any other. Kindly explain

    ReplyDelete
    Replies
    1. Hi. Thx. To Delete, not something widely implemented. To mark as completed using MPPS SET status to COMPLETED.

      Delete
    2. Friend hello, blogger source link failure, could you send me a copy of it? Thank you very much

      Delete
  8. The links to all the ready applications in this series are dead, could you update the links?

    ReplyDelete
  9. Blogger hello, saw your article write very well, but access to the source link failure, can you send a again, please? Thank you very much

    ReplyDelete
  10. Hi all
    download links are all broken

    ReplyDelete
  11. downd links are broken
    kindly fix

    ReplyDelete
  12. Hi Roni,
    I have created MWL query using DCMTK, MPPS SCU using DCM4CHEE toolkit. I am using your worklist manager as SCP. I have successfully get the patient list and successfully send the status(IN PROGRESS, COMPLETED, DISCONTINUED) to worklist manager. Worklist manager changes its status. But my problem is I get all the patient lists including performed lists from worklist manager.

    ReplyDelete
    Replies
    1. That's maybe because the CFIND_MWL_VIEW is of older version and doesn't take into account status of MPPS.

      Here are the SQL scripts that takes that into account:

      CREATE TABLE [MPPS] (
      [id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
      [sop_instance_uid] [nvarchar](64) NOT NULL,
      [patient_name] [nvarchar](64) NULL,
      [patient_id] [nvarchar](64) NULL,
      [patient_sex] [nchar](1) NULL,
      [patient_birth_date] [datetime] NULL,
      [pps_id] [nvarchar](16) NOT NULL,
      [performed_station_ae_title] [nvarchar](16) NULL,
      [performed_station_name] [nvarchar](16) NULL,
      [performed_location] [nvarchar](16) NULL,
      [pps_start_datetime] [datetime] NULL,
      [pps_status] [nvarchar](16) NOT NULL,
      [pps_desc] [nvarchar](64) NULL,
      [performed_procedure_type_desc] [nvarchar](64) NULL,
      [pps_end_datetime] [datetime] NULL,
      [modality] [nvarchar](16) NULL,
      [study_id] [nvarchar](16) NULL,
      [rel_path] [nvarchar](400) NOT NULL,
      )
      GO

      CREATE TABLE [MPPS_SPS](
      [id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
      [mpps_id] [int] NOT NULL FOREIGN KEY REFERENCES MPPS(id) ON DELETE CASCADE,
      [study_instance_uid] [nvarchar](64) NOT NULL,
      [accession_number] [nvarchar](16) NULL,
      [requested_procedure_id] [nvarchar](50) NULL,
      [requested_procedure_desc] [nvarchar](400) NULL,
      [sps_id] [nvarchar](16) NULL,
      [sps_desc] [nvarchar](400) NULL,
      )
      GO

      CREATE VIEW [dbo].[AWAITING_REQUESTED_PROCEDURES_VIEW]
      AS
      SELECT dbo.REQUESTED_PROCEDURE.id, dbo.REQUESTED_PROCEDURE.study_instance_uid, dbo.REQUESTED_PROCEDURE.req_proc_desc, dbo.REQUESTED_PROCEDURE.req_proc_id, dbo.REQUESTED_PROCEDURE.service_request_id, dbo.REQUESTED_PROCEDURE.patient_internal_id,
      dbo.REQUESTED_PROCEDURE.RequestedProcedureCodeSequence
      FROM dbo.REQUESTED_PROCEDURE LEFT OUTER JOIN
      dbo.MPPS_SPS ON dbo.REQUESTED_PROCEDURE.study_instance_uid = dbo.MPPS_SPS.study_instance_uid
      WHERE (dbo.MPPS_SPS.study_instance_uid IS NULL)
      GO

      -- Change the MWL view to take only open procedures
      ALTER VIEW [dbo].[CFIND_MWL_VIEW]
      AS
      SELECT dbo.AWAITING_REQUESTED_PROCEDURES_VIEW.study_instance_uid AS StudyInstanceUID, dbo.AWAITING_REQUESTED_PROCEDURES_VIEW.req_proc_desc AS RequestedProcedureDescription, dbo.AWAITING_REQUESTED_PROCEDURES_VIEW.id AS ScheduledProcedureStepSequence,
      dbo.AWAITING_REQUESTED_PROCEDURES_VIEW.req_proc_id AS RequestedProcedureID, dbo.SERVICE_REQUEST.accession_number AS AccessionNumber, dbo.SERVICE_REQUEST.referring_physicians_names AS ReferringPhysicianName, dbo.SERVICE_REQUEST.requesting_physician AS RequestingPhysician, dbo.N_PATIENT.patient_id AS PatientID,
      dbo.N_PATIENT.patient_name AS PatientName, CONVERT(char(8), dbo.N_PATIENT.birth_date, 112) AS PatientBirthDate, dbo.N_PATIENT.sex AS PatientSex, NULL AS PatientWeight, NULL AS MedicalAlerts, NULL AS Allergies, NULL AS PregnancyStatus, NULL AS RequestedContrastAgent, NULL AS AdmissionID, NULL AS SpecialNeeds, NULL
      AS CurrentPatientLocation, NULL AS PatientState, NULL AS RequestedProcedurePriority, NULL AS PatientTransportArrangements, NULL AS ConfidentialityConstraintOnPatientDataDescription, dbo.SERVICE_REQUEST.requesting_service
      FROM dbo.AWAITING_REQUESTED_PROCEDURES_VIEW INNER JOIN
      dbo.SERVICE_REQUEST ON dbo.AWAITING_REQUESTED_PROCEDURES_VIEW.service_request_id = dbo.SERVICE_REQUEST.id INNER JOIN
      dbo.N_PATIENT ON dbo.AWAITING_REQUESTED_PROCEDURES_VIEW.patient_internal_id = dbo.N_PATIENT.id

      GO

      Delete
  13. Hi Roni,
    I have created MWL using DCMTK and MPPSSCU using DCM4CHEE. I am using your worklist manager application as SCP. I have successfully get the patient list from worklist manager and send the Status (IN PROGRESS, COMPLETED, DISCONTINUED) to worklist manager.The worklist manager also update the status corresponding to my SCU. But next time I have queried to worklist manager, it sends all patients list including which I was already performed and send the status(COMPLETED, DISCONTINUED) to worklist manager.Can you please tell me to recover the problem?

    ReplyDelete
  14. This comment has been removed by the author.

    ReplyDelete
  15. Thank you for your response Roni, I have found my problem and solved it.

    ReplyDelete
  16. Does the modality have to be somehow configured or cleared in the PACS server to be able to create a MPPS and send its images? How does the server manage the modalities that can send objects or use another service from the PACS?

    And congratulations on the site! Keep it up

    ReplyDelete
  17. Thanks. Good question. So the answer is yes and no. No because both MWL and MPPS (and storage too) are initiated by the SCU (the client) so if the PACS (SCP) will let any AE title connect, then no pre configuration is required. Yes because it does make sense to control who can and who can’t connect so implementations tend to require that the AE title is pre configured.

    ReplyDelete
  18. Many systems take this further and allow configuring which services are allowed for every AE by means of mapping for each AE the set of SOP classes/presentation contexts that will be accepted for it

    ReplyDelete
  19. Hello,
    where is TryGetElement defined?

    Stephen

    ReplyDelete
  20. Hello,
    Thank you for your posting on DICOM. Now that I want to get the program running, how can i download it?
    the link on the post seems to be broken.
    Thank you.

    ReplyDelete
  21. Hi Roni,
    Unable to download programs. getting message that "Site can't be reached"
    could you please send me a download link at naseemhaider@gmail.com
    Thanks

    ReplyDelete