背景:
前段时间着重从dcmtk和fo-dicom(mDCM)源码角度进行剖析,期望加深对DICOM协议的理解。知其然,知其所以然。如果“所以然”很不好懂,那我们还是先多多“知其然”吧。搞清楚原理的目的不也是为了更好的运用于实践么?所以理论和实践应该彼此交错进行,理论搞不动了就搞搞应用,应用久了就钻研钻研理论。
以前上DCMTK官网仅仅是浏览关于开源库中各个类的设计模式、依赖关系。最近在打开DCMTK官网的wiki时,才发现OFFIS对DCMTK的介绍是如此的详细。正值国庆假日,就不深挖DCMTK源码了,那就按照DCMTK
wiki中给出的介绍来实际体验分析一下DCMTK,从实践角度来学习一下。
PACSDebugging with DCMTK
前几篇博文分别介绍了worklist查询服务(DICOM医学图像处理:基于DCMTK工具包学习和分析worklist、DICOM医学图像处理:利用fo-dicom发送C-Find查询Worklist)、C-STORE服务(DICOM医学图像处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求、DICOM医学图形处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求(续))和C-MOVE服务(DICOM医学图像处理:AETitle在C-FIND和C-MOVE请求中的设置问题)。此次参考wiki中的说明利用DCMTK中的工具来讲解一下如何调试PACS系统。
下文中会用到的工具有以下两类
服务端 |
dcmqrscp |
客户端 |
echoscu、storescu、findscu、movescu |
PACS是什么?在DICOM标准中并没有明确的定义,DICOM协议大多是通过定义SOP来描述相关网络服务。但是几乎每一个PACS系统会包含以下几种SOP类,
Verification SOP Class |
又称为DICOM ECHO服务,用于查明网络对端系统(即PACS)是否符合DICOM标准(即talks DICOM),以便双方按照DICOM标准进行对话。 |
Storage SOP Classes |
将一个或多个DICOM对象存储到PACS服务器。一个PACS系统往往需要支持多种Storage SOP Classes,用以存储不同设备的图像数据(如CT、US、MR等)。 |
Query SOP Classes |
根据指定的关键字查询PACS数据库。但是并不下载图像,仅仅是查询图像有关的信息。 |
Retrieve SOP Classes |
根据Query SOP Classes的结果找到目标图像后,利用Retrieve SOP Classes服务从PACS服务器下载图像到本地。 |
Storage Commitment SOP Classes |
客户通过该服务确认PACS服务端已经成功完成了图像的归档。 |
因此可以简单的理解为PACS就是提供了上述多种服务的服务端。在DCMTK工具包中给我们提供了一个PACS模拟工具——dcmqrscp,该工具提供了上表中的所有服务(Storage
Commitment SOP Classes除外,该部分并未包含在DCMTK开源包中,而需要购买商用版本)。
下面就利用dcmqrscp与其他的dcmtk工具来模拟调试一下客户端与PACS服务端的交互过程,从实际应用的角度熟悉DICOM3.0标准。
1)安装PACS服务器:
利用DCMTK给出的dcmqrscp工具包结合自己定制的配置文件来搭建我们的PACS服务器(为了更好的学习DCMTK工具包,不建议直接使用wiki中给出的公用版PACS,即www.dicomserver.co.uk/)
dcmqrscp跟其他dcmtk工具包一样,可以通过添加-h或--help命令行参数来查看工具包的使用说明。唯一不同的是要想启动PACS服务器还需要指定一个配置文件。DCMTK提供的默认的配置文件为dmqrscp.cfg。打开dcmtk工具包中的dcmqrscp.cfg文件,其中的注释已经很清楚。简单概括为三部分:
第一部分,网络配置,即传统网络编程中用到参数。如NetworkTCPPort——监听端口,用于监听来自客户端的各种连接请求(需要注意的是要配置自己的防火墙,开放指定的端口);MaxAssociations——允许的最大连接数;MaxPDUSize——定义PDU传输时刻的最大长度等等。
第二部分,关于连接到dcmqrscp服务器的客户机定义。该部分包含在dcmqrscp.cfg配置文件HostTable
BEGIN和Host Table END内。默认的定义如下:
简而言之,该部分就是定义可能连接到PACS服务器的客户机信息,通常包含AETitle、HostName、PortNamer三部分。需要指出的是目前HostName(主机名称)还不支持直接IP地址的方式,因此在本地配置的时候要格外注意。
本地机的配置如下:
acme1 = (ACME1,localhost,11110)
acme2 = (ACME2,localhost,11110)
acmeCTcompany =acme1 , acme2
第三部分,客户机的详细信息。该部分目的多是为了方便用户的阅读,方便配置时使用。在下文中的调试过程中并未用到,因此就不做介绍了。
第四部分,PACS服务端存储位置信息定义。通过该部分设置,可以实现将不同客户端传统过来的数据归档到不同的PACS服务器目录。同时针对不同的AE指定不同的读写权限、存储的研究(study)数量等。默认的配置文件如下,
该部分配置的时候要注意路径必须在本地已经存在,否则会引发错误。例如我在本地的配置如下,
ACME_STORED:\DcmScuScp\DcmScp RW (9, 1024mb) acmeCTcompany
下面给出我在本地机的dcmqrscp.cfg配置文件,
在命令行启动dcmqrscp工具,输出状态如下:
2)PACS的功能调试
PACS可以简单的理解为提供了多种DICOM标准中SOP服务的软件。我们已经利用dcmqrscp工具启动了一个PACS系统,接下来就按照上一节中PACS提供的SOP服务类表格来依次进行测试
Verification SOPClass服务测试
VerificationSOPClass服务是每一个PACS系统必须提供的一项服务,用于指出该PACS服务符合DICOM协议。DCMTK工具包中的echoscu工具可发起该请求,具体指令如下:echoscu.exe
–dlocalhost 11110
11110对应于dcmqrscp.cfg配置文件第一部分给出的NetworkTCPPort,-d是调试选项,方便我们观察工具包的运行状态。运行后的输出结果如下:
喔?竟然出现了几个致命错误。幸好我们开启了-d调试开关,从调试结果中看出错误的原因是无法识别Called
AE Title,因为我们在echoscu命令行中并未指定dcmqrscp的名称。修改后指令如下,echoscu.exe
–d localhost 11110 –aec ACME_STORE
竟然又出现了同样的错误?想必很多第一次接触dcmtk的同学看到这个结果就已经心凉了一半,无心继续下去了。DMCTK的wiki中指出这个错误提示并未指出真正的错误原因,这个是dcmqrscp.exe工具包的问题。这里应该是要求我们同时指定我们自己的AE名称,再次修改后的代码如下:echoscu.exe
–dlocalhost 11110 –aec ACME_STORE –aet ACME1
好吧,又出现了同样的错误,我是服了。看来想好好学习应用也不是很容易的啊。为了能够继续后续的其他测试,查看一下dcmqrscp工具包的源码文件dcmqrscp.cc,找出产生上述问题的原因。
问题排查:
在dcmqrscp.cc文件main函数中的waitForAssociation一行插入断点,进行单步调试。如上在命令行开启echoscu,发送C-ECHO请求。逐行运行代码,具体流程如下,
最后代码停留在_stricmp(HostName,CNF_Config.AEEntries[i].Peers[j].HostName)一行,如下:
该行中的HostName函数指的是我们主机的名称,例如我本机的名称是:PC-201408122158,而CNF_Config.AEEntries[i].Peers[j].HostName指的是我们配置文件dcmqrscp.cfg中的HostTable部分,其中HostName对应的就是上面配置文件中的localhost。
至此,经过简单的源码分析,已经顺利找到了问题的原因。之所以一直提示“Called
AE Title Not Recognized”就是因为我们将HostTable中的Hostname误认为是本机IP地址的字符名称,所以错误的将主机名称设置成了localhost。其实在dcmqrscp工具包的配置文件dcmqrscp.cfg中曾有过提示“Note:in
the current implementation you cannot substitutean IP address for a hostname”。
重新修改配置文件中的hostname为PC-201408122158,再次进行尝试。此次连接测试顺利通过,测试结果如下:
Storage SOP Classes服务测试
连接测试顺利通过后,利用storescu.exe工具包对Storage
SOPClass服务进行测试。具体指令如下:storescu.exe –dlocalhost 11110 00.dcm –aec ACME_STORE –aet ACME1,测试结果显示为Success(如下图)
经过此次storescu测试,成功的将D盘根目录下的00.dcm文件上传到了dcmqrscp.cfg中指定的存储目录下,即D:\DcmScuScp\DcmScp,在存储的过程中dcmqrscp将00.dcm文件进行了重命名(关于重命名的规则可参见DCMTK对dcmqrscp的wiki介绍,也可以通过命令行参数来设定重命名的方式),同时在归档目录下生成了一个名称为index.dat的记录文件,如下图:
Query SOP Classes服务测试
继续我们的调试,通过使用storescu.exe已经能够顺利的将我们的图像上传到指定的PACS服务目录下。接下来对我们上传的数据进行查询测试,使用的工具是findscu.exe,测试的具体指令为:findscu.exe
-v -S -aec ACME_STORE -aet ACME1 localhost11110 -k QueryRetrieveLevel=STUDY -k StudyDate -k StudyDescription -kStudyInstanceUID,查询的结果如下:
查询反馈的结果与我们在利用dcmdump工具显示的信息完全一致,这说明Query
SOPClasses测试顺利通过。
Retrieve SOP Classes服务测试
进行我们最后一项测试,就是将上传到PACS服务器的图像数据重新下载到本地。测试的工具是movescu.exe,具体指令如下:movescu.exe
-v -S-aec ACME_STORE -aet ACME1 -aem ACME1 --port 11110 -od D:\DcmScuScp\DcmSculocalhost 11110 -k QueryRetrieveLevel=STUDY -k StudyInstanceUID = 2.16.840.114421.81295.9407241257
原本以为会顺利将图像保存到本地D:\DcmScuScp\DcmScu目录下,结果PACS服务端和客户端同时停在了如下状态,
由上图可以看出网络连接部分的交互已经完成了,而且对于dcmqrscp.exe模拟的PACS服务端的各项服务(VerificationSOPClass、StorageSOPClass、QuerySOPClass、RetrieveSOPClass)我们都已经测试过了,为何信息交互停留在了ConstructionAssociation
RQ PDU部分呢?仔细检查一下命令行参数以及dcmqrscp.cfg配置文件,发现在本地测试的时候我们将dcmqrscp.exe模拟的PACS服务器监听端口和可能连入的客户端端口都设置成了11110,因此在进行图像传输的过程中会发生冲突,为了验证我们的猜测,将ACME1客户端的端口修改为12345,再一次进行movescu的测试,指令如下:movescu.exe
-v -S-aec ACME_STORE -aet ACME1 -aem ACME1 --port 12345 -od D:\DcmScuScp\DcmScu localhost11110 -k
QueryRetrieveLevel=STUDY -kStudyInstanceUID=2.16.840.114421.81295.9407241257,测试结果如下:
打开本地的D:\DcmScuScp\DcmScu目录,可以看到由storescu.exe上传到PACS服务器的文件。
至此利用DCMTK工具对PACS的调试工作全部结束了,上述的调试完全参照DCMTK中wiki的相关内容,原文链接为http://support.dcmtk.org/redmine/projects/dcmtk/wiki/Howto_PACSDebuggingWithDCMTK,这里我的操作仅供大家参考,如果有精力还希望仔细阅读一下英文原文,原文的讲解更详细更全面。
后续专栏博文介绍
Dicom中的MPPS服务介绍
C#的异步编程模式在fo-dicom中的应用
VMWare三种网络连接模式的实际测试
作者:[email protected]时间:2014-10-03