class WorklistClient:
    """ Worklist client used to test the server"""

    def __init__(self, serveraddress):
        self._ae = AE()
        self._ae.add_requested_context(ModalityWorklistInformationFind)
        self._ae.acse_timeout = 3600
        self._serveraddress = serveraddress

    def get_worklist(self, query_dataset):
        """ Fetch worklist from server, and return as PyDicom dataset """
        assoc = self._ae.associate(self._serveraddress.address, self._serveraddress.port)
        if not assoc.is_established:
            raise Exception('Association rejected, aborted or never connected')

        worklist = []
        for (status, item) in assoc.send_c_find(query_dataset, ModalityWorklistInformationFind):
            if not status:
                print('Connection timed out, was aborted or received invalid response')
            elif status.Status == 0xFF00: # Matches are continuing
                worklist.append(item)
            elif status.Status != 0x0000: # Anything except Success
                raise Exception("Got unexpected status from worklist server")
        assoc.release()
        return worklist
Ejemplo n.º 2
0
    def test_scp_callback_info(self):
        """Test on_c_echo info parameter."""
        self.scp = DummyVerificationSCP()
        self.scp.start()

        ae = AE()
        ae.add_requested_context(VerificationSOPClass)
        ae.acse_timeout = 5
        ae.dimse_timeout = 5
        assoc = ae.associate('localhost', 11112)
        assert assoc.is_established
        rsp = assoc.send_c_echo()
        assert rsp.Status == 0x0000
        assoc.release()
        assert assoc.is_released

        assert 'address' in self.scp.info['requestor']
        assert self.scp.info['requestor']['ae_title'] == b'PYNETDICOM      '
        #assert self.scp.info['requestor']['called_aet'] == b'ANY-SCP         '
        assert isinstance(self.scp.info['requestor']['port'], int)
        assert self.scp.info['acceptor']['port'] == 11112
        assert 'address' in self.scp.info['acceptor']
        assert self.scp.info['acceptor']['ae_title'] == b'PYNETDICOM      '

        self.scp.stop()
Ejemplo n.º 3
0
    def test_unknown_pdu_aborts(self):
        commands = [
            ("recv", None),  # recv a-associate-rq
            ("send", a_associate_ac),
            ("send", b"\x53\x00\x00\x00\x00\x02"),
            ("recv", None),
            ("exit", None),
        ]
        self.scp = scp = start_server(commands)

        def handle(event):
            scp.step()
            scp.step()

        hh = [(evt.EVT_REQUESTED, handle)]

        ae = AE()
        ae.acse_timeout = 5
        ae.dimse_timeout = 5
        ae.network_timeout = 5
        ae.add_requested_context("1.2.840.10008.1.1")
        assoc = ae.associate("localhost", 11112, evt_handlers=hh)
        assert assoc.is_established

        scp.step()  # send bad PDU

        time.sleep(0.1)

        scp.step()  # receive abort
        scp.step()
        scp.shutdown()

        assert assoc.is_aborted
Ejemplo n.º 4
0
def c_get_Option():
    print('Executing the C_GET approach i.e. SCU c_get')
    handlers = [(evt.EVT_C_STORE, handle_store)]
    CompositeInstanceRetrieveWithoutBulkDataGet = '1.2.840.10008.5.1.4.1.2.5.3'
    SecondaryCaptureImageStorage = '1.2.840.10008.5.1.4.1.1.7'

    ae = AE(ae_title=AE_TITLE)
    ae.add_requested_context(CompositeInstanceRetrieveWithoutBulkDataGet)
    ae.add_requested_context(SecondaryCaptureImageStorage)
    role = build_role(SecondaryCaptureImageStorage, scp_role=True)
    assoc = ae.associate(SERVER_ADDRESS,
                         PORT,
                         ext_neg=[role],
                         evt_handlers=handlers)

    if assoc.is_established:
        responses = assoc.send_c_get(
            create_queryDS('IMAGE'),
            CompositeInstanceRetrieveWithoutBulkDataGet)
        for (status, identifier) in responses:
            if status:
                print('C-GET query status: 0x{0:04x}'.format(status.Status))
            else:
                print(
                    'Connection timed out, was aborted or received invalid response'
                )

        assoc.release()
    else:
        print('Association rejected, aborted or never connected')
