我第一次尝试使用 pynetdicom。我已经在我的 PC 上安装了它和 ConQuest DICOM 服务器。我能够使 pynetdicom echo 示例正常工作,但是当我尝试 pynetdicom Storage SCU 示例(https://pydicom.github.io/pynetdicom/stable/examples/storage.html)时,它失败了:
No presentation context for 'CT Image Storage' has been accepted by the peer for the SCP role
问题是,据我所知,该示例已经协商了 CT 图像存储上下文。这是日志记录:
pydev debugger: starting (pid: 39132)
I: Requesting Association
D: Request Parameters:
D: ======================= OUTGOING A-ASSOCIATE-RQ PDU ========================
D: Our Implementation Class UID: 1.2.826.0.1.3680043.9.3811.1.5.3
D: Our Implementation Version Name: PYNETDICOM_153
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 Contexts:
D: Context ID: 1 (Proposed)
D: Abstract Syntax: =Patient Root Query/Retrieve Information Model - GET
D: Proposed SCP/SCU Role: Default
D: Proposed Transfer Syntaxes:
D: =Implicit VR Little Endian
D: =Explicit VR Little Endian
D: =Deflated Explicit VR Little Endian
D: =Explicit VR Big Endian
D: Context ID: 3 (Proposed)
D: Abstract Syntax: =CT Image Storage
D: Proposed SCP/SCU Role: SCP
D: Proposed Transfer Syntaxes:
D: =Implicit VR Little Endian
D: =Explicit VR Little Endian
D: =Deflated 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 PDU ==========================
D: Accept Parameters:
D: ======================= INCOMING A-ASSOCIATE-AC PDU ========================
D: Their Implementation Class UID: 1.2.826.0.1.3680043.2.135.1066.101
D: Their Implementation Version Name: 1.5.0/WIN32
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: 32768
D: Presentation Contexts:
D: Context ID: 1 (Accepted)
D: Abstract Syntax: =Patient Root Query/Retrieve Information Model - GET
D: Accepted SCP/SCU Role: Default
D: Accepted Transfer Syntax: =Implicit VR Little Endian
D: Context ID: 3 (Accepted)
D: Abstract Syntax: =CT Image 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 ==========================
I: Association Accepted
I: Sending Get Request: MsgID 1
I:
I: # Request Identifier
I: (0008,0052) CS [PATIENT] # 1 QueryRetrieveLevel
I: (0010,0010) PN [Wrench, Fred] # 1 PatientName
I:
D: ========================== OUTGOING DIMSE MESSAGE ==========================
D: Message Type : C-GET RQ
D: Message ID : 1
D: Affected SOP Class UID : Patient Root Query/Retrieve Information Model - GET
D: Identifier : Present
D: Priority : Low
D: ============================ END DIMSE MESSAGE =============================
D: pydicom.read_dataset() TransferSyntax="Little Endian Implicit"
I: Received Store Request
D: ========================== INCOMING DIMSE MESSAGE ==========================
D: Message Type : C-STORE RQ
D: Presentation Context ID : 3
D: Message ID : 59647
D: Affected SOP Class UID : CT Image Storage
D: Affected SOP Instance UID : 1.3.6.1.4.1.33997.3232235894.19152.1608672548.1.1.1
D: Move Originator : PYNETDICOM
D: Data Set : Present
D: Priority : Medium
D: ============================ END DIMSE MESSAGE =============================
E: No presentation context for 'CT Image Storage' has been accepted by the peer for the SCP role
D: pydicom.read_dataset() TransferSyntax="Little Endian Implicit"
D: ========================== INCOMING DIMSE MESSAGE ==========================
D: Message Type : C-GET RSP
D: Presentation Context ID : 1
D: Message ID Being Responded To : 1
D: Affected SOP Class UID : Patient Root Query/Retrieve Information Model - GET
D: Remaining Sub-operations : 0
D: Completed Sub-operations : 0
D: Failed Sub-operations : 5
D: Warning Sub-operations : 0
D: Identifier : None
D: Status : 0xFE00
D: ============================ END DIMSE MESSAGE =============================
D:
I: Get SCP Result: 0xFE00 (Cancel)
I: Sub-Operations Remaining: 0, Completed: 0, Failed: 5, Warning: 0
D: pydicom.read_dataset() TransferSyntax="Little Endian Implicit"
C-GET query status: 0xfe00
I: Releasing Association
因此,日志显示 CT 图像存储上下文已被接受,但具有 SCP/SCU 角色:默认值,无论这意味着什么。当我通过 pynetdicom 处理进行调试时,我看到有一个 CT 上下文,但仅适用于 SCP 角色。ConQuest 显示它已发送 DICOM 文件,然后断开连接。
当然还有代码(仅对 pynetdicom 示例进行了细微更改:
from pydicom.dataset import Dataset
from pynetdicom import AE, evt, build_role, debug_logger
from pynetdicom.sop_class import (
PatientRootQueryRetrieveInformationModelGet,
CTImageStorage
)
debug_logger()
# Implement the handler for evt.EVT_C_STORE
def handle_store(event):
"""Handle a C-STORE request event."""
ds = event.dataset
ds.file_meta = event.file_meta
# Save the dataset using the SOP Instance UID as the filename
ds.save_as(ds.SOPInstanceUID, write_like_original=False)
# Return a 'Success' status
return 0x0000
handlers = [(evt.EVT_C_STORE, handle_store)]
# Initialise the Application Entity
ae = AE()
# Add the requested presentation contexts (QR SCU)
ae.add_requested_context(PatientRootQueryRetrieveInformationModelGet)
# Add the requested presentation context (Storage SCP)
ae.add_requested_context(CTImageStorage)
# Create an SCP/SCU Role Selection Negotiation item for CT Image Storage
role = build_role(CTImageStorage, scp_role=True)
# Create our Identifier (query) dataset
# We need to supply a Unique Key Attribute for each level above the
# Query/Retrieve level
ds = Dataset()
ds.QueryRetrieveLevel = 'PATIENT'
ds.PatientName = 'Wrench, Fred'
# Associate with peer AE at IP 127.0.0.1 and port 11112
assoc = ae.associate('127.0.0.1', 5679, ext_neg=[role], evt_handlers=handlers)
if assoc.is_established:
# Use the C-GET service to send the identifier
responses = assoc.send_c_get(ds, PatientRootQueryRetrieveInformationModelGet)
for (status, identifier) in responses:
if status:
print('C-GET query 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')
我一直在谷歌搜索,直到我的谷歌用户感到疼痛。