背景:

之前博文对照过多次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. vue中自定义指令vue.direvtive,自定义过滤器vue.filter(),vue过渡transition

    自定义指令 默认设置的核心指令( v-model,v-bind,v-for,v-if,v-on等 ),Vue 也允许注册自定义指令.注意,在 Vue2.0 里面,代码复用的主要形式和抽象是组件——然而 ...

  2. squid 三种代理实验

    squid 软件既可以做代理,也可以做实现缓存加速,大大降低服务器的I/O.. 1.其中squid代理分为三种,正向代理.透明代理.反向代理. (1)squid正向代理和squid透明代理都位客户端: ...

  3. 【共享单车】—— React后台管理系统开发手记:项目工程化开发

    前言:以下内容基于React全家桶+AntD实战课程的学习实践过程记录.最终成果github地址:https://github.com/66Web/react-antd-manager,欢迎star. ...

  4. IOS URL无法对加号进行编码导致http请求时服务器端获取的内容中加号变成空格问题

    一.背景. 将以下地址请求服务器时,如果postUrl中某个参数值包含符号+  那么在服务器获取到这个参数值时,其加号变成了一个空格. NSString *postUrl = "http地址 ...

  5. icvEvalCARTHaarClassifier

    /* *icvEvalCARTHaarClassifier *作用:通过计算haar特征值,来分配非叶子节点直到出现叶子节点,最后返回输出值val.  */ float icvEvalCARTHaar ...

  6. 应用程序之UIWebView的使用

    UIWebView简介 知识点总结 代码实现 一.UIWebView简介 1.是iOS内置的浏览器控件,可以浏览网页.打开文档等2.能够加载html/htm.pdf.docx.txt等格式的文件3.系 ...

  7. webserver【实时查询当天的天气情况】

    1.webserver是什么? 日常生活中经常会使用到webserver,注册时,会收到验证码,购买东西时,会收到短信,假如,A公司网站和B公司合作,那么A公司注册对的用户可以直接推送给B网站,那怎么 ...

  8. 两个页面相同js方法兼容

    1. a.js页面 //Js获取Url参数 function request(paras) { var url = location.href; var paraString = url.substr ...

  9. mac 下 pycharm 快捷键

    用过快捷键立即感觉高大上了,最主要的是很方便啊!很强大 cmd b 跳转到声明处(cmd加鼠标) opt + 空格 显示符号代码 (esc退出窗口 回车进入代码) cmd []光标之前/后的位置 op ...

  10. java.long中的类和方法

    java.lang.Character.isUpperCase(char ch) 确定指定的字符是否为大写字符. java.lang.Character.toUpperCase()方法用法转换从Uni ...