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.' }
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}' )
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
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")
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
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)
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'
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
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
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()
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()
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()
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
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)
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()
def make_my_ae(): ae = AE(MY.aet) ae.requested_contexts = QueryRetrievePresentationContexts return ae
def custom_store_scu(ae_title): ae = AE(ae_title=ae_title) ae.requested_contexts = StoragePresentationContexts return ae
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)
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:
- 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
#!/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')
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)
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)
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: