本文主要描述,dicom通信的scu,scp的c-echo、c-store、c-find、c-move的使用。

DicomService
IDicomServiceProvider
IDicomCStoreProvider
IDicomCEchoProvider
IDicomCFindProvider
IDicomCMoveProvider
IDicomTransformRule

(1)c-echo

客户端代码:

  DicomClient client = new DicomClient();
client.AssociationAccepted += Client_AssociationAccepted;
client.AssociationRejected += Client_AssociationRejected;
client.AssociationReleased += Client_AssociationReleased;
client.NegotiateAsyncOps();
client.AddRequest(new DicomCEchoRequest()); //client.Send
client.SendAsync(ae_dest.ip,
ae_dest.port,
false,
ae_src.name,//SCU
ae_dest.name//ANY-SCP
);
 private void Client_AssociationReleased(object sender, EventArgs e)
{
//string log = $"Client_AssociationReleased --> {e}";
//AppendLog(log);
} private void Client_AssociationRejected(object sender, AssociationRejectedEventArgs e)
{
string log = $"Client_AssociationRejected --> {e}";
AppendLog("echo ng");
} private void Client_AssociationAccepted(object sender, AssociationAcceptedEventArgs e)
{
string log = $"Client_AssociationAccepted --> {e}";
AppendLog("echo ok");
}

(2)c-store

客户端代码:

 private void SendOne(Switch_Dicom_Image entity)
{
string fileReal = Path.Combine(AppSettings.dicom_path_root, entity.FilePath); var destServer = dao.GetOneDestSwitchAETitle(entity.SrcAETitle); string aet_current = AppSettings.scp_aet; string[] files = new string[] { fileReal }; int expected = files.Length;
var actual = ; var client = new DicomClient();
client.NegotiateAsyncOps(expected, ); foreach (string file in files)
{
try
{
Log($"正在发送文件“{file}”"); DicomCStoreRequest req = new DicomCStoreRequest(file);
req.OnResponseReceived = (req2, res) =>
{
try
{
Interlocked.Increment(ref actual); string log = $"OnResponseReceived --> 【{actual}】 {res.Status} {req2.SOPInstanceUID.UID}";
Log(log); if (res.Status == DicomStatus.Success)
{
using (var dbContext = new StudyProEntities())
{
var record = dbContext.Switch_Dicom_Image.Where(one => one.ImageGUID == entity.ImageGUID).FirstOrDefault();
record.SendStatus = ;
record.SendCount = record.SendCount + ;
record.SendTime = DateTime.Now;
int n = dbContext.SaveChanges();
if (n > )
{
//将接受目录下的文件给删除
File.Delete(file);
} }//end using }
else
{
using (var dbContext = new StudyProEntities())
{
var record = dbContext.Switch_Dicom_Image.Where(one => one.ImageGUID == entity.ImageGUID).FirstOrDefault();
record.SendStatus = ;
record.SendCount = record.SendCount + ;
record.SendError = $"{res.Status}";
int n = dbContext.SaveChanges();
if (n > )
{
//失败不能删除文件
} }//end using
}
}
catch (Exception ex)
{
LogHelper.Instance.Fatal(ex.ToString());
} }; client.AddRequest(req); //client.SendAsync(
client.Send(
destServer.IPAddress,
destServer.Port,
false,
aet_current,//SCU
destServer.AETitle,//ANY-SCP
timeout
);
}
catch (Exception ex)
{
LogHelper.Instance.Fatal(ex.ToString());
} }//end foreach
}

服务端代码:

  mActionLog?.Invoke("接收到待处理的 DicomCStoreRequest...");

             bool b = false;

             string pPatientID = "";
b = request.Dataset.TryGetValue<string>(DicomTag.PatientID, , out pPatientID);
if (!b)
{
throw new Exception("未能识别 PatientID");
}
mActionLog?.Invoke($"pPatientID={pPatientID}"); string pStudyInstanceUID = "";
b = request.Dataset.TryGetValue<string>(DicomTag.StudyInstanceUID, , out pStudyInstanceUID);
if (!b)
{
throw new Exception("未能识别 StudyInstanceUID");
}
mActionLog?.Invoke($"pStudyInstanceUID={pStudyInstanceUID}"); string pSeriesInstanceUID = "";
b = request.Dataset.TryGetValue<string>(DicomTag.SeriesInstanceUID, , out pSeriesInstanceUID);
if (!b)
{
throw new Exception("未能识别 SeriesInstanceUID");
}
mActionLog?.Invoke($"pSeriesInstanceUID={pSeriesInstanceUID}"); string pSOPInstanceUID = "";
b = request.Dataset.TryGetValue<string>(DicomTag.SOPInstanceUID, , out pSOPInstanceUID);
if (!b)
{
throw new Exception("未能识别 pSOPInstanceUID");
} mActionLog?.Invoke($"pSOPInstanceUID={pSOPInstanceUID}"); string file = ""; string pathLocalCache = App.gPathLocalCache;//Path.Combine(Application.StartupPath, "Cache"); string pathRelative = "";
//pathRelative = $"{pStudyInstanceUID}/{pSeriesInstanceUID}/{pSOPInstanceUID}.dcm";
pathRelative = $"{pStudyInstanceUID}/{pSOPInstanceUID}.dcm"; file = Path.Combine(pathLocalCache, pathRelative); var dir = Path.GetDirectoryName(file);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
} if (File.Exists(file))
{
File.Delete(file);
} request.File.Save(file);

