In part I of this post, I was in a meeting with a customer reviewing their workstation code and while sitting there I was thinking to myself, why should my customers have to deal with so many details of the DICOM Q/R Service when all they really want is to retrieve a study just like they would have downloaded a zip file from a web site. And thus, later, back in my office I decided to extended the DICOM Toolkit API to include a C-MOVE method that will take care of everything including the incoming association. In today’s post I’m going to use the new MoveAndStore method to talk about the DICOM Query/Retrieve service. We’ll start at the end and then work our way backwards.
C-MOVE is a DICOM command that means this: The calling AE (we) ask the called AE (the PACS) to send all the DICOM Instances that match the identifier to the target AE.
Here’s how you ask a PACS to send you the DICOM images with RZDCX (version 2.0.1.9).
public void MoveAndStore()
{
// Create an object with the query
matching criteria (Identifier)
DCXOBJ query = new DCXOBJ();
DCXELM e = new DCXELM();
e.Init((int)DICOM_TAGS_ENUM.patientName);
e.Value = DOE^JOHN";
query.insertElement(e);
e.Init((int)DICOM_TAGS_ENUM.patientID);
e.Value = @"123456789";
query.insertElement(e);
// Create an accepter to handle the
incomming association
DCXACC accepter = new
DCXACC();
accepter.StoreDirectory = @".\MoveAndStore";
Directory.CreateDirectory(accepter.StoreDirectory);
// Create a requester and run the query
DCXREQ requester = new
DCXREQ();
requester.MoveAndStore(
MyAETitle, // The AE title
that issue the C-MOVE
IS_AE, // The PACS AE title
IS_Host, // The PACS IP address
IS_port, // The PACS listener port
MyAETitle, // The AE title
to send the
query, // The matching criteria
104, // The port to receive the results
accepter); // The accepter
to handle the results
}
Behind this rather short function hides a lot of DICOM networking and when it returns we should have all the matching objects stored in the directory “.\MoveAndStore”. Readers with some practical DICOM experience probably expect me to say that it can also fail. In that case MoveAndStore throws an exception with the error code and description. Sometimes you would have to set the detailed logging on and start reading logs like we did in chapter 5 of this tutorial on DICOM networking and in some later post we will look together at a DICOM log of a Q/R transaction.
The following diagram, taken from part 2 of the DICOM standard, is commonly seen in DICOM Conformance Statements as the Data Flow diagram of the Q/R Service. These diagrams and their notation are defined by the standard in part 2 that specify the DICOM Conformance Statement – a standard document that every application vendor should provide and that describes how they implemented the standard in their product. At some point we will get to how to read and write these documents.
The vertical dashed line represents the DICOM Protocol Interface between the two applications (it is usually a single dashed line but in this example it got a bit messed up). The arrows accros the interface represents DICOM associations. The arrow points from the application that initiates the association (the requester) to the application that responds to it (the responder or accepter). The upper part of the diagram shows the control chanel where the C-MOVE request is sent and statuses are reported back by the PACS. The lower part of the diagram shows the data chanel where the DICOM instances are sent to the client.
There's a lot of activity behind the scenes of this method:
- The calling AE opens a network connection to the PACS and sends an association request with a Q/R C-MOVE presentation context. This association is like a control chanel of the operation.
- The called AE (The PACS) examines the request and (hopefully) accepts the association and the Q/R C-MOVE presentation context and sends back an association accept primitive.
- The calling AE sends a C-MOVE command with the identifier (the content of the query variable of our function) as a parameter. The C-MOVE command also includes the target AE Title.
- The PACS searches its internal configuration for the target AE Title. This AE must have been previously configured by the PACS administrator because the PACS must resolve the AE Title to IP address and port number of the target AE in order to initiates an association with it.
- The PACS transforms the identifier into a database query, runs the query on its internal database and compose a list with the matching DICOM instances.
- The PACS starts a new association to the target AE requesting the presenation contexts of the objects it intends to send. This association is like a data chanel of the operation.
- The PACS sends the matching instances using C-STORE commands, one C-STORE command for every matching DICOM instance.
- While sending the C-STORE commands on the second association (the data chanel started at step 6) the PACS may send status notifications in the form of C-MOVE responses on the first association (the control chanel started at step 1) with a pending status (0xFF00) and counters of how many instances were already sent and how many are there in total.
- After sending all the instances the PACS closes the second association and sends a C-MOVE response with status success (or failure if something went wrong) and the C-MOVE command ends.
- The calling AE can close the association or send another command.
The PACS (the called AE) who is the responder is acting as the SCP – Service Class Provider, the terms used in DICOM for a server. We (the calling AE) are the requester and are acting as the SCU – Service class user, the term used in DICOM for a client.
The target AE is acting as SCP for the C-STORE commands that the PACS sends. The new MoveAndStore method is intended solely for retreiving instances. To serve unsolisited storage commands, sometimes called DICOM push, we will use the Accepter class DCXACC that implements a DICOM server. We will see this later.
In the first part of the example above we've created a Query object. The rules for this object are almost identical to the ones we've already seen in part I when we've discussed the C-FIND command. The only difference is that we don't add empty elements because the results are DICOM instances sent to us and not records like in C-FIND.
Here are some things to remember and mistakes to avoid:
- The pending C-MOVE responses are optional. The C-MOVE SCP may send pending responses while the transaction is preformed. Remember the may and don't count on these callbacks for anything important, i.e. not more then progress bar and status updates.
- Some PACS will send a pending status after every instance, some will send one every 5 or 10 instances and some will send none.
- Some PACS sends a success response immediately and only then start another associatin and send the resulting instances. This is not a valid implementation of DICOM but you may have to handle it.
- Isolate your DICOM implementation from your application. This is true for every software. Don't mix events from the
It would have been nice to have a progress bar for the Retrieve action as well, right? Let’s add an event handler to the requester whenever a C-MOVE response is received. Here’s how:
The new release of RZDCX has an extended C-MOVE callback that was missing in previous releases. Adding this callback without breaking backwards compatibility is worth a post of its own. Versions prior to 2.0.1.9 have a Boolean parameter that is true as long as the command is going on. The new build (2.0.1.9) reports the command status and the four counters for completed, remaining, failed and warning sub-operations. Sub operations are the C-STORE commands on the data channel. With this callback adding a progress bar is quite easy:
req = new
DCXREQ();
req.OnMoveResponseRecievedEx
+= new IDCXREQEvents_OnMoveResponseRecievedExEventHandler(MoveCallback);
// and now call MoveAndStore just the same
void
req_OnMoveResponseRecievedEx(
ushort status,
ushort remaining,
ushort completed,
ushort failed,
ushort warning)
{
// Update the progress bar and nothing
more!
// Throw an exception to cancel
}
}
The callback is fired for every C-MOVE response with pending status that is sent by the SCP and again I remind you that the SCP may, meaning can but don’t have to, send pending C-MOVE responses. Some PACS will send one pending message for every C-STORE they make, others may send one every now and then and other PACS may not send pending messages at all. This means that you better avoid having important functionality coded or dependent somehow on this callback. I wouldn’t recommend anything more than a progress bar and would also have it clearly stated in the user manual that the progress bar behavior is at to the mercy of the PACS.
Sometimes you may wish to cancel the retrieve maybe because you get to many results or just the user clicked the cancel button. To do this, throw an exception (in C++ you can also return a failed HRESULT) in the callback. The toolkit will send a C-CANCEL command to the SCP on the control channel and the SCP should (hopefully) stop sending transaction.
That's it for today. In the next post we will improve our implementation by adding more event handlers and then split the Accepter from the Requester and handle the inbound association on a separate thread. This will allow us to serve all incoming associations in the same manner.
As always, questions and comments are most welcome.

0 comments:
Post a Comment