Converting from DicomQuery to DicomConnection for Q/R

This article is not yet complete

All the DicomQuery-based operations can also be performed using a DicomConnection object, and doing so has several benefits:

  • It allows finer control over the exact presentation contexts offered
  • Multiple operations can occur on one association
  • Perhaps most importantly, if the DicomConnection is asynchronous, then it provides the best solution to using C-MOVE in DicomObjects, avoiding the problems of deadlocking or lack of return status which affect some of the alternatives.
  • It provides better error reporting generally, with full access to all the returned status information.

The equivalent methods are as shown in this table:

DICOM Operation DicomQuery DicomConnection
C-FIND DoQuery & DoRawQuery Find
C-GET GetImages Get
C-MOVE MoveImages, MoveSync & GetUsingMove Move

When used on an asynchronous DicomConnection these methods send out a C-MOVE request and return immediately. For C-MOVE it is important that an asynchronous DicomConneciton is used, and a separate article DicomConnection.Move gives more details, but for those simply wishing to get more information from C-FIND or C-GET. then a synchronous DicomConnection object, simply created using 'New DicomConnection' - will suffice.

There are ??? steps in converting a simple DicomQuery based operation to one based on DicomConnection:

Contents

Create a DicomConnection object using either of the methods below:

Synchronous
connection = new DicomConnection
Asynchronous
connection = [DicomViewer or DicomServer object].New("DicomConnection")

Add the presentation contexts you wish to use

This is the section which needs a little thinking about, as it replaces work that is done for you internally in DicomQuery, but it is not difficult! Simply pick the SOP class(es) you wish to use from the table below. If using VB or any other language which properly uses COM constants then the "names" are easiest and clearest in your code but in other languages (e.g. those from Borland) you may need to use the string version of the UID instead:

Operation Root Name UID
C-FIND PATIENT doSOP_PatientRootQR_FIND 1.2.840.10008.5.1.4.1.2.1.1
C-MOVE PATIENT doSOP_PatientRootQR_MOVE 1.2.840.10008.5.1.4.1.2.1.2
C-GET PATIENT doSOP_PatientRootQR_GET 1.2.840.10008.5.1.4.1.2.1.3
C-FIND STUDY doSOP_StudyRootQR_FIND 1.2.840.10008.5.1.4.1.2.2.1
C-MOVE STUDY doSOP_StudyRootQR_MOVE 1.2.840.10008.5.1.4.1.2.2.2
C-GET STUDY doSOP_StudyRootQR_GET 1.2.840.10008.5.1.4.1.2.2.3

Having decided which to use, add them to the connection's Contexts collection - e.g.

connection.Contexts.Add doSOP_PatientRootQR_FIND
connection.Contexts.Add doSOP_StudyRootQR_FIND

(you may add as many as you wish up to a maximum of 128!)

Open the association

In the COM_Version this is done using the SetDestination method, and in the .NET_version, the better-named Open method, both of which take 4 parameters, corresponding to the Node, Port, CallingAET & CalledAET properties of the DicomQuery. e.g.

connection.SetDestination RemoteIP, RemotePort, MyAET, RemoteAET

Make the request dataset

Rather than specifying what you wish the SCP to do in terms of properties of the DicomQuery, the DicomConnection-based methods require to supply a DicomDataSet containing the
Those using DoRawQuery will already have a suitable dataset, but for those new to this, it takes a little bit of thinking about. It is important to realise just how different the contents are for querying (C-FIND) and retrieval (C-MOVE & C-GET). They are therefore covered in separate sections:

Retrieval (C-MOVE & C-GET)

The dataset can contain only 5 different attributes, not all of which are needed for all retrievals:

  • The Level (0008,0052) which must be "PATIENT", "STUDY", "SERIES" or "IMAGE"
  • PatientID
  • Study UID
  • Series UID
  • Instance UID

No others are permitted
The level must always be present, and of the others, those 'between' the root and the level (inclusive) must be present - e.g.:

  • A Patient root, image level operation must contain all four.
  • A Study root, series level operation must contain only Study UID & Series UID

So, for this second example, the code would be like this:

Dim dataset as new DicomDataSet
Set dataset.Attributes.Add &H8, &H52, "SERIES"
dataset.StudyUID = ...
dataset.SeriesUID = ...

Querying (C-FIND)

There is a lot more flexibility in what is allowed in the dataset for a C-FIND query, including:

  • Unique keys (as specified above for retrieve operations) for levels above the query level (up to and including the root level)
  • Matching keys - attributes which are used to select the return values. These can include wildcards.
  • Return keys - sent as blank values - instructing the SCP to return these values to you.

Most of the complexity here is normally hidden by using a DicomQuery object, including the choice of a suitable set of blank return keys, to ensure that you have the data you require returned to you. To avoid everyone needing to replicate all this work, DicomObjects provides the QueryDataSet method to access the dataset it would have used itself, and those first converting to using a DicomConnection based query are strongly advised to use that method to avoid the pitfalls of making their own from scratch. The only downside is that you must still create and set up a DicomQuery object 'as if' you were going to use it for DoQuery, but in a conversion scenario you are likely to have it anyway! Setting up the dataset then simply becomes:

Set dataset = QueryObject.QueryDataSet()

Of course, you can also create and populate it yourself from scratch, but if doing so, remember that you need to add the level (0008,0052) yourself (as in the retrieve example above).

Perform the operation

This is simple, just use the appropriate command as below:

connection.Find Root, dataset
OR
connection.Get Root, dataset
OR
connection.Move Root, DestinationAET, dataset

Note that none of these method actually return any data - this is to allow them to be used synchronously (as well as synchronously)

Access your results

The place where you find your results (once the operation is complete) depends on the operation performed:

Find 
The connection's ReturnedDataSets collection
Get 
The connection's ReturnedImages collection
Move 
The images/objects will have been received via ImageReceived etc.

In all cases however, you can also access the connection's ReturnedResponses collection, the last of which contains the 'final' response in which useful attributes such as 0000,1020-0000,1023 (numbers of successful, failed images etc.) and 0000,0901/2 (error details) may be found.

Below is sample vb6 code to demonstrate a simple DicomConnection.Find. This is an example of a study root, series level query, but all other combinations are possible by changing the items in bold, and adding/removing identifiers to/from the dataset.

Close the connection

connection.Close()

or you could if you prefer do move operations on it before closing.

Summary

All the above may sound complicated, but in practice, the changes are quite simple! e.g. assuming that a DicomQuery object has been set up, and the following line is currently used for a Study root query:

Set results = query.DoQuery

Then the replacement code would be:

Dim connection As New DicomConnection
Dim dataset As DicomDataSet
connection.Contexts.Add doSOP_StudyRootQR_FIND 'This is Study Root C-FIND SOP Class 
connection.SetDestination query.Node, query.Port, query.CallingAET, query.CalledAET
Set dataset = query.QueryDataSet
connection.Find "STUDY", dataset 
Set results = connection.ReturnedDataSets
connection.Close
Relevance: