class DummyVerificationSCP(DummyBaseSCP): """A threaded dummy verification SCP used for testing""" def __init__(self, port=11112): self.ae = AE(port=port) self.ae.supported_contexts = VerificationPresentationContexts self.ae.add_supported_context('1.2.3.4') DummyBaseSCP.__init__(self) self.status = 0x0000 def on_c_echo(self, context, info): """Callback for ae.on_c_echo Parameters ---------- delay : int or float Wait `delay` seconds before sending a response """ self.context = context self.info = info time.sleep(self.delay) if self.send_abort: self.ae.active_associations[0].abort() return self.status
class DummyFindSCP(DummyBaseSCP): """A threaded dummy find SCP used for testing""" def __init__(self, port=11112): self.ae = AE(port=port) self.ae.add_supported_context(PatientRootQueryRetrieveInformationModelFind) self.ae.add_supported_context(StudyRootQueryRetrieveInformationModelFind) self.ae.add_supported_context(ModalityWorklistInformationFind) self.ae.add_supported_context(PatientStudyOnlyQueryRetrieveInformationModelFind) DummyBaseSCP.__init__(self) self.statuses = [0x0000] identifier = Dataset() identifier.PatientName = 'Test' self.identifiers = [identifier] self.cancel = False def on_c_find(self, ds, context, info): """Callback for ae.on_c_find""" self.context = context self.info = info time.sleep(self.delay) for status, identifier in zip(self.statuses, self.identifiers): if self.cancel: yield 0xFE00, None return yield status, identifier def on_c_cancel_find(self): """Callback for ae.on_c_cancel_find""" self.cancel = True
class DummyDeleteSCP(DummyBaseSCP): """A threaded dummy delete SCP used for testing""" def __init__(self, port=11112): self.ae = AE(port=port) # Print Job SOP Class self.ae.add_supported_context(PrintJobSOPClass) self.ae.add_supported_context(VerificationSOPClass) DummyBaseSCP.__init__(self) self.status = 0x0000 def on_n_delete(self, context, info): """Callback for ae.on_n_delete""" self.context = context self.info = info time.sleep(self.delay) return self.status
class DummySetSCP(DummyBaseSCP): """A threaded dummy get SCP used for testing""" def __init__(self, port=11112): self.ae = AE(port=port) self.ae.add_supported_context(PrintJobSOPClass) self.ae.add_supported_context(VerificationSOPClass) DummyBaseSCP.__init__(self) self.status = 0x0000 ds = Dataset() ds.PatientName = 'Test' ds.SOPClassUID = PrintJobSOPClass ds.SOPInstanceUID = '1.2.3.4' self.dataset = ds def on_n_set(self, ds, context, info): """Callback for ae.on_n_get""" self.context = context self.info = info time.sleep(self.delay) return self.status, self.dataset
class DummyStorageSCP(DummyBaseSCP): """A threaded dummy storage SCP used for testing""" def __init__(self, port=11112): self.ae = AE(port=port) self.ae.add_supported_context(PatientRootQueryRetrieveInformationModelMove) self.ae.add_supported_context(StudyRootQueryRetrieveInformationModelMove) self.ae.add_supported_context(PatientStudyOnlyQueryRetrieveInformationModelMove) self.ae.add_supported_context(CTImageStorage) self.ae.add_supported_context(RTImageStorage) self.ae.add_supported_context(MRImageStorage) DummyBaseSCP.__init__(self) self.status = 0x0000 self.raise_exception = False def on_c_store(self, ds, context, info): """Callback for ae.on_c_store""" self.context = context self.info = info time.sleep(self.delay) if self.raise_exception: raise ValueError('Dummy msg') return self.status
class DummyGetSCP(DummyBaseSCP): """A threaded dummy get SCP used for testing""" def __init__(self, port=11112): self.ae = AE(port=port) self.ae.add_supported_context(PatientRootQueryRetrieveInformationModelGet) self.ae.add_supported_context(StudyRootQueryRetrieveInformationModelGet) self.ae.add_supported_context(PatientStudyOnlyQueryRetrieveInformationModelGet) self.ae.add_supported_context(CTImageStorage) self.ae.add_requested_context(CTImageStorage) DummyBaseSCP.__init__(self) self.statuses = [0x0000] ds = Dataset() ds.PatientName = 'Test' ds.SOPClassUID = CTImageStorage.UID ds.SOPInstanceUID = '1.2.3.4' self.datasets = [ds] self.no_suboperations = 1 self.cancel = False def on_c_get(self, ds, context, info): """Callback for ae.on_c_get""" self.context = context self.info = info time.sleep(self.delay) ds = Dataset() ds.PatientName = '*' ds.QueryRetrieveLevel = "PATIENT" yield self.no_suboperations for (status, ds) in zip(self.statuses, self.datasets): if self.cancel: yield 0xFE00, None return yield status, ds def on_c_cancel_get(self): """Callback for ae.on_c_cancel_get""" self.cancel = True
transfer_syntax.remove(ExplicitVRLittleEndian) transfer_syntax.insert(0, ExplicitVRLittleEndian) if args.prefer_big and ExplicitVRBigEndian in transfer_syntax: transfer_syntax.remove(ExplicitVRBigEndian) transfer_syntax.insert(0, ExplicitVRBigEndian) def on_c_echo(context, info): """Optional implementation of the AE.on_c_echo callback.""" # Return a Success response to the peer # We could also return a pydicom Dataset with a (0000, 0900) Status # element return 0x0000 # Create application entity ae = AE(ae_title=args.aetitle, port=args.port) ae.add_supported_context(VerificationSOPClass, transfer_syntax) ae.maximum_pdu_size = args.max_pdu # Set timeouts ae.network_timeout = args.timeout ae.acse_timeout = args.acse_timeout ae.dimse_timeout = args.dimse_timeout # Set callback ae.on_c_echo = on_c_echo ae.start()
class CStoreScp(object): dcm_file_path = r'D:\tmp' # dcm_file_path = r'/tmp/dcms/' need_anonymize = 0 need_save_file = 1 pixels = [] def __init__(self, store_ae_title, store_ae_port, store_ae_ip): self.store_ae_port = store_ae_port self.ae = AE(ae_title=store_ae_title, port=store_ae_port) self.ae.bind_addr = store_ae_ip pass def append_pixel(self, pixel): self.pixels.append(pixel) def on_association_accepted(self, primitive): print "client call on_association_accepted" print datetime.datetime.now() print primitive time.sleep(1) def on_association_released(self, primitive=None): print "client call on_association_released" print datetime.datetime.now() print primitive def on_association_aborted(self, primitive=None): print "client call on_association_aborted" print primitive def on_association_rejected(self, primitive): print "client call on_association_rejected" def on_n_set(self, context, info): print "client call on_n_set" print info def on_c_move_cancel(self): print "client call on_c_move_cancel" def on_association_requested(self, primitive): print "client call on_association_requested" print primitive def on_receive_connection(self): """Callback for a connection is received.""" print "client call on_receive_connection" def on_make_connection(self): """Callback for a connection is made.""" print "client call on_make_connection" def on_c_echo(self, context, info): print "client call on_c_echo" print info """Optional implementation of the AE.on_c_echo callback.""" # Return a Success response to the peer # We could also return a pydicom Dataset with a (0000, 0900) Status # element return 0x0000 def on_c_find(self, dataset, context, info): print "client call on_c_find" def on_c_get(self, dataset, context, info): print "client call on_c_get" def on_c_move(self, dataset, move_aet, context, info): print "client call on_c_move" def on_c_store(self, dataset, context, info): print "client call on_c_store" # do anonymize # append pixel self.append_pixel(dataset.PixelData) if self.need_save_file: mode_prefixes = { 'CT Image Storage': 'CT', 'Enhanced CT Image Storage': 'CTE', 'MR Image Storage': 'MR', 'Enhanced MR Image Storage': 'MRE', 'Positron Emission Tomography Image Storage': 'PT', 'Enhanced PET Image Storage': 'PTE', 'RT Image Storage': 'RI', 'RT Dose Storage': 'RD', 'RT Plan Storage': 'RP', 'RT Structure Set Storage': 'RS', 'Computed Radiography Image Storage': 'CR', 'Ultrasound Image Storage': 'US', 'Enhanced Ultrasound Image Storage': 'USE', 'X-Ray Angiographic Image Storage': 'XA', 'Enhanced XA Image Storage': 'XAE', 'Nuclear Medicine Image Storage': 'NM', 'Secondary Capture Image Storage': 'SC' } try: mode_prefix = mode_prefixes[dataset.SOPClassUID.name] except KeyError: mode_prefix = 'UN' filename = '{0!s}.{1!s}.dcm'.format(mode_prefix, dataset.SOPInstanceUID) meta = Dataset() meta.MediaStorageSOPClassUID = dataset.SOPClassUID meta.MediaStorageSOPInstanceUID = dataset.SOPInstanceUID meta.ImplementationClassUID = PYNETDICOM_IMPLEMENTATION_UID meta.TransferSyntaxUID = context.transfer_syntax # The following is not mandatory, set for convenience meta.ImplementationVersionName = PYNETDICOM_IMPLEMENTATION_VERSION ds = FileDataset(filename, {}, file_meta=meta, preamble=b"\0" * 128) ds.update(dataset) ds.is_little_endian = context.transfer_syntax.is_little_endian ds.is_implicit_VR = context.transfer_syntax.is_implicit_VR file_path = os.path.join(self.dcm_file_path, dataset.SeriesInstanceUID) if not os.path.exists(file_path): os.mkdir(file_path) filename = os.path.join(file_path, filename) ds.save_as(filename, write_like_original=False) status_ds = Dataset() status_ds.Status = 0x0000 # Success return status_ds def start(self): print 'c-store scp starting' transfer_syntax = [ ImplicitVRLittleEndian, ExplicitVRLittleEndian, DeflatedExplicitVRLittleEndian, ExplicitVRBigEndian ] for context in StoragePresentationContexts: self.ae.add_supported_context(context.abstract_syntax, transfer_syntax) for context in VerificationPresentationContexts: self.ae.add_supported_context(context.abstract_syntax, transfer_syntax) for context in QueryRetrievePresentationContexts: self.ae.add_supported_context(context.abstract_syntax, transfer_syntax) self.ae.maximum_pdu_size = 528384 # Set timeouts self.ae.network_timeout = None self.ae.acse_timeout = None self.ae.dimse_timeout = None self.ae.on_c_store = self.on_c_store self.ae.on_association_accepted = self.on_association_accepted self.ae.on_association_released = self.on_association_released self.ae.on_association_aborted = self.on_association_aborted self.ae.on_association_rejected = self.on_association_rejected self.ae.on_association_requested = self.on_association_requested self.ae.on_receive_connection = self.on_receive_connection self.ae.on_make_connection = self.on_make_connection self.ae.on_c_echo = self.on_c_echo self.ae.on_c_find = self.on_c_find self.ae.on_c_get = self.on_c_get self.ae.on_c_move = self.on_c_move self.ae.on_n_set = self.on_n_set self.ae.on_c_move_cancel = self.on_c_move_cancel print "c-store started!" try: self.ae.start() except: print "Uncexpected error", sys.exc_info()[0] def port_validation(self): if isinstance(self.store_ae_port, int): test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: test_socket.bind( (os.popen('hostname').read()[:-1], self.store_ae_port)) log.dev_info("Now listen on port {0:d}".format( self.store_ae_port)) except socket.error: log.dev_info( "Cannot listen on port {0:d}, insufficient priveleges". format(self.store_ae_port))
# Test output-directory if args.output_directory is not None: if not os.access(args.output_directory, os.W_OK|os.X_OK): LOGGER.error('No write permissions or the output ' 'directory may not exist:') LOGGER.error(" {0!s}".format(args.output_directory)) sys.exit() # Create application entity ae = AE(ae_title=args.aetitle, port=args.port) ae.bind_addr = args.bind_addr # Add presentation contexts with specified transfer syntaxes for context in StoragePresentationContexts: ae.add_supported_context(context.abstract_syntax, transfer_syntax) for context in VerificationPresentationContexts: ae.add_supported_context(context.abstract_syntax, transfer_syntax) ae.maximum_pdu_size = args.max_pdu # Set timeouts ae.network_timeout = args.timeout ae.acse_timeout = args.acse_timeout ae.dimse_timeout = args.dimse_timeout ae.on_c_store = on_c_store ae.start()
class DummyMoveSCP(DummyBaseSCP): """A threaded dummy move SCP used for testing""" def __init__(self, port=11112): self.ae = AE(port=port) self.ae.add_supported_context( PatientRootQueryRetrieveInformationModelMove) self.ae.add_supported_context( StudyRootQueryRetrieveInformationModelMove) self.ae.add_supported_context( PatientStudyOnlyQueryRetrieveInformationModelMove) self.ae.add_supported_context(CompositeInstanceRootRetrieveMove) self.ae.add_supported_context(HangingProtocolInformationModelMove) self.ae.add_supported_context( DefinedProcedureProtocolInformationModelMove) self.ae.add_supported_context(ColorPaletteInformationModelMove) self.ae.add_supported_context( GenericImplantTemplateInformationModelMove) self.ae.add_supported_context( ImplantAssemblyTemplateInformationModelMove) self.ae.add_supported_context(ImplantTemplateGroupInformationModelMove) self.ae.add_supported_context(RTImageStorage) self.ae.add_supported_context(CTImageStorage) self.ae.add_requested_context(RTImageStorage) self.ae.add_requested_context(CTImageStorage) DummyBaseSCP.__init__(self) self.statuses = [0x0000] self.store_status = 0x0000 ds = Dataset() ds.file_meta = Dataset() ds.file_meta.TransferSyntaxUID = ImplicitVRLittleEndian ds.PatientName = 'Test' ds.SOPClassUID = CTImageStorage ds.SOPInstanceUID = '1.2.3.4' self.datasets = [ds] self.no_suboperations = 1 self.destination_ae = ('localhost', 11112) self.cancel = False self.test_no_yield = False self.test_no_subops = False self.store_context = None self.store_info = None self.move_aet = None def on_c_move(self, ds, move_aet, context, info): """Callback for ae.on_c_move""" self.context = context self.move_aet = move_aet self.info = info time.sleep(self.delay) ds = Dataset() ds.PatientName = '*' ds.QueryRetrieveLevel = "PATIENT" if self.test_no_yield: return yield self.destination_ae if self.test_no_subops: return yield self.no_suboperations for (status, ds) in zip(self.statuses, self.datasets): if self.cancel: yield 0xFE00, None return yield status, ds def on_c_store(self, ds, context, info): self.store_context = context self.store_info = info return self.store_status def on_c_cancel_move(self): """Callback for ae.on_c_cancel_move""" self.cancel = True
class DummyGetSCP(DummyBaseSCP): """A threaded dummy get SCP used for testing""" def __init__(self, port=11112): self.ae = AE(port=port) self.ae.add_supported_context( PatientRootQueryRetrieveInformationModelGet) self.ae.add_supported_context( StudyRootQueryRetrieveInformationModelGet) self.ae.add_supported_context( PatientStudyOnlyQueryRetrieveInformationModelGet) self.ae.add_supported_context( CompositeInstanceRetrieveWithoutBulkDataGet) self.ae.add_supported_context(CompositeInstanceRootRetrieveGet) self.ae.add_supported_context(HangingProtocolInformationModelGet) self.ae.add_supported_context( DefinedProcedureProtocolInformationModelGet) self.ae.add_supported_context(ColorPaletteInformationModelGet) self.ae.add_supported_context( GenericImplantTemplateInformationModelGet) self.ae.add_supported_context( ImplantAssemblyTemplateInformationModelGet) self.ae.add_supported_context(ImplantTemplateGroupInformationModelGet) for cx in StoragePresentationContexts: self.ae.add_supported_context(cx.abstract_syntax, scp_role=True, scu_role=False) DummyBaseSCP.__init__(self) self.statuses = [0x0000] ds = Dataset() ds.file_meta = Dataset() ds.file_meta.TransferSyntaxUID = ImplicitVRLittleEndian ds.PatientName = 'Test' ds.SOPClassUID = CTImageStorage ds.SOPInstanceUID = '1.2.3.4' self.datasets = [ds] self.no_suboperations = 1 self.cancel = False def on_c_get(self, ds, context, info): """Callback for ae.on_c_get""" self.context = context self.info = info time.sleep(self.delay) ds = Dataset() ds.PatientName = '*' ds.QueryRetrieveLevel = "PATIENT" yield self.no_suboperations for (status, ds) in zip(self.statuses, self.datasets): if self.cancel: yield 0xFE00, None return yield status, ds def on_c_cancel_get(self): """Callback for ae.on_c_cancel_get""" self.cancel = True
class DummyFindSCP(DummyBaseSCP): """A threaded dummy find SCP used for testing""" def __init__(self, port=11112): self.ae = AE(port=port) self.ae.add_supported_context( PatientRootQueryRetrieveInformationModelFind) self.ae.add_supported_context( StudyRootQueryRetrieveInformationModelFind) self.ae.add_supported_context(ModalityWorklistInformationFind) self.ae.add_supported_context( PatientStudyOnlyQueryRetrieveInformationModelFind) self.ae.add_supported_context(GeneralRelevantPatientInformationQuery) self.ae.add_supported_context( BreastImagingRelevantPatientInformationQuery) self.ae.add_supported_context(CardiacRelevantPatientInformationQuery) self.ae.add_supported_context( ProductCharacteristicsQueryInformationModelFind) self.ae.add_supported_context( SubstanceApprovalQueryInformationModelFind) self.ae.add_supported_context(HangingProtocolInformationModelFind) self.ae.add_supported_context( DefinedProcedureProtocolInformationModelFind) self.ae.add_supported_context(ColorPaletteInformationModelFind) self.ae.add_supported_context( GenericImplantTemplateInformationModelFind) self.ae.add_supported_context( ImplantAssemblyTemplateInformationModelFind) self.ae.add_supported_context(ImplantTemplateGroupInformationModelFind) DummyBaseSCP.__init__(self) self.statuses = [0x0000] identifier = Dataset() identifier.PatientName = 'Test' self.identifiers = [identifier] self.cancel = False def on_c_find(self, ds, context, info): """Callback for ae.on_c_find""" self.context = context self.info = info time.sleep(self.delay) for status, identifier in zip(self.statuses, self.identifiers): if self.cancel: yield 0xFE00, None return yield status, identifier def on_c_cancel_find(self): """Callback for ae.on_c_cancel_find""" self.cancel = True
class DummyMoveSCP(DummyBaseSCP): """A threaded dummy move SCP used for testing""" def __init__(self, port=11112): self.ae = AE(port=port) self.ae.add_supported_context(PatientRootQueryRetrieveInformationModelMove) self.ae.add_supported_context(StudyRootQueryRetrieveInformationModelMove) self.ae.add_supported_context(PatientStudyOnlyQueryRetrieveInformationModelMove) self.ae.add_supported_context(RTImageStorage) self.ae.add_supported_context(CTImageStorage) self.ae.add_requested_context(RTImageStorage) self.ae.add_requested_context(CTImageStorage) DummyBaseSCP.__init__(self) self.statuses = [0x0000] self.store_status = 0x0000 ds = Dataset() ds.PatientName = 'Test' ds.SOPClassUID = CTImageStorage.UID ds.SOPInstanceUID = '1.2.3.4' self.datasets = [ds] self.no_suboperations = 1 self.destination_ae = ('localhost', 11112) self.cancel = False self.test_no_yield = False self.test_no_subops = False self.store_context = None self.store_info = None self.move_aet = None def on_c_move(self, ds, move_aet, context, info): """Callback for ae.on_c_move""" self.context = context self.move_aet = move_aet self.info = info time.sleep(self.delay) ds = Dataset() ds.PatientName = '*' ds.QueryRetrieveLevel = "PATIENT" if self.test_no_yield: return yield self.destination_ae if self.test_no_subops: return yield self.no_suboperations for (status, ds) in zip(self.statuses, self.datasets): if self.cancel: yield 0xFE00, None return yield status, ds def on_c_store(self, ds, context, info): self.store_context = context self.store_info = info return self.store_status def on_c_cancel_move(self): """Callback for ae.on_c_cancel_move""" self.cancel = True
# Initialise the Application Entity and specify the listen port anon_list = anony_config() print('LocaL AE: ', anon_list[0]) print('MAX_threads : ', anon_list[1]) print('local_PORT: ', anon_list[2]) config_local_AE = anon_list[0] config_MAX_threads = anon_list[1] config_local_PORT = int(anon_list[2]) ae = AE(ae_title=config_local_AE, port=int(config_local_PORT)) ae.supported_contexts = StoragePresentationContexts ae.maximum_associations = config_MAX_threads ae.NumberOfActiveAssociations = config_MAX_threads ae.add_supported_context(VerificationSOPClass) def on_c_echo(context, info): return 0x0000 def operand_equal(ds, active_rule_tag, dicom_tag_value): print('Activve rule tag in equals to: ', active_rule_tag) return dicom_tag_value def getsendingae(ds, context, info): temp = [] dictList = []
from pynetdicom3 import AE, VerificationPresentationContexts, PYNETDICOM_IMPLEMENTATION_UID, PYNETDICOM_IMPLEMENTATION_VERSION from pynetdicom3.sop_class import CTImageStorage, MRImageStorage, StorageServiceClass from pydicom.dataset import Dataset ae = AE(ae_title=b'flavio-pacs', port=11112) # Or we can use the inbuilt VerificationPresentationContexts list, # there's one for each of the supported Service Classes # In this case, we are supporting any requests to use Verification SOP # Class in the association ae.supported_contexts = VerificationPresentationContexts #ae.add_supported_context(StorageServiceClass) ae.add_supported_context(CTImageStorage) ae.add_supported_context(MRImageStorage) # Implement the AE.on_c_store callback def on_c_store(ds, context, info): """Store the pydicom Dataset `ds`. Parameters ---------- ds : pydicom.dataset.Dataset The dataset that the peer has requested be stored. context : namedtuple The presentation context that the dataset was sent under. info : dict Information about the association and storage request. Returns ------- status : int or pydicom.dataset.Dataset The status returned to the peer AE in the C-STORE response. Must be
basedir = '../../tests/dicom_files/' dcm_files = ['RTImageStorage.dcm', 'CTImageStorage.dcm'] dcm_files = [os.path.join(basedir, x) for x in dcm_files] yield len(dcm_files) for dcm in dcm_files: ds = dcmread(dcm, force=True) yield 0xFF00, ds # Create application entity ae = AE(ae_title=args.aetitle, port=args.port) for context in StoragePresentationContexts: ae.add_supported_context( context.abstract_syntax, transfer_syntax, scp_role=True, scu_role=False ) for context in QueryRetrievePresentationContexts: ae.add_supported_context(context.abstract_syntax, transfer_syntax) ae.maximum_pdu_size = args.max_pdu # Set timeouts ae.network_timeout = args.timeout ae.acse_timeout = args.acse_timeout ae.dimse_timeout = args.dimse_timeout ae.on_c_get = on_c_get ae.start()