Example #1
0
def upload_dicom_file(node, dcm_file):
    '''
    Upload the received file to the node
    '''
    try:
        ds = dcmread(dcm_file)
    except:
        return {'status': 400, 'error': 'Invalid DICOM file.'}  # bad request
    ae = AE()
    ae.requested_contexts = StoragePresentationContexts

    assoc = ae.associate(node['address'], node['port'])
    if assoc.is_established:
        status = assoc.send_c_store(ds)
        assoc.release()
        return {
            'status': 201,
            'study_instance_uid': ds.StudyInstanceUID,
            'study_id': ds.StudyID,
            'study_date': ds.StudyDate,
            'patient_id': ds.PatientID
        }
    else:
        return {
            'status':
            500,
            'error':
            'Association with backend DICOM node rejected, aborted, or never connected.'
        }
Example #2
0
    def send_datasets(self,
                      datasets: Iterable[Dataset],
                      override_remote_ae: str = None,
                      override_pacs_url: str = None,
                      override_pacs_port: int = None) -> None:
        if override_remote_ae is not None and override_pacs_url is not None and override_pacs_port is not None:
            send_remote_ae = override_remote_ae
            send_port = override_pacs_port
            send_url = override_pacs_url
        else:
            send_remote_ae = self.remote_ae
            send_port = self.pacs_port
            send_url = self.pacs_url

        ae = AE(ae_title=self.client_ae)
        ae.requested_contexts = StoragePresentationContexts
        with association(ae, send_url, send_port, send_remote_ae) as assoc:
            if assoc.is_established:
                for dataset in datasets:
                    logger.info('Sending %s', dataset.SeriesInstanceUID)
                    status = assoc.send_c_store(dataset)
                    for keyword in ['ErrorComment', 'OffendingElement']:
                        if getattr(status, keyword, None) is not None:
                            raise Exception(
                                f'failed to send because status[{keyword} = {getattr(status, keyword)}'
                            )
            else:
                raise Exception(
                    f'Unable to send because failed to establish association with {self.pacs_url}:{self.pacs_port}'
                )
Example #3
0
    def do_find(self, dataset, query_model="P"):

        ae = AE()

        ae.requested_contexts = QueryRetrievePresentationContexts

        if len(self.ae_title) > 0:
            assoc = ae.associate(self.host, self.port, ae_title=self.ae_title)
        else:
            assoc = ae.associate(self.host, self.port)

        results = []
        if assoc.is_established:
            logger.info("Association accepted by the peer")

            responses = assoc.send_c_find(dataset, query_model=query_model)

            for resp, ds in responses:
                results.append(ds)

            # Release the association
            assoc.release()

        logger.info("Got " + str(len(results)) + " results")

        return results
Example #4
0
    def move_series(self,
                    seriesInstanceUID,
                    move_aet="PYNETDICOM",
                    query_model="P"):

        ae = AE()
        ae.requested_contexts = QueryRetrievePresentationContexts

        if len(self.ae_title) > 0:
            assoc = ae.associate(self.host, self.port, ae_title=self.ae_title)
        else:
            assoc = ae.associate(self.host, self.port)

        if assoc.is_established:
            logger.info("Association accepted by the peer")

            dataset = Dataset()
            dataset.SeriesInstanceUID = seriesInstanceUID
            dataset.QueryRetrieveLevel = "SERIES"

            responses = assoc.send_c_move(dataset,
                                          move_aet,
                                          query_model=query_model)

            for (a, b) in responses:
                pass

            # Release the association
            assoc.release()

        logger.info("Finished")
Example #5
0
def get_association_to_ae(ae_title):

    # Create association to pipeline
    ae = AE(ae_title='test')
    ae.requested_contexts = StoragePresentationContexts
    assoc = ae.associate(config.SCP_HOST, config.SCP_PORT, ae_title=ae_title)

    assert assoc.is_established
    return assoc
Example #6
0
def do_store(d):
    ae = AE(MY.aet)
    ae.requested_contexts = StoragePresentationContexts
    assoc = ae.associate(REMOTE.address,
                         REMOTE.port,
                         ae_title=REMOTE.aet,
                         max_pdu=32764)
    generator = assoc.send_c_store(d)
    return run_query(generator)
