轉(zhuǎn)自:
dicom網(wǎng)絡(luò)通訊入門(3) - assassinx - 博客園
接下來可以進(jìn)行消息傳遞了 ,也就是dimse ,再來復(fù)習(xí)下 什么是dimse 。n-set? n-create c-echo 這些都是dimse? 他們都是屬于一種結(jié)構(gòu)的pdu 那就是tf-pdu(傳輸數(shù)據(jù)和命令的都稱之為tf-pdu 或者transfer pdu?,協(xié)商連接的都稱之為associcate pdu) 。dimse 由 許多tag組成,就像文件解析那篇博文一樣。
tf-pdu數(shù)據(jù)結(jié)構(gòu)分析如下:
如果你又要問此圖是怎么來的 ,dicom標(biāo)準(zhǔn)第八章 33頁。你可能又要問 dimse又是什么 ,dimse全稱 dicom 消息服務(wù)元素DIMSE(DICOM Message Service Element)
為什么你就說echo屬于dimse 。請(qǐng)看dicom標(biāo)準(zhǔn)第七章 的目錄: 9???? DIMSE-C??????? 9.1.5????? C-ECHO SERVICE 。不用我多說了噻。
在這之前還是像以前一樣先把tf-pdu的數(shù)據(jù)結(jié)構(gòu)跟序列化搞了吧:
1 struct PDVset2 { 3 //0x04 P_DATA-TF4 public byte pduType;5 //pdu長度6 public uint pduLen;7 //pdv長度從作用來看他跟上面有所重復(fù) pdv, presentation data value8 public uint itemLen;9 //這個(gè)contextID其實(shí)是指協(xié)商連接時(shí)的presentation context id 10 //最好判斷下是否跟協(xié)商時(shí)的一致11 public byte contextID;12 //消息控制頭 確定pdv類型是command 還是data 發(fā)送完成與否13 public byte msgControlHeader;14 15 public SortedDictionary<uint, DataElement> elements;16 17 public byte[] serial()18 {19 if ((pduLen != 0 && itemLen != 0) == false)20 return null;21 //header22 MemoryStream _stream = new MemoryStream((int)pduLen + 6);23 WarpedStream stream = new WarpedStream(_stream);24 25 stream.writeByte(0x04);26 stream.skip_write(1);27 stream.writeUint(pduLen);28 stream.writeUint(itemLen);29 stream.writeByte(contextID);30 stream.writeByte(msgControlHeader);31 //items32 foreach (DataElement item in elements.Values)33 {34 stream.writeBytes(item.serial());35 }36 37 _stream.Flush();38 39 byte[] data = new byte[_stream.Length];40 Array.Copy(_stream.GetBuffer(), data, _stream.Length);41 stream.close();42 _stream.Close();43 return data;44 }45 }46 enum pdvType47 {48 command, data, commandAndData49 }50 51 struct DataElement52 {53 public uint _tag;54 public WarpedStream.byteOrder bytOrder;55 public bool explicitVR;//是顯式VR的 否則隱式VR56 public uint tag57 {58 get { return _tag; }59 set60 {61 _tag = value;62 VR = VRs.GetVR(value);63 uint _len = VRs.getLen(VR);64 if (_len != 0)65 len = _len;66 }67 }68 public ushort VR;69 //雖然長度為uint 但要看情況隱式時(shí)都是4字節(jié) 顯式時(shí)除ow那幾個(gè)外都是2字節(jié)70 //如果為ow 顯示不但長度為4 在之前還要跳過2字節(jié),除ow那幾個(gè)之外不用跳過71 public uint len;72 public byte[] value;73 public IList<DataElement> items ;//子項(xiàng)74 public bool haveItems;//是否包含子項(xiàng)75 76 //值的顯示77 public string showValue()78 {79 if (haveItems )80 return null;81 82 if (value != null)83 return Tags.VFdecoding(VR, value, bytOrder);84 else85 return null;86 }87 //賦值88 public void setValue(string valStr)89 {90 if (haveItems )91 return;92 93 if (VRs.IsStringValue(VR)) {94 len = (uint)valStr.Length;95 value = Tags.VFencoding(VR, valStr, bytOrder, len);96 }97 else if (len != 0)//就是這個(gè)破地方 因?yàn)閑lement的連續(xù)使用 導(dǎo)致會(huì)截?cái)嘧址?8 value = Tags.VFencoding(VR, valStr, bytOrder, len);99 else
100 {
101 value = Tags.VFencoding(VR, valStr, bytOrder);
102 if (VRs.IsStringValue(VR))
103 len = (uint)value.Length;
104 }
105 }
106 //序列化
107 public byte[] serial()
108 {
109 MemoryStream data_serial = new MemoryStream();
110 serial(this, data_serial);
111 byte[] data = new byte[data_serial.Length];
112 Array.Copy(data_serial.GetBuffer(), data, data.Length);
113 data_serial.Close();
114 return data;
115 }
116 //序列化的遞歸調(diào)用
117 public void serial(DataElement element, MemoryStream data_serial)
118 {
119 //int len_serial = element.getSerialLen();
120 //if ((VR == VRs.SQ && len_serial == UInt32.MaxValue) || (tag == 0xfffee000 && len == UInt32.MaxValue))//靠 遇到文件夾開始標(biāo)簽了
121 if (element.haveItems )
122 {
123 //開始標(biāo)記
124 data_serial.WriteByte((byte)((element._tag & 0x00ff0000) >> 16));
125 data_serial.WriteByte((byte)((element._tag & 0xff000000) >> 24));
126 data_serial.WriteByte((byte)(element._tag & 0x000000ff));
127 data_serial.WriteByte((byte)((element._tag & 0x0000ff00) >> 8));
128 data_serial.WriteByte(0xff); data_serial.WriteByte(0xff); data_serial.WriteByte(0xff); data_serial.WriteByte(0xff);
129
130 foreach (DataElement item in element.items)
131 {
132 item.serial(item, data_serial);
133 }
134
135 //結(jié)束標(biāo)記
136 if (element.VR == VRs.SQ)
137 {
138 data_serial.WriteByte((byte)((0xfffee0dd & 0x00ff0000) >> 16));
139 data_serial.WriteByte((byte)((0xfffee0dd & 0xff000000) >> 24));
140 data_serial.WriteByte((byte)(0xfffee0dd & 0x000000ff));
141 data_serial.WriteByte((byte)((0xfffee0dd & 0x0000ff00) >> 8));
142 }
143 else
144 {
145 data_serial.WriteByte((byte)((0xfffee00d & 0x00ff0000) >> 16));
146 data_serial.WriteByte((byte)((0xfffee00d & 0xff000000) >> 24));
147 data_serial.WriteByte((byte)(0xfffee00d & 0x000000ff));
148 data_serial.WriteByte((byte)((0xfffee00d & 0x0000ff00) >> 8));
149 }
150 data_serial.WriteByte(0x00); data_serial.WriteByte(0x00); data_serial.WriteByte(0x00); data_serial.WriteByte(0x00);
151 }
152 else
153 {
154 byte[] data = new byte[element.getSerialLen()];
155 uint _len = element.len;
156 if (_len % 2 != 0)
157 _len++;
158 if (element.VR == VRs.SQ)
159 _len = 0xffffffff;
160
161 data[0] = (byte)((element._tag & 0x00ff0000) >> 16);
162 data[1] = (byte)((element._tag & 0xff000000) >> 24);
163 data[2] = (byte)(element._tag & 0x000000ff);
164 data[3] = (byte)((element._tag & 0x0000ff00) >> 8);
165
166 if (element.explicitVR)//顯示VR
167 {
168 data[4] = 0x00;
169 data[5] = 0x00;
170 if (VRs.IsLengthField16Bit(VR))
171 {
172 if (element.bytOrder == WarpedStream.byteOrder.bigEdition)
173 {
174 data[6] = (byte)(_len & 0x000000ff);
175 data[7] = (byte)((_len & 0x0000ff00) >> 8);
176 }
177 else
178 {
179 data[6] = (byte)((_len & 0x0000ff00) >> 8);
180 data[7] = (byte)(_len & 0x000000ff);
181 }
182
183 for (int i = 0; i < element.value.Length; i++)
184 data[8 + i] = element.value[i];
185 }
186 else
187 {
188 if (element.bytOrder == WarpedStream.byteOrder.bigEdition)
189 {
190 data[6] = (byte)((_len & 0xff000000) >> 24);
191 data[7] = (byte)((_len & 0x00ff0000) >> 16);
192 data[8] = (byte)((_len & 0x0000ff00) >> 8);
193 data[9] = (byte)(_len & 0x000000ff);
194
195 }
196 else
197 {
198 data[6] = (byte)(_len & 0x000000ff);
199 data[7] = (byte)((_len & 0x0000ff00) >> 8);
200 data[8] = (byte)((_len & 0x00ff0000) >> 16);
201 data[9] = (byte)((_len & 0xff000000) >> 24);
202 }
203 if (element.value == null)
204 throw new Exception(string.Format("異常:tag{0} value未賦值 ", Tags.ToHexString(element.tag)));
205
206 for (int i = 0; i < element.value.Length; i++)
207 data[10 + i] = element.value[i];
208 }
209 //len_ser = (int)(4 + 2 + 4 + len);
210 //len_ser = (int)(4 + 2 + len);
211 }
212 else //隱式Vr
213 {
214 if (element.bytOrder == WarpedStream.byteOrder.bigEdition)
215 {
216 data[4] = (byte)((_len & 0xff000000) >> 24);
217 data[5] = (byte)((_len & 0x00ff0000) >> 16);
218 data[6] = (byte)((_len & 0x0000ff00) >> 8);
219 data[7] = (byte)(_len & 0x000000ff);
220 }
221 else
222 {
223 data[4] = (byte)(_len & 0x000000ff);
224 data[5] = (byte)((_len & 0x0000ff00) >> 8);
225 data[6] = (byte)((_len & 0x00ff0000) >> 16);
226 data[7] = (byte)((_len & 0xff000000) >> 24);
227
228 }
229 if (element.value == null)
230 throw new Exception(string.Format("異常:tag{0} value未賦值 ", Tags.ToHexString(element.tag)));
231
232 for (int i = 0; i < element.value.Length; i++)
233 data[8 + i] = element.value[i];
234 }
235 data_serial.Write(data, 0, data.Length);
236 }
237 }
238
239 //獲取單個(gè)元素序列化長度的遞歸調(diào)用
240 public void getSerialLen_item(DataElement element, ref int len)
241 {
242 if (element.haveItems)
243 {
244 len += element.getHeaderLen();
245 foreach (DataElement item in element.items)
246 getSerialLen_item(item, ref len);
247 len += 8;
248 }
249 else
250 {
251 if (element.value != null)
252 len += element.getHeaderLen() + element.value.Length;
253 else
254 len += element.getHeaderLen();
255 }
256 if (len % 2 != 0)//文件元信息元素整體字節(jié)數(shù)一定是偶數(shù)(包括tag VR 數(shù)據(jù)長度 數(shù)據(jù) 這些一起)
257 len++;
258 }
259
260 //獲取序列化后整個(gè)元素的長度
261 public int getSerialLen()
262 {
263 int serial_len=0;
264 getSerialLen_item(this, ref serial_len);
265 return serial_len;
266 }
267
268 //獲取item的header長度
269 public int getHeaderLen()
270 {
271 int len_ser = 0;
272 int len_tmp = 0;
273 if (explicitVR)//顯示VR
274 {
275 if (tag == 0xfffee000 || tag == 0xfffee00d || tag == 0xfffee0dd)
276 len_ser = 4 + 4;
277 else if (VR == VRs.OB || VR == VRs.OW || VR == VRs.OF ||
278 VR == VRs.UT || VR == VRs.SQ || VR == VRs.UN)
279 len_ser = (int)(4 + 2 + 4 + len_tmp);
280 else
281 len_ser = (int)(4 + 2 + len_tmp);
282 }
283 else //隱式Vr
284 {
285 len_ser = (int)(4 + 4 + len_tmp);
286 }
287
288 return len_ser;
289 }
290 }
不要問我pdv是啥 第一篇就出現(xiàn)過,pdv 即p data value ,它包括許多的data element 也就是俗稱tag。一個(gè)元素接一個(gè)元素 直到結(jié)束 跟文件解析的時(shí)候一樣 ,他的vr方式 以及字節(jié)序 在協(xié)商連接的時(shí)候就已確定 你只管讀就是了。那么新的問題又來了 echo這個(gè)dimse到底包含哪些tag 他們的值又應(yīng)該各是多少?為了解決你這個(gè)疑問我又要翻一個(gè)表出來:
你又要問這個(gè)表是怎么來的 ,dicom第七章 53頁。具體每個(gè)tag的作用各是什么 請(qǐng)參照右邊的說明,有三個(gè)地方我要提一下:
affected sop class uid
受影響的sop uid ,看過第一篇里圖的筒子都知道 打印有打印sop uid ,filmbox 有filmboxuid,那么這里echo也有 對(duì)應(yīng)的 他就是 :
1 //SOPClass: Verification SOP Class
2 public const String Verification = "1.2.840.10008.1.1";
為啥是這個(gè) 我不會(huì)再說了 你懂的。
command field
這個(gè)是作甚的,他的作用是用來區(qū)分不同的dimse 比如 c-create? c-find ,不用我多講了 旁邊的說明已經(jīng)很明顯了 echo時(shí)他的值應(yīng)設(shè)置成0x0030? 。
command data set type
數(shù)據(jù)集選項(xiàng) ,說白了就是給個(gè)標(biāo)識(shí) 后面有無數(shù)據(jù)集,我們這里自然是沒有 那么應(yīng)設(shè)置成0x0101 。
好了開工吧,打住 不是說還有服務(wù)類規(guī)范么 ,只有復(fù)雜的才有服務(wù)類規(guī)范 我們這個(gè)echo是非常非常非常之簡(jiǎn)單的 所以沒有服務(wù)類規(guī)范 直接開動(dòng)吧:
1 //組織Verification_CECHORSP響應(yīng)原語2 //rq端無data ,rsp端無data3 public void Verification_CECHORQ()4 {5 PDVset rq = new PDVset();6 rq.pduType = 0x04;7 rq.contextID = pstContextId;8 rq.msgControlHeader = 0x03;9 rq.elements = new SortedDictionary<uint, DataElement>();
10
11 int len = 0;
12
13 DataElement element = new DataElement();
14 element.bytOrder = bytOrder;
15
16 element.tag = 0x00000002;
17 element.setValue(UIDs.Verification);
18 rq.elements.Add(0x00000002, element);
19 len += (element.getSerialLen());
20
21 element.tag = 0x00000100;
22 element.setValue(0x0030.ToString());
23 rq.elements.Add(0x00000100, element);
24 len += (element.getSerialLen());
25
26 element.tag = 0x00000110;
27 element.setValue(0x03.ToString());
28 rq.elements.Add(0x00000110, element);
29 len += (element.getSerialLen());
30
31 element.tag = 0x00000800;//有無對(duì)應(yīng)的數(shù)據(jù)段
32 element.setValue(0x0101.ToString());
33 rq.elements.Add(0x00000800, element);
34 len += (element.getSerialLen());
35
36
37 element.tag = 0x00000000;//消息原語數(shù)據(jù)長度
38 element.setValue(len.ToString());
39 rq.elements.Add(0x00000000, element);
40 //len += (element.getSerialLen());
41
42 rq.itemLen = (uint)(12 + 2 + len);
43
44 rq.pduLen = rq.itemLen + 4;
45
46 //進(jìn)行c-echo-rsp響應(yīng)
47 stream.writeBytes(rq.serial());
48 }
看看代碼里面特定的地方 是不是跟我上面描述的一樣?就這樣so easy? 。看到?jīng)] 其實(shí)我這些都是在dicom文檔里翻的 就這樣而已沒什么神奇的 相信你也能。再來復(fù)習(xí)下dicom標(biāo)準(zhǔn)跟網(wǎng)絡(luò)通訊相關(guān)的幾個(gè)章節(jié) :
DICOM Part 4: Service Class Specifications? ,服務(wù)類規(guī)范
DICOM Part 7: Message Exchange?消息交換
DICOM Part 8: Network Communication Support for Message Exchange?網(wǎng)絡(luò)通訊對(duì)消息交換的支持
按照他們的套路來 就水到渠成 。
這是我的測(cè)試結(jié)果 不用懷疑哥的水平 哥是拿到醫(yī)院去測(cè)試過的:
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)
總結(jié)
以上是生活随笔為你收集整理的【转】dicom网络通讯入门(3)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。