DICOM医学图形处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求(续)

背景:

上一篇博文中,在对storescp工具源文件storescp.cc和DcmSCP类的源文件scp.cc进行剖析后,得出了两者都可以实现响应C-ECHO和C-STORE(需要对DcmSCP类进行扩展)请求的功能。但是在对DcmSCP类进行扩展,期望模拟实现自己的storescp.exe工具时遇到了问题,客户端提示服务中断链接,而服务端显示保存失败,如下图所示。此次博文通过排除该问题再一次对storescp.cc和scp.cc进行对比,主要从Presentation Context、AbstractSyntax、TransferSyntax等细节出发,认真学习DICOM通讯服务。

问题排除:

1)对比分析storescp.exe工具包与自定义工具包的调试信息

为了排除storescu.exe客户端的问题,大致确定问题出现的范围,决定再一次用storescp.exe作为客户端,使用storescu.exe对其发送C-STORE请求,查看storescu.exe客户端的调试信息。与我们的自定义工具服务端的调试信息进行对比。

对比上图中的调试信息,其中红色部分END A-ASSOCIATE-AC表示的是服务端已经顺利的与客户端完成了握手,即网络已经顺利响应了客户端的连接。蓝色部分表示客户端发送的C-STORE-RQ请求也已经顺利的到达了服务端,唯一不同的就是黄色圆圈标记的部分,说明在服务端接收到C-STORE-RQ请求后对其处理有问题。而我们自己封装的ZSDcmStoreSCP类对于C-STORE-RQ的处理函数是直接拷贝的storescp.exe工具内的storeSCP和storeSCPCallback函数。因此大致可以确定问题可能出现的范围。

2)单步调试

在handleIncomingCommand函数内部调用storeSCP指令处插入断点,进入单步调试状态。利用VS2012提供的新的调试工具“并行堆栈”,找到了第一个返回状态cond.bad()为true的地方,即函数ASC_findAcceptedPresentationContext,如下图所示:

继续单步调试,进入到ASC_findAcceptedPresentationContext函数内部,发现真正出现错误的地方是findPresentationContextID,该函数的参数presentationContextID始终为零。

猜测:可能是presentationContextID参数在传递过程中的某一环节出错了,导致程序失败。为了验证我们的想法,打开storescp.exe工程,进入调试模式,查看参数的数值情况,如下图所示,storescp.exe工具包中presentationContextID参数的值为41。因此证明了我们在presentationContextID参数传递的过程中发生了错误。

3)参数传递流向

为了确定具体传递过程中的错误环节,我们采取回溯的方式,通过回溯findPresentationContextID函数中presentationContextID参数的来源来确定问题出现的具体位置。

(注:为了方便讲图片旋转了90度,劳烦大家歪着脖子将就着看一下下吧哈)

从上图中可以看出T_ASC_PresentationContextID参数最终的来源是我们重载DcmSCP基类的handleIncomingCommand函数,因此与storescp.exe中的是实现对比,仔细查看该部分的代码,发现了一个重大问题,原来我们在将storescp.exe工具中的processCommands函数内容拆解到handleIncomingCommand函数内部时,将多余的DIMSE_receiveCommand函数注释掉的同时,遗漏了注释掉T_ASC_Association局部变量,使得原本应该通过DIMSE_receiveCommand函数获取的presID变量一直被局部变量覆盖为0——至此问题找到了。

那么由于注释掉了多余的DIMSE_receiveCommand函数,我们从何处来获取presID参数呢。查看一下handleIncomingCommand函数的头发现函数参数中也没有出现T_ASC_PresentationContextID类型的参数。但是对比scp.cc原本处理C-ECHO请求的函数handleECHORequest我们发现,scp.cc类中将presID的值存储在了DcmPresentationContextInfo类型的presInfo变量中。因此我们修改storeSCP的调用,将presInfo.presentationContextID传递进入作为presID的初值。

修改完成后,再次调试发现程序运行正常,客户端顺利收到了服务端反馈回来的DIMSE Message,并且在服务端的目录下也看到了storescu.exe传递过来的dcm文件(当然在storeSCP函数内部根据时间等信息对文件进行了重命名)。

至此上一博文中自模拟storescp.exe工具包的问题已经顺利解决,利用我们自己封装的ZSDcmStoreSCP类可以简单地实现storescp.exe工具包的功能。

总结分析:

此次调试排除错误的过程中发现,在组合移接不同文件的代码时要格外的注意细节部分。顺便借着此次传递丢失T_ASC_Association类型参数的问题,我们详细的分析一下传递失败的T_ASC_Associaton类型参数的作用,为什么简单的一个参数传递失败,就会导致C-STORE功能失效?

1)Presentation Context、AbstractSyntax、TransferSyntax细节学习

在上一篇博文中我们摘录了DICOM3.0标准中关于Presentation Context、AbstractSyntax、TransferSyntax几个名词的解释,通俗一点来讲就是说Presentation Context代表的是两个应用实体(AE=Applicaiton Entity)交互的环境(通常叫做上下文),它包含后面的AbstractSyntax和TransferSyntax名词。AbstractSyntax与TransferSyntax比较容易混淆就是因为英文单词中都包含了Syntax,其实两者完全是不同的概念,AbstractSyntax关注的是上层信息,即两个交互的AE之间
进行的是何种交互,也就是标准中所说的服务对象对(SOP)的类型,通过查看dcuid.h文件可知,AbstractSyntax有Store、Query/Retrive、Worklist等类型,例如Store类型的UID_CTImageStorage 、Query/Retrive类型的UID_FINDPatientRootQueryRetrieveInformationModel、Worklist类型的UID_FINDModalityWorklistInformationModel等等。这几个是我们在C-ECHO、C-STORE、C-FIND服务中常用到的几种;而TransferSyntax关注的是信息传输交互时的编码规则,是Explicit还是Implicit,是LittleEndian还是BigEndian,常见的有UID_LittleEndianImplicitTransferSyntax、UID_LittleEndianExplicitTransferSyntax等等,具体的可以自行查看dcuid.h文件。而上面传递丢失的presID参数就是包括了这两种最重要的交互信息。因此会导致整个C-STORE服务失败。

2)storescp.cc与scp.cc整体再对比

那么storescp.exe工具包和DcmScp类又是在什么时刻?什么位置来设置这些参数的呢?下面我们就再一次对比分析一下两种工具的实现流程(细节可参照上一篇博文DICOM医学图像处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求)。

2.1 scp.cc源码文件

借助上一篇博文的图片来分析一下DcmSCP类中对于Presentation Context(即AbstractSyntax和TransferSyntax)的具体操作。

上图中给出了DcmSCP类中设置Presentation Context上下文环境的具体位置和使用的函数。

2.2 storescp.exe源码文件

想必storescp.exe工具包中的设置流程也基本是相似的,接下来我们同样借助上一篇博文的图来分析一下。

对比两个图片我们可以发现DcmSCP和storescp.exe都是在处理真正的DIMSE消息之前对连接的Presentation Context上下文进行配置,至于这么多UID(无论是AbstractSyntax还是TransferSyntax都是在dcuid.h文件中用统一的UID来定义的)存储到哪里了呢?结合上一篇博文的分析,我们知道外部循环开始后主要的核心函数都有一个T_ASC_Association类型的参数assoc(storescp.exe中)或m_assoc(DcmSCP类中),查看一下T_ASC_Association类型的定义,并逐级打开,可以发现正如我们预料中的一样,该变量中存储了连接的上下文的所有UID,如下图所示:

至此我们对于DcmSCP类和storescp.exe工具的源码的剖析总算告一段落了,希望大家能够对DICOM的网络通讯服务有一个更深刻的认识。

3)对新版DCMTK中DcmSCP和DcmStoreSCP实现的初步猜想

通过此次使用DcmSCP类的工程发现,该类的设计有些欠妥,开放的接口不够合理,无法轻松的实现扩展来响应C-STORE等其他请求。猜想新版的DCMTK肯定会进行修改,通过查看dcmtk3.6.1的官方说明,发现新版的dcmtk开源库中的确对DcmSCP和DcmSCU基类进行了大的修改,开放了众多新的接口(如下图)方便用户进行扩展,另外新版的库中直接给出了封装好的DcmStoreSCP和DcmStoreSCU类,所以大家就不要使用我给出的代码ZSDcmStoreSCP类啦,仅供测试使用,具体运用是还是赶紧安装最新版的dcmtk开源库吧。

实例工程代码下载:

1)CSDN资源下载:

连接:http://download.csdn.net/detail/zssureqh/7870789,需要1个积分奥。

2)Github免费下载

连接:https://github.com/zssure-thu/CSDN

后续专栏博文预告:

1)dcmtk3.6.0的DcmSCP与dcmtk3.6.1的DcmSCP分析,以及dcmtk3.6.1的DcmStoreSCP和DcmStoreSCU的使用

2)Dcmtk与fo-dicom保存文件的不同设计模式:单线程VS多线程

3)wlmscpfs.exe与findscu.exe的源码剖析:学习C-FIND请求

作者:[email protected]

时间:2014-09-13

时间: 2024-12-14 13:42:17

DICOM医学图形处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求(续)的相关文章

DICOM医学图像处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求

背景: 上一篇专栏博文中针对PACS终端(或设备终端,如CT设备)与RIS系统之间worklist查询进行了介绍,并着重对比分析了DICOM3.0中各部分对DICOM网络通讯服务的定义.此次通过结合早些时间的博文DICOM医学图像处理:基于DCMTK工具包学习和分析worklist,对DCMTK开源库中提供的storescp.exe和storescu.exe工具的源码进行剖析,从底层深入了解C-STORE服务的触发及响应. 分析思路: storescp.exe和storescu.exe分别充当着

DICOM:dcmqrscp.exe与storescu.exe中C-STORE服务的差别

背景: 专栏中曾分别写过dcmqrscp.exe与storescu.exe工具包的介绍,但是并未深究两者之间的差别.dcmqrscp.exe工具是一个mini版PACS,可分别响应C-FIND.C-MOVE.C-GET.C-STORE等各种DIMSE服务,而storescu.exe工具是C-STORE服务使用者,可以看出这两个工具包都提供C-STORE SCU服务.前几天博友的一个问题使得重新研究了一下两者在实现C-STORE SCU服务上的差别.通过源码分析发现问题根源出在Presentati

DICOM医学图像处理:DCMTK的wiki资料学习之PACS调试

背景: 前段时间着重从dcmtk和fo-dicom(mDCM)源码角度进行剖析,期望加深对DICOM协议的理解.知其然,知其所以然.如果"所以然"很不好懂,那我们还是先多多"知其然"吧.搞清楚原理的目的不也是为了更好的运用于实践么?所以理论和实践应该彼此交错进行,理论搞不动了就搞搞应用,应用久了就钻研钻研理论. 以前上DCMTK官网仅仅是浏览关于开源库中各个类的设计模式.依赖关系.最近在打开DCMTK官网的wiki时,才发现OFFIS对DCMTK的介绍是如此的详细.

DICOM医学图像处理:DICOM存储操作之 “多幅JPG图像数据存入DCM文件”

背景: 续上篇,继续介绍如何将多幅JPG图像数据存入DCM文件.即将有损压缩数据直接写入DCM文件,存储为Multi-frame形式. 多幅JPG图像数据存入DCM文件: 为了避免引起歧义,这里着重说明一下.本博文的描述的场景是:假设我们手中有多张JPG文件,想把JPG文件写入DCM文件,即单个DCM文件包含多幅图像信息的Multi-Frame形式.该问题之前与CSDN博友y317215133y也讨论过,当时我在OFFIS论坛中找到了一个帖子直接给了y317215133y答复.今天重新梳理了一下

DICOM医学图像处理:Dcmtk与fo-dicom保存文件的不同设计模式之“同步VS异步”+“单线程VS多线程”

一.背景: 最近一直在做DCM相关的编程工作,以前项目使用C++居多,所以使用DCMTK开源库,而目前团队使用C#居多,所以需要转向使用fo-dicom库,由于前一篇专栏文章DICOM医学图像处理:利用fo-dicom发送C-Find查询Worklist在调试过程中需要对DIMSE信息进行手动保存,偶然间发现了dcmtk开源库与fo-dicom开源库在保存dcm文件时使用的方式差异很大,因此决定研究一下,期望通过对比分析来看一下孰优孰劣. 二.dcmtk与fo-dicom保存文件函数的源码剖析:

DICOM:DCMTK工具包分析之dcmqrscp.exe、dcmqridx.exe、dcmqrti.exe

背景: 新的一年开始,专栏会坚持下去,继续与大家相互交流学习.本篇博文再一次分析DCMTK自带的工具包dcmqrscp.exe(类似于一个单机版miniPACS),此次主要关注dcmqrscp.exe的数据库部分,通过使用dcmqridx.exe手动操作dcmqrscp.exe的数据库文件,直观了解数据库的记录内容:然后利用dcmqrti.exe查询dcmqrscp.exe的数据库文件,学习如何操作dcmqrscp.exe数据库. 单机版数据库: 单机版数据库就是只能运行在单机上,不提供网络功能

DICOM医学图像处理:fo-dicom网络传输之 C-Echo and C-Store

背景: 上一篇博文对DICOM中的网络传输进行了介绍.主要參照DCMTK Wiki中的英文原文.通过对照DCMTK与fo-dicom两个开源库对DICOM标准的详细实现,对理解DICOM标准有一个更直观的认识.此篇博文是对上一篇博文的补充.由于专栏前面的演示样例大多是利用DCMTK工具包来进行的,此次借着分析fo-dicom源代码结构的机会,參照fo-dicom的README.md,给出C-ECHO 和C-STORE服务的详细实现.在实现的同一时候给出DICOM3.0标准中的相关介绍,帮助我们理

DICOM医学图像处理:Deconstructed PACS之Orthanc

背景: 此篇博文介绍一个开源的.基于WEB的DICOM Server软件.该开源软件完全使用C++编写,不依赖于第三方数据库(内置了SQLite数据库)或其他框架,支持RESTful API设计模式.官网上提供了源代码,同时也给出了编译后的Windows和Linux系统的二进制安装包.Orthanc是PACS领域的一种改革,提出了"解构PACS概念",即Deconstructed PACS,用户可以通过三种方式访问Orthanc:DICOM Server.Web Server和REST

DICOM医学图像处理:全面分析DICOM3.0标准中的通讯服务模块

背景: 最近在做关于PACS终端与RIS系统之间进行worklist查询的相关调试工作,因此又重新对DICOM3.0标准中关于网络传输的部分进行了阅读,在此将本周的工作进行一下总结,以加深对DICOM3.0标准的认识,从底层更加清晰的了解worklist查询.C-STORE.C-FIND等各种服务. 要点: 1)名词简称 该部分中会出现很多的常见名词的缩写,因此为了更好的理解其含义,先给出各个名词所对应的全称,这里没有用中文进行翻译原因有二,其一是因为英文很简单,而且表意很清楚,其二是因为目前D