pydicom / pynetdicom

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

How to set specific presentation contexts #976

Open arai0101 opened 1 day ago

arai0101 commented 1 day ago

I have a problem. I am building a DICOM downloader with PyQt5. The peer PACS is SYNAPSE in the hospital. I want to retrieve CT images and I configure the presentation context, they are proposed and accepted for the SCU/SCP role. However, in the incoming associate-RQ PDU, the presentation context for CT images is not part of negotiation. I really appreciate if you can give me an advice. Thank you. Here is my code.

C-MOVE at PATIENT level

  def c_move(self):
    ae = AE()
    ae.ae_title = self.client_scu['aet'].encode('utf-8')
    ae.maximum_pdu_size = 0
    ae.supported_contexts = StoragePresentationContexts
    ae.add_requested_context(PatientRootQueryRetrieveInformationModelMove)
    ae.add_requested_context(CTImageStorage)
    # ae.add_requested_context(EnhancedCTImageStorage)
    ae.add_requested_context(UltrasoundImageStorage)
    ae.add_requested_context(SecondaryCaptureImageStorage)

    ds = Dataset()
    ds.QueryRetrieveLevel = 'PATIENT'
    ds.PatientName = self.selected_patient['PatientName']
    ds.PatientID = self.selected_patient['PatientID']
    total_images = self.selected_patient['total_images']

    self.download_patient_worker = DownloadPatientWorkerController(ae=ae,
                                                                   ds=ds,
                                                                   total_images=total_images,
                                                                   client_scu=self.client_scu,
                                                                   scp_dict=self.scp_dict,
                                                                   filestore_path=self.filestore_path,
                                                                   parent=self)

    self.download_patient_worker.running.connect(self.running_download_patient)
    self.download_patient_worker.finished.connect(self.on_download_finished)
    self.download_patient_worker.setWindowTitle(f"Downloading {total_images} images...")
    self.download_patient_worker.show()
    self.running_download_patient(True)

class DownloadPatientWorkerController(Ui_downloadPatientWorker, QDialog):
  running = QtCore.pyqtSignal(bool)
  finished = QtCore.pyqtSignal(str)

  def __init__(self, ae, ds, total_images, client_scu, scp_dict, filestore_path, parent=None):
    super().__init__(parent, Qt.Window)
    self.ae = ae
    self.ds = ds
    self.total_images = total_images
    self.client_scu = client_scu
    self.scp_dict = scp_dict
    # self.patient_dir = patient_dir
    self.filestore_path = filestore_path
    self.setupUi(self)
    self.download_patient_instances()

  def setupUi(self, downloadPatientWorker):
    super().setupUi(downloadPatientWorker)
    self.setWindowModality(Qt.WindowModality.WindowModal)
    self.cancelButton.clicked.connect(self.cancel_button_clicked)

  def download_patient_instances(self):
    self.download_thread = QtCore.QThread()
    self.download_patient_worker = DownloadPatientWorkerObject(ae=self.ae,
                                                                ds=self.ds,
                                                                total_images=self.total_images,
                                                                client_scu=self.client_scu,
                                                                scp_dict=self.scp_dict,
                                                                filestore_path=self.filestore_path)

    self.download_patient_worker.moveToThread(self.download_thread)

    self.download_thread.started.connect(self.download_patient_worker.run)
    self.download_patient_worker.finished.connect(self.on_download_finished)
    self.download_patient_worker.progress.connect(self._progress_download_patient)
    self.download_thread.start()

  def _progress_download_patient(self, value):
    data = value.get('data', 0)
    datas = value.get('datas', 0)
    percentage = int(data / datas * 100)
    self.progressBar.setValue(percentage)
    if percentage == 100:
      self.close()

  def on_download_finished(self, patient_dir):
    self.finished.emit(patient_dir)
    self.download_thread.quit()

  def cancel_button_clicked(self):
    self.download_patient_worker.abort_download()
    self.running.emit(False)
    self.close()

  def closeEvent(self, a0):
    self.download_patient_worker.abort_download()
    self.running.emit(False)
    return super().closeEvent(a0)