(3)c-find

客户端代码:

         public List<DicomDataset> GetData(AEInfo ae, DicomCFindRequest dicomCFindRequest)
{
ManualResetEvent mre = new ManualResetEvent(false);
List<DicomDataset> list = new List<DicomDataset>(); dicomCFindRequest.OnResponseReceived =
(DicomCFindRequest request, DicomCFindResponse response) =>
{
Debug.WriteLine($"Status={response.Status}"); if (response.Status == DicomStatus.Success
|| response.Status == DicomStatus.ProcessingFailure)
{
mre.Set();
return;
} //输出值信息
response.ToString(true); if (response.HasDataset)
{ list.Add(response.Dataset);
}
}; //发起C-FIND-RQ,用A-ASSOCIATE服务建立DICOM实体双方之间的连接
var client = new DicomClient();
//client.NegotiateAsyncOps(); client.AddRequest(dicomCFindRequest); client.Send(host: ae.ip,//127.0.0.1
port: ae.port,
useTls: false,
callingAe: local_aet,//SCU-AE
calledAe: ae.name//SCP-AE
); bool b = mre.WaitOne( * );
if (!b)
{
MessageBox.Show("查询超时,请重试!");
return null;
} return list;

服务端代码:

 DicomStatus status = DicomStatus.Success;

             List<DicomCFindResponse> list = new List<DicomCFindResponse>();

             try
{
if (UserCustomCFindRequestHandle != null)
{
IList<DicomDataset> data = UserCustomCFindRequestHandle(request);
if (data != null)
{
LogHelper.Instance.Debug($"OnCFindRequest 结果的记录数为 {data.Count}"); foreach (var one in data)
{
DicomCFindResponse rsp = new DicomCFindResponse(request, DicomStatus.Pending);
rsp.Dataset = one;
list.Add(rsp);
}
}
else
{
status = DicomStatus.QueryRetrieveOutOfResources;
}
}
}
catch (Exception ex)
{
LogHelper.Instance.Error(ex.ToString());
list.Clear();
status = DicomStatus.ProcessingFailure;
} //DicomStatus.QueryRetrieveOutOfResources list.Add(new DicomCFindResponse(request, status));

(4)c-move

客户端代码:

  var requestCMove = new DicomCMoveRequest(ae_dest.name, studyInstanceUid, seriesInstanceUid, sopInstanceUid);

                 var id = requestCMove.MessageID;

                 requestCMove.OnResponseReceived = (DicomCMoveRequest request, DicomCMoveResponse response) =>
{
string log = $"OnResponseReceived --> {response.Status} | Completed={ response.Completed }, Remaining={ response.Remaining }, Failures={ response.Failures }, Warnings={ response.Warnings }";
this.AppendLog(log); //sopInstanceUID
if (response.Status == DicomStatus.Pending)
{
if(response.Dataset!=null)
{
string key = response.Dataset.GetString(DicomTag.SOPInstanceUID);
if (!string.IsNullOrEmpty(key))
{
mActionRun?.Invoke(key);
}
}
} }; var client = new DicomClient();
client.AddRequest(requestCMove); //client.Send
client.SendAsync(ae_dest.ip,
ae_dest.port,
false,
ae_src.name,//SCU-AE
ae_dest.name//SCP-AE
);

服务端代码:

  string aet_current = this.Association.CalledAE;