Example #7
0
def do_ping():
    ae = AE(MY.aet)
    ae.requested_contexts = VerificationPresentationContexts
    assoc = ae.associate(REMOTE.address,
                         REMOTE.port,
                         ae_title=REMOTE.aet,
                         max_pdu=32764)
    if assoc.isAlive():
        generator = assoc.send_c_echo()
        return run_query(generator)
    else:
        return 'Failed due to no assoc'
Example #8
0
def StoreSCU(remotehost,
             remoteport,
             dicomobject,
             aec,
             aet='PYNETDICOM',
             verbose=False):

    MyAE = AE(ae_title=aet)

    # only require Secondary Capture Image Storage beside Verification
    MyAE.requested_contexts = VerificationPresentationContexts
    MyAE.add_requested_context(SecondaryCaptureImageStorage,
                               transfer_syntax=ts)

    # Try and associate with the peer AE
    #   Returns the Association thread
    if verbose:
        print("Requesting Association with peer {0}@{1}:{2}".format(
            aec, remotehost, remoteport))
    assoc = MyAE.associate(addr=remotehost, ae_title=aec, port=remoteport)

    if verbose:
        if assoc.is_established:
            print('Association accepted by peer')
        else:
            print("Could not establish association")
            sys.exit(1)

    # perform a DICOM ECHO, just to make sure remote AE is listening
    if verbose:
        print("DICOM C-ECHO... ", end='')
    status = assoc.send_c_echo()
    if status and verbose:
        print('Response: 0x{0:04x}'.format(status.Status))

    # Send a DIMSE C-STORE request to the peer
    if verbose:
        print("DICOM C-STORE... ", end='')
    try:
        status = assoc.send_c_store(dicomobject)
        if verbose:
            print('Response: 0x{0:04x}'.format(status.Status))
    except:
        if verbose:
            print("problem {}".format(dicomobject.SOPClassUID))
        return False

    if verbose:
        print("Release association")
    assoc.release()

    return True
Example #9
0
    def __get_assoc(self):
        ae = AE(ae_title=config.SCP_AE_TITLE)

        if type(self.contexts) is list:
            ae.requested_contexts = self.contexts
        else:
            ae.add_requested_context(self.contexts)

        assoc = ae.associate(addr=self.host, port=self.port, ae_title=self.ae_title, **self.kwargs)

        if not assoc.is_established:
            raise AssociationException(ae_title=self.ae_title)

        return assoc
Example #10
0
def retrieve_study(cfg, acc_no, output_dir):
    if not os.path.exists(output_dir):
        try:
            os.makedirs(output_dir)
        except OSError as exc:  # Guard against race condition
            if exc.errno != errno.EEXIST:
                raise

    ae = AE(ae_title=cfg['pacs']['my']['aet'])
    ae.requested_contexts = QueryRetrievePresentationContexts
    assoc = ae.associate(cfg['pacs']['called']['ip'],
                         cfg['pacs']['called']['port'])
    ds = Dataset()
    ds.AccessionNumber = acc_no
    ds.QueryRetrieveLevel = "SERIES"
    ds.Modality = ""
    responses = assoc.send_c_find(ds, query_model='P')
    for (status, dataset) in responses:
        if status.Status in (0xFF00, 0xFF01):
            modality = dataset.Modality
            if modality not in ('CR', 'DX'):
                continue

            p_id = dataset.PatientID
            study_uid = dataset.StudyInstanceUID
            series_uid = dataset.SeriesInstanceUID

            cmd_str = r'''movescu {pacs_ip} {pacs_port} +P {port} +xa -aec {pacs_aet} -aet {aet} \
                          -k QueryRetrieveLevel=SERIES \
                          -k PatientID={p_id} \
                          -k StudyInstanceUID={study_uid} \
                          -k SeriesInstanceUID={series_uid} \
                          -od {output_dir} \
                        '''.format(pacs_ip=cfg['pacs']['called']['ip'],
                                   pacs_port=cfg['pacs']['called']['port'],
                                   pacs_aet=cfg['pacs']['called']['aet'],
                                   aet=cfg['pacs']['my']['aet'],
                                   port=cfg['pacs']['my']['port'],
                                   p_id=p_id,
                                   study_uid=study_uid,
                                   series_uid=series_uid,
                                   output_dir=output_dir)
            #print(cmd_str)
            os.system(cmd_str)
            print("Success. acc_no={}".format(acc_no))
        elif status.Status != 0x0:
            print("acc_no={}, status={}".format(acc_no, hex(status.Status)))
    assoc.release()
