背景:
该系列博文同属于DICOM协议中的“网络传输”部分,前两篇系列文章分别介绍了DCMTK和fo-dicom开源库对DICOM标准的具体实现(http://blog.csdn.net/zssureqh/article/details/41016091),以及给出了fo-dicom库对C-ECHO 和C-STORE的简单实现(http://blog.csdn.net/zssureqh/article/details/41250973)。此篇博文是对前一篇的补充,同样采用分析DICOM3.0标准的方式,给出fo-dicom库对C-FIND和C-MOVE的实现示例。
DIMSE协议与ASSOCIATE协议:
DICOM3.0协议第7部分的第8章对两种协议进行了详细介绍:
DIMSE协议:
DIMSE制定了构建消息的流程和编码规则,用于在两个DICOM服务使用者(例如,两个DICOM实体)之间传输请求和响应指令。流程(Procedures)规定了请求和响应指令消息的传输规则,用于解释指令消息中的众多字段(fields)。但是并没有规定请求发起方和执行方如何来对消息进行处理。DIMSE协议指出消息(Messages)可能会被分段(fragmented)利用P-DATA服务在两个DICOM服务使用者之间传输。
ASSOCIATE协议:
连接(Association)的建立包含两个DICOM服务使用者。一个被称为连接请求方(requester),一个被叫做连接接收方(acceptor);双方使用A-ASSOCIATE服务来建立连接。在A-ASSOCIATE服务中,双方所需的参数被称为“应用上下文(Application Context)”,其中给出了两端DICOM应用实体连接建立的相关规则。(在第7部分的附录A和附录D中有详细的介绍)
大致了解了网络传输所需的协议后,我们开始介绍C-FIND和C-STORE服务的具体实现。
C-FIND的fo-dicom实现:
1)C-FIND参数说明:
C-FIND是一项确认服务(confirmed Service),用于匹配对方一系列复合SOP实例的各项属性。该服务指令需要的参数如下:
其中用于匹配对方一系列复合SOP实例属性的值用Identifier来给出。简单来说,在请求方消息(C-FIND-RQ)中Identifier包含了需要查询的各个属性,而在响应方消息(C-FIND-RSP)中,Identifier是返回的查询结果。注意:在发送查询返回结果时Status一直处于Pending状态;当查询结果发送完成后,最后一个C-FIND-RSP消息中Status为Success,且该消息并不包含任何查询结果。
具体的C-FIND-RQ和C-FIND-RSP的编码格式如下所示,除此以外关于C-FIND的相关请求还有其他,例如C-CANCEL-FIND-RQ等。(关于DICOM协议的阅读方法可参照本系列之前的文章http://blog.csdn.net/zssureqh/article/details/41250973):
2)C-FIND代码示例:
下面直接给出C-FIND SCU和C-FIND SCP的代码,其中包含相关的注释,所以就不详细介绍了。
C-FIND SCU在fo-dicom官方的README.md中给出了C-FIND SCU的代码,如下,
CFIND SCU:
namespace CFINDScu
{
class Program
{
static void Main(string[] args)
{
//构造要发送的C-FIND-RQ消息,如果查看DicomCFindRequest类的话
//可以看到其定义与DICOM3.0标准第7部分第9章中规定的编码格式一致
//在构造Study级别的查询时,我们的参数patientID会被填充到消息的Indentifier部分,用来在SCP方进行匹配查询
var cfind = DicomCFindRequest.CreateStudyQuery(patientId: "12345");
//当接收到对方发挥的响应消息时,进行相应的操作【注】:该操作在DICOM3.0协议
//第7部分第8章中有说明,DIMSE协议并未对其做出规定,而应该有用户自己设定
cfind.OnResponseReceived = (rq, rsp) =>
{
//此处我们只是简单的将查询到的结果输出到屏幕
Console.WriteLine("PatientAge:{0} PatientName:{1}", rsp.Dataset.Get<string>(DicomTag.PatientAge), rsp.Dataset.Get<string>(DicomTag.PatientName));
};
//发起C-FIND-RQ:
//该部分就是利用A-ASSOCIATE服务来建立DICOM实体双方之间的连接。
var client = new DicomClient();
client.AddRequest(cfind);
client.Send(host:"127.0.0.1",port: 12345,useTls: false,callingAe: "SCU-AE",calledAe: "SCP-AE");
Console.ReadLine();
}
}
}
【注】:代码中只列出了主要的函数,完整代码参见后面给出的工程连接。
C-FIND SCP:
//DICOM3.0协议第7部分第8章中DIMSE协议并未规定请求方和实现方如何来进行具体操作
//此处定义的DcmCFindCallback代理由用户自己来实现接收到C-FIND-RQ后的操作
public delegate IList<DicomDataset> DcmCFindCallback(DicomCFindRequest request);
//要想提供C-FIND SCP服务,需要继承DicomService类&#