日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【转】dicom网络通讯入门(3)

發(fā)布時(shí)間:2023/12/10 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】dicom网络通讯入门(3) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(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 。請看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的作用各是什么 請參照右邊的說明,有三個(gè)地方我要提一下:

affected sop class uid
受影響的sop uid ,看過第一篇里圖的筒子都知道 打印有打印sop uid ,filmbox 有filmboxuid,那么這里echo也有 對應(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是非常非常非常之簡單的 所以沒有服務(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;//有無對應(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ò)通訊對消息交換的支持

按照他們的套路來 就水到渠成 。


這是我的測試結(jié)果 不用懷疑哥的水平 哥是拿到醫(yī)院去測試過的:

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的【转】dicom网络通讯入门(3)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。