背景:

之前博文对照过多次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服务的更多相关文章

  1. DICOM的Worklist服务

    看 DICOM 标准有一段时间了,前面几篇也介绍了一下 DIMSE-C 消息服务,具体参看Dicom 学习笔记-Dicom 消息服务(DIMSE-C/DIMSE-N),本文就介绍一下 DICOM 标准 ...

  2. DICOM医学图像处理:Orthanc Plugin SDK实现WADO服务

    背景: Orthanc是博主发现的一个很完美的DICOM和HTTP服务端开源软件,前几篇分别介绍了Orthanc的基本使用.Orthanc从0.8.0版本之后给出了Plugin SDK,通过该SDK可 ...

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

    转载:http://blog.csdn.net/zssureqh/article/details/39213817 背景: 上一篇专栏博文中针对PACS终端(或设备终端,如CT设备)与RIS系统之间w ...

  4. DICOM:DICOM3.0网络通信协议(续)

    转载:http://blog.csdn.net/zssureqh/article/details/44278693 题记: 近一年来一直坚持周末写博客,整理工作和闲暇之余的点点滴滴.对于新知识点.新技 ...

  5. DICOM:DICOM3.0网络通信协议

    转载:http://blog.csdn.net/zssureqh/article/details/41016091 背景: 专栏取名为DICOM医学图像处理原因是:博主是从医学图像处理算法研究时开始接 ...

  6. DICOM:C-GET与C-MOVE对照剖析

    背景: 之前专栏中介绍最多的两款PACS各自是基于dcmtk的dcmqrscp以及Orthanc.和基于fo-dicom的DicomService(自己开发的).该类应用场景都是针对于局域网,因此在使 ...

  7. DICOM:DICOM3.0网络通信协议(延续)

    题记: 在过去的一年中一直坚持周末博客,整理工作与休闲比的点点滴滴. 新知识点.新技术的涉猎会单独成文,对于与DICOM相关的知识统一放在了DICOM医学图像处理 专栏里,事实上DICOM英文全称是D ...

  8. DICOM简介

    背景: DICOM分为两大类(这里只是从DICOM相关从业者日常工作角度出发来分类的):1)DICOM医学图像处理,即DCM文件中具体数据的处理,说图像可能有些狭隘,广义上还包括波形(心电).视频(超 ...

  9. DICOM:docker实现DICOM服务虚拟化

    背景: docker,是一个开源的应用容器引擎,眼下大多应用在部署和运维领域,然而因为全然使用沙箱机制,相互之间能够看做独立的主机,且自身对资源的需求也十分有限.远远低于虚拟机.甚至非常多时候.能够直 ...

随机推荐

  1. 在eclipse上部署openfire 3.9.1源码,並配置openfire

    参考文章:https://my.oschina.net/u/1409622/blog/205603 在网上找了很久部署openfire源码的文章,由于我使用的是最新的3.9.1源码,试了很多种部署方法 ...

  2. apache的proxy代理总访问后端web的第一个虚拟主机

    先查看cat /usr/local/apache2/modules 时候有mod_proxy.so  mod_proxy_http.so mod_proxy_connect 如果没有,使用apache ...

  3. Console-算法:fun1(do while)

    ylbtech-Arithmetic:Console-算法[do while]-XX   1.A,Demo(案例) 1.B,Solution(解决方案) using System; namespace ...

  4. TCP/IP网络编程

    https://blog.csdn.net/a987073381/article/details/52206215     TCP的传输连接分为3个阶段:连接建立(三次握手).数据传送和连接释放(四次 ...

  5. Python中将打印输出日志文件

    一. 利用sys.stdout将print行导向到你定义的日志文件中,例如: import sys # make a copy of original stdout route stdout_back ...

  6. linux字符设备文件的打开操作

    2.7  字符设备文件的打开操作(1) 作为例子,这里假定前面对应于/dev/demodev设备节点的驱动程序在自己的代码里实现了如下的struct file_operations对象fops: st ...

  7. 安装ansj分词器

    项目地址:https://github.com/4onni/elasticsearch-analysis-ansj https://github.com/laigood/elasticsearch-a ...

  8. vue-router 响应路由参数的变化

    提醒一下,当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用.因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效.不过,这也意味着组件的生命周 ...

  9. 用二十秒记住几个PHP基础知识点

    数组: 索引数组:数组的键是整数的数组,从0開始. 关联数组:数组的键是字符串的数组 //索引数组 $arr=array('I','love','you'); //关联数组 $arr0=array(' ...

  10. 【BIEE】11_BIEE图形报表在谷歌浏览器64.0.3282.140中访问图例乱码解决

    如上图,使用谷歌浏览器访问BIEE图形报表的时候,标题.图例等涉及到中文的地方全部乱码了!但是用IE打开是不会乱码的,这是由于:谷歌需要设置编码格式 但是55版本以后,谷歌公司为了加快浏览器的速度,提 ...