dicom 影像通信(scu、scp)的c-echo、c-store、c-find、c-move
本文主要描述,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的更多相关文章
- C#开发PACS医学影像处理系统(六):加载Dicom影像
		对于一款软件的扩展性和维护性来说,上层业务逻辑和UI表现一定要自己开发才有控制权,否则项目上线之后容易被掣肘, 而底层图像处理,我们不需要重复造轮子,这里推荐使用fo-dicom,同样基于Dicom3 ... 
- C#开发PACS医学影像处理系统(十五):Dicom影像交叉定位线算法
		1.定位线概念:某个方位的影像在另一个方向的影像上的投影相交线,例如横断面(从头到脚的方向)在矢状面(从左手到右手)上的影像投影面交线. 举个例子:右边的是MR(核磁共振)的某一帧切片,这是从头开始扫 ... 
- C#开发PACS医学影像处理系统(十四):处理Dicom影像窗宽窗位
		概念解释(网络资料): 窗宽: 窗宽指CT图像所显示的CT 值范围.在此CT值范围内的组织结构按其密度高低从白到黑分为16 个灰阶以供观察对比.例如,窗宽选定为100 Hu ,则人眼可分辨的CT值为1 ... 
- 大型三甲医院管理系统源码PACS超声科室源码DICOM影像工作站
		详情点击查看 开发环境 :VS2008 + C# + SQL2000 功能简介 1.患者登记工作站 集中登记患者基本信息和检查信息,包括就诊方式.患者来源.检查类型.检查部位.申请科室.申请医生等.可 ... 
- C#开发PACS医学影像处理系统(十一):Dicom影像挂片协议
		通俗点说,挂片协议可以看作整个系统的一个相对复杂一点的配置文件,可以用JSON或XML格式来读取与保存, 另外,可以制作一个独立的exe配置程序来管理这些挂片协议. 假设配置了CT的挂片协议的右键菜单 ... 
- C#开发PACS医学影像处理系统(十九):Dicom影像放大镜
		在XAML代码设计器中,添加canvas画布与圆形几何对象,利用VisualBrush笔刷来复制画面内容到指定容器: <Canvas x:Name="CvsGlass" Wi ... 
- dicom通讯的工作方式及dicom标准简介
		本文主要讲述dicom标准及dicom通讯的工作方式.dicom全称医学数字图像与通讯 其实嘛就两个方面 那就是“存储”跟“通讯”. 文件数据组织方式 网络数据组织方式.文件数据组织方式就是解析静态 ... 
- DICOM标准相关资料
		由于需要阅读影像,对DICOM需要先熟悉起来.关于DICOM,找了一些资料,可以学习.如下: DICOM标准:http://dicom.nema.org/standard.html 中文 DICOM ... 
- DICOM:DICOM3.0网络通信协议(续)
		转载:http://blog.csdn.net/zssureqh/article/details/44278693 题记: 近一年来一直坚持周末写博客,整理工作和闲暇之余的点点滴滴.对于新知识点.新技 ... 
随机推荐
- 入门Android底层需要的一些技能
			<Android的设计与实现> Android框架层<Linux系统编程手册> Linux系统编程<Android内核剖析> 编译框架和romC语言和Linux内核 ... 
- php Yaf_Loader::import引入文件报错的解决方法
			php Yaf_Loader::import引入文件报错的解决方法 改下配置文件就行<pre>yaf.use_spl_autoload=1</pre> 也可以PHP动态修改 毕 ... 
- windsServer2008设置定时重启
			点击“开始”——“管理工具”——“任务计划程序”. 右键“任务计划程序库”——“创建任务”. 输入计划名称.描述和安全选项(如下图). 点击触发器,点击“新建…”,输入计划周期和时间(如下图):无误后 ... 
- python面向对象<三>
			类属性.实例属性: class Tool(object): #属性(类属性)类对象(Tool) num = 0 #方法 def __init__(self,new_name): self.name = ... 
- 快速搭建Jenkins集群
			关于Jenkins集群 在Jenkins上同时执行多个任务时,单机性能可能达到瓶颈,使用Jenkins集群可以有效的解决此问题,让多台机器同时处理这些任务可以将压力分散,对单机版Jenkins的单点故 ... 
- Servlet中response的相关案例(重定型,验证码,ServletContext文件下载)
			重定向 首先设置状态码,设置响应头 //访问Demo1自动跳转至Demo2 //设置状态码 response.setStatus(302); //设置响应头 response.setHeader(&q ... 
- [UWP]使用Win2D的BorderEffect实现图片的平铺功能
			1. WPF有,而UWP没有的图片平铺功能 在WPF中只要将ImageSource的TileMode属性设置为Tile即可实现图片的平铺,具体可见WPF的这些文档: ImageBrush 类 (Sys ... 
- 对于 TCP 三次握手的理解
			假设名叫 A 和 B 的两个人要进行通信,那么他们两人之间,首先要确保通信顺畅. 而确保通信顺畅,就要从 3 个维度,确定 8 个能力 3 个维度分别是: 1.人知道(A 知道.B 知道) 2.人(A ... 
- 【原创】使用批处理脚本自动生成并上传NuGet包
			Hello 大家好,我是TANZAME,我们又见面了. NuGet 是什么这里就不再重复啰嗦,园子里一搜一大把.今天要跟大家分享的是,在日常开发过程中如何统一管理我们的包,如何通过批处理脚本生成包并自 ... 
- nyoj 67-三角形面积 (海伦公式, 叉积)
			67-三角形面积 内存限制:64MB 时间限制:3000ms 特判: No 通过数:8 提交数:13 难度:2 题目描述: 给你三个点,表示一个三角形的三个顶点,现你的任务是求出该三角形的面积 输入描 ... 
