java通过TCP实现HL7医疗系统传输协议

环境

开发:java、maven、HAPI、socket、IDEA
测试工具:HAPI testPanel 2.0.1、7Edit

问题记录

句段头不对应导致的读取异常
  • 使用测试工具交互正常
    客户端(测试工具)
    在这里插入图片描述服务端
    在这里插入图片描述
  • 连接仪器出现以下问题
    客户端 操作历史结果传输(日志)
    在这里插入图片描述

服务端 接收消息异常
在这里插入图片描述

原因:

如上图,抛出异常为大意为 违反MLLP协议-预期字节“11”处于START状态,但为“239”
刚开始思路不清晰,以为是自己本地版本或者编码的问题,一顿搞,中间问了对方工程师,也以为是版本问题,刚好对方有个中间件版本需要更新,以为是这个原因,结果更新完一试,还是这样。
后来觉着自己对HL7协议还是太不熟悉了,太着急上来就开始搞功能了,于是沉下心有好好熟悉了一下HL7协议的内容。又仔细看了仪器客户端的日志,发现发送的消息前后都有一个字符 展示出来为 如下图的样子:
在这里插入图片描述
了解过HL7底层协议的都知道,这两个字符是为了确定消息边界,我称之为句块头、句块尾,各占一个字节,所以千万不要给它认成字符串,类似的还有(相当于回车或换行),

对应的ASC
在这里插入图片描述
VT对应句段头,对应的句段头是11,但是怎么会出来个239呢。查自付码表也没有查出来是个什么东西,那么这个239到底是是什么,从哪里来的呢?

  1. 可以确定的是,这个239是读取客户端发送过来的信息时候读出来的。这里与客户端交互使用的是第三方的工具包HAPI,通过查看源码可以看出来,HAPI是通过socket与客户端通信的:
    package ca.uhn.hl7v2.app.SimpleServer
    在这里插入图片描述
    关于socket的内容可以自行问度娘,这里不做重点讲解.
  2. 根据HAPI和仪器与服务端的交互逻辑可以知道,这里使用的是一种面向连接套接字【也成为流格式套接字(Stream Sockets)】。流格式套接字的内部有一个缓冲区(也就是字符数组),通过 socket 传输的数据将保存到这个缓冲区。接收端在收到数据后并不一定立即读取,只要数据不超过缓冲区的容量,接收端有可能在缓冲区被填满以后一次性地读取,也可能分成好几次读取。
    也就是说服务端在读取数据的时候会不会是根据自己的设定,并不是一次读一个字符,从而导致判定句段头的时候出错?因为在HAPI TestPanel(HAPI此时工具)中是可以正常读取仪器客户端传送过来的消息的,所以接着扒拉源码…
    下图可以看出,HAPI中支持的句段头确实是11,且不是11的时候就会抛出异常,这也正是 违反MLLP协议-预期字节“11”处于START状态,但为“239”异常的原因。
    在这里插入图片描述
  3. 这里大胆猜一下,会不会是仪器客户端 传过来的句段头虽然在文本编辑器中展示的是[VT],但是它对饮的字节码并不是11?由于没有权限直接进入系统查看仪器的日志,那就用socket自己写了一个服务端,去灵活读取客户端传输过来的信息。服务端代码不贴了就,HL7官方也有对应的demo: java socket 实现HL7服务端
  • 将输入流读取为字符串
    *在这里插入图片描述
    通过对传输信息的逐个读取,断点调试发现开始读取的时候有空格和特殊字符(乱码),使用测试工具则没有。判定仪器客户端与测试工具客户端传输的规范不一致。这种方式能读取到仪器客户端的内容入下:
  1. 进一步确认
    偶然发现,向日葵(远程工具)可以通过远程文件跳过仪器的检测系统读取到系统文件,方便了我查看仪器客户端的日志。通过系统日志可以看到,当服务端端使用HAPI 的时候直接抛出异常被客户端接收,连接断开,客户端进行重试,客户端一共进行了五次重试:
    在这里插入图片描述
    客户端发送的内容为:
    在这里插入图片描述
    目测没有问题,那服务端接收到的空格和乱码怎么解释?当我把日志中的内容复制到文本编辑器的时候展示为:
    在这里插入图片描述
    可以发现[VT]和[FS]在文本编辑器中不可见,且开头多出一个不能识别的字符。将客户端发送得消息在粘贴到IDEA看一下:
    在这里插入图片描述
    通过ASC码表知道,正常的[VT]对应的应该是\u000B,这里多出来的不可见字符\uFEFF应该就是读取字段头时候导致239的原因。
  2. 基于自己的socket服务读取到的句段头字符码是239,因此将句段头改为239后成功读取到仪器客户端发送过来的内容
