Skip to content

Commit ed31516

Browse files
Reject unsupported SOP classes at association negotiation (#64)
The PACS server should only advertise the two mammography SOP classes. Advertising all storage SOP classes then returning FAILURE in the C-STORE handler would cause a modality to abort the association on any non-mammography object.
1 parent 6474fcd commit ed31516

3 files changed

Lines changed: 19 additions & 15 deletions

File tree

src/server.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77

88
import logging
99

10-
from pynetdicom import AE, StoragePresentationContexts, evt
10+
import pydicom.uid as dicom_uid
11+
from pynetdicom import AE, evt
1112
from pynetdicom.sop_class import (
13+
DigitalMammographyXRayImageStorageForPresentation, # type: ignore[attr-defined]
14+
DigitalMammographyXRayImageStorageForProcessing, # type: ignore[attr-defined]
1215
ModalityPerformedProcedureStep, # type: ignore[attr-defined]
1316
ModalityWorklistInformationFind, # type: ignore[attr-defined]
1417
)
@@ -56,8 +59,17 @@ def start(self):
5659
"""Start the PACS server and listen for incoming connections."""
5760
logger.info(f"Starting PACS server: {self.ae_title} on port {self.port}")
5861

62+
transfer_syntaxes = [
63+
dicom_uid.JPEGLosslessSV1, # Hologic preferred
64+
dicom_uid.ExplicitVRLittleEndian,
65+
dicom_uid.ImplicitVRLittleEndian,
66+
dicom_uid.ExplicitVRBigEndian,
67+
dicom_uid.JPEGLSLossless,
68+
dicom_uid.JPEG2000Lossless,
69+
]
5970
self.ae = AE(ae_title=self.ae_title)
60-
self.ae.supported_contexts = StoragePresentationContexts
71+
self.ae.add_supported_context(DigitalMammographyXRayImageStorageForPresentation, transfer_syntaxes)
72+
self.ae.add_supported_context(DigitalMammographyXRayImageStorageForProcessing, transfer_syntaxes)
6173

6274
handlers = [
6375
(evt.EVT_C_ECHO, CEcho().call),

src/services/dicom/c_store.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33

44
from pydicom import Dataset, dcmwrite
55
from pynetdicom.events import Event
6-
from pynetdicom.sop_class import (
7-
DigitalMammographyXRayImageStorageForPresentation, # type: ignore
8-
DigitalMammographyXRayImageStorageForProcessing, # type: ignore
9-
)
106

117
from services.dicom import FAILURE, SUCCESS
128
from services.dicom.image_compressor import ImageCompressor
@@ -18,11 +14,6 @@
1814

1915

2016
class CStore:
21-
VALID_SOP_CLASSES = [
22-
DigitalMammographyXRayImageStorageForPresentation,
23-
DigitalMammographyXRayImageStorageForProcessing,
24-
]
25-
2617
def __init__(
2718
self,
2819
storage: PACSStorage,
@@ -42,10 +33,6 @@ def call(self, event: Event) -> int:
4233
ds = event.dataset
4334
ds.file_meta = event.file_meta
4435

45-
if ds.file_meta.MediaStorageSOPClassUID not in self.VALID_SOP_CLASSES:
46-
logger.error(f"Invalid SOP Class UID: {ds.file_meta.MediaStorageSOPClassUID}")
47-
return FAILURE
48-
4936
sop_instance_uid = ds.get("SOPInstanceUID", "")
5037
accession_number = ds.get("AccessionNumber", "")
5138
patient_id = ds.get("PatientID")

tests/test_server.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
from pynetdicom import evt
55
from pynetdicom.sop_class import ( # pyright: ignore[reportAttributeAccessIssue]
6+
DigitalMammographyXRayImageStorageForPresentation,
7+
DigitalMammographyXRayImageStorageForProcessing,
68
ModalityPerformedProcedureStep,
79
ModalityWorklistInformationFind,
810
)
@@ -51,6 +53,9 @@ def test_start(self, mock_c_store, mock_c_echo, mock_ae, _mock_pacs_storage, _mo
5153
assert subject.ae == mock_ae.return_value
5254

5355
mock_ae.assert_called_once_with(ae_title="SCREENING_PACS")
56+
add_context_calls = [call.args[0] for call in mock_ae.return_value.add_supported_context.call_args_list]
57+
assert DigitalMammographyXRayImageStorageForPresentation in add_context_calls
58+
assert DigitalMammographyXRayImageStorageForProcessing in add_context_calls
5459
mock_ae.return_value.start_server.assert_called_once_with(
5560
("0.0.0.0", 4244),
5661
block=True,

0 commit comments

Comments
 (0)