Ejemplo n.º 5
0
    def test_exception_in_handler(self):
        """Test an exception raised by the handler doesn't shut the server."""
        class DummyAE(object):
            network_timeout = 5
            _servers = []

        dummy = DummyAE()
        server = ThreadedAssociationServer(dummy, ('', 11112))
        dummy._servers.append(server)
        thread = threading.Thread(target=server.serve_forever)
        thread.daemon = True
        thread.start()

        ae = AE()
        ae.add_requested_context('1.2.840.10008.1.1')
        # assoc.dul is Thread-2
        assoc = ae.associate('', 11112)

        assert server.socket.fileno() != -1

        server.shutdown()

        if sys.version_info[0] == 2:
            with pytest.raises(socket.error):
                server.socket.fileno()
        else:
            assert server.socket.fileno() == -1
Ejemplo n.º 6
0
    def test_recv_missing_data(self, caplog):
        """Test missing data when receiving."""
        with caplog.at_level(logging.ERROR, logger="pynetdicom"):
            commands = [
                ("recv", None),  # recv a-associate-rq
                ("send", a_associate_ac),
                ("send", b"\x07\x00\x00\x00\x00\x02\x00"),  # Send short PDU
                ("exit", None),
            ]
            self.scp = scp = start_server(commands)

            def handle(event):
                scp.step()
                scp.step()

            hh = [(evt.EVT_REQUESTED, handle)]

            ae = AE()
            ae.acse_timeout = 5
            ae.dimse_timeout = 5
            # ae.network_timeout = 0.5
            ae.add_requested_context("1.2.840.10008.1.1")
            assoc = ae.associate("localhost", 11112, evt_handlers=hh)
            assert assoc.is_established

            scp.step()  # send short pdu

            scp.step()
            scp.shutdown()
            assert assoc.is_aborted
            assert (
                "The received PDU is shorter than expected (7 of 8 bytes received)"
            ) in caplog.text
Ejemplo n.º 7
0
    def search_patients(self,
                        search_query: str,
                        additional_tags: List[str] = None,
                        wildcard: bool = None) -> List[Dataset]:
        ae = AE(ae_title=self.client_ae)
        ae.add_requested_context(StudyRootQueryRetrieveInformationModelFind)

        if wildcard:
            search_query = f'*{search_query}*'
        patient_id_to_datasets = defaultdict(Dataset)

        with association(ae, self.pacs_url, self.pacs_port,
                         self.remote_ae) as assoc:
            id_responses = _find_patients(assoc, 'PatientID', search_query,
                                          additional_tags)
            for study in checked_responses(id_responses):
                if hasattr(study, 'PatientID'):
                    result = patient_id_to_datasets[study.PatientID]
                    self.update_patient_result(result, study, additional_tags)

        # consecutive find must be in separate associations
        with association(ae, self.pacs_url, self.pacs_port,
                         self.remote_ae) as assoc:
            name_responses = _find_patients(assoc, 'PatientName', search_query,
                                            additional_tags)
            for study in checked_responses(name_responses):
                if hasattr(study, 'PatientID'):
                    result = patient_id_to_datasets[study.PatientID]
                    self.update_patient_result(result, study, additional_tags)

        return list(patient_id_to_datasets.values())
Ejemplo n.º 8
0
    def verify(self) -> bool:
        ae = AE(ae_title=self.client_ae)
        ae.add_requested_context(Verification)
        # setting timeout here doesn't appear to have any effect
        ae.network_timeout = self.timeout

        with association(ae, self.pacs_url, self.pacs_port,
                         self.remote_ae) as assoc:
            logger.debug('Association accepted by the peer')
            # Send a DIMSE C-ECHO request to the peer
            # status is a pydicom Dataset object with (at a minimum) a
            # (0000, 0900) Status element
            status = assoc.send_c_echo()

            # Output the response from the peer
            if status.Status in status_success_or_pending:
                logger.debug('C-ECHO Response: 0x{0:04x}'.format(
                    status.Status))
                return True
            else:
                logger.warning('C-ECHO Failure Response: 0x{0:04x}'.format(
                    status.Status))
                return False

        return False
