引言
??前篇介紹了 DICOM C-Store 消息服務(wù),本文結(jié)合開源 DICOM 庫 fo-dicom 詳細(xì)介紹一下 C-Find 服務(wù)香椎。
C-Find 消息服務(wù)
??C-Find 服務(wù)是一個(gè)查詢服務(wù)涛碑,用于一個(gè) DIMSE-service-user 在同等的DIMSE-service-user 上查詢復(fù)合 SOP 實(shí)例的屬性滿足查詢條件給出的一組屬性的復(fù)合 SOP 實(shí)例朴爬。簡(jiǎn)單點(diǎn)理解就是我們可以使用 C-Find 服務(wù)查詢 PACS 系統(tǒng)里面符合條件的影像丰歌,我們的查詢條件可以是單個(gè)屬性(例如:PatientID 患者 ID 或 Modality 設(shè)備類型等)荒澡,也可以是多個(gè)屬性的組合(例如:PatientID 患者 ID和 StudyInstanceUID 檢查唯一標(biāo)識(shí)等)报辱。在實(shí)際的場(chǎng)景中,worklist 會(huì)用到 C-Find 服務(wù)单山,成像設(shè)備向 PACS 系統(tǒng)發(fā)起 C-Find 請(qǐng)求碍现,然后 PACS 系統(tǒng)請(qǐng)求 RIS 系統(tǒng)獲取登記待檢查的患者列表,然后逐層返回給成像設(shè)備米奸。
??C-Find 服務(wù)流程如下:
C-Find SCU
??同 C-Store SCU 一樣昼接,使用開源庫 fo-dicom 我們可以很輕松的實(shí)現(xiàn) C-Find SCU,fo-dicom 已經(jīng)封裝好了 C-Find Request躏升,具體代碼可以在 GitHub 上查看 DicomCFindRequest.cs辩棒,我們只需要如下的代碼就可以實(shí)現(xiàn) C-Find SCU:
這里需要引用命名空間【Dicom】和【Dicom.Network】
using Dicom;
using Dicom.Network;
var client = new DicomClient();
client.NegotiateAsyncOps();
var request = {構(gòu)造一個(gè) DicomCFindRequest};
// 聲明返回對(duì)象
var studyUids = new List<string>();
request.OnResponseReceived += (req, response) =>
{
DebugStudyResponse(response);
// response.Dataset,這里獲取返回對(duì)象膨疏,先判斷 response.Dataset 是否為空一睁,然后從中獲取需要的返回信息,例如下面獲取 StudyInstanceUID
studyUids.Add(response.Dataset?.GetSingleValue<string>(DicomTag.StudyInstanceUID));
};
client.AddRequest(request);
client.Send({C-Find SCP IP}, {C-Find SCP Port}, false, {C-Find SCU AE Title}, {C-Find SCP AE Title});
構(gòu)造 DicomCFindRequest 參考代碼
// 查詢哪一級(jí)佃却,患者者吁、檢查、序列或影像等
var request = new DicomCFindRequest(DicomQueryRetrieveLevel.Study);
// 編碼
request.Dataset.AddOrUpdate(new DicomTag(0x8, 0x5), "ISO_IR 100");
// 查詢條件饲帅,可以根據(jù)需要添加
request.Dataset.AddOrUpdate(DicomTag.PatientName, "xxx");
request.Dataset.AddOrUpdate(DicomTag.PatientID, "xxx");
request.Dataset.AddOrUpdate(DicomTag.ModalitiesInStudy, "xxx");
request.Dataset.AddOrUpdate(DicomTag.StudyDate, "xxx");
request.Dataset.AddOrUpdate(DicomTag.StudyInstanceUID, "xxx");
request.Dataset.AddOrUpdate(DicomTag.StudyDescription, "xxx");
request.Dataset.AddOrUpdate(DicomTag.StudyTime, "xxx");
…………
C-Find SCP
??C-Find SCP 可以通過派生 DicomService 服務(wù)類來實(shí)現(xiàn) Dicom 服務(wù)的基本框架复凳,然后實(shí)現(xiàn) IDicomServiceProvider 和 IDicomCFindProvider 接口來實(shí)現(xiàn)瘤泪。具體代碼可以參考這里。
C-Find 過程分析
??我分別在兩臺(tái)計(jì)算機(jī)上部署好 C-Find SCU(10.3.13.202) 和 C-Find SCP(10.3.2.209)育八,然后從 C-Find SCU 向 C-Find SCP 發(fā)起一個(gè) C-Find 請(qǐng)求对途,并且使用 wireshark 將整個(gè)過程的數(shù)據(jù)包抓取下來,下圖就是抓取的完整 C-Find 請(qǐng)求過程的數(shù)據(jù)包髓棋。
??上圖紅色方框內(nèi)的三條數(shù)據(jù)包就是 TCP 建立連接的過程:
- C-Find SCU(10.3.13.202)通過端口55074向 C-Find SCP(10.3.2.209) 的3333端口發(fā)送 SYN 包請(qǐng)求連接实檀;
- C-Find SCP(10.3.2.209) 通過3333端口向 C-Find SCU(10.3.13.202)的55074端口發(fā)送 ACK+SYN 包進(jìn)行確認(rèn);
- C-Find SCU(10.3.13.202)通過端口55074向 C-Find SCP(10.3.2.209) 的3333端口發(fā)送 ACK 包確認(rèn)并建立連接按声;
??TCP連接已經(jīng)通了膳犹,緊接著藍(lán)色框內(nèi)的兩行是兩個(gè) AE 建立 association 的過程:
- C-Find SCU(10.3.13.202)向 C-Find SCP(10.3.2.209) 發(fā)送 A-ASSOCIATE 請(qǐng)求;
- C-Find SCP(10.3.2.209)響應(yīng) C-Find SCU(10.3.13.202)的 A-ASSOCIATE 請(qǐng)求签则,然后兩個(gè) AE 就建立了一個(gè) association须床;
??association 建立好了之后,接著就是第一個(gè)黃色框內(nèi)的內(nèi)容渐裂,C-Find 請(qǐng)求交互的過程:
- C-Find SCU(10.3.13.202)向 C-Find SCP(10.3.2.209) 發(fā)送 C-Find 請(qǐng)求豺旬,展開 DICOM 協(xié)議層可以看到請(qǐng)求數(shù)據(jù)是 PatientName,另外編碼格式是 ISO_IR 100芯义;
- C-Find SCP(10.3.2.209)返回給 C-Find SCU(10.3.13.202)關(guān)于 C-Find 請(qǐng)求結(jié)果哈垢;
??association 然后第二個(gè)黃色框內(nèi)的內(nèi)容還是 C-Find SCP(10.3.2.209)給 C-Find SCU(10.3.13.202)返回 C-Find 請(qǐng)求,這里是告訴 C-Find SCU 查詢結(jié)果已經(jīng)發(fā)送完畢扛拨;
??接下來藍(lán)色框內(nèi)的兩行是通過發(fā)送 A-RELEASE 請(qǐng)求釋放 association:
- C-Find SCU(10.3.13.202)向 C-Find SCP(10.3.2.209) 發(fā)送 A-RELEASE 請(qǐng)求斷開 association;
- C-Find SCP(10.3.2.209)響應(yīng) C-Find SCU(10.3.13.202)的 A-RELEASE 請(qǐng)求举塔,然后斷開兩個(gè) AE 之間的 association绑警;
??最后就是斷開 TCP 連接,這里就不再多介紹了央渣。