DICOM:C-GET服务
来源:互联网 发布:移动网络运营岗位 编辑:程序博客网 时间:2024/05/16 02:07
背景:
之前博文对比过多次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-SCU客户端的核心代码如下:
#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函数中服务端是可以明确定位客户端请求的数据的,但是在接收到客户端C-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:C-GET与C-MOVE对比剖析
- DICOM:C-GET与C-MOVE对比剖析(续)
- DICOM:C-GET与C-MOVE对比剖析
- DICOM:试玩Docker发布C-STORE SCP服务
- DICOM:开源DICOM服务框架DCM4CHE 安装
- DICOM:开源DICOM服务框架DCM4CHE 构建
- DICOM:DICOM Print 服务详细介绍
- DICOM:docker实现DICOM服务虚拟化
- DICOM:dcmqrscp.exe与storescu.exe中C-STORE服务的差别
- DICOM:开源DICOM服务框架DCM4CHE构建的准备
- DICOM:docker实现DICOM服务虚拟化之“数据卷”
- DICOM
- DICOM
- DICOM
- DICOM
- DICOM:基于DCMTK实现C-FIND SCU
- dicom c-store pdu包解析
- MongoDB3.0+数据操作工具封装
- [cuda]常量内存
- C#_Socket的TCP使用
- DNS记录类型
- [ios版本]AR 现实增强之高通Vuforia
- DICOM:C-GET服务
- Android代码中使用的设计模式思想
- 使用RecyclerView.adapter.notifyDataSetchang()不能更新数据
- ES6个人学习整理(四)——扩展
- 数据结构和算法经典100题-第24题
- ios图片加载
- 基于hadoop1.0集群环境搭建
- hadoop的Linux环境准备
- Bootstrap 笔记