C#解析HL7协议数据2.X

HL7 卫生信息交换标准(Health Level 7)

标准化的卫生信息传输协议,是医疗领域不同应用之间电子传输的协议。HL7汇集了不同厂商用来设计应用软件之间接口的标准格式,它将允许各个医疗机构在异构系统之间,进行数据交互。

HL7的主要应用领域是HIS/RIS,主要是规范HIS/RIS系统及其设备之间的通信,它涉及到病房和病人信息管理、化验系统、药房系统、放射系统、收费系统等各个方面。HL7的宗旨是开发和研制医院数据信息传输协议和标准,规范临床医学和管理信息格式,降低医院信息系统互连的成本,提高医院信息系统之间数据信息共享的程度。

Health Level 7中的“Level 7”是指OSI的七层模型中的最高一层,第七层。但这并不是说它遵循OSI第七层的定义数据元素,它只是用来构成它自己的抽象数据类型和编码规则。它也没有规定规范说明如何支持OSI第一到第六层的数据。

 HL7 基本语法 每一个 HL7 消息由一些段组成,段由<CR> (即<0x0D>)结尾。消息以 utf-8 格式传输 
每个段由三个字符的段名和固定数目的域组成,域由组件和子组件构成,在每个消息的 MSH 段定义各 个组成单元的分隔符。 
例如: 
MSH|^~&|Z3|Zybio|||20180514144801||ORU^R01|2018481414050147670|P|2.3.1||||||UNICODE 
其中: 
在 MSH 之后的五个字符定义用来区分各域、组件和子组件的分隔符。 标准使用下表的字符: 
| 域分隔符 
^ 组件分隔符 
& 子组件分隔符 
~ 重复分隔符 
\ 转义字符 
 
MSH 的第一个域包括各个分隔符。后面的有些域都是空的,因为他们是可选的并且迈瑞 HL7 接口没有 使用它,详细的域的定义和选取在后面说明。 
域 9: 包含消息类型和事件(ORU、R01) 
域 10: 包含一个唯一标识该消息的消息 ID 
域 11: 包含处理 ID(P 表示产品) 
域 12: 定义消息使用的 HL7 版本(2.3.1) 

服务端代码

using hepu.BloodModel;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
 
namespace hepu
{
    public partial class Blood5_TCP : Form
    {
        public Blood5_TCP()
        {
            InitializeComponent();
        }
        public delegate void showData(string msg);//委托,防止跨线程的访问控件,引起的安全异常
        private const int bufferSize = 8000;//缓存空间
        private TcpClient client;
        private TcpListener server;
        /// <summary>
        /// 结构体:Ip、端口
        /// </summary>
        struct IpAndPort
        {
            public string Ip;
            public string Port;
        }
 
