pydicom / pynetdicom

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

C-Store handler is not triggered #874

Closed jaspreetsingh6993 closed 10 months ago

jaspreetsingh6993 commented 11 months ago

Hello folks, I am new to dicom, facing issues when I am trying to start SCP server and trying to save the dicom files in custom location. I tried same with some third party listener I received the images but C-store handler is not getting triggered and when I tried starting SCP from pynetdiocm itself I am not receiving the files and MOVE SCP response is showing success.

Below is the code, Hidden the IP and sensitive details:

import os
import pandas as pd
import pydicom
from pydicom.dataset import Dataset, FileDataset
from pynetdicom import AE, evt, StoragePresentationContexts, debug_logger
from pynetdicom.sop_class import PatientRootQueryRetrieveInformationModelMove
from pynetdicom.transport import AssociationServer
import time
debug_logger()

    # Implement a handler for evt.EVT_C_STORE
def handle_store(event):
    """Handle a C-STORE request event."""
    # Decode the C-STORE request's *Data Set* parameter to a pydicom Dataset
    ds = event.dataset
    # Add the File Meta Information
    ds.file_meta = event.file_meta
    fpath='\\xyz.file.core.windows.net\abc\test\'
    # Save the dataset using the SOP Instance UID as the filename
    filename = os.path.join(fpath,ds.StudyInstanceUID)
    print(filename)
    os.makedirs(os.path.dirname(filename), exist_ok=True)
    ds.save_as(filename+ds.SeriesInstanceUID+'.dcm')

    # Return a 'Success' status
    return 0x0000

handlers = [(evt.EVT_C_STORE, handle_store)]

study_df = pd.read_csv('test.csv')
study_df.drop(columns=['accession_num'], inplace=True)
study_df = study_df.drop_duplicates()
study_list = study_df.values.flatten().tolist()
study_log = []
status_log = []

ae = AE(ae_title=b'ABC_RESEARCH')
ae.add_requested_context(PatientRootQueryRetrieveInformationModelMove)
ae.supported_contexts = StoragePresentationContexts
scp = ae.start_server(("127.0.0.1", 104), block=False, evt_handlers=handlers)

for i in study_list:
    ds = Dataset()
    ds.QueryRetrieveLevel = 'STUDY'
    ds.StudyInstanceUID = i
    responses = []
    assoc = ae.associate('xxx.xxx.xxx.xx', 105, ae_title='RESEARCH-ALL-QR')
    if assoc.is_established:

        responses = assoc.send_c_move(ds,'ABC_RESEARCH', PatientRootQueryRetrieveInformationModelMove )

        for (status, identifier) in responses:
            if status:
                if status.Status==0:

                    study_log.append(i)
                    status_log.append('success')
                elif status.Status==65280:

                    pass
                else:
                    study_log.append(i)
                    status_log.append('failed')

        assoc.release()
    else:
        study_log.append(i)
        status_log.append('Association issue')

log_df=pd.DataFrame({'study_uid':study_log,'status':status_log})
log_df.to_csv('test_log.csv', index=False)
scp.shutdown()

Below is the logs:

