dicom网络通讯入门(3)

接下来可以进行消息传递了 ,也就是dimse ,再来复习下 什么是dimse 。n-set  n-create c-echo 这些都是dimse  他们都是属于一种结构的pdu 那就是tf-pdu(传输数据和命令的都称之为tf-pdu 或者transfer pdu ,协商连接的都称之为associcate pdu) 。dimse 由 许多tag组成,就像文件解析那篇博文一样。
tf-pdu数据结构分析如下:

dicom网络通讯入门(3)

如果你又要问此图是怎么来的 ,dicom标准第八章 33页。你可能又要问 dimse又是什么 ,dimse全称 dicom 消息服务元素DIMSE(DICOM Message Service Element)
为什么你就说echo属于dimse 。请看dicom标准第七章 的目录: 9     DIMSE-C        9.1.5      C-ECHO SERVICE 。不用我多说了噻。
在这之前还是像以前一样先把tf-pdu的数据结构跟序列化搞了吧:

  1     struct PDVset

  2     {   

  3         //0x04

  4         public byte pduType;

  5         //pdu长度

  6         public uint pduLen;

  7         //pdv长度从作用来看他跟上面有所重复 pdv, presentation data value

  8         public uint itemLen;

  9         //这个contextID其实是指协商连接时的presentation context id 

 10         //最好判断下是否跟协商时的一致

 11         public byte contextID;

 12         //消息控制头 确定pdv类型是command 还是data 发送完成与否

 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             //header

 22             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             //items

 32             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 pdvType

 47     {

 48         command, data, commandAndData

 49     }

 50 

 51     struct DataElement

 52     {

 53         public uint _tag;

 54         public WarpedStream.byteOrder bytOrder;

 55         public bool explicitVR;//是显式VR的 否则隐式VR

 56         public uint tag

 57         {

 58             get { return _tag; }

 59             set

 60             {

 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 但要看情况隐式时都是4字节 显式时除ow那几个外都是2字节

 70         //如果为ow 显示不但长度为4 在之前还要跳过2字节,除ow那几个之外不用跳过

 71         public uint len;

 72         public byte[] value;

 73         public IList<DataElement> items ;//子项

 74         public bool haveItems;//是否包含子项

 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             else

 85                 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)//就是这个破地方 因为element的连续使用 导致会截断字符串

 98                 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         //序列化的递归调用

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))//靠 遇到文件夹开始标签了

121             if (element.haveItems )

122             {

123                 //开始标记

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                 //结束标记

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         //获取单个元素序列化长度的递归调用

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)//文件元信息元素整体字节数一定是偶数(包括tag VR 数据长度 数据 这些一起)

257                 len++;

258         }

259 

260         //获取序列化后整个元素的长度

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是啥 第一篇就出现过,pdv 即p data value ,它包括许多的data element 也就是俗称tag。一个元素接一个元素 直到结束 跟文件解析的时候一样 ,他的vr方式 以及字节序 在协商连接的时候就已确定 你只管读就是了。那么新的问题又来了 echo这个dimse到底包含哪些tag 他们的值又应该各是多少?为了解决你这个疑问我又要翻一个表出来:
dicom网络通讯入门(3)
你又要问这个表是怎么来的 ,dicom第七章 53页。具体每个tag的作用各是什么 请参照右边的说明,有三个地方我要提一下:

affected sop class uid
受影响的sop uid ,看过第一篇里图的筒子都知道 打印有打印sop uid ,filmbox 有filmboxuid,那么这里echo也有 对应的 他就是 :

1 //SOPClass: Verification SOP Class 

2 public const String Verification = "1.2.840.10008.1.1";

为啥是这个 我不会再说了 你懂的。
command field
这个是作甚的,他的作用是用来区分不同的dimse 比如 c-create  c-find ,不用我多讲了 旁边的说明已经很明显了 echo时他的值应设置成0x0030  。
command data set type
数据集选项 ,说白了就是给个标识 后面有无数据集,我们这里自然是没有 那么应设置成0x0101 。

好了开工吧,打住 不是说还有服务类规范么 ,只有复杂的才有服务类规范 我们这个echo是非常非常非常之简单的 所以没有服务类规范 直接开动吧:

 1         //组织Verification_CECHORSP响应原语

 2         //rq端无data ,rsp端无data

 3         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;//有无对应的数据段

32             element.setValue(0x0101.ToString());

33             rq.elements.Add(0x00000800, element);

34             len += (element.getSerialLen());

35 

36 

37             element.tag = 0x00000000;//消息原语数据长度

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             //进行c-echo-rsp响应

47             stream.writeBytes(rq.serial());

48         }


看看代码里面特定的地方 是不是跟我上面描述的一样?就这样so easy  。看到没 其实我这些都是在dicom文档里翻的 就这样而已没什么神奇的 相信你也能。再来复习下dicom标准跟网络通讯相关的几个章节 :

DICOM Part 4: Service Class Specifications  ,服务类规范

DICOM Part 7: Message Exchange 消息交换

DICOM Part 8: Network Communication Support for Message Exchange 网络通讯对消息交换的支持

按照他们的套路来 就水到渠成 。


这是我的测试结果 不用怀疑哥的水平 哥是拿到医院去测试过的:

dicom网络通讯入门(3)

你可能感兴趣的:(DICOM)