        private void Blood5_TCP_Load(object sender, EventArgs e)
        {
           
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            if (txtIP.Text.Trim() == string.Empty)
            {
                return;
            }
            if (txtPort.Text.Trim() == string.Empty)
            {
                return;
            }
 
            Thread thread = new Thread(reciveAndListener);
            //如果线程绑定的方法带有参数的话,那么这个参数的类型必须是object类型,所以讲ip,和端口号 写成一个结构体进行传递
            IpAndPort ipHePort = new IpAndPort();
            ipHePort.Ip = txtIP.Text;//服务器IP
            ipHePort.Port = txtPort.Text;//为程序设置端口
 
            thread.Start((object)ipHePort);
        }
        /// <summary>
        /// 侦听客户端的连接并接收客户端发送的信息
        /// </summary>
        /// <param name="ipAndPort">服务端Ip、侦听端口</param>
        private void reciveAndListener(object ipAndPort)
        {
            IpAndPort ipHePort = (IpAndPort)ipAndPort;
 
            IPAddress ip = IPAddress.Parse(ipHePort.Ip);
            server = new TcpListener(ip, int.Parse(ipHePort.Port));
            server.Start();//启动监听
            rtbtxtShowData.Invoke(new showData(rtbtxtShowData.AppendText), "服务端开启侦听....\n");
            //  btnStart.IsEnabled = false;
 
            //获取连接的客户端对象
            client = server.AcceptTcpClient();
            rtbtxtShowData.Invoke(new showData(rtbtxtShowData.AppendText), "有客户端请求连接,连接已建立!");//AcceptTcpClient 是同步方法,会阻塞进程,得到连接对象后才会执行这一步  
 
            //获得流
            NetworkStream reciveStream = client.GetStream();
 
            #region 循环监听客户端发来的信息
 
            do
            {
                byte[] buffer = new byte[bufferSize];
                int msgSize;
                try
                {
                    lock (reciveStream)
                    {
                        msgSize = reciveStream.Read(buffer, 0, bufferSize);
                    }
                    if (msgSize == 0)
                        return;
                    string msg = Encoding.Default.GetString(buffer, 0, bufferSize);
                    //将hl7数据包转换成xml  方便提取数据
                    NewBlood5 blood5= FormatXml(Encoding.Default.GetString(buffer, 0, msgSize));
                    //这里只做单条数据传输  工作站没有批量发送    如果有批量的话可以判断数据包开头<SB>MSH|^~\&|公司名称(发送端公司名称)|XXX型号(发送端设备名称)||
                    //出现了几次,如果出现多次.    New一个list集合   将实体类添加到集合里
                    YLB_OneSynchronous(blood5);
 
                    //rtbtxtShowData.Invoke(new showData(rtbtxtShowData.AppendText), "\n客户端曰:" + result + "\r\n");
                }
                catch (Exception ex)
                {
                    server.Stop();
                    string aa = ex.Message;
                    MessageBox.Show(aa);
                    rtbtxtShowData.Invoke(new showData(rtbtxtShowData.AppendText), "\n 出现异常:连接被迫关闭");
                    break;
                }
            } while (true);
 
            #endregion
        }
 /// <summary>
        /// 将数据包转换成xml格式   提取有效参数
        /// </summary>
        /// <param name="hl7data"></param>
        /// <returns></returns>
        public NewBlood5  FormatXml(string hl7data)
        { 

            XmlDocument xmlObject = HL7ToXmlConverter.ConvertToXmlObject(hl7data);
                var xns = xmlObject.SelectSingleNode("HL7Message");
                XmlNodeList xnl = xns.ChildNodes;
                NewBlood5 blood5 = new NewBlood5();
                blood5.Barcode = HL7ToXmlConverter.GetText(xmlObject, "PID/PID.2", 0);//样本编号   没有重复的节点
                blood5.WBC = GetValue(xnl, "WBC");//白细胞数目   //重复的节点根据名字取值
                blood5.Neu = GetValue(xnl, "NeuCount");//中性粒细胞数目    Neu# 
            return blood5;
        }
        
        public string GetValue(XmlNodeList xnl,string 项目名) {
            string Value = string.Empty;
            if (!string.IsNullOrEmpty(项目名))
            {
                foreach (XmlNode xn in xnl)
                {
                    XmlElement xe = (XmlElement)xn;
                    XmlNodeList xnl2 = xe.ChildNodes;

                    foreach (XmlNode xn2 in xnl2)
                    {
                        XmlElement xe2 = (XmlElement)xn2;
                        if (xe2.InnerText.Equals(项目名))
                        {
                            var objs = new
                            {
                                Name = xe.GetElementsByTagName("OBX.5")
                            };
                            Value = objs.Name[0].InnerText;
                            return Value;
                        }

                    }
                }
            } 
            return Value;
        }
    }
}

解析hl7数据为xml

在分割hl7信息时 一定要注意有的仪器发出的信息每一小段是\r(回车)分割,有的是\n(换行),在分割时需要准确判断,不然解析出来的数据会被解析成一整个大的xml节点。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
 
namespace hepu.BloodModel
{
    /// <summary>
    /// HL7解析器
    /// </summary>
    public class HL7ToXmlConverter
    {
        private static XmlDocument _xmlDoc;
 
        /// <summary>
        /// 把HL7信息转成XML形式
        /// 分隔顺序 \n,|,~,^,&
        /// </summary>
        /// <param name="sHL7">HL7字符串</param>
        /// <returns></returns>
        public  string ConvertToXml(string sHL7)
        {
            _xmlDoc = ConvertToXmlObject(sHL7);
            return _xmlDoc.OuterXml;
        }
 
