首页 > 学院 > 开发设计 > 正文

dicom网络通讯入门(2)

2019-11-17 03:14:55
字体:
来源:转载
供稿:网友

dicom网络通讯入门(2)

第二篇,前面都是闲扯 其实正文现在才开始,这次是把压箱底的东西都拿出来了。首先我们今天要干的事是实现一个echo响应测试工具 也就是echo 的scu,不是实现打印作业管理么。同学我告诉你还早着呢。本来标题取的就是《dicomviewer 第二弹 之 实现打印管理》名字多霸气,最后我又改回来了。首先你得把数据组织方式搞懂 那就是pdu 和dimse 元素 数据元素。然后基于这之上你得把协商连接这块搞懂 ,协商连接都没通过不用说后面的了。然后你得把实现一个功能 比如打印 ,scu跟scp之间你来我往的 过程和概念搞懂 也就是dimse 然后才是服务类。最够你全都理解了 并且写出东西来了能跟医院的设备正常 连接和操作了,那么恭喜你 差不多了。

最后要说的是: 解析dicom文件那篇你们都已经看过了,dicom网络通讯跟解析文件是一样的 只不过解析的是socket数据流里的 元素 数据结构本身是一样的,然后他有一些规范和标准 ,这就是dimse 和服务类 这些好像都在dicom标准的第四章 第八章 第七章 。

实现这一大坨的东西 有点望而却步了吧,其实总结起来就一句话 概括 按照dicom标准 封装数据 处理数据 ,然后根据特殊的参数和应用场景 依规范响应数据,。好废话少说,开工 看过 标准简介那篇博客 的都知道:PDU是一种数据结构 dataElement是一种数据结构pdu结构总共7种 其中用于连接控制的就占了6种 A-Associate—RQ PDU连接请求协议数据单元,用于关联请求。A-Associate.AC PDU基于DICOM标准的医学图像通信过程的实现连接接受协议数据单元,A.Associate—RJ PDU连接拒绝协议数据单元,A-Release-RQ PDU用于对关联请求的应答。用于拒绝关联请求。连接释放请求协议数据单元,A-Release.RSP PDU连接释放响应协议数据单元,A.Abort PDU传输内容的pdu只有一种P.DATA.TF PDU,当通讯双方建立了关联之后,就可以使用P.DATA-TF所提供的传输服务来实现不同的通信功能了。总之你在进行后面的dimse发送之前先得建立连接,否则你什么也搞不了。好下面就协商连接的pdu进行分析:

你问这图是怎么来的 dicom 第八章 31页。是不是跟上面说的是一样的 开始两字节 然后4字节表示长度,只不过这个更详细了。协商连接pdu说起有6种 其实有好多是大同小异 比如Associate pdu, 他分rq 和ac rq是请求 ac是响应。我把协商连接概述一下

概述之前,什么是通讯:还是炒剩饭 我又得把以前说过的话像背书一样的背一遍了 其实他确实是那么回事。什么是通讯 :命令tag +数据tag 一起组成sop ,就好象说一句这样的话:“把这根萝卜拿去给我切了” “喏 ,萝卜” 。其实这就是通讯 跟人与人之间传达意思一样。说话的时候太熟练了 没察觉到 你要仔细去想 你自己是一台电脑 ,会是一个什么样的步骤。网络传输 跟文件组织 是一样的格式 。不过有命令tag 。很多命令tag组合到一堆这称之为dimse。 echo n-create c-find c-store 这些都称之为dimse ,记住没有c-PRint 大哥打印管理是由很多组dimse 包括n-create 那些 你来我往的一套组成 比如 先n-create 什么东西 再 n-set什么东西,他有一种逻辑规范 什么参数错误则不进行n-set。这很多组dimse称之为服务类 ,比如打印管理 就是一个服务类。这些规范在dicom标准的第八章有说明。总之在dimse传递之前 你必须得协商连接。dicom标准的地址是这个http://medical.nema.org/standard.html,英文的 。看也比较困难 装装面子,主要是理解就行了 我看的也是别人翻译的中文的。不过官方的就是官方的 没办法 某些地方你找不到原因 想参照最标准的指示 你还是得硬着头皮去看英文文档。Associate pdu 协商连接的过程:又说多了 不论如何在进行dimse之前必须得进行连接协商 因为你与别人进行通讯首先你得确定几个东西。 ,谈话的主题是什么,你是用哪国语言。这两个东西一个称之为虚拟语法 abssyntax 一个称之为传输语法 transfersyntax 传输语法其实主要确定两个东西 字节序 和 vr表示方式 ,如果你不知道字节序是什么 请自己百度 vr表示方式 跟文件解析一样的,他们两个一起被称之为表达上下文。注意表达上下文有多个 每个都有id。如果你是scp端 那么连接协商响应 也就是association-ac的时候你要告知 以你scp程序的服务能力可以完成哪些表达上下文的服务 传输语法语法是什么,如果服务不了也要给出对应的上下文id 并进行告知。这样的话scu端知道你服务不了就知难而退 主动断开连接。 其次还有些其他东西比如pdu最大数据长度 一般是0x4000。好了讲完了 这就是协商连接的过程 对照上面的图理解了否。这是官方的解释:

官方的解释 网络协议是分层的,Dicom ul p ,称之为dicom上层协议。 也就是上图的dicom ul service provider。 反正要按照osi的标准来, 也就是说要定义一个associate-rq 或者 ac的数据结构来,一切的数据序列化或者反序列化都由 dicom ul service provider 来进行,反正只怕忽悠不死你。反正他说是那样说 我们自己按照自己的方式来。好终于要动代码了 ,我喜欢的事情来了 噢啦啦啦。其实这是一个抽象化的过程,把你的想法付诸行动 代码化.就像某人说过的 主要的不是技术 而是思路。分成两步 根据文档定义 associate Pdu的数据结构,遵循上面说的原则 一个associate pdu有多高 pst Item,我们把pst Item定义为子项,然后serial()是associate pdu的网络序列化函数:

  1 public enum PDUTypes  2     {  3         AssociateRQ = 0x01,  4         AssociateAC = 0x02,  5         AssociateRJ = 0x03,  6         DataTransfer = 0x04,  7         AssociateReleaseRQ = 0x05,  8         AssociateReleaseRP = 0x06,  9         AssociateAbort = 0x07, 10  11         applicationContext = 0x10, 12         PresentationContext = 0x20, 13         UserInformation = 0x50, 14     } 15  16     struct PDUAssociate { 17         //header 18         public byte pduType; 19         public uint length; 20         public ushort ProteocalVersion; 21         public string CallEdAE ;//length=16 22         public string CallingAE ; 23  24         //10 25         public byte aPPType; 26         public ushort appLength; 27         public string appName; 28  29         //20 30         public IList<PstItem> pstItems; 31         //50 userinfo 32         public byte userinfoType; 33         public ushort userinfoLength; 34         public byte maxnumType; 35         public ushort maxnumLength; 36         public uint maxnum;//DATA-TF PDU的可变字段的最大长度 一般为0x400 即1024 37         public byte impType;//关于实现类的 38         public ushort impLength; 39         public string impUID; 40         public byte impVersionType; 41         public ushort impVersionLength; 42         public string impVersion; 43  44         public byte[] serial() 45         { 46             if (length == 0) 47                 return null; 48             MemoryStream _stream = new MemoryStream((int)length + 6); 49             WarpedStream stream = new WarpedStream(_stream); 50             #region 序列化aassociateAC PDU 51             //header 52             stream.writeByte(pduType); 53             stream.skip_write(1); 54             stream.writeUint(length);//最低要94 我去 这是为什么呢 55             stream.writeUshort(ProteocalVersion); 56             stream.skip_write(2); 57             stream.writeString(CallEdAE, 16); 58             stream.writeString(CallingAE, 16); 59             stream.skip_write(32); 60  61             //10 62             stream.writeByte(appType); 63             stream.skip_write(1); 64             stream.writeUshort(appLength); 65             stream.writeString(appName, 0); 66             //21 67  68             for (int i = 0; i < pstItems.Count; i++) 69             { 70                 if (pstItems[i].used) 71                 { 72                     stream.writeByte(pstItems[i].pstType); 73                     stream.skip_write(1); 74                     stream.writeUshort(pstItems[i].pstLength); 75                     stream.writeByte(pstItems[i].pstID); 76                     stream.skip_write(3); 77                     //if (pstItems[i].used) 78                         //stream.writeBytes(new byte[] { 0x00, 0x00, 0x00 }); 79                     //else 80                     //30 81                     if (pstItems[i].pstType == 0x20)//如果是20则为printSCU 82                     { 83                         stream.writeByte(pstItems[i].absType); 84                         stream.skip_write(1); 85                         stream.writeUshort(pstItems[i].absLength); 86                         stream.writeString(pstItems[i].absStr, 0); 87                     } 88  89                     stream.writeByte(pstItems[i].tsfType); 90                     stream.skip_write(1); 91                     if (pstItems[i].used) 92                         stream.writeUshort(pstItems[i].tsfLeghth); 93                     else 94                         stream.writeUshort(0); 95                     if (pstItems[i].used) 96                         stream.writeString(pstItems[i].tsfStr, 0); 97                 } 98                 else 99                 {100                     stream.writeByte(pstItems[i].pstType);101                     stream.skip_write(1);102                     stream.writeUshort(0x08);103                     stream.writeByte(pstItems[i].pstID);104                     stream.writeBytes(new byte[] { 0x00, 0x04, 0x00 });105                     stream.writeBytes(new byte[] { 0x40, 0x00, 0x00, 0x00 });106                 }107             }108             109 110             //50111             stream.writeByte(userinfoType);112             stream.skip_write(1);113             stream.writeUshort(userinfoLength);114 115             stream.writeByte(maxnumType);116             stream.skip_write(1);117             stream.writeUshort(maxnumLength);118             stream.writeUint(maxnum);119 120             stream.writeByte(impType);121             stream.skip_write(1);122             stream.writeUshort(impLength);123             stream.writeString(impUID, 0);124 125             stream.writeByte(impVersionType);126             stream.skip_write(1);127             stream.writeUshort(impVersionLength);128             stream.writeString(impVersion, 0);129             #endregion130 131             _stream.Flush();132             byte[] data = _stream.GetBuffer();133             stream.close();134             _stream.Close();135             return data;136         }137     }138 139     struct PstItem140     {141         //20 abstractsyntax transfersyntax传输语法 142         public byte pstType;143         public ushort pstLength;144         public byte pstID;145         public bool used;146         //public byte pstRec; //保留字节 有效项是00 00 00 无效项是00 04 00147         public byte absType;//20的子项 30 40 读取的时候应该跟20一并读出来148         public ushort absLength;149
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表