def test_length_check(self): """Test validate_ae_title with no length check.""" assert _config.ALLOW_LONG_DIMSE_AET is False aet = b"12345678901234567890" assert 16 == len(validate_ae_title(aet)) _config.ALLOW_LONG_DIMSE_AET = True assert 20 == len(validate_ae_title(aet)) _config.ALLOW_LONG_DIMSE_AET = False
def test_conversion_rq(self): """ Check conversion to a -RQ PDU produces the correct output """ primitive = C_MOVE() primitive.MessageID = 7 primitive.AffectedSOPClassUID = '1.2.840.10008.5.1.4.1.1.2' primitive.Priority = 0x02 primitive.MoveDestination = validate_ae_title("MOVE_SCP") ref_identifier = Dataset() ref_identifier.PatientID = '*' ref_identifier.QueryRetrieveLevel = "PATIENT" primitive.Identifier = BytesIO(encode(ref_identifier, True, True)) dimse_msg = C_MOVE_RQ() dimse_msg.primitive_to_message(primitive) pdvs = [] for fragment in dimse_msg.encode_msg(1, 16382): pdvs.append(fragment) cs_pdv = pdvs[0].presentation_data_value_list[0][1] ds_pdv = pdvs[1].presentation_data_value_list[0][1] assert cs_pdv == c_move_rq_cmd assert ds_pdv == c_move_rq_ds
def associate(self, addr, port, ae_title='ANY-SCP', max_pdu=16382, ext_neg=None): """Attempts to associate with a remote application entity When requesting an association the local AE is acting as an SCU Parameters ---------- addr : str The peer AE's TCP/IP address (IPv4) port : int The peer AE's listen port number ae_title : str, optional The peer AE's title max_pdu : int, optional The maximum PDV receive size in bytes to use when negotiating the association ext_neg : List of UserInformation objects, optional Used if extended association negotiation is required Returns ------- assoc : pynetdicom.association.Association The Association thread """ if not isinstance(addr, str): raise ValueError("ip_address must be a valid IPv4 string") if not isinstance(port, int): raise ValueError("port must be a valid port number") peer_ae = {'AET' : validate_ae_title(ae_title), 'Address' : addr, 'Port' : port} # Associate assoc = Association(local_ae=self, peer_ae=peer_ae, acse_timeout=self.acse_timeout, dimse_timeout=self.dimse_timeout, max_pdu=max_pdu, ext_neg=ext_neg) # Endlessly loops while the Association negotiation is taking place while (not assoc.is_established and not assoc.is_refused and not assoc.is_aborted and not assoc.dul.kill): time.sleep(0.1) # If the Association was established if assoc.is_established: self.active_associations.append(assoc) return assoc
def MoveOriginatorApplicationEntityTitle(self, value): """ Set the Move Originator AE Title Parameters ---------- value : str or bytes The Move Originator AE Title as a string or bytes object. Cannot be an empty string and will be truncated to 16 characters long """ if isinstance(value, str): value = bytes(value, 'utf-8') if value is not None: self._move_originator_application_entity_title = validate_ae_title(value) else: self._move_originator_application_entity_title = None
def MoveDestination(self, value): """ Set the Move Destination AE Title Parameters ---------- value : str or bytes The Move Destination AE Title as a string or bytes object. Cannot be an empty string and will be truncated to 16 characters long """ if isinstance(value, str): value = bytes(value, 'utf-8') if value is not None: self._move_destination = validate_ae_title(value) else: self._move_destination = None
def test_conversion_rsp(self): """ Check conversion to a -RSP PDU produces the correct output """ primitive = C_FIND() primitive.MessageIDBeingRespondedTo = 5 primitive.AffectedSOPClassUID = '1.2.840.10008.5.1.4.1.1.2' primitive.Status = 0xFF00 ref_identifier = Dataset() ref_identifier.QueryRetrieveLevel = "PATIENT" ref_identifier.RetrieveAETitle = validate_ae_title("FINDSCP") ref_identifier.PatientName = "ANON^A^B^C^D" primitive.Identifier = BytesIO(encode(ref_identifier, True, True)) dimse_msg = C_FIND_RSP() dimse_msg.primitive_to_message(primitive) pdvs = [] for fragment in dimse_msg.encode_msg(1, 16382): pdvs.append(fragment) cs_pdv = pdvs[0].presentation_data_value_list[0][1] ds_pdv = pdvs[1].presentation_data_value_list[0][1] assert cs_pdv == c_find_rsp_cmd assert ds_pdv == c_find_rsp_ds
def main(args=None): """Run the application.""" if args is not None: sys.argv = args args = _setup_argparser() if args.version: print(f"qrscp.py v{__version__}") sys.exit() APP_LOGGER = setup_logging(args, "qrscp") APP_LOGGER.debug(f"qrscp.py v{__version__}") APP_LOGGER.debug("") APP_LOGGER.debug("Using configuration from:") APP_LOGGER.debug(f" {args.config}") APP_LOGGER.debug("") config = ConfigParser() config.read(args.config) if args.ae_title: config["DEFAULT"]["ae_title"] = args.ae_title if args.port: config["DEFAULT"]["port"] = args.port if args.max_pdu: config["DEFAULT"]["max_pdu"] = args.max_pdu if args.acse_timeout: config["DEFAULT"]["acse_timeout"] = args.acse_timeout if args.dimse_timeout: config["DEFAULT"]["dimse_timeout"] = args.dimse_timeout if args.network_timeout: config["DEFAULT"]["network_timeout"] = args.network_timeout if args.bind_address: config["DEFAULT"]["bind_address"] = args.bind_address if args.database_location: config["DEFAULT"]["database_location"] = args.database_location if args.instance_location: config["DEFAULT"]["instance_location"] = args.instance_location # Log configuration settings _log_config(config, APP_LOGGER) app_config = config["DEFAULT"] dests = {} for ae_title in config.sections(): dest = config[ae_title] # Convert to bytes and validate the AE title ae_title = validate_ae_title(ae_title.encode("ascii"), use_short=True) dests[ae_title] = (dest["address"], dest.getint("port")) # Use default or specified configuration file current_dir = os.path.abspath(os.path.dirname(__file__)) instance_dir = os.path.join(current_dir, app_config["instance_location"]) db_path = os.path.join(current_dir, app_config["database_location"]) # The path to the database db_path = f"sqlite:///{db_path}" db.create(db_path) # Clean up the database and storage directory if args.clean: response = input( "This will delete all instances from both the storage directory " "and the database. Are you sure you wish to continue? [yes/no]: ") if response != "yes": sys.exit() if clean(db_path, APP_LOGGER): sys.exit() else: sys.exit(1) # Try to create the instance storage directory os.makedirs(instance_dir, exist_ok=True) ae = AE(app_config["ae_title"]) ae.maximum_pdu_size = app_config.getint("max_pdu") ae.acse_timeout = app_config.getfloat("acse_timeout") ae.dimse_timeout = app_config.getfloat("dimse_timeout") ae.network_timeout = app_config.getfloat("network_timeout") ## Add supported presentation contexts # Verification SCP ae.add_supported_context(VerificationSOPClass, ALL_TRANSFER_SYNTAXES) # Storage SCP - support all transfer syntaxes for cx in AllStoragePresentationContexts: ae.add_supported_context(cx.abstract_syntax, ALL_TRANSFER_SYNTAXES, scp_role=True, scu_role=False) # Query/Retrieve SCP ae.add_supported_context(PatientRootQueryRetrieveInformationModelFind) ae.add_supported_context(PatientRootQueryRetrieveInformationModelMove) ae.add_supported_context(PatientRootQueryRetrieveInformationModelGet) ae.add_supported_context(StudyRootQueryRetrieveInformationModelFind) ae.add_supported_context(StudyRootQueryRetrieveInformationModelMove) ae.add_supported_context(StudyRootQueryRetrieveInformationModelGet) # Set our handler bindings handlers = [ (evt.EVT_C_ECHO, handle_echo, [args, APP_LOGGER]), (evt.EVT_C_FIND, handle_find, [db_path, args, APP_LOGGER]), (evt.EVT_C_GET, handle_get, [db_path, args, APP_LOGGER]), (evt.EVT_C_MOVE, handle_move, [dests, db_path, args, APP_LOGGER]), (evt.EVT_C_STORE, handle_store, [instance_dir, db_path, args, APP_LOGGER]), ] # Listen for incoming association requests ae.start_server((app_config["bind_address"], app_config.getint("port")), evt_handlers=handlers)
def test_bad_ae_bytes(self, aet): """Test validate_ae_title using bad bytes input.""" with pytest.raises((TypeError, ValueError)): validate_ae_title(aet)
def test_good_ae_bytes(self, aet, output): """Test validate_ae_title using bytes input.""" assert validate_ae_title(aet) == output assert isinstance(validate_ae_title(aet), bytes)
def ae_title(self, value): try: self._ae_title = validate_ae_title(value) except: raise