def test_association_acse_timeout(self): """ Check that the Association timeouts are being set correctly """ scp = AEVerificationSCP() scp.ae.acse_timeout = 0 scp.ae.dimse_timeout = 0 ae = AE(scu_sop_class=[VerificationSOPClass]) ae.acse_timeout = 0 ae.dimse_timeout = 0 assoc = ae.associate('localhost', 11112) self.assertTrue(scp.ae.active_associations[0].acse_timeout == 0) self.assertTrue(scp.ae.active_associations[0].dimse_timeout == 0) self.assertTrue(assoc.acse_timeout == 0) self.assertTrue(assoc.dimse_timeout == 0) assoc.release() scp.ae.acse_timeout = 21 scp.ae.dimse_timeout = 22 ae.acse_timeout = 31 ae.dimse_timeout = 32 assoc = ae.associate('localhost', 11112) self.assertTrue(scp.ae.active_associations[0].acse_timeout == 21) self.assertTrue(scp.ae.active_associations[0].dimse_timeout == 22) self.assertTrue(assoc.acse_timeout == 31) self.assertTrue(assoc.dimse_timeout == 32) assoc.release() self.assertRaises(SystemExit, scp.stop)
def test_associate_establish_release(self): """ Check SCU Association with SCP """ scp = AEVerificationSCP() ae = AE(scu_sop_class=[VerificationSOPClass]) assoc = ae.associate('localhost', 11112) self.assertTrue(assoc.is_established == True) assoc.release() self.assertTrue(assoc.is_established == False) self.assertRaises(SystemExit, scp.stop)
def test_associate_max_pdu(self): """ Check Association has correct max PDUs on either end """ scp = AEVerificationSCP() scp.ae.maximum_pdu_size = 54321 ae = AE(scu_sop_class=[VerificationSOPClass]) assoc = ae.associate('localhost', 11112, max_pdu=12345) self.assertTrue(scp.ae.active_associations[0].local_max_pdu == 54321) self.assertTrue(scp.ae.active_associations[0].peer_max_pdu == 12345) self.assertTrue(assoc.local_max_pdu == 12345) self.assertTrue(assoc.peer_max_pdu == 54321) assoc.release() # Check 0 max pdu value assoc = ae.associate('localhost', 11112, max_pdu=0) self.assertTrue(assoc.local_max_pdu == 0) self.assertTrue(scp.ae.active_associations[0].peer_max_pdu == 0) assoc.release() self.assertRaises(SystemExit, scp.stop)
def test_on_c_echo_called(self): """ Check that SCP AE.on_c_echo() was called """ scp = AEVerificationSCP() ae = AE(scu_sop_class=[VerificationSOPClass]) assoc = ae.associate('localhost', 11112) with patch.object(scp.ae, 'on_c_echo') as mock: assoc.send_c_echo() mock.assert_called_with() assoc.release() self.assertRaises(SystemExit, scp.stop)
def test_on_c_store_called(self): """ Check that SCP AE.on_c_store(dataset) was called """ scp = AEStorageSCP() ae = AE(scu_sop_class=StorageSOPClassList) assoc = ae.associate('localhost', 11112) #with patch.object(scp.ae, 'on_c_store') as mock: # assoc.send_c_store(dataset) #mock.assert_called_with() assoc.release() self.assertRaises(SystemExit, scp.stop)
def test_ae_release_assoc(self): """ Association releases OK """ # Start Verification SCP scp = AEVerificationSCP() ae = AE(scu_sop_class=[VerificationSOPClass]) # Test N associate/release cycles for ii in range(10): assoc = ae.associate('localhost', 11112) self.assertTrue(assoc.is_established) if assoc.is_established: assoc.release() self.assertTrue(assoc.is_established == False) self.assertTrue(assoc.is_released == True) self.assertTrue(ae.active_associations == []) # Kill Verification SCP (important!) self.assertRaises(SystemExit, scp.stop)
handler.setLevel(logging.INFO) formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) console = logging.StreamHandler() console.setLevel(logging.INFO) logger.addHandler(handler) logger.addHandler(console) ae = AE(ae_title=b'MY_STORAGE_SCU') ae.add_requested_context(UID('1.2.840.10008.5.1.4.1.1.3.1')) # ae.add_requested_context(UltrasoundMultiframeImageStorage) for cx in ae.requested_contexts: print(cx) # assoc = ae.associate('192.168.3.5', 4100) assoc = ae.associate('127.0.0.1', 4100) if assoc.is_established: logger.info('assoc is established') # dataset = dcmread('./MRI.dcm') dataset = dcmread('./IMG00001') # `status` is the response from the peer to the store request # but may be an empty pydicom Dataset if the peer timed out or # sent an invalid dataset. status = assoc.send_c_store(dataset) assoc.release() else: logger.info('no assoc')
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)
# Create out identifier (query) dataset ds = Dataset() #http://dicom.nema.org/medical/dicom/current/output/chtml/part04/sect_C.6.html (PATIENT, STUDY, SERIES, IMAGES) ds.QueryRetrieveLevel = 'STUDY' # Unique key for PATIENT level #ds.StudyDate = '20060722' #ds[0x0008, 0x0020] = DataElement(0x00080020, 'DA', '20060722') ds.StudyInstanceUID = '*' # Unique key for STUDY level #ds.StudyInstanceUID = '1.3.6.1.4.1.5962.1.2.0.1175775771.5702.0' # Unique key for SERIES level # ds.SeriesInstanceUID = '1.2.3.4' # Associate with peer AE at IP 127.0.0.1 and port 11112 assoc = ae.associate(source_pacs_ip, source_pacs_port, ae_title = source_pacs_ae_title) if assoc.is_established: # Use the C-MOVE service to send the identifier responses = assoc.send_c_move(ds, destination_pacs_ae_title, StudyRootQueryRetrieveInformationModelMove) for (status, identifier) in responses: #status : Failure, Cancel, Warning, Success, Pending if status: if status.Status == 0xFF00: print('Pending') elif status.Status == 0x0000: print('Success') print('Number of Completed Sub-operations ' + str(status.get(0x1021).value)) print('Number of Failed Sub-operations ' + str(status.get(0x1022).value)) print('Number of Warning Sub-operations ' + str(status.get(0x1023).value))
else: transfer_syntax = [ ExplicitVRLittleEndian, ImplicitVRLittleEndian, DeflatedExplicitVRLittleEndian, ExplicitVRBigEndian ] for cx in StoragePresentationContexts: ae.add_requested_context(cx.abstract_syntax, transfer_syntax) if not lfiles: APP_LOGGER.warning("No suitable DICOM files found") sys.exit() # Request association with remote assoc = ae.associate(args.addr, args.port, ae_title=args.called_aet, max_pdu=args.max_pdu) if assoc.is_established: ii = 1 for fpath in lfiles: APP_LOGGER.info('Sending file: {}'.format(fpath)) try: ds = dcmread(fpath) status = assoc.send_c_store(ds, ii) ii += 1 except InvalidDicomError: APP_LOGGER.error('Bad DICOM file: {}'.format(fpath)) except ValueError as exc: APP_LOGGER.error("Store failed: {}".format(fpath)) except Exception as exc: APP_LOGGER.error("Store failed: {}".format(fpath))
sys.exit() # Set Transfer Syntax options transfer_syntax = [ ExplicitVRLittleEndian, ImplicitVRLittleEndian, DeflatedExplicitVRLittleEndian, ExplicitVRBigEndian ] if args.request_little: transfer_syntax = [ExplicitVRLittleEndian] elif args.request_big: transfer_syntax = [ExplicitVRBigEndian] elif args.request_implicit: transfer_syntax = [ImplicitVRLittleEndian] # Bind to port 0, OS will pick an available port ae = AE(ae_title=args.calling_aet) for context in StoragePresentationContexts: ae.add_requested_context(context.abstract_syntax, transfer_syntax) # Request association with remote assoc = ae.associate(args.peer, args.port, ae_title=args.called_aet) if assoc.is_established: APP_LOGGER.info('Sending file: {0!s}'.format(args.dcmfile_in)) status = assoc.send_c_store(dataset) assoc.release()
def send_store(nr_assoc, ds_per_assoc, use_yappi=False): """Send a number of sequential C-STORE requests. Parameters ---------- nr_assoc : int The total number of (sequential) associations that will be made. ds_per_assoc : int The number of C-STORE requests sent per successful association. use_yappi : bool, optional True to use the yappi profiler, False otherwise (default). """ if use_yappi: init_yappi() # Start SCP server = start_storescp() time.sleep(0.5) ae = AE() ae.acse_timeout = 5 ae.dimse_timeout = 5 ae.network_timeout = 5 ae.add_requested_context(DATASET.SOPClassUID, ImplicitVRLittleEndian) # Start timer start_time = time.time() run_times = [] is_successful = True for ii in range(nr_assoc): if not is_successful: break assoc = ae.associate('localhost', 11112) if assoc.is_established: for jj in range(ds_per_assoc): try: status = assoc.send_c_store(DATASET) if status and status.Status != 0x0000: is_successful = False break except RuntimeError: is_successful = False break assoc.release() if is_successful: run_times.append(time.time() - start_time) else: is_successful = False break if is_successful: print("C-STORE SCU transferred {} total datasets over {} " "association(s) in {:.2f} s".format(nr_assoc * ds_per_assoc, nr_assoc, time.time() - start_time)) else: print("C-STORE SCU benchmark failed") server.terminate()
def main(args=None): """Run the application.""" if args is not None: sys.argv = args args = _setup_argparser() if args.version: print('storescu.py v{__version__}') sys.exit() APP_LOGGER = setup_logging(args, 'storescu') APP_LOGGER.debug('storescu.py v{__version__}') APP_LOGGER.debug('') lfiles, badfiles = get_files(args.path, args.recurse) for bad in badfiles: APP_LOGGER.error("Cannot access path: {bad}") ae = AE(ae_title=args.calling_aet) ae.acse_timeout = args.acse_timeout ae.dimse_timeout = args.dimse_timeout ae.network_timeout = args.network_timeout if args.required_contexts: # Only propose required presentation contexts lfiles, contexts = get_contexts(lfiles, APP_LOGGER) try: for abstract, transfer in contexts.items(): for tsyntax in transfer: ae.add_requested_context(abstract, tsyntax) except ValueError: raise ValueError( "More than 128 presentation contexts required with " "the '--required-contexts' flag, please try again " "without it or with fewer files" ) else: # Propose the default presentation contexts if args.request_little: transfer_syntax = [ExplicitVRLittleEndian] elif args.request_big: transfer_syntax = [ExplicitVRBigEndian] elif args.request_implicit: transfer_syntax = [ImplicitVRLittleEndian] else: transfer_syntax = [ ExplicitVRLittleEndian, ImplicitVRLittleEndian, DeflatedExplicitVRLittleEndian, ExplicitVRBigEndian ] for cx in StoragePresentationContexts: ae.add_requested_context(cx.abstract_syntax, transfer_syntax) if not lfiles: APP_LOGGER.warning("No suitable DICOM files found") sys.exit() # Request association with remote assoc = ae.associate( args.addr, args.port, ae_title=args.called_aet, max_pdu=args.max_pdu ) if assoc.is_established: ii = 1 for fpath in lfiles: APP_LOGGER.info('Sending file: {fpath}') try: ds = dcmread(fpath) status = assoc.send_c_store(ds, ii) ii += 1 except InvalidDicomError: APP_LOGGER.error('Bad DICOM file: {fpath}') except Exception as exc: APP_LOGGER.error("Store failed: {fpath}") APP_LOGGER.exception(exc) assoc.release() else: sys.exit(1)
# Add a requested presentation context ae.add_requested_context(PatientRootQueryRetrieveInformationModelMove) # Create out identifier (query) dataset ds = Dataset() ds.QueryRetrieveLevel = 'SERIES' # Unique key for PATIENT level ds.PatientID = '1234567' # Unique key for STUDY level ds.StudyInstanceUID = '1.2.3' # Unique key for SERIES level ds.SeriesInstanceUID = '1.2.3.4' # 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-MOVE service to send the identifier responses = assoc.send_c_move( ds, b'STORE_SCP', PatientRootQueryRetrieveInformationModelMove) for (status, identifier) in responses: if status: print('C-MOVE query status: 0x{0:04x}'.format(status.Status)) # If the status is 'Pending' then the identifier is the C-MOVE response if status.Status in (0xFF00, 0xFF01): print(identifier) else: print(
ExplicitVRBigEndian] if args.request_little: transfer_syntax = [ExplicitVRLittleEndian] elif args.request_big: transfer_syntax = [ExplicitVRBigEndian] elif args.request_implicit: transfer_syntax = [ImplicitVRLittleEndian] # Bind to port 0, OS will pick an available port ae = AE(ae_title=args.calling_aet, port=0, scu_sop_class=StorageSOPClassList, scp_sop_class=[], transfer_syntax=transfer_syntax) # Request association with remote assoc = ae.associate(args.peer, args.port, args.called_aet) if assoc.is_established: logger.info('Sending file: %s' %args.dcmfile_in) status = assoc.send_c_store(dataset) assoc.release() # Quit ae.quit()
# We need to supply a Unique Key Attribute for each level above the # Query/Retrieve level ds = Dataset() ds.QueryRetrieveLevel = 'SERIES' # Unique key for PATIENT level ds.PatientID = '8435' # Unique key for STUDY level ds.StudyInstanceUID = '1.2.3' # Unique key for SERIES level ds.SeriesInstanceUID = '1.2.3.4' ds.SOPInstanceUID = '1.2.3' ds.SOPClassUID = '1.2.840.10008.5.1.4.1.1.2' # Associate with peer AE at IP 127.0.0.1 and port 11112 assoc = ae.associate('medicac.fortiddns.com', 4006, ext_neg=[role], evt_handlers=handlers) if assoc.is_established: # Use the C-GET service to send the identifier responses = assoc.send_c_get(ds, StudyRootQueryRetrieveInformationModelGet) for (status, identifier) in responses: if status: print('C-GET query status: 0x{0:04x}'.format(status.Status)) # If the status is 'Pending' then `identifier` is the C-GET response if status.Status in (0xFF00, 0xFF01): print(identifier) else: print(
def main(args=None): """Run the application.""" if args is not None: sys.argv = args args = _setup_argparser() if args.version: print('getscu.py v{}'.format(__version__)) sys.exit() APP_LOGGER = setup_logging(args, 'getscu') APP_LOGGER.debug('getscu.py v{0!s}'.format(__version__)) APP_LOGGER.debug('') # Create query (identifier) dataset try: # If you're looking at this to see how QR Get 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) # Exclude these SOP Classes _exclusion = [ PlannedImagingAgentAdministrationSRStorage, PerformedImagingAgestAdministrationSRStorage, EncapsulatedSTLStorage, ] store_contexts = [ cx for cx in StoragePresentationContexts if cx.abstract_syntax not in _exclusion ] # Create application entity # Binding to port 0 lets the OS pick an available port ae = AE(ae_title=args.calling_aet) ae.acse_timeout = args.acse_timeout ae.dimse_timeout = args.dimse_timeout ae.network_timeout = args.network_timeout ext_neg = [] ae.add_requested_context(PatientRootQueryRetrieveInformationModelGet) ae.add_requested_context(StudyRootQueryRetrieveInformationModelGet) ae.add_requested_context(PatientStudyOnlyQueryRetrieveInformationModelGet) for cx in store_contexts: ae.add_requested_context(cx.abstract_syntax) # Add SCP/SCU Role Selection Negotiation to the extended negotiation # We want to act as a Storage SCP ext_neg.append(build_role(cx.abstract_syntax, scp_role=True)) if args.study: query_model = StudyRootQueryRetrieveInformationModelGet elif args.psonly: query_model = PatientStudyOnlyQueryRetrieveInformationModelGet else: query_model = PatientRootQueryRetrieveInformationModelGet # Request association with remote assoc = ae.associate(args.addr, args.port, ae_title=args.called_aet, ext_neg=ext_neg, evt_handlers=[(evt.EVT_C_STORE, handle_store, [args, APP_LOGGER])], max_pdu=args.max_pdu) if assoc.is_established: # Send query responses = assoc.send_c_get(identifier, query_model) for (status, rsp_identifier) in responses: # If `status.Status` is one of the 'Pending' statuses then # `rsp_identifier` is the C-GET 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() else: sys.exit(1)
# -*- coding;utf-8 -*- from pynetdicom import AE ae = AE() ae.add_requested_context('1.2.840.10008.1.1') assoc = ae.associate('localhost', 11112) if assoc.is_established: print('Association established with Echo SCP!') assoc.release() else: # Association rejected, aborted or never connected print('Failed to associate')
console = logging.StreamHandler() console.setLevel(logging.INFO) logger.addHandler(console) ds = dcmread('/Volumes/Transcend/mediclouds/PACS/MRI.dcm') # ds = dcmread('/Volumes/Transcend/mediclouds/PACS/IMG00001') ae = AE(ae_title=b'ilab_scu') ae.requested_contexts = StoragePresentationContexts # ae.add_requested_context(VerificationPresentationContexts) # ae.add_requested_context(VerificationSOPClass) # for cx in ae.requested_contexts: # print(cx) assoc = ae.associate('192.168.3.5', 4100, ae_title=b'lkjds') if assoc.is_established: logger.info('assoc is established') dataset = dcmread('./MRI.dcm') # dataset = dcmread('./IMG00001') # `status` is the response from the peer to the store request # but may be an empty pydicom Dataset if the peer timed out or # sent an invalid dataset. status = assoc.send_c_store(ds, 1, 1) assoc.release() else: logger.info('no assoc') # if assoc.is_established: # print('Association connected')
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(('', 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)
def _send_ctimage(hostname, port, transfersyntax): ae = AE() ae.add_requested_context(CTImageStorage, transfersyntax) assoc = ae.associate(hostname, int(port)) return assoc
LOGGER.error(" {0!s}".format(os.path.dirname(filename))) LOGGER.error('Directory may not exist or you may not have write ' 'permission') # Failed - Out of Resources - IOError status_ds.Status = 0xA700 except: LOGGER.error('Could not write file to specified directory:') LOGGER.error(" {0!s}".format(os.path.dirname(filename))) # Failed - Out of Resources - Miscellaneous error status_ds.Status = 0xA701 return status_ds ae.on_c_store = on_c_store # Request association with remote assoc = ae.associate(args.peer, args.port, ae_title=args.called_aet, ext_neg=ext_neg) # Send query if assoc.is_established: response = assoc.send_c_get(d, query_model=query_model) for status, identifier in response: pass assoc.release()
# Add SCP/SCU Role Selection Negotiation to the extended negotiation # We want to act as a Storage SCP ext_neg.append(build_role(cx.abstract_syntax, scp_role=True)) if args.study: query_model = StudyRootQueryRetrieveInformationModelGet elif args.psonly: query_model = PatientStudyOnlyQueryRetrieveInformationModelGet else: query_model = PatientRootQueryRetrieveInformationModelGet # Request association with remote assoc = ae.associate(args.addr, args.port, ae_title=args.called_aet, ext_neg=ext_neg, evt_handlers=[(evt.EVT_C_STORE, handle_store, [args, APP_LOGGER])], max_pdu=args.max_pdu) if assoc.is_established: # Send query responses = assoc.send_c_get(identifier, query_model) for (status, rsp_identifier) in responses: # If `status.Status` is one of the 'Pending' statuses then # `rsp_identifier` is the C-GET 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
scp_sop_class=StorageSOPClassList, transfer_syntax=[ExplicitVRLittleEndian]) # Set the extended negotiation SCP/SCU role selection to allow us to receive # C-STORE requests for the supported SOP classes ext_neg = [] for context in ae.presentation_contexts_scu: tmp = SCP_SCU_RoleSelectionNegotiation() tmp.sop_class_uid = context.AbstractSyntax tmp.scu_role = False tmp.scp_role = True ext_neg.append(tmp) # Request association with remote assoc = ae.associate(args.peer, args.port, args.called_aet, ext_neg=ext_neg) # Create query dataset d = Dataset() d.PatientsName = '*' d.QueryRetrieveLevel = "PATIENT" if args.patient: query_model = 'P' elif args.study: query_model = 'S' elif args.psonly: query_model = 'O' else: query_model = 'P'
class Mover(object): """docstring for DCMover""" def __init__(self, config): # Initialize the server configuration for the DC Mover self.client_name = config['client_name'] self.client_port = config['client_port'] self.host_ip = config['host_ip'] self.host_port = config['host_port'] # Initialize the settings for the mover self.query_model = config['query_model'] self.mv_brk_cnt = config['query_break_count'] # Start ApplicationEntity for communication with PACS self.ae = AE(ae_title=self.client_name) # Adding contexts as required self.ae.add_requested_context('1.2.840.10008.1.1') # Add echo context if self.query_model == 'S': self.ae.add_requested_context( StudyRootQueryRetrieveInformationModelMove) self.ae.add_requested_context( StudyRootQueryRetrieveInformationModelFind) elif self.query_model == 'P': self.ae.add_requested_context( PatientRootQueryRetrieveInformationModelMove) self.ae.add_requested_context( PatientRootQueryRetrieveInformationModelFind) else: print('ERROR: Query model not recognized.') # Associate ApplicationEntity with PACS self.assoc = self.ae.associate(self.host_ip, self.host_port) self.currently_associated = self.assoc_check() def assoc_check(self, print_status=False): if self.assoc.is_established: if print_status: print('SUCCESS: associated with PACS entity') return True else: if print_status: print('FAILURE: not associated with PACS entity') return False def dictify(self, ds): """Turn a pydicom Dataset into a dict with keys derived from the Element keywords. Source: https://github.com/pydicom/pydicom/issues/319" Parameters ---------- ds : pydicom.dataset.Dataset The Dataset to dictify Returns ------- output : dict """ output = dict() for elem in ds: if elem.VR != 'SQ': output[elem.keyword] = str(elem.value) else: output[elem.keyword] = [self.dictify(item) for item in elem] return output @staticmethod def make_qry_ds(qry): ds = Dataset() for i in qry: setattr(ds, i, qry[i]) return ds def send_c_move(self, qry_dict): qry_ds = self.make_qry_ds(qry_dict) qry_response = {'status': list(), 'data': list()} responses = self.assoc.send_c_move(qry_ds, self.client_name, query_model=self.query_model) cnt = 0 for status, ds in responses: status_dict = self.dictify(status) status_dict['status_category'] = code_to_category(status.Status) qry_response['status'].append(status_dict) if ds: data_dict = self.dictify(ds) qry_response['data'].append(data_dict) if cnt == self.mv_brk_cnt - 1: if 'NumberOfCompletedSuboperations' in qry_response['status']: if sum( int(i['NumberOfCompletedSuboperations']) for i in qry_response['status']) == 0: qry_response['status'].append({ 'Status': 'BREAK @ COUNT ={}'.format(self.mv_brk_cnt) }) print( 'ABORTING MOVE: {} STATUSES RECEIVED WITHOUT FILE MOVEMENT' .format(self.mv_brk_cnt)) break cnt += 1 return qry_response def send_c_echo(self): status = self.assoc.send_c_echo() qry_response = { 'status': { 'code': status.Status, 'category': code_to_category(status.Status) } } return qry_response def send_c_find(self, qry_dict): qry_ds = self.make_qry_ds(qry_dict) qry_response = {'status': list(), 'data': list()} responses = self.assoc.send_c_find(qry_ds, query_model=self.query_model) for status, ds in responses: status_dict = { 'code': status.Status, 'category': code_to_category(status.Status) } qry_response['status'].append(status_dict) if ds: data_dict = self.dictify(ds) qry_response['data'].append(data_dict) return qry_response