Closed privateOmega closed 3 years ago
It's a little odd, but the displayed result message should be correct since the values in the code match the DICOM Standard. The actual result code sent by Horus is probably in error (maybe "User Rejection" would be more accurate?), I suggest creating an issue there.
I suspect the contexts themselves aren't accepted because the transfer syntaxes would've been repeated otherwise.
On the other hand, pynetdicom shouldn't be attempting to use a non-accepted presentation context when sending C-STORE requests, could you post the part of the log covering that?
@scaramallion I am not entirely sure if this is a horos issue because, I tried on weasis and that also is not allowing me to send the dicoms. Why do you thinking, contexts aren't accepted?
Which log exactly is it that you want? And could you please post the snippet of what I need to add in my codebase to get that part of the log which you are looking for?
@scaramallion Gentle reminder. :)
Sorry, combination of busy/burnt out. I meant the part of the log between the incoming A-ASSOCIATE-AC and the final A-ABORT/A-RELEASE. I'm assuming there's something different to what you posted earlier? Or does it just abort immediately?
@privateOmega @scaramallion I have a similar issue, I think, with a Fujifilm VisualSonics
D: Request Parameters:
D: ========================= BEGIN A-ASSOCIATE-RQ PDU =========================
D: Their Implementation Class UID: 1.2.840.114340.77
D: Their Implementation Version Name: VSI_001
D: Application Context Name: 1.2.840.10008.3.1.1.1
D: Calling Application Name: New_AE_Title
D: Called Application Name: New_AE_Title
D: Their Max PDU Receive Size: 131072
D: Presentation Contexts:
D: Context ID: 1 (Proposed)
D: Abstract Syntax: =Verification SOP Class
D: Proposed SCP/SCU Role: Default
D: Proposed Transfer Syntax:
D: =Implicit VR Little Endian
D: Context ID: 3 (Proposed)
D: Abstract Syntax: =Secondary Capture Image Storage
D: Proposed SCP/SCU Role: Default
D: Proposed Transfer Syntax:
D: =JPEG Baseline (Process 1)
D: Context ID: 5 (Proposed)
D: Abstract Syntax: =Ultrasound Image Storage
D: Proposed SCP/SCU Role: Default
D: Proposed Transfer Syntax:
D: =JPEG Baseline (Process 1)
D: Context ID: 7 (Proposed)
D: Abstract Syntax: =Ultrasound Multi-frame Image Storage
D: Proposed SCP/SCU Role: Default
D: Proposed Transfer Syntax:
D: =JPEG Baseline (Process 1)
D: Context ID: 9 (Proposed)
D: Abstract Syntax: =Basic Text SR Storage
D: Proposed SCP/SCU Role: Default
D: Proposed Transfer Syntaxes:
D: =Implicit VR Little Endian
D: =Explicit VR Little Endian
D: Requested Extended Negotiation: None
D: Requested Common Extended Negotiation: None
D: Requested Asynchronous Operations Window Negotiation: None
D: Requested User Identity Negotiation: None
D: ========================== END A-ASSOCIATE-RQ PDU ==========================
I: Accepting Association
D: Accept Parameters:
D: ========================= BEGIN A-ASSOCIATE-AC PDU =========================
D: Our Implementation Class UID: 1.2.826.0.1.3680043.9.3811.1.4.1
D: Our Implementation Version Name: PYNETDICOM_141
D: Application Context Name: 1.2.840.10008.3.1.1.1
D: Responding Application Name: resp. AE Title
D: Our Max PDU Receive Size: 16382
D: Presentation Contexts:
D: Context ID: 1 (Rejected - Abstract Syntax Not Supported)
D: Abstract Syntax: =Verification SOP Class
D: Context ID: 3 (Rejected - Transfer Syntax Not Supported)
D: Abstract Syntax: =Secondary Capture Image Storage
D: Context ID: 5 (Rejected - Transfer Syntax Not Supported)
D: Abstract Syntax: =Ultrasound Image Storage
D: Context ID: 7 (Rejected - Transfer Syntax Not Supported)
D: Abstract Syntax: =Ultrasound Multi-frame Image Storage
D: Context ID: 9 (Accepted)
D: Abstract Syntax: =Basic Text SR Storage
D: Accepted SCP/SCU Role: Default
D: Accepted Transfer Syntax: =Implicit VR Little Endian
D: Accepted Extended Negotiation: None
D: Accepted Asynchronous Operations Window Negotiation: None
D: User Identity Negotiation Response: None
D: ========================== END A-ASSOCIATE-AC PDU ==========================
D: Abort Parameters:
D: ============================ BEGIN A-ABORT PDU =============================
D: Abort Source: DUL service-user
D: Abort Reason: No reason given
D: ============================= END A-ABORT PDU ==============================
I: Association Aborted
D: Request Parameters:
@aclark4life could you post your SCP code for the above as well? And could you upgrade to v1.5.6 and see if the issue still occurs?
@scaramallion Good catch thanks! Will try that. Here's my code. Works great already with a few other modalities. ❤️
# Based on https://pydicom.github.io/pynetdicom/stable/examples/storage.html#storage-scp
# and https://pydicom.github.io/pynetdicom/stable/examples/verification.html#verification-scu
import os
from pydicom import dcmread
from pynetdicom import AE, StoragePresentationContexts, evt, debug_logger
from pynetdicom.sop_class import VerificationSOPClass
# Implement a handler evt.EVT_C_STORE
def handle_store(event):
"""Handle a C-STORE request event."""
# Decode the C-STORE request's *Data Set* parameter to a pydicom Dataset
dataset = event.dataset
# Add the File Meta Information
dataset.file_meta = event.file_meta
# Get values from dataset
instance_num = str(dataset["InstanceNumber"].value)
patient_name = str(dataset["PatientName"].value)
patient_id = str(dataset["PatientID"].value)
study_date = str(dataset["StudyDate"].value)
study_time = str(dataset["StudyTime"].value)
series_desc = str(dataset["SeriesDescription"].value)
series_num = str(dataset["SeriesNumber"].value)
modality = str(dataset["Modality"].value)
# Clean up values
patient_name = patient_name.replace("^", "")
# Use modality, patient, study, imaging series values to define directory tree and filename
# unless value is empty string, in which case filter. E.g. create dir '4' instead of '4_' when
# series_desc == ''.
subdir_0 = modality
subdir_1 = "_".join(filter(None, [patient_name, patient_id]))
subdir_2 = "_".join(filter(None, [patient_name, study_date, study_time]))
subdir_3 = "_".join(filter(None, [series_num, series_desc]))
filename = "_".join(filter(None, [patient_name, series_num, instance_num]))
filename += ".dcm"
# Make directories
try:
os.mkdir(subdir_0)
except FileExistsError:
pass
try:
os.mkdir(os.path.join(subdir_0, subdir_1))
except FileExistsError:
pass
try:
os.mkdir(os.path.join(subdir_0, subdir_1, subdir_2))
except FileExistsError:
pass
try:
os.mkdir(os.path.join(subdir_0, subdir_1, subdir_2, subdir_3))
except FileExistsError:
pass
# Configure perms
os.chown(subdir_0, 101, gid=100)
os.chown(os.path.join(subdir_0, subdir_1), 101, gid=100)
os.chown(os.path.join(subdir_0, subdir_1, subdir_2), 101, gid=100)
os.chown(os.path.join(subdir_0, subdir_1, subdir_2, subdir_3), 101, gid=100)
path = os.path.join(subdir_0, subdir_1, subdir_2, subdir_3, filename)
# Save the dataset using modality, patient, study, imaging series as directory tree and filename
dataset.save_as(path, write_like_original=False)
os.chown(path, 101, gid=100)
# --------------------------------------------------------------------------------
#
# Based on https://pydicom.github.io/pynetdicom/stable/examples/storage.html#storage-scu
# Read in our DICOM dataset
ds = dcmread(path)
# Associate with peer AE running Orthanc at ORTHANC_IP_ADDRESS and port 4242
assoc = ae.associate(ORTHANC_IP_ADDRESS, 4242)
if assoc.is_established:
# Use the C-STORE service to send the dataset
assoc.send_c_store(ds)
# Release the association
assoc.release()
#
# --------------------------------------------------------------------------------
# Return a 'Success' status
return 0x0000
handlers = [(evt.EVT_C_STORE, handle_store)]
# Initialise the Application Entity
ae = AE()
# Add the supported presentation contexts
ae.supported_contexts = StoragePresentationContexts
# Add a requested presentation context
ae.add_requested_context(VerificationSOPClass)
# Set requested presentation context for storage-scu to send to Orthanc
ae.requested_contexts = StoragePresentationContexts
# Get Orthanc IP address or bust
ORTHANC_IP_ADDRESS = os.environ.get("ORTHANC_IP_ADDRESS", None)
if not ORTHANC_IP_ADDRESS:
print("Please `export ORTHANC_IP_ADDRESS=<ip_address>` before running scp.py")
exit(1)
DEBUG = os.environ.get("DEBUG", None)
if DEBUG:
debug_logger()
# Start listening for incoming association requests
ae.start_server(("", 104), evt_handlers=handlers)
Context ID: 1 (Rejected - Abstract Syntax Not Supported) D: Abstract Syntax: =Verification SOP Class
No Verification SOP Class as a supported context
Context ID: 3 (Rejected - Transfer Syntax Not Supported) D: Abstract Syntax: =Secondary Capture Image Storage D: Context ID: 5 (Rejected - Transfer Syntax Not Supported) D: Abstract Syntax: =Ultrasound Image Storage D: Context ID: 7 (Rejected - Transfer Syntax Not Supported) D: Abstract Syntax: =Ultrasound Multi-frame Image Storage
The default presentation contexts lists only use non-compressed transfer syntaxes. If you want to support JPEG you have to add it:
from pydicom.uid import JPEGBaseline
# Add the supported presentation contexts
# ae.supported_contexts = StoragePresentationContexts
for cx in StoragePresentationContexts:
cx.add_transfer_syntax(JPEGBaseline)
ae.add_supported_context(cx)
The SCU is aborting because none of the requested contexts it's interested in have been accepted.
@scaramallion Thanks for the help! Do you mean ae.add_supported_context(cx.abstract_syntax)
or something else? ae.add_supported_context(cx)
gives me a "UID must be string" error (or something like that. Thanks again.
Oh, sorry! Forgot how my own code works 🤦♂️.
for cx in StoragePresentationContexts:
cx.add_transfer_syntax(JPEGBaseline)
ae.supported_contexts = StoragePresentationContexts
@aclark4life can I close the issue?
@scaramallion This was @privateOmega 's issue that I hijacked (apologies!), but as far as I'm concerned, yes. Thanks
Ah, whoops! I'll close it anyway, its been a while.
Package versions
pynetdicom = "^1.5.6" pydicom = "^1.4.2"
Issue
Abstract Syntax not supported
I am trying to send a dicom from Horos to a StoreSCP implementation and I am encountering this error
So on setting up debug logger, I got the following
From which I understood, context 221, and 223 got rejected saying Abstract Syntax not supported, which isn't quite making sense to me because Ultrasound Image Storage was accepted in context 213 and 215 .