class DownloadPatientWorkerObject(QtCore.QObject):
  finished = QtCore.pyqtSignal(str)
  progress = QtCore.pyqtSignal(dict)
  cancel = QtCore.pyqtSignal()

  def __init__(self, ae, ds, total_images, client_scu, scp_dict, filestore_path):
    super().__init__()
    self.ae = ae
    self.ds = ds
    self.total_images = total_images
    self.client_scu = client_scu
    self.scp_dict = scp_dict
    # self.patient_dir = patient_dir
    self.filestore_path = filestore_path
    self.cancel_requested = False
    self.is_running = True

  def run(self):
    self.download_instance()
    self.finished.emit(self.patient_dir)

  def abort_download(self):
    self.cancel_requested = True
    self.is_running = False

  def download_instance(self):
    handlers = [(evt.EVT_C_STORE, self.handle_store)]
    # role = build_role(CTImageStorage, scp_role=True)
    self.ae.supported_context = AllStoragePresentationContexts
    self.scp = self.ae.start_server(('', self.client_scu['port']), block=False, evt_handlers=handlers)
    self.assoc = self.ae.associate(str(self.scp_dict['url']), int(self.scp_dict['port']), ae_title=self.scp_dict['aet'].encode('utf-8'))
    i = 0
    if self.assoc.is_established:
      self.create_patient_dir(self.ds.PatientName)
      n = self.total_images
      self.instance_downloaded = 0
      for cx in self.assoc.accepted_contexts:
        cx._as_scp = True
        cx._as_scu = True
        print(cx)
      # Use the C-MOVE service to send the identifier
      responses = self.assoc.send_c_move(self.ds, self.client_scu['aet'].encode('utf-8'), PatientRootQueryRetrieveInformationModelMove)

      i = 0
      self.message_id = None
      for status, identifier in responses:
        if status and identifier:
          for elem in identifier.elements():
            print('elem = ', elem)
          if status.Status in (0xff00, 0xff01):
            print(identifier)
          print('##############################')
          i += 1
        else:
          print('Connection timed out, was aborted or received invalid response')
      # self.download_complete_signal.emit(self.patient_dir)

      # Release the association
      self.assoc.release()
    else:
      print('Association rejected, aborted or never connected')

    # Stop our Storage SCP
    self.scp.shutdown()

  def create_patient_dir(self, patient_name):
    patient_name = str(patient_name)
    if len(patient_name) == 0:
      patient_name = 'BLANK'
    self.patient_dir = os.path.join(self.filestore_path, patient_name)
    if not os.path.exists(self.patient_dir):
      os.makedirs(self.patient_dir)
    else:
      patient_name = patient_name + '(Copy)'
      self.create_patient_dir(patient_name)

  def handle_store(self, event):
    """Handle a C-STORE service request"""
    ds = event.dataset
    ds.file_meta = event.file_meta
    fn = os.path.join(self.patient_dir, ds.SOPInstanceUID)
    ds.save_as(fn, write_like_original=False)
    self.instance_downloaded += 1
    if self.is_running:
      self.progress.emit({"data": self.instance_downloaded, "datas": self.total_images})
    else:
      to_match = PatientRootQueryRetrieveInformationModelMove
      cxs = [cx for cx in self.assoc.accepted_contexts if cx.abstract_syntax == to_match]
      cx_id = cxs[0].context_id
      event.assoc.send_c_cancel(1, cx_id)
    return 0x0000