Example #11
0
def retrieve_study(cfg, acc_no, output_dir):
    if not os.path.exists(output_dir):
        try:
            os.makedirs(output_dir)
        except OSError as exc:  # Guard against race condition
            if exc.errno != errno.EEXIST:
                raise

    ae = AE(ae_title=cfg['pacs']['my']['aet'])
    ae.requested_contexts = QueryRetrievePresentationContexts
    assoc_cf = ae.associate(cfg['pacs']['called']['ip'],
                            cfg['pacs']['called']['port'])
    assoc_cm = ae.associate(cfg['pacs']['called']['ip'],
                            cfg['pacs']['called']['port'])
    ds_q = Dataset()
    ds_q.AccessionNumber = acc_no
    ds_q.QueryRetrieveLevel = "SERIES"
    ds_q.Modality = ""
    responses_cf = assoc_cf.send_c_find(ds_q, query_model='P')
    for (status_cf, dataset_cf) in responses_cf:
        if status_cf.Status in (0xFF00, 0xFF01):
            modality = dataset_cf.Modality
            if modality not in ('CR', 'DX'):
                continue

            responses_cm = assoc_cm.send_c_move(dataset_cf,
                                                cfg['pacs']['my']['aet'],
                                                query_model='P')
            for (status_cm, dataset_cm) in responses_cm:
                if (status_cm.Status == 0x0):
                    pass
                else:
                    print('status: {}; dataset: {}'.format(
                        status_cm, dataset_cm))
        elif status_cf.Status != 0x0:
            print("acc_no={}, status={}".format(acc_no, hex(status_cf.Status)))
    assoc_cf.release()
    assoc_cm.release()
Example #12
0
def upload_folder(upload_dir, upload_ae_title):
    """ Small test program that uploads an entire dicom directory to a node """

    ae = AE(ae_title='BC158VIPT1')
    ae.requested_contexts = StoragePresentationContexts

    assoc = ae.associate('localhost', 11112, ae_title=upload_ae_title)
    if assoc.is_established:
        print('ESTABLISHED')

        for root, _, files in os.walk(upload_dir):
            for file in files:
                if file.endswith('.dcm'):
                    ds = dcmread(os.path.join(root, file))
                    status = assoc.send_c_store(ds)
                    if not status:
                        print(
                            'Connection timed out, was aborted or received invalid response'
                        )
    else:
        print('Not established')

    assoc.release()
Example #13
0
    def verify(self):
        # Verify Connection

        ae = AE()
        ae.requested_contexts = VerificationPresentationContexts

        # Associate with a peer DICOM AE
        if len(self.ae_title) > 0:
            assoc = ae.associate(self.host, self.port, ae_title=self.ae_title)
        else:
            assoc = ae.associate(self.host, self.port)

        result = None

        if assoc.is_established:
            status = assoc.send_c_echo()

            if status:
                result = status.Status

            # Release the association
            assoc.release()

        return not result == None
