分享一个去年做的迈瑞血球分析仪BC-5120的串口开发,如果感兴趣可以留言或者私信,我再继续写其他检验设备的接口开发。
设备BC-5000和BC-5150应该都是通用的协议。详细内容请看传输协议。协议使用HL7,内容超复杂自行百度学习,不懂也没关系。
下载:数据传输协议、采集的数据、报告单的照片以及说明书上对项目的定义
2020年7月29日在文末新增完整解析源代码。
先看看报告单:
看一下用AccessPort串口工具接收的数据:
在程序中获取串口数据byte[] data,删除前后多余的02后,首先判断开始标记:0B,结束标记:1C 0D。然后将数据流转化成字符串,消息段用0D分隔,转换成数组。
data = TrimByte(data, 0x02);//消除前后多余的02字节
if (IsBegin(data, 0x0B) && IsEnd(dataBuffer[id].Data, new byte[] { 0x1C, 0x0D })
{
string txt = Encoding.Default.GetString(data);//数据流转成字符串
txt = txt.SubString(1,txt.Length-3);//消除前后标记
string[] Segment = txt.Split((char)0x0D);//用分隔符0D对消息段分组
}
消息段:段内的字段之间用“|”分隔。
string[] Field = Segment[i].Split('|');
MSH消息段,需要的内容:字段[10]消息控制ID=13(用于发送成功消息),字段[14]类型=P样本,Q质控
MSH|^~\&|||||20191123121334||ORU^R01|13|P|2.3.1||||||UNICODE PID|1||^^^^MR PV1|1 OBR|1||12|00001^Automated Count^99MRC|||
messageID = Field[9];//消息控制ID
type = Field[10];//P样本,Q质控
OBR消息段,字段[4]样本号=12,字段[8]日期=20191123115806
OBR|1||12|00001^Automated Count^99MRC|||20191123115806|||||||||||||||||HM||||||||1
SampleNo = Field[3];//样本号
DateTime = Field[7];//日期yyyyMMddHHmmss
OBX消息段,检验结果保存在这里,字段[4]项目名称,字段[6]结果。其他结果依次获取。
OBX|5|NM|6690-2^WBC^LN||12.36|10*9/L|4.00-10.00|H~N|||F
string itemName = Field[3].Split('^')[1];//只需要名称缩写
string itemValue = Field[5];//检验结果
获取WBC白细胞直方图数据:从“Base64^”开始是以字符串保存的图像Base64数据,需要转换成图像。红细胞和血小板直方图以及散点图类似。
OBX|31|ED|15008^WBC Histogram. BMP^99MRC||^Image^BMP^Base64^Qk1ObgAAAAAAAAYCAAAoAAAApQAAAKUAAAABAAgAAAAAAEh
bmp = Base64ToImage(itemValue.Substring("^Image^BMP^Base64^".Length));//获取直方图
完成后需要向仪器发送成功消息:
string ack = string.Format(@"MSH|^~\&|LIS||||{0}||ACK^R01|{1}|P|2.3.1||||||UNICODE
MSA|AA|{2}", Cvt.Date14(DateTime.Now), messageID, messageID);//messageID是前面获取的消息控制ID
args.SendData = Encoding.UTF8.GetBytes(ack);//发送串口数据
下面是完整的解析代码,因为是LIS系统的一部分,代码并不简洁,其中分类线等数据并没用上:
/// <summary>
/// 迈瑞BC-5000,5120,5150血球分析仪数据解析
/// </summary>
/// <param name="id">检验设备名称或者ID,用于缓冲区</param>
/// <param name="data">串口数据</param>
/// <returns>检验结果类</returns>
public static DataArgs MindrayBC5000(string id, byte[] data)
{
if (data.Length == 1 && data[0] == 2)//过滤0x02
{
return null;
}
data = Comm.TrimByte(data, 0x02);//删除数据流前后的0x02
if (Comm.IsBegin(data, 0x0B) && Comm.dataBuffer.ContainsKey(id))//数据流从0x0B开始
{
Comm.dataBuffer[id].Clear();//清空缓冲区
}
Comm.AddBuffer(id, data);//数据放入缓冲区,同一个患者的结果可能被分成几段传输
if (Comm.IsEnd(Comm.dataBuffer[id].Data, new byte[] { 0x1C, 0x0D }) == false)//以0x1C, 0x0D结束,传输还没结束,继续等待数据
{
return null;
}
//完成接收数据,清除前后标记
Comm.dataBuffer[id].Data.RemoveAt(0);//0x0B开始
Comm.dataBuffer[id].Data.RemoveRange(Comm.dataBuffer[id].Data.Count - 3, 2);//0x1C, 0x0D结束
data = Comm.dataBuffer[id].Data.ToArray();//把缓冲区内数据复制到data后清空缓冲区
Comm.dataBuffer[id] = null;
Comm.dataBuffer.Remove(id);
if (data == null || data.Length < 100)
return null;
string messageID = "";
string type = "";
DataArgs args = new DataArgs();//保存检验结果的类
args.Data = data;
args.Type = 0;
List<ItemArgs> itemList = new List<ItemArgs>();//保存检验项目
string[] Segment = Comm.ByteToString(data).Split((char)0x0D);//数据流转字符串后,以0x0D作为分隔符变成数组
Comm.Log("StartingDataSub");
char splitChar = '|';// Segment[0][3];//用|分隔
List<byte> WSepLine = new List<byte>();
List<byte> RSepLine = new List<byte>();
List<byte> PSepLine = new List<byte>();
List<byte> RHisto = new List<byte>();
List<byte> WHisto = new List<byte>();
List<byte> PHisto = new List<byte>();
List<byte> DHisto = new List<byte>();//散点图
Dictionary<string, int> dictLength = new Dictionary<string, int>();//直方图数据长度
//该设备会传输直方图的图片,无需通过数据自己画图
List<Bitmap> bmpList = new List<Bitmap>();
HistoBox histDIFF = new HistoBox();
histDIFF.HistoType1 = HistoBox.HistoType.DIFF;
for (int i = 0; i < Segment.Length; i++)
{
string[] Field = Segment[i].Split(splitChar);//每行记录再用“|”分隔符拆分获取字段
switch (Field[0])
{
case "MSH":
messageID = Field[9];//消息控制ID
type = Field[10];//P样本,Q质控
break;
case "OBR":
args.SampleNo = Cvt.ToInt(Field[3]);//样本号
args.DateTime = Cvt.ToDateTime(Field[7], "yyyyMMddHHmmss", DateTime.Now);//检验时间
break;
case "OBX":
string itemName = Field[3].Split('^')[1];//项目名称
string itemValue = Field[5];//值
switch (itemName)
{
// 其他数据
case "Take Mode": // 进样模式 IS 8001 99MRC 08001^Take Mode^99MRC
case "Blood Mode": // 血样模式 IS 8002 99MRC 08002^Blood Mode^99MRC
case "Test Mode": // 测量模式 IS 8003 99MRC 08003^Test Mode^99MRC
case "Age": // 年龄 NM 30525-0 LN 30525-0^Age^LN
case "Remark": // 备注 ST 1001 99MRC 01001^Remark^99MRC
case "Ref Group": // 参考组 IS 1002 99MRC 01002^Ref Group^99MRC
case "Qc Level": // 质控级别 IS 5001 99MRC 05001^Qc Level^99MRC
case "Recheck flag": // 复检标志 IS 1006 99MRC 01006^ Recheck flag^99MRC
break;
// 检验结果数据项
case "WBC": // WBC NM Feb-90 LN 6690-2^WBC^LN
case "BAS#": // BAS NM 704-7 LN 704-7^BAS#^LN
case "BAS%": // BAS_PER NM 706-2 LN 706-2^BAS%^LN
case "NEU#": // NEU NM 751-8 LN 751-8^NEU#^LN
case "NEU%": // NEU_PER NM 770-8 LN 770-8^NEU%^LN
case "EOS#": // EOS NM 711-2 LN 711-2^EOS#^LN
case "EOS%": // EOS_PER NM 713-8 LN 713-8^EOS%^LN
case "LYM#": // LYM NM 731-0 LN 731-0^LYM#^LN
case "LYM%": // LYM_PER NM 736-9 LN 736-9^LYM%^LN
case "MON#": // MON NM 742-7 LN 742-7^MON#^LN
case "MON%": // MON_PER NM May-05 LN 5905-5^MON%^LN
// 研究参数
case "*ALY#": // ALY NM 26477-0 LN 26477-0^*ALY#^LN
case "*ALY%": // ALY_PER NM 13046-8 LN 13046-8^*ALY%^LN
case "*LIC#": // LIC(大型未成熟细胞) NM 10000 99MRC 10000^*LIC#^99MRC
case "*LIC%": // LIC_PER(大型未成熟细胞百分比) NM 10001 99MRC 10001^*LIC%^99MRC
case "RBC": // RBC NM 789-8 LN 789-8^RBC^LN
case "HGB": // HGB NM 718-7 LN 718-7^HGB^LN
case "MCV": // MCV NM 787-2 LN 787-2^MCV^LN
case "MCH": // MCH NM 785-6 LN 785-6^MCH^LN
case "MCHC": // MCHC NM 786-4 LN 786-4^MCHC^LN
case "RDW-CV": // RDW_CV NM 788-0 LN 788-0^RDW-CV^LN
case "RDW-SD": // RDW_SD NM 21000-5 LN 21000-5^RDW-SD^LN
case "HCT": // HCT NM Mar-44 LN 4544-3^HCT^LN
case "PLT": // PLT NM 777-3 LN 777-3^PLT^LN
case "MPV": // MPV NM 32623-1 LN 32623-1^MPV^LN
case "PDW": // PDW NM 32207-3 LN 32207-3^PDW^LN
case "PCT": // PCT(血小板压积) NM 10002 99MRC 10002^PCT^99MRC
case "PLCC": // PLCC NM 10013 99MRC 10013^ PLCC^99MRC
case "PLCR": // PLCR NM 10014 99MRC 10014^ PLCR^99MRC
itemList.Add(new ItemArgs(itemName, itemValue, true)); //OBX|7|NM|6690-2^WBC^LN||***.**|10*9/L|***.**-***.**|N|||F
break;
// 质控特有参数
//case "GRAN-X": // GRAN-X NM 10003 99MRC 10003^GRAN-X^99MRC
//case "GRAN-Y": // GRAN-Y NM 10004 99MRC 10004^GRAN-Y^99MRC
//case "GRAN-Y(W)": // GRAN-Y(W) NM 10005 99MRC 10005^GRAN-Y(W)^99MRC
//case "WBC-MCV": // WBCMCV NM 10006 99MRC 10006^WBC-MCV^99MRC
// 检验结果中间数据(WBC、RBC、PLT直方图及散点图数据等)
case "WBC Histogram. Left Line": // WBC直方图左分类线 NM 15001 99MRC 15001^WBC Histogram. Left Line^99MRC
case "WBC Histogram. Right Line": // WBC直方图右分类线 NM 15002 99MRC 15002^WBC Histogram. Right Line^99MRC
case "WBC Histogram. Middle Line": // WBC直方图中间分类线 NM 15003 99MRC 15003^WBC Histogram. Middle Line^99MRC
WSepLine.Add(Cvt.ToByte(itemValue));
break;
case "WBC Histogram. Meta Length": // WBC直方图元数据长度 NM 15004 99MRC 15004^WBC Histogram. Meta Length^99MRC
dictLength.Add(itemName, Cvt.ToInt(itemValue));
break;
case "WBC Histogram. Binary": // WBC直方图二进制数据 ED 15000 99MRC 15000^WBC Histogram. Binary^99MRC
WHisto.AddRange(Comm.StringToByte(itemValue));
break;
//case "WBC Histogram. Left Line Adjusted": // WBC直方图左分类线调整标记 NM 15005 99MRC 15005^WBC Histogram. Left Line Adjusted^99MRC
//case "WBC Histogram. Right Line Adjusted": // WBC直方图右分类线调整标记 NM 15006 99MRC 15006^WBC Histogram. Right Line Adjusted^99MRC
//case "WBC Histogram. Middle Line Adjusted": // WBC直方图中间分类线调整标记 NM 15007 99MRC 15007^WBC Histogram. Middle Line Adjusted^99MRC
case "WBC Histogram. BMP": // WBC直方图位图数据 ED 15008 99MRC 15008^WBC Histogram. BMP^99MRC
bmpList.Add(Comm.Base64ToImage(itemValue.Substring("^Image^BMP^Base64^".Length)));
break;
case "RBC Histogram. BMP": // WBC直方图位图数据 ED 15008 99MRC 15008^WBC Histogram. BMP^99MRC
bmpList.Add(Comm.Base64ToImage(itemValue.Substring("^Image^BMP^Base64^".Length)));
break;
case "PLT Histogram. BMP": // WBC直方图位图数据 ED 15008 99MRC 15008^WBC Histogram. BMP^99MRC
bmpList.Add(Comm.Base64ToImage(itemValue.Substring("^Image^BMP^Base64^".Length)));
break;
//case "WBC Histogram. Total": // WBC直方图总数 NM 15009 99MRC 15009^WBC Histogram. Total^99MRC
case "RBC Histogram. Binary": // RBC直方图二进制数据 ED 15050 99MRC 15050^RBC Histogram. Binary^99MRC
RHisto.AddRange(Comm.StringToByte(itemValue));
break;
case "RBC Histogram. Left Line": // RBC直方图左分类线 NM 15051 99MRC 15051^RBC Histogram. Left Line^99MRC
case "RBC Histogram. Right Line": // RBC直方图右分类线 NM 15052 99MRC 15052^RBC Histogram. Right Line^99MRC
RSepLine.Add(Cvt.ToByte(itemValue));
break;
//case "RBC Histogram. Left Line Adjusted": // RBC直方图左分类线调整标记 IS 15054 99MRC 15054^RBC Histogram. Left Line Adjusted^99MRC
//case "RBC Histogram. Right Line Adjusted": // RBC直方图右分类线调整标记 IS 15055 99MRC 15055^RBC Histogram. Right Line Adjusted^99MRC
case "RBC Histogram. Binary Meta Length": // RBC直方图元数据长度 NM 15053 99MRC 15053^RBC Histogram. Binary Meta Length^99MRC
dictLength.Add(itemName, Cvt.ToInt(itemValue));
break;
case "PLT Histogram. Binary": // PLT直方图二进制数据 ED 15100 99MRC 15100^PLT Histogram. Binary^99MRC
PHisto.AddRange(Comm.StringToByte(itemValue));
break;
case "PLT Histogram. Left Line": // PLT直方图左分类线 NM 15111 99MRC 15111^PLT Histogram. Left Line^99MRC
case "PLT Histogram. Right Line": // PLT直方图右分类线 NM 15112 99MRC 15112^PLT Histogram. Right Line^99MRC
PSepLine.Add(Cvt.ToByte(itemValue));
break;
case "PLT Histogram. Binary Meta Length": // PLT直方图元数据长度 NM 15113 99MRC 15113^PLT Histogram. Binary Meta Length^99MRC
dictLength.Add(itemName, Cvt.ToInt(itemValue));
break;
//case "PLT Histogram. Left Line Adjusted": // PLT直方图左分类线调整标记 IS 15114 99MRC 15114^PLT Histogram. Left Line Adjusted^99MRC
//case "PLT Histogram. Right Line Adjusted": // PLT直方图右分类线调整标记 IS 15115 99MRC 15115^PLT Histogram. Right Line Adjusted^99MRC
case "WBC DIFF Scattergram. BMP": // DIFF散点图位图数据 ED 15200 99MRC 15200^WBC DIFF Scattergram. BMP^99MRC
bmpList.Add(Comm.Base64ToImage(itemValue.Substring("^Image^BMP^Base64^".Length)));
break;
case "WBC DIFF Scattergram. BIN": // DIFF散点图二进制数据 ED 15201 99MRC 15201^ WBC DIFF Scattergram. BIN^99MRC
histDIFF.HistoBytes = Comm.Base64ToStream(itemValue.Substring("^Application^Octer-stream^Base64^".Length));
break;
//case "WBC DIFF Scattergram. BIN type data": // DIFF散点图Type数据 ED 15202 99MRC 15202^ WBC DIFF Scattergram. BIN type data^99MRC
case "WBC DIFF Scattergram. Fsc dimension": // OBX|44|NM|15205^WBC DIFF Scattergram. Fsc dimension^99MRC||256||||||F
histDIFF.Width = Cvt.ToInt(itemValue);
break;
case "WBC DIFF Scattergram. Ssc dimension": // OBX|45|NM|15206^WBC DIFF Scattergram. Ssc dimension^99MRC||256||||||F
histDIFF.Height = Cvt.ToInt(itemValue);
break;
case "WBC DIFF Scattergram. Meta len": // DIFF散点图元数据长度 NM 15203 99MRC 15203^ WBC DIFF Scattergram. Meta len^99MRC
dictLength.Add(itemName, Cvt.ToInt(itemValue));
break;
case "WBC DIFF Scattergram. Meta count": // DIFF散点图元数据数目 NM 15204 99MRC 15204^ WBC DIFF Scattergram. Meta count^99MRC
dictLength.Add(itemName, Cvt.ToInt(itemValue));
break;
// 异常报警信息
case "WBC Abnormal": // 白细胞异常 IS 12011 99MRC 12011^WBC Abnormal^99MRC
case "Imm Granulocytes?": // 未成熟细胞? IS 34165-1 LN 34165-1^Imm Granulocytes?^LN
case "Atypical Lymphs?": // 异常/异型淋巴细胞? IS 15192-8 LN 15192-8^Atypical Lymphs?^LN
case "RBC Abnormal distribution": // 红细胞分布异常 IS 12013 99MRC 12013^RBC Abnormal distribution^99MRC
case "Anemia": // 贫血 IS 12014 99MRC 12014^Anemia^99MRC
case "HGB Interfere": // 血红蛋白异常/干扰? IS 12015 99MRC 12015^HGB Interfere^99MRC
case "PLT Abnormal Distribution": // 血小板分布异常 IS 12016 99MRC 12016^PLT Abnormal Distribution^99MRC
default:
break;
}
break;
default:
break;
}
}
args.ItemsList = itemList.ToArray();
if (WHisto.Count > 0)
{
args.WHisto = WHisto.ToArray();
HistoBox hist0 = new HistoBox(HistoBox.HistoType.WBC, args.WHisto, args.WSepLine);
hist0.MaxFL = 385;
//hist0.LineType = HistoBox.LineTypes.LinesSmooTh;
hist0.XSpace = 1;// 400f / 256f;
hist0.Draw();
bmpList.Add((Bitmap)hist0.Image);
}
if (RHisto.Count > 0)
{
args.RHisto = RHisto.ToArray();
HistoBox hist1 = new HistoBox(HistoBox.HistoType.RBC, args.RHisto, args.RSepLine);
//hist1.LineType = HistoBox.LineTypes.LinesSmooTh;
hist1.XSpace = 1;//250f / 256f;
hist1.Draw();
bmpList.Add((Bitmap)hist1.Image);
}
if (PHisto.Count > 0)
{
args.PHisto = PHisto.ToArray();
HistoBox hist2 = new HistoBox(HistoBox.HistoType.PLT, args.PHisto, args.PSepLine);
//hist2.LineType = HistoBox.LineTypes.LinesSmooTh;
hist2.XSpace = 1;// 30f / 256f;
hist2.Draw();
bmpList.Add((Bitmap)hist2.Image);
}
if (histDIFF.HistoBytes != null)
{
histDIFF.Draw();
bmpList.Add((Bitmap)histDIFF.Image);
}
args.ImageList = bmpList.ToArray();
args.Completed = true;
//向设备发送完成消息
string ack = string.Format(@"MSH|^~\&|LIS||||{0}||ACK^R01|{1}|P|2.3.1||||||UNICODE
MSA|AA|{2}", Cvt.Date14(DateTime.Now), messageID, messageID);
args.SendData = Encoding.UTF8.GetBytes(ack);
return args;
}