MSH|^~\\&|||||20220707133720||ORU^R01|1|P|2.3.1||||0||UTF-8
"PID|1|||||||F
"OBR|1||1||N|20220622170125|20220622173444||1|1005^1^1||||20220622170125|Serum||||||||||Finished
"OBX|1|NM|1|PGⅠ|23.52|ng/mL|-||||F||587367||||0||||2112055C:427
响应客户端

按照规范,服务端接收到消息后应该返回一个ACK格式的MSG,且携带客户端发送信息的ID。消息体也应该包裹在句段头和句段尾之间。官方给出的示例:

在这里插入图片描述
示例中将句段头.尾等格式相关的字符设置为了常量,即保持请求跟响应的格式一致。
但是在与仪器客户端的信息交互中,客户端依然提示失败,查看日志并跟工程师沟通,结论为客户端没有收到响应。
汲取之前的教训,将句段头改为正常的格式,再次测试,客户端收到响应并提示操作成功
至此,仪器客户端与服务端信息交互实现。接下来是解析和拼装HL7信息。

参考:
https://saravanansubramanian.com/hl72xjavaprogramming/#step-2-of-8—build-a-simple-tcpip-server-capable-of-receiving-and-echo-ing-of-received-information
https://hapifhir.github.io/hapi-hl7v2/hapi-hl7overhttp/apidocs/index.html
https://blog.csdn.net/weixin_45004203/article/details/125360186
https://hapifhir.github.io/hapi-hl7v2/hapi-testpanel/index.html

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
HJ212是一种环境监测领域常用的协议,使用TCP连接进行数据传输。下面是一个简单的Java示例代码,通过建立TCP连接实现HJ212协议的解析: ```java import java.io.*; import java.net.*; public class HJ212Client { public static void main(String[] args) throws Exception { String serverIP = "192.168.0.1"; // 服务器IP地址 int serverPort = 6008; // 服务器端口号 String deviceID = "00000000"; // 设备ID String password = "123456"; // 密码 // 建立TCP连接 Socket socket = new Socket(serverIP, serverPort); try { // 发送登录命令 OutputStream out = socket.getOutputStream(); String loginCommand = String.format("##%sQN=1;ST=32;PW=%s;MN=%s;Flag=5;CP=&&\r\n", getLength(deviceID), password, deviceID); out.write(loginCommand.getBytes()); // 读取返回数据 InputStream in = socket.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); String response = reader.readLine(); if (response.startsWith("##") && response.endsWith("&&")) { // 登录成功,解析返回数据 // TODO: 解析返回数据 // 发送查询命令 String queryCommand = String.format("##%sQN=2;ST=32;CN=2011;PW=%s;MN=%s;Flag=5;CP=&&\r\n", getLength(deviceID), password, deviceID); out.write(queryCommand.getBytes()); // 读取返回数据 response = reader.readLine(); if (response.startsWith("##") && response.endsWith("&&")) { // 查询成功,解析返回数据 // TODO: 解析返回数据 } } } finally { // 关闭TCP连接 socket.close(); } } private static String getLength(String str) { // 获取字符串长度,不足14位前面补0 String length = Integer.toString(str.length()); while (length.length() < 14) { length = "0" + length; } return length; } } ``` 以上代码仅为示例,实际应用中需要根据具体情况进行修改和完善。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值