From 6ab9f2c6d084113c0741d4ccce1239bdd1a9c5a4 Mon Sep 17 00:00:00 2001 From: David Wikler Date: Mon, 30 Dec 2024 22:03:44 +0100 Subject: [PATCH] Replace obsolete StoreSCP class --- .../TDWII_PPVS_subscriber/cstore_handler.py | 142 ------------------ .../TDWII_PPVS_subscriber/ppvsscp.py | 14 +- .../TDWII_PPVS_subscriber/storescp.py | 53 ------- 3 files changed, 7 insertions(+), 202 deletions(-) delete mode 100644 tdwii_plus_examples/TDWII_PPVS_subscriber/cstore_handler.py delete mode 100644 tdwii_plus_examples/TDWII_PPVS_subscriber/storescp.py diff --git a/tdwii_plus_examples/TDWII_PPVS_subscriber/cstore_handler.py b/tdwii_plus_examples/TDWII_PPVS_subscriber/cstore_handler.py deleted file mode 100644 index aad6707..0000000 --- a/tdwii_plus_examples/TDWII_PPVS_subscriber/cstore_handler.py +++ /dev/null @@ -1,142 +0,0 @@ -"""Utility classes and functions for the apps.""" - -import os - -# from pydicom import dcmread -# from pydicom.datadict import get_entry, repeater_has_keyword, tag_for_keyword -from pydicom.dataset import Dataset -from pydicom.filewriter import write_file_meta_info - -# from pydicom.tag import Tag -from pydicom.uid import DeflatedExplicitVRLittleEndian -from pynetdicom.dsutils import encode - - -def handle_store(event, args, app_logger): - """Handle a C-STORE request. - - Parameters - ---------- - event : pynetdicom.event.event - The event corresponding to a C-STORE request. - args : argparse.Namespace - The namespace containing the arguments to use. The namespace should - contain ``args.ignore`` and ``args.output_directory`` attributes. - app_logger : logging.Logger - The application's logger. - - Returns - ------- - status : pynetdicom.sop_class.Status or int - A valid return status code, see PS3.4 Annex B.2.3 or the - ``StorageServiceClass`` implementation for the available statuses - """ - if args.ignore: - return 0x0000 - - try: - ds = event.dataset - # Remove any Group 0x0002 elements that may have been included - ds = ds[0x00030000:] - except Exception as exc: - app_logger.error("Unable to decode the dataset") - app_logger.exception(exc) - # Unable to decode dataset - return 0x210 - - # Add the file meta information elements - ds.file_meta = event.file_meta - - # Because pydicom uses deferred reads for its decoding, decoding errors - # are hidden until encountered by accessing a faulty element - try: - sop_class = ds.SOPClassUID - sop_instance = ds.SOPInstanceUID - except Exception as exc: - app_logger.error( - "Unable to decode the received dataset or missing 'SOP Class " "UID' and/or 'SOP Instance UID' elements" - ) - app_logger.exception(exc) - # Unable to decode dataset - return 0xC210 - - try: - # Get the elements we need - mode_prefix = SOP_CLASS_PREFIXES[sop_class][0] - except KeyError: - mode_prefix = "UN" - - filename = f"{mode_prefix}.{sop_instance}.dcm" - app_logger.info(f"Storing DICOM file: {filename}") - - status_ds = Dataset() - status_ds.Status = 0x0000 - - # Try to save to output-directory - if args.output_directory is not None: - filename = os.path.join(args.output_directory, filename) - try: - os.makedirs(args.output_directory, exist_ok=True) - except Exception as exc: - app_logger.error("Unable to create the output directory:") - app_logger.error(f" {args.output_directory}") - app_logger.exception(exc) - # Failed - Out of Resources - IOError - status_ds.Status = 0xA700 - return status_ds - - if os.path.exists(filename): - app_logger.warning("DICOM file already exists, overwriting") - - try: - if event.context.transfer_syntax == DeflatedExplicitVRLittleEndian: - # Workaround for pydicom issue #1086 - with open(filename, "wb") as f: - f.write(b"\x00" * 128) - f.write(b"DICM") - write_file_meta_info(f, event.file_meta) - f.write(encode(ds, False, True, True)) - else: - # We use `write_like_original=False` to ensure that a compliant - # File Meta Information Header is written - ds.save_as(filename, write_like_original=False) - - status_ds.Status = 0x0000 # Success - except IOError as exc: - app_logger.error("Could not write file to specified directory:") - app_logger.error(f" {os.path.dirname(filename)}") - app_logger.exception(exc) - # Failed - Out of Resources - IOError - status_ds.Status = 0xA700 - except Exception as exc: - app_logger.error("Could not write file to specified directory:") - app_logger.error(f" {os.path.dirname(filename)}") - app_logger.exception(exc) - # Failed - Out of Resources - Miscellaneous error - status_ds.Status = 0xA701 - - return status_ds - - -SOP_CLASS_PREFIXES = { - "1.2.840.10008.5.1.4.1.1.2": ("CT", "CT Image Storage"), - "1.2.840.10008.5.1.4.1.1.2.1": ("CTE", "Enhanced CT Image Storage"), - "1.2.840.10008.5.1.4.1.1.4": ("MR", "MR Image Storage"), - "1.2.840.10008.5.1.4.1.1.4.1": ("MRE", "Enhanced MR Image Storage"), - "1.2.840.10008.5.1.4.1.1.128": ("PT", "Positron Emission Tomography Image Storage"), - "1.2.840.10008.5.1.4.1.1.130": ("PTE", "Enhanced PET Image Storage"), - "1.2.840.10008.5.1.4.1.1.481.1": ("RI", "RT Image Storage"), - "1.2.840.10008.5.1.4.1.1.481.2": ("RD", "RT Dose Storage"), - "1.2.840.10008.5.1.4.1.1.481.5": ("RP", "RT Plan Storage"), - "1.2.840.10008.5.1.4.1.1.481.8": ("RN", "RT Ion Plan Storage"), - "1.2.840.10008.5.1.4.1.1.481.9": ("RX", "RT Ion Beams Treatment Record Storage"), - "1.2.840.10008.5.1.4.1.1.481.3": ("RS", "RT Structure Set Storage"), - "1.2.840.10008.5.1.4.1.1.1": ("CR", "Computed Radiography Image Storage"), - "1.2.840.10008.5.1.4.1.1.6.1": ("US", "Ultrasound Image Storage"), - "1.2.840.10008.5.1.4.1.1.6.2": ("USE", "Enhanced US Volume Storage"), - "1.2.840.10008.5.1.4.1.1.12.1": ("XA", "X-Ray Angiographic Image Storage"), - "1.2.840.10008.5.1.4.1.1.12.1.1": ("XAE", "Enhanced XA Image Storage"), - "1.2.840.10008.5.1.4.1.1.20": ("NM", "Nuclear Medicine Image Storage"), - "1.2.840.10008.5.1.4.1.1.7": ("SC", "Secondary Capture Image Storage"), - "1.2.840.10008.5.1.4.34.7": ("RB", "RT Beams Delivery Instruction Storage​"), -} diff --git a/tdwii_plus_examples/TDWII_PPVS_subscriber/ppvsscp.py b/tdwii_plus_examples/TDWII_PPVS_subscriber/ppvsscp.py index c761741..4238e7a 100644 --- a/tdwii_plus_examples/TDWII_PPVS_subscriber/ppvsscp.py +++ b/tdwii_plus_examples/TDWII_PPVS_subscriber/ppvsscp.py @@ -6,10 +6,10 @@ from tdwii_plus_examples import tdwii_config from tdwii_plus_examples.basescp import BaseSCP from tdwii_plus_examples.TDWII_PPVS_subscriber.nevent_receiver import NEventReceiver -from tdwii_plus_examples.TDWII_PPVS_subscriber.storescp import StoreSCP +from tdwii_plus_examples.cstorescp import CStoreSCP -class PPVS_SCP(NEventReceiver, StoreSCP): +class PPVS_SCP(NEventReceiver, CStoreSCP): def __init__( self, ae_title: str = "PPVS_SCP", @@ -27,15 +27,15 @@ def __init__( if port < 1: port = tdwii_config.known_ae_port[ae_title] self.nevent_callback = nevent_callback - StoreSCP.__init__( + CStoreSCP.__init__( self, ae_title=ae_title, port=port, logger=logger, bind_address=bind_address, + sop_classes=None, # use first 128 SOP Classes + transfer_syntaxes=None, # use pynetdicom defaults xfer syntaxes custom_handler=custom_cstore_handler, - storage_presentation_contexts=storage_presentation_contexts, - transfer_syntaxes=transfer_syntaxes, store_directory=store_directory, ) NEventReceiver.__init__( @@ -48,11 +48,11 @@ def __init__( ) def _add_contexts(self): - StoreSCP._add_contexts(self) + CStoreSCP._add_contexts(self) NEventReceiver._add_contexts(self) def _add_handlers(self): - StoreSCP._add_handlers(self) + CStoreSCP._add_handlers(self) NEventReceiver._add_handlers(self) def run(self): diff --git a/tdwii_plus_examples/TDWII_PPVS_subscriber/storescp.py b/tdwii_plus_examples/TDWII_PPVS_subscriber/storescp.py deleted file mode 100644 index 49e3b74..0000000 --- a/tdwii_plus_examples/TDWII_PPVS_subscriber/storescp.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -from argparse import Namespace -from time import sleep - -from pynetdicom import ALL_TRANSFER_SYNTAXES, AllStoragePresentationContexts, evt - -from tdwii_plus_examples.basescp import BaseSCP -from tdwii_plus_examples.TDWII_PPVS_subscriber.cstore_handler import handle_store -from tdwii_plus_examples.cechoscp import CEchoSCP - - -class StoreSCP(CEchoSCP): - def __init__( - self, - ae_title: str = "STORE_SCP", - port: int = 11112, - logger=None, - bind_address: str = "", - storage_presentation_contexts=AllStoragePresentationContexts, - transfer_syntaxes=ALL_TRANSFER_SYNTAXES, - custom_handler=None, - store_directory=os.path.curdir, - ): - self.storage_presentation_contexts = storage_presentation_contexts - self.transfer_syntaxes = transfer_syntaxes - if custom_handler is None: - self.handle_cstore = handle_store - else: - self.handle_cstore = custom_handler - self.store_directory = store_directory - CEchoSCP.__init__(self, ae_title=ae_title, port=port, logger=logger, bind_address=bind_address) - - def _add_contexts(self): - CEchoSCP._add_contexts(self) - for context in self.storage_presentation_contexts: - self.ae.add_supported_context(context.abstract_syntax, self.transfer_syntaxes) - - def _add_handlers(self): - CEchoSCP._add_handlers(self) - args = Namespace(ignore=False, output_directory=self.store_directory) - - self.handlers.append((evt.EVT_C_STORE, self.handle_cstore, [args, self.logger])) - - def run(self): - # Listen for incoming association requests - BaseSCP.run(self) - - -if __name__ == "__main__": - my_scp = StoreSCP() - my_scp.run() - while True: - sleep(100) # sleep forever