Example #14
0
def ingestion_loop():
    logger.log(logging.INFO, 'Creating application entity...')
    ae = AE(ae_title=b'DEEPKNEE')
    ae.requested_contexts = VerificationPresentationContexts
    ae.add_requested_context('1.2.840.10008.5.1.4.1.1.1.1', transfer_syntax=ImplicitVRLittleEndian)

    current = 0
    base_url = f'{args.orthanc_addr}:{args.orthanc_http_port}'
    response = requests.get(f'{base_url}/changes?since={current}&limit=10', auth=('deepknee', 'deepknee'))
    if response.status_code == 200:
        logger.log(logging.INFO, 'Connection to Orthanc via REST is healthy')

    # Orthanc addr must have http, but DICOM communicates via sockets
    assoc = ae.associate(args.orthanc_addr.split('http://')[1], args.orthanc_dicom_port)
    if assoc.is_established:
        logger.log(logging.INFO, 'Connection to Orthanc via DICOM is healthy')
        assoc.release()

    assoc = ae.associate(args.remote_pacs_addr, args.remote_pacs_port)
    if assoc.is_established:
        logger.log(logging.INFO, 'Connection to Remote PACS via DICOM is healthy')
        assoc.release()

    while True:
        response = requests.get(f'{base_url}/changes?since={current}&limit=10', auth=('deepknee', 'deepknee'))
        response = response.json()
        for change in response['Changes']:
            # We must also filter by the imaged body part in the future
            if change['ChangeType'] == 'NewInstance':
                logger.log(logging.INFO, 'Identified new received instance in Orthanc. '
                                         'Checking if it has been created by DeepKnee...')
                # We should not analyze the instances if they are produced by DeepKnee
                # Checking if it was verified by DeepKnee
                resp_verifier = requests.get(f'{base_url}/instances/{change["ID"]}/content/0040-a027',
                                             auth=('deepknee', 'deepknee'))
                resp_verifier.encoding = 'utf-8'
                resp_content = requests.get(f'{base_url}/instances/{change["ID"]}/content/0070-0080',
                                            auth=('deepknee', 'deepknee'))

                resp_content.encoding = 'utf-8'

                if resp_verifier.text.strip("\x00 ") == 'UniOulu-DeepKnee' and \
                        resp_content.text.strip("\x00 ") == 'DEEPKNEE-XRAY':
                    continue

                # Once we are sure that the instance is new, we need to go ahead with teh analysis
                response = requests.get(f'{base_url}/instances/{change["ID"]}/file', auth=('deepknee', 'deepknee'))

                logger.log(logging.INFO, 'Instance has been retrieved from Orthanc')
                dicom_raw_bytes = response.content
                dcm = dcmread(DicomBytesIO(dicom_raw_bytes))

                dicom_base64 = base64.b64encode(dicom_raw_bytes).decode('ascii')
                logger.log(logging.INFO, 'Sending API request to DeepKnee core')
                url = f'{args.deepknee_addr}:{args.deepknee_port}/deepknee/predict/bilateral'
                response_deepknee = requests.post(url, json={'dicom': dicom_base64})

                if response_deepknee.status_code != 200:
                    logger.log(logging.INFO, 'DeepKnee analysis has failed')
                else:
                    logger.log(logging.INFO, 'Getting rid of the instance in Orthanc')
                    if args.orthanc_addr.split('http://')[1] != args.remote_pacs_addr and \
                            args.orthanc_dicom_port != args.remote_pacs_port:
                        response = requests.delete(f'{base_url}/instances/{change["ID"]}',
                                                   auth=('deepknee', 'deepknee'))
                        if response.status_code == 200:
                            logger.log(logging.INFO, 'Instance has been removed from the Orthanc')
                    else:
                        logger.log(logging.INFO, 'Remote PACS is DeepKnee. The instance will not be removed.')

                    logger.log(logging.INFO, 'DeepKnee has successfully analyzed the image. Routing...')

                    # Report
                    deepknee_json = response_deepknee.json()
                    dcm.add_new([0x40, 0xa160], 'LO', 'KL_right: {}, KL_left: {}'.format(deepknee_json['R']['kl'],
                                                                                         deepknee_json['L']['kl']))
                    # Verifier
                    dcm.add_new([0x40, 0xa027], 'LO', 'UniOulu-DeepKnee')
                    # Content label
                    dcm.add_new([0x70, 0x80], 'CS', 'DEEPKNEE-XRAY')

                    dcm[0x08, 0x8].value = 'DERIVED'
                    # Instance_UUID
                    current_uuid = dcm[0x08, 0x18].value
                    dcm[0x08, 0x18].value = generate_uid(prefix='.'.join(current_uuid.split('.')[:-1])+'.')
                    # Series UUID
                    current_uuid = dcm[0x20, 0x0e].value
                    dcm[0x20, 0x0e].value = generate_uid(prefix='.'.join(current_uuid.split('.')[:-1])+'.')
                    logger.log(logging.INFO, 'Connecting to Orthanc over DICOM')
                    assoc = ae.associate(args.remote_pacs_addr, args.remote_pacs_port)
                    if assoc.is_established:
                        logger.log(logging.INFO, 'Association with Orthanc has been established. Routing..')
                        routing_status = assoc.send_c_store(dcm)
                        logger.log(logging.INFO, f'Routing finished. Status: {routing_status}')
                        assoc.release()

            else:
                # Here there should be a code to remove the change from the pacs
                # Now nothing is done here
                pass
            current += 1
        time.sleep(1)