Ejemplo n.º 9
0
    def fetch_slice_thumbnail(self, study_id: str, series_id: str,
                              instance_id: str) -> Optional[str]:
        ae = AE(ae_title=self.client_ae)
        ae.add_requested_context(StudyRootQueryRetrieveInformationModelFind)
        ae.add_requested_context(StudyRootQueryRetrieveInformationModelMove)

        with association(ae, self.pacs_url, self.pacs_port,
                         self.remote_ae) as assoc:
            with storage_scp(self.client_ae, self.dicom_dir) as scp:
                move_dataset = Dataset()
                move_dataset.StudyInstanceUID = study_id
                move_dataset.SeriesInstanceUID = series_id
                move_dataset.SOPInstanceUID = instance_id
                move_dataset.QueryRetrieveLevel = 'IMAGE'

                if scp.is_alive():
                    move_responses = assoc.send_c_move(
                        move_dataset,
                        scp.ae_title,
                        query_model=C_MOVE_QUERY_MODEL)
                else:
                    raise Exception(
                        f'Storage SCP failed to start for series {series_id}')

                check_responses(move_responses)

        dcm_path = os.path.join(self.dicom_dir, f'{instance_id}.dcm')
        png_path = process_and_write_png_from_file(dcm_path)
        return png_path
Ejemplo n.º 10
0
    def fetch_image_as_dicom_file(self, study_id: str, series_id: str,
                                  sop_instance_id: str) -> Optional[str]:
        series_path = os.path.join(self.dicom_dir, series_id)
        with storage_scp(self.client_ae, series_path) as scp:
            ae = AE(ae_title=self.client_ae)
            ae.add_requested_context(
                StudyRootQueryRetrieveInformationModelMove)

            with association(ae, self.pacs_url, self.pacs_port,
                             self.remote_ae) as assoc:
                dataset = Dataset()
                dataset.SeriesInstanceUID = series_id
                dataset.StudyInstanceUID = study_id
                dataset.SOPInstanceUID = sop_instance_id
                dataset.QueryRetrieveLevel = 'IMAGE'

                if scp.is_alive():
                    responses = assoc.send_c_move(
                        dataset, scp.ae_title, query_model=C_MOVE_QUERY_MODEL)
                else:
                    raise Exception(
                        f'Storage SCP failed to start for series {series_id}')

                check_responses(responses)
                filepath = scp.path_for_dataset_instance(dataset)
                return filepath if os.path.exists(filepath) else None
        return None
Ejemplo n.º 11
0
    def images_for_series(self,
                          study_id,
                          series_id,
                          additional_tags=None,
                          max_count=None) -> List[Dataset]:

        ae = AE(ae_title=self.client_ae)
        ae.add_requested_context(StudyRootQueryRetrieveInformationModelFind)

        image_datasets = []
        with association(ae, self.pacs_url, self.pacs_port,
                         self.remote_ae) as series_assoc:
            series_dataset = Dataset()
            series_dataset.StudyInstanceUID = study_id
            series_dataset.SeriesInstanceUID = series_id
            series_dataset.QueryRetrieveLevel = 'IMAGE'
            series_dataset.SOPInstanceUID = ''
            set_undefined_tags_to_blank(series_dataset, additional_tags)

            series_responses = series_assoc.send_c_find(
                series_dataset, query_model=C_FIND_QUERY_MODEL)
            for instance in checked_responses(series_responses):
                if hasattr(instance, 'SOPInstanceUID'):
                    ds = Dataset()
                    ds.SeriesInstanceUID = instance.SeriesInstanceUID
                    ds.SOPInstanceUID = instance.SOPInstanceUID
                    copy_dicom_attributes(ds, instance, additional_tags)
                    image_datasets.append(ds)
                    if max_count and len(image_datasets) >= max_count:
                        break
        return image_datasets