And this is the log:

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:    INDOQCT
D: Called Application Name:     SYNAPSE
D: Our Max PDU Receive Size:    16382
D: Presentation Contexts:
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:   Context ID:        3 (Proposed)
D:     Abstract Syntax: =CT Image Storage
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:   Context ID:        5 (Proposed)
D:     Abstract Syntax: =Ultrasound Image Storage
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:   Context ID:        7 (Proposed)
D:     Abstract Syntax: =Secondary Capture Image Storage
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.113845.1.1
D: Their Implementation Version Name: Syn7,3,0,258
D: Application Context Name:    1.2.840.10008.3.1.1.1
D: Calling Application Name:    INDOQCT
D: Called Application Name:     SYNAPSE
D: Their Max PDU Receive Size:  65000
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: =Explicit VR Little Endian
D:   Context ID:        3 (Accepted)
D:     Abstract Syntax: =CT Image Storage
D:     Accepted SCP/SCU Role: Default
D:     Accepted Transfer Syntax: =Explicit VR Little Endian
D:   Context ID:        5 (Accepted)
D:     Abstract Syntax: =Ultrasound Image Storage
D:     Accepted SCP/SCU Role: Default
D:     Accepted Transfer Syntax: =Explicit VR Little Endian
D:   Context ID:        7 (Accepted)
D:     Abstract Syntax: =Secondary Capture Image Storage
D:     Accepted SCP/SCU Role: Default
D:     Accepted Transfer Syntax: =Explicit 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
ID: 1
Abstract Syntax: Patient Root Query/Retrieve Information Model - MOVE
Transfer Syntax(es):
    =Explicit VR Little Endian
Result: Accepted
Role: SCU and SCP
ID: 3
Abstract Syntax: CT Image Storage
Transfer Syntax(es):
    =Explicit VR Little Endian
Result: Accepted
Role: SCU and SCP
ID: 5
Abstract Syntax: Ultrasound Image Storage
Transfer Syntax(es):
    =Explicit VR Little Endian
Result: Accepted
Role: SCU and SCP
ID: 7
Abstract Syntax: Secondary Capture Image Storage
Transfer Syntax(es):
    =Explicit VR Little Endian
