pydicom / pynetdicom

A Python implementation of the DICOM networking protocol
https://pydicom.github.io/pynetdicom
MIT License
510 stars 180 forks source link

C-FIND specific SOP Class UID #815

Closed OoDimoO closed 1 year ago

OoDimoO commented 1 year ago

Hi, I am struggling with the send_c_find function as I try to retrieve only a certain type of modality let's say Halcyon RTPLAN for which the SOP Class UID is 1.2.246.352.70.1.70. I work on a computer which has been declared as a trusted entity for the application entity that provides the dicom storage. When I request the association between the AE and the trusted entity, I propose the Abstract syntax: 1.2.246.352.70.1.70 which is accepted by the AE. I extended the negotiations to ensure that the role is set to SCU. ( don't know if that is mandatory ) Despite the acceptance of the association, the Find SCP result is failed with the value error of: 0xc001.

On the other hand, when I set the requested_context of the AE to PatientRootQueryRetrieveInformationModelFind, the Find SCP is a success and I can access all radiotherapy dicom of the patient.

I am used to EvilDicom in C# and the way I use this library is by providing a list of SOPClassUID specific to the radiotherapy for the CFIND command to retrieve dicom objects.

I am trying to do the same with pynetdicom but in vain, could you help me out please?

Here is my code


from pynetdicom import AE, build_role, debug_logger, UID, build_context, StoragePresentationContexts, AllStoragePresentationContexts
from pynetdicom.sop_class import PatientRootQueryRetrieveInformationModelFind

debug_logger()
SOP_class_uid = [
                    #build_context("1.2.840.10008.5.1.4.1.2.1.1"),
                    build_context("1.2.246.352.70.1.70")
                    ]
def c_find_studiesIOD(ds, patient_id):
    ds.PatientID = patient_id
    ds.QueryRetrieveLevel = "STUDY"
    ds.StudyDate = None
    ds.AccessionNumber = None
    ds.StudyInstanceUID = None
    return ds

def c_find_seriesIOD(ds):
    ds.QueryRetrieveLevel = "SERIES"
    ds.SeriesInstanceUID = None
    ds.SeriesNumber = None
    ds.SeriesDescription = None
    ds.Modality = None
    return ds

ae = AE(ae_title="S*******") # AE Title of my trusted entity
ae.add_requested_context(PatientRootQueryRetrieveInformationModelFind)
for elem in SOP_class_uid:
    ae.add_requested_context(elem.abstract_syntax)

negotiation_items = []
for context in SOP_class_uid:
    role = build_role(context.abstract_syntax, scu_role=True)
    negotiation_items.append(role)

ds = Dataset()
ds = c_find_studiesIOD(ds,"MYPATIENTID")
ds = c_find_seriesIOD(ds)
#adress, port and AE_title of the daemon passed to associate()
assoc = ae.associate("1*.*.*.***", *****, ae_title="****", contexts=ae.requested_contexts, ext_neg=negotiation_items)
UID_plan = set()
for elem in ae.requested_contexts:
    if assoc.is_established:
        try:
            responses = assoc.send_c_find(ds, elem.abstract_syntax)
            for (status, identifier) in responses:
                if status:
                    if status.Status in (0xFF00, 0xFF01):  # Pending
                        if identifier.Modality == "RTPLAN":
                            UID_plan.add(identifier.SeriesInstanceUID)
                else:
                    print('Connection timed out, was aborted or received invalid response')
        except:
            pass
        assoc.release()
        # Release the association
    else:
        print('Association rejected, aborted or never connected')
print("Number of plans found : " + str(len(UID_plan)))```
brianmanderson commented 1 year ago

The issue is that Halcyon has a different RTPlan SOP UID than the regular default.

I've previously pushed a patch to Evil-Dicom that will fix this issue https://github.com/rexcardan/Evil-DICOM/pull/98

@mrbean-bremen This is what my current pull request should address