zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    智能手表如何救人一命?
    人工智能、机器学习和认知计算入门指南
    PO VO BO DTO POJO DAO的解释
    web UI框架推荐
    面向切面编程AOP
    阿里巴巴java开发规范
    如何理解Spring IOC
    HTML5 3D旋转图片相册
    JSON总结笔记
    轮播图---可以动态添加图片,(封装成一个函数)
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/7228270.html
Copyright © 2011-2022 走看看