Example #15
0
    logger.setLevel(logging.INFO)
    pynetdicom_logger = logging.getLogger('pynetdicom')
    pynetdicom_logger.setLevel(logging.INFO)

if args.debug:
    logger.setLevel(logging.DEBUG)
    pynetdicom_logger = logging.getLogger('pynetdicom')
    pynetdicom_logger.setLevel(logging.DEBUG)

logger.debug('$findscu.py v{0!s}'.format(VERSION))
logger.debug('')

# Create application entity
# Binding to port 0 lets the OS pick an available port
ae = AE(ae_title=args.calling_aet, port=0)
ae.requested_contexts = (QueryRetrievePresentationContexts +
                         BasicWorklistManagementPresentationContexts)

# Request association with remote
assoc = ae.associate(args.peer, args.port, ae_title=args.called_aet)

if assoc.is_established:
    # Import query dataset
    # Check file exists and is readable and DICOM
    logger.debug('Checking input files')
    try:
        with open(args.dcmfile_in, 'rb') as f:
            dataset = dcmread(f, force=True)
    except IOError:
        logger.error('Cannot read input file {0!s}'.format(args.dcmfile_in))
        assoc.release()
        sys.exit()
Example #16
0
def make_my_ae():
    ae = AE(MY.aet)
    ae.requested_contexts = QueryRetrievePresentationContexts
    return ae
Example #17
0
def custom_store_scu(ae_title):
    ae = AE(ae_title=ae_title)
    ae.requested_contexts = StoragePresentationContexts

    return ae
