DCMTK开发笔记(四):C-MOVE操作详解

前言

在使用Dcmtk工具模拟PACS通信时,发现C-MOVE操作在Win10本地环境下服务端和客户端相互通信时可以成功,但在Ubuntu作服务端,Win10作客户端的情况下总是失败。一开始我以为是Win10防火墙设置的问题,但后来发现问题并不在这里。本文记录了解决这个问题的过程。

参考资料

Dcmtk Wiki DcmSCU example program
Dcmtk Forum C-MOVE Response (Refused: OutOfResourcesSubOperations)
Dcmtk Forum Receiving Images from PACS using DCMSCU
vmware虚拟机三种网络模式详解

操作步骤

Win10环境下SCU-SCP互连

  1. 命令行窗口1
e:
cd E:\DCMTK\PACS\SCP
dcmqrscp -d --config dcmqrscp.cfg
  1. 命令行窗口2
e:
cd E:\DCMTK\PACS\SCU
echoscu -d localhost 11112 -aec ACME_STORE -aet ACME1
movescu -d -S -aec ACME_STORE -aet ACME1 -aem ACME1 --port 1234 -od E:\DCMTK\PACS\SCU\database localhost 11112 -k QueryRetrieveLevel=STUDY -k StudyDate=20140101
  1. C-MOVE操作成功执行,E:\DCMTK\PACS\SCU\database 目录下出现新文件
    CT.2.16.840.1.113662.2.1.4519.41582.4105152.419990505.410523251
    在这里插入图片描述
  2. dcmqrscp.cfg 配置文件内容如下
NetworkTCPPort  = 11112
MaxPDUSize      = 16384
MaxAssociations = 16

HostTable BEGIN
acme1           = (ACME1, DESKTOP-ASCCO8S, 1234)
acme2           = (ACME2, Ubuntu-Qin, 5678)
acmeCTcompany   = acme1, acme2
HostTable END

VendorTable BEGIN
"Acme CT Company"   = acmeCTcompany
VendorTable END

AETable BEGIN
ACME_STORE   E:\DCMTK\PACS\SCP\database  RW (100, 1024mb)   acmeCTcompany
AETable END

Win10作Client,Ubuntu作Server(PACS)的情况

错误示范

  1. Ubuntu配置可参见前一篇博客
su root
cd /etc/dcmtk
cp dcmqrscp.cfg dcmqrscp.bak
vim dcmqrscp.cfg
dcmqrscp -d -c dcmqrscp.cfg

dcmqrscp.cfg 内容如下

NetworkTCPPort  = 11112
MaxPDUSize      = 16384
MaxAssociations = 16

HostTable BEGIN
acme1           = (ACME1, 192.168.141.1, 1234)
acme2           = (ACME2, localhost, 5678)
# acme3			= (ACME3, 192.168.6.212, 1235)
acmeCTcompany   = acme1, acme2,acme3
HostTable END

VendorTable BEGIN
"Acme CT Company"   = acmeCTcompany
VendorTable END

AETable BEGIN
ACME_STORE   /var/lib/dcmtk/db/ACME_STORE   RW (100, 1024mb)   acmeCTcompany
AETable END
  1. Win10命令行窗口输入
ping Ubuntu-Qin
e:
cd E:\DCMTK\PACS\SCU
echoscu -d Ubuntu-Qin 11112 -aec ACME_STORE -aet ACME1
storescu -d Ubuntu-Qin 11112 ct.dcm -aec ACME_STORE -aet ACME1
findscu -v -S -aec ACME_STORE -aet ACME1 Ubuntu-Qin 11112 -k QueryRetrieveLevel=STUDY -k StudyDate -k StudyDescription -k StudyInstanceUID
movescu Ubuntu-Qin 11112 -v -S -aec ACME_STORE -aet ACME1 -aem ACME1 --port 1234 -od E:\DCMTK\PACS\SCU\database -k QueryRetrieveLevel=STUDY -k StudyInstanceUID=2.16.840.1.113662.2.1.1519.11582.1990505.1105152

发现Ping,Echo,Store,Find操作都能成功,唯独Move操作失败。Server能接收到Client发来的Move请求,在输出Requesting Sub-Association之后卡好久,最终显示错误信息为Refused:OutOfResourcesSubOperations。
在这里插入图片描述

原因分析