        public static XmlDocument ConvertToXmlObject(string sHL7)
        {
            _xmlDoc = CreateXmlDoc();
 
            //把HL7分成段
            string[] sHL7Lines = sHL7.Split('\r');//这里一定注意   经过测试,TCP方式接收的用\r分隔,webService方式接收的用\n分隔
 
 
            //去掉XML的关键字
            for (int i = 0; i < sHL7Lines.Length; i++)
            {
                sHL7Lines[i] = Regex.Replace(sHL7Lines[i], @"[^ -~]", "");
            }
 
            for (int i = 0; i < sHL7Lines.Length; i++)
            {
                // 判断是否空行
                if (sHL7Lines[i] != string.Empty)
                {
                    string sHL7Line = sHL7Lines[i];
 
                    //通过/r 或/n 回车符分隔
                    string[] sFields = HL7ToXmlConverter.GetMessgeFields(sHL7Line);
 
                    // 为段(一行)创建第一级节点
                    XmlElement el = _xmlDoc.CreateElement(sFields[0]);
                    _xmlDoc.DocumentElement.AppendChild(el);
 
                    // 循环每一行
                    for (int a = 0; a < sFields.Length; a++)
                    {
                        // 为字段创建第二级节点
                        XmlElement fieldEl = _xmlDoc.CreateElement(sFields[0] + "." + a.ToString());
 
                        //是否包括HL7的连接符
                        if (sFields[a] != @"^~\&")
                        {//0:如果这一行有任何分隔符
 
 
 
                            //通过~分隔
                            string[] sComponents = HL7ToXmlConverter.GetRepetitions(sFields[a]);
                            if (sComponents.Length > 1)
                            {//1:如果可以分隔
                                for (int b = 0; b < sComponents.Length; b++)
                                {
                                    XmlElement componentEl = _xmlDoc.CreateElement(sFields[0] + "." + a.ToString() + "." + b.ToString());
 
                                    //通过&分隔 
                                    string[] subComponents = GetSubComponents(sComponents[b]);
                                    if (subComponents.Length > 1)
                                    {//2.如果有字组,一般是没有的。。。
                                        for (int c = 0; c < subComponents.Length; c++)
                                        {
                                            //修改了一个错误
                                            string[] subComponentRepetitions = GetComponents(subComponents[c]);
                                            if (subComponentRepetitions.Length > 1)
                                            {
                                                for (int d = 0; d < subComponentRepetitions.Length; d++)
                                                {
                                                    XmlElement subComponentRepEl = _xmlDoc.CreateElement(sFields[0] + "." + a.ToString() + "." + b.ToString() + "." + c.ToString() + "." + d.ToString());
                                                    subComponentRepEl.InnerText = subComponentRepetitions[d];
                                                    componentEl.AppendChild(subComponentRepEl);
                                                }
                                            }
                                            else
                                            {
                                                XmlElement subComponentEl = _xmlDoc.CreateElement(sFields[0] + "." + a.ToString() + "." + b.ToString() + "." + c.ToString());
                                                subComponentEl.InnerText = subComponents[c];
                                                componentEl.AppendChild(subComponentEl);
 
                                            }
                                        }
                                        fieldEl.AppendChild(componentEl);
                                    }
                                    else
                                    {//2.如果没有字组了,一般是没有的。。。
                                        string[] sRepetitions = HL7ToXmlConverter.GetComponents(sComponents[b]);
                                        if (sRepetitions.Length > 1)
                                        {
                                            XmlElement repetitionEl = null;
                                            for (int c = 0; c < sRepetitions.Length; c++)
                                            {
                                                repetitionEl = _xmlDoc.CreateElement(sFields[0] + "." + a.ToString() + "." + b.ToString() + "." + c.ToString());
                                                repetitionEl.InnerText = sRepetitions[c];
                                                componentEl.AppendChild(repetitionEl);
                                            }
                                            fieldEl.AppendChild(componentEl);
                                            el.AppendChild(fieldEl);
                                        }
                                        else
                                        {
                                            componentEl.InnerText = sComponents[b];
                                            fieldEl.AppendChild(componentEl);
                                            el.AppendChild(fieldEl);
                                        }
                                    }
                                }
                                el.AppendChild(fieldEl);
                            }
                            else
                            {//1:如果不可以分隔,可以直接写节点值了。
                                fieldEl.InnerText = sFields[a];
                                el.AppendChild(fieldEl);
                            }
 
 
 
 
 
 
 
 
 
 
 
 
                        }
                        else
                        {//0:如果不可以分隔,可以直接写节点值了。
                            fieldEl.InnerText = sFields[a];
                            el.AppendChild(fieldEl);
                        }
                    }
                }
            }
 
 
 
            return _xmlDoc;
        }
 