Example #18
0
def main(args=None):
    """Run the application."""
    if args is not None:
        sys.argv = args

    args = _setup_argparser()

    if args.version:
        print(f"movescu.py v{__version__}")
        sys.exit()

    APP_LOGGER = setup_logging(args, "movescu")
    APP_LOGGER.debug(f"movescu.py v{__version__}")
    APP_LOGGER.debug("")

    # Create query (identifier) dataset
    try:
        # If you're looking at this to see how QR Move works then `identifer`
        # is a pydicom Dataset instance with your query keys, e.g.:
        #     identifier = Dataset()
        #     identifier.QueryRetrieveLevel = 'PATIENT'
        #     identifier.PatientName = '*'
        identifier = create_dataset(args, APP_LOGGER)
    except Exception as exc:
        APP_LOGGER.exception(exc)
        sys.exit(1)

    # Create application entity
    ae = AE()

    # Start the Store SCP (optional)
    scp = None
    if args.store:
        transfer_syntax = ALL_TRANSFER_SYNTAXES[:]
        store_handlers = [(evt.EVT_C_STORE, handle_store, [args, APP_LOGGER])]
        ae.ae_title = args.store_aet
        for cx in AllStoragePresentationContexts:
            ae.add_supported_context(cx.abstract_syntax, transfer_syntax)

        scp = ae.start_server(("localhost", args.store_port),
                              block=False,
                              evt_handlers=store_handlers)

    ae.ae_title = args.calling_aet
    ae.acse_timeout = args.acse_timeout
    ae.dimse_timeout = args.dimse_timeout
    ae.network_timeout = args.network_timeout
    ae.requested_contexts = QueryRetrievePresentationContexts
    ae.supported_contexts = []

    # Query/Retrieve Information Models
    if args.study:
        query_model = StudyRootQueryRetrieveInformationModelMove
    elif args.psonly:
        query_model = PatientStudyOnlyQueryRetrieveInformationModelMove
    else:
        query_model = PatientRootQueryRetrieveInformationModelMove

    # Extended Negotiation
    ext_neg = []
    ext_opts = [args.relational_retrieval, args.enhanced_conversion]
    if any(ext_opts):
        app_info = b""
        for option in ext_opts:
            app_info += b"\x01" if option else b"\x00"

        item = SOPClassExtendedNegotiation()
        item.sop_class_uid = query_model
        item.service_class_application_information = app_info
        ext_neg = [item]

    # Request association with remote AE
    assoc = ae.associate(
        args.addr,
        args.port,
        ae_title=args.called_aet,
        max_pdu=args.max_pdu,
        ext_neg=ext_neg,
    )
    if assoc.is_established:
        # Send query
        move_aet = args.move_aet or args.calling_aet
        responses = assoc.send_c_move(identifier, move_aet, query_model)
        for (status, rsp_identifier) in responses:
            # If `status.Status` is one of the 'Pending' statuses then
            #   `rsp_identifier` is the C-MOVE response's Identifier dataset
            if status and status.Status in [0xFF00, 0xFF01]:
                # `rsp_identifier` is a pydicom Dataset containing a query
                # response. You may want to do something interesting here...
                pass

        assoc.release()
        _EXIT_VALUE = 0
    else:
        _EXIT_VALUE = 1

    # Shutdown the Storage SCP (if used)
    if scp:
        scp.shutdown()

    sys.exit(_EXIT_VALUE)
Example #19
0
    logger.setLevel(logging.INFO)
    pynetdicom_logger = logging.getLogger('PYNETDICOM3_')
    pynetdicom_logger.setLevel(logging.INFO)

if args.debug:
    logger.setLevel(logging.DEBUG)
    pynetdicom_logger = logging.getLogger('pynetdicom')
    pynetdicom_logger.setLevel(logging.DEBUG)

logger.debug('$movescu.py v{0!s}'.format(VERSION))
logger.debug('')

# Create application entity
# Binding to port 0 lets the OS pick an available port
ae = AE(ae_title=args.calling_aet)
ae.requested_contexts = QueryRetrievePresentationContexts

# Request association with remote AE
assoc = ae.associate(args.peer, args.port, ae_title=args.called_aet)

if assoc.is_established:
    # Create query dataset
    ds = Dataset()
    ds.PatientName = '*'
    ds.QueryRetrieveLevel = "PATIENT"

    if args.patient:
        query_model = 'P'
    elif args.study:
        query_model = 'S'
    elif args.psonly:
Example #20
0
- 02_Storage_Service_Examples의 SCP도 port 11113으로 구동시키고 동작해야함. 
"""
import os

from pydicom import dcmread
from pydicom.data import get_testdata_files
from pydicom.dataset import Dataset

from pynetdicom import AE, StoragePresentationContexts
from pynetdicom.sop_class import PatientRootQueryRetrieveInformationModelMove

# Create application entity
ae = AE()

# Add the requested presentation contexts (Storage SCU)
ae.requested_contexts = StoragePresentationContexts
# Add a supported presentation context (QR Move SCP)
ae.add_supported_context(PatientRootQueryRetrieveInformationModelMove)


def get_known_aet():
    return {b'STORE_SCP       ': ('127.0.0.1', 11113)}


# Implement the AE.on_c_move callback
def on_c_move(ds, move_aet, context, info):
    """Respond to a C-MOVE request Identifier `ds`.

    Parameters
    ----------
    ds : pydicom.dataset.Dataset
Example #21
0
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import logging
from pydicom import dcmread
from pydicom.uid import ImplicitVRLittleEndian

from pynetdicom import AE, VerificationPresentationContexts
from pynetdicom.sop_class import CTImageStorage, MRImageStorage