string aet_remote = this.Association.CallingAE; Sys_AETitle ae = UserCustomApplicationEntityTitleHandle(aet_remote); //AE Title 长度不能太长,这个是最长的长度,比如:“xxx_client_tool_”,最长16个字符。 LogHelper.Instance.Debug($"根据{aet_remote}查找到的ae --> {JsonConvert.SerializeObject(ae)}"); DicomStatus status = DicomStatus.Success;
IList<DicomCMoveResponse> listResponse = new List<DicomCMoveResponse>(); IList<CMoveReturnInfo> listFind; //DicomClient client = new DicomClient(); if (UserCustomCMoveRequestHandle != null)
{
listFind = UserCustomCMoveRequestHandle(request); if (listFind != null
&& listFind.Count > )
{
int len = listFind.Count; LogHelper.Instance.Debug($"cmove-cstore给客户端文件数为 {len}"); int nRemaining = len;
int nFailures = ;
int nWarnings = ;
int nCompleted = ; if (true)
{
DicomCMoveResponse responseCMove = new DicomCMoveResponse(request, DicomStatus.Pending);
responseCMove.Remaining = nRemaining;
responseCMove.Completed = nCompleted;
responseCMove.Warnings = nWarnings;
responseCMove.Failures = nFailures; base.SendResponseAsync(responseCMove);
//SendResponse(responseCMove);
}//end if foreach (var one in listFind)
{
try
{
string path = AppSettings.dicom_path_root; string file = Path.Combine(path, one.DomainID, one.StudyDateTime.Value.ToString("yyyyMMdd"), one.SysStudyGUID, one.SOPInstanceUID + ".dcm"); if (!File.Exists(file))
{
lock (_objLock)
{
nFailures++;
} throw new Exception($"文件不存在 {file}");
} DicomCStoreRequest dicomCStoreRequest = new DicomCStoreRequest(file);
//读取了dcm文件后,dicomCStoreRequest.Dataset的值将从file读取填充
//dicomCStoreRequest.Dataset.Add(DicomTag.XXX, XXX); dicomCStoreRequest.OnResponseReceived = (rq, rs) =>
{
LogHelper.Instance.Debug($"dicomCStoreRequest --> {rs.Status}"); if (rs.Status == DicomStatus.Success)
{
lock (_objLock)
{
nCompleted++; nRemaining = len - nFailures - nWarnings - nCompleted;
} //--------------------------------------------------------------------
if (true)
{
DicomCMoveResponse response = new DicomCMoveResponse(request, DicomStatus.Pending);
response.Remaining = nRemaining;
response.Completed = nCompleted;
response.Warnings = nWarnings;
response.Failures = nFailures; //将一些信息返回给客户端,作为客户端确认相关操作使用
response.Dataset = new DicomDataset();
response.Dataset.Add(DicomTag.SOPInstanceUID, one.SOPInstanceUID);
response.Dataset.Add(DicomTag.StudyInstanceUID, one.StudyInstanceUID);
response.Dataset.Add(DicomTag.SeriesInstanceUID, one.SeriesInstanceUID);
response.Dataset.Add(DicomTagVNA.CMoveServerFilePath, file); base.SendResponseAsync(response);
//SendResponse(rsponse);
}//end if
//--------------------------------------------------------------------
}
else
{
LogHelper.Instance.Debug($"cmove-cstore给客户端返回失败({rs.Status})");
} }; try
{
LogHelper.Instance.Debug($"发送文件 --> {file}"); DicomClient client = new DicomClient();
client.AddRequest(dicomCStoreRequest); client.Send(
ae.IPAddress,
ae.Port,
false,
aet_current,
aet_remote
);
}
catch (Exception ex)
{
LogHelper.Instance.Debug("cmove发送给客户端失败 --> " + ex.ToString());
throw ex;
} }
catch (Exception ex)
{
Debug.WriteLine(ex.ToString()); DicomCMoveResponse rs = new DicomCMoveResponse(request, DicomStatus.StorageStorageOutOfResources);
listResponse.Add(rs); return listResponse; }
finally
{ } }//end foreach listResponse.Add(new DicomCMoveResponse(request, DicomStatus.Success));
return listResponse; }
} listResponse.Add(new DicomCMoveResponse(request, DicomStatus.NoSuchObjectInstance));
return listResponse;