        /// <summary>
        /// 通过|分隔 字段
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        private static string[] GetMessgeFields(string s)
        {
            return s.Split('|');
        }
 
        /// <summary>
        /// 通过^分隔 组字段
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        private static string[] GetComponents(string s)
        {
            return s.Split('^');
        }
 
        /// <summary>
        /// 通过&分隔 子分组组字段
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        private static string[] GetSubComponents(string s)
        {
            return s.Split('&');
        }
 
        /// <summary>
        /// 通过~分隔 重复
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        private static string[] GetRepetitions(string s)
        {
            return s.Split('~');
        }
 
        /// <summary>
        /// 创建XML对象
        /// </summary>
        /// <returns></returns>
        private  static XmlDocument CreateXmlDoc()
        {
            XmlDocument output = new XmlDocument();
            XmlElement rootNode = output.CreateElement("HL7Message");
            output.AppendChild(rootNode);
            return output;
        }
 
        public  string GetText(XmlDocument xmlObject, string path)
        {
            XmlNode node = xmlObject.DocumentElement.SelectSingleNode(path);
            if (node != null)
            {
                return node.InnerText;
            }
            else
            {
                return null;
            }
        }
        public string GetValue(XmlNodeList xnl,string 项目名) {
            string Value = string.Empty;
            foreach (XmlNode xn in xnl)
            {
                XmlElement xe = (XmlElement)xn;
                XmlNodeList xnl2 = xe.ChildNodes;

                foreach (XmlNode xn2 in xnl2)
                {
                    XmlElement xe2 = (XmlElement)xn2;
                    if (xe2.InnerText.Equals(项目名))
                    {
                        var objs = new
                        {
                            Name = xe.GetElementsByTagName("OBX.5") 
                        };
                        Value = objs.Name[0].InnerText;
                        return Value;
                    }
                    
                }
            }
            return Value;
        }
 
        public static string GetText(XmlDocument xmlObject, string path, int index)
        {
            XmlNodeList nodes = xmlObject.DocumentElement.SelectNodes(path);
            if (index <= nodes.Count)
            {
                return nodes[index].InnerText;
            }
            else
            {
                return null;
            }
 
 
        }
 
        public  String[] GetTexts(XmlDocument xmlObject, string path)
        {
            XmlNodeList nodes = xmlObject.DocumentElement.SelectNodes(path);
            String[] arr = new String[nodes.Count];
            int index = 0;
            foreach (XmlNode node in nodes)
            {
                arr[index++] = node.InnerText;
            }
            return arr;
 
        }
 
    }
 
}

测试:

使用tcp调试工具作为客户端发送hl7数据(工具可在我的资源中下载)tcp-udp助手.zip_tcpudp调试工具-网络安全文档类资源-CSDN下载

以接受病历号和HCT为例

病历号是唯一的节点

HCT的父节点名称有重复

重复时遍历所有节点   根据hct查找

public string GetValue(XmlNodeList xnl,string 项目名) {
            string Value = string.Empty;
            if (!string.IsNullOrEmpty(项目名))
            {
                foreach (XmlNode xn in xnl)
                {
                    XmlElement xe = (XmlElement)xn;
                    XmlNodeList xnl2 = xe.ChildNodes;

                    foreach (XmlNode xn2 in xnl2)
                    {
                        XmlElement xe2 = (XmlElement)xn2;
                        if (xe2.InnerText.Equals(项目名))
                        {
                            var objs = new
                            {
                                Name = xe.GetElementsByTagName("OBX.5")
                            };
                            Value = objs.Name[0].InnerText;
                            return Value;
                        }

                    }
                }
            } 
            return Value;
        }

  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香煎三文鱼

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值