Hello,
I am using Orthanc as a PACS server for testing.
I am running on MacOS using ./Orthanc --verbose.
I wish to query and retrieve images based on the Patient ID. So far, i am stuck on doing a C-Find request from my local machine.
When performing the C-Find, Orthanc is outputting the following information.
I0217 19:44:08.164480 CommandDispatcher.cpp:491] Association Received from AET PYNETDICOM on IP 127.0.0.1
I0217 19:44:08.164669 main.cpp:195] Incoming connection from AET PYNETDICOM on IP 127.0.0.1, calling AET ORTHANC
I0217 19:44:08.164716 CommandDispatcher.cpp:689] Association Acknowledged (Max Send PDV: 16370)
I0217 19:44:08.169743 main.cpp:215] Incoming Find request from AET PYNETDICOM on IP 127.0.0.1, calling AET ORTHANC
W0217 19:44:08.169782 CommandDispatcher.cpp:812] Rejected Find request from remote DICOM modality with AET "PYNETDICOM" and hostname "127.0.0.1"
I0217 19:44:08.169804 CommandDispatcher.cpp:909] DIMSE failure (aborting association): DIMSE Caller passed in an illegal association
This appears to be related to DicomModalities in the configuration file for Orthanc.
What is wrong with my configuration?
Does anyone have a working version of Pynetdicom for C-Find and C-Move?
Please see below for a more detailed explanation of the steps that i have taken.
The steps i have followed are shown below.
1) Configure Orthanc for client
"DicomServerEnabled" : true,
// The DICOM Application Entity Title
"DicomAet" : "ORTHANC",
// Check whether the called AET corresponds during a DICOM request
"DicomCheckCalledAet" : false,
// The DICOM port
"DicomPort" : 4242,
// The list of the known DICOM modalities
"DicomModalities" : {
"PYNETDICOM" : [ "PYNETDICOM", "127.0.0.1", 4242 ]
},.
2) Start Orthanc
./Orthanc --verbose
3) Upload images (Drag/Drop)
4) Create Pynetdicom echo script
from pynetdicom import AE
from pynetdicom.sop_class import VerificationSOPClass
import logging
LOGGER = logging.getLogger('pynetdicom')
LOGGER.setLevel(logging.DEBUG)
# Initialise the Application Entity
ae = AE()
# Add a requested presentation context
ae.add_requested_context(VerificationSOPClass)
# Associate with peer AE at IP 127.0.0.1 and port 11112
assoc = ae.associate('127.0.0.1', 4242)
if assoc.is_established:
# Use the C-ECHO service to send the request
# returns the response status a pydicom Dataset
status = assoc.send_c_echo()
# Check the status of the verification request
if status:
# If the verification request succeeded this will be 0x0000
print('C-ECHO request status: 0x{0:04x}'.format(status.Status))
else:
print('Connection timed out, was aborted or received invalid response')
# Release the association
assoc.release()
else:
print('Association rejected, aborted or never connected')
Response....
I: Requesting Association
D: Request Parameters:
D: ====================== BEGIN A-ASSOCIATE-RQ =====================
D: Our Implementation Class UID: 1.2.826.0.1.3680043.9.3811.1.2.0
D: Our Implementation Version Name: PYNETDICOM_120
D: Application Context Name: 1.2.840.10008.3.1.1.1
D: Calling Application Name: PYNETDICOM
D: Called Application Name: ANY-SCP
D: Our Max PDU Receive Size: 16382
D: Presentation Context:
D: Context ID: 1 (Proposed)
D: Abstract Syntax: =Verification SOP Class
D: Proposed SCP/SCU Role: Default
D: Proposed Transfer Syntaxes:
D: =Implicit VR Little Endian
D: =Explicit VR Little Endian
D: =Explicit VR Big 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 ======================
D: Accept Parameters:
D: ====================== BEGIN A-ASSOCIATE-AC =====================
D: Their Implementation Class UID: 1.2.276.0.7230010.3.0.3.6.2
D: Their Implementation Version Name: OFFIS_DCMTK_362
D: Application Context Name: 1.2.840.10008.3.1.1.1
D: Calling Application Name: PYNETDICOM
D: Called Application Name: ANY-SCP
D: Their Max PDU Receive Size: 16384
D: Presentation Contexts:
D: Context ID: 1 (Accepted)
D: Accepted Transfer Syntax: =Explicit VR Little Endian
D: Role Selection: None
D: Accepted Extended Negotiation: None
D: Accepted Asynchronous Operations Window Negotiation: None
D: User Identity Negotiation Response: None
D: ======================= END A-ASSOCIATE-AC ======================
I: Association Accepted
I: Sending Echo Request: MsgID 1
D: pydicom.read_dataset() TransferSyntax="Little Endian Implicit"
I: Received Echo Response (Status: Success)
C-ECHO request status: 0x0000
Orthanc log.....
I0217 19:43:34.197128 CommandDispatcher.cpp:491] Association Received from AET PYNETDICOM on IP 127.0.0.1
I0217 19:43:34.197223 main.cpp:195] Incoming connection from AET PYNETDICOM on IP 127.0.0.1, calling AET ANY-SCP
I0217 19:43:34.197257 CommandDispatcher.cpp:689] Association Acknowledged (Max Send PDV: 16370)
I0217 19:43:34.200992 main.cpp:215] Incoming Echo request from AET PYNETDICOM on IP 127.0.0.1, calling AET ANY-SCP
I0217 19:43:34.201039 CommandDispatcher.cpp:922] Received Echo Request
I0217 19:43:34.205005 CommandDispatcher.cpp:892] DUL Peer Requested Release
I0217 19:43:34.205042 CommandDispatcher.cpp:899] Association Release
5) Make a c-find request
from pydicom.dataset import Dataset
from pynetdicom import AE
from pynetdicom.sop_class import (
PatientRootQueryRetrieveInformationModelFind,
PatientRootQueryRetrieveInformationModelMove
)
import logging
LOGGER = logging.getLogger('pynetdicom')
LOGGER.setLevel(logging.DEBUG)
# Initialise the Application Entity
ae = AE()
# Add a requested presentation context
ae.add_requested_context(PatientRootQueryRetrieveInformationModelFind)
# Create our Identifier (query) dataset
ds = Dataset()
ds.PatientID = "3018030"
ds.QueryRetrieveLevel = 'STUDY'
# Associate with peer AE at IP 127.0.0.1 and port 5678
assoc = ae.associate('127.0.0.1', 4242, ae_title=b'ORTHANC')
if assoc.is_established:
print('Association Established')
# Use the C-FIND service to send the identifier
# A query_model value of 'P' means use the 'Patient Root Query Retrieve
# Information Model - Find' presentation context
responses = assoc.send_c_find(ds, query_model='P')
for (status, identifier) in responses:
print('C-MOVE query status: 0x{0:04x}'.format(status.Status))
# Release the association
assoc.release()
print('Association Released')
else:
print('Association rejected or aborted')
ds.clear()
Response....
I: Requesting Association
D: Request Parameters:
D: ====================== BEGIN A-ASSOCIATE-RQ =====================
D: Our Implementation Class UID: 1.2.826.0.1.3680043.9.3811.1.2.0
D: Our Implementation Version Name: PYNETDICOM_120
D: Application Context Name: 1.2.840.10008.3.1.1.1
D: Calling Application Name: PYNETDICOM
D: Called Application Name: ORTHANC
D: Our Max PDU Receive Size: 16382
D: Presentation Contexts:
D: Context ID: 1 (Proposed)
D: Abstract Syntax: =Patient Root Query/Retrieve Information Model - FIND
D: Proposed SCP/SCU Role: Default
D: Proposed Transfer Syntaxes:
D: =Implicit VR Little Endian
D: =Explicit VR Little Endian
D: =Explicit VR Big Endian
D: Context ID: 3 (Proposed)
D: Abstract Syntax: =Patient Root Query/Retrieve Information Model - MOVE
D: Proposed SCP/SCU Role: Default
D: Proposed Transfer Syntaxes:
D: =Implicit VR Little Endian
D: =Explicit VR Little Endian
D: =Explicit VR Big 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 ======================
D: Accept Parameters:
D: ====================== BEGIN A-ASSOCIATE-AC =====================
D: Their Implementation Class UID: 1.2.276.0.7230010.3.0.3.6.2
D: Their Implementation Version Name: OFFIS_DCMTK_362
D: Application Context Name: 1.2.840.10008.3.1.1.1
D: Calling Application Name: PYNETDICOM
D: Called Application Name: ORTHANC
D: Their Max PDU Receive Size: 16384
D: Presentation Contexts:
D: Context ID: 1 (Accepted)
D: Accepted Transfer Syntax: =Explicit VR Little Endian
D: Context ID: 3 (Accepted)
D: Accepted Transfer Syntax: =Explicit VR Little Endian
D: Role Selection: None
D: Accepted Extended Negotiation: None
D: Accepted Asynchronous Operations Window Negotiation: None
D: User Identity Negotiation Response: None
D: ======================= END A-ASSOCIATE-AC ======================
I: Association Accepted
Association Established
I: Find SCU Request Identifiers:
I:
I: # DICOM Dataset
I: (0008, 0052) Query/Retrieve Level CS: 'STUDY'
I: (0010, 0020) Patient ID LO: '3018030'
I:
I: Sending Find Request: MsgID 1
D: ===================== OUTGOING DIMSE MESSAGE ====================
D: Message Type : C-FIND RQ
D: Presentation Context ID : 1
D: Message ID : 1
D: Affected SOP Class UID : 1.2.840.10008.5.1.4.1.2.1.1
D: Identifier : Present
D: Priority : Low
D: ======================= END DIMSE MESSAGE =======================
D: Abort Parameters:
D: ========================== BEGIN A-ABORT ========================
D: Abort Source: DUL service-user
D: Abort Reason: No reason given
D: =========================== END A-ABORT =========================
E: Association Aborted
Orthanc Log....
I0217 19:44:08.164480 CommandDispatcher.cpp:491] Association Received from AET PYNETDICOM on IP 127.0.0.1
I0217 19:44:08.164669 main.cpp:195] Incoming connection from AET PYNETDICOM on IP 127.0.0.1, calling AET ORTHANC
I0217 19:44:08.164716 CommandDispatcher.cpp:689] Association Acknowledged (Max Send PDV: 16370)
I0217 19:44:08.169743 main.cpp:215] Incoming Find request from AET PYNETDICOM on IP 127.0.0.1, calling AET ORTHANC
W0217 19:44:08.169782 CommandDispatcher.cpp:812] Rejected Find request from remote DICOM modality with AET "PYNETDICOM" and hostname "127.0.0.1"
I0217 19:44:08.169804 CommandDispatcher.cpp:909] DIMSE failure (aborting association): DIMSE Caller passed in an illegal association