I: Requesting Association
D: Request Parameters:
D: ======================= OUTGOING A-ASSOCIATE-RQ PDU ========================
D: Our Implementation Class UID:      1.2.826.0.1.3680043.9.3811.2.0.2
D: Our Implementation Version Name:   PYNETDICOM_202
D: Application Context Name:    1.2.840.10008.3.1.1.1
D: Calling Application Name:    ABC_RESEARCH
D: Called Application Name:     RESEARCH-ALL-QR
D: Our Max PDU Receive Size:    16382
D: Presentation Context:
D:   Context ID:        1 (Proposed)
D:     Abstract Syntax: =Patient Root Query/Retrieve Information Model - MOVE
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntaxes:
D:       =Implicit VR Little Endian
D:       =Explicit VR Little Endian
D:       =Deflated Explicit VR Little Endian
D:       =Explicit VR Big Endian
D: Requested Extended Negotiation: None
D: Requested Common Extended Negotiation: None
D: Requested Asynchronous Operations Window Negotiation: None
D: Requested User Identity Negotiation: None
D: ========================== END A-ASSOCIATE-RQ PDU ==========================
D: Accept Parameters:
D: ======================= INCOMING A-ASSOCIATE-AC PDU ========================
D: Their Implementation Class UID:    1.2.840.114158.1.1.3
D: Their Implementation Version Name: AcuoMed
D: Application Context Name:    1.2.840.10008.3.1.1.1
D: Calling Application Name:    ABC_RESEARCH
D: Called Application Name:     RESEARCH-ALL-QR
D: Their Max PDU Receive Size:  1048576
D: Presentation Contexts:
D:   Context ID:        1 (Accepted)
D:     Abstract Syntax: =Patient Root Query/Retrieve Information Model - MOVE
D:     Accepted SCP/SCU Role: Default
D:     Accepted Transfer Syntax: =Implicit VR Little Endian
D: Accepted Extended Negotiation: None
D: Accepted Asynchronous Operations Window Negotiation: None
D: User Identity Negotiation Response: None
D: ========================== END A-ASSOCIATE-AC PDU ==========================
I: Association Accepted
I: Sending Move Request: MsgID 1
I:
I: # Request Identifier
I: (0008,0052) CS [STUDY]                                  # 1 QueryRetrieveLevel
I: (0020,000D) UI [1.2.276.0.26.1.1.1.2.2022.175.XXXX.XXXXXXX] # 1 StudyInstanceUID
I:
D: ========================== OUTGOING DIMSE MESSAGE ==========================
D: Message Type                  : C-MOVE RQ
D: Message ID                    : 1
D: Affected SOP Class UID        : Patient Root Query/Retrieve Information Model - MOVE
D: Move Destination              : ABC_RESEARCH
D: Identifier                    : Present
D: Priority                      : Low
D: ============================ END DIMSE MESSAGE =============================
D: pydicom.read_dataset() TransferSyntax="Little Endian Implicit"
D: ========================== INCOMING DIMSE MESSAGE ==========================
D: Message Type                  : C-MOVE RSP
D: Presentation Context ID       : 1
D: Message ID Being Responded To : 1
D: Affected SOP Class UID        : Patient Root Query/Retrieve Information Model - MOVE
D: Remaining Sub-operations      : 36
D: Completed Sub-operations      : 0
D: Failed Sub-operations         : 0
D: Warning Sub-operations        : 0
D: Identifier                    : None
D: Status                        : 0xFF00
D: ============================ END DIMSE MESSAGE =============================
D:
I: Move SCP Response: 1 - 0xFF00 (Pending)
I: Sub-Operations Remaining: 36, Completed: 0, Failed: 0, Warning: 0
D: pydicom.read_dataset() TransferSyntax="Little Endian Implicit"
D: ========================== INCOMING DIMSE MESSAGE ==========================
D: Message Type                  : C-MOVE RSP
D: Presentation Context ID       : 1
D: Message ID Being Responded To : 1
D: Affected SOP Class UID        : Patient Root Query/Retrieve Information Model - MOVE
D: Remaining Sub-operations      : 35
D: Completed Sub-operations      : 1
D: Failed Sub-operations         : 0
D: Warning Sub-operations        : 0
D: Identifier                    : None
D: Status                        : 0xFF00
D: ============================ END DIMSE MESSAGE =============================
D:
I: Move SCP Response: 2 - 0xFF00 (Pending)
I: Sub-Operations Remaining: 35, Completed: 1, Failed: 0, Warning: 0
D: pydicom.read_dataset() TransferSyntax="Little Endian Implicit"
D: ========================== INCOMING DIMSE MESSAGE ==========================
D: Message Type                  : C-MOVE RSP
D: Presentation Context ID       : 1
D: Message ID Being Responded To : 1
D: Affected SOP Class UID        : Patient Root Query/Retrieve Information Model - MOVE
D: Remaining Sub-operations      : 34
D: Completed Sub-operations      : 2
D: Failed Sub-operations         : 0
D: Warning Sub-operations        : 0
D: Identifier                    : None
D: Status                        : 0xFF00
D: ============================ END DIMSE MESSAGE =============================
D:
I: Move SCP Response: 3 - 0xFF00 (Pending)
I: Sub-Operations Remaining: 34, Completed: 2, Failed: 0, Warning: 0
D: pydicom.read_dataset() TransferSyntax="Little Endian Implicit"
...
D: ========================== INCOMING DIMSE MESSAGE ==========================
D: Message Type                  : C-MOVE RSP
D: Presentation Context ID       : 1
D: Message ID Being Responded To : 1
D: Affected SOP Class UID        : Patient Root Query/Retrieve Information Model - MOVE
D: Completed Sub-operations      : 36
D: Failed Sub-operations         : 0
D: Warning Sub-operations        : 0
D: Identifier                    : None
D: Status                        : 0x0000
D: ============================ END DIMSE MESSAGE =============================
D:
I: Move SCP Result: 0x0000 (Success)
I: Sub-Operations Remaining: 0, Completed: 36, Failed: 0, Warning: 0
I: Releasing Association 
scaramallion commented 11 months ago

You have the received files saved as ds.save_as(filename+ds.SeriesInstanceUID+'.dcm'), however the Series Instance UID is not unique for each SOP Instance. You probably want something like ds.save_as(filename+ds.SOPInstanceUID+'.dcm') instead

jaspreetsingh6993 commented 11 months ago

Thanks for your response, I tried with ds.save_as(filename+ds.SOPInstanceUID+'.dcm') as well but no luck.

scaramallion commented 11 months ago

That's weird, something is definitely receiving the datasets. Are you sure that the PACS is configured with the correct details for the destination SCP (and that there's not already a destination with the AE title of ABC_RESEARCH)?

scaramallion commented 10 months ago

@jaspreetsingh6993 did you resolve your issue?