前面的 参考资料2 中有以下描述:

An error during MOVE that refers to sub operations always refers to a problem on the second connection, i.e. the connection the PACS is starting to the storage server in order to send the images. Probably the storage server is behind a firewall, not configured correctly in the PACS (e.g. wrong port or IP address), which is in your case the dcmqrscp config file.

可以看出OutOfResourcesSubOperations 发生在PACS想要发送数据(在这里是CT图像)给Client时。可是即使设置防火墙开放端口1234(百度经验:Win10系统如何在防火墙里开放端口)之后(这一步实际上不需要),问题依然存在。难道是dcmqrscp.cfg 配置出现了问题?可是我们明明可以以ACME1作为AET和PACS通信啊?
部分原因是这样!
在这里插入图片描述
回想我们在Ubuntu系统dcmqrscp.cfg 中的配置,是将192.168.141.1 作为ACME1的IP地址。为什么呢?因为如果将主机Win10的真实IP地址 192.168.6.212 写入dcmqrscp.cfg ,使用echoscu命令会在客户端和服务端分别看到这样的日志
在这里插入图片描述
在这里插入图片描述
来自 192.168.6.212 (主机Win10的真实IP)的请求被识别为来自192.168.141.1(虚拟机Ubuntu VMNet8 的IP地址)!
因此我们将VMNet8 的IP地址写入PACS的配置中,就可以正常通信,我在错误示范中也正是这么做的。至于为什么请参见 参考资料4 vmware虚拟机三种网络模式详解
但是虚拟机Ubuntu向主机Win10发送请求(建立TCP连接)时并不是通过VMNet8的IP地址,而是使用真实IP 192.168.6.212 。。。

解决方法

综上所述,我们在PACS服务端的dcmqrscp.cfg 配置并不完整,需要添加acme3 以让PACS与主机建立连接

NetworkTCPPort  = 11112
MaxPDUSize      = 16384
MaxAssociations = 16

HostTable BEGIN
acme1           = (ACME1, 192.168.141.1, 1234)
acme2           = (ACME2, localhost, 5678)
acme3			= (ACME3, 192.168.6.212, 1235)
acmeCTcompany   = acme1, acme2,acme3
HostTable END

VendorTable BEGIN
"Acme CT Company"   = acmeCTcompany
VendorTable END

AETable BEGIN
ACME_STORE   /var/lib/dcmtk/db/ACME_STORE   RW (100, 1024mb)   acmeCTcompany
AETable END

但是这样做还不够,原因参见 参考资料4
我们还需要一个以acme3作为AET的storescp接收PACS发送的数据。
在Win10新建一个命令行窗口,输入

e:
cd E:\DCMTK\PACS\SCU\moveDest
storescp 1235 -d -aet ACME3 -od E:\DCMTK\PACS\SCU\moveDest

在Win10原来的命令行输入(这一步改变了 -aem 参数,其它不变)

movescu Ubuntu-Qin 11112 -d -S -aec ACME_STORE -aet ACME1 -aem ACME3 --port 1234 -od E:\DCMTK\PACS\SCU\database -k QueryRetrieveLevel=STUDY -k StudyDate=20140101

在这里插入图片描述
C-MOVE操作成功完成,Win10 E:\DCMTK\PACS\SCU\moveDest目录下新增文件 CT.2.16.840.1.113662.2.1.4519.41582.4105152.419990505.410523251。
大功告成!
我们终于实现了Win10作Client,Ubuntu作Server情况下的C-MOVE。但是还有一个问题,为什么Win10环境中不需要另开一个命令行窗口输入storescp接收数据呢?回忆一下这个命令

movescu localhost 11112 -d -S -aec ACME_STORE -aet ACME1 -aem ACME1 --port 1234 -od E:\DCMTK\PACS\SCU\database -k QueryRetrieveLevel=STUDY -k StudyDate=20140101

这里我们使用的主机名是localhost,aet和aem都设为ACME1,在Server和Client都处在Win10的环境下,两者都可以通过localhost进行连接,并且这里的端口号恰恰是1234,因此可以既发送请求又接收文件。为验证这一猜想,我们在Server和Client都处在Ubuntu环境的情况下验证这一想法。