logger = logging.getLogger('Resona7_simulator')

ae = AE(ae_title=b'Resona7')
# We can also do the same thing with the requested contexts
ae.requested_contexts = VerificationPresentationContexts
# Or we can use inbuilt objects like CTImageStorage.
# The requested presentation context's transfer syntaxes can also
#   be specified using a str/UID or list of str/UIDs
ae.add_requested_context(CTImageStorage,
                         transfer_syntax=ImplicitVRLittleEndian)
# Adding a presentation context with multiple transfer syntaxes
ae.add_requested_context(MRImageStorage,
                         transfer_syntax=[ImplicitVRLittleEndian,
                                          '1.2.840.10008.1.2.1'])

# assoc = ae.associate(addr, port)
assoc = ae.associate('192.168.0.6', 2345)
logger.info('simulator')
print('simulator')
if assoc.is_established:
    dataset = dcmread('./MRI.dcm')
Example #22
0
def main(args=None):
    """Run the application."""
    if args is not None:
        sys.argv = args

    args = _setup_argparser()

    if args.version:
        print(f"findscu.py v{__version__}")
        sys.exit()

    APP_LOGGER = setup_logging(args, "findscu")
    APP_LOGGER.debug(f"findscu.py v{__version__}")
    APP_LOGGER.debug("")

    # Create query (identifier) dataset
    try:
        # If you're looking at this to see how QR Find works then `identifer`
        # is a pydicom Dataset instance with your query keys, e.g.:
        #     identifier = Dataset()
        #     identifier.QueryRetrieveLevel = 'PATIENT'
        #     identifier.PatientName = ''
        identifier = create_dataset(args, APP_LOGGER)
    except Exception as exc:
        APP_LOGGER.exception(exc)
        sys.exit(1)

    # Create application entity
    # Binding to port 0 lets the OS pick an available port
    ae = AE(ae_title=args.calling_aet)

    # Set timeouts
    ae.acse_timeout = args.acse_timeout
    ae.dimse_timeout = args.dimse_timeout
    ae.network_timeout = args.network_timeout

    # Set the Presentation Contexts we are requesting the Find SCP support
    ae.requested_contexts = (QueryRetrievePresentationContexts +
                             BasicWorklistManagementPresentationContexts)

    # Query/Retrieve Information Models
    if args.worklist:
        query_model = ModalityWorklistInformationFind
    elif args.study:
        query_model = StudyRootQueryRetrieveInformationModelFind
    elif args.psonly:
        query_model = PatientStudyOnlyQueryRetrieveInformationModelFind
    else:
        query_model = PatientRootQueryRetrieveInformationModelFind

    # Extended Negotiation
    ext_neg = []
    ext_opts = [
        args.relational_query,
        args.dt_matching,
        args.fuzzy_names,
        args.timezone_adj,
        args.enhanced_conversion,
    ]
    if not args.worklist and any(ext_opts):
        app_info = b""
        for option in ext_opts:
            app_info += b"\x01" if option else b"\x00"

        item = SOPClassExtendedNegotiation()
        item.sop_class_uid = query_model
        item.service_class_application_information = app_info
        ext_neg = [item]
    elif args.worklist and any([args.fuzzy_names, args.timezone_adj]):
        app_info = b"\x01\x01"
        for option in [args.fuzzy_names, args.timezone_adj]:
            app_info += b"\x01" if option else b"\x00"

        item = SOPClassExtendedNegotiation()
        item.sop_class_uid = query_model
        item.service_class_application_information = app_info
        ext_neg = [item]

    # Request association with (QR/BWM) Find SCP
    assoc = ae.associate(
        args.addr,
        args.port,
        ae_title=args.called_aet,
        max_pdu=args.max_pdu,
        ext_neg=ext_neg,
    )
    if assoc.is_established:
        # Send C-FIND request, `responses` is a generator
        responses = assoc.send_c_find(identifier, query_model)
        # Used to generate filenames if args.write used
        fname = generate_filename()
        for (status, rsp_identifier) in responses:
            # If `status.Status` is one of the 'Pending' statuses then
            #   `rsp_identifier` is the C-FIND response's Identifier dataset
            if status and status.Status in [0xFF00, 0xFF01]:
                if args.write:
                    rsp_identifier.file_meta = get_file_meta(
                        assoc, query_model)
                    rsp_identifier.save_as(next(fname),
                                           write_like_original=False)

        # Release the association
        assoc.release()
    else:
        sys.exit(1)