dicom 影像通信(scu、scp)的c-echo、c-store、c-find、c-move的更多相关文章

  1. C#开发PACS医学影像处理系统(六):加载Dicom影像

    对于一款软件的扩展性和维护性来说,上层业务逻辑和UI表现一定要自己开发才有控制权,否则项目上线之后容易被掣肘, 而底层图像处理,我们不需要重复造轮子,这里推荐使用fo-dicom,同样基于Dicom3 ...

  2. C#开发PACS医学影像处理系统(十五):Dicom影像交叉定位线算法

    1.定位线概念:某个方位的影像在另一个方向的影像上的投影相交线,例如横断面(从头到脚的方向)在矢状面(从左手到右手)上的影像投影面交线. 举个例子:右边的是MR(核磁共振)的某一帧切片,这是从头开始扫 ...

  3. C#开发PACS医学影像处理系统(十四):处理Dicom影像窗宽窗位

    概念解释(网络资料): 窗宽: 窗宽指CT图像所显示的CT 值范围.在此CT值范围内的组织结构按其密度高低从白到黑分为16 个灰阶以供观察对比.例如,窗宽选定为100 Hu ,则人眼可分辨的CT值为1 ...

  4. 大型三甲医院管理系统源码PACS超声科室源码DICOM影像工作站

    详情点击查看 开发环境 :VS2008 + C# + SQL2000 功能简介 1.患者登记工作站 集中登记患者基本信息和检查信息,包括就诊方式.患者来源.检查类型.检查部位.申请科室.申请医生等.可 ...

  5. C#开发PACS医学影像处理系统(十一):Dicom影像挂片协议

    通俗点说,挂片协议可以看作整个系统的一个相对复杂一点的配置文件,可以用JSON或XML格式来读取与保存, 另外,可以制作一个独立的exe配置程序来管理这些挂片协议. 假设配置了CT的挂片协议的右键菜单 ...

  6. C#开发PACS医学影像处理系统(十九):Dicom影像放大镜

    在XAML代码设计器中,添加canvas画布与圆形几何对象,利用VisualBrush笔刷来复制画面内容到指定容器: <Canvas x:Name="CvsGlass" Wi ...

  7. dicom通讯的工作方式及dicom标准简介

    本文主要讲述dicom标准及dicom通讯的工作方式.dicom全称医学数字图像与通讯 其实嘛就两个方面 那就是“存储”跟“通讯”. 文件数据组织方式  网络数据组织方式.文件数据组织方式就是解析静态 ...

  8. DICOM标准相关资料

    由于需要阅读影像,对DICOM需要先熟悉起来.关于DICOM,找了一些资料,可以学习.如下: DICOM标准:http://dicom.nema.org/standard.html 中文 DICOM ...

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

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

随机推荐

  1. 使用Typescript重构axios(八)——实现基础功能:处理响应data

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  2. Spring Boot 配置 - Consul 配置中心

    ▶ Spring Boot 依赖与配置 Maven 依赖 <dependencyManagement> <dependencies> <dependency> &l ...

  3. Linux学习(推荐学习资源)——保持更新

    1. 介绍 Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和Unix的多用户.多任务.支持多线程和多CPU的操作系统.它能运行主要的Unix工具软件.应用程序和网络协议. ...

  4. Java 8 Streams API 详解

    流式编程作为Java 8的亮点之一,是继Java 5之后对集合的再一次升级,可以说Java 8几大特性中,Streams API 是作为Java 函数式的主角来设计的,夸张的说,有了Streams A ...

  5. VM小技巧——虚拟机解决vm窗口太小的办法

    ——" 慢下来总结才能增大效率" 很多人在装虚拟机的时候,遇到了窗口过小不能自适应的问题.我也是查了好多资料,都说安装Vmware Tools即可解决,还有说修改分辨率也可以.两种 ...

  6. Docker 开篇 1 | 树莓派中搭建Docker

    官网三种安装方式,故不再赘述 大部分内容可参考官网链接:https://docs.docker.com/install/linux/docker-ce/debian/#install-docker-c ...

  7. jquery serialize()函数用法

    jquery serialize()函数用法<pre><html><head><script type="text/javascript" ...

  8. 深入理解计算机系统 第三章 程序的机器级表示 Part2 第二遍

    第一遍对应笔记链接 https://www.cnblogs.com/stone94/p/9943779.html 本章汇编代码中常出现的几个指令及其含义 1.push 操作数的个数:1 将操作数(一般 ...

  9. 最近的项目系列1——core整合SPA

    1.前言 当前,前后端分离大行其道,我本人之前不少项目也是纯前后端分离,但总有些场景,春前后端分离整起来比较痛苦,比如我手头这个公众号项目吧,它涉及到第三方鉴权,第三方凭证,以及微信凭证这些,都不适合 ...

  10. 架构设计:"4+1"视图

    概念 "4+1"视图,是指从5个不同视角来描述软件体系结构. "4+1"分别指: 逻辑视图 过程视图 物理视图 开发视图 场景/用例 视图 逻辑架构的描述可以围 ...