广州手机网站建设,如保做网站赢利,北京封闭小区名单最新,钟表网站模板转自#xff1a;https://blog.csdn.net/zssureqh/article/details/38775315
背景#xff1a; DICOM3.0协议中有介绍关于worklist的部分。简而言之#xff0c;worklist可以看做是放射科设备从医院RIS系统中自动读取患者信息的一种“通信协议”#xff0c;可以指存储在RIS系…转自https://blog.csdn.net/zssureqh/article/details/38775315
背景 DICOM3.0协议中有介绍关于worklist的部分。简而言之worklist可以看做是放射科设备从医院RIS系统中自动读取患者信息的一种“通信协议”可以指存储在RIS系统中的患者数据库主要包括患者的基本信息如年龄、性别、身高、体重、出生年月等这与DCM文件信息头MetaInfo中的多数字段重合。因此从RIS系统中自动获取worklist是医院信息化的必要组成部分。下面简单的给出几个图像形象的描述一下worklist的作用。 worklist的实例学习 在简单的了解了worklist的作用后下面我们利用DCMTK提供的工具包wlmscpfs.exe和findscu.exe来真实模拟一下该场景从而更深刻的学习worklist的功能。
worklist简单的看做一种“通讯”那么自然就存在着通讯的两端暂且称作“服务端”和“客户端”。这里我们用wlmscpfs.exe来作为worklist通讯的服务端即等待外部访问的终端用findscu.exe来作为客户端用来发起worklist访问。
首先简单的介绍一下工具包的指令及使用方式。wlmscpfs.exe是类似于DOS时代的命令通过设定参数可以实现不同的目的。利用WinR键开启操作系统的运行窗口输入cmd后进入到命令提示行窗口。然后进入到DCMTK编译后的bin文件夹我本机地址是C:\Program Files (x86)\DCMTK\bin目录此时直接输入wlmscpfs.exe就可以看到关于该命令工具的各种说明。
cd C:\Program Files (x86)\DCMTK\bin
wlmscpfs.exe 注此处如果为了省事可以将bin文件夹路径添加到windows的环境变量中如是就可以在任何目录下使用bin下的各种工具了 由上图看到wlmscpfs.exe工具至少需要给出port一个参数即开启worklist服务的本机端口号。另外还需要输入worklist数据库文件的地址用来供客户端查询、访问使用。下面我们正式开启worklist的服务端程序至于开启全过程可以跟大家推荐CSDN一位博主的精品文章http://blog.csdn.net/pachleng/article/details/5800513博文中给出了具体的操作步骤这个实例是对DCMTK论坛中的补充和更新。大家可以动手试一下。
第一步建立各级目录 以我的电脑为例我在D盘创建了DCMWorklist文件夹然后建立了两个子文件夹wlistdb和wlistqry。 第二步准备worklist数据库文件开启worklist服务端服务。 然后将worklist的数据库文件拷贝到wlistdb目录下此处参见冷哥博文记得建立OFFIS子目录。至于worklist数据库文件通常在dcmtk库的源码中已经给出了默认目录是dcmtk-3.6.0\dcmtk-3.6.0\dcmwlm\datawlistdb我用的是3.6.0版本。但是源码中的文件通常是.dump扩展名的文件也就是我们常见的文本文件用记事本或者Notepad等工具双击即可打开。通过利用dcmtk工具包中的dump2dcm.exe可以将.dump文件转换成.wl文件转换后的.wl文件就是worklist数据库文件。
dcmp2dcm.exe .\dcmtk-3.6.0\dcmtk-3.6.0\dcmwlm\data\wklistdb\wlist1.dump d:\DCMWorklist\wlistdb\OFFIS\wlist.wl
…… 有了worklist数据库文件后我们就可以开启worklist服务了利用的工具就是前文提到的wlmscpfs.exe从工具名称中的SCP就可以看出这应该是服务端开启服务的。
wlmscpfs.exe –d –dfr –dfp d:\DCMWorklist\wlistdb 104 注其中的-d是为了方便我们观察工具运行过程而开启的调试开关
……
第三步准备查询文件开启worklist查询。 服务端已经准备就绪下面就是该发起worklist查询服务啦。利用的工具是findscu.exe从工具名称中的SCU同样可以猜测出这是客户端。dcmtk源码包中同样给我们提供了查询worklist的查询文件默认目录是dcmtk-3.6.0\dcmtk-3.6.0\dcmwlm\data\wlistqry打开后可以看到里面以.dump格式存在的文件再次利用dump2dcm.exe将.dump转换为.wl文件。
dcmp2dcm.exe .\dcmtk-3.6.0\dcmtk-3.6.0\dcmwlm\data\wklistqrt\wlistqry1.dump d:\DCMWorklist\wlistqry\wlistqry.wl 然后我们利用findscu发起查询请求
findscu.exe –d 127.0.0.1 104 d:\DCMWorklist\wlistqry\wlistqry.wl –aec OFFIS 其中aec代表的是被请求或者说被呼叫的应用端的名称即我们wlistdb文件夹内的OFFIS子文件夹。
通过以上三步我们就简单的利用dcmtk提供的wlmscpf.exe和findscu.exe工具包以及相应的wklistdb数据库文件和wklistqry数据库查询文件模拟了worklist服务开启及查询的整个流程。是不是很简单很容易上手。
实际结果分析 上一部分中提到了在使用wlmscpfs.exe和findscu.exe工具包的时候开启了-d调试模式目的就是为了方便我们跟踪整个通讯的流程。另外为了方便查看我们利用重定向技术将wlmscpfs.exe和findscu.exe工具包的调试信息输出到txt文件方便我们事后进行再次对比查看。下面将两个工具包的调试信息用Notopad打开对比分析一下见下图 从调试信息可以清晰的看到wlmscpfs.exe与findscu.exe之间的通信流程该流程在DICOM3.0标准的第四部分Service Class Specifications 和第八部分Network Communication Support for Message Exchange都有详细的介绍上述的重定向生成的文本文档就是学习DICOM3.0第四、八部分最好的实例。因为dcmtk是开源的所以这方便我们分析wlmscpfs.exe和findscu.exe两个工具包的源码。具体分析见下一节。
wlmscpfs.exe和findscu.exe工具包源码分析 wlmscpfs.exefindscu.exeC/Sworklist服务端worklist客户端源码文件wlmscpfs.cc wlcefs.cc wlmactmg.ccfindscu.cc内部函数1ConnectToDataSource();//开启连接 2WlmActivityManager(); //函数内部利用WSAStartup()启动了Windows套接字服务 3StartProvidingService(); //启动worklist管理服务位于wlmactmg.cc文件。函数内部调用ab两个函数。 aASC_initializeNetwork()函数 ASC_initializeNetwork函数调用了DICOM协议封装的TCP协议函数DUL_InitializeNetwork该函数内部就会出现我们在套接字编程中常见的socket、setsockopt、bind和listen函数 bWaitForAssociation();函数 WaitForAssociation函数调用了DICOM协议封装的TCP/IP协议函数receiveTransportConnectionTCP该函数内部利用的是select端口模式会出现套接字编程中常见的select、accept函数 4disconnectfromDataSource(); //断开连接的函数。1WSAStartup();//初始化套接字服务 2DcmFindSCU::initializeNetWork(); //函数内部调用的也是ASC_initializeNetwork函数。 3DcmFindSCU::performQuery(); //同样该函数内部封装了很多以DUL开头的协议操作函数。DUL是DICOM Upper Layer 的缩写。 4WSACleanup();从上述分析中我们基本可以看出worklist的通讯是建立在TCP/IP这一现有协议之上的可以说是对协议的二次封装。与我们平时进行套接字编程的基本流程相似搞清楚了这一点对于分析工具包、学习工具包的使用都会有很大的帮助。
worklist数据库文件或查询文件*.wl的生成
1问题提出 参照冷哥博文http://blog.csdn.net/pachleng/article/details/5800513、http://blog.csdn.net/pachleng/article/details/5827232中的说明我们可以很容易的利用DCMTK的工具包学习worklist操作。但是在实际应用模仿过程中有的人可能会好奇为什么要把wklist.wl和wklistqry.wl文件分别放在不同目录为什么同为以.wl为扩展名的文件一个就可以作为worklist的数据库文件放在服务端而另一个就是客户单的查询文件如果想发起自己的查询即C-FIND请求我们怎么手动生成wklistqry.wl文件 针对上述问题在dcmtk的论坛中也曾经有人提到过参见http://forum.dcmtk.org/viewtopic.php?f1t1475hilitwlmscpfs.exe%23p5016。利用UltraEdit工具将wklist.wl和wklistqry.wl文件同时打开对比如下 从上图中可以看出作为worklist客户端数据库文件的wklist.wl中是将患者真实信息以符合DICOM3.0标准字段的形式存储而客户端发起查询请求的wklistqry.wl文件内部只是简单的需要查询的空字段即各个字段的值域都为空。参考博文中给出了利用dump2dcm.exe工具包将.dump文件转换成wklistqry.wl文件dump可以认为是普通的文本文件可以利用记事本等工具进行直接编辑。如下图所示 例如作为测试用的wlist-2.dump文件中的患者ID为123456生成后的wlist-2.wl文件中的00100020字段也是123456。
2实际测试 从dump2dcm.exe工具包的说明我们就可以知道.wl文件其实就是dcm文件只是该类文件中并不存在真实的像素信息。通常只包含信息头部分主要指的是患者的各项信息。因此想利用dcmtk的库函数直接获取.wl文件其实就是手动构造dcm文件的过程。参见http://support.dcmtk.org/docs/mod_dcmdata.html#Examples中的第二个例子我们可以手动生成以“.wl”为后缀的dcm文件。具体的代码如下 #include stdio.h
#include tchar.h
#include dcmtk/config/osconfig.h
#include dcmtk/dcmdata/dctk.h
#include dcmtk/dcmdata/dcpxitem.h
#include dcmtk/dcmjpeg/djdecode.h
#include dcmtk/dcmjpeg/djencode.h
#include dcmtk/dcmjpeg/djcodece.h
#include dcmtk/dcmjpeg/djrplol.h
using namespace std;int main()
{char uid[100];DcmFileFormat fileformat;DcmDataset *dataset fileformat.getDataset();/************************************************利用下列语句可以生成worklist的数据库文件即*不含有影像信息的dcm文件*************************************************/dataset-putAndInsertString(DCM_SOPClassUID, UID_SecondaryCaptureImageStorage);dataset-putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));dataset-putAndInsertString(DCM_PatientName, Doe^John);OFCondition status fileformat.saveFile(D:\\DcmWorklist\\worklist\\test.wl, EXS_LittleEndianExplicit);if (status.bad())cerr Error: cannot write DICOM file ( status.text() ) endl;return 0;
} 既然服务端需要的worklist数据库文件和客户端需要的查询文件都是.wl文件唯一的区别就是一个有值域一个没有。因此利用上述代码我们既可以生成worklist数据库文件也可以生成worklist查询文件。
3测试结果 查看findscu.exe工具包我们可以找到-k选项也就是在提供了查询文件wklistqry.wl的同时也可以指定限定的查询字段例如
findscu 127.0.0.1 104 -v -k 0010,0020123456 -aec OFFIS wlistqry.wl 如果不添加-k 0010,0020“123456”限定选项查询结果如前文中重定向的结果相同而添加了限定字段后我们能够查询到的就只有数据库端中满足PatientID字段为123456的患者数据。具体结果如下 从中我们可以看到服务端给我们的反馈是PatientID为123456的患者信息所返回的信息都是wlistqry.wl文件中要求的字段其中通过-k 0010,0020“123456”限定项来限定了查询的结果在服务端的反馈是两个患者中表明有一个匹配的worklist数据库文件如上图中黄色区域所示。 猜想既然我们可以利用dcmtk自由生成客户端的.wl查询文件而-k 0010,0020”123456”就是对该查询文件的补充那么是不是如果我们直接把123456写入到wlistqry.wl中的0010,0020字段的值域而直接利用修改后的查询文件也会得到相同的结果呢此处利用自己的代码将0010,0020字段的值域填充为123456 #include stdio.h
#include tchar.h
#include dcmtk/config/osconfig.h
#include dcmtk/dcmdata/dctk.h
#include dcmtk/dcmdata/dcpxitem.h
#include dcmtk/dcmjpeg/djdecode.h
#include dcmtk/dcmjpeg/djencode.h
#include dcmtk/dcmjpeg/djcodece.h
#include dcmtk/dcmjpeg/djrplol.h
using namespace std;int main()
{char uid[100];DcmFileFormat fileformat;DcmDataset *dataset fileformat.getDataset();/***********************************************【猜测一】*利用下列语句可以生成worklist的查询文件*即* 各个字段数据都为空的dcm文件************************************************/dataset-putAndInsertString(DCM_SOPClassUID, UID_SecondaryCaptureImageStorage);dataset-putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));dataset-putAndInsertString(DCM_ImplementationVersionName,OFFIS_DCMTK_361);dataset-putAndInsertString(DCM_SpecificCharacterSet,);dataset-putAndInsertString(DCM_PatientName, );dataset-putAndInsertString(DCM_PatientID,123456);dataset-putAndInsertString(DCM_PatientBirthDate,);dataset-putAndInsertString(DCM_PatientSex,);OFCondition status fileformat.saveFile(D:\\DcmWorklist\\worklist\\testqry.wl, EXS_LittleEndianExplicit);if (status.bad())cerr Error: cannot write DICOM file ( status.text() ) endl;return 0;
} 然后利用
findscu 127.0.0.1 104 -v -aec OFFIS testqry.wl 指令直接发起查询。 查询结果反馈如下图所示 上图证明了我们的猜想通过写入wlistqry.wl的相关字段的值域就等同于在findscu.exe指令中添加-k XXXX,XXXX限定选项。 至此我们详细的介绍了如何模拟worklist的双端服务如何开启服务端服务、发起客户端查询关键是对如何利用dcmtk的库函数来生成自定义的查询端.wl文件进行了补充设实例测试。
完 作者zssure163.com 时间2014-08-23