Example #23
0
def main(args=None):
    """Run the application."""
    if args is not None:
        sys.argv = args

    args = _setup_argparser()

    if args.version:
        print('findscu.py v{}'.format(__version__))
        sys.exit()

    APP_LOGGER = setup_logging(args, 'findscu')
    APP_LOGGER.debug('findscu.py v{0!s}'.format(__version__))
    APP_LOGGER.debug('')

    # Create query (identifier) dataset
    try:
        # If you're looking at this to see how QR Find works then `identifer`
        # is a pydicom Dataset instance with your query keys, e.g.:
        #     identifier = Dataset()
        #     identifier.QueryRetrieveLevel = 'PATIENT'
        #     identifier.PatientName = ''
        identifier = create_dataset(args, APP_LOGGER)
    except Exception as exc:
        APP_LOGGER.exception(exc)
        raise exc
        sys.exit(1)

    # Create application entity
    # Binding to port 0 lets the OS pick an available port
    ae = AE(ae_title=args.calling_aet)

    # Set timeouts
    ae.acse_timeout = args.acse_timeout
    ae.dimse_timeout = args.dimse_timeout
    ae.network_timeout = args.network_timeout

    # Set the Presentation Contexts we are requesting the Find SCP support
    ae.requested_contexts = (QueryRetrievePresentationContexts +
                             BasicWorklistManagementPresentationContexts)

    # Query/Retrieve Information Models
    if args.worklist:
        query_model = ModalityWorklistInformationFind
    elif args.study:
        query_model = StudyRootQueryRetrieveInformationModelFind
    elif args.psonly:
        query_model = PatientStudyOnlyQueryRetrieveInformationModelFind
    else:
        query_model = PatientRootQueryRetrieveInformationModelFind

    # Request association with (QR/BWM) Find SCP
    assoc = ae.associate(args.addr,
                         args.port,
                         ae_title=args.called_aet,
                         max_pdu=args.max_pdu)
    if assoc.is_established:
        # Send C-FIND request, `responses` is a generator
        responses = assoc.send_c_find(identifier, query_model)
        # Used to generate filenames if args.write used
        fname = generate_filename()
        for (status, rsp_identifier) in responses:
            # If `status.Status` is one of the 'Pending' statuses then
            #   `rsp_identifier` is the C-FIND response's Identifier dataset
            if status and status.Status in [0xFF00, 0xFF01]:
                if args.write:
                    rsp_identifier.file_meta = get_file_meta(
                        assoc, query_model)
                    rsp_identifier.save_as(next(fname),
                                           write_like_original=False)

        # Release the association
        assoc.release()
    else:
        sys.exit(1)
Example #24
0
from pydicom.dataset import Dataset

from pynetdicom import AE, BasicWorklistManagementPresentationContexts

# Initialise the Application Entity
ae = AE()

# Add a requested presentation context
ae.requested_contexts = BasicWorklistManagementPresentationContexts

# Create our Identifier (query) dataset
ds = Dataset()
ds.PatientName = '*'

proc_seq = Dataset()
proc_seq.ScheduledStationAETitle = 'CTSCANNER'
proc_seq.ScheduledProcedureStepStartDate = '20181005'
proc_seq.Modality = 'CT'

ds.ScheduledProcedureStepSequence = [proc_seq]

# Associate with peer AE at IP 127.0.0.1 and port 11112
assoc = ae.associate('127.0.0.1', 11112)

if assoc.is_established:
    # Use the C-FIND service to send the identifier
    # A query_model value of 'W' means use the 'Modality Worklist
    #     Information Model - Find' presentation context
    responses = assoc.send_c_find(ds, query_model='W')

    for (status, identifier) in responses: