DICOM:C-GET服务
背景:
之前博文对照过多次C-MOVE与C-GET服务的差别,两者最大的差别在于C-GET是基于单个TCP连接的点对点的双方服务。而C-MOVE是基于两个TCP连接的三方服务(详情參见:《DICOM:C-GET与C-MOVE对照剖析》。以及DICOM:C-GET与C-MOVE对照剖析(续))。
加之前一篇专栏博文DICOM:DICOM3.0网络通信协议之“开源库实现剖析”也已具体对照了dcm4che和fo-dicom开源库的底层实现,因此本篇博文直接给出基于fo-dicom开源库的C-GET服务实现的主要代码,着重介绍C-GET服务端与C-MOVE服务端发起C-STORE 子操作的差别。
C-GET-SCU:
在fo-dicom开源库中DICOM的各种Client端已经抽象出了DicomClientBase类,针对各种DIMSE-C服务(诸如C-STORE、C-GET、C-MOVE、C-ECHO、C-FIND)唯一不同的就是绑定各自相应的托付就可以。
C-GET-SCUclient的核心代码例如以下:
#region Protected Overrides
protected override void OnConnected()
{
DcmAssociate associate = new DcmAssociate();
byte pcid = associate.AddPresentationContext(_getSopClass);
associate.AddTransferSyntax(pcid, DicomTransferSyntax.ExplicitVRLittleEndian);
associate.AddTransferSyntax(pcid, DicomTransferSyntax.ImplicitVRLittleEndian);
byte pcid2 = associate.AddPresentationContext(DicomUID.CTImageStorage);
associate.AddTransferSyntax(pcid2, DicomTransferSyntax.ExplicitVRLittleEndian);
associate.AddTransferSyntax(pcid2, DicomTransferSyntax.ImplicitVRLittleEndian);
associate.CalledAE = CalledAE;
associate.CallingAE = CallingAE;
associate.MaximumPduLength = MaxPduSize;
//zssure:2015/07/06
//Add UserIdentity Information
//http://medical.nema.org/medical/dicom/current/output/html/part07.html#sect_D.3.3.7
if (userIdentity == null)
SendAssociateRequest(associate);
else
SendAssociateRequest(associate, userIdentity);
//zssure:end,2015/07/06
}
private void PerformQueryOrRelease()
{
if (_getQueries.Count > 0)
{
byte pcid = Associate.FindAbstractSyntax(GetSopClassUID);
if (Associate.GetPresentationContextResult(pcid) == DcmPresContextResult.Accept)
{
current = _getQueries.Dequeue();
SendCGetRequest(pcid,1,Priority,current.ToDataset());
}
else
{
SendReleaseRequest();
}
}
else
{
SendReleaseRequest();
}
}
protected override void OnReceiveCStoreRequest(byte presentationID, ushort messageID, DicomUID affectedInstance,
DcmPriority priority, string moveAE, ushort moveMessageID, DcmDataset dataset, string fileName)
{
try
{
if (OnCStoreRequest != null)
OnCStoreRequest(presentationID, messageID, affectedInstance, priority, moveAE, moveMessageID, dataset, fileName);
SendCStoreResponse(presentationID, messageID, affectedInstance, DcmStatus.Success);
}
catch (System.Exception ex)
{
SendCStoreResponse(presentationID, messageID, affectedInstance, DcmStatus.ProcessingFailure);
}
Console.WriteLine("c-get c-store RQ!");
}
protected override void OnReceiveAssociateAccept(DcmAssociate association)
{
PerformQueryOrRelease();
}
protected override void OnReceiveCGetResponse(byte presentationID, ushort messageID, DcmDataset dataset,
DcmStatus status, ushort remain, ushort complete, ushort warning, ushort failure)
{
if (OnCGetResponse != null)
{
OnCGetResponse(current, dataset, status, remain, complete, warning, failure);
}
if (remain == 0 && status != DcmStatus.Pending)
{
PerformQueryOrRelease();
}
}
【注意】:这里须要注意的有几点:
1)CGETClient端须要响应服务端发起的C-STORE-RQ。因此须要重写OnReceiveCStoreRequest函数;
2)之前在博文DICOM:參考dcm4che2扩展fo-dicom(mDCM)中的UserIdentity字段已经介绍过扩展Association加入UserIdentity字段
C-GET-SCP:
C-GET服务端差别于C-MOVE服务端在于,DicomService服务类自身须要实现OnReceiveCStoreResponse函数,而之前C-MOVE服务端是在发送C-STORE-RQ时直接绑定OnReceiveCStoreResponse事件到CStoreClient。核心代码例如以下:
private ConcurrentDictionary<ushort, CGetParameters> cgetProcessDic = new ConcurrentDictionary<ushort, CGetParameters>();
protected override void OnReceiveCStoreResponse(byte presentationID, ushort messageIdRespondedTo, DicomUID affectedInstance, DcmStatus status)
{
CGetParameters cgetPara = null;
if (status == DcmStatus.Success)
{
try
{
cgetProcessDic.TryGetValue(messageIdRespondedTo, out cgetPara);
cgetPara.CGetStatus.Complete++;
cgetPara.CGetStatus.Remain--;
SendCGetResponse(presentationID, messageIdRespondedTo, DcmStatus.Pending, cgetPara.CGetStatus.Remain, cgetPara.CGetStatus.Complete, cgetPara.CGetStatus.Warning, cgetPara.CGetStatus.Fail);
if (cgetPara.CGetStatus.Remain > 0)
{
///self do something
}
else
{
cgetPara.CGetStatus.Fail++;
cgetPara.CGetStatus.Remain--;
SendCGetResponse(presentationID, messageIdRespondedTo, DcmStatus.Pending, cgetPara.CGetStatus.Remain, cgetPara.CGetStatus.Complete, cgetPara.CGetStatus.Warning, cgetPara.CGetStatus.Fail);
}
}
else if (cgetPara.CGetStatus.Remain == 0)
{
if (cgetProcessDic.TryRemove(messageIdRespondedTo, out cgetPara))
SendCGetResponse(presentationID, messageIdRespondedTo, DcmStatus.Success, cgetPara.CGetStatus.Remain, cgetPara.CGetStatus.Complete, cgetPara.CGetStatus.Warning, cgetPara.CGetStatus.Fail);
else
{
Log.Info("ReceiveCStoreResponse for CGet failed when remove from ConcurrentDictionary<ushort, CGetParameters>");
try
{
cgetProcessDic.TryRemove(messageIdRespondedTo, out cgetPara);
}
catch (System.Exception ex2)
{
Log.Info("ReceiveCStoreResponse for CGet failed when remove from ConcurrentDictionary<ushort, CGetParameters> again,{0},{1}", ex2.Message, ex2.StackTrace);
}
}
}
}
catch (System.Exception ex)
{
Log.Info("ReceiveCStoreResponse for CGet failed! {0},{1}", ex.Message, ex.StackTrace);
SendCGetResponse(presentationID, messageIdRespondedTo, DcmStatus.InvalidArgumentValue, cgetPara.CGetStatus.Complete, cgetPara.CGetStatus.Remain, cgetPara.CGetStatus.Warning, cgetPara.CGetStatus.Fail);
}
}
else
{
cgetPara.CGetStatus.Fail++;
cgetPara.CGetStatus.Remain--;
SendCGetResponse(presentationID, messageIdRespondedTo, DcmStatus.Pending, cgetPara.CGetStatus.Remain, cgetPara.CGetStatus.Complete, cgetPara.CGetStatus.Warning, cgetPara.CGetStatus.Fail);
}
}
【注意】:上述代码须要注意是:
通过线程安全集合类ConcurrentDictionary在C-GET与C-STORE两种服务间同步状态,由于在OnReceiveCGetRequest函数中服务端是能够明白定位client请求的数据的,可是在接收到clientC-STORE-RSP时,通过简单的DICOM Message是无法得知之前在OnReceiveCGetRequest中定位的数据的,因此须要在服务类中加入一个线程安全集合类来共享状态。如是可见,上述代码中大量的操作是在维护ConcurrentDictionary的状态,用于协调C-STORE与C-MOVE在同一个TCP连接中消息的传递。
备注:
这里纠正之前博文DICOM:C-GET与C-MOVE对照剖析中对于C-GET服务的C-STORE和C-MOVE消息流的流程错误。例如以下图所看到的:
作者:zssure@163.com
时间:2015-12-16
DICOM:C-GET服务的更多相关文章
- DICOM的Worklist服务
看 DICOM 标准有一段时间了,前面几篇也介绍了一下 DIMSE-C 消息服务,具体参看Dicom 学习笔记-Dicom 消息服务(DIMSE-C/DIMSE-N),本文就介绍一下 DICOM 标准 ...
- DICOM医学图像处理:Orthanc Plugin SDK实现WADO服务
背景: Orthanc是博主发现的一个很完美的DICOM和HTTP服务端开源软件,前几篇分别介绍了Orthanc的基本使用.Orthanc从0.8.0版本之后给出了Plugin SDK,通过该SDK可 ...
- DICOM医学图像处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求
转载:http://blog.csdn.net/zssureqh/article/details/39213817 背景: 上一篇专栏博文中针对PACS终端(或设备终端,如CT设备)与RIS系统之间w ...
- DICOM:DICOM3.0网络通信协议(续)
转载:http://blog.csdn.net/zssureqh/article/details/44278693 题记: 近一年来一直坚持周末写博客,整理工作和闲暇之余的点点滴滴.对于新知识点.新技 ...
- DICOM:DICOM3.0网络通信协议
转载:http://blog.csdn.net/zssureqh/article/details/41016091 背景: 专栏取名为DICOM医学图像处理原因是:博主是从医学图像处理算法研究时开始接 ...
- DICOM:C-GET与C-MOVE对照剖析
背景: 之前专栏中介绍最多的两款PACS各自是基于dcmtk的dcmqrscp以及Orthanc.和基于fo-dicom的DicomService(自己开发的).该类应用场景都是针对于局域网,因此在使 ...
- DICOM:DICOM3.0网络通信协议(延续)
题记: 在过去的一年中一直坚持周末博客,整理工作与休闲比的点点滴滴. 新知识点.新技术的涉猎会单独成文,对于与DICOM相关的知识统一放在了DICOM医学图像处理 专栏里,事实上DICOM英文全称是D ...
- DICOM简介
背景: DICOM分为两大类(这里只是从DICOM相关从业者日常工作角度出发来分类的):1)DICOM医学图像处理,即DCM文件中具体数据的处理,说图像可能有些狭隘,广义上还包括波形(心电).视频(超 ...
- DICOM:docker实现DICOM服务虚拟化
背景: docker,是一个开源的应用容器引擎,眼下大多应用在部署和运维领域,然而因为全然使用沙箱机制,相互之间能够看做独立的主机,且自身对资源的需求也十分有限.远远低于虚拟机.甚至非常多时候.能够直 ...
随机推荐
- centos7 安装LNMP(php7)之php7.0 yum安装
http://www.jianshu.com/p/35f21210668a 安装过程参考上面的网址
- 常见java异常
1. java.lang.NullPointerException(空指针异常) 调用了未经初始化的对象或者是不存在的对象 经常出现在创建图片,调用数组这些操作中,比如图片未经初始化,或者图片创建时 ...
- Python 把u'\xca\xd3\xc6\xb5\xd7\xa5\xc8\xa1' 输出正常中文
今天碰见从数据库读取出来数据是u'\xca\xd3\xc6\xb5\xd7\xa5\xc8\xa1',输出显示乱码,经常查询处理如下: 两种方式: 1. s = u'\xca\xd3\xc6\xb5\ ...
- DTD 和 Schema简介
什么是DTD? DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块. 它使用一系列的合法元素来定义文档结构. DTD例子 <?xml version="1.0"? ...
- [Tools] Deploy a Monorepo to Now V2
Now by Zeit has recently been updated and now supports multi-language monorepos. In this lesson we'l ...
- Zynq Fatfs文件系统应用笔记
Zynq Fatfs文件系统应用笔 Hello,panda 笔记介绍基于所描写叙述的Zynq Fatfs基于Xilinx xilffsv3.0和Sdpsv2.4,文件系统採用在Bare-Metal和轻 ...
- 在HTML页面中实现一个简单的Tab
参考:http://blog.sina.com.cn/s/blog_6cccb1630100m23i.html HTML页面代码如下: <!DOCTYPE html PUBLIC "- ...
- linux生成指定大小的文件(转)
# dd if=/dev/zero of=50M.file bs=1M count=50在当前目录下生成一个50M的文件 虚拟块设备文件更通用的名称是硬盘镜像文件(Hard Disk Image),但 ...
- Jmeter3.0-插件管理
本文转自推酷:http://www.tuicool.com/articles/UV7fI3V JMeter ,老牌,开源,轻量,Apache基金会的顶级项目,光是这些关键字就足以让大量用户将其纳入自己 ...
- Java学习从入门到精通(2) [转载]
Java Learning Path(二).书籍篇 学习一门新的知识,不可能指望只看一本,或者两本书就能够完全掌握.需要有一个循序渐进的阅读过程.我推荐Oreilly出版的Java系列书籍. 在这里我 ...