本文主要描述,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. Spring Cloud gateway 六 Sentinel nacos存储动态刷新

    微服务当前这么火爆的程度,如果不能学会一种微服务框架技术.怎么能升职加薪,增加简历的筹码?spring cloud 和 Dubbo 需要单独学习.说没有时间?没有精力?要学俩个框架?而Spring C ...

  2. ubuntu开机自启动服务

    ubuntu下一个用来管理开机自启动服务的程序,今天在ss vps上安装时老是提示这个错误,百度后,下面的这个方法可行: vi /etc/apt/source.list 输入i,进入Insert模式 ...

  3. 在小程序中使用md5

    使用md5.js的首先你要有md5.js这个文件https://github.com/emn178/js-md5 您也可以使用Bower安装js-md5. bower install md5 对于no ...

  4. SqlServer2005 查询 第四讲 in

    今天我们来说sql中的命令参数in in --in用于查询某个字段的指定的值的记录信息 注意一下:--对或(or)取反是并且(and),对并且(and)取反是或(or 数据库中不等于表示有两种:!= ...

  5. 微擎修改 icon.jpg 后项目主页未变

    微擎修改 icon.jpg 后项目主页Logo未变 产生原因: 设置了自定义图标,但系统未找到该图标,就选择使用默认的起始图标 解决办法: 在项目根目录位置上传一个图标名为 icon-custom.j ...

  6. nyoj 276-比较字母大小 (顺序比较, 逆序输出)

    276-比较字母大小 内存限制:64MB 时间限制:3000ms 特判: No 通过数:13 提交数:15 难度:1 题目描述: 任意给出两个英文字母,比较它们的大小,规定26个英文字母A,B,C.. ...

  7. 图文详解基于角色的权限控制模型RBAC

    我们开发一个系统,必然面临权限控制的问题,即不同的用户具有不同的访问.操作.数据权限.形成理论的权限控制模型有:自主访问控制(DAC: Discretionary Access Control).强制 ...

  8. 【2018寒假集训Day 1】【位运算】生成字符串

    生成字符串(strs) [问题描述] 假设字符串只由字符“0”,“1”,“”组成,其中字符“”表示该字符可由 字符“0”或“1”替代. 现有一些字符串,根据这些字符串生成所有可生成的字符串.如: {1 ...

  9. php基础文档

    目录 PHP简介 PHP概述和名词解释 PHP常见数据类型 PHP运算符 PHP流程控制语句 PHP函数 PHP类与对象 PHP会话session与缓存cookie(扩展) 1.PHP简介 PHP,即 ...

  10. 分布式远程调用SpringCloud-Feign的两种具体操作方式(精华)

    一 前言 几大RPC框架介绍 1.支持多语言的RPC框架,google的gRPC,Apache(facebook)的Thrift 2.只支持特定语言的RPC框架,例如新浪的Motan 3.支持服务治理 ...