mkdir scu
cd /etc/dcmtk/scu
movescu localhost 11112 -d -S -aec ACME_STORE -aet ACME2 -aem ACME2 --port 5678 -od /etc/dcmtk/scu -k QueryRetrieveLevel=STUDY -k StudyDate=20140101

在这里插入图片描述
/etc/dcmtk/scu 新增dcm文件,结果证实了我们的猜想:dcmqrscp.cfg 的配置不正确和虚拟机的网络通讯方式有很大关系。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
DCMTK是一个用于医学影像处理的开源软件,支持DICOM(Digital Imaging and Communications in Medicine)标准。实现C-MOVE是其中的一个功能,它允许从DICOM服务器上获取医学影像数据。 下面是一个使用DCMTK简单实现C-MOVE的代码示例: ```cpp #include "dcmtk/dcmnet/assoc.h" #include "dcmtk/dcmnet/cond.h" #include "dcmtk/dcmnet/dicom.h" int main() { // 初始化DCMTK库 DcmNetSCU.initializeNetwork(); // 创建一个新的DICOM关联 T_ASC_Parameters* params; ASC_createAssociationParameters(&params); ASC_setAPTitles(params, "C-MOVE_SCU", "C-MOVE_SCP", NULL); ASC_setTransportLayerType(params, TL_TCP); ASC_setAcceptor(params, OFTrue); // 连接DICOM服务器 const char* serverHostname = "127.0.0.1"; const int serverPort = 11112; T_ASC_Association* assoc; ASC_requestAssociation(params, &assoc, serverHostname, serverPort); if(ASC_associationAccepted(assoc)) { // 发送C-MOVE请求 DcmDataset* queryDataSet = new DcmDataset(); // 设置查询条件,例如StudyInstanceUID、SeriesInstanceUID等 queryDataSet->putAndInsertString(DCM_QueryRetrieveLevel, "STUDY"); queryDataSet->putAndInsertString(DCM_StudyInstanceUID, "1.2.3.4.5"); T_DIMSE_C_MoveRQ moveRequest; memset(&moveRequest, 0, sizeof(moveRequest)); moveRequest.MessageID = assoc->nextMsgID++; strncpy(moveRequest.AffectedSOPClassUID, UID_FINDStudyRootQueryRetrieveInformationModel, sizeof(moveRequest.AffectedSOPClassUID)); OFString moveDest = "C-MOVE_SCP"; // C-MOVE_SCP的AETitle OFString moveDestHost; OFString moveDestPort; OFStandard::strExtractSubstring(moveDest, moveDestHost, 0, OFStandard::strChr(moveDest, ':')); OFStandard::strExtractSubstring(moveDest, moveDestPort, OFStandard::strChr(moveDest, ':') + 1, OFString_npos); strncpy(moveRequest.MoveDestination, moveDestHost.c_str(), sizeof(moveRequest.MoveDestination)); moveRequest.MoveDestination[15] = '\0'; moveRequest.Priority = DIMSE_PRIORITY_LOW; moveRequest.Identifier = queryDataSet; DIMSE_sendMoveRequest(assoc, &moveRequest, NULL, NULL, NULL); // 处理C-MOVE响应 T_DIMSE_C_MoveRSP* moveResponse; while(DIMSE_receiveCommand(assoc, DIMSE_NONBLOCKING, 0) == DIMSE_NORMAL) { DIMSE_receiveDataSetInMemory(assoc, DIMSE_NONBLOCKING, NULL, NULL, NULL); DIMSE_getResponse(assoc, params, &moveResponse); // 解析响应,可以获取到影像数据 } delete moveResponse; delete queryDataSet; } else { // 关联未被接受 } // 关闭关联并清理资源 ASC_releaseAssociation(assoc); ASC_destroyAssociation(&assoc); // 清理DCMTK库 DcmNetSCU.cleanupNetwork(); return 0; } ``` 以上代码会连接到一个运行在本地的DICOM服务器,并发送一个C-MOVE请求,然后接收服务器返回的C-MOVE响应。在发送请求时,我们需要设置查询的条件,例如StudyInstanceUID、SeriesInstanceUID等来指定要获取的医学影像数据。在响应处理时,可以解析返回的数据集以获取影像数据。 以上是一个很简单的C-MOVE实现示例,实际应用中可能会有更多的细节和错误处理。使用DCMTK库可以方便地实现DICOM相关的功能,包括C-MOVE等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值