Result: Accepted
Role: SCU and SCP
I: Sending Move Request: MsgID 1
I:
I: # Request Identifier
I: (0008,0052) CS [PATIENT]                                # 1 QueryRetrieveLevel
I: (0010,0010) PN [ANON_NAME]                            # 1 PatientName
I: (0010,0020) LO [ANON_ID]                             # 1 PatientID
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              : INDOQCT
D: Identifier                    : Present
D: Priority                      : Low
D: ============================ END DIMSE MESSAGE =============================
D: Request Parameters:
D: ======================= INCOMING A-ASSOCIATE-RQ PDU ========================
D: Their Implementation Class UID:      1.2.840.113845.1.1
D: Their Implementation Version Name:   Syn7,3,0,258
D: Application Context Name:    1.2.840.10008.3.1.1.1
D: Calling Application Name:    SYNAPSE
D: Called Application Name:     INDOQCT
D: Their Max PDU Receive Size:  65000
D: Presentation Contexts:
D:   Context ID:        1 (Proposed)
D:     Abstract Syntax: =Secondary Capture Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =JPEG Lossless, Non-Hierarchical, First-Order Prediction (Process 14 [Selection Value 1])
D:   Context ID:        3 (Proposed)
D:     Abstract Syntax: =Secondary Capture Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =JPEG 2000 Image Compression (Lossless Only)
D:   Context ID:        5 (Proposed)
D:     Abstract Syntax: =Secondary Capture Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =JPEG Baseline (Process 1)
D:   Context ID:        7 (Proposed)
D:     Abstract Syntax: =Secondary Capture Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =JPEG 2000 Image Compression
D:   Context ID:        9 (Proposed)
D:     Abstract Syntax: =Secondary Capture Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =Explicit VR Little Endian
D:   Context ID:        11 (Proposed)
D:     Abstract Syntax: =Secondary Capture Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =Implicit VR Little Endian
D:   Context ID:        13 (Proposed)
D:     Abstract Syntax: =Basic Text SR Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =Explicit VR Little Endian
D:   Context ID:        15 (Proposed)
D:     Abstract Syntax: =Basic Text SR Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =Implicit VR Little Endian
D:   Context ID:        17 (Proposed)
D:     Abstract Syntax: =Ultrasound Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =JPEG Lossless, Non-Hierarchical, First-Order Prediction (Process 14 [Selection Value 1])
D:   Context ID:        19 (Proposed)
D:     Abstract Syntax: =Ultrasound Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =JPEG 2000 Image Compression (Lossless Only)
D:   Context ID:        21 (Proposed)
D:     Abstract Syntax: =Ultrasound Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =JPEG Baseline (Process 1)
D:   Context ID:        23 (Proposed)
D:     Abstract Syntax: =Ultrasound Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =JPEG 2000 Image Compression
D:   Context ID:        25 (Proposed)
D:     Abstract Syntax: =Ultrasound Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =RLE Lossless
D:   Context ID:        27 (Proposed)
D:     Abstract Syntax: =Ultrasound Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =Explicit VR Little Endian
D:   Context ID:        29 (Proposed)
D:     Abstract Syntax: =Ultrasound Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =Implicit VR Little Endian
D:   Context ID:        31 (Proposed)
D:     Abstract Syntax: =X-Ray Radiofluoroscopic Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =JPEG Lossless, Non-Hierarchical, First-Order Prediction (Process 14 [Selection Value 1])
D:   Context ID:        33 (Proposed)
D:     Abstract Syntax: =X-Ray Radiofluoroscopic Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =JPEG 2000 Image Compression (Lossless Only)
D:   Context ID:        35 (Proposed)
D:     Abstract Syntax: =X-Ray Radiofluoroscopic Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =JPEG Baseline (Process 1)
D:   Context ID:        37 (Proposed)
D:     Abstract Syntax: =X-Ray Radiofluoroscopic Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =JPEG 2000 Image Compression
D:   Context ID:        39 (Proposed)
D:     Abstract Syntax: =X-Ray Radiofluoroscopic Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =Explicit VR Little Endian
D:   Context ID:        41 (Proposed)
D:     Abstract Syntax: =X-Ray Radiofluoroscopic Image Storage
D:     Proposed SCP/SCU Role: Default
D:     Proposed Transfer Syntax:
D:       =Implicit VR Little 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 ==========================
I: Accepting Association
D: Accept Parameters:
D: ======================= OUTGOING A-ASSOCIATE-AC 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: Responding Application Name: resp. AE Title
D: Our Max PDU Receive Size:    0
D: Presentation Contexts:
D:   Context ID:        1 (Rejected - Transfer Syntax Not Supported)
D:     Abstract Syntax: =Secondary Capture Image Storage
D:   Context ID:        3 (Rejected - Transfer Syntax Not Supported)
D:     Abstract Syntax: =Secondary Capture Image Storage
D:   Context ID:        5 (Rejected - Transfer Syntax Not Supported)
D:     Abstract Syntax: =Secondary Capture Image Storage
D:   Context ID:        7 (Rejected - Transfer Syntax Not Supported)
D:     Abstract Syntax: =Secondary Capture Image Storage
D:   Context ID:        9 (Accepted)
D:     Abstract Syntax: =Secondary Capture Image Storage
D:     Accepted SCP/SCU Role: Default
D:     Accepted Transfer Syntax: =Explicit VR Little Endian
D:   Context ID:        11 (Accepted)
D:     Abstract Syntax: =Secondary Capture Image Storage
D:     Accepted SCP/SCU Role: Default
D:     Accepted Transfer Syntax: =Implicit VR Little Endian
D:   Context ID:        13 (Accepted)
D:     Abstract Syntax: =Basic Text SR Storage
D:     Accepted SCP/SCU Role: Default
D:     Accepted Transfer Syntax: =Explicit VR Little Endian
D:   Context ID:        15 (Accepted)
D:     Abstract Syntax: =Basic Text SR Storage
D:     Accepted SCP/SCU Role: Default
D:     Accepted Transfer Syntax: =Implicit VR Little Endian
D:   Context ID:        17 (Rejected - Transfer Syntax Not Supported)
D:     Abstract Syntax: =Ultrasound Image Storage
D:   Context ID:        19 (Rejected - Transfer Syntax Not Supported)
D:     Abstract Syntax: =Ultrasound Image Storage
D:   Context ID:        21 (Rejected - Transfer Syntax Not Supported)
D:     Abstract Syntax: =Ultrasound Image Storage
D:   Context ID:        23 (Rejected - Transfer Syntax Not Supported)
D:     Abstract Syntax: =Ultrasound Image Storage
D:   Context ID:        25 (Rejected - Transfer Syntax Not Supported)
D:     Abstract Syntax: =Ultrasound Image Storage
D:   Context ID:        27 (Accepted)
D:     Abstract Syntax: =Ultrasound Image Storage
D:     Accepted SCP/SCU Role: Default
D:     Accepted Transfer Syntax: =Explicit VR Little Endian
D:   Context ID:        29 (Accepted)
D:     Abstract Syntax: =Ultrasound Image Storage
D:     Accepted SCP/SCU Role: Default
D:     Accepted Transfer Syntax: =Implicit VR Little Endian
D:   Context ID:        31 (Rejected - Transfer Syntax Not Supported)
D:     Abstract Syntax: =X-Ray Radiofluoroscopic Image Storage
D:   Context ID:        33 (Rejected - Transfer Syntax Not Supported)
D:     Abstract Syntax: =X-Ray Radiofluoroscopic Image Storage
D:   Context ID:        35 (Rejected - Transfer Syntax Not Supported)
D:     Abstract Syntax: =X-Ray Radiofluoroscopic Image Storage
D:   Context ID:        37 (Rejected - Transfer Syntax Not Supported)
D:     Abstract Syntax: =X-Ray Radiofluoroscopic Image Storage
D:   Context ID:        39 (Accepted)
D:     Abstract Syntax: =X-Ray Radiofluoroscopic Image Storage
D:     Accepted SCP/SCU Role: Default
D:     Accepted Transfer Syntax: =Explicit VR Little Endian
D:   Context ID:        41 (Accepted)
D:     Abstract Syntax: =X-Ray Radiofluoroscopic Image Storage
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 ==========================
D: pydicom.read_dataset() TransferSyntax="Little Endian Implicit"
D: ========================== INCOMING DIMSE MESSAGE ==========================
I: Association Released
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      : 0
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:
E: Connection closed before the entire PDU was received
D: pydicom.read_dataset() TransferSyntax="Little Endian Implicit"
I: Move SCP Response: 1 - 0xFF00 (Pending)
E: [WinError 10054] An existing connection was forcibly closed by the remote host
Traceback (most recent call last):
  File "D:\IndoDICOM\venv\lib\site-packages\pynetdicom\dul.py", line 269, in _read_pdu_data
    bytestream.extend(self.socket.recv(6))
  File "D:\IndoDICOM\venv\lib\site-packages\pynetdicom\transport.py", line 410, in recv
    bytes_read = self.socket.recv(bufsize)
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host
D: ========================== INCOMING DIMSE MESSAGE ==========================
I: Sub-Operations Remaining: 0, Completed: 0, Failed: 0, Warning: 0
Connection timed out, was aborted or received invalid response
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      : 0
D: Completed Sub-operations      : 0
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: 0, Failed: 0, Warning: 0
Connection timed out, was aborted or received invalid response
I: Releasing Association
scaramallion commented 1 day ago

Don't do this:

      for cx in self.assoc.accepted_contexts:
        cx._as_scp = True
        cx._as_scu = True
        print(cx)

Have you looked at the Q/R Move code example or the Move SCU application code?

The Move SCP controls which presentation contexts it asks for when requesting the association with the destination Storage SCP, all you can do is have a Storage SCP available which supports CT Image Storage or whatever it is you're trying to retrieve.

I'm not seeing any matches to your query in the log, so the Move SCP has nothing to send.

Also, that better not be an actual patient name and ID you've used.


E: [WinError 10054] An existing connection was forcibly closed by the remote host
Traceback (most recent call last):
  File "D:\IndoDICOM\venv\lib\site-packages\pynetdicom\dul.py", line 269, in _read_pdu_data
    bytestream.extend(self.socket.recv(6))
  File "D:\IndoDICOM\venv\lib\site-packages\pynetdicom\transport.py", line 410, in recv
    bytes_read = self.socket.recv(bufsize)
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host

Rude.